wg_engine: introduced composition ability

[issues 1479: Masking](#1479)

Supported composition methods:

    AlphaMask
    InvAlphaMask
    LumaMask
    InvLumaMask
    AddMask
    SubtractMask
    IntersectMask
    DifferenceMask

Usage example:

    //Solid Rectangle
    auto shape = tvg::Shape::gen();
    shape->appendRect(0, 0, 400, 400);
    shape->fill(0, 0, 255);

    //Mask
    auto mask = tvg::Shape::gen();
    mask->appendCircle(200, 200, 125, 125);
    mask->fill(255, 255, 255);        //AlphaMask RGB channels are unused.

    //Nested Mask
    auto nMask = tvg::Shape::gen();
    nMask->appendCircle(220, 220, 125, 125);
    nMask->fill(255, 255, 255);       //AlphaMask RGB channels are unused.

    mask->composite(std::move(nMask), tvg::CompositeMethod::AlphaMask);
    shape->composite(std::move(mask), tvg::CompositeMethod::AlphaMask);
    canvas->push(std::move(shape));

    //Star
    auto star = tvg::Shape::gen();
    star->fill(80, 80, 80);
    star->moveTo(599, 34);
    star->lineTo(653, 143);
    star->lineTo(774, 160);
    star->lineTo(687, 244);
    star->lineTo(707, 365);
    star->lineTo(599, 309);
    star->lineTo(497, 365);
    star->lineTo(512, 245);
    star->lineTo(426, 161);
    star->lineTo(546, 143);
    star->close();
    star->strokeWidth(30);
    star->strokeJoin(tvg::StrokeJoin::Miter);
    star->strokeFill(255, 255, 255);

    //Mask3
    auto mask3 = tvg::Shape::gen();
    mask3->appendCircle(600, 200, 125, 125);
    mask3->fill(255, 255, 255);       //AlphaMask RGB channels are unused.
    mask3->opacity(200);
    star->composite(std::move(mask3), tvg::CompositeMethod::AlphaMask);
    if (canvas->push(std::move(star)) != tvg::Result::Success) return;
This commit is contained in:
Sergii Liebodkin 2024-01-11 20:39:28 +02:00 committed by Hermet Park
parent ca0a1b909a
commit af6969e15e
13 changed files with 811 additions and 103 deletions

View file

@ -29,7 +29,7 @@ WGPUBindGroupLayout WgBindGroupSolidColor::layout = nullptr;
WGPUBindGroupLayout WgBindGroupLinearGradient::layout = nullptr;
WGPUBindGroupLayout WgBindGroupRadialGradient::layout = nullptr;
WGPUBindGroupLayout WgBindGroupPicture::layout = nullptr;
WGPUBindGroupLayout WgBindGroupCompose::layout = nullptr;
WGPUBindGroupLayout WgBindGroupBlit::layout = nullptr;
WGPUBindGroupLayout WgBindGroupCanvas::getLayout(WGPUDevice device)
@ -258,40 +258,38 @@ void WgBindGroupPicture::release()
}
WGPUBindGroupLayout WgBindGroupCompose::getLayout(WGPUDevice device)
WGPUBindGroupLayout WgBindGroupBlit::getLayout(WGPUDevice device)
{
if (layout) return layout;
const WGPUBindGroupLayoutEntry bindGroupLayoutEntries[] {
makeBindGroupLayoutEntrySampler(0),
makeBindGroupLayoutEntryTextureView(1),
makeBindGroupLayoutEntryTextureView(2)
makeBindGroupLayoutEntryTextureView(1)
};
layout = createBindGroupLayout(device, bindGroupLayoutEntries, 3);
layout = createBindGroupLayout(device, bindGroupLayoutEntries, 2);
assert(layout);
return layout;
}
void WgBindGroupCompose::releaseLayout()
void WgBindGroupBlit::releaseLayout()
{
releaseBindGroupLayout(layout);
}
void WgBindGroupCompose::initialize(WGPUDevice device, WGPUQueue queue, WGPUSampler uSampler, WGPUTextureView uTextureSrc, WGPUTextureView uTextureDst)
void WgBindGroupBlit::initialize(WGPUDevice device, WGPUQueue queue, WGPUSampler uSampler, WGPUTextureView uTexture)
{
release();
const WGPUBindGroupEntry bindGroupEntries[] {
makeBindGroupEntrySampler(0, uSampler),
makeBindGroupEntryTextureView(1, uTextureSrc),
makeBindGroupEntryTextureView(2, uTextureDst)
makeBindGroupEntryTextureView(1, uTexture)
};
mBindGroup = createBindGroup(device, getLayout(device), bindGroupEntries, 3);
mBindGroup = createBindGroup(device, getLayout(device), bindGroupEntries, 2);
assert(mBindGroup);
}
void WgBindGroupCompose::release()
void WgBindGroupBlit::release()
{
releaseBindGroup(mBindGroup);
}
}

View file

@ -107,8 +107,8 @@ struct WgBindGroupPicture : public WgBindGroup
void release();
};
// @group(2)
struct WgBindGroupCompose : public WgBindGroup
// @group(0 or 1)
struct WgBindGroupBlit : public WgBindGroup
{
static WGPUBindGroupLayout layout;
static WGPUBindGroupLayout getLayout(WGPUDevice device);
@ -116,8 +116,7 @@ struct WgBindGroupCompose : public WgBindGroup
void initialize(WGPUDevice device, WGPUQueue queue,
WGPUSampler uSampler,
WGPUTextureView uTextureSrc,
WGPUTextureView uTextureDst);
WGPUTextureView uTexture);
void release();
};

View file

@ -313,8 +313,8 @@ WGPUBlendState WgPipeline::makeBlendState()
blendState.color.srcFactor = WGPUBlendFactor_SrcAlpha;
blendState.color.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha;
blendState.alpha.operation = WGPUBlendOperation_Add;
blendState.alpha.srcFactor = WGPUBlendFactor_Zero;
blendState.alpha.dstFactor = WGPUBlendFactor_One;
blendState.alpha.srcFactor = WGPUBlendFactor_One;
blendState.alpha.dstFactor = WGPUBlendFactor_Zero;
return blendState;
}

