thorvg/src/renderer/wg_engine/tvgWgRenderTarget.cpp
Sergii Liebodkin 96e0794a67 wg_engine: introduced scene opacity
[issues 1479: opacity](#1479)

Supported opacity value for scene

Usage example:

    //Create a Scene
    auto scene = tvg::Scene::gen();
    scene->opacity(100);

    //Prepare Circle
    auto shape1 = tvg::Shape::gen();
    shape1->appendCircle(400, 400, 250, 250);
    shape1->fill(255, 255, 0);
    shape1->opacity(100);
    scene->push(std::move(shape1));

    //Round rectangle
    auto shape2 = tvg::Shape::gen();
    shape2->appendRect(450, 100, 200, 200, 50, 50);
    shape2->fill(0, 255, 0);
    shape2->strokeWidth(10);
    shape2->strokeFill(255, 255, 255);
    scene->push(std::move(shape2));

    canvas->push(std::move(scene));
2024-04-05 17:28:34 +09:00

308 lines
13 KiB
C++

/*
* Copyright (c) 2023 - 2024 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 "tvgWgRenderTarget.h"
void WgRenderTarget::initialize(WgContext& context, uint32_t w, uint32_t h)
{
release(context);
// sampler descriptor
WGPUSamplerDescriptor samplerDesc{};
samplerDesc.nextInChain = nullptr;
samplerDesc.label = "The target sampler";
samplerDesc.addressModeU = WGPUAddressMode_ClampToEdge;
samplerDesc.addressModeV = WGPUAddressMode_ClampToEdge;
samplerDesc.addressModeW = WGPUAddressMode_ClampToEdge;
samplerDesc.magFilter = WGPUFilterMode_Nearest;
samplerDesc.minFilter = WGPUFilterMode_Nearest;
samplerDesc.mipmapFilter = WGPUMipmapFilterMode_Nearest;
samplerDesc.lodMinClamp = 0.0f;
samplerDesc.lodMaxClamp = 32.0f;
samplerDesc.compare = WGPUCompareFunction_Undefined;
samplerDesc.maxAnisotropy = 1;
sampler = wgpuDeviceCreateSampler(context.device, &samplerDesc);
assert(sampler);
// texture descriptor
WGPUTextureDescriptor textureDescColor{};
textureDescColor.nextInChain = nullptr;
textureDescColor.label = "The target texture color";
textureDescColor.usage = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopyDst | WGPUTextureUsage_StorageBinding;
textureDescColor.dimension = WGPUTextureDimension_2D;
textureDescColor.size = { w, h, 1 };
textureDescColor.format = WGPUTextureFormat_BGRA8Unorm;
textureDescColor.mipLevelCount = 1;
textureDescColor.sampleCount = 1;
textureDescColor.viewFormatCount = 0;
textureDescColor.viewFormats = nullptr;
mTextureColor = wgpuDeviceCreateTexture(context.device, &textureDescColor);
assert(mTextureColor);
// texture view descriptor
WGPUTextureViewDescriptor textureViewDescColor{};
textureViewDescColor.nextInChain = nullptr;
textureViewDescColor.label = "The target texture view color";
textureViewDescColor.format = WGPUTextureFormat_BGRA8Unorm;
textureViewDescColor.dimension = WGPUTextureViewDimension_2D;
textureViewDescColor.baseMipLevel = 0;
textureViewDescColor.mipLevelCount = 1;
textureViewDescColor.baseArrayLayer = 0;
textureViewDescColor.arrayLayerCount = 1;
textureViewDescColor.aspect = WGPUTextureAspect_All;
textureViewColor = wgpuTextureCreateView(mTextureColor, &textureViewDescColor);
assert(textureViewColor);
// stencil texture
WGPUTextureDescriptor textureDescStencil{};
textureDescStencil.nextInChain = nullptr;
textureDescStencil.label = "The target texture stencil";
textureDescStencil.usage = WGPUTextureUsage_RenderAttachment;
textureDescStencil.dimension = WGPUTextureDimension_2D;
textureDescStencil.size = { w, h, 1 };
textureDescStencil.format = WGPUTextureFormat_Stencil8;
textureDescStencil.mipLevelCount = 1;
textureDescStencil.sampleCount = 1;
textureDescStencil.viewFormatCount = 0;
textureDescStencil.viewFormats = nullptr;
mTextureStencil = wgpuDeviceCreateTexture(context.device, &textureDescStencil);
assert(mTextureStencil);
// texture view descriptor
WGPUTextureViewDescriptor textureViewDescStencil{};
textureViewDescStencil.nextInChain = nullptr;
textureViewDescStencil.label = "The target texture view stncil";
textureViewDescStencil.format = WGPUTextureFormat_Stencil8;
textureViewDescStencil.dimension = WGPUTextureViewDimension_2D;
textureViewDescStencil.baseMipLevel = 0;
textureViewDescStencil.mipLevelCount = 1;
textureViewDescStencil.baseArrayLayer = 0;
textureViewDescStencil.arrayLayerCount = 1;
textureViewDescStencil.aspect = WGPUTextureAspect_All;
textureViewStencil = wgpuTextureCreateView(mTextureStencil, &textureViewDescStencil);
assert(textureViewStencil);
// initialize bind group for blitting
bindGroupBlit.initialize(context.device, context.queue, sampler, textureViewColor);
// initialize window binding groups
WgShaderTypeMat4x4f viewMat(w, h);
mBindGroupCanvasWnd.initialize(context.device, context.queue, viewMat);
WgGeometryData geometryDataWnd;
geometryDataWnd.appendBlitBox();
mMeshDataCanvasWnd.update(context, &geometryDataWnd);
mPipelines = context.pipelines;
}
void WgRenderTarget::release(WgContext& context)
{
mMeshDataCanvasWnd.release(context);
mBindGroupCanvasWnd.release();
bindGroupBlit.release();
if (mTextureStencil) {
wgpuTextureDestroy(mTextureStencil);
wgpuTextureRelease(mTextureStencil);
mTextureStencil = nullptr;
}
if (textureViewStencil) wgpuTextureViewRelease(textureViewStencil);
textureViewStencil = nullptr;
if (mTextureColor) {
wgpuTextureDestroy(mTextureColor);
wgpuTextureRelease(mTextureColor);
mTextureColor = nullptr;
}
if (textureViewColor) wgpuTextureViewRelease(textureViewColor);
textureViewColor = nullptr;
if (sampler) wgpuSamplerRelease(sampler);
sampler = nullptr;
}
void WgRenderTarget::beginRenderPass(WGPUCommandEncoder commandEncoder, bool clear)
{
beginRenderPass(commandEncoder, textureViewColor, clear);
}
void WgRenderTarget::beginRenderPass(WGPUCommandEncoder commandEncoder, WGPUTextureView colorAttachement, bool clear)
{
assert(!mRenderPassEncoder);
// render pass depth stencil attachment
WGPURenderPassDepthStencilAttachment depthStencilAttachment{};
depthStencilAttachment.view = textureViewStencil;
depthStencilAttachment.depthLoadOp = WGPULoadOp_Clear;
depthStencilAttachment.depthStoreOp = WGPUStoreOp_Store;
depthStencilAttachment.depthClearValue = 1.0f;
depthStencilAttachment.depthReadOnly = false;
depthStencilAttachment.stencilLoadOp = WGPULoadOp_Clear;
depthStencilAttachment.stencilStoreOp = WGPUStoreOp_Store;
depthStencilAttachment.stencilClearValue = 0;
depthStencilAttachment.stencilReadOnly = false;
// render pass color attachment
WGPURenderPassColorAttachment colorAttachment{};
colorAttachment.view = colorAttachement;
colorAttachment.resolveTarget = nullptr;
colorAttachment.loadOp = clear ? WGPULoadOp_Clear : WGPULoadOp_Load;
colorAttachment.clearValue = { 0, 0, 0, 0 };
colorAttachment.storeOp = WGPUStoreOp_Store;
// render pass descriptor
WGPURenderPassDescriptor renderPassDesc{};
renderPassDesc.nextInChain = nullptr;
renderPassDesc.label = "The render pass";
renderPassDesc.colorAttachmentCount = 1;
renderPassDesc.colorAttachments = &colorAttachment;
renderPassDesc.depthStencilAttachment = &depthStencilAttachment;
//renderPassDesc.depthStencilAttachment = nullptr;
renderPassDesc.occlusionQuerySet = nullptr;
renderPassDesc.timestampWriteCount = 0;
renderPassDesc.timestampWrites = nullptr;
// begin render pass
mRenderPassEncoder = wgpuCommandEncoderBeginRenderPass(commandEncoder, &renderPassDesc);
}
void WgRenderTarget::endRenderPass()
{
assert(mRenderPassEncoder);
wgpuRenderPassEncoderEnd(mRenderPassEncoder);
wgpuRenderPassEncoderRelease(mRenderPassEncoder);
mRenderPassEncoder = nullptr;
}
void WgRenderTarget::renderShape(WgRenderDataShape* renderData)
{
assert(renderData);
assert(mRenderPassEncoder);
// draw shape geometry
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
for (uint32_t i = 0; i < renderData->meshGroupShapes.meshes.count; i++) {
// draw to stencil (first pass)
mPipelines->fillShape.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint);
renderData->meshGroupShapes.meshes[i]->draw(mRenderPassEncoder);
// fill shape (second pass)
WgRenderSettings& settings = renderData->renderSettingsShape;
if (settings.fillType == WgRenderSettingsType::Solid)
mPipelines->solid.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupSolid);
else if (settings.fillType == WgRenderSettingsType::Linear)
mPipelines->linear.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupLinear);
else if (settings.fillType == WgRenderSettingsType::Radial)
mPipelines->radial.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupRadial);
renderData->meshBBoxShapes.draw(mRenderPassEncoder);
}
}
void WgRenderTarget::renderStroke(WgRenderDataShape* renderData)
{
assert(renderData);
assert(mRenderPassEncoder);
// draw stroke geometry
if (renderData->meshGroupStrokes.meshes.count > 0) {
// draw strokes to stencil (first pass)
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 255);
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
mPipelines->fillStroke.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint);
renderData->meshGroupStrokes.meshes[i]->draw(mRenderPassEncoder);
}
// fill shape (second pass)
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
WgRenderSettings& settings = renderData->renderSettingsStroke;
if (settings.fillType == WgRenderSettingsType::Solid)
mPipelines->solid.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupSolid);
else if (settings.fillType == WgRenderSettingsType::Linear)
mPipelines->linear.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupLinear);
else if (settings.fillType == WgRenderSettingsType::Radial)
mPipelines->radial.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupRadial);
renderData->meshBBoxStrokes.draw(mRenderPassEncoder);
}
}
void WgRenderTarget::renderPicture(WgRenderDataPicture* renderData)
{
assert(renderData);
assert(mRenderPassEncoder);
if (renderData->meshData.bufferTexCoord) {
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
mPipelines->image.use(
mRenderPassEncoder,
mBindGroupCanvasWnd,
renderData->bindGroupPaint,
renderData->bindGroupPicture);
renderData->meshData.drawImage(mRenderPassEncoder);
}
}
void WgRenderTarget::blit(WgContext& context, WgRenderTarget* renderTargetSrc, WgBindGroupOpacity* mBindGroupOpacity)
{
assert(mRenderPassEncoder);
mPipelines->blit.use(mRenderPassEncoder, renderTargetSrc->bindGroupBlit, *mBindGroupOpacity);
mMeshDataCanvasWnd.drawImage(mRenderPassEncoder);
}
void WgRenderTarget::blitColor(WgContext& context, WgRenderTarget* renderTargetSrc)
{
assert(mRenderPassEncoder);
mPipelines->blitColor.use(mRenderPassEncoder, renderTargetSrc->bindGroupBlit);
mMeshDataCanvasWnd.drawImage(mRenderPassEncoder);
}
void WgRenderTarget::compose(WgContext& context, WgRenderTarget* renderTargetSrc, WgRenderTarget* renderTargetMsk, CompositeMethod method)
{
assert(mRenderPassEncoder);
WgPipelineComposition* pipeline = mPipelines->getCompositionPipeline(method);
assert(pipeline);
pipeline->use(mRenderPassEncoder, renderTargetSrc->bindGroupBlit, renderTargetMsk->bindGroupBlit);
mMeshDataCanvasWnd.drawImage(mRenderPassEncoder);
}
//*****************************************************************************
// render terget pool
//*****************************************************************************
WgRenderTarget* WgRenderTargetPool::allocate(WgContext& context, uint32_t w, uint32_t h)
{
WgRenderTarget* renderTarget{};
if (mPool.count > 0) {
renderTarget = mPool.last();
mPool.pop();
} else {
renderTarget = new WgRenderTarget;
renderTarget->initialize(context, w, h);
mList.push(renderTarget);
}
return renderTarget;
};
void WgRenderTargetPool::free(WgContext& context, WgRenderTarget* renderTarget) {
mPool.push(renderTarget);
};
void WgRenderTargetPool::release(WgContext& context)
{
for (uint32_t i = 0; i < mList.count; i++) {
mList[i]->release(context);
delete mList[i];
}
mList.clear();
mPool.clear();
};