edubart/sokol_gp

Some `sgp_draw_line()` not being rendered?

benjitrosch opened this issue · 3 comments

I've got a large 2D grid, where each cell draws a box and an outline like so:

int px = x * TILE_SIZE;
int py = y * TILE_SIZE;

sgp_set_color(0, 0.15f, 0.32f, 1);
sgp_draw_filled_rect(px, py, TILE_SIZE, TILE_SIZE);

sgp_set_color(1, 0.11f, 0.01f, 1);
sgp_draw_line(px, py, px + TILE_SIZE, py);
sgp_draw_line(px, py, px, py + TILE_SIZE);
sgp_draw_line(px + TILE_SIZE, py, px + TILE_SIZE, py + TILE_SIZE);
sgp_draw_line(px, py + TILE_SIZE, px + TILE_SIZE, py + TILE_SIZE);

However, depending on the projection, some of the lines will not be drawn. Transforming the projection changes which lines aren't drawn. You can see it pictured below:

Screen Shot 2023-03-21 at 5 23 15 PM

And here's a video (might be hard to tell because of the low video resolution):

Mar-21-2023.17-28-37.mp4

My code overall looks very similar to most of the example projects:

const int width = sapp_width(), height = sapp_height();

sgp_begin(width, height);
sgp_viewport(0, 0, width, height);
sgp_set_color(0.01f, 0.48f, 0.51f, 1.0f);
sgp_clear();
sgp_reset_color();

sgp_project(0, camera.width, 0, camera.height);
sgp_translate(-camera.x + camera.width * 0.5f, -camera.y + camera.height * 0.5f);

// this is where each cell is drawn

sg_pass_action pass_action = {0};
sg_begin_default_pass(&pass_action, width, height);
sgp_flush();
sgp_end();
sg_end_pass();
sg_commit();

That happened because the automatic batch algorithm was not considering the default line thickness of 1 pixel. I fixed this in c144a80 . This issue would only be visible when drawing lines or points in edge overlapping cases such as this one.

But you should really start drawing inbound lines for drawing your boxes' boundaries, either by offsetting the edges 0.5 pixels inside every box, or by drawing the edges as inbound rectangles instead of lines.

Furthermore, drawing the way you are, will impact automatic batching performance after my fix above, because every line is now overlapping with every neighbor rectangle to fix the issue, making the automatic batch algorithm unable to merge batch calls. You can notice this visually when line thickness between neighbors varies.

One advantage of drawing rectangles instead of lines, is the possibility to draw lines with thickness greater than 1 pixel, because one major limitation of draw lines APIs is the impossibility to adjust line thickness in a cross platform manner. Line thickness is always hard fixed to 1 pixel wide.

Using draw line APIs still make sense though for debugging or drawing grids. If this is just for debugging purposes, then you could leave the way you did and never mind the performance hit.

Thanks for the quick fix @edubart, much appreciated!

Like you said, these were just used for debugging purposes. However I've rewritten it to use rectangles as advised:

sgp_set_color(0, 0.15f, 0.32f, 1);
sgp_draw_filled_rect(px, py, TILE_SIZE, TILE_SIZE);

sgp_set_color(1, 0.11f, 0.01f, 1);

sgp_draw_filled_rect(px, py, TILE_SIZE, 8);
sgp_draw_filled_rect(px, py, 8, TILE_SIZE);
sgp_draw_filled_rect(px + TILE_SIZE, py, 8, TILE_SIZE + 8);
sgp_draw_filled_rect(px, py + TILE_SIZE, TILE_SIZE + 8, 8);

On the note of what you said here:

Furthermore, drawing the way you are, will impact automatic batching performance after my fix above, because every line is now overlapping with every neighbor rectangle to fix the issue, making the automatic batch algorithm unable to merge batch calls.

Are there other patterns, like this one you've identified, which might hurt batching performance? Or better yet, are there best practices to follow to get the most out of the batcher?

Are there other patterns, like this one you've identified, which might hurt batching performance?

Drawing different overlapping primitives in interleaved order is usually what prevent automatic batching, because the automatic algorithm batching algorithm must always obey draw order considering region overlaps.

Or better yet, are there best practices to follow to get the most out of the batcher?

You could draw primitives of same texture/color in layers if that is easy for you, like mentioned in #12 . For example, if that is for debugging only, you could draw everything of debug mode as a second layer, just after the first layer is drawn. This would mean a for loop for each layer.