Merge branch 'master' into medusa

This commit is contained in:
Vicki Pfau 2017-04-22 02:14:56 -07:00
commit 154dd2e8e7
34 changed files with 244 additions and 82 deletions

View File

@ -49,6 +49,8 @@ Features:
- Savestates now contain any RTC override data
- Command line ability to override configuration values
- Add option to allow preloading the entire ROM before running
- GB: Video/audio channel enabling/disabling
- Add option to lock video to integer scaling
Bugfixes:
- LR35902: Fix core never exiting with certain event patterns
- GB Timer: Improve DIV reset behavior
@ -73,6 +75,11 @@ Bugfixes:
- GB: Fix flickering when screen is strobed quickly
- FFmpeg: Fix overflow and general issues with audio encoding
- Qt: Fix crash when changing audio settings after a game is closed
- GBA BIOS: Fix ArcTan sign in HLE BIOS
- GBA BIOS: Fix ArcTan2 sign in HLE BIOS (fixes mgba.io/i/689)
- GBA Video: Don't update background scanline params in mode 0 (fixes mgba.io/i/377)
- Qt: Ensure CLI backend is attached when submitting commands (fixes mgba.io/i/662)
- Core: Fix crash with rewind if savestates shrink
Misc:
- SDL: Remove scancode key input
- GBA Video: Clean up unused timers
@ -126,6 +133,7 @@ Misc:
- Qt: Add .gb/.gbc files to the extension list in Info.plist
- Feature: Make -l option explicit
- Core: Ability to enumerate and modify video and audio channels
- Debugger: Make attaching a backend idempotent
medusa alpha 1: (2017-04-08)
Features:

View File

@ -56,6 +56,11 @@ typedef intptr_t ssize_t;
#include <sys/time.h>
#endif
#ifdef PSP2
// For PATH_MAX on modern toolchains
#include <sys/syslimits.h>
#endif
#ifndef SSIZE_MAX
#define SSIZE_MAX ((ssize_t) (SIZE_MAX >> 1))
#endif

View File

@ -42,6 +42,7 @@ struct mCoreOptions {
int width;
int height;
bool lockAspectRatio;
bool lockIntegerScaling;
bool resampleVideo;
bool suspendScreensaver;
char* shader;

View File

@ -71,6 +71,10 @@ struct GBVideoRenderer {
uint8_t* vram;
union GBOAM* oam;
struct mTileCache* cache;
bool disableBG;
bool disableOBJ;
bool disableWIN;
};
DECL_BITFIELD(GBRegisterLCDC, uint8_t);

View File

@ -349,6 +349,9 @@ void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts)
if (_lookupIntValue(config, "lockAspectRatio", &fakeBool)) {
opts->lockAspectRatio = fakeBool;
}
if (_lookupIntValue(config, "lockIntegerScaling", &fakeBool)) {
opts->lockIntegerScaling = fakeBool;
}
if (_lookupIntValue(config, "resampleVideo", &fakeBool)) {
opts->resampleVideo = fakeBool;
}
@ -396,6 +399,7 @@ void mCoreConfigLoadDefaults(struct mCoreConfig* config, const struct mCoreOptio
ConfigurationSetIntValue(&config->defaultsTable, 0, "volume", opts->volume);
ConfigurationSetIntValue(&config->defaultsTable, 0, "mute", opts->mute);
ConfigurationSetIntValue(&config->defaultsTable, 0, "lockAspectRatio", opts->lockAspectRatio);
ConfigurationSetIntValue(&config->defaultsTable, 0, "lockIntegerScaling", opts->lockIntegerScaling);
ConfigurationSetIntValue(&config->defaultsTable, 0, "resampleVideo", opts->resampleVideo);
ConfigurationSetIntValue(&config->defaultsTable, 0, "suspendScreensaver", opts->suspendScreensaver);
}

View File

