ludic/packages/ludic.render3d/env.ludic
2026-09-30 17:18:48 +03:00

905 lines
38 KiB
Text

# env.ludic — the one gate every R3D_* switch goes through.
# The debug views, feature kills, file writers and hardware fakes are developer
# tools: a headless build (tests, benchmarks, frame dumps) always honours them, a
# windowed build - which is what a player runs - only when R3D_DEV is set to
# anything but "0". Read every R3D_* through r3d_env_has / r3d_env, never
# Os.has_env / Os.env.
export state Render3dState {
ac_lit: AcProg = null
ac_lit_cut: AcProg = null
ac_sh: AcProg = null
ac_sh_cut: AcProg = null
ac_out: AcProg = null
ac_out_cut: AcProg = null
ac_actors: []Actor = null
ac_next_id: int = 0
ac_frame: int = 0
ac_oq: []OutlineReq = null
ac_oq_n: int = 0 # live entries; the array is kept and reused
ac_oq_closed: bool = true
ac_census_at: int = -2
ac_lp0: floats = null
ac_lpx: floats = null
ac_lpy: floats = null
ac_lpz: floats = null
ac_cc_actors: int = 0
ac_cc_draws: int = 0
ac_cc_skinned: int = 0
ac_cc_cut: int = 0
cam_pos: floats = null # x, y, z
cam_yaw: float = 0.0 # radians, 0 = looking down -z
cam_pitch: float = 0.0
cam_fov: float = 0.0 # vertical, radians
cam_near: float = 0.0
cam_far: float = 0.0
cam_aspect: float = 0.0
cam_view: floats = null
cam_proj: floats = null
cam_vp: floats = null
cam_inv_vp: floats = null
cam_inv_proj: floats = null
cam_vp_clean: floats = null # view-projection, kept for depth reconstruction
cam_inv_vp_clean: floats = null
cam_fwd: floats = null
cam_right: floats = null
cam_planes: floats = null
day_on: bool = false
day_hours: float = 0.0 # float bits, 0 .. 24
day_light: float = 0.0 # 0 night .. 1 full day (float bits)
day_ibl: floats = null # rgb scale on the sky's light
day_sun_base: floats = null # the HDRI's sun radiance, kept
day_az0: float = 0.0 # the sun's azimuth at the reference hour (radians, yaw convention)
day_yaw0: float = 0.0 # the sky yaw the scene was tuned at
day_hour0: float = 0.0 # the hour the photograph was taken (10.5)
day_dir: float = 0.0 # +1 / -1: which way the sun travels in yaw
day_az: float = 0.0
day_el: float = 0.0
day_gen: int = 0 # bumps when the light moved enough to rebake the terrain shadow
day_baked_az: float = 0.0
day_baked_el: float = 0.0
day_sky_baked: float = 0.0 # the sky yaw the convolutions were baked at
fire_pos: floats = null
fire_color: floats = null
day_moon: bool = false
day_moon_phase: float = 0.5 # 0.5: full, which is where the game used to be
day_moon_illum: float = 1.0 # the lit fraction, derived from the phase
day_moon_dir: floats = null # toward the moon, whether or not it is up
hand_pos: floats = null
hand_color: floats = null
hand_dir: floats = null
hand_cone: float = 0.0
hand_reach: float = 0.0 # metres the hand light reaches (float bits)
day_overcast: float = 0.0 # 0 clear .. 1 a low grey sky (float bits): dims the sun and the sky's light
day_flash: float = 0.0 # a lightning flash this frame (0..1): the sun brightens for it
day_fog_mul: float = 1.0 # multiplies the base fog density (rain and snow thicken the air)
day_fog_base: float = 0.0
ds_on: int = -1 # -1: not read yet
ds_counting: bool = false
ds_from: int = 90
ds_frames: int = 60
ds_frame_no: int = 0
ds_outside: pointer = "(outside passes)"
ds_pass_names: []pointer = null
ds_pass_prog: []long = null # program changes inside the pass
ds_pass_tex: []long = null # texture binds inside the pass
ds_pass_i: int = 0
ds_row_pass: []int = null
ds_row_prog: []int = null
ds_row_kind: []int = null
ds_row_draws: []long = null
ds_row_inst: []long = null
ds_row_idx: []long = null
ds_last: int = -1
r3d_dev_v: int = -1
gltf_dir: string = null
gltf_bin: pointer = null
gltf_doc: Val = null
gltf_count: int = 0
gltf_ctype: int = 0
gltf_comps: int = 0
gltf_tex_paths: []pointer = null
gltf_tex_ids: words = null
gltf_tex_n: int = 0
gltf_white: int = 0
gltf_flat: int = 0
gltf_mat_names: []string = null
gltf_mat_diff: words = null
gltf_mat_nrm: words = null
gltf_mat_arm: words = null
gltf_mat_n: int = 0
gltf_cutout: bool = false # the material being loaded is alpha-blended (a cut-out atlas)
gpu_kind: int = 0 # the backend running; 0 until gpu_select
gpu_wanted: int = 0 # what was asked for (setting or R3D_GFX)
gpu_fallback_reason: string = null # why the wanted backend is not the one running
gpu_s_depth_test: int = -1
gpu_s_depth_func: int = -1
gpu_s_depth_write: int = -1
gpu_s_blend: int = -1
gpu_s_blend_src: int = -1
gpu_s_blend_dst: int = -1
gpu_s_cull: int = -1
gpu_s_cull_face: int = -1
gpu_s_color_write: int = -1
gpu_s_a2c: int = -1
gpu_s_bias: int = -1
gpu_s_bias_f: float = 0.0 # float bits, for a backend that bakes the bias into a pipeline
gpu_s_bias_u: float = 0.0
gpu_s_scissor: int = -1
gpu_prog_ids: []int = null
gpu_prog_keys: []string = null
gpu_prog_cur: int = 0
gpu_u_tmp: words = null
gpu_cap_probed: bool = false
gpu_cap_windows: bool = false # the advanced features exist on this platform at all
gpu_cap_vulkan: bool = false # a Vulkan loader, an instance and a device
gpu_cap_floor: bool = false # Vulkan 1.3 with everything the renderer's floor needs
gpu_cap_rt: bool = false # ray query + acceleration structures
gpu_cap_mesh: bool = false # VK_EXT_mesh_shader
gpu_cap_nvidia: bool = false
gpu_cap_rtx: int = 0 # the RTX generation (20, 30, 40, 50); 0 = not RTX
gpu_cap_reflex: bool = false # VK_NV_low_latency2
gpu_cap_hdr: bool = false # the instance offers HDR colour spaces
gpu_cap_device: string = ""
gpu_tx: words = null
gpu_tx_cap: int = 0
gpu_bound_2d: int = 0
gpu_bound_array: int = 0
gpu_unit_2d: words = null
gpu_unit_cur: int = 0
gpu_fb: words = null
gpu_fb_cap: int = 0
gpu_fb_cur: int = 0
gpu_glcheck: int = -1
gpu_rb_samples: int = 0
gpu_drawbufs: words = null
gpu_glcheck_seen: []string = null
gpu_variants: []GpuVariant = null
gvk_ready: bool = false
gvk_inst: pointer = null
gvk_pd: pointer = null
gvk_dev: pointer = null
gvk_queue: pointer = null
gvk_family: int = -1
gvk_mp: bytes = null # VkPhysicalDeviceMemoryProperties
gvk_device_name: string = ""
gvk_why: string = "" # why the device did not come up, for the fallback notice
gvk_max_aniso: int = 0x3F800000 # the device's anisotropy limit as float bits; 1.0 when it has none
gvk_want_surface: bool = false # set before gvk_init when the renderer will present to a window
gvk_has_surface: bool = false # the instance and device can make a surface and a swapchain
gvk_errlog: string = null
gvk_errlog_read: bool = false
gvk_has_mdi: bool = false # multiDrawIndirect + drawIndirectFirstInstance
gvk_has_dic: bool = false # drawIndirectCount
gvk_has_mesh: bool = false # VK_EXT_mesh_shader with its meshShader feature on
gvk_mesh_fn: pointer = null # vkCmdDrawMeshTasksEXT: the device's own, the interposer exports none
gvk_n_allocs: int = 0
gvk_blk_mem: []long = null
gvk_blk_kind: []int = null # memory type * 2, + 1 for images
gvk_blk_size: []int = null
gvk_blk_map: []pointer = null
gvk_fr_blk: []int = null # free ranges: block, offset, length (0 = an unused slot)
gvk_fr_off: []int = null
gvk_fr_len: []int = null
gvk_al_blk: []int = null # per allocation id: its block, or -1 for its own memory
gvk_al_mem: []long = null # its own memory when it has one
gvk_al_off: []int = null
gvk_al_len: []int = null
gvk_al_map: []pointer = null
gvk_al_spare: []int = null # ids to reuse
gvk_pool: long = 0
gvk_fence: bytes = null
gvk_frame_fence: bytes = null # the presented frame still on the GPU (one frame in flight)
gvk_tmp_buf: pointer = null # the per-call scratch ring (gvk_tmp)
gvk_tmp_off: int = 0
gvk_seen: words = null # gvk_draw's vertex buffers already bound, reused
gvk_frame_pending: bool = false
gvk_frame_pending_cb: pointer = null
gvk_frame_pending_no: int = -1 # which frame it is (gvk_frame_no when it was submitted)
gvk_dpools: []long = null # a descriptor pool per frame slot (frame_no & 1)
gvk_ring_end: int = 0 # the end of this frame's half of the ring
gvk_retired_frame: []int = null # the last frame that read each retired buffer
gvk_labels: bool = false # R3D_VK_LABELS: each profiled pass is a debug label (Metal System Trace)
gvk_inflight: int = -1
gvk_nopool: bool = false
# R3D_VK_PROF: Vulkan objects made and destroyed, reported every 120 frames
gvk_mk_img: int = 0
gvk_mk_view: int = 0
gvk_mk_buf: int = 0
gvk_mk_mem: int = 0
gvk_mk_smp: int = 0
gvk_mk_set: int = 0
gvk_mk_cmd: int = 0
gvk_mk_dpool: int = 0
gvk_mk_x_img: int = 0
gvk_mk_x_view: int = 0
gvk_mk_x_buf: int = 0
gvk_mk_x_mem: int = 0
gvk_label_depth: int = 0 # -1 until asked: 1 leaves the presented frame in flight
gvk_has_hqr: bool = false
gvk_ts_period: float = 0.0 # float bits: nanoseconds per timestamp tick
gvk_qpool: long = 0
gvk_q_active: int = -1
gvk_prog_var: []GpuVariant = null
gvk_prog_vs: []long = null
gvk_prog_fs: []long = null
gvk_prog_dsl: []long = null
gvk_prog_layout: []long = null
gvk_spv_dir: string = ""
gvk_spv_len: int = 0
gvk_prog_mesh: []int = null
gvk_cp_pipe: []long = null
gvk_cp_layout: []long = null
gvk_cp_dsl: []long = null
gvk_cp_nbuf: []int = null
gvk_cp_ntex: []int = null
gvk_pipe_keys: []string = null
gvk_pipe: []long = null
gvk_zero_vbuf: int = 0
gvk_skip_said: words = null # per program: its skipped draws have been reported
gvk_ublk_v: []bytes = null # per program: the vertex stage's block as last set
gvk_ublk_f: []bytes = null
gvk_prog_tex: []words = null # per program: the texture bound to each manifest sampler
gvk_prog_unit: []words = null # per program: the unit + 1 each sampler was pointed at with u_i
gvk_prog_dirty: []int = null # per program: its blocks or textures changed since its last set
gvk_set_last: []long = null # per program: the descriptor set its last draw used
gvk_set_frame: []int = null # per program: the frame that set belongs to
gvk_set_tex: []words = null # per program: the textures that set was filled with
gvk_ring_buf: int = 0 # a gvk_buf handle
gvk_ring_off: int = 0
gvk_ring_align: int = 256
gvk_dpool: long = 0
gvk_white: int = 0 # a 1x1 white texture for a sampler nothing was bound to
gvk_msaa_max: int = 0 # samples the scene may use (gvk_frame_init reads the device's limits)
gvk_kpool: long = 0
gvk_sc_prog: []int = null # per cached set: its program
gvk_sc_koff: []int = null # per cached set: where its key starts in gvk_sc_keys
gvk_sc_set: []long = null
gvk_sc_next: []int = null # the program's next cached set, -1 at the end
gvk_sc_keys: []long = null # per texture of a key: handle * 65536 + generation, sampler
gvk_sc_head: words = null # per program (< 4096): its most recently made set, -1 if none
gvk_sc_tmp: []long = null # this draw's key while it is looked up
gvk_set_offs: bytes = null # this draw's dynamic offsets, in binding order
gvk_set_ndyn: int = 0
gvk_ub_frame: []int = null # per program: the frame its blocks were last copied into the ring
gvk_ub_offv: []int = null
gvk_ub_offf: []int = null
gvk_sc_made: int = 0 # sets made since start (R3D_VK_PROF)
gvk_default_smp: long = 0 # linear, clamped: for a texture slot nothing was bound to
gvk_cb: pointer = null
gvk_in_pass: bool = false
gvk_fb_cur: int = 0
gvk_fb_read: int = 0
gvk_fb_draw: int = 0
gvk_pass_w: int = 0
gvk_pass_h: int = 0
gvk_pass_ncolor: int = 0
gvk_pass_cfmt: int = 0
gvk_pass_dfmt: int = 0
gvk_pass_samples: int = 1 # the attachments' sample count, which every pipeline in the pass must match
gvk_pass_col: words = null # the pass's colour attachments: texture, layer + 1
gvk_pass_dep: words = null # its depth attachment: texture, layer + 1
gvk_clear_bits: int = 0 # GL_COLOR_BUFFER_BIT / GL_DEPTH_BUFFER_BIT waiting for the pass
gvk_clear_rgba: floats = null # float bits
gvk_vp: words = null # x, y, w, h
gvk_sc: words = null # scissor on, x, y (OpenGL rows), w, h
gvk_screen_color: int = 0
gvk_screen_depth: int = 0
gvk_screen_w: int = 0
gvk_screen_h: int = 0
gvk_layer_views: []int = null # generation * 4096 + layer, beside gvk_layer_view_tex
gvk_layer_view: []long = null
gvk_layer_view_tex: []int = null # the texture each cached view is of, so a release drops them
gvk_fb_ncolor: words = null # per framebuffer: colour slots drawn (draw buffers; 0 = none)
gvk_hdr_want: bool = false # r3d_hdr: the setting
gvk_hdr_on: bool = false # the swapchain is HDR10 now
gvk_has_colorspace: bool = false # the instance took VK_EXT_swapchain_colorspace
gvk_has_hdr_meta: bool = false # the device took VK_EXT_hdr_metadata
r3d_hdr_peak: float = 0.0 # 0 until r3d_hdr_calibrate: 1000
r3d_hdr_paper: float = 0.0 # 200
r3d_hdr_black: float = 0.0 # 0
gvk_fb_counter: int = 0
gvk_prog_counter: int = 0
gvk_wireframe: int = 0
gvk_state: GvkState = null
gvk_ind_buf: int = 0
gvk_ind_off: int = 0
gvk_ind_n: int = 0
gvk_ind_cbuf: int = 0
gvk_ind_coff: int = 0
gvk_mesh_x: int = 0
gvk_mesh_y: int = 0
gvk_mesh_z: int = 0
gvk_surface: long = 0
gvk_swap: long = 0
gvk_swap_n: int = 0
gvk_swap_images: bytes = null
gvk_swap_fmt: int = 0
gvk_swap_w: int = 0
gvk_swap_h: int = 0
gvk_vsync: bool = true
gvk_acq_fence: bytes = null
gvk_swap_stale: bool = false
gvk_size_buf: words = null
gvk_prof_state: int = -1
gvk_us_pipe: long = 0
gvk_us_set: long = 0
gvk_us_draw: long = 0
gvk_n_draws: int = 0
gvk_n_asked: int = 0 # draws that reached gvk_draw_now: every one the renderer asked for
gvk_n_flush: int = 0
gvk_prof_frames: int = 0
gvk_n_pipe_new: int = 0 # pipelines made this profile window
gvk_layout_keys: []string = null
gvk_pc_prog: []int = null
gvk_pc_layout: []int = null
gvk_pc_state: []int = null
gvk_pc_bias: []int = null
gvk_pc_pass: []int = null
gvk_pc_pipe: []long = null
gvk_pc_last: words = null
gvk_tex_image: []long = null
gvk_tex_view: []long = null
gvk_tex_mem: []long = null
gvk_tex_levels: []int = null
gvk_tex_layers: []int = null
gvk_tex_vkfmt: []int = null
gvk_tex_dims_w: []int = null
gvk_tex_dims_h: []int = null
gvk_tex_glfmt: []int = null # the format the renderer asked for (gpu.ludic's vocabulary)
gvk_tex_array: []int = null # 1 for an array image
gvk_tex_gen: []int = null # bumped whenever the image behind a handle is replaced
gvk_tex_smp_sig: []int = null # the sampling parameters the cached sampler was made for
gvk_tex_smp: []long = null
gvk_tex_samples: []int = null # 1, or the sample count of a multisampled renderbuffer
gvk_storage_samples: int = 1 # what the next gvk_tex_storage makes: gpu_rb_storage sets it around its call
gvk_unpack_swap: bool = false # GL_UNPACK_SWAP_BYTES: 16-bit PNG samples arrive big-endian
gvk_st_buf: long = 0
gvk_st_mem: long = 0
gvk_smp_keys: []string = null
gvk_smp: []long = null
gvk_buf: []long = null
gvk_buf_mem: []long = null
gvk_buf_size: []int = null
gvk_buf_map: []pointer = null # host-visible buffers stay mapped for their whole life
gvk_buf_used: []int = null # the frame (gvk_frame_no) a draw last bound the buffer in
gvk_frame_no: int = 1
gvk_retired_buf: []long = null
gvk_retired_mem: []long = null
gvk_buf_gpu: []int = null
grass_prog: int = 0
grass_mesh: Mesh = null
grass_on: bool = true
grass_wind: float = 0.0
grass_s0: float = 0.0 # float bits: blade spacing at the camera (m)
grass_d0: float = 0.0 # the distance at which the spacing has doubled (m)
grass_radius: float = 0.0 # no blades past this (m)
grass_draws: int = 0
grass_dbg: int = 0
grass_merge: bool = false
grass_rec: words = null # VkDrawIndexedIndirectCommand records, 5 words each
grass_tv: floats = null # per record: corner x, corner z, indices per cell, 0 (float bits)
grass_chunk_tv: floats = null
grass_n: int = 0
grass_cmds: int = 0
grass_band_start: words = null # per band: its first record
grass_band_cells: words = null # ... and its cells per tile side
grass_band_n: int = 0
gg_on: bool = false # the blades are culled on the GPU (grass_gpu.ludic)
gg_reset: int = 0
gg_cull: int = 0
gg_prog: int = 0
gg_tiles_buf: []int = null
gg_tv: floats = null
gg_out: int = 0
gg_cmds: int = 0
gg_mesh: []Mesh = null
gg_n: int = 0
gg_max: int = 0
gg_rb: words = null # the per-frame dispatch arguments, made once (grass_gpu.ludic)
gg_cb: words = null
gg_bufs: words = null
gg_texs: words = null
gg_pr: words = null
grass_band_mesh: []Mesh = null # per band: the blade it draws (rows fall with distance)
grass_mesh3: Mesh = null # three rows: the middle distance
grass_mesh2: Mesh = null # two rows, one quad: far enough that the arch is under a pixel
grass_mesh_on: bool = false
grass_mesh_prog: int = 0
r3d_draw_cb: fn() = null
r3d_casters_cb: fn(floats) = null
r3d_fill_cb: fn(Stream, int, int, int) = null
ov_prog: int = 0
ov_mesh: Mesh = null
ov_vbo: int = 0
ov_buf: pointer = null
ov_n: int = 0
ov_tex: int = 0
ov_mode: int = 2 # what the next quads read: 0 the image texture, 1 the font, 2 a flat colour
ov_voff: float = 0.0 # float bits: 4 x ov_mode, added to v (overlay.frag)
ov_white: int = 0
ov_font: int = 0
ov_font_adv: floats = null # float bits, em units, one per glyph in the atlas
ov_font_n: int = 0 # glyphs in the atlas
ov_font_map: words = null
ov_font_hi_cp: words = null # code points >= OV_MAP_N, ascending
ov_font_hi_g: words = null
ov_font_hi_n: int = 0
ov_font_space: int = 0 # the glyph for ' ', and for what has none: '?'
ov_font_qmark: int = 0
ov_font_cols: int = 16
ov_font_rows: int = 6
ov_font_cell: int = 128 # px per cell in the atlas
ov_font_em: int = 100 # px per em in the atlas
ov_pad_x: float = 0.0 # float bits, em
ov_base_y: float = 0.0
ov_ready: bool = false
ov_open: bool = false
ov_dbg: bool = false
ov_ranges: words = null
ov_nr: int = 0
ov_range_start: int = 0
ov_clip_x: int = 0 # the current clip rectangle in screen pixels (top-left origin)
ov_clip_y: int = 0
ov_clip_w: int = 0
ov_clip_h: int = 0
ov_prog_sdr: int = 0
ov_prog_hdr: int = 0
ov_hdr_scale: float = 0.0 # float bits; 0 until ov_begin sets 1
ov_u8_len: int = 1
post_hdr: Target = null
post_ms_fbo: int = 0 # 4x multisampled scene target, resolved into post_hdr
post_ms_samples: int = 1 # temporal AA carries the edges; R3D_MSAA=n to compare
post_bloom: []Target = null
post_p_down: int = 0
post_p_up: int = 0
post_p_tone: int = 0
post_fs: Mesh = null
post_exposure: float = 0.0
post_bloom_strength: int = 0
post_vignette: float = 0.0
post_saturation: float = 0.0
post_contrast: float = 0.0
post_w: int = 0
post_h: int = 0
post_key: float = 0.0 # target mean luminance after exposure (float bits)
post_lum: words = null
post_mips: int = 0
post_adapt: float = 0.0 # smoothed exposure (float bits)
post_exposure_max: float = 20.0 # 20: the ceiling auto-exposure may reach (night lowers it)
post_wb_r: float = 1.02 # 1.02
post_wb_g: float = 1.0 # 1.0
post_wb_b: float = 0.97 # 0.97
post_lift_r: float = 0.004 # 0.004
post_lift_g: float = 0.004 # 0.004
post_lift_b: float = 0.012 # 0.012
post_gain_r: float = 0.99 # 0.99
post_gain_g: float = 0.995 # 0.995
post_gain_b: float = 1.0 # 1.0
post_ao: Target = null
post_ao_blur: Target = null
post_p_ao: int = 0
post_p_ao_blur: int = 0
post_ao_radius: float = 0.0
post_ao_intensity: float = 0.0
post_ao_strength: float = 0.0
post_gi_strength: float = 0.4 # 0.4
post_no_gi: bool = false
post_ldr: Target = null
post_depth_copy: Target = null
post_vol: Target = null
post_p_vol: int = 0
post_vol_density: float = 0.002 # 0.002
post_vol_falloff: float = 0.005 # 0.005
post_vol_mist: float = 0.0 # the day sets these two
post_prev: Target = null # last frame's scene colour, for the SSGI bounce only
post_scene: Target = null # this frame's scene colour before the water, for refraction
post_frame: int = 0
post_color_w: int = 0 # its size: the display's when DLSS upscaled it
post_color_h: int = 0
post_color: int = 0 # the HDR colour the rest of post reads # the resolved depth, copied so passes can read it while drawing into the frame
post_p_sharp: int = 0
post_p_dof: int = 0
post_dof: Target = null
post_p_tone_hdr: int = 0 # the tonemap's HDR10 variant, made the first time HDR is on
post_ldr_hdr: bool = false # post_ldr was made for HDR10 output (10-bit)
post_sharpen: float = 0.0
post_grain: int = 0
post_adapt_t: []Target = null
post_adapt_i: int = 0
post_p_adapt: int = 0
post_adapt_reset: bool = true
prof_on: bool = false
prof_names: []pointer = null
prof_ids: words = null # PROF_SLOTS * PROF_RING query objects
prof_ns: []long = null # accumulated nanoseconds per slot
prof_hits: []long = null # samples accumulated per slot
prof_n: int = 0 # slots in use
prof_frame: int = 0
prof_active: int = -1 # slot whose query is currently open
prof_scratch: words = null
prof_gen: []int = null
prof_gen_cur: int = 0
prof_ft: []long = null # real frame times, microseconds
prof_last_us: long = 0
prof_chunk_us: []long = null
prof_chunk_kind: []int = null
prof_chunk_band: []int = null
prof_chunk_n: []int = null
prof_gen_us_cur: long = 0
prof_gen_us: []long = null # generation microseconds per frame
prof_layer_us_cur: long = 0 # rebuilding and uploading instance buffers
prof_bake_us_cur: long = 0 # the height-field shadow rebake
prof_layer_us: []long = null
prof_bake_us: []long = null
prof_dt: []long = null # frame time, aligned with the arrays above
prof_up_cur: long = 0 # instance bytes uploaded this frame
prof_up: []long = null
prof_pre_swap: long = 0
prof_cpu_us: []long = null
prof_sum_dt: long = 0 # sums over the frames past the first eight, for the mean split
prof_sum_cpu: long = 0
prof_sum_game: long = 0
prof_sum_n: int = 0
prof_mark_last: long = 0
prof_mark_best: long = 0
prof_mark_name: pointer = null
prof_mark_us: []long = null
prof_mark_who: []pointer = null
prof_mk_names: []pointer = null
prof_mk_us: []long = null
prof_mk_n: int = 0
prof_game_us_cur: long = 0
prof_game_us: []long = null
r3d_root: string = "packages/ludic.render3d" # where shaders/ lives
r3d_assets: string = "assets/polyhaven" # where the CC0 assets live
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
q_scratch: floats = null
q_sy: floats = null # views of q_scratch's second, third and fourth
q_sz: floats = null
q_st: floats = null
r3d_sky_prog: int = 0
r3d_fog_density: float = 0.0
r3d_fog_falloff: float = 0.0
r3d_fog_inscatter: float = 0.02 # 0.02, what the shader used to hard-code
r3d_fog_desat: float = 1.35 # 1.35
r3d_sky_relight: float = 0.0
r3d_time: float = 0.0
r3d_ready: bool = false
r3d_plate: bool = false
r3d_sky_path: string = null
r3d_debug: bool = false
r3d_debug_shadow: bool = false
r3d_clip_y: float = -2147483600.0 # -2^31: no clipping
r3d_prepass_env: int = -1
r3d_test_frame: int = 0
r3d_test_resize: int = 0
r3d_no_shadow: bool = false
r3d_no_trees: bool = false
r3d_no_refl: bool = false
r3d_cam_log: int = -1
sc_prog: int = 0
sc_prog_fol: int = 0
sc_prog_wind: int = 0
sc_prog_blade: int = 0
sc_prog_flower: int = 0
sc_prog_card: int = 0
sc_prog_card_shadow: int = 0
sc_prog_card_cheap: int = 0
sc_blade_base: floats = null
sc_blade_tip: floats = null
sc_blade_tint: floats = null
sc_prog_shadow: int = 0
sc_prog_shadow_wind: int = 0
sc_prog_shadow_fol: int = 0 # foliage meshes: alpha-tested casters
sc_prog_fol_depth: int = 0
sc_prog_fol_eq: int = 0
sc_prepass: bool = false # the main pass is drawing over what the prepass laid down
sc_imp_prog: int = 0
sc_imp_prog_shadow: int = 0
sc_bake_prog: int = 0
sc_bake_card_prog: int = 0
sc_bake_flower_prog: int = 0
sc_card: Mesh = null
sc_ident_buf: int = 0
sc_layers: []Layer = null
sc_debug_dump: bool = false
sc_printed: bool = false
sc_dump_n: int = 0
sc_view_gen: int = 1
sc_view_pos: floats = null
sc_view_fwd: floats = null
sc_cull_prog: int = 0
sc_cull_tried: bool = false
sc_ind_base: int = -1 # >= 0: layer_draw_model / layer_draw_depth draw records from here
sc_ind_n: int = 1 # records each of those draws covers: one per level of a material
sc_ind_stride: int = 4 # records between one material's first and the next's (3 for the shadow LOD)
sc_freeze: bool = false # a secondary pass (reflection) reuses the partition
sc_dbg_level: int = -1
sc_dbg_lod: bool = false
sc_dbg_tint: floats = null
sc_cast_all: int = -1
sc_cast_gen: int = -1
sc_cast_dy: float = 0.0
sc_cast_k: float = 0.0
sc_skip_blade: bool = false
sc_skip_grass: bool = false
grass_force: bool = false # R3D_BLADES: the blades on whatever the game set
grass_env_off: bool = false # R3D_NOBLADES: the blades off whatever the game set
sc_skip_card: bool = false
cb_state: int = 12345
shadow_res: int = 2048
shadow_refused: int = 0 # the last size the graphics card had no memory for (0: none)
sh_tex: int = 0
sh_fbo: int = 0
sh_vp: floats = null # the cascades' view-projections, 16 floats each
sh_vp_v: [][]float = null # a view of each
sh_split: floats = null # view-space far distance of each cascade
sh_range: floats = null # 4 light-frustum depth extents (metres)
sh_texel: floats = null # 4 shadow texel sizes (metres)
sh_tmp_proj: floats = null
sh_tmp_vp: floats = null
sh_tmp_inv: floats = null
sh_tmp_view: floats = null
sh_corner: floats = null
sh_cascade: int = 0 # the cascade being rendered (for casters that skip far ones)
sh_printed2: bool = false
sh_printed3: bool = false
sh_force: int = -1 # R3D_FORCE=<c> pins every pixel to cascade c (debug)
sh_printed: bool = false
sky_tex: int = 0
sky_w: int = 0
sky_h: int = 0
sky_irradiance: int = 0
sky_prefilter: int = 0 # GL_TEXTURE_2D_ARRAY
sky_brdf: int = 0
sun_dir: floats = null # toward the sun (float bits)
sun_color: floats = null # radiance (float bits)
sky_fullscreen: Mesh = null
sky_yaw: float = 0.0 # radians: the HDRI is turned by this about y
sky_rot_s: float = 0.0
sky_rot_c: float = 0.0
sun_hdri: floats = null # the sun direction as found in the file
sky_baked: bool = false # the IBL set exists, baked at sky_baked_yaw
sky_baked_yaw: float = 0.0
sky_p_irr: int = 0
sky_p_pre: int = 0
sky_p_brdf: int = 0
sky_prefilter_w: int = 512
STREAM_MAX_CHUNKS: int = 4096
stream_all: []Stream = null
stream_cap_read: bool = false
stream_no_evict: bool = false # R3D_NOEVICT: the old behaviour, for comparison
stream_walk_no: int = 0 # counts ring walks; a chunk's age is measured in these
stream_evictions: int = 0
stream_us_gen: long = 0 # generating new chunks (stream_fill)
stream_us_gather: long = 0 # copying cached chunks into the layer buffer
stream_us_walk: long = 0 # the ring walk itself
stream_walks: int = 0 # streams that walked their whole ring this frame
stream_debug_n: int = 0
stream_scratch: floats = null
stream_deadline: long = 0
gsl_on: bool = false # slInit succeeded: the interposer is the loader, the plugins are in
gsl_dlss_ok: bool = false # what this adapter can run, from slIsFeatureSupported
gsl_rr_ok: bool = false
gsl_fg_ok: bool = false
gsl_reflex_ok: bool = false
gsl_pcl_ok: bool = false
gsl_feats: bytes = null # kept alive: Preferences points at it
gsl_logdir: bytes = null # and at this
gsl_vp: bytes = null # sl::ViewportHandle 0, the one view
gsl_tok_buf: bytes = null
gsl_idx_buf: bytes = null
gsl_token: pointer = null # this frame's sl::FrameToken, owned by Streamline
gsl_frame_n: int = 0
gsl_said: bool = false # one failure message, not one a frame
gsl_fresh: bool = false # a token was taken since the last frame started (a present took it)
gsl_reflex_mode: int = 0 # 0 off, 1 low latency, 2 low latency + boost
gsl_reflex_applied: int = -1
gsl_f_sleep: pointer = null
gsl_f_marker: pointer = null
gsl_dlss_mode: int = 0 # 0 off, 1 DLAA, 2 quality, 3 balanced, 4 performance
gsl_opts: bytes = null
gsl_opt_mode: int = -1
gsl_opt_w: int = 0
gsl_opt_h: int = 0
gsl_rw: int = 0 # the render size DLSS asked for
gsl_rh: int = 0
gsl_set_mode: int = -1
gsl_set_w: int = 0
gsl_set_h: int = 0
gsl_mv: Target = null
gsl_out: Target = null
gsl_consts: bytes = null
gsl_tags: bytes = null
gsl_res: bytes = null
gsl_inputs: bytes = null
gsl_prev_vp: floats = null
gsl_reset: bool = true
gsl_jitter_x: float = 0.0 # float bits, NDC offsets the projection carries this frame
gsl_jitter_y: float = 0.0
gsl_jpx: float = 0.0 # float bits, the same in pixels
gsl_jpy: float = 0.0
gsl_eval_ok: bool = true # the last evaluate worked: only then is the next frame jittered
TERRAIN_HALF: int = 4096 # world half-size in metres (8 km square)
cd_mesh: Mesh = null
cd_range: floats = null # float bits: how far each level is drawn
cd_min: []floats = null # per level: min height of each patch (float bits)
cd_max: []floats = null
cd_draws: int = 0
cd_far_draws: int = 0
cd_near_draws: int = 0
ter_force_far: bool = false
ter_no_split: bool = false
ter_force_near: bool = false
ter_skip: bool = false
ter_height_tex: int = 0
ter_heights: floats = null # CPU copy, float bits, TERRAIN_RES^2
ter_reflect: bool = false # drawing the reflection: the mid mesh is plenty
ter_prog: int = 0
ter_prog_far: int = 0
ter_prog_near: int = 0 # the detailed tier alone (NEAR_ONLY)
ter_sun_prog: int = 0 # tersun.frag: sun visibility into a screen buffer
ter_sun_inline: bool = false # the ground's shader reads the cascades itself (Vulkan); no sun pass
ter_sun_tgt: Target = null # that buffer, at the frame's size
ter_sun_refl: Target = null # and at the reflection's, which is smaller
ter_sun_dumped: bool = false
ter_sun_checked: bool = false
ter_sun_done: bool = false # the caller already ran the pass (so it can time it)
ter_sun_pass: bool = false # selection is drawing the visibility pass
ter_sun_tex: int = 0
ter_prog_cur: int = 0 # the program currently bound during selection
ter_smooth: bool = false # generate the analytic test ground instead of a survey
ter_far_split: float = 0.0 # metres: beyond this the terrain takes its cheap far path (R3D_TFAR)
ter_far_band: float = 0.0 # half-width of the near/far blend (R3D_TBAND)
ter_snow_line: float = 0.0
ter_tex: words = null # 11 material textures (see terrain_bind)
ter_ox: float = 0.0 # world x/z of the terrain centre (float bits)
ter_oz: float = 0.0
ter_dem_tex: int = 0 # a real height map (16-bit), or 0 for the procedural valley
ter_dem_min: float = 0.0
ter_dem_max: float = 0.0
ter_dem_base: float = 0.0
ter_ortho_tex: int = 0 # a photograph of the same window, draped with distance
ter_carpet: int = 0 # the distant-grass carpet (carpet_bake), 0 = none
ter_shadow_tex: int = 0 # height-field sun shadow: RG32F (lowest lit height, occluder distance)
ter_shadow_yaw: float = 1000000000.0 # the sky yaw it was baked for
ter_shadow_gen: int = -1 # the daylight generation it was baked for
ter_shadow_prog: int = 0
ter_lake_level: float = 0.0 # a lake carved into the height map (float bits; ex = 0 → none)
ter_lake_cx: float = 0.0
ter_lake_cz: float = 0.0
ter_lake_ex: float = 0.0
ter_lake_ez: float = 0.0
ter_lake_carve: bool = true
ter_sea_level: float = 0.0
ter_sea_set: bool = false
ter_isle_cx: float = 0.0
ter_isle_cz: float = 0.0
ter_isle_r: float = 0.0
ter_isle_fall: float = 0.0
ter_isle_mode: int = 0
ter_ortho_px: pointer = null # the photograph on the CPU (RGB8, TERRAIN_RES^2) for placement rules
ter_ortho_w: int = 0
ter_ortho_c: int = 3
ter_h_scale: float = 0.0
ter_o_scale: float = 0.0
ter_bw: floats = null
ter_wire: bool = false
ter_printed: bool = false
ter_scr: floats = null
tex_w: int = 0 # the last decoded image
tex_h: int = 0
tex_channels: int = 0
tex_depth: int = 0 # bits per sample (8 or 16)
tex_file_len: int = 0
tex_anisotropy: fixed = 16.0
tex_size_ids: []int = new []int
tex_size_ws: []int = new []int
tex_size_hs: []int = new []int
hdr_max_lum: float = 0.0 # float bits of the brightest texel (sun finding)
hdr_max_x: int = 0
hdr_max_y: int = 0
hdr_sun_r: float = 0.0 # irradiance (float bits) of everything above the IBL clip: the sun
hdr_sun_g: float = 0.0
hdr_sun_b: float = 0.0
hdr_clip: float = 0.0 # float bits; texels above this (per channel) feed the sun, not the IBL
tex_dump_alpha: bool = false
water_mesh: Mesh = null
water_prog: int = 0
water_level: float = 0.0
water_cx: float = 0.0
water_cz: float = 0.0
water_ex: float = 0.0
water_ez: float = 0.0
water_on: bool = false
water_refl: Target = null # the world mirrored in the surface, half resolution
water_refl_div: int = 2 # R3D_REFLDIV overrides: 2 = half res, 4 = quarter
water_saved: floats = null # the real camera's matrices, restored after the pass
water_saved_vp: floats = null # views of its second and third matrices
water_saved_ivp: floats = null
water_dumped: bool = false
wb_n: int = 0
wb_level: floats = null
wb_cx: floats = null
wb_cz: floats = null
wb_ex: floats = null
wb_ez: floats = null
wb_reflect: words = null
wb_primary: int = -1 # the body the reflection pass mirrors, or -1
wb_cells: floats = null # x0, z0, x1, z1 of each rectangle with water showing
wb_ncells: int = -1 # -1: not built for the current mirrored body
wb_blocks: words = null # x0, z0, x1, z1, first cell, cells past the last - of each block
wb_nblocks: int = 0
wb_refl_always: int = -1
wb_dbg: int = -1
wb_build_us: long = 0 # how long the last water_cells_build took (R3D_REFL_DBG prints it)
grass_blade_tex: int = 0
grass_blade_cols: int = 8
grass_push_x: float = 0.0
grass_push_z: float = 0.0
grass_push_r: float = 0.0
post_auto: bool = true
post_contact: float = 1.25 # 1.25: how hard a thing is darkened where it meets the ground
post_vol_steps: float = 24.0 # 24
post_vol_far: float = 800.0 # 800 m
post_vol_g: float = 0.6 # 0.6: air throws light forward
post_vol_mist_h: float = 40.0 # 40 m
post_fxaa: float = 1.0
post_dof_focus: float = 10.0 # 10 m
post_dof_aperture: float = 0.0 # 0 = no lens, and no pass
post_dof_max: float = 12.0 # 12 px
r3d_global_defs: string = ""
r3d_fog_scale: float = 1.0 # float bits: a setting's multiplier over the density the day sets
r3d_fog_base: float = 0.0 # the height fog is measured from (float bits); 0 = y = 0
r3d_ground_alb_r: float = 0.3
r3d_ground_alb_g: float = 0.34
r3d_ground_alb_b: float = 0.14
r3d_ground_hi_r: float = 0.289
r3d_ground_hi_g: float = 0.28
r3d_ground_hi_b: float = 0.26
r3d_ground_hi_y: float = 480.0 # 480
r3d_ground_hi_w: float = 150.0 # 150
r3d_dem_path: string = null
r3d_dem_min: float = 0.0
r3d_dem_max: float = 0.0
r3d_dem_base: float = 0.0
r3d_dem_ox: float = 0.0
r3d_dem_oz: float = 0.0
r3d_ortho_path: string = null
r3d_debug_max: bool = false
r3d_cloud_shadow: float = 0.5 # 0.5
sc_a2c: bool = true
sc_bake_flower: bool = false
sc_dbg_blade: int = 0
sc_skip_flower: bool = false
sh_enabled: bool = true
sh_skip_terrain: bool = false
sh_probe_x: float = 0.0
sh_probe_y: float = 0.0
sh_probe_z: float = 0.0
sky_sun_boost: float = 2.3 # 2.3: the photograph's thin cloud dims its sun; a crisper day wants more
sky_start_yaw: float = 0.0
STREAM_BUDGET_US: int = 2500 # microseconds of generation per frame; a setting may move it
stream_budget_left: int = 0
ter_wet: float = 0.0
ter_dem_blur: float = 0.0 # gaussian texels applied to the survey (0 for lidar; ~3 for 30 m data)
wt_wade_x: float = 0.0
wt_wade_z: float = 0.0
wt_wade_s: float = 0.0
}
function r3d_dev(render3d_st: mut Render3dState) -> bool {
if render3d_st.r3d_dev_v < 0 { if Os.has_env("R3D_DEV") and Os.env("R3D_DEV") != "0" { render3d_st.r3d_dev_v = 1 } else { render3d_st.r3d_dev_v = 0 } }
return render3d_st.r3d_dev_v == 1
}
function r3d_env_has(render3d_st: mut Render3dState, name: string) -> bool {
if is_windowed() and not r3d_dev(render3d_st) { return false }
return Os.has_env(name)
}
function r3d_env(render3d_st: mut Render3dState, name: string) -> string {
if is_windowed() and not r3d_dev(render3d_st) { return "" }
return Os.env(name)
}