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.
1210 lines
52 KiB
C++
1210 lines
52 KiB
C++
/**
|
|
******************************************************************************
|
|
* Xenia : Xbox 360 Emulator Research Project *
|
|
******************************************************************************
|
|
* Copyright 2022 Ben Vanik. All rights reserved. *
|
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
******************************************************************************
|
|
*/
|
|
|
|
#ifndef XENIA_GPU_DXBC_SHADER_TRANSLATOR_H_
|
|
#define XENIA_GPU_DXBC_SHADER_TRANSLATOR_H_
|
|
|
|
#include <cstddef>
|
|
#include <cstring>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "xenia/base/assert.h"
|
|
#include "xenia/base/math.h"
|
|
#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 {
|
|
namespace gpu {
|
|
|
|
// Generates shader model 5_1 byte code (for Direct3D 12).
|
|
//
|
|
// IMPORTANT CONTRIBUTION NOTES:
|
|
//
|
|
// While DXBC may look like a flexible and high-level representation with highly
|
|
// generalized building blocks, actually it has a lot of restrictions on operand
|
|
// usage!
|
|
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
// !!!DO NOT ADD ANYTHING FXC THAT WOULD NOT PRODUCE!!!
|
|
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
// Before adding any sequence that you haven't seen in Xenia, try writing
|
|
// equivalent code in HLSL and running it through FXC, try with /Od, try with
|
|
// full optimization, but if you see that FXC follows a different pattern than
|
|
// what you are expecting, do what FXC does!!!
|
|
// SEE THE NOTES DXBC.H BEFORE WRITING ANYTHING RELATED TO DXBC!
|
|
class DxbcShaderTranslator : public ShaderTranslator {
|
|
public:
|
|
DxbcShaderTranslator(ui::GraphicsProvider::GpuVendorID vendor_id,
|
|
bool bindless_resources_used, bool edram_rov_used,
|
|
bool gamma_render_target_as_srgb = false,
|
|
bool msaa_2x_supported = true,
|
|
uint32_t draw_resolution_scale_x = 1,
|
|
uint32_t draw_resolution_scale_y = 1,
|
|
bool force_emit_source_map = false);
|
|
~DxbcShaderTranslator() override;
|
|
|
|
// Stage linkage ordering and rules (must be respected not only within the
|
|
// DxbcShaderTranslator, but also by everything else between the VS and the
|
|
// PS, such as geometry shaders for primitive types, and built-in pixel
|
|
// shaders for processing the fragment depth when there's no guest pixel
|
|
// shader):
|
|
//
|
|
// Note that VS means the guest VS here - can be VS or DS on the host. RS
|
|
// means the fixed-function rasterizer.
|
|
//
|
|
// The beginning of the parameters must match between the output of the
|
|
// producing stage and the input of the consuming stage, while the tail can be
|
|
// stage-specific or cut off.
|
|
//
|
|
// - Interpolators (TEXCOORD) - VS > GS > RS > PS, used interpolators are all
|
|
// unconditionally referenced in all these stages.
|
|
// - Point coordinates (XESPRITETEXCOORD) - GS > RS > PS, must be present in
|
|
// none or in all, if drawing points, and PsParamGen is used.
|
|
// - Position (SV_Position) - VS > GS > RS > PS, used in PS if actually needed
|
|
// for something (PsParamGen, alpha to coverage when oC0 is written, depth
|
|
// conversion, ROV render backend), the presence in PS depends on the usage
|
|
// within the PS, not on linkage, therefore it's the last in PS so it can be
|
|
// dropped from PS without effect on linkage.
|
|
// - Clip distances (SV_ClipDistance) - VS > GS > RS.
|
|
// - Cull distances (SV_CullDistance) - VS > RS or VS > GS.
|
|
// - Vertex kill AND operator (SV_CullDistance) - VS > RS or VS > GS.
|
|
// - Point size (XEPSIZE) - VS > GS.
|
|
//
|
|
// Therefore, for the direct VS > PS path, the parameters may be the
|
|
// following:
|
|
// - Shared between VS and PS:
|
|
// - Interpolators (TEXCOORD).
|
|
// - Position (SV_Position).
|
|
// - VS output only:
|
|
// - Clip distances (SV_ClipDistance).
|
|
// - Cull distances (SV_CullDistance).
|
|
// - Vertex kill AND operator (SV_CullDistance).
|
|
//
|
|
// When a GS is also used, the path between the VS and the GS is:
|
|
// - Shared between VS and GS:
|
|
// - Interpolators (TEXCOORD).
|
|
// - Position (SV_Position).
|
|
// - Clip distances (SV_ClipDistance).
|
|
// - Cull distances (SV_CullDistance).
|
|
// - Vertex kill AND operator (SV_CullDistance).
|
|
// - Point size (XEPSIZE).
|
|
//
|
|
// Then, between GS and PS, it's:
|
|
// - Shared between GS and PS:
|
|
// - Interpolators (TEXCOORD).
|
|
// - Point coordinates (XESPRITETEXCOORD).
|
|
// - Position (SV_Position).
|
|
// - GS output only:
|
|
// - Clip distances (SV_ClipDistance).
|
|
|
|
union Modification {
|
|
// If anything in this is structure is changed in a way not compatible with
|
|
// the previous layout, invalidate the pipeline storages by increasing this
|
|
// version number (0xYYYYMMDD)!
|
|
static constexpr uint32_t kVersion = 0x20220720;
|
|
|
|
enum class DepthStencilMode : uint32_t {
|
|
kNoModifiers,
|
|
// [earlydepthstencil] - enable if alpha test and alpha to coverage are
|
|
// disabled; ignored if anything in the shader blocks early Z writing.
|
|
kEarlyHint,
|
|
// Converting the depth to the closest 32-bit float representable exactly
|
|
// as a 20e4 float, to support invariance in cases when the guest
|
|
// reuploads a previously resolved depth buffer to the EDRAM, rounding
|
|
// towards zero (which contradicts the rounding used by the Direct3D 9
|
|
// reference rasterizer, but allows SV_DepthLessEqual to be used to allow
|
|
// slightly coarse early Z culling; also truncating regardless of whether
|
|
// the shader writes depth and thus always uses SV_Depth, for
|
|
// consistency). MSAA is limited - depth must be per-sample
|
|
// (SV_DepthLessEqual also explicitly requires sample or centroid position
|
|
// interpolation), thus the sampler has to run at sample frequency even if
|
|
// the device supports stencil loading and thus true non-ROV MSAA via
|
|
// SV_StencilRef.
|
|
// Fixed-function viewport depth bounds must be snapped to float24 for
|
|
// clamping purposes.
|
|
kFloat24Truncating,
|
|
// Similar to kFloat24Truncating, but rounding to the nearest even,
|
|
// however, always using SV_Depth rather than SV_DepthLessEqual because
|
|
// rounding up results in a bigger value. Same viewport usage rules apply.
|
|
kFloat24Rounding,
|
|
};
|
|
|
|
uint64_t value;
|
|
struct VertexShaderModification {
|
|
// uint32_t 0.
|
|
// Interpolators written by the vertex shader and needed by the pixel
|
|
// shader.
|
|
uint32_t interpolator_mask : xenos::kMaxInterpolators;
|
|
uint32_t user_clip_plane_count : 3;
|
|
uint32_t user_clip_plane_cull : 1;
|
|
// Whether vertex killing with the "and" operator is used, and one more
|
|
// SV_CullDistance needs to be written.
|
|
uint32_t vertex_kill_and : 1;
|
|
uint32_t output_point_size : 1;
|
|
// Dynamically indexable register count from SQ_PROGRAM_CNTL.
|
|
uint32_t dynamic_addressable_register_count : 8;
|
|
uint32_t : 2;
|
|
// uint32_t 1.
|
|
// Pipeline stage and input configuration.
|
|
Shader::HostVertexShaderType host_vertex_shader_type
|
|
: Shader::kHostVertexShaderTypeBitCount;
|
|
} vertex;
|
|
struct PixelShaderModification {
|
|
// uint32_t 0.
|
|
// Interpolators written by the vertex shader and needed by the pixel
|
|
// shader.
|
|
uint32_t interpolator_mask : xenos::kMaxInterpolators;
|
|
uint32_t interpolators_centroid : xenos::kMaxInterpolators;
|
|
// uint32_t 1.
|
|
// Dynamically indexable register count from SQ_PROGRAM_CNTL.
|
|
uint32_t param_gen_enable : 1;
|
|
uint32_t param_gen_interpolator : 4;
|
|
// If param_gen_enable is set, this must be set for point primitives, and
|
|
// must not be set for other primitive types - enables the point sprite
|
|
// coordinates input, and also effects the flag bits in PsParamGen.
|
|
uint32_t param_gen_point : 1;
|
|
uint32_t dynamic_addressable_register_count : 8;
|
|
// Non-ROV - depth / stencil output mode.
|
|
DepthStencilMode depth_stencil_mode : 2;
|
|
} pixel;
|
|
|
|
explicit Modification(uint64_t modification_value = 0)
|
|
: value(modification_value) {
|
|
static_assert_size(*this, sizeof(value));
|
|
}
|
|
|
|
uint32_t GetVertexClipDistanceCount() const {
|
|
return vertex.user_clip_plane_cull ? 0 : vertex.user_clip_plane_count;
|
|
}
|
|
uint32_t GetVertexCullDistanceCount() const {
|
|
return (vertex.user_clip_plane_cull ? vertex.user_clip_plane_count : 0) +
|
|
vertex.vertex_kill_and;
|
|
}
|
|
};
|
|
|
|
// Constant buffer bindings in space 0.
|
|
enum class CbufferRegister {
|
|
kSystemConstants,
|
|
kFloatConstants,
|
|
kBoolLoopConstants,
|
|
kFetchConstants,
|
|
kDescriptorIndices,
|
|
};
|
|
|
|
// Some are referenced in xenos_draw.hlsli - check it too when updating!
|
|
enum : uint32_t {
|
|
kSysFlag_SharedMemoryIsUAV_Shift,
|
|
kSysFlag_XYDividedByW_Shift,
|
|
kSysFlag_ZDividedByW_Shift,
|
|
kSysFlag_WNotReciprocal_Shift,
|
|
kSysFlag_PrimitivePolygonal_Shift,
|
|
kSysFlag_PrimitiveLine_Shift,
|
|
kSysFlag_DepthFloat24_Shift,
|
|
kSysFlag_AlphaPassIfLess_Shift,
|
|
kSysFlag_AlphaPassIfEqual_Shift,
|
|
kSysFlag_AlphaPassIfGreater_Shift,
|
|
kSysFlag_ConvertColor0ToGamma_Shift,
|
|
kSysFlag_ConvertColor1ToGamma_Shift,
|
|
kSysFlag_ConvertColor2ToGamma_Shift,
|
|
kSysFlag_ConvertColor3ToGamma_Shift,
|
|
|
|
kSysFlag_ROVDepthStencil_Shift,
|
|
kSysFlag_ROVDepthPassIfLess_Shift,
|
|
kSysFlag_ROVDepthPassIfEqual_Shift,
|
|
kSysFlag_ROVDepthPassIfGreater_Shift,
|
|
// 1 to write new depth to the depth buffer, 0 to keep the old one if the
|
|
// depth test passes.
|
|
kSysFlag_ROVDepthWrite_Shift,
|
|
kSysFlag_ROVStencilTest_Shift,
|
|
// If the depth / stencil test has failed, but resulted in a stencil value
|
|
// that is different than the one currently in the depth buffer, write it
|
|
// anyway and don't run the rest of the shader (to check if the sample may
|
|
// be discarded some way) - use when alpha test and alpha to coverage are
|
|
// disabled. Ignored by the shader if not applicable to it (like if it has
|
|
// kill instructions or writes the depth output).
|
|
// TODO(Triang3l): Investigate replacement with an alpha-to-mask flag,
|
|
// checking `(flags & (alpha test | alpha to mask)) == (always | disabled)`,
|
|
// taking into account the potential relation with occlusion queries (but
|
|
// should be safe at least temporarily).
|
|
kSysFlag_ROVDepthStencilEarlyWrite_Shift,
|
|
|
|
kSysFlag_Count,
|
|
|
|
kSysFlag_SharedMemoryIsUAV = 1u << kSysFlag_SharedMemoryIsUAV_Shift,
|
|
kSysFlag_XYDividedByW = 1u << kSysFlag_XYDividedByW_Shift,
|
|
kSysFlag_ZDividedByW = 1u << kSysFlag_ZDividedByW_Shift,
|
|
kSysFlag_WNotReciprocal = 1u << kSysFlag_WNotReciprocal_Shift,
|
|
kSysFlag_PrimitivePolygonal = 1u << kSysFlag_PrimitivePolygonal_Shift,
|
|
kSysFlag_PrimitiveLine = 1u << kSysFlag_PrimitiveLine_Shift,
|
|
kSysFlag_DepthFloat24 = 1u << kSysFlag_DepthFloat24_Shift,
|
|
kSysFlag_AlphaPassIfLess = 1u << kSysFlag_AlphaPassIfLess_Shift,
|
|
kSysFlag_AlphaPassIfEqual = 1u << kSysFlag_AlphaPassIfEqual_Shift,
|
|
kSysFlag_AlphaPassIfGreater = 1u << kSysFlag_AlphaPassIfGreater_Shift,
|
|
kSysFlag_ConvertColor0ToGamma = 1u << kSysFlag_ConvertColor0ToGamma_Shift,
|
|
kSysFlag_ConvertColor1ToGamma = 1u << kSysFlag_ConvertColor1ToGamma_Shift,
|
|
kSysFlag_ConvertColor2ToGamma = 1u << kSysFlag_ConvertColor2ToGamma_Shift,
|
|
kSysFlag_ConvertColor3ToGamma = 1u << kSysFlag_ConvertColor3ToGamma_Shift,
|
|
kSysFlag_ROVDepthStencil = 1u << kSysFlag_ROVDepthStencil_Shift,
|
|
kSysFlag_ROVDepthPassIfLess = 1u << kSysFlag_ROVDepthPassIfLess_Shift,
|
|
kSysFlag_ROVDepthPassIfEqual = 1u << kSysFlag_ROVDepthPassIfEqual_Shift,
|
|
kSysFlag_ROVDepthPassIfGreater = 1u << kSysFlag_ROVDepthPassIfGreater_Shift,
|
|
kSysFlag_ROVDepthWrite = 1u << kSysFlag_ROVDepthWrite_Shift,
|
|
kSysFlag_ROVStencilTest = 1u << kSysFlag_ROVStencilTest_Shift,
|
|
kSysFlag_ROVDepthStencilEarlyWrite =
|
|
1u << kSysFlag_ROVDepthStencilEarlyWrite_Shift,
|
|
};
|
|
static_assert(kSysFlag_Count <= 32, "Too many flags in the system constants");
|
|
|
|
// IF SYSTEM CONSTANTS ARE CHANGED OR ADDED, THE FOLLOWING MUST BE UPDATED:
|
|
// - SystemConstants::Index enum.
|
|
// - system_constant_rdef_.
|
|
// - d3d12/shaders/xenos_draw.hlsli (for geometry shaders).
|
|
struct SystemConstants {
|
|
uint32_t flags;
|
|
union {
|
|
struct {
|
|
float tessellation_factor_range_min;
|
|
float tessellation_factor_range_max;
|
|
};
|
|
float tessellation_factor_range[2];
|
|
};
|
|
uint32_t line_loop_closing_index;
|
|
|
|
xenos::Endian vertex_index_endian;
|
|
uint32_t vertex_index_offset;
|
|
union {
|
|
struct {
|
|
uint32_t vertex_index_min;
|
|
uint32_t vertex_index_max;
|
|
};
|
|
uint32_t vertex_index_min_max[2];
|
|
};
|
|
|
|
float user_clip_planes[6][4];
|
|
|
|
float ndc_scale[3];
|
|
float point_vertex_diameter_min;
|
|
|
|
float ndc_offset[3];
|
|
float point_vertex_diameter_max;
|
|
|
|
float point_constant_diameter[2];
|
|
// Diameter in guest screen coordinates > radius (0.5 * diameter) in the NDC
|
|
// for the host viewport.
|
|
float point_screen_diameter_to_ndc_radius[2];
|
|
|
|
// Each byte contains post-swizzle TextureSign values for each of the needed
|
|
// components of each of the 32 used texture fetch constants.
|
|
uint32_t texture_swizzled_signs[8];
|
|
|
|
// Whether the contents of each texture in fetch constants comes from a
|
|
// resolve operation.
|
|
uint32_t textures_resolved;
|
|
// Log2 of X and Y sample size. Used for alpha to mask, and for MSAA with
|
|
// ROV, this is used for EDRAM address calculation.
|
|
uint32_t sample_count_log2[2];
|
|
float alpha_test_reference;
|
|
|
|
// If alpha to mask is disabled, the entire alpha_to_mask value must be 0.
|
|
// If alpha to mask is enabled, bits 0:7 are sample offsets, and bit 8 must
|
|
// be 1.
|
|
uint32_t alpha_to_mask;
|
|
uint32_t edram_32bpp_tile_pitch_dwords_scaled;
|
|
uint32_t edram_depth_base_dwords_scaled;
|
|
uint32_t padding_edram_depth_base_dwords_scaled;
|
|
|
|
float color_exp_bias[4];
|
|
|
|
union {
|
|
struct {
|
|
float edram_poly_offset_front_scale;
|
|
float edram_poly_offset_front_offset;
|
|
};
|
|
float edram_poly_offset_front[2];
|
|
};
|
|
union {
|
|
struct {
|
|
float edram_poly_offset_back_scale;
|
|
float edram_poly_offset_back_offset;
|
|
};
|
|
float edram_poly_offset_back[2];
|
|
};
|
|
|
|
// In stencil function/operations (they match the layout of the
|
|
// function/operations in RB_DEPTHCONTROL):
|
|
// 0:2 - comparison function (bit 0 - less, bit 1 - equal, bit 2 - greater).
|
|
// 3:5 - fail operation.
|
|
// 6:8 - pass operation.
|
|
// 9:11 - depth fail operation.
|
|
|
|
union {
|
|
struct {
|
|
uint32_t edram_stencil_front_reference;
|
|
uint32_t edram_stencil_front_read_mask;
|
|
uint32_t edram_stencil_front_write_mask;
|
|
uint32_t edram_stencil_front_func_ops;
|
|
|
|
uint32_t edram_stencil_back_reference;
|
|
uint32_t edram_stencil_back_read_mask;
|
|
uint32_t edram_stencil_back_write_mask;
|
|
uint32_t edram_stencil_back_func_ops;
|
|
};
|
|
struct {
|
|
uint32_t edram_stencil_front[4];
|
|
uint32_t edram_stencil_back[4];
|
|
};
|
|
uint32_t edram_stencil[2][4];
|
|
};
|
|
|
|
uint32_t edram_rt_base_dwords_scaled[4];
|
|
|
|
// RT format combined with RenderTargetCache::kPSIColorFormatFlag values
|
|
// (pass via RenderTargetCache::AddPSIColorFormatFlags).
|
|
uint32_t edram_rt_format_flags[4];
|
|
|
|
// Format info - values to clamp the color to before blending or storing.
|
|
// Low color, low alpha, high color, high alpha.
|
|
float edram_rt_clamp[4][4];
|
|
|
|
// Format info - mask to apply to the old packed RT data, and to apply as
|
|
// inverted to the new packed data, before storing (more or less the inverse
|
|
// of the write mask packed like render target channels). This can be used
|
|
// to bypass unpacking if blending is not used. If 0 and not blending,
|
|
// reading the old data from the EDRAM buffer is not required.
|
|
uint32_t edram_rt_keep_mask[4][2];
|
|
|
|
// Render target blending options - RB_BLENDCONTROL, with only the relevant
|
|
// options (factors and operations - AND 0x1FFF1FFF). If 0x00010001
|
|
// (1 * src + 0 * dst), blending is disabled for the render target.
|
|
uint32_t edram_rt_blend_factors_ops[4];
|
|
|
|
// The constant blend factor for the respective modes.
|
|
float edram_blend_constant[4];
|
|
|
|
private:
|
|
friend class DxbcShaderTranslator;
|
|
|
|
enum class Index : uint32_t {
|
|
kFlags,
|
|
kTessellationFactorRange,
|
|
kLineLoopClosingIndex,
|
|
|
|
kVertexIndexEndian,
|
|
kVertexIndexOffset,
|
|
kVertexIndexMinMax,
|
|
|
|
kUserClipPlanes,
|
|
|
|
kNDCScale,
|
|
kPointVertexDiameterMin,
|
|
|
|
kNDCOffset,
|
|
kPointVertexDiameterMax,
|
|
|
|
kPointConstantDiameter,
|
|
kPointScreenDiameterToNDCRadius,
|
|
|
|
kTextureSwizzledSigns,
|
|
|
|
kTexturesResolved,
|
|
kSampleCountLog2,
|
|
kAlphaTestReference,
|
|
|
|
kAlphaToMask,
|
|
kEdram32bppTilePitchDwordsScaled,
|
|
kEdramDepthBaseDwordsScaled,
|
|
|
|
kColorExpBias,
|
|
|
|
kEdramPolyOffsetFront,
|
|
kEdramPolyOffsetBack,
|
|
|
|
kEdramStencil,
|
|
|
|
kEdramRTBaseDwordsScaled,
|
|
|
|
kEdramRTFormatFlags,
|
|
|
|
kEdramRTClamp,
|
|
|
|
kEdramRTKeepMask,
|
|
|
|
kEdramRTBlendFactorsOps,
|
|
|
|
kEdramBlendConstant,
|
|
|
|
kCount,
|
|
};
|
|
static_assert(
|
|
uint32_t(Index::kCount) <= 64,
|
|
"Too many system constants, can't use uint64_t for usage bits");
|
|
};
|
|
|
|
// Shader resource view binding spaces.
|
|
enum class SRVSpace {
|
|
// SRVMainSpaceRegister t# layout.
|
|
kMain,
|
|
kBindlessTextures2DArray,
|
|
kBindlessTextures3D,
|
|
kBindlessTexturesCube,
|
|
};
|
|
|
|
// Shader resource view bindings in SRVSpace::kMain.
|
|
enum class SRVMainRegister {
|
|
kSharedMemory,
|
|
kBindfulTexturesStart,
|
|
};
|
|
|
|
// 192 textures at most because there are 32 fetch constants, and textures can
|
|
// be 2D array, 3D or cube, and also signed and unsigned.
|
|
static constexpr uint32_t kMaxTextureBindingIndexBits = 8;
|
|
static constexpr uint32_t kMaxTextureBindings =
|
|
(1 << kMaxTextureBindingIndexBits) - 1;
|
|
struct TextureBinding {
|
|
uint32_t bindful_srv_index;
|
|
// Temporary for WriteResourceDefinition.
|
|
uint32_t bindful_srv_rdef_name_ptr;
|
|
uint32_t bindless_descriptor_index;
|
|
uint32_t fetch_constant;
|
|
// Stacked and 3D are separate TextureBindings, even for bindless for null
|
|
// descriptor handling simplicity.
|
|
xenos::FetchOpDimension dimension;
|
|
bool is_signed;
|
|
std::string bindful_name;
|
|
};
|
|
|
|
// Arbitrary limit - there can't be more than 2048 in a shader-visible
|
|
// descriptor heap, though some older hardware (tier 1 resource binding -
|
|
// Nvidia Fermi) doesn't support more than 16 samplers bound at once (we can't
|
|
// really do anything if a game uses more than 16), but just to have some
|
|
// limit so sampler count can easily be packed into 32-bit map keys (for
|
|
// instance, for root signatures). But shaders can specify overrides for
|
|
// filtering modes, and the number of possible combinations is huge - let's
|
|
// limit it to something sane.
|
|
static constexpr uint32_t kMaxSamplerBindingIndexBits = 7;
|
|
static constexpr uint32_t kMaxSamplerBindings =
|
|
(1 << kMaxSamplerBindingIndexBits) - 1;
|
|
struct SamplerBinding {
|
|
uint32_t bindless_descriptor_index;
|
|
uint32_t fetch_constant;
|
|
xenos::TextureFilter mag_filter;
|
|
xenos::TextureFilter min_filter;
|
|
xenos::TextureFilter mip_filter;
|
|
xenos::AnisoFilter aniso_filter;
|
|
std::string bindful_name;
|
|
};
|
|
|
|
// Unordered access view bindings in space 0.
|
|
enum class UAVRegister {
|
|
kSharedMemory,
|
|
kEdram,
|
|
};
|
|
|
|
uint64_t GetDefaultVertexShaderModification(
|
|
uint32_t dynamic_addressable_register_count,
|
|
Shader::HostVertexShaderType host_vertex_shader_type =
|
|
Shader::HostVertexShaderType::kVertex) const override;
|
|
uint64_t GetDefaultPixelShaderModification(
|
|
uint32_t dynamic_addressable_register_count) const override;
|
|
|
|
// Creates a special pixel shader without color outputs - this resets the
|
|
// state of the translator.
|
|
std::vector<uint8_t> CreateDepthOnlyPixelShader();
|
|
|
|
// Common functions useful not only for the translator, but also for render
|
|
// target reinterpretation.
|
|
|
|
// Converts the color value externally clamped to [0, 31.875] to 7e3 floating
|
|
// point, with zeros in bits 10:31, rounding to the nearest even. Source and
|
|
// destination may be the same, temporary must be different than both.
|
|
static void PreClampedFloat32To7e3(dxbc::Assembler& a, uint32_t f10_temp,
|
|
uint32_t f10_temp_component,
|
|
uint32_t f32_temp,
|
|
uint32_t f32_temp_component,
|
|
uint32_t temp_temp,
|
|
uint32_t temp_temp_component);
|
|
// Same as PreClampedFloat32To7e3, but clamps the input to [0, 31.875].
|
|
static void UnclampedFloat32To7e3(dxbc::Assembler& a, uint32_t f10_temp,
|
|
uint32_t f10_temp_component,
|
|
uint32_t f32_temp,
|
|
uint32_t f32_temp_component,
|
|
uint32_t temp_temp,
|
|
uint32_t temp_temp_component);
|
|
// Converts the 7e3 number in bits [f10_shift, f10_shift + 10) to a 32-bit
|
|
// float. Two temporaries must be different, but one can be the same as the
|
|
// source. The destination may be anything writable.
|
|
static void Float7e3To32(dxbc::Assembler& a, const dxbc::Dest& f32,
|
|
uint32_t f10_temp, uint32_t f10_temp_component,
|
|
uint32_t f10_shift, uint32_t temp1_temp,
|
|
uint32_t temp1_temp_component, uint32_t temp2_temp,
|
|
uint32_t temp2_temp_component);
|
|
// Converts the depth value externally clamped to the representable [0, 2)
|
|
// range to 20e4 floating point, with zeros in bits 24:31, rounding to the
|
|
// nearest even or towards zero. Source and destination may be the same,
|
|
// temporary must be different than both. If remap_from_0_to_0_5 is true, it's
|
|
// assumed that 0...1 is pre-remapped to 0...0.5 in the input.
|
|
static void PreClampedDepthTo20e4(
|
|
dxbc::Assembler& a, uint32_t f24_temp, uint32_t f24_temp_component,
|
|
uint32_t f32_temp, uint32_t f32_temp_component, uint32_t temp_temp,
|
|
uint32_t temp_temp_component, bool round_to_nearest_even,
|
|
bool remap_from_0_to_0_5);
|
|
// Converts the 20e4 number in bits [f24_shift, f24_shift + 10) to a 32-bit
|
|
// float. Two temporaries must be different, but one can be the same as the
|
|
// source. The destination may be anything writable. If remap_to_0_to_0_5 is
|
|
// true, 0...1 in float24 will be remaped to 0...0.5 in float32.
|
|
static void Depth20e4To32(dxbc::Assembler& a, const dxbc::Dest& f32,
|
|
uint32_t f24_temp, uint32_t f24_temp_component,
|
|
uint32_t f24_shift, uint32_t temp1_temp,
|
|
uint32_t temp1_temp_component, uint32_t temp2_temp,
|
|
uint32_t temp2_temp_component,
|
|
bool remap_to_0_to_0_5);
|
|
|
|
protected:
|
|
void Reset() override;
|
|
|
|
uint32_t GetModificationRegisterCount() const override;
|
|
|
|
void StartTranslation() override;
|
|
std::vector<uint8_t> CompleteTranslation() override;
|
|
void PostTranslation() override;
|
|
|
|
void ProcessLabel(uint32_t cf_index) override;
|
|
|
|
void ProcessExecInstructionBegin(const ParsedExecInstruction& instr) override;
|
|
void ProcessExecInstructionEnd(const ParsedExecInstruction& instr) override;
|
|
void ProcessLoopStartInstruction(
|
|
const ParsedLoopStartInstruction& instr) override;
|
|
void ProcessLoopEndInstruction(
|
|
const ParsedLoopEndInstruction& instr) override;
|
|
void ProcessJumpInstruction(const ParsedJumpInstruction& 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,
|
|
uint8_t memexport_eM_potentially_written_before) override;
|
|
|
|
private:
|
|
// IF ANY OF THESE ARE CHANGED, WriteInputSignature and WriteOutputSignature
|
|
// MUST BE UPDATED!
|
|
static constexpr uint32_t kInRegisterVSVertexIndex = 0;
|
|
static constexpr uint32_t kInRegisterDSControlPointIndex = 0;
|
|
|
|
// GetSystemConstantSrc + MarkSystemConstantUsed is for special cases of
|
|
// building the source unconditionally - in general, LoadSystemConstant must
|
|
// be used instead.
|
|
void MarkSystemConstantUsed(SystemConstants::Index index) {
|
|
system_constants_used_ |= uint64_t(1) << uint32_t(index);
|
|
}
|
|
// Offset should be offsetof(SystemConstants, field). Swizzle values are
|
|
// relative to the first component in the vector according to offsetof - to
|
|
// request a scalar, use XXXX swizzle, and if it's at +4 in its 16-byte
|
|
// vector, it will be turned into YYYY, and so on. The swizzle may include
|
|
// out-of-bounds components of the vector for simplicity of use, assuming they
|
|
// will be dropped anyway later.
|
|
dxbc::Src GetSystemConstantSrc(size_t offset, uint32_t swizzle) const {
|
|
uint32_t first_component = uint32_t((offset >> 2) & 3);
|
|
return dxbc::Src::CB(
|
|
cbuffer_index_system_constants_,
|
|
uint32_t(CbufferRegister::kSystemConstants), uint32_t(offset >> 4),
|
|
std::min((swizzle & 3) + first_component, uint32_t(3)) |
|
|
std::min(((swizzle >> 2) & 3) + first_component, uint32_t(3)) << 2 |
|
|
std::min(((swizzle >> 4) & 3) + first_component, uint32_t(3)) << 4 |
|
|
std::min(((swizzle >> 6) & 3) + first_component, uint32_t(3)) << 6);
|
|
}
|
|
dxbc::Src LoadSystemConstant(SystemConstants::Index index, size_t offset,
|
|
uint32_t swizzle) {
|
|
MarkSystemConstantUsed(index);
|
|
return GetSystemConstantSrc(offset, swizzle);
|
|
}
|
|
dxbc::Src LoadFlagsSystemConstant() {
|
|
return LoadSystemConstant(SystemConstants::Index::kFlags,
|
|
offsetof(SystemConstants, flags),
|
|
dxbc::Src::kXXXX);
|
|
}
|
|
|
|
Modification GetDxbcShaderModification() const {
|
|
return Modification(current_translation().modification());
|
|
}
|
|
|
|
bool IsDxbcVertexShader() const {
|
|
return is_vertex_shader() &&
|
|
!Shader::IsHostVertexShaderTypeDomain(
|
|
GetDxbcShaderModification().vertex.host_vertex_shader_type);
|
|
}
|
|
bool IsDxbcDomainShader() const {
|
|
return is_vertex_shader() &&
|
|
Shader::IsHostVertexShaderTypeDomain(
|
|
GetDxbcShaderModification().vertex.host_vertex_shader_type);
|
|
}
|
|
|
|
bool IsForceEarlyDepthStencilGlobalFlagEnabled() const {
|
|
return is_pixel_shader() &&
|
|
GetDxbcShaderModification().pixel.depth_stencil_mode ==
|
|
Modification::DepthStencilMode::kEarlyHint &&
|
|
!edram_rov_used_ &&
|
|
current_shader().implicit_early_z_write_allowed();
|
|
}
|
|
|
|
uint32_t GetModificationInterpolatorMask() const {
|
|
Modification modification = GetDxbcShaderModification();
|
|
return is_vertex_shader() ? modification.vertex.interpolator_mask
|
|
: modification.pixel.interpolator_mask;
|
|
}
|
|
|
|
// Whether to use switch-case rather than if (pc >= label) for control flow.
|
|
bool UseSwitchForControlFlow() const;
|
|
|
|
// Allocates new consecutive r# registers for internal use and returns the
|
|
// index of the first.
|
|
uint32_t PushSystemTemp(uint32_t zero_mask = 0, uint32_t count = 1);
|
|
// 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.
|
|
void PWLGammaToLinear(uint32_t target_temp, uint32_t target_temp_component,
|
|
uint32_t source_temp, uint32_t source_temp_component,
|
|
bool source_pre_saturated, uint32_t temp1,
|
|
uint32_t temp1_component, uint32_t temp2,
|
|
uint32_t temp2_component);
|
|
// Converts one scalar, which must be saturated before calling this function,
|
|
// from linear to piecewise linear gamma. The target may be the same as either
|
|
// the source or as temp_or_target, but not as both (and temp_or_target may
|
|
// not be the same as the source). temp_non_target must be different.
|
|
void PreSaturatedLinearToPWLGamma(
|
|
uint32_t target_temp, uint32_t target_temp_component,
|
|
uint32_t source_temp, uint32_t source_temp_component,
|
|
uint32_t temp_or_target, uint32_t temp_or_target_component,
|
|
uint32_t temp_non_target, uint32_t temp_non_target_component);
|
|
|
|
bool IsSampleRate() const {
|
|
assert_true(is_pixel_shader());
|
|
return DSV_IsWritingFloat24Depth() && !current_shader().writes_depth();
|
|
}
|
|
bool IsDepthStencilSystemTempUsed() const {
|
|
// See system_temp_depth_stencil_ documentation for explanation of cases.
|
|
if (edram_rov_used_) {
|
|
// Needed for all cases (early, late, late with oDepth).
|
|
return true;
|
|
}
|
|
if (current_shader().writes_depth()) {
|
|
// With host render targets, the depth format may be float24, in this
|
|
// case, need to multiply it by 0.5 since 0...1 of the guest is stored as
|
|
// 0...0.5 on the host, and also to convert it.
|
|
// With ROV, need to store it to write later.
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
// Whether the current non-ROV pixel shader should convert the depth to 20e4.
|
|
bool DSV_IsWritingFloat24Depth() const {
|
|
if (edram_rov_used_) {
|
|
return false;
|
|
}
|
|
Modification::DepthStencilMode depth_stencil_mode =
|
|
GetDxbcShaderModification().pixel.depth_stencil_mode;
|
|
return depth_stencil_mode ==
|
|
Modification::DepthStencilMode::kFloat24Truncating ||
|
|
depth_stencil_mode ==
|
|
Modification::DepthStencilMode::kFloat24Rounding;
|
|
}
|
|
// Whether it's possible and worth skipping running the translated shader for
|
|
// 2x2 quads.
|
|
bool ROV_IsDepthStencilEarly() const {
|
|
assert_true(edram_rov_used_);
|
|
return !is_depth_only_pixel_shader_ && !current_shader().writes_depth() &&
|
|
!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
|
|
// involved in comparisons) according to the format specified in the system
|
|
// constants. Source and destination may be the same, temporary must be
|
|
// different than both.
|
|
void ROV_DepthTo24Bit(uint32_t d24_temp, uint32_t d24_temp_component,
|
|
uint32_t d32_temp, uint32_t d32_temp_component,
|
|
uint32_t temp_temp, uint32_t temp_temp_component);
|
|
// Does all the related to depth / stencil, including or not including
|
|
// writing based on whether it's late, or on whether it's safe to do it early.
|
|
// Updates system_temp_rov_params_ result and coverage if allowed and safe,
|
|
// updates system_temp_depth_stencil_, and if early and the coverage is empty
|
|
// for all pixels in the 2x2 quad and safe to return early (stencil is
|
|
// unchanged or known that it's safe not to await kills/alphatest/AtoC),
|
|
// returns from the shader.
|
|
void ROV_DepthStencilTest();
|
|
// Unpacks a 32bpp or a 64bpp color in packed_temp.packed_temp_components to
|
|
// color_temp, using 2 temporary VGPRs.
|
|
void ROV_UnpackColor(uint32_t rt_index, uint32_t packed_temp,
|
|
uint32_t packed_temp_components, uint32_t color_temp,
|
|
uint32_t temp1, uint32_t temp1_component, uint32_t temp2,
|
|
uint32_t temp2_component);
|
|
// Packs a float32x4 color value to 32bpp or a 64bpp in color_temp to
|
|
// packed_temp.packed_temp_components, using 2 temporary VGPR. color_temp and
|
|
// packed_temp may be the same if packed_temp_components is 0. If the format
|
|
// is 32bpp, will still write the high part to break register dependency.
|
|
void ROV_PackPreClampedColor(uint32_t rt_index, uint32_t color_temp,
|
|
uint32_t packed_temp,
|
|
uint32_t packed_temp_components, uint32_t temp1,
|
|
uint32_t temp1_component, uint32_t temp2,
|
|
uint32_t temp2_component);
|
|
// Emits a sequence of `case` labels for color blend factors, generating the
|
|
// factor from src_temp.rgb and dst_temp.rgb to factor_temp.rgb. factor_temp
|
|
// can be the same as src_temp or dst_temp.
|
|
void ROV_HandleColorBlendFactorCases(uint32_t src_temp, uint32_t dst_temp,
|
|
uint32_t factor_temp);
|
|
// Emits a sequence of `case` labels for alpha blend factors, generating the
|
|
// factor from src_temp.a and dst_temp.a to factor_temp.factor_component.
|
|
// factor_temp can be the same as src_temp or dst_temp.
|
|
void ROV_HandleAlphaBlendFactorCases(uint32_t src_temp, uint32_t dst_temp,
|
|
uint32_t factor_temp,
|
|
uint32_t factor_component);
|
|
|
|
// Writing the prologue.
|
|
// Applies the offset to vertex or tessellation patch indices in the source
|
|
// components, restricts them to the minimum and the maximum index values, and
|
|
// converts them to floating-point. The destination may be the same as the
|
|
// source.
|
|
void RemapAndConvertVertexIndices(uint32_t dest_temp,
|
|
uint32_t dest_temp_components,
|
|
const dxbc::Src& src);
|
|
void StartVertexShader_LoadVertexIndex();
|
|
void StartVertexOrDomainShader();
|
|
void StartDomainShader();
|
|
void StartPixelShader_LoadROVParameters();
|
|
void StartPixelShader();
|
|
|
|
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
|
|
// tested), overwrites the output to initialize it).
|
|
// For ROV, masks the sample away from coverage_temp.coverage_temp_component
|
|
// if it doesn't pass alpha to mask.
|
|
// threshold_offset and temp.temp_component can be the same if needed.
|
|
void CompletePixelShader_AlphaToMaskSample(
|
|
bool initialize, uint32_t sample_index, float threshold_base,
|
|
dxbc::Src threshold_offset, float threshold_offset_scale,
|
|
uint32_t coverage_temp, uint32_t coverage_temp_component, uint32_t temp,
|
|
uint32_t temp_component);
|
|
// Performs alpha to coverage if necessary, for RTV, writing to oMask, and for
|
|
// ROV, updating the low (coverage) bits of system_temp_rov_params_.x. Done
|
|
// manually even for RTV to maintain the guest dithering pattern and because
|
|
// alpha can be exponent-biased.
|
|
void CompletePixelShader_AlphaToMask();
|
|
void CompletePixelShader_WriteToRTVs();
|
|
void CompletePixelShader_DSV_DepthTo24Bit();
|
|
void CompletePixelShader_WriteToROV();
|
|
void CompletePixelShader();
|
|
|
|
void CompleteShaderCode();
|
|
|
|
// Writes the original instruction disassembly in the output DXBC if enabled,
|
|
// as shader messages, from instruction_disassembly_buffer_.
|
|
void EmitInstructionDisassembly();
|
|
|
|
// Converts a shader translator source operand to a DXBC emitter operand, or
|
|
// returns a zero literal operand if it's not going to be referenced. This may
|
|
// allocate a temporary register and emit instructions if the operand can't be
|
|
// used directly with most DXBC instructions (like, if it's an indexable GPR),
|
|
// in this case, temp_pushed_out will be set to true, and PopSystemTemp must
|
|
// be done when the operand is not needed anymore.
|
|
dxbc::Src LoadOperand(const InstructionOperand& operand,
|
|
uint32_t needed_components, bool& temp_pushed_out);
|
|
// Writes the specified source (src must be usable as a vector `mov` source,
|
|
// including to x#) to an instruction storage target.
|
|
// can_store_memexport_address is for safety, to allow only proper MADs with a
|
|
// stream constant to write to eA.
|
|
void StoreResult(const InstructionResult& result, const dxbc::Src& src,
|
|
bool can_store_memexport_address = false);
|
|
|
|
// The nesting of `if` instructions is the following:
|
|
// - pc checks (labels).
|
|
// - exec predicate/bool constant check.
|
|
// - Instruction-level predicate checks.
|
|
// As an optimization, where possible, the DXBC translator tries to merge
|
|
// multiple execs into one, not creating endif/if doing nothing, if the
|
|
// execution condition is the same. This can't be done across labels
|
|
// (obviously) and in case `setp` is done in a predicated exec - in this case,
|
|
// the predicate value in the current exec may not match the predicate value
|
|
// in the next exec.
|
|
// Instruction-level predicate checks are also merged, and until a `setp` is
|
|
// done, if the instruction has the same predicate condition as the exec it is
|
|
// in, no instruction-level predicate `if` is created as well. One exception
|
|
// to the usual way of instruction-level predicate handling is made for
|
|
// instructions involving derivative computation, such as texture fetches with
|
|
// computed LOD. The part involving derivatives is executed disregarding the
|
|
// predication, but the result storing is predicated (this is handled in
|
|
// texture fetch instruction implementation):
|
|
// https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx9-graphics-reference-asm-ps-registers-output-color
|
|
|
|
// Updates the current flow control condition (to be called in the beginning
|
|
// of exec and in jumps), closing the previous conditionals if needed.
|
|
// However, if the condition is not different, the instruction-level predicate
|
|
// `if` also won't be closed - this must be checked separately if needed (for
|
|
// example, in jumps). Also emits the last disassembly written to
|
|
// instruction_disassembly_buffer_ after closing the previous conditional and
|
|
// before opening a new one.
|
|
void UpdateExecConditionalsAndEmitDisassembly(
|
|
ParsedExecInstruction::Type type, uint32_t bool_constant_index,
|
|
bool condition);
|
|
// Closes `if`s opened by exec and instructions within them (but not by
|
|
// labels) and updates the state accordingly.
|
|
void CloseExecConditionals();
|
|
// Opens or reopens the predicate check conditional for the instruction, and
|
|
// emits the last disassembly written to instruction_disassembly_buffer_ after
|
|
// closing the previous predicate conditional and before opening a new one.
|
|
// This should be called before processing a non-control-flow instruction.
|
|
void UpdateInstructionPredicationAndEmitDisassembly(bool predicated,
|
|
bool condition);
|
|
// Closes the instruction-level predicate `if` if it's open, useful if a flow
|
|
// control instruction needs to do some code which needs to respect the exec's
|
|
// conditional, but can't itself be predicated.
|
|
void CloseInstructionPredication();
|
|
void JumpToLabel(uint32_t address);
|
|
|
|
uint32_t FindOrAddTextureBinding(uint32_t fetch_constant,
|
|
xenos::FetchOpDimension dimension,
|
|
bool is_signed);
|
|
uint32_t FindOrAddSamplerBinding(uint32_t fetch_constant,
|
|
xenos::TextureFilter mag_filter,
|
|
xenos::TextureFilter min_filter,
|
|
xenos::TextureFilter mip_filter,
|
|
xenos::AnisoFilter aniso_filter);
|
|
// Returns the number of texture SRV and sampler offsets that need to be
|
|
// passed via a constant buffer to the shader.
|
|
uint32_t GetBindlessResourceCount() const {
|
|
return uint32_t(texture_bindings_.size() + sampler_bindings_.size());
|
|
}
|
|
// Marks fetch constants as used by the DXBC shader and returns dxbc::Src
|
|
// for the words 01 (pair 0), 23 (pair 1) or 45 (pair 2) of the texture fetch
|
|
// constant.
|
|
dxbc::Src RequestTextureFetchConstantWordPair(uint32_t fetch_constant_index,
|
|
uint32_t pair_index) {
|
|
if (cbuffer_index_fetch_constants_ == kBindingIndexUnallocated) {
|
|
cbuffer_index_fetch_constants_ = cbuffer_count_++;
|
|
}
|
|
uint32_t total_pair_index = fetch_constant_index * 3 + pair_index;
|
|
return dxbc::Src::CB(cbuffer_index_fetch_constants_,
|
|
uint32_t(CbufferRegister::kFetchConstants),
|
|
total_pair_index >> 1,
|
|
(total_pair_index & 1) ? 0b10101110 : 0b00000100);
|
|
}
|
|
dxbc::Src RequestTextureFetchConstantWord(uint32_t fetch_constant_index,
|
|
uint32_t word_index) {
|
|
return RequestTextureFetchConstantWordPair(fetch_constant_index,
|
|
word_index >> 1)
|
|
.SelectFromSwizzled(word_index & 1);
|
|
}
|
|
|
|
void KillPixel(bool condition, const dxbc::Src& condition_src,
|
|
uint8_t memexport_eM_potentially_written_before);
|
|
|
|
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();
|
|
void WritePatchConstantSignature();
|
|
void WriteOutputSignature();
|
|
void WriteShaderCode();
|
|
|
|
// Executable instructions - generated during translation.
|
|
std::vector<uint32_t> shader_code_;
|
|
// Complete shader object, with all the needed blobs and dcl_ instructions -
|
|
// generated in the end of translation.
|
|
std::vector<uint32_t> shader_object_;
|
|
|
|
// Optional Direct3D features used by the shader.
|
|
dxbc::ShaderFeatureInfo shader_feature_info_;
|
|
// The statistics blob.
|
|
dxbc::Statistics statistics_;
|
|
|
|
// Assembler for shader_code_ and statistics_ (must be placed after them for
|
|
// correct initialization order).
|
|
dxbc::Assembler a_;
|
|
// Assembler for shader_object_ and statistics_, for declarations before the
|
|
// shader code that depend on info gathered during translation (must be placed
|
|
// after them for correct initialization order).
|
|
dxbc::Assembler ao_;
|
|
|
|
// Buffer for instruction disassembly comments.
|
|
StringBuffer instruction_disassembly_buffer_;
|
|
|
|
// Whether to write comments with the original Xenos instructions to the
|
|
// output.
|
|
bool emit_source_map_;
|
|
|
|
// Vendor ID of the GPU manufacturer, for toggling unsupported features.
|
|
ui::GraphicsProvider::GpuVendorID vendor_id_;
|
|
|
|
// Whether textures and samplers should be bindless.
|
|
bool bindless_resources_used_;
|
|
|
|
// Whether the output merger should be emulated in pixel shaders.
|
|
bool edram_rov_used_;
|
|
|
|
// Whether with RTV-based output-merger, k_8_8_8_8_GAMMA render targets are
|
|
// represented as host sRGB.
|
|
bool gamma_render_target_as_srgb_;
|
|
|
|
// Whether 2x MSAA is emulated using real 2x MSAA rather than two samples of
|
|
// 4x MSAA.
|
|
bool msaa_2x_supported_;
|
|
|
|
// Guest pixel host width / height.
|
|
uint32_t draw_resolution_scale_x_;
|
|
uint32_t draw_resolution_scale_y_;
|
|
|
|
// Is currently writing the empty depth-only pixel shader, for
|
|
// CompleteTranslation.
|
|
bool is_depth_only_pixel_shader_ = false;
|
|
|
|
// Data types used in constants buffers. Listed in dependency order.
|
|
enum class ShaderRdefTypeIndex {
|
|
kFloat,
|
|
kFloat2,
|
|
kFloat3,
|
|
kFloat4,
|
|
kUint,
|
|
kUint2,
|
|
kUint4,
|
|
// Render target clamping ranges.
|
|
kFloat4Array4,
|
|
// User clip planes.
|
|
kFloat4Array6,
|
|
// Float constants - size written dynamically.
|
|
kFloat4ConstantArray,
|
|
// Bool constants, texture signedness, front/back stencil, render target
|
|
// keep masks.
|
|
kUint4Array2,
|
|
// Loop constants.
|
|
kUint4Array8,
|
|
// Fetch constants.
|
|
kUint4Array48,
|
|
// Descriptor indices - size written dynamically.
|
|
kUint4DescriptorIndexArray,
|
|
|
|
kCount,
|
|
kUnknown = kCount
|
|
};
|
|
|
|
struct ShaderRdefType {
|
|
// Name ignored for arrays.
|
|
const char* name;
|
|
dxbc::RdefVariableClass variable_class;
|
|
dxbc::RdefVariableType variable_type;
|
|
uint16_t row_count;
|
|
uint16_t column_count;
|
|
uint16_t element_count;
|
|
ShaderRdefTypeIndex array_element_type;
|
|
};
|
|
static const ShaderRdefType rdef_types_[size_t(ShaderRdefTypeIndex::kCount)];
|
|
|
|
static constexpr uint32_t kBindingIndexUnallocated = UINT32_MAX;
|
|
|
|
// Number of constant buffer bindings used in this shader - also used for
|
|
// generation of indices of constant buffers that are optional.
|
|
uint32_t cbuffer_count_;
|
|
uint32_t cbuffer_index_system_constants_;
|
|
uint32_t cbuffer_index_float_constants_;
|
|
uint32_t cbuffer_index_bool_loop_constants_;
|
|
uint32_t cbuffer_index_fetch_constants_;
|
|
uint32_t cbuffer_index_descriptor_indices_;
|
|
|
|
struct SystemConstantRdef {
|
|
const char* name;
|
|
ShaderRdefTypeIndex type;
|
|
uint32_t size;
|
|
uint32_t padding_after;
|
|
};
|
|
static const SystemConstantRdef
|
|
system_constant_rdef_[size_t(SystemConstants::Index::kCount)];
|
|
// Mask of system constants (1 << SystemConstants::Index) used in the shader,
|
|
// so the remaining ones can be marked as unused in RDEF.
|
|
uint64_t system_constants_used_;
|
|
|
|
uint32_t out_reg_vs_interpolators_;
|
|
uint32_t out_reg_vs_position_;
|
|
// Clip and cull distances must be tightly packed in Direct3D.
|
|
// Up to 6 SV_ClipDistances or SV_CullDistances depending on
|
|
// user_clip_plane_cull, then one SV_CullDistance if vertex_kill_and is used.
|
|
uint32_t out_reg_vs_clip_cull_distances_;
|
|
uint32_t out_reg_vs_point_size_;
|
|
uint32_t in_reg_ps_interpolators_;
|
|
uint32_t in_reg_ps_point_coordinates_;
|
|
uint32_t in_reg_ps_position_;
|
|
// nointerpolation inputs. SV_IsFrontFace (X) is for non-point PsParamGen,
|
|
// SV_SampleIndex (Y) is for memexport when sample-rate shading is otherwise
|
|
// needed anyway due to depth conversion.
|
|
uint32_t in_reg_ps_front_face_sample_index_;
|
|
|
|
// Mask of domain location actually used in the domain shader.
|
|
uint32_t in_domain_location_used_;
|
|
// Whether kInRegisterDSControlPointIndex has been used in the shader.
|
|
bool in_control_point_index_used_;
|
|
// Mask of the pixel/sample position actually used in the pixel shader.
|
|
uint32_t in_position_used_;
|
|
// Whether the faceness has been used in the pixel shader.
|
|
bool in_front_face_used_;
|
|
|
|
// Number of currently allocated Xenia internal r# registers.
|
|
uint32_t system_temp_count_current_;
|
|
// Total maximum number of temporary registers ever used during this
|
|
// translation (for the declaration).
|
|
uint32_t system_temp_count_max_;
|
|
|
|
// Position in vertex shaders (because viewport and W transformations can be
|
|
// applied in the end of the shader).
|
|
uint32_t system_temp_position_;
|
|
// Special exports in vertex shaders.
|
|
uint32_t system_temp_point_size_edge_flag_kill_vertex_;
|
|
// ROV only - 4 persistent VGPRs when writing to color targets, 2 VGPRs when
|
|
// not:
|
|
// X - Bit masks:
|
|
// 0:3 - Per-sample coverage at the current stage of the shader's execution.
|
|
// Affected by things like SV_Coverage, early or late depth / stencil
|
|
// (always resets bits for failing, no matter if need to defer writing),
|
|
// alpha to coverage.
|
|
// 4:7 - Depth write deferred mask - when early depth / stencil resulted in a
|
|
// different value for the sample (like different stencil if the test
|
|
// failed), but can't write it before running the shader because it's
|
|
// not known if the sample will be discarded by the shader, alphatest or
|
|
// AtoC.
|
|
// Early depth / stencil rejection of the pixel is possible when both 0:3 and
|
|
// 4:7 are zero.
|
|
// 8:11 - Whether color buffers have been written to, if not written on the
|
|
// taken execution path, don't export according to Direct3D 9 register
|
|
// documentation (some games rely on this behavior).
|
|
// Y - Absolute resolution-scaled EDRAM offset for depth / stencil, in dwords,
|
|
// before and during depth testing. During color writing, when the depth /
|
|
// stencil address is not needed anymore, current color sample address.
|
|
// Z - Base-relative resolution-scaled EDRAM offset for 32bpp color data, in
|
|
// dwords.
|
|
// W - Base-relative resolution-scaled EDRAM offset for 64bpp color data, in
|
|
// dwords.
|
|
uint32_t system_temp_rov_params_;
|
|
// Different purposes:
|
|
// - When writing to oDepth: X also used to hold the depth written by the
|
|
// shader, which, for host render targets, if the depth buffer is float24,
|
|
// needs to be remapped from guest 0...1 to host 0...0.5 and, if needed,
|
|
// converted to float24 precision; and for ROV, needs to be written in the
|
|
// end of the shader.
|
|
// - When not writing to oDepth, but using ROV:
|
|
// - ROV_IsDepthStencilEarly: New per-sample depth / stencil values,
|
|
// generated during early depth / stencil test (actual writing checks
|
|
// the remaining coverage bits).
|
|
// - Not ROV_IsDepthStencilEarly: Z gradients in .xy taken in the beginning
|
|
// of the shader before any return statement is possibly reached.
|
|
uint32_t system_temp_depth_stencil_;
|
|
// Up to 4 color outputs in pixel shaders (needs to be readable, because of
|
|
// alpha test, alpha to coverage, exponent bias, gamma, and also for ROV
|
|
// writing).
|
|
uint32_t system_temps_color_[4];
|
|
|
|
// 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).
|
|
uint32_t system_temp_result_;
|
|
// Temporary register ID for previous scalar result, program counter,
|
|
// predicate and absolute address register.
|
|
uint32_t system_temp_ps_pc_p0_a0_;
|
|
// Loop index stack - .x is the active loop, shifted right to .yzw on push.
|
|
uint32_t system_temp_aL_;
|
|
// Loop counter stack, .x is the active loop. Represents number of times
|
|
// remaining to loop.
|
|
uint32_t system_temp_loop_count_;
|
|
// Explicitly set texture gradients and LOD.
|
|
uint32_t system_temp_grad_h_lod_;
|
|
// .w stores `base + index * stride` in bytes from the last vfetch_full as it
|
|
// may be needed by vfetch_mini.
|
|
uint32_t system_temp_grad_v_vfetch_address_;
|
|
|
|
// The bool constant number containing the condition for the currently
|
|
// processed exec (or the last - unless a label has reset this), or
|
|
// kCfExecBoolConstantNone if it's not checked.
|
|
uint32_t cf_exec_bool_constant_;
|
|
static constexpr uint32_t kCfExecBoolConstantNone = UINT32_MAX;
|
|
// The expected bool constant value in the current exec if
|
|
// cf_exec_bool_constant_ is not kCfExecBoolConstantNone.
|
|
bool cf_exec_bool_constant_condition_;
|
|
// Whether the currently processed exec is executed if a predicate is
|
|
// set/unset.
|
|
bool cf_exec_predicated_;
|
|
// The expected predicated condition if cf_exec_predicated_ is true.
|
|
bool cf_exec_predicate_condition_;
|
|
// Whether an `if` for instruction-level predicate check is currently open.
|
|
bool cf_instruction_predicate_if_open_;
|
|
// The expected predicate condition for the current or the last instruction if
|
|
// cf_exec_instruction_predicated_ is true.
|
|
bool cf_instruction_predicate_condition_;
|
|
// Whether there was a `setp` in the current exec before the current
|
|
// instruction, thus instruction-level predicate value can be different than
|
|
// the exec-level predicate value, and can't merge two execs with the same
|
|
// predicate condition anymore.
|
|
bool cf_exec_predicate_written_;
|
|
|
|
// Number of SRV resources used in this shader - also used for generation of
|
|
// indices of SRV resources that are optional.
|
|
uint32_t srv_count_;
|
|
uint32_t srv_index_shared_memory_;
|
|
uint32_t srv_index_bindless_textures_2d_;
|
|
uint32_t srv_index_bindless_textures_3d_;
|
|
uint32_t srv_index_bindless_textures_cube_;
|
|
|
|
// The first binding is at t[SRVMainRegister::kBindfulTexturesStart] of space
|
|
// SRVSpace::kMain.
|
|
std::vector<TextureBinding> texture_bindings_;
|
|
std::unordered_map<uint32_t, uint32_t>
|
|
texture_bindings_for_bindful_srv_indices_;
|
|
|
|
// Number of UAV resources used in this shader - also used for generation of
|
|
// indices of UAV resources that are optional.
|
|
uint32_t uav_count_;
|
|
uint32_t uav_index_shared_memory_;
|
|
uint32_t uav_index_edram_;
|
|
|
|
std::vector<SamplerBinding> sampler_bindings_;
|
|
};
|
|
|
|
} // namespace gpu
|
|
} // namespace xe
|
|
|
|
#endif // XENIA_GPU_DXBC_SHADER_TRANSLATOR_H_
|