[GPU] Vulkan fragment shader interlock RB and related fixes/cleanup
Also fixes addressing of MSAA samples 2 and 3 for 64bpp color render targets in the ROV RB implementation on Direct3D 12. Additionally, with FSI/ROV, alpha test and alpha to coverage are done only if the render target 0 was dynamically written to (according to the Direct3D 9 rules for writing to color render targets, though not sure if they actually apply to the alpha tests on Direct3D 9, but for safety). There is also some code cleanup for things spotted during the development of the feature.
This commit is contained in:
parent
9ab4db285c
commit
45050b2380
|
@ -3189,15 +3189,14 @@ void D3D12CommandProcessor::UpdateSystemConstantValues(
|
|||
// flow.
|
||||
reg::RB_COLOR_INFO color_infos[4];
|
||||
float rt_clamp[4][4];
|
||||
// Two UINT32_MAX if no components actually existing in the RT are written.
|
||||
uint32_t rt_keep_masks[4][2];
|
||||
for (uint32_t i = 0; i < 4; ++i) {
|
||||
auto color_info = regs.Get<reg::RB_COLOR_INFO>(
|
||||
reg::RB_COLOR_INFO::rt_register_indices[i]);
|
||||
color_infos[i] = color_info;
|
||||
if (edram_rov_used) {
|
||||
// Get the mask for keeping previous color's components unmodified,
|
||||
// or two UINT32_MAX if no colors actually existing in the RT are written.
|
||||
DxbcShaderTranslator::ROV_GetColorFormatSystemConstants(
|
||||
RenderTargetCache::GetPSIColorFormatInfo(
|
||||
color_info.color_format, (normalized_color_mask >> (i * 4)) & 0b1111,
|
||||
rt_clamp[i][0], rt_clamp[i][1], rt_clamp[i][2], rt_clamp[i][3],
|
||||
rt_keep_masks[i][0], rt_keep_masks[i][1]);
|
||||
|
@ -3506,8 +3505,8 @@ void D3D12CommandProcessor::UpdateSystemConstantValues(
|
|||
rt_base_dwords_scaled;
|
||||
system_constants_.edram_rt_base_dwords_scaled[i] =
|
||||
rt_base_dwords_scaled;
|
||||
uint32_t format_flags = DxbcShaderTranslator::ROV_AddColorFormatFlags(
|
||||
color_info.color_format);
|
||||
uint32_t format_flags =
|
||||
RenderTargetCache::AddPSIColorFormatFlags(color_info.color_format);
|
||||
dirty |= system_constants_.edram_rt_format_flags[i] != format_flags;
|
||||
system_constants_.edram_rt_format_flags[i] = format_flags;
|
||||
// Can't do float comparisons here because NaNs would result in always
|
||||
|
|
|
@ -267,19 +267,6 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
|||
};
|
||||
static_assert(kSysFlag_Count <= 32, "Too many flags in the system constants");
|
||||
|
||||
// Appended to the format in the format constant.
|
||||
enum : uint32_t {
|
||||
// Starting from bit 4 because the format itself needs 4 bits.
|
||||
kRTFormatFlag_64bpp_Shift = 4,
|
||||
// Requires clamping of blending sources and factors.
|
||||
kRTFormatFlag_FixedPointColor_Shift,
|
||||
kRTFormatFlag_FixedPointAlpha_Shift,
|
||||
|
||||
kRTFormatFlag_64bpp = 1u << kRTFormatFlag_64bpp_Shift,
|
||||
kRTFormatFlag_FixedPointColor = 1u << kRTFormatFlag_FixedPointColor_Shift,
|
||||
kRTFormatFlag_FixedPointAlpha = 1u << kRTFormatFlag_FixedPointAlpha_Shift,
|
||||
};
|
||||
|
||||
// IF SYSTEM CONSTANTS ARE CHANGED OR ADDED, THE FOLLOWING MUST BE UPDATED:
|
||||
// - SystemConstants::Index enum.
|
||||
// - system_constant_rdef_.
|
||||
|
@ -383,7 +370,8 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
|||
|
||||
uint32_t edram_rt_base_dwords_scaled[4];
|
||||
|
||||
// RT format combined with kRTFormatFlags.
|
||||
// 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.
|
||||
|
@ -524,40 +512,6 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
|||
kEdram,
|
||||
};
|
||||
|
||||
// Returns the format with internal flags for passing via the
|
||||
// edram_rt_format_flags system constant.
|
||||
static constexpr uint32_t ROV_AddColorFormatFlags(
|
||||
xenos::ColorRenderTargetFormat format) {
|
||||
uint32_t format_flags = uint32_t(format);
|
||||
if (format == xenos::ColorRenderTargetFormat::k_16_16_16_16 ||
|
||||
format == xenos::ColorRenderTargetFormat::k_16_16_16_16_FLOAT ||
|
||||
format == xenos::ColorRenderTargetFormat::k_32_32_FLOAT) {
|
||||
format_flags |= kRTFormatFlag_64bpp;
|
||||
}
|
||||
if (format == xenos::ColorRenderTargetFormat::k_8_8_8_8 ||
|
||||
format == xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA ||
|
||||
format == xenos::ColorRenderTargetFormat::k_2_10_10_10 ||
|
||||
format == xenos::ColorRenderTargetFormat::k_16_16 ||
|
||||
format == xenos::ColorRenderTargetFormat::k_16_16_16_16 ||
|
||||
format == xenos::ColorRenderTargetFormat::k_2_10_10_10_AS_10_10_10_10) {
|
||||
format_flags |=
|
||||
kRTFormatFlag_FixedPointColor | kRTFormatFlag_FixedPointAlpha;
|
||||
} else if (format == xenos::ColorRenderTargetFormat::k_2_10_10_10_FLOAT ||
|
||||
format == xenos::ColorRenderTargetFormat::
|
||||
k_2_10_10_10_FLOAT_AS_16_16_16_16) {
|
||||
format_flags |= kRTFormatFlag_FixedPointAlpha;
|
||||
}
|
||||
return format_flags;
|
||||
}
|
||||
// Returns the bits that need to be added to the RT flags constant - needs to
|
||||
// be done externally, not in SetColorFormatConstants, because the flags
|
||||
// contain other state.
|
||||
static void ROV_GetColorFormatSystemConstants(
|
||||
xenos::ColorRenderTargetFormat format, uint32_t write_mask,
|
||||
float& clamp_rgb_low, float& clamp_alpha_low, float& clamp_rgb_high,
|
||||
float& clamp_alpha_high, uint32_t& keep_mask_low,
|
||||
uint32_t& keep_mask_high);
|
||||
|
||||
uint64_t GetDefaultVertexShaderModification(
|
||||
uint32_t dynamic_addressable_register_count,
|
||||
Shader::HostVertexShaderType host_vertex_shader_type =
|
||||
|
@ -772,6 +726,7 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
|||
// 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().is_valid_memexport_used();
|
||||
}
|
||||
|
|
|
@ -14,139 +14,13 @@
|
|||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/gpu/draw_util.h"
|
||||
#include "xenia/gpu/render_target_cache.h"
|
||||
#include "xenia/gpu/texture_cache.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
using namespace ucode;
|
||||
|
||||
void DxbcShaderTranslator::ROV_GetColorFormatSystemConstants(
|
||||
xenos::ColorRenderTargetFormat format, uint32_t write_mask,
|
||||
float& clamp_rgb_low, float& clamp_alpha_low, float& clamp_rgb_high,
|
||||
float& clamp_alpha_high, uint32_t& keep_mask_low,
|
||||
uint32_t& keep_mask_high) {
|
||||
keep_mask_low = keep_mask_high = 0;
|
||||
switch (format) {
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8:
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA: {
|
||||
clamp_rgb_low = clamp_alpha_low = 0.0f;
|
||||
clamp_rgb_high = clamp_alpha_high = 1.0f;
|
||||
for (uint32_t i = 0; i < 4; ++i) {
|
||||
if (!(write_mask & (1 << i))) {
|
||||
keep_mask_low |= uint32_t(0xFF) << (i * 8);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case xenos::ColorRenderTargetFormat::k_2_10_10_10:
|
||||
case xenos::ColorRenderTargetFormat::k_2_10_10_10_AS_10_10_10_10: {
|
||||
clamp_rgb_low = clamp_alpha_low = 0.0f;
|
||||
clamp_rgb_high = clamp_alpha_high = 1.0f;
|
||||
for (uint32_t i = 0; i < 3; ++i) {
|
||||
if (!(write_mask & (1 << i))) {
|
||||
keep_mask_low |= uint32_t(0x3FF) << (i * 10);
|
||||
}
|
||||
}
|
||||
if (!(write_mask & 0b1000)) {
|
||||
keep_mask_low |= uint32_t(3) << 30;
|
||||
}
|
||||
} break;
|
||||
case xenos::ColorRenderTargetFormat::k_2_10_10_10_FLOAT:
|
||||
case xenos::ColorRenderTargetFormat::k_2_10_10_10_FLOAT_AS_16_16_16_16: {
|
||||
clamp_rgb_low = clamp_alpha_low = 0.0f;
|
||||
clamp_rgb_high = 31.875f;
|
||||
clamp_alpha_high = 1.0f;
|
||||
for (uint32_t i = 0; i < 3; ++i) {
|
||||
if (!(write_mask & (1 << i))) {
|
||||
keep_mask_low |= uint32_t(0x3FF) << (i * 10);
|
||||
}
|
||||
}
|
||||
if (!(write_mask & 0b1000)) {
|
||||
keep_mask_low |= uint32_t(3) << 30;
|
||||
}
|
||||
} break;
|
||||
case xenos::ColorRenderTargetFormat::k_16_16:
|
||||
case xenos::ColorRenderTargetFormat::k_16_16_16_16:
|
||||
// Alpha clamping affects blending source, so it's non-zero for alpha for
|
||||
// k_16_16 (the render target is fixed-point). There's one deviation from
|
||||
// how Direct3D 11.3 functional specification defines SNorm conversion
|
||||
// (NaN should be 0, not the lowest negative number), but NaN handling in
|
||||
// output shouldn't be very important.
|
||||
clamp_rgb_low = clamp_alpha_low = -32.0f;
|
||||
clamp_rgb_high = clamp_alpha_high = 32.0f;
|
||||
if (!(write_mask & 0b0001)) {
|
||||
keep_mask_low |= 0xFFFFu;
|
||||
}
|
||||
if (!(write_mask & 0b0010)) {
|
||||
keep_mask_low |= 0xFFFF0000u;
|
||||
}
|
||||
if (format == xenos::ColorRenderTargetFormat::k_16_16_16_16) {
|
||||
if (!(write_mask & 0b0100)) {
|
||||
keep_mask_high |= 0xFFFFu;
|
||||
}
|
||||
if (!(write_mask & 0b1000)) {
|
||||
keep_mask_high |= 0xFFFF0000u;
|
||||
}
|
||||
} else {
|
||||
write_mask &= 0b0011;
|
||||
}
|
||||
break;
|
||||
case xenos::ColorRenderTargetFormat::k_16_16_FLOAT:
|
||||
case xenos::ColorRenderTargetFormat::k_16_16_16_16_FLOAT:
|
||||
// No NaNs on the Xbox 360 GPU, though can't use the extended range with
|
||||
// f32tof16.
|
||||
clamp_rgb_low = clamp_alpha_low = -65504.0f;
|
||||
clamp_rgb_high = clamp_alpha_high = 65504.0f;
|
||||
if (!(write_mask & 0b0001)) {
|
||||
keep_mask_low |= 0xFFFFu;
|
||||
}
|
||||
if (!(write_mask & 0b0010)) {
|
||||
keep_mask_low |= 0xFFFF0000u;
|
||||
}
|
||||
if (format == xenos::ColorRenderTargetFormat::k_16_16_16_16_FLOAT) {
|
||||
if (!(write_mask & 0b0100)) {
|
||||
keep_mask_high |= 0xFFFFu;
|
||||
}
|
||||
if (!(write_mask & 0b1000)) {
|
||||
keep_mask_high |= 0xFFFF0000u;
|
||||
}
|
||||
} else {
|
||||
write_mask &= 0b0011;
|
||||
}
|
||||
break;
|
||||
case xenos::ColorRenderTargetFormat::k_32_FLOAT:
|
||||
// No clamping - let min/max always pick the original value.
|
||||
clamp_rgb_low = clamp_alpha_low = clamp_rgb_high = clamp_alpha_high =
|
||||
std::nanf("");
|
||||
write_mask &= 0b0001;
|
||||
if (!(write_mask & 0b0001)) {
|
||||
keep_mask_low = ~uint32_t(0);
|
||||
}
|
||||
break;
|
||||
case xenos::ColorRenderTargetFormat::k_32_32_FLOAT:
|
||||
// No clamping - let min/max always pick the original value.
|
||||
clamp_rgb_low = clamp_alpha_low = clamp_rgb_high = clamp_alpha_high =
|
||||
std::nanf("");
|
||||
write_mask &= 0b0011;
|
||||
if (!(write_mask & 0b0001)) {
|
||||
keep_mask_low = ~uint32_t(0);
|
||||
}
|
||||
if (!(write_mask & 0b0010)) {
|
||||
keep_mask_high = ~uint32_t(0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert_unhandled_case(format);
|
||||
// Disable invalid render targets.
|
||||
write_mask = 0;
|
||||
break;
|
||||
}
|
||||
// Special case handled in the shaders for empty write mask to completely skip
|
||||
// a disabled render target: all keep bits are set.
|
||||
if (!write_mask) {
|
||||
keep_mask_low = keep_mask_high = ~uint32_t(0);
|
||||
}
|
||||
}
|
||||
|
||||
void DxbcShaderTranslator::StartPixelShader_LoadROVParameters() {
|
||||
bool any_color_targets_written = current_shader().writes_color_targets() != 0;
|
||||
|
||||
|
@ -484,8 +358,8 @@ void DxbcShaderTranslator::StartPixelShader_LoadROVParameters() {
|
|||
{
|
||||
// Copy the 4x AA coverage to system_temp_rov_params_.x, making top-right
|
||||
// the sample [2] and bottom-left the sample [1] (the opposite of Direct3D
|
||||
// 12), because on the Xbox 360, 2x MSAA doubles the storage width, 4x MSAA
|
||||
// doubles the storage height.
|
||||
// 12), because on the Xbox 360, 2x MSAA doubles the storage height, 4x MSAA
|
||||
// doubles the storage width.
|
||||
// Flip samples in bits 0:1 to bits 29:30.
|
||||
a_.OpBFRev(dxbc::Dest::R(system_temp_rov_params_, 0b0001),
|
||||
dxbc::Src::VCoverage());
|
||||
|
@ -1304,7 +1178,7 @@ void DxbcShaderTranslator::ROV_UnpackColor(
|
|||
// k_8_8_8_8_GAMMA
|
||||
// ***************************************************************************
|
||||
for (uint32_t i = 0; i < 2; ++i) {
|
||||
a_.OpCase(dxbc::Src::LU(ROV_AddColorFormatFlags(
|
||||
a_.OpCase(dxbc::Src::LU(RenderTargetCache::AddPSIColorFormatFlags(
|
||||
i ? xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA
|
||||
: xenos::ColorRenderTargetFormat::k_8_8_8_8)));
|
||||
// Unpack the components.
|
||||
|
@ -1328,9 +1202,9 @@ void DxbcShaderTranslator::ROV_UnpackColor(
|
|||
// k_2_10_10_10
|
||||
// k_2_10_10_10_AS_10_10_10_10
|
||||
// ***************************************************************************
|
||||
a_.OpCase(dxbc::Src::LU(
|
||||
ROV_AddColorFormatFlags(xenos::ColorRenderTargetFormat::k_2_10_10_10)));
|
||||
a_.OpCase(dxbc::Src::LU(ROV_AddColorFormatFlags(
|
||||
a_.OpCase(dxbc::Src::LU(RenderTargetCache::AddPSIColorFormatFlags(
|
||||
xenos::ColorRenderTargetFormat::k_2_10_10_10)));
|
||||
a_.OpCase(dxbc::Src::LU(RenderTargetCache::AddPSIColorFormatFlags(
|
||||
xenos::ColorRenderTargetFormat::k_2_10_10_10_AS_10_10_10_10)));
|
||||
{
|
||||
// Unpack the components.
|
||||
|
@ -1350,9 +1224,9 @@ void DxbcShaderTranslator::ROV_UnpackColor(
|
|||
// k_2_10_10_10_FLOAT_AS_16_16_16_16
|
||||
// https://github.com/Microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexConvert.cpp
|
||||
// ***************************************************************************
|
||||
a_.OpCase(dxbc::Src::LU(ROV_AddColorFormatFlags(
|
||||
a_.OpCase(dxbc::Src::LU(RenderTargetCache::AddPSIColorFormatFlags(
|
||||
xenos::ColorRenderTargetFormat::k_2_10_10_10_FLOAT)));
|
||||
a_.OpCase(dxbc::Src::LU(ROV_AddColorFormatFlags(
|
||||
a_.OpCase(dxbc::Src::LU(RenderTargetCache::AddPSIColorFormatFlags(
|
||||
xenos::ColorRenderTargetFormat::k_2_10_10_10_FLOAT_AS_16_16_16_16)));
|
||||
{
|
||||
// Unpack the alpha.
|
||||
|
@ -1381,7 +1255,7 @@ void DxbcShaderTranslator::ROV_UnpackColor(
|
|||
// k_16_16_16_16 (64bpp)
|
||||
// ***************************************************************************
|
||||
for (uint32_t i = 0; i < 2; ++i) {
|
||||
a_.OpCase(dxbc::Src::LU(ROV_AddColorFormatFlags(
|
||||
a_.OpCase(dxbc::Src::LU(RenderTargetCache::AddPSIColorFormatFlags(
|
||||
i ? xenos::ColorRenderTargetFormat::k_16_16_16_16
|
||||
: xenos::ColorRenderTargetFormat::k_16_16)));
|
||||
dxbc::Dest color_components_dest(
|
||||
|
@ -1404,7 +1278,7 @@ void DxbcShaderTranslator::ROV_UnpackColor(
|
|||
// k_16_16_16_16_FLOAT (64bpp)
|
||||
// ***************************************************************************
|
||||
for (uint32_t i = 0; i < 2; ++i) {
|
||||
a_.OpCase(dxbc::Src::LU(ROV_AddColorFormatFlags(
|
||||
a_.OpCase(dxbc::Src::LU(RenderTargetCache::AddPSIColorFormatFlags(
|
||||
i ? xenos::ColorRenderTargetFormat::k_16_16_16_16_FLOAT
|
||||
: xenos::ColorRenderTargetFormat::k_16_16_FLOAT)));
|
||||
dxbc::Dest color_components_dest(
|
||||
|
@ -1465,7 +1339,7 @@ void DxbcShaderTranslator::ROV_PackPreClampedColor(
|
|||
// k_8_8_8_8_GAMMA
|
||||
// ***************************************************************************
|
||||
for (uint32_t i = 0; i < 2; ++i) {
|
||||
a_.OpCase(dxbc::Src::LU(ROV_AddColorFormatFlags(
|
||||
a_.OpCase(dxbc::Src::LU(RenderTargetCache::AddPSIColorFormatFlags(
|
||||
i ? xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA
|
||||
: xenos::ColorRenderTargetFormat::k_8_8_8_8)));
|
||||
for (uint32_t j = 0; j < 4; ++j) {
|
||||
|
@ -1496,9 +1370,9 @@ void DxbcShaderTranslator::ROV_PackPreClampedColor(
|
|||
// k_2_10_10_10
|
||||
// k_2_10_10_10_AS_10_10_10_10
|
||||
// ***************************************************************************
|
||||
a_.OpCase(dxbc::Src::LU(
|
||||
ROV_AddColorFormatFlags(xenos::ColorRenderTargetFormat::k_2_10_10_10)));
|
||||
a_.OpCase(dxbc::Src::LU(ROV_AddColorFormatFlags(
|
||||
a_.OpCase(dxbc::Src::LU(RenderTargetCache::AddPSIColorFormatFlags(
|
||||
xenos::ColorRenderTargetFormat::k_2_10_10_10)));
|
||||
a_.OpCase(dxbc::Src::LU(RenderTargetCache::AddPSIColorFormatFlags(
|
||||
xenos::ColorRenderTargetFormat::k_2_10_10_10_AS_10_10_10_10)));
|
||||
for (uint32_t i = 0; i < 4; ++i) {
|
||||
// Denormalize and convert to fixed-point.
|
||||
|
@ -1518,9 +1392,9 @@ void DxbcShaderTranslator::ROV_PackPreClampedColor(
|
|||
// k_2_10_10_10_FLOAT_AS_16_16_16_16
|
||||
// https://github.com/Microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexConvert.cpp
|
||||
// ***************************************************************************
|
||||
a_.OpCase(dxbc::Src::LU(ROV_AddColorFormatFlags(
|
||||
a_.OpCase(dxbc::Src::LU(RenderTargetCache::AddPSIColorFormatFlags(
|
||||
xenos::ColorRenderTargetFormat::k_2_10_10_10_FLOAT)));
|
||||
a_.OpCase(dxbc::Src::LU(ROV_AddColorFormatFlags(
|
||||
a_.OpCase(dxbc::Src::LU(RenderTargetCache::AddPSIColorFormatFlags(
|
||||
xenos::ColorRenderTargetFormat::k_2_10_10_10_FLOAT_AS_16_16_16_16)));
|
||||
{
|
||||
// Convert red directly to the destination, which may be the same as the
|
||||
|
@ -1550,7 +1424,7 @@ void DxbcShaderTranslator::ROV_PackPreClampedColor(
|
|||
// k_16_16_16_16 (64bpp)
|
||||
// ***************************************************************************
|
||||
for (uint32_t i = 0; i < 2; ++i) {
|
||||
a_.OpCase(dxbc::Src::LU(ROV_AddColorFormatFlags(
|
||||
a_.OpCase(dxbc::Src::LU(RenderTargetCache::AddPSIColorFormatFlags(
|
||||
i ? xenos::ColorRenderTargetFormat::k_16_16_16_16
|
||||
: xenos::ColorRenderTargetFormat::k_16_16)));
|
||||
for (uint32_t j = 0; j < (uint32_t(2) << i); ++j) {
|
||||
|
@ -1582,7 +1456,7 @@ void DxbcShaderTranslator::ROV_PackPreClampedColor(
|
|||
// k_16_16_16_16_FLOAT (64bpp)
|
||||
// ***************************************************************************
|
||||
for (uint32_t i = 0; i < 2; ++i) {
|
||||
a_.OpCase(dxbc::Src::LU(ROV_AddColorFormatFlags(
|
||||
a_.OpCase(dxbc::Src::LU(RenderTargetCache::AddPSIColorFormatFlags(
|
||||
i ? xenos::ColorRenderTargetFormat::k_16_16_16_16_FLOAT
|
||||
: xenos::ColorRenderTargetFormat::k_16_16_FLOAT)));
|
||||
for (uint32_t j = 0; j < (uint32_t(2) << i); ++j) {
|
||||
|
@ -2230,7 +2104,8 @@ void DxbcShaderTranslator::CompletePixelShader_WriteToROV() {
|
|||
// Load whether the render target is 64bpp to system_temp_rov_params_.y to
|
||||
// get the needed relative sample address.
|
||||
a_.OpAnd(dxbc::Dest::R(system_temp_rov_params_, 0b0010),
|
||||
rt_format_flags_src, dxbc::Src::LU(kRTFormatFlag_64bpp));
|
||||
rt_format_flags_src,
|
||||
dxbc::Src::LU(RenderTargetCache::kPSIColorFormatFlag_64bpp));
|
||||
// Choose the relative sample address for the render target to
|
||||
// system_temp_rov_params_.y.
|
||||
a_.OpMovC(dxbc::Dest::R(system_temp_rov_params_, 0b0010),
|
||||
|
@ -2287,7 +2162,8 @@ void DxbcShaderTranslator::CompletePixelShader_WriteToROV() {
|
|||
// Get if the blending source color is fixed-point for clamping if it is.
|
||||
// temp.x = whether color is fixed-point.
|
||||
a_.OpAnd(temp_x_dest, rt_format_flags_src,
|
||||
dxbc::Src::LU(kRTFormatFlag_FixedPointColor));
|
||||
dxbc::Src::LU(
|
||||
RenderTargetCache::kPSIColorFormatFlag_FixedPointColor));
|
||||
// Check if the blending source color is fixed-point and needs clamping.
|
||||
// temp.x = free.
|
||||
a_.OpIf(true, temp_x_src);
|
||||
|
@ -2306,7 +2182,8 @@ void DxbcShaderTranslator::CompletePixelShader_WriteToROV() {
|
|||
// Get if the blending source alpha is fixed-point for clamping if it is.
|
||||
// temp.x = whether alpha is fixed-point.
|
||||
a_.OpAnd(temp_x_dest, rt_format_flags_src,
|
||||
dxbc::Src::LU(kRTFormatFlag_FixedPointAlpha));
|
||||
dxbc::Src::LU(
|
||||
RenderTargetCache::kPSIColorFormatFlag_FixedPointAlpha));
|
||||
// Check if the blending source alpha is fixed-point and needs clamping.
|
||||
// temp.x = free.
|
||||
a_.OpIf(true, temp_x_src);
|
||||
|
@ -2387,7 +2264,7 @@ void DxbcShaderTranslator::CompletePixelShader_WriteToROV() {
|
|||
// Get if the format is 64bpp to temp.w.
|
||||
// temp.w = whether the render target is 64bpp.
|
||||
a_.OpAnd(temp_w_dest, rt_format_flags_src,
|
||||
dxbc::Src::LU(kRTFormatFlag_64bpp));
|
||||
dxbc::Src::LU(RenderTargetCache::kPSIColorFormatFlag_64bpp));
|
||||
// Check if the format is 64bpp.
|
||||
// temp.w = free.
|
||||
a_.OpIf(true, temp_w_src);
|
||||
|
@ -2478,8 +2355,10 @@ void DxbcShaderTranslator::CompletePixelShader_WriteToROV() {
|
|||
// Get if the render target color is fixed-point and the source
|
||||
// color factor needs clamping to temp.x.
|
||||
// temp.x = whether color is fixed-point.
|
||||
a_.OpAnd(temp_x_dest, rt_format_flags_src,
|
||||
dxbc::Src::LU(kRTFormatFlag_FixedPointColor));
|
||||
a_.OpAnd(
|
||||
temp_x_dest, rt_format_flags_src,
|
||||
dxbc::Src::LU(
|
||||
RenderTargetCache::kPSIColorFormatFlag_FixedPointColor));
|
||||
// Check if the source color factor needs clamping.
|
||||
a_.OpIf(true, temp_x_src);
|
||||
{
|
||||
|
@ -2558,8 +2437,10 @@ void DxbcShaderTranslator::CompletePixelShader_WriteToROV() {
|
|||
// Get if the render target color is fixed-point and the
|
||||
// destination color factor needs clamping to temp.x.
|
||||
// temp.x = whether color is fixed-point.
|
||||
a_.OpAnd(temp_x_dest, rt_format_flags_src,
|
||||
dxbc::Src::LU(kRTFormatFlag_FixedPointColor));
|
||||
a_.OpAnd(
|
||||
temp_x_dest, rt_format_flags_src,
|
||||
dxbc::Src::LU(
|
||||
RenderTargetCache::kPSIColorFormatFlag_FixedPointColor));
|
||||
// Check if the destination color factor needs clamping.
|
||||
a_.OpIf(true, temp_x_src);
|
||||
{
|
||||
|
@ -2701,8 +2582,10 @@ void DxbcShaderTranslator::CompletePixelShader_WriteToROV() {
|
|||
// Get if the render target alpha is fixed-point and the source
|
||||
// alpha factor needs clamping to temp.y.
|
||||
// temp.y = whether alpha is fixed-point.
|
||||
a_.OpAnd(temp_y_dest, rt_format_flags_src,
|
||||
dxbc::Src::LU(kRTFormatFlag_FixedPointAlpha));
|
||||
a_.OpAnd(
|
||||
temp_y_dest, rt_format_flags_src,
|
||||
dxbc::Src::LU(
|
||||
RenderTargetCache::kPSIColorFormatFlag_FixedPointAlpha));
|
||||
// Check if the source alpha factor needs clamping.
|
||||
a_.OpIf(true, temp_y_src);
|
||||
{
|
||||
|
@ -2769,9 +2652,11 @@ void DxbcShaderTranslator::CompletePixelShader_WriteToROV() {
|
|||
// destination alpha factor needs clamping.
|
||||
// alpha_is_fixed_temp.x = whether alpha is fixed-point.
|
||||
uint32_t alpha_is_fixed_temp = PushSystemTemp();
|
||||
a_.OpAnd(dxbc::Dest::R(alpha_is_fixed_temp, 0b0001),
|
||||
a_.OpAnd(
|
||||
dxbc::Dest::R(alpha_is_fixed_temp, 0b0001),
|
||||
rt_format_flags_src,
|
||||
dxbc::Src::LU(kRTFormatFlag_FixedPointAlpha));
|
||||
dxbc::Src::LU(
|
||||
RenderTargetCache::kPSIColorFormatFlag_FixedPointAlpha));
|
||||
// Check if the destination alpha factor needs clamping.
|
||||
a_.OpIf(true,
|
||||
dxbc::Src::R(alpha_is_fixed_temp, dxbc::Src::kXXXX));
|
||||
|
@ -2925,7 +2810,7 @@ void DxbcShaderTranslator::CompletePixelShader_WriteToROV() {
|
|||
// Get if the format is 64bpp to temp.z.
|
||||
// temp.z = whether the render target is 64bpp.
|
||||
a_.OpAnd(temp_z_dest, rt_format_flags_src,
|
||||
dxbc::Src::LU(kRTFormatFlag_64bpp));
|
||||
dxbc::Src::LU(RenderTargetCache::kPSIColorFormatFlag_64bpp));
|
||||
// Check if the format is 64bpp.
|
||||
// temp.z = free.
|
||||
a_.OpIf(true, temp_z_src);
|
||||
|
@ -2954,16 +2839,29 @@ void DxbcShaderTranslator::CompletePixelShader_WriteToROV() {
|
|||
// Close the sample covered check.
|
||||
a_.OpEndIf();
|
||||
|
||||
// Go to the next sample (samples are at +0, +(80*scale_x), +1,
|
||||
// +(80*scale_x+1), so need to do +(80*scale_x), -(80*scale_x-1),
|
||||
// +(80*scale_x) and -(80*scale_x+1) after each sample).
|
||||
// Go to the next sample (samples are at +0, +(80*scale_x), +dwpp,
|
||||
// +(80*scale_x+dwpp), so need to do +(80*scale_x), -(80*scale_x-dwpp),
|
||||
// +(80*scale_x) and -(80*scale_x+dwpp) after each sample).
|
||||
// Though no need to do this for the last sample as for the next render
|
||||
// target, the address will be recalculated.
|
||||
if (j < 3) {
|
||||
if (j & 1) {
|
||||
// temp.z = whether the render target is 64bpp.
|
||||
a_.OpAnd(temp_z_dest, rt_format_flags_src,
|
||||
dxbc::Src::LU(RenderTargetCache::kPSIColorFormatFlag_64bpp));
|
||||
// temp.z = offset from the current sample to the next.
|
||||
a_.OpMovC(temp_z_dest, temp_z_src,
|
||||
dxbc::Src::LI(-int32_t(tile_width) + 2 * (2 - int32_t(j))),
|
||||
dxbc::Src::LI(-int32_t(tile_width) + (2 - int32_t(j))));
|
||||
// temp.z = free.
|
||||
a_.OpIAdd(dxbc::Dest::R(system_temp_rov_params_, 0b0010),
|
||||
dxbc::Src::R(system_temp_rov_params_, dxbc::Src::kYYYY),
|
||||
dxbc::Src::LI((j & 1) ? -int32_t(tile_width) + 2 - j
|
||||
: int32_t(tile_width)));
|
||||
temp_z_src);
|
||||
} else {
|
||||
a_.OpIAdd(dxbc::Dest::R(system_temp_rov_params_, 0b0010),
|
||||
dxbc::Src::R(system_temp_rov_params_, dxbc::Src::kYYYY),
|
||||
dxbc::Src::LU(tile_width));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2987,6 +2885,17 @@ void DxbcShaderTranslator::CompletePixelShader() {
|
|||
|
||||
if (current_shader().writes_color_target(0) &&
|
||||
!IsForceEarlyDepthStencilGlobalFlagEnabled()) {
|
||||
if (edram_rov_used_) {
|
||||
// Check if the render target 0 was written to on the execution path.
|
||||
uint32_t rt_0_written_temp = PushSystemTemp();
|
||||
a_.OpAnd(dxbc::Dest::R(rt_0_written_temp, 0b0001),
|
||||
dxbc::Src::R(system_temp_rov_params_, dxbc::Src::kXXXX),
|
||||
dxbc::Src::LU(1 << 8));
|
||||
a_.OpIf(true, dxbc::Src::R(rt_0_written_temp, dxbc::Src::kXXXX));
|
||||
// Release rt_0_written_temp.
|
||||
PopSystemTemp();
|
||||
}
|
||||
|
||||
// Alpha test.
|
||||
// X - mask, then masked result (SGPR for loading, VGPR for masking).
|
||||
// Y - operation result (SGPR for mask operations, VGPR for alpha
|
||||
|
@ -3057,11 +2966,16 @@ void DxbcShaderTranslator::CompletePixelShader() {
|
|||
a_.OpEndIf();
|
||||
// Release alpha_test_temp.
|
||||
PopSystemTemp();
|
||||
}
|
||||
|
||||
// Discard samples with alpha to coverage.
|
||||
CompletePixelShader_AlphaToMask();
|
||||
|
||||
if (edram_rov_used_) {
|
||||
// Close the render target 0 written check.
|
||||
a_.OpEndIf();
|
||||
}
|
||||
}
|
||||
|
||||
// Write the values to the render targets. Not applying the exponent bias yet
|
||||
// because the original 0 to 1 alpha value is needed for alpha to coverage,
|
||||
// which is done differently for ROV and RTV/DSV.
|
||||
|
|
|
@ -207,6 +207,134 @@ DEFINE_bool(
|
|||
namespace xe {
|
||||
namespace gpu {
|
||||
|
||||
void RenderTargetCache::GetPSIColorFormatInfo(
|
||||
xenos::ColorRenderTargetFormat format, uint32_t write_mask,
|
||||
float& clamp_rgb_low, float& clamp_alpha_low, float& clamp_rgb_high,
|
||||
float& clamp_alpha_high, uint32_t& keep_mask_low,
|
||||
uint32_t& keep_mask_high) {
|
||||
keep_mask_low = keep_mask_high = 0;
|
||||
switch (format) {
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8:
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA: {
|
||||
clamp_rgb_low = clamp_alpha_low = 0.0f;
|
||||
clamp_rgb_high = clamp_alpha_high = 1.0f;
|
||||
for (uint32_t i = 0; i < 4; ++i) {
|
||||
if (!(write_mask & (1 << i))) {
|
||||
keep_mask_low |= uint32_t(0xFF) << (i * 8);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case xenos::ColorRenderTargetFormat::k_2_10_10_10:
|
||||
case xenos::ColorRenderTargetFormat::k_2_10_10_10_AS_10_10_10_10: {
|
||||
clamp_rgb_low = clamp_alpha_low = 0.0f;
|
||||
clamp_rgb_high = clamp_alpha_high = 1.0f;
|
||||
for (uint32_t i = 0; i < 3; ++i) {
|
||||
if (!(write_mask & (1 << i))) {
|
||||
keep_mask_low |= uint32_t(0x3FF) << (i * 10);
|
||||
}
|
||||
}
|
||||
if (!(write_mask & 0b1000)) {
|
||||
keep_mask_low |= uint32_t(3) << 30;
|
||||
}
|
||||
} break;
|
||||
case xenos::ColorRenderTargetFormat::k_2_10_10_10_FLOAT:
|
||||
case xenos::ColorRenderTargetFormat::k_2_10_10_10_FLOAT_AS_16_16_16_16: {
|
||||
clamp_rgb_low = clamp_alpha_low = 0.0f;
|
||||
clamp_rgb_high = 31.875f;
|
||||
clamp_alpha_high = 1.0f;
|
||||
for (uint32_t i = 0; i < 3; ++i) {
|
||||
if (!(write_mask & (1 << i))) {
|
||||
keep_mask_low |= uint32_t(0x3FF) << (i * 10);
|
||||
}
|
||||
}
|
||||
if (!(write_mask & 0b1000)) {
|
||||
keep_mask_low |= uint32_t(3) << 30;
|
||||
}
|
||||
} break;
|
||||
case xenos::ColorRenderTargetFormat::k_16_16:
|
||||
case xenos::ColorRenderTargetFormat::k_16_16_16_16:
|
||||
// Alpha clamping affects blending source, so it's non-zero for alpha for
|
||||
// k_16_16 (the render target is fixed-point). There's one deviation from
|
||||
// how Direct3D 11.3 functional specification defines SNorm conversion
|
||||
// (NaN should be 0, not the lowest negative number), and that needs to be
|
||||
// handled separately.
|
||||
clamp_rgb_low = clamp_alpha_low = -32.0f;
|
||||
clamp_rgb_high = clamp_alpha_high = 32.0f;
|
||||
if (!(write_mask & 0b0001)) {
|
||||
keep_mask_low |= 0xFFFFu;
|
||||
}
|
||||
if (!(write_mask & 0b0010)) {
|
||||
keep_mask_low |= 0xFFFF0000u;
|
||||
}
|
||||
if (format == xenos::ColorRenderTargetFormat::k_16_16_16_16) {
|
||||
if (!(write_mask & 0b0100)) {
|
||||
keep_mask_high |= 0xFFFFu;
|
||||
}
|
||||
if (!(write_mask & 0b1000)) {
|
||||
keep_mask_high |= 0xFFFF0000u;
|
||||
}
|
||||
} else {
|
||||
write_mask &= 0b0011;
|
||||
}
|
||||
break;
|
||||
case xenos::ColorRenderTargetFormat::k_16_16_FLOAT:
|
||||
case xenos::ColorRenderTargetFormat::k_16_16_16_16_FLOAT:
|
||||
// No NaNs on the Xbox 360 GPU, though can't use the extended range with
|
||||
// Direct3D and Vulkan conversions.
|
||||
// TODO(Triang3l): Use the extended-range encoding in all implementations.
|
||||
clamp_rgb_low = clamp_alpha_low = -65504.0f;
|
||||
clamp_rgb_high = clamp_alpha_high = 65504.0f;
|
||||
if (!(write_mask & 0b0001)) {
|
||||
keep_mask_low |= 0xFFFFu;
|
||||
}
|
||||
if (!(write_mask & 0b0010)) {
|
||||
keep_mask_low |= 0xFFFF0000u;
|
||||
}
|
||||
if (format == xenos::ColorRenderTargetFormat::k_16_16_16_16_FLOAT) {
|
||||
if (!(write_mask & 0b0100)) {
|
||||
keep_mask_high |= 0xFFFFu;
|
||||
}
|
||||
if (!(write_mask & 0b1000)) {
|
||||
keep_mask_high |= 0xFFFF0000u;
|
||||
}
|
||||
} else {
|
||||
write_mask &= 0b0011;
|
||||
}
|
||||
break;
|
||||
case xenos::ColorRenderTargetFormat::k_32_FLOAT:
|
||||
// No clamping - let min/max always pick the original value.
|
||||
clamp_rgb_low = clamp_alpha_low = clamp_rgb_high = clamp_alpha_high =
|
||||
std::nanf("");
|
||||
write_mask &= 0b0001;
|
||||
if (!(write_mask & 0b0001)) {
|
||||
keep_mask_low = ~uint32_t(0);
|
||||
}
|
||||
break;
|
||||
case xenos::ColorRenderTargetFormat::k_32_32_FLOAT:
|
||||
// No clamping - let min/max always pick the original value.
|
||||
clamp_rgb_low = clamp_alpha_low = clamp_rgb_high = clamp_alpha_high =
|
||||
std::nanf("");
|
||||
write_mask &= 0b0011;
|
||||
if (!(write_mask & 0b0001)) {
|
||||
keep_mask_low = ~uint32_t(0);
|
||||
}
|
||||
if (!(write_mask & 0b0010)) {
|
||||
keep_mask_high = ~uint32_t(0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert_unhandled_case(format);
|
||||
// Disable invalid render targets.
|
||||
write_mask = 0;
|
||||
break;
|
||||
}
|
||||
// Special case handled in the shaders for empty write mask to completely skip
|
||||
// a disabled render target: all keep bits are set.
|
||||
if (!write_mask) {
|
||||
keep_mask_low = keep_mask_high = ~uint32_t(0);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t RenderTargetCache::Transfer::GetRangeRectangles(
|
||||
uint32_t start_tiles, uint32_t end_tiles, uint32_t base_tiles,
|
||||
uint32_t pitch_tiles, xenos::MsaaSamples msaa_samples, bool is_64bpp,
|
||||
|
|
|
@ -113,6 +113,54 @@ class RenderTargetCache {
|
|||
kSrgbToLinearExponent);
|
||||
}
|
||||
|
||||
// Pixel shader interlock implementation helpers.
|
||||
|
||||
// Appended to the format in the format constant via bitwise OR.
|
||||
enum : uint32_t {
|
||||
kPSIColorFormatFlag_64bpp_Shift = xenos::kColorRenderTargetFormatBits,
|
||||
// Requires clamping of blending sources and factors.
|
||||
kPSIColorFormatFlag_FixedPointColor_Shift,
|
||||
kPSIColorFormatFlag_FixedPointAlpha_Shift,
|
||||
|
||||
kPSIColorFormatFlag_64bpp = uint32_t(1) << kPSIColorFormatFlag_64bpp_Shift,
|
||||
kPSIColorFormatFlag_FixedPointColor =
|
||||
uint32_t(1) << kPSIColorFormatFlag_FixedPointColor_Shift,
|
||||
kPSIColorFormatFlag_FixedPointAlpha =
|
||||
uint32_t(1) << kPSIColorFormatFlag_FixedPointAlpha_Shift,
|
||||
};
|
||||
|
||||
static constexpr uint32_t AddPSIColorFormatFlags(
|
||||
xenos::ColorRenderTargetFormat format) {
|
||||
uint32_t format_flags = uint32_t(format);
|
||||
if (format == xenos::ColorRenderTargetFormat::k_16_16_16_16 ||
|
||||
format == xenos::ColorRenderTargetFormat::k_16_16_16_16_FLOAT ||
|
||||
format == xenos::ColorRenderTargetFormat::k_32_32_FLOAT) {
|
||||
format_flags |= kPSIColorFormatFlag_64bpp;
|
||||
}
|
||||
if (format == xenos::ColorRenderTargetFormat::k_8_8_8_8 ||
|
||||
format == xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA ||
|
||||
format == xenos::ColorRenderTargetFormat::k_2_10_10_10 ||
|
||||
format == xenos::ColorRenderTargetFormat::k_16_16 ||
|
||||
format == xenos::ColorRenderTargetFormat::k_16_16_16_16 ||
|
||||
format == xenos::ColorRenderTargetFormat::k_2_10_10_10_AS_10_10_10_10) {
|
||||
format_flags |= kPSIColorFormatFlag_FixedPointColor |
|
||||
kPSIColorFormatFlag_FixedPointAlpha;
|
||||
} else if (format == xenos::ColorRenderTargetFormat::k_2_10_10_10_FLOAT ||
|
||||
format == xenos::ColorRenderTargetFormat::
|
||||
k_2_10_10_10_FLOAT_AS_16_16_16_16) {
|
||||
format_flags |= kPSIColorFormatFlag_FixedPointAlpha;
|
||||
}
|
||||
return format_flags;
|
||||
}
|
||||
|
||||
static void GetPSIColorFormatInfo(xenos::ColorRenderTargetFormat format,
|
||||
uint32_t write_mask, float& clamp_rgb_low,
|
||||
float& clamp_alpha_low,
|
||||
float& clamp_rgb_high,
|
||||
float& clamp_alpha_high,
|
||||
uint32_t& keep_mask_low,
|
||||
uint32_t& keep_mask_high);
|
||||
|
||||
virtual ~RenderTargetCache();
|
||||
|
||||
virtual Path GetPath() const = 0;
|
||||
|
|
|
@ -54,8 +54,10 @@ DEFINE_string(
|
|||
"GPU");
|
||||
DEFINE_bool(shader_output_bindless_resources, false,
|
||||
"Output host shader with bindless resources used.", "GPU");
|
||||
DEFINE_bool(shader_output_dxbc_rov, false,
|
||||
"Output ROV-based output-merger code in DXBC pixel shaders.",
|
||||
DEFINE_bool(
|
||||
shader_output_pixel_shader_interlock, false,
|
||||
"Output host shader with a render backend implementation based on pixel "
|
||||
"shader interlock.",
|
||||
"GPU");
|
||||
|
||||
namespace xe {
|
||||
|
@ -124,12 +126,15 @@ int shader_compiler_main(const std::vector<std::string>& args) {
|
|||
SpirvShaderTranslator::Features spirv_features(true);
|
||||
if (cvars::shader_output_type == "spirv" ||
|
||||
cvars::shader_output_type == "spirvtext") {
|
||||
translator = std::make_unique<SpirvShaderTranslator>(spirv_features);
|
||||
translator = std::make_unique<SpirvShaderTranslator>(
|
||||
spirv_features, true, true,
|
||||
cvars::shader_output_pixel_shader_interlock);
|
||||
} else if (cvars::shader_output_type == "dxbc" ||
|
||||
cvars::shader_output_type == "dxbctext") {
|
||||
translator = std::make_unique<DxbcShaderTranslator>(
|
||||
ui::GraphicsProvider::GpuVendorID(0),
|
||||
cvars::shader_output_bindless_resources, cvars::shader_output_dxbc_rov);
|
||||
cvars::shader_output_bindless_resources,
|
||||
cvars::shader_output_pixel_shader_interlock);
|
||||
} else {
|
||||
// Just output microcode disassembly generated during microcode information
|
||||
// gathering.
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "third_party/glslang/SPIRV/GLSL.std.450.h"
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/string_buffer.h"
|
||||
#include "xenia/gpu/spirv_shader.h"
|
||||
|
||||
namespace xe {
|
||||
|
@ -31,6 +32,8 @@ SpirvShaderTranslator::Features::Features(bool all)
|
|||
max_storage_buffer_range(all ? UINT32_MAX : (128 * 1024 * 1024)),
|
||||
clip_distance(all),
|
||||
cull_distance(all),
|
||||
demote_to_helper_invocation(all),
|
||||
fragment_shader_sample_interlock(all),
|
||||
full_draw_index_uint32(all),
|
||||
image_view_format_swizzle(all),
|
||||
signed_zero_inf_nan_preserve_float32(all),
|
||||
|
@ -42,6 +45,14 @@ SpirvShaderTranslator::Features::Features(
|
|||
provider.device_properties().limits.maxStorageBufferRange),
|
||||
clip_distance(provider.device_features().shaderClipDistance),
|
||||
cull_distance(provider.device_features().shaderCullDistance),
|
||||
demote_to_helper_invocation(
|
||||
provider.device_extensions().ext_shader_demote_to_helper_invocation &&
|
||||
provider.device_shader_demote_to_helper_invocation_features()
|
||||
.shaderDemoteToHelperInvocation),
|
||||
fragment_shader_sample_interlock(
|
||||
provider.device_extensions().ext_fragment_shader_interlock &&
|
||||
provider.device_fragment_shader_interlock_features()
|
||||
.fragmentShaderSampleInterlock),
|
||||
full_draw_index_uint32(provider.device_features().fullDrawIndexUint32) {
|
||||
uint32_t device_version = provider.device_properties().apiVersion;
|
||||
const ui::vulkan::VulkanProvider::DeviceExtensions& device_extensions =
|
||||
|
@ -78,9 +89,6 @@ SpirvShaderTranslator::Features::Features(
|
|||
}
|
||||
}
|
||||
|
||||
SpirvShaderTranslator::SpirvShaderTranslator(const Features& features)
|
||||
: features_(features) {}
|
||||
|
||||
uint64_t SpirvShaderTranslator::GetDefaultVertexShaderModification(
|
||||
uint32_t dynamic_addressable_register_count,
|
||||
Shader::HostVertexShaderType host_vertex_shader_type) const {
|
||||
|
@ -99,6 +107,19 @@ uint64_t SpirvShaderTranslator::GetDefaultPixelShaderModification(
|
|||
return shader_modification.value;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> SpirvShaderTranslator::CreateDepthOnlyFragmentShader() {
|
||||
is_depth_only_fragment_shader_ = true;
|
||||
// TODO(Triang3l): Handle in a nicer way (is_depth_only_fragment_shader_ is a
|
||||
// leftover from when a Shader object wasn't used during translation).
|
||||
Shader shader(xenos::ShaderType::kPixel, 0, nullptr, 0);
|
||||
StringBuffer instruction_disassembly_buffer;
|
||||
shader.AnalyzeUcode(instruction_disassembly_buffer);
|
||||
Shader::Translation& translation = *shader.GetOrCreateTranslation(0);
|
||||
TranslateAnalyzedShader(translation);
|
||||
is_depth_only_fragment_shader_ = false;
|
||||
return translation.translated_binary();
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::Reset() {
|
||||
ShaderTranslator::Reset();
|
||||
|
||||
|
@ -109,6 +130,7 @@ void SpirvShaderTranslator::Reset() {
|
|||
input_point_coordinates_ = spv::NoResult;
|
||||
input_fragment_coordinates_ = spv::NoResult;
|
||||
input_front_facing_ = spv::NoResult;
|
||||
input_sample_mask_ = spv::NoResult;
|
||||
std::fill(input_output_interpolators_.begin(),
|
||||
input_output_interpolators_.end(), spv::NoResult);
|
||||
output_point_coordinates_ = spv::NoResult;
|
||||
|
@ -120,6 +142,8 @@ void SpirvShaderTranslator::Reset() {
|
|||
main_interface_.clear();
|
||||
var_main_registers_ = spv::NoResult;
|
||||
var_main_point_size_edge_flag_kill_vertex_ = spv::NoResult;
|
||||
var_main_kill_pixel_ = spv::NoResult;
|
||||
var_main_fsi_color_written_ = spv::NoResult;
|
||||
|
||||
main_switch_op_.reset();
|
||||
main_switch_next_pc_phi_operands_.clear();
|
||||
|
@ -217,6 +241,10 @@ void SpirvShaderTranslator::StartTranslation() {
|
|||
size_t offset;
|
||||
spv::Id type;
|
||||
};
|
||||
spv::Id type_float4_array_4 = builder_->makeArrayType(
|
||||
type_float4_, builder_->makeUintConstant(4), sizeof(float) * 4);
|
||||
builder_->addDecoration(type_float4_array_4, spv::DecorationArrayStride,
|
||||
sizeof(float) * 4);
|
||||
spv::Id type_uint4_array_2 = builder_->makeArrayType(
|
||||
type_uint4_, builder_->makeUintConstant(2), sizeof(uint32_t) * 4);
|
||||
builder_->addDecoration(type_uint4_array_2, spv::DecorationArrayStride,
|
||||
|
@ -250,8 +278,37 @@ void SpirvShaderTranslator::StartTranslation() {
|
|||
type_uint4_array_4},
|
||||
{"alpha_test_reference", offsetof(SystemConstants, alpha_test_reference),
|
||||
type_float_},
|
||||
{"edram_32bpp_tile_pitch_dwords_scaled",
|
||||
offsetof(SystemConstants, edram_32bpp_tile_pitch_dwords_scaled),
|
||||
type_uint_},
|
||||
{"edram_depth_base_dwords_scaled",
|
||||
offsetof(SystemConstants, edram_depth_base_dwords_scaled), type_uint_},
|
||||
{"color_exp_bias", offsetof(SystemConstants, color_exp_bias),
|
||||
type_float4_},
|
||||
{"edram_poly_offset_front_scale",
|
||||
offsetof(SystemConstants, edram_poly_offset_front_scale), type_float_},
|
||||
{"edram_poly_offset_back_scale",
|
||||
offsetof(SystemConstants, edram_poly_offset_back_scale), type_float_},
|
||||
{"edram_poly_offset_front_offset",
|
||||
offsetof(SystemConstants, edram_poly_offset_front_offset), type_float_},
|
||||
{"edram_poly_offset_back_offset",
|
||||
offsetof(SystemConstants, edram_poly_offset_back_offset), type_float_},
|
||||
{"edram_stencil_front", offsetof(SystemConstants, edram_stencil_front),
|
||||
type_uint2_},
|
||||
{"edram_stencil_back", offsetof(SystemConstants, edram_stencil_back),
|
||||
type_uint2_},
|
||||
{"edram_rt_base_dwords_scaled",
|
||||
offsetof(SystemConstants, edram_rt_base_dwords_scaled), type_uint4_},
|
||||
{"edram_rt_format_flags",
|
||||
offsetof(SystemConstants, edram_rt_format_flags), type_uint4_},
|
||||
{"edram_rt_blend_factors_ops",
|
||||
offsetof(SystemConstants, edram_rt_blend_factors_ops), type_uint4_},
|
||||
{"edram_rt_keep_mask", offsetof(SystemConstants, edram_rt_keep_mask),
|
||||
type_uint4_array_2},
|
||||
{"edram_rt_clamp", offsetof(SystemConstants, edram_rt_clamp),
|
||||
type_float4_array_4},
|
||||
{"edram_blend_constant", offsetof(SystemConstants, edram_blend_constant),
|
||||
type_float4_},
|
||||
};
|
||||
id_vector_temp_.clear();
|
||||
id_vector_temp_.reserve(xe::countof(system_constants));
|
||||
|
@ -281,6 +338,7 @@ void SpirvShaderTranslator::StartTranslation() {
|
|||
main_interface_.push_back(uniform_system_constants_);
|
||||
}
|
||||
|
||||
if (!is_depth_only_fragment_shader_) {
|
||||
// Common uniform buffer - float constants.
|
||||
uint32_t float_constant_count =
|
||||
current_shader().constant_register_map().float_count;
|
||||
|
@ -289,11 +347,11 @@ void SpirvShaderTranslator::StartTranslation() {
|
|||
id_vector_temp_.push_back(builder_->makeArrayType(
|
||||
type_float4_, builder_->makeUintConstant(float_constant_count),
|
||||
sizeof(float) * 4));
|
||||
// Currently (as of October 24, 2020) makeArrayType only uses the stride to
|
||||
// check if deduplication can be done - the array stride decoration needs to
|
||||
// be applied explicitly.
|
||||
builder_->addDecoration(id_vector_temp_.back(), spv::DecorationArrayStride,
|
||||
sizeof(float) * 4);
|
||||
// Currently (as of October 24, 2020) makeArrayType only uses the stride
|
||||
// to check if deduplication can be done - the array stride decoration
|
||||
// needs to be applied explicitly.
|
||||
builder_->addDecoration(id_vector_temp_.back(),
|
||||
spv::DecorationArrayStride, sizeof(float) * 4);
|
||||
spv::Id type_float_constants =
|
||||
builder_->makeStructType(id_vector_temp_, "XeFloatConstants");
|
||||
builder_->addMemberName(type_float_constants, 0, "float_constants");
|
||||
|
@ -346,7 +404,8 @@ void SpirvShaderTranslator::StartTranslation() {
|
|||
builder_->addDecoration(uniform_bool_loop_constants_,
|
||||
spv::DecorationDescriptorSet,
|
||||
int(kDescriptorSetConstants));
|
||||
builder_->addDecoration(uniform_bool_loop_constants_, spv::DecorationBinding,
|
||||
builder_->addDecoration(uniform_bool_loop_constants_,
|
||||
spv::DecorationBinding,
|
||||
int(kConstantBufferBoolLoop));
|
||||
if (features_.spirv_version >= spv::Spv_1_4) {
|
||||
main_interface_.push_back(uniform_bool_loop_constants_);
|
||||
|
@ -363,8 +422,8 @@ void SpirvShaderTranslator::StartTranslation() {
|
|||
spv::Id type_fetch_constants =
|
||||
builder_->makeStructType(id_vector_temp_, "XeFetchConstants");
|
||||
builder_->addMemberName(type_fetch_constants, 0, "fetch_constants");
|
||||
builder_->addMemberDecoration(type_fetch_constants, 0, spv::DecorationOffset,
|
||||
0);
|
||||
builder_->addMemberDecoration(type_fetch_constants, 0,
|
||||
spv::DecorationOffset, 0);
|
||||
builder_->addDecoration(type_fetch_constants, spv::DecorationBlock);
|
||||
uniform_fetch_constants_ = builder_->createVariable(
|
||||
spv::NoPrecision, spv::StorageClassUniform, type_fetch_constants,
|
||||
|
@ -388,6 +447,8 @@ void SpirvShaderTranslator::StartTranslation() {
|
|||
spv::Id type_shared_memory =
|
||||
builder_->makeStructType(id_vector_temp_, "XeSharedMemory");
|
||||
builder_->addMemberName(type_shared_memory, 0, "shared_memory");
|
||||
builder_->addMemberDecoration(type_shared_memory, 0,
|
||||
spv::DecorationRestrict);
|
||||
// TODO(Triang3l): Make writable when memexport is implemented.
|
||||
builder_->addMemberDecoration(type_shared_memory, 0,
|
||||
spv::DecorationNonWritable);
|
||||
|
@ -409,12 +470,14 @@ void SpirvShaderTranslator::StartTranslation() {
|
|||
features_.spirv_version >= spv::Spv_1_3 ? spv::StorageClassStorageBuffer
|
||||
: spv::StorageClassUniform,
|
||||
type_shared_memory, "xe_shared_memory");
|
||||
builder_->addDecoration(buffers_shared_memory_, spv::DecorationDescriptorSet,
|
||||
builder_->addDecoration(buffers_shared_memory_,
|
||||
spv::DecorationDescriptorSet,
|
||||
int(kDescriptorSetSharedMemoryAndEdram));
|
||||
builder_->addDecoration(buffers_shared_memory_, spv::DecorationBinding, 0);
|
||||
if (features_.spirv_version >= spv::Spv_1_4) {
|
||||
main_interface_.push_back(buffers_shared_memory_);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_vertex_shader()) {
|
||||
StartVertexOrTessEvalShaderBeforeMain();
|
||||
|
@ -438,6 +501,7 @@ void SpirvShaderTranslator::StartTranslation() {
|
|||
uniform_system_constants_, id_vector_temp_),
|
||||
spv::NoPrecision);
|
||||
|
||||
if (!is_depth_only_fragment_shader_) {
|
||||
// Begin ucode translation. Initialize everything, even without defined
|
||||
// defaults, for safety.
|
||||
var_main_predicate_ = builder_->createVariable(
|
||||
|
@ -474,6 +538,7 @@ void SpirvShaderTranslator::StartTranslation() {
|
|||
builder_->createVariable(spv::NoPrecision, spv::StorageClassFunction,
|
||||
type_register_array, "xe_var_registers");
|
||||
}
|
||||
}
|
||||
|
||||
// Write the execution model-specific prologue with access to variables in the
|
||||
// main function.
|
||||
|
@ -483,6 +548,10 @@ void SpirvShaderTranslator::StartTranslation() {
|
|||
StartFragmentShaderInMain();
|
||||
}
|
||||
|
||||
if (is_depth_only_fragment_shader_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Open the main loop.
|
||||
spv::Block& main_loop_pre_header = *builder_->getBuildPoint();
|
||||
main_loop_header_ = &builder_->makeNewBlock();
|
||||
|
@ -551,6 +620,7 @@ void SpirvShaderTranslator::StartTranslation() {
|
|||
}
|
||||
|
||||
std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
|
||||
if (!is_depth_only_fragment_shader_) {
|
||||
// Close flow control within the last switch case.
|
||||
CloseExecConditionals();
|
||||
bool has_main_switch = !current_shader().label_addresses().empty();
|
||||
|
@ -566,23 +636,24 @@ std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
|
|||
builder_->setBuildPoint(main_switch_header_);
|
||||
builder_->getBuildPoint()->addInstruction(std::move(main_switch_op_));
|
||||
// Build the main switch merge, breaking out of the loop after falling
|
||||
// through the end or breaking from exece (only continuing if a jump - from
|
||||
// a guest loop or from jmp/call - was made).
|
||||
// through the end or breaking from exece (only continuing if a jump -
|
||||
// from a guest loop or from jmp/call - was made).
|
||||
function_main_->addBlock(main_switch_merge_);
|
||||
builder_->setBuildPoint(main_switch_merge_);
|
||||
builder_->createBranch(main_loop_merge_);
|
||||
}
|
||||
|
||||
// Main loop continuation - choose the program counter based on the path
|
||||
// taken (-1 if not from a jump as a safe fallback, which would result in not
|
||||
// hitting any switch case and reaching the final break in the body).
|
||||
// taken (-1 if not from a jump as a safe fallback, which would result in
|
||||
// not hitting any switch case and reaching the final break in the body).
|
||||
function_main_->addBlock(main_loop_continue_);
|
||||
builder_->setBuildPoint(main_loop_continue_);
|
||||
if (has_main_switch) {
|
||||
// OpPhi, if added, must be the first in the block.
|
||||
// If labels were added, but not jumps (for example, due to the call
|
||||
// instruction not being implemented as of October 18, 2020), send an
|
||||
// impossible program counter value (-1) to the OpPhi at the next iteration.
|
||||
// impossible program counter value (-1) to the OpPhi at the next
|
||||
// iteration.
|
||||
if (main_switch_next_pc_phi_operands_.empty()) {
|
||||
main_switch_next_pc_phi_operands_.push_back(
|
||||
builder_->makeIntConstant(-1));
|
||||
|
@ -590,18 +661,21 @@ std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
|
|||
std::unique_ptr<spv::Instruction> main_loop_pc_next_op =
|
||||
std::make_unique<spv::Instruction>(
|
||||
main_loop_pc_next_, type_int_,
|
||||
main_switch_next_pc_phi_operands_.size() >= 2 ? spv::OpPhi
|
||||
main_switch_next_pc_phi_operands_.size() >= 2
|
||||
? spv::OpPhi
|
||||
: spv::OpCopyObject);
|
||||
for (spv::Id operand : main_switch_next_pc_phi_operands_) {
|
||||
main_loop_pc_next_op->addIdOperand(operand);
|
||||
}
|
||||
builder_->getBuildPoint()->addInstruction(std::move(main_loop_pc_next_op));
|
||||
builder_->getBuildPoint()->addInstruction(
|
||||
std::move(main_loop_pc_next_op));
|
||||
}
|
||||
builder_->createBranch(main_loop_header_);
|
||||
|
||||
// Add the main loop merge block and go back to the function.
|
||||
function_main_->addBlock(main_loop_merge_);
|
||||
builder_->setBuildPoint(main_loop_merge_);
|
||||
}
|
||||
|
||||
if (is_vertex_shader()) {
|
||||
CompleteVertexOrTessEvalShaderInMain();
|
||||
|
@ -622,6 +696,20 @@ std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
|
|||
builder_->addExecutionMode(function_main_,
|
||||
spv::ExecutionModeEarlyFragmentTests);
|
||||
}
|
||||
if (edram_fragment_shader_interlock_) {
|
||||
// Accessing per-sample values, so interlocking just when there's common
|
||||
// coverage is enough if the device exposes that.
|
||||
if (features_.fragment_shader_sample_interlock) {
|
||||
builder_->addCapability(
|
||||
spv::CapabilityFragmentShaderSampleInterlockEXT);
|
||||
builder_->addExecutionMode(function_main_,
|
||||
spv::ExecutionModeSampleInterlockOrderedEXT);
|
||||
} else {
|
||||
builder_->addCapability(spv::CapabilityFragmentShaderPixelInterlockEXT);
|
||||
builder_->addExecutionMode(function_main_,
|
||||
spv::ExecutionModePixelInterlockOrderedEXT);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert_true(is_vertex_shader());
|
||||
execution_model = IsSpirvTessEvalShader()
|
||||
|
@ -649,8 +737,10 @@ std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
|
|||
entry_point->addIdOperand(interface_id);
|
||||
}
|
||||
|
||||
if (!is_depth_only_fragment_shader_) {
|
||||
// Specify the binding indices for samplers when the number of textures is
|
||||
// known, as samplers are located after images in the texture descriptor set.
|
||||
// known, as samplers are located after images in the texture descriptor
|
||||
// set.
|
||||
size_t texture_binding_count = texture_bindings_.size();
|
||||
size_t sampler_binding_count = sampler_bindings_.size();
|
||||
for (size_t i = 0; i < sampler_binding_count; ++i) {
|
||||
|
@ -658,6 +748,7 @@ std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
|
|||
spv::DecorationBinding,
|
||||
int(texture_binding_count + i));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(Triang3l): Avoid copy?
|
||||
std::vector<unsigned int> module_uints;
|
||||
|
@ -1682,13 +1773,48 @@ void SpirvShaderTranslator::CompleteVertexOrTessEvalShaderInMain() {
|
|||
void SpirvShaderTranslator::StartFragmentShaderBeforeMain() {
|
||||
Modification shader_modification = GetSpirvShaderModification();
|
||||
|
||||
if (edram_fragment_shader_interlock_) {
|
||||
builder_->addExtension("SPV_EXT_fragment_shader_interlock");
|
||||
|
||||
// EDRAM buffer uint[].
|
||||
id_vector_temp_.clear();
|
||||
id_vector_temp_.push_back(builder_->makeRuntimeArray(type_uint_));
|
||||
// Storage buffers have std430 packing, no padding to 4-component vectors.
|
||||
builder_->addDecoration(id_vector_temp_.back(), spv::DecorationArrayStride,
|
||||
sizeof(uint32_t));
|
||||
spv::Id type_edram = builder_->makeStructType(id_vector_temp_, "XeEdram");
|
||||
builder_->addMemberName(type_edram, 0, "edram");
|
||||
builder_->addMemberDecoration(type_edram, 0, spv::DecorationCoherent);
|
||||
builder_->addMemberDecoration(type_edram, 0, spv::DecorationRestrict);
|
||||
builder_->addMemberDecoration(type_edram, 0, spv::DecorationOffset, 0);
|
||||
builder_->addDecoration(type_edram, features_.spirv_version >= spv::Spv_1_3
|
||||
? spv::DecorationBlock
|
||||
: spv::DecorationBufferBlock);
|
||||
buffer_edram_ = builder_->createVariable(
|
||||
spv::NoPrecision,
|
||||
features_.spirv_version >= spv::Spv_1_3 ? spv::StorageClassStorageBuffer
|
||||
: spv::StorageClassUniform,
|
||||
type_edram, "xe_edram");
|
||||
builder_->addDecoration(buffer_edram_, spv::DecorationDescriptorSet,
|
||||
int(kDescriptorSetSharedMemoryAndEdram));
|
||||
builder_->addDecoration(buffer_edram_, spv::DecorationBinding, 1);
|
||||
if (features_.spirv_version >= spv::Spv_1_4) {
|
||||
main_interface_.push_back(buffer_edram_);
|
||||
}
|
||||
}
|
||||
|
||||
bool param_gen_needed = !is_depth_only_fragment_shader_ &&
|
||||
GetPsParamGenInterpolator() != UINT32_MAX;
|
||||
|
||||
if (!is_depth_only_fragment_shader_) {
|
||||
uint32_t input_location = 0;
|
||||
|
||||
// Interpolator inputs.
|
||||
{
|
||||
uint32_t interpolators_remaining = GetModificationInterpolatorMask();
|
||||
uint32_t interpolator_index;
|
||||
while (xe::bit_scan_forward(interpolators_remaining, &interpolator_index)) {
|
||||
while (
|
||||
xe::bit_scan_forward(interpolators_remaining, &interpolator_index)) {
|
||||
interpolators_remaining &= ~(UINT32_C(1) << interpolator_index);
|
||||
spv::Id interpolator = builder_->createVariable(
|
||||
spv::NoPrecision, spv::StorageClassInput, type_float4_,
|
||||
|
@ -1705,26 +1831,25 @@ void SpirvShaderTranslator::StartFragmentShaderBeforeMain() {
|
|||
}
|
||||
}
|
||||
|
||||
bool param_gen_needed = GetPsParamGenInterpolator() != UINT32_MAX;
|
||||
|
||||
// Point coordinate input.
|
||||
if (shader_modification.pixel.param_gen_point) {
|
||||
if (param_gen_needed) {
|
||||
input_point_coordinates_ =
|
||||
builder_->createVariable(spv::NoPrecision, spv::StorageClassInput,
|
||||
type_float2_, "xe_in_point_coordinates");
|
||||
builder_->addDecoration(input_point_coordinates_, spv::DecorationLocation,
|
||||
int(input_location));
|
||||
builder_->addDecoration(input_point_coordinates_,
|
||||
spv::DecorationLocation, int(input_location));
|
||||
main_interface_.push_back(input_point_coordinates_);
|
||||
}
|
||||
++input_location;
|
||||
}
|
||||
}
|
||||
|
||||
// Fragment coordinates.
|
||||
// TODO(Triang3l): More conditions - fragment shader interlock render backend,
|
||||
// alpha to coverage (if RT 0 is written, and there's no early depth /
|
||||
// stencil), depth writing in the fragment shader (per-sample if supported).
|
||||
if (param_gen_needed) {
|
||||
// TODO(Triang3l): More conditions - alpha to coverage (if RT 0 is written,
|
||||
// and there's no early depth / stencil), depth writing in the fragment shader
|
||||
// (per-sample if supported).
|
||||
if (edram_fragment_shader_interlock_ || param_gen_needed) {
|
||||
input_fragment_coordinates_ = builder_->createVariable(
|
||||
spv::NoPrecision, spv::StorageClassInput, type_float4_, "gl_FragCoord");
|
||||
builder_->addDecoration(input_fragment_coordinates_, spv::DecorationBuiltIn,
|
||||
|
@ -1733,9 +1858,9 @@ void SpirvShaderTranslator::StartFragmentShaderBeforeMain() {
|
|||
}
|
||||
|
||||
// Is front facing.
|
||||
// TODO(Triang3l): Needed for stencil in the fragment shader interlock render
|
||||
// backend.
|
||||
if (param_gen_needed && !GetSpirvShaderModification().pixel.param_gen_point) {
|
||||
if (edram_fragment_shader_interlock_ ||
|
||||
(param_gen_needed &&
|
||||
!GetSpirvShaderModification().pixel.param_gen_point)) {
|
||||
input_front_facing_ = builder_->createVariable(
|
||||
spv::NoPrecision, spv::StorageClassInput, type_bool_, "gl_FrontFacing");
|
||||
builder_->addDecoration(input_front_facing_, spv::DecorationBuiltIn,
|
||||
|
@ -1743,33 +1868,165 @@ void SpirvShaderTranslator::StartFragmentShaderBeforeMain() {
|
|||
main_interface_.push_back(input_front_facing_);
|
||||
}
|
||||
|
||||
// Framebuffer attachment outputs.
|
||||
std::fill(output_fragment_data_.begin(), output_fragment_data_.end(),
|
||||
spv::NoResult);
|
||||
static const char* const kFragmentDataNames[] = {
|
||||
// Sample mask input.
|
||||
if (edram_fragment_shader_interlock_) {
|
||||
// SampleMask depends on SampleRateShading in some SPIR-V revisions.
|
||||
builder_->addCapability(spv::CapabilitySampleRateShading);
|
||||
input_sample_mask_ = builder_->createVariable(
|
||||
spv::NoPrecision, spv::StorageClassInput,
|
||||
builder_->makeArrayType(type_int_, builder_->makeUintConstant(1), 0),
|
||||
"gl_SampleMaskIn");
|
||||
builder_->addDecoration(input_sample_mask_, spv::DecorationFlat);
|
||||
builder_->addDecoration(input_sample_mask_, spv::DecorationBuiltIn,
|
||||
spv::BuiltInSampleMask);
|
||||
main_interface_.push_back(input_sample_mask_);
|
||||
}
|
||||
|
||||
if (!is_depth_only_fragment_shader_) {
|
||||
// Framebuffer color attachment outputs.
|
||||
if (!edram_fragment_shader_interlock_) {
|
||||
std::fill(output_or_var_fragment_data_.begin(),
|
||||
output_or_var_fragment_data_.end(), spv::NoResult);
|
||||
static const char* const kFragmentDataOutputNames[] = {
|
||||
"xe_out_fragment_data_0",
|
||||
"xe_out_fragment_data_1",
|
||||
"xe_out_fragment_data_2",
|
||||
"xe_out_fragment_data_3",
|
||||
};
|
||||
uint32_t color_targets_remaining = current_shader().writes_color_targets();
|
||||
uint32_t color_targets_remaining =
|
||||
current_shader().writes_color_targets();
|
||||
uint32_t color_target_index;
|
||||
while (xe::bit_scan_forward(color_targets_remaining, &color_target_index)) {
|
||||
while (
|
||||
xe::bit_scan_forward(color_targets_remaining, &color_target_index)) {
|
||||
color_targets_remaining &= ~(UINT32_C(1) << color_target_index);
|
||||
spv::Id output_fragment_data_rt = builder_->createVariable(
|
||||
spv::NoPrecision, spv::StorageClassOutput, type_float4_,
|
||||
kFragmentDataNames[color_target_index]);
|
||||
output_fragment_data_[color_target_index] = output_fragment_data_rt;
|
||||
builder_->addDecoration(output_fragment_data_rt, spv::DecorationLocation,
|
||||
kFragmentDataOutputNames[color_target_index]);
|
||||
output_or_var_fragment_data_[color_target_index] =
|
||||
output_fragment_data_rt;
|
||||
builder_->addDecoration(output_fragment_data_rt,
|
||||
spv::DecorationLocation,
|
||||
int(color_target_index));
|
||||
// Make invariant as pixel shaders may be used for various precise
|
||||
// computations.
|
||||
builder_->addDecoration(output_fragment_data_rt, spv::DecorationInvariant);
|
||||
builder_->addDecoration(output_fragment_data_rt,
|
||||
spv::DecorationInvariant);
|
||||
main_interface_.push_back(output_fragment_data_rt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::StartFragmentShaderInMain() {
|
||||
// Set up pixel killing from within the translated shader without affecting
|
||||
// the control flow (unlike with OpKill), similarly to how pixel killing works
|
||||
// on the Xenos, and also keeping a single critical section exit and return
|
||||
// for safety across different Vulkan implementations with fragment shader
|
||||
// interlock.
|
||||
if (current_shader().kills_pixels()) {
|
||||
if (features_.demote_to_helper_invocation) {
|
||||
// TODO(Triang3l): Promoted to SPIR-V 1.6 - don't add the extension there.
|
||||
builder_->addExtension("SPV_EXT_demote_to_helper_invocation");
|
||||
builder_->addCapability(spv::CapabilityDemoteToHelperInvocationEXT);
|
||||
} else {
|
||||
var_main_kill_pixel_ = builder_->createVariable(
|
||||
spv::NoPrecision, spv::StorageClassFunction, type_bool_,
|
||||
"xe_var_kill_pixel", builder_->makeBoolConstant(false));
|
||||
}
|
||||
// For killing with fragment shader interlock when demotion is supported,
|
||||
// using OpIsHelperInvocationEXT to avoid allocating a variable in addition
|
||||
// to the execution mask GPUs naturally have.
|
||||
}
|
||||
|
||||
if (edram_fragment_shader_interlock_) {
|
||||
// Initialize color output variables with fragment shader interlock.
|
||||
std::fill(output_or_var_fragment_data_.begin(),
|
||||
output_or_var_fragment_data_.end(), spv::NoResult);
|
||||
var_main_fsi_color_written_ = spv::NoResult;
|
||||
uint32_t color_targets_written = current_shader().writes_color_targets();
|
||||
if (color_targets_written) {
|
||||
static const char* const kFragmentDataVariableNames[] = {
|
||||
"xe_var_fragment_data_0",
|
||||
"xe_var_fragment_data_1",
|
||||
"xe_var_fragment_data_2",
|
||||
"xe_var_fragment_data_3",
|
||||
};
|
||||
uint32_t color_targets_remaining = color_targets_written;
|
||||
uint32_t color_target_index;
|
||||
while (
|
||||
xe::bit_scan_forward(color_targets_remaining, &color_target_index)) {
|
||||
color_targets_remaining &= ~(UINT32_C(1) << color_target_index);
|
||||
output_or_var_fragment_data_[color_target_index] =
|
||||
builder_->createVariable(
|
||||
spv::NoPrecision, spv::StorageClassFunction, type_float4_,
|
||||
kFragmentDataVariableNames[color_target_index],
|
||||
const_float4_0_);
|
||||
}
|
||||
var_main_fsi_color_written_ = builder_->createVariable(
|
||||
spv::NoPrecision, spv::StorageClassFunction, type_uint_,
|
||||
"xe_var_fsi_color_written", const_uint_0_);
|
||||
}
|
||||
}
|
||||
|
||||
if (edram_fragment_shader_interlock_ && FSI_IsDepthStencilEarly()) {
|
||||
spv::Id msaa_samples = LoadMsaaSamplesFromFlags();
|
||||
FSI_LoadSampleMask(msaa_samples);
|
||||
FSI_LoadEdramOffsets(msaa_samples);
|
||||
builder_->createNoResultOp(spv::OpBeginInvocationInterlockEXT);
|
||||
FSI_DepthStencilTest(msaa_samples, false);
|
||||
if (!is_depth_only_fragment_shader_) {
|
||||
// Skip the rest of the shader if the whole quad (due to derivatives) has
|
||||
// failed the depth / stencil test, and there are no depth and stencil
|
||||
// values to conditionally write after running the shader to check if
|
||||
// samples don't additionally need to be discarded.
|
||||
spv::Id quad_needs_execution = builder_->createBinOp(
|
||||
spv::OpINotEqual, type_bool_, main_fsi_sample_mask_, const_uint_0_);
|
||||
// TODO(Triang3l): Use GroupNonUniformQuad operations where supported.
|
||||
// If none of the pixels in the quad passed the depth / stencil test, the
|
||||
// value of (any samples covered ? 1.0f : 0.0f) for the current pixel will
|
||||
// be 0.0f, and since it will be 0.0f in other pixels too, the derivatives
|
||||
// will be zero as well.
|
||||
builder_->addCapability(spv::CapabilityDerivativeControl);
|
||||
// Query the horizontally adjacent pixel.
|
||||
quad_needs_execution = builder_->createBinOp(
|
||||
spv::OpLogicalOr, type_bool_, quad_needs_execution,
|
||||
builder_->createBinOp(
|
||||
spv::OpFOrdNotEqual, type_bool_,
|
||||
builder_->createUnaryOp(
|
||||
spv::OpDPdxFine, type_float_,
|
||||
builder_->createTriOp(spv::OpSelect, type_float_,
|
||||
quad_needs_execution, const_float_1_,
|
||||
const_float_0_)),
|
||||
const_float_0_));
|
||||
// Query the vertically adjacent pair of pixels.
|
||||
quad_needs_execution = builder_->createBinOp(
|
||||
spv::OpLogicalOr, type_bool_, quad_needs_execution,
|
||||
builder_->createBinOp(
|
||||
spv::OpFOrdNotEqual, type_bool_,
|
||||
builder_->createUnaryOp(
|
||||
spv::OpDPdyCoarse, type_float_,
|
||||
builder_->createTriOp(spv::OpSelect, type_float_,
|
||||
quad_needs_execution, const_float_1_,
|
||||
const_float_0_)),
|
||||
const_float_0_));
|
||||
spv::Block& main_fsi_early_depth_stencil_execute_quad =
|
||||
builder_->makeNewBlock();
|
||||
main_fsi_early_depth_stencil_execute_quad_merge_ =
|
||||
&builder_->makeNewBlock();
|
||||
SpirvCreateSelectionMerge(
|
||||
main_fsi_early_depth_stencil_execute_quad_merge_->getId(),
|
||||
spv::SelectionControlDontFlattenMask);
|
||||
builder_->createConditionalBranch(
|
||||
quad_needs_execution, &main_fsi_early_depth_stencil_execute_quad,
|
||||
main_fsi_early_depth_stencil_execute_quad_merge_);
|
||||
builder_->setBuildPoint(&main_fsi_early_depth_stencil_execute_quad);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_depth_only_fragment_shader_) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t param_gen_interpolator = GetPsParamGenInterpolator();
|
||||
|
||||
// Zero general-purpose registers to prevent crashes when the game
|
||||
|
@ -1928,13 +2185,15 @@ void SpirvShaderTranslator::StartFragmentShaderInMain() {
|
|||
var_main_registers_, id_vector_temp_));
|
||||
}
|
||||
|
||||
if (!edram_fragment_shader_interlock_) {
|
||||
// Initialize the colors for safety.
|
||||
for (uint32_t i = 0; i < xenos::kMaxColorRenderTargets; ++i) {
|
||||
spv::Id output_fragment_data_rt = output_fragment_data_[i];
|
||||
spv::Id output_fragment_data_rt = output_or_var_fragment_data_[i];
|
||||
if (output_fragment_data_rt != spv::NoResult) {
|
||||
builder_->createStore(const_float4_0_, output_fragment_data_rt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::UpdateExecConditionals(
|
||||
|
@ -2299,11 +2558,18 @@ void SpirvShaderTranslator::StoreResult(const InstructionResult& result,
|
|||
assert_true(is_pixel_shader());
|
||||
assert_not_zero(used_write_mask);
|
||||
assert_true(current_shader().writes_color_target(result.storage_index));
|
||||
target_pointer = output_fragment_data_[result.storage_index];
|
||||
// May be spv::NoResult if the color output is explicitly removed due to
|
||||
// an empty write mask without independent blending.
|
||||
// TODO(Triang3l): Store the alpha of the first output in this case for
|
||||
// alpha test and alpha to coverage.
|
||||
target_pointer = output_or_var_fragment_data_[result.storage_index];
|
||||
if (edram_fragment_shader_interlock_) {
|
||||
assert_true(var_main_fsi_color_written_ != spv::NoResult);
|
||||
builder_->createStore(
|
||||
builder_->createBinOp(
|
||||
spv::OpBitwiseOr, type_uint_,
|
||||
builder_->createLoad(var_main_fsi_color_written_,
|
||||
spv::NoPrecision),
|
||||
builder_->makeUintConstant(uint32_t(1)
|
||||
<< result.storage_index)),
|
||||
var_main_fsi_color_written_);
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
// TODO(Triang3l): All storage targets.
|
||||
|
|
|
@ -96,6 +96,9 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
kSysFlag_WNotReciprocal_Shift,
|
||||
kSysFlag_PrimitivePolygonal_Shift,
|
||||
kSysFlag_PrimitiveLine_Shift,
|
||||
kSysFlag_MsaaSamples_Shift,
|
||||
kSysFlag_DepthFloat24_Shift =
|
||||
kSysFlag_MsaaSamples_Shift + xenos::kMsaaSamplesBits,
|
||||
kSysFlag_AlphaPassIfLess_Shift,
|
||||
kSysFlag_AlphaPassIfEqual_Shift,
|
||||
kSysFlag_AlphaPassIfGreater_Shift,
|
||||
|
@ -104,6 +107,26 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
kSysFlag_ConvertColor2ToGamma_Shift,
|
||||
kSysFlag_ConvertColor3ToGamma_Shift,
|
||||
|
||||
kSysFlag_FSIDepthStencil_Shift,
|
||||
kSysFlag_FSIDepthPassIfLess_Shift,
|
||||
kSysFlag_FSIDepthPassIfEqual_Shift,
|
||||
kSysFlag_FSIDepthPassIfGreater_Shift,
|
||||
// 1 to write new depth to the depth buffer, 0 to keep the old one if the
|
||||
// depth test passes.
|
||||
kSysFlag_FSIDepthWrite_Shift,
|
||||
kSysFlag_FSIStencilTest_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_FSIDepthStencilEarlyWrite_Shift,
|
||||
|
||||
kSysFlag_Count,
|
||||
|
||||
// For HostVertexShaderType kVertex, if fullDrawIndexUint32 is not
|
||||
|
@ -127,6 +150,7 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
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,
|
||||
|
@ -134,6 +158,14 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
kSysFlag_ConvertColor1ToGamma = 1u << kSysFlag_ConvertColor1ToGamma_Shift,
|
||||
kSysFlag_ConvertColor2ToGamma = 1u << kSysFlag_ConvertColor2ToGamma_Shift,
|
||||
kSysFlag_ConvertColor3ToGamma = 1u << kSysFlag_ConvertColor3ToGamma_Shift,
|
||||
kSysFlag_FSIDepthStencil = 1u << kSysFlag_FSIDepthStencil_Shift,
|
||||
kSysFlag_FSIDepthPassIfLess = 1u << kSysFlag_FSIDepthPassIfLess_Shift,
|
||||
kSysFlag_FSIDepthPassIfEqual = 1u << kSysFlag_FSIDepthPassIfEqual_Shift,
|
||||
kSysFlag_FSIDepthPassIfGreater = 1u << kSysFlag_FSIDepthPassIfGreater_Shift,
|
||||
kSysFlag_FSIDepthWrite = 1u << kSysFlag_FSIDepthWrite_Shift,
|
||||
kSysFlag_FSIStencilTest = 1u << kSysFlag_FSIStencilTest_Shift,
|
||||
kSysFlag_FSIDepthStencilEarlyWrite =
|
||||
1u << kSysFlag_FSIDepthStencilEarlyWrite_Shift,
|
||||
};
|
||||
static_assert(kSysFlag_Count <= 32, "Too many flags in the system constants");
|
||||
|
||||
|
@ -171,9 +203,55 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
uint32_t texture_swizzles[16];
|
||||
|
||||
float alpha_test_reference;
|
||||
float padding_alpha_test_reference[3];
|
||||
uint32_t edram_32bpp_tile_pitch_dwords_scaled;
|
||||
uint32_t edram_depth_base_dwords_scaled;
|
||||
float padding_edram_depth_base_dwords_scaled;
|
||||
|
||||
float color_exp_bias[4];
|
||||
|
||||
float edram_poly_offset_front_scale;
|
||||
float edram_poly_offset_back_scale;
|
||||
float edram_poly_offset_front_offset;
|
||||
float edram_poly_offset_back_offset;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t edram_stencil_front_reference_masks;
|
||||
uint32_t edram_stencil_front_func_ops;
|
||||
|
||||
uint32_t edram_stencil_back_reference_masks;
|
||||
uint32_t edram_stencil_back_func_ops;
|
||||
};
|
||||
struct {
|
||||
uint32_t edram_stencil_front[2];
|
||||
uint32_t edram_stencil_back[2];
|
||||
};
|
||||
};
|
||||
|
||||
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];
|
||||
|
||||
// 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];
|
||||
|
||||
// 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];
|
||||
|
||||
// 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];
|
||||
|
||||
// The constant blend factor for the respective modes.
|
||||
float edram_blend_constant[4];
|
||||
};
|
||||
|
||||
enum ConstantBuffer : uint32_t {
|
||||
|
@ -248,12 +326,22 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
uint32_t max_storage_buffer_range;
|
||||
bool clip_distance;
|
||||
bool cull_distance;
|
||||
bool demote_to_helper_invocation;
|
||||
bool fragment_shader_sample_interlock;
|
||||
bool full_draw_index_uint32;
|
||||
bool image_view_format_swizzle;
|
||||
bool signed_zero_inf_nan_preserve_float32;
|
||||
bool denorm_flush_to_zero_float32;
|
||||
};
|
||||
SpirvShaderTranslator(const Features& features);
|
||||
|
||||
SpirvShaderTranslator(const Features& features,
|
||||
bool native_2x_msaa_with_attachments,
|
||||
bool native_2x_msaa_no_attachments,
|
||||
bool edram_fragment_shader_interlock)
|
||||
: features_(features),
|
||||
native_2x_msaa_with_attachments_(native_2x_msaa_with_attachments),
|
||||
native_2x_msaa_no_attachments_(native_2x_msaa_no_attachments),
|
||||
edram_fragment_shader_interlock_(edram_fragment_shader_interlock) {}
|
||||
|
||||
uint64_t GetDefaultVertexShaderModification(
|
||||
uint32_t dynamic_addressable_register_count,
|
||||
|
@ -277,6 +365,10 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
features_.max_storage_buffer_range);
|
||||
}
|
||||
|
||||
// Creates a special fragment shader without color outputs - this resets the
|
||||
// state of the translator.
|
||||
std::vector<uint8_t> CreateDepthOnlyFragmentShader();
|
||||
|
||||
// Common functions useful not only for the translator, but also for EDRAM
|
||||
// emulation via conventional render targets.
|
||||
|
||||
|
@ -385,10 +477,10 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
}
|
||||
|
||||
bool IsExecutionModeEarlyFragmentTests() const {
|
||||
// TODO(Triang3l): Not applicable to fragment shader interlock.
|
||||
return is_pixel_shader() &&
|
||||
GetSpirvShaderModification().pixel.depth_stencil_mode ==
|
||||
Modification::DepthStencilMode::kEarlyHint &&
|
||||
!edram_fragment_shader_interlock_ &&
|
||||
current_shader().implicit_early_z_write_allowed();
|
||||
}
|
||||
|
||||
|
@ -528,7 +620,72 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
spv::Id image_unsigned, spv::Id image_signed,
|
||||
spv::Id sampler, spv::Id is_all_signed);
|
||||
|
||||
spv::Id LoadMsaaSamplesFromFlags();
|
||||
// Whether it's possible and worth skipping running the translated shader for
|
||||
// 2x2 quads.
|
||||
bool FSI_IsDepthStencilEarly() const {
|
||||
assert_true(edram_fragment_shader_interlock_);
|
||||
return !is_depth_only_fragment_shader_ &&
|
||||
!current_shader().writes_depth() &&
|
||||
!current_shader().is_valid_memexport_used();
|
||||
}
|
||||
void FSI_LoadSampleMask(spv::Id msaa_samples);
|
||||
void FSI_LoadEdramOffsets(spv::Id msaa_samples);
|
||||
// The address must be a signed int. Whether the render target is 64bpp, if
|
||||
// present at all, must be a bool (if it's NoResult, 32bpp will be assumed).
|
||||
spv::Id FSI_AddSampleOffset(spv::Id sample_0_address, uint32_t sample_index,
|
||||
spv::Id is_64bpp = spv::NoResult);
|
||||
// Updates main_fsi_sample_mask_. Must be called outside non-uniform control
|
||||
// flow because of taking derivatives of the fragment depth.
|
||||
void FSI_DepthStencilTest(spv::Id msaa_samples,
|
||||
bool sample_mask_potentially_narrowed_previouly);
|
||||
// Returns the first and the second 32 bits as two uints.
|
||||
std::array<spv::Id, 2> FSI_ClampAndPackColor(spv::Id color_float4,
|
||||
spv::Id format_with_flags);
|
||||
std::array<spv::Id, 4> FSI_UnpackColor(std::array<spv::Id, 2> color_packed,
|
||||
spv::Id format_with_flags);
|
||||
// The bounds must have the same number of components as the color or alpha.
|
||||
spv::Id FSI_FlushNaNClampAndInBlending(spv::Id color_or_alpha,
|
||||
spv::Id is_fixed_point,
|
||||
spv::Id min_value, spv::Id max_value);
|
||||
spv::Id FSI_ApplyColorBlendFactor(spv::Id value, spv::Id is_fixed_point,
|
||||
spv::Id clamp_min_value,
|
||||
spv::Id clamp_max_value, spv::Id factor,
|
||||
spv::Id source_color, spv::Id source_alpha,
|
||||
spv::Id dest_color, spv::Id dest_alpha,
|
||||
spv::Id constant_color,
|
||||
spv::Id constant_alpha);
|
||||
spv::Id FSI_ApplyAlphaBlendFactor(spv::Id value, spv::Id is_fixed_point,
|
||||
spv::Id clamp_min_value,
|
||||
spv::Id clamp_max_value, spv::Id factor,
|
||||
spv::Id source_alpha, spv::Id dest_alpha,
|
||||
spv::Id constant_alpha);
|
||||
// If source_color_clamped, dest_color, constant_color_clamped are
|
||||
// spv::NoResult, will blend the alpha. Otherwise, will blend the color.
|
||||
// The result will be unclamped (color packing is supposed to clamp it).
|
||||
spv::Id FSI_BlendColorOrAlphaWithUnclampedResult(
|
||||
spv::Id is_fixed_point, spv::Id clamp_min_value, spv::Id clamp_max_value,
|
||||
spv::Id source_color_clamped, spv::Id source_alpha_clamped,
|
||||
spv::Id dest_color, spv::Id dest_alpha, spv::Id constant_color_clamped,
|
||||
spv::Id constant_alpha_clamped, spv::Id equation, spv::Id source_factor,
|
||||
spv::Id dest_factor);
|
||||
|
||||
Features features_;
|
||||
bool native_2x_msaa_with_attachments_;
|
||||
bool native_2x_msaa_no_attachments_;
|
||||
|
||||
// For safety with different drivers (even though fragment shader interlock in
|
||||
// SPIR-V only has one control flow requirement - that both begin and end must
|
||||
// be dynamically executed exactly once in this order), adhering to the more
|
||||
// strict control flow limitations of OpenGL (GLSL) fragment shader interlock,
|
||||
// that begin and end are called only on the outermost level of the control
|
||||
// flow of the main function, and that there are no returns before either
|
||||
// (there's a single return from the shader).
|
||||
bool edram_fragment_shader_interlock_;
|
||||
|
||||
// Is currently writing the empty depth-only pixel shader, such as for depth
|
||||
// and stencil testing with fragment shader interlock.
|
||||
bool is_depth_only_fragment_shader_ = false;
|
||||
|
||||
std::unique_ptr<spv::Builder> builder_;
|
||||
|
||||
|
@ -621,7 +778,23 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
kSystemConstantTextureSwizzledSigns,
|
||||
kSystemConstantTextureSwizzles,
|
||||
kSystemConstantAlphaTestReference,
|
||||
kSystemConstantEdram32bppTilePitchDwordsScaled,
|
||||
kSystemConstantEdramDepthBaseDwordsScaled,
|
||||
kSystemConstantColorExpBias,
|
||||
kSystemConstantEdramPolyOffsetFrontScale,
|
||||
kSystemConstantEdramPolyOffsetBackScale,
|
||||
kSystemConstantEdramPolyOffsetFrontOffset,
|
||||
kSystemConstantEdramPolyOffsetBackOffset,
|
||||
kSystemConstantEdramStencilFront,
|
||||
kSystemConstantEdramStencilBack,
|
||||
kSystemConstantEdramRTBaseDwordsScaled,
|
||||
kSystemConstantEdramRTFormatFlags,
|
||||
kSystemConstantEdramRTBlendFactorsOps,
|
||||
// Accessed as float4[2], not float2[4], due to std140 array stride
|
||||
// alignment.
|
||||
kSystemConstantEdramRTKeepMask,
|
||||
kSystemConstantEdramRTClamp,
|
||||
kSystemConstantEdramBlendConstant,
|
||||
};
|
||||
spv::Id uniform_system_constants_;
|
||||
spv::Id uniform_float_constants_;
|
||||
|
@ -629,6 +802,7 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
spv::Id uniform_fetch_constants_;
|
||||
|
||||
spv::Id buffers_shared_memory_;
|
||||
spv::Id buffer_edram_;
|
||||
|
||||
// Not using combined images and samplers because
|
||||
// maxPerStageDescriptorSamplers is often lower than
|
||||
|
@ -647,6 +821,8 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
spv::Id input_fragment_coordinates_;
|
||||
// PS, only when needed - bool.
|
||||
spv::Id input_front_facing_;
|
||||
// PS, only when needed - int[1].
|
||||
spv::Id input_sample_mask_;
|
||||
|
||||
// VS output or PS input, only the ones that are needed (spv::NoResult for the
|
||||
// unneeded interpolators), indexed by the guest interpolator index - float4.
|
||||
|
@ -671,7 +847,10 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
};
|
||||
spv::Id output_per_vertex_;
|
||||
|
||||
std::array<spv::Id, xenos::kMaxColorRenderTargets> output_fragment_data_;
|
||||
// With fragment shader interlock, variables in the main function.
|
||||
// Otherwise, framebuffer color attachment outputs.
|
||||
std::array<spv::Id, xenos::kMaxColorRenderTargets>
|
||||
output_or_var_fragment_data_;
|
||||
|
||||
std::vector<spv::Id> main_interface_;
|
||||
spv::Function* function_main_;
|
||||
|
@ -698,6 +877,40 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
spv::Id var_main_registers_;
|
||||
// VS only - float3 (special exports).
|
||||
spv::Id var_main_point_size_edge_flag_kill_vertex_;
|
||||
// PS, only when needed - bool.
|
||||
spv::Id var_main_kill_pixel_;
|
||||
// PS, only when writing to color render targets with fragment shader
|
||||
// interlock - uint.
|
||||
// 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).
|
||||
spv::Id var_main_fsi_color_written_;
|
||||
// Loaded by FSI_LoadSampleMask.
|
||||
// Can be modified on the outermost control flow level in the main function.
|
||||
// 0:3 - Per-sample coverage at the current stage of the shader's execution.
|
||||
// Affected by things like gl_SampleMaskIn, 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.
|
||||
spv::Id main_fsi_sample_mask_;
|
||||
// Loaded by FSI_LoadEdramOffsets.
|
||||
// Including the depth render target base.
|
||||
spv::Id main_fsi_address_depth_;
|
||||
// Not including the render target base.
|
||||
spv::Id main_fsi_offset_32bpp_;
|
||||
spv::Id main_fsi_offset_64bpp_;
|
||||
// Loaded by FSI_DepthStencilTest for early depth / stencil, the depth /
|
||||
// stencil values to write at the end of the shader if the specified in
|
||||
// main_fsi_sample_mask_ and if the samples were not discarded later after the
|
||||
// early test.
|
||||
std::array<spv::Id, 4> main_fsi_late_write_depth_stencil_;
|
||||
spv::Block* main_fsi_early_depth_stencil_execute_quad_merge_;
|
||||
spv::Block* main_loop_header_;
|
||||
spv::Block* main_loop_continue_;
|
||||
spv::Block* main_loop_merge_;
|
||||
|
|
|
@ -123,7 +123,7 @@ spv::Id SpirvShaderTranslator::ProcessVectorAluOperation(
|
|||
: spv::NoType;
|
||||
|
||||
// In case the paired scalar instruction (if processed first) terminates the
|
||||
// block (like via OpKill).
|
||||
// block.
|
||||
EnsureBuildPointAvailable();
|
||||
|
||||
// Lookup table for variants of instructions with similar structure.
|
||||
|
@ -838,9 +838,15 @@ spv::Id SpirvShaderTranslator::ProcessVectorAluOperation(
|
|||
SpirvCreateSelectionMerge(merge_block.getId());
|
||||
builder_->createConditionalBranch(condition, &kill_block, &merge_block);
|
||||
builder_->setBuildPoint(&kill_block);
|
||||
// TODO(Triang3l): Demote to helper invocation to keep derivatives if
|
||||
// needed (and return 1 if killed in this case).
|
||||
builder_->createNoResultOp(spv::OpKill);
|
||||
// Kill without influencing the control flow in the translated shader.
|
||||
if (var_main_kill_pixel_ != spv::NoResult) {
|
||||
builder_->createStore(builder_->makeBoolConstant(true),
|
||||
var_main_kill_pixel_);
|
||||
}
|
||||
if (features_.demote_to_helper_invocation) {
|
||||
builder_->createNoResultOp(spv::OpDemoteToHelperInvocationEXT);
|
||||
}
|
||||
builder_->createBranch(&merge_block);
|
||||
builder_->setBuildPoint(&merge_block);
|
||||
return const_float_0_;
|
||||
}
|
||||
|
@ -938,7 +944,7 @@ spv::Id SpirvShaderTranslator::ProcessScalarAluOperation(
|
|||
}
|
||||
|
||||
// In case the paired vector instruction (if processed first) terminates the
|
||||
// block (like via OpKill).
|
||||
// block.
|
||||
EnsureBuildPointAvailable();
|
||||
|
||||
// Lookup table for variants of instructions with similar structure.
|
||||
|
@ -1393,9 +1399,15 @@ spv::Id SpirvShaderTranslator::ProcessScalarAluOperation(
|
|||
SpirvCreateSelectionMerge(merge_block.getId());
|
||||
builder_->createConditionalBranch(condition, &kill_block, &merge_block);
|
||||
builder_->setBuildPoint(&kill_block);
|
||||
// TODO(Triang3l): Demote to helper invocation to keep derivatives if
|
||||
// needed (and return 1 if killed in this case).
|
||||
builder_->createNoResultOp(spv::OpKill);
|
||||
// Kill without influencing the control flow in the translated shader.
|
||||
if (var_main_kill_pixel_ != spv::NoResult) {
|
||||
builder_->createStore(builder_->makeBoolConstant(true),
|
||||
var_main_kill_pixel_);
|
||||
}
|
||||
if (features_.demote_to_helper_invocation) {
|
||||
builder_->createNoResultOp(spv::OpDemoteToHelperInvocationEXT);
|
||||
}
|
||||
builder_->createBranch(&merge_block);
|
||||
builder_->setBuildPoint(&merge_block);
|
||||
return const_float_0_;
|
||||
}
|
||||
|
|
|
@ -1898,30 +1898,14 @@ void SpirvShaderTranslator::ProcessTextureFetchInstruction(
|
|||
builder_->setBuildPoint(&block_dimension_stacked_start);
|
||||
if (use_computed_lod) {
|
||||
// Extract 2D gradients for stacked textures which are 2D arrays.
|
||||
{
|
||||
std::unique_ptr<spv::Instruction> shuffle_op =
|
||||
std::make_unique<spv::Instruction>(builder_->getUniqueId(),
|
||||
type_float2_,
|
||||
spv::OpVectorShuffle);
|
||||
shuffle_op->addIdOperand(gradients_h);
|
||||
shuffle_op->addIdOperand(gradients_h);
|
||||
shuffle_op->addImmediateOperand(0);
|
||||
shuffle_op->addImmediateOperand(1);
|
||||
texture_parameters.gradX = shuffle_op->getResultId();
|
||||
builder_->getBuildPoint()->addInstruction(std::move(shuffle_op));
|
||||
}
|
||||
{
|
||||
std::unique_ptr<spv::Instruction> shuffle_op =
|
||||
std::make_unique<spv::Instruction>(builder_->getUniqueId(),
|
||||
type_float2_,
|
||||
spv::OpVectorShuffle);
|
||||
shuffle_op->addIdOperand(gradients_v);
|
||||
shuffle_op->addIdOperand(gradients_v);
|
||||
shuffle_op->addImmediateOperand(0);
|
||||
shuffle_op->addImmediateOperand(1);
|
||||
texture_parameters.gradY = shuffle_op->getResultId();
|
||||
builder_->getBuildPoint()->addInstruction(std::move(shuffle_op));
|
||||
}
|
||||
uint_vector_temp_.clear();
|
||||
uint_vector_temp_.reserve(2);
|
||||
uint_vector_temp_.push_back(0);
|
||||
uint_vector_temp_.push_back(1);
|
||||
texture_parameters.gradX = builder_->createRvalueSwizzle(
|
||||
spv::NoPrecision, type_float2_, gradients_h, uint_vector_temp_);
|
||||
texture_parameters.gradY = builder_->createRvalueSwizzle(
|
||||
spv::NoPrecision, type_float2_, gradients_v, uint_vector_temp_);
|
||||
}
|
||||
// Check if linear filtering is needed.
|
||||
bool vol_mag_filter_is_fetch_const =
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -67,9 +67,6 @@ const VkDescriptorPoolSize
|
|||
{VK_DESCRIPTOR_TYPE_SAMPLER, kLinkedTypeDescriptorPoolSetCount},
|
||||
};
|
||||
|
||||
// No specific reason for 32768 descriptors, just the "too much" amount from
|
||||
// Direct3D 12 PIX warnings. 2x descriptors for textures because of unsigned and
|
||||
// signed bindings.
|
||||
VulkanCommandProcessor::VulkanCommandProcessor(
|
||||
VulkanGraphicsSystem* graphics_system, kernel::KernelState* kernel_state)
|
||||
: CommandProcessor(graphics_system, kernel_state),
|
||||
|
@ -106,6 +103,32 @@ void VulkanCommandProcessor::TracePlaybackWroteMemory(uint32_t base_ptr,
|
|||
|
||||
void VulkanCommandProcessor::RestoreEdramSnapshot(const void* snapshot) {}
|
||||
|
||||
std::string VulkanCommandProcessor::GetWindowTitleText() const {
|
||||
std::ostringstream title;
|
||||
title << "Vulkan";
|
||||
if (render_target_cache_) {
|
||||
switch (render_target_cache_->GetPath()) {
|
||||
case RenderTargetCache::Path::kHostRenderTargets:
|
||||
title << " - FBO";
|
||||
break;
|
||||
case RenderTargetCache::Path::kPixelShaderInterlock:
|
||||
title << " - FSI";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
uint32_t draw_resolution_scale_x =
|
||||
texture_cache_ ? texture_cache_->draw_resolution_scale_x() : 1;
|
||||
uint32_t draw_resolution_scale_y =
|
||||
texture_cache_ ? texture_cache_->draw_resolution_scale_y() : 1;
|
||||
if (draw_resolution_scale_x > 1 || draw_resolution_scale_y > 1) {
|
||||
title << ' ' << draw_resolution_scale_x << 'x' << draw_resolution_scale_y;
|
||||
}
|
||||
}
|
||||
title << " - HEAVILY INCOMPLETE, early development";
|
||||
return title.str();
|
||||
}
|
||||
|
||||
bool VulkanCommandProcessor::SetupContext() {
|
||||
if (!CommandProcessor::SetupContext()) {
|
||||
XELOGE("Failed to initialize base command processor context");
|
||||
|
@ -146,7 +169,7 @@ bool VulkanCommandProcessor::SetupContext() {
|
|||
size_t(16384)),
|
||||
size_t(uniform_buffer_alignment)));
|
||||
|
||||
// Descriptor set layouts.
|
||||
// Descriptor set layouts that don't depend on the setup of other subsystems.
|
||||
VkShaderStageFlags guest_shader_stages =
|
||||
guest_shader_vertex_stages_ | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
// Empty.
|
||||
|
@ -163,37 +186,6 @@ bool VulkanCommandProcessor::SetupContext() {
|
|||
XELOGE("Failed to create an empty Vulkan descriptor set layout");
|
||||
return false;
|
||||
}
|
||||
// Shared memory and EDRAM.
|
||||
uint32_t shared_memory_binding_count_log2 =
|
||||
SpirvShaderTranslator::GetSharedMemoryStorageBufferCountLog2(
|
||||
provider.device_properties().limits.maxStorageBufferRange);
|
||||
uint32_t shared_memory_binding_count = UINT32_C(1)
|
||||
<< shared_memory_binding_count_log2;
|
||||
VkDescriptorSetLayoutBinding
|
||||
descriptor_set_layout_bindings_shared_memory_and_edram[1];
|
||||
descriptor_set_layout_bindings_shared_memory_and_edram[0].binding = 0;
|
||||
descriptor_set_layout_bindings_shared_memory_and_edram[0].descriptorType =
|
||||
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
descriptor_set_layout_bindings_shared_memory_and_edram[0].descriptorCount =
|
||||
shared_memory_binding_count;
|
||||
descriptor_set_layout_bindings_shared_memory_and_edram[0].stageFlags =
|
||||
guest_shader_stages;
|
||||
descriptor_set_layout_bindings_shared_memory_and_edram[0].pImmutableSamplers =
|
||||
nullptr;
|
||||
// TODO(Triang3l): EDRAM storage image binding for the fragment shader
|
||||
// interlocks case.
|
||||
descriptor_set_layout_create_info.bindingCount = uint32_t(
|
||||
xe::countof(descriptor_set_layout_bindings_shared_memory_and_edram));
|
||||
descriptor_set_layout_create_info.pBindings =
|
||||
descriptor_set_layout_bindings_shared_memory_and_edram;
|
||||
if (dfn.vkCreateDescriptorSetLayout(
|
||||
device, &descriptor_set_layout_create_info, nullptr,
|
||||
&descriptor_set_layout_shared_memory_and_edram_) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"Failed to create a Vulkan descriptor set layout for the shared memory "
|
||||
"and the EDRAM");
|
||||
return false;
|
||||
}
|
||||
// Guest draw constants.
|
||||
VkDescriptorSetLayoutBinding descriptor_set_layout_bindings_constants
|
||||
[SpirvShaderTranslator::kConstantBufferCount] = {};
|
||||
|
@ -289,16 +281,70 @@ bool VulkanCommandProcessor::SetupContext() {
|
|||
return false;
|
||||
}
|
||||
|
||||
uint32_t shared_memory_binding_count_log2 =
|
||||
SpirvShaderTranslator::GetSharedMemoryStorageBufferCountLog2(
|
||||
provider.device_properties().limits.maxStorageBufferRange);
|
||||
uint32_t shared_memory_binding_count = UINT32_C(1)
|
||||
<< shared_memory_binding_count_log2;
|
||||
|
||||
// Requires the transient descriptor set layouts.
|
||||
// TODO(Triang3l): Get the actual draw resolution scale when the texture cache
|
||||
// supports resolution scaling.
|
||||
render_target_cache_ = std::make_unique<VulkanRenderTargetCache>(
|
||||
*register_file_, *memory_, trace_writer_, 1, 1, *this);
|
||||
if (!render_target_cache_->Initialize()) {
|
||||
if (!render_target_cache_->Initialize(shared_memory_binding_count)) {
|
||||
XELOGE("Failed to initialize the render target cache");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Shared memory and EDRAM descriptor set layout.
|
||||
bool edram_fragment_shader_interlock =
|
||||
render_target_cache_->GetPath() ==
|
||||
RenderTargetCache::Path::kPixelShaderInterlock;
|
||||
VkDescriptorSetLayoutBinding
|
||||
shared_memory_and_edram_descriptor_set_layout_bindings[2];
|
||||
shared_memory_and_edram_descriptor_set_layout_bindings[0].binding = 0;
|
||||
shared_memory_and_edram_descriptor_set_layout_bindings[0].descriptorType =
|
||||
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
shared_memory_and_edram_descriptor_set_layout_bindings[0].descriptorCount =
|
||||
shared_memory_binding_count;
|
||||
shared_memory_and_edram_descriptor_set_layout_bindings[0].stageFlags =
|
||||
guest_shader_stages;
|
||||
shared_memory_and_edram_descriptor_set_layout_bindings[0].pImmutableSamplers =
|
||||
nullptr;
|
||||
VkDescriptorSetLayoutCreateInfo
|
||||
shared_memory_and_edram_descriptor_set_layout_create_info;
|
||||
shared_memory_and_edram_descriptor_set_layout_create_info.sType =
|
||||
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||
shared_memory_and_edram_descriptor_set_layout_create_info.pNext = nullptr;
|
||||
shared_memory_and_edram_descriptor_set_layout_create_info.flags = 0;
|
||||
shared_memory_and_edram_descriptor_set_layout_create_info.pBindings =
|
||||
shared_memory_and_edram_descriptor_set_layout_bindings;
|
||||
if (edram_fragment_shader_interlock) {
|
||||
// EDRAM.
|
||||
shared_memory_and_edram_descriptor_set_layout_bindings[1].binding = 1;
|
||||
shared_memory_and_edram_descriptor_set_layout_bindings[1].descriptorType =
|
||||
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
shared_memory_and_edram_descriptor_set_layout_bindings[1].descriptorCount =
|
||||
1;
|
||||
shared_memory_and_edram_descriptor_set_layout_bindings[1].stageFlags =
|
||||
VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
shared_memory_and_edram_descriptor_set_layout_bindings[1]
|
||||
.pImmutableSamplers = nullptr;
|
||||
shared_memory_and_edram_descriptor_set_layout_create_info.bindingCount = 2;
|
||||
} else {
|
||||
shared_memory_and_edram_descriptor_set_layout_create_info.bindingCount = 1;
|
||||
}
|
||||
if (dfn.vkCreateDescriptorSetLayout(
|
||||
device, &shared_memory_and_edram_descriptor_set_layout_create_info,
|
||||
nullptr,
|
||||
&descriptor_set_layout_shared_memory_and_edram_) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"Failed to create a Vulkan descriptor set layout for the shared memory "
|
||||
"and the EDRAM");
|
||||
return false;
|
||||
}
|
||||
|
||||
pipeline_cache_ = std::make_unique<VulkanPipelineCache>(
|
||||
*this, *register_file_, *render_target_cache_,
|
||||
guest_shader_vertex_stages_);
|
||||
|
@ -320,9 +366,8 @@ bool VulkanCommandProcessor::SetupContext() {
|
|||
// Shared memory and EDRAM common bindings.
|
||||
VkDescriptorPoolSize descriptor_pool_sizes[1];
|
||||
descriptor_pool_sizes[0].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
descriptor_pool_sizes[0].descriptorCount = shared_memory_binding_count;
|
||||
// TODO(Triang3l): EDRAM storage image binding for the fragment shader
|
||||
// interlocks case.
|
||||
descriptor_pool_sizes[0].descriptorCount =
|
||||
shared_memory_binding_count + uint32_t(edram_fragment_shader_interlock);
|
||||
VkDescriptorPoolCreateInfo descriptor_pool_create_info;
|
||||
descriptor_pool_create_info.sType =
|
||||
VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
|
@ -369,20 +414,45 @@ bool VulkanCommandProcessor::SetupContext() {
|
|||
shared_memory_binding_range * i;
|
||||
shared_memory_descriptor_buffer_info.range = shared_memory_binding_range;
|
||||
}
|
||||
VkWriteDescriptorSet write_descriptor_sets[1];
|
||||
write_descriptor_sets[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write_descriptor_sets[0].pNext = nullptr;
|
||||
write_descriptor_sets[0].dstSet = shared_memory_and_edram_descriptor_set_;
|
||||
write_descriptor_sets[0].dstBinding = 0;
|
||||
write_descriptor_sets[0].dstArrayElement = 0;
|
||||
write_descriptor_sets[0].descriptorCount = shared_memory_binding_count;
|
||||
write_descriptor_sets[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
write_descriptor_sets[0].pImageInfo = nullptr;
|
||||
write_descriptor_sets[0].pBufferInfo = shared_memory_descriptor_buffers_info;
|
||||
write_descriptor_sets[0].pTexelBufferView = nullptr;
|
||||
// TODO(Triang3l): EDRAM storage image binding for the fragment shader
|
||||
// interlocks case.
|
||||
dfn.vkUpdateDescriptorSets(device, 1, write_descriptor_sets, 0, nullptr);
|
||||
VkWriteDescriptorSet write_descriptor_sets[2];
|
||||
VkWriteDescriptorSet& write_descriptor_set_shared_memory =
|
||||
write_descriptor_sets[0];
|
||||
write_descriptor_set_shared_memory.sType =
|
||||
VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write_descriptor_set_shared_memory.pNext = nullptr;
|
||||
write_descriptor_set_shared_memory.dstSet =
|
||||
shared_memory_and_edram_descriptor_set_;
|
||||
write_descriptor_set_shared_memory.dstBinding = 0;
|
||||
write_descriptor_set_shared_memory.dstArrayElement = 0;
|
||||
write_descriptor_set_shared_memory.descriptorCount =
|
||||
shared_memory_binding_count;
|
||||
write_descriptor_set_shared_memory.descriptorType =
|
||||
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
write_descriptor_set_shared_memory.pImageInfo = nullptr;
|
||||
write_descriptor_set_shared_memory.pBufferInfo =
|
||||
shared_memory_descriptor_buffers_info;
|
||||
write_descriptor_set_shared_memory.pTexelBufferView = nullptr;
|
||||
VkDescriptorBufferInfo edram_descriptor_buffer_info;
|
||||
if (edram_fragment_shader_interlock) {
|
||||
edram_descriptor_buffer_info.buffer = render_target_cache_->edram_buffer();
|
||||
edram_descriptor_buffer_info.offset = 0;
|
||||
edram_descriptor_buffer_info.range = VK_WHOLE_SIZE;
|
||||
VkWriteDescriptorSet& write_descriptor_set_edram = write_descriptor_sets[1];
|
||||
write_descriptor_set_edram.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write_descriptor_set_edram.pNext = nullptr;
|
||||
write_descriptor_set_edram.dstSet = shared_memory_and_edram_descriptor_set_;
|
||||
write_descriptor_set_edram.dstBinding = 1;
|
||||
write_descriptor_set_edram.dstArrayElement = 0;
|
||||
write_descriptor_set_edram.descriptorCount = 1;
|
||||
write_descriptor_set_edram.descriptorType =
|
||||
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
write_descriptor_set_edram.pImageInfo = nullptr;
|
||||
write_descriptor_set_edram.pBufferInfo = &edram_descriptor_buffer_info;
|
||||
write_descriptor_set_edram.pTexelBufferView = nullptr;
|
||||
}
|
||||
dfn.vkUpdateDescriptorSets(device,
|
||||
1 + uint32_t(edram_fragment_shader_interlock),
|
||||
write_descriptor_sets, 0, nullptr);
|
||||
|
||||
// Swap objects.
|
||||
|
||||
|
@ -1041,6 +1111,9 @@ void VulkanCommandProcessor::ShutdownContext() {
|
|||
}
|
||||
descriptor_set_layouts_textures_.clear();
|
||||
|
||||
ui::vulkan::util::DestroyAndNullHandle(
|
||||
dfn.vkDestroyDescriptorSetLayout, device,
|
||||
descriptor_set_layout_shared_memory_and_edram_);
|
||||
for (VkDescriptorSetLayout& descriptor_set_layout_single_transient :
|
||||
descriptor_set_layouts_single_transient_) {
|
||||
ui::vulkan::util::DestroyAndNullHandle(
|
||||
|
@ -1050,9 +1123,6 @@ void VulkanCommandProcessor::ShutdownContext() {
|
|||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyDescriptorSetLayout,
|
||||
device,
|
||||
descriptor_set_layout_constants_);
|
||||
ui::vulkan::util::DestroyAndNullHandle(
|
||||
dfn.vkDestroyDescriptorSetLayout, device,
|
||||
descriptor_set_layout_shared_memory_and_edram_);
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyDescriptorSetLayout,
|
||||
device, descriptor_set_layout_empty_);
|
||||
|
||||
|
@ -2401,7 +2471,8 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
|
|||
// Update system constants before uploading them.
|
||||
UpdateSystemConstantValues(primitive_polygonal, primitive_processing_result,
|
||||
shader_32bit_index_dma, viewport_info,
|
||||
used_texture_mask);
|
||||
used_texture_mask, normalized_depth_control,
|
||||
normalized_color_mask);
|
||||
|
||||
// Update uniform buffers and descriptor sets after binding the pipeline with
|
||||
// the new layout.
|
||||
|
@ -2461,6 +2532,8 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
|
|||
// After all commands that may dispatch, copy or insert barriers, submit the
|
||||
// barriers (may end the render pass), and (re)enter the render pass before
|
||||
// drawing.
|
||||
// TODO(Triang3l): Handle disabled variableMultisampleRate by restarting the
|
||||
// render pass with no attachments if the sample count becomes different.
|
||||
SubmitBarriersAndEnterRenderTargetCacheRenderPass(
|
||||
render_target_cache_->last_update_render_pass(),
|
||||
render_target_cache_->last_update_framebuffer());
|
||||
|
@ -3180,9 +3253,9 @@ void VulkanCommandProcessor::UpdateDynamicState(
|
|||
scissor_rect.extent.height = scissor.extent[1];
|
||||
SetScissor(scissor_rect);
|
||||
|
||||
if (render_target_cache_->GetPath() ==
|
||||
RenderTargetCache::Path::kHostRenderTargets) {
|
||||
// Depth bias.
|
||||
// TODO(Triang3l): Disable the depth bias for the fragment shader interlock RB
|
||||
// implementation.
|
||||
float depth_bias_constant_factor, depth_bias_slope_factor;
|
||||
draw_util::GetPreferredFacePolygonOffset(regs, primitive_polygonal,
|
||||
depth_bias_slope_factor,
|
||||
|
@ -3193,9 +3266,9 @@ void VulkanCommandProcessor::UpdateDynamicState(
|
|||
? draw_util::kD3D10PolygonOffsetFactorUnorm24
|
||||
: draw_util::kD3D10PolygonOffsetFactorFloat24;
|
||||
// With non-square resolution scaling, make sure the worst-case impact is
|
||||
// reverted (slope only along the scaled axis), thus max. More bias is better
|
||||
// than less bias, because less bias means Z fighting with the background is
|
||||
// more likely.
|
||||
// reverted (slope only along the scaled axis), thus max. More bias is
|
||||
// better than less bias, because less bias means Z fighting with the
|
||||
// background is more likely.
|
||||
depth_bias_slope_factor *=
|
||||
xenos::kPolygonOffsetScaleSubpixelUnit *
|
||||
float(std::max(render_target_cache_->draw_resolution_scale_x(),
|
||||
|
@ -3235,12 +3308,12 @@ void VulkanCommandProcessor::UpdateDynamicState(
|
|||
|
||||
// Stencil masks and references.
|
||||
// Due to pretty complex conditions involving registers not directly related
|
||||
// to stencil (primitive type, culling), changing the values only when stencil
|
||||
// is actually needed. However, due to the way dynamic state needs to be set
|
||||
// in Vulkan, which doesn't take into account whether the state actually has
|
||||
// effect on drawing, and because the masks and the references are always
|
||||
// dynamic in Xenia guest pipelines, they must be set in the command buffer
|
||||
// before any draw.
|
||||
// to stencil (primitive type, culling), changing the values only when
|
||||
// stencil is actually needed. However, due to the way dynamic state needs
|
||||
// to be set in Vulkan, which doesn't take into account whether the state
|
||||
// actually has effect on drawing, and because the masks and the references
|
||||
// are always dynamic in Xenia guest pipelines, they must be set in the
|
||||
// command buffer before any draw.
|
||||
if (normalized_depth_control.stencil_enable) {
|
||||
Register stencil_ref_mask_front_reg, stencil_ref_mask_back_reg;
|
||||
if (primitive_polygonal && normalized_depth_control.backface_enable) {
|
||||
|
@ -3274,13 +3347,15 @@ void VulkanCommandProcessor::UpdateDynamicState(
|
|||
stencil_ref_mask_front.stencilmask;
|
||||
dynamic_stencil_compare_mask_front_ = stencil_ref_mask_front.stencilmask;
|
||||
dynamic_stencil_compare_mask_back_update_needed_ |=
|
||||
dynamic_stencil_compare_mask_back_ != stencil_ref_mask_back.stencilmask;
|
||||
dynamic_stencil_compare_mask_back_ !=
|
||||
stencil_ref_mask_back.stencilmask;
|
||||
dynamic_stencil_compare_mask_back_ = stencil_ref_mask_back.stencilmask;
|
||||
// Write mask.
|
||||
dynamic_stencil_write_mask_front_update_needed_ |=
|
||||
dynamic_stencil_write_mask_front_ !=
|
||||
stencil_ref_mask_front.stencilwritemask;
|
||||
dynamic_stencil_write_mask_front_ = stencil_ref_mask_front.stencilwritemask;
|
||||
dynamic_stencil_write_mask_front_ =
|
||||
stencil_ref_mask_front.stencilwritemask;
|
||||
dynamic_stencil_write_mask_back_update_needed_ |=
|
||||
dynamic_stencil_write_mask_back_ !=
|
||||
stencil_ref_mask_back.stencilwritemask;
|
||||
|
@ -3293,14 +3368,15 @@ void VulkanCommandProcessor::UpdateDynamicState(
|
|||
dynamic_stencil_reference_back_ != stencil_ref_mask_back.stencilref;
|
||||
dynamic_stencil_reference_back_ = stencil_ref_mask_back.stencilref;
|
||||
}
|
||||
// Using VK_STENCIL_FACE_FRONT_AND_BACK for higher safety when running on the
|
||||
// Vulkan portability subset without separateStencilMaskRef.
|
||||
// Using VK_STENCIL_FACE_FRONT_AND_BACK for higher safety when running on
|
||||
// the Vulkan portability subset without separateStencilMaskRef.
|
||||
if (dynamic_stencil_compare_mask_front_update_needed_ ||
|
||||
dynamic_stencil_compare_mask_back_update_needed_) {
|
||||
if (dynamic_stencil_compare_mask_front_ ==
|
||||
dynamic_stencil_compare_mask_back_) {
|
||||
deferred_command_buffer_.CmdVkSetStencilCompareMask(
|
||||
VK_STENCIL_FACE_FRONT_AND_BACK, dynamic_stencil_compare_mask_front_);
|
||||
VK_STENCIL_FACE_FRONT_AND_BACK,
|
||||
dynamic_stencil_compare_mask_front_);
|
||||
} else {
|
||||
if (dynamic_stencil_compare_mask_front_update_needed_) {
|
||||
deferred_command_buffer_.CmdVkSetStencilCompareMask(
|
||||
|
@ -3316,7 +3392,8 @@ void VulkanCommandProcessor::UpdateDynamicState(
|
|||
}
|
||||
if (dynamic_stencil_write_mask_front_update_needed_ ||
|
||||
dynamic_stencil_write_mask_back_update_needed_) {
|
||||
if (dynamic_stencil_write_mask_front_ == dynamic_stencil_write_mask_back_) {
|
||||
if (dynamic_stencil_write_mask_front_ ==
|
||||
dynamic_stencil_write_mask_back_) {
|
||||
deferred_command_buffer_.CmdVkSetStencilWriteMask(
|
||||
VK_STENCIL_FACE_FRONT_AND_BACK, dynamic_stencil_write_mask_front_);
|
||||
} else {
|
||||
|
@ -3350,6 +3427,7 @@ void VulkanCommandProcessor::UpdateDynamicState(
|
|||
dynamic_stencil_reference_front_update_needed_ = false;
|
||||
dynamic_stencil_reference_back_update_needed_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(Triang3l): VK_EXT_extended_dynamic_state and
|
||||
// VK_EXT_extended_dynamic_state2.
|
||||
|
@ -3359,23 +3437,67 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
|
|||
bool primitive_polygonal,
|
||||
const PrimitiveProcessor::ProcessingResult& primitive_processing_result,
|
||||
bool shader_32bit_index_dma, const draw_util::ViewportInfo& viewport_info,
|
||||
uint32_t used_texture_mask) {
|
||||
uint32_t used_texture_mask, reg::RB_DEPTHCONTROL normalized_depth_control,
|
||||
uint32_t normalized_color_mask) {
|
||||
#if XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
|
||||
|
||||
const RegisterFile& regs = *register_file_;
|
||||
auto pa_cl_vte_cntl = regs.Get<reg::PA_CL_VTE_CNTL>();
|
||||
auto pa_su_sc_mode_cntl = regs.Get<reg::PA_SU_SC_MODE_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>();
|
||||
auto rb_stencilrefmask = regs.Get<reg::RB_STENCILREFMASK>();
|
||||
auto rb_stencilrefmask_bf =
|
||||
regs.Get<reg::RB_STENCILREFMASK>(XE_GPU_REG_RB_STENCILREFMASK_BF);
|
||||
auto rb_surface_info = regs.Get<reg::RB_SURFACE_INFO>();
|
||||
auto vgt_draw_initiator = regs.Get<reg::VGT_DRAW_INITIATOR>();
|
||||
int32_t vgt_indx_offset = int32_t(regs[XE_GPU_REG_VGT_INDX_OFFSET].u32);
|
||||
|
||||
// Get the color info register values for each render target.
|
||||
bool edram_fragment_shader_interlock =
|
||||
render_target_cache_->GetPath() ==
|
||||
RenderTargetCache::Path::kPixelShaderInterlock;
|
||||
uint32_t draw_resolution_scale_x = texture_cache_->draw_resolution_scale_x();
|
||||
uint32_t draw_resolution_scale_y = texture_cache_->draw_resolution_scale_y();
|
||||
|
||||
// Get the color info register values for each render target. Also, for FSI,
|
||||
// exclude components that don't exist in the format from the write mask.
|
||||
// Don't exclude fully overlapping render targets, however - two render
|
||||
// targets with the same base address are used in the lighting pass of
|
||||
// 4D5307E6, for example, with the needed one picked with dynamic control
|
||||
// flow.
|
||||
reg::RB_COLOR_INFO color_infos[xenos::kMaxColorRenderTargets];
|
||||
float rt_clamp[4][4];
|
||||
// Two UINT32_MAX if no components actually existing in the RT are written.
|
||||
uint32_t rt_keep_masks[4][2];
|
||||
for (uint32_t i = 0; i < xenos::kMaxColorRenderTargets; ++i) {
|
||||
color_infos[i] = regs.Get<reg::RB_COLOR_INFO>(
|
||||
auto color_info = regs.Get<reg::RB_COLOR_INFO>(
|
||||
reg::RB_COLOR_INFO::rt_register_indices[i]);
|
||||
color_infos[i] = color_info;
|
||||
if (edram_fragment_shader_interlock) {
|
||||
RenderTargetCache::GetPSIColorFormatInfo(
|
||||
color_info.color_format, (normalized_color_mask >> (i * 4)) & 0b1111,
|
||||
rt_clamp[i][0], rt_clamp[i][1], rt_clamp[i][2], rt_clamp[i][3],
|
||||
rt_keep_masks[i][0], rt_keep_masks[i][1]);
|
||||
}
|
||||
}
|
||||
|
||||
// Disable depth and stencil if it aliases a color render target (for
|
||||
// instance, during the XBLA logo in 58410954, though depth writing is already
|
||||
// disabled there).
|
||||
bool depth_stencil_enabled = normalized_depth_control.stencil_enable ||
|
||||
normalized_depth_control.z_enable;
|
||||
if (edram_fragment_shader_interlock && depth_stencil_enabled) {
|
||||
for (uint32_t i = 0; i < 4; ++i) {
|
||||
if (rb_depth_info.depth_base == color_infos[i].color_base &&
|
||||
(rt_keep_masks[i][0] != UINT32_MAX ||
|
||||
rt_keep_masks[i][1] != UINT32_MAX)) {
|
||||
depth_stencil_enabled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool dirty = false;
|
||||
|
@ -3419,6 +3541,13 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
|
|||
if (draw_util::IsPrimitiveLine(regs)) {
|
||||
flags |= SpirvShaderTranslator::kSysFlag_PrimitiveLine;
|
||||
}
|
||||
// MSAA sample count.
|
||||
flags |= uint32_t(rb_surface_info.msaa_samples)
|
||||
<< SpirvShaderTranslator::kSysFlag_MsaaSamples_Shift;
|
||||
// Depth format.
|
||||
if (rb_depth_info.depth_format == xenos::DepthRenderTargetFormat::kD24FS8) {
|
||||
flags |= SpirvShaderTranslator::kSysFlag_DepthFloat24;
|
||||
}
|
||||
// Alpha test.
|
||||
xenos::CompareFunction alpha_test_function =
|
||||
rb_colorcontrol.alpha_test_enable ? rb_colorcontrol.alpha_func
|
||||
|
@ -3433,6 +3562,30 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
|
|||
flags |= SpirvShaderTranslator::kSysFlag_ConvertColor0ToGamma << i;
|
||||
}
|
||||
}
|
||||
if (edram_fragment_shader_interlock && depth_stencil_enabled) {
|
||||
flags |= SpirvShaderTranslator::kSysFlag_FSIDepthStencil;
|
||||
if (normalized_depth_control.z_enable) {
|
||||
flags |= uint32_t(normalized_depth_control.zfunc)
|
||||
<< SpirvShaderTranslator::kSysFlag_FSIDepthPassIfLess_Shift;
|
||||
if (normalized_depth_control.z_write_enable) {
|
||||
flags |= SpirvShaderTranslator::kSysFlag_FSIDepthWrite;
|
||||
}
|
||||
} else {
|
||||
// In case stencil is used without depth testing - always pass, and
|
||||
// don't modify the stored depth.
|
||||
flags |= SpirvShaderTranslator::kSysFlag_FSIDepthPassIfLess |
|
||||
SpirvShaderTranslator::kSysFlag_FSIDepthPassIfEqual |
|
||||
SpirvShaderTranslator::kSysFlag_FSIDepthPassIfGreater;
|
||||
}
|
||||
if (normalized_depth_control.stencil_enable) {
|
||||
flags |= SpirvShaderTranslator::kSysFlag_FSIStencilTest;
|
||||
}
|
||||
// Hint - if not applicable to the shader, will not have effect.
|
||||
if (alpha_test_function == xenos::CompareFunction::kAlways &&
|
||||
!rb_colorcontrol.alpha_to_mask_enable) {
|
||||
flags |= SpirvShaderTranslator::kSysFlag_FSIDepthStencilEarlyWrite;
|
||||
}
|
||||
}
|
||||
dirty |= system_constants_.flags != flags;
|
||||
system_constants_.flags = flags;
|
||||
|
||||
|
@ -3492,10 +3645,10 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
|
|||
// to radius conversion to avoid multiplying the per-vertex diameter by an
|
||||
// additional constant in the shader.
|
||||
float point_screen_diameter_to_ndc_radius_x =
|
||||
(/* 0.5f * 2.0f * */ float(texture_cache_->draw_resolution_scale_x())) /
|
||||
(/* 0.5f * 2.0f * */ float(draw_resolution_scale_x)) /
|
||||
std::max(viewport_info.xy_extent[0], uint32_t(1));
|
||||
float point_screen_diameter_to_ndc_radius_y =
|
||||
(/* 0.5f * 2.0f * */ float(texture_cache_->draw_resolution_scale_y())) /
|
||||
(/* 0.5f * 2.0f * */ float(draw_resolution_scale_y)) /
|
||||
std::max(viewport_info.xy_extent[1], uint32_t(1));
|
||||
dirty |= system_constants_.point_screen_diameter_to_ndc_radius[0] !=
|
||||
point_screen_diameter_to_ndc_radius_x;
|
||||
|
@ -3560,7 +3713,25 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
|
|||
dirty |= system_constants_.alpha_test_reference != rb_alpha_ref;
|
||||
system_constants_.alpha_test_reference = rb_alpha_ref;
|
||||
|
||||
// Color exponent bias.
|
||||
uint32_t edram_tile_dwords_scaled =
|
||||
xenos::kEdramTileWidthSamples * xenos::kEdramTileHeightSamples *
|
||||
(draw_resolution_scale_x * draw_resolution_scale_y);
|
||||
|
||||
// EDRAM pitch for FSI render target writing.
|
||||
if (edram_fragment_shader_interlock) {
|
||||
// Align, then multiply by 32bpp tile size in dwords.
|
||||
uint32_t edram_32bpp_tile_pitch_dwords_scaled =
|
||||
((rb_surface_info.surface_pitch *
|
||||
(rb_surface_info.msaa_samples >= xenos::MsaaSamples::k4X ? 2 : 1)) +
|
||||
(xenos::kEdramTileWidthSamples - 1)) /
|
||||
xenos::kEdramTileWidthSamples * edram_tile_dwords_scaled;
|
||||
dirty |= system_constants_.edram_32bpp_tile_pitch_dwords_scaled !=
|
||||
edram_32bpp_tile_pitch_dwords_scaled;
|
||||
system_constants_.edram_32bpp_tile_pitch_dwords_scaled =
|
||||
edram_32bpp_tile_pitch_dwords_scaled;
|
||||
}
|
||||
|
||||
// Color exponent bias and FSI render target writing.
|
||||
for (uint32_t i = 0; i < xenos::kMaxColorRenderTargets; ++i) {
|
||||
reg::RB_COLOR_INFO color_info = color_infos[i];
|
||||
// Exponent bias is in bits 20:25 of RB_COLOR_INFO.
|
||||
|
@ -3581,6 +3752,148 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
|
|||
UINT32_C(0x3F800000) + (color_exp_bias << 23);
|
||||
dirty |= system_constants_.color_exp_bias[i] != color_exp_bias_scale;
|
||||
system_constants_.color_exp_bias[i] = color_exp_bias_scale;
|
||||
if (edram_fragment_shader_interlock) {
|
||||
dirty |=
|
||||
system_constants_.edram_rt_keep_mask[i][0] != rt_keep_masks[i][0];
|
||||
system_constants_.edram_rt_keep_mask[i][0] = rt_keep_masks[i][0];
|
||||
dirty |=
|
||||
system_constants_.edram_rt_keep_mask[i][1] != rt_keep_masks[i][1];
|
||||
system_constants_.edram_rt_keep_mask[i][1] = rt_keep_masks[i][1];
|
||||
if (rt_keep_masks[i][0] != UINT32_MAX ||
|
||||
rt_keep_masks[i][1] != UINT32_MAX) {
|
||||
uint32_t rt_base_dwords_scaled =
|
||||
color_info.color_base * edram_tile_dwords_scaled;
|
||||
dirty |= system_constants_.edram_rt_base_dwords_scaled[i] !=
|
||||
rt_base_dwords_scaled;
|
||||
system_constants_.edram_rt_base_dwords_scaled[i] =
|
||||
rt_base_dwords_scaled;
|
||||
uint32_t format_flags =
|
||||
RenderTargetCache::AddPSIColorFormatFlags(color_info.color_format);
|
||||
dirty |= system_constants_.edram_rt_format_flags[i] != format_flags;
|
||||
system_constants_.edram_rt_format_flags[i] = format_flags;
|
||||
uint32_t blend_factors_ops =
|
||||
regs[reg::RB_BLENDCONTROL::rt_register_indices[i]].u32 & 0x1FFF1FFF;
|
||||
dirty |= system_constants_.edram_rt_blend_factors_ops[i] !=
|
||||
blend_factors_ops;
|
||||
system_constants_.edram_rt_blend_factors_ops[i] = blend_factors_ops;
|
||||
// Can't do float comparisons here because NaNs would result in always
|
||||
// setting the dirty flag.
|
||||
dirty |= std::memcmp(system_constants_.edram_rt_clamp[i], rt_clamp[i],
|
||||
4 * sizeof(float)) != 0;
|
||||
std::memcpy(system_constants_.edram_rt_clamp[i], rt_clamp[i],
|
||||
4 * sizeof(float));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (edram_fragment_shader_interlock) {
|
||||
uint32_t depth_base_dwords_scaled =
|
||||
rb_depth_info.depth_base * edram_tile_dwords_scaled;
|
||||
dirty |= system_constants_.edram_depth_base_dwords_scaled !=
|
||||
depth_base_dwords_scaled;
|
||||
system_constants_.edram_depth_base_dwords_scaled = depth_base_dwords_scaled;
|
||||
|
||||
// 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
|
||||
// are used.
|
||||
float poly_offset_front_scale = 0.0f, poly_offset_front_offset = 0.0f;
|
||||
float poly_offset_back_scale = 0.0f, poly_offset_back_offset = 0.0f;
|
||||
if (primitive_polygonal) {
|
||||
if (pa_su_sc_mode_cntl.poly_offset_front_enable) {
|
||||
poly_offset_front_scale =
|
||||
regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_SCALE].f32;
|
||||
poly_offset_front_offset =
|
||||
regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_OFFSET].f32;
|
||||
}
|
||||
if (pa_su_sc_mode_cntl.poly_offset_back_enable) {
|
||||
poly_offset_back_scale =
|
||||
regs[XE_GPU_REG_PA_SU_POLY_OFFSET_BACK_SCALE].f32;
|
||||
poly_offset_back_offset =
|
||||
regs[XE_GPU_REG_PA_SU_POLY_OFFSET_BACK_OFFSET].f32;
|
||||
}
|
||||
} else {
|
||||
if (pa_su_sc_mode_cntl.poly_offset_para_enable) {
|
||||
poly_offset_front_scale =
|
||||
regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_SCALE].f32;
|
||||
poly_offset_front_offset =
|
||||
regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_OFFSET].f32;
|
||||
poly_offset_back_scale = poly_offset_front_scale;
|
||||
poly_offset_back_offset = poly_offset_front_offset;
|
||||
}
|
||||
}
|
||||
// With non-square resolution scaling, make sure the worst-case impact is
|
||||
// reverted (slope only along the scaled axis), thus max. More bias is
|
||||
// better than less bias, because less bias means Z fighting with the
|
||||
// background is more likely.
|
||||
float poly_offset_scale_factor =
|
||||
xenos::kPolygonOffsetScaleSubpixelUnit *
|
||||
std::max(draw_resolution_scale_x, draw_resolution_scale_y);
|
||||
poly_offset_front_scale *= poly_offset_scale_factor;
|
||||
poly_offset_back_scale *= poly_offset_scale_factor;
|
||||
dirty |= system_constants_.edram_poly_offset_front_scale !=
|
||||
poly_offset_front_scale;
|
||||
system_constants_.edram_poly_offset_front_scale = poly_offset_front_scale;
|
||||
dirty |= system_constants_.edram_poly_offset_front_offset !=
|
||||
poly_offset_front_offset;
|
||||
system_constants_.edram_poly_offset_front_offset = poly_offset_front_offset;
|
||||
dirty |= system_constants_.edram_poly_offset_back_scale !=
|
||||
poly_offset_back_scale;
|
||||
system_constants_.edram_poly_offset_back_scale = poly_offset_back_scale;
|
||||
dirty |= system_constants_.edram_poly_offset_back_offset !=
|
||||
poly_offset_back_offset;
|
||||
system_constants_.edram_poly_offset_back_offset = poly_offset_back_offset;
|
||||
|
||||
if (depth_stencil_enabled && normalized_depth_control.stencil_enable) {
|
||||
uint32_t stencil_front_reference_masks =
|
||||
rb_stencilrefmask.value & 0xFFFFFF;
|
||||
dirty |= system_constants_.edram_stencil_front_reference_masks !=
|
||||
stencil_front_reference_masks;
|
||||
system_constants_.edram_stencil_front_reference_masks =
|
||||
stencil_front_reference_masks;
|
||||
uint32_t stencil_func_ops =
|
||||
(normalized_depth_control.value >> 8) & ((1 << 12) - 1);
|
||||
dirty |=
|
||||
system_constants_.edram_stencil_front_func_ops != stencil_func_ops;
|
||||
system_constants_.edram_stencil_front_func_ops = stencil_func_ops;
|
||||
|
||||
if (primitive_polygonal && normalized_depth_control.backface_enable) {
|
||||
uint32_t stencil_back_reference_masks =
|
||||
rb_stencilrefmask_bf.value & 0xFFFFFF;
|
||||
dirty |= system_constants_.edram_stencil_back_reference_masks !=
|
||||
stencil_back_reference_masks;
|
||||
system_constants_.edram_stencil_back_reference_masks =
|
||||
stencil_back_reference_masks;
|
||||
uint32_t stencil_func_ops_bf =
|
||||
(normalized_depth_control.value >> 20) & ((1 << 12) - 1);
|
||||
dirty |= system_constants_.edram_stencil_back_func_ops !=
|
||||
stencil_func_ops_bf;
|
||||
system_constants_.edram_stencil_back_func_ops = stencil_func_ops_bf;
|
||||
} else {
|
||||
dirty |= std::memcmp(system_constants_.edram_stencil_back,
|
||||
system_constants_.edram_stencil_front,
|
||||
2 * sizeof(uint32_t)) != 0;
|
||||
std::memcpy(system_constants_.edram_stencil_back,
|
||||
system_constants_.edram_stencil_front,
|
||||
2 * sizeof(uint32_t));
|
||||
}
|
||||
}
|
||||
|
||||
dirty |= system_constants_.edram_blend_constant[0] !=
|
||||
regs[XE_GPU_REG_RB_BLEND_RED].f32;
|
||||
system_constants_.edram_blend_constant[0] =
|
||||
regs[XE_GPU_REG_RB_BLEND_RED].f32;
|
||||
dirty |= system_constants_.edram_blend_constant[1] !=
|
||||
regs[XE_GPU_REG_RB_BLEND_GREEN].f32;
|
||||
system_constants_.edram_blend_constant[1] =
|
||||
regs[XE_GPU_REG_RB_BLEND_GREEN].f32;
|
||||
dirty |= system_constants_.edram_blend_constant[2] !=
|
||||
regs[XE_GPU_REG_RB_BLEND_BLUE].f32;
|
||||
system_constants_.edram_blend_constant[2] =
|
||||
regs[XE_GPU_REG_RB_BLEND_BLUE].f32;
|
||||
dirty |= system_constants_.edram_blend_constant[3] !=
|
||||
regs[XE_GPU_REG_RB_BLEND_ALPHA].f32;
|
||||
system_constants_.edram_blend_constant[3] =
|
||||
regs[XE_GPU_REG_RB_BLEND_ALPHA].f32;
|
||||
}
|
||||
|
||||
if (dirty) {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <deque>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
@ -256,6 +257,9 @@ class VulkanCommandProcessor : public CommandProcessor {
|
|||
void SetViewport(const VkViewport& viewport);
|
||||
void SetScissor(const VkRect2D& scissor);
|
||||
|
||||
// Returns the text to display in the GPU backend name in the window title.
|
||||
std::string GetWindowTitleText() const;
|
||||
|
||||
protected:
|
||||
bool SetupContext() override;
|
||||
void ShutdownContext() override;
|
||||
|
@ -437,7 +441,8 @@ class VulkanCommandProcessor : public CommandProcessor {
|
|||
bool primitive_polygonal,
|
||||
const PrimitiveProcessor::ProcessingResult& primitive_processing_result,
|
||||
bool shader_32bit_index_dma, const draw_util::ViewportInfo& viewport_info,
|
||||
uint32_t used_texture_mask);
|
||||
uint32_t used_texture_mask, reg::RB_DEPTHCONTROL normalized_depth_control,
|
||||
uint32_t normalized_color_mask);
|
||||
bool UpdateBindings(const VulkanShader* vertex_shader,
|
||||
const VulkanShader* pixel_shader);
|
||||
// Allocates a descriptor set and fills one or two VkWriteDescriptorSet
|
||||
|
@ -514,12 +519,12 @@ class VulkanCommandProcessor : public CommandProcessor {
|
|||
|
||||
// Descriptor set layouts used by different shaders.
|
||||
VkDescriptorSetLayout descriptor_set_layout_empty_ = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout descriptor_set_layout_shared_memory_and_edram_ =
|
||||
VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout descriptor_set_layout_constants_ = VK_NULL_HANDLE;
|
||||
std::array<VkDescriptorSetLayout,
|
||||
size_t(SingleTransientDescriptorLayout::kCount)>
|
||||
descriptor_set_layouts_single_transient_{};
|
||||
VkDescriptorSetLayout descriptor_set_layout_shared_memory_and_edram_ =
|
||||
VK_NULL_HANDLE;
|
||||
|
||||
// Descriptor set layouts are referenced by pipeline_layouts_.
|
||||
std::unordered_map<TextureDescriptorSetLayoutKey, VkDescriptorSetLayout,
|
||||
|
@ -649,6 +654,9 @@ class VulkanCommandProcessor : public CommandProcessor {
|
|||
// declared as dynamic in the pipeline) invalidates such dynamic state.
|
||||
VkViewport dynamic_viewport_;
|
||||
VkRect2D dynamic_scissor_;
|
||||
// Dynamic fixed-function depth bias, blend constants, stencil state are
|
||||
// applicable only to the render target implementations where they are
|
||||
// actually involved.
|
||||
float dynamic_depth_bias_constant_factor_;
|
||||
float dynamic_depth_bias_slope_factor_;
|
||||
float dynamic_blend_constants_[4];
|
||||
|
|
|
@ -21,6 +21,15 @@ VulkanGraphicsSystem::VulkanGraphicsSystem() {}
|
|||
|
||||
VulkanGraphicsSystem::~VulkanGraphicsSystem() {}
|
||||
|
||||
std::string VulkanGraphicsSystem::name() const {
|
||||
auto vulkan_command_processor =
|
||||
static_cast<VulkanCommandProcessor*>(command_processor());
|
||||
if (vulkan_command_processor != nullptr) {
|
||||
return vulkan_command_processor->GetWindowTitleText();
|
||||
}
|
||||
return "Vulkan - HEAVILY INCOMPLETE, early development";
|
||||
}
|
||||
|
||||
X_STATUS VulkanGraphicsSystem::Setup(cpu::Processor* processor,
|
||||
kernel::KernelState* kernel_state,
|
||||
ui::WindowedAppContext* app_context,
|
||||
|
|
|
@ -26,9 +26,7 @@ class VulkanGraphicsSystem : public GraphicsSystem {
|
|||
|
||||
static bool IsAvailable() { return true; }
|
||||
|
||||
std::string name() const override {
|
||||
return "Vulkan - HEAVILY INCOMPLETE, early development";
|
||||
}
|
||||
std::string name() const override;
|
||||
|
||||
X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state,
|
||||
ui::WindowedAppContext* app_context,
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "third_party/fmt/include/fmt/format.h"
|
||||
#include "third_party/glslang/SPIRV/SpvBuilder.h"
|
||||
|
@ -53,8 +54,32 @@ bool VulkanPipelineCache::Initialize() {
|
|||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
|
||||
bool edram_fragment_shader_interlock =
|
||||
render_target_cache_.GetPath() ==
|
||||
RenderTargetCache::Path::kPixelShaderInterlock;
|
||||
|
||||
shader_translator_ = std::make_unique<SpirvShaderTranslator>(
|
||||
SpirvShaderTranslator::Features(provider));
|
||||
SpirvShaderTranslator::Features(provider),
|
||||
render_target_cache_.msaa_2x_attachments_supported(),
|
||||
render_target_cache_.msaa_2x_no_attachments_supported(),
|
||||
edram_fragment_shader_interlock);
|
||||
|
||||
if (edram_fragment_shader_interlock) {
|
||||
std::vector<uint8_t> depth_only_fragment_shader_code =
|
||||
shader_translator_->CreateDepthOnlyFragmentShader();
|
||||
depth_only_fragment_shader_ = ui::vulkan::util::CreateShaderModule(
|
||||
provider,
|
||||
reinterpret_cast<const uint32_t*>(
|
||||
depth_only_fragment_shader_code.data()),
|
||||
depth_only_fragment_shader_code.size());
|
||||
if (depth_only_fragment_shader_ == VK_NULL_HANDLE) {
|
||||
XELOGE(
|
||||
"VulkanPipelineCache: Failed to create the depth/stencil-only "
|
||||
"fragment shader for the fragment shader interlock render backend "
|
||||
"implementation");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -75,6 +100,8 @@ void VulkanPipelineCache::Shutdown() {
|
|||
pipelines_.clear();
|
||||
|
||||
// Destroy all internal shaders.
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyShaderModule, device,
|
||||
depth_only_fragment_shader_);
|
||||
for (const auto& geometry_shader_pair : geometry_shaders_) {
|
||||
if (geometry_shader_pair.second != VK_NULL_HANDLE) {
|
||||
dfn.vkDestroyShaderModule(device, geometry_shader_pair.second, nullptr);
|
||||
|
@ -179,6 +206,8 @@ VulkanPipelineCache::GetCurrentPixelShaderModification(
|
|||
modification.pixel.param_gen_point = 0;
|
||||
}
|
||||
|
||||
if (render_target_cache_.GetPath() ==
|
||||
RenderTargetCache::Path::kHostRenderTargets) {
|
||||
using DepthStencilMode =
|
||||
SpirvShaderTranslator::Modification::DepthStencilMode;
|
||||
if (shader.implicit_early_z_write_allowed() &&
|
||||
|
@ -189,6 +218,7 @@ VulkanPipelineCache::GetCurrentPixelShaderModification(
|
|||
} else {
|
||||
modification.pixel.depth_stencil_mode = DepthStencilMode::kNoModifiers;
|
||||
}
|
||||
}
|
||||
|
||||
return modification;
|
||||
}
|
||||
|
@ -303,7 +333,11 @@ bool VulkanPipelineCache::ConfigurePipeline(
|
|||
}
|
||||
}
|
||||
VkRenderPass render_pass =
|
||||
render_target_cache_.GetRenderPass(render_pass_key);
|
||||
render_target_cache_.GetPath() ==
|
||||
RenderTargetCache::Path::kPixelShaderInterlock
|
||||
? render_target_cache_.GetFragmentShaderInterlockRenderPass()
|
||||
: render_target_cache_.GetHostRenderTargetsRenderPass(
|
||||
render_pass_key);
|
||||
if (render_pass == VK_NULL_HANDLE) {
|
||||
return false;
|
||||
}
|
||||
|
@ -603,9 +637,8 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
|
|||
description_out.polygon_mode = PipelinePolygonMode::kFill;
|
||||
}
|
||||
|
||||
// TODO(Triang3l): Skip depth / stencil and color state for the fragment
|
||||
// shader interlock RB implementation.
|
||||
|
||||
if (render_target_cache_.GetPath() ==
|
||||
RenderTargetCache::Path::kHostRenderTargets) {
|
||||
if (render_pass_key.depth_and_color_used & 1) {
|
||||
if (normalized_depth_control.z_enable) {
|
||||
description_out.depth_write_enable =
|
||||
|
@ -646,8 +679,8 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
|
|||
}
|
||||
}
|
||||
|
||||
// Color blending and write masks (filled only for the attachments present in
|
||||
// the render pass object).
|
||||
// Color blending and write masks (filled only for the attachments present
|
||||
// in the render pass object).
|
||||
uint32_t render_pass_color_rts = render_pass_key.depth_and_color_used >> 1;
|
||||
if (device_features.independentBlend) {
|
||||
uint32_t render_pass_color_rts_remaining = render_pass_color_rts;
|
||||
|
@ -665,8 +698,10 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
|
|||
// Take the blend control for the first render target that the guest wants
|
||||
// to write to (consider it the most important) and use it for all render
|
||||
// targets, if any.
|
||||
// TODO(Triang3l): Implement an option for independent blending via multiple
|
||||
// draw calls with different pipelines maybe? Though independent blending
|
||||
// TODO(Triang3l): Implement an option for independent blending via
|
||||
// replaying the render pass for each set of render targets with unique
|
||||
// blending parameters, with depth / stencil saved before the first and
|
||||
// restored before each of the rest maybe? Though independent blending
|
||||
// support is pretty wide, with a quite prominent exception of Adreno 4xx
|
||||
// apparently.
|
||||
uint32_t render_pass_color_rts_remaining = render_pass_color_rts;
|
||||
|
@ -678,15 +713,16 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
|
|||
PipelineRenderTarget& render_pass_first_color_rt =
|
||||
description_out.render_targets[render_pass_first_color_rt_index];
|
||||
uint32_t common_blend_rt_index;
|
||||
if (xe::bit_scan_forward(normalized_color_mask, &common_blend_rt_index)) {
|
||||
if (xe::bit_scan_forward(normalized_color_mask,
|
||||
&common_blend_rt_index)) {
|
||||
common_blend_rt_index >>= 2;
|
||||
// If a common write mask will be used for multiple render targets, use
|
||||
// the original RB_COLOR_MASK instead of the normalized color mask as
|
||||
// the normalized color mask has non-existent components forced to
|
||||
// If a common write mask will be used for multiple render targets,
|
||||
// use the original RB_COLOR_MASK instead of the normalized color mask
|
||||
// as the normalized color mask has non-existent components forced to
|
||||
// written (don't need reading to be preserved), while the number of
|
||||
// components may vary between render targets. The attachments in the
|
||||
// pass that must not be written to at all will be excluded via a shader
|
||||
// modification.
|
||||
// pass that must not be written to at all will be excluded via a
|
||||
// shader modification.
|
||||
WritePipelineRenderTargetDescription(
|
||||
regs.Get<reg::RB_BLENDCONTROL>(
|
||||
reg::RB_BLENDCONTROL::rt_register_indices
|
||||
|
@ -700,7 +736,8 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
|
|||
render_pass_first_color_rt);
|
||||
} else {
|
||||
// No render targets are written to, though the render pass still may
|
||||
// contain color attachments - set them to not written and not blending.
|
||||
// contain color attachments - set them to not written and not
|
||||
// blending.
|
||||
render_pass_first_color_rt.src_color_blend_factor =
|
||||
PipelineBlendFactor::kOne;
|
||||
render_pass_first_color_rt.dst_color_blend_factor =
|
||||
|
@ -723,6 +760,7 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1929,6 +1967,10 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
|
|||
command_processor_.GetVulkanProvider();
|
||||
const VkPhysicalDeviceFeatures& device_features = provider.device_features();
|
||||
|
||||
bool edram_fragment_shader_interlock =
|
||||
render_target_cache_.GetPath() ==
|
||||
RenderTargetCache::Path::kPixelShaderInterlock;
|
||||
|
||||
std::array<VkPipelineShaderStageCreateInfo, 3> shader_stages;
|
||||
uint32_t shader_stage_count = 0;
|
||||
|
||||
|
@ -1962,12 +2004,7 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
|
|||
shader_stage_geometry.pName = "main";
|
||||
shader_stage_geometry.pSpecializationInfo = nullptr;
|
||||
}
|
||||
// Pixel shader.
|
||||
if (creation_arguments.pixel_shader) {
|
||||
assert_true(creation_arguments.pixel_shader->is_translated());
|
||||
if (!creation_arguments.pixel_shader->is_valid()) {
|
||||
return false;
|
||||
}
|
||||
// Fragment shader.
|
||||
VkPipelineShaderStageCreateInfo& shader_stage_fragment =
|
||||
shader_stages[shader_stage_count++];
|
||||
shader_stage_fragment.sType =
|
||||
|
@ -1975,11 +2012,24 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
|
|||
shader_stage_fragment.pNext = nullptr;
|
||||
shader_stage_fragment.flags = 0;
|
||||
shader_stage_fragment.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
shader_stage_fragment.module = VK_NULL_HANDLE;
|
||||
shader_stage_fragment.pName = "main";
|
||||
shader_stage_fragment.pSpecializationInfo = nullptr;
|
||||
if (creation_arguments.pixel_shader) {
|
||||
assert_true(creation_arguments.pixel_shader->is_translated());
|
||||
if (!creation_arguments.pixel_shader->is_valid()) {
|
||||
return false;
|
||||
}
|
||||
shader_stage_fragment.module =
|
||||
creation_arguments.pixel_shader->shader_module();
|
||||
assert_true(shader_stage_fragment.module != VK_NULL_HANDLE);
|
||||
shader_stage_fragment.pName = "main";
|
||||
shader_stage_fragment.pSpecializationInfo = nullptr;
|
||||
} else {
|
||||
if (edram_fragment_shader_interlock) {
|
||||
shader_stage_fragment.module = depth_only_fragment_shader_;
|
||||
}
|
||||
}
|
||||
if (shader_stage_fragment.module == VK_NULL_HANDLE) {
|
||||
--shader_stage_count;
|
||||
}
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vertex_input_state = {};
|
||||
|
@ -2087,10 +2137,10 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
|
|||
// formula, though Z has no effect on anything if a depth attachment is not
|
||||
// used (the guest shader can't access Z), enabling only when there's a
|
||||
// depth / stencil attachment for correctness.
|
||||
// TODO(Triang3l): Disable the depth bias for the fragment shader interlock RB
|
||||
// implementation.
|
||||
rasterization_state.depthBiasEnable =
|
||||
(description.render_pass_key.depth_and_color_used & 0b1) ? VK_TRUE
|
||||
(!edram_fragment_shader_interlock &&
|
||||
(description.render_pass_key.depth_and_color_used & 0b1))
|
||||
? VK_TRUE
|
||||
: VK_FALSE;
|
||||
// TODO(Triang3l): Wide lines.
|
||||
rasterization_state.lineWidth = 1.0f;
|
||||
|
@ -2101,6 +2151,7 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
|
|||
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||
if (description.render_pass_key.msaa_samples == xenos::MsaaSamples::k2X &&
|
||||
!render_target_cache_.IsMsaa2xSupported(
|
||||
!edram_fragment_shader_interlock &&
|
||||
description.render_pass_key.depth_and_color_used != 0)) {
|
||||
// Using sample 0 as 0 and 3 as 1 for 2x instead (not exactly the same
|
||||
// sample locations, but still top-left and bottom-right - however, this can
|
||||
|
@ -2119,13 +2170,15 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
|
|||
depth_stencil_state.sType =
|
||||
VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
|
||||
depth_stencil_state.pNext = nullptr;
|
||||
if (!edram_fragment_shader_interlock) {
|
||||
if (description.depth_write_enable ||
|
||||
description.depth_compare_op != xenos::CompareFunction::kAlways) {
|
||||
depth_stencil_state.depthTestEnable = VK_TRUE;
|
||||
depth_stencil_state.depthWriteEnable =
|
||||
description.depth_write_enable ? VK_TRUE : VK_FALSE;
|
||||
depth_stencil_state.depthCompareOp = VkCompareOp(
|
||||
uint32_t(VK_COMPARE_OP_NEVER) + uint32_t(description.depth_compare_op));
|
||||
depth_stencil_state.depthCompareOp =
|
||||
VkCompareOp(uint32_t(VK_COMPARE_OP_NEVER) +
|
||||
uint32_t(description.depth_compare_op));
|
||||
}
|
||||
if (description.stencil_test_enable) {
|
||||
depth_stencil_state.stencilTestEnable = VK_TRUE;
|
||||
|
@ -2154,9 +2207,14 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
|
|||
VkCompareOp(uint32_t(VK_COMPARE_OP_NEVER) +
|
||||
uint32_t(description.stencil_back_compare_op));
|
||||
}
|
||||
}
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo color_blend_state = {};
|
||||
color_blend_state.sType =
|
||||
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||
VkPipelineColorBlendAttachmentState
|
||||
color_blend_attachments[xenos::kMaxColorRenderTargets] = {};
|
||||
if (!edram_fragment_shader_interlock) {
|
||||
uint32_t color_rts_used =
|
||||
description.render_pass_key.depth_and_color_used >> 1;
|
||||
{
|
||||
|
@ -2223,9 +2281,6 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
|
|||
}
|
||||
}
|
||||
}
|
||||
VkPipelineColorBlendStateCreateInfo color_blend_state = {};
|
||||
color_blend_state.sType =
|
||||
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||
color_blend_state.attachmentCount = 32 - xe::lzcnt(color_rts_used);
|
||||
color_blend_state.pAttachments = color_blend_attachments;
|
||||
if (color_rts_used && !device_features.independentBlend) {
|
||||
|
@ -2241,6 +2296,7 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
|
|||
color_blend_attachments[first_color_rt_index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::array<VkDynamicState, 7> dynamic_states;
|
||||
VkPipelineDynamicStateCreateInfo dynamic_state;
|
||||
|
@ -2255,6 +2311,7 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
|
|||
// invalidated (again, even if it has no effect).
|
||||
dynamic_states[dynamic_state.dynamicStateCount++] = VK_DYNAMIC_STATE_VIEWPORT;
|
||||
dynamic_states[dynamic_state.dynamicStateCount++] = VK_DYNAMIC_STATE_SCISSOR;
|
||||
if (!edram_fragment_shader_interlock) {
|
||||
dynamic_states[dynamic_state.dynamicStateCount++] =
|
||||
VK_DYNAMIC_STATE_DEPTH_BIAS;
|
||||
dynamic_states[dynamic_state.dynamicStateCount++] =
|
||||
|
@ -2265,6 +2322,7 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
|
|||
VK_DYNAMIC_STATE_STENCIL_WRITE_MASK;
|
||||
dynamic_states[dynamic_state.dynamicStateCount++] =
|
||||
VK_DYNAMIC_STATE_STENCIL_REFERENCE;
|
||||
}
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipeline_create_info;
|
||||
pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
|
|
|
@ -314,6 +314,10 @@ class VulkanPipelineCache {
|
|||
GeometryShaderKey::Hasher>
|
||||
geometry_shaders_;
|
||||
|
||||
// Empty depth-only pixel shader for writing to depth buffer using fragment
|
||||
// shader interlock when no Xenos pixel shader provided.
|
||||
VkShaderModule depth_only_fragment_shader_ = VK_NULL_HANDLE;
|
||||
|
||||
std::unordered_map<PipelineDescription, Pipeline, PipelineDescription::Hasher>
|
||||
pipelines_;
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "third_party/glslang/SPIRV/GLSL.std.450.h"
|
||||
#include "third_party/glslang/SPIRV/SpvBuilder.h"
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/cvar.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/gpu/draw_util.h"
|
||||
|
@ -33,6 +34,27 @@
|
|||
#include "xenia/gpu/xenos.h"
|
||||
#include "xenia/ui/vulkan/vulkan_util.h"
|
||||
|
||||
DEFINE_string(
|
||||
render_target_path_vulkan, "",
|
||||
"Render target emulation path to use on Vulkan.\n"
|
||||
"Use: [any, fbo, fsi]\n"
|
||||
" fbo:\n"
|
||||
" Host framebuffers and fixed-function blending and depth / stencil "
|
||||
"testing, copying between render targets when needed.\n"
|
||||
" Lower accuracy (limited pixel format support).\n"
|
||||
" Performance limited primarily by render target layout changes requiring "
|
||||
"copying, but generally higher.\n"
|
||||
" fsi:\n"
|
||||
" Manual pixel packing, blending and depth / stencil testing, with free "
|
||||
"render target layout changes.\n"
|
||||
" Requires a GPU supporting fragment shader interlock.\n"
|
||||
" Highest accuracy (all pixel formats handled in software).\n"
|
||||
" Performance limited primarily by overdraw.\n"
|
||||
" Any other value:\n"
|
||||
" Choose what is considered the most optimal for the system (currently "
|
||||
"always FB because the FSI path is much slower now).",
|
||||
"GPU");
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
namespace vulkan {
|
||||
|
@ -43,6 +65,10 @@ namespace shaders {
|
|||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/host_depth_store_2xmsaa_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/host_depth_store_4xmsaa_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/passthrough_position_xy_vs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/resolve_clear_32bpp_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/resolve_clear_32bpp_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/resolve_clear_64bpp_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/resolve_clear_64bpp_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/resolve_fast_32bpp_1x2xmsaa_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/resolve_fast_32bpp_1x2xmsaa_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/resolve_fast_32bpp_4xmsaa_cs.h"
|
||||
|
@ -180,13 +206,61 @@ VulkanRenderTargetCache::VulkanRenderTargetCache(
|
|||
|
||||
VulkanRenderTargetCache::~VulkanRenderTargetCache() { Shutdown(true); }
|
||||
|
||||
bool VulkanRenderTargetCache::Initialize() {
|
||||
bool VulkanRenderTargetCache::Initialize(uint32_t shared_memory_binding_count) {
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::InstanceFunctions& ifn = provider.ifn();
|
||||
VkPhysicalDevice physical_device = provider.physical_device();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const VkPhysicalDeviceLimits& device_limits =
|
||||
provider.device_properties().limits;
|
||||
|
||||
if (cvars::render_target_path_vulkan == "fsi") {
|
||||
path_ = Path::kPixelShaderInterlock;
|
||||
} else {
|
||||
path_ = Path::kHostRenderTargets;
|
||||
}
|
||||
// Fragment shader interlock is a feature implemented by pretty advanced GPUs,
|
||||
// closer to Direct3D 11 / OpenGL ES 3.2 level mainly, not Direct3D 10 /
|
||||
// OpenGL ES 3.1. Thus, it's fine to demand a wide range of other optional
|
||||
// features for the fragment shader interlock backend to work.
|
||||
if (path_ == Path::kPixelShaderInterlock) {
|
||||
const VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT&
|
||||
device_fragment_shader_interlock_features =
|
||||
provider.device_fragment_shader_interlock_features();
|
||||
const VkPhysicalDeviceFeatures& device_features =
|
||||
provider.device_features();
|
||||
// Interlocking between fragments with common sample coverage is enough, but
|
||||
// interlocking more is acceptable too (fragmentShaderShadingRateInterlock
|
||||
// would be okay too, but it's unlikely that an implementation would
|
||||
// advertise only it and not any other ones, as it's a very specific feature
|
||||
// interacting with another optional feature that is variable shading rate,
|
||||
// so there's no need to overcomplicate the checks and the shader execution
|
||||
// mode setting).
|
||||
// Sample-rate shading is required by certain SPIR-V revisions to access the
|
||||
// sample mask fragment shader input.
|
||||
// Stanard sample locations are needed for calculating the depth at the
|
||||
// samples.
|
||||
// It's unlikely that a device exposing fragment shader interlock won't have
|
||||
// a large enough storage buffer range and a sufficient SSBO slot count for
|
||||
// all the shared memory buffers and the EDRAM buffer - an in a conflict
|
||||
// between, for instance, the ability to vfetch and memexport in fragment
|
||||
// shaders, and the usage of fragment shader interlock, prefer the former
|
||||
// for simplicity.
|
||||
if (!provider.device_extensions().ext_fragment_shader_interlock ||
|
||||
!(device_fragment_shader_interlock_features
|
||||
.fragmentShaderSampleInterlock ||
|
||||
device_fragment_shader_interlock_features
|
||||
.fragmentShaderPixelInterlock) ||
|
||||
!device_features.fragmentStoresAndAtomics ||
|
||||
!device_features.sampleRateShading ||
|
||||
!device_limits.standardSampleLocations ||
|
||||
shared_memory_binding_count >=
|
||||
device_limits.maxDescriptorSetStorageBuffers) {
|
||||
path_ = Path::kHostRenderTargets;
|
||||
}
|
||||
}
|
||||
|
||||
// Format support.
|
||||
constexpr VkFormatFeatureFlags kUsedDepthFormatFeatures =
|
||||
|
@ -199,6 +273,30 @@ bool VulkanRenderTargetCache::Initialize() {
|
|||
(depth_unorm24_properties.optimalTilingFeatures &
|
||||
kUsedDepthFormatFeatures) == kUsedDepthFormatFeatures;
|
||||
|
||||
// 2x MSAA support.
|
||||
// TODO(Triang3l): Handle sampledImageIntegerSampleCounts 4 not supported in
|
||||
// transfers.
|
||||
if (cvars::native_2x_msaa) {
|
||||
// Multisampled integer sampled images are optional in Vulkan and in Xenia.
|
||||
msaa_2x_attachments_supported_ =
|
||||
(device_limits.framebufferColorSampleCounts &
|
||||
device_limits.framebufferDepthSampleCounts &
|
||||
device_limits.framebufferStencilSampleCounts &
|
||||
device_limits.sampledImageColorSampleCounts &
|
||||
device_limits.sampledImageDepthSampleCounts &
|
||||
device_limits.sampledImageStencilSampleCounts &
|
||||
VK_SAMPLE_COUNT_2_BIT) &&
|
||||
(device_limits.sampledImageIntegerSampleCounts &
|
||||
(VK_SAMPLE_COUNT_2_BIT | VK_SAMPLE_COUNT_4_BIT)) !=
|
||||
VK_SAMPLE_COUNT_4_BIT;
|
||||
msaa_2x_no_attachments_supported_ =
|
||||
(device_limits.framebufferNoAttachmentsSampleCounts &
|
||||
VK_SAMPLE_COUNT_2_BIT) != 0;
|
||||
} else {
|
||||
msaa_2x_attachments_supported_ = false;
|
||||
msaa_2x_no_attachments_supported_ = false;
|
||||
}
|
||||
|
||||
// Descriptor set layouts.
|
||||
VkDescriptorSetLayoutBinding descriptor_set_layout_bindings[2];
|
||||
descriptor_set_layout_bindings[0].binding = 0;
|
||||
|
@ -429,32 +527,10 @@ bool VulkanRenderTargetCache::Initialize() {
|
|||
|
||||
// TODO(Triang3l): All paths (FSI).
|
||||
|
||||
depth_float24_round_ = cvars::depth_float24_round;
|
||||
if (path_ == Path::kHostRenderTargets) {
|
||||
// Host render targets.
|
||||
|
||||
// TODO(Triang3l): Handle sampledImageIntegerSampleCounts 4 not supported in
|
||||
// transfers.
|
||||
if (cvars::native_2x_msaa) {
|
||||
const VkPhysicalDeviceLimits& device_limits =
|
||||
provider.device_properties().limits;
|
||||
// Multisampled integer sampled images are optional in Vulkan and in Xenia.
|
||||
msaa_2x_attachments_supported_ =
|
||||
(device_limits.framebufferColorSampleCounts &
|
||||
device_limits.framebufferDepthSampleCounts &
|
||||
device_limits.framebufferStencilSampleCounts &
|
||||
device_limits.sampledImageColorSampleCounts &
|
||||
device_limits.sampledImageDepthSampleCounts &
|
||||
device_limits.sampledImageStencilSampleCounts &
|
||||
VK_SAMPLE_COUNT_2_BIT) &&
|
||||
(device_limits.sampledImageIntegerSampleCounts &
|
||||
(VK_SAMPLE_COUNT_2_BIT | VK_SAMPLE_COUNT_4_BIT)) !=
|
||||
VK_SAMPLE_COUNT_4_BIT;
|
||||
msaa_2x_no_attachments_supported_ =
|
||||
(device_limits.framebufferNoAttachmentsSampleCounts &
|
||||
VK_SAMPLE_COUNT_2_BIT) != 0;
|
||||
} else {
|
||||
msaa_2x_attachments_supported_ = false;
|
||||
msaa_2x_no_attachments_supported_ = false;
|
||||
}
|
||||
depth_float24_round_ = cvars::depth_float24_round;
|
||||
|
||||
// Host depth storing pipeline layout.
|
||||
VkDescriptorSetLayout host_depth_store_descriptor_set_layouts[] = {
|
||||
|
@ -464,7 +540,8 @@ bool VulkanRenderTargetCache::Initialize() {
|
|||
descriptor_set_layout_sampled_image_x2_,
|
||||
};
|
||||
VkPushConstantRange host_depth_store_push_constant_range;
|
||||
host_depth_store_push_constant_range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
||||
host_depth_store_push_constant_range.stageFlags =
|
||||
VK_SHADER_STAGE_COMPUTE_BIT;
|
||||
host_depth_store_push_constant_range.offset = 0;
|
||||
host_depth_store_push_constant_range.size = sizeof(HostDepthStoreConstants);
|
||||
VkPipelineLayoutCreateInfo host_depth_store_pipeline_layout_create_info;
|
||||
|
@ -505,8 +582,8 @@ bool VulkanRenderTargetCache::Initialize() {
|
|||
host_depth_store_shader.first, host_depth_store_shader.second);
|
||||
if (host_depth_store_pipeline == VK_NULL_HANDLE) {
|
||||
XELOGE(
|
||||
"VulkanRenderTargetCache: Failed to create the {}-sample host depth "
|
||||
"storing pipeline",
|
||||
"VulkanRenderTargetCache: Failed to create the {}-sample host "
|
||||
"depth storing pipeline",
|
||||
uint32_t(1) << i);
|
||||
Shutdown();
|
||||
return false;
|
||||
|
@ -529,8 +606,8 @@ bool VulkanRenderTargetCache::Initialize() {
|
|||
sizeof(shaders::passthrough_position_xy_vs));
|
||||
if (transfer_passthrough_vertex_shader_ == VK_NULL_HANDLE) {
|
||||
XELOGE(
|
||||
"VulkanRenderTargetCache: Failed to create the render target ownership "
|
||||
"transfer vertex shader");
|
||||
"VulkanRenderTargetCache: Failed to create the render target "
|
||||
"ownership transfer vertex shader");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
@ -558,8 +635,8 @@ bool VulkanRenderTargetCache::Initialize() {
|
|||
uint32_t transfer_pipeline_layout_descriptor_sets_remaining =
|
||||
transfer_pipeline_layout_info.used_descriptor_sets;
|
||||
uint32_t transfer_pipeline_layout_descriptor_set_index;
|
||||
while (
|
||||
xe::bit_scan_forward(transfer_pipeline_layout_descriptor_sets_remaining,
|
||||
while (xe::bit_scan_forward(
|
||||
transfer_pipeline_layout_descriptor_sets_remaining,
|
||||
&transfer_pipeline_layout_descriptor_set_index)) {
|
||||
transfer_pipeline_layout_descriptor_sets_remaining &=
|
||||
~(uint32_t(1) << transfer_pipeline_layout_descriptor_set_index);
|
||||
|
@ -590,7 +667,8 @@ bool VulkanRenderTargetCache::Initialize() {
|
|||
}
|
||||
transfer_pipeline_layout_push_constant_range.size = uint32_t(
|
||||
sizeof(uint32_t) *
|
||||
xe::bit_count(transfer_pipeline_layout_info.used_push_constant_dwords));
|
||||
xe::bit_count(
|
||||
transfer_pipeline_layout_info.used_push_constant_dwords));
|
||||
transfer_pipeline_layout_create_info.pushConstantRangeCount =
|
||||
transfer_pipeline_layout_info.used_push_constant_dwords ? 1 : 0;
|
||||
if (dfn.vkCreatePipelineLayout(
|
||||
|
@ -631,8 +709,8 @@ bool VulkanRenderTargetCache::Initialize() {
|
|||
dump_pipeline_layout_create_info.pPushConstantRanges =
|
||||
&dump_pipeline_layout_push_constant_range;
|
||||
if (dfn.vkCreatePipelineLayout(device, &dump_pipeline_layout_create_info,
|
||||
nullptr,
|
||||
&dump_pipeline_layout_color_) != VK_SUCCESS) {
|
||||
nullptr, &dump_pipeline_layout_color_) !=
|
||||
VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"VulkanRenderTargetCache: Failed to create the color render target "
|
||||
"dumping pipeline layout");
|
||||
|
@ -642,14 +720,162 @@ bool VulkanRenderTargetCache::Initialize() {
|
|||
dump_pipeline_layout_descriptor_set_layouts[kDumpDescriptorSetSource] =
|
||||
descriptor_set_layout_sampled_image_x2_;
|
||||
if (dfn.vkCreatePipelineLayout(device, &dump_pipeline_layout_create_info,
|
||||
nullptr,
|
||||
&dump_pipeline_layout_depth_) != VK_SUCCESS) {
|
||||
nullptr, &dump_pipeline_layout_depth_) !=
|
||||
VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"VulkanRenderTargetCache: Failed to create the depth render target "
|
||||
"dumping pipeline layout");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
} else if (path_ == Path::kPixelShaderInterlock) {
|
||||
// Pixel (fragment) shader interlock.
|
||||
|
||||
// Blending is done in linear space directly in shaders.
|
||||
gamma_render_target_as_srgb_ = false;
|
||||
|
||||
// Always true float24 depth rounded to the nearest even.
|
||||
depth_float24_round_ = true;
|
||||
|
||||
// The pipeline layout and the pipelines for clearing the EDRAM buffer in
|
||||
// resolves.
|
||||
VkPushConstantRange resolve_fsi_clear_push_constant_range;
|
||||
resolve_fsi_clear_push_constant_range.stageFlags =
|
||||
VK_SHADER_STAGE_COMPUTE_BIT;
|
||||
resolve_fsi_clear_push_constant_range.offset = 0;
|
||||
resolve_fsi_clear_push_constant_range.size =
|
||||
sizeof(draw_util::ResolveClearShaderConstants);
|
||||
VkPipelineLayoutCreateInfo resolve_fsi_clear_pipeline_layout_create_info;
|
||||
resolve_fsi_clear_pipeline_layout_create_info.sType =
|
||||
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||
resolve_fsi_clear_pipeline_layout_create_info.pNext = nullptr;
|
||||
resolve_fsi_clear_pipeline_layout_create_info.flags = 0;
|
||||
resolve_fsi_clear_pipeline_layout_create_info.setLayoutCount = 1;
|
||||
resolve_fsi_clear_pipeline_layout_create_info.pSetLayouts =
|
||||
&descriptor_set_layout_storage_buffer_;
|
||||
resolve_fsi_clear_pipeline_layout_create_info.pushConstantRangeCount = 1;
|
||||
resolve_fsi_clear_pipeline_layout_create_info.pPushConstantRanges =
|
||||
&resolve_fsi_clear_push_constant_range;
|
||||
if (dfn.vkCreatePipelineLayout(
|
||||
device, &resolve_fsi_clear_pipeline_layout_create_info, nullptr,
|
||||
&resolve_fsi_clear_pipeline_layout_) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"VulkanRenderTargetCache: Failed to create the resolve EDRAM buffer "
|
||||
"clear pipeline layout");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
resolve_fsi_clear_32bpp_pipeline_ = ui::vulkan::util::CreateComputePipeline(
|
||||
provider, resolve_fsi_clear_pipeline_layout_,
|
||||
draw_resolution_scaled ? shaders::resolve_clear_32bpp_scaled_cs
|
||||
: shaders::resolve_clear_32bpp_cs,
|
||||
draw_resolution_scaled ? sizeof(shaders::resolve_clear_32bpp_scaled_cs)
|
||||
: sizeof(shaders::resolve_clear_32bpp_cs));
|
||||
if (resolve_fsi_clear_32bpp_pipeline_ == VK_NULL_HANDLE) {
|
||||
XELOGE(
|
||||
"VulkanRenderTargetCache: Failed to create the 32bpp resolve EDRAM "
|
||||
"buffer clear pipeline");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
resolve_fsi_clear_64bpp_pipeline_ = ui::vulkan::util::CreateComputePipeline(
|
||||
provider, resolve_fsi_clear_pipeline_layout_,
|
||||
draw_resolution_scaled ? shaders::resolve_clear_64bpp_scaled_cs
|
||||
: shaders::resolve_clear_64bpp_cs,
|
||||
draw_resolution_scaled ? sizeof(shaders::resolve_clear_64bpp_scaled_cs)
|
||||
: sizeof(shaders::resolve_clear_64bpp_cs));
|
||||
if (resolve_fsi_clear_32bpp_pipeline_ == VK_NULL_HANDLE) {
|
||||
XELOGE(
|
||||
"VulkanRenderTargetCache: Failed to create the 64bpp resolve EDRAM "
|
||||
"buffer clear pipeline");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Common render pass.
|
||||
VkSubpassDescription fsi_subpass = {};
|
||||
fsi_subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
// Fragment shader interlock provides synchronization and ordering within a
|
||||
// subpass, create an external by-region dependency to maintain interlocking
|
||||
// between passes. Framebuffer-global dependencies will be made with
|
||||
// explicit barriers when the addressing of the EDRAM buffer relatively to
|
||||
// the fragment coordinates is changed.
|
||||
VkSubpassDependency fsi_subpass_dependencies[2];
|
||||
fsi_subpass_dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
|
||||
fsi_subpass_dependencies[0].dstSubpass = 0;
|
||||
fsi_subpass_dependencies[0].srcStageMask =
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
fsi_subpass_dependencies[0].dstStageMask =
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
fsi_subpass_dependencies[0].srcAccessMask =
|
||||
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
|
||||
fsi_subpass_dependencies[0].dstAccessMask =
|
||||
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
|
||||
fsi_subpass_dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
|
||||
fsi_subpass_dependencies[1] = fsi_subpass_dependencies[0];
|
||||
std::swap(fsi_subpass_dependencies[1].srcSubpass,
|
||||
fsi_subpass_dependencies[1].dstSubpass);
|
||||
VkRenderPassCreateInfo fsi_render_pass_create_info;
|
||||
fsi_render_pass_create_info.sType =
|
||||
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
fsi_render_pass_create_info.pNext = nullptr;
|
||||
fsi_render_pass_create_info.flags = 0;
|
||||
fsi_render_pass_create_info.attachmentCount = 0;
|
||||
fsi_render_pass_create_info.pAttachments = nullptr;
|
||||
fsi_render_pass_create_info.subpassCount = 1;
|
||||
fsi_render_pass_create_info.pSubpasses = &fsi_subpass;
|
||||
fsi_render_pass_create_info.dependencyCount =
|
||||
uint32_t(xe::countof(fsi_subpass_dependencies));
|
||||
fsi_render_pass_create_info.pDependencies = fsi_subpass_dependencies;
|
||||
if (dfn.vkCreateRenderPass(device, &fsi_render_pass_create_info, nullptr,
|
||||
&fsi_render_pass_) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"VulkanRenderTargetCache: Failed to create the fragment shader "
|
||||
"interlock render backend render pass");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Common framebuffer.
|
||||
VkFramebufferCreateInfo fsi_framebuffer_create_info;
|
||||
fsi_framebuffer_create_info.sType =
|
||||
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
||||
fsi_framebuffer_create_info.pNext = nullptr;
|
||||
fsi_framebuffer_create_info.flags = 0;
|
||||
fsi_framebuffer_create_info.renderPass = fsi_render_pass_;
|
||||
fsi_framebuffer_create_info.attachmentCount = 0;
|
||||
fsi_framebuffer_create_info.pAttachments = nullptr;
|
||||
fsi_framebuffer_create_info.width = std::min(
|
||||
xenos::kTexture2DCubeMaxWidthHeight * draw_resolution_scale_x(),
|
||||
device_limits.maxFramebufferWidth);
|
||||
fsi_framebuffer_create_info.height = std::min(
|
||||
xenos::kTexture2DCubeMaxWidthHeight * draw_resolution_scale_y(),
|
||||
device_limits.maxFramebufferHeight);
|
||||
fsi_framebuffer_create_info.layers = 1;
|
||||
if (dfn.vkCreateFramebuffer(device, &fsi_framebuffer_create_info, nullptr,
|
||||
&fsi_framebuffer_.framebuffer) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"VulkanRenderTargetCache: Failed to create the fragment shader "
|
||||
"interlock render backend framebuffer");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
fsi_framebuffer_.host_extent.width = fsi_framebuffer_create_info.width;
|
||||
fsi_framebuffer_.host_extent.height = fsi_framebuffer_create_info.height;
|
||||
} else {
|
||||
assert_unhandled_case(path_);
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reset the last update structures, to keep the defaults consistent between
|
||||
// paths regardless of whether the update for the path actually modifies them.
|
||||
last_update_render_pass_key_ = RenderPassKey();
|
||||
last_update_render_pass_ = VK_NULL_HANDLE;
|
||||
last_update_framebuffer_pitch_tiles_at_32bpp_ = 0;
|
||||
std::memset(last_update_framebuffer_attachments_, 0,
|
||||
sizeof(last_update_framebuffer_attachments_));
|
||||
last_update_framebuffer_ = VK_NULL_HANDLE;
|
||||
|
||||
InitializeCommon();
|
||||
return true;
|
||||
|
@ -667,6 +893,18 @@ void VulkanRenderTargetCache::Shutdown(bool from_destructor) {
|
|||
// already too late.
|
||||
DestroyAllRenderTargets(true);
|
||||
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyPipeline, device,
|
||||
resolve_fsi_clear_64bpp_pipeline_);
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyPipeline, device,
|
||||
resolve_fsi_clear_32bpp_pipeline_);
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyPipelineLayout, device,
|
||||
resolve_fsi_clear_pipeline_layout_);
|
||||
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyFramebuffer, device,
|
||||
fsi_framebuffer_.framebuffer);
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyRenderPass, device,
|
||||
fsi_render_pass_);
|
||||
|
||||
for (const auto& dump_pipeline_pair : dump_pipelines_) {
|
||||
// May be null to prevent recreation attempts.
|
||||
if (dump_pipeline_pair.second != VK_NULL_HANDLE) {
|
||||
|
@ -951,8 +1189,8 @@ bool VulkanRenderTargetCache::Resolve(const Memory& memory,
|
|||
bool clear_depth = resolve_info.IsClearingDepth();
|
||||
bool clear_color = resolve_info.IsClearingColor();
|
||||
if (clear_depth || clear_color) {
|
||||
// TODO(Triang3l): Fragment shader interlock path EDRAM buffer clearing.
|
||||
if (GetPath() == Path::kHostRenderTargets) {
|
||||
switch (GetPath()) {
|
||||
case Path::kHostRenderTargets: {
|
||||
Transfer::Rectangle clear_rectangle;
|
||||
RenderTarget* clear_render_targets[2];
|
||||
// If PrepareHostRenderTargetsResolveClear returns false, may be just an
|
||||
|
@ -970,6 +1208,62 @@ bool VulkanRenderTargetCache::Resolve(const Memory& memory,
|
|||
&clear_rectangle);
|
||||
}
|
||||
cleared = true;
|
||||
} break;
|
||||
case Path::kPixelShaderInterlock: {
|
||||
UseEdramBuffer(EdramBufferUsage::kComputeWrite);
|
||||
// Should be safe to only commit once (if was accessed as unordered or
|
||||
// with fragment shader interlock previously - if there was nothing to
|
||||
// copy, only to clear, for some reason, for instance), overlap of the
|
||||
// depth and the color ranges is highly unlikely.
|
||||
CommitEdramBufferShaderWrites();
|
||||
command_buffer.CmdVkBindDescriptorSets(
|
||||
VK_PIPELINE_BIND_POINT_COMPUTE, resolve_fsi_clear_pipeline_layout_,
|
||||
0, 1, &edram_storage_buffer_descriptor_set_, 0, nullptr);
|
||||
std::pair<uint32_t, uint32_t> clear_group_count =
|
||||
resolve_info.GetClearShaderGroupCount(draw_resolution_scale_x(),
|
||||
draw_resolution_scale_y());
|
||||
assert_true(clear_group_count.first && clear_group_count.second);
|
||||
if (clear_depth) {
|
||||
command_processor_.BindExternalComputePipeline(
|
||||
resolve_fsi_clear_32bpp_pipeline_);
|
||||
draw_util::ResolveClearShaderConstants depth_clear_constants;
|
||||
resolve_info.GetDepthClearShaderConstants(depth_clear_constants);
|
||||
command_buffer.CmdVkPushConstants(
|
||||
resolve_fsi_clear_pipeline_layout_, VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
0, sizeof(depth_clear_constants), &depth_clear_constants);
|
||||
command_processor_.SubmitBarriers(true);
|
||||
command_buffer.CmdVkDispatch(clear_group_count.first,
|
||||
clear_group_count.second, 1);
|
||||
}
|
||||
if (clear_color) {
|
||||
command_processor_.BindExternalComputePipeline(
|
||||
resolve_info.color_edram_info.format_is_64bpp
|
||||
? resolve_fsi_clear_64bpp_pipeline_
|
||||
: resolve_fsi_clear_32bpp_pipeline_);
|
||||
draw_util::ResolveClearShaderConstants color_clear_constants;
|
||||
resolve_info.GetColorClearShaderConstants(color_clear_constants);
|
||||
if (clear_depth) {
|
||||
// Non-RT-specific constants have already been set.
|
||||
command_buffer.CmdVkPushConstants(
|
||||
resolve_fsi_clear_pipeline_layout_, VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
uint32_t(offsetof(draw_util::ResolveClearShaderConstants,
|
||||
rt_specific)),
|
||||
sizeof(color_clear_constants.rt_specific),
|
||||
&color_clear_constants.rt_specific);
|
||||
} else {
|
||||
command_buffer.CmdVkPushConstants(
|
||||
resolve_fsi_clear_pipeline_layout_, VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
0, sizeof(color_clear_constants), &color_clear_constants);
|
||||
}
|
||||
command_processor_.SubmitBarriers(true);
|
||||
command_buffer.CmdVkDispatch(clear_group_count.first,
|
||||
clear_group_count.second, 1);
|
||||
}
|
||||
MarkEdramBufferModified();
|
||||
cleared = true;
|
||||
} break;
|
||||
default:
|
||||
assert_unhandled_case(GetPath());
|
||||
}
|
||||
} else {
|
||||
cleared = true;
|
||||
|
@ -987,8 +1281,15 @@ bool VulkanRenderTargetCache::Update(
|
|||
return false;
|
||||
}
|
||||
|
||||
// TODO(Triang3l): All paths (FSI).
|
||||
auto rb_surface_info = register_file().Get<reg::RB_SURFACE_INFO>();
|
||||
|
||||
RenderPassKey render_pass_key;
|
||||
// Needed even with the fragment shader interlock render backend for passing
|
||||
// the sample count to the pipeline cache.
|
||||
render_pass_key.msaa_samples = rb_surface_info.msaa_samples;
|
||||
|
||||
switch (GetPath()) {
|
||||
case Path::kHostRenderTargets: {
|
||||
RenderTarget* const* depth_and_color_render_targets =
|
||||
last_update_accumulated_render_targets();
|
||||
|
||||
|
@ -996,14 +1297,11 @@ bool VulkanRenderTargetCache::Update(
|
|||
depth_and_color_render_targets,
|
||||
last_update_transfers());
|
||||
|
||||
auto rb_surface_info = register_file().Get<reg::RB_SURFACE_INFO>();
|
||||
uint32_t render_targets_are_srgb =
|
||||
gamma_render_target_as_srgb_
|
||||
? last_update_accumulated_color_targets_are_gamma()
|
||||
: 0;
|
||||
|
||||
RenderPassKey render_pass_key;
|
||||
render_pass_key.msaa_samples = rb_surface_info.msaa_samples;
|
||||
if (depth_and_color_render_targets[0]) {
|
||||
render_pass_key.depth_and_color_used |= 1 << 0;
|
||||
render_pass_key.depth_format =
|
||||
|
@ -1043,7 +1341,7 @@ bool VulkanRenderTargetCache::Update(
|
|||
? last_update_render_pass_
|
||||
: VK_NULL_HANDLE;
|
||||
if (render_pass == VK_NULL_HANDLE) {
|
||||
render_pass = GetRenderPass(render_pass_key);
|
||||
render_pass = GetHostRenderTargetsRenderPass(render_pass_key);
|
||||
if (render_pass == VK_NULL_HANDLE) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1052,12 +1350,13 @@ bool VulkanRenderTargetCache::Update(
|
|||
}
|
||||
|
||||
uint32_t pitch_tiles_at_32bpp =
|
||||
((rb_surface_info.surface_pitch
|
||||
<< uint32_t(rb_surface_info.msaa_samples >= xenos::MsaaSamples::k4X)) +
|
||||
((rb_surface_info.surface_pitch << uint32_t(
|
||||
rb_surface_info.msaa_samples >= xenos::MsaaSamples::k4X)) +
|
||||
(xenos::kEdramTileWidthSamples - 1)) /
|
||||
xenos::kEdramTileWidthSamples;
|
||||
if (framebuffer) {
|
||||
if (last_update_framebuffer_pitch_tiles_at_32bpp_ != pitch_tiles_at_32bpp ||
|
||||
if (last_update_framebuffer_pitch_tiles_at_32bpp_ !=
|
||||
pitch_tiles_at_32bpp ||
|
||||
std::memcmp(last_update_framebuffer_attachments_,
|
||||
depth_and_color_render_targets,
|
||||
sizeof(last_update_framebuffer_attachments_))) {
|
||||
|
@ -1065,7 +1364,8 @@ bool VulkanRenderTargetCache::Update(
|
|||
}
|
||||
}
|
||||
if (!framebuffer) {
|
||||
framebuffer = GetFramebuffer(render_pass_key, pitch_tiles_at_32bpp,
|
||||
framebuffer = GetHostRenderTargetsFramebuffer(
|
||||
render_pass_key, pitch_tiles_at_32bpp,
|
||||
depth_and_color_render_targets);
|
||||
if (!framebuffer) {
|
||||
return false;
|
||||
|
@ -1101,14 +1401,41 @@ bool VulkanRenderTargetCache::Update(
|
|||
vulkan_rt.current_stage_mask(), rt_dst_stage_mask,
|
||||
vulkan_rt.current_access_mask(), rt_dst_access_mask,
|
||||
vulkan_rt.current_layout(), rt_new_layout);
|
||||
vulkan_rt.SetUsage(rt_dst_stage_mask, rt_dst_access_mask, rt_new_layout);
|
||||
vulkan_rt.SetUsage(rt_dst_stage_mask, rt_dst_access_mask,
|
||||
rt_new_layout);
|
||||
}
|
||||
} break;
|
||||
|
||||
case Path::kPixelShaderInterlock: {
|
||||
// For FSI, only the barrier is needed - already scheduled if required.
|
||||
// But the buffer will be used for FSI drawing now.
|
||||
UseEdramBuffer(EdramBufferUsage::kFragmentReadWrite);
|
||||
// Commit preceding unordered (but not FSI) writes like clears as they
|
||||
// aren't synchronized with FSI accesses.
|
||||
CommitEdramBufferShaderWrites(
|
||||
EdramBufferModificationStatus::kViaUnordered);
|
||||
// TODO(Triang3l): Check if this draw call modifies color or depth /
|
||||
// stencil, at least coarsely, to prevent useless barriers.
|
||||
MarkEdramBufferModified(
|
||||
EdramBufferModificationStatus::kViaFragmentShaderInterlock);
|
||||
last_update_render_pass_key_ = render_pass_key;
|
||||
last_update_render_pass_ = fsi_render_pass_;
|
||||
last_update_framebuffer_ = &fsi_framebuffer_;
|
||||
} break;
|
||||
|
||||
default:
|
||||
assert_unhandled_case(GetPath());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
VkRenderPass VulkanRenderTargetCache::GetRenderPass(RenderPassKey key) {
|
||||
auto it = render_passes_.find(key.key);
|
||||
VkRenderPass VulkanRenderTargetCache::GetHostRenderTargetsRenderPass(
|
||||
RenderPassKey key) {
|
||||
assert_true(GetPath() == Path::kHostRenderTargets);
|
||||
|
||||
auto it = render_passes_.find(key);
|
||||
if (it != render_passes_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
@ -1244,10 +1571,10 @@ VkRenderPass VulkanRenderTargetCache::GetRenderPass(RenderPassKey key) {
|
|||
if (dfn.vkCreateRenderPass(device, &render_pass_create_info, nullptr,
|
||||
&render_pass) != VK_SUCCESS) {
|
||||
XELOGE("VulkanRenderTargetCache: Failed to create a render pass");
|
||||
render_passes_.emplace(key.key, VK_NULL_HANDLE);
|
||||
render_passes_.emplace(key, VK_NULL_HANDLE);
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
render_passes_.emplace(key.key, render_pass);
|
||||
render_passes_.emplace(key, render_pass);
|
||||
return render_pass;
|
||||
}
|
||||
|
||||
|
@ -1353,15 +1680,17 @@ VulkanRenderTargetCache::VulkanRenderTarget::~VulkanRenderTarget() {
|
|||
}
|
||||
|
||||
uint32_t VulkanRenderTargetCache::GetMaxRenderTargetWidth() const {
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
return provider.device_properties().limits.maxFramebufferWidth;
|
||||
const VkPhysicalDeviceLimits& device_limits =
|
||||
command_processor_.GetVulkanProvider().device_properties().limits;
|
||||
return std::min(device_limits.maxFramebufferWidth,
|
||||
device_limits.maxImageDimension2D);
|
||||
}
|
||||
|
||||
uint32_t VulkanRenderTargetCache::GetMaxRenderTargetHeight() const {
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
return provider.device_properties().limits.maxFramebufferHeight;
|
||||
const VkPhysicalDeviceLimits& device_limits =
|
||||
command_processor_.GetVulkanProvider().device_properties().limits;
|
||||
return std::min(device_limits.maxFramebufferHeight,
|
||||
device_limits.maxImageDimension2D);
|
||||
}
|
||||
|
||||
RenderTargetCache::RenderTarget* VulkanRenderTargetCache::CreateRenderTarget(
|
||||
|
@ -1615,6 +1944,12 @@ bool VulkanRenderTargetCache::IsHostDepthEncodingDifferent(
|
|||
return false;
|
||||
}
|
||||
|
||||
void VulkanRenderTargetCache::RequestPixelShaderInterlockBarrier() {
|
||||
if (edram_buffer_usage_ == EdramBufferUsage::kFragmentReadWrite) {
|
||||
CommitEdramBufferShaderWrites();
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanRenderTargetCache::GetEdramBufferUsageMasks(
|
||||
EdramBufferUsage usage, VkPipelineStageFlags& stage_mask_out,
|
||||
VkAccessFlags& access_mask_out) {
|
||||
|
@ -1715,7 +2050,7 @@ void VulkanRenderTargetCache::CommitEdramBufferShaderWrites(
|
|||
}
|
||||
|
||||
const VulkanRenderTargetCache::Framebuffer*
|
||||
VulkanRenderTargetCache::GetFramebuffer(
|
||||
VulkanRenderTargetCache::GetHostRenderTargetsFramebuffer(
|
||||
RenderPassKey render_pass_key, uint32_t pitch_tiles_at_32bpp,
|
||||
const RenderTarget* const* depth_and_color_render_targets) {
|
||||
FramebufferKey key;
|
||||
|
@ -1749,8 +2084,10 @@ VulkanRenderTargetCache::GetFramebuffer(
|
|||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const VkPhysicalDeviceLimits& device_limits =
|
||||
provider.device_properties().limits;
|
||||
|
||||
VkRenderPass render_pass = GetRenderPass(render_pass_key);
|
||||
VkRenderPass render_pass = GetHostRenderTargetsRenderPass(render_pass_key);
|
||||
if (render_pass == VK_NULL_HANDLE) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1789,12 +2126,19 @@ VulkanRenderTargetCache::GetFramebuffer(
|
|||
render_pass_key.msaa_samples);
|
||||
} else {
|
||||
assert_zero(render_pass_key.depth_and_color_used);
|
||||
host_extent.width = 0;
|
||||
host_extent.height = 0;
|
||||
// Still needed for occlusion queries.
|
||||
host_extent.width = xenos::kTexture2DCubeMaxWidthHeight;
|
||||
host_extent.height = xenos::kTexture2DCubeMaxWidthHeight;
|
||||
}
|
||||
// Vulkan requires width and height greater than 0.
|
||||
framebuffer_create_info.width = std::max(host_extent.width, uint32_t(1));
|
||||
framebuffer_create_info.height = std::max(host_extent.height, uint32_t(1));
|
||||
// Limiting to the device limit for the case of no attachments, for which
|
||||
// there's no limit imposed by the sizes of the attachments that have been
|
||||
// created successfully.
|
||||
host_extent.width = std::min(host_extent.width * draw_resolution_scale_x(),
|
||||
device_limits.maxFramebufferWidth);
|
||||
host_extent.height = std::min(host_extent.height * draw_resolution_scale_y(),
|
||||
device_limits.maxFramebufferHeight);
|
||||
framebuffer_create_info.width = host_extent.width;
|
||||
framebuffer_create_info.height = host_extent.height;
|
||||
framebuffer_create_info.layers = 1;
|
||||
VkFramebuffer framebuffer;
|
||||
if (dfn.vkCreateFramebuffer(device, &framebuffer_create_info, nullptr,
|
||||
|
@ -4070,7 +4414,8 @@ VkPipeline const* VulkanRenderTargetCache::GetTransferPipelines(
|
|||
: nullptr;
|
||||
}
|
||||
|
||||
VkRenderPass render_pass = GetRenderPass(key.render_pass_key);
|
||||
VkRenderPass render_pass =
|
||||
GetHostRenderTargetsRenderPass(key.render_pass_key);
|
||||
VkShaderModule fragment_shader_module = GetTransferShader(key.shader_key);
|
||||
if (render_pass == VK_NULL_HANDLE ||
|
||||
fragment_shader_module == VK_NULL_HANDLE) {
|
||||
|
@ -4643,7 +4988,8 @@ void VulkanRenderTargetCache::PerformTransfersAndResolveClears(
|
|||
dest_rt_key.GetColorFormat();
|
||||
transfer_render_pass_key.color_rts_use_transfer_formats = 1;
|
||||
}
|
||||
VkRenderPass transfer_render_pass = GetRenderPass(transfer_render_pass_key);
|
||||
VkRenderPass transfer_render_pass =
|
||||
GetHostRenderTargetsRenderPass(transfer_render_pass_key);
|
||||
if (transfer_render_pass == VK_NULL_HANDLE) {
|
||||
continue;
|
||||
}
|
||||
|
@ -4651,7 +4997,7 @@ void VulkanRenderTargetCache::PerformTransfersAndResolveClears(
|
|||
transfer_framebuffer_render_targets[1 + xenos::kMaxColorRenderTargets] =
|
||||
{};
|
||||
transfer_framebuffer_render_targets[dest_rt_key.is_depth ? 0 : 1] = dest_rt;
|
||||
const Framebuffer* transfer_framebuffer = GetFramebuffer(
|
||||
const Framebuffer* transfer_framebuffer = GetHostRenderTargetsFramebuffer(
|
||||
transfer_render_pass_key, dest_rt_key.pitch_tiles_at_32bpp,
|
||||
transfer_framebuffer_render_targets);
|
||||
if (!transfer_framebuffer) {
|
||||
|
|
|
@ -43,6 +43,10 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
|
|||
// true 4x MSAA passes (framebuffers because render target cache render
|
||||
// targets are different for 2x and 4x guest MSAA, pipelines because the
|
||||
// sample mask will have 2 samples excluded for 2x-as-4x).
|
||||
// This has effect only on the attachments, but even in cases when there
|
||||
// are no attachments, it can be used to the sample count between
|
||||
// subsystems, for instance, to specify the desired number of samples to
|
||||
// use when there are no attachments in pipelines.
|
||||
xenos::MsaaSamples msaa_samples : xenos::kMsaaSamplesBits; // 2
|
||||
// << 0 is depth, << 1...4 is color.
|
||||
uint32_t depth_and_color_used : 1 + xenos::kMaxColorRenderTargets; // 7
|
||||
|
@ -81,8 +85,9 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
|
|||
static_assert_size(RenderPassKey, sizeof(uint32_t));
|
||||
|
||||
struct Framebuffer {
|
||||
VkFramebuffer framebuffer;
|
||||
VkExtent2D host_extent;
|
||||
VkFramebuffer framebuffer = VK_NULL_HANDLE;
|
||||
VkExtent2D host_extent{};
|
||||
Framebuffer() = default;
|
||||
Framebuffer(VkFramebuffer framebuffer, const VkExtent2D& host_extent)
|
||||
: framebuffer(framebuffer), host_extent(host_extent) {}
|
||||
};
|
||||
|
@ -96,15 +101,16 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
|
|||
|
||||
// Transient descriptor set layouts must be initialized in the command
|
||||
// processor.
|
||||
bool Initialize();
|
||||
bool Initialize(uint32_t shared_memory_binding_count);
|
||||
void Shutdown(bool from_destructor = false);
|
||||
void ClearCache() override;
|
||||
|
||||
void CompletedSubmissionUpdated();
|
||||
void EndSubmission();
|
||||
|
||||
// TODO(Triang3l): Fragment shader interlock.
|
||||
Path GetPath() const override { return Path::kHostRenderTargets; }
|
||||
Path GetPath() const override { return path_; }
|
||||
|
||||
VkBuffer edram_buffer() const { return edram_buffer_; }
|
||||
|
||||
// Performs the resolve to a shared memory area according to the current
|
||||
// register values, and also clears the render targets if needed. Must be in a
|
||||
|
@ -161,7 +167,11 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
|
|||
// Returns the render pass object, or VK_NULL_HANDLE if failed to create.
|
||||
// A render pass managed by the render target cache may be ended and resumed
|
||||
// at any time (to allow for things like copying and texture loading).
|
||||
VkRenderPass GetRenderPass(RenderPassKey key);
|
||||
VkRenderPass GetHostRenderTargetsRenderPass(RenderPassKey key);
|
||||
VkRenderPass GetFragmentShaderInterlockRenderPass() const {
|
||||
assert_true(GetPath() == Path::kPixelShaderInterlock);
|
||||
return fsi_render_pass_;
|
||||
}
|
||||
|
||||
VkFormat GetDepthVulkanFormat(xenos::DepthRenderTargetFormat format) const;
|
||||
VkFormat GetColorVulkanFormat(xenos::ColorRenderTargetFormat format) const;
|
||||
|
@ -178,6 +188,8 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
|
|||
bool IsHostDepthEncodingDifferent(
|
||||
xenos::DepthRenderTargetFormat format) const override;
|
||||
|
||||
void RequestPixelShaderInterlockBarrier() override;
|
||||
|
||||
private:
|
||||
enum class EdramBufferUsage {
|
||||
// There's no need for combined fragment and compute usages.
|
||||
|
@ -251,6 +263,8 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
|
|||
VulkanCommandProcessor& command_processor_;
|
||||
TraceWriter& trace_writer_;
|
||||
|
||||
Path path_ = Path::kHostRenderTargets;
|
||||
|
||||
// Accessible in fragment and compute shaders.
|
||||
VkDescriptorSetLayout descriptor_set_layout_storage_buffer_ = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout descriptor_set_layout_sampled_image_ = VK_NULL_HANDLE;
|
||||
|
@ -276,9 +290,18 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
|
|||
std::array<VkPipeline, size_t(draw_util::ResolveCopyShaderIndex::kCount)>
|
||||
resolve_copy_pipelines_{};
|
||||
|
||||
// RenderPassKey::key -> VkRenderPass.
|
||||
// VK_NULL_HANDLE if failed to create.
|
||||
std::unordered_map<uint32_t, VkRenderPass> render_passes_;
|
||||
// On the fragment shader interlock path, the render pass key is used purely
|
||||
// for passing parameters to pipeline setup - there's always only one render
|
||||
// pass.
|
||||
RenderPassKey last_update_render_pass_key_;
|
||||
VkRenderPass last_update_render_pass_ = VK_NULL_HANDLE;
|
||||
// The pitch is not used on the fragment shader interlock path.
|
||||
uint32_t last_update_framebuffer_pitch_tiles_at_32bpp_ = 0;
|
||||
// The attachments are not used on the fragment shader interlock path.
|
||||
const RenderTarget* const*
|
||||
last_update_framebuffer_attachments_[1 + xenos::kMaxColorRenderTargets] =
|
||||
{};
|
||||
const Framebuffer* last_update_framebuffer_ = VK_NULL_HANDLE;
|
||||
|
||||
// For host render targets.
|
||||
|
||||
|
@ -809,7 +832,7 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
|
|||
};
|
||||
|
||||
// Returns the framebuffer object, or VK_NULL_HANDLE if failed to create.
|
||||
const Framebuffer* GetFramebuffer(
|
||||
const Framebuffer* GetHostRenderTargetsFramebuffer(
|
||||
RenderPassKey render_pass_key, uint32_t pitch_tiles_at_32bpp,
|
||||
const RenderTarget* const* depth_and_color_render_targets);
|
||||
|
||||
|
@ -845,17 +868,13 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
|
|||
bool msaa_2x_attachments_supported_ = false;
|
||||
bool msaa_2x_no_attachments_supported_ = false;
|
||||
|
||||
// VK_NULL_HANDLE if failed to create.
|
||||
std::unordered_map<RenderPassKey, VkRenderPass, RenderPassKey::Hasher>
|
||||
render_passes_;
|
||||
|
||||
std::unordered_map<FramebufferKey, Framebuffer, FramebufferKey::Hasher>
|
||||
framebuffers_;
|
||||
|
||||
RenderPassKey last_update_render_pass_key_;
|
||||
VkRenderPass last_update_render_pass_ = VK_NULL_HANDLE;
|
||||
uint32_t last_update_framebuffer_pitch_tiles_at_32bpp_ = 0;
|
||||
const RenderTarget* const*
|
||||
last_update_framebuffer_attachments_[1 + xenos::kMaxColorRenderTargets] =
|
||||
{};
|
||||
const Framebuffer* last_update_framebuffer_ = VK_NULL_HANDLE;
|
||||
|
||||
// Set 0 - EDRAM storage buffer, set 1 - source depth sampled image (and
|
||||
// unused stencil from the transfer descriptor set), HostDepthStoreConstants
|
||||
// passed via push constants.
|
||||
|
@ -895,6 +914,15 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
|
|||
// Temporary storage for DumpRenderTargets.
|
||||
std::vector<ResolveCopyDumpRectangle> dump_rectangles_;
|
||||
std::vector<DumpInvocation> dump_invocations_;
|
||||
|
||||
// For pixel (fragment) shader interlock.
|
||||
|
||||
VkRenderPass fsi_render_pass_ = VK_NULL_HANDLE;
|
||||
Framebuffer fsi_framebuffer_;
|
||||
|
||||
VkPipelineLayout resolve_fsi_clear_pipeline_layout_ = VK_NULL_HANDLE;
|
||||
VkPipeline resolve_fsi_clear_32bpp_pipeline_ = VK_NULL_HANDLE;
|
||||
VkPipeline resolve_fsi_clear_64bpp_pipeline_ = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
} // namespace vulkan
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// VK_KHR_get_physical_device_properties2 functions used in Xenia.
|
||||
// Promoted to Vulkan 1.1 core.
|
||||
XE_UI_VULKAN_FUNCTION_PROMOTED(vkGetPhysicalDeviceFeatures2KHR,
|
||||
vkGetPhysicalDeviceFeatures2)
|
||||
XE_UI_VULKAN_FUNCTION_PROMOTED(vkGetPhysicalDeviceMemoryProperties2KHR,
|
||||
vkGetPhysicalDeviceMemoryProperties2)
|
||||
XE_UI_VULKAN_FUNCTION_PROMOTED(vkGetPhysicalDeviceProperties2KHR,
|
||||
|
|
|
@ -696,6 +696,7 @@ bool VulkanProvider::Initialize() {
|
|||
device_extensions_.khr_shader_float_controls = true;
|
||||
device_extensions_.khr_spirv_1_4 = true;
|
||||
if (device_properties_.apiVersion >= VK_MAKE_API_VERSION(0, 1, 3, 0)) {
|
||||
device_extensions_.ext_shader_demote_to_helper_invocation = true;
|
||||
device_extensions_.khr_maintenance4 = true;
|
||||
}
|
||||
}
|
||||
|
@ -709,6 +710,8 @@ bool VulkanProvider::Initialize() {
|
|||
{"VK_EXT_fragment_shader_interlock",
|
||||
offsetof(DeviceExtensions, ext_fragment_shader_interlock)},
|
||||
{"VK_EXT_memory_budget", offsetof(DeviceExtensions, ext_memory_budget)},
|
||||
{"VK_EXT_shader_demote_to_helper_invocation",
|
||||
offsetof(DeviceExtensions, ext_shader_demote_to_helper_invocation)},
|
||||
{"VK_EXT_shader_stencil_export",
|
||||
offsetof(DeviceExtensions, ext_shader_stencil_export)},
|
||||
{"VK_KHR_bind_memory2", offsetof(DeviceExtensions, khr_bind_memory2)},
|
||||
|
@ -816,6 +819,16 @@ bool VulkanProvider::Initialize() {
|
|||
// Get additional device properties.
|
||||
std::memset(&device_float_controls_properties_, 0,
|
||||
sizeof(device_float_controls_properties_));
|
||||
device_float_controls_properties_.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES_KHR;
|
||||
std::memset(&device_fragment_shader_interlock_features_, 0,
|
||||
sizeof(device_fragment_shader_interlock_features_));
|
||||
device_fragment_shader_interlock_features_.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT;
|
||||
std::memset(&device_shader_demote_to_helper_invocation_features_, 0,
|
||||
sizeof(device_shader_demote_to_helper_invocation_features_));
|
||||
device_shader_demote_to_helper_invocation_features_.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT;
|
||||
if (instance_extensions_.khr_get_physical_device_properties2) {
|
||||
VkPhysicalDeviceProperties2KHR device_properties_2;
|
||||
device_properties_2.sType =
|
||||
|
@ -824,8 +837,6 @@ bool VulkanProvider::Initialize() {
|
|||
VkPhysicalDeviceProperties2KHR* device_properties_2_last =
|
||||
&device_properties_2;
|
||||
if (device_extensions_.khr_shader_float_controls) {
|
||||
device_float_controls_properties_.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES_KHR;
|
||||
device_float_controls_properties_.pNext = nullptr;
|
||||
device_properties_2_last->pNext = &device_float_controls_properties_;
|
||||
device_properties_2_last =
|
||||
|
@ -836,6 +847,28 @@ bool VulkanProvider::Initialize() {
|
|||
ifn_.vkGetPhysicalDeviceProperties2KHR(physical_device_,
|
||||
&device_properties_2);
|
||||
}
|
||||
VkPhysicalDeviceFeatures2KHR device_features_2;
|
||||
device_features_2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
|
||||
device_features_2.pNext = nullptr;
|
||||
VkPhysicalDeviceFeatures2KHR* device_features_2_last = &device_features_2;
|
||||
if (device_extensions_.ext_fragment_shader_interlock) {
|
||||
device_fragment_shader_interlock_features_.pNext = nullptr;
|
||||
device_features_2_last->pNext =
|
||||
&device_fragment_shader_interlock_features_;
|
||||
device_features_2_last = reinterpret_cast<VkPhysicalDeviceFeatures2KHR*>(
|
||||
&device_fragment_shader_interlock_features_);
|
||||
}
|
||||
if (device_extensions_.ext_shader_demote_to_helper_invocation) {
|
||||
device_shader_demote_to_helper_invocation_features_.pNext = nullptr;
|
||||
device_features_2_last->pNext =
|
||||
&device_shader_demote_to_helper_invocation_features_;
|
||||
device_features_2_last = reinterpret_cast<VkPhysicalDeviceFeatures2KHR*>(
|
||||
&device_shader_demote_to_helper_invocation_features_);
|
||||
}
|
||||
if (device_features_2_last != &device_features_2) {
|
||||
ifn_.vkGetPhysicalDeviceFeatures2KHR(physical_device_,
|
||||
&device_features_2);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the device.
|
||||
|
@ -888,6 +921,21 @@ bool VulkanProvider::Initialize() {
|
|||
device_create_info_last = reinterpret_cast<VkDeviceCreateInfo*>(
|
||||
&device_portability_subset_features_);
|
||||
}
|
||||
if (device_extensions_.ext_fragment_shader_interlock) {
|
||||
// TODO(Triang3l): Enable only needed fragment shader interlock features.
|
||||
device_fragment_shader_interlock_features_.pNext = nullptr;
|
||||
device_create_info_last->pNext =
|
||||
&device_fragment_shader_interlock_features_;
|
||||
device_create_info_last = reinterpret_cast<VkDeviceCreateInfo*>(
|
||||
&device_fragment_shader_interlock_features_);
|
||||
}
|
||||
if (device_extensions_.ext_shader_demote_to_helper_invocation) {
|
||||
device_shader_demote_to_helper_invocation_features_.pNext = nullptr;
|
||||
device_create_info_last->pNext =
|
||||
&device_shader_demote_to_helper_invocation_features_;
|
||||
device_create_info_last = reinterpret_cast<VkDeviceCreateInfo*>(
|
||||
&device_shader_demote_to_helper_invocation_features_);
|
||||
}
|
||||
if (ifn_.vkCreateDevice(physical_device_, &device_create_info, nullptr,
|
||||
&device_) != VK_SUCCESS) {
|
||||
XELOGE("Failed to create a Vulkan device");
|
||||
|
@ -995,8 +1043,30 @@ bool VulkanProvider::Initialize() {
|
|||
XELOGVK("Vulkan device extensions:");
|
||||
XELOGVK("* VK_EXT_fragment_shader_interlock: {}",
|
||||
device_extensions_.ext_fragment_shader_interlock ? "yes" : "no");
|
||||
if (device_extensions_.ext_fragment_shader_interlock) {
|
||||
XELOGVK(
|
||||
" * Sample interlock: {}",
|
||||
device_fragment_shader_interlock_features_.fragmentShaderSampleInterlock
|
||||
? "yes"
|
||||
: "no");
|
||||
XELOGVK(
|
||||
" * Pixel interlock: {}",
|
||||
device_fragment_shader_interlock_features_.fragmentShaderPixelInterlock
|
||||
? "yes"
|
||||
: "no");
|
||||
}
|
||||
XELOGVK("* VK_EXT_memory_budget: {}",
|
||||
device_extensions_.ext_memory_budget ? "yes" : "no");
|
||||
XELOGVK(
|
||||
"* VK_EXT_shader_demote_to_helper_invocation: {}",
|
||||
device_extensions_.ext_shader_demote_to_helper_invocation ? "yes" : "no");
|
||||
if (device_extensions_.ext_shader_demote_to_helper_invocation) {
|
||||
XELOGVK(" * Demote to helper invocation: {}",
|
||||
device_shader_demote_to_helper_invocation_features_
|
||||
.shaderDemoteToHelperInvocation
|
||||
? "yes"
|
||||
: "no");
|
||||
}
|
||||
XELOGVK("* VK_EXT_shader_stencil_export: {}",
|
||||
device_extensions_.ext_shader_stencil_export ? "yes" : "no");
|
||||
XELOGVK("* VK_KHR_bind_memory2: {}",
|
||||
|
|
|
@ -133,6 +133,8 @@ class VulkanProvider : public GraphicsProvider {
|
|||
struct DeviceExtensions {
|
||||
bool ext_fragment_shader_interlock;
|
||||
bool ext_memory_budget;
|
||||
// Core since 1.3.0.
|
||||
bool ext_shader_demote_to_helper_invocation;
|
||||
bool ext_shader_stencil_export;
|
||||
// Core since 1.1.0.
|
||||
bool khr_bind_memory2;
|
||||
|
@ -198,6 +200,14 @@ class VulkanProvider : public GraphicsProvider {
|
|||
device_float_controls_properties() const {
|
||||
return device_float_controls_properties_;
|
||||
}
|
||||
const VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT&
|
||||
device_fragment_shader_interlock_features() const {
|
||||
return device_fragment_shader_interlock_features_;
|
||||
}
|
||||
const VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT&
|
||||
device_shader_demote_to_helper_invocation_features() const {
|
||||
return device_shader_demote_to_helper_invocation_features_;
|
||||
}
|
||||
|
||||
struct Queue {
|
||||
VkQueue queue = VK_NULL_HANDLE;
|
||||
|
@ -320,6 +330,10 @@ class VulkanProvider : public GraphicsProvider {
|
|||
uint32_t queue_family_graphics_compute_;
|
||||
uint32_t queue_family_sparse_binding_;
|
||||
VkPhysicalDeviceFloatControlsPropertiesKHR device_float_controls_properties_;
|
||||
VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT
|
||||
device_fragment_shader_interlock_features_;
|
||||
VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT
|
||||
device_shader_demote_to_helper_invocation_features_;
|
||||
|
||||
VkDevice device_ = VK_NULL_HANDLE;
|
||||
DeviceFunctions dfn_ = {};
|
||||
|
|
|
@ -191,9 +191,10 @@
|
|||
this.translationComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.translationComboBox.FormattingEnabled = true;
|
||||
this.translationComboBox.Items.AddRange(new object[] {
|
||||
"DXBC (RTV/DSV RB)",
|
||||
"DXBC (ROV RB)",
|
||||
"SPIR-V"});
|
||||
"DXBC (render target RB)",
|
||||
"DXBC (rasterizer-ordered view RB)",
|
||||
"SPIR-V (framebuffer RB)",
|
||||
"SPIR-V (fragment shader interlock RB)"});
|
||||
this.translationComboBox.Location = new System.Drawing.Point(1224, 0);
|
||||
this.translationComboBox.Margin = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.translationComboBox.Name = "translationComboBox";
|
||||
|
|
|
@ -235,6 +235,7 @@ namespace shader_playground {
|
|||
outputType = "dxbctext";
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
outputType = "spirvtext";
|
||||
break;
|
||||
}
|
||||
|
@ -269,8 +270,9 @@ namespace shader_playground {
|
|||
"--vertex_shader_output_type=" + vertexShaderType,
|
||||
"--dxbc_source_map=true",
|
||||
};
|
||||
if (translationComboBox.SelectedIndex == 1) {
|
||||
startArguments.Add("--shader_output_dxbc_rov=true");
|
||||
if (translationComboBox.SelectedIndex == 1 ||
|
||||
translationComboBox.SelectedIndex == 3) {
|
||||
startArguments.Add("--shader_output_pixel_shader_interlock=true");
|
||||
}
|
||||
|
||||
startInfo = new ProcessStartInfo(compilerPath_);
|
||||
|
|
Loading…
Reference in New Issue