wg_engine: geometry stage buffers implementation
Some checks are pending
Android / build_x86_64 (push) Waiting to run
Android / build_aarch64 (push) Waiting to run
iOS / build_x86_64 (push) Waiting to run
iOS / build_arm64 (push) Waiting to run
macOS / build (push) Waiting to run
macOS / compact_test (push) Waiting to run
macOS / unit_test (push) Waiting to run
Ubuntu / build (push) Waiting to run
Ubuntu / compact_test (push) Waiting to run
Ubuntu / unit_test (push) Waiting to run
Windows / build (push) Waiting to run
Windows / compact_test (push) Waiting to run
Windows / unit_test (push) Waiting to run

Implemented task-based rendering and geometry stage buffers:
1. Get information about current frame objects
2. Accumulate geometry data into a stage buffer during frame rendering
3. Flush it to the GPU in single call
4. Run rendering process in post render stage

https://github.com/thorvg/thorvg/issues/3489
https://github.com/thorvg/thorvg/issues/3455
This commit is contained in:
Sergii Liebodkin 2025-05-28 10:53:17 +03:00 committed by Hermet Park
parent 1f53f2d72f
commit 24509b0e41
13 changed files with 709 additions and 314 deletions

View file

@ -7,6 +7,7 @@ source_file = [
'tvgWgRenderData.h',
'tvgWgRenderer.h',
'tvgWgRenderTarget.h',
'tvgWgRenderTask.h',
'tvgWgShaderSrc.h',
'tvgWgShaderTypes.h',
'tvgWgBindGroups.cpp',
@ -17,6 +18,7 @@ source_file = [
'tvgWgRenderData.cpp',
'tvgWgRenderer.cpp',
'tvgWgRenderTarget.cpp',
'tvgWgRenderTask.cpp',
'tvgWgShaderSrc.cpp',
'tvgWgShaderTypes.cpp'
]

View file

@ -268,4 +268,35 @@ void WgContext::releaseQueue(WGPUQueue& queue)
wgpuQueueRelease(queue);
queue = nullptr;
}
}
}
WGPUCommandEncoder WgContext::createCommandEncoder()
{
WGPUCommandEncoderDescriptor commandEncoderDesc{};
return wgpuDeviceCreateCommandEncoder(device, &commandEncoderDesc);
}
void WgContext::submitCommandEncoder(WGPUCommandEncoder commandEncoder)
{
const WGPUCommandBufferDescriptor commandBufferDesc{};
WGPUCommandBuffer commandsBuffer = wgpuCommandEncoderFinish(commandEncoder, &commandBufferDesc);
wgpuQueueSubmit(queue, 1, &commandsBuffer);
wgpuCommandBufferRelease(commandsBuffer);
}
void WgContext::releaseCommandEncoder(WGPUCommandEncoder& commandEncoder)
{
if (commandEncoder) {
wgpuCommandEncoderRelease(commandEncoder);
commandEncoder = nullptr;
}
}
bool WgContext::invalid()
{
return !instance || !device;
}

View file

@ -71,10 +71,12 @@ struct WgContext {
// release buffer objects
void releaseBuffer(WGPUBuffer& buffer);
bool invalid()
{
return !instance || !device;
}
// command encoder
WGPUCommandEncoder createCommandEncoder();
void submitCommandEncoder(WGPUCommandEncoder encoder);
void releaseCommandEncoder(WGPUCommandEncoder& encoder);
bool invalid();
};
#endif // _TVG_WG_COMMON_H_

View file

