From 08700972627fb3730fd27d41bad4d374fb34a12d Mon Sep 17 00:00:00 2001 From: martinfouilleul Date: Fri, 30 Jun 2023 15:18:37 +0200 Subject: [PATCH] [wip, win32, canvas] bin segments to screen tile queues --- examples/polygon/main.c | 2 +- src/gl_canvas.c | 49 ++++--- src/glsl_shaders/backprop.glsl | 18 ++- src/glsl_shaders/path_setup.glsl | 23 +-- src/glsl_shaders/raster.glsl | 80 ++++++++--- src/glsl_shaders/segment_setup.glsl | 210 +++++++++++++++++++++++++++- 6 files changed, 311 insertions(+), 71 deletions(-) diff --git a/examples/polygon/main.c b/examples/polygon/main.c index 2f17972..7912cca 100644 --- a/examples/polygon/main.c +++ b/examples/polygon/main.c @@ -30,7 +30,7 @@ int main() //NOTE: create surface mg_surface surface = mg_surface_create_for_window(window, MG_CANVAS); - mg_surface_swap_interval(surface, 0); + mg_surface_swap_interval(surface, 1); if(mg_surface_is_nil(surface)) { diff --git a/src/gl_canvas.c b/src/gl_canvas.c index 417d9d3..8711147 100644 --- a/src/gl_canvas.c +++ b/src/gl_canvas.c @@ -124,12 +124,10 @@ typedef struct mg_gl_canvas_backend GLuint vao; - /* GLuint pathSetup; - */ GLuint segmentSetup; - /* GLuint backprop; + /* GLuint merge; */ GLuint raster; @@ -242,40 +240,49 @@ 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); - /* + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); + //NOTE: path setup pass glUseProgram(backend->pathSetup); - glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, backend->pathBuffer, backend->pathBufferOffset, pathCount*sizeof(mg_gl_path)); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, backend->pathQueueBuffer); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, backend->tileQueueBuffer); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, backend->tileQueueCountBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, backend->tileQueueBuffer); - glUniform1i(0, tileSize); - glUniform1f(1, scale); +// glUniform1i(0, tileSize); +// glUniform1f(1, scale); + + glDispatchCompute(nTilesX, nTilesY, 1); + + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); - glDispatchCompute(pathCount, 1, 1); - */ //NOTE: segment setup pass glUseProgram(backend->segmentSetup); glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, backend->elementBuffer, backend->elementBufferOffset, eltCount*sizeof(mg_gl_path_elt)); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, backend->segmentCountBuffer); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, backend->segmentBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, backend->tileOpCountBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, backend->tileOpBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, backend->tileQueueBuffer); glUniform1f(0, scale); + glUniform1ui(1, tileSize); + glUniform2i(2, nTilesX, nTilesY); glDispatchCompute(eltCount, 1, 1); - /* + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); + //NOTE: backprop pass + /* glUseProgram(backend->backprop); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, backend->pathQueueBuffer); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, backend->tileQueueBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, backend->tileQueueBuffer); - glDispatchCompute(pathCount*16, 1, 1); + glUniform2i(0, nTilesX, nTilesY); + glDispatchCompute(nTilesY, 1, 1); + */ + /* //NOTE: merge pass glUseProgram(backend->merge); @@ -307,6 +314,8 @@ void mg_gl_render_batch(mg_gl_canvas_backend* backend, glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, backend->pathBuffer, backend->pathBufferOffset, pathCount*sizeof(mg_gl_path)); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, backend->segmentCountBuffer); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, backend->segmentBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, backend->tileQueueBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, backend->tileOpBuffer); // glUniform1ui(0, tileSize); // glUniform1f(1, scale); @@ -316,8 +325,6 @@ void mg_gl_render_batch(mg_gl_canvas_backend* backend, { log_error("gl error %i\n", err); } - - log_info("eltCount = %i\n", eltCount); ASSERT(eltCount != 0); @@ -339,7 +346,7 @@ void mg_gl_render_batch(mg_gl_canvas_backend* backend, } */ - glDispatchCompute(viewportSize.x, viewportSize.y, 1); + glDispatchCompute(nTilesX, nTilesY, 1); //NOTE: blit pass glUseProgram(backend->blit); @@ -741,12 +748,10 @@ 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); diff --git a/src/glsl_shaders/backprop.glsl b/src/glsl_shaders/backprop.glsl index 95e6ccd..46e5c46 100644 --- a/src/glsl_shaders/backprop.glsl +++ b/src/glsl_shaders/backprop.glsl @@ -4,17 +4,23 @@ 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 pathQueueBufferSSBO -{ - mg_gl_path_queue elements[]; -} pathQueueBuffer; - -layout(binding = 1) restrict writeonly buffer tileQueueBufferSSBO +layout(binding = 0) restrict buffer tileQueueBufferSSBO { mg_gl_tile_queue elements[]; } tileQueueBuffer; +layout(location = 0) uniform ivec2 nTiles; + void main() { + int rowIndex = int(gl_WorkGroupID.x); + int sum = 0; + for(int x = nTiles.x-1; x >= 0; x--) + { + int tileIndex = rowIndex * nTiles.x + x; + int offset = tileQueueBuffer.elements[tileIndex].windingOffset; + tileQueueBuffer.elements[tileIndex].windingOffset = sum; + sum += offset; + } } diff --git a/src/glsl_shaders/path_setup.glsl b/src/glsl_shaders/path_setup.glsl index 111d27d..7bd3d01 100644 --- a/src/glsl_shaders/path_setup.glsl +++ b/src/glsl_shaders/path_setup.glsl @@ -4,27 +4,18 @@ 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 pathBufferSSBO -{ - mg_gl_path elements[]; -} pathBuffer; - -layout(binding = 1) restrict writeonly buffer pathQueueBufferSSBO -{ - mg_gl_path_queue elements[]; -} pathQueueBuffer; - -layout(binding = 2) restrict writeonly buffer tileQueueBufferSSBO +layout(binding = 0) restrict writeonly buffer tileQueueBufferSSBO { mg_gl_tile_queue elements[]; } tileQueueBuffer; -layout(binding = 3) restrict writeonly buffer tileQueueCountBufferSSBO -{ - int elements[]; -} tileQueueCountBuffer; - void main() { + uvec2 nTiles = gl_NumWorkGroups.xy; + uvec2 tileCoord = gl_WorkGroupID.xy; + uint tileIndex = tileCoord.y * nTiles.x + tileCoord.x; + tileQueueBuffer.elements[tileIndex].windingOffset = 0; + tileQueueBuffer.elements[tileIndex].first = -1; + tileQueueBuffer.elements[tileIndex].last = -1; } diff --git a/src/glsl_shaders/raster.glsl b/src/glsl_shaders/raster.glsl index 26c9458..3059813 100644 --- a/src/glsl_shaders/raster.glsl +++ b/src/glsl_shaders/raster.glsl @@ -19,6 +19,17 @@ layout(binding = 2) restrict readonly buffer segmentBufferSSBO mg_gl_segment elements[]; } segmentBuffer; +layout(binding = 3) restrict readonly buffer tileQueuesBufferSSBO +{ + mg_gl_tile_queue elements[]; +} tileQueuesBuffer; + +layout(binding = 4) restrict readonly buffer tileOpBufferSSBO +{ + mg_gl_tile_op elements[]; +} tileOpBuffer; + + //layout(location = 0) uniform uint tileSize; // this has to be commented until it's effectively used!! //layout(location = 0) uniform float scale; @@ -121,39 +132,64 @@ int side_of_segment(vec2 p, mg_gl_segment seg) void main() { - int segCount = segmentCountBuffer.elements[0]; - vec2 sampleCoord = vec2(gl_WorkGroupID.xy*uvec2(16, 16) + gl_LocalInvocationID.xy); + uvec2 nTiles = gl_NumWorkGroups.xy; + uvec2 tileCoord = gl_WorkGroupID.xy; + uint tileIndex = tileCoord.y * nTiles.x + tileCoord.x; + ivec2 pixelCoord = ivec2(gl_WorkGroupID.xy*uvec2(16, 16) + gl_LocalInvocationID.xy); + vec2 sampleCoord = vec2(pixelCoord); + + + imageStore(outTexture, ivec2(sampleCoord), vec4(1, 1, 1, 1)); + + mg_gl_tile_queue tileQueue = tileQueuesBuffer.elements[tileIndex]; + int opIndex = tileQueue.first; +// int winding = tileQueue.windingOffset; int winding = 0; - for(int segIndex=0; segIndex seg.box.y) - &&(sampleCoord.y <= seg.box.w) - &&(side_of_segment(sampleCoord, seg) < 0)) - { - winding += seg.windingIncrement; - } + int opCount = 0; + while(opIndex >= 0) + { + //imageStore(outTexture, ivec2(sampleCoord), vec4(0, 1, 0, 1)); + //return; - /* - if(op->crossRight) + mg_gl_tile_op op = tileOpBuffer.elements[opIndex]; + opIndex = op.next; + + if(op.kind == MG_GL_OP_SEGMENT) { - if( (seg.config == MG_GL_BR || seg.config == MG_GL_TL) - &&(sampleCoord.y > seg.box.w)) + int segIndex = op.index; + mg_gl_segment seg = segmentBuffer.elements[segIndex]; + + if( (sampleCoord.y > seg.box.y) + &&(sampleCoord.y <= seg.box.w) + &&(side_of_segment(sampleCoord, seg) < 0)) { winding += seg.windingIncrement; } - else if( (seg.config == MG_GL_BL || seg.config == MG_GL_TR) - &&(sampleCoord.y > seg.box.y)) - { - 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; + } + } +*/ + } + } int pathIndex = 0; // vec4 clip = pathBuffer.elements[pathIndex].clip * scale; diff --git a/src/glsl_shaders/segment_setup.glsl b/src/glsl_shaders/segment_setup.glsl index 0a3b080..b2e45e5 100644 --- a/src/glsl_shaders/segment_setup.glsl +++ b/src/glsl_shaders/segment_setup.glsl @@ -9,18 +9,34 @@ layout(binding = 0) restrict readonly buffer elementBufferSSBO mg_gl_path_elt elements[]; } elementBuffer; -layout(binding = 1) restrict buffer segmentCountBufferSSBO +layout(binding = 1) coherent restrict buffer segmentCountBufferSSBO { int elements[]; } segmentCountBuffer; -layout(binding = 2) restrict writeonly buffer segmentBufferSSBO +layout(binding = 2) restrict buffer segmentBufferSSBO { mg_gl_segment elements[]; } segmentBuffer; -layout(location = 0) uniform float scale; +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) { @@ -82,12 +98,198 @@ int push_segment(in vec2 p[4], int kind) 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]; - //TODO later bin to tiles + bin_to_tiles(segIndex); } void main()