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

View file

@ -22,21 +22,25 @@
#include "tvgWgGeometry.h" #include "tvgWgGeometry.h"
void WgVertexList::computeTriFansIndexes() //***********************************************************************
// WgGeometryData
//***********************************************************************
void WgGeometryData::computeTriFansIndexes()
{ {
assert(mVertexList.count > 2); if (positions.count <= 2) return;
mIndexList.reserve((mVertexList.count - 2) * 3); indexes.reserve((positions.count - 2) * 3);
for (size_t i = 0; i < mVertexList.count - 2; i++) { for (size_t i = 0; i < positions.count - 2; i++) {
mIndexList.push(0); indexes.push(0);
mIndexList.push(i + 1); indexes.push(i + 1);
mIndexList.push(i + 2); 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; const size_t segs = 16;
for (size_t i = 1; i <= segs; i++) { for (size_t i = 1; i <= segs; i++) {
float t = i / (float)segs; 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 t1 = 3 * (1.0f - t) * (1.0f - t) * t;
float t2 = 3 * (1.0f - t) * t * t; float t2 = 3 * (1.0f - t) * t * t;
float t3 = 1 * 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; appendRect(
mVertexList.push(p0); // +0 { pmin.x, pmin.y},
mVertexList.push(p1); // +1 { pmax.x, pmin.y},
mVertexList.push(p2); // +2 { pmin.x, pmax.y},
mVertexList.push(p3); // +3 { pmax.x, pmax.y});
mIndexList.push(index + 0); };
mIndexList.push(index + 1);
mIndexList.push(index + 2);
mIndexList.push(index + 1);
mIndexList.push(index + 3);
mIndexList.push(index + 2);
}
// TODO: optimize vertex and index count void WgGeometryData::appendRect(WgPoint p0, WgPoint p1, WgPoint p2, WgPoint p3)
void WgVertexList::appendCircle(WgPoint center, float radius)
{ {
uint32_t index = mVertexList.count; uint32_t index = positions.count;
uint32_t nSegments = 32; 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 WgGeometryData::appendCircle(WgPoint center, float radius)
{
uint32_t index = positions.count;
uint32_t nSegments = std::trunc(radius);
for (uint32_t i = 0; i < nSegments; i++) { for (uint32_t i = 0; i < nSegments; i++) {
float angle0 = (float)(i + 0) / nSegments * 3.141593f * 2.0f; float angle0 = (float)(i + 0) / nSegments * (float)M_PI * 2.0f;
float angle1 = (float)(i + 1) / nSegments * 3.141593f * 2.0f; float angle1 = (float)(i + 1) / nSegments * (float)M_PI * 2.0f;
WgPoint p0 = center + WgPoint(sin(angle0) * radius, cos(angle0) * radius); WgPoint p0 = center + WgPoint(sin(angle0) * radius, cos(angle0) * radius);
WgPoint p1 = center + WgPoint(sin(angle1) * radius, cos(angle1) * radius); WgPoint p1 = center + WgPoint(sin(angle1) * radius, cos(angle1) * radius);
mVertexList.push(center); // +0 positions.push(center); // +0
mVertexList.push(p0); // +1 positions.push(p0); // +1
mVertexList.push(p1); // +2 positions.push(p1); // +2
mIndexList.push(index + 0); indexes.push(index + 0);
mIndexList.push(index + 1); indexes.push(index + 1);
mIndexList.push(index + 2); indexes.push(index + 2);
index += 3; index += 3;
} }
};
void WgGeometryData::appendImageBox(float w, float h)
{
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 WgVertexList::close() void WgGeometryDataGroup::tesselate(const RenderShape& rshape)
{ {
if (mVertexList.count > 1) { decodePath(rshape, this);
mVertexList.push(mVertexList[0]); 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 <cassert>
#include "tvgMath.h" #include "tvgMath.h"
#include "tvgCommon.h" #include "tvgRender.h"
#include "tvgArray.h" #include "tvgArray.h"
class WgPoint class WgPoint
@ -75,17 +75,38 @@ public:
} }
}; };
class WgVertexList struct WgGeometryData
{ {
public: Array<WgPoint> positions{};
Array<WgPoint> mVertexList; Array<WgPoint> texCoords{};
Array<uint32_t> mIndexList; Array<uint32_t> indexes{};
// webgpu did not support triangle fans primitives type
// so we can emulate triangle fans using indexing
void computeTriFansIndexes(); void computeTriFansIndexes();
void appendCubic(WgPoint p1, WgPoint p2, WgPoint p3); 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 appendRect(WgPoint p0, WgPoint p1, WgPoint p2, WgPoint p3);
void appendCircle(WgPoint center, float radius); void appendCircle(WgPoint center, float radius);
void appendImageBox(float w, float h);
void appendMesh(const RenderMesh* rmesh);
void close(); 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_ #endif // _TVG_WG_GEOMETRY_H_

View file

@ -1,3 +1,4 @@
/* /*
* Copyright (c) 2023 the ThorVG project. All rights reserved. * Copyright (c) 2023 the ThorVG project. All rights reserved.
@ -20,111 +21,129 @@
* SOFTWARE. * SOFTWARE.
*/ */
#ifndef _TVG_WG_RENDER_DATA_H_
#define _TVG_WG_RENDER_DATA_H_
#include "tvgWgRenderData.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); wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, bufferPosition, 0, vertexCount * sizeof(float) * 2);
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, mBufferIndex, WGPUIndexFormat_Uint32, 0, mIndexCount * sizeof(uint32_t)); wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, bufferIndex, WGPUIndexFormat_Uint32, 0, indexCount * sizeof(uint32_t));
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, mIndexCount, 1, 0, 0, 0); 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, 0, bufferPosition, 0, vertexCount * sizeof(float) * 2);
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 1, mBufferTexCoords, 0, mVertexCount * sizeof(float) * 2); wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 1, bufferTexCoord, 0, vertexCount * sizeof(float) * 2);
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, mBufferIndex, WGPUIndexFormat_Uint32, 0, mIndexCount * sizeof(uint32_t)); wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, bufferIndex, WGPUIndexFormat_Uint32, 0, indexCount * sizeof(uint32_t));
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, mIndexCount, 1, 0, 0, 0); wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, indexCount, 1, 0, 0, 0);
} };
void WgGeometryData::update(WGPUDevice device, WGPUQueue queue, WgVertexList* vertexList) void WgMeshData::update(WgContext& context, WgGeometryData* geometryData){
{ release(context);
update(device, queue, assert(geometryData);
(float *)vertexList->mVertexList.data, // buffer position data create and write
vertexList->mVertexList.count, if(geometryData->positions.count > 0) {
vertexList->mIndexList.data, vertexCount = geometryData->positions.count;
vertexList->mIndexList.count); WGPUBufferDescriptor bufferDesc{};
} bufferDesc.nextInChain = nullptr;
bufferDesc.label = "Buffer position geometry data";
bufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex;
void WgGeometryData::update(WGPUDevice device, WGPUQueue queue, float* vertexData, size_t vertexCount, uint32_t* indexData, size_t indexCount) bufferDesc.size = sizeof(float) * vertexCount * 2; // x, y
{ bufferDesc.mappedAtCreation = false;
release(); bufferPosition = wgpuDeviceCreateBuffer(context.device, &bufferDesc);
assert(bufferPosition);
wgpuQueueWriteBuffer(context.queue, bufferPosition, 0, &geometryData->positions[0], vertexCount * sizeof(float) * 2);
}
// buffer vertex data create and write // buffer vertex data create and write
WGPUBufferDescriptor bufferVertexDesc{}; if(geometryData->texCoords.count > 0) {
bufferVertexDesc.nextInChain = nullptr; WGPUBufferDescriptor bufferDesc{};
bufferVertexDesc.label = "Buffer vertex geometry data"; bufferDesc.nextInChain = nullptr;
bufferVertexDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex; bufferDesc.label = "Buffer tex coords geometry data";
bufferVertexDesc.size = sizeof(float) * vertexCount * 2; // x, y bufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex;
bufferVertexDesc.mappedAtCreation = false; bufferDesc.size = sizeof(float) * vertexCount * 2; // u, v
mBufferVertex = wgpuDeviceCreateBuffer(device, &bufferVertexDesc); bufferDesc.mappedAtCreation = false;
assert(mBufferVertex); bufferTexCoord = wgpuDeviceCreateBuffer(context.device, &bufferDesc);
wgpuQueueWriteBuffer(queue, mBufferVertex, 0, vertexData, vertexCount * sizeof(float) * 2); assert(bufferPosition);
mVertexCount = vertexCount; wgpuQueueWriteBuffer(context.queue, bufferTexCoord, 0, &geometryData->texCoords[0], vertexCount * sizeof(float) * 2);
}
// buffer index data create and write // buffer index data create and write
WGPUBufferDescriptor bufferIndexDesc{}; if(geometryData->indexes.count > 0) {
bufferIndexDesc.nextInChain = nullptr; indexCount = geometryData->indexes.count;
bufferIndexDesc.label = "Buffer index geometry data"; WGPUBufferDescriptor bufferDesc{};
bufferIndexDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index; bufferDesc.nextInChain = nullptr;
bufferIndexDesc.size = sizeof(uint32_t) * indexCount; bufferDesc.label = "Buffer index geometry data";
bufferIndexDesc.mappedAtCreation = false; bufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index;
mBufferIndex = wgpuDeviceCreateBuffer(device, &bufferIndexDesc); bufferDesc.size = sizeof(uint32_t) * indexCount;
assert(mBufferIndex); bufferDesc.mappedAtCreation = false;
wgpuQueueWriteBuffer(queue, mBufferIndex, 0, indexData, indexCount * sizeof(uint32_t)); bufferIndex = wgpuDeviceCreateBuffer(context.device, &bufferDesc);
mIndexCount = indexCount; 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) { void WgMeshData::release(WgContext& context)
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()
{ {
if (mBufferIndex) { if (bufferIndex) {
wgpuBufferDestroy(mBufferIndex); wgpuBufferDestroy(bufferIndex);
wgpuBufferRelease(mBufferIndex); wgpuBufferRelease(bufferIndex);
mBufferIndex = nullptr; bufferIndex = nullptr;
mVertexCount = 0; indexCount = 0;
} }
if (mBufferTexCoords) { if (bufferTexCoord) {
wgpuBufferDestroy(mBufferTexCoords); wgpuBufferDestroy(bufferTexCoord);
wgpuBufferRelease(mBufferTexCoords); wgpuBufferRelease(bufferTexCoord);
mBufferTexCoords = nullptr; bufferTexCoord = nullptr;
} }
if (mBufferVertex) { if (bufferPosition) {
wgpuBufferDestroy(mBufferVertex); wgpuBufferDestroy(bufferPosition);
wgpuBufferRelease(mBufferVertex); wgpuBufferRelease(bufferPosition);
mBufferVertex = nullptr; bufferPosition = nullptr;
mIndexCount = 0; 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 // WgImageData
//*********************************************************************** //***********************************************************************
void WgImageData::update(WGPUDevice device, WGPUQueue queue, Surface* surface) void WgImageData::update(WgContext& context, Surface* surface)
{ {
release(); release(context);
assert(surface);
// sampler descriptor // sampler descriptor
WGPUSamplerDescriptor samplerDesc{}; WGPUSamplerDescriptor samplerDesc{};
samplerDesc.nextInChain = nullptr; samplerDesc.nextInChain = nullptr;
@ -139,8 +158,8 @@ void WgImageData::update(WGPUDevice device, WGPUQueue queue, Surface* surface)
samplerDesc.lodMaxClamp = 32.0f; samplerDesc.lodMaxClamp = 32.0f;
samplerDesc.compare = WGPUCompareFunction_Undefined; samplerDesc.compare = WGPUCompareFunction_Undefined;
samplerDesc.maxAnisotropy = 1; samplerDesc.maxAnisotropy = 1;
mSampler = wgpuDeviceCreateSampler(device, &samplerDesc); sampler = wgpuDeviceCreateSampler(context.device, &samplerDesc);
assert(mSampler); assert(sampler);
// texture descriptor // texture descriptor
WGPUTextureDescriptor textureDesc{}; WGPUTextureDescriptor textureDesc{};
textureDesc.nextInChain = nullptr; textureDesc.nextInChain = nullptr;
@ -153,8 +172,8 @@ void WgImageData::update(WGPUDevice device, WGPUQueue queue, Surface* surface)
textureDesc.sampleCount = 1; textureDesc.sampleCount = 1;
textureDesc.viewFormatCount = 0; textureDesc.viewFormatCount = 0;
textureDesc.viewFormats = nullptr; textureDesc.viewFormats = nullptr;
mTexture = wgpuDeviceCreateTexture(device, &textureDesc); texture = wgpuDeviceCreateTexture(context.device, &textureDesc);
assert(mTexture); assert(texture);
// texture view descriptor // texture view descriptor
WGPUTextureViewDescriptor textureViewDesc{}; WGPUTextureViewDescriptor textureViewDesc{};
textureViewDesc.nextInChain = nullptr; textureViewDesc.nextInChain = nullptr;
@ -166,12 +185,12 @@ void WgImageData::update(WGPUDevice device, WGPUQueue queue, Surface* surface)
textureViewDesc.baseArrayLayer = 0; textureViewDesc.baseArrayLayer = 0;
textureViewDesc.arrayLayerCount = 1; textureViewDesc.arrayLayerCount = 1;
textureViewDesc.aspect = WGPUTextureAspect_All; textureViewDesc.aspect = WGPUTextureAspect_All;
mTextureView = wgpuTextureCreateView(mTexture, &textureViewDesc); textureView = wgpuTextureCreateView(texture, &textureViewDesc);
assert(mTextureView); assert(textureView);
// update texture data // update texture data
WGPUImageCopyTexture imageCopyTexture{}; WGPUImageCopyTexture imageCopyTexture{};
imageCopyTexture.nextInChain = nullptr; imageCopyTexture.nextInChain = nullptr;
imageCopyTexture.texture = mTexture; imageCopyTexture.texture = texture;
imageCopyTexture.mipLevel = 0; imageCopyTexture.mipLevel = 0;
imageCopyTexture.origin = { 0, 0, 0 }; imageCopyTexture.origin = { 0, 0, 0 };
imageCopyTexture.aspect = WGPUTextureAspect_All; imageCopyTexture.aspect = WGPUTextureAspect_All;
@ -184,403 +203,129 @@ void WgImageData::update(WGPUDevice device, WGPUQueue queue, Surface* surface)
writeSize.width = surface->w; writeSize.width = surface->w;
writeSize.height = surface->h; writeSize.height = surface->h;
writeSize.depthOrArrayLayers = 1; 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) { if (textureView) {
wgpuTextureDestroy(mTexture); wgpuTextureViewRelease(textureView);
wgpuTextureRelease(mTexture); textureView = nullptr;
mTexture = nullptr; }
if (texture) {
wgpuTextureDestroy(texture);
wgpuTextureRelease(texture);
texture = nullptr;
} }
if (mTextureView) { if (sampler) {
wgpuTextureViewRelease(mTextureView); wgpuSamplerRelease(sampler);
mTextureView = nullptr; sampler = nullptr;
} }
if (mSampler) { };
wgpuSamplerRelease(mSampler);
mSampler = nullptr;
}
}
//*********************************************************************** //***********************************************************************
// WgRenderDataShapeSettings // WgRenderSettings
//*********************************************************************** //***********************************************************************
void WgRenderDataShapeSettings::update(WGPUDevice device, WGPUQueue queue, void WgRenderSettings::update(WgContext& context, const Fill* fill, const uint8_t* color, const RenderUpdateFlag flags)
const Fill* fill, const uint8_t* color,
const RenderUpdateFlag flags)
{ {
// setup fill properties // setup fill properties
if ((flags & (RenderUpdateFlag::Gradient)) && fill) { if ((flags & (RenderUpdateFlag::Gradient)) && fill) {
// setup linear fill properties // setup linear fill properties
if (fill->identifier() == TVG_CLASS_ID_LINEAR) { if (fill->identifier() == TVG_CLASS_ID_LINEAR) {
WgShaderTypeLinearGradient linearGradient((LinearGradient*)fill); WgShaderTypeLinearGradient linearGradient((LinearGradient*)fill);
mBindGroupLinear.initialize(device, queue, linearGradient); bindGroupLinear.initialize(context.device, context.queue, linearGradient);
mFillType = WgRenderDataShapeFillType::Linear; fillType = WgRenderSettingsType::Linear;
} else if (fill->identifier() == TVG_CLASS_ID_RADIAL) { } else if (fill->identifier() == TVG_CLASS_ID_RADIAL) {
WgShaderTypeRadialGradient radialGradient((RadialGradient*)fill); WgShaderTypeRadialGradient radialGradient((RadialGradient*)fill);
mBindGroupRadial.initialize(device, queue, radialGradient); bindGroupRadial.initialize(context.device, context.queue, radialGradient);
mFillType = WgRenderDataShapeFillType::Radial; fillType = WgRenderSettingsType::Radial;
} }
} else if ((flags & (RenderUpdateFlag::Color)) && !fill) { } else if ((flags & (RenderUpdateFlag::Color)) && !fill) {
WgShaderTypeSolidColor solidColor(color); WgShaderTypeSolidColor solidColor(color);
mBindGroupSolid.initialize(device, queue, solidColor); bindGroupSolid.initialize(context.device, context.queue, solidColor);
mFillType = WgRenderDataShapeFillType::Solid; fillType = WgRenderSettingsType::Solid;
} }
} };
void WgRenderDataShapeSettings::release()
void WgRenderSettings::release(WgContext& context)
{ {
mBindGroupSolid.release(); bindGroupSolid.release();
mBindGroupLinear.release(); bindGroupLinear.release();
mBindGroupRadial.release(); bindGroupRadial.release();
} };
//***********************************************************************
// WgRenderDataPaint
//***********************************************************************
void WgRenderDataPaint::release(WgContext& context)
{
bindGroupPaint.release();
};
//*********************************************************************** //***********************************************************************
// WgRenderDataShape // WgRenderDataShape
//*********************************************************************** //***********************************************************************
void WgRenderDataShape::release() void WgRenderDataShape::updateMeshes(WgContext &context, const RenderShape &rshape)
{ {
releaseRenderData(); releaseMeshes(context);
mImageData.release(); // update shapes geometry
mBindGroupPaint.release(); WgGeometryDataGroup shapes;
mRenderSettingsShape.release(); shapes.tesselate(rshape);
mRenderSettingsStroke.release(); meshGroupShapes.update(context, &shapes);
mBindGroupPicture.release(); // update shapes bbox
} WgPoint pmin{}, pmax{};
shapes.getBBox(pmin, pmax);
WgGeometryData box;
void WgRenderDataShape::releaseRenderData() box.appendBox(pmin, pmax);
{ meshBBoxShapes.update(context, &box);
for (uint32_t i = 0; i < mGeometryDataImage.count; i++) { // update strokes geometry
mGeometryDataImage[i]->release(); if (rshape.stroke) {
delete mGeometryDataImage[i]; WgGeometryDataGroup strokes;
} strokes.stroke(rshape);
for (uint32_t i = 0; i < mGeometryDataStroke.count; i++) { strokes.getBBox(pmin, pmax);
mGeometryDataStroke[i]->release(); meshGroupStrokes.update(context, &strokes);
delete mGeometryDataStroke[i]; // update strokes bbox
} WgPoint pmin{}, pmax{};
mGeometryDataStroke.clear(); strokes.getBBox(pmin, pmax);
for (uint32_t i = 0; i < mGeometryDataShape.count; i++) { WgGeometryData box;
mGeometryDataShape[i]->release(); box.appendBox(pmin, pmax);
delete mGeometryDataShape[i]; meshBBoxStrokes.update(context, &box);
}
mGeometryDataShape.clear();
}
void WgRenderDataShape::tesselate(WGPUDevice device, WGPUQueue queue, Surface* surface, const RenderMesh* mesh)
{
// create image geometry data
Array<WgPoint> vertexList;
Array<WgPoint> texCoordsList;
Array<uint32_t> indexList;
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);
}
// 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)
{
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);
}
}
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) void WgRenderDataShape::releaseMeshes(WgContext &context)
{ {
for (uint32_t i = 0; i < outlines.count; i++) { meshBBoxStrokes.release(context);
auto& vlist = outlines[i]->mVertexList; meshBBoxShapes.release(context);
meshGroupStrokes.release(context);
// append single point segment meshGroupShapes.release(context);
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) void WgRenderDataShape::release(WgContext& context)
{ {
float wdt = rshape.stroke->width / 2; releaseMeshes(context);
for (uint32_t i = 0; i < outlines.count; i++) { renderSettingsStroke.release(context);
auto outline = outlines[i]; renderSettingsShape.release(context);
WgRenderDataPaint::release(context);
};
// single point sub-path //***********************************************************************
if (outline->mVertexList.count == 1) { // WgRenderDataPicture
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 void WgRenderDataPicture::release(WgContext& context)
if (outline->mVertexList.count == 2) { {
WgPoint v0 = outline->mVertexList[0]; meshData.release(context);
WgPoint v1 = outline->mVertexList[1]; imageData.release(context);
WgPoint dir0 = (v1 - v0).normal(); bindGroupPicture.release();
WgPoint nrm0 = WgPoint{ -dir0.y, +dir0.x }; WgRenderDataPaint::release(context);
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. * SOFTWARE.
*/ */
#ifndef _TVG_WG_RENDER_DATA_H_
#define _TVG_WG_RENDER_DATA_H_
#include "tvgWgPipelines.h" #include "tvgWgPipelines.h"
#include "tvgWgGeometry.h" #include "tvgWgGeometry.h"
struct WgGeometryData struct WgMeshData {
{ WGPUBuffer bufferPosition{};
WGPUBuffer mBufferVertex{}; WGPUBuffer bufferTexCoord{};
WGPUBuffer mBufferTexCoords{}; WGPUBuffer bufferIndex{};
WGPUBuffer mBufferIndex{}; size_t vertexCount{};
size_t mVertexCount{}; size_t indexCount{};
size_t mIndexCount{};
WgGeometryData() {}
virtual ~WgGeometryData() { release(); }
void initialize(WGPUDevice device) {};
void draw(WGPURenderPassEncoder renderPassEncoder); void draw(WGPURenderPassEncoder renderPassEncoder);
void drawImage(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(WgContext& context, WgGeometryData* geometryData);
void update(WGPUDevice device, WGPUQueue queue, float* vertexData, float* texCoordsData, size_t vertexCount, uint32_t* indexData, size_t indexCount); void release(WgContext& context);
void release();
}; };
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{}; WgBindGroupSolidColor bindGroupSolid{};
WGPUTexture mTexture{}; WgBindGroupLinearGradient bindGroupLinear{};
WGPUTextureView mTextureView{}; WgBindGroupRadialGradient bindGroupRadial{};
WgRenderSettingsType fillType{};
WgImageData() {} void update(WgContext& context, const Fill* fill, const uint8_t* color, const RenderUpdateFlag flags);
virtual ~WgImageData() { release(); } void release(WgContext& context);
void initialize(WGPUDevice device) {};
void update(WGPUDevice device, WGPUQueue queue, Surface* surface);
void release();
}; };
class WgRenderData struct WgRenderDataPaint
{ {
public: WgBindGroupPaint bindGroupPaint{};
virtual void initialize(WGPUDevice device) {};
virtual void release() = 0; 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 WgRenderDataShape: public WgRenderDataPaint
struct WgRenderDataShapeSettings
{ {
WgBindGroupSolidColor mBindGroupSolid{}; WgRenderSettings renderSettingsShape{};
WgBindGroupLinearGradient mBindGroupLinear{}; WgRenderSettings renderSettingsStroke{};
WgBindGroupRadialGradient mBindGroupRadial{}; WgMeshDataGroup meshGroupShapes{};
WgRenderDataShapeFillType mFillType{}; // Default: None WgMeshDataGroup meshGroupStrokes{};
WgMeshData meshBBoxShapes{};
WgMeshData meshBBoxStrokes{};
// update render shape settings defined by flags and fill settings void updateMeshes(WgContext& context, const RenderShape& rshape);
void update(WGPUDevice device, WGPUQueue queue, void releaseMeshes(WgContext& context);
const Fill* fill, const uint8_t* color, const RenderUpdateFlag flags); void release(WgContext& context) override;
void release(); uint32_t identifier() override { return TVG_CLASS_ID_SHAPE; };
}; };
class WgRenderDataShape: public WgRenderData struct WgRenderDataPicture: public WgRenderDataPaint
{ {
public: WgBindGroupPicture bindGroupPicture{};
// geometry data for shapes, strokes and image WgImageData imageData{};
Array<WgGeometryData*> mGeometryDataShape; WgMeshData meshData{};
Array<WgGeometryData*> mGeometryDataStroke;
Array<WgGeometryData*> mGeometryDataImage;
WgImageData mImageData;
// shader settings void update(WgContext& context);
WgBindGroupPaint mBindGroupPaint; void release(WgContext& context) override;
WgRenderDataShapeSettings mRenderSettingsShape; uint32_t identifier() override { return TVG_CLASS_ID_PICTURE; };
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);
}; };
#endif //_TVG_WG_RENDER_DATA_H_

View file

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

View file

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

View file

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