From b08ab0e1307460460c8923af4f5d8d134900c489 Mon Sep 17 00:00:00 2001 From: Michael Ball Date: Sat, 25 Jan 2020 10:06:14 -0800 Subject: [PATCH] 3DS: Implement dynamic graphics modes to improve performance When launching a game, switch the graphics mode if necessary (and by extension the pixel formats used for Graphics::Surfaces and Sprites) to the one that most closely matches the pixel format used in-game. Additional Fixes: - Fix to prevent cursor position from changing when exiting a menu. - Fix to prevent updating of Magnification viewport position when virtual keyboard is open. - Cosmetic code fixes for improper whitespace and missing curly brackets. - Remove RGB8 as a mode option because: 1) It was already commented out in the master 3DS backend. 2) There are currently no games that explicitly require it. Notes: - As these graphics modes are automatically implemented on a per-game basis, they are meant for backend use only and are purposefully not accessible through the Options menu. - RGBA8 (aka RGBA8888) remains the default pixel format, being used for the launcher menu, CLUT8, and for games which do not specify a particular format. --- backends/platform/3ds/config.cpp | 15 +- backends/platform/3ds/main.cpp | 2 +- backends/platform/3ds/osystem-audio.cpp | 6 +- backends/platform/3ds/osystem-events.cpp | 35 ++- backends/platform/3ds/osystem-graphics.cpp | 277 +++++++++++++++------ backends/platform/3ds/osystem.cpp | 21 +- backends/platform/3ds/osystem.h | 60 ++++- backends/platform/3ds/sprite.cpp | 17 +- backends/platform/3ds/sprite.h | 12 +- 9 files changed, 328 insertions(+), 117 deletions(-) diff --git a/backends/platform/3ds/config.cpp b/backends/platform/3ds/config.cpp index 663c6aede8f..4c567c29203 100644 --- a/backends/platform/3ds/config.cpp +++ b/backends/platform/3ds/config.cpp @@ -22,9 +22,9 @@ #define FORBIDDEN_SYMBOL_EXCEPTION_time_h -#include "config.h" -#include "osystem.h" -#include "options-dialog.h" +#include "backends/platform/3ds/config.h" +#include "backends/platform/3ds/osystem.h" +#include "backends/platform/3ds/options-dialog.h" #include "common/config-manager.h" namespace _3DS { @@ -33,8 +33,9 @@ Config config; static Common::String prefix = "3ds_"; static bool confGetBool(Common::String key, bool defaultVal) { - if (ConfMan.hasKey(prefix + key)) + if (ConfMan.hasKey(prefix + key)) { return ConfMan.getBool(prefix + key); + } return defaultVal; } @@ -43,8 +44,9 @@ static void confSetBool(Common::String key, bool val) { } static int confGetInt(Common::String key, int defaultVal) { - if (ConfMan.hasKey(prefix + key)) + if (ConfMan.hasKey(prefix + key)) { return ConfMan.getInt(prefix + key); + } return defaultVal; } @@ -67,8 +69,9 @@ void loadConfig() { } else if (config.screen == kScreenBottom) { GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTTOM); GSPLCD_PowerOffBacklight(GSPLCD_SCREEN_TOP); - } else + } else { GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTH); + } gspLcdExit(); } diff --git a/backends/platform/3ds/main.cpp b/backends/platform/3ds/main.cpp index 41799671782..e003d2ce3bc 100644 --- a/backends/platform/3ds/main.cpp +++ b/backends/platform/3ds/main.cpp @@ -20,7 +20,7 @@ * */ -#include "osystem.h" +#include "backends/platform/3ds/osystem.h" #include "backends/plugins/3ds/3ds-provider.h" #include <3ds.h> diff --git a/backends/platform/3ds/osystem-audio.cpp b/backends/platform/3ds/osystem-audio.cpp index 3434b53fd30..6f16a58ad12 100644 --- a/backends/platform/3ds/osystem-audio.cpp +++ b/backends/platform/3ds/osystem-audio.cpp @@ -20,7 +20,7 @@ * */ -#include "osystem.h" +#include "backends/platform/3ds/osystem.h" #include "audio/mixer.h" namespace _3DS { @@ -55,7 +55,9 @@ static void audioThreadFunc(void *arg) { while (!osys->exiting) { svcSleepThread(5000 * 1000); // Wake up the thread every 5 ms - if (osys->sleeping) continue; + if (osys->sleeping) { + continue; + } ndspWaveBuf *buf = &buffers[bufferIndex]; if (buf->status == NDSP_WBUF_FREE || buf->status == NDSP_WBUF_DONE) { diff --git a/backends/platform/3ds/osystem-events.cpp b/backends/platform/3ds/osystem-events.cpp index 0eb31df2a68..a3947411689 100644 --- a/backends/platform/3ds/osystem-events.cpp +++ b/backends/platform/3ds/osystem-events.cpp @@ -108,10 +108,12 @@ static void eventThreadFunc(void *arg) { // C-Pad used to control the cursor hidCircleRead(&circle); - if (circle.dx < circleDeadzone && circle.dx > -circleDeadzone) + if (circle.dx < circleDeadzone && circle.dx > -circleDeadzone) { circle.dx = 0; - if (circle.dy < circleDeadzone && circle.dy > -circleDeadzone) + } + if (circle.dy < circleDeadzone && circle.dy > -circleDeadzone) { circle.dy = 0; + } cursorDeltaX = (0.0002f + config.sensitivity / 100000.f) * circle.dx * abs(circle.dx); cursorDeltaY = (0.0002f + config.sensitivity / 100000.f) * circle.dy * abs(circle.dy); @@ -119,14 +121,18 @@ static void eventThreadFunc(void *arg) { if (held & KEY_TOUCH) { hidTouchRead(&touch); if (config.snapToBorder) { - if (touch.px < borderSnapZone) + if (touch.px < borderSnapZone) { touch.px = 0; - if (touch.px > 319 - borderSnapZone) + } + if (touch.px > 319 - borderSnapZone) { touch.px = 319; - if (touch.py < borderSnapZone) + } + if (touch.py < borderSnapZone) { touch.py = 0; - if (touch.py > 239 - borderSnapZone) + } + if (touch.py > 239 - borderSnapZone) { touch.py = 239; + } } osys->transformPoint(touch); @@ -201,8 +207,9 @@ static void aptHookFunc(APT_HookType hookType, void *param) { switch (hookType) { case APTHOOK_ONSUSPEND: case APTHOOK_ONSLEEP: - if (g_engine) + if (g_engine) { g_engine->pauseEngine(true); + } osys->sleeping = true; if (R_SUCCEEDED(gspLcdInit())) { GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTH); @@ -211,8 +218,9 @@ static void aptHookFunc(APT_HookType hookType, void *param) { break; case APTHOOK_ONRESTORE: case APTHOOK_ONWAKEUP: - if (g_engine) + if (g_engine) { g_engine->pauseEngine(false); + } osys->sleeping = false; loadConfig(); break; @@ -348,8 +356,9 @@ bool OSystem_3DS::pollEvent(Common::Event &event) { Common::StackLock lock(*eventMutex); - if (_eventQueue.empty()) + if (_eventQueue.empty()) { return false; + } event = _eventQueue.pop(); return true; @@ -381,7 +390,7 @@ bool OSystem_3DS::notifyEvent(const Common::Event &event) { return true; case k3DSEventToggleMagnifyMode: - if (g_gui.isActive()) { + if (_overlayVisible) { displayMessageOnOSD(_("Magnify Mode cannot be activated in menus.")); } else if (config.screen != kScreenBoth && _magnifyMode == MODE_MAGOFF) { // TODO: Automatically enable both screens while magnify mode is on @@ -429,11 +438,13 @@ void OSystem_3DS::runOptionsDialog() { optionsDialogRunning = true; OptionsDialog dialog; - if (g_engine) + if (g_engine) { g_engine->pauseEngine(true); + } int result = dialog.runModal(); - if (g_engine) + if (g_engine) { g_engine->pauseEngine(false); + } if (result > 0) { int oldScreen = config.screen; diff --git a/backends/platform/3ds/osystem-graphics.cpp b/backends/platform/3ds/osystem-graphics.cpp index 95b30193f7d..61f4fc98872 100644 --- a/backends/platform/3ds/osystem-graphics.cpp +++ b/backends/platform/3ds/osystem-graphics.cpp @@ -23,24 +23,44 @@ #include "backends/platform/3ds/osystem.h" #include "backends/platform/3ds/shader_shbin.h" +#include "backends/platform/3ds/options-dialog.h" +#include "backends/platform/3ds/config.h" #include "common/rect.h" #include "graphics/fontman.h" #include "gui/gui-manager.h" -#include "options-dialog.h" -#include "config.h" // Used to transfer the final rendered display to the framebuffer -#define DISPLAY_TRANSFER_FLAGS \ - (GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | \ - GX_TRANSFER_RAW_COPY(0) | GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | \ - GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | \ - GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO)) +#define DISPLAY_TRANSFER_FLAGS \ + (GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | \ + GX_TRANSFER_RAW_COPY(0) | GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | \ + GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | \ + GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO)) +#define TEXTURE_TRANSFER_FLAGS(fmt) \ + (GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | \ + GX_TRANSFER_RAW_COPY(0) | GX_TRANSFER_IN_FORMAT(fmt) | \ + GX_TRANSFER_OUT_FORMAT(fmt) | GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO)) +#define DEFAULT_MODE _modeRGBA8 namespace _3DS { +/* Group the various enums, values, etc. needed for + * each graphics mode into instaces of GfxMode3DS */ +static const GfxMode3DS _modeRGBA8 = { Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0), + GPU_RGBA8, TEXTURE_TRANSFER_FLAGS(GX_TRANSFER_FMT_RGBA8) }; +static const GfxMode3DS _modeRGB565 = { Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0), + GPU_RGB565, TEXTURE_TRANSFER_FLAGS(GX_TRANSFER_FMT_RGB565) }; +static const GfxMode3DS _modeRGB555 = { Graphics::PixelFormat(2, 5, 5, 5, 0, 11, 6, 1, 0), + GPU_RGBA5551, TEXTURE_TRANSFER_FLAGS(GX_TRANSFER_FMT_RGB5A1) }; +static const GfxMode3DS _modeRGB5A1 = { Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0), + GPU_RGBA5551, TEXTURE_TRANSFER_FLAGS(GX_TRANSFER_FMT_RGB5A1) }; +static const GfxMode3DS _modeCLUT8 = _modeRGBA8; -void OSystem_3DS::initGraphics() { +static const GfxMode3DS *gfxModes[] = { &_modeRGBA8, &_modeRGB565, &_modeRGB555, &_modeRGB5A1, &_modeCLUT8 }; + + +void OSystem_3DS::init3DSGraphics() { + _gfxState.gfxMode = gfxModes[CLUT8]; _pfGame = Graphics::PixelFormat::createFormatCLUT8(); - _pfGameTexture = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0); + _pfDefaultTexture = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0); C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); @@ -83,10 +103,12 @@ void OSystem_3DS::initGraphics() { C3D_CullFace(GPU_CULL_NONE); } -void OSystem_3DS::destroyGraphics() { +void OSystem_3DS::destroy3DSGraphics() { _gameScreen.free(); _gameTopTexture.free(); _gameBottomTexture.free(); + _cursor.free(); + _cursorTexture.free(); _overlay.free(); _activityIcon.free(); @@ -124,8 +146,37 @@ bool OSystem_3DS::getFeatureState(OSystem::Feature f) { } } +GraphicsModeID OSystem_3DS::chooseMode(Graphics::PixelFormat *format) { + if (format->bytesPerPixel > 2) { + return RGBA8; + } else if (format->bytesPerPixel > 1) { + if (format->gBits() > 5) { + return RGB565; + } else if (format->aBits() == 0) { + return RGB555; + } else { + return RGB5A1; + } + } + return CLUT8; +} + +bool OSystem_3DS::setGraphicsMode(GraphicsModeID modeID) { + switch (modeID) { + case RGBA8: + case RGB565: + case RGB555: + case RGB5A1: + case CLUT8: + _gfxState.gfxMode = gfxModes[modeID]; + return true; + default: + return false; + } +} + void OSystem_3DS::initSize(uint width, uint height, - const Graphics::PixelFormat *format) { + const Graphics::PixelFormat *format) { debug("3ds initsize w:%d h:%d", width, height); int oldScreen = config.screen; loadConfig(); @@ -135,16 +186,28 @@ void OSystem_3DS::initSize(uint width, uint height, _gameWidth = width; _gameHeight = height; - _gameTopTexture.create(width, height, _pfGameTexture); - _overlay.create(400, 320, _pfGameTexture); _magCenterX = _magWidth / 2; _magCenterY = _magHeight / 2; - if (format) { + _oldPfGame = _pfGame; + if (!format) { + _pfGame = Graphics::PixelFormat::createFormatCLUT8(); + } else { debug("pixelformat: %d %d %d %d %d", format->bytesPerPixel, format->rBits(), format->gBits(), format->bBits(), format->aBits()); _pfGame = *format; } + /* If the current graphics mode does not fit with the pixel + * format being requested, choose one that does and switch to it */ + assert(_pfGame.bytesPerPixel > 0); + if (_pfGame != _oldPfGame) { + assert(_transactionState == kTransactionActive); + _gfxState.gfxModeID = chooseMode(&_pfGame); + _transactionDetails.formatChanged = true; + } + + _gameTopTexture.create(width, height, _gfxState.gfxMode); + _overlay.create(400, 320, &DEFAULT_MODE); _gameScreen.create(width, height, _pfGame); _focusDirty = true; @@ -188,30 +251,69 @@ void OSystem_3DS::updateSize() { _gameBottomTexture.setPosition(_gameBottomX, _gameBottomY); _gameTopTexture.setOffset(0, 0); _gameBottomTexture.setOffset(0, 0); - if (_overlayVisible) + if (_overlayVisible) { _cursorTexture.setScale(1.f, 1.f); - else if (config.screen == kScreenTop) + } else if (config.screen == kScreenTop) { _cursorTexture.setScale(_gameTopTexture.getScaleX(), _gameTopTexture.getScaleY()); - else + } else { _cursorTexture.setScale(_gameBottomTexture.getScaleX(), _gameBottomTexture.getScaleY()); + } } Common::List OSystem_3DS::getSupportedFormats() const { Common::List list; list.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)); // GPU_RGBA8 list.push_back(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); // GPU_RGB565 -// list.push_back(Graphics::PixelFormat(3, 0, 0, 0, 8, 0, 8, 16, 0)); // GPU_RGB8 - list.push_back(Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)); // RGB555 (needed for FMTOWNS?) + list.push_back(Graphics::PixelFormat(2, 5, 5, 5, 0, 11, 6, 1, 0)); // RGB555 (needed for FMTOWNS?) list.push_back(Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0)); // GPU_RGBA5551 list.push_back(Graphics::PixelFormat::createFormatCLUT8()); return list; } void OSystem_3DS::beginGFXTransaction() { - // + assert(_transactionState == kTransactionNone); + _transactionState = kTransactionActive; + _transactionDetails.formatChanged = false; + _oldGfxState = _gfxState; } + OSystem::TransactionError OSystem_3DS::endGFXTransaction() { - return OSystem::kTransactionSuccess; + int errors = OSystem::kTransactionSuccess; + + assert(_transactionState != kTransactionNone); + if (_transactionState == kTransactionRollback) { + if (_gfxState.gfxModeID != _oldGfxState.gfxModeID) { + errors |= OSystem::kTransactionModeSwitchFailed; + _gfxState = _oldGfxState; + } else if ((_gfxState.gfxMode != _oldGfxState.gfxMode) | + (_gfxState.gfxMode != gfxModes[_gfxState.gfxModeID])) { + errors |= OSystem::kTransactionFormatNotSupported; + _gfxState = _oldGfxState; + } + + _oldGfxState.setup = false; + } + if (_transactionDetails.formatChanged) { + if (!setGraphicsMode(_gfxState.gfxModeID)) { + if (_oldGfxState.setup) { + _transactionState = kTransactionRollback; + errors |= endGFXTransaction(); + } + } else if (_gfxState.gfxMode != gfxModes[_gfxState.gfxModeID]) { + if (_oldGfxState.setup) { + _transactionState = kTransactionRollback; + errors |= endGFXTransaction(); + } + } else { + initSize(_gameWidth, _gameHeight, &_pfGame); + clearOverlay(); + _gfxState.setup = true; + _screenChangeId++; + } + } + + _transactionState = kTransactionNone; + return (OSystem::TransactionError)errors; } float OSystem_3DS::getScaleRatio() const { @@ -239,25 +341,34 @@ void OSystem_3DS::grabPalette(byte *colors, uint start, uint num) const { } void OSystem_3DS::copyRectToScreen(const void *buf, int pitch, int x, - int y, int w, int h) { + int y, int w, int h) { Common::Rect rect(x, y, x+w, y+h); _gameScreen.copyRectToSurface(buf, pitch, x, y, w, h); Graphics::Surface subSurface = _gameScreen.getSubArea(rect); - Graphics::Surface *convertedSubSurface = subSurface.convertTo(_pfGameTexture, _palette); - _gameTopTexture.copyRectToSurface(*convertedSubSurface, x, y, Common::Rect(w, h)); + if (_pfGame == _gameTopTexture.format) { + _gameTopTexture.copyRectToSurface(subSurface, x, y, Common::Rect(w, h)); + } else { + Graphics::Surface *convertedSubSurface = subSurface.convertTo(_gameTopTexture.format, _palette); + _gameTopTexture.copyRectToSurface(*convertedSubSurface, x, y, Common::Rect(w, h)); + convertedSubSurface->free(); + delete convertedSubSurface; + } - convertedSubSurface->free(); - delete convertedSubSurface; _gameTopTexture.markDirty(); } void OSystem_3DS::flushGameScreen() { - Graphics::Surface *converted = _gameScreen.convertTo(_pfGameTexture, _palette); - _gameTopTexture.copyRectToSurface(*converted, 0, 0, Common::Rect(converted->w, converted->h)); + if (_pfGame == _gameTopTexture.format) { + _gameTopTexture.copyRectToSurface(_gameScreen, 0, 0, Common::Rect(_gameScreen.w, _gameScreen.h)); + } else { + Graphics::Surface *converted = _gameScreen.convertTo(_gameTopTexture.format, _palette); + _gameTopTexture.copyRectToSurface(*converted, 0, 0, Common::Rect(converted->w, converted->h)); + converted->free(); + delete converted; + } + _gameTopTexture.markDirty(); - converted->free(); - delete converted; } Graphics::Surface *OSystem_3DS::lockScreen() { @@ -268,8 +379,9 @@ void OSystem_3DS::unlockScreen() { } void OSystem_3DS::updateScreen() { - if (sleeping || exiting) + if (sleeping || exiting) { return; + } // updateFocus(); updateMagnify(); @@ -396,10 +508,12 @@ void OSystem_3DS::updateFocus() { _focusTargetPosY = _focusTargetScaleY * _focusRect.top; _focusTargetPosX = _focusTargetScaleX * ((float)_focusRect.left - (newWidth - _focusRect.width())/2.f); } - if (_focusTargetPosX < 0 && _focusTargetScaleY != 240.f / _gameHeight) + if (_focusTargetPosX < 0 && _focusTargetScaleY != 240.f / _gameHeight) { _focusTargetPosX = 0; - if (_focusTargetPosY < 0 && _focusTargetScaleX != 400.f / _gameWidth) + } + if (_focusTargetPosY < 0 && _focusTargetScaleX != 400.f / _gameWidth) { _focusTargetPosY = 0; + } _focusStepPosX = duration * (_focusTargetPosX - _focusPosX); _focusStepPosY = duration * (_focusTargetPosY - _focusPosY); _focusStepScaleX = duration * (_focusTargetScaleX - _focusScaleX); @@ -407,28 +521,32 @@ void OSystem_3DS::updateFocus() { } if (_focusDirty || _focusPosX != _focusTargetPosX || _focusPosY != _focusTargetPosY || - _focusScaleX != _focusTargetScaleX || _focusScaleY != _focusTargetScaleY) { + _focusScaleX != _focusTargetScaleX || _focusScaleY != _focusTargetScaleY) { _focusDirty = false; - if ((_focusStepPosX > 0 && _focusPosX > _focusTargetPosX) || (_focusStepPosX < 0 && _focusPosX < _focusTargetPosX)) + if ((_focusStepPosX > 0 && _focusPosX > _focusTargetPosX) || (_focusStepPosX < 0 && _focusPosX < _focusTargetPosX)) { _focusPosX = _focusTargetPosX; - else if (_focusPosX != _focusTargetPosX) + } else if (_focusPosX != _focusTargetPosX) { _focusPosX += _focusStepPosX; + } - if ((_focusStepPosY > 0 && _focusPosY > _focusTargetPosY) || (_focusStepPosY < 0 && _focusPosY < _focusTargetPosY)) + if ((_focusStepPosY > 0 && _focusPosY > _focusTargetPosY) || (_focusStepPosY < 0 && _focusPosY < _focusTargetPosY)) { _focusPosY = _focusTargetPosY; - else if (_focusPosY != _focusTargetPosY) + } else if (_focusPosY != _focusTargetPosY) { _focusPosY += _focusStepPosY; + } - if ((_focusStepScaleX > 0 && _focusScaleX > _focusTargetScaleX) || (_focusStepScaleX < 0 && _focusScaleX < _focusTargetScaleX)) + if ((_focusStepScaleX > 0 && _focusScaleX > _focusTargetScaleX) || (_focusStepScaleX < 0 && _focusScaleX < _focusTargetScaleX)) { _focusScaleX = _focusTargetScaleX; - else if (_focusScaleX != _focusTargetScaleX) + } else if (_focusScaleX != _focusTargetScaleX) { _focusScaleX += _focusStepScaleX; + } - if ((_focusStepScaleY > 0 && _focusScaleY > _focusTargetScaleY) || (_focusStepScaleY < 0 && _focusScaleY < _focusTargetScaleY)) + if ((_focusStepScaleY > 0 && _focusScaleY > _focusTargetScaleY) || (_focusStepScaleY < 0 && _focusScaleY < _focusTargetScaleY)) { _focusScaleY = _focusTargetScaleY; - else if (_focusScaleY != _focusTargetScaleY) + } else if (_focusScaleY != _focusTargetScaleY) { _focusScaleY += _focusStepScaleY; + } Mtx_Identity(&_focusMatrix); Mtx_Translate(&_focusMatrix, -_focusPosX, -_focusPosY, 0, true); @@ -442,17 +560,14 @@ void OSystem_3DS::updateMagnify() { _magnifyMode = MODE_MAGOFF; } - // TODO: When exiting GUI, prevent cursor's position within GUI from changing - // position of magnification viewport. Possible solution: save in-game cursor - // coordinates separately from GUI cursor coordinates? if (_magnifyMode == MODE_MAGON) { - if (!g_gui.isActive()) { - _magX = (_cursorX < _magCenterX) ? - 0 : ((_cursorX < (_gameWidth - _magCenterX)) ? - _cursorX - _magCenterX : _gameWidth - _magWidth); - _magY = (_cursorY < _magCenterY) ? - 0 : ((_cursorY < _gameHeight - _magCenterY) ? - _cursorY - _magCenterY : _gameHeight - _magHeight); + if (!_overlayVisible) { + _magX = (_cursorScreenX < _magCenterX) ? + 0 : ((_cursorScreenX < (_gameWidth - _magCenterX)) ? + _cursorScreenX - _magCenterX : _gameWidth - _magWidth); + _magY = (_cursorScreenY < _magCenterY) ? + 0 : ((_cursorScreenY < _gameHeight - _magCenterY) ? + _cursorScreenY - _magCenterY : _gameHeight - _magHeight); } _gameTopTexture.setScale(1.f,1.f); _gameTopTexture.setPosition(0,0); @@ -471,7 +586,7 @@ void OSystem_3DS::hideOverlay() { } Graphics::PixelFormat OSystem_3DS::getOverlayFormat() const { - return _pfGameTexture; + return _overlay.format; } void OSystem_3DS::clearOverlay() { @@ -482,13 +597,13 @@ void OSystem_3DS::grabOverlay(void *buf, int pitch) { byte *dst = (byte *)buf; for (int y = 0; y < getOverlayHeight(); ++y) { - memcpy(dst, _overlay.getBasePtr(0, y), getOverlayWidth() * _pfGameTexture.bytesPerPixel); + memcpy(dst, _overlay.getBasePtr(0, y), getOverlayWidth() * _overlay.format.bytesPerPixel); dst += pitch; } } void OSystem_3DS::copyRectToOverlay(const void *buf, int pitch, int x, - int y, int w, int h) { + int y, int w, int h) { _overlay.copyRectToSurface(buf, pitch, x, y, w, h); _overlay.markDirty(); } @@ -525,19 +640,21 @@ void OSystem_3DS::displayMessageOnOSD(const char *msg) { } // Clip the rect - if (width > getOverlayWidth()) + if (width > getOverlayWidth()) { width = getOverlayWidth(); - if (height > getOverlayHeight()) + } + if (height > getOverlayHeight()) { height = getOverlayHeight(); + } - _osdMessage.create(width, height, _pfGameTexture); - _osdMessage.fillRect(Common::Rect(width, height), _pfGameTexture.ARGBToColor(200, 0, 0, 0)); + _osdMessage.create(width, height, &DEFAULT_MODE); + _osdMessage.fillRect(Common::Rect(width, height), _pfDefaultTexture.ARGBToColor(200, 0, 0, 0)); // Render the message, centered, and in white for (i = 0; i < lines.size(); i++) { font->drawString(&_osdMessage, lines[i], 0, 0 + i * lineHeight + vOffset + lineSpacing, width, - _pfGameTexture.RGBToColor(255, 255, 255), + _pfDefaultTexture.RGBToColor(255, 255, 255), Graphics::kTextAlignCenter); } @@ -549,14 +666,19 @@ void OSystem_3DS::displayActivityIconOnOSD(const Graphics::Surface *icon) { _activityIcon.free(); } else { if (!_activityIcon.getPixels() || icon->w != _activityIcon.w || icon->h != _activityIcon.h) { - _activityIcon.create(icon->w, icon->h, _pfGameTexture); + _activityIcon.create(icon->w, icon->h, &DEFAULT_MODE); + } + + if (icon->format == _activityIcon.format) { + _activityIcon.copyRectToSurface(*icon, 0, 0, Common::Rect(icon->w, icon->h)); + } else { + Graphics::Surface *converted = icon->convertTo(_activityIcon.format); + _activityIcon.copyRectToSurface(*converted, 0, 0, Common::Rect(converted->w, converted->h)); + converted->free(); + delete converted; } - Graphics::Surface *converted = icon->convertTo(_pfGameTexture); - _activityIcon.copyRectToSurface(*converted, 0, 0, Common::Rect(converted->w, converted->h)); _activityIcon.markDirty(); - converted->free(); - delete converted; } } @@ -575,8 +697,13 @@ bool OSystem_3DS::showMouse(bool visible) { } void OSystem_3DS::warpMouse(int x, int y) { - _cursorX = x; - _cursorY = y; + if (!_overlayVisible) { + _cursorScreenX = x; + _cursorScreenY = y; + } else { + _cursorOverlayX = x; + _cursorOverlayY = y; + } // TODO: adjust for _cursorScalable ? x -= _cursorHotspotX; @@ -598,9 +725,9 @@ void OSystem_3DS::setCursorDelta(float deltaX, float deltaY) { } void OSystem_3DS::setMouseCursor(const void *buf, uint w, uint h, - int hotspotX, int hotspotY, - uint32 keycolor, bool dontScale, - const Graphics::PixelFormat *format) { + int hotspotX, int hotspotY, + uint32 keycolor, bool dontScale, + const Graphics::PixelFormat *format) { _cursorScalable = !dontScale; _cursorHotspotX = hotspotX; _cursorHotspotY = hotspotY; @@ -609,7 +736,7 @@ void OSystem_3DS::setMouseCursor(const void *buf, uint w, uint h, if (w != _cursor.w || h != _cursor.h || _cursor.format != _pfCursor) { _cursor.create(w, h, _pfCursor); - _cursorTexture.create(w, h, _pfGameTexture); + _cursorTexture.create(w, h, &DEFAULT_MODE); } if ( w != 0 && h != 0 ) { @@ -618,7 +745,11 @@ void OSystem_3DS::setMouseCursor(const void *buf, uint w, uint h, flushCursor(); - warpMouse(_cursorX, _cursorY); + if (!_overlayVisible) { + warpMouse(_cursorScreenX, _cursorScreenY); + } else { + warpMouse(_cursorOverlayX, _cursorOverlayY); + } } void OSystem_3DS::setCursorPalette(const byte *colors, uint start, uint num) { @@ -653,7 +784,7 @@ void applyKeyColor(Graphics::Surface *src, Graphics::Surface *dst, const SrcColo void OSystem_3DS::flushCursor() { if (_cursor.getPixels()) { - Graphics::Surface *converted = _cursor.convertTo(_pfGameTexture, _cursorPaletteEnabled ? _cursorPalette : _palette); + Graphics::Surface *converted = _cursor.convertTo(_cursorTexture.format, _cursorPaletteEnabled ? _cursorPalette : _palette); _cursorTexture.copyRectToSurface(*converted, 0, 0, Common::Rect(converted->w, converted->h)); _cursorTexture.markDirty(); converted->free(); diff --git a/backends/platform/3ds/osystem.cpp b/backends/platform/3ds/osystem.cpp index 475d329c457..11022b6ec93 100644 --- a/backends/platform/3ds/osystem.cpp +++ b/backends/platform/3ds/osystem.cpp @@ -27,6 +27,7 @@ #include <3ds.h> #include "osystem.h" +#include "backends/platform/3ds/config.h" #include "backends/saves/default/default-saves.h" #include "backends/timer/default/default-timer.h" #include "backends/events/default/default-events.h" @@ -34,7 +35,6 @@ #include "common/scummsys.h" #include "common/config-manager.h" #include "common/str.h" -#include "config.h" #include "backends/fs/posix-drives/posix-drives-fs-factory.h" #include "backends/fs/posix-drives/posix-drives-fs.h" @@ -62,8 +62,10 @@ OSystem_3DS::OSystem_3DS(): _cursorPaletteEnabled(false), _cursorVisible(false), _cursorScalable(false), - _cursorX(0), - _cursorY(0), + _cursorScreenX(0), + _cursorScreenY(0), + _cursorOverlayX(0), + _cursorOverlayY(0), _cursorHotspotX(0), _cursorHotspotY(0), _gameTopX(0), @@ -110,7 +112,7 @@ OSystem_3DS::~OSystem_3DS() { exiting = true; destroyEvents(); destroyAudio(); - destroyGraphics(); + destroy3DSGraphics(); delete _timerManager; _timerManager = 0; @@ -125,15 +127,17 @@ void OSystem_3DS::initBackend() { ConfMan.set("joystick_num", 0); ConfMan.registerDefault("fullscreen", true); ConfMan.registerDefault("aspect_ratio", true); - if (!ConfMan.hasKey("vkeybd_pack_name")) + if (!ConfMan.hasKey("vkeybd_pack_name")) { ConfMan.set("vkeybd_pack_name", "vkeybd_small"); - if (!ConfMan.hasKey("gui_theme")) + } + if (!ConfMan.hasKey("gui_theme")) { ConfMan.set("gui_theme", "builtin"); + } _timerManager = new DefaultTimerManager(); _savefileManager = new DefaultSaveFileManager("sdmc:/3ds/scummvm/saves/"); - initGraphics(); + init3DSGraphics(); initAudio(); EventsBaseBackend::initBackend(); initEvents(); @@ -142,7 +146,8 @@ void OSystem_3DS::initBackend() { void OSystem_3DS::updateConfig() { if (_gameScreen.getPixels()) { updateSize(); - warpMouse(_cursorX, _cursorY); + (!_overlayVisible) ? warpMouse(_cursorScreenX, _cursorScreenY) : + warpMouse(_cursorOverlayX, _cursorOverlayY); } } diff --git a/backends/platform/3ds/osystem.h b/backends/platform/3ds/osystem.h index 2013d10a7f0..93439d50611 100644 --- a/backends/platform/3ds/osystem.h +++ b/backends/platform/3ds/osystem.h @@ -49,6 +49,48 @@ enum InputMode { MODE_DRAG, }; +enum GraphicsModeID { + RGBA8, + RGB565, + RGB555, + RGB5A1, + CLUT8 +}; + +enum TransactionState { + kTransactionNone = 0, + kTransactionActive = 1, + kTransactionRollback = 2 +}; + + +struct TransactionDetails { + bool formatChanged, modeChanged; + + TransactionDetails() { + formatChanged = false; + modeChanged = false; + } +}; + +typedef struct GfxMode3DS { + Graphics::PixelFormat surfaceFormat; + GPU_TEXCOLOR textureFormat; + uint32 textureTransferFlags; +} GfxMode3DS; + +struct GfxState { + bool setup; + GraphicsModeID gfxModeID; + const GfxMode3DS *gfxMode; + + GfxState() { + setup = false; + gfxModeID = CLUT8; + } +}; + + class OSystem_3DS : public EventsBaseBackend, public PaletteManager, public Common::EventObserver { public: OSystem_3DS(); @@ -95,6 +137,8 @@ public: void initSize(uint width, uint height, const Graphics::PixelFormat *format = NULL); virtual int getScreenChangeID() const { return _screenChangeId; }; + GraphicsModeID chooseMode(Graphics::PixelFormat *format); + bool setGraphicsMode(GraphicsModeID modeID); void beginGFXTransaction(); OSystem::TransactionError endGFXTransaction(); @@ -143,8 +187,8 @@ public: void updateSize(); private: - void initGraphics(); - void destroyGraphics(); + void init3DSGraphics(); + void destroy3DSGraphics(); void initAudio(); void destroyAudio(); void initEvents(); @@ -166,8 +210,13 @@ private: Thread audioThread; // Graphics - Graphics::PixelFormat _pfGame; - Graphics::PixelFormat _pfGameTexture; + GraphicsModeID _graphicsModeID; + TransactionState _transactionState; + TransactionDetails _transactionDetails; + + GfxState _gfxState, _oldGfxState; + Graphics::PixelFormat _pfDefaultTexture; + Graphics::PixelFormat _pfGame, _oldPfGame; Graphics::PixelFormat _pfCursor; byte _palette[3 * 256]; byte _cursorPalette[3 * 256]; @@ -221,7 +270,8 @@ private: bool _cursorPaletteEnabled; bool _cursorVisible; bool _cursorScalable; - float _cursorX, _cursorY; + float _cursorScreenX, _cursorScreenY; + float _cursorOverlayX, _cursorOverlayY; float _cursorDeltaX, _cursorDeltaY; int _cursorHotspotX, _cursorHotspotY; uint32 _cursorKeyColor; diff --git a/backends/platform/3ds/sprite.cpp b/backends/platform/3ds/sprite.cpp index 1b4383706e6..b3bbcb3e447 100644 --- a/backends/platform/3ds/sprite.cpp +++ b/backends/platform/3ds/sprite.cpp @@ -20,12 +20,16 @@ * */ +#include "backends/platform/3ds/osystem.h" #include "backends/platform/3ds/sprite.h" #include "common/algorithm.h" #include "common/util.h" +namespace _3DS { + Sprite::Sprite() - : dirtyPixels(true) + : textureTransferFlags(0) + , dirtyPixels(true) , dirtyMatrix(true) , actualWidth(0) , actualHeight(0) @@ -45,12 +49,13 @@ Sprite::~Sprite() { // } -void Sprite::create(uint16 width, uint16 height, const Graphics::PixelFormat &f) { +void Sprite::create(uint16 width, uint16 height, const GfxMode3DS *mode) { free(); actualWidth = width; actualHeight = height; - format = f; + format = mode->surfaceFormat; + textureTransferFlags = mode->textureTransferFlags; w = MAX(Common::nextHigher2(width), 64u); h = MAX(Common::nextHigher2(height), 64u); pitch = w * format.bytesPerPixel; @@ -58,7 +63,7 @@ void Sprite::create(uint16 width, uint16 height, const Graphics::PixelFormat &f) if (width && height) { pixels = linearAlloc(h * pitch); - C3D_TexInit(&texture, w, h, GPU_RGBA8); + C3D_TexInit(&texture, w, h, mode->textureFormat); C3D_TexSetFilter(&texture, GPU_LINEAR, GPU_LINEAR); assert(pixels && texture.data); clear(); @@ -94,7 +99,7 @@ void Sprite::transfer() { if (pixels && dirtyPixels) { dirtyPixels = false; GSPGPU_FlushDataCache(pixels, w * h * format.bytesPerPixel); - C3D_SyncDisplayTransfer((u32*)pixels, GX_BUFFER_DIM(w, h), (u32*)texture.data, GX_BUFFER_DIM(w, h), TEXTURE_TRANSFER_FLAGS); + C3D_SyncDisplayTransfer((u32*)pixels, GX_BUFFER_DIM(w, h), (u32*)texture.data, GX_BUFFER_DIM(w, h), textureTransferFlags); } } @@ -143,3 +148,5 @@ C3D_Mtx* Sprite::getMatrix() { } return &modelview; } + +} // namespace _3DS diff --git a/backends/platform/3ds/sprite.h b/backends/platform/3ds/sprite.h index 7bb4d36c3a0..36cc5fcb1f0 100644 --- a/backends/platform/3ds/sprite.h +++ b/backends/platform/3ds/sprite.h @@ -29,21 +29,20 @@ #include <3ds.h> #include -#define TEXTURE_TRANSFER_FLAGS \ - (GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_RAW_COPY(0) | \ - GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGBA8) | \ - GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO)) +namespace _3DS { typedef struct { float position[3]; float texcoord[2]; } vertex; +struct GfxMode3DS; + class Sprite : public Graphics::Surface { public: Sprite(); ~Sprite(); - void create(uint16 width, uint16 height, const Graphics::PixelFormat &format); + void create(uint16 width, uint16 height, const GfxMode3DS *mode); void free(); void convertToInPlace(const Graphics::PixelFormat &dstFormat, const byte *palette = 0); void transfer(); @@ -64,6 +63,7 @@ public: uint16 actualHeight; private: + uint32 textureTransferFlags; bool dirtyPixels; bool dirtyMatrix; C3D_Mtx modelview; @@ -77,4 +77,6 @@ private: float scaleY; }; +} // namespace _3DS + #endif