[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 "xenia/gpu/vulkan/vulkan_pipeline_cache.h"
#include <algorithm>
#include <cstring> #include <cstring>
#include <memory> #include <memory>
@ -43,6 +44,7 @@ bool VulkanPipelineCache::Initialize() {
device_pipeline_features_.features = 0; device_pipeline_features_.features = 0;
// TODO(Triang3l): Support the portability subset. // TODO(Triang3l): Support the portability subset.
device_pipeline_features_.point_polygons = 1;
device_pipeline_features_.triangle_fans = 1; device_pipeline_features_.triangle_fans = 1;
shader_translator_ = std::make_unique<SpirvShaderTranslator>( shader_translator_ = std::make_unique<SpirvShaderTranslator>(
@ -210,15 +212,21 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
description_out.Reset(); description_out.Reset();
const RegisterFile& regs = register_file_; 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.vertex_shader_hash = vertex_shader->ucode_data_hash();
description_out.pixel_shader_hash = description_out.pixel_shader_hash =
pixel_shader ? pixel_shader->ucode_data_hash() : 0; pixel_shader ? pixel_shader->ucode_data_hash() : 0;
description_out.render_pass_key = render_pass_key; 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; 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: case xenos::PrimitiveType::kPointList:
primitive_topology = PipelinePrimitiveTopology::kPointList; primitive_topology = PipelinePrimitiveTopology::kPointList;
break; break;
@ -227,24 +235,76 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
break; break;
case xenos::PrimitiveType::kLineStrip: case xenos::PrimitiveType::kLineStrip:
primitive_topology = PipelinePrimitiveTopology::kLineStrip; primitive_topology = PipelinePrimitiveTopology::kLineStrip;
primitive_restart_allowed = true;
break; break;
case xenos::PrimitiveType::kTriangleList: case xenos::PrimitiveType::kTriangleList:
case xenos::PrimitiveType::kRectangleList:
primitive_topology = PipelinePrimitiveTopology::kTriangleList; primitive_topology = PipelinePrimitiveTopology::kTriangleList;
break; break;
case xenos::PrimitiveType::kTriangleFan: case xenos::PrimitiveType::kTriangleFan:
primitive_topology = device_pipeline_features_.triangle_fans if (device_pipeline_features_.triangle_fans) {
? PipelinePrimitiveTopology::kTriangleFan primitive_topology = PipelinePrimitiveTopology::kTriangleFan;
: PipelinePrimitiveTopology::kTriangleList; primitive_restart_allowed = true;
} else {
primitive_topology = PipelinePrimitiveTopology::kTriangleList;
}
break; break;
case xenos::PrimitiveType::kTriangleStrip: case xenos::PrimitiveType::kTriangleStrip:
primitive_topology = PipelinePrimitiveTopology::kTriangleStrip; primitive_topology = PipelinePrimitiveTopology::kTriangleStrip;
primitive_restart_allowed = true;
break; break;
default: default:
// TODO(Triang3l): All primitive types and tessellation. // TODO(Triang3l): All primitive types and tessellation.
return false; return false;
} }
description_out.primitive_topology = primitive_topology; 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; return true;
} }
@ -374,6 +434,34 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
VkPipelineRasterizationStateCreateInfo rasterization_state = {}; VkPipelineRasterizationStateCreateInfo rasterization_state = {};
rasterization_state.sType = rasterization_state.sType =
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; 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; rasterization_state.lineWidth = 1.0f;
VkPipelineMultisampleStateCreateInfo multisample_state = {}; VkPipelineMultisampleStateCreateInfo multisample_state = {};

View File

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