Prevent mem leaks with destructors for all GPU objects and other measures.

This commit is contained in:
Alberto Torres 2024-09-13 02:37:02 +02:00
parent 52a4c00ffe
commit 8075edf619
14 changed files with 186 additions and 132 deletions

View file

@ -164,15 +164,15 @@ proc set_attachments(self: Framebuffer; layer, mipmap_level: int) =
if tex.tex_type == Tex2DArray:
assert layer >= 0, "Texture array layer must be specified"
glFramebufferTextureLayer(GL_FRAMEBUFFER, attachments[i],
tex.storage.tex, mipmap_level.GLint, layer.GLint)
tex.storage.tex.GLuint, mipmap_level.GLint, layer.GLint)
elif tex.tex_type == TexCube:
assert layer >= 0, "Texture cube side must be specified"
glFramebufferTexture2D(GL_FRAMEBUFFER, attachments[i],
(GL_TEXTURE_CUBE_MAP_POSITIVE_X.int + layer).GLenum,
tex.storage.tex, mipmap_level.GLint)
tex.storage.tex.GLuint, mipmap_level.GLint)
elif self.current_mipmap_level != mipmap_level:
glFramebufferTexture2D(GL_FRAMEBUFFER, attachments[i],
tex.storage.target, tex.storage.tex, mipmap_level.GLint)
tex.storage.target, tex.storage.tex.GLuint, mipmap_level.GLint)
proc enable*(self: Framebuffer, rect = none((int32,int32,int32,int32)),
layer = -1, mipmap_level = 0, mark_textures = true): Framebuffer {.discardable.} =

View file

