313 lines
6.9 KiB
GLSL
313 lines
6.9 KiB
GLSL
|
|
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
|
|
|
precision mediump float;
|
|
layout(std430) buffer;
|
|
|
|
layout(binding = 0) restrict readonly buffer elementBufferSSBO
|
|
{
|
|
mg_gl_path_elt elements[];
|
|
} elementBuffer;
|
|
|
|
layout(binding = 1) coherent restrict buffer segmentCountBufferSSBO
|
|
{
|
|
int elements[];
|
|
} segmentCountBuffer;
|
|
|
|
layout(binding = 2) restrict buffer segmentBufferSSBO
|
|
{
|
|
mg_gl_segment elements[];
|
|
} segmentBuffer;
|
|
|
|
layout(binding = 3) coherent restrict buffer tileOpCountBufferSSBO
|
|
{
|
|
int elements[];
|
|
} tileOpCountBuffer;
|
|
|
|
layout(binding = 4) restrict buffer tileOpBufferSSBO
|
|
{
|
|
mg_gl_tile_op elements[];
|
|
} tileOpBuffer;
|
|
|
|
layout(binding = 5) coherent restrict buffer tileQueuesBufferSSBO
|
|
{
|
|
mg_gl_tile_queue elements[];
|
|
} tileQueuesBuffer;
|
|
|
|
layout(location = 0) uniform float scale;
|
|
layout(location = 1) uniform uint tileSize;
|
|
layout(location = 2) uniform ivec2 nTiles;
|
|
|
|
int push_segment(in vec2 p[4], int kind)
|
|
{
|
|
int segIndex = atomicAdd(segmentCountBuffer.elements[0], 1);
|
|
|
|
vec2 s = p[0];
|
|
vec2 c = p[0];
|
|
vec2 e = p[1];
|
|
|
|
bool goingUp = e.y >= s.y;
|
|
bool goingRight = e.x >= s.x;
|
|
|
|
vec4 box = vec4(min(s.x, e.x),
|
|
min(s.y, e.y),
|
|
max(s.x, e.x),
|
|
max(s.y, e.y));
|
|
|
|
segmentBuffer.elements[segIndex].kind = kind;
|
|
segmentBuffer.elements[segIndex].pathIndex = 0; ///
|
|
segmentBuffer.elements[segIndex].windingIncrement = goingUp ? 1 : -1;
|
|
segmentBuffer.elements[segIndex].box = box;
|
|
|
|
float dx = c.x - box.x;
|
|
float dy = c.y - box.y;
|
|
float alpha = (box.w - box.y)/(box.z - box.x);
|
|
float ofs = box.w - box.y;
|
|
|
|
if(goingUp == goingRight)
|
|
{
|
|
if(kind == MG_GL_LINE)
|
|
{
|
|
segmentBuffer.elements[segIndex].config = MG_GL_BR;
|
|
}
|
|
else if(dy > alpha*dx)
|
|
{
|
|
segmentBuffer.elements[segIndex].config = MG_GL_TL;
|
|
}
|
|
else
|
|
{
|
|
segmentBuffer.elements[segIndex].config = MG_GL_BR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(kind == MG_GL_LINE)
|
|
{
|
|
segmentBuffer.elements[segIndex].config = MG_GL_TR;
|
|
}
|
|
else if(dy < ofs - alpha*dx)
|
|
{
|
|
segmentBuffer.elements[segIndex].config = MG_GL_BL;
|
|
}
|
|
else
|
|
{
|
|
segmentBuffer.elements[segIndex].config = MG_GL_TR;
|
|
}
|
|
}
|
|
|
|
return(segIndex);
|
|
}
|
|
|
|
float ccw(vec2 a, vec2 b, vec2 c)
|
|
{
|
|
return((b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x));
|
|
}
|
|
|
|
int side_of_segment(vec2 p, mg_gl_segment seg)
|
|
{
|
|
int side = 0;
|
|
if(p.y > seg.box.w || p.y <= seg.box.y)
|
|
{
|
|
if(p.x > seg.box.x && p.x <= seg.box.z)
|
|
{
|
|
if(p.y > seg.box.w)
|
|
{
|
|
side = (seg.config == MG_GL_TL || seg.config == MG_GL_BR)? -1 : 1;
|
|
}
|
|
else
|
|
{
|
|
side = (seg.config == MG_GL_TL || seg.config == MG_GL_BR)? 1 : -1;
|
|
}
|
|
}
|
|
}
|
|
else if(p.x > seg.box.z)
|
|
{
|
|
side = 1;
|
|
}
|
|
else if(p.x <= seg.box.x)
|
|
{
|
|
side = -1;
|
|
}
|
|
else
|
|
{
|
|
vec2 a, b, c;
|
|
switch(seg.config)
|
|
{
|
|
case MG_GL_TL:
|
|
a = seg.box.xy;
|
|
b = seg.box.zw;
|
|
break;
|
|
|
|
case MG_GL_BR:
|
|
a = seg.box.zw;
|
|
b = seg.box.xy;
|
|
break;
|
|
|
|
case MG_GL_TR:
|
|
a = seg.box.xw;
|
|
b = seg.box.zy;
|
|
break;
|
|
|
|
case MG_GL_BL:
|
|
a = seg.box.zy;
|
|
b = seg.box.xw;
|
|
break;
|
|
}
|
|
c = seg.hullVertex;
|
|
|
|
if(ccw(a, b, p) < 0)
|
|
{
|
|
// other side of the diagonal
|
|
side = (seg.config == MG_GL_BR || seg.config == MG_GL_TR) ? -1 : 1;
|
|
}
|
|
else if(ccw(b, c, p) < 0 || ccw(c, a, p) < 0)
|
|
{
|
|
// same side of the diagonal, but outside curve hull
|
|
side = (seg.config == MG_GL_BL || seg.config == MG_GL_TL) ? -1 : 1;
|
|
}
|
|
else
|
|
{
|
|
// inside curve hull
|
|
switch(seg.kind)
|
|
{
|
|
case MG_GL_LINE:
|
|
side = 1;
|
|
break;
|
|
|
|
case MG_GL_QUADRATIC:
|
|
{
|
|
vec3 ph = {p.x, p.y, 1};
|
|
vec3 klm = seg.implicitMatrix * ph;
|
|
side = ((klm.x*klm.x - klm.y)*klm.z < 0)? -1 : 1;
|
|
} break;
|
|
|
|
case MG_GL_CUBIC:
|
|
{
|
|
vec3 ph = {p.x, p.y, 1};
|
|
vec3 klm = seg.implicitMatrix * ph;
|
|
side = (seg.sign*(klm.x*klm.x*klm.x - klm.y*klm.z) < 0)? -1 : 1;
|
|
} break;
|
|
}
|
|
}
|
|
}
|
|
return(side);
|
|
}
|
|
|
|
void bin_to_tiles(int segIndex)
|
|
{
|
|
//NOTE: add segment index to the queues of tiles it overlaps with
|
|
const mg_gl_segment seg = segmentBuffer.elements[segIndex];
|
|
|
|
ivec4 pathArea = ivec4(0, 0, nTiles.x, nTiles.y);
|
|
|
|
ivec4 coveredTiles = ivec4(seg.box)/int(tileSize);
|
|
int xMin = max(0, coveredTiles.x - pathArea.x);
|
|
int yMin = max(0, coveredTiles.y - pathArea.y);
|
|
int xMax = min(coveredTiles.z - pathArea.x, pathArea.z-1);
|
|
int yMax = min(coveredTiles.w - pathArea.y, pathArea.w-1);
|
|
|
|
for(int y = yMin; y <= yMax; y++)
|
|
{
|
|
for(int x = xMin ; x <= xMax; x++)
|
|
{
|
|
vec4 tileBox = vec4(float(x + pathArea.x),
|
|
float(y + pathArea.y),
|
|
float(x + pathArea.x + 1),
|
|
float(y + pathArea.y + 1)) * float(tileSize);
|
|
|
|
vec2 bl = {tileBox.x, tileBox.y};
|
|
vec2 br = {tileBox.z, tileBox.y};
|
|
vec2 tr = {tileBox.z, tileBox.w};
|
|
vec2 tl = {tileBox.x, tileBox.w};
|
|
|
|
int sbl = side_of_segment(bl, seg);
|
|
int sbr = side_of_segment(br, seg);
|
|
int str = side_of_segment(tr, seg);
|
|
int stl = side_of_segment(tl, seg);
|
|
|
|
bool crossL = (stl*sbl < 0);
|
|
bool crossR = (str*sbr < 0);
|
|
bool crossT = (stl*str < 0);
|
|
bool crossB = (sbl*sbr < 0);
|
|
|
|
vec2 s0, s1;
|
|
if(seg.config == MG_GL_TL||seg.config == MG_GL_BR)
|
|
{
|
|
s0 = seg.box.xy;
|
|
s1 = seg.box.zw;
|
|
}
|
|
else
|
|
{
|
|
s0 = seg.box.xw;
|
|
s1 = seg.box.zy;
|
|
}
|
|
bool s0Inside = s0.x >= tileBox.x
|
|
&& s0.x < tileBox.z
|
|
&& s0.y >= tileBox.y
|
|
&& s0.y < tileBox.w;
|
|
|
|
bool s1Inside = s1.x >= tileBox.x
|
|
&& s1.x < tileBox.z
|
|
&& s1.y >= tileBox.y
|
|
&& s1.y < tileBox.w;
|
|
|
|
if(crossL || crossR || crossT || crossB || s0Inside || s1Inside)
|
|
{
|
|
int tileOpIndex = atomicAdd(tileOpCountBuffer.elements[0], 1);
|
|
|
|
tileOpBuffer.elements[tileOpIndex].kind = MG_GL_OP_SEGMENT;
|
|
tileOpBuffer.elements[tileOpIndex].index = segIndex;
|
|
tileOpBuffer.elements[tileOpIndex].crossRight = false;
|
|
tileOpBuffer.elements[tileOpIndex].next = -1;
|
|
|
|
int tileIndex = y*pathArea.z + x;
|
|
|
|
tileOpBuffer.elements[tileOpIndex].next = atomicExchange(tileQueuesBuffer.elements[tileIndex].first, tileOpIndex);
|
|
if(tileOpBuffer.elements[tileOpIndex].next == -1)
|
|
{
|
|
tileQueuesBuffer.elements[tileIndex].last = tileOpIndex;
|
|
}
|
|
|
|
//NOTE: if the segment crosses the tile's bottom boundary, update the tile's winding offset
|
|
if(crossB)
|
|
{
|
|
atomicAdd(tileQueuesBuffer.elements[tileIndex].windingOffset, seg.windingIncrement);
|
|
}
|
|
|
|
//NOTE: if the segment crosses the right boundary, mark it.
|
|
if(crossR)
|
|
{
|
|
tileOpBuffer.elements[tileOpIndex].crossRight = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void line_setup(vec2 p[4])
|
|
{
|
|
int segIndex = push_segment(p, MG_GL_LINE);
|
|
segmentBuffer.elements[segIndex].hullVertex = p[0];
|
|
|
|
bin_to_tiles(segIndex);
|
|
}
|
|
|
|
void main()
|
|
{
|
|
int eltIndex = int(gl_WorkGroupID.x);
|
|
|
|
mg_gl_path_elt elt = elementBuffer.elements[eltIndex];
|
|
|
|
switch(elt.kind)
|
|
{
|
|
case MG_GL_LINE:
|
|
{
|
|
vec2 p[4] = {elt.p[0]*scale, elt.p[1]*scale, vec2(0), vec2(0)};
|
|
line_setup(p);
|
|
} break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|