diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.cpp b/rpcs3/Emu/RSX/VK/VKHelpers.cpp index ce2126fb23..637d28f5e7 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.cpp +++ b/rpcs3/Emu/RSX/VK/VKHelpers.cpp @@ -9,6 +9,7 @@ namespace vk std::unique_ptr g_null_texture; std::unique_ptr g_null_image_view; + std::unique_ptr g_scratch_buffer; std::unordered_map> g_typeless_textures; VkSampler g_null_sampler = nullptr; @@ -187,6 +188,19 @@ namespace vk return ptr.get(); } + vk::buffer* get_scratch_buffer() + { + if (!g_scratch_buffer) + { + // 32M disposable scratch memory + g_scratch_buffer = std::make_unique(*g_current_renderer, 32 * 0x100000, + g_current_renderer->get_memory_mapping().device_local, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, 0); + } + + return g_scratch_buffer.get(); + } + void acquire_global_submit_lock() { g_submit_mutex.lock(); @@ -201,6 +215,7 @@ namespace vk { g_null_texture.reset(); g_null_image_view.reset(); + g_scratch_buffer.reset(); g_typeless_textures.clear(); diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.h b/rpcs3/Emu/RSX/VK/VKHelpers.h index 8619477f4b..40c01ed393 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.h +++ b/rpcs3/Emu/RSX/VK/VKHelpers.h @@ -72,6 +72,7 @@ namespace vk class physical_device; class command_buffer; struct image; + struct buffer; struct vk_data_heap; class mem_allocator_base; struct memory_type_mapping; @@ -100,6 +101,7 @@ namespace vk VkSampler null_sampler(); VkImageView null_image_view(vk::command_buffer&); image* get_typeless_helper(VkFormat format); + buffer* get_scratch_buffer(); memory_type_mapping get_memory_mapping(const physical_device& dev); gpu_formats_support get_optimal_tiling_supported_formats(const physical_device& dev); @@ -124,6 +126,10 @@ namespace vk void change_image_layout(VkCommandBuffer cmd, vk::image *image, VkImageLayout new_layout, VkImageSubresourceRange range); void change_image_layout(VkCommandBuffer cmd, vk::image *image, VkImageLayout new_layout); + void copy_image_typeless(VkCommandBuffer cmd, VkImage &src, VkImage &dst, VkImageLayout srcLayout, VkImageLayout dstLayout, + const areai& src_rect, const areai& dst_rect, u32 mipmaps, VkImageAspectFlags src_aspect, VkImageAspectFlags dst_aspect, + VkImageAspectFlags src_transfer_mask = 0xFF, VkImageAspectFlags dst_transfer_mask = 0xFF); + void copy_image(VkCommandBuffer cmd, VkImage &src, VkImage &dst, VkImageLayout srcLayout, VkImageLayout dstLayout, const areai& src_rect, const areai& dst_rect, u32 mipmaps, VkImageAspectFlags src_aspect, VkImageAspectFlags dst_aspect, VkImageAspectFlags src_transfer_mask = 0xFF, VkImageAspectFlags dst_transfer_mask = 0xFF); @@ -701,7 +707,7 @@ namespace vk VkBufferCreateInfo info = {}; std::unique_ptr memory; - buffer(vk::render_device& dev, u64 size, uint32_t memory_type_index, uint32_t access_flags, VkBufferUsageFlagBits usage, VkBufferCreateFlags flags) + buffer(const vk::render_device& dev, u64 size, uint32_t memory_type_index, uint32_t access_flags, VkBufferUsageFlags usage, VkBufferCreateFlags flags) : m_device(dev) { info.size = size; diff --git a/rpcs3/Emu/RSX/VK/VKTexture.cpp b/rpcs3/Emu/RSX/VK/VKTexture.cpp index 0e80511d27..7d2e79224e 100644 --- a/rpcs3/Emu/RSX/VK/VKTexture.cpp +++ b/rpcs3/Emu/RSX/VK/VKTexture.cpp @@ -55,6 +55,51 @@ namespace vk } } + void copy_image_typeless(VkCommandBuffer cmd, VkImage &src, VkImage &dst, VkImageLayout srcLayout, VkImageLayout dstLayout, + const areai& src_rect, const areai& dst_rect, u32 mipmaps, VkImageAspectFlags src_aspect, VkImageAspectFlags dst_aspect, + VkImageAspectFlags src_transfer_mask, VkImageAspectFlags dst_transfer_mask) + { + if (src == dst) + { + copy_image(cmd, src, dst, srcLayout, dstLayout, src_rect, dst_rect, mipmaps, src_aspect, dst_aspect, src_transfer_mask, dst_transfer_mask); + return; + } + + auto preferred_src_format = (src == dst) ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + auto preferred_dst_format = (src == dst) ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + + if (srcLayout != preferred_src_format) + change_image_layout(cmd, src, srcLayout, preferred_src_format, vk::get_image_subresource_range(0, 0, 1, 1, src_aspect)); + + if (dstLayout != preferred_dst_format) + change_image_layout(cmd, dst, dstLayout, preferred_dst_format, vk::get_image_subresource_range(0, 0, 1, 1, dst_aspect)); + + auto scratch_buf = vk::get_scratch_buffer(); + VkBufferImageCopy src_copy{}, dst_copy{}; + src_copy.imageExtent = { u32(src_rect.x2 - src_rect.x1), u32(src_rect.y2 - src_rect.y1), 1 }; + src_copy.imageOffset = { src_rect.x1, src_rect.y1, 0 }; + src_copy.imageSubresource = { src_aspect & src_transfer_mask, 0, 0, 1 }; + + dst_copy.imageExtent = { u32(dst_rect.x2 - dst_rect.x1), u32(dst_rect.y2 - dst_rect.y1), 1 }; + dst_copy.imageOffset = { dst_rect.x1, dst_rect.y1, 0 }; + dst_copy.imageSubresource = { dst_aspect & dst_transfer_mask, 0, 0, 1 }; + + for (u32 mip_level = 0; mip_level < mipmaps; ++mip_level) + { + vkCmdCopyImageToBuffer(cmd, src, preferred_src_format, scratch_buf->value, 1, &src_copy); + vkCmdCopyBufferToImage(cmd, scratch_buf->value, dst, preferred_dst_format, 1, &dst_copy); + + src_copy.imageSubresource.mipLevel++; + dst_copy.imageSubresource.mipLevel++; + } + + if (srcLayout != preferred_src_format) + change_image_layout(cmd, src, preferred_src_format, srcLayout, vk::get_image_subresource_range(0, 0, 1, 1, src_aspect)); + + if (dstLayout != preferred_dst_format) + change_image_layout(cmd, dst, preferred_dst_format, dstLayout, vk::get_image_subresource_range(0, 0, 1, 1, dst_aspect)); + } + void copy_image(VkCommandBuffer cmd, VkImage &src, VkImage &dst, VkImageLayout srcLayout, VkImageLayout dstLayout, const areai& src_rect, const areai& dst_rect, u32 mipmaps, VkImageAspectFlags src_aspect, VkImageAspectFlags dst_aspect, VkImageAspectFlags src_transfer_mask, VkImageAspectFlags dst_transfer_mask) diff --git a/rpcs3/Emu/RSX/VK/VKTextureCache.h b/rpcs3/Emu/RSX/VK/VKTextureCache.h index 6034828e97..684609dba9 100644 --- a/rpcs3/Emu/RSX/VK/VKTextureCache.h +++ b/rpcs3/Emu/RSX/VK/VKTextureCache.h @@ -1177,8 +1177,44 @@ namespace vk VkFormat format; blit_helper(vk::command_buffer *c) : commands(c) {} - void scale_image(vk::image* src, vk::image* dst, areai src_area, areai dst_area, bool interpolate, bool /*is_depth*/, const rsx::typeless_xfer& /*typeless*/) + void scale_image(vk::image* src, vk::image* dst, areai src_area, areai dst_area, bool interpolate, bool /*is_depth*/, const rsx::typeless_xfer& xfer_info) { + const auto src_aspect = vk::get_aspect_flags(src->info.format); + const auto dst_aspect = vk::get_aspect_flags(dst->info.format); + + vk::image* real_src = src; + vk::image* real_dst = dst; + + if (xfer_info.src_is_typeless) + { + auto internal_width = src->width() * xfer_info.src_scaling_hint; + auto format = vk::get_compatible_sampler_format(vk::get_current_renderer()->get_formats_support(), xfer_info.src_gcm_format); + + // Transfer bits from src to typeless src + real_src = vk::get_typeless_helper(format); + src_area.x1 = (u16)(src_area.x1 * xfer_info.src_scaling_hint); + src_area.x2 = (u16)(src_area.x2 * xfer_info.src_scaling_hint); + + vk::copy_image_typeless(*commands, src->value, real_src->value, src->current_layout, real_src->current_layout, + { 0, 0, (s32)src->width(), (s32)src->height() }, { 0, 0, (s32)internal_width, (s32)src->height() }, 1, + vk::get_aspect_flags(src->info.format), vk::get_aspect_flags(format)); + } + + if (xfer_info.dst_is_typeless) + { + auto internal_width = dst->width() * xfer_info.dst_scaling_hint; + auto format = vk::get_compatible_sampler_format(vk::get_current_renderer()->get_formats_support(), xfer_info.dst_gcm_format); + + // Transfer bits from dst to typeless dst + real_dst = vk::get_typeless_helper(format); + dst_area.x1 = (u16)(dst_area.x1 * xfer_info.dst_scaling_hint); + dst_area.x2 = (u16)(dst_area.x2 * xfer_info.dst_scaling_hint); + + vk::copy_image_typeless(*commands, dst->value, real_dst->value, dst->current_layout, real_dst->current_layout, + { 0, 0, (s32)dst->width(), (s32)dst->height() }, { 0, 0, (s32)internal_width, (s32)dst->height() }, 1, + vk::get_aspect_flags(dst->info.format), vk::get_aspect_flags(format)); + } + //Checks if (src_area.x2 <= src_area.x1 || src_area.y2 <= src_area.y1 || dst_area.x2 <= dst_area.x1 || dst_area.y2 <= dst_area.y1) { @@ -1186,29 +1222,36 @@ namespace vk return; } - if (src_area.x1 < 0 || src_area.x2 > (s32)src->width() || src_area.y1 < 0 || src_area.y2 > (s32)src->height()) + if (src_area.x1 < 0 || src_area.x2 >(s32)real_src->width() || src_area.y1 < 0 || src_area.y2 >(s32)real_src->height()) { LOG_ERROR(RSX, "Blit request denied because the source region does not fit!"); return; } - if (dst_area.x1 < 0 || dst_area.x2 > (s32)dst->width() || dst_area.y1 < 0 || dst_area.y2 > (s32)dst->height()) + if (dst_area.x1 < 0 || dst_area.x2 >(s32)real_dst->width() || dst_area.y1 < 0 || dst_area.y2 >(s32)real_dst->height()) { LOG_ERROR(RSX, "Blit request denied because the destination region does not fit!"); return; } - const auto aspect = vk::get_aspect_flags(src->info.format); const auto src_width = src_area.x2 - src_area.x1; const auto src_height = src_area.y2 - src_area.y1; const auto dst_width = dst_area.x2 - dst_area.x1; const auto dst_height = dst_area.y2 - dst_area.y1; - copy_scaled_image(*commands, src->value, dst->value, src->current_layout, dst->current_layout, src_area.x1, src_area.y1, src_width, src_height, - dst_area.x1, dst_area.y1, dst_width, dst_height, 1, aspect, src->info.format == dst->info.format, - interpolate? VK_FILTER_LINEAR : VK_FILTER_NEAREST, src->info.format, dst->info.format); + copy_scaled_image(*commands, real_src->value, real_dst->value, real_src->current_layout, real_dst->current_layout, src_area.x1, src_area.y1, src_width, src_height, + dst_area.x1, dst_area.y1, dst_width, dst_height, 1, dst_aspect, real_src->info.format == real_dst->info.format, + interpolate ? VK_FILTER_LINEAR : VK_FILTER_NEAREST, real_src->info.format, real_dst->info.format); - change_image_layout(*commands, dst, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, {(VkImageAspectFlags)aspect, 0, dst->info.mipLevels, 0, dst->info.arrayLayers}); + if (real_dst != dst) + { + auto internal_width = dst->width() * xfer_info.dst_scaling_hint; + vk::copy_image_typeless(*commands, real_dst->value, dst->value, real_dst->current_layout, dst->current_layout, + { 0, 0, (s32)internal_width, (s32)dst->height() }, { 0, 0, (s32)dst->width(), (s32)dst->height() }, 1, + vk::get_aspect_flags(real_dst->info.format), vk::get_aspect_flags(dst->info.format)); + } + + change_image_layout(*commands, dst, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, {(VkImageAspectFlags)dst_aspect, 0, dst->info.mipLevels, 0, dst->info.arrayLayers}); format = dst->info.format; } }