diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index e73d6c1598..332dec09c0 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -283,11 +283,13 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { [[nodiscard]] vk::Image MakeImage(const Device& device, const MemoryAllocator& allocator, const ImageInfo& info, std::span view_formats, - std::optional format_override = {}) { + std::optional format_override = {}, + VkImageUsageFlags host_usage = 0) { if (info.type == ImageType::Buffer) { return vk::Image{}; } VkImageCreateInfo image_ci = MakeImageCreateInfo(device, info, format_override); + image_ci.usage |= host_usage; const VkImageFormatListCreateInfo image_format_list = { .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO, .pNext = nullptr, @@ -1042,6 +1044,17 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched pitch_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, compute_pass_descriptor_queue); } + for (size_t index = 0; index < VideoCore::Surface::MaxPixelFormat; index++) { + const auto format = static_cast(index); + if (!device.IsHostImageCopySupported() || DefaultBlockWidth(format) == 1) { + continue; + } + const VkFormat vk_format = + MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, format).format; + host_copy_formats[index] = + (device.GetPhysical().GetFormatProperties3(vk_format).optimalTilingFeatures & + VK_FORMAT_FEATURE_2_HOST_IMAGE_TRANSFER_BIT_EXT) != 0; + } if (!device.IsKhrImageFormatListSupported()) { return; } @@ -1968,10 +1981,7 @@ void TextureCacheRuntime::TickFrame() { Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_, VAddr cpu_addr_) : VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_), scheduler{&runtime_.scheduler}, - runtime{&runtime_}, - original_image(MakeImage(runtime_.device, runtime_.memory_allocator, info, - runtime->ViewFormats(info.format))), - aspect_mask(ImageAspectMask(info.format)) { + runtime{&runtime_}, aspect_mask(ImageAspectMask(info.format)) { if (IsPixelFormatASTC(info.format) && !runtime->device.IsOptimalAstcSupported()) { switch (Settings::values.accelerate_astc.GetValue()) { case Settings::AstcDecodeMode::Gpu: @@ -1997,6 +2007,13 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu WillUseAcceleratedUnswizzle(runtime->device, info)) { flags |= VideoCommon::ImageFlagBits::AcceleratedUpload; } + VkImageUsageFlags host_usage{}; + if (False(flags & VideoCommon::ImageFlagBits::Converted) && + runtime->host_copy_formats[static_cast(info.format)]) { + host_usage = VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT; + } + original_image = MakeImage(runtime->device, runtime->memory_allocator, info, + runtime->ViewFormats(info.format), {}, host_usage); if (runtime->device.HasDebuggingToolAttached()) { original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str()); } @@ -2130,6 +2147,33 @@ void Image::UploadMemory(const StagingBufferRef& map, std::span memory, + std::span copies) { + const auto vk_copies = TransformBufferImageCopies(copies, 0, aspect_mask); + boost::container::small_vector regions; + for (const VkBufferImageCopy2& copy : vk_copies) { + regions.push_back(VkMemoryToImageCopyEXT{ + .sType = VK_STRUCTURE_TYPE_MEMORY_TO_IMAGE_COPY_EXT, + .pNext = nullptr, + .pHostPointer = memory.data() + copy.bufferOffset, + .memoryRowLength = copy.bufferRowLength, + .memoryImageHeight = copy.bufferImageHeight, + .imageSubresource = copy.imageSubresource, + .imageOffset = copy.imageOffset, + .imageExtent = copy.imageExtent, + }); + } + runtime->device.GetLogical().CopyMemoryToImageEXT(VkCopyMemoryToImageInfoEXT{ + .sType = VK_STRUCTURE_TYPE_COPY_MEMORY_TO_IMAGE_INFO_EXT, + .pNext = nullptr, + .flags = 0, + .dstImage = *original_image, + .dstImageLayout = VK_IMAGE_LAYOUT_GENERAL, + .regionCount = static_cast(regions.size()), + .pRegions = regions.data(), + }); +} + void Image::DownloadMemory(VkBuffer buffer, size_t offset, std::span copies) { std::array buffer_handles{ @@ -3177,9 +3221,27 @@ void TextureCacheRuntime::AccelerateImageUpload( } void TextureCacheRuntime::TransitionImageLayout(Image& image) { - if (!image.ExchangeInitialization()) { - RecordInitialLayout(scheduler, image.Handle(), image.AspectMask()); + if (image.ExchangeInitialization()) { + return; } + if (!image.CanUploadHostMemory()) { + RecordInitialLayout(scheduler, image.Handle(), image.AspectMask()); + return; + } + device.GetLogical().TransitionImageLayoutEXT(VkHostImageLayoutTransitionInfoEXT{ + .sType = VK_STRUCTURE_TYPE_HOST_IMAGE_LAYOUT_TRANSITION_INFO_EXT, + .pNext = nullptr, + .image = image.Handle(), + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .subresourceRange{ + .aspectMask = image.AspectMask(), + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }); } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index ea8ecc4df6..ee0295d66a 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -6,6 +6,7 @@ #pragma once +#include #include #include "video_core/texture_cache/texture_cache_base.h" @@ -154,6 +155,7 @@ public: std::optional pitch_unswizzle_pass; const Settings::ResolutionScalingInfo& resolution; std::array, VideoCore::Surface::MaxPixelFormat> view_formats; + std::bitset host_copy_formats; static constexpr size_t indexing_slots = 8 * sizeof(size_t); std::array buffers{}; @@ -294,6 +296,13 @@ public: void UploadMemory(const StagingBufferRef& map, std::span copies); + void UploadHostMemory(std::span memory, + std::span copies); + + [[nodiscard]] bool CanUploadHostMemory() const noexcept { + return (original_image.UsageFlags() & VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT) != 0; + } + void DownloadMemory(VkBuffer buffer, size_t offset, std::span copies); diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 5c5ec5ace9..c199b5953c 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -1033,6 +1033,18 @@ void TextureCache

::RefreshContents(Image& image, ImageId image_id) { QueueAsyncDecode(image, image_id); return; } + if constexpr (requires { image.UploadHostMemory(unswizzle_data_buffer, {}); }) { + if (True(image.flags & ImageFlagBits::ReorderableUpload) && image.CanUploadHostMemory()) { + Tegra::Memory::GpuGuestMemory + swizzle_data(*gpu_memory, image.gpu_addr, image.guest_size_bytes, + &swizzle_data_buffer); + unswizzle_data_buffer.resize_destructive(image.unswizzled_size_bytes); + const auto copies = FixSmallVectorADL(UnswizzleImage( + *gpu_memory, image.gpu_addr, image.info, swizzle_data, unswizzle_data_buffer)); + image.UploadHostMemory(unswizzle_data_buffer, copies); + return; + } + } auto staging = runtime.UploadStagingBuffer(MapSizeBytes(image)); UploadImageContents(image, staging); diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index e9075a62b5..cd5e5a8412 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -433,6 +433,13 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR const vk::InstanceDispatch& dld_) : instance{instance_}, dld{dld_}, physical{physical_}, format_properties(GetFormatProperties(physical)) { + const VkPhysicalDeviceMemoryProperties memory_properties = + physical.GetMemoryProperties().memoryProperties; + is_uma = std::all_of(memory_properties.memoryTypes, + memory_properties.memoryTypes + memory_properties.memoryTypeCount, + [](const VkMemoryType& type) { + return (type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0; + }); // Get suitability and device properties. const bool is_suitable = GetSuitability(surface != VkSurfaceKHR{}); @@ -487,15 +494,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR properties.subgroup_size_control.maxSubgroupSize > GuestWarpSize; is_integrated = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; - const VkPhysicalDeviceMemoryProperties memory_properties = - physical.GetMemoryProperties().memoryProperties; - is_uma = std::all_of(memory_properties.memoryTypes, - memory_properties.memoryTypes + memory_properties.memoryTypeCount, - [](const VkMemoryType& type) { - return (type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == - 0 || - (type.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0; - }); supports_d24_depth = IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT, @@ -1425,6 +1423,12 @@ void Device::RemoveUnsuitableExtensions() { RemoveExtensionFeatureIfUnsuitable(extensions.maintenance5, features.maintenance5, VK_KHR_MAINTENANCE_5_EXTENSION_NAME); + if (!is_uma || instance_version < VK_API_VERSION_1_3 || + !features.host_image_copy.hostImageCopy) { + RemoveExtensionFeature(extensions.host_image_copy, features.host_image_copy, + VK_EXT_HOST_IMAGE_COPY_EXTENSION_NAME); + } + extensions.synchronization2 = features.synchronization2.synchronization2; extensions.dynamic_rendering = features.dynamic_rendering.dynamicRendering; diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index b562c210e5..63a2e5b7fa 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -68,6 +68,7 @@ VK_DEFINE_HANDLE(VmaAllocator) FEATURE(EXT, ExtendedDynamicState2, EXTENDED_DYNAMIC_STATE_2, extended_dynamic_state2) \ FEATURE(EXT, ExtendedDynamicState3, EXTENDED_DYNAMIC_STATE_3, extended_dynamic_state3) \ FEATURE(EXT, 4444Formats, 4444_FORMATS, format_a4b4g4r4) \ + FEATURE(EXT, HostImageCopy, HOST_IMAGE_COPY, host_image_copy) \ FEATURE(EXT, IndexTypeUint8, INDEX_TYPE_UINT8, index_type_uint8) \ FEATURE(EXT, LineRasterization, LINE_RASTERIZATION, line_rasterization) \ FEATURE(EXT, PrimitiveTopologyListRestart, PRIMITIVE_TOPOLOGY_LIST_RESTART, \ @@ -988,6 +989,10 @@ FN_MAX_LIMIT_LIST return is_uma; } + bool IsHostImageCopySupported() const { + return extensions.host_image_copy; + } + bool HasNullDescriptor() const { return features.robustness2.nullDescriptor; } diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 9a901cd8fb..01a65f95f5 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -206,6 +206,8 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkGetPipelineExecutablePropertiesKHR); X(vkGetPipelineExecutableStatisticsKHR); X(vkGetSemaphoreCounterValue); + X(vkCopyMemoryToImageEXT); + X(vkTransitionImageLayoutEXT); X(vkQueueBindSparse); X(vkQueueSubmit2); X(vkResetFences); diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index d524a36b65..c896a22bad 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -355,6 +355,8 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkGetPipelineExecutableStatisticsKHR vkGetPipelineExecutableStatisticsKHR{}; PFN_vkGetQueryPoolResults vkGetQueryPoolResults{}; PFN_vkGetSemaphoreCounterValue vkGetSemaphoreCounterValue{}; + PFN_vkCopyMemoryToImageEXT vkCopyMemoryToImageEXT{}; + PFN_vkTransitionImageLayoutEXT vkTransitionImageLayoutEXT{}; PFN_vkQueueBindSparse vkQueueBindSparse{}; PFN_vkQueueSubmit2 vkQueueSubmit2{}; PFN_vkResetFences vkResetFences{}; @@ -1092,6 +1094,14 @@ public: dld->vkResetQueryPool(handle, query_pool, first, count); } + void CopyMemoryToImageEXT(const VkCopyMemoryToImageInfoEXT& info) const { + Check(dld->vkCopyMemoryToImageEXT(handle, &info)); + } + + void TransitionImageLayoutEXT(const VkHostImageLayoutTransitionInfoEXT& info) const { + Check(dld->vkTransitionImageLayoutEXT(handle, 1, &info)); + } + VkResult GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size, void* data, VkDeviceSize stride, VkQueryResultFlags flags) const noexcept {