Merge branch 'master' into shader-decomp

This commit is contained in:
wheremyfoodat 2024-10-06 15:50:15 +03:00
commit 2b82f8b332
29 changed files with 583 additions and 204 deletions

View file

@ -111,6 +111,7 @@ std::string FragmentGenerator::generate(const FragmentConfig& config) {
if (api == API::GLES) {
ret += R"(
#define USING_GLES 1
#define fma(a, b, c) ((a) * (b) + (c))
precision mediump int;
precision mediump float;
@ -506,7 +507,7 @@ void FragmentGenerator::compileLights(std::string& shader, const PICA::FragmentC
"].distanceAttenuationScale + lightSources[" + std::to_string(lightID) + "].distanceAttenuationBias, 0.0, 1.0);\n";
shader += "distance_attenuation = lutLookup(" + std::to_string(16 + lightID) +
", int(clamp(floor(distance_att_delta * 256.0), 0.0, 255.0)));\n";
"u, int(clamp(floor(distance_att_delta * 256.0), 0.0, 255.0)));\n";
}
compileLUTLookup(shader, config, i, spotlightLutIndex);
@ -641,7 +642,7 @@ void FragmentGenerator::compileLUTLookup(std::string& shader, const PICA::Fragme
if (absEnabled) {
bool twoSidedDiffuse = config.lighting.lights[lightIndex].twoSidedDiffuse;
shader += twoSidedDiffuse ? "lut_lookup_delta = abs(lut_lookup_delta);\n" : "lut_lookup_delta = max(lut_lookup_delta, 0.0);\n";
shader += "lut_lookup_result = lutLookup(" + std::to_string(lutIndex) + ", int(clamp(floor(lut_lookup_delta * 256.0), 0.0, 255.0)));\n";
shader += "lut_lookup_result = lutLookup(" + std::to_string(lutIndex) + "u, int(clamp(floor(lut_lookup_delta * 256.0), 0.0, 255.0)));\n";
if (scale != 0) {
shader += "lut_lookup_result *= " + std::to_string(scales[scale]) + ";\n";
}
@ -649,7 +650,7 @@ void FragmentGenerator::compileLUTLookup(std::string& shader, const PICA::Fragme
// Range is [-1, 1] so we need to map it to [0, 1]
shader += "lut_lookup_index = int(clamp(floor(lut_lookup_delta * 128.0), -128.f, 127.f));\n";
shader += "if (lut_lookup_index < 0) lut_lookup_index += 256;\n";
shader += "lut_lookup_result = lutLookup(" + std::to_string(lutIndex) + ", lut_lookup_index);\n";
shader += "lut_lookup_result = lutLookup(" + std::to_string(lutIndex) + "u, lut_lookup_index);\n";
if (scale != 0) {
shader += "lut_lookup_result *= " + std::to_string(scales[scale]) + ";\n";
}

View file

@ -0,0 +1,139 @@
#include "audio/aac_decoder.hpp"
#include <aacdecoder_lib.h>
#include <vector>
using namespace Audio;
void AAC::Decoder::decode(AAC::Message& response, const AAC::Message& request, AAC::Decoder::PaddrCallback paddrCallback) {
// Copy the command and mode fields of the request to the response
response.command = request.command;
response.mode = request.mode;
response.decodeResponse.size = request.decodeRequest.size;
// Write a dummy response at first. We'll be overwriting it later if decoding goes well
response.resultCode = AAC::ResultCode::Success;
response.decodeResponse.channelCount = 2;
response.decodeResponse.sampleCount = 1024;
response.decodeResponse.sampleRate = AAC::SampleRate::Rate48000;
if (!isInitialized()) {
initialize();
// AAC decoder failed to initialize, return dummy data and return without decoding
if (!isInitialized()) {
Helpers::warn("Failed to initialize AAC decoder");
return;
}
}
u8* input = paddrCallback(request.decodeRequest.address);
const u8* inputEnd = paddrCallback(request.decodeRequest.address + request.decodeRequest.size);
u8* outputLeft = paddrCallback(request.decodeRequest.destAddrLeft);
u8* outputRight = nullptr;
if (input == nullptr || inputEnd == nullptr || outputLeft == nullptr) {
Helpers::warn("Invalid pointers passed to AAC decoder");
return;
}
u32 bytesValid = request.decodeRequest.size;
u32 bufferSize = request.decodeRequest.size;
// Each frame is 2048 samples with 2 channels
static constexpr usize frameSize = 2048 * 2;
std::array<s16, frameSize> frame;
std::array<std::vector<s16>, 2> audioStreams;
bool queriedStreamInfo = false;
while (bytesValid != 0) {
if (aacDecoder_Fill(decoderHandle, &input, &bufferSize, &bytesValid) != AAC_DEC_OK) {
Helpers::warn("Failed to fill AAC decoder with samples");
return;
}
auto decodeResult = aacDecoder_DecodeFrame(decoderHandle, frame.data(), frameSize, 0);
if (decodeResult == AAC_DEC_TRANSPORT_SYNC_ERROR) {
// https://android.googlesource.com/platform/external/aac/+/2ddc922/libAACdec/include/aacdecoder_lib.h#362
// According to the above, if we get a sync error, we're not meant to stop decoding, but rather just continue feeding data
} else if (decodeResult == AAC_DEC_OK) {
auto getSampleRate = [](u32 rate) {
switch (rate) {
case 8000: return AAC::SampleRate::Rate8000;
case 11025: return AAC::SampleRate::Rate11025;
case 12000: return AAC::SampleRate::Rate12000;
case 16000: return AAC::SampleRate::Rate16000;
case 22050: return AAC::SampleRate::Rate22050;
case 24000: return AAC::SampleRate::Rate24000;
case 32000: return AAC::SampleRate::Rate32000;
case 44100: return AAC::SampleRate::Rate44100;
case 48000:
default: return AAC::SampleRate::Rate48000;
}
};
auto info = aacDecoder_GetStreamInfo(decoderHandle);
response.decodeResponse.sampleCount = info->frameSize;
response.decodeResponse.channelCount = info->numChannels;
response.decodeResponse.sampleRate = getSampleRate(info->sampleRate);
int channels = info->numChannels;
// Reserve space in our output stream vectors so push_back doesn't do allocations
for (int i = 0; i < channels; i++) {
audioStreams[i].reserve(audioStreams[i].size() + info->frameSize);
}
// Fetch output pointer for right output channel if we've got > 1 channel
if (channels > 1 && outputRight == nullptr) {
outputRight = paddrCallback(request.decodeRequest.destAddrRight);
// If the right output channel doesn't point to a proper padddr, return
if (outputRight == nullptr) {
Helpers::warn("Right AAC output channel doesn't point to valid physical address");
return;
}
}
for (int sample = 0; sample < info->frameSize; sample++) {
for (int stream = 0; stream < channels; stream++) {
audioStreams[stream].push_back(frame[(sample * channels) + stream]);
}
}
} else {
Helpers::warn("Failed to decode AAC frame");
return;
}
}
for (int i = 0; i < 2; i++) {
auto& stream = audioStreams[i];
u8* pointer = (i == 0) ? outputLeft : outputRight;
if (!stream.empty() && pointer != nullptr) {
std::memcpy(pointer, stream.data(), stream.size() * sizeof(s16));
}
}
}
void AAC::Decoder::initialize() {
decoderHandle = aacDecoder_Open(TRANSPORT_TYPE::TT_MP4_ADTS, 1);
if (decoderHandle == nullptr) [[unlikely]] {
return;
}
// Cap output channel count to 2
if (aacDecoder_SetParam(decoderHandle, AAC_PCM_MAX_OUTPUT_CHANNELS, 2) != AAC_DEC_OK) [[unlikely]] {
aacDecoder_Close(decoderHandle);
decoderHandle = nullptr;
return;
}
}
AAC::Decoder::~Decoder() {
if (isInitialized()) {
aacDecoder_Close(decoderHandle);
decoderHandle = nullptr;
}
}

View file

@ -6,6 +6,7 @@
#include <thread>
#include <utility>
#include "audio/aac_decoder.hpp"
#include "services/dsp.hpp"
namespace Audio {
@ -23,6 +24,8 @@ namespace Audio {
for (int i = 0; i < sources.size(); i++) {
sources[i].index = i;
}
aacDecoder.reset(new Audio::AAC::Decoder());
}
void HLE_DSP::resetAudioPipe() {
@ -73,6 +76,7 @@ namespace Audio {
source.reset();
}
mixer.reset();
// Note: Reset audio pipe AFTER resetting all pipes, otherwise the new data will be yeeted
resetAudioPipe();
}
@ -247,6 +251,8 @@ namespace Audio {
source.isBufferIDDirty = false;
}
performMix(read, write);
}
void HLE_DSP::updateSourceConfig(Source& source, HLE::SourceConfiguration::Configuration& config, s16_le* adpcmCoefficients) {
@ -462,6 +468,50 @@ namespace Audio {
}
}
void HLE_DSP::performMix(Audio::HLE::SharedMemory& readRegion, Audio::HLE::SharedMemory& writeRegion) {
updateMixerConfig(readRegion);
// TODO: Do the actual audio mixing
auto& dspStatus = writeRegion.dspStatus;
// Stub the DSP status. It's unknown what the "unknown" field is but Citra sets it to 0, so we do too to be safe
dspStatus.droppedFrames = 0;
dspStatus.unknown = 0;
}
void HLE_DSP::updateMixerConfig(Audio::HLE::SharedMemory& sharedMem) {
auto& config = sharedMem.dspConfiguration;
// No configs have been changed, so there's nothing to update
if (config.dirtyRaw == 0) {
return;
}
if (config.outputFormatDirty) {
mixer.channelFormat = config.outputFormat;
}
if (config.masterVolumeDirty) {
mixer.volumes[0] = config.masterVolume;
}
if (config.auxVolume0Dirty) {
mixer.volumes[1] = config.auxVolumes[0];
}
if (config.auxVolume1Dirty) {
mixer.volumes[2] = config.auxVolumes[1];
}
if (config.auxBusEnable0Dirty) {
mixer.enableAuxStages[0] = config.auxBusEnable[0] != 0;
}
if (config.auxBusEnable1Dirty) {
mixer.enableAuxStages[1] = config.auxBusEnable[1] != 0;
}
config.dirtyRaw = 0;
}
HLE_DSP::SampleBuffer HLE_DSP::decodePCM8(const u8* data, usize sampleCount, Source& source) {
SampleBuffer decodedSamples(sampleCount);
@ -584,7 +634,6 @@ namespace Audio {
switch (request.command) {
case AAC::Command::EncodeDecode:
// Dummy response to stop games from hanging
// TODO: Fix this when implementing AAC
response.resultCode = AAC::ResultCode::Success;
response.decodeResponse.channelCount = 2;
response.decodeResponse.sampleCount = 1024;
@ -593,6 +642,10 @@ namespace Audio {
response.command = request.command;
response.mode = request.mode;
// We've already got an AAC decoder but it's currently disabled until mixing & output is properly implemented
// TODO: Uncomment this when the time comes
// aacDecoder->decode(response, request, [this](u32 paddr) { return getPointerPhys<u8>(paddr); });
break;
case AAC::Command::Init:

View file

@ -90,16 +90,17 @@ void MiniAudioDevice::init(Samples& samples, bool safe) {
deviceConfig.dataCallback = [](ma_device* device, void* out, const void* input, ma_uint32 frameCount) {
auto self = reinterpret_cast<MiniAudioDevice*>(device->pUserData);
s16* output = reinterpret_cast<ma_int16*>(out);
const usize maxSamples = std::min(self->samples->Capacity(), usize(frameCount * channelCount));
// Wait until there's enough samples to pop
while (self->samples->size() < frameCount * channelCount) {
while (self->samples->size() < maxSamples) {
// If audio output is disabled from the emulator thread, make sure that this callback will return and not hang
if (!self->running) {
return;
}
}
self->samples->pop(output, frameCount * channelCount);
self->samples->pop(output, maxSamples);
};
if (ma_device_init(&context, &deviceConfig, &device) != MA_SUCCESS) {

View file

@ -51,7 +51,7 @@ void RendererGL::reset() {
gl.useProgram(oldProgram); // Switch to old GL program
}
#ifdef __ANDROID__
#ifdef USING_GLES
fragShaderGen.setTarget(PICA::ShaderGen::API::GLES, PICA::ShaderGen::Language::GLSL);
#endif
}

View file

@ -114,7 +114,7 @@ hydra::Size HydraCore::getNativeSize() { return {400, 480}; }
void HydraCore::setOutputSize(hydra::Size size) {}
void HydraCore::resetContext() {
#ifdef __ANDROID__
#ifdef USING_GLES
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(getProcAddress))) {
Helpers::panic("OpenGL ES init failed");
}

View file

@ -8,13 +8,13 @@
#include <emulator.hpp>
#include <renderer_gl/renderer_gl.hpp>
static retro_environment_t envCallbacks;
static retro_video_refresh_t videoCallbacks;
static retro_environment_t envCallback;
static retro_video_refresh_t videoCallback;
static retro_audio_sample_batch_t audioBatchCallback;
static retro_input_poll_t inputPollCallback;
static retro_input_state_t inputStateCallback;
static retro_hw_render_callback hw_render;
static retro_hw_render_callback hwRender;
static std::filesystem::path savePath;
static bool screenTouched;
@ -30,17 +30,17 @@ std::filesystem::path Emulator::getAppDataRoot() {
return std::filesystem::path(savePath / "Emulator Files");
}
static void* GetGLProcAddress(const char* name) {
return (void*)hw_render.get_proc_address(name);
static void* getGLProcAddress(const char* name) {
return (void*)hwRender.get_proc_address(name);
}
static void VideoResetContext() {
static void videoResetContext() {
#ifdef USING_GLES
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(GetGLProcAddress))) {
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(getGLProcAddress))) {
Helpers::panic("OpenGL ES init failed");
}
#else
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(GetGLProcAddress))) {
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(getGLProcAddress))) {
Helpers::panic("OpenGL init failed");
}
#endif
@ -48,31 +48,31 @@ static void VideoResetContext() {
emulator->initGraphicsContext(nullptr);
}
static void VideoDestroyContext() {
emulator->deinitGraphicsContext();
static void videoDestroyContext() {
emulator->deinitGraphicsContext();
}
static bool SetHWRender(retro_hw_context_type type) {
hw_render.context_type = type;
hw_render.context_reset = VideoResetContext;
hw_render.context_destroy = VideoDestroyContext;
hw_render.bottom_left_origin = true;
static bool setHWRender(retro_hw_context_type type) {
hwRender.context_type = type;
hwRender.context_reset = videoResetContext;
hwRender.context_destroy = videoDestroyContext;
hwRender.bottom_left_origin = true;
switch (type) {
case RETRO_HW_CONTEXT_OPENGL_CORE:
hw_render.version_major = 4;
hw_render.version_minor = 1;
hwRender.version_major = 4;
hwRender.version_minor = 1;
if (envCallbacks(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render)) {
if (envCallback(RETRO_ENVIRONMENT_SET_HW_RENDER, &hwRender)) {
return true;
}
break;
case RETRO_HW_CONTEXT_OPENGLES3:
case RETRO_HW_CONTEXT_OPENGL:
hw_render.version_major = 3;
hw_render.version_minor = 1;
hwRender.version_major = 3;
hwRender.version_minor = 1;
if (envCallbacks(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render)) {
if (envCallback(RETRO_ENVIRONMENT_SET_HW_RENDER, &hwRender)) {
return true;
}
break;
@ -84,18 +84,18 @@ static bool SetHWRender(retro_hw_context_type type) {
static void videoInit() {
retro_hw_context_type preferred = RETRO_HW_CONTEXT_NONE;
envCallbacks(RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER, &preferred);
envCallback(RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER, &preferred);
if (preferred && SetHWRender(preferred)) return;
if (SetHWRender(RETRO_HW_CONTEXT_OPENGL_CORE)) return;
if (SetHWRender(RETRO_HW_CONTEXT_OPENGL)) return;
if (SetHWRender(RETRO_HW_CONTEXT_OPENGLES3)) return;
if (preferred && setHWRender(preferred)) return;
if (setHWRender(RETRO_HW_CONTEXT_OPENGL_CORE)) return;
if (setHWRender(RETRO_HW_CONTEXT_OPENGL)) return;
if (setHWRender(RETRO_HW_CONTEXT_OPENGLES3)) return;
hw_render.context_type = RETRO_HW_CONTEXT_NONE;
hwRender.context_type = RETRO_HW_CONTEXT_NONE;
}
static bool GetButtonState(uint id) { return inputStateCallback(0, RETRO_DEVICE_JOYPAD, 0, id); }
static float GetAxisState(uint index, uint id) { return inputStateCallback(0, RETRO_DEVICE_ANALOG, index, id); }
static bool getButtonState(uint id) { return inputStateCallback(0, RETRO_DEVICE_JOYPAD, 0, id); }
static float getAxisState(uint index, uint id) { return inputStateCallback(0, RETRO_DEVICE_ANALOG, index, id); }
static void inputInit() {
static const retro_controller_description controllers[] = {
@ -108,7 +108,7 @@ static void inputInit() {
{NULL, 0},
};
envCallbacks(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports);
envCallback(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports);
retro_input_descriptor desc[] = {
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left"},
@ -128,14 +128,14 @@ static void inputInit() {
{0},
};
envCallbacks(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, &desc);
envCallback(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, &desc);
}
static std::string FetchVariable(std::string key, std::string def) {
static std::string fetchVariable(std::string key, std::string def) {
retro_variable var = {nullptr};
var.key = key.c_str();
if (!envCallbacks(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value == nullptr) {
if (!envCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value == nullptr) {
Helpers::warn("Fetching variable %s failed.", key.c_str());
return def;
}
@ -143,13 +143,27 @@ static std::string FetchVariable(std::string key, std::string def) {
return std::string(var.value);
}
static bool FetchVariableBool(std::string key, bool def) {
return FetchVariable(key, def ? "enabled" : "disabled") == "enabled";
static int fetchVariableInt(std::string key, int def) {
std::string value = fetchVariable(key, std::to_string(def));
if (!value.empty() && std::isdigit(value[0])) {
return std::stoi(value);
}
return 0;
}
static bool fetchVariableBool(std::string key, bool def) {
return fetchVariable(key, def ? "enabled" : "disabled") == "enabled";
}
static int fetchVariableRange(std::string key, int min, int max) {
return std::clamp(fetchVariableInt(key, min), min, max);
}
static void configInit() {
static const retro_variable values[] = {
{"panda3ds_use_shader_jit", "Enable shader JIT; enabled|disabled"},
{"panda3ds_use_shader_jit", EmulatorConfig::shaderJitDefault ? "Enable shader JIT; enabled|disabled" : "Enable shader JIT; disabled|enabled"},
{"panda3ds_accelerate_shaders",
EmulatorConfig::accelerateShadersDefault ? "Run 3DS shaders on the GPU; enabled|disabled" : "Run 3DS shaders on the GPU; disabled|enabled"},
{"panda3ds_accurate_shader_mul", "Enable accurate shader multiplication; disabled|enabled"},
@ -167,35 +181,35 @@ static void configInit() {
{nullptr, nullptr},
};
envCallbacks(RETRO_ENVIRONMENT_SET_VARIABLES, (void*)values);
envCallback(RETRO_ENVIRONMENT_SET_VARIABLES, (void*)values);
}
static void configUpdate() {
EmulatorConfig& config = emulator->getConfig();
config.rendererType = RendererType::OpenGL;
config.vsyncEnabled = FetchVariableBool("panda3ds_use_vsync", true);
config.shaderJitEnabled = FetchVariableBool("panda3ds_use_shader_jit", true);
config.chargerPlugged = FetchVariableBool("panda3ds_use_charger", true);
config.batteryPercentage = std::clamp(std::stoi(FetchVariable("panda3ds_battery_level", "5")), 0, 100);
config.dspType = Audio::DSPCore::typeFromString(FetchVariable("panda3ds_dsp_emulation", "null"));
config.audioEnabled = FetchVariableBool("panda3ds_use_audio", false);
config.sdCardInserted = FetchVariableBool("panda3ds_use_virtual_sd", true);
config.sdWriteProtected = FetchVariableBool("panda3ds_write_protect_virtual_sd", false);
config.accurateShaderMul = FetchVariableBool("panda3ds_accurate_shader_mul", false);
config.useUbershaders = FetchVariableBool("panda3ds_use_ubershader", EmulatorConfig::ubershaderDefault);
config.vsyncEnabled = fetchVariableBool("panda3ds_use_vsync", true);
config.shaderJitEnabled = fetchVariableBool("panda3ds_use_shader_jit", EmulatorConfig::shaderJitDefault);
config.chargerPlugged = fetchVariableBool("panda3ds_use_charger", true);
config.batteryPercentage = fetchVariableRange("panda3ds_battery_level", 5, 100);
config.dspType = Audio::DSPCore::typeFromString(fetchVariable("panda3ds_dsp_emulation", "null"));
config.audioEnabled = fetchVariableBool("panda3ds_use_audio", false);
config.sdCardInserted = fetchVariableBool("panda3ds_use_virtual_sd", true);
config.sdWriteProtected = fetchVariableBool("panda3ds_write_protect_virtual_sd", false);
config.accurateShaderMul = fetchVariableBool("panda3ds_accurate_shader_mul", false);
config.useUbershaders = fetchVariableBool("panda3ds_use_ubershader", EmulatorConfig::ubershaderDefault);
config.accelerateShaders = FetchVariableBool("panda3ds_accelerate_shaders", EmulatorConfig::accelerateShadersDefault);
config.forceShadergenForLights = FetchVariableBool("panda3ds_ubershader_lighting_override", true);
config.lightShadergenThreshold = std::clamp(std::stoi(FetchVariable("panda3ds_ubershader_lighting_override_threshold", "1")), 1, 8);
config.forceShadergenForLights = fetchVariableBool("panda3ds_ubershader_lighting_override", true);
config.lightShadergenThreshold = fetchVariableRange("panda3ds_ubershader_lighting_override_threshold", 1, 8);
config.discordRpcEnabled = false;
config.save();
}
static void ConfigCheckVariables() {
static void configCheckVariables() {
bool updated = false;
envCallbacks(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated);
envCallback(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated);
if (updated) {
configUpdate();
@ -207,7 +221,7 @@ void retro_get_system_info(retro_system_info* info) {
info->valid_extensions = "3ds|3dsx|elf|axf|cci|cxi|app";
info->library_version = PANDA3DS_VERSION;
info->library_name = "Panda3DS";
info->block_extract = true;
info->block_extract = false;
}
void retro_get_system_av_info(retro_system_av_info* info) {
@ -223,11 +237,11 @@ void retro_get_system_av_info(retro_system_av_info* info) {
}
void retro_set_environment(retro_environment_t cb) {
envCallbacks = cb;
envCallback = cb;
}
void retro_set_video_refresh(retro_video_refresh_t cb) {
videoCallbacks = cb;
videoCallback = cb;
}
void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) {
@ -246,15 +260,15 @@ void retro_set_input_state(retro_input_state_t cb) {
void retro_init() {
enum retro_pixel_format xrgb888 = RETRO_PIXEL_FORMAT_XRGB8888;
envCallbacks(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &xrgb888);
envCallback(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &xrgb888);
char* save_dir = nullptr;
char* saveDir = nullptr;
if (!envCallbacks(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &save_dir) || save_dir == nullptr) {
if (!envCallback(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &saveDir) || saveDir == nullptr) {
Helpers::warn("No save directory provided by LibRetro.");
savePath = std::filesystem::current_path();
} else {
savePath = std::filesystem::path(save_dir);
savePath = std::filesystem::path(saveDir);
}
emulator = std::make_unique<Emulator>();
@ -293,31 +307,31 @@ void retro_reset() {
}
void retro_run() {
ConfigCheckVariables();
configCheckVariables();
renderer->setFBO(hw_render.get_current_framebuffer());
renderer->setFBO(hwRender.get_current_framebuffer());
renderer->resetStateManager();
inputPollCallback();
HIDService& hid = emulator->getServiceManager().getHID();
hid.setKey(HID::Keys::A, GetButtonState(RETRO_DEVICE_ID_JOYPAD_A));
hid.setKey(HID::Keys::B, GetButtonState(RETRO_DEVICE_ID_JOYPAD_B));
hid.setKey(HID::Keys::X, GetButtonState(RETRO_DEVICE_ID_JOYPAD_X));
hid.setKey(HID::Keys::Y, GetButtonState(RETRO_DEVICE_ID_JOYPAD_Y));
hid.setKey(HID::Keys::L, GetButtonState(RETRO_DEVICE_ID_JOYPAD_L));
hid.setKey(HID::Keys::R, GetButtonState(RETRO_DEVICE_ID_JOYPAD_R));
hid.setKey(HID::Keys::Start, GetButtonState(RETRO_DEVICE_ID_JOYPAD_START));
hid.setKey(HID::Keys::Select, GetButtonState(RETRO_DEVICE_ID_JOYPAD_SELECT));
hid.setKey(HID::Keys::Up, GetButtonState(RETRO_DEVICE_ID_JOYPAD_UP));
hid.setKey(HID::Keys::Down, GetButtonState(RETRO_DEVICE_ID_JOYPAD_DOWN));
hid.setKey(HID::Keys::Left, GetButtonState(RETRO_DEVICE_ID_JOYPAD_LEFT));
hid.setKey(HID::Keys::Right, GetButtonState(RETRO_DEVICE_ID_JOYPAD_RIGHT));
hid.setKey(HID::Keys::A, getButtonState(RETRO_DEVICE_ID_JOYPAD_A));
hid.setKey(HID::Keys::B, getButtonState(RETRO_DEVICE_ID_JOYPAD_B));
hid.setKey(HID::Keys::X, getButtonState(RETRO_DEVICE_ID_JOYPAD_X));
hid.setKey(HID::Keys::Y, getButtonState(RETRO_DEVICE_ID_JOYPAD_Y));
hid.setKey(HID::Keys::L, getButtonState(RETRO_DEVICE_ID_JOYPAD_L));
hid.setKey(HID::Keys::R, getButtonState(RETRO_DEVICE_ID_JOYPAD_R));
hid.setKey(HID::Keys::Start, getButtonState(RETRO_DEVICE_ID_JOYPAD_START));
hid.setKey(HID::Keys::Select, getButtonState(RETRO_DEVICE_ID_JOYPAD_SELECT));
hid.setKey(HID::Keys::Up, getButtonState(RETRO_DEVICE_ID_JOYPAD_UP));
hid.setKey(HID::Keys::Down, getButtonState(RETRO_DEVICE_ID_JOYPAD_DOWN));
hid.setKey(HID::Keys::Left, getButtonState(RETRO_DEVICE_ID_JOYPAD_LEFT));
hid.setKey(HID::Keys::Right, getButtonState(RETRO_DEVICE_ID_JOYPAD_RIGHT));
// Get analog values for the left analog stick (Right analog stick is N3DS-only and unimplemented)
float xLeft = GetAxisState(RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X);
float yLeft = GetAxisState(RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y);
float xLeft = getAxisState(RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X);
float yLeft = getAxisState(RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y);
hid.setCirclepadX((xLeft / +32767) * 0x9C);
hid.setCirclepadY((yLeft / -32767) * 0x9C);
@ -355,7 +369,7 @@ void retro_run() {
hid.updateInputs(emulator->getTicks());
emulator->runFrame();
videoCallbacks(RETRO_HW_FRAME_BUFFER_VALID, emulator->width, emulator->height, 0);
videoCallback(RETRO_HW_FRAME_BUFFER_VALID, emulator->width, emulator->height, 0);
}
void retro_set_controller_port_device(uint port, uint device) {}

View file

@ -130,6 +130,32 @@ MAKE_MEMORY_FUNCTIONS(32)
MAKE_MEMORY_FUNCTIONS(64)
#undef MAKE_MEMORY_FUNCTIONS
static int readFloatThunk(lua_State* L) {
const u32 vaddr = (u32)lua_tonumber(L, 1);
lua_pushnumber(L, (lua_Number)Helpers::bit_cast<float, u32>(LuaManager::g_emulator->getMemory().read32(vaddr)));
return 1;
}
static int writeFloatThunk(lua_State* L) {
const u32 vaddr = (u32)lua_tonumber(L, 1);
const float value = (float)lua_tonumber(L, 2);
LuaManager::g_emulator->getMemory().write32(vaddr, Helpers::bit_cast<u32, float>(value));
return 0;
}
static int readDoubleThunk(lua_State* L) {
const u32 vaddr = (u32)lua_tonumber(L, 1);
lua_pushnumber(L, (lua_Number)Helpers::bit_cast<double, u64>(LuaManager::g_emulator->getMemory().read64(vaddr)));
return 1;
}
static int writeDoubleThunk(lua_State* L) {
const u32 vaddr = (u32)lua_tonumber(L, 1);
const double value = (double)lua_tonumber(L, 2);
LuaManager::g_emulator->getMemory().write64(vaddr, Helpers::bit_cast<u64, double>(value));
return 0;
}
static int getAppIDThunk(lua_State* L) {
std::optional<u64> id = LuaManager::g_emulator->getMemory().getProgramID();
@ -248,10 +274,14 @@ static constexpr luaL_Reg functions[] = {
{ "__read16", read16Thunk },
{ "__read32", read32Thunk },
{ "__read64", read64Thunk },
{ "__readFloat", readFloatThunk },
{ "__readDouble", readDoubleThunk },
{ "__write8", write8Thunk} ,
{ "__write16", write16Thunk },
{ "__write32", write32Thunk },
{ "__write64", write64Thunk },
{ "__writeFloat", writeFloatThunk },
{ "__writeDouble", writeDoubleThunk },
{ "__getAppID", getAppIDThunk },
{ "__pause", pauseThunk },
{ "__resume", resumeThunk },
@ -273,10 +303,15 @@ void LuaManager::initializeThunks() {
read16 = function(addr) return GLOBALS.__read16(addr) end,
read32 = function(addr) return GLOBALS.__read32(addr) end,
read64 = function(addr) return GLOBALS.__read64(addr) end,
readFloat = function(addr) return GLOBALS.__readFloat(addr) end,
readDouble = function(addr) return GLOBALS.__readDouble(addr) end,
write8 = function(addr, value) GLOBALS.__write8(addr, value) end,
write16 = function(addr, value) GLOBALS.__write16(addr, value) end,
write32 = function(addr, value) GLOBALS.__write32(addr, value) end,
write64 = function(addr, value) GLOBALS.__write64(addr, value) end,
writeFloat = function(addr, value) GLOBALS.__writeFloat(addr, value) end,
writeDouble = function(addr, value) GLOBALS.__writeDouble(addr, value) end,
getAppID = function()
local ffi = require("ffi")

View file

@ -1,11 +1,13 @@
#include "panda_qt/about_window.hpp"
#include "version.hpp"
#include <QHBoxLayout>
#include <QLabel>
#include <QTextEdit>
#include <QVBoxLayout>
#include <QtGlobal>
#include "version.hpp"
// Based on https://github.com/dolphin-emu/dolphin/blob/master/Source/Core/DolphinQt/AboutDialog.cpp
AboutWindow::AboutWindow(QWidget* parent) : QDialog(parent) {

View file

@ -1,15 +1,9 @@
#include "panda_qt/cheats_window.hpp"
#include <QCheckBox>
#include <QDialog>
#include <QDialogButtonBox>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QPushButton>
#include <QTextEdit>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QTimer>
#include <QVBoxLayout>
#include <functional>
#include "cheats.hpp"
@ -18,71 +12,17 @@
MainWindow* mainWindow = nullptr;
struct CheatMetadata {
u32 handle = Cheats::badCheatHandle;
std::string name = "New cheat";
std::string code;
bool enabled = true;
};
void dispatchToMainThread(std::function<void()> callback) {
QTimer* timer = new QTimer();
timer->moveToThread(qApp->thread());
timer->setSingleShot(true);
QObject::connect(timer, &QTimer::timeout, [=]()
{
callback();
timer->deleteLater();
});
QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection, Q_ARG(int, 0));
QTimer* timer = new QTimer();
timer->moveToThread(qApp->thread());
timer->setSingleShot(true);
QObject::connect(timer, &QTimer::timeout, [=]() {
callback();
timer->deleteLater();
});
QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection, Q_ARG(int, 0));
}
class CheatEntryWidget : public QWidget {
public:
CheatEntryWidget(Emulator* emu, CheatMetadata metadata, QListWidget* parent);
void Update() {
name->setText(metadata.name.c_str());
enabled->setChecked(metadata.enabled);
update();
}
void Remove() {
emu->getCheats().removeCheat(metadata.handle);
cheatList->takeItem(cheatList->row(listItem));
deleteLater();
}
const CheatMetadata& getMetadata() { return metadata; }
void setMetadata(const CheatMetadata& metadata) { this->metadata = metadata; }
private:
void checkboxChanged(int state);
void editClicked();
Emulator* emu;
CheatMetadata metadata;
u32 handle;
QLabel* name;
QCheckBox* enabled;
QListWidget* cheatList;
QListWidgetItem* listItem;
};
class CheatEditDialog : public QDialog {
public:
CheatEditDialog(Emulator* emu, CheatEntryWidget& cheatEntry);
void accepted();
void rejected();
private:
Emulator* emu;
CheatEntryWidget& cheatEntry;
QTextEdit* codeEdit;
QLineEdit* nameEdit;
};
CheatEntryWidget::CheatEntryWidget(Emulator* emu, CheatMetadata metadata, QListWidget* parent)
: QWidget(), emu(emu), metadata(metadata), cheatList(parent) {
QHBoxLayout* layout = new QHBoxLayout;
@ -219,7 +159,7 @@ void CheatEditDialog::rejected() {
CheatsWindow::CheatsWindow(Emulator* emu, const std::filesystem::path& cheatPath, QWidget* parent)
: QWidget(parent, Qt::Window), emu(emu), cheatPath(cheatPath) {
mainWindow = static_cast<MainWindow*>(parent);
mainWindow = static_cast<MainWindow*>(parent);
QVBoxLayout* layout = new QVBoxLayout;
layout->setContentsMargins(6, 6, 6, 6);
@ -265,4 +205,4 @@ void CheatsWindow::removeClicked() {
CheatEntryWidget* entry = static_cast<CheatEntryWidget*>(cheatList->itemWidget(item));
entry->Remove();
}
}

View file

@ -1,7 +1,7 @@
#include "input_mappings.hpp"
#include <QKeyEvent>
#include "input_mappings.hpp"
InputMappings InputMappings::defaultKeyboardMappings() {
InputMappings mappings;
mappings.setMapping(Qt::Key_L, HID::Keys::A);

View file

@ -1,8 +1,9 @@
#include "panda_qt/shader_editor.hpp"
#include <QPushButton>
#include <QVBoxLayout>
#include "panda_qt/main_window.hpp"
#include "panda_qt/shader_editor.hpp"
using namespace Zep;