[Vulkan] Implement vertex kill (oPts.z) in the translator
A non-zero value in bits 0:30 of the kill flag kills the vertex, tested on the integer bits rather than as a float comparison so denormal flushing can't affect the result, matching the DXBC translator. With PA_CL_CLIP_CNTL::vtx_kill_or, the position W is set to NaN, killing the whole primitive if any of its vertices requests the kill, with the "and" operator, a dedicated cull distance after the user clip plane cull distances is set to -1, culling the primitive only when all of its vertices request it. Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
This commit is contained in:
committed by
Radosław Gliński
parent
3254ac20f8
commit
090cecd1b8
@@ -1363,6 +1363,11 @@ void SpirvShaderTranslator::StartVertexOrTessEvalShaderBeforeMain() {
|
||||
} else {
|
||||
clip_distance_count = user_clip_plane_count;
|
||||
}
|
||||
// Vertex kill with "and" operator writes a dedicated cull distance after
|
||||
// the user clip plane cull distances.
|
||||
if (shader_modification.vertex.vertex_kill_and) {
|
||||
++cull_distance_count;
|
||||
}
|
||||
output_per_vertex_clip_distance_member_index_ = 0;
|
||||
output_per_vertex_cull_distance_member_index_ = 0;
|
||||
if (user_clip_plane_count > 0) {
|
||||
@@ -1998,6 +2003,58 @@ void SpirvShaderTranslator::CompleteVertexOrTessEvalShaderInMain() {
|
||||
position_xyz = builder_->createNoContractionBinOp(
|
||||
spv::OpFAdd, type_float3_, position_xyz, ndc_offset_mul_w);
|
||||
|
||||
// Apply vertex killing requested via the kill flag (oPts.z) - bits 0:30 of
|
||||
// the value being non-zero kills. Done after the NDC transform since the kill
|
||||
// cull distance is just a flag and the position is about to be written.
|
||||
if (current_shader().writes_point_size_edge_flag_kill_vertex() & 0b100) {
|
||||
assert_true(var_main_point_size_edge_flag_kill_vertex_ != spv::NoResult);
|
||||
id_vector_temp_.clear();
|
||||
// Z vector component.
|
||||
id_vector_temp_.push_back(builder_->makeIntConstant(2));
|
||||
spv::Id kill_value = builder_->createLoad(
|
||||
builder_->createAccessChain(spv::StorageClassFunction,
|
||||
var_main_point_size_edge_flag_kill_vertex_,
|
||||
id_vector_temp_),
|
||||
spv::NoPrecision);
|
||||
// Test the integer bits 0:30 rather than comparing the float to avoid
|
||||
// denormal flushing affecting the result (matching the Direct3D 12 path).
|
||||
spv::Id vertex_killed = builder_->createBinOp(
|
||||
spv::OpINotEqual, type_bool_,
|
||||
builder_->createBinOp(
|
||||
spv::OpBitwiseAnd, type_uint_,
|
||||
builder_->createUnaryOp(spv::OpBitcast, type_uint_, kill_value),
|
||||
builder_->makeUintConstant(UINT32_C(0x7FFFFFFF))),
|
||||
const_uint_0_);
|
||||
if (shader_modification.vertex.vertex_kill_and) {
|
||||
// "and" operator - write -1 to the dedicated cull distance when killed
|
||||
// (the primitive is culled only if it's negative for all the vertices).
|
||||
uint32_t vertex_kill_cull_distance_index =
|
||||
shader_modification.vertex.user_clip_plane_cull
|
||||
? user_clip_plane_count
|
||||
: 0;
|
||||
id_vector_temp_.clear();
|
||||
id_vector_temp_.push_back(builder_->makeIntConstant(
|
||||
int(output_per_vertex_cull_distance_member_index_)));
|
||||
id_vector_temp_.push_back(
|
||||
builder_->makeIntConstant(int(vertex_kill_cull_distance_index)));
|
||||
builder_->createStore(
|
||||
builder_->createTriOp(spv::OpSelect, type_float_, vertex_killed,
|
||||
builder_->makeFloatConstant(-1.0f),
|
||||
const_float_0_),
|
||||
builder_->createAccessChain(spv::StorageClassOutput,
|
||||
output_per_vertex_, id_vector_temp_));
|
||||
} else {
|
||||
// "or" operator - setting the position W to NaN kills the whole primitive
|
||||
// if any of its vertices requests the kill.
|
||||
position_w = builder_->createTriOp(
|
||||
spv::OpSelect, type_float_, vertex_killed,
|
||||
builder_->createUnaryOp(
|
||||
spv::OpBitcast, type_float_,
|
||||
builder_->makeUintConstant(UINT32_C(0x7FC00000))),
|
||||
position_w);
|
||||
}
|
||||
}
|
||||
|
||||
// Write the point size.
|
||||
if (output_point_size_ != spv::NoResult) {
|
||||
spv::Id point_size;
|
||||
|
||||
@@ -34,7 +34,7 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
||||
// TODO(Triang3l): Change to 0xYYYYMMDD once it's out of the rapid
|
||||
// prototyping stage (easier to do small granular updates with an
|
||||
// incremental counter).
|
||||
static constexpr uint32_t kVersion = 15;
|
||||
static constexpr uint32_t kVersion = 16;
|
||||
|
||||
enum class DepthStencilMode : uint32_t {
|
||||
kNoModifiers,
|
||||
@@ -81,6 +81,11 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
||||
// If user_clip_plane_count is non-zero, whether they should be cull
|
||||
// distances instead of clip distances.
|
||||
uint32_t user_clip_plane_cull : 1;
|
||||
// Vertex kill (oPts.z) with the "and" operator - the primitive is culled
|
||||
// only when all of its vertices request the kill, emulated with an extra
|
||||
// cull distance written after the user clip plane cull distances. The
|
||||
// "or" operator sets the position to NaN instead and needs no bit here.
|
||||
uint32_t vertex_kill_and : 1;
|
||||
} vertex;
|
||||
struct PixelShaderModification {
|
||||
// uint32_t 0.
|
||||
|
||||
@@ -410,6 +410,13 @@ VulkanPipelineCache::GetCurrentVertexShaderModification(
|
||||
modification.vertex.user_clip_plane_cull =
|
||||
uint32_t(user_clip_planes && pa_cl_clip_cntl.ucp_cull_only_ena);
|
||||
|
||||
// Vertex kill via the kill flag (oPts.z). The "and" operator (kill only when
|
||||
// all vertices of the primitive request it) is emulated with a cull distance;
|
||||
// the "or" operator sets the position to NaN in the translator.
|
||||
modification.vertex.vertex_kill_and =
|
||||
uint32_t((shader.writes_point_size_edge_flag_kill_vertex() & 0b100) &&
|
||||
!pa_cl_clip_cntl.vtx_kill_or);
|
||||
|
||||
if (host_vertex_shader_type ==
|
||||
Shader::HostVertexShaderType::kPointListAsTriangleStrip) {
|
||||
modification.vertex.output_point_parameters = uint32_t(ps_param_gen_used);
|
||||
@@ -1469,12 +1476,9 @@ bool VulkanPipelineCache::GetGeometryShaderKey(
|
||||
}
|
||||
GeometryShaderKey key;
|
||||
key.type = geometry_shader_type;
|
||||
// TODO(Triang3l): Once all needed inputs and outputs are added, uncomment the
|
||||
// real counts here.
|
||||
key.interpolator_count =
|
||||
xe::bit_count(vertex_shader_modification.vertex.interpolator_mask);
|
||||
key.has_vertex_kill_and =
|
||||
/* vertex_shader_modification.vertex.vertex_kill_and */ 0;
|
||||
key.has_vertex_kill_and = vertex_shader_modification.vertex.vertex_kill_and;
|
||||
key.has_point_size =
|
||||
vertex_shader_modification.vertex.output_point_parameters;
|
||||
key.has_point_coordinates = pixel_shader_modification.pixel.param_gen_point;
|
||||
|
||||
Reference in New Issue
Block a user