diff --git a/src/renderer/wg_engine/meson.build b/src/renderer/wg_engine/meson.build index d086e646..5405b559 100644 --- a/src/renderer/wg_engine/meson.build +++ b/src/renderer/wg_engine/meson.build @@ -3,6 +3,7 @@ source_file = [ 'tvgWgPipelineEmpty.cpp', 'tvgWgPipelineLinear.cpp', 'tvgWgPipelineSolid.cpp', + 'tvgWgPipelineRadial.cpp', 'tvgWgRenderData.cpp', 'tvgWgRenderer.cpp', 'tvgWgShaderSrc.cpp' diff --git a/src/renderer/wg_engine/tvgWgPipelineRadial.cpp b/src/renderer/wg_engine/tvgWgPipelineRadial.cpp new file mode 100644 index 00000000..64b15231 --- /dev/null +++ b/src/renderer/wg_engine/tvgWgPipelineRadial.cpp @@ -0,0 +1,303 @@ +/* + * 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 "tvgWgPipelineRadial.h" +#include "tvgWgShaderSrc.h" + +//************************************************************************ +// WgPipelineBindGroupRadial +//************************************************************************ + +void WgPipelineDataRadial::updateGradient(RadialGradient* radialGradient) { + const Fill::ColorStop* stops = nullptr; + auto stopCnt = radialGradient->colorStops(&stops); + + uGradientInfo.nStops[0] = stopCnt; + uGradientInfo.nStops[1] = 0.5f; + + for (uint32_t i = 0; i < stopCnt; ++i) { + uGradientInfo.stopPoints[i] = stops[i].offset; + uGradientInfo.stopColors[i * 4 + 0] = stops[i].r / 255.f; + uGradientInfo.stopColors[i * 4 + 1] = stops[i].g / 255.f; + uGradientInfo.stopColors[i * 4 + 2] = stops[i].b / 255.f; + uGradientInfo.stopColors[i * 4 + 3] = stops[i].a / 255.f; + } + + radialGradient->radial( + &uGradientInfo.centerPos[0], + &uGradientInfo.centerPos[1], + &uGradientInfo.radius[0]); +} + +//************************************************************************ +// WgPipelineBindGroupRadial +//************************************************************************ + +void WgPipelineBindGroupRadial::initialize(WGPUDevice device, WgPipelineRadial& pipelinePipelineRadial) { + // buffer uniform uMatrix + WGPUBufferDescriptor bufferUniformDesc_uMatrix{}; + bufferUniformDesc_uMatrix.nextInChain = nullptr; + bufferUniformDesc_uMatrix.label = "Buffer uniform pipeline radial 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_uGradientInfo{}; + bufferUniformDesc_uGradientInfo.nextInChain = nullptr; + bufferUniformDesc_uGradientInfo.label = "Buffer uniform pipeline radial uGradientInfo"; + bufferUniformDesc_uGradientInfo.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform; + bufferUniformDesc_uGradientInfo.size = sizeof(WgPipelineRadialGradientInfo); + bufferUniformDesc_uGradientInfo.mappedAtCreation = false; + uBufferGradientInfo = wgpuDeviceCreateBuffer(device, &bufferUniformDesc_uGradientInfo); + assert(uBufferGradientInfo); + + // 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) uGradientInfo + WGPUBindGroupEntry bindGroupEntry_uGradientInfo{}; + bindGroupEntry_uGradientInfo.nextInChain = nullptr; + bindGroupEntry_uGradientInfo.binding = 1; + bindGroupEntry_uGradientInfo.buffer = uBufferGradientInfo; + bindGroupEntry_uGradientInfo.offset = 0; + bindGroupEntry_uGradientInfo.size = sizeof(WgPipelineRadialGradientInfo); + bindGroupEntry_uGradientInfo.sampler = nullptr; + bindGroupEntry_uGradientInfo.textureView = nullptr; + // bind group entries + WGPUBindGroupEntry bindGroupEntries[] { + bindGroupEntry_uMatrix, // @binding(0) uMatrix + bindGroupEntry_uGradientInfo // @binding(1) uGradientInfo + }; + // bind group descriptor + WGPUBindGroupDescriptor bindGroupDescPipeline{}; + bindGroupDescPipeline.nextInChain = nullptr; + bindGroupDescPipeline.label = "The binding group pipeline radial"; + bindGroupDescPipeline.layout = pipelinePipelineRadial.mBindGroupLayout; + bindGroupDescPipeline.entryCount = 2; + bindGroupDescPipeline.entries = bindGroupEntries; + mBindGroup = wgpuDeviceCreateBindGroup(device, &bindGroupDescPipeline); + assert(mBindGroup); +} + +void WgPipelineBindGroupRadial::release() { + if (uBufferGradientInfo) { + wgpuBufferDestroy(uBufferGradientInfo); + wgpuBufferRelease(uBufferGradientInfo); + uBufferGradientInfo = nullptr; + } + if (uBufferMatrix) { + wgpuBufferDestroy(uBufferMatrix); + wgpuBufferRelease(uBufferMatrix); + uBufferMatrix = nullptr; + } + if (mBindGroup) { + wgpuBindGroupRelease(mBindGroup); + mBindGroup = nullptr; + } +} + +void WgPipelineBindGroupRadial::update(WGPUQueue queue, WgPipelineDataRadial& pipelineDataRadial) { + wgpuQueueWriteBuffer(queue, uBufferMatrix, 0, &pipelineDataRadial.uMatrix, sizeof(pipelineDataRadial.uMatrix)); + wgpuQueueWriteBuffer(queue, uBufferGradientInfo, 0, &pipelineDataRadial.uGradientInfo, sizeof(pipelineDataRadial.uGradientInfo)); +} + +//************************************************************************ +// WgPipelineRadial +//************************************************************************ + +void WgPipelineRadial::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 radial"; + 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 radial"; + 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_PipelineRadial; + // shader module descriptor + WGPUShaderModuleDescriptor shaderModuleDesc{}; + shaderModuleDesc.nextInChain = &shaderModuleWGSLDesc.chain; + shaderModuleDesc.label = "The shader module pipeline radial"; + 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 radial"; + // 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 WgPipelineRadial::release() { + wgpuRenderPipelineRelease(mRenderPipeline); + wgpuShaderModuleRelease(mShaderModule); + wgpuPipelineLayoutRelease(mPipelineLayout); + wgpuBindGroupLayoutRelease(mBindGroupLayout); +} diff --git a/src/renderer/wg_engine/tvgWgPipelineRadial.h b/src/renderer/wg_engine/tvgWgPipelineRadial.h new file mode 100644 index 00000000..f04e12c3 --- /dev/null +++ b/src/renderer/wg_engine/tvgWgPipelineRadial.h @@ -0,0 +1,61 @@ +/* + * 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_RADIAL_H_ +#define _TVG_WG_PIPELINE_RADIAL_H_ + +#include "tvgWgPipelineBase.h" + +class WgPipelineRadial; + +#define MAX_RADIAL_GRADIENT_STOPS 4 +struct WgPipelineRadialGradientInfo { + alignas(16) float nStops[4]{}; + alignas(16) float centerPos[2]{}; + alignas(8) float radius[2]{}; + alignas(8) float stopPoints[MAX_RADIAL_GRADIENT_STOPS]{}; + alignas(16) float stopColors[4 * MAX_RADIAL_GRADIENT_STOPS]{}; +}; + +struct WgPipelineDataRadial: WgPipelineData { + WgPipelineRadialGradientInfo uGradientInfo{}; // @binding(1) + + void updateGradient(RadialGradient* radialGradient); +}; + +class WgPipelineBindGroupRadial: public WgPipelineBindGroup { +private: + WGPUBuffer uBufferGradientInfo{}; // @binding(1) +public: + void initialize(WGPUDevice device, WgPipelineRadial& pipelinePipelineRadial); + void release(); + + void update(WGPUQueue mQueue, WgPipelineDataRadial& pipelineDataRadial); +}; + +class WgPipelineRadial: public WgPipelineBase { +public: + void initialize(WGPUDevice device) override; + void release() override; +}; + +#endif //_TVG_WG_PIPELINE_RADIAL_H_ diff --git a/src/renderer/wg_engine/tvgWgRenderData.cpp b/src/renderer/wg_engine/tvgWgRenderData.cpp index e0ea6e0e..dc248e3b 100644 --- a/src/renderer/wg_engine/tvgWgRenderData.cpp +++ b/src/renderer/wg_engine/tvgWgRenderData.cpp @@ -81,6 +81,8 @@ void WgGeometryData::release() { void WgRenderDataShape::release() { releaseRenderData(); mPipelineBindGroupSolid.release(); + mPipelineBindGroupLinear.release(); + mPipelineBindGroupRadial.release(); } void WgRenderDataShape::releaseRenderData() { diff --git a/src/renderer/wg_engine/tvgWgRenderData.h b/src/renderer/wg_engine/tvgWgRenderData.h index c5e304f3..80250dcf 100644 --- a/src/renderer/wg_engine/tvgWgRenderData.h +++ b/src/renderer/wg_engine/tvgWgRenderData.h @@ -22,6 +22,7 @@ #include "tvgWgPipelineSolid.h" #include "tvgWgPipelineLinear.h" +#include "tvgWgPipelineRadial.h" #ifndef _TVG_WG_RENDER_DATA_H_ #define _TVG_WG_RENDER_DATA_H_ @@ -55,6 +56,7 @@ public: WgPipelineBindGroupSolid mPipelineBindGroupSolid{}; WgPipelineBindGroupLinear mPipelineBindGroupLinear{}; + WgPipelineBindGroupRadial mPipelineBindGroupRadial{}; WgPipelineBase* mPipelineBase{}; // external WgPipelineBindGroup* mPipelineBindGroup{}; // external diff --git a/src/renderer/wg_engine/tvgWgRenderer.cpp b/src/renderer/wg_engine/tvgWgRenderer.cpp index eb3543c8..49fe8c32 100644 --- a/src/renderer/wg_engine/tvgWgRenderer.cpp +++ b/src/renderer/wg_engine/tvgWgRenderer.cpp @@ -104,6 +104,7 @@ void WgRenderer::initialize() { mPipelineEmpty.initialize(mDevice); mPipelineSolid.initialize(mDevice); mPipelineLinear.initialize(mDevice); + mPipelineRadial.initialize(mDevice); mPipelineBindGroupEmpty.initialize(mDevice, mPipelineEmpty); mGeometryDataPipeline.initialize(mDevice); } @@ -118,6 +119,7 @@ void WgRenderer::release() { if (mSurface) wgpuSurfaceRelease(mSurface); mGeometryDataPipeline.release(); mPipelineBindGroupEmpty.release(); + mPipelineRadial.release(); mPipelineLinear.release(); mPipelineSolid.release(); mPipelineEmpty.release(); @@ -137,6 +139,7 @@ RenderData WgRenderer::prepare(const RenderShape& rshape, RenderData data, const renderDataShape->initialize(mDevice); renderDataShape->mPipelineBindGroupSolid.initialize(mDevice, mPipelineSolid); renderDataShape->mPipelineBindGroupLinear.initialize(mDevice, mPipelineLinear); + renderDataShape->mPipelineBindGroupRadial.initialize(mDevice, mPipelineRadial); } if (flags & RenderUpdateFlag::Path) @@ -152,6 +155,14 @@ RenderData WgRenderer::prepare(const RenderShape& rshape, RenderData data, const renderDataShape->mPipelineBindGroupLinear.update(mQueue, brushDataLinear); renderDataShape->mPipelineBindGroup = &renderDataShape->mPipelineBindGroupLinear; renderDataShape->mPipelineBase = &mPipelineLinear; + } // setup radial fill properties + else if (rshape.fill->identifier() == TVG_CLASS_ID_RADIAL) { + WgPipelineDataRadial brushDataRadial{}; + brushDataRadial.updateMatrix(mViewMatrix, transform); + brushDataRadial.updateGradient((RadialGradient*)rshape.fill); + renderDataShape->mPipelineBindGroupRadial.update(mQueue, brushDataRadial); + renderDataShape->mPipelineBindGroup = &renderDataShape->mPipelineBindGroupRadial; + renderDataShape->mPipelineBase = &mPipelineRadial; } } diff --git a/src/renderer/wg_engine/tvgWgRenderer.h b/src/renderer/wg_engine/tvgWgRenderer.h index 9c9a6acb..1461591c 100644 --- a/src/renderer/wg_engine/tvgWgRenderer.h +++ b/src/renderer/wg_engine/tvgWgRenderer.h @@ -80,6 +80,7 @@ private: WgPipelineEmpty mPipelineEmpty; WgPipelineSolid mPipelineSolid; WgPipelineLinear mPipelineLinear; + WgPipelineRadial mPipelineRadial; WgGeometryData mGeometryDataPipeline; WgPipelineBindGroupEmpty mPipelineBindGroupEmpty; }; diff --git a/src/renderer/wg_engine/tvgWgShaderSrc.cpp b/src/renderer/wg_engine/tvgWgShaderSrc.cpp index 6a2b5e3d..f26d383f 100644 --- a/src/renderer/wg_engine/tvgWgShaderSrc.cpp +++ b/src/renderer/wg_engine/tvgWgShaderSrc.cpp @@ -26,6 +26,7 @@ //************************************************************************ // cShaderSource_PipelineEmpty //************************************************************************ + const char* cShaderSource_PipelineEmpty = R"( // vertex input struct VertexInput { @@ -61,6 +62,7 @@ fn fs_main(in: VertexOutput) -> void { //************************************************************************ // cShaderSource_PipelineSolid //************************************************************************ + const char* cShaderSource_PipelineSolid = R"( // vertex input struct VertexInput { @@ -102,8 +104,9 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f { })"; //************************************************************************ -// cShaderSource_PipelineRadial +// cShaderSource_PipelineLinear //************************************************************************ + const char* cShaderSource_PipelineLinear = R"( // vertex input struct VertexInput { @@ -184,3 +187,82 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f { return color; })"; + +//************************************************************************ +// cShaderSource_PipelineRadial +//************************************************************************ + +const char* cShaderSource_PipelineRadial = R"( +// vertex input +struct VertexInput { + @location(0) position: vec3f +}; + +// Matrix +struct Matrix { + transform: mat4x4f +}; + +// GradientInfo +const MAX_STOP_COUNT = 4; +struct GradientInfo { + nStops : vec4f, + centerPos : vec2f, + radius : vec2f, + stopPoints : vec4f, + stopColors : array +}; + +// vertex output +struct VertexOutput { + @builtin(position) position: vec4f, + @location(0) vScreenCoord: vec2f +}; + +// uMatrix +@group(0) @binding(0) var uMatrix: Matrix; +// uGradientInfo +@group(0) @binding(1) var uGradientInfo: GradientInfo; + +@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.vScreenCoord = in.position.xy; + return out; +} + +@fragment +fn fs_main(in: VertexOutput) -> @location(0) vec4f { + // get interpolation factor + let t: f32 = clamp(distance(uGradientInfo.centerPos, in.vScreenCoord) / uGradientInfo.radius.x, 0.0, 1.0); + + // get stops count + let last: i32 = i32(uGradientInfo.nStops[0]) - 1; + + // resulting color + var color = vec4(1.0); + + // closer than first stop + if (t <= uGradientInfo.stopPoints[0]) { + color = uGradientInfo.stopColors[0]; + } + + // further than last stop + if (t >= uGradientInfo.stopPoints[last]) { + color = uGradientInfo.stopColors[last]; + } + + // look in the middle + for (var i = 0i; i < last; i++) { + let strt = uGradientInfo.stopPoints[i]; + let stop = uGradientInfo.stopPoints[i+1]; + if ((t > strt) && (t < stop)) { + let step: f32 = (t - strt) / (stop - strt); + color = mix(uGradientInfo.stopColors[i], uGradientInfo.stopColors[i+1], step); + } + } + + return color; +})"; diff --git a/src/renderer/wg_engine/tvgWgShaderSrc.h b/src/renderer/wg_engine/tvgWgShaderSrc.h index e8db9d92..2707a4cd 100644 --- a/src/renderer/wg_engine/tvgWgShaderSrc.h +++ b/src/renderer/wg_engine/tvgWgShaderSrc.h @@ -34,4 +34,7 @@ extern const char* cShaderSource_PipelineSolid; // pipeline shader module linear extern const char* cShaderSource_PipelineLinear; -#endif +// pipeline shader module radial +extern const char* cShaderSource_PipelineRadial; + +#endif // _TVG_WG_SHADER_SRC_H_