[Vulkan] Color exponent bias and gamma conversion

This commit is contained in:
Triang3l 2022-06-25 20:35:13 +03:00
parent b1be33004a
commit d30d59883a
6 changed files with 180 additions and 13 deletions

View File

@ -228,6 +228,8 @@ void SpirvShaderTranslator::StartTranslation() {
offsetof(SystemConstants, texture_swizzled_signs), type_uint4_array_2}, offsetof(SystemConstants, texture_swizzled_signs), type_uint4_array_2},
{"texture_swizzles", offsetof(SystemConstants, texture_swizzles), {"texture_swizzles", offsetof(SystemConstants, texture_swizzles),
type_uint4_array_4}, type_uint4_array_4},
{"color_exp_bias", offsetof(SystemConstants, color_exp_bias),
type_float4_},
}; };
id_vector_temp_.clear(); id_vector_temp_.clear();
id_vector_temp_.reserve(xe::countof(system_constants)); id_vector_temp_.reserve(xe::countof(system_constants));
@ -403,6 +405,14 @@ void SpirvShaderTranslator::StartTranslation() {
spv::NoPrecision, type_void_, "main", main_param_types, main_precisions, spv::NoPrecision, type_void_, "main", main_param_types, main_precisions,
&function_main_entry); &function_main_entry);
// Load the flags system constant since it may be used in many places.
id_vector_temp_.clear();
id_vector_temp_.push_back(builder_->makeIntConstant(kSystemConstantFlags));
main_system_constant_flags_ = builder_->createLoad(
builder_->createAccessChain(spv::StorageClassUniform,
uniform_system_constants_, id_vector_temp_),
spv::NoPrecision);
// Begin ucode translation. Initialize everything, even without defined // Begin ucode translation. Initialize everything, even without defined
// defaults, for safety. // defaults, for safety.
var_main_predicate_ = builder_->createVariable( var_main_predicate_ = builder_->createVariable(
@ -580,6 +590,8 @@ std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
if (is_vertex_shader()) { if (is_vertex_shader()) {
CompleteVertexOrTessEvalShaderInMain(); CompleteVertexOrTessEvalShaderInMain();
} else if (is_pixel_shader()) {
CompleteFragmentShaderInMain();
} }
// End the main function. // End the main function.
@ -1115,13 +1127,6 @@ void SpirvShaderTranslator::StartVertexOrTessEvalShaderInMain() {
} }
void SpirvShaderTranslator::CompleteVertexOrTessEvalShaderInMain() { void SpirvShaderTranslator::CompleteVertexOrTessEvalShaderInMain() {
id_vector_temp_.clear();
id_vector_temp_.push_back(builder_->makeIntConstant(kSystemConstantFlags));
spv::Id system_constant_flags = builder_->createLoad(
builder_->createAccessChain(spv::StorageClassUniform,
uniform_system_constants_, id_vector_temp_),
spv::NoPrecision);
id_vector_temp_.clear(); id_vector_temp_.clear();
id_vector_temp_.push_back( id_vector_temp_.push_back(
builder_->makeIntConstant(kOutputPerVertexMemberPosition)); builder_->makeIntConstant(kOutputPerVertexMemberPosition));
@ -1136,7 +1141,7 @@ void SpirvShaderTranslator::CompleteVertexOrTessEvalShaderInMain() {
spv::Id is_w_not_reciprocal = builder_->createBinOp( spv::Id is_w_not_reciprocal = builder_->createBinOp(
spv::OpINotEqual, type_bool_, spv::OpINotEqual, type_bool_,
builder_->createBinOp( builder_->createBinOp(
spv::OpBitwiseAnd, type_uint_, system_constant_flags, spv::OpBitwiseAnd, type_uint_, main_system_constant_flags_,
builder_->makeUintConstant( builder_->makeUintConstant(
static_cast<unsigned int>(kSysFlag_WNotReciprocal))), static_cast<unsigned int>(kSysFlag_WNotReciprocal))),
const_uint_0_); const_uint_0_);
@ -1160,7 +1165,7 @@ void SpirvShaderTranslator::CompleteVertexOrTessEvalShaderInMain() {
spv::Id is_xy_divided_by_w = builder_->createBinOp( spv::Id is_xy_divided_by_w = builder_->createBinOp(
spv::OpINotEqual, type_bool_, spv::OpINotEqual, type_bool_,
builder_->createBinOp( builder_->createBinOp(
spv::OpBitwiseAnd, type_uint_, system_constant_flags, spv::OpBitwiseAnd, type_uint_, main_system_constant_flags_,
builder_->makeUintConstant( builder_->makeUintConstant(
static_cast<unsigned int>(kSysFlag_XYDividedByW))), static_cast<unsigned int>(kSysFlag_XYDividedByW))),
const_uint_0_); const_uint_0_);
@ -1180,7 +1185,7 @@ void SpirvShaderTranslator::CompleteVertexOrTessEvalShaderInMain() {
spv::Id is_z_divided_by_w = builder_->createBinOp( spv::Id is_z_divided_by_w = builder_->createBinOp(
spv::OpINotEqual, type_bool_, spv::OpINotEqual, type_bool_,
builder_->createBinOp( builder_->createBinOp(
spv::OpBitwiseAnd, type_uint_, system_constant_flags, spv::OpBitwiseAnd, type_uint_, main_system_constant_flags_,
builder_->makeUintConstant( builder_->makeUintConstant(
static_cast<unsigned int>(kSysFlag_ZDividedByW))), static_cast<unsigned int>(kSysFlag_ZDividedByW))),
const_uint_0_); const_uint_0_);

View File

@ -61,12 +61,20 @@ class SpirvShaderTranslator : public ShaderTranslator {
kSysFlag_XYDividedByW_Shift, kSysFlag_XYDividedByW_Shift,
kSysFlag_ZDividedByW_Shift, kSysFlag_ZDividedByW_Shift,
kSysFlag_WNotReciprocal_Shift, kSysFlag_WNotReciprocal_Shift,
kSysFlag_ConvertColor0ToGamma_Shift,
kSysFlag_ConvertColor1ToGamma_Shift,
kSysFlag_ConvertColor2ToGamma_Shift,
kSysFlag_ConvertColor3ToGamma_Shift,
kSysFlag_Count, kSysFlag_Count,
kSysFlag_XYDividedByW = 1u << kSysFlag_XYDividedByW_Shift, kSysFlag_XYDividedByW = 1u << kSysFlag_XYDividedByW_Shift,
kSysFlag_ZDividedByW = 1u << kSysFlag_ZDividedByW_Shift, kSysFlag_ZDividedByW = 1u << kSysFlag_ZDividedByW_Shift,
kSysFlag_WNotReciprocal = 1u << kSysFlag_WNotReciprocal_Shift, kSysFlag_WNotReciprocal = 1u << kSysFlag_WNotReciprocal_Shift,
kSysFlag_ConvertColor0ToGamma = 1u << kSysFlag_ConvertColor0ToGamma_Shift,
kSysFlag_ConvertColor1ToGamma = 1u << kSysFlag_ConvertColor1ToGamma_Shift,
kSysFlag_ConvertColor2ToGamma = 1u << kSysFlag_ConvertColor2ToGamma_Shift,
kSysFlag_ConvertColor3ToGamma = 1u << kSysFlag_ConvertColor3ToGamma_Shift,
}; };
static_assert(kSysFlag_Count <= 32, "Too many flags in the system constants"); static_assert(kSysFlag_Count <= 32, "Too many flags in the system constants");
@ -94,6 +102,8 @@ class SpirvShaderTranslator : public ShaderTranslator {
// apply to the result directly in the shader code. In each uint32_t, // apply to the result directly in the shader code. In each uint32_t,
// swizzles for 2 texture fetch constants (in bits 0:11 and 12:23). // swizzles for 2 texture fetch constants (in bits 0:11 and 12:23).
uint32_t texture_swizzles[16]; uint32_t texture_swizzles[16];
float color_exp_bias[4];
}; };
// The minimum limit for maxPerStageDescriptorStorageBuffers is 4, and for // The minimum limit for maxPerStageDescriptorStorageBuffers is 4, and for
@ -308,6 +318,7 @@ class SpirvShaderTranslator : public ShaderTranslator {
void StartFragmentShaderBeforeMain(); void StartFragmentShaderBeforeMain();
void StartFragmentShaderInMain(); void StartFragmentShaderInMain();
void CompleteFragmentShaderInMain();
// Updates the current flow control condition (to be called in the beginning // Updates the current flow control condition (to be called in the beginning
// of exec and in jumps), closing the previous conditionals if needed. // of exec and in jumps), closing the previous conditionals if needed.
@ -509,6 +520,7 @@ class SpirvShaderTranslator : public ShaderTranslator {
kSystemConstantNdcOffset, kSystemConstantNdcOffset,
kSystemConstantTextureSwizzledSigns, kSystemConstantTextureSwizzledSigns,
kSystemConstantTextureSwizzles, kSystemConstantTextureSwizzles,
kSystemConstantColorExpBias,
}; };
spv::Id uniform_system_constants_; spv::Id uniform_system_constants_;
spv::Id uniform_float_constants_; spv::Id uniform_float_constants_;
@ -545,6 +557,7 @@ class SpirvShaderTranslator : public ShaderTranslator {
std::vector<spv::Id> main_interface_; std::vector<spv::Id> main_interface_;
spv::Function* function_main_; spv::Function* function_main_;
spv::Id main_system_constant_flags_;
// bool. // bool.
spv::Id var_main_predicate_; spv::Id var_main_predicate_;
// uint4. // uint4.

View File

@ -11,9 +11,11 @@
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include <utility>
#include "third_party/glslang/SPIRV/GLSL.std.450.h" #include "third_party/glslang/SPIRV/GLSL.std.450.h"
#include "xenia/base/assert.h" #include "xenia/base/assert.h"
#include "xenia/base/math.h"
namespace xe { namespace xe {
namespace gpu { namespace gpu {
@ -423,5 +425,101 @@ spv::Id SpirvShaderTranslator::Depth20e4To32(spv::Builder& builder,
return f32; return f32;
} }
void SpirvShaderTranslator::CompleteFragmentShaderInMain() {
id_vector_temp_.clear();
id_vector_temp_.push_back(builder_->makeIntConstant(kSystemConstantFlags));
spv::Id system_constant_flags = builder_->createLoad(
builder_->createAccessChain(spv::StorageClassUniform,
uniform_system_constants_, id_vector_temp_),
spv::NoPrecision);
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)) {
color_targets_remaining &= ~(UINT32_C(1) << color_target_index);
spv::Id color_variable = output_fragment_data_[color_target_index];
spv::Id color = builder_->createLoad(color_variable, spv::NoPrecision);
// Apply the exponent bias after the alpha test and alpha to coverage
// because they need the unbiased alpha from the shader.
id_vector_temp_.clear();
id_vector_temp_.reserve(2);
id_vector_temp_.push_back(
builder_->makeIntConstant(kSystemConstantColorExpBias));
id_vector_temp_.push_back(
builder_->makeIntConstant(int32_t(color_target_index)));
color = builder_->createBinOp(
spv::OpVectorTimesScalar, type_float4_, color,
builder_->createLoad(builder_->createAccessChain(
spv::StorageClassUniform,
uniform_system_constants_, id_vector_temp_),
spv::NoPrecision));
builder_->addDecoration(color, spv::DecorationNoContraction);
// Convert to gamma space - this is incorrect, since it must be done after
// blending on the Xbox 360, but this is just one of many blending issues in
// the host render target path.
// TODO(Triang3l): Gamma as sRGB check.
spv::Id color_rgb;
{
std::unique_ptr<spv::Instruction> color_rgb_shuffle_op =
std::make_unique<spv::Instruction>(
builder_->getUniqueId(), type_float3_, spv::OpVectorShuffle);
color_rgb_shuffle_op->addIdOperand(color);
color_rgb_shuffle_op->addIdOperand(color);
color_rgb_shuffle_op->addImmediateOperand(0);
color_rgb_shuffle_op->addImmediateOperand(1);
color_rgb_shuffle_op->addImmediateOperand(2);
color_rgb = color_rgb_shuffle_op->getResultId();
builder_->getBuildPoint()->addInstruction(
std::move(color_rgb_shuffle_op));
}
spv::Id is_gamma = builder_->createBinOp(
spv::OpINotEqual, type_bool_,
builder_->createBinOp(
spv::OpBitwiseAnd, type_uint_, main_system_constant_flags_,
builder_->makeUintConstant(kSysFlag_ConvertColor0ToGamma
<< color_target_index)),
const_uint_0_);
spv::Block& block_gamma_head = *builder_->getBuildPoint();
spv::Block& block_gamma = builder_->makeNewBlock();
spv::Block& block_gamma_merge = builder_->makeNewBlock();
SpirvCreateSelectionMerge(block_gamma_merge.getId());
builder_->createConditionalBranch(is_gamma, &block_gamma,
&block_gamma_merge);
builder_->setBuildPoint(&block_gamma);
spv::Id color_rgb_gamma = LinearToPWLGamma(color_rgb, false);
builder_->createBranch(&block_gamma_merge);
builder_->setBuildPoint(&block_gamma_merge);
{
std::unique_ptr<spv::Instruction> gamma_phi_op =
std::make_unique<spv::Instruction>(builder_->getUniqueId(),
type_float3_, spv::OpPhi);
gamma_phi_op->addIdOperand(color_rgb_gamma);
gamma_phi_op->addIdOperand(block_gamma.getId());
gamma_phi_op->addIdOperand(color_rgb);
gamma_phi_op->addIdOperand(block_gamma_head.getId());
color_rgb = gamma_phi_op->getResultId();
builder_->getBuildPoint()->addInstruction(std::move(gamma_phi_op));
}
{
std::unique_ptr<spv::Instruction> color_rgba_shuffle_op =
std::make_unique<spv::Instruction>(
builder_->getUniqueId(), type_float4_, spv::OpVectorShuffle);
color_rgba_shuffle_op->addIdOperand(color_rgb);
color_rgba_shuffle_op->addIdOperand(color);
color_rgba_shuffle_op->addImmediateOperand(0);
color_rgba_shuffle_op->addImmediateOperand(1);
color_rgba_shuffle_op->addImmediateOperand(2);
color_rgba_shuffle_op->addImmediateOperand(3 + 3);
color = color_rgba_shuffle_op->getResultId();
builder_->getBuildPoint()->addInstruction(
std::move(color_rgba_shuffle_op));
}
builder_->createStore(color, color_variable);
}
}
} // namespace gpu } // namespace gpu
} // namespace xe } // namespace xe

View File

@ -3267,6 +3267,13 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
auto pa_cl_vte_cntl = regs.Get<reg::PA_CL_VTE_CNTL>(); auto pa_cl_vte_cntl = regs.Get<reg::PA_CL_VTE_CNTL>();
int32_t vgt_indx_offset = int32_t(regs[XE_GPU_REG_VGT_INDX_OFFSET].u32); 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.
reg::RB_COLOR_INFO color_infos[xenos::kMaxColorRenderTargets];
for (uint32_t i = 0; i < xenos::kMaxColorRenderTargets; ++i) {
color_infos[i] = regs.Get<reg::RB_COLOR_INFO>(
reg::RB_COLOR_INFO::rt_register_indices[i]);
}
bool dirty = false; bool dirty = false;
// Flags. // Flags.
@ -3288,6 +3295,14 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
if (pa_cl_vte_cntl.vtx_w0_fmt) { if (pa_cl_vte_cntl.vtx_w0_fmt) {
flags |= SpirvShaderTranslator::kSysFlag_WNotReciprocal; flags |= SpirvShaderTranslator::kSysFlag_WNotReciprocal;
} }
// Gamma writing.
// TODO(Triang3l): Gamma as sRGB check.
for (uint32_t i = 0; i < xenos::kMaxColorRenderTargets; ++i) {
if (color_infos[i].color_format ==
xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA) {
flags |= SpirvShaderTranslator::kSysFlag_ConvertColor0ToGamma << i;
}
}
dirty |= system_constants_.flags != flags; dirty |= system_constants_.flags != flags;
system_constants_.flags = flags; system_constants_.flags = flags;
@ -3356,6 +3371,29 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
} }
} }
// Color exponent bias.
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.
int32_t color_exp_bias = color_info.color_exp_bias;
if (render_target_cache_->GetPath() ==
RenderTargetCache::Path::kHostRenderTargets &&
(color_info.color_format == xenos::ColorRenderTargetFormat::k_16_16 &&
!render_target_cache_->IsFixedRG16TruncatedToMinus1To1() ||
color_info.color_format ==
xenos::ColorRenderTargetFormat::k_16_16_16_16 &&
!render_target_cache_->IsFixedRGBA16TruncatedToMinus1To1())) {
// Remap from -32...32 to -1...1 by dividing the output values by 32,
// losing blending correctness, but getting the full range.
color_exp_bias -= 5;
}
float color_exp_bias_scale;
*reinterpret_cast<int32_t*>(&color_exp_bias_scale) =
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 (dirty) { if (dirty) {
current_graphics_descriptor_set_values_up_to_date_ &= current_graphics_descriptor_set_values_up_to_date_ &=
~(UINT32_C(1) << SpirvShaderTranslator::kDescriptorSetSystemConstants); ~(UINT32_C(1) << SpirvShaderTranslator::kDescriptorSetSystemConstants);

View File

@ -784,11 +784,10 @@ bool VulkanRenderTargetCache::Resolve(const Memory& memory,
bool draw_resolution_scaled = IsDrawResolutionScaled(); bool draw_resolution_scaled = IsDrawResolutionScaled();
draw_util::ResolveInfo resolve_info; draw_util::ResolveInfo resolve_info;
// TODO(Triang3l): Truncation of fixed16 (but not fixed16 as float16) range to
// -1 to 1.
if (!draw_util::GetResolveInfo( if (!draw_util::GetResolveInfo(
register_file(), memory, trace_writer_, draw_resolution_scale_x(), register_file(), memory, trace_writer_, draw_resolution_scale_x(),
draw_resolution_scale_y(), false, false, resolve_info)) { draw_resolution_scale_y(), IsFixedRG16TruncatedToMinus1To1(),
IsFixedRGBA16TruncatedToMinus1To1(), resolve_info)) {
return false; return false;
} }

View File

@ -128,6 +128,20 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
return last_update_framebuffer_; return last_update_framebuffer_;
} }
// Using R16G16[B16A16]_SNORM, which are -1...1, not the needed -32...32.
// Persistent data doesn't depend on this, so can be overriden by per-game
// configuration.
bool IsFixedRG16TruncatedToMinus1To1() const {
// TODO(Triang3l): Not float16 condition.
return GetPath() == Path::kHostRenderTargets &&
!cvars::snorm16_render_target_full_range;
}
bool IsFixedRGBA16TruncatedToMinus1To1() const {
// TODO(Triang3l): Not float16 condition.
return GetPath() == Path::kHostRenderTargets &&
!cvars::snorm16_render_target_full_range;
}
bool depth_float24_round() const { return depth_float24_round_; } bool depth_float24_round() const { return depth_float24_round_; }
bool msaa_2x_attachments_supported() const { bool msaa_2x_attachments_supported() const {