Browse Source
Added ability to toggle the async unswizzle system between Off, CPU, GPU
Added ability to toggle the async unswizzle system between Off, CPU, GPU
Restored older GPU unswizzle system that worked better Added CPU async texture unswizzling for VRAM limited devices Fixed issue with GPU unswizzle that caused the sparse texture to skip slices Attempted to save ram by only allocating space for the slices that have data Improve speed by saving a buffer that doesn't need to be allocated per-sparse texture Attempt to speed up the CPU unswizzle side by batching to reduce loop counts and using SSE2/AVX2pull/3737/head
committed by
crueter
19 changed files with 1434 additions and 468 deletions
-
10src/common/settings.h
-
1src/common/settings_enums.h
-
20src/qt_common/config/shared_translation.cpp
-
13src/video_core/CMakeLists.txt
-
3src/video_core/host_shaders/block_linear_unswizzle_3d_bcn.comp
-
6src/video_core/renderer_opengl/gl_texture_cache.cpp
-
8src/video_core/renderer_opengl/gl_texture_cache.h
-
247src/video_core/renderer_vulkan/vk_compute_pass.cpp
-
9src/video_core/renderer_vulkan/vk_compute_pass.h
-
37src/video_core/renderer_vulkan/vk_texture_cache.cpp
-
6src/video_core/renderer_vulkan/vk_texture_cache.h
-
78src/video_core/texture_cache/accelerated_swizzle.h
-
928src/video_core/texture_cache/texture_cache.h
-
83src/video_core/texture_cache/texture_cache_base.h
-
133src/video_core/textures/decoders.cpp
-
157src/video_core/textures/decoders_avx2.cpp
-
16src/video_core/textures/decoders_avx2.h
-
131src/video_core/textures/decoders_sse2.cpp
-
16src/video_core/textures/decoders_sse2.h
928
src/video_core/texture_cache/texture_cache.h
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,157 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|||
|
|||
#include <cstring>
|
|||
#include <span>
|
|||
|
|||
#include <immintrin.h> // AVX2
|
|||
|
|||
#include "common/assert.h"
|
|||
#include "common/div_ceil.h"
|
|||
#include "video_core/textures/decoders.h"
|
|||
#include "video_core/textures/decoders_avx2.h"
|
|||
|
|||
namespace Tegra::Texture { |
|||
namespace { |
|||
|
|||
template <u32 mask> |
|||
constexpr u32 Pdep(u32 value) { |
|||
u32 result = 0; |
|||
u32 m = mask; |
|||
for (u32 bit = 1; m; bit += bit) { |
|||
if (value & bit) { |
|||
result |= m & (~m + 1); |
|||
} |
|||
m &= m - 1; |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
template <u32 mask, u32 incr_amount> |
|||
void IncrPdep(u32& value) { |
|||
static constexpr u32 swizzled_incr = Pdep<mask>(incr_amount); |
|||
value = ((value | ~mask) + swizzled_incr) & mask; |
|||
} |
|||
|
|||
template <u32 BYTES_PER_PIXEL> |
|||
__attribute__((target("avx2"))) void UnswizzleGobPermuteAVX2Impl(std::span<u8> output, |
|||
std::span<const u8> input, |
|||
u32 width, u32 height, u32 depth, |
|||
u32 block_height, |
|||
u32 block_depth, u32 stride) { |
|||
static_assert(32 % BYTES_PER_PIXEL == 0, "BYTES_PER_PIXEL must evenly divide 32"); |
|||
|
|||
static constexpr u32 RUN_BYTES = 32; |
|||
static constexpr u32 PIXELS_PER_RUN = RUN_BYTES / BYTES_PER_PIXEL; |
|||
|
|||
const u32 pitch = width * BYTES_PER_PIXEL; |
|||
const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT); |
|||
const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth); |
|||
const u32 slice_size = |
|||
Common::DivCeilLog2(height, block_height + GOB_SIZE_Y_SHIFT) * block_size; |
|||
const u32 block_height_mask = (1U << block_height) - 1; |
|||
const u32 block_depth_mask = (1U << block_depth) - 1; |
|||
const u32 x_shift = GOB_SIZE_SHIFT + block_height + block_depth; |
|||
|
|||
for (u32 slice = 0; slice < depth; ++slice) { |
|||
const u32 offset_z = (slice >> block_depth) * slice_size + |
|||
((slice & block_depth_mask) << (GOB_SIZE_SHIFT + block_height)); |
|||
|
|||
for (u32 line = 0; line < height; ++line) { |
|||
const u32 swizzled_y = Pdep<SWIZZLE_Y_BITS>(line); |
|||
const u32 block_y = line >> GOB_SIZE_Y_SHIFT; |
|||
const u32 offset_y = (block_y >> block_height) * block_size + |
|||
((block_y & block_height_mask) << GOB_SIZE_SHIFT); |
|||
|
|||
u32 swizzled_x = 0; |
|||
u32 column = 0; |
|||
|
|||
for (; column + PIXELS_PER_RUN <= width; |
|||
column += PIXELS_PER_RUN, IncrPdep<SWIZZLE_X_BITS, RUN_BYTES>(swizzled_x)) { |
|||
|
|||
const u32 x = column * BYTES_PER_PIXEL; |
|||
const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift; |
|||
const u32 base_swizzled_offset = offset_z + offset_y + offset_x; |
|||
const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y); |
|||
const u32 unswizzled_offset = |
|||
slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL; |
|||
|
|||
u8* const dst = &output[unswizzled_offset]; |
|||
const u8* const src = &input[swizzled_offset]; |
|||
|
|||
const __m128i lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src)); |
|||
const __m128i hi = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + 32)); |
|||
|
|||
__m256i val = _mm256_castsi128_si256(lo); |
|||
val = _mm256_inserti128_si256(val, hi, 1); |
|||
|
|||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(dst), val); |
|||
} |
|||
|
|||
if constexpr (BYTES_PER_PIXEL <= 16) { |
|||
constexpr u32 PIXELS_PER_16 = 16 / BYTES_PER_PIXEL; |
|||
if (column + PIXELS_PER_16 <= width) { |
|||
const u32 x = column * BYTES_PER_PIXEL; |
|||
const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift; |
|||
const u32 base_swizzled_offset = offset_z + offset_y + offset_x; |
|||
const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y); |
|||
const u32 unswizzled_offset = |
|||
slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL; |
|||
|
|||
u8* const dst = &output[unswizzled_offset]; |
|||
const u8* const src = &input[swizzled_offset]; |
|||
|
|||
const __m128i val = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src)); |
|||
_mm_storeu_si128(reinterpret_cast<__m128i*>(dst), val); |
|||
|
|||
column += PIXELS_PER_16; |
|||
IncrPdep<SWIZZLE_X_BITS, 16>(swizzled_x); |
|||
} |
|||
} |
|||
|
|||
for (; column < width; |
|||
++column, IncrPdep<SWIZZLE_X_BITS, BYTES_PER_PIXEL>(swizzled_x)) { |
|||
|
|||
const u32 x = column * BYTES_PER_PIXEL; |
|||
const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift; |
|||
const u32 base_swizzled_offset = offset_z + offset_y + offset_x; |
|||
const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y); |
|||
const u32 unswizzled_offset = |
|||
slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL; |
|||
|
|||
u8* const dst = &output[unswizzled_offset]; |
|||
const u8* const src = &input[swizzled_offset]; |
|||
|
|||
std::memcpy(dst, src, BYTES_PER_PIXEL); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
} // Anonymous namespace
|
|||
|
|||
void UnswizzleGobPermuteAVX2(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, |
|||
u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, |
|||
u32 stride) { |
|||
switch (bytes_per_pixel) { |
|||
case 1: |
|||
return UnswizzleGobPermuteAVX2Impl<1>(output, input, width, height, depth, block_height, |
|||
block_depth, stride); |
|||
case 2: |
|||
return UnswizzleGobPermuteAVX2Impl<2>(output, input, width, height, depth, block_height, |
|||
block_depth, stride); |
|||
case 4: |
|||
return UnswizzleGobPermuteAVX2Impl<4>(output, input, width, height, depth, block_height, |
|||
block_depth, stride); |
|||
case 8: |
|||
return UnswizzleGobPermuteAVX2Impl<8>(output, input, width, height, depth, block_height, |
|||
block_depth, stride); |
|||
case 16: |
|||
return UnswizzleGobPermuteAVX2Impl<16>(output, input, width, height, depth, block_height, |
|||
block_depth, stride); |
|||
default: |
|||
UNREACHABLE(); |
|||
} |
|||
} |
|||
|
|||
} // namespace Tegra::Texture
|
|||
@ -0,0 +1,16 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project |
|||
// SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include <span> |
|||
|
|||
#include "common/common_types.h" |
|||
|
|||
namespace Tegra::Texture { |
|||
|
|||
void UnswizzleGobPermuteAVX2(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, |
|||
u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, |
|||
u32 stride); |
|||
|
|||
} // namespace Tegra::Texture |
|||
@ -0,0 +1,131 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|||
|
|||
#include <cstring>
|
|||
#include <span>
|
|||
|
|||
#include <emmintrin.h> // SSE2
|
|||
|
|||
#include "common/assert.h"
|
|||
#include "common/div_ceil.h"
|
|||
#include "video_core/textures/decoders.h"
|
|||
#include "video_core/textures/decoders_sse2.h"
|
|||
|
|||
namespace Tegra::Texture { |
|||
namespace { |
|||
|
|||
template <u32 mask> |
|||
constexpr u32 Pdep(u32 value) { |
|||
u32 result = 0; |
|||
u32 m = mask; |
|||
for (u32 bit = 1; m; bit += bit) { |
|||
if (value & bit) { |
|||
result |= m & (~m + 1); |
|||
} |
|||
m &= m - 1; |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
template <u32 mask, u32 incr_amount> |
|||
void IncrPdep(u32& value) { |
|||
static constexpr u32 swizzled_incr = Pdep<mask>(incr_amount); |
|||
value = ((value | ~mask) + swizzled_incr) & mask; |
|||
} |
|||
|
|||
template <u32 BYTES_PER_PIXEL> |
|||
__attribute__((target("sse2"))) void UnswizzleGobPermuteSSE2Impl(std::span<u8> output, |
|||
std::span<const u8> input, |
|||
u32 width, u32 height, u32 depth, |
|||
u32 block_height, |
|||
u32 block_depth, u32 stride) { |
|||
static_assert(16 % BYTES_PER_PIXEL == 0, "BYTES_PER_PIXEL must evenly divide 16"); |
|||
|
|||
static constexpr u32 RUN_BYTES = 16; |
|||
static constexpr u32 PIXELS_PER_RUN = RUN_BYTES / BYTES_PER_PIXEL; |
|||
|
|||
const u32 pitch = width * BYTES_PER_PIXEL; |
|||
const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT); |
|||
const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth); |
|||
const u32 slice_size = |
|||
Common::DivCeilLog2(height, block_height + GOB_SIZE_Y_SHIFT) * block_size; |
|||
const u32 block_height_mask = (1U << block_height) - 1; |
|||
const u32 block_depth_mask = (1U << block_depth) - 1; |
|||
const u32 x_shift = GOB_SIZE_SHIFT + block_height + block_depth; |
|||
|
|||
for (u32 slice = 0; slice < depth; ++slice) { |
|||
const u32 offset_z = (slice >> block_depth) * slice_size + |
|||
((slice & block_depth_mask) << (GOB_SIZE_SHIFT + block_height)); |
|||
|
|||
for (u32 line = 0; line < height; ++line) { |
|||
const u32 swizzled_y = Pdep<SWIZZLE_Y_BITS>(line); |
|||
const u32 block_y = line >> GOB_SIZE_Y_SHIFT; |
|||
const u32 offset_y = (block_y >> block_height) * block_size + |
|||
((block_y & block_height_mask) << GOB_SIZE_SHIFT); |
|||
|
|||
u32 swizzled_x = 0; |
|||
u32 column = 0; |
|||
|
|||
for (; column + PIXELS_PER_RUN <= width; |
|||
column += PIXELS_PER_RUN, IncrPdep<SWIZZLE_X_BITS, RUN_BYTES>(swizzled_x)) { |
|||
|
|||
const u32 x = column * BYTES_PER_PIXEL; |
|||
const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift; |
|||
const u32 base_swizzled_offset = offset_z + offset_y + offset_x; |
|||
const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y); |
|||
const u32 unswizzled_offset = |
|||
slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL; |
|||
|
|||
u8* const dst = &output[unswizzled_offset]; |
|||
const u8* const src = &input[swizzled_offset]; |
|||
|
|||
const __m128i val = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src)); |
|||
_mm_storeu_si128(reinterpret_cast<__m128i*>(dst), val); |
|||
} |
|||
|
|||
for (; column < width; |
|||
++column, IncrPdep<SWIZZLE_X_BITS, BYTES_PER_PIXEL>(swizzled_x)) { |
|||
|
|||
const u32 x = column * BYTES_PER_PIXEL; |
|||
const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift; |
|||
const u32 base_swizzled_offset = offset_z + offset_y + offset_x; |
|||
const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y); |
|||
const u32 unswizzled_offset = |
|||
slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL; |
|||
|
|||
u8* const dst = &output[unswizzled_offset]; |
|||
const u8* const src = &input[swizzled_offset]; |
|||
|
|||
std::memcpy(dst, src, BYTES_PER_PIXEL); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
} // Anonymous namespace
|
|||
|
|||
void UnswizzleGobPermuteSSE2(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, |
|||
u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, |
|||
u32 stride) { |
|||
switch (bytes_per_pixel) { |
|||
case 1: |
|||
return UnswizzleGobPermuteSSE2Impl<1>(output, input, width, height, depth, block_height, |
|||
block_depth, stride); |
|||
case 2: |
|||
return UnswizzleGobPermuteSSE2Impl<2>(output, input, width, height, depth, block_height, |
|||
block_depth, stride); |
|||
case 4: |
|||
return UnswizzleGobPermuteSSE2Impl<4>(output, input, width, height, depth, block_height, |
|||
block_depth, stride); |
|||
case 8: |
|||
return UnswizzleGobPermuteSSE2Impl<8>(output, input, width, height, depth, block_height, |
|||
block_depth, stride); |
|||
case 16: |
|||
return UnswizzleGobPermuteSSE2Impl<16>(output, input, width, height, depth, block_height, |
|||
block_depth, stride); |
|||
default: |
|||
UNREACHABLE(); |
|||
} |
|||
} |
|||
|
|||
} // namespace Tegra::Texture
|
|||
@ -0,0 +1,16 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project |
|||
// SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include <span> |
|||
|
|||
#include "common/common_types.h" |
|||
|
|||
namespace Tegra::Texture { |
|||
|
|||
void UnswizzleGobPermuteSSE2(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, |
|||
u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, |
|||
u32 stride); |
|||
|
|||
} // namespace Tegra::Texture |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue