[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);
if (binding && AreDimensionsCompatible(host_shader_binding.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 =
d3d12_texture_bindings_[fetch_constant_index];
if (host_shader_binding.is_signed) {
// Not supporting signed compressed textures - hopefully DXN and DXT5A are
// not used as signed.
if (texture_util::IsAnySignSigned(binding->swizzled_signs)) {
descriptor_index = d3d12_binding.descriptor_index_signed;
texture = IsSignedVersionSeparateForFormat(binding->key)
? binding->texture_signed
: 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 {
if (texture_util::IsAnySignNotSigned(binding->swizzled_signs)) {
descriptor_index = d3d12_binding.descriptor_index;
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);
if (binding && AreDimensionsCompatible(host_shader_binding.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 =
d3d12_texture_bindings_[fetch_constant_index];
descriptor_index = host_shader_binding.is_signed
? d3d12_binding.descriptor_index_signed
: d3d12_binding.descriptor_index;
// Helper lambda to get standard 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) {
switch (host_shader_binding.dimension) {
case xenos::FetchOpDimension::k3DOrStacked:
@@ -1756,36 +1809,117 @@ void D3D12TextureCache::UpdateTextureBindingsImpl(
if (binding->texture &&
texture_util::IsAnySignNotSigned(binding->swizzled_signs)) {
d3d12_binding.descriptor_index = FindOrCreateTextureDescriptor(
*static_cast<D3D12Texture*>(binding->texture), false,
binding->host_swizzle);
*static_cast<D3D12Texture*>(binding->texture),
binding->key.dimension, false, binding->host_swizzle);
}
if (binding->texture_signed &&
texture_util::IsAnySignSigned(binding->swizzled_signs)) {
d3d12_binding.descriptor_index_signed = FindOrCreateTextureDescriptor(
*static_cast<D3D12Texture*>(binding->texture_signed), true,
binding->host_swizzle);
*static_cast<D3D12Texture*>(binding->texture_signed),
binding->key.dimension, true, binding->host_swizzle);
}
} else {
D3D12Texture* texture = static_cast<D3D12Texture*>(binding->texture);
if (texture) {
if (texture_util::IsAnySignNotSigned(binding->swizzled_signs)) {
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)) {
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(
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;
descriptor_key.key = 0;
descriptor_key.is_signed = uint32_t(is_signed);
descriptor_key.host_swizzle = host_swizzle;
descriptor_key.dimension = uint32_t(dimension);
// Try to find an existing descriptor.
uint32_t existing_descriptor_index =
@@ -1797,7 +1931,7 @@ uint32_t D3D12TextureCache::FindOrCreateTextureDescriptor(
TextureKey texture_key = texture.key();
// 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) &&
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;
switch (texture_key.dimension) {
case xenos::DataDimension::k1D:
case xenos::DataDimension::k2DOrStacked:
desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
ID3D12Resource* resource_for_view = texture.resource();
if (dimension == xenos::DataDimension::k3D) {
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.MipLevels = mip_levels;
desc.Texture2DArray.FirstArraySlice = 0;
desc.Texture2DArray.ArraySize = texture_key.GetDepthOrArraySize();
desc.Texture2DArray.PlaneSlice = 0;
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 =
host_swizzle |
D3D12_SHADER_COMPONENT_MAPPING_ALWAYS_SET_BIT_AVOIDING_ZEROMEM_MISTAKES;
@@ -1894,7 +2038,7 @@ uint32_t D3D12TextureCache::FindOrCreateTextureDescriptor(
}
}
device->CreateShaderResourceView(
texture.resource(), &desc,
resource_for_view, &desc,
GetTextureDescriptorCPUHandle(descriptor_index));
texture.AddSRVDescriptorIndex(descriptor_key, descriptor_index);
return descriptor_index;

View File

@@ -551,6 +551,7 @@ class D3D12TextureCache final : public TextureCache {
struct {
uint32_t is_signed : 1;
uint32_t host_swizzle : 12;
uint32_t dimension : 2;
};
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,
const TextureKey& key, ID3D12Resource* resource,
D3D12_RESOURCE_STATES resource_state);
@@ -595,6 +598,10 @@ class D3D12TextureCache final : public TextureCache {
Microsoft::WRL::ComPtr<ID3D12Resource> resource_;
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
// copying to the shader-visible heap (much faster than recreating, which,
// 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::k2D:
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:
return resource_dimension == xenos::DataDimension::k3D;
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
// cached (for bindful) or a shader-visible global (for bindless) descriptor,
// or UINT32_MAX if failed to create.
uint32_t FindOrCreateTextureDescriptor(D3D12Texture& texture, bool is_signed,
uint32_t host_swizzle);
uint32_t FindOrCreateTextureDescriptor(D3D12Texture& texture,
xenos::DataDimension dimension,
bool is_signed, uint32_t host_swizzle);
void ReleaseTextureDescriptor(uint32_t descriptor_index);
D3D12_CPU_DESCRIPTOR_HANDLE GetTextureDescriptorCPUHandle(
uint32_t descriptor_index) const;