mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-14 12:04:29 +00:00

* 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)
381 lines
17 KiB
C++
Executable file
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);
|
|
}
|