@ -28,6 +28,7 @@ void WgCompositor::initialize(WgContext& context, uint32_t width, uint32_t heigh
{
// pipelines (external handle, do not release)
pipelines.initialize(context);
stageBuffer.initialize(context);
// initialize opacity pool
initPools(context);
// allocate global view matrix handles
@ -37,7 +38,7 @@ void WgCompositor::initialize(WgContext& context, uint32_t width, uint32_t heigh
// create render targets handles
resize(context, width, height);
// composition and blend geometries
meshData.blitBox(context);
meshDataBlit.blitBox(context);
}
@ -54,7 +55,7 @@ void WgCompositor::initPools(WgContext& context)
void WgCompositor::release(WgContext& context)
{
// composition and blend geometries
meshData.release(context);
meshDataBlit.release(context);
// release render targets habdles
resize(context, 0, 0);
// release opacity pool
@ -62,6 +63,8 @@ void WgCompositor::release(WgContext& context)
// release global view matrix handles
context.layouts.releaseBindGroup(bindGroupViewMat);
context.releaseBuffer(bufferViewMat);
// release stage buffer
stageBuffer.release(context);
// release pipelines
pipelines.release(context);
}
@ -81,9 +84,9 @@ void WgCompositor::resize(WgContext& context, uint32_t width, uint32_t height) {
// release existig handles
if ((this->width != width) || (this->height != height)) {
context.layouts.releaseBindGroup(bindGroupStorageTemp);
// release intermediate render storages
storageTemp1.release(context);
storageTemp0.release(context);
// release intermediate render target
targetTemp1.release(context);
targetTemp0.release(context);
// release global stencil buffer handles
context.releaseTextureView(texViewDepthStencilMS);
context.releaseTexture(texDepthStencilMS);
@ -107,10 +110,10 @@ void WgCompositor::resize(WgContext& context, uint32_t width, uint32_t height) {
texViewDepthStencil = context.createTextureView(texDepthStencil);
texDepthStencilMS = context.createTexAttachement(width, height, WGPUTextureFormat_Depth24PlusStencil8, 4);
texViewDepthStencilMS = context.createTextureView(texDepthStencilMS);
// initialize intermediate render storages
storageTemp0.initialize(context, width, height);
storageTemp1.initialize(context, width, height);
bindGroupStorageTemp = context.layouts.createBindGroupStrorage2RO(storageTemp0.texView, storageTemp1.texView);
// initialize intermediate render targets
targetTemp0.initialize(context, width, height);
targetTemp1.initialize(context, width, height);
bindGroupStorageTemp = context.layouts.createBindGroupStrorage2RO(targetTemp0.texView, targetTemp1.texView);
}
}
@ -121,14 +124,14 @@ RenderRegion WgCompositor::shrinkRenderRegion(RenderRegion& rect)
}
void WgCompositor::copyTexture(const WgRenderStorage* dst, const WgRenderStorage* src)
void WgCompositor::copyTexture(const WgRenderTarget* dst, const WgRenderTarget* src)
{
const RenderRegion region = {{0, 0}, {(int32_t)src->width, (int32_t)src->height}};
copyTexture(dst, src, region);
}
void WgCompositor::copyTexture(const WgRenderStorage* dst, const WgRenderStorage* src, const RenderRegion& region)
void WgCompositor::copyTexture(const WgRenderTarget* dst, const WgRenderTarget* src, const RenderRegion& region)
{
assert(dst);
assert(src);
@ -140,16 +143,25 @@ void WgCompositor::copyTexture(const WgRenderStorage* dst, const WgRenderStorage
}
void WgCompositor::beginRenderPass(WGPUCommandEncoder commandEncoder, WgRenderStorage* target, bool clear, WGPUColor clearColor)
void WgCompositor::beginRenderPass(WGPUCommandEncoder commandEncoder, WgRenderTarget* target, bool clear, WGPUColor clearColor)
{
assert(commandEncoder);
assert(target);
assert(commandEncoder);
// do not start same render bass
if (target == currentTarget) return;
// we must to end render pass first
endRenderPass();
this->currentTarget = target;
// start new render pass
this->commandEncoder = commandEncoder;
const WGPURenderPassDepthStencilAttachment depthStencilAttachment{
.view = texViewDepthStencilMS,
.depthLoadOp = WGPULoadOp_Clear, .depthStoreOp = WGPUStoreOp_Discard, .depthClearValue = 1.0f,
.stencilLoadOp = WGPULoadOp_Clear, .stencilStoreOp = WGPUStoreOp_Discard, .stencilClearValue = 0
.depthLoadOp = WGPULoadOp_Clear,
.depthStoreOp = WGPUStoreOp_Discard,
.depthClearValue = 1.0f,
.stencilLoadOp = WGPULoadOp_Clear,
.stencilStoreOp = WGPUStoreOp_Discard,
.stencilClearValue = 0
};
const WGPURenderPassColorAttachment colorAttachment{
.view = target->texViewMS,
@ -158,7 +170,6 @@ void WgCompositor::beginRenderPass(WGPUCommandEncoder commandEncoder, WgRenderSt
.loadOp = clear ? WGPULoadOp_Clear : WGPULoadOp_Load,
.storeOp = WGPUStoreOp_Store,
.clearValue = clearColor
};
WGPURenderPassDescriptor renderPassDesc{ .colorAttachmentCount = 1, .colorAttachments = &colorAttachment, .depthStencilAttachment = &depthStencilAttachment };
renderPassEncoder = wgpuCommandEncoderBeginRenderPass(commandEncoder, &renderPassDesc);
@ -172,11 +183,37 @@ void WgCompositor::endRenderPass()
assert(renderPassEncoder);
wgpuRenderPassEncoderEnd(renderPassEncoder);
wgpuRenderPassEncoderRelease(renderPassEncoder);
this->renderPassEncoder = nullptr;
this->currentTarget = nullptr;
renderPassEncoder = nullptr;
currentTarget = nullptr;
}
}
void WgCompositor::reset(WgContext& context)
{
stageBuffer.clear();
}
void WgCompositor::flush(WgContext& context)
{
stageBuffer.append(&meshDataBlit);
stageBuffer.flush(context);
}
void WgCompositor::requestShape(WgRenderDataShape* renderData)
{
stageBuffer.append(renderData);
// TODO: expand for fill settings
}
void WgCompositor::requestImage(WgRenderDataPicture* renderData)
{
stageBuffer.append(renderData);
// TODO: expand for fill settings
}
void WgCompositor::renderShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod)
{
@ -232,7 +269,7 @@ void WgCompositor::renderImage(WgContext& context, WgRenderDataPicture* renderDa
}
void WgCompositor::renderScene(WgContext& context, WgRenderStorage* scene, WgCompose* compose)
void WgCompositor::renderScene(WgContext& context, WgRenderTarget* scene, WgCompose* compose)
{
assert(scene);
assert(compose);
@ -245,7 +282,7 @@ void WgCompositor::renderScene(WgContext& context, WgRenderStorage* scene, WgCom
}
void WgCompositor::composeScene(WgContext& context, WgRenderStorage* src, WgRenderStorage* mask, WgCompose* cmp)
void WgCompositor::composeScene(WgContext& context, WgRenderTarget* src, WgRenderTarget* mask, WgCompose* cmp)
{
assert(cmp);
assert(src);
@ -257,15 +294,19 @@ void WgCompositor::composeScene(WgContext& context, WgRenderStorage* src, WgRend
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);
drawMeshImage(context, &meshDataBlit);
}
void WgCompositor::blit(WgContext& context, WGPUCommandEncoder encoder, WgRenderStorage* src, WGPUTextureView dstView) {
void WgCompositor::blit(WgContext& context, WGPUCommandEncoder encoder, WgRenderTarget* src, WGPUTextureView dstView)
{
assert(!renderPassEncoder);
const WGPURenderPassDepthStencilAttachment depthStencilAttachment{
.view = texViewDepthStencil,
.depthLoadOp = WGPULoadOp_Load, .depthStoreOp = WGPUStoreOp_Discard,
.stencilLoadOp = WGPULoadOp_Load, .stencilStoreOp = WGPUStoreOp_Discard
.depthLoadOp = WGPULoadOp_Load,
.depthStoreOp = WGPUStoreOp_Discard,
.stencilLoadOp = WGPULoadOp_Load,
.stencilStoreOp = WGPUStoreOp_Discard
};
const WGPURenderPassColorAttachment colorAttachment {
.view = dstView,
@ -274,15 +315,56 @@ void WgCompositor::blit(WgContext& context, WGPUCommandEncoder encoder, WgRender
.storeOp = WGPUStoreOp_Store,
};
const 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);
renderPassEncoder = wgpuCommandEncoderBeginRenderPass(encoder, &renderPassDesc);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, src->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.blit);
drawMeshImage(context, &meshDataBlit);
wgpuRenderPassEncoderEnd(renderPassEncoder);
wgpuRenderPassEncoderRelease(renderPassEncoder);
renderPassEncoder = nullptr;
}
void WgCompositor::drawMesh(WgContext& context, WgMeshData* meshData)
{
assert(meshData);
assert(renderPassEncoder);
uint64_t icount = meshData->ibuffer.count;
uint64_t vsize = meshData->vbuffer.count * sizeof(Point);
uint64_t isize = icount * sizeof(uint32_t);
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, stageBuffer.vbuffer_gpu, meshData->voffset, vsize);
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, stageBuffer.ibuffer_gpu, WGPUIndexFormat_Uint32, meshData->ioffset, isize);
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, icount, 1, 0, 0, 0);
};
void WgCompositor::drawMeshFan(WgContext& context, WgMeshData* meshData)
{
assert(meshData);
assert(renderPassEncoder);
uint64_t icount = (meshData->vbuffer.count - 2) * 3;
uint64_t vsize = meshData->vbuffer.count * sizeof(Point);
uint64_t isize = icount * sizeof(uint32_t);
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, stageBuffer.vbuffer_gpu, meshData->voffset, vsize);
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, context.bufferIndexFan, WGPUIndexFormat_Uint32, 0, isize);
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, icount, 1, 0, 0, 0);
};
void WgCompositor::drawMeshImage(WgContext& context, WgMeshData* meshData)
{
assert(meshData);
assert(renderPassEncoder);
uint64_t icount = meshData->ibuffer.count;
uint64_t vsize = meshData->vbuffer.count * sizeof(Point);
uint64_t isize = icount * sizeof(uint32_t);
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, stageBuffer.vbuffer_gpu, meshData->voffset, vsize);
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 1, stageBuffer.vbuffer_gpu, meshData->toffset, vsize);
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, stageBuffer.ibuffer_gpu, WGPUIndexFormat_Uint32, meshData->ioffset, isize);
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, icount, 1, 0, 0, 0);
};
void WgCompositor::drawShape(WgContext& context, WgRenderDataShape* renderData)
{
assert(renderData);
@ -298,7 +380,7 @@ void WgCompositor::drawShape(WgContext& context, WgRenderDataShape* renderData)
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
// draw to stencil (first pass)
ARRAY_FOREACH(p, renderData->meshGroupShapes.meshes)
(*p)->drawFan(context, renderPassEncoder);
drawMeshFan(context, (*p));
// setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
@ -315,7 +397,7 @@ void WgCompositor::drawShape(WgContext& context, WgRenderDataShape* renderData)
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial);
}
// draw to color (second pass)
renderData->meshDataBBox.drawFan(context, renderPassEncoder);
drawMeshFan(context, &renderData->meshDataBBox);
}
@ -325,10 +407,10 @@ void WgCompositor::blendShape(WgContext& context, WgRenderDataShape* renderData,
assert(renderPassEncoder);
assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count);
if (renderData->renderSettingsShape.skip || renderData->meshGroupShapes.meshes.count == 0 || renderData->viewport.invalid()) return;
// copy current render target data to dst storage
WgRenderStorage *target = currentTarget;
// copy current render target data to dst target
WgRenderTarget *target = currentTarget;
endRenderPass();
copyTexture(&storageTemp0, target);
copyTexture(&targetTemp0, target);
beginRenderPass(commandEncoder, target, false);
// render shape with blend settings
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
@ -340,12 +422,12 @@ void WgCompositor::blendShape(WgContext& context, WgRenderDataShape* renderData,
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
// draw to stencil (first pass)
ARRAY_FOREACH(p, renderData->meshGroupShapes.meshes)
(*p)->drawFan(context, renderPassEncoder);
drawMeshFan(context, (*p));
// setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 3, storageTemp0.bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 3, targetTemp0.bindGroupTexure, 0, nullptr);
uint32_t blendMethodInd = (uint32_t)blendMethod;
WgRenderSettings& settings = renderData->renderSettingsShape;
if (settings.fillType == WgRenderSettingsType::Solid) {
@ -359,7 +441,7 @@ void WgCompositor::blendShape(WgContext& context, WgRenderDataShape* renderData,
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial_blend[blendMethodInd]);
}
// draw to color (second pass)
renderData->meshDataBBox.drawFan(context, renderPassEncoder);
drawMeshFan(context, &renderData->meshDataBBox);
}
@ -379,12 +461,12 @@ void WgCompositor::clipShape(WgContext& context, WgRenderDataShape* renderData)
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
// draw to stencil (first pass)
ARRAY_FOREACH(p, renderData->meshGroupShapes.meshes)
(*p)->drawFan(context, renderPassEncoder);
drawMeshFan(context, (*p));
// merge depth and stencil buffer
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.merge_depth_stencil);
renderData->meshDataBBox.drawFan(context, renderPassEncoder);
drawMeshFan(context, &renderData->meshDataBBox);
// setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
@ -401,7 +483,7 @@ void WgCompositor::clipShape(WgContext& context, WgRenderDataShape* renderData)
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial);
}
// draw to color (second pass)
renderData->meshDataBBox.drawFan(context, renderPassEncoder);
drawMeshFan(context, &renderData->meshDataBBox);
}
@ -421,7 +503,7 @@ void WgCompositor::drawStrokes(WgContext& context, WgRenderDataShape* renderData
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
// draw to stencil (first pass)
renderData->meshGroupStrokes.meshes[i]->draw(context, renderPassEncoder);
drawMesh(context, renderData->meshGroupStrokes.meshes[i]);
// setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
@ -438,7 +520,7 @@ void WgCompositor::drawStrokes(WgContext& context, WgRenderDataShape* renderData
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial);
}
// draw to color (second pass)
renderData->meshGroupStrokesBBox.meshes[i]->drawFan(context, renderPassEncoder);
drawMeshFan(context, renderData->meshGroupStrokesBBox.meshes[i]);
}
}
@ -450,10 +532,10 @@ void WgCompositor::blendStrokes(WgContext& context, WgRenderDataShape* renderDat
assert(renderData->meshGroupStrokes.meshes.count == renderData->meshGroupStrokesBBox.meshes.count);
if (renderData->renderSettingsStroke.skip || renderData->meshGroupStrokes.meshes.count == 0 || renderData->viewport.invalid()) return;
// copy current render target data to dst storage
WgRenderStorage *target = currentTarget;
// copy current render target data to dst target
WgRenderTarget *target = currentTarget;
endRenderPass();
copyTexture(&storageTemp0, target);
copyTexture(&targetTemp0, target);
beginRenderPass(commandEncoder, target, false);
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
// draw strokes to stencil (first pass)
@ -464,12 +546,12 @@ void WgCompositor::blendStrokes(WgContext& context, WgRenderDataShape* renderDat
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
// draw to stencil (first pass)
renderData->meshGroupStrokes.meshes[i]->draw(context, renderPassEncoder);
drawMesh(context, renderData->meshGroupStrokes.meshes[i]);
// setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 3, storageTemp0.bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 3, targetTemp0.bindGroupTexure, 0, nullptr);
uint32_t blendMethodInd = (uint32_t)blendMethod;
WgRenderSettings& settings = renderData->renderSettingsStroke;
if (settings.fillType == WgRenderSettingsType::Solid) {
@ -483,7 +565,7 @@ void WgCompositor::blendStrokes(WgContext& context, WgRenderDataShape* renderDat
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial_blend[blendMethodInd]);
}
// draw to color (second pass)
renderData->meshGroupStrokesBBox.meshes[i]->drawFan(context, renderPassEncoder);
drawMeshFan(context, renderData->meshGroupStrokesBBox.meshes[i]);
}
};
@ -505,12 +587,12 @@ void WgCompositor::clipStrokes(WgContext& context, WgRenderDataShape* renderData
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
// draw to stencil (first pass)
renderData->meshGroupStrokes.meshes[i]->draw(context, renderPassEncoder);
drawMesh(context, renderData->meshGroupStrokes.meshes[i]);
// merge depth and stencil buffer
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.merge_depth_stencil);
renderData->meshDataBBox.drawFan(context, renderPassEncoder);
drawMeshFan(context, &renderData->meshDataBBox);
// setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
@ -527,7 +609,7 @@ void WgCompositor::clipStrokes(WgContext& context, WgRenderDataShape* renderData
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial);
}
// draw to color (second pass)
renderData->meshGroupStrokesBBox.meshes[i]->drawFan(context, renderPassEncoder);
drawMeshFan(context, renderData->meshGroupStrokesBBox.meshes[i]);
}
}
@ -543,14 +625,14 @@ void WgCompositor::drawImage(WgContext& context, WgRenderDataPicture* renderData
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
renderData->meshData.drawImage(context, renderPassEncoder);
drawMeshImage(context, &renderData->meshData);
// draw image
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);
drawMeshImage(context, &renderData->meshData);
}
@ -560,26 +642,26 @@ void WgCompositor::blendImage(WgContext& context, WgRenderDataPicture* renderDat
assert(renderPassEncoder);
if (renderData->viewport.invalid()) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
// copy current render target data to dst storage
WgRenderStorage *target = currentTarget;
// copy current render target data to dst target
WgRenderTarget *target = currentTarget;
endRenderPass();
copyTexture(&storageTemp0, target);
copyTexture(&targetTemp0, target);
beginRenderPass(commandEncoder, target, false);
// setup stencil rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
renderData->meshData.drawImage(context, renderPassEncoder);
drawMeshImage(context, &renderData->meshData);
// blend image
uint32_t blendMethodInd = (uint32_t)blendMethod;
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, storageTemp0.bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 3, targetTemp0.bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.image_blend[blendMethodInd]);
renderData->meshData.drawImage(context, renderPassEncoder);
drawMeshImage(context, &renderData->meshData);
};
@ -594,23 +676,23 @@ void WgCompositor::clipImage(WgContext& context, WgRenderDataPicture* renderData
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
renderData->meshData.drawImage(context, renderPassEncoder);
drawMeshImage(context, &renderData->meshData);
// merge depth and stencil buffer
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.merge_depth_stencil);
renderData->meshData.drawImage(context, renderPassEncoder);
drawMeshImage(context, &renderData->meshData);
// draw image
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);
drawMeshImage(context, &renderData->meshData);
}
void WgCompositor::drawScene(WgContext& context, WgRenderStorage* scene, WgCompose* compose)
void WgCompositor::drawScene(WgContext& context, WgRenderTarget* scene, WgCompose* compose)
{
assert(scene);
assert(compose);
@ -622,19 +704,19 @@ void WgCompositor::drawScene(WgContext& context, WgRenderStorage* scene, WgCompo
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, scene->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, bindGroupOpacities[compose->opacity], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.scene);
meshData.drawImage(context, renderPassEncoder);
drawMeshImage(context, &meshDataBlit);
}
void WgCompositor::blendScene(WgContext& context, WgRenderStorage* scene, WgCompose* compose)
void WgCompositor::blendScene(WgContext& context, WgRenderTarget* scene, WgCompose* compose)
{
assert(scene);
assert(compose);
assert(currentTarget);
// copy current render target data to dst storage
WgRenderStorage *target = currentTarget;
// copy current render target data to dst target
WgRenderTarget *target = currentTarget;
endRenderPass();
copyTexture(&storageTemp0, target);
copyTexture(&targetTemp0, target);
beginRenderPass(commandEncoder, target, false);
// blend scene
uint32_t blendMethodInd = (uint32_t)compose->blend;
@ -642,10 +724,10 @@ void WgCompositor::blendScene(WgContext& context, WgRenderStorage* scene, WgComp
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, rect.x(), rect.y(), rect.w(), rect.h());
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, scene->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, storageTemp0.bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, targetTemp0.bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[compose->opacity], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.scene_blend[blendMethodInd]);
meshData.drawImage(context, renderPassEncoder);
drawMeshImage(context, &meshDataBlit);
}
@ -658,13 +740,13 @@ void WgCompositor::markupClipPath(WgContext& context, WgRenderDataShape* renderD
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
ARRAY_FOREACH(p, renderData->meshGroupStrokes.meshes)
(*p)->draw(context, renderPassEncoder);
drawMesh(context, (*p));
} else {
WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::NonZero) ? pipelines.nonzero : pipelines.evenodd;
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
ARRAY_FOREACH(p, renderData->meshGroupShapes.meshes)
(*p)->drawFan(context, renderPassEncoder);
drawMeshFan(context, (*p));
}
}
@ -686,7 +768,7 @@ void WgCompositor::renderClipPath(WgContext& context, WgRenderDataPaint* paint)
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData0->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_stencil_to_depth);
renderData0->meshDataBBox.drawFan(context, renderPassEncoder);
drawMeshFan(context, &renderData0->meshDataBBox);
// merge clip pathes with AND logic
for (auto p = paint->clips.begin() + 1; p < paint->clips.end(); ++p) {
// get render data
@ -698,31 +780,31 @@ void WgCompositor::renderClipPath(WgContext& context, WgRenderDataPaint* paint)
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[190], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_stencil_to_depth_interm);
renderData->meshDataBBox.drawFan(context, renderPassEncoder);
drawMeshFan(context, &renderData->meshDataBBox);
// copy depth to stencil
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 1);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[190], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_depth_to_stencil);
renderData->meshDataBBox.drawFan(context, renderPassEncoder);
drawMeshFan(context, &renderData->meshDataBBox);
// clear depth current (keep stencil)
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[255], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.clear_depth);
renderData->meshDataBBox.drawFan(context, renderPassEncoder);
drawMeshFan(context, &renderData->meshDataBBox);
// clear depth original (keep stencil)
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData0->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[255], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.clear_depth);
renderData0->meshDataBBox.drawFan(context, renderPassEncoder);
drawMeshFan(context, &renderData0->meshDataBBox);
// copy stencil to depth (clear stencil)
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_stencil_to_depth);
renderData->meshDataBBox.drawFan(context, renderPassEncoder);
drawMeshFan(context, &renderData->meshDataBBox);
}
}
@ -743,12 +825,12 @@ void WgCompositor::clearClipPath(WgContext& context, WgRenderDataPaint* paint)
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[255], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.clear_depth);
renderData->meshDataBBox.drawFan(context, renderPassEncoder);
drawMeshFan(context, &renderData->meshDataBBox);
}
}
bool WgCompositor::gaussianBlur(WgContext& context, WgRenderStorage* dst, const RenderEffectGaussianBlur* params, const WgCompose* compose)
bool WgCompositor::gaussianBlur(WgContext& context, WgRenderTarget* dst, const RenderEffectGaussianBlur* params, const WgCompose* compose)
{
assert(dst);
assert(params);
@ -758,8 +840,8 @@ bool WgCompositor::gaussianBlur(WgContext& context, WgRenderStorage* dst, const
auto renderData = (WgRenderDataEffectParams*)params->rd;
auto aabb = compose->aabb;
auto viewport = compose->rdViewport;
WgRenderStorage* sbuff = dst;
WgRenderStorage* dbuff = &storageTemp0;
WgRenderTarget* sbuff = dst;
WgRenderTarget* dbuff = &targetTemp0;
// begin compute pass
WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass gaussian blur" };
@ -791,14 +873,14 @@ bool WgCompositor::gaussianBlur(WgContext& context, WgRenderStorage* dst, const
wgpuComputePassEncoderRelease(computePassEncoder);
// if final result stored in intermidiate buffer we must copy result to destination buffer
if (sbuff == &storageTemp0)
if (sbuff == &targetTemp0)
copyTexture(sbuff, dbuff, aabb);
return true;
}
bool WgCompositor::dropShadow(WgContext& context, WgRenderStorage* dst, const RenderEffectDropShadow* params, const WgCompose* compose)
bool WgCompositor::dropShadow(WgContext& context, WgRenderTarget* dst, const RenderEffectDropShadow* params, const WgCompose* compose)
{
assert(dst);
assert(params);
@ -811,9 +893,9 @@ bool WgCompositor::dropShadow(WgContext& context, WgRenderStorage* dst, const Re
auto viewport = compose->rdViewport;
{ // apply blur
copyTexture(&storageTemp1, dst, aabb);
WgRenderStorage* sbuff = &storageTemp1;
WgRenderStorage* dbuff = &storageTemp0;
copyTexture(&targetTemp1, dst, aabb);
WgRenderTarget* sbuff = &targetTemp1;
WgRenderTarget* dbuff = &targetTemp0;
WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass drop shadow blur" };
WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(commandEncoder, &computePassDesc);
// horizontal blur
@ -837,7 +919,7 @@ bool WgCompositor::dropShadow(WgContext& context, WgRenderStorage* dst, const Re
}
{ // blend origin (temp0), shadow (temp1) to destination
copyTexture(&storageTemp0, dst, aabb);
copyTexture(&targetTemp0, dst, aabb);
WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass drop shadow blend" };
WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(commandEncoder, &computePassDesc);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 0, bindGroupStorageTemp, 0, nullptr);
@ -854,7 +936,7 @@ bool WgCompositor::dropShadow(WgContext& context, WgRenderStorage* dst, const Re
}
bool WgCompositor::fillEffect(WgContext& context, WgRenderStorage* dst, const RenderEffectFill* params, const WgCompose* compose)
bool WgCompositor::fillEffect(WgContext& context, WgRenderTarget* dst, const RenderEffectFill* params, const WgCompose* compose)
{
assert(dst);
assert(params);
@ -862,7 +944,7 @@ bool WgCompositor::fillEffect(WgContext& context, WgRenderStorage* dst, const Re
assert(compose->rdViewport);
assert(!renderPassEncoder);
copyTexture(&storageTemp0, dst, compose->aabb);
copyTexture(&targetTemp0, dst, compose->aabb);
WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass fill" };
WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(commandEncoder, &computePassDesc);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 0, bindGroupStorageTemp, 0, nullptr);
@ -878,7 +960,7 @@ bool WgCompositor::fillEffect(WgContext& context, WgRenderStorage* dst, const Re
}
bool WgCompositor::tintEffect(WgContext& context, WgRenderStorage* dst, const RenderEffectTint* params, const WgCompose* compose)
bool WgCompositor::tintEffect(WgContext& context, WgRenderTarget* dst, const RenderEffectTint* params, const WgCompose* compose)
{
assert(dst);
assert(params);
@ -886,7 +968,7 @@ bool WgCompositor::tintEffect(WgContext& context, WgRenderStorage* dst, const Re
assert(compose->rdViewport);
assert(!renderPassEncoder);
copyTexture(&storageTemp0, dst, compose->aabb);
copyTexture(&targetTemp0, dst, compose->aabb);
WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass tint" };
WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(commandEncoder, &computePassDesc);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 0, bindGroupStorageTemp, 0, nullptr);
@ -901,7 +983,7 @@ bool WgCompositor::tintEffect(WgContext& context, WgRenderStorage* dst, const Re
return true;
}
bool WgCompositor::tritoneEffect(WgContext& context, WgRenderStorage* dst, const RenderEffectTritone* params, const WgCompose* compose)
bool WgCompositor::tritoneEffect(WgContext& context, WgRenderTarget* dst, const RenderEffectTritone* params, const WgCompose* compose)
{
assert(dst);
assert(params);
@ -909,7 +991,7 @@ bool WgCompositor::tritoneEffect(WgContext& context, WgRenderStorage* dst, const
assert(compose->rdViewport);
assert(!renderPassEncoder);
copyTexture(&storageTemp0, dst, compose->aabb);
copyTexture(&targetTemp0, dst, compose->aabb);
WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass tritone" };
WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(commandEncoder, &computePassDesc);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 0, bindGroupStorageTemp, 0, nullptr);

View file

@ -38,6 +38,8 @@ class WgCompositor
private:
// pipelines
WgPipelines pipelines{};
// stage buffers
WgRenderDataStageBuffer stageBuffer{};
// global stencil/depth buffer handles
WGPUTexture texDepthStencil{};
WGPUTextureView texViewDepthStencil{};
@ -52,21 +54,26 @@ private:
// current render pass handles
WGPURenderPassEncoder renderPassEncoder{};
WGPUCommandEncoder commandEncoder{};
WgRenderStorage* currentTarget{};
// intermediate render storages
WgRenderStorage storageTemp0;
WgRenderStorage storageTemp1;
WgRenderTarget* currentTarget{};
// intermediate render targets
WgRenderTarget targetTemp0;
WgRenderTarget targetTemp1;
WGPUBindGroup bindGroupStorageTemp{};
// composition and blend geometries
WgMeshData meshData;
WgMeshData meshDataBlit;
// render target dimensions
uint32_t width{};
uint32_t height{};
// viewport utilities
RenderRegion shrinkRenderRegion(RenderRegion& rect);
void copyTexture(const WgRenderStorage* dst, const WgRenderStorage* src);
void copyTexture(const WgRenderStorage* dst, const WgRenderStorage* src, const RenderRegion& region);
void copyTexture(const WgRenderTarget* dst, const WgRenderTarget* src);
void copyTexture(const WgRenderTarget* dst, const WgRenderTarget* src, const RenderRegion& region);
// base meshes draw
void drawMesh(WgContext& context, WgMeshData* meshData);
void drawMeshFan(WgContext& context, WgMeshData* meshData);
void drawMeshImage(WgContext& context, WgMeshData* meshData);
// shapes
void drawShape(WgContext& context, WgRenderDataShape* renderData);
@ -84,8 +91,8 @@ private:
void clipImage(WgContext& context, WgRenderDataPicture* renderData);
// scenes
void drawScene(WgContext& context, WgRenderStorage* scene, WgCompose* compose);
void blendScene(WgContext& context, WgRenderStorage* src, WgCompose* compose);
void drawScene(WgContext& context, WgRenderTarget* scene, WgCompose* compose);
void blendScene(WgContext& context, WgRenderTarget* src, WgCompose* compose);
// the renderer prioritizes clipping with the stroke over the shape's fill
void markupClipPath(WgContext& context, WgRenderDataShape* renderData);
@ -100,24 +107,32 @@ public:
void resize(WgContext& context, uint32_t width, uint32_t height);
// render passes workflow
void beginRenderPass(WGPUCommandEncoder encoder, WgRenderStorage* target, bool clear, WGPUColor clearColor = { 0.0, 0.0, 0.0, 0.0 });
void beginRenderPass(WGPUCommandEncoder encoder, WgRenderTarget* target, bool clear, WGPUColor clearColor = { 0.0, 0.0, 0.0, 0.0 });
void endRenderPass();
// request shapes for drawing (staging)
// stage data
void reset(WgContext& context);
void flush(WgContext& context);
void requestShape(WgRenderDataShape* renderData);
void requestImage(WgRenderDataPicture* renderData);
// render shapes, images and scenes
void renderShape(WgContext& context, WgRenderDataShape* 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 renderScene(WgContext& context, WgRenderTarget* scene, WgCompose* compose);
void composeScene(WgContext& context, WgRenderTarget* src, WgRenderTarget* mask, WgCompose* compose);
// blit render storage to texture view (f.e. screen buffer)
void blit(WgContext& context, WGPUCommandEncoder encoder, WgRenderStorage* src, WGPUTextureView dstView);
// blit render target to texture view (f.e. screen buffer)
void blit(WgContext& context, WGPUCommandEncoder encoder, WgRenderTarget* src, WGPUTextureView dstView);
// effects
bool gaussianBlur(WgContext& context, WgRenderStorage* dst, const RenderEffectGaussianBlur* params, const WgCompose* compose);
bool dropShadow(WgContext& context, WgRenderStorage* dst, const RenderEffectDropShadow* params, const WgCompose* compose);
bool fillEffect(WgContext& context, WgRenderStorage* dst, const RenderEffectFill* params, const WgCompose* compose);
bool tintEffect(WgContext& context, WgRenderStorage* dst, const RenderEffectTint* params, const WgCompose* compose);
bool tritoneEffect(WgContext& context, WgRenderStorage* dst, const RenderEffectTritone* params, const WgCompose* compose);
bool gaussianBlur(WgContext& context, WgRenderTarget* dst, const RenderEffectGaussianBlur* params, const WgCompose* compose);
bool dropShadow(WgContext& context, WgRenderTarget* dst, const RenderEffectDropShadow* params, const WgCompose* compose);
bool fillEffect(WgContext& context, WgRenderTarget* dst, const RenderEffectFill* params, const WgCompose* compose);
bool tintEffect(WgContext& context, WgRenderTarget* dst, const RenderEffectTint* params, const WgCompose* compose);
bool tritoneEffect(WgContext& context, WgRenderTarget* dst, const RenderEffectTritone* params, const WgCompose* compose);
};
#endif // _TVG_WG_COMPOSITOR_H_

View file

@ -30,95 +30,88 @@
// WgMeshData
//***********************************************************************
void WgMeshData::draw(WgContext& context, WGPURenderPassEncoder renderPassEncoder)
{
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, bufferPosition, 0, vertexCount * sizeof(float) * 2);
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, bufferIndex, WGPUIndexFormat_Uint32, 0, indexCount * sizeof(uint32_t));
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, indexCount, 1, 0, 0, 0);
}
void WgMeshData::drawFan(WgContext& context, WGPURenderPassEncoder renderPassEncoder)
{
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, bufferPosition, 0, vertexCount * sizeof(float) * 2);
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, context.bufferIndexFan, WGPUIndexFormat_Uint32, 0, indexCount * sizeof(uint32_t));
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, indexCount, 1, 0, 0, 0);
}
void WgMeshData::drawImage(WgContext& context, WGPURenderPassEncoder renderPassEncoder)
{
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, bufferPosition, 0, vertexCount * sizeof(float) * 2);
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 1, bufferTexCoord, 0, vertexCount * sizeof(float) * 2);
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, bufferIndex, WGPUIndexFormat_Uint32, 0, indexCount * sizeof(uint32_t));
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, indexCount, 1, 0, 0, 0);
};
void WgMeshData::update(WgContext& context, const WgVertexBuffer& vertexBuffer)
{
assert(vertexBuffer.count > 2);
vertexCount = vertexBuffer.count;
indexCount = (vertexBuffer.count - 2) * 3;
context.allocateBufferVertex(bufferPosition, (float*)vertexBuffer.data, vertexCount * sizeof(float) * 2);
context.allocateBufferIndexFan(vertexCount);
// setup vertex data
vbuffer.reserve(vertexBuffer.count);
vbuffer.count = vertexBuffer.count;
memcpy(vbuffer.data, vertexBuffer.data, sizeof(vertexBuffer.data[0])*vertexBuffer.count);
// setup tex coords data
tbuffer.clear();
context.allocateBufferIndexFan(vbuffer.count);
}
void WgMeshData::update(WgContext& context, const WgIndexedVertexBuffer& vertexBufferInd)
{
assert(vertexBufferInd.vcount > 2);
vertexCount = vertexBufferInd.vcount;
indexCount = vertexBufferInd.icount;
if (vertexCount > 0) context.allocateBufferVertex(bufferPosition, (float*)vertexBufferInd.vbuff, vertexCount * sizeof(float) * 2);
if (indexCount > 0) context.allocateBufferIndex(bufferIndex, vertexBufferInd.ibuff, indexCount * sizeof(uint32_t));
// setup vertex data
vbuffer.reserve(vertexBufferInd.vcount);
vbuffer.count = vertexBufferInd.vcount;
memcpy(vbuffer.data, vertexBufferInd.vbuff, sizeof(vertexBufferInd.vbuff[0])*vertexBufferInd.vcount);
// setup tex coords data
tbuffer.clear();
// copy index data
ibuffer.reserve(vertexBufferInd.icount);
ibuffer.count = vertexBufferInd.icount;
memcpy(ibuffer.data, vertexBufferInd.ibuff, sizeof(vertexBufferInd.ibuff[0])*vertexBufferInd.icount);
};
void WgMeshData::bbox(WgContext& context, const Point pmin, const Point pmax)
{
vertexCount = 4;
indexCount = 6;
const float data[] = {pmin.x, pmin.y, pmax.x, pmin.y, pmax.x, pmax.y, pmin.x, pmax.y};
context.allocateBufferVertex(bufferPosition, data, sizeof(data));
context.allocateBufferIndexFan(vertexCount);
// setup vertex data
vbuffer.reserve(4);
vbuffer.count = 4;
memcpy(vbuffer.data, data, sizeof(data));
// setup tex coords data
tbuffer.clear();
context.allocateBufferIndexFan(vbuffer.count);
}
void WgMeshData::imageBox(WgContext& context, float w, float h)
{
vertexCount = 4;
indexCount = 6;
const float vdata[] = {0.0f, 0.0f, w, 0.0f, w, h, 0.0f, h};
const float tdata[] = {0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
const uint32_t idata[] = {0, 1, 2, 0, 2, 3};
context.allocateBufferVertex(bufferPosition, vdata, sizeof(vdata));
context.allocateBufferVertex(bufferTexCoord, tdata, sizeof(tdata));
context.allocateBufferIndex(bufferIndex, idata, sizeof(idata));
// setup vertex data
vbuffer.reserve(4);
vbuffer.count = 4;
memcpy(vbuffer.data, vdata, sizeof(vdata));
// setup tex coords data
tbuffer.reserve(4);
tbuffer.count = 4;
memcpy(tbuffer.data, tdata, sizeof(tdata));
// setup indexes data
ibuffer.reserve(6);
ibuffer.count = 6;
memcpy(ibuffer.data, idata, sizeof(idata));
}
void WgMeshData::blitBox(WgContext& context)
{
vertexCount = 4;
indexCount = 6;
const float vdata[] = {-1.0f, +1.0f, +1.0f, +1.0f, +1.0f, -1.0f, -1.0f, -1.0f};
const float tdata[] = {+0.0f, +0.0f, +1.0f, +0.0f, +1.0f, +1.0f, +0.0f, +1.0f};
const uint32_t idata[] = { 0, 1, 2, 0, 2, 3 };
context.allocateBufferVertex(bufferPosition, vdata, sizeof(vdata));
context.allocateBufferVertex(bufferTexCoord, tdata, sizeof(tdata));
context.allocateBufferIndex(bufferIndex, idata, sizeof(idata));
// setup vertex data
vbuffer.reserve(4);
vbuffer.count = 4;
memcpy(vbuffer.data, vdata, sizeof(vdata));
// setup tex coords data
tbuffer.reserve(4);
tbuffer.count = 4;
memcpy(tbuffer.data, tdata, sizeof(tdata));
// setup indexes data
ibuffer.reserve(6);
ibuffer.count = 6;
memcpy(ibuffer.data, idata, sizeof(idata));
}
void WgMeshData::release(WgContext& context)
{
context.releaseBuffer(bufferIndex);
context.releaseBuffer(bufferTexCoord);
context.releaseBuffer(bufferPosition);
};
//***********************************************************************
// WgMeshDataPool
//***********************************************************************
@ -681,3 +674,94 @@ void WgRenderDataEffectParamsPool::release(WgContext& context)
mPool.clear();
mList.clear();
}
//***********************************************************************
// WgRenderDataStageBuffer
//***********************************************************************
void WgRenderDataStageBuffer::append(WgMeshData* meshData)
{
assert(meshData);
uint32_t vsize = meshData->vbuffer.count * sizeof(meshData->vbuffer[0]);
uint32_t tsize = meshData->tbuffer.count * sizeof(meshData->tbuffer[0]);
uint32_t isize = meshData->ibuffer.count * sizeof(meshData->ibuffer[0]);
// append vertex data
if (vbuffer.reserved < vbuffer.count + vsize)
vbuffer.grow(std::max(vsize, vbuffer.reserved));
if (meshData->vbuffer.count > 0) {
meshData->voffset = vbuffer.count;
memcpy(vbuffer.data + vbuffer.count, meshData->vbuffer.data, vsize);
vbuffer.count += vsize;
}
// append tex coords data
if (vbuffer.reserved < vbuffer.count + tsize)
vbuffer.grow(std::max(tsize, vbuffer.reserved));
if (meshData->tbuffer.count > 0) {
meshData->toffset = vbuffer.count;
memcpy(vbuffer.data + vbuffer.count, meshData->tbuffer.data, tsize);
vbuffer.count += tsize;
}
// append index data
if (ibuffer.reserved < ibuffer.count + isize)
ibuffer.grow(std::max(isize, ibuffer.reserved));
if (meshData->ibuffer.count > 0) {
meshData->ioffset = ibuffer.count;
memcpy(ibuffer.data + ibuffer.count, meshData->ibuffer.data, isize);
ibuffer.count += isize;
}
}
void WgRenderDataStageBuffer::append(WgMeshDataGroup* meshDataGroup)
{
ARRAY_FOREACH(p, meshDataGroup->meshes) append(*p);
}
void WgRenderDataStageBuffer::append(WgRenderDataShape* renderDataShape)
{
append(&renderDataShape->meshGroupShapes);
append(&renderDataShape->meshGroupShapesBBox);
append(&renderDataShape->meshGroupStrokes);
append(&renderDataShape->meshGroupStrokesBBox);
append(&renderDataShape->meshDataBBox);
ARRAY_FOREACH(p, renderDataShape->clips)
append((WgRenderDataShape* )(*p));
}
void WgRenderDataStageBuffer::append(WgRenderDataPicture* renderDataPicture)
{
append(&renderDataPicture->meshData);
ARRAY_FOREACH(p, renderDataPicture->clips)
append((WgRenderDataShape* )(*p));
}
void WgRenderDataStageBuffer::release(WgContext& context)
{
context.releaseBuffer(vbuffer_gpu);
context.releaseBuffer(ibuffer_gpu);
}
void WgRenderDataStageBuffer::clear()
{
vbuffer.clear();
ibuffer.clear();
}
void WgRenderDataStageBuffer::flush(WgContext& context)
{
context.allocateBufferVertex(vbuffer_gpu, (float *)vbuffer.data, vbuffer.count);
context.allocateBufferIndex(ibuffer_gpu, (uint32_t *)ibuffer.data, ibuffer.count);
}
void WgRenderDataStageBuffer::bind(WGPURenderPassEncoder renderPass, size_t voffset, size_t toffset)
{
wgpuRenderPassEncoderSetVertexBuffer(renderPass, 0, vbuffer_gpu, voffset, vbuffer.count - voffset);
wgpuRenderPassEncoderSetVertexBuffer(renderPass, 1, vbuffer_gpu, toffset, vbuffer.count - toffset);
wgpuRenderPassEncoderSetIndexBuffer(renderPass, ibuffer_gpu, WGPUIndexFormat_Uint32, 0, ibuffer.count);
}