@ -186,8 +186,8 @@ proc delete_all_shaders*(self: Material, destroy: bool = true) =
self.last_shader = nil
proc destroy*(self: Material) =
for shader in values(self.shaders):
shader.destroy()
self.delete_all_shaders()
self.textures.clear()
var id = 0
@ -389,12 +389,14 @@ proc initShader*(self: Shader, engine: MyouEngine, material: Material,
when not defined(release):
self.vs_code = vs
let vertex_shader = glCreateShader(GL_VERTEX_SHADER)
defer: glDeleteShader(vertex_shader)
# TODO: make a pointer array from the unjoined strings, interleaved with "\n"
# instead of concatenating and converting
var vs2 = vs.cstring
glShaderSource(vertex_shader, 1, cast[cstringArray](addr vs2), nil)
glCompileShader(vertex_shader)
var fragment_shader: GLuint = 0
defer: glDeleteShader(fragment_shader)
template fragment:string = fragment_lines.join("\n")
if has_fragment_shader:
fragment_shader = glCreateShader(GL_FRAGMENT_SHADER)
@ -404,6 +406,10 @@ proc initShader*(self: Shader, engine: MyouEngine, material: Material,
glShaderSource(fragment_shader, 1, cast[cstringArray](addr fragment2), nil)
glCompileShader(fragment_shader)
let prog = glCreateProgram()
defer:
if prog != self.program.GLuint:
echo "deleting program"
glDeleteProgram(prog)
glAttachShader(prog, vertex_shader)
if fragment_shader != 0:
glAttachShader(prog, fragment_shader)
@ -425,7 +431,6 @@ proc initShader*(self: Shader, engine: MyouEngine, material: Material,
echo vs.add_line_numbers(1)
let error_msg = dedent &"""Error compiling vertex shader of material {material.name}
{get_shader_info_log(vertex_shader)}"""
glDeleteShader(vertex_shader)
console_error(error_msg)
return
if fragment_shader != 0:
@ -435,7 +440,6 @@ proc initShader*(self: Shader, engine: MyouEngine, material: Material,
let error_msg = &"Error compiling fragment shader of material {material.name}\n{gl_log}"
# console_error fragment
let lines = fragment.split("\n")
glDeleteShader(fragment_shader)
if "ERROR: 0:" in gl_log:
# Show engine for first error
let line = try: error_msg.split(":")[2].parseInt except: 0
@ -463,9 +467,6 @@ proc initShader*(self: Shader, engine: MyouEngine, material: Material,
# console_error fragment
console_error("================")
console_error error_msg
glDeleteProgram(prog)
glDeleteShader(vertex_shader)
glDeleteShader(fragment_shader)
return
self.camera_render_ubo_index = glGetUniformBlockIndex(prog, "CameraRenderUniform")
@ -517,12 +518,13 @@ proc initShader*(self: Shader, engine: MyouEngine, material: Material,
assert self.texture_locations.len + self.cubemap_locations.len +
extra_location_count <= self.engine.renderer.max_textures
self.program = prog
self.program = prog.GPUProgram
proc use*(self: Shader): GLuint {.inline,discardable.} =
glUseProgram(self.program)
return self.program
glUseProgram(self.program.GLuint)
return self.program.GLuint
proc destroy*(self: Shader) =
glDeleteProgram(self.program)
self.program = 0.GPUProgram

View file

@ -255,23 +255,22 @@ proc draw_mesh*(self: RenderManager, mesh: Mesh, mesh2world: Mat4, cam_data: Ren
# TODO: Also check with cam_data.clipping_plane!
# TODO: Select alternative mesh / LoD
var amesh = mesh
var amesh {.cursor.} = mesh
if not (amesh.data != nil and amesh.data.loaded):
return
self.set_flip_normals mesh.flip
let data = amesh.data
# for ubo in self.bound_ubos:
# if ubo != nil:
# ubo.unbind()
# self.next_ubo = 0
let data {.cursor.} = amesh.data
# unbindAllTextures()
# Main routine for each submesh
# (vao may be null but that's handled later)
for submesh_idx, vao in data.vaos:
if vao == 0:
# vaos have a destructor, and `pairs` copied the value,
# so we either use `mpairs` or keep track of submesh_idx separately.
var submesh_idx = -1
for vao in data.vaos:
submesh_idx.inc
if vao.GLuint == 0.GLuint:
continue
if not (pass == -1 or mesh.passes[submesh_idx] == pass):
@ -282,10 +281,10 @@ proc draw_mesh*(self: RenderManager, mesh: Mesh, mesh2world: Mat4, cam_data: Ren
else:
material_override
let shader = mat.get_shader(mesh)
if shader.program == 0:
let shader {.cursor.} = mat.get_shader(mesh)
let program = shader.use()
if program == 0:
continue
shader.use()
self.set_cull_face(not mat.double_sided)
var ubos_to_bind: seq[UBO]
@ -313,7 +312,7 @@ proc draw_mesh*(self: RenderManager, mesh: Mesh, mesh2world: Mat4, cam_data: Ren
# TODO: move this UBO, consolidate with cameradata
# TODO: update only in draw_viewport
if shader.camera_render_ubo_index.is_valid:
let ubo = self.camera_render_ubo
let ubo {.cursor.} = self.camera_render_ubo
ubos_to_bind.add ubo
ubo_indices.add shader.camera_render_ubo_index
ubo.storage(CameraRenderUniform)[0] = CameraRenderUniform(
@ -327,7 +326,7 @@ proc draw_mesh*(self: RenderManager, mesh: Mesh, mesh2world: Mat4, cam_data: Ren
ubos_to_update.add ubo
if shader.object_ubo_index.is_valid:
let ubo = mesh.object_ubo
let ubo {.cursor.} = mesh.object_ubo
ubos_to_bind.add ubo
ubo_indices.add shader.object_ubo_index
const diag = vec3(1).normalize
@ -344,13 +343,13 @@ proc draw_mesh*(self: RenderManager, mesh: Mesh, mesh2world: Mat4, cam_data: Ren
# UBOs
for i,idx in shader.ubo_indices:
let ubo = shader.ubos[i]
let ubo {.cursor.} = shader.ubos[i]
ubos_to_bind.add ubo
ubo_indices.add idx
ubos_to_bind.bind_all()
for i,ubo in ubos_to_bind:
ubo.set_prog_index(shader.program, ubo_indices[i])
ubo.set_prog_index(program, ubo_indices[i])
for ubo in ubos_to_update:
ubo.update()
@ -364,8 +363,9 @@ proc draw_mesh*(self: RenderManager, mesh: Mesh, mesh2world: Mat4, cam_data: Ren
textures_to_bind.add texture
else:
textures_to_bind.add self.blank_texture
assert texture_locations.len == textures_to_bind.len
let scene = mesh.scene
let scene {.cursor.} = mesh.scene
if scene != nil:
let loc = shader.shadowmap_location
let fb = scene.shadow_maps
@ -389,9 +389,9 @@ proc draw_mesh*(self: RenderManager, mesh: Mesh, mesh2world: Mat4, cam_data: Ren
bind_all(textures_to_bind, texture_locations)
let index_buffer = data.index_buffers[submesh_idx]
let index_buffer = data.index_buffers[submesh_idx].GLuint
let num_indices = data.num_indices[submesh_idx]
glBindVertexArray(vao)
glBindVertexArray(vao.GLuint)
if not data.use_tf:
if index_buffer != 0:
glDrawElements(data.draw_method.GLenum, num_indices.GLsizei, data.index_types[submesh_idx].GLenum, cast[pointer](0))
@ -399,7 +399,7 @@ proc draw_mesh*(self: RenderManager, mesh: Mesh, mesh2world: Mat4, cam_data: Ren
glDrawArrays(data.draw_method.GLenum, 0, num_indices)
else:
assert index_buffer == 0, "Loopback transform feedback is not compatible with indexed meshes"
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, data.tf_vbos[1][submesh_idx])
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, data.tf_vbos[1][submesh_idx].GLuint)
glBeginTransformFeedback(data.draw_method.GLenum)
glDrawArrays(data.draw_method.GLenum, 0, num_indices)
glEndTransformFeedback()

View file

@ -194,11 +194,13 @@ proc newTextureStorage*(ttype: TextureType, width, height, depth: int, format: T
# of Depth_u24_s8: GL_UNSIGNED_INT
ts.layer = 0
ts.tile_size = vec2(1,1)
glGenTextures(1, addr ts.tex)
var tex: GLuint
glGenTextures(1, addr tex)
ts.tex = tex.GPUTexture
return ts
proc preallocate(self: Texture) =
let ts = self.storage
let ts {.cursor.} = self.storage
case self.tex_type:
of Tex2D:
# TODO: only do this if necessary
@ -232,9 +234,7 @@ proc preallocate(self: Texture) =
discard
proc freeTextureStorage(self: Texture) =
if self.storage != nil:
glDeleteTextures(1, addr self.storage.tex)
self.storage = nil
self.storage = nil
proc bind_it*(texture: Texture, reserve_slot: static[int32] = -1, needs_active_texture: static[bool] = false) =
# TODO: rewrite for using texture arrays and/or bindless
@ -252,7 +252,7 @@ proc bind_it*(texture: Texture, reserve_slot: static[int32] = -1, needs_active_t
if active_texture != bound_unit:
active_texture = bound_unit
glActiveTexture(cast[GLenum](GL_TEXTURE0.uint32 + bound_unit.uint32))
var old_tex = bound_textures[bound_unit]
var old_tex {.cursor.} = bound_textures[bound_unit]
if old_tex != nil:
old_tex.storage.unit = -1
if old_tex.storage.target.uint32 != texture.storage.target.uint32:
@ -260,7 +260,7 @@ proc bind_it*(texture: Texture, reserve_slot: static[int32] = -1, needs_active_t
# if old_tex.sampler_object != 0:
# glBindSampler(cast[GLuint](bound_unit), 0)
bound_textures[bound_unit] = nil
glBindTexture(texture.storage.target, texture.storage.tex)
glBindTexture(texture.storage.target, texture.storage.tex.GLuint)
# if texture.sampler_object != 0:
# glBindSampler(bound_unit.GLuint, texture.sampler_object)
bound_textures[bound_unit] = texture
@ -277,7 +277,7 @@ proc unbind*(texture: Texture) =
if texture.storage.unit == -1:
return
let bound_unit = texture.storage.unit
var old_tex = bound_textures[bound_unit]
var old_tex {.cursor.} = bound_textures[bound_unit]
assert old_tex == texture, "unexpected bound texture"
if old_tex == texture:
bound_textures[bound_unit] = nil
@ -308,19 +308,17 @@ proc bind_all*(textures: seq[Texture], locations: seq[GLint]) =
active_texture = unit
texture.storage.unit = unit
glActiveTexture(cast[GLenum](GL_TEXTURE0.uint32 + unit.uint32))
let old_tex = bound_textures[unit]
let old_tex {.cursor.} = bound_textures[unit]
if old_tex != nil:
old_tex.storage.unit = -1
if old_tex.storage.target != texture.storage.target:
glBindTexture(old_tex.storage.target, 0)
glBindTexture(texture.storage.target, texture.storage.tex)
glBindTexture(texture.storage.target, texture.storage.tex.GLuint)
bound_textures[unit] = texture
inc(next_texture)
if next_texture == max_textures:
next_texture = 0
glUniform1i(locations[i], unit)
proc unbindAllTextures*() =
for tex in bound_textures:
@ -428,7 +426,7 @@ proc newTexture*(engine: MyouEngine, name: string,
when defined(myouUseRenderdoc):
# Note: when we switch to arrays we'll have to store resolutions
# instead of names
glObjectLabel(GL_TEXTURE, self.storage.tex,
glObjectLabel(GL_TEXTURE, self.storage.tex.GLuint,
GLsizei(self.name.len), self.name.cstring)
proc generateMipmap*(self: Texture) =

View file

@ -61,12 +61,14 @@ proc initUBO*[T](self: UBO, renderer: RenderManager, name: string, utype: typede
self.binding_point = -1
renderer.enqueue proc() =
glGenBuffers 1, addr self.buffer
glBindBuffer(GL_UNIFORM_BUFFER, self.buffer)
var buffer: GLuint
glGenBuffers 1, addr buffer
self.buffer = GPUBuffer(buffer)
glBindBuffer(GL_UNIFORM_BUFFER, buffer)
glBufferData(GL_UNIFORM_BUFFER, cast[GLsizeiptr](self.byte_storage.byte_len), nil, GL_DYNAMIC_DRAW)
# glBindBuffer(GL_UNIFORM_BUFFER, 0)
when defined(myouUseRenderdoc):
glObjectLabel(GL_BUFFER, self.buffer, GLsizei(name.len), name.cstring)
glObjectLabel(GL_BUFFER, buffer, GLsizei(name.len), name.cstring)
return self
template storage*[T](self: UBO, utype: typedesc[T]): ArrRef[T] = self.byte_storage.to(T)
@ -83,16 +85,17 @@ proc resize*[T](self: UBO, utype: typedesc[T], count: int) =
# but I'm deleting and unbinding the buffer just in case
self.unbind()
self.byte_storage = newArrRef[T](count).to(byte)
glDeleteBuffers 1, addr self.buffer
glGenBuffers 1, addr self.buffer
glBindBuffer(GL_UNIFORM_BUFFER, self.buffer)
var buffer: GLuint
glGenBuffers 1, addr buffer
self.buffer = GPUBuffer(buffer)
glBindBuffer(GL_UNIFORM_BUFFER, buffer)
# TODO: set nil instead of the actual buffer,
# and avoid using it until it's updated for the first time
glBufferData(GL_UNIFORM_BUFFER, cast[GLsizeiptr](self.byte_storage.len), self.byte_storage.toPointer, GL_DYNAMIC_DRAW)
# glBindBuffer(GL_UNIFORM_BUFFER, 0)
when defined(myouUseRenderdoc):
let name = self.name & "*" # asterisk = it was resized
glObjectLabel(GL_BUFFER, self.buffer, GLsizei(name.len), name.cstring)
glObjectLabel(GL_BUFFER, buffer, GLsizei(name.len), name.cstring)
proc newUBO*[T](renderer: RenderManager, name: string, utype: typedesc[T], count: int = 1): UBO =
var ubo = new UBO
@ -126,9 +129,9 @@ proc bind_it*(self: UBO, rebind = false) =
if next_ubo == self.renderer.max_uniform_buffer_bindings:
next_ubo = 0
self.renderer.next_ubo = next_ubo
glBindBufferBase(GL_UNIFORM_BUFFER, self.binding_point.GLuint, self.buffer)
glBindBufferBase(GL_UNIFORM_BUFFER, self.binding_point.GLuint, self.buffer.GLuint)
elif rebind:
glBindBufferBase(GL_UNIFORM_BUFFER, self.binding_point.GLuint, self.buffer)
glBindBufferBase(GL_UNIFORM_BUFFER, self.binding_point.GLuint, self.buffer.GLuint)
proc unbind*(self: UBO) =
if self.binding_point != -1:
@ -158,7 +161,7 @@ proc bind_all*(ubos: seq[UBO]) =
old_ubo.binding_point = -1
renderer.bound_ubos[next_ubo] = ubo
ubo.binding_point = next_ubo
glBindBufferBase(GL_UNIFORM_BUFFER, next_ubo.GLuint, ubo.buffer)
glBindBufferBase(GL_UNIFORM_BUFFER, next_ubo.GLuint, ubo.buffer.GLuint)
inc(next_ubo)
if next_ubo == maxubos:
next_ubo = 0
@ -179,7 +182,7 @@ proc update*(self: UBO) {.inline.} =
# NOTE: if this doesn't work in webgl, set a flag
let len = self.byte_storage.len
if len != 0:
glBindBuffer(GL_UNIFORM_BUFFER, self.buffer)
glBindBuffer(GL_UNIFORM_BUFFER, self.buffer.GLuint)
glBufferData(GL_UNIFORM_BUFFER, len.GLsizeiptr, self.byte_storage.toPointer, GL_DYNAMIC_DRAW)
# glBindBuffer(GL_UNIFORM_BUFFER, 0)
@ -189,7 +192,7 @@ template is_valid*(block_index: GLuint): bool =
proc destroy*(self: UBO) =
self.unbind()
self.byte_storage = nil
glDeleteBuffers 1, addr self.buffer
self.buffer = 0.GPUBuffer
# TODO: make a function/template/macro that generates GLSL code to declare the UBO from an object

View file

@ -108,6 +108,7 @@ proc loadAsync(self: BlendLoader, callback: proc()) =
self.close()
self.resource = loadUri(self.blend_file_path,
proc(ok, err, data: auto) =
self.resource = nil
if ok:
self.blend_file = openBlendFile(self.blend_file_path, data.data, data.byte_len)
callback()

View file

@ -87,11 +87,10 @@ type
TypeLengths = ptr UncheckedArray[uint16]
FNode* = object
dna: TypeAttrsRef
blocks: FNodesByAddress
blend_file {.cursor.}: BlendFile
# we have to keep a reference to this otherwise it will be GC'd
# TODO: remove the two pointers above to use less memory?
blend_file: BlendFile
# (but only in FNodes not referenced in the original, to avoid cycles)
blend_file_ref: BlendFile
p: pointer
count: uint32
@ -285,8 +284,6 @@ proc openBlendFile*(path: string, data: pointer, len: int): BlendFile =
p: blk.data.addr,
tid: tid,
count: if blk.count == 1: real_count else: blk.count,
dna: type_attrs,
blocks: node_blocks,
blend_file: result)
node_blocks[address] = node
if blk.code[2] == '\0':
@ -294,6 +291,7 @@ proc openBlendFile*(path: string, data: pointer, len: int): BlendFile =
if t notin result.named_blocks:
result.named_blocks[t] = @[]
result.named_blocks[t].add(node)
result.dna = type_attrs
result.blocks = node_blocks
result.type_names = type_names
result.struct_to_type = struct_to_type
@ -375,25 +373,25 @@ template get_fblock_of_node(n: FNode): ptr FBlock64 =
proc contains*(n: FNode, name: string): bool =
if n.p == nil:
return
return name in n.dna[n.tid]
return name in n.blend_file.dna[n.tid]
proc `[]`*(n: FNode, name: string): FNode =
if n.p == nil:
return
assert not n.count.testBit(31), "This is an array, expected index instead of \"" & name & "\""
let a = n.dna[n.tid][name]
let a = n.blend_file.dna[n.tid][name]
if a.atype != Value:
let ptri = cast[ptr uint64](n.p+a.offset)[]
if ptri == 0:
return
when defined(disallow_missing_blocks):
assert ptri in n.blocks, "Missing block pointing to: " & name
if ptri notin n.blocks:
assert ptri in n.blend_file.blocks, "Missing block pointing to: " & name
if ptri notin n.blend_file.blocks:
# This may happen if e.g. the user has deleted an image used by a node
# so we'll just return an invalid node
echo "Missing block pointing to: " & name
return
let blk = n.blocks[ptri]
let blk = n.blend_file.blocks[ptri]
var count: uint32 = a.count
if a.atype == PointerArray:
# the sdna_index of this block is 0, which would give an incorrect struct size
@ -403,13 +401,15 @@ proc `[]`*(n: FNode, name: string): FNode =
count.setBit 31
# use blk.count?
var node = FNode(p: blk.p, tid: a.tid, count: count,
dna: n.dna, blocks: n.blocks, blend_file: n.blend_file)
blend_file: n.blend_file,
blend_file_ref: n.blend_file)
if a.tid == B_void.uint16:
node.tid = n.blend_file.struct_to_type[get_fblock_of_node(node).sdna_index]
return node
else:
return FNode(p: n.p+a.offset, tid: a.tid, count: a.count,
dna: n.dna, blocks: n.blocks, blend_file: n.blend_file)
blend_file: n.blend_file,
blend_file_ref: n.blend_file)
proc `[]`*(n: FNode, index: Natural): FNode =
var count = n.count
@ -419,16 +419,17 @@ proc `[]`*(n: FNode, index: Natural): FNode =
let ptri = cast[ptr UncheckedArray[uint64]](n.p)[index]
if ptri == 0:
return
assert ptri in n.blocks, "Missing block"
let blk = n.blocks[ptri]
assert ptri in n.blend_file.blocks, "Missing block"
let blk = n.blend_file.blocks[ptri]
return FNode(p: blk.p, tid: n.tid, count: 1,
dna: n.dna, blocks: n.blocks, blend_file: n.blend_file)
blend_file: n.blend_file,
blend_file_ref: n.blend_file)
else:
var n = n
let size = if n.tid < BASIC_TYPE_LENGTHS.len:
BASIC_TYPE_LENGTHS[n.tid]
else:
# n.dna[n.tid]["(size)"].offset.int
# n.blend_file.dna[n.tid]["(size)"].offset.int
n.blend_file.type_lengths[n.tid].int
n.p = n.p + index * size
n.count = 1
@ -444,10 +445,9 @@ iterator items*(n: FNode, stride=0): FNode =
if ptri == 0:
yield FNode()
continue
assert ptri in n.blocks, "Missing block"
let blk = n.blocks[ptri]
yield FNode(p: blk.p, tid: n.tid, count: 1,
dna: n.dna, blocks: n.blocks)
assert ptri in n.blend_file.blocks, "Missing block"
let blk = n.blend_file.blocks[ptri]
yield FNode(p: blk.p, tid: n.tid, count: 1)
else:
var n = n
let stride = if stride == 0:
@ -538,7 +538,7 @@ proc `$`*(n: FNode, depth=0, max_depth=3, just_name=false, dump_pointers=false):
let kkstr = `$`(kk, depth, max_depth)
ret.add indent & "[" & $i & "]: " & kkstr
return ret.join "\n"
var attrs = toSeq(n.dna[n.tid].pairs)
var attrs = toSeq(n.blend_file.dna[n.tid].pairs)
try:
let name = n.id.name.str
ret.add indent & "id.name: " & name

View file

@ -168,7 +168,6 @@ proc myou_main_loop*(self: MyouEngine) =
##
## You usually don't need to call this. Use `run <#run,MyouEngine>`_
## instead.
self.renderer.unbind_all_ubos()
when compileOption("threads"):
updateTextureWorkerThreads()
updateLoadableWorkerThreads()

View file

@ -486,6 +486,9 @@ proc remove*(self: GameObject, recursive: bool = true) =
self.scene.remove_object(self, recursive)
proc destroy*(self: GameObject, recursive: bool = true) =
if self.is_mesh and self.get_mesh.data.nonNil:
self.get_mesh.data.gpu_buffers_delete()
self.get_mesh.materials.setLen 0
self.body.destroy()
self.remove(recursive)
if recursive:
@ -494,7 +497,9 @@ proc destroy*(self: GameObject, recursive: bool = true) =
child.destroy(true)
self.engine.objects.del(self.name)
self.object_render_ubo.unbind()
self.object_render_ubo.destroy()
self.object_ubo.unbind()
self.object_ubo.destroy()
proc convert_bone_child_to_bone_parent*(self: GameObject) =
assert self.parent == nil or self.parent.is_armature, "An armature parent is required"

View file

@ -136,8 +136,6 @@ proc configure_shadow*(self: Light,
for ob in self.scene.children:
if not (ob.is_mesh and ob.visible):
continue
echo "adding ", ob.name, " otype ", ob.otype
# discard ob.get_world_matrix
let me = ob.get_mesh
let bb = me.bound_box
let world_dim = (ob.world_matrix * vec4(bb[1] - bb[0], 0.0)).xyz

View file

@ -91,23 +91,14 @@ proc remove*(self: MeshData, ob: Mesh, delete_buffers: bool = true) =
self.users.delete(idx)
idx = self.users.find(ob)
if self.users.len == 0:
# if delete_buffers:
# glBindBuffer(GL_ARRAY_BUFFER, 0)
# for buf in self.vertex_buffers:
# glDeleteBuffers(1, addr buf)
# for buf in self.index_buffers:
# glDeleteBuffers(1, addr buf)
# glBindVertexArray(0)
# for vao in self.vaos:
# glDeleteVertexArrays(1, addr vao)
self.engine.mesh_datas.del(self.hash)
ob.data = nil
proc write_vaos*(self: MeshData) =
for i,vao in self.vaos:
if vao == 0:
if vao.int == 0:
continue
glBindVertexArray(vao)
glBindVertexArray(vao.GLuint)
for vb_attrs in self.vao_specs[i].vbs.mitems:
glBindBuffer(GL_ARRAY_BUFFER, vb_attrs.vb)
for a in vb_attrs.attribs:
@ -117,7 +108,7 @@ proc write_vaos*(self: MeshData) =
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.vao_specs[i].ib)
when defined(myouUseRenderdoc):
let name = &"{self.name} {i}"
glObjectLabel(GL_VERTEX_ARRAY, vao, GLsizei(name.len), name.cstring)
glObjectLabel(GL_VERTEX_ARRAY, vao.GLuint, GLsizei(name.len), name.cstring)
glBindVertexArray(0)
proc gpu_buffers_upload*(self: MeshData) =
@ -130,9 +121,9 @@ proc gpu_buffers_upload*(self: MeshData) =
for idx,va in self.varrays:
if va.len == 0:
self.num_indices.add(0)
self.vertex_buffers.add(0)
self.index_buffers.add(0)
self.vaos.add(0)
self.vertex_buffers.add(0.GPUBuffer)
self.index_buffers.add(0.GPUBuffer)
self.vaos.add(0.GPUVao)
self.vao_specs.add VaoSpec()
continue
var vb: GLuint
@ -143,7 +134,7 @@ proc gpu_buffers_upload*(self: MeshData) =
# TODO: initialize without uploading when we know that the array is just zeroes
glBufferData(GL_ARRAY_BUFFER, buffer_size.GLsizeiptr, addr va[0], GL_DYNAMIC_DRAW)
glBindBuffer(GL_ARRAY_BUFFER, 0)
self.vertex_buffers.add(vb)
self.vertex_buffers.add(vb.GPUBuffer)
var ib: GLuint = 0
var num_indices: Natural
if idx < self.iarrays.len:
@ -167,7 +158,7 @@ proc gpu_buffers_upload*(self: MeshData) =
self.num_indices.add(num_indices.int32)
elif not had_indices:
self.num_indices.add((buffer_size div self.stride).int32)
self.index_buffers.add(ib)
self.index_buffers.add(ib.GPUBuffer)
if self.use_tf:
self.tf_vbos.setLen 2
glGenBuffers(2, addr tf[0])
@ -180,8 +171,8 @@ proc gpu_buffers_upload*(self: MeshData) =
glBindBuffer(GL_ARRAY_BUFFER, tf[1])
glBufferData(GL_ARRAY_BUFFER, tf_buffer_size.GLsizeiptr, nil, GL_DYNAMIC_DRAW)
glBindBuffer(GL_ARRAY_BUFFER, 0)
self.tf_vbos[idx].add tf[0]
self.tf_vbos[idx].add tf[1]
self.tf_vbos[idx].add tf[0].GPUBuffer
self.tf_vbos[idx].add tf[1].GPUBuffer
# If transform feedback is enabled, we create two VAOs that have two
# parts:
# * The regular mesh, which has the same buffers and attributes in each
@ -203,7 +194,7 @@ proc gpu_buffers_upload*(self: MeshData) =
)
var vao: GLuint
glGenVertexArrays(1, addr vao)
self.vaos.add(vao)
self.vaos.add(vao.GPUVao)
if self.use_tf:
var vao_tf = vao_spec
vao_spec.vbs.add VertexBufferAttribs(vb: tf[0], attribs: self.tf_layout)
@ -218,14 +209,15 @@ proc gpu_buffers_upload*(self: MeshData) =
# set loaded to true after that
proc gpu_buffers_delete*(self: MeshData) =
glBindVertexArray(0)
self.vaos = @[]
self.tf_vaos = @[]
self.vao_specs = @[]
self.tf_vao_specs = @[]
glBindBuffer(GL_ARRAY_BUFFER, 0)
self.vertex_buffers = @[]
self.index_buffers = @[]
self.loaded = false
glBindVertexArray(0)
for i in 0 ..< self.vaos.len:
glDeleteVertexArrays(1, addr self.vaos[i])
# TODO! delete individual buffers?
self.vaos = @[]
if self.users.len == 0:
self.engine.mesh_datas.del(self.hash)
@ -233,14 +225,14 @@ proc update_varray*(self: MeshData) =
if self.vertex_buffers.len == 0:
return
for i,va in self.varrays:
glBindBuffer(GL_ARRAY_BUFFER, self.vertex_buffers[i])
glBindBuffer(GL_ARRAY_BUFFER, self.vertex_buffers[i].GLuint)
glBufferSubData(GL_ARRAY_BUFFER, 0.GLintptr, cast[GLsizeiptr](va.bytelen), addr va[0])
proc update_iarray*(self: MeshData) =
if self.index_buffers.len == 0:
return
for i,ia in self.iarrays:
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.index_buffers[i])
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.index_buffers[i].GLuint)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, cast[GLsizeiptr](ia.bytelen), addr ia[0], GL_STATIC_DRAW)
proc clone*(self: MeshData): MeshData =

View file

@ -20,4 +20,49 @@ proc get_program_info_log*(program: GLuint): string =
glGetProgramInfoLog(program, logSize.GLsizei, nil, s.cstring)
return s
let main_thread_id = getThreadID()
template handleError(x: untyped, t: string) =
assert main_thread_id == getThreadID()
try:
x
except:
echo "Error when deleting " & t
echo getCurrentExceptionMsg()
type GPUBuffer* = distinct GLuint
type GPUTexture* = distinct GLuint
type GPUVao* = distinct GLuint
type GPUFramebuffer* = distinct GLuint
type GPURenderbuffer* = distinct GLuint
type GPUProgram* = distinct GLuint
type GPUShader* = distinct GLuint
proc `=destroy`(x: var GPUBuffer) =
if x.GLuint != 0.GLuint:
handleError glDeleteBuffers(1, x.GLuint.addr), "Buffer"
proc `=destroy`(x: var GPUTexture) =
if x.GLuint != 0.GLuint:
handleError glDeleteTextures(1, x.GLuint.addr), "Texture"
proc `=destroy`(x: var GPUVao) =
if x.GLuint != 0.GLuint:
handleError glDeleteVertexArrays(1, x.GLuint.addr), "VertexArray"
proc `=destroy`(x: var GPUFramebuffer) =
if x.GLuint != 0.GLuint:
handleError glDeleteFramebuffers(1, x.GLuint.addr), "Framebuffer"
proc `=destroy`(x: var GPURenderbuffer) =
if x.GLuint != 0.GLuint:
handleError glDeleteRenderbuffers(1, x.GLuint.addr), "Renderbuffer"
proc `=destroy`(x: var GPUProgram) =
if x.GLuint != 0.GLuint:
handleError glDeleteProgram(x.GLuint), "Program"
proc `=destroy`(x: var GPUShader) =
if x.GLuint != 0.GLuint:
handleError glDeleteShader(x.GLuint), "Shader"

View file

@ -343,7 +343,6 @@ proc set_objects_auto_update_matrix*(self: Scene, objects: seq[GameObject], auto
proc destroy*(self: Scene) =
for ob in reversed(self.children):
var ob = ob
ob.destroy(recursive=false)
self.world.destroy()
for mat in self.materials.values:
@ -353,6 +352,17 @@ proc destroy*(self: Scene) =
for ubo in self.lighting_UBOs:
ubo.destroy()
self.engine.scenes.del(self.name)
# bound textures can linger
unbindAllTextures()
# probably none of this is actually necessary
# (needs testing without this)
self.children = @[]
self.auto_updated_children = @[]
self.objects.clear()
self.parents.clear()
self.materials.clear()
self.textures.clear()
self.mesh_passes = @[]
proc enable_render*(self: Scene) =
self.enabled = true
@ -417,9 +427,9 @@ proc calculate_max_lights_and_cubemaps*(self: Scene) =
for _,mat in self.materials:
if mat.ubos.anyIt it in self.lighting_UBOs:
mat.delete_all_shaders
echo &"calculated max lights {self.max_point_lights} {self.max_sun_lights}"
echo &"calculated max shadow maps {self.max_shadow_maps}"
echo &"calculated max cubemaps {self.max_cubemaps}"
# echo &"calculated max lights {self.max_point_lights} {self.max_sun_lights}"
# echo &"calculated max shadow maps {self.max_shadow_maps}"
# echo &"calculated max cubemaps {self.max_cubemaps}"
proc get_lighting_UBOs*(self: Scene): seq[UBO] =
# echo &"getting UBOs {self.max_point_lights} {self.max_sun_lights}"
@ -620,7 +630,7 @@ proc sort_cubemaps*(self: Scene) =
# or share depth buffers?
proc ensure_cubemaps*(self: Scene) =
echo "ensuring cubemaps"
# echo "ensuring cubemaps"
let res = self.cubemap_resolution
if res == 0:
return
@ -650,7 +660,7 @@ proc ensure_cubemaps*(self: Scene) =
)
probe.ubo_index = self.cubemaps.len.int32
self.cubemaps.add(probe.cubemap)
echo &"made {self.cubemaps.len} cubemaps"
# echo &"made {self.cubemaps.len} cubemaps"
proc render_all_cubemaps*(self: Scene, use_roughness_prefiltering: bool, mipmap_shader: Material = nil) =
if self.cubemap_UBO == nil:

View file

@ -167,11 +167,11 @@ type
varrays*: seq[ArrRef[float32]]
iarrays*: seq[ArrRef[uint16]]
loaded*: bool
vertex_buffers*: seq[GLuint] ## private
vertex_buffers*: seq[GPUBuffer] ## private
vertex_starts*: seq[int32] ## private
index_buffers*: seq[GLuint] ## private
index_buffers*: seq[GPUBuffer] ## private
num_indices*: seq[int32] ## private
vaos*: seq[GLuint] ## private
vaos*: seq[GPUVao] ## private
vao_specs*: seq[VaoSpec] ## private
index_types*: seq[DataType] ## private
layout*: AttributeList ## private
@ -183,8 +183,8 @@ type
# load_promise: unknown
# load_resolve: unknown
use_tf*: bool ## private
tf_vbos*: seq[seq[GLuint]] ## private
tf_vaos*: seq[GLuint] ## private
tf_vbos*: seq[seq[GPUBuffer]] ## private
tf_vaos*: seq[GPUVao] ## private
tf_vao_specs*: seq[VaoSpec] ## private
tf_layout*: AttributeList ## private
when defined(myouUseRenderdoc):
@ -504,7 +504,7 @@ type
name*: string
size32*: int
byte_storage*: ArrRef[byte]
buffer*: GLuint
buffer*: GPUBuffer
binding_point*: int32
# needs_update*: bool
@ -555,14 +555,14 @@ type
defines*: Table[string, string]
###
scene*: Scene
scene* {.cursor.}: Scene
shaders*: Table[AttributeList, Shader] ## private
users*: seq[GameObject] ## private
render_scene*: Scene ## private
render_scene* {.cursor.}: Scene ## private
has_texture_list_checked*: bool ## private
shader_library*: string ## private
use_debug_shaders*: bool ## private
last_shader*: Shader ## private
last_shader* {.cursor.}: Shader ## private
feedback_varyings*: seq[string]
@ -570,8 +570,8 @@ type
Shader* = ref object
engine* {.cursor.}: MyouEngine # do we need this?
id*: int
program*: GLuint
material*: Material # do we need this?
program*: GPUProgram
material* {.cursor.}: Material # do we need this?
# TODO: combine in single seq?
ubos*: seq[UBO]
@ -638,7 +638,7 @@ type
TextureStorage* = ref object ## private
unit*: GLint
target*: GLenum
tex*: GLuint
tex*: GPUTexture
iformat*: GLenum
format*, gltype*: GLenum
# index of the first layer (or only layer if not using tiles)
@ -651,7 +651,7 @@ type
name*: string
storage*: TextureStorage ## private
loaded*: bool
last_used_shader*: Shader ## private
last_used_shader* {.cursor.}: Shader ## private
sampler_object*: GLuint ## private
width*, height*, depth*: int
tex_type*: TextureType
@ -915,4 +915,5 @@ template enqueue*(renderer: RenderManager, fun: untyped) =
when not defined(release):
from sugar import dump
export sugar
export sugar.dump