refactor(render3d): programs, GPU timers and the context behind gpu.ludic

The last of milestone 1: no OpenGL call is left in render3d outside gpu.ludic but the clock
and the CPU-side buffer helpers.

- gpu_program builds a program and remembers the variant it came from (vertex, fragment and
  defines as one line - the SPIR-V manifest's key), so a backend that cannot compile at run
  time finds the pipeline for the same handle. gpu_use_program and gpu_program_free replace
  32 uses and 2 frees; the overlay's program is recorded under overlay.vert|overlay.frag.
- gpu_query_new / _begin / _end / _result carry R3D_PROF's timers.
- gpu_open, gpu_vsync, gpu_renderer_name and gpu_resize_check carry the context.
- r3d_program_tess is gone: nothing called it and no tessellation shader exists.
- R3D_GLCHECK also checks after framebuffer and renderbuffer changes, viewport, draw buffers,
  program use, texture parameters, the resize check and between frames.

OpenGL frames are byte-identical at the five viewpoints; 59 self-tests pass with and without
R3D_GLCHECK, with no GL error; ludic-dev test 140 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-15 11:33:01 +03:00
parent 447acc11bd
commit ce3986bb02
12 changed files with 109 additions and 59 deletions

View file

@ -32,7 +32,7 @@ function prof_init() -> void {
prof_hits = new []long
prof_ids = words(PROF_SLOTS * PROF_RING)
prof_scratch = words(4)
gl_gen_queries(PROF_SLOTS * PROF_RING, prof_ids)
gpu_query_new(PROF_SLOTS * PROF_RING, prof_ids)
prof_n = 0
prof_frame = 0
prof_active = -1
@ -59,13 +59,13 @@ function prof_begin(name: pointer) -> void {
let s = prof_slot(name)
if s < 0 { return }
prof_active = s
gl_begin_query(GL_TIME_ELAPSED, prof_ids[s * PROF_RING + (prof_frame % PROF_RING)])
gpu_query_begin(prof_ids[s * PROF_RING + (prof_frame % PROF_RING)])
}
function prof_end() -> void {
if not prof_on { return }
if prof_active < 0 { return }
gl_end_query(GL_TIME_ELAPSED)
gpu_query_end()
prof_active = -1
}
@ -78,9 +78,7 @@ function prof_collect() -> void {
var s = 0
while s < prof_n {
let q = prof_ids[s * PROF_RING + slot_frame]
gl_get_query_objectiv(q, GL_QUERY_RESULT_AVAILABLE, prof_scratch)
if prof_scratch[0] != 0 {
gl_get_query_objectui64v(q, GL_QUERY_RESULT, prof_scratch)
if gpu_query_result(q, prof_scratch) {
# the low 32 bits are ample: a pass is far below 4 seconds
prof_ns[s] = prof_ns[s] + prof_scratch[0]
prof_hits[s] = prof_hits[s] + 1