wg_engine: MSAA support, part 1 - move blending to fragment shaders

Apply custom blending using fragment shaders instead of compute shaders.
This commit is contained in:
Sergii Liebodkin 2024-10-15 19:11:43 +00:00 committed by Hermet Park
parent bb253dd3fb
commit 81cb7da9f3
7 changed files with 1174 additions and 949 deletions

View file

@ -77,13 +77,6 @@ void WgCompositor::release(WgContext& context)
} }
WgPipelineBlendType WgCompositor::blendMethodToBlendType(BlendMethod blendMethod)
{
if (blendMethod == BlendMethod::Normal) return WgPipelineBlendType::Normal;
return WgPipelineBlendType::Custom;
}
RenderRegion WgCompositor::shrinkRenderRegion(RenderRegion& rect) RenderRegion WgCompositor::shrinkRenderRegion(RenderRegion& rect)
{ {
// cut viewport to screen dimensions // cut viewport to screen dimensions
@ -125,6 +118,464 @@ void WgCompositor::endRenderPass()
} }
void WgCompositor::renderShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod)
{
assert(renderData);
assert(renderPassEncoder);
// apply clip path if neccessary
if (renderData->clips.count != 0) {
renderClipPath(context, renderData, &storageClipPath);
if (renderData->strokeFirst) {
clipStrokes(context, renderData, &storageClipPath);
clipShape(context, renderData, &storageClipPath);
} else {
clipShape(context, renderData, &storageClipPath);
clipStrokes(context, renderData, &storageClipPath);
}
// use custom blending
} else if (blendMethod != BlendMethod::Normal) {
if (renderData->strokeFirst) {
blendStrokes(context, renderData, blendMethod);
blendShape(context, renderData, blendMethod);
} else {
blendShape(context, renderData, blendMethod);
blendStrokes(context, renderData, blendMethod);
}
// use direct hardware blending
} else {
if (renderData->strokeFirst) {
drawStrokes(context, renderData);
drawShape(context, renderData);
} else {
drawShape(context, renderData);
drawStrokes(context, renderData);
}
}
}
void WgCompositor::renderImage(WgContext& context, WgRenderDataPicture* renderData, BlendMethod blendMethod)
{
assert(renderData);
assert(renderPassEncoder);
// apply clip path if neccessary
if (renderData->clips.count != 0) {
renderClipPath(context, renderData, &storageClipPath);
clipImage(context, renderData, &storageClipPath);
// use custom blending
} else if (blendMethod != BlendMethod::Normal)
blendImage(context, renderData, blendMethod);
// use direct hardware blending
else drawImage(context, renderData);
}
void WgCompositor::renderScene(WgContext& context, WgRenderStorage* scene, WgCompose* compose)
{
assert(scene);
assert(compose);
assert(renderPassEncoder);
// use custom blending
if (compose->blend != BlendMethod::Normal)
blendScene(context, scene, compose);
// use direct hardware blending
else drawScene(context, scene, compose);
}
void WgCompositor::composeScene(WgContext& context, WgRenderStorage* src, WgRenderStorage* mask, WgCompose* cmp)
{
assert(cmp);
assert(src);
assert(mask);
assert(renderPassEncoder);
RenderRegion rect = shrinkRenderRegion(cmp->aabb);
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, rect.x, rect.y, rect.w, rect.h);
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, src->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, mask->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->scene_compose[(uint32_t)cmp->method]);
meshData.drawImage(context, renderPassEncoder);
}
void WgCompositor::blit(WgContext& context, WGPUCommandEncoder encoder, WgRenderStorage* src, WGPUTextureView dstView) {
WGPURenderPassDepthStencilAttachment depthStencilAttachment{ .view = texViewStencil, .stencilLoadOp = WGPULoadOp_Load, .stencilStoreOp = WGPUStoreOp_Discard };
WGPURenderPassColorAttachment colorAttachment { .view = dstView, .loadOp = WGPULoadOp_Load, .storeOp = WGPUStoreOp_Store };
#ifdef __EMSCRIPTEN__
colorAttachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
#endif
WGPURenderPassDescriptor renderPassDesc{ .colorAttachmentCount = 1, .colorAttachments = &colorAttachment, .depthStencilAttachment = &depthStencilAttachment };
WGPURenderPassEncoder renderPass = wgpuCommandEncoderBeginRenderPass(encoder, &renderPassDesc);
wgpuRenderPassEncoderSetBindGroup(renderPass, 0, src->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPass, pipelines->blit);
meshData.drawImage(context, renderPass);
wgpuRenderPassEncoderEnd(renderPass);
wgpuRenderPassEncoderRelease(renderPass);
}
void WgCompositor::drawShape(WgContext& context, WgRenderDataShape* renderData)
{
assert(renderData);
assert(renderPassEncoder);
assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count);
if (renderData->renderSettingsShape.skip) return;
if (renderData->meshGroupShapes.meshes.count == 0) return;
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h);
// setup stencil rules
WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::Winding) ? pipelines->winding : pipelines->evenodd;
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
// draw to stencil (first pass)
for (uint32_t i = 0; i < renderData->meshGroupShapes.meshes.count; i++)
renderData->meshGroupShapes.meshes[i]->drawFan(context, renderPassEncoder);
// setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
WgRenderSettings& settings = renderData->renderSettingsShape;
if (settings.fillType == WgRenderSettingsType::Solid) {
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupSolid, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->solid);
} else if (settings.fillType == WgRenderSettingsType::Linear) {
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupGradient, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->linear);
} else if (settings.fillType == WgRenderSettingsType::Radial) {
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupGradient, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->radial);
}
// draw to color (second pass)
renderData->meshDataBBox.drawFan(context, renderPassEncoder);
}
void WgCompositor::blendShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod)
{
assert(renderData);
assert(renderPassEncoder);
assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count);
if (renderData->renderSettingsShape.skip) return;
if (renderData->meshGroupShapes.meshes.count == 0) return;
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return;
// copy current render target data to dst storage
WgRenderStorage *target = currentTarget;
endRenderPass();
const WGPUImageCopyTexture texSrc { .texture = target->texture };
const WGPUImageCopyTexture texDst { .texture = storageDstCopy.texture };
const WGPUExtent3D copySize { .width = width, .height = height, .depthOrArrayLayers = 1 };
wgpuCommandEncoderCopyTextureToTexture(commandEncoder, &texSrc, &texDst, &copySize);
beginRenderPass(commandEncoder, target, false);
// render shape with blend settings
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h);
// setup stencil rules
WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::Winding) ? pipelines->winding : pipelines->evenodd;
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
// draw to stencil (first pass)
for (uint32_t i = 0; i < renderData->meshGroupShapes.meshes.count; i++)
renderData->meshGroupShapes.meshes[i]->drawFan(context, renderPassEncoder);
// setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 3, storageDstCopy.bindGroupTexure, 0, nullptr);
uint32_t blendMethodInd = (uint32_t)blendMethod;
WgRenderSettings& settings = renderData->renderSettingsShape;
if (settings.fillType == WgRenderSettingsType::Solid) {
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupSolid, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->solid_blend[blendMethodInd]);
} else if (settings.fillType == WgRenderSettingsType::Linear) {
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupGradient, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->linear_blend[blendMethodInd]);
} else if (settings.fillType == WgRenderSettingsType::Radial) {
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupGradient, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->radial_blend[blendMethodInd]);
}
// draw to color (second pass)
renderData->meshDataBBox.drawFan(context, renderPassEncoder);
}
void WgCompositor::clipShape(WgContext& context, WgRenderDataShape* renderData, WgRenderStorage* mask)
{
assert(mask);
assert(renderData);
assert(commandEncoder);
assert(currentTarget);
// skip shape composing if shape do not exist
if (renderData->renderSettingsShape.skip) return;
if (renderData->meshGroupShapes.meshes.count == 0) return;
// store current render pass
WgRenderStorage *target = currentTarget;
endRenderPass();
// render into intermediate buffer
beginRenderPass(commandEncoder, &storageInterm, true);
drawShape(context, renderData);
endRenderPass();
// restore current render pass
beginRenderPass(commandEncoder, target, false);
RenderRegion rect = shrinkRenderRegion(renderData->aabb);
clipRegion(context, &storageInterm, mask, rect);
}
void WgCompositor::drawStrokes(WgContext& context, WgRenderDataShape* renderData)
{
assert(renderData);
assert(renderPassEncoder);
assert(renderData->meshGroupStrokes.meshes.count == renderData->meshGroupStrokesBBox.meshes.count);
if (renderData->renderSettingsStroke.skip) return;
if (renderData->meshGroupStrokes.meshes.count == 0) return;
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h);
// draw strokes to stencil (first pass)
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
// setup stencil rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->direct);
// draw to stencil (first pass)
renderData->meshGroupStrokes.meshes[i]->draw(context, renderPassEncoder);
// setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
WgRenderSettings& settings = renderData->renderSettingsStroke;
if (settings.fillType == WgRenderSettingsType::Solid) {
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupSolid, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->solid);
} else if (settings.fillType == WgRenderSettingsType::Linear) {
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupGradient, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->linear);
} else if (settings.fillType == WgRenderSettingsType::Radial) {
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupGradient, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->radial);
}
// draw to color (second pass)
renderData->meshGroupStrokesBBox.meshes[i]->drawFan(context, renderPassEncoder);
}
}
void WgCompositor::blendStrokes(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod)
{
assert(renderData);
assert(renderPassEncoder);
assert(renderData->meshGroupStrokes.meshes.count == renderData->meshGroupStrokesBBox.meshes.count);
if (renderData->renderSettingsStroke.skip) return;
if (renderData->meshGroupStrokes.meshes.count == 0) return;
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return;
// copy current render target data to dst storage
WgRenderStorage *target = currentTarget;
endRenderPass();
const WGPUImageCopyTexture texSrc { .texture = target->texture };
const WGPUImageCopyTexture texDst { .texture = storageDstCopy.texture };
const WGPUExtent3D copySize { .width = width, .height = height, .depthOrArrayLayers = 1 };
wgpuCommandEncoderCopyTextureToTexture(commandEncoder, &texSrc, &texDst, &copySize);
beginRenderPass(commandEncoder, target, false);
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h);
// draw strokes to stencil (first pass)
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
// setup stencil rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->direct);
// draw to stencil (first pass)
renderData->meshGroupStrokes.meshes[i]->draw(context, renderPassEncoder);
// setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 3, storageDstCopy.bindGroupTexure, 0, nullptr);
uint32_t blendMethodInd = (uint32_t)blendMethod;
WgRenderSettings& settings = renderData->renderSettingsStroke;
if (settings.fillType == WgRenderSettingsType::Solid) {
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupSolid, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->solid_blend[blendMethodInd]);
} else if (settings.fillType == WgRenderSettingsType::Linear) {
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupGradient, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->linear_blend[blendMethodInd]);
} else if (settings.fillType == WgRenderSettingsType::Radial) {
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupGradient, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->radial_blend[blendMethodInd]);
}
// draw to color (second pass)
renderData->meshGroupStrokesBBox.meshes[i]->drawFan(context, renderPassEncoder);
}
};
void WgCompositor::clipStrokes(WgContext& context, WgRenderDataShape* renderData, WgRenderStorage* mask)
{
assert(mask);
assert(renderData);
assert(commandEncoder);
assert(currentTarget);
// skip shape composing if strokes do not exist
if (renderData->renderSettingsStroke.skip) return;
if (renderData->meshGroupStrokes.meshes.count == 0) return;
// store current render pass
WgRenderStorage *target = currentTarget;
endRenderPass();
// render into intermediate buffer
beginRenderPass(commandEncoder, &storageInterm, true);
drawStrokes(context, renderData);
endRenderPass();
// restore current render pass
beginRenderPass(commandEncoder, target, false);
RenderRegion rect = shrinkRenderRegion(renderData->aabb);
clipRegion(context, &storageInterm, mask, rect);
}
void WgCompositor::drawImage(WgContext& context, WgRenderDataPicture* renderData)
{
assert(renderData);
assert(renderPassEncoder);
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h);
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, renderData->bindGroupPicture, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->image);
renderData->meshData.drawImage(context, renderPassEncoder);
}
void WgCompositor::blendImage(WgContext& context, WgRenderDataPicture* renderData, BlendMethod blendMethod)
{
assert(renderData);
assert(renderPassEncoder);
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return;
// copy current render target data to dst storage
WgRenderStorage *target = currentTarget;
endRenderPass();
const WGPUImageCopyTexture texSrc { .texture = target->texture };
const WGPUImageCopyTexture texDst { .texture = storageDstCopy.texture };
const WGPUExtent3D copySize { .width = width, .height = height, .depthOrArrayLayers = 1 };
wgpuCommandEncoderCopyTextureToTexture(commandEncoder, &texSrc, &texDst, &copySize);
beginRenderPass(commandEncoder, target, false);
// blend image
uint32_t blendMethodInd = (uint32_t)blendMethod;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h);
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, renderData->bindGroupPicture, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 3, storageDstCopy.bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->image_blend[blendMethodInd]);
renderData->meshData.drawImage(context, renderPassEncoder);
};
void WgCompositor::clipImage(WgContext& context, WgRenderDataPicture* renderData, WgRenderStorage* mask)
{
assert(mask);
assert(renderData);
assert(commandEncoder);
assert(currentTarget);
// store current render pass
WgRenderStorage *target = currentTarget;
endRenderPass();
// render into intermediate buffer
beginRenderPass(commandEncoder, &storageInterm, true);
drawImage(context, renderData);
endRenderPass();
// restore current render pass
beginRenderPass(commandEncoder, target, false);
RenderRegion rect { 0, 0, (int32_t)width, (int32_t)height };
clipRegion(context, &storageInterm, mask, rect);
}
void WgCompositor::drawScene(WgContext& context, WgRenderStorage* scene, WgCompose* compose)
{
assert(scene);
assert(compose);
assert(currentTarget);
// draw scene
RenderRegion rect = shrinkRenderRegion(compose->aabb);
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, rect.x, rect.y, rect.w, rect.h);
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, scene->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, bindGroupOpacities[compose->opacity], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->scene);
meshData.drawImage(context, renderPassEncoder);
}
void WgCompositor::blendScene(WgContext& context, WgRenderStorage* scene, WgCompose* compose)
{
assert(scene);
assert(compose);
assert(currentTarget);
// copy current render target data to dst storage
WgRenderStorage *target = currentTarget;
endRenderPass();
const WGPUImageCopyTexture texSrc { .texture = target->texture };
const WGPUImageCopyTexture texDst { .texture = storageDstCopy.texture };
const WGPUExtent3D copySize { .width = width, .height = height, .depthOrArrayLayers = 1 };
wgpuCommandEncoderCopyTextureToTexture(commandEncoder, &texSrc, &texDst, &copySize);
beginRenderPass(commandEncoder, target, false);
// blend scene
uint32_t blendMethodInd = (uint32_t)compose->blend;
RenderRegion rect = shrinkRenderRegion(compose->aabb);
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, rect.x, rect.y, rect.w, rect.h);
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, scene->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, storageDstCopy.bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[compose->opacity], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->scene_blend[blendMethodInd]);
meshData.drawImage(context, renderPassEncoder);
}
void WgCompositor::drawClipPath(WgContext& context, WgRenderDataShape* renderData)
{
assert(renderData);
assert(renderPassEncoder);
assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count);
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h);
// setup stencil rules
WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::Winding) ? pipelines->winding : pipelines->evenodd;
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
// draw to stencil (first pass)
for (uint32_t i = 0; i < renderData->meshGroupShapes.meshes.count; i++)
renderData->meshGroupShapes.meshes[i]->drawFan(context, renderPassEncoder);
// setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->clip_path);
// draw to color (second pass)
renderData->meshDataBBox.drawFan(context, renderPassEncoder);
}
void WgCompositor::clipRegion(WgContext& context, WgRenderStorage* src, WgRenderStorage* mask, RenderRegion& rect)
{
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, rect.x, rect.y, rect.w, rect.h);
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, storageInterm.bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, mask->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->scene_clip);
meshData.drawImage(context, renderPassEncoder);
}
void WgCompositor::renderClipPath(WgContext& context, WgRenderDataPaint* renderData, WgRenderStorage* dst) void WgCompositor::renderClipPath(WgContext& context, WgRenderDataPaint* renderData, WgRenderStorage* dst)
{ {
assert(renderData); assert(renderData);
@ -150,350 +601,6 @@ void WgCompositor::renderClipPath(WgContext& context, WgRenderDataPaint* renderD
} }
void WgCompositor::renderShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod)
{
assert(renderData);
assert(renderPassEncoder);
WgPipelineBlendType blentType = blendMethodToBlendType(blendMethod);
// apply clip path if neccessary
if (renderData->clips.count != 0) {
renderClipPath(context, renderData, &storageClipPath);
if (renderData->strokeFirst) {
drawStrokesClipped(context, renderData, &storageClipPath);
drawShapeClipped(context, renderData, &storageClipPath);
} else {
drawShapeClipped(context, renderData, &storageClipPath);
drawStrokesClipped(context, renderData, &storageClipPath);
}
// use custom blending
} else if (blentType == WgPipelineBlendType::Custom) {
if (renderData->strokeFirst) {
blendStrokes(context, renderData, blendMethod);
blendShape(context, renderData, blendMethod);
} else {
blendShape(context, renderData, blendMethod);
blendStrokes(context, renderData, blendMethod);
}
// use direct hardware blending
} else {
if (renderData->strokeFirst) {
drawStrokes(context, renderData, blentType);
drawShape(context, renderData, blentType);
} else {
drawShape(context, renderData, blentType);
drawStrokes(context, renderData, blentType);
}
}
}
void WgCompositor::renderImage(WgContext& context, WgRenderDataPicture* renderData, BlendMethod blendMethod)
{
assert(renderData);
assert(renderPassEncoder);
WgPipelineBlendType blentType = blendMethodToBlendType(blendMethod);
// apply clip path if neccessary
if (renderData->clips.count != 0) {
renderClipPath(context, renderData, &storageClipPath);
drawImageClipped(context, renderData, &storageClipPath);
// use custom blending
} else if (blentType == WgPipelineBlendType::Custom)
blendImage(context, renderData, blendMethod);
else // use direct hardware blending
drawImage(context, renderData, blentType);
};
void WgCompositor::blendShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod)
{
assert(renderData);
assert(commandEncoder);
assert(currentTarget);
// skip shape blending if shapes do not exist
if (renderData->renderSettingsShape.skip) return;
if (renderData->meshGroupShapes.meshes.count == 0) return;
// store current render pass
WgRenderStorage *target = currentTarget;
endRenderPass();
// render into intermediate buffer
beginRenderPass(commandEncoder, &storageInterm, true);
drawShape(context, renderData, WgPipelineBlendType::Custom);
endRenderPass();
// run blend
blend(commandEncoder, &storageInterm, target, 255, blendMethod, renderData->renderSettingsShape.rasterType);
// restore current render pass
beginRenderPass(commandEncoder, target, false);
}
void WgCompositor::blendStrokes(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod)
{
assert(renderData);
assert(commandEncoder);
assert(currentTarget);
// skip strokes blending if strokes do not exist
if (renderData->renderSettingsStroke.skip) return;
if (renderData->meshGroupStrokes.meshes.count == 0) return;
// store current render pass
WgRenderStorage *target = currentTarget;
endRenderPass();
// render into intermediate buffer
beginRenderPass(commandEncoder, &storageInterm, true);
drawStrokes(context, renderData, WgPipelineBlendType::Custom);
endRenderPass();
// run blend
blend(commandEncoder, &storageInterm, target, 255, blendMethod, renderData->renderSettingsStroke.rasterType);
// restore current render pass
beginRenderPass(commandEncoder, target, false);
};
void WgCompositor::blendImage(WgContext& context, WgRenderDataPicture* renderData, BlendMethod blendMethod)
{
assert(renderData);
assert(commandEncoder);
assert(currentTarget);
// store current render pass
WgRenderStorage *target = currentTarget;
endRenderPass();
// render into intermediate buffer
beginRenderPass(commandEncoder, &storageInterm, true);
drawImage(context, renderData, WgPipelineBlendType::Custom);
endRenderPass();
// run blend
blend(commandEncoder, &storageInterm, target, 255, blendMethod, WgRenderRasterType::Image);
// restore current render pass
beginRenderPass(commandEncoder, target, false);
};
void WgCompositor::blendScene(WgContext& context, WgRenderStorage* src, WgCompose* cmp)
{
assert(currentTarget);
RenderRegion rect = shrinkRenderRegion(cmp->aabb);
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, rect.x, rect.y, rect.w, rect.h);
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, src->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, bindGroupOpacities[cmp->opacity], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->sceneBlend);
meshData.drawImage(context, renderPassEncoder);
}
void WgCompositor::drawShapeClipped(WgContext& context, WgRenderDataShape* renderData, WgRenderStorage* mask)
{
assert(mask);
assert(renderData);
assert(commandEncoder);
assert(currentTarget);
// skip shape composing if shape do not exist
if (renderData->renderSettingsShape.skip) return;
if (renderData->meshGroupShapes.meshes.count == 0) return;
// store current render pass
WgRenderStorage *target = currentTarget;
endRenderPass();
// render into intermediate buffer
beginRenderPass(commandEncoder, &storageInterm, true);
drawShape(context, renderData, WgPipelineBlendType::Custom);
endRenderPass();
// restore current render pass
beginRenderPass(commandEncoder, target, false);
RenderRegion rect = shrinkRenderRegion(renderData->aabb);
clipRegion(context, &storageInterm, mask, rect);
}
void WgCompositor::drawStrokesClipped(WgContext& context, WgRenderDataShape* renderData, WgRenderStorage* mask)
{
assert(mask);
assert(renderData);
assert(commandEncoder);
assert(currentTarget);
// skip shape composing if strokes do not exist
if (renderData->renderSettingsStroke.skip) return;
if (renderData->meshGroupStrokes.meshes.count == 0) return;
// store current render pass
WgRenderStorage *target = currentTarget;
endRenderPass();
// render into intermediate buffer
beginRenderPass(commandEncoder, &storageInterm, true);
drawStrokes(context, renderData, WgPipelineBlendType::Custom);
endRenderPass();
// restore current render pass
beginRenderPass(commandEncoder, target, false);
RenderRegion rect = shrinkRenderRegion(renderData->aabb);
clipRegion(context, &storageInterm, mask, rect);
}
void WgCompositor::drawImageClipped(WgContext& context, WgRenderDataPicture* renderData, WgRenderStorage* mask)
{
assert(mask);
assert(renderData);
assert(commandEncoder);
assert(currentTarget);
// store current render pass
WgRenderStorage *target = currentTarget;
endRenderPass();
// render into intermediate buffer
beginRenderPass(commandEncoder, &storageInterm, true);
drawImage(context, renderData, WgPipelineBlendType::Custom);
endRenderPass();
// restore current render pass
beginRenderPass(commandEncoder, target, false);
RenderRegion rect { 0, 0, (int32_t)width, (int32_t)height };
clipRegion(context, &storageInterm, mask, rect);
}
void WgCompositor::clipRegion(WgContext& context, WgRenderStorage* src, WgRenderStorage* mask, RenderRegion& rect)
{
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, rect.x, rect.y, rect.w, rect.h);
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, storageInterm.bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, mask->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->sceneClip);
meshData.drawImage(context, renderPassEncoder);
}
void WgCompositor::composeScene(WgContext& context, WgRenderStorage* src, WgRenderStorage* mask, WgCompose* cmp)
{
assert(cmp);
assert(src);
assert(mask);
assert(renderPassEncoder);
RenderRegion rect = shrinkRenderRegion(cmp->aabb);
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, rect.x, rect.y, rect.w, rect.h);
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, src->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, mask->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->sceneComp[(uint32_t)cmp->method]);
meshData.drawImage(context, renderPassEncoder);
}
void WgCompositor::drawClipPath(WgContext& context, WgRenderDataShape* renderData)
{
assert(renderData);
assert(renderPassEncoder);
assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count);
if (renderData->renderSettingsShape.skip) return;
if (renderData->meshGroupShapes.meshes.count == 0) return;
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h);
// setup stencil rules
WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::Winding) ? pipelines->winding : pipelines->evenodd;
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
// draw to stencil (first pass)
for (uint32_t i = 0; i < renderData->meshGroupShapes.meshes.count; i++)
renderData->meshGroupShapes.meshes[i]->drawFan(context, renderPassEncoder);
// setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->clipPath);
// draw to color (second pass)
renderData->meshDataBBox.drawFan(context, renderPassEncoder);
}
void WgCompositor::drawShape(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType)
{
assert(renderData);
assert(renderPassEncoder);
assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count);
uint32_t blendTypeInd = (uint32_t)blendType;
if (renderData->renderSettingsShape.skip) return;
if (renderData->meshGroupShapes.meshes.count == 0) return;
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h);
// setup stencil rules
WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::Winding) ? pipelines->winding : pipelines->evenodd;
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
// draw to stencil (first pass)
for (uint32_t i = 0; i < renderData->meshGroupShapes.meshes.count; i++)
renderData->meshGroupShapes.meshes[i]->drawFan(context, renderPassEncoder);
// setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
WgRenderSettings& settings = renderData->renderSettingsShape;
if (settings.fillType == WgRenderSettingsType::Solid) {
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupSolid, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->solid[blendTypeInd]);
} else if (settings.fillType == WgRenderSettingsType::Linear) {
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupGradient, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->linear[blendTypeInd]);
} else if (settings.fillType == WgRenderSettingsType::Radial) {
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupGradient, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->radial[blendTypeInd]);
}
// draw to color (second pass)
renderData->meshDataBBox.drawFan(context, renderPassEncoder);
}
void WgCompositor::drawStrokes(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType)
{
assert(renderData);
assert(renderPassEncoder);
assert(renderData->meshGroupStrokes.meshes.count == renderData->meshGroupStrokesBBox.meshes.count);
uint32_t blendTypeInd = (uint32_t)blendType;
if (renderData->renderSettingsStroke.skip) return;
if (renderData->meshGroupStrokes.meshes.count == 0) return;
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h);
// draw strokes to stencil (first pass)
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
// setup stencil rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->direct);
// draw to stencil (first pass)
renderData->meshGroupStrokes.meshes[i]->draw(context, renderPassEncoder);
// setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
WgRenderSettings& settings = renderData->renderSettingsStroke;
if (settings.fillType == WgRenderSettingsType::Solid) {
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupSolid, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->solid[blendTypeInd]);
} else if (settings.fillType == WgRenderSettingsType::Linear) {
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupGradient, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->linear[blendTypeInd]);
} else if (settings.fillType == WgRenderSettingsType::Radial) {
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupGradient, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->radial[blendTypeInd]);
}
// draw to color (second pass)
renderData->meshGroupStrokesBBox.meshes[i]->drawFan(context, renderPassEncoder);
}
}
void WgCompositor::drawImage(WgContext& context, WgRenderDataPicture* renderData, WgPipelineBlendType blendType)
{
assert(renderData);
assert(renderPassEncoder);
uint32_t blendTypeInd = (uint32_t)blendType;
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h);
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, renderData->bindGroupPicture, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->image[blendTypeInd]);
renderData->meshData.drawImage(context, renderPassEncoder);
}
void WgCompositor::mergeMasks(WGPUCommandEncoder encoder, WgRenderStorage* mask0, WgRenderStorage* mask1) void WgCompositor::mergeMasks(WGPUCommandEncoder encoder, WgRenderStorage* mask0, WgRenderStorage* mask1)
{ {
assert(mask0); assert(mask0);
@ -510,49 +617,7 @@ void WgCompositor::mergeMasks(WGPUCommandEncoder encoder, WgRenderStorage* mask0
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 0, mask0->bindGroupRead, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 0, mask0->bindGroupRead, 0, nullptr);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 1, storageDstCopy.bindGroupRead, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 1, storageDstCopy.bindGroupRead, 0, nullptr);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, mask1->bindGroupWrite, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, mask1->bindGroupWrite, 0, nullptr);
wgpuComputePassEncoderSetPipeline(computePassEncoder, pipelines->mergeMasks); wgpuComputePassEncoderSetPipeline(computePassEncoder, pipelines->merge_masks);
wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, (width + 7) / 8, (height + 7) / 8, 1); wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, (width + 7) / 8, (height + 7) / 8, 1);
wgpuComputePassEncoderEnd(computePassEncoder); wgpuComputePassEncoderEnd(computePassEncoder);
} }
void WgCompositor::blend(WGPUCommandEncoder encoder, WgRenderStorage* src, WgRenderStorage* dst, uint8_t opacity, BlendMethod blendMethod, WgRenderRasterType rasterType)
{
assert(src);
assert(dst);
assert(!renderPassEncoder);
WGPUComputePipeline pipeline = pipelines->blendImage[(size_t)blendMethod];
if (rasterType == WgRenderRasterType::Solid) pipeline = pipelines->blendSolid[(size_t)blendMethod];
if (rasterType == WgRenderRasterType::Gradient) pipeline = pipelines->blendGradient[(size_t)blendMethod];
// copy texture to texture
const WGPUImageCopyTexture texSrc { .texture = dst->texture };
const WGPUImageCopyTexture texDst { .texture = storageDstCopy.texture };
const WGPUExtent3D copySize { .width = width, .height = height, .depthOrArrayLayers = 1 };
wgpuCommandEncoderCopyTextureToTexture(encoder, &texSrc, &texDst, &copySize);
// execute compose shader
const WGPUComputePassDescriptor computePassDescriptor{};
WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(encoder, &computePassDescriptor);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 0, src->bindGroupRead, 0, nullptr);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 1, storageDstCopy.bindGroupRead, 0, nullptr);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, dst->bindGroupWrite, 0, nullptr);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 3, bindGroupOpacities[opacity], 0, nullptr);
wgpuComputePassEncoderSetPipeline(computePassEncoder, pipeline);
wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, (width + 7) / 8, (height + 7) / 8, 1);
wgpuComputePassEncoderEnd(computePassEncoder);
}
void WgCompositor::blit(WgContext& context, WGPUCommandEncoder encoder, WgRenderStorage* src, WGPUTextureView dstView) {
WGPURenderPassDepthStencilAttachment depthStencilAttachment{ .view = texViewStencil, .stencilLoadOp = WGPULoadOp_Load, .stencilStoreOp = WGPUStoreOp_Discard };
WGPURenderPassColorAttachment colorAttachment { .view = dstView, .loadOp = WGPULoadOp_Load, .storeOp = WGPUStoreOp_Store };
#ifdef __EMSCRIPTEN__
colorAttachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
#endif
WGPURenderPassDescriptor renderPassDesc{ .colorAttachmentCount = 1, .colorAttachments = &colorAttachment, .depthStencilAttachment = &depthStencilAttachment };
WGPURenderPassEncoder renderPass = wgpuCommandEncoderBeginRenderPass(encoder, &renderPassDesc);
wgpuRenderPassEncoderSetBindGroup(renderPass, 0, src->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPass, pipelines->blit);
meshData.drawImage(context, renderPass);
wgpuRenderPassEncoderEnd(renderPass);
wgpuRenderPassEncoderRelease(renderPass);
}

