Merge branch 'master' into shader-decomp

This commit is contained in:
wheremyfoodat 2024-08-19 22:19:13 +03:00
commit 1366e7a76e
19 changed files with 305 additions and 18 deletions

View file

@ -40,7 +40,7 @@ jobs:
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
- name: Install bundle dependencies
run: brew install dylibbundler imagemagick
run: brew install --overwrite python@3.12 && brew install dylibbundler imagemagick
- name: Run bundle script
run: ./.github/mac-bundle.sh

View file

@ -67,7 +67,7 @@ jobs:
- name: Install bundle dependencies
run: |
brew install dylibbundler imagemagick
brew install --overwrite python@3.12 && brew install dylibbundler imagemagick
- name: Install qt
run: brew install qt && which macdeployqt

130
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,130 @@
# DESCRIPTION: GitLab CI/CD for libRetro (NOT FOR GitLab-proper)
##############################################################################
################################# BOILERPLATE ################################
##############################################################################
# Core definitions
.core-defs:
variables:
GIT_SUBMODULE_STRATEGY: recursive
CORENAME: panda3ds
CORE_ARGS: -DBUILD_LIBRETRO_CORE=ON -DENABLE_USER_BUILD=ON -DENABLE_VULKAN=OFF -DENABLE_LUAJIT=OFF -DENABLE_DISCORD_RPC=OFF
# Inclusion templates, required for the build to work
include:
################################## DESKTOPS ################################
# Linux
- project: 'libretro-infrastructure/ci-templates'
file: '/linux-cmake.yml'
# Windows
- project: 'libretro-infrastructure/ci-templates'
file: '/windows-cmake-mingw.yml'
# MacOS
- project: 'libretro-infrastructure/ci-templates'
file: 'osx-cmake-x86.yml'
# MacOS
- project: 'libretro-infrastructure/ci-templates'
file: 'osx-cmake-arm64.yml'
################################## CELLULAR ################################
# Android
- project: 'libretro-infrastructure/ci-templates'
file: '/android-cmake.yml'
# iOS
- project: 'libretro-infrastructure/ci-templates'
file: '/ios-cmake.yml'
# Stages for building
stages:
- build-prepare
- build-static
- build-shared
##############################################################################
#################################### STAGES ##################################
##############################################################################
#
################################### DESKTOPS #################################
# Linux 64-bit
libretro-build-linux-x64:
image: $CI_SERVER_HOST:5050/libretro-infrastructure/libretro-build-amd64-ubuntu:latest
before_script:
- export NUMPROC=$(($(nproc)/5))
- sudo apt-get update -qy
- sudo apt-get install -qy software-properties-common
- sudo add-apt-repository -y ppa:savoury1/build-tools
- sudo add-apt-repository -y ppa:savoury1/gcc-defaults-12
- sudo apt-get update -qy
- sudo apt-get install -qy cmake gcc-12 g++-12
variables:
CC: /usr/bin/gcc-12
CXX: /usr/bin/g++-12
extends:
- .libretro-linux-cmake-x86_64
- .core-defs
# Windows 64-bit
libretro-build-windows-x64:
extends:
- .libretro-windows-cmake-x86_64
- .core-defs
# MacOS 64-bit
libretro-build-osx-x64:
tags:
- mac-apple-silicon
variables:
CORE_ARGS: -DBUILD_LIBRETRO_CORE=ON -DENABLE_USER_BUILD=ON -DENABLE_VULKAN=OFF -DENABLE_LUAJIT=OFF -DENABLE_DISCORD_RPC=OFF -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCRYPTOPP_AMD64=1
extends:
- .libretro-osx-cmake-x86
- .core-defs
# MacOS arm 64-bit
libretro-build-osx-arm64:
tags:
- mac-apple-silicon
extends:
- .libretro-osx-cmake-arm64
- .core-defs
################################### CELLULAR #################################
# Android ARMv7a
#android-armeabi-v7a:
# extends:
# - .libretro-android-cmake-armeabi-v7a
# - .core-defs
# Android ARMv8a
# android-arm64-v8a:
# extends:
# - .libretro-android-cmake-arm64-v8a
# - .core-defs
# Android 64-bit x86
# android-x86_64:
# extends:
# - .libretro-android-cmake-x86_64
# - .core-defs
# Android 32-bit x86
# android-x86:
# extends:
# - .libretro-android-cmake-x86
# - .core-defs
# iOS
# libretro-build-ios-arm64:
# extends:
# - .libretro-ios-cmake-arm64
# - .core-defs
# variables:
# CORE_ARGS: -DBUILD_LIBRETRO_CORE=ON -DBUILD_PLAY=OFF -DENABLE_AMAZON_S3=off -DBUILD_TESTS=OFF -DCMAKE_TOOLCHAIN_FILE=deps/Dependencies/cmake-ios/ios.cmake -DTARGET_IOS=ON
# LIBNAME: ${CORENAME}_libretro_ios.dylib
################################### CONSOLES #################################

