Merge branch 'master' (early part) into medusa

This commit is contained in:
Vicki Pfau 2019-06-28 16:27:53 -07:00
commit e6aa23f19c
50 changed files with 590 additions and 267 deletions

26
CHANGES
View File

@ -75,6 +75,14 @@ Bugfixes:
- GBA Memory: Fix Vast Fame support (taizou) (fixes mgba.io/i/1170) - GBA Memory: Fix Vast Fame support (taizou) (fixes mgba.io/i/1170)
- GB, GBA Savedata: Fix unmasking savedata crash - GB, GBA Savedata: Fix unmasking savedata crash
- GBA DMA: Fix temporal sorting of DMAs of different priorities - GBA DMA: Fix temporal sorting of DMAs of different priorities
- FFmpeg: Fix encoding audio/video queue issues
- GB Serialize: Fix IRQ pending/EI pending confusion
- GB MBC: Improve multicart detection heuristic (fixes mgba.io/i/1177)
- GB Audio: Fix channel 3 reset value
- GB Audio: Fix channel 4 initial LFSR
- GB, GBA Video: Don't call finishFrame twice in thread proxy
- GB Audio: Fix channel 1, 2 and 4 reset timing
- Util: Fix wrapping edge cases in RingFIFO
Misc: Misc:
- GBA Timer: Use global cycles for timers - GBA Timer: Use global cycles for timers
- GBA: Extend oddly-sized ROMs to full address space (fixes mgba.io/i/722) - GBA: Extend oddly-sized ROMs to full address space (fixes mgba.io/i/722)
@ -108,14 +116,28 @@ Misc:
- Wii: Move audio handling to callbacks (fixes mgba.io/i/803) - Wii: Move audio handling to callbacks (fixes mgba.io/i/803)
- Qt: Clean up FPS target UI (fixes mgba.io/i/436) - Qt: Clean up FPS target UI (fixes mgba.io/i/436)
- Core: Remove broken option for whether rewinding restores save games - Core: Remove broken option for whether rewinding restores save games
- FFmpeg: Support lossless VP9 encoding
- mGUI: Add fast forward toggle
Changes from beta 1:
Features:
- Libretro: Add Game Boy cheat support
Bugfixes:
- PSP2: Fix audio crackling after fast forward
- PSP2: Fix audio crackling when buffer is full
- 3DS: Fix unused screens not clearing (fixes mgba.io/i/1184)
Misc:
- mGUI: Add SGB border configuration option
0.6.3: (2017-04-14) 0.6 beta 1: (2018-09-24)
- Initial beta for 0.6
0.6.3: (2018-04-14)
Bugfixes: Bugfixes:
- GB Audio: Revert unsigned audio changes - GB Audio: Revert unsigned audio changes
- GB Video: Fix bad merge (fixes mgba.io/i/1040) - GB Video: Fix bad merge (fixes mgba.io/i/1040)
- GBA Video: Fix OBJ blending regression (fixes mgba.io/i/1037) - GBA Video: Fix OBJ blending regression (fixes mgba.io/i/1037)
0.6.2: (2017-04-03) 0.6.2: (2018-04-03)
Bugfixes: Bugfixes:
- Core: Fix ROM patches not being unloaded when disabled (fixes mgba.io/i/962) - Core: Fix ROM patches not being unloaded when disabled (fixes mgba.io/i/962)
- 3DS: Fix opening files in directory names with trailing slashes - 3DS: Fix opening files in directory names with trailing slashes

View File

