[Vulkan] Faceness-related rasterization state

This commit is contained in:
Triang3l 2020-11-20 23:47:19 +03:00
parent 8b4a52aeed
commit 6584a2421c
2 changed files with 108 additions and 8 deletions

View File

@ -9,6 +9,7 @@
#include "xenia/gpu/vulkan/vulkan_pipeline_cache.h"
#include <algorithm>
#include <cstring>
#include <memory>
@ -43,6 +44,7 @@ bool VulkanPipelineCache::Initialize() {
device_pipeline_features_.features = 0;
// TODO(Triang3l): Support the portability subset.
device_pipeline_features_.point_polygons = 1;
device_pipeline_features_.triangle_fans = 1;
shader_translator_ = std::make_unique<SpirvShaderTranslator>(
@ -210,15 +212,21 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
description_out.Reset();
const RegisterFile& regs = register_file_;
auto pa_su_sc_mode_cntl = regs.Get<reg::PA_SU_SC_MODE_CNTL>();
auto vgt_draw_initiator = regs.Get<reg::VGT_DRAW_INITIATOR>();
description_out.vertex_shader_hash = vertex_shader->ucode_data_hash();
description_out.pixel_shader_hash =
pixel_shader ? pixel_shader->ucode_data_hash() : 0;
description_out.render_pass_key = render_pass_key;
auto vgt_draw_initiator = regs.Get<reg::VGT_DRAW_INITIATOR>();
xenos::PrimitiveType primitive_type = vgt_draw_initiator.prim_type;
PipelinePrimitiveTopology primitive_topology;
switch (vgt_draw_initiator.prim_type) {
// Vulkan explicitly allows primitive restart only for specific primitive
// types, unlike Direct3D where it's valid for non-strips, but has
// implementation-defined behavior.
bool primitive_restart_allowed = false;
switch (primitive_type) {
case xenos::PrimitiveType::kPointList:
primitive_topology = PipelinePrimitiveTopology::kPointList;
break;
@ -227,24 +235,76 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
break;
case xenos::PrimitiveType::kLineStrip:
primitive_topology = PipelinePrimitiveTopology::kLineStrip;
primitive_restart_allowed = true;
break;
case xenos::PrimitiveType::kTriangleList:
case xenos::PrimitiveType::kRectangleList:
primitive_topology = PipelinePrimitiveTopology::kTriangleList;
break;
case xenos::PrimitiveType::kTriangleFan:
primitive_topology = device_pipeline_features_.triangle_fans
? PipelinePrimitiveTopology::kTriangleFan
: PipelinePrimitiveTopology::kTriangleList;
if (device_pipeline_features_.triangle_fans) {
primitive_topology = PipelinePrimitiveTopology::kTriangleFan;
primitive_restart_allowed = true;
} else {
primitive_topology = PipelinePrimitiveTopology::kTriangleList;
}
break;
case xenos::PrimitiveType::kTriangleStrip:
primitive_topology = PipelinePrimitiveTopology::kTriangleStrip;
primitive_restart_allowed = true;
break;
default:
// TODO(Triang3l): All primitive types and tessellation.
return false;
}
description_out.primitive_topology = primitive_topology;
// TODO(Triang3l): Primitive restart.
description_out.primitive_restart =
primitive_restart_allowed && pa_su_sc_mode_cntl.multi_prim_ib_ena;
// TODO(Triang3l): Tessellation.
bool primitive_polygonal = xenos::IsPrimitivePolygonal(false, primitive_type);
if (primitive_polygonal) {
// Vulkan only allows the polygon mode to be set for both faces - pick the
// most special one (more likely to represent the developer's deliberate
// intentions - fill is very generic, wireframe is common in debug, points
// are for pretty unusual things, but closer to debug purposes too - on the
// Xenos, points have the lowest register value and triangles have the
// highest) based on which faces are not culled.
bool cull_front = pa_su_sc_mode_cntl.cull_front;
bool cull_back = pa_su_sc_mode_cntl.cull_back;
description_out.cull_front = cull_front;
description_out.cull_back = cull_back;
xenos::PolygonType polygon_type = xenos::PolygonType::kTriangles;
if (!cull_front) {
polygon_type =
std::min(polygon_type, pa_su_sc_mode_cntl.polymode_front_ptype);
}
if (!cull_back) {
polygon_type =
std::min(polygon_type, pa_su_sc_mode_cntl.polymode_back_ptype);
}
switch (polygon_type) {
case xenos::PolygonType::kPoints:
// When points are not supported, use lines instead, preserving
// debug-like purpose.
description_out.polygon_mode = device_pipeline_features_.point_polygons
? PipelinePolygonMode::kPoint
: PipelinePolygonMode::kLine;
break;
case xenos::PolygonType::kLines:
description_out.polygon_mode = PipelinePolygonMode::kLine;
break;
case xenos::PolygonType::kTriangles:
description_out.polygon_mode = PipelinePolygonMode::kFill;
break;
default:
assert_unhandled_case(polygon_type);
return false;
}
description_out.front_face_clockwise = pa_su_sc_mode_cntl.face != 0;
} else {
description_out.polygon_mode = PipelinePolygonMode::kFill;
}
return true;
}
@ -374,6 +434,34 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
VkPipelineRasterizationStateCreateInfo rasterization_state = {};
rasterization_state.sType =
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
switch (description.polygon_mode) {
case PipelinePolygonMode::kFill:
rasterization_state.polygonMode = VK_POLYGON_MODE_FILL;
break;
case PipelinePolygonMode::kLine:
rasterization_state.polygonMode = VK_POLYGON_MODE_LINE;
break;
case PipelinePolygonMode::kPoint:
assert_true(device_pipeline_features_.point_polygons);
if (!device_pipeline_features_.point_polygons) {
return false;
}
rasterization_state.polygonMode = VK_POLYGON_MODE_POINT;
break;
default:
assert_unhandled_case(description.polygon_mode);
return false;
}
rasterization_state.cullMode = VK_CULL_MODE_NONE;
if (description.cull_front) {
rasterization_state.cullMode |= VK_CULL_MODE_FRONT_BIT;
}
if (description.cull_back) {
rasterization_state.cullMode |= VK_CULL_MODE_BACK_BIT;
}
rasterization_state.frontFace = description.front_face_clockwise
? VK_FRONT_FACE_CLOCKWISE
: VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterization_state.lineWidth = 1.0f;
VkPipelineMultisampleStateCreateInfo multisample_state = {};

View File

@ -74,6 +74,7 @@ class VulkanPipelineCache {
// method) should result in a different storage file being used.
union DevicePipelineFeatures {
struct {
uint32_t point_polygons : 1;
uint32_t triangle_fans : 1;
};
uint32_t features = 0;
@ -91,6 +92,12 @@ class VulkanPipelineCache {
kPatchList,
};
enum class PipelinePolygonMode : uint32_t {
kFill,
kLine,
kPoint,
};
XEPACKEDSTRUCT(PipelineDescription, {
uint64_t vertex_shader_hash;
// 0 if no pixel shader.
@ -98,8 +105,13 @@ class VulkanPipelineCache {
VulkanRenderTargetCache::RenderPassKey render_pass_key;
// Input assembly.
PipelinePrimitiveTopology primitive_topology : 3;
uint32_t primitive_restart : 1;
PipelinePrimitiveTopology primitive_topology : 3; // 3
uint32_t primitive_restart : 1; // 4
// Rasterization.
PipelinePolygonMode polygon_mode : 2; // 6
uint32_t cull_front : 1; // 7
uint32_t cull_back : 1; // 8
uint32_t front_face_clockwise : 1; // 9
// Including all the padding, for a stable hash.
PipelineDescription() { Reset(); }