mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 13:43:43 +00:00
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:
parent
61081c02af
commit
14b2508cd1
15 changed files with 1517 additions and 5 deletions
|
@ -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;
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
source_file = [
|
||||
'tvgWgRenderer.cpp',
|
||||
'tvgWgPipelineBase.cpp',
|
||||
'tvgWgPipelineEmpty.cpp',
|
||||
'tvgWgPipelineSolid.cpp',
|
||||
'tvgWgRenderData.cpp',
|
||||
'tvgWgRenderer.cpp',
|
||||
'tvgWgShaderSrc.cpp'
|
||||
]
|
||||
|
||||
engine_dep += [declare_dependency(
|
||||
|
|
31
src/renderer/wg_engine/tvgWgCommon.h
Normal file
31
src/renderer/wg_engine/tvgWgCommon.h
Normal 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_
|
73
src/renderer/wg_engine/tvgWgPipelineBase.cpp
Normal file
73
src/renderer/wg_engine/tvgWgPipelineBase.cpp
Normal 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);
|
||||
}
|
56
src/renderer/wg_engine/tvgWgPipelineBase.h
Normal file
56
src/renderer/wg_engine/tvgWgPipelineBase.h
Normal 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_
|
243
src/renderer/wg_engine/tvgWgPipelineEmpty.cpp
Normal file
243
src/renderer/wg_engine/tvgWgPipelineEmpty.cpp
Normal 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);
|
||||
}
|
63
src/renderer/wg_engine/tvgWgPipelineEmpty.h
Normal file
63
src/renderer/wg_engine/tvgWgPipelineEmpty.h
Normal 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_
|
289
src/renderer/wg_engine/tvgWgPipelineSolid.cpp
Normal file
289
src/renderer/wg_engine/tvgWgPipelineSolid.cpp
Normal 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);
|
||||
}
|
56
src/renderer/wg_engine/tvgWgPipelineSolid.h
Normal file
56
src/renderer/wg_engine/tvgWgPipelineSolid.h
Normal 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_
|
176
src/renderer/wg_engine/tvgWgRenderData.cpp
Normal file
176
src/renderer/wg_engine/tvgWgRenderData.cpp
Normal 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];
|
||||
}
|
68
src/renderer/wg_engine/tvgWgRenderData.h
Normal file
68
src/renderer/wg_engine/tvgWgRenderData.h
Normal 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_
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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_ */
|
||||
|
|
102
src/renderer/wg_engine/tvgWgShaderSrc.cpp
Normal file
102
src/renderer/wg_engine/tvgWgShaderSrc.cpp
Normal 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;
|
||||
})";
|
34
src/renderer/wg_engine/tvgWgShaderSrc.h
Normal file
34
src/renderer/wg_engine/tvgWgShaderSrc.h
Normal 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
|
Loading…
Add table
Reference in a new issue