Browse Source

Attempt to simplify the sparse texture processing

Added configurable GPU level texture unswizzling
Renamed GPU Unswizzle to Chunked GPU Unswizzle to prevent confusion
pull/3737/head
Forrest Mark X 2 weeks ago
committed by crueter
parent
commit
707b164af2
  1. 1
      src/common/settings.cpp
  2. 5
      src/common/settings.h
  3. 1
      src/common/settings_enums.h
  4. 11
      src/qt_common/config/shared_translation.cpp
  5. 1
      src/video_core/host_shaders/CMakeLists.txt
  6. 5
      src/video_core/host_shaders/block_linear_unswizzle_2d.comp
  7. 176
      src/video_core/host_shaders/block_linear_unswizzle_2d_bcn.comp
  8. 5
      src/video_core/host_shaders/block_linear_unswizzle_3d.comp
  9. 8
      src/video_core/renderer_opengl/gl_texture_cache.cpp
  10. 17
      src/video_core/renderer_opengl/gl_texture_cache.h
  11. 2
      src/video_core/renderer_opengl/util_shaders.cpp
  12. 698
      src/video_core/renderer_vulkan/vk_compute_pass.cpp
  13. 106
      src/video_core/renderer_vulkan/vk_compute_pass.h
  14. 178
      src/video_core/renderer_vulkan/vk_texture_cache.cpp
  15. 23
      src/video_core/renderer_vulkan/vk_texture_cache.h
  16. 141
      src/video_core/texture_cache/texture_cache.h
  17. 9
      src/video_core/texture_cache/texture_cache_base.h

1
src/common/settings.cpp

@ -47,6 +47,7 @@ SETTING(u16, false);
SWITCHABLE(AnisotropyMode, true); SWITCHABLE(AnisotropyMode, true);
SWITCHABLE(AntiAliasing, false); SWITCHABLE(AntiAliasing, false);
SWITCHABLE(AspectRatio, true); SWITCHABLE(AspectRatio, true);
SWITCHABLE(TexUnswizzleMode, true);
SWITCHABLE(AstcDecodeMode, true); SWITCHABLE(AstcDecodeMode, true);
SWITCHABLE(AstcRecompression, true); SWITCHABLE(AstcRecompression, true);
SWITCHABLE(AudioMode, true); SWITCHABLE(AudioMode, true);

5
src/common/settings.h

@ -64,6 +64,7 @@ SETTING(u16, false);
SWITCHABLE(AnisotropyMode, true); SWITCHABLE(AnisotropyMode, true);
SWITCHABLE(AntiAliasing, false); SWITCHABLE(AntiAliasing, false);
SWITCHABLE(AspectRatio, true); SWITCHABLE(AspectRatio, true);
SWITCHABLE(TexUnswizzleMode, true);
SWITCHABLE(AstcDecodeMode, true); SWITCHABLE(AstcDecodeMode, true);
SWITCHABLE(AstcRecompression, true); SWITCHABLE(AstcRecompression, true);
SWITCHABLE(AudioMode, true); SWITCHABLE(AudioMode, true);
@ -464,6 +465,10 @@ struct Values {
#endif #endif
"max_anisotropy", "max_anisotropy",
Category::RendererAdvanced}; Category::RendererAdvanced};
SwitchableSetting<TexUnswizzleMode, true> accelerate_unswizzle{linkage,
TexUnswizzleMode::Cpu,
"accelerate_unswizzle",
Category::RendererAdvanced};
SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage, SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage,
AstcDecodeMode::Gpu, AstcDecodeMode::Gpu,
"accelerate_astc", "accelerate_astc",

1
src/common/settings_enums.h

@ -129,6 +129,7 @@ ENUM(TimeZone, Auto, Default, Cet, Cst6Cdt, Cuba, Eet, Egypt, Eire, Est, Est5Edt
Japan, Kwajalein, Libya, Met, Mst, Mst7Mdt, Navajo, Nz, NzChat, Poland, Portugal, Prc, Pst8Pdt, Japan, Kwajalein, Libya, Met, Mst, Mst7Mdt, Navajo, Nz, NzChat, Poland, Portugal, Prc, Pst8Pdt,
Roc, Rok, Singapore, Turkey, Uct, Universal, Utc, WSu, Wet, Zulu); Roc, Rok, Singapore, Turkey, Uct, Universal, Utc, WSu, Wet, Zulu);
ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16, X32, X64, None); ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16, X32, X64, None);
ENUM(TexUnswizzleMode, Cpu, Gpu);
ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous); ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous);
ENUM(AstcRecompression, Uncompressed, Bc1, Bc3); ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);
ENUM(FramePacingMode, Target_Auto, Target_30, Target_60, Target_90, Target_120); ENUM(FramePacingMode, Target_Auto, Target_30, Target_60, Target_90, Target_120);

11
src/qt_common/config/shared_translation.cpp

