diff --git a/src/common/FBSurfaceSDL2.cxx b/src/common/FBSurfaceSDL2.cxx index 0756bd92d..551405c95 100644 --- a/src/common/FBSurfaceSDL2.cxx +++ b/src/common/FBSurfaceSDL2.cxx @@ -26,6 +26,7 @@ FBSurfaceSDL2::FBSurfaceSDL2(FrameBufferSDL2& buffer, mySurface(NULL), myTexture(NULL), mySurfaceIsDirty(true), + myIsVisible(true), myTexAccess(SDL_TEXTUREACCESS_STREAMING), myInterpolate(false), myBlendEnabled(false), @@ -149,6 +150,12 @@ void FBSurfaceSDL2::setDstSize(uInt32 w, uInt32 h) myDstR.w = w; myDstR.h = h; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void FBSurfaceSDL2::setVisible(bool visible) +{ + myIsVisible = visible; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void FBSurfaceSDL2::translateCoords(Int32& x, Int32& y) const { @@ -157,13 +164,14 @@ void FBSurfaceSDL2::translateCoords(Int32& x, Int32& y) const } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FBSurfaceSDL2::render() +bool FBSurfaceSDL2::render() { - if(mySurfaceIsDirty) + if(mySurfaceIsDirty && myIsVisible) { //cerr << "src: x=" << mySrcR.x << ", y=" << mySrcR.y << ", w=" << mySrcR.w << ", h=" << mySrcR.h << endl; //cerr << "dst: x=" << myDstR.x << ", y=" << myDstR.y << ", w=" << myDstR.w << ", h=" << myDstR.h << endl; +//cerr << "render()\n"; if(myTexAccess == SDL_TEXTUREACCESS_STREAMING) SDL_UpdateTexture(myTexture, &mySrcR, mySurface->pixels, mySurface->pitch); SDL_RenderCopy(myFB.myRenderer, myTexture, &mySrcR, &myDstR); @@ -171,8 +179,9 @@ void FBSurfaceSDL2::render() mySurfaceIsDirty = false; // Let postFrameUpdate() know that a change has been made - myFB.myDirtyFlag = true; + return myFB.myDirtyFlag = true; } + return false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/common/FBSurfaceSDL2.hxx b/src/common/FBSurfaceSDL2.hxx index 6d5484002..6c1f3adeb 100644 --- a/src/common/FBSurfaceSDL2.hxx +++ b/src/common/FBSurfaceSDL2.hxx @@ -42,8 +42,7 @@ class FBSurfaceSDL2 : public FBSurface void fillRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h, uInt32 color); void drawSurface(const FBSurface* surface); // With hardware surfaces, it's faster to just update the entire surface - void addDirtyRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h) { mySurfaceIsDirty = true; } - void addDirtyRect() { mySurfaceIsDirty = true; } + void setDirty() { mySurfaceIsDirty = true; } uInt32 width() const; uInt32 height() const; @@ -54,9 +53,10 @@ class FBSurfaceSDL2 : public FBSurface void setSrcSize(uInt32 w, uInt32 h); void setDstPos(uInt32 x, uInt32 y); void setDstSize(uInt32 w, uInt32 h); + void setVisible(bool visible); void translateCoords(Int32& x, Int32& y) const; - void render(); + bool render(); void invalidate(); void free(); void reload(); @@ -72,6 +72,7 @@ class FBSurfaceSDL2 : public FBSurface SDL_Rect mySrcR, myDstR; bool mySurfaceIsDirty; + bool myIsVisible; SDL_TextureAccess myTexAccess; // Is pixel data constant or can it change? bool myInterpolate; // Scaling is smoothed or blocky diff --git a/src/emucore/FBSurface.hxx b/src/emucore/FBSurface.hxx index c3e75b800..9a6aa2499 100644 --- a/src/emucore/FBSurface.hxx +++ b/src/emucore/FBSurface.hxx @@ -219,16 +219,10 @@ class FBSurface virtual void drawSurface(const FBSurface* surface); /** - This method should be called to add a dirty rectangle - (ie, an area of the screen that has changed) - - @param x The x coordinate - @param y The y coordinate - @param w The width of the area - @param h The height of the area + This method should be called to indicate that the surface has been + modified, and should be redrawn at the next interval. */ - virtual void addDirtyRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h) { } - virtual void addDirtyRect() { } + virtual void setDirty() { } ////////////////////////////////////////////////////////////////////////// // Note: The following methods are FBSurface-specific, and must be @@ -262,6 +256,12 @@ class FBSurface virtual void setDstPos(uInt32 x, uInt32 y) = 0; virtual void setDstSize(uInt32 w, uInt32 h) = 0; + /** + This method should be called to enable/disable showing the surface + (ie, if hidden it will not be drawn under any circumstances. + */ + virtual void setVisible(bool visible) = 0; + /** This method should be called to translate the given coordinates to the (destination) surface coordinates. @@ -273,8 +273,9 @@ class FBSurface /** This method should be called to draw the surface to the screen. + It will return true if rendering actually occurred. */ - virtual void render() = 0; + virtual bool render() = 0; /** This method should be called to reset the surface to empty diff --git a/src/emucore/FrameBuffer.cxx b/src/emucore/FrameBuffer.cxx index 0c7f5b034..08c1d0848 100644 --- a/src/emucore/FrameBuffer.cxx +++ b/src/emucore/FrameBuffer.cxx @@ -302,7 +302,7 @@ void FrameBuffer::update() msg, 1, 1, myStatsMsg.w, myStatsMsg.color, kTextAlignLeft); myStatsMsg.surface->drawString(infoFont(), info.BankSwitch, 1, 15, myStatsMsg.w, myStatsMsg.color, kTextAlignLeft); - myStatsMsg.surface->addDirtyRect(); // force a full draw + myStatsMsg.surface->setDirty(); myStatsMsg.surface->setDstPos(myImageRect.x() + 1, myImageRect.y() + 1); myStatsMsg.surface->render(); } @@ -486,7 +486,7 @@ inline void FrameBuffer::drawMessage() } else { - myMsg.surface->addDirtyRect(); + myMsg.surface->setDirty(); myMsg.surface->render(); } } diff --git a/src/emucore/TIASurface.cxx b/src/emucore/TIASurface.cxx index f86b4f03e..c8700c017 100644 --- a/src/emucore/TIASurface.cxx +++ b/src/emucore/TIASurface.cxx @@ -226,7 +226,7 @@ uInt32 TIASurface::enableScanlines(int relative, int absolute) intensity = BSPF_min(100u, intensity); mySLineSurface->applyAttributes(); - mySLineSurface->addDirtyRect(); + mySLineSurface->setDirty(); return intensity; } @@ -236,7 +236,7 @@ void TIASurface::enableScanlineInterpolation(bool enable) { mySLineSurface->myAttributes.smoothing = enable; mySLineSurface->applyAttributes(); - mySLineSurface->addDirtyRect(); + mySLineSurface->setDirty(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -245,8 +245,8 @@ void TIASurface::enablePhosphor(bool enable, int blend) myUsePhosphor = enable; myPhosphorBlend = blend; myFilterType = FilterType(enable ? myFilterType | 0x01 : myFilterType & 0x10); - myTiaSurface->addDirtyRect(); - mySLineSurface->addDirtyRect(); + myTiaSurface->setDirty(); + mySLineSurface->setDirty(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -278,8 +278,8 @@ void TIASurface::enableNTSC(bool enable) myOSystem.settings().getInt("tv.scanlines"); mySLineSurface->applyAttributes(); - myTiaSurface->addDirtyRect(); - mySLineSurface->addDirtyRect(); + myTiaSurface->setDirty(); + mySLineSurface->setDirty(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -375,13 +375,13 @@ void TIASurface::render() } // Draw TIA image - myTiaSurface->addDirtyRect(); + myTiaSurface->setDirty(); myTiaSurface->render(); // Draw overlaying scanlines if(myScanlinesEnabled) { - mySLineSurface->addDirtyRect(); + mySLineSurface->setDirty(); mySLineSurface->render(); } } diff --git a/src/gui/ContextMenu.cxx b/src/gui/ContextMenu.cxx index 7fc0b09b9..bb42fa526 100644 --- a/src/gui/ContextMenu.cxx +++ b/src/gui/ContextMenu.cxx @@ -590,7 +590,7 @@ void ContextMenu::drawDialog() s.drawBitmap(down_arrow, ((_w-_x)>>1)-4, (_rowHeight>>1)+y-4, _scrollDnColor, 8); } - s.addDirtyRect(_x, _y, _w, _h); + s.setDirty(); _dirty = false; } diff --git a/src/gui/Dialog.cxx b/src/gui/Dialog.cxx index c0f424e78..4ce63b38e 100644 --- a/src/gui/Dialog.cxx +++ b/src/gui/Dialog.cxx @@ -69,12 +69,6 @@ void Dialog::open(bool refresh) // Make sure we have a valid surface to draw into // Technically, this shouldn't be needed until drawDialog(), but some // dialogs cause drawing to occur within loadConfig() - // Base surfaces are typically large, and will probably cause slow - // performance if we update the whole area each frame - // Instead, dirty rectangle updates should be performed - // However, this policy is left entirely to the framebuffer - // We suggest the hint here, but specific framebuffers are free to - // ignore it if(_surface == NULL) { uInt32 surfaceID = instance().frameBuffer().allocateSurface(_w, _h); @@ -274,6 +268,12 @@ void Dialog::redrawFocus() _focusedWidget = Widget::setFocusForChain(this, getFocusList(), _focusedWidget, 0); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Dialog::addSurface(FBSurface* surface) +{ + mySurfaceStack.push(surface); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Dialog::draw() { @@ -290,7 +290,6 @@ void Dialog::drawDialog() if(_dirty) { // cerr << "Dialog::drawDialog(): w = " << _w << ", h = " << _h << " @ " << &s << endl << endl; - s.fillRect(_x, _y, _w, _h, kDlgColor); s.box(_x, _y, _w, _h, kColor, kShadowColor); @@ -309,13 +308,22 @@ void Dialog::drawDialog() // Draw outlines for focused widgets redrawFocus(); - // Tell the surface this area is dirty - s.addDirtyRect(_x, _y, _w, _h); + // Tell the surface(s) this area is dirty + s.setDirty(); + _dirty = false; } - // Commit surface changes to screen - s.render(); + // Commit surface changes to screen; also render any extra surfaces + // Extra surfaces must be rendered afterwards, so they are drawn on top + if(s.render()) + { + for(int i = 0; i < mySurfaceStack.size(); ++i) + { + mySurfaceStack[i]->setDirty(); + mySurfaceStack[i]->render(); + } + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/Dialog.hxx b/src/gui/Dialog.hxx index cb3a11a4b..ed72abb24 100644 --- a/src/gui/Dialog.hxx +++ b/src/gui/Dialog.hxx @@ -29,6 +29,7 @@ class DialogContainer; class TabWidget; #include "Command.hxx" +#include "Stack.hxx" #include "Widget.hxx" #include "GuiObject.hxx" #include "StellaKeys.hxx" @@ -72,8 +73,16 @@ class Dialog : public GuiObject void addCancelWidget(Widget* w) { _cancelWidget = w; } void setFocus(Widget* w); + /** Returns the base surface associated with this dialog. */ FBSurface& surface() const { return *_surface; } + /** Adds a surface to this dialog, which is rendered on top of the + base surface whenever the base surface is re-rendered. Since + the surface render() call will always occur in such a case, the + surface should call setVisible() to enable/disable its output. + */ + void addSurface(FBSurface* surface); + protected: virtual void draw(); void releaseFocus(); @@ -115,6 +124,8 @@ class Dialog : public GuiObject bool _visible; bool _processCancel; + Common::FixedStack mySurfaceStack; + private: struct Focus { Widget* widget; diff --git a/src/gui/RomInfoWidget.cxx b/src/gui/RomInfoWidget.cxx index 1dd0587a5..7486cf3bc 100644 --- a/src/gui/RomInfoWidget.cxx +++ b/src/gui/RomInfoWidget.cxx @@ -29,7 +29,6 @@ RomInfoWidget::RomInfoWidget(GuiObject* boss, const GUI::Font& font, int x, int y, int w, int h) : Widget(boss, font, x, y, w, h), mySurface(NULL), - mySurfaceID(-1), myZoomLevel(w > 400 ? 2 : 1), mySurfaceIsValid(false), myHaveProperties(false) @@ -75,6 +74,8 @@ void RomInfoWidget::setProperties(const Properties& props) void RomInfoWidget::clearProperties() { myHaveProperties = mySurfaceIsValid = false; + if(mySurface) + mySurface->setVisible(mySurfaceIsValid); // Decide whether the information should be shown immediately if(instance().eventHandler().state() == EventHandler::S_LAUNCHER) @@ -89,12 +90,12 @@ void RomInfoWidget::parseProperties() // Check if a surface has ever been created; if so, we use it // The surface will always be the maximum size, but sometimes we'll // only draw certain parts of it - mySurface = instance().frameBuffer().surface(mySurfaceID); if(mySurface == NULL) { - mySurfaceID = instance().frameBuffer().allocateSurface( - 320*myZoomLevel, 256*myZoomLevel); - mySurface = instance().frameBuffer().surface(mySurfaceID); + uInt32 ID = instance().frameBuffer().allocateSurface( + 320*myZoomLevel, 256*myZoomLevel); + mySurface = instance().frameBuffer().surface(ID); + dialog().addSurface(mySurface); } // Initialize to empty properties entry @@ -116,6 +117,8 @@ void RomInfoWidget::parseProperties() mySurfaceIsValid = false; mySurfaceErrorMsg = msg; } + if(mySurface) + mySurface->setVisible(mySurfaceIsValid); // Now add some info for the message box below the image myRomInfo.push_back("Name: " + myProperties.get(Cartridge_Name)); @@ -145,8 +148,11 @@ void RomInfoWidget::drawWidget(bool hilite) const GUI::Rect& src = mySurface->srcRect(); uInt32 x = _x + ((_w - src.width()) >> 1); uInt32 y = _y + ((yoff - src.height()) >> 1); - mySurface->setDstPos(x, y); - s.drawSurface(mySurface); + + // Make sure when positioning the snapshot surface that we take + // the dialog surface position into account + const GUI::Rect& s_dst = s.dstRect(); + mySurface->setDstPos(x + s_dst.x(), y + s_dst.y()); } else if(mySurfaceErrorMsg != "") { diff --git a/src/gui/RomInfoWidget.hxx b/src/gui/RomInfoWidget.hxx index de5f07fa9..7416162ed 100644 --- a/src/gui/RomInfoWidget.hxx +++ b/src/gui/RomInfoWidget.hxx @@ -47,9 +47,8 @@ class RomInfoWidget : public Widget void parseProperties(); private: - // Surface id and pointer holding the scaled PNG image + // Surface pointer holding the PNG image FBSurface* mySurface; - int mySurfaceID; // How much to zoom the PNG image int myZoomLevel; diff --git a/src/gui/Widget.cxx b/src/gui/Widget.cxx index d85eea480..6f036c1b0 100644 --- a/src/gui/Widget.cxx +++ b/src/gui/Widget.cxx @@ -75,7 +75,7 @@ void Widget::draw() FBSurface& s = _boss->dialog().surface(); bool hasBorder = _flags & WIDGET_BORDER; - int oldX = _x, oldY = _y, oldW = _w, oldH = _h; + int oldX = _x, oldY = _y; // Account for our relative position in the dialog _x = getAbsX(); @@ -93,7 +93,8 @@ void Widget::draw() } // Draw border - if(hasBorder) { + if(hasBorder) + { s.box(_x, _y, _w, _h, kColor, kShadowColor); _x += 4; _y += 4; @@ -105,7 +106,8 @@ void Widget::draw() drawWidget((_flags & WIDGET_HILITED) ? true : false); // Restore x/y - if (hasBorder) { + if (hasBorder) + { _x -= 4; _y -= 4; _w += 8; @@ -124,7 +126,7 @@ void Widget::draw() } // Tell the framebuffer this area is dirty - s.addDirtyRect(getAbsX(), getAbsY(), oldW, oldH); + s.setDirty(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -226,7 +228,7 @@ Widget* Widget::setFocusForChain(GuiObject* boss, WidgetArray& arr, s.frameRect(x, y, w, h, kDlgColor); tmp->setDirty(); tmp->draw(); - s.addDirtyRect(x, y, w, h); + s.setDirty(); } } @@ -269,7 +271,7 @@ Widget* Widget::setFocusForChain(GuiObject* boss, WidgetArray& arr, s.frameRect(x, y, w, h, kWidFrameColor, kDashLine); tmp->setDirty(); tmp->draw(); - s.addDirtyRect(x, y, w, h); + s.setDirty(); return tmp; }