decoders.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <array>
  4. #include <cmath>
  5. #include <cstring>
  6. #include <span>
  7. #include "common/alignment.h"
  8. #include "common/assert.h"
  9. #include "common/bit_util.h"
  10. #include "common/div_ceil.h"
  11. #include "video_core/gpu.h"
  12. #include "video_core/textures/decoders.h"
  13. namespace Tegra::Texture {
  14. namespace {
  15. template <u32 mask>
  16. constexpr u32 pdep(u32 value) {
  17. u32 result = 0;
  18. u32 m = mask;
  19. for (u32 bit = 1; m; bit += bit) {
  20. if (value & bit)
  21. result |= m & (~m + 1);
  22. m &= m - 1;
  23. }
  24. return result;
  25. }
  26. template <u32 mask, u32 incr_amount>
  27. void incrpdep(u32& value) {
  28. constexpr u32 swizzled_incr = pdep<mask>(incr_amount);
  29. value = ((value | ~mask) + swizzled_incr) & mask;
  30. }
  31. template <bool TO_LINEAR, u32 BYTES_PER_PIXEL>
  32. void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32 height, u32 depth,
  33. u32 block_height, u32 block_depth, u32 stride) {
  34. // The origin of the transformation can be configured here, leave it as zero as the current API
  35. // doesn't expose it.
  36. static constexpr u32 origin_x = 0;
  37. static constexpr u32 origin_y = 0;
  38. static constexpr u32 origin_z = 0;
  39. // We can configure here a custom pitch
  40. // As it's not exposed 'width * BYTES_PER_PIXEL' will be the expected pitch.
  41. const u32 pitch = width * BYTES_PER_PIXEL;
  42. const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT);
  43. const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth);
  44. const u32 slice_size =
  45. Common::DivCeilLog2(height, block_height + GOB_SIZE_Y_SHIFT) * block_size;
  46. const u32 block_height_mask = (1U << block_height) - 1;
  47. const u32 block_depth_mask = (1U << block_depth) - 1;
  48. const u32 x_shift = GOB_SIZE_SHIFT + block_height + block_depth;
  49. for (u32 slice = 0; slice < depth; ++slice) {
  50. const u32 z = slice + origin_z;
  51. const u32 offset_z = (z >> block_depth) * slice_size +
  52. ((z & block_depth_mask) << (GOB_SIZE_SHIFT + block_height));
  53. for (u32 line = 0; line < height; ++line) {
  54. const u32 y = line + origin_y;
  55. const u32 swizzled_y = pdep<SWIZZLE_Y_BITS>(y);
  56. const u32 block_y = y >> GOB_SIZE_Y_SHIFT;
  57. const u32 offset_y = (block_y >> block_height) * block_size +
  58. ((block_y & block_height_mask) << GOB_SIZE_SHIFT);
  59. u32 swizzled_x = pdep<SWIZZLE_X_BITS>(origin_x * BYTES_PER_PIXEL);
  60. for (u32 column = 0; column < width;
  61. ++column, incrpdep<SWIZZLE_X_BITS, BYTES_PER_PIXEL>(swizzled_x)) {
  62. const u32 x = (column + origin_x) * BYTES_PER_PIXEL;
  63. const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift;
  64. const u32 base_swizzled_offset = offset_z + offset_y + offset_x;
  65. const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y);
  66. const u32 unswizzled_offset =
  67. slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL;
  68. u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset];
  69. const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset];
  70. std::memcpy(dst, src, BYTES_PER_PIXEL);
  71. }
  72. }
  73. }
  74. }
  75. template <bool TO_LINEAR, u32 BYTES_PER_PIXEL>
  76. void SwizzleSubrectImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32 height,
  77. u32 depth, u32 origin_x, u32 origin_y, u32 extent_x, u32 num_lines,
  78. u32 block_height, u32 block_depth, u32 pitch_linear) {
  79. // The origin of the transformation can be configured here, leave it as zero as the current API
  80. // doesn't expose it.
  81. static constexpr u32 origin_z = 0;
  82. // We can configure here a custom pitch
  83. // As it's not exposed 'width * BYTES_PER_PIXEL' will be the expected pitch.
  84. const u32 pitch = pitch_linear;
  85. const u32 stride = Common::AlignUpLog2(width * BYTES_PER_PIXEL, GOB_SIZE_X_SHIFT);
  86. const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT);
  87. const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth);
  88. const u32 slice_size =
  89. Common::DivCeilLog2(height, block_height + GOB_SIZE_Y_SHIFT) * block_size;
  90. const u32 block_height_mask = (1U << block_height) - 1;
  91. const u32 block_depth_mask = (1U << block_depth) - 1;
  92. const u32 x_shift = GOB_SIZE_SHIFT + block_height + block_depth;
  93. u32 unprocessed_lines = num_lines;
  94. u32 extent_y = std::min(num_lines, height - origin_y);
  95. for (u32 slice = 0; slice < depth; ++slice) {
  96. const u32 z = slice + origin_z;
  97. const u32 offset_z = (z >> block_depth) * slice_size +
  98. ((z & block_depth_mask) << (GOB_SIZE_SHIFT + block_height));
  99. const u32 lines_in_y = std::min(unprocessed_lines, extent_y);
  100. for (u32 line = 0; line < lines_in_y; ++line) {
  101. const u32 y = line + origin_y;
  102. const u32 swizzled_y = pdep<SWIZZLE_Y_BITS>(y);
  103. const u32 block_y = y >> GOB_SIZE_Y_SHIFT;
  104. const u32 offset_y = (block_y >> block_height) * block_size +
  105. ((block_y & block_height_mask) << GOB_SIZE_SHIFT);
  106. u32 swizzled_x = pdep<SWIZZLE_X_BITS>(origin_x * BYTES_PER_PIXEL);
  107. for (u32 column = 0; column < extent_x;
  108. ++column, incrpdep<SWIZZLE_X_BITS, BYTES_PER_PIXEL>(swizzled_x)) {
  109. const u32 x = (column + origin_x) * BYTES_PER_PIXEL;
  110. const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift;
  111. const u32 base_swizzled_offset = offset_z + offset_y + offset_x;
  112. const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y);
  113. const u32 unswizzled_offset =
  114. slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL;
  115. u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset];
  116. const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset];
  117. std::memcpy(dst, src, BYTES_PER_PIXEL);
  118. }
  119. }
  120. unprocessed_lines -= lines_in_y;
  121. if (unprocessed_lines == 0) {
  122. return;
  123. }
  124. }
  125. }
  126. template <bool TO_LINEAR>
  127. void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width,
  128. u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride_alignment) {
  129. switch (bytes_per_pixel) {
  130. #define BPP_CASE(x) \
  131. case x: \
  132. return SwizzleImpl<TO_LINEAR, x>(output, input, width, height, depth, block_height, \
  133. block_depth, stride_alignment);
  134. BPP_CASE(1)
  135. BPP_CASE(2)
  136. BPP_CASE(3)
  137. BPP_CASE(4)
  138. BPP_CASE(6)
  139. BPP_CASE(8)
  140. BPP_CASE(12)
  141. BPP_CASE(16)
  142. #undef BPP_CASE
  143. default:
  144. ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
  145. }
  146. }
  147. } // Anonymous namespace
  148. void UnswizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel,
  149. u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth,
  150. u32 stride_alignment) {
  151. const u32 stride = Common::AlignUpLog2(width, stride_alignment) * bytes_per_pixel;
  152. const u32 new_bpp = std::min(4U, static_cast<u32>(std::countr_zero(width * bytes_per_pixel)));
  153. width = (width * bytes_per_pixel) >> new_bpp;
  154. bytes_per_pixel = 1U << new_bpp;
  155. Swizzle<false>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth,
  156. stride);
  157. }
  158. void SwizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width,
  159. u32 height, u32 depth, u32 block_height, u32 block_depth,
  160. u32 stride_alignment) {
  161. const u32 stride = Common::AlignUpLog2(width, stride_alignment) * bytes_per_pixel;
  162. const u32 new_bpp = std::min(4U, static_cast<u32>(std::countr_zero(width * bytes_per_pixel)));
  163. width = (width * bytes_per_pixel) >> new_bpp;
  164. bytes_per_pixel = 1U << new_bpp;
  165. Swizzle<true>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth,
  166. stride);
  167. }
  168. void SwizzleSubrect(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width,
  169. u32 height, u32 depth, u32 origin_x, u32 origin_y, u32 extent_x, u32 extent_y,
  170. u32 block_height, u32 block_depth, u32 pitch_linear) {
  171. switch (bytes_per_pixel) {
  172. #define BPP_CASE(x) \
  173. case x: \
  174. return SwizzleSubrectImpl<true, x>(output, input, width, height, depth, origin_x, \
  175. origin_y, extent_x, extent_y, block_height, \
  176. block_depth, pitch_linear);
  177. BPP_CASE(1)
  178. BPP_CASE(2)
  179. BPP_CASE(3)
  180. BPP_CASE(4)
  181. BPP_CASE(6)
  182. BPP_CASE(8)
  183. BPP_CASE(12)
  184. BPP_CASE(16)
  185. #undef BPP_CASE
  186. default:
  187. ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
  188. }
  189. }
  190. void UnswizzleSubrect(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel,
  191. u32 width, u32 height, u32 depth, u32 origin_x, u32 origin_y, u32 extent_x,
  192. u32 extent_y, u32 block_height, u32 block_depth, u32 pitch_linear) {
  193. switch (bytes_per_pixel) {
  194. #define BPP_CASE(x) \
  195. case x: \
  196. return SwizzleSubrectImpl<false, x>(output, input, width, height, depth, origin_x, \
  197. origin_y, extent_x, extent_y, block_height, \
  198. block_depth, pitch_linear);
  199. BPP_CASE(1)
  200. BPP_CASE(2)
  201. BPP_CASE(3)
  202. BPP_CASE(4)
  203. BPP_CASE(6)
  204. BPP_CASE(8)
  205. BPP_CASE(12)
  206. BPP_CASE(16)
  207. #undef BPP_CASE
  208. default:
  209. ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
  210. }
  211. }
  212. std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
  213. u32 block_height, u32 block_depth) {
  214. if (tiled) {
  215. const u32 aligned_width = Common::AlignUpLog2(width * bytes_per_pixel, GOB_SIZE_X_SHIFT);
  216. const u32 aligned_height = Common::AlignUpLog2(height, GOB_SIZE_Y_SHIFT + block_height);
  217. const u32 aligned_depth = Common::AlignUpLog2(depth, GOB_SIZE_Z_SHIFT + block_depth);
  218. return aligned_width * aligned_height * aligned_depth;
  219. } else {
  220. return width * height * depth * bytes_per_pixel;
  221. }
  222. }
  223. u64 GetGOBOffset(u32 width, u32 height, u32 dst_x, u32 dst_y, u32 block_height,
  224. u32 bytes_per_pixel) {
  225. auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); };
  226. const u32 gobs_in_block = 1 << block_height;
  227. const u32 y_blocks = GOB_SIZE_Y << block_height;
  228. const u32 x_per_gob = GOB_SIZE_X / bytes_per_pixel;
  229. const u32 x_blocks = div_ceil(width, x_per_gob);
  230. const u32 block_size = GOB_SIZE * gobs_in_block;
  231. const u32 stride = block_size * x_blocks;
  232. const u32 base = (dst_y / y_blocks) * stride + (dst_x / x_per_gob) * block_size;
  233. const u32 relative_y = dst_y % y_blocks;
  234. return base + (relative_y / GOB_SIZE_Y) * GOB_SIZE;
  235. }
  236. } // namespace Tegra::Texture