@ -49,6 +49,7 @@ set(BUILD_SHARED ON CACHE BOOL "Build a shared library")
set(SKIP_LIBRARY OFF CACHE BOOL "Skip building the library (useful for only building libretro or OpenEmu cores)") set(SKIP_LIBRARY OFF CACHE BOOL "Skip building the library (useful for only building libretro or OpenEmu cores)")
set(BUILD_GL ON CACHE BOOL "Build with OpenGL") set(BUILD_GL ON CACHE BOOL "Build with OpenGL")
set(BUILD_GLES2 OFF CACHE BOOL "Build with OpenGL|ES 2") set(BUILD_GLES2 OFF CACHE BOOL "Build with OpenGL|ES 2")
set(BUILD_GLES3 OFF CACHE BOOL "Build with OpenGL|ES 3")
set(USE_EPOXY ON CACHE STRING "Build with libepoxy") set(USE_EPOXY ON CACHE STRING "Build with libepoxy")
set(DISABLE_DEPS OFF CACHE BOOL "Build without dependencies") set(DISABLE_DEPS OFF CACHE BOOL "Build without dependencies")
set(DISTBUILD OFF CACHE BOOL "Build distribution packages") set(DISTBUILD OFF CACHE BOOL "Build distribution packages")
@ -93,7 +94,7 @@ source_group("Utilities" FILES ${UTIL_SRC})
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/src) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/src)
if(NOT CMAKE_BUILD_TYPE) if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type (e.g. Release or Debug)" FORCE) set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type (e.g. Release, RelWithDebInfo, or Debug)" FORCE)
endif() endif()
if(UNIX OR WIN32_UNIX_PATHS) if(UNIX OR WIN32_UNIX_PATHS)
@ -268,6 +269,14 @@ if(APPLE OR CMAKE_C_COMPILER_ID STREQUAL "GNU" AND BUILD_LTO)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -flto") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -flto")
endif() endif()
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
find_program(OBJCOPY ${cross_prefix}objcopy)
find_program(STRIP ${cross_prefix}strip)
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELEASE} -gdwarf")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE} -gdwarf")
endif()
if(BUILD_BBB OR BUILD_RASPI OR BUILD_PANDORA) if(BUILD_BBB OR BUILD_RASPI OR BUILD_PANDORA)
if(NOT BUILD_EGL) if(NOT BUILD_EGL)
add_definitions(-DCOLOR_16_BIT -DCOLOR_5_6_5) add_definitions(-DCOLOR_16_BIT -DCOLOR_5_6_5)
@ -301,7 +310,7 @@ if(DEFINED 3DS)
endif() endif()
if(DEFINED SWITCH) if(DEFINED SWITCH)
set(BUILD_GLES2 ON CACHE BOOL "Build with OpenGL|ES 2" FORCE) set(BUILD_GLES3 ON CACHE BOOL "Build with OpenGL|ES 3" FORCE)
endif() endif()
if(NOT M_CORE_GBA) if(NOT M_CORE_GBA)
@ -443,6 +452,13 @@ endif()
if(NOT BUILD_GLES2) if(NOT BUILD_GLES2)
set(OPENGLES2_LIBRARY "" CACHE PATH "" FORCE) set(OPENGLES2_LIBRARY "" CACHE PATH "" FORCE)
endif() endif()
if(BUILD_GLES3)
find_path(OPENGLES3_INCLUDE_DIR NAMES GLES3/gl3.h)
find_library(OPENGLES3_LIBRARY NAMES GLESv3 GLESv2)
if(NOT OPENGLES3_INCLUDE_DIR OR NOT OPENGLES3_LIBRARY)
set(BUILD_GLES3 OFF CACHE BOOL "OpenGL|ES 3 not found" FORCE)
endif()
endif()
set(WANT_ZLIB ${USE_ZLIB}) set(WANT_ZLIB ${USE_ZLIB})
set(WANT_PNG ${USE_PNG}) set(WANT_PNG ${USE_PNG})
set(WANT_LIBZIP ${USE_LIBZIP}) set(WANT_LIBZIP ${USE_LIBZIP})
@ -909,6 +925,10 @@ if(BUILD_GLES2)
add_definitions(-DBUILD_GLES2) add_definitions(-DBUILD_GLES2)
endif() endif()
if(BUILD_GLES3)
add_definitions(-DBUILD_GLES3)
endif()
if(DISABLE_FRONTENDS) if(DISABLE_FRONTENDS)
set(BUILD_SDL OFF) set(BUILD_SDL OFF)
set(BUILD_QT OFF) set(BUILD_QT OFF)
@ -1062,9 +1082,18 @@ set(CPACK_STRIP_FILES ON)
if(DISTBUILD) if(DISTBUILD)
set(CPACK_ARCHIVE_COMPONENT_INSTALL ON) set(CPACK_ARCHIVE_COMPONENT_INSTALL ON)
if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" AND BUILD_SHARED)
if(NOT APPLE)
add_custom_command(TARGET ${BINARY_NAME} POST_BUILD COMMAND "${OBJCOPY}" --only-keep-debug "$<TARGET_FILE:${BINARY_NAME}>" "$<TARGET_FILE:${BINARY_NAME}>.dSYM")
add_custom_command(TARGET ${BINARY_NAME} POST_BUILD COMMAND "${STRIP}" -S "$<TARGET_FILE:${BINARY_NAME}>")
install(FILES "$<TARGET_FILE:${BINARY_NAME}>.dSYM" DESTINATION ${LIBDIR} COMPONENT lib${BINARY_NAME}-dbg)
endif()
endif()
if(WIN32 OR APPLE) if(WIN32 OR APPLE)
set(CPACK_COMPONENTS_ALL ${BINARY_NAME} ${BINARY_NAME}-qt ${BINARY_NAME}-sdl ${BINARY_NAME}-perf) set(CPACK_COMPONENTS_ALL ${BINARY_NAME} ${BINARY_NAME}-qt ${BINARY_NAME}-sdl ${BINARY_NAME}-qt-dbg ${BINARY_NAME}-sdl-dbg ${BINARY_NAME}-perf)
if(APPLE)
set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE) set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE)
endif()
elseif(3DS) elseif(3DS)
set(CPACK_COMPONENTS_ALL ${BINARY_NAME} ${BINARY_NAME}-3ds ${BINARY_NAME}-perf) set(CPACK_COMPONENTS_ALL ${BINARY_NAME} ${BINARY_NAME}-3ds ${BINARY_NAME}-perf)
elseif(WII) elseif(WII)
@ -1111,6 +1140,19 @@ if(SDL_FOUND)
cpack_add_component(${BINARY_NAME}-sdl GROUP sdl) cpack_add_component(${BINARY_NAME}-sdl GROUP sdl)
endif() endif()
if(DISTBUILD AND CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
cpack_add_component_group(debug PARENT_GROUP dev)
if(BUILD_SHARED AND NOT IS_EMBEDDED)
cpack_add_component(lib${BINARY_NAME}-dbg GROUP debug)
endif()
if(BUILD_QT)
cpack_add_component(${BINARY_NAME}-qt-dbg GROUP debug)
endif()
if(SDL_FOUND)
cpack_add_component(${BINARY_NAME}-sdl-dbg GROUP debug)
endif()
endif()
cpack_add_component_group(test PARENT_GROUP dev) cpack_add_component_group(test PARENT_GROUP dev)
cpack_add_component(${BINARY_NAME}-perf GROUP test) cpack_add_component(${BINARY_NAME}-perf GROUP test)
cpack_add_component(${BINARY_NAME}-fuzz GROUP test) cpack_add_component(${BINARY_NAME}-fuzz GROUP test)
@ -1127,6 +1169,9 @@ else()
if(BUILD_GLES2) if(BUILD_GLES2)
list(APPEND SUMMARY_GL_LIST "OpenGL|ES 2") list(APPEND SUMMARY_GL_LIST "OpenGL|ES 2")
endif() endif()
if(BUILD_GLES3)
list(APPEND SUMMARY_GL_LIST "OpenGL|ES 3")
endif()
endif() endif()
if(NOT SUMMARY_GL_LIST) if(NOT SUMMARY_GL_LIST)
set(SUMMARY_GL OFF) set(SUMMARY_GL OFF)

View File

@ -17,6 +17,7 @@
.Op Fl l Ar loglevel .Op Fl l Ar loglevel
.Op Fl p Ar patchfile .Op Fl p Ar patchfile
.Op Fl s Ar n .Op Fl s Ar n
.Op Fl t Ar statefile
.Ar file .Ar file
.Sh DESCRIPTION .Sh DESCRIPTION
.Nm .Nm
@ -71,13 +72,7 @@ is a bitmask defining which types of messages to log:
.It .It
32 \(en stub messages for unimplemented features 32 \(en stub messages for unimplemented features
.It .It
256 \(en in\(hygame errors 64 \(en in\(hygame errors
.It
512 \(en software interrupts
.It
1024 \(en emulator status messages
.It
2048 \(en serial I/O messages
.El .El
The default is to log warnings, errors, fatal errors, and status messages. The default is to log warnings, errors, fatal errors, and status messages.
.It Fl p Ar patchfile , Fl -patch Ar patchfile .It Fl p Ar patchfile , Fl -patch Ar patchfile
@ -86,6 +81,9 @@ Specify a patch file in BPS, IPS, or UPS format.
Skip every Skip every
.Ar n .Ar n
frames. frames.
.It Fl t Ar statefile , Fl -savestate Ar statefile
Load initial game state from
.Ar statefile .
.El .El
.Sh CONTROLS .Sh CONTROLS
The default controls are as follows: The default controls are as follows:

View File

@ -18,7 +18,7 @@
.Op Fl l Ar loglevel .Op Fl l Ar loglevel
.Op Fl p Ar patchfile .Op Fl p Ar patchfile
.Op Fl s Ar n .Op Fl s Ar n
.Op Fl v Ar moviefile .Op Fl t Ar statefile
.Ar file .Ar file
.Sh DESCRIPTION .Sh DESCRIPTION
.Nm .Nm
@ -79,13 +79,7 @@ is a bitmask defining which types of messages to log:
.It .It
32 \(en stub messages for unimplemented features 32 \(en stub messages for unimplemented features
.It .It
256 \(en in\(hygame errors 64 \(en in\(hygame errors
.It
512 \(en software interrupts
.It
1024 \(en emulator status messages
.It
2048 \(en serial I/O messages
.El .El
The default is to log warnings, errors, fatal errors, and status messages. The default is to log warnings, errors, fatal errors, and status messages.
.It Fl p Ar patchfile , Fl -patch Ar patchfile .It Fl p Ar patchfile , Fl -patch Ar patchfile
@ -94,9 +88,9 @@ Specify a patch file in BPS, IPS, or UPS format.
Skip every Skip every
.Ar n .Ar n
frames. frames.
.It Fl v Ar moviefile , Fl -movie Ar moviefile .It Fl t Ar statefile , Fl -savestate Ar statefile
Play back a movie of recording input from Load initial game state from
.Ar moviefile . .Ar statefile .
.El .El
.Sh CONTROLS .Sh CONTROLS
The default controls are as follows: The default controls are as follows:

View File

@ -47,7 +47,8 @@ enum {
BATTERY_HIGH = 3, BATTERY_HIGH = 3,
BATTERY_FULL = 4, BATTERY_FULL = 4,
BATTERY_CHARGING = 8 BATTERY_CHARGING = 8,
BATTERY_NOT_PRESENT = 16
}; };
struct GUIBackground { struct GUIBackground {

View File

@ -20,6 +20,7 @@ struct RingFIFO {
void RingFIFOInit(struct RingFIFO* buffer, size_t capacity); void RingFIFOInit(struct RingFIFO* buffer, size_t capacity);
void RingFIFODeinit(struct RingFIFO* buffer); void RingFIFODeinit(struct RingFIFO* buffer);
size_t RingFIFOCapacity(const struct RingFIFO* buffer); size_t RingFIFOCapacity(const struct RingFIFO* buffer);
size_t RingFIFOSize(const struct RingFIFO* buffer);
void RingFIFOClear(struct RingFIFO* buffer); void RingFIFOClear(struct RingFIFO* buffer);
size_t RingFIFOWrite(struct RingFIFO* buffer, const void* value, size_t length); size_t RingFIFOWrite(struct RingFIFO* buffer, const void* value, size_t length);
size_t RingFIFORead(struct RingFIFO* buffer, void* output, size_t length); size_t RingFIFORead(struct RingFIFO* buffer, void* output, size_t length);

View File

@ -33,7 +33,6 @@ struct mCoreOptions {
int frameskip; int frameskip;
bool rewindEnable; bool rewindEnable;
int rewindBufferCapacity; int rewindBufferCapacity;
bool rewindSave;
float fpsTarget; float fpsTarget;
size_t audioBuffers; size_t audioBuffers;
unsigned sampleRate; unsigned sampleRate;

View File

@ -137,6 +137,9 @@ struct GBAudioNoiseChannel {
int length; int length;
uint32_t lfsr; uint32_t lfsr;
int nSamples;
int samples;
int8_t sample; int8_t sample;
}; };

View File

@ -177,6 +177,7 @@ void GBGetGameCode(const struct GB* gba, char* out);
void GBTestKeypadIRQ(struct GB* gb); void GBTestKeypadIRQ(struct GB* gb);
void GBFrameStarted(struct GB* gb);
void GBFrameEnded(struct GB* gb); void GBFrameEnded(struct GB* gb);
CXX_GUARD_END CXX_GUARD_END

View File

@ -102,9 +102,9 @@ enum GBIORegisters {
REG_UNK72 = 0x72, REG_UNK72 = 0x72,
REG_UNK73 = 0x73, REG_UNK73 = 0x73,
REG_UNK74 = 0x74, REG_UNK74 = 0x74,
REG_PCM12 = 0x75, REG_UNK75 = 0x75,
REG_PCM34 = 0x76, REG_PCM12 = 0x76,
REG_UNK77 = 0x77, REG_PCM34 = 0x77,
REG_MAX = 0x100 REG_MAX = 0x100
}; };

View File

@ -47,7 +47,7 @@ mLOG_DECLARE_CATEGORY(GB_STATE);
* | 0x00040 - 0x00043: Reserved (DI pending cycles) * | 0x00040 - 0x00043: Reserved (DI pending cycles)
* | 0x00044 - 0x00047: Flags * | 0x00044 - 0x00047: Flags
* | bit 0: Is condition met? * | bit 0: Is condition met?
* | bit 1: Is condition IRQ pending? * | bit 1: Is IRQ pending?
* | bit 2: Double speed * | bit 2: Double speed
* | bit 3: Is EI pending? * | bit 3: Is EI pending?
* | bits 4 - 31: Reserved * | bits 4 - 31: Reserved
@ -232,7 +232,7 @@ DECL_BITFIELD(GBSerializedCpuFlags, uint32_t);
DECL_BIT(GBSerializedCpuFlags, Condition, 0); DECL_BIT(GBSerializedCpuFlags, Condition, 0);
DECL_BIT(GBSerializedCpuFlags, IrqPending, 1); DECL_BIT(GBSerializedCpuFlags, IrqPending, 1);
DECL_BIT(GBSerializedCpuFlags, DoubleSpeed, 2); DECL_BIT(GBSerializedCpuFlags, DoubleSpeed, 2);
DECL_BIT(GBSerializedCpuFlags, EiPending, 1); DECL_BIT(GBSerializedCpuFlags, EiPending, 3);
DECL_BITFIELD(GBSerializedTimerFlags, uint8_t); DECL_BITFIELD(GBSerializedTimerFlags, uint8_t);
DECL_BIT(GBSerializedTimerFlags, IrqPending, 0); DECL_BIT(GBSerializedTimerFlags, IrqPending, 0);

View File

@ -1,7 +1,8 @@
Elijah Chondropoulos
Jaime J. Denizard Jaime J. Denizard
Fog Fog
Philip Horton Philip Horton
Oskenso Kashi
Rohit Nirmal Rohit Nirmal
Rhys Powell Rhys Powell
rootfather
Yuri Kunde Schlesner Yuri Kunde Schlesner

View File

@ -331,7 +331,6 @@ void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts)
_lookupIntValue(config, "frameskip", &opts->frameskip); _lookupIntValue(config, "frameskip", &opts->frameskip);
_lookupIntValue(config, "volume", &opts->volume); _lookupIntValue(config, "volume", &opts->volume);
_lookupIntValue(config, "rewindBufferCapacity", &opts->rewindBufferCapacity); _lookupIntValue(config, "rewindBufferCapacity", &opts->rewindBufferCapacity);
_lookupIntValue(config, "rewindSave", &opts->rewindSave);
_lookupFloatValue(config, "fpsTarget", &opts->fpsTarget); _lookupFloatValue(config, "fpsTarget", &opts->fpsTarget);
unsigned audioBuffers; unsigned audioBuffers;
if (_lookupUIntValue(config, "audioBuffers", &audioBuffers)) { if (_lookupUIntValue(config, "audioBuffers", &audioBuffers)) {
@ -391,7 +390,6 @@ void mCoreConfigLoadDefaults(struct mCoreConfig* config, const struct mCoreOptio
ConfigurationSetIntValue(&config->defaultsTable, 0, "frameskip", opts->frameskip); ConfigurationSetIntValue(&config->defaultsTable, 0, "frameskip", opts->frameskip);
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindEnable", opts->rewindEnable); ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindEnable", opts->rewindEnable);
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferCapacity", opts->rewindBufferCapacity); ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferCapacity", opts->rewindBufferCapacity);
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindSave", opts->rewindSave);
ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget); ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget);
ConfigurationSetUIntValue(&config->defaultsTable, 0, "audioBuffers", opts->audioBuffers); ConfigurationSetUIntValue(&config->defaultsTable, 0, "audioBuffers", opts->audioBuffers);
ConfigurationSetUIntValue(&config->defaultsTable, 0, "sampleRate", opts->sampleRate); ConfigurationSetUIntValue(&config->defaultsTable, 0, "sampleRate", opts->sampleRate);

View File

@ -359,7 +359,6 @@ bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags) {
} }
} }
if (flags & SAVESTATE_RTC) { if (flags & SAVESTATE_RTC) {
mLOG(SAVESTATE, INFO, "Loading RTC");
struct mStateExtdataItem item; struct mStateExtdataItem item;
if (core->rtc.d.serialize) { if (core->rtc.d.serialize) {
core->rtc.d.serialize(&core->rtc.d, &item); core->rtc.d.serialize(&core->rtc.d, &item);

View File

@ -211,7 +211,6 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
encoder->currentAudioSample = 0; encoder->currentAudioSample = 0;
encoder->currentAudioFrame = 0; encoder->currentAudioFrame = 0;
encoder->currentVideoFrame = 0; encoder->currentVideoFrame = 0;
encoder->nextAudioPts = 0;
AVOutputFormat* oformat = av_guess_format(encoder->containerFormat, 0, 0); AVOutputFormat* oformat = av_guess_format(encoder->containerFormat, 0, 0);
#ifndef USE_LIBAV #ifndef USE_LIBAV
@ -306,8 +305,8 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
encoder->video->bit_rate = encoder->videoBitrate; encoder->video->bit_rate = encoder->videoBitrate;
encoder->video->width = encoder->width; encoder->video->width = encoder->width;
encoder->video->height = encoder->height; encoder->video->height = encoder->height;
encoder->videoStream->time_base = (AVRational) { encoder->frameCycles, encoder->cycles }; encoder->video->time_base = (AVRational) { encoder->frameCycles, encoder->cycles };
encoder->video->time_base = encoder->videoStream->time_base; encoder->video->framerate = (AVRational) { encoder->cycles, encoder->frameCycles };
encoder->video->pix_fmt = encoder->pixFormat; encoder->video->pix_fmt = encoder->pixFormat;
encoder->video->gop_size = 60; encoder->video->gop_size = 60;
encoder->video->max_b_frames = 3; encoder->video->max_b_frames = 3;
@ -341,6 +340,10 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
encoder->video->pix_fmt = AV_PIX_FMT_YUV444P; encoder->video->pix_fmt = AV_PIX_FMT_YUV444P;
} }
} }
if (strcmp(vcodec->name, "libvpx-vp9") == 0 && encoder->videoBitrate == 0) {
av_opt_set(encoder->video->priv_data, "lossless", "1", 0);
encoder->video->pix_fmt = AV_PIX_FMT_YUV444P;
}
avcodec_open2(encoder->video, vcodec, 0); avcodec_open2(encoder->video, vcodec, 0);
#if LIBAVCODEC_VERSION_MAJOR >= 55 #if LIBAVCODEC_VERSION_MAJOR >= 55
@ -466,14 +469,13 @@ void _ffmpegPostAudioFrame(struct mAVStream* stream, int16_t left, int16_t right
(const uint8_t**) &encoder->audioBuffer, encoder->audioBufferSize / 4); (const uint8_t**) &encoder->audioBuffer, encoder->audioBufferSize / 4);
#endif #endif
encoder->audioFrame->pts = av_rescale_q(encoder->currentAudioFrame, encoder->audio->time_base, encoder->audioStream->time_base); encoder->audioFrame->pts = encoder->currentAudioFrame;
encoder->currentAudioFrame += samples; encoder->currentAudioFrame += samples;
AVPacket packet; AVPacket packet;
av_init_packet(&packet); av_init_packet(&packet);
packet.data = 0; packet.data = 0;
packet.size = 0; packet.size = 0;
packet.pts = encoder->audioFrame->pts;
int gotData; int gotData;
#ifdef FFMPEG_USE_PACKETS #ifdef FFMPEG_USE_PACKETS
@ -483,6 +485,9 @@ void _ffmpegPostAudioFrame(struct mAVStream* stream, int16_t left, int16_t right
#else #else
avcodec_encode_audio2(encoder->audio, &packet, encoder->audioFrame, &gotData); avcodec_encode_audio2(encoder->audio, &packet, encoder->audioFrame, &gotData);
#endif #endif
packet.pts = av_rescale_q(encoder->audioFrame->pts, encoder->audio->time_base, encoder->audioStream->time_base);
packet.dts = packet.pts;
if (gotData) { if (gotData) {
if (encoder->absf) { if (encoder->absf) {
AVPacket tempPacket; AVPacket tempPacket;
@ -541,7 +546,6 @@ void _ffmpegPostVideoFrame(struct mAVStream* stream, const color_t* pixels, size
av_frame_make_writable(encoder->videoFrame); av_frame_make_writable(encoder->videoFrame);
#endif #endif
encoder->videoFrame->pts = av_rescale_q(encoder->currentVideoFrame, encoder->video->time_base, encoder->videoStream->time_base); encoder->videoFrame->pts = av_rescale_q(encoder->currentVideoFrame, encoder->video->time_base, encoder->videoStream->time_base);
packet.pts = encoder->videoFrame->pts;
++encoder->currentVideoFrame; ++encoder->currentVideoFrame;
sws_scale(encoder->scaleContext, (const uint8_t* const*) &pixels, (const int*) &stride, 0, encoder->iheight, encoder->videoFrame->data, encoder->videoFrame->linesize); sws_scale(encoder->scaleContext, (const uint8_t* const*) &pixels, (const int*) &stride, 0, encoder->iheight, encoder->videoFrame->data, encoder->videoFrame->linesize);
@ -553,6 +557,7 @@ void _ffmpegPostVideoFrame(struct mAVStream* stream, const color_t* pixels, size
#else #else
avcodec_encode_video2(encoder->video, &packet, encoder->videoFrame, &gotData); avcodec_encode_video2(encoder->video, &packet, encoder->videoFrame, &gotData);
#endif #endif
packet.pts = encoder->videoFrame->pts;
if (gotData) { if (gotData) {
#ifndef FFMPEG_USE_PACKET_UNREF #ifndef FFMPEG_USE_PACKET_UNREF
if (encoder->video->coded_frame->key_frame) { if (encoder->video->coded_frame->key_frame) {

View File

@ -56,7 +56,6 @@ struct FFmpegEncoder {
AVFrame* audioFrame; AVFrame* audioFrame;
size_t currentAudioSample; size_t currentAudioSample;
int64_t currentAudioFrame; int64_t currentAudioFrame;
int64_t nextAudioPts; // TODO (0.6): Remove
#ifdef USE_LIBAVRESAMPLE #ifdef USE_LIBAVRESAMPLE
struct AVAudioResampleContext* resampleContext; struct AVAudioResampleContext* resampleContext;
#else #else

View File

@ -108,6 +108,16 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
.title = "Select SGB BIOS path", .title = "Select SGB BIOS path",
.data = "sgb.bios", .data = "sgb.bios",
}; };
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
.title = "Enable SGB borders",
.data = "sgb.borders",
.submenu = 0,
.state = true,
.validStates = (const char*[]) {
"Off", "On"
},
.nStates = 2
};
#endif #endif
size_t i; size_t i;
const char* mapNames[GUI_MAX_INPUTS + 1]; const char* mapNames[GUI_MAX_INPUTS + 1];

View File

@ -54,7 +54,8 @@ static const struct mInputPlatformInfo _mGUIKeyInfo = {
[mGUI_INPUT_DECREASE_BRIGHTNESS] = "Decrease solar brightness", [mGUI_INPUT_DECREASE_BRIGHTNESS] = "Decrease solar brightness",
[mGUI_INPUT_SCREEN_MODE] = "Screen mode", [mGUI_INPUT_SCREEN_MODE] = "Screen mode",
[mGUI_INPUT_SCREENSHOT] = "Take screenshot", [mGUI_INPUT_SCREENSHOT] = "Take screenshot",
[mGUI_INPUT_FAST_FORWARD] = "Fast forward", [mGUI_INPUT_FAST_FORWARD_HELD] = "Fast forward (held)",
[mGUI_INPUT_FAST_FORWARD_TOGGLE] = "Fast forward (toggle)",
}, },
.nKeys = GUI_INPUT_MAX .nKeys = GUI_INPUT_MAX
}; };
@ -412,6 +413,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
runner->lastFpsCheck = 1000000LL * tv.tv_sec + tv.tv_usec; runner->lastFpsCheck = 1000000LL * tv.tv_sec + tv.tv_usec;
int frame = 0; int frame = 0;
bool fastForward = false;
while (running) { while (running) {
if (runner->running) { if (runner->running) {
running = runner->running(runner); running = runner->running(runner);
@ -442,7 +444,10 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
mCoreTakeScreenshot(runner->core); mCoreTakeScreenshot(runner->core);
} }
if (runner->setFrameLimiter) { if (runner->setFrameLimiter) {
if (heldKeys & (1 << mGUI_INPUT_FAST_FORWARD)) { if (guiKeys & (1 << mGUI_INPUT_FAST_FORWARD_TOGGLE)) {
fastForward = !fastForward;
}
if (fastForward || (heldKeys & (1 << mGUI_INPUT_FAST_FORWARD_HELD))) {
runner->setFrameLimiter(runner, false); runner->setFrameLimiter(runner, false);
} else { } else {
runner->setFrameLimiter(runner, true); runner->setFrameLimiter(runner, true);

View File

@ -22,7 +22,8 @@ enum mGUIInput {
mGUI_INPUT_DECREASE_BRIGHTNESS, mGUI_INPUT_DECREASE_BRIGHTNESS,
mGUI_INPUT_SCREEN_MODE, mGUI_INPUT_SCREEN_MODE,
mGUI_INPUT_SCREENSHOT, mGUI_INPUT_SCREENSHOT,
mGUI_INPUT_FAST_FORWARD, mGUI_INPUT_FAST_FORWARD_HELD,
mGUI_INPUT_FAST_FORWARD_TOGGLE
}; };
struct mGUIBackground { struct mGUIBackground {

View File

@ -69,13 +69,13 @@ void mGUIRemapKeys(struct GUIParams* params, struct mInputMap* map, const struct
if (item->data == (void*) (GUI_INPUT_MAX + map->info->nKeys + 2)) { if (item->data == (void*) (GUI_INPUT_MAX + map->info->nKeys + 2)) {
for (i = 0; i < GUIMenuItemListSize(&menu.items); ++i) { for (i = 0; i < GUIMenuItemListSize(&menu.items); ++i) {
item = GUIMenuItemListGetPointer(&menu.items, i); item = GUIMenuItemListGetPointer(&menu.items, i);
if ((uint32_t) item->data < 1) { if ((uintptr_t) item->data < 1) {
continue; continue;
} }
if ((uint32_t) item->data < GUI_INPUT_MAX + 1) { if ((uintptr_t) item->data < GUI_INPUT_MAX + 1) {
mInputBindKey(&params->keyMap, keys->id, item->state - 1, (uint32_t) item->data - 1); mInputBindKey(&params->keyMap, keys->id, item->state - 1, (uintptr_t) item->data - 1);
} else if ((uint32_t) item->data < GUI_INPUT_MAX + map->info->nKeys + 1) { } else if ((uintptr_t) item->data < GUI_INPUT_MAX + map->info->nKeys + 1) {
mInputBindKey(map, keys->id, item->state - 1, (uint32_t) item->data - GUI_INPUT_MAX - 1); mInputBindKey(map, keys->id, item->state - 1, (uintptr_t) item->data - GUI_INPUT_MAX - 1);
} }
} }
break; break;

View File

@ -12,9 +12,6 @@ CXX_GUARD_START
#include <mgba/core/interface.h> #include <mgba/core/interface.h>
#define MAGICKCORE_HDRI_ENABLE 0
#define MAGICKCORE_QUANTUM_DEPTH 8
#if MAGICKWAND_VERSION_MAJOR >= 7 #if MAGICKWAND_VERSION_MAJOR >= 7
#include <MagickWand/MagickWand.h> #include <MagickWand/MagickWand.h>
#else #else

View File

@ -121,7 +121,7 @@ static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bo
if (!block || read) { if (!block || read) {
break; break;
} }
mLOG(GBA_VIDEO, DEBUG, "Proxy thread can't read VRAM. CPU thread asleep?"); mLOG(GBA_VIDEO, DEBUG, "Can't read %"PRIz"u bytes. CPU thread asleep?", length);
MutexLock(&proxyRenderer->mutex); MutexLock(&proxyRenderer->mutex);
ConditionWake(&proxyRenderer->fromThreadCond); ConditionWake(&proxyRenderer->fromThreadCond);
ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex); ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
@ -142,7 +142,7 @@ static void _wait(struct mVideoLogger* logger) {
_proxyThreadRecover(proxyRenderer); _proxyThreadRecover(proxyRenderer);
return; return;
} }
while (proxyRenderer->threadState == PROXY_THREAD_BUSY) { while (RingFIFOSize(&proxyRenderer->dirtyQueue)) {
ConditionWake(&proxyRenderer->toThreadCond); ConditionWake(&proxyRenderer->toThreadCond);
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex); ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
} }

View File

@ -258,6 +258,9 @@ void mVideoLoggerRendererFlush(struct mVideoLogger* logger) {
0xDEADBEEF, 0xDEADBEEF,
}; };
logger->writeData(logger, &dirty, sizeof(dirty)); logger->writeData(logger, &dirty, sizeof(dirty));
if (logger->wait) {
logger->wait(logger);
}
} }
void mVideoLoggerRendererFinishFrame(struct mVideoLogger* logger) { void mVideoLoggerRendererFinishFrame(struct mVideoLogger* logger) {

View File

@ -37,6 +37,8 @@ static bool _updateSweep(struct GBAudioSquareChannel* sweep, bool initial);
static void _updateSquareSample(struct GBAudioSquareChannel* ch); static void _updateSquareSample(struct GBAudioSquareChannel* ch);
static int32_t _updateSquareChannel(struct GBAudioSquareChannel* ch); static int32_t _updateSquareChannel(struct GBAudioSquareChannel* ch);
static int8_t _coalesceNoiseChannel(struct GBAudioNoiseChannel* ch);
static void _updateFrame(struct mTiming* timing, void* user, uint32_t cyclesLate); static void _updateFrame(struct mTiming* timing, void* user, uint32_t cyclesLate);
static void _updateChannel1(struct mTiming* timing, void* user, uint32_t cyclesLate); static void _updateChannel1(struct mTiming* timing, void* user, uint32_t cyclesLate);
static void _updateChannel2(struct mTiming* timing, void* user, uint32_t cyclesLate); static void _updateChannel2(struct mTiming* timing, void* user, uint32_t cyclesLate);
@ -93,7 +95,7 @@ void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52, enum GBAu
audio->sampleEvent.context = audio; audio->sampleEvent.context = audio;
audio->sampleEvent.name = "GB Audio Sample"; audio->sampleEvent.name = "GB Audio Sample";
audio->sampleEvent.callback = _sample; audio->sampleEvent.callback = _sample;
audio->ch1Event.priority = 0x18; audio->sampleEvent.priority = 0x18;
} }
void GBAudioDeinit(struct GBAudio* audio) { void GBAudioDeinit(struct GBAudio* audio) {
@ -118,6 +120,7 @@ void GBAudioReset(struct GBAudio* audio) {
audio->ch1 = (struct GBAudioSquareChannel) { .envelope = { .dead = 2 } }; audio->ch1 = (struct GBAudioSquareChannel) { .envelope = { .dead = 2 } };
audio->ch2 = (struct GBAudioSquareChannel) { .envelope = { .dead = 2 } }; audio->ch2 = (struct GBAudioSquareChannel) { .envelope = { .dead = 2 } };
audio->ch3 = (struct GBAudioWaveChannel) { .bank = 0 }; audio->ch3 = (struct GBAudioWaveChannel) { .bank = 0 };
audio->ch4 = (struct GBAudioNoiseChannel) { .nSamples = 0 };
// TODO: DMG randomness // TODO: DMG randomness
audio->ch3.wavedata8[0] = 0x00; audio->ch3.wavedata8[0] = 0x00;
audio->ch3.wavedata8[1] = 0xFF; audio->ch3.wavedata8[1] = 0xFF;
@ -203,11 +206,6 @@ void GBAudioWriteNR14(struct GBAudio* audio, uint8_t value) {
} }
if (GBAudioRegisterControlIsRestart(value << 8)) { if (GBAudioRegisterControlIsRestart(value << 8)) {
audio->playingCh1 = _resetEnvelope(&audio->ch1.envelope); audio->playingCh1 = _resetEnvelope(&audio->ch1.envelope);
if (audio->playingCh1) {
_updateSquareSample(&audio->ch1);
}
audio->ch1.sweep.realFrequency = audio->ch1.control.frequency; audio->ch1.sweep.realFrequency = audio->ch1.control.frequency;
_resetSweep(&audio->ch1.sweep); _resetSweep(&audio->ch1.sweep);
if (audio->playingCh1 && audio->ch1.sweep.shift) { if (audio->playingCh1 && audio->ch1.sweep.shift) {
@ -219,7 +217,9 @@ void GBAudioWriteNR14(struct GBAudio* audio, uint8_t value) {
--audio->ch1.control.length; --audio->ch1.control.length;
} }
} }
if (audio->playingCh1 && audio->ch1.envelope.dead != 2 && !mTimingIsScheduled(audio->timing, &audio->ch1Event)) { if (audio->playingCh1 && audio->ch1.envelope.dead != 2) {
_updateSquareChannel(&audio->ch1);
mTimingDeschedule(audio->timing, &audio->ch1Event);
mTimingSchedule(audio->timing, &audio->ch1Event, 0); mTimingSchedule(audio->timing, &audio->ch1Event, 0);
} }
} }
@ -260,17 +260,15 @@ void GBAudioWriteNR24(struct GBAudio* audio, uint8_t value) {
if (GBAudioRegisterControlIsRestart(value << 8)) { if (GBAudioRegisterControlIsRestart(value << 8)) {
audio->playingCh2 = _resetEnvelope(&audio->ch2.envelope); audio->playingCh2 = _resetEnvelope(&audio->ch2.envelope);
if (audio->playingCh2) {
_updateSquareSample(&audio->ch2);
}
if (!audio->ch2.control.length) { if (!audio->ch2.control.length) {
audio->ch2.control.length = 64; audio->ch2.control.length = 64;
if (audio->ch2.control.stop && !(audio->frame & 1)) { if (audio->ch2.control.stop && !(audio->frame & 1)) {
--audio->ch2.control.length; --audio->ch2.control.length;
} }
} }
if (audio->playingCh2 && audio->ch2.envelope.dead != 2 && !mTimingIsScheduled(audio->timing, &audio->ch2Event)) { if (audio->playingCh2 && audio->ch2.envelope.dead != 2) {
_updateSquareChannel(&audio->ch2);
mTimingDeschedule(audio->timing, &audio->ch2Event);
mTimingSchedule(audio->timing, &audio->ch2Event, 0); mTimingSchedule(audio->timing, &audio->ch2Event, 0);
} }
} }
@ -331,6 +329,7 @@ void GBAudioWriteNR34(struct GBAudio* audio, uint8_t value) {
} }
} }
audio->ch3.window = 0; audio->ch3.window = 0;
audio->ch3.sample = 0;
} }
mTimingDeschedule(audio->timing, &audio->ch3Fade); mTimingDeschedule(audio->timing, &audio->ch3Fade);
mTimingDeschedule(audio->timing, &audio->ch3Event); mTimingDeschedule(audio->timing, &audio->ch3Event);
@ -376,9 +375,9 @@ void GBAudioWriteNR44(struct GBAudio* audio, uint8_t value) {
audio->playingCh4 = _resetEnvelope(&audio->ch4.envelope); audio->playingCh4 = _resetEnvelope(&audio->ch4.envelope);
if (audio->ch4.power) { if (audio->ch4.power) {
audio->ch4.lfsr = 0x40; audio->ch4.lfsr = 0x7F;
} else { } else {
audio->ch4.lfsr = 0x4000; audio->ch4.lfsr = 0x7FFF;
} }
if (!audio->ch4.length) { if (!audio->ch4.length) {
audio->ch4.length = 64; audio->ch4.length = 64;
@ -386,7 +385,8 @@ void GBAudioWriteNR44(struct GBAudio* audio, uint8_t value) {
--audio->ch4.length; --audio->ch4.length;
} }
} }
if (audio->playingCh4 && audio->ch4.envelope.dead != 2 && !mTimingIsScheduled(audio->timing, &audio->ch4Event)) { if (audio->playingCh4 && audio->ch4.envelope.dead != 2) {
mTimingDeschedule(audio->timing, &audio->ch4Event);
mTimingSchedule(audio->timing, &audio->ch4Event, 0); mTimingSchedule(audio->timing, &audio->ch4Event, 0);
} }
} }
@ -563,11 +563,13 @@ void GBAudioUpdateFrame(struct GBAudio* audio, struct mTiming* timing) {
--audio->ch4.envelope.nextStep; --audio->ch4.envelope.nextStep;
if (audio->ch4.envelope.nextStep == 0) { if (audio->ch4.envelope.nextStep == 0) {
int8_t sample = audio->ch4.sample > 0; int8_t sample = audio->ch4.sample > 0;
audio->ch4.samples -= audio->ch4.sample;
_updateEnvelope(&audio->ch4.envelope); _updateEnvelope(&audio->ch4.envelope);
if (audio->ch4.envelope.dead == 2) { if (audio->ch4.envelope.dead == 2) {
mTimingDeschedule(timing, &audio->ch4Event); mTimingDeschedule(timing, &audio->ch4Event);
} }
audio->ch4.sample = sample * audio->ch4.envelope.currentVolume; audio->ch4.sample = sample * audio->ch4.envelope.currentVolume;
audio->ch4.samples += audio->ch4.sample;
} }
} }
break; break;
@ -575,47 +577,48 @@ void GBAudioUpdateFrame(struct GBAudio* audio, struct mTiming* timing) {
} }
void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right) { void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right) {
int dcOffset = audio->style == GB_AUDIO_GBA ? 0 : 0x8; int dcOffset = audio->style == GB_AUDIO_GBA ? 0 : -0x8;
int sampleLeft = dcOffset; int sampleLeft = dcOffset;
int sampleRight = dcOffset; int sampleRight = dcOffset;
if (audio->playingCh1 && !audio->forceDisableCh[0]) { if (audio->playingCh1 && !audio->forceDisableCh[0]) {
if (audio->ch1Left) { if (audio->ch1Left) {
sampleLeft -= audio->ch1.sample; sampleLeft += audio->ch1.sample;
} }
if (audio->ch1Right) { if (audio->ch1Right) {
sampleRight -= audio->ch1.sample; sampleRight += audio->ch1.sample;
} }
} }
if (audio->playingCh2 && !audio->forceDisableCh[1]) { if (audio->playingCh2 && !audio->forceDisableCh[1]) {
if (audio->ch2Left) { if (audio->ch2Left) {
sampleLeft -= audio->ch2.sample; sampleLeft += audio->ch2.sample;
} }
if (audio->ch2Right) { if (audio->ch2Right) {
sampleRight -= audio->ch2.sample; sampleRight += audio->ch2.sample;
} }
} }
if (audio->playingCh3 && !audio->forceDisableCh[2]) { if (audio->playingCh3 && !audio->forceDisableCh[2]) {
if (audio->ch3Left) { if (audio->ch3Left) {
sampleLeft -= audio->ch3.sample; sampleLeft += audio->ch3.sample;
} }
if (audio->ch3Right) { if (audio->ch3Right) {
sampleRight -= audio->ch3.sample; sampleRight += audio->ch3.sample;
} }
} }
if (audio->playingCh4 && !audio->forceDisableCh[3]) { if (audio->playingCh4 && !audio->forceDisableCh[3]) {
int8_t sample = _coalesceNoiseChannel(&audio->ch4);
if (audio->ch4Left) { if (audio->ch4Left) {
sampleLeft -= audio->ch4.sample; sampleLeft += sample;
} }
if (audio->ch4Right) { if (audio->ch4Right) {
sampleRight -= audio->ch4.sample; sampleRight += sample;
} }
} }
@ -631,8 +634,8 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
int16_t sampleLeft = 0; int16_t sampleLeft = 0;
int16_t sampleRight = 0; int16_t sampleRight = 0;
GBAudioSamplePSG(audio, &sampleLeft, &sampleRight); GBAudioSamplePSG(audio, &sampleLeft, &sampleRight);
sampleLeft = (sampleLeft * audio->masterVolume * 9) >> 7; sampleLeft = (sampleLeft * audio->masterVolume * 6) >> 7;
sampleRight = (sampleRight * audio->masterVolume * 9) >> 7; sampleRight = (sampleRight * audio->masterVolume * 6) >> 7;
mCoreSyncLockAudio(audio->p->sync); mCoreSyncLockAudio(audio->p->sync);
unsigned produced; unsigned produced;
@ -744,6 +747,17 @@ static int32_t _updateSquareChannel(struct GBAudioSquareChannel* ch) {
} }
} }
static int8_t _coalesceNoiseChannel(struct GBAudioNoiseChannel* ch) {
if (!ch->nSamples) {
return ch->sample;
}
// TODO keep track of timing
int8_t sample = ch->samples / ch->nSamples;
ch->nSamples = 0;
ch->samples = 0;
return sample;
}
static void _updateEnvelope(struct GBAudioEnvelope* envelope) { static void _updateEnvelope(struct GBAudioEnvelope* envelope) {
if (envelope->direction) { if (envelope->direction) {
++envelope->currentVolume; ++envelope->currentVolume;
@ -894,18 +908,17 @@ static void _updateChannel4(struct mTiming* timing, void* user, uint32_t cyclesL
struct GBAudio* audio = user; struct GBAudio* audio = user;
struct GBAudioNoiseChannel* ch = &audio->ch4; struct GBAudioNoiseChannel* ch = &audio->ch4;
int32_t baseCycles = ch->ratio ? 2 * ch->ratio : 1; int32_t cycles = ch->ratio ? 2 * ch->ratio : 1;
baseCycles <<= ch->frequency; cycles <<= ch->frequency;
baseCycles *= 8 * audio->timingFactor; cycles *= 8 * audio->timingFactor;
int32_t cycles = 0;
do {
int lsb = ch->lfsr & 1; int lsb = ch->lfsr & 1;
ch->sample = lsb * ch->envelope.currentVolume; ch->sample = lsb * ch->envelope.currentVolume;
++ch->nSamples;
ch->samples += ch->sample;
ch->lfsr >>= 1; ch->lfsr >>= 1;
ch->lfsr ^= (lsb * 0x60) << (ch->power ? 0 : 8); ch->lfsr ^= (lsb * 0x60) << (ch->power ? 0 : 8);
cycles += baseCycles;
} while (cycles + baseCycles < audio->sampleInterval);
mTimingSchedule(timing, &audio->ch4Event, cycles - cyclesLate); mTimingSchedule(timing, &audio->ch4Event, cycles - cyclesLate);
} }

View File

@ -252,9 +252,10 @@ void GBVideoProxyRendererFinishFrame(struct GBVideoRenderer* renderer) {
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
proxyRenderer->logger->lock(proxyRenderer->logger); proxyRenderer->logger->lock(proxyRenderer->logger);
proxyRenderer->logger->wait(proxyRenderer->logger);
} }
if (!proxyRenderer->logger->block) {
proxyRenderer->backend->finishFrame(proxyRenderer->backend); proxyRenderer->backend->finishFrame(proxyRenderer->backend);
}
mVideoLoggerRendererFinishFrame(proxyRenderer->logger); mVideoLoggerRendererFinishFrame(proxyRenderer->logger);
mVideoLoggerRendererFlush(proxyRenderer->logger); mVideoLoggerRendererFlush(proxyRenderer->logger);
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {

View File

@ -816,6 +816,18 @@ void GBGetGameCode(const struct GB* gb, char* out) {
} }
} }
void GBFrameStarted(struct GB* gb) {
GBTestKeypadIRQ(gb);
size_t c;
for (c = 0; c < mCoreCallbacksListSize(&gb->coreCallbacks); ++c) {
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gb->coreCallbacks, c);
if (callbacks->videoFrameStarted) {
callbacks->videoFrameStarted(callbacks->context);
}
}
}
void GBFrameEnded(struct GB* gb) { void GBFrameEnded(struct GB* gb) {
GBSramClean(gb, gb->video.frameCounter); GBSramClean(gb, gb->video.frameCounter);
@ -828,7 +840,21 @@ void GBFrameEnded(struct GB* gb) {
} }
} }
GBTestKeypadIRQ(gb); // TODO: Move to common code
if (gb->stream && gb->stream->postVideoFrame) {
const color_t* pixels;
size_t stride;
gb->video.renderer->getPixels(gb->video.renderer, &stride, (const void**) &pixels);
gb->stream->postVideoFrame(gb->stream, pixels, stride);
}
size_t c;
for (c = 0; c < mCoreCallbacksListSize(&gb->coreCallbacks); ++c) {
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gb->coreCallbacks, c);
if (callbacks->videoFrameEnded) {
callbacks->videoFrameEnded(callbacks->context);
}
}
} }
enum GBModel GBNameToModel(const char* model) { enum GBModel GBNameToModel(const char* model) {

View File

@ -95,17 +95,27 @@ void GBMBCSwitchHalfBank(struct GB* gb, int half, int bank) {
} }
static bool _isMulticart(const uint8_t* mem) { static bool _isMulticart(const uint8_t* mem) {
bool success = true; bool success;
struct VFile* vf; struct VFile* vf;
vf = VFileFromConstMemory(&mem[GB_SIZE_CART_BANK0 * 0x10], 1024); vf = VFileFromConstMemory(&mem[GB_SIZE_CART_BANK0 * 0x10], 1024);
success = success && GBIsROM(vf); success = GBIsROM(vf);
vf->close(vf); vf->close(vf);
if (!success) {
return false;
}
vf = VFileFromConstMemory(&mem[GB_SIZE_CART_BANK0 * 0x20], 1024); vf = VFileFromConstMemory(&mem[GB_SIZE_CART_BANK0 * 0x20], 1024);
success = success && GBIsROM(vf); success = GBIsROM(vf);
vf->close(vf); vf->close(vf);
if (!success) {
vf = VFileFromConstMemory(&mem[GB_SIZE_CART_BANK0 * 0x30], 1024);
success = GBIsROM(vf);
vf->close(vf);
}
return success; return success;
} }

View File

@ -193,7 +193,7 @@ static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum G
softwareRenderer->scx = 0; softwareRenderer->scx = 0;
softwareRenderer->wy = 0; softwareRenderer->wy = 0;
softwareRenderer->currentWy = 0; softwareRenderer->currentWy = 0;
softwareRenderer->lastY = 0; softwareRenderer->lastY = GB_VIDEO_VERTICAL_PIXELS;
softwareRenderer->hasWindow = false; softwareRenderer->hasWindow = false;
softwareRenderer->wx = 0; softwareRenderer->wx = 0;
softwareRenderer->model = model; softwareRenderer->model = model;

View File

@ -337,14 +337,6 @@ void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLat
return; return;
} }
size_t c;
for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) {
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c);
if (callbacks->videoFrameEnded) {
callbacks->videoFrameEnded(callbacks->context);
}
}
GBFrameEnded(video->p); GBFrameEnded(video->p);
mCoreSyncPostFrame(video->p->sync); mCoreSyncPostFrame(video->p->sync);
--video->frameskipCounter; --video->frameskipCounter;
@ -354,24 +346,10 @@ void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLat
} }
++video->frameCounter; ++video->frameCounter;
// TODO: Move to common code
if (video->p->stream && video->p->stream->postVideoFrame) {
const color_t* pixels;
size_t stride;
video->renderer->getPixels(video->renderer, &stride, (const void**) &pixels);
video->p->stream->postVideoFrame(video->p->stream, pixels, stride);
}
if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC])) { if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC])) {
mTimingSchedule(timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH); mTimingSchedule(timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH);
} }
GBFrameStarted(video->p);
for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) {
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c);
if (callbacks->videoFrameStarted) {
callbacks->videoFrameStarted(callbacks->context);
}
}
} }
static void _cleanOAM(struct GBVideo* video, int y) { static void _cleanOAM(struct GBVideo* video, int y) {

View File

@ -253,7 +253,7 @@ static int _applyBias(struct GBAAudio* audio, int sample) {
} else if (sample < 0) { } else if (sample < 0) {
sample = 0; sample = 0;
} }
return ((sample - GBARegisterSOUNDBIASGetBias(audio->soundbias)) * audio->masterVolume) >> 2; return ((sample - GBARegisterSOUNDBIASGetBias(audio->soundbias)) * audio->masterVolume * 3) >> 4;
} }
static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {

View File

@ -117,6 +117,7 @@ void GBAVideoProxyRendererInit(struct GBAVideoRenderer* renderer) {
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
_init(proxyRenderer); _init(proxyRenderer);
_reset(proxyRenderer);
proxyRenderer->backend->init(proxyRenderer->backend); proxyRenderer->backend->init(proxyRenderer->backend);
} }
@ -150,7 +151,7 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD
} }
break; break;
case DIRTY_OAM: case DIRTY_OAM:
if (item->address < SIZE_PALETTE_RAM) { if (item->address < SIZE_OAM) {
logger->oam[item->address] = item->value; logger->oam[item->address] = item->value;
proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address); proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address);
} }
@ -159,6 +160,8 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD
if (item->address <= SIZE_VRAM - 0x1000) { if (item->address <= SIZE_VRAM - 0x1000) {
logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true); logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true);
proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address); proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address);
} else {
logger->readData(logger, NULL, 0x1000, true);
} }
break; break;
case DIRTY_SCANLINE: case DIRTY_SCANLINE:
@ -267,9 +270,10 @@ void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
proxyRenderer->logger->lock(proxyRenderer->logger); proxyRenderer->logger->lock(proxyRenderer->logger);
proxyRenderer->logger->wait(proxyRenderer->logger);
} }
if (!proxyRenderer->logger->block) {
proxyRenderer->backend->finishFrame(proxyRenderer->backend); proxyRenderer->backend->finishFrame(proxyRenderer->backend);
}
mVideoLoggerRendererFinishFrame(proxyRenderer->logger); mVideoLoggerRendererFinishFrame(proxyRenderer->logger);
mVideoLoggerRendererFlush(proxyRenderer->logger); mVideoLoggerRendererFlush(proxyRenderer->logger);
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
@ -283,7 +287,6 @@ static void GBAVideoProxyRendererGetPixels(struct GBAVideoRenderer* renderer, si
proxyRenderer->logger->lock(proxyRenderer->logger); proxyRenderer->logger->lock(proxyRenderer->logger);
// Insert an extra item into the queue to make sure it gets flushed // Insert an extra item into the queue to make sure it gets flushed
mVideoLoggerRendererFlush(proxyRenderer->logger); mVideoLoggerRendererFlush(proxyRenderer->logger);
proxyRenderer->logger->wait(proxyRenderer->logger);
} }
proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels); proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels);
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
@ -297,7 +300,6 @@ static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, si
proxyRenderer->logger->lock(proxyRenderer->logger); proxyRenderer->logger->lock(proxyRenderer->logger);
// Insert an extra item into the queue to make sure it gets flushed // Insert an extra item into the queue to make sure it gets flushed
mVideoLoggerRendererFlush(proxyRenderer->logger); mVideoLoggerRendererFlush(proxyRenderer->logger);
proxyRenderer->logger->wait(proxyRenderer->logger);
} }
proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels); proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels);
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {

View File

@ -194,7 +194,9 @@ static void _drawStart(void) {
C3D_FrameBegin(flags); C3D_FrameBegin(flags);
ctrStartFrame(); ctrStartFrame();
C3D_FrameDrawOn(bottomScreen[doubleBuffer]);
C3D_RenderTargetClear(bottomScreen[doubleBuffer], C3D_CLEAR_COLOR, 0, 0); C3D_RenderTargetClear(bottomScreen[doubleBuffer], C3D_CLEAR_COLOR, 0, 0);
C3D_FrameDrawOn(topScreen[doubleBuffer]);
C3D_RenderTargetClear(topScreen[doubleBuffer], C3D_CLEAR_COLOR, 0, 0); C3D_RenderTargetClear(topScreen[doubleBuffer], C3D_CLEAR_COLOR, 0, 0);
} }

