[D3D12] Fix 8192 texture size storage

This commit is contained in:
Triang3l 2021-12-12 16:27:49 +03:00
parent d813f7435b
commit 9606ff2a31
4 changed files with 95 additions and 79 deletions

View File

@ -1263,7 +1263,7 @@ void TextureCache::RequestTextures(uint32_t used_texture_mask) {
BindingInfoFromFetchConstant(fetch, binding.key, &binding.host_swizzle, BindingInfoFromFetchConstant(fetch, binding.key, &binding.host_swizzle,
&binding.swizzled_signs); &binding.swizzled_signs);
texture_bindings_in_sync_ |= index_bit; texture_bindings_in_sync_ |= index_bit;
if (binding.key.IsInvalid()) { if (!binding.key.is_valid) {
binding.texture = nullptr; binding.texture = nullptr;
binding.texture_signed = nullptr; binding.texture_signed = nullptr;
binding.descriptor_index = UINT32_MAX; binding.descriptor_index = UINT32_MAX;
@ -1425,7 +1425,7 @@ void TextureCache::WriteActiveTextureBindfulSRV(
texture_bindings_[host_shader_binding.fetch_constant]; texture_bindings_[host_shader_binding.fetch_constant];
uint32_t descriptor_index = UINT32_MAX; uint32_t descriptor_index = UINT32_MAX;
Texture* texture = nullptr; Texture* texture = nullptr;
if (!binding.key.IsInvalid() && if (binding.key.is_valid &&
AreDimensionsCompatible(host_shader_binding.dimension, AreDimensionsCompatible(host_shader_binding.dimension,
binding.key.dimension)) { binding.key.dimension)) {
if (host_shader_binding.is_signed) { if (host_shader_binding.is_signed) {
@ -1487,7 +1487,7 @@ uint32_t TextureCache::GetActiveTextureBindlessSRVIndex(
uint32_t descriptor_index = UINT32_MAX; uint32_t descriptor_index = UINT32_MAX;
const TextureBinding& binding = const TextureBinding& binding =
texture_bindings_[host_shader_binding.fetch_constant]; texture_bindings_[host_shader_binding.fetch_constant];
if (!binding.key.IsInvalid() && if (binding.key.is_valid &&
AreDimensionsCompatible(host_shader_binding.dimension, AreDimensionsCompatible(host_shader_binding.dimension,
binding.key.dimension)) { binding.key.dimension)) {
descriptor_index = host_shader_binding.is_signed descriptor_index = host_shader_binding.is_signed
@ -2026,7 +2026,7 @@ TextureCache::LoadMode TextureCache::GetLoadMode(TextureKey key) {
if (key.signed_separate) { if (key.signed_separate) {
return host_format.load_mode_snorm; return host_format.load_mode_snorm;
} }
if (IsDecompressionNeeded(key.format, key.width, key.height)) { if (IsDecompressionNeeded(key.format, key.GetWidth(), key.GetHeight())) {
return host_format.decompress_mode; return host_format.decompress_mode;
} }
return host_format.load_mode; return host_format.load_mode;
@ -2071,11 +2071,11 @@ void TextureCache::BindingInfoFromFetchConstant(
return; return;
} }
uint32_t width, height, depth_or_faces; uint32_t width_minus_1, height_minus_1, depth_or_array_size_minus_1;
uint32_t base_page, mip_page, mip_max_level; uint32_t base_page, mip_page, mip_max_level;
texture_util::GetSubresourcesFromFetchConstant( texture_util::GetSubresourcesFromFetchConstant(
fetch, &width, &height, &depth_or_faces, &base_page, &mip_page, nullptr, fetch, &width_minus_1, &height_minus_1, &depth_or_array_size_minus_1,
&mip_max_level); &base_page, &mip_page, nullptr, &mip_max_level);
if (base_page == 0 && mip_page == 0) { if (base_page == 0 && mip_page == 0) {
// No texture data at all. // No texture data at all.
return; return;
@ -2083,11 +2083,11 @@ void TextureCache::BindingInfoFromFetchConstant(
if (fetch.dimension == xenos::DataDimension::k1D) { if (fetch.dimension == xenos::DataDimension::k1D) {
bool is_invalid_1d = false; bool is_invalid_1d = false;
// TODO(Triang3l): Support long 1D textures. // TODO(Triang3l): Support long 1D textures.
if (width > xenos::kTexture2DCubeMaxWidthHeight) { if (width_minus_1 >= xenos::kTexture2DCubeMaxWidthHeight) {
XELOGE( XELOGE(
"1D texture is too wide ({}) - ignoring! Report the game to Xenia " "1D texture is too wide ({}) - ignoring! Report the game to Xenia "
"developers", "developers",
width); width_minus_1 + 1);
is_invalid_1d = true; is_invalid_1d = true;
} }
assert_false(fetch.tiled); assert_false(fetch.tiled);
@ -2116,9 +2116,9 @@ void TextureCache::BindingInfoFromFetchConstant(
key_out.base_page = base_page; key_out.base_page = base_page;
key_out.mip_page = mip_page; key_out.mip_page = mip_page;
key_out.dimension = fetch.dimension; key_out.dimension = fetch.dimension;
key_out.width = width; key_out.width_minus_1 = width_minus_1;
key_out.height = height; key_out.height_minus_1 = height_minus_1;
key_out.depth = depth_or_faces; key_out.depth_or_array_size_minus_1 = depth_or_array_size_minus_1;
key_out.pitch = fetch.pitch; key_out.pitch = fetch.pitch;
key_out.mip_max_level = mip_max_level; key_out.mip_max_level = mip_max_level;
key_out.tiled = fetch.tiled; key_out.tiled = fetch.tiled;
@ -2126,6 +2126,8 @@ void TextureCache::BindingInfoFromFetchConstant(
key_out.format = format; key_out.format = format;
key_out.endianness = fetch.endianness; key_out.endianness = fetch.endianness;
key_out.is_valid = 1;
if (host_swizzle_out != nullptr) { if (host_swizzle_out != nullptr) {
uint32_t host_swizzle = 0; uint32_t host_swizzle = 0;
for (uint32_t i = 0; i < 4; ++i) { for (uint32_t i = 0; i < 4; ++i) {
@ -2154,8 +2156,8 @@ void TextureCache::LogTextureKeyAction(TextureKey key, const char* action) {
"{} {} {}{}x{}x{} {} {} texture with {} {}packed mip level{}, " "{} {} {}{}x{}x{} {} {} texture with {} {}packed mip level{}, "
"base at 0x{:08X} (pitch {}), mips at 0x{:08X}", "base at 0x{:08X} (pitch {}), mips at 0x{:08X}",
action, key.tiled ? "tiled" : "linear", action, key.tiled ? "tiled" : "linear",
key.scaled_resolve ? "scaled " : "", key.width, key.height, key.depth, key.scaled_resolve ? "scaled " : "", key.GetWidth(), key.GetHeight(),
dimension_names_[uint32_t(key.dimension)], key.GetDepthOrArraySize(), dimension_names_[uint32_t(key.dimension)],
FormatInfo::Get(key.format)->name, key.mip_max_level + 1, FormatInfo::Get(key.format)->name, key.mip_max_level + 1,
key.packed_mips ? "" : "un", key.mip_max_level != 0 ? "s" : "", key.packed_mips ? "" : "un", key.mip_max_level != 0 ? "s" : "",
key.base_page << 12, key.pitch << 5, key.mip_page << 12); key.base_page << 12, key.pitch << 5, key.mip_page << 12);
@ -2168,8 +2170,8 @@ void TextureCache::LogTextureAction(const Texture* texture,
"base at 0x{:08X} (pitch {}, size 0x{:08X}), mips at 0x{:08X} (size " "base at 0x{:08X} (pitch {}, size 0x{:08X}), mips at 0x{:08X} (size "
"0x{:08X})", "0x{:08X})",
action, texture->key.tiled ? "tiled" : "linear", action, texture->key.tiled ? "tiled" : "linear",
texture->key.scaled_resolve ? "scaled " : "", texture->key.width, texture->key.scaled_resolve ? "scaled " : "", texture->key.GetWidth(),
texture->key.height, texture->key.depth, texture->key.GetHeight(), texture->key.GetDepthOrArraySize(),
dimension_names_[uint32_t(texture->key.dimension)], dimension_names_[uint32_t(texture->key.dimension)],
FormatInfo::Get(texture->key.format)->name, FormatInfo::Get(texture->key.format)->name,
texture->key.mip_max_level + 1, texture->key.packed_mips ? "" : "un", texture->key.mip_max_level + 1, texture->key.packed_mips ? "" : "un",
@ -2186,9 +2188,9 @@ TextureCache::Texture* TextureCache::FindOrCreateTexture(TextureKey key) {
load_pipelines_scaled_[uint32_t(load_mode)] != nullptr) { load_pipelines_scaled_[uint32_t(load_mode)] != nullptr) {
texture_util::TextureGuestLayout scaled_resolve_guest_layout = texture_util::TextureGuestLayout scaled_resolve_guest_layout =
texture_util::GetGuestTextureLayout( texture_util::GetGuestTextureLayout(
key.dimension, key.pitch, key.width, key.height, key.depth, key.dimension, key.pitch, key.GetWidth(), key.GetHeight(),
key.tiled, key.format, key.packed_mips, key.base_page != 0, key.GetDepthOrArraySize(), key.tiled, key.format, key.packed_mips,
key.mip_max_level); key.base_page != 0, key.mip_max_level);
if ((scaled_resolve_guest_layout.base.level_data_extent_bytes && if ((scaled_resolve_guest_layout.base.level_data_extent_bytes &&
IsRangeScaledResolved( IsRangeScaledResolved(
key.base_page << 12, key.base_page << 12,
@ -2201,8 +2203,8 @@ TextureCache::Texture* TextureCache::FindOrCreateTexture(TextureKey key) {
} }
} }
} }
uint32_t host_width = key.width; uint32_t host_width = key.GetWidth();
uint32_t host_height = key.height; uint32_t host_height = key.GetHeight();
if (key.scaled_resolve) { if (key.scaled_resolve) {
host_width *= draw_resolution_scale_x_; host_width *= draw_resolution_scale_x_;
host_height *= draw_resolution_scale_y_; host_height *= draw_resolution_scale_y_;
@ -2210,9 +2212,11 @@ TextureCache::Texture* TextureCache::FindOrCreateTexture(TextureKey key) {
// With 3x resolution scaling, a 2D texture may become bigger than the // With 3x resolution scaling, a 2D texture may become bigger than the
// Direct3D 11 limit, and with 2x, a 3D one as well. // Direct3D 11 limit, and with 2x, a 3D one as well.
uint32_t max_host_width_height = GetMaxHostTextureWidthHeight(key.dimension); uint32_t max_host_width_height = GetMaxHostTextureWidthHeight(key.dimension);
uint32_t max_host_depth = GetMaxHostTextureDepth(key.dimension); uint32_t max_host_depth_or_array_size =
GetMaxHostTextureDepthOrArraySize(key.dimension);
if (host_width > max_host_width_height || if (host_width > max_host_width_height ||
host_height > max_host_width_height || key.depth > max_host_depth) { host_height > max_host_width_height ||
key.GetDepthOrArraySize() > max_host_depth_or_array_size) {
return nullptr; return nullptr;
} }
@ -2242,7 +2246,7 @@ TextureCache::Texture* TextureCache::FindOrCreateTexture(TextureKey key) {
desc.Alignment = 0; desc.Alignment = 0;
desc.Width = host_width; desc.Width = host_width;
desc.Height = host_height; desc.Height = host_height;
desc.DepthOrArraySize = key.depth; desc.DepthOrArraySize = key.GetDepthOrArraySize();
desc.MipLevels = key.mip_max_level + 1; desc.MipLevels = key.mip_max_level + 1;
desc.SampleDesc.Count = 1; desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0; desc.SampleDesc.Quality = 0;
@ -2283,8 +2287,9 @@ TextureCache::Texture* TextureCache::FindOrCreateTexture(TextureKey key) {
texture->base_resolved = key.scaled_resolve; texture->base_resolved = key.scaled_resolve;
texture->mips_resolved = key.scaled_resolve; texture->mips_resolved = key.scaled_resolve;
texture->guest_layout = texture_util::GetGuestTextureLayout( texture->guest_layout = texture_util::GetGuestTextureLayout(
key.dimension, key.pitch, key.width, key.height, key.depth, key.tiled, key.dimension, key.pitch, key.GetWidth(), key.GetHeight(),
key.format, key.packed_mips, key.base_page != 0, key.mip_max_level); key.GetDepthOrArraySize(), key.tiled, key.format, key.packed_mips,
key.base_page != 0, key.mip_max_level);
// Never try to upload data that doesn't exist. // Never try to upload data that doesn't exist.
texture->base_in_sync = !texture->guest_layout.base.level_data_extent_bytes; texture->base_in_sync = !texture->guest_layout.base.level_data_extent_bytes;
texture->mips_in_sync = !texture->guest_layout.mips_total_extent_bytes; texture->mips_in_sync = !texture->guest_layout.mips_total_extent_bytes;
@ -2368,9 +2373,9 @@ bool TextureCache::LoadTextureData(Texture* texture) {
// Get the guest layout. // Get the guest layout.
xenos::DataDimension dimension = texture->key.dimension; xenos::DataDimension dimension = texture->key.dimension;
bool is_3d = dimension == xenos::DataDimension::k3D; bool is_3d = dimension == xenos::DataDimension::k3D;
uint32_t width = texture->key.width; uint32_t width = texture->key.GetWidth();
uint32_t height = texture->key.height; uint32_t height = texture->key.GetHeight();
uint32_t depth_or_array_size = texture->key.depth; uint32_t depth_or_array_size = texture->key.GetDepthOrArraySize();
uint32_t depth = is_3d ? depth_or_array_size : 1; uint32_t depth = is_3d ? depth_or_array_size : 1;
uint32_t array_size = is_3d ? 1 : depth_or_array_size; uint32_t array_size = is_3d ? 1 : depth_or_array_size;
xenos::TextureFormat guest_format = texture->key.format; xenos::TextureFormat guest_format = texture->key.format;
@ -2845,7 +2850,7 @@ uint32_t TextureCache::FindOrCreateTextureDescriptor(Texture& texture,
desc.Texture2DArray.MostDetailedMip = 0; desc.Texture2DArray.MostDetailedMip = 0;
desc.Texture2DArray.MipLevels = mip_levels; desc.Texture2DArray.MipLevels = mip_levels;
desc.Texture2DArray.FirstArraySlice = 0; desc.Texture2DArray.FirstArraySlice = 0;
desc.Texture2DArray.ArraySize = texture.key.depth; desc.Texture2DArray.ArraySize = texture.key.GetDepthOrArraySize();
desc.Texture2DArray.PlaneSlice = 0; desc.Texture2DArray.PlaneSlice = 0;
desc.Texture2DArray.ResourceMinLODClamp = 0.0f; desc.Texture2DArray.ResourceMinLODClamp = 0.0f;
break; break;

View File

@ -62,20 +62,24 @@ class D3D12CommandProcessor;
// because textures are streamed this way anyway. // because textures are streamed this way anyway.
class TextureCache { class TextureCache {
struct TextureKey { struct TextureKey {
// Dimensions minus 1 are stored similarly to how they're stored in fetch
// constants so fewer bits can be used, while the maximum size (8192 for 2D)
// can still be encoded (a 8192x sky texture is used in 4D530910).
// Physical 4 KB page with the base mip level, disregarding A/C/E address // Physical 4 KB page with the base mip level, disregarding A/C/E address
// range prefix. // range prefix.
uint32_t base_page : 17; // 17 total uint32_t base_page : 17; // 17 total
xenos::DataDimension dimension : 2; // 19 xenos::DataDimension dimension : 2; // 19
uint32_t width : 13; // 32 uint32_t width_minus_1 : 13; // 32
uint32_t height : 13; // 45 uint32_t height_minus_1 : 13; // 45
uint32_t tiled : 1; // 46 uint32_t tiled : 1; // 46
uint32_t packed_mips : 1; // 47 uint32_t packed_mips : 1; // 47
// Physical 4 KB page with mip 1 and smaller. // Physical 4 KB page with mip 1 and smaller.
uint32_t mip_page : 17; // 64 uint32_t mip_page : 17; // 64
// Layers for stacked and 3D, 6 for cube, 1 for other dimensions. // (Layers for stacked and 3D, 6 for cube, 1 for other dimensions) - 1.
uint32_t depth : 10; // 74 uint32_t depth_or_array_size_minus_1 : 10; // 74
uint32_t pitch : 9; // 83 uint32_t pitch : 9; // 83
uint32_t mip_max_level : 4; // 87 uint32_t mip_max_level : 4; // 87
xenos::TextureFormat format : 6; // 93 xenos::TextureFormat format : 6; // 93
@ -86,6 +90,8 @@ class TextureCache {
// Whether this texture is a resolution-scaled resolve target. // Whether this texture is a resolution-scaled resolve target.
uint32_t scaled_resolve : 1; // 97 uint32_t scaled_resolve : 1; // 97
// Least important in ==, so placed last.
uint32_t is_valid : 1; // 98
TextureKey() { MakeInvalid(); } TextureKey() { MakeInvalid(); }
TextureKey(const TextureKey& key) { TextureKey(const TextureKey& key) {
@ -95,16 +101,17 @@ class TextureCache {
std::memcpy(this, &key, sizeof(*this)); std::memcpy(this, &key, sizeof(*this));
return *this; return *this;
} }
bool IsInvalid() const {
// Zero size is enough for a binding to be invalid (not possible on the
// real GPU since dimensions minus 1 are stored).
return !width;
}
void MakeInvalid() { void MakeInvalid() {
// Zero everything, including the padding, for a stable hash. // Zero everything, including the padding, for a stable hash.
std::memset(this, 0, sizeof(*this)); std::memset(this, 0, sizeof(*this));
} }
uint32_t GetWidth() const { return width_minus_1 + 1; }
uint32_t GetHeight() const { return height_minus_1 + 1; }
uint32_t GetDepthOrArraySize() const {
return depth_or_array_size_minus_1 + 1;
}
using Hasher = xe::hash::XXHasher<TextureKey>; using Hasher = xe::hash::XXHasher<TextureKey>;
bool operator==(const TextureKey& key) const { bool operator==(const TextureKey& key) const {
return !std::memcmp(this, &key, sizeof(*this)); return !std::memcmp(this, &key, sizeof(*this));
@ -544,7 +551,8 @@ class TextureCache {
return 0; return 0;
} }
} }
static uint32_t GetMaxHostTextureDepth(xenos::DataDimension dimension) { static uint32_t GetMaxHostTextureDepthOrArraySize(
xenos::DataDimension dimension) {
switch (dimension) { switch (dimension) {
case xenos::DataDimension::k1D: case xenos::DataDimension::k1D:
case xenos::DataDimension::k2DOrStacked: case xenos::DataDimension::k2DOrStacked:
@ -609,7 +617,7 @@ class TextureCache {
: host_format.dxgi_format_resource; : host_format.dxgi_format_resource;
} }
static DXGI_FORMAT GetDXGIResourceFormat(TextureKey key) { static DXGI_FORMAT GetDXGIResourceFormat(TextureKey key) {
return GetDXGIResourceFormat(key.format, key.width, key.height); return GetDXGIResourceFormat(key.format, key.GetWidth(), key.GetHeight());
} }
static DXGI_FORMAT GetDXGIUnormFormat(xenos::TextureFormat format, static DXGI_FORMAT GetDXGIUnormFormat(xenos::TextureFormat format,
uint32_t width, uint32_t height) { uint32_t width, uint32_t height) {
@ -619,7 +627,7 @@ class TextureCache {
: host_format.dxgi_format_unorm; : host_format.dxgi_format_unorm;
} }
static DXGI_FORMAT GetDXGIUnormFormat(TextureKey key) { static DXGI_FORMAT GetDXGIUnormFormat(TextureKey key) {
return GetDXGIUnormFormat(key.format, key.width, key.height); return GetDXGIUnormFormat(key.format, key.GetWidth(), key.GetHeight());
} }
static LoadMode GetLoadMode(TextureKey key); static LoadMode GetLoadMode(TextureKey key);

View File

@ -20,55 +20,58 @@ namespace gpu {
namespace texture_util { namespace texture_util {
void GetSubresourcesFromFetchConstant( void GetSubresourcesFromFetchConstant(
const xenos::xe_gpu_texture_fetch_t& fetch, uint32_t* width_out, const xenos::xe_gpu_texture_fetch_t& fetch, uint32_t* width_minus_1_out,
uint32_t* height_out, uint32_t* depth_or_faces_out, uint32_t* base_page_out, uint32_t* height_minus_1_out, uint32_t* depth_or_array_size_minus_1_out,
uint32_t* mip_page_out, uint32_t* mip_min_level_out, uint32_t* base_page_out, uint32_t* mip_page_out,
uint32_t* mip_max_level_out, xenos::TextureFilter sampler_mip_filter) { uint32_t* mip_min_level_out, uint32_t* mip_max_level_out,
uint32_t width = 0, height = 0, depth_or_faces = 0; xenos::TextureFilter sampler_mip_filter) {
uint32_t width_minus_1 = 0;
uint32_t height_minus_1 = 0;
uint32_t depth_or_array_size_minus_1 = 0;
switch (fetch.dimension) { switch (fetch.dimension) {
case xenos::DataDimension::k1D: case xenos::DataDimension::k1D:
assert_false(fetch.stacked); assert_false(fetch.stacked);
assert_false(fetch.tiled); assert_false(fetch.tiled);
assert_false(fetch.packed_mips); assert_false(fetch.packed_mips);
width = fetch.size_1d.width; width_minus_1 = fetch.size_1d.width;
break; break;
case xenos::DataDimension::k2DOrStacked: case xenos::DataDimension::k2DOrStacked:
width = fetch.size_2d.width; width_minus_1 = fetch.size_2d.width;
height = fetch.size_2d.height; height_minus_1 = fetch.size_2d.height;
depth_or_faces = fetch.stacked ? fetch.size_2d.stack_depth : 0; depth_or_array_size_minus_1 =
fetch.stacked ? fetch.size_2d.stack_depth : 0;
break; break;
case xenos::DataDimension::k3D: case xenos::DataDimension::k3D:
assert_false(fetch.stacked); assert_false(fetch.stacked);
width = fetch.size_3d.width; width_minus_1 = fetch.size_3d.width;
height = fetch.size_3d.height; height_minus_1 = fetch.size_3d.height;
depth_or_faces = fetch.size_3d.depth; depth_or_array_size_minus_1 = fetch.size_3d.depth;
break; break;
case xenos::DataDimension::kCube: case xenos::DataDimension::kCube:
assert_false(fetch.stacked); assert_false(fetch.stacked);
assert_true(fetch.size_2d.stack_depth == 5); assert_true(fetch.size_2d.stack_depth == 5);
width = fetch.size_2d.width; width_minus_1 = fetch.size_2d.width;
height = fetch.size_2d.height; height_minus_1 = fetch.size_2d.height;
depth_or_faces = 5; depth_or_array_size_minus_1 = 5;
break; break;
} }
++width; if (width_minus_1_out) {
++height; *width_minus_1_out = width_minus_1;
++depth_or_faces;
if (width_out) {
*width_out = width;
} }
if (height_out) { if (height_minus_1_out) {
*height_out = height; *height_minus_1_out = height_minus_1;
} }
if (depth_or_faces_out) { if (depth_or_array_size_minus_1_out) {
*depth_or_faces_out = depth_or_faces; *depth_or_array_size_minus_1_out = depth_or_array_size_minus_1;
} }
uint32_t longest_axis = std::max(width, height); uint32_t longest_axis_minus_1 = std::max(width_minus_1, height_minus_1);
if (fetch.dimension == xenos::DataDimension::k3D) { if (fetch.dimension == xenos::DataDimension::k3D) {
longest_axis = std::max(longest_axis, depth_or_faces); longest_axis_minus_1 =
std::max(longest_axis_minus_1, depth_or_array_size_minus_1);
} }
uint32_t size_mip_max_level = xe::log2_floor(longest_axis); uint32_t size_mip_max_level =
xe::log2_floor(longest_axis_minus_1 + uint32_t(1));
xenos::TextureFilter mip_filter = xenos::TextureFilter mip_filter =
sampler_mip_filter == xenos::TextureFilter::kUseFetchConst sampler_mip_filter == xenos::TextureFilter::kUseFetchConst
? fetch.mip_filter ? fetch.mip_filter

View File

@ -27,10 +27,10 @@ namespace texture_util {
// mip range based on real presence of the base level and mips. Returns 6 faces // mip range based on real presence of the base level and mips. Returns 6 faces
// for cube textures. // for cube textures.
void GetSubresourcesFromFetchConstant( void GetSubresourcesFromFetchConstant(
const xenos::xe_gpu_texture_fetch_t& fetch, uint32_t* width_out, const xenos::xe_gpu_texture_fetch_t& fetch, uint32_t* width_minus_1_out,
uint32_t* height_out, uint32_t* depth_or_faces_out, uint32_t* base_page_out, uint32_t* height_minus_1_out, uint32_t* depth_or_array_size_minus_1_out,
uint32_t* mip_page_out, uint32_t* mip_min_level_out, uint32_t* base_page_out, uint32_t* mip_page_out,
uint32_t* mip_max_level_out, uint32_t* mip_min_level_out, uint32_t* mip_max_level_out,
xenos::TextureFilter sampler_mip_filter = xenos::TextureFilter sampler_mip_filter =
xenos::TextureFilter::kUseFetchConst); xenos::TextureFilter::kUseFetchConst);