mirror of
https://github.com/xenios-jp/XeniOS.git
synced 2026-07-11 15:19:09 -07:00
[APU] Upgrade to SDL3
This commit is contained in:
@@ -45,7 +45,9 @@ jobs:
|
||||
sudo apt-add-repository "deb http://apt.llvm.org/${UBUNTU_BASE}/ llvm-toolchain-${UBUNTU_BASE}-21 main"
|
||||
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install libc++-dev libc++abi-dev libgtk-3-dev libsdl2-dev libx11-xcb-dev clang-21 lld-21 ninja-build cmake libwxgtk3.2-dev libfontconfig1-dev libxtst-dev
|
||||
sudo apt-get -y install libc++-dev libc++abi-dev libgtk-3-dev libx11-xcb-dev clang-21 lld-21 ninja-build cmake libfontconfig1-dev libxtst-dev \
|
||||
libasound2-dev libpulse-dev libudev-dev libdbus-1-dev \
|
||||
libx11-dev libxext-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxss-dev libxkbcommon-dev libxfixes-dev
|
||||
|
||||
# Download linuxdeploy if not cached.
|
||||
if [ '${{ steps.cache-linuxdeploy.outputs.cache-hit }}' != 'true' ]; then
|
||||
|
||||
+4
-3
@@ -29,9 +29,6 @@
|
||||
[submodule "third_party/cxxopts"]
|
||||
path = third_party/cxxopts
|
||||
url = https://github.com/jarro2783/cxxopts.git
|
||||
[submodule "third_party/SDL2"]
|
||||
path = third_party/SDL2
|
||||
url = https://github.com/libsdl-org/SDL.git
|
||||
[submodule "third_party/utfcpp"]
|
||||
path = third_party/utfcpp
|
||||
url = https://github.com/nemtrif/utfcpp.git
|
||||
@@ -129,3 +126,7 @@
|
||||
[submodule "third_party/cereal"]
|
||||
path = third_party/cereal
|
||||
url = https://github.com/USCiLab/cereal.git
|
||||
[submodule "third_party/SDL3"]
|
||||
path = third_party/SDL3
|
||||
url = https://github.com/libsdl-org/SDL.git
|
||||
ignore = dirty
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ if(APPLE)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
# OBJC needed for SDL2's .m sources (Cocoa video, CoreAudio, MFi joystick).
|
||||
# OBJC needed for SDL3's .m sources (Cocoa, CoreAudio, MFi joystick).
|
||||
project(xenia LANGUAGES C CXX OBJC OBJCXX)
|
||||
set(CMAKE_OBJCXX_STANDARD 20)
|
||||
set(CMAKE_OBJCXX_STANDARD_REQUIRED ON)
|
||||
|
||||
+3
-1
@@ -94,7 +94,9 @@ The build script uses Clang 21.
|
||||
You will also need some development libraries. To get them on an Ubuntu system:
|
||||
|
||||
```sh
|
||||
sudo apt-get install build-essential mesa-vulkan-drivers libc++-dev libc++abi-dev liblz4-dev libsdl2-dev libvulkan-dev libx11-xcb-dev clang-21 llvm-21 ninja-build libwxgtk3.2-dev libfontconfig1-dev
|
||||
sudo apt-get install build-essential mesa-vulkan-drivers libc++-dev libc++abi-dev liblz4-dev libvulkan-dev libx11-xcb-dev clang-21 llvm-21 ninja-build libfontconfig1-dev \
|
||||
libasound2-dev libpulse-dev libudev-dev libdbus-1-dev \
|
||||
libx11-dev libxext-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxss-dev libxkbcommon-dev libxfixes-dev
|
||||
```
|
||||
|
||||
In addition, you will need up to date Vulkan libraries and drivers for your hardware, which most distributions have in their standard repositories nowadays.
|
||||
|
||||
@@ -118,7 +118,7 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
X11
|
||||
xcb
|
||||
X11-xcb
|
||||
SDL2
|
||||
SDL3
|
||||
)
|
||||
elseif(APPLE)
|
||||
target_link_libraries(xenia-app PRIVATE
|
||||
|
||||
@@ -8,6 +8,6 @@ target_link_libraries(xenia-apu PUBLIC libavcodec libavutil libavformat xenia-ba
|
||||
# AudioMediaPlayer instantiates SDLAudioDriver directly on Linux, so the
|
||||
# SDL chain is a transitive dependency of xenia-apu there.
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
target_link_libraries(xenia-apu PUBLIC xenia-helper-sdl SDL2)
|
||||
target_link_libraries(xenia-apu PUBLIC xenia-helper-sdl SDL3)
|
||||
endif()
|
||||
xe_target_defaults(xenia-apu)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
add_library(xenia-apu-sdl STATIC)
|
||||
xe_platform_sources(xenia-apu-sdl ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(xenia-apu-sdl PUBLIC xenia-apu xenia-base xenia-helper-sdl SDL2)
|
||||
target_link_libraries(xenia-apu-sdl PUBLIC xenia-apu xenia-base xenia-helper-sdl SDL3)
|
||||
xe_target_defaults(xenia-apu-sdl)
|
||||
|
||||
@@ -50,57 +50,30 @@ SDLAudioDriver::~SDLAudioDriver() {
|
||||
};
|
||||
|
||||
bool SDLAudioDriver::Initialize() {
|
||||
SDL_version ver = {};
|
||||
SDL_GetVersion(&ver);
|
||||
if ((ver.major < 2) || (ver.major == 2 && ver.minor == 0 && ver.patch < 8)) {
|
||||
XELOGW(
|
||||
"SDL library version {}.{}.{} is outdated. "
|
||||
"You may experience choppy audio.",
|
||||
ver.major, ver.minor, ver.patch);
|
||||
}
|
||||
|
||||
if (!xe::helper::sdl::SDLHelper::Prepare()) {
|
||||
return false;
|
||||
}
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
||||
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||
return false;
|
||||
}
|
||||
sdl_initialized_ = true;
|
||||
|
||||
SDL_AudioSpec desired_spec = {};
|
||||
SDL_AudioSpec obtained_spec;
|
||||
desired_spec.freq = frame_frequency_;
|
||||
desired_spec.format = AUDIO_F32;
|
||||
desired_spec.channels = frame_channels_;
|
||||
desired_spec.samples = channel_samples_;
|
||||
desired_spec.callback = SDLCallback;
|
||||
desired_spec.userdata = this;
|
||||
// Allow the hardware to decide between 5.1 and stereo,
|
||||
// unless the input is stereo
|
||||
int allowed_change =
|
||||
frame_channels_ != 2 ? SDL_AUDIO_ALLOW_CHANNELS_CHANGE : 0;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
sdl_device_id_ = SDL_OpenAudioDevice(nullptr, 0, &desired_spec,
|
||||
&obtained_spec, allowed_change);
|
||||
if (sdl_device_id_ <= 0) {
|
||||
XELOGE("SDL_OpenAudioDevice() failed.");
|
||||
return false;
|
||||
}
|
||||
if (obtained_spec.channels == 2 || obtained_spec.channels == 6) {
|
||||
break;
|
||||
}
|
||||
// If the system is 4 or 7.1, let SDL convert
|
||||
allowed_change = 0;
|
||||
SDL_CloseAudioDevice(sdl_device_id_);
|
||||
sdl_device_id_ = -1;
|
||||
}
|
||||
if (sdl_device_id_ <= 0) {
|
||||
XELOGE("Failed to get a compatible SDL Audio Device.");
|
||||
// SDL3 always converts between the stream spec and the device spec, so we
|
||||
// just request our native channel count and let SDL3 downmix/upmix to the
|
||||
// hardware's actual configuration.
|
||||
SDL_AudioSpec spec = {};
|
||||
spec.format = SDL_AUDIO_F32;
|
||||
spec.channels = static_cast<int>(frame_channels_);
|
||||
spec.freq = static_cast<int>(frame_frequency_);
|
||||
|
||||
sdl_stream_ = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK,
|
||||
&spec, SDLCallback, this);
|
||||
if (!sdl_stream_) {
|
||||
XELOGE("SDL_OpenAudioDeviceStream() failed: {}", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
sdl_device_channels_ = obtained_spec.channels;
|
||||
|
||||
SDL_PauseAudioDevice(sdl_device_id_, 0);
|
||||
SDL_ResumeAudioStreamDevice(sdl_stream_);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -125,14 +98,24 @@ void SDLAudioDriver::SubmitFrame(float* frame) {
|
||||
}
|
||||
}
|
||||
|
||||
void SDLAudioDriver::Pause() { SDL_PauseAudioDevice(sdl_device_id_, 1); }
|
||||
void SDLAudioDriver::Pause() {
|
||||
if (sdl_stream_) {
|
||||
SDL_PauseAudioStreamDevice(sdl_stream_);
|
||||
}
|
||||
}
|
||||
|
||||
void SDLAudioDriver::Resume() { SDL_PauseAudioDevice(sdl_device_id_, 0); }
|
||||
void SDLAudioDriver::Resume() {
|
||||
if (sdl_stream_) {
|
||||
SDL_ResumeAudioStreamDevice(sdl_stream_);
|
||||
}
|
||||
}
|
||||
|
||||
void SDLAudioDriver::Shutdown() {
|
||||
if (sdl_device_id_ > 0) {
|
||||
SDL_CloseAudioDevice(sdl_device_id_);
|
||||
sdl_device_id_ = -1;
|
||||
if (sdl_stream_) {
|
||||
// SDL_OpenAudioDeviceStream-created streams own the underlying logical
|
||||
// device and close it on destroy.
|
||||
SDL_DestroyAudioStream(sdl_stream_);
|
||||
sdl_stream_ = nullptr;
|
||||
}
|
||||
if (sdl_initialized_) {
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
@@ -149,58 +132,63 @@ void SDLAudioDriver::Shutdown() {
|
||||
};
|
||||
}
|
||||
|
||||
void SDLAudioDriver::SDLCallback(void* userdata, Uint8* stream, int len) {
|
||||
void SDLAudioDriver::SDLCallback(void* userdata, SDL_AudioStream* stream,
|
||||
int additional_amount, int total_amount) {
|
||||
SCOPE_profile_cpu_f("apu");
|
||||
if (!userdata || !stream) {
|
||||
XELOGE("SDLAudioDriver::sdl_callback called with nullptr.");
|
||||
if (!userdata || !stream || additional_amount <= 0) {
|
||||
return;
|
||||
}
|
||||
const auto driver = static_cast<SDLAudioDriver*>(userdata);
|
||||
assert_true(len == sizeof(float) * driver->channel_samples_ *
|
||||
driver->sdl_device_channels_);
|
||||
const int frame_bytes = static_cast<int>(driver->frame_size_);
|
||||
|
||||
std::unique_lock<std::mutex> guard(driver->frames_mutex_);
|
||||
if (driver->frames_queued_.empty()) {
|
||||
std::memset(stream, 0, len);
|
||||
} else {
|
||||
auto buffer = driver->frames_queued_.front();
|
||||
driver->frames_queued_.pop();
|
||||
if (driver->need_format_conversion_) {
|
||||
switch (driver->sdl_device_channels_) {
|
||||
case 2:
|
||||
conversion::sequential_6_BE_to_interleaved_2_LE(
|
||||
reinterpret_cast<float*>(stream), buffer,
|
||||
driver->channel_samples_);
|
||||
break;
|
||||
case 6:
|
||||
conversion::sequential_6_BE_to_interleaved_6_LE(
|
||||
reinterpret_cast<float*>(stream), buffer,
|
||||
driver->channel_samples_);
|
||||
break;
|
||||
default:
|
||||
assert_unhandled_case(driver->sdl_device_channels_);
|
||||
break;
|
||||
// SDL3 pulls until additional_amount is satisfied or we run out of frames.
|
||||
// If the queue empties, returning early lets SDL3 fill the remainder with
|
||||
// silence — the producer will catch up on the next callback.
|
||||
while (additional_amount > 0) {
|
||||
float* buffer;
|
||||
{
|
||||
std::unique_lock<std::mutex> guard(driver->frames_mutex_);
|
||||
if (driver->frames_queued_.empty()) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
assert_true(driver->sdl_device_channels_ == driver->frame_channels_);
|
||||
std::memcpy(stream, buffer, len);
|
||||
buffer = driver->frames_queued_.front();
|
||||
driver->frames_queued_.pop();
|
||||
}
|
||||
|
||||
// Convert into a scratch buffer in the format SDL3 expects (interleaved
|
||||
// little-endian floats at our requested channel count). SDL3 will then
|
||||
// matrix-mix this to whatever the hardware actually wants.
|
||||
float scratch[kFrameSizeMax / sizeof(float)];
|
||||
if (driver->need_format_conversion_) {
|
||||
// need_format_conversion implies 6-channel BE-sequential input.
|
||||
conversion::sequential_6_BE_to_interleaved_6_LE(scratch, buffer,
|
||||
driver->channel_samples_);
|
||||
} else {
|
||||
std::memcpy(scratch, buffer, frame_bytes);
|
||||
}
|
||||
|
||||
// Scale by master (cvar) and per-driver volume.
|
||||
const uint32_t mv = cvars::volume > 100 ? 100 : cvars::volume;
|
||||
const float volume = driver->volume_ * (mv / 100.0f);
|
||||
if (volume != 1.0f) {
|
||||
float* samples = reinterpret_cast<float*>(stream);
|
||||
const size_t count = len / sizeof(float);
|
||||
const size_t count = frame_bytes / sizeof(float);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
samples[i] *= volume;
|
||||
scratch[i] *= volume;
|
||||
}
|
||||
}
|
||||
driver->frames_unused_.push(buffer);
|
||||
|
||||
SDL_PutAudioStreamData(stream, scratch, frame_bytes);
|
||||
additional_amount -= frame_bytes;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> guard(driver->frames_mutex_);
|
||||
driver->frames_unused_.push(buffer);
|
||||
}
|
||||
|
||||
auto ret = driver->semaphore_->Release(1, nullptr);
|
||||
assert_true(ret);
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace sdl
|
||||
} // namespace apu
|
||||
} // namespace xe
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
|
||||
#include "SDL.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include "xenia/apu/audio_driver.h"
|
||||
#include "xenia/base/threading.h"
|
||||
|
||||
@@ -38,13 +38,13 @@ class SDLAudioDriver : public AudioDriver {
|
||||
void Shutdown() override;
|
||||
|
||||
protected:
|
||||
static void SDLCallback(void* userdata, Uint8* stream, int len);
|
||||
static void SDLCallback(void* userdata, SDL_AudioStream* stream,
|
||||
int additional_amount, int total_amount);
|
||||
|
||||
xe::threading::Semaphore* semaphore_ = nullptr;
|
||||
|
||||
SDL_AudioDeviceID sdl_device_id_ = -1;
|
||||
SDL_AudioStream* sdl_stream_ = nullptr;
|
||||
bool sdl_initialized_ = false;
|
||||
uint8_t sdl_device_channels_ = 0;
|
||||
|
||||
float volume_ = 1.0f;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <dlfcn.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
@@ -18,8 +19,16 @@
|
||||
#include "xenia/base/string.h"
|
||||
#include "xenia/base/system.h"
|
||||
|
||||
// Use headers in third party to not depend on system sdl headers for building
|
||||
#include "third_party/SDL2/include/SDL.h"
|
||||
// Forward-declared instead of pulling in <SDL3/SDL.h> — xenia-base must not
|
||||
// drag SDL onto its include path, and only the dlsym() cast needs the type.
|
||||
// Flags and signature are stable across SDL2/3.
|
||||
namespace {
|
||||
using SDL_ShowSimpleMessageBox_fn = int (*)(uint32_t flags, const char* title,
|
||||
const char* message, void* window);
|
||||
constexpr uint32_t kSDL_MessageBoxError = 0x00000010;
|
||||
constexpr uint32_t kSDL_MessageBoxWarning = 0x00000020;
|
||||
constexpr uint32_t kSDL_MessageBoxInformation = 0x00000040;
|
||||
} // namespace
|
||||
|
||||
namespace xe {
|
||||
|
||||
@@ -37,15 +46,18 @@ void LaunchFileExplorer(const std::filesystem::path& path) {
|
||||
}
|
||||
|
||||
void ShowSimpleMessageBox(SimpleMessageBoxType type, std::string_view message) {
|
||||
void* libsdl2 = dlopen("libSDL2.so", RTLD_LAZY | RTLD_LOCAL);
|
||||
assert_not_null(libsdl2);
|
||||
if (libsdl2) {
|
||||
auto* pSDL_ShowSimpleMessageBox =
|
||||
reinterpret_cast<decltype(SDL_ShowSimpleMessageBox)*>(
|
||||
dlsym(libsdl2, "SDL_ShowSimpleMessageBox"));
|
||||
void* libsdl = dlopen("libSDL3.so.0", RTLD_LAZY | RTLD_LOCAL);
|
||||
if (!libsdl) {
|
||||
libsdl = dlopen("libSDL3.so", RTLD_LAZY | RTLD_LOCAL);
|
||||
}
|
||||
assert_not_null(libsdl);
|
||||
if (libsdl) {
|
||||
auto pSDL_ShowSimpleMessageBox =
|
||||
reinterpret_cast<SDL_ShowSimpleMessageBox_fn>(
|
||||
dlsym(libsdl, "SDL_ShowSimpleMessageBox"));
|
||||
assert_not_null(pSDL_ShowSimpleMessageBox);
|
||||
if (pSDL_ShowSimpleMessageBox) {
|
||||
Uint32 flags;
|
||||
uint32_t flags;
|
||||
const char* title;
|
||||
char* message_copy = reinterpret_cast<char*>(alloca(message.size() + 1));
|
||||
std::memcpy(message_copy, message.data(), message.size());
|
||||
@@ -55,20 +67,20 @@ void ShowSimpleMessageBox(SimpleMessageBoxType type, std::string_view message) {
|
||||
default:
|
||||
case SimpleMessageBoxType::Help:
|
||||
title = "Xenia Help";
|
||||
flags = SDL_MESSAGEBOX_INFORMATION;
|
||||
flags = kSDL_MessageBoxInformation;
|
||||
break;
|
||||
case SimpleMessageBoxType::Warning:
|
||||
title = "Xenia Warning";
|
||||
flags = SDL_MESSAGEBOX_WARNING;
|
||||
flags = kSDL_MessageBoxWarning;
|
||||
break;
|
||||
case SimpleMessageBoxType::Error:
|
||||
title = "Xenia Error";
|
||||
flags = SDL_MESSAGEBOX_ERROR;
|
||||
flags = kSDL_MessageBoxError;
|
||||
break;
|
||||
}
|
||||
pSDL_ShowSimpleMessageBox(flags, title, message_copy, NULL);
|
||||
}
|
||||
dlclose(libsdl2);
|
||||
dlclose(libsdl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
add_library(xenia-helper-sdl STATIC)
|
||||
xe_platform_sources(xenia-helper-sdl ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(xenia-helper-sdl PUBLIC SDL2)
|
||||
target_link_libraries(xenia-helper-sdl PUBLIC SDL3)
|
||||
xe_target_defaults(xenia-helper-sdl)
|
||||
|
||||
@@ -9,13 +9,6 @@
|
||||
|
||||
#include "xenia/helper/sdl/sdl_helper.h"
|
||||
|
||||
// On linux we likely build on an "outdated" system but still want to control
|
||||
// these features when available on a newer system.
|
||||
#if !SDL_VERSION_ATLEAST(2, 0, 14)
|
||||
#define SDL_HINT_AUDIO_DEVICE_APP_NAME "SDL_AUDIO_DEVICE_APP_NAME"
|
||||
#define SDL_HINT_JOYSTICK_RAWINPUT "SDL_JOYSTICK_RAWINPUT"
|
||||
#endif
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
|
||||
@@ -33,9 +26,9 @@ bool SDLHelper::Prepare() {
|
||||
is_prepared_ &= SetHints();
|
||||
is_prepared_ &= RedirectLog();
|
||||
|
||||
SDL_version ver = {};
|
||||
SDL_GetVersion(&ver);
|
||||
XELOGI("SDL Version {}.{}.{} initialized.", ver.major, ver.minor, ver.patch);
|
||||
const int ver = SDL_GetVersion();
|
||||
XELOGI("SDL Version {}.{}.{} initialized.", SDL_VERSIONNUM_MAJOR(ver),
|
||||
SDL_VERSIONNUM_MINOR(ver), SDL_VERSIONNUM_MICRO(ver));
|
||||
|
||||
return is_prepared_;
|
||||
}
|
||||
@@ -48,8 +41,7 @@ bool SDLHelper::SetHints() {
|
||||
// Setting hints with normal priority fails when the hint is set via env
|
||||
// vars or a hint with override priority is set, which does not conclude a
|
||||
// failure.
|
||||
if (SDL_FALSE ==
|
||||
SDL_SetHintWithPriority(
|
||||
if (!SDL_SetHintWithPriority(
|
||||
name, value, override_ ? SDL_HINT_OVERRIDE : SDL_HINT_NORMAL)) {
|
||||
const char* msg_fmt =
|
||||
"SDLHelper: Unable to set hint \"{}\" to value \"{}\".";
|
||||
@@ -63,15 +55,12 @@ bool SDLHelper::SetHints() {
|
||||
return true;
|
||||
};
|
||||
|
||||
// SDL calls timeBeginPeriod(1) but xenia sets this to a lower value before
|
||||
// using NtSetTimerResolution(). Having that value overwritten causes overall
|
||||
// fps drops. Use override priority as timer resolution should always be
|
||||
// managed by xenia. https://bugzilla.libsdl.org/show_bug.cgi?id=5104
|
||||
suc &= setHint(SDL_HINT_TIMER_RESOLUTION, "0", true);
|
||||
|
||||
suc &= setHint(SDL_HINT_AUDIO_CATEGORY, "playback");
|
||||
|
||||
suc &= setHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, "xenia emulator");
|
||||
// SDL3 replaced SDL_HINT_AUDIO_DEVICE_APP_NAME with app metadata. Backends
|
||||
// (PulseAudio, PipeWire, WASAPI session names, etc.) read this for the
|
||||
// user-facing device label.
|
||||
SDL_SetAppMetadata("xenia emulator", nullptr, nullptr);
|
||||
|
||||
// On Windows, SDL's joystick subsystem creates a hidden device-notification
|
||||
// window (WM_DEVICECHANGE + raw-input dispatch) on whichever thread inits
|
||||
@@ -92,7 +81,7 @@ bool SDLHelper::SetHints() {
|
||||
|
||||
bool SDLHelper::RedirectLog() {
|
||||
// Redirect SDL_Log* output (internal library stuff) to our log system.
|
||||
SDL_LogSetOutputFunction(
|
||||
SDL_SetLogOutputFunction(
|
||||
[](void* userdata, int category, SDL_LogPriority priority,
|
||||
const char* message) {
|
||||
const char* msg_fmt = "SDL: {}";
|
||||
@@ -121,7 +110,7 @@ bool SDLHelper::RedirectLog() {
|
||||
// SDL itself isn't that talkative. Additionally to this settings there are
|
||||
// hints that can be switched on to output additional internal logging
|
||||
// information.
|
||||
SDL_LogSetAllPriority(SDL_LogPriority::SDL_LOG_PRIORITY_VERBOSE);
|
||||
SDL_SetLogPriorities(SDL_LOG_PRIORITY_VERBOSE);
|
||||
return true;
|
||||
}
|
||||
} // namespace sdl
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#ifndef XENIA_HELPER_SDL_SDL_HELPER_H_
|
||||
#define XENIA_HELPER_SDL_SDL_HELPER_H_
|
||||
|
||||
#include "SDL.h"
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
namespace xe {
|
||||
namespace helper {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
add_library(xenia-hid-sdl STATIC)
|
||||
xe_platform_sources(xenia-hid-sdl ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(xenia-hid-sdl PUBLIC xenia-base xenia-hid xenia-ui SDL2)
|
||||
target_link_libraries(xenia-hid-sdl PUBLIC xenia-base xenia-hid xenia-ui SDL3)
|
||||
xe_target_defaults(xenia-hid-sdl)
|
||||
|
||||
# Bake the bundled controller mappings.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,7 @@
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
|
||||
#include "SDL.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include "third_party/rapidcsv/src/rapidcsv.h"
|
||||
#include "xenia/hid/input_driver.h"
|
||||
#include "xenia/xbox.h"
|
||||
@@ -54,7 +54,7 @@ class SDLInputDriver final : public InputDriver {
|
||||
|
||||
private:
|
||||
struct ControllerState {
|
||||
SDL_GameController* sdl;
|
||||
SDL_Gamepad* sdl;
|
||||
X_INPUT_CAPABILITIES caps;
|
||||
X_INPUT_STATE state;
|
||||
bool state_changed;
|
||||
@@ -84,7 +84,6 @@ class SDLInputDriver final : public InputDriver {
|
||||
std::optional<size_t> GetControllerIndexFromInstanceID(
|
||||
SDL_JoystickID instance_id);
|
||||
ControllerState* GetControllerState(uint32_t user_index);
|
||||
bool TestSDLVersion() const;
|
||||
void UpdateXCapabilities(ControllerState& state);
|
||||
|
||||
// Owns SDL init, the event pump, and teardown so the UI thread's modal
|
||||
@@ -92,7 +91,7 @@ class SDLInputDriver final : public InputDriver {
|
||||
void SDLEventThread(std::promise<X_STATUS> init_result);
|
||||
|
||||
bool sdl_events_initialized_;
|
||||
bool sdl_gamecontroller_initialized_;
|
||||
bool sdl_gamepad_initialized_;
|
||||
int sdl_events_unflushed_;
|
||||
std::thread sdl_thread_;
|
||||
std::atomic<bool> sdl_thread_should_exit_;
|
||||
|
||||
Vendored
+61
-158
@@ -677,165 +677,68 @@ if(WIN32)
|
||||
endif()
|
||||
|
||||
# ==============================================================================
|
||||
# SDL2
|
||||
# SDL3
|
||||
# ==============================================================================
|
||||
if(WIN32)
|
||||
# Build SDL2 from source on Windows
|
||||
file(GLOB _sdl2_sources
|
||||
"SDL2/src/*.c"
|
||||
"SDL2/src/atomic/*.c"
|
||||
"SDL2/src/audio/*.c"
|
||||
"SDL2/src/audio/directsound/*.c"
|
||||
"SDL2/src/audio/disk/*.c"
|
||||
"SDL2/src/audio/dummy/*.c"
|
||||
"SDL2/src/audio/wasapi/*.c"
|
||||
"SDL2/src/audio/winmm/*.c"
|
||||
"SDL2/src/core/windows/*.c"
|
||||
"SDL2/src/cpuinfo/*.c"
|
||||
"SDL2/src/dynapi/*.c"
|
||||
"SDL2/src/events/*.c"
|
||||
"SDL2/src/file/*.c"
|
||||
"SDL2/src/filesystem/windows/*.c"
|
||||
"SDL2/src/haptic/*.c"
|
||||
"SDL2/src/haptic/dummy/*.c"
|
||||
"SDL2/src/haptic/windows/*.c"
|
||||
"SDL2/src/hidapi/*.c"
|
||||
"SDL2/src/joystick/*.c"
|
||||
"SDL2/src/joystick/dummy/*.c"
|
||||
"SDL2/src/joystick/hidapi/*.c"
|
||||
"SDL2/src/joystick/virtual/*.c"
|
||||
"SDL2/src/joystick/windows/*.c"
|
||||
"SDL2/src/libm/*.c"
|
||||
"SDL2/src/loadso/windows/*.c"
|
||||
"SDL2/src/locale/*.c"
|
||||
"SDL2/src/locale/windows/*.c"
|
||||
"SDL2/src/misc/*.c"
|
||||
"SDL2/src/misc/windows/*.c"
|
||||
"SDL2/src/power/*.c"
|
||||
"SDL2/src/power/windows/*.c"
|
||||
"SDL2/src/render/*.c"
|
||||
"SDL2/src/render/direct3d/*.c"
|
||||
"SDL2/src/render/direct3d11/*.c"
|
||||
"SDL2/src/render/direct3d12/*.c"
|
||||
"SDL2/src/render/opengl/*.c"
|
||||
"SDL2/src/render/opengles2/*.c"
|
||||
"SDL2/src/render/software/*.c"
|
||||
"SDL2/src/sensor/*.c"
|
||||
"SDL2/src/sensor/dummy/*.c"
|
||||
"SDL2/src/sensor/windows/*.c"
|
||||
"SDL2/src/stdlib/*.c"
|
||||
"SDL2/src/thread/*.c"
|
||||
"SDL2/src/thread/generic/SDL_syscond.c"
|
||||
"SDL2/src/thread/windows/*.c"
|
||||
"SDL2/src/timer/*.c"
|
||||
"SDL2/src/timer/windows/*.c"
|
||||
"SDL2/src/video/*.c"
|
||||
"SDL2/src/video/dummy/*.c"
|
||||
"SDL2/src/video/windows/*.c"
|
||||
"SDL2/src/video/yuv2rgb/*.c"
|
||||
)
|
||||
add_library(SDL2 STATIC ${_sdl2_sources})
|
||||
target_compile_definitions(SDL2 PRIVATE
|
||||
HAVE_LIBC
|
||||
SDL_LEAN_AND_MEAN
|
||||
SDL_RENDER_DISABLED
|
||||
)
|
||||
target_include_directories(SDL2 PUBLIC SDL2/include)
|
||||
target_link_libraries(SDL2 PRIVATE setupapi winmm imm32 version)
|
||||
elseif(APPLE)
|
||||
# Build SDL2 from source on macOS. SDL2's auto-detected SDL_config_macosx.h
|
||||
# selects the CoreAudio audio, Cocoa video, IOKit joystick/haptic, MFi
|
||||
# controller, pthread, and dlopen subsystems wired up below; SDL_RENDER_DISABLED
|
||||
# keeps the renderer subsystem out (xenia drives Metal directly).
|
||||
file(GLOB _sdl2_sources
|
||||
"SDL2/src/*.c"
|
||||
"SDL2/src/atomic/*.c"
|
||||
"SDL2/src/audio/*.c"
|
||||
"SDL2/src/audio/coreaudio/*.m"
|
||||
"SDL2/src/audio/disk/*.c"
|
||||
"SDL2/src/audio/dummy/*.c"
|
||||
"SDL2/src/cpuinfo/*.c"
|
||||
"SDL2/src/dynapi/*.c"
|
||||
"SDL2/src/events/*.c"
|
||||
"SDL2/src/file/*.c"
|
||||
"SDL2/src/file/cocoa/*.m"
|
||||
"SDL2/src/filesystem/cocoa/*.m"
|
||||
"SDL2/src/haptic/*.c"
|
||||
"SDL2/src/haptic/darwin/*.c"
|
||||
"SDL2/src/hidapi/*.c"
|
||||
"SDL2/src/joystick/*.c"
|
||||
"SDL2/src/joystick/darwin/*.c"
|
||||
"SDL2/src/joystick/dummy/*.c"
|
||||
"SDL2/src/joystick/hidapi/*.c"
|
||||
"SDL2/src/joystick/iphoneos/*.m"
|
||||
"SDL2/src/joystick/virtual/*.c"
|
||||
"SDL2/src/libm/*.c"
|
||||
"SDL2/src/loadso/dlopen/*.c"
|
||||
"SDL2/src/locale/*.c"
|
||||
"SDL2/src/locale/macosx/*.m"
|
||||
"SDL2/src/misc/*.c"
|
||||
"SDL2/src/misc/macosx/*.m"
|
||||
"SDL2/src/power/*.c"
|
||||
"SDL2/src/power/macosx/*.c"
|
||||
"SDL2/src/render/*.c"
|
||||
"SDL2/src/sensor/*.c"
|
||||
"SDL2/src/sensor/dummy/*.c"
|
||||
"SDL2/src/stdlib/*.c"
|
||||
"SDL2/src/thread/*.c"
|
||||
"SDL2/src/thread/pthread/*.c"
|
||||
"SDL2/src/timer/*.c"
|
||||
"SDL2/src/timer/unix/*.c"
|
||||
"SDL2/src/video/*.c"
|
||||
"SDL2/src/video/cocoa/*.m"
|
||||
"SDL2/src/video/dummy/*.c"
|
||||
"SDL2/src/video/yuv2rgb/*.c"
|
||||
)
|
||||
add_library(SDL2 STATIC ${_sdl2_sources})
|
||||
target_compile_definitions(SDL2 PRIVATE
|
||||
HAVE_LIBC
|
||||
SDL_LEAN_AND_MEAN
|
||||
SDL_RENDER_DISABLED
|
||||
# Apple ships CGL but not <EGL/egl.h>. SDL_USE_BUILTIN_OPENGL_DEFINITIONS
|
||||
# makes SDL_egl.h provide stub EGL typedefs, which is enough for the
|
||||
# whole EGL family (SDL_egl.c, SDL_cocoaopengles.m) to compile — the
|
||||
# actual EGL entry points are runtime-loaded via dlopen("libEGL.so") and
|
||||
# only fail if an app explicitly asks for SDL_GL_CONTEXT_PROFILE_ES.
|
||||
SDL_USE_BUILTIN_OPENGL_DEFINITIONS
|
||||
)
|
||||
# SDL_mfijoystick.m uses __weak — requires ARC. Other .m files don't rely
|
||||
# on explicit retain/release, so ARC across the whole OBJC set is safe.
|
||||
target_compile_options(SDL2 PRIVATE
|
||||
$<$<COMPILE_LANGUAGE:OBJC>:-fobjc-arc>
|
||||
)
|
||||
target_include_directories(SDL2 PUBLIC SDL2/include)
|
||||
target_link_libraries(SDL2 PRIVATE
|
||||
"-framework AVFoundation"
|
||||
"-framework AudioToolbox"
|
||||
"-framework Carbon"
|
||||
"-framework Cocoa"
|
||||
"-framework CoreAudio"
|
||||
"-framework CoreFoundation"
|
||||
"-framework CoreGraphics"
|
||||
"-framework CoreHaptics"
|
||||
"-framework CoreServices"
|
||||
"-framework CoreVideo"
|
||||
"-framework ForceFeedback"
|
||||
"-framework Foundation"
|
||||
"-framework GameController"
|
||||
"-framework IOKit"
|
||||
"-framework Metal"
|
||||
"-framework OpenGL"
|
||||
"-framework QuartzCore"
|
||||
iconv
|
||||
)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
# Use system SDL2 on Linux
|
||||
add_library(SDL2 INTERFACE)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(SDL2_PKG REQUIRED sdl2)
|
||||
target_include_directories(SDL2 INTERFACE ${SDL2_PKG_INCLUDE_DIRS})
|
||||
target_link_libraries(SDL2 INTERFACE ${SDL2_PKG_LDFLAGS})
|
||||
target_compile_options(SDL2 INTERFACE ${SDL2_PKG_CFLAGS_OTHER})
|
||||
# Build SDL3 from source on every platform using upstream's own CMake. Xenia
|
||||
# drives windowing/rendering/GPU directly, so the SDL subsystems we don't
|
||||
# consume are switched off. Only audio + joystick/gamepad/haptic + events are
|
||||
# exercised by xenia-apu-sdl / xenia-hid-sdl. Linux builds shared so the
|
||||
# runtime dlopen("libSDL3.so.0") path in system_gnulinux.cc can resolve and so
|
||||
# linuxdeploy can bundle the .so into the AppImage; Windows/macOS build static.
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
set(SDL_SHARED ON CACHE INTERNAL "")
|
||||
set(SDL_STATIC OFF CACHE INTERNAL "")
|
||||
else()
|
||||
set(SDL_SHARED OFF CACHE INTERNAL "")
|
||||
set(SDL_STATIC ON CACHE INTERNAL "")
|
||||
endif()
|
||||
set(SDL_TEST_LIBRARY OFF CACHE INTERNAL "")
|
||||
set(SDL_INSTALL OFF CACHE INTERNAL "")
|
||||
set(SDL_DISABLE_INSTALL ON CACHE INTERNAL "")
|
||||
set(SDL_CAMERA OFF CACHE INTERNAL "")
|
||||
set(SDL_DIALOG OFF CACHE INTERNAL "")
|
||||
set(SDL_GPU OFF CACHE INTERNAL "")
|
||||
set(SDL_RENDER OFF CACHE INTERNAL "")
|
||||
set(SDL_SENSOR OFF CACHE INTERNAL "")
|
||||
set(SDL_HIDAPI_LIBUSB OFF CACHE INTERNAL "")
|
||||
|
||||
# SDL3's sdlcommands.cmake uses file(GLOB CONFIGURE_DEPENDS ...) for its
|
||||
# source lists. CMake honours that by emitting a "Re-checking globbed
|
||||
# directories" probe on every build — slow and pointless for a submodule
|
||||
# pinned to a release tag. Strip the flag in place at configure time;
|
||||
# .gitmodules marks SDL3 as ignore=dirty so the in-tree edit doesn't pollute
|
||||
# git status. Same pattern as snappy and zlib-ng.
|
||||
set(_sdl3_cmd "${CMAKE_CURRENT_SOURCE_DIR}/SDL3/cmake/sdlcommands.cmake")
|
||||
if(EXISTS "${_sdl3_cmd}")
|
||||
file(READ "${_sdl3_cmd}" _sdl3_cmd_content)
|
||||
string(REPLACE " CONFIGURE_DEPENDS " " " _sdl3_cmd_patched "${_sdl3_cmd_content}")
|
||||
if(NOT "${_sdl3_cmd_patched}" STREQUAL "${_sdl3_cmd_content}")
|
||||
file(WRITE "${_sdl3_cmd}" "${_sdl3_cmd_patched}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_subdirectory(SDL3 EXCLUDE_FROM_ALL)
|
||||
# Consistent target name for downstream link calls.
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
add_library(SDL3 ALIAS SDL3-shared)
|
||||
else()
|
||||
add_library(SDL3 ALIAS SDL3-static)
|
||||
endif()
|
||||
|
||||
# SDL3 explicitly bakes /W3 into its target COMPILE_OPTIONS, which clashes
|
||||
# with the directory-level /W0 above and produces D9025 noise on every TU.
|
||||
# Strip the /W* and /WX flags it added so only the directory-level /W0
|
||||
# survives. Mirrors the same workaround we apply to SPIRV-Tools.
|
||||
if(MSVC)
|
||||
foreach(_sdl3_target SDL3-static SDL_uclibc)
|
||||
if(TARGET ${_sdl3_target})
|
||||
get_target_property(_opts ${_sdl3_target} COMPILE_OPTIONS)
|
||||
if(_opts)
|
||||
list(FILTER _opts EXCLUDE REGEX "^/[Ww]([0-9]|all|X)$")
|
||||
set_target_properties(${_sdl3_target} PROPERTIES COMPILE_OPTIONS "${_opts}")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# ==============================================================================
|
||||
|
||||
Vendored
-1
Submodule third_party/SDL2 deleted from e11183ea6c
+1
Submodule third_party/SDL3 added at d9d5536704
Reference in New Issue
Block a user