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, ©_offset, ©_size, ©_rows); 405 StringUtil::StrideMemCpy(out_ptr, out_stride, m_map_pointer + copy_offset, m_current_pitch, copy_size, copy_rows); 406 return true; 407 }