1 //============================================================================ 2 // 3 // SSSS tt lll lll 4 // SS SS tt ll ll 5 // SS tttttt eeee ll ll aaaa 6 // SSSS tt ee ee ll ll aa 7 // SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" 8 // SS SS tt ee ll ll aa aa 9 // SSSS ttt eeeee llll llll aaaaa 10 // 11 // Copyright (c) 1995-2021 by Bradford W. Mott, Stephen Anthony 12 // and the Stella Team 13 // 14 // See the file "License.txt" for information on usage and redistribution of 15 // this file, and for a DISCLAIMER OF ALL WARRANTIES. 16 //============================================================================ 17 18 #ifndef FRAMEBUFFER_HXX 19 #define FRAMEBUFFER_HXX 20 21 #include <list> 22 23 class OSystem; 24 class Console; 25 class Settings; 26 class FBSurface; 27 class TIASurface; 28 29 #ifdef GUI_SUPPORT 30 #include "Font.hxx" 31 #endif 32 33 #include "Rect.hxx" 34 #include "Variant.hxx" 35 #include "TIAConstants.hxx" 36 #include "FBBackend.hxx" 37 #include "FrameBufferConstants.hxx" 38 #include "EventHandlerConstants.hxx" 39 #include "VideoModeHandler.hxx" 40 #include "bspf.hxx" 41 42 /** 43 This class encapsulates all video buffers and is the basis for the video 44 display in Stella. The FBBackend object contained in this class is 45 platform-specific, and most rendering tasks are delegated to it. 46 47 The TIA is drawn here, and all GUI elements (ala ScummVM, which are drawn 48 into FBSurfaces), are in turn drawn here as well. 49 50 @author Stephen Anthony 51 */ 52 class FrameBuffer 53 { 54 public: 55 // Zoom level step interval 56 static constexpr float ZOOM_STEPS = 0.25; 57 58 enum UpdateMode { 59 NONE = 0, 60 REDRAW = 1, 61 RERENDER = 2 62 }; 63 64 public: 65 explicit FrameBuffer(OSystem& osystem); 66 ~FrameBuffer(); 67 68 /** 69 Initialize the framebuffer object (set up the underlying hardware). 70 Throws an exception upon encountering any errors. 71 */ 72 void initialize(); 73 74 /** 75 (Re)creates the framebuffer display. This must be called before any 76 calls are made to derived methods. 77 78 @param title The title of the application / window 79 @param size The dimensions of the display 80 @param honourHiDPI If true, consult the 'hidpi' setting and enlarge 81 the display size accordingly; if false, use the 82 exact dimensions as given 83 84 @return Status of initialization (see FBInitStatus 'enum') 85 */ 86 FBInitStatus createDisplay(const string& title, BufferType type, 87 Common::Size size, bool honourHiDPI = true); 88 89 /** 90 Updates the display, which depending on the current mode could mean 91 drawing the TIA, any pending menus, etc. 92 */ 93 void update(UpdateMode mode = UpdateMode::NONE); 94 95 /** 96 There is a dedicated update method for emulation mode. 97 */ 98 void updateInEmulationMode(float framesPerSecond); 99 100 /** 101 Set pending rendering flag. 102 */ setPendingRender()103 void setPendingRender() { myPendingRender = true; } 104 105 /** 106 Shows a text message onscreen. 107 108 @param message The message to be shown 109 @param position Onscreen position for the message 110 @param force Force showing this message, even if messages are disabled 111 */ 112 void showTextMessage(const string& message, 113 MessagePosition position = MessagePosition::BottomCenter, 114 bool force = false); 115 /** 116 Shows a message with a gauge bar onscreen. 117 118 @param message The message to be shown 119 @param valueText The value of the gauge bar as text 120 @param value The gauge bar percentage 121 @param minValue The minimal value of the gauge bar 122 @param maxValue The maximal value of the gauge bar 123 */ 124 void showGaugeMessage(const string& message, const string& valueText, 125 float value, float minValue = 0.F, float maxValue = 100.F); 126 127 bool messageShown() const; 128 129 /** 130 Toggles showing or hiding framerate statistics. 131 */ 132 void toggleFrameStats(bool toggle = true); 133 134 /** 135 Shows a message containing frame statistics for the current frame. 136 */ 137 void showFrameStats(bool enable); 138 139 /** 140 Enable/disable any pending messages. Disabled messages aren't removed 141 from the message queue; they're just not redrawn into the framebuffer. 142 */ 143 void enableMessages(bool enable); 144 145 /** 146 Reset 'Paused' display delay counter 147 */ 148 void setPauseDelay(); 149 150 /** 151 Allocate a new surface. The FrameBuffer class takes all responsibility 152 for freeing this surface (ie, other classes must not delete it directly). 153 154 @param w The requested width of the new surface 155 @param h The requested height of the new surface 156 @param inter Interpolation mode 157 @param data If non-null, use the given data values as a static surface 158 159 @return A pointer to a valid surface object, or nullptr 160 */ 161 shared_ptr<FBSurface> allocateSurface( 162 int w, 163 int h, 164 ScalingInterpolation inter = ScalingInterpolation::none, 165 const uInt32* data = nullptr 166 ); 167 168 /** 169 Deallocate a previously allocated surface. If no such surface exists, 170 this method does nothing. 171 172 @param surface The surface to remove/deallocate 173 */ 174 void deallocateSurface(shared_ptr<FBSurface> surface); 175 176 /** 177 Set up the TIA/emulation palette. Due to the way the palette is stored, 178 a call to this method implicitly calls setUIPalette() too. 179 180 @param rgb_palette The array of colors in R/G/B format 181 */ 182 void setTIAPalette(const PaletteArray& rgb_palette); 183 184 /** 185 Set palette for user interface. 186 */ 187 void setUIPalette(); 188 189 /** 190 Returns the current dimensions of the framebuffer image. 191 Note that this will take into account the current scaling (if any) 192 as well as image 'centering'. 193 */ imageRect() const194 const Common::Rect& imageRect() const { return myActiveVidMode.imageR; } 195 196 /** 197 Returns the current dimensions of the framebuffer window. 198 This is the entire area containing the framebuffer image as well as any 199 'unusable' area. 200 */ screenSize() const201 const Common::Size& screenSize() const { return myActiveVidMode.screenS; } screenRect() const202 const Common::Rect& screenRect() const { return myActiveVidMode.screenR; } 203 204 /** 205 Returns the dimensions of the mode specific users' desktop. 206 */ desktopSize(BufferType bufferType) const207 const Common::Size& desktopSize(BufferType bufferType) const { 208 return myDesktopSize[displayId(bufferType)]; 209 } 210 211 /** 212 Get the supported renderers for the video hardware. 213 214 @return An array of supported renderers 215 */ supportedRenderers() const216 const VariantList& supportedRenderers() const { return myRenderers; } 217 218 /** 219 Get the minimum/maximum supported TIA zoom level (windowed mode) 220 for the framebuffer. 221 */ supportedTIAMinZoom() const222 float supportedTIAMinZoom() const { return myTIAMinZoom * hidpiScaleFactor(); } supportedTIAMaxZoom() const223 float supportedTIAMaxZoom() const { return maxWindowZoom(); } 224 225 /** 226 Get the TIA surface associated with the framebuffer. 227 */ tiaSurface() const228 TIASurface& tiaSurface() const { return *myTIASurface; } 229 230 /** 231 Toggles between fullscreen and window mode. 232 */ 233 void toggleFullscreen(bool toggle = true); 234 235 #ifdef ADAPTABLE_REFRESH_SUPPORT 236 /** 237 Toggles between adapt fullscreen refresh rate on and off. 238 */ 239 void toggleAdaptRefresh(bool toggle = true); 240 #endif 241 242 /** 243 Changes the fullscreen overscan. 244 245 @param direction +1 indicates increase, -1 indicates decrease 246 */ 247 void changeOverscan(int direction = +1); 248 249 /** 250 This method is called when the user wants to switch to the previous/next 251 available TIA video mode. In windowed mode, this typically means going 252 to the next/previous zoom level. In fullscreen mode, this typically 253 means switching between normal aspect and fully filling the screen. 254 255 @param direction +1 indicates next mode, -1 indicates previous mode 256 */ 257 void switchVideoMode(int direction = +1); 258 259 /** 260 Sets the state of the cursor (hidden or grabbed) based on the 261 current mode. 262 */ 263 void setCursorState(); 264 265 /** 266 Checks if mouse grabbing is allowed. 267 */ 268 bool grabMouseAllowed(); 269 270 /** 271 Sets the use of grabmouse. 272 */ 273 void enableGrabMouse(bool enable); 274 275 /** 276 Toggles the use of grabmouse (only has effect in emulation mode). 277 */ 278 void toggleGrabMouse(bool toggle = true); 279 280 /** 281 Query whether grabmouse is enabled. 282 */ grabMouseEnabled() const283 bool grabMouseEnabled() const { return myGrabMouse; } 284 285 /** 286 Informs the Framebuffer of a change in EventHandler state. 287 */ 288 void stateChanged(EventHandlerState state); 289 290 /** 291 Answer whether hidpi mode is allowed. In this mode, all FBSurfaces 292 are scaled to 2x normal size. 293 */ hidpiAllowed() const294 bool hidpiAllowed() const { return myHiDPIAllowed[displayId()]; } 295 296 /** 297 Answer whether hidpi mode is enabled. In this mode, all FBSurfaces 298 are scaled to 2x normal size. 299 */ hidpiEnabled() const300 bool hidpiEnabled() const { return myHiDPIEnabled[displayId()]; } hidpiScaleFactor() const301 uInt32 hidpiScaleFactor() const { return myHiDPIEnabled[displayId()] ? 2 : 1; } 302 303 /** 304 This method should be called to save the current settings of all 305 its subsystems. Note that the this may be called when the class 306 hasn't been fully initialized, so we first need to check if the 307 subsytems actually exist. 308 */ 309 void saveConfig(Settings& settings) const; 310 311 #ifdef GUI_SUPPORT 312 /** 313 Get the font object(s) of the framebuffer 314 */ font() const315 const GUI::Font& font() const { return *myFont; } infoFont() const316 const GUI::Font& infoFont() const { return *myInfoFont; } smallFont() const317 const GUI::Font& smallFont() const { return *mySmallFont; } launcherFont() const318 const GUI::Font& launcherFont() const { return *myLauncherFont; } 319 320 /** 321 Get the font description from the font name 322 323 @param name The settings name of the font 324 325 @return The description of the font 326 */ 327 FontDesc getFontDesc(const string& name) const; 328 #endif 329 330 /** 331 Shows or hides the cursor based on the given boolean value. 332 */ showCursor(bool show)333 void showCursor(bool show) { myBackend->showCursor(show); } 334 335 /** 336 Answers if the display is currently in fullscreen mode. 337 */ fullScreen() const338 bool fullScreen() const { return myBackend->fullScreen(); } 339 340 /** 341 This method is called to retrieve the R/G/B data from the given pixel. 342 343 @param pixel The pixel containing R/G/B data 344 @param r The red component of the color 345 @param g The green component of the color 346 @param b The blue component of the color 347 */ getRGB(uInt32 pixel,uInt8 * r,uInt8 * g,uInt8 * b) const348 void getRGB(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b) const { 349 myBackend->getRGB(pixel, r, g, b); 350 } 351 352 /** 353 This method is called to map a given R/G/B triple to the screen palette. 354 355 @param r The red component of the color. 356 @param g The green component of the color. 357 @param b The blue component of the color. 358 */ mapRGB(uInt8 r,uInt8 g,uInt8 b) const359 uInt32 mapRGB(uInt8 r, uInt8 g, uInt8 b) const { 360 return myBackend->mapRGB(r, g, b); 361 } 362 363 /** 364 This method is called to get the specified ARGB data from the viewable 365 FrameBuffer area. Note that this isn't the same as any internal 366 surfaces that may be in use; it should return the actual data as it 367 is currently seen onscreen. 368 369 @param buffer The actual pixel data in ARGB8888 format 370 @param pitch The pitch (in bytes) for the pixel data 371 @param rect The bounding rectangle for the buffer 372 */ readPixels(uInt8 * buffer,uInt32 pitch,const Common::Rect & rect) const373 void readPixels(uInt8* buffer, uInt32 pitch, const Common::Rect& rect) const { 374 myBackend->readPixels(buffer, pitch, rect); 375 } 376 377 /** 378 Clear the framebuffer. 379 */ clear()380 void clear() { myBackend->clear(); } 381 382 /** 383 Transform from window to renderer coordinates, x/y direction 384 */ scaleX(int x) const385 int scaleX(int x) const { return myBackend->scaleX(x); } scaleY(int y) const386 int scaleY(int y) const { return myBackend->scaleY(y); } 387 388 private: 389 /** 390 These methods are used to load/save position and display of the 391 current window. 392 */ 393 string getPositionKey() const; 394 string getDisplayKey(BufferType bufferType = BufferType::None) const; 395 void saveCurrentWindowPosition() const; 396 397 /** 398 Frees and reloads all surfaces that the framebuffer knows about. 399 */ 400 void resetSurfaces(); 401 402 #ifdef GUI_SUPPORT 403 /** 404 Helps to create a basic message onscreen. 405 406 @param message The message to be shown 407 @param position Onscreen position for the message 408 @param force Force showing this message, even if messages are disabled 409 */ 410 void createMessage(const string& message, MessagePosition position, 411 bool force = false); 412 #endif 413 414 /** 415 Draw pending messages. 416 417 @return Indicates whether any changes actually occurred. 418 */ 419 bool drawMessage(); 420 421 /** 422 Hide pending messages. 423 */ 424 void hideMessage(); 425 426 /** 427 Draws the frame stats overlay. 428 */ 429 void drawFrameStats(float framesPerSecond); 430 431 432 /** 433 Get the display used for the current mode. 434 */ 435 int displayId(BufferType bufferType = BufferType::None) const; 436 437 /** 438 Build an applicable video mode based on the current settings in 439 effect, whether TIA mode is active, etc. Then tell the backend 440 to actually use the new mode. 441 442 @return Whether the operation succeeded or failed 443 */ 444 FBInitStatus applyVideoMode(); 445 446 /** 447 Calculate the maximum level by which the base window can be zoomed and 448 still fit in the desktop screen. 449 */ 450 float maxWindowZoom() const; 451 452 /** 453 Enables/disables fullscreen mode. 454 */ 455 void setFullscreen(bool enable); 456 457 #ifdef GUI_SUPPORT 458 /** 459 Setup the UI fonts 460 */ 461 void setupFonts(); 462 #endif 463 464 private: 465 // The parent system for the framebuffer 466 OSystem& myOSystem; 467 468 // Backend used for all platform-specific graphics operations 469 unique_ptr<FBBackend> myBackend; 470 471 // Indicates the number of times the framebuffer was initialized 472 uInt32 myInitializedCount{0}; 473 474 // Used to set intervals between messages while in pause mode 475 Int32 myPausedCount{0}; 476 477 // Maximum dimensions of the desktop area 478 // Note that this takes 'hidpi' mode into account, so in some cases 479 // it will be less than the absolute desktop size 480 vector<Common::Size> myDesktopSize; 481 482 // Maximum absolute dimensions of the desktop area 483 vector<Common::Size> myAbsDesktopSize; 484 485 // The resolution of the attached displays in fullscreen mode 486 // The primary display is typically the first in the array 487 // Windowed modes use myDesktopSize directly 488 vector<Common::Size> myFullscreenDisplays; 489 490 // The resolution of the attached displays in windowed mode 491 vector<Common::Size> myWindowedDisplays; 492 493 // Supported renderers 494 VariantList myRenderers; 495 496 // Flag for pending render 497 bool myPendingRender{false}; 498 499 // The VideoModeHandler class takes responsibility for all video 500 // mode functionality 501 VideoModeHandler myVidModeHandler; 502 VideoModeHandler::Mode myActiveVidMode; 503 504 // Type of the frame buffer 505 BufferType myBufferType{BufferType::None}; 506 507 #ifdef GUI_SUPPORT 508 // The font object to use for the normal in-game GUI 509 unique_ptr<GUI::Font> myFont; 510 511 // The info font object to use for the normal in-game GUI 512 unique_ptr<GUI::Font> myInfoFont; 513 514 // The font object to use when space is very limited 515 unique_ptr<GUI::Font> mySmallFont; 516 517 // The font object to use for the ROM launcher 518 unique_ptr<GUI::Font> myLauncherFont; 519 #endif 520 521 // The TIASurface class takes responsibility for TIA rendering 522 unique_ptr<TIASurface> myTIASurface; 523 524 // Used for onscreen messages and frame statistics 525 // (scanline count and framerate) 526 struct Message { 527 string text; 528 int counter{-1}; 529 int x{0}, y{0}, w{0}, h{0}; 530 MessagePosition position{MessagePosition::BottomCenter}; 531 ColorId color{kNone}; 532 shared_ptr<FBSurface> surface; 533 bool enabled{false}; 534 bool dirty{false}; 535 bool showGauge{false}; 536 float value{0.0F}; 537 string valueText; 538 }; 539 Message myMsg; 540 Message myStatsMsg; 541 bool myStatsEnabled{false}; 542 uInt32 myLastScanlines{0}; 543 544 bool myGrabMouse{false}; 545 vector<bool> myHiDPIAllowed; 546 vector<bool> myHiDPIEnabled; 547 548 // Minimum TIA zoom level that can be used for this framebuffer 549 float myTIAMinZoom{2.F}; 550 551 // Holds a reference to all the surfaces that have been created 552 std::list<shared_ptr<FBSurface>> mySurfaceList; 553 554 // Maximum message width [chars] 555 static constexpr int MESSAGE_WIDTH = 56; 556 // Maximum gauge bar width [chars] 557 static constexpr int GAUGEBAR_WIDTH = 30; 558 559 FullPaletteArray myFullPalette; 560 // Holds UI palette data (for each variation) 561 static UIPaletteArray ourStandardUIPalette, ourClassicUIPalette, 562 ourLightUIPalette, ourDarkUIPalette; 563 564 private: 565 // Following constructors and assignment operators not supported 566 FrameBuffer() = delete; 567 FrameBuffer(const FrameBuffer&) = delete; 568 FrameBuffer(FrameBuffer&&) = delete; 569 FrameBuffer& operator=(const FrameBuffer&) = delete; 570 FrameBuffer& operator=(FrameBuffer&&) = delete; 571 }; 572 573 #endif 574