View file

@ -120,6 +120,25 @@ void WgGeometryData::appendImageBox(float w, float h)
};
void WgGeometryData::appendBlitBox()
{
positions.push({ -1.0f, +1.0f });
positions.push({ +1.0f, +1.0f });
positions.push({ +1.0f, -1.0f });
positions.push({ -1.0f, -1.0f });
texCoords.push({ 0.0f, 0.0f });
texCoords.push({ 1.0f, 0.0f });
texCoords.push({ 1.0f, 1.0f });
texCoords.push({ 0.0f, 1.0f });
indexes.push(0);
indexes.push(1);
indexes.push(2);
indexes.push(0);
indexes.push(2);
indexes.push(3);
}
void WgGeometryData::appendMesh(const RenderMesh* rmesh)
{
assert(rmesh);

View file

@ -90,6 +90,7 @@ struct WgGeometryData
void appendRect(WgPoint p0, WgPoint p1, WgPoint p2, WgPoint p3);
void appendCircle(WgPoint center, float radius);
void appendImageBox(float w, float h);
void appendBlitBox();
void appendMesh(const RenderMesh* rmesh);
void close();
};

View file

@ -223,33 +223,171 @@ void WgPipelineImage::initialize(WGPUDevice device)
}
void WgPipelineBlit::initialize(WGPUDevice device)
{
// vertex and buffers settings
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
WGPUVertexAttribute vertexAttributesTex = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 1 };
WGPUVertexBufferLayout vertexBufferLayouts[] = {
makeVertexBufferLayout(&vertexAttributesPos, 1, sizeof(float) * 2),
makeVertexBufferLayout(&vertexAttributesTex, 1, sizeof(float) * 2)
};
// bind groups and layouts
WGPUBindGroupLayout bindGroupLayouts[] = {
WgBindGroupBlit::getLayout(device)
};
// stencil function
WGPUCompareFunction stencilFuncion = WGPUCompareFunction_Always;
WGPUStencilOperation stencilOperation = WGPUStencilOperation_Zero;
// sheder source and labels
auto shaderSource = cShaderSource_PipelineBlit;
auto shaderLabel = "The shader blit";
auto pipelineLabel = "The render pipeline blit";
// allocate all pipeline handles
allocate(device,
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
stencilFuncion, stencilOperation,
shaderSource, shaderLabel, pipelineLabel);
}
void WgPipelineBlitColor::initialize(WGPUDevice device)
{
// vertex and buffers settings
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
WGPUVertexAttribute vertexAttributesTex = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 1 };
WGPUVertexBufferLayout vertexBufferLayouts[] = {
makeVertexBufferLayout(&vertexAttributesPos, 1, sizeof(float) * 2),
makeVertexBufferLayout(&vertexAttributesTex, 1, sizeof(float) * 2)
};
// bind groups and layouts
WGPUBindGroupLayout bindGroupLayouts[] = {
WgBindGroupBlit::getLayout(device)
};
// stencil function
WGPUCompareFunction stencilFuncion = WGPUCompareFunction_Always;
WGPUStencilOperation stencilOperation = WGPUStencilOperation_Zero;
// sheder source and labels
auto shaderSource = cShaderSource_PipelineBlitColor;
auto shaderLabel = "The shader blit color";
auto pipelineLabel = "The render pipeline blit color";
// allocate all pipeline handles
allocate(device,
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
stencilFuncion, stencilOperation,
shaderSource, shaderLabel, pipelineLabel);
}
void WgPipelineComposition::initialize(WGPUDevice device, const char* shaderSrc)
{
// vertex and buffers settings
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
WGPUVertexAttribute vertexAttributesTex = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 1 };
WGPUVertexBufferLayout vertexBufferLayouts[] = {
makeVertexBufferLayout(&vertexAttributesPos, 1, sizeof(float) * 2),
makeVertexBufferLayout(&vertexAttributesTex, 1, sizeof(float) * 2)
};
// bind groups and layouts
WGPUBindGroupLayout bindGroupLayouts[] = {
WgBindGroupBlit::getLayout(device),
WgBindGroupBlit::getLayout(device)
};
// stencil function
WGPUCompareFunction stencilFuncion = WGPUCompareFunction_Always;
WGPUStencilOperation stencilOperation = WGPUStencilOperation_Zero;
// sheder source and labels
auto shaderSource = shaderSrc;
auto shaderLabel = "The shader compose alpha mask";
auto pipelineLabel = "The render pipeline compose alpha mask";
// allocate all pipeline handles
allocate(device,
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
stencilFuncion, stencilOperation,
shaderSource, shaderLabel, pipelineLabel);
}
//************************************************************************
// pipelines
//************************************************************************
void WgPipelines::initialize(WGPUDevice device)
{
mPipelineFillShape.initialize(device);
mPipelineFillStroke.initialize(device);
mPipelineSolid.initialize(device);
mPipelineLinear.initialize(device);
mPipelineRadial.initialize(device);
mPipelineImage.initialize(device);
fillShape.initialize(device);
fillStroke.initialize(device);
solid.initialize(device);
linear.initialize(device);
radial.initialize(device);
image.initialize(device);
blit.initialize(device);
blitColor.initialize(device);
// composition pipelines
compAlphaMask.initialize(device, cShaderSource_PipelineCompAlphaMask);
compInvAlphaMask.initialize(device, cShaderSource_PipelineCompInvAlphaMask);
compLumaMask.initialize(device, cShaderSource_PipelineCompLumaMask);
compInvLumaMask.initialize(device, cShaderSource_PipelineCompInvLumaMask);
compAddMask.initialize(device, cShaderSource_PipelineCompAddMask);
compSubtractMask.initialize(device, cShaderSource_PipelineCompSubtractMask);
compIntersectMask.initialize(device, cShaderSource_PipelineCompIntersectMask);
compDifferenceMask.initialize(device, cShaderSource_PipelineCompDifferenceMask);
}
void WgPipelines::release()
{
WgBindGroupCompose::releaseLayout();
WgBindGroupBlit::releaseLayout();
WgBindGroupPicture::releaseLayout();
WgBindGroupRadialGradient::releaseLayout();
WgBindGroupLinearGradient::releaseLayout();
WgBindGroupSolidColor::releaseLayout();
WgBindGroupCanvas::releaseLayout();
mPipelineImage.release();
mPipelineRadial.release();
mPipelineLinear.release();
mPipelineSolid.release();
mPipelineFillStroke.release();
mPipelineFillShape.release();
}
compDifferenceMask.release();
compIntersectMask.release();
compSubtractMask.release();
compAddMask.release();
compInvLumaMask.release();
compLumaMask.release();
compInvAlphaMask.release();
compAlphaMask.release();
blitColor.release();
blit.release();
image.release();
radial.release();
linear.release();
solid.release();
fillStroke.release();
fillShape.release();
}
WgPipelineComposition* WgPipelines::getCompositionPipeline(CompositeMethod method)
{
switch (method) {
case CompositeMethod::ClipPath:
case CompositeMethod::AlphaMask: return &compAlphaMask; break;
case CompositeMethod::InvAlphaMask: return &compInvAlphaMask; break;
case CompositeMethod::LumaMask: return &compLumaMask; break;
case CompositeMethod::InvLumaMask: return &compInvLumaMask; break;
case CompositeMethod::AddMask: return &compAddMask; break;
case CompositeMethod::SubtractMask: return &compSubtractMask; break;
case CompositeMethod::IntersectMask: return &compIntersectMask; break;
case CompositeMethod::DifferenceMask: return &compDifferenceMask; break;
default: return nullptr; break;
}
return nullptr;
}

