mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-15 04:24:28 +00:00
wg_engine: composition and blend optimization
* bind groups creation in real time removed - performance boost * blend and composition shaders decomposed - performance boost * shader modules and pipeline layouts generalized - less memory usage * shared single stencil buffer used - less memory usage * bind groups usage simplified * general context API simplified and generalized * all rendering logic moved into new composition class * ready for hardware MSAA (in next steps) * ready for direct mask applience (in next steps)
This commit is contained in:
parent
997093be96
commit
f281cc9e92
21 changed files with 1949 additions and 3716 deletions
2
src/renderer/wg_engine/meson.build
Normal file → Executable file
2
src/renderer/wg_engine/meson.build
Normal file → Executable file
|
@ -1,6 +1,7 @@
|
|||
source_file = [
|
||||
'tvgWgBindGroups.h',
|
||||
'tvgWgCommon.h',
|
||||
'tvgWgCompositor.h',
|
||||
'tvgWgGeometry.h',
|
||||
'tvgWgPipelines.h',
|
||||
'tvgWgRenderData.h',
|
||||
|
@ -10,6 +11,7 @@ source_file = [
|
|||
'tvgWgShaderTypes.h',
|
||||
'tvgWgBindGroups.cpp',
|
||||
'tvgWgCommon.cpp',
|
||||
'tvgWgCompositor.cpp',
|
||||
'tvgWgGeometry.cpp',
|
||||
'tvgWgPipelines.cpp',
|
||||
'tvgWgRenderData.cpp',
|
||||
|
|
1047
src/renderer/wg_engine/tvgWgBindGroups.cpp
Normal file → Executable file
1047
src/renderer/wg_engine/tvgWgBindGroups.cpp
Normal file → Executable file
File diff suppressed because it is too large
Load diff
312
src/renderer/wg_engine/tvgWgBindGroups.h
Normal file → Executable file
312
src/renderer/wg_engine/tvgWgBindGroups.h
Normal file → Executable file
|
@ -24,300 +24,34 @@
|
|||
#define _TVG_WG_BIND_GROUPS_H_
|
||||
|
||||
#include "tvgWgCommon.h"
|
||||
#include "tvgWgShaderTypes.h"
|
||||
|
||||
// @group(0)
|
||||
struct WgBindGroupCanvas : public WgBindGroup
|
||||
{
|
||||
static WGPUBindGroupLayout layout;
|
||||
static WGPUBindGroupLayout getLayout(WGPUDevice device);
|
||||
static void releaseLayout();
|
||||
|
||||
WGPUBuffer uBufferViewMat{};
|
||||
void initialize(WGPUDevice device, WGPUQueue queue,
|
||||
WgShaderTypeMat4x4f& uViewMat);
|
||||
void release();
|
||||
};
|
||||
|
||||
// @group(1)
|
||||
struct WgBindGroupPaint : public WgBindGroup
|
||||
{
|
||||
static WGPUBindGroupLayout layout;
|
||||
static WGPUBindGroupLayout getLayout(WGPUDevice device);
|
||||
static void releaseLayout();
|
||||
|
||||
WGPUBuffer uBufferModelMat{};
|
||||
WGPUBuffer uBufferBlendSettings{};
|
||||
void initialize(WGPUDevice device, WGPUQueue queue,
|
||||
WgShaderTypeMat4x4f& uModelMat,
|
||||
WgShaderTypeBlendSettings& uBlendSettings);
|
||||
void release();
|
||||
};
|
||||
|
||||
// @group(2)
|
||||
struct WgBindGroupSolidColor : public WgBindGroup
|
||||
{
|
||||
static WGPUBindGroupLayout layout;
|
||||
static WGPUBindGroupLayout getLayout(WGPUDevice device);
|
||||
static void releaseLayout();
|
||||
|
||||
WGPUBuffer uBufferSolidColor{};
|
||||
void initialize(WGPUDevice device, WGPUQueue queue,
|
||||
WgShaderTypeSolidColor &uSolidColor);
|
||||
void release();
|
||||
};
|
||||
|
||||
// @group(2)
|
||||
struct WgBindGroupLinearGradient : public WgBindGroup
|
||||
{
|
||||
static WGPUBindGroupLayout layout;
|
||||
static WGPUBindGroupLayout getLayout(WGPUDevice device);
|
||||
static void releaseLayout();
|
||||
|
||||
WGPUBuffer uBufferLinearGradient{};
|
||||
void initialize(WGPUDevice device, WGPUQueue queue,
|
||||
WgShaderTypeLinearGradient &uLinearGradient);
|
||||
void release();
|
||||
};
|
||||
|
||||
// @group(2)
|
||||
struct WgBindGroupRadialGradient : public WgBindGroup
|
||||
{
|
||||
static WGPUBindGroupLayout layout;
|
||||
static WGPUBindGroupLayout getLayout(WGPUDevice device);
|
||||
static void releaseLayout();
|
||||
|
||||
WGPUBuffer uBufferRadialGradient{};
|
||||
void initialize(WGPUDevice device, WGPUQueue queue,
|
||||
WgShaderTypeRadialGradient &uRadialGradient);
|
||||
void release();
|
||||
};
|
||||
|
||||
// @group(2)
|
||||
struct WgBindGroupPicture : public WgBindGroup
|
||||
{
|
||||
static WGPUBindGroupLayout layout;
|
||||
static WGPUBindGroupLayout getLayout(WGPUDevice device);
|
||||
static void releaseLayout();
|
||||
|
||||
void initialize(WGPUDevice device, WGPUQueue queue,
|
||||
WGPUSampler uSampler,
|
||||
WGPUTextureView uTextureView);
|
||||
void release();
|
||||
};
|
||||
|
||||
// @group(0 or 1)
|
||||
struct WgBindGroupTexture : public WgBindGroup
|
||||
{
|
||||
static WGPUBindGroupLayout layout;
|
||||
static WGPUBindGroupLayout getLayout(WGPUDevice device);
|
||||
static void releaseLayout();
|
||||
|
||||
void initialize(WGPUDevice device, WGPUQueue queue,
|
||||
WGPUTextureView uTexture);
|
||||
void release();
|
||||
};
|
||||
|
||||
// @group(0 or 1)
|
||||
struct WgBindGroupTextureStorageRgbaWO : public WgBindGroup
|
||||
{
|
||||
static WGPUBindGroupLayout layout;
|
||||
static WGPUBindGroupLayout getLayout(WGPUDevice device);
|
||||
static void releaseLayout();
|
||||
|
||||
void initialize(WGPUDevice device, WGPUQueue queue,
|
||||
WGPUTextureView uTexture);
|
||||
void release();
|
||||
};
|
||||
|
||||
// @group(0 or 1)
|
||||
struct WgBindGroupTextureStorageRgbaRO : public WgBindGroup
|
||||
{
|
||||
static WGPUBindGroupLayout layout;
|
||||
static WGPUBindGroupLayout getLayout(WGPUDevice device);
|
||||
static void releaseLayout();
|
||||
|
||||
void initialize(WGPUDevice device, WGPUQueue queue,
|
||||
WGPUTextureView uTexture);
|
||||
void release();
|
||||
};
|
||||
|
||||
|
||||
// @group(0 or 1)
|
||||
struct WgBindGroupTextureStorageBgraRO : public WgBindGroup
|
||||
{
|
||||
static WGPUBindGroupLayout layout;
|
||||
static WGPUBindGroupLayout getLayout(WGPUDevice device);
|
||||
static WGPUBindGroupLayout getLayoutRO(WGPUDevice device);
|
||||
static WGPUBindGroupLayout getLayoutWO(WGPUDevice device);
|
||||
static void releaseLayout();
|
||||
|
||||
void initialize(WGPUDevice device, WGPUQueue queue,
|
||||
WGPUTextureView uTexture);
|
||||
void release();
|
||||
};
|
||||
|
||||
|
||||
// @group(0 or 1)
|
||||
struct WgBindGroupTextureStorageBgraWO : public WgBindGroup
|
||||
{
|
||||
static WGPUBindGroupLayout layout;
|
||||
static WGPUBindGroupLayout getLayout(WGPUDevice device);
|
||||
static WGPUBindGroupLayout getLayoutRO(WGPUDevice device);
|
||||
static WGPUBindGroupLayout getLayoutWO(WGPUDevice device);
|
||||
static void releaseLayout();
|
||||
|
||||
void initialize(WGPUDevice device, WGPUQueue queue,
|
||||
WGPUTextureView uTexture);
|
||||
void release();
|
||||
};
|
||||
|
||||
|
||||
// @group(0 or 1)
|
||||
struct WgBindGroupTextureSampled : public WgBindGroup
|
||||
{
|
||||
static WGPUBindGroupLayout layout;
|
||||
static WGPUBindGroupLayout getLayout(WGPUDevice device);
|
||||
static void releaseLayout();
|
||||
|
||||
void initialize(WGPUDevice device, WGPUQueue queue,
|
||||
WGPUSampler uSampler,
|
||||
WGPUTextureView uTexture);
|
||||
void release();
|
||||
};
|
||||
|
||||
// @group(0)
|
||||
struct WgBindGroupTexBlend : public WgBindGroup
|
||||
{
|
||||
static WGPUBindGroupLayout layout;
|
||||
static WGPUBindGroupLayout getLayout(WGPUDevice device);
|
||||
static void releaseLayout();
|
||||
|
||||
void initialize(WGPUDevice device, WGPUQueue queue,
|
||||
WGPUTextureView uTexSrc,
|
||||
WGPUTextureView uTexDst,
|
||||
WGPUTextureView uTexTrg);
|
||||
void release();
|
||||
};
|
||||
|
||||
|
||||
// @group(0)
|
||||
struct WgBindGroupTexBlendMask : public WgBindGroup
|
||||
{
|
||||
static WGPUBindGroupLayout layout;
|
||||
static WGPUBindGroupLayout getLayout(WGPUDevice device);
|
||||
static void releaseLayout();
|
||||
|
||||
void initialize(WGPUDevice device, WGPUQueue queue,
|
||||
WGPUTextureView uTexSrc,
|
||||
WGPUTextureView uTexMsk,
|
||||
WGPUTextureView uTexDst,
|
||||
WGPUTextureView uTexTrg);
|
||||
void release();
|
||||
};
|
||||
|
||||
|
||||
// @group(0)
|
||||
struct WgBindGroupTexMaskCompose : public WgBindGroup
|
||||
{
|
||||
static WGPUBindGroupLayout layout;
|
||||
static WGPUBindGroupLayout getLayout(WGPUDevice device);
|
||||
static void releaseLayout();
|
||||
|
||||
void initialize(WGPUDevice device, WGPUQueue queue,
|
||||
WGPUTextureView uTexMsk0,
|
||||
WGPUTextureView uTexMsk1,
|
||||
WGPUTextureView uTexTrg);
|
||||
void release();
|
||||
};
|
||||
|
||||
|
||||
// @group(0)
|
||||
struct WgBindGroupTexCompose : public WgBindGroup
|
||||
{
|
||||
static WGPUBindGroupLayout layout;
|
||||
static WGPUBindGroupLayout getLayout(WGPUDevice device);
|
||||
static void releaseLayout();
|
||||
|
||||
void initialize(WGPUDevice device, WGPUQueue queue,
|
||||
WGPUTextureView uTexSrc,
|
||||
WGPUTextureView uTexMsk,
|
||||
WGPUTextureView uTexDst,
|
||||
WGPUTextureView uTexTrg);
|
||||
void release();
|
||||
};
|
||||
|
||||
|
||||
// @group(1 or 2)
|
||||
struct WgBindGroupOpacity : public WgBindGroup
|
||||
{
|
||||
static WGPUBindGroupLayout layout;
|
||||
static WGPUBindGroupLayout getLayout(WGPUDevice device);
|
||||
static void releaseLayout();
|
||||
|
||||
WGPUBuffer uBufferOpacity{};
|
||||
void initialize(WGPUDevice device, WGPUQueue queue, uint32_t uOpacity);
|
||||
void release();
|
||||
};
|
||||
|
||||
// @group(2)
|
||||
struct WgBindGroupBlendMethod : public WgBindGroup
|
||||
{
|
||||
static WGPUBindGroupLayout layout;
|
||||
static WGPUBindGroupLayout getLayout(WGPUDevice device);
|
||||
static void releaseLayout();
|
||||
|
||||
WGPUBuffer uBufferBlendMethod{};
|
||||
void initialize(WGPUDevice device, WGPUQueue queue,
|
||||
BlendMethod uBlendMethod);
|
||||
void release();
|
||||
};
|
||||
|
||||
// @group(2)
|
||||
struct WgBindGroupCompositeMethod : public WgBindGroup
|
||||
{
|
||||
static WGPUBindGroupLayout layout;
|
||||
static WGPUBindGroupLayout getLayout(WGPUDevice device);
|
||||
static void releaseLayout();
|
||||
|
||||
WGPUBuffer uBufferCompositeMethod{};
|
||||
void initialize(WGPUDevice device, WGPUQueue queue,
|
||||
CompositeMethod uCompositeMethod);
|
||||
void release();
|
||||
};
|
||||
|
||||
//************************************************************************
|
||||
// bind group pools
|
||||
//************************************************************************
|
||||
|
||||
class WgBindGroupOpacityPool
|
||||
{
|
||||
class WgBindGroupLayouts {
|
||||
private:
|
||||
WgBindGroupOpacity* mPool[256]{};
|
||||
WGPUDevice device{};
|
||||
public:
|
||||
WGPUBindGroupLayout layoutTexSampled{};
|
||||
WGPUBindGroupLayout layoutTexScreen1WO{};
|
||||
WGPUBindGroupLayout layoutTexStrorage1WO{};
|
||||
WGPUBindGroupLayout layoutTexStrorage1RO{};
|
||||
WGPUBindGroupLayout layoutTexStrorage2RO{};
|
||||
WGPUBindGroupLayout layoutTexStrorage3RO{};
|
||||
WGPUBindGroupLayout layoutBuffer1Un{};
|
||||
WGPUBindGroupLayout layoutBuffer2Un{};
|
||||
WGPUBindGroupLayout layoutBuffer3Un{};
|
||||
public:
|
||||
WGPUBindGroup createBindGroupTexSampled(WGPUSampler sampler, WGPUTextureView texView);
|
||||
WGPUBindGroup createBindGroupScreen1WO(WGPUTextureView texView);
|
||||
WGPUBindGroup createBindGroupStrorage1WO(WGPUTextureView texView);
|
||||
WGPUBindGroup createBindGroupStrorage1RO(WGPUTextureView texView);
|
||||
WGPUBindGroup createBindGroupStrorage2RO(WGPUTextureView texView0, WGPUTextureView texView1);
|
||||
WGPUBindGroup createBindGroupStrorage3RO(WGPUTextureView texView0, WGPUTextureView texView1, WGPUTextureView texView2);
|
||||
WGPUBindGroup createBindGroupBuffer1Un(WGPUBuffer buff);
|
||||
WGPUBindGroup createBindGroupBuffer2Un(WGPUBuffer buff0, WGPUBuffer buff1);
|
||||
WGPUBindGroup createBindGroupBuffer3Un(WGPUBuffer buff0, WGPUBuffer buff1, WGPUBuffer buff2);
|
||||
void releaseBindGroup(WGPUBindGroup& bindGroup);
|
||||
public:
|
||||
void initialize(WgContext& context);
|
||||
void release(WgContext& context);
|
||||
WgBindGroupOpacity* allocate(WgContext& context, uint8_t opacity);
|
||||
};
|
||||
|
||||
class WgBindGroupBlendMethodPool
|
||||
{
|
||||
private:
|
||||
WgBindGroupBlendMethod* mPool[(uint8_t)BlendMethod::SoftLight + 1]{};
|
||||
public:
|
||||
void initialize(WgContext& context);
|
||||
void release(WgContext& context);
|
||||
WgBindGroupBlendMethod* allocate(WgContext& context, BlendMethod blendMethod);
|
||||
};
|
||||
|
||||
class WgBindGroupCompositeMethodPool
|
||||
{
|
||||
private:
|
||||
WgBindGroupCompositeMethod* mPool[(uint8_t)CompositeMethod::DifferenceMask + 1]{};
|
||||
public:
|
||||
void initialize(WgContext& context);
|
||||
void release(WgContext& context);
|
||||
WgBindGroupCompositeMethod* allocate(WgContext& context, CompositeMethod composeMethod);
|
||||
};
|
||||
|
||||
#endif // _TVG_WG_BIND_GROUPS_H_
|
||||
|
|
827
src/renderer/wg_engine/tvgWgCommon.cpp
Normal file → Executable file
827
src/renderer/wg_engine/tvgWgCommon.cpp
Normal file → Executable file
|
@ -20,16 +20,13 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "tvgWgCommon.h"
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
#include "tvgWgCommon.h"
|
||||
#include "tvgArray.h"
|
||||
#include <iostream>
|
||||
|
||||
//*****************************************************************************
|
||||
// context
|
||||
//*****************************************************************************
|
||||
|
||||
void WgContext::initialize(WGPUInstance instance, WGPUSurface surface)
|
||||
{
|
||||
assert(instance);
|
||||
|
@ -39,198 +36,119 @@ void WgContext::initialize(WGPUInstance instance, WGPUSurface surface)
|
|||
this->instance = instance;
|
||||
this->surface = surface;
|
||||
|
||||
// request adapter options
|
||||
WGPURequestAdapterOptions requestAdapterOptions{};
|
||||
requestAdapterOptions.nextInChain = nullptr;
|
||||
requestAdapterOptions.compatibleSurface = surface;
|
||||
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
|
||||
const WGPURequestAdapterOptions requestAdapterOptions { .nextInChain = nullptr, .compatibleSurface = surface, .powerPreference = WGPUPowerPreference_HighPerformance, .forceFallbackAdapter = false };
|
||||
auto onAdapterRequestEnded = [](WGPURequestAdapterStatus status, WGPUAdapter adapter, char const * message, void * pUserData) { *((WGPUAdapter*)pUserData) = adapter; };
|
||||
wgpuInstanceRequestAdapter(instance, &requestAdapterOptions, onAdapterRequestEnded, &adapter);
|
||||
#ifdef __EMSCRIPTEN__
|
||||
while (!adapter) emscripten_sleep(10);
|
||||
#endif
|
||||
assert(adapter);
|
||||
|
||||
// get feature names
|
||||
// get adapter and surface properties
|
||||
WGPUFeatureName featureNames[32]{};
|
||||
size_t featuresCount = wgpuAdapterEnumerateFeatures(adapter, featureNames);
|
||||
preferredFormat = wgpuSurfaceGetPreferredFormat(surface, adapter);
|
||||
|
||||
// request device
|
||||
WGPUDeviceDescriptor deviceDesc{};
|
||||
deviceDesc.nextInChain = nullptr;
|
||||
deviceDesc.label = "The device";
|
||||
deviceDesc.requiredFeatureCount = 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
|
||||
const WGPUDeviceDescriptor deviceDesc { .nextInChain = nullptr, .label = "The device", .requiredFeatureCount = featuresCount, .requiredFeatures = featureNames };
|
||||
auto onDeviceRequestEnded = [](WGPURequestDeviceStatus status, WGPUDevice device, char const * message, void * pUserData) { *((WGPUDevice*)pUserData) = device; };
|
||||
wgpuAdapterRequestDevice(adapter, &deviceDesc, onDeviceRequestEnded, &device);
|
||||
#ifdef __EMSCRIPTEN__
|
||||
while (!device) emscripten_sleep(10);
|
||||
#endif
|
||||
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
|
||||
// device uncaptured error callback
|
||||
auto onDeviceError = [](WGPUErrorType type, char const* message, void* pUserData) { std::cout << message << std::endl; };
|
||||
wgpuDeviceSetUncapturedErrorCallback(device, onDeviceError, nullptr);
|
||||
|
||||
// get current queue
|
||||
queue = wgpuDeviceGetQueue(device);
|
||||
assert(queue);
|
||||
|
||||
// create default nearest and linear samplers
|
||||
|
||||
// create shared webgpu assets
|
||||
allocateBufferIndexFan(32768);
|
||||
samplerNearest = createSampler(WGPUFilterMode_Nearest, WGPUMipmapFilterMode_Nearest);
|
||||
assert(samplerNearest);
|
||||
samplerLinear = createSampler(WGPUFilterMode_Linear, WGPUMipmapFilterMode_Linear);
|
||||
assert(samplerNearest);
|
||||
assert(samplerLinear);
|
||||
allocateIndexBufferFan(1024);
|
||||
assert(indexBufferFan);
|
||||
}
|
||||
|
||||
|
||||
void WgContext::release()
|
||||
{
|
||||
releaseBuffer(indexBufferFan);
|
||||
releaseSampler(samplerNearest);
|
||||
releaseSampler(samplerLinear);
|
||||
if (device) {
|
||||
wgpuDeviceDestroy(device);
|
||||
wgpuDeviceRelease(device);
|
||||
device = nullptr;
|
||||
}
|
||||
if (adapter) {
|
||||
wgpuAdapterRelease(adapter);
|
||||
adapter = nullptr;
|
||||
}
|
||||
if (surface) {
|
||||
wgpuSurfaceUnconfigure(surface);
|
||||
wgpuSurfaceRelease(surface);
|
||||
surface = nullptr;
|
||||
}
|
||||
if (instance) {
|
||||
wgpuInstanceRelease(instance);
|
||||
instance = nullptr;
|
||||
}
|
||||
releaseBuffer(bufferIndexFan);
|
||||
wgpuDeviceRelease(device);
|
||||
wgpuAdapterRelease(adapter);
|
||||
}
|
||||
|
||||
|
||||
void WgContext::executeCommandEncoder(WGPUCommandEncoder commandEncoder)
|
||||
WGPUSampler WgContext::createSampler(WGPUFilterMode filter, WGPUMipmapFilterMode mipmapFilter)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
WGPUSampler WgContext::createSampler(WGPUFilterMode minFilter, WGPUMipmapFilterMode mipmapFilter)
|
||||
{
|
||||
WGPUSamplerDescriptor samplerDesc{};
|
||||
samplerDesc.nextInChain = nullptr;
|
||||
samplerDesc.label = "The sampler";
|
||||
samplerDesc.addressModeU = WGPUAddressMode_ClampToEdge;
|
||||
samplerDesc.addressModeV = WGPUAddressMode_ClampToEdge;
|
||||
samplerDesc.addressModeW = WGPUAddressMode_ClampToEdge;
|
||||
samplerDesc.magFilter = minFilter;
|
||||
samplerDesc.minFilter = minFilter;
|
||||
samplerDesc.mipmapFilter = mipmapFilter;
|
||||
samplerDesc.lodMinClamp = 0.0f;
|
||||
samplerDesc.lodMaxClamp = 32.0f;
|
||||
samplerDesc.compare = WGPUCompareFunction_Undefined;
|
||||
samplerDesc.maxAnisotropy = 1;
|
||||
const WGPUSamplerDescriptor samplerDesc {
|
||||
.magFilter = filter, .minFilter = filter, .mipmapFilter = mipmapFilter,
|
||||
.lodMinClamp = 0.0f, .lodMaxClamp = 32.0f, .maxAnisotropy = 1
|
||||
};
|
||||
return wgpuDeviceCreateSampler(device, &samplerDesc);
|
||||
}
|
||||
|
||||
|
||||
WGPUTexture WgContext::createTexture2d(WGPUTextureUsageFlags usage, WGPUTextureFormat format, uint32_t width, uint32_t height, char const * label) {
|
||||
WGPUTextureDescriptor textureDesc{};
|
||||
textureDesc.nextInChain = nullptr;
|
||||
textureDesc.label = label;
|
||||
textureDesc.usage = usage;
|
||||
textureDesc.dimension = WGPUTextureDimension_2D;
|
||||
textureDesc.size = { width, height, 1 };
|
||||
textureDesc.format = format;
|
||||
textureDesc.mipLevelCount = 1;
|
||||
textureDesc.sampleCount = 1;
|
||||
textureDesc.viewFormatCount = 0;
|
||||
textureDesc.viewFormats = nullptr;
|
||||
WGPUTexture WgContext::createTexture(uint32_t width, uint32_t height, WGPUTextureFormat format)
|
||||
{
|
||||
const WGPUTextureDescriptor textureDesc {
|
||||
.usage = WGPUTextureUsage_CopyDst | WGPUTextureUsage_TextureBinding,
|
||||
.dimension = WGPUTextureDimension_2D, .size = { width, height, 1 },
|
||||
.format = format, .mipLevelCount = 1, .sampleCount = 1
|
||||
};
|
||||
return wgpuDeviceCreateTexture(device, &textureDesc);
|
||||
}
|
||||
|
||||
|
||||
WGPUTexture WgContext::createTexture2dMS(WGPUTextureUsageFlags usage, WGPUTextureFormat format, uint32_t width, uint32_t height, uint32_t sc, char const * label)
|
||||
WGPUTexture WgContext::createTexStorage(uint32_t width, uint32_t height, WGPUTextureFormat format, uint32_t sc)
|
||||
{
|
||||
WGPUTextureDescriptor textureDesc{};
|
||||
textureDesc.nextInChain = nullptr;
|
||||
textureDesc.label = label;
|
||||
textureDesc.usage = usage;
|
||||
textureDesc.dimension = WGPUTextureDimension_2D;
|
||||
textureDesc.size = { width, height, 1 };
|
||||
textureDesc.format = format;
|
||||
textureDesc.mipLevelCount = 1;
|
||||
textureDesc.sampleCount = sc;
|
||||
textureDesc.viewFormatCount = 0;
|
||||
textureDesc.viewFormats = nullptr;
|
||||
const WGPUTextureDescriptor textureDesc {
|
||||
.usage = WGPUTextureUsage_CopySrc | WGPUTextureUsage_CopyDst | WGPUTextureUsage_TextureBinding | WGPUTextureUsage_StorageBinding | WGPUTextureUsage_RenderAttachment,
|
||||
.dimension = WGPUTextureDimension_2D, .size = { width, height, 1 },
|
||||
.format = format, .mipLevelCount = 1, .sampleCount = sc
|
||||
};
|
||||
return wgpuDeviceCreateTexture(device, &textureDesc);
|
||||
}
|
||||
|
||||
|
||||
WGPUTextureView WgContext::createTextureView2d(WGPUTexture texture, char const * label)
|
||||
WGPUTexture WgContext::createTexStencil(uint32_t width, uint32_t height, WGPUTextureFormat format, uint32_t sc)
|
||||
{
|
||||
WGPUTextureViewDescriptor textureViewDescColor{};
|
||||
textureViewDescColor.nextInChain = nullptr;
|
||||
textureViewDescColor.label = label;
|
||||
textureViewDescColor.format = wgpuTextureGetFormat(texture);
|
||||
textureViewDescColor.dimension = WGPUTextureViewDimension_2D;
|
||||
textureViewDescColor.baseMipLevel = 0;
|
||||
textureViewDescColor.mipLevelCount = 1;
|
||||
textureViewDescColor.baseArrayLayer = 0;
|
||||
textureViewDescColor.arrayLayerCount = 1;
|
||||
textureViewDescColor.aspect = WGPUTextureAspect_All;
|
||||
return wgpuTextureCreateView(texture, &textureViewDescColor);
|
||||
};
|
||||
|
||||
|
||||
WGPUBuffer WgContext::createBuffer(WGPUBufferUsageFlags usage, uint64_t size,char const * label)
|
||||
{
|
||||
WGPUBufferDescriptor bufferDesc{};
|
||||
bufferDesc.nextInChain = nullptr;
|
||||
bufferDesc.label = label;
|
||||
bufferDesc.usage = usage;
|
||||
bufferDesc.size = size;
|
||||
bufferDesc.mappedAtCreation = false;
|
||||
return wgpuDeviceCreateBuffer(device, &bufferDesc);
|
||||
const WGPUTextureDescriptor textureDesc {
|
||||
.usage = WGPUTextureUsage_RenderAttachment,
|
||||
.dimension = WGPUTextureDimension_2D, .size = { width, height, 1 },
|
||||
.format = format, .mipLevelCount = 1, .sampleCount = sc
|
||||
};
|
||||
return wgpuDeviceCreateTexture(device, &textureDesc);
|
||||
}
|
||||
|
||||
|
||||
void WgContext::releaseSampler(WGPUSampler& sampler)
|
||||
WGPUTextureView WgContext::createTextureView(WGPUTexture texture)
|
||||
{
|
||||
if (sampler) {
|
||||
wgpuSamplerRelease(sampler);
|
||||
sampler = nullptr;
|
||||
const WGPUTextureViewDescriptor textureViewDesc {
|
||||
.format = wgpuTextureGetFormat(texture),
|
||||
.dimension = WGPUTextureViewDimension_2D,
|
||||
.baseMipLevel = 0,
|
||||
.mipLevelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.arrayLayerCount = 1,
|
||||
.aspect = WGPUTextureAspect_All
|
||||
};
|
||||
return wgpuTextureCreateView(texture, &textureViewDesc);
|
||||
}
|
||||
|
||||
|
||||
void WgContext::releaseTextureView(WGPUTextureView& textureView)
|
||||
{
|
||||
if (textureView) {
|
||||
wgpuTextureViewRelease(textureView);
|
||||
textureView = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -246,15 +164,89 @@ void WgContext::releaseTexture(WGPUTexture& texture)
|
|||
}
|
||||
|
||||
|
||||
void WgContext::releaseTextureView(WGPUTextureView& textureView)
|
||||
void WgContext::releaseSampler(WGPUSampler& sampler)
|
||||
{
|
||||
if (textureView) {
|
||||
wgpuTextureViewRelease(textureView);
|
||||
textureView = nullptr;
|
||||
if (sampler) {
|
||||
wgpuSamplerRelease(sampler);
|
||||
sampler = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool WgContext::allocateBufferUniform(WGPUBuffer& buffer, const void* data, uint64_t size)
|
||||
{
|
||||
if ((buffer) && (wgpuBufferGetSize(buffer) >= size))
|
||||
wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
|
||||
else {
|
||||
releaseBuffer(buffer);
|
||||
const WGPUBufferDescriptor bufferDesc { .usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform, .size = size };
|
||||
buffer = wgpuDeviceCreateBuffer(device, &bufferDesc);
|
||||
wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool WgContext::allocateBufferVertex(WGPUBuffer& buffer, const float* data, uint64_t size)
|
||||
{
|
||||
if ((buffer) && (wgpuBufferGetSize(buffer) >= size))
|
||||
wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
|
||||
else {
|
||||
releaseBuffer(buffer);
|
||||
const WGPUBufferDescriptor bufferDesc {
|
||||
.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex,
|
||||
.size = size > WG_VERTEX_BUFFER_MIN_SIZE ? size : WG_VERTEX_BUFFER_MIN_SIZE
|
||||
};
|
||||
buffer = wgpuDeviceCreateBuffer(device, &bufferDesc);
|
||||
wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool WgContext::allocateBufferIndex(WGPUBuffer& buffer, const uint32_t* data, uint64_t size)
|
||||
{
|
||||
if ((buffer) && (wgpuBufferGetSize(buffer) >= size))
|
||||
wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
|
||||
else {
|
||||
releaseBuffer(buffer);
|
||||
const WGPUBufferDescriptor bufferDesc {
|
||||
.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index,
|
||||
.size = size > WG_INDEX_BUFFER_MIN_SIZE ? size : WG_INDEX_BUFFER_MIN_SIZE
|
||||
};
|
||||
buffer = wgpuDeviceCreateBuffer(device, &bufferDesc);
|
||||
wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool WgContext::allocateBufferIndexFan(uint64_t vertexCount)
|
||||
{
|
||||
uint64_t indexCount = (vertexCount - 2) * 3;
|
||||
if ((!bufferIndexFan) || (wgpuBufferGetSize(bufferIndexFan) < indexCount * sizeof(uint32_t))) {
|
||||
tvg::Array<uint32_t> indexes(indexCount);
|
||||
for (size_t i = 0; i < vertexCount - 2; i++) {
|
||||
indexes.push(0);
|
||||
indexes.push(i + 1);
|
||||
indexes.push(i + 2);
|
||||
}
|
||||
releaseBuffer(bufferIndexFan);
|
||||
WGPUBufferDescriptor bufferDesc{
|
||||
.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index,
|
||||
.size = indexCount * sizeof(uint32_t)
|
||||
};
|
||||
bufferIndexFan = wgpuDeviceCreateBuffer(device, &bufferDesc);
|
||||
wgpuQueueWriteBuffer(queue, bufferIndexFan, 0, &indexes[0], indexCount * sizeof(uint32_t));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void WgContext::releaseBuffer(WGPUBuffer& buffer)
|
||||
{
|
||||
if (buffer) {
|
||||
|
@ -263,540 +255,3 @@ void WgContext::releaseBuffer(WGPUBuffer& buffer)
|
|||
buffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WgContext::allocateVertexBuffer(WGPUBuffer& buffer, const void *data, uint64_t size)
|
||||
{
|
||||
if ((buffer) && (wgpuBufferGetSize(buffer) >= size))
|
||||
wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
|
||||
else {
|
||||
// create new buffer and upload data
|
||||
releaseBuffer(buffer);
|
||||
WGPUBufferDescriptor bufferDesc{};
|
||||
bufferDesc.nextInChain = nullptr;
|
||||
bufferDesc.label = "The vertex buffer";
|
||||
bufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex;
|
||||
bufferDesc.size = size > WG_VERTEX_BUFFER_MIN_SIZE ? size : WG_VERTEX_BUFFER_MIN_SIZE;
|
||||
bufferDesc.mappedAtCreation = false;
|
||||
buffer = wgpuDeviceCreateBuffer(device, &bufferDesc);
|
||||
wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WgContext::allocateIndexBuffer(WGPUBuffer& buffer, const void *data, uint64_t size)
|
||||
{
|
||||
if ((buffer) && (wgpuBufferGetSize(buffer) >= size))
|
||||
wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
|
||||
else {
|
||||
// create new buffer and upload data
|
||||
releaseBuffer(buffer);
|
||||
WGPUBufferDescriptor bufferDesc{};
|
||||
bufferDesc.nextInChain = nullptr;
|
||||
bufferDesc.label = "The index buffer";
|
||||
bufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index;
|
||||
bufferDesc.size = size > WG_INDEX_BUFFER_MIN_SIZE ? size : WG_INDEX_BUFFER_MIN_SIZE;
|
||||
bufferDesc.mappedAtCreation = false;
|
||||
buffer = wgpuDeviceCreateBuffer(device, &bufferDesc);
|
||||
wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WgContext::allocateIndexBufferFan(uint64_t vertsCount)
|
||||
{
|
||||
assert(vertsCount >= 3);
|
||||
uint64_t indexCount = (vertsCount - 2) * 3;
|
||||
if ((!indexBufferFan) || (wgpuBufferGetSize(indexBufferFan) < indexCount * sizeof(uint32_t))) {
|
||||
Array<uint32_t> indexes(indexCount);
|
||||
for (size_t i = 0; i < vertsCount - 2; i++) {
|
||||
indexes.push(0);
|
||||
indexes.push(i + 1);
|
||||
indexes.push(i + 2);
|
||||
}
|
||||
releaseBuffer(indexBufferFan);
|
||||
WGPUBufferDescriptor bufferDesc{};
|
||||
bufferDesc.nextInChain = nullptr;
|
||||
bufferDesc.label = "The index buffer";
|
||||
bufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index;
|
||||
bufferDesc.size = indexCount * sizeof(uint32_t);
|
||||
bufferDesc.mappedAtCreation = false;
|
||||
indexBufferFan = wgpuDeviceCreateBuffer(device, &bufferDesc);
|
||||
wgpuQueueWriteBuffer(queue, indexBufferFan, 0, &indexes[0], indexCount * sizeof(uint32_t));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WgContext::releaseVertexBuffer(WGPUBuffer& buffer)
|
||||
{
|
||||
releaseBuffer(buffer);
|
||||
}
|
||||
|
||||
|
||||
void WgContext::releaseIndexBuffer(WGPUBuffer& buffer)
|
||||
{
|
||||
releaseBuffer(buffer);
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
// bind group
|
||||
//*****************************************************************************
|
||||
|
||||
void WgBindGroup::set(WGPURenderPassEncoder encoder, uint32_t groupIndex)
|
||||
{
|
||||
wgpuRenderPassEncoderSetBindGroup(encoder, groupIndex, mBindGroup, 0, nullptr);
|
||||
}
|
||||
|
||||
|
||||
void WgBindGroup::set(WGPUComputePassEncoder encoder, uint32_t groupIndex)
|
||||
{
|
||||
wgpuComputePassEncoderSetBindGroup(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 | WGPUShaderStage_Compute;
|
||||
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::makeBindGroupLayoutEntryTexture(uint32_t binding)
|
||||
{
|
||||
WGPUBindGroupLayoutEntry bindGroupLayoutEntry{};
|
||||
bindGroupLayoutEntry.nextInChain = nullptr;
|
||||
bindGroupLayoutEntry.binding = binding;
|
||||
bindGroupLayoutEntry.visibility = WGPUShaderStage_Fragment | WGPUShaderStage_Compute;
|
||||
bindGroupLayoutEntry.texture.nextInChain = nullptr;
|
||||
bindGroupLayoutEntry.texture.sampleType = WGPUTextureSampleType_Float;
|
||||
bindGroupLayoutEntry.texture.viewDimension = WGPUTextureViewDimension_2D;
|
||||
bindGroupLayoutEntry.texture.multisampled = false;
|
||||
return bindGroupLayoutEntry;
|
||||
}
|
||||
|
||||
|
||||
WGPUBindGroupLayoutEntry WgBindGroup::makeBindGroupLayoutEntryStorage(uint32_t binding, WGPUStorageTextureAccess access, WGPUTextureFormat format)
|
||||
{
|
||||
WGPUBindGroupLayoutEntry bindGroupLayoutEntry{};
|
||||
bindGroupLayoutEntry.nextInChain = nullptr;
|
||||
bindGroupLayoutEntry.binding = binding;
|
||||
bindGroupLayoutEntry.visibility = WGPUShaderStage_Fragment | WGPUShaderStage_Compute;
|
||||
bindGroupLayoutEntry.storageTexture.nextInChain = nullptr;
|
||||
bindGroupLayoutEntry.storageTexture.access = access;
|
||||
bindGroupLayoutEntry.storageTexture.format = format;
|
||||
bindGroupLayoutEntry.storageTexture.viewDimension = WGPUTextureViewDimension_2D;
|
||||
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";
|
||||
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::release()
|
||||
{
|
||||
destroyShaderModule(mShaderModule);
|
||||
destroyPipelineLayout(mPipelineLayout);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
#ifndef __EMSCRIPTEN__
|
||||
shaderModuleDesc.hintCount = 0;
|
||||
shaderModuleDesc.hints = nullptr;
|
||||
#endif
|
||||
return wgpuDeviceCreateShaderModule(device, &shaderModuleDesc);
|
||||
}
|
||||
|
||||
|
||||
void WgPipeline::destroyPipelineLayout(WGPUPipelineLayout& pipelineLayout)
|
||||
{
|
||||
if (pipelineLayout) wgpuPipelineLayoutRelease(pipelineLayout);
|
||||
pipelineLayout = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void WgPipeline::destroyShaderModule(WGPUShaderModule& shaderModule)
|
||||
{
|
||||
if (shaderModule) wgpuShaderModuleRelease(shaderModule);
|
||||
shaderModule = nullptr;
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
// render pipeline
|
||||
//*****************************************************************************
|
||||
|
||||
void WgRenderPipeline::allocate(WGPUDevice device, WgPipelineBlendType blendType, WGPUColorWriteMask writeMask,
|
||||
WGPUVertexBufferLayout vertexBufferLayouts[], uint32_t attribsCount,
|
||||
WGPUBindGroupLayout bindGroupLayouts[], uint32_t bindGroupsCount,
|
||||
WGPUCompareFunction compareFront, WGPUStencilOperation operationFront,
|
||||
WGPUCompareFunction compareBack, WGPUStencilOperation operationBack,
|
||||
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, blendType, writeMask,
|
||||
vertexBufferLayouts, attribsCount,
|
||||
compareFront, operationFront,
|
||||
compareBack, operationBack,
|
||||
mPipelineLayout, mShaderModule, pipelineLabel);
|
||||
assert(mRenderPipeline);
|
||||
}
|
||||
|
||||
|
||||
void WgRenderPipeline::release()
|
||||
{
|
||||
destroyRenderPipeline(mRenderPipeline);
|
||||
WgPipeline::release();
|
||||
}
|
||||
|
||||
|
||||
void WgRenderPipeline::set(WGPURenderPassEncoder renderPassEncoder)
|
||||
{
|
||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, mRenderPipeline);
|
||||
};
|
||||
|
||||
|
||||
WGPUBlendState WgRenderPipeline::makeBlendState(WgPipelineBlendType blendType)
|
||||
{
|
||||
WGPUBlendState blendState{};
|
||||
// srcover
|
||||
if (blendType == WgPipelineBlendType::SrcOver) {
|
||||
blendState.color.operation = WGPUBlendOperation_Add;
|
||||
blendState.color.srcFactor = WGPUBlendFactor_One;
|
||||
blendState.color.dstFactor = WGPUBlendFactor_Zero;
|
||||
// normal
|
||||
} else if (blendType == WgPipelineBlendType::Normal) {
|
||||
blendState.color.operation = WGPUBlendOperation_Add;
|
||||
blendState.color.srcFactor = WGPUBlendFactor_One;
|
||||
blendState.color.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha;
|
||||
// custom
|
||||
} else if (blendType == WgPipelineBlendType::Custom) {
|
||||
blendState.color.operation = WGPUBlendOperation_Add;
|
||||
blendState.color.srcFactor = WGPUBlendFactor_One;
|
||||
blendState.color.dstFactor = WGPUBlendFactor_Zero;
|
||||
}
|
||||
blendState.alpha = blendState.color;
|
||||
return blendState;
|
||||
}
|
||||
|
||||
|
||||
WGPUColorTargetState WgRenderPipeline::makeColorTargetState(const WGPUBlendState* blendState, const WGPUColorWriteMask writeMask)
|
||||
{
|
||||
WGPUColorTargetState colorTargetState{};
|
||||
colorTargetState.nextInChain = nullptr;
|
||||
colorTargetState.format = WGPUTextureFormat_RGBA8Unorm;
|
||||
colorTargetState.blend = blendState;
|
||||
colorTargetState.writeMask = writeMask;
|
||||
return colorTargetState;
|
||||
}
|
||||
|
||||
|
||||
WGPUVertexBufferLayout WgRenderPipeline::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 WgRenderPipeline::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 WgRenderPipeline::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 WgRenderPipeline::makeDepthStencilState(WGPUCompareFunction compareFront, WGPUStencilOperation operationFront, WGPUCompareFunction compareBack, WGPUStencilOperation operationBack)
|
||||
{
|
||||
WGPUDepthStencilState depthStencilState{};
|
||||
depthStencilState.nextInChain = nullptr;
|
||||
depthStencilState.format = WGPUTextureFormat_Stencil8;
|
||||
depthStencilState.depthWriteEnabled = false;
|
||||
depthStencilState.depthCompare = WGPUCompareFunction_Always;
|
||||
depthStencilState.stencilFront.compare = compareFront;
|
||||
depthStencilState.stencilFront.failOp = operationFront;
|
||||
depthStencilState.stencilFront.depthFailOp = operationFront;
|
||||
depthStencilState.stencilFront.passOp = operationFront;
|
||||
depthStencilState.stencilBack.compare = compareBack;
|
||||
depthStencilState.stencilBack.failOp = operationBack;
|
||||
depthStencilState.stencilBack.depthFailOp = operationBack;
|
||||
depthStencilState.stencilBack.passOp = operationBack;
|
||||
depthStencilState.stencilReadMask = 0xFFFFFFFF;
|
||||
depthStencilState.stencilWriteMask = 0xFFFFFFFF;
|
||||
depthStencilState.depthBias = 0;
|
||||
depthStencilState.depthBiasSlopeScale = 0.0f;
|
||||
depthStencilState.depthBiasClamp = 0.0f;
|
||||
return depthStencilState;
|
||||
}
|
||||
|
||||
|
||||
WGPUMultisampleState WgRenderPipeline::makeMultisampleState()
|
||||
{
|
||||
WGPUMultisampleState multisampleState{};
|
||||
multisampleState.nextInChain = nullptr;
|
||||
multisampleState.count = 1;
|
||||
multisampleState.mask = 0xFFFFFFFF;
|
||||
multisampleState.alphaToCoverageEnabled = false;
|
||||
return multisampleState;
|
||||
}
|
||||
|
||||
|
||||
WGPUFragmentState WgRenderPipeline::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;
|
||||
}
|
||||
|
||||
|
||||
WGPURenderPipeline WgRenderPipeline::createRenderPipeline(WGPUDevice device, WgPipelineBlendType blendType, WGPUColorWriteMask writeMask,
|
||||
WGPUVertexBufferLayout vertexBufferLayouts[], uint32_t attribsCount,
|
||||
WGPUCompareFunction compareFront, WGPUStencilOperation operationFront,
|
||||
WGPUCompareFunction compareBack, WGPUStencilOperation operationBack,
|
||||
WGPUPipelineLayout pipelineLayout, WGPUShaderModule shaderModule,
|
||||
const char* pipelineName)
|
||||
{
|
||||
WGPUBlendState blendState = makeBlendState(blendType);
|
||||
WGPUColorTargetState colorTargetStates[] = {
|
||||
makeColorTargetState(&blendState, writeMask)
|
||||
};
|
||||
|
||||
WGPUVertexState vertexState = makeVertexState(shaderModule, vertexBufferLayouts, attribsCount);
|
||||
WGPUPrimitiveState primitiveState = makePrimitiveState();
|
||||
WGPUDepthStencilState depthStencilState = makeDepthStencilState(compareFront, operationFront, compareBack, operationBack);
|
||||
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 WgRenderPipeline::destroyRenderPipeline(WGPURenderPipeline& renderPipeline)
|
||||
{
|
||||
if (renderPipeline) wgpuRenderPipelineRelease(renderPipeline);
|
||||
renderPipeline = nullptr;
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
// compute pipeline
|
||||
//*****************************************************************************
|
||||
|
||||
void WgComputePipeline::allocate(WGPUDevice device,
|
||||
WGPUBindGroupLayout bindGroupLayouts[], uint32_t bindGroupsCount,
|
||||
const char* shaderSource, const char* shaderLabel, const char* pipelineLabel)
|
||||
{
|
||||
mShaderModule = createShaderModule(device, shaderSource, shaderLabel);
|
||||
assert(mShaderModule);
|
||||
|
||||
mPipelineLayout = createPipelineLayout(device, bindGroupLayouts, bindGroupsCount);
|
||||
assert(mPipelineLayout);
|
||||
|
||||
WGPUComputePipelineDescriptor computePipelineDesc{};
|
||||
computePipelineDesc.nextInChain = nullptr;
|
||||
computePipelineDesc.label = pipelineLabel;
|
||||
computePipelineDesc.layout = mPipelineLayout;
|
||||
computePipelineDesc.compute.nextInChain = nullptr;
|
||||
computePipelineDesc.compute.module = mShaderModule;
|
||||
computePipelineDesc.compute.entryPoint = "cs_main";
|
||||
computePipelineDesc.compute.constantCount = 0;
|
||||
computePipelineDesc.compute.constants = nullptr;
|
||||
|
||||
mComputePipeline = wgpuDeviceCreateComputePipeline(device, &computePipelineDesc);
|
||||
assert(mComputePipeline);
|
||||
}
|
||||
|
||||
void WgComputePipeline::release()
|
||||
{
|
||||
if (mComputePipeline) wgpuComputePipelineRelease(mComputePipeline);
|
||||
mComputePipeline = nullptr;
|
||||
WgPipeline::release();
|
||||
}
|
||||
|
||||
|
||||
void WgComputePipeline::set(WGPUComputePassEncoder computePassEncoder)
|
||||
{
|
||||
wgpuComputePassEncoderSetPipeline(computePassEncoder, mComputePipeline);
|
||||
}
|
||||
|
|
152
src/renderer/wg_engine/tvgWgCommon.h
Normal file → Executable file
152
src/renderer/wg_engine/tvgWgCommon.h
Normal file → Executable file
|
@ -25,151 +25,51 @@
|
|||
|
||||
#include <cassert>
|
||||
#include <webgpu/webgpu.h>
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgRender.h"
|
||||
|
||||
#define WG_VERTEX_BUFFER_MIN_SIZE 2048
|
||||
#define WG_INDEX_BUFFER_MIN_SIZE 2048
|
||||
|
||||
struct WgCompositor: public Compositor {
|
||||
BlendMethod blendMethod;
|
||||
};
|
||||
|
||||
enum class WgPipelineBlendType {
|
||||
SrcOver = 0,
|
||||
Normal,
|
||||
Custom
|
||||
};
|
||||
|
||||
struct WgPipelines;
|
||||
class WgPipelines;
|
||||
|
||||
struct WgContext {
|
||||
// external webgpu handles
|
||||
WGPUInstance instance{};
|
||||
WGPUSurface surface{};
|
||||
// common webgpu handles
|
||||
WGPUAdapter adapter{};
|
||||
WGPUDevice device{};
|
||||
WGPUQueue queue{};
|
||||
|
||||
WGPUFeatureName featureNames[32]{};
|
||||
WGPUAdapterProperties adapterProperties{};
|
||||
WGPUSupportedLimits supportedLimits{};
|
||||
|
||||
WGPUTextureFormat preferredFormat{};
|
||||
// external handles (do not release)
|
||||
WgPipelines* pipelines{};
|
||||
// shared webgpu assets
|
||||
WGPUBuffer bufferIndexFan{};
|
||||
WGPUSampler samplerNearest{};
|
||||
WGPUSampler samplerLinear{};
|
||||
WGPUBuffer indexBufferFan{};
|
||||
|
||||
WgPipelines* pipelines{}; // external handle (do not release)
|
||||
|
||||
void initialize(WGPUInstance instance, WGPUSurface surface);
|
||||
void release();
|
||||
|
||||
// create common objects
|
||||
WGPUSampler createSampler(WGPUFilterMode filter, WGPUMipmapFilterMode mipmapFilter);
|
||||
WGPUTexture createTexture(uint32_t width, uint32_t height, WGPUTextureFormat format);
|
||||
WGPUTexture createTexStorage(uint32_t width, uint32_t height, WGPUTextureFormat format, uint32_t sc = 1);
|
||||
WGPUTexture createTexStencil(uint32_t width, uint32_t height, WGPUTextureFormat format, uint32_t sc = 1);
|
||||
WGPUTextureView createTextureView(WGPUTexture texture);
|
||||
|
||||
void executeCommandEncoder(WGPUCommandEncoder commandEncoder);
|
||||
|
||||
WGPUSampler createSampler(WGPUFilterMode minFilter, WGPUMipmapFilterMode mipmapFilter);
|
||||
WGPUTexture createTexture2d(WGPUTextureUsageFlags usage, WGPUTextureFormat format, uint32_t width, uint32_t height, char const * label);
|
||||
WGPUTexture createTexture2dMS(WGPUTextureUsageFlags usage, WGPUTextureFormat format, uint32_t width, uint32_t height, uint32_t sc, char const * label);
|
||||
WGPUTextureView createTextureView2d(WGPUTexture texture, char const * label);
|
||||
WGPUBuffer createBuffer(WGPUBufferUsageFlags usage, uint64_t size, char const * label);
|
||||
|
||||
void releaseSampler(WGPUSampler& sampler);
|
||||
void releaseTexture(WGPUTexture& texture);
|
||||
// release common objects
|
||||
void releaseTextureView(WGPUTextureView& textureView);
|
||||
void releaseTexture(WGPUTexture& texture);
|
||||
void releaseSampler(WGPUSampler& sampler);
|
||||
|
||||
// create buffer objects (return true, if buffer handle was changed)
|
||||
bool allocateBufferUniform(WGPUBuffer& buffer, const void* data, uint64_t size);
|
||||
bool allocateBufferVertex(WGPUBuffer& buffer, const float* data, uint64_t size);
|
||||
bool allocateBufferIndex(WGPUBuffer& buffer, const uint32_t* data, uint64_t size);
|
||||
bool allocateBufferIndexFan(uint64_t vertexCount);
|
||||
|
||||
// release buffer objects
|
||||
void releaseBuffer(WGPUBuffer& buffer);
|
||||
|
||||
void allocateVertexBuffer(WGPUBuffer& buffer, const void *data, uint64_t size);
|
||||
void allocateIndexBuffer(WGPUBuffer& buffer, const void *data, uint64_t size);
|
||||
void allocateIndexBufferFan(uint64_t size);
|
||||
void releaseVertexBuffer(WGPUBuffer& buffer);
|
||||
void releaseIndexBuffer(WGPUBuffer& buffer);
|
||||
};
|
||||
|
||||
struct WgBindGroup
|
||||
{
|
||||
WGPUBindGroup mBindGroup{};
|
||||
|
||||
void set(WGPURenderPassEncoder encoder, uint32_t groupIndex);
|
||||
void set(WGPUComputePassEncoder encoder, uint32_t groupIndex);
|
||||
|
||||
static WGPUBindGroupEntry makeBindGroupEntryBuffer(uint32_t binding, WGPUBuffer buffer);
|
||||
static WGPUBindGroupEntry makeBindGroupEntrySampler(uint32_t binding, WGPUSampler sampler);
|
||||
static WGPUBindGroupEntry makeBindGroupEntryTextureView(uint32_t binding, WGPUTextureView textureView);
|
||||
|
||||
static WGPUBindGroupLayoutEntry makeBindGroupLayoutEntryBuffer(uint32_t binding);
|
||||
static WGPUBindGroupLayoutEntry makeBindGroupLayoutEntrySampler(uint32_t binding);
|
||||
static WGPUBindGroupLayoutEntry makeBindGroupLayoutEntryTexture(uint32_t binding);
|
||||
static WGPUBindGroupLayoutEntry makeBindGroupLayoutEntryStorage(uint32_t binding, WGPUStorageTextureAccess access, WGPUTextureFormat format);
|
||||
|
||||
static WGPUBuffer createBuffer(WGPUDevice device, WGPUQueue queue, const void *data, size_t size);
|
||||
static WGPUBindGroup createBindGroup(WGPUDevice device, WGPUBindGroupLayout layout, const WGPUBindGroupEntry* bindGroupEntries, uint32_t count);
|
||||
static WGPUBindGroupLayout createBindGroupLayout(WGPUDevice device, const WGPUBindGroupLayoutEntry* bindGroupLayoutEntries, uint32_t count);
|
||||
|
||||
static void releaseBuffer(WGPUBuffer& buffer);
|
||||
static void releaseBindGroup(WGPUBindGroup& bindGroup);
|
||||
static void releaseBindGroupLayout(WGPUBindGroupLayout& bindGroupLayout);
|
||||
};
|
||||
|
||||
struct WgPipeline
|
||||
{
|
||||
protected:
|
||||
WGPUPipelineLayout mPipelineLayout{};
|
||||
WGPUShaderModule mShaderModule{};
|
||||
public:
|
||||
virtual ~WgPipeline() {}
|
||||
virtual void initialize(WGPUDevice device) = 0;
|
||||
virtual void release();
|
||||
|
||||
static WGPUPipelineLayout createPipelineLayout(WGPUDevice device, const WGPUBindGroupLayout* bindGroupLayouts, uint32_t count);
|
||||
static WGPUShaderModule createShaderModule(WGPUDevice device, const char* code, const char* label);
|
||||
static void destroyPipelineLayout(WGPUPipelineLayout& pipelineLayout);
|
||||
static void destroyShaderModule(WGPUShaderModule& shaderModule);
|
||||
};
|
||||
|
||||
struct WgRenderPipeline: public WgPipeline
|
||||
{
|
||||
protected:
|
||||
WGPURenderPipeline mRenderPipeline{};
|
||||
void allocate(WGPUDevice device, WgPipelineBlendType blendType, WGPUColorWriteMask writeMask,
|
||||
WGPUVertexBufferLayout vertexBufferLayouts[], uint32_t attribsCount,
|
||||
WGPUBindGroupLayout bindGroupLayouts[], uint32_t bindGroupsCount,
|
||||
WGPUCompareFunction compareFront, WGPUStencilOperation operationFront,
|
||||
WGPUCompareFunction compareBack, WGPUStencilOperation operationBack,
|
||||
const char* shaderSource, const char* shaderLabel, const char* pipelineLabel);
|
||||
public:
|
||||
void release() override;
|
||||
void set(WGPURenderPassEncoder renderPassEncoder);
|
||||
|
||||
static WGPUBlendState makeBlendState(WgPipelineBlendType blendType);
|
||||
static WGPUColorTargetState makeColorTargetState(const WGPUBlendState* blendState, const WGPUColorWriteMask writeMask);
|
||||
static WGPUVertexBufferLayout makeVertexBufferLayout(const WGPUVertexAttribute* vertexAttributes, uint32_t count, uint64_t stride);
|
||||
static WGPUVertexState makeVertexState(WGPUShaderModule shaderModule, const WGPUVertexBufferLayout* buffers, uint32_t count);
|
||||
static WGPUPrimitiveState makePrimitiveState();
|
||||
static WGPUDepthStencilState makeDepthStencilState(WGPUCompareFunction compareFront, WGPUStencilOperation operationFront, WGPUCompareFunction compareBack, WGPUStencilOperation operationBack);
|
||||
static WGPUMultisampleState makeMultisampleState();
|
||||
static WGPUFragmentState makeFragmentState(WGPUShaderModule shaderModule, WGPUColorTargetState* targets, uint32_t size);
|
||||
|
||||
static WGPURenderPipeline createRenderPipeline(WGPUDevice device, WgPipelineBlendType blendType, WGPUColorWriteMask writeMask,
|
||||
WGPUVertexBufferLayout vertexBufferLayouts[], uint32_t attribsCount,
|
||||
WGPUCompareFunction compareFront, WGPUStencilOperation operationFront,
|
||||
WGPUCompareFunction compareBack, WGPUStencilOperation operationBack,
|
||||
WGPUPipelineLayout pipelineLayout, WGPUShaderModule shaderModule,
|
||||
const char* pipelineLabel);
|
||||
static void destroyRenderPipeline(WGPURenderPipeline& renderPipeline);
|
||||
};
|
||||
|
||||
#define WG_COMPUTE_WORKGROUP_SIZE_X 8
|
||||
#define WG_COMPUTE_WORKGROUP_SIZE_Y 8
|
||||
|
||||
struct WgComputePipeline: public WgPipeline
|
||||
{
|
||||
protected:
|
||||
WGPUComputePipeline mComputePipeline{};
|
||||
void allocate(WGPUDevice device,
|
||||
WGPUBindGroupLayout bindGroupLayouts[], uint32_t bindGroupsCount,
|
||||
const char* shaderSource, const char* shaderLabel, const char* pipelineLabel);
|
||||
|
||||
public:
|
||||
void release() override;
|
||||
void set(WGPUComputePassEncoder computePassEncoder);
|
||||
};
|
||||
|
||||
#endif // _TVG_WG_COMMON_H_
|
||||
|
|
509
src/renderer/wg_engine/tvgWgCompositor.cpp
Executable file
509
src/renderer/wg_engine/tvgWgCompositor.cpp
Executable file
|
@ -0,0 +1,509 @@
|
|||
/*
|
||||
* Copyright (c) 2024 the ThorVG project. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "tvgWgCompositor.h"
|
||||
#include "tvgWgShaderTypes.h"
|
||||
|
||||
void WgCompositor::initialize(WgContext& context, uint32_t width, uint32_t height)
|
||||
{
|
||||
// pipelines (external handle, do not release)
|
||||
pipelines = context.pipelines;
|
||||
// store render target dimensions
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
// allocate global stencil buffer handles
|
||||
texStencil = context.createTexStencil(width, height, WGPUTextureFormat_Stencil8);
|
||||
texViewStencil = context.createTextureView(texStencil);
|
||||
// allocate global view matrix handles
|
||||
WgShaderTypeMat4x4f viewMat(width, height);
|
||||
context.allocateBufferUniform(bufferViewMat, &viewMat, sizeof(viewMat));
|
||||
bindGroupViewMat = pipelines->layouts.createBindGroupBuffer1Un(bufferViewMat);
|
||||
// initialize opacity pool
|
||||
for (uint32_t i = 0; i < 256; i++) {
|
||||
float opacity = i / 255.0f;
|
||||
context.allocateBufferUniform(bufferOpacities[i], &opacity, sizeof(float));
|
||||
bindGroupOpacities[i] = pipelines->layouts.createBindGroupBuffer1Un(bufferOpacities[i]);
|
||||
}
|
||||
// initialize intermediate render storages
|
||||
storageClipPath.initialize(context, width, height);
|
||||
storageInterm.initialize(context, width, height);
|
||||
storageDstCopy.initialize(context, width, height);
|
||||
}
|
||||
|
||||
|
||||
void WgCompositor::release(WgContext& context)
|
||||
{
|
||||
// release intermediate render storages
|
||||
storageInterm.release(context);
|
||||
storageDstCopy.release(context);
|
||||
storageClipPath.release(context);
|
||||
// release opacity pool
|
||||
for (uint32_t i = 0; i < 256; i++) {
|
||||
context.pipelines->layouts.releaseBindGroup(bindGroupOpacities[i]);
|
||||
context.releaseBuffer(bufferOpacities[i]);
|
||||
}
|
||||
// release global view matrix handles
|
||||
context.pipelines->layouts.releaseBindGroup(bindGroupViewMat);
|
||||
context.releaseBuffer(bufferViewMat);
|
||||
// release global stencil buffer handles
|
||||
context.releaseTextureView(texViewStencil);
|
||||
context.releaseTexture(texStencil);
|
||||
height = 0;
|
||||
width = 0;
|
||||
pipelines = nullptr;
|
||||
}
|
||||
|
||||
|
||||
WgPipelineBlendType WgCompositor::blendMethodToBlendType(BlendMethod blendMethod)
|
||||
{
|
||||
if (blendMethod == BlendMethod::Normal) return WgPipelineBlendType::Normal;
|
||||
if (blendMethod == BlendMethod::SrcOver) return WgPipelineBlendType::SrcOver;
|
||||
return WgPipelineBlendType::Custom;
|
||||
}
|
||||
|
||||
|
||||
void WgCompositor::beginRenderPass(WGPUCommandEncoder commandEncoder, WgRenderStorage* target, bool clear)
|
||||
{
|
||||
assert(commandEncoder);
|
||||
assert(target);
|
||||
this->currentTarget = target;
|
||||
this->commandEncoder = commandEncoder;
|
||||
WGPURenderPassDepthStencilAttachment depthStencilAttachment{ .view = texViewStencil, .stencilLoadOp = WGPULoadOp_Clear, .stencilStoreOp = WGPUStoreOp_Discard };
|
||||
WGPURenderPassColorAttachment colorAttachment{};
|
||||
colorAttachment.view = target->texView,
|
||||
colorAttachment.loadOp = clear ? WGPULoadOp_Clear : WGPULoadOp_Load,
|
||||
colorAttachment.storeOp = WGPUStoreOp_Store;
|
||||
#ifdef __EMSCRIPTEN__
|
||||
colorAttachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
|
||||
#endif
|
||||
WGPURenderPassDescriptor renderPassDesc{ .colorAttachmentCount = 1, .colorAttachments = &colorAttachment, .depthStencilAttachment = &depthStencilAttachment };
|
||||
renderPassEncoder = wgpuCommandEncoderBeginRenderPass(commandEncoder, &renderPassDesc);
|
||||
assert(renderPassEncoder);
|
||||
}
|
||||
|
||||
|
||||
void WgCompositor::endRenderPass()
|
||||
{
|
||||
assert(renderPassEncoder);
|
||||
wgpuRenderPassEncoderEnd(renderPassEncoder);
|
||||
wgpuRenderPassEncoderRelease(renderPassEncoder);
|
||||
this->renderPassEncoder = nullptr;
|
||||
this->currentTarget = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void WgCompositor::renderClipPath(WgContext& context, WgRenderDataPaint* renderData, WgRenderStorage* dst)
|
||||
{
|
||||
assert(renderData);
|
||||
if (renderData->clips.count == 0) return;
|
||||
// store current render pass
|
||||
WgRenderStorage *target = currentTarget;
|
||||
endRenderPass();
|
||||
// render first clip path
|
||||
beginRenderPass(commandEncoder, dst, true);
|
||||
drawClipPath(context, (WgRenderDataShape*)(renderData->clips[0]));
|
||||
endRenderPass();
|
||||
// render amd merge clip paths
|
||||
for (uint32_t i = 1 ; i < renderData->clips.count; i++) {
|
||||
// render clip path
|
||||
beginRenderPass(commandEncoder, &storageInterm, true);
|
||||
drawClipPath(context, (WgRenderDataShape*)(renderData->clips[i]));
|
||||
endRenderPass();
|
||||
// merge masks
|
||||
mergeMasks(commandEncoder, &storageInterm, dst);
|
||||
}
|
||||
// restore current render pass
|
||||
beginRenderPass(commandEncoder, target, false);
|
||||
}
|
||||
|
||||
|
||||
void WgCompositor::renderShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod)
|
||||
{
|
||||
assert(renderData);
|
||||
assert(renderPassEncoder);
|
||||
WgPipelineBlendType blentType = blendMethodToBlendType(blendMethod);
|
||||
// apply clip path if neccessary
|
||||
if (renderData->clips.count != 0) {
|
||||
renderClipPath(context, renderData, &storageClipPath);
|
||||
if (renderData->strokeFirst) {
|
||||
composeStrokes(context, renderData, &storageClipPath, CompositeMethod::ClipPath);
|
||||
composeShape(context, renderData, &storageClipPath, CompositeMethod::ClipPath);
|
||||
} else {
|
||||
composeShape(context, renderData, &storageClipPath, CompositeMethod::ClipPath);
|
||||
composeStrokes(context, renderData, &storageClipPath, CompositeMethod::ClipPath);
|
||||
}
|
||||
// use custom blending
|
||||
} else if (blentType == WgPipelineBlendType::Custom) {
|
||||
if (renderData->strokeFirst) {
|
||||
blendStrokes(context, renderData, blendMethod);
|
||||
blendShape(context, renderData, blendMethod);
|
||||
} else {
|
||||
blendShape(context, renderData, blendMethod);
|
||||
blendStrokes(context, renderData, blendMethod);
|
||||
}
|
||||
// use direct hardware blending
|
||||
} else {
|
||||
if (renderData->strokeFirst) {
|
||||
drawStrokes(context, renderData, blentType);
|
||||
drawShape(context, renderData, blentType);
|
||||
} else {
|
||||
drawShape(context, renderData, blentType);
|
||||
drawStrokes(context, renderData, blentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WgCompositor::renderImage(WgContext& context, WgRenderDataPicture* renderData, BlendMethod blendMethod)
|
||||
{
|
||||
assert(renderData);
|
||||
assert(renderPassEncoder);
|
||||
WgPipelineBlendType blentType = blendMethodToBlendType(blendMethod);
|
||||
// apply clip path if neccessary
|
||||
if (renderData->clips.count != 0) {
|
||||
renderClipPath(context, renderData, &storageClipPath);
|
||||
composeImage(context, renderData, &storageClipPath, CompositeMethod::ClipPath);
|
||||
// use custom blending
|
||||
} else if (blentType == WgPipelineBlendType::Custom)
|
||||
blendImage(context, renderData, blendMethod);
|
||||
else // use direct hardware blending
|
||||
drawImage(context, renderData, blentType);
|
||||
};
|
||||
|
||||
|
||||
void WgCompositor::blendShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod)
|
||||
{
|
||||
assert(renderData);
|
||||
assert(commandEncoder);
|
||||
assert(currentTarget);
|
||||
// skip shape blending if shapes do not exist
|
||||
if (renderData->renderSettingsShape.skip) return;
|
||||
if (renderData->meshGroupShapes.meshes.count == 0) return;
|
||||
// store current render pass
|
||||
WgRenderStorage *target = currentTarget;
|
||||
endRenderPass();
|
||||
// render into intermediate buffer
|
||||
beginRenderPass(commandEncoder, &storageInterm, true);
|
||||
drawShape(context, renderData, WgPipelineBlendType::Custom);
|
||||
endRenderPass();
|
||||
// run blend
|
||||
blend(commandEncoder, &storageInterm, target, 255, blendMethod, renderData->renderSettingsShape.rasterType);
|
||||
// restore current render pass
|
||||
beginRenderPass(commandEncoder, target, false);
|
||||
}
|
||||
|
||||
void WgCompositor::blendStrokes(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod)
|
||||
{
|
||||
assert(renderData);
|
||||
assert(commandEncoder);
|
||||
assert(currentTarget);
|
||||
// skip strokes blending if strokes do not exist
|
||||
if (renderData->renderSettingsStroke.skip) return;
|
||||
if (renderData->meshGroupStrokes.meshes.count == 0) return;
|
||||
// store current render pass
|
||||
WgRenderStorage *target = currentTarget;
|
||||
endRenderPass();
|
||||
// render into intermediate buffer
|
||||
beginRenderPass(commandEncoder, &storageInterm, true);
|
||||
drawStrokes(context, renderData, WgPipelineBlendType::Custom);
|
||||
endRenderPass();
|
||||
// run blend
|
||||
blend(commandEncoder, &storageInterm, target, 255, blendMethod, renderData->renderSettingsStroke.rasterType);
|
||||
// restore current render pass
|
||||
beginRenderPass(commandEncoder, target, false);
|
||||
};
|
||||
|
||||
|
||||
void WgCompositor::blendImage(WgContext& context, WgRenderDataPicture* renderData, BlendMethod blendMethod)
|
||||
{
|
||||
assert(renderData);
|
||||
assert(commandEncoder);
|
||||
assert(currentTarget);
|
||||
// store current render pass
|
||||
WgRenderStorage *target = currentTarget;
|
||||
endRenderPass();
|
||||
// render into intermediate buffer
|
||||
beginRenderPass(commandEncoder, &storageInterm, true);
|
||||
drawImage(context, renderData, WgPipelineBlendType::Custom);
|
||||
endRenderPass();
|
||||
// run blend
|
||||
blend(commandEncoder, &storageInterm, target, 255, blendMethod, WgRenderRasterType::Image);
|
||||
// restore current render pass
|
||||
beginRenderPass(commandEncoder, target, false);
|
||||
};
|
||||
|
||||
|
||||
void WgCompositor::composeShape(WgContext& context, WgRenderDataShape* renderData, WgRenderStorage* mask, CompositeMethod composeMethod)
|
||||
{
|
||||
assert(mask);
|
||||
assert(renderData);
|
||||
assert(commandEncoder);
|
||||
assert(currentTarget);
|
||||
// skip shape composing if shape do not exist
|
||||
if (renderData->renderSettingsShape.skip) return;
|
||||
if (renderData->meshGroupShapes.meshes.count == 0) return;
|
||||
// store current render pass
|
||||
WgRenderStorage *target = currentTarget;
|
||||
endRenderPass();
|
||||
// render into intermediate buffer
|
||||
beginRenderPass(commandEncoder, &storageInterm, true);
|
||||
drawShape(context, renderData, WgPipelineBlendType::Custom);
|
||||
endRenderPass();
|
||||
// run compisition
|
||||
compose(commandEncoder, &storageInterm, mask, target, composeMethod);
|
||||
// restore current render pass
|
||||
beginRenderPass(commandEncoder, target, false);
|
||||
}
|
||||
|
||||
|
||||
void WgCompositor::composeStrokes(WgContext& context, WgRenderDataShape* renderData, WgRenderStorage* msk, CompositeMethod composeMethod)
|
||||
{
|
||||
assert(msk);
|
||||
assert(renderData);
|
||||
assert(commandEncoder);
|
||||
assert(currentTarget);
|
||||
// skip shape composing if strokes do not exist
|
||||
if (renderData->renderSettingsStroke.skip) return;
|
||||
if (renderData->meshGroupStrokes.meshes.count == 0) return;
|
||||
// store current render pass
|
||||
WgRenderStorage *target = currentTarget;
|
||||
endRenderPass();
|
||||
// render into intermediate buffer
|
||||
beginRenderPass(commandEncoder, &storageInterm, true);
|
||||
drawStrokes(context, renderData, WgPipelineBlendType::Custom);
|
||||
endRenderPass();
|
||||
// run compisition
|
||||
compose(commandEncoder, &storageInterm, msk, target, composeMethod);
|
||||
// restore current render pass
|
||||
beginRenderPass(commandEncoder, target, false);
|
||||
}
|
||||
|
||||
|
||||
void WgCompositor::composeImage(WgContext& context, WgRenderDataPicture* renderData, WgRenderStorage* msk, CompositeMethod composeMethod)
|
||||
{
|
||||
assert(msk);
|
||||
assert(renderData);
|
||||
assert(commandEncoder);
|
||||
assert(currentTarget);
|
||||
// store current render pass
|
||||
WgRenderStorage *target = currentTarget;
|
||||
endRenderPass();
|
||||
// render into intermediate buffer
|
||||
beginRenderPass(commandEncoder, &storageInterm, true);
|
||||
drawImage(context, renderData, WgPipelineBlendType::Custom);
|
||||
endRenderPass();
|
||||
// run compisition
|
||||
compose(commandEncoder, &storageInterm, msk, target, composeMethod);
|
||||
// restore current render pass
|
||||
beginRenderPass(commandEncoder, target, false);
|
||||
}
|
||||
|
||||
|
||||
void WgCompositor::drawClipPath(WgContext& context, WgRenderDataShape* renderData)
|
||||
{
|
||||
assert(renderData);
|
||||
assert(renderPassEncoder);
|
||||
assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count);
|
||||
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return;
|
||||
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h);
|
||||
// setup stencil rules
|
||||
WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::Winding) ? pipelines->winding : pipelines->evenodd;
|
||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
|
||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
|
||||
// draw to stencil (first pass)
|
||||
for (uint32_t i = 0; i < renderData->meshGroupShapes.meshes.count; i++)
|
||||
renderData->meshGroupShapes.meshes[i]->drawFan(context, renderPassEncoder);
|
||||
// setup fill rules
|
||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
|
||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->clipPath);
|
||||
// draw to color (second pass)
|
||||
renderData->meshDataBBox.drawFan(context, renderPassEncoder);
|
||||
}
|
||||
|
||||
|
||||
void WgCompositor::drawShape(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType)
|
||||
{
|
||||
assert(renderData);
|
||||
assert(renderPassEncoder);
|
||||
assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count);
|
||||
uint32_t blendTypeInd = (uint32_t)blendType;
|
||||
if (renderData->renderSettingsShape.skip) return;
|
||||
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return;
|
||||
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h);
|
||||
// setup stencil rules
|
||||
WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::Winding) ? pipelines->winding : pipelines->evenodd;
|
||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
|
||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
|
||||
// draw to stencil (first pass)
|
||||
for (uint32_t i = 0; i < renderData->meshGroupShapes.meshes.count; i++)
|
||||
renderData->meshGroupShapes.meshes[i]->drawFan(context, renderPassEncoder);
|
||||
// setup fill rules
|
||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
|
||||
WgRenderSettings& settings = renderData->renderSettingsShape;
|
||||
if (settings.fillType == WgRenderSettingsType::Solid) {
|
||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupSolid, 0, nullptr);
|
||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->solid[blendTypeInd]);
|
||||
} else if (settings.fillType == WgRenderSettingsType::Linear) {
|
||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupLinear, 0, nullptr);
|
||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->linear[blendTypeInd]);
|
||||
} else if (settings.fillType == WgRenderSettingsType::Radial) {
|
||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupRadial, 0, nullptr);
|
||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->radial[blendTypeInd]);
|
||||
}
|
||||
// draw to color (second pass)
|
||||
renderData->meshDataBBox.drawFan(context, renderPassEncoder);
|
||||
}
|
||||
|
||||
|
||||
void WgCompositor::drawStrokes(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType)
|
||||
{
|
||||
assert(renderData);
|
||||
assert(renderPassEncoder);
|
||||
assert(renderData->meshGroupStrokes.meshes.count == renderData->meshGroupStrokesBBox.meshes.count);
|
||||
uint32_t blendTypeInd = (uint32_t)blendType;
|
||||
if (renderData->renderSettingsStroke.skip) return;
|
||||
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return;
|
||||
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h);
|
||||
// draw strokes to stencil (first pass)
|
||||
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
|
||||
// setup stencil rules
|
||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255);
|
||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
|
||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->direct);
|
||||
// draw to stencil (first pass)
|
||||
renderData->meshGroupStrokes.meshes[i]->draw(context, renderPassEncoder);
|
||||
// setup fill rules
|
||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
|
||||
WgRenderSettings& settings = renderData->renderSettingsStroke;
|
||||
if (settings.fillType == WgRenderSettingsType::Solid) {
|
||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupSolid, 0, nullptr);
|
||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->solid[blendTypeInd]);
|
||||
} else if (settings.fillType == WgRenderSettingsType::Linear) {
|
||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupLinear, 0, nullptr);
|
||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->linear[blendTypeInd]);
|
||||
} else if (settings.fillType == WgRenderSettingsType::Radial) {
|
||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, settings.bindGroupRadial, 0, nullptr);
|
||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->radial[blendTypeInd]);
|
||||
}
|
||||
// draw to color (second pass)
|
||||
renderData->meshGroupStrokesBBox.meshes[i]->drawFan(context, renderPassEncoder);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WgCompositor::drawImage(WgContext& context, WgRenderDataPicture* renderData, WgPipelineBlendType blendType)
|
||||
{
|
||||
assert(renderData);
|
||||
assert(renderPassEncoder);
|
||||
uint32_t blendTypeInd = (uint32_t)blendType;
|
||||
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return;
|
||||
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h);
|
||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
|
||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
|
||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
|
||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, renderData->bindGroupPicture, 0, nullptr);
|
||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines->image[blendTypeInd]);
|
||||
renderData->meshData.drawImage(context, renderPassEncoder);
|
||||
}
|
||||
|
||||
|
||||
void WgCompositor::mergeMasks(WGPUCommandEncoder encoder, WgRenderStorage* mask0, WgRenderStorage* mask1)
|
||||
{
|
||||
assert(mask0);
|
||||
assert(mask1);
|
||||
assert(!renderPassEncoder);
|
||||
// copy dst storage to temporary read only storage
|
||||
const WGPUImageCopyTexture texSrc { .texture = mask1->texture };
|
||||
const WGPUImageCopyTexture texDst { .texture = storageDstCopy.texture };
|
||||
const WGPUExtent3D copySize { .width = width, .height = height, .depthOrArrayLayers = 1 };
|
||||
wgpuCommandEncoderCopyTextureToTexture(encoder, &texSrc, &texDst, ©Size);
|
||||
// execute compose shader
|
||||
const WGPUComputePassDescriptor computePassDescriptor{};
|
||||
WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(encoder, &computePassDescriptor);
|
||||
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 0, mask0->bindGroupRead, 0, nullptr);
|
||||
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 1, storageDstCopy.bindGroupRead, 0, nullptr);
|
||||
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, mask1->bindGroupWrite, 0, nullptr);
|
||||
wgpuComputePassEncoderSetPipeline(computePassEncoder, pipelines->mergeMasks);
|
||||
wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, (width + 7) / 8, (height + 7) / 8, 1);
|
||||
wgpuComputePassEncoderEnd(computePassEncoder);
|
||||
}
|
||||
|
||||
|
||||
void WgCompositor::blend(WGPUCommandEncoder encoder, WgRenderStorage* src, WgRenderStorage* dst, uint8_t opacity, BlendMethod blendMethod, WgRenderRasterType rasterType)
|
||||
{
|
||||
assert(src);
|
||||
assert(dst);
|
||||
assert(!renderPassEncoder);
|
||||
WGPUComputePipeline pipeline = pipelines->blendImage[(size_t)blendMethod];
|
||||
if (rasterType == WgRenderRasterType::Solid) pipeline = pipelines->blendSolid[(size_t)blendMethod];
|
||||
if (rasterType == WgRenderRasterType::Gradient) pipeline = pipelines->blendGradient[(size_t)blendMethod];
|
||||
// copy texture to texture
|
||||
const WGPUImageCopyTexture texSrc { .texture = dst->texture };
|
||||
const WGPUImageCopyTexture texDst { .texture = storageDstCopy.texture };
|
||||
const WGPUExtent3D copySize { .width = width, .height = height, .depthOrArrayLayers = 1 };
|
||||
wgpuCommandEncoderCopyTextureToTexture(encoder, &texSrc, &texDst, ©Size);
|
||||
// execute compose shader
|
||||
const WGPUComputePassDescriptor computePassDescriptor{};
|
||||
WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(encoder, &computePassDescriptor);
|
||||
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 0, src->bindGroupRead, 0, nullptr);
|
||||
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 1, storageDstCopy.bindGroupRead, 0, nullptr);
|
||||
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, dst->bindGroupWrite, 0, nullptr);
|
||||
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 3, bindGroupOpacities[opacity], 0, nullptr);
|
||||
wgpuComputePassEncoderSetPipeline(computePassEncoder, pipeline);
|
||||
wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, (width + 7) / 8, (height + 7) / 8, 1);
|
||||
wgpuComputePassEncoderEnd(computePassEncoder);
|
||||
}
|
||||
|
||||
|
||||
void WgCompositor::compose(WGPUCommandEncoder encoder, WgRenderStorage* src, WgRenderStorage* mask, WgRenderStorage* dst, CompositeMethod composeMethod)
|
||||
{
|
||||
assert(src);
|
||||
assert(mask);
|
||||
assert(dst);
|
||||
assert(!renderPassEncoder);
|
||||
// copy dst storage to temporary read only storage
|
||||
const WGPUImageCopyTexture texSrc { .texture = dst->texture };
|
||||
const WGPUImageCopyTexture texDst { .texture = storageDstCopy.texture };
|
||||
const WGPUExtent3D copySize { .width = width, .height = height, .depthOrArrayLayers = 1 };
|
||||
wgpuCommandEncoderCopyTextureToTexture(encoder, &texSrc, &texDst, ©Size);
|
||||
// execute compose shader
|
||||
const WGPUComputePassDescriptor computePassDescriptor{};
|
||||
WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(encoder, &computePassDescriptor);
|
||||
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 0, src->bindGroupRead, 0, nullptr);
|
||||
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 1, mask->bindGroupRead, 0, nullptr);
|
||||
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, storageDstCopy.bindGroupRead, 0, nullptr);
|
||||
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 3, dst->bindGroupWrite, 0, nullptr);
|
||||
wgpuComputePassEncoderSetPipeline(computePassEncoder, pipelines->compose[(size_t)composeMethod]);
|
||||
wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, (width + 7) / 8, (height + 7) / 8, 1);
|
||||
wgpuComputePassEncoderEnd(computePassEncoder);
|
||||
}
|
90
src/renderer/wg_engine/tvgWgCompositor.h
Executable file
90
src/renderer/wg_engine/tvgWgCompositor.h
Executable file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (c) 2024 the ThorVG project. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_WG_COMPOSITOR_H_
|
||||
#define _TVG_WG_COMPOSITOR_H_
|
||||
|
||||
#include "tvgWgRenderTarget.h"
|
||||
#include "tvgWgRenderData.h"
|
||||
|
||||
struct WgCompose: public Compositor {
|
||||
BlendMethod blend;
|
||||
};
|
||||
|
||||
class WgCompositor {
|
||||
private:
|
||||
// pipelines (external handle, do not release)
|
||||
WgPipelines* pipelines{};
|
||||
// global stencil buffer handles
|
||||
WGPUTexture texStencil{};
|
||||
WGPUTextureView texViewStencil{};
|
||||
// global view matrix handles
|
||||
WGPUBuffer bufferViewMat{};
|
||||
WGPUBindGroup bindGroupViewMat{};
|
||||
// opacity value pool
|
||||
WGPUBuffer bufferOpacities[256]{};
|
||||
WGPUBindGroup bindGroupOpacities[256]{};
|
||||
// current render pass handles
|
||||
WGPURenderPassEncoder renderPassEncoder{};
|
||||
WGPUCommandEncoder commandEncoder{};
|
||||
WgRenderStorage* currentTarget{};
|
||||
// intermediate render storages
|
||||
WgRenderStorage storageInterm;
|
||||
WgRenderStorage storageClipPath;
|
||||
WgRenderStorage storageDstCopy;
|
||||
|
||||
static WgPipelineBlendType blendMethodToBlendType(BlendMethod blendMethod);
|
||||
public:
|
||||
// render target dimensions
|
||||
uint32_t width{};
|
||||
uint32_t height{};
|
||||
public:
|
||||
void initialize(WgContext& context, uint32_t width, uint32_t height);
|
||||
void release(WgContext& context);
|
||||
|
||||
void beginRenderPass(WGPUCommandEncoder encoder, WgRenderStorage* target, bool clear);
|
||||
void endRenderPass();
|
||||
|
||||
void renderClipPath(WgContext& context, WgRenderDataPaint* renderData, WgRenderStorage* dst);
|
||||
|
||||
void renderShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod);
|
||||
void renderImage(WgContext& context, WgRenderDataPicture* renderData, BlendMethod blendMethod);
|
||||
|
||||
void blendShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod);
|
||||
void blendStrokes(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod);
|
||||
void blendImage(WgContext& context, WgRenderDataPicture* renderData, BlendMethod blendMethod);
|
||||
|
||||
void composeShape(WgContext& context, WgRenderDataShape* renderData, WgRenderStorage* mask, CompositeMethod composeMethod);
|
||||
void composeStrokes(WgContext& context, WgRenderDataShape* renderData, WgRenderStorage* mask, CompositeMethod composeMethod);
|
||||
void composeImage(WgContext& context, WgRenderDataPicture* renderData, WgRenderStorage* mask, CompositeMethod composeMethod);
|
||||
|
||||
void drawClipPath(WgContext& context, WgRenderDataShape* renderData);
|
||||
void drawShape(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType);
|
||||
void drawStrokes(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType);
|
||||
void drawImage(WgContext& context, WgRenderDataPicture* renderData, WgPipelineBlendType blendType);
|
||||
|
||||
void mergeMasks(WGPUCommandEncoder encoder, WgRenderStorage* mask0, WgRenderStorage* mask1);
|
||||
void blend(WGPUCommandEncoder encoder, WgRenderStorage* src, WgRenderStorage* dst, uint8_t opacity, BlendMethod blendMethod, WgRenderRasterType rasterType);
|
||||
void compose(WGPUCommandEncoder encoder, WgRenderStorage* src, WgRenderStorage* mask, WgRenderStorage* dst, CompositeMethod composeMethod);
|
||||
};
|
||||
|
||||
#endif // _TVG_WG_COMPOSITOR_H_
|
0
src/renderer/wg_engine/tvgWgGeometry.cpp
Normal file → Executable file
0
src/renderer/wg_engine/tvgWgGeometry.cpp
Normal file → Executable file
0
src/renderer/wg_engine/tvgWgGeometry.h
Normal file → Executable file
0
src/renderer/wg_engine/tvgWgGeometry.h
Normal file → Executable file
747
src/renderer/wg_engine/tvgWgPipelines.cpp
Normal file → Executable file
747
src/renderer/wg_engine/tvgWgPipelines.cpp
Normal file → Executable file
|
@ -23,492 +23,359 @@
|
|||
#include "tvgWgPipelines.h"
|
||||
#include "tvgWgShaderSrc.h"
|
||||
|
||||
#define ARRAY_ELEMENTS_COUNT(arr) sizeof(arr)/sizeof(arr[0])
|
||||
|
||||
//************************************************************************
|
||||
// graphics pipelines
|
||||
//************************************************************************
|
||||
|
||||
void WgPipelineFillShapeWinding::initialize(WGPUDevice device)
|
||||
WGPUShaderModule WgPipelines::createShaderModule(WGPUDevice device, const char* label, const char* code)
|
||||
{
|
||||
// vertex attributes settings
|
||||
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
|
||||
WGPUVertexBufferLayout vertexBufferLayouts[] = {
|
||||
makeVertexBufferLayout(&vertexAttributesPos, 1, sizeof(float) * 2)
|
||||
};
|
||||
|
||||
// bind groups
|
||||
WGPUBindGroupLayout bindGroupLayouts[] = {
|
||||
WgBindGroupCanvas::getLayout(device),
|
||||
WgBindGroupPaint::getLayout(device)
|
||||
};
|
||||
|
||||
// stencil function
|
||||
WGPUCompareFunction stencilFunctionFront = WGPUCompareFunction_Always;
|
||||
WGPUStencilOperation stencilOperationFront = WGPUStencilOperation_IncrementWrap;
|
||||
WGPUCompareFunction stencilFunctionBack = WGPUCompareFunction_Always;
|
||||
WGPUStencilOperation stencilOperationBack = WGPUStencilOperation_DecrementWrap;
|
||||
|
||||
// shader source and labels
|
||||
auto shaderSource = cShaderSource_PipelineFill;
|
||||
auto shaderLabel = "The shader fill";
|
||||
auto pipelineLabel = "The render pipeline fill shape winding";
|
||||
|
||||
// allocate all pipeline handles
|
||||
allocate(device, WgPipelineBlendType::SrcOver, WGPUColorWriteMask_None,
|
||||
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
|
||||
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
||||
stencilFunctionFront, stencilOperationFront, stencilFunctionBack, stencilOperationBack,
|
||||
shaderSource, shaderLabel, pipelineLabel);
|
||||
WGPUShaderModuleWGSLDescriptor shaderModuleWGSLDesc{};
|
||||
shaderModuleWGSLDesc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
|
||||
shaderModuleWGSLDesc.code = code;
|
||||
const WGPUShaderModuleDescriptor shaderModuleDesc { .nextInChain = &shaderModuleWGSLDesc.chain, .label = label };
|
||||
return wgpuDeviceCreateShaderModule(device, &shaderModuleDesc);
|
||||
}
|
||||
|
||||
|
||||
void WgPipelineFillShapeEvenOdd::initialize(WGPUDevice device)
|
||||
WGPUPipelineLayout WgPipelines::createPipelineLayout(WGPUDevice device, const WGPUBindGroupLayout* bindGroupLayouts, const uint32_t bindGroupLayoutsCount)
|
||||
{
|
||||
// vertex attributes settings
|
||||
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
|
||||
WGPUVertexBufferLayout vertexBufferLayouts[] = {
|
||||
makeVertexBufferLayout(&vertexAttributesPos, 1, sizeof(float) * 2)
|
||||
};
|
||||
|
||||
// bind groups
|
||||
WGPUBindGroupLayout bindGroupLayouts[] = {
|
||||
WgBindGroupCanvas::getLayout(device),
|
||||
WgBindGroupPaint::getLayout(device)
|
||||
};
|
||||
|
||||
// stencil function
|
||||
WGPUCompareFunction stencilFunctionFront = WGPUCompareFunction_Always;
|
||||
WGPUStencilOperation stencilOperationFront = WGPUStencilOperation_Invert;
|
||||
WGPUCompareFunction stencilFunctionBack = WGPUCompareFunction_Always;
|
||||
WGPUStencilOperation stencilOperationBack = WGPUStencilOperation_Invert;
|
||||
|
||||
// shader source and labels
|
||||
auto shaderSource = cShaderSource_PipelineFill;
|
||||
auto shaderLabel = "The shader fill";
|
||||
auto pipelineLabel = "The render pipeline fill shape Even Odd";
|
||||
|
||||
// allocate all pipeline handles
|
||||
allocate(device, WgPipelineBlendType::SrcOver, WGPUColorWriteMask_None,
|
||||
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
|
||||
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
||||
stencilFunctionFront, stencilOperationFront, stencilFunctionBack, stencilOperationBack,
|
||||
shaderSource, shaderLabel, pipelineLabel);
|
||||
const WGPUPipelineLayoutDescriptor pipelineLayoutDesc { .bindGroupLayoutCount = bindGroupLayoutsCount, .bindGroupLayouts = bindGroupLayouts };
|
||||
return wgpuDeviceCreatePipelineLayout(device, &pipelineLayoutDesc);
|
||||
}
|
||||
|
||||
|
||||
void WgPipelineFillStroke::initialize(WGPUDevice device)
|
||||
WGPURenderPipeline WgPipelines::createRenderPipeline(
|
||||
WGPUDevice device, const char* pipelineLabel,
|
||||
const WGPUShaderModule shaderModule, const WGPUPipelineLayout pipelineLayout,
|
||||
const WGPUVertexBufferLayout *vertexBufferLayouts, const uint32_t vertexBufferLayoutsCount,
|
||||
const WGPUColorWriteMaskFlags writeMask,
|
||||
const WGPUCompareFunction stencilFunctionFrnt, const WGPUStencilOperation stencilOperationFrnt,
|
||||
const WGPUCompareFunction stencilFunctionBack, const WGPUStencilOperation stencilOperationBack,
|
||||
const WGPUPrimitiveState primitiveState, const WGPUMultisampleState multisampleState, const WGPUBlendState blendState)
|
||||
{
|
||||
// vertex and buffers settings
|
||||
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
|
||||
WGPUVertexBufferLayout vertexBufferLayouts[] = {
|
||||
makeVertexBufferLayout(&vertexAttributesPos, 1, sizeof(float) * 2)
|
||||
const WGPUColorTargetState colorTargetState { .format = WGPUTextureFormat_RGBA8Unorm, .blend = &blendState, .writeMask = writeMask };
|
||||
const WGPUColorTargetState colorTargetStates[] { colorTargetState };
|
||||
const WGPUDepthStencilState depthStencilState {
|
||||
.format = WGPUTextureFormat_Stencil8, .depthCompare = WGPUCompareFunction_Always,
|
||||
.stencilFront = { .compare = stencilFunctionFrnt, .failOp = stencilOperationFrnt, .depthFailOp = stencilOperationFrnt, .passOp = stencilOperationFrnt },
|
||||
.stencilBack = { .compare = stencilFunctionBack, .failOp = stencilOperationBack, .depthFailOp = stencilOperationBack, .passOp = stencilOperationBack },
|
||||
.stencilReadMask = 0xFFFFFFFF, .stencilWriteMask = 0xFFFFFFFF
|
||||
};
|
||||
|
||||
// bind groups and layouts
|
||||
WGPUBindGroupLayout bindGroupLayouts[] = {
|
||||
WgBindGroupCanvas::getLayout(device),
|
||||
WgBindGroupPaint::getLayout(device)
|
||||
const WGPUVertexState vertexState { .module = shaderModule, .entryPoint = "vs_main", .bufferCount = vertexBufferLayoutsCount, .buffers = vertexBufferLayouts };
|
||||
const WGPUFragmentState fragmentState { .module = shaderModule, .entryPoint = "fs_main", .targetCount = 1, .targets = colorTargetStates };
|
||||
const WGPURenderPipelineDescriptor renderPipelineDesc {
|
||||
.label = pipelineLabel,
|
||||
.layout = pipelineLayout,
|
||||
.vertex = vertexState,
|
||||
.primitive = primitiveState,
|
||||
.depthStencil = &depthStencilState,
|
||||
.multisample = multisampleState,
|
||||
.fragment = &fragmentState
|
||||
};
|
||||
|
||||
// stencil function
|
||||
WGPUCompareFunction stencilFunction = WGPUCompareFunction_Always;
|
||||
WGPUStencilOperation stencilOperation = WGPUStencilOperation_Replace;
|
||||
|
||||
// shader source and labels
|
||||
auto shaderSource = cShaderSource_PipelineFill;
|
||||
auto shaderLabel = "The shader fill";
|
||||
auto pipelineLabel = "The render pipeline fill stroke";
|
||||
|
||||
// allocate all pipeline handles
|
||||
allocate(device, WgPipelineBlendType::SrcOver, WGPUColorWriteMask_None,
|
||||
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
|
||||
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
||||
stencilFunction, stencilOperation, stencilFunction, stencilOperation,
|
||||
shaderSource, shaderLabel, pipelineLabel);
|
||||
return wgpuDeviceCreateRenderPipeline(device, &renderPipelineDesc);
|
||||
}
|
||||
|
||||
|
||||
void WgPipelineClipMask::initialize(WGPUDevice device)
|
||||
WGPUComputePipeline WgPipelines::createComputePipeline(
|
||||
WGPUDevice device, const char* pipelineLabel,
|
||||
const WGPUShaderModule shaderModule, const WGPUPipelineLayout pipelineLayout)
|
||||
{
|
||||
// vertex and buffers settings
|
||||
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
|
||||
WGPUVertexBufferLayout vertexBufferLayouts[] = {
|
||||
makeVertexBufferLayout(&vertexAttributesPos, 1, sizeof(float) * 2)
|
||||
const WGPUComputePipelineDescriptor computePipelineDesc{
|
||||
.label = pipelineLabel,
|
||||
.layout = pipelineLayout,
|
||||
.compute = {
|
||||
.module = shaderModule,
|
||||
.entryPoint = "cs_main"
|
||||
}
|
||||
};
|
||||
|
||||
// bind groups
|
||||
WGPUBindGroupLayout bindGroupLayouts[] = {
|
||||
WgBindGroupCanvas::getLayout(device),
|
||||
WgBindGroupPaint::getLayout(device)
|
||||
};
|
||||
|
||||
// stencil function
|
||||
WGPUCompareFunction stencilFunction = WGPUCompareFunction_NotEqual;
|
||||
WGPUStencilOperation stencilOperation = WGPUStencilOperation_Zero;
|
||||
|
||||
// shader source and labels
|
||||
auto shaderSource = cShaderSource_PipelineFill;
|
||||
auto shaderLabel = "The shader fill";
|
||||
auto pipelineLabel = "The render pipeline clip mask";
|
||||
|
||||
// allocate all pipeline handles
|
||||
allocate(device, WgPipelineBlendType::SrcOver, WGPUColorWriteMask_All,
|
||||
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
|
||||
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
||||
stencilFunction, stencilOperation, stencilFunction, stencilOperation,
|
||||
shaderSource, shaderLabel, pipelineLabel);
|
||||
return wgpuDeviceCreateComputePipeline(device, &computePipelineDesc);
|
||||
}
|
||||
|
||||
|
||||
void WgPipelineSolid::initialize(WGPUDevice device, WgPipelineBlendType blendType)
|
||||
void WgPipelines::releaseComputePipeline(WGPUComputePipeline& computePipeline)
|
||||
{
|
||||
// vertex and buffers settings
|
||||
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
|
||||
WGPUVertexBufferLayout vertexBufferLayouts[] = {
|
||||
makeVertexBufferLayout(&vertexAttributesPos, 1, sizeof(float) * 2)
|
||||
};
|
||||
|
||||
// bind groups and layouts
|
||||
WGPUBindGroupLayout bindGroupLayouts[] = {
|
||||
WgBindGroupCanvas::getLayout(device),
|
||||
WgBindGroupPaint::getLayout(device),
|
||||
WgBindGroupSolidColor::getLayout(device)
|
||||
};
|
||||
|
||||
// stencil function
|
||||
WGPUCompareFunction stencilFunction = WGPUCompareFunction_NotEqual;
|
||||
WGPUStencilOperation stencilOperation = WGPUStencilOperation_Zero;
|
||||
|
||||
// shader source and labels
|
||||
auto shaderSource = cShaderSource_PipelineSolid;
|
||||
auto shaderLabel = "The shader solid color";
|
||||
auto pipelineLabel = "The render pipeline solid color";
|
||||
|
||||
// allocate all pipeline handles
|
||||
allocate(device, blendType, WGPUColorWriteMask_All,
|
||||
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
|
||||
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
||||
stencilFunction, stencilOperation, stencilFunction, stencilOperation,
|
||||
shaderSource, shaderLabel, pipelineLabel);
|
||||
if (computePipeline) {
|
||||
wgpuComputePipelineRelease(computePipeline);
|
||||
computePipeline = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WgPipelineLinear::initialize(WGPUDevice device, WgPipelineBlendType blendType)
|
||||
void WgPipelines::releaseRenderPipeline(WGPURenderPipeline& renderPipeline)
|
||||
{
|
||||
// vertex and buffers settings
|
||||
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
|
||||
WGPUVertexBufferLayout vertexBufferLayouts[] = {
|
||||
makeVertexBufferLayout(&vertexAttributesPos, 1, sizeof(float) * 2)
|
||||
};
|
||||
|
||||
// bind groups and layouts
|
||||
WGPUBindGroupLayout bindGroupLayouts[] = {
|
||||
WgBindGroupCanvas::getLayout(device),
|
||||
WgBindGroupPaint::getLayout(device),
|
||||
WgBindGroupLinearGradient::getLayout(device)
|
||||
};
|
||||
|
||||
// stencil function
|
||||
WGPUCompareFunction stencilFunction = WGPUCompareFunction_NotEqual;
|
||||
WGPUStencilOperation stencilOperation = WGPUStencilOperation_Zero;
|
||||
|
||||
// shader source and labels
|
||||
auto shaderSource = cShaderSource_PipelineLinear;
|
||||
auto shaderLabel = "The shader linear gradient";
|
||||
auto pipelineLabel = "The render pipeline linear gradient";
|
||||
|
||||
// allocate all pipeline handles
|
||||
allocate(device, blendType, WGPUColorWriteMask_All,
|
||||
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
|
||||
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
||||
stencilFunction, stencilOperation, stencilFunction, stencilOperation,
|
||||
shaderSource, shaderLabel, pipelineLabel);
|
||||
if (renderPipeline) {
|
||||
wgpuRenderPipelineRelease(renderPipeline);
|
||||
renderPipeline = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WgPipelineRadial::initialize(WGPUDevice device, WgPipelineBlendType blendType)
|
||||
void WgPipelines::releasePipelineLayout(WGPUPipelineLayout& pipelineLayout)
|
||||
{
|
||||
// vertex and buffers settings
|
||||
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
|
||||
WGPUVertexBufferLayout vertexBufferLayouts[] = {
|
||||
makeVertexBufferLayout(&vertexAttributesPos, 1, sizeof(float) * 2)
|
||||
};
|
||||
|
||||
// bind groups and layouts
|
||||
WGPUBindGroupLayout bindGroupLayouts[] = {
|
||||
WgBindGroupCanvas::getLayout(device),
|
||||
WgBindGroupPaint::getLayout(device),
|
||||
WgBindGroupRadialGradient::getLayout(device)
|
||||
};
|
||||
|
||||
// stencil function
|
||||
WGPUCompareFunction stencilFunction = WGPUCompareFunction_NotEqual;
|
||||
WGPUStencilOperation stencilOperation = WGPUStencilOperation_Zero;
|
||||
|
||||
// shader source and labels
|
||||
auto shaderSource = cShaderSource_PipelineRadial;
|
||||
auto shaderLabel = "The shader radial gradient";
|
||||
auto pipelineLabel = "The render pipeline radial gradient";
|
||||
|
||||
// allocate all pipeline handles
|
||||
allocate(device, blendType, WGPUColorWriteMask_All,
|
||||
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
|
||||
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
||||
stencilFunction, stencilOperation, stencilFunction, stencilOperation,
|
||||
shaderSource, shaderLabel, pipelineLabel);
|
||||
if (pipelineLayout) {
|
||||
wgpuPipelineLayoutRelease(pipelineLayout);
|
||||
pipelineLayout = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WgPipelineImage::initialize(WGPUDevice device, WgPipelineBlendType blendType)
|
||||
void WgPipelines::releaseShaderModule(WGPUShaderModule& shaderModule)
|
||||
{
|
||||
// vertex and buffers settings
|
||||
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
|
||||
WGPUVertexAttribute vertexAttributesTex = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 1 };
|
||||
WGPUVertexBufferLayout vertexBufferLayouts[] = {
|
||||
makeVertexBufferLayout(&vertexAttributesPos, 1, sizeof(float) * 2),
|
||||
makeVertexBufferLayout(&vertexAttributesTex, 1, sizeof(float) * 2)
|
||||
};
|
||||
|
||||
// bind groups and layouts
|
||||
WGPUBindGroupLayout bindGroupLayouts[] = {
|
||||
WgBindGroupCanvas::getLayout(device),
|
||||
WgBindGroupPaint::getLayout(device),
|
||||
WgBindGroupPicture::getLayout(device)
|
||||
};
|
||||
|
||||
// stencil function
|
||||
WGPUCompareFunction stencilFunction = WGPUCompareFunction_Always;
|
||||
WGPUStencilOperation stencilOperation = WGPUStencilOperation_Zero;
|
||||
|
||||
// shader source and labels
|
||||
auto shaderSource = cShaderSource_PipelineImage;
|
||||
auto shaderLabel = "The shader image";
|
||||
auto pipelineLabel = "The render pipeline image";
|
||||
|
||||
// allocate all pipeline handles
|
||||
allocate(device, blendType, WGPUColorWriteMask_All,
|
||||
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
|
||||
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
||||
stencilFunction, stencilOperation, stencilFunction, stencilOperation,
|
||||
shaderSource, shaderLabel, pipelineLabel);
|
||||
if (shaderModule) {
|
||||
wgpuShaderModuleRelease(shaderModule);
|
||||
shaderModule = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//************************************************************************
|
||||
// compute pipelines
|
||||
//************************************************************************
|
||||
|
||||
void WgPipelineCopy::initialize(WGPUDevice device)
|
||||
{
|
||||
// bind groups and layouts
|
||||
WGPUBindGroupLayout bindGroupLayouts[] = {
|
||||
WgBindGroupTextureStorageRgbaRO::getLayout(device),
|
||||
WgBindGroupTextureStorageRgbaWO::getLayout(device)
|
||||
};
|
||||
|
||||
// shader source and labels
|
||||
auto shaderSource = cShaderSource_PipelineComputeCopy;
|
||||
auto shaderLabel = "The compute shader copy";
|
||||
auto pipelineLabel = "The compute pipeline copy";
|
||||
|
||||
// allocate all pipeline handles
|
||||
allocate(device,
|
||||
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
||||
shaderSource, shaderLabel, pipelineLabel);
|
||||
}
|
||||
|
||||
|
||||
void WgPipelineBlend::initialize(WGPUDevice device, const char *shaderSource)
|
||||
{
|
||||
// bind groups and layouts
|
||||
WGPUBindGroupLayout bindGroupLayouts[] = {
|
||||
WgBindGroupTexBlend::getLayout(device),
|
||||
WgBindGroupBlendMethod::getLayout(device),
|
||||
WgBindGroupOpacity::getLayout(device)
|
||||
};
|
||||
|
||||
// shader source and labels
|
||||
auto shaderLabel = "The compute shader blend";
|
||||
auto pipelineLabel = "The compute pipeline blend";
|
||||
|
||||
// allocate all pipeline handles
|
||||
allocate(device,
|
||||
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
||||
shaderSource, shaderLabel, pipelineLabel);
|
||||
}
|
||||
|
||||
|
||||
void WgPipelineBlendMask::initialize(WGPUDevice device, const char *shaderSource)
|
||||
{
|
||||
// bind groups and layouts
|
||||
WGPUBindGroupLayout bindGroupLayouts[] = {
|
||||
WgBindGroupTexBlendMask::getLayout(device),
|
||||
WgBindGroupBlendMethod::getLayout(device),
|
||||
WgBindGroupOpacity::getLayout(device)
|
||||
};
|
||||
|
||||
// shader source and labels
|
||||
auto shaderLabel = "The compute shader blend mask";
|
||||
auto pipelineLabel = "The compute pipeline blend mask";
|
||||
|
||||
// allocate all pipeline handles
|
||||
allocate(device,
|
||||
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
||||
shaderSource, shaderLabel, pipelineLabel);
|
||||
}
|
||||
|
||||
|
||||
void WgPipelineMaskCompose::initialize(WGPUDevice device)
|
||||
{
|
||||
// bind groups and layouts
|
||||
WGPUBindGroupLayout bindGroupLayouts[] = {
|
||||
WgBindGroupTexMaskCompose::getLayout(device),
|
||||
};
|
||||
|
||||
// shader source and labels
|
||||
auto shaderSource = cShaderSource_PipelineComputeMaskCompose;
|
||||
auto shaderLabel = "The compute shader mask compose";
|
||||
auto pipelineLabel = "The compute pipeline mask compose";
|
||||
|
||||
// allocate all pipeline handles
|
||||
allocate(device,
|
||||
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
||||
shaderSource, shaderLabel, pipelineLabel);
|
||||
}
|
||||
|
||||
|
||||
void WgPipelineCompose::initialize(WGPUDevice device)
|
||||
{
|
||||
// bind groups and layouts
|
||||
WGPUBindGroupLayout bindGroupLayouts[] = {
|
||||
WgBindGroupTexCompose::getLayout(device),
|
||||
WgBindGroupCompositeMethod::getLayout(device),
|
||||
WgBindGroupBlendMethod::getLayout(device),
|
||||
WgBindGroupOpacity::getLayout(device)
|
||||
};
|
||||
|
||||
// shader source and labels
|
||||
auto shaderSource = cShaderSource_PipelineComputeCompose;
|
||||
auto shaderLabel = "The compute shader compose blend";
|
||||
auto pipelineLabel = "The compute pipeline compose blend";
|
||||
|
||||
// allocate all pipeline handles
|
||||
allocate(device,
|
||||
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
||||
shaderSource, shaderLabel, pipelineLabel);
|
||||
}
|
||||
|
||||
|
||||
void WgPipelineAntiAliasing::initialize(WGPUDevice device)
|
||||
{
|
||||
// bind groups and layouts
|
||||
WGPUBindGroupLayout bindGroupLayouts[] = {
|
||||
WgBindGroupTextureStorageRgbaRO::getLayout(device),
|
||||
WgBindGroupTextureStorageBgraWO::getLayout(device)
|
||||
};
|
||||
|
||||
// shader source and labels
|
||||
auto shaderSource = cShaderSource_PipelineComputeAntiAlias;
|
||||
auto shaderLabel = "The compute shader anti-aliasing";
|
||||
auto pipelineLabel = "The compute pipeline anti-aliasing";
|
||||
|
||||
// allocate all pipeline handles
|
||||
allocate(device,
|
||||
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
|
||||
shaderSource, shaderLabel, pipelineLabel);
|
||||
}
|
||||
|
||||
//************************************************************************
|
||||
// pipelines
|
||||
//************************************************************************
|
||||
|
||||
void WgPipelines::initialize(WgContext& context)
|
||||
{
|
||||
// fill pipelines
|
||||
fillShapeWinding.initialize(context.device);
|
||||
fillShapeEvenOdd.initialize(context.device);
|
||||
fillStroke.initialize(context.device);
|
||||
clipMask.initialize(context.device);
|
||||
for (uint8_t type = (uint8_t)WgPipelineBlendType::SrcOver; type <= (uint8_t)WgPipelineBlendType::Custom; type++) {
|
||||
solid[type].initialize(context.device, (WgPipelineBlendType)type);
|
||||
linear[type].initialize(context.device, (WgPipelineBlendType)type);
|
||||
radial[type].initialize(context.device, (WgPipelineBlendType)type);
|
||||
image[type].initialize(context.device, (WgPipelineBlendType)type);
|
||||
}
|
||||
// compute pipelines
|
||||
computeCopy.initialize(context.device);
|
||||
computeBlendSolid.initialize(context.device, cShaderSource_PipelineComputeBlendSolid);
|
||||
computeBlendGradient.initialize(context.device, cShaderSource_PipelineComputeBlendGradient);
|
||||
computeBlendImage.initialize(context.device, cShaderSource_PipelineComputeBlendImage);
|
||||
computeBlendSolidMask.initialize(context.device, cShaderSource_PipelineComputeBlendSolidMask);
|
||||
computeBlendGradientMask.initialize(context.device, cShaderSource_PipelineComputeBlendGradientMask);
|
||||
computeBlendImageMask.initialize(context.device, cShaderSource_PipelineComputeBlendImageMask);
|
||||
computeMaskCompose.initialize(context.device);
|
||||
computeCompose.initialize(context.device);
|
||||
computeAntiAliasing.initialize(context.device);
|
||||
// store pipelines to context
|
||||
// share pipelines to context
|
||||
assert(!context.pipelines);
|
||||
context.pipelines = this;
|
||||
}
|
||||
|
||||
// initialize bind group layouts
|
||||
layouts.initialize(context);
|
||||
|
||||
void WgPipelines::release()
|
||||
{
|
||||
WgBindGroupTexCompose::releaseLayout();
|
||||
WgBindGroupTexMaskCompose::releaseLayout();
|
||||
WgBindGroupTexBlendMask::releaseLayout();
|
||||
WgBindGroupTexBlend::releaseLayout();
|
||||
WgBindGroupTextureStorageBgraRO::releaseLayout();
|
||||
WgBindGroupTextureStorageBgraWO::releaseLayout();
|
||||
WgBindGroupTextureStorageRgbaRO::releaseLayout();
|
||||
WgBindGroupTextureStorageRgbaWO::releaseLayout();
|
||||
WgBindGroupTexture::releaseLayout();
|
||||
WgBindGroupOpacity::releaseLayout();
|
||||
WgBindGroupPicture::releaseLayout();
|
||||
WgBindGroupRadialGradient::releaseLayout();
|
||||
WgBindGroupLinearGradient::releaseLayout();
|
||||
WgBindGroupSolidColor::releaseLayout();
|
||||
WgBindGroupPaint::releaseLayout();
|
||||
WgBindGroupCanvas::releaseLayout();
|
||||
// compute pipelines
|
||||
computeAntiAliasing.release();
|
||||
computeCompose.release();
|
||||
computeBlendImageMask.release();
|
||||
computeBlendGradientMask.release();
|
||||
computeBlendSolidMask.release();
|
||||
computeBlendImage.release();
|
||||
computeBlendGradient.release();
|
||||
computeBlendSolid.release();
|
||||
computeCopy.release();
|
||||
// fill pipelines
|
||||
for (uint8_t type = (uint8_t)WgPipelineBlendType::SrcOver; type <= (uint8_t)WgPipelineBlendType::Custom; type++) {
|
||||
image[type].release();
|
||||
radial[type].release();
|
||||
linear[type].release();
|
||||
solid[type].release();
|
||||
// common pipeline settings
|
||||
const WGPUVertexAttribute vertexAttributePos { .format = WGPUVertexFormat_Float32x2, .offset = 0, .shaderLocation = 0 };
|
||||
const WGPUVertexAttribute vertexAttributeTex { .format = WGPUVertexFormat_Float32x2, .offset = 0, .shaderLocation = 1 };
|
||||
const WGPUVertexAttribute vertexAttributesPos[] { vertexAttributePos };
|
||||
const WGPUVertexAttribute vertexAttributesTex[] { vertexAttributeTex };
|
||||
const WGPUVertexBufferLayout vertexBufferLayoutPos { .arrayStride = 8, .stepMode = WGPUVertexStepMode_Vertex, .attributeCount = 1, .attributes = vertexAttributesPos };
|
||||
const WGPUVertexBufferLayout vertexBufferLayoutTex { .arrayStride = 8, .stepMode = WGPUVertexStepMode_Vertex, .attributeCount = 1, .attributes = vertexAttributesTex };
|
||||
const WGPUVertexBufferLayout vertexBufferLayoutsShape[] { vertexBufferLayoutPos };
|
||||
const WGPUVertexBufferLayout vertexBufferLayoutsImage[] { vertexBufferLayoutPos, vertexBufferLayoutTex };
|
||||
const WGPUPrimitiveState primitiveState { .topology = WGPUPrimitiveTopology_TriangleList };
|
||||
const WGPUMultisampleState multisampleState { .count = 1, .mask = 0xFFFFFFFF, .alphaToCoverageEnabled = false };
|
||||
|
||||
// blend states
|
||||
const WGPUBlendState blendStateSrc {
|
||||
.color = { .operation = WGPUBlendOperation_Add, .srcFactor = WGPUBlendFactor_One, .dstFactor = WGPUBlendFactor_Zero },
|
||||
.alpha = { .operation = WGPUBlendOperation_Add, .srcFactor = WGPUBlendFactor_One, .dstFactor = WGPUBlendFactor_Zero }
|
||||
};
|
||||
const WGPUBlendState blendStateNrm {
|
||||
.color = { .operation = WGPUBlendOperation_Add, .srcFactor = WGPUBlendFactor_One, .dstFactor = WGPUBlendFactor_OneMinusSrcAlpha },
|
||||
.alpha = { .operation = WGPUBlendOperation_Add, .srcFactor = WGPUBlendFactor_One, .dstFactor = WGPUBlendFactor_OneMinusSrcAlpha }
|
||||
};
|
||||
const WGPUBlendState blendStates[] {
|
||||
blendStateSrc, // WgPipelineBlendType::SrcOver
|
||||
blendStateNrm, // WgPipelineBlendType::Normal
|
||||
blendStateSrc // WgPipelineBlendType::Custom (same as SrcOver)
|
||||
};
|
||||
|
||||
// bind group layouts
|
||||
const WGPUBindGroupLayout bindGroupLayoutsStencil[] = {
|
||||
layouts.layoutBuffer1Un,
|
||||
layouts.layoutBuffer2Un
|
||||
};
|
||||
const WGPUBindGroupLayout bindGroupLayoutsFill[] = {
|
||||
layouts.layoutBuffer1Un,
|
||||
layouts.layoutBuffer2Un,
|
||||
layouts.layoutBuffer1Un
|
||||
};
|
||||
const WGPUBindGroupLayout bindGroupLayoutsImage[] = {
|
||||
layouts.layoutBuffer1Un,
|
||||
layouts.layoutBuffer2Un,
|
||||
layouts.layoutTexSampled
|
||||
};
|
||||
const WGPUBindGroupLayout bindGroupLayoutsMergeMasks[] = {
|
||||
layouts.layoutTexStrorage1RO,
|
||||
layouts.layoutTexStrorage1RO,
|
||||
layouts.layoutTexStrorage1WO
|
||||
};
|
||||
const WGPUBindGroupLayout bindGroupLayoutsBlend[] = {
|
||||
layouts.layoutTexStrorage1RO,
|
||||
layouts.layoutTexStrorage1RO,
|
||||
layouts.layoutTexStrorage1WO,
|
||||
layouts.layoutBuffer1Un
|
||||
};
|
||||
const WGPUBindGroupLayout bindGroupLayoutsCompose[] = {
|
||||
layouts.layoutTexStrorage1RO,
|
||||
layouts.layoutTexStrorage1RO,
|
||||
layouts.layoutTexStrorage1RO,
|
||||
layouts.layoutTexStrorage1WO
|
||||
};
|
||||
const WGPUBindGroupLayout bindGroupLayoutsCopy[] = {
|
||||
layouts.layoutTexStrorage1RO,
|
||||
layouts.layoutTexScreen1WO
|
||||
};
|
||||
|
||||
// pipeline layouts
|
||||
layoutStencil = createPipelineLayout(context.device, bindGroupLayoutsStencil, 2);
|
||||
layoutFill = createPipelineLayout(context.device, bindGroupLayoutsFill, 3);
|
||||
layoutImage = createPipelineLayout(context.device, bindGroupLayoutsImage, 3);
|
||||
layoutBlend = createPipelineLayout(context.device, bindGroupLayoutsBlend, 4);
|
||||
layoutCompose = createPipelineLayout(context.device, bindGroupLayoutsCompose, 4);
|
||||
layoutMergeMasks = createPipelineLayout(context.device, bindGroupLayoutsMergeMasks, 3);
|
||||
layoutCopy = createPipelineLayout(context.device, bindGroupLayoutsCopy, 2);
|
||||
|
||||
// graphics shader modules
|
||||
shaderStencil = createShaderModule(context.device, "The shader stencil", cShaderSrc_Stencil);
|
||||
shaderSolid = createShaderModule(context.device, "The shader solid", cShaderSrc_Solid);
|
||||
shaderRadial = createShaderModule(context.device, "The shader radial", cShaderSrc_Radial);
|
||||
shaderLinear = createShaderModule(context.device, "The shader linear", cShaderSrc_Linear);
|
||||
shaderImage = createShaderModule(context.device, "The shader image", cShaderSrc_Image);
|
||||
// computes shader modules
|
||||
shaderMergeMasks = createShaderModule(context.device, "The shader merge mask", cShaderSrc_MergeMasks);
|
||||
shaderCopy = createShaderModule(context.device, "The shader copy", cShaderSrc_Copy);
|
||||
|
||||
// render pipeline winding
|
||||
winding = createRenderPipeline(
|
||||
context.device, "The render pipeline winding",
|
||||
shaderStencil, layoutStencil,
|
||||
vertexBufferLayoutsShape, 1,
|
||||
WGPUColorWriteMask_None,
|
||||
WGPUCompareFunction_Always, WGPUStencilOperation_IncrementWrap,
|
||||
WGPUCompareFunction_Always, WGPUStencilOperation_DecrementWrap,
|
||||
primitiveState, multisampleState, blendStateSrc);
|
||||
// render pipeline even-odd
|
||||
evenodd = createRenderPipeline(
|
||||
context.device, "The render pipeline even-odd",
|
||||
shaderStencil, layoutStencil,
|
||||
vertexBufferLayoutsShape, 1,
|
||||
WGPUColorWriteMask_None,
|
||||
WGPUCompareFunction_Always, WGPUStencilOperation_Invert,
|
||||
WGPUCompareFunction_Always, WGPUStencilOperation_Invert,
|
||||
primitiveState, multisampleState, blendStateSrc);
|
||||
// render pipeline direct
|
||||
direct = createRenderPipeline(
|
||||
context.device, "The render pipeline direct",
|
||||
shaderStencil, layoutStencil,
|
||||
vertexBufferLayoutsShape, 1,
|
||||
WGPUColorWriteMask_None,
|
||||
WGPUCompareFunction_Always, WGPUStencilOperation_Replace,
|
||||
WGPUCompareFunction_Always, WGPUStencilOperation_Replace,
|
||||
primitiveState, multisampleState, blendStateSrc);
|
||||
// render pipeline clip path
|
||||
clipPath = createRenderPipeline(
|
||||
context.device, "The render pipeline clip path",
|
||||
shaderStencil, layoutStencil,
|
||||
vertexBufferLayoutsShape, 1,
|
||||
WGPUColorWriteMask_All,
|
||||
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
|
||||
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
|
||||
primitiveState, multisampleState, blendStateSrc);
|
||||
|
||||
// render pipeline solid
|
||||
for (uint32_t i = 0; i < 3; i++) {
|
||||
solid[i] = createRenderPipeline(
|
||||
context.device, "The render pipeline solid",
|
||||
shaderSolid, layoutFill,
|
||||
vertexBufferLayoutsShape, 1,
|
||||
WGPUColorWriteMask_All,
|
||||
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
|
||||
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
|
||||
primitiveState, multisampleState, blendStates[i]);
|
||||
}
|
||||
|
||||
// render pipeline radial
|
||||
for (uint32_t i = 0; i < 3; i++) {
|
||||
radial[i] = createRenderPipeline(
|
||||
context.device, "The render pipeline radial",
|
||||
shaderRadial, layoutFill,
|
||||
vertexBufferLayoutsShape, 1,
|
||||
WGPUColorWriteMask_All,
|
||||
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
|
||||
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
|
||||
primitiveState, multisampleState, blendStates[i]);
|
||||
}
|
||||
|
||||
// render pipeline linear
|
||||
for (uint32_t i = 0; i < 3; i++) {
|
||||
linear[i] = createRenderPipeline(
|
||||
context.device, "The render pipeline linear",
|
||||
shaderLinear, layoutFill,
|
||||
vertexBufferLayoutsShape, 1,
|
||||
WGPUColorWriteMask_All,
|
||||
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
|
||||
WGPUCompareFunction_NotEqual, WGPUStencilOperation_Zero,
|
||||
primitiveState, multisampleState, blendStates[i]);
|
||||
}
|
||||
|
||||
// render pipeline image
|
||||
for (uint32_t i = 0; i < 3; i++) {
|
||||
image[i] = createRenderPipeline(
|
||||
context.device, "The render pipeline image",
|
||||
shaderImage, layoutImage,
|
||||
vertexBufferLayoutsImage, 2,
|
||||
WGPUColorWriteMask_All,
|
||||
WGPUCompareFunction_Always, WGPUStencilOperation_Zero,
|
||||
WGPUCompareFunction_Always, WGPUStencilOperation_Zero,
|
||||
primitiveState, multisampleState, blendStates[i]);
|
||||
}
|
||||
|
||||
// compute pipelines
|
||||
mergeMasks = createComputePipeline(context.device, "The pipeline merge masks", shaderMergeMasks, layoutMergeMasks);
|
||||
copy = createComputePipeline(context.device, "The pipeline copy", shaderCopy, layoutCopy);
|
||||
// compute pipelines blend
|
||||
const size_t shaderBlendCnt = sizeof(cShaderSrc_Blend_Solid)/sizeof(cShaderSrc_Blend_Solid[0]);
|
||||
for (uint32_t i = 0; i < shaderBlendCnt; i++) {
|
||||
// blend shaders
|
||||
shaderBlendSolid[i] = createShaderModule(context.device, "The shader blend solid", cShaderSrc_Blend_Solid[i]);
|
||||
shaderBlendGradient[i] = createShaderModule(context.device, "The shader blend gradient", cShaderSrc_Blend_Gradient[i]);
|
||||
shaderBlendImage[i] = createShaderModule(context.device, "The shader blend image", cShaderSrc_Blend_Image[i]);
|
||||
// blend pipelines
|
||||
blendSolid[i] = createComputePipeline(context.device, "The pipeline blend solid", shaderBlendSolid[i], layoutBlend);
|
||||
blendGradient[i] = createComputePipeline(context.device, "The pipeline blend gradient", shaderBlendGradient[i], layoutBlend);
|
||||
blendImage[i] = createComputePipeline(context.device, "The pipeline blend image", shaderBlendImage[i], layoutBlend);
|
||||
}
|
||||
// compute pipelines compose
|
||||
const size_t shaderComposeCnt = sizeof(cShaderSrc_Compose)/sizeof(cShaderSrc_Compose[0]);
|
||||
for (uint32_t i = 0; i < shaderComposeCnt; i++) {
|
||||
shaderCompose[i] = createShaderModule(context.device, "The shader compose", cShaderSrc_Compose[i]);
|
||||
compose[i] = createComputePipeline(context.device, "The pipeline compose", shaderCompose[i], layoutCompose);
|
||||
}
|
||||
clipMask.release();
|
||||
fillStroke.release();
|
||||
fillShapeEvenOdd.release();
|
||||
fillShapeWinding.release();
|
||||
}
|
||||
|
||||
|
||||
bool WgPipelines::isBlendMethodSupportsHW(BlendMethod blendMethod)
|
||||
void WgPipelines::releaseGraphicHandles(WgContext& context)
|
||||
{
|
||||
switch (blendMethod) {
|
||||
case BlendMethod::SrcOver:
|
||||
case BlendMethod::Normal:
|
||||
return true;
|
||||
default: return false;
|
||||
};
|
||||
releaseRenderPipeline(clipPath);
|
||||
for (uint32_t i = 0; i < 3; i++) {
|
||||
releaseRenderPipeline(image[i]);
|
||||
releaseRenderPipeline(linear[i]);
|
||||
releaseRenderPipeline(radial[i]);
|
||||
releaseRenderPipeline(solid[i]);
|
||||
}
|
||||
releaseRenderPipeline(direct);
|
||||
releaseRenderPipeline(evenodd);
|
||||
releaseRenderPipeline(winding);
|
||||
releasePipelineLayout(layoutImage);
|
||||
releasePipelineLayout(layoutFill);
|
||||
releasePipelineLayout(layoutStencil);
|
||||
releaseShaderModule(shaderImage);
|
||||
releaseShaderModule(shaderLinear);
|
||||
releaseShaderModule(shaderRadial);
|
||||
releaseShaderModule(shaderSolid);
|
||||
releaseShaderModule(shaderStencil);
|
||||
}
|
||||
|
||||
|
||||
WgPipelineBlendType WgPipelines::blendMethodToBlendType(BlendMethod blendMethod)
|
||||
void WgPipelines::releaseComputeHandles(WgContext& context)
|
||||
{
|
||||
switch (blendMethod) {
|
||||
case BlendMethod::SrcOver: return WgPipelineBlendType::SrcOver;
|
||||
case BlendMethod::Normal: return WgPipelineBlendType::Normal;
|
||||
default: return WgPipelineBlendType::Custom;
|
||||
};
|
||||
const size_t shaderComposeCnt = sizeof(shaderCompose)/sizeof(shaderCompose[0]);
|
||||
for (uint32_t i = 0; i < shaderComposeCnt; i++) {
|
||||
releaseComputePipeline(compose[i]);
|
||||
releaseShaderModule(shaderCompose[i]);
|
||||
}
|
||||
const size_t shaderBlendImageCnt = sizeof(shaderBlendImage)/sizeof(shaderBlendImage[0]);
|
||||
for (uint32_t i = 0; i < shaderBlendImageCnt; i++) {
|
||||
releaseComputePipeline(blendImage[i]);
|
||||
releaseComputePipeline(blendSolid[i]);
|
||||
releaseComputePipeline(blendGradient[i]);
|
||||
releaseShaderModule(shaderBlendImage[i]);
|
||||
releaseShaderModule(shaderBlendGradient[i]);
|
||||
releaseShaderModule(shaderBlendSolid[i]);
|
||||
}
|
||||
releaseComputePipeline(copy);
|
||||
releaseComputePipeline(mergeMasks);
|
||||
releasePipelineLayout(layoutCompose);
|
||||
releasePipelineLayout(layoutBlend);
|
||||
releasePipelineLayout(layoutMergeMasks);
|
||||
releasePipelineLayout(layoutCopy);
|
||||
releaseShaderModule(shaderMergeMasks);
|
||||
releaseShaderModule(shaderCopy);
|
||||
}
|
||||
|
||||
void WgPipelines::release(WgContext& context)
|
||||
{
|
||||
releaseComputeHandles(context);
|
||||
releaseGraphicHandles(context);
|
||||
layouts.release(context);
|
||||
}
|
||||
|
|
278
src/renderer/wg_engine/tvgWgPipelines.h
Normal file → Executable file
278
src/renderer/wg_engine/tvgWgPipelines.h
Normal file → Executable file
|
@ -25,219 +25,75 @@
|
|||
|
||||
#include "tvgWgBindGroups.h"
|
||||
|
||||
//*****************************************************************************
|
||||
// render pipelines
|
||||
//*****************************************************************************
|
||||
|
||||
struct WgPipelineFillShapeWinding: public WgRenderPipeline
|
||||
{
|
||||
void initialize(WGPUDevice device) override;
|
||||
void use(WGPURenderPassEncoder encoder, WgBindGroupCanvas& groupCanvas, WgBindGroupPaint& groupPaint)
|
||||
{
|
||||
set(encoder);
|
||||
groupCanvas.set(encoder, 0);
|
||||
groupPaint.set(encoder, 1);
|
||||
}
|
||||
};
|
||||
|
||||
struct WgPipelineFillShapeEvenOdd: public WgRenderPipeline
|
||||
{
|
||||
void initialize(WGPUDevice device) override;
|
||||
void use(WGPURenderPassEncoder encoder, WgBindGroupCanvas& groupCanvas, WgBindGroupPaint& groupPaint)
|
||||
{
|
||||
set(encoder);
|
||||
groupCanvas.set(encoder, 0);
|
||||
groupPaint.set(encoder, 1);
|
||||
}
|
||||
};
|
||||
|
||||
struct WgPipelineFillStroke: public WgRenderPipeline
|
||||
{
|
||||
void initialize(WGPUDevice device) override;
|
||||
void use(WGPURenderPassEncoder encoder, WgBindGroupCanvas& groupCanvas, WgBindGroupPaint& groupPaint)
|
||||
{
|
||||
set(encoder);
|
||||
groupCanvas.set(encoder, 0);
|
||||
groupPaint.set(encoder, 1);
|
||||
}
|
||||
};
|
||||
|
||||
struct WgPipelineClipMask: public WgRenderPipeline
|
||||
{
|
||||
void initialize(WGPUDevice device) override;
|
||||
void use(WGPURenderPassEncoder encoder, WgBindGroupCanvas& groupCanvas,WgBindGroupPaint& groupPaint)
|
||||
{
|
||||
set(encoder);
|
||||
groupCanvas.set(encoder, 0);
|
||||
groupPaint.set(encoder, 1);
|
||||
}
|
||||
};
|
||||
|
||||
struct WgPipelineSolid: public WgRenderPipeline
|
||||
{
|
||||
void initialize(WGPUDevice device) override {}
|
||||
void initialize(WGPUDevice device, WgPipelineBlendType blendType);
|
||||
void use(WGPURenderPassEncoder encoder, WgBindGroupCanvas& groupCanvas,WgBindGroupPaint& groupPaint, WgBindGroupSolidColor& groupSolid)
|
||||
{
|
||||
set(encoder);
|
||||
groupCanvas.set(encoder, 0);
|
||||
groupPaint.set(encoder, 1);
|
||||
groupSolid.set(encoder, 2);
|
||||
}
|
||||
};
|
||||
|
||||
struct WgPipelineLinear: public WgRenderPipeline
|
||||
{
|
||||
void initialize(WGPUDevice device) override {}
|
||||
void initialize(WGPUDevice device, WgPipelineBlendType blendType);
|
||||
void use(WGPURenderPassEncoder encoder, WgBindGroupCanvas& groupCanvas, WgBindGroupPaint& groupPaint, WgBindGroupLinearGradient& groupLinear)
|
||||
{
|
||||
set(encoder);
|
||||
groupCanvas.set(encoder, 0);
|
||||
groupPaint.set(encoder, 1);
|
||||
groupLinear.set(encoder, 2);
|
||||
}
|
||||
};
|
||||
|
||||
struct WgPipelineRadial: public WgRenderPipeline
|
||||
{
|
||||
void initialize(WGPUDevice device) override {}
|
||||
void initialize(WGPUDevice device, WgPipelineBlendType blendType);
|
||||
void use(WGPURenderPassEncoder encoder, WgBindGroupCanvas& groupCanvas, WgBindGroupPaint& groupPaint, WgBindGroupRadialGradient& groupRadial)
|
||||
{
|
||||
set(encoder);
|
||||
groupCanvas.set(encoder, 0);
|
||||
groupPaint.set(encoder, 1);
|
||||
groupRadial.set(encoder, 2);
|
||||
}
|
||||
};
|
||||
|
||||
struct WgPipelineImage: public WgRenderPipeline
|
||||
{
|
||||
void initialize(WGPUDevice device) override { assert(false); };
|
||||
void initialize(WGPUDevice device, WgPipelineBlendType blendType);
|
||||
void use(WGPURenderPassEncoder encoder, WgBindGroupCanvas& groupCanvas, WgBindGroupPaint& groupPaint, WgBindGroupPicture& groupPicture)
|
||||
{
|
||||
set(encoder);
|
||||
groupCanvas.set(encoder, 0);
|
||||
groupPaint.set(encoder, 1);
|
||||
groupPicture.set(encoder, 2);
|
||||
}
|
||||
};
|
||||
|
||||
//*****************************************************************************
|
||||
// compute pipelines
|
||||
//*****************************************************************************
|
||||
|
||||
struct WgPipelineCopy: public WgComputePipeline
|
||||
{
|
||||
void initialize(WGPUDevice device) override;
|
||||
void use(WGPUComputePassEncoder encoder, WgBindGroupTextureStorageRgbaRO& groupTexSrc, WgBindGroupTextureStorageRgbaWO& groupTexDst)
|
||||
{
|
||||
set(encoder);
|
||||
groupTexSrc.set(encoder, 0);
|
||||
groupTexDst.set(encoder, 1);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct WgPipelineBlend: public WgComputePipeline
|
||||
{
|
||||
void initialize(WGPUDevice device) override { assert(false); };
|
||||
void initialize(WGPUDevice device, const char *shaderSource);
|
||||
void use(WGPUComputePassEncoder encoder, WgBindGroupTexBlend& groupTexBlend, WgBindGroupBlendMethod& blendMethod, WgBindGroupOpacity& groupOpacity)
|
||||
{
|
||||
set(encoder);
|
||||
groupTexBlend.set(encoder, 0);
|
||||
blendMethod.set(encoder, 1);
|
||||
groupOpacity.set(encoder, 2);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct WgPipelineBlendMask: public WgComputePipeline
|
||||
{
|
||||
void initialize(WGPUDevice device) override { assert(false); };
|
||||
void initialize(WGPUDevice device, const char *shaderSource);
|
||||
void use(WGPUComputePassEncoder encoder, WgBindGroupTexBlendMask& groupTexBlendMask, WgBindGroupBlendMethod& blendMethod, WgBindGroupOpacity& groupOpacity)
|
||||
{
|
||||
set(encoder);
|
||||
groupTexBlendMask.set(encoder, 0);
|
||||
blendMethod.set(encoder, 1);
|
||||
groupOpacity.set(encoder, 2);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct WgPipelineMaskCompose: public WgComputePipeline
|
||||
{
|
||||
void initialize(WGPUDevice device) override;
|
||||
void use(WGPUComputePassEncoder encoder, WgBindGroupTexMaskCompose& groupTexMaskCompose)
|
||||
{
|
||||
set(encoder);
|
||||
groupTexMaskCompose.set(encoder, 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct WgPipelineCompose: public WgComputePipeline
|
||||
{
|
||||
void initialize(WGPUDevice device) override;
|
||||
void use(WGPUComputePassEncoder encoder, WgBindGroupTexCompose& groupTexCompose, WgBindGroupCompositeMethod& groupComposeMethod, WgBindGroupBlendMethod& groupBlendMethod, WgBindGroupOpacity& groupOpacity)
|
||||
{
|
||||
set(encoder);
|
||||
groupTexCompose.set(encoder, 0);
|
||||
groupComposeMethod.set(encoder, 1);
|
||||
groupBlendMethod.set(encoder, 2);
|
||||
groupOpacity.set(encoder, 3);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct WgPipelineAntiAliasing: public WgComputePipeline
|
||||
{
|
||||
void initialize(WGPUDevice device) override;
|
||||
void use(WGPUComputePassEncoder encoder, WgBindGroupTextureStorageRgbaRO& groupTexSrc, WgBindGroupTextureStorageBgraWO& groupTexDst)
|
||||
{
|
||||
set(encoder);
|
||||
groupTexSrc.set(encoder, 0);
|
||||
groupTexDst.set(encoder, 1);
|
||||
}
|
||||
};
|
||||
|
||||
//*****************************************************************************
|
||||
// pipelines
|
||||
//*****************************************************************************
|
||||
|
||||
struct WgPipelines
|
||||
{
|
||||
// render pipelines
|
||||
WgPipelineFillShapeWinding fillShapeWinding;
|
||||
WgPipelineFillShapeEvenOdd fillShapeEvenOdd;
|
||||
WgPipelineFillStroke fillStroke;
|
||||
// fill pipelines
|
||||
WgPipelineClipMask clipMask;
|
||||
WgPipelineSolid solid[3];
|
||||
WgPipelineLinear linear[3];
|
||||
WgPipelineRadial radial[3];
|
||||
WgPipelineImage image[3];
|
||||
// compute pipelines
|
||||
WgPipelineCopy computeCopy;
|
||||
WgPipelineBlend computeBlendSolid;
|
||||
WgPipelineBlend computeBlendGradient;
|
||||
WgPipelineBlend computeBlendImage;
|
||||
WgPipelineBlendMask computeBlendSolidMask;
|
||||
WgPipelineBlendMask computeBlendGradientMask;
|
||||
WgPipelineBlendMask computeBlendImageMask;
|
||||
WgPipelineMaskCompose computeMaskCompose;
|
||||
WgPipelineCompose computeCompose;
|
||||
WgPipelineAntiAliasing computeAntiAliasing;
|
||||
enum class WgPipelineBlendType { SrcOver = 0, Normal, Custom };
|
||||
|
||||
class WgPipelines {
|
||||
private:
|
||||
// graphics pipeline shaders
|
||||
WGPUShaderModule shaderStencil{};
|
||||
WGPUShaderModule shaderSolid{};
|
||||
WGPUShaderModule shaderRadial{};
|
||||
WGPUShaderModule shaderLinear{};
|
||||
WGPUShaderModule shaderImage{};
|
||||
// compute pipeline shaders
|
||||
WGPUShaderModule shaderMergeMasks;
|
||||
WGPUShaderModule shaderBlendSolid[14];
|
||||
WGPUShaderModule shaderBlendGradient[14];
|
||||
WGPUShaderModule shaderBlendImage[14];
|
||||
WGPUShaderModule shaderCompose[10];
|
||||
WGPUShaderModule shaderCopy;
|
||||
private:
|
||||
// graphics pipeline layouts
|
||||
WGPUPipelineLayout layoutStencil{};
|
||||
WGPUPipelineLayout layoutFill{};
|
||||
WGPUPipelineLayout layoutImage{};
|
||||
// compute pipeline layouts
|
||||
WGPUPipelineLayout layoutMergeMasks{};
|
||||
WGPUPipelineLayout layoutBlend{};
|
||||
WGPUPipelineLayout layoutCompose{};
|
||||
WGPUPipelineLayout layoutCopy{};
|
||||
public:
|
||||
// graphics pipeline
|
||||
WgBindGroupLayouts layouts;
|
||||
WGPURenderPipeline winding{};
|
||||
WGPURenderPipeline evenodd{};
|
||||
WGPURenderPipeline direct{};
|
||||
WGPURenderPipeline solid[3]{};
|
||||
WGPURenderPipeline radial[3]{};
|
||||
WGPURenderPipeline linear[3]{};
|
||||
WGPURenderPipeline image[3]{};
|
||||
WGPURenderPipeline clipPath{};
|
||||
// compute pipeline
|
||||
WGPUComputePipeline mergeMasks;
|
||||
WGPUComputePipeline blendSolid[14];
|
||||
WGPUComputePipeline blendGradient[14];
|
||||
WGPUComputePipeline blendImage[14];
|
||||
WGPUComputePipeline compose[10];
|
||||
WGPUComputePipeline copy;
|
||||
private:
|
||||
void releaseGraphicHandles(WgContext& context);
|
||||
void releaseComputeHandles(WgContext& context);
|
||||
private:
|
||||
WGPUShaderModule createShaderModule(WGPUDevice device, const char* label, const char* code);
|
||||
WGPUPipelineLayout createPipelineLayout(WGPUDevice device, const WGPUBindGroupLayout* bindGroupLayouts, const uint32_t bindGroupLayoutsCount);
|
||||
WGPURenderPipeline createRenderPipeline(
|
||||
WGPUDevice device, const char* pipelineLabel,
|
||||
const WGPUShaderModule shaderModule, const WGPUPipelineLayout pipelineLayout,
|
||||
const WGPUVertexBufferLayout *vertexBufferLayouts, const uint32_t vertexBufferLayoutsCount,
|
||||
const WGPUColorWriteMaskFlags writeMask,
|
||||
const WGPUCompareFunction stencilFunctionFrnt, const WGPUStencilOperation stencilOperationFrnt,
|
||||
const WGPUCompareFunction stencilFunctionBack, const WGPUStencilOperation stencilOperationBack,
|
||||
const WGPUPrimitiveState primitiveState, const WGPUMultisampleState multisampleState, const WGPUBlendState blendState);
|
||||
WGPUComputePipeline createComputePipeline(
|
||||
WGPUDevice device, const char* pipelineLabel,
|
||||
const WGPUShaderModule shaderModule, const WGPUPipelineLayout pipelineLayout);
|
||||
void releaseComputePipeline(WGPUComputePipeline& computePipeline);
|
||||
void releaseRenderPipeline(WGPURenderPipeline& renderPipeline);
|
||||
void releasePipelineLayout(WGPUPipelineLayout& pipelineLayout);
|
||||
void releaseShaderModule(WGPUShaderModule& shaderModule);
|
||||
public:
|
||||
void initialize(WgContext& context);
|
||||
void release();
|
||||
|
||||
static bool isBlendMethodSupportsHW(BlendMethod blendMethod);
|
||||
static WgPipelineBlendType blendMethodToBlendType(BlendMethod blendMethod);
|
||||
void release(WgContext& context);
|
||||
};
|
||||
|
||||
#endif // _TVG_WG_PIPELINES_H_
|
||||
|
|
93
src/renderer/wg_engine/tvgWgRenderData.cpp
Normal file → Executable file
93
src/renderer/wg_engine/tvgWgRenderData.cpp
Normal file → Executable file
|
@ -21,11 +21,9 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_WG_RENDER_DATA_H_
|
||||
#define _TVG_WG_RENDER_DATA_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include "tvgWgRenderData.h"
|
||||
#include "tvgWgShaderTypes.h"
|
||||
|
||||
//***********************************************************************
|
||||
// WgMeshData
|
||||
|
@ -42,7 +40,7 @@ void WgMeshData::draw(WgContext& context, WGPURenderPassEncoder renderPassEncode
|
|||
void WgMeshData::drawFan(WgContext& context, WGPURenderPassEncoder renderPassEncoder)
|
||||
{
|
||||
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, bufferPosition, 0, vertexCount * sizeof(float) * 2);
|
||||
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, context.indexBufferFan, WGPUIndexFormat_Uint32, 0, indexCount * sizeof(uint32_t));
|
||||
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, context.bufferIndexFan, WGPUIndexFormat_Uint32, 0, indexCount * sizeof(uint32_t));
|
||||
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, indexCount, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
@ -62,8 +60,8 @@ void WgMeshData::update(WgContext& context, const WgPolyline* polyline)
|
|||
assert(polyline->pts.count > 2);
|
||||
vertexCount = polyline->pts.count;
|
||||
indexCount = (polyline->pts.count - 2) * 3;
|
||||
context.allocateVertexBuffer(bufferPosition, &polyline->pts[0], vertexCount * sizeof(float) * 2);
|
||||
context.allocateIndexBufferFan(vertexCount);
|
||||
context.allocateBufferVertex(bufferPosition, (float *)&polyline->pts[0], vertexCount * sizeof(float) * 2);
|
||||
context.allocateBufferIndexFan(vertexCount);
|
||||
}
|
||||
|
||||
|
||||
|
@ -74,13 +72,13 @@ void WgMeshData::update(WgContext& context, const WgGeometryData* geometryData)
|
|||
indexCount = geometryData->indexes.count;
|
||||
// buffer position data create and write
|
||||
if (geometryData->positions.pts.count > 0)
|
||||
context.allocateVertexBuffer(bufferPosition, &geometryData->positions.pts[0], vertexCount * sizeof(float) * 2);
|
||||
context.allocateBufferVertex(bufferPosition, (float *)&geometryData->positions.pts[0], vertexCount * sizeof(float) * 2);
|
||||
// buffer tex coords data create and write
|
||||
if (geometryData->texCoords.count > 0)
|
||||
context.allocateVertexBuffer(bufferTexCoord, &geometryData->texCoords[0], vertexCount * sizeof(float) * 2);
|
||||
context.allocateBufferVertex(bufferTexCoord, (float *)&geometryData->texCoords[0], vertexCount * sizeof(float) * 2);
|
||||
// buffer index data create and write
|
||||
if (geometryData->indexes.count > 0)
|
||||
context.allocateIndexBuffer(bufferIndex, &geometryData->indexes[0], indexCount * sizeof(uint32_t));
|
||||
context.allocateBufferIndex(bufferIndex, &geometryData->indexes[0], indexCount * sizeof(uint32_t));
|
||||
};
|
||||
|
||||
|
||||
|
@ -92,8 +90,8 @@ void WgMeshData::update(WgContext& context, const WgPoint pmin, const WgPoint pm
|
|||
pmin.x, pmin.y, pmax.x, pmin.y,
|
||||
pmax.x, pmax.y, pmin.x, pmax.y
|
||||
};
|
||||
context.allocateVertexBuffer(bufferPosition, data, sizeof(data));
|
||||
context.allocateIndexBufferFan(vertexCount);
|
||||
context.allocateBufferVertex(bufferPosition, data, sizeof(data));
|
||||
context.allocateBufferIndexFan(vertexCount);
|
||||
}
|
||||
|
||||
|
||||
|
@ -186,29 +184,14 @@ void WgImageData::update(WgContext& context, Surface* surface)
|
|||
{
|
||||
release(context);
|
||||
assert(surface);
|
||||
texture = context.createTexture2d(
|
||||
WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopyDst,
|
||||
WGPUTextureFormat_RGBA8Unorm,
|
||||
surface->w, surface->h, "The shape texture");
|
||||
texture = context.createTexture(surface->w, surface->h, WGPUTextureFormat_RGBA8Unorm);
|
||||
assert(texture);
|
||||
textureView = context.createTextureView2d(texture, "The shape texture view");
|
||||
textureView = context.createTextureView(texture);
|
||||
assert(textureView);
|
||||
// update texture data
|
||||
WGPUImageCopyTexture imageCopyTexture{};
|
||||
imageCopyTexture.nextInChain = nullptr;
|
||||
imageCopyTexture.texture = texture;
|
||||
imageCopyTexture.mipLevel = 0;
|
||||
imageCopyTexture.origin = { 0, 0, 0 };
|
||||
imageCopyTexture.aspect = WGPUTextureAspect_All;
|
||||
WGPUTextureDataLayout textureDataLayout{};
|
||||
textureDataLayout.nextInChain = nullptr;
|
||||
textureDataLayout.offset = 0;
|
||||
textureDataLayout.bytesPerRow = 4 * surface->w;
|
||||
textureDataLayout.rowsPerImage = surface->h;
|
||||
WGPUExtent3D writeSize{};
|
||||
writeSize.width = surface->w;
|
||||
writeSize.height = surface->h;
|
||||
writeSize.depthOrArrayLayers = 1;
|
||||
WGPUImageCopyTexture imageCopyTexture{ .texture = texture };
|
||||
WGPUTextureDataLayout textureDataLayout{ .bytesPerRow = 4 * surface->w, .rowsPerImage = surface->h };
|
||||
WGPUExtent3D writeSize{ .width = surface->w, .height = surface->h, .depthOrArrayLayers = 1 };
|
||||
wgpuQueueWriteTexture(context.queue, &imageCopyTexture, surface->data, 4 * surface->w * surface->h, &textureDataLayout, &writeSize);
|
||||
wgpuQueueSubmit(context.queue, 0, nullptr);
|
||||
};
|
||||
|
@ -228,20 +211,31 @@ void WgRenderSettings::update(WgContext& context, const Fill* fill, const uint8_
|
|||
{
|
||||
// setup fill properties
|
||||
if ((flags & (RenderUpdateFlag::Gradient)) && fill) {
|
||||
rasterType = WgRenderRasterType::Gradient;
|
||||
// setup linear fill properties
|
||||
if (fill->type() == Type::LinearGradient) {
|
||||
WgShaderTypeLinearGradient linearGradient((LinearGradient*)fill);
|
||||
bindGroupLinear.initialize(context.device, context.queue, linearGradient);
|
||||
if (context.allocateBufferUniform(bufferGroupLinear, &linearGradient, sizeof(linearGradient))) {
|
||||
context.pipelines->layouts.releaseBindGroup(bindGroupLinear);
|
||||
bindGroupLinear = context.pipelines->layouts.createBindGroupBuffer1Un(bufferGroupLinear);
|
||||
}
|
||||
fillType = WgRenderSettingsType::Linear;
|
||||
} else if (fill->type() == Type::RadialGradient) {
|
||||
WgShaderTypeRadialGradient radialGradient((RadialGradient*)fill);
|
||||
bindGroupRadial.initialize(context.device, context.queue, radialGradient);
|
||||
if (context.allocateBufferUniform(bufferGroupRadial, &radialGradient, sizeof(radialGradient))) {
|
||||
context.pipelines->layouts.releaseBindGroup(bindGroupRadial);
|
||||
bindGroupRadial = context.pipelines->layouts.createBindGroupBuffer1Un(bufferGroupRadial);
|
||||
}
|
||||
fillType = WgRenderSettingsType::Radial;
|
||||
}
|
||||
skip = false;
|
||||
} else if ((flags & (RenderUpdateFlag::Color)) && !fill) {
|
||||
rasterType = WgRenderRasterType::Solid;
|
||||
WgShaderTypeSolidColor solidColor(color);
|
||||
bindGroupSolid.initialize(context.device, context.queue, solidColor);
|
||||
if (context.allocateBufferUniform(bufferGroupSolid, &solidColor, sizeof(solidColor))) {
|
||||
context.pipelines->layouts.releaseBindGroup(bindGroupSolid);
|
||||
bindGroupSolid = context.pipelines->layouts.createBindGroupBuffer1Un(bufferGroupSolid);
|
||||
}
|
||||
fillType = WgRenderSettingsType::Solid;
|
||||
skip = (color[3] == 0);
|
||||
}
|
||||
|
@ -250,9 +244,13 @@ void WgRenderSettings::update(WgContext& context, const Fill* fill, const uint8_
|
|||
|
||||
void WgRenderSettings::release(WgContext& context)
|
||||
{
|
||||
bindGroupSolid.release();
|
||||
bindGroupLinear.release();
|
||||
bindGroupRadial.release();
|
||||
context.pipelines->layouts.releaseBindGroup(bindGroupSolid);
|
||||
context.pipelines->layouts.releaseBindGroup(bindGroupLinear);
|
||||
context.pipelines->layouts.releaseBindGroup(bindGroupRadial);
|
||||
context.releaseBuffer(bufferGroupSolid);
|
||||
context.releaseBuffer(bufferGroupLinear);
|
||||
context.releaseBuffer(bufferGroupRadial);
|
||||
|
||||
};
|
||||
|
||||
//***********************************************************************
|
||||
|
@ -261,11 +259,26 @@ void WgRenderSettings::release(WgContext& context)
|
|||
|
||||
void WgRenderDataPaint::release(WgContext& context)
|
||||
{
|
||||
bindGroupPaint.release();
|
||||
context.pipelines->layouts.releaseBindGroup(bindGroupPaint);
|
||||
context.releaseBuffer(bufferModelMat);
|
||||
context.releaseBuffer(bufferBlendSettings);
|
||||
clips.clear();
|
||||
};
|
||||
|
||||
|
||||
void WgRenderDataPaint::update(WgContext& context, const tvg::Matrix& transform, tvg::ColorSpace cs, uint8_t opacity)
|
||||
{
|
||||
WgShaderTypeMat4x4f modelMat(transform);
|
||||
WgShaderTypeBlendSettings blendSettings(cs, opacity);
|
||||
bool bufferModelMatChanged = context.allocateBufferUniform(bufferModelMat, &modelMat, sizeof(modelMat));
|
||||
bool bufferBlendSettingsChanged = context.allocateBufferUniform(bufferBlendSettings, &blendSettings, sizeof(blendSettings));
|
||||
if (bufferModelMatChanged || bufferBlendSettingsChanged) {
|
||||
context.pipelines->layouts.releaseBindGroup(bindGroupPaint);
|
||||
bindGroupPaint = context.pipelines->layouts.createBindGroupBuffer2Un(bufferModelMat, bufferBlendSettings);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WgRenderDataPaint::updateClips(tvg::Array<tvg::RenderData> &clips) {
|
||||
this->clips.clear();
|
||||
for (uint32_t i = 0; i < clips.count; i++)
|
||||
|
@ -496,8 +509,6 @@ void WgRenderDataPicture::release(WgContext& context)
|
|||
{
|
||||
meshData.release(context);
|
||||
imageData.release(context);
|
||||
bindGroupPicture.release();
|
||||
context.pipelines->layouts.releaseBindGroup(bindGroupPicture);
|
||||
WgRenderDataPaint::release(context);
|
||||
}
|
||||
|
||||
#endif //_TVG_WG_RENDER_DATA_H_
|
||||
|
|
24
src/renderer/wg_engine/tvgWgRenderData.h
Normal file → Executable file
24
src/renderer/wg_engine/tvgWgRenderData.h
Normal file → Executable file
|
@ -20,6 +20,9 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_WG_RENDER_DATA_H_
|
||||
#define _TVG_WG_RENDER_DATA_H_
|
||||
|
||||
#include "tvgWgPipelines.h"
|
||||
#include "tvgWgGeometry.h"
|
||||
|
||||
|
@ -70,13 +73,18 @@ struct WgImageData {
|
|||
};
|
||||
|
||||
enum class WgRenderSettingsType { None = 0, Solid = 1, Linear = 2, Radial = 3 };
|
||||
enum class WgRenderRasterType { Solid = 0, Gradient, Image };
|
||||
|
||||
struct WgRenderSettings
|
||||
{
|
||||
WgBindGroupSolidColor bindGroupSolid{};
|
||||
WgBindGroupLinearGradient bindGroupLinear{};
|
||||
WgBindGroupRadialGradient bindGroupRadial{};
|
||||
WGPUBuffer bufferGroupSolid{};
|
||||
WGPUBuffer bufferGroupLinear{};
|
||||
WGPUBuffer bufferGroupRadial{};
|
||||
WGPUBindGroup bindGroupSolid{};
|
||||
WGPUBindGroup bindGroupLinear{};
|
||||
WGPUBindGroup bindGroupRadial{};
|
||||
WgRenderSettingsType fillType{};
|
||||
WgRenderRasterType rasterType{};
|
||||
bool skip{};
|
||||
|
||||
void update(WgContext& context, const Fill* fill, const uint8_t* color, const RenderUpdateFlag flags);
|
||||
|
@ -85,7 +93,9 @@ struct WgRenderSettings
|
|||
|
||||
struct WgRenderDataPaint
|
||||
{
|
||||
WgBindGroupPaint bindGroupPaint{};
|
||||
WGPUBuffer bufferModelMat{};
|
||||
WGPUBuffer bufferBlendSettings{};
|
||||
WGPUBindGroup bindGroupPaint{};
|
||||
RenderRegion viewport{};
|
||||
float opacity{};
|
||||
Array<WgRenderDataPaint*> clips;
|
||||
|
@ -94,6 +104,7 @@ struct WgRenderDataPaint
|
|||
virtual void release(WgContext& context);
|
||||
virtual Type type() { return Type::Undefined; };
|
||||
|
||||
void update(WgContext& context, const tvg::Matrix& transform, tvg::ColorSpace cs, uint8_t opacity);
|
||||
void updateClips(tvg::Array<tvg::RenderData> &clips);
|
||||
};
|
||||
|
||||
|
@ -133,11 +144,12 @@ public:
|
|||
|
||||
struct WgRenderDataPicture: public WgRenderDataPaint
|
||||
{
|
||||
WgBindGroupPicture bindGroupPicture{};
|
||||
WGPUBindGroup bindGroupPicture{};
|
||||
WgImageData imageData{};
|
||||
WgMeshData meshData{};
|
||||
|
||||
void update(WgContext& context);
|
||||
void release(WgContext& context) override;
|
||||
Type type() override { return Type::Picture; };
|
||||
};
|
||||
|
||||
#endif // _TVG_WG_RENDER_DATA_H_
|
||||
|
|
426
src/renderer/wg_engine/tvgWgRenderTarget.cpp
Normal file → Executable file
426
src/renderer/wg_engine/tvgWgRenderTarget.cpp
Normal file → Executable file
|
@ -22,407 +22,69 @@
|
|||
|
||||
#include "tvgWgRenderTarget.h"
|
||||
|
||||
//*****************************************************************************
|
||||
// render storage
|
||||
//*****************************************************************************
|
||||
|
||||
void WgRenderStorage::initialize(WgContext& context, uint32_t w, uint32_t h, uint32_t samples, WGPUTextureFormat format)
|
||||
{
|
||||
release(context);
|
||||
// store target storage size
|
||||
this->samples = samples;
|
||||
width = w * samples;
|
||||
height = h * samples;
|
||||
workgroupsCountX = (width + WG_COMPUTE_WORKGROUP_SIZE_X - 1) / WG_COMPUTE_WORKGROUP_SIZE_X; // workgroup size x == 8
|
||||
workgroupsCountY = (height + WG_COMPUTE_WORKGROUP_SIZE_Y - 1) / WG_COMPUTE_WORKGROUP_SIZE_Y; // workgroup size y == 8
|
||||
// create color and stencil textures
|
||||
texColor = context.createTexture2d(
|
||||
WGPUTextureUsage_CopySrc |
|
||||
WGPUTextureUsage_CopyDst |
|
||||
WGPUTextureUsage_TextureBinding |
|
||||
WGPUTextureUsage_StorageBinding |
|
||||
WGPUTextureUsage_RenderAttachment,
|
||||
format, width, height, "The target texture color");
|
||||
texStencil = context.createTexture2d(
|
||||
WGPUTextureUsage_RenderAttachment,
|
||||
WGPUTextureFormat_Stencil8,
|
||||
width, height, "The target texture stencil");
|
||||
assert(texColor);
|
||||
assert(texStencil);
|
||||
texViewColor = context.createTextureView2d(texColor, "The target texture view color");
|
||||
texViewStencil = context.createTextureView2d(texStencil, "The target texture view stencil");
|
||||
assert(texViewColor);
|
||||
assert(texViewStencil);
|
||||
// initialize bind group for blitting
|
||||
if (format == WGPUTextureFormat_RGBA8Unorm) {
|
||||
bindGroupTexStorageRgbaRO.initialize(context.device, context.queue, texViewColor);
|
||||
bindGroupTexStorageRgbaWO.initialize(context.device, context.queue, texViewColor);
|
||||
}
|
||||
if (format == WGPUTextureFormat_BGRA8Unorm)
|
||||
bindGroupTexStorageBgraWO.initialize(context.device, context.queue, texViewColor);
|
||||
// initialize window binding groups
|
||||
WgShaderTypeMat4x4f viewMat(w, h);
|
||||
mBindGroupCanvas.initialize(context.device, context.queue, viewMat);
|
||||
mPipelines = context.pipelines;
|
||||
}
|
||||
void WgRenderStorage::initialize(WgContext& context, uint32_t width, uint32_t height)
|
||||
{
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
texture = context.createTexStorage(width, height, WGPUTextureFormat_RGBA8Unorm);
|
||||
texView = context.createTextureView(texture);
|
||||
bindGroupRead = context.pipelines->layouts.createBindGroupStrorage1RO(texView);
|
||||
bindGroupWrite = context.pipelines->layouts.createBindGroupStrorage1WO(texView);
|
||||
bindGroupTexure = context.pipelines->layouts.createBindGroupTexSampled(context.samplerNearest, texView);
|
||||
}
|
||||
|
||||
|
||||
void WgRenderStorage::release(WgContext& context)
|
||||
{
|
||||
mRenderPassEncoder = nullptr;
|
||||
mBindGroupCanvas.release();
|
||||
bindGroupTexStorageBgraWO.release();
|
||||
bindGroupTexStorageRgbaWO.release();
|
||||
bindGroupTexStorageRgbaRO.release();
|
||||
context.releaseTextureView(texViewStencil);
|
||||
context.releaseTextureView(texViewColor);
|
||||
context.releaseTexture(texStencil);
|
||||
context.releaseTexture(texColor);
|
||||
workgroupsCountX = 0;
|
||||
workgroupsCountY = 0;
|
||||
context.pipelines->layouts.releaseBindGroup(bindGroupTexure);
|
||||
context.pipelines->layouts.releaseBindGroup(bindGroupWrite);
|
||||
context.pipelines->layouts.releaseBindGroup(bindGroupRead);
|
||||
context.releaseTextureView(texView);
|
||||
context.releaseTexture(texture);
|
||||
height = 0;
|
||||
width = 0;
|
||||
}
|
||||
|
||||
|
||||
void WgRenderStorage::renderShape(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType)
|
||||
{
|
||||
assert(renderData);
|
||||
assert(mRenderPassEncoder);
|
||||
if (renderData->strokeFirst) {
|
||||
drawStroke(context, renderData, blendType);
|
||||
drawShape(context, renderData, blendType);
|
||||
} else {
|
||||
drawShape(context, renderData, blendType);
|
||||
drawStroke(context, renderData, blendType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WgRenderStorage::renderPicture(WgContext& context, WgRenderDataPicture* renderData, WgPipelineBlendType blendType)
|
||||
{
|
||||
assert(renderData);
|
||||
assert(mRenderPassEncoder);
|
||||
uint8_t blend = (uint8_t)blendType;
|
||||
auto& vp = renderData->viewport;
|
||||
if ((vp.w <= 0) || (vp.h <= 0)) return;
|
||||
wgpuRenderPassEncoderSetScissorRect(mRenderPassEncoder, vp.x * samples, vp.y * samples, vp.w * samples, vp.h * samples);
|
||||
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
|
||||
mPipelines->image[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, renderData->bindGroupPicture);
|
||||
renderData->meshData.drawImage(context, mRenderPassEncoder);
|
||||
}
|
||||
|
||||
void WgRenderStorage::renderClipPath(WgContext& context, WgRenderDataPaint* renderData) {
|
||||
assert(renderData);
|
||||
assert(mRenderPassEncoder);
|
||||
if (renderData->type() == Type::Shape)
|
||||
drawShapeClipPath(context, (WgRenderDataShape*)renderData);
|
||||
else if (renderData->type() == Type::Picture)
|
||||
drawPictureClipPath(context, (WgRenderDataPicture*)renderData);
|
||||
}
|
||||
|
||||
void WgRenderStorage::drawShape(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType)
|
||||
{
|
||||
assert(renderData);
|
||||
assert(mRenderPassEncoder);
|
||||
assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count);
|
||||
if (renderData->renderSettingsShape.skip) return;
|
||||
// apply viewport
|
||||
auto& vp = renderData->viewport;
|
||||
if ((vp.w <= 0) || (vp.h <= 0)) return;
|
||||
wgpuRenderPassEncoderSetScissorRect(mRenderPassEncoder, vp.x * samples, vp.y * samples, vp.w * samples, vp.h * samples);
|
||||
// setup fill rule
|
||||
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
|
||||
if (renderData->fillRule == FillRule::Winding)
|
||||
mPipelines->fillShapeWinding.use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint);
|
||||
else
|
||||
mPipelines->fillShapeEvenOdd.use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint);
|
||||
// draw to stencil (first pass)
|
||||
for (uint32_t i = 0; i < renderData->meshGroupShapes.meshes.count; i++)
|
||||
renderData->meshGroupShapes.meshes[i]->drawFan(context, mRenderPassEncoder);
|
||||
// fill shape geometry (second pass)
|
||||
uint8_t blend = (uint8_t)blendType;
|
||||
WgRenderSettings& settings = renderData->renderSettingsShape;
|
||||
if (settings.fillType == WgRenderSettingsType::Solid)
|
||||
mPipelines->solid[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupSolid);
|
||||
else if (settings.fillType == WgRenderSettingsType::Linear)
|
||||
mPipelines->linear[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupLinear);
|
||||
else if (settings.fillType == WgRenderSettingsType::Radial)
|
||||
mPipelines->radial[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupRadial);
|
||||
renderData->meshDataBBox.drawFan(context, mRenderPassEncoder);
|
||||
}
|
||||
|
||||
|
||||
void WgRenderStorage::drawStroke(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType)
|
||||
{
|
||||
assert(renderData);
|
||||
assert(mRenderPassEncoder);
|
||||
assert(renderData->meshGroupStrokes.meshes.count == renderData->meshGroupStrokesBBox.meshes.count);
|
||||
if (renderData->renderSettingsStroke.skip) return;
|
||||
// apply viewport
|
||||
auto& vp = renderData->viewport;
|
||||
if ((vp.w <= 0) || (vp.h <= 0)) return;
|
||||
wgpuRenderPassEncoderSetScissorRect(mRenderPassEncoder, vp.x * samples, vp.y * samples, vp.w * samples, vp.h * samples);
|
||||
// draw stroke geometry
|
||||
uint8_t blend = (uint8_t)blendType;
|
||||
// draw strokes to stencil (first pass)
|
||||
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
|
||||
// draw to stencil (first pass)
|
||||
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 255);
|
||||
mPipelines->fillStroke.use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint);
|
||||
renderData->meshGroupStrokes.meshes[i]->draw(context, mRenderPassEncoder);
|
||||
// fill shape (second pass)
|
||||
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
|
||||
WgRenderSettings& settings = renderData->renderSettingsStroke;
|
||||
if (settings.fillType == WgRenderSettingsType::Solid)
|
||||
mPipelines->solid[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupSolid);
|
||||
else if (settings.fillType == WgRenderSettingsType::Linear)
|
||||
mPipelines->linear[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupLinear);
|
||||
else if (settings.fillType == WgRenderSettingsType::Radial)
|
||||
mPipelines->radial[blend].use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint, settings.bindGroupRadial);
|
||||
renderData->meshGroupStrokesBBox.meshes[i]->drawFan(context, mRenderPassEncoder);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WgRenderStorage::drawShapeClipPath(WgContext& context, WgRenderDataShape* renderData) {
|
||||
assert(renderData);
|
||||
assert(renderData->type() == Type::Shape);
|
||||
assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count);
|
||||
// draw shape geometry
|
||||
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
|
||||
// setup fill rule
|
||||
if (renderData->fillRule == FillRule::Winding)
|
||||
mPipelines->fillShapeWinding.use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint);
|
||||
else
|
||||
mPipelines->fillShapeEvenOdd.use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint);
|
||||
// draw to stencil (first pass)
|
||||
for (uint32_t i = 0; i < renderData->meshGroupShapes.meshes.count; i++)
|
||||
renderData->meshGroupShapes.meshes[i]->drawFan(context, mRenderPassEncoder);
|
||||
// fill shape geometry (second pass)
|
||||
mPipelines->clipMask.use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint);
|
||||
renderData->meshDataBBox.drawFan(context, mRenderPassEncoder);
|
||||
}
|
||||
|
||||
void WgRenderStorage::drawPictureClipPath(WgContext& context, WgRenderDataPicture* renderData) {
|
||||
assert(renderData);
|
||||
assert(renderData->type() == Type::Picture);
|
||||
assert(mRenderPassEncoder);
|
||||
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
|
||||
mPipelines->clipMask.use(mRenderPassEncoder, mBindGroupCanvas, renderData->bindGroupPaint);
|
||||
renderData->meshData.drawImage(context, mRenderPassEncoder);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void WgRenderStorage::blend(
|
||||
WgContext& context,
|
||||
WGPUCommandEncoder commandEncoder,
|
||||
WgPipelineBlend* pipeline,
|
||||
WgRenderStorage* texSrc,
|
||||
WgRenderStorage* texDst,
|
||||
WgBindGroupBlendMethod* blendMethod,
|
||||
WgBindGroupOpacity* opacity)
|
||||
{
|
||||
assert(commandEncoder);
|
||||
assert(texSrc);
|
||||
assert(texDst);
|
||||
assert(pipeline);
|
||||
WgBindGroupTexBlend texBlend;
|
||||
texBlend.initialize(context.device, context.queue, texSrc->texViewColor, texDst->texViewColor, texViewColor);
|
||||
WGPUComputePassEncoder computePassEncoder = beginComputePass(commandEncoder);
|
||||
pipeline->use(computePassEncoder, texBlend, *blendMethod, *opacity);
|
||||
dispatchWorkgroups(computePassEncoder);
|
||||
endComputePass(computePassEncoder);
|
||||
}
|
||||
|
||||
|
||||
void WgRenderStorage::blendMask(
|
||||
WgContext& context,
|
||||
WGPUCommandEncoder commandEncoder,
|
||||
WgPipelineBlendMask* pipeline,
|
||||
WgRenderStorage* texMsk,
|
||||
WgRenderStorage* texSrc,
|
||||
WgRenderStorage* texDst,
|
||||
WgBindGroupBlendMethod* blendMethod,
|
||||
WgBindGroupOpacity* opacity)
|
||||
{
|
||||
assert(commandEncoder);
|
||||
assert(texSrc);
|
||||
assert(texMsk);
|
||||
WgBindGroupTexBlendMask texBlendMask;
|
||||
texBlendMask.initialize(context.device, context.queue, texSrc->texViewColor, texMsk->texViewColor, texDst->texViewColor, texViewColor);
|
||||
WGPUComputePassEncoder computePassEncoder = beginComputePass(commandEncoder);
|
||||
pipeline->use(computePassEncoder, texBlendMask, *blendMethod, *opacity);
|
||||
dispatchWorkgroups(computePassEncoder);
|
||||
endComputePass(computePassEncoder);
|
||||
texBlendMask.release();
|
||||
};
|
||||
|
||||
|
||||
void WgRenderStorage::maskCompose(
|
||||
WgContext& context,
|
||||
WGPUCommandEncoder commandEncoder,
|
||||
WgRenderStorage* texMsk0,
|
||||
WgRenderStorage* texMsk1)
|
||||
{
|
||||
assert(commandEncoder);
|
||||
assert(texMsk0);
|
||||
WgBindGroupTexMaskCompose maskCompose;
|
||||
maskCompose.initialize(context.device, context.queue, texMsk0->texViewColor, texMsk1->texViewColor, texViewColor);
|
||||
WGPUComputePassEncoder computePassEncoder = beginComputePass(commandEncoder);
|
||||
mPipelines->computeMaskCompose.use(computePassEncoder, maskCompose);
|
||||
dispatchWorkgroups(computePassEncoder);
|
||||
endComputePass(computePassEncoder);
|
||||
maskCompose.release();
|
||||
}
|
||||
|
||||
|
||||
void WgRenderStorage::compose(
|
||||
WgContext& context,
|
||||
WGPUCommandEncoder commandEncoder,
|
||||
WgRenderStorage* texSrc,
|
||||
WgRenderStorage* texMsk,
|
||||
WgRenderStorage* texDst,
|
||||
WgBindGroupCompositeMethod* composeMethod,
|
||||
WgBindGroupBlendMethod* blendMethod,
|
||||
WgBindGroupOpacity* opacity)
|
||||
{
|
||||
assert(commandEncoder);
|
||||
assert(texSrc);
|
||||
assert(texMsk);
|
||||
WgBindGroupTexCompose texCompose;
|
||||
texCompose.initialize(context.device, context.queue, texSrc->texViewColor, texMsk->texViewColor, texDst->texViewColor, texViewColor);
|
||||
WGPUComputePassEncoder computePassEncoder = beginComputePass(commandEncoder);
|
||||
mPipelines->computeCompose.use(computePassEncoder, texCompose, *composeMethod, *blendMethod, *opacity);
|
||||
dispatchWorkgroups(computePassEncoder);
|
||||
endComputePass(computePassEncoder);
|
||||
texCompose.release();
|
||||
}
|
||||
|
||||
|
||||
void WgRenderStorage::antialias(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetSrc)
|
||||
{
|
||||
assert(commandEncoder);
|
||||
assert(targetSrc);
|
||||
WGPUComputePassEncoder computePassEncoder = beginComputePass(commandEncoder);
|
||||
mPipelines->computeAntiAliasing.use(computePassEncoder, targetSrc->bindGroupTexStorageRgbaRO, bindGroupTexStorageBgraWO);
|
||||
dispatchWorkgroups(computePassEncoder);
|
||||
endComputePass(computePassEncoder);
|
||||
}
|
||||
|
||||
void WgRenderStorage::copy(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetSrc)
|
||||
{
|
||||
assert(commandEncoder);
|
||||
assert(targetSrc);
|
||||
WGPUComputePassEncoder computePassEncoder = beginComputePass(commandEncoder);
|
||||
mPipelines->computeCopy.use(computePassEncoder, targetSrc->bindGroupTexStorageRgbaRO, bindGroupTexStorageRgbaWO);
|
||||
dispatchWorkgroups(computePassEncoder);
|
||||
endComputePass(computePassEncoder);
|
||||
}
|
||||
|
||||
void WgRenderStorage::dispatchWorkgroups(WGPUComputePassEncoder computePassEncoder)
|
||||
{
|
||||
assert(computePassEncoder);
|
||||
wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, workgroupsCountX, workgroupsCountY, 1);
|
||||
}
|
||||
|
||||
|
||||
void WgRenderStorage::beginRenderPass(WGPUCommandEncoder commandEncoder, bool clear)
|
||||
{
|
||||
assert(commandEncoder);
|
||||
// render pass depth stencil attachment
|
||||
WGPURenderPassDepthStencilAttachment depthStencilAttachment{};
|
||||
depthStencilAttachment.view = texViewStencil;
|
||||
depthStencilAttachment.depthLoadOp = WGPULoadOp_Undefined;
|
||||
depthStencilAttachment.depthStoreOp = WGPUStoreOp_Undefined;
|
||||
depthStencilAttachment.depthClearValue = 1.0f;
|
||||
depthStencilAttachment.depthReadOnly = false;
|
||||
depthStencilAttachment.stencilLoadOp = WGPULoadOp_Clear;
|
||||
depthStencilAttachment.stencilStoreOp = WGPUStoreOp_Discard;
|
||||
depthStencilAttachment.stencilClearValue = 0;
|
||||
depthStencilAttachment.stencilReadOnly = false;
|
||||
// render pass color attachment
|
||||
WGPURenderPassColorAttachment colorAttachment{};
|
||||
colorAttachment.view = texViewColor;
|
||||
#ifdef __EMSCRIPTEN__
|
||||
colorAttachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
|
||||
#endif
|
||||
colorAttachment.resolveTarget = nullptr;
|
||||
colorAttachment.loadOp = clear ? WGPULoadOp_Clear : WGPULoadOp_Load;
|
||||
colorAttachment.clearValue = { 0, 0, 0, 0 };
|
||||
colorAttachment.storeOp = WGPUStoreOp_Store;
|
||||
// render pass descriptor
|
||||
WGPURenderPassDescriptor renderPassDesc{};
|
||||
renderPassDesc.nextInChain = nullptr;
|
||||
renderPassDesc.label = "The render pass";
|
||||
renderPassDesc.colorAttachmentCount = 1;
|
||||
renderPassDesc.colorAttachments = &colorAttachment;
|
||||
renderPassDesc.depthStencilAttachment = &depthStencilAttachment;
|
||||
//renderPassDesc.depthStencilAttachment = nullptr;
|
||||
renderPassDesc.occlusionQuerySet = nullptr;
|
||||
renderPassDesc.timestampWrites = nullptr;
|
||||
// begin render pass
|
||||
mRenderPassEncoder = wgpuCommandEncoderBeginRenderPass(commandEncoder, &renderPassDesc);
|
||||
}
|
||||
|
||||
|
||||
void WgRenderStorage::endRenderPass()
|
||||
{
|
||||
assert(mRenderPassEncoder);
|
||||
wgpuRenderPassEncoderEnd(mRenderPassEncoder);
|
||||
wgpuRenderPassEncoderRelease(mRenderPassEncoder);
|
||||
mRenderPassEncoder = nullptr;
|
||||
}
|
||||
|
||||
|
||||
WGPUComputePassEncoder WgRenderStorage::beginComputePass(WGPUCommandEncoder commandEncoder)
|
||||
{
|
||||
assert(commandEncoder);
|
||||
WGPUComputePassDescriptor computePassDesc{};
|
||||
computePassDesc.nextInChain = nullptr;
|
||||
computePassDesc.label = "The compute pass composition";
|
||||
computePassDesc.timestampWrites = nullptr;
|
||||
return wgpuCommandEncoderBeginComputePass(commandEncoder, &computePassDesc);
|
||||
};
|
||||
|
||||
|
||||
void WgRenderStorage::endComputePass(WGPUComputePassEncoder computePassEncoder)
|
||||
{
|
||||
assert(computePassEncoder);
|
||||
wgpuComputePassEncoderEnd(computePassEncoder);
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
// render storage pool
|
||||
//*****************************************************************************
|
||||
|
||||
WgRenderStorage* WgRenderStoragePool::allocate(WgContext& context, uint32_t w, uint32_t h, uint32_t samples)
|
||||
WgRenderStorage* WgRenderStoragePool::allocate(WgContext& context)
|
||||
{
|
||||
WgRenderStorage* renderStorage{};
|
||||
if (mPool.count > 0) {
|
||||
renderStorage = mPool.last();
|
||||
mPool.pop();
|
||||
} else {
|
||||
renderStorage = new WgRenderStorage;
|
||||
renderStorage->initialize(context, w, h, samples);
|
||||
mList.push(renderStorage);
|
||||
}
|
||||
return renderStorage;
|
||||
WgRenderStorage* renderStorage{};
|
||||
if (pool.count > 0) {
|
||||
renderStorage = pool.last();
|
||||
pool.pop();
|
||||
} else {
|
||||
renderStorage = new WgRenderStorage;
|
||||
renderStorage->initialize(context, width, height);
|
||||
list.push(renderStorage);
|
||||
}
|
||||
return renderStorage;
|
||||
};
|
||||
|
||||
|
||||
void WgRenderStoragePool::free(WgContext& context, WgRenderStorage* renderStorage) {
|
||||
mPool.push(renderStorage);
|
||||
void WgRenderStoragePool::free(WgContext& context, WgRenderStorage* renderStorage)
|
||||
{
|
||||
pool.push(renderStorage);
|
||||
};
|
||||
|
||||
|
||||
void WgRenderStoragePool::initialize(WgContext& context, uint32_t width, uint32_t height)
|
||||
{
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
}
|
||||
|
||||
|
||||
void WgRenderStoragePool::release(WgContext& context)
|
||||
{
|
||||
for (uint32_t i = 0; i < mList.count; i++) {
|
||||
mList[i]->release(context);
|
||||
delete mList[i];
|
||||
}
|
||||
mList.clear();
|
||||
mPool.clear();
|
||||
for (uint32_t i = 0; i < list.count; i++) {
|
||||
list[i]->release(context);
|
||||
delete list[i];
|
||||
}
|
||||
list.clear();
|
||||
pool.clear();
|
||||
height = 0;
|
||||
width = 0;
|
||||
};
|
||||
|
|
100
src/renderer/wg_engine/tvgWgRenderTarget.h
Normal file → Executable file
100
src/renderer/wg_engine/tvgWgRenderTarget.h
Normal file → Executable file
|
@ -23,94 +23,34 @@
|
|||
#ifndef _TVG_WG_RENDER_TARGET_H_
|
||||
#define _TVG_WG_RENDER_TARGET_H_
|
||||
|
||||
#include "tvgWgRenderData.h"
|
||||
#include "tvgWgPipelines.h"
|
||||
#include "tvgRender.h"
|
||||
|
||||
class WgRenderStorage {
|
||||
private:
|
||||
// texture buffers
|
||||
WgBindGroupCanvas mBindGroupCanvas;
|
||||
WGPURenderPassEncoder mRenderPassEncoder{};
|
||||
WgPipelines* mPipelines{}; // external handle
|
||||
public:
|
||||
WGPUTexture texColor{};
|
||||
WGPUTexture texStencil{};
|
||||
WGPUTextureView texViewColor{};
|
||||
WGPUTextureView texViewStencil{};
|
||||
WgBindGroupTextureStorageRgbaRO bindGroupTexStorageRgbaRO;
|
||||
WgBindGroupTextureStorageRgbaWO bindGroupTexStorageRgbaWO;
|
||||
WgBindGroupTextureStorageBgraWO bindGroupTexStorageBgraWO;
|
||||
uint32_t samples{};
|
||||
struct WgRenderStorage {
|
||||
WGPUTexture texture{};
|
||||
WGPUTextureView texView{};
|
||||
WGPUBindGroup bindGroupRead{};
|
||||
WGPUBindGroup bindGroupWrite{};
|
||||
WGPUBindGroup bindGroupTexure{};
|
||||
uint32_t width{};
|
||||
uint32_t height{};
|
||||
uint32_t workgroupsCountX{};
|
||||
uint32_t workgroupsCountY{};
|
||||
public:
|
||||
void initialize(WgContext& context, uint32_t w, uint32_t h, uint32_t samples = 1, WGPUTextureFormat format = WGPUTextureFormat_RGBA8Unorm);
|
||||
|
||||
void initialize(WgContext& context, uint32_t width, uint32_t height);
|
||||
void release(WgContext& context);
|
||||
|
||||
void beginRenderPass(WGPUCommandEncoder commandEncoder, bool clear);
|
||||
void endRenderPass();
|
||||
|
||||
void renderShape(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType);
|
||||
void renderPicture(WgContext& context, WgRenderDataPicture* renderData, WgPipelineBlendType blendType);
|
||||
void renderClipPath(WgContext& context, WgRenderDataPaint* renderData);
|
||||
|
||||
void blend(
|
||||
WgContext& context,
|
||||
WGPUCommandEncoder commandEncoder,
|
||||
WgPipelineBlend* pipeline,
|
||||
WgRenderStorage* targetSrc,
|
||||
WgRenderStorage* targetDst,
|
||||
WgBindGroupBlendMethod* blendMethod,
|
||||
WgBindGroupOpacity* opacity);
|
||||
void blendMask(
|
||||
WgContext& context,
|
||||
WGPUCommandEncoder commandEncoder,
|
||||
WgPipelineBlendMask* pipeline,
|
||||
WgRenderStorage* texMsk,
|
||||
WgRenderStorage* texSrc,
|
||||
WgRenderStorage* texDst,
|
||||
WgBindGroupBlendMethod* blendMethod,
|
||||
WgBindGroupOpacity* opacity);
|
||||
void maskCompose(
|
||||
WgContext& context,
|
||||
WGPUCommandEncoder commandEncoder,
|
||||
WgRenderStorage* texMsk0,
|
||||
WgRenderStorage* texMsk1);
|
||||
void compose(
|
||||
WgContext& context,
|
||||
WGPUCommandEncoder commandEncoder,
|
||||
WgRenderStorage* texMsk,
|
||||
WgRenderStorage* texSrc,
|
||||
WgRenderStorage* texDst,
|
||||
WgBindGroupCompositeMethod* composeMethod,
|
||||
WgBindGroupBlendMethod* blendMethod,
|
||||
WgBindGroupOpacity* opacity);
|
||||
void antialias(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetSrc);
|
||||
void copy(WGPUCommandEncoder commandEncoder, WgRenderStorage* targetSrc);
|
||||
private:
|
||||
void drawShape(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType);
|
||||
void drawStroke(WgContext& context, WgRenderDataShape* renderData, WgPipelineBlendType blendType);
|
||||
|
||||
void drawShapeClipPath(WgContext& context, WgRenderDataShape* renderData);
|
||||
void drawPictureClipPath(WgContext& context, WgRenderDataPicture* renderData);
|
||||
|
||||
void dispatchWorkgroups(WGPUComputePassEncoder computePassEncoder);
|
||||
|
||||
WGPUComputePassEncoder beginComputePass(WGPUCommandEncoder commandEncoder);
|
||||
void endComputePass(WGPUComputePassEncoder computePassEncoder);
|
||||
};
|
||||
|
||||
|
||||
class WgRenderStoragePool {
|
||||
private:
|
||||
Array<WgRenderStorage*> mList;
|
||||
Array<WgRenderStorage*> mPool;
|
||||
Array<WgRenderStorage*> list;
|
||||
Array<WgRenderStorage*> pool;
|
||||
uint32_t width{};
|
||||
uint32_t height{};
|
||||
public:
|
||||
WgRenderStorage* allocate(WgContext& context, uint32_t w, uint32_t h, uint32_t samples = 1);
|
||||
void free(WgContext& context, WgRenderStorage* renderTarget);
|
||||
void release(WgContext& context);
|
||||
WgRenderStorage* allocate(WgContext& context);
|
||||
void free(WgContext& context, WgRenderStorage* renderTarget);
|
||||
|
||||
void initialize(WgContext& context, uint32_t width, uint32_t height);
|
||||
void release(WgContext& context);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
#endif // _TVG_WG_RENDER_TARGET_H_
|
||||
|
|
390
src/renderer/wg_engine/tvgWgRenderer.cpp
Normal file → Executable file
390
src/renderer/wg_engine/tvgWgRenderer.cpp
Normal file → Executable file
|
@ -21,15 +21,12 @@
|
|||
*/
|
||||
|
||||
#include "tvgWgRenderer.h"
|
||||
|
||||
#define WG_SSAA_SAMPLES (2)
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
#include <iostream>
|
||||
|
||||
WgRenderer::WgRenderer()
|
||||
{
|
||||
WgGeometryData::gMath = new WgMath();
|
||||
WgGeometryData::gMath->initialize();
|
||||
}
|
||||
|
||||
|
||||
|
@ -37,49 +34,40 @@ WgRenderer::~WgRenderer()
|
|||
{
|
||||
release();
|
||||
mContext.release();
|
||||
WgGeometryData::gMath->release();
|
||||
delete WgGeometryData::gMath;
|
||||
}
|
||||
|
||||
|
||||
void WgRenderer::initialize()
|
||||
{
|
||||
mPipelines.initialize(mContext);
|
||||
mOpacityPool.initialize(mContext);
|
||||
mBlendMethodPool.initialize(mContext);
|
||||
mCompositeMethodPool.initialize(mContext);
|
||||
WgMeshDataGroup::gMeshDataPool = new WgMeshDataPool();
|
||||
WgGeometryData::gMath = new WgMath();
|
||||
WgGeometryData::gMath->initialize();
|
||||
}
|
||||
|
||||
|
||||
void WgRenderer::release()
|
||||
{
|
||||
clearDisposes();
|
||||
WgGeometryData::gMath->release();
|
||||
delete WgGeometryData::gMath;
|
||||
disposeObjects();
|
||||
mContext.releaseTexture(mTexScreen);
|
||||
mContext.releaseTextureView(mTexViewScreen);
|
||||
mContext.pipelines->layouts.releaseBindGroup(mBindGroupScreen);
|
||||
mStorageRoot.release(mContext);
|
||||
mRenderStoragePool.release(mContext);
|
||||
mRenderDataShapePool.release(mContext);
|
||||
WgMeshDataGroup::gMeshDataPool->release(mContext);
|
||||
delete WgMeshDataGroup::gMeshDataPool;
|
||||
mCompositorStack.clear();
|
||||
mRenderStorageStack.clear();
|
||||
mRenderStoragePool.release(mContext);
|
||||
mCompositeMethodPool.release(mContext);
|
||||
mBlendMethodPool.release(mContext);
|
||||
mOpacityPool.release(mContext);
|
||||
mRenderStorageRoot.release(mContext);
|
||||
mRenderStorageMask.release(mContext);
|
||||
mRenderStorageCopy.release(mContext);
|
||||
mRenderStorageScreen.release(mContext);
|
||||
mRenderStorageInterm.release(mContext);
|
||||
mPipelines.release();
|
||||
mPipelines.release(mContext);
|
||||
}
|
||||
|
||||
|
||||
void WgRenderer::clearDisposes()
|
||||
void WgRenderer::disposeObjects()
|
||||
{
|
||||
if (mDisposed.renderDatas.count == 0) return;
|
||||
if (mDisposeRenderDatas.count == 0) return;
|
||||
|
||||
for (auto p = mDisposed.renderDatas.begin(); p < mDisposed.renderDatas.end(); p++) {
|
||||
for (auto p = mDisposeRenderDatas.begin(); p < mDisposeRenderDatas.end(); p++) {
|
||||
auto renderData = (WgRenderDataPaint*)(*p);
|
||||
if (renderData->type() == Type::Shape) {
|
||||
mRenderDataShapePool.free(mContext, (WgRenderDataShape*)renderData);
|
||||
|
@ -87,12 +75,9 @@ void WgRenderer::clearDisposes()
|
|||
renderData->release(mContext);
|
||||
}
|
||||
}
|
||||
mDisposed.renderDatas.clear();
|
||||
mDisposeRenderDatas.clear();
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
RenderData WgRenderer::prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper)
|
||||
{
|
||||
|
@ -106,11 +91,9 @@ RenderData WgRenderer::prepare(const RenderShape& rshape, RenderData data, const
|
|||
renderDataShape->updateMeshes(mContext, rshape, transform);
|
||||
}
|
||||
|
||||
// update paint settings
|
||||
// update paint settings
|
||||
if ((!data) || (flags & (RenderUpdateFlag::Transform | RenderUpdateFlag::Blend))) {
|
||||
WgShaderTypeMat4x4f modelMat(transform);
|
||||
WgShaderTypeBlendSettings blendSettings(mTargetSurface.cs, opacity);
|
||||
renderDataShape->bindGroupPaint.initialize(mContext.device, mContext.queue, modelMat, blendSettings);
|
||||
renderDataShape->update(mContext, transform, mTargetSurface.cs, opacity);
|
||||
renderDataShape->fillRule = rshape.rule;
|
||||
}
|
||||
|
||||
|
@ -145,9 +128,7 @@ RenderData WgRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderD
|
|||
renderDataPicture->viewport = mViewport;
|
||||
renderDataPicture->opacity = opacity;
|
||||
if (flags & (RenderUpdateFlag::Transform | RenderUpdateFlag::Blend)) {
|
||||
WgShaderTypeMat4x4f modelMat(transform);
|
||||
WgShaderTypeBlendSettings blendSettings(surface->cs, opacity);
|
||||
renderDataPicture->bindGroupPaint.initialize(mContext.device, mContext.queue, modelMat, blendSettings);
|
||||
renderDataPicture->update(mContext, transform, surface->cs, opacity);
|
||||
}
|
||||
|
||||
// update image data
|
||||
|
@ -155,13 +136,13 @@ RenderData WgRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderD
|
|||
WgGeometryData geometryData;
|
||||
if (mesh->triangleCnt == 0) geometryData.appendImageBox(surface->w, surface->h);
|
||||
else geometryData.appendMesh(mesh);
|
||||
mContext.pipelines->layouts.releaseBindGroup(renderDataPicture->bindGroupPicture);
|
||||
renderDataPicture->meshData.release(mContext);
|
||||
renderDataPicture->meshData.update(mContext, &geometryData);
|
||||
renderDataPicture->imageData.update(mContext, surface);
|
||||
renderDataPicture->bindGroupPicture.initialize(
|
||||
mContext.device, mContext.queue,
|
||||
mContext.samplerLinear,
|
||||
renderDataPicture->imageData.textureView);
|
||||
renderDataPicture->bindGroupPicture = mContext.pipelines->layouts.createBindGroupTexSampled(
|
||||
mContext.samplerLinear, renderDataPicture->imageData.textureView
|
||||
);
|
||||
}
|
||||
|
||||
// store clips data
|
||||
|
@ -172,151 +153,47 @@ RenderData WgRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderD
|
|||
|
||||
bool WgRenderer::preRender()
|
||||
{
|
||||
// push rot render storage to the render tree stack
|
||||
assert(mRenderStorageStack.count == 0);
|
||||
mRenderStorageStack.push(&mStorageRoot);
|
||||
// create command encoder for drawing
|
||||
WGPUCommandEncoderDescriptor commandEncoderDesc{};
|
||||
commandEncoderDesc.nextInChain = nullptr;
|
||||
commandEncoderDesc.label = "The command encoder";
|
||||
mCommandEncoder = wgpuDeviceCreateCommandEncoder(mContext.device, &commandEncoderDesc);
|
||||
mRenderStorageStack.push(&mRenderStorageRoot);
|
||||
//mRenderStorageRoot.clear(mCommandEncoder);
|
||||
mRenderStorageRoot.beginRenderPass(mCommandEncoder, true);
|
||||
// start root render pass
|
||||
mCompositor.beginRenderPass(mCommandEncoder, mRenderStorageStack.last(), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void WgRenderer::renderClipPath(Array<WgRenderDataPaint*>& clips)
|
||||
{
|
||||
for (uint32_t i = 0; i < clips.count; i++) {
|
||||
renderClipPath(clips[i]->clips);
|
||||
// render image to render target
|
||||
mRenderStorageInterm.beginRenderPass(mCommandEncoder, true);
|
||||
mRenderStorageInterm.renderClipPath(mContext, clips[i]);
|
||||
mRenderStorageInterm.endRenderPass();
|
||||
mRenderStorageCopy.copy(mCommandEncoder, &mRenderStorageMask);
|
||||
mRenderStorageMask.maskCompose(mContext, mCommandEncoder, &mRenderStorageInterm, &mRenderStorageCopy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool WgRenderer::renderShape(RenderData data)
|
||||
{
|
||||
// get current render storage
|
||||
WgRenderDataShape *dataShape = (WgRenderDataShape*)data;
|
||||
if (dataShape->opacity == 0) return 0;
|
||||
WgPipelineBlendType blendType = WgPipelines::blendMethodToBlendType(mBlendMethod);
|
||||
WgRenderStorage* renderStorage = mRenderStorageStack.last();
|
||||
assert(renderStorage);
|
||||
// use masked blend
|
||||
if (dataShape->clips.count > 0) {
|
||||
// terminate current render pass
|
||||
renderStorage->endRenderPass();
|
||||
// render clip path
|
||||
mRenderStorageMask.beginRenderPass(mCommandEncoder, true);
|
||||
mRenderStorageMask.renderClipPath(mContext, dataShape->clips[0]);
|
||||
mRenderStorageMask.endRenderPass();
|
||||
renderClipPath(dataShape->clips);
|
||||
// render image to render target
|
||||
mRenderStorageInterm.beginRenderPass(mCommandEncoder, true);
|
||||
mRenderStorageInterm.renderShape(mContext, dataShape, blendType);
|
||||
mRenderStorageInterm.endRenderPass();
|
||||
// blend shape with current render storage
|
||||
WgBindGroupBlendMethod* blendMethod = mBlendMethodPool.allocate(mContext, mBlendMethod);
|
||||
WgBindGroupOpacity* opacity = mOpacityPool.allocate(mContext, 255);
|
||||
WgPipelineBlendMask* pipeline = &mContext.pipelines->computeBlendSolidMask;
|
||||
if (dataShape->renderSettingsShape.fillType != WgRenderSettingsType::Solid)
|
||||
pipeline = &mContext.pipelines->computeBlendGradientMask;
|
||||
mRenderStorageCopy.copy(mCommandEncoder, renderStorage);
|
||||
renderStorage->blendMask(mContext, mCommandEncoder,
|
||||
pipeline, &mRenderStorageMask, &mRenderStorageInterm, &mRenderStorageCopy, blendMethod, opacity);
|
||||
// restore current render pass
|
||||
renderStorage->beginRenderPass(mCommandEncoder, false);
|
||||
// use hardware blend
|
||||
} else if (WgPipelines::isBlendMethodSupportsHW(mBlendMethod)) {
|
||||
renderStorage->renderShape(mContext, dataShape, blendType);
|
||||
// use custom blend
|
||||
} else {
|
||||
// terminate current render pass
|
||||
renderStorage->endRenderPass();
|
||||
// render image to render target
|
||||
mRenderStorageInterm.beginRenderPass(mCommandEncoder, true);
|
||||
mRenderStorageInterm.renderShape(mContext, dataShape, blendType);
|
||||
mRenderStorageInterm.endRenderPass();
|
||||
// blend shape with current render storage
|
||||
WgBindGroupBlendMethod* blendMethod = mBlendMethodPool.allocate(mContext, mBlendMethod);
|
||||
WgBindGroupOpacity* opacity = mOpacityPool.allocate(mContext, 255);
|
||||
WgPipelineBlend* pipeline = &mContext.pipelines->computeBlendSolid;
|
||||
if (dataShape->renderSettingsShape.fillType != WgRenderSettingsType::Solid)
|
||||
pipeline = &mContext.pipelines->computeBlendGradient;
|
||||
mRenderStorageCopy.copy(mCommandEncoder, renderStorage);
|
||||
renderStorage->blend(mContext, mCommandEncoder,
|
||||
pipeline, &mRenderStorageInterm, &mRenderStorageCopy, blendMethod, opacity);
|
||||
// restore current render pass
|
||||
renderStorage->beginRenderPass(mCommandEncoder, false);
|
||||
}
|
||||
// temporary simple render data to the current render target
|
||||
mCompositor.renderShape(mContext, (WgRenderDataShape*)data, mBlendMethod);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool WgRenderer::renderImage(RenderData data)
|
||||
{
|
||||
// get current render storage
|
||||
WgRenderDataPicture *dataPicture = (WgRenderDataPicture*)data;
|
||||
// get current render storage
|
||||
WgPipelineBlendType blendType = WgPipelines::blendMethodToBlendType(mBlendMethod);
|
||||
WgRenderStorage* renderStorage = mRenderStorageStack.last();
|
||||
assert(renderStorage);
|
||||
// use masked blend
|
||||
if (dataPicture->clips.count > 0) {
|
||||
// terminate current render pass
|
||||
renderStorage->endRenderPass();
|
||||
// render clip path
|
||||
mRenderStorageMask.beginRenderPass(mCommandEncoder, true);
|
||||
mRenderStorageMask.renderClipPath(mContext, dataPicture->clips[0]);
|
||||
mRenderStorageMask.endRenderPass();
|
||||
renderClipPath(dataPicture->clips);
|
||||
// render image to render target
|
||||
mRenderStorageInterm.beginRenderPass(mCommandEncoder, true);
|
||||
mRenderStorageInterm.renderPicture(mContext, dataPicture, blendType);
|
||||
mRenderStorageInterm.endRenderPass();
|
||||
// blend shape with current render storage
|
||||
WgBindGroupBlendMethod* blendMethod = mBlendMethodPool.allocate(mContext, mBlendMethod);
|
||||
WgBindGroupOpacity* opacity = mOpacityPool.allocate(mContext, 255);
|
||||
WgPipelineBlendMask* pipeline = &mContext.pipelines->computeBlendImageMask;
|
||||
mRenderStorageCopy.copy(mCommandEncoder, renderStorage);
|
||||
renderStorage->blendMask(mContext, mCommandEncoder,
|
||||
pipeline, &mRenderStorageMask, &mRenderStorageInterm, &mRenderStorageCopy, blendMethod, opacity);
|
||||
// restore current render pass
|
||||
renderStorage->beginRenderPass(mCommandEncoder, false);
|
||||
// use hardware blend
|
||||
} else if (WgPipelines::isBlendMethodSupportsHW(mBlendMethod))
|
||||
renderStorage->renderPicture(mContext, dataPicture, blendType);
|
||||
// use custom blend
|
||||
else {
|
||||
// terminate current render pass
|
||||
renderStorage->endRenderPass();
|
||||
// render image to render target
|
||||
mRenderStorageInterm.beginRenderPass(mCommandEncoder, true);
|
||||
mRenderStorageInterm.renderPicture(mContext, dataPicture, blendType);
|
||||
mRenderStorageInterm.endRenderPass();
|
||||
// blend shape with current render storage
|
||||
WgBindGroupBlendMethod* blendMethod = mBlendMethodPool.allocate(mContext, mBlendMethod);
|
||||
WgBindGroupOpacity* opacity = mOpacityPool.allocate(mContext, 255);
|
||||
WgPipelineBlend* pipeline = &mContext.pipelines->computeBlendImage;
|
||||
mRenderStorageCopy.copy(mCommandEncoder, renderStorage);
|
||||
renderStorage->blend(mContext, mCommandEncoder,
|
||||
pipeline, &mRenderStorageInterm, &mRenderStorageCopy, blendMethod, opacity);
|
||||
// restore current render pass
|
||||
renderStorage->beginRenderPass(mCommandEncoder, false);
|
||||
}
|
||||
// temporary simple render data to the current render target
|
||||
mCompositor.renderImage(mContext, (WgRenderDataPicture*)data, mBlendMethod);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool WgRenderer::postRender()
|
||||
{
|
||||
mRenderStorageRoot.endRenderPass();
|
||||
mRenderStorageStack.pop();
|
||||
mContext.executeCommandEncoder(mCommandEncoder);
|
||||
// end root render pass
|
||||
mCompositor.endRenderPass();
|
||||
// release command encoder
|
||||
const WGPUCommandBufferDescriptor commandBufferDesc{};
|
||||
WGPUCommandBuffer commandsBuffer = wgpuCommandEncoderFinish(mCommandEncoder, &commandBufferDesc);
|
||||
wgpuQueueSubmit(mContext.queue, 1, &commandsBuffer);
|
||||
wgpuCommandBufferRelease(commandsBuffer);
|
||||
wgpuCommandEncoderRelease(mCommandEncoder);
|
||||
// pop root render storage to the render tree stack
|
||||
mRenderStorageStack.pop();
|
||||
assert(mRenderStorageStack.count == 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -324,8 +201,8 @@ bool WgRenderer::postRender()
|
|||
void WgRenderer::dispose(RenderData data) {
|
||||
auto renderData = (WgRenderDataPaint*)data;
|
||||
if (renderData) {
|
||||
ScopedLock lock(mDisposed.key);
|
||||
mDisposed.renderDatas.push(data);
|
||||
ScopedLock lock(mDisposeKey);
|
||||
mDisposeRenderDatas.push(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -375,28 +252,35 @@ bool WgRenderer::clear()
|
|||
|
||||
bool WgRenderer::sync()
|
||||
{
|
||||
clearDisposes();
|
||||
WGPUSurfaceTexture backBuffer{};
|
||||
wgpuSurfaceGetCurrentTexture(mContext.surface, &backBuffer);
|
||||
|
||||
WGPUCommandEncoderDescriptor commandEncoderDesc{};
|
||||
commandEncoderDesc.nextInChain = nullptr;
|
||||
commandEncoderDesc.label = "The command encoder";
|
||||
disposeObjects();
|
||||
// get current texture
|
||||
WGPUSurfaceTexture surfaceTexture{};
|
||||
wgpuSurfaceGetCurrentTexture(mContext.surface, &surfaceTexture);
|
||||
|
||||
// create command encoder
|
||||
const WGPUCommandEncoderDescriptor commandEncoderDesc{};
|
||||
WGPUCommandEncoder commandEncoder = wgpuDeviceCreateCommandEncoder(mContext.device, &commandEncoderDesc);
|
||||
|
||||
mRenderStorageScreen.antialias(commandEncoder, &mRenderStorageRoot);
|
||||
// copy render to screen with conversion rgba to bgra (screen format)
|
||||
const WGPUComputePassDescriptor computePassDescriptor{};
|
||||
WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(commandEncoder, &computePassDescriptor);
|
||||
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 0, mStorageRoot.bindGroupRead, 0, nullptr);
|
||||
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 1, mBindGroupScreen, 0, nullptr);
|
||||
wgpuComputePassEncoderSetPipeline(computePassEncoder, mContext.pipelines->copy);
|
||||
wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, (mStorageRoot.width + 7) / 8, (mStorageRoot.height + 7) / 8, 1);
|
||||
wgpuComputePassEncoderEnd(computePassEncoder);
|
||||
|
||||
WGPUImageCopyTexture source{};
|
||||
source.texture = mRenderStorageScreen.texColor;
|
||||
WGPUImageCopyTexture dest{};
|
||||
dest.texture = backBuffer.texture;
|
||||
WGPUExtent3D copySize{};
|
||||
copySize.width = mTargetSurface.w;
|
||||
copySize.height = mTargetSurface.h;
|
||||
copySize.depthOrArrayLayers = 1;
|
||||
wgpuCommandEncoderCopyTextureToTexture(commandEncoder, &source, &dest, ©Size);
|
||||
// copy dst storage to temporary read only storage
|
||||
const WGPUImageCopyTexture texSrc { .texture = mTexScreen };
|
||||
const WGPUImageCopyTexture texDst { .texture = surfaceTexture.texture };
|
||||
const WGPUExtent3D copySize { .width = mTargetSurface.w, .height = mTargetSurface.h, .depthOrArrayLayers = 1 };
|
||||
wgpuCommandEncoderCopyTextureToTexture(commandEncoder, &texSrc, &texDst, ©Size);
|
||||
|
||||
mContext.executeCommandEncoder(commandEncoder);
|
||||
// release command encoder
|
||||
const WGPUCommandBufferDescriptor commandBufferDesc{};
|
||||
WGPUCommandBuffer commandsBuffer = wgpuCommandEncoderFinish(commandEncoder, &commandBufferDesc);
|
||||
wgpuQueueSubmit(mContext.queue, 1, &commandsBuffer);
|
||||
wgpuCommandBufferRelease(commandsBuffer);
|
||||
wgpuCommandEncoderRelease(commandEncoder);
|
||||
|
||||
return true;
|
||||
|
@ -413,114 +297,88 @@ bool WgRenderer::target(WGPUInstance instance, WGPUSurface surface, uint32_t w,
|
|||
|
||||
mContext.initialize(instance, surface);
|
||||
|
||||
// configure surface
|
||||
WGPUSurfaceConfiguration surfaceConfiguration{};
|
||||
surfaceConfiguration.nextInChain = nullptr;
|
||||
surfaceConfiguration.device = mContext.device;
|
||||
surfaceConfiguration.format = WGPUTextureFormat_BGRA8Unorm;
|
||||
surfaceConfiguration.usage = WGPUTextureUsage_CopyDst;
|
||||
surfaceConfiguration.viewFormatCount = 0;
|
||||
surfaceConfiguration.viewFormats = nullptr;
|
||||
surfaceConfiguration.alphaMode = WGPUCompositeAlphaMode_Auto;
|
||||
surfaceConfiguration.width = w;
|
||||
surfaceConfiguration.height = h;
|
||||
#ifdef __EMSCRIPTEN__
|
||||
surfaceConfiguration.presentMode = WGPUPresentMode_Fifo;
|
||||
#else
|
||||
surfaceConfiguration.presentMode = WGPUPresentMode_Immediate;
|
||||
#endif
|
||||
wgpuSurfaceConfigure(mContext.surface, &surfaceConfiguration);
|
||||
|
||||
WGPUSurfaceConfiguration surfaceConfiguration {
|
||||
.device = mContext.device,
|
||||
.format = WGPUTextureFormat_BGRA8Unorm,
|
||||
.usage = WGPUTextureUsage_CopyDst,
|
||||
.width = w, .height = h,
|
||||
#ifdef __EMSCRIPTEN__
|
||||
.presentMode = WGPUPresentMode_Fifo,
|
||||
#else
|
||||
.presentMode = WGPUPresentMode_Immediate
|
||||
#endif
|
||||
};
|
||||
wgpuSurfaceConfigure(surface, &surfaceConfiguration);
|
||||
initialize();
|
||||
|
||||
mRenderStorageInterm.initialize(mContext, w, h, WG_SSAA_SAMPLES);
|
||||
mRenderStorageMask.initialize(mContext, w, h, WG_SSAA_SAMPLES);
|
||||
mRenderStorageCopy.initialize(mContext, w, h, WG_SSAA_SAMPLES);
|
||||
mRenderStorageRoot.initialize(mContext, w, h, WG_SSAA_SAMPLES);
|
||||
mRenderStorageScreen.initialize(mContext, w, h, 1, WGPUTextureFormat_BGRA8Unorm);
|
||||
mRenderStoragePool.initialize(mContext, w, h);
|
||||
mStorageRoot.initialize(mContext, w, h);
|
||||
mCompositor.initialize(mContext, w, h);
|
||||
// screen buffer
|
||||
mTexScreen = mContext.createTexStorage(w, h, WGPUTextureFormat_BGRA8Unorm);
|
||||
mTexViewScreen = mContext.createTextureView(mTexScreen);
|
||||
mBindGroupScreen = mContext.pipelines->layouts.createBindGroupScreen1WO(mTexViewScreen);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Compositor* WgRenderer::target(TVG_UNUSED const RenderRegion& region, TVG_UNUSED ColorSpace cs)
|
||||
{
|
||||
mCompositorStack.push(new WgCompositor);
|
||||
mCompositorStack.push(new WgCompose);
|
||||
return mCompositorStack.last();
|
||||
}
|
||||
|
||||
|
||||
bool WgRenderer::beginComposite(TVG_UNUSED Compositor* cmp, TVG_UNUSED CompositeMethod method, TVG_UNUSED uint8_t opacity)
|
||||
bool WgRenderer::beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity)
|
||||
{
|
||||
// save current composition settings
|
||||
WgCompositor *comp = (WgCompositor*)cmp;
|
||||
comp->method = method;
|
||||
comp->opacity = opacity;
|
||||
comp->blendMethod = mBlendMethod;
|
||||
|
||||
WgCompose* compose = (WgCompose *)cmp;
|
||||
compose->method = method;
|
||||
compose->opacity = opacity;
|
||||
compose->blend = mBlendMethod;
|
||||
// end current render pass
|
||||
mRenderStorageStack.last()->endRenderPass();
|
||||
// allocate new render storage and push it to top of render tree
|
||||
WgRenderStorage* renderStorage = mRenderStoragePool.allocate(mContext, mTargetSurface.w, mTargetSurface.h, WG_SSAA_SAMPLES);
|
||||
mRenderStorageStack.push(renderStorage);
|
||||
// begin last render pass
|
||||
mRenderStorageStack.last()->beginRenderPass(mCommandEncoder, true);
|
||||
mCompositor.endRenderPass();
|
||||
// allocate new render storage and push to the render tree stack
|
||||
WgRenderStorage* storage = mRenderStoragePool.allocate(mContext);
|
||||
mRenderStorageStack.push(storage);
|
||||
// begin newly added render pass
|
||||
mCompositor.beginRenderPass(mCommandEncoder, mRenderStorageStack.last(), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool WgRenderer::endComposite(TVG_UNUSED Compositor* cmp)
|
||||
bool WgRenderer::endComposite(Compositor* cmp)
|
||||
{
|
||||
WgCompositor *comp = (WgCompositor*)cmp;
|
||||
// get current composition settings
|
||||
WgCompose* comp = (WgCompose *)cmp;
|
||||
// end current render pass
|
||||
mCompositor.endRenderPass();
|
||||
// finish scene blending
|
||||
if (comp->method == CompositeMethod::None) {
|
||||
// end current render pass
|
||||
mRenderStorageStack.last()->endRenderPass();
|
||||
// get two last render targets
|
||||
WgRenderStorage* renderStorageSrc = mRenderStorageStack.last();
|
||||
// get source and destination render storages
|
||||
WgRenderStorage* src = mRenderStorageStack.last();
|
||||
mRenderStorageStack.pop();
|
||||
|
||||
// blent scene to current render storage
|
||||
WgBindGroupBlendMethod* blendMethod = mBlendMethodPool.allocate(mContext, comp->blendMethod);
|
||||
WgBindGroupOpacity* opacity = mOpacityPool.allocate(mContext, comp->opacity);
|
||||
mRenderStorageCopy.copy(mCommandEncoder, mRenderStorageStack.last());
|
||||
mRenderStorageStack.last()->blend(mContext, mCommandEncoder,
|
||||
&mContext.pipelines->computeBlendImage,
|
||||
renderStorageSrc, &mRenderStorageCopy, blendMethod, opacity);
|
||||
|
||||
WgRenderStorage* dst = mRenderStorageStack.last();
|
||||
// apply blend
|
||||
mCompositor.blend(mCommandEncoder, src, dst, comp->opacity, comp->blend, WgRenderRasterType::Image);
|
||||
// back render targets to the pool
|
||||
mRenderStoragePool.free(mContext, renderStorageSrc);
|
||||
// begin last render pass
|
||||
mRenderStorageStack.last()->beginRenderPass(mCommandEncoder, false);
|
||||
} else {
|
||||
// end current render pass
|
||||
mRenderStorageStack.last()->endRenderPass();
|
||||
|
||||
mRenderStoragePool.free(mContext, src);
|
||||
} else { // finish composition
|
||||
// get source, mask and destination render storages
|
||||
WgRenderStorage* renderStorageSrc = mRenderStorageStack.last();
|
||||
WgRenderStorage* src = mRenderStorageStack.last();
|
||||
mRenderStorageStack.pop();
|
||||
WgRenderStorage* renderStorageMsk = mRenderStorageStack.last();
|
||||
WgRenderStorage* msk = mRenderStorageStack.last();
|
||||
mRenderStorageStack.pop();
|
||||
WgRenderStorage* renderStorageDst = mRenderStorageStack.last();
|
||||
|
||||
// get compose, blend and opacity settings
|
||||
WgBindGroupCompositeMethod* composeMethod = mCompositeMethodPool.allocate(mContext, comp->method);
|
||||
WgBindGroupBlendMethod* blendMethod = mBlendMethodPool.allocate(mContext, mBlendMethod);
|
||||
WgBindGroupOpacity* opacity = mOpacityPool.allocate(mContext, comp->opacity);
|
||||
|
||||
// compose and blend
|
||||
// dest = blend(dest, compose(src, msk, composeMethod), blendMethod, opacity)
|
||||
mRenderStorageCopy.copy(mCommandEncoder, renderStorageDst);
|
||||
renderStorageDst->compose(mContext, mCommandEncoder,
|
||||
renderStorageSrc, renderStorageMsk, &mRenderStorageCopy,
|
||||
composeMethod, blendMethod, opacity);
|
||||
|
||||
WgRenderStorage* dst = mRenderStorageStack.last();
|
||||
// apply composition
|
||||
mCompositor.compose(mCommandEncoder, src, msk, dst,comp->method);
|
||||
// back render targets to the pool
|
||||
mRenderStoragePool.free(mContext, renderStorageSrc);
|
||||
mRenderStoragePool.free(mContext, renderStorageMsk);
|
||||
// begin last render pass
|
||||
mRenderStorageStack.last()->beginRenderPass(mCommandEncoder, false);
|
||||
mRenderStoragePool.free(mContext, src);
|
||||
mRenderStoragePool.free(mContext, msk);
|
||||
}
|
||||
// begin previous render pass
|
||||
mCompositor.beginRenderPass(mCommandEncoder, mRenderStorageStack.last(), false);
|
||||
|
||||
// delete current compositor
|
||||
// delete current compositor settings
|
||||
delete mCompositorStack.last();
|
||||
mCompositorStack.pop();
|
||||
|
||||
|
|
37
src/renderer/wg_engine/tvgWgRenderer.h
Normal file → Executable file
37
src/renderer/wg_engine/tvgWgRenderer.h
Normal file → Executable file
|
@ -23,7 +23,7 @@
|
|||
#ifndef _TVG_WG_RENDERER_H_
|
||||
#define _TVG_WG_RENDERER_H_
|
||||
|
||||
#include "tvgWgRenderTarget.h"
|
||||
#include "tvgWgCompositor.h"
|
||||
|
||||
class WgRenderer : public RenderMethod
|
||||
{
|
||||
|
@ -61,37 +61,32 @@ private:
|
|||
~WgRenderer();
|
||||
void initialize();
|
||||
void release();
|
||||
void clearDisposes();
|
||||
void renderClipPath(Array<WgRenderDataPaint*>& clips);
|
||||
void disposeObjects();
|
||||
|
||||
WGPUCommandEncoder mCommandEncoder{};
|
||||
WgRenderStorage mRenderStorageInterm; // intermidiate buffer to render
|
||||
WgRenderStorage mRenderStorageCopy; // copy of destination target (blend and compostition)
|
||||
WgRenderStorage mRenderStorageMask; // buffer to render mask
|
||||
WgRenderStorage mRenderStorageRoot; // root render storage
|
||||
WgRenderStorage mRenderStorageScreen; // storage with data after antializing
|
||||
WgRenderStoragePool mRenderStoragePool; // pool to hold render tree storages
|
||||
WgBindGroupOpacityPool mOpacityPool; // opacity, blend methods and composite methods pool
|
||||
WgBindGroupBlendMethodPool mBlendMethodPool;
|
||||
WgBindGroupCompositeMethodPool mCompositeMethodPool;
|
||||
WgRenderDataShapePool mRenderDataShapePool; // render data shpes pool
|
||||
WgRenderDataShapePool mRenderDataShapePool;
|
||||
|
||||
// render tree stacks
|
||||
Array<Compositor*> mCompositorStack;
|
||||
WgRenderStorage mStorageRoot;
|
||||
Array<WgCompose*> mCompositorStack;
|
||||
Array<WgRenderStorage*> mRenderStorageStack;
|
||||
WgRenderStoragePool mRenderStoragePool;
|
||||
|
||||
WgContext mContext;
|
||||
WgPipelines mPipelines;
|
||||
WgCompositor mCompositor;
|
||||
|
||||
// screen buffer
|
||||
WGPUTexture mTexScreen{};
|
||||
WGPUTextureView mTexViewScreen{};
|
||||
WGPUBindGroup mBindGroupScreen{};
|
||||
|
||||
Surface mTargetSurface;
|
||||
BlendMethod mBlendMethod{};
|
||||
RenderRegion mViewport{};
|
||||
|
||||
BlendMethod mBlendMethod{}; // current blend method
|
||||
|
||||
// disposed resources, they should be released on synced call.
|
||||
struct {
|
||||
Array<RenderData> renderDatas{};
|
||||
Key key;
|
||||
} mDisposed;
|
||||
Array<RenderData> mDisposeRenderDatas{};
|
||||
Key mDisposeKey{};
|
||||
};
|
||||
|
||||
#endif /* _TVG_WG_RENDERER_H_ */
|
||||
|
|
585
src/renderer/wg_engine/tvgWgShaderSrc.cpp
Normal file → Executable file
585
src/renderer/wg_engine/tvgWgShaderSrc.cpp
Normal file → Executable file
|
@ -26,10 +26,10 @@
|
|||
#define WG_SHADER_SOURCE(...) #__VA_ARGS__
|
||||
|
||||
//************************************************************************
|
||||
// shader pipeline fill
|
||||
// graphics shader source: stencil
|
||||
//************************************************************************
|
||||
|
||||
const char* cShaderSource_PipelineFill = WG_SHADER_SOURCE(
|
||||
const char* cShaderSrc_Stencil = WG_SHADER_SOURCE(
|
||||
// vertex input
|
||||
struct VertexInput {
|
||||
@location(0) position: vec2f
|
||||
|
@ -41,8 +41,8 @@ struct VertexOutput {
|
|||
};
|
||||
|
||||
// uniforms
|
||||
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
|
||||
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f;
|
||||
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
|
||||
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f;
|
||||
|
||||
@vertex
|
||||
fn vs_main(in: VertexInput) -> VertexOutput {
|
||||
|
@ -59,10 +59,10 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|||
);
|
||||
|
||||
//************************************************************************
|
||||
// shader pipeline solid
|
||||
// graphics shader source: solid
|
||||
//************************************************************************
|
||||
|
||||
const char* cShaderSource_PipelineSolid = WG_SHADER_SOURCE(
|
||||
const char* cShaderSrc_Solid = WG_SHADER_SOURCE(
|
||||
// vertex input
|
||||
struct VertexInput {
|
||||
@location(0) position: vec2f
|
||||
|
@ -109,10 +109,10 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|||
);
|
||||
|
||||
//************************************************************************
|
||||
// shader pipeline linear
|
||||
// graphics shader source: linear
|
||||
//************************************************************************
|
||||
|
||||
const char* cShaderSource_PipelineLinear = WG_SHADER_SOURCE(
|
||||
const char* cShaderSrc_Linear = WG_SHADER_SOURCE(
|
||||
// vertex input
|
||||
struct VertexInput {
|
||||
@location(0) position: vec2f
|
||||
|
@ -149,7 +149,7 @@ struct VertexOutput {
|
|||
// uniforms
|
||||
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
|
||||
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f;
|
||||
@group(1) @binding(1) var<uniform> uBlendSettings : BlendSettings;
|
||||
@group(1) @binding(1) var<uniform> uBlendSettings : BlendSettings;
|
||||
@group(2) @binding(0) var<uniform> uLinearGradient : LinearGradient;
|
||||
|
||||
@vertex
|
||||
|
@ -212,10 +212,10 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|||
);
|
||||
|
||||
//************************************************************************
|
||||
// shader pipeline radial
|
||||
// graphics shader source: radial
|
||||
//************************************************************************
|
||||
|
||||
const char* cShaderSource_PipelineRadial = WG_SHADER_SOURCE(
|
||||
const char* cShaderSrc_Radial = WG_SHADER_SOURCE(
|
||||
// vertex input
|
||||
struct VertexInput {
|
||||
@location(0) position: vec2f
|
||||
|
@ -309,10 +309,10 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|||
);
|
||||
|
||||
//************************************************************************
|
||||
// cShaderSource_PipelineImage
|
||||
// graphics shader source: image
|
||||
//************************************************************************
|
||||
|
||||
const char* cShaderSource_PipelineImage = WG_SHADER_SOURCE(
|
||||
const char* cShaderSrc_Image = WG_SHADER_SOURCE(
|
||||
// vertex input
|
||||
struct VertexInput {
|
||||
@location(0) position: vec2f,
|
||||
|
@ -365,296 +365,329 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|||
);
|
||||
|
||||
//************************************************************************
|
||||
// cShaderSource_PipelineComputeBlend
|
||||
// compute shaders blend
|
||||
//************************************************************************
|
||||
|
||||
const std::string strBlendShaderHeader = WG_SHADER_SOURCE(
|
||||
@group(0) @binding(0) var imageSrc : texture_storage_2d<rgba8unorm, read>;
|
||||
@group(0) @binding(1) var imageDst : texture_storage_2d<rgba8unorm, read>;
|
||||
@group(0) @binding(2) var imageTrg : texture_storage_2d<rgba8unorm, write>;
|
||||
@group(1) @binding(0) var<uniform> blendMethod : u32;
|
||||
@group(2) @binding(0) var<uniform> opacity : f32;
|
||||
|
||||
@compute @workgroup_size(8, 8)
|
||||
fn cs_main( @builtin(global_invocation_id) id: vec3u) {
|
||||
let texSize = textureDimensions(imageSrc);
|
||||
if ((id.x >= texSize.x) || (id.y >= texSize.y)) { return; };
|
||||
|
||||
let colorSrc= textureLoad(imageSrc, id.xy);
|
||||
if (colorSrc.a == 0.0) { return; }
|
||||
let colorDst = textureLoad(imageDst, id.xy);
|
||||
|
||||
var One: vec3f = vec3(1.0);
|
||||
var So: f32 = opacity;
|
||||
var Sc: vec3f = colorSrc.rgb;
|
||||
var Sa: f32 = colorSrc.a;
|
||||
var Dc: vec3f = colorDst.rgb;
|
||||
var Da: f32 = colorDst.a;
|
||||
var Rc: vec3f = colorDst.rgb;
|
||||
var Ra: f32 = 1.0;
|
||||
);
|
||||
|
||||
const std::string strBlendMaskShaderHeader = WG_SHADER_SOURCE(
|
||||
@group(0) @binding(0) var imageSrc : texture_storage_2d<rgba8unorm, read>;
|
||||
@group(0) @binding(1) var imageMsk : texture_storage_2d<rgba8unorm, read>;
|
||||
@group(0) @binding(2) var imageDst : texture_storage_2d<rgba8unorm, read>;
|
||||
@group(0) @binding(3) var imageTrg : texture_storage_2d<rgba8unorm, write>;
|
||||
@group(1) @binding(0) var<uniform> blendMethod : u32;
|
||||
@group(2) @binding(0) var<uniform> opacity : f32;
|
||||
|
||||
@compute @workgroup_size(8, 8)
|
||||
fn cs_main( @builtin(global_invocation_id) id: vec3u) {
|
||||
let texSize = textureDimensions(imageSrc);
|
||||
if ((id.x >= texSize.x) || (id.y >= texSize.y)) { return; };
|
||||
|
||||
let colorMsk = textureLoad(imageMsk, id.xy);
|
||||
if (colorMsk.a == 0.0) { return; }
|
||||
let colorSrc = textureLoad(imageSrc, id.xy);
|
||||
if (colorSrc.a == 0.0) { return; }
|
||||
let colorDst = textureLoad(imageDst, id.xy);
|
||||
|
||||
var One: vec3f = vec3(1.0);
|
||||
var So: f32 = opacity;
|
||||
var Sc: vec3f = colorSrc.rgb;
|
||||
var Sa: f32 = colorSrc.a;
|
||||
var Dc: vec3f = colorDst.rgb;
|
||||
var Da: f32 = colorDst.a;
|
||||
var Rc: vec3f = colorDst.rgb;
|
||||
var Ra: f32 = 1.0;
|
||||
);
|
||||
|
||||
const std::string strBlendShaderPreConditionsGradient = WG_SHADER_SOURCE(
|
||||
Sc = Sc + Dc.rgb * (1.0 - Sa);
|
||||
Sa = Sa + Da * (1.0 - Sa);
|
||||
);
|
||||
|
||||
const std::string strBlendShaderPreConditionsImage = WG_SHADER_SOURCE(
|
||||
Sc = Sc * So;
|
||||
Sa = Sa * So;
|
||||
);
|
||||
|
||||
const std::string strBlendShaderBlendMethod = WG_SHADER_SOURCE(
|
||||
switch blendMethod {
|
||||
/* Normal */ case 0u: {
|
||||
Rc = Sc + Dc * (1.0 - Sa);
|
||||
Ra = Sa + Da * (1.0 - Sa);
|
||||
}
|
||||
/* Add */ case 1u: { Rc = Sc + Dc; }
|
||||
/* Screen */ case 2u: { Rc = Sc + Dc - Sc * Dc; }
|
||||
/* Multiply */ case 3u: { Rc = Sc * Dc; }
|
||||
/* Overlay */ case 4u: {
|
||||
Rc.r = select(1.0 - min(1.0, 2 * (1 - Sc.r) * (1 - Dc.r)), min(1.0, 2 * Sc.r * Dc.r), (Dc.r < 0.5));
|
||||
Rc.g = select(1.0 - min(1.0, 2 * (1 - Sc.g) * (1 - Dc.g)), min(1.0, 2 * Sc.g * Dc.g), (Dc.g < 0.5));
|
||||
Rc.b = select(1.0 - min(1.0, 2 * (1 - Sc.b) * (1 - Dc.b)), min(1.0, 2 * Sc.b * Dc.b), (Dc.b < 0.5));
|
||||
}
|
||||
/* Difference */ case 5u: { Rc = abs(Dc - Sc); }
|
||||
/* Exclusion */ case 6u: { Rc = min(One, Sc + Dc - min(One, 2 * Sc * Dc)); }
|
||||
/* SrcOver */ case 7u: { Rc = Sc; Ra = Sa; }
|
||||
/* Darken */ case 8u: { Rc = min(Sc, Dc); }
|
||||
/* Lighten */ case 9u: { Rc = max(Sc, Dc); }
|
||||
/* ColorDodge */ case 10u: {
|
||||
Rc.r = select(Dc.r, (Dc.r * 255.0 / (255.0 - Sc.r * 255.0))/255.0, (1.0 - Sc.r > 0.0));
|
||||
Rc.g = select(Dc.g, (Dc.g * 255.0 / (255.0 - Sc.g * 255.0))/255.0, (1.0 - Sc.g > 0.0));
|
||||
Rc.b = select(Dc.b, (Dc.b * 255.0 / (255.0 - Sc.b * 255.0))/255.0, (1.0 - Sc.b > 0.0));
|
||||
}
|
||||
/* ColorBurn */ case 11u: {
|
||||
Rc.r = select(1.0 - Dc.r, (255.0 - (255.0 - Dc.r * 255.0) / (Sc.r * 255.0)) / 255.0, (Sc.r > 0.0));
|
||||
Rc.g = select(1.0 - Dc.g, (255.0 - (255.0 - Dc.g * 255.0) / (Sc.g * 255.0)) / 255.0, (Sc.g > 0.0));
|
||||
Rc.b = select(1.0 - Dc.b, (255.0 - (255.0 - Dc.b * 255.0) / (Sc.b * 255.0)) / 255.0, (Sc.b > 0.0));
|
||||
}
|
||||
/* HardLight */ case 12u: {
|
||||
Rc.r = select(1.0 - min(1.0, 2 * (1 - Sc.r) * (1 - Dc.r)), min(1.0, 2 * Sc.r * Dc.r), (Sc.r < 0.5));
|
||||
Rc.g = select(1.0 - min(1.0, 2 * (1 - Sc.g) * (1 - Dc.g)), min(1.0, 2 * Sc.g * Dc.g), (Sc.g < 0.5));
|
||||
Rc.b = select(1.0 - min(1.0, 2 * (1 - Sc.b) * (1 - Dc.b)), min(1.0, 2 * Sc.b * Dc.b), (Sc.b < 0.5));
|
||||
}
|
||||
/* SoftLight */ case 13u: { Rc = min(One, (One - 2 * Sc) * Dc * Dc + 2.0 * Sc * Dc); }
|
||||
default: {
|
||||
Rc = Sc + Dc * (1.0 - Sa);
|
||||
Ra = Sa + Da * (1.0 - Sa);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const std::string strBlendShaderPostConditionsGradient = WG_SHADER_SOURCE(
|
||||
// nothing
|
||||
);
|
||||
|
||||
const std::string strBlendShaderPostConditionsImage = WG_SHADER_SOURCE(
|
||||
Rc = select(mix(Dc, Rc, Sa), Rc, blendMethod == 0);
|
||||
Ra = select(mix(Da, Ra, Sa), Ra, blendMethod == 0);
|
||||
);
|
||||
|
||||
const std::string strBlendShaderFooter = WG_SHADER_SOURCE(
|
||||
textureStore(imageTrg, id.xy, vec4(Rc, Ra));
|
||||
}
|
||||
);
|
||||
|
||||
// pipeline shader modules blend solid
|
||||
const std::string strComputeBlendSolid =
|
||||
strBlendShaderHeader +
|
||||
strBlendShaderBlendMethod +
|
||||
strBlendShaderFooter;
|
||||
const char* cShaderSource_PipelineComputeBlendSolid = strComputeBlendSolid.c_str();
|
||||
|
||||
// pipeline shader modules blend gradient
|
||||
const std::string strComputeBlendGradient =
|
||||
strBlendShaderHeader +
|
||||
strBlendShaderPreConditionsGradient +
|
||||
strBlendShaderBlendMethod +
|
||||
strBlendShaderPostConditionsGradient +
|
||||
strBlendShaderFooter;
|
||||
const char* cShaderSource_PipelineComputeBlendGradient = strComputeBlendGradient.c_str();
|
||||
|
||||
// pipeline shader modules blend image
|
||||
const std::string strComputeBlendImage =
|
||||
strBlendShaderHeader +
|
||||
strBlendShaderPreConditionsImage +
|
||||
strBlendShaderBlendMethod +
|
||||
strBlendShaderPostConditionsImage +
|
||||
strBlendShaderFooter;
|
||||
const char* cShaderSource_PipelineComputeBlendImage = strComputeBlendImage.c_str();
|
||||
|
||||
// pipeline shader modules blend solid mask
|
||||
const std::string strComputeBlendSolidMask =
|
||||
strBlendMaskShaderHeader +
|
||||
strBlendShaderBlendMethod +
|
||||
strBlendShaderFooter;
|
||||
const char* cShaderSource_PipelineComputeBlendSolidMask = strComputeBlendSolidMask.c_str();
|
||||
|
||||
// pipeline shader modules blend gradient mask
|
||||
const std::string strComputeBlendGradientMask =
|
||||
strBlendMaskShaderHeader +
|
||||
strBlendShaderPreConditionsGradient +
|
||||
strBlendShaderBlendMethod +
|
||||
strBlendShaderPostConditionsGradient +
|
||||
strBlendShaderFooter;
|
||||
const char* cShaderSource_PipelineComputeBlendGradientMask = strComputeBlendGradientMask.c_str();
|
||||
|
||||
// pipeline shader modules blend image mask
|
||||
const std::string strComputeBlendImageMask =
|
||||
strBlendMaskShaderHeader +
|
||||
strBlendShaderPreConditionsImage +
|
||||
strBlendShaderBlendMethod +
|
||||
strBlendShaderPostConditionsImage +
|
||||
strBlendShaderFooter;
|
||||
const char* cShaderSource_PipelineComputeBlendImageMask = strComputeBlendImageMask.c_str();
|
||||
|
||||
// pipeline shader modules clear
|
||||
const char* cShaderSource_PipelineComputeClear = WG_SHADER_SOURCE(
|
||||
@group(0) @binding(0) var imageDst : texture_storage_2d<rgba8unorm, write>;
|
||||
|
||||
@compute @workgroup_size(8, 8)
|
||||
fn cs_main( @builtin(global_invocation_id) id: vec3u) {
|
||||
textureStore(imageDst, id.xy, vec4f(0.0, 0.0, 0.0, 0.0));
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
// pipeline shader modules copy
|
||||
const char* cShaderSource_PipelineComputeCopy = WG_SHADER_SOURCE(
|
||||
@group(0) @binding(0) var imageSrc : texture_storage_2d<rgba8unorm, read>;
|
||||
@group(1) @binding(0) var imageDst : texture_storage_2d<rgba8unorm, write>;
|
||||
|
||||
@compute @workgroup_size(8, 8)
|
||||
fn cs_main( @builtin(global_invocation_id) id: vec3u) {
|
||||
textureStore(imageDst, id.xy, textureLoad(imageSrc, id.xy));
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
// pipeline shader modules compose
|
||||
const char* cShaderSource_PipelineComputeMaskCompose = WG_SHADER_SOURCE(
|
||||
const char* cShaderSrc_MergeMasks = WG_SHADER_SOURCE(
|
||||
@group(0) @binding(0) var imageMsk0 : texture_storage_2d<rgba8unorm, read>;
|
||||
@group(0) @binding(1) var imageMsk1 : texture_storage_2d<rgba8unorm, read>;
|
||||
@group(0) @binding(2) var imageTrg : texture_storage_2d<rgba8unorm, write>;
|
||||
@group(1) @binding(0) var imageMsk1 : texture_storage_2d<rgba8unorm, read>;
|
||||
@group(2) @binding(0) var imageTrg : texture_storage_2d<rgba8unorm, write>;
|
||||
|
||||
@compute @workgroup_size(8, 8)
|
||||
fn cs_main( @builtin(global_invocation_id) id: vec3u) {
|
||||
fn cs_main(@builtin(global_invocation_id) id: vec3u) {
|
||||
let colorMsk0 = textureLoad(imageMsk0, id.xy);
|
||||
let colorMsk1 = textureLoad(imageMsk1, id.xy);
|
||||
textureStore(imageTrg, id.xy, colorMsk0 * colorMsk1);
|
||||
}
|
||||
);
|
||||
|
||||
// pipeline shader modules compose
|
||||
const char* cShaderSource_PipelineComputeCompose = WG_SHADER_SOURCE(
|
||||
|
||||
//************************************************************************
|
||||
// compute shaders blend
|
||||
//************************************************************************
|
||||
|
||||
std::string cBlendHeader = WG_SHADER_SOURCE(
|
||||
@group(0) @binding(0) var imageSrc : texture_storage_2d<rgba8unorm, read>;
|
||||
@group(0) @binding(1) var imageMsk : texture_storage_2d<rgba8unorm, read>;
|
||||
@group(0) @binding(2) var imageDst : texture_storage_2d<rgba8unorm, read>;
|
||||
@group(0) @binding(3) var imageTrg : texture_storage_2d<rgba8unorm, write>;
|
||||
@group(1) @binding(0) var<uniform> composeMethod : u32;
|
||||
@group(2) @binding(0) var<uniform> blendMethod : u32;
|
||||
@group(3) @binding(0) var<uniform> opacity : f32;
|
||||
@group(1) @binding(0) var imageDst : texture_storage_2d<rgba8unorm, read>;
|
||||
@group(2) @binding(0) var imageTgt : texture_storage_2d<rgba8unorm, write>;
|
||||
@group(3) @binding(0) var<uniform> So : f32;
|
||||
|
||||
@compute @workgroup_size(8, 8)
|
||||
fn cs_main( @builtin(global_invocation_id) id: vec3u) {
|
||||
let texSize = textureDimensions(imageSrc);
|
||||
if ((id.x >= texSize.x) || (id.y >= texSize.y)) { return; };
|
||||
fn cs_main(@builtin(global_invocation_id) id: vec3u) {
|
||||
let colorSrc = textureLoad(imageSrc, id.xy);
|
||||
if (colorSrc.a == 0.0) { return; }
|
||||
let colorDst = textureLoad(imageDst, id.xy);
|
||||
|
||||
var Sc: vec3f = colorSrc.rgb;
|
||||
var Sa: f32 = colorSrc.a;
|
||||
let Dc: vec3f = colorDst.rgb;
|
||||
let Da: f32 = colorDst.a;
|
||||
var Rc: vec3f = colorDst.rgb;
|
||||
var Ra: f32 = 1.0;
|
||||
);
|
||||
|
||||
const std::string cBlendPreProcess_Gradient = WG_SHADER_SOURCE(
|
||||
Sc = Sc + Dc * (1.0 - Sa);
|
||||
Sa = Sa + Da * (1.0 - Sa);
|
||||
);
|
||||
const std::string cBlendPreProcess_Image = WG_SHADER_SOURCE(
|
||||
Sc = Sc * So;
|
||||
Sa = Sa * So;
|
||||
);
|
||||
|
||||
const std::string cBlendEquation_Normal = WG_SHADER_SOURCE(
|
||||
Rc = Sc + Dc * (1.0 - Sa);
|
||||
Ra = Sa + Da * (1.0 - Sa);
|
||||
);
|
||||
const std::string cBlendEquation_Add = WG_SHADER_SOURCE(
|
||||
Rc = Sc + Dc;
|
||||
);
|
||||
const std::string cBlendEquation_Screen = WG_SHADER_SOURCE(
|
||||
Rc = Sc + Dc - Sc * Dc;
|
||||
);
|
||||
const std::string cBlendEquation_Multiply = WG_SHADER_SOURCE(
|
||||
Rc = Sc * Dc;
|
||||
);
|
||||
const std::string cBlendEquation_Overlay = WG_SHADER_SOURCE(
|
||||
Rc.r = select(1.0 - min(1.0, 2 * (1 - Sc.r) * (1 - Dc.r)), min(1.0, 2 * Sc.r * Dc.r), (Dc.r < 0.5));
|
||||
Rc.g = select(1.0 - min(1.0, 2 * (1 - Sc.g) * (1 - Dc.g)), min(1.0, 2 * Sc.g * Dc.g), (Dc.g < 0.5));
|
||||
Rc.b = select(1.0 - min(1.0, 2 * (1 - Sc.b) * (1 - Dc.b)), min(1.0, 2 * Sc.b * Dc.b), (Dc.b < 0.5));
|
||||
);
|
||||
const std::string cBlendEquation_Difference = WG_SHADER_SOURCE(
|
||||
Rc = abs(Dc - Sc);
|
||||
);
|
||||
const std::string cBlendEquation_Exclusion = WG_SHADER_SOURCE(
|
||||
let One = vec3f(1.0, 1.0, 1.0);
|
||||
Rc = min(One, Sc + Dc - min(One, 2 * Sc * Dc));
|
||||
);
|
||||
const std::string cBlendEquation_SrcOver = WG_SHADER_SOURCE(
|
||||
Rc = Sc; Ra = Sa;
|
||||
);
|
||||
const std::string cBlendEquation_Darken = WG_SHADER_SOURCE(
|
||||
Rc = min(Sc, Dc);
|
||||
);
|
||||
const std::string cBlendEquation_Lighten = WG_SHADER_SOURCE(
|
||||
Rc = max(Sc, Dc);
|
||||
);
|
||||
const std::string cBlendEquation_ColorDodge = WG_SHADER_SOURCE(
|
||||
Rc.r = select(Dc.r, (Dc.r * 255.0 / (255.0 - Sc.r * 255.0))/255.0, (1.0 - Sc.r > 0.0));
|
||||
Rc.g = select(Dc.g, (Dc.g * 255.0 / (255.0 - Sc.g * 255.0))/255.0, (1.0 - Sc.g > 0.0));
|
||||
Rc.b = select(Dc.b, (Dc.b * 255.0 / (255.0 - Sc.b * 255.0))/255.0, (1.0 - Sc.b > 0.0));
|
||||
);
|
||||
const std::string cBlendEquation_ColorBurn = WG_SHADER_SOURCE(
|
||||
Rc.r = select(1.0 - Dc.r, (255.0 - (255.0 - Dc.r * 255.0) / (Sc.r * 255.0)) / 255.0, (Sc.r > 0.0));
|
||||
Rc.g = select(1.0 - Dc.g, (255.0 - (255.0 - Dc.g * 255.0) / (Sc.g * 255.0)) / 255.0, (Sc.g > 0.0));
|
||||
Rc.b = select(1.0 - Dc.b, (255.0 - (255.0 - Dc.b * 255.0) / (Sc.b * 255.0)) / 255.0, (Sc.b > 0.0));
|
||||
);
|
||||
const std::string cBlendEquation_HardLight = WG_SHADER_SOURCE(
|
||||
Rc.r = select(1.0 - min(1.0, 2 * (1 - Sc.r) * (1 - Dc.r)), min(1.0, 2 * Sc.r * Dc.r), (Sc.r < 0.5));
|
||||
Rc.g = select(1.0 - min(1.0, 2 * (1 - Sc.g) * (1 - Dc.g)), min(1.0, 2 * Sc.g * Dc.g), (Sc.g < 0.5));
|
||||
Rc.b = select(1.0 - min(1.0, 2 * (1 - Sc.b) * (1 - Dc.b)), min(1.0, 2 * Sc.b * Dc.b), (Sc.b < 0.5));
|
||||
);
|
||||
const std::string cBlendEquation_SoftLight = WG_SHADER_SOURCE(
|
||||
let One = vec3f(1.0, 1.0, 1.0);
|
||||
Rc = min(One, (One - 2 * Sc) * Dc * Dc + 2.0 * Sc * Dc);
|
||||
);
|
||||
|
||||
const std::string cBlendPostProcess_Gradient = WG_SHADER_SOURCE(
|
||||
);
|
||||
const std::string cBlendPostProcess_Image = WG_SHADER_SOURCE(
|
||||
Rc = mix(Dc, Rc, Sa);
|
||||
Ra = mix(Da, Ra, Sa);
|
||||
);
|
||||
|
||||
const std::string cBlendFooter = WG_SHADER_SOURCE(
|
||||
textureStore(imageTgt, id.xy, vec4f(Rc, Ra));
|
||||
}
|
||||
);
|
||||
|
||||
std::string blend_solid_Normal = cBlendHeader + cBlendEquation_Normal + cBlendFooter;
|
||||
std::string blend_solid_Add = cBlendHeader + cBlendEquation_Add + cBlendFooter;
|
||||
std::string blend_solid_Screen = cBlendHeader + cBlendEquation_Screen + cBlendFooter;
|
||||
std::string blend_solid_Multiply = cBlendHeader + cBlendEquation_Multiply + cBlendFooter;
|
||||
std::string blend_solid_Overlay = cBlendHeader + cBlendEquation_Overlay + cBlendFooter;
|
||||
std::string blend_solid_Difference = cBlendHeader + cBlendEquation_Difference + cBlendFooter;
|
||||
std::string blend_solid_Exclusion = cBlendHeader + cBlendEquation_Exclusion + cBlendFooter;
|
||||
std::string blend_solid_SrcOver = cBlendHeader + cBlendEquation_SrcOver + cBlendFooter;
|
||||
std::string blend_solid_Darken = cBlendHeader + cBlendEquation_Darken + cBlendFooter;
|
||||
std::string blend_solid_Lighten = cBlendHeader + cBlendEquation_Lighten + cBlendFooter;
|
||||
std::string blend_solid_ColorDodge = cBlendHeader + cBlendEquation_ColorDodge + cBlendFooter;
|
||||
std::string blend_solid_ColorBurn = cBlendHeader + cBlendEquation_ColorBurn + cBlendFooter;
|
||||
std::string blend_solid_HardLight = cBlendHeader + cBlendEquation_HardLight + cBlendFooter;
|
||||
std::string blend_solid_SoftLight = cBlendHeader + cBlendEquation_SoftLight + cBlendFooter;
|
||||
|
||||
const char* cShaderSrc_Blend_Solid[] {
|
||||
blend_solid_Normal.c_str(),
|
||||
blend_solid_Add.c_str(),
|
||||
blend_solid_Screen.c_str(),
|
||||
blend_solid_Multiply.c_str(),
|
||||
blend_solid_Overlay.c_str(),
|
||||
blend_solid_Difference.c_str(),
|
||||
blend_solid_Exclusion.c_str(),
|
||||
blend_solid_SrcOver.c_str(),
|
||||
blend_solid_Darken.c_str(),
|
||||
blend_solid_Lighten.c_str(),
|
||||
blend_solid_ColorDodge.c_str(),
|
||||
blend_solid_ColorBurn.c_str(),
|
||||
blend_solid_HardLight.c_str(),
|
||||
blend_solid_SoftLight.c_str()
|
||||
};
|
||||
|
||||
|
||||
const std::string cBlendHeader_Gradient = cBlendHeader + cBlendPreProcess_Gradient;
|
||||
const std::string cBlendFooter_Gradient = cBlendPostProcess_Gradient + cBlendFooter;
|
||||
std::string blend_gradient_Normal = cBlendHeader_Gradient + cBlendEquation_Normal + cBlendFooter_Gradient;
|
||||
std::string blend_gradient_Add = cBlendHeader_Gradient + cBlendEquation_Add + cBlendFooter_Gradient;
|
||||
std::string blend_gradient_Screen = cBlendHeader_Gradient + cBlendEquation_Screen + cBlendFooter_Gradient;
|
||||
std::string blend_gradient_Multiply = cBlendHeader_Gradient + cBlendEquation_Multiply + cBlendFooter_Gradient;
|
||||
std::string blend_gradient_Overlay = cBlendHeader_Gradient + cBlendEquation_Overlay + cBlendFooter_Gradient;
|
||||
std::string blend_gradient_Difference = cBlendHeader_Gradient + cBlendEquation_Difference + cBlendFooter_Gradient;
|
||||
std::string blend_gradient_Exclusion = cBlendHeader_Gradient + cBlendEquation_Exclusion + cBlendFooter_Gradient;
|
||||
std::string blend_gradient_SrcOver = cBlendHeader_Gradient + cBlendEquation_SrcOver + cBlendFooter_Gradient;
|
||||
std::string blend_gradient_Darken = cBlendHeader_Gradient + cBlendEquation_Darken + cBlendFooter_Gradient;
|
||||
std::string blend_gradient_Lighten = cBlendHeader_Gradient + cBlendEquation_Lighten + cBlendFooter_Gradient;
|
||||
std::string blend_gradient_ColorDodge = cBlendHeader_Gradient + cBlendEquation_ColorDodge + cBlendFooter_Gradient;
|
||||
std::string blend_gradient_ColorBurn = cBlendHeader_Gradient + cBlendEquation_ColorBurn + cBlendFooter_Gradient;
|
||||
std::string blend_gradient_HardLight = cBlendHeader_Gradient + cBlendEquation_HardLight + cBlendFooter_Gradient;
|
||||
std::string blend_gradient_SoftLight = cBlendHeader_Gradient + cBlendEquation_SoftLight + cBlendFooter_Gradient;
|
||||
|
||||
const char* cShaderSrc_Blend_Gradient[] {
|
||||
blend_gradient_Normal.c_str(),
|
||||
blend_gradient_Add.c_str(),
|
||||
blend_gradient_Screen.c_str(),
|
||||
blend_gradient_Multiply.c_str(),
|
||||
blend_gradient_Overlay.c_str(),
|
||||
blend_gradient_Difference.c_str(),
|
||||
blend_gradient_Exclusion.c_str(),
|
||||
blend_gradient_SrcOver.c_str(),
|
||||
blend_gradient_Darken.c_str(),
|
||||
blend_gradient_Lighten.c_str(),
|
||||
blend_gradient_ColorDodge.c_str(),
|
||||
blend_gradient_ColorBurn.c_str(),
|
||||
blend_gradient_HardLight.c_str(),
|
||||
blend_gradient_SoftLight.c_str()
|
||||
};
|
||||
|
||||
const std::string cBlendHeader_Image = cBlendHeader + cBlendPreProcess_Image;
|
||||
const std::string cBlendFooter_Image = cBlendPostProcess_Image + cBlendFooter;
|
||||
std::string blend_image_Normal = cBlendHeader_Image + cBlendEquation_Normal + cBlendFooter;
|
||||
std::string blend_image_Add = cBlendHeader_Image + cBlendEquation_Add + cBlendFooter_Image;
|
||||
std::string blend_image_Screen = cBlendHeader_Image + cBlendEquation_Screen + cBlendFooter_Image;
|
||||
std::string blend_image_Multiply = cBlendHeader_Image + cBlendEquation_Multiply + cBlendFooter_Image;
|
||||
std::string blend_image_Overlay = cBlendHeader_Image + cBlendEquation_Overlay + cBlendFooter_Image;
|
||||
std::string blend_image_Difference = cBlendHeader_Image + cBlendEquation_Difference + cBlendFooter_Image;
|
||||
std::string blend_image_Exclusion = cBlendHeader_Image + cBlendEquation_Exclusion + cBlendFooter_Image;
|
||||
std::string blend_image_SrcOver = cBlendHeader_Image + cBlendEquation_SrcOver + cBlendFooter_Image;
|
||||
std::string blend_image_Darken = cBlendHeader_Image + cBlendEquation_Darken + cBlendFooter_Image;
|
||||
std::string blend_image_Lighten = cBlendHeader_Image + cBlendEquation_Lighten + cBlendFooter_Image;
|
||||
std::string blend_image_ColorDodge = cBlendHeader_Image + cBlendEquation_ColorDodge + cBlendFooter_Image;
|
||||
std::string blend_image_ColorBurn = cBlendHeader_Image + cBlendEquation_ColorBurn + cBlendFooter_Image;
|
||||
std::string blend_image_HardLight = cBlendHeader_Image + cBlendEquation_HardLight + cBlendFooter_Image;
|
||||
std::string blend_image_SoftLight = cBlendHeader_Image + cBlendEquation_SoftLight + cBlendFooter_Image;
|
||||
|
||||
const char* cShaderSrc_Blend_Image[] {
|
||||
blend_image_Normal.c_str(),
|
||||
blend_image_Add.c_str(),
|
||||
blend_image_Screen.c_str(),
|
||||
blend_image_Multiply.c_str(),
|
||||
blend_image_Overlay.c_str(),
|
||||
blend_image_Difference.c_str(),
|
||||
blend_image_Exclusion.c_str(),
|
||||
blend_image_SrcOver.c_str(),
|
||||
blend_image_Darken.c_str(),
|
||||
blend_image_Lighten.c_str(),
|
||||
blend_image_ColorDodge.c_str(),
|
||||
blend_image_ColorBurn.c_str(),
|
||||
blend_image_HardLight.c_str(),
|
||||
blend_image_SoftLight.c_str()
|
||||
};
|
||||
|
||||
//************************************************************************
|
||||
// compute shaders compose
|
||||
//************************************************************************
|
||||
|
||||
std::string cComposeHeader = WG_SHADER_SOURCE(
|
||||
@group(0) @binding(0) var imageSrc : texture_storage_2d<rgba8unorm, read>;
|
||||
@group(1) @binding(0) var imageMsk : texture_storage_2d<rgba8unorm, read>;
|
||||
@group(2) @binding(0) var imageDst : texture_storage_2d<rgba8unorm, read>;
|
||||
@group(3) @binding(0) var imageTgt : texture_storage_2d<rgba8unorm, write>;
|
||||
|
||||
@compute @workgroup_size(8, 8)
|
||||
fn cs_main(@builtin(global_invocation_id) id: vec3u) {
|
||||
let colorSrc = textureLoad(imageSrc, id.xy);
|
||||
let colorMsk = textureLoad(imageMsk, id.xy);
|
||||
let colorDst = textureLoad(imageDst, id.xy);
|
||||
|
||||
var One: vec3f = vec3(1.0);
|
||||
let luma: f32 = dot(colorMsk.xyz, vec3f(0.2125, 0.7154, 0.0721));
|
||||
var So : f32 = opacity;
|
||||
var Mc : vec3f = colorMsk.rgb;
|
||||
var Ma : f32 = colorMsk.a;
|
||||
var MSc : vec3f = colorSrc.rgb;
|
||||
var MSa : f32 = colorSrc.a;
|
||||
var Sc : vec3f = colorSrc.rgb;
|
||||
var Sa : f32 = colorSrc.a;
|
||||
var Dc : vec3f = colorDst.rgb;
|
||||
var Da : f32 = colorDst.a;
|
||||
var Rc : vec3f = colorDst.rgb;
|
||||
var Ra : f32 = 1.0;
|
||||
var Sc : vec3f = colorSrc.rgb;
|
||||
var Sa : f32 = colorSrc.a;
|
||||
var Mc : vec3f = colorMsk.rgb;
|
||||
var Ma : f32 = colorMsk.a;
|
||||
var Dc : vec3f = colorDst.rgb;
|
||||
var Da : f32 = colorDst.a;
|
||||
var Rc : vec3f = colorDst.rgb;
|
||||
var Ra : f32 = colorDst.a;
|
||||
);
|
||||
|
||||
if (composeMethod <= /* InvLumaMask */5u) {
|
||||
switch composeMethod {
|
||||
/* AlphaMask */ case 2u: { Sc = MSc * Ma; Sa = MSa * Ma; }
|
||||
/* InvAlphaMask */ case 3u: { Sc = MSc * (1.0 - Ma); Sa = MSa * (1.0 - Ma); }
|
||||
/* LumaMask */ case 4u: { Sc = MSc * luma; Sa = MSa * luma; }
|
||||
/* InvLumaMask */ case 5u: { Sc = MSc * (1.0-luma); Sa = MSa * (1.0-luma); }
|
||||
default: { Sc = MSc; Sa = MSa; }
|
||||
}
|
||||
Rc = Sc + Dc * (1.0 - Sa);
|
||||
Ra = Sa + Da * (1.0 - Sa);
|
||||
} else {
|
||||
Sc = Dc;
|
||||
switch composeMethod {
|
||||
/* AddMask */ case 6u: { Sa = MSa + Ma * (1.0 - MSa); }
|
||||
/* SubtractMask */ case 7u: { Sa = MSa * (1.0 - Ma); }
|
||||
/* IntersectMask */ case 8u: { Sa = MSa * Ma; }
|
||||
/* DifferenceMask */ case 9u: { Sa = MSa * (1.0 - Ma) + Ma * (1.0 - MSa); }
|
||||
default: { Rc = Sc; Ra = Sa; }
|
||||
}
|
||||
Rc = Sc;
|
||||
Ra = Sa;
|
||||
}
|
||||
std::string cComposeEquation_None = WG_SHADER_SOURCE(
|
||||
Rc = Dc;
|
||||
Ra = Da;
|
||||
);
|
||||
std::string cComposeEquation_ClipPath = WG_SHADER_SOURCE(
|
||||
Rc = Sc * Ma + Dc * (1.0 - Sa * Ma);
|
||||
Ra = Sa * Ma + Da * (1.0 - Sa * Ma);
|
||||
);
|
||||
|
||||
textureStore(imageTrg, id.xy, vec4f(Rc, Ra));
|
||||
std::string cComposeEquation_AlphaMask = WG_SHADER_SOURCE(
|
||||
Rc = Sc * Ma + Dc * (1.0 - Sa * Ma);
|
||||
Ra = Sa * Ma + Da * (1.0 - Sa * Ma);
|
||||
);
|
||||
std::string cComposeEquation_InvAlphaMask = WG_SHADER_SOURCE(
|
||||
Rc = Sc * (1.0 - Ma) + Dc * (1.0 - Sa * (1.0 - Ma));
|
||||
Ra = Sa * (1.0 - Ma) + Da * (1.0 - Sa * (1.0 - Ma));
|
||||
);
|
||||
std::string cComposeEquation_LumaMask = WG_SHADER_SOURCE(
|
||||
let luma: f32 = dot(Mc, vec3f(0.2125, 0.7154, 0.0721));
|
||||
Rc = Sc * luma + Dc * (1.0 - Sa * luma);
|
||||
Ra = Sa * luma + Da * (1.0 - Sa * luma);
|
||||
);
|
||||
std::string cComposeEquation_InvLumaMask = WG_SHADER_SOURCE(
|
||||
let luma: f32 = dot(Mc, vec3f(0.2125, 0.7154, 0.0721));
|
||||
Rc = Sc * (1.0 - luma) + Dc * (1.0 - Sa * (1.0 - luma));
|
||||
Ra = Sa * (1.0 - luma) + Da * (1.0 - Sa * (1.0 - luma));
|
||||
);
|
||||
std::string cComposeEquation_AddMask = WG_SHADER_SOURCE(
|
||||
Rc = Sc;
|
||||
Ra = Sa + Ma * (1.0 - Sa);
|
||||
);
|
||||
std::string cComposeEquation_SubtractMask = WG_SHADER_SOURCE(
|
||||
Rc = Sc;
|
||||
Ra = Sa * (1.0 - Ma);
|
||||
);
|
||||
std::string cComposeEquation_IntersectMask = WG_SHADER_SOURCE(
|
||||
Rc = Sc;
|
||||
Ra = Sa * Ma;
|
||||
);
|
||||
std::string cComposeEquation_DifferenceMask = WG_SHADER_SOURCE(
|
||||
Rc = Sc;
|
||||
Ra = Sa * (1.0 - Ma) + Ma * (1.0 - Sa);
|
||||
);
|
||||
|
||||
std::string cComposeFooter = WG_SHADER_SOURCE(
|
||||
textureStore(imageTgt, id.xy, vec4f(Rc, Ra));
|
||||
}
|
||||
);
|
||||
|
||||
// pipeline shader modules anti-aliasing
|
||||
const char* cShaderSource_PipelineComputeAntiAlias = WG_SHADER_SOURCE(
|
||||
std::string compose_None = cComposeHeader + cComposeEquation_None + cComposeFooter;
|
||||
std::string compose_ClipPath = cComposeHeader + cComposeEquation_ClipPath + cComposeFooter;
|
||||
std::string compose_AlphaMask = cComposeHeader + cComposeEquation_AlphaMask + cComposeFooter;
|
||||
std::string compose_InvAlphaMask = cComposeHeader + cComposeEquation_InvAlphaMask + cComposeFooter;
|
||||
std::string compose_LumaMask = cComposeHeader + cComposeEquation_LumaMask + cComposeFooter;
|
||||
std::string compose_InvLumaMask = cComposeHeader + cComposeEquation_InvLumaMask + cComposeFooter;
|
||||
std::string compose_AddMask = cComposeHeader + cComposeEquation_AddMask + cComposeFooter;
|
||||
std::string compose_SubtractMask = cComposeHeader + cComposeEquation_SubtractMask + cComposeFooter;
|
||||
std::string compose_IntersectMask = cComposeHeader + cComposeEquation_IntersectMask + cComposeFooter;
|
||||
std::string compose_DifferenceMask = cComposeHeader + cComposeEquation_DifferenceMask + cComposeFooter;
|
||||
|
||||
const char* cShaderSrc_Compose[10] {
|
||||
compose_None.c_str(),
|
||||
compose_ClipPath.c_str(),
|
||||
compose_AlphaMask.c_str(),
|
||||
compose_InvAlphaMask.c_str(),
|
||||
compose_LumaMask.c_str(),
|
||||
compose_InvLumaMask.c_str(),
|
||||
compose_AddMask.c_str(),
|
||||
compose_SubtractMask.c_str(),
|
||||
compose_IntersectMask.c_str(),
|
||||
compose_DifferenceMask.c_str(),
|
||||
};
|
||||
|
||||
const char* cShaderSrc_Copy = WG_SHADER_SOURCE(
|
||||
@group(0) @binding(0) var imageSrc : texture_storage_2d<rgba8unorm, read>;
|
||||
@group(1) @binding(0) var imageDst : texture_storage_2d<bgra8unorm, write>;
|
||||
|
||||
@compute @workgroup_size(8, 8)
|
||||
fn cs_main( @builtin(global_invocation_id) id: vec3u) {
|
||||
let texSizeSrc = textureDimensions(imageSrc);
|
||||
let texSizeDst = textureDimensions(imageDst);
|
||||
if ((id.x >= texSizeDst.x) || (id.y >= texSizeDst.y)) { return; };
|
||||
|
||||
let samples = u32(texSizeSrc.x / texSizeDst.x);
|
||||
var color = vec4f(0);
|
||||
for (var i = 0u; i < samples; i++) {
|
||||
for (var j = 0u; j < samples; j++) {
|
||||
color += textureLoad(imageSrc, vec2(id.x * samples + j, id.y * samples + i));
|
||||
}
|
||||
}
|
||||
|
||||
textureStore(imageDst, id.xy, color / f32(samples * samples));
|
||||
fn cs_main(@builtin(global_invocation_id) id: vec3u) {
|
||||
textureStore(imageDst, id.xy, textureLoad(imageSrc, id.xy));
|
||||
}
|
||||
);
|
||||
);
|
44
src/renderer/wg_engine/tvgWgShaderSrc.h
Normal file → Executable file
44
src/renderer/wg_engine/tvgWgShaderSrc.h
Normal file → Executable file
|
@ -20,37 +20,23 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef _TVG_WG_SHADER_SRC_H_
|
||||
#define _TVG_WG_SHADER_SRC_H_
|
||||
|
||||
//*****************************************************************************
|
||||
// render shader modules
|
||||
//*****************************************************************************
|
||||
// graphics shader sources
|
||||
extern const char* cShaderSrc_Stencil;
|
||||
extern const char* cShaderSrc_Solid;
|
||||
extern const char* cShaderSrc_Linear;
|
||||
extern const char* cShaderSrc_Radial;
|
||||
extern const char* cShaderSrc_Image;
|
||||
|
||||
// pipeline shader modules fill
|
||||
extern const char* cShaderSource_PipelineFill;
|
||||
extern const char* cShaderSource_PipelineSolid;
|
||||
extern const char* cShaderSource_PipelineLinear;
|
||||
extern const char* cShaderSource_PipelineRadial;
|
||||
extern const char* cShaderSource_PipelineImage;
|
||||
// compute shader sources: blend, compose and merge path
|
||||
extern const char* cShaderSrc_MergeMasks;
|
||||
extern const char* cShaderSrc_Blend_Solid[14];
|
||||
extern const char* cShaderSrc_Blend_Solid[14];
|
||||
extern const char* cShaderSrc_Blend_Gradient[14];
|
||||
extern const char* cShaderSrc_Blend_Image[14];
|
||||
extern const char* cShaderSrc_Compose[10];
|
||||
extern const char* cShaderSrc_Copy;
|
||||
|
||||
//*****************************************************************************
|
||||
// compute shader modules
|
||||
//*****************************************************************************
|
||||
|
||||
// pipeline shader modules clear, compose and blend
|
||||
extern const char* cShaderSource_PipelineComputeClear;
|
||||
extern const char* cShaderSource_PipelineComputeCopy;
|
||||
extern const char* cShaderSource_PipelineComputeBlendSolid;
|
||||
extern const char* cShaderSource_PipelineComputeBlendGradient;
|
||||
extern const char* cShaderSource_PipelineComputeBlendImage;
|
||||
extern const char* cShaderSource_PipelineComputeBlendSolidMask;
|
||||
extern const char* cShaderSource_PipelineComputeBlendGradientMask;
|
||||
extern const char* cShaderSource_PipelineComputeBlendImageMask;
|
||||
extern const char* cShaderSource_PipelineComputeMaskCompose;
|
||||
extern const char* cShaderSource_PipelineComputeCompose;
|
||||
extern const char* cShaderSource_PipelineComputeAntiAlias;
|
||||
|
||||
#endif // _TVG_WG_SHADER_SRC_H_
|
||||
#endif // _TVG_WG_SHEDER_SRC_H_
|
||||
|
|
0
src/renderer/wg_engine/tvgWgShaderTypes.cpp
Normal file → Executable file
0
src/renderer/wg_engine/tvgWgShaderTypes.cpp
Normal file → Executable file
2
src/renderer/wg_engine/tvgWgShaderTypes.h
Normal file → Executable file
2
src/renderer/wg_engine/tvgWgShaderTypes.h
Normal file → Executable file
|
@ -23,7 +23,7 @@
|
|||
#ifndef _TVG_WG_SHADER_TYPES_H_
|
||||
#define _TVG_WG_SHADER_TYPES_H_
|
||||
|
||||
#include "tvgWgCommon.h"
|
||||
#include "tvgRender.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// shader types
|
||||
|
|
Loading…
Add table
Reference in a new issue