[D3D12] Bindless textures/samplers

This commit is contained in:
Triang3l
2020-06-19 23:52:33 +03:00
parent 9f789e01b6
commit 40e335e2a9
23 changed files with 3565 additions and 1747 deletions

View File

@@ -12,6 +12,7 @@
#include "third_party/xxhash/xxhash.h"
#include <algorithm>
#include <cfloat>
#include <cstring>
#include "xenia/base/assert.h"
@@ -92,7 +93,6 @@ namespace d3d12 {
#include "xenia/gpu/d3d12/shaders/dxbc/texture_tile_r10g11b11_rgba16_cs.h"
#include "xenia/gpu/d3d12/shaders/dxbc/texture_tile_r11g11b10_rgba16_cs.h"
constexpr uint32_t TextureCache::Texture::kCachedSRVDescriptorSwizzleMissing;
constexpr uint32_t TextureCache::SRVDescriptorCachePage::kHeapSize;
constexpr uint32_t TextureCache::LoadConstants::kGuestPitchTiled;
constexpr uint32_t TextureCache::kScaledResolveBufferSizeLog2;
@@ -905,9 +905,11 @@ const TextureCache::ResolveTileModeInfo
TextureCache::TextureCache(D3D12CommandProcessor* command_processor,
RegisterFile* register_file,
bool bindless_resources_used,
SharedMemory* shared_memory)
: command_processor_(command_processor),
register_file_(register_file),
bindless_resources_used_(bindless_resources_used),
shared_memory_(shared_memory) {}
TextureCache::~TextureCache() { Shutdown(); }
@@ -920,7 +922,8 @@ bool TextureCache::Initialize(bool edram_rov_used) {
// Not currently supported with the RTV/DSV output path for various reasons.
// As of November 27th, 2018, PIX doesn't support tiled buffers.
if (cvars::d3d12_resolution_scale >= 2 && edram_rov_used &&
provider->GetTiledResourcesTier() >= 1 &&
provider->GetTiledResourcesTier() !=
D3D12_TILED_RESOURCES_TIER_NOT_SUPPORTED &&
provider->GetGraphicsAnalysis() == nullptr &&
provider->GetVirtualAddressBitsPerResource() >=
kScaledResolveBufferSizeLog2) {
@@ -947,28 +950,34 @@ bool TextureCache::Initialize(bool edram_rov_used) {
scaled_resolve_heap_count_ = 0;
// Create the loading root signature.
D3D12_ROOT_PARAMETER root_parameters[2];
// Parameter 0 is constants (changed very often when untiling).
D3D12_ROOT_PARAMETER root_parameters[3];
// Parameter 0 is constants (changed multiple times when untiling).
root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
root_parameters[0].Descriptor.ShaderRegister = 0;
root_parameters[0].Descriptor.RegisterSpace = 0;
root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
// Parameter 1 is source and target.
D3D12_DESCRIPTOR_RANGE root_copy_ranges[2];
root_copy_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
root_copy_ranges[0].NumDescriptors = 1;
root_copy_ranges[0].BaseShaderRegister = 0;
root_copy_ranges[0].RegisterSpace = 0;
root_copy_ranges[0].OffsetInDescriptorsFromTableStart = 0;
root_copy_ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
root_copy_ranges[1].NumDescriptors = 1;
root_copy_ranges[1].BaseShaderRegister = 0;
root_copy_ranges[1].RegisterSpace = 0;
root_copy_ranges[1].OffsetInDescriptorsFromTableStart = 1;
// Parameter 1 is the destination.
D3D12_DESCRIPTOR_RANGE root_dest_range;
root_dest_range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
root_dest_range.NumDescriptors = 1;
root_dest_range.BaseShaderRegister = 0;
root_dest_range.RegisterSpace = 0;
root_dest_range.OffsetInDescriptorsFromTableStart = 0;
root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
root_parameters[1].DescriptorTable.NumDescriptorRanges = 2;
root_parameters[1].DescriptorTable.pDescriptorRanges = root_copy_ranges;
root_parameters[1].DescriptorTable.NumDescriptorRanges = 1;
root_parameters[1].DescriptorTable.pDescriptorRanges = &root_dest_range;
root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
// Parameter 2 is the source.
D3D12_DESCRIPTOR_RANGE root_source_range;
root_source_range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
root_source_range.NumDescriptors = 1;
root_source_range.BaseShaderRegister = 0;
root_source_range.RegisterSpace = 0;
root_source_range.OffsetInDescriptorsFromTableStart = 0;
root_parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
root_parameters[2].DescriptorTable.NumDescriptorRanges = 1;
root_parameters[2].DescriptorTable.pDescriptorRanges = &root_source_range;
root_parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
root_signature_desc.NumParameters = UINT(xe::countof(root_parameters));
root_signature_desc.pParameters = root_parameters;
@@ -1033,6 +1042,8 @@ bool TextureCache::Initialize(bool edram_rov_used) {
}
}
srv_descriptor_cache_allocated_ = 0;
// Create a heap with null SRV descriptors, since it's faster to copy a
// descriptor than to create an SRV, and null descriptors are used a lot (for
// the signed version when only unsigned is used, for instance).
@@ -1137,6 +1148,14 @@ void TextureCache::ClearCache() {
Texture* texture = texture_pair.second;
shared_memory_->UnwatchMemoryRange(texture->base_watch_handle);
shared_memory_->UnwatchMemoryRange(texture->mip_watch_handle);
// Bindful descriptor cache will be cleared entirely now, so only release
// bindless descriptors.
if (bindless_resources_used_) {
for (auto descriptor_pair : texture->srv_descriptors) {
command_processor_->ReleaseViewBindlessDescriptorImmediately(
descriptor_pair.second);
}
}
texture->resource->Release();
delete texture;
}
@@ -1148,6 +1167,7 @@ void TextureCache::ClearCache() {
// Clear texture descriptor cache.
srv_descriptor_cache_free_.clear();
srv_descriptor_cache_allocated_ = 0;
for (auto& page : srv_descriptor_cache_) {
page.heap->Release();
}
@@ -1155,7 +1175,7 @@ void TextureCache::ClearCache() {
}
void TextureCache::TextureFetchConstantWritten(uint32_t index) {
texture_keys_in_sync_ &= ~(1u << index);
texture_bindings_in_sync_ &= ~(1u << index);
}
void TextureCache::BeginFrame() {
@@ -1214,12 +1234,18 @@ void TextureCache::BeginFrame() {
// Exclude the texture from the memory usage counter.
textures_total_size_ -= texture->resource_size;
// Destroy the texture.
if (texture->cached_srv_descriptor_swizzle !=
Texture::kCachedSRVDescriptorSwizzleMissing) {
srv_descriptor_cache_free_.push_back(texture->cached_srv_descriptor);
}
shared_memory_->UnwatchMemoryRange(texture->base_watch_handle);
shared_memory_->UnwatchMemoryRange(texture->mip_watch_handle);
if (bindless_resources_used_) {
for (auto descriptor_pair : texture->srv_descriptors) {
command_processor_->ReleaseViewBindlessDescriptorImmediately(
descriptor_pair.second);
}
} else {
for (auto descriptor_pair : texture->srv_descriptors) {
srv_descriptor_cache_free_.push_back(descriptor_pair.second);
}
}
texture->resource->Release();
delete texture;
}
@@ -1262,8 +1288,10 @@ void TextureCache::RequestTextures(uint32_t used_texture_mask) {
// loading may be needed in some draw call later, which may have the same
// key for some binding as before the invalidation, but texture_invalidated_
// being false (menu background in Halo 3).
std::memset(texture_bindings_, 0, sizeof(texture_bindings_));
texture_keys_in_sync_ = 0;
for (size_t i = 0; i < xe::countof(texture_bindings_); ++i) {
texture_bindings_[i].Clear();
}
texture_bindings_in_sync_ = 0;
}
// Update the texture keys and the textures.
@@ -1272,7 +1300,7 @@ void TextureCache::RequestTextures(uint32_t used_texture_mask) {
while (xe::bit_scan_forward(textures_remaining, &index)) {
uint32_t index_bit = uint32_t(1) << index;
textures_remaining &= ~index_bit;
if (texture_keys_in_sync_ & index_bit) {
if (texture_bindings_in_sync_ & index_bit) {
continue;
}
TextureBinding& binding = texture_bindings_[index];
@@ -1282,10 +1310,12 @@ void TextureCache::RequestTextures(uint32_t used_texture_mask) {
uint8_t old_swizzled_signs = binding.swizzled_signs;
BindingInfoFromFetchConstant(fetch, binding.key, &binding.host_swizzle,
&binding.swizzled_signs);
texture_keys_in_sync_ |= index_bit;
texture_bindings_in_sync_ |= index_bit;
if (binding.key.IsInvalid()) {
binding.texture = nullptr;
binding.texture_signed = nullptr;
binding.descriptor_index = UINT32_MAX;
binding.descriptor_index_signed = UINT32_MAX;
continue;
}
@@ -1305,27 +1335,64 @@ void TextureCache::RequestTextures(uint32_t used_texture_mask) {
if (key_changed ||
!texture_util::IsAnySignNotSigned(old_swizzled_signs)) {
binding.texture = FindOrCreateTexture(binding.key);
binding.descriptor_index =
binding.texture
? FindOrCreateTextureDescriptor(*binding.texture, false,
binding.host_swizzle)
: UINT32_MAX;
load_unsigned_data = true;
}
} else {
binding.texture = nullptr;
binding.descriptor_index = UINT32_MAX;
}
if (texture_util::IsAnySignSigned(binding.swizzled_signs)) {
if (key_changed || !texture_util::IsAnySignSigned(old_swizzled_signs)) {
TextureKey signed_key = binding.key;
signed_key.signed_separate = 1;
binding.texture_signed = FindOrCreateTexture(signed_key);
binding.descriptor_index_signed =
binding.texture
? FindOrCreateTextureDescriptor(*binding.texture_signed, true,
binding.host_swizzle)
: UINT32_MAX;
load_signed_data = true;
}
} else {
binding.texture_signed = nullptr;
binding.descriptor_index_signed = UINT32_MAX;
}
} else {
// Same resource for both unsigned and signed, but descriptor formats may
// be different.
if (key_changed) {
binding.texture = FindOrCreateTexture(binding.key);
load_unsigned_data = true;
}
binding.texture_signed = nullptr;
if (texture_util::IsAnySignNotSigned(binding.swizzled_signs)) {
if (key_changed ||
!texture_util::IsAnySignNotSigned(old_swizzled_signs)) {
binding.descriptor_index =
binding.texture
? FindOrCreateTextureDescriptor(*binding.texture, false,
binding.host_swizzle)
: UINT32_MAX;
}
} else {
binding.descriptor_index = UINT32_MAX;
}
if (texture_util::IsAnySignSigned(binding.swizzled_signs)) {
if (key_changed || !texture_util::IsAnySignSigned(old_swizzled_signs)) {
binding.descriptor_index_signed =
binding.texture
? FindOrCreateTextureDescriptor(*binding.texture, true,
binding.host_swizzle)
: UINT32_MAX;
}
} else {
binding.descriptor_index_signed = UINT32_MAX;
}
}
if (load_unsigned_data && binding.texture != nullptr) {
LoadTextureData(binding.texture);
@@ -1368,208 +1435,132 @@ void TextureCache::RequestTextures(uint32_t used_texture_mask) {
}
}
uint64_t TextureCache::GetDescriptorHashForActiveTextures(
const D3D12Shader::TextureSRV* texture_srvs,
uint32_t texture_srv_count) const {
XXH64_state_t hash_state;
XXH64_reset(&hash_state, 0);
for (uint32_t i = 0; i < texture_srv_count; ++i) {
const D3D12Shader::TextureSRV& texture_srv = texture_srvs[i];
// There can be multiple SRVs of the same texture.
XXH64_update(&hash_state, &texture_srv.dimension,
sizeof(texture_srv.dimension));
XXH64_update(&hash_state, &texture_srv.is_signed,
sizeof(texture_srv.is_signed));
bool TextureCache::AreActiveTextureSRVKeysUpToDate(
const TextureSRVKey* keys,
const D3D12Shader::TextureBinding* host_shader_bindings,
uint32_t host_shader_binding_count) const {
for (uint32_t i = 0; i < host_shader_binding_count; ++i) {
const TextureSRVKey& key = keys[i];
const TextureBinding& binding =
texture_bindings_[texture_srv.fetch_constant];
XXH64_update(&hash_state, &binding.key, sizeof(binding.key));
XXH64_update(&hash_state, &binding.host_swizzle,
sizeof(binding.host_swizzle));
XXH64_update(&hash_state, &binding.swizzled_signs,
sizeof(binding.swizzled_signs));
texture_bindings_[host_shader_bindings[i].fetch_constant];
if (key.key != binding.key || key.host_swizzle != binding.host_swizzle ||
key.swizzled_signs != binding.swizzled_signs) {
return false;
}
}
return XXH64_digest(&hash_state);
return true;
}
void TextureCache::WriteTextureSRV(const D3D12Shader::TextureSRV& texture_srv,
D3D12_CPU_DESCRIPTOR_HANDLE handle) {
D3D12_SHADER_RESOURCE_VIEW_DESC desc;
desc.Format = DXGI_FORMAT_UNKNOWN;
Dimension binding_dimension;
uint32_t mip_max_level, array_size;
void TextureCache::WriteActiveTextureSRVKeys(
TextureSRVKey* keys,
const D3D12Shader::TextureBinding* host_shader_bindings,
uint32_t host_shader_binding_count) const {
for (uint32_t i = 0; i < host_shader_binding_count; ++i) {
TextureSRVKey& key = keys[i];
const TextureBinding& binding =
texture_bindings_[host_shader_bindings[i].fetch_constant];
key.key = binding.key;
key.host_swizzle = binding.host_swizzle;
key.swizzled_signs = binding.swizzled_signs;
}
}
void TextureCache::WriteActiveTextureBindfulSRV(
const D3D12Shader::TextureBinding& host_shader_binding,
D3D12_CPU_DESCRIPTOR_HANDLE handle) {
assert_false(bindless_resources_used_);
const TextureBinding& binding =
texture_bindings_[host_shader_binding.fetch_constant];
uint32_t descriptor_index = UINT32_MAX;
Texture* texture = nullptr;
ID3D12Resource* resource = nullptr;
const TextureBinding& binding = texture_bindings_[texture_srv.fetch_constant];
if (!binding.key.IsInvalid()) {
TextureFormat format = binding.key.format;
if (IsSignedVersionSeparate(format) && texture_srv.is_signed) {
texture = binding.texture_signed;
} else {
texture = binding.texture;
}
if (texture != nullptr) {
resource = texture->resource;
}
if (texture_srv.is_signed) {
if (!binding.key.IsInvalid() &&
AreDimensionsCompatible(host_shader_binding.dimension,
binding.key.dimension)) {
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)) {
desc.Format = host_formats_[uint32_t(format)].dxgi_format_snorm;
if (desc.Format == DXGI_FORMAT_UNKNOWN) {
unsupported_format_features_used_[uint32_t(format)] |=
kUnsupportedSnormBit;
}
descriptor_index = binding.descriptor_index_signed;
texture = IsSignedVersionSeparate(binding.key.format)
? binding.texture_signed
: binding.texture;
}
} else {
if (texture_util::IsAnySignNotSigned(binding.swizzled_signs)) {
desc.Format = GetDXGIUnormFormat(binding.key);
if (desc.Format == DXGI_FORMAT_UNKNOWN) {
unsupported_format_features_used_[uint32_t(format)] |=
kUnsupportedUnormBit;
}
descriptor_index = binding.descriptor_index;
texture = binding.texture;
}
}
binding_dimension = binding.key.dimension;
mip_max_level = binding.key.mip_max_level;
array_size = binding.key.depth;
// XE_GPU_SWIZZLE and D3D12_SHADER_COMPONENT_MAPPING are the same except for
// one bit.
desc.Shader4ComponentMapping =
binding.host_swizzle |
D3D12_SHADER_COMPONENT_MAPPING_ALWAYS_SET_BIT_AVOIDING_ZEROMEM_MISTAKES;
} else {
binding_dimension = Dimension::k2D;
mip_max_level = 0;
array_size = 1;
desc.Shader4ComponentMapping = D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(
D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0,
D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0,
D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0,
D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0);
}
if (desc.Format == DXGI_FORMAT_UNKNOWN) {
// A null descriptor must still have a valid format.
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
resource = nullptr;
}
NullSRVDescriptorIndex null_descriptor_index;
switch (texture_srv.dimension) {
case TextureDimension::k3D:
desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
desc.Texture3D.MostDetailedMip = 0;
desc.Texture3D.MipLevels = mip_max_level + 1;
desc.Texture3D.ResourceMinLODClamp = 0.0f;
if (binding_dimension != Dimension::k3D) {
// Create a null descriptor so it's safe to sample this texture even
// though it has different dimensions.
resource = nullptr;
}
null_descriptor_index = NullSRVDescriptorIndex::k3D;
break;
case TextureDimension::kCube:
desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
desc.TextureCube.MostDetailedMip = 0;
desc.TextureCube.MipLevels = mip_max_level + 1;
desc.TextureCube.ResourceMinLODClamp = 0.0f;
if (binding_dimension != Dimension::kCube) {
resource = nullptr;
}
null_descriptor_index = NullSRVDescriptorIndex::kCube;
break;
default:
desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
desc.Texture2DArray.MostDetailedMip = 0;
desc.Texture2DArray.MipLevels = mip_max_level + 1;
desc.Texture2DArray.FirstArraySlice = 0;
desc.Texture2DArray.ArraySize = array_size;
desc.Texture2DArray.PlaneSlice = 0;
desc.Texture2DArray.ResourceMinLODClamp = 0.0f;
if (binding_dimension == Dimension::k3D ||
binding_dimension == Dimension::kCube) {
resource = nullptr;
}
null_descriptor_index = NullSRVDescriptorIndex::k2DArray;
break;
}
auto provider = command_processor_->GetD3D12Context()->GetD3D12Provider();
D3D12_CPU_DESCRIPTOR_HANDLE source_handle;
if (descriptor_index != UINT32_MAX) {
assert_not_null(texture);
MarkTextureUsed(texture);
source_handle = GetTextureDescriptorCPUHandle(descriptor_index);
} else {
NullSRVDescriptorIndex null_descriptor_index;
switch (host_shader_binding.dimension) {
case TextureDimension::k3D:
null_descriptor_index = NullSRVDescriptorIndex::k3D;
break;
case TextureDimension::kCube:
null_descriptor_index = NullSRVDescriptorIndex::kCube;
break;
default:
assert_true(host_shader_binding.dimension == TextureDimension::k1D ||
host_shader_binding.dimension == TextureDimension::k2D);
null_descriptor_index = NullSRVDescriptorIndex::k2DArray;
}
source_handle = provider->OffsetViewDescriptor(
null_srv_descriptor_heap_start_, uint32_t(null_descriptor_index));
}
auto device = provider->GetDevice();
if (resource == nullptr) {
// Copy a pre-made null descriptor since it's faster than to create an SRV.
device->CopyDescriptorsSimple(
1, handle,
provider->OffsetViewDescriptor(null_srv_descriptor_heap_start_,
uint32_t(null_descriptor_index)),
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
return;
}
MarkTextureUsed(texture);
// Take the descriptor from the cache if it's cached, or create a new one in
// the cache, or directly if this texture was already used with a different
// swizzle. Profiling results say that CreateShaderResourceView takes the
// longest time of draw call processing, and it's very noticeable in many
// games.
bool cached_handle_available = false;
D3D12_CPU_DESCRIPTOR_HANDLE cached_handle = {};
assert_not_null(texture);
if (texture->cached_srv_descriptor_swizzle !=
Texture::kCachedSRVDescriptorSwizzleMissing) {
// Use an existing cached descriptor if it has the needed swizzle.
if (binding.host_swizzle == texture->cached_srv_descriptor_swizzle) {
cached_handle_available = true;
cached_handle = texture->cached_srv_descriptor;
}
} else {
// Try to create a new cached descriptor if it doesn't exist yet.
if (!srv_descriptor_cache_free_.empty()) {
cached_handle_available = true;
cached_handle = srv_descriptor_cache_free_.back();
srv_descriptor_cache_free_.pop_back();
} else if (srv_descriptor_cache_.empty() ||
srv_descriptor_cache_.back().current_usage >=
SRVDescriptorCachePage::kHeapSize) {
D3D12_DESCRIPTOR_HEAP_DESC new_heap_desc;
new_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
new_heap_desc.NumDescriptors = SRVDescriptorCachePage::kHeapSize;
new_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
new_heap_desc.NodeMask = 0;
ID3D12DescriptorHeap* new_heap;
if (SUCCEEDED(device->CreateDescriptorHeap(&new_heap_desc,
IID_PPV_ARGS(&new_heap)))) {
SRVDescriptorCachePage new_page;
new_page.heap = new_heap;
new_page.heap_start = new_heap->GetCPUDescriptorHandleForHeapStart();
new_page.current_usage = 1;
cached_handle_available = true;
cached_handle = new_page.heap_start;
srv_descriptor_cache_.push_back(new_page);
}
} else {
SRVDescriptorCachePage& page = srv_descriptor_cache_.back();
cached_handle_available = true;
cached_handle =
provider->OffsetViewDescriptor(page.heap_start, page.current_usage);
++page.current_usage;
}
if (cached_handle_available) {
device->CreateShaderResourceView(resource, &desc, cached_handle);
texture->cached_srv_descriptor = cached_handle;
texture->cached_srv_descriptor_swizzle = binding.host_swizzle;
}
}
if (cached_handle_available) {
device->CopyDescriptorsSimple(1, handle, cached_handle,
{
#if FINE_GRAINED_DRAW_SCOPES
SCOPE_profile_cpu_i(
"gpu",
"xe::gpu::d3d12::TextureCache::WriteActiveTextureBindfulSRV->"
"CopyDescriptorsSimple");
#endif // FINE_GRAINED_DRAW_SCOPES
device->CopyDescriptorsSimple(1, handle, source_handle,
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
} else {
device->CreateShaderResourceView(resource, &desc, handle);
}
}
uint32_t TextureCache::GetActiveTextureBindlessSRVIndex(
const D3D12Shader::TextureBinding& host_shader_binding) {
assert_true(bindless_resources_used_);
uint32_t descriptor_index = UINT32_MAX;
const TextureBinding& binding =
texture_bindings_[host_shader_binding.fetch_constant];
if (!binding.key.IsInvalid() &&
AreDimensionsCompatible(host_shader_binding.dimension,
binding.key.dimension)) {
descriptor_index = host_shader_binding.is_signed
? binding.descriptor_index_signed
: binding.descriptor_index;
}
if (descriptor_index == UINT32_MAX) {
switch (host_shader_binding.dimension) {
case TextureDimension::k3D:
descriptor_index =
uint32_t(D3D12CommandProcessor::SystemBindlessView::kNullTexture3D);
break;
case TextureDimension::kCube:
descriptor_index = uint32_t(
D3D12CommandProcessor::SystemBindlessView::kNullTextureCube);
break;
default:
assert_true(host_shader_binding.dimension == TextureDimension::k1D ||
host_shader_binding.dimension == TextureDimension::k2D);
descriptor_index = uint32_t(
D3D12CommandProcessor::SystemBindlessView::kNullTexture2DArray);
}
}
return descriptor_index;
}
TextureCache::SamplerParameters TextureCache::GetSamplerParameters(
const D3D12Shader::SamplerBinding& binding) const {
auto& regs = *register_file_;
@@ -1583,12 +1574,11 @@ TextureCache::SamplerParameters TextureCache::GetSamplerParameters(
parameters.clamp_z = fetch.clamp_z;
parameters.border_color = fetch.border_color;
uint32_t mip_min_level, mip_max_level;
uint32_t mip_min_level;
texture_util::GetSubresourcesFromFetchConstant(
fetch, nullptr, nullptr, nullptr, nullptr, nullptr, &mip_min_level,
&mip_max_level, binding.mip_filter);
nullptr, binding.mip_filter);
parameters.mip_min_level = mip_min_level;
parameters.mip_max_level = std::max(mip_max_level, mip_min_level);
AnisoFilter aniso_filter = binding.aniso_filter == AnisoFilter::kUseFetchConst
? fetch.aniso_filter
@@ -1675,7 +1665,8 @@ void TextureCache::WriteSampler(SamplerParameters parameters,
desc.BorderColor[3] = 0.0f;
}
desc.MinLOD = float(parameters.mip_min_level);
desc.MaxLOD = float(parameters.mip_max_level);
// Maximum mip level is in the texture resource itself.
desc.MaxLOD = FLT_MAX;
auto device =
command_processor_->GetD3D12Context()->GetD3D12Provider()->GetDevice();
device->CreateSampler(&desc, handle);
@@ -1737,8 +1728,8 @@ bool TextureCache::TileResolvedTexture(
resolve_tile_mode_info_[uint32_t(resolve_tile_mode)];
auto command_list = command_processor_->GetDeferredCommandList();
auto provider = command_processor_->GetD3D12Context()->GetD3D12Provider();
auto device = provider->GetDevice();
auto device =
command_processor_->GetD3D12Context()->GetD3D12Provider()->GetDevice();
uint32_t resolution_scale_log2 = IsResolutionScale2X() ? 1 : 0;
texture_base &= 0x1FFFFFFF;
@@ -1811,12 +1802,8 @@ bool TextureCache::TileResolvedTexture(
}
// Tile the texture.
D3D12_CPU_DESCRIPTOR_HANDLE descriptor_cpu_start;
D3D12_GPU_DESCRIPTOR_HANDLE descriptor_gpu_start;
if (command_processor_->RequestViewDescriptors(
ui::d3d12::DescriptorHeapPool::kHeapIndexInvalid, 2, 2,
descriptor_cpu_start, descriptor_gpu_start) ==
ui::d3d12::DescriptorHeapPool::kHeapIndexInvalid) {
ui::d3d12::util::DescriptorCPUGPUHandlePair descriptors[2];
if (!command_processor_->RequestOneUseSingleViewDescriptors(2, descriptors)) {
return false;
}
if (resolution_scale_log2) {
@@ -1826,19 +1813,15 @@ bool TextureCache::TileResolvedTexture(
}
command_processor_->SubmitBarriers();
command_list->D3DSetComputeRootSignature(resolve_tile_root_signature_);
ResolveTileConstants resolve_tile_constants;
resolve_tile_constants.info = uint32_t(endian) | (uint32_t(format) << 3) |
(resolution_scale_log2 << 9) |
((texture_pitch >> 5) << 10) |
(is_3d ? ((texture_height >> 5) << 19) : 0);
resolve_tile_constants.offset = offset_x | (offset_y << 5) | (offset_z << 10);
resolve_tile_constants.size = resolve_width | (resolve_height << 16);
resolve_tile_constants.host_base = uint32_t(footprint.Offset);
resolve_tile_constants.host_pitch = uint32_t(footprint.Footprint.RowPitch);
ui::d3d12::util::CreateRawBufferSRV(device, descriptor_cpu_start, buffer,
// TODO(Triang3l): Use precreated bindless descriptors here after overall
// cleanup/optimization involving typed buffers.
ui::d3d12::util::CreateRawBufferSRV(device, descriptors[1].first, buffer,
buffer_size);
D3D12_CPU_DESCRIPTOR_HANDLE descriptor_cpu_uav =
provider->OffsetViewDescriptor(descriptor_cpu_start, 1);
command_list->D3DSetComputeRootDescriptorTable(2, descriptors[1].second);
if (resolve_tile_mode_info.typed_uav_format != DXGI_FORMAT_UNKNOWN) {
// Not sure if this alignment is actually needed in Direct3D 12, but for
// safety. Also not using the full 512 MB buffer as a typed UAV because
@@ -1862,22 +1845,32 @@ bool TextureCache::TileResolvedTexture(
device->CreateUnorderedAccessView(resolution_scale_log2
? scaled_resolve_buffer_
: shared_memory_->GetBuffer(),
nullptr, &uav_desc, descriptor_cpu_uav);
nullptr, &uav_desc, descriptors[0].first);
} else {
if (resolution_scale_log2) {
resolve_tile_constants.guest_base = texture_base & 0xFFF;
CreateScaledResolveBufferRawUAV(
descriptor_cpu_uav, texture_base >> 12,
descriptors[0].first, texture_base >> 12,
((texture_base + texture_size - 1) >> 12) - (texture_base >> 12) + 1);
} else {
resolve_tile_constants.guest_base = texture_base;
shared_memory_->WriteRawUAVDescriptor(descriptor_cpu_uav);
shared_memory_->WriteRawUAVDescriptor(descriptors[0].first);
}
}
command_list->D3DSetComputeRootDescriptorTable(1, descriptor_gpu_start);
command_list->D3DSetComputeRootDescriptorTable(1, descriptors[0].second);
resolve_tile_constants.info = uint32_t(endian) | (uint32_t(format) << 3) |
(resolution_scale_log2 << 9) |
((texture_pitch >> 5) << 10) |
(is_3d ? ((texture_height >> 5) << 19) : 0);
resolve_tile_constants.offset = offset_x | (offset_y << 5) | (offset_z << 10);
resolve_tile_constants.size = resolve_width | (resolve_height << 16);
resolve_tile_constants.host_base = uint32_t(footprint.Offset);
resolve_tile_constants.host_pitch = uint32_t(footprint.Footprint.RowPitch);
command_list->D3DSetComputeRoot32BitConstants(
0, sizeof(resolve_tile_constants) / sizeof(uint32_t),
&resolve_tile_constants, 0);
command_processor_->SetComputePipeline(
resolve_tile_pipelines_[uint32_t(resolve_tile_mode)]);
// Each group processes 32x32 texels after resolution scaling has been
@@ -2339,8 +2332,6 @@ TextureCache::Texture* TextureCache::FindOrCreateTexture(TextureKey key) {
}
texture->base_watch_handle = nullptr;
texture->mip_watch_handle = nullptr;
texture->cached_srv_descriptor_swizzle =
Texture::kCachedSRVDescriptorSwizzleMissing;
textures_.insert(std::make_pair(map_key, texture));
COUNT_profile_set("gpu/texture_cache/textures", textures_.size());
textures_total_size_ += texture->resource_size;
@@ -2364,8 +2355,8 @@ bool TextureCache::LoadTextureData(Texture* texture) {
}
auto command_list = command_processor_->GetDeferredCommandList();
auto provider = command_processor_->GetD3D12Context()->GetD3D12Provider();
auto device = provider->GetDevice();
auto device =
command_processor_->GetD3D12Context()->GetD3D12Provider()->GetDevice();
// Get the pipeline.
LoadMode load_mode = GetLoadMode(texture->key);
@@ -2453,16 +2444,19 @@ bool TextureCache::LoadTextureData(Texture* texture) {
// descriptors for base and mips.
bool separate_base_and_mips_descriptors =
scaled_resolve && mip_first == 0 && mip_last != 0;
// TODO(Triang3l): Use precreated bindless descriptors here after overall
// cleanup/optimization involving typed buffers.
uint32_t descriptor_count = separate_base_and_mips_descriptors ? 4 : 2;
D3D12_CPU_DESCRIPTOR_HANDLE descriptor_cpu_start;
D3D12_GPU_DESCRIPTOR_HANDLE descriptor_gpu_start;
if (command_processor_->RequestViewDescriptors(
ui::d3d12::DescriptorHeapPool::kHeapIndexInvalid, descriptor_count,
descriptor_count, descriptor_cpu_start, descriptor_gpu_start) ==
ui::d3d12::DescriptorHeapPool::kHeapIndexInvalid) {
command_processor_->ReleaseScratchGPUBuffer(copy_buffer, copy_buffer_state);
ui::d3d12::util::DescriptorCPUGPUHandlePair descriptors[4];
if (!command_processor_->RequestOneUseSingleViewDescriptors(descriptor_count,
descriptors)) {
return false;
}
// Create two destination descriptors since the table has both.
for (uint32_t i = 0; i < descriptor_count; i += 2) {
ui::d3d12::util::CreateRawBufferUAV(device, descriptors[i].first,
copy_buffer, uint32_t(host_slice_size));
}
if (scaled_resolve) {
// TODO(Triang3l): Allow partial invalidation of scaled textures - send a
// part of scaled_resolve_pages_ to the shader and choose the source
@@ -2470,35 +2464,28 @@ bool TextureCache::LoadTextureData(Texture* texture) {
// it's not, duplicate the texels from the unscaled version - will be
// blocky with filtering, but better than nothing.
UseScaledResolveBufferForReading();
uint32_t srv_descriptor_offset = 0;
uint32_t source_descriptor_index = 1;
if (mip_first == 0) {
CreateScaledResolveBufferRawSRV(
provider->OffsetViewDescriptor(descriptor_cpu_start,
srv_descriptor_offset),
texture->key.base_page, (texture->base_size + 0xFFF) >> 12);
srv_descriptor_offset += 2;
descriptors[source_descriptor_index].first, texture->key.base_page,
(texture->base_size + 0xFFF) >> 12);
source_descriptor_index += 2;
}
if (mip_last != 0) {
CreateScaledResolveBufferRawSRV(
provider->OffsetViewDescriptor(descriptor_cpu_start,
srv_descriptor_offset),
texture->key.mip_page, (texture->mip_size + 0xFFF) >> 12);
descriptors[source_descriptor_index].first, texture->key.mip_page,
(texture->mip_size + 0xFFF) >> 12);
}
} else {
shared_memory_->UseForReading();
shared_memory_->WriteRawSRVDescriptor(descriptor_cpu_start);
}
// Create two destination descriptors since the table has both.
for (uint32_t i = 1; i < descriptor_count; i += 2) {
ui::d3d12::util::CreateRawBufferUAV(
device, provider->OffsetViewDescriptor(descriptor_cpu_start, i),
copy_buffer, uint32_t(host_slice_size));
shared_memory_->WriteRawSRVDescriptor(descriptors[1].first);
}
command_processor_->SetComputePipeline(pipeline);
command_list->D3DSetComputeRootSignature(load_root_signature_);
if (!separate_base_and_mips_descriptors) {
// Will be bound later.
command_list->D3DSetComputeRootDescriptorTable(1, descriptor_gpu_start);
// Will be bound later if separate base and mip descriptors.
command_list->D3DSetComputeRootDescriptorTable(2, descriptors[1].second);
command_list->D3DSetComputeRootDescriptorTable(1, descriptors[0].second);
}
// Submit commands.
@@ -2575,14 +2562,11 @@ bool TextureCache::LoadTextureData(Texture* texture) {
}
std::memcpy(cbuffer_mapping, &load_constants, sizeof(load_constants));
command_list->D3DSetComputeRootConstantBufferView(0, cbuffer_gpu_address);
if (separate_base_and_mips_descriptors) {
if (j == 0) {
command_list->D3DSetComputeRootDescriptorTable(1,
descriptor_gpu_start);
} else if (j == 1) {
command_list->D3DSetComputeRootDescriptorTable(
1, provider->OffsetViewDescriptor(descriptor_gpu_start, 2));
}
if (separate_base_and_mips_descriptors && j <= 1) {
command_list->D3DSetComputeRootDescriptorTable(
2, descriptors[j * 2 + 1].second);
command_list->D3DSetComputeRootDescriptorTable(
1, descriptors[j * 2].second);
}
command_processor_->SubmitBarriers();
// Each thread group processes 32x32x1 blocks after resolution scaling has
@@ -2642,6 +2626,138 @@ bool TextureCache::LoadTextureData(Texture* texture) {
return true;
}
uint32_t TextureCache::FindOrCreateTextureDescriptor(Texture& texture,
bool is_signed,
uint32_t host_swizzle) {
uint32_t descriptor_key = uint32_t(is_signed) | (host_swizzle << 1);
// Try to find an existing descriptor.
auto it = texture.srv_descriptors.find(descriptor_key);
if (it != texture.srv_descriptors.end()) {
return it->second;
}
// Create a new bindless or cached descriptor if supported.
D3D12_SHADER_RESOURCE_VIEW_DESC desc;
TextureFormat format = texture.key.format;
if (IsSignedVersionSeparate(format) &&
texture.key.signed_separate != uint32_t(is_signed)) {
// Not the version with the needed signedness.
return UINT32_MAX;
}
if (is_signed) {
// Not supporting signed compressed textures - hopefully DXN and DXT5A are
// not used as signed.
desc.Format = host_formats_[uint32_t(format)].dxgi_format_snorm;
} else {
desc.Format = GetDXGIUnormFormat(texture.key);
}
if (desc.Format == DXGI_FORMAT_UNKNOWN) {
unsupported_format_features_used_[uint32_t(format)] |=
is_signed ? kUnsupportedSnormBit : kUnsupportedUnormBit;
return UINT32_MAX;
}
uint32_t mip_levels = texture.key.mip_max_level + 1;
switch (texture.key.dimension) {
case Dimension::k1D:
case Dimension::k2D:
desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
desc.Texture2DArray.MostDetailedMip = 0;
desc.Texture2DArray.MipLevels = mip_levels;
desc.Texture2DArray.FirstArraySlice = 0;
desc.Texture2DArray.ArraySize = texture.key.depth;
desc.Texture2DArray.PlaneSlice = 0;
desc.Texture2DArray.ResourceMinLODClamp = 0.0f;
break;
case Dimension::k3D:
desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
desc.Texture3D.MostDetailedMip = 0;
desc.Texture3D.MipLevels = mip_levels;
desc.Texture3D.ResourceMinLODClamp = 0.0f;
break;
case Dimension::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;
auto device =
command_processor_->GetD3D12Context()->GetD3D12Provider()->GetDevice();
uint32_t descriptor_index;
if (bindless_resources_used_) {
descriptor_index =
command_processor_->RequestPersistentViewBindlessDescriptor();
if (descriptor_index == UINT32_MAX) {
XELOGE(
"Failed to create a texture descriptor - no free bindless view "
"descriptors");
return UINT32_MAX;
}
} else {
if (!srv_descriptor_cache_free_.empty()) {
descriptor_index = srv_descriptor_cache_free_.back();
srv_descriptor_cache_free_.pop_back();
} else {
// Allocated + 1 (including the descriptor that is being added), rounded
// up to SRVDescriptorCachePage::kHeapSize, (allocated + 1 + size - 1).
uint32_t cache_pages_needed = (srv_descriptor_cache_allocated_ +
SRVDescriptorCachePage::kHeapSize) /
SRVDescriptorCachePage::kHeapSize;
if (srv_descriptor_cache_.size() < cache_pages_needed) {
D3D12_DESCRIPTOR_HEAP_DESC cache_heap_desc;
cache_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
cache_heap_desc.NumDescriptors = SRVDescriptorCachePage::kHeapSize;
cache_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
cache_heap_desc.NodeMask = 0;
while (srv_descriptor_cache_.size() < cache_pages_needed) {
SRVDescriptorCachePage cache_page;
if (FAILED(device->CreateDescriptorHeap(
&cache_heap_desc, IID_PPV_ARGS(&cache_page.heap)))) {
XELOGE(
"Failed to create a texture descriptor - couldn't create a "
"descriptor cache heap");
return UINT32_MAX;
}
cache_page.heap_start =
cache_page.heap->GetCPUDescriptorHandleForHeapStart();
srv_descriptor_cache_.push_back(cache_page);
}
}
descriptor_index = srv_descriptor_cache_allocated_++;
}
}
device->CreateShaderResourceView(
texture.resource, &desc, GetTextureDescriptorCPUHandle(descriptor_index));
texture.srv_descriptors.insert({descriptor_key, descriptor_index});
return descriptor_index;
}
D3D12_CPU_DESCRIPTOR_HANDLE TextureCache::GetTextureDescriptorCPUHandle(
uint32_t descriptor_index) const {
auto provider = command_processor_->GetD3D12Context()->GetD3D12Provider();
if (bindless_resources_used_) {
return provider->OffsetViewDescriptor(
command_processor_->GetViewBindlessHeapCPUStart(), descriptor_index);
}
D3D12_CPU_DESCRIPTOR_HANDLE heap_start =
srv_descriptor_cache_[descriptor_index /
SRVDescriptorCachePage::kHeapSize]
.heap_start;
uint32_t heap_offset = descriptor_index % SRVDescriptorCachePage::kHeapSize;
return provider->OffsetViewDescriptor(heap_start, heap_offset);
}
void TextureCache::MarkTextureUsed(Texture* texture) {
uint64_t current_frame = command_processor_->GetCurrentFrame();
// This is called very frequently, don't relink unless needed for caching.
@@ -2687,8 +2803,10 @@ void TextureCache::WatchCallback(Texture* texture, bool is_mip) {
}
void TextureCache::ClearBindings() {
std::memset(texture_bindings_, 0, sizeof(texture_bindings_));
texture_keys_in_sync_ = 0;
for (size_t i = 0; i < xe::countof(texture_bindings_); ++i) {
texture_bindings_[i].Clear();
}
texture_bindings_in_sync_ = 0;
// Already reset everything.
texture_invalidated_.store(false, std::memory_order_relaxed);
}