diff --git a/CMakeLists.txt b/CMakeLists.txt index 5980ae42..6a435c41 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -550,6 +550,10 @@ if(BUILD_TESTING) add_test(NAME shader_recompiler_compute COMMAND $) add_test(NAME shader_recompiler_alignbyte COMMAND $ --alignbyte-only) + add_test(NAME graphics_pipeline_rasterization + COMMAND $ --polygon-mode-only) + add_test(NAME graphics_draw_offsets + COMMAND $ --draw-offset-only) add_test(NAME virtual_memory_allocation COMMAND $) add_test(NAME guest_red_zone_patcher diff --git a/src/common/alignment.h b/src/common/alignment.h new file mode 100644 index 00000000..e8ce3205 --- /dev/null +++ b/src/common/alignment.h @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2014 Jannik Vogel +// SPDX-License-Identifier: CC0-1.0 + +#pragma once + +#include +#include + +namespace Common { + +template +[[nodiscard]] constexpr T AlignUp(T value, std::size_t size) { + static_assert(std::is_unsigned_v, "T must be an unsigned value."); + auto mod{static_cast(value % size)}; + value -= mod; + return static_cast(mod == T{0} ? value : value + size); +} + +template +[[nodiscard]] constexpr T AlignDown(T value, std::size_t size) { + static_assert(std::is_unsigned_v, "T must be an unsigned value."); + return static_cast(value - value % size); +} + +} // namespace Common diff --git a/src/graphics/guest_gpu/command_processor/commandProcessor.h b/src/graphics/guest_gpu/command_processor/commandProcessor.h index 78a6de45..55fa3bad 100644 --- a/src/graphics/guest_gpu/command_processor/commandProcessor.h +++ b/src/graphics/guest_gpu/command_processor/commandProcessor.h @@ -148,7 +148,6 @@ private: CommandScheduler& GetScheduler() const { return m_renderer.GetCommandScheduler(); } CommandBuffer& CurrentBuffer() { return GetScheduler().Current(); } void CheckBuffer() const { GetScheduler().CheckActive(); } - GpuResourceManager& GetGpuResources() const { return m_renderer.GetGpuResources(); } RenderContext& m_renderer; HW::Context m_ctx; diff --git a/src/graphics/guest_gpu/graphicsRun.cpp b/src/graphics/guest_gpu/graphicsRun.cpp index 767866bc..f023e4f5 100644 --- a/src/graphics/guest_gpu/graphicsRun.cpp +++ b/src/graphics/guest_gpu/graphicsRun.cpp @@ -427,7 +427,7 @@ void CommandProcessor::DmaData(uint8_t engine, uint8_t dst_sel, uint8_t dst_cach if (!decode_gds(dst_sel, dst_gds)) { EXIT("unsupported dmaData destination selector 0x%02" PRIx8 "\n", dst_sel); } - auto& buffer_cache = GetGpuResources().GetBufferCache(); + auto& buffer_cache = m_renderer.GetBufferCache(); if (src_sel == 2) { buffer_cache.FillBuffer( dst_address_or_offset, num_bytes, @@ -603,11 +603,11 @@ bool GuestGpu::Process(Submission& submission) { } if (progressed) { if (complete) { - m_renderer.GetGpuResources().RunGarbageCollector(); + m_renderer.RunGarbageCollector(); } cp.BufferFlush(); } else if (complete) { - m_renderer.GetGpuResources().RunGarbageCollector(); + m_renderer.RunGarbageCollector(); } break; } @@ -629,16 +629,16 @@ bool GuestGpu::Process(Submission& submission) { Pm4ProcessResult::Complete; if (submission.command_execution.MadeProgress()) { if (complete) { - m_renderer.GetGpuResources().RunGarbageCollector(); + m_renderer.RunGarbageCollector(); } cp.BufferFlush(); } else if (complete) { - m_renderer.GetGpuResources().RunGarbageCollector(); + m_renderer.RunGarbageCollector(); } break; } case SubmissionType::FlipPreparation: - m_renderer.GetGpuResources().RunGarbageCollector(); + m_renderer.RunGarbageCollector(); cp.PrepareCpuFlip(submission.flip_request_id); break; } diff --git a/src/graphics/guest_gpu/tile.cpp b/src/graphics/guest_gpu/tile.cpp index 9498bc33..a8891b6d 100644 --- a/src/graphics/guest_gpu/tile.cpp +++ b/src/graphics/guest_gpu/tile.cpp @@ -1,5 +1,6 @@ #include "graphics/guest_gpu/tile.h" +#include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" #include "common/profiler.h" @@ -15,10 +16,6 @@ namespace Libs::Graphics { -static uint32_t AlignUp(uint32_t value, uint32_t alignment) { - return (value + alignment - 1u) & ~(alignment - 1u); -} - static uint32_t ShiftCeil(uint32_t value, uint32_t shift) { return static_cast((static_cast(value) + (1ull << shift) - 1ull) >> shift); } @@ -33,7 +30,7 @@ static uint32_t CalcLinearAlignedLevelPitch(uint32_t base_width, uint32_t base_h const uint32_t level_width = ShiftCeil(base_width, level); const uint32_t level_height = ShiftCeil(base_height, level); const uint32_t padded_width = - AlignUp(std::max(level_width, 1u), CalcLinearBlockWidth(bytes_per_element)); + Common::AlignUp(std::max(level_width, 1u), CalcLinearBlockWidth(bytes_per_element)); const uint64_t size = static_cast(padded_width) * std::max(level_height, 1u) * bytes_per_element; EXIT_NOT_IMPLEMENTED(size > 0xffffffffull); @@ -61,7 +58,7 @@ static uint32_t SetLinearMipChainLayout(uint32_t levels, const uint32_t* mip_pit offset += mip_size[level]; } - return AlignUp(offset, 256u); + return Common::AlignUp(offset, 256u); } struct Log2BlockDimensions { @@ -366,8 +363,8 @@ bool TileGetTiledTextureLayout(const TileSurfaceDescription& description, TileSu ((description.width >> level) + texture.texel_width - 1u) / texture.texel_width, 1u); mip.height = std::max( ((description.height >> level) + texture.texel_height - 1u) / texture.texel_height, 1u); - mip.padded_width = AlignUp(std::max(ShiftCeil(width0, level), 1u), block.block_width); - mip.padded_height = AlignUp(std::max(ShiftCeil(height0, level), 1u), block.block_height); + mip.padded_width = Common::AlignUp(std::max(ShiftCeil(width0, level), 1u), block.block_width); + mip.padded_height = Common::AlignUp(std::max(ShiftCeil(height0, level), 1u), block.block_height); mip.size = static_cast(block.block_depth) * mip.padded_width * mip.padded_height * block.bytes_per_element; result.block_slice_size += mip.size; @@ -1121,8 +1118,8 @@ static bool TileGetHtileSize(uint32_t width, uint32_t height, TileSizeAlign& hti } // Prospero HTile stores one DWORD per depth tile. Its 32 KiB allocation blocks cover // 1024x512 pixels, independently of the attachment's fragment count. - const uint64_t size = static_cast(AlignUp(width, 1024u) / 1024u) * - (AlignUp(height, 512u) / 512u) * 32768u; + const uint64_t size = static_cast(Common::AlignUp(width, 1024u) / 1024u) * + (Common::AlignUp(height, 512u) / 512u) * 32768u; if (size == 0 || size > UINT32_MAX) { return false; } @@ -1154,13 +1151,13 @@ bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch, &stencil_block_height)); const uint32_t fragments = 1u << num_fragments_log2; const uint64_t depth_bytes_total = - valid_blocks ? static_cast(AlignUp(width, depth_block_width)) * - AlignUp(height, depth_block_height) * depth_bytes * fragments + valid_blocks ? static_cast(Common::AlignUp(width, depth_block_width)) * + Common::AlignUp(height, depth_block_height) * depth_bytes * fragments : 0; const uint64_t stencil_bytes_total = stencil_format == Prospero::StencilFormat::k8UInt && valid_blocks - ? static_cast(AlignUp(width, stencil_block_width)) * - AlignUp(height, stencil_block_height) * fragments + ? static_cast(Common::AlignUp(width, stencil_block_width)) * + Common::AlignUp(height, stencil_block_height) * fragments : 0; TileSizeAlign calculated_htile {}; const bool htile_valid = !htile || TileGetHtileSize(width, height, calculated_htile); @@ -1187,8 +1184,7 @@ uint32_t TileGetRenderTargetPitch(uint32_t width, uint32_t bytes_per_element, &block_width, &block_height)) { return 0; } - const uint64_t pitch = (static_cast(width) + block_width - 1u) & - ~static_cast(block_width - 1u); + const uint64_t pitch = Common::AlignUp(static_cast(width), block_width); return pitch <= UINT32_MAX ? static_cast(pitch) : 0; } @@ -1209,8 +1205,7 @@ bool TileGetRenderTargetSize(uint32_t width, uint32_t height, uint32_t pitch, pitch != TileGetRenderTargetPitch(width, bytes_per_element, num_fragments_log2)) { return false; } - const uint64_t padded_height = (static_cast(height) + block_height - 1u) & - ~static_cast(block_height - 1u); + const uint64_t padded_height = Common::AlignUp(static_cast(height), block_height); const uint64_t size = static_cast(pitch) * padded_height * bytes_per_element * (1u << num_fragments_log2); if (size == 0 || size > UINT32_MAX) { @@ -1372,7 +1367,7 @@ uint32_t TileGetTexturePitch(Prospero::BufferFormat format, uint32_t width, TileTextureElementLayout element {}; if (TileGetTextureElementLayout(format, element) && element.texel_width == 1 && element.texel_height == 1) { - pitch = AlignUp(pitch, CalcLinearBlockWidth(element.bytes)); + pitch = Common::AlignUp(pitch, CalcLinearBlockWidth(element.bytes)); } break; } @@ -1383,7 +1378,7 @@ uint32_t TileGetTexturePitch(Prospero::BufferFormat format, uint32_t width, case Prospero::TileMode::kRenderTarget: { TileTextureBlockLayout layout {}; if (TileGetTextureBlockLayout(format, tile, false, layout)) { - pitch = AlignUp(pitch, layout.block.block_width * layout.texel_width); + pitch = Common::AlignUp(pitch, layout.block.block_width * layout.texel_width); } break; } diff --git a/src/graphics/host_gpu/graphicContext.h b/src/graphics/host_gpu/graphicContext.h index ce95fe8a..dadaa2de 100644 --- a/src/graphics/host_gpu/graphicContext.h +++ b/src/graphics/host_gpu/graphicContext.h @@ -27,7 +27,6 @@ struct GraphicContext { vk::Device device = nullptr; VmaAllocator allocator = nullptr; bool memory_budget_ext_enabled = false; - bool rt_extensions_enabled = false; bool compute_subgroup_size_control_enabled = false; bool sample_rate_shading_enabled = false; bool attachment_feedback_loop_enabled = false; @@ -99,10 +98,6 @@ struct GraphicContext { [[nodiscard]] uint64_t GetTotalMemoryBudget() const; [[nodiscard]] bool CreateImage(const vk::ImageCreateInfo& info, VulkanImage& image); void DeleteImage(VulkanImage& image); - void AppendHardwareRayTracingDeviceExtensions( - const std::vector& available_extensions, - std::vector& device_extensions); - void LoadHardwareRayTracingFunctions() const; uint32_t screen_width = 0; uint32_t screen_height = 0; diff --git a/src/graphics/host_gpu/memoryTracker.cpp b/src/graphics/host_gpu/memoryTracker.cpp index f49b84ba..4c45702b 100644 --- a/src/graphics/host_gpu/memoryTracker.cpp +++ b/src/graphics/host_gpu/memoryTracker.cpp @@ -1,5 +1,6 @@ #include "graphics/host_gpu/memoryTracker.h" +#include "common/alignment.h" #include "common/assert.h" namespace Libs::Graphics { @@ -8,9 +9,6 @@ static_assert(std::atomic::is_always_lock_free); MemoryTracker::MemoryTracker(PageManager& page_manager): m_page_manager(page_manager) { m_regions = std::make_unique[]>(REGION_COUNT); - for (size_t i = 0; i < REGION_COUNT; i++) { - m_regions[i].store(nullptr, std::memory_order_relaxed); - } } MemoryTracker::~MemoryTracker() = default; @@ -34,8 +32,8 @@ void MemoryTracker::ValidateGpuDirtyPages(const RangeSet& dirty, uint64_t vaddr, void MemoryTracker::ValidateGpuDirtyOwnership(const RangeSet& dirty, uint64_t vaddr, uint64_t size, const char* operation) { ValidateRange(vaddr, size); - const auto begin = vaddr & ~(TRACKER_PAGE_SIZE - 1); - const auto end = (vaddr + size + TRACKER_PAGE_SIZE - 1) & ~(TRACKER_PAGE_SIZE - 1); + const auto begin = Common::AlignDown(vaddr, TRACKER_PAGE_SIZE); + const auto end = Common::AlignUp(vaddr + size, TRACKER_PAGE_SIZE); for (auto page = begin; page < end; page += TRACKER_PAGE_SIZE) { const bool has_dirty_bytes = dirty.Intersects(page, TRACKER_PAGE_SIZE); if (IsRegionGpuModified(page, TRACKER_PAGE_SIZE) != has_dirty_bytes) { diff --git a/src/graphics/host_gpu/pageManager.cpp b/src/graphics/host_gpu/pageManager.cpp index 65af9f2b..00debbbd 100644 --- a/src/graphics/host_gpu/pageManager.cpp +++ b/src/graphics/host_gpu/pageManager.cpp @@ -1,5 +1,7 @@ #include "graphics/host_gpu/pageManager.h" +#include "common/alignment.h" +#include "common/virtualMemory.h" #include "graphics/host_gpu/regionDefinitions.h" #include "kernel/memory.h" @@ -32,19 +34,8 @@ constexpr uint64_t REGION_SIZE = TRACKER_REGION_SIZE; constexpr uint64_t ADDRESS_SIZE = TRACKER_ADDRESS_SIZE; constexpr uint64_t REGION_COUNT = ADDRESS_SIZE / REGION_SIZE; -#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS -// The tracker reuses Win32 memory-protection tags as internal page-state values. -// Mirror their canonical numeric values so the shared state-machine logic is identical. -constexpr uint32_t PAGE_NOACCESS = 0x01; -constexpr uint32_t PAGE_READONLY = 0x02; -constexpr uint32_t PAGE_READWRITE = 0x04; -#endif constexpr uint64_t REGION_PAGES = REGION_SIZE / PAGE_SIZE; -constexpr uint32_t NO_ACCESS_PROTECTION = PAGE_NOACCESS; -constexpr uint32_t READ_ONLY_PROTECTION = PAGE_READONLY; -constexpr uint32_t READ_WRITE_PROTECTION = PAGE_READWRITE; - [[noreturn]] void FailFast(const char* reason = nullptr) noexcept { std::fputs("PageManager fail-fast: ", stderr); std::fputs(reason != nullptr ? reason : "invalid page state", stderr); @@ -67,15 +58,6 @@ constexpr uint32_t READ_WRITE_PROTECTION = PAGE_READWRITE; std::_Exit(322); } -Common::VirtualMemory::Mode ToMemoryMode(uint32_t protection) { - switch (protection) { - case NO_ACCESS_PROTECTION: return Common::VirtualMemory::Mode::NoAccess; - case READ_ONLY_PROTECTION: return Common::VirtualMemory::Mode::Read; - case READ_WRITE_PROTECTION: return Common::VirtualMemory::Mode::ReadWrite; - default: Fatal("unmappable protection 0x%08" PRIx32, protection); - } -} - class SpinGuard final { public: explicit SpinGuard(std::atomic_flag& lock): m_lock(lock) { @@ -96,15 +78,6 @@ void ValidateRange(uint64_t vaddr, uint64_t size) { } } -uint64_t PageStart(uint64_t vaddr) { - return vaddr & ~(PAGE_SIZE - 1); -} - -uint64_t PageEnd(uint64_t vaddr, uint64_t size) { - ValidateRange(vaddr, size); - return PageStart(vaddr + size - 1) + PAGE_SIZE; -} - } // namespace struct PageManager::Impl { @@ -112,14 +85,14 @@ struct PageManager::Impl { uint8_t write_watchers : 7 = 0; uint8_t access_watchers : 1 = 0; - [[nodiscard]] uint32_t Perms() const noexcept { + [[nodiscard]] Common::VirtualMemory::Mode Perms() const noexcept { if (access_watchers != 0) { - return NO_ACCESS_PROTECTION; + return Common::VirtualMemory::Mode::NoAccess; } if (write_watchers != 0) { - return READ_ONLY_PROTECTION; + return Common::VirtualMemory::Mode::Read; } - return READ_WRITE_PROTECTION; + return Common::VirtualMemory::Mode::ReadWrite; } template @@ -183,9 +156,6 @@ struct PageManager::Impl { } #endif regions = std::make_unique[]>(REGION_COUNT); - for (uint64_t i = 0; i < REGION_COUNT; i++) { - regions[i].store(nullptr, std::memory_order_relaxed); - } } ~Impl() { @@ -220,11 +190,10 @@ struct PageManager::Impl { return ptr; } - void Protect(uint64_t vaddr, uint64_t size, uint32_t protection) noexcept { - if (!Libs::LibKernel::Memory::ProtectGuestHostMemory(vaddr, size, - ToMemoryMode(protection))) { - Fatal("address-space protection failed at 0x%016" PRIx64 ", new=0x%08" PRIx32, vaddr, - protection); + void Protect(uint64_t vaddr, uint64_t size, Common::VirtualMemory::Mode mode) noexcept { + if (!Libs::LibKernel::Memory::ProtectGuestHostMemory(vaddr, size, mode)) { + Fatal("address-space protection failed at 0x%016" PRIx64 ", mode=0x%08" PRIx32, vaddr, + static_cast(mode)); } } @@ -281,11 +250,12 @@ struct PageManager::Impl { template void UpdatePageWatchers(uint64_t vaddr, uint64_t size) { - const auto begin = PageStart(vaddr); - const auto end = PageEnd(vaddr, size); + ValidateRange(vaddr, size); + const auto begin = Common::AlignDown(vaddr, PAGE_SIZE); + const auto end = Common::AlignUp(vaddr + size, PAGE_SIZE); for (auto chunk_begin = begin; chunk_begin < end;) { - const auto chunk_end = std::min(end, (chunk_begin / REGION_SIZE + 1) * REGION_SIZE); - const auto region_base = chunk_begin / REGION_SIZE * REGION_SIZE; + const auto chunk_end = std::min(end, Common::AlignUp(chunk_begin + 1, REGION_SIZE)); + const auto region_base = Common::AlignDown(chunk_begin, REGION_SIZE); auto* region = track ? GetOrCreateRegion(chunk_begin) : FindRegion(chunk_begin); if (region == nullptr) { Fatal("untracking unknown page 0x%016" PRIx64, chunk_begin); diff --git a/src/graphics/host_gpu/rangeSet.h b/src/graphics/host_gpu/rangeSet.h index 089c8c44..ab475d55 100644 --- a/src/graphics/host_gpu/rangeSet.h +++ b/src/graphics/host_gpu/rangeSet.h @@ -6,17 +6,11 @@ #include #include #include -#include namespace Libs::Graphics { class RangeSet final { public: - struct Range { - uint64_t address = 0; - uint64_t size = 0; - }; - void Add(uint64_t address, uint64_t size) { const auto end = End(address, size); auto it = m_ranges.lower_bound(address); @@ -62,12 +56,6 @@ public: } } - [[nodiscard]] std::vector Intersections(uint64_t address, uint64_t size) const { - std::vector result; - ForEachIntersection(address, size, [&result](Range range) { result.push_back(range); }); - return result; - } - [[nodiscard]] bool Intersects(uint64_t address, uint64_t size) const { const auto end = End(address, size); auto it = m_ranges.lower_bound(address); @@ -88,7 +76,7 @@ public: } template - void ForEachIntersection(uint64_t address, uint64_t size, Func&& func) const { + void ForEachInRange(uint64_t address, uint64_t size, Func&& func) const { const auto end = End(address, size); auto it = m_ranges.upper_bound(address); if (it != m_ranges.begin()) { @@ -98,7 +86,7 @@ public: const auto begin = std::max(address, it->first); const auto last = std::min(end, it->second); if (begin < last) { - func(Range {begin, last - begin}); + func(begin, last); } } } diff --git a/src/graphics/host_gpu/regionManager.h b/src/graphics/host_gpu/regionManager.h index ee9ced6c..3eabd01f 100644 --- a/src/graphics/host_gpu/regionManager.h +++ b/src/graphics/host_gpu/regionManager.h @@ -111,56 +111,43 @@ public: bits.UnsetRange(start, end); } if constexpr (source == DirtySource::Cpu) { - UpdateCpuProtection(); + UpdateProtection(); } else { - UpdateGpuProtection(); + UpdateProtection(); } } template void ForEachModifiedRange(uint64_t vaddr, uint64_t size, Func&& func) { const auto [start, end] = GetPageRange(vaddr, size); - RegionBits mask(GetBits(), start, end); + auto& bits = GetBits(); + RegionBits mask(bits, start, end); if constexpr (clear) { - GetBits().UnsetRange(start, end); + bits.UnsetRange(start, end); + if constexpr (source == DirtySource::Cpu) { + UpdateProtection(); + } else { + UpdateProtection(); + } } - if constexpr (source == DirtySource::Cpu && clear) { - UpdateCpuProtection(); - ForEachRange(mask, std::forward(func)); - return; + for (const auto [first, last]: mask) { + func(m_cpu_addr + first * TRACKER_PAGE_SIZE, (last - first) * TRACKER_PAGE_SIZE); } - if constexpr (source == DirtySource::Gpu && clear) { - UpdateGpuProtection(); - } - ForEachRange(mask, std::forward(func)); } TrackingSpinLock lock; private: - template - void UpdateCpuProtection() { - auto mask = m_cpu_dirty ^ m_writable; - m_writable = m_cpu_dirty; + template + void UpdateProtection() { + const auto protection = is_read ? ~m_gpu_dirty : m_cpu_dirty; + auto& previous = is_read ? m_readable : m_writable; + auto mask = protection ^ previous; if (mask.None()) { return; } - m_page_manager.UpdatePageWatchersForRegion(m_cpu_addr, mask); - } - - template - void UpdateGpuProtection() { - auto readable = ~m_gpu_dirty; - auto mask = readable ^ m_readable; - m_readable = readable; - if (mask.None()) { - return; - } - if constexpr (track) { - m_page_manager.UpdatePageWatchersForRegion(m_cpu_addr, mask); - } else { - m_page_manager.UpdatePageWatchersForRegion(m_cpu_addr, mask); - } + previous = protection; + m_page_manager.UpdatePageWatchersForRegion(m_cpu_addr, mask); } template @@ -191,13 +178,6 @@ private: static_cast((offset + size + TRACKER_PAGE_SIZE - 1) / TRACKER_PAGE_SIZE)}; } - template - void ForEachRange(const RegionBits& bits, Func&& func) const { - for (const auto [start, end]: bits) { - func(m_cpu_addr + start * TRACKER_PAGE_SIZE, (end - start) * TRACKER_PAGE_SIZE); - } - } - PageManager& m_page_manager; uint64_t m_cpu_addr = 0; RegionBits m_cpu_dirty; diff --git a/src/graphics/host_gpu/renderer/cache/bufferCache.cpp b/src/graphics/host_gpu/renderer/cache/bufferCache.cpp index a616e0ba..7558390c 100644 --- a/src/graphics/host_gpu/renderer/cache/bufferCache.cpp +++ b/src/graphics/host_gpu/renderer/cache/bufferCache.cpp @@ -1,5 +1,6 @@ #include "graphics/host_gpu/renderer/cache/bufferCache.h" +#include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" #include "common/profiler.h" @@ -103,8 +104,7 @@ void BufferCache::TouchBuffer(const Buffer& buffer) { } void BufferCache::DeleteBuffer(BufferId id) { - auto* buffer = m_slot_buffers.try_get(id); - if (buffer == nullptr || buffer->is_deleted) { + if (IsBufferInvalid(id)) { return; } Unregister(id); @@ -120,12 +120,11 @@ std::pair BufferCache::DownloadEnvelope(const DownloadCopy& copy.size > copy.buffer->Size() - copy.source_offset) { EXIT("BufferCache: invalid download copy\n"); } - const auto begin = copy.source_offset & ~uint64_t {3}; - if (copy.source_offset > UINT64_MAX - copy.size || - copy.source_offset + copy.size > UINT64_MAX - 3) { + const auto begin = Common::AlignDown(copy.source_offset, 4); + if (copy.source_offset + copy.size > UINT64_MAX - 3) { EXIT("BufferCache: download copy alignment overflow\n"); } - const auto end = (copy.source_offset + copy.size + 3) & ~uint64_t {3}; + const auto end = Common::AlignUp(copy.source_offset + copy.size, 4); if (end > copy.buffer->Size()) { EXIT("BufferCache: aligned download copy exceeds its owner\n"); } @@ -147,7 +146,7 @@ void BufferCache::DownloadBufferMemory(std::span copies) { envelope_size, vk::AccessFlagBits::eMemoryWrite, vk::AccessFlags {}, vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite, vk::AccessFlagBits::eHostRead); - cursor += AlignDownload(envelope_size); + cursor += Common::AlignUp(envelope_size, DOWNLOAD_ALIGNMENT); } download.Commit(); const auto completion_tick = m_scheduler.CurrentTick(); @@ -159,7 +158,7 @@ void BufferCache::DownloadBufferMemory(std::span copies) { const auto offset = cursor + copy.source_offset - source_begin; download.Invalidate(base_offset + offset, copy.size); Libs::LibKernel::Memory::WriteBacking(copy.address, mapped + offset, copy.size); - cursor += AlignDownload(envelope_size); + cursor += Common::AlignUp(envelope_size, DOWNLOAD_ALIGNMENT); } batch.clear(); packed_size = 0; @@ -172,7 +171,7 @@ void BufferCache::DownloadBufferMemory(std::span copies) { DownloadCopy part {copy.buffer, copy.source_offset, copy.address, bytes}; const auto [source_begin, envelope_size] = DownloadEnvelope(part); (void)source_begin; - packed_size += AlignDownload(envelope_size); + packed_size += Common::AlignUp(envelope_size, DOWNLOAD_ALIGNMENT); batch.push_back(part); copy.source_offset += bytes; copy.address += bytes; @@ -252,44 +251,40 @@ void BufferCache::ReadMemory(uint64_t vaddr, uint64_t size, bool is_write) { "addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n", vaddr, size); } - m_scheduler.Context().GetGpu().SendCommandSync( - [this, vaddr, size, is_write] { ReadMemoryOnGpu(vaddr, size, is_write); }); -} + m_scheduler.Context().GetGpu().SendCommandSync([this, vaddr, size, is_write] { + if (is_write && !IsRegionRegistered(vaddr, size)) { + return; + } + auto& buffer = m_slot_buffers[FindBuffer(vaddr, size)]; -void BufferCache::ReadMemoryOnGpu(uint64_t vaddr, uint64_t size, bool is_write) { - if (is_write && !IsRegionRegistered(vaddr, size)) { - return; - } - auto& buffer = m_slot_buffers[FindBuffer(vaddr, size)]; + // Widen nearby CPU reads so they share one GPU drain. + constexpr uint64_t WindowSize = 512 * 1024; + const auto buffer_begin = buffer.CpuAddress(); + const auto buffer_end = buffer_begin + buffer.Size(); + const auto window_begin = std::max(Common::AlignDown(vaddr, WindowSize), buffer_begin); + const auto window_end = std::min(std::max(window_begin + WindowSize, vaddr + size), buffer_end); - // Widen nearby CPU reads so they share one GPU drain. - constexpr uint64_t WindowSize = 512 * 1024; - const auto buffer_begin = buffer.CpuAddress(); - const auto buffer_end = buffer_begin + buffer.Size(); - const auto window_begin = std::max(vaddr & ~(WindowSize - 1), buffer_begin); - const auto window_end = std::min(std::max(window_begin + WindowSize, vaddr + size), buffer_end); - - std::vector copies; - m_memory_tracker.ForEachDownloadRange( - window_begin, window_end - window_begin, - [&](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 { - m_gpu_modified_ranges.ForEachIntersection(address, bytes, [&](RangeSet::Range range) { - copies.push_back( - {&buffer, buffer.Offset(range.address), range.address, range.size}); + std::vector copies; + m_memory_tracker.ForEachDownloadRange( + window_begin, window_end - window_begin, + [&](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 { + m_gpu_modified_ranges.ForEachInRange(address, bytes, [&](uint64_t start, uint64_t end) { + copies.push_back({&buffer, buffer.Offset(start), start, end - start}); + }); }); - }); - if (!copies.empty()) { - DownloadBufferMemory(copies); - // The enumeration covered whole dirty pages and every exact interval on them. - m_memory_tracker.UnmarkRegionAsGpuModified(window_begin, window_end - window_begin); - } - if (is_write) { - m_memory_tracker.MarkRegionAsCpuModified(vaddr, size); - } + if (!copies.empty()) { + DownloadBufferMemory(copies); + // The enumeration covered whole dirty pages and every exact interval on them. + m_memory_tracker.UnmarkRegionAsGpuModified(window_begin, window_end - window_begin); + } + if (is_write) { + m_memory_tracker.MarkRegionAsCpuModified(vaddr, size); + } + }); } BufferId BufferCache::FindBuffer(uint64_t vaddr, uint64_t size) { @@ -340,7 +335,7 @@ BufferCache::OverlapResult BufferCache::ResolveOverlaps(uint64_t vaddr, uint64_t end = std::max(end, buffer_end); if (!has_stream_leap && (stream_score += buffer.StreamScore()) > StreamLeapThreshold) { has_stream_leap = true; - // Fix the shadPS4 bug that reserves space opposite to the incoming stream's growth. + // Reserve space in the incoming stream's direction of growth. // The old buffer extending left of the request predicts growth to the right, and vice versa. if (expands_left) { end += std::min(StreamLeapSize, PageTable::kAddressSpaceSize - end); @@ -371,8 +366,8 @@ void BufferCache::JoinOverlap(BufferId new_id, BufferId overlap_id, bool accumul BufferId BufferCache::CreateBuffer(uint64_t vaddr, uint64_t size) { EXIT_IF(m_scheduler.Current().IsInvalid()); - const auto end = (vaddr + size + CACHING_PAGESIZE - 1) & ~(CACHING_PAGESIZE - 1); - vaddr &= ~(CACHING_PAGESIZE - 1); + const auto end = Common::AlignUp(vaddr + size, CACHING_PAGESIZE); + vaddr = Common::AlignDown(vaddr, CACHING_PAGESIZE); size = end - vaddr; const auto overlap = ResolveOverlaps(vaddr, size); @@ -485,17 +480,16 @@ std::pair BufferCache::ObtainBuffer(uint64_t vaddr, uint64_t } } - auto* buffer = m_slot_buffers.try_get(id); - if (buffer == nullptr || buffer->is_deleted || !buffer->IsInBounds(vaddr, size)) { - id = FindBuffer(vaddr, size); - buffer = &m_slot_buffers[id]; + if (IsBufferInvalid(id) || !m_slot_buffers[id].IsInBounds(vaddr, size)) { + id = FindBuffer(vaddr, size); } - TouchBuffer(*buffer); - (void)SynchronizeBuffer(*buffer, vaddr, size, is_written, is_texel_buffer); + auto& buffer = m_slot_buffers[id]; + TouchBuffer(buffer); + (void)SynchronizeBuffer(buffer, vaddr, size, is_written, is_texel_buffer); if (is_written) { m_gpu_modified_ranges.Add(vaddr, size); } - return {buffer, buffer->Offset(vaddr)}; + return {&buffer, buffer.Offset(vaddr)}; } std::pair BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t size) { @@ -547,9 +541,7 @@ void BufferCache::FillBuffer(uint64_t vaddr, uint64_t size, uint32_t value, bool } m_texture_cache.InvalidateMemoryFromGPU(vaddr, size); - const auto id = FindBuffer(vaddr, size); - auto [dst, dst_offset] = ObtainBuffer(vaddr, size, true, true, id); - EXIT_IF(dst == nullptr); + auto [dst, dst_offset] = ObtainBuffer(vaddr, size, true, true); dst->Fill(dst_offset, size, value); } @@ -583,10 +575,6 @@ void BufferCache::CopyBuffer(uint64_t dst_vaddr, uint64_t src_vaddr, uint64_t si : std::pair {&m_gds_buffer, src_vaddr}; auto [dst, dst_offset] = dst_memory ? ObtainBuffer(dst_vaddr, size, true, true, dst_id) : std::pair {&m_gds_buffer, dst_vaddr}; - EXIT_IF(src == nullptr || dst == nullptr); - if (src == dst && src_offset < dst_offset + size && dst_offset < src_offset + size) { - EXIT("BufferCache: resolved Vulkan copy ranges overlap\n"); - } dst->CopyFrom(command, *src, src_offset, dst_offset, size); } @@ -649,10 +637,10 @@ void BufferCache::RunGarbageCollector() { dirty_size, "garbage collection"); }, [&](uint64_t dirty_address, uint64_t dirty_size) noexcept { - m_gpu_modified_ranges.ForEachIntersection( - dirty_address, dirty_size, [&](RangeSet::Range range) { - copies.push_back({&buffer, range.address - buffer.CpuAddress(), - range.address, range.size}); + m_gpu_modified_ranges.ForEachInRange( + dirty_address, dirty_size, [&](uint64_t start, uint64_t end) { + copies.push_back({&buffer, start - buffer.CpuAddress(), start, + end - start}); }); }); dirty_buffers.push_back(id); diff --git a/src/graphics/host_gpu/renderer/cache/bufferCache.h b/src/graphics/host_gpu/renderer/cache/bufferCache.h index 40f9db0a..924f9edf 100644 --- a/src/graphics/host_gpu/renderer/cache/bufferCache.h +++ b/src/graphics/host_gpu/renderer/cache/bufferCache.h @@ -74,6 +74,11 @@ public: private: friend struct BufferCacheTestAccess; + bool IsBufferInvalid(BufferId id) const { + const auto* buffer = m_slot_buffers.try_get(id); + return buffer == nullptr || buffer->is_deleted; + } + using BufferMap = std::map; struct OverlapResult { BufferMap::iterator first; @@ -87,9 +92,6 @@ private: using PageTable = MultiLevelPageTable; static_assert(CACHING_PAGESIZE == (uint64_t {1} << PageTable::kPageBits)); static constexpr uint64_t DOWNLOAD_ALIGNMENT = 64; - [[nodiscard]] static constexpr uint64_t AlignDownload(uint64_t size) noexcept { - return (size + DOWNLOAD_ALIGNMENT - 1) & ~(DOWNLOAD_ALIGNMENT - 1); - } [[nodiscard]] static std::pair DownloadEnvelope(const DownloadCopy& copy); void WriteDataBuffer(Buffer& buffer, uint64_t address, const void* source, uint64_t size); void TouchBuffer(const Buffer& buffer); @@ -107,7 +109,6 @@ private: uint64_t total_size); [[nodiscard]] bool SynchronizeBufferFromImage(Buffer& buffer, uint64_t vaddr, uint64_t size); void DownloadBufferMemory(std::span copies); - void ReadMemoryOnGpu(uint64_t vaddr, uint64_t size, bool is_write); GraphicContext& m_graphics; CommandScheduler& m_scheduler; diff --git a/src/graphics/host_gpu/renderer/cache/gpuResourceManager.cpp b/src/graphics/host_gpu/renderer/cache/gpuResourceManager.cpp deleted file mode 100644 index 8674f258..00000000 --- a/src/graphics/host_gpu/renderer/cache/gpuResourceManager.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include "graphics/host_gpu/renderer/cache/gpuResourceManager.h" - -#include "common/assert.h" -#include "graphics/guest_gpu/graphicsRun.h" -#include "graphics/host_gpu/renderer/commandScheduler.h" -namespace Libs::Graphics { - -GpuResourceManager::GpuResourceManager(GraphicContext& graphics, CommandScheduler& scheduler) - : m_scheduler(scheduler), m_buffer_cache(graphics, scheduler, m_page_manager, m_texture_cache), - m_texture_cache(graphics, scheduler, m_page_manager, m_buffer_cache) {} - -GpuResourceManager::~GpuResourceManager() = default; - -bool GpuResourceManager::HandleFault(PageFaultAccess access, uint64_t fault_vaddr) noexcept { - // The host reports the faulting byte, not the instruction's access width. Both caches - // resolve its page; guessing a width can cross the end of a valid guest mapping. - constexpr uint64_t fault_size = 1; - if (!IsMapped(fault_vaddr, fault_size)) { - return false; - } - 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); - } - return true; -} - -bool GpuResourceManager::InvalidateMemory(uint64_t vaddr, uint64_t size) { - if (!IsMapped(vaddr, size)) { - return false; - } - m_buffer_cache.InvalidateMemory(vaddr, size); - m_texture_cache.InvalidateMemory(vaddr, size); - return true; -} - -bool GpuResourceManager::IsMapped(uint64_t vaddr, uint64_t size) const noexcept { - if (!GuestRange {vaddr, size}.Valid()) { - 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) { - { - std::lock_guard lock(m_mapped_ranges_mutex); - m_mapped_ranges.Add(vaddr, size); - } -} - -void GpuResourceManager::UnmapMemory(uint64_t vaddr, uint64_t size) { - if (CommandScheduler::InDeferredOperation()) { - EXIT("unsupported memory unmap from an asynchronous GPU completion, " - "addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n", - vaddr, size); - } - const auto unmap = [this, vaddr, size] { - if (m_scheduler.Active()) { - const auto tick = m_scheduler.CurrentTick(); - m_scheduler.Finish(); - m_scheduler.WaitPriorityOperations(tick); - } - m_buffer_cache.InvalidateMemory(vaddr, size); - m_texture_cache.UnmapMemory(vaddr, size); - std::lock_guard lock(m_mapped_ranges_mutex); - m_mapped_ranges.Subtract(vaddr, size); - }; - if (m_gpu == nullptr) { - unmap(); - return; - } - m_gpu->SendCommandSync(unmap); -} - -void GpuResourceManager::PrepareBda() { - std::shared_lock lock(m_mapped_ranges_mutex); - m_mapped_ranges.ForEach([this](uint64_t start, uint64_t end) { - m_buffer_cache.SynchronizeBuffersInRange(start, end - start); - }); - m_fault_process_pending = true; -} - -void GpuResourceManager::RunGarbageCollector() { - if (m_fault_process_pending) { - m_fault_process_pending = false; - m_buffer_cache.ProcessFaultBuffer(); - } - m_texture_cache.ProcessDownloadImages(); - m_texture_cache.RunGarbageCollector(); - m_buffer_cache.RunGarbageCollector(); -} - -} // namespace Libs::Graphics diff --git a/src/graphics/host_gpu/renderer/cache/gpuResourceManager.h b/src/graphics/host_gpu/renderer/cache/gpuResourceManager.h deleted file mode 100644 index 44cf35d1..00000000 --- a/src/graphics/host_gpu/renderer/cache/gpuResourceManager.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_GPURESOURCEMANAGER_H_ -#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_GPURESOURCEMANAGER_H_ - -#include "common/abi.h" -#include "common/common.h" -#include "graphics/host_gpu/pageManager.h" -#include "graphics/host_gpu/renderer/cache/bufferCache.h" -#include "graphics/host_gpu/renderer/cache/textureCache.h" - -#include -#include - -namespace Libs::Graphics { - -class CommandScheduler; -class GuestGpu; - -class GpuResourceManager { -public: - GpuResourceManager(GraphicContext& graphics, CommandScheduler& scheduler); - ~GpuResourceManager(); - KYTY_CLASS_NO_COPY(GpuResourceManager); - - [[nodiscard]] BufferCache& GetBufferCache() { return m_buffer_cache; } - [[nodiscard]] TextureCache& GetTextureCache() { return m_texture_cache; } - void SetGpu(GuestGpu* gpu) noexcept { m_gpu = gpu; } - - [[nodiscard]] bool HandleFault(PageFaultAccess access, uint64_t fault_vaddr) noexcept; - [[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); - void UnmapMemory(uint64_t vaddr, uint64_t size); - void PrepareBda(); - void RunGarbageCollector(); - -private: - PageManager m_page_manager; - CommandScheduler& m_scheduler; - BufferCache m_buffer_cache; - TextureCache m_texture_cache; - mutable std::shared_mutex m_mapped_ranges_mutex; - RangeSet m_mapped_ranges; - GuestGpu* m_gpu = nullptr; - bool m_fault_process_pending = false; -}; - -} // namespace Libs::Graphics - -#endif // EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_GPURESOURCEMANAGER_H_ diff --git a/src/graphics/host_gpu/renderer/cache/streamBuffer.cpp b/src/graphics/host_gpu/renderer/cache/streamBuffer.cpp index c6872f66..b0fc5fb4 100644 --- a/src/graphics/host_gpu/renderer/cache/streamBuffer.cpp +++ b/src/graphics/host_gpu/renderer/cache/streamBuffer.cpp @@ -1,12 +1,12 @@ #include "graphics/host_gpu/renderer/cache/streamBuffer.h" +#include "common/alignment.h" #include "common/assert.h" #include "common/profiler.h" #include "graphics/host_gpu/graphicContext.h" #include "graphics/host_gpu/renderer/commandScheduler.h" #include -#include #include #include @@ -45,16 +45,11 @@ constexpr size_t WATCHES_RESERVE_CHUNK = 0x1000; result = value; return true; } - const auto remainder = value % alignment; - if (remainder == 0) { - result = value; - return true; - } - const auto increment = alignment - remainder; - if (value > std::numeric_limits::max() - increment) { + const auto aligned = Common::AlignUp(value, alignment); + if (aligned < value) { return false; } - result = value + increment; + result = aligned; return true; } @@ -124,12 +119,6 @@ bool Buffer::IsInBounds(uint64_t address, uint64_t size) const noexcept { return address >= m_cpu_address && size <= Size() && address - m_cpu_address <= Size() - size; } -void Buffer::Write(uint64_t offset, const void* source, uint64_t size) { - EXIT_IF(source == nullptr || m_mapped.empty() || offset > Size() || size > Size() - offset); - std::memcpy(m_mapped.data() + offset, source, static_cast(size)); - Flush(offset, size); -} - void Buffer::Flush(uint64_t offset, uint64_t size) { EXIT_IF(m_mapped.empty() || offset > Size() || size > Size() - offset); if (!IsCoherent() && size != 0) { diff --git a/src/graphics/host_gpu/renderer/cache/streamBuffer.h b/src/graphics/host_gpu/renderer/cache/streamBuffer.h index 78170473..9e890485 100644 --- a/src/graphics/host_gpu/renderer/cache/streamBuffer.h +++ b/src/graphics/host_gpu/renderer/cache/streamBuffer.h @@ -55,7 +55,6 @@ public: [[nodiscard]] bool IsInBounds(uint64_t address, uint64_t size) const noexcept; void IncreaseStreamScore(int score) noexcept { stream_score += score; } [[nodiscard]] int StreamScore() const noexcept { return stream_score; } - void Write(uint64_t offset, const void* source, uint64_t size); void Flush(uint64_t offset, uint64_t size); void Invalidate(uint64_t offset, uint64_t size); void CopyFrom(CommandBuffer& command, const Buffer& source, uint64_t source_offset, diff --git a/src/graphics/host_gpu/renderer/cache/textureCache.cpp b/src/graphics/host_gpu/renderer/cache/textureCache.cpp index cfc94f5d..b94d1211 100644 --- a/src/graphics/host_gpu/renderer/cache/textureCache.cpp +++ b/src/graphics/host_gpu/renderer/cache/textureCache.cpp @@ -1,5 +1,6 @@ #include "graphics/host_gpu/renderer/cache/textureCache.h" +#include "common/alignment.h" #include "common/assert.h" #include "common/emulatorConfig.h" #include "common/logging/log.h" @@ -266,9 +267,9 @@ void TextureCache::RegisterImage(ImageId id) { if (!ImagePageTable::TryGetPageRange(image.info.data.address, image.info.data.size, pages)) { EXIT("TextureCache: image registration is outside the guest address space\n"); } - for (size_t page = pages.first; page < pages.last_exclusive; ++page) { + ForEachPage(image.info.data.address, image.info.data.size, [this, id](uint64_t page) { m_image_page_table[page].push_back(id); - } + }); image.registered = true; image.lru_id = m_lru_cache.Insert(id, m_gc_tick); m_total_used_memory += image.AccountedSize(); @@ -284,12 +285,12 @@ void TextureCache::UnregisterImage(ImageId id) { if (!ImagePageTable::TryGetPageRange(image.info.data.address, image.info.data.size, pages)) { EXIT("TextureCache: registered image is outside the guest address space\n"); } - for (size_t page = pages.first; page < pages.last_exclusive; ++page) { + ForEachPage(image.info.data.address, image.info.data.size, [this, id](uint64_t page) { auto* owners = m_image_page_table.Find(page); if (owners == nullptr || !owners->Erase(id)) { EXIT("TextureCache: image missing from page owner index\n"); } - } + }); m_lru_cache.Free(image.lru_id); const auto accounted = image.AccountedSize(); if (accounted > m_total_used_memory) { @@ -312,11 +313,7 @@ void TextureCache::DeleteImage(ImageId id) { } }); for (const auto association: associations) { - auto& associated = m_slot_images[association]; - if (associated.IsGpuModified()) { - associated.ClearGpuModified(); - } - DeleteImage(association); + FreeImage(association); } } if (image->IsGpuModified()) { @@ -443,7 +440,7 @@ void TextureCache::UntrackImageHead(ImageId id) { if (!image.IsTracked() || begin < image.track_addr) { return; } - const auto address = (begin + TRACKER_PAGE_SIZE) & ~(TRACKER_PAGE_SIZE - 1); + const auto address = Common::AlignDown(begin + TRACKER_PAGE_SIZE, TRACKER_PAGE_SIZE); const auto size = address - begin; image.track_addr = address; if (image.track_addr == image.track_addr_end) { @@ -460,7 +457,7 @@ void TextureCache::UntrackImageTail(ImageId id) { if (!image.IsTracked() || image.track_addr_end < end) { return; } - const auto address = end & ~(TRACKER_PAGE_SIZE - 1); + const auto address = Common::AlignDown(end, TRACKER_PAGE_SIZE); const auto size = end - address; image.track_addr_end = address; if (image.track_addr == image.track_addr_end) { @@ -494,10 +491,10 @@ TextureCache::ImageIds TextureCache::FindImagesInRegion(uint64_t address, uint64 } ImageIds result; - for (size_t page = pages.first; page < pages.last_exclusive; ++page) { + ForEachPage(address, size, [&](uint64_t page) { const auto* owners = m_image_page_table.Find(page); if (owners == nullptr) { - continue; + return; } owners->ForEach([&](ImageId id) { auto* image = m_slot_images.try_get(id); @@ -512,7 +509,7 @@ TextureCache::ImageIds TextureCache::FindImagesInRegion(uint64_t address, uint64 result.push_back(id); } }); - } + }); return result; } @@ -1386,9 +1383,6 @@ vk::ImageView TextureCache::FindTexture(ImageId id, const ImageDesc& desc) { case BindingType::Texture: break; case BindingType::Storage: if (!image.info.data.Empty()) { - if (!image.registered || image.depth_id) { - EXIT("TextureCache: cannot acquire an unavailable storage image\n"); - } CommitGpuWrite(image); } TrackImageDownload(id, image); @@ -1851,8 +1845,8 @@ bool TextureCache::IsRegionGpuModified(uint64_t address, uint64_t size) { } 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); + const auto page_begin = Common::AlignDown(address, TRACKER_PAGE_SIZE); + const auto page_end = Common::AlignUp(address + size, TRACKER_PAGE_SIZE); for (const auto id: FindImagesInRegion(address, size, true)) { auto owner = m_slot_images.try_get(id); if (owner == nullptr) { @@ -1970,10 +1964,7 @@ void TextureCache::UnmapMemory(uint64_t address, uint64_t size) { if (owner == nullptr) { continue; } - if (owner->IsGpuModified()) { - owner->ClearGpuModified(); - } - DeleteImage(id); + FreeImage(id); } } @@ -2019,9 +2010,8 @@ void TextureCache::RunGarbageCollector() { if (safe && !TryDownloadImage(id)) { continue; } - owner->ClearGpuModified(); } - DeleteImage(id); + FreeImage(id); if (m_total_used_memory < m_critical_gc_memory && aggressive) { deletions >>= 2; aggressive = false; diff --git a/src/graphics/host_gpu/renderer/cache/textureCache.h b/src/graphics/host_gpu/renderer/cache/textureCache.h index dc775adc..654107c8 100644 --- a/src/graphics/host_gpu/renderer/cache/textureCache.h +++ b/src/graphics/host_gpu/renderer/cache/textureCache.h @@ -13,6 +13,7 @@ #include "graphics/host_gpu/renderer/image/tiler.h" #include +#include #include #include #include @@ -102,6 +103,23 @@ private: using ImageIds = InlinePageOwnerList; using ImagePageTable = MultiLevelPageTable; + // Callers have validated the nonempty 40-bit range with TryGetPageRange. + template + static void ForEachPage(uint64_t address, size_t size, Func&& func) { + using FuncReturn = typename std::invoke_result::type; + static constexpr bool RETURNS_BOOL = std::is_same_v; + const uint64_t page_end = (address + size - 1) >> ImagePageTable::kPageBits; + for (uint64_t page = address >> ImagePageTable::kPageBits; page <= page_end; ++page) { + if constexpr (RETURNS_BOOL) { + if (func(page)) { + break; + } + } else { + func(page); + } + } + } + [[nodiscard]] ImageId InsertImage(const ImageInfo& info); [[nodiscard]] ImageId GetNullImage(const ImageDesc& desc); void RegisterImage(ImageId id); diff --git a/src/graphics/host_gpu/renderer/context.cpp b/src/graphics/host_gpu/renderer/context.cpp index 545cff9b..d71929fb 100644 --- a/src/graphics/host_gpu/renderer/context.cpp +++ b/src/graphics/host_gpu/renderer/context.cpp @@ -61,11 +61,11 @@ void CommandBuffer::SetDebugInfo(uint32_t op, uint64_t submit_id, uint32_t arg0, } void CommandBuffer::BeginRendering(const RenderState& state) const { - EXIT_IF(state.width == 0 || state.height == 0 || state.num_layers == 0 || - state.num_color_attachments > RENDER_COLOR_ATTACHMENTS_MAX); if (m_rendering && m_render_state == state) { return; } + EXIT_IF(state.width == 0 || state.height == 0 || state.num_layers == 0 || + state.num_color_attachments > RENDER_COLOR_ATTACHMENTS_MAX); EndRendering(); std::array colors {}; diff --git a/src/graphics/host_gpu/renderer/debug.cpp b/src/graphics/host_gpu/renderer/debug.cpp index 6d7ee539..fe220707 100644 --- a/src/graphics/host_gpu/renderer/debug.cpp +++ b/src/graphics/host_gpu/renderer/debug.cpp @@ -4,7 +4,6 @@ #include "common/common.h" #include "common/emulatorConfig.h" #include "common/logging/log.h" -#include "common/stringUtils.h" #include "graphics/guest_gpu/gpu_defs.h" #include "graphics/guest_gpu/hardwareContext.h" #include "graphics/host_gpu/renderer/render.h" @@ -89,91 +88,75 @@ void uc_check(const HW::UserConfig& uc) { EXIT_NOT_IMPLEMENTED(user_en.vgpr3 != false); } -void sh_print(const char* func, const HW::Shader& /*uc*/) { - LOGF("%s\n", func); -} +std::string rt_print(const char* func, const HW::RenderTarget& rt) { + std::string dst; + dst.reserve(4096); -std::vector rt_print(const char* func, const HW::RenderTarget& rt) { - std::vector dst; - dst.reserve(40); + dst += fmt::format("{}\n", func); - dst.push_back(fmt::format("{}\n", func)); + dst += fmt::format("\t base.addr = 0x{:016x}\n", rt.base.addr); + dst += fmt::format("\t view.base_array_slice_index = 0x{:08x}\n", + rt.view.base_array_slice_index); + dst += fmt::format("\t view.last_array_slice_index = 0x{:08x}\n", + rt.view.last_array_slice_index); + dst += fmt::format("\t view.current_mip_level = 0x{:08x}\n", rt.view.current_mip_level); + dst += fmt::format("\t info.fmask_compression_enable = {}\n", + rt.info.fmask_compression_enable ? "true" : "false"); - dst.push_back(fmt::format("\t base.addr = 0x{:016x}\n", rt.base.addr)); - dst.push_back(fmt::format("\t view.base_array_slice_index = 0x{:08x}\n", - rt.view.base_array_slice_index)); - dst.push_back(fmt::format("\t view.last_array_slice_index = 0x{:08x}\n", - rt.view.last_array_slice_index)); - dst.push_back( - fmt::format("\t view.current_mip_level = 0x{:08x}\n", rt.view.current_mip_level)); - dst.push_back(fmt::format("\t info.fmask_compression_enable = {}\n", - rt.info.fmask_compression_enable ? "true" : "false")); + dst += fmt::format("\t info.fmask_data_compression_disable = {}\n", + rt.info.fmask_data_compression_disable ? "true" : "false"); + dst += fmt::format("\t info.fmask_one_frag_mode = {}\n", + rt.info.fmask_one_frag_mode ? "true" : "false"); - // dst.push_back(fmt::format("\t info.fmask_compression_mode = 0x{:08x}\n", // - // rt.info.fmask_compression_mode)); - dst.push_back(fmt::format("\t info.fmask_data_compression_disable = {}\n", - rt.info.fmask_data_compression_disable ? "true" : "false")); - dst.push_back(fmt::format("\t info.fmask_one_frag_mode = {}\n", - rt.info.fmask_one_frag_mode ? "true" : "false")); - - dst.push_back(fmt::format("\t info.cmask_fast_clear_enable = {}\n", - rt.info.cmask_fast_clear_enable ? "true" : "false")); - dst.push_back(fmt::format("\t info.dcc_compression_enable = {}\n", - rt.info.dcc_compression_enable ? "true" : "false")); - dst.push_back(fmt::format("\t info.format = 0x{:08x}\n", - static_cast(rt.info.format))); - dst.push_back(fmt::format("\t info.channel_type = 0x{:08x}\n", - static_cast(rt.info.channel_type))); - dst.push_back(fmt::format("\t info.channel_order = 0x{:08x}\n", - static_cast(rt.info.channel_order))); - dst.push_back(fmt::format("\t info.blend_bypa = {}\n", - rt.info.blend_bypass ? "true" : "false")); - dst.push_back(fmt::format("\t info.blend_clamp = {}\n", - rt.info.blend_clamp ? "true" : "false")); - dst.push_back(fmt::format("\t info.round_mode = {}\n", - rt.info.round_mode ? "true" : "false")); - dst.push_back(fmt::format("\t attrib.force_dest_alpha_to_one = {}\n", - rt.attrib.force_dest_alpha_to_one ? "true" : "false")); - dst.push_back( - fmt::format("\t attrib.num_samples = 0x{:08x}\n", rt.attrib.num_samples)); - dst.push_back( - fmt::format("\t attrib.num_fragments = 0x{:08x}\n", rt.attrib.num_fragments)); - dst.push_back(fmt::format("\t attrib2.width = 0x{:08x}\n", rt.attrib2.width)); - dst.push_back( - fmt::format("\t attrib2.height = 0x{:08x}\n", rt.attrib2.height)); - dst.push_back( - fmt::format("\t attrib2.num_mip_levels = 0x{:08x}\n", rt.attrib2.num_mip_levels)); - dst.push_back(fmt::format("\t attrib3.depth = 0x{:08x}\n", rt.attrib3.depth)); - dst.push_back(fmt::format("\t attrib3.tile_mode = 0x{:08x}\n", - static_cast(rt.attrib3.tile_mode))); - dst.push_back( - fmt::format("\t attrib3.dimension = 0x{:08x}\n", rt.attrib3.dimension)); - dst.push_back(fmt::format("\t attrib3.metadata_pipe_aligned = {}\n", - rt.attrib3.metadata_pipe_aligned ? "true" : "false")); - dst.push_back(fmt::format("\t attrib3.write_vrs_rate_hint_to_cmask = {}\n", - rt.attrib3.write_vrs_rate_hint_to_cmask ? "true" : "false")); - dst.push_back(fmt::format("\t dcc.max_uncompressed_block_size = 0x{:08x}\n", - rt.dcc.max_uncompressed_block_size)); - dst.push_back(fmt::format("\t dcc.max_compressed_block_size = 0x{:08x}\n", - rt.dcc.max_compressed_block_size)); - dst.push_back( - fmt::format("\t dcc.color_transform = 0x{:08x}\n", rt.dcc.color_transform)); - dst.push_back(fmt::format("\t dcc.overwrite_combiner_disable = {}\n", - rt.dcc.overwrite_combiner_disable ? "true" : "false")); - dst.push_back(fmt::format("\t dcc.independent_block_size = 0x{:02x}\n", - static_cast(rt.dcc.independent_block_size))); - dst.push_back(fmt::format("\t data_write_on_dcc_clear_to_reg = {}\n", - rt.dcc.data_write_on_dcc_clear_to_reg ? "true" : "false")); - dst.push_back(fmt::format("\t dcc.dcc_clear_key_enable = {}\n", - rt.dcc.dcc_clear_key_enable ? "true" : "false")); - dst.push_back(fmt::format("\t cmask.addr = 0x{:016x}\n", rt.cmask.addr)); - dst.push_back(fmt::format("\t fmask.addr = 0x{:016x}\n", rt.fmask.addr)); - dst.push_back( - fmt::format("\t clear_word0.word0 = 0x{:08x}\n", rt.clear_word0.word0)); - dst.push_back( - fmt::format("\t clear_word1.word1 = 0x{:08x}\n", rt.clear_word1.word1)); - dst.push_back( - fmt::format("\t dcc_addr.addr = 0x{:016x}\n", rt.dcc_addr.addr)); + dst += fmt::format("\t info.cmask_fast_clear_enable = {}\n", + rt.info.cmask_fast_clear_enable ? "true" : "false"); + dst += fmt::format("\t info.dcc_compression_enable = {}\n", + rt.info.dcc_compression_enable ? "true" : "false"); + dst += fmt::format("\t info.format = 0x{:08x}\n", + static_cast(rt.info.format)); + dst += fmt::format("\t info.channel_type = 0x{:08x}\n", + static_cast(rt.info.channel_type)); + dst += fmt::format("\t info.channel_order = 0x{:08x}\n", + static_cast(rt.info.channel_order)); + dst += fmt::format("\t info.blend_bypa = {}\n", + rt.info.blend_bypass ? "true" : "false"); + dst += fmt::format("\t info.blend_clamp = {}\n", + rt.info.blend_clamp ? "true" : "false"); + dst += fmt::format("\t info.round_mode = {}\n", + rt.info.round_mode ? "true" : "false"); + dst += fmt::format("\t attrib.force_dest_alpha_to_one = {}\n", + rt.attrib.force_dest_alpha_to_one ? "true" : "false"); + dst += fmt::format("\t attrib.num_samples = 0x{:08x}\n", rt.attrib.num_samples); + dst += fmt::format("\t attrib.num_fragments = 0x{:08x}\n", rt.attrib.num_fragments); + dst += fmt::format("\t attrib2.width = 0x{:08x}\n", rt.attrib2.width); + dst += fmt::format("\t attrib2.height = 0x{:08x}\n", rt.attrib2.height); + dst += fmt::format("\t attrib2.num_mip_levels = 0x{:08x}\n", rt.attrib2.num_mip_levels); + dst += fmt::format("\t attrib3.depth = 0x{:08x}\n", rt.attrib3.depth); + dst += fmt::format("\t attrib3.tile_mode = 0x{:08x}\n", + static_cast(rt.attrib3.tile_mode)); + dst += fmt::format("\t attrib3.dimension = 0x{:08x}\n", rt.attrib3.dimension); + dst += fmt::format("\t attrib3.metadata_pipe_aligned = {}\n", + rt.attrib3.metadata_pipe_aligned ? "true" : "false"); + dst += fmt::format("\t attrib3.write_vrs_rate_hint_to_cmask = {}\n", + rt.attrib3.write_vrs_rate_hint_to_cmask ? "true" : "false"); + dst += fmt::format("\t dcc.max_uncompressed_block_size = 0x{:08x}\n", + rt.dcc.max_uncompressed_block_size); + dst += fmt::format("\t dcc.max_compressed_block_size = 0x{:08x}\n", + rt.dcc.max_compressed_block_size); + dst += fmt::format("\t dcc.color_transform = 0x{:08x}\n", rt.dcc.color_transform); + dst += fmt::format("\t dcc.overwrite_combiner_disable = {}\n", + rt.dcc.overwrite_combiner_disable ? "true" : "false"); + dst += fmt::format("\t dcc.independent_block_size = 0x{:02x}\n", + static_cast(rt.dcc.independent_block_size)); + dst += fmt::format("\t data_write_on_dcc_clear_to_reg = {}\n", + rt.dcc.data_write_on_dcc_clear_to_reg ? "true" : "false"); + dst += fmt::format("\t dcc.dcc_clear_key_enable = {}\n", + rt.dcc.dcc_clear_key_enable ? "true" : "false"); + dst += fmt::format("\t cmask.addr = 0x{:016x}\n", rt.cmask.addr); + dst += fmt::format("\t fmask.addr = 0x{:016x}\n", rt.fmask.addr); + dst += fmt::format("\t clear_word0.word0 = 0x{:08x}\n", rt.clear_word0.word0); + dst += fmt::format("\t clear_word1.word1 = 0x{:08x}\n", rt.clear_word1.word1); + dst += fmt::format("\t dcc_addr.addr = 0x{:016x}\n", rt.dcc_addr.addr); return dst; } @@ -504,16 +487,7 @@ static void BcPrint(const char* func, const HW::BlendControl& c, const HW::Blend color.red, color.green, color.blue, color.alpha, cc.mode, cc.op); } -static void BcCheck(const HW::BlendControl& /*c*/, const HW::BlendColor& color, - const HW::ColorControl& cc) { - // EXIT_NOT_IMPLEMENTED(c.color_srcblend != 0); - // EXIT_NOT_IMPLEMENTED(c.color_comb_fcn != 0); - // EXIT_NOT_IMPLEMENTED(c.color_destblend != 0); - // EXIT_NOT_IMPLEMENTED(c.alpha_srcblend != 0); - // EXIT_NOT_IMPLEMENTED(c.alpha_comb_fcn != 0); - // EXIT_NOT_IMPLEMENTED(c.alpha_destblend != 0); - // EXIT_NOT_IMPLEMENTED(c.separate_alpha_blend != false); - // EXIT_NOT_IMPLEMENTED(c.enable != false); +static void BcCheck(const HW::BlendColor& color, const HW::ColorControl& cc) { if (color.red != 0.0f || color.green != 0.0f || color.blue != 0.0f || color.alpha != 0.0f) { static bool logged = false; if (!logged) { @@ -576,29 +550,6 @@ static void DPrint(const char* func, const HW::DepthControl& c, const HW::Stenci sm.stencil_opval_bf); } -static void DCheck(const HW::DepthControl& c, const HW::StencilControl& s, - const HW::StencilMask& /*sm*/) { - // EXIT_NOT_IMPLEMENTED(c.stencil_enable != false); - // EXIT_NOT_IMPLEMENTED(c.z_enable != false); - // EXIT_NOT_IMPLEMENTED(c.z_write_enable != false); - // EXIT_NOT_IMPLEMENTED(c.zfunc != 0); - // Back-face stencil state is handled separately when enabled. - // EXIT_NOT_IMPLEMENTED(c.stencilfunc != 0); - // EXIT_NOT_IMPLEMENTED(c.stencilfunc_bf != 0); - // EXIT_NOT_IMPLEMENTED(s.stencil_fail != 0); - // EXIT_NOT_IMPLEMENTED(s.stencil_zpass != 0); - // EXIT_NOT_IMPLEMENTED(s.stencil_zfail != 0); - // Back-face stencil ops may legitimately differ from the front-face ops. - // EXIT_NOT_IMPLEMENTED(sm.stencil_testval != 0); - // EXIT_NOT_IMPLEMENTED(sm.stencil_mask != 0); - // EXIT_NOT_IMPLEMENTED(sm.stencil_writemask != 0); - // EXIT_NOT_IMPLEMENTED(sm.stencil_opval != 0); - // EXIT_NOT_IMPLEMENTED(sm.stencil_testval_bf != 0); - // EXIT_NOT_IMPLEMENTED(sm.stencil_mask_bf != 0); - // EXIT_NOT_IMPLEMENTED(sm.stencil_writemask_bf != 0); - // EXIT_NOT_IMPLEMENTED(sm.stencil_opval_bf != 0); -} - static void EqaaPrint(const char* func, const HW::EqaaControl& c) { LOGF("%s\n", func); @@ -904,14 +855,10 @@ void hw_check(const CommandBuffer& buffer) { const auto& hw = buffer.GetRegisters(); const auto rt_slot = render_target_first_bound_slot(buffer); const auto& rt = hw.GetRenderTarget(rt_slot); - const auto& bc = hw.GetBlendControl(rt_slot); const auto& bclr = hw.GetBlendColor(); const auto& vp = hw.GetScreenViewport(); const auto& c = hw.GetClipControl(); const auto& rc = hw.GetRenderControl(); - const auto& d = hw.GetDepthControl(); - const auto& s = hw.GetStencilControl(); - const auto& sm = hw.GetStencilMask(); const auto& mc = hw.GetModeControl(); const auto& eqaa = hw.GetEqaaControl(); const auto& cc = hw.GetColorControl(); @@ -937,11 +884,10 @@ void hw_check(const CommandBuffer& buffer) { log_phase("rc"); RcCheck(rc); log_phase("depth"); - DCheck(d, s, sm); log_phase("mode"); McCheck(mc); log_phase("blend"); - BcCheck(bc, bclr, cc); + BcCheck(bclr, cc); log_phase("eqaa"); EqaaCheck(eqaa, ac); log_phase("aa"); @@ -1000,7 +946,7 @@ void hw_print(const CommandBuffer& buffer) { hw.GetRenderTargetMask(), hw.GetDepthClearValue(), hw.GetStencilClearValue(), hw.GetLineWidth(), hw.GetPrimitiveResetIndex()); - LOGF("%s", Common::Concat(rt_print("RenderTraget:", rt), "").c_str()); + LOGF("%s", rt_print("RenderTraget:", rt).c_str()); ZPrint("DepthRenderTraget:", z); VpPrint("ScreenViewport:", vp, smc); diff --git a/src/graphics/host_gpu/renderer/debug.h b/src/graphics/host_gpu/renderer/debug.h index 9928a083..166b4003 100644 --- a/src/graphics/host_gpu/renderer/debug.h +++ b/src/graphics/host_gpu/renderer/debug.h @@ -5,7 +5,6 @@ #include #include -#include namespace Libs::Graphics { @@ -13,7 +12,6 @@ class CommandBuffer; namespace HW { class Context; -class Shader; class UserConfig; struct RenderTarget; struct ScanModeControl; @@ -32,8 +30,7 @@ uint32_t render_target_first_bound_slot(const CommandBuffer& buf bool graphics_debug_dump_enabled(); void uc_print(const char* func, const HW::UserConfig& uc); void uc_check(const HW::UserConfig& uc); -void sh_print(const char* func, const HW::Shader& uc); -std::vector rt_print(const char* func, const HW::RenderTarget& rt); +std::string rt_print(const char* func, const HW::RenderTarget& rt); bool RenderIsColorTileModeLinear(Prospero::TileMode tile_mode); void hw_print(const CommandBuffer& buffer); void hw_check(const CommandBuffer& buffer); diff --git a/src/graphics/host_gpu/renderer/image/image.cpp b/src/graphics/host_gpu/renderer/image/image.cpp index 9d4eaa4d..96b27e1e 100644 --- a/src/graphics/host_gpu/renderer/image/image.cpp +++ b/src/graphics/host_gpu/renderer/image/image.cpp @@ -693,11 +693,12 @@ Image::Image(GraphicContext& graphics, CommandScheduler& scheduler, const ImageI } uint64_t Image::HashGuestEdges() const { - constexpr uint64_t page_mask = TRACKER_PAGE_SIZE - 1; std::array bytes {}; 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_end = + std::min(range.End(), Common::AlignUp(range.address, TRACKER_PAGE_SIZE)); + const uint64_t tail_begin = + std::max(range.address, Common::AlignDown(range.End(), TRACKER_PAGE_SIZE)); const uint64_t head_size = head_end - range.address; const uint64_t tail_address = tail_begin < head_end ? head_end : tail_begin; const uint64_t tail_size = range.End() - tail_address; diff --git a/src/graphics/host_gpu/renderer/image/image.h b/src/graphics/host_gpu/renderer/image/image.h index 27c17b45..27a42ceb 100644 --- a/src/graphics/host_gpu/renderer/image/image.h +++ b/src/graphics/host_gpu/renderer/image/image.h @@ -1,6 +1,7 @@ #ifndef EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_IMAGE_H_ #define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_IMAGE_H_ +#include "common/alignment.h" #include "common/assert.h" #include "common/slotVector.h" #include "graphics/host_gpu/graphicContext.h" @@ -133,7 +134,7 @@ public: } [[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}; + return backing.image == nullptr ? 0 : Common::AlignUp(info.data.size, 1024); } [[nodiscard]] uint64_t HashGuestEdges() const; diff --git a/src/graphics/host_gpu/renderer/image/imageInfo.h b/src/graphics/host_gpu/renderer/image/imageInfo.h index e81173f4..a2c4d2c9 100644 --- a/src/graphics/host_gpu/renderer/image/imageInfo.h +++ b/src/graphics/host_gpu/renderer/image/imageInfo.h @@ -82,9 +82,6 @@ struct ImageInfo { [[nodiscard]] constexpr bool IsVolume() const noexcept { return type == Prospero::ImageType::kColor3D; } - [[nodiscard]] constexpr bool IsLayered() const noexcept { - return !IsVolume() && resources.layers > 1; - } [[nodiscard]] constexpr uint32_t TransferLayers() const noexcept { return IsVolume() ? extent.depth : resources.layers; } @@ -342,14 +339,6 @@ ClassifyVideoOutCompression(bool compressed, uint64_t metadata_address, uint32_t } } -[[nodiscard]] inline constexpr bool -CanUseVideoOutNativeWithoutUpload(VideoOutCompression compression, bool render_target, - bool gpu_modified, bool guest_modified) noexcept { - return compression != VideoOutCompression::Uncompressed && - compression != VideoOutCompression::Unsupported && !guest_modified && - (render_target || gpu_modified); -} - struct VideoOutPixelFormatInfo { vk::Format format = vk::Format::eUndefined; Prospero::BufferFormat guest_format = Prospero::BufferFormat::kInvalid; @@ -400,32 +389,6 @@ inline constexpr std::array VIDEO_OUT_FORMAT_POLICIES { return false; } -[[nodiscard]] inline constexpr bool -IsSupportedDisplayRenderTargetTileMode(Prospero::TileMode tile_mode) noexcept { - return tile_mode == Prospero::TileMode::kRenderTarget; -} - -[[nodiscard]] inline constexpr bool IsSupportedStandard64RenderTarget(const ImageInfo& info) { - if (info.tile_mode != Prospero::TileMode::kStandard64KB || info.data.address == 0 || - (info.data.address & 0xffffu) != 0 || info.extent.width == 0 || info.extent.height == 0 || - info.bytes_per_block != 4 || info.resources.levels != 1 || info.resources.layers != 1 || - info.samples != 1) { - return false; - } - const auto expected_pitch = - (static_cast(info.extent.width) + 127u) & ~uint64_t {127u}; - const auto padded_height = - (static_cast(info.extent.height) + 127u) & ~uint64_t {127u}; - return expected_pitch <= UINT32_MAX && info.pitch == expected_pitch && - expected_pitch <= UINT64_MAX / padded_height / info.bytes_per_block && - info.data.size == expected_pitch * padded_height * info.bytes_per_block; -} - -[[nodiscard]] inline constexpr bool IsTiledRenderTarget(const ImageInfo& info) noexcept { - return info.tile_mode == Prospero::TileMode::kRenderTarget || - IsSupportedStandard64RenderTarget(info); -} - [[nodiscard]] inline bool DecodePackedColorClear(vk::Format format, uint32_t packed, vk::ClearColorValue& clear) { vk::ClearColorValue next {}; diff --git a/src/graphics/host_gpu/renderer/image/imageView.cpp b/src/graphics/host_gpu/renderer/image/imageView.cpp index be455f08..2ad0d46b 100644 --- a/src/graphics/host_gpu/renderer/image/imageView.cpp +++ b/src/graphics/host_gpu/renderer/image/imageView.cpp @@ -322,6 +322,12 @@ vk::ImageView Image::FindView(const ImageViewInfo& view_info) { normalized.aspect = vk::ImageAspectFlagBits::eStencil; } normalized.usage = is_storage ? vk::ImageUsageFlagBits::eStorage : vk::ImageUsageFlags {}; + for (const auto& cached: views) { + if (cached.info == normalized) { + return cached.view; + } + } + const bool format_compatible = normalized.format != vk::Format::eUndefined && ImageViewOps::FormatsCompatible(image.format, normalized.format); const bool slice_view = @@ -351,12 +357,6 @@ vk::ImageView Image::FindView(const ImageViewInfo& view_info) { image.layers); } - for (const auto& cached: views) { - if (cached.info == normalized) { - return cached.view; - } - } - vk::ImageViewUsageCreateInfo usage {}; usage.usage = image.usage; if (!is_storage) { diff --git a/src/graphics/host_gpu/renderer/image/textureCommon.cpp b/src/graphics/host_gpu/renderer/image/textureCommon.cpp index 1bec745d..85f3a91f 100644 --- a/src/graphics/host_gpu/renderer/image/textureCommon.cpp +++ b/src/graphics/host_gpu/renderer/image/textureCommon.cpp @@ -1,5 +1,6 @@ #include "graphics/host_gpu/renderer/image/textureCommon.h" +#include "common/alignment.h" #include "common/assert.h" #include "graphics/guest_gpu/gpu_defs.h" #include "graphics/guest_gpu/gpu_format.h" @@ -260,12 +261,10 @@ std::vector TextureBuildImageCopies(const TextureUploadLayo region.bufferRowLength = layout.mips[mip_level].row_length; region.bufferImageHeight = layout.mips[mip_level].image_height; } else { - const auto texel_width = layout.surface.texture.texel_width; - const auto align_to_texel = [texel_width](uint32_t dimension) { - return ((dimension + texel_width - 1u) / texel_width) * texel_width; - }; - const auto aligned_pitch = align_to_texel(mip_pitch); - region.bufferRowLength = aligned_pitch > align_to_texel(mip_width) ? aligned_pitch : 0; + const auto texel_width = layout.surface.texture.texel_width; + const auto aligned_pitch = Common::AlignUp(mip_pitch, texel_width); + region.bufferRowLength = + aligned_pitch > Common::AlignUp(mip_width, texel_width) ? aligned_pitch : 0; } for (uint32_t slice_index = 0; slice_index < mip_depth; ++slice_index) { region.bufferOffset = layout.mips[mip_level].offset + slice_index * layout.slice_stride; diff --git a/src/graphics/host_gpu/renderer/image/tiler.cpp b/src/graphics/host_gpu/renderer/image/tiler.cpp index 70a7482a..445f5ea9 100644 --- a/src/graphics/host_gpu/renderer/image/tiler.cpp +++ b/src/graphics/host_gpu/renderer/image/tiler.cpp @@ -1,5 +1,6 @@ #include "graphics/host_gpu/renderer/image/tiler.h" +#include "common/alignment.h" #include "common/assert.h" #include "gpu_tiler_shaders/gpu_tiler_demote_d16_spv.h" #include "gpu_tiler_shaders/gpu_tiler_depth_spv.h" @@ -221,7 +222,7 @@ void TileManager::Prepare(bool tile, uint64_t tiled_capacity, uint64_t linear_ca const uint64_t uniform_alignment = std::max(limits.minUniformBufferOffsetAlignment, 1); - const uint64_t stride = (sizeof(Push) + uniform_alignment - 1) & ~(uniform_alignment - 1); + const uint64_t stride = Common::AlignUp(sizeof(Push), uniform_alignment); EXIT_NOT_IMPLEMENTED(dispatches.size() > UINT64_MAX / stride); const uint64_t bytes = dispatches.size() * stride; auto [mapped, offset] = m_stream_buffer.Map(bytes, uniform_alignment); @@ -287,12 +288,12 @@ void TileManager::Record(vk::Buffer source, uint64_t source_offset, const auto& limits = m_graphics.GetPhysicalDeviceProperties().limits; const uint64_t descriptor_alignment = std::max(limits.minStorageBufferOffsetAlignment, 4); - const uint64_t source_descriptor_offset = source_offset & ~(descriptor_alignment - 1); - const uint64_t target_descriptor_offset = target_offset & ~(descriptor_alignment - 1); + const uint64_t source_descriptor_offset = Common::AlignDown(source_offset, descriptor_alignment); + const uint64_t target_descriptor_offset = Common::AlignDown(target_offset, descriptor_alignment); const uint64_t source_base = source_offset - source_descriptor_offset; const uint64_t target_base = target_offset - target_descriptor_offset; - const uint64_t source_range = (source_base + source_capacity + 3u) & ~uint64_t {3}; - const uint64_t target_range = (target_base + target_capacity + 3u) & ~uint64_t {3}; + const uint64_t source_range = Common::AlignUp(source_base + source_capacity, 4); + const uint64_t target_range = Common::AlignUp(target_base + target_capacity, 4); EXIT_NOT_IMPLEMENTED(source_range > limits.maxStorageBufferRange || target_range > limits.maxStorageBufferRange || target_offset % 4 != 0 || target_capacity % 4 != 0); @@ -372,7 +373,7 @@ TileManager::Result TileManager::Detile(vk::Buffer tiled, uint64_t tiled_offset, const uint64_t source_base = tiled_offset & (descriptor_alignment - 1); std::vector dispatches; Prepare(false, tiled_capacity, linear_capacity, infos, source_base, 0, dispatches); - auto scratch = AllocateScratch((linear_capacity + 3u) & ~uint64_t {3}); + auto scratch = AllocateScratch(Common::AlignUp(linear_capacity, 4)); DeferDestroy(scratch); Record(tiled, tiled_offset, tiled_capacity, scratch.buffer, 0, scratch.size, dispatches, true); @@ -406,7 +407,7 @@ void TileManager::TileImage(Image& image, std::span r // Reserve all stream parameters before creating a scheduler-lived scratch dependency: // StreamBuffer::Map is allowed to submit the current tick when it wraps. Prepare(true, tiled_capacity, linear_capacity, infos, 0, target_base, dispatches); - auto linear = AllocateScratch((linear_capacity + 3u) & ~uint64_t {3}); + auto linear = AllocateScratch(Common::AlignUp(linear_capacity, 4)); DeferDestroy(linear); image.Download(regions, linear.buffer, 0, linear.size); Result source {linear.buffer, 0, linear.size}; @@ -418,7 +419,7 @@ void TileManager::TileImage(Image& image, std::span r } TileManager::Result TileManager::GetScratchBuffer(uint64_t size) { - auto scratch = AllocateScratch((size + 3u) & ~uint64_t {3}); + auto scratch = AllocateScratch(Common::AlignUp(size, 4)); DeferDestroy(scratch); return {scratch.buffer, 0, scratch.size}; } @@ -426,11 +427,11 @@ TileManager::Result TileManager::GetScratchBuffer(uint64_t size) { TileManager::StorageBinding TileManager::BindStorage(Result buffer, uint64_t size) const { const auto& limits = m_graphics.GetPhysicalDeviceProperties().limits; const auto alignment = std::max(limits.minStorageBufferOffsetAlignment, 4); - const auto descriptor_offset = buffer.offset - buffer.offset % alignment; + const auto descriptor_offset = Common::AlignDown(buffer.offset, alignment); const auto base = buffer.offset - descriptor_offset; EXIT_IF(buffer.buffer == nullptr || size == 0 || buffer.size < size || base > UINT32_MAX || size > UINT64_MAX - base || base + size > UINT64_MAX - 3); - const auto range = (base + size + 3) & ~uint64_t {3}; + const auto range = Common::AlignUp(base + size, 4); EXIT_IF(range > limits.maxStorageBufferRange || range > UINT32_MAX); return {{buffer.buffer, descriptor_offset, range}, static_cast(base)}; } @@ -512,8 +513,8 @@ void TileManager::ConvertD16(Result source, Result target, D16Direction directio const auto target_required = required(layout.height, layout.layers, layout.target_row_stride, layout.target_slice_stride, target_active); EXIT_IF(source_required > UINT64_MAX - 3 || target_required > UINT64_MAX - 3); - const auto source_barrier_size = (source_required + 3) & ~uint64_t {3}; - const auto target_barrier_size = (target_required + 3) & ~uint64_t {3}; + const auto source_barrier_size = Common::AlignUp(source_required, 4); + const auto target_barrier_size = Common::AlignUp(target_required, 4); EXIT_IF(source.size < source_barrier_size || target.size < target_barrier_size); m_scheduler.EndRendering(); diff --git a/src/graphics/host_gpu/renderer/pipeline/descriptorHeap.cpp b/src/graphics/host_gpu/renderer/pipeline/descriptorHeap.cpp index de61c686..64ebe4a6 100644 --- a/src/graphics/host_gpu/renderer/pipeline/descriptorHeap.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/descriptorHeap.cpp @@ -20,7 +20,7 @@ constexpr std::array DescriptorPoolSizes = { DescriptorHeap::DescriptorHeap(GraphicContext& graphics, MasterSemaphore& master_semaphore) : m_graphics(graphics), m_master_semaphore(master_semaphore) { - CreatePool(); + CreateDescriptorPool(); } DescriptorHeap::~DescriptorHeap() { @@ -49,7 +49,7 @@ vk::DescriptorSet DescriptorHeap::Commit(vk::DescriptorSetLayout layout) { m_pending_pools.pop_front(); EXIT_IF(m_graphics.device.resetDescriptorPool(m_current_pool, {}) != vk::Result::eSuccess); } else { - CreatePool(); + CreateDescriptorPool(); } m_sets.clear(); @@ -82,7 +82,7 @@ bool DescriptorHeap::Allocate(vk::DescriptorSetLayout layout, Batch& batch) { } } -void DescriptorHeap::CreatePool() { +void DescriptorHeap::CreateDescriptorPool() { vk::DescriptorPoolCreateInfo create {}; create.maxSets = DescriptorHeapCount; create.poolSizeCount = static_cast(DescriptorPoolSizes.size()); diff --git a/src/graphics/host_gpu/renderer/pipeline/descriptorHeap.h b/src/graphics/host_gpu/renderer/pipeline/descriptorHeap.h index 86cad34d..2fb83ae5 100644 --- a/src/graphics/host_gpu/renderer/pipeline/descriptorHeap.h +++ b/src/graphics/host_gpu/renderer/pipeline/descriptorHeap.h @@ -31,7 +31,7 @@ private: }; [[nodiscard]] bool Allocate(vk::DescriptorSetLayout layout, Batch& batch); - void CreatePool(); + void CreateDescriptorPool(); GraphicContext& m_graphics; MasterSemaphore& m_master_semaphore; diff --git a/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp b/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp index 49198387..d93250ca 100644 --- a/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp @@ -1,5 +1,6 @@ #include "graphics/host_gpu/renderer/pipeline/descriptors.h" +#include "common/alignment.h" #include "common/assert.h" #include "common/common.h" #include "common/file.h" @@ -145,7 +146,7 @@ NativeStorageBuffer(RenderContext& context, const PreparedBindings::BufferSource } auto [buffer, offset] = context.GetBufferCache().ObtainBuffer(address, size, resource.written, resource.formatted, id); - const auto aligned_offset = offset - offset % alignment; + const auto aligned_offset = Common::AlignDown(offset, alignment); const auto adjustment = offset - aligned_offset; const auto max_range = graphics.GetPhysicalDeviceProperties().limits.maxStorageBufferRange; if (adjustment % sizeof(uint32_t) != 0 || adjustment >= 256 || size > max_range - adjustment) { @@ -169,34 +170,6 @@ NativeStorageBuffer(RenderContext& context, const PreparedBindings::BufferSource return result; } -static bool IsSupportedSampledColorResource(const ShaderRecompiler::IR::ImageResource& resource) { - bool supported_dimension = false; - switch (resource.dimension) { - case ShaderRecompiler::Decoder::ImageDimension::Dim1D: - case ShaderRecompiler::Decoder::ImageDimension::Dim1DArray: - case ShaderRecompiler::Decoder::ImageDimension::Dim2D: - case ShaderRecompiler::Decoder::ImageDimension::Dim2DArray: - case ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa: - case ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaaArray: - supported_dimension = true; - break; - default: break; - } - return resource.resource_class == ShaderRecompiler::IR::ImageResourceClass::Sampled && - resource.numeric_class != Prospero::TextureNumericClass::Unsupported && - supported_dimension && resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None && - resource.read && !resource.written && !resource.atomic && !resource.depth_compare; -} - -bool IsSupportedSampledVideoOutView(const ShaderRecompiler::IR::ImageResource& resource, - const ShaderTextureResource& descriptor, const Image& image) { - return image.usage.video_out && image.info.resources.layers == 1 && - IsSupportedSampledColorResource(resource) && - resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D && - descriptor.Type() == Prospero::ImageType::kColor2D && descriptor.Depth() == 0 && - descriptor.BaseArray5() == 0; -} - bool IsSupportedDepthTextureEncoding(const ShaderTextureResource& descriptor, bool r128) { constexpr uint32_t field1_reserved_mask = 0x200fff00u; constexpr uint32_t field2_reserved_mask = 0xf0003000u; @@ -721,13 +694,6 @@ static vk::DescriptorBufferInfo NativeUpload(RenderContext& context, return {buffer.Handle(), offset, data.size_bytes()}; } -void RenderExecutor::TrackImageBinding(ImageId id) { - EXIT_IF(m_context.GetTextureCache().m_slot_images.try_get(id) == nullptr); - if (std::ranges::find(m_bound_images, id) == m_bound_images.end()) { - m_bound_images.push_back(id); - } -} - void RenderExecutor::BindImage(ImageId id, bool storage) { auto& image = m_context.GetTextureCache().GetImage(id); if (image.info.data.Empty()) { @@ -738,13 +704,13 @@ void RenderExecutor::BindImage(ImageId id, bool storage) { } image.binding.is_bound = true; image.binding.shader_write |= storage; - TrackImageBinding(id); + m_bound_images.push_back(id); } void RenderExecutor::BindRenderTarget(ImageId id) { auto& image = m_context.GetTextureCache().GetImage(id); image.binding.is_target = true; - TrackImageBinding(id); + m_bound_images.push_back(id); } void RenderExecutor::ResetBindings() { @@ -797,10 +763,7 @@ void RenderExecutor::FindBuffers(PreparedBindings& prepared) { for (uint32_t i = 0; i < program.info.buffers.size(); i++) { auto descriptor = DecodeNativeDescriptor(snapshot.buffers[i]); const auto address = descriptor.Base48(); - const auto stride = descriptor.Stride(); - const auto records = descriptor.NumRecords(); - // The descriptor has a 14-bit stride and 32-bit record count, so the product fits u64. - const auto requested_size = stride != 0 ? static_cast(stride) * records : records; + const auto requested_size = descriptor.GetSize(); if (address == 0 || requested_size == 0) { prepared.buffer_sources.push_back({}); continue; @@ -909,7 +872,7 @@ RenderExecutor::PrepareGraphicsBindings(const ShaderStageRuntime& vertex, } if (bindings.vertex.runtime->program->info.uses_dma || (bindings.pixel && bindings.pixel->runtime->program->info.uses_dma)) { - m_context.GetGpuResources().PrepareBda(); + m_context.PrepareBda(); } RebindBuffers(bindings.vertex); if (bindings.pixel) { diff --git a/src/graphics/host_gpu/renderer/pipeline/descriptors.h b/src/graphics/host_gpu/renderer/pipeline/descriptors.h index aafc8e7d..3bfebcb6 100644 --- a/src/graphics/host_gpu/renderer/pipeline/descriptors.h +++ b/src/graphics/host_gpu/renderer/pipeline/descriptors.h @@ -66,9 +66,6 @@ template [[nodiscard]] bool IsSupportedDepthTextureEncoding(const ShaderTextureResource& descriptor, bool r128 = false); -[[nodiscard]] bool -IsSupportedSampledVideoOutView(const ShaderRecompiler::IR::ImageResource& resource, - const ShaderTextureResource& descriptor, const Image& image); void ValidateStorageTexture(const ShaderRecompiler::IR::ImageResource& resource, const ShaderTextureResource& descriptor, uint64_t size); diff --git a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp index 6e0d4b92..84d6db8b 100644 --- a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp @@ -258,10 +258,6 @@ struct PipelineCache::ProgramCache { "create recompiled shader module"); EXIT_IF(module == nullptr); if (options.dump_ir) { - if (!options.early_dump) { - LOGF("%s decoded RDNA2:\n%s", options.dump_label, result.decoded_dump.c_str()); - LOGF("%s IR:\n%s", options.dump_label, result.ir_dump.c_str()); - } LOGF("%s SPIR-V words=%" PRIu64 " wave_size=%u\n", options.dump_label, static_cast(result.spirv.size()), options.wave_size); } @@ -633,7 +629,7 @@ bool PipelineStaticParameters::operator==(const PipelineStaticParameters& other) return std::memcmp(this, &other, sizeof(*this)) == 0; } -PipelineCache::Pipeline& PipelineCache::CreateGraphicsPipeline( +PipelineCache::Pipeline& PipelineCache::GetGraphicsPipeline( std::span colors, const RenderDepthInfo& depth, const ShaderVertexInputInfo& vs_input_info, CommandBuffer& command, const ShaderPixelInputInfo* ps_input_info, vk::PrimitiveTopology topology, @@ -673,6 +669,16 @@ PipelineCache::Pipeline& PipelineCache::CreateGraphicsPipeline( EXIT("mixed color attachment sample counts are unsupported: %u and %u\n", attachment_samples, colors[i].desc.info.samples); } + const auto& rt = ctx.GetRenderTarget(colors[i].target_slot); + const auto& bc = ctx.GetBlendControl(colors[i].target_slot); + static_params.color_srcblend[i] = bc.color_srcblend; + static_params.color_comb_fcn[i] = bc.color_comb_fcn; + static_params.color_destblend[i] = bc.color_destblend; + static_params.alpha_srcblend[i] = bc.alpha_srcblend; + static_params.alpha_comb_fcn[i] = bc.alpha_comb_fcn; + static_params.alpha_destblend[i] = bc.alpha_destblend; + static_params.separate_alpha_blend[i] = bc.separate_alpha_blend; + static_params.blend_enable[i] = bc.enable && !rt.info.blend_bypass; } const bool with_depth = depth.desc.view_info.format != vk::Format::eUndefined && static_cast(depth.image_id); @@ -732,19 +738,6 @@ PipelineCache::Pipeline& PipelineCache::CreateGraphicsPipeline( static_params.polygon_mode = ResolvePolygonMode(mc, static_params.cull_front, static_params.cull_back); - for (uint32_t i = 0; i < color_count; i++) { - const auto& rt = ctx.GetRenderTarget(colors[i].target_slot); - const auto& bc = ctx.GetBlendControl(colors[i].target_slot); - static_params.color_srcblend[i] = bc.color_srcblend; - static_params.color_comb_fcn[i] = bc.color_comb_fcn; - static_params.color_destblend[i] = bc.color_destblend; - static_params.alpha_srcblend[i] = bc.alpha_srcblend; - static_params.alpha_comb_fcn[i] = bc.alpha_comb_fcn; - static_params.alpha_destblend[i] = bc.alpha_destblend; - static_params.separate_alpha_blend[i] = bc.separate_alpha_blend; - static_params.blend_enable[i] = bc.enable; - static_params.blend_bypass[i] = rt.info.blend_bypass; - } if (vs_input_info.stage.program->stage != ShaderType::Mesh) { EXIT_IF(vs_input_info.buffers_num < 0 || vs_input_info.buffers_num > ShaderVertexInputInfo::RES_MAX || @@ -803,8 +796,8 @@ PipelineCache::Pipeline& PipelineCache::CreateGraphicsPipeline( } PipelineCache::Pipeline& -PipelineCache::CreateComputePipeline(const ShaderComputeInputInfo& input_info, - const ShaderProgram& compute_program) { +PipelineCache::GetComputePipeline(const ShaderComputeInputInfo& input_info, + const ShaderProgram& compute_program) { KYTY_PROFILER_BLOCK("PipelineCache::CreatePipeline(Compute)", profiler::colors::RedA100); EXIT_IF(!compute_program); diff --git a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.h b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.h index 6fa19e74..310d1775 100644 --- a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.h +++ b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.h @@ -59,7 +59,6 @@ struct PipelineStaticParameters { uint8_t alpha_destblend[RENDER_COLOR_ATTACHMENTS_MAX] = {}; bool separate_alpha_blend[RENDER_COLOR_ATTACHMENTS_MAX] = {}; bool blend_enable[RENDER_COLOR_ATTACHMENTS_MAX] = {}; - bool blend_bypass[RENDER_COLOR_ATTACHMENTS_MAX] = {}; bool operator==(const PipelineStaticParameters& other) const noexcept; }; @@ -69,7 +68,7 @@ struct PipelineStaticParameters { static_assert(std::is_trivially_copyable_v); static_assert(std::is_standard_layout_v); static_assert(alignof(PipelineStaticParameters) == 1); -static_assert(sizeof(PipelineStaticParameters) == 166); +static_assert(sizeof(PipelineStaticParameters) == 158); struct PipelineRenderingState { std::array color_formats {}; @@ -138,13 +137,13 @@ public: ShaderComputeInputInfo& input_info); Pipeline& - CreateGraphicsPipeline(std::span colors, const RenderDepthInfo& depth, - const ShaderVertexInputInfo& vs_input_info, CommandBuffer& command, - const ShaderPixelInputInfo* ps_input_info, - vk::PrimitiveTopology topology, bool primitive_restart_enable, - const ShaderProgram& vertex_program, const ShaderProgram& pixel_program); - Pipeline& CreateComputePipeline(const ShaderComputeInputInfo& input_info, - const ShaderProgram& compute_program); + GetGraphicsPipeline(std::span colors, const RenderDepthInfo& depth, + const ShaderVertexInputInfo& vs_input_info, CommandBuffer& command, + const ShaderPixelInputInfo* ps_input_info, + vk::PrimitiveTopology topology, bool primitive_restart_enable, + const ShaderProgram& vertex_program, const ShaderProgram& pixel_program); + Pipeline& GetComputePipeline(const ShaderComputeInputInfo& input_info, + const ShaderProgram& compute_program); private: struct ProgramCache; diff --git a/src/graphics/host_gpu/renderer/pipeline/shaders.cpp b/src/graphics/host_gpu/renderer/pipeline/shaders.cpp index d0ec8496..708d7f7a 100644 --- a/src/graphics/host_gpu/renderer/pipeline/shaders.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/shaders.cpp @@ -116,10 +116,6 @@ static void GetInputFormat(const ShaderBufferResource& res, vk::Format& format, } format = vk::Format::eR16G16Sfloat; size = 2; - if (NarrowInputFormat(format, size, used_components)) { - LOGF("InputFormat: narrowing fmt=%u to %s for used_components=%u\n", raw_format, - vk::to_string(format).c_str(), used_components); - } return; } @@ -259,35 +255,23 @@ void CreatePipelineInternal( EXIT_NOT_IMPLEMENTED( rect_list && (tess_control_shader_module == nullptr || tess_eval_shader_module == nullptr)); - vk::PipelineShaderStageCreateInfo vert_shader_stage_info {}; - vert_shader_stage_info.stage = vertex_stage; - vert_shader_stage_info.module = vertex_program.module; - vert_shader_stage_info.pName = "main"; - - vk::PipelineShaderStageCreateInfo frag_shader_stage_info {}; - frag_shader_stage_info.stage = vk::ShaderStageFlagBits::eFragment; - frag_shader_stage_info.module = pixel_program.module; - frag_shader_stage_info.pName = "main"; - - vk::PipelineShaderStageCreateInfo tess_control_shader_stage_info {}; - tess_control_shader_stage_info.stage = vk::ShaderStageFlagBits::eTessellationControl; - tess_control_shader_stage_info.module = tess_control_shader_module; - tess_control_shader_stage_info.pName = "main"; - - vk::PipelineShaderStageCreateInfo tess_eval_shader_stage_info {}; - tess_eval_shader_stage_info.stage = vk::ShaderStageFlagBits::eTessellationEvaluation; - tess_eval_shader_stage_info.module = tess_eval_shader_module; - tess_eval_shader_stage_info.pName = "main"; - - vk::PipelineShaderStageCreateInfo shader_stages[4] = {}; - uint32_t shader_stage_count = 0; - shader_stages[shader_stage_count++] = vert_shader_stage_info; + vk::PipelineShaderStageCreateInfo shader_stages[4] = { + {.stage = vertex_stage, .module = vertex_program.module, .pName = "main"}}; + uint32_t shader_stage_count = 1; if (rect_list) { - shader_stages[shader_stage_count++] = tess_control_shader_stage_info; - shader_stages[shader_stage_count++] = tess_eval_shader_stage_info; + shader_stages[shader_stage_count++] = {.stage = + vk::ShaderStageFlagBits::eTessellationControl, + .module = tess_control_shader_module, + .pName = "main"}; + shader_stages[shader_stage_count++] = {.stage = + vk::ShaderStageFlagBits::eTessellationEvaluation, + .module = tess_eval_shader_module, + .pName = "main"}; } if (ps_active) { - shader_stages[shader_stage_count++] = frag_shader_stage_info; + shader_stages[shader_stage_count++] = {.stage = vk::ShaderStageFlagBits::eFragment, + .module = pixel_program.module, + .pName = "main"}; } vk::VertexInputAttributeDescription input_attr[ShaderVertexInputInfo::RES_MAX] {}; @@ -410,8 +394,7 @@ void CreatePipelineInternal( EXIT_NOT_IMPLEMENTED((static_params.color_mask[i] & ~0x0fu) != 0); color_blend_attachment[i].colorWriteMask = vk::ColorComponentFlags {static_params.color_mask[i]}; - color_blend_attachment[i].blendEnable = - (static_params.blend_enable[i] && !static_params.blend_bypass[i]) ? VK_TRUE : VK_FALSE; + color_blend_attachment[i].blendEnable = static_params.blend_enable[i] ? VK_TRUE : VK_FALSE; color_blend_attachment[i].srcColorBlendFactor = GetBlendFactor(static_params.color_srcblend[i]); color_blend_attachment[i].dstColorBlendFactor = diff --git a/src/graphics/host_gpu/renderer/render.h b/src/graphics/host_gpu/renderer/render.h index 22499d65..fdc5fca5 100644 --- a/src/graphics/host_gpu/renderer/render.h +++ b/src/graphics/host_gpu/renderer/render.h @@ -187,12 +187,11 @@ private: [[nodiscard]] bool PrepareDrawRenderState(CommandBuffer& buffer, const DrawCallInfo& draw, uint32_t render_target_slice_offset, - bool log_setup_phases, DrawRenderState& state); + DrawRenderState& state); void ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buffer, const DrawCallInfo& draw, DrawRenderState& state, vk::PrimitiveTopology topology, const DrawEmitInfo& emit, const DrawIndexBufferSource& index_source, - bool primitive_restart_enable, bool log_pipeline_phase, - bool set_bind_debug, bool set_auto_debug); + bool primitive_restart_enable); [[nodiscard]] RenderState AcquireRenderTargets(CommandBuffer& buffer, RenderColorInfo* colors, uint32_t color_count, RenderDepthInfo& depth, const std::optional& pixel = std::nullopt); @@ -200,7 +199,6 @@ private: uint32_t render_target_slice_offset); void BindImage(ImageId id, bool storage); void BindRenderTarget(ImageId id); - void TrackImageBinding(ImageId id); void ResetBindings(); [[nodiscard]] bool TryConsumeComputeMetaClear(const ShaderComputeInputInfo& input, const CommandBuffer& buffer); diff --git a/src/graphics/host_gpu/renderer/renderCompute.cpp b/src/graphics/host_gpu/renderer/renderCompute.cpp index f0be459e..97808cb2 100644 --- a/src/graphics/host_gpu/renderer/renderCompute.cpp +++ b/src/graphics/host_gpu/renderer/renderCompute.cpp @@ -36,18 +36,12 @@ #include namespace Libs::Graphics { -static uint64_t BufferDescriptorSize(const ShaderBufferResource& descriptor) { - const uint64_t records = descriptor.NumRecords(); - const uint64_t stride = descriptor.Stride(); - return stride == 0 ? records : records * stride; -} - static bool FillSourcesDisjoint(std::span sources, GuestRange destination, uint32_t output_buffer = UINT32_MAX) { for (uint32_t i = 0; i < sources.size(); ++i) { if (i == output_buffer) continue; const auto source = DecodeNativeDescriptor(sources[i]); - const auto bytes = BufferDescriptorSize(source); + const auto bytes = source.GetSize(); if (source.Base48() < destination.End() && destination.address < source.Base48() + bytes) return false; } @@ -67,7 +61,7 @@ bool RenderExecutor::TryConsumeComputeMetaClear(const ShaderComputeInputInfo& in const auto descriptor = DecodeNativeDescriptor(resources.buffers[i]); // A metadata resource that is also read is not proven to be a full overwrite. Execute it // conservatively instead of replacing the dispatch with a coarse full-surface clear. - if (cache.IsMeta(descriptor.Base48()) && (!resource.written || resource.read)) { + if ((!resource.written || resource.read) && cache.IsMeta(descriptor.Base48())) { return false; } } @@ -115,7 +109,7 @@ bool ResolveComputeBufferFill(const ShaderComputeInputInfo& input, uint32_t grou const uint64_t invocations = input.dispatch_thread_dimensions ? group_x : static_cast(group_x) * input.threads_num[0]; - const auto size = BufferDescriptorSize(descriptor); + const auto size = descriptor.GetSize(); if (invocations != descriptor.NumRecords() || size == 0 || size > UINT32_MAX || (input.dispatch_thread_dimensions && (group_x % input.threads_num[0] != 0 || input.dispatch_threads_num[0] != group_x || @@ -264,9 +258,6 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer, input_info.dispatch_threads_num[2] = thread_group_z; } - const uint32_t frame_num = static_cast(m_context.GetGpu().GetFrameNum()); - const bool large_workgroup = - (input_info.threads_num[0] * input_info.threads_num[1] * input_info.threads_num[2] >= 512); const auto& program = *input_info.stage.program; const auto& resources = input_info.stage.resources; if (TryConsumeComputeMetaClear(input_info, buffer)) { @@ -278,14 +269,17 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer, ResetBindings(); return; } - const auto sampled_images = std::count_if( - program.info.images.begin(), program.info.images.end(), [](const auto& image) { - return image.resource_class == ShaderRecompiler::IR::ImageResourceClass::Sampled; - }); + const bool large_workgroup = + (input_info.threads_num[0] * input_info.threads_num[1] * input_info.threads_num[2] >= 512); const bool has_sampler = !program.info.samplers.empty(); static std::atomic dispatch_log_count {0}; if ((large_workgroup || has_sampler) && dispatch_log_count.fetch_add(1, std::memory_order_relaxed) < 512) { + const auto sampled_images = std::count_if( + program.info.images.begin(), program.info.images.end(), [](const auto& image) { + return image.resource_class == ShaderRecompiler::IR::ImageResourceClass::Sampled; + }); + const uint32_t frame_num = static_cast(m_context.GetGpu().GetFrameNum()); LOGF("GraphicsRenderDispatchDirect: frame=%u shader=0x%016" PRIx64 " groups=%ux%ux%u mode=0x%08" PRIx32 " local=%ux%ux%u " "buffers=%zu textures=%zu sampled=%zu storage=%zu samplers=%zu push=%u\n", @@ -374,11 +368,11 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer, buffer.EndRendering(); auto& pipeline = - m_context.GetPipelineCache().CreateComputePipeline(input_info, compute_program); + m_context.GetPipelineCache().GetComputePipeline(input_info, compute_program); auto bindings = PrepareBindings(input_info.stage); FindBuffers(bindings); if (program.info.uses_dma) { - m_context.GetGpuResources().PrepareBda(); + m_context.PrepareBda(); } RebindBuffers(bindings); RebindImages(bindings); diff --git a/src/graphics/host_gpu/renderer/renderContext.cpp b/src/graphics/host_gpu/renderer/renderContext.cpp index 224beccf..81c3fa53 100644 --- a/src/graphics/host_gpu/renderer/renderContext.cpp +++ b/src/graphics/host_gpu/renderer/renderContext.cpp @@ -14,7 +14,8 @@ RenderContext::RenderContext(GraphicContext& graphics) : m_graphics(graphics), m_render_executor(*this), m_command_scheduler(*this, graphics), m_descriptor_heap(graphics, m_command_scheduler.GetMasterSemaphore()), m_pipeline_cache(graphics), m_sampler_cache(graphics), - m_gpu_resources(graphics, m_command_scheduler) { + m_buffer_cache(graphics, m_command_scheduler, m_page_manager, m_texture_cache), + m_texture_cache(graphics, m_command_scheduler, m_page_manager, m_buffer_cache) { EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread()); } @@ -27,12 +28,10 @@ void RenderContext::InitializeGpu(VideoOut::VideoOutDriver* video_out) { EXIT_IF(m_gpu != nullptr); m_video_out = video_out; m_gpu = std::make_unique(*this); - m_gpu_resources.SetGpu(m_gpu.get()); } void RenderContext::ShutdownGpu() { if (m_gpu != nullptr) { - m_gpu_resources.SetGpu(nullptr); m_gpu->Shutdown(); m_gpu.reset(); } @@ -55,6 +54,88 @@ VideoOut::VideoOutDriver& RenderContext::GetVideoOut() const { return *m_video_out; } +bool RenderContext::HandleFault(PageFaultAccess access, uint64_t fault_vaddr) noexcept { + // The host reports the faulting byte, not the instruction's access width. Both caches + // resolve its page; guessing a width can cross the end of a valid guest mapping. + constexpr uint64_t fault_size = 1; + if (!IsMapped(fault_vaddr, fault_size)) { + return false; + } + 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); + } + return true; +} + +bool RenderContext::InvalidateMemory(uint64_t vaddr, uint64_t size) { + if (!IsMapped(vaddr, size)) { + return false; + } + m_buffer_cache.InvalidateMemory(vaddr, size); + m_texture_cache.InvalidateMemory(vaddr, size); + return true; +} + +bool RenderContext::IsMapped(uint64_t vaddr, uint64_t size) const noexcept { + if (!GuestRange {vaddr, size}.Valid()) { + return false; + } + std::shared_lock lock(m_mapped_ranges_mutex); + return m_mapped_ranges.Contains(vaddr, size); +} + +void RenderContext::MapMemory(uint64_t vaddr, uint64_t size) { + std::lock_guard lock(m_mapped_ranges_mutex); + m_mapped_ranges.Add(vaddr, size); +} + +void RenderContext::UnmapMemory(uint64_t vaddr, uint64_t size) { + if (CommandScheduler::InDeferredOperation()) { + EXIT("unsupported memory unmap from an asynchronous GPU completion, " + "addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n", + vaddr, size); + } + const auto unmap = [this, vaddr, size] { + if (m_command_scheduler.Active()) { + const auto tick = m_command_scheduler.CurrentTick(); + m_command_scheduler.Finish(); + m_command_scheduler.WaitPriorityOperations(tick); + } + m_buffer_cache.InvalidateMemory(vaddr, size); + m_texture_cache.UnmapMemory(vaddr, size); + std::lock_guard lock(m_mapped_ranges_mutex); + m_mapped_ranges.Subtract(vaddr, size); + }; + // Shutdown still owns the GPU while queued rendering drains, but its command lane no + // longer accepts external work. Use the guest GPU's state for the teardown route. + if (m_gpu == nullptr || m_gpu->IsStopping()) { + unmap(); + return; + } + m_gpu->SendCommandSync(unmap); +} + +void RenderContext::PrepareBda() { + std::shared_lock lock(m_mapped_ranges_mutex); + m_mapped_ranges.ForEach([this](uint64_t start, uint64_t end) { + m_buffer_cache.SynchronizeBuffersInRange(start, end - start); + }); + m_fault_process_pending = true; +} + +void RenderContext::RunGarbageCollector() { + if (m_fault_process_pending) { + m_fault_process_pending = false; + m_buffer_cache.ProcessFaultBuffer(); + } + m_texture_cache.ProcessDownloadImages(); + m_texture_cache.RunGarbageCollector(); + m_buffer_cache.RunGarbageCollector(); +} + void RenderContext::AddInterruptEq(LibKernel::EventQueue::KernelEqueue eq, int event_id) { Common::LockGuard lock(m_interrupt_mutex); diff --git a/src/graphics/host_gpu/renderer/renderContext.h b/src/graphics/host_gpu/renderer/renderContext.h index 294b08e8..f3c3e087 100644 --- a/src/graphics/host_gpu/renderer/renderContext.h +++ b/src/graphics/host_gpu/renderer/renderContext.h @@ -5,8 +5,9 @@ #include "common/assert.h" #include "common/common.h" #include "common/threads.h" +#include "graphics/host_gpu/pageManager.h" +#include "graphics/host_gpu/rangeSet.h" #include "graphics/host_gpu/renderer/cache/bufferCache.h" -#include "graphics/host_gpu/renderer/cache/gpuResourceManager.h" #include "graphics/host_gpu/renderer/cache/samplerCache.h" #include "graphics/host_gpu/renderer/cache/textureCache.h" #include "graphics/host_gpu/renderer/commandScheduler.h" @@ -15,6 +16,7 @@ #include "kernel/eventQueue.h" #include +#include #include namespace Libs::VideoOut { @@ -42,11 +44,18 @@ public: PipelineCache& GetPipelineCache() { return m_pipeline_cache; } DescriptorHeap& GetDescriptorHeap() { return m_descriptor_heap; } SamplerCache& GetSamplerCache() { return m_sampler_cache; } - GpuResourceManager& GetGpuResources() { return m_gpu_resources; } - BufferCache& GetBufferCache() { return m_gpu_resources.GetBufferCache(); } - TextureCache& GetTextureCache() { return m_gpu_resources.GetTextureCache(); } + BufferCache& GetBufferCache() { return m_buffer_cache; } + TextureCache& GetTextureCache() { return m_texture_cache; } RenderExecutor& GetRenderExecutor() { return m_render_executor; } + [[nodiscard]] bool HandleFault(PageFaultAccess access, uint64_t fault_vaddr) noexcept; + [[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); + void UnmapMemory(uint64_t vaddr, uint64_t size); + void PrepareBda(); + void RunGarbageCollector(); + void AddInterruptEq(LibKernel::EventQueue::KernelEqueue eq, int event_id); void DeleteInterruptEq(LibKernel::EventQueue::KernelEqueue eq, int event_id); void TriggerInterrupt(int event_id, uint32_t context_id); @@ -64,9 +73,14 @@ private: DescriptorHeap m_descriptor_heap; PipelineCache m_pipeline_cache; SamplerCache m_sampler_cache; - GpuResourceManager m_gpu_resources; + PageManager m_page_manager; + BufferCache m_buffer_cache; + TextureCache m_texture_cache; + mutable std::shared_mutex m_mapped_ranges_mutex; + RangeSet m_mapped_ranges; std::unique_ptr m_gpu; VideoOut::VideoOutDriver* m_video_out = nullptr; + bool m_fault_process_pending = false; Common::Mutex m_interrupt_mutex; std::vector m_interrupt_eqs; diff --git a/src/graphics/host_gpu/renderer/renderDraw.cpp b/src/graphics/host_gpu/renderer/renderDraw.cpp index 04e31da6..5129eb92 100644 --- a/src/graphics/host_gpu/renderer/renderDraw.cpp +++ b/src/graphics/host_gpu/renderer/renderDraw.cpp @@ -46,42 +46,34 @@ namespace Libs::Graphics { -int32_t ResolveVertexOffset(uint32_t index_offset, const ShaderVertexInputInfo& vs_input_info) { - if (index_offset != 0 || !vs_input_info.fetch_embedded) { - return static_cast(index_offset); +std::pair ResolveDrawOffsets(uint32_t index_offset, + const ShaderVertexInputInfo& vs_input_info) { + auto vertex_offset = static_cast(index_offset); + uint32_t instance_offset = 0; + if (!vs_input_info.fetch_embedded) { + return {vertex_offset, instance_offset}; } EXIT_IF(!vs_input_info.stage); const auto& program = *vs_input_info.stage.program; const auto& resources = vs_input_info.stage.resources; - if (program.info.vertex_offset_sgpr >= static_cast(program.user_data_base)) { + if (index_offset == 0 && + program.info.vertex_offset_sgpr >= static_cast(program.user_data_base)) { const auto index = static_cast(program.info.vertex_offset_sgpr) - program.user_data_base; if (index < resources.user_data.size()) { - return static_cast(resources.user_data[index]); + vertex_offset = static_cast(resources.user_data[index]); } } - - return 0; -} - -uint32_t ResolveInstanceOffset(const ShaderVertexInputInfo& vs_input_info) { - if (!vs_input_info.fetch_embedded) { - return 0; - } - - EXIT_IF(!vs_input_info.stage); - const auto& program = *vs_input_info.stage.program; - const auto& resources = vs_input_info.stage.resources; if (program.info.instance_offset_sgpr >= static_cast(program.user_data_base)) { const auto index = static_cast(program.info.instance_offset_sgpr) - program.user_data_base; if (index < resources.user_data.size()) { - return resources.user_data[index]; + instance_offset = resources.user_data[index]; } } - return 0; + return {vertex_offset, instance_offset}; } static std::atomic g_draw_state_log_count = 0; @@ -467,11 +459,13 @@ struct DrawRenderState { }; struct DrawCallInfo { - const char* name = nullptr; CommandBufferDebugOp debug_op = CommandBufferDebugOp::DrawIndex; uint32_t index_count = 0; uint32_t instance_count = 0; uint32_t first_instance = 0; + + [[nodiscard]] bool IsIndexed() const { return debug_op == CommandBufferDebugOp::DrawIndex; } + [[nodiscard]] const char* Name() const { return IsIndexed() ? "DrawIndex" : "DrawIndexAuto"; } }; RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderColorInfo* colors, @@ -662,7 +656,6 @@ static bool ConsumeMetadataColorOperation(const CommandBuffer& buffer) { } struct DrawEmitInfo { - bool indexed = false; int32_t vertex_offset = 0; uint32_t first_vertex = 0; uint32_t first_instance = 0; @@ -904,14 +897,14 @@ static bool ResolvePrimitiveRestart(const CommandBuffer& buffer, bool RenderExecutor::PrepareDrawRenderState(CommandBuffer& buffer, const DrawCallInfo& draw, uint32_t render_target_slice_offset, - bool log_setup_phases, DrawRenderState& state) { + DrawRenderState& state) { auto& ctx = buffer.GetRegisters(); if (ResolveColorTargets(buffer, render_target_slice_offset)) { return false; } - if (log_setup_phases) { - LogDrawPhase(draw.name, "ResolveRenderColorTarget"); + if (draw.IsIndexed()) { + LogDrawPhase(draw.Name(), "ResolveRenderColorTarget"); } for (uint32_t slot = 0; slot < RENDER_COLOR_ATTACHMENTS_MAX; slot++) { if (slot == 0 || (render_target_mask_slot(ctx.GetRenderTargetMask(), slot) != 0 && @@ -923,14 +916,14 @@ bool RenderExecutor::PrepareDrawRenderState(CommandBuffer& buffer, const DrawCal } } } - if (log_setup_phases) { - LogDrawPhase(draw.name, "ResolveRenderDepthTarget"); + if (draw.IsIndexed()) { + LogDrawPhase(draw.Name(), "ResolveRenderDepthTarget"); } ResolveRenderDepthTarget(buffer, state.depth_info); state.ps_active = DrawHasActivePixelShader(buffer); if (state.color_count == 0 && !state.depth_info.image_id && !state.ps_active) { - LogFramebufferSkip(draw.name, state.color_info[0], state.depth_info, buffer, + LogFramebufferSkip(draw.Name(), state.color_info[0], state.depth_info, buffer, draw.index_count, 0); return false; } @@ -938,7 +931,7 @@ bool RenderExecutor::PrepareDrawRenderState(CommandBuffer& buffer, const DrawCal return true; } -static void RefreshShaders(CommandBuffer& buffer, const DrawCallInfo& draw, bool log_phases, +static void RefreshShaders(CommandBuffer& buffer, const DrawCallInfo& draw, DrawRenderState& state) { auto& ctx = buffer.GetRegisters(); auto& sh_ctx = buffer.GetShaders(); @@ -955,8 +948,8 @@ static void RefreshShaders(CommandBuffer& buffer, const DrawCallInfo& draw, bool target_export_mapping[state.color_info[i].target_slot] = state.color_info[i].export_mapping; } auto& pipeline_cache = buffer.GetContext().GetPipelineCache(); - if (log_phases) { - LogDrawPhase(draw.name, "GetGraphicsPrograms"); + if (draw.IsIndexed()) { + LogDrawPhase(draw.Name(), "GetGraphicsPrograms"); } state.programs = pipeline_cache.GetGraphicsPrograms( vertex_shader_info, pixel_shader_info, shader_regs, ctx, buffer.GetUserConfig(), @@ -974,17 +967,14 @@ static PreparedIndexBuffer PrepareIndexBuffer(CommandBuffer& buffe auto& stream = buffer.GetContext().GetBufferCache().GetUtilityBuffer(MemoryUsage::Stream); prepared.offset = stream.Copy(source.host_data, source.size, 16); prepared.buffer = stream.Handle(); + SetVulkanObjectNameF(buffer.GetContext().GetGraphics().device, prepared.buffer, + "Kyty.IndexBuffer[guest=transient size=0x{:x} type={}]", source.size, + static_cast(source.type)); } else { auto [buffer_ptr, offset] = buffer.GetContext().GetBufferCache().ObtainBuffer(source.address, source.size, false); prepared.buffer = buffer_ptr->Handle(); prepared.offset = offset; - } - if (source.host_data != nullptr) { - SetVulkanObjectNameF(buffer.GetContext().GetGraphics().device, prepared.buffer, - "Kyty.IndexBuffer[guest=transient size=0x{:x} type={}]", source.size, - static_cast(source.type)); - } else { SetVulkanObjectNameF(buffer.GetContext().GetGraphics().device, prepared.buffer, "Kyty.IndexBuffer[guest=0x{:016x} size=0x{:x} type={}]", source.address, source.size, static_cast(source.type)); @@ -1011,19 +1001,19 @@ static void CommitIndexBuffer(vk::CommandBuffer vk_buffer, const PreparedIndexBu } static void LogDrawStateIfNeeded(const CommandBuffer& buffer, const DrawCallInfo& draw, - const DrawRenderState& state, bool always_log, - bool force_legacy_rect_log, uint32_t index_type_and_size, + const DrawRenderState& state, uint32_t index_type_and_size, const void* index_addr) { if (!graphics_debug_dump_enabled()) { return; } - if (!always_log && !force_legacy_rect_log) { + if (!draw.IsIndexed() && + buffer.GetUserConfig().GetPrimType() != Prospero::PrimitiveType::kRectListLegacy) { return; } if (state.ps_active) { - LogDrawTargetState(draw.name, state.color_info[0], state.depth_info, buffer, + LogDrawTargetState(draw.Name(), state.color_info[0], state.depth_info, buffer, state.ps_input_info, draw.index_count, 0); } LogDrawInputState(buffer, state.color_info[0], state.vs_input_info, index_type_and_size, @@ -1041,7 +1031,7 @@ static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_ case Prospero::PrimitiveType::kTriFan: case Prospero::PrimitiveType::kTriStrip: case Prospero::PrimitiveType::kRectList: - if (emit.indexed) { + if (draw.IsIndexed()) { vk_buffer.drawIndexed(draw.index_count, draw.instance_count, 0, emit.vertex_offset, emit.first_instance); } else { @@ -1050,7 +1040,7 @@ static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_ } break; case Prospero::PrimitiveType::kRectListLegacy: - if (emit.indexed) { + if (draw.IsIndexed()) { EXIT("unknown primitive type: %u\n", static_cast(ucfg.GetPrimType())); } // Sarah @@ -1060,7 +1050,7 @@ static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_ case Prospero::PrimitiveType::kQuadListLegacy: EXIT_NOT_IMPLEMENTED((draw.index_count & 0x3u) != 0); for (uint32_t i = 0; i < draw.index_count; i += 4) { - if (emit.indexed) { + if (draw.IsIndexed()) { vk_buffer.drawIndexed(4, draw.instance_count, i, emit.vertex_offset, emit.first_instance); } else { @@ -1077,8 +1067,7 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff const DrawCallInfo& draw, DrawRenderState& state, vk::PrimitiveTopology topology, const DrawEmitInfo& emit, const DrawIndexBufferSource& index_source, - bool primitive_restart_enable, bool log_pipeline_phase, - bool set_bind_debug, bool set_auto_debug) { + bool primitive_restart_enable) { auto& ucfg = buffer.GetUserConfig(); const bool mesh_active = state.vs_input_info.stage.program->stage == ShaderType::Mesh; uint32_t mesh_groups = 0; @@ -1086,7 +1075,7 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff const auto& mesh = state.vs_input_info.mesh; if (primitive_restart_enable || mesh.primitives_per_group == 0) { EXIT("unsupported mesh draw: primitive=%u indexed=%u restart=%u\n", - static_cast(ucfg.GetPrimType()), emit.indexed, primitive_restart_enable); + static_cast(ucfg.GetPrimType()), draw.IsIndexed(), primitive_restart_enable); } const auto primitives = mesh.InputPrimitiveCount(draw.index_count); if (primitives == 0 || draw.instance_count == 0) { @@ -1103,20 +1092,20 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff } } - if (mesh_active && emit.indexed) { + if (mesh_active && draw.IsIndexed()) { // Register the original guest indices for shader reads; PrepareGraphicsBindings // synchronizes registered BDA ranges before any draw commands are committed. (void)m_context.GetBufferCache().FindBuffer( index_source.address, static_cast(draw.index_count) * index_source.guest_element_size); } - LogDrawPhase(draw.name, "PrepareBindings"); + LogDrawPhase(draw.Name(), "PrepareBindings"); auto bindings = PrepareGraphicsBindings(state.vs_input_info.stage, state.ps_input_info.stage, state.ps_active); PreparedVertexBuffers vertex_bindings; PreparedIndexBuffer index_binding; if (!mesh_active) { - LogDrawPhase(draw.name, "PrepareVertexBuffers"); + LogDrawPhase(draw.Name(), "PrepareVertexBuffers"); vertex_bindings = AcquireVertexBuffers(buffer, state.vs_input_info); index_binding = PrepareIndexBuffer(buffer, index_source); } @@ -1124,10 +1113,10 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff AcquireRenderTargets(buffer, state.color_info, state.color_count, state.depth_info, bindings.pixel); - if (log_pipeline_phase) { - LogDrawPhase(draw.name, "CreatePipeline"); + if (draw.IsIndexed()) { + LogDrawPhase(draw.Name(), "CreatePipeline"); } - auto& pipeline = m_context.GetPipelineCache().CreateGraphicsPipeline( + auto& pipeline = m_context.GetPipelineCache().GetGraphicsPipeline( std::span {state.color_info, state.color_count}, state.depth_info, state.vs_input_info, buffer, state.ps_active ? &state.ps_input_info : nullptr, topology, primitive_restart_enable, state.programs.vertex, state.programs.pixel); @@ -1136,19 +1125,12 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff // point onward, every operation targets the current command buffer and cannot touch guest // memory. auto vk_buffer = buffer.Handle(); - if (set_bind_debug) { - SetDrawDebugPhase(buffer, submit_id, draw, 0x100u); - } - if (set_auto_debug) { - SetDrawDebugPhase(buffer, submit_id, draw, 0x200u); - } + SetDrawDebugPhase(buffer, submit_id, draw, draw.IsIndexed() ? 0x100u : 0x200u); if (!mesh_active) { CommitVertexBuffers(vk_buffer, vertex_bindings); } - if (bindings.pixel.has_value()) { - if (set_auto_debug) { - SetDrawDebugPhase(buffer, submit_id, draw, 0x300u); - } + if (bindings.pixel && !draw.IsIndexed()) { + SetDrawDebugPhase(buffer, submit_id, draw, 0x300u); } std::array descriptor_stages {&bindings.vertex, nullptr}; const size_t descriptor_stage_count = bindings.pixel.has_value() ? 2u : 1u; @@ -1160,7 +1142,7 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff if (mesh_active) { const uint32_t draw_data[] { draw.index_count, - emit.indexed ? static_cast(emit.vertex_offset) : emit.first_vertex, + draw.IsIndexed() ? static_cast(emit.vertex_offset) : emit.first_vertex, emit.first_instance, index_source.guest_element_size, static_cast(index_source.address), static_cast(index_source.address >> 32u)}; @@ -1183,13 +1165,13 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff : vk::ImageAspectFlags {}); } - LogDrawPhase(draw.name, "BeginRendering"); - if (set_auto_debug) { + LogDrawPhase(draw.Name(), "BeginRendering"); + if (!draw.IsIndexed()) { SetDrawDebugPhase(buffer, submit_id, draw, 0x400u); } m_context.GetCommandScheduler().BeginRendering(rendering); vk_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.pipeline); - if (set_auto_debug) { + if (!draw.IsIndexed()) { SetDrawDebugPhase(buffer, submit_id, draw, 0x500u); } if (mesh_active) { @@ -1198,7 +1180,7 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff EmitDrawPrimitives(ucfg, vk_buffer, state.vs_input_info, draw, emit); } - if (set_auto_debug) { + if (!draw.IsIndexed()) { SetDrawDebugPhase(buffer, submit_id, draw, 0x600u); } vk::PipelineStageFlags shader_write_stages = {}; @@ -1213,8 +1195,8 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff m_context.GetCommandScheduler().EndRendering(); ShaderWriteBarrier(vk_buffer, shader_write_stages); } - LogDrawPhase(draw.name, "DrawComplete"); - if (set_auto_debug) { + LogDrawPhase(draw.Name(), "DrawComplete"); + if (!draw.IsIndexed()) { SetDrawDebugPhase(buffer, submit_id, draw, 0x700u); } } @@ -1248,7 +1230,7 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer, } if (graphics_debug_dump_enabled()) { - sh_print("GraphicsRenderDrawIndex():Shader:", sh_ctx); + LOGF("GraphicsRenderDrawIndex():Shader:\n"); uc_print("GraphicsRenderDrawIndex():UserConfig:", ucfg); hw_print(buffer); @@ -1304,34 +1286,30 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer, index_source.size = expanded_indices.size() * sizeof(uint16_t); } - const DrawCallInfo draw {"DrawIndex", CommandBufferDebugOp::DrawIndex, args.index_count, + const DrawCallInfo draw {CommandBufferDebugOp::DrawIndex, args.index_count, args.instance_count, args.first_instance}; DrawRenderState state {}; - if (!PrepareDrawRenderState(buffer, draw, args.render_target_slice_offset, true, - state)) { + if (!PrepareDrawRenderState(buffer, draw, args.render_target_slice_offset, state)) { ResetBindings(); return; } - RefreshShaders(buffer, draw, true, state); + RefreshShaders(buffer, draw, state); - LogDrawStateIfNeeded(buffer, draw, state, true, false, args.index_type_and_size, + LogDrawStateIfNeeded(buffer, draw, state, args.index_type_and_size, args.index_addr); const bool indirect = args.offset_source == DrawOffsetSource::IndirectArgs; - const auto vertex_offset = - indirect - ? args.base_vertex - : ResolveVertexOffset(ucfg.GetIndexOffset(), state.vs_input_info) + args.base_vertex; + const auto [vertex_offset, instance_offset] = + indirect ? std::pair {0, args.first_instance} + : ResolveDrawOffsets(ucfg.GetIndexOffset(), state.vs_input_info); DrawEmitInfo emit {}; - emit.indexed = true; - emit.vertex_offset = vertex_offset; - emit.first_instance = - indirect ? args.first_instance : ResolveInstanceOffset(state.vs_input_info); + emit.vertex_offset = vertex_offset + args.base_vertex; + emit.first_instance = instance_offset; ExecutePreparedDraw(submit_id, buffer, draw, state, topology, emit, index_source, - primitive_restart, true, true, false); + primitive_restart); ResetBindings(); } @@ -1364,7 +1342,7 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const D } if (graphics_debug_dump_enabled()) { - sh_print("GraphicsRenderDrawIndexAuto():Shader:", sh_ctx); + LOGF("GraphicsRenderDrawIndexAuto():Shader:\n"); uc_print("GraphicsRenderDrawIndexAuto():UserConfig:", ucfg); hw_print(buffer); @@ -1380,12 +1358,11 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const D hw_check(buffer); - const DrawCallInfo draw {"DrawIndexAuto", CommandBufferDebugOp::DrawIndexAuto, + const DrawCallInfo draw {CommandBufferDebugOp::DrawIndexAuto, args.vertex_count, args.instance_count, args.first_instance}; DrawRenderState state {}; - if (!PrepareDrawRenderState(buffer, draw, args.render_target_slice_offset, false, - state)) { + if (!PrepareDrawRenderState(buffer, draw, args.render_target_slice_offset, state)) { ResetBindings(); return; } @@ -1395,7 +1372,7 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const D ResetBindings(); return; } - RefreshShaders(buffer, draw, false, state); + RefreshShaders(buffer, draw, state); const bool rect_list = topology == vk::PrimitiveTopology::ePatchList; if (rect_list && state.vs_input_info.buffers_num == 0 && @@ -1411,23 +1388,18 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const D return; } - LogDrawStateIfNeeded(buffer, draw, state, false, - ucfg.GetPrimType() == Prospero::PrimitiveType::kRectListLegacy, 0, - nullptr); + LogDrawStateIfNeeded(buffer, draw, state, 0, nullptr); const bool indirect = args.offset_source == DrawOffsetSource::IndirectArgs; - const auto vertex_offset = - indirect ? static_cast(args.first_vertex) - : ResolveVertexOffset(ucfg.GetIndexOffset(), state.vs_input_info) + - static_cast(args.first_vertex); + const auto [vertex_offset, instance_offset] = + indirect ? std::pair {0, args.first_instance} + : ResolveDrawOffsets(ucfg.GetIndexOffset(), state.vs_input_info); DrawEmitInfo emit {}; - emit.first_vertex = static_cast(vertex_offset); - emit.first_instance = - indirect ? args.first_instance : ResolveInstanceOffset(state.vs_input_info); + emit.first_vertex = static_cast(vertex_offset + static_cast(args.first_vertex)); + emit.first_instance = instance_offset; DrawIndexBufferSource index_source {}; - ExecutePreparedDraw(submit_id, buffer, draw, state, topology, emit, index_source, false, false, - false, true); + ExecutePreparedDraw(submit_id, buffer, draw, state, topology, emit, index_source, false); ResetBindings(); } diff --git a/src/graphics/host_gpu/renderer/renderDraw.h b/src/graphics/host_gpu/renderer/renderDraw.h index d695354b..6c6de1bc 100644 --- a/src/graphics/host_gpu/renderer/renderDraw.h +++ b/src/graphics/host_gpu/renderer/renderDraw.h @@ -2,14 +2,14 @@ #define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_RENDERDRAW_H_ #include +#include namespace Libs::Graphics { struct ShaderVertexInputInfo; -[[nodiscard]] int32_t ResolveVertexOffset(uint32_t index_offset, - const ShaderVertexInputInfo& vs_input_info); -[[nodiscard]] uint32_t ResolveInstanceOffset(const ShaderVertexInputInfo& vs_input_info); +[[nodiscard]] std::pair +ResolveDrawOffsets(uint32_t index_offset, const ShaderVertexInputInfo& vs_input_info); } // namespace Libs::Graphics diff --git a/src/graphics/host_gpu/renderer/renderTarget.h b/src/graphics/host_gpu/renderer/renderTarget.h index 8969814b..ff18dd24 100644 --- a/src/graphics/host_gpu/renderer/renderTarget.h +++ b/src/graphics/host_gpu/renderer/renderTarget.h @@ -84,14 +84,6 @@ struct PipelineStencilDynamicState { #pragma pack(pop) -inline constexpr bool stencil_face_accesses_attachment(const PipelineStencilStaticState& state, - const PipelineStencilDynamicState& dynamic) { - return state.compareOp != vk::CompareOp::eAlways || - (dynamic.writeMask != 0 && - (state.failOp != vk::StencilOp::eKeep || state.passOp != vk::StencilOp::eKeep || - state.depthFailOp != vk::StencilOp::eKeep)); -} - static_assert(std::is_trivially_copyable_v); static_assert(std::is_standard_layout_v); static_assert(alignof(PipelineStencilStaticState) == 1); diff --git a/src/graphics/host_gpu/shaders/gpu_tiler_standard64.inc b/src/graphics/host_gpu/shaders/gpu_tiler_standard64.inc index 1e2cfb09..fc590a1d 100644 --- a/src/graphics/host_gpu/shaders/gpu_tiler_standard64.inc +++ b/src/graphics/host_gpu/shaders/gpu_tiler_standard64.inc @@ -1,29 +1,17 @@ +#include "gpu_tiler_standard.inc" uint standard64_offset(uvec2 p) { uint x = p.x, y = p.y; + uint offset = standard_offset(p); switch (ELEMENT_BYTES) { - case 1: return (x & 0x0fu) ^ ((x << 5) & 0x200u) ^ ((x << 6) & 0x800u) ^ - ((x << 7) & 0x2000u) ^ ((x << 8) & 0x8000u) ^ - ((y << 4) & 0x1f0u) ^ ((y << 5) & 0x400u) ^ - ((y << 6) & 0x1000u) ^ ((y << 7) & 0x4000u); - case 2: return ((x << 1) & 0x00eu) ^ ((x << 4) & 0x080u) ^ - ((x << 5) & 0x200u) ^ ((x << 6) & 0x800u) ^ - ((x << 7) & 0x2000u) ^ ((x << 8) & 0x8000u) ^ - ((y << 4) & 0x070u) ^ ((y << 5) & 0x100u) ^ - ((y << 6) & 0x400u) ^ ((y << 7) & 0x1000u) ^ ((y << 8) & 0x4000u); - case 4: return ((x << 2) & 0x00cu) ^ ((x << 5) & 0x080u) ^ - ((x << 6) & 0x200u) ^ ((x << 7) & 0x800u) ^ - ((x << 8) & 0x2000u) ^ ((x << 9) & 0x8000u) ^ - ((y << 4) & 0x070u) ^ ((y << 5) & 0x100u) ^ - ((y << 6) & 0x400u) ^ ((y << 7) & 0x1000u) ^ ((y << 8) & 0x4000u); - case 8: return ((x << 3) & 0x008u) ^ ((x << 5) & 0x0c0u) ^ - ((x << 6) & 0x200u) ^ ((x << 7) & 0x800u) ^ - ((x << 8) & 0x2000u) ^ ((x << 9) & 0x8000u) ^ - ((y << 4) & 0x030u) ^ ((y << 6) & 0x100u) ^ - ((y << 7) & 0x400u) ^ ((y << 8) & 0x1000u) ^ ((y << 9) & 0x4000u); - default: return ((x << 6) & 0x0c0u) ^ ((x << 7) & 0x200u) ^ - ((x << 8) & 0x800u) ^ ((x << 9) & 0x2000u) ^ - ((x << 10) & 0x8000u) ^ ((y << 4) & 0x030u) ^ - ((y << 6) & 0x100u) ^ ((y << 7) & 0x400u) ^ - ((y << 8) & 0x1000u) ^ ((y << 9) & 0x4000u); + case 1: return offset ^ ((x << 7) & 0x2000u) ^ ((x << 8) & 0x8000u) ^ + ((y << 6) & 0x1000u) ^ ((y << 7) & 0x4000u); + case 2: return offset ^ ((x << 7) & 0x2000u) ^ ((x << 8) & 0x8000u) ^ + ((y << 7) & 0x1000u) ^ ((y << 8) & 0x4000u); + case 4: return offset ^ ((x << 8) & 0x2000u) ^ ((x << 9) & 0x8000u) ^ + ((y << 7) & 0x1000u) ^ ((y << 8) & 0x4000u); + case 8: return offset ^ ((x << 8) & 0x2000u) ^ ((x << 9) & 0x8000u) ^ + ((y << 8) & 0x1000u) ^ ((y << 9) & 0x4000u); + default: return offset ^ ((x << 9) & 0x2000u) ^ ((x << 10) & 0x8000u) ^ + ((y << 8) & 0x1000u) ^ ((y << 9) & 0x4000u); } } diff --git a/src/graphics/host_gpu/vulkanCommon.h b/src/graphics/host_gpu/vulkanCommon.h index 6c8998a4..4f520e6b 100644 --- a/src/graphics/host_gpu/vulkanCommon.h +++ b/src/graphics/host_gpu/vulkanCommon.h @@ -38,7 +38,6 @@ void SetVulkanObjectNameF(vk::Device device, Handle handle, fmt::format_string(args)...); vk::DebugUtilsObjectNameInfoEXT info {}; - info.sType = vk::StructureType::eDebugUtilsObjectNameInfoEXT; info.objectType = Handle::objectType; info.objectHandle = static_cast( reinterpret_cast(static_cast(handle))); diff --git a/src/graphics/presentation/window/vulkanWindow.cpp b/src/graphics/presentation/window/vulkanWindow.cpp index 50caec4e..9cdcc7d5 100644 --- a/src/graphics/presentation/window/vulkanWindow.cpp +++ b/src/graphics/presentation/window/vulkanWindow.cpp @@ -64,7 +64,6 @@ struct VulkanExtensions { vk::PhysicalDeviceVulkan12Features WindowContext::RequiredVulkan12Features() noexcept { vk::PhysicalDeviceVulkan12Features features {}; - features.sType = vk::StructureType::ePhysicalDeviceVulkan12Features; features.samplerMirrorClampToEdge = VK_TRUE; features.timelineSemaphore = VK_TRUE; features.shaderOutputLayer = VK_TRUE; @@ -76,7 +75,6 @@ vk::PhysicalDeviceVulkan12Features WindowContext::RequiredVulkan12Features() noe vk::PhysicalDeviceVulkan13Features WindowContext::RequiredVulkan13Features() noexcept { vk::PhysicalDeviceVulkan13Features features {}; - features.sType = vk::StructureType::ePhysicalDeviceVulkan13Features; features.dynamicRendering = VK_TRUE; features.synchronization2 = VK_TRUE; return features; @@ -201,35 +199,24 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa vk::PhysicalDeviceFeatures2 device_features2 {}; vk::PhysicalDeviceVulkan13Features features13 {}; - features13.sType = vk::StructureType::ePhysicalDeviceVulkan13Features; - features13.pNext = nullptr; vk::PhysicalDeviceColorWriteEnableFeaturesEXT color_write_ext {}; - color_write_ext.sType = vk::StructureType::ePhysicalDeviceColorWriteEnableFeaturesEXT; - color_write_ext.pNext = nullptr; vk::PhysicalDeviceDepthClipEnableFeaturesEXT depth_clip_enable {}; - depth_clip_enable.sType = vk::StructureType::ePhysicalDeviceDepthClipEnableFeaturesEXT; depth_clip_enable.pNext = &color_write_ext; vk::PhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control {}; - depth_clip_control.sType = vk::StructureType::ePhysicalDeviceDepthClipControlFeaturesEXT; depth_clip_control.pNext = &depth_clip_enable; vk::PhysicalDeviceVulkan12Features features12 {}; - features12.sType = vk::StructureType::ePhysicalDeviceVulkan12Features; #if defined(__APPLE__) features12.pNext = &depth_clip_control; #else vk::PhysicalDeviceFragmentShaderBarycentricFeaturesKHR fragment_barycentric {}; - fragment_barycentric.sType = - vk::StructureType::ePhysicalDeviceFragmentShaderBarycentricFeaturesKHR; fragment_barycentric.pNext = &depth_clip_control; features12.pNext = &fragment_barycentric; #endif features13.pNext = &features12; - - device_features2.sType = vk::StructureType::ePhysicalDeviceFeatures2; device_features2.pNext = &features13; device.getFeatures2(&device_features2); @@ -506,83 +493,31 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa } } -static void VulkanInitSubgroupSizeControl(vk::PhysicalDevice physical_device, - GraphicContext& graphics) { - EXIT_IF(physical_device == nullptr); - - vk::PhysicalDeviceSubgroupSizeControlProperties subgroup_size_control {}; - subgroup_size_control.sType = vk::StructureType::ePhysicalDeviceSubgroupSizeControlProperties; - subgroup_size_control.pNext = nullptr; - - vk::PhysicalDeviceVulkan11Properties properties11 {}; - properties11.sType = vk::StructureType::ePhysicalDeviceVulkan11Properties; - properties11.pNext = &subgroup_size_control; - - vk::PhysicalDeviceProperties2 properties2 {}; - properties2.sType = vk::StructureType::ePhysicalDeviceProperties2; - properties2.pNext = &properties11; - - physical_device.getProperties2(&properties2); - - vk::PhysicalDeviceVulkan13Features features13 {}; - features13.sType = vk::StructureType::ePhysicalDeviceVulkan13Features; - features13.pNext = nullptr; - - vk::PhysicalDeviceFeatures2 features2 {}; - features2.sType = vk::StructureType::ePhysicalDeviceFeatures2; - features2.pNext = &features13; - - physical_device.getFeatures2(&features2); - - graphics.subgroup_size = properties11.subgroupSize; - graphics.min_subgroup_size = subgroup_size_control.minSubgroupSize; - graphics.max_subgroup_size = subgroup_size_control.maxSubgroupSize; - graphics.required_subgroup_size_stages = subgroup_size_control.requiredSubgroupSizeStages; - graphics.compute_subgroup_size_control_enabled = - features13.subgroupSizeControl == VK_TRUE && - (graphics.required_subgroup_size_stages & vk::ShaderStageFlagBits::eCompute) && - subgroup_size_control.minSubgroupSize <= 64 && - subgroup_size_control.maxSubgroupSize >= 64; - - LOGF("Vulkan subgroup: default=%u min=%u max=%u stages=0x%08x size_control=%s wave64=%s\n", - graphics.subgroup_size, graphics.min_subgroup_size, graphics.max_subgroup_size, - static_cast(graphics.required_subgroup_size_stages), - graphics.compute_subgroup_size_control_enabled ? "true" : "false", - graphics.SupportsComputeWave64() ? "true" : "false"); -} - -static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const VulkanExtensions& r, - uint32_t queue_family, - const std::vector& device_extensions, - GraphicContext& graphics) { +static vk::Device VulkanCreateDevice(GraphicContext& graphics, + const std::vector& device_extensions) { + const auto physical_device = graphics.physical_device; + const auto queue_family = graphics.queue_family; EXIT_IF(physical_device == nullptr); EXIT_IF(queue_family == static_cast(-1)); const float queue_priority = 1.0f; vk::DeviceQueueCreateInfo queue_create_info {}; - queue_create_info.sType = vk::StructureType::eDeviceQueueCreateInfo; queue_create_info.queueFamilyIndex = queue_family; queue_create_info.queueCount = 1; queue_create_info.pQueuePriorities = &queue_priority; vk::PhysicalDeviceColorWriteEnableFeaturesEXT color_write_ext {}; - color_write_ext.sType = vk::StructureType::ePhysicalDeviceColorWriteEnableFeaturesEXT; - color_write_ext.pNext = nullptr; color_write_ext.colorWriteEnable = VK_TRUE; vk::PhysicalDeviceDepthClipEnableFeaturesEXT depth_clip_enable {}; - depth_clip_enable.sType = vk::StructureType::ePhysicalDeviceDepthClipEnableFeaturesEXT; depth_clip_enable.pNext = &color_write_ext; depth_clip_enable.depthClipEnable = VK_TRUE; vk::PhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control {}; - depth_clip_control.sType = vk::StructureType::ePhysicalDeviceDepthClipControlFeaturesEXT; // MoltenVK lacks VK_EXT_depth_clip_enable and VK_EXT_color_write_enable, so drop those // feature structs from the chain on macOS (the renderer falls back to default depth // clipping and static color-write masks). -#if defined(__APPLE__) - depth_clip_control.pNext = nullptr; -#else +#if !defined(__APPLE__) depth_clip_control.pNext = &depth_clip_enable; #endif depth_clip_control.depthClipControl = VK_TRUE; @@ -591,40 +526,19 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V features12.pNext = &depth_clip_control; vk::PhysicalDeviceVulkan13Features supported_features13 {}; - supported_features13.sType = vk::StructureType::ePhysicalDeviceVulkan13Features; - - vk::PhysicalDeviceVulkan12Features supported_features12 {}; - supported_features12.sType = vk::StructureType::ePhysicalDeviceVulkan12Features; - supported_features12.pNext = nullptr; - supported_features13.pNext = &supported_features12; - -#if !defined(__APPLE__) - vk::PhysicalDeviceFragmentShaderBarycentricFeaturesKHR supported_fragment_barycentric {}; - supported_fragment_barycentric.sType = - vk::StructureType::ePhysicalDeviceFragmentShaderBarycentricFeaturesKHR; - supported_fragment_barycentric.pNext = nullptr; - supported_features12.pNext = &supported_fragment_barycentric; -#endif const auto robustness2_ext_enabled = HasExtension(device_extensions, VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); vk::PhysicalDeviceRobustness2FeaturesEXT supported_robustness2 {}; - supported_robustness2.sType = vk::StructureType::ePhysicalDeviceRobustness2FeaturesEXT; - supported_robustness2.pNext = nullptr; if (robustness2_ext_enabled) { -#if defined(__APPLE__) - supported_features12.pNext = &supported_robustness2; -#else - supported_fragment_barycentric.pNext = &supported_robustness2; -#endif + supported_features13.pNext = &supported_robustness2; } const bool mesh_extension = HasExtension(device_extensions, VK_EXT_MESH_SHADER_EXTENSION_NAME); vk::PhysicalDeviceMeshShaderFeaturesEXT supported_mesh {}; supported_mesh.pNext = &supported_features13; vk::PhysicalDeviceFeatures2 supported_features2 {}; - supported_features2.sType = vk::StructureType::ePhysicalDeviceFeatures2; supported_features2.pNext = mesh_extension ? static_cast(&supported_mesh) : static_cast(&supported_features13); const bool feedback_extensions = @@ -645,53 +559,53 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V supported_features2.pNext = &provoking_vertex; } physical_device.getFeatures2(&supported_features2); + graphics.mesh_shader_enabled = mesh_extension && supported_mesh.meshShader; + + vk::PhysicalDeviceSubgroupSizeControlProperties subgroup_size_control {}; + + vk::PhysicalDeviceVulkan11Properties properties11 {}; + properties11.pNext = &subgroup_size_control; + + vk::PhysicalDeviceProperties2 properties2 {}; + properties2.pNext = &properties11; + + if (graphics.mesh_shader_enabled) { + subgroup_size_control.pNext = &graphics.mesh_shader_properties; + } + physical_device.getProperties2(&properties2); + + graphics.subgroup_size = properties11.subgroupSize; + graphics.min_subgroup_size = subgroup_size_control.minSubgroupSize; + graphics.max_subgroup_size = subgroup_size_control.maxSubgroupSize; + graphics.required_subgroup_size_stages = subgroup_size_control.requiredSubgroupSizeStages; + graphics.compute_subgroup_size_control_enabled = + supported_features13.subgroupSizeControl == VK_TRUE && + (graphics.required_subgroup_size_stages & vk::ShaderStageFlagBits::eCompute) && + subgroup_size_control.minSubgroupSize <= 64 && + subgroup_size_control.maxSubgroupSize >= 64; + + LOGF("Vulkan subgroup: default=%u min=%u max=%u stages=0x%08x size_control=%s wave64=%s\n", + graphics.subgroup_size, graphics.min_subgroup_size, graphics.max_subgroup_size, + static_cast(graphics.required_subgroup_size_stages), + graphics.compute_subgroup_size_control_enabled ? "true" : "false", + graphics.SupportsComputeWave64() ? "true" : "false"); graphics.provoking_vertex_last_enabled = provoking_extension && provoking_vertex.provokingVertexLast; graphics.attachment_feedback_loop_enabled = feedback_extensions && feedback_layout.attachmentFeedbackLoopLayout && feedback_dynamic.attachmentFeedbackLoopDynamicState; LOGF("Vulkan depth feedback support: %s\n", graphics.attachment_feedback_loop_enabled ? "true" : "false"); - graphics.mesh_shader_enabled = mesh_extension && supported_mesh.meshShader; if (graphics.mesh_shader_enabled) { - vk::PhysicalDeviceProperties2 properties {}; - properties.pNext = &graphics.mesh_shader_properties; - physical_device.getProperties2(&properties); LOGF("Vulkan MeshEXT: invocations=%u vertices=%u primitives=%u shared=%u\n", graphics.mesh_shader_properties.maxMeshWorkGroupInvocations, graphics.mesh_shader_properties.maxMeshOutputVertices, graphics.mesh_shader_properties.maxMeshOutputPrimitives, graphics.mesh_shader_properties.maxMeshSharedMemorySize); } - const auto required_features12 = WindowContext::RequiredVulkan12Features(); - const auto required_features13 = WindowContext::RequiredVulkan13Features(); - EXIT_NOT_IMPLEMENTED(required_features12.samplerMirrorClampToEdge == VK_TRUE && - supported_features12.samplerMirrorClampToEdge != VK_TRUE); - EXIT_NOT_IMPLEMENTED(required_features12.timelineSemaphore == VK_TRUE && - supported_features12.timelineSemaphore != VK_TRUE); - EXIT_NOT_IMPLEMENTED(required_features13.dynamicRendering == VK_TRUE && - supported_features13.dynamicRendering != VK_TRUE); - EXIT_NOT_IMPLEMENTED(required_features13.synchronization2 == VK_TRUE && - supported_features13.synchronization2 != VK_TRUE); - EXIT_NOT_IMPLEMENTED(supported_features2.features.sampleRateShading != VK_TRUE); - EXIT_NOT_IMPLEMENTED(supported_features2.features.depthBiasClamp != VK_TRUE); - EXIT_NOT_IMPLEMENTED(supported_features2.features.shaderClipDistance != VK_TRUE); - EXIT_NOT_IMPLEMENTED(supported_features2.features.shaderCullDistance != VK_TRUE); - EXIT_NOT_IMPLEMENTED(supported_features2.features.largePoints != VK_TRUE); - EXIT_NOT_IMPLEMENTED(supported_features2.features.multiViewport != VK_TRUE); - EXIT_NOT_IMPLEMENTED(supported_features2.features.fillModeNonSolid != VK_TRUE); + // VulkanFindPhysicalDevice already checked the required creation features. These two + // requirements are specific to this creation path and are not part of device selection. EXIT_NOT_IMPLEMENTED(supported_features2.features.shaderInt64 != VK_TRUE); EXIT_NOT_IMPLEMENTED(supported_features2.features.vertexPipelineStoresAndAtomics != VK_TRUE); - EXIT_NOT_IMPLEMENTED(required_features12.shaderOutputLayer == VK_TRUE && - supported_features12.shaderOutputLayer != VK_TRUE); - EXIT_NOT_IMPLEMENTED(required_features12.shaderOutputViewportIndex == VK_TRUE && - supported_features12.shaderOutputViewportIndex != VK_TRUE); - EXIT_NOT_IMPLEMENTED(required_features12.bufferDeviceAddress == VK_TRUE && - supported_features12.bufferDeviceAddress != VK_TRUE); - EXIT_NOT_IMPLEMENTED(required_features12.shaderBufferInt64Atomics == VK_TRUE && - supported_features12.shaderBufferInt64Atomics != VK_TRUE); -#if !defined(__APPLE__) - EXIT_NOT_IMPLEMENTED(supported_fragment_barycentric.fragmentShaderBarycentric != VK_TRUE); -#endif vk::PhysicalDeviceFeatures device_features {}; device_features.fragmentStoresAndAtomics = VK_TRUE; device_features.samplerAnisotropy = VK_TRUE; @@ -715,13 +629,10 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V device_features.shaderInt64 = VK_TRUE; vk::PhysicalDeviceRobustness2FeaturesEXT robustness2 {}; - robustness2.sType = vk::StructureType::ePhysicalDeviceRobustness2FeaturesEXT; #if defined(__APPLE__) robustness2.pNext = &features12; #else vk::PhysicalDeviceFragmentShaderBarycentricFeaturesKHR fragment_barycentric {}; - fragment_barycentric.sType = - vk::StructureType::ePhysicalDeviceFragmentShaderBarycentricFeaturesKHR; fragment_barycentric.pNext = &features12; fragment_barycentric.fragmentShaderBarycentric = VK_TRUE; robustness2.pNext = &fragment_barycentric; @@ -732,11 +643,7 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V robustness2.nullDescriptor = supported_robustness2.nullDescriptor; } - const bool subgroup_size_control_enabled = - graphics.compute_subgroup_size_control_enabled && - supported_features13.subgroupSizeControl == VK_TRUE; - - auto features13 = required_features13; + auto features13 = WindowContext::RequiredVulkan13Features(); #if defined(__APPLE__) features13.pNext = robustness2_ext_enabled ? static_cast(&robustness2) : static_cast(&features12); @@ -745,7 +652,8 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V : static_cast(&fragment_barycentric); #endif features13.robustImageAccess = supported_features13.robustImageAccess; - features13.subgroupSizeControl = subgroup_size_control_enabled ? VK_TRUE : VK_FALSE; + features13.subgroupSizeControl = + graphics.compute_subgroup_size_control_enabled ? VK_TRUE : VK_FALSE; LOGF("Vulkan robustness: robustImageAccess=%s robustImageAccess2=%s\n", features13.robustImageAccess == VK_TRUE ? "true" : "false", @@ -755,7 +663,6 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V vk::PhysicalDeviceMeshShaderFeaturesEXT mesh_features {}; mesh_features.pNext = &features13; mesh_features.meshShader = graphics.mesh_shader_enabled; - create_info.sType = vk::StructureType::eDeviceCreateInfo; feedback_dynamic.pNext = mesh_extension ? static_cast(&mesh_features) : static_cast(&features13); create_info.pNext = graphics.attachment_feedback_loop_enabled @@ -766,7 +673,6 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V provoking_vertex.transformFeedbackPreservesProvokingVertex = VK_FALSE; create_info.pNext = &provoking_vertex; } - create_info.flags = {}; create_info.pQueueCreateInfos = &queue_create_info; create_info.queueCreateInfoCount = 1; create_info.enabledExtensionCount = static_cast(device_extensions.size()); @@ -796,8 +702,6 @@ static void VulkanGetExtensions(SDL_Window* window, VulkanExtensions& r) { r.required_extensions = std::vector(required_extensions_count); // @suppress("Ambiguous problem") - std::memset(r.required_extensions.data(), 0, - sizeof(const char*) * r.required_extensions.size()); sdl_result = SDL_Vulkan_GetInstanceExtensions(window, &required_extensions_count, r.required_extensions.data()); @@ -989,8 +893,6 @@ void WindowContext::CreateVulkan() { VulkanCheckInstanceVersion(); vk::ApplicationInfo app_info {}; - app_info.sType = vk::StructureType::eApplicationInfo; - app_info.pNext = nullptr; app_info.pApplicationName = "Kyty"; app_info.applicationVersion = 1; app_info.pEngineName = "Kyty"; @@ -1019,17 +921,11 @@ void WindowContext::CreateVulkan() { } vk::ValidationFeaturesEXT validation_features {}; - validation_features.sType = vk::StructureType::eValidationFeaturesEXT; - validation_features.pNext = nullptr; validation_features.enabledValidationFeatureCount = enabled_features_count; validation_features.pEnabledValidationFeatures = enabled_features; - validation_features.disabledValidationFeatureCount = 0; - validation_features.pDisabledValidationFeatures = nullptr; vk::DebugUtilsMessengerCreateInfoEXT dbg_create_info {}; - dbg_create_info.sType = vk::StructureType::eDebugUtilsMessengerCreateInfoEXT; dbg_create_info.pNext = &validation_features; - dbg_create_info.flags = {}; dbg_create_info.messageSeverity = vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose | vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | @@ -1038,12 +934,9 @@ void WindowContext::CreateVulkan() { vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance; dbg_create_info.pfnUserCallback = VulkanDebugMessengerCallback; - dbg_create_info.pUserData = nullptr; vk::InstanceCreateInfo inst_info {}; - inst_info.sType = vk::StructureType::eInstanceCreateInfo; inst_info.pNext = (r.enable_validation_layers ? &dbg_create_info : nullptr); - inst_info.flags = {}; #if defined(__APPLE__) // MoltenVK requires VK_KHR_portability_enumeration + flag to surface // portability devices. Without this, enumeratePhysicalDevices hides the @@ -1110,10 +1003,8 @@ void WindowContext::CreateVulkan() { } #endif - uint32_t queue_family = static_cast(-1); - VulkanFindPhysicalDevice(graphic_ctx.instance, surface, device_extensions, surface_capabilities, - graphic_ctx.physical_device, queue_family); + graphic_ctx.physical_device, graphic_ctx.queue_family); if (graphic_ctx.physical_device == nullptr) { EXIT("Could not find suitable device"); @@ -1121,9 +1012,6 @@ void WindowContext::CreateVulkan() { vk::PhysicalDevicePushDescriptorProperties push_descriptor_properties {}; vk::PhysicalDeviceProperties2 physical_device_properties {}; - push_descriptor_properties.sType = vk::StructureType::ePhysicalDevicePushDescriptorProperties; - push_descriptor_properties.pNext = nullptr; - physical_device_properties.sType = vk::StructureType::ePhysicalDeviceProperties2; physical_device_properties.pNext = &push_descriptor_properties; graphic_ctx.physical_device.getProperties2(&physical_device_properties); graphic_ctx.physical_device_properties = physical_device_properties.properties; @@ -1172,16 +1060,12 @@ void WindowContext::CreateVulkan() { } } - VulkanInitSubgroupSizeControl(graphic_ctx.physical_device, graphic_ctx); - - graphic_ctx.device = VulkanCreateDevice(graphic_ctx.physical_device, r, queue_family, - device_extensions, graphic_ctx); + graphic_ctx.device = VulkanCreateDevice(graphic_ctx, device_extensions); if (graphic_ctx.device == nullptr) { EXIT("Could not create device"); } VULKAN_HPP_DEFAULT_DISPATCHER.init(graphic_ctx.device); - graphic_ctx.queue_family = queue_family; - graphic_ctx.device.getQueue(queue_family, 0, &graphic_ctx.queue); + graphic_ctx.device.getQueue(graphic_ctx.queue_family, 0, &graphic_ctx.queue); EXIT_IF(graphic_ctx.queue == nullptr); if (!graphic_ctx.CreateAllocator()) { @@ -1189,7 +1073,7 @@ void WindowContext::CreateVulkan() { } render_context = std::make_unique(graphic_ctx); - LibKernel::Memory::InstallGpuResources(&render_context->GetGpuResources()); + LibKernel::Memory::InstallGpuResources(render_context.get()); presenter = std::make_unique(*this); } diff --git a/src/graphics/rt/hardware.cpp b/src/graphics/rt/hardware.cpp deleted file mode 100644 index dcb28cb2..00000000 --- a/src/graphics/rt/hardware.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "graphics/rt/hardware.h" - -#include "common/assert.h" -#include "common/logging/log.h" - -#include -#include - -namespace Libs::Graphics { -namespace { - -bool ExtensionAvailable(const std::vector& available, const char* name) { - return std::ranges::any_of(available, [name](const auto& extension) { - return std::strcmp(extension.extensionName, name) == 0; - }); -} - -void AppendExtensionIfMissing(std::vector& extensions, const char* name) { - if (std::ranges::none_of(extensions, [name](const char* extension) { - return std::strcmp(extension, name) == 0; - })) { - extensions.push_back(name); - } -} - -} // namespace - -void GraphicContext::AppendHardwareRayTracingDeviceExtensions( - const std::vector& available_extensions, - std::vector& device_extensions) { - - const char* required[] = { - VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME, - VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME, - VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME, - VK_KHR_RAY_QUERY_EXTENSION_NAME, - }; - for (const auto* extension: required) { - if (!ExtensionAvailable(available_extensions, extension)) { - LOGF("Vulkan RT: extension %s is unavailable; hardware ray tracing disabled\n", - extension); - rt_extensions_enabled = false; - return; - } - } - for (const auto* extension: required) { - AppendExtensionIfMissing(device_extensions, extension); - } - if (ExtensionAvailable(available_extensions, VK_KHR_SPIRV_1_4_EXTENSION_NAME)) { - AppendExtensionIfMissing(device_extensions, VK_KHR_SPIRV_1_4_EXTENSION_NAME); - } - if (ExtensionAvailable(available_extensions, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME)) { - AppendExtensionIfMissing(device_extensions, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME); - } - - rt_extensions_enabled = true; - LOGF("Vulkan RT: enabling hardware ray query extensions\n"); -} - -void GraphicContext::LoadHardwareRayTracingFunctions() const { - EXIT_IF(device == nullptr); - if (!rt_extensions_enabled) { - return; - } - - const auto& dispatcher = VULKAN_HPP_DEFAULT_DISPATCHER; - if (dispatcher.vkGetBufferDeviceAddressKHR == nullptr || - dispatcher.vkCreateAccelerationStructureKHR == nullptr || - dispatcher.vkDestroyAccelerationStructureKHR == nullptr || - dispatcher.vkGetAccelerationStructureBuildSizesKHR == nullptr || - dispatcher.vkCmdBuildAccelerationStructuresKHR == nullptr || - dispatcher.vkGetAccelerationStructureDeviceAddressKHR == nullptr) { - EXIT("Vulkan RT: failed to load required device functions\n"); - } -} - -} // namespace Libs::Graphics diff --git a/src/graphics/rt/hardware.h b/src/graphics/rt/hardware.h deleted file mode 100644 index afe96741..00000000 --- a/src/graphics/rt/hardware.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_RT_HARDWARE_H_ -#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_RT_HARDWARE_H_ - -#include "graphics/host_gpu/graphicContext.h" - -#include - -namespace Libs::Graphics {} // namespace Libs::Graphics - -#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_RT_HARDWARE_H_ */ diff --git a/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp b/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp index 2695046c..b4cc95ea 100644 --- a/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp +++ b/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp @@ -173,12 +173,6 @@ void MarkCleanFlatSlots(const ResourcePlan& program, const DescriptorSource* sou } } -uint64_t ScalarBufferSize(const ShaderBufferResource& descriptor) { - return descriptor.Stride() == 0u - ? descriptor.NumRecords() - : static_cast(descriptor.Stride()) * descriptor.NumRecords(); -} - bool ReadSpecializationWord(const SrtRuntime& runtime, uint64_t address, uint32_t& word) { return runtime.read_specialization_memory != nullptr && runtime.read_specialization_memory(runtime.userdata, address, &word); @@ -188,7 +182,7 @@ bool ReadScalarBufferWord(const ShaderBufferResource& descriptor, uint32_t dynam uint32_t immediate_offset, const SrtRuntime& runtime, uint32_t& word) { const auto byte_offset = static_cast(dynamic_offset) + immediate_offset; const auto aligned = byte_offset & ~uint64_t {3}; - const auto size = ScalarBufferSize(descriptor); + const auto size = descriptor.GetSize(); if (aligned > size || size - aligned < sizeof(uint32_t)) { word = 0; return true; @@ -223,7 +217,7 @@ bool MaterializeIndirectImage(const DescriptorSource::IndirectImage& indirect, const auto period = uint64_t {1} << 32u; const auto step = std::gcd(indirect.selector_stride, period); const auto residue = static_cast(indirect.selector_offset) % step; - const auto size = ScalarBufferSize(material); + const auto size = material.GetSize(); const auto limit = std::min(UINT32_MAX, size + 3u); const auto probe_count = residue <= limit ? (limit - residue) / step + 1u : 0u; if (probe_count > MaxIndirectImageProbes) { diff --git a/src/graphics/shader/shaderBindings.h b/src/graphics/shader/shaderBindings.h index 301340ca..a8d4d73a 100644 --- a/src/graphics/shader/shaderBindings.h +++ b/src/graphics/shader/shaderBindings.h @@ -44,6 +44,10 @@ struct ShaderBufferResource { [[nodiscard]] uint16_t Stride() const { return (fields[1] >> 16u) & 0x3FFFu; } [[nodiscard]] bool SwizzleEnabled() const { return ((fields[1] >> 31u) & 0x1u) == 1; } [[nodiscard]] uint32_t NumRecords() const { return fields[2]; } + [[nodiscard]] uint64_t GetSize() const { + // The 14-bit stride and 32-bit record count require a 64-bit product. + return Stride() == 0 ? NumRecords() : static_cast(Stride()) * NumRecords(); + } [[nodiscard]] uint8_t DstSelX() const { return (fields[3] >> 0u) & 0x7u; } [[nodiscard]] uint8_t DstSelY() const { return (fields[3] >> 3u) & 0x7u; } [[nodiscard]] uint8_t DstSelZ() const { return (fields[3] >> 6u) & 0x7u; } diff --git a/src/kernel/memory.cpp b/src/kernel/memory.cpp index 7897bc36..28ae3b4a 100644 --- a/src/kernel/memory.cpp +++ b/src/kernel/memory.cpp @@ -7,7 +7,7 @@ #include "common/threads.h" #include "common/virtualMemory.h" #include "graphics/guest_gpu/graphicsRun.h" -#include "graphics/host_gpu/renderer/cache/gpuResourceManager.h" +#include "graphics/host_gpu/renderer/renderContext.h" #include "libs/errno.h" #include "libs/libs.h" @@ -69,9 +69,9 @@ constexpr uint64_t DEFAULT_FLEXIBLE_MEMORY_SIZE = 1ull * 1024ull * 1024ull * 102 static uint64_t g_flexible_memory_size = DEFAULT_FLEXIBLE_MEMORY_SIZE; static bool g_flexible_memory_size_frozen = false; -static Graphics::GpuResourceManager* g_gpu_resources = nullptr; +static Graphics::RenderContext* g_gpu_resources = nullptr; -static Graphics::GpuResourceManager& GetGpuResources() { +static Graphics::RenderContext& GetGpuResources() { EXIT_IF(g_gpu_resources == nullptr); return *g_gpu_resources; } @@ -910,7 +910,7 @@ void InvalidateMemory(uint64_t vaddr, uint64_t size) { (void)GetGpuResources().InvalidateMemory(vaddr, size); } -void InstallGpuResources(Graphics::GpuResourceManager* resources) noexcept { +void InstallGpuResources(Graphics::RenderContext* resources) noexcept { EXIT_IF(resources != nullptr && g_gpu_resources != nullptr); g_gpu_resources = resources; } diff --git a/src/kernel/memory.h b/src/kernel/memory.h index 8e1f0d13..d0e5fd9f 100644 --- a/src/kernel/memory.h +++ b/src/kernel/memory.h @@ -6,7 +6,7 @@ #include "common/virtualMemory.h" namespace Libs::Graphics { -class GpuResourceManager; +class RenderContext; enum class PageFaultAccess; } // namespace Libs::Graphics @@ -114,7 +114,7 @@ bool TryReadPrtBacking(uint64_t vaddr, void* data, uint64_t si [[nodiscard]] uint64_t ClampRangeSize(uint64_t vaddr, uint64_t size); void WriteBacking(uint64_t vaddr, const void* data, uint64_t size) noexcept; void InvalidateMemory(uint64_t vaddr, uint64_t size); -void InstallGpuResources(Graphics::GpuResourceManager* resources) noexcept; +void InstallGpuResources(Graphics::RenderContext* renderer) noexcept; [[nodiscard]] bool HandleGpuFault(Graphics::PageFaultAccess access, uint64_t fault_vaddr) noexcept; int KYTY_SYSV_ABI KernelMapNamedFlexibleMemory(void** addr_in_out, size_t len, int prot, int flags, diff --git a/tests/MemoryTrackerTests.cpp b/tests/MemoryTrackerTests.cpp index 49ebe216..47721108 100644 --- a/tests/MemoryTrackerTests.cpp +++ b/tests/MemoryTrackerTests.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS @@ -196,16 +197,22 @@ void TestRangeSet() { !ranges.Intersects(0x1100, 0x100) && !ranges.Intersects(0x1240, 1), "range set intersection did not preserve half-open boundaries"); - auto intersections = ranges.Intersections(0x1070, 0x1b0); - Check(intersections.size() == 2 && intersections[0].address == 0x1070 && - intersections[0].size == 0x90 && - intersections[1].address == 0x1200 && intersections[1].size == 0x20, + std::vector> intersections; + ranges.ForEachInRange(0x1070, 0x1b0, [&](uint64_t start, uint64_t end) { + intersections.emplace_back(start, end); + }); + Check(intersections.size() == 2 && intersections[0].first == 0x1070 && + intersections[0].second == 0x1100 && + intersections[1].first == 0x1200 && intersections[1].second == 0x1220, "range set did not merge and intersect exact byte ranges"); ranges.Subtract(0x1040, 0x1e0); - intersections = ranges.Intersections(0x1000, 0x300); - Check(intersections.size() == 2 && intersections[0].address == 0x1000 && - intersections[0].size == 0x40 && - intersections[1].address == 0x1220 && intersections[1].size == 0x20, + intersections.clear(); + ranges.ForEachInRange(0x1000, 0x300, [&](uint64_t start, uint64_t end) { + intersections.emplace_back(start, end); + }); + Check(intersections.size() == 2 && intersections[0].first == 0x1000 && + intersections[0].second == 0x1040 && + intersections[1].first == 0x1220 && intersections[1].second == 0x1240, "range set subtraction did not preserve both exact tails"); } @@ -436,7 +443,7 @@ void TestExactDirtyIntervalsSharingTrackerPage() { "disjoint byte dirtiness duplicated the page watcher"); exact_dirty.Subtract(address + 64, 16); - if (exact_dirty.Intersections(address, page_size).empty()) { + if (!exact_dirty.Intersects(address, page_size)) { tracker.UnmarkRegionAsGpuModified(address, page_size); } Check(g_protection_calls == 1 && @@ -445,7 +452,7 @@ void TestExactDirtyIntervalsSharingTrackerPage() { "draining one exact interval prematurely released its shared page"); exact_dirty.Subtract(address + 192, 32); - if (exact_dirty.Intersections(address, page_size).empty()) { + if (!exact_dirty.Intersects(address, page_size)) { tracker.UnmarkRegionAsGpuModified(address, page_size); } Check(g_protection_calls == 2 && @@ -471,17 +478,17 @@ void TestGpuDownloadProtectionMirrors() { tracker.MarkRegionAsGpuModified(address + 16, 32); tracker.MarkRegionAsGpuModified(address + page_size * 2 + 16, 32); - std::vector visited; + std::vector> visited; ResetProtectionLog(); tracker.ForEachDownloadRange( address, page_size * 3, [&](uint64_t range_address, uint64_t range_size) noexcept { visited.push_back({range_address, range_size}); }); - Check(visited.size() == 2 && visited[0].address == address && - visited[0].size == page_size && - visited[1].address == address + page_size * 2 && - visited[1].size == page_size && g_protection_calls == 0 && + Check(visited.size() == 2 && visited[0].first == address && + visited[0].second == page_size && + visited[1].first == address + page_size * 2 && + visited[1].second == page_size && g_protection_calls == 0 && tracker.IsRegionGpuModified(address, page_size * 3), "non-clearing download changed protection or lost sparse ranges"); @@ -491,8 +498,8 @@ void TestGpuDownloadProtectionMirrors() { [&](uint64_t range_address, uint64_t range_size) noexcept { visited.push_back({range_address, range_size}); }); - Check(visited.size() == 1 && visited[0].address == address && - visited[0].size == page_size && g_protection_log.size() == 1 && + Check(visited.size() == 1 && visited[0].first == address && + visited[0].second == page_size && g_protection_log.size() == 1 && g_protection_log[0].address == address && g_protection_log[0].size == page_size && g_protection_log[0].mode == Common::VirtualMemory::Mode::Read && diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 4db2a195..c2189034 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -17,7 +17,6 @@ #include "graphics/host_gpu/memoryTracker.h" #include "graphics/host_gpu/pageManager.h" #include "graphics/host_gpu/renderer/cache/bufferCache.h" -#include "graphics/host_gpu/renderer/cache/gpuResourceManager.h" #include "graphics/host_gpu/renderer/cache/textureCache.h" #include "graphics/host_gpu/renderer/colorRenderTarget.h" #include "graphics/host_gpu/renderer/depthRenderTarget.h" @@ -2111,16 +2110,14 @@ public: void CheckGpuMappedRangeLifecycle() { EnsureRuntimeContext(); - auto &context = Renderer(); - CommandScheduler scheduler(context, m_runtime_context); + RenderContext context(m_runtime_context); + auto &scheduler = context.GetCommandScheduler(); HW::Context registers{}; HW::UserConfig user_config{}; HW::Shader shaders{}; scheduler.Begin(registers, user_config, shaders); context.InitializeGpu(nullptr); - auto &gpu = context.GetGpu(); - GpuResourceManager resources(m_runtime_context, scheduler); - resources.SetGpu(&gpu); + auto &resources = context; constexpr uint64_t base = 0x0000000200000000ull; constexpr uint64_t page = 0x4000; @@ -2159,7 +2156,6 @@ public: resources.IsMapped(new_prt, page * 6), "old-unmap/new-map did not replace full PRT coverage"); - resources.SetGpu(nullptr); scheduler.Finish(); context.ShutdownGpu(); std::printf("[host] %-32s ok\n", "GpuMappedRangeLifecycle"); @@ -2167,12 +2163,13 @@ public: void CheckStreamBufferRing() { EnsureRuntimeContext(); - CommandScheduler scheduler(Renderer(), m_runtime_context); + RenderContext context(m_runtime_context); + auto &scheduler = context.GetCommandScheduler(); HW::Context registers{}; HW::UserConfig user_config{}; HW::Shader shaders{}; scheduler.Begin(registers, user_config, shaders); - GpuResourceManager resources(m_runtime_context, scheduler); + auto &resources = context; auto &cache = resources.GetBufferCache(); constexpr std::array, 4> utilities{{ {MemoryUsage::Upload, 512ull << 20}, @@ -2800,7 +2797,7 @@ public: ordered_finished.load(), "submit done did not drain prior PM4 work"); - auto &resources = context.GetGpuResources(); + auto &resources = context; constexpr uint64_t empty_unmap_base = 0x0000000200400000ull; constexpr uint64_t empty_unmap_size = 0x4000; resources.MapMemory(empty_unmap_base, empty_unmap_size); @@ -3187,15 +3184,18 @@ public: std::binary_semaphore release_command{0}; std::atomic shutdown_complete{false}; std::atomic submission_lane_entered{false}; + bool shutdown_owner_visible = false; + resources.MapMemory(empty_unmap_base, empty_unmap_size); gpu.SendCommand([&] { command_entered.release(); release_command.acquire(); + shutdown_owner_visible = &context.GetGpu() == &gpu; gpu.Done(); submission_lane_entered = true; }); command_entered.acquire(); std::jthread shutdown_thread([&] { - gpu.Shutdown(); + context.ShutdownGpu(); shutdown_complete = true; }); while (!gpu.IsStopping()) { @@ -3207,8 +3207,13 @@ public: shutdown_thread.join(); Require( "GpuCommandLane", "owned shutdown completion", - shutdown_complete.load() && submission_lane_entered.load(), - "GPU owner did not drain a command that entered the submission lane"); + shutdown_complete.load() && submission_lane_entered.load() && + shutdown_owner_visible, + "GPU owner did not remain available while draining queued work"); + resources.UnmapMemory(empty_unmap_base, empty_unmap_size); + Require("GpuCommandLane", "unmap after shutdown", + !resources.IsMapped(empty_unmap_base, empty_unmap_size), + "renderer retained mapped coverage after the GPU command lane stopped"); context.ShutdownGpu(); std::printf("[host] %-32s ok\n", "GpuCommandLane"); } @@ -3459,14 +3464,13 @@ public: constexpr uint32_t ring_fault_second_value = 0x4e5f6071u; EnsureRuntimeContext(); - auto &context = Renderer(); - CommandScheduler scheduler(context, m_runtime_context); + RenderContext context(m_runtime_context); + auto &scheduler = context.GetCommandScheduler(); HW::Context registers{}; HW::UserConfig user_config{}; HW::Shader shaders{}; scheduler.Begin(registers, user_config, shaders); context.InitializeGpu(nullptr); - auto &gpu = context.GetGpu(); int64_t direct_offset = -1; Require(name, "direct allocation", @@ -3487,8 +3491,7 @@ public: std::memcpy(memory + clean_offset, &clean_value, sizeof(clean_value)); { - GpuResourceManager resources(m_runtime_context, scheduler); - resources.SetGpu(&gpu); + auto &resources = context; auto &cache = resources.GetBufferCache(); resources.MapMemory(base, allocation_size); @@ -4284,7 +4287,6 @@ public: cache.ReadMemory(base + reacquire_disjoint_offset, sizeof(reacquire_value)); - resources.SetGpu(nullptr); resources.UnmapMemory(base, allocation_size); scheduler.Finish(); } @@ -4393,8 +4395,8 @@ public: constexpr uint64_t allocation_size = 0x2800000; constexpr uint64_t allocation_alignment = 0x200000; EnsureRuntimeContext(); - auto &context = Renderer(); - CommandScheduler scheduler(context, m_runtime_context); + RenderContext context(m_runtime_context); + auto &scheduler = context.GetCommandScheduler(); HW::Context registers{}; HW::UserConfig user_config{}; HW::Shader shaders{}; @@ -4419,8 +4421,7 @@ public: std::memcpy(memory, &initial, sizeof(initial)); { - GpuResourceManager resources(m_runtime_context, scheduler); - resources.SetGpu(&gpu); + auto &resources = context; namespace Exception = Common::HostException; Require(name, "guest fault handler", Exception::InstallHandler([](const Exception::ExceptionInfo &info) { @@ -7756,7 +7757,6 @@ public: !boundary_buffer_cache.HasGpuDirtyBytes(final_word_address, sizeof(uint32_t)), "a final-byte CPU read did not publish the GPU-owned final word"); - resources.SetGpu(nullptr); resources.UnmapMemory(base, allocation_size); scheduler.Finish(); LibKernel::Memory::InstallGpuResources(nullptr); @@ -7794,13 +7794,14 @@ public: mapped == reinterpret_cast(base), "BGRA16 fixed mapping failed"); - CommandScheduler scheduler(Renderer(), m_runtime_context); + RenderContext context(m_runtime_context); + auto &scheduler = context.GetCommandScheduler(); HW::Context registers{}; HW::UserConfig user_config{}; HW::Shader shaders{}; scheduler.Begin(registers, user_config, shaders); { - GpuResourceManager resources(m_runtime_context, scheduler); + auto &resources = context; resources.MapMemory(base, allocation_size); const uint32_t pitch = TileGetTexturePitch(format, 1, tile); TileSizeAlign total{}; @@ -7973,7 +7974,7 @@ public: registers.SetRenderTargetMask(0x0f); scheduler.Begin(registers, user_config, shaders); - auto &resources = context.GetGpuResources(); + auto &resources = context; auto &texture_cache = resources.GetTextureCache(); auto &executor = context.GetRenderExecutor(); resources.MapMemory(base, allocation_size); @@ -8114,7 +8115,7 @@ public: registers.SetRenderTargetMask(0x0f); scheduler.Begin(registers, user_config, shaders); - auto &resources = context.GetGpuResources(); + auto &resources = context; auto &texture_cache = resources.GetTextureCache(); auto &executor = context.GetRenderExecutor(); resources.MapMemory(base, allocation_size); @@ -8369,7 +8370,7 @@ public: registers.SetRenderTargetMask(0x0f); scheduler.Begin(registers, user_config, shaders); - auto &resources = context.GetGpuResources(); + auto &resources = context; auto &texture_cache = resources.GetTextureCache(); auto &executor = context.GetRenderExecutor(); resources.MapMemory(base, allocation_size); @@ -8442,7 +8443,7 @@ public: HW::UserConfig user_config{}; HW::Shader shaders{}; scheduler.Begin(registers, user_config, shaders); - auto &resources = context.GetGpuResources(); + auto &resources = context; auto &cache = resources.GetTextureCache(); auto &executor = context.GetRenderExecutor(); resources.MapMemory(base, allocation_size); @@ -8564,7 +8565,7 @@ public: HW::UserConfig user_config{}; HW::Shader shaders{}; scheduler.Begin(registers, user_config, shaders); - auto &resources = context.GetGpuResources(); + auto &resources = context; auto &cache = resources.GetTextureCache(); auto &executor = context.GetRenderExecutor(); resources.MapMemory(base, allocation_size); @@ -8680,7 +8681,7 @@ public: HW::UserConfig user_config{}; HW::Shader shaders{}; scheduler.Begin(registers, user_config, shaders); - auto &resources = context.GetGpuResources(); + auto &resources = context; auto &cache = resources.GetTextureCache(); auto &executor = context.GetRenderExecutor(); resources.MapMemory(base, allocation_size); @@ -8898,7 +8899,7 @@ public: registers.SetRenderTargetMask(0x0f); scheduler.Begin(registers, user_config, shaders); - auto &resources = context.GetGpuResources(); + auto &resources = context; auto &texture_cache = resources.GetTextureCache(); auto &executor = context.GetRenderExecutor(); resources.MapMemory(base, allocation_size); @@ -9008,7 +9009,7 @@ public: registers.SetRenderTargetMask(0x0f); scheduler.Begin(registers, user_config, shaders); - auto &resources = context.GetGpuResources(); + auto &resources = context; auto &texture_cache = resources.GetTextureCache(); auto &executor = context.GetRenderExecutor(); resources.MapMemory(base, allocation_size); @@ -9105,7 +9106,7 @@ public: HW::UserConfig user_config{}; HW::Shader shaders{}; scheduler.Begin(registers, user_config, shaders); - auto &resources = context.GetGpuResources(); + auto &resources = context; auto &texture_cache = resources.GetTextureCache(); auto &executor = context.GetRenderExecutor(); resources.MapMemory(base, allocation_size); @@ -11959,7 +11960,7 @@ public: HW::Shader shaders{}; registers.SetRenderTargetMask(0xf); scheduler.Begin(registers, user_config, shaders); - auto &resources = context.GetGpuResources(); + auto &resources = context; auto &cache = context.GetTextureCache(); auto &executor = context.GetRenderExecutor(); int64_t direct_offset = -1; @@ -12108,7 +12109,7 @@ public: mode.polymode_back_ptype = back; mode.provoking_vtx_last = provoking_last; registers.SetModeControl(mode); - return context.GetPipelineCache().CreateGraphicsPipeline( + return context.GetPipelineCache().GetGraphicsPipeline( std::span{&color, 1u}, depth, vertex, scheduler.Current(), &pixel, vk::PrimitiveTopology::eTriangleList, false, vertex_shader, pixel_shader); }; @@ -12218,6 +12219,36 @@ public: last_pixels[i] == (solid_pixels[i] == 0 ? 0 : 0x3e800000u), "last-vertex flat shading changed coverage or did not use vertex two"); } + + const auto blend_pipeline = [&](bool enabled, bool bypass) -> PipelineCache::Pipeline & { + auto blend = registers.GetBlendControl(0); + blend.enable = enabled; + blend.color_srcblend = static_cast(Prospero::BlendFactor::kZero); + blend.alpha_srcblend = static_cast(Prospero::BlendFactor::kZero); + registers.SetBlendControl(0, blend); + auto target = registers.GetRenderTarget(0).info; + target.blend_bypass = bypass; + registers.SetColorInfo(0, target); + return pipeline(true, 2, 2); + }; + auto &disabled_blend = blend_pipeline(false, false); + Require(name, "effective blend pipeline cache", + blend_pipeline(false, true).pipeline == disabled_blend.pipeline && + blend_pipeline(true, true).pipeline == disabled_blend.pipeline, + "disabled and bypassed blending did not share the same host pipeline"); + auto &enabled_blend = blend_pipeline(true, false); + Require(name, "enabled blend pipeline cache", + enabled_blend.pipeline != disabled_blend.pipeline && + blend_pipeline(true, false).pipeline == enabled_blend.pipeline, + "active blending was lost from the pipeline key or missed its cached pipeline"); + draw(enabled_blend); + const auto blended_pixels = read_color(); + Require(name, "active blend output", + std::ranges::all_of(blended_pixels, [](u32 value) { return value == 0; }), + "zero-factor blending did not clear the fragment color"); + draw(blend_pipeline(true, true)); + Require(name, "bypassed blend output", read_color() == solid_pixels, + "blend bypass did not restore the unblended fragment color and coverage"); } resources.UnmapMemory(depth_address, allocation_size); scheduler.Finish(); @@ -25307,7 +25338,7 @@ void CheckEmbeddedFetchVertexOffset() { program.info.vertex_offset_sgpr = result.program.info.vertex_offset_sgpr; vertex.stage.program = &program; vertex.stage.resources = result.resources; - return ResolveVertexOffset(index_offset, vertex); + return ResolveDrawOffsets(index_offset, vertex).first; }; const auto ResolveInstance = [](const CompiledShader &result) { @@ -25319,7 +25350,7 @@ void CheckEmbeddedFetchVertexOffset() { result.program.info.instance_offset_sgpr; vertex.stage.program = &program; vertex.stage.resources = result.resources; - return ResolveInstanceOffset(vertex); + return ResolveDrawOffsets(0, vertex).second; }; const auto valid = @@ -25848,64 +25879,6 @@ void CheckSampledDepthResource() { std::printf("[host] %-32s ok\n", "SampledDepthResource"); } -void CheckSampledVideoOutView(RenderContext &renderer) { - auto &context = renderer.GetGraphics(); - CommandScheduler scheduler(renderer, context); - ShaderRecompiler::IR::ImageResource resource{}; - resource.resource_class = ShaderRecompiler::IR::ImageResourceClass::Sampled; - resource.numeric_class = Prospero::TextureNumericClass::Float; - resource.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D; - resource.read = true; - - ShaderTextureResource descriptor{}; - descriptor.fields[3] = static_cast(Prospero::ImageType::kColor2D) - << 28u; - ImageInfo info{}; - info.pixel_format = vk::Format::eR8G8B8A8Unorm; - info.guest_format = Prospero::BufferFormat::k8_8_8_8UNorm; - info.type = Prospero::ImageType::kColor2D; - info.extent = {1, 1, 1}; - info.resources = {1, 1}; - info.pitch = 1; - info.bytes_per_block = 4; - info.samples = 1; - info.tile_mode = Prospero::TileMode::kLinear; - info.mip_layout[0] = {0, 4, 1, 1}; - Image image(context, scheduler, info); - image.usage.video_out = true; - Require("SampledVideoOutView", "basic 2D", - IsSupportedSampledVideoOutView(resource, descriptor, image), - "basic 2D video-out view was rejected"); - - const auto basic_resource = resource; - resource.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2DArray; - const bool rejects_array_resource = - !IsSupportedSampledVideoOutView(resource, descriptor, image); - resource = basic_resource; - descriptor.fields[3] = - static_cast(Prospero::ImageType::kColor2DArray) << 28u; - const bool rejects_array_descriptor = - !IsSupportedSampledVideoOutView(resource, descriptor, image); - descriptor.fields[3] = static_cast(Prospero::ImageType::kColor2D) - << 28u; - descriptor.fields[4] = 1u << 16u; - const bool rejects_base_layer = - !IsSupportedSampledVideoOutView(resource, descriptor, image); - descriptor.fields[4] = 1u; - const bool rejects_layer_count = - !IsSupportedSampledVideoOutView(resource, descriptor, image); - descriptor.fields[4] = 0; - image.info.resources.layers = 2; - const bool rejects_layered_image = - !IsSupportedSampledVideoOutView(resource, descriptor, image); - Require("SampledVideoOutView", "array hard failures", - rejects_array_resource && rejects_array_descriptor && - rejects_base_layer && rejects_layer_count && - rejects_layered_image, - "unsupported layered video-out view was accepted"); - std::printf("[host] %-32s ok\n", "SampledVideoOutView"); -} - void CheckImageTransitionState(RenderContext &renderer) { constexpr const char *name = "ImageTransitionState"; auto &context = renderer.GetGraphics(); @@ -27382,57 +27355,6 @@ void CheckStandard64RenderTargetTileRoundTrip() { pitch == 384 && storage.size == 0x60000 && storage.align == 0x10000, "partial Standard64KB footprint was not padded in 128x128 blocks"); - ImageInfo info{}; - info.data = {0x10000, storage.size}; - info.pixel_format = vk::Format::eR8G8B8A8Unorm; - info.guest_format = format; - info.type = Prospero::ImageType::kColor2D; - info.extent = {width, height, 1}; - info.resources = {1, 1}; - info.pitch = pitch; - info.bytes_per_block = 4; - info.samples = 1; - info.tile_mode = tile; - info.mip_layout[0] = {0, storage.size, pitch, height}; - Require("Standard64RenderTarget", "support boundary", - IsSupportedStandard64RenderTarget(info) && IsTiledRenderTarget(info), - "exact Standard64KB render target was not classified as tiled"); - Require("Standard64RenderTarget", "display tile boundary", - IsSupportedDisplayRenderTargetTileMode( - Prospero::TileMode::kRenderTarget) && - !IsSupportedDisplayRenderTargetTileMode(tile), - "Standard64KB render target could alias a mode-27 display image"); - auto unsupported = info; - unsupported.data.address += 4; - Require("Standard64RenderTarget", "address guard", - !IsSupportedStandard64RenderTarget(unsupported), - "unaligned Standard64KB backing was accepted"); - unsupported = info; - unsupported.bytes_per_block = 8; - Require("Standard64RenderTarget", "element guard", - !IsSupportedStandard64RenderTarget(unsupported), - "unimplemented Standard64KB element size was accepted"); - unsupported = info; - unsupported.pitch += 128; - Require("Standard64RenderTarget", "pitch guard", - !IsSupportedStandard64RenderTarget(unsupported), - "non-minimal Standard64KB pitch was accepted"); - unsupported = info; - unsupported.data.size += 0x10000; - Require("Standard64RenderTarget", "size guard", - !IsSupportedStandard64RenderTarget(unsupported), - "non-exact Standard64KB allocation was accepted"); - unsupported = info; - unsupported.resources.levels = 2; - Require("Standard64RenderTarget", "mip guard", - !IsSupportedStandard64RenderTarget(unsupported), - "unimplemented Standard64KB mip chain was accepted"); - unsupported = info; - unsupported.resources.layers = 2; - Require("Standard64RenderTarget", "layer guard", - !IsSupportedStandard64RenderTarget(unsupported), - "unimplemented Standard64KB array was accepted"); - std::printf("[host] %-32s ok\n", "Standard64RenderTarget"); } @@ -27590,30 +27512,6 @@ void CheckPs5DepthRegisterDecoding() { std::printf("[host] %-32s ok\n", "Ps5DepthRegisterDecoding"); } -void CheckStencilAttachmentAccess() { - PipelineStencilStaticState state{vk::StencilOp::eKeep, vk::StencilOp::eKeep, - vk::StencilOp::eKeep, - vk::CompareOp::eAlways}; - PipelineStencilDynamicState dynamic{0xff, 0xff, 0}; - Require("StencilAttachmentAccess", "always keep", - !stencil_face_accesses_attachment(state, dynamic), - "ALWAYS/KEEP state was classified as stencil access"); - state.compareOp = vk::CompareOp::eEqual; - Require("StencilAttachmentAccess", "compare reads", - stencil_face_accesses_attachment(state, dynamic), - "real stencil comparison was classified as no access"); - state.compareOp = vk::CompareOp::eAlways; - state.passOp = vk::StencilOp::eZero; - Require("StencilAttachmentAccess", "write operation", - stencil_face_accesses_attachment(state, dynamic), - "write-capable stencil operation was classified as no access"); - dynamic.writeMask = 0; - Require("StencilAttachmentAccess", "masked write", - !stencil_face_accesses_attachment(state, dynamic), - "fully masked stencil write was classified as access"); - std::printf("[host] %-32s ok\n", "StencilAttachmentAccess"); -} - void CheckDepthAttachmentWrites() { RenderDepthInfo target{}; target.desc.view_info.format = vk::Format::eD32SfloatS8Uint; @@ -29419,6 +29317,10 @@ int main(int argc, char **argv) { vulkan.CheckBgra16Readback(); return 0; } + if (argc == 2 && std::strcmp(argv[1], "--draw-offset-only") == 0) { + CheckEmbeddedFetchVertexOffset(); + return 0; + } if (argc == 2 && std::strcmp(argv[1], "--polygon-mode-only") == 0) { VulkanHarness vulkan; vulkan.CheckRasterization(false); @@ -29498,7 +29400,6 @@ int main(int argc, char **argv) { if (argc == 2 && std::strcmp(argv[1], "--image-view-only") == 0) { VulkanHarness vulkan; CheckSampledColorViews(); - CheckSampledVideoOutView(vulkan.RuntimeRenderer()); return 0; } if (argc == 2 && std::strcmp(argv[1], "--image-transition-only") == 0) { @@ -29550,7 +29451,6 @@ int main(int argc, char **argv) { VulkanHarness vulkan; CheckRenderTargetFormatContract(); CheckSampledColorViews(); - CheckSampledVideoOutView(vulkan.RuntimeRenderer()); CheckImageTransitionState(vulkan.RuntimeRenderer()); CheckSampledDepthResource(); CheckDepthTextureEncoding(); @@ -29566,7 +29466,6 @@ int main(int argc, char **argv) { CheckNativeMsaaState(); CheckPs5DepthRegisterDecoding(); CheckDepthHtileStencilCompatibility(); - CheckStencilAttachmentAccess(); CheckDepthAttachmentWrites(); CheckDynamicRenderingState(); CheckDepthTargetFootprints(); diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index e3205a23..62d13031 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -1093,6 +1093,22 @@ constexpr uint32_t EncodeSopp(uint32_t opcode, uint32_t simm = 0) { (simm & 0xffffu); } +void TestShaderBufferResourceSize() { + ShaderBufferResource descriptor; + descriptor.fields[2] = UINT32_MAX; + Check(descriptor.GetSize() == UINT32_MAX, + "zero-stride buffer records must count bytes"); + descriptor.fields[1] = 0x3FFFu << 16u; + Check(descriptor.GetSize() == uint64_t{0x3FFF} * UINT32_MAX, + "strided buffer byte size must preserve the full 64-bit product"); + descriptor.UpdateAddress48(0x123456789ABCull); + Check(descriptor.GetSize() == uint64_t{0x3FFF} * UINT32_MAX, + "buffer address must not affect the descriptor byte size"); + descriptor.fields[2] = 0; + Check(descriptor.GetSize() == 0, + "zero-record buffer must be empty even with a nonzero stride"); +} + void TestNativeShaderResourceDependencies() { const auto stages = ShaderPipelineStages(vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment | @@ -12738,6 +12754,7 @@ int main() { EnsureConfigInitialized(); TestResourceDescriptorClassification(); + TestShaderBufferResourceSize(); TestNativeShaderResourceDependencies(); TestNormalizedImageContracts(); TestSpirvRequirementsAnalysis();