View file

@ -95,17 +95,62 @@ struct WgPipelineImage: public WgPipeline
}
};
struct WgPipelineBlit: public WgPipeline
{
void initialize(WGPUDevice device) override;
void use(WGPURenderPassEncoder encoder, WgBindGroupBlit& groupBlit)
{
set(encoder);
groupBlit.set(encoder, 0);
}
};
struct WgPipelineBlitColor: public WgPipeline
{
void initialize(WGPUDevice device) override;
void use(WGPURenderPassEncoder encoder, WgBindGroupBlit& groupBlit)
{
set(encoder);
groupBlit.set(encoder, 0);
}
};
struct WgPipelineComposition: public WgPipeline
{
void initialize(WGPUDevice device) override {};
void initialize(WGPUDevice device, const char* shaderSrc);
void use(WGPURenderPassEncoder encoder, WgBindGroupBlit& groupBlitSrc, WgBindGroupBlit& groupBlitMsk)
{
set(encoder);
groupBlitSrc.set(encoder, 0);
groupBlitMsk.set(encoder, 1);
}
};
struct WgPipelines
{
WgPipelineFillShape mPipelineFillShape;
WgPipelineFillStroke mPipelineFillStroke;
WgPipelineSolid mPipelineSolid;
WgPipelineLinear mPipelineLinear;
WgPipelineRadial mPipelineRadial;
WgPipelineImage mPipelineImage;
WgPipelineFillShape fillShape;
WgPipelineFillStroke fillStroke;
WgPipelineSolid solid;
WgPipelineLinear linear;
WgPipelineRadial radial;
WgPipelineImage image;
WgPipelineBlit blit;
WgPipelineBlitColor blitColor;
// composition pipelines
WgPipelineComposition compAlphaMask;
WgPipelineComposition compInvAlphaMask;
WgPipelineComposition compLumaMask;
WgPipelineComposition compInvLumaMask;
WgPipelineComposition compAddMask;
WgPipelineComposition compSubtractMask;
WgPipelineComposition compIntersectMask;
WgPipelineComposition compDifferenceMask;
void initialize(WGPUDevice device);
void release();
WgPipelineComposition* getCompositionPipeline(CompositeMethod method);
};
#endif // _TVG_WG_PIPELINES_H_

View file

