diff --git a/CHANGES b/CHANGES index da3e2688d..f7ceefdfb 100644 --- a/CHANGES +++ b/CHANGES @@ -75,6 +75,14 @@ Bugfixes: - GBA Memory: Fix Vast Fame support (taizou) (fixes mgba.io/i/1170) - GB, GBA Savedata: Fix unmasking savedata crash - 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: - GBA Timer: Use global cycles for timers - 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) - Qt: Clean up FPS target UI (fixes mgba.io/i/436) - 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: - GB Audio: Revert unsigned audio changes - GB Video: Fix bad merge (fixes mgba.io/i/1040) - GBA Video: Fix OBJ blending regression (fixes mgba.io/i/1037) -0.6.2: (2017-04-03) +0.6.2: (2018-04-03) Bugfixes: - Core: Fix ROM patches not being unloaded when disabled (fixes mgba.io/i/962) - 3DS: Fix opening files in directory names with trailing slashes diff --git a/CMakeLists.txt b/CMakeLists.txt index a526f0f5a..f1341b95c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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(BUILD_GL ON CACHE BOOL "Build with OpenGL") 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(DISABLE_DEPS OFF CACHE BOOL "Build without dependencies") 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) 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() 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") 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(NOT BUILD_EGL) add_definitions(-DCOLOR_16_BIT -DCOLOR_5_6_5) @@ -301,7 +310,7 @@ if(DEFINED 3DS) endif() 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() if(NOT M_CORE_GBA) @@ -443,6 +452,13 @@ endif() if(NOT BUILD_GLES2) set(OPENGLES2_LIBRARY "" CACHE PATH "" FORCE) 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_PNG ${USE_PNG}) set(WANT_LIBZIP ${USE_LIBZIP}) @@ -909,6 +925,10 @@ if(BUILD_GLES2) add_definitions(-DBUILD_GLES2) endif() +if(BUILD_GLES3) + add_definitions(-DBUILD_GLES3) +endif() + if(DISABLE_FRONTENDS) set(BUILD_SDL OFF) set(BUILD_QT OFF) @@ -1062,9 +1082,18 @@ set(CPACK_STRIP_FILES ON) if(DISTBUILD) 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 "$" "$.dSYM") + add_custom_command(TARGET ${BINARY_NAME} POST_BUILD COMMAND "${STRIP}" -S "$") + install(FILES "$.dSYM" DESTINATION ${LIBDIR} COMPONENT lib${BINARY_NAME}-dbg) + endif() + endif() if(WIN32 OR APPLE) - set(CPACK_COMPONENTS_ALL ${BINARY_NAME} ${BINARY_NAME}-qt ${BINARY_NAME}-sdl ${BINARY_NAME}-perf) - set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE) + 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) + endif() elseif(3DS) set(CPACK_COMPONENTS_ALL ${BINARY_NAME} ${BINARY_NAME}-3ds ${BINARY_NAME}-perf) elseif(WII) @@ -1111,6 +1140,19 @@ if(SDL_FOUND) cpack_add_component(${BINARY_NAME}-sdl GROUP sdl) 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(${BINARY_NAME}-perf GROUP test) cpack_add_component(${BINARY_NAME}-fuzz GROUP test) @@ -1127,6 +1169,9 @@ else() if(BUILD_GLES2) list(APPEND SUMMARY_GL_LIST "OpenGL|ES 2") endif() + if(BUILD_GLES3) + list(APPEND SUMMARY_GL_LIST "OpenGL|ES 3") + endif() endif() if(NOT SUMMARY_GL_LIST) set(SUMMARY_GL OFF) diff --git a/doc/medusa-emu-qt.6 b/doc/medusa-emu-qt.6 index a965f0eb2..25d6bda33 100644 --- a/doc/medusa-emu-qt.6 +++ b/doc/medusa-emu-qt.6 @@ -17,6 +17,7 @@ .Op Fl l Ar loglevel .Op Fl p Ar patchfile .Op Fl s Ar n +.Op Fl t Ar statefile .Ar file .Sh DESCRIPTION .Nm @@ -71,13 +72,7 @@ is a bitmask defining which types of messages to log: .It 32 \(en stub messages for unimplemented features .It -256 \(en in\(hygame errors -.It -512 \(en software interrupts -.It -1024 \(en emulator status messages -.It -2048 \(en serial I/O messages +64 \(en in\(hygame errors .El The default is to log warnings, errors, fatal errors, and status messages. .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 .Ar n frames. +.It Fl t Ar statefile , Fl -savestate Ar statefile +Load initial game state from +.Ar statefile . .El .Sh CONTROLS The default controls are as follows: diff --git a/doc/medusa-emu.6 b/doc/medusa-emu.6 index 4829ec87f..29758fce1 100644 --- a/doc/medusa-emu.6 +++ b/doc/medusa-emu.6 @@ -18,7 +18,7 @@ .Op Fl l Ar loglevel .Op Fl p Ar patchfile .Op Fl s Ar n -.Op Fl v Ar moviefile +.Op Fl t Ar statefile .Ar file .Sh DESCRIPTION .Nm @@ -79,13 +79,7 @@ is a bitmask defining which types of messages to log: .It 32 \(en stub messages for unimplemented features .It -256 \(en in\(hygame errors -.It -512 \(en software interrupts -.It -1024 \(en emulator status messages -.It -2048 \(en serial I/O messages +64 \(en in\(hygame errors .El The default is to log warnings, errors, fatal errors, and status messages. .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 .Ar n frames. -.It Fl v Ar moviefile , Fl -movie Ar moviefile -Play back a movie of recording input from -.Ar moviefile . +.It Fl t Ar statefile , Fl -savestate Ar statefile +Load initial game state from +.Ar statefile . .El .Sh CONTROLS The default controls are as follows: diff --git a/include/mgba-util/gui.h b/include/mgba-util/gui.h index 183fcfa80..a13251bc6 100644 --- a/include/mgba-util/gui.h +++ b/include/mgba-util/gui.h @@ -47,7 +47,8 @@ enum { BATTERY_HIGH = 3, BATTERY_FULL = 4, - BATTERY_CHARGING = 8 + BATTERY_CHARGING = 8, + BATTERY_NOT_PRESENT = 16 }; struct GUIBackground { diff --git a/include/mgba-util/ring-fifo.h b/include/mgba-util/ring-fifo.h index c5b1fdcc5..1cfaa3d00 100644 --- a/include/mgba-util/ring-fifo.h +++ b/include/mgba-util/ring-fifo.h @@ -20,6 +20,7 @@ struct RingFIFO { void RingFIFOInit(struct RingFIFO* buffer, size_t capacity); void RingFIFODeinit(struct RingFIFO* buffer); size_t RingFIFOCapacity(const struct RingFIFO* buffer); +size_t RingFIFOSize(const struct RingFIFO* buffer); void RingFIFOClear(struct RingFIFO* buffer); size_t RingFIFOWrite(struct RingFIFO* buffer, const void* value, size_t length); size_t RingFIFORead(struct RingFIFO* buffer, void* output, size_t length); diff --git a/include/mgba/core/config.h b/include/mgba/core/config.h index a55d19c0d..b2439c41e 100644 --- a/include/mgba/core/config.h +++ b/include/mgba/core/config.h @@ -33,7 +33,6 @@ struct mCoreOptions { int frameskip; bool rewindEnable; int rewindBufferCapacity; - bool rewindSave; float fpsTarget; size_t audioBuffers; unsigned sampleRate; diff --git a/include/mgba/internal/gb/audio.h b/include/mgba/internal/gb/audio.h index ff7b3830a..837e0a1b3 100644 --- a/include/mgba/internal/gb/audio.h +++ b/include/mgba/internal/gb/audio.h @@ -137,6 +137,9 @@ struct GBAudioNoiseChannel { int length; uint32_t lfsr; + int nSamples; + int samples; + int8_t sample; }; diff --git a/include/mgba/internal/gb/gb.h b/include/mgba/internal/gb/gb.h index d0e5df0d9..47c3a03df 100644 --- a/include/mgba/internal/gb/gb.h +++ b/include/mgba/internal/gb/gb.h @@ -177,6 +177,7 @@ void GBGetGameCode(const struct GB* gba, char* out); void GBTestKeypadIRQ(struct GB* gb); +void GBFrameStarted(struct GB* gb); void GBFrameEnded(struct GB* gb); CXX_GUARD_END diff --git a/include/mgba/internal/gb/io.h b/include/mgba/internal/gb/io.h index 95afebe0f..15172ff0f 100644 --- a/include/mgba/internal/gb/io.h +++ b/include/mgba/internal/gb/io.h @@ -102,9 +102,9 @@ enum GBIORegisters { REG_UNK72 = 0x72, REG_UNK73 = 0x73, REG_UNK74 = 0x74, - REG_PCM12 = 0x75, - REG_PCM34 = 0x76, - REG_UNK77 = 0x77, + REG_UNK75 = 0x75, + REG_PCM12 = 0x76, + REG_PCM34 = 0x77, REG_MAX = 0x100 }; diff --git a/include/mgba/internal/gb/serialize.h b/include/mgba/internal/gb/serialize.h index f4f36c640..14cfd12d4 100644 --- a/include/mgba/internal/gb/serialize.h +++ b/include/mgba/internal/gb/serialize.h @@ -47,7 +47,7 @@ mLOG_DECLARE_CATEGORY(GB_STATE); * | 0x00040 - 0x00043: Reserved (DI pending cycles) * | 0x00044 - 0x00047: Flags * | bit 0: Is condition met? - * | bit 1: Is condition IRQ pending? + * | bit 1: Is IRQ pending? * | bit 2: Double speed * | bit 3: Is EI pending? * | bits 4 - 31: Reserved @@ -232,7 +232,7 @@ DECL_BITFIELD(GBSerializedCpuFlags, uint32_t); DECL_BIT(GBSerializedCpuFlags, Condition, 0); DECL_BIT(GBSerializedCpuFlags, IrqPending, 1); DECL_BIT(GBSerializedCpuFlags, DoubleSpeed, 2); -DECL_BIT(GBSerializedCpuFlags, EiPending, 1); +DECL_BIT(GBSerializedCpuFlags, EiPending, 3); DECL_BITFIELD(GBSerializedTimerFlags, uint8_t); DECL_BIT(GBSerializedTimerFlags, IrqPending, 0); diff --git a/res/patrons.txt b/res/patrons.txt index 4da58cb0e..deaca59b2 100644 --- a/res/patrons.txt +++ b/res/patrons.txt @@ -1,7 +1,8 @@ +Elijah Chondropoulos Jaime J. Denizard Fog Philip Horton +Oskenso Kashi Rohit Nirmal Rhys Powell -rootfather Yuri Kunde Schlesner diff --git a/src/core/config.c b/src/core/config.c index cb5c1a6dc..932c904c2 100644 --- a/src/core/config.c +++ b/src/core/config.c @@ -331,7 +331,6 @@ void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts) _lookupIntValue(config, "frameskip", &opts->frameskip); _lookupIntValue(config, "volume", &opts->volume); _lookupIntValue(config, "rewindBufferCapacity", &opts->rewindBufferCapacity); - _lookupIntValue(config, "rewindSave", &opts->rewindSave); _lookupFloatValue(config, "fpsTarget", &opts->fpsTarget); unsigned 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, "rewindEnable", opts->rewindEnable); ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferCapacity", opts->rewindBufferCapacity); - ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindSave", opts->rewindSave); ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget); ConfigurationSetUIntValue(&config->defaultsTable, 0, "audioBuffers", opts->audioBuffers); ConfigurationSetUIntValue(&config->defaultsTable, 0, "sampleRate", opts->sampleRate); diff --git a/src/core/serialize.c b/src/core/serialize.c index 2baf4d133..71b56f8b0 100644 --- a/src/core/serialize.c +++ b/src/core/serialize.c @@ -359,7 +359,6 @@ bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags) { } } if (flags & SAVESTATE_RTC) { - mLOG(SAVESTATE, INFO, "Loading RTC"); struct mStateExtdataItem item; if (core->rtc.d.serialize) { core->rtc.d.serialize(&core->rtc.d, &item); diff --git a/src/feature/ffmpeg/ffmpeg-encoder.c b/src/feature/ffmpeg/ffmpeg-encoder.c index 64f96fa00..52ea79454 100644 --- a/src/feature/ffmpeg/ffmpeg-encoder.c +++ b/src/feature/ffmpeg/ffmpeg-encoder.c @@ -211,7 +211,6 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) { encoder->currentAudioSample = 0; encoder->currentAudioFrame = 0; encoder->currentVideoFrame = 0; - encoder->nextAudioPts = 0; AVOutputFormat* oformat = av_guess_format(encoder->containerFormat, 0, 0); #ifndef USE_LIBAV @@ -306,8 +305,8 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) { encoder->video->bit_rate = encoder->videoBitrate; encoder->video->width = encoder->width; encoder->video->height = encoder->height; - encoder->videoStream->time_base = (AVRational) { encoder->frameCycles, encoder->cycles }; - encoder->video->time_base = encoder->videoStream->time_base; + encoder->video->time_base = (AVRational) { encoder->frameCycles, encoder->cycles }; + encoder->video->framerate = (AVRational) { encoder->cycles, encoder->frameCycles }; encoder->video->pix_fmt = encoder->pixFormat; encoder->video->gop_size = 60; 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; } } + 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); #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); #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; AVPacket packet; av_init_packet(&packet); packet.data = 0; packet.size = 0; - packet.pts = encoder->audioFrame->pts; int gotData; #ifdef FFMPEG_USE_PACKETS @@ -483,6 +485,9 @@ void _ffmpegPostAudioFrame(struct mAVStream* stream, int16_t left, int16_t right #else avcodec_encode_audio2(encoder->audio, &packet, encoder->audioFrame, &gotData); #endif + packet.pts = av_rescale_q(encoder->audioFrame->pts, encoder->audio->time_base, encoder->audioStream->time_base); + packet.dts = packet.pts; + if (gotData) { if (encoder->absf) { AVPacket tempPacket; @@ -541,7 +546,6 @@ void _ffmpegPostVideoFrame(struct mAVStream* stream, const color_t* pixels, size av_frame_make_writable(encoder->videoFrame); #endif encoder->videoFrame->pts = av_rescale_q(encoder->currentVideoFrame, encoder->video->time_base, encoder->videoStream->time_base); - packet.pts = encoder->videoFrame->pts; ++encoder->currentVideoFrame; 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 avcodec_encode_video2(encoder->video, &packet, encoder->videoFrame, &gotData); #endif + packet.pts = encoder->videoFrame->pts; if (gotData) { #ifndef FFMPEG_USE_PACKET_UNREF if (encoder->video->coded_frame->key_frame) { diff --git a/src/feature/ffmpeg/ffmpeg-encoder.h b/src/feature/ffmpeg/ffmpeg-encoder.h index 88deffb6e..7b2edf177 100644 --- a/src/feature/ffmpeg/ffmpeg-encoder.h +++ b/src/feature/ffmpeg/ffmpeg-encoder.h @@ -56,7 +56,6 @@ struct FFmpegEncoder { AVFrame* audioFrame; size_t currentAudioSample; int64_t currentAudioFrame; - int64_t nextAudioPts; // TODO (0.6): Remove #ifdef USE_LIBAVRESAMPLE struct AVAudioResampleContext* resampleContext; #else diff --git a/src/feature/gui/gui-config.c b/src/feature/gui/gui-config.c index 6d6aa75af..3a938fade 100644 --- a/src/feature/gui/gui-config.c +++ b/src/feature/gui/gui-config.c @@ -108,6 +108,16 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t .title = "Select SGB BIOS path", .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 size_t i; const char* mapNames[GUI_MAX_INPUTS + 1]; diff --git a/src/feature/gui/gui-runner.c b/src/feature/gui/gui-runner.c index 3877aef54..3dc0965a6 100644 --- a/src/feature/gui/gui-runner.c +++ b/src/feature/gui/gui-runner.c @@ -54,7 +54,8 @@ static const struct mInputPlatformInfo _mGUIKeyInfo = { [mGUI_INPUT_DECREASE_BRIGHTNESS] = "Decrease solar brightness", [mGUI_INPUT_SCREEN_MODE] = "Screen mode", [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 }; @@ -412,6 +413,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) { runner->lastFpsCheck = 1000000LL * tv.tv_sec + tv.tv_usec; int frame = 0; + bool fastForward = false; while (running) { if (runner->running) { running = runner->running(runner); @@ -442,7 +444,10 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) { mCoreTakeScreenshot(runner->core); } 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); } else { runner->setFrameLimiter(runner, true); diff --git a/src/feature/gui/gui-runner.h b/src/feature/gui/gui-runner.h index 6c91d1347..fda78f982 100644 --- a/src/feature/gui/gui-runner.h +++ b/src/feature/gui/gui-runner.h @@ -22,7 +22,8 @@ enum mGUIInput { mGUI_INPUT_DECREASE_BRIGHTNESS, mGUI_INPUT_SCREEN_MODE, mGUI_INPUT_SCREENSHOT, - mGUI_INPUT_FAST_FORWARD, + mGUI_INPUT_FAST_FORWARD_HELD, + mGUI_INPUT_FAST_FORWARD_TOGGLE }; struct mGUIBackground { diff --git a/src/feature/gui/remap.c b/src/feature/gui/remap.c index b17560319..4238454b6 100644 --- a/src/feature/gui/remap.c +++ b/src/feature/gui/remap.c @@ -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)) { for (i = 0; i < GUIMenuItemListSize(&menu.items); ++i) { item = GUIMenuItemListGetPointer(&menu.items, i); - if ((uint32_t) item->data < 1) { + if ((uintptr_t) item->data < 1) { continue; } - if ((uint32_t) item->data < GUI_INPUT_MAX + 1) { - mInputBindKey(¶ms->keyMap, keys->id, item->state - 1, (uint32_t) item->data - 1); - } else if ((uint32_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); + if ((uintptr_t) item->data < GUI_INPUT_MAX + 1) { + mInputBindKey(¶ms->keyMap, keys->id, item->state - 1, (uintptr_t) item->data - 1); + } else if ((uintptr_t) item->data < GUI_INPUT_MAX + map->info->nKeys + 1) { + mInputBindKey(map, keys->id, item->state - 1, (uintptr_t) item->data - GUI_INPUT_MAX - 1); } } break; diff --git a/src/feature/imagemagick/imagemagick-gif-encoder.h b/src/feature/imagemagick/imagemagick-gif-encoder.h index 6d713bfa4..39946e916 100644 --- a/src/feature/imagemagick/imagemagick-gif-encoder.h +++ b/src/feature/imagemagick/imagemagick-gif-encoder.h @@ -12,9 +12,6 @@ CXX_GUARD_START #include -#define MAGICKCORE_HDRI_ENABLE 0 -#define MAGICKCORE_QUANTUM_DEPTH 8 - #if MAGICKWAND_VERSION_MAJOR >= 7 #include #else diff --git a/src/feature/thread-proxy.c b/src/feature/thread-proxy.c index 8fe944846..196877002 100644 --- a/src/feature/thread-proxy.c +++ b/src/feature/thread-proxy.c @@ -121,7 +121,7 @@ static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bo if (!block || read) { 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); ConditionWake(&proxyRenderer->fromThreadCond); ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex); @@ -142,7 +142,7 @@ static void _wait(struct mVideoLogger* logger) { _proxyThreadRecover(proxyRenderer); return; } - while (proxyRenderer->threadState == PROXY_THREAD_BUSY) { + while (RingFIFOSize(&proxyRenderer->dirtyQueue)) { ConditionWake(&proxyRenderer->toThreadCond); ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex); } diff --git a/src/feature/video-logger.c b/src/feature/video-logger.c index 6f49e482d..be1c7b3f7 100644 --- a/src/feature/video-logger.c +++ b/src/feature/video-logger.c @@ -258,6 +258,9 @@ void mVideoLoggerRendererFlush(struct mVideoLogger* logger) { 0xDEADBEEF, }; logger->writeData(logger, &dirty, sizeof(dirty)); + if (logger->wait) { + logger->wait(logger); + } } void mVideoLoggerRendererFinishFrame(struct mVideoLogger* logger) { diff --git a/src/gb/audio.c b/src/gb/audio.c index daf479a26..994eaeedb 100644 --- a/src/gb/audio.c +++ b/src/gb/audio.c @@ -37,6 +37,8 @@ static bool _updateSweep(struct GBAudioSquareChannel* sweep, bool initial); static void _updateSquareSample(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 _updateChannel1(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.name = "GB Audio Sample"; audio->sampleEvent.callback = _sample; - audio->ch1Event.priority = 0x18; + audio->sampleEvent.priority = 0x18; } void GBAudioDeinit(struct GBAudio* audio) { @@ -118,6 +120,7 @@ void GBAudioReset(struct GBAudio* audio) { audio->ch1 = (struct GBAudioSquareChannel) { .envelope = { .dead = 2 } }; audio->ch2 = (struct GBAudioSquareChannel) { .envelope = { .dead = 2 } }; audio->ch3 = (struct GBAudioWaveChannel) { .bank = 0 }; + audio->ch4 = (struct GBAudioNoiseChannel) { .nSamples = 0 }; // TODO: DMG randomness audio->ch3.wavedata8[0] = 0x00; audio->ch3.wavedata8[1] = 0xFF; @@ -203,11 +206,6 @@ void GBAudioWriteNR14(struct GBAudio* audio, uint8_t value) { } if (GBAudioRegisterControlIsRestart(value << 8)) { audio->playingCh1 = _resetEnvelope(&audio->ch1.envelope); - - if (audio->playingCh1) { - _updateSquareSample(&audio->ch1); - } - audio->ch1.sweep.realFrequency = audio->ch1.control.frequency; _resetSweep(&audio->ch1.sweep); if (audio->playingCh1 && audio->ch1.sweep.shift) { @@ -219,7 +217,9 @@ void GBAudioWriteNR14(struct GBAudio* audio, uint8_t value) { --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); } } @@ -260,17 +260,15 @@ void GBAudioWriteNR24(struct GBAudio* audio, uint8_t value) { if (GBAudioRegisterControlIsRestart(value << 8)) { audio->playingCh2 = _resetEnvelope(&audio->ch2.envelope); - if (audio->playingCh2) { - _updateSquareSample(&audio->ch2); - } - if (!audio->ch2.control.length) { audio->ch2.control.length = 64; if (audio->ch2.control.stop && !(audio->frame & 1)) { --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); } } @@ -331,6 +329,7 @@ void GBAudioWriteNR34(struct GBAudio* audio, uint8_t value) { } } audio->ch3.window = 0; + audio->ch3.sample = 0; } mTimingDeschedule(audio->timing, &audio->ch3Fade); mTimingDeschedule(audio->timing, &audio->ch3Event); @@ -376,9 +375,9 @@ void GBAudioWriteNR44(struct GBAudio* audio, uint8_t value) { audio->playingCh4 = _resetEnvelope(&audio->ch4.envelope); if (audio->ch4.power) { - audio->ch4.lfsr = 0x40; + audio->ch4.lfsr = 0x7F; } else { - audio->ch4.lfsr = 0x4000; + audio->ch4.lfsr = 0x7FFF; } if (!audio->ch4.length) { audio->ch4.length = 64; @@ -386,7 +385,8 @@ void GBAudioWriteNR44(struct GBAudio* audio, uint8_t value) { --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); } } @@ -563,11 +563,13 @@ void GBAudioUpdateFrame(struct GBAudio* audio, struct mTiming* timing) { --audio->ch4.envelope.nextStep; if (audio->ch4.envelope.nextStep == 0) { int8_t sample = audio->ch4.sample > 0; + audio->ch4.samples -= audio->ch4.sample; _updateEnvelope(&audio->ch4.envelope); if (audio->ch4.envelope.dead == 2) { mTimingDeschedule(timing, &audio->ch4Event); } audio->ch4.sample = sample * audio->ch4.envelope.currentVolume; + audio->ch4.samples += audio->ch4.sample; } } break; @@ -575,47 +577,48 @@ void GBAudioUpdateFrame(struct GBAudio* audio, struct mTiming* timing) { } 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 sampleRight = dcOffset; if (audio->playingCh1 && !audio->forceDisableCh[0]) { if (audio->ch1Left) { - sampleLeft -= audio->ch1.sample; + sampleLeft += audio->ch1.sample; } if (audio->ch1Right) { - sampleRight -= audio->ch1.sample; + sampleRight += audio->ch1.sample; } } if (audio->playingCh2 && !audio->forceDisableCh[1]) { if (audio->ch2Left) { - sampleLeft -= audio->ch2.sample; + sampleLeft += audio->ch2.sample; } if (audio->ch2Right) { - sampleRight -= audio->ch2.sample; + sampleRight += audio->ch2.sample; } } if (audio->playingCh3 && !audio->forceDisableCh[2]) { if (audio->ch3Left) { - sampleLeft -= audio->ch3.sample; + sampleLeft += audio->ch3.sample; } if (audio->ch3Right) { - sampleRight -= audio->ch3.sample; + sampleRight += audio->ch3.sample; } } if (audio->playingCh4 && !audio->forceDisableCh[3]) { + int8_t sample = _coalesceNoiseChannel(&audio->ch4); if (audio->ch4Left) { - sampleLeft -= audio->ch4.sample; + sampleLeft += sample; } 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 sampleRight = 0; GBAudioSamplePSG(audio, &sampleLeft, &sampleRight); - sampleLeft = (sampleLeft * audio->masterVolume * 9) >> 7; - sampleRight = (sampleRight * audio->masterVolume * 9) >> 7; + sampleLeft = (sampleLeft * audio->masterVolume * 6) >> 7; + sampleRight = (sampleRight * audio->masterVolume * 6) >> 7; mCoreSyncLockAudio(audio->p->sync); 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) { if (envelope->direction) { ++envelope->currentVolume; @@ -894,18 +908,17 @@ static void _updateChannel4(struct mTiming* timing, void* user, uint32_t cyclesL struct GBAudio* audio = user; struct GBAudioNoiseChannel* ch = &audio->ch4; - int32_t baseCycles = ch->ratio ? 2 * ch->ratio : 1; - baseCycles <<= ch->frequency; - baseCycles *= 8 * audio->timingFactor; - int32_t cycles = 0; + int32_t cycles = ch->ratio ? 2 * ch->ratio : 1; + cycles <<= ch->frequency; + cycles *= 8 * audio->timingFactor; + + int lsb = ch->lfsr & 1; + ch->sample = lsb * ch->envelope.currentVolume; + ++ch->nSamples; + ch->samples += ch->sample; + ch->lfsr >>= 1; + ch->lfsr ^= (lsb * 0x60) << (ch->power ? 0 : 8); - do { - int lsb = ch->lfsr & 1; - ch->sample = lsb * ch->envelope.currentVolume; - ch->lfsr >>= 1; - ch->lfsr ^= (lsb * 0x60) << (ch->power ? 0 : 8); - cycles += baseCycles; - } while (cycles + baseCycles < audio->sampleInterval); mTimingSchedule(timing, &audio->ch4Event, cycles - cyclesLate); } diff --git a/src/gb/extra/proxy.c b/src/gb/extra/proxy.c index 36411939b..c5230c88c 100644 --- a/src/gb/extra/proxy.c +++ b/src/gb/extra/proxy.c @@ -252,9 +252,10 @@ void GBVideoProxyRendererFinishFrame(struct GBVideoRenderer* renderer) { struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { proxyRenderer->logger->lock(proxyRenderer->logger); - proxyRenderer->logger->wait(proxyRenderer->logger); } - proxyRenderer->backend->finishFrame(proxyRenderer->backend); + if (!proxyRenderer->logger->block) { + proxyRenderer->backend->finishFrame(proxyRenderer->backend); + } mVideoLoggerRendererFinishFrame(proxyRenderer->logger); mVideoLoggerRendererFlush(proxyRenderer->logger); if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { diff --git a/src/gb/gb.c b/src/gb/gb.c index 4775f072c..29b12df2d 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -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) { 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) { diff --git a/src/gb/mbc.c b/src/gb/mbc.c index 2da6a264e..817b3dc9d 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -95,17 +95,27 @@ void GBMBCSwitchHalfBank(struct GB* gb, int half, int bank) { } static bool _isMulticart(const uint8_t* mem) { - bool success = true; + bool success; struct VFile* vf; vf = VFileFromConstMemory(&mem[GB_SIZE_CART_BANK0 * 0x10], 1024); - success = success && GBIsROM(vf); + success = GBIsROM(vf); vf->close(vf); + if (!success) { + return false; + } + vf = VFileFromConstMemory(&mem[GB_SIZE_CART_BANK0 * 0x20], 1024); - success = success && GBIsROM(vf); + success = GBIsROM(vf); vf->close(vf); + if (!success) { + vf = VFileFromConstMemory(&mem[GB_SIZE_CART_BANK0 * 0x30], 1024); + success = GBIsROM(vf); + vf->close(vf); + } + return success; } diff --git a/src/gb/renderers/software.c b/src/gb/renderers/software.c index d339c9629..d2e000422 100644 --- a/src/gb/renderers/software.c +++ b/src/gb/renderers/software.c @@ -193,7 +193,7 @@ static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum G softwareRenderer->scx = 0; softwareRenderer->wy = 0; softwareRenderer->currentWy = 0; - softwareRenderer->lastY = 0; + softwareRenderer->lastY = GB_VIDEO_VERTICAL_PIXELS; softwareRenderer->hasWindow = false; softwareRenderer->wx = 0; softwareRenderer->model = model; diff --git a/src/gb/video.c b/src/gb/video.c index 5ab37d1af..f90536ae0 100644 --- a/src/gb/video.c +++ b/src/gb/video.c @@ -337,14 +337,6 @@ void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLat 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); mCoreSyncPostFrame(video->p->sync); --video->frameskipCounter; @@ -354,24 +346,10 @@ void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLat } ++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])) { mTimingSchedule(timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH); } - - for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) { - struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c); - if (callbacks->videoFrameStarted) { - callbacks->videoFrameStarted(callbacks->context); - } - } + GBFrameStarted(video->p); } static void _cleanOAM(struct GBVideo* video, int y) { diff --git a/src/gba/audio.c b/src/gba/audio.c index 14f47ff56..c0ed7cf4c 100644 --- a/src/gba/audio.c +++ b/src/gba/audio.c @@ -253,7 +253,7 @@ static int _applyBias(struct GBAAudio* audio, int sample) { } else if (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) { diff --git a/src/gba/extra/proxy.c b/src/gba/extra/proxy.c index aa8e231ba..c871307ca 100644 --- a/src/gba/extra/proxy.c +++ b/src/gba/extra/proxy.c @@ -117,6 +117,7 @@ void GBAVideoProxyRendererInit(struct GBAVideoRenderer* renderer) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; _init(proxyRenderer); + _reset(proxyRenderer); proxyRenderer->backend->init(proxyRenderer->backend); } @@ -150,7 +151,7 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD } break; case DIRTY_OAM: - if (item->address < SIZE_PALETTE_RAM) { + if (item->address < SIZE_OAM) { logger->oam[item->address] = item->value; 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) { logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true); proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address); + } else { + logger->readData(logger, NULL, 0x1000, true); } break; case DIRTY_SCANLINE: @@ -267,9 +270,10 @@ void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { proxyRenderer->logger->lock(proxyRenderer->logger); - proxyRenderer->logger->wait(proxyRenderer->logger); } - proxyRenderer->backend->finishFrame(proxyRenderer->backend); + if (!proxyRenderer->logger->block) { + proxyRenderer->backend->finishFrame(proxyRenderer->backend); + } mVideoLoggerRendererFinishFrame(proxyRenderer->logger); mVideoLoggerRendererFlush(proxyRenderer->logger); if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { @@ -283,7 +287,6 @@ static void GBAVideoProxyRendererGetPixels(struct GBAVideoRenderer* renderer, si proxyRenderer->logger->lock(proxyRenderer->logger); // Insert an extra item into the queue to make sure it gets flushed mVideoLoggerRendererFlush(proxyRenderer->logger); - proxyRenderer->logger->wait(proxyRenderer->logger); } proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels); if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { @@ -297,7 +300,6 @@ static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, si proxyRenderer->logger->lock(proxyRenderer->logger); // Insert an extra item into the queue to make sure it gets flushed mVideoLoggerRendererFlush(proxyRenderer->logger); - proxyRenderer->logger->wait(proxyRenderer->logger); } proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels); if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { diff --git a/src/platform/3ds/main.c b/src/platform/3ds/main.c index bafe09129..05fe1dcc8 100644 --- a/src/platform/3ds/main.c +++ b/src/platform/3ds/main.c @@ -194,7 +194,9 @@ static void _drawStart(void) { C3D_FrameBegin(flags); ctrStartFrame(); + C3D_FrameDrawOn(bottomScreen[doubleBuffer]); C3D_RenderTargetClear(bottomScreen[doubleBuffer], C3D_CLEAR_COLOR, 0, 0); + C3D_FrameDrawOn(topScreen[doubleBuffer]); C3D_RenderTargetClear(topScreen[doubleBuffer], C3D_CLEAR_COLOR, 0, 0); } diff --git a/src/platform/libretro/libretro.c b/src/platform/libretro/libretro.c index d805e778f..fb6703ceb 100644 --- a/src/platform/libretro/libretro.c +++ b/src/platform/libretro/libretro.c @@ -66,6 +66,8 @@ static struct mImageSource imageSource; static uint32_t* camData = NULL; static unsigned camWidth; static unsigned camHeight; +static unsigned imcapWidth; +static unsigned imcapHeight; static size_t camStride; static void _reloadSettings(void) { @@ -604,24 +606,49 @@ void retro_cheat_set(unsigned index, bool enabled, const char* code) { cheatSet = device->createSet(device, NULL); mCheatAddSet(device, cheatSet); } - // Convert the super wonky unportable libretro format to something normal - char realCode[] = "XXXXXXXX XXXXXXXX"; - 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] = ' '; - } else { - realCode[pos] = code[i]; +// Convert the super wonky unportable libretro format to something normal +#ifdef M_CORE_GBA + if (core->platform(core) == PLATFORM_GBA) { + char realCode[] = "XXXXXXXX XXXXXXXX"; + 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] = ' '; + } else { + realCode[pos] = code[i]; + } + if ((pos == 13 && (realCode[pos] == ' ' || !realCode[pos])) || pos == 17) { + realCode[pos] = '\0'; + mCheatAddLine(cheatSet, realCode, 0); + pos = 0; + continue; + } + ++pos; } - if ((pos == 13 && (realCode[pos] == ' ' || !realCode[pos])) || pos == 17) { - realCode[pos] = '\0'; - mCheatAddLine(cheatSet, realCode, 0); - pos = 0; - continue; - } - ++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) { @@ -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) { - if (!camData || width != camWidth || height != camHeight) { - camData = malloc(sizeof(*buffer) * height * pitch); + if (!camData || width > camWidth || height > camHeight) { + if (camData) { + free(camData); + } + 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 = height; - camStride = pitch / sizeof(*buffer); + 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) { UNUSED(image); UNUSED(colorFormats); + if (camData) { + free(camData); + } camData = NULL; + imcapWidth = w; + imcapHeight = h; cam.start(); } @@ -803,8 +850,18 @@ static void _requestImage(struct mImageSource* image, const void** buffer, size_ UNUSED(image); if (!camData) { 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; *colorFormat = mCOLOR_XRGB8; } diff --git a/src/platform/psp2/psp2-context.c b/src/platform/psp2/psp2-context.c index 4ee45f59c..41e3a4a11 100644 --- a/src/platform/psp2/psp2-context.c +++ b/src/platform/psp2/psp2-context.c @@ -97,18 +97,23 @@ void mPSP2MapKey(struct mInputMap* map, int pspKey, int key) { static THREAD_ENTRY _audioThread(void* context) { struct mPSP2AudioContext* audio = (struct mPSP2AudioContext*) context; 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); while (audio->running) { 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) { buffer = &audio->buffer[audio->readOffset]; - audio->samples -= PSP2_SAMPLES; audio->readOffset += PSP2_SAMPLES; if (audio->readOffset >= PSP2_AUDIO_BUFFER_SIZE) { 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 { buffer = zeroBuffer; } @@ -280,6 +285,13 @@ uint16_t mPSP2PollInput(struct mGUIRunner* runner) { void mPSP2SetFrameLimiter(struct mGUIRunner* runner, bool limit) { UNUSED(runner); + if (!frameLimiter && limit) { + MutexLock(&audioContext.mutex); + while (audioContext.samples) { + ConditionWait(&audioContext.cond, &audioContext.mutex); + } + MutexUnlock(&audioContext.mutex); + } frameLimiter = limit; } diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 2e9ac8d1b..4cee5acc4 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -271,7 +271,7 @@ endif() 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}) -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) 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) 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 "$" "$.dSYM") + add_custom_command(TARGET ${BINARY_NAME}-qt POST_BUILD COMMAND "${STRIP}" -S "$") + install(FILES "$.dSYM" DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-qt-dbg) + endif() +endif() diff --git a/src/platform/qt/CoreController.cpp b/src/platform/qt/CoreController.cpp index 9d6429cef..46f777c5a 100644 --- a/src/platform/qt/CoreController.cpp +++ b/src/platform/qt/CoreController.cpp @@ -46,6 +46,7 @@ CoreController::CoreController(mCore* core, QObject* parent) m_buffers[0].fill(0xFF); m_buffers[1].fill(0xFF); m_activeBuffer = &m_buffers[0]; + m_completeBuffer = m_buffers[0]; m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast(m_activeBuffer->data()), size.width()); @@ -209,12 +210,9 @@ CoreController::~CoreController() { m_threadContext.core->deinit(m_threadContext.core); } -color_t* CoreController::drawContext() { +const color_t* CoreController::drawContext() { QMutexLocker locker(&m_mutex); - if (!m_completeBuffer) { - return nullptr; - } - return reinterpret_cast(m_completeBuffer->data()); + return reinterpret_cast(m_completeBuffer.constData()); } bool CoreController::isPaused() { @@ -762,7 +760,7 @@ void CoreController::updateKeys() { void CoreController::finishFrame() { QMutexLocker locker(&m_mutex); - m_completeBuffer = m_activeBuffer; + memcpy(m_completeBuffer.data(), m_activeBuffer->constData(), m_activeBuffer->size()); // TODO: Generalize this to triple buffering? m_activeBuffer = &m_buffers[0]; @@ -770,7 +768,7 @@ void CoreController::finishFrame() { m_activeBuffer = &m_buffers[1]; } // 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(m_activeBuffer->data()), screenDimensions().width()); for (auto& action : m_frameActions) { diff --git a/src/platform/qt/CoreController.h b/src/platform/qt/CoreController.h index 898eba66e..107f4b2eb 100644 --- a/src/platform/qt/CoreController.h +++ b/src/platform/qt/CoreController.h @@ -58,7 +58,7 @@ public: mCoreThread* thread() { return &m_threadContext; } - color_t* drawContext(); + const color_t* drawContext(); bool isPaused(); bool hasStarted(); @@ -170,7 +170,7 @@ private: QByteArray m_buffers[2]; QByteArray* m_activeBuffer; - QByteArray* m_completeBuffer = nullptr; + QByteArray m_completeBuffer; std::unique_ptr m_cacheSet; std::unique_ptr m_override; diff --git a/src/platform/qt/DisplayQt.cpp b/src/platform/qt/DisplayQt.cpp index a19a3b049..7018ee274 100644 --- a/src/platform/qt/DisplayQt.cpp +++ b/src/platform/qt/DisplayQt.cpp @@ -52,7 +52,7 @@ void DisplayQt::filter(bool filter) { void DisplayQt::framePosted() { update(); - color_t* buffer = m_context->drawContext(); + const color_t* buffer = m_context->drawContext(); if (const_cast(m_backing).bits() == reinterpret_cast(buffer)) { return; } diff --git a/src/platform/qt/VideoView.cpp b/src/platform/qt/VideoView.cpp index ea842ba67..8ee77f7ed 100644 --- a/src/platform/qt/VideoView.cpp +++ b/src/platform/qt/VideoView.cpp @@ -51,6 +51,7 @@ VideoView::VideoView(QWidget* parent) if (s_acodecMap.empty()) { s_acodecMap["mp3"] = "libmp3lame"; s_acodecMap["opus"] = "libopus"; + s_acodecMap["vorbis"] = "libvorbis"; s_acodecMap["uncompressed"] = "pcm_s16le"; } if (s_vcodecMap.empty()) { @@ -101,7 +102,7 @@ VideoView::VideoView(QWidget* parent) .container = "MKV", .vcodec = "h.264", .acodec = "FLAC", - .vbr = 0, + .vbr = -1, .abr = 0, .dims = QSize(), }); @@ -170,8 +171,8 @@ void VideoView::updatePresets() { addPreset(m_ui.presetWebM, { .container = "WebM", - .vcodec = "VP8", - .acodec = "Vorbis", + .vcodec = "VP9", + .acodec = "Opus", .vbr = 800, .abr = 128 }); @@ -181,7 +182,7 @@ void VideoView::updatePresets() { .container = "MKV", .vcodec = "h.264", .acodec = "FLAC", - .vbr = 0, + .vbr = -1, .abr = 0, .dims = QSize(m_nativeWidth, m_nativeHeight) }); @@ -315,7 +316,7 @@ void VideoView::setAudioBitrate(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); validateSettings(); if (manual) { diff --git a/src/platform/qt/VideoView.ui b/src/platform/qt/VideoView.ui index eb42fd4f5..1e9dfe367 100644 --- a/src/platform/qt/VideoView.ui +++ b/src/platform/qt/VideoView.ui @@ -106,7 +106,7 @@ - High Quality + High &Quality presets @@ -116,7 +116,7 @@ - YouTube + &YouTube presets @@ -136,7 +136,7 @@ - Lossless + &Lossless true @@ -153,7 +153,7 @@ - 1080p + &1080p resolutions @@ -163,7 +163,7 @@ - 720p + &720p resolutions @@ -173,7 +173,7 @@ - 480p + &480p resolutions @@ -186,7 +186,7 @@ false - Native + &Native true @@ -274,6 +274,11 @@ VP8 + + + VP9 + + FFV1 diff --git a/src/platform/sdl/CMakeLists.txt b/src/platform/sdl/CMakeLists.txt index c5af5df52..09ed2ba39 100644 --- a/src/platform/sdl/CMakeLists.txt +++ b/src/platform/sdl/CMakeLists.txt @@ -105,3 +105,11 @@ install(TARGETS ${BINARY_NAME}-sdl DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT if(UNIX) install(FILES ${CMAKE_SOURCE_DIR}/doc/${BINARY_NAME}.6 DESTINATION ${MANDIR}/man6 COMPONENT ${BINARY_NAME}-sdl) 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 "$" "$.dSYM") + add_custom_command(TARGET ${BINARY_NAME}-sdl POST_BUILD COMMAND "${STRIP}" -S "$") + install(FILES "$.dSYM" DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-sdl-dbg) + endif() +endif() diff --git a/src/platform/sdl/main.c b/src/platform/sdl/main.c index 7da3e8243..8666a8aa6 100644 --- a/src/platform/sdl/main.c +++ b/src/platform/sdl/main.c @@ -57,7 +57,6 @@ int main(int argc, char** argv) { .useBios = true, .rewindEnable = true, .rewindBufferCapacity = 600, - .rewindSave = true, .audioBuffers = 1024, .videoSync = false, .audioSync = true, diff --git a/src/platform/switch/CMakeLists.txt b/src/platform/switch/CMakeLists.txt index 8b511705e..f33d6a937 100644 --- a/src/platform/switch/CMakeLists.txt +++ b/src/platform/switch/CMakeLists.txt @@ -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 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) 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) 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 COMMAND ${NACPTOOL} --create "${PROJECT_NAME}" "endrift" "${VERSION_STRING}" control.nacp) diff --git a/src/platform/switch/gui-font.c b/src/platform/switch/gui-font.c index 814c0c63a..af08ece63 100644 --- a/src/platform/switch/gui-font.c +++ b/src/platform/switch/gui-font.c @@ -9,7 +9,7 @@ #include #include -#include +#include #define GLYPH_HEIGHT 24 #define CELL_HEIGHT 32 @@ -58,7 +58,7 @@ struct GUIFont { GLuint font; GLuint program; GLuint vbo; - GLuint offsetLocation; + GLuint vao; GLuint texLocation; GLuint dimsLocation; GLuint transformLocation; @@ -166,12 +166,16 @@ struct GUIFont* GUIFontCreate(void) { font->originLocation = glGetUniformLocation(font->program, "origin"); font->glyphLocation = glGetUniformLocation(font->program, "glyph"); font->cutoffLocation = glGetUniformLocation(font->program, "cutoff"); - font->offsetLocation = glGetAttribLocation(font->program, "offset"); + GLuint offsetLocation = glGetAttribLocation(font->program, "offset"); glGenBuffers(1, &font->vbo); + glGenVertexArrays(1, &font->vao); + glBindVertexArray(font->vao); glBindBuffer(GL_ARRAY_BUFFER, font->vbo); 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; } @@ -180,6 +184,7 @@ void GUIFontDestroy(struct GUIFont* font) { glDeleteBuffers(1, &font->vbo); glDeleteProgram(font->program); glDeleteTextures(1, &font->font); + glDeleteVertexArrays(1, &font->vao); free(font); } @@ -222,9 +227,9 @@ void GUIFontDrawGlyph(const struct GUIFont* font, int x, int y, uint32_t color, struct GUIFontGlyphMetric metric = defaultFontMetrics[glyph]; glUseProgram(font->program); + glBindVertexArray(font->vao); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, font->font); - glBindBuffer(GL_ARRAY_BUFFER, font->vbo); glEnable(GL_BLEND); 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); 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); glUniform4f(font->colorLocation, 0.0, 0.0, 0.0, ((color >> 24) & 0xFF) / 128.0f); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); 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); - glDisableVertexAttribArray(font->offsetLocation); - glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); glUseProgram(0); } @@ -291,9 +292,9 @@ void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment } glUseProgram(font->program); + glBindVertexArray(font->vao); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, font->font); - glBindBuffer(GL_ARRAY_BUFFER, font->vbo); glEnable(GL_BLEND); 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); 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); glUniform4f(font->colorLocation, 0.0, 0.0, 0.0, ((color >> 24) & 0xFF) / 128.0f); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); 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); - glDisableVertexAttribArray(font->offsetLocation); - glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); glUseProgram(0); } @@ -334,9 +331,9 @@ void GUIFontDrawIconSize(const struct GUIFont* font, int x, int y, int w, int h, } glUseProgram(font->program); + glBindVertexArray(font->vao); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, font->font); - glBindBuffer(GL_ARRAY_BUFFER, font->vbo); glEnable(GL_BLEND); 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); 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); glUniform4f(font->colorLocation, 0.0, 0.0, 0.0, ((color >> 24) & 0xFF) / 128.0f); 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); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glDisableVertexAttribArray(font->offsetLocation); - glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); glUseProgram(0); } diff --git a/src/platform/switch/main.c b/src/platform/switch/main.c index cc6b43111..55be2f910 100644 --- a/src/platform/switch/main.c +++ b/src/platform/switch/main.c @@ -10,15 +10,17 @@ #include #include #include +#include #include #include -#include +#include #define AUTO_INPUT 0x4E585031 #define SAMPLES 0x400 #define BUFFER_SIZE 0x1000 #define N_BUFFERS 4 +#define ANALOG_DEADZONE 0x4000 TimeType __nx_time_type = TimeType_UserSystemClock; @@ -63,21 +65,23 @@ static const char* const _fragmentShader = static GLuint program; static GLuint vbo; -static GLuint offsetLocation; +static GLuint vao; +static GLuint pbo; static GLuint texLocation; static GLuint dimsLocation; static GLuint insizeLocation; static GLuint colorLocation; static GLuint tex; -static color_t frameBuffer[256 * 256]; +static color_t* frameBuffer; static struct mAVStream stream; static int audioBufferActive; static struct GBAStereoSample audioBuffer[N_BUFFERS][SAMPLES] __attribute__((__aligned__(0x1000))); static AudioOutBuffer audoutBuffer[N_BUFFERS]; static int enqueuedBuffers; static bool frameLimiter = true; -static int framecount = 0; +static unsigned framecount = 0; +static unsigned framecap = 10; static bool initEgl() { s_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); @@ -105,11 +109,11 @@ static bool initEgl() { goto _fail1; } - //EGLint contextAttributeList[] = { - // EGL_CONTEXT_CLIENT_VERSION, 2, - // EGL_NONE - //}; - s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, NULL); + EGLint contextAttributeList[] = { + EGL_CONTEXT_CLIENT_VERSION, 3, + EGL_NONE + }; + s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, contextAttributeList); if (!s_context) { goto _fail2; } @@ -144,12 +148,13 @@ static void _mapKey(struct mInputMap* map, uint32_t binding, int nativeKey, enum } static void _drawStart(void) { - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glClear(GL_COLOR_BUFFER_BIT); } static void _drawEnd(void) { - if (frameLimiter || (framecount & 3) == 0) { + if (frameLimiter || framecount >= framecap) { eglSwapBuffers(s_display, s_surface); + framecount = 0; } } @@ -158,6 +163,40 @@ static uint32_t _pollInput(const struct mInputMap* map) { hidScanInput(); u32 padkeys = hidKeysHeld(CONTROLLER_P1_AUTO); 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; } @@ -196,15 +235,16 @@ static void _gameLoaded(struct mGUIRunner* runner) { 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, 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) { - glBindBuffer(GL_ARRAY_BUFFER, vbo); - glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glUseProgram(program); + glBindVertexArray(vao); float aspectX = width / (float) runner->params.width; float aspectY = height / (float) runner->params.height; 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); } - glVertexAttribPointer(offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); - glEnableVertexAttribArray(offsetLocation); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glDisableVertexAttribArray(offsetLocation); - glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(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) { - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, tex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, frameBuffer); + ++framecount; + if (!frameLimiter && framecount < framecap) { + return; + } unsigned 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) { @@ -257,11 +310,7 @@ static void _drawScreenshot(struct mGUIRunner* runner, const color_t* pixels, un } static uint16_t _pollGameInput(struct mGUIRunner* runner) { - int keys = 0; - hidScanInput(); - u32 padkeys = hidKeysHeld(CONTROLLER_P1_AUTO); - keys |= mInputMapKeyBits(&runner->core->inputMap, AUTO_INPUT, padkeys, 0); - return keys; + return _pollInput(&runner->core->inputMap); } static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) { @@ -310,7 +359,13 @@ static int _batteryState(void) { u32 charge; int state = 0; 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; } @@ -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_MIN_FILTER, GL_LINEAR); 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(); GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); @@ -382,12 +444,16 @@ int main(int argc, char* argv[]) { colorLocation = glGetUniformLocation(program, "color"); dimsLocation = glGetUniformLocation(program, "dims"); insizeLocation = glGetUniformLocation(program, "insize"); - offsetLocation = glGetAttribLocation(program, "offset"); + GLuint offsetLocation = glGetAttribLocation(program, "offset"); glGenBuffers(1, &vbo); + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); glBindBuffer(GL_ARRAY_BUFFER, vbo); 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.postVideoFrame = NULL; @@ -451,12 +517,26 @@ int main(int argc, char* argv[]) { }, { .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, .teardown = NULL, .gameLoaded = _gameLoaded, .gameUnloaded = NULL, - .prepareForFrame = NULL, + .prepareForFrame = _prepareForFrame, .drawFrame = _drawFrame, .drawScreenshot = _drawScreenshot, .paused = NULL, @@ -477,7 +557,25 @@ int main(int argc, char* argv[]) { _mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_DRIGHT, GUI_INPUT_RIGHT); audoutStartAudioOut(); - mGUIRunloop(&runner); + + 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); + } + + 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(); audoutExit(); diff --git a/src/platform/test/perf-main.c b/src/platform/test/perf-main.c index e613a9e10..1f444e049 100644 --- a/src/platform/test/perf-main.c +++ b/src/platform/test/perf-main.c @@ -29,11 +29,12 @@ #include #include -#define PERF_OPTIONS "DF:L:NPS:" +#define PERF_OPTIONS "DF:L:NPS:T" #define PERF_USAGE \ "\nBenchmark options:\n" \ " -F FRAMES Run for the specified number of FRAMES before exiting\n" \ " -N Disable video rendering entirely\n" \ + " -T Use threaded video rendering\n" \ " -P CSV output, useful for parsing\n" \ " -S SEC Run for SEC in-game seconds before exiting\n" \ " -L FILE Load a savestate when starting the test\n" \ @@ -41,6 +42,7 @@ struct PerfOpts { bool noVideo; + bool threadedVideo; bool csv; unsigned duration; unsigned frames; @@ -89,7 +91,7 @@ int main(int argc, char** argv) { struct mLogger logger = { .log = _log }; mLogSetDefaultLogger(&logger); - struct PerfOpts perfOpts = { false, false, 0, 0, 0, false }; + struct PerfOpts perfOpts = { false, false, false, 0, 0, 0, false }; struct mSubParser subparser = { .usage = PERF_USAGE, .parse = _parsePerfOpts, @@ -162,6 +164,12 @@ bool _mPerfRunCore(const char* fname, const struct mArguments* args, const struc mCoreConfigInit(&core->config, "perf"); mCoreConfigLoad(&core->config); + if (perfOpts->threadedVideo) { + mCoreConfigSetOverrideIntValue(&core->config, "threadedVideo", 1); + } else { + mCoreConfigSetOverrideIntValue(&core->config, "threadedVideo", 0); + } + struct mCoreOptions opts = {}; mCoreConfigMap(&core->config, &opts); opts.audioSync = false; @@ -200,6 +208,8 @@ bool _mPerfRunCore(const char* fname, const struct mArguments* args, const struc const char* rendererName; if (perfOpts->noVideo) { rendererName = "none"; + } else if (perfOpts->threadedVideo) { + rendererName = "threaded-software"; } else { rendererName = "software"; } @@ -313,6 +323,9 @@ static bool _parsePerfOpts(struct mSubParser* parser, int option, const char* ar case 'S': opts->duration = strtoul(arg, 0, 10); return !errno; + case 'T': + opts->threadedVideo = true; + return true; case 'L': opts->savestate = strdup(arg); return true; diff --git a/src/platform/wii/main.c b/src/platform/wii/main.c index 185771dd6..1885f5c37 100644 --- a/src/platform/wii/main.c +++ b/src/platform/wii/main.c @@ -229,15 +229,6 @@ static void reconfigureScreen(struct mGUIRunner* runner) { 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, 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) { - UNUSED(runner); + runner->core->desiredVideoDimensions(runner->core, &corew, &coreh); uint32_t color = 0xFFFFFF3F; if (!faded) { color |= 0xC0; @@ -838,9 +829,9 @@ void _drawFrame(struct mGUIRunner* runner, bool faded) { GX_InvalidateTexAll(); GX_LoadTexObj(&tex, GX_TEXMAP0); - GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0); - s16 vertWidth = TEX_W; - s16 vertHeight = TEX_H; + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0); + s16 vertWidth = corew; + s16 vertHeight = coreh; if (filterMode == FM_LINEAR_2x) { Mtx44 proj; @@ -850,19 +841,19 @@ void _drawFrame(struct mGUIRunner* runner, bool faded) { GX_Begin(GX_QUADS, GX_VTXFMT0, 4); GX_Position2s16(0, TEX_H * 2); GX_Color1u32(0xFFFFFFFF); - GX_TexCoord2s16(0, 1); + GX_TexCoord2f32(0, 1); GX_Position2s16(TEX_W * 2, TEX_H * 2); GX_Color1u32(0xFFFFFFFF); - GX_TexCoord2s16(1, 1); + GX_TexCoord2f32(1, 1); GX_Position2s16(TEX_W * 2, 0); GX_Color1u32(0xFFFFFFFF); - GX_TexCoord2s16(1, 0); + GX_TexCoord2f32(1, 0); GX_Position2s16(0, 0); GX_Color1u32(0xFFFFFFFF); - GX_TexCoord2s16(0, 0); + GX_TexCoord2f32(0, 0); GX_End(); 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); } + int hfactor = vmode->fbWidth / (corew * wAdjust); + int vfactor = vmode->efbHeight / (coreh * hAdjust); + if (hfactor > vfactor) { + scaleFactor = vfactor; + } else { + scaleFactor = hfactor; + } + if (screenMode == SM_PA) { vertWidth *= scaleFactor; vertHeight *= scaleFactor; @@ -885,19 +884,19 @@ void _drawFrame(struct mGUIRunner* runner, bool faded) { GX_Begin(GX_QUADS, GX_VTXFMT0, 4); GX_Position2s16(0, vertHeight); GX_Color1u32(color); - GX_TexCoord2s16(0, 1); + GX_TexCoord2f32(0, coreh / (float) TEX_H); GX_Position2s16(vertWidth, vertHeight); GX_Color1u32(color); - GX_TexCoord2s16(1, 1); + GX_TexCoord2f32(corew / (float) TEX_W, coreh / (float) TEX_H); GX_Position2s16(vertWidth, 0); GX_Color1u32(color); - GX_TexCoord2s16(1, 0); + GX_TexCoord2f32(corew / (float) TEX_W, 0); GX_Position2s16(0, 0); GX_Color1u32(color); - GX_TexCoord2s16(0, 0); + GX_TexCoord2f32(0, 0); GX_End(); } diff --git a/src/util/gui.c b/src/util/gui.c index ec37f99e5..8d5a1ec59 100644 --- a/src/util/gui.c +++ b/src/util/gui.c @@ -5,6 +5,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#define KEY_DELAY 45 +#define KEY_REPEAT 5 + void GUIInit(struct GUIParams* params) { memset(params->inputHistory, 0, sizeof(params->inputHistory)); strncpy(params->currentPath, params->basePath, PATH_MAX); @@ -19,7 +22,7 @@ void GUIPollInput(struct GUIParams* params, uint32_t* newInputOut, uint32_t* hel } else { 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); } } diff --git a/src/util/gui/menu.c b/src/util/gui/menu.c index f9cba3d36..68ba7781f 100644 --- a/src/util/gui/menu.c +++ b/src/util/gui/menu.c @@ -260,6 +260,9 @@ void GUIDrawBattery(struct GUIParams* params) { return; } int state = params->batteryState(); + if (state == BATTERY_NOT_PRESENT) { + return; + } uint32_t color = 0xFF000000; if (state == (BATTERY_CHARGING | BATTERY_FULL)) { color |= 0xFFC060; diff --git a/src/util/ring-fifo.c b/src/util/ring-fifo.c index 167690c61..bdaac75aa 100644 --- a/src/util/ring-fifo.c +++ b/src/util/ring-fifo.c @@ -22,6 +22,18 @@ size_t RingFIFOCapacity(const struct RingFIFO* buffer) { 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) { ATOMIC_STORE(buffer->readPtr, 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); // Wrap around if we can't fit enough in here - if ((intptr_t) data - (intptr_t) buffer->data + length >= buffer->capacity) { - if (end == buffer->data) { + if ((uintptr_t) data - (uintptr_t) buffer->data + length >= buffer->capacity) { + if (end == buffer->data || end > data) { // Oops! If we wrap now, it'll appear empty return 0; } @@ -65,8 +77,8 @@ size_t RingFIFORead(struct RingFIFO* buffer, void* output, size_t length) { ATOMIC_LOAD(end, buffer->writePtr); // Wrap around if we can't fit enough in here - if ((intptr_t) data - (intptr_t) buffer->data + length >= buffer->capacity) { - if (end == data) { + if ((uintptr_t) data - (uintptr_t) buffer->data + length >= buffer->capacity) { + if (end >= data) { // Oops! If we wrap now, it'll appear full 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; remaining = bufferEnd - (uintptr_t) data; } else { - remaining = (intptr_t) end - (intptr_t) data; + remaining = (uintptr_t) end - (uintptr_t) data; } // If the pointers touch, it's empty if (remaining < length) { @@ -87,6 +99,6 @@ size_t RingFIFORead(struct RingFIFO* buffer, void* output, size_t length) { if (output) { memcpy(output, data, length); } - ATOMIC_STORE(buffer->readPtr, (void*) ((intptr_t) data + length)); + ATOMIC_STORE(buffer->readPtr, (void*) ((uintptr_t) data + length)); return length; }