From 78451a4e9efe8d25d406ff682de09b41aca1be68 Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Mon, 9 Feb 2015 14:10:07 -0800 Subject: [PATCH] Cleaning up texture info/uploading. Edge cases still likely. --- src/xenia/gpu/gl4/texture_cache.cc | 323 ++++++++++++++--------------- src/xenia/gpu/texture_info.cc | 260 +++++++++++------------ src/xenia/gpu/texture_info.h | 28 ++- 3 files changed, 287 insertions(+), 324 deletions(-) diff --git a/src/xenia/gpu/gl4/texture_cache.cc b/src/xenia/gpu/gl4/texture_cache.cc index 152c83c85..b6329e515 100644 --- a/src/xenia/gpu/gl4/texture_cache.cc +++ b/src/xenia/gpu/gl4/texture_cache.cc @@ -412,161 +412,138 @@ void TextureSwap(Endian endianness, void* dest, const void* src, } } +struct TextureConfig { + TextureFormat texture_format; + GLenum internal_format; + GLenum format; + GLenum type; +}; + +// https://code.google.com/p/glsnewton/source/browse/trunk/Source/uDDSLoader.pas?r=62 +// http://dench.flatlib.jp/opengl/textures +// http://fossies.org/linux/WebKit/Source/ThirdParty/ANGLE/src/libGLESv2/formatutils.cpp +static const TextureConfig texture_configs[64] = { + {TextureFormat::k_1_REVERSE, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::k_1, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, + {TextureFormat::k_8, GL_R8, GL_RED, GL_UNSIGNED_BYTE}, + {TextureFormat::k_1_5_5_5, GL_RGB5_A1, GL_BGRA, + GL_UNSIGNED_SHORT_1_5_5_5_REV}, + {TextureFormat::k_5_6_5, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, + {TextureFormat::k_6_5_5, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, + {TextureFormat::k_8_8_8_8, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, + {TextureFormat::k_2_10_10_10, GL_RGB10_A2, GL_RGBA, + GL_UNSIGNED_INT_2_10_10_10_REV}, + {TextureFormat::k_8_A, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, + {TextureFormat::k_8_B, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, + {TextureFormat::k_8_8, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, + {TextureFormat::k_Cr_Y1_Cb_Y0, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::k_Y1_Cr_Y0_Cb, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::kUnknown, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::k_8_8_8_8_A, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::k_4_4_4_4, GL_RGBA4, GL_RGBA, + GL_UNSIGNED_SHORT_4_4_4_4_REV}, + {TextureFormat::k_10_11_11, GL_R11F_G11F_B10F, GL_RGB, + GL_UNSIGNED_INT_10F_11F_11F_REV}, // ? + {TextureFormat::k_11_11_10, GL_R11F_G11F_B10F, GL_RGB, + GL_UNSIGNED_INT_10F_11F_11F_REV}, // ? + {TextureFormat::k_DXT1, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, + GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_UNSIGNED_BYTE}, + {TextureFormat::k_DXT2_3, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, + GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_UNSIGNED_BYTE}, + {TextureFormat::k_DXT4_5, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, + GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_UNSIGNED_BYTE}, + {TextureFormat::kUnknown, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::k_24_8, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, + GL_UNSIGNED_INT_24_8}, + {TextureFormat::k_24_8_FLOAT, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, + GL_FLOAT_32_UNSIGNED_INT_24_8_REV}, + {TextureFormat::k_16, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, + {TextureFormat::k_16_16, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, + {TextureFormat::k_16_16_16_16, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::k_16_EXPAND, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::k_16_16_EXPAND, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::k_16_16_16_16_EXPAND, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::k_16_FLOAT, GL_R16F, GL_RED, GL_HALF_FLOAT}, + {TextureFormat::k_16_16_FLOAT, GL_RG16F, GL_RG, GL_HALF_FLOAT}, + {TextureFormat::k_16_16_16_16_FLOAT, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT}, + {TextureFormat::k_32, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, + {TextureFormat::k_32_32, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, + {TextureFormat::k_32_32_32_32, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::k_32_FLOAT, GL_R32F, GL_RED, GL_FLOAT}, + {TextureFormat::k_32_32_FLOAT, GL_RG32F, GL_RG, GL_FLOAT}, + {TextureFormat::k_32_32_32_32_FLOAT, GL_RGBA32F, GL_RGBA, GL_FLOAT}, + {TextureFormat::k_32_AS_8, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::k_32_AS_8_8, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::k_16_MPEG, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::k_16_16_MPEG, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::k_8_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::k_32_AS_8_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::k_32_AS_8_8_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::k_16_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::k_16_MPEG_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::k_16_16_MPEG_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::k_DXN, GL_COMPRESSED_RG_RGTC2, GL_COMPRESSED_RG_RGTC2, + GL_INVALID_ENUM}, + {TextureFormat::k_8_8_8_8_AS_16_16_16_16, GL_RGBA8, GL_RGBA, + GL_UNSIGNED_BYTE}, + {TextureFormat::k_DXT1_AS_16_16_16_16, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, + GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_UNSIGNED_BYTE}, + {TextureFormat::k_DXT2_3_AS_16_16_16_16, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, + GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_UNSIGNED_BYTE}, + {TextureFormat::k_DXT4_5_AS_16_16_16_16, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, + GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_UNSIGNED_BYTE}, + {TextureFormat::k_2_10_10_10_AS_16_16_16_16, GL_RGB10_A2, GL_RGBA, + GL_UNSIGNED_INT_2_10_10_10_REV}, + {TextureFormat::k_10_11_11_AS_16_16_16_16, GL_R11F_G11F_B10F, GL_RGB, + GL_UNSIGNED_INT_10F_11F_11F_REV}, + {TextureFormat::k_11_11_10_AS_16_16_16_16, GL_R11F_G11F_B10F, + GL_INVALID_ENUM, GL_INVALID_ENUM}, + {TextureFormat::k_32_32_32_FLOAT, GL_RGB32F, GL_RGB, GL_FLOAT}, + {TextureFormat::k_DXT3A, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, + {TextureFormat::k_DXT5A, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, + {TextureFormat::k_CTX1, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, + {TextureFormat::k_DXT3A_AS_1_1_1_1, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::kUnknown, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, + {TextureFormat::kUnknown, GL_INVALID_ENUM, GL_INVALID_ENUM, + GL_INVALID_ENUM}, +}; + bool TextureCache::UploadTexture2D(GLuint texture, const TextureInfo& texture_info) { const auto host_address = memory_->Translate(texture_info.guest_address); - GLenum internal_format = GL_RGBA8; - GLenum format = GL_RGBA; - GLenum type = GL_UNSIGNED_BYTE; - // https://code.google.com/p/glsnewton/source/browse/trunk/Source/uDDSLoader.pas?r=62 - // http://dench.flatlib.jp/opengl/textures - // http://fossies.org/linux/WebKit/Source/ThirdParty/ANGLE/src/libGLESv2/formatutils.cpp - switch (texture_info.format) { - case TextureFormat::k_8: - internal_format = GL_R8; - format = GL_RED; - type = GL_UNSIGNED_BYTE; - break; - case TextureFormat::k_1_5_5_5: - internal_format = GL_RGB5_A1; - format = GL_BGRA; - type = GL_UNSIGNED_SHORT_1_5_5_5_REV; - break; - case TextureFormat::k_5_6_5: - internal_format = GL_RGB565; - format = GL_RGB; - type = GL_UNSIGNED_SHORT_5_6_5; - break; - case TextureFormat::k_2_10_10_10: - case TextureFormat::k_2_10_10_10_AS_16_16_16_16: - internal_format = GL_RGB10_A2; - format = GL_RGBA; - type = GL_UNSIGNED_INT_2_10_10_10_REV; - break; - case TextureFormat::k_10_11_11: - case TextureFormat::k_10_11_11_AS_16_16_16_16: - // ? - internal_format = GL_R11F_G11F_B10F; - format = GL_RGB; - type = GL_UNSIGNED_INT_10F_11F_11F_REV; - break; - case TextureFormat::k_11_11_10: - case TextureFormat::k_11_11_10_AS_16_16_16_16: - internal_format = GL_R11F_G11F_B10F; - format = GL_RGB; - type = GL_UNSIGNED_INT_10F_11F_11F_REV; - break; - case TextureFormat::k_8_8_8_8: - case TextureFormat::k_8_8_8_8_AS_16_16_16_16: - internal_format = GL_RGBA8; - format = GL_RGBA; - type = GL_UNSIGNED_BYTE; - break; - case TextureFormat::k_4_4_4_4: - internal_format = GL_RGBA4; - format = GL_RGBA; - type = GL_UNSIGNED_SHORT_4_4_4_4_REV; - break; - case TextureFormat::k_16_FLOAT: - internal_format = GL_R16F; - format = GL_RED; - type = GL_HALF_FLOAT; - break; - case TextureFormat::k_16_16_FLOAT: - internal_format = GL_RG16F; - format = GL_RG; - type = GL_HALF_FLOAT; - break; - case TextureFormat::k_16_16_16_16_FLOAT: - internal_format = GL_RGBA16F; - format = GL_RGBA; - type = GL_HALF_FLOAT; - break; - case TextureFormat::k_32_FLOAT: - internal_format = GL_R32F; - format = GL_RED; - type = GL_FLOAT; - break; - case TextureFormat::k_32_32_FLOAT: - internal_format = GL_RG32F; - format = GL_RG; - type = GL_FLOAT; - break; - case TextureFormat::k_32_32_32_FLOAT: - internal_format = GL_RGB32F; - format = GL_RGB; - type = GL_FLOAT; - break; - case TextureFormat::k_32_32_32_32_FLOAT: - internal_format = GL_RGBA32F; - format = GL_RGBA; - type = GL_FLOAT; - break; - case TextureFormat::k_DXT1: - case TextureFormat::k_DXT1_AS_16_16_16_16: - // or GL_COMPRESSED_RGB_S3TC_DXT1_EXT? - internal_format = format = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; - break; - case TextureFormat::k_DXT2_3: - case TextureFormat::k_DXT2_3_AS_16_16_16_16: - internal_format = format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; - break; - case TextureFormat::k_DXT4_5: - case TextureFormat::k_DXT4_5_AS_16_16_16_16: - internal_format = format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - break; - case TextureFormat::k_DXN: - internal_format = format = GL_COMPRESSED_RG_RGTC2; - break; - case TextureFormat::k_24_8: - internal_format = GL_DEPTH24_STENCIL8; - format = GL_DEPTH_STENCIL; - type = GL_UNSIGNED_INT_24_8; - break; - case TextureFormat::k_24_8_FLOAT: - internal_format = GL_DEPTH24_STENCIL8; - format = GL_DEPTH_STENCIL; - type = GL_FLOAT_32_UNSIGNED_INT_24_8_REV; - break; - default: - case TextureFormat::k_1_REVERSE: - case TextureFormat::k_1: - case TextureFormat::k_6_5_5: - case TextureFormat::k_8_A: - case TextureFormat::k_8_B: - case TextureFormat::k_8_8: - case TextureFormat::k_Cr_Y1_Cb_Y0: - case TextureFormat::k_Y1_Cr_Y0_Cb: - case TextureFormat::k_8_8_8_8_A: - case TextureFormat::k_16: - case TextureFormat::k_16_16: - case TextureFormat::k_16_16_16_16: - case TextureFormat::k_16_EXPAND: - case TextureFormat::k_16_16_EXPAND: - case TextureFormat::k_16_16_16_16_EXPAND: - case TextureFormat::k_32_32: - case TextureFormat::k_32_32_32_32: - case TextureFormat::k_32_AS_8: - case TextureFormat::k_32_AS_8_8: - case TextureFormat::k_16_MPEG: - case TextureFormat::k_16_16_MPEG: - case TextureFormat::k_8_INTERLACED: - case TextureFormat::k_32_AS_8_INTERLACED: - case TextureFormat::k_32_AS_8_8_INTERLACED: - case TextureFormat::k_16_INTERLACED: - case TextureFormat::k_16_MPEG_INTERLACED: - case TextureFormat::k_16_16_MPEG_INTERLACED: - case TextureFormat::k_DXT3A: - case TextureFormat::k_DXT5A: - case TextureFormat::k_CTX1: - case TextureFormat::k_DXT3A_AS_1_1_1_1: - assert_unhandled_case(texture_info.format); - return false; + const auto& config = + texture_configs[uint32_t(texture_info.format_info->format)]; + if (config.format == GL_INVALID_ENUM) { + assert_always("Unhandled texture format"); + return false; } - size_t unpack_length = texture_info.input_length; - glTextureStorage2D(texture, 1, internal_format, + size_t unpack_length = texture_info.output_length; + glTextureStorage2D(texture, 1, config.internal_format, texture_info.size_2d.output_width, texture_info.size_2d.output_height); assert_true(unpack_length % 4 == 0); @@ -574,8 +551,7 @@ bool TextureCache::UploadTexture2D(GLuint texture, auto allocation = scratch_buffer_->Acquire(unpack_length); if (!texture_info.is_tiled) { - if (texture_info.size_2d.input_pitch == - texture_info.size_2d.logical_pitch) { + if (texture_info.size_2d.input_pitch == texture_info.size_2d.output_pitch) { // Fast path copy entire image. TextureSwap(texture_info.endianness, allocation.host_ptr, host_address, unpack_length); @@ -585,11 +561,12 @@ bool TextureCache::UploadTexture2D(GLuint texture, // this exact thing under the covers, so we just always do it here. const uint8_t* src = host_address; uint8_t* dest = reinterpret_cast(allocation.host_ptr); + uint32_t pitch = std::min(texture_info.size_2d.input_pitch, + texture_info.size_2d.output_pitch); for (uint32_t y = 0; y < texture_info.size_2d.block_height; y++) { - TextureSwap(texture_info.endianness, dest, src, - texture_info.size_2d.logical_pitch); + TextureSwap(texture_info.endianness, dest, src, pitch); src += texture_info.size_2d.input_pitch; - dest += texture_info.size_2d.logical_pitch; + dest += texture_info.size_2d.output_pitch; } } } else { @@ -598,26 +575,26 @@ bool TextureCache::UploadTexture2D(GLuint texture, // TODO(benvanik): optimize this inner loop (or work by tiles). const uint8_t* src = host_address; uint8_t* dest = reinterpret_cast(allocation.host_ptr); - uint32_t output_pitch = - (texture_info.size_2d.output_width / texture_info.block_size) * - texture_info.texel_pitch; - auto bpp = - (texture_info.texel_pitch >> 2) + - ((texture_info.texel_pitch >> 1) >> (texture_info.texel_pitch >> 2)); + uint32_t bytes_per_block = texture_info.format_info->block_width * + texture_info.format_info->block_height * + texture_info.format_info->bits_per_pixel / 8; + auto bpp = (bytes_per_block >> 2) + + ((bytes_per_block >> 1) >> (bytes_per_block >> 2)); for (uint32_t y = 0, output_base_offset = 0; y < texture_info.size_2d.block_height; - y++, output_base_offset += output_pitch) { + y++, output_base_offset += texture_info.size_2d.output_pitch) { auto input_base_offset = TextureInfo::TiledOffset2DOuter( - y, (texture_info.size_2d.input_width / texture_info.block_size), bpp); + y, (texture_info.size_2d.input_width / + texture_info.format_info->block_width), + bpp); for (uint32_t x = 0, output_offset = output_base_offset; x < texture_info.size_2d.block_width; - x++, output_offset += texture_info.texel_pitch) { + x++, output_offset += bytes_per_block) { auto input_offset = TextureInfo::TiledOffset2DInner(x, y, bpp, input_base_offset) >> bpp; TextureSwap(texture_info.endianness, dest + output_offset, - src + input_offset * texture_info.texel_pitch, - texture_info.texel_pitch); + src + input_offset * bytes_per_block, bytes_per_block); } } } @@ -628,12 +605,12 @@ bool TextureCache::UploadTexture2D(GLuint texture, scratch_buffer_->Flush(); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratch_buffer_->handle()); - if (texture_info.is_compressed) { - glCompressedTextureSubImage2D(texture, 0, 0, 0, - texture_info.size_2d.output_width, - texture_info.size_2d.output_height, format, - static_cast(unpack_length), - reinterpret_cast(unpack_offset)); + if (texture_info.is_compressed()) { + glCompressedTextureSubImage2D( + texture, 0, 0, 0, texture_info.size_2d.output_width, + texture_info.size_2d.output_height, config.format, + static_cast(unpack_length), + reinterpret_cast(unpack_offset)); } else { // Most of these don't seem to have an effect on compressed images. // glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE); @@ -642,8 +619,8 @@ bool TextureCache::UploadTexture2D(GLuint texture, glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTextureSubImage2D(texture, 0, 0, 0, texture_info.size_2d.output_width, - texture_info.size_2d.output_height, format, type, - reinterpret_cast(unpack_offset)); + texture_info.size_2d.output_height, config.format, + config.type, reinterpret_cast(unpack_offset)); } glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); return true; diff --git a/src/xenia/gpu/texture_info.cc b/src/xenia/gpu/texture_info.cc index 9fd7aa744..82fa9ddfa 100644 --- a/src/xenia/gpu/texture_info.cc +++ b/src/xenia/gpu/texture_info.cc @@ -19,6 +19,78 @@ namespace gpu { using namespace xe::gpu::ucode; using namespace xe::gpu::xenos; +static const FormatInfo format_infos[64] = { + {TextureFormat::k_1_REVERSE, FormatType::kUncompressed, 1, 1, 1}, + {TextureFormat::k_1, FormatType::kUncompressed, 1, 1, 1}, + {TextureFormat::k_8, FormatType::kUncompressed, 1, 1, 8}, + {TextureFormat::k_1_5_5_5, FormatType::kUncompressed, 1, 1, 16}, + {TextureFormat::k_5_6_5, FormatType::kUncompressed, 1, 1, 16}, + {TextureFormat::k_6_5_5, FormatType::kUncompressed, 1, 1, 16}, + {TextureFormat::k_8_8_8_8, FormatType::kUncompressed, 1, 1, 32}, + {TextureFormat::k_2_10_10_10, FormatType::kUncompressed, 1, 1, 32}, + {TextureFormat::k_8_A, FormatType::kUncompressed, 1, 1, 8}, + {TextureFormat::k_8_B, FormatType::kUncompressed, 1, 1, 8}, + {TextureFormat::k_8_8, FormatType::kUncompressed, 1, 1, 16}, + {TextureFormat::k_Cr_Y1_Cb_Y0, FormatType::kCompressed, 2, 1, 16}, + {TextureFormat::k_Y1_Cr_Y0_Cb, FormatType::kCompressed, 2, 1, 16}, + {TextureFormat::kUnknown, FormatType::kUncompressed, 0, 0}, + {TextureFormat::k_8_8_8_8_A, FormatType::kUncompressed, 1, 1, 32}, + {TextureFormat::k_4_4_4_4, FormatType::kUncompressed, 1, 1, 16}, + {TextureFormat::k_10_11_11, FormatType::kUncompressed, 1, 1, 32}, + {TextureFormat::k_11_11_10, FormatType::kUncompressed, 1, 1, 32}, + {TextureFormat::k_DXT1, FormatType::kCompressed, 4, 4, 4}, + {TextureFormat::k_DXT2_3, FormatType::kCompressed, 4, 4, 8}, + {TextureFormat::k_DXT4_5, FormatType::kCompressed, 4, 4, 8}, + {TextureFormat::kUnknown, FormatType::kUncompressed, 0, 0}, + {TextureFormat::k_24_8, FormatType::kUncompressed, 1, 1, 32}, + {TextureFormat::k_24_8_FLOAT, FormatType::kUncompressed, 1, 1, 32}, + {TextureFormat::k_16, FormatType::kUncompressed, 1, 1, 16}, + {TextureFormat::k_16_16, FormatType::kUncompressed, 1, 1, 32}, + {TextureFormat::k_16_16_16_16, FormatType::kUncompressed, 1, 1, 64}, + {TextureFormat::k_16_EXPAND, FormatType::kUncompressed, 1, 1, 16}, + {TextureFormat::k_16_16_EXPAND, FormatType::kUncompressed, 1, 1, 32}, + {TextureFormat::k_16_16_16_16_EXPAND, FormatType::kUncompressed, 1, 1, 64}, + {TextureFormat::k_16_FLOAT, FormatType::kUncompressed, 1, 1, 16}, + {TextureFormat::k_16_16_FLOAT, FormatType::kUncompressed, 1, 1, 32}, + {TextureFormat::k_16_16_16_16_FLOAT, FormatType::kUncompressed, 1, 1, 64}, + {TextureFormat::k_32, FormatType::kUncompressed, 1, 1, 32}, + {TextureFormat::k_32_32, FormatType::kUncompressed, 1, 1, 64}, + {TextureFormat::k_32_32_32_32, FormatType::kUncompressed, 1, 1, 128}, + {TextureFormat::k_32_FLOAT, FormatType::kUncompressed, 1, 1, 32}, + {TextureFormat::k_32_32_FLOAT, FormatType::kUncompressed, 1, 1, 64}, + {TextureFormat::k_32_32_32_32_FLOAT, FormatType::kUncompressed, 1, 1, 128}, + {TextureFormat::k_32_AS_8, FormatType::kCompressed, 4, 1, 8}, + {TextureFormat::k_32_AS_8_8, FormatType::kCompressed, 2, 1, 16}, + {TextureFormat::k_16_MPEG, FormatType::kUncompressed, 1, 1, 16}, + {TextureFormat::k_16_16_MPEG, FormatType::kUncompressed, 1, 1, 32}, + {TextureFormat::k_8_INTERLACED, FormatType::kUncompressed, 1, 1, 8}, + {TextureFormat::k_32_AS_8_INTERLACED, FormatType::kCompressed, 4, 1, 8}, + {TextureFormat::k_32_AS_8_8_INTERLACED, FormatType::kCompressed, 1, 1, 16}, + {TextureFormat::k_16_INTERLACED, FormatType::kUncompressed, 1, 1, 16}, + {TextureFormat::k_16_MPEG_INTERLACED, FormatType::kUncompressed, 1, 1, 16}, + {TextureFormat::k_16_16_MPEG_INTERLACED, FormatType::kUncompressed, 1, 1, + 32}, + {TextureFormat::k_DXN, FormatType::kCompressed, 4, 4, 8}, + {TextureFormat::k_8_8_8_8_AS_16_16_16_16, FormatType::kUncompressed, 1, 1, + 32}, + {TextureFormat::k_DXT1_AS_16_16_16_16, FormatType::kCompressed, 4, 4, 4}, + {TextureFormat::k_DXT2_3_AS_16_16_16_16, FormatType::kCompressed, 4, 4, 8}, + {TextureFormat::k_DXT4_5_AS_16_16_16_16, FormatType::kCompressed, 4, 4, 8}, + {TextureFormat::k_2_10_10_10_AS_16_16_16_16, FormatType::kUncompressed, 1, + 1, 32}, + {TextureFormat::k_10_11_11_AS_16_16_16_16, FormatType::kUncompressed, 1, 1, + 32}, + {TextureFormat::k_11_11_10_AS_16_16_16_16, FormatType::kUncompressed, 1, 1, + 32}, + {TextureFormat::k_32_32_32_FLOAT, FormatType::kUncompressed, 1, 1, 96}, + {TextureFormat::k_DXT3A, FormatType::kCompressed, 4, 4, 4}, + {TextureFormat::k_DXT5A, FormatType::kCompressed, 4, 4, 4}, + {TextureFormat::k_CTX1, FormatType::kCompressed, 4, 4, 4}, + {TextureFormat::k_DXT3A_AS_1_1_1_1, FormatType::kCompressed, 4, 4, 4}, + {TextureFormat::kUnknown, FormatType::kUncompressed, 0, 0}, + {TextureFormat::kUnknown, FormatType::kUncompressed, 0, 0}, +}; + bool TextureInfo::Prepare(const xe_gpu_texture_fetch_t& fetch, TextureInfo* out_info) { std::memset(out_info, 0, sizeof(TextureInfo)); @@ -46,125 +118,20 @@ bool TextureInfo::Prepare(const xe_gpu_texture_fetch_t& fetch, info.depth = fetch.size_3d.depth; break; } + info.format_info = &format_infos[fetch.format]; info.endianness = static_cast(fetch.endianness); - - info.block_size = 0; - info.texel_pitch = 0; info.is_tiled = fetch.tiled; - info.is_compressed = false; - info.input_length = 0; - info.format = static_cast(fetch.format); - switch (fetch.format) { - case FMT_8: - info.block_size = 1; - info.texel_pitch = 1; - break; - case FMT_1_5_5_5: - info.block_size = 1; - info.texel_pitch = 2; - break; - case FMT_8_8_8_8: - case FMT_8_8_8_8_AS_16_16_16_16: - info.block_size = 1; - info.texel_pitch = 4; - break; - case FMT_4_4_4_4: - info.block_size = 1; - info.texel_pitch = 2; - break; - case FMT_16_16_16_16_FLOAT: - info.block_size = 1; - info.texel_pitch = 8; - break; - case FMT_32_FLOAT: - info.block_size = 1; - info.texel_pitch = 4; - break; - case FMT_DXT1: - info.block_size = 4; - info.texel_pitch = 8; - info.is_compressed = true; - break; - case FMT_DXT2_3: - case FMT_DXT4_5: - info.block_size = 4; - info.texel_pitch = 16; - info.is_compressed = true; - break; - case FMT_DXN: - // BC5 - info.block_size = 4; - info.texel_pitch = 16; - info.is_compressed = true; - break; - case FMT_DXT1_AS_16_16_16_16: - // TODO(benvanik): conversion? - info.block_size = 4; - info.texel_pitch = 8; - info.is_compressed = true; - break; - case FMT_DXT2_3_AS_16_16_16_16: - case FMT_DXT4_5_AS_16_16_16_16: - // TODO(benvanik): conversion? - info.block_size = 4; - info.texel_pitch = 16; - info.is_compressed = true; - break; - case FMT_1_REVERSE: - case FMT_1: - case FMT_5_6_5: - case FMT_6_5_5: - case FMT_2_10_10_10: - case FMT_8_A: - case FMT_8_B: - case FMT_8_8: - case FMT_Cr_Y1_Cb_Y0: - case FMT_Y1_Cr_Y0_Cb: - case FMT_5_5_5_1: - case FMT_8_8_8_8_A: - case FMT_10_11_11: - case FMT_11_11_10: - case FMT_24_8: - case FMT_24_8_FLOAT: - case FMT_16: - case FMT_16_16: - case FMT_16_16_16_16: - case FMT_16_EXPAND: - case FMT_16_16_EXPAND: - case FMT_16_16_16_16_EXPAND: - case FMT_16_FLOAT: - case FMT_16_16_FLOAT: - case FMT_32: - case FMT_32_32: - case FMT_32_32_32_32: - case FMT_32_32_FLOAT: - case FMT_32_32_32_32_FLOAT: - case FMT_32_AS_8: - case FMT_32_AS_8_8: - case FMT_16_MPEG: - case FMT_16_16_MPEG: - case FMT_8_INTERLACED: - case FMT_32_AS_8_INTERLACED: - case FMT_32_AS_8_8_INTERLACED: - case FMT_16_INTERLACED: - case FMT_16_MPEG_INTERLACED: - case FMT_16_16_MPEG_INTERLACED: - case FMT_2_10_10_10_AS_16_16_16_16: - case FMT_10_11_11_AS_16_16_16_16: - case FMT_11_11_10_AS_16_16_16_16: - case FMT_32_32_32_FLOAT: - case FMT_DXT3A: - case FMT_DXT5A: - case FMT_CTX1: - case FMT_DXT3A_AS_1_1_1_1: - PLOGE("Unhandled texture format"); - return false; - default: - assert_unhandled_case(fetch.format); - return false; + info.input_length = 0; // Populated below. + info.output_length = 0; + + if (info.format_info->format == TextureFormat::kUnknown) { + assert_true("Unsupported texture format"); + return false; } // Must be called here when we know the format. + info.input_length = 0; // Populated below. + info.output_length = 0; switch (info.dimension) { case Dimension::k1D: info.CalculateTextureSizes1D(fetch); @@ -192,39 +159,44 @@ void TextureInfo::CalculateTextureSizes2D(const xe_gpu_texture_fetch_t& fetch) { size_2d.logical_width = 1 + fetch.size_2d.width; size_2d.logical_height = 1 + fetch.size_2d.height; - size_2d.block_width = size_2d.logical_width / block_size; - size_2d.block_height = size_2d.logical_height / block_size; - if (!is_compressed) { - // Must be 32x32 but also must have a pitch that is a multiple of 256 bytes. - uint32_t bytes_per_block = block_size * block_size * texel_pitch; - uint32_t width_multiple = 32; - if (!is_tiled) { - // 256 stride requirement for untiled textures. - width_multiple = poly::round_up(width_multiple, 256 / bytes_per_block); - } - size_2d.input_width = poly::round_up(size_2d.logical_width, width_multiple); - size_2d.input_height = poly::round_up(size_2d.logical_height, 32); - size_2d.output_width = size_2d.logical_width; - size_2d.output_height = size_2d.logical_height; + // Here be dragons. The values here are used in texture_cache.cc to copy + // images and create GL textures. Changes here will impact that code. + // TODO(benvanik): generic texture copying utility. - size_2d.logical_pitch = (size_2d.logical_width / block_size) * texel_pitch; - size_2d.input_pitch = (size_2d.input_width / block_size) * texel_pitch; - input_length = size_2d.block_height * size_2d.logical_pitch; // ? - } else { - // Must be 128x128 minimum (block_size * 32). - size_2d.input_width = - poly::round_up(size_2d.logical_width, block_size * 32); - size_2d.input_height = - poly::round_up(size_2d.logical_height, block_size * 32); - size_2d.output_width = size_2d.input_width; - size_2d.output_height = size_2d.input_height; + // w/h in blocks must be a multiple of block size. + uint32_t block_width = + poly::round_up(size_2d.logical_width, format_info->block_width) / + format_info->block_width; + uint32_t block_height = + poly::round_up(size_2d.logical_height, format_info->block_height) / + format_info->block_height; - uint32_t block_count = (size_2d.input_width / block_size) * - (size_2d.input_height / block_size); - size_2d.logical_pitch = (size_2d.input_width / block_size) * texel_pitch; - size_2d.input_pitch = (size_2d.input_width / block_size) * texel_pitch; - input_length = block_count * texel_pitch; + // Tiles are 32x32 blocks. All textures must be multiples of tile dimensions. + uint32_t tile_width = uint32_t(std::ceilf(block_width / 32.0f)); + uint32_t tile_height = uint32_t(std::ceilf(block_height / 32.0f)); + size_2d.block_width = tile_width * 32; + size_2d.block_height = tile_height * 32; + + uint32_t bytes_per_block = format_info->block_width * + format_info->block_height * + format_info->bits_per_pixel / 8; + uint32_t byte_pitch = tile_width * 32 * bytes_per_block; + if (!is_tiled) { + // Each row must be a multiple of 256 in linear textures. + byte_pitch = poly::round_up(byte_pitch, 256); } + + size_2d.input_width = tile_width * 32 * format_info->block_width; + size_2d.input_height = tile_height * 32 * format_info->block_height; + + size_2d.output_width = block_width * format_info->block_width; + size_2d.output_height = block_height * format_info->block_height; + + size_2d.input_pitch = byte_pitch; + size_2d.output_pitch = block_width * bytes_per_block; + + input_length = size_2d.input_pitch * size_2d.block_height; + output_length = size_2d.output_pitch * block_height; } // https://code.google.com/p/crunch/source/browse/trunk/inc/crn_decomp.h#4104 diff --git a/src/xenia/gpu/texture_info.h b/src/xenia/gpu/texture_info.h index fcf37a9b2..5240c4026 100644 --- a/src/xenia/gpu/texture_info.h +++ b/src/xenia/gpu/texture_info.h @@ -84,21 +84,35 @@ enum class TextureFormat : uint32_t { kUnknown = 0xFFFFFFFFu, }; +enum class FormatType { + kUncompressed, + kCompressed, +}; + +struct FormatInfo { + TextureFormat format; + FormatType type; + uint32_t block_width; + uint32_t block_height; + uint32_t bits_per_pixel; +}; + struct TextureInfo { uint32_t guest_address; - uint32_t input_length; uint32_t swizzle; Dimension dimension; uint32_t width; uint32_t height; uint32_t depth; - uint32_t block_size; - uint32_t texel_pitch; + const FormatInfo* format_info; xenos::Endian endianness; bool is_tiled; - bool is_compressed; + uint32_t input_length; + uint32_t output_length; - TextureFormat format; + bool is_compressed() const { + return format_info->type == FormatType::kCompressed; + } union { struct { @@ -111,10 +125,10 @@ struct TextureInfo { uint32_t block_height; uint32_t input_width; uint32_t input_height; + uint32_t input_pitch; uint32_t output_width; uint32_t output_height; - uint32_t logical_pitch; - uint32_t input_pitch; + uint32_t output_pitch; } size_2d; struct { } size_3d;