@ -39,8 +39,8 @@ void WgRenderTarget::initialize(WgContext& context, WgPipelines& pipelines, uint
samplerDesc.lodMaxClamp = 32.0f;
samplerDesc.compare = WGPUCompareFunction_Undefined;
samplerDesc.maxAnisotropy = 1;
mSampler = wgpuDeviceCreateSampler(context.device, &samplerDesc);
assert(mSampler);
sampler = wgpuDeviceCreateSampler(context.device, &samplerDesc);
assert(sampler);
// texture descriptor
WGPUTextureDescriptor textureDescColor{};
textureDescColor.nextInChain = nullptr;
@ -48,7 +48,7 @@ void WgRenderTarget::initialize(WgContext& context, WgPipelines& pipelines, uint
textureDescColor.usage = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopyDst;
textureDescColor.dimension = WGPUTextureDimension_2D;
textureDescColor.size = { w, h, 1 };
textureDescColor.format = WGPUTextureFormat_RGBA8Unorm;
textureDescColor.format = WGPUTextureFormat_BGRA8Unorm;
textureDescColor.mipLevelCount = 1;
textureDescColor.sampleCount = 1;
textureDescColor.viewFormatCount = 0;
@ -59,15 +59,15 @@ void WgRenderTarget::initialize(WgContext& context, WgPipelines& pipelines, uint
WGPUTextureViewDescriptor textureViewDescColor{};
textureViewDescColor.nextInChain = nullptr;
textureViewDescColor.label = "The target texture view color";
textureViewDescColor.format = WGPUTextureFormat_RGBA8Unorm;
textureViewDescColor.format = WGPUTextureFormat_BGRA8Unorm;
textureViewDescColor.dimension = WGPUTextureViewDimension_2D;
textureViewDescColor.baseMipLevel = 0;
textureViewDescColor.mipLevelCount = 1;
textureViewDescColor.baseArrayLayer = 0;
textureViewDescColor.arrayLayerCount = 1;
textureViewDescColor.aspect = WGPUTextureAspect_All;
mTextureViewColor = wgpuTextureCreateView(mTextureColor, &textureViewDescColor);
assert(mTextureViewColor);
textureViewColor = wgpuTextureCreateView(mTextureColor, &textureViewDescColor);
assert(textureViewColor);
// stencil texture
WGPUTextureDescriptor textureDescStencil{};
textureDescStencil.nextInChain = nullptr;
@ -93,49 +93,56 @@ void WgRenderTarget::initialize(WgContext& context, WgPipelines& pipelines, uint
textureViewDescStencil.baseArrayLayer = 0;
textureViewDescStencil.arrayLayerCount = 1;
textureViewDescStencil.aspect = WGPUTextureAspect_All;
mTextureViewStencil = wgpuTextureCreateView(mTextureStencil, &textureViewDescStencil);
assert(mTextureViewStencil);
textureViewStencil = wgpuTextureCreateView(mTextureStencil, &textureViewDescStencil);
assert(textureViewStencil);
// initialize bind group for blitting
bindGroupBlit.initialize(context.device, context.queue, sampler, textureViewColor);
// initialize window binding groups
WgShaderTypeMat4x4f viewMat(w, h);
mBindGroupCanvasWnd.initialize(context.device, context.queue, viewMat);
WgGeometryData geometryDataWnd;
geometryDataWnd.appendBlitBox();
mMeshDataCanvasWnd.update(context, &geometryDataWnd);
mPipelines = &pipelines;
}
void WgRenderTarget::release(WgContext& context)
{
mMeshDataCanvasWnd.release(context);
mBindGroupCanvasWnd.release();
bindGroupBlit.release();
if (mTextureStencil) {
wgpuTextureDestroy(mTextureStencil);
wgpuTextureRelease(mTextureStencil);
mTextureStencil = nullptr;
}
if (mTextureViewStencil) wgpuTextureViewRelease(mTextureViewStencil);
mTextureViewStencil = nullptr;
if (textureViewStencil) wgpuTextureViewRelease(textureViewStencil);
textureViewStencil = nullptr;
if (mTextureColor) {
wgpuTextureDestroy(mTextureColor);
wgpuTextureRelease(mTextureColor);
mTextureColor = nullptr;
}
if (mTextureViewColor) wgpuTextureViewRelease(mTextureViewColor);
mTextureViewColor = nullptr;
if (mSampler) wgpuSamplerRelease(mSampler);
mSampler = nullptr;
if (textureViewColor) wgpuTextureViewRelease(textureViewColor);
textureViewColor = nullptr;
if (sampler) wgpuSamplerRelease(sampler);
sampler = nullptr;
}
void WgRenderTarget::beginRenderPass(WGPUCommandEncoder commandEncoder)
void WgRenderTarget::beginRenderPass(WGPUCommandEncoder commandEncoder, bool clear)
{
beginRenderPass(commandEncoder, mTextureViewColor);
beginRenderPass(commandEncoder, textureViewColor, clear);
}
void WgRenderTarget::beginRenderPass(WGPUCommandEncoder commandEncoder, WGPUTextureView colorAttachement)
void WgRenderTarget::beginRenderPass(WGPUCommandEncoder commandEncoder, WGPUTextureView colorAttachement, bool clear)
{
assert(!mRenderPassEncoder);
// render pass depth stencil attachment
WGPURenderPassDepthStencilAttachment depthStencilAttachment{};
depthStencilAttachment.view = mTextureViewStencil;
depthStencilAttachment.view = textureViewStencil;
depthStencilAttachment.depthLoadOp = WGPULoadOp_Clear;
depthStencilAttachment.depthStoreOp = WGPUStoreOp_Store;
depthStencilAttachment.depthClearValue = 1.0f;
@ -148,8 +155,8 @@ void WgRenderTarget::beginRenderPass(WGPUCommandEncoder commandEncoder, WGPUText
WGPURenderPassColorAttachment colorAttachment{};
colorAttachment.view = colorAttachement;
colorAttachment.resolveTarget = nullptr;
colorAttachment.loadOp = WGPULoadOp_Clear;
colorAttachment.clearValue = {0, 0, 0, 0};
colorAttachment.loadOp = clear ? WGPULoadOp_Clear : WGPULoadOp_Load;
colorAttachment.clearValue = { 0, 0, 0, 0 };
colorAttachment.storeOp = WGPUStoreOp_Store;
// render pass descriptor
WGPURenderPassDescriptor renderPassDesc{};
@ -184,16 +191,16 @@ void WgRenderTarget::renderShape(WgRenderDataShape* renderData)
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
for (uint32_t i = 0; i < renderData->meshGroupShapes.meshes.count; i++) {
// draw to stencil (first pass)
mPipelines->mPipelineFillShape.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint);
mPipelines->fillShape.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint);
renderData->meshGroupShapes.meshes[i]->draw(mRenderPassEncoder);
// fill shape (second pass)
WgRenderSettings& settings = renderData->renderSettingsShape;
if (settings.fillType == WgRenderSettingsType::Solid)
mPipelines->mPipelineSolid.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupSolid);
mPipelines->solid.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupSolid);
else if (settings.fillType == WgRenderSettingsType::Linear)
mPipelines->mPipelineLinear.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupLinear);
mPipelines->linear.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupLinear);
else if (settings.fillType == WgRenderSettingsType::Radial)
mPipelines->mPipelineRadial.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupRadial);
mPipelines->radial.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupRadial);
renderData->meshBBoxShapes.draw(mRenderPassEncoder);
}
}
@ -208,18 +215,18 @@ void WgRenderTarget::renderStroke(WgRenderDataShape* renderData)
// draw strokes to stencil (first pass)
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 255);
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
mPipelines->mPipelineFillStroke.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint);
mPipelines->fillStroke.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint);
renderData->meshGroupStrokes.meshes[i]->draw(mRenderPassEncoder);
}
// fill shape (second pass)
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
WgRenderSettings& settings = renderData->renderSettingsStroke;
if (settings.fillType == WgRenderSettingsType::Solid)
mPipelines->mPipelineSolid.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupSolid);
mPipelines->solid.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupSolid);
else if (settings.fillType == WgRenderSettingsType::Linear)
mPipelines->mPipelineLinear.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupLinear);
mPipelines->linear.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupLinear);
else if (settings.fillType == WgRenderSettingsType::Radial)
mPipelines->mPipelineRadial.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupRadial);
mPipelines->radial.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupRadial);
renderData->meshBBoxStrokes.draw(mRenderPassEncoder);
}
}
@ -231,7 +238,7 @@ void WgRenderTarget::renderPicture(WgRenderDataPicture* renderData)
assert(mRenderPassEncoder);
if (renderData->meshData.bufferTexCoord) {
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
mPipelines->mPipelineImage.use(
mPipelines->image.use(
mRenderPassEncoder,
mBindGroupCanvasWnd,
renderData->bindGroupPaint,
@ -239,3 +246,29 @@ void WgRenderTarget::renderPicture(WgRenderDataPicture* renderData)
renderData->meshData.drawImage(mRenderPassEncoder);
}
}
void WgRenderTarget::blit(WgContext& context, WgRenderTarget* renderTargetSrc)
{
assert(mRenderPassEncoder);
mPipelines->blit.use(mRenderPassEncoder, renderTargetSrc->bindGroupBlit);
mMeshDataCanvasWnd.drawImage(mRenderPassEncoder);
}
void WgRenderTarget::blitColor(WgContext& context, WgRenderTarget* renderTargetSrc)
{
assert(mRenderPassEncoder);
mPipelines->blitColor.use(mRenderPassEncoder, renderTargetSrc->bindGroupBlit);
mMeshDataCanvasWnd.drawImage(mRenderPassEncoder);
}
void WgRenderTarget::compose(WgContext& context, WgRenderTarget* renderTargetSrc, WgRenderTarget* renderTargetMsk, CompositeMethod method)
{
assert(mRenderPassEncoder);
WgPipelineComposition* pipeline = mPipelines->getCompositionPipeline(method);
assert(pipeline);
pipeline->use(mRenderPassEncoder, renderTargetSrc->bindGroupBlit, renderTargetMsk->bindGroupBlit);
mMeshDataCanvasWnd.drawImage(mRenderPassEncoder);
}

View file

@ -31,24 +31,32 @@ private:
WGPURenderPassEncoder mRenderPassEncoder{};
// fill and blit data
WgBindGroupCanvas mBindGroupCanvasWnd;
// composition handles
WgMeshData mMeshDataCanvasWnd;
// gpu buffers
WGPUSampler mSampler{};
WGPUTexture mTextureColor{};
WGPUTexture mTextureStencil{};
WGPUTextureView mTextureViewColor{};
WGPUTextureView mTextureViewStencil{};
WgPipelines* mPipelines{}; // external handle
public:
WGPUSampler sampler{};
WGPUTextureView textureViewColor{};
WGPUTextureView textureViewStencil{};
WgBindGroupBlit bindGroupBlit;
public:
void initialize(WgContext& context, WgPipelines& pipelines, uint32_t w, uint32_t h);
void release(WgContext& context);
void beginRenderPass(WGPUCommandEncoder commandEncoder, WGPUTextureView colorAttachement);
void beginRenderPass(WGPUCommandEncoder commandEncoder);
void beginRenderPass(WGPUCommandEncoder commandEncoder, WGPUTextureView colorAttachement, bool clear);
void beginRenderPass(WGPUCommandEncoder commandEncoder, bool clear);
void endRenderPass();
void renderShape(WgRenderDataShape* renderData);
void renderStroke(WgRenderDataShape* renderData);
void renderPicture(WgRenderDataPicture* renderData);
void blit(WgContext& context, WgRenderTarget* renderTargetSrc);
void blitColor(WgContext& context, WgRenderTarget* renderTargetSrc);
void compose(WgContext& context, WgRenderTarget* renderTargetSrc, WgRenderTarget* renderTargetMsk, CompositeMethod method);
};
#endif