View File

@ -66,6 +66,8 @@ static struct mImageSource imageSource;
static uint32_t* camData = NULL; static uint32_t* camData = NULL;
static unsigned camWidth; static unsigned camWidth;
static unsigned camHeight; static unsigned camHeight;
static unsigned imcapWidth;
static unsigned imcapHeight;
static size_t camStride; static size_t camStride;
static void _reloadSettings(void) { static void _reloadSettings(void) {
@ -604,7 +606,9 @@ void retro_cheat_set(unsigned index, bool enabled, const char* code) {
cheatSet = device->createSet(device, NULL); cheatSet = device->createSet(device, NULL);
mCheatAddSet(device, cheatSet); mCheatAddSet(device, cheatSet);
} }
// Convert the super wonky unportable libretro format to something normal // Convert the super wonky unportable libretro format to something normal
#ifdef M_CORE_GBA
if (core->platform(core) == PLATFORM_GBA) {
char realCode[] = "XXXXXXXX XXXXXXXX"; char realCode[] = "XXXXXXXX XXXXXXXX";
size_t len = strlen(code) + 1; // Include null terminator size_t len = strlen(code) + 1; // Include null terminator
size_t i, pos; size_t i, pos;
@ -622,6 +626,29 @@ void retro_cheat_set(unsigned index, bool enabled, const char* code) {
} }
++pos; ++pos;
} }
}
#endif
#ifdef M_CORE_GB
if (core->platform(core) == PLATFORM_GB) {
char realCode[] = "XXX-XXX-XXX";
size_t len = strlen(code) + 1; // Include null terminator
size_t i, pos;
for (i = 0, pos = 0; i < len; ++i) {
if (isspace((int) code[i]) || code[i] == '+') {
realCode[pos] = '\0';
} else {
realCode[pos] = code[i];
}
if (pos == 11 || !realCode[pos]) {
realCode[pos] = '\0';
mCheatAddLine(cheatSet, realCode, 0);
pos = 0;
continue;
}
++pos;
}
}
#endif
} }
unsigned retro_get_region(void) { unsigned retro_get_region(void) {
@ -777,20 +804,40 @@ static uint8_t _readLux(struct GBALuminanceSource* lux) {
} }
static void _updateCamera(const uint32_t* buffer, unsigned width, unsigned height, size_t pitch) { static void _updateCamera(const uint32_t* buffer, unsigned width, unsigned height, size_t pitch) {
if (!camData || width != camWidth || height != camHeight) { if (!camData || width > camWidth || height > camHeight) {
camData = malloc(sizeof(*buffer) * height * pitch); if (camData) {
camWidth = width; free(camData);
camHeight = height; }
camStride = pitch / sizeof(*buffer); unsigned bufPitch = pitch / sizeof(*buffer);
unsigned bufHeight = height;
if (imcapWidth > bufPitch) {
bufPitch = imcapWidth;
}
if (imcapHeight > bufHeight) {
bufHeight = imcapHeight;
}
camData = malloc(sizeof(*buffer) * bufHeight * bufPitch);
memset(camData, 0xFF, sizeof(*buffer) * bufHeight * bufPitch);
camWidth = width;
camHeight = bufHeight;
camStride = bufPitch;
}
size_t i;
for (i = 0; i < height; ++i) {
memcpy(&camData[camStride * i], &buffer[pitch * i / sizeof(*buffer)], pitch);
} }
memcpy(camData, buffer, sizeof(*buffer) * height * pitch);
} }
static void _startImage(struct mImageSource* image, unsigned w, unsigned h, int colorFormats) { static void _startImage(struct mImageSource* image, unsigned w, unsigned h, int colorFormats) {
UNUSED(image); UNUSED(image);
UNUSED(colorFormats); UNUSED(colorFormats);
if (camData) {
free(camData);
}
camData = NULL; camData = NULL;
imcapWidth = w;
imcapHeight = h;
cam.start(); cam.start();
} }
@ -803,8 +850,18 @@ static void _requestImage(struct mImageSource* image, const void** buffer, size_
UNUSED(image); UNUSED(image);
if (!camData) { if (!camData) {
cam.start(); cam.start();
*buffer = NULL;
return;
} }
*buffer = camData; size_t offset = 0;
if (imcapWidth < camWidth) {
offset += (camWidth - imcapWidth) / 2;
}
if (imcapHeight < camHeight) {
offset += (camHeight - imcapHeight) / 2 * camStride;
}
*buffer = &camData[offset];
*stride = camStride; *stride = camStride;
*colorFormat = mCOLOR_XRGB8; *colorFormat = mCOLOR_XRGB8;
} }