@ -169,6 +169,10 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
tr("Specifies how videos should be decoded.\nIt can either use the CPU or the GPU for " tr("Specifies how videos should be decoded.\nIt can either use the CPU or the GPU for "
"decoding, or perform no decoding at all (black screen on videos).\n" "decoding, or perform no decoding at all (black screen on videos).\n"
"In most cases, GPU decoding provides the best performance.")); "In most cases, GPU decoding provides the best performance."));
INSERT(Settings, accelerate_unswizzle, tr("Texture Unsiwzzle Method:"),
tr("This option controls how generic textures should be unswizzled.\n"
"CPU: Use the CPU for unswizzling.\n"
"GPU: Use the GPU's compute shaders to unswizzling generic textures (recommended)."));
INSERT(Settings, accelerate_astc, tr("ASTC Decoding Method:"), INSERT(Settings, accelerate_astc, tr("ASTC Decoding Method:"),
tr("This option controls how ASTC textures should be decoded.\n" tr("This option controls how ASTC textures should be decoded.\n"
"CPU: Use the CPU for decoding.\n" "CPU: Use the CPU for decoding.\n"
@ -230,7 +234,7 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
INSERT(Settings, gpu_clock, tr("GPU Clocks"), INSERT(Settings, gpu_clock, tr("GPU Clocks"),
tr("Makes the game believe GPU work finishes faster than it does, so it stops lowering " tr("Makes the game believe GPU work finishes faster than it does, so it stops lowering "
"resolution and render distance to fit the Switch's clocks.")); "resolution and render distance to fit the Switch's clocks."));
INSERT(Settings, gpu_unswizzle_enabled, tr("GPU Unswizzle"),
INSERT(Settings, gpu_unswizzle_enabled, tr("Chunked GPU Unswizzle"),
tr("Accelerates BCn 3D texture decoding using GPU compute.\n" tr("Accelerates BCn 3D texture decoding using GPU compute.\n"
"Disable if experiencing crashes or graphical glitches.")); "Disable if experiencing crashes or graphical glitches."));
INSERT(Settings, gpu_unswizzle_texture_size, tr("GPU Unswizzle Max Texture Size"), INSERT(Settings, gpu_unswizzle_texture_size, tr("GPU Unswizzle Max Texture Size"),
@ -386,6 +390,11 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
PAIR(SpirvOptimizeMode, OnLoad, tr("On Load")), PAIR(SpirvOptimizeMode, OnLoad, tr("On Load")),
PAIR(SpirvOptimizeMode, Always, tr("Always")), PAIR(SpirvOptimizeMode, Always, tr("Always")),
}}); }});
translations->insert({Settings::EnumMetadata<Settings::TexUnswizzleMode>::Index(),
{
PAIR(TexUnswizzleMode, Cpu, tr("CPU")),
PAIR(TexUnswizzleMode, Gpu, tr("GPU")),
}});
translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(), translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(),
{ {
PAIR(AstcDecodeMode, Cpu, tr("CPU")), PAIR(AstcDecodeMode, Cpu, tr("CPU")),

1
src/video_core/host_shaders/CMakeLists.txt

@ -21,6 +21,7 @@ set(SHADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_msaa.frag ${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_msaa.frag
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_stencil_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.comp
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d_bcn.comp
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_bcn.comp ${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_bcn.comp
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d24s8.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d24s8.frag
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d32f.frag ${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d32f.frag

5
src/video_core/host_shaders/block_linear_unswizzle_2d.comp

@ -39,6 +39,7 @@ UNIFORM(4) uint block_size;
UNIFORM(5) uint x_shift; UNIFORM(5) uint x_shift;
UNIFORM(6) uint block_height; UNIFORM(6) uint block_height;
UNIFORM(7) uint block_height_mask; UNIFORM(7) uint block_height_mask;
UNIFORM(8) uvec3 valid_extent;
END_PUSH_CONSTANTS END_PUSH_CONSTANTS
#if HAS_EXTENDED_TYPES #if HAS_EXTENDED_TYPES
@ -104,6 +105,10 @@ uvec4 ReadTexel(uint offset) {
} }
void main() { void main() {
if (any(greaterThanEqual(gl_GlobalInvocationID, valid_extent))) {
return;
}
uvec3 pos = gl_GlobalInvocationID + origin; uvec3 pos = gl_GlobalInvocationID + origin;
pos.x <<= bytes_per_block_log2; pos.x <<= bytes_per_block_log2;

176
src/video_core/host_shaders/block_linear_unswizzle_2d_bcn.comp

@ -0,0 +1,176 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#version 430
#ifdef VULKAN
#extension GL_EXT_shader_16bit_storage : require
#extension GL_EXT_shader_8bit_storage : require
#define HAS_EXTENDED_TYPES 1
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
#define END_PUSH_CONSTANTS };
#define UNIFORM(n)
#define BINDING_INPUT_BUFFER 0
#define BINDING_OUTPUT_BUFFER 1
#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
#extension GL_NV_gpu_shader5 : enable
#ifdef GL_NV_gpu_shader5
#define HAS_EXTENDED_TYPES 1
#else
#define HAS_EXTENDED_TYPES 0
#endif
#define BEGIN_PUSH_CONSTANTS
#define END_PUSH_CONSTANTS
#define UNIFORM(n) layout (location = n) uniform
#define BINDING_INPUT_BUFFER 0
#define BINDING_OUTPUT_BUFFER 0
#endif
BEGIN_PUSH_CONSTANTS
UNIFORM(0) uvec3 origin;
UNIFORM(1) ivec3 destination;
UNIFORM(2) uint bytes_per_block_log2;
UNIFORM(3) uint layer_stride;
UNIFORM(4) uint block_size;
UNIFORM(5) uint x_shift;
UNIFORM(6) uint block_height;
UNIFORM(7) uint block_height_mask;
UNIFORM(8) uvec3 valid_extent;
UNIFORM(9) uint out_pitch;
UNIFORM(10) uint out_layer_stride;
END_PUSH_CONSTANTS
#if HAS_EXTENDED_TYPES
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; };
#endif
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[]; };
#if HAS_EXTENDED_TYPES
layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBufferU8 { uint8_t out8data[]; };
layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBufferU16 { uint16_t out16data[]; };
#endif
layout(binding = BINDING_OUTPUT_BUFFER, std430) buffer OutputBufferU32 { uint out32data[]; };
layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBufferU64 { uvec2 out64data[]; };
layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBufferU128 { uvec4 out128data[]; };
layout(local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
const uint GOB_SIZE_X = 64;
const uint GOB_SIZE_Y = 8;
const uint GOB_SIZE_Z = 1;
const uint GOB_SIZE = GOB_SIZE_X * GOB_SIZE_Y * GOB_SIZE_Z;
const uint GOB_SIZE_X_SHIFT = 6;
const uint GOB_SIZE_Y_SHIFT = 3;
const uint GOB_SIZE_Z_SHIFT = 0;
const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHIFT;
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1, GOB_SIZE_Y - 1);
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 * 64 + pos.x);
}
uvec4 ReadTexel(uint offset) {
switch (bytes_per_block_log2) {
#if HAS_EXTENDED_TYPES
case 0:
return uvec4(u8data[offset], 0, 0, 0);
case 1:
return uvec4(u16data[offset / 2], 0, 0, 0);
#else
case 0:
return uvec4(bitfieldExtract(u32data[offset / 4], int((offset * 8) & 24), 8), 0, 0, 0);
case 1:
return uvec4(bitfieldExtract(u32data[offset / 4], int((offset * 8) & 16), 16), 0, 0, 0);
#endif
case 2:
return uvec4(u32data[offset / 4], 0, 0, 0);
case 3:
return uvec4(u64data[offset / 8], 0, 0);
case 4:
return u128data[offset / 16];
}
return uvec4(0);
}
void WriteTexel(uint offset, uvec4 texel) {
switch (bytes_per_block_log2) {
#if HAS_EXTENDED_TYPES
case 0:
out8data[offset] = uint8_t(texel.x);
break;
case 1:
out16data[offset / 2] = uint16_t(texel.x);
break;
#else
case 0: {
const uint shift = (offset & 3) * 8;
atomicAnd(out32data[offset / 4], ~(0xFFu << shift));
atomicOr(out32data[offset / 4], texel.x << shift);
break;
}
case 1: {
const uint shift = (offset & 2) * 8;
atomicAnd(out32data[offset / 4], ~(0xFFFFu << shift));
atomicOr(out32data[offset / 4], texel.x << shift);
break;
}
#endif
case 2:
out32data[offset / 4] = texel.x;
break;
case 3:
out64data[offset / 8] = texel.xy;
break;
case 4:
out128data[offset / 16] = texel;
break;
}
}
void main() {
if (any(greaterThanEqual(gl_GlobalInvocationID, valid_extent))) {
return;
}
uvec3 pos = gl_GlobalInvocationID + origin;
pos.x <<= bytes_per_block_log2;
const uint swizzle = SwizzleOffset(pos.xy);
const uint block_y = pos.y >> GOB_SIZE_Y_SHIFT;
uint offset = 0;
offset += pos.z * layer_stride;
const uint width_in_gobs = (out_pitch + 63) / 64;
const uint row_stride = width_in_gobs * block_size;
offset += (block_y >> block_height) * row_stride;
offset += (block_y & block_height_mask) << GOB_SIZE_SHIFT;
offset += (pos.x >> GOB_SIZE_X_SHIFT) << x_shift;
offset += swizzle;
const uvec4 texel = ReadTexel(offset);
const ivec3 coord = ivec3(gl_GlobalInvocationID) + destination;
const uint out_offset = (coord.z * out_layer_stride) + (coord.y * out_pitch) + (coord.x << bytes_per_block_log2);
WriteTexel(out_offset, texel);
}

5
src/video_core/host_shaders/block_linear_unswizzle_3d.comp

@ -41,6 +41,7 @@ UNIFORM(6) uint block_height;
UNIFORM(7) uint block_height_mask; UNIFORM(7) uint block_height_mask;
UNIFORM(8) uint block_depth; UNIFORM(8) uint block_depth;
UNIFORM(9) uint block_depth_mask; UNIFORM(9) uint block_depth_mask;
UNIFORM(10) uvec3 valid_extent;
END_PUSH_CONSTANTS END_PUSH_CONSTANTS
#if HAS_EXTENDED_TYPES #if HAS_EXTENDED_TYPES
@ -106,6 +107,10 @@ uvec4 ReadTexel(uint offset) {
} }
void main() { void main() {
if (any(greaterThanEqual(gl_GlobalInvocationID, valid_extent))) {
return;
}
uvec3 pos = gl_GlobalInvocationID + origin; uvec3 pos = gl_GlobalInvocationID + origin;
pos.x <<= bytes_per_block_log2; pos.x <<= bytes_per_block_log2;

8
src/video_core/renderer_opengl/gl_texture_cache.cpp

@ -552,6 +552,10 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, ProgramManager&
TextureCacheRuntime::~TextureCacheRuntime() = default; TextureCacheRuntime::~TextureCacheRuntime() = default;
bool TextureCacheRuntime::IsUnswizzleStorageFormatSupported(PixelFormat format) {
return false;
}
void TextureCacheRuntime::Finish() { void TextureCacheRuntime::Finish() {
glFinish(); glFinish();
} }
@ -652,9 +656,7 @@ void TextureCacheRuntime::BlitFramebuffer(Framebuffer* dst, Framebuffer* src,
void TextureCacheRuntime::AccelerateImageUpload(Image &image, const StagingBufferMap &map, void TextureCacheRuntime::AccelerateImageUpload(Image &image, const StagingBufferMap &map,
std::span<const SwizzleParameters> swizzles, std::span<const SwizzleParameters> swizzles,
u32 z_src_start, u32 z_image_start, u32 z_count,
[[maybe_unused]] std::span<const u8> slice_has_data,
std::span<const VideoCommon::Accelerated::SliceBBox> slice_bounds, [[maybe_unused]] bool image_already_uploaded) {
u32 z_src_start, u32 z_image_start) {
switch (image.info.type) { switch (image.info.type) {
case ImageType::e2D: case ImageType::e2D:
if (IsPixelFormatASTC(image.info.format)) { if (IsPixelFormatASTC(image.info.format)) {

17
src/video_core/renderer_opengl/gl_texture_cache.h

@ -41,6 +41,8 @@ using VideoCommon::NUM_RT;
using VideoCommon::Region2D; using VideoCommon::Region2D;
using VideoCommon::RenderTargets; using VideoCommon::RenderTargets;
using VideoCore::Surface::PixelFormat;
struct FormatProperties { struct FormatProperties {
GLenum compatibility_class; GLenum compatibility_class;
bool compatibility_by_size; bool compatibility_by_size;
@ -73,6 +75,8 @@ public:
StagingBufferPool& staging_buffer_pool); StagingBufferPool& staging_buffer_pool);
~TextureCacheRuntime(); ~TextureCacheRuntime();
bool IsUnswizzleStorageFormatSupported(PixelFormat format);
void Finish(); void Finish();
StagingBufferMap UploadStagingBuffer(size_t size, bool deferred = false); StagingBufferMap UploadStagingBuffer(size_t size, bool deferred = false);
@ -100,6 +104,10 @@ public:
return true; return true;
} }
bool CanAccelerateUnswizzle() const noexcept {
return false;
}
void CopyImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies); void CopyImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
void CopyImageMSAA(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies); void CopyImageMSAA(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
@ -118,12 +126,9 @@ public:
const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter, const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter,
Tegra::Engines::Fermi2D::Operation operation); Tegra::Engines::Fermi2D::Operation operation);
void AccelerateImageUpload(Image&, const StagingBufferMap&,
std::span<const VideoCommon::SwizzleParameters>,
u32 z_src_start, u32 z_image_start, u32 z_count,
std::span<const u8> slice_has_data = {},
std::span<const VideoCommon::Accelerated::SliceBBox> slice_bounds = {},
bool image_already_uploaded = false);
void AccelerateImageUpload(Image &, const StagingBufferMap &,
std::span<const VideoCommon::SwizzleParameters>,
u32 z_src_start, u32 z_image_start);
void InsertUploadMemoryBarrier(); void InsertUploadMemoryBarrier();

2
src/video_core/renderer_opengl/util_shaders.cpp

@ -140,6 +140,7 @@ void UtilShaders::BlockLinearUpload2D(Image& image, const StagingBufferMap& map,
glUniform1ui(5, params.x_shift); glUniform1ui(5, params.x_shift);
glUniform1ui(6, params.block_height); glUniform1ui(6, params.block_height);
glUniform1ui(7, params.block_height_mask); glUniform1ui(7, params.block_height_mask);
glUniform3ui(8, num_tiles.width, num_tiles.height, image.info.resources.layers);
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_INPUT_BUFFER, map.buffer, input_offset, glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_INPUT_BUFFER, map.buffer, input_offset,
image.guest_size_bytes - swizzle.buffer_offset); image.guest_size_bytes - swizzle.buffer_offset);
glBindImageTexture(BINDING_OUTPUT_IMAGE, image.StorageHandle(), swizzle.level, GL_TRUE, 0, glBindImageTexture(BINDING_OUTPUT_IMAGE, image.StorageHandle(), swizzle.level, GL_TRUE, 0,
@ -178,6 +179,7 @@ void UtilShaders::BlockLinearUpload3D(Image& image, const StagingBufferMap& map,
glUniform1ui(7, params.block_height_mask); glUniform1ui(7, params.block_height_mask);
glUniform1ui(8, params.block_depth); glUniform1ui(8, params.block_depth);
glUniform1ui(9, params.block_depth_mask); glUniform1ui(9, params.block_depth_mask);
glUniform3ui(10, num_tiles.width, num_tiles.height, num_tiles.depth);
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_INPUT_BUFFER, map.buffer, input_offset, glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_INPUT_BUFFER, map.buffer, input_offset,
image.guest_size_bytes - swizzle.buffer_offset); image.guest_size_bytes - swizzle.buffer_offset);
glBindImageTexture(BINDING_OUTPUT_IMAGE, image.StorageHandle(), swizzle.level, GL_TRUE, 0, glBindImageTexture(BINDING_OUTPUT_IMAGE, image.StorageHandle(), swizzle.level, GL_TRUE, 0,

698
src/video_core/renderer_vulkan/vk_compute_pass.cpp

@ -22,7 +22,11 @@
#include "video_core/host_shaders/resolve_conditional_render_comp_spv.h" #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_quad_indexed_comp_spv.h"
#include "video_core/host_shaders/vulkan_uint8_comp_spv.h" #include "video_core/host_shaders/vulkan_uint8_comp_spv.h"
#include "video_core/host_shaders/block_linear_unswizzle_2d_bcn_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_bcn_comp_spv.h"
#include "video_core/host_shaders/block_linear_unswizzle_2d_comp_spv.h"
#include "video_core/host_shaders/block_linear_unswizzle_3d_comp_spv.h"
#include "video_core/host_shaders/pitch_unswizzle_comp_spv.h"
#include "video_core/renderer_vulkan/vk_compute_pass.h" #include "video_core/renderer_vulkan/vk_compute_pass.h"
#include "video_core/renderer_vulkan/vk_descriptor_pool.h" #include "video_core/renderer_vulkan/vk_descriptor_pool.h"
#include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_scheduler.h"
@ -30,6 +34,7 @@
#include "video_core/renderer_vulkan/vk_update_descriptor.h" #include "video_core/renderer_vulkan/vk_update_descriptor.h"
#include "video_core/texture_cache/accelerated_swizzle.h" #include "video_core/texture_cache/accelerated_swizzle.h"
#include "video_core/texture_cache/types.h" #include "video_core/texture_cache/types.h"
#include "video_core/texture_cache/util.h"
#include "video_core/textures/decoders.h" #include "video_core/textures/decoders.h"
#include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_wrapper.h" #include "video_core/vulkan_common/vulkan_wrapper.h"
@ -615,6 +620,390 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
scheduler.Finish(); scheduler.Finish();
} }
struct BlockLinearUnswizzle2DImagePushConstants {
u32 origin[3];
u32 padding1 = 0;
s32 destination[3];
u32 bytes_per_block_log2;
u32 layer_stride;
u32 block_size;
u32 x_shift;
u32 block_height;
u32 block_height_mask;
u32 padding2[3] = {0};
u32 valid_extent[3];
u32 padding3 = 0;
};
static_assert(sizeof(BlockLinearUnswizzle2DImagePushConstants) == 80, "Push constants size is incorrect");
struct BlockLinearUnswizzle3DImagePushConstants {
u32 origin[3];
u32 padding1 = 0;
s32 destination[3];
u32 bytes_per_block_log2;
u32 slice_size;
u32 block_size;
u32 x_shift;
u32 block_height;
u32 block_height_mask;
u32 block_depth;
u32 block_depth_mask;
u32 padding2 = 0;
u32 valid_extent[3];
u32 padding3 = 0;
};
static_assert(sizeof(BlockLinearUnswizzle3DImagePushConstants) == 80, "Push constants size is incorrect");
BlockLinearUnswizzle2DImagePass::BlockLinearUnswizzle2DImagePass(
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
: ComputePass(device_, scheduler_, descriptor_pool_, ASTC_DESCRIPTOR_SET_BINDINGS,
ASTC_PASS_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY, ASTC_BANK_INFO,
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearUnswizzle2DImagePushConstants)>,
BLOCK_LINEAR_UNSWIZZLE_2D_COMP_SPV),
scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
BlockLinearUnswizzle2DImagePass::~BlockLinearUnswizzle2DImagePass() = default;
void BlockLinearUnswizzle2DImagePass::Unswizzle(
Image& image, const StagingBufferRef& map,
std::span<const VideoCommon::SwizzleParameters> swizzles) {
using namespace VideoCommon::Accelerated;
scheduler.RequestOutsideRenderPassOperationContext();
const VkPipeline vk_pipeline = *pipeline;
const VkImageAspectFlags aspect_mask = image.AspectMask();
const VkImage vk_image = image.Handle();
const bool is_initialized = image.ExchangeInitialization();
scheduler.Record([vk_pipeline, vk_image, aspect_mask,
is_initialized](vk::CommandBuffer cmdbuf) {
const VkImageMemoryBarrier image_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = static_cast<VkAccessFlags>(is_initialized ? VK_ACCESS_SHADER_WRITE_BIT
: VK_ACCESS_NONE),
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
.oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = vk_image,
.subresourceRange{
.aspectMask = aspect_mask,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
cmdbuf.PipelineBarrier(is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER
: VkPipelineStageFlags(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT),
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline);
});
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
const size_t input_offset = swizzle.buffer_offset + map.offset;
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 16U);
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U);
const u32 num_dispatches_z = Common::DivCeil(swizzle.num_tiles.depth, 8U);
compute_pass_descriptor_queue.Acquire(scheduler, 2);
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
image.guest_size_bytes - swizzle.buffer_offset);
compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
const auto params = MakeBlockLinearSwizzle3DParams(swizzle, image.info);
const std::array<u32, 3> valid_extent{swizzle.num_tiles.width, swizzle.num_tiles.height,
swizzle.num_tiles.depth};
scheduler.Record([this, num_dispatches_x, num_dispatches_y, num_dispatches_z,
params, valid_extent, descriptor_data](vk::CommandBuffer cmdbuf) {
const BlockLinearUnswizzle3DImagePushConstants uniforms{
.origin = {params.origin[0], params.origin[1], params.origin[2]},
.destination = {params.destination[0], params.destination[1],
params.destination[2]},
.bytes_per_block_log2 = params.bytes_per_block_log2,
.slice_size = params.slice_size,
.block_size = params.block_size,
.x_shift = params.x_shift,
.block_height = params.block_height,
.block_height_mask = params.block_height_mask,
.block_depth = params.block_depth,
.block_depth_mask = params.block_depth_mask,
.valid_extent = {valid_extent[0], valid_extent[1], valid_extent[2]},
};
const VkDescriptorSet set = descriptor_allocator.Commit();
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, uniforms);
cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, num_dispatches_z);
});
}
scheduler.Record([vk_image, aspect_mask](vk::CommandBuffer cmdbuf) {
const VkImageMemoryBarrier image_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = vk_image,
.subresourceRange{
.aspectMask = aspect_mask,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, image_barrier);
});
//Causes device loss!
//scheduler.Finish();
}
BlockLinearUnswizzle3DImagePass::BlockLinearUnswizzle3DImagePass(
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
: ComputePass(device_, scheduler_, descriptor_pool_, ASTC_DESCRIPTOR_SET_BINDINGS,
ASTC_PASS_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY, ASTC_BANK_INFO,
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearUnswizzle3DImagePushConstants)>,
BLOCK_LINEAR_UNSWIZZLE_3D_COMP_SPV),
scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
BlockLinearUnswizzle3DImagePass::~BlockLinearUnswizzle3DImagePass() = default;
void BlockLinearUnswizzle3DImagePass::Unswizzle(
Image& image, const StagingBufferRef& map,
std::span<const VideoCommon::SwizzleParameters> swizzles,
u32 z_src, u32 z_image_start) {
using namespace VideoCommon::Accelerated;
scheduler.RequestOutsideRenderPassOperationContext();
const VkPipeline vk_pipeline = *pipeline;
const VkImageAspectFlags aspect_mask = image.AspectMask();
const VkImage vk_image = image.Handle();
const bool is_initialized = image.ExchangeInitialization();
scheduler.Record([vk_pipeline, vk_image, aspect_mask,
is_initialized](vk::CommandBuffer cmdbuf) {
const VkImageMemoryBarrier image_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = static_cast<VkAccessFlags>(is_initialized ? VK_ACCESS_SHADER_WRITE_BIT
: VK_ACCESS_NONE),
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
.oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = vk_image,
.subresourceRange{
.aspectMask = aspect_mask,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
cmdbuf.PipelineBarrier(is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER
: VkPipelineStageFlags(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT),
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline);
});
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
const size_t input_offset = swizzle.buffer_offset + map.offset;
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 16U);
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U);
const u32 num_dispatches_z = Common::DivCeil(swizzle.num_tiles.depth, 8U);
compute_pass_descriptor_queue.Acquire(scheduler, 2);
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
image.guest_size_bytes - swizzle.buffer_offset);
compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
const auto params_base = MakeBlockLinearSwizzle3DParams(swizzle, image.info);
auto params = params_base;
params.origin[2] += z_src;
params.destination[2] += static_cast<s32>(z_image_start);
const std::array<u32, 3> valid_extent{swizzle.num_tiles.width, swizzle.num_tiles.height,
swizzle.num_tiles.depth};
scheduler.Record([this, num_dispatches_x, num_dispatches_y, num_dispatches_z,
params, valid_extent, descriptor_data](vk::CommandBuffer cmdbuf) {
const BlockLinearUnswizzle3DImagePushConstants uniforms{
.origin = {params.origin[0], params.origin[1], params.origin[2]},
.destination = {params.destination[0], params.destination[1],
params.destination[2]},
.bytes_per_block_log2 = params.bytes_per_block_log2,
.slice_size = params.slice_size,
.block_size = params.block_size,
.x_shift = params.x_shift,
.block_height = params.block_height,
.block_height_mask = params.block_height_mask,
.block_depth = params.block_depth,
.block_depth_mask = params.block_depth_mask,
.valid_extent = {valid_extent[0], valid_extent[1], valid_extent[2]},
};
const VkDescriptorSet set = descriptor_allocator.Commit();
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, uniforms);
cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, num_dispatches_z);
});
}
scheduler.Record([vk_image, aspect_mask](vk::CommandBuffer cmdbuf) {
const VkImageMemoryBarrier image_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = vk_image,
.subresourceRange{
.aspectMask = aspect_mask,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, image_barrier);
});
//Causes device loss!
//scheduler.Finish();
}
struct BlockLinearUnswizzleLinearImagePushConstants {
std::array<u32, 2> origin;
std::array<s32, 2> destination;
u32 bytes_per_block;
u32 pitch;
std::array<u32, 2> valid_extent;
};
BlockLinearUnswizzleLinearImagePass::BlockLinearUnswizzleLinearImagePass(const Device& device_, Scheduler& scheduler_,
DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
: ComputePass(device_, scheduler_, descriptor_pool_, ASTC_DESCRIPTOR_SET_BINDINGS,
ASTC_PASS_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY, ASTC_BANK_INFO,
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearUnswizzleLinearImagePushConstants)>,
PITCH_UNSWIZZLE_COMP_SPV),
scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
BlockLinearUnswizzleLinearImagePass::~BlockLinearUnswizzleLinearImagePass() = default;
void BlockLinearUnswizzleLinearImagePass::Unswizzle(Image& image, const StagingBufferRef& map,
std::span<const VideoCommon::SwizzleParameters> swizzles) {
scheduler.RequestOutsideRenderPassOperationContext();
const VkPipeline vk_pipeline = *pipeline;
const VkImageAspectFlags aspect_mask = image.AspectMask();
const VkImage vk_image = image.Handle();
const bool is_initialized = image.ExchangeInitialization();
scheduler.Record([vk_pipeline, vk_image, aspect_mask,
is_initialized](vk::CommandBuffer cmdbuf) {
const VkImageMemoryBarrier image_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = static_cast<VkAccessFlags>(is_initialized ? VK_ACCESS_SHADER_WRITE_BIT
: VK_ACCESS_NONE),
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
.oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = vk_image,
.subresourceRange{
.aspectMask = aspect_mask,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
cmdbuf.PipelineBarrier(is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER
: VkPipelineStageFlags(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT),
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline);
});
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(image.info.format);
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
const size_t input_offset = swizzle.buffer_offset + map.offset;
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32u);
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32u);
compute_pass_descriptor_queue.Acquire(scheduler, 2);
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
image.guest_size_bytes - swizzle.buffer_offset);
compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
const s32 image_layers = image.info.resources.layers;
const BlockLinearUnswizzleLinearImagePushConstants params{
.origin = {0, 0},
.destination = {0, 0},
.bytes_per_block = bytes_per_block,
.pitch = image.info.pitch,
.valid_extent = {swizzle.num_tiles.width, swizzle.num_tiles.height}
};
scheduler.Record([this, num_dispatches_x, num_dispatches_y, image_layers, params,
descriptor_data](vk::CommandBuffer cmdbuf) {
const VkDescriptorSet set = descriptor_allocator.Commit();
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params);
cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, image_layers);
});
}
scheduler.Record([vk_image, aspect_mask](vk::CommandBuffer cmdbuf) {
const VkImageMemoryBarrier image_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = vk_image,
.subresourceRange{
.aspectMask = aspect_mask,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, image_barrier);
});
//Causes device loss!
//scheduler.Finish();
}
constexpr u32 BL3D_BINDING_INPUT_BUFFER = 0; constexpr u32 BL3D_BINDING_INPUT_BUFFER = 0;
constexpr u32 BL3D_BINDING_OUTPUT_BUFFER = 1; constexpr u32 BL3D_BINDING_OUTPUT_BUFFER = 1;
@ -708,7 +1097,7 @@ void BlockLinearUnswizzle3DPass::Unswizzle(
{ {
using namespace VideoCommon::Accelerated; using namespace VideoCommon::Accelerated;
const u32 MAX_BATCH_SLICES = (std::min)(z_count, image.info.size.depth);
const u32 MAX_BATCH_SLICES = (std::clamp)(z_count, 1u, image.info.size.depth);
static constexpr u32 MAX_WINDOW_SLICES = 4; static constexpr u32 MAX_WINDOW_SLICES = 4;
static constexpr float AREA_GROWTH_LIMIT = 3.0f; static constexpr float AREA_GROWTH_LIMIT = 3.0f;
@ -720,8 +1109,8 @@ void BlockLinearUnswizzle3DPass::Unswizzle(
const auto& sw = swizzles[0]; const auto& sw = swizzles[0];
const auto params = MakeBlockLinearSwizzle3DParams(sw, image.info); const auto params = MakeBlockLinearSwizzle3DParams(sw, image.info);
const u32 blocks_x = (image.info.size.width + 3) / 4;
const u32 blocks_y = (image.info.size.height + 3) / 4;
const u32 blocks_x = sw.num_tiles.width;
const u32 blocks_y = sw.num_tiles.height;
const VkImageLayout initial_prior_layout = image_already_uploaded const VkImageLayout initial_prior_layout = image_already_uploaded
? VK_IMAGE_LAYOUT_GENERAL ? VK_IMAGE_LAYOUT_GENERAL
@ -729,7 +1118,7 @@ void BlockLinearUnswizzle3DPass::Unswizzle(
scheduler.RequestOutsideRenderPassOperationContext(); scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([dst_image = image.Handle(), aspect = image.AspectMask(), initial_prior_layout](vk::CommandBuffer cmdbuf) {
scheduler.Record([dst_image = image.Handle(), aspect = image.AspectMask(), initial_prior_layout, level = sw.level](vk::CommandBuffer cmdbuf) {
if (dst_image == VK_NULL_HANDLE) { if (dst_image == VK_NULL_HANDLE) {
return; return;
} }
@ -749,7 +1138,7 @@ void BlockLinearUnswizzle3DPass::Unswizzle(
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image, .image = dst_image,
.subresourceRange = {aspect, 0, 1, 0, 1},
.subresourceRange = {aspect, static_cast<u32>(level), 1, 0, 1},
}; };
cmdbuf.PipelineBarrier(src_stage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, pre_barrier); cmdbuf.PipelineBarrier(src_stage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, pre_barrier);
@ -868,7 +1257,7 @@ void BlockLinearUnswizzle3DPass::Unswizzle(
} }
} }
scheduler.Record([dst_image = image.Handle(), aspect = image.AspectMask()](vk::CommandBuffer cmdbuf) {
scheduler.Record([dst_image = image.Handle(), aspect = image.AspectMask(), level = sw.level](vk::CommandBuffer cmdbuf) {
if (dst_image == VK_NULL_HANDLE) return; if (dst_image == VK_NULL_HANDLE) return;
const VkImageMemoryBarrier post_barrier{ const VkImageMemoryBarrier post_barrier{
@ -881,7 +1270,7 @@ void BlockLinearUnswizzle3DPass::Unswizzle(
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image, .image = dst_image,
.subresourceRange = {aspect, 0, 1, 0, 1},
.subresourceRange = {aspect, static_cast<u32>(level), 1, 0, 1},
}; };
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
@ -946,20 +1335,39 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
const VkBuffer out_buffer = *image.compute_unswizzle_buffer; const VkBuffer out_buffer = *image.compute_unswizzle_buffer;
const VkImage dst_image = image.Handle(); const VkImage dst_image = image.Handle();
const VkImageAspectFlags aspect = image.AspectMask(); const VkImageAspectFlags aspect = image.AspectMask();
const u32 level = sw.level;
const s32 dst_x = static_cast<s32>(origin_x * 4u); const s32 dst_x = static_cast<s32>(origin_x * 4u);
const s32 dst_y = static_cast<s32>(origin_y * 4u); const s32 dst_y = static_cast<s32>(origin_y * 4u);
const u32 copy_width = extent_x * 4u;
const u32 copy_height = extent_y * 4u;
const VideoCommon::Extent3D level_size = VideoCommon::MipSize(image.info.size, level);
const u32 level_width = (std::max)(level_size.width, 1u);
const u32 level_height = (std::max)(level_size.height, 1u);
const u32 copy_width = (std::min)(extent_x * 4u, level_width - (std::min)(level_width, static_cast<u32>(dst_x)));
const u32 copy_height = (std::min)(extent_y * 4u, level_height - (std::min)(level_height, static_cast<u32>(dst_y)));
scheduler.Record([this, set, descriptor_data, pc, gx, gy, gz, scheduler.Record([this, set, descriptor_data, pc, gx, gy, gz,
z_dst, z_count, barrier_size, z_dst, z_count, barrier_size,
out_buffer, dst_image, aspect,
out_buffer, dst_image, aspect, level,
dst_x, dst_y, copy_width, copy_height](vk::CommandBuffer cmdbuf) { dst_x, dst_y, copy_width, copy_height](vk::CommandBuffer cmdbuf) {
if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) { if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) {
return; return;
} }
const VkBufferMemoryBarrier war_barrier{
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = out_buffer,
.offset = 0,
.size = VK_WHOLE_SIZE,
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0, war_barrier);
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
@ -985,7 +1393,7 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
.bufferOffset = 0, // Read from start of staging buffer .bufferOffset = 0, // Read from start of staging buffer
.bufferRowLength = 0, .bufferRowLength = 0,
.bufferImageHeight = 0, .bufferImageHeight = 0,
.imageSubresource = {aspect, 0, 0, 1},
.imageSubresource = {aspect, level, 0, 1},
.imageOffset = {dst_x, dst_y, static_cast<s32>(z_dst)}, .imageOffset = {dst_x, dst_y, static_cast<s32>(z_dst)},
.imageExtent = {copy_width, copy_height, z_count}, .imageExtent = {copy_width, copy_height, z_count},
}; };
@ -1154,6 +1562,274 @@ void BlockLinearUnswizzle3DPass::UnswizzleZeroChunk(
}); });
} }
struct alignas(16) BlockLinearUnswizzle2DPushConstants {
u32 origin[3];
u32 padding1;
s32 destination[3];
u32 bytes_per_block_log2;
u32 layer_stride;
u32 block_size;
u32 x_shift;
u32 block_height;
u32 block_height_mask;
u32 padding2[3];
u32 valid_extent[3];
u32 out_pitch;
u32 out_layer_stride;
u32 padding3[3];
};
BlockLinearUnswizzle2DPass::BlockLinearUnswizzle2DPass(
const Device& device_, Scheduler& scheduler_,
DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
: ComputePass(device_, scheduler_, descriptor_pool_,
std::array<VkDescriptorSetLayoutBinding, 2>{{
{
.binding = BL3D_BINDING_INPUT_BUFFER,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
{
.binding = BL3D_BINDING_OUTPUT_BUFFER,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
}},
std::array<VkDescriptorUpdateTemplateEntry, 2>{{
{
.dstBinding = BL3D_BINDING_INPUT_BUFFER,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = BL3D_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
},
{
.dstBinding = BL3D_BINDING_OUTPUT_BUFFER,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = BL3D_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
}
}},
DescriptorBankInfo{
.uniform_buffers = 0,
.storage_buffers = 2,
.texture_buffers = 0,
.image_buffers = 0,
.textures = 0,
.images = 0,
.score = 2,
},
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearUnswizzle2DPushConstants)>,
BLOCK_LINEAR_UNSWIZZLE_2D_BCN_COMP_SPV),
scheduler{scheduler_},
staging_buffer_pool{staging_buffer_pool_},
compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
BlockLinearUnswizzle2DPass::~BlockLinearUnswizzle2DPass() = default;
void BlockLinearUnswizzle2DPass::Unswizzle(
Image& image, const StagingBufferRef& map,
std::span<const VideoCommon::SwizzleParameters> swizzles) {
scheduler.RequestOutsideRenderPassOperationContext();
const VkPipeline vk_pipeline = *pipeline;
const VkImageAspectFlags aspect_mask = image.AspectMask();
const VkImage vk_image = image.Handle();
const bool is_initialized = image.ExchangeInitialization();
scheduler.Record([vk_image, aspect_mask, is_initialized, vk_pipeline](vk::CommandBuffer cmdbuf) {
if (vk_image == VK_NULL_HANDLE) {
return;
}
const VkAccessFlags src_access =
is_initialized ? (VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT) : 0u;
const VkPipelineStageFlags src_stage = is_initialized
? (VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT)
: VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
const VkImageMemoryBarrier pre_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = src_access,
.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 = vk_image,
.subresourceRange = {aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0,
VK_REMAINING_ARRAY_LAYERS},
};
cmdbuf.PipelineBarrier(src_stage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, pre_barrier);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline);
});
for (const VideoCommon::SwizzleParameters& sw : swizzles) {
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(image.info.format);
const u32 out_pitch = sw.num_tiles.width * bytes_per_block;
const u32 out_layer_stride = sw.num_tiles.height * out_pitch;
const u32 out_tile_height = sw.num_tiles.height;
const VkDeviceSize required_size = static_cast<VkDeviceSize>(out_layer_stride) * image.info.resources.layers;
if (!image.has_compute_unswizzle_buffer || required_size > image.compute_unswizzle_buffer_size) {
image.compute_unswizzle_buffer_size = required_size;
VkBufferCreateInfo ci{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.size = image.compute_unswizzle_buffer_size,
.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
};
image.compute_unswizzle_buffer =
image.runtime->memory_allocator.CreateBuffer(ci, MemoryUsage::DeviceLocal);
image.has_compute_unswizzle_buffer = true;
image.compute_unswizzle_buffer_is_zero = false;
}
const size_t input_offset = sw.buffer_offset + map.offset;
const u32 num_dispatches_x = Common::DivCeil(sw.num_tiles.width, 32u);
const u32 num_dispatches_y = Common::DivCeil(sw.num_tiles.height, 32u);
compute_pass_descriptor_queue.Acquire(scheduler, 2);
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
image.guest_size_bytes - sw.buffer_offset);
compute_pass_descriptor_queue.AddBuffer(*image.compute_unswizzle_buffer, 0,
image.compute_unswizzle_buffer_size);
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
const auto params = VideoCommon::Accelerated::MakeBlockLinearSwizzle2DParams(sw, image.info);
const std::array<u32, 3> valid_extent{sw.num_tiles.width, sw.num_tiles.height, 1u};
const VkBuffer out_buffer = *image.compute_unswizzle_buffer;
const VkImage dst_image = vk_image;
const u32 level = sw.level;
const s32 image_layers = image.info.resources.layers;
scheduler.Record([out_buffer](vk::CommandBuffer cmdbuf) {
const VkBufferMemoryBarrier war_barrier{
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = out_buffer,
.offset = 0,
.size = VK_WHOLE_SIZE,
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, war_barrier);
});
scheduler.Record([this, num_dispatches_x, num_dispatches_y, image_layers, params, valid_extent,
out_pitch, out_layer_stride, descriptor_data](vk::CommandBuffer cmdbuf) {
const BlockLinearUnswizzle2DPushConstants uniforms{
.origin = {params.origin[0], params.origin[1], params.origin[2]},
.destination = {params.destination[0], params.destination[1],
params.destination[2]},
.bytes_per_block_log2 = params.bytes_per_block_log2,
.layer_stride = params.layer_stride,
.block_size = params.block_size,
.x_shift = params.x_shift,
.block_height = params.block_height,
.block_height_mask = params.block_height_mask,
.valid_extent = {valid_extent[0], valid_extent[1], valid_extent[2]},
.out_pitch = out_pitch,
.out_layer_stride = out_layer_stride,
};
const VkDescriptorSet set = descriptor_allocator.Commit();
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, uniforms);
cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, image_layers);
});
const VideoCommon::Extent3D level_size = VideoCommon::MipSize(image.info.size, level);
const u32 level_width = (std::max)(level_size.width, 1u);
const u32 level_height = (std::max)(level_size.height, 1u);
const PixelFormat image_format = image.info.format;
scheduler.Record([out_buffer, dst_image, aspect_mask, level, image_layers, out_tile_height, out_pitch, bytes_per_block,
level_width, level_height, image_format](vk::CommandBuffer cmdbuf) {
if (dst_image == VK_NULL_HANDLE) {
return;
}
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 = 0,
.size = VK_WHOLE_SIZE,
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, buffer_barrier);
const u32 block_width = VideoCore::Surface::DefaultBlockWidth(image_format);
const u32 block_height = VideoCore::Surface::DefaultBlockHeight(image_format);
const VkBufferImageCopy copy{
.bufferOffset = 0,
.bufferRowLength = (out_pitch / bytes_per_block) * block_width,
.bufferImageHeight = out_tile_height * block_height,
.imageSubresource = {aspect_mask, level, 0, static_cast<u32>(image_layers)},
.imageOffset = {0, 0, 0},
.imageExtent = {level_width, level_height, 1},
};
cmdbuf.CopyBufferToImage(out_buffer, dst_image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
});
}
scheduler.Record([vk_image, aspect_mask](vk::CommandBuffer cmdbuf) {
if (vk_image == VK_NULL_HANDLE) {
return;
}
const VkImageMemoryBarrier post_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = vk_image,
.subresourceRange = {aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0,
VK_REMAINING_ARRAY_LAYERS},
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0, post_barrier);
});
//Causes device loss!
//scheduler.Finish();
}
MSAACopyPass::MSAACopyPass(const Device& device_, Scheduler& scheduler_, MSAACopyPass::MSAACopyPass(const Device& device_, Scheduler& scheduler_,
DescriptorPool& descriptor_pool_, DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_, StagingBufferPool& staging_buffer_pool_,

106
src/video_core/renderer_vulkan/vk_compute_pass.h

@ -172,4 +172,110 @@ private:
ComputePassDescriptorQueue& compute_pass_descriptor_queue; ComputePassDescriptorQueue& compute_pass_descriptor_queue;
}; };
class MSAACopyPass final : public ComputePass {
public:
explicit MSAACopyPass(const Device& device_, Scheduler& scheduler_,
DescriptorPool& descriptor_pool_, StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
~MSAACopyPass();
void CopyImage(Image& dst_image, Image& src_image,
std::span<const VideoCommon::ImageCopy> copies, bool msaa_to_non_msaa);
private:
Scheduler& scheduler;
StagingBufferPool& staging_buffer_pool;
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
std::array<vk::ShaderModule, 2> modules;
std::array<vk::Pipeline, 2> pipelines;
};
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();
void Unswizzle(Image& image, const StagingBufferRef& map,
std::span<const VideoCommon::SwizzleParameters> swizzles);
private:
Scheduler& scheduler;
StagingBufferPool& staging_buffer_pool;
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
};
class BlockLinearUnswizzle2DImagePass final : public ComputePass {
public:
explicit BlockLinearUnswizzle2DImagePass(const Device& device_, Scheduler& scheduler_,
DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
~BlockLinearUnswizzle2DImagePass();
void Unswizzle(Image& image, const StagingBufferRef& map,
std::span<const VideoCommon::SwizzleParameters> swizzles);
private:
Scheduler& scheduler;
StagingBufferPool& staging_buffer_pool;
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
};
class BlockLinearUnswizzle3DImagePass final : public ComputePass {
public:
explicit BlockLinearUnswizzle3DImagePass(const Device& device_, Scheduler& scheduler_,
DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
~BlockLinearUnswizzle3DImagePass();
void Unswizzle(Image& image, const StagingBufferRef& map,
std::span<const VideoCommon::SwizzleParameters> swizzles,
u32 z_src = 0, u32 z_image_start = 0);
private:
Scheduler& scheduler;
StagingBufferPool& staging_buffer_pool;
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
};
class BlockLinearUnswizzleLinearImagePass final : public ComputePass {
public:
explicit BlockLinearUnswizzleLinearImagePass(const Device& device_, Scheduler& scheduler_,
DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
~BlockLinearUnswizzleLinearImagePass();
void Unswizzle(Image& image, const StagingBufferRef& map,
std::span<const VideoCommon::SwizzleParameters> swizzles);
private:
Scheduler& scheduler;
StagingBufferPool& staging_buffer_pool;
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
};
class MSAACopyPass final : public ComputePass {
public:
explicit MSAACopyPass(const Device& device_, Scheduler& scheduler_,
DescriptorPool& descriptor_pool_, StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
~MSAACopyPass();
void CopyImage(Image& dst_image, Image& src_image,
std::span<const VideoCommon::ImageCopy> copies, bool msaa_to_non_msaa);
private:
Scheduler& scheduler;
StagingBufferPool& staging_buffer_pool;
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
std::array<vk::ShaderModule, 2> modules;
std::array<vk::Pipeline, 2> pipelines;
};
} // namespace Vulkan } // namespace Vulkan

178
src/video_core/renderer_vulkan/vk_texture_cache.cpp

@ -185,6 +185,20 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
}; };
} }
[[nodiscard]] std::optional<VkFormat> BlockTexelViewFormat(PixelFormat format) {
if (!VideoCore::Surface::IsPixelFormatBCn(format)) {
return std::nullopt;
}
switch (VideoCore::Surface::BytesPerBlock(format)) {
case 8:
return VK_FORMAT_R32G32_UINT;
case 16:
return VK_FORMAT_R32G32B32A32_UINT;
default:
return std::nullopt;
}
}
[[nodiscard]] vk::Image MakeImage(const Device& device, const MemoryAllocator& allocator, [[nodiscard]] vk::Image MakeImage(const Device& device, const MemoryAllocator& allocator,
const ImageInfo& info, std::span<const VkFormat> view_formats, const ImageInfo& info, std::span<const VkFormat> view_formats,
std::optional<VkFormat> format_override = {}) { std::optional<VkFormat> format_override = {}) {
@ -202,12 +216,18 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
image_ci.flags |= image_ci.flags |=
VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT; VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;
bool is_block_format = false;
if (BlockTexelViewFormat(info.format)) {
image_ci.flags |= VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT;
is_block_format = true;
}
const bool has_storage_compatible_view = const bool has_storage_compatible_view =
std::any_of(view_formats.begin(), view_formats.end(), [&device](VkFormat view_format) { std::any_of(view_formats.begin(), view_formats.end(), [&device](VkFormat view_format) {
return device.IsFormatSupported(view_format, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT, return device.IsFormatSupported(view_format, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT,
FormatType::Optimal); FormatType::Optimal);
}); });
if (has_storage_compatible_view) {
if (has_storage_compatible_view && !is_block_format) {
image_ci.usage |= VK_IMAGE_USAGE_STORAGE_BIT; image_ci.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
} }
@ -218,6 +238,24 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
return allocator.CreateImage(image_ci); return allocator.CreateImage(image_ci);
} }
[[nodiscard]] VkFormat UnswizzleStorageFormat(u32 bytes_per_block) {
switch (bytes_per_block) {
case 1:
return VK_FORMAT_R8_UINT;
case 2:
return VK_FORMAT_R16_UINT;
case 4:
return VK_FORMAT_R32_UINT;
case 8:
return VK_FORMAT_R32G32_UINT;
case 16:
return VK_FORMAT_R32G32B32A32_UINT;
default:
ASSERT_MSG(false, "Invalid bytes_per_block={} for accelerated unswizzle", bytes_per_block);
return VK_FORMAT_R32_UINT;
}
}
[[nodiscard]] vk::ImageView MakeStorageView(const vk::Device& device, u32 level, VkImage image, [[nodiscard]] vk::ImageView MakeStorageView(const vk::Device& device, u32 level, VkImage image,
VkFormat format) { VkFormat format) {
static constexpr VkImageViewUsageCreateInfo storage_image_view_usage_create_info{ static constexpr VkImageViewUsageCreateInfo storage_image_view_usage_create_info{
@ -918,6 +956,11 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
const auto image_format = static_cast<PixelFormat>(index_a); const auto image_format = static_cast<PixelFormat>(index_a);
if (IsPixelFormatASTC(image_format) && !device.IsOptimalAstcSupported()) { if (IsPixelFormatASTC(image_format) && !device.IsOptimalAstcSupported()) {
view_formats[index_a].push_back(VK_FORMAT_A8B8G8R8_UNORM_PACK32); view_formats[index_a].push_back(VK_FORMAT_A8B8G8R8_UNORM_PACK32);
} else if (!IsPixelFormatASTC(image_format) && !IsPixelFormatBCn(image_format)) {
if (IsUnswizzleStorageFormatSupported(image_format)) {
const u32 bpp = VideoCore::Surface::BytesPerBlock(image_format);
view_formats[index_a].push_back(UnswizzleStorageFormat(bpp));
}
} }
for (size_t index_b = 0; index_b < VideoCore::Surface::MaxPixelFormat; index_b++) { for (size_t index_b = 0; index_b < VideoCore::Surface::MaxPixelFormat; index_b++) {
const auto view_format = static_cast<PixelFormat>(index_b); const auto view_format = static_cast<PixelFormat>(index_b);
@ -930,11 +973,62 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
} }
if (Settings::values.gpu_unswizzle_enabled.GetValue()) { if (Settings::values.gpu_unswizzle_enabled.GetValue()) {
bl2d_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
staging_buffer_pool, compute_pass_descriptor_queue);
bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool, bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
staging_buffer_pool, compute_pass_descriptor_queue); staging_buffer_pool, compute_pass_descriptor_queue);
generic_2d_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
staging_buffer_pool, compute_pass_descriptor_queue);
generic_3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
staging_buffer_pool, compute_pass_descriptor_queue);
generic_linear_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
staging_buffer_pool, compute_pass_descriptor_queue);
} }
} }
[nodicard] bool ByteWidthSupported(u32 bpp, const Device& device) {
switch (bpp) {
case 1:
return device.IsStorageBuffer8BitAccessSupported();
case 2:
return device.IsStorageBuffer16BitAccessSupported();
case 4:
case 8:
case 16:
return true;
default:
return false;
}
}
bool TextureCacheRuntime::IsUnswizzleStorageFormatSupported(PixelFormat format) const {
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(format);
if (!ByteWidthSupported(bytes_per_block, device)) {
return false;
}
PixelFormat reference_format;
switch (bytes_per_block) {
case 1:
reference_format = PixelFormat::R8_UINT;
break;
case 2:
reference_format = PixelFormat::R16_UINT;
break;
case 4:
reference_format = PixelFormat::R32_UINT;
break;
case 8:
reference_format = PixelFormat::R32G32_UINT;
break;
case 16:
reference_format = PixelFormat::R32G32B32A32_UINT;
break;
default:
return false;
}
return VideoCore::Surface::IsViewCompatible(format, reference_format, false, true);
}
void TextureCacheRuntime::Finish() { void TextureCacheRuntime::Finish() {
scheduler.Finish(); scheduler.Finish();
} }
@ -1772,6 +1866,8 @@ void TextureCacheRuntime::TickFrame() {
sentenced_unswizzle_buffers.Tick(); sentenced_unswizzle_buffers.Tick();
} }
// Is this even needed? I wasn't sure if the buffer was cleared when done with,
// someone who knows how these systems works better than me could probably explain
void TextureCacheRuntime::ReleaseSparseUnswizzleBuffer(Image& image) { void TextureCacheRuntime::ReleaseSparseUnswizzleBuffer(Image& image) {
if (image.has_compute_unswizzle_buffer) { if (image.has_compute_unswizzle_buffer) {
sentenced_unswizzle_buffers.Push(std::move(image.compute_unswizzle_buffer)); sentenced_unswizzle_buffers.Push(std::move(image.compute_unswizzle_buffer));
@ -1784,11 +1880,36 @@ void TextureCacheRuntime::ReleaseSparseUnswizzleBuffer(Image& image) {
}); });
} }
[[nodiscard]] boost::container::small_vector<VkFormat, 8> BuildViewFormats(
const ImageInfo& info, std::span<const VkFormat> base_view_formats) {
boost::container::small_vector<VkFormat, 8> formats(base_view_formats.begin(),
base_view_formats.end());
if (Settings::values.gpu_unswizzle_enabled.GetValue()) {
if (const auto block_view_format = BlockTexelViewFormat(info.format)) {
formats.push_back(*block_view_format);
}
}
return formats;
}
Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_, Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_,
VAddr cpu_addr_) VAddr cpu_addr_)
: VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_), scheduler{&runtime_.scheduler}, : VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_), scheduler{&runtime_.scheduler},
runtime{&runtime_}, runtime{&runtime_},
original_image(MakeImage(runtime_.device, runtime_.memory_allocator, info, original_image(MakeImage(runtime_.device, runtime_.memory_allocator, info,
WillUseWidenedAstcFormat(runtime_.device, info)
? std::span<const VkFormat>{}
: runtime->ViewFormats(info.format),
WillUseWidenedAstcFormat(runtime_.device, info)
? std::make_optional(VK_FORMAT_R32G32B32A32_SFLOAT)
: std::nullopt)),
WillUseWidenedAstcFormat(runtime_.device, info)
? std::span<const VkFormat>{}
: std::span<const VkFormat>(BuildViewFormats(
info, runtime->ViewFormats(info.format))),
WillUseWidenedAstcFormat(runtime_.device, info)
? std::make_optional(VK_FORMAT_R32G32B32A32_SFLOAT)
: std::nullopt)),
runtime->ViewFormats(info.format))), runtime->ViewFormats(info.format))),
aspect_mask(ImageAspectMask(info.format)) { aspect_mask(ImageAspectMask(info.format)) {
if (IsPixelFormatASTC(info.format) && !runtime->device.IsOptimalAstcSupported()) { if (IsPixelFormatASTC(info.format) && !runtime->device.IsOptimalAstcSupported()) {
@ -1811,6 +1932,10 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
flags |= VideoCommon::ImageFlagBits::Converted; flags |= VideoCommon::ImageFlagBits::Converted;
flags |= VideoCommon::ImageFlagBits::CostlyLoad; flags |= VideoCommon::ImageFlagBits::CostlyLoad;
} }
if (runtime->IsUnswizzleStorageFormatSupported(info.format) &&
(info.type == ImageType::e2D || info.type == ImageType::e3D || info.type == ImageType::Linear)) {
flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
}
if (runtime->device.HasDebuggingToolAttached()) { if (runtime->device.HasDebuggingToolAttached()) {
original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str()); original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str());
} }
@ -1823,6 +1948,14 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
storage_image_views[level] = storage_image_views[level] =
MakeStorageView(device, level, *original_image, storage_format); MakeStorageView(device, level, *original_image, storage_format);
} }
} else if (True(flags & VideoCommon::ImageFlagBits::AcceleratedUpload) && runtime->IsUnswizzleStorageFormatSupported(info.format)) {
const auto& device = runtime->device.GetLogical();
const VkFormat storage_format =
UnswizzleStorageFormat(VideoCore::Surface::BytesPerBlock(info.format));
for (s32 level = 0; level < info.resources.levels; ++level) {
storage_image_views[level] =
MakeStorageView(device, level, *original_image, storage_format);
}
} }
} }
@ -1843,8 +1976,8 @@ void Image::AllocateComputeUnswizzleBuffer(u32 max_slices) {
using VideoCore::Surface::BytesPerBlock; using VideoCore::Surface::BytesPerBlock;
const u32 block_bytes = BytesPerBlock(info.format); // 8 for BC1, 16 for BC6H const u32 block_bytes = BytesPerBlock(info.format); // 8 for BC1, 16 for BC6H
const u32 block_width = 4;
const u32 block_height = 4;
const u32 block_width = VideoCore::Surface::DefaultBlockWidth(info.format);
const u32 block_height = VideoCore::Surface::DefaultBlockHeight(info.format);
// BCn is 4x4x1 blocks // BCn is 4x4x1 blocks
const u32 blocks_x = (info.size.width + block_width - 1) / block_width; const u32 blocks_x = (info.size.width + block_width - 1) / block_width;
@ -2205,6 +2338,14 @@ VkImageView Image::StorageImageView(s32 level) noexcept {
auto format_info = auto format_info =
MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, true, info.format); MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, true, info.format);
if (WillUseAcceleratedAstcDecode(runtime->device, info)) { if (WillUseAcceleratedAstcDecode(runtime->device, info)) {
format_info.format = WillUseWidenedAstcFormat(runtime->device, info)
? VK_FORMAT_R32G32B32A32_SFLOAT
: VK_FORMAT_A8B8G8R8_UNORM_PACK32;
format_info.format = WillUseWidenedAstcFormat(runtime->device, info)
? VK_FORMAT_R32G32B32A32_SFLOAT
: VK_FORMAT_A8B8G8R8_UNORM_PACK32;
} else if (const auto block_view_format = BlockTexelViewFormat(info.format)) {
format_info.format = *block_view_format;
format_info.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32; format_info.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
} }
view = MakeStorageView(runtime->device.GetLogical(), level, *(this->*current_image), view = MakeStorageView(runtime->device.GetLogical(), level, *(this->*current_image),
@ -2848,33 +2989,26 @@ VkRenderPass Framebuffer::RenderPassVariant(u32 color_clear_mask, bool depth_ste
void TextureCacheRuntime::AccelerateImageUpload( void TextureCacheRuntime::AccelerateImageUpload(
Image& image, const StagingBufferRef& map, Image& image, const StagingBufferRef& map,
std::span<const VideoCommon::SwizzleParameters> swizzles, std::span<const VideoCommon::SwizzleParameters> swizzles,
u32 z_src_start, u32 z_image_start, u32 z_count,
std::span<const u8> slice_has_data,
std::span<const VideoCommon::Accelerated::SliceBBox> slice_bounds,
bool image_already_uploaded) {
u32 z_src_start, u32 z_image_start) {
if (IsPixelFormatASTC(image.info.format)) { if (IsPixelFormatASTC(image.info.format)) {
return astc_decoder_pass->Assemble(image, map, swizzles); return astc_decoder_pass->Assemble(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");
}
ASSERT(false);
if (!Settings::values.gpu_unswizzle_enabled.GetValue() || (!generic_2d_unswizzle_pass && !generic_3d_unswizzle_pass)) {
ASSERT(false && "GPU unswizzle is disabled for this texture");
return; return;
} }
if (bl3d_unswizzle_pass &&
IsPixelFormatBCn(image.info.format) &&
image.info.type == ImageType::e3D &&
image.info.resources.levels == 1 &&
image.info.resources.layers == 1) {
return bl3d_unswizzle_pass->Unswizzle(image, map, swizzles,
z_src_start, z_image_start, z_count,
slice_has_data, slice_bounds,
image_already_uploaded);
if (image.info.type == ImageType::e2D && generic_2d_unswizzle_pass) {
return generic_2d_unswizzle_pass->Unswizzle(image, map, swizzles);
}
if (image.info.type == ImageType::e3D && generic_3d_unswizzle_pass) {
return generic_3d_unswizzle_pass->Unswizzle(image, map, swizzles,
z_src_start, z_image_start);
}
if (image.info.type == ImageType::Linear && generic_linear_unswizzle_pass) {
return generic_linear_unswizzle_pass->Unswizzle(image, map, swizzles);
} }
ASSERT(false); ASSERT(false);

23
src/video_core/renderer_vulkan/vk_texture_cache.h

@ -51,6 +51,8 @@ public:
DescriptorPool& descriptor_pool, DescriptorPool& descriptor_pool,
ComputePassDescriptorQueue& compute_pass_descriptor_queue); ComputePassDescriptorQueue& compute_pass_descriptor_queue);
bool IsUnswizzleStorageFormatSupported(PixelFormat format) const;
void Finish(); void Finish();
StagingBufferRef UploadStagingBuffer(size_t size, bool deferred = false); StagingBufferRef UploadStagingBuffer(size_t size, bool deferred = false);
@ -94,12 +96,14 @@ public:
return true; return true;
} }
void AccelerateImageUpload(Image&, const StagingBufferRef&,
std::span<const VideoCommon::SwizzleParameters>,
u32 z_src_start, u32 z_image_start, u32 z_count,
std::span<const u8> slice_has_data = {},
std::span<const VideoCommon::Accelerated::SliceBBox> slice_bounds = {},
bool image_already_uploaded = false);
bool CanAccelerateUnswizzle() const noexcept {
return Settings::values.accelerate_unswizzle.GetValue() ==
Settings::TexUnswizzleMode::Gpu;
}
void AccelerateImageUpload(Image &, const StagingBufferRef &,
std::span<const VideoCommon::SwizzleParameters>,
u32 z_src_start, u32 z_image_start);
void InsertUploadMemoryBarrier() {} void InsertUploadMemoryBarrier() {}
@ -155,6 +159,12 @@ public:
std::optional<ASTCDecoderPass> astc_decoder_pass; std::optional<ASTCDecoderPass> astc_decoder_pass;
std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass; std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass;
std::optional<MSAACopyPass> msaa_copy_pass;
std::optional<BlockLinearUnswizzle2DPass> bl2d_unswizzle_pass;
std::optional<BlockLinearUnswizzle2DImagePass> generic_2d_unswizzle_pass;
std::optional<BlockLinearUnswizzle3DImagePass> generic_3d_unswizzle_pass;
std::optional<BlockLinearUnswizzleLinearImagePass> generic_linear_unswizzle_pass;
std::optional<MSAACopyPass> msaa_copy_pass;
const Settings::ResolutionScalingInfo& resolution; const Settings::ResolutionScalingInfo& resolution;
std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats; std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats;
@ -332,6 +342,7 @@ public:
u64 allocation_tick; u64 allocation_tick;
friend class BlockLinearUnswizzle3DPass; friend class BlockLinearUnswizzle3DPass;
friend class BlockLinearUnswizzle2DPass;
friend class TextureCacheRuntime; friend class TextureCacheRuntime;
private: private:

141
src/video_core/texture_cache/texture_cache.h

@ -1119,9 +1119,7 @@ void TextureCache<P>::RefreshContents(Image& image, ImageId image_id) {
return; return;
} }
const bool gpu_unswizzle_enabled = Settings::values.gpu_unswizzle_enabled.GetValue();
if (gpu_unswizzle_enabled &&
if (Settings::values.gpu_unswizzle_enabled.GetValue() &&
IsPixelFormatBCn(image.info.format) && IsPixelFormatBCn(image.info.format) &&
image.info.type == ImageType::e3D && image.info.type == ImageType::e3D &&
image.info.resources.levels == 1 && image.info.resources.levels == 1 &&
@ -1144,11 +1142,13 @@ void TextureCache<P>::UploadImageContents(Image& image, StagingBuffer& staging)
const GPUVAddr gpu_addr = image.gpu_addr; const GPUVAddr gpu_addr = image.gpu_addr;
if (True(image.flags & ImageFlagBits::AcceleratedUpload)) { if (True(image.flags & ImageFlagBits::AcceleratedUpload)) {
gpu_memory->ReadBlock(gpu_addr, mapped_span.data(), mapped_span.size_bytes(),
VideoCommon::CacheType::NoTextureCache);
const auto uploads = FullUploadSwizzles(image.info);
runtime.AccelerateImageUpload(image, staging, FixSmallVectorADL(uploads), 0, 0, 0);
return;
if (IsPixelFormatASTC(image.info.format) || runtime.CanAccelerateUnswizzle()) {
const auto uploads = FullUploadSwizzles(image.info);
gpu_memory->ReadBlock(gpu_addr, mapped_span.data(), mapped_span.size_bytes(),
VideoCommon::CacheType::NoTextureCache);
runtime.AccelerateImageUpload(image, staging, FixSmallVectorADL(uploads), 0, 0);
return;
}
} }
Tegra::Memory::GpuGuestMemory<u8, Tegra::Memory::GuestMemoryFlags::UnsafeRead> swizzle_data( Tegra::Memory::GpuGuestMemory<u8, Tegra::Memory::GuestMemoryFlags::UnsafeRead> swizzle_data(
@ -1427,65 +1427,31 @@ void TextureCache<P>::TickAsyncUnswizzle() {
gpu_memory->GetSubmappedRange(image.gpu_addr, image.guest_size_bytes); gpu_memory->GetSubmappedRange(image.gpu_addr, image.guest_size_bytes);
task.sparse_segments.assign(segs.begin(), segs.end()); task.sparse_segments.assign(segs.begin(), segs.end());
std::sort(task.sparse_segments.begin(), task.sparse_segments.end(),
[](const auto& a, const auto& b) { return a.first < b.first; });
task.segment_scan_cursor = 0; task.segment_scan_cursor = 0;
task.slice_has_data.assign(image.info.size.depth, 0u); task.slice_has_data.assign(image.info.size.depth, 0u);
task.slice_bounds.assign(image.info.size.depth, {});
if (image.info.size.depth > 1) { if (image.info.size.depth > 1) {
const auto uploads = FullUploadSwizzles(task.info); const auto uploads = FullUploadSwizzles(task.info);
const auto sp = VideoCommon::Accelerated::MakeBlockLinearSwizzle3DParams( const auto sp = VideoCommon::Accelerated::MakeBlockLinearSwizzle3DParams(
uploads[0], task.info); uploads[0], task.info);
const u64 swizzled_slice_size = sp.slice_size;
task.swizzled_slice_size = swizzled_slice_size;
task.swizzle_block_depth = sp.block_depth;
const u32 blocks_x = Common::DivCeil(task.info.size.width, 4u);
const u32 blocks_y = Common::DivCeil(task.info.size.height, 4u);
const u32 zz_count = 1u << sp.block_depth;
const u32 total_groups = Common::DivCeil(
static_cast<u32>(image.info.size.depth), zz_count);
u32 group_watermark = 0;
for (const auto& [seg_gpu_addr, seg_size] : task.sparse_segments) {
const u64 seg_start = seg_gpu_addr - image.gpu_addr;
const u64 seg_end = seg_start + seg_size;
while (group_watermark < total_groups &&
static_cast<u64>(group_watermark) * swizzled_slice_size + swizzled_slice_size <= seg_start) {
++group_watermark;
}
for (u32 g = group_watermark; g < total_groups; ++g) {
const u64 group_base = static_cast<u64>(g) * swizzled_slice_size;
if (group_base >= seg_end) break;
const u64 local_start = (std::max)(seg_start, group_base) - group_base;
const u64 local_end = (std::min)(seg_end, group_base + swizzled_slice_size) - group_base;
VideoCommon::Accelerated::ForEachZInGroupOverlap(
local_start, local_end,
sp.block_size, sp.x_shift, sp.block_height, sp.block_height_mask,
sp.block_depth, sp.block_depth_mask, bytes_per_block, blocks_x, blocks_y,
[&](u32 zz, const VideoCommon::Accelerated::SliceBBox& box) {
const u32 z = g * zz_count + zz;
if (z >= static_cast<u32>(image.info.size.depth)) return;
task.slice_has_data[z] = 1u;
auto& acc = task.slice_bounds[z];
if (acc.x1 <= acc.x0 || acc.y1 <= acc.y0) {
acc = box;
} else {
acc.x0 = (std::min)(acc.x0, box.x0);
acc.y0 = (std::min)(acc.y0, box.y0);
acc.x1 = (std::max)(acc.x1, box.x1);
acc.y1 = (std::max)(acc.y1, box.y1);
}
});
task.swizzle_group_size = sp.slice_size;
task.slices_per_group = 1u << sp.block_depth;
const u32 num_groups = Common::DivCeil(
static_cast<u32>(image.info.size.depth), task.slices_per_group);
task.slice_has_data.assign(num_groups, 0u);
if (task.swizzle_group_size > 0) {
for (const auto& [seg_gpu_addr, seg_size] : task.sparse_segments) {
if (seg_gpu_addr < image.gpu_addr) continue;
const u64 seg_start = seg_gpu_addr - image.gpu_addr;
const u64 seg_end = seg_start + seg_size;
const u32 g_first = static_cast<u32>(seg_start / task.swizzle_group_size);
const u32 g_last = static_cast<u32>((seg_end - 1) / task.swizzle_group_size);
for (u32 g = g_first; g <= g_last && g < num_groups; ++g) {
task.slice_has_data[g] = 1u;
}
} }
} }
} else { } else {
@ -1495,10 +1461,6 @@ void TextureCache<P>::TickAsyncUnswizzle() {
} }
task.initialized = true; task.initialized = true;
task.was_rescaled = image.IsRescaled();
if (task.was_rescaled) {
image.ScaleDown(true);
}
} }
// Read data // Read data
@ -1526,8 +1488,8 @@ void TextureCache<P>::TickAsyncUnswizzle() {
size_t cursor = read_start; size_t cursor = read_start;
const bool can_smart_skip = const bool can_smart_skip =
task.swizzle_block_depth == 0 &&
task.swizzled_slice_size > 0 &&
task.swizzle_group_size > 0 &&
task.slices_per_group > 0 &&
!task.slice_has_data.empty(); !task.slice_has_data.empty();
auto fill_gap = [&](size_t gap_start_rel, size_t gap_end_rel) { auto fill_gap = [&](size_t gap_start_rel, size_t gap_end_rel) {
@ -1539,14 +1501,14 @@ void TextureCache<P>::TickAsyncUnswizzle() {
size_t pos = gap_start_rel; size_t pos = gap_start_rel;
while (pos < gap_end_rel) { while (pos < gap_end_rel) {
const u64 abs_pos = pos + base_off; const u64 abs_pos = pos + base_off;
const u32 z_abs = static_cast<u32>(abs_pos / task.swizzled_slice_size);
const u32 z_in_bm = z_abs - task.incremental_z_start;
if (z_in_bm >= static_cast<u32>(task.slice_has_data.size())) break;
const size_t slice_abs_end =
(static_cast<size_t>(z_abs) + 1) * task.swizzled_slice_size;
const size_t end_rel = (std::min)(gap_end_rel,
slice_abs_end - base_off);
if (task.slice_has_data[z_in_bm])
const u32 z_group = static_cast<u32>(abs_pos / task.swizzle_group_size);
const u32 z_group_local = z_group -
(task.incremental_z_start / task.slices_per_group);
if (z_group_local >= static_cast<u32>(task.slice_has_data.size())) break;
const size_t group_abs_end =
(static_cast<size_t>(z_group) + 1) * task.swizzle_group_size;
const size_t end_rel = (std::min)(gap_end_rel, group_abs_end - base_off);
if (task.slice_has_data[z_group_local])
std::memset(staging_base + pos, 0, end_rel - pos); std::memset(staging_base + pos, 0, end_rel - pos);
pos = end_rel; pos = end_rel;
} }
@ -1589,12 +1551,12 @@ void TextureCache<P>::TickAsyncUnswizzle() {
} }
const bool is_final_batch = task.current_offset >= task.total_size; const bool is_final_batch = task.current_offset >= task.total_size;
const size_t bytes_ready = task.current_offset - task.last_submitted_offset;
const u32 complete_slices = static_cast<u32>(bytes_ready / task.bytes_per_slice);
const bool is_sparse_3d = task.is_sparse && task.swizzle_group_size > 0 && task.slices_per_group > 0;
const size_t effective_group_size = is_sparse_3d ? task.swizzle_group_size : task.bytes_per_slice;
const u32 slices_per_group_val = is_sparse_3d ? task.slices_per_group : 1u;
const std::span<const u8> sparse_hint =
task.is_sparse ? std::span<const u8>(task.slice_has_data)
: std::span<const u8>{};
const size_t bytes_ready = task.current_offset - task.last_submitted_offset;
const u32 complete_slices = static_cast<u32>(bytes_ready / effective_group_size) * slices_per_group_val;
const u32 total_slices = task.is_incremental const u32 total_slices = task.is_incremental
? task.incremental_z_count ? task.incremental_z_count
@ -1603,37 +1565,34 @@ void TextureCache<P>::TickAsyncUnswizzle() {
? task.incremental_z_count ? task.incremental_z_count
: (swizzle_slices_per_batch == 0 ? image.info.size.depth : swizzle_slices_per_batch); : (swizzle_slices_per_batch == 0 ? image.info.size.depth : swizzle_slices_per_batch);
if (complete_slices >= batch || (is_final_batch && complete_slices > 0)) {
const u32 z_src = static_cast<u32>(task.last_submitted_offset / task.bytes_per_slice);
const u32 z_image = task.incremental_z_start + z_src; // + 0 for full tasks
if (complete_slices >= slices_per_group_val || (is_final_batch && complete_slices > 0)) {
const u32 z_src = static_cast<u32>(task.last_submitted_offset / effective_group_size) * slices_per_group_val;
const u32 z_image = task.incremental_z_start + z_src;
const u32 z_count = (std::min)({complete_slices, batch, total_slices - z_src}); const u32 z_count = (std::min)({complete_slices, batch, total_slices - z_src});
if (z_count > 0) { if (z_count > 0) {
auto uploads = FullUploadSwizzles(task.info); auto uploads = FullUploadSwizzles(task.info);
if (task.is_incremental) { if (task.is_incremental) {
uploads[0].num_tiles.depth = task.incremental_z_count; uploads[0].num_tiles.depth = task.incremental_z_count;
} else if (task.is_sparse) {
uploads[0].num_tiles.depth = z_count;
} }
runtime.AccelerateImageUpload(image, task.staging_buffer, runtime.AccelerateImageUpload(image, task.staging_buffer,
FixSmallVectorADL(uploads), FixSmallVectorADL(uploads),
z_src, z_image, z_count,
sparse_hint,
std::span<const VideoCommon::Accelerated::SliceBBox>(task.slice_bounds),
task.is_incremental);
task.last_submitted_offset += static_cast<size_t>(z_count) * task.bytes_per_slice;
z_src, z_image);
const u32 groups_dispatched = z_count / slices_per_group_val;
task.last_submitted_offset += static_cast<size_t>(groups_dispatched) * effective_group_size;
} }
} }
// Check if complete // Check if complete
const u32 slices_submitted = static_cast<u32>(task.last_submitted_offset / task.bytes_per_slice);
const u32 slices_submitted = static_cast<u32>(task.last_submitted_offset / effective_group_size) * slices_per_group_val;
const bool all_submitted = slices_submitted >= total_slices || const bool all_submitted = slices_submitted >= total_slices ||
(is_final_batch && bytes_ready < task.bytes_per_slice);
(is_final_batch && bytes_ready < effective_group_size);
if (is_final_batch && all_submitted) { if (is_final_batch && all_submitted) {
runtime.FreeDeferredStagingBuffer(task.staging_buffer); runtime.FreeDeferredStagingBuffer(task.staging_buffer);
runtime.ReleaseSparseUnswizzleBuffer(image); runtime.ReleaseSparseUnswizzleBuffer(image);
image.flags &= ~ImageFlagBits::IsDecoding; image.flags &= ~ImageFlagBits::IsDecoding;
if (task.was_rescaled) {
image.ScaleUp();
}
unswizzle_queue.pop_front(); unswizzle_queue.pop_front();
} }
} }

9
src/video_core/texture_cache/texture_cache_base.h

@ -144,11 +144,10 @@ class TextureCache : public VideoCommon::ChannelSetupCaches<TextureCacheChannelI
bool was_rescaled = false; bool was_rescaled = false;
bool is_sparse = false; bool is_sparse = false;
std::vector<u8> slice_has_data; std::vector<u8> slice_has_data;
std::vector<VideoCommon::Accelerated::SliceBBox> slice_bounds;
std::vector<std::pair<GPUVAddr, size_t>> sparse_segments; std::vector<std::pair<GPUVAddr, size_t>> sparse_segments;
size_t segment_scan_cursor = 0; size_t segment_scan_cursor = 0;
u64 swizzled_slice_size = 0;
u32 swizzle_block_depth = 0;
size_t swizzle_group_size = 0;
u32 slices_per_group = 0;
bool is_incremental = false; bool is_incremental = false;
size_t staging_base_byte_offset = 0; size_t staging_base_byte_offset = 0;
u32 incremental_z_start = 0; u32 incremental_z_start = 0;
@ -440,6 +439,10 @@ private:
void QueueAsyncUnswizzle(Image& image, ImageId image_id); void QueueAsyncUnswizzle(Image& image, ImageId image_id);
void TickAsyncUnswizzle(); void TickAsyncUnswizzle();
bool IsUnswizzleStorageFormatSupported(PixelFormat format) {
return runtime.IsUnswizzleStorageFormatSupported(format);
}
Runtime& runtime; Runtime& runtime;
Tegra::MaxwellDeviceMemoryManager& device_memory; Tegra::MaxwellDeviceMemoryManager& device_memory;

Loading…
Cancel
Save