View file

@ -56,46 +56,53 @@ private:
WgRenderStorage storageDstCopy; WgRenderStorage storageDstCopy;
// composition and blend geometries // composition and blend geometries
WgMeshData meshData; WgMeshData meshData;
static WgPipelineBlendType blendMethodToBlendType(BlendMethod blendMethod);
void clipRegion(WgContext& context, WgRenderStorage* src, WgRenderStorage* mask, RenderRegion& rect);
RenderRegion shrinkRenderRegion(RenderRegion& rect);
public:
// render target dimensions // render target dimensions
uint32_t width{}; uint32_t width{};
uint32_t height{}; uint32_t height{};
// viewport utilities
RenderRegion shrinkRenderRegion(RenderRegion& rect);
public: public:
void initialize(WgContext& context, uint32_t width, uint32_t height); void initialize(WgContext& context, uint32_t width, uint32_t height);
void release(WgContext& context); void release(WgContext& context);
// render passes workflow
void beginRenderPass(WGPUCommandEncoder encoder, WgRenderStorage* target, bool clear); void beginRenderPass(WGPUCommandEncoder encoder, WgRenderStorage* target, bool clear);
void endRenderPass(); void endRenderPass();
void renderClipPath(WgContext& context, WgRenderDataPaint* renderData, WgRenderStorage* dst); // render shapes, images and scenes
void renderShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod); void renderShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod);
void renderImage(WgContext& context, WgRenderDataPicture* renderData, BlendMethod blendMethod); void renderImage(WgContext& context, WgRenderDataPicture* renderData, BlendMethod blendMethod);
void renderScene(WgContext& context, WgRenderStorage* scene, WgCompose* compose);
void composeScene(WgContext& context, WgRenderStorage* src, WgRenderStorage* mask, WgCompose* compose);
void blendShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod); // blit render storage to texture view (f.e. screen buffer)
void blendStrokes(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod);
void blendImage(WgContext& context, WgRenderDataPicture* renderData, BlendMethod blendMethod);
void blendScene(WgContext& context, WgRenderStorage* src, WgCompose* cmp);
void drawShapeClipped(WgContext& context, WgRenderDataShape* renderData, WgRenderStorage* mask);
void drawStrokesClipped(WgContext& context, WgRenderDataShape* renderData, WgRenderStorage* mask);
void drawImageClipped(WgContext& context, WgRenderDataPicture* renderData, WgRenderStorage* mask);
void composeScene(WgContext& context, WgRenderStorage* src, WgRenderStorage* mask, WgCompose* cmp);
void drawClipPath(WgContext& context, WgRenderDataShape* renderData);
void drawShape(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType);
void drawStrokes(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType);
void drawImage(WgContext& context, WgRenderDataPicture* renderData, WgPipelineBlendType blendType);
void mergeMasks(WGPUCommandEncoder encoder, WgRenderStorage* mask0, WgRenderStorage* mask1);
void blend(WGPUCommandEncoder encoder, WgRenderStorage* src, WgRenderStorage* dst, uint8_t opacity, BlendMethod blendMethod, WgRenderRasterType rasterType);
void blit(WgContext& context, WGPUCommandEncoder encoder, WgRenderStorage* src, WGPUTextureView dstView); void blit(WgContext& context, WGPUCommandEncoder encoder, WgRenderStorage* src, WGPUTextureView dstView);
private:
// shapes
void drawShape(WgContext& context, WgRenderDataShape* renderData);
void blendShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod);
void clipShape(WgContext& context, WgRenderDataShape* renderData, WgRenderStorage* mask); // TODO: optimize
// strokes
void drawStrokes(WgContext& context, WgRenderDataShape* renderData);
void blendStrokes(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod);
void clipStrokes(WgContext& context, WgRenderDataShape* renderData, WgRenderStorage* mask); // TODO: optimize
// images
void drawImage(WgContext& context, WgRenderDataPicture* renderData);
void blendImage(WgContext& context, WgRenderDataPicture* renderData, BlendMethod blendMethod);
void clipImage(WgContext& context, WgRenderDataPicture* renderData, WgRenderStorage* mask); // TODO: optimize
// scenes
void drawScene(WgContext& context, WgRenderStorage* scene, WgCompose* compose);
void blendScene(WgContext& context, WgRenderStorage* src, WgCompose* compose);
private:
// clip path utils (TODO: optimize)
void drawClipPath(WgContext& context, WgRenderDataShape* renderData);
void clipRegion(WgContext& context, WgRenderStorage* src, WgRenderStorage* mask, RenderRegion& rect);
void renderClipPath(WgContext& context, WgRenderDataPaint* renderData, WgRenderStorage* dst);
void mergeMasks(WGPUCommandEncoder encoder, WgRenderStorage* mask0, WgRenderStorage* mask1);
}; };
#endif // _TVG_WG_COMPOSITOR_H_ #endif // _TVG_WG_COMPOSITOR_H_

