bcn.cpp 3.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <stb_dxt.h>
  4. #include <string.h>
  5. #include "common/alignment.h"
  6. #include "video_core/textures/bcn.h"
  7. #include "video_core/textures/workers.h"
  8. namespace Tegra::Texture::BCN {
  9. using BCNCompressor = void(u8* block_output, const u8* block_input, bool any_alpha);
  10. template <u32 BytesPerBlock, bool ThresholdAlpha = false>
  11. void CompressBCN(std::span<const uint8_t> data, uint32_t width, uint32_t height, uint32_t depth,
  12. std::span<uint8_t> output, BCNCompressor f) {
  13. constexpr u8 alpha_threshold = 128;
  14. constexpr u32 bytes_per_px = 4;
  15. const u32 plane_dim = width * height;
  16. Common::ThreadWorker& workers{GetThreadWorkers()};
  17. for (u32 z = 0; z < depth; z++) {
  18. for (u32 y = 0; y < height; y += 4) {
  19. auto compress_row = [z, y, width, height, plane_dim, f, data, output]() {
  20. for (u32 x = 0; x < width; x += 4) {
  21. // Gather 4x4 block of RGBA texels
  22. u8 input_colors[4][4][4];
  23. bool any_alpha = false;
  24. for (u32 j = 0; j < 4; j++) {
  25. for (u32 i = 0; i < 4; i++) {
  26. const size_t coord =
  27. (z * plane_dim + (y + j) * width + (x + i)) * bytes_per_px;
  28. if ((x + i < width) && (y + j < height)) {
  29. if constexpr (ThresholdAlpha) {
  30. if (data[coord + 3] >= alpha_threshold) {
  31. input_colors[j][i][0] = data[coord + 0];
  32. input_colors[j][i][1] = data[coord + 1];
  33. input_colors[j][i][2] = data[coord + 2];
  34. input_colors[j][i][3] = 255;
  35. } else {
  36. any_alpha = true;
  37. memset(input_colors[j][i], 0, bytes_per_px);
  38. }
  39. } else {
  40. memcpy(input_colors[j][i], &data[coord], bytes_per_px);
  41. }
  42. } else {
  43. memset(input_colors[j][i], 0, bytes_per_px);
  44. }
  45. }
  46. }
  47. const u32 bytes_per_row = BytesPerBlock * Common::DivideUp(width, 4U);
  48. const u32 bytes_per_plane = bytes_per_row * Common::DivideUp(height, 4U);
  49. f(output.data() + z * bytes_per_plane + (y / 4) * bytes_per_row +
  50. (x / 4) * BytesPerBlock,
  51. reinterpret_cast<u8*>(input_colors), any_alpha);
  52. }
  53. };
  54. workers.QueueWork(std::move(compress_row));
  55. }
  56. workers.WaitForRequests();
  57. }
  58. }
  59. void CompressBC1(std::span<const uint8_t> data, uint32_t width, uint32_t height, uint32_t depth,
  60. std::span<uint8_t> output) {
  61. CompressBCN<8, true>(data, width, height, depth, output,
  62. [](u8* block_output, const u8* block_input, bool any_alpha) {
  63. stb_compress_bc1_block(block_output, block_input, any_alpha,
  64. STB_DXT_NORMAL);
  65. });
  66. }
  67. void CompressBC3(std::span<const uint8_t> data, uint32_t width, uint32_t height, uint32_t depth,
  68. std::span<uint8_t> output) {
  69. CompressBCN<16, false>(data, width, height, depth, output,
  70. [](u8* block_output, const u8* block_input, bool any_alpha) {
  71. stb_compress_bc3_block(block_output, block_input, STB_DXT_NORMAL);
  72. });
  73. }
  74. } // namespace Tegra::Texture::BCN