@ -50,6 +50,8 @@ void mCoreRewindAppend(struct mCoreRewindContext* context, struct mCore* core) {
if (size2 > size) {
context->currentState->truncate(context->currentState, size2);
size = size2;
} else if (size > size2) {
nextState->truncate(nextState, size);
}
void* current = context->currentState->map(context->currentState, size, MAP_READ);
void* next = nextState->map(nextState, size, MAP_READ);

View File

@ -751,6 +751,9 @@ void CLIDebuggerAttachSystem(struct CLIDebugger* debugger, struct CLIDebuggerSys
}
void CLIDebuggerAttachBackend(struct CLIDebugger* debugger, struct CLIDebuggerBackend* backend) {
if (debugger->backend == backend) {
return;
}
if (debugger->backend && debugger->backend->deinit) {
debugger->backend->deinit(debugger->backend);
}

View File

@ -465,11 +465,13 @@ void DSAttachDebugger(struct DS* ds, struct mDebugger* debugger) {
}
void DSDetachDebugger(struct DS* ds) {
ds->debugger = NULL;
if (ds->debugger) {
ARMHotplugDetach(ds->ds7.cpu, CPU_COMPONENT_DEBUGGER);
ARMHotplugDetach(ds->ds9.cpu, CPU_COMPONENT_DEBUGGER);
}
ds->ds7.cpu->components[CPU_COMPONENT_DEBUGGER] = NULL;
ds->ds9.cpu->components[CPU_COMPONENT_DEBUGGER] = NULL;
ds->debugger = NULL;
}
bool DSLoadROM(struct DS* ds, struct VFile* vf) {

View File

@ -640,8 +640,8 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
audio->lastRight = sampleRight;
audio->clock += audio->sampleInterval;
if (audio->clock >= CLOCKS_PER_BLIP_FRAME) {
blip_end_frame(audio->left, audio->clock);
blip_end_frame(audio->right, audio->clock);
blip_end_frame(audio->left, CLOCKS_PER_BLIP_FRAME);
blip_end_frame(audio->right, CLOCKS_PER_BLIP_FRAME);
audio->clock -= CLOCKS_PER_BLIP_FRAME;
}
}

View File

@ -629,7 +629,19 @@ static size_t _GBCoreListAudioChannels(const struct mCore* core, const struct mC
static void _GBCoreEnableVideoLayer(struct mCore* core, size_t id, bool enable) {
struct GB* gb = core->board;
// TODO
switch (id) {
case 0:
gb->video.renderer->disableBG = !enable;
break;
case 1:
gb->video.renderer->disableOBJ = !enable;
break;
case 2:
gb->video.renderer->disableWIN = !enable;
break;
default:
break;
}
}
static void _GBCoreEnableAudioChannel(struct mCore* core, size_t id, bool enable) {

View File

@ -49,6 +49,10 @@ void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer* renderer) {
renderer->d.getPixels = GBVideoSoftwareRendererGetPixels;
renderer->d.putPixels = GBVideoSoftwareRendererPutPixels;
renderer->d.disableBG = false;
renderer->d.disableOBJ = false;
renderer->d.disableWIN = false;
renderer->temporaryBuffer = 0;
}
@ -128,9 +132,12 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i
if (GBRegisterLCDCIsTileMap(softwareRenderer->lcdc)) {
maps += GB_SIZE_MAP;
}
if (softwareRenderer->d.disableBG) {
memset(&softwareRenderer->row[startX], 0, endX - startX);
}
if (GBRegisterLCDCIsBgEnable(softwareRenderer->lcdc) || softwareRenderer->model >= GB_MODEL_CGB) {
if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->wy <= y && endX >= softwareRenderer->wx - 7) {
if (softwareRenderer->wx - 7 > 0) {
if (softwareRenderer->wx - 7 > 0 && !softwareRenderer->d.disableBG) {
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, softwareRenderer->wx - 7, softwareRenderer->scx, softwareRenderer->scy + y);
}
@ -138,15 +145,17 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i
if (GBRegisterLCDCIsWindowTileMap(softwareRenderer->lcdc)) {
maps += GB_SIZE_MAP;
}
if (!softwareRenderer->d.disableWIN) {
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, softwareRenderer->wx - 7, endX, 7 - softwareRenderer->wx, softwareRenderer->currentWy);
} else {
}
} else if (!softwareRenderer->d.disableBG) {
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, endX, softwareRenderer->scx, softwareRenderer->scy + y);
}
} else {
} else if (!softwareRenderer->d.disableBG) {
memset(&softwareRenderer->row[startX], 0, endX - startX);
}
if (GBRegisterLCDCIsObjEnable(softwareRenderer->lcdc)) {
if (GBRegisterLCDCIsObjEnable(softwareRenderer->lcdc) && !softwareRenderer->d.disableOBJ) {
size_t i;
for (i = 0; i < oamMax; ++i) {
GBVideoSoftwareRendererDrawObj(softwareRenderer, &obj[i], startX, endX, y);

View File

@ -297,8 +297,8 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
audio->lastRight = sampleRight;
audio->clock += audio->sampleInterval;
if (audio->clock >= CLOCKS_PER_FRAME) {
blip_end_frame(audio->psg.left, audio->clock);
blip_end_frame(audio->psg.right, audio->clock);
blip_end_frame(audio->psg.left, CLOCKS_PER_FRAME);
blip_end_frame(audio->psg.right, CLOCKS_PER_FRAME);
audio->clock -= CLOCKS_PER_FRAME;
}
}

View File

@ -271,7 +271,7 @@ static void _Div(struct GBA* gba, int32_t num, int32_t denom) {
}
}
static int16_t _ArcTan(int16_t i) {
static int16_t _ArcTan(int32_t i, int32_t* r1, int32_t* r3) {
int32_t a = -((i * i) >> 14);
int32_t b = ((0xA9 * a) >> 14) + 0x390;
b = ((b * a) >> 14) + 0x91C;
@ -280,10 +280,16 @@ static int16_t _ArcTan(int16_t i) {
b = ((b * a) >> 14) + 0x2081;
b = ((b * a) >> 14) + 0x3651;
b = ((b * a) >> 14) + 0xA2F9;
if (r1) {
*r1 = a;
}
if (r3) {
*r3 = b;
}
return (i * b) >> 16;
}
static int16_t _ArcTan2(int16_t x, int16_t y) {
static int16_t _ArcTan2(int32_t x, int32_t y, int32_t* r1) {
if (!y) {
if (x >= 0) {
return 0;
@ -299,21 +305,21 @@ static int16_t _ArcTan2(int16_t x, int16_t y) {
if (y >= 0) {
if (x >= 0) {
if (x >= y) {
return _ArcTan((y << 14)/ x);
return _ArcTan((y << 14) / x, r1, NULL);
}
} else if (-x >= y) {
return _ArcTan((y << 14) / x) + 0x8000;
return _ArcTan((y << 14) / x, r1, NULL) + 0x8000;
}
return 0x4000 - _ArcTan((x << 14) / y);
return 0x4000 - _ArcTan((x << 14) / y, r1, NULL);
} else {
if (x <= 0) {
if (-x > -y) {
return _ArcTan((y << 14) / x) + 0x8000;
return _ArcTan((y << 14) / x, r1, NULL) + 0x8000;
}
} else if (x >= -y) {
return _ArcTan((y << 14) / x) + 0x10000;
return _ArcTan((y << 14) / x, r1, NULL) + 0x10000;
}
return 0xC000 - _ArcTan((x << 14) / y);
return 0xC000 - _ArcTan((x << 14) / y, r1, NULL);
}
}
@ -356,10 +362,11 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
cpu->gprs[0] = sqrt((uint32_t) cpu->gprs[0]);
break;
case 0x9:
cpu->gprs[0] = (uint16_t) _ArcTan(cpu->gprs[0]);
cpu->gprs[0] = _ArcTan(cpu->gprs[0], &cpu->gprs[1], &cpu->gprs[3]);
break;
case 0xA:
cpu->gprs[0] = (uint16_t) _ArcTan2(cpu->gprs[0], cpu->gprs[1]);
cpu->gprs[0] = (uint16_t) _ArcTan2(cpu->gprs[0], cpu->gprs[1], &cpu->gprs[1]);
cpu->gprs[3] = 0x170;
break;
case 0xB:
case 0xC:

View File

@ -268,6 +268,7 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
}
}
#ifdef USE_DEBUGGERS
void GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger) {
gba->debugger = (struct ARMDebugger*) debugger->platform;
gba->debugger->setSoftwareBreakpoint = _setSoftwareBreakpoint;
@ -277,10 +278,13 @@ void GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger) {
}
void GBADetachDebugger(struct GBA* gba) {
gba->debugger = 0;
if (gba->debugger) {
ARMHotplugDetach(gba->cpu, CPU_COMPONENT_DEBUGGER);
gba->cpu->components[CPU_COMPONENT_DEBUGGER] = 0;
}
gba->cpu->components[CPU_COMPONENT_DEBUGGER] = NULL;
gba->debugger = NULL;
}
#endif
bool GBALoadMB(struct GBA* gba, struct VFile* vf) {
GBAUnloadROM(gba);

View File

@ -581,10 +581,12 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render
}
}
}
if (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) != 0) {
softwareRenderer->bg[2].sx += softwareRenderer->bg[2].dmx;
softwareRenderer->bg[2].sy += softwareRenderer->bg[2].dmy;
softwareRenderer->bg[3].sx += softwareRenderer->bg[3].dmx;
softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy;
}
GBAVideoSoftwareRendererPostprocessBuffer(softwareRenderer);

View File

@ -67,6 +67,10 @@ static void mGLContextResized(struct VideoBackend* v, unsigned w, unsigned h) {
drawH = w * v->height / v->width;
}
}
if (v->lockIntegerScaling) {
drawW -= drawW % v->width;
drawH -= drawH % v->height;
}
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClearColor(0, 0, 0, 0);

