Compare commits

..
Author SHA1 Message Date
ClaxtenandGitHub e91dd39cb0 Drop redundant PROT_NONE tracking in reserve paths for Linux (#122)
src: platform: Linux: Drop redundant PROT_NONE tracking in reserve paths

* Some UE4 games, such as The Pathless, reserve a 512 GiB virtual address range during libc startup.
  Tracking every 4 KiB page causes a long delay and is unnecessary since the range is already PROT_NONE,
  and untracked pages are treated as NoAccess.

Signed-off-by: Claxten <claxten10@gmail.com>
2026-07-30 00:00:21 +02:00
nmzik cc76827e63 Fix vertex buffer ranges crossing memory mappings 2026-07-29 21:05:24 +02:00
nmzik 832bc84100 fix(shader): stabilize scalar provenance phis in cyclic CFGs 2026-07-29 21:05:24 +02:00
nmzik 65a0f0baa7 NpManager ABI 2026-07-29 21:05:24 +02:00
nmzik b9ae2537ef renderer: broaden compatibility 2026-07-29 18:47:26 +02:00
nmzik 0b9edaa721 graphics: broaden storage image atomic compatibility 2026-07-29 18:47:24 +02:00
nmzik 8a244677d7 fix(renderer): resolve delayed GPU page faults through buffer and texture caches 2026-07-29 18:47:19 +02:00
nmzikandGitHub f6e01e5403 Optimize bulk memory invalidation (#124)
Build and Release KytyPS5 / Build KytyPS5 (Windows) (push) Canceled after 0s
Build and Release KytyPS5 / Build KytyPS5 (macOS) (push) Canceled after 0s
Build and Release KytyPS5 / Build KytyPS5 (Linux) (push) Canceled after 0s
Build and Release KytyPS5 / Release KytyPS5 (push) Canceled after 0s
* Per page -> per range search (optimization)
2026-07-29 05:09:36 +02:00
nmzikandGitHub 861729fc6c Optimize texture cache tracking (#123)
Optimize texture cache page tracking
2026-07-29 03:37:09 +02:00
nmzik 687ce025c6 KernelOpen: minor fix 2026-07-29 00:31:09 +02:00
nmzik a21d1aaa47 fix 2026-07-29 00:31:09 +02:00
nmzik ec11f31aa6 fix graphical bug (PPSA17221) 2026-07-29 00:31:08 +02:00
nmzik aeceaff028 new ABIs + one stub 2026-07-29 00:31:08 +02:00
nmzik e76f2d1af8 new ABIs 2026-07-29 00:31:08 +02:00
nmzik 64845e2294 new ABI, broaden support 2026-07-29 00:31:08 +02:00
nmzik 4b9030bd3d ABI fixes: libNet, libSaveData (rewrite the legacy Kyty implementation) 2026-07-29 00:30:24 +02:00
nmzik a6b61d7aa0 refactor(graphics): split renderer and recompiler into focused modules 2026-07-29 00:30:24 +02:00
129 changed files with 2024 additions and 1154 deletions
+27 -19
View File
@@ -158,16 +158,24 @@ file(GLOB kyty_emulator_src CONFIGURE_DEPENDS
graphics/host_gpu/*.h
graphics/host_gpu/renderer/*.cpp
graphics/host_gpu/renderer/*.h
graphics/host_gpu/renderer/cache/*.cpp
graphics/host_gpu/renderer/cache/*.h
graphics/host_gpu/renderer/image/*.cpp
graphics/host_gpu/renderer/image/*.h
graphics/host_gpu/renderer/pipeline/*.cpp
graphics/host_gpu/renderer/pipeline/*.h
graphics/shader/*.cpp
graphics/shader/*.h
graphics/shader/recompiler/*.cpp
graphics/shader/recompiler/*.h
graphics/shader/recompiler/shaderIR/*.cpp
graphics/shader/recompiler/shaderIR/*.h
graphics/shader/recompiler/spirvEmitter/*.cpp
graphics/shader/recompiler/spirvEmitter/*.h
graphics/host_gpu/objects/*.cpp
graphics/host_gpu/objects/*.h
graphics/shader/recompiler/cfg/*.cpp
graphics/shader/recompiler/cfg/*.h
graphics/shader/recompiler/decompiler/*.cpp
graphics/shader/recompiler/decompiler/*.h
graphics/shader/recompiler/emitter/*.cpp
graphics/shader/recompiler/emitter/*.h
graphics/shader/recompiler/ir/*.cpp
graphics/shader/recompiler/ir/*.h
graphics/presentation/*.cpp
graphics/presentation/*.h
graphics/presentation/window/*.cpp
@@ -311,8 +319,8 @@ add_kyty_full_emulator_test(shader_cfg_tests ../tests/shaderCfgTests.cpp)
add_executable(scalar_provenance_tests EXCLUDE_FROM_ALL
../tests/ScalarProvenanceTests.cpp
graphics/host_gpu/hostMemory.cpp
graphics/shader/recompiler/ScalarProvenance.cpp
graphics/shader/recompiler/SrtWalker.cpp
graphics/shader/recompiler/ir/ScalarProvenance.cpp
graphics/shader/recompiler/ir/SrtWalker.cpp
)
target_link_libraries(scalar_provenance_tests fmt::fmt)
target_include_directories(scalar_provenance_tests PRIVATE ${inc_headers})
@@ -344,9 +352,9 @@ add_executable(shader_stage_runtime_tests EXCLUDE_FROM_ALL
graphics/guest_gpu/gpu_format.cpp
graphics/host_gpu/hostMemory.cpp
graphics/shader/shaderStageRuntime.cpp
graphics/shader/recompiler/ResourceMaterialization.cpp
graphics/shader/recompiler/ScalarProvenance.cpp
graphics/shader/recompiler/SrtWalker.cpp
graphics/shader/recompiler/ir/ResourceMaterialization.cpp
graphics/shader/recompiler/ir/ScalarProvenance.cpp
graphics/shader/recompiler/ir/SrtWalker.cpp
)
target_link_libraries(shader_stage_runtime_tests fmt::fmt)
target_include_directories(shader_stage_runtime_tests PRIVATE ${inc_headers})
@@ -355,20 +363,20 @@ add_executable(resource_tracking_tests EXCLUDE_FROM_ALL
../tests/ResourceTrackingTests.cpp
graphics/guest_gpu/gpu_format.cpp
graphics/host_gpu/hostMemory.cpp
graphics/shader/recompiler/ScalarProvenance.cpp
graphics/shader/recompiler/SrtWalker.cpp
graphics/shader/recompiler/SrtPatcher.cpp
graphics/shader/recompiler/ResourceTracking.cpp
graphics/shader/recompiler/ResourceMaterialization.cpp
graphics/shader/recompiler/ShaderInfoCollection.cpp
graphics/shader/recompiler/BindingLayout.cpp
graphics/shader/recompiler/ir/ScalarProvenance.cpp
graphics/shader/recompiler/ir/SrtWalker.cpp
graphics/shader/recompiler/ir/SrtPatcher.cpp
graphics/shader/recompiler/ir/ResourceTracking.cpp
graphics/shader/recompiler/ir/ResourceMaterialization.cpp
graphics/shader/recompiler/ir/ShaderInfoCollection.cpp
graphics/shader/recompiler/ir/BindingLayout.cpp
)
target_link_libraries(resource_tracking_tests fmt::fmt)
target_include_directories(resource_tracking_tests PRIVATE ${inc_headers})
add_executable(resource_mutex_tests EXCLUDE_FROM_ALL
../tests/ResourceMutexTests.cpp
graphics/host_gpu/renderer/resourceMutex.cpp
graphics/host_gpu/renderer/cache/resourceMutex.cpp
)
target_link_libraries(resource_mutex_tests common)
target_include_directories(resource_mutex_tests PRIVATE ${inc_headers})
-10
View File
@@ -432,11 +432,6 @@ uint64_t SysVirtualReserveAligned(uint64_t address, uint64_t size, uint64_t alig
pthread_mutex_lock(&g_virtual_mutex);
record_alloc(ret_addr, size);
uintptr_t page_start = ret_addr >> 12u;
uintptr_t page_end = (ret_addr + size - 1) >> 12u;
for (uintptr_t page = page_start; page <= page_end; page++) {
(*g_protects)[page] = PROT_NONE;
}
pthread_mutex_unlock(&g_virtual_mutex);
return ret_addr;
@@ -470,11 +465,6 @@ bool SysVirtualReserveFixed(uint64_t address, uint64_t size) {
if (ptr != MAP_FAILED) {
pthread_mutex_lock(&g_virtual_mutex);
record_alloc(ret_addr, size);
uintptr_t page_start = ret_addr >> 12u;
uintptr_t page_end = (ret_addr + size - 1) >> 12u;
for (uintptr_t page = page_start; page <= page_end; page++) {
(*g_protects)[page] = PROT_NONE;
}
pthread_mutex_unlock(&g_virtual_mutex);
return true;
@@ -65,10 +65,23 @@ constexpr uint32_t GcrKnownMask = GcrGl2MetadataInvalidate | GcrGl0V
GcrGl2Writeback | GcrOrder012 | GcrOrder210;
constexpr uint32_t RegisterSelectorMask = 0x70000000u;
uint32_t NormalizeRegisterOffset(uint32_t raw_offset) {
return (raw_offset & ~RegisterSelectorMask);
constexpr uint32_t NormalizeRegisterOffset(uint32_t raw_offset) {
return raw_offset & ~RegisterSelectorMask;
}
// Indirect Cx descriptors retain their selector. Selector 1 offsets 0..31 address the
// SPI_PS_INPUT_CNTL register bank; ordinary context-register offsets remain unchanged.
constexpr uint32_t DecodeIndirectCxRegisterOffset(uint32_t raw_offset) {
const auto offset = NormalizeRegisterOffset(raw_offset);
return (raw_offset & RegisterSelectorMask) == Pm4::CX_PS_SHADER_USAGE_BASE && offset < 32u
? Pm4::SPI_PS_INPUT_CNTL_0 + offset
: offset;
}
static_assert(DecodeIndirectCxRegisterOffset(Pm4::CX_PS_SHADER_USAGE_BASE + 2u) ==
Pm4::SPI_PS_INPUT_CNTL_0 + 2u);
static_assert(DecodeIndirectCxRegisterOffset(Pm4::DB_Z_INFO) == Pm4::DB_Z_INFO);
bool ReleaseMemGcrNeedsBarrier(uint32_t eop_event_type, uint32_t gcr_cntl) {
return eop_event_type != 0x28u ||
(gcr_cntl & (GcrGl2MetadataInvalidate | GcrGl0VectorInvalidate | GcrGl1Invalidate |
@@ -2321,14 +2334,18 @@ KYTY_CP_OP_PARSER(CpOpIndirectCxRegs) {
EXIT("indirect CX registers have null address, num_regs = %" PRIu32 "\n", indirect_num_dw);
}
for (uint32_t i = 0; i < indirect_num_dw; i++, indirect_buffer += 2) {
auto cmd_offset = indirect_buffer[0];
auto value = indirect_buffer[1];
// Keep the encoded offset for packet control values, and use the decoded offset only
// for register dispatch.
auto raw_cmd_offset = indirect_buffer[0];
auto cmd_offset = DecodeIndirectCxRegisterOffset(raw_cmd_offset);
auto value = indirect_buffer[1];
if (HwCtxTrySetFakeRegister(cmd_offset, value)) {
continue;
}
if (cmd_offset == 0xffffffffu) {
// The sentinel is an encoded descriptor value and must be checked before normalization.
if (raw_cmd_offset == 0xffffffffu) {
static bool logged = false;
if (!logged) {
LOGF("\t temporary: skipping indirect CX sentinel pair offset = 0xffffffff, value "
+3
View File
@@ -385,6 +385,9 @@ constexpr uint32_t SPI_SHADER_POS_FORMAT = 0x1C3;
constexpr uint32_t SPI_SHADER_Z_FORMAT = 0x1C4;
constexpr uint32_t SPI_SHADER_COL_FORMAT = 0x1C5;
// Indirect Cx descriptor selector for the 32-entry PS input-control register bank.
constexpr uint32_t CX_PS_SHADER_USAGE_BASE = 0x10000000u;
constexpr uint32_t CB_BLEND0_CONTROL = 0x1E0;
constexpr uint32_t CB_BLEND0_CONTROL_COLOR_SRCBLEND_SHIFT = 0;
constexpr uint32_t CB_BLEND0_CONTROL_COLOR_SRCBLEND_MASK = 0x1F;
+43 -2
View File
@@ -38,10 +38,51 @@ public:
bool downloaded) noexcept;
[[nodiscard]] bool InvalidateRegion(uint64_t vaddr, uint64_t size,
PageFaultPhase phase) noexcept;
template <typename Flush>
void InvalidateRegion(uint64_t vaddr, uint64_t size, Flush&& on_flush) {
static_assert(std::is_invocable_v<Flush&>);
CheckNotInUploadCallback();
ValidateRange(vaddr, size);
const auto update_cpu_state = [this, vaddr, size] {
std::lock_guard access(m_access_mutex);
std::vector<RegionManager*> managers;
Iterate<false>(vaddr, size, [&](RegionManager* manager, uint64_t, uint64_t) {
managers.push_back(manager);
});
std::vector<std::unique_lock<TrackingSpinLock>> locks;
locks.reserve(managers.size());
for (auto* manager: managers) {
locks.emplace_back(manager->lock);
}
const bool gpu_modified = Iterate<false>(
vaddr, size, [](RegionManager* manager, uint64_t offset, uint64_t bytes) {
return manager->IsModified<DirtySource::Gpu>(offset, bytes);
});
if (gpu_modified) {
return true;
}
Iterate<false>(vaddr, size,
[](RegionManager* manager, uint64_t offset, uint64_t bytes) {
const auto changed = manager->ChangeState<DirtySource::Cpu, true>(
manager->GetCpuAddr() + offset, bytes);
manager->ApplyProtection(changed, false);
});
return false;
};
if (!update_cpu_state()) {
return;
}
std::forward<Flush>(on_flush)();
if (update_cpu_state()) {
EXIT("memory invalidation retained GPU-owned pages\n");
}
}
[[nodiscard]] bool InvalidateVirtualGpuWrite(PageFaultAccess access, uint64_t vaddr,
uint64_t size, PageFaultPhase phase) noexcept;
void ValidateGpuDirtyPages(const RangeSet& dirty, uint64_t vaddr, uint64_t size,
const char* operation) const noexcept;
void ValidateGpuDirtyPages(const RangeSet& dirty, uint64_t vaddr, uint64_t size,
const char* operation) const noexcept;
void ValidateGpuDirtyOwnership(const RangeSet& dirty, uint64_t vaddr, uint64_t size,
const char* operation);
+311 -165
View File
@@ -2,6 +2,7 @@
#include "graphics/host_gpu/regionDefinitions.h"
#include <algorithm>
#include <array>
#include <atomic>
#include <cstdarg>
@@ -71,8 +72,8 @@ static int PageProtToPosix(uint32_t protection) {
// Query the current protection of the page containing vaddr via the Mach VM map and
// collapse it to the tracker's read/write tags (execute is irrelevant to write tracking).
static uint32_t MachQueryPageProt(uint64_t vaddr) {
auto region_addr = static_cast<mach_vm_address_t>(vaddr);
mach_vm_size_t region_size = 0;
auto region_addr = static_cast<mach_vm_address_t>(vaddr);
mach_vm_size_t region_size = 0;
vm_region_basic_info_data_64_t info {};
mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64;
mach_port_t object_name = MACH_PORT_NULL;
@@ -164,22 +165,27 @@ int ToHostProtection(uint32_t protection) {
}
}
struct HostMapping {
uint64_t end = 0;
uint32_t protection = UNKNOWN_PROTECTION;
};
// Async-signal-safe lookup in the address-ordered /proc/self/maps.
uint32_t QueryHostProtection(uint64_t vaddr) noexcept {
HostMapping QueryHostMapping(uint64_t vaddr) noexcept {
int fd = ::open("/proc/self/maps", O_RDONLY | O_CLOEXEC); // NOLINT
if (fd < 0) {
return UNKNOWN_PROTECTION;
return {};
}
enum class Field { Start, End, Perms, Rest };
uint32_t result = UNKNOWN_PROTECTION;
auto field = Field::Start;
uint64_t start = 0;
uint64_t end = 0;
char perms[4] = {};
uint32_t perms_len = 0;
bool line_valid = true;
HostMapping result {};
auto field = Field::Start;
uint64_t start = 0;
uint64_t end = 0;
char perms[4] = {};
uint32_t perms_len = 0;
bool line_valid = true;
char buffer[8192];
@@ -248,10 +254,11 @@ uint32_t QueryHostProtection(uint64_t vaddr) noexcept {
if (vaddr < start) {
done = true;
} else if (vaddr < end && perms_len >= 2) {
result = perms[1] == 'w' ? READ_WRITE_PROTECTION
: perms[0] == 'r' ? READ_ONLY_PROTECTION
: NO_ACCESS_PROTECTION;
done = true;
result.end = end;
result.protection = perms[1] == 'w' ? READ_WRITE_PROTECTION
: perms[0] == 'r' ? READ_ONLY_PROTECTION
: NO_ACCESS_PROTECTION;
done = true;
} else {
field = Field::Rest;
}
@@ -266,6 +273,10 @@ uint32_t QueryHostProtection(uint64_t vaddr) noexcept {
::close(fd);
return result;
}
uint32_t QueryHostProtection(uint64_t vaddr) noexcept {
return QueryHostMapping(vaddr).protection;
}
#endif
class SpinGuard final {
@@ -301,28 +312,48 @@ uint64_t PageEnd(uint64_t vaddr, uint64_t size) {
struct PageManager::Impl {
struct PageState {
std::atomic_flag lock = ATOMIC_FLAG_INIT;
uint32_t mappings = 0;
uint32_t gpu_read_mappings = 0;
uint32_t gpu_write_mappings = 0;
uint32_t write_watchers = 0;
uint32_t access_watchers = 0;
uint32_t original_protection = 0;
uint32_t backing_writer = 0;
std::atomic_flag lock = ATOMIC_FLAG_INIT;
uint32_t mappings = 0;
uint32_t gpu_read_mappings = 0;
uint32_t gpu_write_mappings = 0;
uint32_t write_watchers = 0;
uint32_t access_watchers = 0;
uint32_t original_protection = 0;
uint32_t backing_writer = 0;
#if defined(__linux__)
// Shadow the protection applied through Protect().
uint32_t current_protection = UNKNOWN_PROTECTION;
#endif
bool resolving = false;
bool resolving_read_write = false;
bool late_read_pending = false;
bool late_write_pending = false;
bool resolving = false;
bool resolving_read_write = false;
bool late_read_pending = false;
bool late_write_pending = false;
};
struct Region {
std::array<PageState, REGION_PAGES> pages;
};
class PageRangeGuard final {
public:
explicit PageRangeGuard(std::span<PageState*> pages): m_pages(pages) {
for (auto* page: m_pages) {
while (page->lock.test_and_set(std::memory_order_acquire)) {
std::atomic_signal_fence(std::memory_order_seq_cst);
}
}
}
~PageRangeGuard() {
for (auto it = m_pages.rbegin(); it != m_pages.rend(); ++it) {
(*it)->lock.clear(std::memory_order_release);
}
}
KYTY_CLASS_NO_COPY(PageRangeGuard);
private:
std::span<PageState*> m_pages;
};
Impl(PageFaultHandler handler, void* context): fault_handler(handler), fault_context(context) {
if (fault_handler == nullptr) {
Fatal("null fault handler");
@@ -337,8 +368,7 @@ struct PageManager::Impl {
#elif defined(__APPLE__)
// Under Rosetta the host page size is 4 KB, matching TRACKER_PAGE_SIZE.
if (static_cast<uint64_t>(getpagesize()) != PAGE_SIZE) {
Fatal("unsupported host page size 0x%08" PRIx32,
static_cast<uint32_t>(getpagesize()));
Fatal("unsupported host page size 0x%08" PRIx32, static_cast<uint32_t>(getpagesize()));
}
#else
const auto host_page_size = ::sysconf(_SC_PAGESIZE);
@@ -405,42 +435,57 @@ struct PageManager::Impl {
if (old_protection == NO_ACCESS_PROTECTION && new_protection != NO_ACCESS_PROTECTION) {
page.late_read_pending = true;
}
if ((old_protection == NO_ACCESS_PROTECTION ||
old_protection == READ_ONLY_PROTECTION) &&
if ((old_protection == NO_ACCESS_PROTECTION || old_protection == READ_ONLY_PROTECTION) &&
new_protection == READ_WRITE_PROTECTION) {
page.late_write_pending = true;
}
}
static uint32_t QueryProtection([[maybe_unused]] PageState& page, uint64_t vaddr) {
static void ValidateInitialProtection(std::span<PageState*> pages, uint64_t vaddr) {
const auto end = vaddr + pages.size() * PAGE_SIZE;
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
MEMORY_BASIC_INFORMATION info {};
if (VirtualQuery(reinterpret_cast<const void*>(static_cast<uintptr_t>(vaddr)), &info,
sizeof(info)) == 0 ||
info.State != MEM_COMMIT || info.Protect != PAGE_READWRITE) {
Fatal("basic path requires PAGE_READWRITE at 0x%016" PRIx64 " (state=0x%08" PRIx32
", protection=0x%08" PRIx32 ")",
vaddr, static_cast<uint32_t>(info.State), static_cast<uint32_t>(info.Protect));
for (auto address = vaddr; address < end;) {
MEMORY_BASIC_INFORMATION info {};
if (VirtualQuery(reinterpret_cast<const void*>(static_cast<uintptr_t>(address)), &info,
sizeof(info)) == 0 ||
info.State != MEM_COMMIT || info.Protect != PAGE_READWRITE) {
Fatal("basic path requires PAGE_READWRITE at 0x%016" PRIx64 " (state=0x%08" PRIx32
", protection=0x%08" PRIx32 ")",
address, static_cast<uint32_t>(info.State),
static_cast<uint32_t>(info.Protect));
}
const auto region_end = reinterpret_cast<uint64_t>(info.BaseAddress) + info.RegionSize;
if (region_end <= address) {
Fatal("VirtualQuery returned an invalid region at 0x%016" PRIx64, address);
}
address = std::min(end, region_end);
}
return info.Protect;
#elif defined(__APPLE__)
const uint32_t protection = MachQueryPageProt(vaddr);
if (protection != PAGE_READWRITE) {
Fatal("basic path requires PAGE_READWRITE at 0x%016" PRIx64 " (protection=0x%08" PRIx32
")",
vaddr, protection);
for (auto address = vaddr; address < end; address += PAGE_SIZE) {
const uint32_t protection = MachQueryPageProt(address);
if (protection != PAGE_READWRITE) {
Fatal("basic path requires PAGE_READWRITE at 0x%016" PRIx64
" (protection=0x%08" PRIx32 ")",
address, protection);
}
}
return protection;
#else
const auto host_protection = QueryHostProtection(vaddr);
if (host_protection != READ_WRITE_PROTECTION) {
Fatal("basic path requires a read/write mapping at 0x%016" PRIx64
" (protection=0x%08" PRIx32 ")",
vaddr, host_protection);
for (auto address = vaddr; address < end;) {
const auto mapping = QueryHostMapping(address);
if (mapping.protection != READ_WRITE_PROTECTION || mapping.end <= address) {
Fatal("basic path requires a read/write mapping at 0x%016" PRIx64
" (protection=0x%08" PRIx32 ")",
address, mapping.protection);
}
address = std::min(end, mapping.end);
}
for (auto* page: pages) {
page->current_protection = READ_WRITE_PROTECTION;
}
page.current_protection = host_protection;
return host_protection;
#endif
for (auto* page: pages) {
page->original_protection = READ_WRITE_PROTECTION;
}
}
static bool AllowsAccess([[maybe_unused]] const PageState& page, uint64_t vaddr,
@@ -470,7 +515,8 @@ struct PageManager::Impl {
const auto permitted = [](uint32_t protection, PageFaultAccess wanted) {
switch (wanted) {
case PageFaultAccess::Read:
return protection == READ_ONLY_PROTECTION || protection == READ_WRITE_PROTECTION;
return protection == READ_ONLY_PROTECTION ||
protection == READ_WRITE_PROTECTION;
case PageFaultAccess::Write: return protection == READ_WRITE_PROTECTION;
default: return false;
}
@@ -483,26 +529,84 @@ struct PageManager::Impl {
#endif
}
static void Protect([[maybe_unused]] PageState& page, uint64_t vaddr, uint32_t protection,
uint32_t expected_old, bool fault_path) noexcept {
static void ProtectRange(std::span<PageState*> pages, uint64_t vaddr, uint32_t protection,
std::span<const uint32_t> expected_old, bool fault_path) noexcept {
const auto size = pages.size() * PAGE_SIZE;
if (pages.size() != expected_old.size()) {
FailFast("protection range state size mismatch");
}
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
DWORD old_protection = 0;
if (VirtualProtect(reinterpret_cast<void*>(static_cast<uintptr_t>(vaddr)), PAGE_SIZE,
protection, &old_protection) == 0 ||
old_protection != expected_old) {
if (fault_path) {
FailFast("VirtualProtect fault transition did not match expected protection");
struct HostRange {
uint64_t begin = 0;
uint64_t end = 0;
};
std::vector<HostRange> host_ranges;
const auto end = vaddr + size;
for (auto address = vaddr; address < end;) {
MEMORY_BASIC_INFORMATION info {};
if (VirtualQuery(reinterpret_cast<const void*>(static_cast<uintptr_t>(address)), &info,
sizeof(info)) == 0 ||
info.State != MEM_COMMIT) {
if (fault_path) {
FailFast("VirtualProtect fault transition did not match expected protection");
}
Fatal("invalid protection transition at 0x%016" PRIx64 ", state=0x%08" PRIx32
", new=0x%08" PRIx32,
address, static_cast<uint32_t>(info.State), protection);
}
const auto region_end = reinterpret_cast<uint64_t>(info.BaseAddress) + info.RegionSize;
const auto query_end = std::min(end, region_end);
if (query_end <= address) {
if (fault_path) {
FailFast("VirtualQuery returned an invalid fault transition region");
}
Fatal("VirtualQuery returned an invalid region at 0x%016" PRIx64, address);
}
const auto first_page = static_cast<size_t>((address - vaddr) / PAGE_SIZE);
const auto last_page =
static_cast<size_t>((query_end - vaddr + PAGE_SIZE - 1) / PAGE_SIZE);
for (auto page = first_page; page < last_page; page++) {
if (info.Protect != expected_old[page]) {
if (fault_path) {
FailFast(
"VirtualProtect fault transition did not match expected protection");
}
Fatal("invalid protection transition at 0x%016" PRIx64 ", actual=0x%08" PRIx32
", expected=0x%08" PRIx32 ", new=0x%08" PRIx32,
vaddr + page * PAGE_SIZE, static_cast<uint32_t>(info.Protect),
expected_old[page], protection);
}
}
const auto allocation = reinterpret_cast<uint64_t>(info.AllocationBase);
if (host_ranges.empty() || allocation != host_ranges.back().begin) {
host_ranges.push_back({allocation, query_end});
} else {
host_ranges.back().end = query_end;
}
address = query_end;
}
for (auto range: host_ranges) {
range.begin = std::max(range.begin, vaddr);
DWORD old_protection = 0;
const auto first_page = static_cast<size_t>((range.begin - vaddr) / PAGE_SIZE);
if (VirtualProtect(reinterpret_cast<void*>(static_cast<uintptr_t>(range.begin)),
range.end - range.begin, protection, &old_protection) == 0 ||
old_protection != expected_old[first_page]) {
if (fault_path) {
FailFast("VirtualProtect fault transition did not match expected protection");
}
Fatal("invalid protection transition at 0x%016" PRIx64 ", old=0x%08" PRIx32
", expected=0x%08" PRIx32 ", new=0x%08" PRIx32,
range.begin, static_cast<uint32_t>(old_protection), expected_old[first_page],
protection);
}
Fatal("invalid protection transition at 0x%016" PRIx64 ", old=0x%08" PRIx32
", expected=0x%08" PRIx32 ", new=0x%08" PRIx32,
vaddr, static_cast<uint32_t>(old_protection), expected_old, protection);
}
#elif defined(__APPLE__)
// mprotect cannot report the previous protection, so the expected_old comparison
// is dropped; the tracker is the sole mutator of these pages and drives the
// transition from its own shadow state.
(void)expected_old;
if (mprotect(reinterpret_cast<void*>(static_cast<uintptr_t>(vaddr)), PAGE_SIZE,
if (mprotect(reinterpret_cast<void*>(static_cast<uintptr_t>(vaddr)), size,
PageProtToPosix(protection)) != 0) {
if (fault_path) {
FailFast("mprotect fault transition failed");
@@ -510,16 +614,18 @@ struct PageManager::Impl {
Fatal("mprotect failed at 0x%016" PRIx64 ", new=0x%08" PRIx32, vaddr, protection);
}
#else
if (page.current_protection != UNKNOWN_PROTECTION &&
page.current_protection != expected_old) {
if (fault_path) {
FailFast("mprotect fault transition did not match expected protection");
for (size_t i = 0; i < pages.size(); i++) {
const auto actual = pages[i]->current_protection;
if (actual != UNKNOWN_PROTECTION && actual != expected_old[i]) {
if (fault_path) {
FailFast("mprotect fault transition did not match expected protection");
}
Fatal("invalid protection transition at 0x%016" PRIx64 ", old=0x%08" PRIx32
", expected=0x%08" PRIx32 ", new=0x%08" PRIx32,
vaddr + i * PAGE_SIZE, actual, expected_old[i], protection);
}
Fatal("invalid protection transition at 0x%016" PRIx64 ", old=0x%08" PRIx32
", expected=0x%08" PRIx32 ", new=0x%08" PRIx32,
vaddr, page.current_protection, expected_old, protection);
}
if (::mprotect(reinterpret_cast<void*>(static_cast<uintptr_t>(vaddr)), PAGE_SIZE,
if (::mprotect(reinterpret_cast<void*>(static_cast<uintptr_t>(vaddr)), size,
ToHostProtection(protection)) != 0) {
if (fault_path) {
FailFast("mprotect failed on the fault path");
@@ -527,10 +633,19 @@ struct PageManager::Impl {
Fatal("mprotect failed at 0x%016" PRIx64 ", new=0x%08" PRIx32 " (%s)", vaddr,
protection, std::strerror(errno));
}
page.current_protection = protection;
for (auto* page: pages) {
page->current_protection = protection;
}
#endif
}
static void Protect(PageState& page, uint64_t vaddr, uint32_t protection, uint32_t expected_old,
bool fault_path) noexcept {
PageState* pages[] = {&page};
uint32_t expected[] = {expected_old};
ProtectRange(pages, vaddr, protection, expected, fault_path);
}
std::unique_ptr<std::atomic<Region*>[]> regions;
std::vector<std::unique_ptr<Region>> region_storage;
std::mutex region_mutex;
@@ -584,26 +699,6 @@ bool PageManager::IsMapped(uint64_t vaddr, uint64_t size) const noexcept {
return true;
}
bool PageManager::HasAnyMapping(uint64_t vaddr, uint64_t size) const noexcept {
if (g_in_fault_resolution || vaddr == 0 || size == 0 || vaddr >= ADDRESS_SIZE ||
size > ADDRESS_SIZE - vaddr) {
return false;
}
const auto end = PageEnd(vaddr, size);
for (auto page_vaddr = PageStart(vaddr); page_vaddr < end; page_vaddr += PAGE_SIZE) {
auto* region = m_impl->FindRegion(page_vaddr);
if (region == nullptr) {
continue;
}
auto& page = m_impl->GetPage(*region, page_vaddr);
SpinGuard lock(page.lock);
if (page.mappings != 0) {
return true;
}
}
return false;
}
bool PageManager::HasGpuAccess(uint64_t vaddr, uint64_t size, GpuAccess access) const noexcept {
if (access != GpuAccess::Read && access != GpuAccess::Write && access != GpuAccess::ReadWrite) {
FailFast("HasGpuAccess received an invalid GPU access mode");
@@ -634,65 +729,134 @@ void PageManager::UpdatePageWatchers(bool track, uint64_t vaddr, uint64_t size,
if (mode != PageWatchMode::Write && mode != PageWatchMode::ReadWrite) {
Fatal("invalid watcher mode");
}
const auto end = PageEnd(vaddr, size);
for (auto page_vaddr = PageStart(vaddr); page_vaddr < end; page_vaddr += PAGE_SIZE) {
auto* region =
track ? m_impl->GetOrCreateRegion(page_vaddr) : m_impl->FindRegion(page_vaddr);
const auto begin = PageStart(vaddr);
const auto end = PageEnd(vaddr, size);
for (auto chunk_begin = begin; chunk_begin < end;) {
const auto chunk_end = std::min(end, (chunk_begin / REGION_SIZE + 1) * REGION_SIZE);
auto* region =
track ? m_impl->GetOrCreateRegion(chunk_begin) : m_impl->FindRegion(chunk_begin);
if (region == nullptr) {
Fatal("untracking unknown page 0x%016" PRIx64, page_vaddr);
Fatal("untracking unknown page 0x%016" PRIx64, chunk_begin);
}
auto& page = m_impl->GetPage(*region, page_vaddr);
SpinGuard lock(page.lock);
if (page.resolving && track) {
FailFast("new page watcher raced active fault resolution");
const auto page_count = static_cast<size_t>((chunk_end - chunk_begin) / PAGE_SIZE);
std::vector<Impl::PageState*> pages;
pages.reserve(page_count);
for (auto address = chunk_begin; address < chunk_end; address += PAGE_SIZE) {
pages.push_back(&m_impl->GetPage(*region, address));
}
if (page.mappings == 0) {
Fatal("watching unmapped page 0x%016" PRIx64, page_vaddr);
Impl::PageRangeGuard lock(pages);
std::vector<uint8_t> first_watchers(page_count);
for (size_t i = 0; i < page_count; i++) {
auto& page = *pages[i];
const auto address = chunk_begin + i * PAGE_SIZE;
if (page.resolving && track) {
FailFast("new page watcher raced active fault resolution");
}
if (page.mappings == 0) {
Fatal("watching unmapped page 0x%016" PRIx64, address);
}
auto& watchers =
(mode == PageWatchMode::ReadWrite ? page.access_watchers : page.write_watchers);
if (track) {
if (watchers == std::numeric_limits<uint32_t>::max()) {
Fatal("watcher overflow at 0x%016" PRIx64, address);
}
first_watchers[i] = page.write_watchers == 0 && page.access_watchers == 0;
} else {
if (watchers == 0) {
Fatal("watcher underflow at 0x%016" PRIx64, address);
}
if (page.backing_writer != 0 && page.backing_writer != CurrentThread()) {
Fatal("backing write ownership changed at 0x%016" PRIx64, address);
}
}
}
auto& watchers =
(mode == PageWatchMode::ReadWrite ? page.access_watchers : page.write_watchers);
if (track) {
if (watchers == std::numeric_limits<uint32_t>::max()) {
Fatal("watcher overflow at 0x%016" PRIx64, page_vaddr);
}
const bool first_watcher = page.write_watchers == 0 && page.access_watchers == 0;
if (first_watcher) {
page.original_protection = Impl::QueryProtection(page, page_vaddr);
}
const auto old_protection = Impl::WatcherProtection(page);
watchers++;
const auto new_protection = Impl::WatcherProtection(page);
if (new_protection != old_protection) {
Impl::Protect(page, page_vaddr, new_protection, old_protection, false);
}
switch (new_protection) {
case NO_ACCESS_PROTECTION:
page.late_read_pending = false;
page.late_write_pending = false;
break;
case READ_ONLY_PROTECTION: page.late_write_pending = false; break;
default: break;
}
} else {
if (watchers == 0) {
Fatal("watcher underflow at 0x%016" PRIx64, page_vaddr);
}
if (page.backing_writer != 0 && page.backing_writer != CurrentThread()) {
Fatal("backing write ownership changed at 0x%016" PRIx64, page_vaddr);
}
const auto old_protection = Impl::WatcherProtection(page);
watchers--;
const auto new_protection = Impl::WatcherProtection(page);
if (page.backing_writer == 0 && new_protection != old_protection) {
Impl::Protect(page, page_vaddr, new_protection, old_protection, false);
}
if (page.backing_writer == 0) {
Impl::PublishDelayedFaults(page, old_protection, new_protection);
}
if (page.backing_writer == 0 && page.write_watchers == 0 && page.access_watchers == 0) {
page.original_protection = 0;
for (size_t first = 0; first < page_count;) {
while (first < page_count && first_watchers[first] == 0) {
first++;
}
auto last = first;
while (last < page_count && first_watchers[last] != 0) {
last++;
}
if (first != last) {
Impl::ValidateInitialProtection(std::span {pages}.subspan(first, last - first),
chunk_begin + first * PAGE_SIZE);
}
first = last;
}
}
std::vector<uint32_t> old_protections(page_count);
std::vector<uint32_t> new_protections(page_count);
std::vector<uint8_t> transitions(page_count);
for (size_t i = 0; i < page_count; i++) {
auto& page = *pages[i];
auto& watchers =
(mode == PageWatchMode::ReadWrite ? page.access_watchers : page.write_watchers);
const auto old_protection = Impl::WatcherProtection(page);
if (track) {
watchers++;
} else {
watchers--;
}
const auto new_protection = Impl::WatcherProtection(page);
old_protections[i] = old_protection;
new_protections[i] = new_protection;
if (new_protection != old_protection && (track || page.backing_writer == 0)) {
transitions[i] = 1;
}
}
for (size_t first = 0; first < page_count;) {
while (first < page_count && transitions[first] == 0) {
first++;
}
if (first == page_count) {
break;
}
const auto protection = new_protections[first];
auto current = first + 1;
auto last = current;
for (; current < page_count && new_protections[current] == protection; current++) {
if (old_protections[current] != new_protections[current] &&
transitions[current] == 0) {
break;
}
if (transitions[current] != 0) {
last = current + 1;
}
}
Impl::ProtectRange(std::span {pages}.subspan(first, last - first),
chunk_begin + first * PAGE_SIZE, protection,
std::span {old_protections}.subspan(first, last - first), false);
first = current;
}
for (size_t i = 0; i < page_count; i++) {
auto& page = *pages[i];
const auto protection = new_protections[i];
if (track) {
switch (protection) {
case NO_ACCESS_PROTECTION:
page.late_read_pending = false;
page.late_write_pending = false;
break;
case READ_ONLY_PROTECTION: page.late_write_pending = false; break;
default: break;
}
} else if (page.backing_writer == 0) {
Impl::PublishDelayedFaults(page, old_protections[i], protection);
if (page.write_watchers == 0 && page.access_watchers == 0) {
page.original_protection = 0;
}
}
}
chunk_begin = chunk_end;
}
}
@@ -966,22 +1130,4 @@ bool PageManager::HandleFault(PageFaultAccess access, uint64_t fault_vaddr) noex
return true;
}
bool PageManager::HandleWriteRange(uint64_t vaddr, uint64_t size) noexcept {
if (g_in_fault_resolution || vaddr == 0 || size == 0 || vaddr >= ADDRESS_SIZE ||
size > ADDRESS_SIZE - vaddr) {
return false;
}
const auto end = PageEnd(vaddr, size);
for (auto page_vaddr = PageStart(vaddr); page_vaddr < end; page_vaddr += PAGE_SIZE) {
if (!IsMapped(page_vaddr, 1)) {
continue;
}
const auto fault_vaddr = std::max(page_vaddr, vaddr);
if (!HandleFault(PageFaultAccess::Write, fault_vaddr)) {
return false;
}
}
return true;
}
} // namespace Libs::Graphics
+1 -3
View File
@@ -41,7 +41,6 @@ public:
[[nodiscard]] uint64_t GetPageSize() const;
[[nodiscard]] bool IsTracked(uint64_t vaddr) const noexcept;
[[nodiscard]] bool IsMapped(uint64_t vaddr, uint64_t size) const noexcept;
[[nodiscard]] bool HasAnyMapping(uint64_t vaddr, uint64_t size) const noexcept;
[[nodiscard]] bool HasGpuAccess(uint64_t vaddr, uint64_t size, GpuAccess access) const noexcept;
void UpdatePageWatchers(bool track, uint64_t vaddr, uint64_t size,
@@ -50,9 +49,8 @@ public:
void OnGpuUnmap(uint64_t vaddr, uint64_t size, GpuAccess access = GpuAccess::ReadWrite);
[[nodiscard]] bool HandleFault(PageFaultAccess access, uint64_t fault_vaddr) noexcept;
[[nodiscard]] bool HandleWriteRange(uint64_t vaddr, uint64_t size) noexcept;
[[nodiscard]] std::vector<std::unique_ptr<BackingWrite>>
ReserveBackingWrites(std::span<const RangeSet::Range> ranges);
ReserveBackingWrites(std::span<const RangeSet::Range> ranges);
private:
void BeginBackingWrite(uint64_t vaddr, uint64_t size) noexcept;
+10
View File
@@ -59,6 +59,16 @@ public:
return result;
}
[[nodiscard]] bool Contains(uint64_t address, uint64_t size) const {
const auto end = End(address, size);
auto it = m_ranges.upper_bound(address);
if (it == m_ranges.begin()) {
return false;
}
--it;
return it->first <= address && it->second >= end;
}
template <typename Func>
void ForEachIntersection(uint64_t address, uint64_t size, Func&& func) const {
const auto end = End(address, size);
@@ -1,13 +1,13 @@
#include "graphics/host_gpu/renderer/bufferCache.h"
#include "graphics/host_gpu/renderer/cache/bufferCache.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/profiler.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/renderer/cache/resourceMutex.h"
#include "graphics/host_gpu/renderer/cache/textureCache.h"
#include "graphics/host_gpu/renderer/commandScheduler.h"
#include "graphics/host_gpu/renderer/render.h"
#include "graphics/host_gpu/renderer/resourceMutex.h"
#include "graphics/host_gpu/renderer/textureCache.h"
#include "kernel/memory.h"
#include <algorithm>
@@ -183,9 +183,8 @@ BufferCache::RecordDownloads(std::span<const DownloadCopy> copies) {
return {};
}
auto& download = m_download_buffer;
const auto [mapped, base_offset] =
download.Map(reservation_size, DOWNLOAD_ALIGNMENT);
auto& download = m_download_buffer;
const auto [mapped, base_offset] = download.Map(reservation_size, DOWNLOAD_ALIGNMENT);
if (mapped == nullptr) {
EXIT("BufferCache: download batch could not reserve the shared stream\n");
}
@@ -215,40 +214,38 @@ void BufferCache::PublishDownloads(std::span<const DownloadRange> downloads) {
}
}
void BufferCache::QueueGarbageDownload(std::span<const DownloadCopy> copies,
RetiredBuffer retire) {
void BufferCache::QueueGarbageDownload(std::span<const DownloadCopy> copies, RetiredBuffer retire) {
if (copies.empty()) {
return;
}
auto downloads = RecordDownloads(copies);
const auto tick = m_scheduler.CurrentTick();
auto downloads = RecordDownloads(copies);
const auto tick = m_scheduler.CurrentTick();
BeginBackingPublication(retire.address, retire.size, tick);
m_scheduler.DeferOperation(
[this, downloads = std::move(downloads), retire = std::move(retire), tick]() mutable {
PublishDownloads(downloads);
{
FaultSafeCacheLock lock(this, m_mutex);
if (m_memory_tracker.IsRegionGpuModified(retire.address, retire.size)) {
m_memory_tracker.ForEachDownloadRange<true>(
retire.address, retire.size,
[&](uint64_t address, uint64_t size) noexcept {
m_memory_tracker.ValidateGpuDirtyPages(
m_gpu_modified_ranges, address, size,
"asynchronous garbage retirement");
},
[](uint64_t, uint64_t) noexcept {});
}
for (const auto& range: downloads) {
m_gpu_modified_ranges.Subtract(range.address, range.size);
}
if (m_memory_tracker.IsRegionGpuModified(retire.address, retire.size) ||
!m_gpu_modified_ranges.Intersections(retire.address, retire.size).empty()) {
EXIT("BufferCache: asynchronous garbage collection retained GPU ownership\n");
}
m_memory_tracker.UntrackMemory(retire.address, retire.size);
}
CompleteBackingPublication(retire.address, retire.size, tick);
});
m_scheduler.DeferOperation([this, downloads = std::move(downloads), retire = std::move(retire),
tick]() mutable {
PublishDownloads(downloads);
{
FaultSafeCacheLock lock(this, m_mutex);
if (m_memory_tracker.IsRegionGpuModified(retire.address, retire.size)) {
m_memory_tracker.ForEachDownloadRange<true>(
retire.address, retire.size,
[&](uint64_t address, uint64_t size) noexcept {
m_memory_tracker.ValidateGpuDirtyPages(m_gpu_modified_ranges, address, size,
"asynchronous garbage retirement");
},
[](uint64_t, uint64_t) noexcept {});
}
for (const auto& range: downloads) {
m_gpu_modified_ranges.Subtract(range.address, range.size);
}
if (m_memory_tracker.IsRegionGpuModified(retire.address, retire.size) ||
!m_gpu_modified_ranges.Intersections(retire.address, retire.size).empty()) {
EXIT("BufferCache: asynchronous garbage collection retained GPU ownership\n");
}
m_memory_tracker.UntrackMemory(retire.address, retire.size);
}
CompleteBackingPublication(retire.address, retire.size, tick);
});
}
BufferCache::BufferCache(GraphicContext& graphics, CommandScheduler& scheduler,
@@ -301,14 +298,13 @@ BufferCache::~BufferCache() {
bool BufferCache::SynchronizeBacking(uint64_t vaddr, uint64_t size) {
bool waited = false;
for (;;) {
uint64_t tick = 0;
uint64_t tick = 0;
const auto page_begin = vaddr & ~(TRACKER_PAGE_SIZE - 1);
const auto page_end =
(vaddr + size + TRACKER_PAGE_SIZE - 1) & ~(TRACKER_PAGE_SIZE - 1);
const auto page_end = (vaddr + size + TRACKER_PAGE_SIZE - 1) & ~(TRACKER_PAGE_SIZE - 1);
CacheRange affected {.address = page_begin, .size = page_end - page_begin};
{
FaultSafeCacheLock lock(this, m_mutex);
bool changed = true;
bool changed = true;
while (changed) {
changed = false;
for (const auto& [address, cached]: m_buffers) {
@@ -384,6 +380,73 @@ BufferBinding BufferCache::UploadTransient(const void* data, uint64_t size, uint
return {owner, owner->Handle(), 0};
}
void BufferCache::InvalidateMemory(uint64_t vaddr, uint64_t size) {
if (vaddr == 0 || size == 0 || vaddr >= TRACKER_ADDRESS_SIZE ||
size > TRACKER_ADDRESS_SIZE - vaddr) {
EXIT("BufferCache: invalid memory-invalidation range\n");
}
(void)SynchronizeBacking(vaddr, size);
if (!HasPageOverlap(vaddr, size)) {
return;
}
m_memory_tracker.InvalidateRegion(vaddr, size,
[this, vaddr, size] { ReadMemory(vaddr, size); });
}
void BufferCache::ReadMemory(uint64_t vaddr, uint64_t size) {
(void)SynchronizeBacking(vaddr, size);
std::vector<DownloadCopy> copies;
{
FaultSafeCacheLock lock(this, m_mutex);
m_memory_tracker.ForEachDownloadRange<false>(
vaddr, size,
[&](uint64_t address, uint64_t bytes) noexcept {
m_memory_tracker.ValidateGpuDirtyPages(m_gpu_modified_ranges, address, bytes,
"memory invalidation");
},
[&](uint64_t address, uint64_t bytes) noexcept {
for (const auto range: m_gpu_modified_ranges.Intersections(address, bytes)) {
for (uint64_t copied = 0; copied < range.size;) {
const auto copy_address = range.address + copied;
auto owner = m_buffers.upper_bound(copy_address);
if (owner == m_buffers.begin()) {
EXIT("BufferCache: invalidation readback has no buffer owner\n");
}
auto& cached = *std::prev(owner)->second;
if (!cached.buffer->IsInBounds(copy_address, 1)) {
EXIT(
"BufferCache: invalidation readback is outside its buffer owner\n");
}
const auto copy_size = std::min(range.size - copied,
cached.vaddr + cached.size - copy_address);
copies.push_back({cached.buffer, cached.buffer->Offset(copy_address),
copy_address, copy_size});
copied += copy_size;
}
}
});
}
if (copies.empty()) {
return;
}
auto downloads = RecordDownloads(copies);
m_scheduler.FinishCurrent();
PublishDownloads(downloads);
{
FaultSafeCacheLock lock(this, m_mutex);
m_memory_tracker.ForEachDownloadRange<true>(
vaddr, size,
[&](uint64_t address, uint64_t bytes) noexcept {
m_memory_tracker.ValidateGpuDirtyPages(m_gpu_modified_ranges, address, bytes,
"memory invalidation completion");
},
[](uint64_t, uint64_t) noexcept {});
for (const auto& range: downloads) {
m_gpu_modified_ranges.Subtract(range.address, range.size);
}
}
}
bool BufferCache::InvalidateMemory(PageFaultAccess access, uint64_t vaddr, uint64_t size,
PageFaultPhase phase) noexcept {
const auto page = vaddr & ~(TRACKER_PAGE_SIZE - 1);
@@ -544,8 +607,8 @@ void BufferCache::UnmapMemory(uint64_t vaddr, uint64_t size) {
m_memory_tracker.ForEachDownloadRange<true>(
begin, bytes,
[&](uint64_t address, uint64_t download_size) noexcept {
m_memory_tracker.ValidateGpuDirtyPages(
m_gpu_modified_ranges, address, download_size, "unmap retirement");
m_memory_tracker.ValidateGpuDirtyPages(m_gpu_modified_ranges, address,
download_size, "unmap retirement");
},
[](uint64_t, uint64_t) noexcept {});
}
@@ -722,12 +785,11 @@ ImageBufferSource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t siz
{
FaultSafeCacheLock lock(this, m_mutex);
const bool cpu_modified = m_memory_tracker.IsRegionCpuModified(vaddr, size);
const bool gpu_modified = m_memory_tracker.IsRegionGpuModified(vaddr, size);
const auto dirty = m_gpu_modified_ranges.Intersections(vaddr, size);
const bool invalidated =
!m_image_invalidated_ranges.Intersections(vaddr, size).empty();
const bool requested_gpu_owned = !dirty.empty();
const bool cpu_modified = m_memory_tracker.IsRegionCpuModified(vaddr, size);
const bool gpu_modified = m_memory_tracker.IsRegionGpuModified(vaddr, size);
const auto dirty = m_gpu_modified_ranges.Intersections(vaddr, size);
const bool invalidated = !m_image_invalidated_ranges.Intersections(vaddr, size).empty();
const bool requested_gpu_owned = !dirty.empty();
m_memory_tracker.ValidateGpuDirtyOwnership(m_gpu_modified_ranges, vaddr, size,
"image source");
@@ -807,8 +869,8 @@ ImageBufferSource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t siz
}
FaultSafeCacheLock lock(this, m_mutex);
const auto dirty = m_gpu_modified_ranges.Intersections(vaddr, size);
const bool invalidated = !m_image_invalidated_ranges.Intersections(vaddr, size).empty();
const auto dirty = m_gpu_modified_ranges.Intersections(vaddr, size);
const bool invalidated = !m_image_invalidated_ranges.Intersections(vaddr, size).empty();
const bool requested_gpu_owned = !dirty.empty();
auto owner = find_owner();
if (requested_gpu_owned && owner == m_buffers.end()) {
@@ -831,9 +893,8 @@ ImageBufferSource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t siz
[&]() noexcept {
for (const auto& [address, upload_size]: uploads) {
cached.buffer->CopyFrom(
m_scheduler.Current(), m_staging_buffer,
stage_offset + address - stage_address, cached.buffer->Offset(address),
upload_size, vk::AccessFlagBits::eHostWrite);
m_scheduler.Current(), m_staging_buffer, stage_offset + address - stage_address,
cached.buffer->Offset(address), upload_size, vk::AccessFlagBits::eHostWrite);
}
});
DiscardGpuDirtyBytesLocked(vaddr, size, "staged image source transfer");
@@ -917,9 +978,8 @@ std::pair<std::shared_ptr<Buffer>, uint64_t> BufferCache::ObtainBufferForImageWr
[&]() noexcept {
for (const auto& [address, upload_size]: uploads) {
cached.buffer->CopyFrom(
m_scheduler.Current(), m_staging_buffer,
stage_offset + address - stage_address, cached.buffer->Offset(address),
upload_size, vk::AccessFlagBits::eHostWrite);
m_scheduler.Current(), m_staging_buffer, stage_offset + address - stage_address,
cached.buffer->Offset(address), upload_size, vk::AccessFlagBits::eHostWrite);
}
});
return {cached.buffer, cached.buffer->Offset(vaddr)};
@@ -946,12 +1006,12 @@ void BufferCache::FillBuffer(uint64_t vaddr, uint64_t size, uint32_t value, bool
const auto region = m_texture_cache.QueryRegion(vaddr, size);
if (!HasGpuDirtyBytes(vaddr, size) && !region.gpu_image_bytes) {
if (region.image_bytes) {
m_texture_cache.PrepareHostWrite(vaddr, size);
m_texture_cache.InvalidateMemory(vaddr, size);
}
std::array<uint32_t, 4096> values;
values.fill(value);
const std::span<const uint8_t> bytes {
reinterpret_cast<const uint8_t*>(values.data()), sizeof(values)};
const std::span<const uint8_t> bytes {reinterpret_cast<const uint8_t*>(values.data()),
sizeof(values)};
for (uint64_t offset = 0; offset < size;) {
const auto chunk = std::min<uint64_t>(size - offset, bytes.size());
WriteHostMemory(vaddr + offset, bytes.first(chunk));
@@ -992,7 +1052,7 @@ void BufferCache::CopyBuffer(uint64_t dst_vaddr, uint64_t src_vaddr, uint64_t si
if (src_memory) {
(void)SynchronizeBacking(src_vaddr, size);
}
const auto src_region =
const auto src_region =
src_memory ? m_texture_cache.QueryRegion(src_vaddr, size) : TextureCache::RegionInfo {};
const auto dst_region =
dst_memory ? m_texture_cache.QueryRegion(dst_vaddr, size) : TextureCache::RegionInfo {};
@@ -1004,7 +1064,7 @@ void BufferCache::CopyBuffer(uint64_t dst_vaddr, uint64_t src_vaddr, uint64_t si
!HasGpuDirtyBytes(dst_vaddr, size) && !src_region.gpu_image_bytes &&
!dst_region.gpu_image_bytes) {
if (dst_region.image_bytes) {
m_texture_cache.PrepareHostWrite(dst_vaddr, size);
m_texture_cache.InvalidateMemory(dst_vaddr, size);
}
std::array<uint8_t, 64 * 1024> bytes;
for (uint64_t offset = 0; offset < size;) {
@@ -1037,10 +1097,10 @@ void BufferCache::CopyBuffer(uint64_t dst_vaddr, uint64_t src_vaddr, uint64_t si
EXIT("BufferCache: resolved Vulkan copy ranges overlap\n");
}
auto& source = src.owner != nullptr ? *std::static_pointer_cast<Buffer>(src.owner)
: src_gds ? m_gds_buffer
: m_stream_buffer;
auto& destination = dst.owner != nullptr ? *std::static_pointer_cast<Buffer>(dst.owner)
: m_gds_buffer;
: src_gds ? m_gds_buffer
: m_stream_buffer;
auto& destination =
dst.owner != nullptr ? *std::static_pointer_cast<Buffer>(dst.owner) : m_gds_buffer;
if (source.Handle() != src.buffer || destination.Handle() != dst.buffer) {
EXIT("BufferCache: resolved copy owner does not match its Vulkan handle\n");
}
@@ -1107,7 +1167,7 @@ void BufferCache::BeginBackingPublication(uint64_t vaddr, uint64_t size, uint64_
void BufferCache::CompleteBackingPublication(uint64_t vaddr, uint64_t size, uint64_t tick) {
std::lock_guard lock(m_publication_mutex);
const auto publication =
const auto publication =
std::ranges::find_if(m_pending_backing_publications, [&](const auto& pending) {
return pending.address == vaddr && pending.size == size && pending.tick == tick;
});
@@ -1170,7 +1230,7 @@ void BufferCache::RunGarbageCollector() {
const uint64_t age = std::min<uint64_t>(aggressive ? 80 : 160, tick);
const size_t limit = aggressive ? 64 : 32;
std::vector<RetiredBuffer> retires;
std::vector<RetiredBuffer> retires;
std::vector<std::pair<RetiredBuffer, std::vector<DownloadCopy>>> dirty_retires;
{
FaultSafeCacheLock lock(this, m_mutex);
@@ -1190,8 +1250,8 @@ void BufferCache::RunGarbageCollector() {
}
for (const auto address: candidates) {
auto& cached = *m_buffers.at(address);
m_memory_tracker.ValidateGpuDirtyOwnership(
m_gpu_modified_ranges, cached.vaddr, cached.size, "garbage collection");
m_memory_tracker.ValidateGpuDirtyOwnership(m_gpu_modified_ranges, cached.vaddr,
cached.size, "garbage collection");
retires.push_back({address, cached.size, cached.buffer});
// GC runs immediately before submission. Preserve every source referenced by commands
// already recorded in the active batch.
@@ -1205,13 +1265,13 @@ void BufferCache::RunGarbageCollector() {
m_memory_tracker.ForEachDownloadRange<false>(
retire.address, retire.size,
[&](uint64_t address, uint64_t size) noexcept {
m_memory_tracker.ValidateGpuDirtyPages(
m_gpu_modified_ranges, address, size, "garbage collection");
m_memory_tracker.ValidateGpuDirtyPages(m_gpu_modified_ranges, address, size,
"garbage collection");
},
[&](uint64_t address, uint64_t size) noexcept {
for (const auto range: m_gpu_modified_ranges.Intersections(address, size)) {
copies.push_back({retire.owner, range.address - retire.address, range.address,
range.size});
copies.push_back({retire.owner, range.address - retire.address,
range.address, range.size});
}
});
}
@@ -6,7 +6,7 @@
#include "common/threads.h"
#include "graphics/host_gpu/memoryTracker.h"
#include "graphics/host_gpu/rangeSet.h"
#include "graphics/host_gpu/renderer/streamBuffer.h"
#include "graphics/host_gpu/renderer/cache/streamBuffer.h"
#include <map>
#include <memory>
@@ -49,6 +49,8 @@ public:
[[nodiscard]] bool InvalidateMemory(PageFaultAccess access, uint64_t vaddr, uint64_t size,
PageFaultPhase phase) noexcept;
void InvalidateMemory(uint64_t vaddr, uint64_t size);
void ReadMemory(uint64_t vaddr, uint64_t size);
void UnmapMemory(uint64_t vaddr, uint64_t size);
[[nodiscard]] BufferBinding ObtainBuffer(CommandBuffer& command, uint64_t vaddr, uint64_t size,
bool is_written = false, bool is_read = true,
@@ -71,8 +73,8 @@ public:
[[nodiscard]] bool IsRegionCpuModified(uint64_t vaddr, uint64_t size);
[[nodiscard]] bool IsRegionGpuModified(uint64_t vaddr, uint64_t size);
void InvalidateImageAliases(uint64_t vaddr, uint64_t size);
void BeginBackingPublication(uint64_t vaddr, uint64_t size, uint64_t tick);
void CompleteBackingPublication(uint64_t vaddr, uint64_t size, uint64_t tick);
void BeginBackingPublication(uint64_t vaddr, uint64_t size, uint64_t tick);
void CompleteBackingPublication(uint64_t vaddr, uint64_t size, uint64_t tick);
[[nodiscard]] bool SynchronizeBacking(uint64_t vaddr, uint64_t size);
void PublishImageBuffer(uint64_t vaddr, uint64_t size);
void ValidateGpuAccess(uint64_t vaddr, uint64_t size, bool is_read, bool is_written) const;
@@ -91,23 +93,21 @@ private:
struct RetiredBuffer;
struct FaultReadback;
struct PendingBackingPublication;
static constexpr uint64_t DOWNLOAD_ALIGNMENT = 64;
[[nodiscard]] static uint64_t AlignDown(uint64_t value) noexcept;
[[nodiscard]] static uint64_t AlignUp(uint64_t value);
static constexpr uint64_t DOWNLOAD_ALIGNMENT = 64;
[[nodiscard]] static uint64_t AlignDown(uint64_t value) noexcept;
[[nodiscard]] static uint64_t AlignUp(uint64_t value);
[[nodiscard]] static constexpr uint64_t AlignDownload(uint64_t size) noexcept {
return (size + DOWNLOAD_ALIGNMENT - 1) & ~(DOWNLOAD_ALIGNMENT - 1);
}
[[nodiscard]] static bool PageOverlaps(uint64_t left, uint64_t left_size, uint64_t right,
uint64_t right_size) noexcept;
[[nodiscard]] static std::pair<uint64_t, uint64_t>
DownloadEnvelope(const DownloadCopy& copy);
[[nodiscard]] static bool ResolveOverlap(CacheRange& merged, CacheRange candidate) noexcept;
uint64_t right_size) noexcept;
[[nodiscard]] static std::pair<uint64_t, uint64_t> DownloadEnvelope(const DownloadCopy& copy);
[[nodiscard]] static bool ResolveOverlap(CacheRange& merged, CacheRange candidate) noexcept;
void Upload(CommandBuffer& command, Buffer& destination, uint64_t destination_offset,
const void* source, uint64_t size);
[[nodiscard]] CachedBuffer& GetOrCreateBuffer(CommandBuffer& command, uint64_t vaddr,
uint64_t size);
[[nodiscard]] std::vector<DownloadRange>
RecordDownloads(std::span<const DownloadCopy> copies);
[[nodiscard]] std::vector<DownloadRange> RecordDownloads(std::span<const DownloadCopy> copies);
void PublishDownloads(std::span<const DownloadRange> downloads);
void QueueGarbageDownload(std::span<const DownloadCopy> copies, RetiredBuffer retire);
void RefreshInvalidatedRanges(CommandBuffer& command, CachedBuffer& cached, uint64_t vaddr,
@@ -1,4 +1,4 @@
#include "graphics/host_gpu/renderer/gpuResourceManager.h"
#include "graphics/host_gpu/renderer/cache/gpuResourceManager.h"
#include "common/assert.h"
#include "graphics/guest_gpu/command_processor/commandProcessor.h"
@@ -36,7 +36,8 @@ bool GpuResourceManager::InvalidateMemory(PageFaultAccess access, uint64_t vaddr
}
bool GpuResourceManager::HandleFault(PageFaultAccess access, uint64_t fault_vaddr) noexcept {
if (!m_page_manager.IsMapped(fault_vaddr, 1)) {
constexpr uint64_t fault_size = 8;
if (!IsMapped(fault_vaddr, fault_size)) {
return false;
}
if (CommandScheduler::InDeferredOperation()) {
@@ -47,10 +48,15 @@ bool GpuResourceManager::HandleFault(PageFaultAccess access, uint64_t fault_vadd
bool handled = false;
const auto resolve = [this, access, fault_vaddr, &handled](CommandProcessor& cp) {
cp.BeginReadbackTransaction();
(void)m_buffer_cache.SynchronizeBacking(fault_vaddr, 1);
{
ResourceMutex::FaultScope fault(m_resource_mutex);
handled = m_page_manager.HandleFault(access, fault_vaddr);
if (access == PageFaultAccess::Write) {
m_buffer_cache.InvalidateMemory(fault_vaddr, fault_size);
m_texture_cache.InvalidateMemory(fault_vaddr, fault_size);
} else {
m_buffer_cache.ReadMemory(fault_vaddr, fault_size);
}
handled = true;
}
cp.EndReadbackTransaction();
};
@@ -68,47 +74,52 @@ bool GpuResourceManager::HandleFault(PageFaultAccess access, uint64_t fault_vadd
return handled;
}
void GpuResourceManager::PrepareHostWrite(uint64_t vaddr, uint64_t size) {
if (!m_page_manager.HasAnyMapping(vaddr, size)) {
return;
bool GpuResourceManager::InvalidateMemory(uint64_t vaddr, uint64_t size) {
if (!IsMapped(vaddr, size)) {
return false;
}
if (CommandScheduler::InDeferredOperation()) {
EXIT("unsupported host write from an asynchronous GPU completion, addr=0x%016" PRIx64
" size=0x%016" PRIx64 "\n",
EXIT("unsupported memory invalidation from an asynchronous GPU completion, "
"addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
vaddr, size);
}
const auto handle_range = [this, vaddr, size] {
if (!m_page_manager.HandleWriteRange(vaddr, size)) {
EXIT("failed to prepare host write, addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
vaddr, size);
}
};
const auto resolve = [this, &handle_range](CommandProcessor& cp) {
const auto resolve = [this, vaddr, size](CommandProcessor& cp) {
cp.BeginReadbackTransaction();
{
ResourceMutex::FaultScope fault(m_resource_mutex);
handle_range();
m_buffer_cache.InvalidateMemory(vaddr, size);
m_texture_cache.InvalidateMemory(vaddr, size);
}
cp.EndReadbackTransaction();
};
if (auto* cp = Gpu::CurrentCommandProcessor(); cp != nullptr) {
resolve(*cp);
return;
return true;
}
if (m_resource_mutex.IsOwnedByCurrentThread()) {
EXIT("unsupported host write from a pre-owned resource transaction, addr=0x%016" PRIx64
" size=0x%016" PRIx64 "\n",
EXIT("unsupported memory invalidation from a pre-owned resource transaction, "
"addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
vaddr, size);
}
EXIT_IF(m_gpu == nullptr);
m_gpu->SendCommandSyncWithProcessor(resolve);
return true;
}
bool GpuResourceManager::IsMapped(uint64_t vaddr, uint64_t size) const noexcept {
return m_page_manager.IsMapped(vaddr, size);
if (vaddr == 0 || size == 0 || vaddr >= TRACKER_ADDRESS_SIZE ||
size > TRACKER_ADDRESS_SIZE - vaddr) {
return false;
}
std::shared_lock lock(m_mapped_ranges_mutex);
return m_mapped_ranges.Contains(vaddr, size);
}
void GpuResourceManager::MapMemory(uint64_t vaddr, uint64_t size, GpuAccess access) {
{
std::lock_guard lock(m_mapped_ranges_mutex);
m_mapped_ranges.Add(vaddr, size);
}
m_page_manager.OnGpuMap(vaddr, size, access);
}
@@ -120,6 +131,8 @@ void GpuResourceManager::UnmapMemory(uint64_t vaddr, uint64_t size, GpuAccess ac
m_texture_cache.UnmapMemory(vaddr, size);
m_buffer_cache.UnmapMemory(vaddr, size);
m_page_manager.OnGpuUnmap(vaddr, size, access);
std::lock_guard lock(m_mapped_ranges_mutex);
m_mapped_ranges.Subtract(vaddr, size);
};
if (m_gpu == nullptr) {
if (m_resource_mutex.IsOwnedByCurrentThread()) {
@@ -4,11 +4,12 @@
#include "common/abi.h"
#include "common/common.h"
#include "graphics/host_gpu/pageManager.h"
#include "graphics/host_gpu/renderer/bufferCache.h"
#include "graphics/host_gpu/renderer/resourceMutex.h"
#include "graphics/host_gpu/renderer/textureCache.h"
#include "graphics/host_gpu/renderer/cache/bufferCache.h"
#include "graphics/host_gpu/renderer/cache/resourceMutex.h"
#include "graphics/host_gpu/renderer/cache/textureCache.h"
#include <cstdint>
#include <shared_mutex>
namespace Libs::Graphics {
@@ -26,7 +27,7 @@ public:
void SetGpu(Gpu* gpu) noexcept { m_gpu = gpu; }
[[nodiscard]] bool HandleFault(PageFaultAccess access, uint64_t fault_vaddr) noexcept;
void PrepareHostWrite(uint64_t vaddr, uint64_t size);
[[nodiscard]] bool InvalidateMemory(uint64_t vaddr, uint64_t size);
[[nodiscard]] bool IsMapped(uint64_t vaddr, uint64_t size) const noexcept;
void MapMemory(uint64_t vaddr, uint64_t size, GpuAccess access);
void UnmapMemory(uint64_t vaddr, uint64_t size, GpuAccess access);
@@ -38,11 +39,13 @@ private:
[[nodiscard]] bool InvalidateMemory(PageFaultAccess access, uint64_t vaddr, uint64_t size,
PageFaultPhase phase) noexcept;
PageManager m_page_manager;
ResourceMutex m_resource_mutex;
BufferCache m_buffer_cache;
TextureCache m_texture_cache;
Gpu* m_gpu = nullptr;
PageManager m_page_manager;
ResourceMutex m_resource_mutex;
BufferCache m_buffer_cache;
TextureCache m_texture_cache;
mutable std::shared_mutex m_mapped_ranges_mutex;
RangeSet m_mapped_ranges;
Gpu* m_gpu = nullptr;
};
} // namespace Libs::Graphics
@@ -1,4 +1,4 @@
#include "graphics/host_gpu/renderer/resourceMutex.h"
#include "graphics/host_gpu/renderer/cache/resourceMutex.h"
#include "common/assert.h"
@@ -1,4 +1,4 @@
#include "graphics/host_gpu/renderer/samplerCache.h"
#include "graphics/host_gpu/renderer/cache/samplerCache.h"
#include "common/assert.h"
#include "common/logging/log.h"
@@ -1,4 +1,4 @@
#include "graphics/host_gpu/renderer/streamBuffer.h"
#include "graphics/host_gpu/renderer/cache/streamBuffer.h"
#include "common/assert.h"
#include "common/profiler.h"
@@ -1,4 +1,4 @@
#include "graphics/host_gpu/renderer/textureCache.h"
#include "graphics/host_gpu/renderer/cache/textureCache.h"
#include "common/assert.h"
#include "common/emulatorConfig.h"
@@ -7,13 +7,13 @@
#include "graphics/guest_gpu/gpu_format.h"
#include "graphics/guest_gpu/tile.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/objects/textureCommon.h"
#include "graphics/host_gpu/renderer/bufferCache.h"
#include "graphics/host_gpu/renderer/cache/bufferCache.h"
#include "graphics/host_gpu/renderer/cache/resourceMutex.h"
#include "graphics/host_gpu/renderer/commandScheduler.h"
#include "graphics/host_gpu/renderer/imageView.h"
#include "graphics/host_gpu/renderer/image/imageView.h"
#include "graphics/host_gpu/renderer/image/textureCommon.h"
#include "graphics/host_gpu/renderer/image/tiler.h"
#include "graphics/host_gpu/renderer/render.h"
#include "graphics/host_gpu/renderer/resourceMutex.h"
#include "graphics/host_gpu/renderer/tiler.h"
#include "kernel/memory.h"
#include <algorithm>
@@ -58,8 +58,8 @@ private:
TextureCache::TextureCache(GraphicContext& graphics, CommandScheduler& scheduler,
PageManager& page_manager, BufferCache& buffer_cache,
ResourceMutex& resource_mutex)
: m_graphics(graphics), m_scheduler(scheduler),
m_memory_tracker(page_manager, PageWatchMode::Write), m_blit_helper(graphics, scheduler),
: m_graphics(graphics), m_scheduler(scheduler), m_page_manager(page_manager),
m_blit_helper(graphics, scheduler),
m_tiler(std::make_unique<TileManager>(graphics, scheduler,
buffer_cache.GetUtilityBuffer(MemoryUsage::Stream))),
m_buffer_cache(buffer_cache), m_resource_mutex(resource_mutex),
@@ -80,7 +80,7 @@ TextureCache::TextureCache(GraphicContext& graphics, CommandScheduler& scheduler
TextureCache::~TextureCache() {
for (uint32_t index = 0; index < m_slots.size(); index++) {
if (m_slots[index].image != nullptr && m_slots[index].image->registered) {
UnregisterImage({index, m_slots[index].generation}, false);
UnregisterImage({index, m_slots[index].generation});
}
m_slots[index].image.reset();
}
@@ -117,8 +117,7 @@ bool TextureCache::SafeToDownload(const Image& image) {
return false;
}
const auto range = image.info.data;
return !m_buffer_cache.HasGpuDirtyBytes(range.address, range.size) &&
!m_memory_tracker.IsRegionCpuModified(range.address, range.size);
return !m_buffer_cache.HasGpuDirtyBytes(range.address, range.size);
}
Image& TextureCache::ResolveImage(ImageId id) {
@@ -187,21 +186,17 @@ void TextureCache::RegisterImage(ImageId id) {
m_total_used_memory += image.AccountedSize();
}
void TextureCache::UnregisterImage(ImageId id, bool release_tracking) {
void TextureCache::UnregisterImage(ImageId id) {
auto& image = ResolveImage(id);
if (!image.registered) {
return;
}
UntrackImage(id);
std::vector<ImageOwnerIndex::ByteRange> releases;
if (!m_image_owner_index.Unregister(id, releases)) {
EXIT("TextureCache: image missing from owner index\n");
}
m_lru_cache.Free(image.lru_id);
if (release_tracking) {
for (const auto& range: releases) {
m_memory_tracker.UntrackMemory(range.address, range.size);
}
}
const auto accounted = image.AccountedSize();
if (accounted > m_total_used_memory) {
EXIT("TextureCache: image accounting underflow\n");
@@ -210,7 +205,7 @@ void TextureCache::UnregisterImage(ImageId id, bool release_tracking) {
image.registered = false;
}
void TextureCache::DeleteImage(ImageId id, bool release_tracking) {
void TextureCache::DeleteImage(ImageId id) {
auto owner = ResolveOwner(id);
if (owner == nullptr || !owner->registered) {
return;
@@ -224,7 +219,7 @@ void TextureCache::DeleteImage(ImageId id, bool release_tracking) {
}
}
for (const auto association: associations) {
ReleaseGpuTracking(association);
ClearGpuModified(association);
DeleteImage(association);
}
}
@@ -235,7 +230,7 @@ void TextureCache::DeleteImage(ImageId id, bool release_tracking) {
if (owner->info.metadata.kind == ImageMetadataKind::Htile) {
m_surface_metas.erase(owner->info.metadata.range.address);
}
UnregisterImage(id, release_tracking);
UnregisterImage(id);
const auto erase_slot = [this, id, retained = owner] {
auto& slot = m_slots[id.index];
if (slot.generation != id.generation || slot.image != retained) {
@@ -266,10 +261,10 @@ void TextureCache::DeleteImages(std::span<const ImageId> ids,
continue;
}
if (native_source == id) {
ReleaseGpuTracking(id);
ClearGpuModified(id);
} else if (owner->IsGpuModified()) {
DownloadImage(id);
ReleaseGpuTracking(id);
ClearGpuModified(id);
}
DeleteImage(id);
}
@@ -296,6 +291,121 @@ void TextureCache::TouchImage(Image& image) {
}
}
void TextureCache::TrackImage(ImageId id) {
auto& image = ResolveImage(id);
if (!image.registered) {
return;
}
const auto image_begin = image.info.data.address;
const auto image_end = image.info.data.End();
if (image_begin == image.track_addr && image_end == image.track_addr_end) {
return;
}
if (!image.IsTracked()) {
image.track_addr = image_begin;
image.track_addr_end = image_end;
m_page_manager.UpdatePageWatchers(true, image_begin, image.info.data.size);
return;
}
if (image_begin < image.track_addr) {
TrackImageHead(id);
}
if (image.track_addr_end < image_end) {
TrackImageTail(id);
}
}
void TextureCache::TrackImageHead(ImageId id) {
auto& image = ResolveImage(id);
if (!image.registered) {
return;
}
const auto image_begin = image.info.data.address;
if (image_begin == image.track_addr) {
return;
}
if (!image.IsTracked() || image_begin > image.track_addr) {
EXIT("TextureCache: invalid image head tracking range\n");
}
const auto size = image.track_addr - image_begin;
image.track_addr = image_begin;
m_page_manager.UpdatePageWatchers(true, image_begin, size);
}
void TextureCache::TrackImageTail(ImageId id) {
auto& image = ResolveImage(id);
if (!image.registered) {
return;
}
const auto image_end = image.info.data.End();
if (image_end == image.track_addr_end) {
return;
}
if (!image.IsTracked() || image.track_addr_end > image_end) {
EXIT("TextureCache: invalid image tail tracking range\n");
}
const auto address = image.track_addr_end;
const auto size = image_end - address;
image.track_addr_end = image_end;
m_page_manager.UpdatePageWatchers(true, address, size);
}
void TextureCache::UntrackImage(ImageId id) {
auto& image = ResolveImage(id);
if (!image.IsTracked()) {
return;
}
const auto address = image.track_addr;
const auto size = image.track_addr_end - image.track_addr;
image.track_addr = 0;
image.track_addr_end = 0;
if (size != 0) {
m_page_manager.UpdatePageWatchers(false, address, size);
}
}
void TextureCache::UntrackImageHead(ImageId id) {
auto& image = ResolveImage(id);
const auto begin = image.info.data.address;
if (!image.IsTracked() || begin < image.track_addr) {
return;
}
const auto address = (begin + TRACKER_PAGE_SIZE) & ~(TRACKER_PAGE_SIZE - 1);
const auto size = address - begin;
image.track_addr = address;
if (image.track_addr == image.track_addr_end) {
image.MarkMaybeCpuDirty();
if (image.NeedsMaybeCpuHash()) {
image.SetMaybeCpuHash(image.HashGuestEdges());
}
UntrackImage(id);
}
if (size != 0) {
m_page_manager.UpdatePageWatchers(false, begin, size);
}
}
void TextureCache::UntrackImageTail(ImageId id) {
auto& image = ResolveImage(id);
const auto end = image.info.data.End();
if (!image.IsTracked() || image.track_addr_end < end) {
return;
}
const auto address = end & ~(TRACKER_PAGE_SIZE - 1);
const auto size = end - address;
image.track_addr_end = address;
if (image.track_addr == image.track_addr_end) {
image.MarkMaybeCpuDirty();
if (image.NeedsMaybeCpuHash()) {
image.SetMaybeCpuHash(image.HashGuestEdges());
}
UntrackImage(id);
}
if (size != 0) {
m_page_manager.UpdatePageWatchers(false, address, size);
}
}
void TextureCache::TrackImageDownload(ImageId id) {
std::lock_guard transaction(m_resource_mutex);
CacheLock lock(*this, m_lock);
@@ -376,9 +486,6 @@ void TextureCache::ValidateImageDesc(const ImageDesc& desc) const {
}
void TextureCache::PrepareImageCopy(Image& image) {
const auto range = image.info.data;
m_memory_tracker.ForEachUploadRange(
range.address, range.size, false, [](uint64_t, uint64_t) noexcept {}, []() noexcept {});
if (image.IsCpuDirty()) {
image.RefreshComplete();
}
@@ -471,6 +578,7 @@ void TextureCache::CopyImage(ImageId destination_id, ImageId source_id) {
RefreshCopySource(source_id);
auto& destination = ResolveImage(destination_id);
auto& source = ResolveImage(source_id);
TrackImage(destination_id);
if (source.backing.samples != destination.backing.samples) {
EXIT("TextureCache: cannot issue an unequal-sample image copy\n");
}
@@ -479,7 +587,6 @@ void TextureCache::CopyImage(ImageId destination_id, ImageId source_id) {
if (source.info.data == destination.info.data) {
destination.MarkBufferModified();
}
RestoreGpuTracking(destination);
return;
}
const bool source_depth = source.info.IsDepth();
@@ -503,7 +610,6 @@ void TextureCache::CopyImage(ImageId destination_id, ImageId source_id) {
destination.MarkGpuModified();
}
destination.ClearBufferModified();
RestoreGpuTracking(destination);
}
void TextureCache::CopyImageMip(ImageId destination_id, ImageId source_id, uint32_t mip,
@@ -511,6 +617,7 @@ void TextureCache::CopyImageMip(ImageId destination_id, ImageId source_id, uint3
RefreshCopySource(source_id);
auto& destination = ResolveImage(destination_id);
auto& source = ResolveImage(source_id);
TrackImage(destination_id);
if (source.IsBufferModified() || source.backing.samples != destination.backing.samples) {
EXIT("TextureCache: invalid mip-copy ownership or sample count\n");
}
@@ -520,7 +627,6 @@ void TextureCache::CopyImageMip(ImageId destination_id, ImageId source_id, uint3
if (source.IsGpuModified()) {
destination.MarkGpuModified();
}
RestoreGpuTracking(destination);
}
ImageId TextureCache::ResolveDepthOverlap(const ImageInfo& requested, BindingType binding,
@@ -590,7 +696,7 @@ ImageId TextureCache::ResolveDepthOverlap(const ImageInfo& requested, BindingTyp
if (copied) {
DeleteImages(std::array {cached_id}, cached_id);
} else {
ReleaseGpuTracking(cached_id);
ClearGpuModified(cached_id);
DeleteImage(cached_id);
}
return replacement_id;
@@ -903,39 +1009,29 @@ void TextureCache::InitializeImage(ImageId id, const ImageDesc& desc) {
if (image.info.data.Empty()) {
return;
}
TrackImage(id);
if (image.info.metadata.compression != VideoOutCompression::Uncompressed) {
m_memory_tracker.ForEachUploadRange(
image.info.data.address, image.info.data.size, false,
[](uint64_t, uint64_t) noexcept {}, []() noexcept {});
if (image.IsCpuDirty()) {
image.RefreshComplete();
}
return;
}
if (image.info.samples > 1) {
RestoreGpuTracking(image);
return;
}
bool data_gpu_owned = false;
bool data_imported = false;
bool uploaded = false;
m_memory_tracker.ForEachUploadRange(
image.info.data.address, image.info.data.size, false,
[&](uint64_t, uint64_t) noexcept { uploaded = true; },
[&]() noexcept {
uploaded |= image.IsBufferModified() || image.IsDefinitelyCpuDirty();
if (!uploaded) {
return;
}
const auto source =
m_buffer_cache.ObtainBufferForImage(image.info.data.address, image.info.data.size);
if (source.buffer == nullptr) {
EXIT("TextureCache: failed to obtain image upload source\n");
}
data_gpu_owned |= source.gpu_owned;
data_imported = true;
UploadImage(image, desc, *source.buffer, source.offset);
});
bool data_gpu_owned = false;
bool data_imported = false;
const bool upload = image.IsBufferModified() || image.IsCpuDirty();
if (upload) {
const auto source =
m_buffer_cache.ObtainBufferForImage(image.info.data.address, image.info.data.size);
if (source.buffer == nullptr) {
EXIT("TextureCache: failed to obtain image upload source\n");
}
data_gpu_owned |= source.gpu_owned;
data_imported = true;
UploadImage(image, desc, *source.buffer, source.offset);
}
if (data_imported) {
image.ClearBufferModified();
}
@@ -945,39 +1041,27 @@ void TextureCache::InitializeImage(ImageId id, const ImageDesc& desc) {
if (image.IsCpuDirty()) {
image.RefreshComplete();
}
RestoreGpuTracking(image);
}
void TextureCache::RefreshImage(ImageId id, const ImageDesc& desc) {
auto& image = ResolveImage(id);
bool unchanged_maybe = false;
TrackImage(id);
auto& image = ResolveImage(id);
if (image.IsMaybeCpuDirty()) {
const auto hash = image.HashGuestEdges();
if (image.NeedsMaybeCpuHash()) {
image.SetMaybeCpuHash(hash);
return;
}
unchanged_maybe = !image.ResolveMaybeCpuHash(hash);
if (unchanged_maybe) {
m_memory_tracker.ForEachUploadRange(
image.info.data.address, image.info.data.size, false,
[](uint64_t, uint64_t) noexcept {}, []() noexcept {});
}
(void)image.ResolveMaybeCpuHash(hash);
}
bool cpu_dirty = image.IsBufferModified() || image.IsDefinitelyCpuDirty();
if (!unchanged_maybe) {
cpu_dirty |=
m_memory_tracker.IsRegionCpuModified(image.info.data.address, image.info.data.size);
}
if (image.info.metadata.compression != VideoOutCompression::Uncompressed) {
if (cpu_dirty) {
EXIT("TextureCache: compressed guest image refresh is unsupported\n");
}
RestoreGpuTracking(image);
return;
}
if (!cpu_dirty) {
RestoreGpuTracking(image);
return;
}
InitializeImage(id, desc);
@@ -1029,7 +1113,7 @@ ImageId TextureCache::FindImage(ImageDesc& desc, bool exact_format) {
ImageId result {};
bool replacement_buffer = false;
bool replacing_image = false;
bool inserted_new = false;
{
std::lock_guard transaction(m_resource_mutex);
CacheLock lock(*this, m_lock);
@@ -1079,20 +1163,19 @@ ImageId TextureCache::FindImage(ImageDesc& desc, bool exact_format) {
}
replacement_buffer = resolved.IsBufferModified();
DeleteImage(result);
result = {};
replacing_image = true;
result = {};
}
}
if (!result) {
result = InsertImage(desc.info);
inserted_new = true;
auto& inserted = ResolveImage(result);
if (replacement_buffer || m_buffer_cache.HasGpuDirtyBytes(inserted.info.data.address,
inserted.info.data.size)) {
inserted.MarkBufferModified();
} else if (replacing_image) {
m_memory_tracker.MarkRegionAsCpuModified(inserted.info.data.address,
inserted.info.data.size);
}
}
if (inserted_new) {
InitializeImage(result, desc);
} else {
RefreshImage(result, desc);
@@ -1101,9 +1184,7 @@ ImageId TextureCache::FindImage(ImageDesc& desc, bool exact_format) {
auto& image = ResolveImage(result);
if (desc.type == BindingType::VideoOut &&
desc.info.metadata.compression != VideoOutCompression::Uncompressed) {
const bool guest_dirty =
image.IsBufferModified() || image.IsCpuDirty() ||
m_memory_tracker.IsRegionCpuModified(image.info.data.address, image.info.data.size);
const bool guest_dirty = image.IsBufferModified() || image.IsCpuDirty();
const bool native_current =
(image.usage.render_target || image.IsGpuModified()) && !guest_dirty;
if (!native_current) {
@@ -1252,6 +1333,7 @@ void TextureCache::MarkGpuWritten(ImageId id) {
if (!image.registered || image.depth_id) {
EXIT("TextureCache: cannot mark an unavailable image GPU-written\n");
}
TrackImage(id);
CommitGpuWrite(image);
}
@@ -1265,13 +1347,10 @@ void TextureCache::CommitGpuWrite(Image& image) {
}
m_buffer_cache.InvalidateImageAliases(range.address, range.size);
image.ClearBufferModified();
m_memory_tracker.ForEachUploadRange(
range.address, range.size, true, [](uint64_t, uint64_t) noexcept {}, []() noexcept {});
if (image.IsCpuDirty()) {
image.RefreshComplete();
}
image.MarkGpuModified();
RestoreGpuTracking(image);
}
bool TextureCache::ClearImageFromBuffer(CommandBuffer& command, uint64_t address, uint64_t size,
@@ -1335,8 +1414,7 @@ bool TextureCache::ClearImageFromBuffer(CommandBuffer& command, uint64_t address
if (m_buffer_cache.HasGpuDirtyBytes(address, size)) {
m_buffer_cache.DiscardGpuDirtyBytes(address, size);
}
if (image.IsBufferModified() || image.IsCpuDirty() ||
m_memory_tracker.IsRegionCpuModified(image.info.data.address, image.info.data.size)) {
if (image.IsBufferModified() || image.IsCpuDirty()) {
ImageDesc refresh {.info = image.info, .view_info = {}, .type = UploadBinding(image)};
InitializeImage(selected, refresh);
if (image.info.samples == 1 && (image.IsBufferModified() || image.IsCpuDirty())) {
@@ -1361,14 +1439,12 @@ bool TextureCache::ClearImageFromBuffer(CommandBuffer& command, uint64_t address
return true;
}
void TextureCache::PrepareHostWrite(uint64_t address, uint64_t size) {
void TextureCache::InvalidateMemory(uint64_t address, uint64_t size) {
if (!GuestRange {address, size}.Valid()) {
EXIT("TextureCache: invalid host-write range\n");
EXIT("TextureCache: invalid memory-invalidation range\n");
}
CacheLock lock(*this, m_lock);
InvalidateCpuAliases(address, size);
m_memory_tracker.ForEachDownloadRange<true>(address, size, [](uint64_t, uint64_t) noexcept {});
m_memory_tracker.MarkRegionAsCpuModified(address, size);
}
void TextureCache::DownloadDepth(Image& image, Buffer& destination, uint64_t destination_offset) {
@@ -1567,18 +1643,15 @@ bool TextureCache::SynchronizeImageToBuffer(ImageId id) {
if (!plan.valid) {
return false;
}
const auto range = image.info.data;
const bool refresh = image.IsDefinitelyCpuDirty() ||
m_memory_tracker.IsRegionCpuModified(range.address, range.size);
if (refresh) {
const auto range = image.info.data;
if (image.IsCpuDirty()) {
RefreshImage(id,
ImageDesc {.info = image.info, .view_info = {}, .type = UploadBinding(image)});
}
if (!image.IsGpuModified()) {
return true;
}
if (image.IsDefinitelyCpuDirty() || image.IsBufferModified() ||
m_memory_tracker.IsRegionCpuModified(range.address, range.size)) {
if (image.IsDefinitelyCpuDirty() || image.IsBufferModified()) {
EXIT("TextureCache: image mirror source is not native-current\n");
}
auto [destination, offset] =
@@ -1591,7 +1664,7 @@ bool TextureCache::SynchronizeImageToBuffer(ImageId id) {
m_buffer_cache.PublishImageBuffer(range.address, range.size);
image.MarkBufferModified();
RetainImage(m_scheduler.Current(), id);
ReleaseGpuTracking(id);
ClearGpuModified(id);
return true;
}
@@ -1633,7 +1706,7 @@ bool TextureCache::InvalidateMemoryFromGPU(uint64_t address, uint64_t size,
if (!formatted_buffer_write) {
EXIT("TextureCache: buffer write aliases GPU-modified image\n");
}
ReleaseGpuTracking(id);
ClearGpuModified(id);
}
owner->MarkBufferModified();
found = true;
@@ -1660,50 +1733,40 @@ TextureCache::RegionInfo TextureCache::QueryRegion(uint64_t address, uint64_t si
}
void TextureCache::InvalidateCpuAliases(uint64_t address, uint64_t size) {
const auto page_begin = address & ~(TRACKER_PAGE_SIZE - 1);
const auto page_end = (address + size + TRACKER_PAGE_SIZE - 1) & ~(TRACKER_PAGE_SIZE - 1);
for (const auto id: FindImagesInRegion(address, size, true)) {
auto owner = ResolveOwner(id);
if (owner == nullptr || owner->depth_id) {
continue;
}
owner->InvalidateCpuWrite(address, size);
if (owner->NeedsMaybeCpuHash()) {
owner->SetMaybeCpuHash(owner->HashGuestEdges());
if (owner->Overlaps(address, size)) {
owner->InvalidateCpuWrite(address, size);
UntrackImage(id);
continue;
}
const auto image_begin = owner->info.data.address;
const auto image_end = owner->info.data.End();
if (page_end < image_end) {
UntrackImageHead(id);
} else if (image_begin < page_begin) {
UntrackImageTail(id);
} else {
owner->MarkMaybeCpuDirty();
if (owner->NeedsMaybeCpuHash()) {
owner->SetMaybeCpuHash(owner->HashGuestEdges());
}
UntrackImage(id);
}
}
}
void TextureCache::RestoreGpuTracking(const Image& image) {
if (!image.IsGpuModified()) {
return;
}
constexpr uint64_t page_mask = TRACKER_PAGE_SIZE - 1;
const auto range = image.info.data;
const auto begin = range.address & ~page_mask;
const auto end = (range.End() + page_mask) & ~page_mask;
for (auto page = begin; page < end; page += TRACKER_PAGE_SIZE) {
if (!m_memory_tracker.IsRegionGpuModified(page, TRACKER_PAGE_SIZE) &&
!m_memory_tracker.IsRegionCpuModified(page, TRACKER_PAGE_SIZE)) {
m_memory_tracker.MarkRegionAsGpuModified(page, TRACKER_PAGE_SIZE);
}
}
}
void TextureCache::ReleaseGpuTracking(ImageId id) {
void TextureCache::ClearGpuModified(ImageId id) {
auto owner = ResolveOwner(id);
if (owner == nullptr || !owner->IsGpuModified()) {
return;
}
const auto released = owner->info.data;
owner->ClearGpuModified();
m_memory_tracker.ForEachDownloadRange<true>(released.address, released.size,
[](uint64_t, uint64_t) noexcept {});
for (const auto candidate: FindImagesInRegion(released.address, released.size, true)) {
const auto survivor = ResolveOwner(candidate);
if (survivor != nullptr && survivor.get() != owner.get() && !survivor->depth_id) {
RestoreGpuTracking(*survivor);
}
}
RestoreGpuTracking(*owner);
}
bool TextureCache::IsMeta(uint64_t address) {
@@ -1755,27 +1818,25 @@ bool TextureCache::InvalidateMemory(PageFaultAccess access, uint64_t address, ui
return false;
}
if (phase == PageFaultPhase::Invalidate) {
const bool gpu_image =
m_memory_tracker.InvalidateVirtualGpuWrite(access, address, size, phase);
CpuFaultAction action = gpu_image ? CpuFaultAction::Download
: m_memory_tracker.BeginCpuFault(address, size, access);
{
CacheLock lock(*this, m_lock);
CacheLock lock(*this, m_lock);
const bool tracked =
std::ranges::any_of(FindImagesInRegion(address, size, true), [&](ImageId id) {
const auto owner = ResolveOwner(id);
return owner != nullptr && !owner->depth_id && owner->IsTracked();
});
if (tracked) {
InvalidateCpuAliases(address, size);
}
return action != CpuFaultAction::Untracked;
return tracked;
}
if (phase == PageFaultPhase::Complete) {
const bool gpu_image = m_memory_tracker.IsRegionGpuModified(address, size);
return gpu_image ? m_memory_tracker.InvalidateVirtualGpuWrite(access, address, size, phase)
: m_memory_tracker.CompleteCpuFault(address, size, access, false);
}
if (phase != PageFaultPhase::Release) {
if (phase != PageFaultPhase::Complete && phase != PageFaultPhase::Release) {
return false;
}
(void)m_memory_tracker.InvalidateVirtualGpuWrite(access, address, size, phase);
return true;
CacheLock lock(*this, m_lock);
return std::ranges::any_of(FindImagesInRegion(address, size, true), [&](ImageId id) {
const auto owner = ResolveOwner(id);
return owner != nullptr && !owner->depth_id;
});
}
void TextureCache::UnmapMemory(uint64_t address, uint64_t size) {
@@ -1794,16 +1855,10 @@ void TextureCache::UnmapMemory(uint64_t address, uint64_t size) {
continue;
}
if (owner->IsGpuModified()) {
ReleaseGpuTracking(id);
ClearGpuModified(id);
}
DeleteImage(id);
}
m_memory_tracker.UntrackMemory(address, size);
for (const auto id: FindImagesInRegion(address, size, true)) {
if (const auto survivor = ResolveOwner(id); survivor != nullptr) {
RestoreGpuTracking(*survivor);
}
}
}
void TextureCache::RunGarbageCollector() {
@@ -1849,7 +1904,7 @@ void TextureCache::RunGarbageCollector() {
if (safe && !TryDownloadImage(id)) {
continue;
}
ReleaseGpuTracking(id);
ClearGpuModified(id);
}
DeleteImage(id);
if (m_total_used_memory < m_critical_gc_memory && aggressive) {
@@ -4,10 +4,11 @@
#include "common/abi.h"
#include "common/common.h"
#include "common/lruCache.h"
#include "graphics/host_gpu/memoryTracker.h"
#include "graphics/host_gpu/renderer/blitHelper.h"
#include "graphics/host_gpu/renderer/image.h"
#include "graphics/host_gpu/renderer/multiLevelPageTable.h"
#include "graphics/host_gpu/pageManager.h"
#include "graphics/host_gpu/regionManager.h"
#include "graphics/host_gpu/renderer/cache/multiLevelPageTable.h"
#include "graphics/host_gpu/renderer/image/blitHelper.h"
#include "graphics/host_gpu/renderer/image/image.h"
#include <compare>
#include <map>
@@ -64,7 +65,7 @@ public:
[[nodiscard]] bool ClearImageFromBuffer(CommandBuffer& command, uint64_t address, uint64_t size,
uint32_t packed_clear);
void PrepareHostWrite(uint64_t address, uint64_t size);
void InvalidateMemory(uint64_t address, uint64_t size);
[[nodiscard]] bool SynchronizeImageToBuffer(uint64_t address, uint64_t size);
[[nodiscard]] bool InvalidateMemoryFromGPU(uint64_t address, uint64_t size,
bool formatted_buffer_write = false);
@@ -109,11 +110,17 @@ private:
[[nodiscard]] ImageId InsertImage(const ImageInfo& info);
[[nodiscard]] ImageId GetNullImage(const ImageDesc& desc);
void RegisterImage(ImageId id);
void UnregisterImage(ImageId id, bool release_tracking);
void DeleteImage(ImageId id, bool release_tracking = true);
void UnregisterImage(ImageId id);
void DeleteImage(ImageId id);
void DeleteImages(std::span<const ImageId> ids, std::optional<ImageId> native_source = {});
void RetainImage(CommandBuffer& command, ImageId id);
void TouchImage(Image& image);
void TrackImage(ImageId id);
void TrackImageHead(ImageId id);
void TrackImageTail(ImageId id);
void UntrackImage(ImageId id);
void UntrackImageHead(ImageId id);
void UntrackImageTail(ImageId id);
void TrackImageDownload(ImageId id);
void TrackImageDownloadLocked(ImageId id, Image& image);
[[nodiscard]] static bool SameBacking(const ImageInfo& cached, const ImageInfo& requested,
@@ -148,8 +155,7 @@ private:
void ValidateImageDesc(const ImageDesc& desc) const;
void InvalidateCpuAliases(uint64_t address, uint64_t size);
void RestoreGpuTracking(const Image& image);
void ReleaseGpuTracking(ImageId id);
void ClearGpuModified(ImageId id);
[[nodiscard]] bool SynchronizeImageToBuffer(ImageId id);
void DownloadImage(ImageId id);
@@ -160,7 +166,7 @@ private:
GraphicContext& m_graphics;
CommandScheduler& m_scheduler;
TrackingSpinLock m_lock;
MemoryTracker m_memory_tracker;
PageManager& m_page_manager;
BlitHelper m_blit_helper;
std::unique_ptr<TileManager> m_tiler;
BufferCache& m_buffer_cache;
@@ -7,9 +7,9 @@
#include "graphics/guest_gpu/hardwareContext.h"
#include "graphics/guest_gpu/tile.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/objects/textureCommon.h"
#include "graphics/host_gpu/renderer/image/textureCommon.h"
#include "graphics/host_gpu/renderer/debug.h"
#include "graphics/host_gpu/renderer/descriptorCache.h"
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
#include "graphics/host_gpu/renderer/render.h"
#include "graphics/host_gpu/renderer/renderContext.h"
#include "graphics/host_gpu/vulkanCommon.h"
@@ -3,7 +3,7 @@
#include "graphics/guest_gpu/gpu_defs.h"
#include "graphics/host_gpu/renderer/renderTarget.h"
#include "graphics/host_gpu/renderer/textureCache.h"
#include "graphics/host_gpu/renderer/cache/textureCache.h"
#include "graphics/host_gpu/vulkanCommon.h"
#include <cstdint>
+2 -2
View File
@@ -8,8 +8,8 @@
#include "graphics/host_gpu/renderer/colorRenderTarget.h"
#include "graphics/host_gpu/renderer/debug.h"
#include "graphics/host_gpu/renderer/depthRenderTarget.h"
#include "graphics/host_gpu/renderer/descriptorCache.h"
#include "graphics/host_gpu/renderer/imageView.h"
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
#include "graphics/host_gpu/renderer/image/imageView.h"
#include "graphics/host_gpu/renderer/render.h"
#include "graphics/host_gpu/renderer/renderContext.h"
#include "graphics/host_gpu/vma.h"
@@ -10,10 +10,10 @@
#include "graphics/guest_gpu/hardwareContext.h"
#include "graphics/guest_gpu/tile.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/objects/textureCommon.h"
#include "graphics/host_gpu/renderer/image/textureCommon.h"
#include "graphics/host_gpu/renderer/debug.h"
#include "graphics/host_gpu/renderer/descriptorCache.h"
#include "graphics/host_gpu/renderer/imageView.h"
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
#include "graphics/host_gpu/renderer/image/imageView.h"
#include "graphics/host_gpu/renderer/render.h"
#include "graphics/host_gpu/renderer/renderContext.h"
#include "graphics/host_gpu/vulkanCommon.h"
@@ -2,9 +2,9 @@
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_DEPTHRENDERTARGET_H_
#include "common/assert.h"
#include "graphics/host_gpu/renderer/imageView.h"
#include "graphics/host_gpu/renderer/image/imageView.h"
#include "graphics/host_gpu/renderer/renderTarget.h"
#include "graphics/host_gpu/renderer/textureCache.h"
#include "graphics/host_gpu/renderer/cache/textureCache.h"
#include "graphics/host_gpu/vulkanCommon.h"
#include <cstdint>
@@ -1,11 +1,11 @@
#include "graphics/host_gpu/renderer/blitHelper.h"
#include "graphics/host_gpu/renderer/image/blitHelper.h"
#include "common/assert.h"
#include "gpu_blit_shaders/gpu_blit_color_to_ms_depth_spv.h"
#include "gpu_blit_shaders/gpu_blit_fs_triangle_spv.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/renderer/commandScheduler.h"
#include "graphics/host_gpu/renderer/image.h"
#include "graphics/host_gpu/renderer/image/image.h"
#include "graphics/host_gpu/renderer/renderTarget.h"
#include <algorithm>
@@ -1,11 +1,11 @@
#include "graphics/host_gpu/renderer/image.h"
#include "graphics/host_gpu/renderer/image/image.h"
#include "common/assert.h"
#include "common/profiler.h"
#include "graphics/host_gpu/renderer/cache/streamBuffer.h"
#include "graphics/host_gpu/renderer/commandScheduler.h"
#include "graphics/host_gpu/renderer/imageView.h"
#include "graphics/host_gpu/renderer/image/imageView.h"
#include "graphics/host_gpu/renderer/renderTarget.h"
#include "graphics/host_gpu/renderer/streamBuffer.h"
#include "kernel/memory.h"
#include <algorithm>
@@ -99,9 +99,9 @@ vk::ImageAspectFlags Image::FullAspectMask(vk::Format format) noexcept {
}
}
Image::Barriers Image::GetBarriers(vk::ImageLayout destination_layout,
vk::AccessFlags2 destination_access,
vk::PipelineStageFlags2 destination_stage,
Image::Barriers Image::GetBarriers(vk::ImageLayout destination_layout,
vk::AccessFlags2 destination_access,
vk::PipelineStageFlags2 destination_stage,
std::optional<ImageSubresourceRange> range) {
auto& state = backing.state;
auto& subresource_states = backing.subresource_states;
@@ -130,25 +130,25 @@ Image::Barriers Image::GetBarriers(vk::ImageLayout destination_layout,
constexpr auto write_access = vk::AccessFlagBits2::eTransferWrite |
vk::AccessFlagBits2::eShaderWrite |
vk::AccessFlagBits2::eMemoryWrite;
const bool repeated_write =
const bool repeated_write =
static_cast<bool>(subresource_state.access_mask & write_access);
if (subresource_state.layout != destination_layout ||
subresource_state.access_mask != destination_access || repeated_write) {
vk::ImageMemoryBarrier2 barrier {};
barrier.srcStageMask = subresource_state.pl_stage;
barrier.srcAccessMask = subresource_state.access_mask;
barrier.dstStageMask = destination_stage;
barrier.dstAccessMask = destination_access;
barrier.oldLayout = subresource_state.layout;
barrier.newLayout = destination_layout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = backing.image;
barrier.subresourceRange.aspectMask = FullAspectMask(backing.format);
barrier.subresourceRange.baseMipLevel = level;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = layer;
barrier.subresourceRange.layerCount = 1;
barrier.srcStageMask = subresource_state.pl_stage;
barrier.srcAccessMask = subresource_state.access_mask;
barrier.dstStageMask = destination_stage;
barrier.dstAccessMask = destination_access;
barrier.oldLayout = subresource_state.layout;
barrier.newLayout = destination_layout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = backing.image;
barrier.subresourceRange.aspectMask = FullAspectMask(backing.format);
barrier.subresourceRange.baseMipLevel = level;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = layer;
barrier.subresourceRange.layerCount = 1;
barriers.push_back(barrier);
subresource_state = {destination_stage, destination_access, destination_layout};
}
@@ -159,10 +159,10 @@ Image::Barriers Image::GetBarriers(vk::ImageLayout destination_layout,
subresource_states.clear();
}
} else {
constexpr auto write_access = vk::AccessFlagBits2::eTransferWrite |
vk::AccessFlagBits2::eShaderWrite |
vk::AccessFlagBits2::eMemoryWrite;
const bool repeated_write = static_cast<bool>(state.access_mask & write_access);
constexpr auto write_access = vk::AccessFlagBits2::eTransferWrite |
vk::AccessFlagBits2::eShaderWrite |
vk::AccessFlagBits2::eMemoryWrite;
const bool repeated_write = static_cast<bool>(state.access_mask & write_access);
if (state.layout == destination_layout && state.access_mask == destination_access &&
!repeated_write) {
return {};
@@ -191,8 +191,7 @@ Image::Barriers Image::GetBarriers(vk::ImageLayout destination_layout,
}
void Image::Transit(vk::ImageLayout destination_layout, vk::AccessFlags2 destination_access,
std::optional<ImageSubresourceRange> range,
vk::CommandBuffer command_buffer) {
std::optional<ImageSubresourceRange> range, vk::CommandBuffer command_buffer) {
const auto transfer_access =
vk::AccessFlagBits2::eTransferRead | vk::AccessFlagBits2::eTransferWrite;
vk::PipelineStageFlags2 destination_stage {};
@@ -201,8 +200,8 @@ void Image::Transit(vk::ImageLayout destination_layout, vk::AccessFlags2 destina
}
if (!destination_access ||
static_cast<bool>(destination_access & ~vk::AccessFlags2 {transfer_access})) {
destination_stage |= vk::PipelineStageFlagBits2::eAllGraphics |
vk::PipelineStageFlagBits2::eComputeShader;
destination_stage |=
vk::PipelineStageFlagBits2::eAllGraphics | vk::PipelineStageFlagBits2::eComputeShader;
}
const auto barriers =
GetBarriers(destination_layout, destination_access, destination_stage, range);
@@ -218,10 +217,9 @@ void Image::Transit(vk::ImageLayout destination_layout, vk::AccessFlags2 destina
command_buffer.pipelineBarrier2(dependency);
}
void Image::Upload(std::span<const vk::BufferImageCopy> copies, vk::Buffer buffer,
uint64_t offset, uint64_t size) {
EXIT_IF(m_scheduler == nullptr || copies.empty() || buffer == nullptr ||
size == 0);
void Image::Upload(std::span<const vk::BufferImageCopy> copies, vk::Buffer buffer, uint64_t offset,
uint64_t size) {
EXIT_IF(m_scheduler == nullptr || copies.empty() || buffer == nullptr || size == 0);
m_scheduler->EndRendering();
vk::BufferMemoryBarrier2 buffer_barrier {};
buffer_barrier.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands;
@@ -234,16 +232,15 @@ void Image::Upload(std::span<const vk::BufferImageCopy> copies, vk::Buffer buffe
buffer_barrier.offset = offset;
buffer_barrier.size = size;
const auto image_barriers =
GetBarriers(vk::ImageLayout::eTransferDstOptimal,
vk::AccessFlagBits2::eTransferWrite,
GetBarriers(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite,
vk::PipelineStageFlagBits2::eCopy, {});
vk::DependencyInfo dependency {};
dependency.dependencyFlags = vk::DependencyFlagBits::eByRegion;
dependency.dependencyFlags = vk::DependencyFlagBits::eByRegion;
dependency.bufferMemoryBarrierCount = 1;
dependency.pBufferMemoryBarriers = &buffer_barrier;
dependency.imageMemoryBarrierCount = static_cast<uint32_t>(image_barriers.size());
dependency.pImageMemoryBarriers = image_barriers.data();
auto command = m_scheduler->Current().Handle();
dependency.imageMemoryBarrierCount = static_cast<uint32_t>(image_barriers.size());
dependency.pImageMemoryBarriers = image_barriers.data();
auto command = m_scheduler->Current().Handle();
command.pipelineBarrier2(dependency);
command.copyBufferToImage(buffer, backing.image, vk::ImageLayout::eTransferDstOptimal,
static_cast<uint32_t>(copies.size()), copies.data());
@@ -256,8 +253,7 @@ void Image::Upload(std::span<const vk::BufferImageCopy> copies, vk::Buffer buffe
dependency.pImageMemoryBarriers = nullptr;
command.pipelineBarrier2(dependency);
Transit(vk::ImageLayout::eGeneral,
vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eTransferRead, {},
command);
vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eTransferRead, {}, command);
}
void Image::Download(std::span<const vk::BufferImageCopy> copies, vk::Buffer buffer,
@@ -265,7 +261,7 @@ void Image::Download(std::span<const vk::BufferImageCopy> copies, vk::Buffer buf
EXIT_IF(m_scheduler == nullptr || copies.empty() || buffer == nullptr || size == 0);
m_scheduler->EndRendering();
vk::BufferMemoryBarrier2 buffer_barrier {};
buffer_barrier.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands;
buffer_barrier.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands;
buffer_barrier.srcAccessMask =
vk::AccessFlagBits2::eMemoryRead | vk::AccessFlagBits2::eMemoryWrite;
buffer_barrier.dstStageMask = vk::PipelineStageFlagBits2::eCopy;
@@ -276,16 +272,15 @@ void Image::Download(std::span<const vk::BufferImageCopy> copies, vk::Buffer buf
buffer_barrier.offset = offset;
buffer_barrier.size = size;
const auto image_barriers =
GetBarriers(vk::ImageLayout::eTransferSrcOptimal,
vk::AccessFlagBits2::eTransferRead,
GetBarriers(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead,
vk::PipelineStageFlagBits2::eCopy, {});
vk::DependencyInfo dependency {};
dependency.dependencyFlags = vk::DependencyFlagBits::eByRegion;
dependency.dependencyFlags = vk::DependencyFlagBits::eByRegion;
dependency.bufferMemoryBarrierCount = 1;
dependency.pBufferMemoryBarriers = &buffer_barrier;
dependency.imageMemoryBarrierCount = static_cast<uint32_t>(image_barriers.size());
dependency.pImageMemoryBarriers = image_barriers.data();
auto command = m_scheduler->Current().Handle();
dependency.imageMemoryBarrierCount = static_cast<uint32_t>(image_barriers.size());
dependency.pImageMemoryBarriers = image_barriers.data();
auto command = m_scheduler->Current().Handle();
command.pipelineBarrier2(dependency);
command.copyImageToBuffer(backing.image, vk::ImageLayout::eTransferSrcOptimal, buffer,
static_cast<uint32_t>(copies.size()), copies.data());
@@ -299,11 +294,11 @@ void Image::Download(std::span<const vk::BufferImageCopy> copies, vk::Buffer buf
command.pipelineBarrier2(dependency);
}
std::pair<uint32_t, uint32_t>
Image::SanitizeCopyLayers(const Image& source, const Image& destination, uint32_t depth) {
const auto source_type = source.backing.image_type;
const auto destination_type = destination.backing.image_type;
uint32_t source_layers = source.backing.layers;
std::pair<uint32_t, uint32_t> Image::SanitizeCopyLayers(const Image& source,
const Image& destination, uint32_t depth) {
const auto source_type = source.backing.image_type;
const auto destination_type = destination.backing.image_type;
uint32_t source_layers = source.backing.layers;
uint32_t destination_layers = destination.backing.layers;
if (source_type == vk::ImageType::e3D) {
source_layers = 1;
@@ -312,13 +307,10 @@ Image::SanitizeCopyLayers(const Image& source, const Image& destination, uint32_
destination_layers = 1;
}
if (source_type == destination_type) {
source_layers = destination_layers =
std::min(source_layers, destination_layers);
} else if (source_type == vk::ImageType::e2D &&
destination_type == vk::ImageType::e3D) {
source_layers = destination_layers = std::min(source_layers, destination_layers);
} else if (source_type == vk::ImageType::e2D && destination_type == vk::ImageType::e3D) {
source_layers = depth;
} else if (source_type == vk::ImageType::e3D &&
destination_type == vk::ImageType::e2D) {
} else if (source_type == vk::ImageType::e3D && destination_type == vk::ImageType::e2D) {
destination_layers = depth;
}
return {source_layers, destination_layers};
@@ -327,12 +319,11 @@ Image::SanitizeCopyLayers(const Image& source, const Image& destination, uint32_
void Image::CopyImage(Image& source) {
EXIT_IF(m_scheduler == nullptr || source.backing.samples != backing.samples);
m_scheduler->EndRendering();
const uint32_t levels =
std::min(source.backing.mip_levels, backing.mip_levels);
const uint32_t levels = std::min(source.backing.mip_levels, backing.mip_levels);
const uint32_t base_depth = backing.image_type == vk::ImageType::e3D
? backing.extent.depth
: source.backing.extent.depth;
const auto source_aspect =
const auto source_aspect =
FullAspectMask(source.backing.format) & ~vk::ImageAspectFlagBits::eStencil;
const auto destination_aspect =
FullAspectMask(backing.format) & ~vk::ImageAspectFlagBits::eStencil;
@@ -342,8 +333,7 @@ void Image::CopyImage(Image& source) {
const auto width = std::max(source.backing.extent.width >> level, 1u);
const auto height = std::max(source.backing.extent.height >> level, 1u);
const auto depth = std::max(base_depth >> level, 1u);
const auto [source_layers, destination_layers] =
SanitizeCopyLayers(source, *this, depth);
const auto [source_layers, destination_layers] = SanitizeCopyLayers(source, *this, depth);
vk::ImageCopy copy {};
copy.srcSubresource = {source_aspect, level, 0, 1};
copy.dstSubresource = {destination_aspect, level, 0, 1};
@@ -351,8 +341,7 @@ void Image::CopyImage(Image& source) {
if (source.backing.image_type == vk::ImageType::e3D) {
copy.extent = {width, height, depth};
} else {
copy.srcSubresource.layerCount =
std::min(source_layers, destination_layers);
copy.srcSubresource.layerCount = std::min(source_layers, destination_layers);
copy.dstSubresource.layerCount = copy.srcSubresource.layerCount;
copy.extent = {width, height, 1};
}
@@ -369,34 +358,30 @@ void Image::CopyImage(Image& source) {
return;
}
auto command = m_scheduler->Current().Handle();
source.Transit(vk::ImageLayout::eTransferSrcOptimal,
vk::AccessFlagBits2::eTransferRead, {}, command);
Transit(vk::ImageLayout::eTransferDstOptimal,
vk::AccessFlagBits2::eTransferWrite, {}, command);
command.copyImage(source.backing.image, vk::ImageLayout::eTransferSrcOptimal,
backing.image, vk::ImageLayout::eTransferDstOptimal,
static_cast<uint32_t>(copies.size()), copies.data());
source.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {},
command);
Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {}, command);
command.copyImage(source.backing.image, vk::ImageLayout::eTransferSrcOptimal, backing.image,
vk::ImageLayout::eTransferDstOptimal, static_cast<uint32_t>(copies.size()),
copies.data());
Transit(vk::ImageLayout::eGeneral,
vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eTransferRead, {},
command);
vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eTransferRead, {}, command);
}
void Image::Resolve(Image& source, const ImageSubresourceRange& source_range,
const ImageSubresourceRange& destination_range) {
EXIT_IF(m_scheduler == nullptr || backing.samples != 1 ||
source.backing.image_type != vk::ImageType::e2D ||
backing.image_type != vk::ImageType::e2D ||
source_range.level_count != 1 || destination_range.level_count != 1 ||
backing.image_type != vk::ImageType::e2D || source_range.level_count != 1 ||
destination_range.level_count != 1 ||
source_range.base_level >= source.backing.mip_levels ||
destination_range.base_level >= backing.mip_levels ||
source_range.base_layer >= source.backing.layers ||
destination_range.base_layer >= backing.layers);
const auto layers = std::min(
{source_range.layer_count, destination_range.layer_count,
source.backing.layers - source_range.base_layer,
backing.layers - destination_range.base_layer});
const auto source_width =
std::max(source.backing.extent.width >> source_range.base_level, 1u);
const auto layers = std::min({source_range.layer_count, destination_range.layer_count,
source.backing.layers - source_range.base_layer,
backing.layers - destination_range.base_layer});
const auto source_width = std::max(source.backing.extent.width >> source_range.base_level, 1u);
const auto source_height =
std::max(source.backing.extent.height >> source_range.base_level, 1u);
const auto destination_width =
@@ -404,43 +389,40 @@ void Image::Resolve(Image& source, const ImageSubresourceRange& source_range,
const auto destination_height =
std::max(backing.extent.height >> destination_range.base_level, 1u);
const bool copy = source.backing.samples == 1;
EXIT_IF(layers == 0 || info.extent.width > source_width ||
info.extent.height > source_height || info.extent.width > destination_width ||
info.extent.height > destination_height ||
EXIT_IF(layers == 0 || info.extent.width > source_width || info.extent.height > source_height ||
info.extent.width > destination_width || info.extent.height > destination_height ||
(copy ? !ImageViewOps::FormatsCompatible(source.backing.format, backing.format)
: source.backing.format != backing.format));
auto resolved_source_range = source_range;
auto resolved_destination_range = destination_range;
auto resolved_source_range = source_range;
auto resolved_destination_range = destination_range;
resolved_source_range.layer_count = layers;
resolved_destination_range.layer_count = layers;
const vk::Extent3D resolve_extent {info.extent.width, info.extent.height, 1};
m_scheduler->EndRendering();
auto command = m_scheduler->Current().Handle();
source.Transit(vk::ImageLayout::eTransferSrcOptimal,
vk::AccessFlagBits2::eTransferRead, resolved_source_range, command);
Transit(vk::ImageLayout::eTransferDstOptimal,
vk::AccessFlagBits2::eTransferWrite, resolved_destination_range, command);
source.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead,
resolved_source_range, command);
Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite,
resolved_destination_range, command);
if (copy) {
vk::ImageCopy region {};
region.srcSubresource = {vk::ImageAspectFlagBits::eColor,
resolved_source_range.base_level,
region.srcSubresource = {vk::ImageAspectFlagBits::eColor, resolved_source_range.base_level,
resolved_source_range.base_layer, layers};
region.dstSubresource = {vk::ImageAspectFlagBits::eColor,
resolved_destination_range.base_level,
resolved_destination_range.base_layer, layers};
region.extent = resolve_extent;
command.copyImage(source.backing.image, vk::ImageLayout::eTransferSrcOptimal,
backing.image, vk::ImageLayout::eTransferDstOptimal, region);
region.extent = resolve_extent;
command.copyImage(source.backing.image, vk::ImageLayout::eTransferSrcOptimal, backing.image,
vk::ImageLayout::eTransferDstOptimal, region);
} else {
vk::ImageResolve region {};
region.srcSubresource = {vk::ImageAspectFlagBits::eColor,
resolved_source_range.base_level,
region.srcSubresource = {vk::ImageAspectFlagBits::eColor, resolved_source_range.base_level,
resolved_source_range.base_layer, layers};
region.dstSubresource = {vk::ImageAspectFlagBits::eColor,
resolved_destination_range.base_level,
resolved_destination_range.base_layer, layers};
region.extent = resolve_extent;
region.extent = resolve_extent;
command.resolveImage(source.backing.image, vk::ImageLayout::eTransferSrcOptimal,
backing.image, vk::ImageLayout::eTransferDstOptimal, region);
}
@@ -454,22 +436,21 @@ uint32_t Image::CopyRows(uint64_t row_size, uint32_t rows, uint64_t capacity) no
}
void Image::CopyImageWithBuffer(Image& source, Buffer& buffer) {
EXIT_IF(m_scheduler == nullptr || buffer.Handle() == nullptr ||
source.backing.samples != 1 || backing.samples != 1);
EXIT_IF(m_scheduler == nullptr || buffer.Handle() == nullptr || source.backing.samples != 1 ||
backing.samples != 1);
m_scheduler->EndRendering();
const uint32_t levels =
std::min(source.backing.mip_levels, backing.mip_levels);
const auto source_aspect =
const uint32_t levels = std::min(source.backing.mip_levels, backing.mip_levels);
const auto source_aspect =
FullAspectMask(source.backing.format) & ~vk::ImageAspectFlagBits::eStencil;
const auto destination_aspect =
FullAspectMask(backing.format) & ~vk::ImageAspectFlagBits::eStencil;
const auto source_bytes = DepthAspectTransferBytes(source.backing.format) != 0
? DepthAspectTransferBytes(source.backing.format)
: source.info.bytes_per_block;
const auto destination_bytes = DepthAspectTransferBytes(backing.format) != 0
? DepthAspectTransferBytes(backing.format)
: info.bytes_per_block;
const uint32_t source_block = source.info.IsBlock() ? 4u : 1u;
const auto source_bytes = DepthAspectTransferBytes(source.backing.format) != 0
? DepthAspectTransferBytes(source.backing.format)
: source.info.bytes_per_block;
const auto destination_bytes = DepthAspectTransferBytes(backing.format) != 0
? DepthAspectTransferBytes(backing.format)
: info.bytes_per_block;
const uint32_t source_block = source.info.IsBlock() ? 4u : 1u;
const uint32_t destination_block = info.IsBlock() ? 4u : 1u;
EXIT_IF(levels == 0 || source_bytes == 0 || source_bytes != destination_bytes ||
source_block != destination_block);
@@ -484,74 +465,66 @@ void Image::CopyImageWithBuffer(Image& source, Buffer& buffer) {
barrier.buffer = buffer.Handle();
barrier.offset = 0;
vk::DependencyInfo dependency {};
dependency.dependencyFlags = vk::DependencyFlagBits::eByRegion;
dependency.dependencyFlags = vk::DependencyFlagBits::eByRegion;
dependency.bufferMemoryBarrierCount = 1;
dependency.pBufferMemoryBarriers = &barrier;
auto command = m_scheduler->Current().Handle();
source.Transit(vk::ImageLayout::eTransferSrcOptimal,
vk::AccessFlagBits2::eTransferRead, {}, command);
Transit(vk::ImageLayout::eTransferDstOptimal,
vk::AccessFlagBits2::eTransferWrite, {}, command);
auto command = m_scheduler->Current().Handle();
source.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {},
command);
Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {}, command);
for (uint32_t level = 0; level < levels; level++) {
const auto width = std::max(source.backing.extent.width >> level, 1u);
const auto height = std::max(source.backing.extent.height >> level, 1u);
const auto source_depth = source.backing.image_type == vk::ImageType::e3D
? std::max(source.backing.extent.depth >> level, 1u)
: source.backing.layers;
const auto width = std::max(source.backing.extent.width >> level, 1u);
const auto height = std::max(source.backing.extent.height >> level, 1u);
const auto source_depth = source.backing.image_type == vk::ImageType::e3D
? std::max(source.backing.extent.depth >> level, 1u)
: source.backing.layers;
const auto destination_depth = backing.image_type == vk::ImageType::e3D
? std::max(backing.extent.depth >> level, 1u)
: backing.layers;
const auto slices = std::min(source_depth, destination_depth);
const auto block_rows = (height + source_block - 1) / source_block;
const auto slices = std::min(source_depth, destination_depth);
const auto block_rows = (height + source_block - 1) / source_block;
const auto row_size =
static_cast<uint64_t>((width + source_block - 1) / source_block) * source_bytes;
const auto rows_per_copy = CopyRows(row_size, block_rows, buffer.Size());
EXIT_IF(slices == 0 || rows_per_copy == 0);
for (uint32_t slice = 0; slice < slices; slice++) {
for (uint32_t block_row = 0; block_row < block_rows;
block_row += rows_per_copy) {
const auto copy_rows = std::min(rows_per_copy, block_rows - block_row);
const auto y = block_row * source_block;
const auto copy_height =
std::min(copy_rows * source_block, height - y);
const auto copy_size = row_size * copy_rows;
for (uint32_t block_row = 0; block_row < block_rows; block_row += rows_per_copy) {
const auto copy_rows = std::min(rows_per_copy, block_rows - block_row);
const auto y = block_row * source_block;
const auto copy_height = std::min(copy_rows * source_block, height - y);
const auto copy_size = row_size * copy_rows;
vk::BufferImageCopy source_copy {};
source_copy.imageSubresource = {
source_aspect, level,
source.backing.image_type == vk::ImageType::e3D ? 0u : slice, 1};
source_copy.imageOffset = {
0, static_cast<int32_t>(y),
source.backing.image_type == vk::ImageType::e3D
? static_cast<int32_t>(slice)
: 0};
source_copy.imageExtent = {width, copy_height, 1};
auto destination_copy = source_copy;
source_copy.imageOffset = {0, static_cast<int32_t>(y),
source.backing.image_type == vk::ImageType::e3D
? static_cast<int32_t>(slice)
: 0};
source_copy.imageExtent = {width, copy_height, 1};
auto destination_copy = source_copy;
destination_copy.imageSubresource = {
destination_aspect, level,
backing.image_type == vk::ImageType::e3D ? 0u : slice, 1};
destination_copy.imageOffset.z =
backing.image_type == vk::ImageType::e3D
? static_cast<int32_t>(slice)
: 0;
backing.image_type == vk::ImageType::e3D ? static_cast<int32_t>(slice) : 0;
barrier.size = copy_size;
barrier.srcAccessMask = vk::AccessFlagBits2::eTransferRead;
barrier.dstAccessMask = vk::AccessFlagBits2::eTransferWrite;
command.pipelineBarrier2(dependency);
command.copyImageToBuffer(source.backing.image,
vk::ImageLayout::eTransferSrcOptimal,
buffer.Handle(), source_copy);
vk::ImageLayout::eTransferSrcOptimal, buffer.Handle(),
source_copy);
barrier.srcAccessMask = vk::AccessFlagBits2::eTransferWrite;
barrier.dstAccessMask = vk::AccessFlagBits2::eTransferRead;
command.pipelineBarrier2(dependency);
command.copyBufferToImage(buffer.Handle(), backing.image,
vk::ImageLayout::eTransferDstOptimal,
destination_copy);
vk::ImageLayout::eTransferDstOptimal, destination_copy);
}
}
}
Transit(vk::ImageLayout::eGeneral,
vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eTransferRead, {},
command);
vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eTransferRead, {}, command);
}
void Image::CopyMip(Image& source, uint32_t mip, uint32_t layer) {
@@ -561,11 +534,9 @@ void Image::CopyMip(Image& source, uint32_t mip, uint32_t layer) {
const auto width = std::max(backing.extent.width >> mip, 1u);
const auto height = std::max(backing.extent.height >> mip, 1u);
const auto depth = std::max(backing.extent.depth >> mip, 1u);
EXIT_IF(width != source.backing.extent.width ||
height != source.backing.extent.height);
const auto [source_layers, destination_layers] =
SanitizeCopyLayers(source, *this, depth);
const auto aspects = FullAspectMask(source.backing.format);
EXIT_IF(width != source.backing.extent.width || height != source.backing.extent.height);
const auto [source_layers, destination_layers] = SanitizeCopyLayers(source, *this, depth);
const auto aspects = FullAspectMask(source.backing.format);
EXIT_IF(aspects != FullAspectMask(backing.format));
std::array<vk::ImageCopy, 2> copies {};
uint32_t copy_count = 0;
@@ -580,16 +551,13 @@ void Image::CopyMip(Image& source, uint32_t mip, uint32_t layer) {
copy.extent = {width, height, depth};
}
auto command = m_scheduler->Current().Handle();
Transit(vk::ImageLayout::eTransferDstOptimal,
vk::AccessFlagBits2::eTransferWrite, {}, command);
source.Transit(vk::ImageLayout::eTransferSrcOptimal,
vk::AccessFlagBits2::eTransferRead, {}, command);
command.copyImage(source.backing.image, vk::ImageLayout::eTransferSrcOptimal,
backing.image, vk::ImageLayout::eTransferDstOptimal, copy_count,
copies.data());
Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {}, command);
source.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {},
command);
command.copyImage(source.backing.image, vk::ImageLayout::eTransferSrcOptimal, backing.image,
vk::ImageLayout::eTransferDstOptimal, copy_count, copies.data());
Transit(vk::ImageLayout::eGeneral,
vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eTransferRead, {},
command);
vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eTransferRead, {}, command);
}
namespace ImageOps {
@@ -601,8 +569,7 @@ void Validate(const ImageInfo& info) {
if (info.pixel_format == vk::Format::eUndefined) {
const bool metadata_empty =
info.metadata.range.address == 0 && info.metadata.range.size == 0 &&
info.metadata.kind == ImageMetadataKind::None &&
info.metadata.control == 0 &&
info.metadata.kind == ImageMetadataKind::None && info.metadata.control == 0 &&
info.metadata.compression == VideoOutCompression::Uncompressed &&
!info.metadata.stencil_compressed;
if (info.data.Empty() || info.HasStencil() || !metadata_empty || info.extent.width == 0 ||
@@ -615,9 +582,9 @@ void Validate(const ImageInfo& info) {
}
if (info.extent.width == 0 || info.extent.height == 0 || info.extent.depth == 0 ||
info.resources.levels == 0 ||
info.resources.levels > info.mip_layout.size() || info.resources.layers == 0 ||
info.samples == 0 || vulkan_sample_count(info.samples) == vk::SampleCountFlagBits {} ||
info.resources.levels == 0 || info.resources.levels > info.mip_layout.size() ||
info.resources.layers == 0 || info.samples == 0 ||
vulkan_sample_count(info.samples) == vk::SampleCountFlagBits {} ||
info.bytes_per_block == 0 || (info.data.address != 0 && info.pitch == 0)) {
EXIT("invalid image geometry or format\n");
}
@@ -688,11 +655,11 @@ uint32_t RenderTargetTransferFormat(uint32_t bytes_per_element) {
} // namespace ImageOps
Image::Image(GraphicContext& graphics, CommandScheduler& scheduler,
const ImageInfo& image_info)
Image::Image(GraphicContext& graphics, CommandScheduler& scheduler, const ImageInfo& image_info)
: info(image_info), m_graphics(&graphics), m_scheduler(&scheduler) {
KYTY_PROFILER_FUNCTION();
ImageOps::Validate(info);
m_cpu_dirty = !info.data.Empty();
if (info.pixel_format == vk::Format::eUndefined) {
return;
}
@@ -742,9 +709,9 @@ Image::Image(GraphicContext& graphics, CommandScheduler& scheduler,
}
uint64_t Image::HashGuestEdges() const {
constexpr uint64_t page_mask = TRACKER_PAGE_SIZE - 1;
constexpr uint64_t page_mask = TRACKER_PAGE_SIZE - 1;
std::array<uint8_t, TRACKER_PAGE_SIZE * 2> bytes {};
const auto range = info.data;
const auto range = info.data;
const uint64_t head_end = std::min(range.End(), (range.address + page_mask) & ~page_mask);
const uint64_t tail_begin = std::max(range.address, range.End() & ~page_mask);
const uint64_t head_size = head_end - range.address;
@@ -3,7 +3,7 @@
#include "common/assert.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/renderer/imageInfo.h"
#include "graphics/host_gpu/renderer/image/imageInfo.h"
#include <compare>
#include <limits>
@@ -66,16 +66,16 @@ public:
[[nodiscard]] vk::ImageView FindView(const ImageViewInfo& view_info);
void AssociateDepth(ImageId image_id) { depth_id = image_id; }
using Barriers = std::vector<vk::ImageMemoryBarrier2>;
[[nodiscard]] Barriers
GetBarriers(vk::ImageLayout destination_layout, vk::AccessFlags2 destination_access,
vk::PipelineStageFlags2 destination_stage,
std::optional<ImageSubresourceRange> range);
[[nodiscard]] Barriers GetBarriers(vk::ImageLayout destination_layout,
vk::AccessFlags2 destination_access,
vk::PipelineStageFlags2 destination_stage,
std::optional<ImageSubresourceRange> range);
void Transit(vk::ImageLayout destination_layout, vk::AccessFlags2 destination_access,
std::optional<ImageSubresourceRange> range, vk::CommandBuffer command_buffer);
void Upload(std::span<const vk::BufferImageCopy> copies, vk::Buffer buffer,
uint64_t offset, uint64_t size);
void Download(std::span<const vk::BufferImageCopy> copies, vk::Buffer buffer,
uint64_t offset, uint64_t size);
void Upload(std::span<const vk::BufferImageCopy> copies, vk::Buffer buffer, uint64_t offset,
uint64_t size);
void Download(std::span<const vk::BufferImageCopy> copies, vk::Buffer buffer, uint64_t offset,
uint64_t size);
void CopyImage(Image& source);
void Resolve(Image& source, const ImageSubresourceRange& source_range,
const ImageSubresourceRange& destination_range);
@@ -84,8 +84,8 @@ public:
void InvalidateCpuWrite(uint64_t vaddr, uint64_t size) {
if (ImageRangeOverlaps(info.data.address, info.data.size, vaddr, size)) {
m_cpu_dirty = true;
m_maybe_cpu_dirty = false;
m_cpu_dirty = true;
m_maybe_cpu_dirty = false;
m_maybe_hash_valid = false;
} else if (ImagePageRangesOverlap(info.data.address, info.data.size, vaddr, size)) {
m_maybe_cpu_dirty = true;
@@ -95,6 +95,11 @@ public:
[[nodiscard]] bool IsCpuDirty() const { return m_cpu_dirty || m_maybe_cpu_dirty; }
[[nodiscard]] bool IsDefinitelyCpuDirty() const { return m_cpu_dirty; }
[[nodiscard]] bool IsMaybeCpuDirty() const { return m_maybe_cpu_dirty; }
void MarkMaybeCpuDirty() {
if (!m_cpu_dirty) {
m_maybe_cpu_dirty = true;
}
}
[[nodiscard]] bool NeedsMaybeCpuHash() const {
return m_maybe_cpu_dirty && !m_maybe_hash_valid;
}
@@ -102,14 +107,14 @@ public:
if (!NeedsMaybeCpuHash()) {
EXIT("image cannot initialize maybe-dirty hash\n");
}
m_maybe_cpu_hash = hash;
m_maybe_cpu_hash = hash;
m_maybe_hash_valid = true;
}
[[nodiscard]] bool ResolveMaybeCpuHash(uint64_t hash) {
if (!m_maybe_cpu_dirty || !m_maybe_hash_valid || m_cpu_dirty) {
EXIT("image cannot resolve maybe-dirty hash\n");
}
m_maybe_cpu_dirty = false;
m_maybe_cpu_dirty = false;
m_maybe_hash_valid = false;
m_cpu_dirty |= hash != m_maybe_cpu_hash;
return m_cpu_dirty;
@@ -125,14 +130,15 @@ public:
}
[[nodiscard]] bool IsGpuModified() const noexcept { return m_gpu_modified; }
void MarkGpuModified() noexcept { m_gpu_modified = true; }
void ClearGpuModified() noexcept { m_gpu_modified = false; }
void MarkGpuModified() noexcept { m_gpu_modified = true; }
void ClearGpuModified() noexcept { m_gpu_modified = false; }
[[nodiscard]] bool IsBufferModified() const noexcept { return m_buffer_modified; }
void MarkBufferModified() noexcept { m_buffer_modified = true; }
void ClearBufferModified() noexcept { m_buffer_modified = false; }
void MarkBufferModified() noexcept { m_buffer_modified = true; }
void ClearBufferModified() noexcept { m_buffer_modified = false; }
[[nodiscard]] bool Overlaps(uint64_t address, uint64_t size, bool pages = false) const noexcept {
[[nodiscard]] bool Overlaps(uint64_t address, uint64_t size,
bool pages = false) const noexcept {
return pages ? ImagePageRangesOverlap(info.data.address, info.data.size, address, size)
: ImageRangeOverlaps(info.data.address, info.data.size, address, size);
}
@@ -142,38 +148,41 @@ public:
[[nodiscard]] bool SafeToDownload() const noexcept {
return IsGpuModified() && !IsBufferModified() && !IsCpuDirty();
}
[[nodiscard]] bool IsTracked() const noexcept { return track_addr != 0 && track_addr_end != 0; }
[[nodiscard]] uint64_t AccountedSize() const noexcept {
return backing.image == nullptr ? 0 : (info.data.size + 1023) & ~uint64_t {1023};
}
[[nodiscard]] uint64_t HashGuestEdges() const;
ImageInfo info;
VulkanImage backing;
ImageViewCache views;
ImageUsage usage;
ImageBinding binding;
bool registered = false;
ImageId depth_id {};
uint64_t tick_accessed_last = 0;
size_t lru_id = 0;
ImageInfo info;
VulkanImage backing;
ImageViewCache views;
ImageUsage usage;
ImageBinding binding;
bool registered = false;
uint64_t track_addr = 0;
uint64_t track_addr_end = 0;
ImageId depth_id {};
uint64_t tick_accessed_last = 0;
size_t lru_id = 0;
private:
friend struct ImageTestAccess;
[[nodiscard]] static vk::ImageAspectFlags FullAspectMask(vk::Format format) noexcept;
[[nodiscard]] static uint32_t CopyRows(uint64_t row_size, uint32_t rows,
uint64_t capacity) noexcept;
[[nodiscard]] static uint32_t CopyRows(uint64_t row_size, uint32_t rows,
uint64_t capacity) noexcept;
[[nodiscard]] static std::pair<uint32_t, uint32_t>
SanitizeCopyLayers(const Image& source, const Image& destination, uint32_t depth);
GraphicContext* m_graphics = nullptr;
CommandScheduler* m_scheduler = nullptr;
uint64_t m_maybe_cpu_hash = 0;
bool m_cpu_dirty = false;
bool m_maybe_cpu_dirty = false;
bool m_maybe_hash_valid = false;
bool m_gpu_modified = false;
bool m_buffer_modified = false;
GraphicContext* m_graphics = nullptr;
CommandScheduler* m_scheduler = nullptr;
uint64_t m_maybe_cpu_hash = 0;
bool m_cpu_dirty = false;
bool m_maybe_cpu_dirty = false;
bool m_maybe_hash_valid = false;
bool m_gpu_modified = false;
bool m_buffer_modified = false;
};
namespace ImageOps {
@@ -1,8 +1,8 @@
#include "graphics/host_gpu/renderer/imageView.h"
#include "graphics/host_gpu/renderer/image/imageView.h"
#include "common/assert.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/renderer/image.h"
#include "graphics/host_gpu/renderer/image/image.h"
#include <mutex>
@@ -2,8 +2,8 @@
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_IMAGEVIEW_H_
#include "common/assert.h"
#include "graphics/host_gpu/renderer/imageInfo.h"
#include "graphics/shader/recompiler/ShaderIR.h"
#include "graphics/host_gpu/renderer/image/imageInfo.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/shader.h"
namespace Libs::Graphics {
@@ -103,10 +103,7 @@ IsSupportedSampledDepthUintResource(const ShaderRecompiler::IR::ImageResource& r
inline void ValidateStorageColorView(vk::Format image_format, vk::Format view_format,
uint32_t swizzle) noexcept {
const auto srgb_view = SrgbStorageViewFormat(image_format);
const bool srgb_storage_view =
srgb_view != vk::Format::eUndefined && view_format == srgb_view;
if ((image_format != view_format && !srgb_storage_view) ||
if (!ImageViewOps::FormatsCompatible(image_format, view_format) ||
!IsValidImageSwizzle(swizzle)) {
UnsupportedColorView("storage", image_format, view_format, swizzle);
}
@@ -122,7 +119,10 @@ IsSupportedStorageImageResource(const ShaderRecompiler::IR::ImageResource& resou
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim3D ||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray) &&
resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None && resource.written &&
!resource.atomic && !resource.depth_compare;
(!resource.atomic ||
(resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint &&
resource.read)) &&
!resource.depth_compare;
}
inline void
@@ -1,9 +1,9 @@
#include "graphics/host_gpu/objects/textureCommon.h"
#include "graphics/host_gpu/renderer/image/textureCommon.h"
#include "common/assert.h"
#include "graphics/guest_gpu/gpu_defs.h"
#include "graphics/guest_gpu/gpu_format.h"
#include "graphics/host_gpu/renderer/tiler.h"
#include "graphics/host_gpu/renderer/image/tiler.h"
#include "graphics/host_gpu/vulkanCommon.h"
#include <algorithm>
@@ -102,6 +102,10 @@ constexpr RenderTargetFormatMapping kRenderTargetFormats[] = {
Prospero::ChannelType::kUNorm,
Prospero::ChannelOrder::kStandard,
{vk::Format::eR16G16B16A16Unorm, 8}},
{Prospero::ChannelLayout::k16_16_16_16,
Prospero::ChannelType::kUInt,
Prospero::ChannelOrder::kStandard,
{vk::Format::eR16G16B16A16Uint, 8}},
{Prospero::ChannelLayout::k16_16_16_16,
Prospero::ChannelType::kFloat,
Prospero::ChannelOrder::kStandard,
@@ -1,5 +1,5 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_OBJECTS_TEXTURECOMMON_H_
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_OBJECTS_TEXTURECOMMON_H_
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_HOST_GPU_RENDERER_IMAGE_TEXTURECOMMON_H_
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_HOST_GPU_RENDERER_IMAGE_TEXTURECOMMON_H_
#include "common/abi.h"
#include "common/common.h"
@@ -51,4 +51,4 @@ bool TextureBuildGpuTileInfos(uint64_t size,
} // namespace Libs::Graphics
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_OBJECTS_TEXTURECOMMON_H_ */
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_HOST_GPU_RENDERER_IMAGE_TEXTURECOMMON_H_ */
@@ -1,4 +1,4 @@
#include "graphics/host_gpu/renderer/tiler.h"
#include "graphics/host_gpu/renderer/image/tiler.h"
#include "common/assert.h"
#include "gpu_tiler_shaders/gpu_tiler_demote_d16_spv.h"
@@ -15,8 +15,8 @@
#include "gpu_tiler_shaders/gpu_tiler_swap_bgra16_spv.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/renderer/commandScheduler.h"
#include "graphics/host_gpu/renderer/image.h"
#include "graphics/host_gpu/renderer/streamBuffer.h"
#include "graphics/host_gpu/renderer/image/image.h"
#include "graphics/host_gpu/renderer/cache/streamBuffer.h"
#include <algorithm>
#include <array>
@@ -1,8 +1,8 @@
#include "graphics/host_gpu/renderer/descriptorCache.h"
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
#include "common/assert.h"
#include "common/profiler.h"
#include "graphics/shader/recompiler/ShaderIR.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include <array>
@@ -6,7 +6,7 @@
#include "common/common.h"
#include "common/threads.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/renderer/textureCache.h"
#include "graphics/host_gpu/renderer/cache/textureCache.h"
#include "graphics/host_gpu/vulkanCommon.h"
#include "graphics/shader/shaderBindings.h"
@@ -1,4 +1,4 @@
#include "graphics/host_gpu/renderer/descriptors.h"
#include "graphics/host_gpu/renderer/pipeline/descriptors.h"
#include "common/assert.h"
#include "common/common.h"
@@ -14,18 +14,18 @@
#include "graphics/guest_gpu/tile.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/hostMemory.h"
#include "graphics/host_gpu/objects/textureCommon.h"
#include "graphics/host_gpu/renderer/debug.h"
#include "graphics/host_gpu/renderer/descriptorCache.h"
#include "graphics/host_gpu/renderer/imageView.h"
#include "graphics/host_gpu/renderer/image/imageView.h"
#include "graphics/host_gpu/renderer/image/textureCommon.h"
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
#include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h"
#include "graphics/host_gpu/renderer/render.h"
#include "graphics/host_gpu/renderer/renderContext.h"
#include "graphics/host_gpu/renderer/shaderResourceBarrier.h"
#include "graphics/host_gpu/vma.h"
#include "graphics/host_gpu/vulkanCommon.h"
#include "graphics/shader/recompiler/BindingLayout.h"
#include "graphics/shader/recompiler/ResourceMaterialization.h"
#include "graphics/shader/recompiler/ShaderIR.h"
#include "graphics/shader/recompiler/ir/BindingLayout.h"
#include "graphics/shader/recompiler/ir/ResourceMaterialization.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/shader.h"
#include <algorithm>
@@ -239,11 +239,11 @@ bool IsSupportedDepthTextureEncoding(const ShaderTextureResource& descriptor, co
const uint32_t field3_expected =
(descriptor.Type() << 28u) | field3_common | descriptor.DstSelXYZW();
const uint32_t field4_expected = descriptor.Depth() | (descriptor.BaseArray5() << 16u);
const bool common = (descriptor.fields[1] & field1_reserved_mask) == 0 &&
(descriptor.fields[2] & field2_reserved_mask) == 0 &&
descriptor.fields[3] == field3_expected &&
descriptor.fields[4] == field4_expected &&
descriptor.fields[5] == field5_expected;
const bool common = (descriptor.fields[1] & field1_reserved_mask) == 0 &&
(descriptor.fields[2] & field2_reserved_mask) == 0 &&
descriptor.fields[3] == field3_expected &&
descriptor.fields[4] == field4_expected &&
descriptor.fields[5] == field5_expected;
if (!common || (descriptor.fields[6] == 0 && descriptor.fields[7] != 0)) {
return false;
}
@@ -318,8 +318,8 @@ static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::Imag
const bool valid_2d_slice =
(is_color_2d && descriptor.Depth() == 0 && descriptor.BaseArray5() == 0) ||
(is_color_2d_array && descriptor.BaseArray5() <= descriptor.Depth());
const bool is_2d = resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D &&
valid_2d_slice;
const bool is_2d =
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D && valid_2d_slice;
const bool is_2d_array =
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray &&
is_color_2d_array && descriptor.BaseArray5() <= descriptor.Depth();
@@ -333,18 +333,20 @@ static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::Imag
!Prospero::IsFmaskTextureFormat(descriptor.Format()) && (is_2d || is_2d_array) &&
TileGetBlockLayout(TileBlockFamily::Depth64KB, depth_bpe, depth_block);
const bool supported_standard_tile =
tile == Prospero::GpuEnumValue(Prospero::TileMode::kStandard4KB) &&
TileIsStandard4KBTextureSupported(descriptor.Format());
(tile == Prospero::GpuEnumValue(Prospero::TileMode::kStandard4KB) &&
TileIsStandard4KBTextureSupported(descriptor.Format())) ||
(tile == Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB) &&
TileIsStandard64KBTextureSupported(descriptor.Format()));
const bool supported_tile = tile == Prospero::GpuEnumValue(Prospero::TileMode::kLinear) ||
tile == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) ||
supported_depth_tile || supported_standard_tile;
const auto swizzle = descriptor.DstSelXYZW();
const bool supported_swizzle =
IsValidImageSwizzle(descriptor.DstSelXYZW()) &&
(descriptor.DstSelXYZW() == DstSel(4, 5, 6, 7) || !resource.read);
IsValidImageSwizzle(swizzle) &&
(swizzle == DstSel(4, 5, 6, 7) || !resource.read || resource.atomic);
const bool supported_mip_view = descriptor.BaseLevel() == 0 || is_1d || is_2d;
return (is_1d || is_1d_array || is_2d || is_2d_array || is_3d) && supported_tile &&
supported_mip_view &&
descriptor.BaseLevel() == descriptor.LastLevel() &&
supported_mip_view && descriptor.BaseLevel() == descriptor.LastLevel() &&
descriptor.LastLevel() <= descriptor.MaxMip() && descriptor.MinLod() == 0 &&
supported_swizzle && descriptor.BCSwizzle() == 0 && !descriptor.MsaaDepth();
}
@@ -375,8 +377,10 @@ void ValidateStorageTexture(const ShaderRecompiler::IR::ImageResource& resource,
const bool encoding_ok = IsSupportedStorageTextureEncoding(descriptor);
const bool uint_resource =
resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint;
const bool format_ok = Prospero::IsSupportedTextureFormat(format) &&
uint_resource == Prospero::IsUintTextureFormat(format);
const bool format_ok =
Prospero::IsSupportedTextureFormat(format) &&
uint_resource == Prospero::IsUintTextureFormat(format) &&
(!resource.atomic || format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt));
if (resource_ok && descriptor_ok && encoding_ok && format_ok && size != 0) {
return;
}
@@ -616,12 +620,12 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
resource.written);
}
const auto pixel_format = TextureGetFormat(format);
const auto pixel_format = TextureGetFormat(format);
const auto storage_view_format = SrgbStorageViewFormat(pixel_format);
const auto view_format =
storage && storage_view_format != vk::Format::eUndefined ? storage_view_format
: pixel_format;
const auto block_bytes = Prospero::BlockCompressedBytesPerBlock(format);
const auto view_format = storage && storage_view_format != vk::Format::eUndefined
? storage_view_format
: pixel_format;
const auto block_bytes = Prospero::BlockCompressedBytesPerBlock(format);
TextureCache::ImageDesc desc {};
desc.info.data = {address, size.size};
desc.info.pixel_format = pixel_format;
@@ -2,9 +2,9 @@
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_DESCRIPTORS_H_
#include "common/assert.h"
#include "graphics/host_gpu/renderer/image.h"
#include "graphics/host_gpu/renderer/image/image.h"
#include "graphics/host_gpu/vulkanCommon.h"
#include "graphics/shader/recompiler/ShaderIR.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/shaderBindings.h"
#include <cstdint>
@@ -1,4 +1,4 @@
#include "graphics/host_gpu/renderer/pipelineCache.h"
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
#include "common/assert.h"
#include "common/logging/log.h"
@@ -7,7 +7,7 @@
#include "graphics/host_gpu/renderer/colorRenderTarget.h"
#include "graphics/host_gpu/renderer/debug.h"
#include "graphics/host_gpu/renderer/depthRenderTarget.h"
#include "graphics/host_gpu/renderer/imageView.h"
#include "graphics/host_gpu/renderer/image/imageView.h"
#include "graphics/host_gpu/renderer/render.h"
#include "graphics/host_gpu/renderer/renderContext.h"
@@ -1,4 +1,4 @@
#include "graphics/host_gpu/renderer/shaderResourceBarrier.h"
#include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h"
#include "common/assert.h"
#include "graphics/shader/shader.h"
@@ -2,7 +2,7 @@
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_SHADERRESOURCEBARRIER_H_
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/shader/recompiler/ResourceMaterialization.h"
#include "graphics/shader/recompiler/ir/ResourceMaterialization.h"
#include <vector>
@@ -1,6 +1,6 @@
#include "graphics/host_gpu/renderer/shaderSubgroup.h"
#include "graphics/host_gpu/renderer/pipeline/shaderSubgroup.h"
#include "graphics/shader/recompiler/SpirvEmitter.h"
#include "graphics/shader/recompiler/emitter/SpirvEmitter.h"
namespace Libs::Graphics {
@@ -2,7 +2,7 @@
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_SHADERSUBGROUP_H_
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/shader/recompiler/ShaderIR.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
namespace Libs::Graphics {
@@ -6,14 +6,14 @@
#include "graphics/guest_gpu/gpu_defs.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/renderer/debug.h"
#include "graphics/host_gpu/renderer/descriptorCache.h"
#include "graphics/host_gpu/renderer/pipelineCache.h"
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
#include "graphics/host_gpu/renderer/render.h"
#include "graphics/host_gpu/renderer/renderContext.h"
#include "graphics/host_gpu/renderer/renderTarget.h"
#include "graphics/host_gpu/renderer/shaderSubgroup.h"
#include "graphics/host_gpu/renderer/pipeline/shaderSubgroup.h"
#include "graphics/host_gpu/vulkanCommon.h"
#include "graphics/shader/recompiler/ShaderIR.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/shader.h"
#include <algorithm>
+1 -1
View File
@@ -4,7 +4,7 @@
#include "common/abi.h"
#include "common/assert.h"
#include "common/common.h"
#include "graphics/host_gpu/renderer/descriptorCache.h"
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
#include "graphics/host_gpu/renderer/renderTarget.h"
#include "graphics/host_gpu/vulkanCommon.h"
@@ -10,17 +10,17 @@
#include "graphics/guest_gpu/graphicsRun.h"
#include "graphics/guest_gpu/hardwareContext.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/renderer/descriptorCache.h"
#include "graphics/host_gpu/renderer/descriptors.h"
#include "graphics/host_gpu/renderer/imageInfo.h"
#include "graphics/host_gpu/renderer/pipelineCache.h"
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
#include "graphics/host_gpu/renderer/pipeline/descriptors.h"
#include "graphics/host_gpu/renderer/image/imageInfo.h"
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
#include "graphics/host_gpu/renderer/render.h"
#include "graphics/host_gpu/renderer/renderContext.h"
#include "graphics/host_gpu/renderer/shaderResourceBarrier.h"
#include "graphics/host_gpu/renderer/shaderSubgroup.h"
#include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h"
#include "graphics/host_gpu/renderer/pipeline/shaderSubgroup.h"
#include "graphics/host_gpu/vulkanCommon.h"
#include "graphics/shader/recompiler/ResourceMaterialization.h"
#include "graphics/shader/recompiler/ShaderIR.h"
#include "graphics/shader/recompiler/ir/ResourceMaterialization.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/shader.h"
#include "kernel/eventQueue.h"
#include "kernel/pthread.h"
@@ -5,13 +5,13 @@
#include "common/assert.h"
#include "common/common.h"
#include "common/threads.h"
#include "graphics/host_gpu/renderer/bufferCache.h"
#include "graphics/host_gpu/renderer/cache/bufferCache.h"
#include "graphics/host_gpu/renderer/commandScheduler.h"
#include "graphics/host_gpu/renderer/descriptorCache.h"
#include "graphics/host_gpu/renderer/gpuResourceManager.h"
#include "graphics/host_gpu/renderer/pipelineCache.h"
#include "graphics/host_gpu/renderer/samplerCache.h"
#include "graphics/host_gpu/renderer/textureCache.h"
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
#include "graphics/host_gpu/renderer/cache/gpuResourceManager.h"
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
#include "graphics/host_gpu/renderer/cache/samplerCache.h"
#include "graphics/host_gpu/renderer/cache/textureCache.h"
#include "kernel/eventQueue.h"
#include <memory>
+135 -86
View File
@@ -16,17 +16,18 @@
#include "graphics/host_gpu/renderer/colorRenderTarget.h"
#include "graphics/host_gpu/renderer/debug.h"
#include "graphics/host_gpu/renderer/depthRenderTarget.h"
#include "graphics/host_gpu/renderer/descriptorCache.h"
#include "graphics/host_gpu/renderer/pipelineCache.h"
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
#include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h"
#include "graphics/host_gpu/renderer/pipeline/shaderSubgroup.h"
#include "graphics/host_gpu/renderer/render.h"
#include "graphics/host_gpu/renderer/renderContext.h"
#include "graphics/host_gpu/renderer/shaderResourceBarrier.h"
#include "graphics/host_gpu/renderer/shaderSubgroup.h"
#include "graphics/host_gpu/vulkanCommon.h"
#include "graphics/shader/recompiler/ResourceMaterialization.h"
#include "graphics/shader/recompiler/ShaderIR.h"
#include "graphics/shader/recompiler/ir/ResourceMaterialization.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/shader.h"
#include "kernel/eventQueue.h"
#include "kernel/memory.h"
#include "kernel/pthread.h"
#include "libs/errno.h"
@@ -221,8 +222,7 @@ static void LogDrawTargetState(const char* draw_name, const RenderColorInfo& col
LogMrtState(draw_name, buffer, ps_input_info);
}
static void LogDrawInputState(const RenderCommandBuffer& buffer,
const RenderColorInfo& color,
static void LogDrawInputState(const RenderCommandBuffer& buffer, const RenderColorInfo& color,
const ShaderVertexInputInfo& vs_input_info,
uint32_t index_type_and_size, uint32_t index_count,
const void* index_addr) {
@@ -499,9 +499,9 @@ struct DrawCallInfo {
};
RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderColorInfo* colors,
uint32_t color_count, RenderDepthInfo& depth) {
uint32_t color_count, RenderDepthInfo& depth) {
EXIT_IF(colors == nullptr || color_count > RENDER_COLOR_ATTACHMENTS_MAX);
auto& cache = m_context.GetTextureCache();
auto& cache = m_context.GetTextureCache();
RenderState state {};
state.width = std::numeric_limits<uint32_t>::max();
state.height = std::numeric_limits<uint32_t>::max();
@@ -512,8 +512,7 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
auto& target = colors[i];
EXIT_IF(!target.image_id);
const auto old_image = cache.ResolveOwner(target.image_id);
if (old_image == nullptr ||
(!old_image->registered && !old_image->info.data.Empty()) ||
if (old_image == nullptr || (!old_image->registered && !old_image->info.data.Empty()) ||
old_image->binding.needs_rebind) {
if (old_image != nullptr) {
old_image->binding = {};
@@ -522,7 +521,7 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
BindRenderTarget(target.image_id);
}
target.image_view = cache.FindRenderTarget(target.image_id, target.desc);
auto& image = cache.GetImage(target.image_id);
auto& image = cache.GetImage(target.image_id);
EXIT_IF(image.backing.samples != target.samples || target.image_view == nullptr);
if (attachment_samples == 0) {
attachment_samples = target.samples;
@@ -530,20 +529,19 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
EXIT("mixed color attachment sample counts are unsupported: %u and %u\n",
attachment_samples, target.samples);
}
const auto& view = target.desc.view_info;
const auto layout =
image.binding.is_bound ? vk::ImageLayout::eGeneral
: vk::ImageLayout::eColorAttachmentOptimal;
const auto& view = target.desc.view_info;
const auto layout = image.binding.is_bound ? vk::ImageLayout::eGeneral
: vk::ImageLayout::eColorAttachmentOptimal;
image.Transit(layout,
vk::AccessFlagBits2::eColorAttachmentRead |
vk::AccessFlagBits2::eColorAttachmentWrite,
ImageSubresourceRange {view.base_level, view.level_count, view.base_layer,
view.layer_count},
buffer.Handle());
state.width = std::min(state.width, target.extent.width);
state.height = std::min(state.height, target.extent.height);
state.num_layers = std::min(state.num_layers, view.layer_count);
auto& attachment = state.color_attachments[i];
state.width = std::min(state.width, target.extent.width);
state.height = std::min(state.height, target.extent.height);
state.num_layers = std::min(state.num_layers, view.layer_count);
auto& attachment = state.color_attachments[i];
attachment.image_view = target.image_view;
attachment.image_layout = layout;
attachment.clear_value = target.color_clear_value.uint32;
@@ -561,8 +559,7 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
depth.depth_meta_clear_enable =
depth.htile &&
cache.IsMetaCleared(depth.htile_buffer_vaddr, depth.desc.view_info.base_layer);
depth.depth_load_clear_enable =
depth.depth_clear_enable || depth.depth_meta_clear_enable;
depth.depth_load_clear_enable = depth.depth_clear_enable || depth.depth_meta_clear_enable;
if (depth.depth_meta_clear_enable &&
!cache.TouchMeta(depth.htile_buffer_vaddr, depth.desc.view_info.base_layer, false)) {
EXIT("failed to consume HTile clear state\n");
@@ -572,12 +569,12 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
if (attachment_samples == 0) {
attachment_samples = depth.samples;
} else if (attachment_samples != depth.samples) {
EXIT("mixed color/depth sample counts are unsupported: %u and %u\n",
attachment_samples, depth.samples);
EXIT("mixed color/depth sample counts are unsupported: %u and %u\n", attachment_samples,
depth.samples);
}
const auto layout = depth_attachment_layout(depth);
const auto writes = depth.AttachmentWriteAspects();
auto access = vk::AccessFlags2 {vk::AccessFlagBits2::eDepthStencilAttachmentRead};
auto access = vk::AccessFlags2 {vk::AccessFlagBits2::eDepthStencilAttachmentRead};
if (writes) {
access |= vk::AccessFlagBits2::eDepthStencilAttachmentWrite;
}
@@ -586,21 +583,19 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
ImageSubresourceRange {view.base_level, view.level_count, view.base_layer,
view.layer_count},
buffer.Handle());
state.width = std::min(state.width, depth.width);
state.height = std::min(state.height, depth.height);
state.num_layers = std::min(state.num_layers, view.layer_count);
const auto aspects = ImageViewOps::DepthAspectMask(depth.format);
auto& attachment = state.depth_stencil_attachment;
state.width = std::min(state.width, depth.width);
state.height = std::min(state.height, depth.height);
state.num_layers = std::min(state.num_layers, view.layer_count);
const auto aspects = ImageViewOps::DepthAspectMask(depth.format);
auto& attachment = state.depth_stencil_attachment;
attachment.image_view = depth.image_view;
attachment.image_layout = layout;
attachment.clear_value[0] = std::bit_cast<uint32_t>(depth.depth_clear_value);
attachment.clear_value[1] = depth.stencil_clear_value;
attachment.has_depth =
static_cast<bool>(aspects & vk::ImageAspectFlagBits::eDepth);
attachment.depth_clear = depth.depth_load_clear_enable;
attachment.has_stencil =
static_cast<bool>(aspects & vk::ImageAspectFlagBits::eStencil);
attachment.stencil_clear = depth.stencil_clear_enable;
attachment.has_depth = static_cast<bool>(aspects & vk::ImageAspectFlagBits::eDepth);
attachment.depth_clear = depth.depth_load_clear_enable;
attachment.has_stencil = static_cast<bool>(aspects & vk::ImageAspectFlagBits::eStencil);
attachment.stencil_clear = depth.stencil_clear_enable;
}
if (attachment_samples == 0 ||
vulkan_sample_count(attachment_samples) == vk::SampleCountFlagBits {}) {
@@ -685,6 +680,85 @@ static uint64_t VertexBufferDescriptorSize(const ShaderVertexInputBuffer& buffer
: buffer.num_records);
}
struct VertexBufferRange {
uint64_t base_address = 0;
uint64_t requested_end = 0;
uint64_t acquired_end = 0;
BufferBinding binding;
[[nodiscard]] uint64_t RequestedSize() const { return requested_end - base_address; }
};
static std::vector<BufferBinding> AcquireVertexBuffers(RenderCommandBuffer& buffer,
const ShaderVertexInputInfo& vs_input_info) {
// Collect the non-empty guest vertex ranges.
std::vector<VertexBufferRange> ranges;
ranges.reserve(vs_input_info.buffers_num);
for (int i = 0; i < vs_input_info.buffers_num; i++) {
const auto& vertex = vs_input_info.buffers[i];
const auto size = VertexBufferDescriptorSize(vertex);
if (size == 0) {
continue;
}
if (vertex.addr == 0 || size > UINT64_MAX - vertex.addr) {
EXIT("invalid vertex buffer range: addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
vertex.addr, size);
}
ranges.push_back({vertex.addr, vertex.addr + size});
}
std::ranges::sort(ranges, [](const VertexBufferRange& left, const VertexBufferRange& right) {
return left.base_address < right.base_address;
});
// Merge overlapping or touching ranges before acquiring host buffers.
std::vector<VertexBufferRange> merged_ranges;
merged_ranges.reserve(ranges.size());
for (const auto& range: ranges) {
if (!merged_ranges.empty() && merged_ranges.back().requested_end >= range.base_address) {
merged_ranges.back().requested_end =
std::max(merged_ranges.back().requested_end, range.requested_end);
continue;
}
merged_ranges.push_back(range);
}
auto& cache = buffer.GetContext().GetBufferCache();
for (auto& range: merged_ranges) {
// PPSA20298
const auto size =
Libs::LibKernel::Memory::ClampRangeSize(range.base_address, range.RequestedSize());
range.acquired_end = range.base_address + size;
range.binding = cache.ObtainBuffer(buffer, range.base_address, size);
}
// Rebuild slot bindings, offsetting non-empty slots into their acquired merged range.
std::vector<BufferBinding> bindings;
bindings.reserve(vs_input_info.buffers_num);
for (int i = 0; i < vs_input_info.buffers_num; i++) {
const auto& vertex = vs_input_info.buffers[i];
const auto size = VertexBufferDescriptorSize(vertex);
if (size == 0) {
auto owner = cache.ObtainNullBuffer();
bindings.push_back({owner, owner->Handle(), 0});
continue;
}
const auto range = std::ranges::find_if(merged_ranges, [&](const VertexBufferRange& value) {
return vertex.addr >= value.base_address && vertex.addr < value.acquired_end;
});
if (range == merged_ranges.end()) {
EXIT("vertex buffer address is outside the acquired range: addr=0x%016" PRIx64 "\n",
vertex.addr);
}
auto binding = range->binding;
binding.offset += vertex.addr - range->base_address;
bindings.push_back(std::move(binding));
}
return bindings;
}
static void SetDrawDebugPhase(RenderCommandBuffer& buffer, uint64_t submit_id,
const DrawCallInfo& draw, uint32_t phase) {
EXIT_IF(draw.name == nullptr);
@@ -736,9 +810,9 @@ static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw, bool use
}
bool RenderExecutor::PrepareDrawRenderState(uint64_t submit_id, RenderCommandBuffer& buffer,
const DrawCallInfo& draw,
uint32_t render_target_slice_offset,
bool log_setup_phases, DrawRenderState& state) {
const DrawCallInfo& draw,
uint32_t render_target_slice_offset,
bool log_setup_phases, DrawRenderState& state) {
EXIT_IF(draw.name == nullptr);
auto& ctx = buffer.GetRegisters();
@@ -823,37 +897,13 @@ static std::vector<BufferBinding> PrepareVertexBuffers(uint64_t
(void)submit_id;
LogDrawPhase(draw.name, "PrepareVertexBuffers");
std::vector<BufferBinding> bindings;
bindings.reserve(vs_input_info.buffers_num);
for (int i = 0; i < vs_input_info.buffers_num; i++) {
const auto& b = vs_input_info.buffers[i];
const auto size = VertexBufferDescriptorSize(b);
if (size == 0) {
auto owner = buffer.GetContext().GetBufferCache().ObtainNullBuffer();
bindings.push_back({owner, owner->Handle(), 0});
} else {
bindings.push_back(
buffer.GetContext().GetBufferCache().ObtainBuffer(buffer, b.addr, size));
}
}
return bindings;
return AcquireVertexBuffers(buffer, vs_input_info);
}
static void RebindVertexBuffers(RenderCommandBuffer& buffer,
const ShaderVertexInputInfo& vs_input_info,
std::vector<BufferBinding>& bindings) {
EXIT_IF(bindings.size() != static_cast<size_t>(vs_input_info.buffers_num));
for (int i = 0; i < vs_input_info.buffers_num; i++) {
const auto& vertex = vs_input_info.buffers[i];
const auto size = VertexBufferDescriptorSize(vertex);
if (size == 0) {
auto owner = buffer.GetContext().GetBufferCache().ObtainNullBuffer();
bindings[i] = {owner, owner->Handle(), 0};
} else {
bindings[i] =
buffer.GetContext().GetBufferCache().ObtainBuffer(buffer, vertex.addr, size);
}
}
bindings = AcquireVertexBuffers(buffer, vs_input_info);
}
static PreparedIndexBuffer PrepareIndexBuffer(RenderCommandBuffer& buffer,
@@ -1011,17 +1061,17 @@ static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_
}
void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, RenderCommandBuffer& buffer,
const DrawCallInfo& draw, DrawRenderState& state,
vk::PrimitiveTopology topology, const DrawEmitInfo& emit,
const DrawIndexBufferSource& index_source,
bool log_pipeline_phase, bool set_bind_debug,
bool set_auto_debug) {
const DrawCallInfo& draw, DrawRenderState& state,
vk::PrimitiveTopology topology, const DrawEmitInfo& emit,
const DrawIndexBufferSource& index_source,
bool log_pipeline_phase, bool set_bind_debug,
bool set_auto_debug) {
EXIT_IF(draw.name == nullptr);
auto& ucfg = buffer.GetUserConfig();
LogDrawPhase(draw.name, "PrepareBindings");
auto bindings = PrepareGraphicsBindings(buffer, state.vs_input_info.stage,
state.ps_input_info.stage, state.ps_active);
auto bindings = PrepareGraphicsBindings(buffer, state.vs_input_info.stage,
state.ps_input_info.stage, state.ps_active);
auto vertex_bindings = PrepareVertexBuffers(submit_id, buffer, draw, state.vs_input_info);
auto index_binding = PrepareIndexBuffer(buffer, index_source);
RebindVertexBuffers(buffer, state.vs_input_info, vertex_bindings);
@@ -1094,10 +1144,10 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, RenderCommandBuffer
}
void RenderExecutor::DrawIndex(uint64_t submit_id, RenderCommandBuffer& buffer,
uint32_t index_type_and_size, uint32_t index_count,
const void* index_addr, uint32_t flags, uint32_t type,
uint32_t instance_count, uint32_t render_target_slice_offset,
int32_t vertex_offset_add, uint32_t first_instance) {
uint32_t index_type_and_size, uint32_t index_count,
const void* index_addr, uint32_t flags, uint32_t type,
uint32_t instance_count, uint32_t render_target_slice_offset,
int32_t vertex_offset_add, uint32_t first_instance) {
KYTY_PROFILER_FUNCTION();
EXIT_IF(buffer.IsInvalid());
@@ -1228,11 +1278,10 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, RenderCommandBuffer& buffer,
}
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer,
uint32_t index_count,
uint32_t flags, uint32_t render_target_slice_offset,
uint32_t instance_count, uint32_t first_vertex,
uint32_t first_instance) {
void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer, uint32_t index_count,
uint32_t flags, uint32_t render_target_slice_offset,
uint32_t instance_count, uint32_t first_vertex,
uint32_t first_instance) {
KYTY_PROFILER_FUNCTION();
EXIT_IF(buffer.IsInvalid());
@@ -1290,7 +1339,8 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer,
instance_count, first_instance};
DrawRenderState state {};
if (!PrepareDrawRenderState(submit_id, buffer, draw, render_target_slice_offset, false, state)) {
if (!PrepareDrawRenderState(submit_id, buffer, draw, render_target_slice_offset, false,
state)) {
ResetBindings();
return;
}
@@ -1340,7 +1390,7 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, RenderCommandBuffer& buffer,
}
bool RenderExecutor::ResolveColorTargets(uint64_t submit_id, RenderCommandBuffer& buffer,
uint32_t render_target_slice_offset) {
uint32_t render_target_slice_offset) {
const auto& hw = buffer.GetRegisters();
if (hw.GetColorControl().mode != 3) {
return false;
@@ -1369,8 +1419,7 @@ bool RenderExecutor::ResolveColorTargets(uint64_t submit_id, RenderCommandBuffer
cache.MarkGpuWritten(dst.image_id);
auto& source = cache.GetImage(src.image_id);
auto& destination = cache.GetImage(dst.image_id);
destination.Resolve(source,
{src.base_mip_level, 1, src.base_array_layer, 1},
destination.Resolve(source, {src.base_mip_level, 1, src.base_array_layer, 1},
{dst.base_mip_level, 1, dst.base_array_layer, 1});
return true;
}
+1 -1
View File
@@ -5,7 +5,7 @@
#include "common/logging/log.h"
#include "common/threads.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/renderer/bufferCache.h"
#include "graphics/host_gpu/renderer/cache/bufferCache.h"
#include "graphics/host_gpu/renderer/render.h"
#include "graphics/host_gpu/renderer/renderContext.h"
#include "graphics/presentation/videoOut.h"
+1 -1
View File
@@ -12,7 +12,7 @@
#include "graphics/guest_gpu/gpu_defs.h"
#include "graphics/guest_gpu/graphicsRun.h"
#include "graphics/guest_gpu/tile.h"
#include "graphics/host_gpu/renderer/imageInfo.h"
#include "graphics/host_gpu/renderer/image/imageInfo.h"
#include "graphics/host_gpu/renderer/render.h"
#include "graphics/host_gpu/renderer/renderContext.h"
#include "graphics/presentation/presenter.h"
@@ -2,17 +2,17 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "graphics/shader/recompiler/BindingLayout.h"
#include "graphics/shader/recompiler/ResourceMaterialization.h"
#include "graphics/shader/recompiler/ResourceTracking.h"
#include "graphics/shader/recompiler/ScalarProvenance.h"
#include "graphics/shader/recompiler/ShaderCFG.h"
#include "graphics/shader/recompiler/ShaderDecoder.h"
#include "graphics/shader/recompiler/ShaderIR.h"
#include "graphics/shader/recompiler/ShaderInfoCollection.h"
#include "graphics/shader/recompiler/SpirvEmitter.h"
#include "graphics/shader/recompiler/SrtPatcher.h"
#include "graphics/shader/recompiler/SrtWalker.h"
#include "graphics/shader/recompiler/ir/BindingLayout.h"
#include "graphics/shader/recompiler/ir/ResourceMaterialization.h"
#include "graphics/shader/recompiler/ir/ResourceTracking.h"
#include "graphics/shader/recompiler/ir/ScalarProvenance.h"
#include "graphics/shader/recompiler/cfg/ShaderCFG.h"
#include "graphics/shader/recompiler/decompiler/ShaderDecoder.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/recompiler/ir/ShaderInfoCollection.h"
#include "graphics/shader/recompiler/emitter/SpirvEmitter.h"
#include "graphics/shader/recompiler/ir/SrtPatcher.h"
#include "graphics/shader/recompiler/ir/SrtWalker.h"
#include <algorithm>
#include <array>
@@ -3,7 +3,7 @@
#include "common/common.h"
#include "common/stringUtils.h"
#include "graphics/shader/recompiler/ResourceMaterialization.h"
#include "graphics/shader/recompiler/ir/ResourceMaterialization.h"
#include "graphics/shader/shader.h"
#include <optional>
@@ -1,4 +1,4 @@
#include "graphics/shader/recompiler/ShaderCFG.h"
#include "graphics/shader/recompiler/cfg/ShaderCFG.h"
#include <algorithm>
#include <fmt/format.h>
@@ -3,7 +3,7 @@
#include "common/common.h"
#include "common/stringUtils.h"
#include "graphics/shader/recompiler/ShaderDecoder.h"
#include "graphics/shader/recompiler/decompiler/ShaderDecoder.h"
#include <vector>
@@ -1,4 +1,4 @@
#include "graphics/shader/recompiler/ExportOps.h"
#include "graphics/shader/recompiler/decompiler/ExportOps.h"
#include <fmt/format.h>
@@ -1,7 +1,7 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_EXPORTOPS_H_
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_EXPORTOPS_H_
#include "graphics/shader/recompiler/ShaderDecoder.h"
#include "graphics/shader/recompiler/decompiler/ShaderDecoder.h"
namespace Libs::Graphics::ShaderRecompiler::Decoder {
@@ -1,4 +1,4 @@
#include "graphics/shader/recompiler/ImageOps.h"
#include "graphics/shader/recompiler/decompiler/ImageOps.h"
#include <algorithm>
#include <fmt/format.h>
@@ -1,7 +1,7 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_IMAGEOPS_H_
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_IMAGEOPS_H_
#include "graphics/shader/recompiler/ShaderDecoder.h"
#include "graphics/shader/recompiler/decompiler/ShaderDecoder.h"
namespace Libs::Graphics::ShaderRecompiler::Decoder {
@@ -1,4 +1,4 @@
#include "graphics/shader/recompiler/MemoryOps.h"
#include "graphics/shader/recompiler/decompiler/MemoryOps.h"
#include <fmt/format.h>
#include <iterator>
@@ -1,7 +1,7 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_MEMORYOPS_H_
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_MEMORYOPS_H_
#include "graphics/shader/recompiler/ShaderDecoder.h"
#include "graphics/shader/recompiler/decompiler/ShaderDecoder.h"
namespace Libs::Graphics::ShaderRecompiler::Decoder {
@@ -1,4 +1,4 @@
#include "graphics/shader/recompiler/ScalarAluOps.h"
#include "graphics/shader/recompiler/decompiler/ScalarAluOps.h"
#include <iterator>
@@ -1,7 +1,7 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SCALARALUOPS_H_
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SCALARALUOPS_H_
#include "graphics/shader/recompiler/ShaderDecoder.h"
#include "graphics/shader/recompiler/decompiler/ShaderDecoder.h"
namespace Libs::Graphics::ShaderRecompiler::Decoder {
@@ -1,10 +1,10 @@
#include "graphics/shader/recompiler/ShaderDecoder.h"
#include "graphics/shader/recompiler/decompiler/ShaderDecoder.h"
#include "graphics/shader/recompiler/ExportOps.h"
#include "graphics/shader/recompiler/ImageOps.h"
#include "graphics/shader/recompiler/MemoryOps.h"
#include "graphics/shader/recompiler/ScalarAluOps.h"
#include "graphics/shader/recompiler/VectorAluOps.h"
#include "graphics/shader/recompiler/decompiler/ExportOps.h"
#include "graphics/shader/recompiler/decompiler/ImageOps.h"
#include "graphics/shader/recompiler/decompiler/MemoryOps.h"
#include "graphics/shader/recompiler/decompiler/ScalarAluOps.h"
#include "graphics/shader/recompiler/decompiler/VectorAluOps.h"
#include <algorithm>
#include <bit>
@@ -1,4 +1,4 @@
#include "graphics/shader/recompiler/VectorAluOps.h"
#include "graphics/shader/recompiler/decompiler/VectorAluOps.h"
#include <fmt/format.h>
#include <iterator>
@@ -1,7 +1,7 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_VECTORALUOPS_H_
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_VECTORALUOPS_H_
#include "graphics/shader/recompiler/ShaderDecoder.h"
#include "graphics/shader/recompiler/decompiler/ShaderDecoder.h"
namespace Libs::Graphics::ShaderRecompiler::Decoder {
@@ -1,4 +1,4 @@
#include "graphics/shader/recompiler/SpirvBuilder.h"
#include "graphics/shader/recompiler/emitter/SpirvBuilder.h"
#include <cstdio>
#include <cstring>
@@ -1,7 +1,7 @@
#include "graphics/shader/recompiler/SpirvEmitter.h"
#include "graphics/shader/recompiler/emitter/SpirvEmitter.h"
#include "graphics/shader/recompiler/SrtWalker.h"
#include "graphics/shader/recompiler/spirvEmitter/spirvEmitterInternal.h"
#include "graphics/shader/recompiler/ir/SrtWalker.h"
#include "graphics/shader/recompiler/emitter/spirvEmitterInternal.h"
#include <algorithm>
#include <array>
@@ -3,7 +3,7 @@
#include "common/common.h"
#include "common/stringUtils.h"
#include "graphics/shader/recompiler/ResourceMaterialization.h"
#include "graphics/shader/recompiler/ir/ResourceMaterialization.h"
#include <vector>
@@ -1,4 +1,4 @@
#include "graphics/shader/recompiler/spirvEmitter/spirvEmitterInternal.h"
#include "graphics/shader/recompiler/emitter/spirvEmitterInternal.h"
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
@@ -1,7 +1,7 @@
#include "common/assert.h"
#include "graphics/guest_gpu/gpu_defs.h"
#include "graphics/shader/recompiler/SpirvEmitter.h"
#include "graphics/shader/recompiler/spirvEmitter/spirvEmitterInternal.h"
#include "graphics/shader/recompiler/emitter/SpirvEmitter.h"
#include "graphics/shader/recompiler/emitter/spirvEmitterInternal.h"
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
@@ -1,4 +1,4 @@
#include "graphics/shader/recompiler/spirvEmitter/spirvEmitterInternal.h"
#include "graphics/shader/recompiler/emitter/spirvEmitterInternal.h"
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
@@ -1,4 +1,4 @@
#include "graphics/shader/recompiler/spirvEmitter/spirvEmitterInternal.h"
#include "graphics/shader/recompiler/emitter/spirvEmitterInternal.h"
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
@@ -1,4 +1,4 @@
#include "graphics/shader/recompiler/spirvEmitter/spirvEmitterInternal.h"
#include "graphics/shader/recompiler/emitter/spirvEmitterInternal.h"
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
@@ -1,5 +1,5 @@
#include "common/assert.h"
#include "graphics/shader/recompiler/spirvEmitter/spirvEmitterInternal.h"
#include "graphics/shader/recompiler/emitter/spirvEmitterInternal.h"
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
@@ -1,4 +1,4 @@
#include "graphics/shader/recompiler/spirvEmitter/spirvEmitterInternal.h"
#include "graphics/shader/recompiler/emitter/spirvEmitterInternal.h"
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
@@ -3,11 +3,11 @@
#include "common/common.h"
#include "common/stringUtils.h"
#include "graphics/shader/recompiler/BindingLayout.h"
#include "graphics/shader/recompiler/ir/BindingLayout.h"
#include "graphics/shader/recompiler/BufferFormat.h"
#include "graphics/shader/recompiler/ResourceMaterialization.h"
#include "graphics/shader/recompiler/ShaderIR.h"
#include "graphics/shader/recompiler/SpirvBuilder.h"
#include "graphics/shader/recompiler/ir/ResourceMaterialization.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/recompiler/emitter/SpirvBuilder.h"
#include <algorithm>
#include <array>
@@ -1,4 +1,4 @@
#include "graphics/shader/recompiler/spirvEmitter/spirvEmitterInternal.h"
#include "graphics/shader/recompiler/emitter/spirvEmitterInternal.h"
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
@@ -1,4 +1,4 @@
#include "graphics/shader/recompiler/spirvEmitter/spirvEmitterInternal.h"
#include "graphics/shader/recompiler/emitter/spirvEmitterInternal.h"
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
@@ -1,4 +1,4 @@
#include "graphics/shader/recompiler/spirvEmitter/spirvEmitterInternal.h"
#include "graphics/shader/recompiler/emitter/spirvEmitterInternal.h"
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
@@ -1,6 +1,6 @@
#include "graphics/shader/recompiler/BindingLayout.h"
#include "graphics/shader/recompiler/ir/BindingLayout.h"
#include "graphics/shader/recompiler/ScalarProvenance.h"
#include "graphics/shader/recompiler/ir/ScalarProvenance.h"
#include <algorithm>
#include <array>
@@ -1,7 +1,7 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_BINDINGLAYOUT_H_
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_BINDINGLAYOUT_H_
#include "graphics/shader/recompiler/ShaderIR.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
namespace Libs::Graphics::ShaderRecompiler::IR {
@@ -1,4 +1,4 @@
#include "graphics/shader/recompiler/ResourceMaterialization.h"
#include "graphics/shader/recompiler/ir/ResourceMaterialization.h"
#include "graphics/guest_gpu/gpu_format.h"
#include "graphics/shader/shaderBindings.h"
@@ -1,7 +1,7 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_RESOURCEMATERIALIZATION_H_
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_RESOURCEMATERIALIZATION_H_
#include "graphics/shader/recompiler/SrtWalker.h"
#include "graphics/shader/recompiler/ir/SrtWalker.h"
namespace Libs::Graphics::ShaderRecompiler::IR {
@@ -1,6 +1,6 @@
#include "graphics/shader/recompiler/ResourceTracking.h"
#include "graphics/shader/recompiler/ir/ResourceTracking.h"
#include "graphics/shader/recompiler/ScalarProvenance.h"
#include "graphics/shader/recompiler/ir/ScalarProvenance.h"
#include <algorithm>
#include <fmt/format.h>
@@ -1,7 +1,7 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_RESOURCETRACKING_H_
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_RESOURCETRACKING_H_
#include "graphics/shader/recompiler/ShaderIR.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
namespace Libs::Graphics::ShaderRecompiler::IR {
@@ -1,4 +1,4 @@
#include "graphics/shader/recompiler/ScalarProvenance.h"
#include "graphics/shader/recompiler/ir/ScalarProvenance.h"
#include <algorithm>
#include <array>
@@ -784,10 +784,10 @@ private:
if (incoming.empty()) {
return ScalarProvenance::Undefined;
}
if (incoming.size() == 1) {
return incoming[0];
}
if (*phi == ScalarProvenance::Undefined) {
if (incoming.size() == 1) {
return incoming[0];
}
*phi = AddValue({ScalarValueOp::Phi, block.start_pc});
}
m_graph.values[*phi].phi_args = std::move(incoming);
@@ -1,7 +1,7 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SCALARPROVENANCE_H_
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SCALARPROVENANCE_H_
#include "graphics/shader/recompiler/ShaderIR.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
namespace Libs::Graphics::ShaderRecompiler::IR {
@@ -1,8 +1,8 @@
#include "graphics/shader/recompiler/ShaderIR.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "common/assert.h"
#include "graphics/shader/recompiler/BufferFormat.h"
#include "graphics/shader/recompiler/shaderIR/ShaderIRInternal.h"
#include "graphics/shader/recompiler/ir/ShaderIRInternal.h"
#include <algorithm>
#include <fmt/format.h>
@@ -3,8 +3,8 @@
#include "common/common.h"
#include "common/stringUtils.h"
#include "graphics/shader/recompiler/ShaderCFG.h"
#include "graphics/shader/recompiler/ShaderDecoder.h"
#include "graphics/shader/recompiler/cfg/ShaderCFG.h"
#include "graphics/shader/recompiler/decompiler/ShaderDecoder.h"
#include "graphics/shader/shader.h"
#include <array>
@@ -1,7 +1,7 @@
#ifndef EMULATOR_SRC_GRAPHICS_SHADER_RECOMPILER_SHADERIR_SHADERIRINTERNAL_H_
#define EMULATOR_SRC_GRAPHICS_SHADER_RECOMPILER_SHADERIR_SHADERIRINTERNAL_H_
#include "graphics/shader/recompiler/ShaderIR.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
namespace Libs::Graphics::ShaderRecompiler::IR {
@@ -1,7 +1,7 @@
#include "common/assert.h"
#include "graphics/shader/recompiler/BufferFormat.h"
#include "graphics/shader/recompiler/ShaderIR.h"
#include "graphics/shader/recompiler/shaderIR/ShaderIRInternal.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/recompiler/ir/ShaderIRInternal.h"
#include <algorithm>
#include <fmt/format.h>
@@ -1,7 +1,7 @@
#include "common/assert.h"
#include "graphics/shader/recompiler/BufferFormat.h"
#include "graphics/shader/recompiler/ShaderIR.h"
#include "graphics/shader/recompiler/shaderIR/ShaderIRInternal.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/recompiler/ir/ShaderIRInternal.h"
#include <algorithm>
#include <fmt/format.h>
@@ -1,7 +1,7 @@
#include "common/assert.h"
#include "graphics/shader/recompiler/BufferFormat.h"
#include "graphics/shader/recompiler/ShaderIR.h"
#include "graphics/shader/recompiler/shaderIR/ShaderIRInternal.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/recompiler/ir/ShaderIRInternal.h"
#include <algorithm>
#include <fmt/format.h>
@@ -1,4 +1,4 @@
#include "graphics/shader/recompiler/ShaderInfoCollection.h"
#include "graphics/shader/recompiler/ir/ShaderInfoCollection.h"
#include <algorithm>
#include <fmt/format.h>

Some files were not shown because too many files have changed in this diff Show More