Files
Xenia-Canary/src/xenia/gpu/d3d12/d3d12_render_target_cache.h
Triang3l 7869b080d3 [D3D12] Truncate depth to float24 in EDRAM range ownership transfers and resolves by default
Doesn't ruin the "greater or equal" depth test in subsequent rendering passes if precision is lost, unlike rounding to the nearest
2022-06-22 12:53:09 +03:00

822 lines
32 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_D3D12_D3D12_RENDER_TARGET_CACHE_H_
#define XENIA_GPU_D3D12_D3D12_RENDER_TARGET_CACHE_H_
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <deque>
#include <functional>
#include <memory>
#include <unordered_map>
#include <utility>
#include <vector>
#include "xenia/base/assert.h"
#include "xenia/gpu/d3d12/d3d12_shared_memory.h"
#include "xenia/gpu/d3d12/d3d12_texture_cache.h"
#include "xenia/gpu/draw_util.h"
#include "xenia/gpu/render_target_cache.h"
#include "xenia/gpu/trace_writer.h"
#include "xenia/gpu/xenos.h"
#include "xenia/memory.h"
#include "xenia/ui/d3d12/d3d12_cpu_descriptor_pool.h"
#include "xenia/ui/d3d12/d3d12_provider.h"
#include "xenia/ui/d3d12/d3d12_upload_buffer_pool.h"
#include "xenia/ui/d3d12/d3d12_util.h"
namespace xe {
namespace gpu {
namespace d3d12 {
class D3D12CommandProcessor;
class D3D12RenderTargetCache final : public RenderTargetCache {
public:
D3D12RenderTargetCache(const RegisterFile& register_file,
const Memory& memory, TraceWriter& trace_writer,
uint32_t draw_resolution_scale_x,
uint32_t draw_resolution_scale_y,
D3D12CommandProcessor& command_processor,
bool bindless_resources_used)
: RenderTargetCache(register_file, memory, &trace_writer,
draw_resolution_scale_x, draw_resolution_scale_y),
command_processor_(command_processor),
trace_writer_(trace_writer),
bindless_resources_used_(bindless_resources_used) {}
~D3D12RenderTargetCache() override;
bool Initialize();
void Shutdown(bool from_destructor = false);
void CompletedSubmissionUpdated();
void BeginSubmission();
Path GetPath() const override { return path_; }
bool Update(bool is_rasterization_done,
reg::RB_DEPTHCONTROL normalized_depth_control,
uint32_t normalized_color_mask,
const Shader& vertex_shader) override;
void InvalidateCommandListRenderTargets() {
are_current_command_list_render_targets_valid_ = false;
}
bool msaa_2x_supported() const { return msaa_2x_supported_; }
void WriteEdramRawSRVDescriptor(D3D12_CPU_DESCRIPTOR_HANDLE handle);
void WriteEdramRawUAVDescriptor(D3D12_CPU_DESCRIPTOR_HANDLE handle);
void WriteEdramUintPow2SRVDescriptor(D3D12_CPU_DESCRIPTOR_HANDLE handle,
uint32_t element_size_bytes_pow2);
void WriteEdramUintPow2UAVDescriptor(D3D12_CPU_DESCRIPTOR_HANDLE handle,
uint32_t element_size_bytes_pow2);
// Performs the resolve to a shared memory area according to the current
// register values, and also clears the render targets if needed. Must be in a
// frame for calling.
bool Resolve(const Memory& memory, D3D12SharedMemory& shared_memory,
D3D12TextureCache& texture_cache, uint32_t& written_address_out,
uint32_t& written_length_out);
// Returns true if any downloads were submitted to the command processor.
bool InitializeTraceSubmitDownloads();
void InitializeTraceCompleteDownloads();
void RestoreEdramSnapshot(const void* snapshot);
// For host render targets.
bool gamma_render_target_as_srgb() const {
return gamma_render_target_as_srgb_;
}
// Using R16G16[B16A16]_SNORM, which are -1...1, not the needed -32...32.
// Persistent data doesn't depend on this, so can be overriden by per-game
// configuration.
bool IsFixed16TruncatedToMinus1To1() const {
return GetPath() == Path::kHostRenderTargets &&
!cvars::snorm16_render_target_full_range;
}
bool depth_float24_round() const { return depth_float24_round_; }
bool depth_float24_convert_in_pixel_shader() const {
return depth_float24_convert_in_pixel_shader_;
}
DXGI_FORMAT GetColorResourceDXGIFormat(
xenos::ColorRenderTargetFormat format) const;
DXGI_FORMAT GetColorDrawDXGIFormat(
xenos::ColorRenderTargetFormat format) const;
DXGI_FORMAT GetColorOwnershipTransferDXGIFormat(
xenos::ColorRenderTargetFormat format,
bool* is_integer_out = nullptr) const;
static DXGI_FORMAT GetDepthResourceDXGIFormat(
xenos::DepthRenderTargetFormat format);
static DXGI_FORMAT GetDepthDSVDXGIFormat(
xenos::DepthRenderTargetFormat format);
static DXGI_FORMAT GetDepthSRVDepthDXGIFormat(
xenos::DepthRenderTargetFormat format);
static DXGI_FORMAT GetDepthSRVStencilDXGIFormat(
xenos::DepthRenderTargetFormat format);
protected:
uint32_t GetMaxRenderTargetWidth() const override {
return D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION;
}
uint32_t GetMaxRenderTargetHeight() const override {
return D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION;
}
RenderTarget* CreateRenderTarget(RenderTargetKey key) override;
bool IsHostDepthEncodingDifferent(
xenos::DepthRenderTargetFormat format) const override;
void RequestPixelShaderInterlockBarrier() override;
private:
enum class EdramBufferModificationStatus {
// The values are ordered by how strong the barrier conditions are.
// No uncommitted ROV/UAV writes.
kUnmodified,
// Need to commit before the next ROV usage with overlap.
kAsROV,
// Need to commit before any next ROV usage.
kAsUAV,
};
void TransitionEdramBuffer(D3D12_RESOURCE_STATES new_state);
void MarkEdramBufferModified(
EdramBufferModificationStatus modification_status =
EdramBufferModificationStatus::kAsUAV);
void CommitEdramBufferUAVWrites(EdramBufferModificationStatus commit_status =
EdramBufferModificationStatus::kAsROV);
D3D12CommandProcessor& command_processor_;
TraceWriter& trace_writer_;
bool bindless_resources_used_;
Path path_ = Path::kHostRenderTargets;
// For host render targets, an EDRAM-sized scratch buffer for:
// - Guest render target data copied from host render targets during copying
// in resolves and in frame trace creation.
// - Host float32 depth in ownership transfers when the host depth texture and
// the destination are the same.
// For rasterizer-ordered view, the buffer containing the EDRAM data.
// (Note that if a hybrid RTV / DSV + ROV approach to color render targets is
// added, which is, however, unlikely as it would have very complicated
// interaction with depth / stencil testing, host depth will need to be
// copied to a different buffer - the same range may have ROV-owned color and
// host float32 depth at the same time).
ID3D12Resource* edram_buffer_ = nullptr;
D3D12_RESOURCE_STATES edram_buffer_state_;
EdramBufferModificationStatus edram_buffer_modification_status_ =
EdramBufferModificationStatus::kUnmodified;
// Non-shader-visible descriptor heap containing pre-created SRV and UAV
// descriptors of the EDRAM buffer, for faster binding (by copying rather
// than creation).
enum class EdramBufferDescriptorIndex : uint32_t {
kRawSRV,
kR32UintSRV,
kR32G32UintSRV,
kR32G32B32A32UintSRV,
kRawUAV,
kR32UintUAV,
kR32G32UintUAV,
kR32G32B32A32UintUAV,
kCount,
};
ID3D12DescriptorHeap* edram_buffer_descriptor_heap_ = nullptr;
D3D12_CPU_DESCRIPTOR_HANDLE edram_buffer_descriptor_heap_start_;
// Resolve copying root signature and pipelines.
// Parameter 0 - draw_util::ResolveCopyShaderConstants or its ::DestRelative.
// Parameter 1 - destination (shared memory or a part of it).
// Parameter 2 - source (EDRAM).
ID3D12RootSignature* resolve_copy_root_signature_ = nullptr;
struct ResolveCopyShaderCode {
const void* unscaled;
size_t unscaled_size;
const void* scaled;
size_t scaled_size;
};
static const ResolveCopyShaderCode
kResolveCopyShaders[size_t(draw_util::ResolveCopyShaderIndex::kCount)];
ID3D12PipelineState* resolve_copy_pipelines_[size_t(
draw_util::ResolveCopyShaderIndex::kCount)] = {};
// For traces.
ID3D12Resource* edram_snapshot_download_buffer_ = nullptr;
std::unique_ptr<ui::d3d12::D3D12UploadBufferPool>
edram_snapshot_restore_pool_;
// For host render targets.
class D3D12RenderTarget final : public RenderTarget {
public:
// descriptor_draw_srgb is only used for k_8_8_8_8 render targets when host
// sRGB (gamma_render_target_as_srgb) is used. descriptor_load is present
// when the DXGI formats are different for drawing and bit-exact loading
// (for NaN pattern preservation across EDRAM tile ownership transfers in
// floating-point formats, and to distinguish between two -1 representations
// in snorm formats).
D3D12RenderTarget(
RenderTargetKey key, ID3D12Resource* resource,
ui::d3d12::D3D12CpuDescriptorPool::Descriptor&& descriptor_draw,
ui::d3d12::D3D12CpuDescriptorPool::Descriptor&& descriptor_draw_srgb,
ui::d3d12::D3D12CpuDescriptorPool::Descriptor&&
descriptor_load_separate,
ui::d3d12::D3D12CpuDescriptorPool::Descriptor&& descriptor_srv,
ui::d3d12::D3D12CpuDescriptorPool::Descriptor&& descriptor_srv_stencil,
D3D12_RESOURCE_STATES resource_state)
: RenderTarget(key),
resource_(resource),
descriptor_draw_(std::move(descriptor_draw)),
descriptor_draw_srgb_(std::move(descriptor_draw_srgb)),
descriptor_load_separate_(std::move(descriptor_load_separate)),
descriptor_srv_(std::move(descriptor_srv)),
descriptor_srv_stencil_(std::move(descriptor_srv_stencil)),
resource_state_(resource_state) {}
ID3D12Resource* resource() const { return resource_.Get(); }
const ui::d3d12::D3D12CpuDescriptorPool::Descriptor& descriptor_draw()
const {
return descriptor_draw_;
}
const ui::d3d12::D3D12CpuDescriptorPool::Descriptor& descriptor_draw_srgb()
const {
return descriptor_draw_srgb_;
}
const ui::d3d12::D3D12CpuDescriptorPool::Descriptor& descriptor_srv()
const {
return descriptor_srv_;
}
const ui::d3d12::D3D12CpuDescriptorPool::Descriptor&
descriptor_srv_stencil() const {
return descriptor_srv_stencil_;
}
const ui::d3d12::D3D12CpuDescriptorPool::Descriptor&
descriptor_load_separate() const {
return descriptor_load_separate_;
}
D3D12_RESOURCE_STATES SetResourceState(D3D12_RESOURCE_STATES new_state) {
D3D12_RESOURCE_STATES old_state = resource_state_;
resource_state_ = new_state;
return old_state;
}
uint32_t temporary_srv_descriptor_index() const {
return temporary_srv_descriptor_index_;
}
void SetTemporarySRVDescriptorIndex(uint32_t index) {
temporary_srv_descriptor_index_ = index;
}
uint32_t temporary_srv_descriptor_index_stencil() const {
return temporary_srv_descriptor_index_stencil_;
}
void SetTemporarySRVDescriptorIndexStencil(uint32_t index) {
temporary_srv_descriptor_index_stencil_ = index;
}
uint32_t temporary_sort_index() const { return temporary_sort_index_; }
void SetTemporarySortIndex(uint32_t index) {
temporary_sort_index_ = index;
}
private:
Microsoft::WRL::ComPtr<ID3D12Resource> resource_;
ui::d3d12::D3D12CpuDescriptorPool::Descriptor descriptor_draw_;
ui::d3d12::D3D12CpuDescriptorPool::Descriptor descriptor_draw_srgb_;
ui::d3d12::D3D12CpuDescriptorPool::Descriptor descriptor_load_separate_;
// Texture SRV non-shader-visible descriptors, to prepare shader-visible
// descriptors faster, by copying rather than by creating every time.
// TODO(Triang3l): With bindless resources, persistently store them in the
// heap.
ui::d3d12::D3D12CpuDescriptorPool::Descriptor descriptor_srv_;
ui::d3d12::D3D12CpuDescriptorPool::Descriptor descriptor_srv_stencil_;
D3D12_RESOURCE_STATES resource_state_;
// Temporary storage for indices in operations like transfers and dumps.
uint32_t temporary_srv_descriptor_index_ = UINT32_MAX;
uint32_t temporary_srv_descriptor_index_stencil_ = UINT32_MAX;
uint32_t temporary_sort_index_ = 0;
};
enum TransferCBVRegister : uint32_t {
kTransferCBVRegisterStencilMask,
kTransferCBVRegisterAddress,
kTransferCBVRegisterHostDepthAddress,
};
enum TransferSRVRegister : uint32_t {
kTransferSRVRegisterColor,
kTransferSRVRegisterDepth,
kTransferSRVRegisterStencil,
kTransferSRVRegisterHostDepth,
kTransferSRVRegisterCount,
};
enum TransferUsedRootParameter : uint32_t {
// Changed 8 times per transfer.
kTransferUsedRootParameterStencilMaskConstant,
kTransferUsedRootParameterColorSRV,
// Mutually exclusive with ColorSRV.
kTransferUsedRootParameterDepthSRV,
// Mutually exclusive with ColorSRV.
kTransferUsedRootParameterStencilSRV,
// May happen to be the same for different sources.
kTransferUsedRootParameterAddressConstant,
kTransferUsedRootParameterHostDepthSRV,
kTransferUsedRootParameterHostDepthAddressConstant,
kTransferUsedRootParameterCount,
kTransferUsedRootParameterStencilMaskConstantBit =
uint32_t(1) << kTransferUsedRootParameterStencilMaskConstant,
kTransferUsedRootParameterColorSRVBit =
uint32_t(1) << kTransferUsedRootParameterColorSRV,
kTransferUsedRootParameterDepthSRVBit =
uint32_t(1) << kTransferUsedRootParameterDepthSRV,
kTransferUsedRootParameterStencilSRVBit =
uint32_t(1) << kTransferUsedRootParameterStencilSRV,
kTransferUsedRootParameterAddressConstantBit =
uint32_t(1) << kTransferUsedRootParameterAddressConstant,
kTransferUsedRootParameterHostDepthSRVBit =
uint32_t(1) << kTransferUsedRootParameterHostDepthSRV,
kTransferUsedRootParameterHostDepthAddressConstantBit =
uint32_t(1) << kTransferUsedRootParameterHostDepthAddressConstant,
kTransferUsedRootParametersDescriptorMask =
kTransferUsedRootParameterColorSRVBit |
kTransferUsedRootParameterDepthSRVBit |
kTransferUsedRootParameterStencilSRVBit |
kTransferUsedRootParameterHostDepthSRVBit,
};
enum class TransferRootSignatureIndex {
kColor,
kDepth,
kDepthStencil,
kColorToStencilBit,
kStencilToStencilBit,
kColorAndHostDepth,
kDepthAndHostDepth,
kDepthStencilAndHostDepth,
kCount,
};
static const uint32_t
kTransferUsedRootParameters[size_t(TransferRootSignatureIndex::kCount)];
enum class TransferMode : uint32_t {
// 1 SRV (color texture), source constant.
kColorToDepth,
// 1 SRV (color texture), source constant.
kColorToColor,
// 1 or 2 SRVs (depth texture, stencil texture if SV_StencilRef is
// supported), source constant.
kDepthToDepth,
// 2 SRVs (depth texture, stencil texture), source constant.
kDepthToColor,
// 1 SRV (color texture), mask constant (most frequently changed, 8 times
// per transfer), source constant.
kColorToStencilBit,
// 1 SRV (stencil texture), mask constant, source constant.
kDepthToStencilBit,
// Two-source modes, using the host depth if it, when converted to the guest
// format, matches what's in the owner source (not modified, keep host
// precision), or the guest data otherwise (significantly modified, possibly
// cleared). Stencil for SV_StencilRef is always taken from the guest
// source.
// 2 SRVs (color texture, host depth texture or buffer), source constant,
// host depth source constant.
kColorAndHostDepthToDepth,
// When using different source and destination depth formats. 2 or 3 SRVs
// (depth texture, stencil texture if SV_StencilRef is supported, host depth
// texture or buffer), source constant, host depth source constant.
kDepthAndHostDepthToDepth,
kCount,
};
enum class TransferOutput {
kColor,
kDepth,
// With this output, kTransferCBVRegisterStencilMask is used.
kStencilBit,
};
struct TransferModeInfo {
TransferOutput output;
TransferRootSignatureIndex root_signature_no_stencil_ref;
TransferRootSignatureIndex root_signature_with_stencil_ref;
};
static const TransferModeInfo kTransferModes[size_t(TransferMode::kCount)];
union TransferShaderKey {
uint32_t key;
struct {
xenos::MsaaSamples dest_msaa_samples : xenos::kMsaaSamplesBits;
uint32_t dest_resource_format : xenos::kRenderTargetFormatBits;
xenos::MsaaSamples source_msaa_samples : xenos::kMsaaSamplesBits;
// Always 1x when host_depth_source_is_copy is true not to create the same
// pipeline for different MSAA sample counts as it doesn't matter in this
// case.
xenos::MsaaSamples host_depth_source_msaa_samples
: xenos::kMsaaSamplesBits;
uint32_t source_resource_format : xenos::kRenderTargetFormatBits;
// If host depth is also fetched, whether it's pre-copied to the EDRAM
// buffer (but since it's just a scratch buffer, with tiles laid out
// linearly with the same pitch as in the original render target; also no
// swapping of 40-sample columns as opposed to the host render target -
// this is done only for the color source).
uint32_t host_depth_source_is_copy : 1;
// Last bits because this affects the root signature - after sorting, only
// change it as fewer times as possible. Depth buffers have an additional
// stencil SRV.
static_assert(size_t(TransferMode::kCount) <= (size_t(1) << 3));
TransferMode mode : 3;
};
TransferShaderKey() : key(0) { static_assert_size(*this, sizeof(key)); }
struct Hasher {
size_t operator()(const TransferShaderKey& key) const {
return std::hash<uint32_t>{}(key.key);
}
};
bool operator==(const TransferShaderKey& other_key) const {
return key == other_key.key;
}
bool operator!=(const TransferShaderKey& other_key) const {
return !(*this == other_key);
}
bool operator<(const TransferShaderKey& other_key) const {
return key < other_key.key;
}
};
union TransferAddressConstant {
uint32_t constant;
struct {
// All in tiles.
uint32_t dest_pitch : xenos::kEdramPitchTilesBits;
uint32_t source_pitch : xenos::kEdramPitchTilesBits;
// Safe to use 12 bits for signed difference - no ownership transfer can
// ever occur between render targets with EDRAM base >= 2048 as this would
// result in 0-length spans. 10 + 10 + 12 is exactly 32, any more bits,
// and more root 32-bit constants will be used.
// Destination base in tiles minus source base in tiles (not vice versa
// because this is a transform of the coordinate system, not addresses
// themselves).
// 0 for host_depth_source_is_copy (ignored in this case anyway as
// destination == source anyway).
int32_t source_to_dest : xenos::kEdramBaseTilesBits;
};
TransferAddressConstant() : constant(0) {
static_assert_size(*this, sizeof(constant));
}
bool operator==(const TransferAddressConstant& other_constant) const {
return constant == other_constant.constant;
}
bool operator!=(const TransferAddressConstant& other_constant) const {
return !(*this == other_constant);
}
};
struct TransferInvocation {
Transfer transfer;
TransferShaderKey shader_key;
TransferInvocation(const Transfer& transfer,
const TransferShaderKey& shader_key)
: transfer(transfer), shader_key(shader_key) {}
bool operator<(const TransferInvocation& other_invocation) {
// TODO(Triang3l): See if it may be better to sort by the source in the
// first place, especially when reading the same data multiple times (like
// to write the stencil bits after depth) for better read locality.
// Sort by the shader key primarily to reduce pipeline state (context)
// switches.
if (shader_key != other_invocation.shader_key) {
return shader_key < other_invocation.shader_key;
}
// Host depth render targets are changed rarely if they exist, won't save
// many binding changes, ignore them for simplicity (their existence is
// caught by the shader key change).
assert_not_null(transfer.source);
assert_not_null(other_invocation.transfer.source);
uint32_t source_index =
static_cast<const D3D12RenderTarget*>(transfer.source)
->temporary_sort_index();
uint32_t other_source_index = static_cast<const D3D12RenderTarget*>(
other_invocation.transfer.source)
->temporary_sort_index();
if (source_index != other_source_index) {
return source_index < other_source_index;
}
return transfer.start_tiles < other_invocation.transfer.start_tiles;
}
bool CanBeMergedIntoOneDraw(
const TransferInvocation& other_invocation) const {
return shader_key == other_invocation.shader_key &&
transfer.AreSourcesSame(other_invocation.transfer);
}
};
enum {
kHostDepthStoreRootParameterConstants,
kHostDepthStoreRootParameterSource,
kHostDepthStoreRootParameterDest,
kHostDepthStoreRootParameterCount,
};
union DumpPipelineKey {
uint32_t key;
struct {
xenos::MsaaSamples msaa_samples : 2;
uint32_t resource_format : 4;
// Last bit because this affects the root signature - after sorting, only
// change it at most once. Depth buffers have an additional stencil SRV.
uint32_t is_depth : 1;
};
DumpPipelineKey() : key(0) { static_assert_size(*this, sizeof(key)); }
struct Hasher {
size_t operator()(const DumpPipelineKey& key) const {
return std::hash<uint32_t>{}(key.key);
}
};
bool operator==(const DumpPipelineKey& other_key) const {
return key == other_key.key;
}
bool operator!=(const DumpPipelineKey& other_key) const {
return !(*this == other_key);
}
bool operator<(const DumpPipelineKey& other_key) const {
return key < other_key.key;
}
xenos::ColorRenderTargetFormat GetColorFormat() const {
assert_false(is_depth);
return xenos::ColorRenderTargetFormat(resource_format);
}
xenos::DepthRenderTargetFormat GetDepthFormat() const {
assert_true(is_depth);
return xenos::DepthRenderTargetFormat(resource_format);
}
};
union DumpOffsets {
uint32_t offsets;
struct {
uint32_t dispatch_first_tile : xenos::kEdramBaseTilesBits;
uint32_t source_base_tiles : xenos::kEdramBaseTilesBits;
};
DumpOffsets() : offsets(0) { static_assert_size(*this, sizeof(offsets)); }
bool operator==(const DumpOffsets& other_offsets) const {
return offsets == other_offsets.offsets;
}
bool operator!=(const DumpOffsets& other_offsets) const {
return !(*this == other_offsets);
}
};
union DumpPitches {
uint32_t pitches;
struct {
// Both in tiles.
uint32_t dest_pitch : xenos::kEdramPitchTilesBits;
uint32_t source_pitch : xenos::kEdramPitchTilesBits;
};
DumpPitches() : pitches(0) { static_assert_size(*this, sizeof(pitches)); }
bool operator==(const DumpPitches& other_pitches) const {
return pitches == other_pitches.pitches;
}
bool operator!=(const DumpPitches& other_pitches) const {
return !(*this == other_pitches);
}
};
enum DumpCbuffer : uint32_t {
kDumpCbufferOffsets,
kDumpCbufferPitches,
kDumpCbufferCount,
};
enum DumpRootParameter : uint32_t {
// May be changed multiple times for the same source.
kDumpRootParameterOffsets,
// One resolve may need multiple sources.
kDumpRootParameterSource,
// May be different for different sources.
kDumpRootParameterColorPitches = kDumpRootParameterSource + 1,
// Only changed between 32bpp and 64bpp.
kDumpRootParameterColorEdram,
kDumpRootParameterColorCount,
// Same change frequency than the source (though currently the command
// processor can't contiguously allocate multiple descriptors with bindless,
// when such functionality is added, switch to one root signature).
kDumpRootParameterDepthStencil = kDumpRootParameterSource + 1,
kDumpRootParameterDepthPitches,
kDumpRootParameterDepthEdram,
kDumpRootParameterDepthCount,
};
struct DumpInvocation {
ResolveCopyDumpRectangle rectangle;
DumpPipelineKey pipeline_key;
DumpInvocation(const ResolveCopyDumpRectangle& rectangle,
const DumpPipelineKey& pipeline_key)
: rectangle(rectangle), pipeline_key(pipeline_key) {}
bool operator<(const DumpInvocation& other_invocation) {
// Sort by the pipeline key primarily to reduce pipeline state (context)
// switches.
if (pipeline_key != other_invocation.pipeline_key) {
return pipeline_key < other_invocation.pipeline_key;
}
assert_not_null(rectangle.render_target);
uint32_t render_target_index =
static_cast<const D3D12RenderTarget*>(rectangle.render_target)
->temporary_sort_index();
const ResolveCopyDumpRectangle& other_rectangle =
other_invocation.rectangle;
uint32_t other_render_target_index =
static_cast<const D3D12RenderTarget*>(other_rectangle.render_target)
->temporary_sort_index();
if (render_target_index != other_render_target_index) {
return render_target_index < other_render_target_index;
}
if (rectangle.row_first != other_rectangle.row_first) {
return rectangle.row_first < other_rectangle.row_first;
}
return rectangle.row_first_start < other_rectangle.row_first_start;
}
};
// Returns:
// - A pointer to 1 pipeline for writing color or depth (or stencil via
// SV_StencilRef).
// - A pointer to 8 pipelines for writing stencil by discarding samples
// depending on whether they have one bit set, from 1 << 0 to 1 << 7.
// - Null if failed to create.
ID3D12PipelineState* const* GetOrCreateTransferPipelines(
TransferShaderKey key);
static TransferMode GetTransferMode(bool dest_is_stencil_bit,
bool dest_is_depth, bool source_is_depth,
bool source_has_host_depth) {
assert_true(dest_is_depth ||
(!dest_is_stencil_bit && !source_has_host_depth));
if (dest_is_stencil_bit) {
return source_is_depth ? TransferMode::kDepthToStencilBit
: TransferMode::kColorToStencilBit;
}
if (dest_is_depth) {
if (source_is_depth) {
return source_has_host_depth ? TransferMode::kDepthAndHostDepthToDepth
: TransferMode::kDepthToDepth;
}
return source_has_host_depth ? TransferMode::kColorAndHostDepthToDepth
: TransferMode::kColorToDepth;
}
return source_is_depth ? TransferMode::kDepthToColor
: TransferMode::kColorToColor;
}
// Do ownership transfers for render targets - each render target / vector may
// be null / empty in case there's nothing to do for them.
// resolve_clear_rectangle is expected to be provided by
// PrepareHostRenderTargetsResolveClear which should do all the needed size
// bound checks.
void PerformTransfersAndResolveClears(
uint32_t render_target_count, RenderTarget* const* render_targets,
const std::vector<Transfer>* render_target_transfers,
const uint64_t* render_target_resolve_clear_values = nullptr,
const Transfer::Rectangle* resolve_clear_rectangle = nullptr);
// Accepts an array of (1 + xenos::kMaxColorRenderTargets) render targets,
// first depth, then color.
void SetCommandListRenderTargets(
RenderTarget* const* depth_and_color_render_targets);
ID3D12PipelineState* GetOrCreateDumpPipeline(DumpPipelineKey key);
// Writes contents of host render targets within rectangles from
// ResolveInfo::GetCopyEdramTileSpan to edram_buffer_.
void DumpRenderTargets(uint32_t dump_base, uint32_t dump_row_length_used,
uint32_t dump_rows, uint32_t dump_pitch);
bool use_stencil_reference_output_ = false;
bool gamma_render_target_as_srgb_ = false;
bool depth_float24_round_ = false;
bool depth_float24_convert_in_pixel_shader_ = false;
bool msaa_2x_supported_ = false;
std::shared_ptr<ui::d3d12::D3D12CpuDescriptorPool> descriptor_pool_color_;
std::shared_ptr<ui::d3d12::D3D12CpuDescriptorPool> descriptor_pool_depth_;
std::shared_ptr<ui::d3d12::D3D12CpuDescriptorPool> descriptor_pool_srv_;
ui::d3d12::D3D12CpuDescriptorPool::Descriptor null_rtv_descriptor_ss_;
ui::d3d12::D3D12CpuDescriptorPool::Descriptor null_rtv_descriptor_ms_;
// Possible tile ownership transfer paths:
// - To color:
// - From color: 1 SRV (color).
// - From depth: 2 SRVs (depth, stencil).
// - To depth / stencil (with SV_StencilRef):
// - From color: 1 SRV (color).
// - From depth: 2 SRVs (depth, stencil).
// - From color and float32 depth: 2 SRVs (color with stencil, depth).
// - Different depth buffer: depth SRV is a texture.
// - Same depth buffer: depth SRV is a buffer (pre-copied).
// - To depth (no SV_StencilRef):
// - From color: 1 SRV (color).
// - From depth: 1 SRV (depth).
// - From color and float32 depth: 2 SRVs (color, depth).
// - Different depth buffer: depth SRV is a texture.
// - Same depth buffer: depth SRV is a buffer (pre-copied).
// - To stencil (no SV_StencilRef):
// - From color: 1 SRV (color).
// - From depth: 1 SRV (stencil).
const RenderTarget* const*
current_command_list_render_targets_[1 + xenos::kMaxColorRenderTargets];
uint32_t are_current_command_list_render_targets_srgb_ = 0;
bool are_current_command_list_render_targets_valid_ = false;
// Temporary storage for descriptors used in PerformTransfersAndResolveClears
// and DumpRenderTargets.
std::vector<D3D12_CPU_DESCRIPTOR_HANDLE> current_temporary_descriptors_cpu_;
std::vector<ui::d3d12::util::DescriptorCpuGpuHandlePair>
current_temporary_descriptors_gpu_;
ID3D12RootSignature* host_depth_store_root_signature_ = nullptr;
ID3D12PipelineState*
host_depth_store_pipelines_[size_t(xenos::MsaaSamples::k4X) + 1] = {};
std::unique_ptr<ui::d3d12::D3D12UploadBufferPool>
transfer_vertex_buffer_pool_;
ID3D12RootSignature* transfer_root_signatures_[size_t(
TransferRootSignatureIndex::kCount)] = {};
std::unordered_map<TransferShaderKey, ID3D12PipelineState*,
TransferShaderKey::Hasher>
transfer_pipelines_;
std::unordered_map<TransferShaderKey, std::array<ID3D12PipelineState*, 8>,
TransferShaderKey::Hasher>
transfer_stencil_bit_pipelines_;
// Temporary storage for PerformTransfersAndResolveClears.
std::vector<TransferInvocation> current_transfer_invocations_;
// Temporary storage for DumpRenderTargets.
std::vector<ResolveCopyDumpRectangle> dump_rectangles_;
std::vector<DumpInvocation> dump_invocations_;
ID3D12RootSignature* dump_root_signature_color_ = nullptr;
ID3D12RootSignature* dump_root_signature_depth_ = nullptr;
// Compute pipelines for copying host render target contents to the EDRAM
// buffer. May be null if failed to create.
std::unordered_map<DumpPipelineKey, ID3D12PipelineState*,
DumpPipelineKey::Hasher>
dump_pipelines_;
// Parameter 0 - 2 root constants (red, green).
ID3D12RootSignature* uint32_rtv_clear_root_signature_ = nullptr;
// [32 or 32_32][MSAA samples].
ID3D12PipelineState*
uint32_rtv_clear_pipelines_[2][size_t(xenos::MsaaSamples::k4X) + 1] = {};
std::vector<Transfer> clear_transfers_[2];
// Temporary storage for DXBC building.
std::vector<uint32_t> built_shader_;
// For rasterizer-ordered view (pixel shader interlock).
ID3D12RootSignature* resolve_rov_clear_root_signature_ = nullptr;
// Clearing 32bpp color or depth.
ID3D12PipelineState* resolve_rov_clear_32bpp_pipeline_ = nullptr;
// Clearing 64bpp color.
ID3D12PipelineState* resolve_rov_clear_64bpp_pipeline_ = nullptr;
};
} // namespace d3d12
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_D3D12_D3D12_RENDER_TARGET_CACHE_H_