Merge branch 'master' (early part) into medusa

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

26
CHANGES
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -816,6 +816,18 @@ void GBGetGameCode(const struct GB* gb, char* out) {
}
}
void GBFrameStarted(struct GB* gb) {
GBTestKeypadIRQ(gb);
size_t c;
for (c = 0; c < mCoreCallbacksListSize(&gb->coreCallbacks); ++c) {
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gb->coreCallbacks, c);
if (callbacks->videoFrameStarted) {
callbacks->videoFrameStarted(callbacks->context);
}
}
}
void GBFrameEnded(struct GB* gb) {
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) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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