diff --git a/src/renderer/wg_engine/tvgWgCommon.cpp b/src/renderer/wg_engine/tvgWgCommon.cpp index 865ed235..e7f798f2 100644 --- a/src/renderer/wg_engine/tvgWgCommon.cpp +++ b/src/renderer/wg_engine/tvgWgCommon.cpp @@ -494,7 +494,7 @@ void WgPipeline::destroyShaderModule(WGPUShaderModule& shaderModule) // render pipeline //***************************************************************************** -void WgRenderPipeline::allocate(WGPUDevice device, +void WgRenderPipeline::allocate(WGPUDevice device, WgPipelineBlendType blendType, WGPUVertexBufferLayout vertexBufferLayouts[], uint32_t attribsCount, WGPUBindGroupLayout bindGroupLayouts[], uint32_t bindGroupsCount, WGPUCompareFunction stencilCompareFunction, WGPUStencilOperation stencilOperation, @@ -506,7 +506,7 @@ void WgRenderPipeline::allocate(WGPUDevice device, mPipelineLayout = createPipelineLayout(device, bindGroupLayouts, bindGroupsCount); assert(mPipelineLayout); - mRenderPipeline = createRenderPipeline(device, + mRenderPipeline = createRenderPipeline(device, blendType, vertexBufferLayouts, attribsCount, stencilCompareFunction, stencilOperation, mPipelineLayout, mShaderModule, pipelineLabel); @@ -527,14 +527,42 @@ void WgRenderPipeline::set(WGPURenderPassEncoder renderPassEncoder) }; -WGPUBlendState WgRenderPipeline::makeBlendState() +WGPUBlendState WgRenderPipeline::makeBlendState(WgPipelineBlendType blendType) { WGPUBlendState blendState{}; - blendState.color.operation = WGPUBlendOperation_Add; - blendState.color.srcFactor = WGPUBlendFactor_One; - blendState.color.dstFactor = WGPUBlendFactor_Zero; + // src + if (blendType == WgPipelineBlendType::Src) { + blendState.color.operation = WGPUBlendOperation_Add; + blendState.color.srcFactor = WGPUBlendFactor_One; + blendState.color.dstFactor = WGPUBlendFactor_Zero; + } else // normal + if (blendType == WgPipelineBlendType::Normal) { + blendState.color.operation = WGPUBlendOperation_Add; + blendState.color.srcFactor = WGPUBlendFactor_SrcAlpha; + blendState.color.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha; + } else // add + if (blendType == WgPipelineBlendType::Add) { + blendState.color.operation = WGPUBlendOperation_Add; + blendState.color.srcFactor = WGPUBlendFactor_One; + blendState.color.dstFactor = WGPUBlendFactor_One; + } else // mult + if (blendType == WgPipelineBlendType::Mult) { + blendState.color.operation = WGPUBlendOperation_Add; + blendState.color.srcFactor = WGPUBlendFactor_Dst; + blendState.color.dstFactor = WGPUBlendFactor_Zero; + } else // min + if (blendType == WgPipelineBlendType::Min) { + blendState.color.operation = WGPUBlendOperation_Min; + blendState.color.srcFactor = WGPUBlendFactor_One; + blendState.color.dstFactor = WGPUBlendFactor_One; + } else // max + if (blendType == WgPipelineBlendType::Max) { + blendState.color.operation = WGPUBlendOperation_Max; + blendState.color.srcFactor = WGPUBlendFactor_One; + blendState.color.dstFactor = WGPUBlendFactor_One; + } blendState.alpha.operation = WGPUBlendOperation_Add; - blendState.alpha.srcFactor = WGPUBlendFactor_One; + blendState.alpha.srcFactor = WGPUBlendFactor_SrcAlpha; blendState.alpha.dstFactor = WGPUBlendFactor_Zero; return blendState; } @@ -638,13 +666,13 @@ WGPUFragmentState WgRenderPipeline::makeFragmentState(WGPUShaderModule shaderMod } -WGPURenderPipeline WgRenderPipeline::createRenderPipeline(WGPUDevice device, +WGPURenderPipeline WgRenderPipeline::createRenderPipeline(WGPUDevice device, WgPipelineBlendType blendType, WGPUVertexBufferLayout vertexBufferLayouts[], uint32_t attribsCount, WGPUCompareFunction stencilCompareFunction, WGPUStencilOperation stencilOperation, WGPUPipelineLayout pipelineLayout, WGPUShaderModule shaderModule, const char* pipelineName) { - WGPUBlendState blendState = makeBlendState(); + WGPUBlendState blendState = makeBlendState(blendType); WGPUColorTargetState colorTargetStates[] = { makeColorTargetState(&blendState) }; diff --git a/src/renderer/wg_engine/tvgWgCommon.h b/src/renderer/wg_engine/tvgWgCommon.h index cba2c63b..7b7a92b4 100644 --- a/src/renderer/wg_engine/tvgWgCommon.h +++ b/src/renderer/wg_engine/tvgWgCommon.h @@ -28,6 +28,15 @@ #include "tvgCommon.h" #include "tvgRender.h" +enum class WgPipelineBlendType { + Src = 0, // S + Normal, // (Sa * S) + (255 - Sa) * D + Add, // (S + D) + Mult, // (S * D) + Min, // min(S, D) + Max // max(S, D) +}; + struct WgPipelines; struct WgContext { @@ -107,7 +116,7 @@ struct WgRenderPipeline: public WgPipeline { protected: WGPURenderPipeline mRenderPipeline{}; - void allocate(WGPUDevice device, + void allocate(WGPUDevice device, WgPipelineBlendType blendType, WGPUVertexBufferLayout vertexBufferLayouts[], uint32_t attribsCount, WGPUBindGroupLayout bindGroupLayouts[], uint32_t bindGroupsCount, WGPUCompareFunction stencilCompareFunction, WGPUStencilOperation stencilOperation, @@ -116,7 +125,7 @@ public: void release() override; void set(WGPURenderPassEncoder renderPassEncoder); - static WGPUBlendState makeBlendState(); + static WGPUBlendState makeBlendState(WgPipelineBlendType blendType); static WGPUColorTargetState makeColorTargetState(const WGPUBlendState* blendState); static WGPUVertexBufferLayout makeVertexBufferLayout(const WGPUVertexAttribute* vertexAttributes, uint32_t count, uint64_t stride); static WGPUVertexState makeVertexState(WGPUShaderModule shaderModule, const WGPUVertexBufferLayout* buffers, uint32_t count); @@ -125,7 +134,7 @@ public: static WGPUMultisampleState makeMultisampleState(); static WGPUFragmentState makeFragmentState(WGPUShaderModule shaderModule, WGPUColorTargetState* targets, uint32_t size); - static WGPURenderPipeline createRenderPipeline(WGPUDevice device, + static WGPURenderPipeline createRenderPipeline(WGPUDevice device, WgPipelineBlendType blendType, WGPUVertexBufferLayout vertexBufferLayouts[], uint32_t attribsCount, WGPUCompareFunction stencilCompareFunction, WGPUStencilOperation stencilOperation, WGPUPipelineLayout pipelineLayout, WGPUShaderModule shaderModule, diff --git a/src/renderer/wg_engine/tvgWgPipelines.cpp b/src/renderer/wg_engine/tvgWgPipelines.cpp index 77af9547..e14af258 100644 --- a/src/renderer/wg_engine/tvgWgPipelines.cpp +++ b/src/renderer/wg_engine/tvgWgPipelines.cpp @@ -53,7 +53,7 @@ void WgPipelineFillShape::initialize(WGPUDevice device) auto pipelineLabel = "The render pipeline fill shape"; // allocate all pipeline handles - allocate(device, + allocate(device, WgPipelineBlendType::Src, vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts), bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts), stencilFuncion, stencilOperation, @@ -85,7 +85,7 @@ void WgPipelineFillStroke::initialize(WGPUDevice device) auto pipelineLabel = "The render pipeline fill stroke"; // allocate all pipeline handles - allocate(device, + allocate(device, WgPipelineBlendType::Src, vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts), bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts), stencilFuncion, stencilOperation, @@ -93,7 +93,7 @@ void WgPipelineFillStroke::initialize(WGPUDevice device) } -void WgPipelineSolid::initialize(WGPUDevice device) +void WgPipelineSolid::initialize(WGPUDevice device, WgPipelineBlendType blendType) { // vertex and buffers settings WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 }; @@ -118,7 +118,7 @@ void WgPipelineSolid::initialize(WGPUDevice device) auto pipelineLabel = "The render pipeline solid color"; // allocate all pipeline handles - allocate(device, + allocate(device, blendType, vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts), bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts), stencilFuncion, stencilOperation, @@ -126,7 +126,7 @@ void WgPipelineSolid::initialize(WGPUDevice device) } -void WgPipelineLinear::initialize(WGPUDevice device) +void WgPipelineLinear::initialize(WGPUDevice device, WgPipelineBlendType blendType) { // vertex and buffers settings WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 }; @@ -151,7 +151,7 @@ void WgPipelineLinear::initialize(WGPUDevice device) auto pipelineLabel = "The render pipeline linear gradient"; // allocate all pipeline handles - allocate(device, + allocate(device, blendType, vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts), bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts), stencilFuncion, stencilOperation, @@ -159,7 +159,7 @@ void WgPipelineLinear::initialize(WGPUDevice device) } -void WgPipelineRadial::initialize(WGPUDevice device) +void WgPipelineRadial::initialize(WGPUDevice device, WgPipelineBlendType blendType) { // vertex and buffers settings WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 }; @@ -184,7 +184,7 @@ void WgPipelineRadial::initialize(WGPUDevice device) auto pipelineLabel = "The render pipeline radial gradient"; // allocate all pipeline handles - allocate(device, + allocate(device, blendType, vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts), bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts), stencilFuncion, stencilOperation, @@ -192,7 +192,7 @@ void WgPipelineRadial::initialize(WGPUDevice device) } -void WgPipelineImage::initialize(WGPUDevice device) +void WgPipelineImage::initialize(WGPUDevice device, WgPipelineBlendType blendType) { // vertex and buffers settings WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 }; @@ -219,7 +219,7 @@ void WgPipelineImage::initialize(WGPUDevice device) auto pipelineLabel = "The render pipeline image"; // allocate all pipeline handles - allocate(device, + allocate(device, blendType, vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts), bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts), stencilFuncion, stencilOperation, @@ -320,10 +320,12 @@ void WgPipelines::initialize(WgContext& context) // fill pipelines fillShape.initialize(context.device); fillStroke.initialize(context.device); - solid.initialize(context.device); - linear.initialize(context.device); - radial.initialize(context.device); - image.initialize(context.device); + for (uint8_t type = (uint8_t)WgPipelineBlendType::Src; type <= (uint8_t)WgPipelineBlendType::Max; type++) { + solid[type].initialize(context.device, (WgPipelineBlendType)type); + linear[type].initialize(context.device, (WgPipelineBlendType)type); + radial[type].initialize(context.device, (WgPipelineBlendType)type); + image[type].initialize(context.device, (WgPipelineBlendType)type); + } // compute pipelines computeClear.initialize(context.device); computeBlend.initialize(context.device); @@ -352,10 +354,41 @@ void WgPipelines::release() computeBlend.release(); computeClear.release(); // fill pipelines - image.release(); - radial.release(); - linear.release(); - solid.release(); + for (uint8_t type = (uint8_t)WgPipelineBlendType::Src; type <= (uint8_t)WgPipelineBlendType::Max; type++) { + image[type].release(); + radial[type].release(); + linear[type].release(); + solid[type].release(); + } fillStroke.release(); fillShape.release(); } + + +bool WgPipelines::isBlendMethodSupportsHW(BlendMethod blendMethod) +{ + switch (blendMethod) { + case BlendMethod::SrcOver: + case BlendMethod::Normal: + case BlendMethod::Add: + case BlendMethod::Multiply: + case BlendMethod::Darken: + case BlendMethod::Lighten: + return true; + default: return false; + }; +} + + +WgPipelineBlendType WgPipelines::blendMethodToBlendType(BlendMethod blendMethod) +{ + switch (blendMethod) { + case BlendMethod::SrcOver: return WgPipelineBlendType::Src; + case BlendMethod::Normal: return WgPipelineBlendType::Normal; + case BlendMethod::Add: return WgPipelineBlendType::Add; + case BlendMethod::Multiply: return WgPipelineBlendType::Mult; + case BlendMethod::Darken: return WgPipelineBlendType::Min; + case BlendMethod::Lighten: return WgPipelineBlendType::Max; + default: return WgPipelineBlendType::Src; + }; +} diff --git a/src/renderer/wg_engine/tvgWgPipelines.h b/src/renderer/wg_engine/tvgWgPipelines.h index 5ec4fe2c..61e58fba 100644 --- a/src/renderer/wg_engine/tvgWgPipelines.h +++ b/src/renderer/wg_engine/tvgWgPipelines.h @@ -53,7 +53,8 @@ struct WgPipelineFillStroke: public WgRenderPipeline struct WgPipelineSolid: public WgRenderPipeline { - void initialize(WGPUDevice device) override; + void initialize(WGPUDevice device) override {} + void initialize(WGPUDevice device, WgPipelineBlendType blendType); void use(WGPURenderPassEncoder encoder, WgBindGroupCanvas& groupCanvas,WgBindGroupPaint& groupPaint, WgBindGroupSolidColor& groupSolid) { set(encoder); @@ -65,7 +66,8 @@ struct WgPipelineSolid: public WgRenderPipeline struct WgPipelineLinear: public WgRenderPipeline { - void initialize(WGPUDevice device) override; + void initialize(WGPUDevice device) override {} + void initialize(WGPUDevice device, WgPipelineBlendType blendType); void use(WGPURenderPassEncoder encoder, WgBindGroupCanvas& groupCanvas, WgBindGroupPaint& groupPaint, WgBindGroupLinearGradient& groupLinear) { set(encoder); @@ -77,7 +79,8 @@ struct WgPipelineLinear: public WgRenderPipeline struct WgPipelineRadial: public WgRenderPipeline { - void initialize(WGPUDevice device) override; + void initialize(WGPUDevice device) override {} + void initialize(WGPUDevice device, WgPipelineBlendType blendType); void use(WGPURenderPassEncoder encoder, WgBindGroupCanvas& groupCanvas, WgBindGroupPaint& groupPaint, WgBindGroupRadialGradient& groupRadial) { set(encoder); @@ -89,7 +92,8 @@ struct WgPipelineRadial: public WgRenderPipeline struct WgPipelineImage: public WgRenderPipeline { - void initialize(WGPUDevice device) override; + void initialize(WGPUDevice device) override {} + void initialize(WGPUDevice device, WgPipelineBlendType blendType); void use(WGPURenderPassEncoder encoder, WgBindGroupCanvas& groupCanvas, WgBindGroupPaint& groupPaint, WgBindGroupPicture& groupPicture) { set(encoder); @@ -161,10 +165,11 @@ struct WgPipelines // render pipelines WgPipelineFillShape fillShape; WgPipelineFillStroke fillStroke; - WgPipelineSolid solid; - WgPipelineLinear linear; - WgPipelineRadial radial; - WgPipelineImage image; + // fill pipelines + WgPipelineSolid solid[6]; + WgPipelineLinear linear[6]; + WgPipelineRadial radial[6]; + WgPipelineImage image[6]; // compute pipelines WgPipelineClear computeClear; WgPipelineBlend computeBlend; @@ -173,6 +178,9 @@ struct WgPipelines void initialize(WgContext& context); void release(); + + static bool isBlendMethodSupportsHW(BlendMethod blendMethod); + static WgPipelineBlendType blendMethodToBlendType(BlendMethod blendMethod); }; #endif // _TVG_WG_PIPELINES_H_ diff --git a/src/renderer/wg_engine/tvgWgRenderTarget.cpp b/src/renderer/wg_engine/tvgWgRenderTarget.cpp index 2877bb37..c9b6c59c 100644 --- a/src/renderer/wg_engine/tvgWgRenderTarget.cpp +++ b/src/renderer/wg_engine/tvgWgRenderTarget.cpp @@ -23,21 +23,30 @@ #include "tvgWgRenderTarget.h" //***************************************************************************** -// render target +// render storage //***************************************************************************** -void WgRenderTarget::initialize(WgContext& context, uint32_t w, uint32_t h, uint32_t samples) -{ + void WgRenderStorage::initialize(WgContext& context, uint32_t w, uint32_t h, uint32_t samples) + { release(context); + // store target storage size + width = w * samples; + height = h * samples; + workgroupsCountX = (width + WG_COMPUTE_WORKGROUP_SIZE_X - 1) / WG_COMPUTE_WORKGROUP_SIZE_X; // workgroup size x == 8 + workgroupsCountY = (height + WG_COMPUTE_WORKGROUP_SIZE_Y - 1) / WG_COMPUTE_WORKGROUP_SIZE_Y; // workgroup size y == 8 // create color and stencil textures texColor = context.createTexture2d( - WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_StorageBinding, - WGPUTextureFormat_RGBA8Unorm, - w * samples, h * samples, "The target texture color"); + WGPUTextureUsage_CopySrc | + WGPUTextureUsage_CopyDst | + WGPUTextureUsage_TextureBinding | + WGPUTextureUsage_StorageBinding | + WGPUTextureUsage_RenderAttachment, + WGPUTextureFormat_RGBA8Unorm, + width, height, "The target texture color"); texStencil = context.createTexture2d( WGPUTextureUsage_RenderAttachment, - WGPUTextureFormat_Stencil8, - w * samples, h * samples, "The target texture stencil"); + WGPUTextureFormat_Stencil8, + width, height, "The target texture stencil"); assert(texColor); assert(texStencil); texViewColor = context.createTextureView2d(texColor, "The target texture view color"); @@ -50,96 +59,150 @@ void WgRenderTarget::initialize(WgContext& context, uint32_t w, uint32_t h, uint WgShaderTypeMat4x4f viewMat(w, h); mBindGroupCanvas.initialize(context.device, context.queue, viewMat); mPipelines = context.pipelines; -} + } -void WgRenderTarget::release(WgContext& context) +void WgRenderStorage::release(WgContext& context) { + mRenderPassEncoder = nullptr; mBindGroupCanvas.release(); bindGroupTexStorage.release(); context.releaseTextureView(texViewStencil); context.releaseTextureView(texViewColor); context.releaseTexture(texStencil); context.releaseTexture(texColor); + workgroupsCountX = 0; + workgroupsCountY = 0; + height = 0; + width = 0; } -void WgRenderTarget::renderShape(WGPUCommandEncoder commandEncoder, WgRenderDataShape* renderData) +void WgRenderStorage::renderShape(WgRenderDataShape* renderData, WgPipelineBlendType blendType) { assert(renderData); - assert(commandEncoder); - WGPURenderPassEncoder renderPassEncoder = beginRenderPass(commandEncoder); + assert(mRenderPassEncoder); if (renderData->strokeFirst) - drawStroke(renderPassEncoder, renderData); - drawShape(renderPassEncoder, renderData); + drawStroke(renderData, blendType); + drawShape(renderData, blendType); if (!renderData->strokeFirst) - drawStroke(renderPassEncoder, renderData); - endRenderPass(renderPassEncoder); + drawStroke(renderData, blendType); } -void WgRenderTarget::renderPicture(WGPUCommandEncoder commandEncoder, WgRenderDataPicture* renderData) +void WgRenderStorage::renderPicture(WgRenderDataPicture* renderData, WgPipelineBlendType blendType) { assert(renderData); - assert(commandEncoder); - WGPURenderPassEncoder renderPassEncoder = beginRenderPass(commandEncoder); - wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); - mPipelines->image.use(renderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, renderData->bindGroupPicture); - renderData->meshData.drawImage(renderPassEncoder); - endRenderPass(renderPassEncoder); + assert(mRenderPassEncoder); + uint8_t blend = (uint8_t)blendType; + wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0); + mPipelines->image[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, renderData->bindGroupPicture); + renderData->meshData.drawImage(mRenderPassEncoder); } -void WgRenderTarget::drawShape(WGPURenderPassEncoder renderPassEncoder, WgRenderDataShape* renderData) +void WgRenderStorage::drawShape(WgRenderDataShape* renderData, WgPipelineBlendType blendType) { assert(renderData); - assert(renderPassEncoder); + assert(mRenderPassEncoder); // draw shape geometry - wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); + uint8_t blend = (uint8_t)blendType; + wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0); for (uint32_t i = 0; i < renderData->meshGroupShapes.meshes.count; i++) { // draw to stencil (first pass) - mPipelines->fillShape.use(renderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint); - renderData->meshGroupShapes.meshes[i]->draw(renderPassEncoder); + mPipelines->fillShape.use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint); + renderData->meshGroupShapes.meshes[i]->draw(mRenderPassEncoder); // fill shape (second pass) WgRenderSettings& settings = renderData->renderSettingsShape; if (settings.fillType == WgRenderSettingsType::Solid) - mPipelines->solid.use(renderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupSolid); + mPipelines->solid[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupSolid); else if (settings.fillType == WgRenderSettingsType::Linear) - mPipelines->linear.use(renderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupLinear); + mPipelines->linear[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupLinear); else if (settings.fillType == WgRenderSettingsType::Radial) - mPipelines->radial.use(renderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupRadial); - renderData->meshBBoxShapes.draw(renderPassEncoder); + mPipelines->radial[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupRadial); + renderData->meshBBoxShapes.draw(mRenderPassEncoder); } } -void WgRenderTarget::drawStroke(WGPURenderPassEncoder renderPassEncoder, WgRenderDataShape* renderData) +void WgRenderStorage::drawStroke(WgRenderDataShape* renderData, WgPipelineBlendType blendType) { assert(renderData); - assert(renderPassEncoder); + assert(mRenderPassEncoder); // draw stroke geometry + uint8_t blend = (uint8_t)blendType; if (renderData->meshGroupStrokes.meshes.count > 0) { // draw strokes to stencil (first pass) - wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255); + wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 255); for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) { - mPipelines->fillStroke.use(renderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint); - renderData->meshGroupStrokes.meshes[i]->draw(renderPassEncoder); + mPipelines->fillStroke.use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint); + renderData->meshGroupStrokes.meshes[i]->draw(mRenderPassEncoder); } // fill shape (second pass) - wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); + wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0); WgRenderSettings& settings = renderData->renderSettingsStroke; if (settings.fillType == WgRenderSettingsType::Solid) - mPipelines->solid.use(renderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupSolid); + mPipelines->solid[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupSolid); else if (settings.fillType == WgRenderSettingsType::Linear) - mPipelines->linear.use(renderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupLinear); + mPipelines->linear[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupLinear); else if (settings.fillType == WgRenderSettingsType::Radial) - mPipelines->radial.use(renderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupRadial); - renderData->meshBBoxStrokes.draw(renderPassEncoder); + mPipelines->radial[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupRadial); + renderData->meshBBoxStrokes.draw(mRenderPassEncoder); } } -WGPURenderPassEncoder WgRenderTarget::beginRenderPass(WGPUCommandEncoder commandEncoder) +void WgRenderStorage::clear(WGPUCommandEncoder commandEncoder) +{ + assert(commandEncoder); + WGPUComputePassEncoder computePassEncoder = beginComputePass(commandEncoder); + mPipelines->computeClear.use(computePassEncoder, bindGroupTexStorage); + dispatchWorkgroups(computePassEncoder); + endComputePass(computePassEncoder); +} + + +void WgRenderStorage::blend(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetSrc, WgBindGroupBlendMethod* blendMethod) +{ + assert(commandEncoder); + assert(targetSrc); + WGPUComputePassEncoder computePassEncoder = beginComputePass(commandEncoder); + mPipelines->computeBlend.use(computePassEncoder, targetSrc->bindGroupTexStorage, bindGroupTexStorage, *blendMethod); + dispatchWorkgroups(computePassEncoder); + endComputePass(computePassEncoder); +}; + + +void WgRenderStorage::compose(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetMsk, WgBindGroupCompositeMethod* composeMethod, WgBindGroupOpacity* opacity) +{ + assert(commandEncoder); + assert(targetMsk); + WGPUComputePassEncoder computePassEncoder = beginComputePass(commandEncoder); + mPipelines->computeCompose.use(computePassEncoder, bindGroupTexStorage, targetMsk->bindGroupTexStorage, *composeMethod, *opacity); + dispatchWorkgroups(computePassEncoder); + endComputePass(computePassEncoder); +}; + + +void WgRenderStorage::antialias(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetSrc) +{ + assert(commandEncoder); + assert(targetSrc); + WGPUComputePassEncoder computePassEncoder = beginComputePass(commandEncoder); + mPipelines->computeAntiAliasing.use(computePassEncoder, targetSrc->bindGroupTexStorage, bindGroupTexStorage); + dispatchWorkgroups(computePassEncoder); + endComputePass(computePassEncoder); +} + + +void WgRenderStorage::dispatchWorkgroups(WGPUComputePassEncoder computePassEncoder) +{ + assert(computePassEncoder); + wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, workgroupsCountX, workgroupsCountY, 1); +} + + +void WgRenderStorage::beginRenderPass(WGPUCommandEncoder commandEncoder, bool clear) { assert(commandEncoder); // render pass depth stencil attachment @@ -157,7 +220,7 @@ WGPURenderPassEncoder WgRenderTarget::beginRenderPass(WGPUCommandEncoder command WGPURenderPassColorAttachment colorAttachment{}; colorAttachment.view = texViewColor; colorAttachment.resolveTarget = nullptr; - colorAttachment.loadOp = WGPULoadOp_Clear; + colorAttachment.loadOp = clear ? WGPULoadOp_Clear : WGPULoadOp_Load; colorAttachment.clearValue = { 0, 0, 0, 0 }; colorAttachment.storeOp = WGPUStoreOp_Store; // render pass descriptor @@ -171,111 +234,16 @@ WGPURenderPassEncoder WgRenderTarget::beginRenderPass(WGPUCommandEncoder command renderPassDesc.occlusionQuerySet = nullptr; renderPassDesc.timestampWrites = nullptr; // begin render pass - return wgpuCommandEncoderBeginRenderPass(commandEncoder, &renderPassDesc); + mRenderPassEncoder = wgpuCommandEncoderBeginRenderPass(commandEncoder, &renderPassDesc); } -void WgRenderTarget::endRenderPass(WGPURenderPassEncoder renderPassEncoder) +void WgRenderStorage::endRenderPass() { - assert(renderPassEncoder); - wgpuRenderPassEncoderEnd(renderPassEncoder); - wgpuRenderPassEncoderRelease(renderPassEncoder); -} - -//***************************************************************************** -// render storage -//***************************************************************************** - - void WgRenderStorage::initialize(WgContext& context, uint32_t w, uint32_t h, uint32_t samples) - { - release(context); - // store target storage size - width = w * samples; - height = h * samples; - workgroupsCountX = (width + WG_COMPUTE_WORKGROUP_SIZE_X - 1) / WG_COMPUTE_WORKGROUP_SIZE_X; // workgroup size x == 8 - workgroupsCountY = (height + WG_COMPUTE_WORKGROUP_SIZE_Y - 1) / WG_COMPUTE_WORKGROUP_SIZE_Y; // workgroup size y == 8 - // create color and stencil textures - texStorage = context.createTexture2d( - WGPUTextureUsage_StorageBinding | WGPUTextureUsage_CopySrc | WGPUTextureUsage_CopyDst, - WGPUTextureFormat_RGBA8Unorm, - width, height, "The target texture storage color"); - assert(texStorage); - texViewStorage = context.createTextureView2d(texStorage, "The target texture storage view color"); - assert(texViewStorage); - // initialize bind group for blitting - bindGroupTexStorage.initialize(context.device, context.queue, texViewStorage); - mPipelines = context.pipelines; - } - - -void WgRenderStorage::release(WgContext& context) -{ - bindGroupTexStorage.release(); - context.releaseTextureView(texViewStorage); - context.releaseTexture(texStorage); - workgroupsCountX = 0; - workgroupsCountY = 0; - height = 0; - width = 0; -} - -void WgRenderStorage::clear(WGPUCommandEncoder commandEncoder) -{ - assert(commandEncoder); - WGPUComputePassEncoder computePassEncoder = beginComputePass(commandEncoder); - mPipelines->computeClear.use(computePassEncoder, bindGroupTexStorage); - dispatchWorkgroups(computePassEncoder); - endRenderPass(computePassEncoder); -} - - -void WgRenderStorage::blend(WGPUCommandEncoder commandEncoder, WgRenderTarget* targetSrc, WgBindGroupBlendMethod* blendMethod) -{ - assert(commandEncoder); - assert(targetSrc); - WGPUComputePassEncoder computePassEncoder = beginComputePass(commandEncoder); - mPipelines->computeBlend.use(computePassEncoder, targetSrc->bindGroupTexStorage, bindGroupTexStorage, *blendMethod); - dispatchWorkgroups(computePassEncoder); - endRenderPass(computePassEncoder); -}; - -void WgRenderStorage::blend(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetSrc, WgBindGroupBlendMethod* blendMethod) -{ - assert(commandEncoder); - assert(targetSrc); - WGPUComputePassEncoder computePassEncoder = beginComputePass(commandEncoder); - mPipelines->computeBlend.use(computePassEncoder, targetSrc->bindGroupTexStorage, bindGroupTexStorage, *blendMethod); - dispatchWorkgroups(computePassEncoder); - endRenderPass(computePassEncoder); -}; - - -void WgRenderStorage::compose(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetMsk, WgBindGroupCompositeMethod* composeMethod, WgBindGroupOpacity* opacity) -{ - assert(commandEncoder); - assert(targetMsk); - WGPUComputePassEncoder computePassEncoder = beginComputePass(commandEncoder); - mPipelines->computeCompose.use(computePassEncoder, bindGroupTexStorage, targetMsk->bindGroupTexStorage, *composeMethod, *opacity); - dispatchWorkgroups(computePassEncoder); - endRenderPass(computePassEncoder); -}; - - -void WgRenderStorage::antialias(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetSrc) -{ - assert(commandEncoder); - assert(targetSrc); - WGPUComputePassEncoder computePassEncoder = beginComputePass(commandEncoder); - mPipelines->computeAntiAliasing.use(computePassEncoder, targetSrc->bindGroupTexStorage, bindGroupTexStorage); - dispatchWorkgroups(computePassEncoder); - endRenderPass(computePassEncoder); -} - - -void WgRenderStorage::dispatchWorkgroups(WGPUComputePassEncoder computePassEncoder) -{ - assert(computePassEncoder); - wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, workgroupsCountX, workgroupsCountY, 1); + assert(mRenderPassEncoder); + wgpuRenderPassEncoderEnd(mRenderPassEncoder); + wgpuRenderPassEncoderRelease(mRenderPassEncoder); + mRenderPassEncoder = nullptr; } @@ -290,7 +258,7 @@ WGPUComputePassEncoder WgRenderStorage::beginComputePass(WGPUCommandEncoder comm }; -void WgRenderStorage::endRenderPass(WGPUComputePassEncoder computePassEncoder) +void WgRenderStorage::endComputePass(WGPUComputePassEncoder computePassEncoder) { assert(computePassEncoder); wgpuComputePassEncoderEnd(computePassEncoder); diff --git a/src/renderer/wg_engine/tvgWgRenderTarget.h b/src/renderer/wg_engine/tvgWgRenderTarget.h index 35f5b049..80a4bdaf 100644 --- a/src/renderer/wg_engine/tvgWgRenderTarget.h +++ b/src/renderer/wg_engine/tvgWgRenderTarget.h @@ -25,10 +25,11 @@ #include "tvgWgRenderData.h" -class WgRenderTarget { +class WgRenderStorage { private: - // canvas info + // texture buffers WgBindGroupCanvas mBindGroupCanvas; + WGPURenderPassEncoder mRenderPassEncoder{}; WgPipelines* mPipelines{}; // external handle public: WGPUTexture texColor{}; @@ -36,29 +37,6 @@ public: WGPUTextureView texViewColor{}; WGPUTextureView texViewStencil{}; WgBindGroupTextureStorage bindGroupTexStorage; -public: - void initialize(WgContext& context, uint32_t w, uint32_t h, uint32_t samples = 1); - void release(WgContext& context); - - void renderShape(WGPUCommandEncoder commandEncoder, WgRenderDataShape* renderData); - void renderPicture(WGPUCommandEncoder commandEncoder, WgRenderDataPicture* renderData); -private: - void drawShape(WGPURenderPassEncoder renderPassEncoder, WgRenderDataShape* renderData); - void drawStroke(WGPURenderPassEncoder renderPassEncoder, WgRenderDataShape* renderData); - - WGPURenderPassEncoder beginRenderPass(WGPUCommandEncoder commandEncoder); - void endRenderPass(WGPURenderPassEncoder renderPassEncoder); -}; - - -class WgRenderStorage { -private: - // texture buffers - WgPipelines* mPipelines{}; // external handle -public: - WGPUTexture texStorage{}; - WGPUTextureView texViewStorage{}; - WgBindGroupTextureStorage bindGroupTexStorage; uint32_t width{}; uint32_t height{}; uint32_t workgroupsCountX{}; @@ -67,16 +45,24 @@ public: void initialize(WgContext& context, uint32_t w, uint32_t h, uint32_t samples = 1); void release(WgContext& context); + void beginRenderPass(WGPUCommandEncoder commandEncoder, bool clear); + void endRenderPass(); + + void renderShape(WgRenderDataShape* renderData, WgPipelineBlendType blendType); + void renderPicture(WgRenderDataPicture* renderData, WgPipelineBlendType blendType); + void clear(WGPUCommandEncoder commandEncoder); - void blend(WGPUCommandEncoder commandEncoder, WgRenderTarget* targetSrc, WgBindGroupBlendMethod* blendMethod); void blend(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetSrc, WgBindGroupBlendMethod* blendMethod); void compose(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetMsk, WgBindGroupCompositeMethod* composeMethod, WgBindGroupOpacity* opacity); void antialias(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetSrc); private: + void drawShape(WgRenderDataShape* renderData, WgPipelineBlendType blendType); + void drawStroke(WgRenderDataShape* renderData, WgPipelineBlendType blendType); + void dispatchWorkgroups(WGPUComputePassEncoder computePassEncoder); WGPUComputePassEncoder beginComputePass(WGPUCommandEncoder commandEncoder); - void endRenderPass(WGPUComputePassEncoder computePassEncoder); + void endComputePass(WGPUComputePassEncoder computePassEncoder); }; diff --git a/src/renderer/wg_engine/tvgWgRenderer.cpp b/src/renderer/wg_engine/tvgWgRenderer.cpp index 20bfbfcc..6543bfa1 100644 --- a/src/renderer/wg_engine/tvgWgRenderer.cpp +++ b/src/renderer/wg_engine/tvgWgRenderer.cpp @@ -133,43 +133,74 @@ RenderData WgRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderD return renderDataPicture; } - bool WgRenderer::preRender() { WGPUCommandEncoderDescriptor commandEncoderDesc{}; commandEncoderDesc.nextInChain = nullptr; commandEncoderDesc.label = "The command encoder"; mCommandEncoder = wgpuDeviceCreateCommandEncoder(mContext.device, &commandEncoderDesc); - mRenderStorageRoot.clear(mCommandEncoder); mRenderStorageStack.push(&mRenderStorageRoot); + //mRenderStorageRoot.clear(mCommandEncoder); + mRenderStorageRoot.beginRenderPass(mCommandEncoder, true); return true; } bool WgRenderer::renderShape(RenderData data) { - // render shape to render target - mRenderTarget.renderShape(mCommandEncoder, (WgRenderDataShape *)data); - // blend shape with current render storage - WgBindGroupBlendMethod* blendMethod = mBlendMethodPool.allocate(mContext, mBlendMethod); - mRenderStorageStack.last()->blend(mCommandEncoder, &mRenderTarget, blendMethod); + // get current render storage + WgPipelineBlendType blendType = WgPipelines::blendMethodToBlendType(mBlendMethod); + WgRenderStorage* renderStorage = mRenderStorageStack.last(); + assert(renderStorage); + // use hardware blend + if (WgPipelines::isBlendMethodSupportsHW(mBlendMethod)) + renderStorage->renderShape((WgRenderDataShape *)data, blendType); + else { // use custom blend + // terminate current render pass + renderStorage->endRenderPass(); + // render image to render target + mRenderTarget.beginRenderPass(mCommandEncoder, true); + mRenderTarget.renderShape((WgRenderDataShape *)data, blendType); + mRenderTarget.endRenderPass(); + // blend shape with current render storage + WgBindGroupBlendMethod* blendMethod = mBlendMethodPool.allocate(mContext, mBlendMethod); + renderStorage->blend(mCommandEncoder, &mRenderTarget, blendMethod); + // restore current render pass + renderStorage->beginRenderPass(mCommandEncoder, false); + } return true; } bool WgRenderer::renderImage(RenderData data) { - // render image to render target - mRenderTarget.renderPicture(mCommandEncoder, (WgRenderDataPicture *)data); - // blend image with current render storage - WgBindGroupBlendMethod* blendMethod = mBlendMethodPool.allocate(mContext, mBlendMethod); - mRenderStorageStack.last()->blend(mCommandEncoder, &mRenderTarget, blendMethod); + // get current render storage + WgPipelineBlendType blendType = WgPipelines::blendMethodToBlendType(mBlendMethod); + WgRenderStorage* renderStorage = mRenderStorageStack.last(); + assert(renderStorage); + // use hardware blend + if (WgPipelines::isBlendMethodSupportsHW(mBlendMethod)) + renderStorage->renderPicture((WgRenderDataPicture *)data, blendType); + else { // use custom blend + // terminate current render pass + renderStorage->endRenderPass(); + // render image to render target + mRenderTarget.beginRenderPass(mCommandEncoder, true); + mRenderTarget.renderPicture((WgRenderDataPicture *)data, blendType); + mRenderTarget.endRenderPass(); + // blend shape with current render storage + WgBindGroupBlendMethod* blendMethod = mBlendMethodPool.allocate(mContext, mBlendMethod); + renderStorage->blend(mCommandEncoder, &mRenderTarget, blendMethod); + // restore current render pass + renderStorage->beginRenderPass(mCommandEncoder, false); + } return true; } bool WgRenderer::postRender() { + mRenderStorageRoot.endRenderPass(); mRenderStorageStack.pop(); mContext.executeCommandEncoder(mCommandEncoder); wgpuCommandEncoderRelease(mCommandEncoder); @@ -233,7 +264,7 @@ bool WgRenderer::sync() mRenderStorageScreen.antialias(commandEncoder, &mRenderStorageRoot); WGPUImageCopyTexture source{}; - source.texture = mRenderStorageScreen.texStorage; + source.texture = mRenderStorageScreen.texColor; WGPUImageCopyTexture dest{}; dest.texture = backBuffer.texture; WGPUExtent3D copySize{}; @@ -313,15 +344,17 @@ Compositor* WgRenderer::target(TVG_UNUSED const RenderRegion& region, TVG_UNUSED bool WgRenderer::beginComposite(TVG_UNUSED Compositor* cmp, TVG_UNUSED CompositeMethod method, TVG_UNUSED uint8_t opacity) { - // save current composition settings + // save current composition settings cmp->method = method; cmp->opacity = opacity; + // end current render pass + mRenderStorageStack.last()->endRenderPass(); // allocate new render storage and push it to top of render tree WgRenderStorage* renderStorage = mRenderStoragePool.allocate(mContext, mTargetSurface.w * WG_SSAA_SAMPLES, mTargetSurface.h * WG_SSAA_SAMPLES); - renderStorage->clear(mCommandEncoder); mRenderStorageStack.push(renderStorage); - + // begin last render pass + mRenderStorageStack.last()->beginRenderPass(mCommandEncoder, true); return true; } @@ -329,6 +362,8 @@ bool WgRenderer::beginComposite(TVG_UNUSED Compositor* cmp, TVG_UNUSED Composite bool WgRenderer::endComposite(TVG_UNUSED Compositor* cmp) { if (cmp->method == CompositeMethod::None) { + // end current render pass + mRenderStorageStack.last()->endRenderPass(); // get two last render targets WgRenderStorage* renderStorageSrc = mRenderStorageStack.last(); mRenderStorageStack.pop(); @@ -339,7 +374,11 @@ bool WgRenderer::endComposite(TVG_UNUSED Compositor* cmp) // back render targets to the pool mRenderStoragePool.free(mContext, renderStorageSrc); + // begin last render pass + mRenderStorageStack.last()->beginRenderPass(mCommandEncoder, false); } else { + // end current render pass + mRenderStorageStack.last()->endRenderPass(); // get two last render targets WgRenderStorage* renderStorageSrc = mRenderStorageStack.last(); mRenderStorageStack.pop(); @@ -358,6 +397,8 @@ bool WgRenderer::endComposite(TVG_UNUSED Compositor* cmp) // back render targets to the pool mRenderStoragePool.free(mContext, renderStorageSrc); mRenderStoragePool.free(mContext, renderStorageMsk); + // begin last render pass + mRenderStorageStack.last()->beginRenderPass(mCommandEncoder, false); } // delete current compositor diff --git a/src/renderer/wg_engine/tvgWgRenderer.h b/src/renderer/wg_engine/tvgWgRenderer.h index 1a6d9e84..d5433916 100644 --- a/src/renderer/wg_engine/tvgWgRenderer.h +++ b/src/renderer/wg_engine/tvgWgRenderer.h @@ -62,7 +62,7 @@ public: private: // render handles WGPUCommandEncoder mCommandEncoder{}; - WgRenderTarget mRenderTarget; + WgRenderStorage mRenderTarget; WgRenderStorage mRenderStorageRoot; WgRenderStorage mRenderStorageScreen; WgRenderStoragePool mRenderStoragePool; @@ -76,7 +76,6 @@ private: // native window handles WGPUSurface mSurface{}; -private: WgContext mContext; WgPipelines mPipelines; Surface mTargetSurface;