render3d: recycle texture and buffer ids, free a released model whole, actor_release
A freed texture or buffer id goes on a spare list the next one takes; tex_note_size keeps sizes by id. model_release frees prims, meshes, material names, the skin and leaves gltf_cached. actor_release takes an actor off the stage and actor_new reuses its record (ECS's Things come and go all day). steady.ludic: an actor round at 0 bytes over 2000. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
2fe4018d28
commit
9b482d609a
14 changed files with 208 additions and 39 deletions
|
|
@ -86,6 +86,7 @@ function read_one(anim_st: AnimState, render3d_st: mut Render3dState, a: Val) ->
|
|||
c.ckeys[k] = nk
|
||||
c.coff[k] = at
|
||||
for i in 0 .. nk { c.times[at + i] = tb[i] }
|
||||
free(tb)
|
||||
if nk > 0 and c.times[at + nk - 1] > c.dur { c.dur = c.times[at + nk - 1] }
|
||||
at += nk
|
||||
let vb = gltf_accessor_floats(render3d_st, value_as_int(value_get(sp, "output")))
|
||||
|
|
@ -93,6 +94,7 @@ function read_one(anim_st: AnimState, render3d_st: mut Render3dState, a: Val) ->
|
|||
if c.cpath[k] == AC_ROT { comps = 4 }
|
||||
c.cvoff[k] = av
|
||||
for i in 0 .. nk * comps { c.vals[av + i] = vb[i] }
|
||||
free(vb)
|
||||
av += nk * comps
|
||||
k += 1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,12 +122,10 @@ function actor_hide_part(a: Actor, name: string, hidden: bool) -> void {
|
|||
}
|
||||
|
||||
function actor_new(render3d_st: mut Render3dState, model: Model) -> Actor {
|
||||
let a = new Actor
|
||||
let a = actor_fresh(render3d_st)
|
||||
a.model = model
|
||||
a.scale = 1.0
|
||||
a.tint = v3_new(1.0, 1.0, 1.0)
|
||||
a.rough = 1.0
|
||||
a.mat = m4_new()
|
||||
render3d_st.ac_next_id += 1; a.id = render3d_st.ac_next_id
|
||||
if model != null { a.radius = Math.max(model.radius, model.height) + 1.0 }
|
||||
a.cull = 450.0
|
||||
|
|
@ -417,3 +415,36 @@ function actor_clear_all(render3d_st: mut Render3dState) -> void {
|
|||
List.clear(render3d_st.ac_actors)
|
||||
outline_clear(render3d_st)
|
||||
}
|
||||
|
||||
# A record as `new Actor` makes it, with its matrix and tint: a released one when there is one.
|
||||
function actor_fresh(render3d_st: mut Render3dState) -> Actor {
|
||||
let sp = render3d_st.ac_spare
|
||||
if sp == null or len(sp) == 0 {
|
||||
let a = new Actor
|
||||
a.tint = v3_new(1.0, 1.0, 1.0)
|
||||
a.mat = m4_new()
|
||||
return a
|
||||
}
|
||||
let a = List.pop(sp)
|
||||
a.x = 0.0; a.y = 0.0; a.z = 0.0; a.yaw = 0.0; a.scale = 0.0; a.rough = 0.0
|
||||
a.tint[0] = 1.0; a.tint[1] = 1.0; a.tint[2] = 1.0
|
||||
m4_identity(a.mat)
|
||||
a.visible = true; a.cast_hidden = false; a.casts = true; a.row = -1
|
||||
a.cutout = false; a.emissive = 0.0; a.radius = 0.0; a.cull = 0.0; a.outline = 0.0
|
||||
a.skin = null; a.ocol = null
|
||||
return a
|
||||
}
|
||||
|
||||
# Off the stage for good, and its record kept for the next actor_new: whoever released it never
|
||||
# touches it again. What it was lent stays its lender's (a skin clone, a rim colour); what it made
|
||||
# for itself (a part's tint, a part hidden) goes.
|
||||
function actor_release(render3d_st: mut Render3dState, a: Actor) -> void {
|
||||
if a == null or a.id == 0 { return } # released already
|
||||
actor_remove(render3d_st, a)
|
||||
if a.ptint != null { free(a.ptint); a.ptint = null }
|
||||
if a.hide != null { free(a.hide); a.hide = null }
|
||||
a.model = null; a.skin = null; a.ocol = null
|
||||
a.id = 0
|
||||
if render3d_st.ac_spare == null { render3d_st.ac_spare = new []Actor }
|
||||
push(render3d_st.ac_spare, a)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ export state Render3dState {
|
|||
ac_out_cut: AcProg = null
|
||||
ac_actors: []Actor = null
|
||||
ac_next_id: int = 0
|
||||
ac_spare: []Actor = null # released actors, taken again by actor_new (actor_release)
|
||||
ac_frame: int = 0
|
||||
ac_oq: []OutlineReq = null
|
||||
ac_oq_n: int = 0 # live entries; the array is kept and reused
|
||||
|
|
@ -211,6 +212,8 @@ export state Render3dState {
|
|||
gvk_lay_flat: []int = null # every layout known, end to end; gvk_lay_off / _len find each
|
||||
gvk_lay_off: []int = null
|
||||
gvk_lay_len: []int = null
|
||||
gvk_tex_spare: []int = null # ids of freed textures, handed out again by gvk_tex_new
|
||||
gvk_buf_spare: []int = null # the same for buffers (gvk_buf_delete)
|
||||
gvk_has_bc: bool = false # textureCompressionBC: .dds textures are uploaded compressed
|
||||
# R3D_VK_PROF: Vulkan objects made and destroyed, reported every 120 frames
|
||||
gvk_mk_img: int = 0
|
||||
|
|
@ -805,7 +808,6 @@ export state Render3dState {
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -45,7 +45,9 @@ function gltf_mat_find(render3d_st: Render3dState, name: string) -> int {
|
|||
function gltf_mat_remember(render3d_st: mut Render3dState, name: string, diff: int, nrm: int, arm: int) -> void {
|
||||
if render3d_st.gltf_mat_names == null { render3d_st.gltf_mat_names = new []string; render3d_st.gltf_mat_diff = words(256); render3d_st.gltf_mat_nrm = words(256); render3d_st.gltf_mat_arm = words(256) }
|
||||
if gltf_mat_find(render3d_st, name) >= 0 or render3d_st.gltf_mat_n >= 256 { return }
|
||||
push(render3d_st.gltf_mat_names, name); render3d_st.gltf_mat_diff[render3d_st.gltf_mat_n] = diff; render3d_st.gltf_mat_nrm[render3d_st.gltf_mat_n] = nrm; render3d_st.gltf_mat_arm[render3d_st.gltf_mat_n] = arm
|
||||
# its own copy of the name: a primitive frees its own with it (model_release)
|
||||
push(render3d_st.gltf_mat_names, name[0 .. len(name)])
|
||||
render3d_st.gltf_mat_diff[render3d_st.gltf_mat_n] = diff; render3d_st.gltf_mat_nrm[render3d_st.gltf_mat_n] = nrm; render3d_st.gltf_mat_arm[render3d_st.gltf_mat_n] = arm
|
||||
render3d_st.gltf_mat_n += 1
|
||||
}
|
||||
|
||||
|
|
@ -53,14 +55,23 @@ function gltf_texture(render3d_st: mut Render3dState, uri: string, srgb: bool) -
|
|||
if render3d_st.gltf_tex_paths == null { render3d_st.gltf_tex_paths = new []pointer; render3d_st.gltf_tex_ids = words(256); render3d_st.gltf_tex_refs = words(256) }
|
||||
var png: string = uri
|
||||
let n = len(uri)
|
||||
if n > 4 and uri[n - 4] == '.' and uri[n - 3] == 'j' { png = uri[0 .. n - 4] + ".png" }
|
||||
var made = false
|
||||
if n > 4 and uri[n - 4] == '.' and uri[n - 3] == 'j' { png = uri[0 .. n - 4] + ".png"; made = true }
|
||||
let path = render3d_st.gltf_dir + "/" + png
|
||||
if made { free(png) }
|
||||
var i = 0
|
||||
while i < render3d_st.gltf_tex_n { if render3d_st.gltf_tex_paths[i] == path { render3d_st.gltf_tex_refs[i] += 1; return render3d_st.gltf_tex_ids[i] }; i += 1 }
|
||||
while i < render3d_st.gltf_tex_n {
|
||||
if render3d_st.gltf_tex_paths[i] == path {
|
||||
free(path) # the cache holds its own
|
||||
render3d_st.gltf_tex_refs[i] += 1
|
||||
return render3d_st.gltf_tex_ids[i]
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
var dil = 0
|
||||
if render3d_st.gltf_cutout { dil = 24 }
|
||||
let id = tex_load_ex(render3d_st, path, srgb, dil)
|
||||
if render3d_st.gltf_tex_n < 256 { push(render3d_st.gltf_tex_paths, path); render3d_st.gltf_tex_ids[render3d_st.gltf_tex_n] = id; render3d_st.gltf_tex_refs[render3d_st.gltf_tex_n] = 1; render3d_st.gltf_tex_n += 1 }
|
||||
if render3d_st.gltf_tex_n < 256 { push(render3d_st.gltf_tex_paths, path); render3d_st.gltf_tex_ids[render3d_st.gltf_tex_n] = id; render3d_st.gltf_tex_refs[render3d_st.gltf_tex_n] = 1; render3d_st.gltf_tex_n += 1 } else { free(path) }
|
||||
return id
|
||||
}
|
||||
|
||||
|
|
@ -165,7 +176,9 @@ function gltf_prim(render3d_st: mut Render3dState, p: Val) -> Prim {
|
|||
# Load the mesh of the node called `node_name` from dir/file.
|
||||
function gltf_load(render3d_st: mut Render3dState, dir: string, file: string, node_name: string) -> Model {
|
||||
render3d_st.gltf_dir = dir
|
||||
let text = Fs.read_text(dir + "/" + file)
|
||||
let at = dir + "/" + file
|
||||
let text = Fs.read_text(at)
|
||||
free(at)
|
||||
if text == null { print(`gltf: cannot read {dir}/{file}`); return null }
|
||||
# the last file's tree goes first (it is read only until the next load: ludic.anim's read.ludic),
|
||||
# and the text once parsed - nothing gives memory back on its own (plan 23 of maroon-lake)
|
||||
|
|
@ -174,7 +187,9 @@ function gltf_load(render3d_st: mut Render3dState, dir: string, file: string, no
|
|||
free(text)
|
||||
let buffers = value_get(render3d_st.gltf_doc, "buffers")
|
||||
let bin_uri = value_as_str(value_get(value_at(buffers, 0), "uri"))
|
||||
render3d_st.gltf_bin = file_open(dir + "/" + bin_uri, "rb")
|
||||
let bin_at = dir + "/" + bin_uri
|
||||
render3d_st.gltf_bin = file_open(bin_at, "rb")
|
||||
free(bin_at)
|
||||
if render3d_st.gltf_bin == null { print(`gltf: cannot open {bin_uri}`); return null }
|
||||
let nodes = value_get(render3d_st.gltf_doc, "nodes")
|
||||
var mesh_idx = -1
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# model the first load made, found by comparing the names in place, so asking allocates nothing. For
|
||||
# props a game places while it plays (a fish, a sign an animal leaves); a model whose document is
|
||||
# read after the load (ludic.anim's clips) still goes through gltf_load. A model loaded here is shared
|
||||
# by every actor drawing it, and is never released.
|
||||
# by every actor drawing it; model_release takes it out of the cache as it lets it go.
|
||||
export function gltf_cached(render3d_st: mut Render3dState, dir: string, file: string, node_name: string) -> Model {
|
||||
if render3d_st.gltf_kept == null {
|
||||
render3d_st.gltf_kept = new []Model
|
||||
|
|
@ -21,6 +21,23 @@ export function gltf_cached(render3d_st: mut Render3dState, dir: string, file: s
|
|||
return m
|
||||
}
|
||||
|
||||
# a model let go (model_release) leaves the cache, so the next ask loads it afresh
|
||||
function gltf_cached_forget(render3d_st: mut Render3dState, m: Model) -> void {
|
||||
if render3d_st.gltf_kept == null { return }
|
||||
let kept = render3d_st.gltf_kept
|
||||
let dirs = render3d_st.gltf_kept_dir
|
||||
let files = render3d_st.gltf_kept_file
|
||||
let nodes = render3d_st.gltf_kept_node
|
||||
for i in 0 .. len(kept) {
|
||||
if kept[i] == m {
|
||||
let last = len(kept) - 1
|
||||
kept[i] = kept[last]; dirs[i] = dirs[last]; files[i] = files[last]; nodes[i] = nodes[last]
|
||||
List.pop(kept); List.pop(dirs); List.pop(files); List.pop(nodes)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function gltf_cached_count(render3d_st: Render3dState) -> int {
|
||||
if render3d_st.gltf_kept == null { return 0 }
|
||||
return len(render3d_st.gltf_kept)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ function gltf_tex_unref(render3d_st: mut Render3dState, id: int) -> void {
|
|||
function gltf_tex_forget(render3d_st: mut Render3dState, i: int) -> void {
|
||||
let last = render3d_st.gltf_tex_n - 1
|
||||
let paths = render3d_st.gltf_tex_paths
|
||||
free(paths[i])
|
||||
paths[i] = paths[last]
|
||||
render3d_st.gltf_tex_ids[i] = render3d_st.gltf_tex_ids[last]
|
||||
render3d_st.gltf_tex_refs[i] = render3d_st.gltf_tex_refs[last]
|
||||
|
|
@ -42,6 +43,7 @@ function gltf_mats_forget(render3d_st: mut Render3dState, id: int) -> void {
|
|||
if render3d_st.gltf_mat_diff[k] == id or render3d_st.gltf_mat_nrm[k] == id or render3d_st.gltf_mat_arm[k] == id {
|
||||
let last = render3d_st.gltf_mat_n - 1
|
||||
let names = render3d_st.gltf_mat_names
|
||||
free(names[k])
|
||||
names[k] = names[last]
|
||||
render3d_st.gltf_mat_diff[k] = render3d_st.gltf_mat_diff[last]
|
||||
render3d_st.gltf_mat_nrm[k] = render3d_st.gltf_mat_nrm[last]
|
||||
|
|
@ -54,17 +56,31 @@ function gltf_mats_forget(render3d_st: mut Render3dState, id: int) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
# Let a model go: every primitive's mesh, and its textures once no other model uses them. The
|
||||
# caller says when - no actor may draw the model afterwards (a released model's prims are emptied,
|
||||
# so a stale draw of it draws nothing).
|
||||
# Let a model go: every primitive's mesh, and its textures once no other model uses it; the
|
||||
# primitives, their meshes and the model itself are freed, and a model gltf_cached handed out
|
||||
# leaves its cache. The caller says when: nothing may draw the model, or hold it, afterwards. For
|
||||
# a model gltf_load or gltf_cached made (a procedural one - a card, a blade - owns its parts otherwise).
|
||||
function model_release(render3d_st: mut Render3dState, m: Model) -> void {
|
||||
if m == null or m.prims == null { return }
|
||||
for i in 0 .. len(m.prims) {
|
||||
let pr = m.prims[i]
|
||||
if pr.mesh != null { gpu_mesh_free(render3d_st, pr.mesh); pr.mesh = null }
|
||||
gltf_tex_unref(render3d_st, pr.diff); gltf_tex_unref(render3d_st, pr.nrm); gltf_tex_unref(render3d_st, pr.arm)
|
||||
pr.diff = 0; pr.nrm = 0; pr.arm = 0
|
||||
if m == null { return }
|
||||
gltf_cached_forget(render3d_st, m)
|
||||
if m.prims != null {
|
||||
for i in 0 .. len(m.prims) {
|
||||
let pr = m.prims[i]
|
||||
gltf_tex_unref(render3d_st, pr.diff); gltf_tex_unref(render3d_st, pr.nrm); gltf_tex_unref(render3d_st, pr.arm)
|
||||
if pr.mesh != null { mesh_release(render3d_st, pr.mesh) }
|
||||
if pr.name != null { free(pr.name) }
|
||||
free(pr)
|
||||
}
|
||||
free(m.prims)
|
||||
}
|
||||
let prims = m.prims
|
||||
while len(prims) > 0 { List.pop(prims) }
|
||||
skin_free(m.skin)
|
||||
free(m)
|
||||
}
|
||||
|
||||
# a mesh and what it holds: its buffers (their handles given back), its layout and buffer lists
|
||||
function mesh_release(render3d_st: mut Render3dState, me: Mesh) -> void {
|
||||
gpu_mesh_free(render3d_st, me)
|
||||
if me.attrs != null { free(me.attrs) }
|
||||
if me.vbufs != null { free(me.vbufs) }
|
||||
free(me)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -599,7 +599,9 @@ function gpu_tex_free(render3d_st: mut Render3dState, tex: int) -> void {
|
|||
if tex == 0 { return }
|
||||
let ids = gpu_tmp(render3d_st)
|
||||
ids[0] = tex
|
||||
gvk_flush(render3d_st); if tex < len(render3d_st.gvk_tex_image) { gvk_tex_release(render3d_st, tex) }
|
||||
gvk_flush(render3d_st)
|
||||
gvk_tex_give_back(render3d_st, tex)
|
||||
tex_note_forget(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 render3d_st.gpu_bound_2d == tex { render3d_st.gpu_bound_2d = 0 }
|
||||
let o = gpu_tx_at(render3d_st, tex)
|
||||
|
|
@ -699,7 +701,8 @@ function gpu_fb_free(render3d_st: mut Render3dState, fb: int) -> void {
|
|||
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 } }
|
||||
}
|
||||
function gpu_rb_free(render3d_st: mut Render3dState, rb: int) -> void {
|
||||
gvk_flush(render3d_st); if rb > 0 and rb < len(render3d_st.gvk_tex_image) { gvk_tex_release(render3d_st, rb) }
|
||||
gvk_flush(render3d_st)
|
||||
gvk_tex_give_back(render3d_st, rb)
|
||||
}
|
||||
function gpu_viewport(render3d_st: mut Render3dState, x: int, y: int, w: int, h: int) -> void { gvk_viewport(render3d_st, x, y, w, h) }
|
||||
|
||||
|
|
|
|||
|
|
@ -1460,10 +1460,10 @@ function gvk_program_new(render3d_st: mut Render3dState, vs: string, fs: string,
|
|||
|
||||
function gvk_mesh_free(render3d_st: mut Render3dState, m: Mesh) -> void {
|
||||
if m == null { return }
|
||||
if m.vbufs != null { for i in 0 .. m.n_vbufs { if m.vbufs[i] > 0 { gvk_buf_release(render3d_st, m.vbufs[i]) } } }
|
||||
if m.vbufs != null { for i in 0 .. m.n_vbufs { if m.vbufs[i] > 0 { gvk_buf_delete(render3d_st, m.vbufs[i]) } } }
|
||||
m.n_vbufs = 0
|
||||
m.vbo = 0
|
||||
if m.ebo > 0 { gvk_buf_release(render3d_st, m.ebo); m.ebo = 0 }
|
||||
if m.ebo > 0 { gvk_buf_delete(render3d_st, m.ebo); m.ebo = 0 }
|
||||
}
|
||||
|
||||
function gvk_scissor(render3d_st: mut Render3dState, x: int, y: int, w: int, h: int) -> void {
|
||||
|
|
|
|||
|
|
@ -57,6 +57,14 @@ function gvk_channel_bytes(ifmt: int) -> int {
|
|||
# renderer asks for mipmaps after the upload; an image made without pixels is a target and gets
|
||||
# one level. Every level sits in SHADER_READ_ONLY_OPTIMAL between uses.
|
||||
|
||||
# a texture freed for good: its image goes, and its id is handed out again by gvk_tex_new - once, and
|
||||
# only if it held an image, so a texture freed twice cannot give two new ones the same id
|
||||
function gvk_tex_give_back(render3d_st: mut Render3dState, tex: int) -> void {
|
||||
if tex <= 0 or tex >= len(render3d_st.gvk_tex_image) or render3d_st.gvk_tex_image[tex] == 0 { return }
|
||||
gvk_tex_release(render3d_st, tex)
|
||||
if render3d_st.gvk_tex_spare == null { render3d_st.gvk_tex_spare = new []int }
|
||||
push(render3d_st.gvk_tex_spare, tex)
|
||||
}
|
||||
function gvk_tex_new(render3d_st: mut Render3dState) -> int {
|
||||
let zero: long = 0
|
||||
if render3d_st.gvk_tex_image == null {
|
||||
|
|
@ -73,6 +81,18 @@ function gvk_tex_new(render3d_st: mut Render3dState) -> int {
|
|||
push(render3d_st.gvk_tex_image, zero); push(render3d_st.gvk_tex_view, zero); push(render3d_st.gvk_tex_mem, zero)
|
||||
push(render3d_st.gvk_tex_levels, 0); push(render3d_st.gvk_tex_layers, 0); push(render3d_st.gvk_tex_vkfmt, 0)
|
||||
}
|
||||
# an id a freed texture gave back first: its generation goes on counting, so every cache keyed by
|
||||
# (id, generation) - views, sets, samplers - tells the new texture from the old
|
||||
if render3d_st.gvk_tex_spare != null and len(render3d_st.gvk_tex_spare) > 0 {
|
||||
let sp = render3d_st.gvk_tex_spare
|
||||
let t = sp[len(sp) - 1]
|
||||
List.pop(sp)
|
||||
render3d_st.gvk_tex_image[t] = zero; render3d_st.gvk_tex_view[t] = zero; render3d_st.gvk_tex_mem[t] = zero
|
||||
render3d_st.gvk_tex_levels[t] = 0; render3d_st.gvk_tex_layers[t] = 0; render3d_st.gvk_tex_vkfmt[t] = 0
|
||||
render3d_st.gvk_tex_dims_w[t] = 0; render3d_st.gvk_tex_dims_h[t] = 0; render3d_st.gvk_tex_glfmt[t] = 0; render3d_st.gvk_tex_array[t] = 0
|
||||
render3d_st.gvk_tex_smp_sig[t] = 0; render3d_st.gvk_tex_smp[t] = zero; render3d_st.gvk_tex_samples[t] = 0
|
||||
return t
|
||||
}
|
||||
push(render3d_st.gvk_tex_image, zero); push(render3d_st.gvk_tex_view, zero); push(render3d_st.gvk_tex_mem, zero)
|
||||
push(render3d_st.gvk_tex_levels, 0); push(render3d_st.gvk_tex_layers, 0); push(render3d_st.gvk_tex_vkfmt, 0)
|
||||
push(render3d_st.gvk_tex_dims_w, 0); push(render3d_st.gvk_tex_dims_h, 0)
|
||||
|
|
@ -605,10 +625,29 @@ function gvk_buf_new(render3d_st: mut Render3dState) -> int {
|
|||
# handle 0 is "no buffer", as it is on OpenGL
|
||||
push(render3d_st.gvk_buf, zero); push(render3d_st.gvk_buf_mem, zero); push(render3d_st.gvk_buf_size, 0); push(render3d_st.gvk_buf_map, null); push(render3d_st.gvk_buf_used, 0)
|
||||
}
|
||||
# a handle a deleted buffer gave back first (gvk_buf_delete)
|
||||
if render3d_st.gvk_buf_spare != null and len(render3d_st.gvk_buf_spare) > 0 {
|
||||
let sp = render3d_st.gvk_buf_spare
|
||||
let b = sp[len(sp) - 1]
|
||||
List.pop(sp)
|
||||
return b
|
||||
}
|
||||
push(render3d_st.gvk_buf, zero); push(render3d_st.gvk_buf_mem, zero); push(render3d_st.gvk_buf_size, 0); push(render3d_st.gvk_buf_map, null); push(render3d_st.gvk_buf_used, 0)
|
||||
return len(render3d_st.gvk_buf) - 1
|
||||
}
|
||||
|
||||
# a buffer done with for good (a mesh freed): its storage goes, and its handle is handed out again
|
||||
# by gvk_buf_new. gvk_buf_release alone keeps the handle, for a caller that fills it again.
|
||||
function gvk_buf_delete(render3d_st: mut Render3dState, b: int) -> void {
|
||||
if b <= 0 or b >= len(render3d_st.gvk_buf) { return }
|
||||
gvk_buf_release(render3d_st, b)
|
||||
if render3d_st.gvk_buf_gpu != null and b < len(render3d_st.gvk_buf_gpu) { render3d_st.gvk_buf_gpu[b] = 0 }
|
||||
if render3d_st.gvk_buf_spare == null { render3d_st.gvk_buf_spare = new []int }
|
||||
let sp = render3d_st.gvk_buf_spare
|
||||
for i in 0 .. len(sp) { if sp[i] == b { return } }
|
||||
push(sp, b)
|
||||
}
|
||||
|
||||
function gvk_buf_release(render3d_st: mut Render3dState, b: int) -> void {
|
||||
if render3d_st.gvk_buf[b] == 0 { return }
|
||||
let zero: long = 0
|
||||
|
|
|
|||
|
|
@ -227,3 +227,21 @@ function skin_clone(src: Skin) -> Skin {
|
|||
skin_pose(sk)
|
||||
return sk
|
||||
}
|
||||
|
||||
# A skin loaded with a model, let go with it (model_release): every array it owns, its joints' names
|
||||
# and itself. The views into gmat, inv_bind and bones are slices of those, and freeing a view would
|
||||
# free its owner's data again, so only the lists that hold them go (a few bytes a joint stay).
|
||||
function skin_free(sk: Skin) -> void {
|
||||
if sk == null { return }
|
||||
if sk.names != null {
|
||||
for i in 0 .. len(sk.names) { if sk.names[i] != null and len(sk.names[i]) > 0 { free(sk.names[i]) } }
|
||||
free(sk.names)
|
||||
}
|
||||
free(sk.par); free(sk.walk); free(sk.rest_t); free(sk.rest_r); free(sk.rest_s); free(sk.rest_g)
|
||||
free(sk.pose_r); free(sk.pose_t); free(sk.gmat); free(sk.joints); free(sk.inv_bind); free(sk.bones)
|
||||
free(sk.tmp_l); free(sk.tmp_q); free(sk.tmp_a); free(sk.tmp_b); free(sk.tmp_c); free(sk.tmp_v)
|
||||
if sk.gmat_v != null { free(sk.gmat_v) }
|
||||
if sk.inv_v != null { free(sk.inv_v) }
|
||||
if sk.bones_v != null { free(sk.bones_v) }
|
||||
free(sk)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -284,22 +284,23 @@ function tex_load_ex(render3d_st: mut Render3dState, path: pointer, srgb: bool,
|
|||
return id
|
||||
}
|
||||
# the size each loaded texture was, by id: a nine-slice or a UI image asks
|
||||
# a texture's size, by its id: a slot per id (ids are reused), not a list that grows by every load
|
||||
function tex_note_size(render3d_st: mut Render3dState, id: int) -> void {
|
||||
push(render3d_st.tex_size_ids, id)
|
||||
push(render3d_st.tex_size_ws, render3d_st.tex_w)
|
||||
push(render3d_st.tex_size_hs, render3d_st.tex_h)
|
||||
if id <= 0 { return }
|
||||
while len(render3d_st.tex_size_ws) <= id { push(render3d_st.tex_size_ws, 0); push(render3d_st.tex_size_hs, 0) }
|
||||
render3d_st.tex_size_ws[id] = render3d_st.tex_w
|
||||
render3d_st.tex_size_hs[id] = render3d_st.tex_h
|
||||
}
|
||||
function tex_note_forget(render3d_st: mut Render3dState, id: int) -> void {
|
||||
if id > 0 and id < len(render3d_st.tex_size_ws) { render3d_st.tex_size_ws[id] = 0; render3d_st.tex_size_hs[id] = 0 }
|
||||
}
|
||||
function tex_width(render3d_st: Render3dState, id: int) -> int {
|
||||
for i in 0 .. len(render3d_st.tex_size_ids) {
|
||||
if render3d_st.tex_size_ids[i] == id { return render3d_st.tex_size_ws[i] }
|
||||
}
|
||||
return 0
|
||||
if id <= 0 or id >= len(render3d_st.tex_size_ws) { return 0 }
|
||||
return render3d_st.tex_size_ws[id]
|
||||
}
|
||||
function tex_height(render3d_st: Render3dState, id: int) -> int {
|
||||
for i in 0 .. len(render3d_st.tex_size_ids) {
|
||||
if render3d_st.tex_size_ids[i] == id { return render3d_st.tex_size_hs[i] }
|
||||
}
|
||||
return 0
|
||||
if id <= 0 or id >= len(render3d_st.tex_size_hs) { return 0 }
|
||||
return render3d_st.tex_size_hs[id]
|
||||
}
|
||||
|
||||
# A small solid-colour fallback texture (linear rgb 0..255), for missing maps.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue