mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' (early part) into medusa
This commit is contained in:
commit
e6aa23f19c
26
CHANGES
26
CHANGES
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE)
|
if(APPLE)
|
||||||
|
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)
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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(¶ms->keyMap, keys->id, item->state - 1, (uint32_t) item->data - 1);
|
mInputBindKey(¶ms->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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
|
||||||
|
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);
|
mTimingSchedule(timing, &audio->ch4Event, cycles - cyclesLate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
proxyRenderer->backend->finishFrame(proxyRenderer->backend);
|
if (!proxyRenderer->logger->block) {
|
||||||
|
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) {
|
||||||
|
|
28
src/gb/gb.c
28
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) {
|
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) {
|
||||||
|
|
16
src/gb/mbc.c
16
src/gb/mbc.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
proxyRenderer->backend->finishFrame(proxyRenderer->backend);
|
if (!proxyRenderer->logger->block) {
|
||||||
|
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) {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,24 +606,49 @@ 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
|
||||||
char realCode[] = "XXXXXXXX XXXXXXXX";
|
#ifdef M_CORE_GBA
|
||||||
size_t len = strlen(code) + 1; // Include null terminator
|
if (core->platform(core) == PLATFORM_GBA) {
|
||||||
size_t i, pos;
|
char realCode[] = "XXXXXXXX XXXXXXXX";
|
||||||
for (i = 0, pos = 0; i < len; ++i) {
|
size_t len = strlen(code) + 1; // Include null terminator
|
||||||
if (isspace((int) code[i]) || code[i] == '+') {
|
size_t i, pos;
|
||||||
realCode[pos] = ' ';
|
for (i = 0, pos = 0; i < len; ++i) {
|
||||||
} else {
|
if (isspace((int) code[i]) || code[i] == '+') {
|
||||||
realCode[pos] = 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) {
|
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) {
|
||||||
|
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;
|
camWidth = width;
|
||||||
camHeight = height;
|
camHeight = bufHeight;
|
||||||
camStride = pitch / sizeof(*buffer);
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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 &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>&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>&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>&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>&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>&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>&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>
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
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();
|
psmExit();
|
||||||
audoutExit();
|
audoutExit();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue