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 WgBindGroupLinearGradient::layout = nullptr;
WGPUBindGroupLayout WgBindGroupRadialGradient::layout = nullptr; WGPUBindGroupLayout WgBindGroupRadialGradient::layout = nullptr;
WGPUBindGroupLayout WgBindGroupPicture::layout = nullptr; WGPUBindGroupLayout WgBindGroupPicture::layout = nullptr;
WGPUBindGroupLayout WgBindGroupCompose::layout = nullptr; WGPUBindGroupLayout WgBindGroupBlit::layout = nullptr;
WGPUBindGroupLayout WgBindGroupCanvas::getLayout(WGPUDevice device) 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; if (layout) return layout;
const WGPUBindGroupLayoutEntry bindGroupLayoutEntries[] { const WGPUBindGroupLayoutEntry bindGroupLayoutEntries[] {
makeBindGroupLayoutEntrySampler(0), makeBindGroupLayoutEntrySampler(0),
makeBindGroupLayoutEntryTextureView(1), makeBindGroupLayoutEntryTextureView(1)
makeBindGroupLayoutEntryTextureView(2)
}; };
layout = createBindGroupLayout(device, bindGroupLayoutEntries, 3); layout = createBindGroupLayout(device, bindGroupLayoutEntries, 2);
assert(layout); assert(layout);
return layout; return layout;
} }
void WgBindGroupCompose::releaseLayout() void WgBindGroupBlit::releaseLayout()
{ {
releaseBindGroupLayout(layout); 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(); release();
const WGPUBindGroupEntry bindGroupEntries[] { const WGPUBindGroupEntry bindGroupEntries[] {
makeBindGroupEntrySampler(0, uSampler), makeBindGroupEntrySampler(0, uSampler),
makeBindGroupEntryTextureView(1, uTextureSrc), makeBindGroupEntryTextureView(1, uTexture)
makeBindGroupEntryTextureView(2, uTextureDst)
}; };
mBindGroup = createBindGroup(device, getLayout(device), bindGroupEntries, 3); mBindGroup = createBindGroup(device, getLayout(device), bindGroupEntries, 2);
assert(mBindGroup); assert(mBindGroup);
} }
void WgBindGroupCompose::release() void WgBindGroupBlit::release()
{ {
releaseBindGroup(mBindGroup); releaseBindGroup(mBindGroup);
} }

View file

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

View file

@ -313,8 +313,8 @@ WGPUBlendState WgPipeline::makeBlendState()
blendState.color.srcFactor = WGPUBlendFactor_SrcAlpha; blendState.color.srcFactor = WGPUBlendFactor_SrcAlpha;
blendState.color.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha; blendState.color.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha;
blendState.alpha.operation = WGPUBlendOperation_Add; blendState.alpha.operation = WGPUBlendOperation_Add;
blendState.alpha.srcFactor = WGPUBlendFactor_Zero; blendState.alpha.srcFactor = WGPUBlendFactor_One;
blendState.alpha.dstFactor = WGPUBlendFactor_One; blendState.alpha.dstFactor = WGPUBlendFactor_Zero;
return blendState; 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) void WgGeometryData::appendMesh(const RenderMesh* rmesh)
{ {
assert(rmesh); assert(rmesh);

View file

@ -90,6 +90,7 @@ struct WgGeometryData
void appendRect(WgPoint p0, WgPoint p1, WgPoint p2, WgPoint p3); void appendRect(WgPoint p0, WgPoint p1, WgPoint p2, WgPoint p3);
void appendCircle(WgPoint center, float radius); void appendCircle(WgPoint center, float radius);
void appendImageBox(float w, float h); void appendImageBox(float w, float h);
void appendBlitBox();
void appendMesh(const RenderMesh* rmesh); void appendMesh(const RenderMesh* rmesh);
void close(); 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 // pipelines
//************************************************************************ //************************************************************************
void WgPipelines::initialize(WGPUDevice device) void WgPipelines::initialize(WGPUDevice device)
{ {
mPipelineFillShape.initialize(device); fillShape.initialize(device);
mPipelineFillStroke.initialize(device); fillStroke.initialize(device);
mPipelineSolid.initialize(device); solid.initialize(device);
mPipelineLinear.initialize(device); linear.initialize(device);
mPipelineRadial.initialize(device); radial.initialize(device);
mPipelineImage.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() void WgPipelines::release()
{ {
WgBindGroupCompose::releaseLayout(); WgBindGroupBlit::releaseLayout();
WgBindGroupPicture::releaseLayout(); WgBindGroupPicture::releaseLayout();
WgBindGroupRadialGradient::releaseLayout(); WgBindGroupRadialGradient::releaseLayout();
WgBindGroupLinearGradient::releaseLayout(); WgBindGroupLinearGradient::releaseLayout();
WgBindGroupSolidColor::releaseLayout(); WgBindGroupSolidColor::releaseLayout();
WgBindGroupCanvas::releaseLayout(); WgBindGroupCanvas::releaseLayout();
mPipelineImage.release(); compDifferenceMask.release();
mPipelineRadial.release(); compIntersectMask.release();
mPipelineLinear.release(); compSubtractMask.release();
mPipelineSolid.release(); compAddMask.release();
mPipelineFillStroke.release(); compInvLumaMask.release();
mPipelineFillShape.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 struct WgPipelines
{ {
WgPipelineFillShape mPipelineFillShape; WgPipelineFillShape fillShape;
WgPipelineFillStroke mPipelineFillStroke; WgPipelineFillStroke fillStroke;
WgPipelineSolid mPipelineSolid; WgPipelineSolid solid;
WgPipelineLinear mPipelineLinear; WgPipelineLinear linear;
WgPipelineRadial mPipelineRadial; WgPipelineRadial radial;
WgPipelineImage mPipelineImage; 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 initialize(WGPUDevice device);
void release(); void release();
WgPipelineComposition* getCompositionPipeline(CompositeMethod method);
}; };
#endif // _TVG_WG_PIPELINES_H_ #endif // _TVG_WG_PIPELINES_H_

View file

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

View file

@ -48,7 +48,14 @@ void WgRenderer::initialize()
void WgRenderer::release() 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 (mSwapChain) wgpuSwapChainRelease(mSwapChain);
if (mSurface) wgpuSurfaceRelease(mSurface); if (mSurface) wgpuSurfaceRelease(mSurface);
mPipelines.release(); mPipelines.release();
@ -123,26 +130,39 @@ RenderData WgRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderD
bool WgRenderer::preRender() 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; return true;
} }
bool WgRenderer::renderShape(RenderData data) bool WgRenderer::renderShape(RenderData data)
{ {
mRenderDatas.push(data); mRenderTargetStack.last()->renderShape((WgRenderDataShape *)data);
mRenderTargetStack.last()->renderStroke((WgRenderDataShape *)data);
return true; return true;
} }
bool WgRenderer::renderImage(RenderData data) bool WgRenderer::renderImage(RenderData data)
{ {
mRenderDatas.push(data); mRenderTargetStack.last()->renderPicture((WgRenderDataPicture *)data);
return true; return true;
} }
bool WgRenderer::postRender() bool WgRenderer::postRender()
{ {
mRenderTargetRoot.endRenderPass();
mRenderTargetStack.pop();
mContext.executeCommandEncoder(mCommandEncoder);
wgpuCommandEncoderRelease(mCommandEncoder);
return true; return true;
} }
@ -193,35 +213,17 @@ bool WgRenderer::clear()
bool WgRenderer::sync() bool WgRenderer::sync()
{ {
WGPUTextureView backBufferView = wgpuSwapChainGetCurrentTextureView(mSwapChain); WGPUTextureView backBufferView = wgpuSwapChainGetCurrentTextureView(mSwapChain);
// command encoder descriptor
WGPUCommandEncoderDescriptor commandEncoderDesc{}; WGPUCommandEncoderDescriptor commandEncoderDesc{};
commandEncoderDesc.nextInChain = nullptr; commandEncoderDesc.nextInChain = nullptr;
commandEncoderDesc.label = "The command encoder"; commandEncoderDesc.label = "The command encoder";
WGPUCommandEncoder commandEncoder = wgpuDeviceCreateCommandEncoder(mContext.device, &commandEncoderDesc); WGPUCommandEncoder commandEncoder = wgpuDeviceCreateCommandEncoder(mContext.device, &commandEncoderDesc);
mRenderTargetWnd.beginRenderPass(commandEncoder, backBufferView, true);
// render datas mRenderTargetWnd.blitColor(mContext, &mRenderTargetRoot);
mRenderTarget.beginRenderPass(commandEncoder, backBufferView); mRenderTargetWnd.endRenderPass();
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();
mContext.executeCommandEncoder(commandEncoder); mContext.executeCommandEncoder(commandEncoder);
wgpuCommandEncoderRelease(commandEncoder); wgpuCommandEncoderRelease(commandEncoder);
// go to the next frame
wgpuTextureViewRelease(backBufferView); wgpuTextureViewRelease(backBufferView);
wgpuSwapChainPresent(mSwapChain); wgpuSwapChainPresent(mSwapChain);
mRenderDatas.clear();
return true; return true;
} }
@ -233,7 +235,7 @@ bool WgRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t
mTargetSurface.w = w; mTargetSurface.w = w;
mTargetSurface.h = h; mTargetSurface.h = h;
// TODO: Add ability to render into offscreen buffer mRenderTargetRoot.initialize(mContext, mPipelines, w, h);
return true; return true;
} }
@ -273,26 +275,99 @@ bool WgRenderer::target(void* window, uint32_t w, uint32_t h)
mSwapChain = wgpuDeviceCreateSwapChain(mContext.device, mSurface, &swapChainDesc); mSwapChain = wgpuDeviceCreateSwapChain(mContext.device, mSurface, &swapChainDesc);
assert(mSwapChain); assert(mSwapChain);
mRenderTarget.initialize(mContext, mPipelines, w, h); mRenderTargetWnd.initialize(mContext, mPipelines, w, h);
mRenderTargetRoot.initialize(mContext, mPipelines, w, h);
return true; return true;
} }
Compositor* WgRenderer::target(TVG_UNUSED const RenderRegion& region, TVG_UNUSED ColorSpace cs) 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) 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) 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 init(uint32_t threads);
static bool term(); static bool term();
// render handles
WGPUCommandEncoder mCommandEncoder{};
Array<WgRenderTarget*> mRenderTargetStack;
Array<WgRenderTarget*> mRenderTargetPool;
Array<Compositor*> mCompositorStack;
WgRenderTarget* allocateRenderTarget();
void releaseRenderTarget(WgRenderTarget* renderTarget);
private: private:
Array<RenderData> mRenderDatas{};
WgContext mContext; WgContext mContext;
WgPipelines mPipelines; WgPipelines mPipelines;
WgRenderTarget mRenderTarget; WgRenderTarget mRenderTargetRoot;
WgRenderTarget mRenderTargetWnd;
WGPUSurface mSurface{}; WGPUSurface mSurface{};
WGPUSwapChain mSwapChain{}; WGPUSwapChain mSwapChain{};
Surface mTargetSurface; Surface mTargetSurface;

View file

@ -334,3 +334,373 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
return vec4f(result.rgb, result.a * uBlendSettigs.opacity); 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 // pipeline shader module image
extern const char* cShaderSource_PipelineImage; 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_ #endif // _TVG_WG_SHADER_SRC_H_