wg_engine: Fill Rule winding optimization

[issues 1479: Fill Rule](#1479)

In this solution we dont need to find silhouette, that is not a cheep operation (decreasing performance in 2 times)

For winding, you can select separate operations for front and back faces (increment for front, decrement for back)
After rendering the fan, the value in the stencil buffer will be the winding number. You can fill the appropriate portion by rendering a screen-sized quad with stencil testing enabled and the stencil function set according to which winding rule you wish to use.

For even-odd, you don't need to distinguish front and back faces; you can just use INVERT as the operation.
This commit is contained in:
Sergii Liebodkin 2024-05-22 16:20:00 +03:00 committed by Hermet Park
parent a66f3a1a65
commit 9c8b10603e
5 changed files with 87 additions and 32 deletions

View file

@ -550,7 +550,8 @@ void WgPipeline::destroyShaderModule(WGPUShaderModule& shaderModule)
void WgRenderPipeline::allocate(WGPUDevice device, WgPipelineBlendType blendType,
WGPUVertexBufferLayout vertexBufferLayouts[], uint32_t attribsCount,
WGPUBindGroupLayout bindGroupLayouts[], uint32_t bindGroupsCount,
WGPUCompareFunction stencilCompareFunction, WGPUStencilOperation stencilOperation,
WGPUCompareFunction compareFront, WGPUStencilOperation operationFront,
WGPUCompareFunction compareBack, WGPUStencilOperation operationBack,
const char* shaderSource, const char* shaderLabel, const char* pipelineLabel)
{
mShaderModule = createShaderModule(device, shaderSource, shaderLabel);
@ -561,7 +562,8 @@ void WgRenderPipeline::allocate(WGPUDevice device, WgPipelineBlendType blendType
mRenderPipeline = createRenderPipeline(device, blendType,
vertexBufferLayouts, attribsCount,
stencilCompareFunction, stencilOperation,
compareFront, operationFront,
compareBack, operationBack,
mPipelineLayout, mShaderModule, pipelineLabel);
assert(mRenderPipeline);
}
@ -670,21 +672,21 @@ WGPUPrimitiveState WgRenderPipeline::makePrimitiveState()
}
WGPUDepthStencilState WgRenderPipeline::makeDepthStencilState(WGPUCompareFunction compare, WGPUStencilOperation operation)
WGPUDepthStencilState WgRenderPipeline::makeDepthStencilState(WGPUCompareFunction compareFront, WGPUStencilOperation operationFront, WGPUCompareFunction compareBack, WGPUStencilOperation operationBack)
{
WGPUDepthStencilState depthStencilState{};
depthStencilState.nextInChain = nullptr;
depthStencilState.format = WGPUTextureFormat_Stencil8;
depthStencilState.depthWriteEnabled = false;
depthStencilState.depthCompare = WGPUCompareFunction_Always;
depthStencilState.stencilFront.compare = compare;
depthStencilState.stencilFront.failOp = operation;
depthStencilState.stencilFront.depthFailOp = operation;
depthStencilState.stencilFront.passOp = operation;
depthStencilState.stencilBack.compare = compare;
depthStencilState.stencilBack.failOp = operation;
depthStencilState.stencilBack.depthFailOp = operation;
depthStencilState.stencilBack.passOp = operation;
depthStencilState.stencilFront.compare = compareFront;
depthStencilState.stencilFront.failOp = operationFront;
depthStencilState.stencilFront.depthFailOp = operationFront;
depthStencilState.stencilFront.passOp = operationFront;
depthStencilState.stencilBack.compare = compareBack;
depthStencilState.stencilBack.failOp = operationBack;
depthStencilState.stencilBack.depthFailOp = operationBack;
depthStencilState.stencilBack.passOp = operationBack;
depthStencilState.stencilReadMask = 0xFFFFFFFF;
depthStencilState.stencilWriteMask = 0xFFFFFFFF;
depthStencilState.depthBias = 0;
@ -721,7 +723,8 @@ WGPUFragmentState WgRenderPipeline::makeFragmentState(WGPUShaderModule shaderMod
WGPURenderPipeline WgRenderPipeline::createRenderPipeline(WGPUDevice device, WgPipelineBlendType blendType,
WGPUVertexBufferLayout vertexBufferLayouts[], uint32_t attribsCount,
WGPUCompareFunction stencilCompareFunction, WGPUStencilOperation stencilOperation,
WGPUCompareFunction compareFront, WGPUStencilOperation operationFront,
WGPUCompareFunction compareBack, WGPUStencilOperation operationBack,
WGPUPipelineLayout pipelineLayout, WGPUShaderModule shaderModule,
const char* pipelineName)
{
@ -732,7 +735,7 @@ WGPURenderPipeline WgRenderPipeline::createRenderPipeline(WGPUDevice device, WgP
WGPUVertexState vertexState = makeVertexState(shaderModule, vertexBufferLayouts, attribsCount);
WGPUPrimitiveState primitiveState = makePrimitiveState();
WGPUDepthStencilState depthStencilState = makeDepthStencilState(stencilCompareFunction, stencilOperation);
WGPUDepthStencilState depthStencilState = makeDepthStencilState(compareFront, operationFront, compareBack, operationBack);
WGPUMultisampleState multisampleState = makeMultisampleState();
WGPUFragmentState fragmentState = makeFragmentState(shaderModule, colorTargetStates, 1);

View file

@ -128,7 +128,8 @@ protected:
void allocate(WGPUDevice device, WgPipelineBlendType blendType,
WGPUVertexBufferLayout vertexBufferLayouts[], uint32_t attribsCount,
WGPUBindGroupLayout bindGroupLayouts[], uint32_t bindGroupsCount,
WGPUCompareFunction stencilCompareFunction, WGPUStencilOperation stencilOperation,
WGPUCompareFunction compareFront, WGPUStencilOperation operationFront,
WGPUCompareFunction compareBack, WGPUStencilOperation operationBack,
const char* shaderSource, const char* shaderLabel, const char* pipelineLabel);
public:
void release() override;
@ -139,13 +140,14 @@ public:
static WGPUVertexBufferLayout makeVertexBufferLayout(const WGPUVertexAttribute* vertexAttributes, uint32_t count, uint64_t stride);
static WGPUVertexState makeVertexState(WGPUShaderModule shaderModule, const WGPUVertexBufferLayout* buffers, uint32_t count);
static WGPUPrimitiveState makePrimitiveState();
static WGPUDepthStencilState makeDepthStencilState(WGPUCompareFunction compare, WGPUStencilOperation operation);
static WGPUDepthStencilState makeDepthStencilState(WGPUCompareFunction compareFront, WGPUStencilOperation operationFront, WGPUCompareFunction compareBack, WGPUStencilOperation operationBack);
static WGPUMultisampleState makeMultisampleState();
static WGPUFragmentState makeFragmentState(WGPUShaderModule shaderModule, WGPUColorTargetState* targets, uint32_t size);
static WGPURenderPipeline createRenderPipeline(WGPUDevice device, WgPipelineBlendType blendType,
WGPUVertexBufferLayout vertexBufferLayouts[], uint32_t attribsCount,
WGPUCompareFunction stencilCompareFunction, WGPUStencilOperation stencilOperation,
WGPUCompareFunction compareFront, WGPUStencilOperation operationFront,
WGPUCompareFunction compareBack, WGPUStencilOperation operationBack,
WGPUPipelineLayout pipelineLayout, WGPUShaderModule shaderModule,
const char* pipelineLabel);
static void destroyRenderPipeline(WGPURenderPipeline& renderPipeline);

View file

@ -29,7 +29,7 @@
// graphics pipelines
//************************************************************************
void WgPipelineFillShape::initialize(WGPUDevice device)
void WgPipelineFillShapeWinding::initialize(WGPUDevice device)
{
// vertex attributes settings
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
@ -44,19 +44,55 @@ void WgPipelineFillShape::initialize(WGPUDevice device)
};
// stencil function
WGPUCompareFunction stencilFuncion = WGPUCompareFunction_Always;
WGPUStencilOperation stencilOperation = WGPUStencilOperation_Invert;
WGPUCompareFunction stencilFuncionFront = WGPUCompareFunction_Always;
WGPUStencilOperation stencilOperationFront = WGPUStencilOperation_IncrementWrap;
WGPUCompareFunction stencilFuncionBack = WGPUCompareFunction_Always;
WGPUStencilOperation stencilOperationBack = WGPUStencilOperation_DecrementWrap;
// sheder source and labels
auto shaderSource = cShaderSource_PipelineFill;
auto shaderLabel = "The shader fill";
auto pipelineLabel = "The render pipeline fill shape";
auto pipelineLabel = "The render pipeline fill shape winding";
// allocate all pipeline handles
allocate(device, WgPipelineBlendType::Src,
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
stencilFuncion, stencilOperation,
stencilFuncionFront, stencilOperationFront, stencilFuncionBack, stencilOperationBack,
shaderSource, shaderLabel, pipelineLabel);
}
void WgPipelineFillShapeEvenOdd::initialize(WGPUDevice device)
{
// vertex attributes settings
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
WGPUVertexBufferLayout vertexBufferLayouts[] = {
makeVertexBufferLayout(&vertexAttributesPos, 1, sizeof(float) * 2)
};
// bind groups
WGPUBindGroupLayout bindGroupLayouts[] = {
WgBindGroupCanvas::getLayout(device),
WgBindGroupPaint::getLayout(device)
};
// stencil function
WGPUCompareFunction stencilFuncionFront = WGPUCompareFunction_Always;
WGPUStencilOperation stencilOperationFront = WGPUStencilOperation_Invert;
WGPUCompareFunction stencilFuncionBack = WGPUCompareFunction_Always;
WGPUStencilOperation stencilOperationBack = WGPUStencilOperation_Invert;
// sheder source and labels
auto shaderSource = cShaderSource_PipelineFill;
auto shaderLabel = "The shader fill";
auto pipelineLabel = "The render pipeline fill shape Even Odd";
// allocate all pipeline handles
allocate(device, WgPipelineBlendType::Src,
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
stencilFuncionFront, stencilOperationFront, stencilFuncionBack, stencilOperationBack,
shaderSource, shaderLabel, pipelineLabel);
}
@ -88,7 +124,7 @@ void WgPipelineFillStroke::initialize(WGPUDevice device)
allocate(device, WgPipelineBlendType::Src,
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
stencilFuncion, stencilOperation,
stencilFuncion, stencilOperation, stencilFuncion, stencilOperation,
shaderSource, shaderLabel, pipelineLabel);
}
@ -121,7 +157,7 @@ void WgPipelineSolid::initialize(WGPUDevice device, WgPipelineBlendType blendTyp
allocate(device, blendType,
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
stencilFuncion, stencilOperation,
stencilFuncion, stencilOperation, stencilFuncion, stencilOperation,
shaderSource, shaderLabel, pipelineLabel);
}
@ -154,7 +190,7 @@ void WgPipelineLinear::initialize(WGPUDevice device, WgPipelineBlendType blendTy
allocate(device, blendType,
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
stencilFuncion, stencilOperation,
stencilFuncion, stencilOperation, stencilFuncion, stencilOperation,
shaderSource, shaderLabel, pipelineLabel);
}
@ -187,7 +223,7 @@ void WgPipelineRadial::initialize(WGPUDevice device, WgPipelineBlendType blendTy
allocate(device, blendType,
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
stencilFuncion, stencilOperation,
stencilFuncion, stencilOperation, stencilFuncion, stencilOperation,
shaderSource, shaderLabel, pipelineLabel);
}
@ -222,7 +258,7 @@ void WgPipelineImage::initialize(WGPUDevice device, WgPipelineBlendType blendTyp
allocate(device, blendType,
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
stencilFuncion, stencilOperation,
stencilFuncion, stencilOperation, stencilFuncion, stencilOperation,
shaderSource, shaderLabel, pipelineLabel);
}
@ -340,7 +376,8 @@ void WgPipelineAntiAliasing::initialize(WGPUDevice device)
void WgPipelines::initialize(WgContext& context)
{
// fill pipelines
fillShape.initialize(context.device);
fillShapeWinding.initialize(context.device);
fillShapeEvenOdd.initialize(context.device);
fillStroke.initialize(context.device);
for (uint8_t type = (uint8_t)WgPipelineBlendType::Src; type <= (uint8_t)WgPipelineBlendType::Max; type++) {
solid[type].initialize(context.device, (WgPipelineBlendType)type);
@ -386,7 +423,8 @@ void WgPipelines::release()
solid[type].release();
}
fillStroke.release();
fillShape.release();
fillShapeEvenOdd.release();
fillShapeWinding.release();
}

View file

@ -29,7 +29,18 @@
// render pipelines
//*****************************************************************************
struct WgPipelineFillShape: public WgRenderPipeline
struct WgPipelineFillShapeWinding: public WgRenderPipeline
{
void initialize(WGPUDevice device) override;
void use(WGPURenderPassEncoder encoder, WgBindGroupCanvas& groupCanvas, WgBindGroupPaint& groupPaint)
{
set(encoder);
groupCanvas.set(encoder, 0);
groupPaint.set(encoder, 1);
}
};
struct WgPipelineFillShapeEvenOdd: public WgRenderPipeline
{
void initialize(WGPUDevice device) override;
void use(WGPURenderPassEncoder encoder, WgBindGroupCanvas& groupCanvas, WgBindGroupPaint& groupPaint)
@ -177,7 +188,8 @@ struct WgPipelineAntiAliasing: public WgComputePipeline
struct WgPipelines
{
// render pipelines
WgPipelineFillShape fillShape;
WgPipelineFillShapeWinding fillShapeWinding;
WgPipelineFillShapeEvenOdd fillShapeEvenOdd;
WgPipelineFillStroke fillStroke;
// fill pipelines
WgPipelineSolid solid[6];

View file

@ -117,7 +117,7 @@ void WgRenderStorage::drawShapeWinding(WgContext& context, WgRenderDataShape* re
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
for (uint32_t i = 0; i < renderData->meshGroupShapes.meshes.count; i++) {
// draw to stencil (first pass)
mPipelines->fillShape.use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint);
mPipelines->fillShapeWinding.use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint);
renderData->meshGroupShapes.meshes[i]->drawFan(context, mRenderPassEncoder);
// fill shape (second pass)
WgRenderSettings& settings = renderData->renderSettingsShape;
@ -140,7 +140,7 @@ void WgRenderStorage::drawShapeEvenOdd(WgContext& context, WgRenderDataShape* re
// draw shape geometry
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
// draw to stencil (first pass)
mPipelines->fillShape.use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint);
mPipelines->fillShapeEvenOdd.use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint);
for (uint32_t i = 0; i < renderData->meshGroupShapes.meshes.count; i++)
renderData->meshGroupShapes.meshes[i]->drawFan(context, mRenderPassEncoder);
// fill shape geometry (second pass)