View File

@ -171,6 +171,10 @@ static void mGLES2ContextResized(struct VideoBackend* v, unsigned w, unsigned h)
drawH = w * v->height / v->width;
}
}
if (v->lockIntegerScaling) {
drawW -= drawW % v->width;
drawH -= drawH % v->height;
}
glViewport(0, 0, v->width, v->height);
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);

View File

@ -16,7 +16,6 @@
#include <psp2/display.h>
#include <psp2/kernel/processmgr.h>
#include <psp2/kernel/threadmgr.h>
#include <psp2/moduleinfo.h>
#include <psp2/power.h>
#include <psp2/sysmodule.h>
#include <psp2/touch.h>
@ -164,6 +163,7 @@ int main() {
mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_RIGHT, GUI_INPUT_RIGHT);
mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_SQUARE, mGUI_INPUT_SCREEN_MODE);
scePowerSetArmClockFrequency(444);
mGUIRunloop(&runner);
vita2d_fini();

View File

@ -20,6 +20,7 @@
#include <mgba-util/memory.h>
#include <mgba-util/circle-buffer.h>
#include <mgba-util/math.h>
#include <mgba-util/ring-fifo.h>
#include <mgba-util/threading.h>
#include <mgba-util/vfs.h>
@ -31,7 +32,6 @@
#include <psp2/gxm.h>
#include <psp2/kernel/sysmem.h>
#include <psp2/motion.h>
#include <psp2/power.h>
#include <vita2d.h>
@ -63,8 +63,8 @@ bool frameLimiter = true;
extern const uint8_t _binary_backdrop_png_start[];
static vita2d_texture* backdrop = 0;
#define PSP2_SAMPLES 128
#define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * 40)
#define PSP2_SAMPLES 256
#define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * 20)
static struct mPSP2AudioContext {
struct RingFIFO buffer;
@ -176,7 +176,6 @@ void mPSP2Setup(struct mGUIRunner* runner) {
mCoreConfigSetDefaultIntValue(&runner->config, "threadedVideo", 1);
mCoreLoadForeignConfig(runner->core, &runner->config);
scePowerSetArmClockFrequency(333);
mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_CROSS, GBA_KEY_A);
mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_CIRCLE, GBA_KEY_B);
mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_START, GBA_KEY_START);
@ -193,8 +192,10 @@ void mPSP2Setup(struct mGUIRunner* runner) {
desc = (struct mInputAxis) { GBA_KEY_RIGHT, GBA_KEY_LEFT, 192, 64 };
mInputBindAxis(&runner->core->inputMap, PSP2_INPUT, 1, &desc);
tex = vita2d_create_empty_texture_format(256, 256, SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
screenshot = vita2d_create_empty_texture_format(256, 256, SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
unsigned width, height;
runner->core->desiredVideoDimensions(runner->core, &width, &height);
tex = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
screenshot = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
outputBuffer = vita2d_texture_get_datap(tex);
runner->core->setVideoBuffer(runner->core, outputBuffer, 256);
@ -219,7 +220,6 @@ void mPSP2Setup(struct mGUIRunner* runner) {
}
void mPSP2LoadROM(struct mGUIRunner* runner) {
scePowerSetArmClockFrequency(444);
float rate = 60.0f / 1.001f;
sceDisplayGetRefreshRate(&rate);
double ratio = GBAAudioCalculateRatio(1, rate, 1);
@ -262,7 +262,6 @@ void mPSP2PrepareForFrame(struct mGUIRunner* runner) {
blip_clear(runner->core->getAudioChannel(runner->core, 1));
break;
}
sceKernelDelayThread(400);
}
blip_read_samples(runner->core->getAudioChannel(runner->core, 0), &samples[0].left, PSP2_SAMPLES, true);
blip_read_samples(runner->core->getAudioChannel(runner->core, 1), &samples[0].right, PSP2_SAMPLES, true);
@ -297,7 +296,6 @@ void mPSP2UnloadROM(struct mGUIRunner* runner) {
default:
break;
}
scePowerSetArmClockFrequency(333);
}
void mPSP2Paused(struct mGUIRunner* runner) {

View File

@ -2,8 +2,13 @@ find_program(PYTHON python)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in ${CMAKE_CURRENT_BINARY_DIR}/setup.py)
get_property(INCLUDE_DIRECTORIES DIRECTORY PROPERTY INCLUDE_DIRECTORIES)
set(INCLUDE_FLAGS)
foreach(DIR IN LISTS INCLUDE_DIRECTORIES)
list(APPEND INCLUDE_FLAGS "-I${DIR}")
endforeach()
add_custom_command(OUTPUT build/lib/${BINARY_NAME}/__init__.py
COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. ${PYTHON} ${CMAKE_CURRENT_BINARY_DIR}/setup.py build --build-base ${CMAKE_CURRENT_BINARY_DIR}
COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. CPPFLAGS="${INCLUDE_FLAGS}" ${PYTHON} ${CMAKE_CURRENT_BINARY_DIR}/setup.py build --build-base ${CMAKE_CURRENT_BINARY_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${BINARY_NAME}
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/setup.py

View File

@ -38,8 +38,16 @@ void DebuggerConsoleController::enterLine(const QString& line) {
m_cond.wakeOne();
}
void DebuggerConsoleController::detach() {
m_lines.append(QString());
m_cond.wakeOne();
DebuggerController::detach();
}
void DebuggerConsoleController::attachInternal() {
m_history.clear();
mCore* core = m_gameController->thread()->core;
CLIDebuggerAttachBackend(&m_cliDebugger, &m_backend.d);
CLIDebuggerAttachSystem(&m_cliDebugger, core->cliDebuggerSystem(core));
}
@ -60,6 +68,8 @@ void DebuggerConsoleController::init(struct CLIDebuggerBackend* be) {
void DebuggerConsoleController::deinit(struct CLIDebuggerBackend* be) {
Backend* consoleBe = reinterpret_cast<Backend*>(be);
DebuggerConsoleController* self = consoleBe->self;
self->m_lines.append(QString());
self->m_cond.wakeOne();
}
const char* DebuggerConsoleController::readLine(struct CLIDebuggerBackend* be, size_t* len) {
@ -71,6 +81,9 @@ const char* DebuggerConsoleController::readLine(struct CLIDebuggerBackend* be, s
self->m_cond.wait(&self->m_mutex);
}
self->m_last = self->m_lines.takeFirst().toUtf8();
if (self->m_last.isEmpty()) {
self->m_last = "\n";
}
*len = self->m_last.size();
return self->m_last.constData();
@ -87,6 +100,9 @@ const char* DebuggerConsoleController::historyLast(struct CLIDebuggerBackend* be
DebuggerConsoleController* self = consoleBe->self;
GameController::Interrupter interrupter(self->m_gameController, true);
QMutexLocker lock(&self->m_mutex);
if (self->m_history.isEmpty()) {
return "\n";
}
self->m_last = self->m_history.last().toUtf8();
return self->m_last.constData();
}

View File

@ -30,6 +30,7 @@ signals:
public slots:
void enterLine(const QString&);
virtual void detach() override;
protected:
virtual void attachInternal() override;

View File

@ -54,6 +54,7 @@ Display* Display::create(QWidget* parent) {
Display::Display(QWidget* parent)
: QWidget(parent)
, m_lockAspectRatio(false)
, m_lockIntegerScaling(false)
, m_filter(false)
{
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
@ -78,6 +79,10 @@ QSize Display::viewportSize() {
ds.setHeight(s.width() * m_coreHeight / m_coreWidth);
}
}
if (isIntegerScalingLocked()) {
ds.setWidth(ds.width() - ds.width() % m_coreWidth);
ds.setHeight(ds.height() - ds.height() % m_coreHeight);
}
return ds;
}
@ -90,6 +95,10 @@ void Display::lockAspectRatio(bool lock) {
m_messagePainter.resize(size(), m_lockAspectRatio, devicePixelRatio());
}
void Display::lockIntegerScaling(bool lock) {
m_lockIntegerScaling = lock;
}
void Display::filter(bool filter) {
m_filter = filter;
}

View File

@ -38,6 +38,7 @@ public:
static void setDriver(Driver driver) { s_driver = driver; }
bool isAspectRatioLocked() const { return m_lockAspectRatio; }
bool isIntegerScalingLocked() const { return m_lockIntegerScaling; }
bool isFiltered() const { return m_filter; }
virtual bool isDrawing() const = 0;
@ -57,6 +58,7 @@ public slots:
virtual void unpauseDrawing() = 0;
virtual void forceDraw() = 0;
virtual void lockAspectRatio(bool lock);
virtual void lockIntegerScaling(bool lock);
virtual void filter(bool filter);
virtual void framePosted(const uint32_t*) = 0;
virtual void setShaders(struct VDir*) = 0;
@ -78,6 +80,7 @@ private:
MessagePainter m_messagePainter;
bool m_lockAspectRatio;
bool m_lockIntegerScaling;
bool m_filter;
QTimer m_mouseTimer;
int m_coreWidth;

View File

@ -73,6 +73,8 @@ void DisplayGL::startDrawing(mCoreThread* thread) {
mCoreSyncSetVideoSync(&m_context->sync, false);
lockAspectRatio(isAspectRatioLocked());
lockIntegerScaling(isIntegerScalingLocked());
unsigned width, height;
thread->core->desiredVideoDimensions(thread->core, &width, &height);
setSystemDimensions(width, height);
@ -140,6 +142,13 @@ void DisplayGL::lockAspectRatio(bool lock) {
}
}
void DisplayGL::lockIntegerScaling(bool lock) {
Display::lockIntegerScaling(lock);
if (m_drawThread) {
QMetaObject::invokeMethod(m_painter, "lockIntegerScaling", Q_ARG(bool, lock));
}
}
void DisplayGL::filter(bool filter) {
Display::filter(filter);
if (m_drawThread) {
@ -294,6 +303,13 @@ void PainterGL::lockAspectRatio(bool lock) {
}
}
void PainterGL::lockIntegerScaling(bool lock) {
m_backend->lockIntegerScaling = lock;
if (m_started && !m_active) {
forceDraw();
}
}
void PainterGL::filter(bool filter) {
m_backend->filter = filter;
if (m_started && !m_active) {

View File

@ -55,6 +55,7 @@ public slots:
void unpauseDrawing() override;
void forceDraw() override;
void lockAspectRatio(bool lock) override;
void lockIntegerScaling(bool lock) override;
void filter(bool filter) override;
void framePosted(const uint32_t*) override;
void setShaders(struct VDir*) override;
@ -96,6 +97,7 @@ public slots:
void unpause();
void resize(const QSize& size);
void lockAspectRatio(bool lock);
void lockIntegerScaling(bool lock);
void filter(bool filter);
void setShaders(struct VDir*);

View File

@ -31,6 +31,11 @@ void DisplayQt::lockAspectRatio(bool lock) {
update();
}
void DisplayQt::lockIntegerScaling(bool lock) {
Display::lockIntegerScaling(lock);
update();
}
void DisplayQt::filter(bool filter) {
Display::filter(filter);
update();

View File

@ -30,6 +30,7 @@ public slots:
void unpauseDrawing() override { m_isDrawing = true; }
void forceDraw() override { update(); }
void lockAspectRatio(bool lock) override;
void lockIntegerScaling(bool lock) override;
void filter(bool filter) override;
void framePosted(const uint32_t*) override;
void setShaders(struct VDir*) override {}

View File

@ -623,6 +623,7 @@ void GameController::closeGame() {
return;
}
setDebugger(nullptr);
if (mCoreThreadIsPaused(&m_threadContext)) {
mCoreThreadUnpause(&m_threadContext);
}

View File

@ -177,6 +177,7 @@ void SettingsView::updateConfig() {
saveSetting("frameskip", m_ui.frameskip);
saveSetting("fpsTarget", m_ui.fpsTarget);
saveSetting("lockAspectRatio", m_ui.lockAspectRatio);
saveSetting("lockIntegerScaling", m_ui.lockIntegerScaling);
saveSetting("volume", m_ui.volume);
saveSetting("mute", m_ui.mute);
saveSetting("rewindEnable", m_ui.rewind);
@ -260,6 +261,7 @@ void SettingsView::reloadConfig() {
loadSetting("frameskip", m_ui.frameskip);
loadSetting("fpsTarget", m_ui.fpsTarget);
loadSetting("lockAspectRatio", m_ui.lockAspectRatio);
loadSetting("lockIntegerScaling", m_ui.lockIntegerScaling);
loadSetting("volume", m_ui.volume);
loadSetting("mute", m_ui.mute);
loadSetting("rewindEnable", m_ui.rewind);

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>650</width>
<height>450</height>
<height>454</height>
</rect>
</property>
<property name="sizePolicy">
@ -115,7 +115,7 @@
<property name="editable">
<bool>true</bool>
</property>
<property name="currentText" stdset="0">
<property name="currentText">
<string>1536</string>
</property>
<property name="currentIndex">
@ -181,7 +181,7 @@
<property name="editable">
<bool>true</bool>
</property>
<property name="currentText" stdset="0">
<property name="currentText">
<string>44100</string>
</property>
<property name="currentIndex">
@ -380,13 +380,20 @@
</property>
</widget>
</item>
<item row="11" column="1">
<item row="12" column="1">
<widget class="QCheckBox" name="resampleVideo">
<property name="text">
<string>Bilinear filtering</string>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QCheckBox" name="lockIntegerScaling">
<property name="text">
<string>Force integer scaling</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="interface_2">

View File

@ -493,6 +493,7 @@ void Window::selectPatch() {
void Window::openView(QWidget* widget) {
connect(this, SIGNAL(shutdown()), widget, SLOT(close()));
connect(m_controller, SIGNAL(gameStopped(mCoreThread*)), widget, SLOT(close()));
widget->setAttribute(Qt::WA_DeleteOnClose);
widget->show();
}
@ -811,6 +812,39 @@ void Window::gameStarted(mCoreThread* context, const QString& fname) {
m_hitUnimplementedBiosCall = false;
m_fpsTimer.start();
m_focusCheck.start();
m_controller->threadInterrupt();
if (m_controller->isLoaded()) {
mCore* core = m_controller->thread()->core;
const mCoreChannelInfo* videoLayers;
const mCoreChannelInfo* audioChannels;
size_t nVideo = core->listVideoLayers(core, &videoLayers);
size_t nAudio = core->listAudioChannels(core, &audioChannels);
if (nVideo) {
for (size_t i = 0; i < nVideo; ++i) {
QAction* action = new QAction(videoLayers[i].visibleName, m_videoLayers);
action->setCheckable(true);
action->setChecked(true);
connect(action, &QAction::triggered, [this, videoLayers, i](bool enable) {
m_controller->setVideoLayerEnabled(videoLayers[i].id, enable);
});
m_videoLayers->addAction(action);
}
}
if (nAudio) {
for (size_t i = 0; i < nAudio; ++i) {
QAction* action = new QAction(audioChannels[i].visibleName, m_audioChannels);
action->setCheckable(true);
action->setChecked(true);
connect(action, &QAction::triggered, [this, audioChannels, i](bool enable) {
m_controller->setAudioChannelEnabled(audioChannels[i].id, enable);
});
m_audioChannels->addAction(action);
}
}
}
m_controller->threadContinue();
}
void Window::gameStopped() {
@ -834,6 +868,9 @@ void Window::gameStopped() {
m_screenWidget->setMinimumSize(m_display->minimumSize());
setMouseTracking(false);
m_videoLayers->clear();
m_audioChannels->clear();
m_fpsTimer.stop();
m_focusCheck.stop();
}
@ -1301,6 +1338,14 @@ void Window::setupMenu(QMenuBar* menubar) {
}, this);
m_config->updateOption("lockAspectRatio");
ConfigOption* lockIntegerScaling = m_config->addOption("lockIntegerScaling");
lockIntegerScaling->addBoolean(tr("Force integer scaling"), avMenu);
lockIntegerScaling->connect([this](const QVariant& value) {
m_display->lockIntegerScaling(value.toBool());
m_screenWidget->setLockIntegerScaling(value.toBool());
}, this);
m_config->updateOption("lockIntegerScaling");
ConfigOption* resampleVideo = m_config->addOption("resampleVideo");
resampleVideo->addBoolean(tr("Bilinear filtering"), avMenu);
resampleVideo->connect([this](const QVariant& value) {
@ -1376,45 +1421,12 @@ void Window::setupMenu(QMenuBar* menubar) {
#endif
avMenu->addSeparator();
QMenu* videoLayers = avMenu->addMenu(tr("Video layers"));
m_inputModel->addMenu(videoLayers, avMenu);
for (int i = 0; i < 4; ++i) {
QAction* enableBg = new QAction(tr("Background %0").arg(i), videoLayers);
enableBg->setCheckable(true);
enableBg->setChecked(true);
connect(enableBg, &QAction::triggered, [this, i](bool enable) { m_controller->setVideoLayerEnabled(i, enable); });
addControlledAction(videoLayers, enableBg, QString("enableBG%0").arg(i));
}
m_videoLayers = avMenu->addMenu(tr("Video layers"));
m_inputModel->addMenu(m_videoLayers, avMenu);
QAction* enableObj = new QAction(tr("OBJ (sprites)"), videoLayers);
enableObj->setCheckable(true);
enableObj->setChecked(true);
connect(enableObj, &QAction::triggered, [this](bool enable) { m_controller->setVideoLayerEnabled(4, enable); });
addControlledAction(videoLayers, enableObj, "enableOBJ");
QMenu* audioChannels = avMenu->addMenu(tr("Audio channels"));
m_inputModel->addMenu(audioChannels, avMenu);
for (int i = 0; i < 4; ++i) {
QAction* enableCh = new QAction(tr("Channel %0").arg(i + 1), audioChannels);
enableCh->setCheckable(true);
enableCh->setChecked(true);
connect(enableCh, &QAction::triggered, [this, i](bool enable) { m_controller->setAudioChannelEnabled(i, enable); });
addControlledAction(audioChannels, enableCh, QString("enableCh%0").arg(i + 1));
}
QAction* enableChA = new QAction(tr("Channel A"), audioChannels);
enableChA->setCheckable(true);
enableChA->setChecked(true);
connect(enableChA, &QAction::triggered, [this, i](bool enable) { m_controller->setAudioChannelEnabled(4, enable); });
addControlledAction(audioChannels, enableChA, QString("enableChA"));
QAction* enableChB = new QAction(tr("Channel B"), audioChannels);
enableChB->setCheckable(true);
enableChB->setChecked(true);
connect(enableChB, &QAction::triggered, [this, i](bool enable) { m_controller->setAudioChannelEnabled(5, enable); });
addControlledAction(audioChannels, enableChB, QString("enableChB"));
m_audioChannels = avMenu->addMenu(tr("Audio channels"));
m_inputModel->addMenu(m_audioChannels, avMenu);
QMenu* toolsMenu = menubar->addMenu(tr("&Tools"));
m_inputModel->addMenu(toolsMenu);
@ -1657,6 +1669,10 @@ void WindowBackground::setCenteredAspectRatio(int width, int height) {
m_aspectHeight = height;
}
void WindowBackground::setLockIntegerScaling(bool lock) {
m_lockIntegerScaling = lock;
}
void WindowBackground::paintEvent(QPaintEvent*) {
const QPixmap* logo = pixmap();
if (!logo) {
@ -1680,6 +1696,10 @@ void WindowBackground::paintEvent(QPaintEvent*) {
ds.setHeight(ds.width() * m_aspectHeight / m_aspectWidth);
}
}
if (m_lockIntegerScaling) {
ds.setWidth(ds.width() - ds.width() % m_aspectWidth);
ds.setHeight(ds.height() - ds.height() % m_aspectHeight);
}
QPoint origin = QPoint((s.width() - ds.width()) / 2, (s.height() - ds.height()) / 2);
QRect full(origin, ds);
painter.drawPixmap(full, *logo);

View File

@ -172,6 +172,8 @@ private:
QTimer m_fpsTimer;
QList<QString> m_mruFiles;
QMenu* m_mruMenu;
QMenu* m_videoLayers;
QMenu* m_audioChannels;
ShaderSelector* m_shaderView;
bool m_fullscreenOnStart;
QTimer m_focusCheck;
@ -207,6 +209,7 @@ public:
virtual QSize sizeHint() const override;
void setLockAspectRatio(int width, int height);
void setCenteredAspectRatio(int width, int height);
void setLockIntegerScaling(bool lock);
protected:
virtual void paintEvent(QPaintEvent*) override;
@ -216,6 +219,7 @@ private:
bool m_centered;
int m_aspectWidth;
int m_aspectHeight;
bool m_lockIntegerScaling;
};
}

View File

@ -35,6 +35,7 @@ struct VideoBackend {
bool filter;
bool lockAspectRatio;
bool lockIntegerScaling;
};
struct VideoShader {