diff --git a/src/graphics/host_gpu/renderer/cache/textureCache.cpp b/src/graphics/host_gpu/renderer/cache/textureCache.cpp index ca121548f..45a37dd3f 100644 --- a/src/graphics/host_gpu/renderer/cache/textureCache.cpp +++ b/src/graphics/host_gpu/renderer/cache/textureCache.cpp @@ -460,7 +460,7 @@ void TextureCache::PrepareImageCopy(Image& image) { void TextureCache::RefreshCopySource(ImageId id) { auto& image = m_slot_images[id]; - RefreshImage(id, ImageDesc {.info = image.info, .view_info = {}, .type = UploadBinding(image)}); + RefreshImage(id); if (image.IsDefinitelyCpuDirty()) { EXIT("TextureCache: image copy source remained CPU-dirty after refresh\n"); } @@ -648,8 +648,7 @@ ImageId TextureCache::ResolveDepthOverlap(const ImageInfo& requested, BindingTyp if (!recreate) { return cached_id; } - RefreshImage(cached_id, - ImageDesc {.info = cached.info, .view_info = {}, .type = UploadBinding(cached)}); + RefreshImage(cached_id); auto info = requested; if (retain_cached_layout) { info.data = cached.info.data; @@ -810,8 +809,7 @@ ImageId TextureCache::ExpandImage(const ImageInfo& info, ImageId source_id) { if (source.binding.is_bound || source.binding.is_target) { source.binding.needs_rebind = true; } - InitializeImage(expanded_id, - ImageDesc {.info = info, .view_info = {}, .type = UploadBinding(source)}); + InitializeImage(expanded_id); CopyImage(expanded_id, source_id); FreeImage(source_id); return expanded_id; @@ -929,23 +927,23 @@ TextureCache::DownloadPlan TextureCache::BuildDownload(const Image& image) const return plan; } -void TextureCache::UploadImage(Image& image, const ImageDesc& desc, Buffer& source, - uint64_t source_offset) { - const auto& info = image.info; - const auto upload = [&](std::vector& copies, TileManager::Result linear) { +void TextureCache::UploadImage(Image& image, Buffer& source, uint64_t source_offset) { + const auto& info = image.info; + const auto binding = UploadBinding(image); + const auto upload = [&](std::vector& copies, TileManager::Result linear) { for (auto& copy: copies) { copy.bufferOffset += linear.offset; } image.Upload(copies, linear.buffer, linear.offset, linear.size); }; - if (desc.type != BindingType::DepthTarget) { - auto plan = BuildColorTransfer(image, desc.type, TransferDirection::Upload); + if (binding != BindingType::DepthTarget) { + auto plan = BuildColorTransfer(image, binding, TransferDirection::Upload); if (!plan.valid) { EXIT("TextureCache: invalid color upload: binding=%u addr=0x%016" PRIx64 " size=0x%016" PRIx64 " format=%u tile=%u family=%u extent=%ux%ux%u " "pitch=%u levels=%u layers=%u samples=%u\n", - static_cast(desc.type), info.data.address, info.data.size, + static_cast(binding), info.data.address, info.data.size, static_cast(info.guest_format), static_cast(info.tile_mode), static_cast(plan.layout.surface.texture.block.family), info.extent.width, info.extent.height, info.extent.depth, info.pitch, info.resources.levels, @@ -963,7 +961,7 @@ void TextureCache::UploadImage(Image& image, const ImageDesc& desc, Buffer& sour return; } - if (desc.type != BindingType::DepthTarget || info.samples != 1 || image.backing.samples != 1 || + if (info.samples != 1 || image.backing.samples != 1 || info.resources.layers == 0 || info.data.size % info.resources.layers != 0 || Prospero::NumBytesPerElement(info.guest_format) != info.bytes_per_block) { EXIT("TextureCache: invalid depth upload\n"); @@ -1023,7 +1021,7 @@ void TextureCache::UploadImage(Image& image, const ImageDesc& desc, Buffer& sour upload(copies, linear); } -void TextureCache::InitializeImage(ImageId id, const ImageDesc& desc) { +void TextureCache::InitializeImage(ImageId id) { auto& image = m_slot_images[id]; if (image.info.data.Empty()) { return; @@ -1047,7 +1045,7 @@ void TextureCache::InitializeImage(ImageId id, const ImageDesc& desc) { EXIT("TextureCache: failed to obtain image upload source\n"); } data_imported = true; - UploadImage(image, desc, *source, source_offset); + UploadImage(image, *source, source_offset); } if (data_imported) { image.ClearBufferModified(); @@ -1057,7 +1055,7 @@ void TextureCache::InitializeImage(ImageId id, const ImageDesc& desc) { } } -void TextureCache::RefreshImage(ImageId id, const ImageDesc& desc) { +void TextureCache::RefreshImage(ImageId id) { TrackImage(id); auto& image = m_slot_images[id]; if (image.IsMaybeCpuDirty()) { @@ -1078,7 +1076,7 @@ void TextureCache::RefreshImage(ImageId id, const ImageDesc& desc) { if (!cpu_dirty) { return; } - InitializeImage(id, desc); + InitializeImage(id); } void TextureCache::AssociateStencil(ImageId depth_id, GuestRange stencil) { @@ -1192,7 +1190,7 @@ void TextureCache::UpdateImage(ImageId id) { std::scoped_lock lock {m_lock}; auto& image = m_slot_images[id]; TouchImage(image); - RefreshImage(id, ImageDesc {.info = image.info, .type = UploadBinding(image)}); + RefreshImage(id); } ImageId TextureCache::FindImageFromRange(uint64_t address, uint64_t size, bool ensure_valid) { @@ -1248,7 +1246,7 @@ vk::ImageView TextureCache::FindTexture(ImageId id, const ImageDesc& desc) { image.MarkGpuModified(); } if (!image.info.data.Empty()) { - RefreshImage(id, desc); + RefreshImage(id); } switch (desc.type) { case BindingType::Texture: break; @@ -1280,7 +1278,7 @@ vk::ImageView TextureCache::FindRenderTarget(ImageId id, const ImageDesc& desc) TouchImage(image); image.MarkGpuModified(); image.usage.render_target = true; - RefreshImage(id, desc); + RefreshImage(id); // DCC uses a separate metadata allocation. Register it when the color target is bound, // matching the point where CMask/FMask will be registered. Preserve a PendingDcc entry // because the metadata fill may have run before this bind. @@ -1313,7 +1311,7 @@ vk::ImageView TextureCache::FindDepthTarget(ImageId id, const ImageDesc& desc) { TouchImage(image); image.MarkGpuModified(); image.usage.depth_target = true; - RefreshImage(id, desc); + RefreshImage(id); if (desc.info.HasMetadata()) { image.info.metadata = desc.info.metadata; auto [metadata, inserted] = @@ -1418,8 +1416,7 @@ bool TextureCache::ClearImageFromBuffer(CommandBuffer& command, uint64_t address } } if (image.IsBufferModified() || image.IsCpuDirty()) { - ImageDesc refresh {.info = image.info, .view_info = {}, .type = UploadBinding(image)}; - InitializeImage(selected, refresh); + InitializeImage(selected); if (image.info.samples == 1 && (image.IsBufferModified() || image.IsCpuDirty())) { EXIT("TextureCache: image clear retained guest ownership\n"); } diff --git a/src/graphics/host_gpu/renderer/cache/textureCache.h b/src/graphics/host_gpu/renderer/cache/textureCache.h index ab0abde77..11a3db4e8 100644 --- a/src/graphics/host_gpu/renderer/cache/textureCache.h +++ b/src/graphics/host_gpu/renderer/cache/textureCache.h @@ -137,12 +137,12 @@ private: [[nodiscard]] ImageId ResolveDepthOverlap(const ImageInfo& requested, BindingType binding, ImageId cached); [[nodiscard]] ImageId ExpandImage(const ImageInfo& info, ImageId source); - void RefreshImage(ImageId id, const ImageDesc& desc); - void InitializeImage(ImageId id, const ImageDesc& desc); + void RefreshImage(ImageId id); + void InitializeImage(ImageId id); [[nodiscard]] ColorTransferPlan BuildColorTransfer(const Image& image, BindingType binding, TransferDirection direction) const; [[nodiscard]] DownloadPlan BuildDownload(const Image& image) const; - void UploadImage(Image& image, const ImageDesc& desc, Buffer& source, uint64_t source_offset); + void UploadImage(Image& image, Buffer& source, uint64_t source_offset); void DownloadImageData(Image& image, Buffer& destination, uint64_t destination_offset, uint64_t destination_size, DownloadPlan plan); void DownloadDepth(Image& image, Buffer& destination, uint64_t destination_offset); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 30f8a897e..8aacf7296 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -4566,11 +4566,22 @@ public: vk::PipelineStageFlagBits::eTransfer, vk::AccessFlagBits::eTransferWrite); const std::array volume_values{0x10203040u, 0x50607080u}; - std::memcpy(memory + 0x1000, volume_values.data(), sizeof(volume_values)); + TileSizeAlign volume_size{}; + TileGetTextureTotalSize(Prospero::BufferFormat::k32UInt, 1, 1, 2, 1, + Prospero::TileMode::kLinear, true, volume_size); + const auto volume_pitch = TileGetTexturePitch( + Prospero::BufferFormat::k32UInt, 1, Prospero::TileMode::kLinear); + const auto volume_slice_words = volume_size.size / 2 / sizeof(uint32_t); + std::vector volume_guest(volume_size.size / sizeof(uint32_t)); + volume_guest[0] = volume_values[0]; + volume_guest[volume_slice_words] = volume_values[1]; + std::memcpy(memory + 0x1000, volume_guest.data(), volume_size.size); auto array_desc = - MakeLinearDesc(base + 0x1000, sizeof(volume_values), + MakeLinearDesc(base + 0x1000, volume_size.size, vk::Format::eR32Uint, Prospero::BufferFormat::k32UInt, Prospero::ImageType::kColor2D, {1, 1, 1}, 2, 4, 1); + array_desc.info.pitch = volume_pitch; + array_desc.info.mip_layout[0].pitch = volume_pitch; const auto array_image = texture_cache.FindImage(array_desc); (void)texture_cache.FindTexture(array_image, array_desc); texture_cache.MarkGpuWritten(array_image); @@ -4590,12 +4601,10 @@ public: "ownership"); constexpr uint64_t unique_volume_offset = 0x27b0000; - std::memcpy(memory + unique_volume_offset, volume_values.data(), - sizeof(volume_values)); - auto unique_volume_desc = MakeLinearDesc( - base + unique_volume_offset, sizeof(volume_values), - vk::Format::eR32Uint, Prospero::BufferFormat::k32UInt, - Prospero::ImageType::kColor3D, {1, 1, 2}, 1, sizeof(uint32_t), 1); + std::memcpy(memory + unique_volume_offset, volume_guest.data(), + volume_size.size); + auto unique_volume_desc = volume_desc; + unique_volume_desc.info.data.address = base + unique_volume_offset; const auto unique_volume_image = texture_cache.FindImage(unique_volume_desc); (void)texture_cache.FindTexture(unique_volume_image, unique_volume_desc); @@ -4621,6 +4630,16 @@ public: "partial 3D synchronization was accepted, " "full-volume synchronization was " "rejected, or ownership changed"); + auto volume_readback = CreateHostBuffer( + name, volume_size.size, vk::BufferUsageFlagBits::eTransferDst, + std::vector(volume_guest.size(), 0)); + TransferReadBarrier(full_volume.Handle(), volume_size.size); + const vk::BufferCopy volume_copy{0, 0, volume_size.size}; + command.Handle().copyBuffer(full_volume.Handle(), volume_readback.buffer, + 1, &volume_copy); + HostReadBarrier(volume_readback.buffer, volume_readback.size, + vk::PipelineStageFlagBits::eTransfer, + vk::AccessFlagBits::eTransferWrite); constexpr uint64_t depth_containment_offset = 0x27c0000; constexpr std::array depth_containment_values{0.25f, 0.75f, @@ -6156,6 +6175,13 @@ public: const auto mip_prefix_words = ReadBuffer( name, mip_prefix_readback, mip_prefix_size / sizeof(uint32_t)); + const auto volume_words = + ReadBuffer(name, volume_readback, volume_guest.size()); + Require(name, "formatted volume content", + volume_words[0] == volume_values[0] && + volume_words[volume_slice_words] == volume_values[1], + "volume synchronization lost a padded depth slice"); + DestroyBuffer(&volume_readback); const auto mip_formatted_words = ReadBuffer( name, mip_formatted_readback, mip_guest_size / sizeof(uint32_t)); const auto mip0_word = mip_sizes[0].offset / sizeof(uint32_t);