thorvg/src/renderer/wg_engine/tvgWgPipelines.cpp
Sergii Liebodkin f281cc9e92 wg_engine: composition and blend optimization
* bind groups creation in real time removed - performance boost
* blend and composition shaders decomposed - performance boost
* shader modules and pipeline layouts generalized - less memory usage
* shared single stencil buffer used - less memory usage
* bind groups usage simplified
* general context API simplified and generalized
* all rendering logic moved into new composition class
* ready for hardware MSAA (in next steps)
* ready for direct mask applience (in next steps)
2024-09-30 15:52:44 +09:00

381 lines
17 KiB
C++
Executable file

/*
* Copyright (c) 2023 - 2024 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgWgPipelines.h"
#include "tvgWgShaderSrc.h"
WGPUShaderModule WgPipelines::createShaderModule(WGPUDevice device, const char* label, const char* code)
{
WGPUShaderModuleWGSLDescriptor shaderModuleWGSLDesc{};
shaderModuleWGSLDesc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
shaderModuleWGSLDesc.code = code;
const WGPUShaderModuleDescriptor shaderModuleDesc { .nextInChain = &shaderModuleWGSLDesc.chain, .label = label };
return wgpuDeviceCreateShaderModule(device, &shaderModuleDesc);
}
WGPUPipelineLayout WgPipelines::createPipelineLayout(WGPUDevice device, const WGPUBindGroupLayout* bindGroupLayouts, const uint32_t bindGroupLayoutsCount)
{
const WGPUPipelineLayoutDescriptor pipelineLayoutDesc { .bindGroupLayoutCount = bindGroupLayoutsCount, .bindGroupLayouts = bindGroupLayouts };
return wgpuDeviceCreatePipelineLayout(device, &pipelineLayoutDesc);
}
WGPURenderPipeline WgPipelines::createRenderPipeline(
WGPUDevice device, const char* pipelineLabel,
const WGPUShaderModule shaderModule, const WGPUPipelineLayout pipelineLayout,
const WGPUVertexBufferLayout *vertexBufferLayouts, const uint32_t vertexBufferLayoutsCount,
const WGPUColorWriteMaskFlags writeMask,
const WGPUCompareFunction stencilFunctionFrnt, const WGPUStencilOperation stencilOperationFrnt,
const WGPUCompareFunction stencilFunctionBack, const WGPUStencilOperation stencilOperationBack,
const WGPUPrimitiveState primitiveState, const WGPUMultisampleState multisampleState, const WGPUBlendState blendState)
{
const WGPUColorTargetState colorTargetState { .format = WGPUTextureFormat_RGBA8Unorm, .blend = &blendState, .writeMask = writeMask };
const WGPUColorTargetState colorTargetStates[] { colorTargetState };
const WGPUDepthStencilState depthStencilState {
.format = WGPUTextureFormat_Stencil8, .depthCompare = WGPUCompareFunction_Always,
.stencilFront = { .compare = stencilFunctionFrnt, .failOp = stencilOperationFrnt, .depthFailOp = stencilOperationFrnt, .passOp = stencilOperationFrnt },
.stencilBack = { .compare = stencilFunctionBack, .failOp = stencilOperationBack, .depthFailOp = stencilOperationBack, .passOp = stencilOperationBack },
.stencilReadMask = 0xFFFFFFFF, .stencilWriteMask = 0xFFFFFFFF
};
const WGPUVertexState vertexState { .module = shaderModule, .entryPoint = "vs_main", .bufferCount = vertexBufferLayoutsCount, .buffers = vertexBufferLayouts };
const WGPUFragmentState fragmentState { .module = shaderModule, .entryPoint = "fs_main", .targetCount = 1, .targets = colorTargetStates };
const WGPURenderPipelineDescriptor renderPipelineDesc {
.label = pipelineLabel,
.layout = pipelineLayout,
.vertex = vertexState,
.primitive = primitiveState,
.depthStencil = &depthStencilState,
.multisample = multisampleState,
.fragment = &fragmentState
};
return wgpuDeviceCreateRenderPipeline(device, &renderPipelineDesc);
}
WGPUComputePipeline WgPipelines::createComputePipeline(
WGPUDevice device, const char* pipelineLabel,
const WGPUShaderModule shaderModule, const WGPUPipelineLayout pipelineLayout)
{
const WGPUComputePipelineDescriptor computePipelineDesc{
.label = pipelineLabel,
.layout = pipelineLayout,
.compute = {
.module = shaderModule,
.entryPoint = "cs_main"
}
};
return wgpuDeviceCreateComputePipeline(device, &computePipelineDesc);
}
void WgPipelines::releaseComputePipeline(WGPUComputePipeline& computePipeline)
{
if (computePipeline) {
wgpuComputePipelineRelease(computePipeline);
computePipeline = nullptr;
}
}
void WgPipelines::releaseRenderPipeline(WGPURenderPipeline& renderPipeline)
{
if (renderPipeline) {
wgpuRenderPipelineRelease(renderPipeline);
renderPipeline = nullptr;
}
}
void WgPipelines::releasePipelineLayout(WGPUPipelineLayout& pipelineLayout)
{
if (pipelineLayout) {
wgpuPipelineLayoutRelease(pipelineLayout);
pipelineLayout = nullptr;
}
}
void WgPipelines::releaseShaderModule(WGPUShaderModule& shaderModule)
{
if (shaderModule) {
wgpuShaderModuleRelease(shaderModule);
shaderModule = nullptr;
}
}
void WgPipelines::initialize(WgContext& context)
{
// share pipelines to context
assert(!context.pipelines);
context.pipelines = this;
// initialize bind group layouts
layouts.initialize(context);
// common pipeline settings
const WGPUVertexAttribute vertexAttributePos { .format = WGPUVertexFormat_Float32x2, .offset = 0, .shaderLocation = 0 };
const WGPUVertexAttribute vertexAttributeTex { .format = WGPUVertexFormat_Float32x2, .offset = 0, .shaderLocation = 1 };
const WGPUVertexAttribute vertexAttributesPos[] { vertexAttributePos };
const WGPUVertexAttribute vertexAttributesTex[] { vertexAttributeTex };
const WGPUVertexBufferLayout vertexBufferLayoutPos { .arrayStride = 8, .stepMode = WGPUVertexStepMode_Vertex, .attributeCount = 1, .attributes = vertexAttributesPos };
const WGPUVertexBufferLayout vertexBufferLayoutTex { .arrayStride = 8, .stepMode = WGPUVertexStepMode_Vertex, .attributeCount = 1, .attributes = vertexAttributesTex };
const WGPUVertexBufferLayout vertexBufferLayoutsShape[] { vertexBufferLayoutPos };
const WGPUVertexBufferLayout vertexBufferLayoutsImage[] { vertexBufferLayoutPos, vertexBufferLayoutTex };
const WGPUPrimitiveState primitiveState { .topology = WGPUPrimitiveTopology_TriangleList };
const WGPUMultisampleState multisampleState { .count = 1, .mask = 0xFFFFFFFF, .alphaToCoverageEnabled = false };
// blend states
const WGPUBlendState blendStateSrc {
.color = { .operation = WGPUBlendOperation_Add, .srcFactor = WGPUBlendFactor_One, .dstFactor = WGPUBlendFactor_Zero },
.alpha = { .operation = WGPUBlendOperation_Add, .srcFactor = WGPUBlendFactor_One, .dstFactor = WGPUBlendFactor_Zero }
};
const WGPUBlendState blendStateNrm {
.color = { .operation = WGPUBlendOperation_Add, .srcFactor = WGPUBlendFactor_One, .dstFactor = WGPUBlendFactor_OneMinusSrcAlpha },
.alpha = { .operation = WGPUBlendOperation_Add, .srcFactor = WGPUBlendFactor_One, .dstFactor = WGPUBlendFactor_OneMinusSrcAlpha }
};
const WGPUBlendState blendStates[] {
blendStateSrc, // WgPipelineBlendType::SrcOver
blendStateNrm, // WgPipelineBlendType::Normal
blendStateSrc // WgPipelineBlendType::Custom (same as SrcOver)
};
// bind group layouts
const WGPUBindGroupLayout bindGroupLayoutsStencil[] = {
layouts.layoutBuffer1Un,
layouts.layoutBuffer2Un
};
const WGPUBindGroupLayout bindGroupLayoutsFill[] = {
layouts.layoutBuffer1Un,
layouts.layoutBuffer2Un,
layouts.layoutBuffer1Un
};
const WGPUBindGroupLayout bindGroupLayoutsImage[] = {
layouts.layoutBuffer1Un,
layouts.layoutBuffer2Un,
layouts.layoutTexSampled
};
const WGPUBindGroupLayout bindGroupLayoutsMergeMasks[] = {
layouts.layoutTexStrorage1RO,
layouts.layoutTexStrorage1RO,
layouts.layoutTexStrorage1WO
};
const WGPUBindGroupLayout bindGroupLayoutsBlend[] = {
layouts.layoutTexStrorage1RO,
layouts.layoutTexStrorage1RO,
layouts.layoutTexStrorage1WO,
layouts.layoutBuffer1Un
};
const WGPUBindGroupLayout bindGroupLayoutsCompose[] = {
layouts.layoutTexStrorage1RO,
layouts.layoutTexStrorage1RO,
layouts.layoutTexStrorage1RO,
layouts.layoutTexStrorage1WO
};
const WGPUBindGroupLayout bindGroupLayoutsCopy[] = {
layouts.layoutTexStrorage1RO,
layouts.layoutTexScreen1WO
};
// pipeline layouts
layoutStencil = createPipelineLayout(context.device, bindGroupLayoutsStencil, 2);
layoutFill = createPipelineLayout(context.device, bindGroupLayoutsFill, 3);
layoutImage = createPipelineLayout(context.device, bindGroupLayoutsImage, 3);
layoutBlend = createPipelineLayout(context.device, bindGroupLayoutsBlend, 4);
layoutCompose = createPipelineLayout(context.device, bindGroupLayoutsCompose, 4);
layoutMergeMasks = createPipelineLayout(context.device, bindGroupLayoutsMergeMasks, 3);
layoutCopy = createPipelineLayout(context.device, bindGroupLayoutsCopy, 2);
// graphics shader modules
shaderStencil = createShaderModule(context.device, "The shader stencil", cShaderSrc_Stencil);
shaderSolid = createShaderModule(context.device, "The shader solid", cShaderSrc_Solid);
shaderRadial = createShaderModule(context.device, "The shader radial", cShaderSrc_Radial);
shaderLinear = createShaderModule(context.device, "The shader linear", cShaderSrc_Linear);
shaderImage = createShaderModule(context.device, "The shader image", cShaderSrc_Image);
// computes shader modules
shaderMergeMasks = createShaderModule(context.device, "The shader merge mask", cShaderSrc_MergeMasks);
shaderCopy = createShaderModule(context.device, "The shader copy", cShaderSrc_Copy);
// render pipeline winding
winding = createRenderPipeline(
context.device, "The render pipeline winding",
shaderStencil, layoutStencil,
vertexBufferLayoutsShape, 1,
WGPUColorWriteMask_None,
WGPUCompareFunction_Always, WGPUStencilOperation_IncrementWrap,
WGPUCompareFunction_Always, WGPUStencilOperation_DecrementWrap,
primitiveState, multisampleState, blendStateSrc);
// render pipeline even-odd
evenodd = createRenderPipeline(
context.device, "The render pipeline even-odd",
shaderStencil, layoutStencil,
vertexBufferLayoutsShape, 1,
WGPUColorWriteMask_None,
WGPUCompareFunction_Always, WGPUStencilOperation_Invert,
WGPUCompareFunction_Always, WGPUStencilOperation_Invert,
primitiveState, multisampleState, blendStateSrc);
// render pipeline direct
direct = createRenderPipeline(
context.device, "The render pipeline direct",
shaderStencil, layoutStencil,
vertexBufferLayoutsShape, 1,
WGPUColorWriteMask_None,
WGPUCompareFunction_Always, WGPUStencilOperation_Replace,
WGPUCompareFunction_Always, WGPUStencilOperation_Replace,
primitiveState, multisampleState, blendStateSrc);
// render pipeline clip path
clipPath = createRenderPipeline(
context.device, "The render pipeline clip path",
shaderStencil, layoutStencil,
vertexBufferLayoutsShape, 1,
WGPUColorWriteMask_All,
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
primitiveState, multisampleState, blendStateSrc);
// render pipeline solid
for (uint32_t i = 0; i < 3; i++) {
solid[i] = createRenderPipeline(
context.device, "The render pipeline solid",
shaderSolid, layoutFill,
vertexBufferLayoutsShape, 1,
WGPUColorWriteMask_All,
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
primitiveState, multisampleState, blendStates[i]);
}
// render pipeline radial
for (uint32_t i = 0; i < 3; i++) {
radial[i] = createRenderPipeline(
context.device, "The render pipeline radial",
shaderRadial, layoutFill,
vertexBufferLayoutsShape, 1,
WGPUColorWriteMask_All,
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
primitiveState, multisampleState, blendStates[i]);
}
// render pipeline linear
for (uint32_t i = 0; i < 3; i++) {
linear[i] = createRenderPipeline(
context.device, "The render pipeline linear",
shaderLinear, layoutFill,
vertexBufferLayoutsShape, 1,
WGPUColorWriteMask_All,
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
primitiveState, multisampleState, blendStates[i]);
}
// render pipeline image
for (uint32_t i = 0; i < 3; i++) {
image[i] = createRenderPipeline(
context.device, "The render pipeline image",
shaderImage, layoutImage,
vertexBufferLayoutsImage, 2,
WGPUColorWriteMask_All,
WGPUCompareFunction_Always, WGPUStencilOperation_Zero,
WGPUCompareFunction_Always, WGPUStencilOperation_Zero,
primitiveState, multisampleState, blendStates[i]);
}
// compute pipelines
mergeMasks = createComputePipeline(context.device, "The pipeline merge masks", shaderMergeMasks, layoutMergeMasks);
copy = createComputePipeline(context.device, "The pipeline copy", shaderCopy, layoutCopy);
// compute pipelines blend
const size_t shaderBlendCnt = sizeof(cShaderSrc_Blend_Solid)/sizeof(cShaderSrc_Blend_Solid[0]);
for (uint32_t i = 0; i < shaderBlendCnt; i++) {
// blend shaders
shaderBlendSolid[i] = createShaderModule(context.device, "The shader blend solid", cShaderSrc_Blend_Solid[i]);
shaderBlendGradient[i] = createShaderModule(context.device, "The shader blend gradient", cShaderSrc_Blend_Gradient[i]);
shaderBlendImage[i] = createShaderModule(context.device, "The shader blend image", cShaderSrc_Blend_Image[i]);
// blend pipelines
blendSolid[i] = createComputePipeline(context.device, "The pipeline blend solid", shaderBlendSolid[i], layoutBlend);
blendGradient[i] = createComputePipeline(context.device, "The pipeline blend gradient", shaderBlendGradient[i], layoutBlend);
blendImage[i] = createComputePipeline(context.device, "The pipeline blend image", shaderBlendImage[i], layoutBlend);
}
// compute pipelines compose
const size_t shaderComposeCnt = sizeof(cShaderSrc_Compose)/sizeof(cShaderSrc_Compose[0]);
for (uint32_t i = 0; i < shaderComposeCnt; i++) {
shaderCompose[i] = createShaderModule(context.device, "The shader compose", cShaderSrc_Compose[i]);
compose[i] = createComputePipeline(context.device, "The pipeline compose", shaderCompose[i], layoutCompose);
}
}
void WgPipelines::releaseGraphicHandles(WgContext& context)
{
releaseRenderPipeline(clipPath);
for (uint32_t i = 0; i < 3; i++) {
releaseRenderPipeline(image[i]);
releaseRenderPipeline(linear[i]);
releaseRenderPipeline(radial[i]);
releaseRenderPipeline(solid[i]);
}
releaseRenderPipeline(direct);
releaseRenderPipeline(evenodd);
releaseRenderPipeline(winding);
releasePipelineLayout(layoutImage);
releasePipelineLayout(layoutFill);
releasePipelineLayout(layoutStencil);
releaseShaderModule(shaderImage);
releaseShaderModule(shaderLinear);
releaseShaderModule(shaderRadial);
releaseShaderModule(shaderSolid);
releaseShaderModule(shaderStencil);
}
void WgPipelines::releaseComputeHandles(WgContext& context)
{
const size_t shaderComposeCnt = sizeof(shaderCompose)/sizeof(shaderCompose[0]);
for (uint32_t i = 0; i < shaderComposeCnt; i++) {
releaseComputePipeline(compose[i]);
releaseShaderModule(shaderCompose[i]);
}
const size_t shaderBlendImageCnt = sizeof(shaderBlendImage)/sizeof(shaderBlendImage[0]);
for (uint32_t i = 0; i < shaderBlendImageCnt; i++) {
releaseComputePipeline(blendImage[i]);
releaseComputePipeline(blendSolid[i]);
releaseComputePipeline(blendGradient[i]);
releaseShaderModule(shaderBlendImage[i]);
releaseShaderModule(shaderBlendGradient[i]);
releaseShaderModule(shaderBlendSolid[i]);
}
releaseComputePipeline(copy);
releaseComputePipeline(mergeMasks);
releasePipelineLayout(layoutCompose);
releasePipelineLayout(layoutBlend);
releasePipelineLayout(layoutMergeMasks);
releasePipelineLayout(layoutCopy);
releaseShaderModule(shaderMergeMasks);
releaseShaderModule(shaderCopy);
}
void WgPipelines::release(WgContext& context)
{
releaseComputeHandles(context);
releaseGraphicHandles(context);
layouts.release(context);
}