mirror of
https://github.com/scratchfoundation/bgfx.git
synced 2024-11-25 00:58:30 -05:00
e5da24f490
When a lot of lines are being drawn, this can overflow the vertex buffer, causing a crash in `nvgRenderFlush`. While the underlying code handles this case in an okay manner (truncating the vertex buffer instead of crashing), the `nvgRenderFlush` code is missing a check for this case, causing it to think it has more space than was allocated. I tried to fix it with multiple buffers as mentioned in #160, however it seems more complex than it appears. This fix just makes it truncate the output according to the actual allocated amount of bytes in the vertex buffer, so it doesn't crash at least.
1055 lines
27 KiB
C++
1055 lines
27 KiB
C++
/*
|
|
* Copyright 2011-2014 Branimir Karadzic. All rights reserved.
|
|
* License: http://www.opensource.org/licenses/BSD-2-Clause
|
|
*/
|
|
|
|
//
|
|
// Copyright (c) 2009-2013 Mikko Mononen memon@inside.org
|
|
//
|
|
// 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.
|
|
//
|
|
#define NVG_ANTIALIAS 1
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include "nanovg.h"
|
|
|
|
#include <bgfx.h>
|
|
|
|
#include <bx\bx.h>
|
|
|
|
namespace
|
|
{
|
|
#include "vs_nanovg_fill.bin.h"
|
|
#include "fs_nanovg_fill.bin.h"
|
|
|
|
static bgfx::VertexDecl s_nvgDecl;
|
|
|
|
enum GLNVGshaderType
|
|
{
|
|
NSVG_SHADER_FILLGRAD,
|
|
NSVG_SHADER_FILLIMG,
|
|
NSVG_SHADER_SIMPLE,
|
|
NSVG_SHADER_IMG
|
|
};
|
|
|
|
// These are additional flags on top of NVGimageFlags.
|
|
enum NVGimageFlagsGL {
|
|
NVG_IMAGE_NODELETE = 1<<16, // Do not delete GL texture handle.
|
|
};
|
|
|
|
struct GLNVGtexture
|
|
{
|
|
bgfx::TextureHandle id;
|
|
int width, height;
|
|
int type;
|
|
int flags;
|
|
};
|
|
|
|
enum GLNVGcallType
|
|
{
|
|
GLNVG_FILL,
|
|
GLNVG_CONVEXFILL,
|
|
GLNVG_STROKE,
|
|
GLNVG_TRIANGLES,
|
|
};
|
|
|
|
struct GLNVGcall
|
|
{
|
|
int type;
|
|
int image;
|
|
int pathOffset;
|
|
int pathCount;
|
|
int vertexOffset;
|
|
int vertexCount;
|
|
int uniformOffset;
|
|
};
|
|
|
|
struct GLNVGpath
|
|
{
|
|
int fillOffset;
|
|
int fillCount;
|
|
int strokeOffset;
|
|
int strokeCount;
|
|
};
|
|
|
|
struct GLNVGfragUniforms
|
|
{
|
|
float scissorMat[12]; // matrices are actually 3 vec4s
|
|
float paintMat[12];
|
|
NVGcolor innerCol;
|
|
NVGcolor outerCol;
|
|
|
|
// u_scissorExtScale
|
|
float scissorExt[2];
|
|
float scissorScale[2];
|
|
|
|
// u_extentRadius
|
|
float extent[2];
|
|
float radius;
|
|
|
|
// u_params
|
|
float feather;
|
|
float strokeMult;
|
|
float texType;
|
|
float type;
|
|
};
|
|
|
|
struct GLNVGcontext
|
|
{
|
|
bgfx::ProgramHandle prog;
|
|
bgfx::UniformHandle u_scissorMat;
|
|
bgfx::UniformHandle u_paintMat;
|
|
bgfx::UniformHandle u_innerCol;
|
|
bgfx::UniformHandle u_outerCol;
|
|
bgfx::UniformHandle u_viewSize;
|
|
bgfx::UniformHandle u_scissorExtScale;
|
|
bgfx::UniformHandle u_extentRadius;
|
|
bgfx::UniformHandle u_params;
|
|
bgfx::UniformHandle u_halfTexel;
|
|
|
|
bgfx::UniformHandle s_tex;
|
|
|
|
uint64_t state;
|
|
bgfx::TextureHandle th;
|
|
|
|
bgfx::TransientVertexBuffer tvb;
|
|
uint8_t viewid;
|
|
|
|
struct GLNVGtexture* textures;
|
|
float view[2];
|
|
int ntextures;
|
|
int ctextures;
|
|
int textureId;
|
|
int vertBuf;
|
|
int fragSize;
|
|
int edgeAntiAlias;
|
|
|
|
// Per frame buffers
|
|
struct GLNVGcall* calls;
|
|
int ccalls;
|
|
int ncalls;
|
|
struct GLNVGpath* paths;
|
|
int cpaths;
|
|
int npaths;
|
|
struct NVGvertex* verts;
|
|
int cverts;
|
|
int nverts;
|
|
unsigned char* uniforms;
|
|
int cuniforms;
|
|
int nuniforms;
|
|
};
|
|
|
|
static struct GLNVGtexture* glnvg__allocTexture(struct GLNVGcontext* gl)
|
|
{
|
|
struct GLNVGtexture* tex = NULL;
|
|
int i;
|
|
|
|
for (i = 0; i < gl->ntextures; i++)
|
|
{
|
|
if (gl->textures[i].id.idx == bgfx::invalidHandle)
|
|
{
|
|
tex = &gl->textures[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (tex == NULL)
|
|
{
|
|
if (gl->ntextures+1 > gl->ctextures)
|
|
{
|
|
int old = gl->ctextures;
|
|
gl->ctextures = (gl->ctextures == 0) ? 2 : gl->ctextures*2;
|
|
gl->textures = (struct GLNVGtexture*)realloc(gl->textures, sizeof(struct GLNVGtexture)*gl->ctextures);
|
|
memset(&gl->textures[old], 0xff, (gl->ctextures-old)*sizeof(struct GLNVGtexture) );
|
|
|
|
if (gl->textures == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
tex = &gl->textures[gl->ntextures++];
|
|
}
|
|
|
|
memset(tex, 0, sizeof(*tex));
|
|
|
|
return tex;
|
|
}
|
|
|
|
static struct GLNVGtexture* glnvg__findTexture(struct GLNVGcontext* gl, int id)
|
|
{
|
|
int i;
|
|
for (i = 0; i < gl->ntextures; i++)
|
|
{
|
|
if (gl->textures[i].id.idx == id)
|
|
{
|
|
return &gl->textures[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int glnvg__deleteTexture(struct GLNVGcontext* gl, int id)
|
|
{
|
|
for (int ii = 0; ii < gl->ntextures; ii++)
|
|
{
|
|
if (gl->textures[ii].id.idx == id)
|
|
{
|
|
if (bgfx::isValid(gl->textures[ii].id)
|
|
&& (gl->textures[ii].flags & NVG_IMAGE_NODELETE) == 0)
|
|
{
|
|
bgfx::destroyTexture(gl->textures[ii].id);
|
|
}
|
|
memset(&gl->textures[ii], 0, sizeof(gl->textures[ii]));
|
|
gl->textures[ii].id.idx = bgfx::invalidHandle;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int nvgRenderCreate(void* _userPtr)
|
|
{
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)_userPtr;
|
|
|
|
const bgfx::Memory* vs_nanovg_fill;
|
|
const bgfx::Memory* fs_nanovg_fill;
|
|
|
|
switch (bgfx::getRendererType() )
|
|
{
|
|
case bgfx::RendererType::Direct3D9:
|
|
vs_nanovg_fill = bgfx::makeRef(vs_nanovg_fill_dx9, sizeof(vs_nanovg_fill_dx9) );
|
|
fs_nanovg_fill = bgfx::makeRef(fs_nanovg_fill_dx9, sizeof(fs_nanovg_fill_dx9) );
|
|
break;
|
|
|
|
case bgfx::RendererType::Direct3D11:
|
|
vs_nanovg_fill = bgfx::makeRef(vs_nanovg_fill_dx11, sizeof(vs_nanovg_fill_dx11) );
|
|
fs_nanovg_fill = bgfx::makeRef(fs_nanovg_fill_dx11, sizeof(fs_nanovg_fill_dx11) );
|
|
break;
|
|
|
|
default:
|
|
vs_nanovg_fill = bgfx::makeRef(vs_nanovg_fill_glsl, sizeof(vs_nanovg_fill_glsl) );
|
|
fs_nanovg_fill = bgfx::makeRef(fs_nanovg_fill_glsl, sizeof(fs_nanovg_fill_glsl) );
|
|
break;
|
|
}
|
|
|
|
gl->prog = bgfx::createProgram(
|
|
bgfx::createShader(vs_nanovg_fill)
|
|
, bgfx::createShader(fs_nanovg_fill)
|
|
, true
|
|
);
|
|
|
|
gl->u_scissorMat = bgfx::createUniform("u_scissorMat", bgfx::UniformType::Uniform3x3fv);
|
|
gl->u_paintMat = bgfx::createUniform("u_paintMat", bgfx::UniformType::Uniform3x3fv);
|
|
gl->u_innerCol = bgfx::createUniform("u_innerCol", bgfx::UniformType::Uniform4fv);
|
|
gl->u_outerCol = bgfx::createUniform("u_outerCol", bgfx::UniformType::Uniform4fv);
|
|
gl->u_viewSize = bgfx::createUniform("u_viewSize", bgfx::UniformType::Uniform2fv);
|
|
gl->u_scissorExtScale = bgfx::createUniform("u_scissorExtScale", bgfx::UniformType::Uniform4fv);
|
|
gl->u_extentRadius = bgfx::createUniform("u_extentRadius", bgfx::UniformType::Uniform4fv);
|
|
gl->u_params = bgfx::createUniform("u_params", bgfx::UniformType::Uniform4fv);
|
|
gl->s_tex = bgfx::createUniform("s_tex", bgfx::UniformType::Uniform1i);
|
|
|
|
if (bgfx::getRendererType() == bgfx::RendererType::Direct3D9)
|
|
{
|
|
gl->u_halfTexel = bgfx::createUniform("u_halfTexel", bgfx::UniformType::Uniform4fv);
|
|
}
|
|
else
|
|
{
|
|
gl->u_halfTexel.idx = bgfx::invalidHandle;
|
|
}
|
|
|
|
s_nvgDecl
|
|
.begin()
|
|
.add(bgfx::Attrib::Position, 2, bgfx::AttribType::Float)
|
|
.add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float)
|
|
.end();
|
|
|
|
int align = 16;
|
|
gl->fragSize = sizeof(struct GLNVGfragUniforms) + align - sizeof(struct GLNVGfragUniforms) % align;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int nvgRenderCreateTexture(void* _userPtr, int _type, int _width, int _height, int _flags, const unsigned char* _rgba)
|
|
{
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)_userPtr;
|
|
struct GLNVGtexture* tex = glnvg__allocTexture(gl);
|
|
|
|
if (tex == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
tex->width = _width;
|
|
tex->height = _height;
|
|
tex->type = _type;
|
|
tex->flags = _flags;
|
|
|
|
uint32_t bytesPerPixel = NVG_TEXTURE_RGBA == tex->type ? 4 : 1;
|
|
uint32_t pitch = tex->width * bytesPerPixel;
|
|
|
|
const bgfx::Memory* mem = NULL;
|
|
if (NULL != _rgba)
|
|
{
|
|
mem = bgfx::alloc(tex->height * pitch);
|
|
bgfx::imageSwizzleBgra8(tex->width, tex->height, pitch, _rgba, mem->data);
|
|
}
|
|
|
|
tex->id = bgfx::createTexture2D(tex->width
|
|
, tex->height
|
|
, 1
|
|
, NVG_TEXTURE_RGBA == _type ? bgfx::TextureFormat::BGRA8 : bgfx::TextureFormat::R8
|
|
, BGFX_TEXTURE_NONE
|
|
, mem
|
|
);
|
|
|
|
return tex->id.idx;
|
|
}
|
|
|
|
static int nvgRenderDeleteTexture(void* _userPtr, int image)
|
|
{
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)_userPtr;
|
|
return glnvg__deleteTexture(gl, image);
|
|
}
|
|
|
|
static int nvgRenderUpdateTexture(void* _userPtr, int image, int x, int y, int w, int h, const unsigned char* data)
|
|
{
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)_userPtr;
|
|
struct GLNVGtexture* tex = glnvg__findTexture(gl, image);
|
|
if (tex == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
uint32_t bytesPerPixel = NVG_TEXTURE_RGBA == tex->type ? 4 : 1;
|
|
uint32_t pitch = tex->width * bytesPerPixel;
|
|
|
|
bgfx::updateTexture2D(tex->id, 0, x, y, w, h
|
|
, bgfx::makeRef(data + y*pitch + x*bytesPerPixel, h*pitch)
|
|
, pitch
|
|
);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int nvgRenderGetTextureSize(void* _userPtr, int image, int* w, int* h)
|
|
{
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)_userPtr;
|
|
struct GLNVGtexture* tex = glnvg__findTexture(gl, image);
|
|
|
|
if (!bgfx::isValid(tex->id) )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
*w = tex->width;
|
|
*h = tex->height;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void glnvg__xformIdentity(float* t)
|
|
{
|
|
t[0] = 1.0f; t[1] = 0.0f;
|
|
t[2] = 0.0f; t[3] = 1.0f;
|
|
t[4] = 0.0f; t[5] = 0.0f;
|
|
}
|
|
|
|
static void glnvg__xformInverse(float* inv, float* t)
|
|
{
|
|
double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1];
|
|
if (det > -1e-6 && det < 1e-6) {
|
|
glnvg__xformIdentity(t);
|
|
return;
|
|
}
|
|
invdet = 1.0 / det;
|
|
inv[0] = (float)(t[3] * invdet);
|
|
inv[2] = (float)(-t[2] * invdet);
|
|
inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet);
|
|
inv[1] = (float)(-t[1] * invdet);
|
|
inv[3] = (float)(t[0] * invdet);
|
|
inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet);
|
|
}
|
|
|
|
static void glnvg__xformToMat3x4(float* m3, 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;
|
|
}
|
|
|
|
static int glnvg__convertPaint(struct GLNVGcontext* gl, struct GLNVGfragUniforms* frag, struct NVGpaint* paint,
|
|
struct NVGscissor* scissor, float width, float fringe)
|
|
{
|
|
struct GLNVGtexture* tex = NULL;
|
|
float invxform[6] = {};
|
|
|
|
memset(frag, 0, sizeof(*frag));
|
|
|
|
frag->innerCol = paint->innerColor;
|
|
frag->outerCol = paint->outerColor;
|
|
|
|
glnvg__xformInverse(invxform, paint->xform);
|
|
glnvg__xformToMat3x4(frag->paintMat, invxform);
|
|
|
|
if (scissor->extent[0] < 0.5f || scissor->extent[1] < 0.5f)
|
|
{
|
|
memset(frag->scissorMat, 0, sizeof(frag->scissorMat));
|
|
frag->scissorExt[0] = 1.0f;
|
|
frag->scissorExt[1] = 1.0f;
|
|
frag->scissorScale[0] = 1.0f;
|
|
frag->scissorScale[1] = 1.0f;
|
|
}
|
|
else
|
|
{
|
|
glnvg__xformInverse(invxform, scissor->xform);
|
|
glnvg__xformToMat3x4(frag->scissorMat, invxform);
|
|
frag->scissorExt[0] = scissor->extent[0];
|
|
frag->scissorExt[1] = scissor->extent[1];
|
|
frag->scissorScale[0] = sqrtf(scissor->xform[0]*scissor->xform[0] + scissor->xform[2]*scissor->xform[2]) / fringe;
|
|
frag->scissorScale[1] = 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;
|
|
|
|
bgfx::TextureHandle invalid = BGFX_INVALID_HANDLE;
|
|
gl->th = invalid;
|
|
if (paint->image != 0)
|
|
{
|
|
tex = glnvg__findTexture(gl, paint->image);
|
|
if (tex == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
frag->type = NSVG_SHADER_FILLIMG;
|
|
frag->texType = tex->type == NVG_TEXTURE_RGBA ? 0.0f : 1.0f;
|
|
gl->th = tex->id;
|
|
}
|
|
else
|
|
{
|
|
frag->type = NSVG_SHADER_FILLGRAD;
|
|
frag->radius = paint->radius;
|
|
frag->feather = paint->feather;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void glnvg__mat3(float* dst, float* src)
|
|
{
|
|
dst[0] = src[0];
|
|
dst[1] = src[1];
|
|
dst[2] = src[2];
|
|
|
|
dst[3] = src[4];
|
|
dst[4] = src[5];
|
|
dst[5] = src[6];
|
|
|
|
dst[6] = src[8];
|
|
dst[7] = src[9];
|
|
dst[8] = src[10];
|
|
}
|
|
|
|
static struct GLNVGfragUniforms* nvg__fragUniformPtr(struct GLNVGcontext* gl, int i)
|
|
{
|
|
return (struct GLNVGfragUniforms*)&gl->uniforms[i];
|
|
}
|
|
|
|
static void nvgRenderSetUniforms(struct GLNVGcontext* gl, int uniformOffset, int image)
|
|
{
|
|
struct GLNVGfragUniforms* frag = nvg__fragUniformPtr(gl, uniformOffset);
|
|
float tmp[9]; // Maybe there's a way to get rid of this...
|
|
glnvg__mat3(tmp, frag->scissorMat);
|
|
bgfx::setUniform(gl->u_scissorMat, tmp);
|
|
glnvg__mat3(tmp, frag->paintMat);
|
|
bgfx::setUniform(gl->u_paintMat, tmp);
|
|
|
|
bgfx::setUniform(gl->u_innerCol, frag->innerCol.rgba);
|
|
bgfx::setUniform(gl->u_outerCol, frag->outerCol.rgba);
|
|
bgfx::setUniform(gl->u_scissorExtScale, &frag->scissorExt[0]);
|
|
bgfx::setUniform(gl->u_extentRadius, &frag->extent[0]);
|
|
bgfx::setUniform(gl->u_params, &frag->feather);
|
|
|
|
bgfx::TextureHandle handle = BGFX_INVALID_HANDLE;
|
|
|
|
if (image != 0)
|
|
{
|
|
struct GLNVGtexture* tex = glnvg__findTexture(gl, image);
|
|
if (tex != NULL)
|
|
{
|
|
handle = tex->id;
|
|
|
|
if (bgfx::isValid(gl->u_halfTexel) )
|
|
{
|
|
float halfTexel[4] = { 0.5f / tex->width, 0.5f / tex->height };
|
|
bgfx::setUniform(gl->u_halfTexel, halfTexel);
|
|
}
|
|
}
|
|
}
|
|
|
|
gl->th = handle;
|
|
}
|
|
|
|
static void nvgRenderViewport(void* _userPtr, int width, int height)
|
|
{
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)_userPtr;
|
|
gl->view[0] = (float)width;
|
|
gl->view[1] = (float)height;
|
|
bgfx::setViewRect(gl->viewid, 0, 0, width, height);
|
|
}
|
|
|
|
static void fan(uint32_t _start, uint32_t _count)
|
|
{
|
|
uint32_t numTris = _count-2;
|
|
bgfx::TransientIndexBuffer tib;
|
|
bgfx::allocTransientIndexBuffer(&tib, numTris*3);
|
|
uint16_t* data = (uint16_t*)tib.data;
|
|
for (uint32_t ii = 0; ii < numTris; ++ii)
|
|
{
|
|
data[ii*3+0] = _start;
|
|
data[ii*3+1] = _start + ii + 1;
|
|
data[ii*3+2] = _start + ii + 2;
|
|
}
|
|
|
|
bgfx::setIndexBuffer(&tib);
|
|
}
|
|
|
|
static void glnvg__fill(struct GLNVGcontext* gl, struct GLNVGcall* call)
|
|
{
|
|
struct GLNVGpath* paths = &gl->paths[call->pathOffset];
|
|
int i, npaths = call->pathCount;
|
|
|
|
// set bindpoint for solid loc
|
|
nvgRenderSetUniforms(gl, call->uniformOffset, 0);
|
|
|
|
for (i = 0; i < npaths; i++)
|
|
{
|
|
if (2 < paths[i].fillCount)
|
|
{
|
|
bgfx::setProgram(gl->prog);
|
|
bgfx::setState(0);
|
|
bgfx::setStencil(0
|
|
| BGFX_STENCIL_TEST_ALWAYS
|
|
| BGFX_STENCIL_FUNC_RMASK(0xff)
|
|
| BGFX_STENCIL_OP_FAIL_S_KEEP
|
|
| BGFX_STENCIL_OP_FAIL_Z_KEEP
|
|
| BGFX_STENCIL_OP_PASS_Z_INCR
|
|
, 0
|
|
| BGFX_STENCIL_TEST_ALWAYS
|
|
| BGFX_STENCIL_FUNC_RMASK(0xff)
|
|
| BGFX_STENCIL_OP_FAIL_S_KEEP
|
|
| BGFX_STENCIL_OP_FAIL_Z_KEEP
|
|
| BGFX_STENCIL_OP_PASS_Z_DECR
|
|
);
|
|
bgfx::setVertexBuffer(&gl->tvb);
|
|
bgfx::setTexture(0, gl->s_tex, gl->th);
|
|
fan(paths[i].fillOffset, paths[i].fillCount);
|
|
bgfx::submit(gl->viewid);
|
|
}
|
|
}
|
|
|
|
// Draw aliased off-pixels
|
|
nvgRenderSetUniforms(gl, call->uniformOffset + gl->fragSize, call->image);
|
|
|
|
if (gl->edgeAntiAlias)
|
|
{
|
|
// Draw fringes
|
|
for (i = 0; i < npaths; i++)
|
|
{
|
|
bgfx::setProgram(gl->prog);
|
|
bgfx::setState(gl->state
|
|
| BGFX_STATE_PT_TRISTRIP
|
|
);
|
|
bgfx::setStencil(0
|
|
| BGFX_STENCIL_TEST_EQUAL
|
|
| BGFX_STENCIL_FUNC_RMASK(0xff)
|
|
| BGFX_STENCIL_OP_FAIL_S_KEEP
|
|
| BGFX_STENCIL_OP_FAIL_Z_KEEP
|
|
| BGFX_STENCIL_OP_PASS_Z_KEEP
|
|
);
|
|
bgfx::setVertexBuffer(&gl->tvb, paths[i].strokeOffset, paths[i].strokeCount);
|
|
bgfx::setTexture(0, gl->s_tex, gl->th);
|
|
bgfx::submit(gl->viewid);
|
|
}
|
|
}
|
|
|
|
// Draw fill
|
|
bgfx::setProgram(gl->prog);
|
|
bgfx::setState(gl->state);
|
|
bgfx::setVertexBuffer(&gl->tvb, call->vertexOffset, call->vertexCount);
|
|
bgfx::setTexture(0, gl->s_tex, gl->th);
|
|
bgfx::setStencil(0
|
|
| BGFX_STENCIL_TEST_NOTEQUAL
|
|
| BGFX_STENCIL_FUNC_RMASK(0xff)
|
|
| BGFX_STENCIL_OP_FAIL_S_ZERO
|
|
| BGFX_STENCIL_OP_FAIL_Z_ZERO
|
|
| BGFX_STENCIL_OP_PASS_Z_ZERO
|
|
);
|
|
bgfx::submit(gl->viewid);
|
|
}
|
|
|
|
static void glnvg__convexFill(struct GLNVGcontext* gl, struct GLNVGcall* call)
|
|
{
|
|
struct GLNVGpath* paths = &gl->paths[call->pathOffset];
|
|
int i, npaths = call->pathCount;
|
|
|
|
nvgRenderSetUniforms(gl, call->uniformOffset, call->image);
|
|
|
|
for (i = 0; i < npaths; i++)
|
|
{
|
|
bgfx::setProgram(gl->prog);
|
|
bgfx::setState(gl->state);
|
|
bgfx::setVertexBuffer(&gl->tvb);
|
|
bgfx::setTexture(0, gl->s_tex, gl->th);
|
|
fan(paths[i].fillOffset, paths[i].fillCount);
|
|
bgfx::submit(gl->viewid);
|
|
}
|
|
|
|
if (gl->edgeAntiAlias)
|
|
{
|
|
// Draw fringes
|
|
for (i = 0; i < npaths; i++)
|
|
{
|
|
bgfx::setProgram(gl->prog);
|
|
bgfx::setState(gl->state
|
|
| BGFX_STATE_PT_TRISTRIP
|
|
);
|
|
bgfx::setVertexBuffer(&gl->tvb, paths[i].strokeOffset, paths[i].strokeCount);
|
|
bgfx::setTexture(0, gl->s_tex, gl->th);
|
|
bgfx::submit(gl->viewid);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void glnvg__stroke(struct GLNVGcontext* gl, struct GLNVGcall* call)
|
|
{
|
|
struct GLNVGpath* paths = &gl->paths[call->pathOffset];
|
|
int npaths = call->pathCount, i;
|
|
|
|
nvgRenderSetUniforms(gl, call->uniformOffset, call->image);
|
|
|
|
// Draw Strokes
|
|
for (i = 0; i < npaths; i++)
|
|
{
|
|
bgfx::setProgram(gl->prog);
|
|
bgfx::setState(gl->state
|
|
| BGFX_STATE_PT_TRISTRIP
|
|
);
|
|
bgfx::setVertexBuffer(&gl->tvb, paths[i].strokeOffset, paths[i].strokeCount);
|
|
bgfx::setTexture(0, gl->s_tex, gl->th);
|
|
bgfx::setTexture(0, gl->s_tex, gl->th);
|
|
bgfx::submit(gl->viewid);
|
|
}
|
|
}
|
|
|
|
static void glnvg__triangles(struct GLNVGcontext* gl, struct GLNVGcall* call)
|
|
{
|
|
if (3 <= call->vertexCount)
|
|
{
|
|
nvgRenderSetUniforms(gl, call->uniformOffset, call->image);
|
|
|
|
bgfx::setProgram(gl->prog);
|
|
bgfx::setState(gl->state);
|
|
bgfx::setVertexBuffer(&gl->tvb, call->vertexOffset, call->vertexCount);
|
|
bgfx::setTexture(0, gl->s_tex, gl->th);
|
|
bgfx::submit(gl->viewid);
|
|
}
|
|
}
|
|
|
|
static void nvgRenderFlush(void* _userPtr)
|
|
{
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)_userPtr;
|
|
|
|
if (gl->ncalls > 0)
|
|
{
|
|
bgfx::allocTransientVertexBuffer(&gl->tvb, gl->nverts, s_nvgDecl);
|
|
|
|
int allocated = gl->tvb.size/gl->tvb.stride;
|
|
|
|
if (allocated < gl->nverts) {
|
|
gl->nverts = allocated;
|
|
BX_WARN(true, "Vertex number truncated due to transient vertex buffer overflow");
|
|
}
|
|
|
|
|
|
memcpy(gl->tvb.data, gl->verts, gl->nverts * sizeof(struct NVGvertex) );
|
|
|
|
gl->state = 0
|
|
| BGFX_STATE_RGB_WRITE
|
|
| BGFX_STATE_ALPHA_WRITE
|
|
;
|
|
|
|
// if (alphaBlend == NVG_PREMULTIPLIED_ALPHA)
|
|
// {
|
|
// gl->state |= BGFX_STATE_BLEND_FUNC_SEPARATE(
|
|
// BGFX_STATE_BLEND_SRC_ALPHA, BGFX_STATE_BLEND_INV_SRC_ALPHA
|
|
// , BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_INV_SRC_ALPHA
|
|
// );
|
|
// }
|
|
// else
|
|
{
|
|
gl->state |= BGFX_STATE_BLEND_FUNC(
|
|
BGFX_STATE_BLEND_SRC_ALPHA, BGFX_STATE_BLEND_INV_SRC_ALPHA
|
|
);
|
|
}
|
|
|
|
bgfx::setUniform(gl->u_viewSize, gl->view);
|
|
|
|
for (uint32_t ii = 0, num = gl->ncalls; ii < num; ++ii)
|
|
{
|
|
struct GLNVGcall* call = &gl->calls[ii];
|
|
switch (call->type)
|
|
{
|
|
case GLNVG_FILL:
|
|
glnvg__fill(gl, call);
|
|
break;
|
|
|
|
case GLNVG_CONVEXFILL:
|
|
glnvg__convexFill(gl, call);
|
|
break;
|
|
|
|
case GLNVG_STROKE:
|
|
glnvg__stroke(gl, call);
|
|
break;
|
|
|
|
case GLNVG_TRIANGLES:
|
|
glnvg__triangles(gl, call);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Reset calls
|
|
gl->nverts = 0;
|
|
gl->npaths = 0;
|
|
gl->ncalls = 0;
|
|
gl->nuniforms = 0;
|
|
}
|
|
|
|
static int glnvg__maxVertCount(const struct NVGpath* paths, int npaths)
|
|
{
|
|
int i, count = 0;
|
|
for (i = 0; i < npaths; i++)
|
|
{
|
|
count += paths[i].nfill;
|
|
count += paths[i].nstroke;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static int glnvg__maxi(int a, int b) { return a > b ? a : b; }
|
|
|
|
static struct GLNVGcall* glnvg__allocCall(struct GLNVGcontext* gl)
|
|
{
|
|
struct GLNVGcall* ret = NULL;
|
|
if (gl->ncalls+1 > gl->ccalls)
|
|
{
|
|
gl->ccalls = gl->ccalls == 0 ? 32 : gl->ccalls * 2;
|
|
gl->calls = (struct GLNVGcall*)realloc(gl->calls, sizeof(struct GLNVGcall) * gl->ccalls);
|
|
}
|
|
ret = &gl->calls[gl->ncalls++];
|
|
memset(ret, 0, sizeof(struct GLNVGcall));
|
|
return ret;
|
|
}
|
|
|
|
static int glnvg__allocPaths(struct GLNVGcontext* gl, int n)
|
|
{
|
|
int ret = 0;
|
|
if (gl->npaths + n > gl->cpaths) {
|
|
GLNVGpath* paths;
|
|
int cpaths = glnvg__maxi(gl->npaths + n, 128) + gl->cpaths / 2; // 1.5x Overallocate
|
|
paths = (GLNVGpath*)realloc(gl->paths, sizeof(GLNVGpath) * cpaths);
|
|
if (paths == NULL) return -1;
|
|
gl->paths = paths;
|
|
gl->cpaths = cpaths;
|
|
}
|
|
ret = gl->npaths;
|
|
gl->npaths += n;
|
|
return ret;
|
|
}
|
|
|
|
static int glnvg__allocVerts(GLNVGcontext* gl, int n)
|
|
{
|
|
int ret = 0;
|
|
if (gl->nverts+n > gl->cverts)
|
|
{
|
|
NVGvertex* verts;
|
|
int cverts = glnvg__maxi(gl->nverts + n, 4096) + gl->cverts/2; // 1.5x Overallocate
|
|
verts = (NVGvertex*)realloc(gl->verts, sizeof(NVGvertex) * cverts);
|
|
if (verts == NULL) return -1;
|
|
gl->verts = verts;
|
|
gl->cverts = cverts;
|
|
}
|
|
ret = gl->nverts;
|
|
gl->nverts += n;
|
|
return ret;
|
|
}
|
|
|
|
static int glnvg__allocFragUniforms(struct GLNVGcontext* gl, int n)
|
|
{
|
|
int ret = 0, structSize = gl->fragSize;
|
|
if (gl->nuniforms+n > gl->cuniforms)
|
|
{
|
|
gl->cuniforms = gl->cuniforms == 0 ? glnvg__maxi(n, 32) : gl->cuniforms * 2;
|
|
gl->uniforms = (unsigned char*)realloc(gl->uniforms, gl->cuniforms * structSize);
|
|
}
|
|
ret = gl->nuniforms * structSize;
|
|
gl->nuniforms += n;
|
|
return ret;
|
|
}
|
|
|
|
static void glnvg__vset(struct NVGvertex* vtx, float x, float y, float u, float v)
|
|
{
|
|
vtx->x = x;
|
|
vtx->y = y;
|
|
vtx->u = u;
|
|
vtx->v = v;
|
|
}
|
|
|
|
static void nvgRenderFill(void* _userPtr, struct NVGpaint* paint, struct NVGscissor* scissor, float fringe,
|
|
const float* bounds, const struct NVGpath* paths, int npaths)
|
|
{
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)_userPtr;
|
|
|
|
struct GLNVGcall* call = glnvg__allocCall(gl);
|
|
struct NVGvertex* quad;
|
|
struct GLNVGfragUniforms* frag;
|
|
int i, maxverts, offset;
|
|
|
|
call->type = GLNVG_FILL;
|
|
call->pathOffset = glnvg__allocPaths(gl, npaths);
|
|
call->pathCount = npaths;
|
|
call->image = paint->image;
|
|
|
|
if (npaths == 1 && paths[0].convex)
|
|
{
|
|
call->type = GLNVG_CONVEXFILL;
|
|
}
|
|
|
|
// Allocate vertices for all the paths.
|
|
maxverts = glnvg__maxVertCount(paths, npaths) + 6;
|
|
offset = glnvg__allocVerts(gl, maxverts);
|
|
|
|
for (i = 0; i < npaths; i++)
|
|
{
|
|
struct GLNVGpath* copy = &gl->paths[call->pathOffset + i];
|
|
const struct NVGpath* path = &paths[i];
|
|
memset(copy, 0, sizeof(struct GLNVGpath));
|
|
if (path->nfill > 0)
|
|
{
|
|
copy->fillOffset = offset;
|
|
copy->fillCount = path->nfill;
|
|
memcpy(&gl->verts[offset], path->fill, sizeof(struct NVGvertex) * path->nfill);
|
|
offset += path->nfill;
|
|
}
|
|
|
|
if (path->nstroke > 0)
|
|
{
|
|
copy->strokeOffset = offset;
|
|
copy->strokeCount = path->nstroke;
|
|
memcpy(&gl->verts[offset], path->stroke, sizeof(struct NVGvertex) * path->nstroke);
|
|
offset += path->nstroke;
|
|
}
|
|
}
|
|
|
|
// Quad
|
|
call->vertexOffset = offset;
|
|
call->vertexCount = 6;
|
|
quad = &gl->verts[call->vertexOffset];
|
|
glnvg__vset(&quad[0], bounds[0], bounds[3], 0.5f, 1.0f);
|
|
glnvg__vset(&quad[1], bounds[2], bounds[3], 0.5f, 1.0f);
|
|
glnvg__vset(&quad[2], bounds[2], bounds[1], 0.5f, 1.0f);
|
|
|
|
glnvg__vset(&quad[3], bounds[0], bounds[3], 0.5f, 1.0f);
|
|
glnvg__vset(&quad[4], bounds[2], bounds[1], 0.5f, 1.0f);
|
|
glnvg__vset(&quad[5], bounds[0], bounds[1], 0.5f, 1.0f);
|
|
|
|
// Setup uniforms for draw calls
|
|
if (call->type == GLNVG_FILL)
|
|
{
|
|
call->uniformOffset = glnvg__allocFragUniforms(gl, 2);
|
|
// Simple shader for stencil
|
|
frag = nvg__fragUniformPtr(gl, call->uniformOffset);
|
|
memset(frag, 0, sizeof(*frag));
|
|
frag->type = NSVG_SHADER_SIMPLE;
|
|
// Fill shader
|
|
glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, fringe, fringe);
|
|
}
|
|
else
|
|
{
|
|
call->uniformOffset = glnvg__allocFragUniforms(gl, 1);
|
|
// Fill shader
|
|
glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, fringe, fringe);
|
|
}
|
|
}
|
|
|
|
static void nvgRenderStroke(void* _userPtr, struct NVGpaint* paint, struct NVGscissor* scissor, float fringe,
|
|
float strokeWidth, const struct NVGpath* paths, int npaths)
|
|
{
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)_userPtr;
|
|
|
|
struct GLNVGcall* call = glnvg__allocCall(gl);
|
|
int i, maxverts, offset;
|
|
|
|
call->type = GLNVG_STROKE;
|
|
call->pathOffset = glnvg__allocPaths(gl, npaths);
|
|
call->pathCount = npaths;
|
|
call->image = paint->image;
|
|
|
|
// Allocate vertices for all the paths.
|
|
maxverts = glnvg__maxVertCount(paths, npaths);
|
|
offset = glnvg__allocVerts(gl, maxverts);
|
|
|
|
for (i = 0; i < npaths; i++)
|
|
{
|
|
struct GLNVGpath* copy = &gl->paths[call->pathOffset + i];
|
|
const struct NVGpath* path = &paths[i];
|
|
memset(copy, 0, sizeof(struct GLNVGpath));
|
|
if (path->nstroke)
|
|
{
|
|
copy->strokeOffset = offset;
|
|
copy->strokeCount = path->nstroke;
|
|
memcpy(&gl->verts[offset], path->stroke, sizeof(struct NVGvertex) * path->nstroke);
|
|
offset += path->nstroke;
|
|
}
|
|
}
|
|
|
|
// Fill shader
|
|
call->uniformOffset = glnvg__allocFragUniforms(gl, 1);
|
|
glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, strokeWidth, fringe);
|
|
}
|
|
|
|
static void nvgRenderTriangles(void* _userPtr, struct NVGpaint* paint, struct NVGscissor* scissor,
|
|
const struct NVGvertex* verts, int nverts)
|
|
{
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)_userPtr;
|
|
struct GLNVGcall* call = glnvg__allocCall(gl);
|
|
struct GLNVGfragUniforms* frag;
|
|
|
|
call->type = GLNVG_TRIANGLES;
|
|
call->image = paint->image;
|
|
|
|
// Allocate vertices for all the paths.
|
|
call->vertexOffset = glnvg__allocVerts(gl, nverts);
|
|
call->vertexCount = nverts;
|
|
memcpy(&gl->verts[call->vertexOffset], verts, sizeof(struct NVGvertex) * nverts);
|
|
|
|
// Fill shader
|
|
call->uniformOffset = glnvg__allocFragUniforms(gl, 1);
|
|
frag = nvg__fragUniformPtr(gl, call->uniformOffset);
|
|
glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, 1.0f);
|
|
frag->type = NSVG_SHADER_IMG;
|
|
}
|
|
|
|
static void nvgRenderDelete(void* _userPtr)
|
|
{
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)_userPtr;
|
|
|
|
if (gl == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
bgfx::destroyProgram(gl->prog);
|
|
|
|
bgfx::destroyUniform(gl->u_scissorMat);
|
|
bgfx::destroyUniform(gl->u_paintMat);
|
|
bgfx::destroyUniform(gl->u_innerCol);
|
|
bgfx::destroyUniform(gl->u_outerCol);
|
|
bgfx::destroyUniform(gl->u_viewSize);
|
|
bgfx::destroyUniform(gl->u_scissorExtScale);
|
|
bgfx::destroyUniform(gl->u_extentRadius);
|
|
bgfx::destroyUniform(gl->u_params);
|
|
bgfx::destroyUniform(gl->s_tex);
|
|
|
|
if (bgfx::isValid(gl->u_halfTexel) )
|
|
{
|
|
bgfx::destroyUniform(gl->u_halfTexel);
|
|
}
|
|
|
|
for (uint32_t ii = 0, num = gl->ntextures; ii < num; ++ii)
|
|
{
|
|
if (bgfx::isValid(gl->textures[ii].id)
|
|
&& (gl->textures[ii].flags & NVG_IMAGE_NODELETE) == 0)
|
|
{
|
|
bgfx::destroyTexture(gl->textures[ii].id);
|
|
}
|
|
}
|
|
|
|
free(gl->textures);
|
|
|
|
free(gl);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
NVGcontext* nvgCreate(int edgeaa, unsigned char viewid)
|
|
{
|
|
struct NVGparams params;
|
|
struct NVGcontext* ctx = NULL;
|
|
struct GLNVGcontext* gl = (struct GLNVGcontext*)malloc(sizeof(struct GLNVGcontext));
|
|
if (gl == NULL) goto error;
|
|
memset(gl, 0, sizeof(struct GLNVGcontext));
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
params.renderCreate = nvgRenderCreate;
|
|
params.renderCreateTexture = nvgRenderCreateTexture;
|
|
params.renderDeleteTexture = nvgRenderDeleteTexture;
|
|
params.renderUpdateTexture = nvgRenderUpdateTexture;
|
|
params.renderGetTextureSize = nvgRenderGetTextureSize;
|
|
params.renderViewport = nvgRenderViewport;
|
|
params.renderFlush = nvgRenderFlush;
|
|
params.renderFill = nvgRenderFill;
|
|
params.renderStroke = nvgRenderStroke;
|
|
params.renderTriangles = nvgRenderTriangles;
|
|
params.renderDelete = nvgRenderDelete;
|
|
params.userPtr = gl;
|
|
params.edgeAntiAlias = edgeaa;
|
|
|
|
gl->edgeAntiAlias = edgeaa;
|
|
gl->viewid = uint8_t(viewid);
|
|
|
|
ctx = nvgCreateInternal(¶ms);
|
|
if (ctx == NULL) goto error;
|
|
|
|
return ctx;
|
|
|
|
error:
|
|
// 'gl' is freed by nvgDeleteInternal.
|
|
if (ctx != NULL)
|
|
{
|
|
nvgDeleteInternal(ctx);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void nvgDelete(struct NVGcontext* ctx)
|
|
{
|
|
nvgDeleteInternal(ctx);
|
|
}
|