From 97cb18d3fdb99568f4778194b23f6d51103dc219 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 8 Jun 2022 01:53:35 -0700 Subject: [PATCH 01/24] GBA Video: Fix two sprite boundary conditions in OpenGL --- include/mgba/internal/gba/renderers/gl.h | 1 + src/gba/renderers/gl.c | 36 ++++++++++++++++++++---- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/include/mgba/internal/gba/renderers/gl.h b/include/mgba/internal/gba/renderers/gl.h index 1a972ada8..42068e53f 100644 --- a/include/mgba/internal/gba/renderers/gl.h +++ b/include/mgba/internal/gba/renderers/gl.h @@ -103,6 +103,7 @@ enum { GBA_GL_OBJ_VRAM = 2, GBA_GL_OBJ_PALETTE, GBA_GL_OBJ_CHARBASE, + GBA_GL_OBJ_TILE, GBA_GL_OBJ_STRIDE, GBA_GL_OBJ_LOCALPALETTE, GBA_GL_OBJ_INFLAGS, diff --git a/src/gba/renderers/gl.c b/src/gba/renderers/gl.c index 02374d8e4..0fcd70617 100644 --- a/src/gba/renderers/gl.c +++ b/src/gba/renderers/gl.c @@ -85,25 +85,37 @@ static const char* const _vertexShader = "}"; static const char* const _renderTile16 = + "#ifndef VRAM_MASK\n" + "#define VRAM_MASK\n" + "#endif\n" "int renderTile(int tile, int paletteId, ivec2 localCoord) {\n" " int address = charBase + tile * 16 + (localCoord.x >> 2) + (localCoord.y << 1);\n" - " int halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0).r;\n" + " int halfrow = texelFetch(vram, ivec2(address & 255, (address >> 8) VRAM_MASK), 0).r;\n" " int entry = (halfrow >> (4 * (localCoord.x & 3))) & 15;\n" " if (entry == 0) {\n" " discard;\n" " }\n" " return paletteId * 16 + entry;\n" + "}\n" + "int mask(int tile) {\n" + " return tile & 31;\n" "}"; static const char* const _renderTile256 = + "#ifndef VRAM_MASK\n" + "#define VRAM_MASK\n" + "#endif\n" "int renderTile(int tile, int paletteId, ivec2 localCoord) {\n" " int address = charBase + tile * 32 + (localCoord.x >> 1) + (localCoord.y << 2);\n" - " int halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0).r;\n" + " int halfrow = texelFetch(vram, ivec2(address & 255, (address >> 8) VRAM_MASK), 0).r;\n" " int entry = (halfrow >> (8 * (localCoord.x & 1))) & 255;\n" " if (entry == 0) {\n" " discard;\n" " }\n" " return entry;\n" + "}" + "int mask(int tile) {\n" + " return tile & 15;\n" "}"; static const struct GBAVideoGLUniform _uniformsMode0[] = { @@ -167,7 +179,7 @@ static const char* const _renderMode0 = " int tile = map & 1023;\n" " int paletteEntry = renderTile(tile, map >> 12, coord & 7);\n" " color = texelFetch(palette, ivec2(paletteEntry, int(texCoord.y)), 0);\n" - "}"; + "}\n"; static const char* const _fetchTileOverflow = "int fetchTile(ivec2 coord) {\n" @@ -415,6 +427,7 @@ static const struct GBAVideoGLUniform _uniformsObj[] = { { "objwin", GBA_GL_OBJ_OBJWIN, }, { "mosaic", GBA_GL_OBJ_MOSAIC, }, { "cyclesRemaining", GBA_GL_OBJ_CYCLES, }, + { "tile", GBA_GL_OBJ_TILE, }, { 0 } }; @@ -424,6 +437,7 @@ static const char* const _renderObj = "uniform isampler2D vram;\n" "uniform sampler2D palette;\n" "uniform int charBase;\n" + "uniform int tile;\n" "uniform int stride;\n" "uniform int localPalette;\n" "uniform ivec4 inflags;\n" @@ -437,6 +451,8 @@ static const char* const _renderObj = "OUT(2) out ivec4 window;\n" "int renderTile(int tile, int paletteId, ivec2 localCoord);\n" + "int mask(int);\n" + "#define VRAM_MASK & 191\n" "void main() {\n" " vec2 incoord = texCoord;\n" @@ -461,12 +477,12 @@ static const char* const _renderObj = " if ((coord & ~(dims.xy - 1)) != ivec2(0, 0)) {\n" " discard;\n" " }\n" - " int paletteEntry = renderTile((coord.x >> 3) + (coord.y >> 3) * stride, localPalette, coord & 7);\n" + " int paletteEntry = renderTile(mask((coord.x >> 3) + tile) + (coord.y >> 3) * stride, localPalette, coord & 7);\n" " color = texelFetch(palette, ivec2(paletteEntry + 256, int(texCoord.y) + mosaic.w), 0);\n" " flags = inflags;\n" " gl_FragDepth = float(flags.x) / 16.;\n" " window = ivec4(objwin, 0);\n" - "}"; + "}\n"; static const struct GBAVideoGLUniform _uniformsObjPriority[] = { { "loc", GBA_GL_VS_LOC, }, @@ -1709,6 +1725,15 @@ void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GB int align = GBAObjAttributesAIs256Color(sprite->a) && !GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt); unsigned charBase = (BASE_TILE >> 1) + (GBAObjAttributesCGetTile(sprite->c) & ~align) * 0x10; + unsigned tile = 0; + if (!GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt)) { + if (GBAObjAttributesAIs256Color(sprite->a)) { + tile = (charBase >> 5) & 0xF; + } else { + tile = (charBase >> 4) & 0x1F; + } + charBase &= ~0x1FF; + } int stride = GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? (width >> 3) : (0x20 >> GBAObjAttributesAGet256Color(sprite->a)); int totalWidth = width; @@ -1744,6 +1769,7 @@ void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GB glUniform1i(uniforms[GBA_GL_OBJ_VRAM], 0); glUniform1i(uniforms[GBA_GL_OBJ_PALETTE], 1); glUniform1i(uniforms[GBA_GL_OBJ_CHARBASE], charBase); + glUniform1i(uniforms[GBA_GL_OBJ_TILE], tile); glUniform1i(uniforms[GBA_GL_OBJ_STRIDE], stride); glUniform1i(uniforms[GBA_GL_OBJ_LOCALPALETTE], GBAObjAttributesCGetPalette(sprite->c)); glUniform4i(uniforms[GBA_GL_OBJ_INFLAGS], GBAObjAttributesCGetPriority(sprite->c), From ae0c5e91aa99b0e41b9595d6d9b254a7bb730b7c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 6 Jun 2022 17:21:02 -0700 Subject: [PATCH 02/24] Qt: Simplify Window drawing (fixes #2190) --- CHANGES | 1 + src/platform/qt/DisplayGL.cpp | 29 ++++++------ src/platform/qt/DisplayGL.h | 5 ++- src/platform/qt/LoadSaveState.cpp | 5 +++ src/platform/qt/LoadSaveState.h | 9 ++++ src/platform/qt/Window.cpp | 74 ++++++++++--------------------- src/platform/qt/Window.h | 6 +-- 7 files changed, 59 insertions(+), 70 deletions(-) diff --git a/CHANGES b/CHANGES index 10831f0c2..ae7e136e4 100644 --- a/CHANGES +++ b/CHANGES @@ -57,6 +57,7 @@ Other fixes: - Qt: Fix some hangs when using the debugger console - Qt: Fix crash when clicking past last tile in viewer - Qt: Fix preloading for ROM replacing + - Qt: Fix screen not displaying on Wayland (fixes mgba.io/i/2190) - VFS: Failed file mapping should return NULL on POSIX Misc: - Core: Suspend runloop when a core crashes diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 221acffc0..dd683b781 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -56,10 +56,13 @@ uint qHash(const QSurfaceFormat& format, uint seed) { } void mGLWidget::initializeGL() { - m_vao.create(); - m_program.create(); + m_vao = std::make_unique(); + m_vao->create(); - m_program.addShaderFromSourceCode(QOpenGLShader::Vertex, R"(#version 150 core + m_program = std::make_unique(); + m_program->create(); + + m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, R"(#version 150 core in vec4 position; out vec2 texCoord; void main() { @@ -67,7 +70,7 @@ void mGLWidget::initializeGL() { texCoord = (position.st + 1.0) * 0.5; })"); - m_program.addShaderFromSourceCode(QOpenGLShader::Fragment, R"(#version 150 core + m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, R"(#version 150 core in vec2 texCoord; out vec4 color; uniform sampler2D tex; @@ -75,9 +78,9 @@ void mGLWidget::initializeGL() { color = vec4(texture(tex, texCoord).rgb, 1.0); })"); - m_program.link(); - m_program.setUniformValue("tex", 0); - m_positionLocation = m_program.attributeLocation("position"); + m_program->link(); + m_program->setUniformValue("tex", 0); + m_positionLocation = m_program->attributeLocation("position"); connect(&m_refresh, &QTimer::timeout, this, static_cast(&QWidget::update)); } @@ -85,11 +88,11 @@ void mGLWidget::initializeGL() { void mGLWidget::finalizeVAO() { QOpenGLFunctions_Baseline* fn = context()->versionFunctions(); fn->glGetError(); // Clear the error - m_vao.bind(); + m_vao->bind(); fn->glBindBuffer(GL_ARRAY_BUFFER, m_vbo); fn->glEnableVertexAttribArray(m_positionLocation); fn->glVertexAttribPointer(m_positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); - m_vao.release(); + m_vao->release(); if (fn->glGetError() == GL_NO_ERROR) { m_vaoDone = true; } @@ -100,13 +103,13 @@ void mGLWidget::paintGL() { finalizeVAO(); } QOpenGLFunctions_Baseline* fn = context()->versionFunctions(); - m_program.bind(); - m_vao.bind(); + m_program->bind(); + m_vao->bind(); fn->glBindTexture(GL_TEXTURE_2D, m_tex); fn->glDrawArrays(GL_TRIANGLE_FAN, 0, 4); fn->glBindTexture(GL_TEXTURE_2D, 0); - m_vao.release(); - m_program.release(); + m_vao->release(); + m_program->release(); // TODO: Better timing ++m_refreshResidue; diff --git a/src/platform/qt/DisplayGL.h b/src/platform/qt/DisplayGL.h index 54256ed30..a75f1a8dd 100644 --- a/src/platform/qt/DisplayGL.h +++ b/src/platform/qt/DisplayGL.h @@ -32,6 +32,7 @@ #include #include +#include #include "CoreController.h" #include "VideoProxy.h" @@ -62,8 +63,8 @@ private: GLuint m_vbo; bool m_vaoDone = false; - QOpenGLVertexArrayObject m_vao; - QOpenGLShaderProgram m_program; + std::unique_ptr m_vao; + std::unique_ptr m_program; GLuint m_positionLocation; QTimer m_refresh; diff --git a/src/platform/qt/LoadSaveState.cpp b/src/platform/qt/LoadSaveState.cpp index aa76ed523..3198a4c90 100644 --- a/src/platform/qt/LoadSaveState.cpp +++ b/src/platform/qt/LoadSaveState.cpp @@ -9,6 +9,7 @@ #include "GamepadAxisEvent.h" #include "GamepadButtonEvent.h" #include "VFileDevice.h" +#include "utils.h" #include #include @@ -251,6 +252,10 @@ void LoadSaveState::focusInEvent(QFocusEvent*) { void LoadSaveState::paintEvent(QPaintEvent*) { QPainter painter(this); + + painter.setRenderHint(QPainter::SmoothPixmapTransform, true); QRect full(QPoint(), size()); + painter.fillRect(full, Qt::black); + painter.drawPixmap(clampSize(m_dims, size(), m_lockAspectRatio, m_lockIntegerScaling), m_background); painter.fillRect(full, QColor(0, 0, 0, 128)); } diff --git a/src/platform/qt/LoadSaveState.h b/src/platform/qt/LoadSaveState.h index 5596c1b10..f74e5b24b 100644 --- a/src/platform/qt/LoadSaveState.h +++ b/src/platform/qt/LoadSaveState.h @@ -32,6 +32,10 @@ public: void setInputController(InputController* controller); void setMode(LoadSave mode); + void setBackground(const QPixmap& pixmap) { m_background = pixmap; } + void setDimensions(const QSize& dims) { m_dims = dims; } + void setLockIntegerScaling(bool lockIntegerScaling) { m_lockIntegerScaling = lockIntegerScaling; } + void setLockAspectRatio(bool lockApsectRatio) { m_lockAspectRatio = lockApsectRatio; } signals: void closed(); @@ -54,6 +58,11 @@ private: int m_currentFocus; QPixmap m_currentImage; + QPixmap m_background; + + QSize m_dims; + bool m_lockAspectRatio; + bool m_lockIntegerScaling; }; } diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index c32662fb8..9a2551503 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #ifdef USE_SQLITE3 @@ -58,7 +57,6 @@ #include "TileView.h" #include "VideoProxy.h" #include "VideoView.h" -#include "utils.h" #ifdef USE_DISCORD_RPC #include "DiscordCoordinator.h" @@ -113,14 +111,12 @@ Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWi m_libraryView = new LibraryController(nullptr, ConfigController::configDir() + "/library.sqlite3", m_config); ConfigOption* showLibrary = m_config->addOption("showLibrary"); showLibrary->connect([this](const QVariant& value) { - if (value.toBool()) { - if (m_controller) { - m_screenWidget->layout()->addWidget(m_libraryView); - } else { + if (!m_controller) { + if (value.toBool()) { attachWidget(m_libraryView); + } else { + attachWidget(m_screenWidget); } - } else { - detachWidget(m_libraryView); } }, this); m_config->updateOption("showLibrary"); @@ -150,7 +146,6 @@ Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWi resizeFrame(QSize(GB_VIDEO_HORIZONTAL_PIXELS * i, GB_VIDEO_VERTICAL_PIXELS * i)); #endif setLogo(); - setCentralWidget(m_screenWidget); connect(this, &Window::shutdown, m_logView, &QWidget::hide); connect(&m_fpsTimer, &QTimer::timeout, this, &Window::showFPS); @@ -883,7 +878,6 @@ void Window::gameStarted() { action.value()->setEnabled(m_controller->platform() == action.key()); } QSize size = m_controller->screenDimensions(); - m_screenWidget->setDimensions(size.width(), size.height()); m_config->updateOption("lockIntegerScaling"); m_config->updateOption("lockAspectRatio"); m_config->updateOption("interframeBlending"); @@ -977,7 +971,6 @@ void Window::gameStopped() { m_audioProcessor.reset(); } m_display->stopDrawing(); - detachWidget(m_display.get()); setLogo(); if (m_display) { #ifdef M_CORE_GB @@ -988,6 +981,7 @@ void Window::gameStopped() { } m_controller.reset(); + detachWidget(); updateTitle(); if (m_pendingClose) { @@ -1039,7 +1033,7 @@ void Window::unimplementedBiosCall(int) { void Window::reloadDisplayDriver() { if (m_controller) { m_display->stopDrawing(); - detachWidget(m_display.get()); + detachWidget(); } m_display = std::unique_ptr(Display::create(this)); if (!m_display) { @@ -1054,7 +1048,7 @@ void Window::reloadDisplayDriver() { #endif connect(m_display.get(), &QGBA::Display::hideCursor, [this]() { - if (static_cast(m_screenWidget->layout())->currentWidget() == m_display.get()) { + if (centralWidget() == m_display.get()) { m_screenWidget->setCursor(Qt::BlankCursor); } }); @@ -1215,15 +1209,13 @@ void Window::openStateWindow(LoadSave ls) { m_stateWindow = new LoadSaveState(m_controller); connect(this, &Window::shutdown, m_stateWindow, &QWidget::close); connect(m_stateWindow, &LoadSaveState::closed, [this]() { - detachWidget(m_stateWindow); - static_cast(m_screenWidget->layout())->setCurrentWidget(m_display.get()); + attachWidget(m_display.get()); m_stateWindow = nullptr; QMetaObject::invokeMethod(this, "setFocus", Qt::QueuedConnection); }); if (!wasPaused) { m_controller->setPaused(true); connect(m_stateWindow, &LoadSaveState::closed, [this]() { - m_screenWidget->filter(m_config->getOption("resampleVideo").toInt()); if (m_controller) { m_controller->setPaused(false); } @@ -1232,6 +1224,10 @@ void Window::openStateWindow(LoadSave ls) { m_stateWindow->setAttribute(Qt::WA_DeleteOnClose); m_stateWindow->setMode(ls); + m_stateWindow->setDimensions(m_controller->screenDimensions()); + m_config->updateOption("lockAspectRatio"); + m_config->updateOption("lockIntegerScaling"); + QImage still(m_controller->getPixels()); if (still.format() != QImage::Format_RGB888) { still = still.convertToFormat(QImage::Format_RGB888); @@ -1249,8 +1245,7 @@ void Window::openStateWindow(LoadSave ls) { QPixmap pixmap; pixmap.convertFromImage(output); - m_screenWidget->setPixmap(pixmap); - m_screenWidget->filter(true); + m_stateWindow->setBackground(pixmap); #ifndef Q_OS_MAC menuBar()->show(); @@ -1536,8 +1531,8 @@ void Window::setupMenu(QMenuBar* menubar) { if (m_display) { m_display->lockAspectRatio(value.toBool()); } - if (m_controller) { - m_screenWidget->setLockAspectRatio(value.toBool()); + if (m_stateWindow) { + m_stateWindow->setLockAspectRatio(value.toBool()); } }, this); m_config->updateOption("lockAspectRatio"); @@ -1548,8 +1543,8 @@ void Window::setupMenu(QMenuBar* menubar) { if (m_display) { m_display->lockIntegerScaling(value.toBool()); } - if (m_controller) { - m_screenWidget->setLockIntegerScaling(value.toBool()); + if (m_stateWindow) { + m_stateWindow->setLockIntegerScaling(value.toBool()); } }, this); m_config->updateOption("lockIntegerScaling"); @@ -1569,9 +1564,6 @@ void Window::setupMenu(QMenuBar* menubar) { if (m_display) { m_display->filter(value.toBool()); } - if (m_controller) { - m_screenWidget->filter(value.toBool()); - } }, this); m_config->updateOption("resampleVideo"); @@ -1894,13 +1886,12 @@ void Window::setupOptions() { } void Window::attachWidget(QWidget* widget) { - m_screenWidget->layout()->addWidget(widget); - m_screenWidget->unsetCursor(); - static_cast(m_screenWidget->layout())->setCurrentWidget(widget); + takeCentralWidget(); + setCentralWidget(widget); } -void Window::detachWidget(QWidget* widget) { - m_screenWidget->layout()->removeWidget(widget); +void Window::detachWidget() { + m_config->updateOption("showLibrary"); } void Window::appendMRU(const QString& fname) { @@ -1996,7 +1987,7 @@ void Window::focusCheck() { } void Window::updateFrame() { - if (static_cast(m_screenWidget->layout())->currentWidget() != m_display.get()) { + if (!m_controller) { return; } QPixmap pixmap; @@ -2171,17 +2162,12 @@ void Window::updateMute() { void Window::setLogo() { m_screenWidget->setPixmap(m_logo); m_screenWidget->setDimensions(m_logo.width(), m_logo.height()); - m_screenWidget->setLockIntegerScaling(false); - m_screenWidget->setLockAspectRatio(true); - m_screenWidget->filter(true); m_screenWidget->unsetCursor(); } WindowBackground::WindowBackground(QWidget* parent) : QWidget(parent) { - setLayout(new QStackedLayout()); - layout()->setContentsMargins(0, 0, 0, 0); } void WindowBackground::setPixmap(const QPixmap& pmap) { @@ -2202,24 +2188,12 @@ void WindowBackground::setDimensions(int width, int height) { m_aspectHeight = height; } -void WindowBackground::setLockIntegerScaling(bool lock) { - m_lockIntegerScaling = lock; -} - -void WindowBackground::setLockAspectRatio(bool lock) { - m_lockAspectRatio = lock; -} - -void WindowBackground::filter(bool filter) { - m_filter = filter; -} - void WindowBackground::paintEvent(QPaintEvent* event) { QWidget::paintEvent(event); const QPixmap& logo = pixmap(); QPainter painter(this); - painter.setRenderHint(QPainter::SmoothPixmapTransform, m_filter); + painter.setRenderHint(QPainter::SmoothPixmapTransform, true); painter.fillRect(QRect(QPoint(), size()), Qt::black); - QRect full(clampSize(QSize(m_aspectWidth, m_aspectHeight), size(), m_lockAspectRatio, m_lockIntegerScaling)); + QRect full(clampSize(QSize(m_aspectWidth, m_aspectHeight), size(), true, false)); painter.drawPixmap(full, logo); } diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index f129c44e5..696604773 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -167,7 +167,7 @@ private: void openStateWindow(LoadSave); void attachWidget(QWidget* widget); - void detachWidget(QWidget* widget); + void detachWidget(); void appendMRU(const QString& fname); void clearMRU(); @@ -277,7 +277,6 @@ public: void setDimensions(int width, int height); void setLockIntegerScaling(bool lock); void setLockAspectRatio(bool lock); - void filter(bool filter); const QPixmap& pixmap() const { return m_pixmap; } @@ -289,9 +288,6 @@ private: QSize m_sizeHint; int m_aspectWidth; int m_aspectHeight; - bool m_lockAspectRatio; - bool m_lockIntegerScaling; - bool m_filter; }; } From 7b8ba05f885c6b6f228e27e5e8d9de1c71ea16dc Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 6 Jun 2022 17:21:32 -0700 Subject: [PATCH 03/24] Qt: Only disable DisplayGL updates on Windows --- src/platform/qt/DisplayGL.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index dd683b781..4691726bc 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -205,7 +205,9 @@ void DisplayGL::startDrawing(std::shared_ptr controller) { CoreController::Interrupter interrupter(controller); QMetaObject::invokeMethod(m_painter.get(), "start"); if (!m_gl) { - setUpdatesEnabled(false); + if (QGuiApplication::platformName() == "windows") { + setUpdatesEnabled(false); + } } else { show(); } @@ -290,7 +292,7 @@ void DisplayGL::unpauseDrawing() { if (m_hasStarted) { m_isDrawing = true; QMetaObject::invokeMethod(m_painter.get(), "unpause", Qt::BlockingQueuedConnection); - if (!m_gl) { + if (!m_gl && QGuiApplication::platformName() == "windows") { setUpdatesEnabled(false); } } From 6e196748d88b830747e930c65365c9452654ab23 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 8 Jun 2022 17:55:37 -0700 Subject: [PATCH 04/24] Scripting: Hand off full filename to Lua, add directory to DLL search path --- src/script/engines/lua.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index 2770e8898..24e54c88e 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -11,6 +11,10 @@ #include #include +#ifdef _WIN32 +#include +#endif + #define MAX_KEY_SIZE 128 static struct mScriptEngineContext* _luaCreate(struct mScriptEngine2*, struct mScriptContext*); @@ -476,24 +480,27 @@ bool _luaLoad(struct mScriptEngineContext* ctx, const char* filename, struct VFi free(luaContext->lastError); luaContext->lastError = NULL; } - char name[80]; + char name[PATH_MAX + 1]; if (filename) { if (*filename == '*') { snprintf(name, sizeof(name), "=%s", filename + 1); } else { +#ifdef _WIN32 + char dirname[PATH_MAX] = {0}; const char* lastSlash = strrchr(filename, '/'); const char* lastBackslash = strrchr(filename, '\\'); if (lastSlash && lastBackslash) { - if (lastSlash > lastBackslash) { - filename = lastSlash + 1; - } else { - filename = lastBackslash + 1; + if (lastSlash < lastBackslash) { + lastSlash = lastBackslash; } - } else if (lastSlash) { - filename = lastSlash + 1; } else if (lastBackslash) { - filename = lastBackslash + 1; + lastSlash = lastBackslash; } + if (lastSlash) { + strncpy(dirname, filename, lastSlash - filename); + AddDllDirectory(dirname); + } +#endif snprintf(name, sizeof(name), "@%s", filename); } filename = name; From b42b997f00b3b305e42cd1ceaae4f006a64b5fbc Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 8 Jun 2022 18:04:43 -0700 Subject: [PATCH 05/24] Scripting: Why does this function take PCWSTR?? --- src/script/engines/lua.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index 24e54c88e..096532b8d 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -486,7 +486,7 @@ bool _luaLoad(struct mScriptEngineContext* ctx, const char* filename, struct VFi snprintf(name, sizeof(name), "=%s", filename + 1); } else { #ifdef _WIN32 - char dirname[PATH_MAX] = {0}; + wchar_t dirname[PATH_MAX] = {0}; const char* lastSlash = strrchr(filename, '/'); const char* lastBackslash = strrchr(filename, '\\'); if (lastSlash && lastBackslash) { @@ -497,7 +497,7 @@ bool _luaLoad(struct mScriptEngineContext* ctx, const char* filename, struct VFi lastSlash = lastBackslash; } if (lastSlash) { - strncpy(dirname, filename, lastSlash - filename); + MultiByteToWideChar(CP_UTF8, 0, filename, lastSlash - filename, dirname, PATH_MAX); AddDllDirectory(dirname); } #endif From 3e4f1fcb2eb257db679110458521adb0d8e9aace Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 8 Jun 2022 21:44:01 -0700 Subject: [PATCH 06/24] Scripting: Lua requires should be relative to the file they were run from --- src/script/engines/lua.c | 101 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 94 insertions(+), 7 deletions(-) diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index 096532b8d..343b07cb2 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -49,6 +49,8 @@ static int _luaPairsTable(lua_State* lua); static int _luaGetList(lua_State* lua); static int _luaLenList(lua_State* lua); +static int _luaRequireShim(lua_State* lua); + #if LUA_VERSION_NUM < 503 #define lua_pushinteger lua_pushnumber #endif @@ -78,6 +80,8 @@ struct mScriptEngineContextLua { struct mScriptEngineContext d; lua_State* lua; int func; + char lastDirectory[PATH_MAX]; + int require; char* lastError; }; @@ -163,6 +167,9 @@ struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mS #endif lua_pop(luaContext->lua, 1); + lua_getglobal(luaContext->lua, "require"); + luaContext->require = luaL_ref(luaContext->lua, LUA_REGISTRYINDEX); + return &luaContext->d; } @@ -330,7 +337,7 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext, bool } return _luaCoerceFunction(luaContext); case LUA_TTABLE: - // This function pops the value internally via luaL_ref + // This function pops the value internally if (!pop) { break; } @@ -481,12 +488,11 @@ bool _luaLoad(struct mScriptEngineContext* ctx, const char* filename, struct VFi luaContext->lastError = NULL; } char name[PATH_MAX + 1]; + char dirname[PATH_MAX] = {0}; if (filename) { if (*filename == '*') { snprintf(name, sizeof(name), "=%s", filename + 1); } else { -#ifdef _WIN32 - wchar_t dirname[PATH_MAX] = {0}; const char* lastSlash = strrchr(filename, '/'); const char* lastBackslash = strrchr(filename, '\\'); if (lastSlash && lastBackslash) { @@ -497,10 +503,8 @@ bool _luaLoad(struct mScriptEngineContext* ctx, const char* filename, struct VFi lastSlash = lastBackslash; } if (lastSlash) { - MultiByteToWideChar(CP_UTF8, 0, filename, lastSlash - filename, dirname, PATH_MAX); - AddDllDirectory(dirname); + strncpy(dirname, filename, lastSlash - filename); } -#endif snprintf(name, sizeof(name), "@%s", filename); } filename = name; @@ -509,6 +513,11 @@ bool _luaLoad(struct mScriptEngineContext* ctx, const char* filename, struct VFi switch (ret) { case LUA_OK: luaContext->func = luaL_ref(luaContext->lua, LUA_REGISTRYINDEX); + if (dirname[0]) { + strncpy(luaContext->lastDirectory, dirname, sizeof(luaContext->lastDirectory)); + } else { + memset(luaContext->lastDirectory, 0, sizeof(luaContext->lastDirectory)); + } return true; case LUA_ERRSYNTAX: luaContext->lastError = strdup(lua_tostring(luaContext->lua, -1)); @@ -522,8 +531,21 @@ bool _luaLoad(struct mScriptEngineContext* ctx, const char* filename, struct VFi bool _luaRun(struct mScriptEngineContext* context) { struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) context; + + if (luaContext->lastDirectory[0]) { + // Shim require to look in the previous location + lua_pushstring(luaContext->lua, luaContext->lastDirectory); + lua_pushcclosure(luaContext->lua, _luaRequireShim, 1); + lua_setglobal(luaContext->lua, "require"); + } + lua_rawgeti(luaContext->lua, LUA_REGISTRYINDEX, luaContext->func); - return _luaInvoke(luaContext, NULL); + bool ret = _luaInvoke(luaContext, NULL); + + // Restore previous value of require + lua_rawgeti(luaContext->lua, LUA_REGISTRYINDEX, luaContext->require); + lua_setglobal(luaContext->lua, "require"); + return ret; } const char* _luaGetError(struct mScriptEngineContext* context) { @@ -962,3 +984,68 @@ static int _luaLenList(lua_State* lua) { lua_pushinteger(lua, mScriptListSize(list)); return 1; } + +static int _luaRequireShim(lua_State* lua) { + struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); + + const char* path = lua_tostring(lua, lua_upvalueindex(1)); + const char* oldpath; + const char* oldcpath; + + lua_getglobal(luaContext->lua, "package"); + + lua_pushliteral(luaContext->lua, "path"); + lua_pushliteral(luaContext->lua, "path"); + lua_gettable(luaContext->lua, -3); + oldpath = strdup(lua_tostring(luaContext->lua, -1)); + lua_pushliteral(luaContext->lua, ";"); + lua_pushstring(luaContext->lua, path); + lua_pushliteral(luaContext->lua, "/?.lua;"); + lua_pushstring(luaContext->lua, path); + lua_pushliteral(luaContext->lua, "/?/init.lua"); + lua_concat(luaContext->lua, 6); + lua_settable(luaContext->lua, -3); + +#ifdef _WIN32 +#define DLL "dll" +#elif defined(__APPLE__) +#define DLL "dylib" +#else +#define DLL "so" +#endif + lua_pushliteral(luaContext->lua, "cpath"); + lua_pushliteral(luaContext->lua, "cpath"); + lua_gettable(luaContext->lua, -3); + oldcpath = strdup(lua_tostring(luaContext->lua, -1)); + lua_pushliteral(luaContext->lua, ";"); + lua_pushstring(luaContext->lua, path); + lua_pushliteral(luaContext->lua, "/?." DLL ";"); + lua_pushstring(luaContext->lua, path); + lua_pushliteral(luaContext->lua, "/?/init." DLL); + lua_concat(luaContext->lua, 6); + lua_settable(luaContext->lua, -3); + + lua_pop(luaContext->lua, 1); + + lua_rawgeti(luaContext->lua, LUA_REGISTRYINDEX, luaContext->require); + lua_rotate(luaContext->lua, -2, 1); + int ret = lua_pcall(luaContext->lua, 1, 0, 0); + + lua_getglobal(luaContext->lua, "package"); + + lua_pushliteral(luaContext->lua, "path"); + lua_pushstring(luaContext->lua, oldpath); + lua_settable(luaContext->lua, -3); + + lua_pushliteral(luaContext->lua, "cpath"); + lua_pushstring(luaContext->lua, oldcpath); + lua_settable(luaContext->lua, -3); + + lua_pop(luaContext->lua, 1); + + if (ret) { + lua_error(luaContext->lua); + } + + return 0; +} From b43509e705808e6bcc720367124d2db92583d967 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 9 Jun 2022 01:00:46 -0700 Subject: [PATCH 07/24] Scripting: Free memory leak --- src/script/engines/lua.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index 343b07cb2..c101155da 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -989,15 +989,13 @@ static int _luaRequireShim(lua_State* lua) { struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); const char* path = lua_tostring(lua, lua_upvalueindex(1)); - const char* oldpath; - const char* oldcpath; lua_getglobal(luaContext->lua, "package"); lua_pushliteral(luaContext->lua, "path"); lua_pushliteral(luaContext->lua, "path"); lua_gettable(luaContext->lua, -3); - oldpath = strdup(lua_tostring(luaContext->lua, -1)); + char* oldpath = strdup(lua_tostring(luaContext->lua, -1)); lua_pushliteral(luaContext->lua, ";"); lua_pushstring(luaContext->lua, path); lua_pushliteral(luaContext->lua, "/?.lua;"); @@ -1016,7 +1014,7 @@ static int _luaRequireShim(lua_State* lua) { lua_pushliteral(luaContext->lua, "cpath"); lua_pushliteral(luaContext->lua, "cpath"); lua_gettable(luaContext->lua, -3); - oldcpath = strdup(lua_tostring(luaContext->lua, -1)); + char* oldcpath = strdup(lua_tostring(luaContext->lua, -1)); lua_pushliteral(luaContext->lua, ";"); lua_pushstring(luaContext->lua, path); lua_pushliteral(luaContext->lua, "/?." DLL ";"); @@ -1043,6 +1041,8 @@ static int _luaRequireShim(lua_State* lua) { lua_pop(luaContext->lua, 1); + free(oldpath); + free(oldcpath); if (ret) { lua_error(luaContext->lua); } From aee35a4442918fa9faaf4874d9e26a06ad7e7390 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 9 Jun 2022 14:54:30 -0700 Subject: [PATCH 08/24] Scripting: More cleanup --- src/script/engines/lua.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index c101155da..af9a18e77 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -182,6 +182,9 @@ void _luaDestroy(struct mScriptEngineContext* ctx) { if (luaContext->func > 0) { luaL_unref(luaContext->lua, LUA_REGISTRYINDEX, luaContext->func); } + if (luaContext->require > 0) { + luaL_unref(luaContext->lua, LUA_REGISTRYINDEX, luaContext->require); + } lua_close(luaContext->lua); free(luaContext); } @@ -1047,5 +1050,5 @@ static int _luaRequireShim(lua_State* lua) { lua_error(luaContext->lua); } - return 0; + return 1; } From 45b47c32d2090c3e89f8f47150b9b0057272b6a4 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 9 Jun 2022 16:18:34 -0700 Subject: [PATCH 09/24] Scripting: Much cleaner require shimming --- src/script/engines/lua.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index af9a18e77..aba42141c 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -80,7 +80,6 @@ struct mScriptEngineContextLua { struct mScriptEngineContext d; lua_State* lua; int func; - char lastDirectory[PATH_MAX]; int require; char* lastError; }; @@ -515,12 +514,15 @@ bool _luaLoad(struct mScriptEngineContext* ctx, const char* filename, struct VFi int ret = lua_load(luaContext->lua, _reader, &data, filename, "t"); switch (ret) { case LUA_OK: - luaContext->func = luaL_ref(luaContext->lua, LUA_REGISTRYINDEX); if (dirname[0]) { - strncpy(luaContext->lastDirectory, dirname, sizeof(luaContext->lastDirectory)); - } else { - memset(luaContext->lastDirectory, 0, sizeof(luaContext->lastDirectory)); + lua_getupvalue(luaContext->lua, -1, 1); + lua_pushliteral(luaContext->lua, "require"); + lua_pushstring(luaContext->lua, dirname); + lua_pushcclosure(luaContext->lua, _luaRequireShim, 1); + lua_rawset(luaContext->lua, -3); + lua_pop(luaContext->lua, 1); } + luaContext->func = luaL_ref(luaContext->lua, LUA_REGISTRYINDEX); return true; case LUA_ERRSYNTAX: luaContext->lastError = strdup(lua_tostring(luaContext->lua, -1)); @@ -535,20 +537,8 @@ bool _luaLoad(struct mScriptEngineContext* ctx, const char* filename, struct VFi bool _luaRun(struct mScriptEngineContext* context) { struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) context; - if (luaContext->lastDirectory[0]) { - // Shim require to look in the previous location - lua_pushstring(luaContext->lua, luaContext->lastDirectory); - lua_pushcclosure(luaContext->lua, _luaRequireShim, 1); - lua_setglobal(luaContext->lua, "require"); - } - lua_rawgeti(luaContext->lua, LUA_REGISTRYINDEX, luaContext->func); - bool ret = _luaInvoke(luaContext, NULL); - - // Restore previous value of require - lua_rawgeti(luaContext->lua, LUA_REGISTRYINDEX, luaContext->require); - lua_setglobal(luaContext->lua, "require"); - return ret; + return _luaInvoke(luaContext, NULL); } const char* _luaGetError(struct mScriptEngineContext* context) { From d9092c6acfa586400ecaaa0992daae2bd0b146d7 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 9 Jun 2022 16:37:40 -0700 Subject: [PATCH 10/24] Scripting: Invert search order --- src/script/engines/lua.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index aba42141c..3131f060d 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -986,15 +986,14 @@ static int _luaRequireShim(lua_State* lua) { lua_getglobal(luaContext->lua, "package"); lua_pushliteral(luaContext->lua, "path"); - lua_pushliteral(luaContext->lua, "path"); - lua_gettable(luaContext->lua, -3); - char* oldpath = strdup(lua_tostring(luaContext->lua, -1)); - lua_pushliteral(luaContext->lua, ";"); lua_pushstring(luaContext->lua, path); lua_pushliteral(luaContext->lua, "/?.lua;"); lua_pushstring(luaContext->lua, path); - lua_pushliteral(luaContext->lua, "/?/init.lua"); - lua_concat(luaContext->lua, 6); + lua_pushliteral(luaContext->lua, "/?/init.lua;"); + lua_pushliteral(luaContext->lua, "path"); + lua_gettable(luaContext->lua, -7); + char* oldpath = strdup(lua_tostring(luaContext->lua, -1)); + lua_concat(luaContext->lua, 5); lua_settable(luaContext->lua, -3); #ifdef _WIN32 @@ -1005,15 +1004,14 @@ static int _luaRequireShim(lua_State* lua) { #define DLL "so" #endif lua_pushliteral(luaContext->lua, "cpath"); - lua_pushliteral(luaContext->lua, "cpath"); - lua_gettable(luaContext->lua, -3); - char* oldcpath = strdup(lua_tostring(luaContext->lua, -1)); - lua_pushliteral(luaContext->lua, ";"); lua_pushstring(luaContext->lua, path); lua_pushliteral(luaContext->lua, "/?." DLL ";"); lua_pushstring(luaContext->lua, path); - lua_pushliteral(luaContext->lua, "/?/init." DLL); - lua_concat(luaContext->lua, 6); + lua_pushliteral(luaContext->lua, "/?/init." DLL ";"); + lua_pushliteral(luaContext->lua, "cpath"); + lua_gettable(luaContext->lua, -7); + char* oldcpath = strdup(lua_tostring(luaContext->lua, -1)); + lua_concat(luaContext->lua, 5); lua_settable(luaContext->lua, -3); lua_pop(luaContext->lua, 1); From 9ac6096c3d3be6636aa20d0c4d41fb2ca78cd514 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 9 Jun 2022 21:10:27 -0700 Subject: [PATCH 11/24] GB SIO: Fix bidirectional transfer starting (fixes #2290) --- CHANGES | 1 + src/gb/sio/lockstep.c | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index ae7e136e4..079ffdb8d 100644 --- a/CHANGES +++ b/CHANGES @@ -26,6 +26,7 @@ Emulation fixes: - GB I/O: Fix writing to WAVE RAM behavior (fixes mgba.io/i/1334) - GB Memory: Add cursory cartridge open bus emulation (fixes mgba.io/i/2032) - GB Serialize: Fix loading MBC1 states that affect bank 0 (fixes mgba.io/i/2402) + - GB SIO: Fix bidirectional transfer starting (fixes mgba.io/i/2290) - GB Video: Draw SGB border pieces that overlap GB graphics (fixes mgba.io/i/1339) - GBA: Improve timing when not booting from BIOS - GBA: Fix expected entry point for multiboot ELFs (fixes mgba.io/i/2450) diff --git a/src/gb/sio/lockstep.c b/src/gb/sio/lockstep.c index d739140e5..4d1f1f549 100644 --- a/src/gb/sio/lockstep.c +++ b/src/gb/sio/lockstep.c @@ -157,7 +157,7 @@ static int32_t _masterUpdate(struct GBSIOLockstepNode* node) { } } // Tell the other GBs they can continue up to where we were - node->p->d.addCycles(&node->p->d, 0, node->eventDiff); + node->p->d.addCycles(&node->p->d, node->id, node->eventDiff); #ifndef NDEBUG node->phase = node->p->d.transferActive; #endif @@ -252,6 +252,12 @@ static uint8_t GBSIOLockstepNodeWriteSC(struct GBSIODriver* driver, uint8_t valu mLockstepLock(&node->p->d); bool claimed = false; if (ATOMIC_CMPXCHG(node->p->masterClaimed, claimed, true)) { + if (node->id != 0) { + node->p->players[0]->id = 1; + node->p->players[1] = node->p->players[0]; + node->p->players[0] = node->p->players[1]; + node->id = 0; + } ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); ATOMIC_STORE(node->p->d.transferCycles, GBSIOCyclesPerTransfer[(value >> 1) & 1]); mTimingDeschedule(&driver->p->p->timing, &driver->p->event); From 601d025e3017bd56b7c12da842cba1edee5f6e03 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 10 Jun 2022 19:55:45 -0700 Subject: [PATCH 12/24] Scripting: Fix require return values --- src/script/engines/lua.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index 3131f060d..f8d71762e 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -981,6 +981,7 @@ static int _luaLenList(lua_State* lua) { static int _luaRequireShim(lua_State* lua) { struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); + int oldtop = lua_gettop(luaContext->lua); const char* path = lua_tostring(lua, lua_upvalueindex(1)); lua_getglobal(luaContext->lua, "package"); @@ -1018,7 +1019,7 @@ static int _luaRequireShim(lua_State* lua) { lua_rawgeti(luaContext->lua, LUA_REGISTRYINDEX, luaContext->require); lua_rotate(luaContext->lua, -2, 1); - int ret = lua_pcall(luaContext->lua, 1, 0, 0); + int ret = lua_pcall(luaContext->lua, 1, LUA_MULTRET, 0); lua_getglobal(luaContext->lua, "package"); @@ -1038,5 +1039,6 @@ static int _luaRequireShim(lua_State* lua) { lua_error(luaContext->lua); } - return 1; + int newtop = lua_gettop(luaContext->lua); + return newtop - oldtop + 1; } From c12fa581b356bdabd67b8d1a6f6f21a98694197f Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 10 Jun 2022 20:51:26 -0700 Subject: [PATCH 13/24] Scripting: Fix build with Lua 5.1 and 5.2 --- src/script/engines/lua.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index f8d71762e..f5adfcdd5 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -55,6 +55,14 @@ static int _luaRequireShim(lua_State* lua); #define lua_pushinteger lua_pushnumber #endif +#ifndef LUA_OK +#define LUA_OK 0 +#endif + +#if LUA_VERSION_NUM < 502 +#define luaL_traceback(L, M, S, level) lua_pushstring(L, S) +#endif + const struct mScriptType mSTLuaFunc = { .base = mSCRIPT_TYPE_FUNCTION, .size = 0, @@ -427,7 +435,8 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v } else { mScriptValueWrap(value, newValue); } - luaL_setmetatable(luaContext->lua, "mSTList"); + lua_getfield(luaContext->lua, LUA_REGISTRYINDEX, "mSTList"); + lua_setmetatable(luaContext->lua, -2); break; case mSCRIPT_TYPE_TABLE: newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); @@ -436,7 +445,8 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v } else { mScriptValueWrap(value, newValue); } - luaL_setmetatable(luaContext->lua, "mSTTable"); + lua_getfield(luaContext->lua, LUA_REGISTRYINDEX, "mSTTable"); + lua_setmetatable(luaContext->lua, -2); break; case mSCRIPT_TYPE_FUNCTION: newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); @@ -453,7 +463,8 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v } else { mScriptValueWrap(value, newValue); } - luaL_setmetatable(luaContext->lua, "mSTStruct"); + lua_getfield(luaContext->lua, LUA_REGISTRYINDEX, "mSTStruct"); + lua_setmetatable(luaContext->lua, -2); break; default: ok = false; @@ -511,7 +522,11 @@ bool _luaLoad(struct mScriptEngineContext* ctx, const char* filename, struct VFi } filename = name; } +#if LUA_VERSION_NUM >= 502 int ret = lua_load(luaContext->lua, _reader, &data, filename, "t"); +#else + int ret = lua_load(luaContext->lua, _reader, &data, filename); +#endif switch (ret) { case LUA_OK: if (dirname[0]) { @@ -662,8 +677,8 @@ void _luaDeref(struct mScriptValue* value) { static struct mScriptEngineContextLua* _luaGetContext(lua_State* lua) { lua_pushliteral(lua, "mCtx"); - int type = lua_rawget(lua, LUA_REGISTRYINDEX); - if (type != LUA_TLIGHTUSERDATA) { + lua_rawget(lua, LUA_REGISTRYINDEX); + if (lua_type(lua, -1) != LUA_TLIGHTUSERDATA) { lua_pop(lua, 1); lua_pushliteral(lua, "Function called from invalid context"); lua_error(lua); @@ -1018,7 +1033,7 @@ static int _luaRequireShim(lua_State* lua) { lua_pop(luaContext->lua, 1); lua_rawgeti(luaContext->lua, LUA_REGISTRYINDEX, luaContext->require); - lua_rotate(luaContext->lua, -2, 1); + lua_insert(luaContext->lua, -2); int ret = lua_pcall(luaContext->lua, 1, LUA_MULTRET, 0); lua_getglobal(luaContext->lua, "package"); From d2ac7c4ca0e2ff304b745c347ce4eb0c4f1b5507 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 10 Jun 2022 20:59:27 -0700 Subject: [PATCH 14/24] Qt: Add Lua to report view --- src/platform/qt/ReportView.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/platform/qt/ReportView.cpp b/src/platform/qt/ReportView.cpp index edfddd21c..b38483e7f 100644 --- a/src/platform/qt/ReportView.cpp +++ b/src/platform/qt/ReportView.cpp @@ -61,6 +61,10 @@ #include #endif +#ifdef USE_LUA +#include +#endif + #ifdef USE_LZMA #include <7zVersion.h> #endif @@ -168,6 +172,11 @@ void ReportView::generateReport() { #else swReport << QString("libLZMA not linked"); #endif +#ifdef USE_LUA + swReport << QString("Lua version: %1").arg(QLatin1String(LUA_RELEASE)); +#else + swReport << QString("Lua not linked"); +#endif #ifdef USE_MINIZIP swReport << QString("minizip linked"); #else From 7f453ce20294789a50584d5614a3e371dfa411e5 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 14 Jun 2022 00:13:51 -0700 Subject: [PATCH 15/24] GBA Video: Fix horizontal lines in GL when charbase is changed (fixes #1631) --- CHANGES | 1 + include/mgba/internal/gba/renderers/gl.h | 2 ++ src/gba/renderers/gl.c | 26 ++++++++++++++++-------- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/CHANGES b/CHANGES index 079ffdb8d..fed10442a 100644 --- a/CHANGES +++ b/CHANGES @@ -45,6 +45,7 @@ Emulation fixes: - GBA Video: Fix rare crash in modes 3-5 - GBA Video: Fix sprites with mid-frame palette changes in GL (fixes mgba.io/i/2476) - GBA Video: Fix OBJ tile wrapping with 2D char mapping (fixes mgba.io/i/2443) + - GBA Video: Fix horizontal lines in GL when charbase is changed (fixes mgba.io/i/1631) Other fixes: - ARM: Disassemble Thumb mov pseudo-instruction properly - Core: Don't attempt to restore rewind diffs past start of rewind diff --git a/include/mgba/internal/gba/renderers/gl.h b/include/mgba/internal/gba/renderers/gl.h index 42068e53f..9e022f6f5 100644 --- a/include/mgba/internal/gba/renderers/gl.h +++ b/include/mgba/internal/gba/renderers/gl.h @@ -49,6 +49,7 @@ struct GBAVideoGLBackground { int enabled; unsigned priority; uint32_t charBase; + uint32_t oldCharBase; int mosaic; int multipalette; uint32_t screenBase; @@ -99,6 +100,7 @@ enum { GBA_GL_BG_TRANSFORM, GBA_GL_BG_RANGE, GBA_GL_BG_MOSAIC, + GBA_GL_BG_OLDCHARBASE, GBA_GL_OBJ_VRAM = 2, GBA_GL_OBJ_PALETTE, diff --git a/src/gba/renderers/gl.c b/src/gba/renderers/gl.c index 0fcd70617..a06b2e4d6 100644 --- a/src/gba/renderers/gl.c +++ b/src/gba/renderers/gl.c @@ -204,6 +204,7 @@ static const struct GBAVideoGLUniform _uniformsMode2[] = { { "vram", GBA_GL_BG_VRAM, }, { "palette", GBA_GL_BG_PALETTE, }, { "screenBase", GBA_GL_BG_SCREENBASE, }, + { "oldCharBase", GBA_GL_BG_OLDCHARBASE, }, { "charBase", GBA_GL_BG_CHARBASE, }, { "size", GBA_GL_BG_SIZE, }, { "offset", GBA_GL_BG_OFFSET, }, @@ -240,6 +241,7 @@ static const char* const _renderMode2 = "uniform isampler2D vram;\n" "uniform sampler2D palette;\n" "uniform int screenBase;\n" + "uniform ivec2 oldCharBase;\n" "uniform int charBase;\n" "uniform int size;\n" "uniform ivec4 transform[160];\n" @@ -256,7 +258,17 @@ static const char* const _renderMode2 = " int mapAddress = screenBase + (map >> 1);\n" " int twomaps = texelFetch(vram, ivec2(mapAddress & 255, mapAddress >> 8), 0).r;\n" " int tile = (twomaps >> (8 * (map & 1))) & 255;\n" - " int address = charBase + tile * 32 + ((coord.x >> 9) & 3) + ((coord.y >> 6) & 0x1C);\n" + " int newCharBase = charBase;\n" + " if (newCharBase != oldCharBase.x) {\n" + " int y = int(texCoord.y);\n" + // If the charbase has changed (and the scale is greater than 1), we might still be drawing + // the tile associated with the pixel above us. If we're still on that tile, we want to use + // the charbase associated with it instead of the new one. Cf. https://mgba.io/i/1631 + " if (y == oldCharBase.y && transform[y - 1].w >> 11 == coord.y >> 11) {\n" + " newCharBase = oldCharBase.x;\n" + " }\n" + " }\n" + " int address = newCharBase + tile * 32 + ((coord.x >> 9) & 3) + ((coord.y >> 6) & 0x1C);\n" " int halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0).r;\n" " int entry = (halfrow >> (8 * ((coord.x >> 8) & 1))) & 255;\n" " if (entry == 0) {\n" @@ -1012,42 +1024,34 @@ uint16_t GBAVideoGLRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, case REG_BG0HOFS: value &= 0x01FF; glRenderer->bg[0].x = value; - dirty = false; break; case REG_BG0VOFS: value &= 0x01FF; glRenderer->bg[0].y = value; - dirty = false; break; case REG_BG1HOFS: value &= 0x01FF; glRenderer->bg[1].x = value; - dirty = false; break; case REG_BG1VOFS: value &= 0x01FF; glRenderer->bg[1].y = value; - dirty = false; break; case REG_BG2HOFS: value &= 0x01FF; glRenderer->bg[2].x = value; - dirty = false; break; case REG_BG2VOFS: value &= 0x01FF; glRenderer->bg[2].y = value; - dirty = false; break; case REG_BG3HOFS: value &= 0x01FF; glRenderer->bg[3].x = value; - dirty = false; break; case REG_BG3VOFS: value &= 0x01FF; glRenderer->bg[3].y = value; - dirty = false; break; case REG_BG2PA: glRenderer->bg[2].affine.dx = value; @@ -1346,6 +1350,7 @@ void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) { if (_needsVramUpload(glRenderer, y) || glRenderer->oamDirty || glRenderer->regsDirty) { if (glRenderer->firstY >= 0) { _drawScanlines(glRenderer, y - 1); + glRenderer->firstY = y; glBindVertexArray(0); } } @@ -1620,6 +1625,7 @@ static void GBAVideoGLRendererUpdateDISPCNT(struct GBAVideoGLRenderer* renderer) static void GBAVideoGLRendererWriteBGCNT(struct GBAVideoGLBackground* bg, uint16_t value) { bg->priority = GBARegisterBGCNTGetPriority(value); + bg->oldCharBase = bg->charBase; bg->charBase = GBARegisterBGCNTGetCharBase(value) << 13; bg->mosaic = GBARegisterBGCNTGetMosaic(value); bg->multipalette = GBARegisterBGCNTGet256Color(value); @@ -1898,10 +1904,12 @@ void GBAVideoGLRendererDrawBackgroundMode2(struct GBAVideoGLRenderer* renderer, glBindVertexArray(shader->vao); _prepareTransform(renderer, background, uniforms, y); glUniform1i(uniforms[GBA_GL_BG_SCREENBASE], background->screenBase); + glUniform2i(uniforms[GBA_GL_BG_OLDCHARBASE], background->oldCharBase, renderer->firstY); glUniform1i(uniforms[GBA_GL_BG_CHARBASE], background->charBase); glUniform1i(uniforms[GBA_GL_BG_SIZE], background->size); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); + background->oldCharBase = background->charBase; } void GBAVideoGLRendererDrawBackgroundMode3(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) { From 4707368d7fc89ca5c6d49f0770bf4a06ebce9c85 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 14 Jun 2022 00:17:54 -0700 Subject: [PATCH 16/24] Qt: Fix typo --- src/platform/qt/ConfigController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index 4fc4037e0..767cd277f 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -136,7 +136,7 @@ ConfigController::ConfigController(QObject* parent) m_subparsers[1].usage = "Frontend options:\n" " --ecard FILE Scan an e-Reader card in the first loaded game\n" - " Can be paassed multiple times for multiple cards\n" + " Can be passed multiple times for multiple cards\n" " --mb FILE Boot a multiboot image with FILE inserted into the ROM slot"; m_subparsers[1].parse = nullptr; m_subparsers[1].parseLong = [](struct mSubParser* parser, const char* option, const char* arg) { From f7d8b77bd47cf0d762496ad3a25aefa9ebd45ac9 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 14 Jun 2022 15:15:11 -0700 Subject: [PATCH 17/24] GB MBC: Fix edge case with Pocket Cam register accesses (fixes #2557) --- CHANGES | 1 + src/gb/mbc.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/CHANGES b/CHANGES index fed10442a..390ac425f 100644 --- a/CHANGES +++ b/CHANGES @@ -25,6 +25,7 @@ Emulation fixes: - GB Audio: Fix APU re-enable timing glitch - GB I/O: Fix writing to WAVE RAM behavior (fixes mgba.io/i/1334) - GB Memory: Add cursory cartridge open bus emulation (fixes mgba.io/i/2032) + - GB MBC: Fix edge case with Pocket Cam register accesses (fixes mgba.io/i/2557) - GB Serialize: Fix loading MBC1 states that affect bank 0 (fixes mgba.io/i/2402) - GB SIO: Fix bidirectional transfer starting (fixes mgba.io/i/2290) - GB Video: Draw SGB border pieces that overlap GB graphics (fixes mgba.io/i/1339) diff --git a/src/gb/mbc.c b/src/gb/mbc.c index 9e8724964..282891cfe 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -1402,11 +1402,16 @@ void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value) { if (value < 0x10) { GBMBCSwitchSramBank(gb, value); memory->mbcState.pocketCam.registersActive = false; + memory->directSramAccess = true; } else { memory->mbcState.pocketCam.registersActive = true; + memory->directSramAccess = false; } break; case 0x5: + if (!memory->mbcState.pocketCam.registersActive) { + break; + } address &= 0x7F; if (address == 0 && value & 1) { value &= 6; // TODO: Timing From 85737f1103bcde52c33884ab55c05142b4ce82ad Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 14 Jun 2022 20:15:26 -0700 Subject: [PATCH 18/24] SDL: Support exposing an axis directly as the gyro value (closes #2531) --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index 390ac425f..891e2ca6f 100644 --- a/CHANGES +++ b/CHANGES @@ -92,6 +92,7 @@ Misc: - Qt: Add e-Card passing to the command line (closes mgba.io/i/2474) - Qt: Boot both a multiboot image and ROM with CLI args (closes mgba.io/i/1941) - Qt: Improve cheat parsing (fixes mgba.io/i/2297) + - SDL: Support exposing an axis directly as the gyro value (closes mgba.io/i/2531) - Windows: Attach to console if present - Vita: Add bilinear filtering option (closes mgba.io/i/344) From a0613e27ab7817dd180835f0900c4b3364b3bb71 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 14 Jun 2022 21:12:53 -0700 Subject: [PATCH 19/24] SDL: Support exposing an axis directly as the gyro value (closes #2531) --- src/platform/qt/InputController.cpp | 10 ++++++++++ src/platform/qt/SensorView.ui | 4 ++-- src/platform/sdl/sdl-events.c | 14 ++++++++++++++ src/platform/sdl/sdl-events.h | 1 + 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index 9a8458e1d..a0fb91397 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -341,6 +341,11 @@ void InputController::registerGyroAxisX(int axis) { #ifdef BUILD_SDL if (m_playerAttached) { m_sdlPlayer.rotation.gyroX = axis; + if (m_sdlPlayer.rotation.gyroY == axis) { + m_sdlPlayer.rotation.gyroZ = axis; + } else { + m_sdlPlayer.rotation.gyroZ = -1; + } } #endif } @@ -349,6 +354,11 @@ void InputController::registerGyroAxisY(int axis) { #ifdef BUILD_SDL if (m_playerAttached) { m_sdlPlayer.rotation.gyroY = axis; + if (m_sdlPlayer.rotation.gyroX == axis) { + m_sdlPlayer.rotation.gyroZ = axis; + } else { + m_sdlPlayer.rotation.gyroZ = -1; + } } #endif } diff --git a/src/platform/qt/SensorView.ui b/src/platform/qt/SensorView.ui index d75b5329d..7d65ba52c 100644 --- a/src/platform/qt/SensorView.ui +++ b/src/platform/qt/SensorView.ui @@ -284,10 +284,10 @@ false - -2147483647 + -1073741823 - 2147483647 + 1073741823 0 diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index b7e8b002b..a675ff2c0 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -199,6 +199,7 @@ bool mSDLAttachPlayer(struct mSDLEvents* events, struct mSDLPlayer* player) { player->rotation.gyroSensitivity = 2.2e9f; player->rotation.gyroX = 0; player->rotation.gyroY = 1; + player->rotation.gyroZ = -1; player->rotation.zDelta = 0; CircleBufferInit(&player->rotation.zHistory, sizeof(float) * GYRO_STEPS); player->rotation.p = player; @@ -327,6 +328,13 @@ void mSDLPlayerLoadConfig(struct mSDLPlayer* context, const struct Configuration context->rotation.gyroY = axis; } } + value = mInputGetCustomValue(config, "gba", SDL_BINDING_BUTTON, "gyroAxisZ", name); + if (value) { + axis = strtol(value, &end, 0); + if (axis >= 0 && axis < numAxes && end && !*end) { + context->rotation.gyroZ = axis; + } + } value = mInputGetCustomValue(config, "gba", SDL_BINDING_BUTTON, "gyroSensitivity", name); if (value) { float sensitivity = strtof_u(value, &end); @@ -357,6 +365,8 @@ void mSDLPlayerSaveConfig(const struct mSDLPlayer* context, struct Configuration mInputSetCustomValue(config, "gba", SDL_BINDING_BUTTON, "gyroAxisX", value, name); snprintf(value, sizeof(value), "%i", context->rotation.gyroY); mInputSetCustomValue(config, "gba", SDL_BINDING_BUTTON, "gyroAxisY", value, name); + snprintf(value, sizeof(value), "%i", context->rotation.gyroZ); + mInputSetCustomValue(config, "gba", SDL_BINDING_BUTTON, "gyroAxisZ", value, name); snprintf(value, sizeof(value), "%g", context->rotation.gyroSensitivity); mInputSetCustomValue(config, "gba", SDL_BINDING_BUTTON, "gyroSensitivity", value, name); } @@ -750,6 +760,10 @@ static void _mSDLRotationSample(struct mRotationSource* source) { } } #endif + if (rotation->gyroZ >= 0) { + rotation->zDelta = SDL_JoystickGetAxis(rotation->p->joystick->joystick, rotation->gyroZ) / 1.e5f; + return; + } int x = SDL_JoystickGetAxis(rotation->p->joystick->joystick, rotation->gyroX); int y = SDL_JoystickGetAxis(rotation->p->joystick->joystick, rotation->gyroY); diff --git a/src/platform/sdl/sdl-events.h b/src/platform/sdl/sdl-events.h index 1763865e2..8bdbd7c0b 100644 --- a/src/platform/sdl/sdl-events.h +++ b/src/platform/sdl/sdl-events.h @@ -95,6 +95,7 @@ struct mSDLPlayer { // Gyro int gyroX; int gyroY; + int gyroZ; float gyroSensitivity; struct CircleBuffer zHistory; int oldX; From 33b3d33da2f4e3c201834420e21b92d7fbffa443 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 15 Jun 2022 18:39:38 -0700 Subject: [PATCH 20/24] Core: Rename GBAStereoSample to mStereoSample --- include/mgba/core/interface.h | 5 +++++ include/mgba/internal/gba/audio.h | 8 ++------ src/gba/extra/audio-mixer.c | 4 ++-- src/platform/psp2/psp2-context.c | 4 ++-- src/platform/qt/AudioDevice.cpp | 8 ++++---- src/platform/switch/main.c | 4 ++-- src/platform/wii/main.c | 6 +++--- 7 files changed, 20 insertions(+), 19 deletions(-) diff --git a/include/mgba/core/interface.h b/include/mgba/core/interface.h index a480db6e5..fadaaa29d 100644 --- a/include/mgba/core/interface.h +++ b/include/mgba/core/interface.h @@ -194,6 +194,11 @@ struct mAVStream { void (*postAudioBuffer)(struct mAVStream*, struct blip_t* left, struct blip_t* right); }; +struct mStereoSample { + int16_t left; + int16_t right; +}; + struct mKeyCallback { uint16_t (*readKeys)(struct mKeyCallback*); bool requireOpposingDirections; diff --git a/include/mgba/internal/gba/audio.h b/include/mgba/internal/gba/audio.h index 65336b299..26fe69992 100644 --- a/include/mgba/internal/gba/audio.h +++ b/include/mgba/internal/gba/audio.h @@ -11,6 +11,7 @@ CXX_GUARD_START #include +#include #include #include #include @@ -94,11 +95,6 @@ struct GBAAudio { struct mTimingEvent sampleEvent; }; -struct GBAStereoSample { - int16_t left; - int16_t right; -}; - struct GBAMP2kADSR { uint8_t attack; uint8_t decay; @@ -278,7 +274,7 @@ struct GBAAudioMixer { double tempo; double frame; - struct GBAStereoSample last; + struct mStereoSample last; }; void GBAAudioInit(struct GBAAudio* audio, size_t samples); diff --git a/src/gba/extra/audio-mixer.c b/src/gba/extra/audio-mixer.c index fe9ed6901..503fdc8ad 100644 --- a/src/gba/extra/audio-mixer.c +++ b/src/gba/extra/audio-mixer.c @@ -125,7 +125,7 @@ static void _stepSample(struct GBAAudioMixer* mixer, struct GBAMP2kTrack* track) for (nSample = 0; nSample < updates; ++nSample) { int8_t sample = memory->load8(cpu, sampleBase + sampleI, 0); - struct GBAStereoSample stereo = { + struct mStereoSample stereo = { (sample * track->channel->leftVolume * track->channel->envelopeV) >> 9, (sample * track->channel->rightVolume * track->channel->envelopeV) >> 9 }; @@ -277,7 +277,7 @@ void _mp2kStep(struct GBAAudioMixer* mixer) { uint32_t interval = mixer->p->sampleInterval / OVERSAMPLE; int i; for (i = 0; i < OVERSAMPLE; ++i) { - struct GBAStereoSample sample = {0}; + struct mStereoSample sample = {0}; size_t track; for (track = 0; track < MP2K_MAX_SOUND_CHANNELS; ++track) { if (!mixer->activeTracks[track].channel->status) { diff --git a/src/platform/psp2/psp2-context.c b/src/platform/psp2/psp2-context.c index 82f0c6029..f860bc4a1 100644 --- a/src/platform/psp2/psp2-context.c +++ b/src/platform/psp2/psp2-context.c @@ -88,7 +88,7 @@ static vita2d_texture* backdrop = 0; #define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * 16) static struct mPSP2AudioContext { - struct GBAStereoSample buffer[PSP2_AUDIO_BUFFER_SIZE]; + struct mStereoSample buffer[PSP2_AUDIO_BUFFER_SIZE]; size_t writeOffset; size_t readOffset; size_t samples; @@ -255,7 +255,7 @@ static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* rig } ConditionWait(&audioContext.cond, &audioContext.mutex); } - struct GBAStereoSample* samples = &audioContext.buffer[audioContext.writeOffset]; + struct mStereoSample* samples = &audioContext.buffer[audioContext.writeOffset]; blip_read_samples(left, &samples[0].left, PSP2_SAMPLES, true); blip_read_samples(right, &samples[0].right, PSP2_SAMPLES, true); audioContext.samples += PSP2_SAMPLES; diff --git a/src/platform/qt/AudioDevice.cpp b/src/platform/qt/AudioDevice.cpp index 9baf12712..74f863444 100644 --- a/src/platform/qt/AudioDevice.cpp +++ b/src/platform/qt/AudioDevice.cpp @@ -45,17 +45,17 @@ qint64 AudioDevice::readData(char* data, qint64 maxSize) { return 0; } - maxSize /= sizeof(GBAStereoSample); + maxSize /= sizeof(mStereoSample); mCoreSyncLockAudio(&m_context->impl->sync); int available = std::min({ blip_samples_avail(m_context->core->getAudioChannel(m_context->core, 0)), maxSize, std::numeric_limits::max() }); - blip_read_samples(m_context->core->getAudioChannel(m_context->core, 0), &reinterpret_cast(data)->left, available, true); - blip_read_samples(m_context->core->getAudioChannel(m_context->core, 1), &reinterpret_cast(data)->right, available, true); + blip_read_samples(m_context->core->getAudioChannel(m_context->core, 0), &reinterpret_cast(data)->left, available, true); + blip_read_samples(m_context->core->getAudioChannel(m_context->core, 1), &reinterpret_cast(data)->right, available, true); mCoreSyncConsumeAudio(&m_context->impl->sync); - return available * sizeof(GBAStereoSample); + return available * sizeof(mStereoSample); } qint64 AudioDevice::writeData(const char*, qint64) { diff --git a/src/platform/switch/main.c b/src/platform/switch/main.c index 0a40d3a90..25b6f6eca 100644 --- a/src/platform/switch/main.c +++ b/src/platform/switch/main.c @@ -115,7 +115,7 @@ static float gyroZ = 0; static float tiltX = 0; static float tiltY = 0; -static struct GBAStereoSample audioBuffer[N_BUFFERS][BUFFER_SIZE / 4] __attribute__((__aligned__(0x1000))); +static struct mStereoSample audioBuffer[N_BUFFERS][BUFFER_SIZE / 4] __attribute__((__aligned__(0x1000))); static enum ScreenMode { SM_PA, @@ -584,7 +584,7 @@ static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* rig blip_clear(right); return; } - struct GBAStereoSample* samples = audioBuffer[audioBufferActive]; + struct mStereoSample* samples = audioBuffer[audioBufferActive]; blip_read_samples(left, &samples[0].left, SAMPLES, true); blip_read_samples(right, &samples[0].right, SAMPLES, true); audoutAppendAudioOutBuffer(&audoutBuffer[audioBufferActive]); diff --git a/src/platform/wii/main.c b/src/platform/wii/main.c index 8b3ed8226..36bb9abfc 100644 --- a/src/platform/wii/main.c +++ b/src/platform/wii/main.c @@ -141,7 +141,7 @@ static void* framebuffer[2] = { 0, 0 }; static int whichFb = 0; static struct AudioBuffer { - struct GBAStereoSample samples[SAMPLES] __attribute__((__aligned__(32))); + struct mStereoSample samples[SAMPLES] __attribute__((__aligned__(32))); volatile size_t size; } audioBuffer[BUFFERS] = {0}; static volatile int currentAudioBuffer = 0; @@ -685,8 +685,8 @@ static void _audioDMA(void) { if (buffer->size != SAMPLES) { return; } - DCFlushRange(buffer->samples, SAMPLES * sizeof(struct GBAStereoSample)); - AUDIO_InitDMA((u32) buffer->samples, SAMPLES * sizeof(struct GBAStereoSample)); + DCFlushRange(buffer->samples, SAMPLES * sizeof(struct mStereoSample)); + AUDIO_InitDMA((u32) buffer->samples, SAMPLES * sizeof(struct mStereoSample)); buffer->size = 0; currentAudioBuffer = (currentAudioBuffer + 1) % BUFFERS; } From 6159c5a70b6a92cb18948e9e1d7d214b3cdc9a48 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 15 Jun 2022 20:34:06 -0700 Subject: [PATCH 21/24] GBA Audio: Decrunchify GB audio --- include/mgba/internal/gba/audio.h | 8 +- include/mgba/internal/gba/serialize.h | 37 +++++++--- src/gb/audio.c | 19 +++-- src/gba/audio.c | 102 ++++++++++++++++++++------ src/gba/serialize.c | 2 +- src/gba/video.c | 7 +- 6 files changed, 125 insertions(+), 50 deletions(-) diff --git a/include/mgba/internal/gba/audio.h b/include/mgba/internal/gba/audio.h index 26fe69992..8b212dd6a 100644 --- a/include/mgba/internal/gba/audio.h +++ b/include/mgba/internal/gba/audio.h @@ -80,14 +80,16 @@ struct GBAAudio { bool enable; size_t samples; - unsigned sampleRate; - GBARegisterSOUNDBIAS soundbias; struct GBAAudioMixer* mixer; bool externalMixing; int32_t sampleInterval; + int32_t lastSample; + int sampleIndex; + struct mStereoSample currentSamples[GBA_MAX_SAMPLES]; + bool forceDisableChA; bool forceDisableChB; int masterVolume; @@ -305,6 +307,8 @@ uint32_t GBAAudioReadWaveRAM(struct GBAAudio* audio, int address); uint32_t GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value); void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles); +void GBAAudioSample(struct GBAAudio* audio, int32_t timestamp); + struct GBASerializedState; void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state); void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state); diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index f3ed332dc..07223ea3f 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -71,7 +71,7 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * | 0x00188 - 0x0018B: Next event * 0x0018C - 0x001AB: Audio FIFO 1 * 0x001AC - 0x001CB: Audio FIFO 2 - * 0x001CC - 0x001DF: Audio miscellaneous state + * 0x001CC - 0x001EF: Audio miscellaneous state * | 0x001CC - 0x001CF: Channel A internal audio samples * | 0x001D0 - 0x001D3: Channel B internal audio samples * | 0x001D4 - 0x001D7: Next sample @@ -104,9 +104,13 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * | bit 3: Is channel 3's memory readable? * | bit 4: Skip frame * | bits 5 - 7: Reserved - * 0x001E0 - 0x001FF: Video miscellaneous state - * | 0x001E0 - 0x001E3: Next event - * | 0x001E4 - 0x001F7: Reserved + * | 0x001E0 - 0x001E3: Last sample + * | 0x001E4 - 0x001E7: Additional audio flags + * | bits 0 - 3: Current sample index + * | 0x001E8 - 0x001EF: Reserved + * 0x001F0 - 0x001FF: Video miscellaneous state + * | 0x001F0 - 0x001F3: Reserved + * | 0x001F4 - 0x001F7: Next event * | 0x001F8 - 0x001FB: Miscellaneous flags * | 0x001FC - 0x001FF: Frame counter * 0x00200 - 0x00213: Timer 0 @@ -227,7 +231,8 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * 0x00368 - 0x0036F: Reserved (leave zero) * 0x00370 - 0x0037F: Audio FIFO A samples * 0x00380 - 0x0038F: Audio FIFO B samples - * 0x00390 - 0x003FF: Reserved (leave zero) + * 0x00390 - 0x003CF: Audio rendered samples + * 0x003D0 - 0x003FF: Reserved (leave zero) * 0x00400 - 0x007FF: I/O memory * 0x00800 - 0x00BFF: Palette * 0x00C00 - 0x00FFF: OAM @@ -243,6 +248,9 @@ DECL_BITS(GBASerializedAudioFlags, FIFOSamplesB, 2, 3); // Yay legacy? DECL_BITS(GBASerializedAudioFlags, FIFOInternalSamplesA, 5, 2); DECL_BITS(GBASerializedAudioFlags, FIFOSamplesA, 7, 3); +DECL_BITFIELD(GBASerializedAudioFlags2, uint32_t); +DECL_BITS(GBASerializedAudioFlags2, SampleIndex, 0, 4); + DECL_BITFIELD(GBASerializedVideoFlags, uint32_t); DECL_BITS(GBASerializedVideoFlags, Mode, 0, 2); @@ -303,11 +311,14 @@ struct GBASerializedState { int8_t sampleB; GBASerializedAudioFlags gbaFlags; GBSerializedAudioFlags flags; + int32_t lastSample; + GBASerializedAudioFlags2 gbaFlags2; + int32_t reserved[2]; } audio; struct { + int32_t reserved; int32_t nextEvent; - int32_t reserved[5]; GBASerializedVideoFlags flags; uint32_t frameCounter; } video; @@ -384,14 +395,16 @@ struct GBASerializedState { int32_t biosStall; uint32_t matrixMappings[16]; - uint32_t reservedMatrix[2]; + uint32_t reservedMatrix[2]; - struct { - int8_t chA[16]; - int8_t chB[16]; - } samples; + struct { + int8_t chA[16]; + int8_t chB[16]; + } samples; - uint32_t reserved[28]; + struct mStereoSample currentSamples[16]; + + uint32_t reserved[12]; uint16_t io[SIZE_IO >> 1]; uint16_t pram[SIZE_PALETTE_RAM >> 1]; diff --git a/src/gb/audio.c b/src/gb/audio.c index 47911f794..94a1c40c4 100644 --- a/src/gb/audio.c +++ b/src/gb/audio.c @@ -11,6 +11,9 @@ #include #include #include +#ifdef M_CORE_GBA +#include +#endif #ifdef __3DS__ #define blip_add_delta blip_add_delta_fast @@ -69,7 +72,6 @@ void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52, enum GBAu audio->timingFactor = 2; } - audio->frameEvent.context = audio; audio->frameEvent.name = "GB Audio Frame Sequencer"; audio->frameEvent.callback = _updateFrame; audio->frameEvent.priority = 0x10; @@ -85,14 +87,10 @@ void GBAudioDeinit(struct GBAudio* audio) { } void GBAudioReset(struct GBAudio* audio) { - mTimingDeschedule(audio->timing, &audio->frameEvent); mTimingDeschedule(audio->timing, &audio->sampleEvent); if (audio->style != GB_AUDIO_GBA) { mTimingSchedule(audio->timing, &audio->sampleEvent, 0); } - if (audio->style == GB_AUDIO_GBA) { - mTimingSchedule(audio->timing, &audio->frameEvent, 0); - } audio->ch1 = (struct GBAudioSquareChannel) { .sweep = { .time = 8 }, .envelope = { .dead = 2 } }; audio->ch2 = (struct GBAudioSquareChannel) { .envelope = { .dead = 2 } }; audio->ch3 = (struct GBAudioWaveChannel) { .bank = 0 }; @@ -458,11 +456,12 @@ void GBAudioWriteNR52(struct GBAudio* audio, uint8_t value) { } void _updateFrame(struct mTiming* timing, void* user, uint32_t cyclesLate) { - struct GBAudio* audio = user; - GBAudioUpdateFrame(audio); - if (audio->style == GB_AUDIO_GBA) { - mTimingSchedule(timing, &audio->frameEvent, audio->timingFactor * FRAME_CYCLES - cyclesLate); - } +#ifdef M_CORE_GBA + struct GBAAudio* audio = user; + GBAAudioSample(audio, mTimingCurrentTime(timing)); + mTimingSchedule(timing, &audio->psg.frameEvent, audio->psg.timingFactor * FRAME_CYCLES - cyclesLate); + GBAudioUpdateFrame(&audio->psg); +#endif } void GBAudioRun(struct GBAudio* audio, int32_t timestamp, int channels) { diff --git a/src/gba/audio.c b/src/gba/audio.c index 00c20bd46..c4f989f63 100644 --- a/src/gba/audio.c +++ b/src/gba/audio.c @@ -44,6 +44,7 @@ void GBAAudioInit(struct GBAAudio* audio, size_t samples) { GBAudioInit(&audio->psg, 0, nr52, GB_AUDIO_GBA); audio->psg.timing = &audio->p->timing; audio->psg.clockRate = GBA_ARM7TDMI_FREQUENCY; + audio->psg.frameEvent.context = audio; audio->samples = samples; // Guess too large; we hang producing extra samples if we guess too low blip_set_rates(audio->psg.left, GBA_ARM7TDMI_FREQUENCY, 96000); @@ -58,6 +59,8 @@ void GBAAudioInit(struct GBAAudio* audio, size_t samples) { void GBAAudioReset(struct GBAAudio* audio) { GBAudioReset(&audio->psg); + mTimingDeschedule(&audio->p->timing, &audio->psg.frameEvent); + mTimingSchedule(&audio->p->timing, &audio->psg.frameEvent, 0); mTimingDeschedule(&audio->p->timing, &audio->sampleEvent); mTimingSchedule(&audio->p->timing, &audio->sampleEvent, 0); audio->chA.dmaSource = 1; @@ -77,11 +80,12 @@ void GBAAudioReset(struct GBAAudio* audio) { audio->chA.samples[i] = 0; audio->chB.samples[i] = 0; } - audio->sampleRate = 0x8000; audio->soundbias = 0x200; audio->volume = 0; audio->volumeChA = false; audio->volumeChB = false; + audio->lastSample = 0; + audio->sampleIndex = 0; audio->chARight = false; audio->chALeft = false; audio->chATimer = false; @@ -89,7 +93,7 @@ void GBAAudioReset(struct GBAAudio* audio) { audio->chBLeft = false; audio->chBTimer = false; audio->enable = false; - audio->sampleInterval = GBA_ARM7TDMI_FREQUENCY / audio->sampleRate; + audio->sampleInterval = GBA_ARM7TDMI_FREQUENCY / 0x8000; audio->psg.sampleInterval = audio->sampleInterval; blip_clear(audio->psg.left); @@ -141,56 +145,67 @@ void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA* } void GBAAudioWriteSOUND1CNT_LO(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR10(&audio->psg, value); } void GBAAudioWriteSOUND1CNT_HI(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR11(&audio->psg, value); GBAudioWriteNR12(&audio->psg, value >> 8); } void GBAAudioWriteSOUND1CNT_X(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR13(&audio->psg, value); GBAudioWriteNR14(&audio->psg, value >> 8); } void GBAAudioWriteSOUND2CNT_LO(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR21(&audio->psg, value); GBAudioWriteNR22(&audio->psg, value >> 8); } void GBAAudioWriteSOUND2CNT_HI(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR23(&audio->psg, value); GBAudioWriteNR24(&audio->psg, value >> 8); } void GBAAudioWriteSOUND3CNT_LO(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); audio->psg.ch3.size = GBAudioRegisterBankGetSize(value); audio->psg.ch3.bank = GBAudioRegisterBankGetBank(value); GBAudioWriteNR30(&audio->psg, value); } void GBAAudioWriteSOUND3CNT_HI(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR31(&audio->psg, value); audio->psg.ch3.volume = GBAudioRegisterBankVolumeGetVolumeGBA(value >> 8); } void GBAAudioWriteSOUND3CNT_X(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR33(&audio->psg, value); GBAudioWriteNR34(&audio->psg, value >> 8); } void GBAAudioWriteSOUND4CNT_LO(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR41(&audio->psg, value); GBAudioWriteNR42(&audio->psg, value >> 8); } void GBAAudioWriteSOUND4CNT_HI(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR43(&audio->psg, value); GBAudioWriteNR44(&audio->psg, value >> 8); } void GBAAudioWriteSOUNDCNT_LO(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR50(&audio->psg, value); GBAudioWriteNR51(&audio->psg, value >> 8); } @@ -328,17 +343,17 @@ static int _applyBias(struct GBAAudio* audio, int sample) { return ((sample - GBARegisterSOUNDBIASGetBias(audio->soundbias)) * audio->masterVolume * 3) >> 4; } -static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { - struct GBAAudio* audio = user; - int16_t samplesLeft[GBA_MAX_SAMPLES]; - int16_t samplesRight[GBA_MAX_SAMPLES]; - int32_t timestamp = mTimingCurrentTime(&audio->p->timing) - cyclesLate - SAMPLE_INTERVAL; +void GBAAudioSample(struct GBAAudio* audio, int32_t timestamp) { + timestamp -= audio->lastSample; + timestamp -= audio->sampleIndex * audio->sampleInterval; // TODO: This can break if the interval changes between samples + + int maxSample = 2 << GBARegisterSOUNDBIASGetResolution(audio->soundbias); int sample; - for (sample = 0; sample * audio->sampleInterval < (int32_t) SAMPLE_INTERVAL; ++sample) { + for (sample = audio->sampleIndex; timestamp >= audio->sampleInterval && sample < maxSample; ++sample, timestamp -= audio->sampleInterval) { int16_t sampleLeft = 0; int16_t sampleRight = 0; int psgShift = 4 - audio->volume; - GBAudioRun(&audio->psg, timestamp + (sample + 1) * audio->sampleInterval, 0xF); + GBAudioRun(&audio->psg, sample * audio->sampleInterval + audio->lastSample, 0xF); GBAudioSamplePSG(&audio->psg, &sampleLeft, &sampleRight); sampleLeft >>= psgShift; sampleRight >>= psgShift; @@ -370,21 +385,34 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { sampleLeft = _applyBias(audio, sampleLeft); sampleRight = _applyBias(audio, sampleRight); - samplesLeft[sample] = sampleLeft; - samplesRight[sample] = sampleRight; + audio->currentSamples[sample].left = sampleLeft; + audio->currentSamples[sample].right = sampleRight; } - memset(audio->chA.samples, audio->chA.samples[sample - 1], sizeof(audio->chA.samples)); - memset(audio->chB.samples, audio->chB.samples[sample - 1], sizeof(audio->chB.samples)); + audio->sampleIndex = sample; + if (sample == maxSample) { + audio->lastSample += SAMPLE_INTERVAL; + audio->sampleIndex = 0; + } +} + +static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { + struct GBAAudio* audio = user; + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing) - cyclesLate); + + int samples = 2 << GBARegisterSOUNDBIASGetResolution(audio->soundbias); + int sampleMask = 1 << GBARegisterSOUNDBIASGetResolution(audio->soundbias); + memset(audio->chA.samples, audio->chA.samples[samples - 1], sizeof(audio->chA.samples)); + memset(audio->chB.samples, audio->chB.samples[samples - 1], sizeof(audio->chB.samples)); mCoreSyncLockAudio(audio->p->sync); unsigned produced; int32_t sampleSumLeft = 0; int32_t sampleSumRight = 0; int i; - for (i = 0; i < sample; ++i) { - int16_t sampleLeft = samplesLeft[i]; - int16_t sampleRight = samplesRight[i]; + for (i = 0; i < samples; ++i) { + int16_t sampleLeft = audio->currentSamples[i].left; + int16_t sampleRight = audio->currentSamples[i].right; sampleSumLeft += sampleLeft; sampleSumRight += sampleRight; if ((size_t) blip_samples_avail(audio->psg.left) < audio->samples) { @@ -399,12 +427,14 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { audio->clock -= CLOCKS_PER_FRAME; } } - } - // TODO: Post all frames - if (audio->p->stream && audio->p->stream->postAudioFrame) { - sampleSumLeft /= sample; - sampleSumRight /= sample; - audio->p->stream->postAudioFrame(audio->p->stream, sampleSumLeft, sampleSumRight); + // TODO: Post all frames + if (audio->p->stream && audio->p->stream->postAudioFrame && (i & (sampleMask - 1)) == sampleMask - 1) { + sampleSumLeft /= sampleMask; + sampleSumRight /= sampleMask; + audio->p->stream->postAudioFrame(audio->p->stream, sampleSumLeft, sampleSumRight); + sampleSumLeft = 0; + sampleSumRight = 0; + } } produced = blip_samples_avail(audio->psg.left); bool wait = produced >= audio->samples; @@ -428,9 +458,15 @@ void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* memcpy(state->samples.chA, audio->chA.samples, sizeof(audio->chA.samples)); memcpy(state->samples.chB, audio->chB.samples, sizeof(audio->chB.samples)); + size_t i; + for (i = 0; i < GBA_MAX_SAMPLES; ++i) { + STORE_16(audio->currentSamples[i].left, 0, &state->currentSamples[i].left); + STORE_16(audio->currentSamples[i].right, 0, &state->currentSamples[i].right); + } + STORE_32(audio->lastSample, 0, &state->audio.lastSample); + int readA = audio->chA.fifoRead; int readB = audio->chB.fifoRead; - size_t i; for (i = 0; i < GBA_AUDIO_FIFO_SIZE; ++i) { STORE_32(audio->chA.fifo[readA], i << 2, state->audio.fifoA); STORE_32(audio->chB.fifo[readB], i << 2, state->audio.fifoB); @@ -464,6 +500,11 @@ void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* flags = GBASerializedAudioFlagsSetFIFOInternalSamplesA(flags, audio->chA.internalRemaining); flags = GBASerializedAudioFlagsSetFIFOInternalSamplesB(flags, audio->chB.internalRemaining); STORE_16(flags, 0, &state->audio.gbaFlags); + + GBASerializedAudioFlags2 flags2 = 0; + flags2 = GBASerializedAudioFlags2SetSampleIndex(flags2, audio->sampleIndex); + STORE_32(flags2, 0, &state->audio.gbaFlags2); + STORE_32(audio->sampleEvent.when - mTimingCurrentTime(&audio->p->timing), 0, &state->audio.nextSample); } @@ -475,9 +516,15 @@ void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState memcpy(audio->chA.samples, state->samples.chA, sizeof(audio->chA.samples)); memcpy(audio->chB.samples, state->samples.chB, sizeof(audio->chB.samples)); + size_t i; + for (i = 0; i < GBA_MAX_SAMPLES; ++i) { + LOAD_16(audio->currentSamples[i].left, 0, &state->currentSamples[i].left); + LOAD_16(audio->currentSamples[i].right, 0, &state->currentSamples[i].right); + } + LOAD_32(audio->lastSample, 0, &state->audio.lastSample); + int readA = 0; int readB = 0; - size_t i; for (i = 0; i < GBA_AUDIO_FIFO_SIZE; ++i) { LOAD_32(audio->chA.fifo[readA], i << 2, state->audio.fifoA); LOAD_32(audio->chB.fifo[readB], i << 2, state->audio.fifoB); @@ -494,8 +541,15 @@ void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState audio->chA.internalRemaining = GBASerializedAudioFlagsGetFIFOInternalSamplesA(flags); audio->chB.internalRemaining = GBASerializedAudioFlagsGetFIFOInternalSamplesB(flags); + GBASerializedAudioFlags2 flags2; + LOAD_32(flags2, 0, &state->audio.gbaFlags2); + audio->sampleIndex = GBASerializedAudioFlags2GetSampleIndex(flags2); + uint32_t when; LOAD_32(when, 0, &state->audio.nextSample); + if (state->versionMagic < 0x01000007) { + audio->lastSample = when - SAMPLE_INTERVAL; + } mTimingSchedule(&audio->p->timing, &audio->sampleEvent, when); } diff --git a/src/gba/serialize.c b/src/gba/serialize.c index 9c5416e72..c64d7c22b 100644 --- a/src/gba/serialize.c +++ b/src/gba/serialize.c @@ -15,7 +15,7 @@ #include MGBA_EXPORT const uint32_t GBASavestateMagic = 0x01000000; -MGBA_EXPORT const uint32_t GBASavestateVersion = 0x00000006; +MGBA_EXPORT const uint32_t GBASavestateVersion = 0x00000007; mLOG_DEFINE_CATEGORY(GBA_STATE, "GBA Savestate", "gba.serialize"); diff --git a/src/gba/video.c b/src/gba/video.c index 659313575..7268f19de 100644 --- a/src/gba/video.c +++ b/src/gba/video.c @@ -377,7 +377,12 @@ void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState break; } uint32_t when; - LOAD_32(when, 0, &state->video.nextEvent); + if (state->versionMagic < 0x01000007) { + // This field was moved in v7 + LOAD_32(when, 0, &state->audio.lastSample); + } else { + LOAD_32(when, 0, &state->video.nextEvent); + } mTimingSchedule(&video->p->timing, &video->event, when); LOAD_16(video->vcount, REG_VCOUNT, state->io); From fa910fc629751da9c990c8d5fb34405a0e459068 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 15 Jun 2022 23:31:20 -0700 Subject: [PATCH 22/24] Mac: Start bringing up Universal build support --- src/platform/qt/CMakeLists.txt | 5 ++++- src/platform/sdl/CMakeLists.txt | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 9ad0d27f1..57fda3249 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -401,7 +401,10 @@ if(QT_STATIC) find_package(Cups) find_package(${QT}PrintSupport) list(APPEND QT_LIBRARIES Cups ${QT}::PrintSupport ${QT}::QCocoaIntegrationPlugin ${QT}::CoreAudioPlugin ${QT}::AVFServicePlugin ${QT}::QCocoaPrinterSupportPlugin) - list(APPEND QT_LIBRARIES ${QT}AccessibilitySupport ${QT}CglSupport ${QT}ClipboardSupport ${QT}FontDatabaseSupport ${QT}GraphicsSupport ${QT}ThemeSupport) + list(APPEND QT_LIBRARIES ${QT}AccessibilitySupport ${QT}ClipboardSupport ${QT}FontDatabaseSupport ${QT}GraphicsSupport ${QT}ThemeSupport) + if(CMAKE_SYSTEM_VERSION VERSION_LESS "19.0") + list(APPEND QT_LIBRARIES ${QT}CglSupport) + endif() list(APPEND QT_LIBRARIES "-framework AVFoundation" "-framework CoreMedia" "-framework SystemConfiguration" "-framework Security") set_target_properties(${QT}::Core PROPERTIES INTERFACE_LINK_LIBRARIES "${QTPCRE}") elseif(UNIX) diff --git a/src/platform/sdl/CMakeLists.txt b/src/platform/sdl/CMakeLists.txt index 47f05cc4d..cacf0bc48 100644 --- a/src/platform/sdl/CMakeLists.txt +++ b/src/platform/sdl/CMakeLists.txt @@ -56,6 +56,9 @@ elseif(APPLE) if(NOT CMAKE_SYSTEM_VERSION VERSION_LESS "17.0") # Darwin 17.x is macOS 10.13 list(APPEND SDL_LIBRARY "-framework Metal") endif() + if(NOT CMAKE_SYSTEM_VERSION VERSION_LESS "19.0") # Darwin 19.x is macOS 10.15 + list(APPEND SDL_LIBRARY "-framework GameController" "-framework CoreHaptics") + endif() endif() if(NOT SDLMAIN_LIBRARY) From 07221181ea8c47989efcc6eb3dfafff2b9ffb9c2 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 16 Jun 2022 15:38:38 -0700 Subject: [PATCH 23/24] Qt: Fix VAO reinitialization --- src/platform/qt/DisplayGL.cpp | 7 +++++++ src/platform/qt/DisplayGL.h | 1 + 2 files changed, 8 insertions(+) diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 4691726bc..27183899d 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -82,6 +82,8 @@ void mGLWidget::initializeGL() { m_program->setUniformValue("tex", 0); m_positionLocation = m_program->attributeLocation("position"); + m_vaoDone = false; + connect(&m_refresh, &QTimer::timeout, this, static_cast(&QWidget::update)); } @@ -98,6 +100,10 @@ void mGLWidget::finalizeVAO() { } } +void mGLWidget::reset() { + m_vaoDone = false; +} + void mGLWidget::paintGL() { if (!m_vaoDone) { finalizeVAO(); @@ -210,6 +216,7 @@ void DisplayGL::startDrawing(std::shared_ptr controller) { } } else { show(); + m_gl->reset(); } } diff --git a/src/platform/qt/DisplayGL.h b/src/platform/qt/DisplayGL.h index a75f1a8dd..007cf6165 100644 --- a/src/platform/qt/DisplayGL.h +++ b/src/platform/qt/DisplayGL.h @@ -53,6 +53,7 @@ public: void setTex(GLuint tex) { m_tex = tex; } void setVBO(GLuint vbo) { m_vbo = vbo; } void finalizeVAO(); + void reset(); protected: void initializeGL() override; From ddccbee47b393bf2a99a6307f9c22aecfc04c9f4 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 16 Jun 2022 15:41:28 -0700 Subject: [PATCH 24/24] Qt: Show correct channel for modern macOS builds --- src/platform/qt/ApplicationUpdater.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/platform/qt/ApplicationUpdater.cpp b/src/platform/qt/ApplicationUpdater.cpp index c59413c26..ce752ecce 100644 --- a/src/platform/qt/ApplicationUpdater.cpp +++ b/src/platform/qt/ApplicationUpdater.cpp @@ -150,7 +150,13 @@ const char* ApplicationUpdater::platform() { return uninstallInfo.exists() ? "win32-installer" : "win32"; #endif #elif defined(Q_OS_MACOS) +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + // Modern macOS build + return "macos"; +#else + // Legacy "OS X" build return "osx"; +#endif #elif defined(Q_OS_LINUX) && defined(__x86_64__) return "appimage-x64"; #else