diff --git a/src/xenia/gpu/d3d12/texture_cache.cc b/src/xenia/gpu/d3d12/texture_cache.cc index e913840c0..f2098c9df 100644 --- a/src/xenia/gpu/d3d12/texture_cache.cc +++ b/src/xenia/gpu/d3d12/texture_cache.cc @@ -1263,7 +1263,7 @@ void TextureCache::RequestTextures(uint32_t used_texture_mask) { BindingInfoFromFetchConstant(fetch, binding.key, &binding.host_swizzle, &binding.swizzled_signs); texture_bindings_in_sync_ |= index_bit; - if (binding.key.IsInvalid()) { + if (!binding.key.is_valid) { binding.texture = nullptr; binding.texture_signed = nullptr; binding.descriptor_index = UINT32_MAX; @@ -1425,7 +1425,7 @@ void TextureCache::WriteActiveTextureBindfulSRV( texture_bindings_[host_shader_binding.fetch_constant]; uint32_t descriptor_index = UINT32_MAX; Texture* texture = nullptr; - if (!binding.key.IsInvalid() && + if (binding.key.is_valid && AreDimensionsCompatible(host_shader_binding.dimension, binding.key.dimension)) { if (host_shader_binding.is_signed) { @@ -1487,7 +1487,7 @@ uint32_t TextureCache::GetActiveTextureBindlessSRVIndex( uint32_t descriptor_index = UINT32_MAX; const TextureBinding& binding = texture_bindings_[host_shader_binding.fetch_constant]; - if (!binding.key.IsInvalid() && + if (binding.key.is_valid && AreDimensionsCompatible(host_shader_binding.dimension, binding.key.dimension)) { descriptor_index = host_shader_binding.is_signed @@ -2026,7 +2026,7 @@ TextureCache::LoadMode TextureCache::GetLoadMode(TextureKey key) { if (key.signed_separate) { 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.load_mode; @@ -2071,11 +2071,11 @@ void TextureCache::BindingInfoFromFetchConstant( 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; texture_util::GetSubresourcesFromFetchConstant( - fetch, &width, &height, &depth_or_faces, &base_page, &mip_page, nullptr, - &mip_max_level); + fetch, &width_minus_1, &height_minus_1, &depth_or_array_size_minus_1, + &base_page, &mip_page, nullptr, &mip_max_level); if (base_page == 0 && mip_page == 0) { // No texture data at all. return; @@ -2083,11 +2083,11 @@ void TextureCache::BindingInfoFromFetchConstant( if (fetch.dimension == xenos::DataDimension::k1D) { bool is_invalid_1d = false; // TODO(Triang3l): Support long 1D textures. - if (width > xenos::kTexture2DCubeMaxWidthHeight) { + if (width_minus_1 >= xenos::kTexture2DCubeMaxWidthHeight) { XELOGE( "1D texture is too wide ({}) - ignoring! Report the game to Xenia " "developers", - width); + width_minus_1 + 1); is_invalid_1d = true; } assert_false(fetch.tiled); @@ -2116,9 +2116,9 @@ void TextureCache::BindingInfoFromFetchConstant( key_out.base_page = base_page; key_out.mip_page = mip_page; key_out.dimension = fetch.dimension; - key_out.width = width; - key_out.height = height; - key_out.depth = depth_or_faces; + key_out.width_minus_1 = width_minus_1; + key_out.height_minus_1 = height_minus_1; + key_out.depth_or_array_size_minus_1 = depth_or_array_size_minus_1; key_out.pitch = fetch.pitch; key_out.mip_max_level = mip_max_level; key_out.tiled = fetch.tiled; @@ -2126,6 +2126,8 @@ void TextureCache::BindingInfoFromFetchConstant( key_out.format = format; key_out.endianness = fetch.endianness; + key_out.is_valid = 1; + if (host_swizzle_out != nullptr) { uint32_t host_swizzle = 0; 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{}, " "base at 0x{:08X} (pitch {}), mips at 0x{:08X}", action, key.tiled ? "tiled" : "linear", - key.scaled_resolve ? "scaled " : "", key.width, key.height, key.depth, - dimension_names_[uint32_t(key.dimension)], + key.scaled_resolve ? "scaled " : "", key.GetWidth(), key.GetHeight(), + key.GetDepthOrArraySize(), dimension_names_[uint32_t(key.dimension)], FormatInfo::Get(key.format)->name, key.mip_max_level + 1, key.packed_mips ? "" : "un", key.mip_max_level != 0 ? "s" : "", 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 " "0x{:08X})", action, texture->key.tiled ? "tiled" : "linear", - texture->key.scaled_resolve ? "scaled " : "", texture->key.width, - texture->key.height, texture->key.depth, + texture->key.scaled_resolve ? "scaled " : "", texture->key.GetWidth(), + texture->key.GetHeight(), texture->key.GetDepthOrArraySize(), dimension_names_[uint32_t(texture->key.dimension)], FormatInfo::Get(texture->key.format)->name, 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) { texture_util::TextureGuestLayout scaled_resolve_guest_layout = texture_util::GetGuestTextureLayout( - key.dimension, key.pitch, key.width, key.height, key.depth, - key.tiled, key.format, key.packed_mips, key.base_page != 0, - key.mip_max_level); + key.dimension, key.pitch, key.GetWidth(), key.GetHeight(), + key.GetDepthOrArraySize(), key.tiled, key.format, key.packed_mips, + key.base_page != 0, key.mip_max_level); if ((scaled_resolve_guest_layout.base.level_data_extent_bytes && IsRangeScaledResolved( key.base_page << 12, @@ -2201,8 +2203,8 @@ TextureCache::Texture* TextureCache::FindOrCreateTexture(TextureKey key) { } } } - uint32_t host_width = key.width; - uint32_t host_height = key.height; + uint32_t host_width = key.GetWidth(); + uint32_t host_height = key.GetHeight(); if (key.scaled_resolve) { host_width *= draw_resolution_scale_x_; 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 // Direct3D 11 limit, and with 2x, a 3D one as well. 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 || - 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; } @@ -2242,7 +2246,7 @@ TextureCache::Texture* TextureCache::FindOrCreateTexture(TextureKey key) { desc.Alignment = 0; desc.Width = host_width; desc.Height = host_height; - desc.DepthOrArraySize = key.depth; + desc.DepthOrArraySize = key.GetDepthOrArraySize(); desc.MipLevels = key.mip_max_level + 1; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; @@ -2283,8 +2287,9 @@ TextureCache::Texture* TextureCache::FindOrCreateTexture(TextureKey key) { texture->base_resolved = key.scaled_resolve; texture->mips_resolved = key.scaled_resolve; texture->guest_layout = texture_util::GetGuestTextureLayout( - key.dimension, key.pitch, key.width, key.height, key.depth, key.tiled, - key.format, key.packed_mips, key.base_page != 0, key.mip_max_level); + key.dimension, key.pitch, key.GetWidth(), key.GetHeight(), + 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. texture->base_in_sync = !texture->guest_layout.base.level_data_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. xenos::DataDimension dimension = texture->key.dimension; bool is_3d = dimension == xenos::DataDimension::k3D; - uint32_t width = texture->key.width; - uint32_t height = texture->key.height; - uint32_t depth_or_array_size = texture->key.depth; + uint32_t width = texture->key.GetWidth(); + uint32_t height = texture->key.GetHeight(); + uint32_t depth_or_array_size = texture->key.GetDepthOrArraySize(); uint32_t depth = is_3d ? depth_or_array_size : 1; uint32_t array_size = is_3d ? 1 : depth_or_array_size; xenos::TextureFormat guest_format = texture->key.format; @@ -2845,7 +2850,7 @@ uint32_t TextureCache::FindOrCreateTextureDescriptor(Texture& texture, desc.Texture2DArray.MostDetailedMip = 0; desc.Texture2DArray.MipLevels = mip_levels; desc.Texture2DArray.FirstArraySlice = 0; - desc.Texture2DArray.ArraySize = texture.key.depth; + desc.Texture2DArray.ArraySize = texture.key.GetDepthOrArraySize(); desc.Texture2DArray.PlaneSlice = 0; desc.Texture2DArray.ResourceMinLODClamp = 0.0f; break; diff --git a/src/xenia/gpu/d3d12/texture_cache.h b/src/xenia/gpu/d3d12/texture_cache.h index 7d5570eac..6c35f1c98 100644 --- a/src/xenia/gpu/d3d12/texture_cache.h +++ b/src/xenia/gpu/d3d12/texture_cache.h @@ -62,30 +62,36 @@ class D3D12CommandProcessor; // because textures are streamed this way anyway. class TextureCache { 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 // range prefix. uint32_t base_page : 17; // 17 total xenos::DataDimension dimension : 2; // 19 - uint32_t width : 13; // 32 + uint32_t width_minus_1 : 13; // 32 - uint32_t height : 13; // 45 - uint32_t tiled : 1; // 46 - uint32_t packed_mips : 1; // 47 + uint32_t height_minus_1 : 13; // 45 + uint32_t tiled : 1; // 46 + uint32_t packed_mips : 1; // 47 // Physical 4 KB page with mip 1 and smaller. uint32_t mip_page : 17; // 64 - // Layers for stacked and 3D, 6 for cube, 1 for other dimensions. - uint32_t depth : 10; // 74 - uint32_t pitch : 9; // 83 - uint32_t mip_max_level : 4; // 87 - xenos::TextureFormat format : 6; // 93 - xenos::Endian endianness : 2; // 95 + // (Layers for stacked and 3D, 6 for cube, 1 for other dimensions) - 1. + uint32_t depth_or_array_size_minus_1 : 10; // 74 + uint32_t pitch : 9; // 83 + uint32_t mip_max_level : 4; // 87 + xenos::TextureFormat format : 6; // 93 + xenos::Endian endianness : 2; // 95 // Whether this texture is signed and has a different host representation // than an unsigned view of the same guest texture. uint32_t signed_separate : 1; // 96 // Whether this texture is a resolution-scaled resolve target. uint32_t scaled_resolve : 1; // 97 + // Least important in ==, so placed last. + uint32_t is_valid : 1; // 98 TextureKey() { MakeInvalid(); } TextureKey(const TextureKey& key) { @@ -95,16 +101,17 @@ class TextureCache { std::memcpy(this, &key, sizeof(*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() { // Zero everything, including the padding, for a stable hash. 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; bool operator==(const TextureKey& key) const { return !std::memcmp(this, &key, sizeof(*this)); @@ -544,7 +551,8 @@ class TextureCache { return 0; } } - static uint32_t GetMaxHostTextureDepth(xenos::DataDimension dimension) { + static uint32_t GetMaxHostTextureDepthOrArraySize( + xenos::DataDimension dimension) { switch (dimension) { case xenos::DataDimension::k1D: case xenos::DataDimension::k2DOrStacked: @@ -609,7 +617,7 @@ class TextureCache { : host_format.dxgi_format_resource; } 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, uint32_t width, uint32_t height) { @@ -619,7 +627,7 @@ class TextureCache { : host_format.dxgi_format_unorm; } 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); diff --git a/src/xenia/gpu/texture_util.cc b/src/xenia/gpu/texture_util.cc index e64e4c205..03a912b81 100644 --- a/src/xenia/gpu/texture_util.cc +++ b/src/xenia/gpu/texture_util.cc @@ -20,55 +20,58 @@ namespace gpu { namespace texture_util { void GetSubresourcesFromFetchConstant( - const xenos::xe_gpu_texture_fetch_t& fetch, uint32_t* width_out, - uint32_t* height_out, uint32_t* depth_or_faces_out, uint32_t* base_page_out, - uint32_t* mip_page_out, uint32_t* mip_min_level_out, - uint32_t* mip_max_level_out, xenos::TextureFilter sampler_mip_filter) { - uint32_t width = 0, height = 0, depth_or_faces = 0; + const xenos::xe_gpu_texture_fetch_t& fetch, uint32_t* width_minus_1_out, + uint32_t* height_minus_1_out, uint32_t* depth_or_array_size_minus_1_out, + uint32_t* base_page_out, uint32_t* mip_page_out, + uint32_t* mip_min_level_out, uint32_t* mip_max_level_out, + 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) { case xenos::DataDimension::k1D: assert_false(fetch.stacked); assert_false(fetch.tiled); assert_false(fetch.packed_mips); - width = fetch.size_1d.width; + width_minus_1 = fetch.size_1d.width; break; case xenos::DataDimension::k2DOrStacked: - width = fetch.size_2d.width; - height = fetch.size_2d.height; - depth_or_faces = fetch.stacked ? fetch.size_2d.stack_depth : 0; + width_minus_1 = fetch.size_2d.width; + height_minus_1 = fetch.size_2d.height; + depth_or_array_size_minus_1 = + fetch.stacked ? fetch.size_2d.stack_depth : 0; break; case xenos::DataDimension::k3D: assert_false(fetch.stacked); - width = fetch.size_3d.width; - height = fetch.size_3d.height; - depth_or_faces = fetch.size_3d.depth; + width_minus_1 = fetch.size_3d.width; + height_minus_1 = fetch.size_3d.height; + depth_or_array_size_minus_1 = fetch.size_3d.depth; break; case xenos::DataDimension::kCube: assert_false(fetch.stacked); assert_true(fetch.size_2d.stack_depth == 5); - width = fetch.size_2d.width; - height = fetch.size_2d.height; - depth_or_faces = 5; + width_minus_1 = fetch.size_2d.width; + height_minus_1 = fetch.size_2d.height; + depth_or_array_size_minus_1 = 5; break; } - ++width; - ++height; - ++depth_or_faces; - if (width_out) { - *width_out = width; + if (width_minus_1_out) { + *width_minus_1_out = width_minus_1; } - if (height_out) { - *height_out = height; + if (height_minus_1_out) { + *height_minus_1_out = height_minus_1; } - if (depth_or_faces_out) { - *depth_or_faces_out = depth_or_faces; + if (depth_or_array_size_minus_1_out) { + *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) { - 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 = sampler_mip_filter == xenos::TextureFilter::kUseFetchConst ? fetch.mip_filter diff --git a/src/xenia/gpu/texture_util.h b/src/xenia/gpu/texture_util.h index 733b3e88f..1988ed690 100644 --- a/src/xenia/gpu/texture_util.h +++ b/src/xenia/gpu/texture_util.h @@ -27,10 +27,10 @@ namespace texture_util { // mip range based on real presence of the base level and mips. Returns 6 faces // for cube textures. void GetSubresourcesFromFetchConstant( - const xenos::xe_gpu_texture_fetch_t& fetch, uint32_t* width_out, - uint32_t* height_out, uint32_t* depth_or_faces_out, uint32_t* base_page_out, - uint32_t* mip_page_out, uint32_t* mip_min_level_out, - uint32_t* mip_max_level_out, + const xenos::xe_gpu_texture_fetch_t& fetch, uint32_t* width_minus_1_out, + uint32_t* height_minus_1_out, uint32_t* depth_or_array_size_minus_1_out, + uint32_t* base_page_out, uint32_t* mip_page_out, + uint32_t* mip_min_level_out, uint32_t* mip_max_level_out, xenos::TextureFilter sampler_mip_filter = xenos::TextureFilter::kUseFetchConst);