View file

@ -28,22 +28,19 @@
#include "tvgWgShaderTypes.h"
struct WgMeshData {
WGPUBuffer bufferPosition{};
WGPUBuffer bufferTexCoord{};
WGPUBuffer bufferIndex{};
size_t vertexCount{};
size_t indexCount{};
void draw(WgContext& context, WGPURenderPassEncoder renderPassEncoder);
void drawFan(WgContext& context, WGPURenderPassEncoder renderPassEncoder);
void drawImage(WgContext& context, WGPURenderPassEncoder renderPassEncoder);
Array<Point> vbuffer;
Array<Point> tbuffer;
Array<uint32_t> ibuffer;
size_t voffset{};
size_t toffset{};
size_t ioffset{};
void update(WgContext& context, const WgVertexBuffer& vertexBuffer);
void update(WgContext& context, const WgIndexedVertexBuffer& vertexBufferInd);
void bbox(WgContext& context, const Point pmin, const Point pmax);
void imageBox(WgContext& context, float w, float h);
void blitBox(WgContext& context);
void release(WgContext& context);
void release(WgContext& context) {};
};
class WgMeshDataPool {
@ -224,4 +221,23 @@ public:
void release(WgContext& context);
};
class WgRenderDataStageBuffer {
private:
Array<uint8_t> vbuffer;
Array<uint8_t> ibuffer;
public:
WGPUBuffer vbuffer_gpu{};
WGPUBuffer ibuffer_gpu{};
void append(WgMeshData* meshData);
void append(WgMeshDataGroup* meshDataGroup);
void append(WgRenderDataShape* renderDataShape);
void append(WgRenderDataPicture* renderDataPicture);
void initialize(WgContext& context){};
void release(WgContext& context);
void clear();
void flush(WgContext& context);
void bind(WGPURenderPassEncoder renderPass, size_t voffset, size_t toffset);
};
#endif // _TVG_WG_RENDER_DATA_H_