View file

@ -158,81 +158,68 @@ void WgPipelines::initialize(WgContext& context)
.color = { .operation = WGPUBlendOperation_Add, .srcFactor = WGPUBlendFactor_One, .dstFactor = WGPUBlendFactor_OneMinusSrcAlpha }, .color = { .operation = WGPUBlendOperation_Add, .srcFactor = WGPUBlendFactor_One, .dstFactor = WGPUBlendFactor_OneMinusSrcAlpha },
.alpha = { .operation = WGPUBlendOperation_Add, .srcFactor = WGPUBlendFactor_One, .dstFactor = WGPUBlendFactor_OneMinusSrcAlpha } .alpha = { .operation = WGPUBlendOperation_Add, .srcFactor = WGPUBlendFactor_One, .dstFactor = WGPUBlendFactor_OneMinusSrcAlpha }
}; };
const WGPUBlendState blendStates[] {
blendStateNrm, // WgPipelineBlendType::Normal
blendStateSrc // WgPipelineBlendType::Custom (same as SrcOver)
};
// bind group layouts // bind group layouts helpers
const WGPUBindGroupLayout bindGroupLayoutsStencil[] = { const WGPUBindGroupLayout bindGroupLayoutsStencil[] = { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un };
layouts.layoutBuffer1Un, // bind group layouts normal blend
layouts.layoutBuffer2Un const WGPUBindGroupLayout bindGroupLayoutsSolid[] { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un, layouts.layoutBuffer1Un };
}; const WGPUBindGroupLayout bindGroupLayoutsGradient[] { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un, layouts.layoutTexSampledBuff2Un };
const WGPUBindGroupLayout bindGroupLayoutsSolid[] = { const WGPUBindGroupLayout bindGroupLayoutsImage[] { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un, layouts.layoutTexSampled };
layouts.layoutBuffer1Un, const WGPUBindGroupLayout bindGroupLayoutsScene[] { layouts.layoutTexSampled, layouts.layoutBuffer1Un };
layouts.layoutBuffer2Un, // bind group layouts custom blend
layouts.layoutBuffer1Un const WGPUBindGroupLayout bindGroupLayoutsSolidBlend[] { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un, layouts.layoutBuffer1Un, layouts.layoutTexSampled };
}; const WGPUBindGroupLayout bindGroupLayoutsGradientBlend[] { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un, layouts.layoutTexSampledBuff2Un, layouts.layoutTexSampled };
const WGPUBindGroupLayout bindGroupLayoutsGradient[] = { const WGPUBindGroupLayout bindGroupLayoutsImageBlend[] { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un, layouts.layoutTexSampled, layouts.layoutTexSampled };
layouts.layoutBuffer1Un, const WGPUBindGroupLayout bindGroupLayoutsSceneBlend[] { layouts.layoutTexSampled, layouts.layoutTexSampled, layouts.layoutBuffer1Un };
layouts.layoutBuffer2Un, // bind group layouts scene compose
layouts.layoutTexSampledBuff2Un const WGPUBindGroupLayout bindGroupLayoutsSceneCompose[] { layouts.layoutTexSampled, layouts.layoutTexSampled };
}; const WGPUBindGroupLayout bindGroupLayoutsMergeMasks[] { layouts.layoutTexStrorage1RO, layouts.layoutTexStrorage1RO, layouts.layoutTexStrorage1WO };
const WGPUBindGroupLayout bindGroupLayoutsImage[] = { // bind group layouts blit
layouts.layoutBuffer1Un, const WGPUBindGroupLayout bindGroupLayoutsBlit[] { layouts.layoutTexSampled };
layouts.layoutBuffer2Un,
layouts.layoutTexSampled
};
const WGPUBindGroupLayout bindGroupLayoutsSceneComp[] = {
layouts.layoutTexSampled,
layouts.layoutTexSampled
};
const WGPUBindGroupLayout bindGroupLayoutsSceneBlend[] = {
layouts.layoutTexSampled,
layouts.layoutBuffer1Un
};
const WGPUBindGroupLayout bindGroupLayoutsBlit[] = {
layouts.layoutTexSampled
};
const WGPUBindGroupLayout bindGroupLayoutsMergeMasks[] = {
layouts.layoutTexStrorage1RO,
layouts.layoutTexStrorage1RO,
layouts.layoutTexStrorage1WO
};
const WGPUBindGroupLayout bindGroupLayoutsBlend[] = {
layouts.layoutTexStrorage1RO,
layouts.layoutTexStrorage1RO,
layouts.layoutTexStrorage1WO,
layouts.layoutBuffer1Un
};
// pipeline layouts // shaders
layoutStencil = createPipelineLayout(context.device, bindGroupLayoutsStencil, 2); char shaderSourceBuff[16384]{};
layoutSolid = createPipelineLayout(context.device, bindGroupLayoutsSolid, 3); shader_stencil = createShaderModule(context.device, "The shader stencil", cShaderSrc_Stencil);
layoutGradient = createPipelineLayout(context.device, bindGroupLayoutsGradient, 3); // shader normal blend
layoutImage = createPipelineLayout(context.device, bindGroupLayoutsImage, 3); shader_solid = createShaderModule(context.device, "The shader solid", cShaderSrc_Solid);
layoutSceneComp = createPipelineLayout(context.device, bindGroupLayoutsSceneComp, 2); shader_radial = createShaderModule(context.device, "The shader radial", cShaderSrc_Radial);
layoutSceneBlend = createPipelineLayout(context.device, bindGroupLayoutsSceneBlend, 2); shader_linear = createShaderModule(context.device, "The shader linear", cShaderSrc_Linear);
layoutBlit = createPipelineLayout(context.device, bindGroupLayoutsBlit, 1); shader_image = createShaderModule(context.device, "The shader image", cShaderSrc_Image);
layoutBlend = createPipelineLayout(context.device, bindGroupLayoutsBlend, 4); shader_scene = createShaderModule(context.device, "The shader scene", cShaderSrc_Scene);
layoutMergeMasks = createPipelineLayout(context.device, bindGroupLayoutsMergeMasks, 3); // shader custom blend
shader_solid_blend = createShaderModule(context.device, "The shader blend solid", strcat(strcpy(shaderSourceBuff, cShaderSrc_Solid_Blend), cShaderSrc_BlendFuncs));
shader_linear_blend = createShaderModule(context.device, "The shader blend linear", strcat(strcpy(shaderSourceBuff, cShaderSrc_Linear_Blend), cShaderSrc_BlendFuncs));
shader_radial_blend = createShaderModule(context.device, "The shader blend radial", strcat(strcpy(shaderSourceBuff, cShaderSrc_Radial_Blend), cShaderSrc_BlendFuncs));
shader_image_blend = createShaderModule(context.device, "The shader blend image", strcat(strcpy(shaderSourceBuff, cShaderSrc_Image_Blend), cShaderSrc_BlendFuncs));
shader_scene_blend = createShaderModule(context.device, "The shader blend scene", strcat(strcpy(shaderSourceBuff, cShaderSrc_Scene_Blend), cShaderSrc_BlendFuncs));
// shader compose
shader_scene_compose = createShaderModule(context.device, "The shader scene composition", cShaderSrc_Scene_Compose);
shader_merge_masks = createShaderModule(context.device, "The shader merge mask", cShaderSrc_MergeMasks);
// shader blit
shader_blit = createShaderModule(context.device, "The shader blit", cShaderSrc_Blit);
// graphics shader modules // layouts
shaderStencil = createShaderModule(context.device, "The shader stencil", cShaderSrc_Stencil); layout_stencil = createPipelineLayout(context.device, bindGroupLayoutsStencil, 2);
shaderSolid = createShaderModule(context.device, "The shader solid", cShaderSrc_Solid); // layouts normal blend
shaderRadial = createShaderModule(context.device, "The shader radial", cShaderSrc_Radial); layout_solid = createPipelineLayout(context.device, bindGroupLayoutsSolid, 3);
shaderLinear = createShaderModule(context.device, "The shader linear", cShaderSrc_Linear); layout_gradient = createPipelineLayout(context.device, bindGroupLayoutsGradient, 3);
shaderImage = createShaderModule(context.device, "The shader image", cShaderSrc_Image); layout_image = createPipelineLayout(context.device, bindGroupLayoutsImage, 3);
shaderSceneComp = createShaderModule(context.device, "The shader scene composition", cShaderSrc_Scene_Comp); layout_scene = createPipelineLayout(context.device, bindGroupLayoutsScene, 2);
shaderSceneBlend = createShaderModule(context.device, "The shader scene blend", cShaderSrc_Scene_Blend); // layouts custom blend
shaderBlit = createShaderModule(context.device, "The shader blit", cShaderSrc_Blit); layout_solid_blend = createPipelineLayout(context.device, bindGroupLayoutsSolidBlend, 4);
// computes shader modules layout_gradient_blend = createPipelineLayout(context.device, bindGroupLayoutsGradientBlend, 4);
shaderMergeMasks = createShaderModule(context.device, "The shader merge mask", cShaderSrc_MergeMasks); layout_image_blend = createPipelineLayout(context.device, bindGroupLayoutsImageBlend, 4);
layout_scene_blend = createPipelineLayout(context.device, bindGroupLayoutsSceneBlend, 3);
// layout compose
layout_scene_compose = createPipelineLayout(context.device, bindGroupLayoutsSceneCompose, 2);
layout_merge_masks = createPipelineLayout(context.device, bindGroupLayoutsMergeMasks, 3);
// layout blit
layout_blit = createPipelineLayout(context.device, bindGroupLayoutsBlit, 1);
// render pipeline winding // render pipeline winding
winding = createRenderPipeline( winding = createRenderPipeline(
context.device, "The render pipeline winding", context.device, "The render pipeline winding",
shaderStencil, "vs_main", "fs_main", layoutStencil, shader_stencil, "vs_main", "fs_main", layout_stencil,
vertexBufferLayoutsShape, 1, vertexBufferLayoutsShape, 1,
WGPUColorWriteMask_None, offscreenTargetFormat, WGPUColorWriteMask_None, offscreenTargetFormat,
WGPUCompareFunction_Always, WGPUStencilOperation_IncrementWrap, WGPUCompareFunction_Always, WGPUStencilOperation_IncrementWrap,
@ -241,7 +228,7 @@ void WgPipelines::initialize(WgContext& context)
// render pipeline even-odd // render pipeline even-odd
evenodd = createRenderPipeline( evenodd = createRenderPipeline(
context.device, "The render pipeline even-odd", context.device, "The render pipeline even-odd",
shaderStencil, "vs_main", "fs_main", layoutStencil, shader_stencil, "vs_main", "fs_main", layout_stencil,
vertexBufferLayoutsShape, 1, vertexBufferLayoutsShape, 1,
WGPUColorWriteMask_None, offscreenTargetFormat, WGPUColorWriteMask_None, offscreenTargetFormat,
WGPUCompareFunction_Always, WGPUStencilOperation_Invert, WGPUCompareFunction_Always, WGPUStencilOperation_Invert,
@ -250,16 +237,16 @@ void WgPipelines::initialize(WgContext& context)
// render pipeline direct // render pipeline direct
direct = createRenderPipeline( direct = createRenderPipeline(
context.device, "The render pipeline direct", context.device, "The render pipeline direct",
shaderStencil,"vs_main", "fs_main", layoutStencil, shader_stencil, "vs_main", "fs_main", layout_stencil,
vertexBufferLayoutsShape, 1, vertexBufferLayoutsShape, 1,
WGPUColorWriteMask_None, offscreenTargetFormat, WGPUColorWriteMask_None, offscreenTargetFormat,
WGPUCompareFunction_Always, WGPUStencilOperation_Replace, WGPUCompareFunction_Always, WGPUStencilOperation_Replace,
WGPUCompareFunction_Always, WGPUStencilOperation_Replace, WGPUCompareFunction_Always, WGPUStencilOperation_Replace,
primitiveState, multisampleState, blendStateSrc); primitiveState, multisampleState, blendStateSrc);
// render pipeline clip path // render pipeline clip path
clipPath = createRenderPipeline( clip_path = createRenderPipeline(
context.device, "The render pipeline clip path", context.device, "The render pipeline clip path",
shaderStencil, "vs_main", "fs_main", layoutStencil, shader_stencil, "vs_main", "fs_main", layout_stencil,
vertexBufferLayoutsShape, 1, vertexBufferLayoutsShape, 1,
WGPUColorWriteMask_All, offscreenTargetFormat, WGPUColorWriteMask_All, offscreenTargetFormat,
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero, WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
@ -267,81 +254,116 @@ void WgPipelines::initialize(WgContext& context)
primitiveState, multisampleState, blendStateSrc); primitiveState, multisampleState, blendStateSrc);
// render pipeline solid // render pipeline solid
for (uint32_t i = 0; i < 2; i++) { solid = createRenderPipeline(
solid[i] = createRenderPipeline(
context.device, "The render pipeline solid", context.device, "The render pipeline solid",
shaderSolid, "vs_main", "fs_main", layoutSolid, shader_solid, "vs_main", "fs_main", layout_solid,
vertexBufferLayoutsShape, 1, vertexBufferLayoutsShape, 1,
WGPUColorWriteMask_All, offscreenTargetFormat, WGPUColorWriteMask_All, offscreenTargetFormat,
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero, WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero, WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
primitiveState, multisampleState, blendStates[i]); primitiveState, multisampleState, blendStateNrm);
}
// render pipeline radial // render pipeline radial
for (uint32_t i = 0; i < 2; i++) { radial = createRenderPipeline(
radial[i] = createRenderPipeline(
context.device, "The render pipeline radial", context.device, "The render pipeline radial",
shaderRadial, "vs_main", "fs_main", layoutGradient, shader_radial, "vs_main", "fs_main", layout_gradient,
vertexBufferLayoutsShape, 1, vertexBufferLayoutsShape, 1,
WGPUColorWriteMask_All, offscreenTargetFormat, WGPUColorWriteMask_All, offscreenTargetFormat,
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero, WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero, WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
primitiveState, multisampleState, blendStates[i]); primitiveState, multisampleState, blendStateNrm);
}
// render pipeline linear // render pipeline linear
for (uint32_t i = 0; i < 2; i++) { linear = createRenderPipeline(
linear[i] = createRenderPipeline(
context.device, "The render pipeline linear", context.device, "The render pipeline linear",
shaderLinear, "vs_main", "fs_main", layoutGradient, shader_linear, "vs_main", "fs_main", layout_gradient,
vertexBufferLayoutsShape, 1, vertexBufferLayoutsShape, 1,
WGPUColorWriteMask_All, offscreenTargetFormat, WGPUColorWriteMask_All, offscreenTargetFormat,
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero, WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero, WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
primitiveState, multisampleState, blendStates[i]); primitiveState, multisampleState, blendStateNrm);
}
// render pipeline image // render pipeline image
for (uint32_t i = 0; i < 2; i++) { image = createRenderPipeline(
image[i] = createRenderPipeline(
context.device, "The render pipeline image", context.device, "The render pipeline image",
shaderImage, "vs_main", "fs_main", layoutImage, shader_image, "vs_main", "fs_main", layout_image,
vertexBufferLayoutsImage, 2, vertexBufferLayoutsImage, 2,
WGPUColorWriteMask_All, offscreenTargetFormat, WGPUColorWriteMask_All, offscreenTargetFormat,
WGPUCompareFunction_Always, WGPUStencilOperation_Zero, WGPUCompareFunction_Always, WGPUStencilOperation_Zero,
WGPUCompareFunction_Always, WGPUStencilOperation_Zero, WGPUCompareFunction_Always, WGPUStencilOperation_Zero,
primitiveState, multisampleState, blendStates[i]); primitiveState, multisampleState, blendStateNrm);
} // render pipeline scene
scene = createRenderPipeline(
// render pipeline blit context.device, "The render pipeline scene",
blit = createRenderPipeline(context.device, "The render pipeline blit", shader_scene, "vs_main", "fs_main", layout_scene,
shaderBlit, "vs_main", "fs_main", layoutBlit,
vertexBufferLayoutsImage, 2, vertexBufferLayoutsImage, 2,
// must be preferred screen pixel format WGPUColorWriteMask_All, offscreenTargetFormat,
WGPUColorWriteMask_All, context.preferredFormat, WGPUCompareFunction_Always, WGPUStencilOperation_Zero,
WGPUCompareFunction_Always, WGPUStencilOperation_Zero,
primitiveState, multisampleState, blendStateNrm);
// blend shader names
const char* shaderBlendNames[] {
"fs_main_Normal",
"fs_main_Multiply",
"fs_main_Screen",
"fs_main_Overlay",
"fs_main_Darken",
"fs_main_Lighten",
"fs_main_ColorDodge",
"fs_main_ColorBurn",
"fs_main_HardLight",
"fs_main_SoftLight",
"fs_main_Difference",
"fs_main_Exclusion",
"fs_main_Normal", //TODO: a padding for reserved Hue.
"fs_main_Normal", //TODO: a padding for reserved Saturation.
"fs_main_Normal", //TODO: a padding for reserved Color.
"fs_main_Normal", //TODO: a padding for reserved Luminosity.
"fs_main_Add",
"fs_main_Normal" //TODO: a padding for reserved Hardmix.
};
// render pipeline shape blend
for (uint32_t i = 0; i < 18; i++) {
// blend solid
solid_blend[i] = createRenderPipeline(context.device, "The render pipeline solid blend",
shader_solid_blend, "vs_main", shaderBlendNames[i], layout_solid_blend,
vertexBufferLayoutsShape, 1,
WGPUColorWriteMask_All, offscreenTargetFormat,
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
primitiveState, multisampleState, blendStateSrc);
// blend radial
radial_blend[i] = createRenderPipeline(context.device, "The render pipeline radial blend",
shader_radial_blend, "vs_main", shaderBlendNames[i], layout_gradient_blend,
vertexBufferLayoutsShape, 1,
WGPUColorWriteMask_All, offscreenTargetFormat,
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
primitiveState, multisampleState, blendStateSrc);
// blend linear
linear_blend[i] = createRenderPipeline(context.device, "The render pipeline linear blend",
shader_linear_blend, "vs_main", shaderBlendNames[i], layout_gradient_blend,
vertexBufferLayoutsShape, 1,
WGPUColorWriteMask_All, offscreenTargetFormat,
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
primitiveState, multisampleState, blendStateSrc);
// blend image
image_blend[i] = createRenderPipeline(context.device, "The render pipeline image blend",
shader_image_blend, "vs_main", shaderBlendNames[i], layout_image_blend,
vertexBufferLayoutsImage, 2,
WGPUColorWriteMask_All, offscreenTargetFormat,
WGPUCompareFunction_Always, WGPUStencilOperation_Zero, WGPUCompareFunction_Always, WGPUStencilOperation_Zero,
WGPUCompareFunction_Always, WGPUStencilOperation_Zero, WGPUCompareFunction_Always, WGPUStencilOperation_Zero,
primitiveState, multisampleState, blendStateSrc); primitiveState, multisampleState, blendStateSrc);
// blend scene
// render pipeline blend scene_blend[i] = createRenderPipeline(context.device, "The render pipeline scene blend",
sceneBlend = createRenderPipeline(context.device, "The render pipeline scene blend", shader_scene_blend, "vs_main", shaderBlendNames[i], layout_scene_blend,
shaderSceneBlend, "vs_main", "fs_main", layoutSceneBlend,
vertexBufferLayoutsImage, 2, vertexBufferLayoutsImage, 2,
WGPUColorWriteMask_All, offscreenTargetFormat, WGPUColorWriteMask_All, offscreenTargetFormat,
WGPUCompareFunction_Always, WGPUStencilOperation_Zero, WGPUCompareFunction_Always, WGPUStencilOperation_Zero,
WGPUCompareFunction_Always, WGPUStencilOperation_Zero, WGPUCompareFunction_Always, WGPUStencilOperation_Zero,
primitiveState, multisampleState, blendStateNrm); primitiveState, multisampleState, blendStateSrc);
}
// render pipeline scene clip path
sceneClip = createRenderPipeline(
context.device, "The render pipeline scene clip path",
shaderSceneComp, "vs_main", "fs_main_ClipPath", layoutSceneComp,
vertexBufferLayoutsImage, 2,
WGPUColorWriteMask_All, offscreenTargetFormat,
WGPUCompareFunction_Always, WGPUStencilOperation_Zero,
WGPUCompareFunction_Always, WGPUStencilOperation_Zero,
primitiveState, multisampleState, blendStateNrm);
// compose shader names // compose shader names
const char* shaderComposeNames[] { const char* shaderComposeNames[] {
@ -374,15 +396,10 @@ void WgPipelines::initialize(WgContext& context)
}; };
// render pipeline scene composition // render pipeline scene composition
size_t shaderComposeNamesCnt = sizeof(shaderComposeNames) / sizeof(shaderComposeNames[0]); for (uint32_t i = 0; i < 11; i++) {
size_t composeBlendspCnt = sizeof(composeBlends) / sizeof(composeBlends[0]); scene_compose[i] = createRenderPipeline(
size_t sceneCompCnt = sizeof(sceneComp) / sizeof(sceneComp[0]);
assert(shaderComposeNamesCnt == composeBlendspCnt);
assert(composeBlendspCnt == sceneCompCnt);
for (uint32_t i = 0; i < sceneCompCnt; i++) {
sceneComp[i] = createRenderPipeline(
context.device, "The render pipeline scene composition", context.device, "The render pipeline scene composition",
shaderSceneComp, "vs_main", shaderComposeNames[i], layoutSceneComp, shader_scene_compose, "vs_main", shaderComposeNames[i], layout_scene_compose,
vertexBufferLayoutsImage, 2, vertexBufferLayoutsImage, 2,
WGPUColorWriteMask_All, offscreenTargetFormat, WGPUColorWriteMask_All, offscreenTargetFormat,
WGPUCompareFunction_Always, WGPUStencilOperation_Zero, WGPUCompareFunction_Always, WGPUStencilOperation_Zero,
@ -390,99 +407,95 @@ void WgPipelines::initialize(WgContext& context)
primitiveState, multisampleState, composeBlends[i]); primitiveState, multisampleState, composeBlends[i]);
} }
// render pipeline scene clip path
scene_clip = createRenderPipeline(
context.device, "The render pipeline scene clip path",
shader_scene_compose, "vs_main", "fs_main_ClipPath", layout_scene_compose,
vertexBufferLayoutsImage, 2,
WGPUColorWriteMask_All, offscreenTargetFormat,
WGPUCompareFunction_Always, WGPUStencilOperation_Zero,
WGPUCompareFunction_Always, WGPUStencilOperation_Zero,
primitiveState, multisampleState, blendStateNrm);
// compute pipelines // compute pipelines
mergeMasks = createComputePipeline(context.device, "The pipeline merge masks", shaderMergeMasks, "cs_main", layoutMergeMasks); merge_masks = createComputePipeline(
context.device, "The compute pipeline merge masks",
shader_merge_masks, "cs_main", layout_merge_masks);
// compute shader blend names // render pipeline blit
const char* shaderBlendNames[] { blit = createRenderPipeline(context.device, "The render pipeline blit",
"cs_main_Normal", shader_blit, "vs_main", "fs_main", layout_blit,
"cs_main_Multiply", vertexBufferLayoutsImage, 2,
"cs_main_Screen", // must be preferred screen pixel format
"cs_main_Overlay", WGPUColorWriteMask_All, context.preferredFormat,
"cs_main_Darken", WGPUCompareFunction_Always, WGPUStencilOperation_Zero,
"cs_main_Lighten", WGPUCompareFunction_Always, WGPUStencilOperation_Zero,
"cs_main_ColorDodge", primitiveState, multisampleState, blendStateSrc);
"cs_main_ColorBurn",
"cs_main_HardLight",
"cs_main_SoftLight",
"cs_main_Difference",
"cs_main_Exclusion",
"cs_main_Normal", //TODO: a padding for reserved Hue.
"cs_main_Normal", //TODO: a padding for reserved Saturation.
"cs_main_Normal", //TODO: a padding for reserved Color.
"cs_main_Normal", //TODO: a padding for reserved Luminosity.
"cs_main_Add",
"cs_main_Normal" //TODO: a padding for reserved Hardmix.
};
// create blend shaders
char shaderSourceBuff[16384];
shaderBlendSolid = createShaderModule(context.device, "The shader blend solid", strcat(strcpy(shaderSourceBuff, cShaderSrc_BlendHeader_Solid), cShaderSrc_Blend_Funcs));
shaderBlendGradient = createShaderModule(context.device, "The shader blend gradient", strcat(strcpy(shaderSourceBuff, cShaderSrc_BlendHeader_Gradient), cShaderSrc_Blend_Funcs));
shaderBlendImage = createShaderModule(context.device, "The shader blend image", strcat(strcpy(shaderSourceBuff, cShaderSrc_BlendHeader_Image), cShaderSrc_Blend_Funcs));
// create blend pipelines
const size_t shaderBlendNamesCnt = sizeof(shaderBlendNames) / sizeof(shaderBlendNames[0]);
const size_t pipesBlendCnt = sizeof(blendSolid) / sizeof(blendSolid[0]);
assert(shaderBlendNamesCnt == pipesBlendCnt);
for (uint32_t i = 0; i < pipesBlendCnt; i++) {
blendSolid[i] = createComputePipeline(context.device, "The pipeline blend solid", shaderBlendSolid, shaderBlendNames[i], layoutBlend);
blendGradient[i] = createComputePipeline(context.device, "The pipeline blend gradient", shaderBlendGradient, shaderBlendNames[i], layoutBlend);
blendImage[i] = createComputePipeline(context.device, "The pipeline blend image", shaderBlendImage, shaderBlendNames[i], layoutBlend);
}
} }
void WgPipelines::releaseGraphicHandles(WgContext& context) void WgPipelines::releaseGraphicHandles(WgContext& context)
{ {
// pipeline blit
releaseRenderPipeline(blit); releaseRenderPipeline(blit);
releaseRenderPipeline(clipPath); // pipelines compose
releaseRenderPipeline(sceneBlend); releaseRenderPipeline(scene_clip);
releaseRenderPipeline(sceneClip); for (uint32_t i = 0; i < 11; i++)
size_t pipesSceneCompCnt = sizeof(sceneComp) / sizeof(sceneComp[0]); releaseRenderPipeline(scene_compose[i]);
for (uint32_t i = 0; i < pipesSceneCompCnt; i++) // pipelines custom blend
releaseRenderPipeline(sceneComp[i]); for (uint32_t i = 0; i < 18; i++) {
for (uint32_t i = 0; i < 2; i++) { releaseRenderPipeline(scene_blend[i]);
releaseRenderPipeline(image[i]); releaseRenderPipeline(image_blend[i]);
releaseRenderPipeline(linear[i]); releaseRenderPipeline(linear_blend[i]);
releaseRenderPipeline(radial[i]); releaseRenderPipeline(radial_blend[i]);
releaseRenderPipeline(solid[i]); releaseRenderPipeline(solid_blend[i]);
} }
// pipelines normal blend
releaseRenderPipeline(scene);
releaseRenderPipeline(image);
releaseRenderPipeline(linear);
releaseRenderPipeline(radial);
releaseRenderPipeline(solid);
// pipelines helpers
releaseRenderPipeline(clip_path);
releaseRenderPipeline(direct); releaseRenderPipeline(direct);
releaseRenderPipeline(evenodd); releaseRenderPipeline(evenodd);
releaseRenderPipeline(winding); releaseRenderPipeline(winding);
releasePipelineLayout(layoutBlit); // layouts
releasePipelineLayout(layoutSceneBlend); releasePipelineLayout(layout_blit);
releasePipelineLayout(layoutSceneComp); releasePipelineLayout(layout_merge_masks);
releasePipelineLayout(layoutImage); releasePipelineLayout(layout_scene_compose);
releasePipelineLayout(layoutGradient); releasePipelineLayout(layout_scene_blend);
releasePipelineLayout(layoutSolid); releasePipelineLayout(layout_image_blend);
releasePipelineLayout(layoutStencil); releasePipelineLayout(layout_gradient_blend);
releaseShaderModule(shaderBlit); releasePipelineLayout(layout_solid_blend);
releaseShaderModule(shaderSceneBlend); releasePipelineLayout(layout_scene);
releaseShaderModule(shaderSceneComp); releasePipelineLayout(layout_image);
releaseShaderModule(shaderImage); releasePipelineLayout(layout_gradient);
releaseShaderModule(shaderLinear); releasePipelineLayout(layout_solid);
releaseShaderModule(shaderRadial); releasePipelineLayout(layout_stencil);
releaseShaderModule(shaderSolid); // shaders
releaseShaderModule(shaderStencil); releaseShaderModule(shader_blit);
releaseShaderModule(shader_merge_masks);
releaseShaderModule(shader_scene_compose);
releaseShaderModule(shader_scene_blend);
releaseShaderModule(shader_image_blend);
releaseShaderModule(shader_linear_blend);
releaseShaderModule(shader_radial_blend);
releaseShaderModule(shader_solid_blend);
releaseShaderModule(shader_scene);
releaseShaderModule(shader_image);
releaseShaderModule(shader_linear);
releaseShaderModule(shader_radial);
releaseShaderModule(shader_solid);
releaseShaderModule(shader_stencil);
} }
void WgPipelines::releaseComputeHandles(WgContext& context) void WgPipelines::releaseComputeHandles(WgContext& context)
{ {
const size_t pipesBlendCnt = sizeof(blendSolid)/sizeof(blendSolid[0]); releaseComputePipeline(merge_masks);
for (uint32_t i = 0; i < pipesBlendCnt; i++) { releasePipelineLayout(layout_merge_masks);
releaseComputePipeline(blendImage[i]); releaseShaderModule(shader_merge_masks);
releaseComputePipeline(blendSolid[i]);
releaseComputePipeline(blendGradient[i]);
}
releaseComputePipeline(mergeMasks);
releasePipelineLayout(layoutBlend);
releasePipelineLayout(layoutMergeMasks);
releaseShaderModule(shaderBlendImage);
releaseShaderModule(shaderBlendGradient);
releaseShaderModule(shaderBlendSolid);
releaseShaderModule(shaderMergeMasks);
} }
void WgPipelines::release(WgContext& context) void WgPipelines::release(WgContext& context)

View file

@ -25,56 +25,72 @@
#include "tvgWgBindGroups.h" #include "tvgWgBindGroups.h"
enum class WgPipelineBlendType { Normal, Custom };
class WgPipelines { class WgPipelines {
private: private:
// graphics pipeline shaders // shaders helpers
WGPUShaderModule shaderStencil{}; WGPUShaderModule shader_stencil{};
WGPUShaderModule shaderSolid{}; // shaders normal blend
WGPUShaderModule shaderRadial{}; WGPUShaderModule shader_solid{};
WGPUShaderModule shaderLinear{}; WGPUShaderModule shader_radial{};
WGPUShaderModule shaderImage{}; WGPUShaderModule shader_linear{};
WGPUShaderModule shaderSceneComp{}; WGPUShaderModule shader_image{};
WGPUShaderModule shaderSceneBlend{}; WGPUShaderModule shader_scene{};
WGPUShaderModule shaderBlit{}; // shaders custom blend
// compute pipeline shaders WGPUShaderModule shader_solid_blend{};
WGPUShaderModule shaderMergeMasks; WGPUShaderModule shader_radial_blend{};
WGPUShaderModule shaderBlendSolid; WGPUShaderModule shader_linear_blend{};
WGPUShaderModule shaderBlendGradient; WGPUShaderModule shader_image_blend{};
WGPUShaderModule shaderBlendImage; WGPUShaderModule shader_scene_blend{};
// shader scene compose
WGPUShaderModule shader_scene_compose{};
WGPUShaderModule shader_merge_masks;
// shader blit
WGPUShaderModule shader_blit{};
private: private:
// graphics pipeline layouts // layouts helpers
WGPUPipelineLayout layoutStencil{}; WGPUPipelineLayout layout_stencil{};
WGPUPipelineLayout layoutSolid{}; // layouts normal blend
WGPUPipelineLayout layoutGradient{}; WGPUPipelineLayout layout_solid{};
WGPUPipelineLayout layoutImage{}; WGPUPipelineLayout layout_gradient{};
WGPUPipelineLayout layoutSceneComp{}; WGPUPipelineLayout layout_image{};
WGPUPipelineLayout layoutSceneBlend{}; WGPUPipelineLayout layout_scene{};
WGPUPipelineLayout layoutBlit{}; // layouts custom blend
// compute pipeline layouts WGPUPipelineLayout layout_solid_blend{};
WGPUPipelineLayout layoutMergeMasks{}; WGPUPipelineLayout layout_gradient_blend{};
WGPUPipelineLayout layoutBlend{}; WGPUPipelineLayout layout_image_blend{};
WGPUPipelineLayout layout_scene_blend{};
// layouts scene compose
WGPUPipelineLayout layout_scene_compose{};
WGPUPipelineLayout layout_merge_masks{};
// layouts blit
WGPUPipelineLayout layout_blit{};
public: public:
// graphics pipeline // pipelines helpers
WgBindGroupLayouts layouts;
WGPURenderPipeline winding{}; WGPURenderPipeline winding{};
WGPURenderPipeline evenodd{}; WGPURenderPipeline evenodd{};
WGPURenderPipeline direct{}; WGPURenderPipeline direct{};
WGPURenderPipeline solid[2]{}; WGPURenderPipeline clip_path{};
WGPURenderPipeline radial[2]{}; // pipelines normal blend
WGPURenderPipeline linear[2]{}; WGPURenderPipeline solid{};
WGPURenderPipeline image[2]{}; WGPURenderPipeline radial{};
WGPURenderPipeline sceneClip; WGPURenderPipeline linear{};
WGPURenderPipeline sceneComp[11]; WGPURenderPipeline image{};
WGPURenderPipeline sceneBlend; WGPURenderPipeline scene{};
// pipelines custom blend
WGPURenderPipeline solid_blend[18]{};
WGPURenderPipeline radial_blend[18]{};
WGPURenderPipeline linear_blend[18]{};
WGPURenderPipeline image_blend[18]{};
WGPURenderPipeline scene_blend[18]{};
// pipelines compose
WGPURenderPipeline scene_compose[11]{};
WGPURenderPipeline scene_clip{};
WGPUComputePipeline merge_masks{};
// pipeline blit
WGPURenderPipeline blit{}; WGPURenderPipeline blit{};
WGPURenderPipeline clipPath{}; public:
// compute pipeline // layouts
WGPUComputePipeline mergeMasks; WgBindGroupLayouts layouts;
WGPUComputePipeline blendSolid[18];
WGPUComputePipeline blendGradient[18];
WGPUComputePipeline blendImage[18];
private: private:
void releaseGraphicHandles(WgContext& context); void releaseGraphicHandles(WgContext& context);
void releaseComputeHandles(WgContext& context); void releaseComputeHandles(WgContext& context);

View file

@ -385,19 +385,10 @@ bool WgRenderer::endComposite(RenderCompositor* cmp)
WgRenderStorage* src = mRenderStorageStack.last(); WgRenderStorage* src = mRenderStorageStack.last();
mRenderStorageStack.pop(); mRenderStorageStack.pop();
WgRenderStorage* dst = mRenderStorageStack.last(); WgRenderStorage* dst = mRenderStorageStack.last();
// apply normal blend
if (comp->blend == BlendMethod::Normal) {
// begin previous render pass // begin previous render pass
mCompositor.beginRenderPass(mCommandEncoder, dst, false); mCompositor.beginRenderPass(mCommandEncoder, dst, false);
// apply blend // apply composition
mCompositor.blendScene(mContext, src, comp); mCompositor.renderScene(mContext, src, comp);
// apply custom blend
} else {
// apply custom blend
mCompositor.blend(mCommandEncoder, src, dst, comp->opacity, comp->blend, WgRenderRasterType::Image);
// begin previous render pass
mCompositor.beginRenderPass(mCommandEncoder, dst, false);
}
// back render targets to the pool // back render targets to the pool
mRenderStoragePool.free(mContext, src); mRenderStoragePool.free(mContext, src);
} else { // finish composition } else { // finish composition

View file

@ -50,7 +50,7 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
)"; )";
//************************************************************************ //************************************************************************
// graphics shader source: solid // graphics shader source: solid normal blend
//************************************************************************ //************************************************************************
const char* cShaderSrc_Solid = R"( const char* cShaderSrc_Solid = R"(
@ -79,12 +79,12 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
)"; )";
//************************************************************************ //************************************************************************
// graphics shader source: linear // graphics shader source: linear normal blend
//************************************************************************ //************************************************************************
const char* cShaderSrc_Linear = R"( const char* cShaderSrc_Linear = R"(
struct VertexInput { @location(0) position: vec2f }; struct VertexInput { @location(0) position: vec2f };
struct VertexOutput { @builtin(position) position : vec4f, @location(0) vScreenCoord : vec4f }; struct VertexOutput { @builtin(position) position : vec4f, @location(0) vGradCoord : vec4f };
// uniforms // uniforms
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f; @group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
@ -99,13 +99,13 @@ struct VertexOutput { @builtin(position) position : vec4f, @location(0) vScreenC
fn vs_main(in: VertexInput) -> VertexOutput { fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
out.position = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0); out.position = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0);
out.vScreenCoord = uTransformGrad * vec4f(in.position.xy, 0.0, 1.0); out.vGradCoord = uTransformGrad * vec4f(in.position.xy, 0.0, 1.0);
return out; return out;
} }
@fragment @fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f { fn fs_main(in: VertexOutput) -> @location(0) vec4f {
let pos = in.vScreenCoord.xy; let pos = in.vGradCoord.xy;
let st = uSettingGrad.xy; let st = uSettingGrad.xy;
let ed = uSettingGrad.zw; let ed = uSettingGrad.zw;
let ba = ed - st; let ba = ed - st;
@ -117,12 +117,12 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
)"; )";
//************************************************************************ //************************************************************************
// graphics shader source: radial // graphics shader source: radial normal blend
//************************************************************************ //************************************************************************
const char* cShaderSrc_Radial = R"( const char* cShaderSrc_Radial = R"(
struct VertexInput { @location(0) position: vec2f }; struct VertexInput { @location(0) position: vec2f };
struct VertexOutput { @builtin(position) position : vec4f, @location(0) vScreenCoord : vec4f }; struct VertexOutput { @builtin(position) position : vec4f, @location(0) vGradCoord : vec4f };
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f; @group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f; @group(1) @binding(0) var<uniform> uModelMat : mat4x4f;
@ -136,13 +136,13 @@ struct VertexOutput { @builtin(position) position : vec4f, @location(0) vScreenC
fn vs_main(in: VertexInput) -> VertexOutput { fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
out.position = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0); out.position = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0);
out.vScreenCoord = uTransformGrad * vec4f(in.position.xy, 0.0, 1.0); out.vGradCoord = uTransformGrad * vec4f(in.position.xy, 0.0, 1.0);
return out; return out;
} }
@fragment @fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f { fn fs_main(in: VertexOutput) -> @location(0) vec4f {
let t: f32 = distance(uSettingGrad.zw, in.vScreenCoord.xy) / uSettingGrad.r; let t: f32 = distance(uSettingGrad.zw, in.vGradCoord.xy) / uSettingGrad.r;
let Sc = textureSample(uTextureGrad, uSamplerGrad, vec2f(t, 0.5)); let Sc = textureSample(uTextureGrad, uSamplerGrad, vec2f(t, 0.5));
let So = uBlendSettings.a; let So = uBlendSettings.a;
return vec4f(Sc.rgb * Sc.a * So, Sc.a * So); return vec4f(Sc.rgb * Sc.a * So, Sc.a * So);
@ -150,12 +150,12 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
)"; )";
//************************************************************************ //************************************************************************
// graphics shader source: image // graphics shader source: image normal blend
//************************************************************************ //************************************************************************
const char* cShaderSrc_Image = R"( const char* cShaderSrc_Image = R"(
struct VertexInput { @location(0) position: vec2f, @location(1) texCoord: vec2f }; struct VertexInput { @location(0) position: vec2f, @location(1) texCoord: vec2f };
struct VertexOutput { @builtin(position) position: vec4f, @location(0) texCoord: vec2f }; struct VertexOutput { @builtin(position) position: vec4f, @location(0) vTexCoord: vec2f };
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f; @group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f; @group(1) @binding(0) var<uniform> uModelMat : mat4x4f;
@ -167,13 +167,13 @@ struct VertexOutput { @builtin(position) position: vec4f, @location(0) texCoord:
fn vs_main(in: VertexInput) -> VertexOutput { fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
out.position = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0); out.position = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0);
out.texCoord = in.texCoord; out.vTexCoord = in.texCoord;
return out; return out;
} }
@fragment @fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f { fn fs_main(in: VertexOutput) -> @location(0) vec4f {
var Sc: vec4f = textureSample(uTextureView, uSampler, in.texCoord.xy); var Sc: vec4f = textureSample(uTextureView, uSampler, in.vTexCoord.xy);
let So: f32 = uBlendSettings.a; let So: f32 = uBlendSettings.a;
switch u32(uBlendSettings.r) { switch u32(uBlendSettings.r) {
case 0u: { Sc = Sc.rgba; } case 0u: { Sc = Sc.rgba; }
@ -186,11 +186,367 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
}; };
)"; )";
//************************************************************************
// graphics shader source: scene normal blend
//************************************************************************
const char* cShaderSrc_Scene = R"(
struct VertexInput { @location(0) position: vec2f, @location(1) texCoord: vec2f };
struct VertexOutput { @builtin(position) position: vec4f, @location(0) vTexCoord: vec2f };
@group(0) @binding(0) var uSamplerSrc : sampler;
@group(0) @binding(1) var uTextureSrc : texture_2d<f32>;
@group(1) @binding(0) var<uniform> So : f32;
@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
out.position = vec4f(in.position.xy, 0.0, 1.0);
out.vTexCoord = in.texCoord;
return out;
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f {
return textureSample(uTextureSrc, uSamplerSrc, in.vTexCoord.xy) * So;
};
)";
//************************************************************************
// graphics shader source: custom shaders
//************************************************************************
const char* cShaderSrc_Solid_Blend = R"(
struct VertexInput { @location(0) position: vec2f };
struct VertexOutput { @builtin(position) position: vec4f, @location(1) vScrCoord: vec2f };
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f;
@group(1) @binding(1) var<uniform> uBlendSettings : vec4f;
@group(2) @binding(0) var<uniform> uSolidColor : vec4f;
@group(3) @binding(0) var uSamplerDst : sampler;
@group(3) @binding(1) var uTextureDst : texture_2d<f32>;
@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let pos = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0);
out.position = pos;
out.vScrCoord = vec2f(0.5 + pos.x * 0.5, 0.5 - pos.y * 0.5);
return out;
}
struct FragData { Sc: vec3f, Sa: f32, So: f32, Dc: vec3f, Da: f32 };
fn getFragData(in: VertexOutput) -> FragData {
// get source data
let colorSrc = uSolidColor;
let colorDst = textureSample(uTextureDst, uSamplerDst, in.vScrCoord.xy);
// fill fragment data
var data: FragData;
data.Sc = colorSrc.rgb;
data.Sa = colorSrc.a;
data.So = uBlendSettings.a;
data.Dc = colorDst.rgb;
data.Da = colorDst.a;
data.Sc = data.Sa * data.So * data.Sc;
data.Sa = data.Sa * data.So;
return data;
};
fn postProcess(d: FragData, R: vec4f) -> vec4f { return R; };
)";
const char* cShaderSrc_Linear_Blend = R"(
struct VertexInput { @location(0) position: vec2f };
struct VertexOutput { @builtin(position) position: vec4f, @location(0) vGradCoord : vec4f, @location(1) vScrCoord: vec2f };
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f;
@group(1) @binding(1) var<uniform> uBlendSettings : vec4f;
@group(2) @binding(0) var uSamplerGrad : sampler;
@group(2) @binding(1) var uTextureGrad : texture_2d<f32>;
@group(2) @binding(2) var<uniform> uSettingGrad : vec4f;
@group(2) @binding(3) var<uniform> uTransformGrad : mat4x4f;
@group(3) @binding(0) var uSamplerDst : sampler;
@group(3) @binding(1) var uTextureDst : texture_2d<f32>;
@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let pos = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0);
out.position = pos;
out.vGradCoord = uTransformGrad * vec4f(in.position.xy, 0.0, 1.0);
out.vScrCoord = vec2f(0.5 + pos.x * 0.5, 0.5 - pos.y * 0.5);
return out;
}
struct FragData { Sc: vec3f, Sa: f32, So: f32, Dc: vec3f, Da: f32 };
fn getFragData(in: VertexOutput) -> FragData {
// get source data
let pos = in.vGradCoord.xy;
let st = uSettingGrad.xy;
let ed = uSettingGrad.zw;
let ba = ed - st;
let t = dot(pos - st, ba) / dot(ba, ba);
let colorSrc = textureSample(uTextureGrad, uSamplerGrad, vec2f(t, 0.5));
let colorDst = textureSample(uTextureDst, uSamplerDst, in.vScrCoord.xy);
// fill fragment data
var data: FragData;
data.Sc = colorSrc.rgb;
data.Sa = colorSrc.a;
data.So = uBlendSettings.a;
data.Dc = colorDst.rgb;
data.Da = colorDst.a;
data.Sc = mix(data.Dc, data.Sc, data.Sa * data.So);
data.Sa = mix(data.Da, 1.0, data.Sa * data.So);
return data;
};
fn postProcess(d: FragData, R: vec4f) -> vec4f { return R; };
)";
const char* cShaderSrc_Radial_Blend = R"(
struct VertexInput { @location(0) position: vec2f };
struct VertexOutput { @builtin(position) position: vec4f, @location(0) vGradCoord : vec4f, @location(1) vScrCoord: vec2f };
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f;
@group(1) @binding(1) var<uniform> uBlendSettings : vec4f;
@group(2) @binding(0) var uSamplerGrad : sampler;
@group(2) @binding(1) var uTextureGrad : texture_2d<f32>;
@group(2) @binding(2) var<uniform> uSettingGrad : vec4f;
@group(2) @binding(3) var<uniform> uTransformGrad : mat4x4f;
@group(3) @binding(0) var uSamplerDst : sampler;
@group(3) @binding(1) var uTextureDst : texture_2d<f32>;
@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let pos = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0);
out.position = pos;
out.vGradCoord = uTransformGrad * vec4f(in.position.xy, 0.0, 1.0);
out.vScrCoord = vec2f(0.5 + pos.x * 0.5, 0.5 - pos.y * 0.5);
return out;
}
struct FragData { Sc: vec3f, Sa: f32, So: f32, Dc: vec3f, Da: f32 };
fn getFragData(in: VertexOutput) -> FragData {
// get source data
let t: f32 = distance(uSettingGrad.zw, in.vGradCoord.xy) / uSettingGrad.r;
let colorSrc = textureSample(uTextureGrad, uSamplerGrad, vec2f(t, 0.5));
let colorDst = textureSample(uTextureDst, uSamplerDst, in.vScrCoord.xy);
// fill fragment data
var data: FragData;
data.Sc = colorSrc.rgb;
data.Sa = colorSrc.a;
data.So = uBlendSettings.a;
data.Dc = colorDst.rgb;
data.Da = colorDst.a;
data.Sc = mix(data.Dc, data.Sc, data.Sa * data.So);
data.Sa = mix(data.Da, 1.0, data.Sa * data.So);
return data;
};
fn postProcess(d: FragData, R: vec4f) -> vec4f { return R; };
)";
const char* cShaderSrc_Image_Blend = R"(
struct VertexInput { @location(0) position: vec2f, @location(1) texCoord: vec2f };
struct VertexOutput { @builtin(position) position: vec4f, @location(0) vTexCoord : vec2f, @location(1) vScrCoord: vec2f };
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f;
@group(1) @binding(1) var<uniform> uBlendSettings : vec4f;
@group(2) @binding(0) var uSamplerSrc : sampler;
@group(2) @binding(1) var uTextureSrc : texture_2d<f32>;
@group(3) @binding(0) var uSamplerDst : sampler;
@group(3) @binding(1) var uTextureDst : texture_2d<f32>;
@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let pos = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0);
out.position = pos;
out.vTexCoord = in.texCoord;
out.vScrCoord = vec2f(0.5 + pos.x * 0.5, 0.5 - pos.y * 0.5);
return out;
}
struct FragData { Sc: vec3f, Sa: f32, So: f32, Dc: vec3f, Da: f32 };
fn getFragData(in: VertexOutput) -> FragData {
// get source data
let colorSrc = textureSample(uTextureSrc, uSamplerSrc, in.vTexCoord.xy);
let colorDst = textureSample(uTextureDst, uSamplerDst, in.vScrCoord.xy);
// fill fragment data
var data: FragData;
data.Sc = colorSrc.rgb;
data.Sa = colorSrc.a;
data.So = uBlendSettings.a;
data.Dc = colorDst.rgb;
data.Da = colorDst.a;
switch u32(uBlendSettings.r) {
case 0u: { data.Sc = data.Sc.rgb; }
case 1u: { data.Sc = data.Sc.bgr; }
case 2u: { data.Sc = data.Sc.rgb; }
case 3u: { data.Sc = data.Sc.bgr; }
default: {}
}
data.Sc = data.Sc * data.So;
data.Sa = data.Sa * data.So;
return data;
};
fn postProcess(d: FragData, R: vec4f) -> vec4f { return mix(vec4(d.Dc, d.Da), R, d.Sa); };
)";
const char* cShaderSrc_Scene_Blend = R"(
struct VertexInput { @location(0) position: vec2f, @location(1) texCoord: vec2f };
struct VertexOutput { @builtin(position) position: vec4f, @location(0) vScrCoord: vec2f };
@group(0) @binding(0) var uSamplerSrc : sampler;
@group(0) @binding(1) var uTextureSrc : texture_2d<f32>;
@group(1) @binding(0) var uSamplerDst : sampler;
@group(1) @binding(1) var uTextureDst : texture_2d<f32>;
@group(2) @binding(0) var<uniform> So : f32;
@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
out.position = vec4f(in.position.xy, 0.0, 1.0);
out.vScrCoord = in.texCoord;
return out;
}
struct FragData { Sc: vec3f, Sa: f32, So: f32, Dc: vec3f, Da: f32 };
fn getFragData(in: VertexOutput) -> FragData {
// get source data
let colorSrc = textureSample(uTextureSrc, uSamplerSrc, in.vScrCoord.xy);
let colorDst = textureSample(uTextureDst, uSamplerDst, in.vScrCoord.xy);
// fill fragment data
var data: FragData;
data.Sc = colorSrc.rgb;
data.Sa = colorSrc.a;
data.Dc = colorDst.rgb;
data.Da = colorDst.a;
return data;
};
fn postProcess(d: FragData, R: vec4f) -> vec4f { return mix(vec4(d.Dc, d.Da), R, d.Sa * So); };
)";
const char* cShaderSrc_BlendFuncs = R"(
@fragment
fn fs_main_Normal(in: VertexOutput) -> @location(0) vec4f {
// used as debug blend method
return vec4f(1.0, 0.0, 0.0, 1.0);
}
@fragment
fn fs_main_Multiply(in: VertexOutput) -> @location(0) vec4f {
let d: FragData = getFragData(in);
let Rc = d.Sc * d.Dc;
return postProcess(d, vec4f(Rc, 1.0));
}
@fragment
fn fs_main_Screen(in: VertexOutput) -> @location(0) vec4f {
let d: FragData = getFragData(in);
let Rc = d.Sc + d.Dc - d.Sc * d.Dc;
return postProcess(d, vec4f(Rc, 1.0));
}
@fragment
fn fs_main_Overlay(in: VertexOutput) -> @location(0) vec4f {
let d: FragData = getFragData(in);
var Rc: vec3f;
Rc.r = select(1.0 - min(1.0, 2 * (1 - d.Sc.r) * (1 - d.Dc.r)), min(1.0, 2 * d.Sc.r * d.Dc.r), (d.Dc.r < 0.5));
Rc.g = select(1.0 - min(1.0, 2 * (1 - d.Sc.g) * (1 - d.Dc.g)), min(1.0, 2 * d.Sc.g * d.Dc.g), (d.Dc.g < 0.5));
Rc.b = select(1.0 - min(1.0, 2 * (1 - d.Sc.b) * (1 - d.Dc.b)), min(1.0, 2 * d.Sc.b * d.Dc.b), (d.Dc.b < 0.5));
return postProcess(d, vec4f(Rc, 1.0));
}
@fragment
fn fs_main_Darken(in: VertexOutput) -> @location(0) vec4f {
let d: FragData = getFragData(in);
let Rc = min(d.Sc, d.Dc);
return postProcess(d, vec4f(Rc, 1.0));
}
@fragment
fn fs_main_Lighten(in: VertexOutput) -> @location(0) vec4f {
let d: FragData = getFragData(in);
let Rc = max(d.Sc, d.Dc);
return postProcess(d, vec4f(Rc, 1.0));
}
@fragment
fn fs_main_ColorDodge(in: VertexOutput) -> @location(0) vec4f {
let d: FragData = getFragData(in);
var Rc: vec3f;
Rc.r = select(d.Dc.r, (d.Dc.r * 255.0 / (255.0 - d.Sc.r * 255.0))/255.0, (1.0 - d.Sc.r > 0.0));
Rc.g = select(d.Dc.g, (d.Dc.g * 255.0 / (255.0 - d.Sc.g * 255.0))/255.0, (1.0 - d.Sc.g > 0.0));
Rc.b = select(d.Dc.b, (d.Dc.b * 255.0 / (255.0 - d.Sc.b * 255.0))/255.0, (1.0 - d.Sc.b > 0.0));
return postProcess(d, vec4f(Rc, 1.0));
}
@fragment
fn fs_main_ColorBurn(in: VertexOutput) -> @location(0) vec4f {
let d: FragData = getFragData(in);
var Rc: vec3f;
Rc.r = select(1.0 - d.Dc.r, (255.0 - (255.0 - d.Dc.r * 255.0) / (d.Sc.r * 255.0)) / 255.0, (d.Sc.r > 0.0));
Rc.g = select(1.0 - d.Dc.g, (255.0 - (255.0 - d.Dc.g * 255.0) / (d.Sc.g * 255.0)) / 255.0, (d.Sc.g > 0.0));
Rc.b = select(1.0 - d.Dc.b, (255.0 - (255.0 - d.Dc.b * 255.0) / (d.Sc.b * 255.0)) / 255.0, (d.Sc.b > 0.0));
return postProcess(d, vec4f(Rc, 1.0));
}
@fragment
fn fs_main_HardLight(in: VertexOutput) -> @location(0) vec4f {
let d: FragData = getFragData(in);
var Rc: vec3f;
Rc.r = select(1.0 - min(1.0, 2 * (1 - d.Sc.r) * (1 - d.Dc.r)), min(1.0, 2 * d.Sc.r * d.Dc.r), (d.Sc.r < 0.5));
Rc.g = select(1.0 - min(1.0, 2 * (1 - d.Sc.g) * (1 - d.Dc.g)), min(1.0, 2 * d.Sc.g * d.Dc.g), (d.Sc.g < 0.5));
Rc.b = select(1.0 - min(1.0, 2 * (1 - d.Sc.b) * (1 - d.Dc.b)), min(1.0, 2 * d.Sc.b * d.Dc.b), (d.Sc.b < 0.5));
return postProcess(d, vec4f(Rc, 1.0));
}
@fragment
fn fs_main_SoftLight(in: VertexOutput) -> @location(0) vec4f {
let d: FragData = getFragData(in);
let One = vec3f(1.0, 1.0, 1.0);
let Rc = min(One, (One - 2 * d.Sc) * d.Dc * d.Dc + 2.0 * d.Sc * d.Dc);
return postProcess(d, vec4f(Rc, 1.0));
}
@fragment
fn fs_main_Difference(in: VertexOutput) -> @location(0) vec4f {
let d: FragData = getFragData(in);
let One = vec3f(1.0, 1.0, 1.0);
let Rc = abs(d.Dc - d.Sc);
return postProcess(d, vec4f(Rc, 1.0));
}
@fragment
fn fs_main_Exclusion(in: VertexOutput) -> @location(0) vec4f {
let d: FragData = getFragData(in);
let One = vec3f(1.0, 1.0, 1.0);
let Rc = d.Sc + d.Dc - 2 * d.Sc * d.Dc;
return postProcess(d, vec4f(Rc, 1.0));
}
@fragment
fn fs_main_Add(in: VertexOutput) -> @location(0) vec4f {
let d: FragData = getFragData(in);
let Rc = d.Sc + d.Dc;
return postProcess(d, vec4f(Rc, 1.0));
}
)";
//************************************************************************ //************************************************************************
// graphics shader source: scene compose // graphics shader source: scene compose
//************************************************************************ //************************************************************************
const char* cShaderSrc_Scene_Comp = R"( const char* cShaderSrc_Scene_Compose = R"(
struct VertexInput { @location(0) position: vec2f, @location(1) texCoord: vec2f }; struct VertexInput { @location(0) position: vec2f, @location(1) texCoord: vec2f };
struct VertexOutput { @builtin(position) position: vec4f, @location(0) texCoord: vec2f }; struct VertexOutput { @builtin(position) position: vec4f, @location(0) texCoord: vec2f };
@ -293,33 +649,6 @@ fn fs_main_DarkenMask(in: VertexOutput) -> @location(0) vec4f {
}; };
)"; )";
//************************************************************************
// graphics shader source: scene blend
//************************************************************************
const char* cShaderSrc_Scene_Blend = R"(
struct VertexInput { @location(0) position: vec2f, @location(1) texCoord: vec2f };
struct VertexOutput { @builtin(position) position: vec4f, @location(0) texCoord: vec2f };
@group(0) @binding(0) var uSamplerSrc : sampler;
@group(0) @binding(1) var uTextureSrc : texture_2d<f32>;
@group(1) @binding(0) var<uniform> So : f32;
@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
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(uTextureSrc, uSamplerSrc, in.texCoord.xy) * So;
};
)";
//************************************************************************ //************************************************************************
// graphics shader source: texture blit // graphics shader source: texture blit
//************************************************************************ //************************************************************************
@ -361,205 +690,3 @@ fn cs_main(@builtin(global_invocation_id) id: vec3u) {
textureStore(imageTrg, id.xy, colorMsk0 * colorMsk1); textureStore(imageTrg, id.xy, colorMsk0 * colorMsk1);
} }
)"; )";
//************************************************************************
// compute shader source: blend
//************************************************************************
const char* cShaderSrc_BlendHeader_Solid = R"(
@group(0) @binding(0) var imageSrc : texture_storage_2d<rgba8unorm, read>;
@group(1) @binding(0) var imageDst : texture_storage_2d<rgba8unorm, read>;
@group(2) @binding(0) var imageTgt : texture_storage_2d<rgba8unorm, write>;
@group(3) @binding(0) var<uniform> So : f32;
struct FragData { Sc: vec3f, Sa: f32, Dc: vec3f, Da: f32, skip: bool };
fn getFragData(id: vec2u) -> FragData {
var data: FragData;
data.skip = true;
let colorSrc = textureLoad(imageSrc, id.xy);
if (colorSrc.a == 0.0) { return data; }
let colorDst = textureLoad(imageDst, id.xy);
data.Sc = colorSrc.rgb;
data.Sa = colorSrc.a;
data.Dc = colorDst.rgb;
data.Da = colorDst.a;
data.skip = false;
return data;
};
fn postProcess(d: FragData, R: vec4f) -> vec4f { return R; };
)";
const char* cShaderSrc_BlendHeader_Gradient = R"(
@group(0) @binding(0) var imageSrc : texture_storage_2d<rgba8unorm, read>;
@group(1) @binding(0) var imageDst : texture_storage_2d<rgba8unorm, read>;
@group(2) @binding(0) var imageTgt : texture_storage_2d<rgba8unorm, write>;
@group(3) @binding(0) var<uniform> So : f32;
struct FragData { Sc: vec3f, Sa: f32, Dc: vec3f, Da: f32, skip: bool };
fn getFragData(id: vec2u) -> FragData {
var data: FragData;
data.skip = true;
let colorSrc = textureLoad(imageSrc, id.xy);
if (colorSrc.a == 0.0) { return data; }
let colorDst = textureLoad(imageDst, id.xy);
data.Sc = colorSrc.rgb;
data.Sa = colorSrc.a;
data.Dc = colorDst.rgb;
data.Da = colorDst.a;
data.skip = false;
data.Sc = data.Sc + data.Dc * (1.0 - data.Sa);
data.Sa = data.Sa + data.Da * (1.0 - data.Sa);
return data;
};
fn postProcess(d: FragData, R: vec4f) -> vec4f { return R; };
)";
const char* cShaderSrc_BlendHeader_Image = R"(
@group(0) @binding(0) var imageSrc : texture_storage_2d<rgba8unorm, read>;
@group(1) @binding(0) var imageDst : texture_storage_2d<rgba8unorm, read>;
@group(2) @binding(0) var imageTgt : texture_storage_2d<rgba8unorm, write>;
@group(3) @binding(0) var<uniform> So : f32;
struct FragData { Sc: vec3f, Sa: f32, Dc: vec3f, Da: f32, skip: bool };
fn getFragData(id: vec2u) -> FragData {
var data: FragData;
data.skip = true;
let colorSrc = textureLoad(imageSrc, id.xy);
if (colorSrc.a == 0.0) { return data; }
let colorDst = textureLoad(imageDst, id.xy);
data.Sc = colorSrc.rgb;
data.Sa = colorSrc.a;
data.Dc = colorDst.rgb;
data.Da = colorDst.a;
data.skip = false;
return data;
};
fn postProcess(d: FragData, R: vec4f) -> vec4f {
return mix(vec4(d.Dc, d.Da), R, d.Sa * So);
};
)";
const char* cShaderSrc_Blend_Funcs = R"(
@compute @workgroup_size(8, 8)
fn cs_main_Normal(@builtin(global_invocation_id) id: vec3u) {
let d: FragData = getFragData(id.xy);
if (d.skip) { return; }
let Rc = d.Sc + d.Dc * (1.0 - d.Sa);
let Ra = d.Sa + d.Da * (1.0 - d.Sa);
textureStore(imageTgt, id.xy, vec4f(Rc, Ra));
};
@compute @workgroup_size(8, 8)
fn cs_main_Add(@builtin(global_invocation_id) id: vec3u) {
let d: FragData = getFragData(id.xy);
if (d.skip) { return; }
let Rc = d.Sc + d.Dc;
textureStore(imageTgt, id.xy, postProcess(d, vec4f(Rc, 1.0)));
};
@compute @workgroup_size(8, 8)
fn cs_main_Screen(@builtin(global_invocation_id) id: vec3u) {
let d: FragData = getFragData(id.xy);
if (d.skip) { return; }
let Rc = d.Sc + d.Dc - d.Sc * d.Dc;
textureStore(imageTgt, id.xy, postProcess(d, vec4f(Rc, 1.0)));
};
@compute @workgroup_size(8, 8)
fn cs_main_Multiply(@builtin(global_invocation_id) id: vec3u) {
let d: FragData = getFragData(id.xy);
if (d.skip) { return; }
let Rc = d.Sc * d.Dc;
textureStore(imageTgt, id.xy, postProcess(d, vec4f(Rc, 1.0)));
};
@compute @workgroup_size(8, 8)
fn cs_main_Overlay(@builtin(global_invocation_id) id: vec3u) {
let d: FragData = getFragData(id.xy);
if (d.skip) { return; }
var Rc: vec3f;
Rc.r = select(1.0 - min(1.0, 2 * (1 - d.Sc.r) * (1 - d.Dc.r)), min(1.0, 2 * d.Sc.r * d.Dc.r), (d.Dc.r < 0.5));
Rc.g = select(1.0 - min(1.0, 2 * (1 - d.Sc.g) * (1 - d.Dc.g)), min(1.0, 2 * d.Sc.g * d.Dc.g), (d.Dc.g < 0.5));
Rc.b = select(1.0 - min(1.0, 2 * (1 - d.Sc.b) * (1 - d.Dc.b)), min(1.0, 2 * d.Sc.b * d.Dc.b), (d.Dc.b < 0.5));
textureStore(imageTgt, id.xy, postProcess(d, vec4f(Rc, 1.0)));
};
@compute @workgroup_size(8, 8)
fn cs_main_Difference(@builtin(global_invocation_id) id: vec3u) {
let d: FragData = getFragData(id.xy);
if (d.skip) { return; }
let Rc = abs(d.Dc - d.Sc);
textureStore(imageTgt, id.xy, postProcess(d, vec4f(Rc, 1.0)));
};
@compute @workgroup_size(8, 8)
fn cs_main_Exclusion(@builtin(global_invocation_id) id: vec3u) {
let d: FragData = getFragData(id.xy);
if (d.skip) { return; }
let One = vec3f(1.0, 1.0, 1.0);
let Rc = min(One, d.Sc + d.Dc - min(One, 2 * d.Sc * d.Dc));
textureStore(imageTgt, id.xy, postProcess(d, vec4f(Rc, 1.0)));
};
@compute @workgroup_size(8, 8)
fn cs_main_Darken(@builtin(global_invocation_id) id: vec3u) {
let d: FragData = getFragData(id.xy);
if (d.skip) { return; }
let Rc = min(d.Sc, d.Dc);
textureStore(imageTgt, id.xy, postProcess(d, vec4f(Rc, 1.0)));
};
@compute @workgroup_size(8, 8)
fn cs_main_Lighten(@builtin(global_invocation_id) id: vec3u) {
let d: FragData = getFragData(id.xy);
if (d.skip) { return; }
let Rc = max(d.Sc, d.Dc);
textureStore(imageTgt, id.xy, postProcess(d, vec4f(Rc, 1.0)));
};
@compute @workgroup_size(8, 8)
fn cs_main_ColorDodge(@builtin(global_invocation_id) id: vec3u) {
let d: FragData = getFragData(id.xy);
if (d.skip) { return; }
var Rc: vec3f;
Rc.r = select(d.Dc.r, (d.Dc.r * 255.0 / (255.0 - d.Sc.r * 255.0))/255.0, (1.0 - d.Sc.r > 0.0));
Rc.g = select(d.Dc.g, (d.Dc.g * 255.0 / (255.0 - d.Sc.g * 255.0))/255.0, (1.0 - d.Sc.g > 0.0));
Rc.b = select(d.Dc.b, (d.Dc.b * 255.0 / (255.0 - d.Sc.b * 255.0))/255.0, (1.0 - d.Sc.b > 0.0));
textureStore(imageTgt, id.xy, postProcess(d, vec4f(Rc, 1.0)));
};
@compute @workgroup_size(8, 8)
fn cs_main_ColorBurn(@builtin(global_invocation_id) id: vec3u) {
let d: FragData = getFragData(id.xy);
if (d.skip) { return; }
var Rc: vec3f;
Rc.r = select(1.0 - d.Dc.r, (255.0 - (255.0 - d.Dc.r * 255.0) / (d.Sc.r * 255.0)) / 255.0, (d.Sc.r > 0.0));
Rc.g = select(1.0 - d.Dc.g, (255.0 - (255.0 - d.Dc.g * 255.0) / (d.Sc.g * 255.0)) / 255.0, (d.Sc.g > 0.0));
Rc.b = select(1.0 - d.Dc.b, (255.0 - (255.0 - d.Dc.b * 255.0) / (d.Sc.b * 255.0)) / 255.0, (d.Sc.b > 0.0));
textureStore(imageTgt, id.xy, postProcess(d, vec4f(Rc, 1.0)));
};
@compute @workgroup_size(8, 8)
fn cs_main_HardLight(@builtin(global_invocation_id) id: vec3u) {
let d: FragData = getFragData(id.xy);
if (d.skip) { return; }
var Rc: vec3f;
Rc.r = select(1.0 - min(1.0, 2 * (1 - d.Sc.r) * (1 - d.Dc.r)), min(1.0, 2 * d.Sc.r * d.Dc.r), (d.Sc.r < 0.5));
Rc.g = select(1.0 - min(1.0, 2 * (1 - d.Sc.g) * (1 - d.Dc.g)), min(1.0, 2 * d.Sc.g * d.Dc.g), (d.Sc.g < 0.5));
Rc.b = select(1.0 - min(1.0, 2 * (1 - d.Sc.b) * (1 - d.Dc.b)), min(1.0, 2 * d.Sc.b * d.Dc.b), (d.Sc.b < 0.5));
textureStore(imageTgt, id.xy, postProcess(d, vec4f(Rc, 1.0)));
};
@compute @workgroup_size(8, 8)
fn cs_main_SoftLight(@builtin(global_invocation_id) id: vec3u) {
let d: FragData = getFragData(id.xy);
if (d.skip) { return; }
let One = vec3f(1.0, 1.0, 1.0);
let Rc = min(One, (One - 2 * d.Sc) * d.Dc * d.Dc + 2.0 * d.Sc * d.Dc);
textureStore(imageTgt, id.xy, postProcess(d, vec4f(Rc, 1.0)));
};
)";

