[D3D12] Cleanup primitive types and front/back facing
This commit is contained in:
@@ -194,6 +194,7 @@ D3D12Shader* PipelineCache::LoadShader(ShaderType shader_type,
|
||||
|
||||
bool PipelineCache::EnsureShadersTranslated(D3D12Shader* vertex_shader,
|
||||
D3D12Shader* pixel_shader,
|
||||
bool tessellated,
|
||||
PrimitiveType primitive_type) {
|
||||
auto& regs = *register_file_;
|
||||
|
||||
@@ -207,12 +208,14 @@ bool PipelineCache::EnsureShadersTranslated(D3D12Shader* vertex_shader,
|
||||
xenos::xe_gpu_program_cntl_t sq_program_cntl;
|
||||
sq_program_cntl.dword_0 = regs[XE_GPU_REG_SQ_PROGRAM_CNTL].u32;
|
||||
if (!vertex_shader->is_translated() &&
|
||||
!TranslateShader(vertex_shader, sq_program_cntl, primitive_type)) {
|
||||
!TranslateShader(vertex_shader, sq_program_cntl, tessellated,
|
||||
primitive_type)) {
|
||||
XELOGE("Failed to translate the vertex shader!");
|
||||
return false;
|
||||
}
|
||||
if (pixel_shader != nullptr && !pixel_shader->is_translated() &&
|
||||
!TranslateShader(pixel_shader, sq_program_cntl, primitive_type)) {
|
||||
!TranslateShader(pixel_shader, sq_program_cntl, tessellated,
|
||||
primitive_type)) {
|
||||
XELOGE("Failed to translate the pixel shader!");
|
||||
return false;
|
||||
}
|
||||
@@ -220,7 +223,7 @@ bool PipelineCache::EnsureShadersTranslated(D3D12Shader* vertex_shader,
|
||||
}
|
||||
|
||||
bool PipelineCache::ConfigurePipeline(
|
||||
D3D12Shader* vertex_shader, D3D12Shader* pixel_shader,
|
||||
D3D12Shader* vertex_shader, D3D12Shader* pixel_shader, bool tessellated,
|
||||
PrimitiveType primitive_type, IndexFormat index_format, bool early_z,
|
||||
const RenderTargetCache::PipelineRenderTarget render_targets[5],
|
||||
void** pipeline_handle_out, ID3D12RootSignature** root_signature_out) {
|
||||
@@ -232,9 +235,9 @@ bool PipelineCache::ConfigurePipeline(
|
||||
assert_not_null(root_signature_out);
|
||||
|
||||
PipelineDescription description;
|
||||
if (!GetCurrentStateDescription(vertex_shader, pixel_shader, primitive_type,
|
||||
index_format, early_z, render_targets,
|
||||
description)) {
|
||||
if (!GetCurrentStateDescription(vertex_shader, pixel_shader, tessellated,
|
||||
primitive_type, index_format, early_z,
|
||||
render_targets, description)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -260,7 +263,8 @@ bool PipelineCache::ConfigurePipeline(
|
||||
}
|
||||
}
|
||||
|
||||
if (!EnsureShadersTranslated(vertex_shader, pixel_shader, primitive_type)) {
|
||||
if (!EnsureShadersTranslated(vertex_shader, pixel_shader, tessellated,
|
||||
primitive_type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -289,33 +293,17 @@ bool PipelineCache::ConfigurePipeline(
|
||||
|
||||
bool PipelineCache::TranslateShader(D3D12Shader* shader,
|
||||
xenos::xe_gpu_program_cntl_t cntl,
|
||||
bool tessellated,
|
||||
PrimitiveType primitive_type) {
|
||||
// Set the target for vertex shader translation.
|
||||
DxbcShaderTranslator::VertexShaderType vertex_shader_type;
|
||||
if (primitive_type == PrimitiveType::kTrianglePatch) {
|
||||
vertex_shader_type =
|
||||
DxbcShaderTranslator::VertexShaderType::kTriangleDomain;
|
||||
} else if (primitive_type == PrimitiveType::kQuadPatch) {
|
||||
vertex_shader_type = DxbcShaderTranslator::VertexShaderType::kQuadDomain;
|
||||
} else {
|
||||
vertex_shader_type = DxbcShaderTranslator::VertexShaderType::kVertex;
|
||||
}
|
||||
shader_translator_->SetVertexShaderType(vertex_shader_type);
|
||||
|
||||
// Perform translation.
|
||||
// If this fails the shader will be marked as invalid and ignored later.
|
||||
if (!shader_translator_->Translate(shader, cntl)) {
|
||||
if (!shader_translator_->Translate(
|
||||
shader, tessellated ? primitive_type : PrimitiveType::kNone, cntl)) {
|
||||
XELOGE("Shader %.16" PRIX64 " translation failed; marking as ignored",
|
||||
shader->ucode_data_hash());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vertex_shader_type != DxbcShaderTranslator::VertexShaderType::kVertex) {
|
||||
// For checking later for safety (so a vertex shader won't be accidentally
|
||||
// used as a domain shader or vice versa).
|
||||
shader->SetDomainShaderPrimitiveType(primitive_type);
|
||||
}
|
||||
|
||||
uint32_t texture_srv_count;
|
||||
const DxbcShaderTranslator::TextureSRV* texture_srvs =
|
||||
shader_translator_->GetTextureSRVs(texture_srv_count);
|
||||
@@ -360,19 +348,20 @@ bool PipelineCache::TranslateShader(D3D12Shader* shader,
|
||||
}
|
||||
|
||||
bool PipelineCache::GetCurrentStateDescription(
|
||||
D3D12Shader* vertex_shader, D3D12Shader* pixel_shader,
|
||||
D3D12Shader* vertex_shader, D3D12Shader* pixel_shader, bool tessellated,
|
||||
PrimitiveType primitive_type, IndexFormat index_format, bool early_z,
|
||||
const RenderTargetCache::PipelineRenderTarget render_targets[5],
|
||||
PipelineDescription& description_out) {
|
||||
auto& regs = *register_file_;
|
||||
uint32_t pa_su_sc_mode_cntl = regs[XE_GPU_REG_PA_SU_SC_MODE_CNTL].u32;
|
||||
bool primitive_two_faced = IsPrimitiveTwoFaced(tessellated, primitive_type);
|
||||
|
||||
// Initialize all unused fields to zero for comparison/hashing.
|
||||
std::memset(&description_out, 0, sizeof(description_out));
|
||||
|
||||
// Root signature.
|
||||
description_out.root_signature = command_processor_->GetRootSignature(
|
||||
vertex_shader, pixel_shader, primitive_type);
|
||||
vertex_shader, pixel_shader, tessellated);
|
||||
if (description_out.root_signature == nullptr) {
|
||||
return false;
|
||||
}
|
||||
@@ -393,93 +382,79 @@ bool PipelineCache::GetCurrentStateDescription(
|
||||
}
|
||||
|
||||
// Primitive topology type, tessellation mode and geometry shader.
|
||||
description_out.tessellation_mode = PipelineTessellationMode::kNone;
|
||||
switch (primitive_type) {
|
||||
case PrimitiveType::kPointList:
|
||||
description_out.primitive_topology_type =
|
||||
PipelinePrimitiveTopologyType::kPoint;
|
||||
break;
|
||||
case PrimitiveType::kLineList:
|
||||
case PrimitiveType::kLineStrip:
|
||||
case PrimitiveType::kLineLoop:
|
||||
// Quads are emulated as line lists with adjacency.
|
||||
case PrimitiveType::kQuadList:
|
||||
case PrimitiveType::k2DLineStrip:
|
||||
description_out.primitive_topology_type =
|
||||
PipelinePrimitiveTopologyType::kLine;
|
||||
break;
|
||||
case PrimitiveType::kTrianglePatch:
|
||||
case PrimitiveType::kQuadPatch:
|
||||
description_out.primitive_topology_type =
|
||||
PipelinePrimitiveTopologyType::kPatch;
|
||||
switch (TessellationMode(regs[XE_GPU_REG_VGT_HOS_CNTL].u32 & 0x3)) {
|
||||
case TessellationMode::kContinuous:
|
||||
description_out.tessellation_mode =
|
||||
PipelineTessellationMode::kContinuous;
|
||||
break;
|
||||
case TessellationMode::kAdaptive:
|
||||
description_out.tessellation_mode =
|
||||
FLAGS_d3d12_tessellation_adaptive
|
||||
? PipelineTessellationMode::kAdaptive
|
||||
: PipelineTessellationMode::kContinuous;
|
||||
break;
|
||||
default:
|
||||
description_out.tessellation_mode =
|
||||
PipelineTessellationMode::kDiscrete;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
description_out.primitive_topology_type =
|
||||
PipelinePrimitiveTopologyType::kTriangle;
|
||||
break;
|
||||
}
|
||||
switch (primitive_type) {
|
||||
case PrimitiveType::kLinePatch:
|
||||
description_out.patch_type = PipelinePatchType::kLine;
|
||||
break;
|
||||
case PrimitiveType::kTrianglePatch:
|
||||
description_out.patch_type = PipelinePatchType::kTriangle;
|
||||
break;
|
||||
case PrimitiveType::kQuadPatch:
|
||||
description_out.patch_type = PipelinePatchType::kQuad;
|
||||
break;
|
||||
default:
|
||||
description_out.patch_type = PipelinePatchType::kNone;
|
||||
break;
|
||||
}
|
||||
switch (primitive_type) {
|
||||
case PrimitiveType::kPointList:
|
||||
description_out.geometry_shader = PipelineGeometryShader::kPointList;
|
||||
break;
|
||||
case PrimitiveType::kRectangleList:
|
||||
description_out.geometry_shader = PipelineGeometryShader::kRectangleList;
|
||||
break;
|
||||
case PrimitiveType::kQuadList:
|
||||
description_out.geometry_shader = PipelineGeometryShader::kQuadList;
|
||||
break;
|
||||
default:
|
||||
description_out.geometry_shader = PipelineGeometryShader::kNone;
|
||||
break;
|
||||
if (tessellated) {
|
||||
switch (TessellationMode(regs[XE_GPU_REG_VGT_HOS_CNTL].u32 & 0x3)) {
|
||||
case TessellationMode::kContinuous:
|
||||
description_out.tessellation_mode =
|
||||
PipelineTessellationMode::kContinuous;
|
||||
break;
|
||||
case TessellationMode::kAdaptive:
|
||||
description_out.tessellation_mode =
|
||||
FLAGS_d3d12_tessellation_adaptive
|
||||
? PipelineTessellationMode::kAdaptive
|
||||
: PipelineTessellationMode::kContinuous;
|
||||
break;
|
||||
default:
|
||||
description_out.tessellation_mode = PipelineTessellationMode::kDiscrete;
|
||||
break;
|
||||
}
|
||||
description_out.primitive_topology_type =
|
||||
PipelinePrimitiveTopologyType::kPatch;
|
||||
switch (primitive_type) {
|
||||
case PrimitiveType::kLinePatch:
|
||||
description_out.patch_type = PipelinePatchType::kLine;
|
||||
break;
|
||||
case PrimitiveType::kTrianglePatch:
|
||||
description_out.patch_type = PipelinePatchType::kTriangle;
|
||||
break;
|
||||
case PrimitiveType::kQuadPatch:
|
||||
description_out.patch_type = PipelinePatchType::kQuad;
|
||||
break;
|
||||
default:
|
||||
assert_unhandled_case(primitive_type);
|
||||
return false;
|
||||
}
|
||||
description_out.geometry_shader = PipelineGeometryShader::kNone;
|
||||
} else {
|
||||
description_out.tessellation_mode = PipelineTessellationMode::kNone;
|
||||
switch (primitive_type) {
|
||||
case PrimitiveType::kPointList:
|
||||
description_out.primitive_topology_type =
|
||||
PipelinePrimitiveTopologyType::kPoint;
|
||||
break;
|
||||
case PrimitiveType::kLineList:
|
||||
case PrimitiveType::kLineStrip:
|
||||
case PrimitiveType::kLineLoop:
|
||||
// Quads are emulated as line lists with adjacency.
|
||||
case PrimitiveType::kQuadList:
|
||||
case PrimitiveType::k2DLineStrip:
|
||||
description_out.primitive_topology_type =
|
||||
PipelinePrimitiveTopologyType::kLine;
|
||||
break;
|
||||
default:
|
||||
description_out.primitive_topology_type =
|
||||
PipelinePrimitiveTopologyType::kTriangle;
|
||||
break;
|
||||
}
|
||||
description_out.patch_type = PipelinePatchType::kNone;
|
||||
switch (primitive_type) {
|
||||
case PrimitiveType::kPointList:
|
||||
description_out.geometry_shader = PipelineGeometryShader::kPointList;
|
||||
break;
|
||||
case PrimitiveType::kRectangleList:
|
||||
description_out.geometry_shader =
|
||||
PipelineGeometryShader::kRectangleList;
|
||||
break;
|
||||
case PrimitiveType::kQuadList:
|
||||
description_out.geometry_shader = PipelineGeometryShader::kQuadList;
|
||||
break;
|
||||
default:
|
||||
description_out.geometry_shader = PipelineGeometryShader::kNone;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Rasterizer state.
|
||||
uint32_t cull_mode;
|
||||
if (primitive_type == PrimitiveType::kPointList ||
|
||||
primitive_type == PrimitiveType::kRectangleList) {
|
||||
cull_mode = 0;
|
||||
} else {
|
||||
cull_mode = pa_su_sc_mode_cntl & 0x3;
|
||||
}
|
||||
if (cull_mode & 1) {
|
||||
// More special, so checked first - generally back faces are culled.
|
||||
description_out.cull_mode = PipelineCullMode::kFront;
|
||||
} else if (cull_mode & 2) {
|
||||
description_out.cull_mode = PipelineCullMode::kBack;
|
||||
} else {
|
||||
description_out.cull_mode = PipelineCullMode::kNone;
|
||||
}
|
||||
description_out.front_counter_clockwise = (pa_su_sc_mode_cntl & 0x4) == 0;
|
||||
// Because Direct3D 12 doesn't support per-side fill mode and depth bias, the
|
||||
// values to use depends on the current culling state.
|
||||
// If front faces are culled, use the ones for back faces.
|
||||
@@ -495,33 +470,56 @@ bool PipelineCache::GetCurrentStateDescription(
|
||||
// Here we also assume that only one side is culled - if two sides are culled,
|
||||
// the D3D12 command processor will drop such draw early.
|
||||
float poly_offset = 0.0f, poly_offset_scale = 0.0f;
|
||||
// With ROV, the depth bias is applied in the pixel shader because per-sample
|
||||
// depth is needed for MSAA.
|
||||
if (!(cull_mode & 1)) {
|
||||
// Front faces aren't culled.
|
||||
uint32_t fill_mode = (pa_su_sc_mode_cntl >> 5) & 0x7;
|
||||
if (fill_mode == 0 || fill_mode == 1) {
|
||||
description_out.fill_mode_wireframe = 1;
|
||||
if (primitive_two_faced) {
|
||||
uint32_t cull_mode = pa_su_sc_mode_cntl & 0x3;
|
||||
description_out.front_counter_clockwise = (pa_su_sc_mode_cntl & 0x4) == 0;
|
||||
if (cull_mode == 1) {
|
||||
description_out.cull_mode = PipelineCullMode::kFront;
|
||||
} else if (cull_mode == 2) {
|
||||
description_out.cull_mode = PipelineCullMode::kBack;
|
||||
} else {
|
||||
description_out.cull_mode = PipelineCullMode::kNone;
|
||||
}
|
||||
if (!edram_rov_used_ && ((pa_su_sc_mode_cntl >> 11) & 0x1)) {
|
||||
// With ROV, the depth bias is applied in the pixel shader because
|
||||
// per-sample depth is needed for MSAA.
|
||||
if (cull_mode != 1) {
|
||||
// Front faces aren't culled.
|
||||
uint32_t fill_mode = (pa_su_sc_mode_cntl >> 5) & 0x7;
|
||||
if (fill_mode == 0 || fill_mode == 1) {
|
||||
description_out.fill_mode_wireframe = 1;
|
||||
}
|
||||
if (!edram_rov_used_ && (pa_su_sc_mode_cntl & (1 << 11))) {
|
||||
poly_offset = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_OFFSET].f32;
|
||||
poly_offset_scale = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_SCALE].f32;
|
||||
}
|
||||
}
|
||||
if (cull_mode != 2) {
|
||||
// Back faces aren't culled.
|
||||
uint32_t fill_mode = (pa_su_sc_mode_cntl >> 8) & 0x7;
|
||||
if (fill_mode == 0 || fill_mode == 1) {
|
||||
description_out.fill_mode_wireframe = 1;
|
||||
}
|
||||
// Prefer front depth bias because in general, front faces are the ones
|
||||
// that are rendered (except for shadow volumes).
|
||||
if (!edram_rov_used_ && (pa_su_sc_mode_cntl & (1 << 12)) &&
|
||||
poly_offset == 0.0f && poly_offset_scale == 0.0f) {
|
||||
poly_offset = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_BACK_OFFSET].f32;
|
||||
poly_offset_scale = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_BACK_SCALE].f32;
|
||||
}
|
||||
}
|
||||
if (((pa_su_sc_mode_cntl >> 3) & 0x3) == 0) {
|
||||
// Fill mode is disabled.
|
||||
description_out.fill_mode_wireframe = 0;
|
||||
}
|
||||
} else {
|
||||
// Filled front faces only.
|
||||
// Use front depth bias if POLY_OFFSET_PARA_ENABLED
|
||||
// (POLY_OFFSET_FRONT_ENABLED is for two-sided primitives).
|
||||
if (!edram_rov_used_ && (pa_su_sc_mode_cntl & (1 << 13))) {
|
||||
poly_offset = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_OFFSET].f32;
|
||||
poly_offset_scale = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_SCALE].f32;
|
||||
}
|
||||
}
|
||||
if (!(cull_mode & 2)) {
|
||||
// Back faces aren't culled.
|
||||
uint32_t fill_mode = (pa_su_sc_mode_cntl >> 8) & 0x7;
|
||||
if (fill_mode == 0 || fill_mode == 1) {
|
||||
description_out.fill_mode_wireframe = 1;
|
||||
}
|
||||
// Prefer front depth bias because in general, front faces are the ones
|
||||
// that are rendered (except for shadow volumes).
|
||||
if (!edram_rov_used_ && ((pa_su_sc_mode_cntl >> 12) & 0x1) &&
|
||||
poly_offset == 0.0f && poly_offset_scale == 0.0f) {
|
||||
poly_offset = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_BACK_OFFSET].f32;
|
||||
poly_offset_scale = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_BACK_SCALE].f32;
|
||||
}
|
||||
}
|
||||
if (!edram_rov_used_) {
|
||||
// Conversion based on the calculations in Call of Duty 4 and the values it
|
||||
// writes to the registers, and also on:
|
||||
@@ -551,12 +549,9 @@ bool PipelineCache::GetCurrentStateDescription(
|
||||
description_out.depth_bias_slope_scaled =
|
||||
poly_offset_scale * (1.0f / 16.0f);
|
||||
}
|
||||
if ((pa_su_sc_mode_cntl & (0x3 << 3)) == 0) {
|
||||
// Fill mode is disabled.
|
||||
description_out.fill_mode_wireframe = 0;
|
||||
}
|
||||
if (FLAGS_d3d12_tessellation_wireframe &&
|
||||
description_out.tessellation_mode != PipelineTessellationMode::kNone) {
|
||||
if (FLAGS_d3d12_tessellation_wireframe && tessellated &&
|
||||
(primitive_type == PrimitiveType::kTrianglePatch ||
|
||||
primitive_type == PrimitiveType::kQuadPatch)) {
|
||||
description_out.fill_mode_wireframe = 1;
|
||||
}
|
||||
// CLIP_DISABLE
|
||||
@@ -596,7 +591,7 @@ bool PipelineCache::GetCurrentStateDescription(
|
||||
(rb_depthcontrol >> 17) & 0x7;
|
||||
description_out.stencil_front_pass_op = (rb_depthcontrol >> 14) & 0x7;
|
||||
description_out.stencil_front_func = (rb_depthcontrol >> 8) & 0x7;
|
||||
if (rb_depthcontrol & 0x80) {
|
||||
if (primitive_two_faced && (rb_depthcontrol & 0x80)) {
|
||||
description_out.stencil_back_fail_op = (rb_depthcontrol >> 23) & 0x7;
|
||||
description_out.stencil_back_depth_fail_op =
|
||||
(rb_depthcontrol >> 29) & 0x7;
|
||||
@@ -768,7 +763,7 @@ ID3D12PipelineState* PipelineCache::CreatePipelineState(
|
||||
if (description.tessellation_mode != PipelineTessellationMode::kNone) {
|
||||
switch (description.patch_type) {
|
||||
case PipelinePatchType::kTriangle:
|
||||
if (description.vertex_shader->GetDomainShaderPrimitiveType() !=
|
||||
if (description.vertex_shader->patch_primitive_type() !=
|
||||
PrimitiveType::kTrianglePatch) {
|
||||
XELOGE(
|
||||
"Tried to use vertex shader %.16" PRIX64
|
||||
@@ -794,7 +789,7 @@ ID3D12PipelineState* PipelineCache::CreatePipelineState(
|
||||
state_desc.VS.BytecodeLength = sizeof(tessellation_triangle_vs);
|
||||
break;
|
||||
case PipelinePatchType::kQuad:
|
||||
if (description.vertex_shader->GetDomainShaderPrimitiveType() !=
|
||||
if (description.vertex_shader->patch_primitive_type() !=
|
||||
PrimitiveType::kQuadPatch) {
|
||||
XELOGE("Tried to use vertex shader %.16" PRIX64
|
||||
" for quad patch tessellation, but it's not a tessellation "
|
||||
@@ -825,7 +820,7 @@ ID3D12PipelineState* PipelineCache::CreatePipelineState(
|
||||
state_desc.DS.BytecodeLength =
|
||||
description.vertex_shader->translated_binary().size();
|
||||
} else {
|
||||
if (description.vertex_shader->GetDomainShaderPrimitiveType() !=
|
||||
if (description.vertex_shader->patch_primitive_type() !=
|
||||
PrimitiveType::kNone) {
|
||||
XELOGE("Tried to use vertex shader %.16" PRIX64
|
||||
" without tessellation, but it's a tessellation domain shader",
|
||||
|
||||
Reference in New Issue
Block a user