[D3D12] Fix 4D5307DF fur textures

- Fixes issue https://github.com/xenia-canary/game-compatibility/issues/6
- Implements a copy texture logic
This commit is contained in:
Raffaele
2025-12-19 23:47:32 +01:00
committed by Herman S.
parent bc54720559
commit 26824edcfa
2 changed files with 191 additions and 38 deletions

View File

@@ -582,21 +582,42 @@ void D3D12TextureCache::WriteActiveTextureBindfulSRV(
const TextureBinding* binding = GetValidTextureBinding(fetch_constant_index); const TextureBinding* binding = GetValidTextureBinding(fetch_constant_index);
if (binding && AreDimensionsCompatible(host_shader_binding.dimension, if (binding && AreDimensionsCompatible(host_shader_binding.dimension,
binding->key.dimension)) { binding->key.dimension)) {
bool force_special_view =
(host_shader_binding.dimension == xenos::FetchOpDimension::k2D &&
binding->key.dimension == xenos::DataDimension::k3D);
const D3D12TextureBinding& d3d12_binding = const D3D12TextureBinding& d3d12_binding =
d3d12_texture_bindings_[fetch_constant_index]; d3d12_texture_bindings_[fetch_constant_index];
if (host_shader_binding.is_signed) { if (host_shader_binding.is_signed) {
// Not supporting signed compressed textures - hopefully DXN and DXT5A are // Not supporting signed compressed textures - hopefully DXN and DXT5A are
// not used as signed. // not used as signed.
if (texture_util::IsAnySignSigned(binding->swizzled_signs)) { if (texture_util::IsAnySignSigned(binding->swizzled_signs)) {
descriptor_index = d3d12_binding.descriptor_index_signed;
texture = IsSignedVersionSeparateForFormat(binding->key) texture = IsSignedVersionSeparateForFormat(binding->key)
? binding->texture_signed ? binding->texture_signed
: binding->texture; : binding->texture;
if (force_special_view && texture) {
// Request the 2D view of the 3D texture on demand
descriptor_index = FindOrCreateTextureDescriptor(
*static_cast<D3D12Texture*>(texture),
xenos::DataDimension::k2DOrStacked, true, binding->host_swizzle);
} else {
descriptor_index = d3d12_binding.descriptor_index_signed;
}
} }
} else { } else {
if (texture_util::IsAnySignNotSigned(binding->swizzled_signs)) { if (texture_util::IsAnySignNotSigned(binding->swizzled_signs)) {
descriptor_index = d3d12_binding.descriptor_index;
texture = binding->texture; texture = binding->texture;
if (force_special_view && texture) {
// Request the 2D view of the 3D texture on demand
descriptor_index = FindOrCreateTextureDescriptor(
*static_cast<D3D12Texture*>(texture),
xenos::DataDimension::k2DOrStacked, false, binding->host_swizzle);
} else {
descriptor_index = d3d12_binding.descriptor_index;
texture = binding->texture;
}
} }
} }
} }
@@ -646,12 +667,44 @@ uint32_t D3D12TextureCache::GetActiveTextureBindlessSRVIndex(
const TextureBinding* binding = GetValidTextureBinding(fetch_constant_index); const TextureBinding* binding = GetValidTextureBinding(fetch_constant_index);
if (binding && AreDimensionsCompatible(host_shader_binding.dimension, if (binding && AreDimensionsCompatible(host_shader_binding.dimension,
binding->key.dimension)) { binding->key.dimension)) {
// 3D Texture on 2D Request
bool force_special_view =
(host_shader_binding.dimension == xenos::FetchOpDimension::k2D &&
binding->key.dimension == xenos::DataDimension::k3D);
const D3D12TextureBinding& d3d12_binding = const D3D12TextureBinding& d3d12_binding =
d3d12_texture_bindings_[fetch_constant_index]; d3d12_texture_bindings_[fetch_constant_index];
descriptor_index = host_shader_binding.is_signed
? d3d12_binding.descriptor_index_signed // Helper lambda to get standard index
: d3d12_binding.descriptor_index; uint32_t standard_index = host_shader_binding.is_signed
? d3d12_binding.descriptor_index_signed
: d3d12_binding.descriptor_index;
if (force_special_view) {
// Determine which texture object to use
Texture* texture = nullptr;
if (host_shader_binding.is_signed) {
texture = IsSignedVersionSeparateForFormat(binding->key)
? binding->texture_signed
: binding->texture;
if (texture) {
descriptor_index = FindOrCreateTextureDescriptor(
*static_cast<D3D12Texture*>(texture),
xenos::DataDimension::k2DOrStacked, true, binding->host_swizzle);
}
} else {
texture = binding->texture;
if (texture) {
descriptor_index = FindOrCreateTextureDescriptor(
*static_cast<D3D12Texture*>(texture),
xenos::DataDimension::k2DOrStacked, false, binding->host_swizzle);
}
}
} else {
descriptor_index = standard_index;
}
} }
if (descriptor_index == UINT32_MAX) { if (descriptor_index == UINT32_MAX) {
switch (host_shader_binding.dimension) { switch (host_shader_binding.dimension) {
case xenos::FetchOpDimension::k3DOrStacked: case xenos::FetchOpDimension::k3DOrStacked:
@@ -1756,36 +1809,117 @@ void D3D12TextureCache::UpdateTextureBindingsImpl(
if (binding->texture && if (binding->texture &&
texture_util::IsAnySignNotSigned(binding->swizzled_signs)) { texture_util::IsAnySignNotSigned(binding->swizzled_signs)) {
d3d12_binding.descriptor_index = FindOrCreateTextureDescriptor( d3d12_binding.descriptor_index = FindOrCreateTextureDescriptor(
*static_cast<D3D12Texture*>(binding->texture), false, *static_cast<D3D12Texture*>(binding->texture),
binding->host_swizzle); binding->key.dimension, false, binding->host_swizzle);
} }
if (binding->texture_signed && if (binding->texture_signed &&
texture_util::IsAnySignSigned(binding->swizzled_signs)) { texture_util::IsAnySignSigned(binding->swizzled_signs)) {
d3d12_binding.descriptor_index_signed = FindOrCreateTextureDescriptor( d3d12_binding.descriptor_index_signed = FindOrCreateTextureDescriptor(
*static_cast<D3D12Texture*>(binding->texture_signed), true, *static_cast<D3D12Texture*>(binding->texture_signed),
binding->host_swizzle); binding->key.dimension, true, binding->host_swizzle);
} }
} else { } else {
D3D12Texture* texture = static_cast<D3D12Texture*>(binding->texture); D3D12Texture* texture = static_cast<D3D12Texture*>(binding->texture);
if (texture) { if (texture) {
if (texture_util::IsAnySignNotSigned(binding->swizzled_signs)) { if (texture_util::IsAnySignNotSigned(binding->swizzled_signs)) {
d3d12_binding.descriptor_index = FindOrCreateTextureDescriptor( d3d12_binding.descriptor_index = FindOrCreateTextureDescriptor(
*texture, false, binding->host_swizzle); *texture, binding->key.dimension, false, binding->host_swizzle);
} }
if (texture_util::IsAnySignSigned(binding->swizzled_signs)) { if (texture_util::IsAnySignSigned(binding->swizzled_signs)) {
d3d12_binding.descriptor_index_signed = FindOrCreateTextureDescriptor( d3d12_binding.descriptor_index_signed = FindOrCreateTextureDescriptor(
*texture, true, binding->host_swizzle); *texture, binding->key.dimension, true, binding->host_swizzle);
} }
} }
} }
} }
} }
ID3D12Resource* D3D12TextureCache::D3D12Texture::GetOrCreate3DAs2DResource(
D3D12_RESOURCE_STATES end_state) {
auto& d3d12_cache = static_cast<D3D12TextureCache&>(texture_cache());
// 1. If it already exists, just transition and return it.
if (resource_3d_as_2d_) {
if (resource_3d_as_2d_state_ != end_state) {
d3d12_cache.command_processor_.PushTransitionBarrier(
resource_3d_as_2d_.Get(), resource_3d_as_2d_state_, end_state);
resource_3d_as_2d_state_ = end_state;
}
return resource_3d_as_2d_.Get();
}
// 2. Create the 2D Alias Resource.
D3D12_RESOURCE_DESC source_desc = resource_->GetDesc();
D3D12_RESOURCE_DESC desc = source_desc;
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
const ui::d3d12::D3D12Provider& provider =
d3d12_cache.command_processor_.GetD3D12Provider();
ID3D12Device* device = provider.GetDevice();
if (FAILED(device->CreateCommittedResource(
&ui::d3d12::util::kHeapPropertiesDefault,
provider.GetHeapFlagCreateNotZeroed(), &desc,
D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
IID_PPV_ARGS(&resource_3d_as_2d_)))) {
XELOGE("D3D12Texture: Failed to create 3D-as-2D alias resource");
return nullptr;
}
resource_3d_as_2d_state_ = D3D12_RESOURCE_STATE_COPY_DEST;
// 3. Perform the Copy (Slice 0 of 3D -> 2D).
// Transition MAIN 3D resource to COPY_SOURCE.
D3D12_RESOURCE_STATES old_main_state =
SetResourceState(D3D12_RESOURCE_STATE_COPY_SOURCE);
d3d12_cache.command_processor_.PushTransitionBarrier(
resource(), old_main_state, D3D12_RESOURCE_STATE_COPY_SOURCE);
auto& command_list = d3d12_cache.command_processor_.GetDeferredCommandList();
D3D12_TEXTURE_COPY_LOCATION dest_loc = {};
dest_loc.pResource = resource_3d_as_2d_.Get();
dest_loc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
dest_loc.SubresourceIndex = 0;
D3D12_TEXTURE_COPY_LOCATION src_loc = {};
src_loc.pResource = resource();
src_loc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
src_loc.SubresourceIndex = 0;
// Define the copy box to select ONLY Slice 0
D3D12_BOX src_box;
src_box.left = 0;
src_box.top = 0;
src_box.front = 0;
src_box.right = static_cast<UINT>(source_desc.Width);
src_box.bottom = source_desc.Height;
src_box.back = 1; // CRITICAL: Only copy 1 depth slice!
command_list.D3DCopyTextureRegion(&dest_loc, 0, 0, 0, &src_loc, &src_box);
// 4. Restore States.
d3d12_cache.command_processor_.PushTransitionBarrier(
resource(), D3D12_RESOURCE_STATE_COPY_SOURCE, old_main_state);
SetResourceState(old_main_state);
d3d12_cache.command_processor_.PushTransitionBarrier(
resource_3d_as_2d_.Get(), D3D12_RESOURCE_STATE_COPY_DEST, end_state);
resource_3d_as_2d_state_ = end_state;
return resource_3d_as_2d_.Get();
}
uint32_t D3D12TextureCache::FindOrCreateTextureDescriptor( uint32_t D3D12TextureCache::FindOrCreateTextureDescriptor(
D3D12Texture& texture, bool is_signed, uint32_t host_swizzle) { D3D12Texture& texture, xenos::DataDimension dimension, bool is_signed,
uint32_t host_swizzle) {
D3D12Texture::SRVDescriptorKey descriptor_key; D3D12Texture::SRVDescriptorKey descriptor_key;
descriptor_key.key = 0;
descriptor_key.is_signed = uint32_t(is_signed); descriptor_key.is_signed = uint32_t(is_signed);
descriptor_key.host_swizzle = host_swizzle; descriptor_key.host_swizzle = host_swizzle;
descriptor_key.dimension = uint32_t(dimension);
// Try to find an existing descriptor. // Try to find an existing descriptor.
uint32_t existing_descriptor_index = uint32_t existing_descriptor_index =
@@ -1797,7 +1931,7 @@ uint32_t D3D12TextureCache::FindOrCreateTextureDescriptor(
TextureKey texture_key = texture.key(); TextureKey texture_key = texture.key();
// Create a new bindless or cached descriptor if supported. // Create a new bindless or cached descriptor if supported.
D3D12_SHADER_RESOURCE_VIEW_DESC desc; D3D12_SHADER_RESOURCE_VIEW_DESC desc = {}; // Zero out struct
if (IsSignedVersionSeparateForFormat(texture_key) && if (IsSignedVersionSeparateForFormat(texture_key) &&
texture_key.signed_separate != uint32_t(is_signed)) { texture_key.signed_separate != uint32_t(is_signed)) {
@@ -1819,34 +1953,44 @@ uint32_t D3D12TextureCache::FindOrCreateTextureDescriptor(
} }
uint32_t mip_levels = texture_key.mip_max_level + 1; uint32_t mip_levels = texture_key.mip_max_level + 1;
switch (texture_key.dimension) { ID3D12Resource* resource_for_view = texture.resource();
case xenos::DataDimension::k1D:
case xenos::DataDimension::k2DOrStacked: if (dimension == xenos::DataDimension::k3D) {
desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY; desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
desc.Texture3D.MostDetailedMip = 0;
desc.Texture3D.MipLevels = mip_levels;
desc.Texture3D.ResourceMinLODClamp = 0.0f;
} else if (dimension == xenos::DataDimension::kCube) {
desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
desc.TextureCube.MostDetailedMip = 0;
desc.TextureCube.MipLevels = mip_levels;
desc.TextureCube.ResourceMinLODClamp = 0.0f;
} else {
desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
if (texture_key.dimension == xenos::DataDimension::k3D) {
resource_for_view = texture.GetOrCreate3DAs2DResource(
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
if (!resource_for_view) {
return UINT32_MAX;
}
// Configure SRV for the new 2D resource
desc.Texture2DArray.MostDetailedMip = 0;
desc.Texture2DArray.MipLevels = 1;
desc.Texture2DArray.FirstArraySlice = 0;
desc.Texture2DArray.ArraySize = 1;
desc.Texture2DArray.PlaneSlice = 0;
desc.Texture2DArray.ResourceMinLODClamp = 0.0f;
} else {
// Standard behavior
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.GetDepthOrArraySize(); 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; }
case xenos::DataDimension::k3D:
desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
desc.Texture3D.MostDetailedMip = 0;
desc.Texture3D.MipLevels = mip_levels;
desc.Texture3D.ResourceMinLODClamp = 0.0f;
break;
case xenos::DataDimension::kCube:
desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
desc.TextureCube.MostDetailedMip = 0;
desc.TextureCube.MipLevels = mip_levels;
desc.TextureCube.ResourceMinLODClamp = 0.0f;
break;
default:
assert_unhandled_case(texture_key.dimension);
return UINT32_MAX;
} }
desc.Shader4ComponentMapping = desc.Shader4ComponentMapping =
host_swizzle | host_swizzle |
D3D12_SHADER_COMPONENT_MAPPING_ALWAYS_SET_BIT_AVOIDING_ZEROMEM_MISTAKES; D3D12_SHADER_COMPONENT_MAPPING_ALWAYS_SET_BIT_AVOIDING_ZEROMEM_MISTAKES;
@@ -1894,7 +2038,7 @@ uint32_t D3D12TextureCache::FindOrCreateTextureDescriptor(
} }
} }
device->CreateShaderResourceView( device->CreateShaderResourceView(
texture.resource(), &desc, resource_for_view, &desc,
GetTextureDescriptorCPUHandle(descriptor_index)); GetTextureDescriptorCPUHandle(descriptor_index));
texture.AddSRVDescriptorIndex(descriptor_key, descriptor_index); texture.AddSRVDescriptorIndex(descriptor_key, descriptor_index);
return descriptor_index; return descriptor_index;

View File

@@ -551,6 +551,7 @@ class D3D12TextureCache final : public TextureCache {
struct { struct {
uint32_t is_signed : 1; uint32_t is_signed : 1;
uint32_t host_swizzle : 12; uint32_t host_swizzle : 12;
uint32_t dimension : 2;
}; };
SRVDescriptorKey() : key(0) { static_assert_size(*this, sizeof(key)); } SRVDescriptorKey() : key(0) { static_assert_size(*this, sizeof(key)); }
@@ -568,6 +569,8 @@ class D3D12TextureCache final : public TextureCache {
} }
}; };
ID3D12Resource* GetOrCreate3DAs2DResource(D3D12_RESOURCE_STATES end_state);
explicit D3D12Texture(D3D12TextureCache& texture_cache, explicit D3D12Texture(D3D12TextureCache& texture_cache,
const TextureKey& key, ID3D12Resource* resource, const TextureKey& key, ID3D12Resource* resource,
D3D12_RESOURCE_STATES resource_state); D3D12_RESOURCE_STATES resource_state);
@@ -595,6 +598,10 @@ class D3D12TextureCache final : public TextureCache {
Microsoft::WRL::ComPtr<ID3D12Resource> resource_; Microsoft::WRL::ComPtr<ID3D12Resource> resource_;
D3D12_RESOURCE_STATES resource_state_; D3D12_RESOURCE_STATES resource_state_;
Microsoft::WRL::ComPtr<ID3D12Resource> resource_3d_as_2d_;
D3D12_RESOURCE_STATES resource_3d_as_2d_state_ =
D3D12_RESOURCE_STATE_COMMON;
// For bindful - indices in the non-shader-visible descriptor cache for // For bindful - indices in the non-shader-visible descriptor cache for
// copying to the shader-visible heap (much faster than recreating, which, // copying to the shader-visible heap (much faster than recreating, which,
// according to profiling, was often a bottleneck in many games). // according to profiling, was often a bottleneck in many games).
@@ -717,7 +724,8 @@ class D3D12TextureCache final : public TextureCache {
case xenos::FetchOpDimension::k1D: case xenos::FetchOpDimension::k1D:
case xenos::FetchOpDimension::k2D: case xenos::FetchOpDimension::k2D:
return resource_dimension == xenos::DataDimension::k1D || return resource_dimension == xenos::DataDimension::k1D ||
resource_dimension == xenos::DataDimension::k2DOrStacked; resource_dimension == xenos::DataDimension::k2DOrStacked ||
resource_dimension == xenos::DataDimension::k3D;
case xenos::FetchOpDimension::k3DOrStacked: case xenos::FetchOpDimension::k3DOrStacked:
return resource_dimension == xenos::DataDimension::k3D; return resource_dimension == xenos::DataDimension::k3D;
case xenos::FetchOpDimension::kCube: case xenos::FetchOpDimension::kCube:
@@ -730,8 +738,9 @@ class D3D12TextureCache final : public TextureCache {
// Returns the index of an existing of a newly created non-shader-visible // Returns the index of an existing of a newly created non-shader-visible
// cached (for bindful) or a shader-visible global (for bindless) descriptor, // cached (for bindful) or a shader-visible global (for bindless) descriptor,
// or UINT32_MAX if failed to create. // or UINT32_MAX if failed to create.
uint32_t FindOrCreateTextureDescriptor(D3D12Texture& texture, bool is_signed, uint32_t FindOrCreateTextureDescriptor(D3D12Texture& texture,
uint32_t host_swizzle); xenos::DataDimension dimension,
bool is_signed, uint32_t host_swizzle);
void ReleaseTextureDescriptor(uint32_t descriptor_index); void ReleaseTextureDescriptor(uint32_t descriptor_index);
D3D12_CPU_DESCRIPTOR_HANDLE GetTextureDescriptorCPUHandle( D3D12_CPU_DESCRIPTOR_HANDLE GetTextureDescriptorCPUHandle(
uint32_t descriptor_index) const; uint32_t descriptor_index) const;