diff --git a/.github/workflows/fork-windows-check.yml b/.github/workflows/fork-windows-check.yml index df03d8dde3..97675f7590 100644 --- a/.github/workflows/fork-windows-check.yml +++ b/.github/workflows/fork-windows-check.yml @@ -8,6 +8,8 @@ on: - .github/workflows/fork-windows-check.yml - .github/workflows/build-windows.yml - src/CMakeLists.txt + - src/video_core/** + - src/shader_recompiler/** - src/yuzu/** - src/yuzu_cmd/** - src/qt_common/** diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 97126571ab..2583edb22e 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1257,6 +1257,15 @@ StagingBufferRef TextureCacheRuntime::UploadStagingBuffer(size_t size, bool defe return staging_buffer_pool.Request(size, MemoryUsage::Upload, deferred); } +void TextureCacheRuntime::DeferGuestRead(u8* dst, Tegra::MaxwellDeviceMemoryManager& memory, + DAddr address, size_t size) { + // Runs when the worker records this chunk, before its commands are submitted: the copies + // recorded after it read the staging memory only once the GPU executes them. + scheduler.Record([dst, memory = &memory, address, size](vk::CommandBuffer) { + memory->ReadBlockUnsafe(address, dst, size); + }); +} + StagingBufferRef TextureCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) { return staging_buffer_pool.Request(size, MemoryUsage::Download, deferred); } diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 8ac0a1bfa3..ea99c862e9 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -63,6 +63,12 @@ class TextureCacheRuntime { StagingBufferRef UploadStagingBuffer(size_t size, bool deferred = false); + /// Reads `size` bytes of guest memory at device address `address` into `dst` (mapped staging + /// memory) on the worker thread, in order with the commands recorded around it: the GPU + /// thread hands the copy off instead of making it. + void DeferGuestRead(u8* dst, Tegra::MaxwellDeviceMemoryManager& memory, DAddr address, + size_t size); + StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false); void FreeDeferredStagingBuffer(StagingBufferRef& ref); diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 1d4b4c82d0..ce089a2e42 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -2001,12 +2001,19 @@ void TextureCache
::UploadLayers(Image& image, std::span ::UploadImageContents(Image& image, StagingBuffer& staging)
return;
}
+ if (image.info.type == ImageType::Linear) {
+ // A linear image is read straight from guest memory into its destination: copying it
+ // into swizzle_data first, as below, read it twice (3% of the split's back thread with
+ // Pokopia's town in view).
+ if (True(image.flags & ImageFlagBits::Converted)) {
+ unswizzle_data_buffer.resize_destructive(image.unswizzled_size_bytes);
+ auto copies = UnswizzleLinearImage(*gpu_memory, gpu_addr, image.info,
+ image.guest_size_bytes, unswizzle_data_buffer);
+ ConvertImage(unswizzle_data_buffer, image.info, mapped_span, copies);
+ image.UploadMemory(staging, copies);
+ } else {
+ // Pokopia rewrites three 4 MiB linear textures in turn and each is uploaded again
+ // every 3 frames: the worker thread copies guest memory into the staging buffer,
+ // right before the image copy it records, instead of this thread (~2.5% of the
+ // split's back thread). EDEN_NX2_DEFER_UPLOADS=0 copies here.
+ if constexpr (requires(u8* p) {
+ runtime.DeferGuestRead(p, device_memory, DAddr{}, size_t{});
+ }) {
+ static const bool defer_uploads = [] {
+ const char* const value = std::getenv("EDEN_NX2_DEFER_UPLOADS");
+ return value == nullptr || value[0] != '0';
+ }();
+ constexpr size_t DeferMinBytes = 64_KiB;
+ if (defer_uploads && image.guest_size_bytes >= DeferMinBytes &&
+ False(image.flags & ImageFlagBits::Sparse) &&
+ mapped_span.size_bytes() >= image.guest_size_bytes) {
+ runtime.DeferGuestRead(mapped_span.data(), device_memory, image.cpu_addr,
+ image.guest_size_bytes);
+ const auto copies = LinearImageCopies(image.info, image.guest_size_bytes);
+ image.UploadMemory(staging, copies);
+ return;
+ }
+ }
+ const auto copies = UnswizzleLinearImage(*gpu_memory, gpu_addr, image.info,
+ image.guest_size_bytes, mapped_span);
+ image.UploadMemory(staging, copies);
+ }
+ return;
+ }
Tegra::Memory::GpuGuestMemory