wip(render3d): plan 22.14 - the OpenGL backend removed (suite 306/306, not yet handed over)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
c2a5df4be8
commit
9a4137e9da
8 changed files with 162 additions and 388 deletions
12
changes/render3d-vulkan-only.md
Normal file
12
changes/render3d-vulkan-only.md
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
bump: major
|
||||||
|
type: breaking
|
||||||
|
**render3d draws with Vulkan only; its OpenGL backend is gone.** `gpu.ludic` keeps its seam -
|
||||||
|
every `gpu_*` and `u_*` call a renderer or a game makes is unchanged - but each now speaks to the
|
||||||
|
Vulkan backend alone: no `gl_*` entry point is called from the package, and the GLSL source is
|
||||||
|
no longer assembled at run time (the same files are still what `ludic-dev shaders` compiles to
|
||||||
|
SPIR-V). `gpu_request` accepts any name and runs Vulkan; there is no fallback, so a machine whose
|
||||||
|
Vulkan cannot start is told why (`gpu_fallback_reason`, printed) and `r3d_init` fails.
|
||||||
|
`gpu_is_gl()` stays, always false, for callers that still ask. The `R3D_GLCHECK` error checks went
|
||||||
|
with the backend (their messages were built on every call, checked or not). A program that uses
|
||||||
|
`Vk.*` now also links the runtime's window layer, which still lives with its OpenGL
|
||||||
|
(`gl_width`, `gl_set_drawable`, ...); separating the two is the runtime's own decision.
|
||||||
|
|
@ -150,10 +150,8 @@ export state Render3dState {
|
||||||
gpu_fb: words = null
|
gpu_fb: words = null
|
||||||
gpu_fb_cap: int = 0
|
gpu_fb_cap: int = 0
|
||||||
gpu_fb_cur: int = 0
|
gpu_fb_cur: int = 0
|
||||||
gpu_glcheck: int = -1
|
|
||||||
gpu_rb_samples: int = 0
|
gpu_rb_samples: int = 0
|
||||||
gpu_drawbufs: words = null
|
gpu_drawbufs: words = null
|
||||||
gpu_glcheck_seen: []string = null
|
|
||||||
gpu_variants: []GpuVariant = null
|
gpu_variants: []GpuVariant = null
|
||||||
gvk_ready: bool = false
|
gvk_ready: bool = false
|
||||||
gvk_inst: pointer = null
|
gvk_inst: pointer = null
|
||||||
|
|
@ -554,9 +552,6 @@ export state Render3dState {
|
||||||
r3d_root: string = "packages/ludic.render3d" # where shaders/ lives
|
r3d_root: string = "packages/ludic.render3d" # where shaders/ lives
|
||||||
r3d_assets: string = "assets/polyhaven" # where the CC0 assets live
|
r3d_assets: string = "assets/polyhaven" # where the CC0 assets live
|
||||||
r3d_root_found: bool = false
|
r3d_root_found: bool = false
|
||||||
r3d_noise_src: string = null
|
|
||||||
r3d_lighting_src: string = null
|
|
||||||
r3d_wind_src: string = null
|
|
||||||
r3d_prog_log: int = -1
|
r3d_prog_log: int = -1
|
||||||
q_scratch: floats = null
|
q_scratch: floats = null
|
||||||
q_sy: floats = null # views of q_scratch's second, third and fourth
|
q_sy: floats = null # views of q_scratch's second, third and fourth
|
||||||
|
|
|
||||||
|
|
@ -23,45 +23,31 @@
|
||||||
# this file, and neither may any gl_uniform* call.
|
# this file, and neither may any gl_uniform* call.
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
const GPU_GL: int = 1
|
|
||||||
const GPU_VK: int = 2
|
const GPU_VK: int = 2
|
||||||
|
|
||||||
|
|
||||||
# Ask for a backend before r3d_init ("gl", "opengl", "vk", "vulkan"). The environment
|
# Vulkan is the renderer (OpenGL was retired: maroon-lake docs/plan/22). gpu_request stays so a
|
||||||
# (R3D_GFX) wins over it, so a test or a take can force one whatever the setting says.
|
# caller that asks for a backend by name still builds; whatever it names, Vulkan is what runs.
|
||||||
function gpu_request(render3d_st: mut Render3dState, name: string) -> void { render3d_st.gpu_wanted = gpu_kind_of(name) }
|
function gpu_request(render3d_st: mut Render3dState, name: string) -> void { render3d_st.gpu_wanted = GPU_VK }
|
||||||
|
function gpu_name(kind: int) -> string { return "vulkan" }
|
||||||
|
|
||||||
function gpu_kind_of(name: string) -> int {
|
# Start Vulkan. There is nothing to fall back to: a machine whose Vulkan cannot start is told why
|
||||||
if name == "vk" or name == "vulkan" { return GPU_VK }
|
# (gpu_fallback_reason, printed) and r3d_init fails, so the program can say so and stop.
|
||||||
if name == "gl" or name == "opengl" { return GPU_GL }
|
|
||||||
return GPU_GL
|
|
||||||
}
|
|
||||||
function gpu_name(kind: int) -> string {
|
|
||||||
if kind == GPU_VK { return "vulkan" }
|
|
||||||
return "opengl"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Settle the backend. Anything that cannot run lands on OpenGL with a reason a
|
|
||||||
# caller can show the player (gpu_fallback_reason).
|
|
||||||
function gpu_select(render3d_st: mut Render3dState) -> int {
|
function gpu_select(render3d_st: mut Render3dState) -> int {
|
||||||
if render3d_st.gpu_kind != 0 { return render3d_st.gpu_kind }
|
if render3d_st.gpu_kind != 0 { return render3d_st.gpu_kind }
|
||||||
if r3d_env_has(render3d_st, "R3D_GFX") { render3d_st.gpu_wanted = gpu_kind_of(r3d_env(render3d_st, "R3D_GFX")) }
|
# a window needs a surface: Win32 on Windows, a CAMetalLayer through MoltenVK on macOS
|
||||||
if render3d_st.gpu_wanted == 0 { render3d_st.gpu_wanted = GPU_GL }
|
render3d_st.gvk_want_surface = is_windowed()
|
||||||
render3d_st.gpu_kind = GPU_GL
|
let os = Os.platform()
|
||||||
if render3d_st.gpu_wanted == GPU_VK {
|
if is_windowed() and os != "windows" and os != "macos" { render3d_st.gpu_fallback_reason = `no Vulkan window is built for {os}` }
|
||||||
# a window needs a surface: Win32 on Windows, a CAMetalLayer through MoltenVK on macOS
|
else if not gvk_init(render3d_st) { render3d_st.gpu_fallback_reason = render3d_st.gvk_why }
|
||||||
render3d_st.gvk_want_surface = is_windowed()
|
else if not gvk_manifest(render3d_st) { render3d_st.gpu_fallback_reason = "the renderer's SPIR-V manifest is missing" }
|
||||||
let os = Os.platform()
|
else { render3d_st.gpu_kind = GPU_VK }
|
||||||
if is_windowed() and os != "windows" and os != "macos" { render3d_st.gpu_fallback_reason = `no Vulkan window is built for {os}` }
|
if render3d_st.gpu_kind != GPU_VK { print(`r3d: vulkan cannot start: {render3d_st.gpu_fallback_reason}`) }
|
||||||
else if not gvk_init(render3d_st) { render3d_st.gpu_fallback_reason = render3d_st.gvk_why }
|
|
||||||
else if not gvk_manifest(render3d_st) { render3d_st.gpu_fallback_reason = "the renderer's SPIR-V manifest is missing" }
|
|
||||||
else { render3d_st.gpu_kind = GPU_VK }
|
|
||||||
if render3d_st.gpu_kind == GPU_GL { print(`r3d: vulkan requested: {render3d_st.gpu_fallback_reason}; using opengl`) }
|
|
||||||
}
|
|
||||||
return render3d_st.gpu_kind
|
return render3d_st.gpu_kind
|
||||||
}
|
}
|
||||||
function gpu_backend(render3d_st: Render3dState) -> string { return gpu_name(render3d_st.gpu_kind) }
|
function gpu_backend(render3d_st: Render3dState) -> string { return "vulkan" }
|
||||||
function gpu_is_gl(render3d_st: Render3dState) -> bool { return render3d_st.gpu_kind != GPU_VK }
|
# always false now; kept until the game stops asking (maroon-lake src/app/boot_load.ludic)
|
||||||
|
function gpu_is_gl(render3d_st: Render3dState) -> bool { return false }
|
||||||
|
|
||||||
# ---- render state ----------------------------------------------------------------
|
# ---- render state ----------------------------------------------------------------
|
||||||
# -1 = not known yet: the first set always reaches the driver, so the cache never
|
# -1 = not known yet: the first set always reaches the driver, so the cache never
|
||||||
|
|
@ -77,61 +63,51 @@ function gpu_state_forget(render3d_st: mut Render3dState) -> void {
|
||||||
render3d_st.gpu_s_cull = -1; render3d_st.gpu_s_cull_face = -1; render3d_st.gpu_s_color_write = -1; render3d_st.gpu_s_a2c = -1
|
render3d_st.gpu_s_cull = -1; render3d_st.gpu_s_cull_face = -1; render3d_st.gpu_s_color_write = -1; render3d_st.gpu_s_a2c = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
function gpu_gl_cap(render3d_st: Render3dState, cap: int, on: int) -> void { if render3d_st.gpu_kind == GPU_VK { return }; if on == 1 { gl_enable(cap) } else { gl_disable(cap) } }
|
|
||||||
|
|
||||||
function gpu_depth_test(render3d_st: mut Render3dState, on: bool) -> void {
|
function gpu_depth_test(render3d_st: mut Render3dState, on: bool) -> void {
|
||||||
let v = gpu_b(on)
|
let v = gpu_b(on)
|
||||||
if v == render3d_st.gpu_s_depth_test { return }
|
if v == render3d_st.gpu_s_depth_test { return }
|
||||||
render3d_st.gpu_s_depth_test = v
|
render3d_st.gpu_s_depth_test = v
|
||||||
gpu_gl_cap(render3d_st, GL_DEPTH_TEST, v)
|
|
||||||
}
|
}
|
||||||
# GL_LESS, GL_LEQUAL, GL_EQUAL, GL_ALWAYS, ... (the comparison names are the same in every API)
|
# GL_LESS, GL_LEQUAL, GL_EQUAL, GL_ALWAYS, ... (the comparison names are the same in every API)
|
||||||
function gpu_depth_func(render3d_st: mut Render3dState, f: int) -> void {
|
function gpu_depth_func(render3d_st: mut Render3dState, f: int) -> void {
|
||||||
if f == render3d_st.gpu_s_depth_func { return }
|
if f == render3d_st.gpu_s_depth_func { return }
|
||||||
render3d_st.gpu_s_depth_func = f
|
render3d_st.gpu_s_depth_func = f
|
||||||
if render3d_st.gpu_kind != GPU_VK { gl_depth_func(f) }
|
|
||||||
}
|
}
|
||||||
function gpu_depth_write(render3d_st: mut Render3dState, on: bool) -> void {
|
function gpu_depth_write(render3d_st: mut Render3dState, on: bool) -> void {
|
||||||
let v = gpu_b(on)
|
let v = gpu_b(on)
|
||||||
if v == render3d_st.gpu_s_depth_write { return }
|
if v == render3d_st.gpu_s_depth_write { return }
|
||||||
render3d_st.gpu_s_depth_write = v
|
render3d_st.gpu_s_depth_write = v
|
||||||
if render3d_st.gpu_kind != GPU_VK { gl_depth_mask(v) }
|
|
||||||
}
|
}
|
||||||
function gpu_blend(render3d_st: mut Render3dState, on: bool) -> void {
|
function gpu_blend(render3d_st: mut Render3dState, on: bool) -> void {
|
||||||
let v = gpu_b(on)
|
let v = gpu_b(on)
|
||||||
if v == render3d_st.gpu_s_blend { return }
|
if v == render3d_st.gpu_s_blend { return }
|
||||||
render3d_st.gpu_s_blend = v
|
render3d_st.gpu_s_blend = v
|
||||||
gpu_gl_cap(render3d_st, GL_BLEND, v)
|
|
||||||
}
|
}
|
||||||
function gpu_blend_func(render3d_st: mut Render3dState, src: int, dst: int) -> void {
|
function gpu_blend_func(render3d_st: mut Render3dState, src: int, dst: int) -> void {
|
||||||
if src == render3d_st.gpu_s_blend_src and dst == render3d_st.gpu_s_blend_dst { return }
|
if src == render3d_st.gpu_s_blend_src and dst == render3d_st.gpu_s_blend_dst { return }
|
||||||
render3d_st.gpu_s_blend_src = src; render3d_st.gpu_s_blend_dst = dst
|
render3d_st.gpu_s_blend_src = src; render3d_st.gpu_s_blend_dst = dst
|
||||||
if render3d_st.gpu_kind != GPU_VK { gl_blend_func(src, dst) }
|
|
||||||
}
|
}
|
||||||
function gpu_cull(render3d_st: mut Render3dState, on: bool) -> void {
|
function gpu_cull(render3d_st: mut Render3dState, on: bool) -> void {
|
||||||
let v = gpu_b(on)
|
let v = gpu_b(on)
|
||||||
if v == render3d_st.gpu_s_cull { return }
|
if v == render3d_st.gpu_s_cull { return }
|
||||||
render3d_st.gpu_s_cull = v
|
render3d_st.gpu_s_cull = v
|
||||||
gpu_gl_cap(render3d_st, GL_CULL_FACE, v)
|
|
||||||
}
|
}
|
||||||
# GL_BACK or GL_FRONT
|
# GL_BACK or GL_FRONT
|
||||||
function gpu_cull_face(render3d_st: mut Render3dState, face: int) -> void {
|
function gpu_cull_face(render3d_st: mut Render3dState, face: int) -> void {
|
||||||
if face == render3d_st.gpu_s_cull_face { return }
|
if face == render3d_st.gpu_s_cull_face { return }
|
||||||
render3d_st.gpu_s_cull_face = face
|
render3d_st.gpu_s_cull_face = face
|
||||||
if render3d_st.gpu_kind != GPU_VK { gl_cull_face(face) }
|
|
||||||
}
|
}
|
||||||
# all four channels together: nothing in the renderer writes a partial mask
|
# all four channels together: nothing in the renderer writes a partial mask
|
||||||
function gpu_color_write(render3d_st: mut Render3dState, on: bool) -> void {
|
function gpu_color_write(render3d_st: mut Render3dState, on: bool) -> void {
|
||||||
let v = gpu_b(on)
|
let v = gpu_b(on)
|
||||||
if v == render3d_st.gpu_s_color_write { return }
|
if v == render3d_st.gpu_s_color_write { return }
|
||||||
render3d_st.gpu_s_color_write = v
|
render3d_st.gpu_s_color_write = v
|
||||||
if render3d_st.gpu_kind != GPU_VK { gl_color_mask(v, v, v, v) }
|
|
||||||
}
|
}
|
||||||
function gpu_alpha_to_coverage(render3d_st: mut Render3dState, on: bool) -> void {
|
function gpu_alpha_to_coverage(render3d_st: mut Render3dState, on: bool) -> void {
|
||||||
let v = gpu_b(on)
|
let v = gpu_b(on)
|
||||||
if v == render3d_st.gpu_s_a2c { return }
|
if v == render3d_st.gpu_s_a2c { return }
|
||||||
render3d_st.gpu_s_a2c = v
|
render3d_st.gpu_s_a2c = v
|
||||||
gpu_gl_cap(render3d_st, GL_SAMPLE_ALPHA_TO_COVERAGE, v)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Depth bias for the shadow casters; (0, 0) turns it off. factor/units are fixed, as
|
# Depth bias for the shadow casters; (0, 0) turns it off. factor/units are fixed, as
|
||||||
|
|
@ -139,25 +115,17 @@ function gpu_alpha_to_coverage(render3d_st: mut Render3dState, on: bool) -> void
|
||||||
function gpu_depth_bias(render3d_st: mut Render3dState, factor: fixed, units: fixed) -> void {
|
function gpu_depth_bias(render3d_st: mut Render3dState, factor: fixed, units: fixed) -> void {
|
||||||
var v = 1
|
var v = 1
|
||||||
if factor == 0.0 and units == 0.0 { v = 0 }
|
if factor == 0.0 and units == 0.0 { v = 0 }
|
||||||
if v != render3d_st.gpu_s_bias { render3d_st.gpu_s_bias = v; gpu_gl_cap(render3d_st, GL_POLYGON_OFFSET_FILL, v) }
|
if v != render3d_st.gpu_s_bias { render3d_st.gpu_s_bias = v }
|
||||||
render3d_st.gpu_s_bias_f = float(factor); render3d_st.gpu_s_bias_u = float(units)
|
render3d_st.gpu_s_bias_f = float(factor); render3d_st.gpu_s_bias_u = float(units)
|
||||||
if v == 1 and render3d_st.gpu_kind != GPU_VK { gl_polygon_offset(factor, units) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# A scissor rectangle in top-down pixels (x, y from the top-left of the drawable), or
|
# A scissor rectangle in top-down pixels (x, y from the top-left of the drawable), or
|
||||||
# off. Every API but OpenGL counts rows from the top; the GL backend flips it.
|
# off. Every API but OpenGL counts rows from the top; the GL backend flips it.
|
||||||
function gpu_scissor(render3d_st: mut Render3dState, x: int, y_top: int, w: int, h: int) -> void {
|
function gpu_scissor(render3d_st: mut Render3dState, x: int, y_top: int, w: int, h: int) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { render3d_st.gpu_s_scissor = 1; gvk_scissor(render3d_st, x, gl_height() - y_top - h, w, h); return }
|
render3d_st.gpu_s_scissor = 1; gvk_scissor(render3d_st, x, gl_height() - y_top - h, w, h)
|
||||||
if render3d_st.gpu_s_scissor != 1 { render3d_st.gpu_s_scissor = 1; gl_enable(GL_SCISSOR_TEST) }
|
|
||||||
gl_scissor(x, gl_height() - y_top - h, w, h)
|
|
||||||
gpu_glcheck_after(render3d_st, "scissor")
|
|
||||||
}
|
}
|
||||||
function gpu_scissor_off(render3d_st: mut Render3dState) -> void {
|
function gpu_scissor_off(render3d_st: mut Render3dState) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { render3d_st.gpu_s_scissor = 0; gvk_scissor_off(render3d_st); return }
|
render3d_st.gpu_s_scissor = 0; gvk_scissor_off(render3d_st)
|
||||||
if render3d_st.gpu_s_scissor == 0 { return }
|
|
||||||
render3d_st.gpu_s_scissor = 0
|
|
||||||
gl_disable(GL_SCISSOR_TEST)
|
|
||||||
gpu_glcheck_after(render3d_st, "scissor off")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---- uniforms --------------------------------------------------------------------
|
# ---- uniforms --------------------------------------------------------------------
|
||||||
|
|
@ -165,20 +133,14 @@ function gpu_scissor_off(render3d_st: mut Render3dState) -> void {
|
||||||
# the handle is the location. On a pipeline API it will name a slot in the program's
|
# the handle is the location. On a pipeline API it will name a slot in the program's
|
||||||
# uniform block, so the u_* setters below are the only code that knows which. Arrays
|
# uniform block, so the u_* setters below are the only code that knows which. Arrays
|
||||||
# are looked up by their first element ("u_bones[0]"), as every GL driver accepts.
|
# are looked up by their first element ("u_bones[0]"), as every GL driver accepts.
|
||||||
function gpu_uniform(render3d_st: Render3dState, prog: int, name: string) -> int { if render3d_st.gpu_kind == GPU_VK { return gvk_uniform(render3d_st, prog, name) }; return gl_get_uniform_location(prog, name) }
|
function gpu_uniform(render3d_st: Render3dState, prog: int, name: string) -> int { return gvk_uniform(render3d_st, prog, name) }
|
||||||
|
|
||||||
# ---- programs ----------------------------------------------------------------------
|
# ---- programs ----------------------------------------------------------------------
|
||||||
# A program remembers the variant it was built from - vertex file, fragment file and the
|
# A program remembers the variant it was built from - vertex file, fragment file and the
|
||||||
# defines on one line, the key the SPIR-V manifest uses - which is how a backend that cannot
|
# defines on one line, the key the SPIR-V manifest uses - which is how a backend that cannot
|
||||||
# compile shaders at run time finds its pipeline for the same handle.
|
# compile shaders at run time finds its pipeline for the same handle.
|
||||||
function gpu_program(render3d_st: mut Render3dState, vs_src: string, fs_src: string, vs: string, fs: string, defines: string) -> int {
|
function gpu_program(render3d_st: mut Render3dState, vs: string, fs: string, defines: string) -> int {
|
||||||
if render3d_st.gpu_kind == GPU_VK { return gvk_program_new(render3d_st, vs, fs, defines) }
|
return gvk_program_new(render3d_st, vs, fs, defines)
|
||||||
let p = gl_program(vs_src, fs_src)
|
|
||||||
if p == 0 { return 0 }
|
|
||||||
if render3d_st.gpu_prog_ids == null { render3d_st.gpu_prog_ids = new []int; render3d_st.gpu_prog_keys = new []string }
|
|
||||||
push(render3d_st.gpu_prog_ids, p)
|
|
||||||
push(render3d_st.gpu_prog_keys, `{vs}|{fs}|{Text.replace(defines, "\n", ";")}`)
|
|
||||||
return p
|
|
||||||
}
|
}
|
||||||
# the manifest key a program was built from; "" for one this layer did not build
|
# the manifest key a program was built from; "" for one this layer did not build
|
||||||
function gpu_program_key(render3d_st: Render3dState, p: int) -> string {
|
function gpu_program_key(render3d_st: Render3dState, p: int) -> string {
|
||||||
|
|
@ -186,54 +148,44 @@ function gpu_program_key(render3d_st: Render3dState, p: int) -> string {
|
||||||
for i in 0 .. len(render3d_st.gpu_prog_ids) { if render3d_st.gpu_prog_ids[i] == p { return render3d_st.gpu_prog_keys[i] } }
|
for i in 0 .. len(render3d_st.gpu_prog_ids) { if render3d_st.gpu_prog_ids[i] == p { return render3d_st.gpu_prog_keys[i] } }
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
function gpu_use_program(render3d_st: mut Render3dState, p: int) -> void { ds_program_change(render3d_st, render3d_st.gpu_prog_cur, p); if render3d_st.gpu_kind == GPU_VK { render3d_st.gpu_prog_cur = p; return }; gl_use_program(p); render3d_st.gpu_prog_cur = p; gpu_glcheck_after(render3d_st, "use program") }
|
function gpu_use_program(render3d_st: mut Render3dState, p: int) -> void { ds_program_change(render3d_st, render3d_st.gpu_prog_cur, p); render3d_st.gpu_prog_cur = p }
|
||||||
function gpu_program_free(render3d_st: mut Render3dState, p: int) -> void {
|
function gpu_program_free(render3d_st: mut Render3dState, p: int) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { return }
|
|
||||||
if p == 0 { return }
|
|
||||||
gl_delete_program(p)
|
|
||||||
if render3d_st.gpu_prog_cur == p { render3d_st.gpu_prog_cur = 0 }
|
|
||||||
if render3d_st.gpu_prog_ids != null { for i in 0 .. len(render3d_st.gpu_prog_ids) { if render3d_st.gpu_prog_ids[i] == p { render3d_st.gpu_prog_ids[i] = 0; render3d_st.gpu_prog_keys[i] = "" } } }
|
|
||||||
gpu_glcheck_after(render3d_st, "program free")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---- GPU timers (R3D_PROF) ----------------------------------------------------------
|
# ---- GPU timers (R3D_PROF) ----------------------------------------------------------
|
||||||
function gpu_query_new(render3d_st: mut Render3dState, n: int, ids: words) -> void { if render3d_st.gpu_kind == GPU_VK { gvk_query_new(render3d_st, n, ids); return }; gl_gen_queries(n, ids) }
|
function gpu_query_new(render3d_st: mut Render3dState, n: int, ids: words) -> void { gvk_query_new(render3d_st, n, ids) }
|
||||||
function gpu_query_begin(render3d_st: mut Render3dState, id: int) -> void { if render3d_st.gpu_kind == GPU_VK { gvk_query_begin(render3d_st, id); return }; gl_begin_query(GL_TIME_ELAPSED, id) }
|
function gpu_query_begin(render3d_st: mut Render3dState, id: int) -> void { gvk_query_begin(render3d_st, id) }
|
||||||
function gpu_query_end(render3d_st: mut Render3dState) -> void { if render3d_st.gpu_kind == GPU_VK { gvk_query_end(render3d_st); return }; gl_end_query(GL_TIME_ELAPSED) }
|
function gpu_query_end(render3d_st: mut Render3dState) -> void { gvk_query_end(render3d_st) }
|
||||||
# true once the query has its result; the nanoseconds (low 32 bits) are then in out[0]
|
# true once the query has its result; the nanoseconds (low 32 bits) are then in out[0]
|
||||||
function gpu_query_result(render3d_st: mut Render3dState, id: int, out: words) -> bool {
|
function gpu_query_result(render3d_st: mut Render3dState, id: int, out: words) -> bool {
|
||||||
if render3d_st.gpu_kind == GPU_VK { return gvk_query_result(render3d_st, id, out) }
|
return gvk_query_result(render3d_st, id, out)
|
||||||
gl_get_query_objectiv(id, GL_QUERY_RESULT_AVAILABLE, out)
|
|
||||||
if out[0] == 0 { return false }
|
|
||||||
gl_get_query_objectui64v(id, GL_QUERY_RESULT, out)
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---- the context --------------------------------------------------------------------
|
# ---- the context --------------------------------------------------------------------
|
||||||
function gpu_open(render3d_st: mut Render3dState, w: int, h: int, title: string) -> bool { if render3d_st.gpu_kind == GPU_VK { return gvk_open(render3d_st, w, h, title) }; return gl_open(w, h, title) }
|
function gpu_open(render3d_st: mut Render3dState, w: int, h: int, title: string) -> bool { return gvk_open(render3d_st, w, h, title) }
|
||||||
function gpu_vsync(render3d_st: mut Render3dState, on: int) -> void { if render3d_st.gpu_kind == GPU_VK { render3d_st.gvk_vsync = on != 0; if render3d_st.gvk_swap != 0 { render3d_st.gvk_swap_stale = true }; return }; gl_vsync(on) }
|
function gpu_vsync(render3d_st: mut Render3dState, on: int) -> void { render3d_st.gvk_vsync = on != 0; if render3d_st.gvk_swap != 0 { render3d_st.gvk_swap_stale = true } }
|
||||||
function gpu_renderer_name(render3d_st: Render3dState) -> string { if render3d_st.gpu_kind == GPU_VK { return `{render3d_st.gvk_device_name} (Vulkan)` }; return gl_get_string(GL_RENDERER) }
|
function gpu_renderer_name(render3d_st: Render3dState) -> string { return `{render3d_st.gvk_device_name} (Vulkan)` }
|
||||||
function gpu_resize_check(render3d_st: mut Render3dState) -> bool { if render3d_st.gpu_kind == GPU_VK { return gvk_resize_check(render3d_st) }; let r = gl_resize_check(); gpu_glcheck_after(render3d_st, "the resize check"); return r }
|
function gpu_resize_check(render3d_st: mut Render3dState) -> bool { return gvk_resize_check(render3d_st) }
|
||||||
# the finished frame: presented to the window, or (headless) the GPU's work finished
|
# the finished frame: presented to the window, or (headless) the GPU's work finished
|
||||||
function gpu_present(render3d_st: mut Render3dState) -> void { if render3d_st.gpu_kind == GPU_VK { gvk_present(render3d_st); return }; Gl.swap() }
|
function gpu_present(render3d_st: mut Render3dState) -> void { gvk_present(render3d_st) }
|
||||||
# the frame as it will be presented, to a binary PPM with the top row first; before gpu_present
|
# the frame as it will be presented, to a binary PPM with the top row first; before gpu_present
|
||||||
function gpu_screenshot(render3d_st: mut Render3dState, path: string) -> bool { if render3d_st.gpu_kind == GPU_VK { return gvk_screenshot(render3d_st, path) }; return Gl.screenshot(path: path) }
|
function gpu_screenshot(render3d_st: mut Render3dState, path: string) -> bool { return gvk_screenshot(render3d_st, path) }
|
||||||
|
|
||||||
function gpu_tmp(render3d_st: mut Render3dState) -> words { if render3d_st.gpu_u_tmp == null { render3d_st.gpu_u_tmp = words(4) }; return render3d_st.gpu_u_tmp }
|
function gpu_tmp(render3d_st: mut Render3dState) -> words { if render3d_st.gpu_u_tmp == null { render3d_st.gpu_u_tmp = words(4) }; return render3d_st.gpu_u_tmp }
|
||||||
# float bits (IEEE singles in an int), like every other number in the renderer
|
# float bits (IEEE singles in an int), like every other number in the renderer
|
||||||
function u_f(render3d_st: mut Render3dState, loc: int, v: float) -> void { if render3d_st.gpu_kind == GPU_VK { let t = gpu_tmp(render3d_st); t[0] = float_bits(v); gvk_u_set(render3d_st, loc, data_of(t), 4, 1); return }; let t = gpu_tmp(render3d_st); t[0] = float_bits(v); gl_uniform1fv(loc, 1, t) }
|
function u_f(render3d_st: mut Render3dState, loc: int, v: float) -> void { let t = gpu_tmp(render3d_st); t[0] = float_bits(v); gvk_u_set(render3d_st, loc, data_of(t), 4, 1) }
|
||||||
function u_f2(render3d_st: mut Render3dState, loc: int, x: float, y: float) -> void { if render3d_st.gpu_kind == GPU_VK { let t = gpu_tmp(render3d_st); t[0] = float_bits(x); t[1] = float_bits(y); gvk_u_set(render3d_st, loc, data_of(t), 8, 1); return }; let t = gpu_tmp(render3d_st); t[0] = float_bits(x); t[1] = float_bits(y); gl_uniform2fv(loc, 1, t) }
|
function u_f2(render3d_st: mut Render3dState, loc: int, x: float, y: float) -> void { let t = gpu_tmp(render3d_st); t[0] = float_bits(x); t[1] = float_bits(y); gvk_u_set(render3d_st, loc, data_of(t), 8, 1) }
|
||||||
function u_f3(render3d_st: mut Render3dState, loc: int, x: float, y: float, z: float) -> void { if render3d_st.gpu_kind == GPU_VK { let t = gpu_tmp(render3d_st); t[0] = float_bits(x); t[1] = float_bits(y); t[2] = float_bits(z); gvk_u_set(render3d_st, loc, data_of(t), 12, 1); return }; let t = gpu_tmp(render3d_st); t[0] = float_bits(x); t[1] = float_bits(y); t[2] = float_bits(z); gl_uniform3fv(loc, 1, t) }
|
function u_f3(render3d_st: mut Render3dState, loc: int, x: float, y: float, z: float) -> void { let t = gpu_tmp(render3d_st); t[0] = float_bits(x); t[1] = float_bits(y); t[2] = float_bits(z); gvk_u_set(render3d_st, loc, data_of(t), 12, 1) }
|
||||||
function u_f4(render3d_st: mut Render3dState, loc: int, x: float, y: float, z: float, w: float) -> void { if render3d_st.gpu_kind == GPU_VK { let t = gpu_tmp(render3d_st); t[0] = float_bits(x); t[1] = float_bits(y); t[2] = float_bits(z); t[3] = float_bits(w); gvk_u_set(render3d_st, loc, data_of(t), 16, 1); return }; let t = gpu_tmp(render3d_st); t[0] = float_bits(x); t[1] = float_bits(y); t[2] = float_bits(z); t[3] = float_bits(w); gl_uniform4fv(loc, 1, t) }
|
function u_f4(render3d_st: mut Render3dState, loc: int, x: float, y: float, z: float, w: float) -> void { let t = gpu_tmp(render3d_st); t[0] = float_bits(x); t[1] = float_bits(y); t[2] = float_bits(z); t[3] = float_bits(w); gvk_u_set(render3d_st, loc, data_of(t), 16, 1) }
|
||||||
function u_v3(render3d_st: mut Render3dState, loc: int, v: floats) -> void { if render3d_st.gpu_kind == GPU_VK { gvk_u_set(render3d_st, loc, data_of(v), 12, 1); return }; gl_uniform3fv(loc, 1, v) }
|
function u_v3(render3d_st: mut Render3dState, loc: int, v: floats) -> void { gvk_u_set(render3d_st, loc, data_of(v), 12, 1) }
|
||||||
function u_fv(render3d_st: mut Render3dState, loc: int, n: int, v: floats) -> void { if render3d_st.gpu_kind == GPU_VK { gvk_u_set(render3d_st, loc, data_of(v), 4, n); return }; gl_uniform1fv(loc, n, v) }
|
function u_fv(render3d_st: mut Render3dState, loc: int, n: int, v: floats) -> void { gvk_u_set(render3d_st, loc, data_of(v), 4, n) }
|
||||||
# n vec4s from 4n float bits. Not u_fv with 4n: on Vulkan an array element is copied at the size
|
# n vec4s from 4n float bits. Not u_fv with 4n: on Vulkan an array element is copied at the size
|
||||||
# given and placed at the array's stride, so a vec4 array fed floats got one float per element -
|
# given and placed at the array's stride, so a vec4 array fed floats got one float per element -
|
||||||
# which drew the chunked grass with every tile at a nonsense corner and zero blades a cell.
|
# which drew the chunked grass with every tile at a nonsense corner and zero blades a cell.
|
||||||
function u_f4v(render3d_st: mut Render3dState, loc: int, n: int, v: floats) -> void { if render3d_st.gpu_kind == GPU_VK { gvk_u_set(render3d_st, loc, data_of(v), 16, n); return }; gl_uniform4fv(loc, n, v) }
|
function u_f4v(render3d_st: mut Render3dState, loc: int, n: int, v: floats) -> void { gvk_u_set(render3d_st, loc, data_of(v), 16, n) }
|
||||||
function u_mat4(render3d_st: mut Render3dState, loc: int, m: floats) -> void { if render3d_st.gpu_kind == GPU_VK { gvk_u_set(render3d_st, loc, data_of(m), 64, 1); return }; gl_uniform_matrix4fv(loc, 1, 0, m) }
|
function u_mat4(render3d_st: mut Render3dState, loc: int, m: floats) -> void { gvk_u_set(render3d_st, loc, data_of(m), 64, 1) }
|
||||||
function u_mat4n(render3d_st: mut Render3dState, loc: int, n: int, m: floats) -> void { if render3d_st.gpu_kind == GPU_VK { gvk_u_set(render3d_st, loc, data_of(m), 64, n); return }; gl_uniform_matrix4fv(loc, n, 0, m) }
|
function u_mat4n(render3d_st: mut Render3dState, loc: int, n: int, m: floats) -> void { gvk_u_set(render3d_st, loc, data_of(m), 64, n) }
|
||||||
function u_i(render3d_st: mut Render3dState, loc: int, v: int) -> void { if render3d_st.gpu_kind == GPU_VK { let t = gpu_tmp(render3d_st); t[0] = v; gvk_u_set(render3d_st, loc, data_of(t), 4, 1); return }; gl_uniform1i(loc, v) }
|
function u_i(render3d_st: mut Render3dState, loc: int, v: int) -> void { let t = gpu_tmp(render3d_st); t[0] = v; gvk_u_set(render3d_st, loc, data_of(t), 4, 1) }
|
||||||
|
|
||||||
# ---- what this machine can do ----------------------------------------------------
|
# ---- what this machine can do ----------------------------------------------------
|
||||||
# The advanced graphics features are Windows features: the Vulkan renderer, ray tracing,
|
# The advanced graphics features are Windows features: the Vulkan renderer, ray tracing,
|
||||||
|
|
@ -310,7 +262,7 @@ function gpu_caps_probe(render3d_st: mut Render3dState) -> void {
|
||||||
# interposer, and a second instance made and destroyed under it is what this avoids.
|
# interposer, and a second instance made and destroyed under it is what this avoids.
|
||||||
var inst: pointer = null
|
var inst: pointer = null
|
||||||
var own = false
|
var own = false
|
||||||
if render3d_st.gpu_kind == GPU_VK and render3d_st.gvk_ready {
|
if render3d_st.gvk_ready {
|
||||||
inst = render3d_st.gvk_inst
|
inst = render3d_st.gvk_inst
|
||||||
} else {
|
} else {
|
||||||
let out = bytes(8)
|
let out = bytes(8)
|
||||||
|
|
@ -412,18 +364,12 @@ function gpu_mesh_new(render3d_st: Render3dState) -> Mesh {
|
||||||
m.attrs = words(GPU_MAX_ATTRS * GPU_ATTR_W)
|
m.attrs = words(GPU_MAX_ATTRS * GPU_ATTR_W)
|
||||||
for i in 0 .. GPU_MAX_ATTRS * GPU_ATTR_W { m.attrs[i] = 0 }
|
for i in 0 .. GPU_MAX_ATTRS * GPU_ATTR_W { m.attrs[i] = 0 }
|
||||||
m.vbufs = words(GPU_MAX_VBUFS)
|
m.vbufs = words(GPU_MAX_VBUFS)
|
||||||
if render3d_st.gpu_kind != GPU_VK { m.vao = gl_vao() }
|
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
# a vertex buffer for the mesh being built (data may be null: storage only); returns it
|
# a vertex buffer for the mesh being built (data may be null: storage only); returns it
|
||||||
function gpu_mesh_vertices(render3d_st: mut Render3dState, m: Mesh, data: pointer, nbytes: int, usage: int) -> int {
|
function gpu_mesh_vertices(render3d_st: mut Render3dState, m: Mesh, data: pointer, nbytes: int, usage: int) -> int {
|
||||||
var b = 0
|
var b = 0
|
||||||
if render3d_st.gpu_kind == GPU_VK { b = gvk_buf_new(render3d_st); gvk_buf_upload(render3d_st, b, nbytes, data) }
|
b = gvk_buf_new(render3d_st); gvk_buf_upload(render3d_st, b, nbytes, data)
|
||||||
else {
|
|
||||||
b = gl_buffer()
|
|
||||||
gl_bind_buffer(GL_ARRAY_BUFFER, b)
|
|
||||||
gl_buffer_data(GL_ARRAY_BUFFER, nbytes, data, gpu_gl_usage(usage))
|
|
||||||
}
|
|
||||||
if m.vbo == 0 { m.vbo = b }
|
if m.vbo == 0 { m.vbo = b }
|
||||||
if m.n_vbufs < GPU_MAX_VBUFS { m.vbufs[m.n_vbufs] = b; m.n_vbufs += 1 }
|
if m.n_vbufs < GPU_MAX_VBUFS { m.vbufs[m.n_vbufs] = b; m.n_vbufs += 1 }
|
||||||
m.cur_buf = b
|
m.cur_buf = b
|
||||||
|
|
@ -442,57 +388,39 @@ function gpu_mesh_record(m: Mesh, index: int, comps: int, type: int, stride: int
|
||||||
}
|
}
|
||||||
# attribute `index` read from the last vertex buffer (stride 0: tightly packed)
|
# attribute `index` read from the last vertex buffer (stride 0: tightly packed)
|
||||||
function gpu_mesh_attr(render3d_st: Render3dState, m: Mesh, index: int, comps: int, type: int, stride: int, offset: int, normalized: bool) -> void {
|
function gpu_mesh_attr(render3d_st: Render3dState, m: Mesh, index: int, comps: int, type: int, stride: int, offset: int, normalized: bool) -> void {
|
||||||
if render3d_st.gpu_kind != GPU_VK {
|
|
||||||
gl_enable_vertex_attrib_array(index)
|
|
||||||
gl_vertex_attrib_pointer(index, comps, gpu_gl_type(type), gpu_b(normalized), stride, gl_ptr(null, offset))
|
|
||||||
}
|
|
||||||
gpu_mesh_record(m, index, comps, type, stride, offset, normalized, false)
|
gpu_mesh_record(m, index, comps, type, stride, offset, normalized, false)
|
||||||
}
|
}
|
||||||
# the index buffer: 4-byte or 2-byte indices
|
# the index buffer: 4-byte or 2-byte indices
|
||||||
function gpu_mesh_indices(render3d_st: mut Render3dState, m: Mesh, data: pointer, nbytes: int, index_bytes: int) -> void {
|
function gpu_mesh_indices(render3d_st: mut Render3dState, m: Mesh, data: pointer, nbytes: int, index_bytes: int) -> void {
|
||||||
m.itype = GL_UNSIGNED_INT
|
m.itype = GL_UNSIGNED_INT
|
||||||
if index_bytes == 2 { m.itype = GL_UNSIGNED_SHORT }
|
if index_bytes == 2 { m.itype = GL_UNSIGNED_SHORT }
|
||||||
if render3d_st.gpu_kind == GPU_VK { m.ebo = gvk_buf_new(render3d_st); gvk_buf_upload(render3d_st, m.ebo, nbytes, data); return }
|
|
||||||
m.ebo = gl_buffer()
|
m.ebo = gvk_buf_new(render3d_st); gvk_buf_upload(render3d_st, m.ebo, nbytes, data)
|
||||||
gl_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, m.ebo)
|
|
||||||
gl_buffer_data(GL_ELEMENT_ARRAY_BUFFER, nbytes, data, GL_STATIC_DRAW)
|
|
||||||
}
|
}
|
||||||
# finished describing: nothing else is bound to it by accident
|
# finished describing: nothing else is bound to it by accident
|
||||||
function gpu_mesh_done(render3d_st: Render3dState, m: Mesh) -> void { if render3d_st.gpu_kind == GPU_VK { return }; gl_bind_vertex_array(0) }
|
function gpu_mesh_done(render3d_st: Render3dState, m: Mesh) -> void { }
|
||||||
|
|
||||||
# Per-instance data: `buf` feeds the attributes named next, one element per instance. A mesh
|
# Per-instance data: `buf` feeds the attributes named next, one element per instance. A mesh
|
||||||
# drawn from different instance buffers (the scatter layers' LOD buckets) is re-pointed here
|
# drawn from different instance buffers (the scatter layers' LOD buckets) is re-pointed here
|
||||||
# before each draw; on Vulkan that is a vertex-buffer binding, not a change of layout.
|
# before each draw; on Vulkan that is a vertex-buffer binding, not a change of layout.
|
||||||
function gpu_mesh_bind_instances(render3d_st: Render3dState, m: Mesh, buf: int) -> void {
|
function gpu_mesh_bind_instances(render3d_st: Render3dState, m: Mesh, buf: int) -> void {
|
||||||
if render3d_st.gpu_kind != GPU_VK {
|
|
||||||
gl_bind_vertex_array(m.vao)
|
|
||||||
gl_bind_buffer(GL_ARRAY_BUFFER, buf)
|
|
||||||
}
|
|
||||||
m.cur_buf = buf
|
m.cur_buf = buf
|
||||||
m.ibuf = buf
|
m.ibuf = buf
|
||||||
}
|
}
|
||||||
function gpu_mesh_attr_inst(render3d_st: Render3dState, m: Mesh, index: int, comps: int, type: int, stride: int, offset: int) -> void {
|
function gpu_mesh_attr_inst(render3d_st: Render3dState, m: Mesh, index: int, comps: int, type: int, stride: int, offset: int) -> void {
|
||||||
if render3d_st.gpu_kind != GPU_VK {
|
|
||||||
gl_enable_vertex_attrib_array(index)
|
|
||||||
gl_vertex_attrib_pointer(index, comps, gpu_gl_type(type), 0, stride, gl_ptr(null, offset))
|
|
||||||
gl_vertex_attrib_divisor(index, 1)
|
|
||||||
}
|
|
||||||
gpu_mesh_record(m, index, comps, type, stride, offset, false, true)
|
gpu_mesh_record(m, index, comps, type, stride, offset, false, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
# a buffer on its own (instances, a stream): made, filled whole, freed
|
# a buffer on its own (instances, a stream): made, filled whole, freed
|
||||||
function gpu_buffer_new(render3d_st: mut Render3dState) -> int { if render3d_st.gpu_kind == GPU_VK { return gvk_buf_new(render3d_st) }; return gl_buffer() }
|
function gpu_buffer_new(render3d_st: mut Render3dState) -> int { return gvk_buf_new(render3d_st) }
|
||||||
function gpu_buffer_upload(render3d_st: mut Render3dState, buf: int, nbytes: int, data: pointer, usage: int) -> void {
|
function gpu_buffer_upload(render3d_st: mut Render3dState, buf: int, nbytes: int, data: pointer, usage: int) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_buf_upload(render3d_st, buf, nbytes, data); return }
|
gvk_buf_upload(render3d_st, buf, nbytes, data)
|
||||||
gl_bind_buffer(GL_ARRAY_BUFFER, buf)
|
|
||||||
gl_buffer_data(GL_ARRAY_BUFFER, nbytes, data, gpu_gl_usage(usage))
|
|
||||||
}
|
}
|
||||||
function gpu_buffer_free(render3d_st: mut Render3dState, buf: int) -> void {
|
function gpu_buffer_free(render3d_st: mut Render3dState, buf: int) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { if buf > 0 { gvk_buf_release(render3d_st, buf) }; return }
|
if buf > 0 { gvk_buf_release(render3d_st, buf) }
|
||||||
if buf == 0 { return }
|
|
||||||
let ids = gpu_tmp(render3d_st)
|
|
||||||
ids[0] = buf
|
|
||||||
gl_delete_buffers(1, ids)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---- compute and indirect draws (Vulkan) ----------------------------------------------------
|
# ---- compute and indirect draws (Vulkan) ----------------------------------------------------
|
||||||
|
|
@ -500,92 +428,69 @@ function gpu_buffer_free(render3d_st: mut Render3dState, buf: int) -> void {
|
||||||
# the draws then read. OpenGL here is 4.1 (macOS) with no compute, so gpu_compute is 0 there and
|
# the draws then read. OpenGL here is 4.1 (macOS) with no compute, so gpu_compute is 0 there and
|
||||||
# a caller keeps its CPU path. A compute program's binding 0 is its parameter block (params,
|
# a caller keeps its CPU path. A compute program's binding 0 is its parameter block (params,
|
||||||
# copied at the dispatch); bindings 1.. are `bufs`, gpu buffers.
|
# copied at the dispatch); bindings 1.. are `bufs`, gpu buffers.
|
||||||
function gpu_has_compute(render3d_st: Render3dState) -> bool { return render3d_st.gpu_kind == GPU_VK }
|
function gpu_has_compute(render3d_st: Render3dState) -> bool { return true }
|
||||||
# several records in one indirect draw, each with its own firstInstance
|
# several records in one indirect draw, each with its own firstInstance
|
||||||
function gpu_has_mdi(render3d_st: Render3dState) -> bool { return render3d_st.gpu_kind == GPU_VK and render3d_st.gvk_has_mdi }
|
function gpu_has_mdi(render3d_st: Render3dState) -> bool { return render3d_st.gvk_has_mdi }
|
||||||
# mesh shaders (VK_EXT_mesh_shader): a *.mesh program drawn with gpu_draw_mesh_tasks
|
# mesh shaders (VK_EXT_mesh_shader): a *.mesh program drawn with gpu_draw_mesh_tasks
|
||||||
function gpu_has_mesh(render3d_st: Render3dState) -> bool { return render3d_st.gpu_kind == GPU_VK and render3d_st.gvk_has_mesh }
|
function gpu_has_mesh(render3d_st: Render3dState) -> bool { return render3d_st.gvk_has_mesh }
|
||||||
function gpu_draw_mesh_tasks(render3d_st: mut Render3dState, x: int, y: int, z: int) -> void { if render3d_st.gpu_kind == GPU_VK { gvk_draw_mesh_tasks_now(render3d_st, x, y, z) } }
|
function gpu_draw_mesh_tasks(render3d_st: mut Render3dState, x: int, y: int, z: int) -> void { gvk_draw_mesh_tasks_now(render3d_st, x, y, z) }
|
||||||
function gpu_compute(render3d_st: mut Render3dState, name: string, n_bufs: int) -> int {
|
function gpu_compute(render3d_st: mut Render3dState, name: string, n_bufs: int) -> int {
|
||||||
if render3d_st.gpu_kind != GPU_VK { return 0 }
|
|
||||||
return gvk_compute_new(render3d_st, name, n_bufs)
|
return gvk_compute_new(render3d_st, name, n_bufs)
|
||||||
}
|
}
|
||||||
function gpu_dispatch(render3d_st: mut Render3dState, c: int, params: pointer, n_params: int, bufs: words, groups: int) -> void {
|
function gpu_dispatch(render3d_st: mut Render3dState, c: int, params: pointer, n_params: int, bufs: words, groups: int) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK and c > 0 { gvk_dispatch(render3d_st, c, params, n_params, bufs, groups, 1, 1) }
|
if c > 0 { gvk_dispatch(render3d_st, c, params, n_params, bufs, groups, 1, 1) }
|
||||||
}
|
}
|
||||||
# a compute program that also samples n_tex textures (bound after its buffers), and its 2-D dispatch
|
# a compute program that also samples n_tex textures (bound after its buffers), and its 2-D dispatch
|
||||||
function gpu_compute_tex(render3d_st: mut Render3dState, name: string, n_bufs: int, n_tex: int) -> int {
|
function gpu_compute_tex(render3d_st: mut Render3dState, name: string, n_bufs: int, n_tex: int) -> int {
|
||||||
if render3d_st.gpu_kind != GPU_VK { return 0 }
|
|
||||||
return gvk_compute_new_tex(render3d_st, name, n_bufs, n_tex)
|
return gvk_compute_new_tex(render3d_st, name, n_bufs, n_tex)
|
||||||
}
|
}
|
||||||
function gpu_dispatch_tex(render3d_st: mut Render3dState, c: int, params: pointer, n_params: int, bufs: words, texs: words, gx: int, gy: int) -> void {
|
function gpu_dispatch_tex(render3d_st: mut Render3dState, c: int, params: pointer, n_params: int, bufs: words, texs: words, gx: int, gy: int) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK and c > 0 { gvk_dispatch_tex(render3d_st, c, params, n_params, bufs, texs, gx, gy, 1) }
|
if c > 0 { gvk_dispatch_tex(render3d_st, c, params, n_params, bufs, texs, gx, gy, 1) }
|
||||||
}
|
}
|
||||||
# a buffer a compute pass writes (never reallocated under a draw that reads it)
|
# a buffer a compute pass writes (never reallocated under a draw that reads it)
|
||||||
function gpu_buffer_gpu_owned(render3d_st: mut Render3dState, buf: int) -> void { if render3d_st.gpu_kind == GPU_VK { gvk_buf_gpu_owned(render3d_st, buf) } }
|
function gpu_buffer_gpu_owned(render3d_st: mut Render3dState, buf: int) -> void { gvk_buf_gpu_owned(render3d_st, buf) }
|
||||||
# the host-visible contents of a buffer, for a readback after gpu_finish; null on OpenGL
|
# the host-visible contents of a buffer, for a readback after gpu_finish; null on OpenGL
|
||||||
function gpu_buffer_map(render3d_st: Render3dState, buf: int) -> pointer {
|
function gpu_buffer_map(render3d_st: Render3dState, buf: int) -> pointer {
|
||||||
if render3d_st.gpu_kind != GPU_VK or buf <= 0 { return null }
|
if buf <= 0 { return null }
|
||||||
return render3d_st.gvk_buf_map[buf]
|
return render3d_st.gvk_buf_map[buf]
|
||||||
}
|
}
|
||||||
function gpu_finish(render3d_st: mut Render3dState) -> void { if render3d_st.gpu_kind == GPU_VK { gvk_flush(render3d_st) } }
|
function gpu_finish(render3d_st: mut Render3dState) -> void { gvk_flush(render3d_st) }
|
||||||
# n indexed draws of mesh m from VkDrawIndexedIndirectCommand records in buffer cmds at offset
|
# n indexed draws of mesh m from VkDrawIndexedIndirectCommand records in buffer cmds at offset
|
||||||
# (bytes); each record's firstInstance selects its instances out of the bound instance buffer.
|
# (bytes); each record's firstInstance selects its instances out of the bound instance buffer.
|
||||||
# With count_buf > 0 the GPU's own count (a uint at count_off) is used, up to n.
|
# With count_buf > 0 the GPU's own count (a uint at count_off) is used, up to n.
|
||||||
function gpu_draw_mesh_indirect(render3d_st: mut Render3dState, m: Mesh, cmds: int, offset: int, n: int, count_buf: int, count_off: int) -> void {
|
function gpu_draw_mesh_indirect(render3d_st: mut Render3dState, m: Mesh, cmds: int, offset: int, n: int, count_buf: int, count_off: int) -> void {
|
||||||
if m != null { ds_draw(render3d_st, DS_INDIRECT, n, m.count) }
|
if m != null { ds_draw(render3d_st, DS_INDIRECT, n, m.count) }
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_draw_indirect_now(render3d_st, m, cmds, offset, n, count_buf, count_off) }
|
gvk_draw_indirect_now(render3d_st, m, cmds, offset, n, count_buf, count_off)
|
||||||
}
|
}
|
||||||
|
|
||||||
# drawing
|
# drawing
|
||||||
function gpu_mesh_bind(render3d_st: Render3dState, m: Mesh) -> void { if render3d_st.gpu_kind == GPU_VK { return }; gl_bind_vertex_array(m.vao) }
|
function gpu_mesh_bind(render3d_st: Render3dState, m: Mesh) -> void { }
|
||||||
function gpu_mesh_unbind(render3d_st: Render3dState) -> void { if render3d_st.gpu_kind == GPU_VK { return }; gl_bind_vertex_array(0) }
|
function gpu_mesh_unbind(render3d_st: Render3dState) -> void { }
|
||||||
function gpu_draw_mesh(render3d_st: mut Render3dState, m: Mesh) -> void {
|
function gpu_draw_mesh(render3d_st: mut Render3dState, m: Mesh) -> void {
|
||||||
ds_draw(render3d_st, DS_MESH, 1, m.count)
|
ds_draw(render3d_st, DS_MESH, 1, m.count)
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_draw_now(render3d_st, m, 0, 0, 1); return }
|
|
||||||
gpu_glcheck_before(render3d_st, "a draw")
|
gvk_draw_now(render3d_st, m, 0, 0, 1)
|
||||||
gl_bind_vertex_array(m.vao)
|
|
||||||
if m.ebo != 0 { gl_draw_elements(m.mode, m.count, m.itype, null) }
|
|
||||||
else { gl_draw_arrays(m.mode, 0, m.count) }
|
|
||||||
gpu_glcheck_after(render3d_st, "a draw")
|
|
||||||
}
|
}
|
||||||
function gpu_draw_mesh_instanced(render3d_st: mut Render3dState, m: Mesh, n: int) -> void {
|
function gpu_draw_mesh_instanced(render3d_st: mut Render3dState, m: Mesh, n: int) -> void {
|
||||||
ds_draw(render3d_st, DS_INSTANCED, n, m.count * n)
|
ds_draw(render3d_st, DS_INSTANCED, n, m.count * n)
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_draw_now(render3d_st, m, 0, 0, n); return }
|
|
||||||
gpu_glcheck_before(render3d_st, "an instanced draw")
|
gvk_draw_now(render3d_st, m, 0, 0, n)
|
||||||
gl_bind_vertex_array(m.vao)
|
|
||||||
if m.ebo != 0 { gl_draw_elements_instanced(m.mode, m.count, m.itype, null, n) }
|
|
||||||
else { gl_draw_arrays_instanced(m.mode, 0, m.count, n) }
|
|
||||||
gpu_glcheck_after(render3d_st, "an instanced draw")
|
|
||||||
}
|
}
|
||||||
# the bound mesh's indices again (a patch mesh drawn once per terrain node)
|
# the bound mesh's indices again (a patch mesh drawn once per terrain node)
|
||||||
function gpu_draw_bound_elements(render3d_st: mut Render3dState, m: Mesh) -> void {
|
function gpu_draw_bound_elements(render3d_st: mut Render3dState, m: Mesh) -> void {
|
||||||
ds_draw(render3d_st, DS_PATCH, 1, m.count)
|
ds_draw(render3d_st, DS_PATCH, 1, m.count)
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_draw_now(render3d_st, m, 0, 0, 1); return }
|
|
||||||
gpu_glcheck_before(render3d_st, "a terrain patch")
|
gvk_draw_now(render3d_st, m, 0, 0, 1)
|
||||||
gl_draw_elements(m.mode, m.count, m.itype, null)
|
|
||||||
gpu_glcheck_after(render3d_st, "a terrain patch")
|
|
||||||
}
|
}
|
||||||
# vertices [first, first + count) of the bound mesh, as triangles (the overlay's ranges)
|
# vertices [first, first + count) of the bound mesh, as triangles (the overlay's ranges)
|
||||||
function gpu_draw_range(render3d_st: mut Render3dState, m: Mesh, first: int, count: int) -> void {
|
function gpu_draw_range(render3d_st: mut Render3dState, m: Mesh, first: int, count: int) -> void {
|
||||||
ds_draw(render3d_st, DS_RANGE, 1, count)
|
ds_draw(render3d_st, DS_RANGE, 1, count)
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_draw_now(render3d_st, m, first, count, 1); return }
|
|
||||||
gpu_glcheck_before(render3d_st, "an overlay draw")
|
gvk_draw_now(render3d_st, m, first, count, 1)
|
||||||
gl_draw_arrays(GL_TRIANGLES, first, count)
|
|
||||||
gpu_glcheck_after(render3d_st, "an overlay draw")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function gpu_mesh_free(render3d_st: mut Render3dState, m: Mesh) -> void {
|
function gpu_mesh_free(render3d_st: mut Render3dState, m: Mesh) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_mesh_free(render3d_st, m); return }
|
gvk_mesh_free(render3d_st, m)
|
||||||
if m == null { return }
|
|
||||||
let ids = gpu_tmp(render3d_st)
|
|
||||||
if m.vbufs != null {
|
|
||||||
for i in 0 .. m.n_vbufs { ids[0] = m.vbufs[i]; gl_delete_buffers(1, ids) }
|
|
||||||
m.n_vbufs = 0
|
|
||||||
} else if m.vbo != 0 { ids[0] = m.vbo; gl_delete_buffers(1, ids) }
|
|
||||||
m.vbo = 0
|
|
||||||
if m.ebo != 0 { ids[0] = m.ebo; gl_delete_buffers(1, ids); m.ebo = 0 }
|
|
||||||
if m.vao != 0 { ids[0] = m.vao; gl_delete_vertex_arrays(1, ids); m.vao = 0 }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---- textures -----------------------------------------------------------------------------
|
# ---- textures -----------------------------------------------------------------------------
|
||||||
|
|
@ -617,18 +522,16 @@ function gpu_tx_at(render3d_st: mut Render3dState, tex: int) -> int {
|
||||||
}
|
}
|
||||||
function gpu_bound(render3d_st: Render3dState, kind: int) -> int { if kind == GPU_TEX2D_ARRAY { return render3d_st.gpu_bound_array }; return render3d_st.gpu_bound_2d }
|
function gpu_bound(render3d_st: Render3dState, kind: int) -> int { if kind == GPU_TEX2D_ARRAY { return render3d_st.gpu_bound_array }; return render3d_st.gpu_bound_2d }
|
||||||
|
|
||||||
function gpu_tex_new(render3d_st: mut Render3dState) -> int { if render3d_st.gpu_kind == GPU_VK { return gvk_tex_new(render3d_st) }; return gl_texture() }
|
function gpu_tex_new(render3d_st: mut Render3dState) -> int { return gvk_tex_new(render3d_st) }
|
||||||
# does texture `tex` have an image behind it? Always on OpenGL; on Vulkan an image whose memory could
|
# does texture `tex` have an image behind it? Always on OpenGL; on Vulkan an image whose memory could
|
||||||
# not be had is never made, and a caller that can fall back (a smaller shadow map) asks here.
|
# not be had is never made, and a caller that can fall back (a smaller shadow map) asks here.
|
||||||
function gpu_tex_ok(render3d_st: Render3dState, tex: int) -> bool {
|
function gpu_tex_ok(render3d_st: Render3dState, tex: int) -> bool {
|
||||||
if render3d_st.gpu_kind != GPU_VK { return tex > 0 }
|
|
||||||
return tex > 0 and tex < len(render3d_st.gvk_tex_image) and render3d_st.gvk_tex_image[tex] != 0
|
return tex > 0 and tex < len(render3d_st.gvk_tex_image) and render3d_st.gvk_tex_image[tex] != 0
|
||||||
}
|
}
|
||||||
# what GL has on each unit's 2D target, for R3D_GLCHECK: deleting a texture unbinds it everywhere
|
# what GL has on each unit's 2D target, for R3D_GLCHECK: deleting a texture unbinds it everywhere
|
||||||
function gpu_tex_unit(render3d_st: mut Render3dState, unit: int) -> void { if render3d_st.gpu_kind == GPU_VK { render3d_st.gpu_unit_cur = unit; return }; gl_active_texture(GL_TEXTURE0 + unit); render3d_st.gpu_unit_cur = unit }
|
function gpu_tex_unit(render3d_st: mut Render3dState, unit: int) -> void { render3d_st.gpu_unit_cur = unit }
|
||||||
function gpu_tex_bind(render3d_st: mut Render3dState, kind: int, tex: int) -> void {
|
function gpu_tex_bind(render3d_st: mut Render3dState, kind: int, tex: int) -> void {
|
||||||
ds_tex_bind(render3d_st)
|
ds_tex_bind(render3d_st)
|
||||||
if render3d_st.gpu_kind != GPU_VK { gl_bind_texture(gpu_gl_target(kind), tex) }
|
|
||||||
if kind == GPU_TEX2D_ARRAY { render3d_st.gpu_bound_array = tex } else { render3d_st.gpu_bound_2d = tex }
|
if kind == GPU_TEX2D_ARRAY { render3d_st.gpu_bound_array = tex } else { render3d_st.gpu_bound_2d = tex }
|
||||||
if kind != GPU_TEX2D_ARRAY and render3d_st.gpu_unit_cur < 32 {
|
if kind != GPU_TEX2D_ARRAY and render3d_st.gpu_unit_cur < 32 {
|
||||||
if render3d_st.gpu_unit_2d == null { render3d_st.gpu_unit_2d = words(32); for i in 0 .. 32 { render3d_st.gpu_unit_2d[i] = -1 } }
|
if render3d_st.gpu_unit_2d == null { render3d_st.gpu_unit_2d = words(32); for i in 0 .. 32 { render3d_st.gpu_unit_2d[i] = -1 } }
|
||||||
|
|
@ -636,29 +539,24 @@ function gpu_tex_bind(render3d_st: mut Render3dState, kind: int, tex: int) -> vo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# pixel transfer packing (alignment, byte swap) for the uploads and read-backs that follow
|
# pixel transfer packing (alignment, byte swap) for the uploads and read-backs that follow
|
||||||
function gpu_pixel_store(render3d_st: mut Render3dState, pname: int, value: int) -> void { if render3d_st.gpu_kind == GPU_VK { if pname == GL_UNPACK_SWAP_BYTES { render3d_st.gvk_unpack_swap = value == 1 }; return }; gl_pixel_storei(pname, value); gpu_glcheck_after(render3d_st, "pixel store") }
|
function gpu_pixel_store(render3d_st: mut Render3dState, pname: int, value: int) -> void { if pname == GL_UNPACK_SWAP_BYTES { render3d_st.gvk_unpack_swap = value == 1 } }
|
||||||
function gpu_tex_image2d(render3d_st: mut Render3dState, ifmt: int, w: int, h: int, fmt: int, ty: int, data: pointer) -> void {
|
function gpu_tex_image2d(render3d_st: mut Render3dState, ifmt: int, w: int, h: int, fmt: int, ty: int, data: pointer) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK {
|
gvk_flush(render3d_st)
|
||||||
gvk_flush(render3d_st)
|
gvk_tex_storage(render3d_st, render3d_st.gpu_bound_2d, false, ifmt, w, h, 1, data != null)
|
||||||
gvk_tex_storage(render3d_st, render3d_st.gpu_bound_2d, false, ifmt, w, h, 1, data != null)
|
if data != null { gvk_tex_upload(render3d_st, render3d_st.gpu_bound_2d, ifmt, w, h, 1, fmt, ty, data) }
|
||||||
if data != null { gvk_tex_upload(render3d_st, render3d_st.gpu_bound_2d, ifmt, w, h, 1, fmt, ty, data) }
|
|
||||||
} else { gl_tex_image2d(GL_TEXTURE_2D, 0, ifmt, w, h, 0, fmt, ty, data) }
|
|
||||||
gpu_glcheck_after(render3d_st, `a {w}x{h} texture upload (format {ifmt})`)
|
|
||||||
let o = gpu_tx_at(render3d_st, render3d_st.gpu_bound_2d)
|
let o = gpu_tx_at(render3d_st, render3d_st.gpu_bound_2d)
|
||||||
if o >= 0 { render3d_st.gpu_tx[o] = GPU_TEX2D; render3d_st.gpu_tx[o + 1] = w; render3d_st.gpu_tx[o + 2] = h; render3d_st.gpu_tx[o + 3] = 1; render3d_st.gpu_tx[o + 4] = ifmt }
|
if o >= 0 { render3d_st.gpu_tx[o] = GPU_TEX2D; render3d_st.gpu_tx[o + 1] = w; render3d_st.gpu_tx[o + 2] = h; render3d_st.gpu_tx[o + 3] = 1; render3d_st.gpu_tx[o + 4] = ifmt }
|
||||||
}
|
}
|
||||||
function gpu_tex_image3d(render3d_st: mut Render3dState, ifmt: int, w: int, h: int, layers: int, fmt: int, ty: int, data: pointer) -> void {
|
function gpu_tex_image3d(render3d_st: mut Render3dState, ifmt: int, w: int, h: int, layers: int, fmt: int, ty: int, data: pointer) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK {
|
gvk_flush(render3d_st)
|
||||||
gvk_flush(render3d_st)
|
gvk_tex_storage(render3d_st, render3d_st.gpu_bound_array, true, ifmt, w, h, layers, data != null)
|
||||||
gvk_tex_storage(render3d_st, render3d_st.gpu_bound_array, true, ifmt, w, h, layers, data != null)
|
if data != null { gvk_tex_upload(render3d_st, render3d_st.gpu_bound_array, ifmt, w, h, layers, fmt, ty, data) }
|
||||||
if data != null { gvk_tex_upload(render3d_st, render3d_st.gpu_bound_array, ifmt, w, h, layers, fmt, ty, data) }
|
|
||||||
} else { gl_tex_image3d(GL_TEXTURE_2D_ARRAY, 0, ifmt, w, h, layers, 0, fmt, ty, data) }
|
|
||||||
gpu_glcheck_after(render3d_st, `a {w}x{h}x{layers} array upload (format {ifmt})`)
|
|
||||||
let o = gpu_tx_at(render3d_st, render3d_st.gpu_bound_array)
|
let o = gpu_tx_at(render3d_st, render3d_st.gpu_bound_array)
|
||||||
if o >= 0 { render3d_st.gpu_tx[o] = GPU_TEX2D_ARRAY; render3d_st.gpu_tx[o + 1] = w; render3d_st.gpu_tx[o + 2] = h; render3d_st.gpu_tx[o + 3] = layers; render3d_st.gpu_tx[o + 4] = ifmt }
|
if o >= 0 { render3d_st.gpu_tx[o] = GPU_TEX2D_ARRAY; render3d_st.gpu_tx[o + 1] = w; render3d_st.gpu_tx[o + 2] = h; render3d_st.gpu_tx[o + 3] = layers; render3d_st.gpu_tx[o + 4] = ifmt }
|
||||||
}
|
}
|
||||||
function gpu_tex_param(render3d_st: mut Render3dState, kind: int, pname: int, value: int) -> void {
|
function gpu_tex_param(render3d_st: mut Render3dState, kind: int, pname: int, value: int) -> void {
|
||||||
if render3d_st.gpu_kind != GPU_VK { gl_tex_parameteri(gpu_gl_target(kind), pname, value) }
|
|
||||||
let o = gpu_tx_at(render3d_st, gpu_bound(render3d_st, kind))
|
let o = gpu_tx_at(render3d_st, gpu_bound(render3d_st, kind))
|
||||||
if o < 0 { return }
|
if o < 0 { return }
|
||||||
if pname == GL_TEXTURE_MIN_FILTER { render3d_st.gpu_tx[o + 5] = value }
|
if pname == GL_TEXTURE_MIN_FILTER { render3d_st.gpu_tx[o + 5] = value }
|
||||||
|
|
@ -667,69 +565,39 @@ function gpu_tex_param(render3d_st: mut Render3dState, kind: int, pname: int, va
|
||||||
if pname == GL_TEXTURE_WRAP_T { render3d_st.gpu_tx[o + 8] = value }
|
if pname == GL_TEXTURE_WRAP_T { render3d_st.gpu_tx[o + 8] = value }
|
||||||
if pname == GL_TEXTURE_COMPARE_MODE { if value == GL_NONE { render3d_st.gpu_tx[o + 9] = 0 } }
|
if pname == GL_TEXTURE_COMPARE_MODE { if value == GL_NONE { render3d_st.gpu_tx[o + 9] = 0 } }
|
||||||
if pname == GL_TEXTURE_COMPARE_FUNC { render3d_st.gpu_tx[o + 9] = value }
|
if pname == GL_TEXTURE_COMPARE_FUNC { render3d_st.gpu_tx[o + 9] = value }
|
||||||
gpu_glcheck_after(render3d_st, "tex param")
|
|
||||||
}
|
}
|
||||||
# a float parameter (fixed, as Gl.* takes it): anisotropy is the one the renderer sets
|
# a float parameter (fixed, as Gl.* takes it): anisotropy is the one the renderer sets
|
||||||
function gpu_tex_paramf(render3d_st: mut Render3dState, kind: int, pname: int, value: fixed) -> void {
|
function gpu_tex_paramf(render3d_st: mut Render3dState, kind: int, pname: int, value: fixed) -> void {
|
||||||
if render3d_st.gpu_kind != GPU_VK { gl_tex_parameterf(gpu_gl_target(kind), pname, value) }
|
|
||||||
let o = gpu_tx_at(render3d_st, gpu_bound(render3d_st, kind))
|
let o = gpu_tx_at(render3d_st, gpu_bound(render3d_st, kind))
|
||||||
if o >= 0 and pname == 0x84FE { render3d_st.gpu_tx[o + 11] = float_bits(float(value)) }
|
if o >= 0 and pname == 0x84FE { render3d_st.gpu_tx[o + 11] = float_bits(float(value)) }
|
||||||
gpu_glcheck_after(render3d_st, "tex paramf")
|
|
||||||
}
|
}
|
||||||
# the border colour clamp-to-border reads (four fixed values in `rgba`)
|
# the border colour clamp-to-border reads (four fixed values in `rgba`)
|
||||||
function gpu_tex_border(render3d_st: Render3dState, kind: int, rgba: pointer) -> void { if render3d_st.gpu_kind == GPU_VK { return }; gl_tex_parameterfv(gpu_gl_target(kind), GL_TEXTURE_BORDER_COLOR, rgba) }
|
function gpu_tex_border(render3d_st: Render3dState, kind: int, rgba: pointer) -> void { }
|
||||||
function gpu_tex_mips(render3d_st: mut Render3dState, kind: int) -> void {
|
function gpu_tex_mips(render3d_st: mut Render3dState, kind: int) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK {
|
let mt = gpu_bound(render3d_st, kind)
|
||||||
let mt = gpu_bound(render3d_st, kind)
|
let mo = gpu_tx_at(render3d_st, mt)
|
||||||
let mo = gpu_tx_at(render3d_st, mt)
|
if mo >= 0 { gvk_mips_now(render3d_st, mt, render3d_st.gpu_tx[mo + 1], render3d_st.gpu_tx[mo + 2]) }
|
||||||
if mo >= 0 { gvk_mips_now(render3d_st, mt, render3d_st.gpu_tx[mo + 1], render3d_st.gpu_tx[mo + 2]) }
|
|
||||||
} else { gl_generate_mipmap(gpu_gl_target(kind)) }
|
|
||||||
gpu_glcheck_after(render3d_st, `mipmaps for texture {gpu_bound(render3d_st, kind)}`)
|
|
||||||
let o = gpu_tx_at(render3d_st, gpu_bound(render3d_st, kind))
|
let o = gpu_tx_at(render3d_st, gpu_bound(render3d_st, kind))
|
||||||
if o >= 0 { render3d_st.gpu_tx[o + 10] = 1 }
|
if o >= 0 { render3d_st.gpu_tx[o + 10] = 1 }
|
||||||
}
|
}
|
||||||
# level 0 of the bound texture into `out`
|
# level 0 of the bound texture into `out`
|
||||||
function gpu_tex_read(render3d_st: mut Render3dState, kind: int, fmt: int, ty: int, out: pointer) -> void {
|
function gpu_tex_read(render3d_st: mut Render3dState, kind: int, fmt: int, ty: int, out: pointer) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_flush(render3d_st); let rt = gpu_bound(render3d_st, kind); let ro = gpu_tx_at(render3d_st, rt); if ro >= 0 { gvk_tex_read(render3d_st, rt, render3d_st.gpu_tx[ro + 4], render3d_st.gpu_tx[ro + 1], render3d_st.gpu_tx[ro + 2], fmt, ty, out) }; return }
|
gvk_flush(render3d_st); let rt = gpu_bound(render3d_st, kind); let ro = gpu_tx_at(render3d_st, rt); if ro >= 0 { gvk_tex_read(render3d_st, rt, render3d_st.gpu_tx[ro + 4], render3d_st.gpu_tx[ro + 1], render3d_st.gpu_tx[ro + 2], fmt, ty, out) }
|
||||||
gl_get_tex_image(gpu_gl_target(kind), 0, fmt, ty, out)
|
|
||||||
gpu_glcheck_after(render3d_st, `a read-back of texture {gpu_bound(render3d_st, kind)}`)
|
|
||||||
}
|
}
|
||||||
function gpu_tex_free(render3d_st: mut Render3dState, tex: int) -> void {
|
function gpu_tex_free(render3d_st: mut Render3dState, tex: int) -> void {
|
||||||
if tex == 0 { return }
|
if tex == 0 { return }
|
||||||
let ids = gpu_tmp(render3d_st)
|
let ids = gpu_tmp(render3d_st)
|
||||||
ids[0] = tex
|
ids[0] = tex
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_flush(render3d_st); if tex < len(render3d_st.gvk_tex_image) { gvk_tex_release(render3d_st, tex) } } else { gl_delete_textures(1, ids) }
|
gvk_flush(render3d_st); if tex < len(render3d_st.gvk_tex_image) { gvk_tex_release(render3d_st, tex) }
|
||||||
if render3d_st.gpu_unit_2d != null { for i in 0 .. 32 { if render3d_st.gpu_unit_2d[i] == tex { render3d_st.gpu_unit_2d[i] = 0; if gpu_glcheck_on(render3d_st) { gpu_glcheck_say(render3d_st, `gpu: texture {tex} freed while bound on unit {i}`) } } } }
|
if render3d_st.gpu_unit_2d != null { for i in 0 .. 32 { if render3d_st.gpu_unit_2d[i] == tex { render3d_st.gpu_unit_2d[i] = 0 } } }
|
||||||
if render3d_st.gpu_bound_2d == tex { render3d_st.gpu_bound_2d = 0 }
|
if render3d_st.gpu_bound_2d == tex { render3d_st.gpu_bound_2d = 0 }
|
||||||
let o = gpu_tx_at(render3d_st, tex)
|
let o = gpu_tx_at(render3d_st, tex)
|
||||||
if o >= 0 { for i in 0 .. GPU_TX_W { render3d_st.gpu_tx[o + i] = 0 } }
|
if o >= 0 { for i in 0 .. GPU_TX_W { render3d_st.gpu_tx[o + i] = 0 } }
|
||||||
}
|
}
|
||||||
# a texture on a unit for a program's sampler, by the sampler's name
|
# a texture on a unit for a program's sampler, by the sampler's name
|
||||||
function gpu_bind_sampler(render3d_st: mut Render3dState, prog: int, name: string, unit: int, kind: int, tex: int) -> void {
|
function gpu_bind_sampler(render3d_st: mut Render3dState, prog: int, name: string, unit: int, kind: int, tex: int) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_bind_texture(render3d_st, prog, name, tex); return }
|
gvk_bind_texture(render3d_st, prog, name, tex)
|
||||||
gpu_tex_unit(render3d_st, unit)
|
|
||||||
gpu_tex_bind(render3d_st, kind, tex)
|
|
||||||
u_i(render3d_st, gpu_uniform(render3d_st, prog, name), unit)
|
|
||||||
if gpu_glcheck_on(render3d_st) {
|
|
||||||
# a sampler reading a texture nobody made (0 or freed) is the "unloadable" the driver warns of
|
|
||||||
let o = gpu_tx_at(render3d_st, tex)
|
|
||||||
if tex == 0 or o < 0 or render3d_st.gpu_tx[o] == 0 { gpu_glcheck_say(render3d_st, `gpu: {name} on unit {unit} of program {prog} samples texture {tex}, which has no image`) }
|
|
||||||
else {
|
|
||||||
# incomplete: a min filter that reads mipmaps (GL's default does, when none was set) on a
|
|
||||||
# texture that never had them generated - the driver samples zero ("unloadable")
|
|
||||||
let mn = render3d_st.gpu_tx[o + 5]
|
|
||||||
let wants_mips = mn == 0 or mn == 0x2700 or mn == 0x2701 or mn == 0x2702 or mn == 0x2703
|
|
||||||
if wants_mips and render3d_st.gpu_tx[o + 10] == 0 {
|
|
||||||
var why = "a mipmap filter"
|
|
||||||
if mn == 0 { why = "no min filter set (GL's default reads mipmaps)" }
|
|
||||||
gpu_glcheck_say(render3d_st, `gpu: {name} on unit {unit} of program {prog} samples texture {tex} ({render3d_st.gpu_tx[o + 1]}x{render3d_st.gpu_tx[o + 2]}, format {render3d_st.gpu_tx[o + 4]}) with {why} but no mipmaps`)
|
|
||||||
}
|
|
||||||
# compare mode on: only a shadow sampler may read it; a plain one reads zero ("unloadable")
|
|
||||||
if render3d_st.gpu_tx[o + 9] != 0 and not Text.contains(name, "shadow") { gpu_glcheck_say(render3d_st, `gpu: {name} on unit {unit} of program {prog} samples texture {tex} ({render3d_st.gpu_tx[o + 1]}x{render3d_st.gpu_tx[o + 2]}, format {render3d_st.gpu_tx[o + 4]}) with depth compare on`) }
|
|
||||||
|
|
||||||
}
|
|
||||||
gpu_glcheck_after(render3d_st, `binding {name} (texture {tex}) on unit {unit}`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---- render targets and passes -------------------------------------------------------------
|
# ---- render targets and passes -------------------------------------------------------------
|
||||||
|
|
@ -757,181 +625,88 @@ function gpu_fb_at(render3d_st: mut Render3dState, fb: int) -> int {
|
||||||
}
|
}
|
||||||
return fb * GPU_FB_W
|
return fb * GPU_FB_W
|
||||||
}
|
}
|
||||||
function gpu_glcheck_on(render3d_st: mut Render3dState) -> bool {
|
function gpu_check(render3d_st: Render3dState, tag: string) -> int { return 0 }
|
||||||
if render3d_st.gpu_glcheck < 0 { render3d_st.gpu_glcheck = 0; if r3d_env_has(render3d_st, "R3D_GLCHECK") { render3d_st.gpu_glcheck = 1 } }
|
|
||||||
return render3d_st.gpu_glcheck == 1
|
|
||||||
}
|
|
||||||
function gpu_check(render3d_st: Render3dState, tag: string) -> int { if render3d_st.gpu_kind == GPU_VK { return 0 }; return gl_check(tag) }
|
|
||||||
# a named checkpoint that costs nothing unless R3D_GLCHECK is set
|
# a named checkpoint that costs nothing unless R3D_GLCHECK is set
|
||||||
function gpu_debug_check(render3d_st: mut Render3dState, tag: string) -> void { if render3d_st.gpu_kind == GPU_VK { return }; if gpu_glcheck_on(render3d_st) { gl_check(tag) } }
|
function gpu_debug_check(render3d_st: mut Render3dState, tag: string) -> void { }
|
||||||
|
|
||||||
function gpu_fb_new(render3d_st: mut Render3dState) -> int { if render3d_st.gpu_kind == GPU_VK { render3d_st.gvk_fb_counter += 1; return render3d_st.gvk_fb_counter }; return gl_framebuffer() }
|
function gpu_fb_new(render3d_st: mut Render3dState) -> int { render3d_st.gvk_fb_counter += 1; return render3d_st.gvk_fb_counter }
|
||||||
function gpu_fb_bind(render3d_st: mut Render3dState, fb: int) -> void {
|
function gpu_fb_bind(render3d_st: mut Render3dState, fb: int) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_rebind(render3d_st, fb) } else { gl_bind_framebuffer(GL_FRAMEBUFFER, fb) }
|
gvk_rebind(render3d_st, fb)
|
||||||
render3d_st.gpu_fb_cur = fb
|
render3d_st.gpu_fb_cur = fb
|
||||||
gpu_glcheck_after(render3d_st, "fb bind")
|
|
||||||
}
|
}
|
||||||
function gpu_fb_bind_read(render3d_st: mut Render3dState, fb: int) -> void { if render3d_st.gpu_kind == GPU_VK { render3d_st.gvk_fb_read = fb; return }; gl_bind_framebuffer(GL_READ_FRAMEBUFFER, fb); gpu_glcheck_after(render3d_st, "fb bind read") }
|
function gpu_fb_bind_read(render3d_st: mut Render3dState, fb: int) -> void { render3d_st.gvk_fb_read = fb }
|
||||||
function gpu_fb_bind_draw(render3d_st: mut Render3dState, fb: int) -> void { if render3d_st.gpu_kind == GPU_VK { render3d_st.gvk_fb_draw = fb; return }; gl_bind_framebuffer(GL_DRAW_FRAMEBUFFER, fb); gpu_glcheck_after(render3d_st, "fb bind draw") }
|
function gpu_fb_bind_draw(render3d_st: mut Render3dState, fb: int) -> void { render3d_st.gvk_fb_draw = fb }
|
||||||
function gpu_fb_color(render3d_st: mut Render3dState, slot: int, tex: int) -> void {
|
function gpu_fb_color(render3d_st: mut Render3dState, slot: int, tex: int) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_pass_end(render3d_st) } else { gl_framebuffer_texture2d(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + slot, GL_TEXTURE_2D, tex, 0) }
|
gvk_pass_end(render3d_st)
|
||||||
let o = gpu_fb_at(render3d_st, render3d_st.gpu_fb_cur)
|
let o = gpu_fb_at(render3d_st, render3d_st.gpu_fb_cur)
|
||||||
if o >= 0 and slot < 2 { render3d_st.gpu_fb[o + slot] = tex; if slot == 0 { render3d_st.gpu_fb[o + 7] = 0 } }
|
if o >= 0 and slot < 2 { render3d_st.gpu_fb[o + slot] = tex; if slot == 0 { render3d_st.gpu_fb[o + 7] = 0 } }
|
||||||
gpu_glcheck_after(render3d_st, "attaching to {gpu_fb_describe(gpu_fb_cur)}")
|
|
||||||
}
|
}
|
||||||
function gpu_fb_color_layer(render3d_st: mut Render3dState, slot: int, tex: int, layer: int) -> void {
|
function gpu_fb_color_layer(render3d_st: mut Render3dState, slot: int, tex: int, layer: int) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_pass_end(render3d_st) } else { gl_framebuffer_texture_layer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + slot, tex, 0, layer) }
|
gvk_pass_end(render3d_st)
|
||||||
let o = gpu_fb_at(render3d_st, render3d_st.gpu_fb_cur)
|
let o = gpu_fb_at(render3d_st, render3d_st.gpu_fb_cur)
|
||||||
if o >= 0 and slot < 2 { render3d_st.gpu_fb[o + slot] = tex; if slot == 0 { render3d_st.gpu_fb[o + 7] = layer + 1 } }
|
if o >= 0 and slot < 2 { render3d_st.gpu_fb[o + slot] = tex; if slot == 0 { render3d_st.gpu_fb[o + 7] = layer + 1 } }
|
||||||
gpu_glcheck_after(render3d_st, "attaching to {gpu_fb_describe(gpu_fb_cur)}")
|
|
||||||
}
|
}
|
||||||
function gpu_fb_depth(render3d_st: mut Render3dState, tex: int) -> void {
|
function gpu_fb_depth(render3d_st: mut Render3dState, tex: int) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_pass_end(render3d_st) } else { gl_framebuffer_texture2d(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, tex, 0) }
|
gvk_pass_end(render3d_st)
|
||||||
let o = gpu_fb_at(render3d_st, render3d_st.gpu_fb_cur)
|
let o = gpu_fb_at(render3d_st, render3d_st.gpu_fb_cur)
|
||||||
if o >= 0 { render3d_st.gpu_fb[o + 2] = tex; render3d_st.gpu_fb[o + 3] = 0 }
|
if o >= 0 { render3d_st.gpu_fb[o + 2] = tex; render3d_st.gpu_fb[o + 3] = 0 }
|
||||||
gpu_glcheck_after(render3d_st, "attaching to {gpu_fb_describe(gpu_fb_cur)}")
|
|
||||||
}
|
}
|
||||||
function gpu_fb_depth_layer(render3d_st: mut Render3dState, tex: int, layer: int) -> void {
|
function gpu_fb_depth_layer(render3d_st: mut Render3dState, tex: int, layer: int) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_pass_end(render3d_st) } else { gl_framebuffer_texture_layer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, tex, 0, layer) }
|
gvk_pass_end(render3d_st)
|
||||||
let o = gpu_fb_at(render3d_st, render3d_st.gpu_fb_cur)
|
let o = gpu_fb_at(render3d_st, render3d_st.gpu_fb_cur)
|
||||||
if o >= 0 { render3d_st.gpu_fb[o + 2] = tex; render3d_st.gpu_fb[o + 3] = layer + 1 }
|
if o >= 0 { render3d_st.gpu_fb[o + 2] = tex; render3d_st.gpu_fb[o + 3] = layer + 1 }
|
||||||
gpu_glcheck_after(render3d_st, "attaching to {gpu_fb_describe(gpu_fb_cur)}")
|
|
||||||
}
|
}
|
||||||
function gpu_rb_new(render3d_st: mut Render3dState) -> int {
|
function gpu_rb_new(render3d_st: mut Render3dState) -> int {
|
||||||
if render3d_st.gpu_kind == GPU_VK { return gvk_tex_new(render3d_st) }
|
return gvk_tex_new(render3d_st)
|
||||||
let ids = gpu_tmp(render3d_st)
|
|
||||||
gl_gen_renderbuffers(1, ids)
|
|
||||||
return ids[0]
|
|
||||||
}
|
}
|
||||||
# storage for a renderbuffer: samples > 0 makes it multisampled
|
# storage for a renderbuffer: samples > 0 makes it multisampled
|
||||||
function gpu_rb_storage(render3d_st: mut Render3dState, rb: int, ifmt: int, w: int, h: int, samples: int) -> void {
|
function gpu_rb_storage(render3d_st: mut Render3dState, rb: int, ifmt: int, w: int, h: int, samples: int) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK {
|
gvk_flush(render3d_st); render3d_st.gpu_rb_samples = samples
|
||||||
gvk_flush(render3d_st); render3d_st.gpu_rb_samples = samples
|
render3d_st.gvk_storage_samples = samples
|
||||||
render3d_st.gvk_storage_samples = samples
|
gvk_tex_storage(render3d_st, rb, false, ifmt, w, h, 1, false)
|
||||||
gvk_tex_storage(render3d_st, rb, false, ifmt, w, h, 1, false)
|
render3d_st.gvk_storage_samples = 1
|
||||||
render3d_st.gvk_storage_samples = 1
|
|
||||||
return
|
|
||||||
}
|
|
||||||
gl_bind_renderbuffer(GL_RENDERBUFFER, rb)
|
|
||||||
if samples > 0 { gl_renderbuffer_storage_multisample(GL_RENDERBUFFER, samples, ifmt, w, h) }
|
|
||||||
else { gl_renderbuffer_storage(GL_RENDERBUFFER, ifmt, w, h) }
|
|
||||||
render3d_st.gpu_rb_samples = samples
|
|
||||||
gpu_glcheck_after(render3d_st, "rb storage")
|
|
||||||
}
|
}
|
||||||
function gpu_fb_color_rb(render3d_st: mut Render3dState, slot: int, rb: int) -> void {
|
function gpu_fb_color_rb(render3d_st: mut Render3dState, slot: int, rb: int) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_pass_end(render3d_st); let co = gpu_fb_at(render3d_st, render3d_st.gpu_fb_cur); if co >= 0 and slot < 2 { render3d_st.gpu_fb[co + slot] = rb }; return }
|
gvk_pass_end(render3d_st); let co = gpu_fb_at(render3d_st, render3d_st.gpu_fb_cur); if co >= 0 and slot < 2 { render3d_st.gpu_fb[co + slot] = rb }
|
||||||
gl_framebuffer_renderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + slot, GL_RENDERBUFFER, rb)
|
|
||||||
let o = gpu_fb_at(render3d_st, render3d_st.gpu_fb_cur)
|
|
||||||
if o >= 0 { render3d_st.gpu_fb[o + 4] = rb; render3d_st.gpu_fb[o + 6] = render3d_st.gpu_rb_samples }
|
|
||||||
}
|
}
|
||||||
function gpu_fb_depth_rb(render3d_st: mut Render3dState, rb: int) -> void {
|
function gpu_fb_depth_rb(render3d_st: mut Render3dState, rb: int) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_pass_end(render3d_st); let dop = gpu_fb_at(render3d_st, render3d_st.gpu_fb_cur); if dop >= 0 { render3d_st.gpu_fb[dop + 2] = rb; render3d_st.gpu_fb[dop + 3] = 0 }; return }
|
gvk_pass_end(render3d_st); let dop = gpu_fb_at(render3d_st, render3d_st.gpu_fb_cur); if dop >= 0 { render3d_st.gpu_fb[dop + 2] = rb; render3d_st.gpu_fb[dop + 3] = 0 }
|
||||||
gl_framebuffer_renderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rb)
|
|
||||||
let o = gpu_fb_at(render3d_st, render3d_st.gpu_fb_cur)
|
|
||||||
if o >= 0 { render3d_st.gpu_fb[o + 5] = rb; render3d_st.gpu_fb[o + 6] = render3d_st.gpu_rb_samples }
|
|
||||||
}
|
}
|
||||||
# colour slots 0 .. n-1 are drawn into (several: an MRT bake)
|
# colour slots 0 .. n-1 are drawn into (several: an MRT bake)
|
||||||
function gpu_fb_draw_buffers(render3d_st: mut Render3dState, n: int) -> void {
|
function gpu_fb_draw_buffers(render3d_st: mut Render3dState, n: int) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_fb_colors(render3d_st, render3d_st.gpu_fb_cur, n); return }
|
gvk_fb_colors(render3d_st, render3d_st.gpu_fb_cur, n)
|
||||||
if render3d_st.gpu_drawbufs == null { render3d_st.gpu_drawbufs = words(8) }
|
|
||||||
for i in 0 .. n { render3d_st.gpu_drawbufs[i] = GL_COLOR_ATTACHMENT0 + i }
|
|
||||||
gl_draw_buffers(n, render3d_st.gpu_drawbufs)
|
|
||||||
gpu_glcheck_after(render3d_st, "fb draw buffers")
|
|
||||||
}
|
}
|
||||||
# a depth-only target: no colour is drawn or read
|
# a depth-only target: no colour is drawn or read
|
||||||
function gpu_fb_no_color(render3d_st: mut Render3dState) -> void {
|
function gpu_fb_no_color(render3d_st: mut Render3dState) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_fb_colors(render3d_st, render3d_st.gpu_fb_cur, 0); return }
|
gvk_fb_colors(render3d_st, render3d_st.gpu_fb_cur, 0)
|
||||||
gl_draw_buffer(GL_NONE)
|
|
||||||
gl_read_buffer(GL_NONE)
|
|
||||||
gpu_glcheck_after(render3d_st, "fb no color")
|
|
||||||
}
|
}
|
||||||
function gpu_fb_status(render3d_st: Render3dState) -> int { if render3d_st.gpu_kind == GPU_VK { return GL_FRAMEBUFFER_COMPLETE }; return gl_check_framebuffer_status(GL_FRAMEBUFFER) }
|
function gpu_fb_status(render3d_st: Render3dState) -> int { return GL_FRAMEBUFFER_COMPLETE }
|
||||||
function gpu_fb_free(render3d_st: mut Render3dState, fb: int) -> void {
|
function gpu_fb_free(render3d_st: mut Render3dState, fb: int) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_fb_forget(render3d_st, fb); let fo = gpu_fb_at(render3d_st, fb); if fo >= 0 { for i in 0 .. GPU_FB_W { render3d_st.gpu_fb[fo + i] = 0 } }; return }
|
gvk_fb_forget(render3d_st, fb); let fo = gpu_fb_at(render3d_st, fb); if fo >= 0 { for i in 0 .. GPU_FB_W { render3d_st.gpu_fb[fo + i] = 0 } }
|
||||||
if fb == 0 { return }
|
|
||||||
let ids = gpu_tmp(render3d_st)
|
|
||||||
ids[0] = fb
|
|
||||||
gl_delete_framebuffers(1, ids)
|
|
||||||
let o = gpu_fb_at(render3d_st, fb)
|
|
||||||
if o >= 0 { for i in 0 .. GPU_FB_W { render3d_st.gpu_fb[o + i] = 0 } }
|
|
||||||
gpu_glcheck_after(render3d_st, "fb free")
|
|
||||||
}
|
}
|
||||||
function gpu_rb_free(render3d_st: mut Render3dState, rb: int) -> void {
|
function gpu_rb_free(render3d_st: mut Render3dState, rb: int) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_flush(render3d_st); if rb > 0 and rb < len(render3d_st.gvk_tex_image) { gvk_tex_release(render3d_st, rb) }; return }
|
gvk_flush(render3d_st); if rb > 0 and rb < len(render3d_st.gvk_tex_image) { gvk_tex_release(render3d_st, rb) }
|
||||||
if rb == 0 { return }
|
|
||||||
let ids = gpu_tmp(render3d_st)
|
|
||||||
ids[0] = rb
|
|
||||||
gl_delete_renderbuffers(1, ids)
|
|
||||||
gpu_glcheck_after(render3d_st, "rb free")
|
|
||||||
}
|
}
|
||||||
function gpu_viewport(render3d_st: mut Render3dState, x: int, y: int, w: int, h: int) -> void { if render3d_st.gpu_kind == GPU_VK { gvk_viewport(render3d_st, x, y, w, h); return }; gl_viewport(x, y, w, h); gpu_glcheck_after(render3d_st, "viewport") }
|
function gpu_viewport(render3d_st: mut Render3dState, x: int, y: int, w: int, h: int) -> void { gvk_viewport(render3d_st, x, y, w, h) }
|
||||||
|
|
||||||
# R3D_GLCHECK: before a draw or a clear, the bound framebuffer must be complete; after it, no
|
function gpu_clear_color(render3d_st: mut Render3dState, r: fixed, g: fixed, b: fixed, a: fixed) -> void { gvk_clear_color(render3d_st, float(r), float(g), float(b), float(a)) }
|
||||||
# error may be pending. Each report names the framebuffer and what was being done, once per
|
|
||||||
# distinct message, so the first bad pass is found without a flood. (Checking when a viewport
|
|
||||||
# is set reported the shadow pass, which sets its viewport before attaching a cascade.)
|
|
||||||
function gpu_glcheck_say(render3d_st: mut Render3dState, msg: string) -> void {
|
|
||||||
if render3d_st.gpu_glcheck_seen == null { render3d_st.gpu_glcheck_seen = new []string }
|
|
||||||
for i in 0 .. len(render3d_st.gpu_glcheck_seen) { if render3d_st.gpu_glcheck_seen[i] == msg { return } }
|
|
||||||
push(render3d_st.gpu_glcheck_seen, msg)
|
|
||||||
print(msg)
|
|
||||||
}
|
|
||||||
# a framebuffer as a person reads it: its handle and its colour (or depth) attachment's size and format
|
|
||||||
function gpu_fb_describe(render3d_st: mut Render3dState, fb: int) -> string {
|
|
||||||
if fb == 0 { return "the default framebuffer" }
|
|
||||||
let o = gpu_fb_at(render3d_st, fb)
|
|
||||||
if o < 0 { return `framebuffer {fb}` }
|
|
||||||
var tex = render3d_st.gpu_fb[o]
|
|
||||||
var what = "colour"
|
|
||||||
if tex == 0 { tex = render3d_st.gpu_fb[o + 2]; what = "depth" }
|
|
||||||
let t = gpu_tx_at(render3d_st, tex)
|
|
||||||
if tex == 0 or t < 0 { return `framebuffer {fb} (nothing recorded attached)` }
|
|
||||||
return `framebuffer {fb} ({what} texture {tex}, {render3d_st.gpu_tx[t + 1]}x{render3d_st.gpu_tx[t + 2]}, format {render3d_st.gpu_tx[t + 4]})`
|
|
||||||
}
|
|
||||||
function gpu_glcheck_before(render3d_st: mut Render3dState, what: string) -> void {
|
|
||||||
if render3d_st.gpu_kind == GPU_VK { return }
|
|
||||||
if not gpu_glcheck_on(render3d_st) { return }
|
|
||||||
let pending = gl_get_error()
|
|
||||||
if pending != 0 { gpu_glcheck_say(render3d_st, `gpu: error {pending} pending before {what} into {gpu_fb_describe(render3d_st, render3d_st.gpu_fb_cur)}`) }
|
|
||||||
if render3d_st.gpu_unit_2d != null and render3d_st.gpu_unit_2d[0] == 0 { gpu_glcheck_say(render3d_st, `gpu: {what} into {gpu_fb_describe(render3d_st, render3d_st.gpu_fb_cur)} with unit 0's 2D texture deleted`) }
|
|
||||||
let st = gl_check_framebuffer_status(GL_FRAMEBUFFER)
|
|
||||||
if st != GL_FRAMEBUFFER_COMPLETE { gpu_glcheck_say(render3d_st, `gpu: {gpu_fb_describe(render3d_st, render3d_st.gpu_fb_cur)} incomplete ({st}) at {what}`) }
|
|
||||||
}
|
|
||||||
function gpu_glcheck_after(render3d_st: mut Render3dState, what: string) -> void {
|
|
||||||
if render3d_st.gpu_kind == GPU_VK { return }
|
|
||||||
if not gpu_glcheck_on(render3d_st) { return }
|
|
||||||
let e = gl_get_error()
|
|
||||||
if e != 0 { gpu_glcheck_say(render3d_st, `gpu: error {e} from {what} into {gpu_fb_describe(render3d_st, render3d_st.gpu_fb_cur)}`) }
|
|
||||||
}
|
|
||||||
function gpu_clear_color(render3d_st: mut Render3dState, r: fixed, g: fixed, b: fixed, a: fixed) -> void { if render3d_st.gpu_kind == GPU_VK { gvk_clear_color(render3d_st, float(r), float(g), float(b), float(a)); return }; gl_clear_color(r, g, b, a) }
|
|
||||||
function gpu_clear(render3d_st: mut Render3dState, mask: int) -> void {
|
function gpu_clear(render3d_st: mut Render3dState, mask: int) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_clear(render3d_st, mask, render3d_st.gpu_fb, gpu_fb_at(render3d_st, render3d_st.gvk_fb_cur)); return }
|
gvk_clear(render3d_st, mask, render3d_st.gpu_fb, gpu_fb_at(render3d_st, render3d_st.gvk_fb_cur))
|
||||||
gpu_glcheck_before(render3d_st, "a clear")
|
|
||||||
gl_clear(mask)
|
|
||||||
gpu_glcheck_after(render3d_st, "a clear")
|
|
||||||
}
|
}
|
||||||
# the bound read framebuffer's [0, w) x [0, h) into the bound draw framebuffer's, unscaled
|
# the bound read framebuffer's [0, w) x [0, h) into the bound draw framebuffer's, unscaled
|
||||||
function gpu_blit(render3d_st: mut Render3dState, w: int, h: int, mask: int) -> void {
|
function gpu_blit(render3d_st: mut Render3dState, w: int, h: int, mask: int) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_blit(render3d_st, w, h, mask); return }
|
gvk_blit(render3d_st, w, h, mask)
|
||||||
gl_blit_framebuffer(0, 0, w, h, 0, 0, w, h, mask, GL_NEAREST)
|
|
||||||
gpu_glcheck_after(render3d_st, "a blit")
|
|
||||||
}
|
}
|
||||||
# the framebuffer the finished frame is presented from (an offscreen one, headless)
|
# the framebuffer the finished frame is presented from (an offscreen one, headless)
|
||||||
function gpu_screen_fb(render3d_st: Render3dState) -> int { if render3d_st.gpu_kind == GPU_VK { return 0 }; return gl_screen_fbo() }
|
function gpu_screen_fb(render3d_st: Render3dState) -> int { return 0 }
|
||||||
function gpu_multisample(render3d_st: mut Render3dState, on: bool) -> void { if render3d_st.gpu_kind == GPU_VK { return }; gpu_gl_cap(render3d_st, GL_MULTISAMPLE, gpu_b(on)); gpu_glcheck_after(render3d_st, "multisample") }
|
function gpu_multisample(render3d_st: mut Render3dState, on: bool) -> void { }
|
||||||
# the most samples the scene may be drawn with: the device's colour-and-depth limit on Vulkan (0 before
|
# the most samples the scene may be drawn with: the device's colour-and-depth limit on Vulkan (0 before
|
||||||
# it is open), 4 on OpenGL, which has always asked for up to that
|
# it is open), 4 on OpenGL, which has always asked for up to that
|
||||||
function gpu_msaa_max(render3d_st: Render3dState) -> int { if render3d_st.gpu_kind == GPU_VK { return render3d_st.gvk_msaa_max }; return 4 }
|
function gpu_msaa_max(render3d_st: Render3dState) -> int { return render3d_st.gvk_msaa_max }
|
||||||
function gpu_wireframe(render3d_st: mut Render3dState, on: bool) -> void {
|
function gpu_wireframe(render3d_st: mut Render3dState, on: bool) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { render3d_st.gvk_wireframe = gpu_b(on); return }
|
render3d_st.gvk_wireframe = gpu_b(on)
|
||||||
if on { gl_polygon_mode(GL_FRONT_AND_BACK, GL_LINE) } else { gl_polygon_mode(GL_FRONT_AND_BACK, GL_FILL) }
|
|
||||||
gpu_glcheck_after(render3d_st, "wireframe")
|
|
||||||
}
|
}
|
||||||
# the presented frame as RGB8, bottom row first (a photograph)
|
# the presented frame as RGB8, bottom row first (a photograph)
|
||||||
function gpu_read_screen(render3d_st: mut Render3dState, w: int, h: int, out: pointer) -> void {
|
function gpu_read_screen(render3d_st: mut Render3dState, w: int, h: int, out: pointer) -> void {
|
||||||
if render3d_st.gpu_kind == GPU_VK { gvk_read_screen(render3d_st, w, h, out); return }
|
gvk_read_screen(render3d_st, w, h, out)
|
||||||
gl_bind_framebuffer(GL_READ_FRAMEBUFFER, gl_screen_fbo())
|
|
||||||
gl_pixel_storei(GL_PACK_ALIGNMENT, 1)
|
|
||||||
gl_read_pixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, out)
|
|
||||||
gpu_glcheck_after(render3d_st, `a {w}x{h} read of the screen`)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ function ov_pick_prog(render3d_st: mut Render3dState) -> void {
|
||||||
if gpu_hdr_active(render3d_st) {
|
if gpu_hdr_active(render3d_st) {
|
||||||
if render3d_st.ov_prog_hdr == 0 {
|
if render3d_st.ov_prog_hdr == 0 {
|
||||||
r3d_program_log(render3d_st, "overlay.vert", "overlay.frag", "#define HDR10\n")
|
r3d_program_log(render3d_st, "overlay.vert", "overlay.frag", "#define HDR10\n")
|
||||||
render3d_st.ov_prog_hdr = gpu_program(render3d_st, "#version 410 core\n#define HDR10\n" + r3d_shader_file(render3d_st, "overlay.vert"), "#version 410 core\n#define HDR10\n" + r3d_shader_file(render3d_st, "overlay.frag"), "overlay.vert", "overlay.frag", "#define HDR10\n")
|
render3d_st.ov_prog_hdr = gpu_program(render3d_st, "overlay.vert", "overlay.frag", "#define HDR10\n")
|
||||||
}
|
}
|
||||||
if render3d_st.ov_prog_hdr != 0 { render3d_st.ov_prog = render3d_st.ov_prog_hdr; return }
|
if render3d_st.ov_prog_hdr != 0 { render3d_st.ov_prog = render3d_st.ov_prog_hdr; return }
|
||||||
}
|
}
|
||||||
|
|
@ -47,7 +47,7 @@ function ov_pick_prog(render3d_st: mut Render3dState) -> void {
|
||||||
|
|
||||||
function overlay_init(render3d_st: mut Render3dState, font_dir: string) -> bool {
|
function overlay_init(render3d_st: mut Render3dState, font_dir: string) -> bool {
|
||||||
r3d_program_log(render3d_st, "overlay.vert", "overlay.frag", "")
|
r3d_program_log(render3d_st, "overlay.vert", "overlay.frag", "")
|
||||||
render3d_st.ov_prog = gpu_program(render3d_st, "#version 410 core\n" + r3d_shader_file(render3d_st, "overlay.vert"), "#version 410 core\n" + r3d_shader_file(render3d_st, "overlay.frag"), "overlay.vert", "overlay.frag", "")
|
render3d_st.ov_prog = gpu_program(render3d_st, "overlay.vert", "overlay.frag", "")
|
||||||
if render3d_st.ov_prog == 0 { print("overlay: program failed"); return false }
|
if render3d_st.ov_prog == 0 { print("overlay: program failed"); return false }
|
||||||
render3d_st.ov_prog_sdr = render3d_st.ov_prog
|
render3d_st.ov_prog_sdr = render3d_st.ov_prog
|
||||||
render3d_st.ov_mesh = gpu_mesh_new(render3d_st)
|
render3d_st.ov_mesh = gpu_mesh_new(render3d_st)
|
||||||
|
|
|
||||||
|
|
@ -34,28 +34,6 @@ function r3d_find_root(render3d_st: mut Render3dState) -> void {
|
||||||
print(`r3d: cannot find the renderer's shaders (looked in {render3d_st.r3d_root}/shaders and {alt}/shaders)`)
|
print(`r3d: cannot find the renderer's shaders (looked in {render3d_st.r3d_root}/shaders and {alt}/shaders)`)
|
||||||
}
|
}
|
||||||
|
|
||||||
function r3d_shader_file(render3d_st: mut Render3dState, name: string) -> string {
|
|
||||||
r3d_find_root(render3d_st)
|
|
||||||
let path = `{render3d_st.r3d_root}/shaders/{name}`
|
|
||||||
let s = Fs.read_text(path)
|
|
||||||
if s == null { print(`r3d: missing shader {path}`); return "" }
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
function r3d_shader_src(render3d_st: mut Render3dState, name: string, defines: string, is_frag: bool) -> string {
|
|
||||||
if render3d_st.r3d_noise_src == null { render3d_st.r3d_noise_src = r3d_shader_file(render3d_st, "noise.glsl") }
|
|
||||||
if render3d_st.r3d_lighting_src == null { render3d_st.r3d_lighting_src = r3d_shader_file(render3d_st, "lighting.glsl") }
|
|
||||||
# the wind goes to BOTH stages: the grass and the crowns move in the vertex shader, the water
|
|
||||||
# ripples in the fragment one, and they have to be reading the same field or the meadow and
|
|
||||||
# the lake disagree about the weather
|
|
||||||
if render3d_st.r3d_wind_src == null { render3d_st.r3d_wind_src = r3d_shader_file(render3d_st, "wind.glsl") }
|
|
||||||
var s = "#version 410 core\n" + render3d_st.r3d_global_defs + defines + render3d_st.r3d_wind_src
|
|
||||||
if is_frag { s = s + render3d_st.r3d_noise_src + render3d_st.r3d_lighting_src }
|
|
||||||
# a blade's colour field is worked out once per vertex, not once per pixel (grass.vert)
|
|
||||||
else if Text.contains(defines, "#define GBLADE") { s = s + render3d_st.r3d_noise_src }
|
|
||||||
return s + r3d_shader_file(render3d_st, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
# Build a program from a vertex + fragment file pair (defines apply to both).
|
# Build a program from a vertex + fragment file pair (defines apply to both).
|
||||||
# R3D_PROGRAMS_LOG=<file>: append every program built (vertex, fragment, defines) - the list a
|
# R3D_PROGRAMS_LOG=<file>: append every program built (vertex, fragment, defines) - the list a
|
||||||
# backend that cannot compile shaders at run time (Vulkan: SPIR-V is built ahead) must cover
|
# backend that cannot compile shaders at run time (Vulkan: SPIR-V is built ahead) must cover
|
||||||
|
|
@ -72,7 +50,7 @@ function r3d_program_log(render3d_st: mut Render3dState, vs: string, fs: string,
|
||||||
}
|
}
|
||||||
function r3d_program(render3d_st: mut Render3dState, vs: string, fs: string, defines: string) -> int {
|
function r3d_program(render3d_st: mut Render3dState, vs: string, fs: string, defines: string) -> int {
|
||||||
r3d_program_log(render3d_st, vs, fs, defines)
|
r3d_program_log(render3d_st, vs, fs, defines)
|
||||||
let p = gpu_program(render3d_st, r3d_shader_src(render3d_st, vs, defines, false), r3d_shader_src(render3d_st, fs, defines, true), vs, fs, `{render3d_st.r3d_global_defs}{defines}`)
|
let p = gpu_program(render3d_st, vs, fs, `{render3d_st.r3d_global_defs}{defines}`)
|
||||||
if p == 0 { print(`r3d: program failed: {vs} + {fs}`) }
|
if p == 0 { print(`r3d: program failed: {vs} + {fs}`) }
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,8 +81,9 @@ function r3d_init(render3d_st: mut Render3dState, w: int, h: int, title: string)
|
||||||
# the window and the graphics backend; nothing is baked yet, but a frame can be presented
|
# the window and the graphics backend; nothing is baked yet, but a frame can be presented
|
||||||
function r3d_open(render3d_st: mut Render3dState, w: int, h: int, title: string) -> bool {
|
function r3d_open(render3d_st: mut Render3dState, w: int, h: int, title: string) -> bool {
|
||||||
r3d_env_flags(render3d_st)
|
r3d_env_flags(render3d_st)
|
||||||
gpu_select(render3d_st)
|
# Vulkan or nothing: a machine it cannot start on stops here, with the reason already printed
|
||||||
if not gpu_open(render3d_st, w, h, title) { print("r3d: no OpenGL context"); return false }
|
if gpu_select(render3d_st) != GPU_VK { return false }
|
||||||
|
if not gpu_open(render3d_st, w, h, title) { print("r3d: the Vulkan window or device could not be made"); return false }
|
||||||
if r3d_env_has(render3d_st, "R3D_NOVSYNC") { gpu_vsync(render3d_st, 0) }
|
if r3d_env_has(render3d_st, "R3D_NOVSYNC") { gpu_vsync(render3d_st, 0) }
|
||||||
var renderer: string = gpu_renderer_name(render3d_st)
|
var renderer: string = gpu_renderer_name(render3d_st)
|
||||||
print(`r3d: {gl_width()}x{gl_height()} on {renderer}`)
|
print(`r3d: {gl_width()}x{gl_height()} on {renderer}`)
|
||||||
|
|
@ -165,7 +166,6 @@ function r3d_resize(render3d_st: mut Render3dState) -> void {
|
||||||
print(`r3d: resized to {gl_width()}x{gl_height()}`)
|
print(`r3d: resized to {gl_width()}x{gl_height()}`)
|
||||||
}
|
}
|
||||||
function r3d_frame(render3d_st: mut Render3dState, time: float) -> void {
|
function r3d_frame(render3d_st: mut Render3dState, time: float) -> void {
|
||||||
gpu_glcheck_after(render3d_st, "the time between frames")
|
|
||||||
outline_frame(render3d_st)
|
outline_frame(render3d_st)
|
||||||
if not render3d_st.r3d_ready { return }
|
if not render3d_st.r3d_ready { return }
|
||||||
if gpu_resize_check(render3d_st) { r3d_resize(render3d_st) }
|
if gpu_resize_check(render3d_st) { r3d_resize(render3d_st) }
|
||||||
|
|
|
||||||
|
|
@ -1464,6 +1464,10 @@ function maybe_splice_runtime() -> void {
|
||||||
do_import("runtime/native/process.ludic")
|
do_import("runtime/native/process.ludic")
|
||||||
cur_dir = saved
|
cur_dir = saved
|
||||||
}
|
}
|
||||||
|
# Vk.* draws into the runtime's window, whose size, drawable and clock (gl_width,
|
||||||
|
# gl_set_drawable, gl_now_us, ...) still live in the GL runtime: splice and link it too,
|
||||||
|
# until the window layer stands apart from OpenGL (maroon-lake docs/plan/22, 22.15)
|
||||||
|
if g_uses_vk { g_uses_gl = true }
|
||||||
# Gl.*: splice the OpenGL surface (gl.ludic + the generated gl_api.ludic). The
|
# Gl.*: splice the OpenGL surface (gl.ludic + the generated gl_api.ludic). The
|
||||||
# native calls are the linked GL entry points themselves; the window attach is
|
# native calls are the linked GL entry points themselves; the window attach is
|
||||||
# is_windowed()-guarded, so a headless build renders into an offscreen context.
|
# is_windowed()-guarded, so a headless build renders into an offscreen context.
|
||||||
|
|
|
||||||
|
|
@ -455,7 +455,7 @@ function lab_plate_case() -> void {
|
||||||
# from the checkout's root, where the renderer's shaders and the plate are found beside it
|
# from the checkout's root, where the renderer's shaders and the plate are found beside it
|
||||||
let work = `{tmp_dir()}/labrun`
|
let work = `{tmp_dir()}/labrun`
|
||||||
shell(`rm -rf {work} build/lab/probe && mkdir -p {work}`)
|
shell(`rm -rf {work} build/lab/probe && mkdir -p {work}`)
|
||||||
if not shq(`yes '' | head -300 | /usr/bin/time -l {bin} > {work}/run.out 2> {work}/time.out`) { bad2(lbl, capture_line(`tail -1 {work}/run.out`)); return }
|
if not shq(`yes '' | head -300 | {vk_env()}/usr/bin/time -l {bin} > {work}/run.out 2> {work}/time.out`) { bad2(lbl, capture_line(`tail -1 {work}/run.out`)); return }
|
||||||
var n = 0
|
var n = 0
|
||||||
let names = ["front", "side", "above"]
|
let names = ["front", "side", "above"]
|
||||||
for i in 0 .. 3 {
|
for i in 0 .. 3 {
|
||||||
|
|
@ -608,12 +608,22 @@ function headless_case(path: pointer, exp: pointer, label: pointer) -> void {
|
||||||
let got = capture_line(`{out} < /dev/null`)
|
let got = capture_line(`{out} < /dev/null`)
|
||||||
if got == exp { ok(label) } else { bad2(label, `got [{got}] want [{exp}]`) }
|
if got == exp { ok(label) } else { bad2(label, `got [{got}] want [{exp}]`) }
|
||||||
}
|
}
|
||||||
|
# render3d draws with Vulkan only: on a Mac with no loader beside the binary, a run finds MoltenVK
|
||||||
|
# through the SDK - VULKAN_SDK as set, else the newest ~/VulkanSDK/<version>/macOS
|
||||||
|
function vk_env() -> string {
|
||||||
|
let set = Os.env("VULKAN_SDK")
|
||||||
|
if set != null and set != "" { return "" }
|
||||||
|
let found = capture_line("ls -d $HOME/VulkanSDK/*/macOS 2>/dev/null | tail -1")
|
||||||
|
if len(found) == 0 { return "" }
|
||||||
|
# the SDK's loader finds MoltenVK only through its driver manifest
|
||||||
|
return `VULKAN_SDK={found} VK_ICD_FILENAMES={found}/share/vulkan/icd.d/MoltenVK_icd.json `
|
||||||
|
}
|
||||||
# a program built headless whose verdict is one of its lines (the renderer logs before it)
|
# a program built headless whose verdict is one of its lines (the renderer logs before it)
|
||||||
function headless_line_case(path: pointer, exp: pointer, label: pointer) -> void {
|
function headless_line_case(path: pointer, exp: pointer, label: pointer) -> void {
|
||||||
let out = `{tmp_dir()}/h_{flat(path)}`
|
let out = `{tmp_dir()}/h_{flat(path)}`
|
||||||
if not shq(`bin/ludicc --headless examples/{path}.ludic -o {out} > {out}.log 2>&1`) { bad2(label, capture_line(`grep -i error {out}.log | head -1`)); return }
|
if not shq(`bin/ludicc --headless examples/{path}.ludic -o {out} > {out}.log 2>&1`) { bad2(label, capture_line(`grep -i error {out}.log | head -1`)); return }
|
||||||
let got = capture_line(`{out} < /dev/null 2>&1 | grep -c '^{exp}$'`)
|
let got = capture_line(`{vk_env()}{out} < /dev/null 2>&1 | grep -c '^{exp}$'`)
|
||||||
if got == "1" { ok(label) } else { bad2(label, capture_line(`{out} < /dev/null 2>&1 | grep -i fail | head -1`)) }
|
if got == "1" { ok(label) } else { bad2(label, capture_line(`{vk_env()}{out} < /dev/null 2>&1 | grep -i -E 'fail|cannot' | head -1`)) }
|
||||||
}
|
}
|
||||||
# ludic migrate state and components: the header names what every member needs, a field read in a
|
# ludic migrate state and components: the header names what every member needs, a field read in a
|
||||||
# member is not edited, a module named like a package keeps a state of its own, and the program
|
# member is not edited, a module named like a package keeps a state of its own, and the program
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue