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.
This commit is contained in:
Michael Ball 2020-01-25 10:06:14 -08:00 committed by Bastien Bouclet
parent 99b9db456a
commit b08ab0e130
9 changed files with 328 additions and 117 deletions

View File

@ -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();
}

View File

@ -20,7 +20,7 @@
*
*/
#include "osystem.h"
#include "backends/platform/3ds/osystem.h"
#include "backends/plugins/3ds/3ds-provider.h"
#include <3ds.h>

View File

@ -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) {

View File

@ -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;

View File

@ -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<Graphics::PixelFormat> OSystem_3DS::getSupportedFormats() const {
Common::List<Graphics::PixelFormat> 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();

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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<uint16>(Common::nextHigher2(width), 64u);
h = MAX<uint16>(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

View File

@ -29,21 +29,20 @@
#include <3ds.h>
#include <citro3d.h>
#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