Implement non-solid polygon rasterization
Implements Vulkan's 'fillModeNonSolid' feature.
Mostly a copy-and-paste of src/Renderer logic. It sets the batch size
for non-solid triangles to 1 so their edges or vertices can get
individually rasterized as lines or points.
Bug: b/139872671
Tests: dEQP-VK.rasterization.culling.*
Change-Id: Ifbae8dad32e8b43159fee459c75d7e593fc08041
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/35031
Tested-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/Device/Context.cpp b/src/Device/Context.cpp
index f517e6b..6825d01 100644
--- a/src/Device/Context.cpp
+++ b/src/Device/Context.cpp
@@ -29,7 +29,7 @@
init();
}
- bool Context::isDrawPoint() const
+ bool Context::isDrawPoint(bool polygonModeAware) const
{
switch(topology)
{
@@ -37,46 +37,48 @@
return true;
case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
+ return false;
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
- break;
+ return polygonModeAware ? (polygonMode == VK_POLYGON_MODE_POINT) : false;
default:
UNIMPLEMENTED("topology %d", int(topology));
}
return false;
}
- bool Context::isDrawLine() const
+ bool Context::isDrawLine(bool polygonModeAware) const
{
switch(topology)
{
+ case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
+ return false;
case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
return true;
- case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
- break;
+ return polygonModeAware ? (polygonMode == VK_POLYGON_MODE_LINE) : false;
default:
UNIMPLEMENTED("topology %d", int(topology));
}
return false;
}
- bool Context::isDrawTriangle() const
+ bool Context::isDrawTriangle(bool polygonModeAware) const
{
switch(topology)
{
- case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
- case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
- case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
- return true;
case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
- break;
+ return false;
+ case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
+ case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
+ case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
+ return polygonModeAware ? (polygonMode == VK_POLYGON_MODE_FILL) : true;
default:
UNIMPLEMENTED("topology %d", int(topology));
}
diff --git a/src/Device/Context.hpp b/src/Device/Context.hpp
index c86d956..610ec88 100644
--- a/src/Device/Context.hpp
+++ b/src/Device/Context.hpp
@@ -74,9 +74,9 @@
void init();
- bool isDrawPoint() const;
- bool isDrawLine() const;
- bool isDrawTriangle() const;
+ bool isDrawPoint(bool polygonModeAware) const;
+ bool isDrawLine(bool polygonModeAware) const;
+ bool isDrawTriangle(bool polygonModeAware) const;
bool depthWriteActive() const;
bool depthBufferActive() const;
@@ -96,6 +96,7 @@
// Pixel processor states
VkCullModeFlags cullMode;
VkFrontFace frontFace;
+ VkPolygonMode polygonMode;
float depthBias;
float slopeDepthBias;
diff --git a/src/Device/Renderer.cpp b/src/Device/Renderer.cpp
index 3aa4c3e..debcc7c 100644
--- a/src/Device/Renderer.cpp
+++ b/src/Device/Renderer.cpp
@@ -206,16 +206,33 @@
}
DrawCall::SetupFunction setupPrimitives = nullptr;
+ unsigned int numPrimitivesPerBatch = MaxBatchSize / ms;
- if(context->isDrawTriangle())
+ if(context->isDrawTriangle(false))
{
- setupPrimitives = &DrawCall::setupTriangles;
+ switch(context->polygonMode)
+ {
+ case VK_POLYGON_MODE_FILL:
+ setupPrimitives = &DrawCall::setupSolidTriangles;
+ break;
+ case VK_POLYGON_MODE_LINE:
+ setupPrimitives = &DrawCall::setupWireframeTriangles;
+ numPrimitivesPerBatch = 1;
+ break;
+ case VK_POLYGON_MODE_POINT:
+ setupPrimitives = &DrawCall::setupPointTriangles;
+ numPrimitivesPerBatch = 1;
+ break;
+ default:
+ UNSUPPORTED("polygon mode: %d", int(context->polygonMode));
+ return;
+ }
}
- else if(context->isDrawLine())
+ else if(context->isDrawLine(false))
{
setupPrimitives = &DrawCall::setupLines;
}
- else // Point draw
+ else // Point primitive topology
{
setupPrimitives = &DrawCall::setupPoints;
}
@@ -224,7 +241,7 @@
draw->occlusionQuery = occlusionQuery;
draw->batchDataPool = &batchDataPool;
draw->numPrimitives = count;
- draw->numPrimitivesPerBatch = MaxBatchSize / ms;
+ draw->numPrimitivesPerBatch = numPrimitivesPerBatch;
draw->numBatches = (count + draw->numPrimitivesPerBatch - 1) / draw->numPrimitivesPerBatch;
draw->topology = context->topology;
draw->indexType = indexType;
@@ -297,7 +314,7 @@
float F = viewport.maxDepth;
float Z = F - N;
- if(context->isDrawTriangle())
+ if(context->isDrawTriangle(false))
{
N += context->depthBias;
}
@@ -567,7 +584,7 @@
triangleIndicesOut[triangleCount][2] = triangleIndicesOut[triangleCount - 1][2];
}
- int DrawCall::setupTriangles(Triangle *triangles, Primitive *primitives, const DrawCall *drawCall, int count)
+ int DrawCall::setupSolidTriangles(Triangle *triangles, Primitive *primitives, const DrawCall *drawCall, int count)
{
auto &state = drawCall->setupState;
auto setupRoutine = drawCall->setupPointer;
@@ -607,6 +624,94 @@
return visible;
}
+ int DrawCall::setupWireframeTriangles(Triangle *triangles, Primitive *primitives, const DrawCall *drawCall, int count)
+ {
+ auto& state = drawCall->setupState;
+
+ int ms = state.multiSample;
+ int visible = 0;
+
+ const Vertex &v0 = triangles[0].v0;
+ const Vertex &v1 = triangles[0].v1;
+ const Vertex &v2 = triangles[0].v2;
+
+ float d = (v0.position.y * v1.position.x - v0.position.x * v1.position.y) * v2.position.w +
+ (v0.position.x * v2.position.y - v0.position.y * v2.position.x) * v1.position.w +
+ (v2.position.x * v1.position.y - v1.position.x * v2.position.y) * v0.position.w;
+
+ bool frontFacing = (state.frontFace == VK_FRONT_FACE_COUNTER_CLOCKWISE) ? d > 0.0f : d < 0.0f;
+ if(state.cullMode & VK_CULL_MODE_FRONT_BIT)
+ {
+ if(frontFacing) return 0;
+ }
+ if(state.cullMode & VK_CULL_MODE_BACK_BIT)
+ {
+ if(!frontFacing) return 0;
+ }
+
+ // Copy attributes
+ triangles[1].v0 = v1;
+ triangles[1].v1 = v2;
+ triangles[2].v0 = v2;
+ triangles[2].v1 = v0;
+
+ for(int i = 0; i < 3; i++)
+ {
+ if(setupLine(*primitives, *triangles, *drawCall))
+ {
+ primitives += ms;
+ visible++;
+ }
+
+ triangles++;
+ }
+
+ return visible;
+ }
+
+ int DrawCall::setupPointTriangles(Triangle *triangles, Primitive *primitives, const DrawCall *drawCall, int count)
+ {
+ auto& state = drawCall->setupState;
+
+ int ms = state.multiSample;
+ int visible = 0;
+
+ const Vertex &v0 = triangles[0].v0;
+ const Vertex &v1 = triangles[0].v1;
+ const Vertex &v2 = triangles[0].v2;
+
+ float d = (v0.position.y * v1.position.x - v0.position.x * v1.position.y) * v2.position.w +
+ (v0.position.x * v2.position.y - v0.position.y * v2.position.x) * v1.position.w +
+ (v2.position.x * v1.position.y - v1.position.x * v2.position.y) * v0.position.w;
+
+ bool frontFacing = (state.frontFace == VK_FRONT_FACE_COUNTER_CLOCKWISE) ? d > 0.0f : d < 0.0f;
+ if(state.cullMode & VK_CULL_MODE_FRONT_BIT)
+ {
+ if(frontFacing) return 0;
+ }
+ if(state.cullMode & VK_CULL_MODE_BACK_BIT)
+ {
+ if(!frontFacing) return 0;
+ }
+
+ // Copy attributes
+ triangles[1].v0 = v1;
+ triangles[2].v0 = v2;
+
+ for(int i = 0; i < 3; i++)
+ {
+ if(setupPoint(*primitives, *triangles, *drawCall))
+ {
+ primitives += ms;
+ visible++;
+ }
+
+ triangles++;
+ }
+
+ return visible;
+ }
+
int DrawCall::setupLines(Triangle *triangles, Primitive *primitives, const DrawCall *drawCall, int count)
{
auto &state = drawCall->setupState;
diff --git a/src/Device/Renderer.hpp b/src/Device/Renderer.hpp
index b8a378b..4b96471 100644
--- a/src/Device/Renderer.hpp
+++ b/src/Device/Renderer.hpp
@@ -179,7 +179,9 @@
unsigned int triangleCount,
VkPrimitiveTopology topology);
- static int setupTriangles(Triangle *triangles, Primitive *primitives, const DrawCall *drawCall, int count);
+ static int setupSolidTriangles(Triangle* triangles, Primitive* primitives, const DrawCall* drawCall, int count);
+ static int setupWireframeTriangles(Triangle* triangles, Primitive* primitives, const DrawCall* drawCall, int count);
+ static int setupPointTriangles(Triangle* triangles, Primitive* primitives, const DrawCall* drawCall, int count);
static int setupLines(Triangle *triangles, Primitive *primitives, const DrawCall *drawCall, int count);
static int setupPoints(Triangle *triangles, Primitive *primitives, const DrawCall *drawCall, int count);
diff --git a/src/Device/SetupProcessor.cpp b/src/Device/SetupProcessor.cpp
index abf4c36..9984051 100644
--- a/src/Device/SetupProcessor.cpp
+++ b/src/Device/SetupProcessor.cpp
@@ -69,14 +69,14 @@
bool vPosZW = (context->pixelShader && context->pixelShader->hasBuiltinInput(spv::BuiltInFragCoord));
- state.isDrawPoint = context->isDrawPoint();
- state.isDrawLine = context->isDrawLine();
- state.isDrawTriangle = context->isDrawTriangle();
+ state.isDrawPoint = context->isDrawPoint(true);
+ state.isDrawLine = context->isDrawLine(true);
+ state.isDrawTriangle = context->isDrawTriangle(true);
+ state.applySlopeDepthBias = context->isDrawTriangle(false) && (context->slopeDepthBias != 0.0f);
state.interpolateZ = context->depthBufferActive() || vPosZW;
state.interpolateW = context->pixelShader != nullptr;
state.frontFace = context->frontFace;
state.cullMode = context->cullMode;
- state.slopeDepthBias = context->slopeDepthBias != 0.0f;
state.multiSample = context->sampleCount;
state.rasterizerDiscard = context->rasterizerDiscard;
diff --git a/src/Device/SetupProcessor.hpp b/src/Device/SetupProcessor.hpp
index 8a3374c..1c79710 100644
--- a/src/Device/SetupProcessor.hpp
+++ b/src/Device/SetupProcessor.hpp
@@ -42,11 +42,11 @@
bool isDrawPoint : 1;
bool isDrawLine : 1;
bool isDrawTriangle : 1;
+ bool applySlopeDepthBias : 1;
bool interpolateZ : 1;
bool interpolateW : 1;
VkFrontFace frontFace : BITS(VK_FRONT_FACE_MAX_ENUM);
VkCullModeFlags cullMode : BITS(VK_CULL_MODE_FLAG_BITS_MAX_ENUM);
- bool slopeDepthBias : 1;
unsigned int multiSample : 3; // 1, 2 or 4
bool rasterizerDiscard : 1;
diff --git a/src/Pipeline/SetupRoutine.cpp b/src/Pipeline/SetupRoutine.cpp
index 80ac5df..7740cc1 100644
--- a/src/Pipeline/SetupRoutine.cpp
+++ b/src/Pipeline/SetupRoutine.cpp
@@ -427,7 +427,7 @@
Float c = z0;
- if(state.isDrawTriangle && state.slopeDepthBias)
+ if(state.applySlopeDepthBias)
{
Float bias = Max(Abs(Float(A.x)), Abs(Float(B.x)));
bias *= *Pointer<Float>(data + OFFSET(DrawData,slopeDepthBias));
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index c86221c..4abb0f8 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -44,7 +44,7 @@
VK_TRUE, // drawIndirectFirstInstance
VK_FALSE, // depthClamp
VK_FALSE, // depthBiasClamp
- VK_FALSE, // fillModeNonSolid
+ VK_TRUE, // fillModeNonSolid
VK_FALSE, // depthBounds
VK_FALSE, // wideLines
VK_FALSE, // largePoints
diff --git a/src/Vulkan/VkPipeline.cpp b/src/Vulkan/VkPipeline.cpp
index 89e835d..0e131f1 100644
--- a/src/Vulkan/VkPipeline.cpp
+++ b/src/Vulkan/VkPipeline.cpp
@@ -382,8 +382,7 @@
const VkPipelineRasterizationStateCreateInfo* rasterizationState = pCreateInfo->pRasterizationState;
if((rasterizationState->flags != 0) ||
- (rasterizationState->depthClampEnable != VK_FALSE) ||
- (rasterizationState->polygonMode != VK_POLYGON_MODE_FILL))
+ (rasterizationState->depthClampEnable != VK_FALSE))
{
UNIMPLEMENTED("pCreateInfo->pRasterizationState settings");
}
@@ -391,6 +390,7 @@
context.rasterizerDiscard = (rasterizationState->rasterizerDiscardEnable == VK_TRUE);
context.cullMode = rasterizationState->cullMode;
context.frontFace = rasterizationState->frontFace;
+ context.polygonMode = rasterizationState->polygonMode;
context.depthBias = (rasterizationState->depthBiasEnable != VK_FALSE) ? rasterizationState->depthBiasConstantFactor : 0.0f;
context.slopeDepthBias = (rasterizationState->depthBiasEnable != VK_FALSE) ? rasterizationState->depthBiasSlopeFactor : 0.0f;