From f76ff94c28a5937944093cefc84eb46337ad41ec Mon Sep 17 00:00:00 2001 From: martinfouilleul Date: Thu, 29 Jun 2023 15:48:52 +0200 Subject: [PATCH] [wip, win32, canvas] raster elements directly per pixel --- examples/polygon/build.bat | 4 + examples/polygon/main.c | 37 +++--- src/gl_canvas.c | 61 +++++++-- src/glsl_shaders/common.glsl | 10 +- src/glsl_shaders/raster.glsl | 240 ++++++++++++++++++++++++++++++++--- 5 files changed, 305 insertions(+), 47 deletions(-) create mode 100644 examples/polygon/build.bat diff --git a/examples/polygon/build.bat b/examples/polygon/build.bat new file mode 100644 index 0000000..65b9b36 --- /dev/null +++ b/examples/polygon/build.bat @@ -0,0 +1,4 @@ + +set INCLUDES=/I ..\..\src /I ..\..\src\util /I ..\..\src\platform /I ../../ext /I ../../ext/angle_headers + +cl /we4013 /Zi /Zc:preprocessor /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.dll.lib /out:../../bin/example_poly.exe diff --git a/examples/polygon/main.c b/examples/polygon/main.c index fab8886..2f17972 100644 --- a/examples/polygon/main.c +++ b/examples/polygon/main.c @@ -6,6 +6,7 @@ * @revision: * *****************************************************************/ +#include #include #include #include @@ -19,8 +20,6 @@ int main() { - LogLevel(LOG_LEVEL_MESSAGE); - mp_init(); mp_clock_init(); //TODO put that in mp_init()? @@ -30,11 +29,17 @@ int main() mp_rect contentRect = mp_window_get_content_rect(window); //NOTE: create surface - mg_surface surface = mg_surface_create_for_window(window, MG_BACKEND_DEFAULT); + mg_surface surface = mg_surface_create_for_window(window, MG_CANVAS); mg_surface_swap_interval(surface, 0); + if(mg_surface_is_nil(surface)) + { + printf("Error: couldn't create surface\n"); + return(-1); + } + //TODO: create canvas - mg_canvas canvas = mg_canvas_create(surface); + mg_canvas canvas = mg_canvas_create(); if(mg_canvas_is_nil(canvas)) { @@ -54,10 +59,10 @@ int main() f64 startTime = mp_get_time(MP_CLOCK_MONOTONIC); mp_pump_events(0); - mp_event event = {0}; - while(mp_next_event(&event)) + mp_event* event = 0; + while((event = mp_next_event(mem_scratch())) != 0) { - switch(event.type) + switch(event->type) { case MP_EVENT_WINDOW_CLOSE: { @@ -66,21 +71,21 @@ int main() case MP_EVENT_KEYBOARD_KEY: { - if(event.key.action == MP_KEY_PRESS) + if(event->key.action == MP_KEY_PRESS) { - if(event.key.code == MP_KEY_LEFT) + if(event->key.code == MP_KEY_LEFT) { x-=1; } - if(event.key.code == MP_KEY_RIGHT) + if(event->key.code == MP_KEY_RIGHT) { x+=1; } - if(event.key.code == MP_KEY_UP) + if(event->key.code == MP_KEY_UP) { y-=1; } - if(event.key.code == MP_KEY_DOWN) + if(event->key.code == MP_KEY_DOWN) { y+=1; } @@ -97,7 +102,7 @@ int main() // background mg_set_color_rgba(0, 1, 1, 1); mg_clear(); -/* + mg_move_to(100, 100); mg_line_to(150, 150); mg_line_to(100, 200); @@ -106,6 +111,7 @@ int main() mg_set_color_rgba(1, 0, 0, 1); mg_fill(); +/* mg_move_to(200, 100); mg_line_to(410, 100); mg_line_to(410, 200); @@ -151,11 +157,10 @@ int main() mg_close_path(); mg_set_color_rgba(0, 0, 1, 1); mg_stroke(); -*/ + mg_set_color_rgba(1, 0, 0, 1); mg_rounded_rectangle_fill(100, 100, 200, 300, 20); -/* mg_move_to(x+8, y+8); mg_line_to(x+33, y+8); mg_line_to(x+33, y+19); @@ -168,7 +173,7 @@ int main() frameTime, 1./frameTime); - mg_flush(); + mg_render(surface, canvas); mg_surface_present(surface); mem_arena_clear(mem_scratch()); diff --git a/src/gl_canvas.c b/src/gl_canvas.c index 2f0baa3..2869683 100644 --- a/src/gl_canvas.c +++ b/src/gl_canvas.c @@ -17,34 +17,43 @@ typedef struct mg_gl_image GLuint texture; } mg_gl_image; -typedef enum { +enum _mg_gl_cmd { MG_GL_FILL, MG_GL_STROKE, -} mg_gl_cmd; +}; +typedef int mg_gl_cmd; typedef struct mg_gl_path { - mg_gl_cmd cmd; - float uvTransform[9]; + float uvTransform[12]; vec4 color; vec4 box; vec4 clip; + mg_gl_cmd cmd; + u8 pad[12]; } mg_gl_path; -typedef enum { +enum _mg_gl_seg_kind{ MG_GL_LINE = 1, MG_GL_QUADRATIC, MG_GL_CUBIC, -} mg_gl_seg_kind; +}; +typedef int mg_gl_seg_kind; typedef struct mg_gl_path_elt { + vec2 p[4]; int pathIndex; int localEltIndex; mg_gl_seg_kind kind; - vec2 p[4]; + u8 pad[4]; } mg_gl_path_elt; +enum { + LAYOUT_PATH_SIZE = sizeof(mg_gl_path), + LAYOUT_PATH_ELT_SIZE = sizeof(mg_gl_path_elt), +}; + //////////////////////////////////////////////////////////// //NOTE: these are just here for the sizes... @@ -115,10 +124,12 @@ typedef struct mg_gl_canvas_backend GLuint vao; + /* GLuint pathSetup; GLuint segmentSetup; GLuint backprop; GLuint merge; + */ GLuint raster; GLuint blit; @@ -211,6 +222,13 @@ void mg_gl_render_batch(mg_gl_canvas_backend* backend, vec2 viewportSize, f32 scale) { + //NOTE: send the buffers + glBindBuffer(GL_SHADER_STORAGE_BUFFER, backend->pathBuffer); + glBufferData(GL_SHADER_STORAGE_BUFFER, LAYOUT_PATH_SIZE*pathCount, backend->pathBufferData, GL_STREAM_DRAW); + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, backend->elementBuffer); + glBufferData(GL_SHADER_STORAGE_BUFFER, LAYOUT_PATH_ELT_SIZE*eltCount, backend->elementBufferData, GL_STREAM_DRAW); + //NOTE: clear counters int zero = 0; glBindBuffer(GL_SHADER_STORAGE_BUFFER, backend->segmentCountBuffer); @@ -222,6 +240,7 @@ void mg_gl_render_batch(mg_gl_canvas_backend* backend, glBindBuffer(GL_SHADER_STORAGE_BUFFER, backend->tileOpCountBuffer); glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(int), &zero, GL_DYNAMIC_COPY); + /* //NOTE: path setup pass glUseProgram(backend->pathSetup); @@ -273,10 +292,11 @@ void mg_gl_render_batch(mg_gl_canvas_backend* backend, glUniform1f(1, scale); glDispatchCompute(nTilesX, nTilesY, 1); + */ //NOTE: raster pass glUseProgram(backend->raster); - +/* glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, backend->screenTilesBuffer); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, backend->tileOpBuffer); glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 2, backend->pathBuffer, backend->pathBufferOffset, pathCount*sizeof(mg_gl_path)); @@ -285,9 +305,28 @@ void mg_gl_render_batch(mg_gl_canvas_backend* backend, glUniform1i(0, tileSize); glUniform1f(1, scale); glUniform1i(2, backend->msaaCount); +*/ + glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, backend->pathBuffer, backend->pathBufferOffset, pathCount*sizeof(mg_gl_path)); + glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 1, backend->elementBuffer, backend->elementBufferOffset, eltCount*sizeof(mg_gl_path_elt)); + +// glUniform1ui(0, tileSize); + glUniform1f(1, scale); + glUniform1ui(2, eltCount); + + int err = glGetError(); + if(err) + { + log_error("gl error %i\n", err); + } + + + log_info("eltCount = %i\n", eltCount); + + ASSERT(eltCount != 0); glBindImageTexture(0, backend->outTexture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8); + /* if(image) { //TODO: make sure this image belongs to that context @@ -300,6 +339,7 @@ void mg_gl_render_batch(mg_gl_canvas_backend* backend, { glUniform1ui(3, 0); } + */ glDispatchCompute(viewportSize.x, viewportSize.y, 1); @@ -358,9 +398,6 @@ void mg_gl_canvas_render(mg_canvas_backend* interface, vec2 currentPos = {0}; mg_image currentImage = mg_image_nil(); - ///////////////////////////////////////////////////////////////////////////////// - //TODO: we must map or allocate elementBufferData and pathBufferData... - ///////////////////////////////////////////////////////////////////////////////// mg_gl_encoding_context context = {.glEltCount = 0, .elementBufferData = backend->elementBufferData, .pathBufferData = backend->pathBufferData }; @@ -706,10 +743,12 @@ mg_canvas_backend* gl_canvas_backend_create(mg_wgl_surface* surface) //NOTE: create programs int err = 0; + /* err |= mg_gl_canvas_compile_compute_program(glsl_path_setup, &backend->pathSetup); err |= mg_gl_canvas_compile_compute_program(glsl_segment_setup, &backend->segmentSetup); err |= mg_gl_canvas_compile_compute_program(glsl_backprop, &backend->backprop); err |= mg_gl_canvas_compile_compute_program(glsl_merge, &backend->merge); + */ err |= mg_gl_canvas_compile_compute_program(glsl_raster, &backend->raster); err |= mg_gl_canvas_compile_render_program("blit", glsl_blit_vertex, glsl_blit_fragment, &backend->blit); diff --git a/src/glsl_shaders/common.glsl b/src/glsl_shaders/common.glsl index b6942e3..1b1cb53 100644 --- a/src/glsl_shaders/common.glsl +++ b/src/glsl_shaders/common.glsl @@ -22,19 +22,19 @@ layout(std430) buffer; struct mg_gl_path { - int cmd; - float uvTransform[9]; + mat3 uvTransform; vec4 color; vec4 box; vec4 clip; + int cmd; }; struct mg_gl_path_elt { + vec2 p[4]; int pathIndex; int localEltIndex; int kind; - vec2 p[4]; }; struct mg_gl_segment @@ -44,8 +44,8 @@ struct mg_gl_segment int config; //TODO pack these int windingIncrement; vec4 box; - float hullMatrix[9]; - float implicitMatrix[9]; + mat3 hullMatrix; + mat3 implicitMatrix; float sign; vec2 hullVertex; int debugID; diff --git a/src/glsl_shaders/raster.glsl b/src/glsl_shaders/raster.glsl index cc77255..c5c96ca 100644 --- a/src/glsl_shaders/raster.glsl +++ b/src/glsl_shaders/raster.glsl @@ -1,31 +1,241 @@ -layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; +layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in; precision mediump float; layout(std430) buffer; -layout(binding = 0) restrict readonly buffer screenTilesBufferSSBO -{ - mg_gl_tile_queue elements[]; -} screenTilesBuffer; - -layout(binding = 1) restrict readonly buffer tileOpBufferSSBO -{ - mg_gl_tile_op elements[]; -} tileOpBuffer; - -layout(binding = 2) restrict readonly buffer pathBufferSSBO +layout(binding = 0) restrict readonly buffer pathBufferSSBO { mg_gl_path elements[]; } pathBuffer; -layout(binding = 3) restrict readonly buffer segmentBufferSSBO +layout(binding = 1) restrict readonly buffer elementBufferSSBO { - mg_gl_segment elements[]; -} segmentBuffer; + mg_gl_path_elt elements[]; +} elementBuffer; +//layout(location = 0) uniform uint tileSize; // this has to be commented until it's effectively used!! +layout(location = 1) uniform float scale; +layout(location = 2) uniform uint eltCount; + +layout(rgba8, binding = 0) uniform restrict writeonly image2D outTexture; + +void init_segment(in vec2 p[4], int kind, out mg_gl_segment seg) +{ + vec2 s = p[0]; + vec2 c = p[0]; + vec2 e = p[1]; + + bool goingUp = e.y >= s.y; + bool goingRight = e.x >= s.x; + + seg.kind = kind; + seg.pathIndex = 0; /// + seg.windingIncrement = goingUp ? 1 : -1; + seg.box = vec4(min(s.x, e.x), + min(s.y, e.y), + max(s.x, e.x), + max(s.y, e.y)); + + float dx = c.x - seg.box.x; + float dy = c.y - seg.box.y; + float alpha = (seg.box.w - seg.box.y)/(seg.box.z - seg.box.x); + float ofs = seg.box.w - seg.box.y; + + if(goingUp == goingRight) + { + if(seg.kind == MG_GL_LINE) + { + seg.config = MG_GL_BR; + } + else if(dy > alpha*dx) + { + seg.config = MG_GL_TL; + } + else + { + seg.config = MG_GL_BR; + } + } + else + { + if(seg.kind == MG_GL_LINE) + { + seg.config = MG_GL_TR; + } + else if(dy < ofs - alpha*dx) + { + seg.config = MG_GL_BL; + } + else + { + seg.config = MG_GL_TR; + } + } +} + +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 main() { + vec2 sampleCoord = vec2(gl_WorkGroupID.xy*uvec2(16, 16) + gl_LocalInvocationID.xy); + int winding = 0; + + for(int i=0; i seg.box.y) + &&(sampleCoord.y <= seg.box.w) + &&(side_of_segment(sampleCoord, seg) < 0)) + { + winding += seg.windingIncrement; + } + + /* + if(op->crossRight) + { + if( (seg.config == MG_GL_BR || seg.config == MG_GL_TL) + &&(sampleCoord.y > seg.box.w)) + { + winding += seg.windingIncrement; + } + else if( (seg.config == MG_GL_BL || seg.config == MG_GL_TR) + &&(sampleCoord.y > seg.box.y)) + { + winding -= seg.windingIncrement; + } + } + */ + + } break; + + case MG_GL_QUADRATIC: + case MG_GL_CUBIC: + break; + } + } + + int pathIndex = 0; + vec4 clip = pathBuffer.elements[pathIndex].clip * scale; + +/* if( sampleCoord.x >= clip.x + && sampleCoord.x < clip.z + && sampleCoord.y >= clip.y + && sampleCoord.y < clip.w) +*/ { + /* + bool filled = (pathBuffer[pathIndex].cmd == MG_GL_FILL && (winding[sampleIndex] & 1)) + ||(pathBuffer[pathIndex].cmd == MG_GL_STROKE && (winding[sampleIndex] != 0)); + */ + bool filled = (winding & 1) != 0; + if(filled) + { + // write to texture + imageStore(outTexture, ivec2(sampleCoord), vec4(1, 0, 0, 1)); + } + } }