forked from eden-emu/eden
Compare commits
11 commits
deps-cpp-j
...
master
Author | SHA1 | Date | |
---|---|---|---|
e4953d5866 | |||
43f450499c | |||
dee960ccd9 | |||
18942b7ee0 | |||
f75a8c44c7 | |||
07cab5ba07 | |||
896bfcdaaa | |||
9ccb792d29 | |||
bbeb7dd56b | |||
eeb68768d6 | |||
0b29fb7c8a |
20 changed files with 291 additions and 43 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -19,9 +19,6 @@
|
||||||
[submodule "cpp-httplib"]
|
[submodule "cpp-httplib"]
|
||||||
path = externals/cpp-httplib
|
path = externals/cpp-httplib
|
||||||
url = https://github.com/yhirose/cpp-httplib.git
|
url = https://github.com/yhirose/cpp-httplib.git
|
||||||
[submodule "ffmpeg"]
|
|
||||||
path = externals/ffmpeg/ffmpeg
|
|
||||||
url = https://github.com/FFmpeg/FFmpeg.git
|
|
||||||
[submodule "vcpkg"]
|
[submodule "vcpkg"]
|
||||||
path = externals/vcpkg
|
path = externals/vcpkg
|
||||||
url = https://github.com/microsoft/vcpkg.git
|
url = https://github.com/microsoft/vcpkg.git
|
||||||
|
|
|
@ -491,6 +491,8 @@ set(FFmpeg_COMPONENTS
|
||||||
avutil
|
avutil
|
||||||
swscale)
|
swscale)
|
||||||
|
|
||||||
|
set(CPM_SOURCE_CACHE ${CMAKE_SOURCE_DIR}/.cache/cpm)
|
||||||
|
|
||||||
add_subdirectory(externals)
|
add_subdirectory(externals)
|
||||||
|
|
||||||
if (ENABLE_QT)
|
if (ENABLE_QT)
|
||||||
|
|
9
externals/CMakeLists.txt
vendored
9
externals/CMakeLists.txt
vendored
|
@ -84,17 +84,22 @@ if (YUZU_USE_EXTERNAL_SDL2)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
include(CPM)
|
include(CPM)
|
||||||
set(CPM_SOURCE_CACHE ${CMAKE_SOURCE_DIR}/.cache/cpm)
|
|
||||||
set(CPM_USE_LOCAL_PACKAGES OFF)
|
set(CPM_USE_LOCAL_PACKAGES OFF)
|
||||||
|
|
||||||
if ("${YUZU_SYSTEM_PROFILE}" STREQUAL "steamdeck")
|
if ("${YUZU_SYSTEM_PROFILE}" STREQUAL "steamdeck")
|
||||||
set(SDL_HASH cc016b0046)
|
set(SDL_HASH cc016b0046)
|
||||||
set(SDL_PIPEWIRE OFF) # build errors out with this on
|
set(SDL_PIPEWIRE OFF) # build errors out with this on
|
||||||
|
set(SDL_SHA512SUM 34d5ef58da6a4f9efa6689c82f67badcbd741f5a4f562a9c2c30828fa839830fb07681c5dc6a7851520e261c8405a416ac0a2c2513b51984fb3b4fa4dcb3e20b)
|
||||||
else()
|
else()
|
||||||
set(SDL_HASH 2e4c12cd2c)
|
set(SDL_HASH 2e4c12cd2c)
|
||||||
|
set(SDL_SHA512SUM d95af47f469a312876f8ab361074a1e7b8083db19935a102d9c6e5887ace6008e64475a8c54b00164b40cad86492bb1b2366084efdd0b2555e5fea6d9c5da80e)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
CPMAddPackage("gh:libsdl-org/SDL#${SDL_HASH}")
|
CPMAddPackage(
|
||||||
|
NAME SDL2
|
||||||
|
URL "https://github.com/libsdl-org/SDL/archive/${SDL_HASH}.zip"
|
||||||
|
URL_HASH SHA512=${SDL_SHA512SUM}
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# ENet
|
# ENet
|
||||||
|
|
2
externals/cpp-httplib
vendored
2
externals/cpp-httplib
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit ca5fe354fb83194bc72a676c4cc4136fca5316d0
|
Subproject commit a609330e4c6374f741d3b369269f7848255e1954
|
13
externals/ffmpeg/CMakeLists.txt
vendored
13
externals/ffmpeg/CMakeLists.txt
vendored
|
@ -19,8 +19,17 @@ if (NOT WIN32 AND NOT ANDROID)
|
||||||
message(FATAL_ERROR "Required program `autoconf` not found.")
|
message(FATAL_ERROR "Required program `autoconf` not found.")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(FFmpeg_PREFIX ${PROJECT_SOURCE_DIR}/externals/ffmpeg/ffmpeg)
|
include(CPM)
|
||||||
set(FFmpeg_BUILD_DIR ${PROJECT_BINARY_DIR}/externals/ffmpeg-build)
|
set(CPM_USE_LOCAL_PACKAGES OFF)
|
||||||
|
|
||||||
|
CPMAddPackage(
|
||||||
|
NAME ffmpeg
|
||||||
|
URL "https://github.com/ffmpeg/ffmpeg/archive/9c1294eadd.zip"
|
||||||
|
URL_HASH SHA512=2076e4cb843787c44718c70c4452517273dbc963ef98442f343762ade6c7b9f78555ae9b50a7c628844a15d8cb5e866c04e2f1acfb77093cea4fbc9edf3ad21a
|
||||||
|
)
|
||||||
|
|
||||||
|
set(FFmpeg_PREFIX ${ffmpeg_SOURCE_DIR})
|
||||||
|
set(FFmpeg_BUILD_DIR ${ffmpeg_BINARY_DIR})
|
||||||
set(FFmpeg_MAKEFILE ${FFmpeg_BUILD_DIR}/Makefile)
|
set(FFmpeg_MAKEFILE ${FFmpeg_BUILD_DIR}/Makefile)
|
||||||
make_directory(${FFmpeg_BUILD_DIR})
|
make_directory(${FFmpeg_BUILD_DIR})
|
||||||
|
|
||||||
|
|
1
externals/ffmpeg/ffmpeg
vendored
1
externals/ffmpeg/ffmpeg
vendored
|
@ -1 +0,0 @@
|
||||||
Subproject commit 9c1294eaddb88cb0e044c675ccae059a85fc9c6c
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -334,6 +337,48 @@ std::vector<std::string> ListCubebSinkDevices(bool capture) {
|
||||||
return device_list;
|
return device_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* REVERSION TO 3833 - function GetCubebLatency REINTRODUCED FROM 3833 - DIABLO 3 FIX */
|
||||||
|
u32 GetCubebLatency() {
|
||||||
|
cubeb* ctx;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
auto com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Init cubeb
|
||||||
|
if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) {
|
||||||
|
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
|
||||||
|
// Return a large latency so we choose SDL instead.
|
||||||
|
return 10000u;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (SUCCEEDED(com_init_result)) {
|
||||||
|
CoUninitialize();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Get min latency
|
||||||
|
cubeb_stream_params params{};
|
||||||
|
params.rate = TargetSampleRate;
|
||||||
|
params.channels = 2;
|
||||||
|
params.format = CUBEB_SAMPLE_S16LE;
|
||||||
|
params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||||
|
params.layout = CUBEB_LAYOUT_STEREO;
|
||||||
|
|
||||||
|
u32 latency{0};
|
||||||
|
const auto latency_error = cubeb_get_min_latency(ctx, ¶ms, &latency);
|
||||||
|
if (latency_error != CUBEB_OK) {
|
||||||
|
LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error);
|
||||||
|
latency = TargetSampleCount * 2;
|
||||||
|
}
|
||||||
|
latency = std::max(latency, TargetSampleCount * 2);
|
||||||
|
cubeb_destroy(ctx);
|
||||||
|
return latency;
|
||||||
|
}
|
||||||
|
|
||||||
|
// REVERTED back to 3833 - Below namespace section and function IsCubebSuitable() removed, reverting to GetCubebLatency() above. - DIABLO 3 FIX
|
||||||
|
/*
|
||||||
namespace {
|
namespace {
|
||||||
static long TmpDataCallback(cubeb_stream*, void*, const void*, void*, long) {
|
static long TmpDataCallback(cubeb_stream*, void*, const void*, void*, long) {
|
||||||
return TargetSampleCount;
|
return TargetSampleCount;
|
||||||
|
@ -400,5 +445,6 @@ bool IsCubebSuitable() {
|
||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
} // namespace AudioCore::Sink
|
} // namespace AudioCore::Sink
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -96,12 +99,20 @@ private:
|
||||||
*/
|
*/
|
||||||
std::vector<std::string> ListCubebSinkDevices(bool capture);
|
std::vector<std::string> ListCubebSinkDevices(bool capture);
|
||||||
|
|
||||||
|
// REVERSION - function GetCubebLatency() reintroduced from EA-3833 - DIABLO 3 FIX
|
||||||
|
/**
|
||||||
|
* Get the reported latency for this sink.
|
||||||
|
*
|
||||||
|
* @return Minimum latency for this sink.
|
||||||
|
*/
|
||||||
|
u32 GetCubebLatency();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this backend is suitable for use.
|
* Check if this backend is suitable for use.
|
||||||
* Checks if enabled, its latency, whether it opens successfully, etc.
|
* Checks if enabled, its latency, whether it opens successfully, etc.
|
||||||
*
|
*
|
||||||
* @return True is this backend is suitable, false otherwise.
|
* @return True is this backend is suitable, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool IsCubebSuitable();
|
// bool IsCubebSuitable(); // REVERTED BACK TO GetCubebLatency() FROM 3833
|
||||||
|
|
||||||
} // namespace AudioCore::Sink
|
} // namespace AudioCore::Sink
|
||||||
|
|
|
@ -234,6 +234,13 @@ std::vector<std::string> ListSDLSinkDevices(bool capture) {
|
||||||
return device_list;
|
return device_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* REVERSION to 3833 - function GetSDLLatency() REINTRODUCED FROM 3833 - DIABLO 3 FIX */
|
||||||
|
u32 GetSDLLatency() {
|
||||||
|
return TargetSampleCount * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// REVERTED back to 3833 - Below function IsSDLSuitable() removed, reverting to GetSDLLatency() above. - DIABLO 3 FIX
|
||||||
|
/*
|
||||||
bool IsSDLSuitable() {
|
bool IsSDLSuitable() {
|
||||||
#if !defined(HAVE_SDL2)
|
#if !defined(HAVE_SDL2)
|
||||||
return false;
|
return false;
|
||||||
|
@ -271,5 +278,6 @@ bool IsSDLSuitable() {
|
||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
} // namespace AudioCore::Sink
|
} // namespace AudioCore::Sink
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -87,12 +90,20 @@ private:
|
||||||
*/
|
*/
|
||||||
std::vector<std::string> ListSDLSinkDevices(bool capture);
|
std::vector<std::string> ListSDLSinkDevices(bool capture);
|
||||||
|
|
||||||
|
// REVERSION - function GetSDLLatency() reintroduced from EA-3833 - DIABLO 3 FIX
|
||||||
/**
|
/**
|
||||||
|
* Get the reported latency for this sink.
|
||||||
|
*
|
||||||
|
* @return Minimum latency for this sink.
|
||||||
|
*/
|
||||||
|
u32 GetSDLLatency();
|
||||||
|
|
||||||
|
/** REVERTED back to 3833 - Below function IsSDLSuitable() removed, reverting to GetSDLLatency() above. - DIABLO 3 FIX
|
||||||
* Check if this backend is suitable for use.
|
* Check if this backend is suitable for use.
|
||||||
* Checks if enabled, its latency, whether it opens successfully, etc.
|
* Checks if enabled, its latency, whether it opens successfully, etc.
|
||||||
*
|
*
|
||||||
* @return True is this backend is suitable, false otherwise.
|
* @return True is this backend is suitable, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool IsSDLSuitable();
|
//bool IsSDLSuitable(); // REVERTED for GetSDLLatency() from EA-3833
|
||||||
|
|
||||||
} // namespace AudioCore::Sink
|
} // namespace AudioCore::Sink
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -25,7 +28,8 @@ namespace {
|
||||||
struct SinkDetails {
|
struct SinkDetails {
|
||||||
using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view);
|
using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view);
|
||||||
using ListDevicesFn = std::vector<std::string> (*)(bool);
|
using ListDevicesFn = std::vector<std::string> (*)(bool);
|
||||||
using SuitableFn = bool (*)();
|
using LatencyFn = u32 (*)(); // REINTRODUCED FROM 3833 - DIABLO 3 FIX
|
||||||
|
// using SuitableFn = bool (*)(); // REVERTED FOR ABOVE - DIABLO 3 FIX
|
||||||
|
|
||||||
/// Name for this sink.
|
/// Name for this sink.
|
||||||
Settings::AudioEngine id;
|
Settings::AudioEngine id;
|
||||||
|
@ -33,10 +37,18 @@ struct SinkDetails {
|
||||||
FactoryFn factory;
|
FactoryFn factory;
|
||||||
/// A method to call to list available devices.
|
/// A method to call to list available devices.
|
||||||
ListDevicesFn list_devices;
|
ListDevicesFn list_devices;
|
||||||
|
/// Method to get the latency of this backend - REINTRODUCED FROM 3833 - DIABLO 3 FIX
|
||||||
|
LatencyFn latency;
|
||||||
/// Check whether this backend is suitable to be used.
|
/// Check whether this backend is suitable to be used.
|
||||||
SuitableFn is_suitable;
|
/// SuitableFn is_suitable; // REVERTED FOR LatencyFn latency ABOVE - DIABLO 3 FIX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// NOTE TO PROBABLY FIX LATER FOR ANDROID - the return value "0u" for the first HAVE_OBOE
|
||||||
|
// section below was just copied from the null section so there's a somewhat valid value
|
||||||
|
// being returned, since the previous "true" value probably isn't compatible with the
|
||||||
|
// previous EA-3833 code. (HAVE_OBOE was introduced in a later release.) Eventually need
|
||||||
|
// to change "0u" for something else directly from the oboe_sink.cpp functions.
|
||||||
|
|
||||||
// sink_details is ordered in terms of desirability, with the best choice at the top.
|
// sink_details is ordered in terms of desirability, with the best choice at the top.
|
||||||
constexpr SinkDetails sink_details[] = {
|
constexpr SinkDetails sink_details[] = {
|
||||||
#ifdef HAVE_OBOE
|
#ifdef HAVE_OBOE
|
||||||
|
@ -46,7 +58,7 @@ constexpr SinkDetails sink_details[] = {
|
||||||
return std::make_unique<OboeSink>();
|
return std::make_unique<OboeSink>();
|
||||||
},
|
},
|
||||||
[](bool capture) { return std::vector<std::string>{"Default"}; },
|
[](bool capture) { return std::vector<std::string>{"Default"}; },
|
||||||
[]() { return true; },
|
[]() { return 0u; },
|
||||||
},
|
},
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_CUBEB
|
#ifdef HAVE_CUBEB
|
||||||
|
@ -56,7 +68,7 @@ constexpr SinkDetails sink_details[] = {
|
||||||
return std::make_unique<CubebSink>(device_id);
|
return std::make_unique<CubebSink>(device_id);
|
||||||
},
|
},
|
||||||
&ListCubebSinkDevices,
|
&ListCubebSinkDevices,
|
||||||
&IsCubebSuitable,
|
&GetCubebLatency,
|
||||||
},
|
},
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_SDL2
|
#ifdef HAVE_SDL2
|
||||||
|
@ -66,7 +78,7 @@ constexpr SinkDetails sink_details[] = {
|
||||||
return std::make_unique<SDLSink>(device_id);
|
return std::make_unique<SDLSink>(device_id);
|
||||||
},
|
},
|
||||||
&ListSDLSinkDevices,
|
&ListSDLSinkDevices,
|
||||||
&IsSDLSuitable,
|
&GetSDLLatency,
|
||||||
},
|
},
|
||||||
#endif
|
#endif
|
||||||
SinkDetails{
|
SinkDetails{
|
||||||
|
@ -75,7 +87,7 @@ constexpr SinkDetails sink_details[] = {
|
||||||
return std::make_unique<NullSink>(device_id);
|
return std::make_unique<NullSink>(device_id);
|
||||||
},
|
},
|
||||||
[](bool capture) { return std::vector<std::string>{"null"}; },
|
[](bool capture) { return std::vector<std::string>{"null"}; },
|
||||||
[]() { return true; },
|
[]() { return 0u; },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -88,6 +100,8 @@ const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) {
|
||||||
auto iter = find_backend(sink_id);
|
auto iter = find_backend(sink_id);
|
||||||
|
|
||||||
if (sink_id == Settings::AudioEngine::Auto) {
|
if (sink_id == Settings::AudioEngine::Auto) {
|
||||||
|
// REVERTED TO 3833 BELOW - DIABLO 3 FIX
|
||||||
|
/*
|
||||||
// Auto-select a backend. Use the sink details ordering, preferring cubeb first, checking
|
// Auto-select a backend. Use the sink details ordering, preferring cubeb first, checking
|
||||||
// that the backend is available and suitable to use.
|
// that the backend is available and suitable to use.
|
||||||
for (auto& details : sink_details) {
|
for (auto& details : sink_details) {
|
||||||
|
@ -96,14 +110,29 @@ const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/ // END REVERTED CODE - DIABLO 3 FIX
|
||||||
|
|
||||||
|
// BEGIN REINTRODUCED FROM 3833 - REPLACED CODE BLOCK ABOVE - DIABLO 3 FIX
|
||||||
|
// Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which
|
||||||
|
// causes audio issues, in that case go with SDL.
|
||||||
|
#if defined(HAVE_CUBEB) && defined(HAVE_SDL2)
|
||||||
|
iter = find_backend(Settings::AudioEngine::Cubeb);
|
||||||
|
if (iter->latency() > TargetSampleCount * 3) {
|
||||||
|
iter = find_backend(Settings::AudioEngine::Sdl2);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
iter = std::begin(sink_details);
|
||||||
|
#endif
|
||||||
|
// END REINTRODUCED SECTION FROM 3833 - DIABLO 3 FIX
|
||||||
LOG_INFO(Service_Audio, "Auto-selecting the {} backend",
|
LOG_INFO(Service_Audio, "Auto-selecting the {} backend",
|
||||||
Settings::CanonicalizeEnum(iter->id));
|
Settings::CanonicalizeEnum(iter->id));
|
||||||
|
/* BEGIN REMOVED - REVERTING BACK TO 3833, this didn't exist at all. - DIABLO 3 FIX
|
||||||
} else {
|
} else {
|
||||||
if (iter != std::end(sink_details) && !iter->is_suitable()) {
|
if (iter != std::end(sink_details) && !iter->is_suitable()) {
|
||||||
LOG_ERROR(Service_Audio, "Selected backend {} is not suitable, falling back to null",
|
LOG_ERROR(Service_Audio, "Selected backend {} is not suitable, falling back to null",
|
||||||
Settings::CanonicalizeEnum(iter->id));
|
Settings::CanonicalizeEnum(iter->id));
|
||||||
iter = find_backend(Settings::AudioEngine::Null);
|
iter = find_backend(Settings::AudioEngine::Null);
|
||||||
}
|
} */ // END REMOVED REVERT - DIABLO 3 FIX
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iter == std::end(sink_details)) {
|
if (iter == std::end(sink_details)) {
|
||||||
|
|
|
@ -728,22 +728,26 @@ Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& li
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://android.googlesource.com/platform/frameworks/native/%2B/master/libs/gui/BufferQueueProducer.cpp#1457
|
||||||
Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
|
Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
|
||||||
LOG_DEBUG(Service_Nvnflinger, "api = {}", api);
|
LOG_DEBUG(Service_Nvnflinger, "disconnect api = {}", api);
|
||||||
|
|
||||||
Status status = Status::NoError;
|
|
||||||
std::shared_ptr<IConsumerListener> listener;
|
std::shared_ptr<IConsumerListener> listener;
|
||||||
|
Status status = Status::NoError;
|
||||||
|
|
||||||
{
|
{
|
||||||
std::scoped_lock lock{core->mutex};
|
std::scoped_lock lock{core->mutex};
|
||||||
|
|
||||||
core->WaitWhileAllocatingLocked();
|
core->WaitWhileAllocatingLocked();
|
||||||
|
|
||||||
if (core->is_abandoned) {
|
if (core->is_abandoned) {
|
||||||
// Disconnecting after the surface has been abandoned is a no-op.
|
|
||||||
return Status::NoError;
|
return Status::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (core->connected_api == NativeWindowApi::NoConnectedApi) {
|
||||||
|
LOG_DEBUG(Service_Nvnflinger, "disconnect: not connected (req = {})", api);
|
||||||
|
return Status::NoInit;
|
||||||
|
}
|
||||||
|
|
||||||
switch (api) {
|
switch (api) {
|
||||||
case NativeWindowApi::Egl:
|
case NativeWindowApi::Egl:
|
||||||
case NativeWindowApi::Cpu:
|
case NativeWindowApi::Cpu:
|
||||||
|
@ -758,20 +762,20 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
|
||||||
buffer_wait_event->Signal();
|
buffer_wait_event->Signal();
|
||||||
listener = core->consumer_listener;
|
listener = core->consumer_listener;
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR(Service_Nvnflinger, "still connected to another api (cur = {} req = {})",
|
LOG_ERROR(Service_Nvnflinger,
|
||||||
|
"disconnect: still connected to another api (cur = {} req = {})",
|
||||||
core->connected_api, api);
|
core->connected_api, api);
|
||||||
status = Status::BadValue;
|
status = Status::BadValue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_ERROR(Service_Nvnflinger, "unknown api = {}", api);
|
LOG_ERROR(Service_Nvnflinger, "disconnect: unknown api = {}", api);
|
||||||
status = Status::BadValue;
|
status = Status::BadValue;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call back without lock held
|
if (listener) {
|
||||||
if (listener != nullptr) {
|
|
||||||
listener->OnBuffersReleased();
|
listener->OnBuffersReleased();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,17 +40,22 @@ Result IHOSBinderDriver::TransactParcel(s32 binder_id, u32 transaction_id,
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IHOSBinderDriver::AdjustRefcount(s32 binder_id, s32 addval, s32 type) {
|
Result IHOSBinderDriver::AdjustRefcount(s32 binder_id, s32 addval, s32 type) {
|
||||||
LOG_WARNING(Service_VI, "(STUBBED) called id={}, addval={}, type={}", binder_id, addval, type);
|
LOG_DEBUG(Service_VI, "called id={}, addval={}, type={}", binder_id, addval, type);
|
||||||
|
R_UNLESS(type == 0 || type == 1, ResultUnknown);
|
||||||
|
m_server->AdjustRefcount(binder_id, addval, type == 1);
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IHOSBinderDriver::GetNativeHandle(s32 binder_id, u32 type_id,
|
Result IHOSBinderDriver::GetNativeHandle(s32 binder_id, u32 type_id,
|
||||||
OutCopyHandle<Kernel::KReadableEvent> out_handle) {
|
OutCopyHandle<Kernel::KReadableEvent> out_handle) {
|
||||||
LOG_WARNING(Service_VI, "(STUBBED) called id={}, type_id={}", binder_id, type_id);
|
LOG_DEBUG(Service_VI, "called id={}, type_id={}", binder_id, type_id);
|
||||||
|
|
||||||
const auto binder = m_server->TryGetBinder(binder_id);
|
const auto binder = m_server->TryGetBinder(binder_id);
|
||||||
R_UNLESS(binder != nullptr, ResultUnknown);
|
R_UNLESS(binder != nullptr, ResultUnknown);
|
||||||
|
|
||||||
|
auto native_handle = binder->GetNativeHandle(type_id);
|
||||||
|
R_UNLESS(native_handle != nullptr, ResultUnknown);
|
||||||
|
|
||||||
*out_handle = binder->GetNativeHandle(type_id);
|
*out_handle = binder->GetNativeHandle(type_id);
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
|
@ -17,6 +17,7 @@ s32 HosBinderDriverServer::RegisterBinder(std::shared_ptr<android::IBinder>&& bi
|
||||||
last_id++;
|
last_id++;
|
||||||
|
|
||||||
binders[last_id] = std::move(binder);
|
binders[last_id] = std::move(binder);
|
||||||
|
refcounts[last_id] = {}; // strong = 1, weak = 0
|
||||||
|
|
||||||
return last_id;
|
return last_id;
|
||||||
}
|
}
|
||||||
|
@ -25,6 +26,29 @@ void HosBinderDriverServer::UnregisterBinder(s32 binder_id) {
|
||||||
std::scoped_lock lk{lock};
|
std::scoped_lock lk{lock};
|
||||||
|
|
||||||
binders.erase(binder_id);
|
binders.erase(binder_id);
|
||||||
|
refcounts.erase(binder_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HosBinderDriverServer::AdjustRefcount(s32 binder_id, s32 delta, bool is_weak) {
|
||||||
|
std::scoped_lock lk{lock};
|
||||||
|
|
||||||
|
auto search_rc = refcounts.find(binder_id);
|
||||||
|
if (search_rc == refcounts.end()) {
|
||||||
|
LOG_WARNING(Service_VI, "AdjustRefcount called for unknown binder id {}", binder_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& rc = search_rc->second;
|
||||||
|
s32& counter = is_weak ? rc.weak : rc.strong;
|
||||||
|
counter += delta;
|
||||||
|
|
||||||
|
if (counter < 0)
|
||||||
|
counter = 0;
|
||||||
|
|
||||||
|
if (rc.strong == 0 && rc.weak == 0) {
|
||||||
|
binders.erase(binder_id);
|
||||||
|
refcounts.erase(search_rc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<android::IBinder> HosBinderDriverServer::TryGetBinder(s32 id) const {
|
std::shared_ptr<android::IBinder> HosBinderDriverServer::TryGetBinder(s32 id) const {
|
||||||
|
|
|
@ -26,10 +26,18 @@ public:
|
||||||
|
|
||||||
std::shared_ptr<android::IBinder> TryGetBinder(s32 id) const;
|
std::shared_ptr<android::IBinder> TryGetBinder(s32 id) const;
|
||||||
|
|
||||||
|
void AdjustRefcount(s32 binder_id, s32 delta, bool is_weak);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_map<s32, std::shared_ptr<android::IBinder>> binders;
|
struct RefCounts {
|
||||||
|
s32 strong{1};
|
||||||
|
s32 weak{0};
|
||||||
|
};
|
||||||
|
|
||||||
mutable std::mutex lock;
|
mutable std::mutex lock;
|
||||||
s32 last_id{};
|
s32 last_id = 0;
|
||||||
|
std::unordered_map<s32, std::shared_ptr<android::IBinder>> binders;
|
||||||
|
std::unordered_map<s32, RefCounts> refcounts;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::Nvnflinger
|
} // namespace Service::Nvnflinger
|
||||||
|
|
|
@ -41,6 +41,8 @@ enum class IoMode : u32 {
|
||||||
enum class OptionType : u32 {
|
enum class OptionType : u32 {
|
||||||
DoNotCloseSocket = 0,
|
DoNotCloseSocket = 0,
|
||||||
GetServerCertChain = 1,
|
GetServerCertChain = 1,
|
||||||
|
SkipDefaultVerify = 2,
|
||||||
|
EnableAlpn = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is nn::ssl::sf::SslVersion
|
// This is nn::ssl::sf::SslVersion
|
||||||
|
@ -93,11 +95,11 @@ public:
|
||||||
{20, nullptr, "SetRenegotiationMode"},
|
{20, nullptr, "SetRenegotiationMode"},
|
||||||
{21, nullptr, "GetRenegotiationMode"},
|
{21, nullptr, "GetRenegotiationMode"},
|
||||||
{22, &ISslConnection::SetOption, "SetOption"},
|
{22, &ISslConnection::SetOption, "SetOption"},
|
||||||
{23, nullptr, "GetOption"},
|
{23, &ISslConnection::GetOption, "GetOption"},
|
||||||
{24, nullptr, "GetVerifyCertErrors"},
|
{24, nullptr, "GetVerifyCertErrors"},
|
||||||
{25, nullptr, "GetCipherInfo"},
|
{25, nullptr, "GetCipherInfo"},
|
||||||
{26, nullptr, "SetNextAlpnProto"},
|
{26, &ISslConnection::SetNextAlpnProto, "SetNextAlpnProto"},
|
||||||
{27, nullptr, "GetNextAlpnProto"},
|
{27, &ISslConnection::GetNextAlpnProto, "GetNextAlpnProto"},
|
||||||
{28, nullptr, "SetDtlsSocketDescriptor"},
|
{28, nullptr, "SetDtlsSocketDescriptor"},
|
||||||
{29, nullptr, "GetDtlsHandshakeTimeout"},
|
{29, nullptr, "GetDtlsHandshakeTimeout"},
|
||||||
{30, nullptr, "SetPrivateOption"},
|
{30, nullptr, "SetPrivateOption"},
|
||||||
|
@ -140,7 +142,10 @@ private:
|
||||||
std::optional<int> fd_to_close;
|
std::optional<int> fd_to_close;
|
||||||
bool do_not_close_socket = false;
|
bool do_not_close_socket = false;
|
||||||
bool get_server_cert_chain = false;
|
bool get_server_cert_chain = false;
|
||||||
|
bool skip_default_verify = false;
|
||||||
|
bool enable_alpn = false;
|
||||||
std::shared_ptr<Network::SocketBase> socket;
|
std::shared_ptr<Network::SocketBase> socket;
|
||||||
|
std::vector<u8> next_alpn_proto;
|
||||||
bool did_handshake = false;
|
bool did_handshake = false;
|
||||||
|
|
||||||
Result SetSocketDescriptorImpl(s32* out_fd, s32 fd) {
|
Result SetSocketDescriptorImpl(s32* out_fd, s32 fd) {
|
||||||
|
@ -381,6 +386,12 @@ private:
|
||||||
case OptionType::GetServerCertChain:
|
case OptionType::GetServerCertChain:
|
||||||
get_server_cert_chain = static_cast<bool>(parameters.value);
|
get_server_cert_chain = static_cast<bool>(parameters.value);
|
||||||
break;
|
break;
|
||||||
|
case OptionType::SkipDefaultVerify:
|
||||||
|
skip_default_verify = static_cast<bool>(parameters.value);
|
||||||
|
break;
|
||||||
|
case OptionType::EnableAlpn:
|
||||||
|
enable_alpn = static_cast<bool>(parameters.value);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_WARNING(Service_SSL, "Unknown option={}, value={}", parameters.option,
|
LOG_WARNING(Service_SSL, "Unknown option={}, value={}", parameters.option,
|
||||||
parameters.value);
|
parameters.value);
|
||||||
|
@ -389,6 +400,63 @@ private:
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetOption(HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto option = rp.PopRaw<OptionType>();
|
||||||
|
|
||||||
|
u8 value = 0;
|
||||||
|
|
||||||
|
switch (option) {
|
||||||
|
case OptionType::DoNotCloseSocket:
|
||||||
|
value = static_cast<u8>(do_not_close_socket);
|
||||||
|
break;
|
||||||
|
case OptionType::GetServerCertChain:
|
||||||
|
value = static_cast<u8>(get_server_cert_chain);
|
||||||
|
break;
|
||||||
|
case OptionType::SkipDefaultVerify:
|
||||||
|
value = static_cast<u8>(skip_default_verify);
|
||||||
|
break;
|
||||||
|
case OptionType::EnableAlpn:
|
||||||
|
value = static_cast<u8>(enable_alpn);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_WARNING(Service_SSL, "Unknown option={}", option);
|
||||||
|
value = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_SSL, "GetOption called, option={}, ret value={}", option, value);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.Push<u8>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetNextAlpnProto(HLERequestContext& ctx) {
|
||||||
|
const auto data = ctx.ReadBuffer(0);
|
||||||
|
next_alpn_proto.assign(data.begin(), data.end());
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_SSL, "SetNextAlpnProto called, size={}", next_alpn_proto.size());
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetNextAlpnProto(HLERequestContext& ctx) {
|
||||||
|
const size_t writable = ctx.GetWriteBufferSize();
|
||||||
|
const size_t to_write = std::min(next_alpn_proto.size(), writable);
|
||||||
|
|
||||||
|
if (to_write != 0) {
|
||||||
|
ctx.WriteBuffer(std::span<const u8>(next_alpn_proto.data(), to_write));
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_SSL, "GetNextAlpnProto called, size={}", to_write);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.Push<u32>(static_cast<u32>(to_write));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class ISslContext final : public ServiceFramework<ISslContext> {
|
class ISslContext final : public ServiceFramework<ISslContext> {
|
||||||
|
@ -398,7 +466,7 @@ public:
|
||||||
shared_data{std::make_shared<SslContextSharedData>()} {
|
shared_data{std::make_shared<SslContextSharedData>()} {
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, &ISslContext::SetOption, "SetOption"},
|
{0, &ISslContext::SetOption, "SetOption"},
|
||||||
{1, nullptr, "GetOption"},
|
{1, &ISslContext::GetOption, "GetOption"},
|
||||||
{2, &ISslContext::CreateConnection, "CreateConnection"},
|
{2, &ISslContext::CreateConnection, "CreateConnection"},
|
||||||
{3, &ISslContext::GetConnectionCount, "GetConnectionCount"},
|
{3, &ISslContext::GetConnectionCount, "GetConnectionCount"},
|
||||||
{4, &ISslContext::ImportServerPki, "ImportServerPki"},
|
{4, &ISslContext::ImportServerPki, "ImportServerPki"},
|
||||||
|
@ -434,6 +502,17 @@ private:
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetOption(HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto parameters = rp.PopRaw<OptionType>();
|
||||||
|
|
||||||
|
LOG_WARNING(Service_SSL, "(STUBBED) called. option={}", parameters);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreateConnection(HLERequestContext& ctx) {
|
void CreateConnection(HLERequestContext& ctx) {
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
@ -1368,8 +1371,7 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src,
|
||||||
std::span<const VideoCommon::ImageCopy> copies) {
|
std::span<const VideoCommon::ImageCopy> copies) {
|
||||||
// As per the size-compatible formats section of vulkan, copy manually via ReinterpretImage
|
// As per the size-compatible formats section of vulkan, copy manually via ReinterpretImage
|
||||||
// these images that aren't size-compatible
|
// these images that aren't size-compatible
|
||||||
if (HasAlpha(src.info.format) != HasAlpha(dst.info.format) ||
|
if (BytesPerBlock(src.info.format) != BytesPerBlock(dst.info.format)) {
|
||||||
BytesPerBlock(src.info.format) != BytesPerBlock(dst.info.format)) {
|
|
||||||
auto oneCopy = VideoCommon::ImageCopy{
|
auto oneCopy = VideoCommon::ImageCopy{
|
||||||
.src_offset = VideoCommon::Offset3D(0, 0, 0),
|
.src_offset = VideoCommon::Offset3D(0, 0, 0),
|
||||||
.dst_offset = VideoCommon::Offset3D(0, 0, 0),
|
.dst_offset = VideoCommon::Offset3D(0, 0, 0),
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -263,6 +266,12 @@ bool HasAlpha(PixelFormat pixel_format) {
|
||||||
case PixelFormat::B8G8R8A8_SRGB:
|
case PixelFormat::B8G8R8A8_SRGB:
|
||||||
case PixelFormat::BC1_RGBA_SRGB:
|
case PixelFormat::BC1_RGBA_SRGB:
|
||||||
case PixelFormat::A4B4G4R4_UNORM:
|
case PixelFormat::A4B4G4R4_UNORM:
|
||||||
|
case PixelFormat::BC2_SRGB:
|
||||||
|
case PixelFormat::BC2_UNORM:
|
||||||
|
case PixelFormat::BC3_SRGB:
|
||||||
|
case PixelFormat::BC3_UNORM:
|
||||||
|
case PixelFormat::BC7_SRGB:
|
||||||
|
case PixelFormat::BC7_UNORM:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/settings.h"
|
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "common/settings.h"
|
||||||
#include "video_core/vulkan_common/vk_enum_string_helper.h"
|
#include "video_core/vulkan_common/vk_enum_string_helper.h"
|
||||||
#include "video_core/vulkan_common/vma.h"
|
#include "video_core/vulkan_common/vma.h"
|
||||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||||
|
@ -440,14 +440,15 @@ Instance Instance::Create(u32 version, Span<const char*> layers, Span<const char
|
||||||
#else
|
#else
|
||||||
constexpr VkFlags ci_flags{};
|
constexpr VkFlags ci_flags{};
|
||||||
#endif
|
#endif
|
||||||
|
// DO NOT TOUCH, breaks RNDA3!!
|
||||||
|
// Don't know why, but gloom + yellow line glitch appears
|
||||||
const VkApplicationInfo application_info{
|
const VkApplicationInfo application_info{
|
||||||
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
.pApplicationName = "Eden Emulator",
|
.pApplicationName = "yuzu Emulator",
|
||||||
.applicationVersion = VK_MAKE_VERSION(0, 1, 0),
|
.applicationVersion = VK_MAKE_VERSION(1, 3, 0),
|
||||||
.pEngineName = "Eden Emulator",
|
.pEngineName = "yuzu Emulator",
|
||||||
.engineVersion = VK_MAKE_VERSION(0, 1, 0),
|
.engineVersion = VK_MAKE_VERSION(1, 3, 0),
|
||||||
.apiVersion = VK_API_VERSION_1_3,
|
.apiVersion = VK_API_VERSION_1_3,
|
||||||
};
|
};
|
||||||
const VkInstanceCreateInfo ci{
|
const VkInstanceCreateInfo ci{
|
||||||
|
|
1
src/yuzu/externals/CMakeLists.txt
vendored
1
src/yuzu/externals/CMakeLists.txt
vendored
|
@ -6,7 +6,6 @@ set(BUILD_SHARED_LIBS OFF)
|
||||||
|
|
||||||
# QuaZip
|
# QuaZip
|
||||||
include(CPM)
|
include(CPM)
|
||||||
set(CPM_SOURCE_CACHE ${CMAKE_SOURCE_DIR}/.cache/cpm)
|
|
||||||
set(CPM_USE_LOCAL_PACKAGES ON)
|
set(CPM_USE_LOCAL_PACKAGES ON)
|
||||||
|
|
||||||
CPMAddPackage(
|
CPMAddPackage(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue