mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-10 14:41:50 +00:00

Before the current changes, all surfaces were painted using a full-screen overlay, no matter how large the object was rendered. This approach is redundant and required reorganization. At the moment, all objects are rendered using an overlay equal to the box of the object itself, which reduces the cost of filling the surface. Also surfaces and images were divided into different entities, which reduces the pressure on memory. Also geometry data for rendering and geometry data for calculations in system memory were logically separated.
492 lines
No EOL
18 KiB
C++
492 lines
No EOL
18 KiB
C++
/*
|
|
* 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 "tvgWgCommon.h"
|
|
#include <iostream>
|
|
|
|
//*****************************************************************************
|
|
// context
|
|
//*****************************************************************************
|
|
|
|
void WgContext::initialize()
|
|
{
|
|
// create instance
|
|
WGPUInstanceDescriptor instanceDesc{};
|
|
instanceDesc.nextInChain = nullptr;
|
|
instance = wgpuCreateInstance(&instanceDesc);
|
|
assert(instance);
|
|
|
|
// 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(instance, &requestAdapterOptions, onAdapterRequestEnded, &adapter);
|
|
assert(adapter);
|
|
|
|
// adapter enumarate fueatures
|
|
size_t featuresCount = wgpuAdapterEnumerateFeatures(adapter, featureNames);
|
|
wgpuAdapterGetProperties(adapter, &adapterProperties);
|
|
wgpuAdapterGetLimits(adapter, &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(adapter, &deviceDesc, onDeviceRequestEnded, &device);
|
|
assert(device);
|
|
|
|
// on device error function
|
|
auto onDeviceError = [](WGPUErrorType type, char const* message, void* pUserData) {
|
|
TVGERR("WG_RENDERER", "Uncaptured device error: %s", message);
|
|
// TODO: remove direct error message
|
|
std::cout << message << std::endl;
|
|
};
|
|
// set device error handling
|
|
wgpuDeviceSetUncapturedErrorCallback(device, onDeviceError, nullptr);
|
|
|
|
queue = wgpuDeviceGetQueue(device);
|
|
assert(queue);
|
|
}
|
|
|
|
|
|
void WgContext::release()
|
|
{
|
|
if (device) {
|
|
wgpuDeviceDestroy(device);
|
|
wgpuDeviceRelease(device);
|
|
}
|
|
if (adapter) wgpuAdapterRelease(adapter);
|
|
if (instance) wgpuInstanceRelease(instance);
|
|
}
|
|
|
|
|
|
void WgContext::executeCommandEncoder(WGPUCommandEncoder commandEncoder)
|
|
{
|
|
// command buffer descriptor
|
|
WGPUCommandBufferDescriptor commandBufferDesc{};
|
|
commandBufferDesc.nextInChain = nullptr;
|
|
commandBufferDesc.label = "The command buffer";
|
|
WGPUCommandBuffer commandsBuffer = nullptr;
|
|
commandsBuffer = wgpuCommandEncoderFinish(commandEncoder, &commandBufferDesc);
|
|
wgpuQueueSubmit(queue, 1, &commandsBuffer);
|
|
wgpuCommandBufferRelease(commandsBuffer);
|
|
}
|
|
|
|
|
|
//*****************************************************************************
|
|
// bind group
|
|
//*****************************************************************************
|
|
|
|
void WgBindGroup::set(WGPURenderPassEncoder encoder, uint32_t groupIndex)
|
|
{
|
|
wgpuRenderPassEncoderSetBindGroup(encoder, groupIndex, mBindGroup, 0, nullptr);
|
|
}
|
|
|
|
|
|
WGPUBindGroupEntry WgBindGroup::makeBindGroupEntryBuffer(uint32_t binding, WGPUBuffer buffer)
|
|
{
|
|
WGPUBindGroupEntry bindGroupEntry{};
|
|
bindGroupEntry.nextInChain = nullptr;
|
|
bindGroupEntry.binding = binding;
|
|
bindGroupEntry.buffer = buffer;
|
|
bindGroupEntry.offset = 0;
|
|
bindGroupEntry.size = wgpuBufferGetSize(buffer);
|
|
bindGroupEntry.sampler = nullptr;
|
|
bindGroupEntry.textureView = nullptr;
|
|
return bindGroupEntry;
|
|
}
|
|
|
|
|
|
WGPUBindGroupEntry WgBindGroup::makeBindGroupEntrySampler(uint32_t binding, WGPUSampler sampler)
|
|
{
|
|
WGPUBindGroupEntry bindGroupEntry{};
|
|
bindGroupEntry.nextInChain = nullptr;
|
|
bindGroupEntry.binding = binding;
|
|
bindGroupEntry.buffer = nullptr;
|
|
bindGroupEntry.offset = 0;
|
|
bindGroupEntry.size = 0;
|
|
bindGroupEntry.sampler = sampler;
|
|
bindGroupEntry.textureView = nullptr;
|
|
return bindGroupEntry;
|
|
}
|
|
|
|
|
|
WGPUBindGroupEntry WgBindGroup::makeBindGroupEntryTextureView(uint32_t binding, WGPUTextureView textureView)
|
|
{
|
|
WGPUBindGroupEntry bindGroupEntry{};
|
|
bindGroupEntry.nextInChain = nullptr;
|
|
bindGroupEntry.binding = binding;
|
|
bindGroupEntry.buffer = nullptr;
|
|
bindGroupEntry.offset = 0;
|
|
bindGroupEntry.size = 0;
|
|
bindGroupEntry.sampler = nullptr;
|
|
bindGroupEntry.textureView = textureView;
|
|
return bindGroupEntry;
|
|
}
|
|
|
|
|
|
WGPUBindGroupLayoutEntry WgBindGroup::makeBindGroupLayoutEntryBuffer(uint32_t binding)
|
|
{
|
|
WGPUBindGroupLayoutEntry bindGroupLayoutEntry{};
|
|
bindGroupLayoutEntry.nextInChain = nullptr;
|
|
bindGroupLayoutEntry.binding = binding;
|
|
bindGroupLayoutEntry.visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment;
|
|
bindGroupLayoutEntry.buffer.nextInChain = nullptr;
|
|
bindGroupLayoutEntry.buffer.type = WGPUBufferBindingType_Uniform;
|
|
bindGroupLayoutEntry.buffer.hasDynamicOffset = false;
|
|
bindGroupLayoutEntry.buffer.minBindingSize = 0;
|
|
return bindGroupLayoutEntry;
|
|
}
|
|
|
|
|
|
WGPUBindGroupLayoutEntry WgBindGroup::makeBindGroupLayoutEntrySampler(uint32_t binding)
|
|
{
|
|
WGPUBindGroupLayoutEntry bindGroupLayoutEntry{};
|
|
bindGroupLayoutEntry.nextInChain = nullptr;
|
|
bindGroupLayoutEntry.binding = binding;
|
|
bindGroupLayoutEntry.visibility = WGPUShaderStage_Fragment;
|
|
bindGroupLayoutEntry.sampler.nextInChain = nullptr;
|
|
bindGroupLayoutEntry.sampler.type = WGPUSamplerBindingType_Filtering;
|
|
return bindGroupLayoutEntry;
|
|
}
|
|
|
|
|
|
WGPUBindGroupLayoutEntry WgBindGroup::makeBindGroupLayoutEntryTextureView(uint32_t binding)
|
|
{
|
|
WGPUBindGroupLayoutEntry bindGroupLayoutEntry{};
|
|
bindGroupLayoutEntry.nextInChain = nullptr;
|
|
bindGroupLayoutEntry.binding = binding;
|
|
bindGroupLayoutEntry.visibility = WGPUShaderStage_Fragment;
|
|
bindGroupLayoutEntry.texture.nextInChain = nullptr;
|
|
bindGroupLayoutEntry.texture.sampleType = WGPUTextureSampleType_Float;
|
|
bindGroupLayoutEntry.texture.viewDimension = WGPUTextureViewDimension_2D;
|
|
bindGroupLayoutEntry.texture.multisampled = false;
|
|
return bindGroupLayoutEntry;
|
|
}
|
|
|
|
|
|
WGPUBuffer WgBindGroup::createBuffer(WGPUDevice device, WGPUQueue queue, const void *data, size_t size)
|
|
{
|
|
WGPUBufferDescriptor bufferDescriptor{};
|
|
bufferDescriptor.nextInChain = nullptr;
|
|
bufferDescriptor.label = "The uniform buffer";
|
|
bufferDescriptor.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform;
|
|
bufferDescriptor.size = size;
|
|
bufferDescriptor.mappedAtCreation = false;
|
|
WGPUBuffer buffer = wgpuDeviceCreateBuffer(device, &bufferDescriptor);
|
|
assert(buffer);
|
|
wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
|
|
return buffer;
|
|
}
|
|
|
|
|
|
WGPUBindGroup WgBindGroup::createBindGroup(WGPUDevice device, WGPUBindGroupLayout layout, const WGPUBindGroupEntry* bindGroupEntries, uint32_t count)
|
|
{
|
|
WGPUBindGroupDescriptor bindGroupDesc{};
|
|
bindGroupDesc.nextInChain = nullptr;
|
|
bindGroupDesc.label = "The binding group sampler";
|
|
bindGroupDesc.layout = layout;
|
|
bindGroupDesc.entryCount = count;
|
|
bindGroupDesc.entries = bindGroupEntries;
|
|
return wgpuDeviceCreateBindGroup(device, &bindGroupDesc);
|
|
}
|
|
|
|
|
|
WGPUBindGroupLayout WgBindGroup::createBindGroupLayout(WGPUDevice device, const WGPUBindGroupLayoutEntry* bindGroupLayoutEntries, uint32_t count)
|
|
{
|
|
WGPUBindGroupLayoutDescriptor bindGroupLayoutDesc{};
|
|
bindGroupLayoutDesc.nextInChain = nullptr;
|
|
bindGroupLayoutDesc.label = "The bind group layout";
|
|
bindGroupLayoutDesc.entryCount = count;
|
|
bindGroupLayoutDesc.entries = bindGroupLayoutEntries; // @binding
|
|
return wgpuDeviceCreateBindGroupLayout(device, &bindGroupLayoutDesc);
|
|
}
|
|
|
|
|
|
void WgBindGroup::releaseBuffer(WGPUBuffer& buffer)
|
|
{
|
|
if (buffer) {
|
|
wgpuBufferDestroy(buffer);
|
|
wgpuBufferRelease(buffer);
|
|
buffer = nullptr;
|
|
}
|
|
}
|
|
|
|
|
|
void WgBindGroup::releaseBindGroup(WGPUBindGroup& bindGroup)
|
|
{
|
|
if (bindGroup) wgpuBindGroupRelease(bindGroup);
|
|
bindGroup = nullptr;
|
|
}
|
|
|
|
|
|
void WgBindGroup::releaseBindGroupLayout(WGPUBindGroupLayout& bindGroupLayout)
|
|
{
|
|
if (bindGroupLayout) wgpuBindGroupLayoutRelease(bindGroupLayout);
|
|
bindGroupLayout = nullptr;
|
|
}
|
|
|
|
//*****************************************************************************
|
|
// pipeline
|
|
//*****************************************************************************
|
|
|
|
void WgPipeline::allocate(WGPUDevice device,
|
|
WGPUVertexBufferLayout vertexBufferLayouts[], uint32_t attribsCount,
|
|
WGPUBindGroupLayout bindGroupLayouts[], uint32_t bindGroupsCount,
|
|
WGPUCompareFunction stencilCompareFunction, WGPUStencilOperation stencilOperation,
|
|
const char* shaderSource, const char* shaderLabel, const char* pipelineLabel)
|
|
{
|
|
mShaderModule = createShaderModule(device, shaderSource, shaderLabel);
|
|
assert(mShaderModule);
|
|
|
|
mPipelineLayout = createPipelineLayout(device, bindGroupLayouts, bindGroupsCount);
|
|
assert(mPipelineLayout);
|
|
|
|
mRenderPipeline = createRenderPipeline(device,
|
|
vertexBufferLayouts, attribsCount,
|
|
stencilCompareFunction, stencilOperation,
|
|
mPipelineLayout, mShaderModule, pipelineLabel);
|
|
assert(mRenderPipeline);
|
|
}
|
|
|
|
|
|
void WgPipeline::release()
|
|
{
|
|
destroyRenderPipeline(mRenderPipeline);
|
|
destroyShaderModule(mShaderModule);
|
|
destroyPipelineLayout(mPipelineLayout);
|
|
}
|
|
|
|
|
|
void WgPipeline::set(WGPURenderPassEncoder renderPassEncoder)
|
|
{
|
|
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, mRenderPipeline);
|
|
};
|
|
|
|
|
|
WGPUBlendState WgPipeline::makeBlendState()
|
|
{
|
|
WGPUBlendState blendState{};
|
|
blendState.color.operation = WGPUBlendOperation_Add;
|
|
blendState.color.srcFactor = WGPUBlendFactor_SrcAlpha;
|
|
blendState.color.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha;
|
|
blendState.alpha.operation = WGPUBlendOperation_Add;
|
|
blendState.alpha.srcFactor = WGPUBlendFactor_Zero;
|
|
blendState.alpha.dstFactor = WGPUBlendFactor_One;
|
|
return blendState;
|
|
}
|
|
|
|
|
|
WGPUColorTargetState WgPipeline::makeColorTargetState(const WGPUBlendState* blendState)
|
|
{
|
|
WGPUColorTargetState colorTargetState{};
|
|
colorTargetState.nextInChain = nullptr;
|
|
colorTargetState.format = WGPUTextureFormat_BGRA8Unorm; // (WGPUTextureFormat_BGRA8UnormSrgb)
|
|
colorTargetState.blend = blendState;
|
|
colorTargetState.writeMask = WGPUColorWriteMask_All;
|
|
return colorTargetState;
|
|
}
|
|
|
|
|
|
WGPUVertexBufferLayout WgPipeline::makeVertexBufferLayout(const WGPUVertexAttribute* vertexAttributes, uint32_t count, uint64_t stride)
|
|
{
|
|
WGPUVertexBufferLayout vertexBufferLayoutPos{};
|
|
vertexBufferLayoutPos.arrayStride = stride;
|
|
vertexBufferLayoutPos.stepMode = WGPUVertexStepMode_Vertex;
|
|
vertexBufferLayoutPos.attributeCount = count;
|
|
vertexBufferLayoutPos.attributes = vertexAttributes;
|
|
return vertexBufferLayoutPos;
|
|
}
|
|
|
|
|
|
WGPUVertexState WgPipeline::makeVertexState(WGPUShaderModule shaderModule, const WGPUVertexBufferLayout* buffers, uint32_t count)
|
|
{
|
|
WGPUVertexState vertexState{};
|
|
vertexState.nextInChain = nullptr;
|
|
vertexState.module = shaderModule;
|
|
vertexState.entryPoint = "vs_main";
|
|
vertexState.constantCount = 0;
|
|
vertexState.constants = nullptr;
|
|
vertexState.bufferCount = count;
|
|
vertexState.buffers = buffers;
|
|
return vertexState;
|
|
}
|
|
|
|
|
|
WGPUPrimitiveState WgPipeline::makePrimitiveState()
|
|
{
|
|
WGPUPrimitiveState primitiveState{};
|
|
primitiveState.nextInChain = nullptr;
|
|
primitiveState.topology = WGPUPrimitiveTopology_TriangleList;
|
|
primitiveState.stripIndexFormat = WGPUIndexFormat_Undefined;
|
|
primitiveState.frontFace = WGPUFrontFace_CCW;
|
|
primitiveState.cullMode = WGPUCullMode_None;
|
|
return primitiveState;
|
|
}
|
|
|
|
WGPUDepthStencilState WgPipeline::makeDepthStencilState(WGPUCompareFunction compare, WGPUStencilOperation operation)
|
|
{
|
|
WGPUDepthStencilState depthStencilState{};
|
|
depthStencilState.nextInChain = nullptr;
|
|
depthStencilState.format = WGPUTextureFormat_Stencil8;
|
|
depthStencilState.depthWriteEnabled = false;
|
|
depthStencilState.depthCompare = WGPUCompareFunction_Always;
|
|
depthStencilState.stencilFront.compare = compare;
|
|
depthStencilState.stencilFront.failOp = operation;
|
|
depthStencilState.stencilFront.depthFailOp = operation;
|
|
depthStencilState.stencilFront.passOp = operation;
|
|
depthStencilState.stencilBack.compare = compare;
|
|
depthStencilState.stencilBack.failOp = operation;
|
|
depthStencilState.stencilBack.depthFailOp = operation;
|
|
depthStencilState.stencilBack.passOp = operation;
|
|
depthStencilState.stencilReadMask = 0xFFFFFFFF;
|
|
depthStencilState.stencilWriteMask = 0xFFFFFFFF;
|
|
depthStencilState.depthBias = 0;
|
|
depthStencilState.depthBiasSlopeScale = 0.0f;
|
|
depthStencilState.depthBiasClamp = 0.0f;
|
|
return depthStencilState;
|
|
}
|
|
|
|
|
|
WGPUMultisampleState WgPipeline::makeMultisampleState()
|
|
{
|
|
WGPUMultisampleState multisampleState{};
|
|
multisampleState.nextInChain = nullptr;
|
|
multisampleState.count = 1;
|
|
multisampleState.mask = 0xFFFFFFFF;
|
|
multisampleState.alphaToCoverageEnabled = false;
|
|
return multisampleState;
|
|
}
|
|
|
|
|
|
WGPUFragmentState WgPipeline::makeFragmentState(WGPUShaderModule shaderModule, WGPUColorTargetState* targets, uint32_t size)
|
|
{
|
|
WGPUFragmentState fragmentState{};
|
|
fragmentState.nextInChain = nullptr;
|
|
fragmentState.module = shaderModule;
|
|
fragmentState.entryPoint = "fs_main";
|
|
fragmentState.constantCount = 0;
|
|
fragmentState.constants = nullptr;
|
|
fragmentState.targetCount = size;
|
|
fragmentState.targets = targets;
|
|
return fragmentState;
|
|
}
|
|
|
|
|
|
WGPUPipelineLayout WgPipeline::createPipelineLayout(WGPUDevice device, const WGPUBindGroupLayout* bindGroupLayouts, uint32_t count)
|
|
{
|
|
WGPUPipelineLayoutDescriptor pipelineLayoutDesc{};
|
|
pipelineLayoutDesc.nextInChain = nullptr;
|
|
pipelineLayoutDesc.label = "The Pipeline layout";
|
|
pipelineLayoutDesc.bindGroupLayoutCount = count;
|
|
pipelineLayoutDesc.bindGroupLayouts = bindGroupLayouts;
|
|
return wgpuDeviceCreatePipelineLayout(device, &pipelineLayoutDesc);
|
|
}
|
|
|
|
|
|
WGPUShaderModule WgPipeline::createShaderModule(WGPUDevice device, const char* code, const char* label)
|
|
{
|
|
WGPUShaderModuleWGSLDescriptor shaderModuleWGSLDesc{};
|
|
shaderModuleWGSLDesc.chain.next = nullptr;
|
|
shaderModuleWGSLDesc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
|
|
shaderModuleWGSLDesc.code = code;
|
|
WGPUShaderModuleDescriptor shaderModuleDesc{};
|
|
shaderModuleDesc.nextInChain = &shaderModuleWGSLDesc.chain;
|
|
shaderModuleDesc.label = label;
|
|
shaderModuleDesc.hintCount = 0;
|
|
shaderModuleDesc.hints = nullptr;
|
|
return wgpuDeviceCreateShaderModule(device, &shaderModuleDesc);
|
|
}
|
|
|
|
|
|
WGPURenderPipeline WgPipeline::createRenderPipeline(WGPUDevice device,
|
|
WGPUVertexBufferLayout vertexBufferLayouts[], uint32_t attribsCount,
|
|
WGPUCompareFunction stencilCompareFunction, WGPUStencilOperation stencilOperation,
|
|
WGPUPipelineLayout pipelineLayout, WGPUShaderModule shaderModule,
|
|
const char* pipelineName)
|
|
{
|
|
WGPUBlendState blendState = makeBlendState();
|
|
WGPUColorTargetState colorTargetStates[] = {
|
|
makeColorTargetState(&blendState)
|
|
};
|
|
|
|
WGPUVertexState vertexState = makeVertexState(shaderModule, vertexBufferLayouts, attribsCount);
|
|
WGPUPrimitiveState primitiveState = makePrimitiveState();
|
|
WGPUDepthStencilState depthStencilState = makeDepthStencilState(stencilCompareFunction, stencilOperation);
|
|
WGPUMultisampleState multisampleState = makeMultisampleState();
|
|
WGPUFragmentState fragmentState = makeFragmentState(shaderModule, colorTargetStates, 1);
|
|
|
|
WGPURenderPipelineDescriptor renderPipelineDesc{};
|
|
renderPipelineDesc.nextInChain = nullptr;
|
|
renderPipelineDesc.label = pipelineName;
|
|
renderPipelineDesc.layout = pipelineLayout;
|
|
renderPipelineDesc.vertex = vertexState;
|
|
renderPipelineDesc.primitive = primitiveState;
|
|
renderPipelineDesc.depthStencil = &depthStencilState;
|
|
renderPipelineDesc.multisample = multisampleState;
|
|
renderPipelineDesc.fragment = &fragmentState;
|
|
return wgpuDeviceCreateRenderPipeline(device, &renderPipelineDesc);
|
|
}
|
|
|
|
|
|
void WgPipeline::destroyPipelineLayout(WGPUPipelineLayout& pipelineLayout)
|
|
{
|
|
if (pipelineLayout) wgpuPipelineLayoutRelease(pipelineLayout);
|
|
pipelineLayout = nullptr;
|
|
}
|
|
|
|
|
|
void WgPipeline::destroyShaderModule(WGPUShaderModule& shaderModule)
|
|
{
|
|
if (shaderModule) wgpuShaderModuleRelease(shaderModule);
|
|
shaderModule = nullptr;
|
|
}
|
|
|
|
|
|
void WgPipeline::destroyRenderPipeline(WGPURenderPipeline& renderPipeline)
|
|
{
|
|
if (renderPipeline) wgpuRenderPipelineRelease(renderPipeline);
|
|
renderPipeline = nullptr;
|
|
} |