[GPU] 3d-to-2d textures default enable
Replaced the modal cvar with bool gpu_3d_to_2d_texture with "mode 2" now enabled by default and "mode 1" removed as unnecessary
This commit is contained in:
@@ -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<UINT>(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<D3D12Texture> 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.
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -175,16 +175,6 @@ void DeferredCommandBuffer::Execute(VkCommandBuffer command_buffer) {
|
||||
args.filter);
|
||||
} break;
|
||||
|
||||
case Command::kVkCopyImage: {
|
||||
auto& args = *reinterpret_cast<const ArgsVkCopyImage*>(stream);
|
||||
dfn.vkCmdCopyImage(
|
||||
command_buffer, args.src_image, args.src_image_layout,
|
||||
args.dst_image, args.dst_image_layout, args.region_count,
|
||||
reinterpret_cast<const VkImageCopy*>(
|
||||
reinterpret_cast<const uint8_t*>(stream) +
|
||||
xe::align(sizeof(ArgsVkCopyImage), alignof(VkImageCopy))));
|
||||
} break;
|
||||
|
||||
case Command::kVkDispatch: {
|
||||
auto& args = *reinterpret_cast<const ArgsVkDispatch*>(stream);
|
||||
dfn.vkCmdDispatch(command_buffer, args.group_count_x,
|
||||
|
||||
@@ -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<uint8_t*>(
|
||||
WriteCommand(Command::kVkCopyImage,
|
||||
header_size + sizeof(VkImageCopy) * region_count));
|
||||
auto& args = *reinterpret_cast<ArgsVkCopyImage*>(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<VkImageCopy*>(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<ArgsVkDispatch*>(
|
||||
@@ -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;
|
||||
|
||||
@@ -1101,8 +1101,7 @@ std::unique_ptr<TextureCache::Texture> 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<VulkanTextureCache&>(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;
|
||||
|
||||
@@ -337,10 +337,8 @@ class VulkanTextureCache final : public TextureCache {
|
||||
|
||||
std::unordered_map<ViewKey, VkImageView, ViewKey::Hasher> 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<VulkanTexture> texture_3d_as_2d_;
|
||||
VkImageView image_view_3d_as_2d_unsigned_ = VK_NULL_HANDLE;
|
||||
VkImageView image_view_3d_as_2d_signed_ = VK_NULL_HANDLE;
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user