[GPU/D3D12] Memexport from anywhere in control flow + 8/16bpp memexport
There's no limit on the number of memory exports in a shader on the real Xenos, and exports can be done anywhere, including in loops. Now, instead of deferring the exports to the end of the shader, and assuming that export allocs are executed only once, Xenia flushes exports when it reaches an alloc (allocs terminate memory exports on Xenos, as well as individual ALU instructions with `serialize`, but not handling this case for simplicity, it's only truly mandatory to flush memory exports before starting a new one), the end of the shader, or a pixel with outstanding exports is killed. To know which eM# registers need to be flushed to the memory, traversing the successors of each exec potentially writing any eM#, and specifying that certain eM# registers might have potentially been written before each reached control flow instruction, until a flush point or the end of the shader is reached. Also, some games export to sub-32bpp formats. These are now supported via atomic AND clearing the bits of the dword to replace followed by an atomic OR inserting the new byte/short.
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
#include "xenia/base/string_buffer.h"
|
||||
#include "xenia/gpu/dxbc.h"
|
||||
#include "xenia/gpu/shader_translator.h"
|
||||
#include "xenia/gpu/ucode.h"
|
||||
#include "xenia/ui/graphics_provider.h"
|
||||
|
||||
namespace xe {
|
||||
@@ -589,13 +590,16 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
void ProcessLoopEndInstruction(
|
||||
const ParsedLoopEndInstruction& instr) override;
|
||||
void ProcessJumpInstruction(const ParsedJumpInstruction& instr) override;
|
||||
void ProcessAllocInstruction(const ParsedAllocInstruction& instr) override;
|
||||
void ProcessAllocInstruction(const ParsedAllocInstruction& instr,
|
||||
uint8_t export_eM) override;
|
||||
|
||||
void ProcessVertexFetchInstruction(
|
||||
const ParsedVertexFetchInstruction& instr) override;
|
||||
void ProcessTextureFetchInstruction(
|
||||
const ParsedTextureFetchInstruction& instr) override;
|
||||
void ProcessAluInstruction(const ParsedAluInstruction& instr) override;
|
||||
void ProcessAluInstruction(
|
||||
const ParsedAluInstruction& instr,
|
||||
uint8_t memexport_eM_potentially_written_before) override;
|
||||
|
||||
private:
|
||||
// IF ANY OF THESE ARE CHANGED, WriteInputSignature and WriteOutputSignature
|
||||
@@ -674,6 +678,11 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
// Frees the last allocated internal r# registers for later reuse.
|
||||
void PopSystemTemp(uint32_t count = 1);
|
||||
|
||||
// ExportToMemory modifies the values of eA/eM# for simplicity, call only
|
||||
// before starting a new export or ending the invocation or making it
|
||||
// inactive.
|
||||
void ExportToMemory(uint8_t export_eM);
|
||||
|
||||
// Converts one scalar from piecewise linear gamma to linear. The target may
|
||||
// be the same as the source, the temporary variables must be different. If
|
||||
// the source is not pre-saturated, saturation will be done internally.
|
||||
@@ -728,7 +737,7 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
bool ROV_IsDepthStencilEarly() const {
|
||||
assert_true(edram_rov_used_);
|
||||
return !is_depth_only_pixel_shader_ && !current_shader().writes_depth() &&
|
||||
!current_shader().is_valid_memexport_used();
|
||||
!current_shader().memexport_eM_written();
|
||||
}
|
||||
// Converts the pre-clamped depth value to 24-bit (storing the result in bits
|
||||
// 0:23 and zeros in 24:31, not creating room for stencil - since this may be
|
||||
@@ -787,14 +796,6 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
void StartPixelShader_LoadROVParameters();
|
||||
void StartPixelShader();
|
||||
|
||||
// Writing the epilogue.
|
||||
// ExportToMemory modifies the values of eA/eM# for simplicity, don't call
|
||||
// multiple times.
|
||||
void ExportToMemory_PackFixed32(const uint32_t* eM_temps, uint32_t eM_count,
|
||||
const uint32_t bits[4],
|
||||
const dxbc::Src& is_integer,
|
||||
const dxbc::Src& is_signed);
|
||||
void ExportToMemory();
|
||||
void CompleteVertexOrDomainShader();
|
||||
// For RTV, adds the sample to coverage_temp.coverage_temp_component if it
|
||||
// passes alpha to mask (or, if initialize == true (for the first sample
|
||||
@@ -917,13 +918,16 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
.SelectFromSwizzled(word_index & 1);
|
||||
}
|
||||
|
||||
void KillPixel(bool condition, const dxbc::Src& condition_src);
|
||||
void KillPixel(bool condition, const dxbc::Src& condition_src,
|
||||
uint8_t memexport_eM_potentially_written_before);
|
||||
|
||||
void ProcessVectorAluOperation(const ParsedAluInstruction& instr,
|
||||
uint32_t& result_swizzle,
|
||||
bool& predicate_written);
|
||||
void ProcessScalarAluOperation(const ParsedAluInstruction& instr,
|
||||
bool& predicate_written);
|
||||
void ProcessVectorAluOperation(
|
||||
const ParsedAluInstruction& instr,
|
||||
uint8_t memexport_eM_potentially_written_before, uint32_t& result_swizzle,
|
||||
bool& predicate_written);
|
||||
void ProcessScalarAluOperation(
|
||||
const ParsedAluInstruction& instr,
|
||||
uint8_t memexport_eM_potentially_written_before, bool& predicate_written);
|
||||
|
||||
void WriteResourceDefinition();
|
||||
void WriteInputSignature();
|
||||
@@ -1124,14 +1128,16 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
// writing).
|
||||
uint32_t system_temps_color_[4];
|
||||
|
||||
// Bits containing whether each eM# has been written, for up to 16 streams, or
|
||||
// UINT32_MAX if memexport is not used. 8 bits (5 used) for each stream, with
|
||||
// 4 `alloc export`s per component.
|
||||
uint32_t system_temp_memexport_written_;
|
||||
// eA in each `alloc export`, or UINT32_MAX if not used.
|
||||
uint32_t system_temps_memexport_address_[Shader::kMaxMemExports];
|
||||
// eM# in each `alloc export`, or UINT32_MAX if not used.
|
||||
uint32_t system_temps_memexport_data_[Shader::kMaxMemExports][5];
|
||||
// Memory export temporary registers are allocated if the shader writes any
|
||||
// eM# (current_shader().memexport_eM_written() != 0).
|
||||
// X - whether memexport is enabled for this invocation.
|
||||
// Y - which eM# elements have been written so far by the invocation since the
|
||||
// last memory write.
|
||||
uint32_t system_temp_memexport_enabled_and_eM_written_;
|
||||
// eA.
|
||||
uint32_t system_temp_memexport_address_;
|
||||
// eM#.
|
||||
uint32_t system_temps_memexport_data_[ucode::kMaxMemExportElementCount];
|
||||
|
||||
// Vector ALU or fetch result / scratch (since Xenos write masks can contain
|
||||
// swizzles).
|
||||
@@ -1195,10 +1201,6 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
uint32_t uav_index_edram_;
|
||||
|
||||
std::vector<SamplerBinding> sampler_bindings_;
|
||||
|
||||
// Number of `alloc export`s encountered so far in the translation. The index
|
||||
// of the current eA/eM# temp register set is this minus 1, if it's not 0.
|
||||
uint32_t memexport_alloc_current_count_;
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
Reference in New Issue
Block a user