mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' (early part) into medusa
This commit is contained in:
commit
1bef350858
22
CHANGES
22
CHANGES
|
@ -18,7 +18,22 @@ Misc:
|
|||
- DS Core: Add symbol loading
|
||||
- DS Video: Simplify VRAM mapping
|
||||
|
||||
0.8.0: (Future)
|
||||
0.9.0: (Future)
|
||||
Emulation fixes:
|
||||
- ARM: Fix ALU reading PC after shifting
|
||||
- ARM: Fix STR storing PC after address calculation
|
||||
- GBA DMA: Linger last DMA on bus (fixes mgba.io/i/301 and mgba.io/i/1320)
|
||||
- GBA Memory: Misaligned SRAM writes are ignored
|
||||
- GBA Serialize: Fix serializing DMA transfer register
|
||||
- GBA Serialize: Fix audio serialization for desynced FIFOs
|
||||
- GBA Serialize: Fix audio DMA timing deserialization
|
||||
- GBA Video: Fix OAM not invalidating after reset (fixes mgba.io/i/1630)
|
||||
Other fixes:
|
||||
- Qt: Only dynamically reset video scale if a game is running
|
||||
- Qt: Fix race condition with proxied video events
|
||||
- Qt: Force OpenGL paint engine creation thread (fixes mgba.io/i/1642)
|
||||
|
||||
0.8.0: (2020-01-21)
|
||||
Features:
|
||||
- Improved logging configuration
|
||||
- One-Player BattleChip/Progress/Beast Link Gate support
|
||||
|
@ -121,6 +136,8 @@ Other fixes:
|
|||
- 3DS: Fix screen darkening (fixes mgba.io/i/1562)
|
||||
- Core: Fix uninitialized memory issues with graphics caches
|
||||
- Core: Return null for out of bounds cached tile VRAM querying
|
||||
- GBA Video: Fix OpenGL fragment data binding
|
||||
- GBA Video: Fix mosaic in modes 1-5 in OpenGL (fixes mgba.io/i/1620)
|
||||
- Debugger: Fix tracing skipping instructions (fixes mgba.io/i/1614)
|
||||
- OpenGL: Only invalidate texture if dimensions change (fixes mgba.io/i/1612)
|
||||
- Qt: Fix fast forward mute being reset (fixes mgba.io/i/1574)
|
||||
|
@ -132,6 +149,8 @@ Other fixes:
|
|||
- Qt: Fix sprite view using wrong base address (fixes mgba.io/i/1603)
|
||||
- Qt: Fix inability to clear default keybindings
|
||||
- Qt: Release held actions if they get rebound
|
||||
- Qt: Fix crash double-clicking menus in shortcut settings (fixes mgba.io/i/1627)
|
||||
- Qt: Fix record A/V window not updating resolution (fixes mgba.io/i/1626)
|
||||
- Vita: Fix analog controls (fixes mgba.io/i/1554)
|
||||
- Wii: Fix game fast-forwarding after slowing down
|
||||
- Wii: Improve audio buffering (fixes mgba.io/i/1617)
|
||||
|
@ -140,6 +159,7 @@ Misc:
|
|||
- GBA: Trim non-movie ROMs to 32 MiB if applicable
|
||||
- GBA Audio: Redo channel 4 batching for GBA only
|
||||
- GBA I/O: Stop logging several harmless invalid register reads
|
||||
- GBA Video: Changing OpenGL scaling no longer requires restart
|
||||
- Debugger: Separate aliases from main commands
|
||||
- Debugger: Print break-/watchpoint ID when breaking in CLI
|
||||
- Debugger: Minor interface cleanup
|
||||
|
|
|
@ -109,6 +109,7 @@ struct GBA {
|
|||
bool haltPending;
|
||||
bool cpuBlocked;
|
||||
bool earlyExit;
|
||||
uint32_t dmaPC;
|
||||
int idleDetectionStep;
|
||||
int idleDetectionFailures;
|
||||
int32_t cachedRegisters[16];
|
||||
|
|
|
@ -196,6 +196,7 @@ struct GBAVideoGLRenderer {
|
|||
};
|
||||
|
||||
void GBAVideoGLRendererCreate(struct GBAVideoGLRenderer* renderer);
|
||||
void GBAVideoGLRendererSetScale(struct GBAVideoGLRenderer* renderer, int scale);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -66,9 +66,10 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
|
|||
* 0x0018C - 0x001AB: Audio FIFO 1
|
||||
* 0x001AC - 0x001CB: Audio FIFO 2
|
||||
* 0x001CC - 0x001DF: Audio miscellaneous state
|
||||
* | 0x001CC - 0x001D3: Reserved
|
||||
* | 0x001CC - 0x001CF: FIFO 1 size
|
||||
* | 0x001D0 - 0x001D3: Reserved
|
||||
* | 0x001D4 - 0x001D7: Next sample
|
||||
* | 0x001D8 - 0x001DB: FIFO size
|
||||
* | 0x001D8 - 0x001DB: FIFO 2 size
|
||||
* | TODO: Fix this, they're in big-endian order, but field is little-endian
|
||||
* | 0x001DC - 0x001DC: Channel 1 envelope state
|
||||
* | bits 0 - 3: Current volume
|
||||
|
@ -170,7 +171,8 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
|
|||
* | bits 9 - 23: Reserved
|
||||
* 0x002C4 - 0x002C7: Game Boy Player next event
|
||||
* 0x002C8 - 0x002CB: Current DMA transfer word
|
||||
* 0x002CC - 0x002DF: Reserved (leave zero)
|
||||
* 0x002CC - 0x002CF: Last DMA transfer PC
|
||||
* 0x002D0 - 0x002DF: Reserved (leave zero)
|
||||
* 0x002E0 - 0x002EF: Savedata state
|
||||
* | 0x002E0 - 0x002E0: Savedata type
|
||||
* | 0x002E1 - 0x002E1: Savedata command (see savedata.h)
|
||||
|
@ -256,9 +258,10 @@ struct GBASerializedState {
|
|||
struct GBSerializedPSGState psg;
|
||||
uint8_t fifoA[32];
|
||||
uint8_t fifoB[32];
|
||||
int32_t reserved[2];
|
||||
uint32_t fifoSizeA;
|
||||
int32_t reserved;
|
||||
int32_t nextSample;
|
||||
uint32_t fifoSize;
|
||||
uint32_t fifoSizeB;
|
||||
GBSerializedAudioFlags flags;
|
||||
} audio;
|
||||
|
||||
|
@ -300,8 +303,9 @@ struct GBASerializedState {
|
|||
} hw;
|
||||
|
||||
uint32_t dmaTransferRegister;
|
||||
uint32_t dmaBlockPC;
|
||||
|
||||
uint32_t reservedHardware[5];
|
||||
uint32_t reservedHardware[4];
|
||||
|
||||
struct {
|
||||
uint8_t type;
|
||||
|
|
|
@ -321,7 +321,10 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
|
|||
DEFINE_INSTRUCTION_ARM(NAME, \
|
||||
int rd = (opcode >> 12) & 0xF; \
|
||||
int rn = (opcode >> 16) & 0xF; \
|
||||
UNUSED(rn); \
|
||||
int32_t n = cpu->gprs[rn]; \
|
||||
if (UNLIKELY(rn == ARM_PC && (opcode & 0x02000010) == 0x00000010)) { \
|
||||
n += WORD_SIZE_ARM; \
|
||||
} \
|
||||
SHIFTER(cpu, opcode); \
|
||||
BODY; \
|
||||
S_BODY; \
|
||||
|
@ -436,6 +439,10 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
|
|||
uint32_t address; \
|
||||
int rn = (opcode >> 16) & 0xF; \
|
||||
int rd = (opcode >> 12) & 0xF; \
|
||||
int32_t d = cpu->gprs[rd]; \
|
||||
if (UNLIKELY(rd == ARM_PC)) { \
|
||||
d += WORD_SIZE_ARM; \
|
||||
} \
|
||||
int rm = opcode & 0xF; \
|
||||
UNUSED(rm); \
|
||||
address = ADDRESS; \
|
||||
|
@ -534,58 +541,52 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
|
|||
// Begin ALU definitions
|
||||
|
||||
DEFINE_ALU_INSTRUCTION_ARM(ADD, ARM_ADDITION_S(n, cpu->shifterOperand, cpu->gprs[rd]),
|
||||
int32_t n = cpu->gprs[rn];
|
||||
cpu->gprs[rd] = n + cpu->shifterOperand;)
|
||||
|
||||
DEFINE_ALU_INSTRUCTION_ARM(ADC, ARM_ADDITION_S(n, cpu->shifterOperand, cpu->gprs[rd]),
|
||||
int32_t n = cpu->gprs[rn];
|
||||
cpu->gprs[rd] = n + cpu->shifterOperand + cpu->cpsr.c;)
|
||||
|
||||
DEFINE_ALU_INSTRUCTION_ARM(AND, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]),
|
||||
cpu->gprs[rd] = cpu->gprs[rn] & cpu->shifterOperand;)
|
||||
DEFINE_ALU_INSTRUCTION_ARM(AND, ARM_NEUTRAL_S(n, cpu->shifterOperand, cpu->gprs[rd]),
|
||||
cpu->gprs[rd] = n & cpu->shifterOperand;)
|
||||
|
||||
DEFINE_ALU_INSTRUCTION_ARM(BIC, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]),
|
||||
cpu->gprs[rd] = cpu->gprs[rn] & ~cpu->shifterOperand;)
|
||||
DEFINE_ALU_INSTRUCTION_ARM(BIC, ARM_NEUTRAL_S(n, cpu->shifterOperand, cpu->gprs[rd]),
|
||||
cpu->gprs[rd] = n & ~cpu->shifterOperand;)
|
||||
|
||||
DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(CMN, ARM_ADDITION_S(cpu->gprs[rn], cpu->shifterOperand, aluOut),
|
||||
int32_t aluOut = cpu->gprs[rn] + cpu->shifterOperand;)
|
||||
DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(CMN, ARM_ADDITION_S(n, cpu->shifterOperand, aluOut),
|
||||
int32_t aluOut = n + cpu->shifterOperand;)
|
||||
|
||||
DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(CMP, ARM_SUBTRACTION_S(cpu->gprs[rn], cpu->shifterOperand, aluOut),
|
||||
int32_t aluOut = cpu->gprs[rn] - cpu->shifterOperand;)
|
||||
DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(CMP, ARM_SUBTRACTION_S(n, cpu->shifterOperand, aluOut),
|
||||
int32_t aluOut = n - cpu->shifterOperand;)
|
||||
|
||||
DEFINE_ALU_INSTRUCTION_ARM(EOR, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]),
|
||||
cpu->gprs[rd] = cpu->gprs[rn] ^ cpu->shifterOperand;)
|
||||
DEFINE_ALU_INSTRUCTION_ARM(EOR, ARM_NEUTRAL_S(n, cpu->shifterOperand, cpu->gprs[rd]),
|
||||
cpu->gprs[rd] = n ^ cpu->shifterOperand;)
|
||||
|
||||
DEFINE_ALU_INSTRUCTION_ARM(MOV, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]),
|
||||
DEFINE_ALU_INSTRUCTION_ARM(MOV, ARM_NEUTRAL_S(n, cpu->shifterOperand, cpu->gprs[rd]),
|
||||
cpu->gprs[rd] = cpu->shifterOperand;)
|
||||
|
||||
DEFINE_ALU_INSTRUCTION_ARM(MVN, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]),
|
||||
DEFINE_ALU_INSTRUCTION_ARM(MVN, ARM_NEUTRAL_S(n, cpu->shifterOperand, cpu->gprs[rd]),
|
||||
cpu->gprs[rd] = ~cpu->shifterOperand;)
|
||||
|
||||
DEFINE_ALU_INSTRUCTION_ARM(ORR, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]),
|
||||
cpu->gprs[rd] = cpu->gprs[rn] | cpu->shifterOperand;)
|
||||
DEFINE_ALU_INSTRUCTION_ARM(ORR, ARM_NEUTRAL_S(n, cpu->shifterOperand, cpu->gprs[rd]),
|
||||
cpu->gprs[rd] = n | cpu->shifterOperand;)
|
||||
|
||||
DEFINE_ALU_INSTRUCTION_ARM(RSB, ARM_SUBTRACTION_S(cpu->shifterOperand, n, cpu->gprs[rd]),
|
||||
int32_t n = cpu->gprs[rn];
|
||||
cpu->gprs[rd] = cpu->shifterOperand - n;)
|
||||
|
||||
DEFINE_ALU_INSTRUCTION_ARM(RSC, ARM_SUBTRACTION_CARRY_S(cpu->shifterOperand, n, cpu->gprs[rd], !cpu->cpsr.c),
|
||||
int32_t n = cpu->gprs[rn];
|
||||
cpu->gprs[rd] = cpu->shifterOperand - n - !cpu->cpsr.c;)
|
||||
|
||||
DEFINE_ALU_INSTRUCTION_ARM(SBC, ARM_SUBTRACTION_CARRY_S(n, cpu->shifterOperand, cpu->gprs[rd], !cpu->cpsr.c),
|
||||
int32_t n = cpu->gprs[rn];
|
||||
cpu->gprs[rd] = n - cpu->shifterOperand - !cpu->cpsr.c;)
|
||||
|
||||
DEFINE_ALU_INSTRUCTION_ARM(SUB, ARM_SUBTRACTION_S(n, cpu->shifterOperand, cpu->gprs[rd]),
|
||||
int32_t n = cpu->gprs[rn];
|
||||
cpu->gprs[rd] = n - cpu->shifterOperand;)
|
||||
|
||||
DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(TEQ, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, aluOut),
|
||||
int32_t aluOut = cpu->gprs[rn] ^ cpu->shifterOperand;)
|
||||
DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(TEQ, ARM_NEUTRAL_S(n, cpu->shifterOperand, aluOut),
|
||||
int32_t aluOut = n ^ cpu->shifterOperand;)
|
||||
|
||||
DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(TST, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, aluOut),
|
||||
int32_t aluOut = cpu->gprs[rn] & cpu->shifterOperand;)
|
||||
DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(TST, ARM_NEUTRAL_S(n, cpu->shifterOperand, aluOut),
|
||||
int32_t aluOut = n & cpu->shifterOperand;)
|
||||
|
||||
// End ALU definitions
|
||||
|
||||
|
@ -649,10 +650,10 @@ DEFINE_LOAD_STORE_MODE_3_DOUBLE_INSTRUCTION_ARM(LDRD, LOAD, cpu->gprs[rd & ~1] =
|
|||
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRH, LOAD, cpu->gprs[rd] = cpu->memory.load16(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSB, LOAD, cpu->gprs[rd] = ARM_SXT_8(cpu->memory.load8(cpu, address, ¤tCycles)); ARM_LOAD_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSH, LOAD, cpu->gprs[rd] = address & 1 ? ARM_SXT_8(cpu->memory.load16(cpu, address, ¤tCycles)) : ARM_SXT_16(cpu->memory.load16(cpu, address, ¤tCycles)); ARM_LOAD_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_INSTRUCTION_ARM(STR, STORE, cpu->memory.store32(cpu, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_INSTRUCTION_ARM(STRB, STORE, cpu->memory.store8(cpu, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_INSTRUCTION_ARM(STR, STORE, cpu->memory.store32(cpu, address, d, ¤tCycles); ARM_STORE_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_INSTRUCTION_ARM(STRB, STORE, cpu->memory.store8(cpu, address, d, ¤tCycles); ARM_STORE_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_MODE_3_DOUBLE_INSTRUCTION_ARM(STRD, STORE, cpu->memory.store32(cpu, address, cpu->gprs[rd & ~1], ¤tCycles); cpu->memory.store32(cpu, address + 4, cpu->gprs[rd | 1], ¤tCycles); ARM_STORE_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(STRH, STORE, cpu->memory.store16(cpu, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;)
|
||||
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(STRH, STORE, cpu->memory.store16(cpu, address, d, ¤tCycles); ARM_STORE_POST_BODY;)
|
||||
|
||||
DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(LDRBT, LOAD,
|
||||
enum PrivilegeMode priv = cpu->privilegeMode;
|
||||
|
|
|
@ -138,6 +138,7 @@ static void _postEvent(struct mVideoLogger* logger, enum mVideoLoggerEvent event
|
|||
MutexLock(&proxyRenderer->mutex);
|
||||
proxyRenderer->event = event;
|
||||
ConditionWake(&proxyRenderer->toThreadCond);
|
||||
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
}
|
||||
|
||||
|
|
|
@ -348,7 +348,9 @@ void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState*
|
|||
CircleBufferDump(&audio->chA.fifo, state->audio.fifoA, sizeof(state->audio.fifoA));
|
||||
CircleBufferDump(&audio->chB.fifo, state->audio.fifoB, sizeof(state->audio.fifoB));
|
||||
uint32_t fifoSize = CircleBufferSize(&audio->chA.fifo);
|
||||
STORE_32(fifoSize, 0, &state->audio.fifoSize);
|
||||
STORE_32(fifoSize, 0, &state->audio.fifoSizeA);
|
||||
fifoSize = CircleBufferSize(&audio->chB.fifo);
|
||||
STORE_32(fifoSize, 0, &state->audio.fifoSizeB);
|
||||
STORE_32(audio->sampleEvent.when - mTimingCurrentTime(&audio->p->timing), 0, &state->audio.nextSample);
|
||||
}
|
||||
|
||||
|
@ -358,13 +360,20 @@ void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState
|
|||
CircleBufferClear(&audio->chA.fifo);
|
||||
CircleBufferClear(&audio->chB.fifo);
|
||||
uint32_t fifoSize;
|
||||
LOAD_32(fifoSize, 0, &state->audio.fifoSize);
|
||||
if (state->audio.fifoSize > CircleBufferCapacity(&audio->chA.fifo)) {
|
||||
LOAD_32(fifoSize, 0, &state->audio.fifoSizeA);
|
||||
if (fifoSize > CircleBufferCapacity(&audio->chA.fifo)) {
|
||||
fifoSize = CircleBufferCapacity(&audio->chA.fifo);
|
||||
}
|
||||
size_t i;
|
||||
for (i = 0; i < fifoSize; ++i) {
|
||||
CircleBufferWrite8(&audio->chA.fifo, state->audio.fifoA[i]);
|
||||
}
|
||||
|
||||
LOAD_32(fifoSize, 0, &state->audio.fifoSizeB);
|
||||
if (fifoSize > CircleBufferCapacity(&audio->chB.fifo)) {
|
||||
fifoSize = CircleBufferCapacity(&audio->chB.fifo);
|
||||
}
|
||||
for (i = 0; i < fifoSize; ++i) {
|
||||
CircleBufferWrite8(&audio->chB.fifo, state->audio.fifoB[i]);
|
||||
}
|
||||
|
||||
|
|
|
@ -355,6 +355,20 @@ static void _GBACoreReloadConfigOption(struct mCore* core, const char* option, c
|
|||
}
|
||||
return;
|
||||
}
|
||||
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
||||
struct GBACore* gbacore = (struct GBACore*) core;
|
||||
if (strcmp("videoScale", option) == 0) {
|
||||
if (config != &core->config) {
|
||||
mCoreConfigCopyValue(&core->config, config, "videoScale");
|
||||
}
|
||||
if (gbacore->glRenderer.outputTex != (unsigned) -1 && mCoreConfigGetIntValue(&core->config, "hwaccelVideo", &fakeBool) && fakeBool) {
|
||||
int scale;
|
||||
mCoreConfigGetIntValue(config, "videoScale", &scale);
|
||||
GBAVideoGLRendererSetScale(&gbacore->glRenderer, scale);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void _GBACoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) {
|
||||
|
|
|
@ -219,6 +219,7 @@ void GBADMAUpdate(struct GBA* gba) {
|
|||
}
|
||||
|
||||
if (memory->activeDMA >= 0) {
|
||||
gba->dmaPC = gba->cpu->gprs[ARM_PC];
|
||||
mTimingDeschedule(&gba->timing, &memory->dmaEvent);
|
||||
mTimingSchedule(&gba->timing, &memory->dmaEvent, memory->dma[memory->activeDMA].when - currentTime);
|
||||
} else {
|
||||
|
|
|
@ -203,6 +203,7 @@ void GBAReset(struct ARMCore* cpu) {
|
|||
|
||||
gba->cpuBlocked = false;
|
||||
gba->earlyExit = false;
|
||||
gba->dmaPC = 0;
|
||||
if (gba->yankedRomSize) {
|
||||
gba->memory.romSize = gba->yankedRomSize;
|
||||
gba->memory.romMask = toPow2(gba->memory.romSize) - 1;
|
||||
|
|
11
src/gba/io.c
11
src/gba/io.c
|
@ -950,7 +950,8 @@ void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) {
|
|||
STORE_32(gba->memory.dma[i].when, 0, &state->dma[i].when);
|
||||
}
|
||||
|
||||
state->dmaTransferRegister = gba->memory.dmaTransferRegister;
|
||||
STORE_32(gba->memory.dmaTransferRegister, 0, &state->dmaTransferRegister);
|
||||
STORE_32(gba->dmaPC, 0, &state->dmaBlockPC);
|
||||
|
||||
GBAHardwareSerialize(&gba->memory.hw, state);
|
||||
}
|
||||
|
@ -988,12 +989,12 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
|||
LOAD_32(gba->memory.dma[i].nextDest, 0, &state->dma[i].nextDest);
|
||||
LOAD_32(gba->memory.dma[i].nextCount, 0, &state->dma[i].nextCount);
|
||||
LOAD_32(gba->memory.dma[i].when, 0, &state->dma[i].when);
|
||||
if (GBADMARegisterGetTiming(gba->memory.dma[i].reg) != GBA_DMA_TIMING_NOW) {
|
||||
GBADMASchedule(gba, i, &gba->memory.dma[i]);
|
||||
}
|
||||
}
|
||||
GBAAudioWriteSOUNDCNT_X(&gba->audio, gba->memory.io[REG_SOUNDCNT_X >> 1]);
|
||||
gba->memory.dmaTransferRegister = state->dmaTransferRegister;
|
||||
|
||||
LOAD_32(gba->memory.dmaTransferRegister, 0, &state->dmaTransferRegister);
|
||||
LOAD_32(gba->dmaPC, 0, &state->dmaBlockPC);
|
||||
|
||||
GBADMAUpdate(gba);
|
||||
GBAHardwareDeserialize(&gba->memory.hw, state);
|
||||
}
|
||||
|
|
|
@ -337,7 +337,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
|
|||
}
|
||||
|
||||
#define LOAD_BAD \
|
||||
if (gba->performingDMA) { \
|
||||
if (gba->performingDMA || cpu->gprs[ARM_PC] - gba->dmaPC == (gba->cpu->executionMode == MODE_THUMB ? WORD_SIZE_THUMB : WORD_SIZE_ARM)) { \
|
||||
value = gba->bus; \
|
||||
} else { \
|
||||
value = cpu->prefetch[1]; \
|
||||
|
@ -772,12 +772,12 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
#define STORE_SRAM \
|
||||
if (address & 0x3) { \
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Unaligned SRAM Store32: 0x%08X", address); \
|
||||
value = 0; \
|
||||
} \
|
||||
GBAStore8(cpu, address & ~0x3, value, cycleCounter); \
|
||||
GBAStore8(cpu, (address & ~0x3) | 1, value, cycleCounter); \
|
||||
GBAStore8(cpu, (address & ~0x3) | 2, value, cycleCounter); \
|
||||
GBAStore8(cpu, (address & ~0x3) | 3, value, cycleCounter);
|
||||
} else { \
|
||||
GBAStore8(cpu, address, value, cycleCounter); \
|
||||
GBAStore8(cpu, address | 1, value, cycleCounter); \
|
||||
GBAStore8(cpu, address | 2, value, cycleCounter); \
|
||||
GBAStore8(cpu, address | 3, value, cycleCounter); \
|
||||
}
|
||||
|
||||
#define STORE_BAD \
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Bad memory Store32: 0x%08X", address);
|
||||
|
@ -923,8 +923,12 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
|
|||
break;
|
||||
case REGION_CART_SRAM:
|
||||
case REGION_CART_SRAM_MIRROR:
|
||||
GBAStore8(cpu, (address & ~0x1), value, cycleCounter);
|
||||
GBAStore8(cpu, (address & ~0x1) | 1, value, cycleCounter);
|
||||
if (address & 1) {
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Unaligned SRAM Store16: 0x%08X", address);
|
||||
break;
|
||||
}
|
||||
GBAStore8(cpu, address, value, cycleCounter);
|
||||
GBAStore8(cpu, address | 1, value, cycleCounter);
|
||||
break;
|
||||
default:
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Bad memory Store16: 0x%08X", address);
|
||||
|
|
|
@ -268,10 +268,10 @@ static const char* const _renderMode2 =
|
|||
" ivec2 offset[4];\n"
|
||||
" vec2 incoord = texCoord;\n"
|
||||
" if (mosaic.x > 1) {\n"
|
||||
" incoord.x = float(int(incoord.x) % mosaic.x);\n"
|
||||
" incoord.x = floor(incoord.x - float(int(incoord.x) % mosaic.x));\n"
|
||||
" }\n"
|
||||
" if (mosaic.y > 1) {\n"
|
||||
" incoord.y = float(int(incoord.y) % mosaic.y);\n"
|
||||
" incoord.y = floor(incoord.y - float(int(incoord.y) % mosaic.y));\n"
|
||||
" }\n"
|
||||
" loadAffine(int(incoord.y), mat, offset);\n"
|
||||
" float y = fract(incoord.y);\n"
|
||||
|
@ -321,10 +321,10 @@ static const char* const _renderMode35 =
|
|||
" ivec2 offset[4];\n"
|
||||
" vec2 incoord = texCoord;\n"
|
||||
" if (mosaic.x > 1) {\n"
|
||||
" incoord.x = float(int(incoord.x) % mosaic.x);\n"
|
||||
" incoord.x = floor(incoord.x - float(int(incoord.x) % mosaic.x));\n"
|
||||
" }\n"
|
||||
" if (mosaic.y > 1) {\n"
|
||||
" incoord.y = float(int(incoord.y) % mosaic.y);\n"
|
||||
" incoord.y = floor(incoord.y - float(int(incoord.y) % mosaic.y));\n"
|
||||
" }\n"
|
||||
" loadAffine(int(incoord.y), mat, offset);\n"
|
||||
" float y = fract(incoord.y);\n"
|
||||
|
@ -386,10 +386,10 @@ static const char* const _renderMode4 =
|
|||
" ivec2 offset[4];\n"
|
||||
" vec2 incoord = texCoord;\n"
|
||||
" if (mosaic.x > 1) {\n"
|
||||
" incoord.x = float(int(incoord.x) % mosaic.x);\n"
|
||||
" incoord.x = floor(incoord.x - float(int(incoord.x) % mosaic.x));\n"
|
||||
" }\n"
|
||||
" if (mosaic.y > 1) {\n"
|
||||
" incoord.y = float(int(incoord.y) % mosaic.y);\n"
|
||||
" incoord.y = floor(incoord.y - float(int(incoord.y) % mosaic.y));\n"
|
||||
" }\n"
|
||||
" loadAffine(int(incoord.y), mat, offset);\n"
|
||||
" float y = fract(incoord.y);\n"
|
||||
|
@ -696,7 +696,7 @@ void GBAVideoGLRendererCreate(struct GBAVideoGLRenderer* renderer) {
|
|||
renderer->scale = 1;
|
||||
}
|
||||
|
||||
static void _compileShader(struct GBAVideoGLRenderer* glRenderer, struct GBAVideoGLShader* shader, const char** shaderBuffer, int shaderBufferLines, GLuint vs, const struct GBAVideoGLUniform* uniforms, char* log) {
|
||||
static void _compileShader(struct GBAVideoGLRenderer* glRenderer, struct GBAVideoGLShader* shader, const char** shaderBuffer, int shaderBufferLines, GLuint vs, const struct GBAVideoGLUniform* uniforms, const char* const* outFrags, char* log) {
|
||||
GLuint program = glCreateProgram();
|
||||
shader->program = program;
|
||||
|
||||
|
@ -709,16 +709,20 @@ static void _compileShader(struct GBAVideoGLRenderer* glRenderer, struct GBAVide
|
|||
if (log[0]) {
|
||||
mLOG(GBA_VIDEO, ERROR, "Fragment shader compilation failure: %s", log);
|
||||
}
|
||||
size_t i;
|
||||
#ifndef BUILD_GLES3
|
||||
for (i = 0; outFrags[i]; ++i) {
|
||||
glBindFragDataLocation(program, i, outFrags[i]);
|
||||
}
|
||||
#else
|
||||
UNUSED(outFrags);
|
||||
#endif
|
||||
glLinkProgram(program);
|
||||
glGetProgramInfoLog(program, 2048, 0, log);
|
||||
if (log[0]) {
|
||||
mLOG(GBA_VIDEO, ERROR, "Program link failure: %s", log);
|
||||
}
|
||||
glDeleteShader(fs);
|
||||
#ifndef BUILD_GLES3
|
||||
glBindFragDataLocation(program, 0, "color");
|
||||
glBindFragDataLocation(program, 1, "flags");
|
||||
#endif
|
||||
|
||||
glGenVertexArrays(1, &shader->vao);
|
||||
glBindVertexArray(shader->vao);
|
||||
|
@ -727,7 +731,6 @@ static void _compileShader(struct GBAVideoGLRenderer* glRenderer, struct GBAVide
|
|||
glEnableVertexAttribArray(positionLocation);
|
||||
glVertexAttribPointer(positionLocation, 2, GL_INT, GL_FALSE, 0, NULL);
|
||||
|
||||
size_t i;
|
||||
for (i = 0; uniforms[i].name; ++i) {
|
||||
shader->uniforms[uniforms[i].type] = glGetUniformLocation(program, uniforms[i].name);
|
||||
}
|
||||
|
@ -752,19 +755,7 @@ static void _initFramebufferTexture(GLuint tex, GLenum format, GLenum attachment
|
|||
_initFramebufferTextureEx(tex, format, format, GL_UNSIGNED_BYTE, attachment, scale);
|
||||
}
|
||||
|
||||
void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer) {
|
||||
struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
|
||||
glRenderer->temporaryBuffer = NULL;
|
||||
|
||||
glGenFramebuffers(GBA_GL_FBO_MAX, glRenderer->fbo);
|
||||
glGenTextures(GBA_GL_TEX_MAX, glRenderer->layers);
|
||||
|
||||
glGenTextures(1, &glRenderer->vramTex);
|
||||
glBindTexture(GL_TEXTURE_2D, glRenderer->vramTex);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA4, 256, 192, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 0);
|
||||
|
||||
static void _initFramebuffers(struct GBAVideoGLRenderer* glRenderer) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OBJ]);
|
||||
_initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_OBJ_COLOR], GL_RGBA, GL_COLOR_ATTACHMENT0, glRenderer->scale);
|
||||
_initFramebufferTextureEx(glRenderer->layers[GBA_GL_TEX_OBJ_FLAGS], GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, GL_COLOR_ATTACHMENT1, glRenderer->scale);
|
||||
|
@ -781,7 +772,28 @@ void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer) {
|
|||
glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OUTPUT]);
|
||||
_initFramebufferTexture(glRenderer->outputTex, GL_RGB, GL_COLOR_ATTACHMENT0, glRenderer->scale);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
struct GBAVideoGLBackground* bg = &glRenderer->bg[i];
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, bg->fbo);
|
||||
_initFramebufferTexture(bg->tex, GL_RGBA, GL_COLOR_ATTACHMENT0, glRenderer->scale);
|
||||
_initFramebufferTextureEx(bg->flags, GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, GL_COLOR_ATTACHMENT1, glRenderer->scale);
|
||||
}
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer) {
|
||||
struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
|
||||
glRenderer->temporaryBuffer = NULL;
|
||||
|
||||
glGenFramebuffers(GBA_GL_FBO_MAX, glRenderer->fbo);
|
||||
glGenTextures(GBA_GL_TEX_MAX, glRenderer->layers);
|
||||
|
||||
glGenTextures(1, &glRenderer->vramTex);
|
||||
glBindTexture(GL_TEXTURE_2D, glRenderer->vramTex);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA4, 256, 192, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 0);
|
||||
|
||||
glGenBuffers(1, &glRenderer->vbo);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, glRenderer->vbo);
|
||||
|
@ -814,11 +826,9 @@ void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer) {
|
|||
glGenFramebuffers(1, &bg->fbo);
|
||||
glGenTextures(1, &bg->tex);
|
||||
glGenTextures(1, &bg->flags);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, bg->fbo);
|
||||
_initFramebufferTexture(bg->tex, GL_RGBA, GL_COLOR_ATTACHMENT0, glRenderer->scale);
|
||||
_initFramebufferTextureEx(bg->flags, GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, GL_COLOR_ATTACHMENT1, glRenderer->scale);
|
||||
}
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
_initFramebuffers(glRenderer);
|
||||
|
||||
char log[2048];
|
||||
const GLchar* shaderBuffer[4];
|
||||
|
@ -838,56 +848,52 @@ void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer) {
|
|||
mLOG(GBA_VIDEO, ERROR, "Vertex shader compilation failure: %s", log);
|
||||
}
|
||||
|
||||
const char* const noWindow[] = {"color", "flags", NULL};
|
||||
const char* const window[] = {"color", "flags", "window", NULL};
|
||||
const char* const onlyWindow[] = {"window", NULL};
|
||||
const char* const onlyColor[] = {"color", NULL};
|
||||
|
||||
shaderBuffer[1] = _renderMode0;
|
||||
|
||||
shaderBuffer[2] = _renderTile16;
|
||||
_compileShader(glRenderer, &glRenderer->bgShader[0], shaderBuffer, 3, vs, _uniformsMode0, log);
|
||||
_compileShader(glRenderer, &glRenderer->bgShader[0], shaderBuffer, 3, vs, _uniformsMode0, noWindow, log);
|
||||
|
||||
shaderBuffer[2] = _renderTile256;
|
||||
_compileShader(glRenderer, &glRenderer->bgShader[1], shaderBuffer, 3, vs, _uniformsMode0, log);
|
||||
_compileShader(glRenderer, &glRenderer->bgShader[1], shaderBuffer, 3, vs, _uniformsMode0, noWindow, log);
|
||||
|
||||
shaderBuffer[1] = _renderMode2;
|
||||
shaderBuffer[2] = _interpolate;
|
||||
|
||||
shaderBuffer[3] = _fetchTileOverflow;
|
||||
_compileShader(glRenderer, &glRenderer->bgShader[2], shaderBuffer, 4, vs, _uniformsMode2, log);
|
||||
_compileShader(glRenderer, &glRenderer->bgShader[2], shaderBuffer, 4, vs, _uniformsMode2, noWindow, log);
|
||||
|
||||
shaderBuffer[3] = _fetchTileNoOverflow;
|
||||
_compileShader(glRenderer, &glRenderer->bgShader[3], shaderBuffer, 4, vs, _uniformsMode2, log);
|
||||
_compileShader(glRenderer, &glRenderer->bgShader[3], shaderBuffer, 4, vs, _uniformsMode2, noWindow, log);
|
||||
|
||||
shaderBuffer[1] = _renderMode4;
|
||||
shaderBuffer[2] = _interpolate;
|
||||
_compileShader(glRenderer, &glRenderer->bgShader[4], shaderBuffer, 3, vs, _uniformsMode4, log);
|
||||
_compileShader(glRenderer, &glRenderer->bgShader[4], shaderBuffer, 3, vs, _uniformsMode4, noWindow, log);
|
||||
|
||||
shaderBuffer[1] = _renderMode35;
|
||||
shaderBuffer[2] = _interpolate;
|
||||
_compileShader(glRenderer, &glRenderer->bgShader[5], shaderBuffer, 3, vs, _uniformsMode35, log);
|
||||
_compileShader(glRenderer, &glRenderer->bgShader[5], shaderBuffer, 3, vs, _uniformsMode35, noWindow, log);
|
||||
|
||||
shaderBuffer[1] = _renderObj;
|
||||
|
||||
shaderBuffer[2] = _renderTile16;
|
||||
_compileShader(glRenderer, &glRenderer->objShader[0], shaderBuffer, 3, vs, _uniformsObj, log);
|
||||
#ifndef BUILD_GLES3
|
||||
glBindFragDataLocation(glRenderer->objShader[0].program, 2, "window");
|
||||
#endif
|
||||
_compileShader(glRenderer, &glRenderer->objShader[0], shaderBuffer, 3, vs, _uniformsObj, window, log);
|
||||
|
||||
shaderBuffer[2] = _renderTile256;
|
||||
_compileShader(glRenderer, &glRenderer->objShader[1], shaderBuffer, 3, vs, _uniformsObj, log);
|
||||
#ifndef BUILD_GLES3
|
||||
glBindFragDataLocation(glRenderer->objShader[1].program, 2, "window");
|
||||
#endif
|
||||
_compileShader(glRenderer, &glRenderer->objShader[1], shaderBuffer, 3, vs, _uniformsObj, window, log);
|
||||
|
||||
shaderBuffer[1] = _renderObjPriority;
|
||||
_compileShader(glRenderer, &glRenderer->objShader[2], shaderBuffer, 2, vs, _uniformsObjPriority, log);
|
||||
_compileShader(glRenderer, &glRenderer->objShader[2], shaderBuffer, 2, vs, _uniformsObjPriority, noWindow, log);
|
||||
|
||||
shaderBuffer[1] = _renderWindow;
|
||||
_compileShader(glRenderer, &glRenderer->windowShader, shaderBuffer, 2, vs, _uniformsWindow, log);
|
||||
#ifndef BUILD_GLES3
|
||||
glBindFragDataLocation(glRenderer->windowShader.program, 0, "window");
|
||||
#endif
|
||||
_compileShader(glRenderer, &glRenderer->windowShader, shaderBuffer, 2, vs, _uniformsWindow, onlyWindow, log);
|
||||
|
||||
shaderBuffer[1] = _finalize;
|
||||
_compileShader(glRenderer, &glRenderer->finalizeShader, shaderBuffer, 2, vs, _uniformsFinalize, log);
|
||||
_compileShader(glRenderer, &glRenderer->finalizeShader, shaderBuffer, 2, vs, _uniformsFinalize, onlyColor, log);
|
||||
|
||||
glBindVertexArray(0);
|
||||
glDeleteShader(vs);
|
||||
|
@ -925,6 +931,7 @@ void GBAVideoGLRendererDeinit(struct GBAVideoRenderer* renderer) {
|
|||
void GBAVideoGLRendererReset(struct GBAVideoRenderer* renderer) {
|
||||
struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
|
||||
|
||||
glRenderer->oamDirty = true;
|
||||
glRenderer->paletteDirty = true;
|
||||
glRenderer->vramDirty = 0xFFFFFF;
|
||||
glRenderer->firstAffine = -1;
|
||||
|
@ -1858,4 +1865,17 @@ void GBAVideoGLRendererDrawWindow(struct GBAVideoGLRenderer* renderer, int y) {
|
|||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
}
|
||||
|
||||
void GBAVideoGLRendererSetScale(struct GBAVideoGLRenderer* renderer, int scale) {
|
||||
if (scale == renderer->scale) {
|
||||
return;
|
||||
}
|
||||
if (renderer->temporaryBuffer) {
|
||||
mappedMemoryFree(renderer->temporaryBuffer, GBA_VIDEO_HORIZONTAL_PIXELS * GBA_VIDEO_VERTICAL_PIXELS * renderer->scale * renderer->scale * BYTES_PER_PIXEL);
|
||||
renderer->temporaryBuffer = NULL;
|
||||
}
|
||||
renderer->scale = scale;
|
||||
_initFramebuffers(renderer);
|
||||
renderer->paletteDirty = true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -152,7 +152,7 @@ int main() {
|
|||
.teardown = mPSP2Teardown,
|
||||
.gameLoaded = mPSP2LoadROM,
|
||||
.gameUnloaded = mPSP2UnloadROM,
|
||||
.prepareForFrame = NULL,
|
||||
.prepareForFrame = mPSP2Swap,
|
||||
.drawFrame = mPSP2Draw,
|
||||
.drawScreenshot = mPSP2DrawScreenshot,
|
||||
.paused = mPSP2Paused,
|
||||
|
|
|
@ -51,8 +51,8 @@ static enum ScreenMode {
|
|||
} screenMode;
|
||||
|
||||
static void* outputBuffer;
|
||||
static vita2d_texture* tex;
|
||||
static vita2d_texture* oldTex;
|
||||
static int currentTex;
|
||||
static vita2d_texture* tex[4];
|
||||
static vita2d_texture* screenshot;
|
||||
static Thread audioThread;
|
||||
static bool interframeBlending = false;
|
||||
|
@ -324,12 +324,14 @@ void mPSP2Setup(struct mGUIRunner* runner) {
|
|||
|
||||
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);
|
||||
oldTex = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
|
||||
tex[0] = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
|
||||
tex[1] = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
|
||||
tex[2] = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
|
||||
tex[3] = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
|
||||
currentTex = 0;
|
||||
screenshot = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
|
||||
|
||||
outputBuffer = anonymousMemoryMap(256 * toPow2(height) * 4);
|
||||
runner->core->setVideoBuffer(runner->core, outputBuffer, 256);
|
||||
runner->core->setVideoBuffer(runner->core, vita2d_texture_get_datap(tex[currentTex]), 256);
|
||||
runner->core->setAudioBufferSize(runner->core, PSP2_SAMPLES);
|
||||
|
||||
rotation.d.sample = _sampleRotation;
|
||||
|
@ -490,8 +492,10 @@ void mPSP2Unpaused(struct mGUIRunner* runner) {
|
|||
void mPSP2Teardown(struct mGUIRunner* runner) {
|
||||
UNUSED(runner);
|
||||
CircleBufferDeinit(&rumble.history);
|
||||
vita2d_free_texture(tex);
|
||||
vita2d_free_texture(oldTex);
|
||||
vita2d_free_texture(tex[0]);
|
||||
vita2d_free_texture(tex[1]);
|
||||
vita2d_free_texture(tex[2]);
|
||||
vita2d_free_texture(tex[3]);
|
||||
vita2d_free_texture(screenshot);
|
||||
mappedMemoryFree(outputBuffer, 256 * 256 * 4);
|
||||
frameLimiter = true;
|
||||
|
@ -583,17 +587,18 @@ void _drawTex(vita2d_texture* t, unsigned width, unsigned height, bool faded, bo
|
|||
tint);
|
||||
}
|
||||
|
||||
void mPSP2Swap(struct mGUIRunner* runner) {
|
||||
currentTex = (currentTex + 1) & 3;
|
||||
runner->core->setVideoBuffer(runner->core, vita2d_texture_get_datap(tex[currentTex]), 256);
|
||||
}
|
||||
|
||||
void mPSP2Draw(struct mGUIRunner* runner, bool faded) {
|
||||
unsigned width, height;
|
||||
runner->core->desiredVideoDimensions(runner->core, &width, &height);
|
||||
void* texpixels = vita2d_texture_get_datap(tex);
|
||||
if (interframeBlending) {
|
||||
void* oldTexpixels = vita2d_texture_get_datap(oldTex);
|
||||
memcpy(oldTexpixels, texpixels, 256 * height * 4);
|
||||
_drawTex(oldTex, width, height, faded, false);
|
||||
_drawTex(tex[(currentTex - 1) & 3], width, height, faded, false);
|
||||
}
|
||||
memcpy(texpixels, outputBuffer, 256 * height * 4);
|
||||
_drawTex(tex, width, height, faded, interframeBlending);
|
||||
_drawTex(tex[currentTex], width, height, faded, interframeBlending);
|
||||
}
|
||||
|
||||
void mPSP2DrawScreenshot(struct mGUIRunner* runner, const uint32_t* pixels, unsigned width, unsigned height, bool faded) {
|
||||
|
|
|
@ -20,6 +20,7 @@ void mPSP2UnloadROM(struct mGUIRunner* runner);
|
|||
void mPSP2PrepareForFrame(struct mGUIRunner* runner);
|
||||
void mPSP2Paused(struct mGUIRunner* runner);
|
||||
void mPSP2Unpaused(struct mGUIRunner* runner);
|
||||
void mPSP2Swap(struct mGUIRunner* runner);
|
||||
void mPSP2Draw(struct mGUIRunner* runner, bool faded);
|
||||
void mPSP2DrawScreenshot(struct mGUIRunner* runner, const color_t* pixels, unsigned width, unsigned height, bool faded);
|
||||
void mPSP2IncrementScreenMode(struct mGUIRunner* runner);
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>© 2013 – 2019 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0
|
||||
<string>© 2013 – 2020 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0
|
||||
Game Boy Advance is a registered trademark of Nintendo Co., Ltd.</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
|
|
|
@ -51,6 +51,7 @@ public:
|
|||
virtual bool supportsShaders() const = 0;
|
||||
virtual VideoShader* shaders() = 0;
|
||||
virtual int framebufferHandle() { return -1; }
|
||||
virtual void setVideoScale(int scale) {}
|
||||
|
||||
QSize viewportSize();
|
||||
|
||||
|
|
|
@ -221,7 +221,6 @@ void DisplayGL::clearShaders() {
|
|||
QMetaObject::invokeMethod(m_painter, "clearShaders");
|
||||
}
|
||||
|
||||
|
||||
void DisplayGL::resizeContext() {
|
||||
if (m_drawThread) {
|
||||
m_isDrawing = false;
|
||||
|
@ -230,6 +229,15 @@ void DisplayGL::resizeContext() {
|
|||
}
|
||||
}
|
||||
|
||||
void DisplayGL::setVideoScale(int scale) {
|
||||
if (m_drawThread) {
|
||||
m_isDrawing = false;
|
||||
CoreController::Interrupter interrupter(m_context);
|
||||
mCoreConfigSetIntValue(&m_context->thread()->core->config, "videoScale", scale);
|
||||
QMetaObject::invokeMethod(m_painter, "resizeContext", Qt::BlockingQueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayGL::resizeEvent(QResizeEvent* event) {
|
||||
Display::resizeEvent(event);
|
||||
resizePainter();
|
||||
|
@ -266,6 +274,15 @@ PainterGL::PainterGL(QWindow* surface, QOpenGLContext* parent, int forceVersion)
|
|||
|
||||
m_gl->makeCurrent(m_surface);
|
||||
m_window = new QOpenGLPaintDevice;
|
||||
{
|
||||
// XXX: Qt creates TLS for OpenGL objects in the local thread
|
||||
// We need to prevent that thread from being the painter thread
|
||||
// Qt also caches the engine object on the device if a different
|
||||
// engine is active, so let's make a temporary one
|
||||
QOpenGLPaintDevice* fakeDevice = new QOpenGLPaintDevice;
|
||||
QPainter fakePainter(fakeDevice);
|
||||
m_window->paintEngine();
|
||||
}
|
||||
#if defined(_WIN32) && defined(USE_EPOXY)
|
||||
epoxy_handle_external_wglMakeCurrent();
|
||||
#endif
|
||||
|
@ -351,6 +368,11 @@ void PainterGL::resizeContext() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (m_started) {
|
||||
mCore* core = m_context->thread()->core;
|
||||
core->reloadConfigOption(core, "videoScale", NULL);
|
||||
}
|
||||
|
||||
QSize size = m_context->screenDimensions();
|
||||
m_backend->setDimensions(m_backend, size.width(), size.height());
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ public slots:
|
|||
void setShaders(struct VDir*) override;
|
||||
void clearShaders() override;
|
||||
void resizeContext() override;
|
||||
void setVideoScale(int scale) override;
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent*) override { forceDraw(); }
|
||||
|
|
|
@ -488,7 +488,7 @@ void SettingsView::updateConfig() {
|
|||
|
||||
int videoScale = m_controller->getOption("videoScale", 1).toInt();
|
||||
int hwaccelVideo = m_controller->getOption("hwaccelVideo").toInt();
|
||||
if (videoScale != m_ui.videoScale->value() || hwaccelVideo != m_ui.hwaccelVideo->currentIndex()) {
|
||||
if (hwaccelVideo != m_ui.hwaccelVideo->currentIndex()) {
|
||||
emit videoRendererChanged();
|
||||
}
|
||||
saveSetting("videoScale", m_ui.videoScale);
|
||||
|
|
|
@ -61,7 +61,7 @@ void ShortcutView::load(const QModelIndex& index) {
|
|||
}
|
||||
QString name = m_model->name(index);
|
||||
const Shortcut* item = m_controller->shortcut(name);
|
||||
if (!item->action()) {
|
||||
if (!item || !item->action()) {
|
||||
return;
|
||||
}
|
||||
int shortcut = item->shortcut();
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include "CoreController.h"
|
||||
|
||||
#include <QThread>
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
VideoProxy::VideoProxy() {
|
||||
|
@ -81,11 +83,22 @@ bool VideoProxy::readData(void* data, size_t length, bool block) {
|
|||
}
|
||||
|
||||
void VideoProxy::postEvent(enum mVideoLoggerEvent event) {
|
||||
emit eventPosted(event);
|
||||
if (QThread::currentThread() == thread()) {
|
||||
// We're on the main thread
|
||||
emit eventPosted(event);
|
||||
} else {
|
||||
m_mutex.lock();
|
||||
emit eventPosted(event);
|
||||
m_fromThreadCond.wait(&m_mutex, 1);
|
||||
m_mutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void VideoProxy::handleEvent(int event) {
|
||||
m_mutex.lock();
|
||||
m_logger.d.handleEvent(&m_logger.d, static_cast<enum mVideoLoggerEvent>(event));
|
||||
m_fromThreadCond.wakeAll();
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
void VideoProxy::lock() {
|
||||
|
|
|
@ -197,11 +197,15 @@ VideoView::~VideoView() {
|
|||
}
|
||||
|
||||
void VideoView::setController(std::shared_ptr<CoreController> controller) {
|
||||
connect(controller.get(), &CoreController::stopping, this, &VideoView::stopRecording);
|
||||
connect(this, &VideoView::recordingStarted, controller.get(), &CoreController::setAVStream);
|
||||
connect(this, &VideoView::recordingStopped, controller.get(), &CoreController::clearAVStream, Qt::DirectConnection);
|
||||
CoreController* controllerPtr = controller.get();
|
||||
connect(controllerPtr, &CoreController::frameAvailable, this, [this, controllerPtr]() {
|
||||
setNativeResolution(controllerPtr->screenDimensions());
|
||||
});
|
||||
connect(controllerPtr, &CoreController::stopping, this, &VideoView::stopRecording);
|
||||
connect(this, &VideoView::recordingStarted, controllerPtr, &CoreController::setAVStream);
|
||||
connect(this, &VideoView::recordingStopped, controllerPtr, &CoreController::clearAVStream, Qt::DirectConnection);
|
||||
|
||||
setNativeResolution(controller->screenDimensions());
|
||||
setNativeResolution(controllerPtr->screenDimensions());
|
||||
}
|
||||
|
||||
void VideoView::startRecording() {
|
||||
|
@ -225,6 +229,9 @@ void VideoView::stopRecording() {
|
|||
}
|
||||
|
||||
void VideoView::setNativeResolution(const QSize& dims) {
|
||||
if (dims.width() == m_nativeWidth && dims.height() == m_nativeHeight) {
|
||||
return;
|
||||
}
|
||||
m_nativeWidth = dims.width();
|
||||
m_nativeHeight = dims.height();
|
||||
m_ui.presetNative->setText(tr("Native (%0x%1)").arg(m_nativeWidth).arg(m_nativeHeight));
|
||||
|
|
|
@ -1709,6 +1709,13 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
}
|
||||
}, this);
|
||||
|
||||
ConfigOption* videoScale = m_config->addOption("videoScale");
|
||||
videoScale->connect([this](const QVariant& value) {
|
||||
if (m_display) {
|
||||
m_display->setVideoScale(value.toInt());
|
||||
}
|
||||
}, this);
|
||||
|
||||
m_actions.addHiddenAction(tr("Exit fullscreen"), "exitFullScreen", this, &Window::exitFullScreen, "frame", QKeySequence("Esc"));
|
||||
|
||||
m_actions.addHeldAction(tr("GameShark Button (held)"), "holdGSButton", [this](bool held) {
|
||||
|
|
|
@ -40,9 +40,9 @@
|
|||
</message>
|
||||
<message>
|
||||
<location filename="../AboutScreen.ui" line="86"/>
|
||||
<source>© 2013 – 2019 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0
|
||||
<source>© 2013 – 2020 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0
|
||||
Game Boy Advance is a registered trademark of Nintendo Co., Ltd.</source>
|
||||
<translation>© 2013 – 2019 Jeffrey Pfau, lizenziert unter der Mozilla Public License, Version 2.0
|
||||
<translation>© 2013 – 2020 Jeffrey Pfau, lizenziert unter der Mozilla Public License, Version 2.0
|
||||
Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.</translation>
|
||||
</message>
|
||||
<message>
|
||||
|
|
|
@ -30,9 +30,9 @@
|
|||
</message>
|
||||
<message>
|
||||
<location filename="../AboutScreen.ui" line="86"/>
|
||||
<source>© 2013 – 2019 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0
|
||||
<source>© 2013 – 2020 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0
|
||||
Game Boy Advance is a registered trademark of Nintendo Co., Ltd.</source>
|
||||
<translation>© 2013 – 2019 Jeffrey Pfau, licenciado bajo la Mozilla Public License, versión 2.0
|
||||
<translation>© 2013 – 2020 Jeffrey Pfau, licenciado bajo la Mozilla Public License, versión 2.0
|
||||
Game Boy Advance es una marca registrada de Nintendo Co., Ltd.</translation>
|
||||
</message>
|
||||
<message>
|
||||
|
|
|
@ -24,9 +24,9 @@
|
|||
<translation>{projectName} desidera ringraziare i seguenti sponsor di Patreon:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>© 2013 – 2016 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0
|
||||
<source>© 2013 – 2020 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0
|
||||
Game Boy Advance is a registered trademark of Nintendo Co., Ltd.</source>
|
||||
<translation>© 2013 - 2016 Jeffrey Pfau, sotto licenza Mozilla Public License, versione 2.0
|
||||
<translation>© 2013 - 2020 Jeffrey Pfau, sotto licenza Mozilla Public License, versione 2.0
|
||||
Game Boy Advance è un marchio registrato di Nintendo Co., Ltd.</translation>
|
||||
</message>
|
||||
<message>
|
||||
|
|
|
@ -30,9 +30,9 @@
|
|||
</message>
|
||||
<message>
|
||||
<location filename="../AboutScreen.ui" line="86"/>
|
||||
<source>© 2013 – 2018 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0
|
||||
<source>© 2013 – 2020 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0
|
||||
Game Boy Advance is a registered trademark of Nintendo Co., Ltd.</source>
|
||||
<translation type="unfinished">© 2013 – 2018 Jeffrey Pfau, est autorisé sous la license Publique de Mozilla, version 2.0
|
||||
<translation type="unfinished">© 2013 – 2020 Jeffrey Pfau, est autorisé sous la license Publique de Mozilla, version 2.0
|
||||
Game Boy Advance est une marque de fabrique enregistré par Nintendo Co., Ltd.</translation>
|
||||
</message>
|
||||
<message>
|
||||
|
|
|
@ -24,9 +24,9 @@
|
|||
<translation>{projectName}은 Patreon의 다음 고객에게 감사드립니다:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>© 2013 – 2016 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0
|
||||
<source>© 2013 – 2020 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0
|
||||
Game Boy Advance is a registered trademark of Nintendo Co., Ltd.</source>
|
||||
<translation>© 2013 - 2016 Jeffrey Pfau, 모질라 공식 라이센스 버전 2.0에 따라 사용 허가되었습니다.
|
||||
<translation>© 2013 - 2020 Jeffrey Pfau, 모질라 공식 라이센스 버전 2.0에 따라 사용 허가되었습니다.
|
||||
Game Boy Advance는 Nintendo Co., Ltd.의 등록 상표입니다.</translation>
|
||||
</message>
|
||||
<message>
|
||||
|
|
|
@ -30,9 +30,9 @@
|
|||
</message>
|
||||
<message>
|
||||
<location filename="../AboutScreen.ui" line="86"/>
|
||||
<source>© 2013 – 2019 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0
|
||||
<source>© 2013 – 2020 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0
|
||||
Game Boy Advance is a registered trademark of Nintendo Co., Ltd.</source>
|
||||
<translation>© 2013 - 2019 Mozilla Public License, 2.0 sürümünde lisanslı Jeffrey Pfau
|
||||
<translation>© 2013 - 2020 Mozilla Public License, 2.0 sürümünde lisanslı Jeffrey Pfau
|
||||
Game Boy Advance, Nintendo Co., Ltd.'nin tescilli ticari markasıdır.</translation>
|
||||
</message>
|
||||
<message>
|
||||
|
|
|
@ -30,9 +30,9 @@
|
|||
</message>
|
||||
<message>
|
||||
<location filename="../AboutScreen.ui" line="86"/>
|
||||
<source>© 2013 – 2019 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0
|
||||
<source>© 2013 – 2020 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0
|
||||
Game Boy Advance is a registered trademark of Nintendo Co., Ltd.</source>
|
||||
<translation>© 2013 – 2019 Jeffrey Pfau,基于 Mozilla 公共许可证(版本 2.0)授权
|
||||
<translation>© 2013 – 2020 Jeffrey Pfau,基于 Mozilla 公共许可证(版本 2.0)授权
|
||||
Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标。</translation>
|
||||
</message>
|
||||
<message>
|
||||
|
|
Loading…
Reference in New Issue