Compare commits
6 Commits
7972becddd
...
master
Author | SHA1 | Date | |
---|---|---|---|
ff583a3724 | |||
6f979a1905 | |||
b8c5be8052 | |||
5555808095 | |||
2ea2ce8e54 | |||
13d46ad901 |
BIN
articles/circle-rasterization/.static/circles.webp
Normal file
BIN
articles/circle-rasterization/.static/circles.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
84
articles/circle-rasterization/page.mmd
Normal file
84
articles/circle-rasterization/page.mmd
Normal 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
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
@ -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)
|
||||||
|
@ -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"
|
||||||
|
@ -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
3
remote_host.sh
Executable 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
56
tools/git_webhook.py
Normal 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()
|
Reference in New Issue
Block a user