[D3D12] Cleanup primitive types and front/back facing

This commit is contained in:
Triang3l
2019-07-13 22:25:03 +03:00
parent a16a746432
commit 4825e69fda
12 changed files with 323 additions and 303 deletions

View File

@@ -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",