[D3D12] Vertex kill and multipass vertex exports

This commit is contained in:
Triang3l
2020-02-20 08:55:40 +03:00
parent bb13103102
commit 1336316139
23 changed files with 1959 additions and 1611 deletions

View File

@@ -1313,10 +1313,14 @@ bool D3D12CommandProcessor::IssueDraw(PrimitiveType primitive_type,
bool memexport_used = memexport_used_vertex || memexport_used_pixel;
bool primitive_two_faced = IsPrimitiveTwoFaced(tessellated, primitive_type);
auto sq_program_cntl = regs.Get<reg::SQ_PROGRAM_CNTL>();
auto pa_su_sc_mode_cntl = regs.Get<reg::PA_SU_SC_MODE_CNTL>();
if (!memexport_used_vertex && primitive_two_faced &&
pa_su_sc_mode_cntl.cull_front && pa_su_sc_mode_cntl.cull_back) {
// Both sides are culled - can't be expressed in the pipeline state.
if (!memexport_used_vertex &&
(sq_program_cntl.vs_export_mode ==
xenos::VertexShaderExportMode::kMultipass ||
(primitive_two_faced && pa_su_sc_mode_cntl.cull_front &&
pa_su_sc_mode_cntl.cull_back))) {
// All faces are culled - can't be expressed in the pipeline state.
return true;
}
@@ -2440,6 +2444,10 @@ void D3D12CommandProcessor::UpdateSystemConstantValues(
if (viewport_scale_z < 0.0f) {
flags |= DxbcShaderTranslator::kSysFlag_ReverseZ;
}
// Primitive killing condition.
if (pa_cl_clip_cntl.vtx_kill_or) {
flags |= DxbcShaderTranslator::kSysFlag_KillIfAnyVertexKilled;
}
// Alpha test.
if (rb_colorcontrol.alpha_test_enable) {
flags |= uint32_t(rb_colorcontrol.alpha_func)
@@ -2534,6 +2542,18 @@ void D3D12CommandProcessor::UpdateSystemConstantValues(
}
}
// Half pixel offset.
bool half_pixel_offset =
cvars::d3d12_half_pixel_offset && !pa_su_vtx_cntl.pix_center;
// Like in OpenGL - VPOS giving pixel centers, or like in Direct3D 9 - VPOS
// giving the top-left corner.
// TODO(Triang3l): Check if ps_param_gen should give center positions in
// OpenGL mode on the Xbox 360.
float param_gen_half_pixel_offset = half_pixel_offset ? 0.0f : 0.5f;
dirty |=
system_constants_.pixel_half_pixel_offset != param_gen_half_pixel_offset;
system_constants_.pixel_half_pixel_offset = param_gen_half_pixel_offset;
// Conversion to Direct3D 12 normalized device coordinates.
// See viewport configuration in UpdateFixedFunctionState for explanations.
// X and Y scale/offset is to convert unnormalized coordinates generated by
@@ -2545,72 +2565,72 @@ void D3D12CommandProcessor::UpdateSystemConstantValues(
// different register (and if there's such register at all).
float viewport_scale_x = regs[XE_GPU_REG_PA_CL_VPORT_XSCALE].f32;
float viewport_scale_y = regs[XE_GPU_REG_PA_CL_VPORT_YSCALE].f32;
// When VPORT_Z_SCALE_ENA is disabled, Z/W is directly what is expected to be
// written to the depth buffer, and for some reason DX_CLIP_SPACE_DEF isn't
// set in this case in draws in games.
bool gl_clip_space_def =
!pa_cl_clip_cntl.dx_clip_space_def && pa_cl_vte_cntl.vport_z_scale_ena;
float ndc_scale_x, ndc_scale_y, ndc_scale_z;
if (primitive_two_faced && pa_su_sc_mode_cntl.cull_front &&
pa_su_sc_mode_cntl.cull_back) {
// Kill all primitives if both faces are culled, but the vertex shader still
// needs to do memexport (not NaN because of comparison for setting the
// dirty flag).
ndc_scale_x = ndc_scale_y = ndc_scale_z = 0;
// Kill all primitives if multipass or both faces are culled, but still need
// to do memexport.
if (sq_program_cntl.vs_export_mode ==
xenos::VertexShaderExportMode::kMultipass ||
(primitive_two_faced && pa_su_sc_mode_cntl.cull_front &&
pa_su_sc_mode_cntl.cull_back)) {
dirty |= !std::isnan(system_constants_.ndc_scale[0]);
dirty |= !std::isnan(system_constants_.ndc_scale[1]);
dirty |= !std::isnan(system_constants_.ndc_scale[2]);
dirty |= !std::isnan(system_constants_.ndc_offset[0]);
dirty |= !std::isnan(system_constants_.ndc_offset[1]);
dirty |= !std::isnan(system_constants_.ndc_offset[2]);
float nan_value = std::nanf("");
system_constants_.ndc_scale[0] = nan_value;
system_constants_.ndc_scale[1] = nan_value;
system_constants_.ndc_scale[2] = nan_value;
system_constants_.ndc_offset[0] = nan_value;
system_constants_.ndc_offset[1] = nan_value;
system_constants_.ndc_offset[2] = nan_value;
} else {
if (pa_cl_vte_cntl.vport_x_scale_ena) {
ndc_scale_x = viewport_scale_x >= 0.0f ? 1.0f : -1.0f;
} else {
ndc_scale_x = 1.0f / 1280.0f;
}
if (pa_cl_vte_cntl.vport_y_scale_ena) {
ndc_scale_y = viewport_scale_y >= 0.0f ? -1.0f : 1.0f;
} else {
ndc_scale_y = -1.0f / 1280.0f;
}
ndc_scale_z = gl_clip_space_def ? 0.5f : 1.0f;
}
float ndc_offset_x = pa_cl_vte_cntl.vport_x_offset_ena ? 0.0f : -1.0f;
float ndc_offset_y = pa_cl_vte_cntl.vport_y_offset_ena ? 0.0f : 1.0f;
float ndc_offset_z = gl_clip_space_def ? 0.5f : 0.0f;
// Like in OpenGL - VPOS giving pixel centers.
// TODO(Triang3l): Check if ps_param_gen should give center positions in
// OpenGL mode on the Xbox 360.
float pixel_half_pixel_offset = 0.5f;
if (cvars::d3d12_half_pixel_offset && !pa_su_vtx_cntl.pix_center) {
// Signs are hopefully correct here, tested in GTA IV on both clearing
// (without a viewport) and drawing things near the edges of the screen.
if (pa_cl_vte_cntl.vport_x_scale_ena) {
if (viewport_scale_x != 0.0f) {
ndc_offset_x += 0.5f / viewport_scale_x;
// When VPORT_Z_SCALE_ENA is disabled, Z/W is directly what is expected to
// be written to the depth buffer, and for some reason DX_CLIP_SPACE_DEF
// isn't set in this case in draws in games.
bool gl_clip_space_def =
!pa_cl_clip_cntl.dx_clip_space_def && pa_cl_vte_cntl.vport_z_scale_ena;
float ndc_scale_x = pa_cl_vte_cntl.vport_x_scale_ena
? (viewport_scale_x >= 0.0f ? 1.0f : -1.0f)
: (1.0f / 1280.0f);
float ndc_scale_y = pa_cl_vte_cntl.vport_y_scale_ena
? (viewport_scale_y >= 0.0f ? -1.0f : 1.0f)
: (-1.0f / 1280.0f);
float ndc_scale_z = gl_clip_space_def ? 0.5f : 1.0f;
float ndc_offset_x = pa_cl_vte_cntl.vport_x_offset_ena ? 0.0f : -1.0f;
float ndc_offset_y = pa_cl_vte_cntl.vport_y_offset_ena ? 0.0f : 1.0f;
float ndc_offset_z = gl_clip_space_def ? 0.5f : 0.0f;
if (half_pixel_offset) {
// Signs are hopefully correct here, tested in GTA IV on both clearing
// (without a viewport) and drawing things near the edges of the screen.
if (pa_cl_vte_cntl.vport_x_scale_ena) {
if (viewport_scale_x != 0.0f) {
ndc_offset_x += 0.5f / viewport_scale_x;
}
} else {
ndc_offset_x += 1.0f / 2560.0f;
}
} else {
ndc_offset_x += 1.0f / 2560.0f;
}
if (pa_cl_vte_cntl.vport_y_scale_ena) {
if (viewport_scale_y != 0.0f) {
ndc_offset_y += 0.5f / viewport_scale_y;
if (pa_cl_vte_cntl.vport_y_scale_ena) {
if (viewport_scale_y != 0.0f) {
ndc_offset_y += 0.5f / viewport_scale_y;
}
} else {
ndc_offset_y -= 1.0f / 2560.0f;
}
} else {
ndc_offset_y -= 1.0f / 2560.0f;
}
// Like in Direct3D 9 - VPOS giving the top-left corner.
pixel_half_pixel_offset = 0.0f;
dirty |= system_constants_.ndc_scale[0] != ndc_scale_x;
dirty |= system_constants_.ndc_scale[1] != ndc_scale_y;
dirty |= system_constants_.ndc_scale[2] != ndc_scale_z;
dirty |= system_constants_.ndc_offset[0] != ndc_offset_x;
dirty |= system_constants_.ndc_offset[1] != ndc_offset_y;
dirty |= system_constants_.ndc_offset[2] != ndc_offset_z;
system_constants_.ndc_scale[0] = ndc_scale_x;
system_constants_.ndc_scale[1] = ndc_scale_y;
system_constants_.ndc_scale[2] = ndc_scale_z;
system_constants_.ndc_offset[0] = ndc_offset_x;
system_constants_.ndc_offset[1] = ndc_offset_y;
system_constants_.ndc_offset[2] = ndc_offset_z;
}
dirty |= system_constants_.ndc_scale[0] != ndc_scale_x;
dirty |= system_constants_.ndc_scale[1] != ndc_scale_y;
dirty |= system_constants_.ndc_scale[2] != ndc_scale_z;
dirty |= system_constants_.ndc_offset[0] != ndc_offset_x;
dirty |= system_constants_.ndc_offset[1] != ndc_offset_y;
dirty |= system_constants_.ndc_offset[2] != ndc_offset_z;
dirty |= system_constants_.pixel_half_pixel_offset != pixel_half_pixel_offset;
system_constants_.ndc_scale[0] = ndc_scale_x;
system_constants_.ndc_scale[1] = ndc_scale_y;
system_constants_.ndc_scale[2] = ndc_scale_z;
system_constants_.ndc_offset[0] = ndc_offset_x;
system_constants_.ndc_offset[1] = ndc_offset_y;
system_constants_.ndc_offset[2] = ndc_offset_z;
system_constants_.pixel_half_pixel_offset = pixel_half_pixel_offset;
// Point size.
float point_size_x = float(pa_su_point_size.width) * 0.125f;