mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 13:43:43 +00:00
wg_engine: Blending optimization
[issues 1479: lottie](#1479) To optimize bled operations hardware pipeline blend stage are used for some blend methods: BlendMethod::SrcOver BlendMethod::Normal BlendMethod::Add BlendMethod::Multiply BlendMethod::Darken BlendMethod::Lighten Other types compute shaders used
This commit is contained in:
parent
622b25a3b4
commit
b0280150db
8 changed files with 302 additions and 230 deletions
|
@ -494,7 +494,7 @@ void WgPipeline::destroyShaderModule(WGPUShaderModule& shaderModule)
|
||||||
// render pipeline
|
// render pipeline
|
||||||
//*****************************************************************************
|
//*****************************************************************************
|
||||||
|
|
||||||
void WgRenderPipeline::allocate(WGPUDevice device,
|
void WgRenderPipeline::allocate(WGPUDevice device, WgPipelineBlendType blendType,
|
||||||
WGPUVertexBufferLayout vertexBufferLayouts[], uint32_t attribsCount,
|
WGPUVertexBufferLayout vertexBufferLayouts[], uint32_t attribsCount,
|
||||||
WGPUBindGroupLayout bindGroupLayouts[], uint32_t bindGroupsCount,
|
WGPUBindGroupLayout bindGroupLayouts[], uint32_t bindGroupsCount,
|
||||||
WGPUCompareFunction stencilCompareFunction, WGPUStencilOperation stencilOperation,
|
WGPUCompareFunction stencilCompareFunction, WGPUStencilOperation stencilOperation,
|
||||||
|
@ -506,7 +506,7 @@ void WgRenderPipeline::allocate(WGPUDevice device,
|
||||||
mPipelineLayout = createPipelineLayout(device, bindGroupLayouts, bindGroupsCount);
|
mPipelineLayout = createPipelineLayout(device, bindGroupLayouts, bindGroupsCount);
|
||||||
assert(mPipelineLayout);
|
assert(mPipelineLayout);
|
||||||
|
|
||||||
mRenderPipeline = createRenderPipeline(device,
|
mRenderPipeline = createRenderPipeline(device, blendType,
|
||||||
vertexBufferLayouts, attribsCount,
|
vertexBufferLayouts, attribsCount,
|
||||||
stencilCompareFunction, stencilOperation,
|
stencilCompareFunction, stencilOperation,
|
||||||
mPipelineLayout, mShaderModule, pipelineLabel);
|
mPipelineLayout, mShaderModule, pipelineLabel);
|
||||||
|
@ -527,14 +527,42 @@ void WgRenderPipeline::set(WGPURenderPassEncoder renderPassEncoder)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
WGPUBlendState WgRenderPipeline::makeBlendState()
|
WGPUBlendState WgRenderPipeline::makeBlendState(WgPipelineBlendType blendType)
|
||||||
{
|
{
|
||||||
WGPUBlendState blendState{};
|
WGPUBlendState blendState{};
|
||||||
blendState.color.operation = WGPUBlendOperation_Add;
|
// src
|
||||||
blendState.color.srcFactor = WGPUBlendFactor_One;
|
if (blendType == WgPipelineBlendType::Src) {
|
||||||
blendState.color.dstFactor = WGPUBlendFactor_Zero;
|
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.operation = WGPUBlendOperation_Add;
|
||||||
blendState.alpha.srcFactor = WGPUBlendFactor_One;
|
blendState.alpha.srcFactor = WGPUBlendFactor_SrcAlpha;
|
||||||
blendState.alpha.dstFactor = WGPUBlendFactor_Zero;
|
blendState.alpha.dstFactor = WGPUBlendFactor_Zero;
|
||||||
return blendState;
|
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,
|
WGPUVertexBufferLayout vertexBufferLayouts[], uint32_t attribsCount,
|
||||||
WGPUCompareFunction stencilCompareFunction, WGPUStencilOperation stencilOperation,
|
WGPUCompareFunction stencilCompareFunction, WGPUStencilOperation stencilOperation,
|
||||||
WGPUPipelineLayout pipelineLayout, WGPUShaderModule shaderModule,
|
WGPUPipelineLayout pipelineLayout, WGPUShaderModule shaderModule,
|
||||||
const char* pipelineName)
|
const char* pipelineName)
|
||||||
{
|
{
|
||||||
WGPUBlendState blendState = makeBlendState();
|
WGPUBlendState blendState = makeBlendState(blendType);
|
||||||
WGPUColorTargetState colorTargetStates[] = {
|
WGPUColorTargetState colorTargetStates[] = {
|
||||||
makeColorTargetState(&blendState)
|
makeColorTargetState(&blendState)
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,6 +28,15 @@
|
||||||
#include "tvgCommon.h"
|
#include "tvgCommon.h"
|
||||||
#include "tvgRender.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 WgPipelines;
|
||||||
|
|
||||||
struct WgContext {
|
struct WgContext {
|
||||||
|
@ -107,7 +116,7 @@ struct WgRenderPipeline: public WgPipeline
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
WGPURenderPipeline mRenderPipeline{};
|
WGPURenderPipeline mRenderPipeline{};
|
||||||
void allocate(WGPUDevice device,
|
void allocate(WGPUDevice device, WgPipelineBlendType blendType,
|
||||||
WGPUVertexBufferLayout vertexBufferLayouts[], uint32_t attribsCount,
|
WGPUVertexBufferLayout vertexBufferLayouts[], uint32_t attribsCount,
|
||||||
WGPUBindGroupLayout bindGroupLayouts[], uint32_t bindGroupsCount,
|
WGPUBindGroupLayout bindGroupLayouts[], uint32_t bindGroupsCount,
|
||||||
WGPUCompareFunction stencilCompareFunction, WGPUStencilOperation stencilOperation,
|
WGPUCompareFunction stencilCompareFunction, WGPUStencilOperation stencilOperation,
|
||||||
|
@ -116,7 +125,7 @@ public:
|
||||||
void release() override;
|
void release() override;
|
||||||
void set(WGPURenderPassEncoder renderPassEncoder);
|
void set(WGPURenderPassEncoder renderPassEncoder);
|
||||||
|
|
||||||
static WGPUBlendState makeBlendState();
|
static WGPUBlendState makeBlendState(WgPipelineBlendType blendType);
|
||||||
static WGPUColorTargetState makeColorTargetState(const WGPUBlendState* blendState);
|
static WGPUColorTargetState makeColorTargetState(const WGPUBlendState* blendState);
|
||||||
static WGPUVertexBufferLayout makeVertexBufferLayout(const WGPUVertexAttribute* vertexAttributes, uint32_t count, uint64_t stride);
|
static WGPUVertexBufferLayout makeVertexBufferLayout(const WGPUVertexAttribute* vertexAttributes, uint32_t count, uint64_t stride);
|
||||||
static WGPUVertexState makeVertexState(WGPUShaderModule shaderModule, const WGPUVertexBufferLayout* buffers, uint32_t count);
|
static WGPUVertexState makeVertexState(WGPUShaderModule shaderModule, const WGPUVertexBufferLayout* buffers, uint32_t count);
|
||||||
|
@ -125,7 +134,7 @@ public:
|
||||||
static WGPUMultisampleState makeMultisampleState();
|
static WGPUMultisampleState makeMultisampleState();
|
||||||
static WGPUFragmentState makeFragmentState(WGPUShaderModule shaderModule, WGPUColorTargetState* targets, uint32_t size);
|
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,
|
WGPUVertexBufferLayout vertexBufferLayouts[], uint32_t attribsCount,
|
||||||
WGPUCompareFunction stencilCompareFunction, WGPUStencilOperation stencilOperation,
|
WGPUCompareFunction stencilCompareFunction, WGPUStencilOperation stencilOperation,
|
||||||
WGPUPipelineLayout pipelineLayout, WGPUShaderModule shaderModule,
|
WGPUPipelineLayout pipelineLayout, WGPUShaderModule shaderModule,
|
||||||
|
|
|
@ -53,7 +53,7 @@ void WgPipelineFillShape::initialize(WGPUDevice device)
|
||||||
auto pipelineLabel = "The render pipeline fill shape";
|
auto pipelineLabel = "The render pipeline fill shape";
|
||||||
|
|
||||||
// allocate all pipeline handles
|
// allocate all pipeline handles
|
||||||
allocate(device,
|
allocate(device, WgPipelineBlendType::Src,
|
||||||
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
|
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
|
||||||
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
||||||
stencilFuncion, stencilOperation,
|
stencilFuncion, stencilOperation,
|
||||||
|
@ -85,7 +85,7 @@ void WgPipelineFillStroke::initialize(WGPUDevice device)
|
||||||
auto pipelineLabel = "The render pipeline fill stroke";
|
auto pipelineLabel = "The render pipeline fill stroke";
|
||||||
|
|
||||||
// allocate all pipeline handles
|
// allocate all pipeline handles
|
||||||
allocate(device,
|
allocate(device, WgPipelineBlendType::Src,
|
||||||
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
|
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
|
||||||
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
||||||
stencilFuncion, stencilOperation,
|
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
|
// vertex and buffers settings
|
||||||
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
|
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
|
||||||
|
@ -118,7 +118,7 @@ void WgPipelineSolid::initialize(WGPUDevice device)
|
||||||
auto pipelineLabel = "The render pipeline solid color";
|
auto pipelineLabel = "The render pipeline solid color";
|
||||||
|
|
||||||
// allocate all pipeline handles
|
// allocate all pipeline handles
|
||||||
allocate(device,
|
allocate(device, blendType,
|
||||||
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
|
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
|
||||||
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
||||||
stencilFuncion, stencilOperation,
|
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
|
// vertex and buffers settings
|
||||||
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
|
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
|
||||||
|
@ -151,7 +151,7 @@ void WgPipelineLinear::initialize(WGPUDevice device)
|
||||||
auto pipelineLabel = "The render pipeline linear gradient";
|
auto pipelineLabel = "The render pipeline linear gradient";
|
||||||
|
|
||||||
// allocate all pipeline handles
|
// allocate all pipeline handles
|
||||||
allocate(device,
|
allocate(device, blendType,
|
||||||
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
|
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
|
||||||
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
||||||
stencilFuncion, stencilOperation,
|
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
|
// vertex and buffers settings
|
||||||
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
|
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
|
||||||
|
@ -184,7 +184,7 @@ void WgPipelineRadial::initialize(WGPUDevice device)
|
||||||
auto pipelineLabel = "The render pipeline radial gradient";
|
auto pipelineLabel = "The render pipeline radial gradient";
|
||||||
|
|
||||||
// allocate all pipeline handles
|
// allocate all pipeline handles
|
||||||
allocate(device,
|
allocate(device, blendType,
|
||||||
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
|
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
|
||||||
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
||||||
stencilFuncion, stencilOperation,
|
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
|
// vertex and buffers settings
|
||||||
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
|
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
|
||||||
|
@ -219,7 +219,7 @@ void WgPipelineImage::initialize(WGPUDevice device)
|
||||||
auto pipelineLabel = "The render pipeline image";
|
auto pipelineLabel = "The render pipeline image";
|
||||||
|
|
||||||
// allocate all pipeline handles
|
// allocate all pipeline handles
|
||||||
allocate(device,
|
allocate(device, blendType,
|
||||||
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
|
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
|
||||||
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
||||||
stencilFuncion, stencilOperation,
|
stencilFuncion, stencilOperation,
|
||||||
|
@ -320,10 +320,12 @@ void WgPipelines::initialize(WgContext& context)
|
||||||
// fill pipelines
|
// fill pipelines
|
||||||
fillShape.initialize(context.device);
|
fillShape.initialize(context.device);
|
||||||
fillStroke.initialize(context.device);
|
fillStroke.initialize(context.device);
|
||||||
solid.initialize(context.device);
|
for (uint8_t type = (uint8_t)WgPipelineBlendType::Src; type <= (uint8_t)WgPipelineBlendType::Max; type++) {
|
||||||
linear.initialize(context.device);
|
solid[type].initialize(context.device, (WgPipelineBlendType)type);
|
||||||
radial.initialize(context.device);
|
linear[type].initialize(context.device, (WgPipelineBlendType)type);
|
||||||
image.initialize(context.device);
|
radial[type].initialize(context.device, (WgPipelineBlendType)type);
|
||||||
|
image[type].initialize(context.device, (WgPipelineBlendType)type);
|
||||||
|
}
|
||||||
// compute pipelines
|
// compute pipelines
|
||||||
computeClear.initialize(context.device);
|
computeClear.initialize(context.device);
|
||||||
computeBlend.initialize(context.device);
|
computeBlend.initialize(context.device);
|
||||||
|
@ -352,10 +354,41 @@ void WgPipelines::release()
|
||||||
computeBlend.release();
|
computeBlend.release();
|
||||||
computeClear.release();
|
computeClear.release();
|
||||||
// fill pipelines
|
// fill pipelines
|
||||||
image.release();
|
for (uint8_t type = (uint8_t)WgPipelineBlendType::Src; type <= (uint8_t)WgPipelineBlendType::Max; type++) {
|
||||||
radial.release();
|
image[type].release();
|
||||||
linear.release();
|
radial[type].release();
|
||||||
solid.release();
|
linear[type].release();
|
||||||
|
solid[type].release();
|
||||||
|
}
|
||||||
fillStroke.release();
|
fillStroke.release();
|
||||||
fillShape.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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -53,7 +53,8 @@ struct WgPipelineFillStroke: public WgRenderPipeline
|
||||||
|
|
||||||
struct WgPipelineSolid: 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)
|
void use(WGPURenderPassEncoder encoder, WgBindGroupCanvas& groupCanvas,WgBindGroupPaint& groupPaint, WgBindGroupSolidColor& groupSolid)
|
||||||
{
|
{
|
||||||
set(encoder);
|
set(encoder);
|
||||||
|
@ -65,7 +66,8 @@ struct WgPipelineSolid: public WgRenderPipeline
|
||||||
|
|
||||||
struct WgPipelineLinear: 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)
|
void use(WGPURenderPassEncoder encoder, WgBindGroupCanvas& groupCanvas, WgBindGroupPaint& groupPaint, WgBindGroupLinearGradient& groupLinear)
|
||||||
{
|
{
|
||||||
set(encoder);
|
set(encoder);
|
||||||
|
@ -77,7 +79,8 @@ struct WgPipelineLinear: public WgRenderPipeline
|
||||||
|
|
||||||
struct WgPipelineRadial: 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)
|
void use(WGPURenderPassEncoder encoder, WgBindGroupCanvas& groupCanvas, WgBindGroupPaint& groupPaint, WgBindGroupRadialGradient& groupRadial)
|
||||||
{
|
{
|
||||||
set(encoder);
|
set(encoder);
|
||||||
|
@ -89,7 +92,8 @@ struct WgPipelineRadial: public WgRenderPipeline
|
||||||
|
|
||||||
struct WgPipelineImage: 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)
|
void use(WGPURenderPassEncoder encoder, WgBindGroupCanvas& groupCanvas, WgBindGroupPaint& groupPaint, WgBindGroupPicture& groupPicture)
|
||||||
{
|
{
|
||||||
set(encoder);
|
set(encoder);
|
||||||
|
@ -161,10 +165,11 @@ struct WgPipelines
|
||||||
// render pipelines
|
// render pipelines
|
||||||
WgPipelineFillShape fillShape;
|
WgPipelineFillShape fillShape;
|
||||||
WgPipelineFillStroke fillStroke;
|
WgPipelineFillStroke fillStroke;
|
||||||
WgPipelineSolid solid;
|
// fill pipelines
|
||||||
WgPipelineLinear linear;
|
WgPipelineSolid solid[6];
|
||||||
WgPipelineRadial radial;
|
WgPipelineLinear linear[6];
|
||||||
WgPipelineImage image;
|
WgPipelineRadial radial[6];
|
||||||
|
WgPipelineImage image[6];
|
||||||
// compute pipelines
|
// compute pipelines
|
||||||
WgPipelineClear computeClear;
|
WgPipelineClear computeClear;
|
||||||
WgPipelineBlend computeBlend;
|
WgPipelineBlend computeBlend;
|
||||||
|
@ -173,6 +178,9 @@ struct WgPipelines
|
||||||
|
|
||||||
void initialize(WgContext& context);
|
void initialize(WgContext& context);
|
||||||
void release();
|
void release();
|
||||||
|
|
||||||
|
static bool isBlendMethodSupportsHW(BlendMethod blendMethod);
|
||||||
|
static WgPipelineBlendType blendMethodToBlendType(BlendMethod blendMethod);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _TVG_WG_PIPELINES_H_
|
#endif // _TVG_WG_PIPELINES_H_
|
||||||
|
|
|
@ -23,21 +23,30 @@
|
||||||
#include "tvgWgRenderTarget.h"
|
#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);
|
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
|
// create color and stencil textures
|
||||||
texColor = context.createTexture2d(
|
texColor = context.createTexture2d(
|
||||||
WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_StorageBinding,
|
WGPUTextureUsage_CopySrc |
|
||||||
|
WGPUTextureUsage_CopyDst |
|
||||||
|
WGPUTextureUsage_TextureBinding |
|
||||||
|
WGPUTextureUsage_StorageBinding |
|
||||||
|
WGPUTextureUsage_RenderAttachment,
|
||||||
WGPUTextureFormat_RGBA8Unorm,
|
WGPUTextureFormat_RGBA8Unorm,
|
||||||
w * samples, h * samples, "The target texture color");
|
width, height, "The target texture color");
|
||||||
texStencil = context.createTexture2d(
|
texStencil = context.createTexture2d(
|
||||||
WGPUTextureUsage_RenderAttachment,
|
WGPUTextureUsage_RenderAttachment,
|
||||||
WGPUTextureFormat_Stencil8,
|
WGPUTextureFormat_Stencil8,
|
||||||
w * samples, h * samples, "The target texture stencil");
|
width, height, "The target texture stencil");
|
||||||
assert(texColor);
|
assert(texColor);
|
||||||
assert(texStencil);
|
assert(texStencil);
|
||||||
texViewColor = context.createTextureView2d(texColor, "The target texture view color");
|
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);
|
WgShaderTypeMat4x4f viewMat(w, h);
|
||||||
mBindGroupCanvas.initialize(context.device, context.queue, viewMat);
|
mBindGroupCanvas.initialize(context.device, context.queue, viewMat);
|
||||||
mPipelines = context.pipelines;
|
mPipelines = context.pipelines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgRenderTarget::release(WgContext& context)
|
void WgRenderStorage::release(WgContext& context)
|
||||||
{
|
{
|
||||||
|
mRenderPassEncoder = nullptr;
|
||||||
mBindGroupCanvas.release();
|
mBindGroupCanvas.release();
|
||||||
bindGroupTexStorage.release();
|
bindGroupTexStorage.release();
|
||||||
context.releaseTextureView(texViewStencil);
|
context.releaseTextureView(texViewStencil);
|
||||||
context.releaseTextureView(texViewColor);
|
context.releaseTextureView(texViewColor);
|
||||||
context.releaseTexture(texStencil);
|
context.releaseTexture(texStencil);
|
||||||
context.releaseTexture(texColor);
|
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(renderData);
|
||||||
assert(commandEncoder);
|
assert(mRenderPassEncoder);
|
||||||
WGPURenderPassEncoder renderPassEncoder = beginRenderPass(commandEncoder);
|
|
||||||
if (renderData->strokeFirst)
|
if (renderData->strokeFirst)
|
||||||
drawStroke(renderPassEncoder, renderData);
|
drawStroke(renderData, blendType);
|
||||||
drawShape(renderPassEncoder, renderData);
|
drawShape(renderData, blendType);
|
||||||
if (!renderData->strokeFirst)
|
if (!renderData->strokeFirst)
|
||||||
drawStroke(renderPassEncoder, renderData);
|
drawStroke(renderData, blendType);
|
||||||
endRenderPass(renderPassEncoder);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgRenderTarget::renderPicture(WGPUCommandEncoder commandEncoder, WgRenderDataPicture* renderData)
|
void WgRenderStorage::renderPicture(WgRenderDataPicture* renderData, WgPipelineBlendType blendType)
|
||||||
{
|
{
|
||||||
assert(renderData);
|
assert(renderData);
|
||||||
assert(commandEncoder);
|
assert(mRenderPassEncoder);
|
||||||
WGPURenderPassEncoder renderPassEncoder = beginRenderPass(commandEncoder);
|
uint8_t blend = (uint8_t)blendType;
|
||||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
|
||||||
mPipelines->image.use(renderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, renderData->bindGroupPicture);
|
mPipelines->image[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, renderData->bindGroupPicture);
|
||||||
renderData->meshData.drawImage(renderPassEncoder);
|
renderData->meshData.drawImage(mRenderPassEncoder);
|
||||||
endRenderPass(renderPassEncoder);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgRenderTarget::drawShape(WGPURenderPassEncoder renderPassEncoder, WgRenderDataShape* renderData)
|
void WgRenderStorage::drawShape(WgRenderDataShape* renderData, WgPipelineBlendType blendType)
|
||||||
{
|
{
|
||||||
assert(renderData);
|
assert(renderData);
|
||||||
assert(renderPassEncoder);
|
assert(mRenderPassEncoder);
|
||||||
// draw shape geometry
|
// 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++) {
|
for (uint32_t i = 0; i < renderData->meshGroupShapes.meshes.count; i++) {
|
||||||
// draw to stencil (first pass)
|
// draw to stencil (first pass)
|
||||||
mPipelines->fillShape.use(renderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint);
|
mPipelines->fillShape.use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint);
|
||||||
renderData->meshGroupShapes.meshes[i]->draw(renderPassEncoder);
|
renderData->meshGroupShapes.meshes[i]->draw(mRenderPassEncoder);
|
||||||
// fill shape (second pass)
|
// fill shape (second pass)
|
||||||
WgRenderSettings& settings = renderData->renderSettingsShape;
|
WgRenderSettings& settings = renderData->renderSettingsShape;
|
||||||
if (settings.fillType == WgRenderSettingsType::Solid)
|
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)
|
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)
|
else if (settings.fillType == WgRenderSettingsType::Radial)
|
||||||
mPipelines->radial.use(renderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupRadial);
|
mPipelines->radial[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupRadial);
|
||||||
renderData->meshBBoxShapes.draw(renderPassEncoder);
|
renderData->meshBBoxShapes.draw(mRenderPassEncoder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgRenderTarget::drawStroke(WGPURenderPassEncoder renderPassEncoder, WgRenderDataShape* renderData)
|
void WgRenderStorage::drawStroke(WgRenderDataShape* renderData, WgPipelineBlendType blendType)
|
||||||
{
|
{
|
||||||
assert(renderData);
|
assert(renderData);
|
||||||
assert(renderPassEncoder);
|
assert(mRenderPassEncoder);
|
||||||
// draw stroke geometry
|
// draw stroke geometry
|
||||||
|
uint8_t blend = (uint8_t)blendType;
|
||||||
if (renderData->meshGroupStrokes.meshes.count > 0) {
|
if (renderData->meshGroupStrokes.meshes.count > 0) {
|
||||||
// draw strokes to stencil (first pass)
|
// draw strokes to stencil (first pass)
|
||||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255);
|
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 255);
|
||||||
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
|
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
|
||||||
mPipelines->fillStroke.use(renderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint);
|
mPipelines->fillStroke.use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint);
|
||||||
renderData->meshGroupStrokes.meshes[i]->draw(renderPassEncoder);
|
renderData->meshGroupStrokes.meshes[i]->draw(mRenderPassEncoder);
|
||||||
}
|
}
|
||||||
// fill shape (second pass)
|
// fill shape (second pass)
|
||||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
|
||||||
WgRenderSettings& settings = renderData->renderSettingsStroke;
|
WgRenderSettings& settings = renderData->renderSettingsStroke;
|
||||||
if (settings.fillType == WgRenderSettingsType::Solid)
|
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)
|
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)
|
else if (settings.fillType == WgRenderSettingsType::Radial)
|
||||||
mPipelines->radial.use(renderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupRadial);
|
mPipelines->radial[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupRadial);
|
||||||
renderData->meshBBoxStrokes.draw(renderPassEncoder);
|
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);
|
assert(commandEncoder);
|
||||||
// render pass depth stencil attachment
|
// render pass depth stencil attachment
|
||||||
|
@ -157,7 +220,7 @@ WGPURenderPassEncoder WgRenderTarget::beginRenderPass(WGPUCommandEncoder command
|
||||||
WGPURenderPassColorAttachment colorAttachment{};
|
WGPURenderPassColorAttachment colorAttachment{};
|
||||||
colorAttachment.view = texViewColor;
|
colorAttachment.view = texViewColor;
|
||||||
colorAttachment.resolveTarget = nullptr;
|
colorAttachment.resolveTarget = nullptr;
|
||||||
colorAttachment.loadOp = WGPULoadOp_Clear;
|
colorAttachment.loadOp = clear ? WGPULoadOp_Clear : WGPULoadOp_Load;
|
||||||
colorAttachment.clearValue = { 0, 0, 0, 0 };
|
colorAttachment.clearValue = { 0, 0, 0, 0 };
|
||||||
colorAttachment.storeOp = WGPUStoreOp_Store;
|
colorAttachment.storeOp = WGPUStoreOp_Store;
|
||||||
// render pass descriptor
|
// render pass descriptor
|
||||||
|
@ -171,111 +234,16 @@ WGPURenderPassEncoder WgRenderTarget::beginRenderPass(WGPUCommandEncoder command
|
||||||
renderPassDesc.occlusionQuerySet = nullptr;
|
renderPassDesc.occlusionQuerySet = nullptr;
|
||||||
renderPassDesc.timestampWrites = nullptr;
|
renderPassDesc.timestampWrites = nullptr;
|
||||||
// begin render pass
|
// begin render pass
|
||||||
return wgpuCommandEncoderBeginRenderPass(commandEncoder, &renderPassDesc);
|
mRenderPassEncoder = wgpuCommandEncoderBeginRenderPass(commandEncoder, &renderPassDesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgRenderTarget::endRenderPass(WGPURenderPassEncoder renderPassEncoder)
|
void WgRenderStorage::endRenderPass()
|
||||||
{
|
{
|
||||||
assert(renderPassEncoder);
|
assert(mRenderPassEncoder);
|
||||||
wgpuRenderPassEncoderEnd(renderPassEncoder);
|
wgpuRenderPassEncoderEnd(mRenderPassEncoder);
|
||||||
wgpuRenderPassEncoderRelease(renderPassEncoder);
|
wgpuRenderPassEncoderRelease(mRenderPassEncoder);
|
||||||
}
|
mRenderPassEncoder = nullptr;
|
||||||
|
|
||||||
//*****************************************************************************
|
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -290,7 +258,7 @@ WGPUComputePassEncoder WgRenderStorage::beginComputePass(WGPUCommandEncoder comm
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
void WgRenderStorage::endRenderPass(WGPUComputePassEncoder computePassEncoder)
|
void WgRenderStorage::endComputePass(WGPUComputePassEncoder computePassEncoder)
|
||||||
{
|
{
|
||||||
assert(computePassEncoder);
|
assert(computePassEncoder);
|
||||||
wgpuComputePassEncoderEnd(computePassEncoder);
|
wgpuComputePassEncoderEnd(computePassEncoder);
|
||||||
|
|
|
@ -25,10 +25,11 @@
|
||||||
|
|
||||||
#include "tvgWgRenderData.h"
|
#include "tvgWgRenderData.h"
|
||||||
|
|
||||||
class WgRenderTarget {
|
class WgRenderStorage {
|
||||||
private:
|
private:
|
||||||
// canvas info
|
// texture buffers
|
||||||
WgBindGroupCanvas mBindGroupCanvas;
|
WgBindGroupCanvas mBindGroupCanvas;
|
||||||
|
WGPURenderPassEncoder mRenderPassEncoder{};
|
||||||
WgPipelines* mPipelines{}; // external handle
|
WgPipelines* mPipelines{}; // external handle
|
||||||
public:
|
public:
|
||||||
WGPUTexture texColor{};
|
WGPUTexture texColor{};
|
||||||
|
@ -36,29 +37,6 @@ public:
|
||||||
WGPUTextureView texViewColor{};
|
WGPUTextureView texViewColor{};
|
||||||
WGPUTextureView texViewStencil{};
|
WGPUTextureView texViewStencil{};
|
||||||
WgBindGroupTextureStorage bindGroupTexStorage;
|
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 width{};
|
||||||
uint32_t height{};
|
uint32_t height{};
|
||||||
uint32_t workgroupsCountX{};
|
uint32_t workgroupsCountX{};
|
||||||
|
@ -67,16 +45,24 @@ public:
|
||||||
void initialize(WgContext& context, uint32_t w, uint32_t h, uint32_t samples = 1);
|
void initialize(WgContext& context, uint32_t w, uint32_t h, uint32_t samples = 1);
|
||||||
void release(WgContext& context);
|
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 clear(WGPUCommandEncoder commandEncoder);
|
||||||
void blend(WGPUCommandEncoder commandEncoder, WgRenderTarget* targetSrc, WgBindGroupBlendMethod* blendMethod);
|
|
||||||
void blend(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetSrc, WgBindGroupBlendMethod* blendMethod);
|
void blend(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetSrc, WgBindGroupBlendMethod* blendMethod);
|
||||||
void compose(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetMsk, WgBindGroupCompositeMethod* composeMethod, WgBindGroupOpacity* opacity);
|
void compose(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetMsk, WgBindGroupCompositeMethod* composeMethod, WgBindGroupOpacity* opacity);
|
||||||
void antialias(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetSrc);
|
void antialias(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetSrc);
|
||||||
private:
|
private:
|
||||||
|
void drawShape(WgRenderDataShape* renderData, WgPipelineBlendType blendType);
|
||||||
|
void drawStroke(WgRenderDataShape* renderData, WgPipelineBlendType blendType);
|
||||||
|
|
||||||
void dispatchWorkgroups(WGPUComputePassEncoder computePassEncoder);
|
void dispatchWorkgroups(WGPUComputePassEncoder computePassEncoder);
|
||||||
|
|
||||||
WGPUComputePassEncoder beginComputePass(WGPUCommandEncoder commandEncoder);
|
WGPUComputePassEncoder beginComputePass(WGPUCommandEncoder commandEncoder);
|
||||||
void endRenderPass(WGPUComputePassEncoder computePassEncoder);
|
void endComputePass(WGPUComputePassEncoder computePassEncoder);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -133,43 +133,74 @@ RenderData WgRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderD
|
||||||
return renderDataPicture;
|
return renderDataPicture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool WgRenderer::preRender()
|
bool WgRenderer::preRender()
|
||||||
{
|
{
|
||||||
WGPUCommandEncoderDescriptor commandEncoderDesc{};
|
WGPUCommandEncoderDescriptor commandEncoderDesc{};
|
||||||
commandEncoderDesc.nextInChain = nullptr;
|
commandEncoderDesc.nextInChain = nullptr;
|
||||||
commandEncoderDesc.label = "The command encoder";
|
commandEncoderDesc.label = "The command encoder";
|
||||||
mCommandEncoder = wgpuDeviceCreateCommandEncoder(mContext.device, &commandEncoderDesc);
|
mCommandEncoder = wgpuDeviceCreateCommandEncoder(mContext.device, &commandEncoderDesc);
|
||||||
mRenderStorageRoot.clear(mCommandEncoder);
|
|
||||||
mRenderStorageStack.push(&mRenderStorageRoot);
|
mRenderStorageStack.push(&mRenderStorageRoot);
|
||||||
|
//mRenderStorageRoot.clear(mCommandEncoder);
|
||||||
|
mRenderStorageRoot.beginRenderPass(mCommandEncoder, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool WgRenderer::renderShape(RenderData data)
|
bool WgRenderer::renderShape(RenderData data)
|
||||||
{
|
{
|
||||||
// render shape to render target
|
// get current render storage
|
||||||
mRenderTarget.renderShape(mCommandEncoder, (WgRenderDataShape *)data);
|
WgPipelineBlendType blendType = WgPipelines::blendMethodToBlendType(mBlendMethod);
|
||||||
// blend shape with current render storage
|
WgRenderStorage* renderStorage = mRenderStorageStack.last();
|
||||||
WgBindGroupBlendMethod* blendMethod = mBlendMethodPool.allocate(mContext, mBlendMethod);
|
assert(renderStorage);
|
||||||
mRenderStorageStack.last()->blend(mCommandEncoder, &mRenderTarget, blendMethod);
|
// 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool WgRenderer::renderImage(RenderData data)
|
bool WgRenderer::renderImage(RenderData data)
|
||||||
{
|
{
|
||||||
// render image to render target
|
// get current render storage
|
||||||
mRenderTarget.renderPicture(mCommandEncoder, (WgRenderDataPicture *)data);
|
WgPipelineBlendType blendType = WgPipelines::blendMethodToBlendType(mBlendMethod);
|
||||||
// blend image with current render storage
|
WgRenderStorage* renderStorage = mRenderStorageStack.last();
|
||||||
WgBindGroupBlendMethod* blendMethod = mBlendMethodPool.allocate(mContext, mBlendMethod);
|
assert(renderStorage);
|
||||||
mRenderStorageStack.last()->blend(mCommandEncoder, &mRenderTarget, blendMethod);
|
// 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool WgRenderer::postRender()
|
bool WgRenderer::postRender()
|
||||||
{
|
{
|
||||||
|
mRenderStorageRoot.endRenderPass();
|
||||||
mRenderStorageStack.pop();
|
mRenderStorageStack.pop();
|
||||||
mContext.executeCommandEncoder(mCommandEncoder);
|
mContext.executeCommandEncoder(mCommandEncoder);
|
||||||
wgpuCommandEncoderRelease(mCommandEncoder);
|
wgpuCommandEncoderRelease(mCommandEncoder);
|
||||||
|
@ -233,7 +264,7 @@ bool WgRenderer::sync()
|
||||||
mRenderStorageScreen.antialias(commandEncoder, &mRenderStorageRoot);
|
mRenderStorageScreen.antialias(commandEncoder, &mRenderStorageRoot);
|
||||||
|
|
||||||
WGPUImageCopyTexture source{};
|
WGPUImageCopyTexture source{};
|
||||||
source.texture = mRenderStorageScreen.texStorage;
|
source.texture = mRenderStorageScreen.texColor;
|
||||||
WGPUImageCopyTexture dest{};
|
WGPUImageCopyTexture dest{};
|
||||||
dest.texture = backBuffer.texture;
|
dest.texture = backBuffer.texture;
|
||||||
WGPUExtent3D copySize{};
|
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)
|
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->method = method;
|
||||||
cmp->opacity = opacity;
|
cmp->opacity = opacity;
|
||||||
|
|
||||||
|
// end current render pass
|
||||||
|
mRenderStorageStack.last()->endRenderPass();
|
||||||
// allocate new render storage and push it to top of render tree
|
// 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);
|
WgRenderStorage* renderStorage = mRenderStoragePool.allocate(mContext, mTargetSurface.w * WG_SSAA_SAMPLES, mTargetSurface.h * WG_SSAA_SAMPLES);
|
||||||
renderStorage->clear(mCommandEncoder);
|
|
||||||
mRenderStorageStack.push(renderStorage);
|
mRenderStorageStack.push(renderStorage);
|
||||||
|
// begin last render pass
|
||||||
|
mRenderStorageStack.last()->beginRenderPass(mCommandEncoder, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,6 +362,8 @@ bool WgRenderer::beginComposite(TVG_UNUSED Compositor* cmp, TVG_UNUSED Composite
|
||||||
bool WgRenderer::endComposite(TVG_UNUSED Compositor* cmp)
|
bool WgRenderer::endComposite(TVG_UNUSED Compositor* cmp)
|
||||||
{
|
{
|
||||||
if (cmp->method == CompositeMethod::None) {
|
if (cmp->method == CompositeMethod::None) {
|
||||||
|
// end current render pass
|
||||||
|
mRenderStorageStack.last()->endRenderPass();
|
||||||
// get two last render targets
|
// get two last render targets
|
||||||
WgRenderStorage* renderStorageSrc = mRenderStorageStack.last();
|
WgRenderStorage* renderStorageSrc = mRenderStorageStack.last();
|
||||||
mRenderStorageStack.pop();
|
mRenderStorageStack.pop();
|
||||||
|
@ -339,7 +374,11 @@ bool WgRenderer::endComposite(TVG_UNUSED Compositor* cmp)
|
||||||
|
|
||||||
// back render targets to the pool
|
// back render targets to the pool
|
||||||
mRenderStoragePool.free(mContext, renderStorageSrc);
|
mRenderStoragePool.free(mContext, renderStorageSrc);
|
||||||
|
// begin last render pass
|
||||||
|
mRenderStorageStack.last()->beginRenderPass(mCommandEncoder, false);
|
||||||
} else {
|
} else {
|
||||||
|
// end current render pass
|
||||||
|
mRenderStorageStack.last()->endRenderPass();
|
||||||
// get two last render targets
|
// get two last render targets
|
||||||
WgRenderStorage* renderStorageSrc = mRenderStorageStack.last();
|
WgRenderStorage* renderStorageSrc = mRenderStorageStack.last();
|
||||||
mRenderStorageStack.pop();
|
mRenderStorageStack.pop();
|
||||||
|
@ -358,6 +397,8 @@ bool WgRenderer::endComposite(TVG_UNUSED Compositor* cmp)
|
||||||
// back render targets to the pool
|
// back render targets to the pool
|
||||||
mRenderStoragePool.free(mContext, renderStorageSrc);
|
mRenderStoragePool.free(mContext, renderStorageSrc);
|
||||||
mRenderStoragePool.free(mContext, renderStorageMsk);
|
mRenderStoragePool.free(mContext, renderStorageMsk);
|
||||||
|
// begin last render pass
|
||||||
|
mRenderStorageStack.last()->beginRenderPass(mCommandEncoder, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete current compositor
|
// delete current compositor
|
||||||
|
|
|
@ -62,7 +62,7 @@ public:
|
||||||
private:
|
private:
|
||||||
// render handles
|
// render handles
|
||||||
WGPUCommandEncoder mCommandEncoder{};
|
WGPUCommandEncoder mCommandEncoder{};
|
||||||
WgRenderTarget mRenderTarget;
|
WgRenderStorage mRenderTarget;
|
||||||
WgRenderStorage mRenderStorageRoot;
|
WgRenderStorage mRenderStorageRoot;
|
||||||
WgRenderStorage mRenderStorageScreen;
|
WgRenderStorage mRenderStorageScreen;
|
||||||
WgRenderStoragePool mRenderStoragePool;
|
WgRenderStoragePool mRenderStoragePool;
|
||||||
|
@ -76,7 +76,6 @@ private:
|
||||||
|
|
||||||
// native window handles
|
// native window handles
|
||||||
WGPUSurface mSurface{};
|
WGPUSurface mSurface{};
|
||||||
private:
|
|
||||||
WgContext mContext;
|
WgContext mContext;
|
||||||
WgPipelines mPipelines;
|
WgPipelines mPipelines;
|
||||||
Surface mTargetSurface;
|
Surface mTargetSurface;
|
||||||
|
|
Loading…
Add table
Reference in a new issue