View file

@ -48,7 +48,14 @@ void WgRenderer::initialize()
void WgRenderer::release()
{
mPipelines.release();
// clear render targets
for (uint32_t i = 0; i < mRenderTargetPool.count; i++) {
mRenderTargetPool[i]->release(mContext);
delete mRenderTargetPool[i];
}
mRenderTargetPool.clear();
mRenderTargetRoot.release(mContext);
mRenderTargetWnd.release(mContext);
if (mSwapChain) wgpuSwapChainRelease(mSwapChain);
if (mSurface) wgpuSurfaceRelease(mSurface);
mPipelines.release();
@ -123,26 +130,39 @@ RenderData WgRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderD
bool WgRenderer::preRender()
{
// command encoder descriptor
WGPUCommandEncoderDescriptor commandEncoderDesc{};
commandEncoderDesc.nextInChain = nullptr;
commandEncoderDesc.label = "The command encoder";
mCommandEncoder = wgpuDeviceCreateCommandEncoder(mContext.device, &commandEncoderDesc);
// render datas
mRenderTargetStack.push(&mRenderTargetRoot);
mRenderTargetRoot.beginRenderPass(mCommandEncoder, true);
return true;
}
bool WgRenderer::renderShape(RenderData data)
{
mRenderDatas.push(data);
mRenderTargetStack.last()->renderShape((WgRenderDataShape *)data);
mRenderTargetStack.last()->renderStroke((WgRenderDataShape *)data);
return true;
}
bool WgRenderer::renderImage(RenderData data)
{
mRenderDatas.push(data);
mRenderTargetStack.last()->renderPicture((WgRenderDataPicture *)data);
return true;
}
bool WgRenderer::postRender()
{
mRenderTargetRoot.endRenderPass();
mRenderTargetStack.pop();
mContext.executeCommandEncoder(mCommandEncoder);
wgpuCommandEncoderRelease(mCommandEncoder);
return true;
}
@ -193,35 +213,17 @@ bool WgRenderer::clear()
bool WgRenderer::sync()
{
WGPUTextureView backBufferView = wgpuSwapChainGetCurrentTextureView(mSwapChain);
// command encoder descriptor
WGPUCommandEncoderDescriptor commandEncoderDesc{};
commandEncoderDesc.nextInChain = nullptr;
commandEncoderDesc.label = "The command encoder";
WGPUCommandEncoder commandEncoder = wgpuDeviceCreateCommandEncoder(mContext.device, &commandEncoderDesc);
// render datas
mRenderTarget.beginRenderPass(commandEncoder, backBufferView);
for (size_t i = 0; i < mRenderDatas.count; i++) {
WgRenderDataPaint* renderData = (WgRenderDataShape*)(mRenderDatas[i]);
if (renderData->identifier() == TVG_CLASS_ID_SHAPE) {
mRenderTarget.renderShape((WgRenderDataShape *)renderData);
mRenderTarget.renderStroke((WgRenderDataShape *)renderData);
} else if (renderData->identifier() == TVG_CLASS_ID_PICTURE) {
mRenderTarget.renderPicture((WgRenderDataPicture *)renderData);
}
}
mRenderTarget.endRenderPass();
mRenderTargetWnd.beginRenderPass(commandEncoder, backBufferView, true);
mRenderTargetWnd.blitColor(mContext, &mRenderTargetRoot);
mRenderTargetWnd.endRenderPass();
mContext.executeCommandEncoder(commandEncoder);
wgpuCommandEncoderRelease(commandEncoder);
// go to the next frame
wgpuTextureViewRelease(backBufferView);
wgpuSwapChainPresent(mSwapChain);
mRenderDatas.clear();
return true;
}
@ -233,7 +235,7 @@ bool WgRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t
mTargetSurface.w = w;
mTargetSurface.h = h;
// TODO: Add ability to render into offscreen buffer
mRenderTargetRoot.initialize(mContext, mPipelines, w, h);
return true;
}
@ -273,26 +275,99 @@ bool WgRenderer::target(void* window, uint32_t w, uint32_t h)
mSwapChain = wgpuDeviceCreateSwapChain(mContext.device, mSurface, &swapChainDesc);
assert(mSwapChain);
mRenderTarget.initialize(mContext, mPipelines, w, h);
mRenderTargetWnd.initialize(mContext, mPipelines, w, h);
mRenderTargetRoot.initialize(mContext, mPipelines, w, h);
return true;
}
Compositor* WgRenderer::target(TVG_UNUSED const RenderRegion& region, TVG_UNUSED ColorSpace cs)
{
return nullptr;
mCompositorStack.push(new Compositor);
return mCompositorStack.last();
}
bool WgRenderer::beginComposite(TVG_UNUSED Compositor* cmp, TVG_UNUSED CompositeMethod method, TVG_UNUSED uint8_t opacity)
{
return false;
// save current composition settings
cmp->method = method;
cmp->opacity = opacity;
// end current render target
mRenderTargetStack.last()->endRenderPass();
// create new render target and begin new render pass
WgRenderTarget* renderTarget = allocateRenderTarget();
renderTarget->beginRenderPass(mCommandEncoder, true);
mRenderTargetStack.push(renderTarget);
return true;
}
bool WgRenderer::endComposite(TVG_UNUSED Compositor* cmp)
{
return false;
if (cmp->method == CompositeMethod::None) {
// end current render pass
mRenderTargetStack.last()->endRenderPass();
// get two last render targets
WgRenderTarget* renderTargetSrc = mRenderTargetStack.last();
mRenderTargetStack.pop();
// apply current render target
WgRenderTarget* renderTarget = mRenderTargetStack.last();
renderTarget->beginRenderPass(mCommandEncoder, false);
renderTarget->blit(mContext, renderTargetSrc);
// back render targets to the pool
releaseRenderTarget(renderTargetSrc);
} else {
// end current render pass
mRenderTargetStack.last()->endRenderPass();
// get two last render targets
WgRenderTarget* renderTargetSrc = mRenderTargetStack.last();
mRenderTargetStack.pop();
WgRenderTarget* renderTargetMsk = mRenderTargetStack.last();
mRenderTargetStack.pop();
// apply current render target
WgRenderTarget* renderTarget = mRenderTargetStack.last();
renderTarget->beginRenderPass(mCommandEncoder, false);
renderTarget->compose(mContext, renderTargetSrc, renderTargetMsk, cmp->method);
// back render targets to the pool
releaseRenderTarget(renderTargetSrc);
releaseRenderTarget(renderTargetMsk);
}
// delete current compositor
delete mCompositorStack.last();
mCompositorStack.pop();
return true;
}
WgRenderTarget* WgRenderer::allocateRenderTarget()
{
WgRenderTarget* renderTarget = nullptr;
if (mRenderTargetPool.count > 0) {
renderTarget = mRenderTargetPool.last();
mRenderTargetPool.pop();
} else {
renderTarget = new WgRenderTarget;
renderTarget->initialize(mContext, mPipelines, mTargetSurface.w, mTargetSurface.h);
}
return renderTarget;
}
void WgRenderer::releaseRenderTarget(WgRenderTarget* renderTarget)
{
mRenderTargetPool.push(renderTarget);
}

