From 36aace57cac906770b0f0be9cb34e29ae52e41a7 Mon Sep 17 00:00:00 2001 From: Vincent Lejeune Date: Wed, 16 Mar 2016 00:42:40 +0100 Subject: [PATCH] vulkan: Use simpler texture object --- rpcs3/Emu/RSX/VK/VKFormats.cpp | 26 +++++ rpcs3/Emu/RSX/VK/VKFormats.h | 4 + rpcs3/Emu/RSX/VK/VKGSRender.cpp | 61 ++++++------ rpcs3/Emu/RSX/VK/VKGSRender.h | 84 +--------------- rpcs3/Emu/RSX/VK/VKHelpers.h | 150 ++++++++++++++++++++++++++++- rpcs3/Emu/RSX/VK/VKRenderTargets.h | 88 +++++++++-------- rpcs3/Emu/RSX/VK/VKTexture.cpp | 38 +++++++- rpcs3/Emu/RSX/VK/VKTextureCache.h | 86 +++++++---------- 8 files changed, 337 insertions(+), 200 deletions(-) diff --git a/rpcs3/Emu/RSX/VK/VKFormats.cpp b/rpcs3/Emu/RSX/VK/VKFormats.cpp index b57337d400..cae7eee7c8 100644 --- a/rpcs3/Emu/RSX/VK/VKFormats.cpp +++ b/rpcs3/Emu/RSX/VK/VKFormats.cpp @@ -39,6 +39,32 @@ VkFormat get_compatible_depth_surface_format(const gpu_formats_support &support, throw EXCEPTION("Invalid format (0x%x)", format); } +std::tuple get_min_filter_and_mip(u8 min_filter) +{ + switch (min_filter) + { + case CELL_GCM_TEXTURE_NEAREST: return std::make_tuple(VK_FILTER_NEAREST, VK_SAMPLER_MIPMAP_MODE_NEAREST); + case CELL_GCM_TEXTURE_LINEAR: return std::make_tuple(VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_NEAREST); + case CELL_GCM_TEXTURE_NEAREST_NEAREST: return std::make_tuple(VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_NEAREST); + case CELL_GCM_TEXTURE_LINEAR_NEAREST: return std::make_tuple(VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_NEAREST); + case CELL_GCM_TEXTURE_NEAREST_LINEAR: return std::make_tuple(VK_FILTER_NEAREST, VK_SAMPLER_MIPMAP_MODE_LINEAR); + case CELL_GCM_TEXTURE_LINEAR_LINEAR: return std::make_tuple(VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_LINEAR); + case CELL_GCM_TEXTURE_CONVOLUTION_MIN: return std::make_tuple(VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_LINEAR); + } + throw EXCEPTION("Invalid max filter (0x%x)", min_filter); +} + +VkFilter get_mag_filter(u8 mag_filter) +{ + switch (mag_filter) + { + case CELL_GCM_TEXTURE_NEAREST: return VK_FILTER_NEAREST; + case CELL_GCM_TEXTURE_LINEAR: return VK_FILTER_LINEAR; + case CELL_GCM_TEXTURE_CONVOLUTION_MAG: return VK_FILTER_LINEAR; + } + throw EXCEPTION("Invalid mag filter (0x%x)", mag_filter); +} + VkSamplerAddressMode vk_wrap_mode(u32 gcm_wrap) { switch (gcm_wrap) diff --git a/rpcs3/Emu/RSX/VK/VKFormats.h b/rpcs3/Emu/RSX/VK/VKFormats.h index b0a69ebe61..b07e3c3db5 100644 --- a/rpcs3/Emu/RSX/VK/VKFormats.h +++ b/rpcs3/Emu/RSX/VK/VKFormats.h @@ -1,5 +1,6 @@ #pragma once #include "VKHelpers.h" +#include namespace vk { @@ -11,6 +12,9 @@ namespace vk gpu_formats_support get_optimal_tiling_supported_formats(VkPhysicalDevice physical_device); VkFormat get_compatible_depth_surface_format(const gpu_formats_support &support, rsx::surface_depth_format format); + + std::tuple get_min_filter_and_mip(u8 min_filter); + VkFilter get_mag_filter(u8 mag_filter); VkSamplerAddressMode vk_wrap_mode(u32 gcm_wrap); float max_aniso(u32 gcm_aniso); VkComponentMapping get_component_mapping(u32 format, u8 swizzle_mask); diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 4e906e3f7b..612c2715db 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -415,6 +415,8 @@ VKGSRender::VKGSRender() : GSRender(frame_type::Vulkan) m_uniform_buffer.reset(new vk::buffer(*m_device, RING_BUFFER_SIZE, m_memory_type_mapping.host_visible_coherent, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, 0)); m_index_buffer_ring_info.init(RING_BUFFER_SIZE); m_index_buffer.reset(new vk::buffer(*m_device, RING_BUFFER_SIZE, m_memory_type_mapping.host_visible_coherent, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, 0)); + m_texture_upload_buffer_ring_info.init(8 * RING_BUFFER_SIZE); + m_texture_upload_buffer.reset(new vk::buffer(*m_device, 8 * RING_BUFFER_SIZE, m_memory_type_mapping.host_visible_coherent, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, 0)); m_render_passes = get_precomputed_render_passes(*m_device, m_optimal_tiling_supported_formats); @@ -464,15 +466,19 @@ VKGSRender::~VKGSRender() m_index_buffer.release(); m_uniform_buffer.release(); m_attrib_buffers.release(); + m_texture_upload_buffer.release(); null_buffer.release(); null_buffer_view.release(); m_buffer_view_to_clean.clear(); + m_sampler_to_clean.clear(); m_framebuffer_to_clean.clear(); for (auto &render_pass : m_render_passes) if (render_pass) vkDestroyRenderPass(*m_device, render_pass, nullptr); + m_rtts.destroy(); + vkFreeDescriptorSets(*m_device, descriptor_pool, 1, &descriptor_sets); vkDestroyPipelineLayout(*m_device, pipeline_layout, nullptr); vkDestroyDescriptorSetLayout(*m_device, descriptor_layouts, nullptr); @@ -551,26 +557,28 @@ void VKGSRender::end() (u8)vk::get_draw_buffers(rsx::to_surface_target(rsx::method_registers[NV4097_SET_SURFACE_COLOR_TARGET])).size()); VkRenderPass current_render_pass = m_render_passes[idx]; - vk::texture *texture0 = nullptr; for (int i = 0; i < rsx::limits::textures_count; ++i) { if (m_program->has_uniform("tex" + std::to_string(i))) { if (!textures[i].enabled()) { - m_program->bind_uniform({ vk::null_sampler(), vk::null_image_view(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL}, "tex" + std::to_string(i), descriptor_sets); + m_program->bind_uniform({ vk::null_sampler(), vk::null_image_view(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }, "tex" + std::to_string(i), descriptor_sets); continue; } + vk::image_view *texture0 = m_texture_cache.upload_texture(m_command_buffer, textures[i], m_rtts, m_memory_type_mapping, m_texture_upload_buffer_ring_info, m_texture_upload_buffer.get()); - vk::texture &tex = (texture0)? (*texture0): m_texture_cache.upload_texture(m_command_buffer, textures[i], m_rtts); - vk::sampler sampler(*m_device, + VkFilter min_filter; + VkSamplerMipmapMode mip_mode; + std::tie(min_filter, mip_mode) = vk::get_min_filter_and_mip(textures[i].min_filter()); + m_sampler_to_clean.push_back(std::make_unique( + *m_device, vk::vk_wrap_mode(textures[i].wrap_s()), vk::vk_wrap_mode(textures[i].wrap_t()), vk::vk_wrap_mode(textures[i].wrap_r()), !!(textures[i].format() & CELL_GCM_TEXTURE_UN), textures[i].bias(), vk::max_aniso(textures[i].max_aniso()), textures[i].min_lod(), textures[i].max_lod(), - VK_FILTER_LINEAR, VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_NEAREST - ); - m_program->bind_uniform({ sampler.value, tex, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }, "tex" + std::to_string(i), descriptor_sets); - texture0 = &tex; + min_filter, vk::get_mag_filter(textures[i].mag_filter()), mip_mode + )); + m_program->bind_uniform({ m_sampler_to_clean.back()->value, texture0->value, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }, "tex" + std::to_string(i), descriptor_sets); } } @@ -733,22 +741,19 @@ void VKGSRender::clear_surface(u32 mask) { if (std::get<1>(m_rtts.m_bound_render_targets[i]) == nullptr) continue; - VkImage color_image = (*std::get<1>(m_rtts.m_bound_render_targets[i])); - VkImageLayout old_layout = std::get<1>(m_rtts.m_bound_render_targets[i])->get_layout(); - std::get<1>(m_rtts.m_bound_render_targets[i])->change_layout(m_command_buffer, VK_IMAGE_LAYOUT_GENERAL); - + VkImage color_image = std::get<1>(m_rtts.m_bound_render_targets[i])->value; + change_image_layout(m_command_buffer, color_image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_ASPECT_COLOR_BIT); vkCmdClearColorImage(m_command_buffer, color_image, VK_IMAGE_LAYOUT_GENERAL, &color_clear_values.color, 1, &range); - std::get<1>(m_rtts.m_bound_render_targets[i])->change_layout(m_command_buffer, old_layout); + change_image_layout(m_command_buffer, color_image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_ASPECT_COLOR_BIT); } } if (mask & 0x3) { - VkImageLayout old_layout = std::get<1>(m_rtts.m_bound_depth_stencil)->get_layout(); - std::get<1>(m_rtts.m_bound_depth_stencil)->change_layout(m_command_buffer, VK_IMAGE_LAYOUT_GENERAL); - - vkCmdClearDepthStencilImage(m_command_buffer, (*std::get<1>(m_rtts.m_bound_depth_stencil)), VK_IMAGE_LAYOUT_GENERAL, &depth_stencil_clear_values.depthStencil, 1, &depth_range); - std::get<1>(m_rtts.m_bound_depth_stencil)->change_layout(m_command_buffer, old_layout); + VkImage depth_stencil_image = std::get<1>(m_rtts.m_bound_depth_stencil)->value; + change_image_layout(m_command_buffer, depth_stencil_image, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_ASPECT_DEPTH_BIT); + vkCmdClearDepthStencilImage(m_command_buffer, std::get<1>(m_rtts.m_bound_depth_stencil)->value, VK_IMAGE_LAYOUT_GENERAL, &depth_stencil_clear_values.depthStencil, 1, &depth_range); + change_image_layout(m_command_buffer, depth_stencil_image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_ASPECT_DEPTH_BIT); } if (!was_recording) @@ -1062,16 +1067,16 @@ void VKGSRender::prepare_rtts() clip_horizontal, clip_vertical, rsx::to_surface_target(rsx::method_registers[NV4097_SET_SURFACE_COLOR_TARGET]), get_color_surface_addresses(), get_zeta_surface_address(), - (*m_device), &m_command_buffer, m_optimal_tiling_supported_formats); + (*m_device), &m_command_buffer, m_optimal_tiling_supported_formats, m_memory_type_mapping); //Bind created rtts as current fbo... std::vector draw_buffers = vk::get_draw_buffers(rsx::to_surface_target(rsx::method_registers[NV4097_SET_SURFACE_COLOR_TARGET])); - std::vector > fbo_images; + std::vector> fbo_images; for (u8 index: draw_buffers) { - vk::texture *raw = std::get<1>(m_rtts.m_bound_render_targets[index]); + vk::image *raw = std::get<1>(m_rtts.m_bound_render_targets[index]); VkImageSubresourceRange subres = {}; subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; @@ -1080,14 +1085,14 @@ void VKGSRender::prepare_rtts() subres.layerCount = 1; subres.levelCount = 1; - fbo_images.push_back(std::make_unique(*m_device, *raw, VK_IMAGE_VIEW_TYPE_2D, raw->get_format(), vk::default_component_map(), subres)); + fbo_images.push_back(std::make_unique(*m_device, raw->value, VK_IMAGE_VIEW_TYPE_2D, raw->info.format, vk::default_component_map(), subres)); } m_draw_buffers_count = fbo_images.size(); if (std::get<1>(m_rtts.m_bound_depth_stencil) != nullptr) { - vk::texture *raw = (std::get<1>(m_rtts.m_bound_depth_stencil)); + vk::image *raw = (std::get<1>(m_rtts.m_bound_depth_stencil)); VkImageSubresourceRange subres = {}; subres.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; @@ -1096,7 +1101,7 @@ void VKGSRender::prepare_rtts() subres.layerCount = 1; subres.levelCount = 1; - fbo_images.push_back(std::make_unique(*m_device, *raw, VK_IMAGE_VIEW_TYPE_2D, raw->get_format(), vk::default_component_map(), subres)); + fbo_images.push_back(std::make_unique(*m_device, raw->value, VK_IMAGE_VIEW_TYPE_2D, raw->info.format, vk::default_component_map(), subres)); } size_t idx = vk::get_render_pass_location(vk::get_compatible_surface_format(m_surface.color_format), vk::get_compatible_depth_surface_format(m_optimal_tiling_supported_formats, m_surface.depth_format), (u8)draw_buffers.size()); @@ -1190,9 +1195,9 @@ void VKGSRender::flip(int buffer) VkImage image_to_flip = nullptr; if (std::get<1>(m_rtts.m_bound_render_targets[0]) != nullptr) - image_to_flip = (*std::get<1>(m_rtts.m_bound_render_targets[0])); + image_to_flip = std::get<1>(m_rtts.m_bound_render_targets[0])->value; else - image_to_flip = (*std::get<1>(m_rtts.m_bound_render_targets[1])); + image_to_flip = std::get<1>(m_rtts.m_bound_render_targets[1])->value; VkImage target_image = m_swap_chain->get_swap_chain_image(m_current_present_image); vk::copy_scaled_image(m_command_buffer, image_to_flip, target_image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, @@ -1232,6 +1237,7 @@ void VKGSRender::flip(int buffer) m_uniform_buffer_ring_info.m_get_pos = m_uniform_buffer_ring_info.get_current_put_pos_minus_one(); m_index_buffer_ring_info.m_get_pos = m_index_buffer_ring_info.get_current_put_pos_minus_one(); m_attrib_ring_info.m_get_pos = m_attrib_ring_info.get_current_put_pos_minus_one(); + m_texture_upload_buffer_ring_info.m_get_pos = m_texture_upload_buffer_ring_info.get_current_put_pos_minus_one(); if (m_present_semaphore) { vkDestroySemaphore((*m_device), m_present_semaphore, nullptr); @@ -1239,10 +1245,11 @@ void VKGSRender::flip(int buffer) } //Feed back damaged resources to the main texture cache for management... - m_texture_cache.merge_dirty_textures(m_rtts.invalidated_resources); +// m_texture_cache.merge_dirty_textures(m_rtts.invalidated_resources); m_rtts.invalidated_resources.clear(); m_buffer_view_to_clean.clear(); + m_sampler_to_clean.clear(); m_draw_calls = 0; dirty_frame = true; diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.h b/rpcs3/Emu/RSX/VK/VKGSRender.h index 1e9a0c2c3a..ecc751ed9f 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.h +++ b/rpcs3/Emu/RSX/VK/VKGSRender.h @@ -12,87 +12,6 @@ #pragma comment(lib, "VKstatic.1.lib") -namespace vk -{ -// TODO: factorize between backends -class data_heap -{ - /** - * Does alloc cross get position ? - */ - template - bool can_alloc(size_t size) const - { - size_t alloc_size = align(size, Alignement); - size_t aligned_put_pos = align(m_put_pos, Alignement); - if (aligned_put_pos + alloc_size < m_size) - { - // range before get - if (aligned_put_pos + alloc_size < m_get_pos) - return true; - // range after get - if (aligned_put_pos > m_get_pos) - return true; - return false; - } - else - { - // ..]....[..get.. - if (aligned_put_pos < m_get_pos) - return false; - // ..get..]...[... - // Actually all resources extending beyond heap space starts at 0 - if (alloc_size > m_get_pos) - return false; - return true; - } - } - - size_t m_size; - size_t m_put_pos; // Start of free space -public: - data_heap() = default; - ~data_heap() = default; - data_heap(const data_heap&) = delete; - data_heap(data_heap&&) = delete; - - size_t m_get_pos; // End of free space - - void init(size_t heap_size) - { - m_size = heap_size; - m_put_pos = 0; - m_get_pos = heap_size - 1; - } - - template - size_t alloc(size_t size) - { - if (!can_alloc(size)) throw EXCEPTION("Working buffer not big enough"); - size_t alloc_size = align(size, Alignement); - size_t aligned_put_pos = align(m_put_pos, Alignement); - if (aligned_put_pos + alloc_size < m_size) - { - m_put_pos = aligned_put_pos + alloc_size; - return aligned_put_pos; - } - else - { - m_put_pos = alloc_size; - return 0; - } - } - - /** - * return current putpos - 1 - */ - size_t get_current_put_pos_minus_one() const - { - return (m_put_pos - 1 > 0) ? m_put_pos - 1 : m_size - 1; - } -}; -} - class VKGSRender : public GSRender { private: @@ -130,6 +49,8 @@ private: std::unique_ptr m_uniform_buffer; vk::data_heap m_index_buffer_ring_info; std::unique_ptr m_index_buffer; + vk::data_heap m_texture_upload_buffer_ring_info; + std::unique_ptr m_texture_upload_buffer; //Vulkan internals u32 m_current_present_image = 0xFFFF; @@ -152,6 +73,7 @@ private: std::vector > m_buffer_view_to_clean; std::vector > m_framebuffer_to_clean; + std::vector > m_sampler_to_clean; u32 m_draw_calls = 0; diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.h b/rpcs3/Emu/RSX/VK/VKHelpers.h index 0cdc56c128..4498178a52 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.h +++ b/rpcs3/Emu/RSX/VK/VKHelpers.h @@ -14,6 +14,7 @@ #include "Emu/state.h" #include "VulkanAPI.h" #include "../GCM.h" +#include "../Common/TextureUtils.h" namespace rsx { @@ -332,6 +333,60 @@ namespace vk } }; + struct image + { + VkImage value; + VkImageCreateInfo info = {}; + std::shared_ptr memory; + + image(VkDevice dev, uint32_t memory_type_index, + VkImageType image_type, + VkFormat format, + uint32_t width, uint32_t height, uint32_t depth, + VkDeviceSize mipmaps, VkDeviceSize layers, + VkSampleCountFlagBits samples, + VkImageLayout initial_layout, + VkImageTiling tiling, + VkImageUsageFlags usage, + VkImageCreateFlags image_flags) + : m_device(dev) + { + info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + info.imageType = image_type; + info.format = format; + info.extent = { width, height, depth }; + info.mipLevels = mipmaps; + info.arrayLayers = layers; + info.samples = samples; + info.tiling = tiling; + info.usage = usage; + info.flags = image_flags; + info.initialLayout = initial_layout; + info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + CHECK_RESULT(vkCreateImage(m_device, &info, nullptr, &value)); + + VkMemoryRequirements memory_req; + vkGetImageMemoryRequirements(m_device, value, &memory_req); + memory = std::make_shared(m_device, memory_req.size, memory_type_index); + + CHECK_RESULT(vkBindImageMemory(m_device, value, memory->memory, 0)); + } + + // TODO: Ctor that uses a provided memory heap + + ~image() + { + vkDestroyImage(m_device, value, nullptr); + } + + image(const image&) = delete; + image(image&&) = delete; + + private: + VkDevice m_device; + }; + struct image_view { VkImageView value; @@ -1202,8 +1257,9 @@ namespace vk struct bound_sampler { - VkImageView image_view = nullptr; - VkSampler sampler = nullptr; + VkFormat format; + VkImage image; + VkComponentMapping mapping; }; struct bound_buffer @@ -1251,4 +1307,94 @@ namespace vk void bind_uniform(const VkBufferView &buffer_view, const std::string &binding_name, VkDescriptorSet &descriptor_set); }; } + + + + // TODO: factorize between backends + class data_heap + { + /** + * Does alloc cross get position ? + */ + template + bool can_alloc(size_t size) const + { + size_t alloc_size = align(size, Alignement); + size_t aligned_put_pos = align(m_put_pos, Alignement); + if (aligned_put_pos + alloc_size < m_size) + { + // range before get + if (aligned_put_pos + alloc_size < m_get_pos) + return true; + // range after get + if (aligned_put_pos > m_get_pos) + return true; + return false; + } + else + { + // ..]....[..get.. + if (aligned_put_pos < m_get_pos) + return false; + // ..get..]...[... + // Actually all resources extending beyond heap space starts at 0 + if (alloc_size > m_get_pos) + return false; + return true; + } + } + + size_t m_size; + size_t m_put_pos; // Start of free space + public: + data_heap() = default; + ~data_heap() = default; + data_heap(const data_heap&) = delete; + data_heap(data_heap&&) = delete; + + size_t m_get_pos; // End of free space + + void init(size_t heap_size) + { + m_size = heap_size; + m_put_pos = 0; + m_get_pos = heap_size - 1; + } + + template + size_t alloc(size_t size) + { + if (!can_alloc(size)) throw EXCEPTION("Working buffer not big enough"); + size_t alloc_size = align(size, Alignement); + size_t aligned_put_pos = align(m_put_pos, Alignement); + if (aligned_put_pos + alloc_size < m_size) + { + m_put_pos = aligned_put_pos + alloc_size; + return aligned_put_pos; + } + else + { + m_put_pos = alloc_size; + return 0; + } + } + + /** + * return current putpos - 1 + */ + size_t get_current_put_pos_minus_one() const + { + return (m_put_pos - 1 > 0) ? m_put_pos - 1 : m_size - 1; + } + }; + + + /** + * Allocate enough space in upload_buffer and write all mipmap/layer data into the subbuffer. + * Then copy all layers into dst_image. + * dst_image must be in TRANSFER_DST_OPTIMAL layout and upload_buffer have TRANSFER_SRC_BIT usage flag. + */ + void copy_mipmaped_image_using_buffer(VkCommandBuffer cmd, VkImage dst_image, + const std::vector subresource_layout, int format, bool is_swizzled, + vk::data_heap &upload_heap, vk::buffer* upload_buffer); } \ No newline at end of file diff --git a/rpcs3/Emu/RSX/VK/VKRenderTargets.h b/rpcs3/Emu/RSX/VK/VKRenderTargets.h index eff7a96b42..327169a8f8 100644 --- a/rpcs3/Emu/RSX/VK/VKRenderTargets.h +++ b/rpcs3/Emu/RSX/VK/VKRenderTargets.h @@ -10,41 +10,49 @@ namespace rsx { struct vk_render_target_traits { - using surface_storage_type = vk::texture ; - using surface_type = vk::texture*; + using surface_storage_type = std::unique_ptr; + using surface_type = vk::image*; using command_list_type = vk::command_buffer*; using download_buffer_object = void*; - static vk::texture create_new_surface(u32 address, surface_color_format format, size_t width, size_t height, vk::render_device &device, vk::command_buffer *cmd, const vk::gpu_formats_support &support) + static std::unique_ptr create_new_surface(u32 address, surface_color_format format, size_t width, size_t height, vk::render_device &device, vk::command_buffer *cmd, const vk::gpu_formats_support &support, const vk::memory_type_mapping &mem_mapping) { VkFormat requested_format = vk::get_compatible_surface_format(format); - - vk::texture rtt; - rtt.create(device, requested_format, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT|VK_IMAGE_USAGE_TRANSFER_SRC_BIT|VK_IMAGE_USAGE_SAMPLED_BIT, width, height, 1, true); - rtt.change_layout(*cmd, VK_IMAGE_LAYOUT_GENERAL); + std::unique_ptr rtt; + rtt.reset(new vk::image(device, mem_mapping.device_local, + VK_IMAGE_TYPE_2D, + requested_format, + width, height, 1, 1, 1, + VK_SAMPLE_COUNT_1_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT|VK_IMAGE_USAGE_TRANSFER_SRC_BIT|VK_IMAGE_USAGE_SAMPLED_BIT, + 0)); + change_image_layout(*cmd, rtt->value, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_ASPECT_COLOR_BIT); //Clear new surface VkClearColorValue clear_color; VkImageSubresourceRange range = vk::default_image_subresource_range(); - + clear_color.float32[0] = 0.f; clear_color.float32[1] = 0.f; clear_color.float32[2] = 0.f; clear_color.float32[3] = 0.f; - vkCmdClearColorImage(*cmd, rtt, rtt.get_layout(), &clear_color, 1, &range); - rtt.change_layout(*cmd, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + vkCmdClearColorImage(*cmd, rtt->value, VK_IMAGE_LAYOUT_GENERAL, &clear_color, 1, &range); + change_image_layout(*cmd, rtt->value, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_ASPECT_COLOR_BIT); return rtt; } - static vk::texture create_new_surface(u32 address, surface_depth_format format, size_t width, size_t height, vk::render_device &device, vk::command_buffer *cmd, const vk::gpu_formats_support &support) + static std::unique_ptr create_new_surface(u32 address, surface_depth_format format, size_t width, size_t height, vk::render_device &device, vk::command_buffer *cmd, const vk::gpu_formats_support &support, const vk::memory_type_mapping &mem_mapping) { VkFormat requested_format = vk::get_compatible_depth_surface_format(support, format); - vk::texture rtt; - rtt.create(device, requested_format, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT|VK_IMAGE_USAGE_SAMPLED_BIT, width, height, 1, true); - rtt.change_layout(*cmd, VK_IMAGE_LAYOUT_GENERAL); + std::unique_ptr ds; + ds.reset(new vk::image(device, mem_mapping.device_local, + VK_IMAGE_TYPE_2D, requested_format, width, height, 1, 1, 1, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT|VK_IMAGE_USAGE_SAMPLED_BIT, 0)); + change_image_layout(*cmd, ds->value, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); //Clear new surface.. VkClearDepthStencilValue clear_depth = {}; @@ -61,54 +69,52 @@ namespace rsx clear_depth.depth = 1.f; clear_depth.stencil = 0; - vkCmdClearDepthStencilImage(*cmd, rtt, rtt.get_layout(), &clear_depth, 1, &range); - rtt.change_layout(*cmd, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); - - return rtt; + vkCmdClearDepthStencilImage(*cmd, ds->value, VK_IMAGE_LAYOUT_GENERAL, &clear_depth, 1, &range); + change_image_layout(*cmd, ds->value, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + + return ds; } - static void prepare_rtt_for_drawing(vk::command_buffer* pcmd, vk::texture *surface) + static void prepare_rtt_for_drawing(vk::command_buffer* pcmd, vk::image *surface) { - surface->change_layout(*pcmd, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); +// surface->change_layout(*pcmd, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); } - static void prepare_rtt_for_sampling(vk::command_buffer* pcmd, vk::texture *surface) + static void prepare_rtt_for_sampling(vk::command_buffer* pcmd, vk::image *surface) { - surface->change_layout(*pcmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); +// surface->change_layout(*pcmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } - static void prepare_ds_for_drawing(vk::command_buffer* pcmd, vk::texture *surface) + static void prepare_ds_for_drawing(vk::command_buffer* pcmd, vk::image *surface) { - surface->change_layout(*pcmd, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); +// surface->change_layout(*pcmd, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); } - static void prepare_ds_for_sampling(vk::command_buffer* pcmd, vk::texture *surface) + static void prepare_ds_for_sampling(vk::command_buffer* pcmd, vk::image *surface) { - surface->change_layout(*pcmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); +// surface->change_layout(*pcmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } - static bool rtt_has_format_width_height(const vk::texture &rtt, surface_color_format format, size_t width, size_t height) + static bool rtt_has_format_width_height(const std::unique_ptr &rtt, surface_color_format format, size_t width, size_t height) { VkFormat fmt = vk::get_compatible_surface_format(format); - vk::texture &tex = const_cast(rtt); - - if (tex.get_format() == fmt && - tex.width() == width && - tex.height() == height) + + if (rtt->info.format == fmt && + rtt->info.extent.width == width && + rtt->info.extent.height == height) return true; return false; } - static bool ds_has_format_width_height(const vk::texture &ds, surface_depth_format format, size_t width, size_t height) + static bool ds_has_format_width_height(const std::unique_ptr &ds, surface_depth_format format, size_t width, size_t height) { // TODO: check format //VkFormat fmt = vk::get_compatible_depth_surface_format(format); - vk::texture &tex = const_cast(ds); if (//tex.get_format() == fmt && - tex.width() == width && - tex.height() == height) + ds->info.extent.width == width && + ds->info.extent.height == height) return true; return false; @@ -138,13 +144,19 @@ namespace rsx { } - static vk::texture *get(const vk::texture &tex) + static vk::image *get(const std::unique_ptr &tex) { - return const_cast(&tex); + return tex.get(); } }; struct vk_render_targets : public rsx::surface_store { + + void destroy() + { + m_render_targets_storage.clear(); + m_depth_stencil_storage.clear(); + } }; } diff --git a/rpcs3/Emu/RSX/VK/VKTexture.cpp b/rpcs3/Emu/RSX/VK/VKTexture.cpp index 0b2a3d35eb..d857f6384a 100644 --- a/rpcs3/Emu/RSX/VK/VKTexture.cpp +++ b/rpcs3/Emu/RSX/VK/VKTexture.cpp @@ -4,7 +4,6 @@ #include "../RSXThread.h" #include "../RSXTexture.h" #include "../rsx_utils.h" -#include "../Common/TextureUtils.h" #include "VKFormats.h" namespace vk @@ -36,8 +35,8 @@ namespace vk subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subres.baseArrayLayer = 0; subres.baseMipLevel = 0; - subres.layerCount = 1; - subres.levelCount = 1; + subres.layerCount = 100; + subres.levelCount = 100; return subres; } @@ -129,6 +128,39 @@ namespace vk copy_image(cmd, isrc, idst, srcLayout, dstLayout, width, height, mipmaps, aspect); } + void copy_mipmaped_image_using_buffer(VkCommandBuffer cmd, VkImage dst_image, + const std::vector subresource_layout, int format, bool is_swizzled, + vk::data_heap &upload_heap, vk::buffer* upload_buffer) + { + u32 mipmap_level = 0; + u32 block_in_pixel = get_format_block_size_in_texel(format); + u8 block_size_in_bytes = get_format_block_size_in_bytes(format); + for (const rsx_subresource_layout &layout : subresource_layout) + { + u32 row_pitch = align(layout.width_in_block * block_size_in_bytes, 256); + size_t image_linear_size = row_pitch * layout.height_in_block * layout.depth; + size_t offset_in_buffer = upload_heap.alloc<512>(image_linear_size); + + void *mapped_buffer = upload_buffer->map(offset_in_buffer, image_linear_size); + gsl::span mapped{ (gsl::byte*)mapped_buffer, gsl::narrow(image_linear_size) }; + upload_texture_subresource(mapped, layout, format, is_swizzled, 256); + upload_buffer->unmap(); + + VkBufferImageCopy copy_info = {}; + copy_info.bufferOffset = offset_in_buffer; + copy_info.imageExtent.height = layout.height_in_block * block_in_pixel; + copy_info.imageExtent.width = layout.width_in_block * block_in_pixel; + copy_info.imageExtent.depth = layout.depth; + copy_info.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy_info.imageSubresource.layerCount = 1; + copy_info.imageSubresource.mipLevel = mipmap_level; + copy_info.bufferRowLength = block_in_pixel * row_pitch / block_size_in_bytes; + + vkCmdCopyBufferToImage(cmd, upload_buffer->value, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_info); + mipmap_level++; + } + } + texture::texture(vk::swap_chain_image &img) { m_image_contents = img; diff --git a/rpcs3/Emu/RSX/VK/VKTextureCache.h b/rpcs3/Emu/RSX/VK/VKTextureCache.h index 54d4d185d8..161093bd67 100644 --- a/rpcs3/Emu/RSX/VK/VKTextureCache.h +++ b/rpcs3/Emu/RSX/VK/VKTextureCache.h @@ -15,8 +15,8 @@ namespace vk u16 height; u16 depth; u16 mipmaps; - - vk::texture uploaded_texture; + std::unique_ptr uploaded_image_view; + std::unique_ptr uploaded_texture; u64 protected_rgn_start; u64 protected_rgn_end; @@ -32,6 +32,8 @@ namespace vk std::vector m_cache; u32 num_dirty_textures = 0; + std::vector > m_temporary_image_view; + bool lock_memory_region(u32 start, u32 size) { static const u32 memory_page_size = 4096; @@ -98,7 +100,7 @@ namespace vk { if (tex.exists) { - tex.uploaded_texture.destroy(); + tex.uploaded_texture.reset(); tex.exists = false; } @@ -106,8 +108,7 @@ namespace vk } } - cached_texture_object object; - m_cache.push_back(object); + m_cache.push_back(cached_texture_object()); return m_cache[m_cache.size() - 1]; } @@ -133,7 +134,7 @@ namespace vk { if (tex.dirty && tex.exists) { - tex.uploaded_texture.destroy(); + tex.uploaded_texture.reset(); tex.exists = false; } } @@ -148,19 +149,10 @@ namespace vk void destroy() { - for (cached_texture_object &tex : m_cache) - { - if (tex.exists) - { - tex.uploaded_texture.destroy(); - tex.exists = false; - } - } - m_cache.resize(0); } - vk::texture& upload_texture(command_buffer cmd, rsx::texture &tex, rsx::vk_render_targets &m_rtts) + vk::image_view* upload_texture(command_buffer cmd, rsx::texture &tex, rsx::vk_render_targets &m_rtts, const vk::memory_type_mapping &memory_type_mapping, data_heap& upload_heap, vk::buffer* upload_buffer) { if (num_dirty_textures > 32) { @@ -175,21 +167,27 @@ namespace vk const u32 range = (u32)get_texture_size(tex); //First check if it exists as an rtt... - vk::texture *rtt_texture = nullptr; + vk::image *rtt_texture = nullptr; if (rtt_texture = m_rtts.get_texture_from_render_target_if_applicable(texaddr)) { - return *rtt_texture; + m_temporary_image_view.push_back(std::make_unique(*vk::get_current_renderer(), rtt_texture->value, VK_IMAGE_VIEW_TYPE_2D, rtt_texture->info.format, + vk::default_component_map(), + vk::default_image_subresource_range())); + return m_temporary_image_view.back().get(); } if (rtt_texture = m_rtts.get_texture_from_depth_stencil_if_applicable(texaddr)) { - return *rtt_texture; + m_temporary_image_view.push_back(std::make_unique(*vk::get_current_renderer(), rtt_texture->value, VK_IMAGE_VIEW_TYPE_2D, rtt_texture->info.format, + vk::default_component_map(), + vk::default_image_subresource_range())); + return m_temporary_image_view.back().get(); } cached_texture_object& cto = find_cached_texture(texaddr, range, true, tex.width(), tex.height(), tex.mipmap()); if (cto.exists && !cto.dirty) { - return cto.uploaded_texture; + return cto.uploaded_image_view.get(); } u32 raw_format = tex.format(); @@ -198,21 +196,32 @@ namespace vk VkComponentMapping mapping = vk::get_component_mapping(format, tex.remap()); VkFormat vk_format = get_compatible_sampler_format(format); - cto.uploaded_texture.create(*vk::get_current_renderer(), vk_format, VK_IMAGE_USAGE_SAMPLED_BIT, tex.width(), tex.height(), tex.mipmap(), false, mapping); - cto.uploaded_texture.init(tex, cmd); - cto.uploaded_texture.change_layout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + cto.uploaded_texture = std::make_unique(*vk::get_current_renderer(), memory_type_mapping.device_local, + VK_IMAGE_TYPE_2D, + vk_format, + tex.width(), tex.height(), 1, tex.mipmap(), 1, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, 0); + change_image_layout(cmd, cto.uploaded_texture->value, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_ASPECT_COLOR_BIT); + + cto.uploaded_image_view = std::make_unique(*vk::get_current_renderer(), cto.uploaded_texture->value, VK_IMAGE_VIEW_TYPE_2D, vk_format, + vk::get_component_mapping(tex.format() & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN), tex.remap()), + vk::default_image_subresource_range()); + + copy_mipmaped_image_using_buffer(cmd, cto.uploaded_texture->value, get_subresources_layout(tex), format, !(tex.format() & CELL_GCM_TEXTURE_LN), upload_heap, upload_buffer); + + change_image_layout(cmd, cto.uploaded_texture->value, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_ASPECT_COLOR_BIT); cto.exists = true; cto.dirty = false; cto.native_rsx_address = texaddr; cto.native_rsx_size = range; - cto.width = cto.uploaded_texture.width(); - cto.height = cto.uploaded_texture.height(); - cto.mipmaps = cto.uploaded_texture.mipmaps(); + cto.width = tex.width(); + cto.height = tex.height(); + cto.mipmaps = tex.mipmap(); lock_object(cto); - return cto.uploaded_texture; + return cto.uploaded_image_view.get(); } bool invalidate_address(u32 rsx_address) @@ -239,28 +248,7 @@ namespace vk void flush(vk::command_buffer &cmd) { - //Finish all pending transactions for any cache managed textures.. - for (cached_texture_object &tex : m_cache) - { - if (tex.dirty || !tex.exists) continue; - tex.uploaded_texture.flush(cmd); - } - } - - void merge_dirty_textures(std::list dirty_textures) - { - for (vk::texture &tex : dirty_textures) - { - cached_texture_object cto; - cto.uploaded_texture = tex; - cto.locked = false; - cto.exists = true; - cto.dirty = true; - cto.native_rsx_address = 0; - - num_dirty_textures++; - m_cache.push_back(cto); - } + m_temporary_image_view.clear(); } }; }