- Move and Zoom perf_text example with the mouse

- Reverted to using 4 bits of subpixel precision and computing cross products with 32 bits integers, for perf reasons
- Fixed some confusion in tile array size vs number of elements (could still clean-up naming a bit though)
- Fixed bug where triangles on the left or below canvas boundaries where bucketted to the first colum/row of tiles
- Fixed bug in mouse button message handling (was always sending press on left button)
This commit is contained in:
martinfouilleul 2023-02-09 13:09:41 +01:00
parent 833767d6e2
commit 4a8c77f023
6 changed files with 167 additions and 78 deletions

View File

@ -10,32 +10,54 @@
#define LOG_SUBSYSTEM "Main"
static const char* TEST_STRING =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla quam enim, aliquam in placerat luctus, rutrum in quam.\n" \
"Cras urna elit, pellentesque ac ipsum at, lobortis scelerisque eros. Aenean et turpis nibh. Maecenas lectus augue, eleifend\n" \
"nec efficitur eu, faucibus eget turpis. Suspendisse vel nulla mi. Duis imperdiet neque orci, ac ultrices orci molestie a.\n"
"Etiam malesuada vulputate hendrerit. Cras ultricies diam in lectus finibus, eu laoreet diam rutrum.\n" \
"\n" \
"Etiam dictum orci arcu, ac fermentum leo dapibus lacinia. Integer vitae elementum ex. Vestibulum tempor nunc eu hendrerit\n" \
"ornare. Nunc pretium ligula sit amet massa pulvinar, vitae imperdiet justo bibendum. Maecenas consectetur elementum mi, sed\n" \
"vehicula neque pulvinar sit amet. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tortor erat, accumsan in laoreet\n" \
"quis, placerat nec enim. Nulla facilisi. Morbi vitae nibh ligula. Suspendisse in molestie magna, eget aliquet mauris. Sed \n" \
"aliquam faucibus magna.\n" \
"\n" \
"Sed metus odio, imperdiet et consequat non, faucibus nec risus. Suspendisse facilisis sem neque, id scelerisque dui mattis sit\n" \
"amet. Nullam tincidunt nisl nec dui dignissim mattis. Proin fermentum ornare ipsum. Proin eleifend, mi vitae porttitor placerat,\n" \
"neque magna elementum turpis, eu aliquet mi urna et leo. Pellentesque interdum est mauris, sed pellentesque risus blandit in.\n" \
"Phasellus dignissim consequat eros, at aliquam elit finibus posuere. Proin suscipit tortor leo, id vulputate odio lobortis in.\n" \
"Vestibulum et orci ligula. Sed scelerisque nunc non nisi aliquam, vel eleifend felis suscipit. Integer posuere sapien elit, \n" \
"lacinia ultricies nibh sodales nec.\n" \
"\n" \
"Etiam aliquam purus sit amet purus ultricies tristique. Nunc maximus nunc quis magna ornare, vel interdum urna fermentum.\n" \
"Vestibulum cursus nisl ut nulla egestas, quis mattis elit venenatis. Praesent malesuada mi non magna aliquam fringilla eget eu\n" \
"turpis. Integer suscipit elit vel consectetur vulputate. Integer euismod, erat eget elementum tempus, magna metus consectetur\n" \
"elit, sed feugiat urna sapien sodales sapien. Sed sit amet varius nunc. Curabitur sodales nunc justo, ac scelerisque ipsum semper\n" \
"eget. Integer ornare, velit ut hendrerit dapibus, erat mauris commodo justo, vel semper urna justo non mauris. Proin blandit,\n" \
"enim ut posuere placerat, leo nibh tristique eros, ut pulvinar sapien elit eget enim. Pellentesque et mauris lectus. Curabitur\n" \
"quis lobortis leo, sit amet egestas dui. Nullam ut sapien eu justo lacinia ultrices. Ut tincidunt, sem non luctus tempus, felis\n" \
"purus imperdiet nisi, non ultricies libero ipsum eu augue. Mauris at luctus enim.";
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla quam enim, aliquam in placerat luctus, rutrum in quam. "
"Cras urna elit, pellentesque ac ipsum at, lobortis scelerisque eros. Aenean et turpis nibh. Maecenas lectus augue, eleifend "
"nec efficitur eu, faucibus eget turpis. Suspendisse vel nulla mi. Duis imperdiet neque orci, ac ultrices orci molestie a. "
"Etiam malesuada vulputate hendrerit. Cras ultricies diam in lectus finibus, eu laoreet diam rutrum.\n"
"\n"
"Etiam dictum orci arcu, ac fermentum leo dapibus lacinia. Integer vitae elementum ex. Vestibulum tempor nunc eu hendrerit "
"ornare. Nunc pretium ligula sit amet massa pulvinar, vitae imperdiet justo bibendum. Maecenas consectetur elementum mi, sed "
"vehicula neque pulvinar sit amet. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tortor erat, accumsan in laoreet "
"quis, placerat nec enim. Nulla facilisi. Morbi vitae nibh ligula. Suspendisse in molestie magna, eget aliquet mauris. Sed "
"aliquam faucibus magna.\n"
"\n"
"Sed metus odio, imperdiet et consequat non, faucibus nec risus. Suspendisse facilisis sem neque, id scelerisque dui mattis sit "
"amet. Nullam tincidunt nisl nec dui dignissim mattis. Proin fermentum ornare ipsum. Proin eleifend, mi vitae porttitor placerat, "
"neque magna elementum turpis, eu aliquet mi urna et leo. Pellentesque interdum est mauris, sed pellentesque risus blandit in. "
"Phasellus dignissim consequat eros, at aliquam elit finibus posuere. Proin suscipit tortor leo, id vulputate odio lobortis in. "
"Vestibulum et orci ligula. Sed scelerisque nunc non nisi aliquam, vel eleifend felis suscipit. Integer posuere sapien elit, "
"lacinia ultricies nibh sodales nec.\n"
"\n"
"Etiam aliquam purus sit amet purus ultricies tristique. Nunc maximus nunc quis magna ornare, vel interdum urna fermentum. "
"Vestibulum cursus nisl ut nulla egestas, quis mattis elit venenatis. Praesent malesuada mi non magna aliquam fringilla eget eu "
"turpis. Integer suscipit elit vel consectetur vulputate. Integer euismod, erat eget elementum tempus, magna metus consectetur "
"elit, sed feugiat urna sapien sodales sapien. Sed sit amet varius nunc. Curabitur sodales nunc justo, ac scelerisque ipsum semper "
"eget. Integer ornare, velit ut hendrerit dapibus, erat mauris commodo justo, vel semper urna justo non mauris. Proin blandit, "
"enim ut posuere placerat, leo nibh tristique eros, ut pulvinar sapien elit eget enim. Pellentesque et mauris lectus. Curabitur "
"quis lobortis leo, sit amet egestas dui. Nullam ut sapien eu justo lacinia ultrices. Ut tincidunt, sem non luctus tempus, felis "
"purus imperdiet nisi, non ultricies libero ipsum eu augue. Mauris at luctus enim.\n"
"\n"
"Aliquam sed tortor a justo pulvinar dictum consectetur eu felis. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices "
"posuere cubilia curae; Etiam vehicula porttitor volutpat. Morbi fringilla tortor nec accumsan aliquet. Aliquam in commodo neque. "
"Sed laoreet tellus in consectetur aliquet. Nullam nibh eros, feugiat sit amet aliquam non, malesuada vel urna. Ut vel egestas nunc. "
"Pellentesque vitae ante quis ante pharetra pretium. Nam quis eros commodo, mattis enim sed, finibus ante. Quisque lacinia tortor ut "
"odio laoreet, vel viverra libero porttitor. Vestibulum vitae dapibus ex. Phasellus varius lorem sed justo sollicitudin faucibus. "
"Etiam aliquam lacinia consectetur. Phasellus nulla ipsum, viverra non nulla in, rhoncus posuere nunc.\n"
"\n"
"Phasellus efficitur commodo tellus, eget lobortis erat porta quis. Aenean condimentum tortor ut neque dapibus, vitae vulputate quam "
"condimentum. Aliquam elementum vitae nulla vitae tristique. Suspendisse feugiat turpis ac magna dapibus, ut blandit diam tincidunt. "
"Integer id dui id enim ullamcorper dictum. Maecenas malesuada vitae ex pharetra iaculis. Curabitur eu dolor consectetur, tempus augue "
"sed, finibus est. Nulla facilisi. Vivamus sed lacinia turpis, in gravida dolor. Aenean interdum consectetur enim a malesuada. Sed turpis "
"nisi, lacinia et fermentum nec, pharetra id dui. Vivamus neque ligula, iaculis sed tempor eget, vehicula blandit quam. Morbi rhoncus quam "
"semper magna mollis luctus. Donec eu dolor ut ante ullamcorper porta. Mauris et est tristique libero pharetra faucibus.\n"
"\n"
"Duis ut elementum sem. Praesent commodo erat nec sem ultricies sollicitudin. Suspendisse a pellentesque sapien. Nunc ac magna a dui "
"elementum luctus non a mi. Cras elementum nunc sed nunc gravida, sit amet accumsan tortor pulvinar. Etiam elit arcu, pellentesque non ex "
"id, vestibulum pellentesque velit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque habitant morbi tristique senectus "
"et netus et malesuada fames ac turpis egestas. Proin sit amet velit eget tellus vulputate sagittis eget non massa. Cras accumsan tempor "
"tortor, quis rutrum neque placerat id. Nullam a egestas eros, eu porta nisi. Aenean rutrum, sapien quis fermentum tempus, dolor orci "
"faucibus eros, vel luctus justo leo vitae ante. Curabitur aliquam condimentum ipsum sit amet ultrices. Nullam ac velit semper, dapibus urna "
"sit amet, malesuada enim. Mauris ultricies nibh orci.";
mg_font create_font()
@ -106,6 +128,15 @@ int main()
u32* codePoints = malloc_array(utf32, codePointCount);
utf8_to_codepoints(codePointCount, codePoints, str8_from_cstring((char*)TEST_STRING));
u32 glyphCount = 0;
for(int i=0; i<codePointCount; i++)
{
if(codePoints[i] != ' ' && codePoints[i] != '\n')
{
glyphCount++;
}
}
// start app
mp_window_bring_to_front(window);
mp_window_focus(window);
@ -114,9 +145,14 @@ int main()
bool tracked = false;
vec2 trackPoint = {0};
f32 startX = 10;
f32 startY = contentRect.h - lineHeight - 10;
f32 zoom = 1;
f32 startX = 10;
f32 startY = (contentRect.h - lineHeight - 10);
/*
f32 startX = -100;
f32 startY = -100;
*/
while(!mp_should_quit())
{
f64 startFrameTime = mp_get_time(MP_CLOCK_MONOTONIC);
@ -140,17 +176,29 @@ int main()
{
tracked = true;
vec2 mousePos = mp_input_mouse_position();
trackPoint.x = mousePos.x - startX;
trackPoint.y = mousePos.y - startY;
trackPoint.x = mousePos.x/zoom - startX;
trackPoint.y = mousePos.y/zoom - startY;
}
else
{
tracked = false;
}
}
} break;
case MP_EVENT_MOUSE_WHEEL:
{
vec2 mousePos = mp_input_mouse_position();
f32 trackX = mousePos.x/zoom - startX;
f32 trackY = mousePos.y/zoom - startY;
zoom *= 1 + event.move.deltaY * 0.01;
zoom = Clamp(zoom, 0.2, 10);
startX = mousePos.x/zoom - trackX;
startY = mousePos.y/zoom - trackY;
} break;
default:
break;
}
@ -159,14 +207,28 @@ int main()
if(tracked)
{
vec2 mousePos = mp_input_mouse_position();
startX = mousePos.x - trackPoint.x;
startY = mousePos.y - trackPoint.y;
startX = mousePos.x/zoom - trackPoint.x;
startY = mousePos.y/zoom - trackPoint.y;
}
f32 textX = startX;
f32 textY = startY;
mg_surface_prepare(surface);
/*
mg_set_color_rgba(1, 1, 1, 1);
mg_clear();
mg_set_color_rgba(1, 0, 0, 1);
for(int i=0; i<1000; i++)
{
mg_rectangle_fill(0, 0, 100, 100);
}
*/
mg_matrix_push((mg_mat2x3){zoom, 0, 0,
0, zoom, 0});
mg_set_color_rgba(1, 1, 1, 1);
mg_clear();
@ -181,11 +243,10 @@ int main()
{
bool lineBreak = false;
int subIndex = 0;
for(; (startIndex+subIndex) < codePointCount && subIndex < 512; subIndex++)
for(; (startIndex+subIndex) < codePointCount && subIndex < 120; subIndex++)
{
if(codePoints[startIndex + subIndex] == '\n')
{
lineBreak = true;
break;
}
}
@ -196,22 +257,23 @@ int main()
mg_glyph_outlines((str32){subIndex, glyphs});
mg_fill();
if(lineBreak)
{
textY -= lineHeight;
mg_move_to(textX, textY);
startIndex++;
}
textY -= lineHeight;
mg_move_to(textX, textY);
startIndex++;
startIndex += subIndex;
}
mg_matrix_pop();
mg_set_color_rgba(0, 0, 1, 1);
mg_set_font(font);
mg_set_font_size(14);
mg_move_to(10, 10 + lineHeight);
str8 text = str8_pushf(mem_scratch(),
"Milepost vector graphics test program (frame time = %fs, fps = %f)...",
"Test program: %i glyphs, frame time = %fs, fps = %f",
glyphCount,
frameTime,
1./frameTime);
mg_text_outlines(text);

