summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/common/bit_field.h2
-rw-r--r--src/core/crypto/partition_data_manager.cpp7
-rw-r--r--src/core/hle/kernel/memory/memory_block.h23
-rw-r--r--src/core/hle/kernel/memory/memory_block_manager.cpp36
-rw-r--r--src/core/hle/kernel/memory/memory_block_manager.h3
-rw-r--r--src/core/hle/kernel/memory/page_table.cpp44
-rw-r--r--src/core/hle/kernel/memory/page_table.h2
-rw-r--r--src/core/hle/kernel/shared_memory.cpp8
-rw-r--r--src/core/hle/kernel/shared_memory.h2
-rw-r--r--src/core/hle/kernel/svc.cpp143
-rw-r--r--src/core/hle/kernel/thread.cpp3
-rw-r--r--src/core/hle/service/bcat/backend/boxcat.cpp7
-rw-r--r--src/core/hle/service/es/es.cpp1
-rw-r--r--src/core/hle/service/time/time.cpp50
-rw-r--r--src/core/settings.cpp10
-rw-r--r--src/core/settings.h11
-rw-r--r--src/core/telemetry_session.cpp16
-rw-r--r--src/tests/core/core_timing.cpp18
-rw-r--r--src/video_core/CMakeLists.txt24
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h119
-rw-r--r--src/video_core/buffer_cache/map_interval.h18
-rw-r--r--src/video_core/dma_pusher.cpp3
-rw-r--r--src/video_core/engines/fermi_2d.cpp2
-rw-r--r--src/video_core/engines/maxwell_3d.cpp8
-rw-r--r--src/video_core/engines/maxwell_dma.cpp11
-rw-r--r--src/video_core/engines/shader_bytecode.h3
-rw-r--r--src/video_core/fence_manager.h170
-rw-r--r--src/video_core/gpu.cpp29
-rw-r--r--src/video_core/gpu.h29
-rw-r--r--src/video_core/gpu_asynch.cpp4
-rw-r--r--src/video_core/gpu_asynch.h2
-rw-r--r--src/video_core/gpu_thread.cpp39
-rw-r--r--src/video_core/gpu_thread.h11
-rw-r--r--src/video_core/memory_manager.cpp18
-rw-r--r--src/video_core/query_cache.h46
-rw-r--r--src/video_core/rasterizer_interface.h18
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_fence_manager.cpp72
-rw-r--r--src/video_core/renderer_opengl/gl_fence_manager.h53
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp62
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h10
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp17
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h3
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.h2
-rw-r--r--src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp220
-rw-r--r--src/video_core/renderer_vulkan/nsight_aftermath_tracker.h87
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_device.cpp35
-rw-r--r--src/video_core/renderer_vulkan/vk_device.h14
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.cpp101
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.h74
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp22
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h3
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.cpp15
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp65
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h8
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp10
-rw-r--r--src/video_core/renderer_vulkan/wrapper.cpp57
-rw-r--r--src/video_core/renderer_vulkan/wrapper.h54
-rw-r--r--src/video_core/shader/control_flow.cpp4
-rw-r--r--src/video_core/shader/decode/arithmetic_half.cpp51
-rw-r--r--src/video_core/shader/decode/arithmetic_integer.cpp4
-rw-r--r--src/video_core/texture_cache/surface_base.h18
-rw-r--r--src/video_core/texture_cache/texture_cache.h114
-rw-r--r--src/video_core/textures/decoders.cpp3
-rw-r--r--src/video_core/textures/decoders.h5
-rw-r--r--src/yuzu/applets/profile_select.cpp2
-rw-r--r--src/yuzu/configuration/config.cpp8
-rw-r--r--src/yuzu/configuration/configure_filesystem.cpp2
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp5
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui33
-rw-r--r--src/yuzu_cmd/config.cpp4
-rw-r--r--src/yuzu_cmd/default_ini.h6
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp7
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp3
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h2
-rw-r--r--src/yuzu_tester/config.cpp4
79 files changed, 1964 insertions, 244 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0913be72c..3a57356ab 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -54,8 +54,10 @@ else()
add_compile_options(
-Wall
-Werror=implicit-fallthrough
+ -Werror=missing-declarations
-Werror=reorder
-Wextra
+ -Wmissing-declarations
-Wno-attributes
-Wno-unused-parameter
)
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index fd2bbbd99..26ae6c7fc 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -180,7 +180,7 @@ public:
}
constexpr void Assign(const T& value) {
- storage = (static_cast<StorageType>(storage) & ~mask) | FormatValue(value);
+ storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value));
}
constexpr T Value() const {
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index d64302f2e..7ed71ac3a 100644
--- a/src/core/crypto/partition_data_manager.cpp
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -202,8 +202,8 @@ static std::array<Key128, 0x20> FindEncryptedMasterKeyFromHex(const std::vector<
return out;
}
-FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
- const std::string& name) {
+static FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
+ const std::string& name) {
const auto upper = Common::ToUpper(name);
for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) {
@@ -345,8 +345,7 @@ FileSys::VirtualFile PartitionDataManager::GetPackage2Raw(Package2Type type) con
return package2.at(static_cast<size_t>(type));
}
-bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) {
-
+static bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) {
const std::vector<u8> iv(header.header_ctr.begin(), header.header_ctr.end());
Package2Header temp = header;
AESCipher<Key128> cipher(key, Mode::CTR);
diff --git a/src/core/hle/kernel/memory/memory_block.h b/src/core/hle/kernel/memory/memory_block.h
index e11043b60..9db1f7b39 100644
--- a/src/core/hle/kernel/memory/memory_block.h
+++ b/src/core/hle/kernel/memory/memory_block.h
@@ -17,7 +17,7 @@ namespace Kernel::Memory {
enum class MemoryState : u32 {
None = 0,
- Mask = 0xFFFFFFFF, // TODO(bunnei): This should probable be 0xFF
+ Mask = 0xFF,
All = ~None,
FlagCanReprotect = (1 << 8),
@@ -253,6 +253,23 @@ public:
};
}
+ void ShareToDevice(MemoryPermission /*new_perm*/) {
+ ASSERT((attribute & MemoryAttribute::DeviceShared) == MemoryAttribute::DeviceShared ||
+ device_use_count == 0);
+ attribute |= MemoryAttribute::DeviceShared;
+ const u16 new_use_count{++device_use_count};
+ ASSERT(new_use_count > 0);
+ }
+
+ void UnshareToDevice(MemoryPermission /*new_perm*/) {
+ ASSERT((attribute & MemoryAttribute::DeviceShared) == MemoryAttribute::DeviceShared);
+ const u16 prev_use_count{device_use_count--};
+ ASSERT(prev_use_count > 0);
+ if (prev_use_count == 1) {
+ attribute &= ~MemoryAttribute::DeviceShared;
+ }
+ }
+
private:
constexpr bool HasProperties(MemoryState s, MemoryPermission p, MemoryAttribute a) const {
constexpr MemoryAttribute AttributeIgnoreMask{MemoryAttribute::DontCareMask |
@@ -287,9 +304,9 @@ private:
state = new_state;
perm = new_perm;
- // TODO(bunnei): Is this right?
attribute = static_cast<MemoryAttribute>(
- new_attribute /*| (attribute & (MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared))*/);
+ new_attribute |
+ (attribute & (MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared)));
}
constexpr MemoryBlock Split(VAddr split_addr) {
diff --git a/src/core/hle/kernel/memory/memory_block_manager.cpp b/src/core/hle/kernel/memory/memory_block_manager.cpp
index 1ebc126c0..900395c37 100644
--- a/src/core/hle/kernel/memory/memory_block_manager.cpp
+++ b/src/core/hle/kernel/memory/memory_block_manager.cpp
@@ -143,6 +143,42 @@ void MemoryBlockManager::Update(VAddr addr, std::size_t num_pages, MemoryState s
}
}
+void MemoryBlockManager::UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func,
+ MemoryPermission perm) {
+ const std::size_t prev_count{memory_block_tree.size()};
+ const VAddr end_addr{addr + num_pages * PageSize};
+ iterator node{memory_block_tree.begin()};
+
+ while (node != memory_block_tree.end()) {
+ MemoryBlock* block{&(*node)};
+ iterator next_node{std::next(node)};
+ const VAddr cur_addr{block->GetAddress()};
+ const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr};
+
+ if (addr < cur_end_addr && cur_addr < end_addr) {
+ iterator new_node{node};
+
+ if (addr > cur_addr) {
+ memory_block_tree.insert(node, block->Split(addr));
+ }
+
+ if (end_addr < cur_end_addr) {
+ new_node = memory_block_tree.insert(node, block->Split(end_addr));
+ }
+
+ lock_func(new_node, perm);
+
+ MergeAdjacent(new_node, next_node);
+ }
+
+ if (cur_end_addr - 1 >= end_addr - 1) {
+ break;
+ }
+
+ node = next_node;
+ }
+}
+
void MemoryBlockManager::IterateForRange(VAddr start, VAddr end, IterateFunc&& func) {
const_iterator it{FindIterator(start)};
MemoryInfo info{};
diff --git a/src/core/hle/kernel/memory/memory_block_manager.h b/src/core/hle/kernel/memory/memory_block_manager.h
index 0f2270f0f..9451b5df6 100644
--- a/src/core/hle/kernel/memory/memory_block_manager.h
+++ b/src/core/hle/kernel/memory/memory_block_manager.h
@@ -45,6 +45,9 @@ public:
MemoryPermission perm = MemoryPermission::None,
MemoryAttribute attribute = MemoryAttribute::None);
+ using LockFunc = std::function<void(iterator, MemoryPermission)>;
+ void UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func, MemoryPermission perm);
+
using IterateFunc = std::function<void(const MemoryInfo&)>;
void IterateForRange(VAddr start, VAddr end, IterateFunc&& func);
diff --git a/src/core/hle/kernel/memory/page_table.cpp b/src/core/hle/kernel/memory/page_table.cpp
index 091e52ca4..3281611f8 100644
--- a/src/core/hle/kernel/memory/page_table.cpp
+++ b/src/core/hle/kernel/memory/page_table.cpp
@@ -840,6 +840,50 @@ ResultVal<VAddr> PageTable::AllocateAndMapMemory(std::size_t needed_num_pages, s
return MakeResult<VAddr>(addr);
}
+ResultCode PageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
+ std::lock_guard lock{page_table_lock};
+
+ MemoryPermission perm{};
+ if (const ResultCode result{CheckMemoryState(
+ nullptr, &perm, nullptr, addr, size, MemoryState::FlagCanChangeAttribute,
+ MemoryState::FlagCanChangeAttribute, MemoryPermission::None, MemoryPermission::None,
+ MemoryAttribute::LockedAndIpcLocked, MemoryAttribute::None,
+ MemoryAttribute::DeviceSharedAndUncached)};
+ result.IsError()) {
+ return result;
+ }
+
+ block_manager->UpdateLock(addr, size / PageSize,
+ [](MemoryBlockManager::iterator block, MemoryPermission perm) {
+ block->ShareToDevice(perm);
+ },
+ perm);
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode PageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) {
+ std::lock_guard lock{page_table_lock};
+
+ MemoryPermission perm{};
+ if (const ResultCode result{CheckMemoryState(
+ nullptr, &perm, nullptr, addr, size, MemoryState::FlagCanChangeAttribute,
+ MemoryState::FlagCanChangeAttribute, MemoryPermission::None, MemoryPermission::None,
+ MemoryAttribute::LockedAndIpcLocked, MemoryAttribute::None,
+ MemoryAttribute::DeviceSharedAndUncached)};
+ result.IsError()) {
+ return result;
+ }
+
+ block_manager->UpdateLock(addr, size / PageSize,
+ [](MemoryBlockManager::iterator block, MemoryPermission perm) {
+ block->UnshareToDevice(perm);
+ },
+ perm);
+
+ return RESULT_SUCCESS;
+}
+
ResultCode PageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
block_manager = std::make_unique<MemoryBlockManager>(start, end);
diff --git a/src/core/hle/kernel/memory/page_table.h b/src/core/hle/kernel/memory/page_table.h
index 80384ab0f..a867aa050 100644
--- a/src/core/hle/kernel/memory/page_table.h
+++ b/src/core/hle/kernel/memory/page_table.h
@@ -53,6 +53,8 @@ public:
bool is_map_only, VAddr region_start,
std::size_t region_num_pages, MemoryState state,
MemoryPermission perm, PAddr map_addr = 0);
+ ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size);
+ ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
Common::PageTable& PageTableImpl() {
return page_table_impl;
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index c67696757..0cd467110 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -36,22 +36,22 @@ std::shared_ptr<SharedMemory> SharedMemory::Create(
}
ResultCode SharedMemory::Map(Process& target_process, VAddr address, std::size_t size,
- Memory::MemoryPermission permission) {
+ Memory::MemoryPermission permissions) {
const u64 page_count{(size + Memory::PageSize - 1) / Memory::PageSize};
if (page_list.GetNumPages() != page_count) {
UNIMPLEMENTED_MSG("Page count does not match");
}
- Memory::MemoryPermission expected =
+ const Memory::MemoryPermission expected =
&target_process == owner_process ? owner_permission : user_permission;
- if (permission != expected) {
+ if (permissions != expected) {
UNIMPLEMENTED_MSG("Permission does not match");
}
return target_process.PageTable().MapPages(address, page_list, Memory::MemoryState::Shared,
- permission);
+ permissions);
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index cd16d6412..0ef87235c 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -51,7 +51,7 @@ public:
* @param permissions Memory block map permissions (specified by SVC field)
*/
ResultCode Map(Process& target_process, VAddr address, std::size_t size,
- Memory::MemoryPermission permission);
+ Memory::MemoryPermission permissions);
/**
* Gets a pointer to the shared memory block
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 4134acf65..25b4a23b4 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -55,9 +55,6 @@ constexpr bool IsValidAddressRange(VAddr address, u64 size) {
return address + size > address;
}
-// 8 GiB
-constexpr u64 MAIN_MEMORY_SIZE = 0x200000000;
-
// Helper function that performs the common sanity checks for svcMapMemory
// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
// in the same order.
@@ -1229,6 +1226,142 @@ static ResultCode QueryMemory32(Core::System& system, u32 memory_info_address,
return QueryMemory(system, memory_info_address, page_info_address, query_address);
}
+static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
+ u64 src_address, u64 size) {
+ LOG_DEBUG(Kernel_SVC,
+ "called. process_handle=0x{:08X}, dst_address=0x{:016X}, "
+ "src_address=0x{:016X}, size=0x{:016X}",
+ process_handle, dst_address, src_address, size);
+
+ if (!Common::Is4KBAligned(src_address)) {
+ LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
+ src_address);
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (!Common::Is4KBAligned(dst_address)) {
+ LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
+ dst_address);
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (size == 0 || !Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size);
+ return ERR_INVALID_SIZE;
+ }
+
+ if (!IsValidAddressRange(dst_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination address range overflows the address space (dst_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ dst_address, size);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ if (!IsValidAddressRange(src_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Source address range overflows the address space (src_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ src_address, size);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
+ auto process = handle_table.Get<Process>(process_handle);
+ if (!process) {
+ LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
+ process_handle);
+ return ERR_INVALID_HANDLE;
+ }
+
+ auto& page_table = process->PageTable();
+ if (!page_table.IsInsideAddressSpace(src_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Source address range is not within the address space (src_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ src_address, size);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ if (!page_table.IsInsideASLRRegion(dst_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ dst_address, size);
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ return page_table.MapProcessCodeMemory(dst_address, src_address, size);
+}
+
+static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle,
+ u64 dst_address, u64 src_address, u64 size) {
+ LOG_DEBUG(Kernel_SVC,
+ "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, "
+ "size=0x{:016X}",
+ process_handle, dst_address, src_address, size);
+
+ if (!Common::Is4KBAligned(dst_address)) {
+ LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
+ dst_address);
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (!Common::Is4KBAligned(src_address)) {
+ LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
+ src_address);
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (size == 0 || Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
+ return ERR_INVALID_SIZE;
+ }
+
+ if (!IsValidAddressRange(dst_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination address range overflows the address space (dst_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ dst_address, size);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ if (!IsValidAddressRange(src_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Source address range overflows the address space (src_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ src_address, size);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
+ auto process = handle_table.Get<Process>(process_handle);
+ if (!process) {
+ LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
+ process_handle);
+ return ERR_INVALID_HANDLE;
+ }
+
+ auto& page_table = process->PageTable();
+ if (!page_table.IsInsideAddressSpace(src_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Source address range is not within the address space (src_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ src_address, size);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ if (!page_table.IsInsideASLRRegion(dst_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ dst_address, size);
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ return page_table.UnmapProcessCodeMemory(dst_address, src_address, size);
+}
+
/// Exits the current process
static void ExitProcess(Core::System& system) {
auto* current_process = system.Kernel().CurrentProcess();
@@ -2256,8 +2389,8 @@ static const FunctionDef SVC_Table_64[] = {
{0x74, nullptr, "MapProcessMemory"},
{0x75, nullptr, "UnmapProcessMemory"},
{0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"},
- {0x77, nullptr, "MapProcessCodeMemory"},
- {0x78, nullptr, "UnmapProcessCodeMemory"},
+ {0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"},
+ {0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"},
{0x79, nullptr, "CreateProcess"},
{0x7A, nullptr, "StartProcess"},
{0x7B, nullptr, "TerminateProcess"},
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 4c0451c01..a919750a6 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -150,8 +150,7 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context,
context.pc = entry_point;
context.sp = stack_top;
// TODO(merry): Perform a hardware test to determine the below value.
- // AHP = 0, DN = 1, FTZ = 1, RMode = Round towards zero
- context.fpcr = 0x03C00000;
+ context.fpcr = 0;
}
ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::string name,
diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp
index f589864ee..5febe8fc1 100644
--- a/src/core/hle/service/bcat/backend/boxcat.cpp
+++ b/src/core/hle/service/bcat/backend/boxcat.cpp
@@ -18,6 +18,7 @@
#include "core/hle/service/bcat/backend/boxcat.h"
#include "core/settings.h"
+namespace Service::BCAT {
namespace {
// Prevents conflicts with windows macro called CreateFile
@@ -30,10 +31,6 @@ bool VfsDeleteFileWrap(FileSys::VirtualDir dir, std::string_view name) {
return dir->DeleteFile(name);
}
-} // Anonymous namespace
-
-namespace Service::BCAT {
-
constexpr ResultCode ERROR_GENERAL_BCAT_FAILURE{ErrorModule::BCAT, 1};
constexpr char BOXCAT_HOSTNAME[] = "api.yuzu-emu.org";
@@ -90,8 +87,6 @@ constexpr u32 PORT = 443;
constexpr u32 TIMEOUT_SECONDS = 30;
[[maybe_unused]] constexpr u64 VFS_COPY_BLOCK_SIZE = 1ULL << 24; // 4MB
-namespace {
-
std::string GetBINFilePath(u64 title_id) {
return fmt::format("{}bcat/{:016X}/launchparam.bin",
FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id);
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp
index 86f36915a..f8e9df4b1 100644
--- a/src/core/hle/service/es/es.cpp
+++ b/src/core/hle/service/es/es.cpp
@@ -4,6 +4,7 @@
#include "core/crypto/key_manager.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/service/es/es.h"
#include "core/hle/service/service.h"
namespace Service::ES {
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index e722886de..67f1bbcf3 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -20,8 +20,8 @@ namespace Service::Time {
class ISystemClock final : public ServiceFramework<ISystemClock> {
public:
- ISystemClock(Clock::SystemClockCore& clock_core)
- : ServiceFramework("ISystemClock"), clock_core{clock_core} {
+ explicit ISystemClock(Clock::SystemClockCore& clock_core, Core::System& system)
+ : ServiceFramework("ISystemClock"), clock_core{clock_core}, system{system} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ISystemClock::GetCurrentTime, "GetCurrentTime"},
@@ -46,9 +46,8 @@ private:
}
s64 posix_time{};
- if (const ResultCode result{
- clock_core.GetCurrentTime(Core::System::GetInstance(), posix_time)};
- result != RESULT_SUCCESS) {
+ if (const ResultCode result{clock_core.GetCurrentTime(system, posix_time)};
+ result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
@@ -69,9 +68,8 @@ private:
}
Clock::SystemClockContext system_clock_context{};
- if (const ResultCode result{
- clock_core.GetClockContext(Core::System::GetInstance(), system_clock_context)};
- result != RESULT_SUCCESS) {
+ if (const ResultCode result{clock_core.GetClockContext(system, system_clock_context)};
+ result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
@@ -83,12 +81,13 @@ private:
}
Clock::SystemClockCore& clock_core;
+ Core::System& system;
};
class ISteadyClock final : public ServiceFramework<ISteadyClock> {
public:
- ISteadyClock(Clock::SteadyClockCore& clock_core)
- : ServiceFramework("ISteadyClock"), clock_core{clock_core} {
+ explicit ISteadyClock(Clock::SteadyClockCore& clock_core, Core::System& system)
+ : ServiceFramework("ISteadyClock"), clock_core{clock_core}, system{system} {
static const FunctionInfo functions[] = {
{0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"},
};
@@ -105,14 +104,14 @@ private:
return;
}
- const Clock::SteadyClockTimePoint time_point{
- clock_core.GetCurrentTimePoint(Core::System::GetInstance())};
+ const Clock::SteadyClockTimePoint time_point{clock_core.GetCurrentTimePoint(system)};
IPC::ResponseBuilder rb{ctx, (sizeof(Clock::SteadyClockTimePoint) / 4) + 2};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(time_point);
}
Clock::SteadyClockCore& clock_core;
+ Core::System& system;
};
ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
@@ -134,7 +133,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
}
const auto current_time_point{
- time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(Core::System::GetInstance())};
+ time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)};
if (const ResultCode result{Clock::ClockSnapshot::GetCurrentTime(
clock_snapshot.user_time, current_time_point, clock_snapshot.user_context)};
result != RESULT_SUCCESS) {
@@ -176,21 +175,24 @@ void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ct
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardUserSystemClockCore());
+ rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardUserSystemClockCore(),
+ system);
}
void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardNetworkSystemClockCore());
+ rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardNetworkSystemClockCore(),
+ system);
}
void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISteadyClock>(module->GetTimeManager().GetStandardSteadyClockCore());
+ rb.PushIpcInterface<ISteadyClock>(module->GetTimeManager().GetStandardSteadyClockCore(),
+ system);
}
void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) {
@@ -204,7 +206,8 @@ void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& c
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardLocalSystemClockCore());
+ rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardLocalSystemClockCore(),
+ system);
}
void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient(
@@ -228,8 +231,7 @@ void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERe
IPC::RequestParser rp{ctx};
const auto context{rp.PopRaw<Clock::SystemClockContext>()};
- const auto current_time_point{
- steady_clock_core.GetCurrentTimePoint(Core::System::GetInstance())};
+ const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) {
const auto ticks{Clock::TimeSpanType::FromTicks(
@@ -255,8 +257,8 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
Clock::SystemClockContext user_context{};
if (const ResultCode result{
module->GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(
- Core::System::GetInstance(), user_context)};
- result != RESULT_SUCCESS) {
+ system, user_context)};
+ result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
@@ -264,8 +266,8 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
Clock::SystemClockContext network_context{};
if (const ResultCode result{
module->GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext(
- Core::System::GetInstance(), network_context)};
- result != RESULT_SUCCESS) {
+ system, network_context)};
+ result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
@@ -274,7 +276,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
Clock::ClockSnapshot clock_snapshot{};
if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal(
&ctx.GetThread(), user_context, network_context, type, clock_snapshot)};
- result != RESULT_SUCCESS) {
+ result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index c1282cb80..cd6c257f5 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -92,7 +92,7 @@ void LogSettings() {
LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit);
LogSetting("Renderer_FrameLimit", Settings::values.frame_limit);
LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache);
- LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation);
+ LogSetting("Renderer_GPUAccuracyLevel", Settings::values.gpu_accuracy);
LogSetting("Renderer_UseAsynchronousGpuEmulation",
Settings::values.use_asynchronous_gpu_emulation);
LogSetting("Renderer_UseVsync", Settings::values.use_vsync);
@@ -109,4 +109,12 @@ void LogSettings() {
LogSetting("Services_BCATBoxcatLocal", Settings::values.bcat_boxcat_local);
}
+bool IsGPULevelExtreme() {
+ return values.gpu_accuracy == GPUAccuracy::Extreme;
+}
+
+bool IsGPULevelHigh() {
+ return values.gpu_accuracy == GPUAccuracy::Extreme || values.gpu_accuracy == GPUAccuracy::High;
+}
+
} // namespace Settings
diff --git a/src/core/settings.h b/src/core/settings.h
index c73d1c596..7d09253f5 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -376,6 +376,12 @@ enum class RendererBackend {
Vulkan = 1,
};
+enum class GPUAccuracy : u32 {
+ Normal = 0,
+ High = 1,
+ Extreme = 2,
+};
+
struct Values {
// System
bool use_docked_mode;
@@ -436,7 +442,7 @@ struct Values {
bool use_frame_limit;
u16 frame_limit;
bool use_disk_shader_cache;
- bool use_accurate_gpu_emulation;
+ GPUAccuracy gpu_accuracy;
bool use_asynchronous_gpu_emulation;
bool use_vsync;
bool force_30fps_mode;
@@ -480,6 +486,9 @@ struct Values {
std::map<u64, std::vector<std::string>> disabled_addons;
} extern values;
+bool IsGPULevelExtreme();
+bool IsGPULevelHigh();
+
void Apply();
void LogSettings();
} // namespace Settings
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index fd5a3ee9f..1c3b03a1c 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -56,6 +56,18 @@ static const char* TranslateRenderer(Settings::RendererBackend backend) {
return "Unknown";
}
+static const char* TranslateGPUAccuracyLevel(Settings::GPUAccuracy backend) {
+ switch (backend) {
+ case Settings::GPUAccuracy::Normal:
+ return "Normal";
+ case Settings::GPUAccuracy::High:
+ return "High";
+ case Settings::GPUAccuracy::Extreme:
+ return "Extreme";
+ }
+ return "Unknown";
+}
+
u64 GetTelemetryId() {
u64 telemetry_id{};
const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) +
@@ -184,8 +196,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit);
AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit);
AddField(field_type, "Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache);
- AddField(field_type, "Renderer_UseAccurateGpuEmulation",
- Settings::values.use_accurate_gpu_emulation);
+ AddField(field_type, "Renderer_GPUAccuracyLevel",
+ TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy));
AddField(field_type, "Renderer_UseAsynchronousGpuEmulation",
Settings::values.use_asynchronous_gpu_emulation);
AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync);
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp
index 1e3940801..ff2d11cc8 100644
--- a/src/tests/core/core_timing.cpp
+++ b/src/tests/core/core_timing.cpp
@@ -14,13 +14,14 @@
#include "core/core.h"
#include "core/core_timing.h"
+namespace {
// Numbers are chosen randomly to make sure the correct one is given.
-static constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}};
-static constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals
+constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}};
+constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals
-static std::bitset<CB_IDS.size()> callbacks_ran_flags;
-static u64 expected_callback = 0;
-static s64 lateness = 0;
+std::bitset<CB_IDS.size()> callbacks_ran_flags;
+u64 expected_callback = 0;
+s64 lateness = 0;
template <unsigned int IDX>
void CallbackTemplate(u64 userdata, s64 cycles_late) {
@@ -31,7 +32,7 @@ void CallbackTemplate(u64 userdata, s64 cycles_late) {
REQUIRE(lateness == cycles_late);
}
-static u64 callbacks_done = 0;
+u64 callbacks_done = 0;
void EmptyCallback(u64 userdata, s64 cycles_late) {
++callbacks_done;
@@ -48,8 +49,8 @@ struct ScopeInit final {
Core::Timing::CoreTiming core_timing;
};
-static void AdvanceAndCheck(Core::Timing::CoreTiming& core_timing, u32 idx, u32 context = 0,
- int expected_lateness = 0, int cpu_downcount = 0) {
+void AdvanceAndCheck(Core::Timing::CoreTiming& core_timing, u32 idx, u32 context = 0,
+ int expected_lateness = 0, int cpu_downcount = 0) {
callbacks_ran_flags = 0;
expected_callback = CB_IDS[idx];
lateness = expected_lateness;
@@ -62,6 +63,7 @@ static void AdvanceAndCheck(Core::Timing::CoreTiming& core_timing, u32 idx, u32
REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags);
}
+} // Anonymous namespace
TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
ScopeInit guard;
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 258d58eba..8ede4ba9b 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -23,6 +23,7 @@ add_library(video_core STATIC
engines/shader_bytecode.h
engines/shader_header.h
engines/shader_type.h
+ fence_manager.h
gpu.cpp
gpu.h
gpu_asynch.cpp
@@ -51,6 +52,8 @@ add_library(video_core STATIC
renderer_opengl/gl_buffer_cache.h
renderer_opengl/gl_device.cpp
renderer_opengl/gl_device.h
+ renderer_opengl/gl_fence_manager.cpp
+ renderer_opengl/gl_fence_manager.h
renderer_opengl/gl_framebuffer_cache.cpp
renderer_opengl/gl_framebuffer_cache.h
renderer_opengl/gl_rasterizer.cpp
@@ -160,6 +163,8 @@ if (ENABLE_VULKAN)
renderer_vulkan/fixed_pipeline_state.h
renderer_vulkan/maxwell_to_vk.cpp
renderer_vulkan/maxwell_to_vk.h
+ renderer_vulkan/nsight_aftermath_tracker.cpp
+ renderer_vulkan/nsight_aftermath_tracker.h
renderer_vulkan/renderer_vulkan.h
renderer_vulkan/renderer_vulkan.cpp
renderer_vulkan/vk_blit_screen.cpp
@@ -174,6 +179,8 @@ if (ENABLE_VULKAN)
renderer_vulkan/vk_descriptor_pool.h
renderer_vulkan/vk_device.cpp
renderer_vulkan/vk_device.h
+ renderer_vulkan/vk_fence_manager.cpp
+ renderer_vulkan/vk_fence_manager.h
renderer_vulkan/vk_graphics_pipeline.cpp
renderer_vulkan/vk_graphics_pipeline.h
renderer_vulkan/vk_image.cpp
@@ -213,19 +220,30 @@ if (ENABLE_VULKAN)
renderer_vulkan/wrapper.cpp
renderer_vulkan/wrapper.h
)
-
- target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include)
- target_compile_definitions(video_core PRIVATE HAS_VULKAN)
endif()
create_target_directory_groups(video_core)
target_link_libraries(video_core PUBLIC common core)
target_link_libraries(video_core PRIVATE glad)
+
if (ENABLE_VULKAN)
+ target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include)
+ target_compile_definitions(video_core PRIVATE HAS_VULKAN)
target_link_libraries(video_core PRIVATE sirit)
endif()
+if (ENABLE_NSIGHT_AFTERMATH)
+ if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK})
+ message(ERROR "Environment variable NSIGHT_AFTERMATH_SDK has to be provided")
+ endif()
+ if (NOT WIN32)
+ message(ERROR "Nsight Aftermath doesn't support non-Windows platforms")
+ endif()
+ target_compile_definitions(video_core PRIVATE HAS_NSIGHT_AFTERMATH)
+ target_include_directories(video_core PRIVATE "$ENV{NSIGHT_AFTERMATH_SDK}/include")
+endif()
+
if (MSVC)
target_compile_options(video_core PRIVATE /we4267)
else()
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 83e7a1cde..510f11089 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -5,6 +5,7 @@
#pragma once
#include <array>
+#include <list>
#include <memory>
#include <mutex>
#include <unordered_map>
@@ -18,8 +19,10 @@
#include "common/alignment.h"
#include "common/common_types.h"
+#include "common/logging/log.h"
#include "core/core.h"
#include "core/memory.h"
+#include "core/settings.h"
#include "video_core/buffer_cache/buffer_block.h"
#include "video_core/buffer_cache/map_interval.h"
#include "video_core/memory_manager.h"
@@ -79,6 +82,9 @@ public:
auto map = MapAddress(block, gpu_addr, cpu_addr, size);
if (is_written) {
map->MarkAsModified(true, GetModifiedTicks());
+ if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) {
+ MarkForAsyncFlush(map);
+ }
if (!map->IsWritten()) {
map->MarkAsWritten(true);
MarkRegionAsWritten(map->GetStart(), map->GetEnd() - 1);
@@ -137,11 +143,22 @@ public:
});
for (auto& object : objects) {
if (object->IsModified() && object->IsRegistered()) {
+ mutex.unlock();
FlushMap(object);
+ mutex.lock();
}
}
}
+ bool MustFlushRegion(VAddr addr, std::size_t size) {
+ std::lock_guard lock{mutex};
+
+ const std::vector<MapInterval> objects = GetMapsInRange(addr, size);
+ return std::any_of(objects.cbegin(), objects.cend(), [](const MapInterval& map) {
+ return map->IsModified() && map->IsRegistered();
+ });
+ }
+
/// Mark the specified region as being invalidated
void InvalidateRegion(VAddr addr, u64 size) {
std::lock_guard lock{mutex};
@@ -154,6 +171,77 @@ public:
}
}
+ void OnCPUWrite(VAddr addr, std::size_t size) {
+ std::lock_guard lock{mutex};
+
+ for (const auto& object : GetMapsInRange(addr, size)) {
+ if (object->IsMemoryMarked() && object->IsRegistered()) {
+ UnmarkMemory(object);
+ object->SetSyncPending(true);
+ marked_for_unregister.emplace_back(object);
+ }
+ }
+ }
+
+ void SyncGuestHost() {
+ std::lock_guard lock{mutex};
+
+ for (const auto& object : marked_for_unregister) {
+ if (object->IsRegistered()) {
+ object->SetSyncPending(false);
+ Unregister(object);
+ }
+ }
+ marked_for_unregister.clear();
+ }
+
+ void CommitAsyncFlushes() {
+ if (uncommitted_flushes) {
+ auto commit_list = std::make_shared<std::list<MapInterval>>();
+ for (auto& map : *uncommitted_flushes) {
+ if (map->IsRegistered() && map->IsModified()) {
+ // TODO(Blinkhawk): Implement backend asynchronous flushing
+ // AsyncFlushMap(map)
+ commit_list->push_back(map);
+ }
+ }
+ if (!commit_list->empty()) {
+ committed_flushes.push_back(commit_list);
+ } else {
+ committed_flushes.emplace_back();
+ }
+ } else {
+ committed_flushes.emplace_back();
+ }
+ uncommitted_flushes.reset();
+ }
+
+ bool ShouldWaitAsyncFlushes() const {
+ return !committed_flushes.empty() && committed_flushes.front() != nullptr;
+ }
+
+ bool HasUncommittedFlushes() const {
+ return uncommitted_flushes != nullptr;
+ }
+
+ void PopAsyncFlushes() {
+ if (committed_flushes.empty()) {
+ return;
+ }
+ auto& flush_list = committed_flushes.front();
+ if (!flush_list) {
+ committed_flushes.pop_front();
+ return;
+ }
+ for (MapInterval& map : *flush_list) {
+ if (map->IsRegistered()) {
+ // TODO(Blinkhawk): Replace this for reading the asynchronous flush
+ FlushMap(map);
+ }
+ }
+ committed_flushes.pop_front();
+ }
+
virtual BufferType GetEmptyBuffer(std::size_t size) = 0;
protected:
@@ -196,17 +284,30 @@ protected:
const IntervalType interval{new_map->GetStart(), new_map->GetEnd()};
mapped_addresses.insert({interval, new_map});
rasterizer.UpdatePagesCachedCount(cpu_addr, size, 1);
+ new_map->SetMemoryMarked(true);
if (inherit_written) {
MarkRegionAsWritten(new_map->GetStart(), new_map->GetEnd() - 1);
new_map->MarkAsWritten(true);
}
}
- /// Unregisters an object from the cache
- void Unregister(MapInterval& map) {
+ void UnmarkMemory(const MapInterval& map) {
+ if (!map->IsMemoryMarked()) {
+ return;
+ }
const std::size_t size = map->GetEnd() - map->GetStart();
rasterizer.UpdatePagesCachedCount(map->GetStart(), size, -1);
+ map->SetMemoryMarked(false);
+ }
+
+ /// Unregisters an object from the cache
+ void Unregister(const MapInterval& map) {
+ UnmarkMemory(map);
map->MarkAsRegistered(false);
+ if (map->IsSyncPending()) {
+ marked_for_unregister.remove(map);
+ map->SetSyncPending(false);
+ }
if (map->IsWritten()) {
UnmarkRegionAsWritten(map->GetStart(), map->GetEnd() - 1);
}
@@ -264,6 +365,9 @@ private:
MapInterval new_map = CreateMap(new_start, new_end, new_gpu_addr);
if (modified_inheritance) {
new_map->MarkAsModified(true, GetModifiedTicks());
+ if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) {
+ MarkForAsyncFlush(new_map);
+ }
}
Register(new_map, write_inheritance);
return new_map;
@@ -450,6 +554,13 @@ private:
return false;
}
+ void MarkForAsyncFlush(MapInterval& map) {
+ if (!uncommitted_flushes) {
+ uncommitted_flushes = std::make_shared<std::unordered_set<MapInterval>>();
+ }
+ uncommitted_flushes->insert(map);
+ }
+
VideoCore::RasterizerInterface& rasterizer;
Core::System& system;
@@ -479,6 +590,10 @@ private:
u64 modified_ticks = 0;
std::vector<u8> staging_buffer;
+ std::list<MapInterval> marked_for_unregister;
+
+ std::shared_ptr<std::unordered_set<MapInterval>> uncommitted_flushes{};
+ std::list<std::shared_ptr<std::list<MapInterval>>> committed_flushes;
std::recursive_mutex mutex;
};
diff --git a/src/video_core/buffer_cache/map_interval.h b/src/video_core/buffer_cache/map_interval.h
index b0956029d..29d8b26f3 100644
--- a/src/video_core/buffer_cache/map_interval.h
+++ b/src/video_core/buffer_cache/map_interval.h
@@ -46,6 +46,22 @@ public:
return is_registered;
}
+ void SetMemoryMarked(bool is_memory_marked_) {
+ is_memory_marked = is_memory_marked_;
+ }
+
+ bool IsMemoryMarked() const {
+ return is_memory_marked;
+ }
+
+ void SetSyncPending(bool is_sync_pending_) {
+ is_sync_pending = is_sync_pending_;
+ }
+
+ bool IsSyncPending() const {
+ return is_sync_pending;
+ }
+
VAddr GetStart() const {
return start;
}
@@ -83,6 +99,8 @@ private:
bool is_written{};
bool is_modified{};
bool is_registered{};
+ bool is_memory_marked{};
+ bool is_sync_pending{};
u64 ticks{};
};
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index 0b77afc71..324dafdcd 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -21,6 +21,7 @@ MICROPROFILE_DEFINE(DispatchCalls, "GPU", "Execute command buffer", MP_RGB(128,
void DmaPusher::DispatchCalls() {
MICROPROFILE_SCOPE(DispatchCalls);
+ gpu.SyncGuestHost();
// On entering GPU code, assume all memory may be touched by the ARM core.
gpu.Maxwell3D().OnMemoryWrite();
@@ -32,6 +33,8 @@ void DmaPusher::DispatchCalls() {
}
}
gpu.FlushCommands();
+ gpu.SyncGuestHost();
+ gpu.OnCommandListEnd();
}
bool DmaPusher::Step() {
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index 85d308e26..bace6affb 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -28,7 +28,7 @@ void Fermi2D::CallMethod(const GPU::MethodCall& method_call) {
}
}
-std::pair<u32, u32> DelimitLine(u32 src_1, u32 src_2, u32 dst_1, u32 dst_2, u32 src_line) {
+static std::pair<u32, u32> DelimitLine(u32 src_1, u32 src_2, u32 dst_1, u32 dst_2, u32 src_line) {
const u32 line_a = src_2 - src_1;
const u32 line_b = dst_2 - dst_1;
const u32 excess = std::max<s32>(0, line_a - src_line + src_1);
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index baa74ad4c..2824ed707 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -404,7 +404,11 @@ void Maxwell3D::ProcessQueryGet() {
switch (regs.query.query_get.operation) {
case Regs::QueryOperation::Release:
- StampQueryResult(regs.query.query_sequence, regs.query.query_get.short_query == 0);
+ if (regs.query.query_get.fence == 1) {
+ rasterizer.SignalSemaphore(regs.query.QueryAddress(), regs.query.query_sequence);
+ } else {
+ StampQueryResult(regs.query.query_sequence, regs.query.query_get.short_query == 0);
+ }
break;
case Regs::QueryOperation::Acquire:
// TODO(Blinkhawk): Under this operation, the GPU waits for the CPU to write a value that
@@ -483,7 +487,7 @@ void Maxwell3D::ProcessSyncPoint() {
const u32 increment = regs.sync_info.increment.Value();
[[maybe_unused]] const u32 cache_flush = regs.sync_info.unknown.Value();
if (increment) {
- system.GPU().IncrementSyncPoint(sync_point);
+ rasterizer.SignalSyncPoint(sync_point);
}
}
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index c2610f992..3bfed6ab8 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -104,8 +104,13 @@ void MaxwellDMA::HandleCopy() {
write_buffer.resize(dst_size);
}
- memory_manager.ReadBlock(source, read_buffer.data(), src_size);
- memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
+ if (Settings::IsGPULevelExtreme()) {
+ memory_manager.ReadBlock(source, read_buffer.data(), src_size);
+ memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
+ } else {
+ memory_manager.ReadBlockUnsafe(source, read_buffer.data(), src_size);
+ memory_manager.ReadBlockUnsafe(dest, write_buffer.data(), dst_size);
+ }
Texture::UnswizzleSubrect(
regs.x_count, regs.y_count, regs.dst_pitch, regs.src_params.size_x, bytes_per_pixel,
@@ -136,7 +141,7 @@ void MaxwellDMA::HandleCopy() {
write_buffer.resize(dst_size);
}
- if (Settings::values.use_accurate_gpu_emulation) {
+ if (Settings::IsGPULevelExtreme()) {
memory_manager.ReadBlock(source, read_buffer.data(), src_size);
memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
} else {
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 7231597d4..cde3a26b9 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -655,6 +655,7 @@ union Instruction {
}
constexpr Instruction(u64 value) : value{value} {}
+ constexpr Instruction(const Instruction& instr) : value(instr.value) {}
BitField<0, 8, Register> gpr0;
BitField<8, 8, Register> gpr8;
@@ -817,11 +818,9 @@ union Instruction {
BitField<32, 1, u64> saturate;
BitField<49, 2, HalfMerge> merge;
- BitField<43, 1, u64> negate_a;
BitField<44, 1, u64> abs_a;
BitField<47, 2, HalfType> type_a;
- BitField<31, 1, u64> negate_b;
BitField<30, 1, u64> abs_b;
BitField<28, 2, HalfType> type_b;
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h
new file mode 100644
index 000000000..dabd1588c
--- /dev/null
+++ b/src/video_core/fence_manager.h
@@ -0,0 +1,170 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <algorithm>
+#include <array>
+#include <memory>
+#include <queue>
+
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "core/core.h"
+#include "core/memory.h"
+#include "core/settings.h"
+#include "video_core/gpu.h"
+#include "video_core/memory_manager.h"
+#include "video_core/rasterizer_interface.h"
+
+namespace VideoCommon {
+
+class FenceBase {
+public:
+ FenceBase(u32 payload, bool is_stubbed)
+ : address{}, payload{payload}, is_semaphore{false}, is_stubbed{is_stubbed} {}
+
+ FenceBase(GPUVAddr address, u32 payload, bool is_stubbed)
+ : address{address}, payload{payload}, is_semaphore{true}, is_stubbed{is_stubbed} {}
+
+ GPUVAddr GetAddress() const {
+ return address;
+ }
+
+ u32 GetPayload() const {
+ return payload;
+ }
+
+ bool IsSemaphore() const {
+ return is_semaphore;
+ }
+
+private:
+ GPUVAddr address;
+ u32 payload;
+ bool is_semaphore;
+
+protected:
+ bool is_stubbed;
+};
+
+template <typename TFence, typename TTextureCache, typename TTBufferCache, typename TQueryCache>
+class FenceManager {
+public:
+ void SignalSemaphore(GPUVAddr addr, u32 value) {
+ TryReleasePendingFences();
+ const bool should_flush = ShouldFlush();
+ CommitAsyncFlushes();
+ TFence new_fence = CreateFence(addr, value, !should_flush);
+ fences.push(new_fence);
+ QueueFence(new_fence);
+ if (should_flush) {
+ rasterizer.FlushCommands();
+ }
+ rasterizer.SyncGuestHost();
+ }
+
+ void SignalSyncPoint(u32 value) {
+ TryReleasePendingFences();
+ const bool should_flush = ShouldFlush();
+ CommitAsyncFlushes();
+ TFence new_fence = CreateFence(value, !should_flush);
+ fences.push(new_fence);
+ QueueFence(new_fence);
+ if (should_flush) {
+ rasterizer.FlushCommands();
+ }
+ rasterizer.SyncGuestHost();
+ }
+
+ void WaitPendingFences() {
+ auto& gpu{system.GPU()};
+ auto& memory_manager{gpu.MemoryManager()};
+ while (!fences.empty()) {
+ TFence& current_fence = fences.front();
+ if (ShouldWait()) {
+ WaitFence(current_fence);
+ }
+ PopAsyncFlushes();
+ if (current_fence->IsSemaphore()) {
+ memory_manager.Write<u32>(current_fence->GetAddress(), current_fence->GetPayload());
+ } else {
+ gpu.IncrementSyncPoint(current_fence->GetPayload());
+ }
+ fences.pop();
+ }
+ }
+
+protected:
+ FenceManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
+ TTextureCache& texture_cache, TTBufferCache& buffer_cache,
+ TQueryCache& query_cache)
+ : system{system}, rasterizer{rasterizer}, texture_cache{texture_cache},
+ buffer_cache{buffer_cache}, query_cache{query_cache} {}
+
+ virtual ~FenceManager() {}
+
+ /// Creates a Sync Point Fence Interface, does not create a backend fence if 'is_stubbed' is
+ /// true
+ virtual TFence CreateFence(u32 value, bool is_stubbed) = 0;
+ /// Creates a Semaphore Fence Interface, does not create a backend fence if 'is_stubbed' is true
+ virtual TFence CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) = 0;
+ /// Queues a fence into the backend if the fence isn't stubbed.
+ virtual void QueueFence(TFence& fence) = 0;
+ /// Notifies that the backend fence has been signaled/reached in host GPU.
+ virtual bool IsFenceSignaled(TFence& fence) const = 0;
+ /// Waits until a fence has been signalled by the host GPU.
+ virtual void WaitFence(TFence& fence) = 0;
+
+ Core::System& system;
+ VideoCore::RasterizerInterface& rasterizer;
+ TTextureCache& texture_cache;
+ TTBufferCache& buffer_cache;
+ TQueryCache& query_cache;
+
+private:
+ void TryReleasePendingFences() {
+ auto& gpu{system.GPU()};
+ auto& memory_manager{gpu.MemoryManager()};
+ while (!fences.empty()) {
+ TFence& current_fence = fences.front();
+ if (ShouldWait() && !IsFenceSignaled(current_fence)) {
+ return;
+ }
+ PopAsyncFlushes();
+ if (current_fence->IsSemaphore()) {
+ memory_manager.Write<u32>(current_fence->GetAddress(), current_fence->GetPayload());
+ } else {
+ gpu.IncrementSyncPoint(current_fence->GetPayload());
+ }
+ fences.pop();
+ }
+ }
+
+ bool ShouldWait() const {
+ return texture_cache.ShouldWaitAsyncFlushes() || buffer_cache.ShouldWaitAsyncFlushes() ||
+ query_cache.ShouldWaitAsyncFlushes();
+ }
+
+ bool ShouldFlush() const {
+ return texture_cache.HasUncommittedFlushes() || buffer_cache.HasUncommittedFlushes() ||
+ query_cache.HasUncommittedFlushes();
+ }
+
+ void PopAsyncFlushes() {
+ texture_cache.PopAsyncFlushes();
+ buffer_cache.PopAsyncFlushes();
+ query_cache.PopAsyncFlushes();
+ }
+
+ void CommitAsyncFlushes() {
+ texture_cache.CommitAsyncFlushes();
+ buffer_cache.CommitAsyncFlushes();
+ query_cache.CommitAsyncFlushes();
+ }
+
+ std::queue<TFence> fences;
+};
+
+} // namespace VideoCommon
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index a606f4abd..3b7572d61 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -125,6 +125,28 @@ bool GPU::CancelSyncptInterrupt(const u32 syncpoint_id, const u32 value) {
return true;
}
+u64 GPU::RequestFlush(VAddr addr, std::size_t size) {
+ std::unique_lock lck{flush_request_mutex};
+ const u64 fence = ++last_flush_fence;
+ flush_requests.emplace_back(fence, addr, size);
+ return fence;
+}
+
+void GPU::TickWork() {
+ std::unique_lock lck{flush_request_mutex};
+ while (!flush_requests.empty()) {
+ auto& request = flush_requests.front();
+ const u64 fence = request.fence;
+ const VAddr addr = request.addr;
+ const std::size_t size = request.size;
+ flush_requests.pop_front();
+ flush_request_mutex.unlock();
+ renderer->Rasterizer().FlushRegion(addr, size);
+ current_flush_fence.store(fence);
+ flush_request_mutex.lock();
+ }
+}
+
u64 GPU::GetTicks() const {
// This values were reversed engineered by fincs from NVN
// The gpu clock is reported in units of 385/625 nanoseconds
@@ -142,6 +164,13 @@ void GPU::FlushCommands() {
renderer->Rasterizer().FlushCommands();
}
+void GPU::SyncGuestHost() {
+ renderer->Rasterizer().SyncGuestHost();
+}
+
+void GPU::OnCommandListEnd() {
+ renderer->Rasterizer().ReleaseFences();
+}
// Note that, traditionally, methods are treated as 4-byte addressable locations, and hence
// their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4.
// So the values you see in docs might be multiplied by 4.
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 1a2d747be..5e3eb94e9 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -155,7 +155,23 @@ public:
/// Calls a GPU method.
void CallMethod(const MethodCall& method_call);
+ /// Flush all current written commands into the host GPU for execution.
void FlushCommands();
+ /// Synchronizes CPU writes with Host GPU memory.
+ void SyncGuestHost();
+ /// Signal the ending of command list.
+ virtual void OnCommandListEnd();
+
+ /// Request a host GPU memory flush from the CPU.
+ u64 RequestFlush(VAddr addr, std::size_t size);
+
+ /// Obtains current flush request fence id.
+ u64 CurrentFlushRequestFence() const {
+ return current_flush_fence.load(std::memory_order_relaxed);
+ }
+
+ /// Tick pending requests within the GPU.
+ void TickWork();
/// Returns a reference to the Maxwell3D GPU engine.
Engines::Maxwell3D& Maxwell3D();
@@ -325,6 +341,19 @@ private:
std::condition_variable sync_cv;
+ struct FlushRequest {
+ FlushRequest(u64 fence, VAddr addr, std::size_t size)
+ : fence{fence}, addr{addr}, size{size} {}
+ u64 fence;
+ VAddr addr;
+ std::size_t size;
+ };
+
+ std::list<FlushRequest> flush_requests;
+ std::atomic<u64> current_flush_fence{};
+ u64 last_flush_fence{};
+ std::mutex flush_request_mutex;
+
const bool is_async;
};
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
index 20e73a37e..53305ab43 100644
--- a/src/video_core/gpu_asynch.cpp
+++ b/src/video_core/gpu_asynch.cpp
@@ -52,4 +52,8 @@ void GPUAsynch::WaitIdle() const {
gpu_thread.WaitIdle();
}
+void GPUAsynch::OnCommandListEnd() {
+ gpu_thread.OnCommandListEnd();
+}
+
} // namespace VideoCommon
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h
index 03fd0eef0..517658612 100644
--- a/src/video_core/gpu_asynch.h
+++ b/src/video_core/gpu_asynch.h
@@ -32,6 +32,8 @@ public:
void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
void WaitIdle() const override;
+ void OnCommandListEnd() override;
+
protected:
void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const override;
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 10cda686b..c3bb4fe06 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -6,6 +6,7 @@
#include "common/microprofile.h"
#include "core/core.h"
#include "core/frontend/emu_window.h"
+#include "core/settings.h"
#include "video_core/dma_pusher.h"
#include "video_core/gpu.h"
#include "video_core/gpu_thread.h"
@@ -14,8 +15,9 @@
namespace VideoCommon::GPUThread {
/// Runs the GPU thread
-static void RunThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context,
- Tegra::DmaPusher& dma_pusher, SynchState& state) {
+static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
+ Core::Frontend::GraphicsContext& context, Tegra::DmaPusher& dma_pusher,
+ SynchState& state) {
MicroProfileOnThreadCreate("GpuThread");
// Wait for first GPU command before acquiring the window context
@@ -37,10 +39,14 @@ static void RunThread(VideoCore::RendererBase& renderer, Core::Frontend::Graphic
dma_pusher.DispatchCalls();
} else if (const auto data = std::get_if<SwapBuffersCommand>(&next.data)) {
renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr);
+ } else if (const auto data = std::get_if<OnCommandListEndCommand>(&next.data)) {
+ renderer.Rasterizer().ReleaseFences();
+ } else if (const auto data = std::get_if<GPUTickCommand>(&next.data)) {
+ system.GPU().TickWork();
} else if (const auto data = std::get_if<FlushRegionCommand>(&next.data)) {
renderer.Rasterizer().FlushRegion(data->addr, data->size);
} else if (const auto data = std::get_if<InvalidateRegionCommand>(&next.data)) {
- renderer.Rasterizer().InvalidateRegion(data->addr, data->size);
+ renderer.Rasterizer().OnCPUWrite(data->addr, data->size);
} else if (std::holds_alternative<EndProcessingCommand>(next.data)) {
return;
} else {
@@ -65,8 +71,8 @@ ThreadManager::~ThreadManager() {
void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
Core::Frontend::GraphicsContext& context,
Tegra::DmaPusher& dma_pusher) {
- thread = std::thread{RunThread, std::ref(renderer), std::ref(context), std::ref(dma_pusher),
- std::ref(state)};
+ thread = std::thread{RunThread, std::ref(system), std::ref(renderer),
+ std::ref(context), std::ref(dma_pusher), std::ref(state)};
}
void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
@@ -78,16 +84,29 @@ void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
}
void ThreadManager::FlushRegion(VAddr addr, u64 size) {
- PushCommand(FlushRegionCommand(addr, size));
+ if (!Settings::IsGPULevelHigh()) {
+ PushCommand(FlushRegionCommand(addr, size));
+ return;
+ }
+ if (!Settings::IsGPULevelExtreme()) {
+ return;
+ }
+ if (system.Renderer().Rasterizer().MustFlushRegion(addr, size)) {
+ auto& gpu = system.GPU();
+ u64 fence = gpu.RequestFlush(addr, size);
+ PushCommand(GPUTickCommand());
+ while (fence > gpu.CurrentFlushRequestFence()) {
+ }
+ }
}
void ThreadManager::InvalidateRegion(VAddr addr, u64 size) {
- system.Renderer().Rasterizer().InvalidateRegion(addr, size);
+ system.Renderer().Rasterizer().OnCPUWrite(addr, size);
}
void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) {
// Skip flush on asynch mode, as FlushAndInvalidateRegion is not used for anything too important
- InvalidateRegion(addr, size);
+ system.Renderer().Rasterizer().OnCPUWrite(addr, size);
}
void ThreadManager::WaitIdle() const {
@@ -95,6 +114,10 @@ void ThreadManager::WaitIdle() const {
}
}
+void ThreadManager::OnCommandListEnd() {
+ PushCommand(OnCommandListEndCommand());
+}
+
u64 ThreadManager::PushCommand(CommandData&& command_data) {
const u64 fence{++state.last_fence};
state.queue.Push(CommandDataContainer(std::move(command_data), fence));
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index cd74ad330..5a28335d6 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -70,9 +70,16 @@ struct FlushAndInvalidateRegionCommand final {
u64 size;
};
+/// Command called within the gpu, to schedule actions after a command list end
+struct OnCommandListEndCommand final {};
+
+/// Command to make the gpu look into pending requests
+struct GPUTickCommand final {};
+
using CommandData =
std::variant<EndProcessingCommand, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand,
- InvalidateRegionCommand, FlushAndInvalidateRegionCommand>;
+ InvalidateRegionCommand, FlushAndInvalidateRegionCommand, OnCommandListEndCommand,
+ GPUTickCommand>;
struct CommandDataContainer {
CommandDataContainer() = default;
@@ -122,6 +129,8 @@ public:
// Wait until the gpu thread is idle.
void WaitIdle() const;
+ void OnCommandListEnd();
+
private:
/// Pushes a command to be executed by the GPU thread
u64 PushCommand(CommandData&& command_data);
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index fd49bc2a9..dbee9f634 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -51,11 +51,8 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) {
const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)};
MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr);
- ASSERT(system.CurrentProcess()
- ->PageTable()
- .SetMemoryAttribute(cpu_addr, size, Kernel::Memory::MemoryAttribute::DeviceShared,
- Kernel::Memory::MemoryAttribute::DeviceShared)
- .IsSuccess());
+ ASSERT(
+ system.CurrentProcess()->PageTable().LockForDeviceAddressSpace(cpu_addr, size).IsSuccess());
return gpu_addr;
}
@@ -66,11 +63,8 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size)
const u64 aligned_size{Common::AlignUp(size, page_size)};
MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr);
- ASSERT(system.CurrentProcess()
- ->PageTable()
- .SetMemoryAttribute(cpu_addr, size, Kernel::Memory::MemoryAttribute::DeviceShared,
- Kernel::Memory::MemoryAttribute::DeviceShared)
- .IsSuccess());
+ ASSERT(
+ system.CurrentProcess()->PageTable().LockForDeviceAddressSpace(cpu_addr, size).IsSuccess());
return gpu_addr;
}
@@ -87,9 +81,7 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
UnmapRange(gpu_addr, aligned_size);
ASSERT(system.CurrentProcess()
->PageTable()
- .SetMemoryAttribute(cpu_addr.value(), size,
- Kernel::Memory::MemoryAttribute::DeviceShared,
- Kernel::Memory::MemoryAttribute::None)
+ .UnlockForDeviceAddressSpace(cpu_addr.value(), size)
.IsSuccess());
return gpu_addr;
diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h
index 5ea2b01f2..2f75f8801 100644
--- a/src/video_core/query_cache.h
+++ b/src/video_core/query_cache.h
@@ -12,10 +12,12 @@
#include <mutex>
#include <optional>
#include <unordered_map>
+#include <unordered_set>
#include <vector>
#include "common/assert.h"
#include "core/core.h"
+#include "core/settings.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
@@ -130,6 +132,9 @@ public:
}
query->BindCounter(Stream(type).Current(), timestamp);
+ if (Settings::values.use_asynchronous_gpu_emulation) {
+ AsyncFlushQuery(cpu_addr);
+ }
}
/// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch.
@@ -170,6 +175,37 @@ public:
return streams[static_cast<std::size_t>(type)];
}
+ void CommitAsyncFlushes() {
+ committed_flushes.push_back(uncommitted_flushes);
+ uncommitted_flushes.reset();
+ }
+
+ bool HasUncommittedFlushes() const {
+ return uncommitted_flushes != nullptr;
+ }
+
+ bool ShouldWaitAsyncFlushes() const {
+ if (committed_flushes.empty()) {
+ return false;
+ }
+ return committed_flushes.front() != nullptr;
+ }
+
+ void PopAsyncFlushes() {
+ if (committed_flushes.empty()) {
+ return;
+ }
+ auto& flush_list = committed_flushes.front();
+ if (!flush_list) {
+ committed_flushes.pop_front();
+ return;
+ }
+ for (VAddr query_address : *flush_list) {
+ FlushAndRemoveRegion(query_address, 4);
+ }
+ committed_flushes.pop_front();
+ }
+
protected:
std::array<QueryPool, VideoCore::NumQueryTypes> query_pools;
@@ -224,6 +260,13 @@ private:
return found != std::end(contents) ? &*found : nullptr;
}
+ void AsyncFlushQuery(VAddr addr) {
+ if (!uncommitted_flushes) {
+ uncommitted_flushes = std::make_shared<std::unordered_set<VAddr>>();
+ }
+ uncommitted_flushes->insert(addr);
+ }
+
static constexpr std::uintptr_t PAGE_SIZE = 4096;
static constexpr unsigned PAGE_SHIFT = 12;
@@ -235,6 +278,9 @@ private:
std::unordered_map<u64, std::vector<CachedQuery>> cached_queries;
std::array<CounterStream, VideoCore::NumQueryTypes> streams;
+
+ std::shared_ptr<std::unordered_set<VAddr>> uncommitted_flushes{};
+ std::list<std::shared_ptr<std::unordered_set<VAddr>>> committed_flushes;
};
template <class QueryCache, class HostCounter>
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index 8ae5b9c4e..603f61952 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -49,15 +49,33 @@ public:
/// Records a GPU query and caches it
virtual void Query(GPUVAddr gpu_addr, QueryType type, std::optional<u64> timestamp) = 0;
+ /// Signal a GPU based semaphore as a fence
+ virtual void SignalSemaphore(GPUVAddr addr, u32 value) = 0;
+
+ /// Signal a GPU based syncpoint as a fence
+ virtual void SignalSyncPoint(u32 value) = 0;
+
+ /// Release all pending fences.
+ virtual void ReleaseFences() = 0;
+
/// Notify rasterizer that all caches should be flushed to Switch memory
virtual void FlushAll() = 0;
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
virtual void FlushRegion(VAddr addr, u64 size) = 0;
+ /// Check if the the specified memory area requires flushing to CPU Memory.
+ virtual bool MustFlushRegion(VAddr addr, u64 size) = 0;
+
/// Notify rasterizer that any caches of the specified region should be invalidated
virtual void InvalidateRegion(VAddr addr, u64 size) = 0;
+ /// Notify rasterizer that any caches of the specified region are desync with guest
+ virtual void OnCPUWrite(VAddr addr, u64 size) = 0;
+
+ /// Sync memory between guest and host.
+ virtual void SyncGuestHost() = 0;
+
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
/// and invalidated
virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0;
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index cb5792407..4efce0de7 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -52,7 +52,7 @@ Buffer OGLBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) {
}
void OGLBufferCache::WriteBarrier() {
- glMemoryBarrier(GL_ALL_BARRIER_BITS);
+ glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
}
GLuint OGLBufferCache::ToHandle(const Buffer& buffer) {
@@ -72,6 +72,7 @@ void OGLBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, s
void OGLBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size,
u8* data) {
MICROPROFILE_SCOPE(OpenGL_Buffer_Download);
+ glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
glGetNamedBufferSubData(buffer->GetHandle(), static_cast<GLintptr>(offset),
static_cast<GLsizeiptr>(size), data);
}
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.cpp b/src/video_core/renderer_opengl/gl_fence_manager.cpp
new file mode 100644
index 000000000..99ddcb3f8
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_fence_manager.cpp
@@ -0,0 +1,72 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+
+#include "video_core/renderer_opengl/gl_fence_manager.h"
+
+namespace OpenGL {
+
+GLInnerFence::GLInnerFence(u32 payload, bool is_stubbed)
+ : VideoCommon::FenceBase(payload, is_stubbed), sync_object{} {}
+
+GLInnerFence::GLInnerFence(GPUVAddr address, u32 payload, bool is_stubbed)
+ : VideoCommon::FenceBase(address, payload, is_stubbed), sync_object{} {}
+
+GLInnerFence::~GLInnerFence() = default;
+
+void GLInnerFence::Queue() {
+ if (is_stubbed) {
+ return;
+ }
+ ASSERT(sync_object.handle == 0);
+ sync_object.Create();
+}
+
+bool GLInnerFence::IsSignaled() const {
+ if (is_stubbed) {
+ return true;
+ }
+ ASSERT(sync_object.handle != 0);
+ GLsizei length;
+ GLint sync_status;
+ glGetSynciv(sync_object.handle, GL_SYNC_STATUS, sizeof(GLint), &length, &sync_status);
+ return sync_status == GL_SIGNALED;
+}
+
+void GLInnerFence::Wait() {
+ if (is_stubbed) {
+ return;
+ }
+ ASSERT(sync_object.handle != 0);
+ glClientWaitSync(sync_object.handle, 0, GL_TIMEOUT_IGNORED);
+}
+
+FenceManagerOpenGL::FenceManagerOpenGL(Core::System& system,
+ VideoCore::RasterizerInterface& rasterizer,
+ TextureCacheOpenGL& texture_cache,
+ OGLBufferCache& buffer_cache, QueryCache& query_cache)
+ : GenericFenceManager(system, rasterizer, texture_cache, buffer_cache, query_cache) {}
+
+Fence FenceManagerOpenGL::CreateFence(u32 value, bool is_stubbed) {
+ return std::make_shared<GLInnerFence>(value, is_stubbed);
+}
+
+Fence FenceManagerOpenGL::CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) {
+ return std::make_shared<GLInnerFence>(addr, value, is_stubbed);
+}
+
+void FenceManagerOpenGL::QueueFence(Fence& fence) {
+ fence->Queue();
+}
+
+bool FenceManagerOpenGL::IsFenceSignaled(Fence& fence) const {
+ return fence->IsSignaled();
+}
+
+void FenceManagerOpenGL::WaitFence(Fence& fence) {
+ fence->Wait();
+}
+
+} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.h b/src/video_core/renderer_opengl/gl_fence_manager.h
new file mode 100644
index 000000000..c917b3343
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_fence_manager.h
@@ -0,0 +1,53 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <glad/glad.h>
+
+#include "common/common_types.h"
+#include "video_core/fence_manager.h"
+#include "video_core/renderer_opengl/gl_buffer_cache.h"
+#include "video_core/renderer_opengl/gl_query_cache.h"
+#include "video_core/renderer_opengl/gl_resource_manager.h"
+#include "video_core/renderer_opengl/gl_texture_cache.h"
+
+namespace OpenGL {
+
+class GLInnerFence : public VideoCommon::FenceBase {
+public:
+ GLInnerFence(u32 payload, bool is_stubbed);
+ GLInnerFence(GPUVAddr address, u32 payload, bool is_stubbed);
+ ~GLInnerFence();
+
+ void Queue();
+
+ bool IsSignaled() const;
+
+ void Wait();
+
+private:
+ OGLSync sync_object;
+};
+
+using Fence = std::shared_ptr<GLInnerFence>;
+using GenericFenceManager =
+ VideoCommon::FenceManager<Fence, TextureCacheOpenGL, OGLBufferCache, QueryCache>;
+
+class FenceManagerOpenGL final : public GenericFenceManager {
+public:
+ FenceManagerOpenGL(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
+ TextureCacheOpenGL& texture_cache, OGLBufferCache& buffer_cache,
+ QueryCache& query_cache);
+
+protected:
+ Fence CreateFence(u32 value, bool is_stubbed) override;
+ Fence CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) override;
+ void QueueFence(Fence& fence) override;
+ bool IsFenceSignaled(Fence& fence) const override;
+ void WaitFence(Fence& fence) override;
+};
+
+} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 175374f0d..4c16c89d2 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -99,9 +99,10 @@ RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWind
ScreenInfo& info, GLShader::ProgramManager& program_manager,
StateTracker& state_tracker)
: RasterizerAccelerated{system.Memory()}, texture_cache{system, *this, device, state_tracker},
- shader_cache{*this, system, emu_window, device}, query_cache{system, *this}, system{system},
- screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker},
- buffer_cache{*this, system, device, STREAM_BUFFER_SIZE} {
+ shader_cache{*this, system, emu_window, device}, query_cache{system, *this},
+ buffer_cache{*this, system, device, STREAM_BUFFER_SIZE},
+ fence_manager{system, *this, texture_cache, buffer_cache, query_cache}, system{system},
+ screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker} {
CheckExtensions();
}
@@ -599,6 +600,8 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
EndTransformFeedback();
++num_queued_commands;
+
+ system.GPU().TickWork();
}
void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
@@ -649,6 +652,13 @@ void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {
query_cache.FlushRegion(addr, size);
}
+bool RasterizerOpenGL::MustFlushRegion(VAddr addr, u64 size) {
+ if (!Settings::IsGPULevelHigh()) {
+ return buffer_cache.MustFlushRegion(addr, size);
+ }
+ return texture_cache.MustFlushRegion(addr, size) || buffer_cache.MustFlushRegion(addr, size);
+}
+
void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
if (addr == 0 || size == 0) {
@@ -660,8 +670,52 @@ void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
query_cache.InvalidateRegion(addr, size);
}
+void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) {
+ MICROPROFILE_SCOPE(OpenGL_CacheManagement);
+ if (addr == 0 || size == 0) {
+ return;
+ }
+ texture_cache.OnCPUWrite(addr, size);
+ shader_cache.InvalidateRegion(addr, size);
+ buffer_cache.OnCPUWrite(addr, size);
+ query_cache.InvalidateRegion(addr, size);
+}
+
+void RasterizerOpenGL::SyncGuestHost() {
+ MICROPROFILE_SCOPE(OpenGL_CacheManagement);
+ texture_cache.SyncGuestHost();
+ buffer_cache.SyncGuestHost();
+}
+
+void RasterizerOpenGL::SignalSemaphore(GPUVAddr addr, u32 value) {
+ auto& gpu{system.GPU()};
+ if (!gpu.IsAsync()) {
+ auto& memory_manager{gpu.MemoryManager()};
+ memory_manager.Write<u32>(addr, value);
+ return;
+ }
+ fence_manager.SignalSemaphore(addr, value);
+}
+
+void RasterizerOpenGL::SignalSyncPoint(u32 value) {
+ auto& gpu{system.GPU()};
+ if (!gpu.IsAsync()) {
+ gpu.IncrementSyncPoint(value);
+ return;
+ }
+ fence_manager.SignalSyncPoint(value);
+}
+
+void RasterizerOpenGL::ReleaseFences() {
+ auto& gpu{system.GPU()};
+ if (!gpu.IsAsync()) {
+ return;
+ }
+ fence_manager.WaitPendingFences();
+}
+
void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) {
- if (Settings::values.use_accurate_gpu_emulation) {
+ if (Settings::IsGPULevelExtreme()) {
FlushRegion(addr, size);
}
InvalidateRegion(addr, size);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index caea174d2..ebd2173eb 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -23,6 +23,7 @@
#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_opengl/gl_buffer_cache.h"
#include "video_core/renderer_opengl/gl_device.h"
+#include "video_core/renderer_opengl/gl_fence_manager.h"
#include "video_core/renderer_opengl/gl_framebuffer_cache.h"
#include "video_core/renderer_opengl/gl_query_cache.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
@@ -66,7 +67,13 @@ public:
void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override;
void FlushAll() override;
void FlushRegion(VAddr addr, u64 size) override;
+ bool MustFlushRegion(VAddr addr, u64 size) override;
void InvalidateRegion(VAddr addr, u64 size) override;
+ void OnCPUWrite(VAddr addr, u64 size) override;
+ void SyncGuestHost() override;
+ void SignalSemaphore(GPUVAddr addr, u32 value) override;
+ void SignalSyncPoint(u32 value) override;
+ void ReleaseFences() override;
void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
void FlushCommands() override;
void TickFrame() override;
@@ -222,6 +229,8 @@ private:
SamplerCacheOpenGL sampler_cache;
FramebufferCacheOpenGL framebuffer_cache;
QueryCache query_cache;
+ OGLBufferCache buffer_cache;
+ FenceManagerOpenGL fence_manager;
Core::System& system;
ScreenInfo& screen_info;
@@ -229,7 +238,6 @@ private:
StateTracker& state_tracker;
static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
- OGLBufferCache buffer_cache;
GLint vertex_binding = 0;
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 6d2ff20f9..f63156b8d 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -448,7 +448,7 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
// Look up shader in the cache based on address
const auto cpu_addr{memory_manager.GpuToCpuAddress(address)};
- Shader shader{cpu_addr ? TryGet(*cpu_addr) : nullptr};
+ Shader shader{cpu_addr ? TryGet(*cpu_addr) : null_shader};
if (shader) {
return last_shaders[static_cast<std::size_t>(program)] = shader;
}
@@ -477,7 +477,12 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
const std::size_t size_in_bytes = code.size() * sizeof(u64);
shader = CachedShader::CreateFromCache(params, found->second, size_in_bytes);
}
- Register(shader);
+
+ if (cpu_addr) {
+ Register(shader);
+ } else {
+ null_shader = shader;
+ }
return last_shaders[static_cast<std::size_t>(program)] = shader;
}
@@ -486,7 +491,7 @@ Shader ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) {
auto& memory_manager{system.GPU().MemoryManager()};
const auto cpu_addr{memory_manager.GpuToCpuAddress(code_addr)};
- auto kernel = cpu_addr ? TryGet(*cpu_addr) : nullptr;
+ auto kernel = cpu_addr ? TryGet(*cpu_addr) : null_kernel;
if (kernel) {
return kernel;
}
@@ -507,7 +512,11 @@ Shader ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) {
kernel = CachedShader::CreateFromCache(params, found->second, size_in_bytes);
}
- Register(kernel);
+ if (cpu_addr) {
+ Register(kernel);
+ } else {
+ null_kernel = kernel;
+ }
return kernel;
}
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index c836df5bd..91690b470 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -125,6 +125,9 @@ private:
ShaderDiskCacheOpenGL disk_cache;
std::unordered_map<u64, PrecompiledShader> runtime_cache;
+ Shader null_shader{};
+ Shader null_kernel{};
+
std::array<Shader, Maxwell::MaxShaderProgram> last_shaders;
};
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
index 9fe6bdbf9..9a950f4de 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
@@ -129,7 +129,7 @@ struct alignas(32) FixedPipelineState {
auto& binding = bindings[index];
binding.raw = 0;
binding.enabled.Assign(enabled ? 1 : 0);
- binding.stride.Assign(stride);
+ binding.stride.Assign(static_cast<u16>(stride));
binding_divisors[index] = divisor;
}
diff --git a/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp b/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp
new file mode 100644
index 000000000..435c8c1b8
--- /dev/null
+++ b/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp
@@ -0,0 +1,220 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#ifdef HAS_NSIGHT_AFTERMATH
+
+#include <mutex>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include <fmt/format.h>
+
+#define VK_NO_PROTOTYPES
+#include <vulkan/vulkan.h>
+
+#include <GFSDK_Aftermath.h>
+#include <GFSDK_Aftermath_Defines.h>
+#include <GFSDK_Aftermath_GpuCrashDump.h>
+#include <GFSDK_Aftermath_GpuCrashDumpDecoding.h>
+
+#include "common/common_paths.h"
+#include "common/common_types.h"
+#include "common/file_util.h"
+#include "common/logging/log.h"
+#include "common/scope_exit.h"
+
+#include "video_core/renderer_vulkan/nsight_aftermath_tracker.h"
+
+namespace Vulkan {
+
+static constexpr char AFTERMATH_LIB_NAME[] = "GFSDK_Aftermath_Lib.x64.dll";
+
+NsightAftermathTracker::NsightAftermathTracker() = default;
+
+NsightAftermathTracker::~NsightAftermathTracker() {
+ if (initialized) {
+ (void)GFSDK_Aftermath_DisableGpuCrashDumps();
+ }
+}
+
+bool NsightAftermathTracker::Initialize() {
+ if (!dl.Open(AFTERMATH_LIB_NAME)) {
+ LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath DLL");
+ return false;
+ }
+
+ if (!dl.GetSymbol("GFSDK_Aftermath_DisableGpuCrashDumps",
+ &GFSDK_Aftermath_DisableGpuCrashDumps) ||
+ !dl.GetSymbol("GFSDK_Aftermath_EnableGpuCrashDumps",
+ &GFSDK_Aftermath_EnableGpuCrashDumps) ||
+ !dl.GetSymbol("GFSDK_Aftermath_GetShaderDebugInfoIdentifier",
+ &GFSDK_Aftermath_GetShaderDebugInfoIdentifier) ||
+ !dl.GetSymbol("GFSDK_Aftermath_GetShaderHashSpirv", &GFSDK_Aftermath_GetShaderHashSpirv) ||
+ !dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_CreateDecoder",
+ &GFSDK_Aftermath_GpuCrashDump_CreateDecoder) ||
+ !dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_DestroyDecoder",
+ &GFSDK_Aftermath_GpuCrashDump_DestroyDecoder) ||
+ !dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_GenerateJSON",
+ &GFSDK_Aftermath_GpuCrashDump_GenerateJSON) ||
+ !dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_GetJSON",
+ &GFSDK_Aftermath_GpuCrashDump_GetJSON)) {
+ LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath function pointers");
+ return false;
+ }
+
+ dump_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + "gpucrash";
+
+ (void)FileUtil::DeleteDirRecursively(dump_dir);
+ if (!FileUtil::CreateDir(dump_dir)) {
+ LOG_ERROR(Render_Vulkan, "Failed to create Nsight Aftermath dump directory");
+ return false;
+ }
+
+ if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_EnableGpuCrashDumps(
+ GFSDK_Aftermath_Version_API, GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan,
+ GFSDK_Aftermath_GpuCrashDumpFeatureFlags_Default, GpuCrashDumpCallback,
+ ShaderDebugInfoCallback, CrashDumpDescriptionCallback, this))) {
+ LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_EnableGpuCrashDumps failed");
+ return false;
+ }
+
+ LOG_INFO(Render_Vulkan, "Nsight Aftermath dump directory is \"{}\"", dump_dir);
+
+ initialized = true;
+ return true;
+}
+
+void NsightAftermathTracker::SaveShader(const std::vector<u32>& spirv) const {
+ if (!initialized) {
+ return;
+ }
+
+ std::vector<u32> spirv_copy = spirv;
+ GFSDK_Aftermath_SpirvCode shader;
+ shader.pData = spirv_copy.data();
+ shader.size = static_cast<u32>(spirv_copy.size() * 4);
+
+ std::scoped_lock lock{mutex};
+
+ GFSDK_Aftermath_ShaderHash hash;
+ if (!GFSDK_Aftermath_SUCCEED(
+ GFSDK_Aftermath_GetShaderHashSpirv(GFSDK_Aftermath_Version_API, &shader, &hash))) {
+ LOG_ERROR(Render_Vulkan, "Failed to hash SPIR-V module");
+ return;
+ }
+
+ FileUtil::IOFile file(fmt::format("{}/source_{:016x}.spv", dump_dir, hash.hash), "wb");
+ if (!file.IsOpen()) {
+ LOG_ERROR(Render_Vulkan, "Failed to dump SPIR-V module with hash={:016x}", hash.hash);
+ return;
+ }
+ if (file.WriteArray(spirv.data(), spirv.size()) != spirv.size()) {
+ LOG_ERROR(Render_Vulkan, "Failed to write SPIR-V module with hash={:016x}", hash.hash);
+ return;
+ }
+}
+
+void NsightAftermathTracker::OnGpuCrashDumpCallback(const void* gpu_crash_dump,
+ u32 gpu_crash_dump_size) {
+ std::scoped_lock lock{mutex};
+
+ LOG_CRITICAL(Render_Vulkan, "called");
+
+ GFSDK_Aftermath_GpuCrashDump_Decoder decoder;
+ if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_CreateDecoder(
+ GFSDK_Aftermath_Version_API, gpu_crash_dump, gpu_crash_dump_size, &decoder))) {
+ LOG_ERROR(Render_Vulkan, "Failed to create decoder");
+ return;
+ }
+ SCOPE_EXIT({ GFSDK_Aftermath_GpuCrashDump_DestroyDecoder(decoder); });
+
+ u32 json_size = 0;
+ if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_GenerateJSON(
+ decoder, GFSDK_Aftermath_GpuCrashDumpDecoderFlags_ALL_INFO,
+ GFSDK_Aftermath_GpuCrashDumpFormatterFlags_NONE, nullptr, nullptr, nullptr, nullptr,
+ this, &json_size))) {
+ LOG_ERROR(Render_Vulkan, "Failed to generate JSON");
+ return;
+ }
+ std::vector<char> json(json_size);
+ if (!GFSDK_Aftermath_SUCCEED(
+ GFSDK_Aftermath_GpuCrashDump_GetJSON(decoder, json_size, json.data()))) {
+ LOG_ERROR(Render_Vulkan, "Failed to query JSON");
+ return;
+ }
+
+ const std::string base_name = [this] {
+ const int id = dump_id++;
+ if (id == 0) {
+ return fmt::format("{}/crash.nv-gpudmp", dump_dir);
+ } else {
+ return fmt::format("{}/crash_{}.nv-gpudmp", dump_dir, id);
+ }
+ }();
+
+ std::string_view dump_view(static_cast<const char*>(gpu_crash_dump), gpu_crash_dump_size);
+ if (FileUtil::WriteStringToFile(false, base_name, dump_view) != gpu_crash_dump_size) {
+ LOG_ERROR(Render_Vulkan, "Failed to write dump file");
+ return;
+ }
+ const std::string_view json_view(json.data(), json.size());
+ if (FileUtil::WriteStringToFile(true, base_name + ".json", json_view) != json.size()) {
+ LOG_ERROR(Render_Vulkan, "Failed to write JSON");
+ return;
+ }
+}
+
+void NsightAftermathTracker::OnShaderDebugInfoCallback(const void* shader_debug_info,
+ u32 shader_debug_info_size) {
+ std::scoped_lock lock{mutex};
+
+ GFSDK_Aftermath_ShaderDebugInfoIdentifier identifier;
+ if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GetShaderDebugInfoIdentifier(
+ GFSDK_Aftermath_Version_API, shader_debug_info, shader_debug_info_size, &identifier))) {
+ LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_GetShaderDebugInfoIdentifier failed");
+ return;
+ }
+
+ const std::string path =
+ fmt::format("{}/shader_{:016x}{:016x}.nvdbg", dump_dir, identifier.id[0], identifier.id[1]);
+ FileUtil::IOFile file(path, "wb");
+ if (!file.IsOpen()) {
+ LOG_ERROR(Render_Vulkan, "Failed to create file {}", path);
+ return;
+ }
+ if (file.WriteBytes(static_cast<const u8*>(shader_debug_info), shader_debug_info_size) !=
+ shader_debug_info_size) {
+ LOG_ERROR(Render_Vulkan, "Failed to write file {}", path);
+ return;
+ }
+}
+
+void NsightAftermathTracker::OnCrashDumpDescriptionCallback(
+ PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description) {
+ add_description(GFSDK_Aftermath_GpuCrashDumpDescriptionKey_ApplicationName, "yuzu");
+}
+
+void NsightAftermathTracker::GpuCrashDumpCallback(const void* gpu_crash_dump,
+ u32 gpu_crash_dump_size, void* user_data) {
+ static_cast<NsightAftermathTracker*>(user_data)->OnGpuCrashDumpCallback(gpu_crash_dump,
+ gpu_crash_dump_size);
+}
+
+void NsightAftermathTracker::ShaderDebugInfoCallback(const void* shader_debug_info,
+ u32 shader_debug_info_size, void* user_data) {
+ static_cast<NsightAftermathTracker*>(user_data)->OnShaderDebugInfoCallback(
+ shader_debug_info, shader_debug_info_size);
+}
+
+void NsightAftermathTracker::CrashDumpDescriptionCallback(
+ PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description, void* user_data) {
+ static_cast<NsightAftermathTracker*>(user_data)->OnCrashDumpDescriptionCallback(
+ add_description);
+}
+
+} // namespace Vulkan
+
+#endif // HAS_NSIGHT_AFTERMATH
diff --git a/src/video_core/renderer_vulkan/nsight_aftermath_tracker.h b/src/video_core/renderer_vulkan/nsight_aftermath_tracker.h
new file mode 100644
index 000000000..afe7ae99e
--- /dev/null
+++ b/src/video_core/renderer_vulkan/nsight_aftermath_tracker.h
@@ -0,0 +1,87 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <mutex>
+#include <string>
+#include <vector>
+
+#define VK_NO_PROTOTYPES
+#include <vulkan/vulkan.h>
+
+#ifdef HAS_NSIGHT_AFTERMATH
+#include <GFSDK_Aftermath_Defines.h>
+#include <GFSDK_Aftermath_GpuCrashDump.h>
+#include <GFSDK_Aftermath_GpuCrashDumpDecoding.h>
+#endif
+
+#include "common/common_types.h"
+#include "common/dynamic_library.h"
+
+namespace Vulkan {
+
+class NsightAftermathTracker {
+public:
+ NsightAftermathTracker();
+ ~NsightAftermathTracker();
+
+ NsightAftermathTracker(const NsightAftermathTracker&) = delete;
+ NsightAftermathTracker& operator=(const NsightAftermathTracker&) = delete;
+
+ // Delete move semantics because Aftermath initialization uses a pointer to this.
+ NsightAftermathTracker(NsightAftermathTracker&&) = delete;
+ NsightAftermathTracker& operator=(NsightAftermathTracker&&) = delete;
+
+ bool Initialize();
+
+ void SaveShader(const std::vector<u32>& spirv) const;
+
+private:
+#ifdef HAS_NSIGHT_AFTERMATH
+ static void GpuCrashDumpCallback(const void* gpu_crash_dump, u32 gpu_crash_dump_size,
+ void* user_data);
+
+ static void ShaderDebugInfoCallback(const void* shader_debug_info, u32 shader_debug_info_size,
+ void* user_data);
+
+ static void CrashDumpDescriptionCallback(
+ PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description, void* user_data);
+
+ void OnGpuCrashDumpCallback(const void* gpu_crash_dump, u32 gpu_crash_dump_size);
+
+ void OnShaderDebugInfoCallback(const void* shader_debug_info, u32 shader_debug_info_size);
+
+ void OnCrashDumpDescriptionCallback(
+ PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description);
+
+ mutable std::mutex mutex;
+
+ std::string dump_dir;
+ int dump_id = 0;
+
+ bool initialized = false;
+
+ Common::DynamicLibrary dl;
+ PFN_GFSDK_Aftermath_DisableGpuCrashDumps GFSDK_Aftermath_DisableGpuCrashDumps;
+ PFN_GFSDK_Aftermath_EnableGpuCrashDumps GFSDK_Aftermath_EnableGpuCrashDumps;
+ PFN_GFSDK_Aftermath_GetShaderDebugInfoIdentifier GFSDK_Aftermath_GetShaderDebugInfoIdentifier;
+ PFN_GFSDK_Aftermath_GetShaderHashSpirv GFSDK_Aftermath_GetShaderHashSpirv;
+ PFN_GFSDK_Aftermath_GpuCrashDump_CreateDecoder GFSDK_Aftermath_GpuCrashDump_CreateDecoder;
+ PFN_GFSDK_Aftermath_GpuCrashDump_DestroyDecoder GFSDK_Aftermath_GpuCrashDump_DestroyDecoder;
+ PFN_GFSDK_Aftermath_GpuCrashDump_GenerateJSON GFSDK_Aftermath_GpuCrashDump_GenerateJSON;
+ PFN_GFSDK_Aftermath_GpuCrashDump_GetJSON GFSDK_Aftermath_GpuCrashDump_GetJSON;
+#endif
+};
+
+#ifndef HAS_NSIGHT_AFTERMATH
+inline NsightAftermathTracker::NsightAftermathTracker() = default;
+inline NsightAftermathTracker::~NsightAftermathTracker() = default;
+inline bool NsightAftermathTracker::Initialize() {
+ return false;
+}
+inline void NsightAftermathTracker::SaveShader(const std::vector<u32>&) const {}
+#endif
+
+} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index 23beafa4f..52566bb79 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -105,6 +105,8 @@ vk::DescriptorUpdateTemplateKHR VKComputePipeline::CreateDescriptorUpdateTemplat
}
vk::ShaderModule VKComputePipeline::CreateShaderModule(const std::vector<u32>& code) const {
+ device.SaveShader(code);
+
VkShaderModuleCreateInfo ci;
ci.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
ci.pNext = nullptr;
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp
index 52d29e49d..e90c76492 100644
--- a/src/video_core/renderer_vulkan/vk_device.cpp
+++ b/src/video_core/renderer_vulkan/vk_device.cpp
@@ -9,6 +9,7 @@
#include <string_view>
#include <thread>
#include <unordered_set>
+#include <utility>
#include <vector>
#include "common/assert.h"
@@ -167,6 +168,7 @@ bool VKDevice::Create() {
VkPhysicalDeviceFeatures2 features2;
features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
features2.pNext = nullptr;
+ const void* first_next = &features2;
void** next = &features2.pNext;
auto& features = features2.features;
@@ -296,7 +298,19 @@ bool VKDevice::Create() {
LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
}
- logical = vk::Device::Create(physical, queue_cis, extensions, features2, dld);
+ VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv;
+ if (nv_device_diagnostics_config) {
+ nsight_aftermath_tracker.Initialize();
+
+ diagnostics_nv.sType = VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV;
+ diagnostics_nv.pNext = &features2;
+ diagnostics_nv.flags = VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_DEBUG_INFO_BIT_NV |
+ VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_RESOURCE_TRACKING_BIT_NV |
+ VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_AUTOMATIC_CHECKPOINTS_BIT_NV;
+ first_next = &diagnostics_nv;
+ }
+
+ logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld);
if (!logical) {
LOG_ERROR(Render_Vulkan, "Failed to create logical device");
return false;
@@ -344,17 +358,12 @@ VkFormat VKDevice::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFla
void VKDevice::ReportLoss() const {
LOG_CRITICAL(Render_Vulkan, "Device loss occured!");
- // Wait some time to let the log flush
- std::this_thread::sleep_for(std::chrono::seconds{1});
-
- if (!nv_device_diagnostic_checkpoints) {
- return;
- }
+ // Wait for the log to flush and for Nsight Aftermath to dump the results
+ std::this_thread::sleep_for(std::chrono::seconds{3});
+}
- [[maybe_unused]] const std::vector data = graphics_queue.GetCheckpointDataNV(dld);
- // Catch here in debug builds (or with optimizations disabled) the last graphics pipeline to be
- // executed. It can be done on a debugger by evaluating the expression:
- // *(VKGraphicsPipeline*)data[0]
+void VKDevice::SaveShader(const std::vector<u32>& spirv) const {
+ nsight_aftermath_tracker.SaveShader(spirv);
}
bool VKDevice::IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) const {
@@ -527,8 +536,8 @@ std::vector<const char*> VKDevice::LoadExtensions() {
Test(extension, has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME,
false);
if (Settings::values.renderer_debug) {
- Test(extension, nv_device_diagnostic_checkpoints,
- VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME, true);
+ Test(extension, nv_device_diagnostics_config,
+ VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true);
}
}
diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/renderer_vulkan/vk_device.h
index 60d64572a..a4d841e26 100644
--- a/src/video_core/renderer_vulkan/vk_device.h
+++ b/src/video_core/renderer_vulkan/vk_device.h
@@ -10,6 +10,7 @@
#include <vector>
#include "common/common_types.h"
+#include "video_core/renderer_vulkan/nsight_aftermath_tracker.h"
#include "video_core/renderer_vulkan/wrapper.h"
namespace Vulkan {
@@ -43,6 +44,9 @@ public:
/// Reports a device loss.
void ReportLoss() const;
+ /// Reports a shader to Nsight Aftermath.
+ void SaveShader(const std::vector<u32>& spirv) const;
+
/// Returns the dispatch loader with direct function pointers of the device.
const vk::DeviceDispatch& GetDispatchLoader() const {
return dld;
@@ -173,11 +177,6 @@ public:
return ext_transform_feedback;
}
- /// Returns true if the device supports VK_NV_device_diagnostic_checkpoints.
- bool IsNvDeviceDiagnosticCheckpoints() const {
- return nv_device_diagnostic_checkpoints;
- }
-
/// Returns the vendor name reported from Vulkan.
std::string_view GetVendorName() const {
return vendor_name;
@@ -233,7 +232,7 @@ private:
bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback.
- bool nv_device_diagnostic_checkpoints{}; ///< Support for VK_NV_device_diagnostic_checkpoints.
+ bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
// Telemetry parameters
std::string vendor_name; ///< Device's driver name.
@@ -241,6 +240,9 @@ private:
/// Format properties dictionary.
std::unordered_map<VkFormat, VkFormatProperties> format_properties;
+
+ /// Nsight Aftermath GPU crash tracker
+ NsightAftermathTracker nsight_aftermath_tracker;
};
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.cpp b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
new file mode 100644
index 000000000..a02be5487
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
@@ -0,0 +1,101 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <memory>
+#include <thread>
+
+#include "video_core/renderer_vulkan/vk_buffer_cache.h"
+#include "video_core/renderer_vulkan/vk_device.h"
+#include "video_core/renderer_vulkan/vk_fence_manager.h"
+#include "video_core/renderer_vulkan/vk_scheduler.h"
+#include "video_core/renderer_vulkan/vk_texture_cache.h"
+#include "video_core/renderer_vulkan/wrapper.h"
+
+namespace Vulkan {
+
+InnerFence::InnerFence(const VKDevice& device, VKScheduler& scheduler, u32 payload, bool is_stubbed)
+ : VideoCommon::FenceBase(payload, is_stubbed), device{device}, scheduler{scheduler} {}
+
+InnerFence::InnerFence(const VKDevice& device, VKScheduler& scheduler, GPUVAddr address,
+ u32 payload, bool is_stubbed)
+ : VideoCommon::FenceBase(address, payload, is_stubbed), device{device}, scheduler{scheduler} {}
+
+InnerFence::~InnerFence() = default;
+
+void InnerFence::Queue() {
+ if (is_stubbed) {
+ return;
+ }
+ ASSERT(!event);
+
+ event = device.GetLogical().CreateEvent();
+ ticks = scheduler.Ticks();
+
+ scheduler.RequestOutsideRenderPassOperationContext();
+ scheduler.Record([event = *event](vk::CommandBuffer cmdbuf) {
+ cmdbuf.SetEvent(event, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
+ });
+}
+
+bool InnerFence::IsSignaled() const {
+ if (is_stubbed) {
+ return true;
+ }
+ ASSERT(event);
+ return IsEventSignalled();
+}
+
+void InnerFence::Wait() {
+ if (is_stubbed) {
+ return;
+ }
+ ASSERT(event);
+
+ if (ticks >= scheduler.Ticks()) {
+ scheduler.Flush();
+ }
+ while (!IsEventSignalled()) {
+ std::this_thread::yield();
+ }
+}
+
+bool InnerFence::IsEventSignalled() const {
+ switch (const VkResult result = event.GetStatus()) {
+ case VK_EVENT_SET:
+ return true;
+ case VK_EVENT_RESET:
+ return false;
+ default:
+ throw vk::Exception(result);
+ }
+}
+
+VKFenceManager::VKFenceManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
+ const VKDevice& device, VKScheduler& scheduler,
+ VKTextureCache& texture_cache, VKBufferCache& buffer_cache,
+ VKQueryCache& query_cache)
+ : GenericFenceManager(system, rasterizer, texture_cache, buffer_cache, query_cache),
+ device{device}, scheduler{scheduler} {}
+
+Fence VKFenceManager::CreateFence(u32 value, bool is_stubbed) {
+ return std::make_shared<InnerFence>(device, scheduler, value, is_stubbed);
+}
+
+Fence VKFenceManager::CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) {
+ return std::make_shared<InnerFence>(device, scheduler, addr, value, is_stubbed);
+}
+
+void VKFenceManager::QueueFence(Fence& fence) {
+ fence->Queue();
+}
+
+bool VKFenceManager::IsFenceSignaled(Fence& fence) const {
+ return fence->IsSignaled();
+}
+
+void VKFenceManager::WaitFence(Fence& fence) {
+ fence->Wait();
+}
+
+} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.h b/src/video_core/renderer_vulkan/vk_fence_manager.h
new file mode 100644
index 000000000..04d07fe6a
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.h
@@ -0,0 +1,74 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+
+#include "video_core/fence_manager.h"
+#include "video_core/renderer_vulkan/wrapper.h"
+
+namespace Core {
+class System;
+}
+
+namespace VideoCore {
+class RasterizerInterface;
+}
+
+namespace Vulkan {
+
+class VKBufferCache;
+class VKDevice;
+class VKQueryCache;
+class VKScheduler;
+class VKTextureCache;
+
+class InnerFence : public VideoCommon::FenceBase {
+public:
+ explicit InnerFence(const VKDevice& device, VKScheduler& scheduler, u32 payload,
+ bool is_stubbed);
+ explicit InnerFence(const VKDevice& device, VKScheduler& scheduler, GPUVAddr address,
+ u32 payload, bool is_stubbed);
+ ~InnerFence();
+
+ void Queue();
+
+ bool IsSignaled() const;
+
+ void Wait();
+
+private:
+ bool IsEventSignalled() const;
+
+ const VKDevice& device;
+ VKScheduler& scheduler;
+ vk::Event event;
+ u64 ticks = 0;
+};
+using Fence = std::shared_ptr<InnerFence>;
+
+using GenericFenceManager =
+ VideoCommon::FenceManager<Fence, VKTextureCache, VKBufferCache, VKQueryCache>;
+
+class VKFenceManager final : public GenericFenceManager {
+public:
+ explicit VKFenceManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
+ const VKDevice& device, VKScheduler& scheduler,
+ VKTextureCache& texture_cache, VKBufferCache& buffer_cache,
+ VKQueryCache& query_cache);
+
+protected:
+ Fence CreateFence(u32 value, bool is_stubbed) override;
+ Fence CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) override;
+ void QueueFence(Fence& fence) override;
+ bool IsFenceSignaled(Fence& fence) const override;
+ void WaitFence(Fence& fence) override;
+
+private:
+ const VKDevice& device;
+ VKScheduler& scheduler;
+};
+
+} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 343999cf5..8332b42aa 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -148,6 +148,8 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules(
continue;
}
+ device.SaveShader(stage->code);
+
ci.codeSize = stage->code.size() * sizeof(u32);
ci.pCode = stage->code.data();
modules.push_back(device.GetLogical().CreateShaderModule(ci));
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 8fdc6400d..91b1b16a5 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -207,7 +207,7 @@ std::array<Shader, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() {
const GPUVAddr program_addr{GetShaderAddress(system, program)};
const std::optional cpu_addr = memory_manager.GpuToCpuAddress(program_addr);
ASSERT(cpu_addr);
- auto shader = cpu_addr ? TryGet(*cpu_addr) : nullptr;
+ auto shader = cpu_addr ? TryGet(*cpu_addr) : null_shader;
if (!shader) {
const auto host_ptr{memory_manager.GetPointer(program_addr)};
@@ -218,7 +218,11 @@ std::array<Shader, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() {
shader = std::make_shared<CachedShader>(system, stage, program_addr, *cpu_addr,
std::move(code), stage_offset);
- Register(shader);
+ if (cpu_addr) {
+ Register(shader);
+ } else {
+ null_shader = shader;
+ }
}
shaders[index] = std::move(shader);
}
@@ -261,7 +265,7 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach
const auto cpu_addr = memory_manager.GpuToCpuAddress(program_addr);
ASSERT(cpu_addr);
- auto shader = cpu_addr ? TryGet(*cpu_addr) : nullptr;
+ auto shader = cpu_addr ? TryGet(*cpu_addr) : null_kernel;
if (!shader) {
// No shader found - create a new one
const auto host_ptr = memory_manager.GetPointer(program_addr);
@@ -271,7 +275,11 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach
shader = std::make_shared<CachedShader>(system, Tegra::Engines::ShaderType::Compute,
program_addr, *cpu_addr, std::move(code),
kernel_main_offset);
- Register(shader);
+ if (cpu_addr) {
+ Register(shader);
+ } else {
+ null_kernel = shader;
+ }
}
Specialization specialization;
@@ -330,8 +338,10 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) {
Specialization specialization;
if (fixed_state.rasterizer.Topology() == Maxwell::PrimitiveTopology::Points) {
- ASSERT(fixed_state.rasterizer.point_size != 0);
- std::memcpy(&specialization.point_size, &fixed_state.rasterizer.point_size, sizeof(u32));
+ float point_size;
+ std::memcpy(&point_size, &fixed_state.rasterizer.point_size, sizeof(float));
+ specialization.point_size = point_size;
+ ASSERT(point_size != 0.0f);
}
for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) {
specialization.attribute_types[i] = fixed_state.vertex_input.attributes[i].Type();
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index 7ccdb7083..602a0a340 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -182,6 +182,9 @@ private:
VKUpdateDescriptorQueue& update_descriptor_queue;
VKRenderPassCache& renderpass_cache;
+ Shader null_shader{};
+ Shader null_kernel{};
+
std::array<Shader, Maxwell::MaxShaderProgram> last_shaders;
GraphicsPipelineCacheKey last_graphics_key;
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index 0966c7ff7..813f7c162 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -113,8 +113,19 @@ u64 HostCounter::BlockingQuery() const {
if (ticks >= cache.Scheduler().Ticks()) {
cache.Scheduler().Flush();
}
- return cache.Device().GetLogical().GetQueryResult<u64>(
- query.first, query.second, VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
+ u64 data;
+ const VkResult result = cache.Device().GetLogical().GetQueryResults(
+ query.first, query.second, 1, sizeof(data), &data, sizeof(data),
+ VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
+ switch (result) {
+ case VK_SUCCESS:
+ return data;
+ case VK_ERROR_DEVICE_LOST:
+ cache.Device().ReportLoss();
+ [[fallthrough]];
+ default:
+ throw vk::Exception(result);
+ }
}
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 71007bbe8..8a1f57891 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -17,6 +17,7 @@
#include "common/microprofile.h"
#include "core/core.h"
#include "core/memory.h"
+#include "core/settings.h"
#include "video_core/engines/kepler_compute.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
@@ -299,7 +300,9 @@ RasterizerVulkan::RasterizerVulkan(Core::System& system, Core::Frontend::EmuWind
pipeline_cache(system, *this, device, scheduler, descriptor_pool, update_descriptor_queue,
renderpass_cache),
buffer_cache(*this, system, device, memory_manager, scheduler, staging_pool),
- sampler_cache(device), query_cache(system, *this, device, scheduler) {
+ sampler_cache(device),
+ fence_manager(system, *this, device, scheduler, texture_cache, buffer_cache, query_cache),
+ query_cache(system, *this, device, scheduler) {
scheduler.SetQueryCache(query_cache);
}
@@ -347,11 +350,6 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
buffer_bindings.Bind(scheduler);
- if (device.IsNvDeviceDiagnosticCheckpoints()) {
- scheduler.Record(
- [&pipeline](vk::CommandBuffer cmdbuf) { cmdbuf.SetCheckpointNV(&pipeline); });
- }
-
BeginTransformFeedback();
const auto pipeline_layout = pipeline.GetLayout();
@@ -365,6 +363,8 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
});
EndTransformFeedback();
+
+ system.GPU().TickWork();
}
void RasterizerVulkan::Clear() {
@@ -478,11 +478,6 @@ void RasterizerVulkan::DispatchCompute(GPUVAddr code_addr) {
TransitionImages(image_views, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT);
- if (device.IsNvDeviceDiagnosticCheckpoints()) {
- scheduler.Record(
- [&pipeline](vk::CommandBuffer cmdbuf) { cmdbuf.SetCheckpointNV(nullptr); });
- }
-
scheduler.Record([grid_x = launch_desc.grid_dim_x, grid_y = launch_desc.grid_dim_y,
grid_z = launch_desc.grid_dim_z, pipeline_handle = pipeline.GetHandle(),
layout = pipeline.GetLayout(),
@@ -514,6 +509,13 @@ void RasterizerVulkan::FlushRegion(VAddr addr, u64 size) {
query_cache.FlushRegion(addr, size);
}
+bool RasterizerVulkan::MustFlushRegion(VAddr addr, u64 size) {
+ if (!Settings::IsGPULevelHigh()) {
+ return buffer_cache.MustFlushRegion(addr, size);
+ }
+ return texture_cache.MustFlushRegion(addr, size) || buffer_cache.MustFlushRegion(addr, size);
+}
+
void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size) {
if (addr == 0 || size == 0) {
return;
@@ -524,6 +526,47 @@ void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size) {
query_cache.InvalidateRegion(addr, size);
}
+void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) {
+ if (addr == 0 || size == 0) {
+ return;
+ }
+ texture_cache.OnCPUWrite(addr, size);
+ pipeline_cache.InvalidateRegion(addr, size);
+ buffer_cache.OnCPUWrite(addr, size);
+ query_cache.InvalidateRegion(addr, size);
+}
+
+void RasterizerVulkan::SyncGuestHost() {
+ texture_cache.SyncGuestHost();
+ buffer_cache.SyncGuestHost();
+}
+
+void RasterizerVulkan::SignalSemaphore(GPUVAddr addr, u32 value) {
+ auto& gpu{system.GPU()};
+ if (!gpu.IsAsync()) {
+ gpu.MemoryManager().Write<u32>(addr, value);
+ return;
+ }
+ fence_manager.SignalSemaphore(addr, value);
+}
+
+void RasterizerVulkan::SignalSyncPoint(u32 value) {
+ auto& gpu{system.GPU()};
+ if (!gpu.IsAsync()) {
+ gpu.IncrementSyncPoint(value);
+ return;
+ }
+ fence_manager.SignalSyncPoint(value);
+}
+
+void RasterizerVulkan::ReleaseFences() {
+ auto& gpu{system.GPU()};
+ if (!gpu.IsAsync()) {
+ return;
+ }
+ fence_manager.WaitPendingFences();
+}
+
void RasterizerVulkan::FlushAndInvalidateRegion(VAddr addr, u64 size) {
FlushRegion(addr, size);
InvalidateRegion(addr, size);
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index d9108f862..2fa46b0cc 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -21,6 +21,7 @@
#include "video_core/renderer_vulkan/vk_buffer_cache.h"
#include "video_core/renderer_vulkan/vk_compute_pass.h"
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
+#include "video_core/renderer_vulkan/vk_fence_manager.h"
#include "video_core/renderer_vulkan/vk_memory_manager.h"
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
#include "video_core/renderer_vulkan/vk_query_cache.h"
@@ -118,7 +119,13 @@ public:
void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override;
void FlushAll() override;
void FlushRegion(VAddr addr, u64 size) override;
+ bool MustFlushRegion(VAddr addr, u64 size) override;
void InvalidateRegion(VAddr addr, u64 size) override;
+ void OnCPUWrite(VAddr addr, u64 size) override;
+ void SyncGuestHost() override;
+ void SignalSemaphore(GPUVAddr addr, u32 value) override;
+ void SignalSyncPoint(u32 value) override;
+ void ReleaseFences() override;
void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
void FlushCommands() override;
void TickFrame() override;
@@ -261,6 +268,7 @@ private:
VKPipelineCache pipeline_cache;
VKBufferCache buffer_cache;
VKSamplerCache sampler_cache;
+ VKFenceManager fence_manager;
VKQueryCache query_cache;
std::array<View, Maxwell::NumRenderTargets> color_attachments;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 900f551b3..ae7ba3eb5 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -166,7 +166,15 @@ void VKScheduler::SubmitExecution(VkSemaphore semaphore) {
submit_info.pCommandBuffers = current_cmdbuf.address();
submit_info.signalSemaphoreCount = semaphore ? 1 : 0;
submit_info.pSignalSemaphores = &semaphore;
- device.GetGraphicsQueue().Submit(submit_info, *current_fence);
+ switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info, *current_fence)) {
+ case VK_SUCCESS:
+ break;
+ case VK_ERROR_DEVICE_LOST:
+ device.ReportLoss();
+ [[fallthrough]];
+ default:
+ vk::Check(result);
+ }
}
void VKScheduler::AllocateNewContext() {
diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp
index 9b94dfff1..7f5bc1404 100644
--- a/src/video_core/renderer_vulkan/wrapper.cpp
+++ b/src/video_core/renderer_vulkan/wrapper.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
#include <exception>
#include <memory>
#include <optional>
@@ -16,6 +17,23 @@ namespace Vulkan::vk {
namespace {
+void SortPhysicalDevices(std::vector<VkPhysicalDevice>& devices, const InstanceDispatch& dld) {
+ std::stable_sort(devices.begin(), devices.end(), [&](auto lhs, auto rhs) {
+ // This will call Vulkan more than needed, but these calls are cheap.
+ const auto lhs_properties = vk::PhysicalDevice(lhs, dld).GetProperties();
+ const auto rhs_properties = vk::PhysicalDevice(rhs, dld).GetProperties();
+
+ // Prefer discrete GPUs, Nvidia over AMD, AMD over Intel, Intel over the rest.
+ const bool preferred =
+ (lhs_properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU &&
+ rhs_properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) ||
+ (lhs_properties.vendorID == 0x10DE && rhs_properties.vendorID != 0x10DE) ||
+ (lhs_properties.vendorID == 0x1002 && rhs_properties.vendorID != 0x1002) ||
+ (lhs_properties.vendorID == 0x8086 && rhs_properties.vendorID != 0x8086);
+ return !preferred;
+ });
+}
+
template <typename T>
bool Proc(T& result, const InstanceDispatch& dld, const char* proc_name,
VkInstance instance = nullptr) noexcept {
@@ -61,9 +79,9 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkCmdPipelineBarrier);
X(vkCmdPushConstants);
X(vkCmdSetBlendConstants);
- X(vkCmdSetCheckpointNV);
X(vkCmdSetDepthBias);
X(vkCmdSetDepthBounds);
+ X(vkCmdSetEvent);
X(vkCmdSetScissor);
X(vkCmdSetStencilCompareMask);
X(vkCmdSetStencilReference);
@@ -76,6 +94,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkCreateDescriptorPool);
X(vkCreateDescriptorSetLayout);
X(vkCreateDescriptorUpdateTemplateKHR);
+ X(vkCreateEvent);
X(vkCreateFence);
X(vkCreateFramebuffer);
X(vkCreateGraphicsPipelines);
@@ -94,6 +113,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkDestroyDescriptorPool);
X(vkDestroyDescriptorSetLayout);
X(vkDestroyDescriptorUpdateTemplateKHR);
+ X(vkDestroyEvent);
X(vkDestroyFence);
X(vkDestroyFramebuffer);
X(vkDestroyImage);
@@ -113,10 +133,10 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkFreeMemory);
X(vkGetBufferMemoryRequirements);
X(vkGetDeviceQueue);
+ X(vkGetEventStatus);
X(vkGetFenceStatus);
X(vkGetImageMemoryRequirements);
X(vkGetQueryPoolResults);
- X(vkGetQueueCheckpointDataNV);
X(vkMapMemory);
X(vkQueueSubmit);
X(vkResetFences);
@@ -271,6 +291,10 @@ void Destroy(VkDevice device, VkDeviceMemory handle, const DeviceDispatch& dld)
dld.vkFreeMemory(device, handle, nullptr);
}
+void Destroy(VkDevice device, VkEvent handle, const DeviceDispatch& dld) noexcept {
+ dld.vkDestroyEvent(device, handle, nullptr);
+}
+
void Destroy(VkDevice device, VkFence handle, const DeviceDispatch& dld) noexcept {
dld.vkDestroyFence(device, handle, nullptr);
}
@@ -383,7 +407,8 @@ std::optional<std::vector<VkPhysicalDevice>> Instance::EnumeratePhysicalDevices(
if (dld->vkEnumeratePhysicalDevices(handle, &num, physical_devices.data()) != VK_SUCCESS) {
return std::nullopt;
}
- return physical_devices;
+ SortPhysicalDevices(physical_devices, *dld);
+ return std::make_optional(std::move(physical_devices));
}
DebugCallback Instance::TryCreateDebugCallback(
@@ -409,17 +434,6 @@ DebugCallback Instance::TryCreateDebugCallback(
return DebugCallback(messenger, handle, *dld);
}
-std::vector<VkCheckpointDataNV> Queue::GetCheckpointDataNV(const DeviceDispatch& dld) const {
- if (!dld.vkGetQueueCheckpointDataNV) {
- return {};
- }
- u32 num;
- dld.vkGetQueueCheckpointDataNV(queue, &num, nullptr);
- std::vector<VkCheckpointDataNV> checkpoints(num);
- dld.vkGetQueueCheckpointDataNV(queue, &num, checkpoints.data());
- return checkpoints;
-}
-
void Buffer::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const {
Check(dld->vkBindBufferMemory(owner, handle, memory, offset));
}
@@ -469,12 +483,11 @@ std::vector<VkImage> SwapchainKHR::GetImages() const {
}
Device Device::Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci,
- Span<const char*> enabled_extensions,
- const VkPhysicalDeviceFeatures2& enabled_features,
+ Span<const char*> enabled_extensions, const void* next,
DeviceDispatch& dld) noexcept {
VkDeviceCreateInfo ci;
ci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
- ci.pNext = &enabled_features;
+ ci.pNext = next;
ci.flags = 0;
ci.queueCreateInfoCount = queues_ci.size();
ci.pQueueCreateInfos = queues_ci.data();
@@ -613,6 +626,16 @@ ShaderModule Device::CreateShaderModule(const VkShaderModuleCreateInfo& ci) cons
return ShaderModule(object, handle, *dld);
}
+Event Device::CreateEvent() const {
+ VkEventCreateInfo ci;
+ ci.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
+ ci.pNext = nullptr;
+ ci.flags = 0;
+ VkEvent object;
+ Check(dld->vkCreateEvent(handle, &ci, nullptr, &object));
+ return Event(object, handle, *dld);
+}
+
SwapchainKHR Device::CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const {
VkSwapchainKHR object;
Check(dld->vkCreateSwapchainKHR(handle, &ci, nullptr, &object));
diff --git a/src/video_core/renderer_vulkan/wrapper.h b/src/video_core/renderer_vulkan/wrapper.h
index fb3657819..bda16a2cb 100644
--- a/src/video_core/renderer_vulkan/wrapper.h
+++ b/src/video_core/renderer_vulkan/wrapper.h
@@ -197,9 +197,9 @@ struct DeviceDispatch : public InstanceDispatch {
PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier;
PFN_vkCmdPushConstants vkCmdPushConstants;
PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants;
- PFN_vkCmdSetCheckpointNV vkCmdSetCheckpointNV;
PFN_vkCmdSetDepthBias vkCmdSetDepthBias;
PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds;
+ PFN_vkCmdSetEvent vkCmdSetEvent;
PFN_vkCmdSetScissor vkCmdSetScissor;
PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask;
PFN_vkCmdSetStencilReference vkCmdSetStencilReference;
@@ -212,6 +212,7 @@ struct DeviceDispatch : public InstanceDispatch {
PFN_vkCreateDescriptorPool vkCreateDescriptorPool;
PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout;
PFN_vkCreateDescriptorUpdateTemplateKHR vkCreateDescriptorUpdateTemplateKHR;
+ PFN_vkCreateEvent vkCreateEvent;
PFN_vkCreateFence vkCreateFence;
PFN_vkCreateFramebuffer vkCreateFramebuffer;
PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines;
@@ -230,6 +231,7 @@ struct DeviceDispatch : public InstanceDispatch {
PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool;
PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout;
PFN_vkDestroyDescriptorUpdateTemplateKHR vkDestroyDescriptorUpdateTemplateKHR;
+ PFN_vkDestroyEvent vkDestroyEvent;
PFN_vkDestroyFence vkDestroyFence;
PFN_vkDestroyFramebuffer vkDestroyFramebuffer;
PFN_vkDestroyImage vkDestroyImage;
@@ -249,10 +251,10 @@ struct DeviceDispatch : public InstanceDispatch {
PFN_vkFreeMemory vkFreeMemory;
PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
PFN_vkGetDeviceQueue vkGetDeviceQueue;
+ PFN_vkGetEventStatus vkGetEventStatus;
PFN_vkGetFenceStatus vkGetFenceStatus;
PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
PFN_vkGetQueryPoolResults vkGetQueryPoolResults;
- PFN_vkGetQueueCheckpointDataNV vkGetQueueCheckpointDataNV;
PFN_vkMapMemory vkMapMemory;
PFN_vkQueueSubmit vkQueueSubmit;
PFN_vkResetFences vkResetFences;
@@ -281,6 +283,7 @@ void Destroy(VkDevice, VkDescriptorPool, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkDescriptorSetLayout, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkDescriptorUpdateTemplateKHR, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkDeviceMemory, const DeviceDispatch&) noexcept;
+void Destroy(VkDevice, VkEvent, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkFence, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkFramebuffer, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkImage, const DeviceDispatch&) noexcept;
@@ -567,12 +570,8 @@ public:
/// Construct a queue handle.
constexpr Queue(VkQueue queue, const DeviceDispatch& dld) noexcept : queue{queue}, dld{&dld} {}
- /// Returns the checkpoint data.
- /// @note Returns an empty vector when the function pointer is not present.
- std::vector<VkCheckpointDataNV> GetCheckpointDataNV(const DeviceDispatch& dld) const;
-
- void Submit(Span<VkSubmitInfo> submit_infos, VkFence fence) const {
- Check(dld->vkQueueSubmit(queue, submit_infos.size(), submit_infos.data(), fence));
+ VkResult Submit(Span<VkSubmitInfo> submit_infos, VkFence fence) const noexcept {
+ return dld->vkQueueSubmit(queue, submit_infos.size(), submit_infos.data(), fence);
}
VkResult Present(const VkPresentInfoKHR& present_info) const noexcept {
@@ -654,13 +653,21 @@ public:
std::vector<VkImage> GetImages() const;
};
+class Event : public Handle<VkEvent, VkDevice, DeviceDispatch> {
+ using Handle<VkEvent, VkDevice, DeviceDispatch>::Handle;
+
+public:
+ VkResult GetStatus() const noexcept {
+ return dld->vkGetEventStatus(owner, handle);
+ }
+};
+
class Device : public Handle<VkDevice, NoOwner, DeviceDispatch> {
using Handle<VkDevice, NoOwner, DeviceDispatch>::Handle;
public:
static Device Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci,
- Span<const char*> enabled_extensions,
- const VkPhysicalDeviceFeatures2& enabled_features,
+ Span<const char*> enabled_extensions, const void* next,
DeviceDispatch& dld) noexcept;
Queue GetQueue(u32 family_index) const noexcept;
@@ -702,6 +709,8 @@ public:
ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const;
+ Event CreateEvent() const;
+
SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const;
DeviceMemory TryAllocateMemory(const VkMemoryAllocateInfo& ai) const noexcept;
@@ -734,18 +743,11 @@ public:
dld->vkResetQueryPoolEXT(handle, query_pool, first, count);
}
- void GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size,
- void* data, VkDeviceSize stride, VkQueryResultFlags flags) const {
- Check(dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride,
- flags));
- }
-
- template <typename T>
- T GetQueryResult(VkQueryPool query_pool, u32 first, VkQueryResultFlags flags) const {
- static_assert(std::is_trivially_copyable_v<T>);
- T value;
- GetQueryResults(query_pool, first, 1, sizeof(T), &value, sizeof(T), flags);
- return value;
+ VkResult GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size,
+ void* data, VkDeviceSize stride, VkQueryResultFlags flags) const
+ noexcept {
+ return dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride,
+ flags);
}
};
@@ -920,10 +922,6 @@ public:
dld->vkCmdPushConstants(handle, layout, flags, offset, size, values);
}
- void SetCheckpointNV(const void* checkpoint_marker) const noexcept {
- dld->vkCmdSetCheckpointNV(handle, checkpoint_marker);
- }
-
void SetViewport(u32 first, Span<VkViewport> viewports) const noexcept {
dld->vkCmdSetViewport(handle, first, viewports.size(), viewports.data());
}
@@ -956,6 +954,10 @@ public:
dld->vkCmdSetDepthBounds(handle, min_depth_bounds, max_depth_bounds);
}
+ void SetEvent(VkEvent event, VkPipelineStageFlags stage_flags) const noexcept {
+ dld->vkCmdSetEvent(handle, event, stage_flags);
+ }
+
void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers,
const VkDeviceSize* offsets,
const VkDeviceSize* sizes) const noexcept {
diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp
index 6d313963a..e00a3fb70 100644
--- a/src/video_core/shader/control_flow.cpp
+++ b/src/video_core/shader/control_flow.cpp
@@ -587,8 +587,6 @@ bool TryQuery(CFGRebuildState& state) {
return true;
}
-} // Anonymous namespace
-
void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) {
const auto get_expr = ([&](const Condition& cond) -> Expr {
Expr result{};
@@ -655,6 +653,8 @@ void DecompileShader(CFGRebuildState& state) {
state.manager->Decompile();
}
+} // Anonymous namespace
+
std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 start_address,
const CompilerSettings& settings,
Registry& registry) {
diff --git a/src/video_core/shader/decode/arithmetic_half.cpp b/src/video_core/shader/decode/arithmetic_half.cpp
index ee7d9a29d..a276aee44 100644
--- a/src/video_core/shader/decode/arithmetic_half.cpp
+++ b/src/video_core/shader/decode/arithmetic_half.cpp
@@ -19,22 +19,46 @@ u32 ShaderIR::DecodeArithmeticHalf(NodeBlock& bb, u32 pc) {
const Instruction instr = {program_code[pc]};
const auto opcode = OpCode::Decode(instr);
- if (opcode->get().GetId() == OpCode::Id::HADD2_C ||
- opcode->get().GetId() == OpCode::Id::HADD2_R) {
+ bool negate_a = false;
+ bool negate_b = false;
+ bool absolute_a = false;
+ bool absolute_b = false;
+
+ switch (opcode->get().GetId()) {
+ case OpCode::Id::HADD2_R:
if (instr.alu_half.ftz == 0) {
LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName());
}
+ negate_a = ((instr.value >> 43) & 1) != 0;
+ negate_b = ((instr.value >> 31) & 1) != 0;
+ absolute_a = ((instr.value >> 44) & 1) != 0;
+ absolute_b = ((instr.value >> 30) & 1) != 0;
+ break;
+ case OpCode::Id::HADD2_C:
+ if (instr.alu_half.ftz == 0) {
+ LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName());
+ }
+ negate_a = ((instr.value >> 43) & 1) != 0;
+ negate_b = ((instr.value >> 56) & 1) != 0;
+ absolute_a = ((instr.value >> 44) & 1) != 0;
+ absolute_b = ((instr.value >> 54) & 1) != 0;
+ break;
+ case OpCode::Id::HMUL2_R:
+ negate_a = ((instr.value >> 43) & 1) != 0;
+ absolute_a = ((instr.value >> 44) & 1) != 0;
+ absolute_b = ((instr.value >> 30) & 1) != 0;
+ break;
+ case OpCode::Id::HMUL2_C:
+ negate_b = ((instr.value >> 31) & 1) != 0;
+ absolute_a = ((instr.value >> 44) & 1) != 0;
+ absolute_b = ((instr.value >> 54) & 1) != 0;
+ break;
}
- const bool negate_a =
- opcode->get().GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0;
- const bool negate_b =
- opcode->get().GetId() != OpCode::Id::HMUL2_C && instr.alu_half.negate_b != 0;
-
Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.alu_half.type_a);
- op_a = GetOperandAbsNegHalf(op_a, instr.alu_half.abs_a, negate_a);
+ op_a = GetOperandAbsNegHalf(op_a, absolute_a, negate_a);
- auto [type_b, op_b] = [&]() -> std::tuple<HalfType, Node> {
+ auto [type_b, op_b] = [this, instr, opcode]() -> std::pair<HalfType, Node> {
switch (opcode->get().GetId()) {
case OpCode::Id::HADD2_C:
case OpCode::Id::HMUL2_C:
@@ -48,17 +72,16 @@ u32 ShaderIR::DecodeArithmeticHalf(NodeBlock& bb, u32 pc) {
}
}();
op_b = UnpackHalfFloat(op_b, type_b);
- // redeclaration to avoid a bug in clang with reusing local bindings in lambdas
- Node op_b_alt = GetOperandAbsNegHalf(op_b, instr.alu_half.abs_b, negate_b);
+ op_b = GetOperandAbsNegHalf(op_b, absolute_b, negate_b);
- Node value = [&]() {
+ Node value = [this, opcode, op_a, op_b = op_b] {
switch (opcode->get().GetId()) {
case OpCode::Id::HADD2_C:
case OpCode::Id::HADD2_R:
- return Operation(OperationCode::HAdd, PRECISE, op_a, op_b_alt);
+ return Operation(OperationCode::HAdd, PRECISE, op_a, op_b);
case OpCode::Id::HMUL2_C:
case OpCode::Id::HMUL2_R:
- return Operation(OperationCode::HMul, PRECISE, op_a, op_b_alt);
+ return Operation(OperationCode::HMul, PRECISE, op_a, op_b);
default:
UNIMPLEMENTED_MSG("Unhandled half float instruction: {}", opcode->get().GetName());
return Immediate(0);
diff --git a/src/video_core/shader/decode/arithmetic_integer.cpp b/src/video_core/shader/decode/arithmetic_integer.cpp
index 0f4c3103a..9af8c606d 100644
--- a/src/video_core/shader/decode/arithmetic_integer.cpp
+++ b/src/video_core/shader/decode/arithmetic_integer.cpp
@@ -249,8 +249,8 @@ u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) {
}
case OpCode::Id::LEA_IMM: {
const bool neg = instr.lea.imm.neg != 0;
- return {Immediate(static_cast<u32>(instr.lea.imm.entry_a)),
- GetOperandAbsNegInteger(GetRegister(instr.gpr8), false, neg, true),
+ return {GetOperandAbsNegInteger(GetRegister(instr.gpr8), false, neg, true),
+ Immediate(static_cast<u32>(instr.lea.imm.entry_a)),
Immediate(static_cast<u32>(instr.lea.imm.entry_b))};
}
case OpCode::Id::LEA_RZ: {
diff --git a/src/video_core/texture_cache/surface_base.h b/src/video_core/texture_cache/surface_base.h
index c5ab21f56..79e10ffbb 100644
--- a/src/video_core/texture_cache/surface_base.h
+++ b/src/video_core/texture_cache/surface_base.h
@@ -192,6 +192,22 @@ public:
index = index_;
}
+ void SetMemoryMarked(bool is_memory_marked_) {
+ is_memory_marked = is_memory_marked_;
+ }
+
+ bool IsMemoryMarked() const {
+ return is_memory_marked;
+ }
+
+ void SetSyncPending(bool is_sync_pending_) {
+ is_sync_pending = is_sync_pending_;
+ }
+
+ bool IsSyncPending() const {
+ return is_sync_pending;
+ }
+
void MarkAsPicked(bool is_picked_) {
is_picked = is_picked_;
}
@@ -303,6 +319,8 @@ private:
bool is_target{};
bool is_registered{};
bool is_picked{};
+ bool is_memory_marked{};
+ bool is_sync_pending{};
u32 index{NO_RT};
u64 modification_tick{};
};
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 69ca08fd1..cf6bd005a 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -6,6 +6,7 @@
#include <algorithm>
#include <array>
+#include <list>
#include <memory>
#include <mutex>
#include <set>
@@ -62,6 +63,30 @@ public:
}
}
+ void OnCPUWrite(VAddr addr, std::size_t size) {
+ std::lock_guard lock{mutex};
+
+ for (const auto& surface : GetSurfacesInRegion(addr, size)) {
+ if (surface->IsMemoryMarked()) {
+ UnmarkMemory(surface);
+ surface->SetSyncPending(true);
+ marked_for_unregister.emplace_back(surface);
+ }
+ }
+ }
+
+ void SyncGuestHost() {
+ std::lock_guard lock{mutex};
+
+ for (const auto& surface : marked_for_unregister) {
+ if (surface->IsRegistered()) {
+ surface->SetSyncPending(false);
+ Unregister(surface);
+ }
+ }
+ marked_for_unregister.clear();
+ }
+
/**
* Guarantees that rendertargets don't unregister themselves if the
* collide. Protection is currently only done on 3D slices.
@@ -85,10 +110,20 @@ public:
return a->GetModificationTick() < b->GetModificationTick();
});
for (const auto& surface : surfaces) {
+ mutex.unlock();
FlushSurface(surface);
+ mutex.lock();
}
}
+ bool MustFlushRegion(VAddr addr, std::size_t size) {
+ std::lock_guard lock{mutex};
+
+ const auto surfaces = GetSurfacesInRegion(addr, size);
+ return std::any_of(surfaces.cbegin(), surfaces.cend(),
+ [](const TSurface& surface) { return surface->IsModified(); });
+ }
+
TView GetTextureSurface(const Tegra::Texture::TICEntry& tic,
const VideoCommon::Shader::Sampler& entry) {
std::lock_guard lock{mutex};
@@ -206,8 +241,14 @@ public:
auto surface_view = GetSurface(gpu_addr, *cpu_addr,
SurfaceParams::CreateForFramebuffer(system, index), true);
- if (render_targets[index].target)
- render_targets[index].target->MarkAsRenderTarget(false, NO_RT);
+ if (render_targets[index].target) {
+ auto& surface = render_targets[index].target;
+ surface->MarkAsRenderTarget(false, NO_RT);
+ const auto& cr_params = surface->GetSurfaceParams();
+ if (!cr_params.is_tiled && Settings::values.use_asynchronous_gpu_emulation) {
+ AsyncFlushSurface(surface);
+ }
+ }
render_targets[index].target = surface_view.first;
render_targets[index].view = surface_view.second;
if (render_targets[index].target)
@@ -284,6 +325,34 @@ public:
return ++ticks;
}
+ void CommitAsyncFlushes() {
+ committed_flushes.push_back(uncommitted_flushes);
+ uncommitted_flushes.reset();
+ }
+
+ bool HasUncommittedFlushes() const {
+ return uncommitted_flushes != nullptr;
+ }
+
+ bool ShouldWaitAsyncFlushes() const {
+ return !committed_flushes.empty() && committed_flushes.front() != nullptr;
+ }
+
+ void PopAsyncFlushes() {
+ if (committed_flushes.empty()) {
+ return;
+ }
+ auto& flush_list = committed_flushes.front();
+ if (!flush_list) {
+ committed_flushes.pop_front();
+ return;
+ }
+ for (TSurface& surface : *flush_list) {
+ FlushSurface(surface);
+ }
+ committed_flushes.pop_front();
+ }
+
protected:
explicit TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
bool is_astc_supported)
@@ -345,9 +414,20 @@ protected:
surface->SetCpuAddr(*cpu_addr);
RegisterInnerCache(surface);
surface->MarkAsRegistered(true);
+ surface->SetMemoryMarked(true);
rasterizer.UpdatePagesCachedCount(*cpu_addr, size, 1);
}
+ void UnmarkMemory(TSurface surface) {
+ if (!surface->IsMemoryMarked()) {
+ return;
+ }
+ const std::size_t size = surface->GetSizeInBytes();
+ const VAddr cpu_addr = surface->GetCpuAddr();
+ rasterizer.UpdatePagesCachedCount(cpu_addr, size, -1);
+ surface->SetMemoryMarked(false);
+ }
+
void Unregister(TSurface surface) {
if (guard_render_targets && surface->IsProtected()) {
return;
@@ -355,9 +435,11 @@ protected:
if (!guard_render_targets && surface->IsRenderTarget()) {
ManageRenderTargetUnregister(surface);
}
- const std::size_t size = surface->GetSizeInBytes();
- const VAddr cpu_addr = surface->GetCpuAddr();
- rasterizer.UpdatePagesCachedCount(cpu_addr, size, -1);
+ UnmarkMemory(surface);
+ if (surface->IsSyncPending()) {
+ marked_for_unregister.remove(surface);
+ surface->SetSyncPending(false);
+ }
UnregisterInnerCache(surface);
surface->MarkAsRegistered(false);
ReserveSurface(surface->GetSurfaceParams(), surface);
@@ -417,7 +499,7 @@ private:
**/
RecycleStrategy PickStrategy(std::vector<TSurface>& overlaps, const SurfaceParams& params,
const GPUVAddr gpu_addr, const MatchTopologyResult untopological) {
- if (Settings::values.use_accurate_gpu_emulation) {
+ if (Settings::IsGPULevelExtreme()) {
return RecycleStrategy::Flush;
}
// 3D Textures decision
@@ -461,7 +543,7 @@ private:
}
switch (PickStrategy(overlaps, params, gpu_addr, untopological)) {
case RecycleStrategy::Ignore: {
- return InitializeSurface(gpu_addr, params, Settings::values.use_accurate_gpu_emulation);
+ return InitializeSurface(gpu_addr, params, Settings::IsGPULevelExtreme());
}
case RecycleStrategy::Flush: {
std::sort(overlaps.begin(), overlaps.end(),
@@ -509,7 +591,7 @@ private:
}
const auto& final_params = new_surface->GetSurfaceParams();
if (cr_params.type != final_params.type) {
- if (Settings::values.use_accurate_gpu_emulation) {
+ if (Settings::IsGPULevelExtreme()) {
BufferCopy(current_surface, new_surface);
}
} else {
@@ -598,7 +680,7 @@ private:
if (passed_tests == 0) {
return {};
// In Accurate GPU all tests should pass, else we recycle
- } else if (Settings::values.use_accurate_gpu_emulation && passed_tests != overlaps.size()) {
+ } else if (Settings::IsGPULevelExtreme() && passed_tests != overlaps.size()) {
return {};
}
for (const auto& surface : overlaps) {
@@ -668,7 +750,7 @@ private:
for (const auto& surface : overlaps) {
if (!surface->MatchTarget(params.target)) {
if (overlaps.size() == 1 && surface->GetCpuAddr() == cpu_addr) {
- if (Settings::values.use_accurate_gpu_emulation) {
+ if (Settings::IsGPULevelExtreme()) {
return std::nullopt;
}
Unregister(surface);
@@ -1106,6 +1188,13 @@ private:
TView view;
};
+ void AsyncFlushSurface(TSurface& surface) {
+ if (!uncommitted_flushes) {
+ uncommitted_flushes = std::make_shared<std::list<TSurface>>();
+ }
+ uncommitted_flushes->push_back(surface);
+ }
+
VideoCore::RasterizerInterface& rasterizer;
FormatLookupTable format_lookup_table;
@@ -1150,6 +1239,11 @@ private:
std::unordered_map<u32, TSurface> invalid_cache;
std::vector<u8> invalid_memory;
+ std::list<TSurface> marked_for_unregister;
+
+ std::shared_ptr<std::list<TSurface>> uncommitted_flushes{};
+ std::list<std::shared_ptr<std::list<TSurface>>> committed_flushes;
+
StagingCache staging_cache;
std::recursive_mutex mutex;
};
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 7df5f1452..fae8638ec 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -11,6 +11,7 @@
#include "video_core/textures/texture.h"
namespace Tegra::Texture {
+namespace {
/**
* This table represents the internal swizzle of a gob,
@@ -174,6 +175,8 @@ void SwizzledData(u8* const swizzled_data, u8* const unswizzled_data, const bool
}
}
+} // Anonymous namespace
+
void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
u32 out_bytes_per_pixel, u8* const swizzled_data, u8* const unswizzled_data,
bool unswizzle, u32 block_height, u32 block_depth, u32 width_spacing) {
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index e5eac3f3b..9f2d6d308 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -56,8 +56,7 @@ void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32
u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height,
u32 offset_x, u32 offset_y);
-void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 dst_y,
- const u32 block_height, const std::size_t copy_size, const u8* source_data,
- u8* swizzle_data);
+void SwizzleKepler(u32 width, u32 height, u32 dst_x, u32 dst_y, u32 block_height,
+ std::size_t copy_size, const u8* source_data, u8* swizzle_data);
} // namespace Tegra::Texture
diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp
index 6aff38735..4bc8ee726 100644
--- a/src/yuzu/applets/profile_select.cpp
+++ b/src/yuzu/applets/profile_select.cpp
@@ -17,6 +17,7 @@
#include "yuzu/applets/profile_select.h"
#include "yuzu/main.h"
+namespace {
QString FormatUserEntryText(const QString& username, Common::UUID uuid) {
return QtProfileSelectionDialog::tr(
"%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. "
@@ -41,6 +42,7 @@ QPixmap GetIcon(Common::UUID uuid) {
return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
+} // Anonymous namespace
QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
: QDialog(parent), profile_manager(std::make_unique<Service::Account::ProfileManager>()) {
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 7f6dfac84..196a3a116 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -639,8 +639,8 @@ void Config::ReadRendererValues() {
Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toInt();
Settings::values.use_disk_shader_cache =
ReadSetting(QStringLiteral("use_disk_shader_cache"), true).toBool();
- Settings::values.use_accurate_gpu_emulation =
- ReadSetting(QStringLiteral("use_accurate_gpu_emulation"), false).toBool();
+ const int gpu_accuracy_level = ReadSetting(QStringLiteral("gpu_accuracy"), 0).toInt();
+ Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level);
Settings::values.use_asynchronous_gpu_emulation =
ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool();
Settings::values.use_vsync = ReadSetting(QStringLiteral("use_vsync"), true).toBool();
@@ -1080,8 +1080,8 @@ void Config::SaveRendererValues() {
WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100);
WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache,
true);
- WriteSetting(QStringLiteral("use_accurate_gpu_emulation"),
- Settings::values.use_accurate_gpu_emulation, false);
+ WriteSetting(QStringLiteral("gpu_accuracy"), static_cast<int>(Settings::values.gpu_accuracy),
+ 0);
WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"),
Settings::values.use_asynchronous_gpu_emulation, false);
WriteSetting(QStringLiteral("use_vsync"), Settings::values.use_vsync, true);
diff --git a/src/yuzu/configuration/configure_filesystem.cpp b/src/yuzu/configuration/configure_filesystem.cpp
index 29f540eb7..835ee821c 100644
--- a/src/yuzu/configuration/configure_filesystem.cpp
+++ b/src/yuzu/configuration/configure_filesystem.cpp
@@ -138,7 +138,7 @@ void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit)
str = QFileDialog::getOpenFileName(this, caption, QFileInfo(edit->text()).dir().path(),
QStringLiteral("NX Gamecard;*.xci"));
} else {
- str = QFileDialog::getExistingDirectory(this, caption, edit->text());
+ str = QFileDialog::getExistingDirectory(this, caption, edit->text()) + QDir::separator();
}
if (str.isEmpty())
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index b9f429f84..0a3f47339 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -19,7 +19,7 @@ ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default;
void ConfigureGraphicsAdvanced::SetConfiguration() {
const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
- ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation);
+ ui->gpu_accuracy->setCurrentIndex(static_cast<int>(Settings::values.gpu_accuracy));
ui->use_vsync->setEnabled(runtime_lock);
ui->use_vsync->setChecked(Settings::values.use_vsync);
ui->force_30fps_mode->setEnabled(runtime_lock);
@@ -29,7 +29,8 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
}
void ConfigureGraphicsAdvanced::ApplyConfiguration() {
- Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked();
+ auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(ui->gpu_accuracy->currentIndex());
+ Settings::values.gpu_accuracy = gpu_accuracy;
Settings::values.use_vsync = ui->use_vsync->isChecked();
Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked();
Settings::values.max_anisotropy = ui->anisotropic_filtering_combobox->currentIndex();
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index 42eec278e..0c7b383e0 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -23,11 +23,34 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
- <widget class="QCheckBox" name="use_accurate_gpu_emulation">
- <property name="text">
- <string>Use accurate GPU emulation (slow)</string>
- </property>
- </widget>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLabel" name="label_gpu_accuracy">
+ <property name="text">
+ <string>Accuracy Level:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="gpu_accuracy">
+ <item>
+ <property name="text">
+ <string notr="true">Normal</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string notr="true">High</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string notr="true">Extreme(very slow)</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
</item>
<item>
<widget class="QCheckBox" name="use_vsync">
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 80341747f..d1ac354bf 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -388,8 +388,8 @@ void Config::ReadValues() {
static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100));
Settings::values.use_disk_shader_cache =
sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false);
- Settings::values.use_accurate_gpu_emulation =
- sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false);
+ const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0);
+ Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level);
Settings::values.use_asynchronous_gpu_emulation =
sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false);
Settings::values.use_vsync =
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 171d16fa0..60b1a62fa 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -146,9 +146,9 @@ frame_limit =
# 0 (default): Off, 1 : On
use_disk_shader_cache =
-# Whether to use accurate GPU emulation
-# 0 (default): Off (fast), 1 : On (slow)
-use_accurate_gpu_emulation =
+# Which gpu accuracy level to use
+# 0 (Normal), 1 (High), 2 (Extreme)
+gpu_accuracy =
# Whether to use asynchronous GPU emulation
# 0 : Off (slow), 1 (default): On (fast)
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 19584360c..e5e684206 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -181,9 +181,10 @@ void EmuWindow_SDL2::PollEvents() {
const u32 current_time = SDL_GetTicks();
if (current_time > last_time + 2000) {
const auto results = Core::System::GetInstance().GetAndResetPerfStats();
- const auto title = fmt::format(
- "yuzu {} | {}-{} | FPS: {:.0f} ({:.0%})", Common::g_build_fullname,
- Common::g_scm_branch, Common::g_scm_desc, results.game_fps, results.emulation_speed);
+ const auto title =
+ fmt::format("yuzu {} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname,
+ Common::g_scm_branch, Common::g_scm_desc, results.game_fps,
+ results.emulation_speed * 100.0);
SDL_SetWindowTitle(render_window, title.c_str());
last_time = current_time;
}
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
index f2990910e..cb8e68a39 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -29,6 +29,7 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen)
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
SDL_SysWMinfo wm;
+ SDL_VERSION(&wm.version);
if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) {
LOG_CRITICAL(Frontend, "Failed to get information from the window manager");
std::exit(EXIT_FAILURE);
@@ -70,7 +71,7 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen)
EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() = default;
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const {
- return nullptr;
+ return std::make_unique<DummyContext>();
}
void EmuWindow_SDL2_VK::Present() {
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
index b8021ebea..77a6ca72b 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
@@ -22,3 +22,5 @@ public:
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
};
+
+class DummyContext : public Core::Frontend::GraphicsContext {};
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp
index ee2591c8f..c0325cc3c 100644
--- a/src/yuzu_tester/config.cpp
+++ b/src/yuzu_tester/config.cpp
@@ -126,8 +126,8 @@ void Config::ReadValues() {
Settings::values.frame_limit = 100;
Settings::values.use_disk_shader_cache =
sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false);
- Settings::values.use_accurate_gpu_emulation =
- sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false);
+ const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0);
+ Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level);
Settings::values.use_asynchronous_gpu_emulation =
sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false);