Compare commits

...

6 Commits

8 changed files with 154 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,84 @@
Title: Circle Rasterization
Brief: Investigation on fast grid-aligned circle rasterization.
Date: 1737757212
Tags: Programming, Optimization, C
CSS: /style.css
![](/articles/circle-rasterization/circles.webp)
Currently drastically overthinking anything related to dream Minecraft-like game of mine,
and today it was all about chunk loading. Particularly, ideal way to infer which chunks
should be loaded based on distance to the viewer, instead of typical direct grid.
For that circle rasterization is needed. I came up with following pieces of code, one reusable macro,
and others are meant to be directly copy pasted where needed:
Macro:
```c
/* Emits `x` and `y` for every intersecting cell */
/* We snap position to the nearest corner, which means there's no aliasing */
/* It works great for integer radii */
#define m_iter_circle_pixels(p_center_x, p_center_y, p_radius) \
for (float y = (p_center_y + ceilf(p_radius)) - 1; y > (p_center_y - ceilf(p_radius)) - 1; --y) \
for (float x = p_center_x - ceilf(sqrtf(p_radius * p_radius - (y - p_center_y + (y <= p_center_y)) * (y - p_center_y + (y <= p_center_y)))); \
x < p_center_x + ceilf(sqrtf(p_radius * p_radius - (y - p_center_y + (y <= p_center_y)) * (y - p_center_y + (y <= p_center_y)))); ++x)
```
Floating point based one:
```c
float const rs = state->r * state->r;
float const cr = ceilf(state->r);
for (float iy = -cr; iy <= cr - 1; ++iy) {
float const dx = ceilf(sqrtf(rs - (iy + (iy <= 0)) * (iy + (iy <= 0))));
for (float ix = -dx; ix < dx; ++ix) {
/* iy and ix are floating point offsets from (0, 0) */
}
}
```
Integer math based one:
```c
/* Neat shorthand making integer based loops drastically faster */
static int32_t ceil_sqrt(int32_t const n) {
int32_t res = 1;
#pragma clang loop unroll_count(8)
while(res * res < n)
res++;
return res;
}
/* This one beats the float in raw performance, but might scale worse at increasing radii, assuming sqrt is a hardware intrinsic with known worst time */
int32_t const rsi = (int32_t)state->r * (int32_t)state->r;
for (int32_t iy = -(int32_t)state->r; iy <= (int32_t)state->r - 1; ++iy) {
int32_t const dx = ceil_sqrt(rsi - (iy + (iy <= 0)) * (iy + (iy <= 0)));
for (int32_t ix = -dx; ix < dx; ++ix) {
/* iy and ix are integer offsets from (0, 0) */
}
}
```
Integer math based with accumulated ceil(sqrt()), the fastest I could come up with:
```c
int32_t const rsi = (int32_t)state->r * (int32_t)state->r;
int32_t acc = 1;
for (int32_t iy = (int32_t)state->r - 1; iy >= 0; --iy) {
while (acc * acc < rsi - iy * iy) acc++;
for (int32_t ix = -acc; ix < acc; ++ix) {
/* lower portion */
x = (float)ix;
y = (float)iy;
/* upper portion */
x = (float)ix;
y = (float)-iy - 1;
}
}
```
Note that they assume center point at coordinate origin, quadrant symmetry and whole number radii.
Benchmarks:
```
Profile 'float' on average took: 0.001537s, worst case: 0.003272s, sample count: 277
Profile 'int32_t' on average took: 0.000726s, worst case: 0.002293s, sample count: 277
Profile 'int32_t acc' on average took: 0.000650s, worst case: 0.001732s, sample count: 277
```

View File

@ -51,6 +51,7 @@ CSS: /style.css
- [Occlusion culling for terrain](https://www.researchgate.net/publication/248358913_Voxel_Column_Culling_Occlusion_Culling_For_Large_Terrain_Models) - [Occlusion culling for terrain](https://www.researchgate.net/publication/248358913_Voxel_Column_Culling_Occlusion_Culling_For_Large_Terrain_Models)
- [Billboard quad transformation optimization](https://gamedev.stackexchange.com/questions/201963/efficient-calculation-of-billboard-sprite-transformations) - [Billboard quad transformation optimization](https://gamedev.stackexchange.com/questions/201963/efficient-calculation-of-billboard-sprite-transformations)
- [NVidia bindless extensions](https://developer.download.nvidia.com/opengl/tutorials/bindless_graphics.pdf) - [NVidia bindless extensions](https://developer.download.nvidia.com/opengl/tutorials/bindless_graphics.pdf)
- [hacksoflife blog, full of good things](http://hacksoflife.blogspot.com/search/label/OpenGL)
## technical stuff ## technical stuff
- [Determinism between opengl vendors](https://stackoverflow.com/questions/7922526/opengl-deterministic-rendering-between-gpu-vendor) - [Determinism between opengl vendors](https://stackoverflow.com/questions/7922526/opengl-deterministic-rendering-between-gpu-vendor)

View File

@ -34,3 +34,12 @@ logo = "/logo.png"
## Language specifier, used in RSS feed. ## Language specifier, used in RSS feed.
## ##
language = "en" language = "en"
## Port that is used to listed to remote git push signals.
##
webhook_port = 14032
## Something that only git hosting and your server should know.
## See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization
##
webhook_auth = "Basic you-secure-credentials"

View File

@ -3,6 +3,7 @@
set +e set +e
git submodule update --init --recursive git submodule update --init --recursive
git-lfs fetch
(cd tools/mmd && make release) (cd tools/mmd && make release)
(cd tools/mmd/build && make) (cd tools/mmd/build && make)

3
remote_host.sh Executable file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env bash
python3 -m http.server --directory ./html/ & python3 ./tools/git_webhook.py

56
tools/git_webhook.py Normal file
View File

@ -0,0 +1,56 @@
#!/usr/bin/env python3
from http.client import parse_headers
from http.server import BaseHTTPRequestHandler
from http.server import HTTPServer
import subprocess
import config
## Simple way to automatically pull and recompile on a remote server.
## Run this from the root directory.
##
## Currently supports:
## - Gitea (Via GET method).
##
class HttpHandler(BaseHTTPRequestHandler):
def do_GET(self):
got_gitea_push_event = False
got_auth = False
for header in self.headers:
match [header, self.headers[header]]:
case ["X-Gitea-Event", "push"]:
got_gitea_push_event = True
case ["Authorization", config.webhook_auth]:
got_auth = True
if not got_gitea_push_event or not got_auth:
self.send_response(400)
return
# todo: This way of doing it blocks both parties. Not ideal.
self.send_response(200)
subprocess.run(["git", "pull"])
subprocess.run(["./compile.sh"])
print("Pulled and recompiled.")
def run(server_class=HTTPServer, handler_class=HttpHandler):
server_address = ('', config.webhook_port)
httpd = server_class(server_address, handler_class)
try:
httpd.serve_forever()
except KeyboardInterrupt:
httpd.server_close()
if __name__ == "__main__":
run()