diff --git a/src/xenia/gpu/d3d12/d3d12_texture_cache.cc b/src/xenia/gpu/d3d12/d3d12_texture_cache.cc index 7a788d641..9c37c483f 100644 --- a/src/xenia/gpu/d3d12/d3d12_texture_cache.cc +++ b/src/xenia/gpu/d3d12/d3d12_texture_cache.cc @@ -1408,7 +1408,11 @@ bool D3D12TextureCache::LoadTextureDataFromResidentMemoryImpl(Texture& texture, const texture_util::TextureGuestLayout& guest_layout = d3d12_texture.guest_layout(); xenos::DataDimension dimension = texture_key.dimension; + // Whether the host texture is 3D (determines depth vs array layer layout). bool is_3d = dimension == xenos::DataDimension::k3D; + // Whether to use 3D tiling when reading from guest memory. + // For 3D-as-2D wrappers, the host texture is 2D but we need 3D tiling. + bool is_3d_tiling = is_3d || d3d12_texture.force_load_3d_tiling(); uint32_t width = texture_key.GetWidth(); uint32_t height = texture_key.GetHeight(); uint32_t depth_or_array_size = texture_key.GetDepthOrArraySize(); @@ -1597,7 +1601,7 @@ bool D3D12TextureCache::LoadTextureDataFromResidentMemoryImpl(Texture& texture, assert_true(texture_resolution_scale_x <= 7); assert_true(texture_resolution_scale_y <= 7); load_constants.is_tiled_3d_endian_scale = - uint32_t(texture_key.tiled) | (uint32_t(is_3d) << 1) | + uint32_t(texture_key.tiled) | (uint32_t(is_3d_tiling) << 1) | (uint32_t(texture_key.endianness) << 2) | (texture_resolution_scale_x << 4) | (texture_resolution_scale_y << 7); @@ -1836,10 +1840,7 @@ void D3D12TextureCache::UpdateTextureBindingsImpl( ID3D12Resource* D3D12TextureCache::D3D12Texture::GetOrCreate3DAs2DResource( D3D12_RESOURCE_STATES end_state) { - int32_t mode = cvars::gpu_3d_to_2d_texture_mode; - - // Mode 0: Feature disabled. - if (mode == 0) { + if (!cvars::gpu_3d_to_2d_texture) { return nullptr; } @@ -1877,64 +1878,20 @@ ID3D12Resource* D3D12TextureCache::D3D12Texture::GetOrCreate3DAs2DResource( return nullptr; } - if (mode == 1) { - // Mode 1: GPU copy - copy slice 0 from the 3D texture to the 2D resource. - D3D12_RESOURCE_STATES old_main_state = - SetResourceState(D3D12_RESOURCE_STATE_COPY_SOURCE); - d3d12_cache.command_processor_.PushTransitionBarrier( - resource(), old_main_state, D3D12_RESOURCE_STATE_COPY_SOURCE); + // Create a modified key for the 2D wrapper with depth=1 and mip_max_level=0. + // Keep dimension as k3D so guest layout uses 3D tiling math to correctly + // read slice 0 from the 3D-tiled guest memory. + TextureKey key_2d = key(); + key_2d.depth_or_array_size_minus_1 = 0; + key_2d.mip_max_level = 0; - auto& command_list = - d3d12_cache.command_processor_.GetDeferredCommandList(); + texture_3d_as_2d_.reset(new D3D12Texture( + d3d12_cache, key_2d, resource_2d.Get(), initial_state, false)); - D3D12_TEXTURE_COPY_LOCATION dest_loc = {}; - dest_loc.pResource = resource_2d.Get(); - dest_loc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - dest_loc.SubresourceIndex = 0; - - D3D12_TEXTURE_COPY_LOCATION src_loc = {}; - src_loc.pResource = resource(); - src_loc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - src_loc.SubresourceIndex = 0; - - D3D12_BOX src_box; - src_box.left = 0; - src_box.top = 0; - src_box.front = 0; - src_box.right = static_cast(source_desc.Width); - src_box.bottom = source_desc.Height; - src_box.back = 1; // Only copy 1 depth slice. - - command_list.D3DCopyTextureRegion(&dest_loc, 0, 0, 0, &src_loc, &src_box); - - // Restore source state. - d3d12_cache.command_processor_.PushTransitionBarrier( - resource(), D3D12_RESOURCE_STATE_COPY_SOURCE, old_main_state); - SetResourceState(old_main_state); - - // Create a minimal wrapper for the 2D resource. - TextureKey key_2d = key(); - key_2d.depth_or_array_size_minus_1 = 0; - key_2d.mip_max_level = 0; - texture_3d_as_2d_.reset(new D3D12Texture( - d3d12_cache, key_2d, resource_2d.Get(), initial_state, false)); - } else { - // Mode 2: CPU re-upload - reload texture data from guest memory. - // Keep dimension as k3D so LoadTextureData uses 3D tiling math to correctly - // read slice 0 from the 3D-tiled guest memory. - TextureKey key_load = key(); - key_load.depth_or_array_size_minus_1 = 0; - key_load.mip_max_level = 0; - - std::unique_ptr texture_wrap(new D3D12Texture( - d3d12_cache, key_load, resource_2d.Get(), initial_state, false)); - - if (!d3d12_cache.LoadTextureData(*texture_wrap)) { - XELOGE("D3D12Texture: Failed to untile 3D-as-2D data"); - return nullptr; - } - - texture_3d_as_2d_ = std::move(texture_wrap); + if (!d3d12_cache.LoadTextureData(*texture_3d_as_2d_)) { + XELOGE("D3D12Texture: Failed to load 3D-as-2D texture data"); + texture_3d_as_2d_.reset(); + return nullptr; } // Transition to requested state. diff --git a/src/xenia/gpu/gpu_flags.cc b/src/xenia/gpu/gpu_flags.cc index 21634dec8..6d1e063c8 100644 --- a/src/xenia/gpu/gpu_flags.cc +++ b/src/xenia/gpu/gpu_flags.cc @@ -90,8 +90,7 @@ DEFINE_bool(no_discard_stencil_in_transfer_pipelines, false, "May improve performance on some GPUs.", "GPU"); -DEFINE_int32(gpu_3d_to_2d_texture_mode, 2, - "Handle shaders that sample 3D textures as 2D by creating a 2D " - "copy of slice 0. 0 = disabled, 1 = GPU copy, " - "2 = CPU re-upload from guest memory (default).", - "GPU"); +DEFINE_bool(gpu_3d_to_2d_texture, true, + "Handle shaders that sample 3D textures as 2D by creating a 2D " + "texture from slice 0 of the guest memory.", + "GPU"); diff --git a/src/xenia/gpu/gpu_flags.h b/src/xenia/gpu/gpu_flags.h index e73854223..2e865abf5 100644 --- a/src/xenia/gpu/gpu_flags.h +++ b/src/xenia/gpu/gpu_flags.h @@ -36,7 +36,7 @@ DECLARE_bool(disassemble_pm4); DECLARE_bool(no_discard_stencil_in_transfer_pipelines); -DECLARE_int32(gpu_3d_to_2d_texture_mode); +DECLARE_bool(gpu_3d_to_2d_texture); #define XE_GPU_FINE_GRAINED_DRAW_SCOPES 1 diff --git a/src/xenia/gpu/texture_cache.h b/src/xenia/gpu/texture_cache.h index a8d1729dd..f14d14a3d 100644 --- a/src/xenia/gpu/texture_cache.h +++ b/src/xenia/gpu/texture_cache.h @@ -251,6 +251,11 @@ class TextureCache { return guest_layout().mips_total_extent_bytes; } + // For 3D-as-2D wrappers: the host texture is 2D but we need 3D tiling + // when loading from guest memory. + bool force_load_3d_tiling() const { return force_load_3d_tiling_; } + void SetForceLoad3DTiling(bool force) { force_load_3d_tiling_ = force; } + uint64_t GetHostMemoryUsage() const { return host_memory_usage_; } uint64_t last_usage_submission_index() const { @@ -323,6 +328,10 @@ class TextureCache { // Set to false via constructor for wrapper textures. bool in_usage_list_; + // For 3D-as-2D wrappers: use 3D tiling when loading even though the host + // texture is 2D. + bool force_load_3d_tiling_ = false; + // Whether the most up-to-date base / mips contain pages with data from a // resolve operation (rather than from the CPU or memexport), primarily for // choosing between piecewise linear gamma and sRGB when the former is diff --git a/src/xenia/gpu/vulkan/deferred_command_buffer.cc b/src/xenia/gpu/vulkan/deferred_command_buffer.cc index b533b625d..0ddfef765 100644 --- a/src/xenia/gpu/vulkan/deferred_command_buffer.cc +++ b/src/xenia/gpu/vulkan/deferred_command_buffer.cc @@ -175,16 +175,6 @@ void DeferredCommandBuffer::Execute(VkCommandBuffer command_buffer) { args.filter); } break; - case Command::kVkCopyImage: { - auto& args = *reinterpret_cast(stream); - dfn.vkCmdCopyImage( - command_buffer, args.src_image, args.src_image_layout, - args.dst_image, args.dst_image_layout, args.region_count, - reinterpret_cast( - reinterpret_cast(stream) + - xe::align(sizeof(ArgsVkCopyImage), alignof(VkImageCopy)))); - } break; - case Command::kVkDispatch: { auto& args = *reinterpret_cast(stream); dfn.vkCmdDispatch(command_buffer, args.group_count_x, diff --git a/src/xenia/gpu/vulkan/deferred_command_buffer.h b/src/xenia/gpu/vulkan/deferred_command_buffer.h index 8352e06d4..4fef1ee6f 100644 --- a/src/xenia/gpu/vulkan/deferred_command_buffer.h +++ b/src/xenia/gpu/vulkan/deferred_command_buffer.h @@ -260,32 +260,6 @@ class DeferredCommandBuffer { regions, sizeof(VkImageBlit) * region_count); } - VkImageCopy* CmdCopyImageEmplace(VkImage src_image, - VkImageLayout src_image_layout, - VkImage dst_image, - VkImageLayout dst_image_layout, - uint32_t region_count) { - const size_t header_size = - xe::align(sizeof(ArgsVkCopyImage), alignof(VkImageCopy)); - uint8_t* args_ptr = reinterpret_cast( - WriteCommand(Command::kVkCopyImage, - header_size + sizeof(VkImageCopy) * region_count)); - auto& args = *reinterpret_cast(args_ptr); - args.src_image = src_image; - args.src_image_layout = src_image_layout; - args.dst_image = dst_image; - args.dst_image_layout = dst_image_layout; - args.region_count = region_count; - return reinterpret_cast(args_ptr + header_size); - } - void CmdVkCopyImage(VkImage src_image, VkImageLayout src_image_layout, - VkImage dst_image, VkImageLayout dst_image_layout, - uint32_t region_count, const VkImageCopy* regions) { - std::memcpy(CmdCopyImageEmplace(src_image, src_image_layout, dst_image, - dst_image_layout, region_count), - regions, sizeof(VkImageCopy) * region_count); - } - void CmdVkDispatch(uint32_t group_count_x, uint32_t group_count_y, uint32_t group_count_z) { auto& args = *reinterpret_cast( @@ -424,7 +398,6 @@ class DeferredCommandBuffer { kVkCopyBuffer, kVkCopyBufferToImage, kVkBlitImage, - kVkCopyImage, kVkDispatch, kVkDraw, kVkDrawIndexed, @@ -531,16 +504,6 @@ class DeferredCommandBuffer { static_assert(alignof(VkImageBlit) <= alignof(uintmax_t)); }; - struct ArgsVkCopyImage { - VkImage src_image; - VkImageLayout src_image_layout; - VkImage dst_image; - VkImageLayout dst_image_layout; - uint32_t region_count; - // Followed by aligned VkImageCopy[]. - static_assert(alignof(VkImageCopy) <= alignof(uintmax_t)); - }; - struct ArgsVkDispatch { uint32_t group_count_x; uint32_t group_count_y; diff --git a/src/xenia/gpu/vulkan/vulkan_texture_cache.cc b/src/xenia/gpu/vulkan/vulkan_texture_cache.cc index 79d581f90..cc1b5b4f8 100644 --- a/src/xenia/gpu/vulkan/vulkan_texture_cache.cc +++ b/src/xenia/gpu/vulkan/vulkan_texture_cache.cc @@ -1101,8 +1101,7 @@ std::unique_ptr VulkanTextureCache::CreateTexture( VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // For scaled resolve textures with mips, we need transfer source to generate // mip levels via blit from the base level. - // For 3D textures, we need transfer source to support 3D-to-2D conversion. - if ((key.scaled_resolve && key.mip_max_level > 0) || is_3d) { + if (key.scaled_resolve && key.mip_max_level > 0) { image_create_info.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; } image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; @@ -1170,7 +1169,11 @@ bool VulkanTextureCache::LoadTextureDataFromResidentMemoryImpl(Texture& texture, const texture_util::TextureGuestLayout& guest_layout = vulkan_texture.guest_layout(); xenos::DataDimension dimension = texture_key.dimension; + // Whether the host image is 3D (determines depth vs array layer layout). bool is_3d = dimension == xenos::DataDimension::k3D; + // Whether to use 3D tiling when reading from guest memory. + // For 3D-as-2D wrappers, the host image is 2D but we need 3D tiling. + bool is_3d_tiling = is_3d || vulkan_texture.force_load_3d_tiling(); uint32_t width = texture_key.GetWidth(); uint32_t height = texture_key.GetHeight(); uint32_t depth_or_array_size = texture_key.GetDepthOrArraySize(); @@ -1476,7 +1479,7 @@ bool VulkanTextureCache::LoadTextureDataFromResidentMemoryImpl(Texture& texture, assert_true(texture_resolution_scale_x <= 7); assert_true(texture_resolution_scale_y <= 7); load_constants.is_tiled_3d_endian_scale = - uint32_t(texture_key.tiled) | (uint32_t(is_3d) << 1) | + uint32_t(texture_key.tiled) | (uint32_t(is_3d_tiling) << 1) | (uint32_t(texture_key.endianness) << 2) | (texture_resolution_scale_x << 4) | (texture_resolution_scale_y << 7); @@ -1911,10 +1914,7 @@ VkImageView VulkanTextureCache::VulkanTexture::GetView(bool is_signed, VkImageView VulkanTextureCache::VulkanTexture::GetOrCreate3DAs2DImageView( bool is_signed, uint32_t host_swizzle) { - int32_t mode = cvars::gpu_3d_to_2d_texture_mode; - - // Mode 0: Feature disabled. - if (mode == 0) { + if (!cvars::gpu_3d_to_2d_texture) { return VK_NULL_HANDLE; } @@ -1927,10 +1927,6 @@ VkImageView VulkanTextureCache::VulkanTexture::GetOrCreate3DAs2DImageView( VulkanTextureCache& vulkan_texture_cache = static_cast(texture_cache()); - const ui::vulkan::VulkanDevice* const vulkan_device = - vulkan_texture_cache.command_processor_.GetVulkanDevice(); - const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions(); - const VkDevice device = vulkan_device->device(); // Create the 2D texture wrapper if it doesn't exist. if (!texture_3d_as_2d_) { @@ -1970,142 +1966,49 @@ VkImageView VulkanTextureCache::VulkanTexture::GetOrCreate3DAs2DImageView( } // Create a modified key for the 2D wrapper with depth=1 and - // mip_max_level=0. Keep dimension as k3D so that in Mode 2, LoadTextureData - // uses 3D tiling math to correctly read slice 0 from the 3D-tiled guest - // memory. + // mip_max_level=0. Keep dimension as k3D so guest layout uses 3D tiling + // math to correctly read slice 0 from the 3D-tiled guest memory. TextureKey key_2d = key(); key_2d.depth_or_array_size_minus_1 = 0; key_2d.mip_max_level = 0; - if (mode == 1) { - // Mode 1: GPU copy - copy slice 0 from the 3D image to the 2D image. - DeferredCommandBuffer& command_buffer = - vulkan_texture_cache.command_processor_.deferred_command_buffer(); + // Create the wrapper first so LoadTextureData can work with it. + texture_3d_as_2d_.reset(new VulkanTexture(vulkan_texture_cache, key_2d, + image_2d, allocation_2d, false)); - // Get the current layout from usage tracking (like D3D12 does). - VkPipelineStageFlags src_stage_mask; - VkAccessFlags src_access_mask; - VkImageLayout src_old_layout; - vulkan_texture_cache.GetTextureUsageMasks( - usage_, src_stage_mask, src_access_mask, src_old_layout); - - // Transition 2D image to transfer destination. - VkImageMemoryBarrier barrier_2d_to_dst = {}; - barrier_2d_to_dst.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier_2d_to_dst.srcAccessMask = 0; - barrier_2d_to_dst.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier_2d_to_dst.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - barrier_2d_to_dst.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - barrier_2d_to_dst.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier_2d_to_dst.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier_2d_to_dst.image = image_2d; - barrier_2d_to_dst.subresourceRange = - ui::vulkan::util::InitializeSubresourceRange(); - command_buffer.CmdVkPipelineBarrier( - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, - 0, nullptr, 0, nullptr, 1, &barrier_2d_to_dst); - - // Transition 3D image to transfer source. - VkImageMemoryBarrier barrier_3d_to_src = {}; - barrier_3d_to_src.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier_3d_to_src.srcAccessMask = src_access_mask; - barrier_3d_to_src.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - barrier_3d_to_src.oldLayout = src_old_layout; - barrier_3d_to_src.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - barrier_3d_to_src.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier_3d_to_src.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier_3d_to_src.image = image_; - barrier_3d_to_src.subresourceRange = - ui::vulkan::util::InitializeSubresourceRange(); - command_buffer.CmdVkPipelineBarrier( - src_stage_mask, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, - nullptr, 1, &barrier_3d_to_src); - - // Use vkCmdCopyImage for the slice copy. - // This works for all formats including compressed (BC) formats. - VkImageCopy copy_region = {}; - copy_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - copy_region.srcSubresource.mipLevel = 0; - copy_region.srcSubresource.baseArrayLayer = 0; - copy_region.srcSubresource.layerCount = 1; - copy_region.srcOffset = {0, 0, 0}; - copy_region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - copy_region.dstSubresource.mipLevel = 0; - copy_region.dstSubresource.baseArrayLayer = 0; - copy_region.dstSubresource.layerCount = 1; - copy_region.dstOffset = {0, 0, 0}; - copy_region.extent.width = key().GetWidth(); - copy_region.extent.height = key().GetHeight(); - copy_region.extent.depth = 1; - command_buffer.CmdVkCopyImage( - image_, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image_2d, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); - - // Transition 3D image back to guest shader sampled state. - VkPipelineStageFlags dst_stage_mask; - VkAccessFlags dst_access_mask; - VkImageLayout new_layout; - vulkan_texture_cache.GetTextureUsageMasks(Usage::kGuestShaderSampled, - dst_stage_mask, dst_access_mask, - new_layout); - barrier_3d_to_src.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - barrier_3d_to_src.dstAccessMask = dst_access_mask; - barrier_3d_to_src.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - barrier_3d_to_src.newLayout = new_layout; - command_buffer.CmdVkPipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, - dst_stage_mask, 0, 0, nullptr, 0, - nullptr, 1, &barrier_3d_to_src); - // Update tracking - texture is now in guest shader sampled state. - SetUsage(Usage::kGuestShaderSampled); - - // Transition 2D image to shader read. - barrier_2d_to_dst.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier_2d_to_dst.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - barrier_2d_to_dst.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - barrier_2d_to_dst.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - command_buffer.CmdVkPipelineBarrier( - VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - 0, 0, nullptr, 0, nullptr, 1, &barrier_2d_to_dst); - - // Create the wrapper after successful GPU copy. - texture_3d_as_2d_.reset(new VulkanTexture( - vulkan_texture_cache, key_2d, image_2d, allocation_2d, false)); - texture_3d_as_2d_->SetUsage(Usage::kGuestShaderSampled); - } else { - // Mode 2: CPU re-upload - reload texture data from guest memory. - // Create the wrapper first so LoadTextureData can work with it. - texture_3d_as_2d_.reset(new VulkanTexture( - vulkan_texture_cache, key_2d, image_2d, allocation_2d, false)); - - if (!vulkan_texture_cache.LoadTextureData(*texture_3d_as_2d_)) { - XELOGE("VulkanTexture: Failed to untile 3D-as-2D data"); - texture_3d_as_2d_.reset(); - return VK_NULL_HANDLE; - } - - // LoadTextureData leaves the texture in TRANSFER_DST state. - // Transition to shader read state. - DeferredCommandBuffer& command_buffer = - vulkan_texture_cache.command_processor_.deferred_command_buffer(); - VkImageMemoryBarrier barrier_to_shader = {}; - barrier_to_shader.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier_to_shader.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier_to_shader.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - barrier_to_shader.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - barrier_to_shader.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - barrier_to_shader.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier_to_shader.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier_to_shader.image = texture_3d_as_2d_->image(); - barrier_to_shader.subresourceRange = - ui::vulkan::util::InitializeSubresourceRange(); - command_buffer.CmdVkPipelineBarrier( - VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - 0, 0, nullptr, 0, nullptr, 1, &barrier_to_shader); - texture_3d_as_2d_->SetUsage(Usage::kGuestShaderSampled); + if (!vulkan_texture_cache.LoadTextureData(*texture_3d_as_2d_)) { + XELOGE("VulkanTexture: Failed to load 3D-as-2D texture data"); + texture_3d_as_2d_.reset(); + return VK_NULL_HANDLE; } + + // LoadTextureData leaves the texture in TRANSFER_DST state. + // Transition to shader read state. + DeferredCommandBuffer& command_buffer = + vulkan_texture_cache.command_processor_.deferred_command_buffer(); + VkImageMemoryBarrier barrier_to_shader = {}; + barrier_to_shader.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier_to_shader.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier_to_shader.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + barrier_to_shader.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier_to_shader.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier_to_shader.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier_to_shader.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier_to_shader.image = texture_3d_as_2d_->image(); + barrier_to_shader.subresourceRange = + ui::vulkan::util::InitializeSubresourceRange(); + command_buffer.CmdVkPipelineBarrier( + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + 0, 0, nullptr, 0, nullptr, 1, &barrier_to_shader); + texture_3d_as_2d_->SetUsage(Usage::kGuestShaderSampled); } // Create the image view. + const ui::vulkan::VulkanDevice* const vulkan_device = + vulkan_texture_cache.command_processor_.GetVulkanDevice(); + const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions(); + const VkDevice device = vulkan_device->device(); + const HostFormatPair& host_format_pair = vulkan_texture_cache.GetHostFormatPair(key()); VkFormat format = (is_signed ? host_format_pair.format_signed @@ -3062,6 +2965,9 @@ void VulkanTextureCache::GetTextureUsageMasks(VulkanTexture::Usage usage, layout = VK_IMAGE_LAYOUT_UNDEFINED; switch (usage) { case VulkanTexture::Usage::kUndefined: + // For UNDEFINED layout, use TOP_OF_PIPE as source stage (wait for + // nothing) with no access mask (discarding old contents). + stage_mask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; break; case VulkanTexture::Usage::kTransferDestination: stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT; diff --git a/src/xenia/gpu/vulkan/vulkan_texture_cache.h b/src/xenia/gpu/vulkan/vulkan_texture_cache.h index 00319ddbf..ea77d8c1e 100644 --- a/src/xenia/gpu/vulkan/vulkan_texture_cache.h +++ b/src/xenia/gpu/vulkan/vulkan_texture_cache.h @@ -337,10 +337,8 @@ class VulkanTextureCache final : public TextureCache { std::unordered_map views_; - // For 3D textures sampled as 2D - cached 2D copy of slice 0. - // This is a wrapper around the 2D image with a modified key (depth=1). - // For Mode 1 (GPU copy), the wrapper is created after the copy. - // For Mode 2 (CPU re-upload), LoadTextureData is called on the wrapper. + // For 3D textures sampled as 2D - cached 2D texture loaded from slice 0. + // Uses a modified key (depth=1) with 3D tiling to read from guest memory. std::unique_ptr texture_3d_as_2d_; VkImageView image_view_3d_as_2d_unsigned_ = VK_NULL_HANDLE; VkImageView image_view_3d_as_2d_signed_ = VK_NULL_HANDLE; diff --git a/src/xenia/ui/vulkan/functions/device_1_0.inc b/src/xenia/ui/vulkan/functions/device_1_0.inc index b0c36538c..c2c251131 100644 --- a/src/xenia/ui/vulkan/functions/device_1_0.inc +++ b/src/xenia/ui/vulkan/functions/device_1_0.inc @@ -15,7 +15,6 @@ XE_UI_VULKAN_FUNCTION(vkCmdClearAttachments) XE_UI_VULKAN_FUNCTION(vkCmdClearColorImage) XE_UI_VULKAN_FUNCTION(vkCmdCopyBuffer) XE_UI_VULKAN_FUNCTION(vkCmdCopyBufferToImage) -XE_UI_VULKAN_FUNCTION(vkCmdCopyImage) XE_UI_VULKAN_FUNCTION(vkCmdCopyImageToBuffer) XE_UI_VULKAN_FUNCTION(vkCmdDispatch) XE_UI_VULKAN_FUNCTION(vkCmdDraw)