mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 13:43:43 +00:00

Texture must be fully uploaded into GPU memory before we can use or destroy it. This change force texture data updates
435 lines
15 KiB
C++
435 lines
15 KiB
C++
|
|
/*
|
|
* Copyright (c) 2023 - 2024 the ThorVG project. All rights reserved.
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#ifndef _TVG_WG_RENDER_DATA_H_
|
|
#define _TVG_WG_RENDER_DATA_H_
|
|
|
|
#include "tvgWgRenderData.h"
|
|
|
|
//***********************************************************************
|
|
// WgMeshData
|
|
//***********************************************************************
|
|
|
|
void WgMeshData::draw(WgContext& context, WGPURenderPassEncoder renderPassEncoder)
|
|
{
|
|
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 WgMeshData::drawFan(WgContext& context, WGPURenderPassEncoder renderPassEncoder)
|
|
{
|
|
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, bufferPosition, 0, vertexCount * sizeof(float) * 2);
|
|
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, context.indexBufferFan, WGPUIndexFormat_Uint32, 0, indexCount * sizeof(uint32_t));
|
|
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, indexCount, 1, 0, 0, 0);
|
|
}
|
|
|
|
|
|
void WgMeshData::drawImage(WgContext& context, WGPURenderPassEncoder renderPassEncoder)
|
|
{
|
|
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, const WgPolyline* polyline)
|
|
{
|
|
assert(polyline);
|
|
assert(polyline->pts.count > 2);
|
|
vertexCount = polyline->pts.count;
|
|
indexCount = (polyline->pts.count - 2) * 3;
|
|
context.allocateVertexBuffer(bufferPosition, &polyline->pts[0], vertexCount * sizeof(float) * 2);
|
|
context.allocateIndexBufferFan(vertexCount);
|
|
}
|
|
|
|
|
|
void WgMeshData::update(WgContext& context, const WgGeometryData* geometryData)
|
|
{
|
|
assert(geometryData);
|
|
vertexCount = geometryData->positions.pts.count;
|
|
indexCount = geometryData->indexes.count;
|
|
// buffer position data create and write
|
|
if (geometryData->positions.pts.count > 0)
|
|
context.allocateVertexBuffer(bufferPosition, &geometryData->positions.pts[0], vertexCount * sizeof(float) * 2);
|
|
// buffer tex coords data create and write
|
|
if (geometryData->texCoords.count > 0)
|
|
context.allocateVertexBuffer(bufferTexCoord, &geometryData->texCoords[0], vertexCount * sizeof(float) * 2);
|
|
// buffer index data create and write
|
|
if (geometryData->indexes.count > 0)
|
|
context.allocateIndexBuffer(bufferIndex, &geometryData->indexes[0], indexCount * sizeof(uint32_t));
|
|
};
|
|
|
|
|
|
void WgMeshData::update(WgContext& context, const WgPoint pmin, const WgPoint pmax)
|
|
{
|
|
vertexCount = 4;
|
|
indexCount = 6;
|
|
const float data[] = {
|
|
pmin.x, pmin.y, pmax.x, pmin.y,
|
|
pmax.x, pmax.y, pmin.x, pmax.y
|
|
};
|
|
context.allocateVertexBuffer(bufferPosition, data, sizeof(data));
|
|
context.allocateIndexBufferFan(vertexCount);
|
|
}
|
|
|
|
|
|
void WgMeshData::release(WgContext& context)
|
|
{
|
|
context.releaseBuffer(bufferIndex);
|
|
context.releaseBuffer(bufferTexCoord);
|
|
context.releaseBuffer(bufferPosition);
|
|
};
|
|
|
|
|
|
//***********************************************************************
|
|
// WgMeshDataPool
|
|
//***********************************************************************
|
|
|
|
WgMeshData* WgMeshDataPool::allocate(WgContext& context)
|
|
{
|
|
WgMeshData* meshData{};
|
|
if (mPool.count > 0) {
|
|
meshData = mPool.last();
|
|
mPool.pop();
|
|
} else {
|
|
meshData = new WgMeshData();
|
|
mList.push(meshData);
|
|
}
|
|
return meshData;
|
|
}
|
|
|
|
|
|
void WgMeshDataPool::free(WgContext& context, WgMeshData* meshData)
|
|
{
|
|
mPool.push(meshData);
|
|
}
|
|
|
|
|
|
void WgMeshDataPool::release(WgContext& context)
|
|
{
|
|
for (uint32_t i = 0; i < mList.count; i++) {
|
|
mList[i]->release(context);
|
|
delete mList[i];
|
|
}
|
|
mPool.clear();
|
|
mList.clear();
|
|
}
|
|
|
|
WgMeshDataPool* WgMeshDataGroup::gMeshDataPool = nullptr;
|
|
|
|
//***********************************************************************
|
|
// WgMeshDataGroup
|
|
//***********************************************************************
|
|
|
|
void WgMeshDataGroup::append(WgContext& context, const WgPolyline* polyline)
|
|
{
|
|
assert(polyline);
|
|
assert(polyline->pts.count >= 3);
|
|
meshes.push(gMeshDataPool->allocate(context));
|
|
meshes.last()->update(context, polyline);
|
|
}
|
|
|
|
|
|
void WgMeshDataGroup::append(WgContext& context, const WgGeometryData* geometryData)
|
|
{
|
|
assert(geometryData);
|
|
assert(geometryData->positions.pts.count >= 3);
|
|
meshes.push(gMeshDataPool->allocate(context));
|
|
meshes.last()->update(context, geometryData);
|
|
}
|
|
|
|
|
|
void WgMeshDataGroup::append(WgContext& context, const WgPoint pmin, const WgPoint pmax)
|
|
{
|
|
meshes.push(gMeshDataPool->allocate(context));
|
|
meshes.last()->update(context, pmin, pmax);
|
|
}
|
|
|
|
|
|
void WgMeshDataGroup::release(WgContext& context)
|
|
{
|
|
for (uint32_t i = 0; i < meshes.count; i++)
|
|
gMeshDataPool->free(context, meshes[i]);
|
|
meshes.clear();
|
|
};
|
|
|
|
|
|
//***********************************************************************
|
|
// WgImageData
|
|
//***********************************************************************
|
|
|
|
void WgImageData::update(WgContext& context, Surface* surface)
|
|
{
|
|
release(context);
|
|
assert(surface);
|
|
texture = context.createTexture2d(
|
|
WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopyDst,
|
|
WGPUTextureFormat_RGBA8Unorm,
|
|
surface->w, surface->h, "The shape texture");
|
|
assert(texture);
|
|
textureView = context.createTextureView2d(texture, "The shape texture view");
|
|
assert(textureView);
|
|
// update texture data
|
|
WGPUImageCopyTexture imageCopyTexture{};
|
|
imageCopyTexture.nextInChain = nullptr;
|
|
imageCopyTexture.texture = texture;
|
|
imageCopyTexture.mipLevel = 0;
|
|
imageCopyTexture.origin = { 0, 0, 0 };
|
|
imageCopyTexture.aspect = WGPUTextureAspect_All;
|
|
WGPUTextureDataLayout textureDataLayout{};
|
|
textureDataLayout.nextInChain = nullptr;
|
|
textureDataLayout.offset = 0;
|
|
textureDataLayout.bytesPerRow = 4 * surface->w;
|
|
textureDataLayout.rowsPerImage = surface->h;
|
|
WGPUExtent3D writeSize{};
|
|
writeSize.width = surface->w;
|
|
writeSize.height = surface->h;
|
|
writeSize.depthOrArrayLayers = 1;
|
|
wgpuQueueWriteTexture(context.queue, &imageCopyTexture, surface->data, 4 * surface->w * surface->h, &textureDataLayout, &writeSize);
|
|
wgpuQueueSubmit(context.queue, 0, nullptr);
|
|
};
|
|
|
|
|
|
void WgImageData::release(WgContext& context)
|
|
{
|
|
context.releaseTextureView(textureView);
|
|
context.releaseTexture(texture);
|
|
};
|
|
|
|
//***********************************************************************
|
|
// WgRenderSettings
|
|
//***********************************************************************
|
|
|
|
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);
|
|
bindGroupLinear.initialize(context.device, context.queue, linearGradient);
|
|
fillType = WgRenderSettingsType::Linear;
|
|
} else if (fill->identifier() == TVG_CLASS_ID_RADIAL) {
|
|
WgShaderTypeRadialGradient radialGradient((RadialGradient*)fill);
|
|
bindGroupRadial.initialize(context.device, context.queue, radialGradient);
|
|
fillType = WgRenderSettingsType::Radial;
|
|
}
|
|
skip = false;
|
|
} else if ((flags & (RenderUpdateFlag::Color)) && !fill) {
|
|
WgShaderTypeSolidColor solidColor(color);
|
|
bindGroupSolid.initialize(context.device, context.queue, solidColor);
|
|
fillType = WgRenderSettingsType::Solid;
|
|
skip = (color[3] == 0);
|
|
}
|
|
};
|
|
|
|
|
|
void WgRenderSettings::release(WgContext& context)
|
|
{
|
|
bindGroupSolid.release();
|
|
bindGroupLinear.release();
|
|
bindGroupRadial.release();
|
|
};
|
|
|
|
//***********************************************************************
|
|
// WgRenderDataPaint
|
|
//***********************************************************************
|
|
|
|
void WgRenderDataPaint::release(WgContext& context)
|
|
{
|
|
bindGroupPaint.release();
|
|
};
|
|
|
|
//***********************************************************************
|
|
// WgRenderDataShape
|
|
//***********************************************************************
|
|
|
|
void WgRenderDataShape::updateBBox(WgPoint pmin, WgPoint pmax)
|
|
{
|
|
pMin.x = std::min(pMin.x, pmin.x);
|
|
pMin.y = std::min(pMin.y, pmin.y);
|
|
pMax.x = std::max(pMax.x, pmax.x);
|
|
pMax.y = std::max(pMax.y, pmax.y);
|
|
}
|
|
|
|
|
|
void WgRenderDataShape::updateMeshes(WgContext &context, const RenderShape &rshape, const RenderTransform* rt)
|
|
{
|
|
releaseMeshes(context);
|
|
strokeFirst = rshape.stroke ? rshape.stroke->strokeFirst : false;
|
|
|
|
static WgPolyline polyline;
|
|
polyline.clear();
|
|
// decode path
|
|
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) {
|
|
// proceed current polyline
|
|
updateMeshes(context, &polyline, rshape.stroke);
|
|
polyline.clear();
|
|
polyline.appendPoint(rshape.path.pts[pntIndex]);
|
|
pntIndex++;
|
|
} else if (cmd == PathCommand::LineTo) {
|
|
polyline.appendPoint(rshape.path.pts[pntIndex]);
|
|
pntIndex++;
|
|
} else if (cmd == PathCommand::Close) {
|
|
polyline.close();
|
|
} else if (cmd == PathCommand::CubicTo) {
|
|
assert(polyline.pts.count > 0);
|
|
WgPoint pt0 = polyline.pts.last().trans(rt->m);
|
|
WgPoint pt1 = WgPoint(rshape.path.pts[pntIndex + 0]).trans(rt->m);
|
|
WgPoint pt2 = WgPoint(rshape.path.pts[pntIndex + 1]).trans(rt->m);
|
|
WgPoint pt3 = WgPoint(rshape.path.pts[pntIndex + 2]).trans(rt->m);
|
|
uint32_t nsegs = (uint32_t)(pt0.dist(pt1) + pt1.dist(pt2) + pt2.dist(pt3));
|
|
polyline.appendCubic(
|
|
rshape.path.pts[pntIndex + 0],
|
|
rshape.path.pts[pntIndex + 1],
|
|
rshape.path.pts[pntIndex + 2],
|
|
nsegs / 2);
|
|
pntIndex += 3;
|
|
}
|
|
}
|
|
// proceed last polyline
|
|
updateMeshes(context, &polyline, rshape.stroke);
|
|
meshDataBBox.update(context, pMin, pMax);
|
|
}
|
|
|
|
|
|
void WgRenderDataShape::updateMeshes(WgContext& context, const WgPolyline* polyline, const RenderStroke* rstroke)
|
|
{
|
|
assert(polyline);
|
|
// generate fill geometry
|
|
if (polyline->pts.count >= 3) {
|
|
WgPoint pmin{}, pmax{};
|
|
polyline->getBBox(pmin, pmax);
|
|
meshGroupShapes.append(context, polyline);
|
|
meshGroupShapesBBox.append(context, pmin, pmax);
|
|
updateBBox(pmin, pmax);
|
|
}
|
|
// generate strokes geometry
|
|
if ((polyline->pts.count >= 1) && rstroke && (rstroke->width > 0.0f)) {
|
|
static WgGeometryData geometryData; geometryData.clear();
|
|
static WgPolyline trimmed;
|
|
// trim -> split -> stroke
|
|
float trimBegin = rstroke->trim.begin < rstroke->trim.end ? rstroke->trim.begin : rstroke->trim.end;
|
|
float trimEnd = rstroke->trim.begin < rstroke->trim.end ? rstroke->trim.end : rstroke->trim.begin;
|
|
if (trimBegin == trimEnd) return;
|
|
if ((rstroke->dashPattern) && ((trimBegin != 0.0f) || (trimEnd != 1.0f))) {
|
|
polyline->trim(&trimmed, trimBegin, trimEnd);
|
|
geometryData.appendStrokeDashed(&trimmed, rstroke);
|
|
} else // trim -> stroke
|
|
if ((trimBegin != 0.0f) || (trimEnd != 1.0f)) {
|
|
polyline->trim(&trimmed, trimBegin, trimEnd);
|
|
geometryData.appendStroke(&trimmed, rstroke);
|
|
} else // split -> stroke
|
|
if (rstroke->dashPattern) {
|
|
geometryData.appendStrokeDashed(polyline, rstroke);
|
|
} else { // stroke
|
|
geometryData.appendStroke(polyline, rstroke);
|
|
}
|
|
// append render meshes and bboxes
|
|
if(geometryData.positions.pts.count >= 3) {
|
|
WgPoint pmin{}, pmax{};
|
|
geometryData.positions.getBBox(pmin, pmax);
|
|
meshGroupStrokes.append(context, &geometryData);
|
|
meshGroupStrokesBBox.append(context, pmin, pmax);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void WgRenderDataShape::releaseMeshes(WgContext &context)
|
|
{
|
|
meshGroupStrokesBBox.release(context);
|
|
meshGroupStrokes.release(context);
|
|
meshGroupShapesBBox.release(context);
|
|
meshGroupShapes.release(context);
|
|
pMin = {FLT_MAX, FLT_MAX};
|
|
pMax = {0.0f, 0.0f};
|
|
}
|
|
|
|
|
|
void WgRenderDataShape::release(WgContext& context)
|
|
{
|
|
releaseMeshes(context);
|
|
meshDataBBox.release(context);
|
|
renderSettingsStroke.release(context);
|
|
renderSettingsShape.release(context);
|
|
WgRenderDataPaint::release(context);
|
|
};
|
|
|
|
//***********************************************************************
|
|
// WgRenderDataShapePool
|
|
//***********************************************************************
|
|
|
|
WgRenderDataShape* WgRenderDataShapePool::allocate(WgContext& context)
|
|
{
|
|
WgRenderDataShape* dataShape{};
|
|
if (mPool.count > 0) {
|
|
dataShape = mPool.last();
|
|
mPool.pop();
|
|
} else {
|
|
dataShape = new WgRenderDataShape();
|
|
mList.push(dataShape);
|
|
}
|
|
return dataShape;
|
|
}
|
|
|
|
|
|
void WgRenderDataShapePool::free(WgContext& context, WgRenderDataShape* dataShape)
|
|
{
|
|
dataShape->meshGroupShapes.release(context);
|
|
dataShape->meshGroupShapesBBox.release(context);
|
|
dataShape->meshGroupStrokes.release(context);
|
|
dataShape->meshGroupStrokesBBox.release(context);
|
|
mPool.push(dataShape);
|
|
}
|
|
|
|
|
|
void WgRenderDataShapePool::release(WgContext& context)
|
|
{
|
|
for (uint32_t i = 0; i < mList.count; i++) {
|
|
mList[i]->release(context);
|
|
delete mList[i];
|
|
}
|
|
mPool.clear();
|
|
mList.clear();
|
|
}
|
|
|
|
//***********************************************************************
|
|
// WgRenderDataPicture
|
|
//***********************************************************************
|
|
|
|
void WgRenderDataPicture::release(WgContext& context)
|
|
{
|
|
meshData.release(context);
|
|
imageData.release(context);
|
|
bindGroupPicture.release();
|
|
WgRenderDataPaint::release(context);
|
|
}
|
|
|
|
#endif //_TVG_WG_RENDER_DATA_H_
|