View file

@ -261,7 +261,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
include/audio/miniaudio_device.hpp include/ring_buffer.hpp include/bitfield.hpp include/audio/dsp_shared_mem.hpp
include/audio/hle_core.hpp include/capstone.hpp include/audio/aac.hpp include/PICA/pica_frag_config.hpp
include/PICA/pica_frag_uniforms.hpp include/PICA/shader_gen_types.hpp include/PICA/shader_decompiler.hpp
include/PICA/pica_vert_config.hpp
include/PICA/pica_vert_config.hpp include/sdl_sensors.hpp
)
cmrc_add_resource_library(
@ -521,17 +521,17 @@ elseif(BUILD_HYDRA_CORE)
target_link_libraries(Alber PUBLIC AlberCore)
elseif(BUILD_LIBRETRO_CORE)
include_directories(third_party/libretro/include)
add_library(Alber SHARED src/libretro_core.cpp)
target_link_libraries(Alber PUBLIC AlberCore)
set_target_properties(Alber PROPERTIES
OUTPUT_NAME "panda3ds_libretro"
PREFIX ""
)
add_library(panda3ds_libretro SHARED src/libretro_core.cpp)
target_link_libraries(panda3ds_libretro PUBLIC AlberCore)
set_target_properties(panda3ds_libretro PROPERTIES PREFIX "")
endif()
if(ENABLE_LTO OR ENABLE_USER_BUILD)
set_target_properties(Alber PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
if (NOT BUILD_LIBRETRO_CORE)
set_target_properties(Alber PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
else()
set_target_properties(panda3ds_libretro PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
endif()
if(ENABLE_TESTS)

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

View file

@ -122,6 +122,7 @@ class MainWindow : public QMainWindow {
void showAboutMenu();
void initControllers();
void pollControllers();
void setupControllerSensors(SDL_GameController* controller);
void sendMessage(const EmulatorMessage& message);
void dispatchMessage(const EmulatorMessage& message);

View file

@ -37,4 +37,6 @@ class FrontendSDL {
// And so the user can still use the keyboard to control the analog
bool keyboardAnalogX = false;
bool keyboardAnalogY = false;
void setupControllerSensors(SDL_GameController* controller);
};

32
include/sdl_sensors.hpp Normal file
View file

@ -0,0 +1,32 @@
#pragma once
#include <cmath>
#include <glm/glm.hpp>
#include <numbers>
#include "helpers.hpp"
#include "services/hid.hpp"
namespace Sensors::SDL {
// Convert the rotation data we get from SDL sensor events to rotation data we can feed right to HID
// Returns [pitch, roll, yaw]
static glm::vec3 convertRotation(glm::vec3 rotation) {
// Convert the rotation from rad/s to deg/s and scale by the gyroscope coefficient in HID
constexpr float scale = 180.f / std::numbers::pi * HIDService::gyroscopeCoeff;
// The axes are also inverted, so invert scale before the multiplication.
return rotation * -scale;
}
static glm::vec3 convertAcceleration(float* data) {
// Set our cap to ~9 m/s^2. The 3DS sensors cap at -930 and +930, so values above this value will get clamped to 930
// At rest (3DS laid flat on table), hardware reads around ~0 for x and z axis, and around ~480 for y axis due to gravity.
// This code tries to mimic this approximately, with offsets based on measurements from my DualShock 4.
static constexpr float accelMax = 9.f;
s16 x = std::clamp<s16>(s16(data[0] / accelMax * 930.f), -930, +930);
s16 y = std::clamp<s16>(s16(data[1] / (SDL_STANDARD_GRAVITY * accelMax) * 930.f - 350.f), -930, +930);
s16 z = std::clamp<s16>(s16((data[2] - 2.1f) / accelMax * 930.f), -930, +930);
return glm::vec3(x, y, z);
}
} // namespace Sensors::SDL

View file

@ -56,6 +56,7 @@ class HIDService {
s16 circlePadX, circlePadY; // Circlepad state
s16 touchScreenX, touchScreenY; // Touchscreen state
s16 roll, pitch, yaw; // Gyroscope state
s16 accelX, accelY, accelZ; // Accelerometer state
bool accelerometerEnabled;
bool eventsInitialized;
@ -87,7 +88,14 @@ class HIDService {
*(T*)&sharedMem[offset] = value;
}
template <typename T>
T* getSharedMemPointer(size_t offset) {
return (T*)&sharedMem[offset];
}
public:
static constexpr float gyroscopeCoeff = 14.375f; // Same as retail 3DS
HIDService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
void reset();
void handleSyncRequest(u32 messagePointer);
@ -128,6 +136,12 @@ class HIDService {
void setPitch(s16 value) { pitch = value; }
void setYaw(s16 value) { yaw = value; }
void setAccel(s16 x, s16 y, s16 z) {
accelX = x;
accelY = y;
accelZ = z;
}
void updateInputs(u64 currentTimestamp);
void setSharedMem(u8* ptr) {

View file

@ -627,5 +627,6 @@ namespace Audio {
rateMultiplier = 1.f;
buffers = {};
currentSamples.clear();
}
} // namespace Audio

View file

@ -35,6 +35,7 @@ void HIDService::reset() {
circlePadX = circlePadY = 0;
touchScreenX = touchScreenY = 0;
roll = pitch = yaw = 0;
accelX = accelY = accelZ = 0;
}
void HIDService::handleSyncRequest(u32 messagePointer) {
@ -103,7 +104,6 @@ void HIDService::getGyroscopeLowCalibrateParam(u32 messagePointer) {
void HIDService::getGyroscopeCoefficient(u32 messagePointer) {
log("HID::GetGyroscopeLowRawToDpsCoefficient\n");
constexpr float gyroscopeCoeff = 14.375f; // Same as retail 3DS
mem.write32(messagePointer, IPC::responseHeader(0x15, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, Helpers::bit_cast<u32, float>(gyroscopeCoeff));
@ -190,6 +190,20 @@ void HIDService::updateInputs(u64 currentTick) {
writeSharedMem<u64>(0x108, currentTick); // Write new tick count
}
writeSharedMem<u32>(0x118, nextAccelerometerIndex); // Index last updated by the HID module
const size_t accelEntryOffset = 0x128 + (nextAccelerometerIndex * 6); // Offset in the array of 8 accelerometer entries
// Raw data of current accelerometer entry
// TODO: How is the "raw" data actually calculated?
s16* accelerometerDataRaw = getSharedMemPointer<s16>(0x120);
accelerometerDataRaw[0] = accelX;
accelerometerDataRaw[1] = accelY;
accelerometerDataRaw[2] = accelZ;
// Accelerometer entry in entry table
s16* accelerometerData = getSharedMemPointer<s16>(accelEntryOffset);
accelerometerData[0] = accelX;
accelerometerData[1] = accelY;
accelerometerData[2] = accelZ;
nextAccelerometerIndex = (nextAccelerometerIndex + 1) % 8; // Move to next entry
// Next, update gyro state
@ -198,9 +212,10 @@ void HIDService::updateInputs(u64 currentTick) {
writeSharedMem<u64>(0x158, currentTick); // Write new tick count
}
const size_t gyroEntryOffset = 0x178 + (nextGyroIndex * 6); // Offset in the array of 8 touchscreen entries
writeSharedMem<u16>(gyroEntryOffset, pitch);
writeSharedMem<u16>(gyroEntryOffset + 2, yaw);
writeSharedMem<u16>(gyroEntryOffset + 4, roll);
s16* gyroData = getSharedMemPointer<s16>(gyroEntryOffset);
gyroData[0] = pitch;
gyroData[1] = yaw;
gyroData[2] = roll;
// Since gyroscope euler angles are relative, we zero them out here and the frontend will update them again when we receive a new rotation
roll = pitch = yaw = 0;

View file

@ -1,6 +1,6 @@
#include "emulator.hpp"
#ifndef __ANDROID__
#if !defined(__ANDROID__) && !defined(__LIBRETRO__)
#include <SDL_filesystem.h>
#endif

View file

@ -372,7 +372,7 @@ uint retro_api_version() { return RETRO_API_VERSION; }
usize retro_get_memory_size(uint id) {
if (id == RETRO_MEMORY_SYSTEM_RAM) {
return 0;
return Memory::FCRAM_SIZE;
}
return 0;
@ -380,7 +380,7 @@ usize retro_get_memory_size(uint id) {
void* retro_get_memory_data(uint id) {
if (id == RETRO_MEMORY_SYSTEM_RAM) {
return 0;
return emulator->getMemory().getFCRAM();
}
return nullptr;

View file

@ -9,6 +9,7 @@
#include "cheats.hpp"
#include "input_mappings.hpp"
#include "sdl_sensors.hpp"
#include "services/dsp.hpp"
MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent), keyboardMappings(InputMappings::defaultKeyboardMappings()) {
@ -521,6 +522,8 @@ void MainWindow::initControllers() {
SDL_Joystick* stick = SDL_GameControllerGetJoystick(gameController);
gameControllerID = SDL_JoystickInstanceID(stick);
}
setupControllerSensors(gameController);
}
}
@ -558,6 +561,8 @@ void MainWindow::pollControllers() {
if (gameController == nullptr) {
gameController = SDL_GameControllerOpen(event.cdevice.which);
gameControllerID = event.cdevice.which;
setupControllerSensors(gameController);
}
break;
@ -598,6 +603,37 @@ void MainWindow::pollControllers() {
}
break;
}
case SDL_CONTROLLERSENSORUPDATE: {
if (event.csensor.sensor == SDL_SENSOR_GYRO) {
auto rotation = Sensors::SDL::convertRotation({
event.csensor.data[0],
event.csensor.data[1],
event.csensor.data[2],
});
hid.setPitch(s16(rotation.x));
hid.setRoll(s16(rotation.y));
hid.setYaw(s16(rotation.z));
} else if (event.csensor.sensor == SDL_SENSOR_ACCEL) {
auto accel = Sensors::SDL::convertAcceleration(event.csensor.data);
hid.setAccel(accel.x, accel.y, accel.z);
}
break;
}
}
}
}
void MainWindow::setupControllerSensors(SDL_GameController* controller) {
bool haveGyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) == SDL_TRUE;
bool haveAccelerometer = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) == SDL_TRUE;
if (haveGyro) {
SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE);
}
if (haveAccelerometer) {
SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE);
}
}

View file

@ -2,6 +2,8 @@
#include <glad/gl.h>
#include "sdl_sensors.hpp"
FrontendSDL::FrontendSDL() : keyboardMappings(InputMappings::defaultKeyboardMappings()) {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) {
Helpers::panic("Failed to initialize SDL2");
@ -20,6 +22,8 @@ FrontendSDL::FrontendSDL() : keyboardMappings(InputMappings::defaultKeyboardMapp
SDL_Joystick* stick = SDL_GameControllerGetJoystick(gameController);
gameControllerID = SDL_JoystickInstanceID(stick);
}
setupControllerSensors(gameController);
}
const EmulatorConfig& config = emu.getConfig();
@ -200,6 +204,8 @@ void FrontendSDL::run() {
if (gameController == nullptr) {
gameController = SDL_GameControllerOpen(event.cdevice.which);
gameControllerID = event.cdevice.which;
setupControllerSensors(gameController);
}
break;
@ -280,6 +286,24 @@ void FrontendSDL::run() {
}
break;
}
case SDL_CONTROLLERSENSORUPDATE: {
if (event.csensor.sensor == SDL_SENSOR_GYRO) {
auto rotation = Sensors::SDL::convertRotation({
event.csensor.data[0],
event.csensor.data[1],
event.csensor.data[2],
});
hid.setPitch(s16(rotation.x));
hid.setRoll(s16(rotation.y));
hid.setYaw(s16(rotation.z));
} else if (event.csensor.sensor == SDL_SENSOR_ACCEL) {
auto accel = Sensors::SDL::convertAcceleration(event.csensor.data);
hid.setAccel(accel.x, accel.y, accel.z);
}
break;
}
case SDL_DROPFILE: {
char* droppedDir = event.drop.file;
@ -342,3 +366,16 @@ void FrontendSDL::run() {
SDL_GL_SwapWindow(window);
}
}
void FrontendSDL::setupControllerSensors(SDL_GameController* controller) {
bool haveGyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) == SDL_TRUE;
bool haveAccelerometer = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) == SDL_TRUE;
if (haveGyro) {
SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE);
}
if (haveAccelerometer) {
SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE);
}
}

View file

@ -432,6 +432,25 @@ namespace OpenGL {
return m_handle != 0;
}
bool createFromBinary(const uint8_t* binary, size_t size, GLenum format) {
m_handle = glCreateProgram();
glProgramBinary(m_handle, format, binary, size);
GLint success;
glGetProgramiv(m_handle, GL_LINK_STATUS, &success);
if (!success) {
char buf[4096];
glGetProgramInfoLog(m_handle, 4096, nullptr, buf);
fprintf(stderr, "Failed to link program\nError: %s\n", buf);
glDeleteProgram(m_handle);
m_handle = 0;
}
return m_handle != 0;
}
GLuint handle() const { return m_handle; }
bool exists() const { return m_handle != 0; }
void use() const { glUseProgram(m_handle); }