View File

@ -97,18 +97,23 @@ void mPSP2MapKey(struct mInputMap* map, int pspKey, int key) {
static THREAD_ENTRY _audioThread(void* context) { static THREAD_ENTRY _audioThread(void* context) {
struct mPSP2AudioContext* audio = (struct mPSP2AudioContext*) context; struct mPSP2AudioContext* audio = (struct mPSP2AudioContext*) context;
uint32_t zeroBuffer[PSP2_SAMPLES] = {0}; uint32_t zeroBuffer[PSP2_SAMPLES] = {0};
void* buffer = zeroBuffer;
int audioPort = sceAudioOutOpenPort(SCE_AUDIO_OUT_PORT_TYPE_MAIN, PSP2_SAMPLES, 48000, SCE_AUDIO_OUT_MODE_STEREO); int audioPort = sceAudioOutOpenPort(SCE_AUDIO_OUT_PORT_TYPE_MAIN, PSP2_SAMPLES, 48000, SCE_AUDIO_OUT_MODE_STEREO);
while (audio->running) { while (audio->running) {
MutexLock(&audio->mutex); MutexLock(&audio->mutex);
void* buffer; if (buffer != zeroBuffer) {
// Can only happen in successive iterations
audio->samples -= PSP2_SAMPLES;
ConditionWake(&audio->cond);
}
if (audio->samples >= PSP2_SAMPLES) { if (audio->samples >= PSP2_SAMPLES) {
buffer = &audio->buffer[audio->readOffset]; buffer = &audio->buffer[audio->readOffset];
audio->samples -= PSP2_SAMPLES;
audio->readOffset += PSP2_SAMPLES; audio->readOffset += PSP2_SAMPLES;
if (audio->readOffset >= PSP2_AUDIO_BUFFER_SIZE) { if (audio->readOffset >= PSP2_AUDIO_BUFFER_SIZE) {
audio->readOffset = 0; audio->readOffset = 0;
} }
ConditionWake(&audio->cond); // Don't mark samples as read until the next loop iteration to prevent
// writing to the buffer while being read (see above)
} else { } else {
buffer = zeroBuffer; buffer = zeroBuffer;
} }
@ -280,6 +285,13 @@ uint16_t mPSP2PollInput(struct mGUIRunner* runner) {
void mPSP2SetFrameLimiter(struct mGUIRunner* runner, bool limit) { void mPSP2SetFrameLimiter(struct mGUIRunner* runner, bool limit) {
UNUSED(runner); UNUSED(runner);
if (!frameLimiter && limit) {
MutexLock(&audioContext.mutex);
while (audioContext.samples) {
ConditionWait(&audioContext.cond, &audioContext.mutex);
}
MutexUnlock(&audioContext.mutex);
}
frameLimiter = limit; frameLimiter = limit;
} }

View File

@ -271,7 +271,7 @@ endif()
qt5_wrap_ui(UI_SRC ${UI_FILES}) qt5_wrap_ui(UI_SRC ${UI_FILES})
add_executable(${BINARY_NAME}-qt WIN32 MACOSX_BUNDLE main.cpp ${CMAKE_SOURCE_DIR}/res/${BINARY_NAME}.icns ${SOURCE_FILES} ${PLATFORM_SRC} ${UI_SRC} ${AUDIO_SRC} ${RESOURCES}) add_executable(${BINARY_NAME}-qt WIN32 MACOSX_BUNDLE main.cpp ${CMAKE_SOURCE_DIR}/res/${BINARY_NAME}.icns ${SOURCE_FILES} ${PLATFORM_SRC} ${UI_SRC} ${AUDIO_SRC} ${RESOURCES})
set_target_properties(${BINARY_NAME}-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/res/info.plist.in COMPILE_DEFINITIONS "${FEATURE_DEFINES};${FUNCTION_DEFINES};${OS_DEFINES};${QT_DEFINES}") set_target_properties(${BINARY_NAME}-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/res/info.plist.in COMPILE_DEFINITIONS "${FEATURE_DEFINES};${FUNCTION_DEFINES};${OS_DEFINES};${QT_DEFINES}" COMPILE_OPTIONS "${FEATURE_FLAGS}")
list(APPEND QT_LIBRARIES Qt5::Widgets) list(APPEND QT_LIBRARIES Qt5::Widgets)
if(BUILD_GL OR BUILD_GLES2) if(BUILD_GL OR BUILD_GLES2)
@ -350,3 +350,11 @@ elseif(WIN32)
install(CODE "execute_process(COMMAND \"${BASH}\" \"${CMAKE_SOURCE_DIR}/tools/deploy-win.sh\" \"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.exe\" \"\${CMAKE_INSTALL_PREFIX}\" \"\$ENV{PWD}\" WORKING_DIRECTORY \"${CMAKE_BINARY_DIR}\")" COMPONENT ${BINARY_NAME}-qt) install(CODE "execute_process(COMMAND \"${BASH}\" \"${CMAKE_SOURCE_DIR}/tools/deploy-win.sh\" \"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.exe\" \"\${CMAKE_INSTALL_PREFIX}\" \"\$ENV{PWD}\" WORKING_DIRECTORY \"${CMAKE_BINARY_DIR}\")" COMPONENT ${BINARY_NAME}-qt)
endif() endif()
endif() endif()
if(DISTBUILD AND CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
if(NOT APPLE)
add_custom_command(TARGET ${BINARY_NAME}-qt POST_BUILD COMMAND "${OBJCOPY}" --only-keep-debug "$<TARGET_FILE:${BINARY_NAME}-qt>" "$<TARGET_FILE:${BINARY_NAME}-qt>.dSYM")
add_custom_command(TARGET ${BINARY_NAME}-qt POST_BUILD COMMAND "${STRIP}" -S "$<TARGET_FILE:${BINARY_NAME}-qt>")
install(FILES "$<TARGET_FILE:${BINARY_NAME}-qt>.dSYM" DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-qt-dbg)
endif()
endif()

View File

@ -46,6 +46,7 @@ CoreController::CoreController(mCore* core, QObject* parent)
m_buffers[0].fill(0xFF); m_buffers[0].fill(0xFF);
m_buffers[1].fill(0xFF); m_buffers[1].fill(0xFF);
m_activeBuffer = &m_buffers[0]; m_activeBuffer = &m_buffers[0];
m_completeBuffer = m_buffers[0];
m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer->data()), size.width()); m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer->data()), size.width());
@ -209,12 +210,9 @@ CoreController::~CoreController() {
m_threadContext.core->deinit(m_threadContext.core); m_threadContext.core->deinit(m_threadContext.core);
} }
color_t* CoreController::drawContext() { const color_t* CoreController::drawContext() {
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
if (!m_completeBuffer) { return reinterpret_cast<const color_t*>(m_completeBuffer.constData());
return nullptr;
}
return reinterpret_cast<color_t*>(m_completeBuffer->data());
} }
bool CoreController::isPaused() { bool CoreController::isPaused() {
@ -762,7 +760,7 @@ void CoreController::updateKeys() {
void CoreController::finishFrame() { void CoreController::finishFrame() {
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
m_completeBuffer = m_activeBuffer; memcpy(m_completeBuffer.data(), m_activeBuffer->constData(), m_activeBuffer->size());
// TODO: Generalize this to triple buffering? // TODO: Generalize this to triple buffering?
m_activeBuffer = &m_buffers[0]; m_activeBuffer = &m_buffers[0];
@ -770,7 +768,7 @@ void CoreController::finishFrame() {
m_activeBuffer = &m_buffers[1]; m_activeBuffer = &m_buffers[1];
} }
// Copy contents to avoid issues when doing frameskip // Copy contents to avoid issues when doing frameskip
memcpy(m_activeBuffer->data(), m_completeBuffer->data(), m_activeBuffer->size()); memcpy(m_activeBuffer->data(), m_completeBuffer.constData(), m_activeBuffer->size());
m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer->data()), screenDimensions().width()); m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer->data()), screenDimensions().width());
for (auto& action : m_frameActions) { for (auto& action : m_frameActions) {

View File

@ -58,7 +58,7 @@ public:
mCoreThread* thread() { return &m_threadContext; } mCoreThread* thread() { return &m_threadContext; }
color_t* drawContext(); const color_t* drawContext();
bool isPaused(); bool isPaused();
bool hasStarted(); bool hasStarted();
@ -170,7 +170,7 @@ private:
QByteArray m_buffers[2]; QByteArray m_buffers[2];
QByteArray* m_activeBuffer; QByteArray* m_activeBuffer;
QByteArray* m_completeBuffer = nullptr; QByteArray m_completeBuffer;
std::unique_ptr<mCacheSet> m_cacheSet; std::unique_ptr<mCacheSet> m_cacheSet;
std::unique_ptr<Override> m_override; std::unique_ptr<Override> m_override;

View File

@ -52,7 +52,7 @@ void DisplayQt::filter(bool filter) {
void DisplayQt::framePosted() { void DisplayQt::framePosted() {
update(); update();
color_t* buffer = m_context->drawContext(); const color_t* buffer = m_context->drawContext();
if (const_cast<const QImage&>(m_backing).bits() == reinterpret_cast<const uchar*>(buffer)) { if (const_cast<const QImage&>(m_backing).bits() == reinterpret_cast<const uchar*>(buffer)) {
return; return;
} }

View File

@ -51,6 +51,7 @@ VideoView::VideoView(QWidget* parent)
if (s_acodecMap.empty()) { if (s_acodecMap.empty()) {
s_acodecMap["mp3"] = "libmp3lame"; s_acodecMap["mp3"] = "libmp3lame";
s_acodecMap["opus"] = "libopus"; s_acodecMap["opus"] = "libopus";
s_acodecMap["vorbis"] = "libvorbis";
s_acodecMap["uncompressed"] = "pcm_s16le"; s_acodecMap["uncompressed"] = "pcm_s16le";
} }
if (s_vcodecMap.empty()) { if (s_vcodecMap.empty()) {
@ -101,7 +102,7 @@ VideoView::VideoView(QWidget* parent)
.container = "MKV", .container = "MKV",
.vcodec = "h.264", .vcodec = "h.264",
.acodec = "FLAC", .acodec = "FLAC",
.vbr = 0, .vbr = -1,
.abr = 0, .abr = 0,
.dims = QSize(), .dims = QSize(),
}); });
@ -170,8 +171,8 @@ void VideoView::updatePresets() {
addPreset(m_ui.presetWebM, { addPreset(m_ui.presetWebM, {
.container = "WebM", .container = "WebM",
.vcodec = "VP8", .vcodec = "VP9",
.acodec = "Vorbis", .acodec = "Opus",
.vbr = 800, .vbr = 800,
.abr = 128 .abr = 128
}); });
@ -181,7 +182,7 @@ void VideoView::updatePresets() {
.container = "MKV", .container = "MKV",
.vcodec = "h.264", .vcodec = "h.264",
.acodec = "FLAC", .acodec = "FLAC",
.vbr = 0, .vbr = -1,
.abr = 0, .abr = 0,
.dims = QSize(m_nativeWidth, m_nativeHeight) .dims = QSize(m_nativeWidth, m_nativeHeight)
}); });
@ -315,7 +316,7 @@ void VideoView::setAudioBitrate(int br, bool manual) {
} }
void VideoView::setVideoBitrate(int br, bool manual) { void VideoView::setVideoBitrate(int br, bool manual) {
m_vbr = br * 1000; m_vbr = br >= 0 ? br * 1000 : 0;
FFmpegEncoderSetVideo(&m_encoder, m_videoCodecCstr, m_vbr); FFmpegEncoderSetVideo(&m_encoder, m_videoCodecCstr, m_vbr);
validateSettings(); validateSettings();
if (manual) { if (manual) {

View File

@ -106,7 +106,7 @@
<item> <item>
<widget class="QRadioButton" name="presetHQ"> <widget class="QRadioButton" name="presetHQ">
<property name="text"> <property name="text">
<string>High Quality</string> <string>High &amp;Quality</string>
</property> </property>
<attribute name="buttonGroup"> <attribute name="buttonGroup">
<string notr="true">presets</string> <string notr="true">presets</string>
@ -116,7 +116,7 @@
<item> <item>
<widget class="QRadioButton" name="presetYoutube"> <widget class="QRadioButton" name="presetYoutube">
<property name="text"> <property name="text">
<string>YouTube</string> <string>&amp;YouTube</string>
</property> </property>
<attribute name="buttonGroup"> <attribute name="buttonGroup">
<string notr="true">presets</string> <string notr="true">presets</string>
@ -136,7 +136,7 @@
<item> <item>
<widget class="QRadioButton" name="presetLossless"> <widget class="QRadioButton" name="presetLossless">
<property name="text"> <property name="text">
<string>Lossless</string> <string>&amp;Lossless</string>
</property> </property>
<property name="checked"> <property name="checked">
<bool>true</bool> <bool>true</bool>
@ -153,7 +153,7 @@
<item> <item>
<widget class="QRadioButton" name="preset1080"> <widget class="QRadioButton" name="preset1080">
<property name="text"> <property name="text">
<string>1080p</string> <string>&amp;1080p</string>
</property> </property>
<attribute name="buttonGroup"> <attribute name="buttonGroup">
<string notr="true">resolutions</string> <string notr="true">resolutions</string>
@ -163,7 +163,7 @@
<item> <item>
<widget class="QRadioButton" name="preset720"> <widget class="QRadioButton" name="preset720">
<property name="text"> <property name="text">
<string>720p</string> <string>&amp;720p</string>
</property> </property>
<attribute name="buttonGroup"> <attribute name="buttonGroup">
<string notr="true">resolutions</string> <string notr="true">resolutions</string>
@ -173,7 +173,7 @@
<item> <item>
<widget class="QRadioButton" name="preset480"> <widget class="QRadioButton" name="preset480">
<property name="text"> <property name="text">
<string>480p</string> <string>&amp;480p</string>
</property> </property>
<attribute name="buttonGroup"> <attribute name="buttonGroup">
<string notr="true">resolutions</string> <string notr="true">resolutions</string>
@ -186,7 +186,7 @@
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Native</string> <string>&amp;Native</string>
</property> </property>
<property name="checked"> <property name="checked">
<bool>true</bool> <bool>true</bool>
@ -274,6 +274,11 @@
<string>VP8</string> <string>VP8</string>
</property> </property>
</item> </item>
<item>
<property name="text">
<string>VP9</string>
</property>
</item>
<item> <item>
<property name="text"> <property name="text">
<string>FFV1</string> <string>FFV1</string>

View File

@ -105,3 +105,11 @@ install(TARGETS ${BINARY_NAME}-sdl DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT
if(UNIX) if(UNIX)
install(FILES ${CMAKE_SOURCE_DIR}/doc/${BINARY_NAME}.6 DESTINATION ${MANDIR}/man6 COMPONENT ${BINARY_NAME}-sdl) install(FILES ${CMAKE_SOURCE_DIR}/doc/${BINARY_NAME}.6 DESTINATION ${MANDIR}/man6 COMPONENT ${BINARY_NAME}-sdl)
endif() endif()
if(DISTBUILD AND CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
if(NOT APPLE)
add_custom_command(TARGET ${BINARY_NAME}-sdl POST_BUILD COMMAND "${OBJCOPY}" --only-keep-debug "$<TARGET_FILE:${BINARY_NAME}-sdl>" "$<TARGET_FILE:${BINARY_NAME}-sdl>.dSYM")
add_custom_command(TARGET ${BINARY_NAME}-sdl POST_BUILD COMMAND "${STRIP}" -S "$<TARGET_FILE:${BINARY_NAME}-sdl>")
install(FILES "$<TARGET_FILE:${BINARY_NAME}-sdl>.dSYM" DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-sdl-dbg)
endif()
endif()

View File

@ -57,7 +57,6 @@ int main(int argc, char** argv) {
.useBios = true, .useBios = true,
.rewindEnable = true, .rewindEnable = true,
.rewindBufferCapacity = 600, .rewindBufferCapacity = 600,
.rewindSave = true,
.audioBuffers = 1024, .audioBuffers = 1024,
.videoSync = false, .videoSync = false,
.audioSync = true, .audioSync = true,

View File

@ -8,7 +8,7 @@ set(OS_DEFINES USE_VFS_FILE IOAPI_NO_64)
list(APPEND CORE_VFS_SRC ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-file.c ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-dirent.c) list(APPEND CORE_VFS_SRC ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-file.c ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-dirent.c)
list(APPEND GUI_SRC ${CMAKE_CURRENT_SOURCE_DIR}/gui-font.c) list(APPEND GUI_SRC ${CMAKE_CURRENT_SOURCE_DIR}/gui-font.c)
include_directories(AFTER ${OPENGLES2_INCLUDE_DIR} ${OPENGL_EGL_INCLUDE_DIR}) include_directories(AFTER ${OPENGLES3_INCLUDE_DIR} ${OPENGL_EGL_INCLUDE_DIR})
file(GLOB OS_SRC ${CMAKE_SOURCE_DIR}/src/platform/wii/wii-*.c) file(GLOB OS_SRC ${CMAKE_SOURCE_DIR}/src/platform/wii/wii-*.c)
if(${CMAKE_BUILD_TYPE} STREQUAL Debug OR ${CMAKE_BUILD_TYPE} STREQUAL RelWithDebInfo) if(${CMAKE_BUILD_TYPE} STREQUAL Debug OR ${CMAKE_BUILD_TYPE} STREQUAL RelWithDebInfo)
@ -34,7 +34,7 @@ endif()
add_executable(${BINARY_NAME}.elf ${GUI_SRC} ${PLATFORM_SRC} main.c) add_executable(${BINARY_NAME}.elf ${GUI_SRC} ${PLATFORM_SRC} main.c)
set_target_properties(${BINARY_NAME}.elf PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}") set_target_properties(${BINARY_NAME}.elf PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
target_link_libraries(${BINARY_NAME}.elf ${BINARY_NAME} ${M_LIBRARY} ${EGL_LIBRARY} ${OPENGLES2_LIBRARY} ${GLAPI_LIBRARY} ${NOUVEAU_LIBRARY} stdc++ ${OS_LIB}) target_link_libraries(${BINARY_NAME}.elf ${BINARY_NAME} ${M_LIBRARY} ${EGL_LIBRARY} ${OPENGLES3_LIBRARY} ${GLAPI_LIBRARY} ${NOUVEAU_LIBRARY} stdc++ ${OS_LIB})
add_custom_command(OUTPUT control.nacp add_custom_command(OUTPUT control.nacp
COMMAND ${NACPTOOL} --create "${PROJECT_NAME}" "endrift" "${VERSION_STRING}" control.nacp) COMMAND ${NACPTOOL} --create "${PROJECT_NAME}" "endrift" "${VERSION_STRING}" control.nacp)

View File

@ -9,7 +9,7 @@
#include <mgba-util/string.h> #include <mgba-util/string.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
#include <GLES2/gl2.h> #include <GLES3/gl3.h>
#define GLYPH_HEIGHT 24 #define GLYPH_HEIGHT 24
#define CELL_HEIGHT 32 #define CELL_HEIGHT 32
@ -58,7 +58,7 @@ struct GUIFont {
GLuint font; GLuint font;
GLuint program; GLuint program;
GLuint vbo; GLuint vbo;
GLuint offsetLocation; GLuint vao;
GLuint texLocation; GLuint texLocation;
GLuint dimsLocation; GLuint dimsLocation;
GLuint transformLocation; GLuint transformLocation;
@ -166,12 +166,16 @@ struct GUIFont* GUIFontCreate(void) {
font->originLocation = glGetUniformLocation(font->program, "origin"); font->originLocation = glGetUniformLocation(font->program, "origin");
font->glyphLocation = glGetUniformLocation(font->program, "glyph"); font->glyphLocation = glGetUniformLocation(font->program, "glyph");
font->cutoffLocation = glGetUniformLocation(font->program, "cutoff"); font->cutoffLocation = glGetUniformLocation(font->program, "cutoff");
font->offsetLocation = glGetAttribLocation(font->program, "offset"); GLuint offsetLocation = glGetAttribLocation(font->program, "offset");
glGenBuffers(1, &font->vbo); glGenBuffers(1, &font->vbo);
glGenVertexArrays(1, &font->vao);
glBindVertexArray(font->vao);
glBindBuffer(GL_ARRAY_BUFFER, font->vbo); glBindBuffer(GL_ARRAY_BUFFER, font->vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(_offsets), _offsets, GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, sizeof(_offsets), _offsets, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0); glVertexAttribPointer(offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(offsetLocation);
glBindVertexArray(0);
return font; return font;
} }
@ -180,6 +184,7 @@ void GUIFontDestroy(struct GUIFont* font) {
glDeleteBuffers(1, &font->vbo); glDeleteBuffers(1, &font->vbo);
glDeleteProgram(font->program); glDeleteProgram(font->program);
glDeleteTextures(1, &font->font); glDeleteTextures(1, &font->font);
glDeleteVertexArrays(1, &font->vao);
free(font); free(font);
} }
@ -222,9 +227,9 @@ void GUIFontDrawGlyph(const struct GUIFont* font, int x, int y, uint32_t color,
struct GUIFontGlyphMetric metric = defaultFontMetrics[glyph]; struct GUIFontGlyphMetric metric = defaultFontMetrics[glyph];
glUseProgram(font->program); glUseProgram(font->program);
glBindVertexArray(font->vao);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, font->font); glBindTexture(GL_TEXTURE_2D, font->font);
glBindBuffer(GL_ARRAY_BUFFER, font->vbo);
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@ -235,19 +240,15 @@ void GUIFontDrawGlyph(const struct GUIFont* font, int x, int y, uint32_t color,
glUniform3f(font->originLocation, x, y - GLYPH_HEIGHT + metric.padding.top * 2, 0); glUniform3f(font->originLocation, x, y - GLYPH_HEIGHT + metric.padding.top * 2, 0);
glUniformMatrix2fv(font->transformLocation, 1, GL_FALSE, (float[4]) {1.0, 0.0, 0.0, 1.0}); glUniformMatrix2fv(font->transformLocation, 1, GL_FALSE, (float[4]) {1.0, 0.0, 0.0, 1.0});
glVertexAttribPointer(font->offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(font->offsetLocation);
glUniform1f(font->cutoffLocation, 0.1f); glUniform1f(font->cutoffLocation, 0.1f);
glUniform4f(font->colorLocation, 0.0, 0.0, 0.0, ((color >> 24) & 0xFF) / 128.0f); glUniform4f(font->colorLocation, 0.0, 0.0, 0.0, ((color >> 24) & 0xFF) / 128.0f);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glUniform1f(font->cutoffLocation, 0.7f); glUniform1f(font->cutoffLocation, 0.7f);
glUniform4f(font->colorLocation, ((color >> 16) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, (color & 0xFF) / 255.0f, ((color >> 24) & 0xFF) / 255.0f); glUniform4f(font->colorLocation, (color & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, ((color >> 16) & 0xFF) / 255.0f, ((color >> 24) & 0xFF) / 255.0f);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisableVertexAttribArray(font->offsetLocation); glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUseProgram(0); glUseProgram(0);
} }
@ -291,9 +292,9 @@ void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment
} }
glUseProgram(font->program); glUseProgram(font->program);
glBindVertexArray(font->vao);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, font->font); glBindTexture(GL_TEXTURE_2D, font->font);
glBindBuffer(GL_ARRAY_BUFFER, font->vbo);
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@ -304,19 +305,15 @@ void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment
glUniform3f(font->originLocation, x, y, 0); glUniform3f(font->originLocation, x, y, 0);
glUniformMatrix2fv(font->transformLocation, 1, GL_FALSE, (float[4]) {hFlip, 0.0, 0.0, vFlip}); glUniformMatrix2fv(font->transformLocation, 1, GL_FALSE, (float[4]) {hFlip, 0.0, 0.0, vFlip});
glVertexAttribPointer(font->offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(font->offsetLocation);
glUniform1f(font->cutoffLocation, 0.1f); glUniform1f(font->cutoffLocation, 0.1f);
glUniform4f(font->colorLocation, 0.0, 0.0, 0.0, ((color >> 24) & 0xFF) / 128.0f); glUniform4f(font->colorLocation, 0.0, 0.0, 0.0, ((color >> 24) & 0xFF) / 128.0f);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glUniform1f(font->cutoffLocation, 0.7f); glUniform1f(font->cutoffLocation, 0.7f);
glUniform4f(font->colorLocation, ((color >> 16) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, (color & 0xFF) / 255.0f, ((color >> 24) & 0xFF) / 255.0f); glUniform4f(font->colorLocation, (color & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, ((color >> 16) & 0xFF) / 255.0f, ((color >> 24) & 0xFF) / 255.0f);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisableVertexAttribArray(font->offsetLocation); glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUseProgram(0); glUseProgram(0);
} }
@ -334,9 +331,9 @@ void GUIFontDrawIconSize(const struct GUIFont* font, int x, int y, int w, int h,
} }
glUseProgram(font->program); glUseProgram(font->program);
glBindVertexArray(font->vao);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, font->font); glBindTexture(GL_TEXTURE_2D, font->font);
glBindBuffer(GL_ARRAY_BUFFER, font->vbo);
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@ -347,9 +344,6 @@ void GUIFontDrawIconSize(const struct GUIFont* font, int x, int y, int w, int h,
glUniform3f(font->originLocation, x + w / 2 - metric.width, y + h / 2 - metric.height, 0); glUniform3f(font->originLocation, x + w / 2 - metric.width, y + h / 2 - metric.height, 0);
glUniformMatrix2fv(font->transformLocation, 1, GL_FALSE, (float[4]) {w * 0.5f / metric.width, 0.0, 0.0, h * 0.5f / metric.height}); glUniformMatrix2fv(font->transformLocation, 1, GL_FALSE, (float[4]) {w * 0.5f / metric.width, 0.0, 0.0, h * 0.5f / metric.height});
glVertexAttribPointer(font->offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(font->offsetLocation);
glUniform1f(font->cutoffLocation, 0.1f); glUniform1f(font->cutoffLocation, 0.1f);
glUniform4f(font->colorLocation, 0.0, 0.0, 0.0, ((color >> 24) & 0xFF) / 128.0f); glUniform4f(font->colorLocation, 0.0, 0.0, 0.0, ((color >> 24) & 0xFF) / 128.0f);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
@ -358,7 +352,6 @@ void GUIFontDrawIconSize(const struct GUIFont* font, int x, int y, int w, int h,
glUniform4f(font->colorLocation, ((color >> 16) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, (color & 0xFF) / 255.0f, ((color >> 24) & 0xFF) / 255.0f); glUniform4f(font->colorLocation, ((color >> 16) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, (color & 0xFF) / 255.0f, ((color >> 24) & 0xFF) / 255.0f);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisableVertexAttribArray(font->offsetLocation); glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUseProgram(0); glUseProgram(0);
} }

View File

@ -10,15 +10,17 @@
#include <mgba/internal/gba/input.h> #include <mgba/internal/gba/input.h>
#include <mgba-util/gui.h> #include <mgba-util/gui.h>
#include <mgba-util/gui/font.h> #include <mgba-util/gui/font.h>
#include <mgba-util/gui/menu.h>
#include <switch.h> #include <switch.h>
#include <EGL/egl.h> #include <EGL/egl.h>
#include <GLES2/gl2.h> #include <GLES3/gl3.h>
#define AUTO_INPUT 0x4E585031 #define AUTO_INPUT 0x4E585031
#define SAMPLES 0x400 #define SAMPLES 0x400
#define BUFFER_SIZE 0x1000 #define BUFFER_SIZE 0x1000
#define N_BUFFERS 4 #define N_BUFFERS 4
#define ANALOG_DEADZONE 0x4000
TimeType __nx_time_type = TimeType_UserSystemClock; TimeType __nx_time_type = TimeType_UserSystemClock;
@ -63,21 +65,23 @@ static const char* const _fragmentShader =
static GLuint program; static GLuint program;
static GLuint vbo; static GLuint vbo;
static GLuint offsetLocation; static GLuint vao;
static GLuint pbo;
static GLuint texLocation; static GLuint texLocation;
static GLuint dimsLocation; static GLuint dimsLocation;
static GLuint insizeLocation; static GLuint insizeLocation;
static GLuint colorLocation; static GLuint colorLocation;
static GLuint tex; static GLuint tex;
static color_t frameBuffer[256 * 256]; static color_t* frameBuffer;
static struct mAVStream stream; static struct mAVStream stream;
static int audioBufferActive; static int audioBufferActive;
static struct GBAStereoSample audioBuffer[N_BUFFERS][SAMPLES] __attribute__((__aligned__(0x1000))); static struct GBAStereoSample audioBuffer[N_BUFFERS][SAMPLES] __attribute__((__aligned__(0x1000)));
static AudioOutBuffer audoutBuffer[N_BUFFERS]; static AudioOutBuffer audoutBuffer[N_BUFFERS];
static int enqueuedBuffers; static int enqueuedBuffers;
static bool frameLimiter = true; static bool frameLimiter = true;
static int framecount = 0; static unsigned framecount = 0;
static unsigned framecap = 10;
static bool initEgl() { static bool initEgl() {
s_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); s_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
@ -105,11 +109,11 @@ static bool initEgl() {
goto _fail1; goto _fail1;
} }
//EGLint contextAttributeList[] = { EGLint contextAttributeList[] = {
// EGL_CONTEXT_CLIENT_VERSION, 2, EGL_CONTEXT_CLIENT_VERSION, 3,
// EGL_NONE EGL_NONE
//}; };
s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, NULL); s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, contextAttributeList);
if (!s_context) { if (!s_context) {
goto _fail2; goto _fail2;
} }
@ -144,12 +148,13 @@ static void _mapKey(struct mInputMap* map, uint32_t binding, int nativeKey, enum
} }
static void _drawStart(void) { static void _drawStart(void) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
} }
static void _drawEnd(void) { static void _drawEnd(void) {
if (frameLimiter || (framecount & 3) == 0) { if (frameLimiter || framecount >= framecap) {
eglSwapBuffers(s_display, s_surface); eglSwapBuffers(s_display, s_surface);
framecount = 0;
} }
} }
@ -158,6 +163,40 @@ static uint32_t _pollInput(const struct mInputMap* map) {
hidScanInput(); hidScanInput();
u32 padkeys = hidKeysHeld(CONTROLLER_P1_AUTO); u32 padkeys = hidKeysHeld(CONTROLLER_P1_AUTO);
keys |= mInputMapKeyBits(map, AUTO_INPUT, padkeys, 0); keys |= mInputMapKeyBits(map, AUTO_INPUT, padkeys, 0);
JoystickPosition jspos;
hidJoystickRead(&jspos, CONTROLLER_P1_AUTO, JOYSTICK_LEFT);
int l = mInputMapKey(map, AUTO_INPUT, __builtin_ctz(KEY_LSTICK_LEFT));
int r = mInputMapKey(map, AUTO_INPUT, __builtin_ctz(KEY_LSTICK_RIGHT));
int u = mInputMapKey(map, AUTO_INPUT, __builtin_ctz(KEY_LSTICK_UP));
int d = mInputMapKey(map, AUTO_INPUT, __builtin_ctz(KEY_LSTICK_DOWN));
if (l == -1) {
l = mInputMapKey(map, AUTO_INPUT, __builtin_ctz(KEY_DLEFT));
}
if (r == -1) {
r = mInputMapKey(map, AUTO_INPUT, __builtin_ctz(KEY_DRIGHT));
}
if (u == -1) {
u = mInputMapKey(map, AUTO_INPUT, __builtin_ctz(KEY_DUP));
}
if (d == -1) {
d = mInputMapKey(map, AUTO_INPUT, __builtin_ctz(KEY_DDOWN));
}
if (jspos.dx < -ANALOG_DEADZONE && l != -1) {
keys |= 1 << l;
}
if (jspos.dx > ANALOG_DEADZONE && r != -1) {
keys |= 1 << r;
}
if (jspos.dy < -ANALOG_DEADZONE && d != -1) {
keys |= 1 << d;
}
if (jspos.dy > ANALOG_DEADZONE && u != -1) {
keys |= 1 << u;
}
return keys; return keys;
} }
@ -196,15 +235,16 @@ static void _gameLoaded(struct mGUIRunner* runner) {
double ratio = GBAAudioCalculateRatio(1, 60.0, 1); double ratio = GBAAudioCalculateRatio(1, 60.0, 1);
blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), samplerate * ratio); blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), samplerate * ratio);
blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), samplerate * ratio); blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), samplerate * ratio);
mCoreConfigGetUIntValue(&runner->config, "fastForwardCap", &framecap);
} }
static void _drawTex(struct mGUIRunner* runner, unsigned width, unsigned height, bool faded) { static void _drawTex(struct mGUIRunner* runner, unsigned width, unsigned height, bool faded) {
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glUseProgram(program); glUseProgram(program);
glBindVertexArray(vao);
float aspectX = width / (float) runner->params.width; float aspectX = width / (float) runner->params.width;
float aspectY = height / (float) runner->params.height; float aspectY = height / (float) runner->params.height;
float max; float max;
@ -226,26 +266,39 @@ static void _drawTex(struct mGUIRunner* runner, unsigned width, unsigned height,
glUniform4f(colorLocation, 0.8f, 0.8f, 0.8f, 0.8f); glUniform4f(colorLocation, 0.8f, 0.8f, 0.8f, 0.8f);
} }
glVertexAttribPointer(offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(offsetLocation);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisableVertexAttribArray(offsetLocation); glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUseProgram(0); glUseProgram(0);
} }
static void _prepareForFrame(struct mGUIRunner* runner) {
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
frameBuffer = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, 256 * 256 * 4, GL_MAP_WRITE_BIT);
if (frameBuffer) {
runner->core->setVideoBuffer(runner->core, frameBuffer, 256);
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
static void _drawFrame(struct mGUIRunner* runner, bool faded) { static void _drawFrame(struct mGUIRunner* runner, bool faded) {
glActiveTexture(GL_TEXTURE0); ++framecount;
glBindTexture(GL_TEXTURE_2D, tex); if (!frameLimiter && framecount < framecap) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, frameBuffer); return;
}
unsigned width, height; unsigned width, height;
runner->core->desiredVideoDimensions(runner->core, &width, &height); runner->core->desiredVideoDimensions(runner->core, &width, &height);
_drawTex(runner, width, height, faded);
++framecount; glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, height, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
_drawTex(runner, width, height, faded);
} }
static void _drawScreenshot(struct mGUIRunner* runner, const color_t* pixels, unsigned width, unsigned height, bool faded) { static void _drawScreenshot(struct mGUIRunner* runner, const color_t* pixels, unsigned width, unsigned height, bool faded) {
@ -257,11 +310,7 @@ static void _drawScreenshot(struct mGUIRunner* runner, const color_t* pixels, un
} }
static uint16_t _pollGameInput(struct mGUIRunner* runner) { static uint16_t _pollGameInput(struct mGUIRunner* runner) {
int keys = 0; return _pollInput(&runner->core->inputMap);
hidScanInput();
u32 padkeys = hidKeysHeld(CONTROLLER_P1_AUTO);
keys |= mInputMapKeyBits(&runner->core->inputMap, AUTO_INPUT, padkeys, 0);
return keys;
} }
static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) { static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) {
@ -310,7 +359,13 @@ static int _batteryState(void) {
u32 charge; u32 charge;
int state = 0; int state = 0;
if (R_SUCCEEDED(psmGetBatteryChargePercentage(&charge))) { if (R_SUCCEEDED(psmGetBatteryChargePercentage(&charge))) {
state = charge / 25; state = (charge + 12) / 25;
} else {
return BATTERY_NOT_PRESENT;
}
ChargerType type;
if (R_SUCCEEDED(psmGetChargerType(&type)) && type) {
state |= BATTERY_CHARGING;
} }
return state; return state;
} }
@ -338,6 +393,13 @@ int main(int argc, char* argv[]) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glGenBuffers(1, &pbo);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glBufferData(GL_PIXEL_UNPACK_BUFFER, 256 * 256 * 4, NULL, GL_STREAM_DRAW);
frameBuffer = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, 256 * 256 * 4, GL_MAP_WRITE_BIT);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
program = glCreateProgram(); program = glCreateProgram();
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
@ -382,12 +444,16 @@ int main(int argc, char* argv[]) {
colorLocation = glGetUniformLocation(program, "color"); colorLocation = glGetUniformLocation(program, "color");
dimsLocation = glGetUniformLocation(program, "dims"); dimsLocation = glGetUniformLocation(program, "dims");
insizeLocation = glGetUniformLocation(program, "insize"); insizeLocation = glGetUniformLocation(program, "insize");
offsetLocation = glGetAttribLocation(program, "offset"); GLuint offsetLocation = glGetAttribLocation(program, "offset");
glGenBuffers(1, &vbo); glGenBuffers(1, &vbo);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(_offsets), _offsets, GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, sizeof(_offsets), _offsets, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0); glVertexAttribPointer(offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(offsetLocation);
glBindVertexArray(0);
stream.videoDimensionsChanged = NULL; stream.videoDimensionsChanged = NULL;
stream.postVideoFrame = NULL; stream.postVideoFrame = NULL;
@ -451,12 +517,26 @@ int main(int argc, char* argv[]) {
}, },
{ .id = 0 } { .id = 0 }
}, },
.nConfigExtra = 0, .configExtra = (struct GUIMenuItem[]) {
{
.title = "Fast forward cap",
.data = "fastForwardCap",
.submenu = 0,
.state = 7,
.validStates = (const char*[]) {
"3", "4", "5", "6", "7", "8", "9",
"10", "11", "12", "13", "14", "15",
"20", "30"
},
.nStates = 15
},
},
.nConfigExtra = 1,
.setup = _setup, .setup = _setup,
.teardown = NULL, .teardown = NULL,
.gameLoaded = _gameLoaded, .gameLoaded = _gameLoaded,
.gameUnloaded = NULL, .gameUnloaded = NULL,
.prepareForFrame = NULL, .prepareForFrame = _prepareForFrame,
.drawFrame = _drawFrame, .drawFrame = _drawFrame,
.drawScreenshot = _drawScreenshot, .drawScreenshot = _drawScreenshot,
.paused = NULL, .paused = NULL,
@ -477,7 +557,25 @@ int main(int argc, char* argv[]) {
_mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_DRIGHT, GUI_INPUT_RIGHT); _mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_DRIGHT, GUI_INPUT_RIGHT);
audoutStartAudioOut(); audoutStartAudioOut();
if (argc > 1) {
size_t i;
for (i = 0; runner.keySources[i].id; ++i) {
mInputMapLoad(&runner.params.keyMap, runner.keySources[i].id, mCoreConfigGetInput(&runner.config));
}
mGUIRun(&runner, argv[1]);
} else {
mGUIRunloop(&runner); mGUIRunloop(&runner);
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
glDeleteBuffers(1, &pbo);
glDeleteTextures(1, &tex);
glDeleteBuffers(1, &vbo);
glDeleteProgram(program);
glDeleteVertexArrays(1, &vao);
psmExit(); psmExit();
audoutExit(); audoutExit();

View File

@ -29,11 +29,12 @@
#include <inttypes.h> #include <inttypes.h>
#include <sys/time.h> #include <sys/time.h>
#define PERF_OPTIONS "DF:L:NPS:" #define PERF_OPTIONS "DF:L:NPS:T"
#define PERF_USAGE \ #define PERF_USAGE \
"\nBenchmark options:\n" \ "\nBenchmark options:\n" \
" -F FRAMES Run for the specified number of FRAMES before exiting\n" \ " -F FRAMES Run for the specified number of FRAMES before exiting\n" \
" -N Disable video rendering entirely\n" \ " -N Disable video rendering entirely\n" \
" -T Use threaded video rendering\n" \
" -P CSV output, useful for parsing\n" \ " -P CSV output, useful for parsing\n" \
" -S SEC Run for SEC in-game seconds before exiting\n" \ " -S SEC Run for SEC in-game seconds before exiting\n" \
" -L FILE Load a savestate when starting the test\n" \ " -L FILE Load a savestate when starting the test\n" \
@ -41,6 +42,7 @@
struct PerfOpts { struct PerfOpts {
bool noVideo; bool noVideo;
bool threadedVideo;
bool csv; bool csv;
unsigned duration; unsigned duration;
unsigned frames; unsigned frames;
@ -89,7 +91,7 @@ int main(int argc, char** argv) {
struct mLogger logger = { .log = _log }; struct mLogger logger = { .log = _log };
mLogSetDefaultLogger(&logger); mLogSetDefaultLogger(&logger);
struct PerfOpts perfOpts = { false, false, 0, 0, 0, false }; struct PerfOpts perfOpts = { false, false, false, 0, 0, 0, false };
struct mSubParser subparser = { struct mSubParser subparser = {
.usage = PERF_USAGE, .usage = PERF_USAGE,
.parse = _parsePerfOpts, .parse = _parsePerfOpts,
@ -162,6 +164,12 @@ bool _mPerfRunCore(const char* fname, const struct mArguments* args, const struc
mCoreConfigInit(&core->config, "perf"); mCoreConfigInit(&core->config, "perf");
mCoreConfigLoad(&core->config); mCoreConfigLoad(&core->config);
if (perfOpts->threadedVideo) {
mCoreConfigSetOverrideIntValue(&core->config, "threadedVideo", 1);
} else {
mCoreConfigSetOverrideIntValue(&core->config, "threadedVideo", 0);
}
struct mCoreOptions opts = {}; struct mCoreOptions opts = {};
mCoreConfigMap(&core->config, &opts); mCoreConfigMap(&core->config, &opts);
opts.audioSync = false; opts.audioSync = false;
@ -200,6 +208,8 @@ bool _mPerfRunCore(const char* fname, const struct mArguments* args, const struc
const char* rendererName; const char* rendererName;
if (perfOpts->noVideo) { if (perfOpts->noVideo) {
rendererName = "none"; rendererName = "none";
} else if (perfOpts->threadedVideo) {
rendererName = "threaded-software";
} else { } else {
rendererName = "software"; rendererName = "software";
} }
@ -313,6 +323,9 @@ static bool _parsePerfOpts(struct mSubParser* parser, int option, const char* ar
case 'S': case 'S':
opts->duration = strtoul(arg, 0, 10); opts->duration = strtoul(arg, 0, 10);
return !errno; return !errno;
case 'T':
opts->threadedVideo = true;
return true;
case 'L': case 'L':
opts->savestate = strdup(arg); opts->savestate = strdup(arg);
return true; return true;

View File

@ -229,15 +229,6 @@ static void reconfigureScreen(struct mGUIRunner* runner) {
double ratio = GBAAudioCalculateRatio(1, audioSampleRate, 1); double ratio = GBAAudioCalculateRatio(1, audioSampleRate, 1);
blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio); blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio);
blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio); blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio);
runner->core->desiredVideoDimensions(runner->core, &corew, &coreh);
int hfactor = vmode->fbWidth / (corew * wAdjust);
int vfactor = vmode->efbHeight / (coreh * hAdjust);
if (hfactor > vfactor) {
scaleFactor = vfactor;
} else {
scaleFactor = hfactor;
}
} }
} }
} }
@ -812,7 +803,7 @@ void _unpaused(struct mGUIRunner* runner) {
} }
void _drawFrame(struct mGUIRunner* runner, bool faded) { void _drawFrame(struct mGUIRunner* runner, bool faded) {
UNUSED(runner); runner->core->desiredVideoDimensions(runner->core, &corew, &coreh);
uint32_t color = 0xFFFFFF3F; uint32_t color = 0xFFFFFF3F;
if (!faded) { if (!faded) {
color |= 0xC0; color |= 0xC0;
@ -838,9 +829,9 @@ void _drawFrame(struct mGUIRunner* runner, bool faded) {
GX_InvalidateTexAll(); GX_InvalidateTexAll();
GX_LoadTexObj(&tex, GX_TEXMAP0); GX_LoadTexObj(&tex, GX_TEXMAP0);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0); GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
s16 vertWidth = TEX_W; s16 vertWidth = corew;
s16 vertHeight = TEX_H; s16 vertHeight = coreh;
if (filterMode == FM_LINEAR_2x) { if (filterMode == FM_LINEAR_2x) {
Mtx44 proj; Mtx44 proj;
@ -850,19 +841,19 @@ void _drawFrame(struct mGUIRunner* runner, bool faded) {
GX_Begin(GX_QUADS, GX_VTXFMT0, 4); GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
GX_Position2s16(0, TEX_H * 2); GX_Position2s16(0, TEX_H * 2);
GX_Color1u32(0xFFFFFFFF); GX_Color1u32(0xFFFFFFFF);
GX_TexCoord2s16(0, 1); GX_TexCoord2f32(0, 1);
GX_Position2s16(TEX_W * 2, TEX_H * 2); GX_Position2s16(TEX_W * 2, TEX_H * 2);
GX_Color1u32(0xFFFFFFFF); GX_Color1u32(0xFFFFFFFF);
GX_TexCoord2s16(1, 1); GX_TexCoord2f32(1, 1);
GX_Position2s16(TEX_W * 2, 0); GX_Position2s16(TEX_W * 2, 0);
GX_Color1u32(0xFFFFFFFF); GX_Color1u32(0xFFFFFFFF);
GX_TexCoord2s16(1, 0); GX_TexCoord2f32(1, 0);
GX_Position2s16(0, 0); GX_Position2s16(0, 0);
GX_Color1u32(0xFFFFFFFF); GX_Color1u32(0xFFFFFFFF);
GX_TexCoord2s16(0, 0); GX_TexCoord2f32(0, 0);
GX_End(); GX_End();
GX_SetTexCopySrc(0, 0, TEX_W * 2, TEX_H * 2); GX_SetTexCopySrc(0, 0, TEX_W * 2, TEX_H * 2);
@ -871,6 +862,14 @@ void _drawFrame(struct mGUIRunner* runner, bool faded) {
GX_LoadTexObj(&rescaleTex, GX_TEXMAP0); GX_LoadTexObj(&rescaleTex, GX_TEXMAP0);
} }
int hfactor = vmode->fbWidth / (corew * wAdjust);
int vfactor = vmode->efbHeight / (coreh * hAdjust);
if (hfactor > vfactor) {
scaleFactor = vfactor;
} else {
scaleFactor = hfactor;
}
if (screenMode == SM_PA) { if (screenMode == SM_PA) {
vertWidth *= scaleFactor; vertWidth *= scaleFactor;
vertHeight *= scaleFactor; vertHeight *= scaleFactor;
@ -885,19 +884,19 @@ void _drawFrame(struct mGUIRunner* runner, bool faded) {
GX_Begin(GX_QUADS, GX_VTXFMT0, 4); GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
GX_Position2s16(0, vertHeight); GX_Position2s16(0, vertHeight);
GX_Color1u32(color); GX_Color1u32(color);
GX_TexCoord2s16(0, 1); GX_TexCoord2f32(0, coreh / (float) TEX_H);
GX_Position2s16(vertWidth, vertHeight); GX_Position2s16(vertWidth, vertHeight);
GX_Color1u32(color); GX_Color1u32(color);
GX_TexCoord2s16(1, 1); GX_TexCoord2f32(corew / (float) TEX_W, coreh / (float) TEX_H);
GX_Position2s16(vertWidth, 0); GX_Position2s16(vertWidth, 0);
GX_Color1u32(color); GX_Color1u32(color);
GX_TexCoord2s16(1, 0); GX_TexCoord2f32(corew / (float) TEX_W, 0);
GX_Position2s16(0, 0); GX_Position2s16(0, 0);
GX_Color1u32(color); GX_Color1u32(color);
GX_TexCoord2s16(0, 0); GX_TexCoord2f32(0, 0);
GX_End(); GX_End();
} }

View File

@ -5,6 +5,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba-util/gui.h> #include <mgba-util/gui.h>
#define KEY_DELAY 45
#define KEY_REPEAT 5
void GUIInit(struct GUIParams* params) { void GUIInit(struct GUIParams* params) {
memset(params->inputHistory, 0, sizeof(params->inputHistory)); memset(params->inputHistory, 0, sizeof(params->inputHistory));
strncpy(params->currentPath, params->basePath, PATH_MAX); strncpy(params->currentPath, params->basePath, PATH_MAX);
@ -19,7 +22,7 @@ void GUIPollInput(struct GUIParams* params, uint32_t* newInputOut, uint32_t* hel
} else { } else {
params->inputHistory[i] = -1; params->inputHistory[i] = -1;
} }
if (!params->inputHistory[i] || (params->inputHistory[i] >= 30 && !(params->inputHistory[i] % 5))) { if (!params->inputHistory[i] || (params->inputHistory[i] >= KEY_DELAY && !(params->inputHistory[i] % KEY_REPEAT))) {
newInput |= (1 << i); newInput |= (1 << i);
} }
} }

View File

@ -260,6 +260,9 @@ void GUIDrawBattery(struct GUIParams* params) {
return; return;
} }
int state = params->batteryState(); int state = params->batteryState();
if (state == BATTERY_NOT_PRESENT) {
return;
}
uint32_t color = 0xFF000000; uint32_t color = 0xFF000000;
if (state == (BATTERY_CHARGING | BATTERY_FULL)) { if (state == (BATTERY_CHARGING | BATTERY_FULL)) {
color |= 0xFFC060; color |= 0xFFC060;

View File

@ -22,6 +22,18 @@ size_t RingFIFOCapacity(const struct RingFIFO* buffer) {
return buffer->capacity; return buffer->capacity;
} }
size_t RingFIFOSize(const struct RingFIFO* buffer) {
const void* read;
const void* write;
ATOMIC_LOAD(read, buffer->readPtr);
ATOMIC_LOAD(write, buffer->writePtr);
if (read <= write) {
return (uintptr_t) write - (uintptr_t) read;
} else {
return buffer->capacity - (uintptr_t) read + (uintptr_t) write;
}
}
void RingFIFOClear(struct RingFIFO* buffer) { void RingFIFOClear(struct RingFIFO* buffer) {
ATOMIC_STORE(buffer->readPtr, buffer->data); ATOMIC_STORE(buffer->readPtr, buffer->data);
ATOMIC_STORE(buffer->writePtr, buffer->data); ATOMIC_STORE(buffer->writePtr, buffer->data);
@ -33,8 +45,8 @@ size_t RingFIFOWrite(struct RingFIFO* buffer, const void* value, size_t length)
ATOMIC_LOAD(end, buffer->readPtr); ATOMIC_LOAD(end, buffer->readPtr);
// Wrap around if we can't fit enough in here // Wrap around if we can't fit enough in here
if ((intptr_t) data - (intptr_t) buffer->data + length >= buffer->capacity) { if ((uintptr_t) data - (uintptr_t) buffer->data + length >= buffer->capacity) {
if (end == buffer->data) { if (end == buffer->data || end > data) {
// Oops! If we wrap now, it'll appear empty // Oops! If we wrap now, it'll appear empty
return 0; return 0;
} }
@ -65,8 +77,8 @@ size_t RingFIFORead(struct RingFIFO* buffer, void* output, size_t length) {
ATOMIC_LOAD(end, buffer->writePtr); ATOMIC_LOAD(end, buffer->writePtr);
// Wrap around if we can't fit enough in here // Wrap around if we can't fit enough in here
if ((intptr_t) data - (intptr_t) buffer->data + length >= buffer->capacity) { if ((uintptr_t) data - (uintptr_t) buffer->data + length >= buffer->capacity) {
if (end == data) { if (end >= data) {
// Oops! If we wrap now, it'll appear full // Oops! If we wrap now, it'll appear full
return 0; return 0;
} }
@ -78,7 +90,7 @@ size_t RingFIFORead(struct RingFIFO* buffer, void* output, size_t length) {
uintptr_t bufferEnd = (uintptr_t) buffer->data + buffer->capacity; uintptr_t bufferEnd = (uintptr_t) buffer->data + buffer->capacity;
remaining = bufferEnd - (uintptr_t) data; remaining = bufferEnd - (uintptr_t) data;
} else { } else {
remaining = (intptr_t) end - (intptr_t) data; remaining = (uintptr_t) end - (uintptr_t) data;
} }
// If the pointers touch, it's empty // If the pointers touch, it's empty
if (remaining < length) { if (remaining < length) {
@ -87,6 +99,6 @@ size_t RingFIFORead(struct RingFIFO* buffer, void* output, size_t length) {
if (output) { if (output) {
memcpy(output, data, length); memcpy(output, data, length);
} }
ATOMIC_STORE(buffer->readPtr, (void*) ((intptr_t) data + length)); ATOMIC_STORE(buffer->readPtr, (void*) ((uintptr_t) data + length));
return length; return length;
} }