View file

@ -22,7 +22,7 @@
#include "tvgWgRenderTarget.h"
void WgRenderStorage::initialize(WgContext& context, uint32_t width, uint32_t height)
void WgRenderTarget::initialize(WgContext& context, uint32_t width, uint32_t height)
{
this->width = width;
this->height = height;
@ -36,7 +36,7 @@ void WgRenderStorage::initialize(WgContext& context, uint32_t width, uint32_t he
}
void WgRenderStorage::release(WgContext& context)
void WgRenderTarget::release(WgContext& context)
{
context.layouts.releaseBindGroup(bindGroupTexure);
context.layouts.releaseBindGroup(bindGroupWrite);
@ -50,38 +50,38 @@ void WgRenderStorage::release(WgContext& context)
}
//*****************************************************************************
// render storage pool
// render target pool
//*****************************************************************************
WgRenderStorage* WgRenderStoragePool::allocate(WgContext& context)
WgRenderTarget* WgRenderTargetPool::allocate(WgContext& context)
{
WgRenderStorage* renderStorage{};
WgRenderTarget* renderTarget{};
if (pool.count > 0) {
renderStorage = pool.last();
renderTarget = pool.last();
pool.pop();
} else {
renderStorage = new WgRenderStorage;
renderStorage->initialize(context, width, height);
list.push(renderStorage);
renderTarget = new WgRenderTarget;
renderTarget->initialize(context, width, height);
list.push(renderTarget);
}
return renderStorage;
return renderTarget;
};
void WgRenderStoragePool::free(WgContext& context, WgRenderStorage* renderStorage)
void WgRenderTargetPool::free(WgContext& context, WgRenderTarget* renderTarget)
{
pool.push(renderStorage);
pool.push(renderTarget);
};
void WgRenderStoragePool::initialize(WgContext& context, uint32_t width, uint32_t height)
void WgRenderTargetPool::initialize(WgContext& context, uint32_t width, uint32_t height)
{
this->width = width;
this->height = height;
}
void WgRenderStoragePool::release(WgContext& context)
void WgRenderTargetPool::release(WgContext& context)
{
ARRAY_FOREACH(p, list) {
(*p)->release(context);

View file

@ -26,7 +26,7 @@
#include "tvgWgPipelines.h"
#include "tvgRender.h"
struct WgRenderStorage {
struct WgRenderTarget {
WGPUTexture texture{};
WGPUTexture textureMS{};
WGPUTextureView texView{};
@ -42,15 +42,15 @@ struct WgRenderStorage {
};
class WgRenderStoragePool {
class WgRenderTargetPool {
private:
Array<WgRenderStorage*> list;
Array<WgRenderStorage*> pool;
Array<WgRenderTarget*> list;
Array<WgRenderTarget*> pool;
uint32_t width{};
uint32_t height{};
public:
WgRenderStorage* allocate(WgContext& context);
void free(WgContext& context, WgRenderStorage* renderTarget);
WgRenderTarget* allocate(WgContext& context);
void free(WgContext& context, WgRenderTarget* renderTarget);
void initialize(WgContext& context, uint32_t width, uint32_t height);
void release(WgContext& context);

View file

@ -0,0 +1,93 @@
/*
* Copyright (c) 2025 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgWgRenderTask.h"
#include <iostream>
//***********************************************************************
// WgPaintTask
//***********************************************************************
void WgPaintTask::run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder)
{
if (renderData->type() == tvg::Type::Shape)
compositor.renderShape(context, (WgRenderDataShape*)renderData, blendMethod);
if (renderData->type() == tvg::Type::Picture)
compositor.renderImage(context, (WgRenderDataPicture*)renderData, blendMethod);
else assert(true);
}
//***********************************************************************
// WgSceneTask
//***********************************************************************
void WgSceneTask::run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder)
{
// begin the render pass for the current scene and clear the target content
WGPUColor color{};
if ((compose->method == MaskMethod::None) && (compose->blend != BlendMethod::Normal)) color = { 1.0, 1.0, 1.0, 0.0 };
compositor.beginRenderPass(encoder, renderTarget, true, color);
// run all childs (scenes and shapes)
runChildren(context, compositor, encoder);
// we must to end current render pass for current scene
compositor.endRenderPass();
// we must to apply effect for current scene
if (effect)
runEffect(context, compositor, encoder);
// there's no point in continuing if the scene has no destination target (e.g., the root scene)
if (!renderTargetDst) return;
// apply scene blending
if (compose->method == MaskMethod::None) {
compositor.beginRenderPass(encoder, renderTargetDst, false);
compositor.renderScene(context, renderTarget, compose);
// apply scene composition (for scenes, that have a handle to mask)
} else if (renderTargetMsk) {
compositor.beginRenderPass(encoder, renderTargetDst, false);
compositor.composeScene(context, renderTarget, renderTargetMsk, compose);
}
}
void WgSceneTask::runChildren(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder)
{
ARRAY_FOREACH(task, children) {
WgRenderTask* renderTask = *task;
// we need to restore current render pass without clear
compositor.beginRenderPass(encoder, renderTarget, false);
// run children (shape or scene)
renderTask->run(context, compositor, encoder);
}
}
void WgSceneTask::runEffect(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder)
{
assert(effect);
switch (effect->type) {
case SceneEffect::GaussianBlur: compositor.gaussianBlur(context, renderTarget, (RenderEffectGaussianBlur*)effect, compose); break;
case SceneEffect::DropShadow: compositor.dropShadow(context, renderTarget, (RenderEffectDropShadow*)effect, compose); break;
case SceneEffect::Fill: compositor.fillEffect(context, renderTarget, (RenderEffectFill*)effect, compose); break;
case SceneEffect::Tint: compositor.tintEffect(context, renderTarget, (RenderEffectTint*)effect, compose); break;
case SceneEffect::Tritone : compositor.tritoneEffect(context, renderTarget, (RenderEffectTritone*)effect, compose); break;
default: break;
}
}

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2025 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_WG_RENDER_TASK_H_
#define _TVG_WG_RENDER_TASK_H_
#include "tvgWgCompositor.h"
// base class for any renderable objects
struct WgRenderTask {
virtual ~WgRenderTask() {}
virtual void run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder) = 0;
};
// task for sinlge shape rendering
struct WgPaintTask: public WgRenderTask {
// shape render properties
WgRenderDataPaint* renderData{};
BlendMethod blendMethod{};
WgPaintTask(WgRenderDataPaint* renderData, BlendMethod blendMethod) :
renderData(renderData), blendMethod(blendMethod) {}
// apply shape execution, including custom blending and clipping
void run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder) override;
};
// task for scene rendering with blending, composition and effect
struct WgSceneTask: public WgRenderTask {
public:
// parent scene (nullptr for root scene)
WgSceneTask* parent{};
// childs can be shapes or scenes tesks
Array<WgRenderTask*> children;
// scene blend/compose targets
WgRenderTarget* renderTarget{};
WgRenderTarget* renderTargetMsk{};
WgRenderTarget* renderTargetDst{};
// scene blend/compose properties
WgCompose* compose{};
// scene effect properties
const RenderEffect* effect{};
WgSceneTask(WgRenderTarget* renderTarget, WgCompose* compose, WgSceneTask* parent) :
parent(parent), renderTarget(renderTarget), compose(compose) {}
// run all, including all shapes drawing, blending, composition and effect
void run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder) override;
private:
void runChildren(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder);
void runEffect(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder);
};
#endif // _TVG_WG_RENDER_TASK_H_

View file

@ -47,13 +47,13 @@ void WgRenderer::release()
mRenderDataEffectParamsPool.release(mContext);
WgMeshDataPool::gMeshDataPool->release(mContext);
// clear render storage pool
mRenderStoragePool.release(mContext);
// clear render pool
mRenderTargetPool.release(mContext);
// clear rendering tree stacks
mCompositorStack.clear();
mRenderStorageStack.clear();
mRenderStorageRoot.release(mContext);
mCompositorList.clear();
mRenderTargetStack.clear();
mRenderTargetRoot.release(mContext);
// release context handles
mCompositor.release(mContext);
@ -195,50 +195,71 @@ RenderData WgRenderer::prepare(RenderSurface* surface, RenderData data, const Ma
bool WgRenderer::preRender()
{
// invalidate context
if (mContext.invalid()) return false;
// push rot render storage to the render tree stack
assert(mRenderStorageStack.count == 0);
mRenderStorageStack.push(&mRenderStorageRoot);
// create command encoder for drawing
WGPUCommandEncoderDescriptor commandEncoderDesc{};
mCommandEncoder = wgpuDeviceCreateCommandEncoder(mContext.device, &commandEncoderDesc);
// start root render pass
mCompositor.beginRenderPass(mCommandEncoder, mRenderStorageStack.last(), true);
// reset stage data
mCompositor.reset(mContext);
// push root render target to the render tree stack
assert(mRenderTargetStack.count == 0);
mRenderTargetStack.push(&mRenderTargetRoot);
// create root compose settings
WgCompose* compose = new WgCompose();
compose->aabb = { { 0, 0 }, { (int32_t)mTargetSurface.w, (int32_t)mTargetSurface.h } };
compose->blend = BlendMethod::Normal;
compose->method = MaskMethod::None;
compose->opacity = 255;
mCompositorList.push(compose);
// create root scene
WgSceneTask* sceneTask = new WgSceneTask(&mRenderTargetRoot, compose, nullptr);
mRenderTaskList.push(sceneTask);
mSceneTaskStack.push(sceneTask);
return true;
}
bool WgRenderer::renderShape(RenderData data)
{
// temporary simple render data to the current render target
mCompositor.renderShape(mContext, (WgRenderDataShape*)data, mBlendMethod);
WgPaintTask* paintTask = new WgPaintTask((WgRenderDataPaint*)data, mBlendMethod);
WgSceneTask* sceneTask = mSceneTaskStack.last();
sceneTask->children.push(paintTask);
mRenderTaskList.push(paintTask);
mCompositor.requestShape((WgRenderDataShape*)data);
return true;
}
bool WgRenderer::renderImage(RenderData data)
{
// temporary simple render data to the current render target
mCompositor.renderImage(mContext, (WgRenderDataPicture*)data, mBlendMethod);
WgPaintTask* paintTask = new WgPaintTask((WgRenderDataPaint*)data, mBlendMethod);
WgSceneTask* sceneTask = mSceneTaskStack.last();
sceneTask->children.push(paintTask);
mRenderTaskList.push(paintTask);
mCompositor.requestImage((WgRenderDataPicture*)data);
return true;
}
bool WgRenderer::postRender()
{
// end root render pass
mCompositor.endRenderPass();
// release command encoder
const WGPUCommandBufferDescriptor commandBufferDesc{};
WGPUCommandBuffer commandsBuffer = wgpuCommandEncoderFinish(mCommandEncoder, &commandBufferDesc);
wgpuQueueSubmit(mContext.queue, 1, &commandsBuffer);
wgpuCommandBufferRelease(commandsBuffer);
wgpuCommandEncoderRelease(mCommandEncoder);
// pop root render storage to the render tree stack
mRenderStorageStack.pop();
assert(mRenderStorageStack.count == 0);
// clear viewport list and store allocated handles to pool
// flush stage data to gpu
mCompositor.flush(mContext);
// create command encoder for drawing
WGPUCommandEncoder commandEncoder = mContext.createCommandEncoder();
// run rendering (all the fun is here)
WgSceneTask* sceneTaskRoot = mSceneTaskStack.last();
sceneTaskRoot->run(mContext, mCompositor, commandEncoder);
// execute and release command encoder
mContext.submitCommandEncoder(commandEncoder);
mContext.releaseCommandEncoder(commandEncoder);
// clear the render tasks tree
mSceneTaskStack.pop();
assert(mSceneTaskStack.count == 0);
mRenderTargetStack.pop();
assert(mRenderTargetStack.count == 0);
ARRAY_FOREACH(p, mRenderTaskList) { delete (*p); };
mRenderTaskList.clear();
ARRAY_FOREACH(p, mCompositorList) { delete (*p); };
mCompositorList.clear();
ARRAY_FOREACH(p, mRenderDataViewportList)
mRenderDataViewportPool.free(mContext, *p);
mRenderDataViewportList.clear();
@ -325,18 +346,11 @@ bool WgRenderer::sync()
WGPUTextureView dstTextureView = mContext.createTextureView(dstTexture);
// create command encoder
const WGPUCommandEncoderDescriptor commandEncoderDesc{};
WGPUCommandEncoder commandEncoder = wgpuDeviceCreateCommandEncoder(mContext.device, &commandEncoderDesc);
WGPUCommandEncoder commandEncoder = mContext.createCommandEncoder();
// show root offscreen buffer
mCompositor.blit(mContext, commandEncoder, &mRenderStorageRoot, dstTextureView);
// release command encoder
const WGPUCommandBufferDescriptor commandBufferDesc{};
WGPUCommandBuffer commandsBuffer = wgpuCommandEncoderFinish(commandEncoder, &commandBufferDesc);
wgpuQueueSubmit(mContext.queue, 1, &commandsBuffer);
wgpuCommandBufferRelease(commandsBuffer);
wgpuCommandEncoderRelease(commandEncoder);
mCompositor.blit(mContext, commandEncoder, &mRenderTargetRoot, dstTextureView);
mContext.submitCommandEncoder(commandEncoder);
mContext.releaseCommandEncoder(commandEncoder);
// release dest buffer view
mContext.releaseTextureView(dstTextureView);
@ -367,8 +381,8 @@ bool WgRenderer::target(WGPUDevice device, WGPUInstance instance, void* target,
mContext.initialize(instance, device);
// initialize render tree instances
mRenderStoragePool.initialize(mContext, width, height);
mRenderStorageRoot.initialize(mContext, width, height);
mRenderTargetPool.initialize(mContext, width, height);
mRenderTargetRoot.initialize(mContext, width, height);
mCompositor.initialize(mContext, width, height);
// store target properties
@ -387,12 +401,12 @@ bool WgRenderer::target(WGPUDevice device, WGPUInstance instance, void* target,
// update render targets dimentions
if ((mTargetSurface.w != width) || (mTargetSurface.h != height) || (type == 0 ? (surface != (WGPUSurface)target) : (targetTexture != (WGPUTexture)target))) {
// release render tagets
mRenderStoragePool.release(mContext);
mRenderStorageRoot.release(mContext);
mRenderTargetPool.release(mContext);
mRenderTargetRoot.release(mContext);
clearTargets();
mRenderStoragePool.initialize(mContext, width, height);
mRenderStorageRoot.initialize(mContext, width, height);
mRenderTargetPool.initialize(mContext, width, height);
mRenderTargetRoot.initialize(mContext, width, height);
mCompositor.resize(mContext, width, height);
// store target properties
@ -447,7 +461,7 @@ RenderCompositor* WgRenderer::target(const RenderRegion& region, TVG_UNUSED Colo
compose->rdViewport->update(mContext, region);
mRenderDataViewportList.push(compose->rdViewport);
}
mCompositorStack.push(compose);
mCompositorList.push(compose);
return compose;
}
@ -459,57 +473,52 @@ bool WgRenderer::beginComposite(RenderCompositor* cmp, MaskMethod method, uint8_
compose->method = method;
compose->opacity = opacity;
compose->blend = mBlendMethod;
// end current render pass
mCompositor.endRenderPass();
// allocate new render storage and push to the render tree stack
WgRenderStorage* storage = mRenderStoragePool.allocate(mContext);
mRenderStorageStack.push(storage);
// begin newly added render pass
WGPUColor color{};
if ((compose->method == MaskMethod::None) && (compose->blend != BlendMethod::Normal)) color = { 1.0, 1.0, 1.0, 0.0 };
mCompositor.beginRenderPass(mCommandEncoder, mRenderStorageStack.last(), true, color);
WgSceneTask* sceneTaskCurrent = mSceneTaskStack.last();
// allocate new render target and push to the render tree stack
WgRenderTarget* renderTarget = mRenderTargetPool.allocate(mContext);
mRenderTargetStack.push(renderTarget);
// create and setup new scene task
WgSceneTask* sceneTask = new WgSceneTask(renderTarget, compose, sceneTaskCurrent);
sceneTaskCurrent->children.push(sceneTask);
mRenderTaskList.push(sceneTask);
mSceneTaskStack.push(sceneTask);
return true;
}
bool WgRenderer::endComposite(RenderCompositor* cmp)
{
// get current composition settings
WgCompose* comp = (WgCompose*)cmp;
// we must to end current render pass to run blend/composition mechanics
mCompositor.endRenderPass();
// finish scene blending
if (comp->method == MaskMethod::None) {
// get source and destination render storages
WgRenderStorage* src = mRenderStorageStack.last();
mRenderStorageStack.pop();
WgRenderStorage* dst = mRenderStorageStack.last();
// begin previous render pass
mCompositor.beginRenderPass(mCommandEncoder, dst, false);
// apply composition
mCompositor.renderScene(mContext, src, comp);
if (cmp->method == MaskMethod::None) {
// get source and destination render targets
WgRenderTarget* src = mRenderTargetStack.last();
mRenderTargetStack.pop();
// pop source scene
WgSceneTask* srcScene = mSceneTaskStack.last();
mSceneTaskStack.pop();
// setup render target compose destitations
srcScene->renderTargetDst = mSceneTaskStack.last()->renderTarget;
srcScene->renderTargetMsk = nullptr;
// back render targets to the pool
mRenderStoragePool.free(mContext, src);
} else { // finish composition
// get source, mask and destination render storages
WgRenderStorage* src = mRenderStorageStack.last();
mRenderStorageStack.pop();
WgRenderStorage* msk = mRenderStorageStack.last();
mRenderStorageStack.pop();
WgRenderStorage* dst = mRenderStorageStack.last();
// begin previous render pass
mCompositor.beginRenderPass(mCommandEncoder, dst, false);
// apply composition
mCompositor.composeScene(mContext, src, msk, comp);
mRenderTargetPool.free(mContext, src);
} else { // finish scene composition
// get source, mask and destination render targets
WgRenderTarget* src = mRenderTargetStack.last();
mRenderTargetStack.pop();
WgRenderTarget* msk = mRenderTargetStack.last();
mRenderTargetStack.pop();
// get source and mask scenes
WgSceneTask* srcScene = mSceneTaskStack.last();
mSceneTaskStack.pop();
WgSceneTask* mskScene = mSceneTaskStack.last();
mSceneTaskStack.pop();
// setup render target compose destitations
srcScene->renderTargetDst = mSceneTaskStack.last()->renderTarget;
srcScene->renderTargetMsk = mskScene->renderTarget;
// back render targets to the pool
mRenderStoragePool.free(mContext, src);
mRenderStoragePool.free(mContext, msk);
mRenderTargetPool.free(mContext, src);
mRenderTargetPool.free(mContext, msk);
}
// delete current compositor settings
delete mCompositorStack.last();
mCompositorStack.pop();
return true;
}
@ -606,20 +615,8 @@ bool WgRenderer::region(RenderEffect* effect)
bool WgRenderer::render(RenderCompositor* cmp, const RenderEffect* effect, TVG_UNUSED bool direct)
{
// we must to end current render pass to resolve ms texture before effect
mCompositor.endRenderPass();
WgCompose* comp = (WgCompose*)cmp;
WgRenderStorage* dst = mRenderStorageStack.last();
switch (effect->type) {
case SceneEffect::GaussianBlur: return mCompositor.gaussianBlur(mContext, dst, (RenderEffectGaussianBlur*)effect, comp);
case SceneEffect::DropShadow: return mCompositor.dropShadow(mContext, dst, (RenderEffectDropShadow*)effect, comp);
case SceneEffect::Fill: return mCompositor.fillEffect(mContext, dst, (RenderEffectFill*)effect, comp);
case SceneEffect::Tint: return mCompositor.tintEffect(mContext, dst, (RenderEffectTint*)effect, comp);
case SceneEffect::Tritone : return mCompositor.tritoneEffect(mContext, dst, (RenderEffectTritone*)effect, comp);
default: return false;
}
return false;
mSceneTaskStack.last()->effect = effect;
return true;
}

View file

@ -23,7 +23,7 @@
#ifndef _TVG_WG_RENDERER_H_
#define _TVG_WG_RENDERER_H_
#include "tvgWgCompositor.h"
#include "tvgWgRenderTask.h"
class WgRenderer : public RenderMethod
{
@ -72,13 +72,15 @@ private:
bool surfaceConfigure(WGPUSurface surface, WgContext& context, uint32_t width, uint32_t height);
// render tree stacks
WgRenderStorage mRenderStorageRoot;
Array<WgCompose*> mCompositorStack;
Array<WgRenderStorage*> mRenderStorageStack;
WgRenderTarget mRenderTargetRoot;
Array<WgCompose*> mCompositorList;
Array<WgRenderTarget*> mRenderTargetStack;
Array<WgRenderDataViewport*> mRenderDataViewportList;
Array<WgSceneTask*> mSceneTaskStack;
Array<WgRenderTask*> mRenderTaskList;
// render storage pool
WgRenderStoragePool mRenderStoragePool;
// render target pool
WgRenderTargetPool mRenderTargetPool;
// render data paint pools
WgRenderDataShapePool mRenderDataShapePool;
@ -100,7 +102,6 @@ private:
Key mDisposeKey{};
// gpu handles
WGPUCommandEncoder mCommandEncoder{};
WGPUTexture targetTexture{}; // external handle
WGPUSurfaceTexture surfaceTexture{};
WGPUSurface surface{}; // external handle