diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 196f93a453..ac48ab5e34 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -821,20 +821,6 @@ void BufferCache
::BindHostIndexBuffer() {
const u32 size = channel_state->index_buffer.size;
const auto& draw_state = maxwell3d->draw_manager.draw_state;
if (draw_state.inline_index_draw_indexes.empty()) {
- if constexpr (USE_UNIFIED_MEMORY && !HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
- const auto window =
- TryResolveUnifiedRange(channel_state->index_buffer.device_addr, size);
- if (window && runtime.IsUnifiedIndexRange(draw_state.topology,
- draw_state.index_buffer.format,
- window->offset)) {
- runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format,
- draw_state.index_buffer.first,
- draw_state.index_buffer.count,
- runtime.UnifiedWindowBuffer(window->window),
- static_cast(window->offset), size);
- return;
- }
- }
SynchronizeBuffer(buffer, channel_state->index_buffer.device_addr, size);
} else {
if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) {
@@ -888,7 +874,6 @@ void BufferCache::UpdateVertexBufferSlot(u32 index, const Binding& binding) {
enabled_vertex_buffers_mask |= (1u << index);
} else {
enabled_vertex_buffers_mask &= ~(1u << index);
- unified_vertex_buffers_mask &= ~(1u << index);
}
}
@@ -921,39 +906,15 @@ void BufferCache
::BindHostVertexBuffers() {
const Binding& binding = VertexBufferSlot(index);
Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer, binding.buffer_id);
- bool needs_bind = flags[Dirty::VertexBuffer0 + index];
- u32 unified_window = NO_UNIFIED_WINDOW;
- u64 unified_offset = 0;
- if constexpr (USE_UNIFIED_MEMORY) {
- const bool was_unified = ((unified_vertex_buffers_mask >> index) & 1) != 0;
- if (needs_bind || was_unified) {
- const auto window = TryResolveUnifiedRange(binding.device_addr, binding.size);
- if (window) {
- unified_window = static_cast(window->window);
- unified_offset = window->offset;
- }
- }
- if (was_unified && unified_window == NO_UNIFIED_WINDOW) {
- needs_bind = true;
- }
- }
- if (unified_window == NO_UNIFIED_WINDOW) {
- SynchronizeBuffer(buffer, binding.device_addr, binding.size);
- }
- if (!needs_bind) {
+ SynchronizeBuffer(buffer, binding.device_addr, binding.size);
+ if (!flags[Dirty::VertexBuffer0 + index]) {
flush_bindings();
continue;
}
flags[Dirty::VertexBuffer0 + index] = false;
const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
- u32 offset = static_cast(unified_offset);
- if (unified_window == NO_UNIFIED_WINDOW) {
- offset = buffer.Offset(binding.device_addr);
- buffer.MarkUsage(offset, binding.size);
- unified_vertex_buffers_mask &= ~(1u << index);
- } else {
- unified_vertex_buffers_mask |= 1u << index;
- }
+ const u32 offset = buffer.Offset(binding.device_addr);
+ buffer.MarkUsage(offset, binding.size);
if (!bindings.buffers.empty() && index != last_index + 1) {
flush_bindings();
}
@@ -964,7 +925,6 @@ void BufferCache::BindHostVertexBuffers() {
bindings.offsets.push_back(offset);
bindings.sizes.push_back(binding.size);
bindings.strides.push_back(stride);
- bindings.unified_windows.push_back(unified_window);
last_index = index;
}
flush_bindings();
@@ -972,31 +932,12 @@ void BufferCache
::BindHostVertexBuffers() {
HostBindings host_bindings;
bool any_valid{false};
auto& flags = maxwell3d->dirty.flags;
- std::array unified_windows;
- std::array unified_offsets{};
- unified_windows.fill(NO_UNIFIED_WINDOW);
for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
const Binding& binding = channel_state->vertex_buffers[index];
Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer, binding.buffer_id);
- bool needs_bind = flags[Dirty::VertexBuffer0 + index];
- if constexpr (USE_UNIFIED_MEMORY) {
- const bool was_unified = ((unified_vertex_buffers_mask >> index) & 1) != 0;
- if (needs_bind || was_unified) {
- const auto window = TryResolveUnifiedRange(binding.device_addr, binding.size);
- if (window) {
- unified_windows[index] = static_cast(window->window);
- unified_offsets[index] = window->offset;
- }
- }
- if (was_unified && unified_windows[index] == NO_UNIFIED_WINDOW) {
- needs_bind = true;
- }
- }
- if (unified_windows[index] == NO_UNIFIED_WINDOW) {
- SynchronizeBuffer(buffer, binding.device_addr, binding.size);
- }
- if (!needs_bind) {
+ SynchronizeBuffer(buffer, binding.device_addr, binding.size);
+ if (!flags[Dirty::VertexBuffer0 + index]) {
continue;
}
flags[Dirty::VertexBuffer0 + index] = false;
@@ -1015,20 +956,13 @@ void BufferCache::BindHostVertexBuffers() {
Buffer& buffer = slot_buffers[binding.buffer_id];
const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
- u32 offset = static_cast(unified_offsets[index]);
- if (unified_windows[index] == NO_UNIFIED_WINDOW) {
- offset = buffer.Offset(binding.device_addr);
- buffer.MarkUsage(offset, binding.size);
- unified_vertex_buffers_mask &= ~(1u << index);
- } else {
- unified_vertex_buffers_mask |= 1u << index;
- }
+ const u32 offset = buffer.Offset(binding.device_addr);
+ buffer.MarkUsage(offset, binding.size);
host_bindings.buffers.push_back(&buffer);
host_bindings.offsets.push_back(offset);
host_bindings.sizes.push_back(binding.size);
host_bindings.strides.push_back(stride);
- host_bindings.unified_windows.push_back(unified_windows[index]);
}
runtime.BindVertexBuffers(host_bindings);
}
diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h
index 581f0d3b3f..b84cd2709c 100644
--- a/src/video_core/buffer_cache/buffer_cache_base.h
+++ b/src/video_core/buffer_cache/buffer_cache_base.h
@@ -10,7 +10,6 @@
#include
#include
#include
-#include
#include
#include
#include
@@ -97,15 +96,12 @@ static constexpr Binding NULL_BINDING{
.buffer_id = NULL_BUFFER_ID,
};
-static constexpr u32 NO_UNIFIED_WINDOW = (std::numeric_limits::max)();
-
template
struct HostBindings {
boost::container::static_vector buffers;
boost::container::static_vector offsets;
boost::container::static_vector sizes;
boost::container::static_vector strides;
- boost::container::static_vector unified_windows;
u32 min_index{NUM_VERTEX_BUFFERS};
u32 max_index{0};
};
@@ -509,7 +505,6 @@ private:
u32 last_index_count = 0;
u32 enabled_vertex_buffers_mask = 0;
- u32 unified_vertex_buffers_mask = 0;
u64 vertex_buffers_serial = 0;
std::array v_buffer{};
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index c4e7ac3f6f..f8b64c57e8 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -17,12 +17,14 @@ set(SHADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/astc_decoder.comp
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_float.frag
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d.comp
+ ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d_buffer.comp
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth.frag
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_stencil_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d.comp
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_bcn.comp
+ ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_buffer.comp
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d24s8.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d32f.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_d32f_to_abgr8.frag
diff --git a/src/video_core/host_shaders/block_linear_unswizzle_2d_buffer.comp b/src/video_core/host_shaders/block_linear_unswizzle_2d_buffer.comp
new file mode 100644
index 0000000000..3fc71e05b6
--- /dev/null
+++ b/src/video_core/host_shaders/block_linear_unswizzle_2d_buffer.comp
@@ -0,0 +1,104 @@
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#version 430
+
+#extension GL_EXT_shader_16bit_storage : require
+#extension GL_EXT_shader_8bit_storage : require
+
+#define BINDING_INPUT_BUFFER 0
+#define BINDING_OUTPUT_BUFFER 1
+
+layout(push_constant) uniform PushConstants {
+ uvec3 dim;
+ uint bytes_per_block_log2;
+
+ uvec3 origin;
+ uint layer_stride;
+
+ uint block_size;
+ uint x_shift;
+ uint block_height;
+ uint block_height_mask;
+} pc;
+
+layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; };
+layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; };
+layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; };
+
+layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer {
+ uint out_u32[];
+};
+
+layout(local_size_x = 16, local_size_y = 8, local_size_z = 1) in;
+
+const uint GOB_SIZE_X = 64;
+const uint GOB_SIZE_Y = 8;
+
+const uint GOB_SIZE_X_SHIFT = 6;
+const uint GOB_SIZE_Y_SHIFT = 3;
+const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT;
+
+const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u);
+
+uint SwizzleTable(uint pos) {
+ const uint t[8] = uint[](
+ 0x12100200, 0x13110301, 0x16140604, 0x17150705,
+ 0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
+ );
+ const uint i = pos >> 4;
+ const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
+ return (h << 4) | (pos & 0xf);
+}
+
+uint SwizzleOffset(uvec2 pos) {
+ pos = pos & SWIZZLE_MASK;
+ return SwizzleTable(pos.y * 64u + pos.x);
+}
+
+uvec4 ReadTexel(uint offset) {
+ switch (pc.bytes_per_block_log2) {
+ case 2u:
+ return uvec4(u32data[offset / 4u], 0u, 0u, 0u);
+ case 3u:
+ return uvec4(u64data[offset / 8u], 0u, 0u);
+ case 4u:
+ return u128data[offset / 16u];
+ }
+ return uvec4(0u);
+}
+
+void main() {
+ uvec3 coord = gl_GlobalInvocationID;
+ if (coord.x >= pc.dim.x || coord.y >= pc.dim.y || coord.z >= pc.dim.z) {
+ return;
+ }
+
+ uvec3 pos = coord + pc.origin;
+ pos.x <<= pc.bytes_per_block_log2;
+
+ uint swizzle = SwizzleOffset(pos.xy);
+ uint block_y = pos.y >> GOB_SIZE_Y_SHIFT;
+
+ uint offset = 0u;
+ offset += pos.z * pc.layer_stride;
+ offset += (block_y >> pc.block_height) * pc.block_size;
+ offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT;
+ offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift;
+ offset += swizzle;
+
+ uvec4 texel = ReadTexel(offset);
+
+ uint words = 1u << (pc.bytes_per_block_log2 - 2u);
+ uint linear_index = coord.x + coord.y * pc.dim.x + coord.z * pc.dim.x * pc.dim.y;
+ uint out_idx = linear_index * words;
+
+ out_u32[out_idx] = texel.x;
+ if (words > 1u) {
+ out_u32[out_idx + 1u] = texel.y;
+ }
+ if (words > 2u) {
+ out_u32[out_idx + 2u] = texel.z;
+ out_u32[out_idx + 3u] = texel.w;
+ }
+}
diff --git a/src/video_core/host_shaders/block_linear_unswizzle_3d_buffer.comp b/src/video_core/host_shaders/block_linear_unswizzle_3d_buffer.comp
new file mode 100644
index 0000000000..7333568eb7
--- /dev/null
+++ b/src/video_core/host_shaders/block_linear_unswizzle_3d_buffer.comp
@@ -0,0 +1,105 @@
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#version 430
+
+#define BINDING_INPUT_BUFFER 0
+#define BINDING_OUTPUT_BUFFER 1
+
+layout(push_constant) uniform PushConstants {
+ uvec3 dim;
+ uint bytes_per_block_log2;
+
+ uvec3 origin;
+ uint slice_size;
+
+ uint block_size;
+ uint x_shift;
+ uint block_height;
+ uint block_height_mask;
+
+ uint block_depth;
+ uint block_depth_mask;
+} pc;
+
+layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; };
+layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; };
+layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; };
+
+layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer {
+ uint out_u32[];
+};
+
+layout(local_size_x = 8, local_size_y = 8, local_size_z = 4) in;
+
+const uint GOB_SIZE_X = 64;
+const uint GOB_SIZE_Y = 8;
+
+const uint GOB_SIZE_X_SHIFT = 6;
+const uint GOB_SIZE_Y_SHIFT = 3;
+const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT;
+
+const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u);
+
+uint SwizzleTable(uint pos) {
+ const uint t[8] = uint[](
+ 0x12100200, 0x13110301, 0x16140604, 0x17150705,
+ 0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
+ );
+ const uint i = pos >> 4;
+ const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
+ return (h << 4) | (pos & 0xf);
+}
+
+uint SwizzleOffset(uvec2 pos) {
+ pos = pos & SWIZZLE_MASK;
+ return SwizzleTable(pos.y * 64u + pos.x);
+}
+
+uvec4 ReadTexel(uint offset) {
+ switch (pc.bytes_per_block_log2) {
+ case 2u:
+ return uvec4(u32data[offset / 4u], 0u, 0u, 0u);
+ case 3u:
+ return uvec4(u64data[offset / 8u], 0u, 0u);
+ case 4u:
+ return u128data[offset / 16u];
+ }
+ return uvec4(0u);
+}
+
+void main() {
+ uvec3 coord = gl_GlobalInvocationID;
+ if (coord.x >= pc.dim.x || coord.y >= pc.dim.y || coord.z >= pc.dim.z) {
+ return;
+ }
+
+ uvec3 pos = coord + pc.origin;
+ pos.x <<= pc.bytes_per_block_log2;
+
+ uint swizzle = SwizzleOffset(pos.xy);
+ uint block_y = pos.y >> GOB_SIZE_Y_SHIFT;
+
+ uint offset = 0u;
+ offset += (pos.z >> pc.block_depth) * pc.slice_size;
+ offset += (pos.z & pc.block_depth_mask) << (GOB_SIZE_SHIFT + pc.block_height);
+ offset += (block_y >> pc.block_height) * pc.block_size;
+ offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT;
+ offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift;
+ offset += swizzle;
+
+ uvec4 texel = ReadTexel(offset);
+
+ uint words = 1u << (pc.bytes_per_block_log2 - 2u);
+ uint linear_index = coord.x + coord.y * pc.dim.x + coord.z * pc.dim.x * pc.dim.y;
+ uint out_idx = linear_index * words;
+
+ out_u32[out_idx] = texel.x;
+ if (words > 1u) {
+ out_u32[out_idx + 1u] = texel.y;
+ }
+ if (words > 2u) {
+ out_u32[out_idx + 2u] = texel.z;
+ out_u32[out_idx + 3u] = texel.w;
+ }
+}
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index c5ea286762..0b93b081f5 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -727,25 +727,6 @@ void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t si
});
}
-bool BufferCacheRuntime::IsUnifiedIndexRange(PrimitiveTopology topology, IndexFormat index_format,
- u64 offset) const {
- const VkIndexType vk_index_type = MaxwellToVK::IndexFormat(index_format);
- const bool needs_uint8_pass =
- vk_index_type == VK_INDEX_TYPE_UINT8_EXT && !device.IsExtIndexTypeUint8Supported();
- if (topology == PrimitiveTopology::Quads || topology == PrimitiveTopology::QuadStrip ||
- needs_uint8_pass) {
- return (offset % device.GetStorageBufferAlignment()) == 0;
- }
- switch (vk_index_type) {
- case VK_INDEX_TYPE_UINT32:
- return (offset % 4) == 0;
- case VK_INDEX_TYPE_UINT16:
- return (offset % 2) == 0;
- default:
- return true;
- }
-}
-
void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat index_format,
u32 base_vertex, u32 num_indices, VkBuffer buffer,
u32 offset, [[maybe_unused]] u32 size) {
@@ -821,11 +802,6 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bindings) {
boost::container::static_vector buffer_handles(bindings.buffers.size());
for (u32 i = 0; i < bindings.buffers.size(); ++i) {
- if (i < bindings.unified_windows.size() &&
- bindings.unified_windows[i] != VideoCommon::NO_UNIFIED_WINDOW) {
- buffer_handles[i] = unified_memory->GetWindowBuffer(bindings.unified_windows[i]);
- continue;
- }
auto handle = bindings.buffers[i]->Handle();
if (handle == VK_NULL_HANDLE) {
bindings.offsets[i] = 0;
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index a15afcc03d..73649bfeb8 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -218,9 +218,6 @@ public:
(offset % device.GetStorageBufferAlignment()) == 0;
}
- [[nodiscard]] bool IsUnifiedIndexRange(PrimitiveTopology topology, IndexFormat index_format,
- u64 offset) const;
-
void BindTextureBuffer(Buffer& buffer, u32 offset, u32 size,
VideoCore::Surface::PixelFormat format) {
guest_descriptor_queue.AddTexelBuffer(buffer.View(offset, size, format),
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
index 80c434dc8f..77ccdcd16f 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
@@ -22,7 +22,9 @@
#include "video_core/host_shaders/resolve_conditional_render_comp_spv.h"
#include "video_core/host_shaders/vulkan_quad_indexed_comp_spv.h"
#include "video_core/host_shaders/vulkan_uint8_comp_spv.h"
+#include "video_core/host_shaders/block_linear_unswizzle_2d_buffer_comp_spv.h"
#include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h"
+#include "video_core/host_shaders/block_linear_unswizzle_3d_buffer_comp_spv.h"
#include "video_core/renderer_vulkan/vk_compute_pass.h"
#include "video_core/surface.h"
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
@@ -872,4 +874,480 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
});
}
+namespace {
+constexpr u32 BL2D_BINDING_INPUT_BUFFER = 0;
+constexpr u32 BL2D_BINDING_OUTPUT_BUFFER = 1;
+
+struct alignas(16) BlockLinearUnswizzle2DPushConstants {
+ std::array dim;
+ u32 bytes_per_block_log2;
+ std::array origin;
+ u32 layer_stride;
+ u32 block_size;
+ u32 x_shift;
+ u32 block_height;
+ u32 block_height_mask;
+};
+static_assert(sizeof(BlockLinearUnswizzle2DPushConstants) <= 128);
+
+constexpr std::array BL2D_BINDINGS{{
+ {
+ .binding = BL2D_BINDING_INPUT_BUFFER,
+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+ .descriptorCount = 1,
+ .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
+ .pImmutableSamplers = nullptr,
+ },
+ {
+ .binding = BL2D_BINDING_OUTPUT_BUFFER,
+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+ .descriptorCount = 1,
+ .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
+ .pImmutableSamplers = nullptr,
+ },
+}};
+
+constexpr std::array BL2D_TEMPLATE{{
+ {
+ .dstBinding = BL2D_BINDING_INPUT_BUFFER,
+ .dstArrayElement = 0,
+ .descriptorCount = 1,
+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+ .offset = BL2D_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
+ .stride = sizeof(DescriptorUpdateEntry),
+ },
+ {
+ .dstBinding = BL2D_BINDING_OUTPUT_BUFFER,
+ .dstArrayElement = 0,
+ .descriptorCount = 1,
+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+ .offset = BL2D_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry),
+ .stride = sizeof(DescriptorUpdateEntry),
+ },
+}};
+
+constexpr DescriptorBankInfo BL2D_BANK_INFO{
+ .uniform_buffers = 0,
+ .storage_buffers = 2,
+ .texture_buffers = 0,
+ .image_buffers = 0,
+ .textures = 0,
+ .images = 0,
+ .score = 2,
+};
+} // Anonymous namespace
+
+BlockLinearUnswizzle2DPass::BlockLinearUnswizzle2DPass(
+ const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
+ StagingBufferPool& staging_buffer_pool_,
+ ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
+ : ComputePass(device_, scheduler_, descriptor_pool_, BL2D_BINDINGS, BL2D_TEMPLATE,
+ BL2D_BANK_INFO,
+ COMPUTE_PUSH_CONSTANT_RANGE,
+ BLOCK_LINEAR_UNSWIZZLE_2D_BUFFER_COMP_SPV),
+ scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
+ compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
+
+BlockLinearUnswizzle2DPass::~BlockLinearUnswizzle2DPass() = default;
+
+bool BlockLinearUnswizzle2DPass::IsSupported(const Device& device,
+ const VideoCommon::ImageInfo& info) {
+ if (info.type != VideoCommon::ImageType::e2D) {
+ return false;
+ }
+ if (info.resources.levels != 1 || info.resources.layers != 1) {
+ return false;
+ }
+ if (info.num_samples > 1) {
+ return false;
+ }
+ if (VideoCore::Surface::IsPixelFormatASTC(info.format) && !device.IsOptimalAstcSupported()) {
+ return false;
+ }
+ if (VideoCore::Surface::IsPixelFormatBCn(info.format) && !device.IsOptimalBcnSupported()) {
+ return false;
+ }
+ const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format);
+ return bytes_per_block == 4 || bytes_per_block == 8 || bytes_per_block == 16;
+}
+
+void BlockLinearUnswizzle2DPass::Unswizzle(
+ Image& image, const StagingBufferRef& swizzled,
+ std::span swizzles) {
+ if (swizzles.empty()) {
+ return;
+ }
+ const VideoCommon::SwizzleParameters& sw = swizzles.front();
+ const auto params = VideoCommon::Accelerated::MakeBlockLinearSwizzle2DParams(sw, image.info);
+
+ const u32 width = sw.num_tiles.width;
+ const u32 height = sw.num_tiles.height;
+ const u32 texel_width = image.info.size.width;
+ const u32 texel_height = image.info.size.height;
+ const u32 depth = image.info.resources.layers;
+ const u32 bytes_per_block = 1u << params.bytes_per_block_log2;
+ const VkDeviceSize output_size =
+ static_cast(width) * height * depth * bytes_per_block;
+
+ const StagingBufferRef output =
+ staging_buffer_pool.Request(static_cast(output_size), MemoryUsage::DeviceLocal);
+
+ BlockLinearUnswizzle2DPushConstants pc{};
+ pc.dim = {width, height, depth};
+ pc.bytes_per_block_log2 = params.bytes_per_block_log2;
+ pc.origin = params.origin;
+ pc.layer_stride = params.layer_stride;
+ pc.block_size = params.block_size;
+ pc.x_shift = params.x_shift;
+ pc.block_height = params.block_height;
+ pc.block_height_mask = params.block_height_mask;
+
+ scheduler.RequestOutsideRenderPassOperationContext();
+
+ compute_pass_descriptor_queue.Acquire(scheduler, 2);
+ compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, sw.buffer_offset + swizzled.offset,
+ image.guest_size_bytes - sw.buffer_offset);
+ compute_pass_descriptor_queue.AddBuffer(output.buffer, output.offset, output_size);
+
+ const void* descriptor_data = compute_pass_descriptor_queue.UpdateData();
+ const VkDescriptorSet set = descriptor_allocator.Commit();
+
+ const u32 gx = Common::DivCeil(width, 16u);
+ const u32 gy = Common::DivCeil(height, 8u);
+ const bool is_initialized = image.ExchangeInitialization();
+
+ const VkBuffer out_buffer = output.buffer;
+ const VkDeviceSize out_offset = output.offset;
+ const VkImage dst_image = image.Handle();
+ const VkImageAspectFlags aspect = image.AspectMask();
+
+ scheduler.Record([this, set, descriptor_data, pc, gx, gy, depth, output_size, out_buffer,
+ out_offset, dst_image, aspect, width, height,
+ is_initialized](vk::CommandBuffer cmdbuf) {
+ if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) {
+ return;
+ }
+ device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
+ cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
+ cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
+ cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pc), &pc);
+ cmdbuf.Dispatch(gx, gy, depth);
+
+ const VkBufferMemoryBarrier buffer_barrier{
+ .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .buffer = out_buffer,
+ .offset = out_offset,
+ .size = output_size,
+ };
+ const VkImageMemoryBarrier pre_copy{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = static_cast(is_initialized ? VK_ACCESS_SHADER_READ_BIT
+ : VK_ACCESS_NONE),
+ .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
+ .oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
+ .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = dst_image,
+ .subresourceRange{
+ .aspectMask = aspect,
+ .baseMipLevel = 0,
+ .levelCount = VK_REMAINING_MIP_LEVELS,
+ .baseArrayLayer = 0,
+ .layerCount = VK_REMAINING_ARRAY_LAYERS,
+ },
+ };
+ cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
+ (is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE
+ : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT),
+ VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, buffer_barrier, pre_copy);
+
+ const VkBufferImageCopy copy{
+ .bufferOffset = out_offset,
+ .bufferRowLength = 0,
+ .bufferImageHeight = 0,
+ .imageSubresource{
+ .aspectMask = aspect,
+ .mipLevel = 0,
+ .baseArrayLayer = 0,
+ .layerCount = depth,
+ },
+ .imageOffset = {0, 0, 0},
+ .imageExtent = {texel_width, texel_height, 1},
+ };
+ cmdbuf.CopyBufferToImage(out_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
+
+ const VkImageMemoryBarrier post_copy{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .newLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = dst_image,
+ .subresourceRange{
+ .aspectMask = aspect,
+ .baseMipLevel = 0,
+ .levelCount = VK_REMAINING_MIP_LEVELS,
+ .baseArrayLayer = 0,
+ .layerCount = VK_REMAINING_ARRAY_LAYERS,
+ },
+ };
+ cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
+ 0, {}, {}, post_copy);
+ });
+}
+
+namespace {
+constexpr u32 BL3DB_BINDING_INPUT_BUFFER = 0;
+constexpr u32 BL3DB_BINDING_OUTPUT_BUFFER = 1;
+
+struct alignas(16) BlockLinearUnswizzle3DBufferPushConstants {
+ std::array dim;
+ u32 bytes_per_block_log2;
+ std::array origin;
+ u32 slice_size;
+ u32 block_size;
+ u32 x_shift;
+ u32 block_height;
+ u32 block_height_mask;
+ u32 block_depth;
+ u32 block_depth_mask;
+};
+static_assert(sizeof(BlockLinearUnswizzle3DBufferPushConstants) <= 128);
+
+constexpr std::array BL3DB_BINDINGS{{
+ {
+ .binding = BL3DB_BINDING_INPUT_BUFFER,
+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+ .descriptorCount = 1,
+ .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
+ .pImmutableSamplers = nullptr,
+ },
+ {
+ .binding = BL3DB_BINDING_OUTPUT_BUFFER,
+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+ .descriptorCount = 1,
+ .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
+ .pImmutableSamplers = nullptr,
+ },
+}};
+
+constexpr std::array BL3DB_TEMPLATE{{
+ {
+ .dstBinding = BL3DB_BINDING_INPUT_BUFFER,
+ .dstArrayElement = 0,
+ .descriptorCount = 1,
+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+ .offset = BL3DB_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
+ .stride = sizeof(DescriptorUpdateEntry),
+ },
+ {
+ .dstBinding = BL3DB_BINDING_OUTPUT_BUFFER,
+ .dstArrayElement = 0,
+ .descriptorCount = 1,
+ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+ .offset = BL3DB_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry),
+ .stride = sizeof(DescriptorUpdateEntry),
+ },
+}};
+
+constexpr DescriptorBankInfo BL3DB_BANK_INFO{
+ .uniform_buffers = 0,
+ .storage_buffers = 2,
+ .texture_buffers = 0,
+ .image_buffers = 0,
+ .textures = 0,
+ .images = 0,
+ .score = 2,
+};
+} // Anonymous namespace
+
+BlockLinearUnswizzle3DBufferPass::BlockLinearUnswizzle3DBufferPass(
+ const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
+ StagingBufferPool& staging_buffer_pool_,
+ ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
+ : ComputePass(device_, scheduler_, descriptor_pool_, BL3DB_BINDINGS, BL3DB_TEMPLATE,
+ BL3DB_BANK_INFO,
+ COMPUTE_PUSH_CONSTANT_RANGE,
+ BLOCK_LINEAR_UNSWIZZLE_3D_BUFFER_COMP_SPV),
+ scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
+ compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
+
+BlockLinearUnswizzle3DBufferPass::~BlockLinearUnswizzle3DBufferPass() = default;
+
+bool BlockLinearUnswizzle3DBufferPass::IsSupported(const Device& device,
+ const VideoCommon::ImageInfo& info) {
+ if (info.type != VideoCommon::ImageType::e3D) {
+ return false;
+ }
+ if (info.resources.levels != 1 || info.resources.layers != 1) {
+ return false;
+ }
+ if (info.num_samples > 1) {
+ return false;
+ }
+ if (info.size.depth <= 1) {
+ return false;
+ }
+ if (VideoCore::Surface::IsPixelFormatASTC(info.format)) {
+ return false;
+ }
+ if (VideoCore::Surface::IsPixelFormatBCn(info.format) && !device.IsOptimalBcnSupported()) {
+ return false;
+ }
+ const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format);
+ return bytes_per_block == 4 || bytes_per_block == 8 || bytes_per_block == 16;
+}
+
+void BlockLinearUnswizzle3DBufferPass::Unswizzle(
+ Image& image, const StagingBufferRef& swizzled,
+ std::span swizzles) {
+ if (swizzles.empty()) {
+ return;
+ }
+
+ const VideoCommon::SwizzleParameters& sw = swizzles.front();
+ const auto params = VideoCommon::Accelerated::MakeBlockLinearSwizzle3DParams(sw, image.info);
+
+ const u32 blocks_x = sw.num_tiles.width;
+ const u32 blocks_y = sw.num_tiles.height;
+ const u32 blocks_z = sw.num_tiles.depth;
+ const u32 bytes_per_block = 1u << params.bytes_per_block_log2;
+ const VkDeviceSize output_size =
+ static_cast(blocks_x) * blocks_y * blocks_z * bytes_per_block;
+
+ const StagingBufferRef output =
+ staging_buffer_pool.Request(static_cast(output_size), MemoryUsage::DeviceLocal);
+
+ BlockLinearUnswizzle3DBufferPushConstants pc{};
+ pc.dim = {blocks_x, blocks_y, blocks_z};
+ pc.bytes_per_block_log2 = params.bytes_per_block_log2;
+ pc.origin = params.origin;
+ pc.slice_size = params.slice_size;
+ pc.block_size = params.block_size;
+ pc.x_shift = params.x_shift;
+ pc.block_height = params.block_height;
+ pc.block_height_mask = params.block_height_mask;
+ pc.block_depth = params.block_depth;
+ pc.block_depth_mask = params.block_depth_mask;
+
+ scheduler.RequestOutsideRenderPassOperationContext();
+
+ compute_pass_descriptor_queue.Acquire(scheduler, 2);
+ compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, sw.buffer_offset + swizzled.offset,
+ image.guest_size_bytes - sw.buffer_offset);
+ compute_pass_descriptor_queue.AddBuffer(output.buffer, output.offset, output_size);
+
+ const void* descriptor_data = compute_pass_descriptor_queue.UpdateData();
+ const VkDescriptorSet set = descriptor_allocator.Commit();
+
+ const u32 gx = Common::DivCeil(blocks_x, 8u);
+ const u32 gy = Common::DivCeil(blocks_y, 8u);
+ const u32 gz = Common::DivCeil(blocks_z, 4u);
+ const bool is_initialized = image.ExchangeInitialization();
+
+ const VkBuffer out_buffer = output.buffer;
+ const VkDeviceSize out_offset = output.offset;
+ const VkImage dst_image = image.Handle();
+ const VkImageAspectFlags aspect = image.AspectMask();
+ const VkExtent3D extent{
+ .width = image.info.size.width,
+ .height = image.info.size.height,
+ .depth = image.info.size.depth,
+ };
+
+ scheduler.Record([this, set, descriptor_data, pc, gx, gy, gz, output_size, out_buffer,
+ out_offset, dst_image, aspect, extent,
+ is_initialized](vk::CommandBuffer cmdbuf) {
+ if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) {
+ return;
+ }
+ device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
+ cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
+ cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
+ cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pc), &pc);
+ cmdbuf.Dispatch(gx, gy, gz);
+
+ const VkBufferMemoryBarrier buffer_barrier{
+ .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .buffer = out_buffer,
+ .offset = out_offset,
+ .size = output_size,
+ };
+ const VkImageMemoryBarrier pre_copy{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = static_cast(is_initialized ? VK_ACCESS_SHADER_READ_BIT
+ : VK_ACCESS_NONE),
+ .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
+ .oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
+ .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = dst_image,
+ .subresourceRange{
+ .aspectMask = aspect,
+ .baseMipLevel = 0,
+ .levelCount = VK_REMAINING_MIP_LEVELS,
+ .baseArrayLayer = 0,
+ .layerCount = VK_REMAINING_ARRAY_LAYERS,
+ },
+ };
+ cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
+ (is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE
+ : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT),
+ VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, buffer_barrier, pre_copy);
+
+ const VkBufferImageCopy copy{
+ .bufferOffset = out_offset,
+ .bufferRowLength = 0,
+ .bufferImageHeight = 0,
+ .imageSubresource{
+ .aspectMask = aspect,
+ .mipLevel = 0,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ .imageOffset = {0, 0, 0},
+ .imageExtent = extent,
+ };
+ cmdbuf.CopyBufferToImage(out_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
+
+ const VkImageMemoryBarrier post_copy{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .newLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = dst_image,
+ .subresourceRange{
+ .aspectMask = aspect,
+ .baseMipLevel = 0,
+ .levelCount = VK_REMAINING_MIP_LEVELS,
+ .baseArrayLayer = 0,
+ .layerCount = VK_REMAINING_ARRAY_LAYERS,
+ },
+ };
+ cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
+ 0, {}, {}, post_copy);
+ });
+}
+
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h
index 3d30aa6bdc..c08d8724f6 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.h
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.h
@@ -164,4 +164,43 @@ private:
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
};
+class BlockLinearUnswizzle2DPass final : public ComputePass {
+public:
+ explicit BlockLinearUnswizzle2DPass(const Device& device_, Scheduler& scheduler_,
+ DescriptorPool& descriptor_pool_,
+ StagingBufferPool& staging_buffer_pool_,
+ ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
+ ~BlockLinearUnswizzle2DPass();
+
+ [[nodiscard]] static bool IsSupported(const Device& device,
+ const VideoCommon::ImageInfo& info);
+
+ void Unswizzle(Image& image, const StagingBufferRef& swizzled,
+ std::span swizzles);
+
+private:
+ Scheduler& scheduler;
+ StagingBufferPool& staging_buffer_pool;
+ ComputePassDescriptorQueue& compute_pass_descriptor_queue;
+};
+
+class BlockLinearUnswizzle3DBufferPass final : public ComputePass {
+public:
+ explicit BlockLinearUnswizzle3DBufferPass(
+ const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
+ StagingBufferPool& staging_buffer_pool_,
+ ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
+ ~BlockLinearUnswizzle3DBufferPass();
+
+ [[nodiscard]] static bool IsSupported(const Device& device, const VideoCommon::ImageInfo& info);
+
+ void Unswizzle(Image& image, const StagingBufferRef& swizzled,
+ std::span swizzles);
+
+private:
+ Scheduler& scheduler;
+ StagingBufferPool& staging_buffer_pool;
+ ComputePassDescriptorQueue& compute_pass_descriptor_queue;
+};
+
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index c3f56408b5..5d3bad180c 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -968,6 +968,10 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
staging_buffer_pool, compute_pass_descriptor_queue);
}
+ bl2d_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
+ compute_pass_descriptor_queue);
+ bl3db_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
+ compute_pass_descriptor_queue);
}
void TextureCacheRuntime::Finish() {
@@ -1894,6 +1898,14 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
}
flags |= VideoCommon::ImageFlagBits::Converted;
flags |= VideoCommon::ImageFlagBits::CostlyLoad;
+ } else if (runtime->bl2d_unswizzle_pass &&
+ BlockLinearUnswizzle2DPass::IsSupported(runtime->device, info)) {
+ flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
+ flags |= VideoCommon::ImageFlagBits::CostlyLoad;
+ } else if (runtime->bl3db_unswizzle_pass &&
+ BlockLinearUnswizzle3DBufferPass::IsSupported(runtime->device, info)) {
+ flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
+ flags |= VideoCommon::ImageFlagBits::CostlyLoad;
}
if (IsPixelFormatBCn(info.format) && !runtime->device.IsOptimalBcnSupported()) {
flags |= VideoCommon::ImageFlagBits::Converted;
@@ -3141,6 +3153,15 @@ void TextureCacheRuntime::AccelerateImageUpload(
return astc_decoder_pass->Assemble(image, map, swizzles);
}
+ if (bl2d_unswizzle_pass && BlockLinearUnswizzle2DPass::IsSupported(device, image.info)) {
+ return bl2d_unswizzle_pass->Unswizzle(image, map, swizzles);
+ }
+
+ if (bl3db_unswizzle_pass &&
+ BlockLinearUnswizzle3DBufferPass::IsSupported(device, image.info)) {
+ return bl3db_unswizzle_pass->Unswizzle(image, map, swizzles);
+ }
+
if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) {
if (IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D) {
ASSERT(false && "GPU unswizzle is disabled for BCn 3D texture");
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 1b1e35aa45..1fb1f3e5f5 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -159,6 +159,8 @@ public:
std::optional astc_decoder_pass;
std::optional bl3d_unswizzle_pass;
+ std::optional bl2d_unswizzle_pass;
+ std::optional bl3db_unswizzle_pass;
const Settings::ResolutionScalingInfo& resolution;
std::array, VideoCore::Surface::MaxPixelFormat> view_formats;