View file

@ -23,21 +23,27 @@
#ifndef _TVG_WG_SHADER_SRC_H_ #ifndef _TVG_WG_SHADER_SRC_H_
#define _TVG_WG_SHADER_SRC_H_ #define _TVG_WG_SHADER_SRC_H_
// graphics shader sources // helper shaders
extern const char* cShaderSrc_Stencil; extern const char* cShaderSrc_Stencil;
// shaders normal blend
extern const char* cShaderSrc_Solid; extern const char* cShaderSrc_Solid;
extern const char* cShaderSrc_Linear; extern const char* cShaderSrc_Linear;
extern const char* cShaderSrc_Radial; extern const char* cShaderSrc_Radial;
extern const char* cShaderSrc_Image; extern const char* cShaderSrc_Image;
extern const char* cShaderSrc_Scene_Comp; extern const char* cShaderSrc_Scene;
// shaders custrom blend
extern const char* cShaderSrc_Solid_Blend;
extern const char* cShaderSrc_Linear_Blend;
extern const char* cShaderSrc_Radial_Blend;
extern const char* cShaderSrc_Image_Blend;
extern const char* cShaderSrc_Scene_Blend; extern const char* cShaderSrc_Scene_Blend;
extern const char* cShaderSrc_BlendFuncs;
// shaders scene compose
extern const char* cShaderSrc_Scene_Compose;
// shaders blit
extern const char* cShaderSrc_Blit; extern const char* cShaderSrc_Blit;
// compute shader sources: blend, compose and merge path // compute shader sources: blend, compose and merge path
extern const char* cShaderSrc_MergeMasks; extern const char* cShaderSrc_MergeMasks;
extern const char* cShaderSrc_BlendHeader_Solid;
extern const char* cShaderSrc_BlendHeader_Gradient;
extern const char* cShaderSrc_BlendHeader_Image;
extern const char* cShaderSrc_Blend_Funcs;
#endif // _TVG_WG_SHEDER_SRC_H_ #endif // _TVG_WG_SHEDER_SRC_H_