From 14cc49e72711189c3e1880eb6e055bd1cae3920a Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 26 Aug 2026 17:00:18 -0400 Subject: [PATCH] Adjustments on upload for textures --- .../renderer_opengl/gl_texture_cache.h | 1 + .../renderer_vulkan/vk_buffer_cache.cpp | 7 +- .../renderer_vulkan/vk_buffer_cache.h | 2 +- .../renderer_vulkan/vk_compute_pass.cpp | 225 ++++++++++-------- .../renderer_vulkan/vk_compute_pass.h | 3 + .../renderer_vulkan/vk_texture_cache.cpp | 62 ++++- .../renderer_vulkan/vk_texture_cache.h | 16 ++ src/video_core/texture_cache/image_base.h | 2 + src/video_core/texture_cache/texture_cache.h | 52 ++++ .../texture_cache/texture_cache_base.h | 3 + .../vulkan_common/vulkan_memory_allocator.cpp | 11 + .../vulkan_common/vulkan_memory_allocator.h | 10 + 12 files changed, 289 insertions(+), 105 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 8aed499bb7..017c32986c 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h @@ -373,6 +373,7 @@ struct TextureCacheParams { static constexpr bool HAS_DEVICE_MEMORY_INFO = true; static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true; static constexpr bool HAS_MSAA_DOWNLOADS = false; + static constexpr bool USE_UNIFIED_MEMORY = false; using Runtime = OpenGL::TextureCacheRuntime; using Image = OpenGL::Image; diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 0b93b081f5..57f62f1282 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -424,11 +424,8 @@ void BufferCacheRuntime::TryEnableUnifiedMemory(void* base, size_t size, std::span hardware_buffers, size_t hardware_buffer_window, size_t hardware_buffer_base) { - unified_memory = std::make_unique( - device, base, size, hardware_buffers, hardware_buffer_window, hardware_buffer_base); - if (!unified_memory->IsValid()) { - unified_memory.reset(); - } + unified_memory = memory_allocator.CreateHostMemoryImport( + base, size, hardware_buffers, hardware_buffer_window, hardware_buffer_base); } void BufferCacheRuntime::CopyToUnifiedMemory( diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 73649bfeb8..ff42e23963 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -264,7 +264,7 @@ private: std::shared_ptr quad_strip_index_buffer; vk::Buffer null_buffer; - std::unique_ptr unified_memory; + HostMemoryImport* unified_memory{}; boost::container::small_vector pending_unified_copies; std::unique_ptr uint8_pass; diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index 71968efe3b..ea03620169 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -4,6 +4,7 @@ // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include #include @@ -955,10 +956,10 @@ bool BlockLinearUnswizzle2DPass::IsSupported(const Device& device, if (info.type != VideoCommon::ImageType::e2D) { return false; } - if (info.resources.levels != 1 || info.resources.layers != 1) { + if (info.num_samples > 1) { return false; } - if (info.num_samples > 1) { + if (device.GetStorageBufferAlignment() > Tegra::Texture::GOB_SIZE) { return false; } if (VideoCore::Surface::IsPixelFormatASTC(info.format) && !device.IsOptimalAstcSupported()) { @@ -974,83 +975,37 @@ bool BlockLinearUnswizzle2DPass::IsSupported(const Device& device, void BlockLinearUnswizzle2DPass::Unswizzle( Image& image, const StagingBufferRef& swizzled, std::span swizzles) { - if (swizzles.empty()) { + UnswizzleFrom(image, swizzled.buffer, swizzled.offset, swizzles); +} + +void BlockLinearUnswizzle2DPass::UnswizzleFrom( + Image& image, VkBuffer source_buffer, VkDeviceSize source_offset, + std::span swizzles) { + const VkImage dst_image = image.Handle(); + if (swizzles.empty() || source_buffer == VK_NULL_HANDLE || dst_image == VK_NULL_HANDLE) { return; } - const VideoCommon::SwizzleParameters& sw = swizzles.front(); - const auto params = VideoCommon::Accelerated::MakeBlockLinearSwizzle2DParams(sw, image.info); - - const u32 width = sw.num_tiles.width; - const u32 height = sw.num_tiles.height; - const u32 texel_width = image.info.size.width; - const u32 texel_height = image.info.size.height; - const u32 depth = image.info.resources.layers; - const u32 bytes_per_block = 1u << params.bytes_per_block_log2; - const VkDeviceSize output_size = - static_cast(width) * height * depth * bytes_per_block; - - const StagingBufferRef output = - staging_buffer_pool.Request(static_cast(output_size), MemoryUsage::DeviceLocal); - - BlockLinearUnswizzle2DPushConstants pc{}; - pc.dim = {width, height, depth}; - pc.bytes_per_block_log2 = params.bytes_per_block_log2; - pc.origin = params.origin; - pc.layer_stride = params.layer_stride; - pc.block_size = params.block_size; - pc.x_shift = params.x_shift; - pc.block_height = params.block_height; - pc.block_height_mask = params.block_height_mask; + const u32 layers = image.info.resources.layers; + const VkImageAspectFlags aspect = image.AspectMask(); scheduler.RequestOutsideRenderPassOperationContext(); - compute_pass_descriptor_queue.Acquire(scheduler, 2); - compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, sw.buffer_offset + swizzled.offset, - image.guest_size_bytes - sw.buffer_offset); - compute_pass_descriptor_queue.AddBuffer(output.buffer, output.offset, output_size); - - const void* descriptor_data = compute_pass_descriptor_queue.UpdateData(); - const VkDescriptorSet set = descriptor_allocator.Commit(); - - const u32 gx = Common::DivCeil(width, 16u); - const u32 gy = Common::DivCeil(height, 8u); - const bool is_initialized = image.ExchangeInitialization(); - - const VkBuffer out_buffer = output.buffer; - const VkDeviceSize out_offset = output.offset; - const VkImage dst_image = image.Handle(); - const VkImageAspectFlags aspect = image.AspectMask(); - - scheduler.Record([this, set, descriptor_data, pc, gx, gy, depth, output_size, out_buffer, - out_offset, dst_image, aspect, texel_width, texel_height, - is_initialized](vk::CommandBuffer cmdbuf) { - if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) { - return; - } - device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); - cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pc), &pc); - cmdbuf.Dispatch(gx, gy, depth); - - const VkBufferMemoryBarrier buffer_barrier{ - .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .buffer = out_buffer, - .offset = out_offset, - .size = output_size, - }; - const VkImageMemoryBarrier pre_copy{ + VkAccessFlags pre_access = VK_ACCESS_NONE; + VkImageLayout pre_layout = VK_IMAGE_LAYOUT_UNDEFINED; + VkPipelineStageFlags pre_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + if (image.ExchangeInitialization()) { + pre_access = VK_ACCESS_SHADER_READ_BIT; + pre_layout = VK_IMAGE_LAYOUT_GENERAL; + pre_stage = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE; + } + scheduler.Record([dst_image, aspect, pre_access, pre_layout, + pre_stage](vk::CommandBuffer cmdbuf) { + const VkImageMemoryBarrier barrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcAccessMask = static_cast(is_initialized ? VK_ACCESS_SHADER_READ_BIT - : VK_ACCESS_NONE), + .srcAccessMask = pre_access, .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, - .oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED, + .oldLayout = pre_layout, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -1063,27 +1018,96 @@ void BlockLinearUnswizzle2DPass::Unswizzle( .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | - (is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE - : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT), - VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, buffer_barrier, pre_copy); + cmdbuf.PipelineBarrier(pre_stage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, {}, barrier); + }); - const VkBufferImageCopy copy{ - .bufferOffset = out_offset, - .bufferRowLength = 0, - .bufferImageHeight = 0, - .imageSubresource{ - .aspectMask = aspect, - .mipLevel = 0, - .baseArrayLayer = 0, - .layerCount = depth, - }, - .imageOffset = {0, 0, 0}, - .imageExtent = {texel_width, texel_height, 1}, - }; - cmdbuf.CopyBufferToImage(out_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy); + for (const VideoCommon::SwizzleParameters& sw : swizzles) { + const auto params = + VideoCommon::Accelerated::MakeBlockLinearSwizzle2DParams(sw, image.info); + const u32 width = sw.num_tiles.width; + const u32 height = sw.num_tiles.height; + const u32 bytes_per_block = 1u << params.bytes_per_block_log2; + const VkDeviceSize output_size = + static_cast(width) * height * layers * bytes_per_block; + if (output_size == 0) { + continue; + } + const u32 level = static_cast(sw.level); + const u32 texel_width = (std::max)(1u, image.info.size.width >> level); + const u32 texel_height = (std::max)(1u, image.info.size.height >> level); - const VkImageMemoryBarrier post_copy{ + const StagingBufferRef output = + staging_buffer_pool.Request(static_cast(output_size), MemoryUsage::DeviceLocal); + + BlockLinearUnswizzle2DPushConstants pc{}; + pc.dim = {width, height, layers}; + pc.bytes_per_block_log2 = params.bytes_per_block_log2; + pc.origin = params.origin; + pc.layer_stride = params.layer_stride; + pc.block_size = params.block_size; + pc.x_shift = params.x_shift; + pc.block_height = params.block_height; + pc.block_height_mask = params.block_height_mask; + + compute_pass_descriptor_queue.Acquire(scheduler, 2); + compute_pass_descriptor_queue.AddBuffer(source_buffer, sw.buffer_offset + source_offset, + image.guest_size_bytes - sw.buffer_offset); + compute_pass_descriptor_queue.AddBuffer(output.buffer, output.offset, output_size); + + const void* descriptor_data = compute_pass_descriptor_queue.UpdateData(); + const VkDescriptorSet set = descriptor_allocator.Commit(); + + const u32 gx = Common::DivCeil(width, 16u); + const u32 gy = Common::DivCeil(height, 8u); + const VkBuffer out_buffer = output.buffer; + const VkDeviceSize out_offset = output.offset; + + scheduler.Record([this, set, descriptor_data, pc, gx, gy, layers, output_size, out_buffer, + out_offset, dst_image, aspect, texel_width, texel_height, + level](vk::CommandBuffer cmdbuf) { + if (out_buffer == VK_NULL_HANDLE) { + return; + } + device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); + cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pc), &pc); + cmdbuf.Dispatch(gx, gy, layers); + + const VkBufferMemoryBarrier buffer_barrier{ + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = out_buffer, + .offset = out_offset, + .size = output_size, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, buffer_barrier, {}); + + const VkBufferImageCopy copy{ + .bufferOffset = out_offset, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource{ + .aspectMask = aspect, + .mipLevel = level, + .baseArrayLayer = 0, + .layerCount = layers, + }, + .imageOffset = {0, 0, 0}, + .imageExtent = {texel_width, texel_height, 1}, + }; + cmdbuf.CopyBufferToImage(out_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + copy); + }); + } + + scheduler.Record([dst_image, aspect](vk::CommandBuffer cmdbuf) { + const VkImageMemoryBarrier barrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, @@ -1101,8 +1125,8 @@ void BlockLinearUnswizzle2DPass::Unswizzle( .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, - 0, {}, {}, post_copy); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, + vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, {}, {}, barrier); }); } @@ -1288,13 +1312,20 @@ void BlockLinearUnswizzle3DBufferPass::Unswizzle( .offset = out_offset, .size = output_size, }; + VkAccessFlags pre_copy_access = VK_ACCESS_NONE; + VkImageLayout pre_copy_layout = VK_IMAGE_LAYOUT_UNDEFINED; + VkPipelineStageFlags pre_copy_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + if (is_initialized) { + pre_copy_access = VK_ACCESS_SHADER_READ_BIT; + pre_copy_layout = VK_IMAGE_LAYOUT_GENERAL; + pre_copy_stage = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE; + } const VkImageMemoryBarrier pre_copy{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, - .srcAccessMask = static_cast(is_initialized ? VK_ACCESS_SHADER_READ_BIT - : VK_ACCESS_NONE), + .srcAccessMask = pre_copy_access, .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, - .oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED, + .oldLayout = pre_copy_layout, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -1307,9 +1338,7 @@ void BlockLinearUnswizzle3DBufferPass::Unswizzle( .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | - (is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE - : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT), + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | pre_copy_stage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, buffer_barrier, pre_copy); const VkBufferImageCopy copy{ diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h index c08d8724f6..d303161292 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.h +++ b/src/video_core/renderer_vulkan/vk_compute_pass.h @@ -178,6 +178,9 @@ public: void Unswizzle(Image& image, const StagingBufferRef& swizzled, std::span swizzles); + void UnswizzleFrom(Image& image, VkBuffer source_buffer, VkDeviceSize source_offset, + std::span swizzles); + private: Scheduler& scheduler; StagingBufferPool& staging_buffer_pool; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 5d3bad180c..162f323457 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -3157,7 +3157,7 @@ void TextureCacheRuntime::AccelerateImageUpload( return bl2d_unswizzle_pass->Unswizzle(image, map, swizzles); } - if (bl3db_unswizzle_pass && + if (bl3db_unswizzle_pass && z_count == 0 && BlockLinearUnswizzle3DBufferPass::IsSupported(device, image.info)) { return bl3db_unswizzle_pass->Unswizzle(image, map, swizzles); } @@ -3177,6 +3177,66 @@ void TextureCacheRuntime::AccelerateImageUpload( ASSERT(false); } +bool TextureCacheRuntime::IsUnifiedMemoryBindable() const noexcept { + const HostMemoryImport* const import = memory_allocator.GetHostMemoryImport(); + return import != nullptr && import->IsValid() && import->IsBindable(); +} + +u64 TextureCacheRuntime::UnifiedMemoryBase() const noexcept { + const HostMemoryImport* const import = memory_allocator.GetHostMemoryImport(); + if (import == nullptr) { + return 0; + } + return import->GetBaseOffset(); +} + +u64 TextureCacheRuntime::UnifiedMemorySize() const noexcept { + const HostMemoryImport* const import = memory_allocator.GetHostMemoryImport(); + if (import == nullptr) { + return 0; + } + return import->GetSize(); +} + +u64 TextureCacheRuntime::UnifiedMemoryWindowSize() const noexcept { + const HostMemoryImport* const import = memory_allocator.GetHostMemoryImport(); + if (import == nullptr) { + return 0; + } + return import->GetWindowSize(); +} + +bool TextureCacheRuntime::CanUploadImageDirectly(const VideoCommon::ImageInfo& info) const { + return bl2d_unswizzle_pass.has_value() && + BlockLinearUnswizzle2DPass::IsSupported(device, info); +} + +bool TextureCacheRuntime::UploadImageDirectly( + Image& image, size_t window_index, u64 window_offset, + std::span swizzles) { + if ((window_offset % device.GetStorageBufferAlignment()) != 0) { + return false; + } + if (image.guest_size_bytes > device.GetMaxStorageBufferRange()) { + return false; + } + const HostMemoryImport* const import = memory_allocator.GetHostMemoryImport(); + if (import == nullptr || window_index >= import->GetWindowCount()) { + return false; + } + const VkBuffer window_buffer = import->GetWindowBuffer(window_index); + if (window_buffer == VK_NULL_HANDLE) { + return false; + } + bl2d_unswizzle_pass->UnswizzleFrom(image, window_buffer, + static_cast(window_offset), swizzles); + return true; +} + +u64 TextureCacheRuntime::CurrentTick() const noexcept { + return scheduler.CurrentTick(); +} + void TextureCacheRuntime::TransitionImageLayout(Image& image) { if (!image.ExchangeInitialization()) { VkImageMemoryBarrier barrier{ diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 1fb1f3e5f5..6ee1c52990 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -101,6 +101,21 @@ public: std::span, u32 z_start, u32 z_count); + [[nodiscard]] bool IsUnifiedMemoryBindable() const noexcept; + + [[nodiscard]] u64 UnifiedMemoryBase() const noexcept; + + [[nodiscard]] u64 UnifiedMemorySize() const noexcept; + + [[nodiscard]] u64 UnifiedMemoryWindowSize() const noexcept; + + [[nodiscard]] bool CanUploadImageDirectly(const VideoCommon::ImageInfo& info) const; + + bool UploadImageDirectly(Image& image, size_t window_index, u64 window_offset, + std::span swizzles); + + [[nodiscard]] u64 CurrentTick() const noexcept; + void InsertUploadMemoryBarrier() {} void TransitionImageLayout(Image& image); @@ -588,6 +603,7 @@ struct TextureCacheParams { static constexpr bool HAS_DEVICE_MEMORY_INFO = true; static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true; static constexpr bool HAS_MSAA_DOWNLOADS = true; + static constexpr bool USE_UNIFIED_MEMORY = true; using Runtime = Vulkan::TextureCacheRuntime; using Image = Vulkan::Image; diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h index 79fc8505d6..4fd7eafe7d 100644 --- a/src/video_core/texture_cache/image_base.h +++ b/src/video_core/texture_cache/image_base.h @@ -95,6 +95,8 @@ struct ImageBase { u32 scale_rating = 0; u64 scale_tick = 0; bool has_scaled = false; + bool direct_upload_used = false; + bool direct_upload_blocked = false; size_t channel = 0; diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index b6a37286a0..75d1f42b31 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -585,6 +585,10 @@ FramebufferId TextureCache

::GetFramebufferId(const RenderTargets& key) { template void TextureCache

::WriteMemory(DAddr cpu_addr, size_t size) { ForEachImageInRegion(cpu_addr, size, [this](ImageId image_id, Image& image) { + if (image.direct_upload_used) { + image.direct_upload_used = false; + image.direct_upload_blocked = true; + } if (True(image.flags & ImageFlagBits::CpuModified)) { return; } @@ -1145,11 +1149,59 @@ void TextureCache

::RefreshContents(Image& image, ImageId image_id) { QueueAsyncUnswizzle(image, image_id); return; } + if (True(image.flags & ImageFlagBits::AcceleratedUpload) && + TryUploadFromUnifiedMemory(image)) { + runtime.InsertUploadMemoryBarrier(); + return; + } auto staging = runtime.UploadStagingBuffer(MapSizeBytes(image)); UploadImageContents(image, staging); runtime.InsertUploadMemoryBarrier(); } +template +bool TextureCache

::TryUploadFromUnifiedMemory([[maybe_unused]] Image& image) { + if constexpr (USE_UNIFIED_MEMORY) { + if (image.direct_upload_blocked || image.guest_size_bytes == 0) { + return false; + } + if (!runtime.IsUnifiedMemoryBindable() || !runtime.CanUploadImageDirectly(image.info)) { + return false; + } + const u64 window_size = runtime.UnifiedMemoryWindowSize(); + if (window_size == 0) { + return false; + } + const u8* const first = gpu_memory->GetSpan(image.gpu_addr, image.guest_size_bytes); + if (first == nullptr) { + return false; + } + const u64 phys_offset = static_cast(first - device_memory.GetPhysicalBase()); + const u64 unified_base = runtime.UnifiedMemoryBase(); + if (phys_offset < unified_base) { + return false; + } + const u64 relative = phys_offset - unified_base; + const u64 unified_size = runtime.UnifiedMemorySize(); + if (relative >= unified_size || unified_size - relative < image.guest_size_bytes) { + return false; + } + const u64 local_offset = relative % window_size; + if (window_size - local_offset < image.guest_size_bytes) { + return false; + } + const auto swizzles = FullUploadSwizzles(image.info); + if (!runtime.UploadImageDirectly(image, static_cast(relative / window_size), + local_offset, FixSmallVectorADL(swizzles))) { + return false; + } + image.direct_upload_used = true; + return true; + } else { + return false; + } +} + template template void TextureCache

::UploadImageContents(Image& image, StagingBuffer& staging) { diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index d143c1ef72..687c770794 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -108,6 +108,7 @@ class TextureCache : public VideoCommon::ChannelSetupCaches::max)()}; @@ -307,6 +308,8 @@ private: void RefreshContents(Image& image, ImageId image_id); + bool TryUploadFromUnifiedMemory(Image& image); + /// Upload data from guest to an image template void UploadImageContents(Image& image, StagingBuffer& staging_buffer); diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index 3589f0afc6..2be71f0a7c 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp @@ -651,6 +651,17 @@ namespace Vulkan { return MemoryCommit(allocator, a, info); } + HostMemoryImport *MemoryAllocator::CreateHostMemoryImport( + void *base, size_t size, std::span hardware_buffers, + size_t hardware_buffer_window, size_t hardware_buffer_base) { + unified_memory = std::make_unique( + device, base, size, hardware_buffers, hardware_buffer_window, hardware_buffer_base); + if (!unified_memory->IsValid()) { + unified_memory.reset(); + } + return unified_memory.get(); + } + MemoryCommit MemoryAllocator::Commit(const vk::Buffer &buffer, MemoryUsage usage) { // Allocate memory appropriate for this buffer automatically const auto vma_usage = MemoryUsageVma(usage); diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.h b/src/video_core/vulkan_common/vulkan_memory_allocator.h index 8cff6b844d..26d599488e 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.h +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h @@ -192,6 +192,15 @@ namespace Vulkan { /// Commits memory required by the buffer and binds it (for buffers created outside VMA). MemoryCommit Commit(const vk::Buffer &buffer, MemoryUsage usage); + HostMemoryImport *CreateHostMemoryImport(void *base, size_t size, + std::span hardware_buffers, + size_t hardware_buffer_window, + size_t hardware_buffer_base); + + [[nodiscard]] HostMemoryImport *GetHostMemoryImport() const noexcept { + return unified_memory.get(); + } + private: static bool IsAutoUsage(VmaMemoryUsage u) noexcept { switch (u) { @@ -209,6 +218,7 @@ namespace Vulkan { const VkPhysicalDeviceMemoryProperties properties; ///< Physical device memory properties. VkDeviceSize buffer_image_granularity; ///< Adjacent buffer/image granularity u32 valid_memory_types{~0u}; + std::unique_ptr unified_memory; }; } // namespace Vulkan