From 9cbaad61405f0a4c8b88eb4e6637b84a12633c1a Mon Sep 17 00:00:00 2001 From: Bastien Bouclet Date: Tue, 13 Sep 2016 20:35:05 +0200 Subject: SDL: Switch the OpenGL renderer to use small textures to draw the OSD --- backends/graphics/opengl/opengl-graphics.cpp | 209 ++++++++++++++++++--------- backends/graphics/opengl/opengl-graphics.h | 77 ++++++++-- 2 files changed, 201 insertions(+), 85 deletions(-) (limited to 'backends/graphics/opengl') diff --git a/backends/graphics/opengl/opengl-graphics.cpp b/backends/graphics/opengl/opengl-graphics.cpp index 8861d364e6..3e2be6e8cd 100644 --- a/backends/graphics/opengl/opengl-graphics.cpp +++ b/backends/graphics/opengl/opengl-graphics.cpp @@ -57,7 +57,8 @@ OpenGLGraphicsManager::OpenGLGraphicsManager() _cursorKeyColor(0), _cursorVisible(false), _cursorDontScale(false), _cursorPaletteEnabled(false), _forceRedraw(false), _scissorOverride(3) #ifdef USE_OSD - , _osdAlpha(0), _osdFadeStartTime(0), _osd(nullptr) + , _osdMessageChangeRequest(false), _osdMessageAlpha(0), _osdMessageFadeStartTime(0), _osdMessageSurface(nullptr), + _osdIconChangeRequest(false), _osdIconSurface(nullptr) #endif { memset(_gamePalette, 0, sizeof(_gamePalette)); @@ -69,7 +70,9 @@ OpenGLGraphicsManager::~OpenGLGraphicsManager() { delete _overlay; delete _cursor; #ifdef USE_OSD - delete _osd; + delete _osdMessageSurface; + delete _osdIconSurface; + _osdIconNextData.free(); #endif #if !USE_FORCED_GLES ShaderManager::destroy(); @@ -362,13 +365,27 @@ void OpenGLGraphicsManager::updateScreen() { return; } +#ifdef USE_OSD + { + Common::StackLock lock(_osdMutex); + + if (_osdMessageChangeRequest) { + osdMessageUpdateSurface(); + } + + if (_osdIconChangeRequest) { + osdIconUpdateSurface(); + } + } +#endif + // We only update the screen when there actually have been any changes. if ( !_forceRedraw && !_gameScreen->isDirty() && !(_overlayVisible && _overlay->isDirty()) && !(_cursorVisible && _cursor && _cursor->isDirty()) #ifdef USE_OSD - && _osdAlpha == 0 + && !_osdMessageSurface && !_osdIconSurface #endif ) { return; @@ -381,9 +398,6 @@ void OpenGLGraphicsManager::updateScreen() { _cursor->updateGLTexture(); } _overlay->updateGLTexture(); -#ifdef USE_OSD - _osd->updateGLTexture(); -#endif // Clear the screen buffer. if (_scissorOverride && !_overlayVisible) { @@ -424,29 +438,45 @@ void OpenGLGraphicsManager::updateScreen() { #ifdef USE_OSD // Fourth step: Draw the OSD. - if (_osdAlpha > 0) { - Common::StackLock lock(_osdMutex); - + if (_osdMessageSurface) { // Update alpha value. - const int diff = g_system->getMillis(false) - _osdFadeStartTime; + const int diff = g_system->getMillis(false) - _osdMessageFadeStartTime; if (diff > 0) { - if (diff >= kOSDFadeOutDuration) { + if (diff >= kOSDMessageFadeOutDuration) { // Back to full transparency. - _osdAlpha = 0; + _osdMessageAlpha = 0; } else { // Do a fade out. - _osdAlpha = kOSDInitialAlpha - diff * kOSDInitialAlpha / kOSDFadeOutDuration; + _osdMessageAlpha = kOSDMessageInitialAlpha - diff * kOSDMessageInitialAlpha / kOSDMessageFadeOutDuration; } } // Set the OSD transparency. - g_context.getActivePipeline()->setColor(1.0f, 1.0f, 1.0f, _osdAlpha / 100.0f); + g_context.getActivePipeline()->setColor(1.0f, 1.0f, 1.0f, _osdMessageAlpha / 100.0f); + + int dstX = (_outputScreenWidth - _osdMessageSurface->getWidth()) / 2; + int dstY = (_outputScreenHeight - _osdMessageSurface->getHeight()) / 2; // Draw the OSD texture. - g_context.getActivePipeline()->drawTexture(_osd->getGLTexture(), 0, 0, _outputScreenWidth, _outputScreenHeight); + g_context.getActivePipeline()->drawTexture(_osdMessageSurface->getGLTexture(), + dstX, dstY, _osdMessageSurface->getWidth(), _osdMessageSurface->getHeight()); // Reset color. g_context.getActivePipeline()->setColor(1.0f, 1.0f, 1.0f, 1.0f); + + if (_osdMessageAlpha <= 0) { + delete _osdMessageSurface; + _osdMessageSurface = nullptr; + } + } + + if (_osdIconSurface) { + int dstX = _outputScreenWidth - _osdIconSurface->getWidth() - kOSDIconRightMargin; + int dstY = kOSDIconTopMargin; + + // Draw the OSD icon texture. + g_context.getActivePipeline()->drawTexture(_osdIconSurface->getGLTexture(), + dstX, dstY, _osdIconSurface->getWidth(), _osdIconSurface->getHeight()); } #endif @@ -703,85 +733,130 @@ void OpenGLGraphicsManager::setCursorPalette(const byte *colors, uint start, uin void OpenGLGraphicsManager::displayMessageOnOSD(const char *msg) { #ifdef USE_OSD // HACK: Actually no client code should use graphics functions from - // another thread. But the MT-32 emulator still does, thus we need to - // make sure this doesn't happen while a updateScreen call is done. + // another thread. But the MT-32 emulator and network synchronization still do, + // thus we need to make sure this doesn't happen while a updateScreen call is done. Common::StackLock lock(_osdMutex); - // Slip up the lines. + _osdMessageChangeRequest = true; + + _osdMessageNextData = msg; +#endif +} + +#ifdef USE_OSD +void OpenGLGraphicsManager::osdMessageUpdateSurface() { + // Split up the lines. Common::Array osdLines; - Common::StringTokenizer tokenizer(msg, "\n"); + Common::StringTokenizer tokenizer(_osdMessageNextData, "\n"); while (!tokenizer.empty()) { osdLines.push_back(tokenizer.nextToken()); } // Do the actual drawing like the SDL backend. const Graphics::Font *font = getFontOSD(); - Graphics::Surface *dst = _osd->getSurface(); - _osd->fill(0); - _osd->flagDirty(); // Determine a rect which would contain the message string (clipped to the // screen dimensions). const int vOffset = 6; const int lineSpacing = 1; const int lineHeight = font->getFontHeight() + 2 * lineSpacing; - int width = 0; - int height = lineHeight * osdLines.size() + 2 * vOffset; + uint width = 0; + uint height = lineHeight * osdLines.size() + 2 * vOffset; for (uint i = 0; i < osdLines.size(); i++) { - width = MAX(width, font->getStringWidth(osdLines[i]) + 14); + width = MAX(width, font->getStringWidth(osdLines[i]) + 14); } // Clip the rect - width = MIN(width, dst->w); - height = MIN(height, dst->h); + width = MIN(width, _displayWidth); + height = MIN(height, _displayHeight); + + delete _osdMessageSurface; + _osdMessageSurface = nullptr; + + _osdMessageSurface = createSurface(_defaultFormatAlpha); + assert(_osdMessageSurface); + // We always filter the osd with GL_LINEAR. This assures it's + // readable in case it needs to be scaled and does not affect it + // otherwise. + _osdMessageSurface->enableLinearFiltering(true); + + _osdMessageSurface->allocate(width, height); - int dstX = (dst->w - width) / 2; - int dstY = (dst->h - height) / 2; + Graphics::Surface *dst = _osdMessageSurface->getSurface(); // Draw a dark gray rect. const uint32 color = dst->format.RGBToColor(40, 40, 40); - dst->fillRect(Common::Rect(dstX, dstY, dstX + width, dstY + height), color); + dst->fillRect(Common::Rect(0, 0, width, height), color); - // Render the message, centered, and in white + // Render the message in white const uint32 white = dst->format.RGBToColor(255, 255, 255); for (uint i = 0; i < osdLines.size(); ++i) { font->drawString(dst, osdLines[i], - dstX, dstY + i * lineHeight + vOffset + lineSpacing, width, + 0, i * lineHeight + vOffset + lineSpacing, width, white, Graphics::kTextAlignCenter); } + _osdMessageSurface->updateGLTexture(); + // Init the OSD display parameters. - _osdAlpha = kOSDInitialAlpha; - _osdFadeStartTime = g_system->getMillis() + kOSDFadeOutDelay; -#endif -} + _osdMessageAlpha = kOSDMessageInitialAlpha; + _osdMessageFadeStartTime = g_system->getMillis() + kOSDMessageFadeOutDelay; -void OpenGLGraphicsManager::copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h) { -#ifdef USE_OSD - _osd->copyRectToTexture(x, y, w, h, buf, pitch); -#endif + // Clear the text update request + _osdMessageNextData.clear(); + _osdMessageChangeRequest = false; } +#endif -void OpenGLGraphicsManager::clearOSD() { +void OpenGLGraphicsManager::displayActivityIconOnOSD(const Graphics::Surface *icon) { #ifdef USE_OSD // HACK: Actually no client code should use graphics functions from - // another thread. But the MT-32 emulator still does, thus we need to - // make sure this doesn't happen while a updateScreen call is done. + // another thread. But the MT-32 emulator and network synchronization still do, + // thus we need to make sure this doesn't happen while a updateScreen call is done. + // HACK: We can't make OpenGL calls outside of the main thread. This method + // stores a copy of the icon. The main thread will pick up the changed icon, + // and copy it to an OpenGL texture. Common::StackLock lock(_osdMutex); - Graphics::Surface *dst = _osd->getSurface(); - _osd->fill(0); - _osd->flagDirty(); + _osdIconChangeRequest = true; - // Init the OSD display parameters. - _osdAlpha = kOSDInitialAlpha; - _osdFadeStartTime = g_system->getMillis() + kOSDFadeOutDelay; + _osdIconNextData.free(); + _osdIconNextData.copyFrom(*icon); #endif } -Graphics::PixelFormat OpenGLGraphicsManager::getOSDFormat() { - return _defaultFormatAlpha; +#ifdef USE_OSD +void OpenGLGraphicsManager::osdIconUpdateSurface() { + delete _osdIconSurface; + _osdIconSurface = nullptr; + + if (_osdIconNextData.getPixels()) { + Graphics::Surface *converted = _osdIconNextData.convertTo(_defaultFormatAlpha); + _osdIconNextData.free(); + + _osdIconSurface = createSurface(_defaultFormatAlpha); + assert(_osdIconSurface); + // We always filter the osd with GL_LINEAR. This assures it's + // readable in case it needs to be scaled and does not affect it + // otherwise. + _osdIconSurface->enableLinearFiltering(true); + + _osdIconSurface->allocate(converted->w, converted->h); + + Graphics::Surface *dst = _osdIconSurface->getSurface(); + + // Copy the icon to the texture + dst->copyRectToSurface(*converted, 0, 0, Common::Rect(0, 0, converted->w, converted->h)); + + converted->free(); + delete converted; + + _osdIconSurface->updateGLTexture(); + } + + _osdIconChangeRequest = false; } +#endif void OpenGLGraphicsManager::setPalette(const byte *colors, uint start, uint num) { assert(_gameScreen->hasPalette()); @@ -849,22 +924,6 @@ void OpenGLGraphicsManager::setActualScreenSize(uint width, uint height) { _overlay->allocate(overlayWidth, overlayHeight); _overlay->fill(0); -#ifdef USE_OSD - if (!_osd || _osd->getFormat() != _defaultFormatAlpha) { - delete _osd; - _osd = nullptr; - - _osd = createSurface(_defaultFormatAlpha); - assert(_osd); - // We always filter the osd with GL_LINEAR. This assures it's - // readable in case it needs to be scaled and does not affect it - // otherwise. - _osd->enableLinearFiltering(true); - } - _osd->allocate(_overlay->getWidth(), _overlay->getHeight()); - _osd->fill(0); -#endif - // Re-setup the scaling for the screen and cursor recalculateDisplayArea(); recalculateCursorScaling(); @@ -949,8 +1008,12 @@ void OpenGLGraphicsManager::notifyContextCreate(const Graphics::PixelFormat &def } #ifdef USE_OSD - if (_osd) { - _osd->recreate(); + if (_osdMessageSurface) { + _osdMessageSurface->recreate(); + } + + if (_osdIconSurface) { + _osdIconSurface->recreate(); } #endif } @@ -969,8 +1032,12 @@ void OpenGLGraphicsManager::notifyContextDestroy() { } #ifdef USE_OSD - if (_osd) { - _osd->destroy(); + if (_osdMessageSurface) { + _osdMessageSurface->destroy(); + } + + if (_osdIconSurface) { + _osdIconSurface->destroy(); } #endif diff --git a/backends/graphics/opengl/opengl-graphics.h b/backends/graphics/opengl/opengl-graphics.h index 55d2c5c826..366ad48fad 100644 --- a/backends/graphics/opengl/opengl-graphics.h +++ b/backends/graphics/opengl/opengl-graphics.h @@ -30,6 +30,8 @@ #include "common/frac.h" #include "common/mutex.h" +#include "graphics/surface.h" + namespace Graphics { class Font; } // End of namespace Graphics @@ -115,9 +117,7 @@ public: virtual void setCursorPalette(const byte *colors, uint start, uint num); virtual void displayMessageOnOSD(const char *msg); - virtual void copyRectToOSD(const void *buf, int pitch, int x, int y, int w, int h); - virtual void clearOSD(); - virtual Graphics::PixelFormat getOSDFormat(); + virtual void displayActivityIconOnOSD(const Graphics::Surface *icon); // PaletteManager interface virtual void setPalette(const byte *colors, uint start, uint num); @@ -548,30 +548,79 @@ protected: private: /** - * The OSD's contents. + * Request for the OSD icon surface to be updated. */ - Surface *_osd; + bool _osdMessageChangeRequest; /** - * Current opacity level of the OSD. + * The next OSD message. + * + * If this value is not empty, the OSD message will be set + * to it on the next frame. */ - uint8 _osdAlpha; + Common::String _osdMessageNextData; /** - * When fading the OSD has started. + * Set the OSD message surface with the value of the next OSD message. */ - uint32 _osdFadeStartTime; + void osdMessageUpdateSurface(); /** - * Mutex to allow displayMessageOnOSD to be used from the audio thread. + * The OSD message's contents. */ - Common::Mutex _osdMutex; + Surface *_osdMessageSurface; + + /** + * Current opacity level of the OSD message. + */ + uint8 _osdMessageAlpha; + + /** + * When fading the OSD message has started. + */ + uint32 _osdMessageFadeStartTime; + + enum { + kOSDMessageFadeOutDelay = 2 * 1000, + kOSDMessageFadeOutDuration = 500, + kOSDMessageInitialAlpha = 80 + }; + + /** + * Request for the OSD icon surface to be updated. + */ + bool _osdIconChangeRequest; + + /** + * The next OSD background activity icon. + * + * The OSD icon will be updated with this data on the next frame. + * Can be an unallocated surface if the OSD icon should not be displayed. + */ + Graphics::Surface _osdIconNextData; + + /** + * Set the OSD icon surface with the value of the next OSD icon. + */ + void osdIconUpdateSurface(); + + /** + * The OSD background activity icon's contents. + */ + Surface *_osdIconSurface; enum { - kOSDFadeOutDelay = 2 * 1000, - kOSDFadeOutDuration = 500, - kOSDInitialAlpha = 80 + kOSDIconTopMargin = 10, + kOSDIconRightMargin = 10 }; + + /** + * Mutex for the OSD draw calls. + * + * Mutex to allow displayMessageOnOSD and displayActivityIconOnOSD + * to be used from the audio and network threads. + */ + Common::Mutex _osdMutex; #endif }; -- cgit v1.2.3