[D3D12] Texture utility functions, all block sizes and bpp
This commit is contained in:
parent
35aaa72722
commit
8a24ff5078
|
@ -32,13 +32,16 @@ class D3D12CommandProcessor;
|
||||||
// - If the texture has a base address, but no mip address, it's not mipmapped -
|
// - If the texture has a base address, but no mip address, it's not mipmapped -
|
||||||
// the host texture has only the largest level too.
|
// the host texture has only the largest level too.
|
||||||
// - If the texture has different non-zero base address and mip address, a host
|
// - If the texture has different non-zero base address and mip address, a host
|
||||||
// texture full mipmap pyramid is created, disregarding min/max LOD and
|
// texture with mip_max_level+1 mipmaps is created - mip_min_level is ignored
|
||||||
// treating it purely as sampler state because there are tfetch instructions
|
// and treated purely as sampler state because there are tfetch instructions
|
||||||
// working directly with LOD values - including fetching with an explicit LOD.
|
// working directly with LOD values - including fetching with an explicit LOD.
|
||||||
|
// However, the max level is not ignored because any mip count can be
|
||||||
|
// specified when creating a texture, and another texture may be placed after
|
||||||
|
// the last one.
|
||||||
// - If the texture has a mip address, but the base address is 0 or the same as
|
// - If the texture has a mip address, but the base address is 0 or the same as
|
||||||
// the mip address, a fully mipmapped texture is created, but min/max LOD is
|
// the mip address, a mipmapped texture is created, but min/max LOD is clamped
|
||||||
// clamped to 1 - the game is expected to do that anyway until the largest LOD
|
// to the lower bound of 1 - the game is expected to do that anyway until the
|
||||||
// is loaded.
|
// largest LOD is loaded.
|
||||||
// TODO(Triang3l): Check if there are any games with BaseAddress==MipAddress
|
// TODO(Triang3l): Check if there are any games with BaseAddress==MipAddress
|
||||||
// but min or max LOD being 0, especially check Modern Warfare 2/3.
|
// but min or max LOD being 0, especially check Modern Warfare 2/3.
|
||||||
// TODO(Triang3l): Attach the largest LOD to existing textures with a valid
|
// TODO(Triang3l): Attach the largest LOD to existing textures with a valid
|
||||||
|
|
|
@ -33,7 +33,7 @@ const FormatInfo* FormatInfo::Get(uint32_t gpu_format) {
|
||||||
FORMAT_INFO(k_8_8 , kUncompressed, 1, 1, 16),
|
FORMAT_INFO(k_8_8 , kUncompressed, 1, 1, 16),
|
||||||
FORMAT_INFO(k_Cr_Y1_Cb_Y0 , kCompressed , 2, 1, 16),
|
FORMAT_INFO(k_Cr_Y1_Cb_Y0 , kCompressed , 2, 1, 16),
|
||||||
FORMAT_INFO(k_Y1_Cr_Y0_Cb , kCompressed , 2, 1, 16),
|
FORMAT_INFO(k_Y1_Cr_Y0_Cb , kCompressed , 2, 1, 16),
|
||||||
FORMAT_INFO(kUnknown , kUncompressed, 0, 0, 0), // k_Shadow
|
FORMAT_INFO(kUnknown , kUncompressed, 1, 1, 32), // k_Shadow
|
||||||
FORMAT_INFO(k_8_8_8_8_A , kUncompressed, 1, 1, 32),
|
FORMAT_INFO(k_8_8_8_8_A , kUncompressed, 1, 1, 32),
|
||||||
FORMAT_INFO(k_4_4_4_4 , kUncompressed, 1, 1, 16),
|
FORMAT_INFO(k_4_4_4_4 , kUncompressed, 1, 1, 16),
|
||||||
FORMAT_INFO(k_10_11_11 , kUncompressed, 1, 1, 32),
|
FORMAT_INFO(k_10_11_11 , kUncompressed, 1, 1, 32),
|
||||||
|
@ -41,7 +41,7 @@ const FormatInfo* FormatInfo::Get(uint32_t gpu_format) {
|
||||||
FORMAT_INFO(k_DXT1 , kCompressed , 4, 4, 4),
|
FORMAT_INFO(k_DXT1 , kCompressed , 4, 4, 4),
|
||||||
FORMAT_INFO(k_DXT2_3 , kCompressed , 4, 4, 8),
|
FORMAT_INFO(k_DXT2_3 , kCompressed , 4, 4, 8),
|
||||||
FORMAT_INFO(k_DXT4_5 , kCompressed , 4, 4, 8),
|
FORMAT_INFO(k_DXT4_5 , kCompressed , 4, 4, 8),
|
||||||
FORMAT_INFO(kUnknown , kUncompressed, 0, 0, 0), // k_DXV
|
FORMAT_INFO(kUnknown , kUncompressed, 1, 1, 64), // k_DXV
|
||||||
FORMAT_INFO(k_24_8 , kUncompressed, 1, 1, 32),
|
FORMAT_INFO(k_24_8 , kUncompressed, 1, 1, 32),
|
||||||
FORMAT_INFO(k_24_8_FLOAT , kUncompressed, 1, 1, 32),
|
FORMAT_INFO(k_24_8_FLOAT , kUncompressed, 1, 1, 32),
|
||||||
FORMAT_INFO(k_16 , kUncompressed, 1, 1, 16),
|
FORMAT_INFO(k_16 , kUncompressed, 1, 1, 16),
|
||||||
|
@ -82,8 +82,8 @@ const FormatInfo* FormatInfo::Get(uint32_t gpu_format) {
|
||||||
FORMAT_INFO(k_DXT5A , kCompressed , 4, 4, 4),
|
FORMAT_INFO(k_DXT5A , kCompressed , 4, 4, 4),
|
||||||
FORMAT_INFO(k_CTX1 , kCompressed , 4, 4, 4),
|
FORMAT_INFO(k_CTX1 , kCompressed , 4, 4, 4),
|
||||||
FORMAT_INFO(k_DXT3A_AS_1_1_1_1 , kCompressed , 4, 4, 4),
|
FORMAT_INFO(k_DXT3A_AS_1_1_1_1 , kCompressed , 4, 4, 4),
|
||||||
FORMAT_INFO(kUnknown , kUncompressed, 0, 0, 0), // k_2_10_10_10_FLOAT
|
FORMAT_INFO(kUnknown , kUncompressed, 1, 1, 32), // k_2_10_10_10_FLOAT
|
||||||
FORMAT_INFO(kUnknown , kUncompressed, 0, 0, 0), // invalid
|
FORMAT_INFO(kUnknown , kUncompressed, 1, 1, 32), // invalid
|
||||||
};
|
};
|
||||||
return &format_infos[gpu_format];
|
return &format_infos[gpu_format];
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2018 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "xenia/gpu/texture_util.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "xenia/base/assert.h"
|
||||||
|
#include "xenia/base/math.h"
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace gpu {
|
||||||
|
namespace texture_util {
|
||||||
|
|
||||||
|
bool GetGuestMipBlocks(Dimension dimension, uint32_t width, uint32_t height,
|
||||||
|
uint32_t depth, TextureFormat format, uint32_t mip,
|
||||||
|
uint32_t& width_blocks_out, uint32_t& height_blocks_out,
|
||||||
|
uint32_t& depth_blocks_out) {
|
||||||
|
// Get mipmap size.
|
||||||
|
// TODO(Triang3l): Verify if mipmap storage actually needs to be power of two.
|
||||||
|
if (mip != 0) {
|
||||||
|
width = std::max(xe::next_pow2(width) >> mip, 1u);
|
||||||
|
if (dimension != Dimension::k1D) {
|
||||||
|
height = std::max(xe::next_pow2(height) >> mip, 1u);
|
||||||
|
if (dimension == Dimension::k3D) {
|
||||||
|
depth = std::max(xe::next_pow2(depth) >> mip, 1u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the size in blocks rather than in pixels.
|
||||||
|
const FormatInfo* format_info = FormatInfo::Get(format);
|
||||||
|
width = xe::align(width, format_info->block_width) / format_info->block_width;
|
||||||
|
height =
|
||||||
|
xe::align(height, format_info->block_height) / format_info->block_height;
|
||||||
|
|
||||||
|
// 32x32x4-align.
|
||||||
|
width_blocks_out = xe::align(width, 32u);
|
||||||
|
if (dimension != Dimension::k1D) {
|
||||||
|
height_blocks_out = xe::align(height, 32u);
|
||||||
|
if (dimension == Dimension::k3D) {
|
||||||
|
depth_blocks_out = xe::align(depth, 4u);
|
||||||
|
} else {
|
||||||
|
depth_blocks_out = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
height_blocks_out = 1;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GetGuestMipStorageSize(uint32_t width_blocks, uint32_t height_blocks,
|
||||||
|
uint32_t depth_blocks, bool is_tiled,
|
||||||
|
TextureFormat format, uint32_t* row_pitch_out) {
|
||||||
|
const FormatInfo* format_info = FormatInfo::Get(format);
|
||||||
|
uint32_t row_pitch = width_blocks * format_info->block_width *
|
||||||
|
format_info->block_height * 8 /
|
||||||
|
format_info->bits_per_pixel;
|
||||||
|
if (!is_tiled) {
|
||||||
|
row_pitch = xe::align(row_pitch, 256u);
|
||||||
|
}
|
||||||
|
if (row_pitch_out != nullptr) {
|
||||||
|
*row_pitch_out = row_pitch;
|
||||||
|
}
|
||||||
|
return xe::align(row_pitch * height_blocks * depth_blocks, 4096u);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetPackedMipOffset(uint32_t width, uint32_t height, uint32_t depth,
|
||||||
|
TextureFormat format, uint32_t mip, uint32_t& x_blocks,
|
||||||
|
uint32_t& y_blocks, uint32_t& z_blocks) {
|
||||||
|
// Tile size is 32x32, and once textures go <=16 they are packed into a
|
||||||
|
// single tile together. The math here is insane. Most sourced from
|
||||||
|
// graph paper, looking at dds dumps and executable reverse engineering.
|
||||||
|
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
|
// 0 +.4x4.+ +.....8x8.....+ +............16x16............+
|
||||||
|
// 1 +.4x4.+ +.....8x8.....+ +............16x16............+
|
||||||
|
// 2 +.4x4.+ +.....8x8.....+ +............16x16............+
|
||||||
|
// 3 +.4x4.+ +.....8x8.....+ +............16x16............+
|
||||||
|
// 4 x +.....8x8.....+ +............16x16............+
|
||||||
|
// 5 +.....8x8.....+ +............16x16............+
|
||||||
|
// 6 +.....8x8.....+ +............16x16............+
|
||||||
|
// 7 +.....8x8.....+ +............16x16............+
|
||||||
|
// 8 2x2 +............16x16............+
|
||||||
|
// 9 2x2 +............16x16............+
|
||||||
|
// 0 +............16x16............+
|
||||||
|
// ... .....
|
||||||
|
//
|
||||||
|
// The 2x2 and 1x1 squares are packed in their specific positions because
|
||||||
|
// each square is the size of at least one block (which is 4x4 pixels max)
|
||||||
|
//
|
||||||
|
// if (tile_aligned(w) > tile_aligned(h)) {
|
||||||
|
// // wider than tall, so packed horizontally
|
||||||
|
// } else if (tile_aligned(w) < tile_aligned(h)) {
|
||||||
|
// // taller than wide, so packed vertically
|
||||||
|
// } else {
|
||||||
|
// square
|
||||||
|
// }
|
||||||
|
// It's important to use logical sizes here, as the input sizes will be
|
||||||
|
// for the entire packed tile set, not the actual texture.
|
||||||
|
// The minimum dimension is what matters most: if either width or height
|
||||||
|
// is <= 16 this mode kicks in.
|
||||||
|
|
||||||
|
uint32_t log2_width = xe::log2_ceil(width);
|
||||||
|
uint32_t log2_height = xe::log2_ceil(height);
|
||||||
|
uint32_t log2_size = std::min(log2_width, log2_height);
|
||||||
|
if (log2_size > 4 + mip) {
|
||||||
|
// The shortest dimension is bigger than 16, not packed.
|
||||||
|
x_blocks = 0;
|
||||||
|
y_blocks = 0;
|
||||||
|
z_blocks = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint32_t packed_mip_base = (log2_size > 4) ? (log2_size - 4) : 0;
|
||||||
|
uint32_t packed_mip = mip - packed_mip_base;
|
||||||
|
|
||||||
|
// Find the block offset of the mip.
|
||||||
|
if (packed_mip < 3) {
|
||||||
|
if (log2_width > log2_height) {
|
||||||
|
// Wider than tall. Laid out vertically.
|
||||||
|
x_blocks = 0;
|
||||||
|
y_blocks = 16 >> packed_mip;
|
||||||
|
} else {
|
||||||
|
// Taller than wide. Laid out horizontally.
|
||||||
|
x_blocks = 16 >> packed_mip;
|
||||||
|
y_blocks = 0;
|
||||||
|
}
|
||||||
|
z_blocks = 0;
|
||||||
|
} else {
|
||||||
|
uint32_t offset;
|
||||||
|
if (log2_width > log2_height) {
|
||||||
|
// Wider than tall. Laid out horizontally.
|
||||||
|
offset = (1 << (log2_width - packed_mip_base)) >> (packed_mip - 2);
|
||||||
|
x_blocks = offset;
|
||||||
|
y_blocks = 0;
|
||||||
|
} else {
|
||||||
|
// Taller than wide. Laid out vertically.
|
||||||
|
x_blocks = 0;
|
||||||
|
offset = (1 << (log2_height - packed_mip_base)) >> (packed_mip - 2);
|
||||||
|
y_blocks = offset;
|
||||||
|
}
|
||||||
|
if (offset < 4) {
|
||||||
|
// Pack 1x1 Z mipmaps along Z - not reached for 2D.
|
||||||
|
uint32_t log2_depth = xe::log2_ceil(depth);
|
||||||
|
if (log2_depth > 1 + packed_mip) {
|
||||||
|
z_blocks = (log2_depth - packed_mip) * 4;
|
||||||
|
} else {
|
||||||
|
z_blocks = 4;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
z_blocks = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const FormatInfo* format_info = FormatInfo::Get(format);
|
||||||
|
x_blocks /= format_info->block_width;
|
||||||
|
y_blocks /= format_info->block_height;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace texture_util
|
||||||
|
} // namespace gpu
|
||||||
|
} // namespace xe
|
|
@ -0,0 +1,67 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2018 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XENIA_GPU_TEXTURE_UTIL_H_
|
||||||
|
#define XENIA_GPU_TEXTURE_UTIL_H_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "xenia/base/math.h"
|
||||||
|
#include "xenia/gpu/texture_info.h"
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace gpu {
|
||||||
|
namespace texture_util {
|
||||||
|
|
||||||
|
// This namespace replaces texture_extent and most of texture_info for
|
||||||
|
// simplicity.
|
||||||
|
|
||||||
|
// Calculates width, height and depth of the image backing the guest mipmap (or
|
||||||
|
// the base level if mip is 0).
|
||||||
|
bool GetGuestMipBlocks(Dimension dimension, uint32_t width, uint32_t height,
|
||||||
|
uint32_t depth, TextureFormat format, uint32_t mip,
|
||||||
|
uint32_t& width_blocks_out, uint32_t& height_blocks_out,
|
||||||
|
uint32_t& depth_blocks_out);
|
||||||
|
|
||||||
|
// Calculates the number of bytes required to store a single mip level - width,
|
||||||
|
// height and depth must be obtained via GetGuestMipExtent.
|
||||||
|
uint32_t GetGuestMipStorageSize(uint32_t width_blocks, uint32_t height_blocks,
|
||||||
|
uint32_t depth_blocks, bool is_tiled,
|
||||||
|
TextureFormat format, uint32_t* row_pitch_out);
|
||||||
|
|
||||||
|
// Gets the number of the mipmap level where the packed mips are stored.
|
||||||
|
inline uint32_t GetPackedMipLevel(uint32_t width, uint32_t height) {
|
||||||
|
uint32_t log2_size = xe::log2_ceil(std::min(width, height));
|
||||||
|
return log2_size > 4 ? log2_size - 4 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the offset of the mipmap within the tail in blocks, or zeros (and
|
||||||
|
// returns false) if the mip level is not packed. Width, height and depth are in
|
||||||
|
// texels. For non-3D textures, set depth to 1.
|
||||||
|
bool GetPackedMipOffset(uint32_t width, uint32_t height, uint32_t depth,
|
||||||
|
TextureFormat format, uint32_t mip, uint32_t& x_blocks,
|
||||||
|
uint32_t& y_blocks, uint32_t& z_blocks);
|
||||||
|
|
||||||
|
// Calculates the maximum mipmap count for a texture.
|
||||||
|
inline uint32_t GetMaximumMipCount(uint32_t width, uint32_t height,
|
||||||
|
uint32_t depth, bool has_packed_mips) {
|
||||||
|
uint32_t size = std::max(width, height);
|
||||||
|
size = std::max(size, depth);
|
||||||
|
uint32_t smallest_mip = xe::log2_ceil(size);
|
||||||
|
if (has_packed_mips) {
|
||||||
|
smallest_mip = std::min(smallest_mip, GetPackedMipLevel(width, height));
|
||||||
|
}
|
||||||
|
return smallest_mip + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace texture_util
|
||||||
|
} // namespace gpu
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
|
#endif // XENIA_GPU_TEXTURE_UTIL_H_
|
Loading…
Reference in New Issue