[GPU] Viewport in draw_util with host API flexibility

This commit is contained in:
Triang3l
2020-11-16 23:03:42 +03:00
parent eda027a220
commit 10d7bcde93
10 changed files with 471 additions and 398 deletions

View File

@@ -1996,15 +1996,44 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type,
current_external_pipeline_ = nullptr;
}
// Get dynamic rasterizer state.
// Supersampling replacing multisampling due to difficulties of emulating
// EDRAM with multisampling with RTV/DSV (with ROV, there's MSAA), and also
// resolution scale.
uint32_t pixel_size_x, pixel_size_y;
if (edram_rov_used_) {
pixel_size_x = 1;
pixel_size_y = 1;
} else {
xenos::MsaaSamples msaa_samples =
regs.Get<reg::RB_SURFACE_INFO>().msaa_samples;
pixel_size_x = msaa_samples >= xenos::MsaaSamples::k4X ? 2 : 1;
pixel_size_y = msaa_samples >= xenos::MsaaSamples::k2X ? 2 : 1;
}
if (texture_cache_->IsResolutionScale2X()) {
pixel_size_x *= 2;
pixel_size_y *= 2;
}
draw_util::ViewportInfo viewport_info;
draw_util::GetHostViewportInfo(regs, float(pixel_size_x), float(pixel_size_y),
true, float(D3D12_VIEWPORT_BOUNDS_MAX), false,
viewport_info);
draw_util::Scissor scissor;
draw_util::GetScissor(regs, scissor);
scissor.left *= pixel_size_x;
scissor.top *= pixel_size_y;
scissor.width *= pixel_size_x;
scissor.height *= pixel_size_y;
// Update viewport, scissor, blend factor and stencil reference.
UpdateFixedFunctionState(primitive_two_faced);
UpdateFixedFunctionState(viewport_info, scissor, primitive_two_faced);
// Update system constants before uploading them.
UpdateSystemConstantValues(
memexport_used, primitive_two_faced, line_loop_closing_index,
indexed ? index_buffer_info->endianness : xenos::Endian::kNone,
used_texture_mask, early_z, GetCurrentColorMask(pixel_shader),
pipeline_render_targets);
viewport_info, pixel_size_x, pixel_size_y, used_texture_mask, early_z,
GetCurrentColorMask(pixel_shader), pipeline_render_targets);
// Update constant buffers, descriptors and root parameters.
if (!UpdateBindings(vertex_shader, pixel_shader, root_signature)) {
@@ -2753,87 +2782,21 @@ void D3D12CommandProcessor::ClearCommandAllocatorCache() {
command_allocator_writable_last_ = nullptr;
}
void D3D12CommandProcessor::UpdateFixedFunctionState(bool primitive_two_faced) {
void D3D12CommandProcessor::UpdateFixedFunctionState(
const draw_util::ViewportInfo& viewport_info,
const draw_util::Scissor& scissor, bool primitive_two_faced) {
#if XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
SCOPE_profile_cpu_f("gpu");
#endif // XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
const RegisterFile& regs = *register_file_;
// Window parameters.
// http://ftp.tku.edu.tw/NetBSD/NetBSD-current/xsrc/external/mit/xf86-video-ati/dist/src/r600_reg_auto_r6xx.h
// See r200UpdateWindow:
// https://github.com/freedreno/mesa/blob/master/src/mesa/drivers/dri/r200/r200_state.c
auto pa_sc_window_offset = regs.Get<reg::PA_SC_WINDOW_OFFSET>();
// Supersampling replacing multisampling due to difficulties of emulating
// EDRAM with multisampling with RTV/DSV (with ROV, there's MSAA), and also
// resolution scale.
uint32_t pixel_size_x, pixel_size_y;
if (edram_rov_used_) {
pixel_size_x = 1;
pixel_size_y = 1;
} else {
xenos::MsaaSamples msaa_samples =
regs.Get<reg::RB_SURFACE_INFO>().msaa_samples;
pixel_size_x = msaa_samples >= xenos::MsaaSamples::k4X ? 2 : 1;
pixel_size_y = msaa_samples >= xenos::MsaaSamples::k2X ? 2 : 1;
}
if (texture_cache_->IsResolutionScale2X()) {
pixel_size_x *= 2;
pixel_size_y *= 2;
}
// Viewport.
// PA_CL_VTE_CNTL contains whether offsets and scales are enabled.
// http://www.x.org/docs/AMD/old/evergreen_3D_registers_v2.pdf
// In games, either all are enabled (for regular drawing) or none are (for
// rectangle lists usually).
//
// If scale/offset is enabled, the Xenos shader is writing (neglecting W
// division) position in the NDC (-1, -1, dx_clip_space_def - 1) -> (1, 1, 1)
// box. If it's not, the position is in screen space. Since we can only use
// the NDC in PC APIs, we use a viewport of the largest possible size, and
// divide the position by it in translated shaders.
auto pa_cl_vte_cntl = regs.Get<reg::PA_CL_VTE_CNTL>();
float viewport_scale_x =
pa_cl_vte_cntl.vport_x_scale_ena
? std::abs(regs[XE_GPU_REG_PA_CL_VPORT_XSCALE].f32)
: 4096.0f;
float viewport_scale_y =
pa_cl_vte_cntl.vport_y_scale_ena
? std::abs(regs[XE_GPU_REG_PA_CL_VPORT_YSCALE].f32)
: 4096.0f;
float viewport_scale_z = pa_cl_vte_cntl.vport_z_scale_ena
? regs[XE_GPU_REG_PA_CL_VPORT_ZSCALE].f32
: 1.0f;
float viewport_offset_x = pa_cl_vte_cntl.vport_x_offset_ena
? regs[XE_GPU_REG_PA_CL_VPORT_XOFFSET].f32
: std::abs(viewport_scale_x);
float viewport_offset_y = pa_cl_vte_cntl.vport_y_offset_ena
? regs[XE_GPU_REG_PA_CL_VPORT_YOFFSET].f32
: std::abs(viewport_scale_y);
float viewport_offset_z = pa_cl_vte_cntl.vport_z_offset_ena
? regs[XE_GPU_REG_PA_CL_VPORT_ZOFFSET].f32
: 0.0f;
if (regs.Get<reg::PA_SU_SC_MODE_CNTL>().vtx_window_offset_enable) {
viewport_offset_x += float(pa_sc_window_offset.window_x_offset);
viewport_offset_y += float(pa_sc_window_offset.window_y_offset);
}
D3D12_VIEWPORT viewport;
viewport.TopLeftX =
(viewport_offset_x - viewport_scale_x) * float(pixel_size_x);
viewport.TopLeftY =
(viewport_offset_y - viewport_scale_y) * float(pixel_size_y);
viewport.Width = viewport_scale_x * 2.0f * float(pixel_size_x);
viewport.Height = viewport_scale_y * 2.0f * float(pixel_size_y);
viewport.MinDepth = viewport_offset_z;
viewport.MaxDepth = viewport_offset_z + viewport_scale_z;
if (viewport_scale_z < 0.0f) {
// MinDepth > MaxDepth doesn't work on Nvidia, emulating it in vertex
// shaders and when applying polygon offset.
std::swap(viewport.MinDepth, viewport.MaxDepth);
}
viewport.TopLeftX = viewport_info.left;
viewport.TopLeftY = viewport_info.top;
viewport.Width = viewport_info.width;
viewport.Height = viewport_info.height;
viewport.MinDepth = viewport_info.z_min;
viewport.MaxDepth = viewport_info.z_max;
ff_viewport_update_needed_ |= ff_viewport_.TopLeftX != viewport.TopLeftX;
ff_viewport_update_needed_ |= ff_viewport_.TopLeftY != viewport.TopLeftY;
ff_viewport_update_needed_ |= ff_viewport_.Width != viewport.Width;
@@ -2847,13 +2810,11 @@ void D3D12CommandProcessor::UpdateFixedFunctionState(bool primitive_two_faced) {
}
// Scissor.
draw_util::Scissor scissor;
draw_util::GetScissor(regs, scissor);
D3D12_RECT scissor_rect;
scissor_rect.left = LONG(scissor.left * pixel_size_x);
scissor_rect.top = LONG(scissor.top * pixel_size_y);
scissor_rect.right = LONG((scissor.left + scissor.width) * pixel_size_x);
scissor_rect.bottom = LONG((scissor.top + scissor.height) * pixel_size_y);
scissor_rect.left = LONG(scissor.left);
scissor_rect.top = LONG(scissor.top);
scissor_rect.right = LONG(scissor.left + scissor.width);
scissor_rect.bottom = LONG(scissor.top + scissor.height);
ff_scissor_update_needed_ |= ff_scissor_.left != scissor_rect.left;
ff_scissor_update_needed_ |= ff_scissor_.top != scissor_rect.top;
ff_scissor_update_needed_ |= ff_scissor_.right != scissor_rect.right;
@@ -2865,6 +2826,8 @@ void D3D12CommandProcessor::UpdateFixedFunctionState(bool primitive_two_faced) {
}
if (!edram_rov_used_) {
const RegisterFile& regs = *register_file_;
// Blend factor.
ff_blend_factor_update_needed_ |=
ff_blend_factor_[0] != regs[XE_GPU_REG_RB_BLEND_RED].f32;
@@ -2908,7 +2871,9 @@ void D3D12CommandProcessor::UpdateFixedFunctionState(bool primitive_two_faced) {
void D3D12CommandProcessor::UpdateSystemConstantValues(
bool shared_memory_is_uav, bool primitive_two_faced,
uint32_t line_loop_closing_index, xenos::Endian index_endian,
uint32_t used_texture_mask, bool early_z, uint32_t color_mask,
const draw_util::ViewportInfo& viewport_info, uint32_t pixel_size_x,
uint32_t pixel_size_y, uint32_t used_texture_mask, bool early_z,
uint32_t color_mask,
const RenderTargetCache::PipelineRenderTarget render_targets[4]) {
#if XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
SCOPE_profile_cpu_f("gpu");
@@ -2920,7 +2885,6 @@ void D3D12CommandProcessor::UpdateSystemConstantValues(
auto pa_su_point_minmax = regs.Get<reg::PA_SU_POINT_MINMAX>();
auto pa_su_point_size = regs.Get<reg::PA_SU_POINT_SIZE>();
auto pa_su_sc_mode_cntl = regs.Get<reg::PA_SU_SC_MODE_CNTL>();
auto pa_su_vtx_cntl = regs.Get<reg::PA_SU_VTX_CNTL>();
float rb_alpha_ref = regs[XE_GPU_REG_RB_ALPHA_REF].f32;
auto rb_colorcontrol = regs.Get<reg::RB_COLORCONTROL>();
auto rb_depth_info = regs.Get<reg::RB_DEPTH_INFO>();
@@ -2986,11 +2950,6 @@ void D3D12CommandProcessor::UpdateSystemConstantValues(
}
}
// Get viewport Z scale - needed for flags and ROV output.
float viewport_scale_z = pa_cl_vte_cntl.vport_z_scale_ena
? regs[XE_GPU_REG_PA_CL_VPORT_ZSCALE].f32
: 1.0f;
bool dirty = false;
// Flags.
@@ -3023,10 +2982,6 @@ void D3D12CommandProcessor::UpdateSystemConstantValues(
flags |= (pa_cl_clip_cntl.value & 0b111111)
<< DxbcShaderTranslator::kSysFlag_UserClipPlane0_Shift;
}
// Reversed depth.
if (viewport_scale_z < 0.0f) {
flags |= DxbcShaderTranslator::kSysFlag_ReverseZ;
}
// Whether SV_IsFrontFace matters.
if (primitive_two_faced) {
flags |= DxbcShaderTranslator::kSysFlag_PrimitiveTwoFaced;
@@ -3122,81 +3077,24 @@ void D3D12CommandProcessor::UpdateSystemConstantValues(
}
// 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
// shaders (for rectangle list drawing, for instance) to the viewport of the
// largest possible render target size that is used to emulate unnormalized
// coordinates. Z scale/offset is to convert from OpenGL NDC to Direct3D NDC
// if needed. Also apply half-pixel offset to reproduce Direct3D 9
// rasterization rules - must be done before clipping, not through the
// viewport, for SSAA and resolution scale to work correctly.
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;
// 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 {
// 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 / 4096.0f);
float ndc_scale_y = pa_cl_vte_cntl.vport_y_scale_ena
? (viewport_scale_y >= 0.0f ? -1.0f : 1.0f)
: (-1.0f / 4096.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 (cvars::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;
}
} else {
ndc_offset_x += 1.0f / xenos::kTexture2DCubeMaxWidthHeight;
}
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 / xenos::kTexture2DCubeMaxWidthHeight;
}
for (uint32_t i = 0; i < 3; ++i) {
dirty |= !std::isnan(system_constants_.ndc_scale[i]);
system_constants_.ndc_scale[i] = nan_value;
}
} else {
for (uint32_t i = 0; i < 3; ++i) {
dirty |= system_constants_.ndc_scale[i] != viewport_info.ndc_scale[i];
dirty |= system_constants_.ndc_offset[i] != viewport_info.ndc_offset[i];
system_constants_.ndc_scale[i] = viewport_info.ndc_scale[i];
system_constants_.ndc_offset[i] = viewport_info.ndc_offset[i];
}
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;
}
// Point size.
@@ -3212,19 +3110,10 @@ void D3D12CommandProcessor::UpdateSystemConstantValues(
system_constants_.point_size[1] = point_size_y;
system_constants_.point_size_min_max[0] = point_size_min;
system_constants_.point_size_min_max[1] = point_size_max;
float point_screen_to_ndc_x, point_screen_to_ndc_y;
if (pa_cl_vte_cntl.vport_x_scale_ena) {
point_screen_to_ndc_x =
(viewport_scale_x != 0.0f) ? (0.5f / viewport_scale_x) : 0.0f;
} else {
point_screen_to_ndc_x = 1.0f / xenos::kTexture2DCubeMaxWidthHeight;
}
if (pa_cl_vte_cntl.vport_y_scale_ena) {
point_screen_to_ndc_y =
(viewport_scale_y != 0.0f) ? (-0.5f / viewport_scale_y) : 0.0f;
} else {
point_screen_to_ndc_y = -1.0f / xenos::kTexture2DCubeMaxWidthHeight;
}
float point_screen_to_ndc_x =
(0.5f * 2.0f * pixel_size_x) / viewport_info.width;
float point_screen_to_ndc_y =
(0.5f * 2.0f * pixel_size_y) / viewport_info.height;
dirty |= system_constants_.point_screen_to_ndc[0] != point_screen_to_ndc_x;
dirty |= system_constants_.point_screen_to_ndc[1] != point_screen_to_ndc_y;
system_constants_.point_screen_to_ndc[0] = point_screen_to_ndc_x;
@@ -3374,20 +3263,11 @@ void D3D12CommandProcessor::UpdateSystemConstantValues(
dirty |= system_constants_.edram_depth_base_dwords != depth_base_dwords;
system_constants_.edram_depth_base_dwords = depth_base_dwords;
// The Z range is reversed in the vertex shader if it's reverse - use the
// absolute value of the scale.
float depth_range_scale = std::abs(viewport_scale_z);
float depth_range_scale = viewport_info.z_max - viewport_info.z_min;
dirty |= system_constants_.edram_depth_range_scale != depth_range_scale;
system_constants_.edram_depth_range_scale = depth_range_scale;
float depth_range_offset = pa_cl_vte_cntl.vport_z_offset_ena
? regs[XE_GPU_REG_PA_CL_VPORT_ZOFFSET].f32
: 0.0f;
if (viewport_scale_z < 0.0f) {
// Similar to MinDepth in fixed-function viewport calculation.
depth_range_offset += viewport_scale_z;
}
dirty |= system_constants_.edram_depth_range_offset != depth_range_offset;
system_constants_.edram_depth_range_offset = depth_range_offset;
dirty |= system_constants_.edram_depth_range_offset != viewport_info.z_min;
system_constants_.edram_depth_range_offset = viewport_info.z_min;
// For non-polygons, front polygon offset is used, and it's enabled if
// POLY_OFFSET_PARA_ENABLED is set, for polygons, separate front and back