wg_engine: Added ability to draw multiple solid color filled shapes

[issues 1479: Shape](https://github.com/thorvg/thorvg/issues/1479)

In order to build you need third party libraries. Before you start please read this: [LearnWebGPU](https://eliemichel.github.io/LearnWebGPU/getting-started/hello-webgpu.html)

Usage example:

    // init glfw
    glfwInit();

    // create a windowed mode window and its opengl context
    glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
    GLFWwindow* window = glfwCreateWindow(800, 800, "WebGPU base app", nullptr, nullptr);

    // get window size
    int width{}, height{};
    glfwGetWindowSize(window, &width, &height);

    // init engine webgpu
    tvg::Initializer::init(tvg::CanvasEngine::Wg, 0);

    // create wg canvas
    auto canvasWg = tvg::WgCanvas::gen();
    canvas_wg->target(glfwGetWin32Window(window), width, height);

    // prepare a shape (Rectangle + Rectangle + Circle + Circle)
    auto shape1 = tvg::Shape::gen();
    shape1->appendRect(0, 0, 200, 200);                //x, y, w, h
    shape1->appendRect(100, 100, 300, 300, 100, 100);  //x, y, w, h, rx, ry
    shape1->appendCircle(400, 400, 100, 100);          //cx, cy, radiusW, radiusH
    shape1->appendCircle(400, 500, 170, 100);          //cx, cy, radiusW, radiusH
    shape1->fill(255, 255, 0);                         //r, g, b

    canvas_wg->push(std::move(shape1));

    while (!glfwWindowShouldClose(window)) {
        // webgpu
        canvas_wg->draw();
        canvas_wg->sync();

        // pull events
        glfwPollEvents();
    }

    // terminate engine and window
    tvg::Initializer::term(tvg::CanvasEngine::Wg);
    glfwDestroyWindow(window);
    glfwTerminate();
This commit is contained in:
Sergii Liebodkin 2023-10-17 10:10:32 +03:00 committed by Hermet Park
parent 61081c02af
commit 14b2508cd1
15 changed files with 1517 additions and 5 deletions

View file

@ -55,6 +55,9 @@ WgCanvas::~WgCanvas()
Result WgCanvas::target(void* window, uint32_t w, uint32_t h) noexcept
{
#ifdef THORVG_WG_RASTER_SUPPORT
if (!window) return Result::InvalidArguments;
if ((w == 0) || (h == 0)) return Result::InvalidArguments;
//We know renderer type, avoid dynamic_cast for performance.
auto renderer = static_cast<WgRenderer*>(Canvas::pImpl->renderer);
if (!renderer) return Result::MemoryCorruption;

View file

@ -1,5 +1,10 @@
source_file = [
'tvgWgRenderer.cpp',
'tvgWgPipelineBase.cpp',
'tvgWgPipelineEmpty.cpp',
'tvgWgPipelineSolid.cpp',
'tvgWgRenderData.cpp',
'tvgWgRenderer.cpp',
'tvgWgShaderSrc.cpp'
]
engine_dep += [declare_dependency(

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2023 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_COMMON_H_
#define _TVG_WG_COMMON_H_
#include <cassert>
#include <webgpu/webgpu.h>
#include "tvgCommon.h"
#include "tvgRender.h"
#endif // _TVG_WG_COMMON_H_

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2023 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 "tvgWgPipelineBase.h"
/************************************************************************/
// WgPipelineData
/************************************************************************/
void WgPipelineData::updateMatrix(const float* viewMatrix, const RenderTransform* transform) {
float modelMatrix[16]{};
if (transform) {
modelMatrix[0] = transform->m.e11;
modelMatrix[1] = transform->m.e21;
modelMatrix[3] = transform->m.e31;
modelMatrix[4] = transform->m.e12;
modelMatrix[5] = transform->m.e22;
modelMatrix[7] = transform->m.e32;
modelMatrix[10] = 1.0f;
modelMatrix[12] = transform->m.e13;
modelMatrix[13] = transform->m.e23;
modelMatrix[15] = transform->m.e33;
} else {
modelMatrix[0] = 1.0f;
modelMatrix[5] = 1.0f;
modelMatrix[10] = 1.0f;
modelMatrix[15] = 1.0f;
}
// matrix multiply
for(auto i = 0; i < 4; ++i) {
for(auto j = 0; j < 4; ++j) {
float sum = 0.0;
for (auto k = 0; k < 4; ++k)
sum += viewMatrix[k*4+i] * modelMatrix[j*4+k];
uMatrix.transform[j*4+i] = sum;
}
}
}
/************************************************************************/
// WgPipelineBindGroup
/************************************************************************/
void WgPipelineBindGroup::bind(WGPURenderPassEncoder renderPassEncoder, uint32_t groupIndex) {
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, groupIndex, mBindGroup, 0, nullptr);
}
/************************************************************************/
// WgPipelinePipeline
/************************************************************************/
void WgPipelineBase::set(WGPURenderPassEncoder renderPassEncoder) {
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, mRenderPipeline);
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2023 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_PIPELINE_BASE_H_
#define _TVG_WG_PIPELINE_BASE_H_
#include "tvgWgCommon.h"
struct WgPipelineMatrix {
float transform[4*4]{};
};
struct WgPipelineData {
WgPipelineMatrix uMatrix{}; // @binding(0)
void updateMatrix(const float* viewMatrix, const RenderTransform* transform);
};
struct WgPipelineBindGroup {
WGPUBuffer uBufferMatrix{};
WGPUBindGroup mBindGroup{};
void bind(WGPURenderPassEncoder renderPassEncoder, uint32_t groupIndex);
};
class WgPipelineBase {
public:
WGPUBindGroupLayout mBindGroupLayout{}; // @group(0)
WGPUPipelineLayout mPipelineLayout{};
WGPUShaderModule mShaderModule{};
WGPURenderPipeline mRenderPipeline{};
public:
virtual void initialize(WGPUDevice device) = 0;
virtual void release() = 0;
void set(WGPURenderPassEncoder renderPassEncoder);
};
#endif // _TVG_WG_PIPELINE_BASE_H_

View file

@ -0,0 +1,243 @@
/*
* Copyright (c) 2023 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 "tvgWgPipelineEmpty.h"
#include "tvgWgShaderSrc.h"
//************************************************************************
// WgPipelineBindGroupEmpty
//************************************************************************
void WgPipelineBindGroupEmpty::initialize(WGPUDevice device, WgPipelineEmpty& pipelinePipelineEmpty) {
// buffer uniform uMatrix
WGPUBufferDescriptor bufferUniformDesc_uMatrix{};
bufferUniformDesc_uMatrix.nextInChain = nullptr;
bufferUniformDesc_uMatrix.label = "Buffer uniform pipeline empty uMatrix";
bufferUniformDesc_uMatrix.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform;
bufferUniformDesc_uMatrix.size = sizeof(WgPipelineMatrix);
bufferUniformDesc_uMatrix.mappedAtCreation = false;
uBufferMatrix = wgpuDeviceCreateBuffer(device, &bufferUniformDesc_uMatrix);
assert(uBufferMatrix);
// bind group entry @binding(0) uMatrix
WGPUBindGroupEntry bindGroupEntry_uMatrix{};
bindGroupEntry_uMatrix.nextInChain = nullptr;
bindGroupEntry_uMatrix.binding = 0;
bindGroupEntry_uMatrix.buffer = uBufferMatrix;
bindGroupEntry_uMatrix.offset = 0;
bindGroupEntry_uMatrix.size = sizeof(WgPipelineMatrix);
bindGroupEntry_uMatrix.sampler = nullptr;
bindGroupEntry_uMatrix.textureView = nullptr;
// bind group entries
WGPUBindGroupEntry bindGroupEntries[] {
bindGroupEntry_uMatrix // @binding(0) uMatrix
};
// bind group descriptor
WGPUBindGroupDescriptor bindGroupDescPipeline{};
bindGroupDescPipeline.nextInChain = nullptr;
bindGroupDescPipeline.label = "The binding group pipeline empty";
bindGroupDescPipeline.layout = pipelinePipelineEmpty.mBindGroupLayout;
bindGroupDescPipeline.entryCount = 1;
bindGroupDescPipeline.entries = bindGroupEntries;
mBindGroup = wgpuDeviceCreateBindGroup(device, &bindGroupDescPipeline);
assert(mBindGroup);
}
void WgPipelineBindGroupEmpty::release() {
if (uBufferMatrix) {
wgpuBufferDestroy(uBufferMatrix);
wgpuBufferRelease(uBufferMatrix);
uBufferMatrix = nullptr;
}
if (mBindGroup) {
wgpuBindGroupRelease(mBindGroup);
mBindGroup = nullptr;
}
}
void WgPipelineBindGroupEmpty::update(WGPUQueue queue, WgPipelineDataEmpty& pipelineDataEmpty) {
wgpuQueueWriteBuffer(queue, uBufferMatrix, 0, &pipelineDataEmpty.uMatrix, sizeof(pipelineDataEmpty.uMatrix));
}
//************************************************************************
// WgPipelineEmpty
//************************************************************************
void WgPipelineEmpty::initialize(WGPUDevice device) {
// bind group layout group 0
// bind group layout descriptor @group(0) @binding(0) uMatrix
WGPUBindGroupLayoutEntry bindGroupLayoutEntry_uMatrix{};
bindGroupLayoutEntry_uMatrix.nextInChain = nullptr;
bindGroupLayoutEntry_uMatrix.binding = 0;
bindGroupLayoutEntry_uMatrix.visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment;
bindGroupLayoutEntry_uMatrix.buffer.nextInChain = nullptr;
bindGroupLayoutEntry_uMatrix.buffer.type = WGPUBufferBindingType_Uniform;
bindGroupLayoutEntry_uMatrix.buffer.hasDynamicOffset = false;
bindGroupLayoutEntry_uMatrix.buffer.minBindingSize = 0;
// bind group layout entries @group(0)
WGPUBindGroupLayoutEntry bindGroupLayoutEntries[] {
bindGroupLayoutEntry_uMatrix
};
// bind group layout descriptor scene @group(0)
WGPUBindGroupLayoutDescriptor bindGroupLayoutDesc{};
bindGroupLayoutDesc.nextInChain = nullptr;
bindGroupLayoutDesc.label = "Bind group layout pipeline empty";
bindGroupLayoutDesc.entryCount = 1;
bindGroupLayoutDesc.entries = bindGroupLayoutEntries; // @binding
mBindGroupLayout = wgpuDeviceCreateBindGroupLayout(device, &bindGroupLayoutDesc);
assert(mBindGroupLayout);
// pipeline layout
// bind group layout descriptors
WGPUBindGroupLayout mBindGroupLayouts[] {
mBindGroupLayout
};
// pipeline layout descriptor
WGPUPipelineLayoutDescriptor pipelineLayoutDesc{};
pipelineLayoutDesc.nextInChain = nullptr;
pipelineLayoutDesc.label = "Pipeline pipeline layout empty";
pipelineLayoutDesc.bindGroupLayoutCount = 1;
pipelineLayoutDesc.bindGroupLayouts = mBindGroupLayouts;
mPipelineLayout = wgpuDeviceCreatePipelineLayout(device, &pipelineLayoutDesc);
assert(mPipelineLayout);
// depth stencil state
WGPUDepthStencilState depthStencilState{};
depthStencilState.nextInChain = nullptr;
depthStencilState.format = WGPUTextureFormat_Stencil8;
depthStencilState.depthWriteEnabled = false;
depthStencilState.depthCompare = WGPUCompareFunction_Always;
// depthStencilState.stencilFront
depthStencilState.stencilFront.compare = WGPUCompareFunction_Always;
depthStencilState.stencilFront.failOp = WGPUStencilOperation_Invert;
depthStencilState.stencilFront.depthFailOp = WGPUStencilOperation_Invert;
depthStencilState.stencilFront.passOp = WGPUStencilOperation_Invert;
// depthStencilState.stencilBack
depthStencilState.stencilBack.compare = WGPUCompareFunction_Always;
depthStencilState.stencilBack.failOp = WGPUStencilOperation_Invert;
depthStencilState.stencilBack.depthFailOp = WGPUStencilOperation_Invert;
depthStencilState.stencilBack.passOp = WGPUStencilOperation_Invert;
// stencil mask
depthStencilState.stencilReadMask = 0xFFFFFFFF;
depthStencilState.stencilWriteMask = 0xFFFFFFFF;
// depth bias
depthStencilState.depthBias = 0;
depthStencilState.depthBiasSlopeScale = 0.0f;
depthStencilState.depthBiasClamp = 0.0f;
// shader module wgsl descriptor
WGPUShaderModuleWGSLDescriptor shaderModuleWGSLDesc{};
shaderModuleWGSLDesc.chain.next = nullptr;
shaderModuleWGSLDesc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
shaderModuleWGSLDesc.code = cShaderSource_PipelineEmpty;
// shader module descriptor
WGPUShaderModuleDescriptor shaderModuleDesc{};
shaderModuleDesc.nextInChain = &shaderModuleWGSLDesc.chain;
shaderModuleDesc.label = "The shader module pipeline empty";
shaderModuleDesc.hintCount = 0;
shaderModuleDesc.hints = nullptr;
mShaderModule = wgpuDeviceCreateShaderModule(device, &shaderModuleDesc);
assert(mShaderModule);
// vertex attributes
WGPUVertexAttribute vertexAttributes[] = {
{ WGPUVertexFormat_Float32x3, sizeof(float) * 0, 0 }, // position
};
// vertex buffer layout
WGPUVertexBufferLayout vertexBufferLayout{};
vertexBufferLayout.arrayStride = sizeof(float) * 3; // position
vertexBufferLayout.stepMode = WGPUVertexStepMode_Vertex;
vertexBufferLayout.attributeCount = 1; // position
vertexBufferLayout.attributes = vertexAttributes;
// blend state
WGPUBlendState blendState{};
// blendState.color
blendState.color.operation = WGPUBlendOperation_Add;
blendState.color.srcFactor = WGPUBlendFactor_SrcAlpha;
blendState.color.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha;
// blendState.alpha
blendState.alpha.operation = WGPUBlendOperation_Add;
blendState.alpha.srcFactor = WGPUBlendFactor_Zero;
blendState.alpha.dstFactor = WGPUBlendFactor_One;
// color target state (WGPUTextureFormat_BGRA8UnormSrgb)
WGPUColorTargetState colorTargetState0{};
colorTargetState0.nextInChain = nullptr;
colorTargetState0.format = WGPUTextureFormat_BGRA8Unorm;
//colorTargetState0.format = WGPUTextureFormat_BGRA8UnormSrgb;
colorTargetState0.blend = &blendState;
colorTargetState0.writeMask = WGPUColorWriteMask_All;
// color target states
WGPUColorTargetState colorTargetStates[] = {
colorTargetState0
};
// fragmanet state
WGPUFragmentState fragmentState{};
fragmentState.nextInChain = nullptr;
fragmentState.module = mShaderModule;
fragmentState.entryPoint = "fs_main";
fragmentState.constantCount = 0;
fragmentState.constants = nullptr;
fragmentState.targetCount = 1;
fragmentState.targets = colorTargetStates; // render target index
// render pipeline descriptor
WGPURenderPipelineDescriptor renderPipelineDesc{};
renderPipelineDesc.nextInChain = nullptr;
renderPipelineDesc.label = "Render pipeline pipeline empty";
// renderPipelineDesc.layout
renderPipelineDesc.layout = mPipelineLayout;
// renderPipelineDesc.vertex
renderPipelineDesc.vertex.nextInChain = nullptr;
renderPipelineDesc.vertex.module = mShaderModule;
renderPipelineDesc.vertex.entryPoint = "vs_main";
renderPipelineDesc.vertex.constantCount = 0;
renderPipelineDesc.vertex.constants = nullptr;
renderPipelineDesc.vertex.bufferCount = 1;
renderPipelineDesc.vertex.buffers = &vertexBufferLayout; // buffer index
// renderPipelineDesc.primitive
renderPipelineDesc.primitive.nextInChain = nullptr;
renderPipelineDesc.primitive.topology = WGPUPrimitiveTopology_TriangleList;
renderPipelineDesc.primitive.stripIndexFormat = WGPUIndexFormat_Undefined;
renderPipelineDesc.primitive.frontFace = WGPUFrontFace_CCW;
renderPipelineDesc.primitive.cullMode = WGPUCullMode_None;
// renderPipelineDesc.depthStencil
renderPipelineDesc.depthStencil = &depthStencilState;
// renderPipelineDesc.multisample
renderPipelineDesc.multisample.nextInChain = nullptr;
renderPipelineDesc.multisample.count = 1;
renderPipelineDesc.multisample.mask = 0xFFFFFFFF;
renderPipelineDesc.multisample.alphaToCoverageEnabled = false;
// renderPipelineDesc.fragment
renderPipelineDesc.fragment = &fragmentState;
mRenderPipeline = wgpuDeviceCreateRenderPipeline(device, &renderPipelineDesc);
assert(mRenderPipeline);
}
void WgPipelineEmpty::release() {
wgpuRenderPipelineRelease(mRenderPipeline);
wgpuShaderModuleRelease(mShaderModule);
wgpuPipelineLayoutRelease(mPipelineLayout);
wgpuBindGroupLayoutRelease(mBindGroupLayout);
}

View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 2023 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_PIPELINE_EMPTY_H_
#define _TVG_WG_PIPELINE_EMPTY_H_
#include "tvgWgPipelineBase.h"
class WgPipelineEmpty;
struct WgPipelineDataEmpty: WgPipelineData {};
class WgPipelineBindGroupEmpty: public WgPipelineBindGroup {
public:
void initialize(WGPUDevice device, WgPipelineEmpty& pipelinePipelineEmpty);
void release();
void update(WGPUQueue mQueue, WgPipelineDataEmpty& pipelineDataSolid);
};
/*
* This pipeline is used for drawing filled, concave polygons using the stencil buffer
* This can be done using the stencil buffer, with a two-pass algorithm.
*
* First, clear the stencil buffer and disable writing into the color buffer. Next, draw each of the triangles in turn, using the INVERT function in the stencil buffer. (For best performance, use triangle fans.)
* This flips the value between zero and a nonzero value every time a triangle is drawn that covers a pixel.
*
* After all the triangles are drawn, if a pixel is covered an even number of times, the value in the stencil buffers is zero; otherwise, it's nonzero.
* Finally, draw a large polygon over the whole region (or redraw the triangles), but allow drawing only where the stencil buffer is nonzero.
*
* There is a link to the solution, how to draw filled, concave polygons using the stencil buffer:
* https://www.glprogramming.com/red/chapter14.html#name13
*
* The benefit of this solution is to don`t use complex tesselation to fill self intersected or concave poligons.
*
* This pipeline implements the first pass of this solution. It`s did not write anything into color buffer but fills the stencil buffer using invert strategy
*/
class WgPipelineEmpty: public WgPipelineBase {
public:
void initialize(WGPUDevice device) override;
void release() override;
};
#endif // _TVG_WG_PIPELINE_EMPTY_H_

View file

@ -0,0 +1,289 @@
/*
* Copyright (c) 2023 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 "tvgWgPipelineSolid.h"
#include "tvgWgShaderSrc.h"
//************************************************************************
// WgPipelineDataSolid
//************************************************************************
void WgPipelineDataSolid::updateColor(const RenderShape& renderShape) {
uColorInfo.color[0] = renderShape.color[0] / 255.0f; // red
uColorInfo.color[1] = renderShape.color[1] / 255.0f; // green
uColorInfo.color[2] = renderShape.color[2] / 255.0f; // blue
uColorInfo.color[3] = renderShape.color[3] / 255.0f; // alpha
}
//************************************************************************
// WgPipelineBindGroupSolid
//************************************************************************
void WgPipelineBindGroupSolid::initialize(WGPUDevice device, WgPipelineSolid& pipelinePipelineSolid) {
// buffer uniform uMatrix
WGPUBufferDescriptor bufferUniformDesc_uMatrix{};
bufferUniformDesc_uMatrix.nextInChain = nullptr;
bufferUniformDesc_uMatrix.label = "Buffer uniform pipeline solid uMatrix";
bufferUniformDesc_uMatrix.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform;
bufferUniformDesc_uMatrix.size = sizeof(WgPipelineMatrix);
bufferUniformDesc_uMatrix.mappedAtCreation = false;
uBufferMatrix = wgpuDeviceCreateBuffer(device, &bufferUniformDesc_uMatrix);
assert(uBufferMatrix);
// buffer uniform uColorInfo
WGPUBufferDescriptor bufferUniformDesc_uColorInfo{};
bufferUniformDesc_uColorInfo.nextInChain = nullptr;
bufferUniformDesc_uColorInfo.label = "Buffer uniform pipeline solid uColorInfo";
bufferUniformDesc_uColorInfo.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform;
bufferUniformDesc_uColorInfo.size = sizeof(WgPipelineSolidColorInfo);
bufferUniformDesc_uColorInfo.mappedAtCreation = false;
uBufferColorInfo = wgpuDeviceCreateBuffer(device, &bufferUniformDesc_uColorInfo);
assert(uBufferColorInfo);
// bind group entry @binding(0) uMatrix
WGPUBindGroupEntry bindGroupEntry_uMatrix{};
bindGroupEntry_uMatrix.nextInChain = nullptr;
bindGroupEntry_uMatrix.binding = 0;
bindGroupEntry_uMatrix.buffer = uBufferMatrix;
bindGroupEntry_uMatrix.offset = 0;
bindGroupEntry_uMatrix.size = sizeof(WgPipelineMatrix);
bindGroupEntry_uMatrix.sampler = nullptr;
bindGroupEntry_uMatrix.textureView = nullptr;
// bind group entry @binding(1) uColorInfo
WGPUBindGroupEntry bindGroupEntry_uColorInfo{};
bindGroupEntry_uColorInfo.nextInChain = nullptr;
bindGroupEntry_uColorInfo.binding = 1;
bindGroupEntry_uColorInfo.buffer = uBufferColorInfo;
bindGroupEntry_uColorInfo.offset = 0;
bindGroupEntry_uColorInfo.size = sizeof(WgPipelineSolidColorInfo);
bindGroupEntry_uColorInfo.sampler = nullptr;
bindGroupEntry_uColorInfo.textureView = nullptr;
// bind group entries
WGPUBindGroupEntry bindGroupEntries[] {
bindGroupEntry_uMatrix, // @binding(0) uMatrix
bindGroupEntry_uColorInfo // @binding(1) uColorInfo
};
// bind group descriptor
WGPUBindGroupDescriptor bindGroupDescPipeline{};
bindGroupDescPipeline.nextInChain = nullptr;
bindGroupDescPipeline.label = "The binding group pipeline solid";
bindGroupDescPipeline.layout = pipelinePipelineSolid.mBindGroupLayout;
bindGroupDescPipeline.entryCount = 2;
bindGroupDescPipeline.entries = bindGroupEntries;
mBindGroup = wgpuDeviceCreateBindGroup(device, &bindGroupDescPipeline);
assert(mBindGroup);
}
void WgPipelineBindGroupSolid::release() {
if (uBufferColorInfo) {
wgpuBufferDestroy(uBufferColorInfo);
wgpuBufferRelease(uBufferColorInfo);
uBufferColorInfo = nullptr;
}
if (uBufferMatrix) {
wgpuBufferDestroy(uBufferMatrix);
wgpuBufferRelease(uBufferMatrix);
uBufferMatrix = nullptr;
}
if (mBindGroup) {
wgpuBindGroupRelease(mBindGroup);
mBindGroup = nullptr;
}
}
void WgPipelineBindGroupSolid::update(WGPUQueue queue, WgPipelineDataSolid& pipelineDataSolid) {
wgpuQueueWriteBuffer(queue, uBufferMatrix, 0, &pipelineDataSolid.uMatrix, sizeof(pipelineDataSolid.uMatrix));
wgpuQueueWriteBuffer(queue, uBufferColorInfo, 0, &pipelineDataSolid.uColorInfo, sizeof(pipelineDataSolid.uColorInfo));
}
//************************************************************************
// WgPipelineSolid
//************************************************************************
void WgPipelineSolid::initialize(WGPUDevice device) {
// bind group layout group 0
// bind group layout descriptor @group(0) @binding(0) uMatrix
WGPUBindGroupLayoutEntry bindGroupLayoutEntry_uMatrix{};
bindGroupLayoutEntry_uMatrix.nextInChain = nullptr;
bindGroupLayoutEntry_uMatrix.binding = 0;
bindGroupLayoutEntry_uMatrix.visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment;
bindGroupLayoutEntry_uMatrix.buffer.nextInChain = nullptr;
bindGroupLayoutEntry_uMatrix.buffer.type = WGPUBufferBindingType_Uniform;
bindGroupLayoutEntry_uMatrix.buffer.hasDynamicOffset = false;
bindGroupLayoutEntry_uMatrix.buffer.minBindingSize = 0;
// bind group layout descriptor @group(0) @binding(1) uColorInfo
WGPUBindGroupLayoutEntry bindGroupLayoutEntry_uColorInfo{};
bindGroupLayoutEntry_uColorInfo.nextInChain = nullptr;
bindGroupLayoutEntry_uColorInfo.binding = 1;
bindGroupLayoutEntry_uColorInfo.visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment;
bindGroupLayoutEntry_uColorInfo.buffer.nextInChain = nullptr;
bindGroupLayoutEntry_uColorInfo.buffer.type = WGPUBufferBindingType_Uniform;
bindGroupLayoutEntry_uColorInfo.buffer.hasDynamicOffset = false;
bindGroupLayoutEntry_uColorInfo.buffer.minBindingSize = 0;
// bind group layout entries @group(0)
WGPUBindGroupLayoutEntry bindGroupLayoutEntries[] {
bindGroupLayoutEntry_uMatrix,
bindGroupLayoutEntry_uColorInfo
};
// bind group layout descriptor scene @group(0)
WGPUBindGroupLayoutDescriptor bindGroupLayoutDesc{};
bindGroupLayoutDesc.nextInChain = nullptr;
bindGroupLayoutDesc.label = "Bind group layout pipeline solid";
bindGroupLayoutDesc.entryCount = 2;
bindGroupLayoutDesc.entries = bindGroupLayoutEntries; // @binding
mBindGroupLayout = wgpuDeviceCreateBindGroupLayout(device, &bindGroupLayoutDesc);
assert(mBindGroupLayout);
// pipeline layout
// bind group layout descriptors
WGPUBindGroupLayout mBindGroupLayouts[] {
mBindGroupLayout
};
// pipeline layout descriptor
WGPUPipelineLayoutDescriptor pipelineLayoutDesc{};
pipelineLayoutDesc.nextInChain = nullptr;
pipelineLayoutDesc.label = "Pipeline pipeline layout solid";
pipelineLayoutDesc.bindGroupLayoutCount = 1;
pipelineLayoutDesc.bindGroupLayouts = mBindGroupLayouts;
mPipelineLayout = wgpuDeviceCreatePipelineLayout(device, &pipelineLayoutDesc);
assert(mPipelineLayout);
// depth stencil state
WGPUDepthStencilState depthStencilState{};
depthStencilState.nextInChain = nullptr;
depthStencilState.format = WGPUTextureFormat_Stencil8;
depthStencilState.depthWriteEnabled = false;
depthStencilState.depthCompare = WGPUCompareFunction_Always;
// depthStencilState.stencilFront
depthStencilState.stencilFront.compare = WGPUCompareFunction_NotEqual;
depthStencilState.stencilFront.failOp = WGPUStencilOperation_Zero;
depthStencilState.stencilFront.depthFailOp = WGPUStencilOperation_Zero;
depthStencilState.stencilFront.passOp = WGPUStencilOperation_Zero;
// depthStencilState.stencilBack
depthStencilState.stencilBack.compare = WGPUCompareFunction_NotEqual;
depthStencilState.stencilBack.failOp = WGPUStencilOperation_Zero;
depthStencilState.stencilBack.depthFailOp = WGPUStencilOperation_Zero;
depthStencilState.stencilBack.passOp = WGPUStencilOperation_Zero;
// stencil mask
depthStencilState.stencilReadMask = 0xFFFFFFFF;
depthStencilState.stencilWriteMask = 0xFFFFFFFF;
// depth bias
depthStencilState.depthBias = 0;
depthStencilState.depthBiasSlopeScale = 0.0f;
depthStencilState.depthBiasClamp = 0.0f;
// shader module wgsl descriptor
WGPUShaderModuleWGSLDescriptor shaderModuleWGSLDesc{};
shaderModuleWGSLDesc.chain.next = nullptr;
shaderModuleWGSLDesc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
shaderModuleWGSLDesc.code = cShaderSource_PipelineSolid;
// shader module descriptor
WGPUShaderModuleDescriptor shaderModuleDesc{};
shaderModuleDesc.nextInChain = &shaderModuleWGSLDesc.chain;
shaderModuleDesc.label = "The shader module pipeline solid";
shaderModuleDesc.hintCount = 0;
shaderModuleDesc.hints = nullptr;
mShaderModule = wgpuDeviceCreateShaderModule(device, &shaderModuleDesc);
assert(mShaderModule);
// vertex attributes
WGPUVertexAttribute vertexAttributes[] = {
{ WGPUVertexFormat_Float32x3, sizeof(float) * 0, 0 }, // position
};
// vertex buffer layout
WGPUVertexBufferLayout vertexBufferLayout{};
vertexBufferLayout.arrayStride = sizeof(float) * 3; // position
vertexBufferLayout.stepMode = WGPUVertexStepMode_Vertex;
vertexBufferLayout.attributeCount = 1; // position
vertexBufferLayout.attributes = vertexAttributes;
// blend state
WGPUBlendState blendState{};
// blendState.color
blendState.color.operation = WGPUBlendOperation_Add;
blendState.color.srcFactor = WGPUBlendFactor_SrcAlpha;
blendState.color.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha;
// blendState.alpha
blendState.alpha.operation = WGPUBlendOperation_Add;
blendState.alpha.srcFactor = WGPUBlendFactor_Zero;
blendState.alpha.dstFactor = WGPUBlendFactor_One;
// color target state (WGPUTextureFormat_BGRA8UnormSrgb)
WGPUColorTargetState colorTargetState0{};
colorTargetState0.nextInChain = nullptr;
colorTargetState0.format = WGPUTextureFormat_BGRA8Unorm;
//colorTargetState0.format = WGPUTextureFormat_BGRA8UnormSrgb;
colorTargetState0.blend = &blendState;
colorTargetState0.writeMask = WGPUColorWriteMask_All;
// color target states
WGPUColorTargetState colorTargetStates[] = {
colorTargetState0
};
// fragmanet state
WGPUFragmentState fragmentState{};
fragmentState.nextInChain = nullptr;
fragmentState.module = mShaderModule;
fragmentState.entryPoint = "fs_main";
fragmentState.constantCount = 0;
fragmentState.constants = nullptr;
fragmentState.targetCount = 1;
fragmentState.targets = colorTargetStates; // render target index
// render pipeline descriptor
WGPURenderPipelineDescriptor renderPipelineDesc{};
renderPipelineDesc.nextInChain = nullptr;
renderPipelineDesc.label = "Render pipeline pipeline solid";
// renderPipelineDesc.layout
renderPipelineDesc.layout = mPipelineLayout;
// renderPipelineDesc.vertex
renderPipelineDesc.vertex.nextInChain = nullptr;
renderPipelineDesc.vertex.module = mShaderModule;
renderPipelineDesc.vertex.entryPoint = "vs_main";
renderPipelineDesc.vertex.constantCount = 0;
renderPipelineDesc.vertex.constants = nullptr;
renderPipelineDesc.vertex.bufferCount = 1;
renderPipelineDesc.vertex.buffers = &vertexBufferLayout; // buffer index
// renderPipelineDesc.primitive
renderPipelineDesc.primitive.nextInChain = nullptr;
renderPipelineDesc.primitive.topology = WGPUPrimitiveTopology_TriangleList;
renderPipelineDesc.primitive.stripIndexFormat = WGPUIndexFormat_Undefined;
renderPipelineDesc.primitive.frontFace = WGPUFrontFace_CCW;
renderPipelineDesc.primitive.cullMode = WGPUCullMode_None;
// renderPipelineDesc.depthStencil
renderPipelineDesc.depthStencil = &depthStencilState;
// renderPipelineDesc.multisample
renderPipelineDesc.multisample.nextInChain = nullptr;
renderPipelineDesc.multisample.count = 1;
renderPipelineDesc.multisample.mask = 0xFFFFFFFF;
renderPipelineDesc.multisample.alphaToCoverageEnabled = false;
// renderPipelineDesc.fragment
renderPipelineDesc.fragment = &fragmentState;
mRenderPipeline = wgpuDeviceCreateRenderPipeline(device, &renderPipelineDesc);
assert(mRenderPipeline);
}
void WgPipelineSolid::release() {
wgpuRenderPipelineRelease(mRenderPipeline);
wgpuShaderModuleRelease(mShaderModule);
wgpuPipelineLayoutRelease(mPipelineLayout);
wgpuBindGroupLayoutRelease(mBindGroupLayout);
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2023 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_PIPELINE_SOLID_H_
#define _TVG_WG_PIPELINE_SOLID_H_
#include "tvgWgPipelineBase.h"
class WgPipelineSolid;
struct WgPipelineSolidColorInfo {
float color[4]{};
};
struct WgPipelineDataSolid: WgPipelineData {
WgPipelineSolidColorInfo uColorInfo{}; // @binding(1)
void updateColor(const RenderShape& renderShape);
};
class WgPipelineBindGroupSolid: public WgPipelineBindGroup {
private:
WGPUBuffer uBufferColorInfo{}; // @binding(1)
public:
void initialize(WGPUDevice device, WgPipelineSolid& pipelinePipelineSolid);
void release();
void update(WGPUQueue mQueue, WgPipelineDataSolid& pipelineDataSolid);
};
class WgPipelineSolid: public WgPipelineBase {
public:
void initialize(WGPUDevice device) override;
void release() override;
};
#endif //_TVG_WG_PIPELINE_SOLID_H_

View file

@ -0,0 +1,176 @@
/*
* Copyright (c) 2023 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 "tvgWgRenderData.h"
//***********************************************************************
// WgGeometryData
//***********************************************************************
void WgGeometryData::draw(WGPURenderPassEncoder renderPassEncoder) {
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, mBufferVertex, 0, mVertexCount * sizeof(float) * 3);
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, mBufferIndex, WGPUIndexFormat_Uint32, 0, mIndexCount * sizeof(uint32_t));
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, mIndexCount, 1, 0, 0, 0);
}
void WgGeometryData::update(WGPUDevice device, WGPUQueue queue, float* vertexData, size_t vertexCount, uint32_t* indexData, size_t indexCount) {
release();
// buffer vertex data create and write
WGPUBufferDescriptor bufferVertexDesc{};
bufferVertexDesc.nextInChain = nullptr;
bufferVertexDesc.label = "Buffer vertex geometry data";
bufferVertexDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex;
bufferVertexDesc.size = sizeof(float) * vertexCount * 3; // x, y, z
bufferVertexDesc.mappedAtCreation = false;
mBufferVertex = wgpuDeviceCreateBuffer(device, &bufferVertexDesc);
assert(mBufferVertex);
wgpuQueueWriteBuffer(queue, mBufferVertex, 0, vertexData, sizeof(float) * vertexCount * 3);
mVertexCount = vertexCount;
// buffer index data create and write
WGPUBufferDescriptor bufferIndexDesc{};
bufferIndexDesc.nextInChain = nullptr;
bufferIndexDesc.label = "Buffer index geometry data";
bufferIndexDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index;
bufferIndexDesc.size = sizeof(uint32_t) * indexCount;
bufferIndexDesc.mappedAtCreation = false;
mBufferIndex = wgpuDeviceCreateBuffer(device, &bufferIndexDesc);
assert(mBufferIndex);
wgpuQueueWriteBuffer(queue, mBufferIndex, 0, indexData, sizeof(uint32_t) * indexCount);
mIndexCount = indexCount;
}
void WgGeometryData::release() {
if (mBufferIndex) {
wgpuBufferDestroy(mBufferIndex);
wgpuBufferRelease(mBufferIndex);
mBufferIndex = nullptr;
mVertexCount = 0;
}
if (mBufferVertex) {
wgpuBufferDestroy(mBufferVertex);
wgpuBufferRelease(mBufferVertex);
mBufferVertex = nullptr;
mIndexCount = 0;
}
}
//***********************************************************************
// WgRenderDataShape
//***********************************************************************
void WgRenderDataShape::release() {
releaseRenderData();
mPipelineBindGroupSolid.release();
}
void WgRenderDataShape::releaseRenderData() {
for (uint32_t i = 0; i < mGeometryDataFill.count; i++) {
mGeometryDataFill[i]->release();
delete mGeometryDataFill[i];
}
mGeometryDataFill.clear();
}
void WgRenderDataShape::tesselate(WGPUDevice device, WGPUQueue queue, const RenderShape& rshape) {
releaseRenderData();
Array<Array<float>*> outlines;
size_t pntIndex = 0;
for (uint32_t cmdIndex = 0; cmdIndex < rshape.path.cmds.count; cmdIndex++) {
PathCommand cmd = rshape.path.cmds[cmdIndex];
if (cmd == PathCommand::MoveTo) {
outlines.push(new Array<float>);
auto outline = outlines.last();
outline->push(rshape.path.pts[pntIndex].x);
outline->push(rshape.path.pts[pntIndex].y);
outline->push(0.0f);
pntIndex++;
} else if (cmd == PathCommand::LineTo) {
auto outline = outlines.last();
outline->push(rshape.path.pts[pntIndex].x);
outline->push(rshape.path.pts[pntIndex].y);
outline->push(0.0f);
pntIndex++;
} else if (cmd == PathCommand::Close) {
auto outline = outlines.last();
if ((outline) && (outline->count > 1)) {
outline->push(outline->data[0]);
outline->push(outline->data[1]);
outline->push(0.0f);
}
} else if (cmd == PathCommand::CubicTo) {
auto outline = outlines.last();
Point p0 = { outline->data[outline->count - 3], outline->data[outline->count - 2] };
Point p1 = { rshape.path.pts[pntIndex + 0].x, rshape.path.pts[pntIndex + 0].y };
Point p2 = { rshape.path.pts[pntIndex + 1].x, rshape.path.pts[pntIndex + 1].y };
Point p3 = { rshape.path.pts[pntIndex + 2].x, rshape.path.pts[pntIndex + 2].y };
const size_t segs = 16;
for (size_t i = 1; i <= segs; i++) {
float t = i / (float)segs;
// get cubic spline interpolation coefficients
float t0 = 1 * (1.0f - t) * (1.0f - t) * (1.0f - t);
float t1 = 3 * (1.0f - t) * (1.0f - t) * t;
float t2 = 3 * (1.0f - t) * t * t;
float t3 = 1 * t * t * t;
outline->push(p0.x * t0 + p1.x * t1 + p2.x * t2 + p3.x * t3);
outline->push(p0.y * t0 + p1.y * t1 + p2.y * t2 + p3.y * t3);
outline->push(0.0f);
}
pntIndex += 3;
}
}
// create render index buffers to emulate triangle fan polygon
Array<uint32_t> indexBuffer;
for (size_t i = 0; i < outlines.count; i++) {
auto outline = outlines[i];
size_t vertexCount = outline->count / 3;
assert(vertexCount > 2);
indexBuffer.clear();
indexBuffer.reserve((vertexCount - 2) * 3);
for (size_t j = 0; j < vertexCount - 2; j++) {
indexBuffer.push(0);
indexBuffer.push(j + 1);
indexBuffer.push(j + 2);
}
WgGeometryData* geometryData = new WgGeometryData();
geometryData->initialize(device);
geometryData->update(device, queue, outline->data, vertexCount, indexBuffer.data, indexBuffer.count);
mGeometryDataFill.push(geometryData);
}
for (size_t i = 0; i < outlines.count; i++)
delete outlines[i];
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2023 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 "tvgWgPipelineSolid.h"
#ifndef _TVG_WG_RENDER_DATA_H_
#define _TVG_WG_RENDER_DATA_H_
class WgGeometryData {
public:
WGPUBuffer mBufferVertex{};
WGPUBuffer mBufferIndex{};
size_t mVertexCount{};
size_t mIndexCount{};
public:
WgGeometryData() {}
virtual ~WgGeometryData() { release(); }
void initialize(WGPUDevice device) {};
void draw(WGPURenderPassEncoder renderPassEncoder);
void update(WGPUDevice device, WGPUQueue queue, float* vertexData, size_t vertexCount, uint32_t* indexData, size_t indexCount);
void release();
};
class WgRenderData {
public:
virtual void initialize(WGPUDevice device) {};
virtual void release() = 0;
};
class WgRenderDataShape: public WgRenderData {
public:
Array<WgGeometryData*> mGeometryDataFill;
Array<WgGeometryData*> mGeometryDataStroke;
WgPipelineBindGroupSolid mPipelineBindGroupSolid{};
WgPipelineBase* mPipelineBase{}; // external
WgPipelineBindGroup* mPipelineBindGroup{}; // external
public:
WgRenderDataShape() {}
void release() override;
void releaseRenderData();
void tesselate(WGPUDevice device, WGPUQueue queue, const RenderShape& rshape);
};
#endif //_TVG_WG_RENDER_DATA_H_

View file

@ -22,6 +22,14 @@
#include "tvgWgRenderer.h"
#include <iostream>
#ifdef _WIN32
// TODO: cross-platform realization
#include <windows.h>
#endif
#include "tvgWgRenderData.h"
#include "tvgWgShaderSrc.h"
WgRenderer::WgRenderer() {
initialize();
}
@ -31,15 +39,117 @@ WgRenderer::~WgRenderer() {
}
void WgRenderer::initialize() {
TVGERR("WG_ENGINE", "TODO: WgRenderer::initialize()");
// create instance
WGPUInstanceDescriptor instanceDesc{};
instanceDesc.nextInChain = nullptr;
mInstance = wgpuCreateInstance(&instanceDesc);
assert(mInstance);
// request adapter options
WGPURequestAdapterOptions requestAdapterOptions{};
requestAdapterOptions.nextInChain = nullptr;
requestAdapterOptions.compatibleSurface = nullptr;
requestAdapterOptions.powerPreference = WGPUPowerPreference_HighPerformance;
requestAdapterOptions.forceFallbackAdapter = false;
// on adapter request ended function
auto onAdapterRequestEnded = [](WGPURequestAdapterStatus status, WGPUAdapter adapter, char const * message, void * pUserData) {
if (status != WGPURequestAdapterStatus_Success)
TVGERR("WG_RENDERER", "Adapter request: %s", message);
*((WGPUAdapter*)pUserData) = adapter;
};
// request adapter
wgpuInstanceRequestAdapter(mInstance, &requestAdapterOptions, onAdapterRequestEnded, &mAdapter);
assert(mAdapter);
// adapter enumarate fueatures
WGPUFeatureName featureNames[32]{};
size_t featuresCount = wgpuAdapterEnumerateFeatures(mAdapter, featureNames);
WGPUAdapterProperties adapterProperties{};
wgpuAdapterGetProperties(mAdapter, &adapterProperties);
WGPUSupportedLimits supportedLimits{};
wgpuAdapterGetLimits(mAdapter, &supportedLimits);
// reguest device
WGPUDeviceDescriptor deviceDesc{};
deviceDesc.nextInChain = nullptr;
deviceDesc.label = "The device";
deviceDesc.requiredFeaturesCount = featuresCount;
deviceDesc.requiredFeatures = featureNames;
deviceDesc.requiredLimits = nullptr;
deviceDesc.defaultQueue.nextInChain = nullptr;
deviceDesc.defaultQueue.label = "The default queue";
deviceDesc.deviceLostCallback = nullptr;
deviceDesc.deviceLostUserdata = nullptr;
// on device request ended function
auto onDeviceRequestEnded = [](WGPURequestDeviceStatus status, WGPUDevice device, char const * message, void * pUserData) {
if (status != WGPURequestDeviceStatus_Success)
TVGERR("WG_RENDERER", "Device request: %s", message);
*((WGPUDevice*)pUserData) = device;
};
// request device
wgpuAdapterRequestDevice(mAdapter, &deviceDesc, onDeviceRequestEnded, &mDevice);
assert(mDevice);
// on device error function
auto onDeviceError = [](WGPUErrorType type, char const* message, void* pUserData) {
TVGERR("WG_RENDERER", "Uncaptured device error: %s", message);
};
// set device error handling
wgpuDeviceSetUncapturedErrorCallback(mDevice, onDeviceError, nullptr);
mQueue = wgpuDeviceGetQueue(mDevice);
assert(mQueue);
// create pipelines
mPipelineEmpty.initialize(mDevice);
mPipelineSolid.initialize(mDevice);
mPipelineBindGroupEmpty.initialize(mDevice, mPipelineEmpty);
mGeometryDataPipeline.initialize(mDevice);
}
void WgRenderer::release() {
TVGERR("WG_ENGINE", "TODO: WgRenderer::release()");
if (mStencilTex) {
wgpuTextureDestroy(mStencilTex);
wgpuTextureRelease(mStencilTex);
}
if (mStencilTexView) wgpuTextureViewRelease(mStencilTexView);
if (mSwapChain) wgpuSwapChainRelease(mSwapChain);
if (mSurface) wgpuSurfaceRelease(mSurface);
mGeometryDataPipeline.release();
mPipelineBindGroupEmpty.release();
mPipelineSolid.release();
mPipelineEmpty.release();
if (mDevice) {
wgpuDeviceDestroy(mDevice);
wgpuDeviceRelease(mDevice);
}
if (mAdapter) wgpuAdapterRelease(mAdapter);
if (mInstance) wgpuInstanceRelease(mInstance);
}
RenderData WgRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) {
return nullptr;
// get or create render data shape
auto renderDataShape = (WgRenderDataShape*)data;
if (!renderDataShape) {
renderDataShape = new WgRenderDataShape();
renderDataShape->initialize(mDevice);
renderDataShape->mPipelineBindGroupSolid.initialize(mDevice, mPipelineSolid);
}
if (flags & RenderUpdateFlag::Path)
renderDataShape->tesselate(mDevice, mQueue, rshape);
// setup solid fill properties
if ((flags & (RenderUpdateFlag::Color | RenderUpdateFlag::Transform)) && (!rshape.fill)) {
WgPipelineDataSolid pipelineDataSolid{};
pipelineDataSolid.updateMatrix(mViewMatrix, transform);
pipelineDataSolid.updateColor(rshape);
renderDataShape->mPipelineBindGroupSolid.update(mQueue, pipelineDataSolid);
renderDataShape->mPipelineBindGroup = &renderDataShape->mPipelineBindGroupSolid;
renderDataShape->mPipelineBase = &mPipelineSolid;
}
return renderDataShape;
}
RenderData WgRenderer::prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) {
@ -55,6 +165,7 @@ bool WgRenderer::preRender() {
}
bool WgRenderer::renderShape(RenderData data) {
mRenderDatas.push(data);
return true;
}
@ -67,6 +178,8 @@ bool WgRenderer::postRender() {
}
bool WgRenderer::dispose(RenderData data) {
auto renderData = (WgRenderData*)data;
if (renderData) renderData->release();
return true;
}
@ -95,10 +208,187 @@ bool WgRenderer::clear() {
}
bool WgRenderer::sync() {
WGPUTextureView backBufferView = wgpuSwapChainGetCurrentTextureView(mSwapChain);
// command buffer descriptor
WGPUCommandBufferDescriptor commandBufferDesc{};
commandBufferDesc.nextInChain = nullptr;
commandBufferDesc.label = "The command buffer";
WGPUCommandBuffer commandsBuffer = nullptr; {
// command encoder descriptor
WGPUCommandEncoderDescriptor commandEncoderDesc{};
commandEncoderDesc.nextInChain = nullptr;
commandEncoderDesc.label = "The command encoder";
// begin render pass
WGPUCommandEncoder commandEncoder = wgpuDeviceCreateCommandEncoder(mDevice, &commandEncoderDesc); {
// render pass depth stencil attachment
WGPURenderPassDepthStencilAttachment depthStencilAttachment{};
depthStencilAttachment.view = mStencilTexView;
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 = backBufferView;
colorAttachment.resolveTarget = nullptr;
colorAttachment.loadOp = WGPULoadOp_Clear;
colorAttachment.storeOp = WGPUStoreOp_Store;
colorAttachment.clearValue = { 0.0f, 0.0f, 0.0f, 1.0 };
// 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
WGPURenderPassEncoder renderPassEncoder = wgpuCommandEncoderBeginRenderPass(commandEncoder, &renderPassDesc); {
// iterate render data
for (size_t i = 0; i < mRenderDatas.count; i++) {
WgRenderDataShape* renderData = (WgRenderDataShape*)(mRenderDatas[i]);
for (uint32_t j = 0; j < renderData->mGeometryDataFill.count; j++) {
// draw to stencil (first pass)
mPipelineEmpty.set(renderPassEncoder);
mPipelineBindGroupEmpty.bind(renderPassEncoder, 0);
renderData->mGeometryDataFill[j]->draw(renderPassEncoder);
// fill shape (secind pass)
renderData->mPipelineBase->set(renderPassEncoder);
renderData->mPipelineBindGroup->bind(renderPassEncoder, 0);
mGeometryDataPipeline.draw(renderPassEncoder);
}
}
}
// end render pass
wgpuRenderPassEncoderEnd(renderPassEncoder);
wgpuRenderPassEncoderRelease(renderPassEncoder);
// release backbuffer and present
wgpuTextureViewRelease(backBufferView);
}
commandsBuffer = wgpuCommandEncoderFinish(commandEncoder, &commandBufferDesc);
wgpuCommandEncoderRelease(commandEncoder);
}
wgpuQueueSubmit(mQueue, 1, &commandsBuffer);
wgpuCommandBufferRelease(commandsBuffer);
// go to the next frame
wgpuSwapChainPresent(mSwapChain);
mRenderDatas.clear();
return true;
}
bool WgRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h) {
// store target surface properties
mTargetSurface.stride = stride;
mTargetSurface.w = w;
mTargetSurface.h = h;
// update view matrix
mViewMatrix[0] = +2.0f / w; mViewMatrix[1] = +0.0f; mViewMatrix[2] = +0.0f; mViewMatrix[3] = +0.0f;
mViewMatrix[4] = +0.0f; mViewMatrix[5] = -2.0f / h; mViewMatrix[6] = +0.0f; mViewMatrix[7] = +0.0f;
mViewMatrix[8] = +0.0f; mViewMatrix[9] = +0.0f; mViewMatrix[10] = -1.0f; mViewMatrix[11] = +0.0f;
mViewMatrix[12] = -1.0f; mViewMatrix[13] = +1.0f; mViewMatrix[14] = +0.0f; mViewMatrix[15] = +1.0f;
// TODO: Add ability to render into offscreen buffer
return true;
}
// target for native window handle
bool WgRenderer::target(void* window, uint32_t w, uint32_t h) {
// store target surface properties
mTargetSurface.stride = w;
mTargetSurface.w = w > 0 ? w : 1;
mTargetSurface.h = h > 0 ? h : 1;
// update view matrix
mViewMatrix[0] = +2.0f / w; mViewMatrix[1] = +0.0f; mViewMatrix[2] = +0.0f; mViewMatrix[3] = +0.0f;
mViewMatrix[4] = +0.0f; mViewMatrix[5] = -2.0f / h; mViewMatrix[6] = +0.0f; mViewMatrix[7] = +0.0f;
mViewMatrix[8] = +0.0f; mViewMatrix[9] = +0.0f; mViewMatrix[10] = -1.0f; mViewMatrix[11] = +0.0f;
mViewMatrix[12] = -1.0f; mViewMatrix[13] = +1.0f; mViewMatrix[14] = +0.0f; mViewMatrix[15] = +1.0f;
// TODO: replace solution to cross-platform realization
// surface descriptor from windows hwnd
WGPUSurfaceDescriptorFromWindowsHWND surfaceDescHwnd{};
surfaceDescHwnd.chain.next = nullptr;
surfaceDescHwnd.chain.sType = WGPUSType_SurfaceDescriptorFromWindowsHWND;
surfaceDescHwnd.hinstance = GetModuleHandle(NULL);
surfaceDescHwnd.hwnd = (HWND)window;
WGPUSurfaceDescriptor surfaceDesc{};
surfaceDesc.nextInChain = (const WGPUChainedStruct*)&surfaceDescHwnd;
surfaceDesc.label = "The surface";
mSurface = wgpuInstanceCreateSurface(mInstance, &surfaceDesc);
assert(mSurface);
// get preferred format
WGPUTextureFormat swapChainFormat = WGPUTextureFormat_BGRA8Unorm;
// swapchain descriptor
WGPUSwapChainDescriptor swapChainDesc{};
swapChainDesc.nextInChain = nullptr;
swapChainDesc.label = "The swapchain";
swapChainDesc.usage = WGPUTextureUsage_RenderAttachment;
swapChainDesc.format = swapChainFormat;
swapChainDesc.width = mTargetSurface.w;
swapChainDesc.height = mTargetSurface.h;
swapChainDesc.presentMode = WGPUPresentMode_Mailbox;
mSwapChain = wgpuDeviceCreateSwapChain(mDevice, mSurface, &swapChainDesc);
assert(mSwapChain);
// depth-stencil texture
WGPUTextureDescriptor textureDesc{};
textureDesc.nextInChain = nullptr;
textureDesc.label = "The depth-stencil texture";
textureDesc.usage = WGPUTextureUsage_RenderAttachment;
textureDesc.dimension = WGPUTextureDimension_2D;
textureDesc.size = { swapChainDesc.width, swapChainDesc.height, 1 }; // window size
textureDesc.format = WGPUTextureFormat_Stencil8;
textureDesc.mipLevelCount = 1;
textureDesc.sampleCount = 1;
textureDesc.viewFormatCount = 0;
textureDesc.viewFormats = nullptr;
mStencilTex = wgpuDeviceCreateTexture(mDevice, &textureDesc);
assert(mStencilTex);
// depth-stencil texture view
WGPUTextureViewDescriptor textureViewDesc{};
textureViewDesc.nextInChain = nullptr;
textureViewDesc.label = "The depth-stencil texture view";
textureViewDesc.format = WGPUTextureFormat_Stencil8;
textureViewDesc.dimension = WGPUTextureViewDimension_2D;
textureViewDesc.baseMipLevel = 0;
textureViewDesc.mipLevelCount = 1;
textureViewDesc.baseArrayLayer = 0;
textureViewDesc.arrayLayerCount = 1;
textureViewDesc.aspect = WGPUTextureAspect_All;
mStencilTexView = wgpuTextureCreateView(mStencilTex, &textureViewDesc);
assert(mStencilTexView);
// update pipeline geometry data
static float vertexData[] = {
0.0f, 0.0f, 0.0f,
(float)w, 0.0f, 0.0f,
(float)w, (float)h, 0.0f,
1.0f, (float)h, 0.0f
};
static uint32_t indexData[] = { 0, 1, 2, 0, 2, 3 };
// update render data pipeline empty
mGeometryDataPipeline.update(mDevice, mQueue, vertexData, 4, indexData, 6);
// update bind group pipeline empty
WgPipelineDataEmpty pipelineDataEmpty{};
pipelineDataEmpty.updateMatrix(mViewMatrix, nullptr);
mPipelineBindGroupEmpty.update(mQueue, pipelineDataEmpty);
return true;
}

View file

@ -23,7 +23,9 @@
#ifndef _TVG_WG_RENDERER_H_
#define _TVG_WG_RENDERER_H_
#include "tvgRender.h"
#include "tvgWgPipelineEmpty.h"
#include "tvgWgPipelineSolid.h"
#include "tvgWgRenderData.h"
class WgRenderer : public RenderMethod
{
@ -51,6 +53,7 @@ public:
bool clear();
bool sync();
bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h);
bool target(void* window, uint32_t w, uint32_t h); // temporary solution
Compositor* target(const RenderRegion& region, ColorSpace cs);
bool beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity);
@ -59,6 +62,26 @@ public:
static WgRenderer* gen();
static bool init(uint32_t threads);
static bool term();
private:
Array<RenderData> mRenderDatas{};
private:
Surface mTargetSurface = { nullptr, 0, 0, 0, ColorSpace::Unsupported, true };
float mViewMatrix[16]{};
// basic webgpu instances (TODO: create separated entity)
WGPUInstance mInstance{};
WGPUAdapter mAdapter{};
WGPUDevice mDevice{};
WGPUQueue mQueue{};
// webgpu surface handles (TODO: create separated entity)
WGPUSurface mSurface{};
WGPUSwapChain mSwapChain{};
WGPUTexture mStencilTex{};
WGPUTextureView mStencilTexView{};
private:
WgPipelineEmpty mPipelineEmpty;
WgPipelineSolid mPipelineSolid;
WgGeometryData mGeometryDataPipeline;
WgPipelineBindGroupEmpty mPipelineBindGroupEmpty;
};
#endif /* _TVG_WG_RENDERER_H_ */

View file

@ -0,0 +1,102 @@
/*
* Copyright (c) 2023 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 "tvgWgShaderSrc.h"
#include <string>
//************************************************************************
// cShaderSource_PipelineEmpty
//************************************************************************
const char* cShaderSource_PipelineEmpty = R"(
// vertex input
struct VertexInput {
@location(0) position: vec3f
};
// Matrix
struct Matrix {
transform: mat4x4f
};
// vertex output
struct VertexOutput {
@builtin(position) position: vec4f
};
// uMatrix
@group(0) @binding(0) var<uniform> uMatrix: Matrix;
@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
// fill output
var out: VertexOutput;
out.position = uMatrix.transform * vec4f(in.position.xy, 0.0, 1.0);
return out;
}
@fragment
fn fs_main(in: VertexOutput) -> void {
// nothing to draw, just stencil value
})";
//************************************************************************
// cShaderSource_PipelineSolid
//************************************************************************
const char* cShaderSource_PipelineSolid = R"(
// vertex input
struct VertexInput {
@location(0) position: vec3f
};
// Matrix
struct Matrix {
transform: mat4x4f
};
// ColorInfo
struct ColorInfo {
color: vec4f
};
// vertex output
struct VertexOutput {
@builtin(position) position: vec4f
};
// uMatrix
@group(0) @binding(0) var<uniform> uMatrix: Matrix;
// uColorInfo
@group(0) @binding(1) var<uniform> uColorInfo: ColorInfo;
@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
// fill output
var out: VertexOutput;
out.position = uMatrix.transform * vec4f(in.position.xy, 0.0, 1.0);
//out.position = vec4f(in.position.xy, 0.0, 1.0);
return out;
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f {
return uColorInfo.color;
})";

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2023 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.
*/
#pragma once
#ifndef _TVG_WG_SHADER_SRC_H_
#define _TVG_WG_SHADER_SRC_H_
// pipeline shader module empty
extern const char* cShaderSource_PipelineEmpty;
// pipeline shader module solid
extern const char* cShaderSource_PipelineSolid;
#endif