Compare commits

..

2 Commits

Author SHA1 Message Date
lizzie 62c413b10e 2026-09-06 21:26:09
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-06 21:26:09 +00:00
lizzie 37a605ff9e 2026-09-06 20:55:53
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-06 20:55:53 +00:00
18 changed files with 110 additions and 226 deletions
+16 -69
View File
@@ -11,34 +11,15 @@ set(dynarmic_VERSION ${dynarmic_VERSION_MAJOR}.${dynarmic_VERSION_MINOR}.${dynar
project(dynarmic LANGUAGES C CXX ASM VERSION ${dynarmic_VERSION})
# Determine if we're built as a subproject (using add_subdirectory)
# or if this is the master project.
set(MASTER_PROJECT OFF)
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(MASTER_PROJECT ON)
endif()
if (MASTER_PROJECT)
include(CTest)
endif()
# Dynarmic project options
option(DYNARMIC_ENABLE_CPU_FEATURE_DETECTION "Turning this off causes dynarmic to assume the host CPU doesn't support anything later than SSE3" ON)
if (OPENBSD OR DRAGONFLY OR NETBSD)
set(REQUIRE_WX ON)
else()
set(REQUIRE_WX OFF)
endif()
option(DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT "Enables support for systems that require W^X" ${REQUIRE_WX})
option(DYNARMIC_IGNORE_ASSERTS "Ignore asserts" ON)
option(DYNARMIC_TESTS_USE_UNICORN "Enable fuzzing tests against unicorn" OFF)
CMAKE_DEPENDENT_OPTION(DYNARMIC_USE_LLVM "Support disassembly of jitted x86_64 code using LLVM" OFF "NOT YUZU_DISABLE_LLVM" OFF)
option(DYNARMIC_INSTALL "Install dynarmic headers and CMake files" OFF)
option(DYNARMIC_USE_BUNDLED_EXTERNALS "Use all bundled externals (useful when e.g. cross-compiling)" OFF)
# Set hard requirements for C++
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -88,7 +69,7 @@ if (MSVC)
-Wno-missing-braces)
endif()
else()
set(DYNARMIC_CXX_FLAGS
list(APPEND DYNARMIC_CXX_FLAGS
-Wall
-Wextra
-Wcast-qual
@@ -103,37 +84,35 @@ else()
# to Address& after first checking isMEM(), and that code is inlined in a situation where
# GCC knows that the variable is actually a Reg64. isMEM() will never return true for a
# Reg64, but GCC doesn't know that.
list(APPEND DYNARMIC_CXX_FLAGS -Wno-array-bounds)
list(APPEND DYNARMIC_CXX_FLAGS -Wstack-usage=4096)
list(APPEND DYNARMIC_CXX_FLAGS
-Wno-array-bounds
-Wstack-usage=4096)
endif()
if (CXX_CLANG)
# Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6.
# And this in turns limits the size of a std::array.
list(APPEND DYNARMIC_CXX_FLAGS -fbracket-depth=1024)
# Clang mistakenly blames CMake for using unused arguments during compilation
list(APPEND DYNARMIC_CXX_FLAGS -Wno-unused-command-line-argument)
list(APPEND DYNARMIC_CXX_FLAGS
# Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6.
# And this in turns limits the size of a std::array.
-fbracket-depth=1024
# Clang mistakenly blames CMake for using unused arguments during compilation
-Wno-unused-command-line-argument)
endif()
endif()
if (NOT Boost_FOUND)
find_package(Boost 1.57 REQUIRED)
endif()
find_package(fmt 8 CONFIG)
if ("arm64" IN_LIST ARCHITECTURE OR DYNARMIC_TESTS)
if (ARCHITECTURE_arm64)
find_package(oaknut 2.0.1 CONFIG)
endif()
if ("riscv64" IN_LIST ARCHITECTURE)
if (ARCHITECTURE_riscv64)
find_package(biscuit 0.9.1 REQUIRED)
endif()
if ("loongarch64" IN_LIST ARCHITECTURE)
if (ARCHITECTURE_loongarch64)
find_package(lagoon REQUIRED)
endif()
if ("x86_64" IN_LIST ARCHITECTURE)
if (ARCHITECTURE_x86_64)
find_package(xbyak 7 CONFIG)
endif()
@@ -142,44 +121,12 @@ if (DYNARMIC_USE_LLVM)
separate_arguments(LLVM_DEFINITIONS)
endif()
# Dynarmic project files
add_subdirectory(src/dynarmic)
if (DYNARMIC_TESTS)
find_package(Catch2 3 CONFIG)
if (DYNARMIC_TESTS_USE_UNICORN)
find_package(Unicorn REQUIRED)
endif()
endif()
# Dynarmic project files
add_subdirectory(src/dynarmic)
if (DYNARMIC_TESTS)
add_subdirectory(tests)
endif()
#
# Install
#
if (DYNARMIC_INSTALL)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
install(TARGETS dynarmic EXPORT dynarmicTargets)
install(EXPORT dynarmicTargets
NAMESPACE dynarmic::
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/dynarmic"
)
configure_package_config_file(CMakeModules/dynarmicConfig.cmake.in
dynarmicConfig.cmake
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/dynarmic"
)
write_basic_package_version_file(dynarmicConfigVersion.cmake
COMPATIBILITY SameMajorVersion
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/dynarmicConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/dynarmicConfigVersion.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/dynarmic"
)
install(DIRECTORY src/dynarmic TYPE INCLUDE FILES_MATCHING PATTERN "*.h")
endif()
@@ -1,29 +0,0 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
function(target_architecture_specific_sources project arch)
if (NOT MULTIARCH_BUILD)
target_sources("${project}" PRIVATE ${ARGN})
return()
endif()
foreach(input_file IN LISTS ARGN)
if(input_file MATCHES ".cpp$")
if(NOT IS_ABSOLUTE ${input_file})
set(input_file "${CMAKE_CURRENT_SOURCE_DIR}/${input_file}")
endif()
set(output_file "${CMAKE_CURRENT_BINARY_DIR}/arch_gen/${input_file}")
add_custom_command(
OUTPUT "${output_file}"
COMMAND ${CMAKE_COMMAND} "-Darch=${arch}"
"-Dinput_file=${input_file}"
"-Doutput_file=${output_file}"
-P "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/impl/TargetArchitectureSpecificSourcesWrapFile.cmake"
DEPENDS "${input_file}"
VERBATIM
)
target_sources(${project} PRIVATE "${output_file}")
endif()
endforeach()
endfunction()
@@ -1,31 +0,0 @@
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
set(ARCHITECTURE "@ARCHITECTURE@")
if (NOT @BUILD_SHARED_LIBS@)
find_dependency(Boost 1.57)
find_dependency(fmt 9)
find_dependency(mcl 0.1.12 EXACT)
if ("arm64" IN_LIST ARCHITECTURE)
find_dependency(oaknut 2.0.1)
endif()
if ("riscv" IN_LIST ARCHITECTURE)
find_dependency(biscuit 0.9.1)
endif()
if ("x86_64" IN_LIST ARCHITECTURE)
find_dependency(xbyak 7)
endif()
if (@DYNARMIC_USE_LLVM@)
find_dependency(LLVM)
endif()
endif()
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
check_required_components(@PROJECT_NAME@)
@@ -1,6 +0,0 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
string(TOUPPER "${arch}" arch)
file(READ "${input_file}" f_contents)
file(WRITE "${output_file}" "#if defined(ARCHITECTURE_${arch})\n${f_contents}\n#endif\n")
+6 -14
View File
@@ -1,7 +1,5 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
include(TargetArchitectureSpecificSources)
add_library(dynarmic STATIC
mcl/bit.hpp
mcl/function_info.hpp
@@ -135,7 +133,7 @@ add_library(dynarmic STATIC
interface/A64/config.h
)
if ("x86_64" IN_LIST ARCHITECTURE)
if (ARCHITECTURE_x86_64)
# Newer versions of xbyak (>= 7.25.0) have stricter checks that currently
# fail in dynarmic
target_compile_definitions(dynarmic PRIVATE XBYAK_STRICT_CHECK_MEM_REG_SIZE=0)
@@ -143,7 +141,7 @@ if ("x86_64" IN_LIST ARCHITECTURE)
target_compile_definitions(dynarmic PRIVATE XBYAK_OLD_DISP_CHECK=1)
target_link_libraries(dynarmic PRIVATE xbyak::xbyak)
target_architecture_specific_sources(dynarmic "x86_64"
target_sources(dynarmic PRIVATE
backend/x64/abi.cpp
backend/x64/abi.h
backend/x64/block_of_code.cpp
@@ -201,10 +199,10 @@ if ("x86_64" IN_LIST ARCHITECTURE)
)
endif()
if ("arm64" IN_LIST ARCHITECTURE)
if (ARCHITECTURE_arm64)
target_link_libraries(dynarmic PRIVATE merry::oaknut)
target_architecture_specific_sources(dynarmic "arm64"
target_sources(dynarmic PRIVATE
backend/arm64/a32_jitstate.cpp
backend/arm64/a32_jitstate.h
backend/arm64/a64_jitstate.h
@@ -255,7 +253,7 @@ if ("arm64" IN_LIST ARCHITECTURE)
)
endif()
if ("riscv64" IN_LIST ARCHITECTURE)
if (ARCHITECTURE_riscv64)
target_link_libraries(dynarmic PRIVATE biscuit::biscuit)
target_sources(dynarmic PRIVATE
@@ -295,7 +293,7 @@ if ("riscv64" IN_LIST ARCHITECTURE)
message(WARNING "TODO: Incomplete frontend for this host architecture")
endif()
if ("loongarch64" IN_LIST ARCHITECTURE)
if (ARCHITECTURE_loongarch64)
target_link_libraries(dynarmic PRIVATE lagoon::lagoon)
target_sources(dynarmic PRIVATE
@@ -400,15 +398,9 @@ if (DYNARMIC_USE_LLVM)
target_compile_definitions(dynarmic PRIVATE DYNARMIC_USE_LLVM=1 ${LLVM_DEFINITIONS})
llvm_config(dynarmic USE_SHARED armdesc armdisassembler aarch64desc aarch64disassembler x86desc x86disassembler)
endif()
if (DYNARMIC_ENABLE_CPU_FEATURE_DETECTION)
target_compile_definitions(dynarmic PRIVATE DYNARMIC_ENABLE_CPU_FEATURE_DETECTION=1)
endif()
if (DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT)
target_compile_definitions(dynarmic PRIVATE DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT=1)
endif()
if (DYNARMIC_IGNORE_ASSERTS)
target_compile_definitions(dynarmic PRIVATE MCL_IGNORE_ASSERTS=1)
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
target_compile_definitions(dynarmic PRIVATE FMT_USE_WINDOWS_H=0)
endif()
@@ -78,7 +78,6 @@ void ProtectMemory(const void* base, size_t size, bool is_executable) {
static const HostFeature features = []() {
HostFeature f{};
#ifdef DYNARMIC_ENABLE_CPU_FEATURE_DETECTION
using Cpu = Xbyak::util::Cpu;
Xbyak::util::Cpu cpu_info{};
if (cpu_info.has(Cpu::tSSSE3)) f |= HostFeature::SSSE3;
@@ -121,7 +120,6 @@ static const HostFeature features = []() {
}
}
return f;
#endif
}();
HostFeature GetHostFeatures() {
return features;
@@ -235,6 +235,7 @@ const void* EmitReadMemoryMov(BlockOfCode& code, int value_idx, const Xbyak::Reg
code.xadd(qword[addr], Xbyak::Reg64(value_idx));
break;
case 128:
ASSERT(Xbyak::Xmm(value_idx) != xmm0);
code.lock();
code.cmpxchg16b(xword[addr]);
if (code.HasHostFeature(HostFeature::SSE41)) {
@@ -6226,9 +6226,7 @@ void EmitX64::EmitVectorZeroExtend64(EmitContext& ctx, IR::Inst* inst) {
void EmitX64::EmitVectorZeroUpper(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
auto const a = ctx.reg_alloc.UseScratchXmm(code, args[0]);
code.movq(a, a); // TODO: !IsLastUse
ctx.reg_alloc.DefineValue(code, inst, a);
}
@@ -14,12 +14,6 @@
#include "dynarmic/backend/x64/hostloc.h"
#include "dynarmic/common/spin_lock.h"
#ifdef DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT
static const auto default_cg_mode = Xbyak::DontSetProtectRWE;
#else
static const auto default_cg_mode = nullptr; //Allow RWE
#endif
namespace Dynarmic {
void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp, bool waitpkg) {
@@ -78,7 +72,13 @@ namespace {
struct SpinLockImpl {
void Initialize() noexcept;
static void GlobalInitialize() noexcept;
Xbyak::CodeGenerator code = Xbyak::CodeGenerator(4096, default_cg_mode);
Xbyak::CodeGenerator code = Xbyak::CodeGenerator(4096
#ifdef DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT
, Xbyak::DontSetProtectRWE
#else
, nullptr //Allow RWE
#endif
);
void (*lock)(volatile int*) = nullptr;
void (*unlock)(volatile int*) = nullptr;
};
@@ -91,7 +91,7 @@ struct detail {
shifts[arg_index] = bit_position;
}
}
#if !defined(DYNARMIC_IGNORE_ASSERTS) && !defined(__ANDROID__)
#if !defined(__ANDROID__)
// Avoids a MSVC ICE, and avoids Android NDK issue.
DEBUG_ASSERT(std::all_of(masks.begin(), masks.end(), [](auto m) { return m != 0; }));
#endif
+3 -5
View File
@@ -1,7 +1,5 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
include(TargetArchitectureSpecificSources)
add_executable(dynarmic_tests
fp/FPToFixed.cpp
fp/FPValue.cpp
@@ -44,13 +42,13 @@ if (DYNARMIC_TESTS_USE_UNICORN)
)
endif()
if ("riscv" IN_LIST ARCHITECTURE)
if (ARCHITECTURE_riscv64)
target_link_libraries(dynarmic_tests PRIVATE biscuit::biscuit)
endif()
if ("x86_64" IN_LIST ARCHITECTURE)
if (ARCHITECTURE_x86_64)
target_link_libraries(dynarmic_tests PRIVATE xbyak::xbyak)
target_architecture_specific_sources(dynarmic_tests "x86_64"
target_sources(dynarmic_tests PRIVATE
x64_cpu_info.cpp
native/preserve_xmm.cpp
)
@@ -192,7 +192,7 @@ public:
if (host_visible) {
return StagingBufferRef{};
}
return staging_pool.Request(device, size_bytes, MemoryUsage::Upload);
return staging_pool.Request(size_bytes, MemoryUsage::Upload);
}();
u8* staging_data = host_visible ? buffer.Mapped().data() : staging.mapped_span.data();
@@ -366,11 +366,11 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m
}
StagingBufferRef BufferCacheRuntime::UploadStagingBuffer(size_t size) {
return staging_pool.Request(device, size, MemoryUsage::Upload);
return staging_pool.Request(size, MemoryUsage::Upload);
}
StagingBufferRef BufferCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) {
return staging_pool.Request(device, size, MemoryUsage::Download, deferred);
return staging_pool.Request(size, MemoryUsage::Download, deferred);
}
VkFormat BufferCacheRuntime::TexelBufferFormat(VideoCore::Surface::PixelFormat format) const {
@@ -149,7 +149,7 @@ public:
std::span<u8> BindMappedUniformBuffer([[maybe_unused]] size_t stage,
[[maybe_unused]] u32 binding_index,
u32 size) {
const StagingBufferRef ref = staging_pool.Request(device, size, MemoryUsage::Upload);
const StagingBufferRef ref = staging_pool.Request(size, MemoryUsage::Upload);
guest_descriptor_queue.AddBuffer(ref.buffer, ref.device_address,
static_cast<u32>(ref.offset), size);
return ref.mapped_span;
@@ -287,7 +287,7 @@ Uint8Pass::~Uint8Pass() = default;
std::pair<VkBuffer, VkDeviceSize> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buffer,
u32 src_offset) {
const u32 staging_size = static_cast<u32>(num_vertices * sizeof(u16));
const auto staging = staging_buffer_pool.Request(device, staging_size, MemoryUsage::DeviceLocal);
const auto staging = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal);
compute_pass_descriptor_queue.Acquire(scheduler, 2);
compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices);
@@ -345,7 +345,7 @@ std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble(
const u32 num_tri_vertices = (is_strip ? (num_vertices - 2) / 2 : num_vertices / 4) * 6;
const std::size_t staging_size = num_tri_vertices * sizeof(u32);
const auto staging = staging_buffer_pool.Request(device, staging_size, MemoryUsage::DeviceLocal);
const auto staging = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal);
compute_pass_descriptor_queue.Acquire(scheduler, 2);
compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size);
@@ -852,7 +852,7 @@ public:
void PushUnsyncedQueries() override {
CloseCounter();
auto staging_ref = staging_pool.Request(device,
auto staging_ref = staging_pool.Request(
pending_flush_queries.size() * TFBQueryBank::QUERY_SIZE, MemoryUsage::Download, true);
size_t offset_base = staging_ref.offset;
for (auto q : pending_flush_queries) {
@@ -1657,7 +1657,7 @@ void QueryCacheRuntime::SyncValues(std::span<SyncValuesType> values, VkBuffer ba
impl->copies_setup.clear();
impl->copies_setup.resize(impl->little_cache.size());
if constexpr (SyncValuesType::GeneratesBaseBuffer) {
ref = impl->staging_pool.Request(impl->device, total_size, MemoryUsage::Upload);
ref = impl->staging_pool.Request(total_size, MemoryUsage::Upload);
size_t current_offset = ref.offset;
size_t accumulated_size = 0;
for (size_t i = 0; i < values.size(); i++) {
@@ -28,14 +28,26 @@ using namespace Common::Literals;
// Maximum potential alignment of a Vulkan buffer
constexpr VkDeviceSize MAX_ALIGNMENT = 256;
size_t GetStreamBufferSize(const Device& device, size_t max_stream_buffer_size, size_t max_alignment) {
// Stream buffer size in bytes
// *NIX drivers are more sensitive to increased buffers for streaming.
// Windows ones however, can intake bigger buffers and generally do not OOM.
// - GTX 960 on Windows will not OOM with 256mib
// - GT 1030 on ^NIX will OOM with 256mib
#if defined(__FreeBSD__)
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 128_MiB;
#else
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 256_MiB;
#endif
size_t GetStreamBufferSize(const Device& device) {
if (!device.HasDebuggingToolAttached()) {
return max_stream_buffer_size;
return MAX_STREAM_BUFFER_SIZE;
}
VkDeviceSize size{0};
bool has_device_local_host_visible_heap{};
ForEachDeviceLocalHostVisibleHeap(device, [&size, &has_device_local_host_visible_heap](size_t index, VkMemoryHeap& heap) {
ForEachDeviceLocalHostVisibleHeap(device, [&size, &has_device_local_host_visible_heap](
size_t index, VkMemoryHeap& heap) {
has_device_local_host_visible_heap = true;
size = (std::max)(size, heap.size);
});
@@ -43,27 +55,28 @@ size_t GetStreamBufferSize(const Device& device, size_t max_stream_buffer_size,
// If rebar is not supported, cut the max heap size to 40%. This will allow 2 captures to be
// loaded at the same time in RenderDoc. If rebar is supported, this shouldn't be an issue
// as the heap will be much larger.
if (size <= max_stream_buffer_size) {
if (size <= MAX_STREAM_BUFFER_SIZE) {
size = size * 40 / 100;
}
} else {
size = max_stream_buffer_size;
size = MAX_STREAM_BUFFER_SIZE;
}
return (std::min)(Common::AlignUp(size, max_alignment), max_stream_buffer_size);
return (std::min)(Common::AlignUp(size, MAX_ALIGNMENT), MAX_STREAM_BUFFER_SIZE);
}
} // Anonymous namespace
StagingBufferPool::StagingBufferPool(const Device& device, MemoryAllocator& memory_allocator_, Scheduler& scheduler_)
: memory_allocator{memory_allocator_}, scheduler{scheduler_}
, stream_buffer_size{GetStreamBufferSize(device, 256_MiB, MAX_ALIGNMENT)}
{
StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_,
Scheduler& scheduler_)
: device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_},
stream_buffer_size{GetStreamBufferSize(device)}, region_size{stream_buffer_size /
StagingBufferPool::NUM_SYNCS} {
VkBufferCreateInfo stream_ci = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.size = stream_buffer_size,
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
| VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
@@ -74,20 +87,7 @@ StagingBufferPool::StagingBufferPool(const Device& device, MemoryAllocator& memo
if (device.IsBufferDeviceAddressSupported()) {
stream_ci.usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
}
// *BSD drivers are more sensitive to increased buffers for streaming.
// Windows ones however, can intake bigger buffers and generally do not OOM.
// - GTX 960 on Windows will not OOM with 256mib
// - GT 1030 on ^BSD will OOM with 256mib
// This doesn't seem to be, however, universally true
try {
stream_buffer = memory_allocator.CreateBuffer(stream_ci, MemoryUsage::Stream);
} catch (vk::Exception& e) {
LOG_ERROR(Render_Vulkan, "Can't fit {} bytes buffer, halving", stream_ci.size);
stream_buffer_size = GetStreamBufferSize(device, 128_MiB, MAX_ALIGNMENT);
stream_ci.size = stream_buffer_size;
stream_buffer = memory_allocator.CreateBuffer(stream_ci, MemoryUsage::Stream);
}
region_size = stream_buffer_size / StagingBufferPool::NUM_SYNCS;
stream_buffer = memory_allocator.CreateBuffer(stream_ci, MemoryUsage::Stream);
if (device.HasDebuggingToolAttached()) {
stream_buffer.SetObjectNameEXT("Stream Buffer");
}
@@ -100,10 +100,11 @@ StagingBufferPool::StagingBufferPool(const Device& device, MemoryAllocator& memo
StagingBufferPool::~StagingBufferPool() = default;
StagingBufferRef StagingBufferPool::Request(const Device& device, size_t size, MemoryUsage usage, bool deferred) {
return (!deferred && usage == MemoryUsage::Upload && size <= region_size)
? GetStreamBuffer(device, size)
: GetStagingBuffer(device, size, usage, deferred);
StagingBufferRef StagingBufferPool::Request(size_t size, MemoryUsage usage, bool deferred) {
if (!deferred && usage == MemoryUsage::Upload && size <= region_size) {
return GetStreamBuffer(size);
}
return GetStagingBuffer(size, usage, deferred);
}
void StagingBufferPool::FreeDeferred(StagingBufferRef& ref) {
@@ -126,10 +127,11 @@ void StagingBufferPool::TickFrame() {
ReleaseCache(MemoryUsage::Download);
}
StagingBufferRef StagingBufferPool::GetStreamBuffer(const Device& device, size_t size) {
if (AreRegionsActive(Region(free_iterator) + 1, (std::min)(Region(iterator + size) + 1, NUM_SYNCS))) {
StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) {
if (AreRegionsActive(Region(free_iterator) + 1,
(std::min)(Region(iterator + size) + 1, NUM_SYNCS))) {
// Avoid waiting for the previous usages to be free
return GetStagingBuffer(device, size, MemoryUsage::Upload);
return GetStagingBuffer(size, MemoryUsage::Upload);
}
const u64 current_tick = scheduler.CurrentTick();
std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + Region(iterator),
@@ -138,14 +140,15 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(const Device& device, size_t
free_iterator = (std::max)(free_iterator, iterator + size);
if (iterator + size >= stream_buffer_size) {
std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + NUM_SYNCS, current_tick);
std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + NUM_SYNCS,
current_tick);
used_iterator = 0;
iterator = 0;
free_iterator = size;
if (AreRegionsActive(0, Region(size) + 1)) {
// Avoid waiting for the previous usages to be free
return GetStagingBuffer(device, size, MemoryUsage::Upload);
return GetStagingBuffer(size, MemoryUsage::Upload);
}
}
const size_t offset = iterator;
@@ -153,7 +156,7 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(const Device& device, size_t
return StagingBufferRef{
.buffer = *stream_buffer,
.device_address = stream_buffer_address,
.offset = VkDeviceSize(offset),
.offset = static_cast<VkDeviceSize>(offset),
.mapped_span = stream_pointer.subspan(offset, size),
.usage{},
.log2_level{},
@@ -163,18 +166,21 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(const Device& device, size_t
bool StagingBufferPool::AreRegionsActive(size_t region_begin, size_t region_end) const {
const u64 gpu_tick = scheduler.GetMasterSemaphore().KnownGpuTick();
return std::any_of(sync_ticks.begin() + region_begin, sync_ticks.begin() + region_end, [gpu_tick](u64 sync_tick) {
return gpu_tick < sync_tick;
});
return std::any_of(sync_ticks.begin() + region_begin, sync_ticks.begin() + region_end,
[gpu_tick](u64 sync_tick) { return gpu_tick < sync_tick; });
};
StagingBufferRef StagingBufferPool::GetStagingBuffer(const Device& device, size_t size, MemoryUsage usage, bool deferred) {
if (const std::optional<StagingBufferRef> ref = TryGetReservedBuffer(size, usage, deferred))
StagingBufferRef StagingBufferPool::GetStagingBuffer(size_t size, MemoryUsage usage,
bool deferred) {
if (const std::optional<StagingBufferRef> ref = TryGetReservedBuffer(size, usage, deferred)) {
return *ref;
return CreateStagingBuffer(device, size, usage, deferred);
}
return CreateStagingBuffer(size, usage, deferred);
}
std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t size, MemoryUsage usage, bool deferred) {
std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t size,
MemoryUsage usage,
bool deferred) {
StagingBuffers& cache_level = GetCache(usage)[Common::Log2Ceil(size)];
const auto is_free = [this](const StagingBuffer& entry) {
@@ -196,7 +202,7 @@ std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t s
return it->Ref();
}
StagingBufferRef StagingBufferPool::CreateStagingBuffer(const Device& device, size_t size, MemoryUsage usage, bool deferred) {
StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage, bool deferred) {
auto const log2_size = Common::Log2Ceil<u32>(u32(size));
VkBufferCreateInfo buffer_ci = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
@@ -33,10 +33,11 @@ class StagingBufferPool {
public:
static constexpr size_t NUM_SYNCS = 16;
explicit StagingBufferPool(const Device& device, MemoryAllocator& memory_allocator, Scheduler& scheduler);
explicit StagingBufferPool(const Device& device, MemoryAllocator& memory_allocator,
Scheduler& scheduler);
~StagingBufferPool();
StagingBufferRef Request(const Device& device, size_t size, MemoryUsage usage, bool deferred = false);
StagingBufferRef Request(size_t size, MemoryUsage usage, bool deferred = false);
void FreeDeferred(StagingBufferRef& ref);
[[nodiscard]] VkBuffer StreamBuf() const noexcept {
@@ -83,18 +84,27 @@ private:
static constexpr size_t NUM_LEVELS = sizeof(size_t) * CHAR_BIT;
using StagingBuffersCache = std::array<StagingBuffers, NUM_LEVELS>;
StagingBufferRef GetStreamBuffer(const Device& device, size_t size);
StagingBufferRef GetStreamBuffer(size_t size);
bool AreRegionsActive(size_t region_begin, size_t region_end) const;
StagingBufferRef GetStagingBuffer(const Device& device, size_t size, MemoryUsage usage, bool deferred = false);
std::optional<StagingBufferRef> TryGetReservedBuffer(size_t size, MemoryUsage usage, bool deferred);
StagingBufferRef CreateStagingBuffer(const Device& device, size_t size, MemoryUsage usage, bool deferred);
StagingBufferRef GetStagingBuffer(size_t size, MemoryUsage usage, bool deferred = false);
std::optional<StagingBufferRef> TryGetReservedBuffer(size_t size, MemoryUsage usage,
bool deferred);
StagingBufferRef CreateStagingBuffer(size_t size, MemoryUsage usage, bool deferred);
StagingBuffersCache& GetCache(MemoryUsage usage);
void ReleaseCache(MemoryUsage usage);
void ReleaseLevel(StagingBuffersCache& cache, size_t log2);
size_t Region(size_t iter) const noexcept {
return iter / region_size;
}
const Device& device;
MemoryAllocator& memory_allocator;
Scheduler& scheduler;
@@ -975,11 +975,11 @@ void TextureCacheRuntime::Finish() {
}
StagingBufferRef TextureCacheRuntime::UploadStagingBuffer(size_t size, bool deferred) {
return staging_buffer_pool.Request(device, size, MemoryUsage::Upload, deferred);
return staging_buffer_pool.Request(size, MemoryUsage::Upload, deferred);
}
StagingBufferRef TextureCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) {
return staging_buffer_pool.Request(device, size, MemoryUsage::Download, deferred);
return staging_buffer_pool.Request(size, MemoryUsage::Download, deferred);
}
void TextureCacheRuntime::FreeDeferredStagingBuffer(StagingBufferRef& ref) {