render3d: start-up declared, ov_nine and the caster test on buffers made once - 362 frame sites to 122
The renderer starts itself from the first frame (gpu_select), so the device, its tables, the manifest and programs, grass, shadows, sky, terrain textures and the actor pool read as frame allocations: each is @alloc_ok as start-up, with gvk_fail (a failure) and the actor census and texture dump (debug switches). ov_nine's four corner/uv arrays are one floats(16) made in gvk_startup_state; ac_in_light's four points are made there too. Compiled (steady, and ludic deps over main 4316ff97). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
8dfcccad41
commit
9f233104a3
17 changed files with 37 additions and 8 deletions
|
|
@ -76,6 +76,7 @@ function ac_prog_new(render3d_st: mut Render3dState, vs: string, fs: string, def
|
|||
u_i(render3d_st, gpu_uniform(render3d_st, p, "u_diff"), 0); u_i(render3d_st, gpu_uniform(render3d_st, p, "u_nrm"), 1); u_i(render3d_st, gpu_uniform(render3d_st, p, "u_arm"), 2)
|
||||
return a
|
||||
}
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function actor_init(render3d_st: mut Render3dState) -> void {
|
||||
# the records play takes, made now: an actor first placed in play (a fish, a sign, a bed) made its
|
||||
# record, matrix and tint then (actor_release gives them back to this pool)
|
||||
|
|
@ -266,6 +267,7 @@ function outline_frame(render3d_st: mut Render3dState) -> void { if render3d_st.
|
|||
|
||||
# R3D_ACTOR_CENSUS=<frame>: at that frame, the lit pass's actors grouped by model - skinned or rigid,
|
||||
# how many share it, the primitives each draws - so instancing is aimed at what repeats
|
||||
@alloc_ok("a debug census and dumps, only under their switches")
|
||||
function actor_census(render3d_st: Render3dState) -> void {
|
||||
let keys = new []Model
|
||||
let cnt = new []int
|
||||
|
|
@ -364,7 +366,6 @@ function actor_draw_outline_one(render3d_st: mut Render3dState, a: Actor, ap: Ac
|
|||
# depth, only the draw. (Each actor used to draw into every cascade out to 300 m - in town that was
|
||||
# about 1500 skinned shadow draws a frame against 69 in the lit pass.)
|
||||
function ac_in_light(render3d_st: mut Render3dState, vp: floats, x: float, y: float, z: float, r: float) -> bool {
|
||||
if render3d_st.ac_lp0 == null { render3d_st.ac_lp0 = floats(3); render3d_st.ac_lpx = floats(3); render3d_st.ac_lpy = floats(3); render3d_st.ac_lpz = floats(3) }
|
||||
m4_xform_point(render3d_st.ac_lp0, vp, x, y, z)
|
||||
m4_xform_point(render3d_st.ac_lpx, vp, x + r, y, z)
|
||||
m4_xform_point(render3d_st.ac_lpy, vp, x, y + r, z)
|
||||
|
|
|
|||
|
|
@ -692,6 +692,7 @@ export state Render3dState {
|
|||
stream_debug_n: int = 0
|
||||
stream_scratch: floats = null
|
||||
gvk_pipe_bufs: words = null # a pipeline's vertex buffers, while it is made
|
||||
ov_nine_buf: floats = null # ov_nine's corners and uvs
|
||||
gvk_ac: pointer = null # the VkAllocationCallbacks every create and destroy is given (R3D_ALLOC_VK=1)
|
||||
gvk_prime_b: words = null # buffers made since the frame began, read once so MoltenVK makes their
|
||||
gvk_prime_h: []long = null # Metal buffers now (gvk_prime), and their handles
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ function gpu_name(kind: int) -> string { return "vulkan" }
|
|||
|
||||
# Start Vulkan. There is nothing to fall back to: a machine whose Vulkan cannot start is told why
|
||||
# (gpu_fallback_reason, printed) and r3d_init fails, so the program can say so and stop.
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function gpu_select(render3d_st: mut Render3dState) -> int {
|
||||
if render3d_st.gpu_kind != 0 { return render3d_st.gpu_kind }
|
||||
# a window needs a surface: Win32 on Windows, a CAMetalLayer through MoltenVK on macOS
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ property GpuVariant {
|
|||
}
|
||||
|
||||
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function gpu_variant_new(id: string, vs: string, fs: string, defs: string) -> GpuVariant {
|
||||
let v = new GpuVariant
|
||||
v.id = id; v.vs = vs; v.fs = fs; v.defs = defs
|
||||
|
|
@ -46,6 +47,7 @@ function gpu_variant_new(id: string, vs: string, fs: string, defs: string) -> Gp
|
|||
}
|
||||
|
||||
# Read a manifest; returns how many programs it holds (0: none, or no file).
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function gpu_manifest_load(render3d_st: mut Render3dState, path: string) -> int {
|
||||
render3d_st.gpu_variants = new []GpuVariant
|
||||
let text = Fs.read_text(path)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
# ---- the device ---------------------------------------------------------------------------
|
||||
|
||||
@alloc_ok("a failure, said once: the message is built only when something is already wrong")
|
||||
function gvk_fail(render3d_st: mut Render3dState, what: string, r: int) -> bool {
|
||||
render3d_st.gvk_why = `{what} failed (VkResult {r})`
|
||||
gvk_note(render3d_st, `r3d: vulkan: {render3d_st.gvk_why}`)
|
||||
|
|
@ -57,6 +58,7 @@ function gvk_no_command_pooling() -> void {
|
|||
Os.set_env("MVK_CONFIG_USE_COMMAND_POOLING", "0")
|
||||
}
|
||||
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function gvk_init(render3d_st: mut Render3dState) -> bool {
|
||||
if render3d_st.gvk_ready { return true }
|
||||
gvk_no_command_pooling()
|
||||
|
|
@ -514,6 +516,7 @@ function gvk_mem_id(x: long) -> int { return int(x) }
|
|||
# ---- one-shot commands --------------------------------------------------------------------
|
||||
# Uploads, bakes and read-backs record into a command buffer, submit it and wait. The frame
|
||||
# itself does not go through here.
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function gvk_cmd_init(render3d_st: mut Render3dState) -> bool {
|
||||
let cpi = bytes(VkCommandPoolCreateInfo_sizeof)
|
||||
Vk.zero(cpi, VkCommandPoolCreateInfo_sizeof)
|
||||
|
|
@ -730,6 +733,7 @@ function gvk_labels_close(render3d_st: mut Render3dState) -> void {
|
|||
# Everything a frame's draws use as scratch or as a table, made once the device is up: each was made
|
||||
# on the first draw that needed it, which put an allocation in whichever frame that was (plan 25.2)
|
||||
const GVK_PROGS_MAX: int = 4096
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function gvk_startup_state(render3d_st: mut Render3dState) -> void {
|
||||
let zero: long = 0
|
||||
render3d_st.gvk_tmp_buf = bytes(GVK_TMP_BYTES)
|
||||
|
|
@ -765,4 +769,6 @@ function gvk_startup_state(render3d_st: mut Render3dState) -> void {
|
|||
for i in 0 .. 32 { render3d_st.gpu_unit_2d[i] = -1 }
|
||||
}
|
||||
if render3d_st.cam_planes == null { render3d_st.cam_planes = floats(16) }
|
||||
if render3d_st.ov_nine_buf == null { render3d_st.ov_nine_buf = floats(16) }
|
||||
if render3d_st.ac_lp0 == null { render3d_st.ac_lp0 = floats(3); render3d_st.ac_lpx = floats(3); render3d_st.ac_lpy = floats(3); render3d_st.ac_lpz = floats(3) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -267,6 +267,7 @@ function gvk_dispatch_tex(render3d_st: mut Render3dState, c: int, params: pointe
|
|||
}
|
||||
|
||||
# R3D_VK_PROBE=1: one dispatch over a GPU-owned buffer, read back - the compute path end to end
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function gvk_compute_probe(render3d_st: mut Render3dState) -> void {
|
||||
let c = gvk_compute_new(render3d_st, "probe", 1)
|
||||
if c == 0 { print("r3d: vulkan compute probe: FAILED (no program)"); return }
|
||||
|
|
@ -681,6 +682,7 @@ function gvk_bind_texture(render3d_st: mut Render3dState, p: int, name: string,
|
|||
# gvk_frame_reset.
|
||||
const GVK_RING_BYTES: int = 64 * 1024 * 1024
|
||||
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function gvk_frame_init(render3d_st: mut Render3dState) -> bool {
|
||||
let props = bytes(VkPhysicalDeviceProperties_sizeof)
|
||||
Vk.get_physical_device_properties(render3d_st.gvk_pd, props)
|
||||
|
|
@ -754,6 +756,7 @@ function gvk_ring_put(render3d_st: mut Render3dState, blk: pointer, n: int) -> i
|
|||
# sampler, so a set is never matched against a texture it no longer describes.
|
||||
const GVK_KPOOL_SETS: int = 8192
|
||||
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function gvk_kpool_make(render3d_st: mut Render3dState) -> bool {
|
||||
let sizes = bytes(VkDescriptorPoolSize_sizeof * 2)
|
||||
Vk.put_i32(sizes, VkDescriptorPoolSize_type, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC)
|
||||
|
|
@ -775,6 +778,7 @@ function gvk_kpool_make(render3d_st: mut Render3dState) -> bool {
|
|||
return true
|
||||
}
|
||||
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function gvk_sc_clear(render3d_st: mut Render3dState) -> void {
|
||||
# emptied, not replaced: five fresh lists each time the pool filled were never given back
|
||||
if render3d_st.gvk_sc_prog == null {
|
||||
|
|
@ -1413,6 +1417,7 @@ function gvk_screenshot(render3d_st: mut Render3dState, path: string) -> bool {
|
|||
# ---- what gpu.ludic's Vulkan branches call -----------------------------------------------------
|
||||
|
||||
# the manifest the programs are looked up in, from the renderer's own shader directory
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function gvk_manifest(render3d_st: mut Render3dState) -> bool {
|
||||
r3d_find_root(render3d_st)
|
||||
render3d_st.gvk_spv_dir = `{render3d_st.r3d_root}/shaders/spv`
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ function grass_blade_mesh(render3d_st: mut Render3dState, rows: int) -> Mesh {
|
|||
return m
|
||||
}
|
||||
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function grass_init(render3d_st: mut Render3dState) -> void {
|
||||
render3d_st.grass_merge = gpu_has_mdi(render3d_st) and not r3d_env_has(render3d_st, "R3D_GRASS_TILES")
|
||||
var defs = "#define FOLIAGE\n#define BLADE\n#define GBLADE\n"
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ function gg_base(b: int) -> int {
|
|||
}
|
||||
|
||||
# once, after grass_init: the programs, the buffers and the three instanced blades
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function gg_init(render3d_st: mut Render3dState) -> void {
|
||||
if not gpu_has_compute(render3d_st) or render3d_st.grass_prog == 0 { return }
|
||||
if r3d_env_has(render3d_st, "R3D_GRASS_GPU") and r3d_env(render3d_st, "R3D_GRASS_GPU") == "0" { return }
|
||||
|
|
|
|||
|
|
@ -363,15 +363,14 @@ function ov_sub(render3d_st: mut Render3dState, tex: int, x: int, y: int, w: int
|
|||
function ov_nine(render3d_st: mut Render3dState, tex: int, tw: int, src: int, x: int, y: int, w: int, h: int, dst: int, r: float, g: float, b: float, a: float) -> void {
|
||||
ov_use_tex(render3d_st, tex)
|
||||
let s = float(src) / float(tw)
|
||||
let xs = floats(4); let ys = floats(4); let us = floats(4); let vs = floats(4)
|
||||
xs[0] = float(x); xs[1] = float(x + dst); xs[2] = float(x + w - dst); xs[3] = float(x + w)
|
||||
ys[0] = float(y); ys[1] = float(y + dst); ys[2] = float(y + h - dst); ys[3] = float(y + h)
|
||||
us[0] = 0.0; us[1] = s; us[2] = 1.0 - s; us[3] = 1.0
|
||||
vs[0] = 0.0; vs[1] = s; vs[2] = 1.0 - s; vs[3] = 1.0
|
||||
let nb = render3d_st.ov_nine_buf # made once (gvk_startup_state): xs, ys, us, vs
|
||||
nb[0 + 0] = float(x); nb[0 + 1] = float(x + dst); nb[0 + 2] = float(x + w - dst); nb[0 + 3] = float(x + w)
|
||||
nb[4 + 0] = float(y); nb[4 + 1] = float(y + dst); nb[4 + 2] = float(y + h - dst); nb[4 + 3] = float(y + h)
|
||||
nb[8 + 0] = 0.0; nb[8 + 1] = s; nb[8 + 2] = 1.0 - s; nb[8 + 3] = 1.0
|
||||
nb[12 + 0] = 0.0; nb[12 + 1] = s; nb[12 + 2] = 1.0 - s; nb[12 + 3] = 1.0
|
||||
for j in 0 .. 3 {
|
||||
for i in 0 .. 3 { ov_quad(render3d_st, xs[i], ys[j], xs[i + 1], ys[j + 1], us[i], vs[j], us[i + 1], vs[j + 1], r, g, b, a) }
|
||||
for i in 0 .. 3 { ov_quad(render3d_st, nb[0 + i], nb[4 + j], nb[0 + i + 1], nb[4 + j + 1], nb[8 + i], nb[12 + j], nb[8 + i + 1], nb[12 + j + 1], r, g, b, a) }
|
||||
}
|
||||
free(xs); free(ys); free(us); free(vs)
|
||||
}
|
||||
# an arbitrary quad (float-bit pixel corners, clockwise from top-left) of a texture
|
||||
function ov_quad4(render3d_st: mut Render3dState, x0: float, y0: float, x1: float, y1: float, x2: float, y2: float, x3: float, y3: float, u0: float, v0: float, u1: float, v1: float, r: float, g: float, b: float, a: float) -> void {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ const PROF_SLOTS: int = 16
|
|||
const PROF_RING: int = 4 # frames of latency before a result is read
|
||||
|
||||
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function prof_init(render3d_st: mut Render3dState) -> void {
|
||||
render3d_st.prof_on = r3d_env_has(render3d_st, "R3D_PROF")
|
||||
if not render3d_st.prof_on { return }
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ function r3d_home() -> string {
|
|||
|
||||
# Settle r3d_root on first use: the package as checked out beside the project, else the
|
||||
# copy that ships with the toolchain.
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function r3d_find_root(render3d_st: mut Render3dState) -> void {
|
||||
if render3d_st.r3d_root_found { return }
|
||||
render3d_st.r3d_root_found = true
|
||||
|
|
|
|||
|
|
@ -505,6 +505,7 @@ function impostor_bake(render3d_st: mut Render3dState, model: Model, tiles: int,
|
|||
|
||||
# Give a layer a LOD chain. `dists` (float bits) are the outer distances of each level;
|
||||
# the last one becomes the layer's `near` so the impostor (if any) starts there.
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function layer_set_lods(render3d_st: mut Render3dState, l: Layer, models: []Model, dists: floats) -> void {
|
||||
l.lods = models
|
||||
l.n_lods = len(models)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
const SHADOW_CASCADES: int = 5
|
||||
|
||||
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function shadow_init(render3d_st: mut Render3dState) -> void {
|
||||
shadow_make_tex(render3d_st)
|
||||
render3d_st.sh_fbo = gpu_fb_new(render3d_st)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ function sky_dir_from_uv(o: floats, u: float, v: float) -> void {
|
|||
v3_set(o, st * Math.sin(phi), Math.cos(theta), -(st * Math.cos(phi)))
|
||||
}
|
||||
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function sky_load(render3d_st: mut Render3dState, path: string) -> bool {
|
||||
render3d_st.sky_tex = tex_load_hdr(render3d_st, path)
|
||||
if render3d_st.sky_tex == 0 { return false }
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ function gsl_boot(render3d_st: mut Render3dState) -> void {
|
|||
}
|
||||
|
||||
# After Vk.open(), before vkCreateInstance.
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function gsl_init(render3d_st: mut Render3dState) -> void {
|
||||
if render3d_st.gsl_on or Vk.sl_active() == 0 { return }
|
||||
render3d_st.gsl_feats = gsl_struct(20)
|
||||
|
|
|
|||
|
|
@ -325,6 +325,7 @@ function terrain_height_smooth(render3d_st: mut Render3dState, x: float, z: floa
|
|||
return h
|
||||
}
|
||||
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function terrain_load_textures(render3d_st: mut Render3dState) -> void {
|
||||
render3d_st.ter_tex = words(15)
|
||||
let a = render3d_st.r3d_assets + "/textures/"
|
||||
|
|
@ -361,6 +362,7 @@ function terrain_init_step(render3d_st: mut Render3dState, i: int) -> void {
|
|||
terrain_init_finish(render3d_st)
|
||||
}
|
||||
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function terrain_init_finish(render3d_st: mut Render3dState) -> void {
|
||||
cdlod_init(render3d_st)
|
||||
render3d_st.ter_wire = r3d_env_has(render3d_st, "R3D_WIRE")
|
||||
|
|
@ -591,6 +593,7 @@ function terrain_draw(render3d_st: mut Render3dState) -> void {
|
|||
# One 32x32 patch mesh (a_xz in 0..1) drawn once per selected quadtree node; the vertex
|
||||
# shader places, scales and morphs it. Levels are drawn out to cd_range[k] = 48 * 2^k m,
|
||||
# so cells are 1 m within 48 m, 2 m to 96 m, 4 m to 192 m ... 256 m at the root.
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function cdlod_init(render3d_st: mut Render3dState) -> void {
|
||||
let m = gpu_mesh_new(render3d_st)
|
||||
let n = CD_G + 1
|
||||
|
|
@ -624,6 +627,7 @@ function cdlod_init(render3d_st: mut Render3dState) -> void {
|
|||
}
|
||||
|
||||
# min/max height per patch at every level, from the CPU copy of the height field
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function cdlod_bounds(render3d_st: mut Render3dState) -> void {
|
||||
render3d_st.cd_min = new []floats; render3d_st.cd_max = new []floats
|
||||
let t = TERRAIN_RES / CD_LEAVES # texels per leaf patch side
|
||||
|
|
|
|||
|
|
@ -321,6 +321,7 @@ function tex_solid(render3d_st: mut Render3dState, r: int, g: int, b: int, a: in
|
|||
|
||||
# ---- Radiance .hdr (RGBE, new-style RLE) -> RGB float bits -------------------------
|
||||
|
||||
@alloc_ok("start-up: the device, its tables, the programs, the passes and the world's first textures are made once, before play")
|
||||
function hdr_decode(render3d_st: mut Render3dState, path: pointer) -> floats {
|
||||
let d = r3d_read_file(render3d_st, path)
|
||||
if d == null { print(`hdr: cannot read {path}`); return null }
|
||||
|
|
@ -437,6 +438,7 @@ function tex_target(render3d_st: mut Render3dState, w: int, h: int, ifmt: int, f
|
|||
}
|
||||
|
||||
# Debug: the brightest texel of an RGBA float texture and where it is.
|
||||
@alloc_ok("a debug census and dumps, only under their switches")
|
||||
function tex_max(render3d_st: mut Render3dState, tex: int, w: int, h: int, tag: pointer) -> void {
|
||||
let buf = floats(w * h * 4)
|
||||
gpu_tex_bind(render3d_st, GPU_TEX2D, tex)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue