wg_engine: shape bbox based rendering (optimization)

Before the current changes, all surfaces were painted using a full-screen overlay, no matter how large the object was rendered. This approach is redundant and required reorganization. At the moment, all objects are rendered using an overlay equal to the box of the object itself, which reduces the cost of filling the surface.
Also surfaces and images were divided into different entities, which reduces the pressure on memory.
Also geometry data for rendering and geometry data for calculations in system memory were logically separated.
This commit is contained in:
Sergii Liebodkin 2023-12-22 03:01:11 +02:00 committed by Hermet Park
parent 323b615f0d
commit 8553044875
8 changed files with 717 additions and 645 deletions

View file

@ -90,6 +90,7 @@ void WgContext::initialize()
assert(queue);
}
void WgContext::release()
{
if (device) {
@ -100,6 +101,7 @@ void WgContext::release()
if (instance) wgpuInstanceRelease(instance);
}
void WgContext::executeCommandEncoder(WGPUCommandEncoder commandEncoder)
{
// command buffer descriptor
@ -112,6 +114,7 @@ void WgContext::executeCommandEncoder(WGPUCommandEncoder commandEncoder)
wgpuCommandBufferRelease(commandsBuffer);
}
//*****************************************************************************
// bind group
//*****************************************************************************

View file

@ -22,21 +22,25 @@
#include "tvgWgGeometry.h"
void WgVertexList::computeTriFansIndexes()
//***********************************************************************
// WgGeometryData
//***********************************************************************
void WgGeometryData::computeTriFansIndexes()
{
assert(mVertexList.count > 2);
mIndexList.reserve((mVertexList.count - 2) * 3);
for (size_t i = 0; i < mVertexList.count - 2; i++) {
mIndexList.push(0);
mIndexList.push(i + 1);
mIndexList.push(i + 2);
}
if (positions.count <= 2) return;
indexes.reserve((positions.count - 2) * 3);
for (size_t i = 0; i < positions.count - 2; i++) {
indexes.push(0);
indexes.push(i + 1);
indexes.push(i + 2);
}
};
void WgVertexList::appendCubic(WgPoint p1, WgPoint p2, WgPoint p3)
void WgGeometryData::appendCubic(WgPoint p1, WgPoint p2, WgPoint p3)
{
WgPoint p0 = mVertexList.count > 0 ? mVertexList.last() : WgPoint(0.0f, 0.0f);
WgPoint p0 = positions.count > 0 ? positions.last() : WgPoint(0.0f, 0.0f);
const size_t segs = 16;
for (size_t i = 1; i <= segs; i++) {
float t = i / (float)segs;
@ -45,51 +49,368 @@ void WgVertexList::appendCubic(WgPoint p1, WgPoint p2, WgPoint p3)
float t1 = 3 * (1.0f - t) * (1.0f - t) * t;
float t2 = 3 * (1.0f - t) * t * t;
float t3 = 1 * t * t * t;
mVertexList.push(p0 * t0 + p1 * t1 + p2 * t2 + p3 * t3);
}
positions.push(p0 * t0 + p1 * t1 + p2 * t2 + p3 * t3);
}
};
void WgVertexList::appendRect(WgPoint p0, WgPoint p1, WgPoint p2, WgPoint p3)
void WgGeometryData::appendBox(WgPoint pmin, WgPoint pmax)
{
uint32_t index = mVertexList.count;
mVertexList.push(p0); // +0
mVertexList.push(p1); // +1
mVertexList.push(p2); // +2
mVertexList.push(p3); // +3
mIndexList.push(index + 0);
mIndexList.push(index + 1);
mIndexList.push(index + 2);
mIndexList.push(index + 1);
mIndexList.push(index + 3);
mIndexList.push(index + 2);
}
appendRect(
{ pmin.x, pmin.y},
{ pmax.x, pmin.y},
{ pmin.x, pmax.y},
{ pmax.x, pmax.y});
};
void WgGeometryData::appendRect(WgPoint p0, WgPoint p1, WgPoint p2, WgPoint p3)
{
uint32_t index = positions.count;
positions.push(p0); // +0
positions.push(p1); // +1
positions.push(p2); // +2
positions.push(p3); // +3
indexes.push(index + 0);
indexes.push(index + 1);
indexes.push(index + 2);
indexes.push(index + 1);
indexes.push(index + 3);
indexes.push(index + 2);
};
// TODO: optimize vertex and index count
void WgVertexList::appendCircle(WgPoint center, float radius)
void WgGeometryData::appendCircle(WgPoint center, float radius)
{
uint32_t index = mVertexList.count;
uint32_t nSegments = 32;
uint32_t index = positions.count;
uint32_t nSegments = std::trunc(radius);
for (uint32_t i = 0; i < nSegments; i++) {
float angle0 = (float)(i + 0) / nSegments * 3.141593f * 2.0f;
float angle1 = (float)(i + 1) / nSegments * 3.141593f * 2.0f;
float angle0 = (float)(i + 0) / nSegments * (float)M_PI * 2.0f;
float angle1 = (float)(i + 1) / nSegments * (float)M_PI * 2.0f;
WgPoint p0 = center + WgPoint(sin(angle0) * radius, cos(angle0) * radius);
WgPoint p1 = center + WgPoint(sin(angle1) * radius, cos(angle1) * radius);
mVertexList.push(center); // +0
mVertexList.push(p0); // +1
mVertexList.push(p1); // +2
mIndexList.push(index + 0);
mIndexList.push(index + 1);
mIndexList.push(index + 2);
positions.push(center); // +0
positions.push(p0); // +1
positions.push(p1); // +2
indexes.push(index + 0);
indexes.push(index + 1);
indexes.push(index + 2);
index += 3;
}
}
};
void WgVertexList::close()
void WgGeometryData::appendImageBox(float w, float h)
{
if (mVertexList.count > 1) {
mVertexList.push(mVertexList[0]);
positions.push({ 0.0f, 0.0f });
positions.push({ w , 0.0f });
positions.push({ w , h });
positions.push({ 0.0f, h });
texCoords.push({ 0.0f, 0.0f });
texCoords.push({ 1.0f, 0.0f });
texCoords.push({ 1.0f, 1.0f });
texCoords.push({ 0.0f, 1.0f });
indexes.push(0);
indexes.push(1);
indexes.push(2);
indexes.push(0);
indexes.push(2);
indexes.push(3);
};
void WgGeometryData::appendMesh(const RenderMesh* rmesh)
{
assert(rmesh);
positions.reserve(rmesh->triangleCnt * 3);
texCoords.reserve(rmesh->triangleCnt * 3);
indexes.reserve(rmesh->triangleCnt * 3);
for (uint32_t i = 0; i < rmesh->triangleCnt; i++) {
positions.push(rmesh->triangles[i].vertex[0].pt);
positions.push(rmesh->triangles[i].vertex[1].pt);
positions.push(rmesh->triangles[i].vertex[2].pt);
texCoords.push(rmesh->triangles[i].vertex[0].uv);
texCoords.push(rmesh->triangles[i].vertex[1].uv);
texCoords.push(rmesh->triangles[i].vertex[2].uv);
indexes.push(i*3 + 0);
indexes.push(i*3 + 1);
indexes.push(i*3 + 2);
}
};
void WgGeometryData::close()
{
if (positions.count > 1) {
positions.push(positions[0]);
}
};
//***********************************************************************
// WgGeometryDataGroup
//***********************************************************************
void WgGeometryDataGroup::getBBox(WgPoint& pmin, WgPoint& pmax) {
assert(geometries.count > 0);
assert(geometries[0]->positions.count > 0);
pmin = geometries[0]->positions[0];
pmax = geometries[0]->positions[0];
for (uint32_t i = 0; i < geometries.count; i++) {
for (uint32_t j = 0; j < geometries[i]->positions.count; j++) {
pmin.x = std::min(pmin.x, geometries[i]->positions[j].x);
pmin.y = std::min(pmin.y, geometries[i]->positions[j].y);
pmax.x = std::max(pmax.x, geometries[i]->positions[j].x);
pmax.y = std::max(pmax.y, geometries[i]->positions[j].y);
}
}
}
void WgGeometryDataGroup::tesselate(const RenderShape& rshape)
{
decodePath(rshape, this);
for (uint32_t i = 0; i < geometries.count; i++)
geometries[i]->computeTriFansIndexes();
}
void WgGeometryDataGroup::stroke(const RenderShape& rshape)
{
assert(rshape.stroke);
if (rshape.stroke->dashPattern) {
// first step: decode path data
WgGeometryDataGroup segments{};
decodePath(rshape, &segments);
// second step: split path to segments using dash patterns
WgGeometryDataGroup outlines{};
strokeSegments(rshape, &segments, &outlines);
// third step: create geometry for strokes
auto strokeData = new WgGeometryData;
strokeSublines(rshape, &outlines, strokeData);
// append strokes geometry data
geometries.push(strokeData);
} else {
// first step: decode path data
WgGeometryDataGroup outlines{};
decodePath(rshape, &outlines);
// second step: create geometry for strokes
auto strokeData = new WgGeometryData;
strokeSublines(rshape, &outlines, strokeData);
// append strokes geometry data
geometries.push(strokeData);
}
}
void WgGeometryDataGroup::release()
{
for (uint32_t i = 0; i < geometries.count; i++)
delete geometries[i];
geometries.clear();
}
void WgGeometryDataGroup::decodePath(const RenderShape& rshape, WgGeometryDataGroup* outlines)
{
size_t pntIndex = 0;
for (uint32_t cmdIndex = 0; cmdIndex < rshape.path.cmds.count; cmdIndex++) {
PathCommand cmd = rshape.path.cmds[cmdIndex];
if (cmd == PathCommand::MoveTo) {
outlines->geometries.push(new WgGeometryData);
auto outline = outlines->geometries.last();
outline->positions.push(rshape.path.pts[pntIndex]);
pntIndex++;
} else if (cmd == PathCommand::LineTo) {
auto outline = outlines->geometries.last();
if (outline)
outline->positions.push(rshape.path.pts[pntIndex]);
pntIndex++;
} else if (cmd == PathCommand::Close) {
auto outline = outlines->geometries.last();
if ((outline) && (outline->positions.count > 0))
outline->positions.push(outline->positions[0]);
} else if (cmd == PathCommand::CubicTo) {
auto outline = outlines->geometries.last();
if ((outline) && (outline->positions.count > 0))
outline->appendCubic(
rshape.path.pts[pntIndex + 0],
rshape.path.pts[pntIndex + 1],
rshape.path.pts[pntIndex + 2]
);
pntIndex += 3;
}
}
}
void WgGeometryDataGroup::strokeSegments(const RenderShape& rshape, WgGeometryDataGroup* outlines, WgGeometryDataGroup* segments)
{
for (uint32_t i = 0; i < outlines->geometries.count; i++) {
auto& vlist = outlines->geometries[i]->positions;
// append single point segment
if (vlist.count == 1) {
auto segment = new WgGeometryData;
segment->positions.push(vlist.last());
segments->geometries.push(segment);
}
if (vlist.count >= 2) {
uint32_t icurr = 1;
uint32_t ipatt = 0;
WgPoint vcurr = vlist[0];
while (icurr < vlist.count) {
if (ipatt % 2 == 0) {
segments->geometries.push(new WgGeometryData);
segments->geometries.last()->positions.push(vcurr);
}
float lcurr = rshape.stroke->dashPattern[ipatt];
while ((icurr < vlist.count) && (vlist[icurr].dist(vcurr) < lcurr)) {
lcurr -= vlist[icurr].dist(vcurr);
vcurr = vlist[icurr];
icurr++;
if (ipatt % 2 == 0) segments->geometries.last()->positions.push(vcurr);
}
if (icurr < vlist.count) {
vcurr = vcurr + (vlist[icurr] - vlist[icurr-1]).normal() * lcurr;
if (ipatt % 2 == 0) segments->geometries.last()->positions.push(vcurr);
}
ipatt = (ipatt + 1) % rshape.stroke->dashCnt;
}
}
}
}
void WgGeometryDataGroup::strokeSublines(const RenderShape& rshape, WgGeometryDataGroup* outlines, WgGeometryData* strokes)
{
float wdt = rshape.stroke->width / 2;
for (uint32_t i = 0; i < outlines->geometries.count; i++) {
auto outline = outlines->geometries[i];
// single point sub-path
if (outline->positions.count == 1) {
if (rshape.stroke->cap == StrokeCap::Round) {
strokes->appendCircle(outline->positions[0], wdt);
} else if (rshape.stroke->cap == StrokeCap::Butt) {
// for zero length sub-paths no stroke is rendered
} else if (rshape.stroke->cap == StrokeCap::Square) {
strokes->appendRect(
outline->positions[0] + WgPoint(+wdt, +wdt),
outline->positions[0] + WgPoint(+wdt, -wdt),
outline->positions[0] + WgPoint(-wdt, +wdt),
outline->positions[0] + WgPoint(-wdt, -wdt)
);
}
}
// single line sub-path
if (outline->positions.count == 2) {
WgPoint v0 = outline->positions[0];
WgPoint v1 = outline->positions[1];
WgPoint dir0 = (v1 - v0).normal();
WgPoint nrm0 = WgPoint{ -dir0.y, +dir0.x };
if (rshape.stroke->cap == StrokeCap::Round) {
strokes->appendRect(
v0 - nrm0 * wdt, v0 + nrm0 * wdt,
v1 - nrm0 * wdt, v1 + nrm0 * wdt);
strokes->appendCircle(outline->positions[0], wdt);
strokes->appendCircle(outline->positions[1], wdt);
} else if (rshape.stroke->cap == StrokeCap::Butt) {
strokes->appendRect(
v0 - nrm0 * wdt, v0 + nrm0 * wdt,
v1 - nrm0 * wdt, v1 + nrm0 * wdt
);
} else if (rshape.stroke->cap == StrokeCap::Square) {
strokes->appendRect(
v0 - nrm0 * wdt - dir0 * wdt, v0 + nrm0 * wdt - dir0 * wdt,
v1 - nrm0 * wdt + dir0 * wdt, v1 + nrm0 * wdt + dir0 * wdt
);
}
}
// multi-lined sub-path
if (outline->positions.count > 2) {
// append first cap
WgPoint v0 = outline->positions[0];
WgPoint v1 = outline->positions[1];
WgPoint dir0 = (v1 - v0).normal();
WgPoint nrm0 = WgPoint{ -dir0.y, +dir0.x };
if (rshape.stroke->cap == StrokeCap::Round) {
strokes->appendCircle(v0, wdt);
} else if (rshape.stroke->cap == StrokeCap::Butt) {
// no cap needed
} else if (rshape.stroke->cap == StrokeCap::Square) {
strokes->appendRect(
v0 - nrm0 * wdt - dir0 * wdt,
v0 + nrm0 * wdt - dir0 * wdt,
v0 - nrm0 * wdt,
v0 + nrm0 * wdt
);
}
// append last cap
v0 = outline->positions[outline->positions.count - 2];
v1 = outline->positions[outline->positions.count - 1];
dir0 = (v1 - v0).normal();
nrm0 = WgPoint{ -dir0.y, +dir0.x };
if (rshape.stroke->cap == StrokeCap::Round) {
strokes->appendCircle(v1, wdt);
} else if (rshape.stroke->cap == StrokeCap::Butt) {
// no cap needed
} else if (rshape.stroke->cap == StrokeCap::Square) {
strokes->appendRect(
v1 - nrm0 * wdt,
v1 + nrm0 * wdt,
v1 - nrm0 * wdt + dir0 * wdt,
v1 + nrm0 * wdt + dir0 * wdt
);
}
// append sub-lines
for (uint32_t j = 0; j < outline->positions.count - 1; j++) {
WgPoint v0 = outline->positions[j + 0];
WgPoint v1 = outline->positions[j + 1];
WgPoint dir = (v1 - v0).normal();
WgPoint nrm { -dir.y, +dir.x };
strokes->appendRect(
v0 - nrm * wdt,
v0 + nrm * wdt,
v1 - nrm * wdt,
v1 + nrm * wdt
);
}
// append joints (TODO: separate by joint types)
for (uint32_t j = 1; j < outline->positions.count - 1; j++) {
WgPoint v0 = outline->positions[j - 1];
WgPoint v1 = outline->positions[j + 0];
WgPoint v2 = outline->positions[j + 1];
WgPoint dir0 = (v1 - v0).normal();
WgPoint dir1 = (v1 - v2).normal();
WgPoint nrm0 { -dir0.y, +dir0.x };
WgPoint nrm1 { +dir1.y, -dir1.x };
if (rshape.stroke->join == StrokeJoin::Round) {
strokes->appendCircle(v1, wdt);
} else if (rshape.stroke->join == StrokeJoin::Bevel) {
strokes->appendRect(
v1 - nrm0 * wdt, v1 - nrm1 * wdt,
v1 + nrm1 * wdt, v1 + nrm0 * wdt
);
} else if (rshape.stroke->join == StrokeJoin::Miter) {
WgPoint nrm = (dir0 + dir1).normal();
float cosine = nrm.dot(nrm0);
if ((cosine != 0.0f) && (abs(wdt / cosine) <= rshape.stroke->miterlimit * 2)) {
strokes->appendRect(v1 + nrm * (wdt / cosine), v1 + nrm0 * wdt, v1 + nrm1 * wdt, v1);
strokes->appendRect(v1 - nrm * (wdt / cosine), v1 - nrm0 * wdt, v1 - nrm1 * wdt, v1);
} else {
strokes->appendRect(
v1 - nrm0 * wdt, v1 - nrm1 * wdt,
v1 + nrm1 * wdt, v1 + nrm0 * wdt);
}
}
}
}
}
}

View file

@ -25,7 +25,7 @@
#include <cassert>
#include "tvgMath.h"
#include "tvgCommon.h"
#include "tvgRender.h"
#include "tvgArray.h"
class WgPoint
@ -75,17 +75,38 @@ public:
}
};
class WgVertexList
struct WgGeometryData
{
public:
Array<WgPoint> mVertexList;
Array<uint32_t> mIndexList;
Array<WgPoint> positions{};
Array<WgPoint> texCoords{};
Array<uint32_t> indexes{};
// webgpu did not support triangle fans primitives type
// so we can emulate triangle fans using indexing
void computeTriFansIndexes();
void appendCubic(WgPoint p1, WgPoint p2, WgPoint p3);
void appendBox(WgPoint pmin, WgPoint pmax);
void appendRect(WgPoint p0, WgPoint p1, WgPoint p2, WgPoint p3);
void appendCircle(WgPoint center, float radius);
void appendImageBox(float w, float h);
void appendMesh(const RenderMesh* rmesh);
void close();
};
struct WgGeometryDataGroup
{
Array<WgGeometryData*> geometries{};
virtual ~WgGeometryDataGroup() { release(); }
void getBBox(WgPoint& pmin, WgPoint& pmax);
void tesselate(const RenderShape& rshape);
void stroke(const RenderShape& rshape);
void release();
private:
static void decodePath(const RenderShape& rshape, WgGeometryDataGroup* outlines);
static void strokeSegments(const RenderShape& rshape, WgGeometryDataGroup* outlines, WgGeometryDataGroup* segments);
static void strokeSublines(const RenderShape& rshape, WgGeometryDataGroup* outlines, WgGeometryData* strokes);
};
#endif // _TVG_WG_GEOMETRY_H_

View file

@ -1,3 +1,4 @@
/*
* Copyright (c) 2023 the ThorVG project. All rights reserved.
@ -20,111 +21,129 @@
* SOFTWARE.
*/
#ifndef _TVG_WG_RENDER_DATA_H_
#define _TVG_WG_RENDER_DATA_H_
#include "tvgWgRenderData.h"
//***********************************************************************
// WgGeometryData
// WgMeshData
//***********************************************************************
void WgGeometryData::draw(WGPURenderPassEncoder renderPassEncoder)
void WgMeshData::draw(WGPURenderPassEncoder renderPassEncoder)
{
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, mBufferVertex, 0, mVertexCount * sizeof(float) * 2);
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, mBufferIndex, WGPUIndexFormat_Uint32, 0, mIndexCount * sizeof(uint32_t));
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, mIndexCount, 1, 0, 0, 0);
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, bufferPosition, 0, vertexCount * sizeof(float) * 2);
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, bufferIndex, WGPUIndexFormat_Uint32, 0, indexCount * sizeof(uint32_t));
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, indexCount, 1, 0, 0, 0);
}
void WgGeometryData::drawImage(WGPURenderPassEncoder renderPassEncoder)
void WgMeshData::drawImage(WGPURenderPassEncoder renderPassEncoder)
{
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, mBufferVertex, 0, mVertexCount * sizeof(float) * 2);
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 1, mBufferTexCoords, 0, mVertexCount * sizeof(float) * 2);
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, mBufferIndex, WGPUIndexFormat_Uint32, 0, mIndexCount * sizeof(uint32_t));
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, mIndexCount, 1, 0, 0, 0);
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, bufferPosition, 0, vertexCount * sizeof(float) * 2);
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 1, bufferTexCoord, 0, vertexCount * sizeof(float) * 2);
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, bufferIndex, WGPUIndexFormat_Uint32, 0, indexCount * sizeof(uint32_t));
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, indexCount, 1, 0, 0, 0);
};
void WgMeshData::update(WgContext& context, WgGeometryData* geometryData){
release(context);
assert(geometryData);
// buffer position data create and write
if(geometryData->positions.count > 0) {
vertexCount = geometryData->positions.count;
WGPUBufferDescriptor bufferDesc{};
bufferDesc.nextInChain = nullptr;
bufferDesc.label = "Buffer position geometry data";
bufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex;
bufferDesc.size = sizeof(float) * vertexCount * 2; // x, y
bufferDesc.mappedAtCreation = false;
bufferPosition = wgpuDeviceCreateBuffer(context.device, &bufferDesc);
assert(bufferPosition);
wgpuQueueWriteBuffer(context.queue, bufferPosition, 0, &geometryData->positions[0], vertexCount * sizeof(float) * 2);
}
void WgGeometryData::update(WGPUDevice device, WGPUQueue queue, WgVertexList* vertexList)
{
update(device, queue,
(float *)vertexList->mVertexList.data,
vertexList->mVertexList.count,
vertexList->mIndexList.data,
vertexList->mIndexList.count);
}
void WgGeometryData::update(WGPUDevice device, WGPUQueue queue, float* vertexData, size_t vertexCount, uint32_t* indexData, size_t indexCount)
{
release();
// buffer vertex data create and write
WGPUBufferDescriptor bufferVertexDesc{};
bufferVertexDesc.nextInChain = nullptr;
bufferVertexDesc.label = "Buffer vertex geometry data";
bufferVertexDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex;
bufferVertexDesc.size = sizeof(float) * vertexCount * 2; // x, y
bufferVertexDesc.mappedAtCreation = false;
mBufferVertex = wgpuDeviceCreateBuffer(device, &bufferVertexDesc);
assert(mBufferVertex);
wgpuQueueWriteBuffer(queue, mBufferVertex, 0, vertexData, vertexCount * sizeof(float) * 2);
mVertexCount = vertexCount;
if(geometryData->texCoords.count > 0) {
WGPUBufferDescriptor bufferDesc{};
bufferDesc.nextInChain = nullptr;
bufferDesc.label = "Buffer tex coords geometry data";
bufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex;
bufferDesc.size = sizeof(float) * vertexCount * 2; // u, v
bufferDesc.mappedAtCreation = false;
bufferTexCoord = wgpuDeviceCreateBuffer(context.device, &bufferDesc);
assert(bufferPosition);
wgpuQueueWriteBuffer(context.queue, bufferTexCoord, 0, &geometryData->texCoords[0], vertexCount * sizeof(float) * 2);
}
// buffer index data create and write
WGPUBufferDescriptor bufferIndexDesc{};
bufferIndexDesc.nextInChain = nullptr;
bufferIndexDesc.label = "Buffer index geometry data";
bufferIndexDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index;
bufferIndexDesc.size = sizeof(uint32_t) * indexCount;
bufferIndexDesc.mappedAtCreation = false;
mBufferIndex = wgpuDeviceCreateBuffer(device, &bufferIndexDesc);
assert(mBufferIndex);
wgpuQueueWriteBuffer(queue, mBufferIndex, 0, indexData, indexCount * sizeof(uint32_t));
mIndexCount = indexCount;
if(geometryData->indexes.count > 0) {
indexCount = geometryData->indexes.count;
WGPUBufferDescriptor bufferDesc{};
bufferDesc.nextInChain = nullptr;
bufferDesc.label = "Buffer index geometry data";
bufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index;
bufferDesc.size = sizeof(uint32_t) * indexCount;
bufferDesc.mappedAtCreation = false;
bufferIndex = wgpuDeviceCreateBuffer(context.device, &bufferDesc);
assert(bufferIndex);
wgpuQueueWriteBuffer(context.queue, bufferIndex, 0, &geometryData->indexes[0], indexCount * sizeof(uint32_t));
}
};
void WgGeometryData::update(WGPUDevice device, WGPUQueue queue, float* vertexData, float* texCoordsData, size_t vertexCount, uint32_t* indexData, size_t indexCount) {
update(device, queue, vertexData, vertexCount, indexData, indexCount);
// buffer tex coords data create and write
WGPUBufferDescriptor bufferTexCoordsDesc{};
bufferTexCoordsDesc.nextInChain = nullptr;
bufferTexCoordsDesc.label = "Buffer tex coords geometry data";
bufferTexCoordsDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex;
bufferTexCoordsDesc.size = sizeof(float) * vertexCount * 2; // u, v
bufferTexCoordsDesc.mappedAtCreation = false;
mBufferTexCoords = wgpuDeviceCreateBuffer(device, &bufferTexCoordsDesc);
assert(mBufferTexCoords);
wgpuQueueWriteBuffer(queue, mBufferTexCoords, 0, texCoordsData, vertexCount * sizeof(float) * 2);
}
void WgGeometryData::release()
void WgMeshData::release(WgContext& context)
{
if (mBufferIndex) {
wgpuBufferDestroy(mBufferIndex);
wgpuBufferRelease(mBufferIndex);
mBufferIndex = nullptr;
mVertexCount = 0;
if (bufferIndex) {
wgpuBufferDestroy(bufferIndex);
wgpuBufferRelease(bufferIndex);
bufferIndex = nullptr;
indexCount = 0;
}
if (mBufferTexCoords) {
wgpuBufferDestroy(mBufferTexCoords);
wgpuBufferRelease(mBufferTexCoords);
mBufferTexCoords = nullptr;
if (bufferTexCoord) {
wgpuBufferDestroy(bufferTexCoord);
wgpuBufferRelease(bufferTexCoord);
bufferTexCoord = nullptr;
}
if (mBufferVertex) {
wgpuBufferDestroy(mBufferVertex);
wgpuBufferRelease(mBufferVertex);
mBufferVertex = nullptr;
mIndexCount = 0;
if (bufferPosition) {
wgpuBufferDestroy(bufferPosition);
wgpuBufferRelease(bufferPosition);
bufferPosition = nullptr;
bufferPosition = 0;
}
};
//***********************************************************************
// WgMeshDataGroup
//***********************************************************************
void WgMeshDataGroup::update(WgContext& context, WgGeometryDataGroup* geometryDataGroup)
{
release(context);
assert(geometryDataGroup);
for (uint32_t i = 0; i < geometryDataGroup->geometries.count; i++) {
if (geometryDataGroup->geometries[i]->positions.count > 2) {
meshes.push(new WgMeshData());
meshes.last()->update(context, geometryDataGroup->geometries[i]);
}
}
};
void WgMeshDataGroup::release(WgContext& context)
{
for (uint32_t i = 0; i < meshes.count; i++)
meshes[i]->release(context);
meshes.clear();
};
//***********************************************************************
// WgImageData
//***********************************************************************
void WgImageData::update(WGPUDevice device, WGPUQueue queue, Surface* surface)
void WgImageData::update(WgContext& context, Surface* surface)
{
release();
release(context);
assert(surface);
// sampler descriptor
WGPUSamplerDescriptor samplerDesc{};
samplerDesc.nextInChain = nullptr;
@ -139,8 +158,8 @@ void WgImageData::update(WGPUDevice device, WGPUQueue queue, Surface* surface)
samplerDesc.lodMaxClamp = 32.0f;
samplerDesc.compare = WGPUCompareFunction_Undefined;
samplerDesc.maxAnisotropy = 1;
mSampler = wgpuDeviceCreateSampler(device, &samplerDesc);
assert(mSampler);
sampler = wgpuDeviceCreateSampler(context.device, &samplerDesc);
assert(sampler);
// texture descriptor
WGPUTextureDescriptor textureDesc{};
textureDesc.nextInChain = nullptr;
@ -153,8 +172,8 @@ void WgImageData::update(WGPUDevice device, WGPUQueue queue, Surface* surface)
textureDesc.sampleCount = 1;
textureDesc.viewFormatCount = 0;
textureDesc.viewFormats = nullptr;
mTexture = wgpuDeviceCreateTexture(device, &textureDesc);
assert(mTexture);
texture = wgpuDeviceCreateTexture(context.device, &textureDesc);
assert(texture);
// texture view descriptor
WGPUTextureViewDescriptor textureViewDesc{};
textureViewDesc.nextInChain = nullptr;
@ -166,12 +185,12 @@ void WgImageData::update(WGPUDevice device, WGPUQueue queue, Surface* surface)
textureViewDesc.baseArrayLayer = 0;
textureViewDesc.arrayLayerCount = 1;
textureViewDesc.aspect = WGPUTextureAspect_All;
mTextureView = wgpuTextureCreateView(mTexture, &textureViewDesc);
assert(mTextureView);
textureView = wgpuTextureCreateView(texture, &textureViewDesc);
assert(textureView);
// update texture data
WGPUImageCopyTexture imageCopyTexture{};
imageCopyTexture.nextInChain = nullptr;
imageCopyTexture.texture = mTexture;
imageCopyTexture.texture = texture;
imageCopyTexture.mipLevel = 0;
imageCopyTexture.origin = { 0, 0, 0 };
imageCopyTexture.aspect = WGPUTextureAspect_All;
@ -184,403 +203,129 @@ void WgImageData::update(WGPUDevice device, WGPUQueue queue, Surface* surface)
writeSize.width = surface->w;
writeSize.height = surface->h;
writeSize.depthOrArrayLayers = 1;
wgpuQueueWriteTexture(queue, &imageCopyTexture, surface->data, 4 * surface->w * surface->h, &textureDataLayout, &writeSize);
}
wgpuQueueWriteTexture(context.queue, &imageCopyTexture, surface->data, 4 * surface->w * surface->h, &textureDataLayout, &writeSize);
};
void WgImageData::release()
void WgImageData::release(WgContext& context)
{
if (mTexture) {
wgpuTextureDestroy(mTexture);
wgpuTextureRelease(mTexture);
mTexture = nullptr;
if (textureView) {
wgpuTextureViewRelease(textureView);
textureView = nullptr;
}
if (mTextureView) {
wgpuTextureViewRelease(mTextureView);
mTextureView = nullptr;
}
if (mSampler) {
wgpuSamplerRelease(mSampler);
mSampler = nullptr;
if (texture) {
wgpuTextureDestroy(texture);
wgpuTextureRelease(texture);
texture = nullptr;
}
if (sampler) {
wgpuSamplerRelease(sampler);
sampler = nullptr;
}
};
//***********************************************************************
// WgRenderDataShapeSettings
// WgRenderSettings
//***********************************************************************
void WgRenderDataShapeSettings::update(WGPUDevice device, WGPUQueue queue,
const Fill* fill, const uint8_t* color,
const RenderUpdateFlag flags)
void WgRenderSettings::update(WgContext& context, const Fill* fill, const uint8_t* color, const RenderUpdateFlag flags)
{
// setup fill properties
if ((flags & (RenderUpdateFlag::Gradient)) && fill) {
// setup linear fill properties
if (fill->identifier() == TVG_CLASS_ID_LINEAR) {
WgShaderTypeLinearGradient linearGradient((LinearGradient*)fill);
mBindGroupLinear.initialize(device, queue, linearGradient);
mFillType = WgRenderDataShapeFillType::Linear;
bindGroupLinear.initialize(context.device, context.queue, linearGradient);
fillType = WgRenderSettingsType::Linear;
} else if (fill->identifier() == TVG_CLASS_ID_RADIAL) {
WgShaderTypeRadialGradient radialGradient((RadialGradient*)fill);
mBindGroupRadial.initialize(device, queue, radialGradient);
mFillType = WgRenderDataShapeFillType::Radial;
bindGroupRadial.initialize(context.device, context.queue, radialGradient);
fillType = WgRenderSettingsType::Radial;
}
} else if ((flags & (RenderUpdateFlag::Color)) && !fill) {
WgShaderTypeSolidColor solidColor(color);
mBindGroupSolid.initialize(device, queue, solidColor);
mFillType = WgRenderDataShapeFillType::Solid;
}
bindGroupSolid.initialize(context.device, context.queue, solidColor);
fillType = WgRenderSettingsType::Solid;
}
};
void WgRenderDataShapeSettings::release()
void WgRenderSettings::release(WgContext& context)
{
mBindGroupSolid.release();
mBindGroupLinear.release();
mBindGroupRadial.release();
}
bindGroupSolid.release();
bindGroupLinear.release();
bindGroupRadial.release();
};
//***********************************************************************
// WgRenderDataPaint
//***********************************************************************
void WgRenderDataPaint::release(WgContext& context)
{
bindGroupPaint.release();
};
//***********************************************************************
// WgRenderDataShape
//***********************************************************************
void WgRenderDataShape::release()
void WgRenderDataShape::updateMeshes(WgContext &context, const RenderShape &rshape)
{
releaseRenderData();
mImageData.release();
mBindGroupPaint.release();
mRenderSettingsShape.release();
mRenderSettingsStroke.release();
mBindGroupPicture.release();
releaseMeshes(context);
// update shapes geometry
WgGeometryDataGroup shapes;
shapes.tesselate(rshape);
meshGroupShapes.update(context, &shapes);
// update shapes bbox
WgPoint pmin{}, pmax{};
shapes.getBBox(pmin, pmax);
WgGeometryData box;
box.appendBox(pmin, pmax);
meshBBoxShapes.update(context, &box);
// update strokes geometry
if (rshape.stroke) {
WgGeometryDataGroup strokes;
strokes.stroke(rshape);
strokes.getBBox(pmin, pmax);
meshGroupStrokes.update(context, &strokes);
// update strokes bbox
WgPoint pmin{}, pmax{};
strokes.getBBox(pmin, pmax);
WgGeometryData box;
box.appendBox(pmin, pmax);
meshBBoxStrokes.update(context, &box);
}
}
void WgRenderDataShape::releaseRenderData()
void WgRenderDataShape::releaseMeshes(WgContext &context)
{
for (uint32_t i = 0; i < mGeometryDataImage.count; i++) {
mGeometryDataImage[i]->release();
delete mGeometryDataImage[i];
}
for (uint32_t i = 0; i < mGeometryDataStroke.count; i++) {
mGeometryDataStroke[i]->release();
delete mGeometryDataStroke[i];
}
mGeometryDataStroke.clear();
for (uint32_t i = 0; i < mGeometryDataShape.count; i++) {
mGeometryDataShape[i]->release();
delete mGeometryDataShape[i];
}
mGeometryDataShape.clear();
meshBBoxStrokes.release(context);
meshBBoxShapes.release(context);
meshGroupStrokes.release(context);
meshGroupShapes.release(context);
}
void WgRenderDataShape::tesselate(WGPUDevice device, WGPUQueue queue, Surface* surface, const RenderMesh* mesh)
void WgRenderDataShape::release(WgContext& context)
{
// create image geometry data
Array<WgPoint> vertexList;
Array<WgPoint> texCoordsList;
Array<uint32_t> indexList;
releaseMeshes(context);
renderSettingsStroke.release(context);
renderSettingsShape.release(context);
WgRenderDataPaint::release(context);
};
if (mesh && mesh->triangleCnt) {
vertexList.reserve(mesh->triangleCnt * 3);
texCoordsList.reserve(mesh->triangleCnt * 3);
indexList.reserve(mesh->triangleCnt * 3);
for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
vertexList.push(mesh->triangles[i].vertex[0].pt);
vertexList.push(mesh->triangles[i].vertex[1].pt);
vertexList.push(mesh->triangles[i].vertex[2].pt);
texCoordsList.push(mesh->triangles[i].vertex[0].uv);
texCoordsList.push(mesh->triangles[i].vertex[1].uv);
texCoordsList.push(mesh->triangles[i].vertex[2].uv);
indexList.push(i*3 + 0);
indexList.push(i*3 + 1);
indexList.push(i*3 + 2);
}
} else {
vertexList.push({ 0.0f, 0.0f });
vertexList.push({ (float)surface->w, 0.0f });
vertexList.push({ (float)surface->w, (float)surface->h });
vertexList.push({ 0.0f, (float)surface->h });
texCoordsList.push({0.0f, 0.0f});
texCoordsList.push({1.0f, 0.0f});
texCoordsList.push({1.0f, 1.0f});
texCoordsList.push({0.0f, 1.0f});
indexList.push(0);
indexList.push(1);
indexList.push(2);
indexList.push(0);
indexList.push(2);
indexList.push(3);
}
//***********************************************************************
// WgRenderDataPicture
//***********************************************************************
// create geometry data for image
WgGeometryData* geometryData = new WgGeometryData();
geometryData->update(device, queue,
(float *)vertexList.data, (float *)texCoordsList.data, vertexList.count,
indexList.data, indexList.count);
mGeometryDataImage.push(geometryData);
// update image data
mImageData.update(device, queue, surface);
}
void WgRenderDataShape::tesselate(WGPUDevice device, WGPUQueue queue, const RenderShape& rshape)
void WgRenderDataPicture::release(WgContext& context)
{
Array<WgVertexList*> outlines{};
decodePath(rshape, outlines);
// create geometry data for fill for each outline
for (uint32_t i = 0; i < outlines.count; i++) {
auto outline = outlines[i];
// append shape if it can create at least one triangle
if (outline->mVertexList.count > 2) {
outline->computeTriFansIndexes();
// create geometry data for fill using triangle fan indexes
WgGeometryData* geometryData = new WgGeometryData();
geometryData->update(device, queue, outline);
mGeometryDataShape.push(geometryData);
}
meshData.release(context);
imageData.release(context);
bindGroupPicture.release();
WgRenderDataPaint::release(context);
}
for (uint32_t i = 0; i < outlines.count; i++)
delete outlines[i];
}
// TODO: separate to entity
void WgRenderDataShape::stroke(WGPUDevice device, WGPUQueue queue, const RenderShape& rshape)
{
if (!rshape.stroke) return;
// TODO: chnage to shared_ptrs
Array<WgVertexList*> outlines{};
decodePath(rshape, outlines);
WgVertexList strokes;
if (rshape.stroke->dashPattern) {
Array<WgVertexList*> segments;
strokeSegments(rshape, outlines, segments);
strokeSublines(rshape, segments, strokes);
for (uint32_t i = 0; i < segments.count; i++)
delete segments[i];
} else
strokeSublines(rshape, outlines, strokes);
// append shape if it can create at least one triangle
// TODO: create single geometry data for strokes pere shape
if (strokes.mIndexList.count > 2) {
WgGeometryData* geometryData = new WgGeometryData();
geometryData->initialize(device);
geometryData->update(device, queue, &strokes);
mGeometryDataStroke.push(geometryData);
}
for (uint32_t i = 0; i < outlines.count; i++)
delete outlines[i];
}
void WgRenderDataShape::decodePath(const RenderShape& rshape, Array<WgVertexList*>& outlines)
{
size_t pntIndex = 0;
for (uint32_t cmdIndex = 0; cmdIndex < rshape.path.cmds.count; cmdIndex++) {
PathCommand cmd = rshape.path.cmds[cmdIndex];
if (cmd == PathCommand::MoveTo) {
outlines.push(new WgVertexList);
auto outline = outlines.last();
outline->mVertexList.push(rshape.path.pts[pntIndex]);
pntIndex++;
} else if (cmd == PathCommand::LineTo) {
auto outline = outlines.last();
if (outline)
outline->mVertexList.push(rshape.path.pts[pntIndex]);
pntIndex++;
} else if (cmd == PathCommand::Close) {
auto outline = outlines.last();
if ((outline) && (outline->mVertexList.count > 0))
outline->mVertexList.push(outline->mVertexList[0]);
} else if (cmd == PathCommand::CubicTo) {
auto outline = outlines.last();
if ((outline) && (outline->mVertexList.count > 0))
outline->appendCubic(
rshape.path.pts[pntIndex + 0],
rshape.path.pts[pntIndex + 1],
rshape.path.pts[pntIndex + 2]
);
pntIndex += 3;
}
}
}
void WgRenderDataShape::strokeSegments(const RenderShape& rshape, Array<WgVertexList*>& outlines, Array<WgVertexList*>& segments)
{
for (uint32_t i = 0; i < outlines.count; i++) {
auto& vlist = outlines[i]->mVertexList;
// append single point segment
if (vlist.count == 1) {
auto segment = new WgVertexList();
segment->mVertexList.push(vlist.last());
segments.push(segment);
}
if (vlist.count >= 2) {
uint32_t icurr = 1;
uint32_t ipatt = 0;
WgPoint vcurr = vlist[0];
while (icurr < vlist.count) {
if (ipatt % 2 == 0) {
segments.push(new WgVertexList());
segments.last()->mVertexList.push(vcurr);
}
float lcurr = rshape.stroke->dashPattern[ipatt];
while ((icurr < vlist.count) && (vlist[icurr].dist(vcurr) < lcurr)) {
lcurr -= vlist[icurr].dist(vcurr);
vcurr = vlist[icurr];
icurr++;
if (ipatt % 2 == 0) segments.last()->mVertexList.push(vcurr);
}
if (icurr < vlist.count) {
vcurr = vcurr + (vlist[icurr] - vlist[icurr-1]).normal() * lcurr;
if (ipatt % 2 == 0) segments.last()->mVertexList.push(vcurr);
}
ipatt = (ipatt + 1) % rshape.stroke->dashCnt;
}
}
}
}
void WgRenderDataShape::strokeSublines(const RenderShape& rshape, Array<WgVertexList*>& outlines, WgVertexList& strokes)
{
float wdt = rshape.stroke->width / 2;
for (uint32_t i = 0; i < outlines.count; i++) {
auto outline = outlines[i];
// single point sub-path
if (outline->mVertexList.count == 1) {
if (rshape.stroke->cap == StrokeCap::Round) {
strokes.appendCircle(outline->mVertexList[0], wdt);
} else if (rshape.stroke->cap == StrokeCap::Butt) {
// for zero length sub-paths no stroke is rendered
} else if (rshape.stroke->cap == StrokeCap::Square) {
strokes.appendRect(
outline->mVertexList[0] + WgPoint(+wdt, +wdt),
outline->mVertexList[0] + WgPoint(+wdt, -wdt),
outline->mVertexList[0] + WgPoint(-wdt, +wdt),
outline->mVertexList[0] + WgPoint(-wdt, -wdt)
);
}
}
// single line sub-path
if (outline->mVertexList.count == 2) {
WgPoint v0 = outline->mVertexList[0];
WgPoint v1 = outline->mVertexList[1];
WgPoint dir0 = (v1 - v0).normal();
WgPoint nrm0 = WgPoint{ -dir0.y, +dir0.x };
if (rshape.stroke->cap == StrokeCap::Round) {
strokes.appendRect(
v0 - nrm0 * wdt, v0 + nrm0 * wdt,
v1 - nrm0 * wdt, v1 + nrm0 * wdt);
strokes.appendCircle(outline->mVertexList[0], wdt);
strokes.appendCircle(outline->mVertexList[1], wdt);
} else if (rshape.stroke->cap == StrokeCap::Butt) {
strokes.appendRect(
v0 - nrm0 * wdt, v0 + nrm0 * wdt,
v1 - nrm0 * wdt, v1 + nrm0 * wdt
);
} else if (rshape.stroke->cap == StrokeCap::Square) {
strokes.appendRect(
v0 - nrm0 * wdt - dir0 * wdt, v0 + nrm0 * wdt - dir0 * wdt,
v1 - nrm0 * wdt + dir0 * wdt, v1 + nrm0 * wdt + dir0 * wdt
);
}
}
// multi-lined sub-path
if (outline->mVertexList.count > 2) {
// append first cap
WgPoint v0 = outline->mVertexList[0];
WgPoint v1 = outline->mVertexList[1];
WgPoint dir0 = (v1 - v0).normal();
WgPoint nrm0 = WgPoint{ -dir0.y, +dir0.x };
if (rshape.stroke->cap == StrokeCap::Round) {
strokes.appendCircle(v0, wdt);
} else if (rshape.stroke->cap == StrokeCap::Butt) {
// no cap needed
} else if (rshape.stroke->cap == StrokeCap::Square) {
strokes.appendRect(
v0 - nrm0 * wdt - dir0 * wdt,
v0 + nrm0 * wdt - dir0 * wdt,
v0 - nrm0 * wdt,
v0 + nrm0 * wdt
);
}
// append last cap
v0 = outline->mVertexList[outline->mVertexList.count - 2];
v1 = outline->mVertexList[outline->mVertexList.count - 1];
dir0 = (v1 - v0).normal();
nrm0 = WgPoint{ -dir0.y, +dir0.x };
if (rshape.stroke->cap == StrokeCap::Round) {
strokes.appendCircle(v1, wdt);
} else if (rshape.stroke->cap == StrokeCap::Butt) {
// no cap needed
} else if (rshape.stroke->cap == StrokeCap::Square) {
strokes.appendRect(
v1 - nrm0 * wdt,
v1 + nrm0 * wdt,
v1 - nrm0 * wdt + dir0 * wdt,
v1 + nrm0 * wdt + dir0 * wdt
);
}
// append sub-lines
for (uint32_t j = 0; j < outline->mVertexList.count - 1; j++) {
WgPoint v0 = outline->mVertexList[j + 0];
WgPoint v1 = outline->mVertexList[j + 1];
WgPoint dir = (v1 - v0).normal();
WgPoint nrm { -dir.y, +dir.x };
strokes.appendRect(
v0 - nrm * wdt,
v0 + nrm * wdt,
v1 - nrm * wdt,
v1 + nrm * wdt
);
}
// append joints (TODO: separate by joint types)
for (uint32_t j = 1; j < outline->mVertexList.count - 1; j++) {
WgPoint v0 = outline->mVertexList[j - 1];
WgPoint v1 = outline->mVertexList[j + 0];
WgPoint v2 = outline->mVertexList[j + 1];
WgPoint dir0 = (v1 - v0).normal();
WgPoint dir1 = (v1 - v2).normal();
WgPoint nrm0 { -dir0.y, +dir0.x };
WgPoint nrm1 { +dir1.y, -dir1.x };
if (rshape.stroke->join == StrokeJoin::Round) {
strokes.appendCircle(v1, wdt);
} else if (rshape.stroke->join == StrokeJoin::Bevel) {
strokes.appendRect(
v1 - nrm0 * wdt, v1 - nrm1 * wdt,
v1 + nrm1 * wdt, v1 + nrm0 * wdt
);
} else if (rshape.stroke->join == StrokeJoin::Miter) {
WgPoint nrm = (dir0 + dir1).normal();
float cosine = nrm.dot(nrm0);
if ((cosine != 0.0f) && (abs(wdt / cosine) <= rshape.stroke->miterlimit * 2)) {
strokes.appendRect(v1 + nrm * (wdt / cosine), v1 + nrm0 * wdt, v1 + nrm1 * wdt, v1);
strokes.appendRect(v1 - nrm * (wdt / cosine), v1 - nrm0 * wdt, v1 - nrm1 * wdt, v1);
} else {
strokes.appendRect(
v1 - nrm0 * wdt, v1 - nrm1 * wdt,
v1 + nrm1 * wdt, v1 + nrm0 * wdt);
}
}
}
}
}
}
#endif //_TVG_WG_RENDER_DATA_H_

View file

@ -20,95 +20,82 @@
* SOFTWARE.
*/
#ifndef _TVG_WG_RENDER_DATA_H_
#define _TVG_WG_RENDER_DATA_H_
#include "tvgWgPipelines.h"
#include "tvgWgGeometry.h"
struct WgGeometryData
{
WGPUBuffer mBufferVertex{};
WGPUBuffer mBufferTexCoords{};
WGPUBuffer mBufferIndex{};
size_t mVertexCount{};
size_t mIndexCount{};
struct WgMeshData {
WGPUBuffer bufferPosition{};
WGPUBuffer bufferTexCoord{};
WGPUBuffer bufferIndex{};
size_t vertexCount{};
size_t indexCount{};
WgGeometryData() {}
virtual ~WgGeometryData() { release(); }
void initialize(WGPUDevice device) {};
void draw(WGPURenderPassEncoder renderPassEncoder);
void drawImage(WGPURenderPassEncoder renderPassEncoder);
void update(WGPUDevice device, WGPUQueue queue, WgVertexList* vertexList);
void update(WGPUDevice device, WGPUQueue queue, float* vertexData, size_t vertexCount, uint32_t* indexData, size_t indexCount);
void update(WGPUDevice device, WGPUQueue queue, float* vertexData, float* texCoordsData, size_t vertexCount, uint32_t* indexData, size_t indexCount);
void release();
void update(WgContext& context, WgGeometryData* geometryData);
void release(WgContext& context);
};
struct WgImageData
struct WgMeshDataGroup {
Array<WgMeshData*> meshes{};
void update(WgContext& context, WgGeometryDataGroup* geometryDataGroup);
void release(WgContext& context);
};
struct WgImageData {
WGPUSampler sampler{};
WGPUTexture texture{};
WGPUTextureView textureView{};
void update(WgContext& context, Surface* surface);
void release(WgContext& context);
};
enum class WgRenderSettingsType { None = 0, Solid = 1, Linear = 2, Radial = 3 };
struct WgRenderSettings
{
WGPUSampler mSampler{};
WGPUTexture mTexture{};
WGPUTextureView mTextureView{};
WgBindGroupSolidColor bindGroupSolid{};
WgBindGroupLinearGradient bindGroupLinear{};
WgBindGroupRadialGradient bindGroupRadial{};
WgRenderSettingsType fillType{};
WgImageData() {}
virtual ~WgImageData() { release(); }
void initialize(WGPUDevice device) {};
void update(WGPUDevice device, WGPUQueue queue, Surface* surface);
void release();
void update(WgContext& context, const Fill* fill, const uint8_t* color, const RenderUpdateFlag flags);
void release(WgContext& context);
};
class WgRenderData
struct WgRenderDataPaint
{
public:
virtual void initialize(WGPUDevice device) {};
virtual void release() = 0;
WgBindGroupPaint bindGroupPaint{};
virtual void release(WgContext& context);
virtual uint32_t identifier() { return TVG_CLASS_ID_UNDEFINED; };
};
enum class WgRenderDataShapeFillType { None = 0, Solid = 1, Linear = 2, Radial = 3 };
struct WgRenderDataShapeSettings
struct WgRenderDataShape: public WgRenderDataPaint
{
WgBindGroupSolidColor mBindGroupSolid{};
WgBindGroupLinearGradient mBindGroupLinear{};
WgBindGroupRadialGradient mBindGroupRadial{};
WgRenderDataShapeFillType mFillType{}; // Default: None
WgRenderSettings renderSettingsShape{};
WgRenderSettings renderSettingsStroke{};
WgMeshDataGroup meshGroupShapes{};
WgMeshDataGroup meshGroupStrokes{};
WgMeshData meshBBoxShapes{};
WgMeshData meshBBoxStrokes{};
// update render shape settings defined by flags and fill settings
void update(WGPUDevice device, WGPUQueue queue,
const Fill* fill, const uint8_t* color, const RenderUpdateFlag flags);
void release();
void updateMeshes(WgContext& context, const RenderShape& rshape);
void releaseMeshes(WgContext& context);
void release(WgContext& context) override;
uint32_t identifier() override { return TVG_CLASS_ID_SHAPE; };
};
class WgRenderDataShape: public WgRenderData
struct WgRenderDataPicture: public WgRenderDataPaint
{
public:
// geometry data for shapes, strokes and image
Array<WgGeometryData*> mGeometryDataShape;
Array<WgGeometryData*> mGeometryDataStroke;
Array<WgGeometryData*> mGeometryDataImage;
WgImageData mImageData;
WgBindGroupPicture bindGroupPicture{};
WgImageData imageData{};
WgMeshData meshData{};
// shader settings
WgBindGroupPaint mBindGroupPaint;
WgRenderDataShapeSettings mRenderSettingsShape;
WgRenderDataShapeSettings mRenderSettingsStroke;
WgBindGroupPicture mBindGroupPicture;
public:
WgRenderDataShape() {}
void release() override;
void releaseRenderData();
void tesselate(WGPUDevice device, WGPUQueue queue, Surface* surface, const RenderMesh* mesh);
void tesselate(WGPUDevice device, WGPUQueue queue, const RenderShape& rshape);
void stroke(WGPUDevice device, WGPUQueue queue, const RenderShape& rshape);
private:
void decodePath(const RenderShape& rshape, Array<WgVertexList*>& outlines);
void strokeSegments(const RenderShape& rshape, Array<WgVertexList*>& outlines, Array<WgVertexList*>& segments);
void strokeSublines(const RenderShape& rshape, Array<WgVertexList*>& outlines, WgVertexList& strokes);
void update(WgContext& context);
void release(WgContext& context) override;
uint32_t identifier() override { return TVG_CLASS_ID_PICTURE; };
};
#endif //_TVG_WG_RENDER_DATA_H_

View file

@ -22,9 +22,9 @@
#include "tvgWgRenderTarget.h"
void WgRenderTarget::initialize(WGPUDevice device, WGPUQueue queue, WgPipelines& pipelines, uint32_t w, uint32_t h)
void WgRenderTarget::initialize(WgContext& context, WgPipelines& pipelines, uint32_t w, uint32_t h)
{
release();
release(context);
// sampler descriptor
WGPUSamplerDescriptor samplerDesc{};
samplerDesc.nextInChain = nullptr;
@ -39,7 +39,7 @@ void WgRenderTarget::initialize(WGPUDevice device, WGPUQueue queue, WgPipelines&
samplerDesc.lodMaxClamp = 32.0f;
samplerDesc.compare = WGPUCompareFunction_Undefined;
samplerDesc.maxAnisotropy = 1;
mSampler = wgpuDeviceCreateSampler(device, &samplerDesc);
mSampler = wgpuDeviceCreateSampler(context.device, &samplerDesc);
assert(mSampler);
// texture descriptor
WGPUTextureDescriptor textureDescColor{};
@ -53,7 +53,7 @@ void WgRenderTarget::initialize(WGPUDevice device, WGPUQueue queue, WgPipelines&
textureDescColor.sampleCount = 1;
textureDescColor.viewFormatCount = 0;
textureDescColor.viewFormats = nullptr;
mTextureColor = wgpuDeviceCreateTexture(device, &textureDescColor);
mTextureColor = wgpuDeviceCreateTexture(context.device, &textureDescColor);
assert(mTextureColor);
// texture view descriptor
WGPUTextureViewDescriptor textureViewDescColor{};
@ -80,7 +80,7 @@ void WgRenderTarget::initialize(WGPUDevice device, WGPUQueue queue, WgPipelines&
textureDescStencil.sampleCount = 1;
textureDescStencil.viewFormatCount = 0;
textureDescStencil.viewFormats = nullptr;
mTextureStencil = wgpuDeviceCreateTexture(device, &textureDescStencil);
mTextureStencil = wgpuDeviceCreateTexture(context.device, &textureDescStencil);
assert(mTextureStencil);
// texture view descriptor
WGPUTextureViewDescriptor textureViewDescStencil{};
@ -97,22 +97,14 @@ void WgRenderTarget::initialize(WGPUDevice device, WGPUQueue queue, WgPipelines&
assert(mTextureViewStencil);
// initialize window binding groups
WgShaderTypeMat4x4f viewMat(w, h);
mBindGroupCanvasWnd.initialize(device, queue, viewMat);
WgShaderTypeMat4x4f modelMat;
WgShaderTypeBlendSettings blendSettings(ColorSpace::ABGR8888, 255);
mBindGroupPaintWnd.initialize(device, queue, modelMat, blendSettings);
// update pipeline geometry data
WgVertexList wnd;
wnd.appendRect(WgPoint(0.0f, 0.0f), WgPoint(w, 0.0f), WgPoint(0.0f, h), WgPoint(w, h));
mGeometryDataWnd.update(device, queue, &wnd);
mBindGroupCanvasWnd.initialize(context.device, context.queue, viewMat);
mPipelines = &pipelines;
}
void WgRenderTarget::release()
void WgRenderTarget::release(WgContext& context)
{
mBindGroupCanvasWnd.release();
mBindGroupPaintWnd.release();
mGeometryDataWnd.release();
if (mTextureStencil) {
wgpuTextureDestroy(mTextureStencil);
wgpuTextureRelease(mTextureStencil);
@ -131,11 +123,13 @@ void WgRenderTarget::release()
mSampler = nullptr;
}
void WgRenderTarget::beginRenderPass(WGPUCommandEncoder commandEncoder)
{
beginRenderPass(commandEncoder, mTextureViewColor);
}
void WgRenderTarget::beginRenderPass(WGPUCommandEncoder commandEncoder, WGPUTextureView colorAttachement)
{
assert(!mRenderPassEncoder);
@ -172,6 +166,7 @@ void WgRenderTarget::beginRenderPass(WGPUCommandEncoder commandEncoder, WGPUText
mRenderPassEncoder = wgpuCommandEncoderBeginRenderPass(commandEncoder, &renderPassDesc);
}
void WgRenderTarget::endRenderPass()
{
assert(mRenderPassEncoder);
@ -180,66 +175,67 @@ void WgRenderTarget::endRenderPass()
mRenderPassEncoder = nullptr;
}
void WgRenderTarget::renderShape(WgRenderDataShape* renderData)
{
assert(renderData);
assert(mRenderPassEncoder);
// draw shape geometry
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
for (uint32_t i = 0; i < renderData->mGeometryDataShape.count; i++) {
for (uint32_t i = 0; i < renderData->meshGroupShapes.meshes.count; i++) {
// draw to stencil (first pass)
mPipelines->mPipelineFillShape.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->mBindGroupPaint);
renderData->mGeometryDataShape[i]->draw(mRenderPassEncoder);
mPipelines->mPipelineFillShape.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint);
renderData->meshGroupShapes.meshes[i]->draw(mRenderPassEncoder);
// fill shape (second pass)
WgRenderDataShapeSettings& settings = renderData->mRenderSettingsShape;
if (settings.mFillType == WgRenderDataShapeFillType::Solid)
mPipelines->mPipelineSolid.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->mBindGroupPaint, settings.mBindGroupSolid);
else if (settings.mFillType == WgRenderDataShapeFillType::Linear)
mPipelines->mPipelineLinear.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->mBindGroupPaint, settings.mBindGroupLinear);
else if (settings.mFillType == WgRenderDataShapeFillType::Radial)
mPipelines->mPipelineRadial.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->mBindGroupPaint, settings.mBindGroupRadial);
mGeometryDataWnd.draw(mRenderPassEncoder);
WgRenderSettings& settings = renderData->renderSettingsShape;
if (settings.fillType == WgRenderSettingsType::Solid)
mPipelines->mPipelineSolid.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupSolid);
else if (settings.fillType == WgRenderSettingsType::Linear)
mPipelines->mPipelineLinear.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupLinear);
else if (settings.fillType == WgRenderSettingsType::Radial)
mPipelines->mPipelineRadial.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupRadial);
renderData->meshBBoxShapes.draw(mRenderPassEncoder);
}
}
void WgRenderTarget::renderStroke(WgRenderDataShape* renderData)
{
assert(renderData);
assert(mRenderPassEncoder);
// draw stroke geometry
if (renderData->mGeometryDataStroke.count > 0) {
if (renderData->meshGroupStrokes.meshes.count > 0) {
// draw strokes to stencil (first pass)
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 255);
for (uint32_t i = 0; i < renderData->mGeometryDataStroke.count; i++) {
mPipelines->mPipelineFillStroke.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->mBindGroupPaint);
renderData->mGeometryDataStroke[i]->draw(mRenderPassEncoder);
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
mPipelines->mPipelineFillStroke.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint);
renderData->meshGroupStrokes.meshes[i]->draw(mRenderPassEncoder);
}
// fill shape (second pass)
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
WgRenderDataShapeSettings& settings = renderData->mRenderSettingsStroke;
if (settings.mFillType == WgRenderDataShapeFillType::Solid)
mPipelines->mPipelineSolid.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->mBindGroupPaint, settings.mBindGroupSolid);
else if (settings.mFillType == WgRenderDataShapeFillType::Linear)
mPipelines->mPipelineLinear.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->mBindGroupPaint, settings.mBindGroupLinear);
else if (settings.mFillType == WgRenderDataShapeFillType::Radial)
mPipelines->mPipelineRadial.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->mBindGroupPaint, settings.mBindGroupRadial);
mGeometryDataWnd.draw(mRenderPassEncoder);
WgRenderSettings& settings = renderData->renderSettingsStroke;
if (settings.fillType == WgRenderSettingsType::Solid)
mPipelines->mPipelineSolid.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupSolid);
else if (settings.fillType == WgRenderSettingsType::Linear)
mPipelines->mPipelineLinear.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupLinear);
else if (settings.fillType == WgRenderSettingsType::Radial)
mPipelines->mPipelineRadial.use(mRenderPassEncoder, mBindGroupCanvasWnd, renderData->bindGroupPaint, settings.bindGroupRadial);
renderData->meshBBoxStrokes.draw(mRenderPassEncoder);
}
}
void WgRenderTarget::renderImage(WgRenderDataShape* renderData)
void WgRenderTarget::renderPicture(WgRenderDataPicture* renderData)
{
assert(renderData);
assert(mRenderPassEncoder);
if (renderData->mGeometryDataImage.count > 0) {
if (renderData->meshData.bufferTexCoord) {
wgpuRenderPassEncoderSetStencilReference(mRenderPassEncoder, 0);
for (uint32_t j = 0; j < renderData->mGeometryDataImage.count; j++) {
mPipelines->mPipelineImage.use(
mRenderPassEncoder,
mBindGroupCanvasWnd,
renderData->mBindGroupPaint,
renderData->mBindGroupPicture);
renderData->mGeometryDataImage[j]->drawImage(mRenderPassEncoder);
}
renderData->bindGroupPaint,
renderData->bindGroupPicture);
renderData->meshData.drawImage(mRenderPassEncoder);
}
}

View file

@ -31,8 +31,6 @@ private:
WGPURenderPassEncoder mRenderPassEncoder{};
// fill and blit data
WgBindGroupCanvas mBindGroupCanvasWnd;
WgBindGroupPaint mBindGroupPaintWnd;
WgGeometryData mGeometryDataWnd;
// gpu buffers
WGPUSampler mSampler{};
WGPUTexture mTextureColor{};
@ -41,8 +39,8 @@ private:
WGPUTextureView mTextureViewStencil{};
WgPipelines* mPipelines{}; // external handle
public:
void initialize(WGPUDevice device, WGPUQueue queue, WgPipelines& pipelines, uint32_t w, uint32_t h);
void release();
void initialize(WgContext& context, WgPipelines& pipelines, uint32_t w, uint32_t h);
void release(WgContext& context);
void beginRenderPass(WGPUCommandEncoder commandEncoder, WGPUTextureView colorAttachement);
void beginRenderPass(WGPUCommandEncoder commandEncoder);
@ -50,7 +48,7 @@ public:
void renderShape(WgRenderDataShape* renderData);
void renderStroke(WgRenderDataShape* renderData);
void renderImage(WgRenderDataShape* renderData);
void renderPicture(WgRenderDataPicture* renderData);
};
#endif

View file

@ -60,29 +60,24 @@ RenderData WgRenderer::prepare(const RenderShape& rshape, RenderData data, const
{
// get or create render data shape
auto renderDataShape = (WgRenderDataShape*)data;
if (!renderDataShape) {
if (!renderDataShape)
renderDataShape = new WgRenderDataShape();
renderDataShape->initialize(mContext.device);
}
// update geometry
if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Stroke)) {
renderDataShape->releaseRenderData();
renderDataShape->tesselate(mContext.device, mContext.queue, rshape);
renderDataShape->stroke(mContext.device, mContext.queue, rshape);
}
if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Stroke))
renderDataShape->updateMeshes(mContext, rshape);
// update paint settings
if (flags & (RenderUpdateFlag::Transform | RenderUpdateFlag::Blend)) {
WgShaderTypeMat4x4f modelMat(transform);
WgShaderTypeBlendSettings blendSettings(mTargetSurface.cs, opacity);
renderDataShape->mBindGroupPaint.initialize(mContext.device, mContext.queue, modelMat, blendSettings);
renderDataShape->bindGroupPaint.initialize(mContext.device, mContext.queue, modelMat, blendSettings);
}
// setup fill settings
renderDataShape->mRenderSettingsShape.update(mContext.device, mContext.queue, rshape.fill, rshape.color, flags);
renderDataShape->renderSettingsShape.update(mContext, rshape.fill, rshape.color, flags);
if (rshape.stroke)
renderDataShape->mRenderSettingsStroke.update(mContext.device, mContext.queue, rshape.stroke->fill, rshape.stroke->color, flags);
renderDataShape->renderSettingsStroke.update(mContext, rshape.stroke->fill, rshape.stroke->color, flags);
return renderDataShape;
}
@ -97,30 +92,32 @@ RenderData WgRenderer::prepare(TVG_UNUSED const Array<RenderData>& scene, TVG_UN
RenderData WgRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
{
// get or create render data shape
auto renderDataShape = (WgRenderDataShape*)data;
if (!renderDataShape) {
renderDataShape = new WgRenderDataShape();
renderDataShape->initialize(mContext.device);
}
auto renderDataPicture = (WgRenderDataPicture*)data;
if (!renderDataPicture)
renderDataPicture = new WgRenderDataPicture();
// update paint settings
if (flags & (RenderUpdateFlag::Transform | RenderUpdateFlag::Blend)) {
WgShaderTypeMat4x4f modelMat(transform);
WgShaderTypeBlendSettings blendSettings(surface->cs, opacity);
renderDataShape->mBindGroupPaint.initialize(mContext.device, mContext.queue, modelMat, blendSettings);
renderDataPicture->bindGroupPaint.initialize(mContext.device, mContext.queue, modelMat, blendSettings);
}
// update image data
if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Image)) {
renderDataShape->releaseRenderData();
renderDataShape->tesselate(mContext.device, mContext.queue, surface, mesh);
renderDataShape->mBindGroupPicture.initialize(
WgGeometryData geometryData;
if (mesh->triangleCnt == 0) geometryData.appendImageBox(surface->w, surface->h);
else geometryData.appendMesh(mesh);
renderDataPicture->meshData.release(mContext);
renderDataPicture->meshData.update(mContext, &geometryData);
renderDataPicture->imageData.update(mContext, surface);
renderDataPicture->bindGroupPicture.initialize(
mContext.device, mContext.queue,
renderDataShape->mImageData.mSampler,
renderDataShape->mImageData.mTextureView);
renderDataPicture->imageData.sampler,
renderDataPicture->imageData.textureView);
}
return renderDataShape;
return renderDataPicture;
}
@ -152,8 +149,8 @@ bool WgRenderer::postRender()
bool WgRenderer::dispose(RenderData data)
{
auto renderData = (WgRenderData*)data;
if (renderData) renderData->release();
auto renderData = (WgRenderDataPaint*)data;
if (renderData) renderData->release(mContext);
return true;
}
@ -206,10 +203,13 @@ bool WgRenderer::sync()
// render datas
mRenderTarget.beginRenderPass(commandEncoder, backBufferView);
for (size_t i = 0; i < mRenderDatas.count; i++) {
WgRenderDataShape* renderData = (WgRenderDataShape*)(mRenderDatas[i]);
mRenderTarget.renderShape(renderData);
mRenderTarget.renderStroke(renderData);
mRenderTarget.renderImage(renderData);
WgRenderDataPaint* renderData = (WgRenderDataShape*)(mRenderDatas[i]);
if (renderData->identifier() == TVG_CLASS_ID_SHAPE) {
mRenderTarget.renderShape((WgRenderDataShape *)renderData);
mRenderTarget.renderStroke((WgRenderDataShape *)renderData);
} else if (renderData->identifier() == TVG_CLASS_ID_PICTURE) {
mRenderTarget.renderPicture((WgRenderDataPicture *)renderData);
}
}
mRenderTarget.endRenderPass();
@ -273,7 +273,7 @@ bool WgRenderer::target(void* window, uint32_t w, uint32_t h)
mSwapChain = wgpuDeviceCreateSwapChain(mContext.device, mSurface, &swapChainDesc);
assert(mSwapChain);
mRenderTarget.initialize(mContext.device, mContext.queue, mPipelines, w, h);
mRenderTarget.initialize(mContext, mPipelines, w, h);
return true;
}
@ -283,6 +283,7 @@ Compositor* WgRenderer::target(TVG_UNUSED const RenderRegion& region, TVG_UNUSED
return nullptr;
}
bool WgRenderer::beginComposite(TVG_UNUSED Compositor* cmp, TVG_UNUSED CompositeMethod method, TVG_UNUSED uint8_t opacity)
{
return false;