diff --git a/packages/ludic.render3d/actor.ludic b/packages/ludic.render3d/actor.ludic index c6fae5b3..18046019 100644 --- a/packages/ludic.render3d/actor.ludic +++ b/packages/ludic.render3d/actor.ludic @@ -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=: 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) diff --git a/packages/ludic.render3d/env.ludic b/packages/ludic.render3d/env.ludic index 1c06c86b..3d26e72f 100644 --- a/packages/ludic.render3d/env.ludic +++ b/packages/ludic.render3d/env.ludic @@ -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 diff --git a/packages/ludic.render3d/gpu.ludic b/packages/ludic.render3d/gpu.ludic index 6930e378..4c9d9826 100644 --- a/packages/ludic.render3d/gpu.ludic +++ b/packages/ludic.render3d/gpu.ludic @@ -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 diff --git a/packages/ludic.render3d/gpu_manifest.ludic b/packages/ludic.render3d/gpu_manifest.ludic index 498ae78d..034f410c 100644 --- a/packages/ludic.render3d/gpu_manifest.ludic +++ b/packages/ludic.render3d/gpu_manifest.ludic @@ -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) diff --git a/packages/ludic.render3d/gpu_vk.ludic b/packages/ludic.render3d/gpu_vk.ludic index 7070d36c..e71497ef 100644 --- a/packages/ludic.render3d/gpu_vk.ludic +++ b/packages/ludic.render3d/gpu_vk.ludic @@ -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) } } diff --git a/packages/ludic.render3d/gpu_vk_draw.ludic b/packages/ludic.render3d/gpu_vk_draw.ludic index 883da82b..8bc1f850 100644 --- a/packages/ludic.render3d/gpu_vk_draw.ludic +++ b/packages/ludic.render3d/gpu_vk_draw.ludic @@ -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` diff --git a/packages/ludic.render3d/grass.ludic b/packages/ludic.render3d/grass.ludic index 27641155..25611827 100644 --- a/packages/ludic.render3d/grass.ludic +++ b/packages/ludic.render3d/grass.ludic @@ -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" diff --git a/packages/ludic.render3d/grass_gpu.ludic b/packages/ludic.render3d/grass_gpu.ludic index aef4afdd..6a693b14 100644 --- a/packages/ludic.render3d/grass_gpu.ludic +++ b/packages/ludic.render3d/grass_gpu.ludic @@ -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 } diff --git a/packages/ludic.render3d/overlay.ludic b/packages/ludic.render3d/overlay.ludic index 1841f531..cbb44b12 100644 --- a/packages/ludic.render3d/overlay.ludic +++ b/packages/ludic.render3d/overlay.ludic @@ -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 { diff --git a/packages/ludic.render3d/prof.ludic b/packages/ludic.render3d/prof.ludic index d151bfcf..fc8015e1 100644 --- a/packages/ludic.render3d/prof.ludic +++ b/packages/ludic.render3d/prof.ludic @@ -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 } diff --git a/packages/ludic.render3d/programs.ludic b/packages/ludic.render3d/programs.ludic index be4bdda3..6b0b1ac6 100644 --- a/packages/ludic.render3d/programs.ludic +++ b/packages/ludic.render3d/programs.ludic @@ -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 diff --git a/packages/ludic.render3d/scatter.ludic b/packages/ludic.render3d/scatter.ludic index 2a63b5c0..667b7254 100644 --- a/packages/ludic.render3d/scatter.ludic +++ b/packages/ludic.render3d/scatter.ludic @@ -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) diff --git a/packages/ludic.render3d/shadow.ludic b/packages/ludic.render3d/shadow.ludic index 96ed642d..be7eabd8 100644 --- a/packages/ludic.render3d/shadow.ludic +++ b/packages/ludic.render3d/shadow.ludic @@ -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) diff --git a/packages/ludic.render3d/sky.ludic b/packages/ludic.render3d/sky.ludic index fc9c5296..acc40811 100644 --- a/packages/ludic.render3d/sky.ludic +++ b/packages/ludic.render3d/sky.ludic @@ -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 } diff --git a/packages/ludic.render3d/streamline.ludic b/packages/ludic.render3d/streamline.ludic index 72cd77cc..6fea92b6 100644 --- a/packages/ludic.render3d/streamline.ludic +++ b/packages/ludic.render3d/streamline.ludic @@ -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) diff --git a/packages/ludic.render3d/terrain.ludic b/packages/ludic.render3d/terrain.ludic index 863ac4c8..10ec7964 100644 --- a/packages/ludic.render3d/terrain.ludic +++ b/packages/ludic.render3d/terrain.ludic @@ -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 diff --git a/packages/ludic.render3d/texture.ludic b/packages/ludic.render3d/texture.ludic index ade9b9a3..8d21cfb3 100644 --- a/packages/ludic.render3d/texture.ludic +++ b/packages/ludic.render3d/texture.ludic @@ -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)