View File

@ -94,9 +94,11 @@ enum {
MG_GL_CANVAS_VERTEX_BUFFER_SIZE = MG_GL_CANVAS_DEFAULT_BUFFER_LENGTH * LAYOUT_VERTEX_SIZE,
MG_GL_CANVAS_SHAPE_BUFFER_SIZE = MG_GL_CANVAS_DEFAULT_BUFFER_LENGTH * LAYOUT_SHAPE_SIZE,
MG_GL_CANVAS_INDEX_BUFFER_SIZE = MG_GL_CANVAS_DEFAULT_BUFFER_LENGTH * LAYOUT_INT_SIZE,
MG_GL_CANVAS_TILE_COUNTER_BUFFER_SIZE = 65536,
MG_GL_CANVAS_TILE_ARRAY_SIZE = sizeof(int)*4096,
MG_GL_CANVAS_TILE_ARRAY_BUFFER_SIZE = MG_GL_CANVAS_TILE_COUNTER_BUFFER_SIZE * MG_GL_CANVAS_TILE_ARRAY_SIZE,
MG_GL_CANVAS_TILE_COUNTER_BUFFER_LENGTH = 65536,
MG_GL_CANVAS_TILE_COUNTER_BUFFER_SIZE = sizeof(int)*MG_GL_CANVAS_TILE_COUNTER_BUFFER_LENGTH,
MG_GL_CANVAS_TILE_ARRAY_LENGTH = 1<<10,
MG_GL_CANVAS_TILE_ARRAY_SIZE = sizeof(int)*MG_GL_CANVAS_TILE_ARRAY_LENGTH,
MG_GL_CANVAS_TILE_ARRAY_BUFFER_SIZE = MG_GL_CANVAS_TILE_COUNTER_BUFFER_LENGTH * MG_GL_CANVAS_TILE_ARRAY_SIZE,
};
void mg_gl_canvas_update_vertex_layout(mg_gl_canvas_backend* backend)
@ -186,7 +188,7 @@ void mg_gl_canvas_draw_batch(mg_canvas_backend* interface, u32 shapeCount, u32 v
const int tileSize = 16;
const int tileCountX = (frame.w*contentsScaling.x + tileSize - 1)/tileSize;
const int tileCountY = (frame.h*contentsScaling.y + tileSize - 1)/tileSize;
const int tileArraySize = MG_GL_CANVAS_TILE_ARRAY_SIZE;
const int tileArraySize = MG_GL_CANVAS_TILE_ARRAY_LENGTH;
//TODO: ensure there's enough space in tile buffer

View File

@ -53,12 +53,19 @@ bool is_top_left(ivec2 a, ivec2 b)
||(b.y < a.y));
}
int64_t orient2d(i64vec2 a, i64vec2 b, i64vec2 p)
//////////////////////////////////////////////////////////////////////////////
//TODO: we should do these computations on 64bits, because otherwise
// we might overflow for values > 2048.
// Unfortunately this is costly.
// Another way is to precompute triangle edges (b - a) in full precision
// once to avoid doing it all the time...
//////////////////////////////////////////////////////////////////////////////
int orient2d(ivec2 a, ivec2 b, ivec2 p)
{
return((b.x-a.x)*(p.y-a.y) - (b.y-a.y)*(p.x-a.x));
}
int64_t is_clockwise(i64vec2 p0, i64vec2 p1, i64vec2 p2)
int is_clockwise(ivec2 p0, ivec2 p1, ivec2 p2)
{
return((p1 - p0).x*(p2 - p0).y - (p1 - p0).y*(p2 - p0).x);
}
@ -68,21 +75,21 @@ void main()
ivec2 pixelCoord = ivec2(gl_WorkGroupID.xy*uvec2(16, 16) + gl_LocalInvocationID.xy);
uvec2 tileCoord = uvec2(pixelCoord) / tileSize;
uint tileIndex = tileCoord.y * tileCount.x + tileCoord.x;
uint tileCounter = tileCounterBuffer.elements[tileIndex];
uint tileCounter = min(tileCounterBuffer.elements[tileIndex], tileArraySize);
const float subPixelFactor = 256.;
const float subPixelFactor = 16.;
ivec2 centerPoint = ivec2((vec2(pixelCoord) + vec2(0.5, 0.5)) * subPixelFactor);
//*
const int sampleCount = 8;
ivec2 samplePoints[sampleCount] = ivec2[sampleCount](centerPoint + ivec2(1, 3)*16,
centerPoint + ivec2(-1, -3)*16,
centerPoint + ivec2(5, -1)*16,
centerPoint + ivec2(-3, 5)*16,
centerPoint + ivec2(-5, -5)*16,
centerPoint + ivec2(-7, 1)*16,
centerPoint + ivec2(3, -7)*16,
centerPoint + ivec2(7, 7)*16);
ivec2 samplePoints[sampleCount] = ivec2[sampleCount](centerPoint + ivec2(1, 3),
centerPoint + ivec2(-1, -3),
centerPoint + ivec2(5, -1),
centerPoint + ivec2(-3, 5),
centerPoint + ivec2(-5, -5),
centerPoint + ivec2(-7, 1),
centerPoint + ivec2(3, -7),
centerPoint + ivec2(7, 7));
/*/
const int sampleCount = 4;
ivec2 samplePoints[sampleCount] = ivec2[sampleCount](centerPoint + ivec2(-2, 6),
@ -148,7 +155,7 @@ void main()
ivec4 clip = ivec4(round((shapeBuffer.elements[zIndex].clip * vec4(scaling, scaling) + vec4(0.5, 0.5, 0.5, 0.5)) * subPixelFactor));
//NOTE(martin): reorder triangle counter-clockwise and compute bias for each edge
int64_t cw = is_clockwise(p0, p1, p2);
int cw = is_clockwise(p0, p1, p2);
if(cw < 0)
{
uint tmpIndex = i1;
@ -164,9 +171,9 @@ void main()
vec4 cubic1 = vertexBuffer.elements[i1].cubic;
vec4 cubic2 = vertexBuffer.elements[i2].cubic;
int64_t bias0 = is_top_left(p1, p2) ? 0 : -1;
int64_t bias1 = is_top_left(p2, p0) ? 0 : -1;
int64_t bias2 = is_top_left(p0, p1) ? 0 : -1;
int bias0 = is_top_left(p1, p2) ? 0 : -1;
int bias1 = is_top_left(p2, p0) ? 0 : -1;
int bias2 = is_top_left(p0, p1) ? 0 : -1;
for(int sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
{
@ -180,9 +187,9 @@ void main()
continue;
}
int64_t w0 = orient2d(p1, p2, samplePoint);
int64_t w1 = orient2d(p2, p0, samplePoint);
int64_t w2 = orient2d(p0, p1, samplePoint);
int w0 = orient2d(p1, p2, samplePoint);
int w1 = orient2d(p2, p0, samplePoint);
int w2 = orient2d(p0, p1, samplePoint);
if((w0+bias0) >= 0 && (w1+bias1) >= 0 && (w2+bias2) >= 0)
{

View File

@ -53,7 +53,7 @@ void main()
{
uint tileIndex = gl_WorkGroupID.x;
uint tileArrayOffset = tileArraySize * tileIndex;
uint tileArrayCount = tileCounterBuffer.elements[tileIndex];
uint tileArrayCount = min(tileCounterBuffer.elements[tileIndex], tileArraySize);
for(uint tileArrayIndex=1u; tileArrayIndex < tileArrayCount; tileArrayIndex++)
{

View File

@ -66,20 +66,25 @@ void main()
min(max(max(p0.x, p1.x), p2.x), clip.z),
min(max(max(p0.y, p1.y), p2.y), clip.w));
uvec4 box = uvec4(floor(fbox))/tileSize;
ivec4 box = ivec4(floor(fbox))/int(tileSize);
uint xMin = max(0u, box.x);
uint yMin = max(0u, box.y);
uint xMax = min(box.z, tileCount.x - 1u);
uint yMax = min(box.w, tileCount.y - 1u);
//NOTE(martin): it's importat to do the computation with signed int, so that we can have negative xMax/yMax
// otherwise all triangles on the left or below the x/y axis are attributed to tiles on row/column 0.
int xMin = max(0, box.x);
int yMin = max(0, box.y);
int xMax = min(box.z, int(tileCount.x) - 1);
int yMax = min(box.w, int(tileCount.y) - 1);
for(uint y = yMin; y <= yMax; y++)
for(int y = yMin; y <= yMax; y++)
{
for(uint x = xMin ; x <= xMax; x++)
for(int x = xMin ; x <= xMax; x++)
{
uint tileIndex = y*tileCount.x + x;
uint tileIndex = uint(y)*tileCount.x + uint(x);
uint tileCounter = atomicAdd(tileCounterBuffer.elements[tileIndex], 1u);
tileArrayBuffer.elements[tileArraySize*tileIndex + tileCounter] = triangleIndex;
if(tileCounter < tileArraySize)
{
tileArrayBuffer.elements[tileArraySize*tileIndex + tileCounter] = triangleIndex;
}
}
}
}

View File

@ -1,10 +1,9 @@
Overview
--------
[ ] Pan/Zoom on text example
[x] Pan/Zoom on text example
[ ] Clean+Fixes of canvas code and examples
[ ] Make backend selection easier
[ ] Investigate and fix artifact when seam is aligned to tile boundary?
[ ] Image API and backend
[ ] Build image atlas on top
@ -24,15 +23,29 @@ Clean+Fixes
[ ] Clean shaders (folder/filenames, version string, debug flags, ...)
[ ] Fix resource loading path in examples (to load font relative to executable)
[x] Fix resource loading path in examples (to load font relative to executable)
[ ] GL loader: allow using different GL versions (eg using a desktop GL surface and a GLES surface in the same app).
[ ] Clean up surface backend and canvas backend compile-time and run-time selections
[!] Investigate artifact when shifting positions of vertices by (0.5, 0.5) before multiplying
by subpixel precision and truncating... -> ie sampling at the middle of pixels vs at integer coordinates...
> seems to happen on tile boundaries
[ ] What alignment gives crisp-ier lines?
[!] decide how to handle overflow in cross-product computation in triangle rasterizing code
- could reduce subpixel precision (but only to some extent, since we need subpixel precision for msaa)
- could try to center all coords around 0 to maximize usable range
- could use int64_t and i64vec (but it has a big perf cost)
- could precompute some int64_t computation...
[!] handle pathological case where there are a lot of triangle on the same tile
> can we detect the first solid triangle covering the whole tile?
[!] handle pathological case where there are a lot of very small triangles (eg when zooming out)
> do we need the same precision then?
[!] Investigate why WM_MOUSEMOVE messages seem to lag behing mouse cursor
Canvas renderer
---------------