duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

gpu_texture.cpp (11929B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR PolyForm-Strict-1.0.0)
      3 
      4 #include "gpu_texture.h"
      5 #include "gpu_device.h"
      6 
      7 #include "common/align.h"
      8 #include "common/assert.h"
      9 #include "common/bitutils.h"
     10 #include "common/log.h"
     11 #include "common/string_util.h"
     12 
     13 Log_SetChannel(GPUTexture);
     14 
     15 GPUTexture::GPUTexture(u16 width, u16 height, u8 layers, u8 levels, u8 samples, Type type, Format format)
     16   : m_width(width), m_height(height), m_layers(layers), m_levels(levels), m_samples(samples), m_type(type),
     17     m_format(format)
     18 {
     19   GPUDevice::s_total_vram_usage += GetVRAMUsage();
     20 }
     21 
     22 GPUTexture::~GPUTexture()
     23 {
     24   GPUDevice::s_total_vram_usage -= GetVRAMUsage();
     25 }
     26 
     27 const char* GPUTexture::GetFormatName(Format format)
     28 {
     29   static constexpr const char* format_names[static_cast<u8>(Format::MaxCount)] = {
     30     "Unknown", // Unknown
     31     "RGBA8",   // RGBA8
     32     "BGRA8",   // BGRA8
     33     "RGB565",  // RGB565
     34     "RGB5551", // RGBA5551
     35     "R8",      // R8
     36     "D16",     // D16
     37     "D24S8",   // D24S8
     38     "D32F",    // D32F
     39     "D32FS8S", // D32FS8
     40     "R16",     // R16
     41     "R16I",    // R16I
     42     "R16U",    // R16U
     43     "R16F",    // R16F
     44     "R32I",    // R32I
     45     "R32U",    // R32U
     46     "R32F",    // R32F
     47     "RG8",     // RG8
     48     "RG16",    // RG16
     49     "RG16F",   // RG16F
     50     "RG32F",   // RG32F
     51     "RGBA16",  // RGBA16
     52     "RGBA16F", // RGBA16F
     53     "RGBA32F", // RGBA32F
     54     "RGB10A2", // RGB10A2
     55   };
     56 
     57   return format_names[static_cast<u8>(format)];
     58 }
     59 
     60 u32 GPUTexture::GetCompressedBytesPerBlock() const
     61 {
     62   return GetCompressedBytesPerBlock(m_format);
     63 }
     64 
     65 u32 GPUTexture::GetCompressedBytesPerBlock(Format format)
     66 {
     67   // TODO: Implement me
     68   return GetPixelSize(format);
     69 }
     70 
     71 u32 GPUTexture::GetCompressedBlockSize() const
     72 {
     73   return GetCompressedBlockSize(m_format);
     74 }
     75 
     76 u32 GPUTexture::GetCompressedBlockSize(Format format)
     77 {
     78   // TODO: Implement me
     79   /*if (format >= Format::BC1 && format <= Format::BC7)
     80     return 4;
     81   else*/
     82   return 1;
     83 }
     84 
     85 u32 GPUTexture::CalcUploadPitch(Format format, u32 width)
     86 {
     87   /*
     88   if (format >= Format::BC1 && format <= Format::BC7)
     89     width = Common::AlignUpPow2(width, 4) / 4;
     90   */
     91   return width * GetCompressedBytesPerBlock(format);
     92 }
     93 
     94 u32 GPUTexture::CalcUploadPitch(u32 width) const
     95 {
     96   return CalcUploadPitch(m_format, width);
     97 }
     98 
     99 u32 GPUTexture::CalcUploadRowLengthFromPitch(u32 pitch) const
    100 {
    101   return CalcUploadRowLengthFromPitch(m_format, pitch);
    102 }
    103 
    104 u32 GPUTexture::CalcUploadRowLengthFromPitch(Format format, u32 pitch)
    105 {
    106   const u32 block_size = GetCompressedBlockSize(format);
    107   const u32 bytes_per_block = GetCompressedBytesPerBlock(format);
    108   return ((pitch + (bytes_per_block - 1)) / bytes_per_block) * block_size;
    109 }
    110 
    111 u32 GPUTexture::CalcUploadSize(u32 height, u32 pitch) const
    112 {
    113   return CalcUploadSize(m_format, height, pitch);
    114 }
    115 
    116 u32 GPUTexture::CalcUploadSize(Format format, u32 height, u32 pitch)
    117 {
    118   const u32 block_size = GetCompressedBlockSize(format);
    119   return pitch * ((static_cast<u32>(height) + (block_size - 1)) / block_size);
    120 }
    121 
    122 std::array<float, 4> GPUTexture::GetUNormClearColor() const
    123 {
    124   return GPUDevice::RGBA8ToFloat(m_clear_value.color);
    125 }
    126 
    127 size_t GPUTexture::GetVRAMUsage() const
    128 {
    129   if (m_levels == 1) [[likely]]
    130     return ((static_cast<size_t>(m_width * m_height) * GetPixelSize(m_format)) * m_layers * m_samples);
    131 
    132   const size_t ps = GetPixelSize(m_format) * m_layers * m_samples;
    133   u32 width = m_width;
    134   u32 height = m_height;
    135   size_t ts = 0;
    136   for (u32 i = 0; i < m_levels; i++)
    137   {
    138     width = (width > 1) ? (width / 2) : width;
    139     height = (height > 1) ? (height / 2) : height;
    140     ts += static_cast<size_t>(width * height) * ps;
    141   }
    142 
    143   return ts;
    144 }
    145 
    146 u32 GPUTexture::GetPixelSize(GPUTexture::Format format)
    147 {
    148   static constexpr std::array<u8, static_cast<size_t>(Format::MaxCount)> sizes = {{
    149     0,  // Unknown
    150     4,  // RGBA8
    151     4,  // BGRA8
    152     2,  // RGB565
    153     2,  // RGBA5551
    154     1,  // R8
    155     2,  // D16
    156     4,  // D24S8
    157     4,  // D32F
    158     8,  // D32FS8
    159     2,  // R16
    160     2,  // R16I
    161     2,  // R16U
    162     2,  // R16F
    163     4,  // R32I
    164     4,  // R32U
    165     4,  // R32F
    166     2,  // RG8
    167     2,  // RG16
    168     2,  // RG16F
    169     8,  // RG32F
    170     8,  // RGBA16
    171     8,  // RGBA16F
    172     16, // RGBA32F
    173     4,  // RGB10A2
    174   }};
    175 
    176   return sizes[static_cast<size_t>(format)];
    177 }
    178 
    179 bool GPUTexture::IsDepthFormat(Format format)
    180 {
    181   return (format >= Format::D16 && format <= Format::D32FS8);
    182 }
    183 
    184 bool GPUTexture::IsDepthStencilFormat(Format format)
    185 {
    186   return (format == Format::D24S8 || format == Format::D32FS8);
    187 }
    188 
    189 bool GPUTexture::IsCompressedFormat(Format format)
    190 {
    191   // TODO: Implement me
    192   return false;
    193 }
    194 
    195 bool GPUTexture::ValidateConfig(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format)
    196 {
    197   if (width > MAX_WIDTH || height > MAX_HEIGHT || layers > MAX_LAYERS || levels > MAX_LEVELS || samples > MAX_SAMPLES)
    198   {
    199     ERROR_LOG("Invalid dimensions: {}x{}x{} {} {}.", width, height, layers, levels, samples);
    200     return false;
    201   }
    202 
    203   const u32 max_texture_size = g_gpu_device->GetMaxTextureSize();
    204   if (width > max_texture_size || height > max_texture_size)
    205   {
    206     ERROR_LOG("Texture width ({}) or height ({}) exceeds max texture size ({}).", width, height, max_texture_size);
    207     return false;
    208   }
    209 
    210   const u32 max_samples = g_gpu_device->GetMaxMultisamples();
    211   if (samples > max_samples)
    212   {
    213     ERROR_LOG("Texture samples ({}) exceeds max samples ({}).", samples, max_samples);
    214     return false;
    215   }
    216 
    217   if (samples > 1)
    218   {
    219     if (levels > 1)
    220     {
    221       ERROR_LOG("Multisampled textures can't have mip levels.");
    222       return false;
    223     }
    224     else if (type != Type::RenderTarget && type != Type::DepthStencil)
    225     {
    226       ERROR_LOG("Multisampled textures must be render targets or depth stencil targets.");
    227       return false;
    228     }
    229   }
    230 
    231   if (layers > 1 && type != Type::Texture && type != Type::DynamicTexture)
    232   {
    233     ERROR_LOG("Texture arrays are not supported on targets.");
    234     return false;
    235   }
    236 
    237   if (levels > 1 && type != Type::Texture && type != Type::DynamicTexture)
    238   {
    239     ERROR_LOG("Mipmaps are not supported on targets.");
    240     return false;
    241   }
    242 
    243   return true;
    244 }
    245 
    246 bool GPUTexture::ConvertTextureDataToRGBA8(u32 width, u32 height, std::vector<u32>& texture_data,
    247                                            u32& texture_data_stride, GPUTexture::Format format)
    248 {
    249   switch (format)
    250   {
    251     case Format::BGRA8:
    252     {
    253       for (u32 y = 0; y < height; y++)
    254       {
    255         u8* pixels = reinterpret_cast<u8*>(texture_data.data()) + (y * texture_data_stride);
    256         for (u32 x = 0; x < width; x++)
    257         {
    258           u32 pixel;
    259           std::memcpy(&pixel, pixels, sizeof(pixel));
    260           pixel = (pixel & 0xFF00FF00) | ((pixel & 0xFF) << 16) | ((pixel >> 16) & 0xFF);
    261           std::memcpy(pixels, &pixel, sizeof(pixel));
    262           pixels += sizeof(pixel);
    263         }
    264       }
    265 
    266       return true;
    267     }
    268 
    269     case Format::RGBA8:
    270       return true;
    271 
    272     case Format::RGB565:
    273     {
    274       std::vector<u32> temp(width * height);
    275 
    276       for (u32 y = 0; y < height; y++)
    277       {
    278         const u8* pixels_in = reinterpret_cast<const u8*>(texture_data.data()) + (y * texture_data_stride);
    279         u8* pixels_out = reinterpret_cast<u8*>(temp.data()) + (y * width * sizeof(u32));
    280 
    281         for (u32 x = 0; x < width; x++)
    282         {
    283           // RGB565 -> RGBA8
    284           u16 pixel_in;
    285           std::memcpy(&pixel_in, pixels_in, sizeof(u16));
    286           pixels_in += sizeof(u16);
    287           const u8 r5 = Truncate8(pixel_in >> 11);
    288           const u8 g6 = Truncate8((pixel_in >> 5) & 0x3F);
    289           const u8 b5 = Truncate8(pixel_in & 0x1F);
    290           const u32 rgba8 = ZeroExtend32((r5 << 3) | (r5 & 7)) | (ZeroExtend32((g6 << 2) | (g6 & 3)) << 8) |
    291                             (ZeroExtend32((b5 << 3) | (b5 & 7)) << 16) | (0xFF000000u);
    292           std::memcpy(pixels_out, &rgba8, sizeof(u32));
    293           pixels_out += sizeof(u32);
    294         }
    295       }
    296 
    297       texture_data = std::move(temp);
    298       texture_data_stride = sizeof(u32) * width;
    299       return true;
    300     }
    301 
    302     case Format::RGBA5551:
    303     {
    304       std::vector<u32> temp(width * height);
    305 
    306       for (u32 y = 0; y < height; y++)
    307       {
    308         const u8* pixels_in = reinterpret_cast<const u8*>(texture_data.data()) + (y * texture_data_stride);
    309         u8* pixels_out = reinterpret_cast<u8*>(temp.data()) + (y * width * sizeof(u32));
    310 
    311         for (u32 x = 0; x < width; x++)
    312         {
    313           // RGBA5551 -> RGBA8
    314           u16 pixel_in;
    315           std::memcpy(&pixel_in, pixels_in, sizeof(u16));
    316           pixels_in += sizeof(u16);
    317           const u8 a1 = Truncate8(pixel_in >> 15);
    318           const u8 r5 = Truncate8((pixel_in >> 10) & 0x1F);
    319           const u8 g6 = Truncate8((pixel_in >> 5) & 0x1F);
    320           const u8 b5 = Truncate8(pixel_in & 0x1F);
    321           const u32 rgba8 = ZeroExtend32((r5 << 3) | (r5 & 7)) | (ZeroExtend32((g6 << 3) | (g6 & 7)) << 8) |
    322                             (ZeroExtend32((b5 << 3) | (b5 & 7)) << 16) | (a1 ? 0xFF000000u : 0u);
    323           std::memcpy(pixels_out, &rgba8, sizeof(u32));
    324           pixels_out += sizeof(u32);
    325         }
    326       }
    327 
    328       texture_data = std::move(temp);
    329       texture_data_stride = sizeof(u32) * width;
    330       return true;
    331     }
    332 
    333     default:
    334       [[unlikely]] ERROR_LOG("Unknown pixel format {}", static_cast<u32>(format));
    335       return false;
    336   }
    337 }
    338 
    339 void GPUTexture::FlipTextureDataRGBA8(u32 width, u32 height, u8* texture_data, u32 texture_data_stride)
    340 {
    341   std::unique_ptr<u8[]> temp = std::make_unique<u8[]>(texture_data_stride);
    342   for (u32 flip_row = 0; flip_row < (height / 2); flip_row++)
    343   {
    344     u8* top_ptr = &texture_data[flip_row * texture_data_stride];
    345     u8* bottom_ptr = &texture_data[((height - 1) - flip_row) * texture_data_stride];
    346     std::memcpy(temp.get(), top_ptr, texture_data_stride);
    347     std::memcpy(top_ptr, bottom_ptr, texture_data_stride);
    348     std::memcpy(bottom_ptr, temp.get(), texture_data_stride);
    349   }
    350 }
    351 
    352 void GPUTexture::MakeReadyForSampling()
    353 {
    354 }
    355 
    356 GPUDownloadTexture::GPUDownloadTexture(u32 width, u32 height, GPUTexture::Format format, bool is_imported)
    357   : m_width(width), m_height(height), m_format(format), m_is_imported(is_imported)
    358 {
    359 }
    360 
    361 GPUDownloadTexture::~GPUDownloadTexture() = default;
    362 
    363 u32 GPUDownloadTexture::GetBufferSize(u32 width, u32 height, GPUTexture::Format format, u32 pitch_align /* = 1 */)
    364 {
    365   DebugAssert(std::has_single_bit(pitch_align));
    366   const u32 bytes_per_pixel = GPUTexture::GetPixelSize(format);
    367   const u32 pitch = Common::AlignUpPow2(width * bytes_per_pixel, pitch_align);
    368   return (pitch * height);
    369 }
    370 
    371 u32 GPUDownloadTexture::GetTransferPitch(u32 width, u32 pitch_align) const
    372 {
    373   DebugAssert(std::has_single_bit(pitch_align));
    374   const u32 bytes_per_pixel = GPUTexture::GetPixelSize(m_format);
    375   return Common::AlignUpPow2(width * bytes_per_pixel, pitch_align);
    376 }
    377 
    378 void GPUDownloadTexture::GetTransferSize(u32 x, u32 y, u32 width, u32 height, u32 pitch, u32* copy_offset,
    379                                          u32* copy_size, u32* copy_rows) const
    380 {
    381   const u32 bytes_per_pixel = GPUTexture::GetPixelSize(m_format);
    382   *copy_offset = (y * pitch) + (x * bytes_per_pixel);
    383   *copy_size = width * bytes_per_pixel;
    384   *copy_rows = height;
    385 }
    386 
    387 bool GPUDownloadTexture::ReadTexels(u32 x, u32 y, u32 width, u32 height, void* out_ptr, u32 out_stride)
    388 {
    389   if (m_needs_flush)
    390     Flush();
    391 
    392   // if we're imported, and this is the same buffer, bail out
    393   if (m_map_pointer == out_ptr)
    394   {
    395     // but stride should match
    396     DebugAssert(x == 0 && y == 0 && width <= m_width && height <= m_height && out_stride == m_current_pitch);
    397     return true;
    398   }
    399 
    400   if (!Map(x, y, width, height))
    401     return false;
    402 
    403   u32 copy_offset, copy_size, copy_rows;
    404   GetTransferSize(x, y, width, height, m_current_pitch, &copy_offset, &copy_size, &copy_rows);
    405   StringUtil::StrideMemCpy(out_ptr, out_stride, m_map_pointer + copy_offset, m_current_pitch, copy_size, copy_rows);
    406   return true;
    407 }