View file

@ -60,11 +60,19 @@ public:
static bool init(uint32_t threads);
static bool term();
// render handles
WGPUCommandEncoder mCommandEncoder{};
Array<WgRenderTarget*> mRenderTargetStack;
Array<WgRenderTarget*> mRenderTargetPool;
Array<Compositor*> mCompositorStack;
WgRenderTarget* allocateRenderTarget();
void releaseRenderTarget(WgRenderTarget* renderTarget);
private:
Array<RenderData> mRenderDatas{};
WgContext mContext;
WgPipelines mPipelines;
WgRenderTarget mRenderTarget;
WgRenderTarget mRenderTargetRoot;
WgRenderTarget mRenderTargetWnd;
WGPUSurface mSurface{};
WGPUSwapChain mSwapChain{};
Surface mTargetSurface;

View file

@ -334,3 +334,373 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
return vec4f(result.rgb, result.a * uBlendSettigs.opacity);
};
)";
//************************************************************************
// cShaderSource_PipelineBlit
//************************************************************************
const char* cShaderSource_PipelineBlit = R"(
// vertex input
struct VertexInput {
@location(0) position: vec2f,
@location(1) texCoord: vec2f
};
// vertex output
struct VertexOutput {
@builtin(position) position: vec4f,
@location(0) texCoord: vec2f
};
@group(0) @binding(0) var uSamplerSrc : sampler;
@group(0) @binding(1) var uTextureViewSrc : texture_2d<f32>;
@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
// fill output
var out: VertexOutput;
out.position = vec4f(in.position.xy, 0.0, 1.0);
out.texCoord = in.texCoord;
return out;
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f {
return textureSample(uTextureViewSrc, uSamplerSrc, in.texCoord.xy);
};
)";
//************************************************************************
// cShaderSource_PipelineBlitColor
//************************************************************************
const char* cShaderSource_PipelineBlitColor = R"(
// vertex input
struct VertexInput {
@location(0) position: vec2f,
@location(1) texCoord: vec2f
};
// vertex output
struct VertexOutput {
@builtin(position) position: vec4f,
@location(0) texCoord: vec2f
};
@group(0) @binding(0) var uSamplerSrc : sampler;
@group(0) @binding(1) var uTextureViewSrc : texture_2d<f32>;
@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
// fill output
var out: VertexOutput;
out.position = vec4f(in.position.xy, 0.0, 1.0);
out.texCoord = in.texCoord;
return out;
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f {
let color: vec4f = textureSample(uTextureViewSrc, uSamplerSrc, in.texCoord.xy);
return vec4f(color.rgb, 1.0);
};
)";
//************************************************************************
// cShaderSource_PipelineCompAlphaMask
//************************************************************************
const char* cShaderSource_PipelineCompAlphaMask = R"(
// vertex input
struct VertexInput {
@location(0) position: vec2f,
@location(1) texCoord: vec2f
};
// vertex output
struct VertexOutput {
@builtin(position) position: vec4f,
@location(0) texCoord: vec2f
};
@group(0) @binding(0) var uSamplerSrc : sampler;
@group(0) @binding(1) var uTextureViewSrc : texture_2d<f32>;
@group(1) @binding(0) var uSamplerMsk : sampler;
@group(1) @binding(1) var uTextureViewMsk : texture_2d<f32>;
@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
// fill output
var out: VertexOutput;
out.position = vec4f(in.position.xy, 0.0, 1.0);
out.texCoord = in.texCoord;
return out;
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f {
let colorSrc: vec4f = textureSample(uTextureViewSrc, uSamplerSrc, in.texCoord.xy);
let colorMsk: vec4f = textureSample(uTextureViewMsk, uSamplerMsk, in.texCoord.xy);
return vec4f(colorSrc.rgb, colorSrc.a * colorMsk.a);
};
)";
const char* cShaderSource_PipelineCompInvAlphaMask = R"(
// vertex input
struct VertexInput {
@location(0) position: vec2f,
@location(1) texCoord: vec2f
};
// vertex output
struct VertexOutput {
@builtin(position) position: vec4f,
@location(0) texCoord: vec2f
};
@group(0) @binding(0) var uSamplerSrc : sampler;
@group(0) @binding(1) var uTextureViewSrc : texture_2d<f32>;
@group(1) @binding(0) var uSamplerMsk : sampler;
@group(1) @binding(1) var uTextureViewMsk : texture_2d<f32>;
@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
// fill output
var out: VertexOutput;
out.position = vec4f(in.position.xy, 0.0, 1.0);
out.texCoord = in.texCoord;
return out;
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f {
let colorSrc: vec4f = textureSample(uTextureViewSrc, uSamplerSrc, in.texCoord.xy);
let colorMsk: vec4f = textureSample(uTextureViewMsk, uSamplerMsk, in.texCoord.xy);
return vec4f(colorSrc.rgb, colorSrc.a * (1.0 - colorMsk.a));
};
)";
const char* cShaderSource_PipelineCompLumaMask = R"(
// vertex input
struct VertexInput {
@location(0) position: vec2f,
@location(1) texCoord: vec2f
};
// vertex output
struct VertexOutput {
@builtin(position) position: vec4f,
@location(0) texCoord: vec2f
};
@group(0) @binding(0) var uSamplerSrc : sampler;
@group(0) @binding(1) var uTextureViewSrc : texture_2d<f32>;
@group(1) @binding(0) var uSamplerMsk : sampler;
@group(1) @binding(1) var uTextureViewMsk : texture_2d<f32>;
@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
// fill output
var out: VertexOutput;
out.position = vec4f(in.position.xy, 0.0, 1.0);
out.texCoord = in.texCoord;
return out;
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f {
let colorSrc: vec4f = textureSample(uTextureViewSrc, uSamplerSrc, in.texCoord.xy);
let colorMsk: vec4f = textureSample(uTextureViewMsk, uSamplerMsk, in.texCoord.xy);
let luma: f32 = (0.299 * colorMsk.r + 0.587 * colorMsk.g + 0.114 * colorMsk.b);
return colorSrc * luma;
};
)";
const char* cShaderSource_PipelineCompInvLumaMask = R"(
// vertex input
struct VertexInput {
@location(0) position: vec2f,
@location(1) texCoord: vec2f
};
// vertex output
struct VertexOutput {
@builtin(position) position: vec4f,
@location(0) texCoord: vec2f
};
@group(0) @binding(0) var uSamplerSrc : sampler;
@group(0) @binding(1) var uTextureViewSrc : texture_2d<f32>;
@group(1) @binding(0) var uSamplerMsk : sampler;
@group(1) @binding(1) var uTextureViewMsk : texture_2d<f32>;
@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
// fill output
var out: VertexOutput;
out.position = vec4f(in.position.xy, 0.0, 1.0);
out.texCoord = in.texCoord;
return out;
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f {
let colorSrc: vec4f = textureSample(uTextureViewSrc, uSamplerSrc, in.texCoord.xy);
let colorMsk: vec4f = textureSample(uTextureViewMsk, uSamplerMsk, in.texCoord.xy);
let luma: f32 = (0.299 * colorMsk.r + 0.587 * colorMsk.g + 0.114 * colorMsk.b);
return colorSrc * (1.0 - luma);
};
)";
const char* cShaderSource_PipelineCompAddMask = R"(
// vertex input
struct VertexInput {
@location(0) position: vec2f,
@location(1) texCoord: vec2f
};
// vertex output
struct VertexOutput {
@builtin(position) position: vec4f,
@location(0) texCoord: vec2f
};
@group(0) @binding(0) var uSamplerSrc : sampler;
@group(0) @binding(1) var uTextureViewSrc : texture_2d<f32>;
@group(1) @binding(0) var uSamplerMsk : sampler;
@group(1) @binding(1) var uTextureViewMsk : texture_2d<f32>;
@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
// fill output
var out: VertexOutput;
out.position = vec4f(in.position.xy, 0.0, 1.0);
out.texCoord = in.texCoord;
return out;
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f {
let colorSrc: vec4f = textureSample(uTextureViewSrc, uSamplerSrc, in.texCoord.xy);
let colorMsk: vec4f = textureSample(uTextureViewMsk, uSamplerMsk, in.texCoord.xy);
let color: vec4f = colorSrc + colorMsk * (1.0 - colorSrc.a);
return min(color, vec4f(1.0));
};
)";
const char* cShaderSource_PipelineCompSubtractMask = R"(
// vertex input
struct VertexInput {
@location(0) position: vec2f,
@location(1) texCoord: vec2f
};
// vertex output
struct VertexOutput {
@builtin(position) position: vec4f,
@location(0) texCoord: vec2f
};
@group(0) @binding(0) var uSamplerSrc : sampler;
@group(0) @binding(1) var uTextureViewSrc : texture_2d<f32>;
@group(1) @binding(0) var uSamplerMsk : sampler;
@group(1) @binding(1) var uTextureViewMsk : texture_2d<f32>;
@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
// fill output
var out: VertexOutput;
out.position = vec4f(in.position.xy, 0.0, 1.0);
out.texCoord = in.texCoord;
return out;
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f {
let colorSrc: vec4f = textureSample(uTextureViewSrc, uSamplerSrc, in.texCoord.xy);
let colorMsk: vec4f = textureSample(uTextureViewMsk, uSamplerMsk, in.texCoord.xy);
let a: f32 = colorSrc.a - colorMsk.a;
if (a <= 0.0) {
return vec4f(0.0, 0.0, 0.0, 0.0);
} else {
return vec4f(colorSrc.rgb, colorSrc.a * a);
}
};
)";
const char* cShaderSource_PipelineCompIntersectMask = R"(
// vertex input
struct VertexInput {
@location(0) position: vec2f,
@location(1) texCoord: vec2f
};
// vertex output
struct VertexOutput {
@builtin(position) position: vec4f,
@location(0) texCoord: vec2f
};
@group(0) @binding(0) var uSamplerSrc : sampler;
@group(0) @binding(1) var uTextureViewSrc : texture_2d<f32>;
@group(1) @binding(0) var uSamplerMsk : sampler;
@group(1) @binding(1) var uTextureViewMsk : texture_2d<f32>;
@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
// fill output
var out: VertexOutput;
out.position = vec4f(in.position.xy, 0.0, 1.0);
out.texCoord = in.texCoord;
return out;
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f {
let colorSrc: vec4f = textureSample(uTextureViewSrc, uSamplerSrc, in.texCoord.xy);
let colorMsk: vec4f = textureSample(uTextureViewMsk, uSamplerMsk, in.texCoord.xy);
let intAlpha: f32 = colorSrc.a * colorMsk.a;
return vec4f(colorMsk.rgb, colorMsk.a * intAlpha);
};
)";
const char* cShaderSource_PipelineCompDifferenceMask = R"(
// vertex input
struct VertexInput {
@location(0) position: vec2f,
@location(1) texCoord: vec2f
};
// vertex output
struct VertexOutput {
@builtin(position) position: vec4f,
@location(0) texCoord: vec2f
};
@group(0) @binding(0) var uSamplerSrc : sampler;
@group(0) @binding(1) var uTextureViewSrc : texture_2d<f32>;
@group(1) @binding(0) var uSamplerMsk : sampler;
@group(1) @binding(1) var uTextureViewMsk : texture_2d<f32>;
@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
// fill output
var out: VertexOutput;
out.position = vec4f(in.position.xy, 0.0, 1.0);
out.texCoord = in.texCoord;
return out;
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f {
let colorSrc: vec4f = textureSample(uTextureViewSrc, uSamplerSrc, in.texCoord.xy);
let colorMsk: vec4f = textureSample(uTextureViewMsk, uSamplerMsk, in.texCoord.xy);
let da: f32 = colorSrc.a - colorMsk.a;
if (da > 0.0) {
return vec4f(colorSrc.rgb, colorSrc.a * da);
} else {
return vec4f(colorMsk.rgb, colorMsk.a * (-da));
}
};
)";

View file

@ -40,4 +40,18 @@ extern const char* cShaderSource_PipelineRadial;
// pipeline shader module image
extern const char* cShaderSource_PipelineImage;
// pipeline shader module blit
extern const char* cShaderSource_PipelineBlit;
extern const char* cShaderSource_PipelineBlitColor;
// pipeline shader module composes
extern const char* cShaderSource_PipelineCompAlphaMask;
extern const char* cShaderSource_PipelineCompInvAlphaMask;
extern const char* cShaderSource_PipelineCompLumaMask;
extern const char* cShaderSource_PipelineCompInvLumaMask;
extern const char* cShaderSource_PipelineCompAddMask;
extern const char* cShaderSource_PipelineCompSubtractMask;
extern const char* cShaderSource_PipelineCompIntersectMask;
extern const char* cShaderSource_PipelineCompDifferenceMask;
#endif // _TVG_WG_SHADER_SRC_H_