From 9c8b10603efaa197bcb273ff7b891496b88318e0 Mon Sep 17 00:00:00 2001 From: Sergii Liebodkin Date: Wed, 22 May 2024 16:20:00 +0300 Subject: [PATCH] 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. --- src/renderer/wg_engine/tvgWgCommon.cpp | 29 +++++---- src/renderer/wg_engine/tvgWgCommon.h | 8 ++- src/renderer/wg_engine/tvgWgPipelines.cpp | 62 ++++++++++++++++---- src/renderer/wg_engine/tvgWgPipelines.h | 16 ++++- src/renderer/wg_engine/tvgWgRenderTarget.cpp | 4 +- 5 files changed, 87 insertions(+), 32 deletions(-) diff --git a/src/renderer/wg_engine/tvgWgCommon.cpp b/src/renderer/wg_engine/tvgWgCommon.cpp index d26cd0a3..08f216ea 100644 --- a/src/renderer/wg_engine/tvgWgCommon.cpp +++ b/src/renderer/wg_engine/tvgWgCommon.cpp @@ -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); diff --git a/src/renderer/wg_engine/tvgWgCommon.h b/src/renderer/wg_engine/tvgWgCommon.h index 668552a9..41410108 100644 --- a/src/renderer/wg_engine/tvgWgCommon.h +++ b/src/renderer/wg_engine/tvgWgCommon.h @@ -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); diff --git a/src/renderer/wg_engine/tvgWgPipelines.cpp b/src/renderer/wg_engine/tvgWgPipelines.cpp index de969815..b9e03851 100644 --- a/src/renderer/wg_engine/tvgWgPipelines.cpp +++ b/src/renderer/wg_engine/tvgWgPipelines.cpp @@ -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(); } diff --git a/src/renderer/wg_engine/tvgWgPipelines.h b/src/renderer/wg_engine/tvgWgPipelines.h index 8d477dd1..7a90d768 100644 --- a/src/renderer/wg_engine/tvgWgPipelines.h +++ b/src/renderer/wg_engine/tvgWgPipelines.h @@ -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]; diff --git a/src/renderer/wg_engine/tvgWgRenderTarget.cpp b/src/renderer/wg_engine/tvgWgRenderTarget.cpp index 1b41a206..e11ef2bf 100644 --- a/src/renderer/wg_engine/tvgWgRenderTarget.cpp +++ b/src/renderer/wg_engine/tvgWgRenderTarget.cpp @@ -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)