Files
asarium 7029e4a10f Add compile time option to enable logging in release builds
This mostly meant for special docker standalone server builds where it
might be useful to have full release performance but also have the logs
available if necessary. This now always compiles the log statements but
enables or disables them with a constexpr so they should not appear in
actual release binaries.
2020-02-24 18:54:55 +01:00

801 lines
25 KiB
C++

//
// Copyright (c) 2009-2013 Mikko Mononen [email protected]
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
// This code was adapted from the original NanoVG OpenGL renderer to work with the FreeSpace Open graphics API
#include "NanoVGRenderer.h"
#include "tracing/tracing.h"
// That is a wrapper function for log prints to be availiable for nanovg components. For now it is stb_truetype.h
// Planted by ksotar with blessing from asarium
extern "C" {
void nvgOldCPrintf(SCP_FORMAT_STRING const char *message, ...) {
if (LoggingEnabled) {
SCP_string buf;
va_list args;
va_start(args, message);
vsprintf(buf, message, args);
va_end(args);
outwnd_printf2("%s", message);
}
}
}
namespace {
using namespace graphics::paths;
int nvgRenderCreate(void* userptr) {
auto renderer = static_cast<NanoVGRenderer*>(userptr);
try {
renderer->initialize();
return 1;
} catch (...) {
return 0;
}
}
void nvgRenderDelete(void* userptr) {
auto renderer = static_cast<NanoVGRenderer*>(userptr);
delete renderer;
}
void nvgViewport(void* userptr, int width, int height) {
auto renderer = static_cast<NanoVGRenderer*>(userptr);
renderer->setViewport(width, height);
}
void nvgRenderTriangles(void* userptr, NVGpaint* paint, NVGscissor* scissor, const NVGvertex* verts, int nverts) {
auto renderer = static_cast<NanoVGRenderer*>(userptr);
renderer->renderTriangles(paint, scissor, verts, nverts);
}
void nvgRenderFill(void* userptr,
NVGpaint* paint,
NVGscissor* scissor,
float fringe,
const float* bounds,
const NVGpath* paths,
int npaths) {
auto renderer = static_cast<NanoVGRenderer*>(userptr);
renderer->renderFill(paint, scissor, fringe, bounds, paths, npaths);
}
void nvgRenderStroke(void* userptr,
NVGpaint* paint,
NVGscissor* scissor,
float fringe,
float strokeWidth,
const NVGpath* paths,
int npaths) {
auto renderer = static_cast<NanoVGRenderer*>(userptr);
renderer->renderStroke(paint, scissor, fringe, strokeWidth, paths, npaths);
}
void nvgRenderFlush(void* userptr) {
auto renderer = static_cast<NanoVGRenderer*>(userptr);
renderer->renderFlush();
}
void nvgRenderCancel(void* userptr) {
auto renderer = static_cast<NanoVGRenderer*>(userptr);
renderer->renderCancel();
}
int nvgCreateTexture(void* userptr, int type, int w, int h, int imageFlags, const unsigned char* data) {
auto renderer = static_cast<NanoVGRenderer*>(userptr);
return renderer->createTexture(type, w, h, imageFlags, data);
}
int nvgUpdateTexture(void* userptr, int image, int x, int y, int w, int h, const unsigned char* data) {
auto renderer = static_cast<NanoVGRenderer*>(userptr);
return renderer->updateTexture(image, x, y, w, h, data);
}
int nvgDeleteTexture(void* userptr, int image) {
auto renderer = static_cast<NanoVGRenderer*>(userptr);
return renderer->deleteTexture(image);
}
int nvgGetTextureSize(void* userptr, int image, int* w, int* h) {
auto renderer = static_cast<NanoVGRenderer*>(userptr);
return renderer->getTextureSize(image, w, h);
}
NVGcolor premulColor(NVGcolor c) {
c.r *= c.a;
c.g *= c.a;
c.b *= c.a;
return c;
}
vec4 colorToVec4(NVGcolor c) {
vec4 out;
out.xyzw.x = c.r;
out.xyzw.y = c.g;
out.xyzw.z = c.b;
out.xyzw.w = c.a;
return out;
}
void xformToMat3x4(float m3[12], float* t) {
m3[0] = t[0];
m3[1] = t[1];
m3[2] = 0.0f;
m3[3] = 0.0f;
m3[4] = t[2];
m3[5] = t[3];
m3[6] = 0.0f;
m3[7] = 0.0f;
m3[8] = t[4];
m3[9] = t[5];
m3[10] = 1.0f;
m3[11] = 0.0f;
}
size_t maxVertCount(const NVGpath* paths, size_t npaths) {
size_t count = 0;
for (size_t i = 0; i < npaths; i++) {
count += paths[i].nfill;
count += paths[i].nstroke;
}
return count;
}
void vertexSet(NVGvertex* vtx, float x, float y, float u, float v) {
vtx->x = x;
vtx->y = y;
vtx->u = u;
vtx->v = v;
}
nanovg_material getDefaultMaterial() {
nanovg_material mat;
material_set_nanovg(&mat, -1);
return mat;
}
}
namespace graphics {
namespace paths {
NVGcontext* createNanoVGContext() {
NVGparams params;
NVGcontext* ctx = nullptr;
auto nvgRenderer = new NanoVGRenderer();
memset(&params, 0, sizeof(params));
params.renderCreate = nvgRenderCreate;
params.renderDelete = nvgRenderDelete;
params.renderViewport = nvgViewport;
params.renderTriangles = nvgRenderTriangles;
params.renderFill = nvgRenderFill;
params.renderStroke = nvgRenderStroke;
params.renderFlush = nvgRenderFlush;
params.renderCancel = nvgRenderCancel;
params.renderCreateTexture = nvgCreateTexture;
params.renderUpdateTexture = nvgUpdateTexture;
params.renderDeleteTexture = nvgDeleteTexture;
params.renderGetTextureSize = nvgGetTextureSize;
params.userPtr = nvgRenderer;
params.edgeAntiAlias = 1;
ctx = nvgCreateInternal(&params);
if (ctx == nullptr) {
return nullptr;
}
return ctx;
}
void deleteNanoVGContext(NVGcontext* context) {
Assertion(context != nullptr, "Invalid context passed!");
nvgDeleteInternal(context);
}
NanoVGRenderer::NanoVGRenderer() {
}
void NanoVGRenderer::initialize() {
_vertexBuffer = gr_create_buffer(BufferType::Vertex, BufferUsageHint::Streaming);
_vertexLayout.add_vertex_component(vertex_format_data::POSITION2, sizeof(NVGvertex), (int) offsetof(NVGvertex, x));
_vertexLayout.add_vertex_component(vertex_format_data::TEX_COORD2, sizeof(NVGvertex), (int) offsetof(NVGvertex, u));
// Now create all the materials that we are going to need
{
auto material = getDefaultMaterial();
// Material for simple triangle draws. These use different primitive types so if that is moved into the
// material struct at some point this can be easily adjusted
_trianglesMaterial = material;
_triangleFillMaterial = material;
_triangleStrokeMaterial = material;
}
{
// Materials for the fill shader
auto material = getDefaultMaterial();
material.set_stencil_test(true);
material.set_stencil_mask(0xFF);
material.set_stencil_func(ComparisionFunction::Always, 0, 0xFF);
material.set_color_mask(false, false, false, false);
material.set_front_stencil_op(StencilOperation::Keep, StencilOperation::Keep, StencilOperation::IncrementWrap);
material.set_back_stencil_op(StencilOperation::Keep, StencilOperation::Keep, StencilOperation::DecrementWrap);
_fillShapeMaterial = material;
material.set_color(true, true, true, true);
material.set_stencil_func(ComparisionFunction::Equal, 0x00, 0xFF);
material.set_front_stencil_op(StencilOperation::Keep, StencilOperation::Keep, StencilOperation::Keep);
material.set_back_stencil_op(StencilOperation::Keep, StencilOperation::Keep, StencilOperation::Keep);
_fillAntiAliasMaterial = material;
material.set_stencil_func(ComparisionFunction::NotEqual, 0x00, 0xFF);
material.set_front_stencil_op(StencilOperation::Zero, StencilOperation::Zero, StencilOperation::Zero);
material.set_back_stencil_op(StencilOperation::Zero, StencilOperation::Zero, StencilOperation::Zero);
_fillFillMaterial = material;
}
{
// Pipeline states for the stroke shader
auto material = getDefaultMaterial();
material.set_stencil_test(true);
material.set_stencil_mask(0xFF);
material.set_stencil_func(ComparisionFunction::Equal, 0x00, 0xFF);
material.set_front_stencil_op(StencilOperation::Keep, StencilOperation::Keep, StencilOperation::Increment);
material.set_back_stencil_op(StencilOperation::Keep, StencilOperation::Keep, StencilOperation::Increment);
_strokeFillMaterial = material;
material.set_front_stencil_op(StencilOperation::Keep, StencilOperation::Keep, StencilOperation::Keep);
material.set_back_stencil_op(StencilOperation::Keep, StencilOperation::Keep, StencilOperation::Keep);
_strokeAntiaiasMaterial = material;
material.set_color_mask(false, false, false, false);
material.set_stencil_func(ComparisionFunction::Always, 0x00, 0xFF);
material.set_front_stencil_op(StencilOperation::Zero, StencilOperation::Zero, StencilOperation::Zero);
material.set_back_stencil_op(StencilOperation::Zero, StencilOperation::Zero, StencilOperation::Zero);
_strokeClearStencilMaterial = material;
}
}
void NanoVGRenderer::setViewport(int width, int height) {
_viewport.x = width;
_viewport.y = height;
}
void NanoVGRenderer::renderFill(NVGpaint* paint,
NVGscissor* scissor,
float fringe,
const float* bounds,
const NVGpath* paths,
int npaths) {
if (npaths <= 0) {
// Ignore irrelevant render calls
mprintf(("NanoVG asked us to render filled triangles but no paths were supplied!\n"));
return;
}
auto call = addDrawCall();
call->type = CallType::Fill;
call->pathOffset = static_cast<uint32_t>(addPaths((size_t) npaths));
call->pathCount = static_cast<uint32_t>((size_t) npaths);
call->image = paint->image;
if (npaths == 1 && paths[0].convex) {
call->type = CallType::ConvexFill;
}
// Allocate vertices for all the paths.
auto maxverts = maxVertCount(paths, (size_t) npaths) + 6;
auto offset = addVertices(maxverts);
for (int i = 0; i < npaths; i++) {
Path* copy = &_paths[call->pathOffset + i];
const NVGpath* path = &paths[i];
memset(copy, 0, sizeof(Path));
if (path->nfill > 0) {
copy->fillOffset = static_cast<uint32_t>(offset);
copy->fillCount = static_cast<uint32_t>((size_t) path->nfill);
memcpy(&_vertices[offset], path->fill, sizeof(NVGvertex) * path->nfill);
offset += path->nfill;
}
if (path->nstroke > 0) {
copy->strokeOffset = static_cast<uint32_t>(offset);
copy->strokeCount = static_cast<uint32_t>((size_t) path->nstroke);
memcpy(&_vertices[offset], path->stroke, sizeof(NVGvertex) * path->nstroke);
offset += path->nstroke;
}
}
// Quad
call->triangleOffset = static_cast<uint32_t>(offset);
call->triangleCount = 6;
auto quad = &_vertices[call->triangleOffset];
vertexSet(&quad[0], bounds[0], bounds[3], 0.5f, 1.0f);
vertexSet(&quad[1], bounds[2], bounds[3], 0.5f, 1.0f);
vertexSet(&quad[2], bounds[2], bounds[1], 0.5f, 1.0f);
vertexSet(&quad[3], bounds[0], bounds[3], 0.5f, 1.0f);
vertexSet(&quad[4], bounds[2], bounds[1], 0.5f, 1.0f);
vertexSet(&quad[5], bounds[0], bounds[1], 0.5f, 1.0f);
// Setup uniforms for draw calls
if (call->type == CallType::Fill) {
call->uniformIndex = addUniformData(2);
// Simple shader for stencil
auto frag = &_uniformData[call->uniformIndex];
frag->strokeThr = -1.0f;
frag->type = NanoVGShaderType::Simple;
// Fill shader
convertPaint(&_uniformData[call->uniformIndex + 1], paint, scissor, fringe, fringe, -1.0f);
} else {
call->uniformIndex = addUniformData(1);
// Fill shader
convertPaint(&_uniformData[call->uniformIndex], paint, scissor, fringe, fringe, -1.0f);
}
}
void NanoVGRenderer::renderTriangles(NVGpaint* paint, NVGscissor* scissor, const NVGvertex* verts, int nverts) {
if (nverts <= 0) {
// Ignore irrelevant render calls
mprintf(("NanoVG asked us to render triangles but no vertices were supplied!\n"));
return;
}
_vertices.insert(_vertices.end(), verts, verts + nverts);
auto call = addDrawCall();
call->type = CallType::Triangles;
call->triangleCount = static_cast<uint32_t>(nverts);
call->triangleOffset = static_cast<uint32_t>(addVertices(verts, static_cast<size_t>(nverts)));
call->image = paint->image;
call->uniformIndex = addUniformData(1);
auto uniformData = &_uniformData[call->uniformIndex];
auto succcess = convertPaint(uniformData, paint, scissor, 1.0f, 1.0f, -1.0f);
Assertion(succcess, "Failed to convert paint, probably caused by an invalid texture handle.");
uniformData->type = NanoVGShaderType::Image;
}
void NanoVGRenderer::renderStroke(NVGpaint* paint,
NVGscissor* scissor,
float fringe,
float strokeWidth,
const NVGpath* paths,
int npaths) {
if (npaths <= 0) {
// Ignore irrelevant render calls
mprintf(("NanoVG asked us to render stroke triangles but no paths were supplied!\n"));
return;
}
auto call = addDrawCall();
call->type = CallType::Stroke;
call->pathOffset = static_cast<uint32_t>(addPaths((size_t) npaths));
call->pathCount = static_cast<uint32_t>(npaths);
call->image = paint->image;
// Allocate vertices for all the paths.
auto maxverts = maxVertCount(paths, (size_t) npaths);
auto offset = addVertices(maxverts);
for (int i = 0; i < npaths; i++) {
Path* copy = &_paths[call->pathOffset + i];
const NVGpath* path = &paths[i];
memset(copy, 0, sizeof(*copy));
if (path->nstroke) {
copy->strokeOffset = static_cast<uint32_t>(offset);
copy->strokeCount = static_cast<uint32_t>(path->nstroke);
memcpy(&_vertices[offset], path->stroke, sizeof(NVGvertex) * path->nstroke);
offset += path->nstroke;
}
}
// Fill shader
call->uniformIndex = addUniformData(2);
convertPaint(&_uniformData[call->uniformIndex], paint, scissor, strokeWidth, fringe, -1.0f);
convertPaint(&_uniformData[call->uniformIndex + 1], paint, scissor, strokeWidth, fringe, 1.0f - 0.5f / 255.0f);
}
void NanoVGRenderer::renderFlush() {
if (_drawCalls.empty()) {
return;
}
TRACE_SCOPE(tracing::NanoVGFlushFrame);
GR_DEBUG_SCOPE("NanoVG flush");
gr_set_viewport(0, 0, gr_screen.max_w, gr_screen.max_h);
_uniformBuffer = gr_get_uniform_buffer(uniform_block_type::NanoVGData, _uniformData.size());
// This copies the uniform data from our vector into the uniform buffer aligner
for (auto& uniform : _uniformData) {
memcpy(_uniformBuffer.aligner().addTypedElement<nanovg_draw_data>(), &uniform, sizeof(uniform));
}
_uniformBuffer.submitData();
gr_update_buffer_data(_vertexBuffer, sizeof(NVGvertex) * _vertices.size(), _vertices.data());
for (auto& drawCall : _drawCalls) {
switch (drawCall.type) {
case CallType::Fill:
drawFill(drawCall);
break;
case CallType::ConvexFill:
drawConvexFill(drawCall);
break;
case CallType::Stroke:
drawStroke(drawCall);
break;
case CallType::Triangles:
drawTriangles(drawCall);
break;
}
}
// Reset all data again
renderCancel();
}
void NanoVGRenderer::renderCancel() {
// Clear all data written by the render functions
_vertices.clear();
_uniformData.clear();
_drawCalls.clear();
_paths.clear();
}
int NanoVGRenderer::createTexture(int type, int w, int h, int imageFlags, const unsigned char* data) {
int bpp;
if (type == NVG_TEXTURE_RGBA) {
bpp = 32;
} else {
bpp = 8;
}
Assertion(!(imageFlags & NVG_IMAGE_REPEATX), "Repeat X is not supported yet!");
Assertion(!(imageFlags & NVG_IMAGE_REPEATY), "Repeat Y is not supported yet!");
Assertion(!(imageFlags & NVG_IMAGE_GENERATE_MIPMAPS), "Generate Mipmaps is not supported yet!");
std::unique_ptr<uint8_t[]> data_buffer(new uint8_t[w * h * bpp]);
if (data != nullptr) {
memcpy(data_buffer.get(), data, static_cast<size_t>(w * h * bpp));
}
Image img;
img.data = std::move(data_buffer);
img.bitmap = bm_create(bpp, w, h, img.data.get(), bpp == 8 ? BMP_AABITMAP : 0);
img.type = type;
img.flags = imageFlags;
img.width = w;
img.height = h;
auto id = ++_lastImageId;
_textureMap.insert(std::make_pair(id, std::move(img)));
return id;
}
int NanoVGRenderer::updateTexture(int image, int /*x*/, int /*y*/, int /*w*/, int /*h*/, const unsigned char* data) {
auto texture = getTexture(image);
if (texture == nullptr) {
return 0;
}
int bpp;
if (texture->type == NVG_TEXTURE_RGBA) {
bpp = 32;
} else {
bpp = 8;
}
// Copy the updated data to our internal buffer so that changes in the hardware texture detail don't discard the
// changes NanoVG made to the texture
memcpy(texture->data.get(), data, texture->width * texture->height * bpp / 8);
// TODO: This could probably be done better by only uploading the changed area
gr_update_texture(texture->bitmap, bpp, data, texture->width, texture->height);
return 1;
}
int NanoVGRenderer::deleteTexture(int image) {
auto iter = _textureMap.find(image);
if (iter == _textureMap.end()) {
return 0;
}
bm_release(iter->second.bitmap);
// This will call the destructor of the class and free the resources
_textureMap.erase(iter);
return 1;
}
int NanoVGRenderer::getTextureSize(int image, int* w, int* h) {
auto img = getTexture(image);
if (img == nullptr) {
return 0;
}
*w = img->width;
*h = img->height;
return 1;
}
NanoVGRenderer::DrawCall* NanoVGRenderer::addDrawCall() {
_drawCalls.emplace_back();
return &_drawCalls.back();
}
size_t NanoVGRenderer::addUniformData(size_t num) {
nanovg_draw_data data;
memset(&data, 0, sizeof(data));
// Push an unitialized struct into the vector but set the viewport here since that is the same for all elements
data.viewSize = { i2fl(_viewport.x), i2fl(_viewport.y) };
auto current = _uniformData.size();
_uniformData.resize(current + num, data);
return current;
}
size_t NanoVGRenderer::addVertices(const NVGvertex* vert, size_t num) {
auto offset = _vertices.size();
_vertices.insert(_vertices.end(), vert, vert + num);
return offset;
}
size_t NanoVGRenderer::addVertices(size_t num) {
auto offset = _vertices.size();
_vertices.resize(offset + num);
return offset;
}
size_t NanoVGRenderer::addPaths(size_t num) {
auto offset = _paths.size();
_paths.resize(offset + num);
return offset;
}
bool NanoVGRenderer::convertPaint(graphics::nanovg_draw_data* frag,
NVGpaint* paint,
NVGscissor* scissor,
float width,
float fringe,
float strokeThr) {
float invxform[6];
frag->innerCol = colorToVec4(premulColor(paint->innerColor));
frag->outerCol = colorToVec4(premulColor(paint->outerColor));
if (scissor->extent[0] < -0.5f || scissor->extent[1] < -0.5f) {
memset(&frag->scissorMat, 0, sizeof(frag->scissorMat));
frag->scissorExt.x = 1.0f;
frag->scissorExt.y = 1.0f;
frag->scissorScale.x = 1.0f;
frag->scissorScale.y = 1.0f;
} else {
nvgTransformInverse(invxform, scissor->xform);
xformToMat3x4(frag->scissorMat, invxform);
frag->scissorExt.x = scissor->extent[0];
frag->scissorExt.y = scissor->extent[1];
frag->scissorScale.x =
sqrtf(scissor->xform[0] * scissor->xform[0] + scissor->xform[2] * scissor->xform[2]) / fringe;
frag->scissorScale.y =
sqrtf(scissor->xform[1] * scissor->xform[1] + scissor->xform[3] * scissor->xform[3]) / fringe;
}
memcpy(&frag->extent, paint->extent, sizeof(frag->extent));
frag->strokeMult = (width * 0.5f + fringe * 0.5f) / fringe;
frag->strokeThr = strokeThr;
if (paint->image != 0) {
auto tex = getTexture(paint->image);
if (tex == nullptr) {
return false;
}
if ((tex->flags & NVG_IMAGE_FLIPY) != 0) {
float flipped[6];
nvgTransformScale(flipped, 1.0f, -1.0f);
nvgTransformMultiply(flipped, paint->xform);
nvgTransformInverse(invxform, flipped);
} else {
nvgTransformInverse(invxform, paint->xform);
}
frag->type = NanoVGShaderType::FillImage;
if (tex->type == NVG_TEXTURE_RGBA) {
frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0 : 1;
} else {
frag->texType = 2;
}
// printf("frag->texType = %d\n", frag->texType);
} else {
frag->type = NanoVGShaderType::FillGradient;
frag->radius = paint->radius;
frag->feather = paint->feather;
nvgTransformInverse(invxform, paint->xform);
}
xformToMat3x4(frag->paintMat, invxform);
auto tex = getTexture(paint->image);
if (tex != nullptr) {
// Since textures may be stored in texture arrays we need to pass the index of the image into the shader
frag->texArrayIndex = bm_get_array_index(tex->bitmap);
}
return true;
}
NanoVGRenderer::Image* NanoVGRenderer::getTexture(int id) {
auto iter = _textureMap.find(id);
if (iter == _textureMap.end()) {
return nullptr;
}
return &iter->second;
}
void NanoVGRenderer::drawTriangles(const DrawCall& call) {
GR_DEBUG_SCOPE("Draw triangles");
TRACE_SCOPE(tracing::NanoVGDrawTriangles);
auto mat = _trianglesMaterial;
materialSetTexture(mat, call.image);
gr_bind_uniform_buffer(uniform_block_type::NanoVGData, _uniformBuffer.getAlignerElementOffset(call.uniformIndex),
sizeof(nanovg_draw_data), _uniformBuffer.bufferHandle());
gr_render_nanovg(&mat, PRIM_TYPE_TRIS, &_vertexLayout, call.triangleOffset, call.triangleCount, _vertexBuffer);
}
void NanoVGRenderer::drawFill(const DrawCall& call) {
GR_DEBUG_SCOPE("Draw fill");
TRACE_SCOPE(tracing::NanoVGDrawFill);
auto mat = _fillShapeMaterial;
mat.set_texture_map(TM_BASE_TYPE, -1);
gr_bind_uniform_buffer(uniform_block_type::NanoVGData, _uniformBuffer.getAlignerElementOffset(call.uniformIndex),
sizeof(nanovg_draw_data), _uniformBuffer.bufferHandle());
auto pathOffset = call.pathOffset;
for (size_t i = 0; i < call.pathCount; ++i) {
gr_render_nanovg(&mat,
PRIM_TYPE_TRISTRIP,
&_vertexLayout,
_paths[pathOffset + i].fillOffset,
_paths[pathOffset + i].fillCount,
_vertexBuffer);
}
mat = _fillAntiAliasMaterial;
materialSetTexture(mat, call.image);
gr_bind_uniform_buffer(uniform_block_type::NanoVGData,
_uniformBuffer.getAlignerElementOffset(call.uniformIndex + 1), sizeof(nanovg_draw_data),
_uniformBuffer.bufferHandle());
// Draw fringes
for (size_t i = 0; i < call.pathCount; ++i) {
gr_render_nanovg(&mat,
PRIM_TYPE_TRISTRIP,
&_vertexLayout,
_paths[pathOffset + i].strokeOffset,
_paths[pathOffset + i].strokeCount,
_vertexBuffer);
}
mat = _fillFillMaterial;
materialSetTexture(mat, call.image);
gr_render_nanovg(&mat, PRIM_TYPE_TRIS, &_vertexLayout, call.triangleOffset, call.triangleCount, _vertexBuffer);
}
void NanoVGRenderer::drawConvexFill(const DrawCall& call) {
GR_DEBUG_SCOPE("Draw convex fill");
TRACE_SCOPE(tracing::NanoVGDrawConvexFill);
auto mat = _triangleFillMaterial;
materialSetTexture(mat, call.image);
gr_bind_uniform_buffer(uniform_block_type::NanoVGData, _uniformBuffer.getAlignerElementOffset(call.uniformIndex),
sizeof(nanovg_draw_data), _uniformBuffer.bufferHandle());
auto pathOffset = call.pathOffset;
for (size_t i = 0; i < call.pathCount; ++i) {
gr_render_nanovg(&mat,
PRIM_TYPE_TRIFAN,
&_vertexLayout,
_paths[pathOffset + i].fillOffset,
_paths[pathOffset + i].fillCount,
_vertexBuffer);
}
mat = _triangleStrokeMaterial;
materialSetTexture(mat, call.image);
// Draw fringes
for (size_t i = 0; i < call.pathCount; ++i) {
gr_render_nanovg(&mat,
PRIM_TYPE_TRISTRIP,
&_vertexLayout,
_paths[pathOffset + i].strokeOffset,
_paths[pathOffset + i].strokeCount,
_vertexBuffer);
}
}
void NanoVGRenderer::drawStroke(const DrawCall& call) {
GR_DEBUG_SCOPE("Draw stroke");
TRACE_SCOPE(tracing::NanoVGDrawStroke);
auto mat = _strokeFillMaterial;
materialSetTexture(mat, call.image);
gr_bind_uniform_buffer(uniform_block_type::NanoVGData,
_uniformBuffer.getAlignerElementOffset(call.uniformIndex + 1), sizeof(nanovg_draw_data),
_uniformBuffer.bufferHandle());
auto pathOffset = call.pathOffset;
// Fill the stroke base without overlap
for (size_t i = 0; i < call.pathCount; ++i) {
gr_render_nanovg(&mat,
PRIM_TYPE_TRISTRIP,
&_vertexLayout,
_paths[pathOffset + i].strokeOffset,
_paths[pathOffset + i].strokeCount,
_vertexBuffer);
}
// Draw anti-aliased pixels.
gr_bind_uniform_buffer(uniform_block_type::NanoVGData, _uniformBuffer.getAlignerElementOffset(call.uniformIndex),
sizeof(nanovg_draw_data), _uniformBuffer.bufferHandle());
mat = _strokeAntiaiasMaterial;
materialSetTexture(mat, call.image);
for (size_t i = 0; i < call.pathCount; ++i) {
gr_render_nanovg(&mat,
PRIM_TYPE_TRISTRIP,
&_vertexLayout,
_paths[pathOffset + i].strokeOffset,
_paths[pathOffset + i].strokeCount,
_vertexBuffer);
}
// Clear stencil buffer.
mat = _strokeClearStencilMaterial;
materialSetTexture(mat, call.image);
for (size_t i = 0; i < call.pathCount; ++i) {
gr_render_nanovg(&mat,
PRIM_TYPE_TRISTRIP,
&_vertexLayout,
_paths[pathOffset + i].strokeOffset,
_paths[pathOffset + i].strokeCount,
_vertexBuffer);
}
}
void NanoVGRenderer::materialSetTexture(nanovg_material& mat, int nvgHandle) {
auto tex = getTexture(nvgHandle);
if (tex != nullptr) {
mat.set_texture_map(TM_BASE_TYPE, tex->bitmap);
if (tex->type == NVG_TEXTURE_ALPHA) {
mat.set_texture_type(material::TEX_TYPE_AABITMAP);
}
} else {
mat.set_texture_map(TM_BASE_TYPE, -1);
}
}
}
}