From 4b5770d6bbcb2f26d470871149a59c71025766f8 Mon Sep 17 00:00:00 2001 From: waynemwashuma <94756970+waynemwashuma@users.noreply.github.com> Date: Wed, 17 Sep 2025 01:15:54 +0300 Subject: [PATCH 1/6] Remove `meshAddHook2` component hook --- src/render-webgl/hooks/index.js | 1 - src/render-webgl/hooks/mesh.js | 47 --------------------------------- src/render-webgl/index.js | 1 - src/render-webgl/plugin.js | 6 ++--- 4 files changed, 2 insertions(+), 53 deletions(-) delete mode 100644 src/render-webgl/hooks/index.js delete mode 100644 src/render-webgl/hooks/mesh.js diff --git a/src/render-webgl/hooks/index.js b/src/render-webgl/hooks/index.js deleted file mode 100644 index 5d454702..00000000 --- a/src/render-webgl/hooks/index.js +++ /dev/null @@ -1 +0,0 @@ -export * from './mesh.js' \ No newline at end of file diff --git a/src/render-webgl/hooks/mesh.js b/src/render-webgl/hooks/mesh.js deleted file mode 100644 index 4009884b..00000000 --- a/src/render-webgl/hooks/mesh.js +++ /dev/null @@ -1,47 +0,0 @@ -/** @import { ComponentHook } from "../../ecs/index.js" */ -import { Query, Entity } from '../../ecs/index.js' -import { Assets } from '../../asset/index.js' -import { Mesh, Meshed } from '../../render-core/index.js' -import { createVAO } from '../core/function.js' -import { MainWindow, Windows, Window } from '../../window/index.js' -import { warn } from '../../logger/index.js' -import { MeshCache, AttributeMap } from '../resources/index.js' -import { typeidGeneric } from '../../reflect/index.js' - -// TODO - In the future,use the `AssetAdded` event to build gpu representation instead -// TODO - Queue the meshes instead of trying to add them immediately. -/** - * @type {ComponentHook} - */ -export function meshAddHook2(entity, world) { - - // SAFETY: Component is guaranteed as this is its component hook - const handle = world.get(entity, Meshed) - const attributeMap = world.getResource(AttributeMap) - - /** @type {Assets} */ - const meshes = world.getResourceByTypeId(typeidGeneric(Assets, [Mesh])) - - /** @type {MeshCache} */ - const meshcache = world.getResource(MeshCache) - const windows = new Query(world, [Entity, Window, MainWindow]) - const canvases = world.getResource(Windows) - - const window = windows.single() - - if (!window) return warn('Please define the main window for rendering.') - - const canvas = canvases.getWindow(window[0]) - - if(!canvas) return - - const gl = canvas.getContext('webgl2') - - if (!gl) return warn('WebGL 2.0 context is not created or is lost.') - if (meshcache.has(handle.handle.id())) return - - const mesh = meshes.get(handle.handle) - const vao = createVAO(gl, mesh, attributeMap) - - meshcache.set(handle.handle.id(), vao) -} \ No newline at end of file diff --git a/src/render-webgl/index.js b/src/render-webgl/index.js index fe5b2085..6fd451a1 100644 --- a/src/render-webgl/index.js +++ b/src/render-webgl/index.js @@ -1,4 +1,3 @@ -export * from './hooks/index.js' export * from './core/index.js' export * from './resources/index.js' export * from './shaders/index.js' diff --git a/src/render-webgl/plugin.js b/src/render-webgl/plugin.js index 80575b52..2d1c72b8 100644 --- a/src/render-webgl/plugin.js +++ b/src/render-webgl/plugin.js @@ -1,9 +1,8 @@ import { App, AppSchedule, Plugin } from '../app/index.js' -import { ComponentHooks, Entity, Query, World } from '../ecs/index.js' +import { Entity, Query, World } from '../ecs/index.js' import { warn } from '../logger/index.js' -import { MeshAttribute, ProgramCache, BasicMaterial, Meshed } from '../render-core/index.js' +import { MeshAttribute, ProgramCache, BasicMaterial } from '../render-core/index.js' import { MainWindow, Window, Windows } from '../window/index.js' -import { meshAddHook2 } from './hooks/index.js' import { WebglMaterialPlugin } from './plugins/index.js' import { AttributeMap, ClearColor, MeshCache, UBOCache, WebglProgramCache } from './resources/index.js' import { basicMaterial3DFragment, basicMaterial3DVertex } from './shaders/index.js' @@ -18,7 +17,6 @@ export class WebglRendererPlugin extends Plugin { registerDefaultAttributeLocs(attribute) app - .setComponentHooks(Meshed, new ComponentHooks(meshAddHook2)) .setResource(new ProgramCache()) .setResource(new MeshCache()) .setResource(new UBOCache()) From 63b37911a8a4efa893a60e2c400a070280a66ef1 Mon Sep 17 00:00:00 2001 From: waynemwashuma <94756970+waynemwashuma@users.noreply.github.com> Date: Wed, 17 Sep 2025 01:21:57 +0300 Subject: [PATCH 2/6] Add `queueMeshes` system --- src/render-webgl/systems/index.js | 79 +++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 5 deletions(-) diff --git a/src/render-webgl/systems/index.js b/src/render-webgl/systems/index.js index 0e37aabc..a70e2d5f 100644 --- a/src/render-webgl/systems/index.js +++ b/src/render-webgl/systems/index.js @@ -1,16 +1,19 @@ -/** @import {SystemFunc} from '../../ecs/index.js' */ +/** @import {SystemFunc, World} from '../../ecs/index.js' */ /** @import { Constructor } from '../../reflect/index.js' */ -/** @import {UniformBind} from '../../render-core/index.js' */ +/** @import {UniformBind } from '../../render-core/index.js' */ +/** @import { AssetId } from '../../asset/index.js' */ + import { Entity, Query } from '../../ecs/index.js' import { assert, warn } from '../../logger/index.js' import { Windows, MainWindow, Window } from '../../window/index.js' import { typeid, typeidGeneric } from '../../reflect/index.js' import { AttributeMap, ClearColor, MeshCache, UBOCache, WebglProgramCache } from '../resources/index.js' -import { Camera, Material, Mesh, RenderLists3D, ShaderStage } from '../../render-core/index.js' -import { WebglRenderPipeline, createShader, validateShader, createProgram, validateProgram } from '../core/index.js' -import { Assets } from '../../asset/index.js' +import { Camera, Material, Mesh, MeshAdded, MeshModified, MeshDropped, RenderLists3D, ShaderStage } from '../../render-core/index.js' +import { WebglRenderPipeline, createShader, validateShader, createProgram, validateProgram, createVAO } from '../core/index.js' +import { AssetEvent, Assets } from '../../asset/index.js' import { GlobalTransform3D } from '../../transform/index.js' import { Affine3 } from '../../math/index.js' +import { Events } from '../../event/index.js' /** * @template T @@ -182,4 +185,70 @@ export function genRender(type) { } }) } +} + +/** + * @param {World} world + */ +export function queueMeshes(world) { + + /** @type {Events} */ + const added = world.getResourceByTypeId(typeidGeneric(Events, [MeshAdded])) + + /** @type {Events} */ + const modified = world.getResourceByTypeId(typeidGeneric(Events, [MeshModified])) + + /** @type {Assets} */ + const meshes = world.getResourceByTypeId(typeidGeneric(Assets, [Mesh])) + + /** @type {MeshCache} */ + const cache = world.getResource(MeshCache) + const attributes = world.getResource(AttributeMap) + const windows = new Query(world, [Entity, Window, MainWindow]) + const canvases = world.getResource(Windows) + + // TODO: Find a way to decouple to allow multi-window support. + const window = windows.single() + + if (!window) return warn('Please define the main window for rendering.') + + const canvas = canvases.getWindow(window[0]) + + if (!canvas) return + + const gl = canvas.getContext('webgl2') + + if (!gl) return warn('WebGL 2.0 context is not created or is lost.') + + added.each((event) => { + const { id } = event.data + + updateMesh(gl, id, meshes, cache, attributes) + }) + + modified.each((event) => { + const { id } = event.data + + // SAFETY:Guaranteed to exist as it was added before modified. + const vao = /**@type {WebGLVertexArrayObject} */ (cache.get(id)) + gl.deleteVertexArray(vao) + updateMesh(gl, id, meshes, cache, attributes) + }) +} + +/** + * @param {WebGL2RenderingContext} gl + * @param {AssetId} id + * @param {Assets} meshes + * @param {MeshCache} cache + * @param {AttributeMap} attributeMap + */ +function updateMesh(gl, id, meshes, cache, attributeMap) { + const mesh = meshes.getByAssetId(id) + + assert(mesh, 'Internal error: Mesh should have been loaded before sending to gpu') + + const vao = createVAO(gl, mesh, attributeMap) + + cache.set(id, vao) } \ No newline at end of file From 43b090d56184a326208ff12d309b6a430e29d97e Mon Sep 17 00:00:00 2001 From: waynemwashuma <94756970+waynemwashuma@users.noreply.github.com> Date: Wed, 17 Sep 2025 01:22:43 +0300 Subject: [PATCH 3/6] Add `disposeDroppedMeshes` system --- src/render-webgl/systems/index.js | 37 +++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/render-webgl/systems/index.js b/src/render-webgl/systems/index.js index a70e2d5f..e82534b3 100644 --- a/src/render-webgl/systems/index.js +++ b/src/render-webgl/systems/index.js @@ -236,6 +236,43 @@ export function queueMeshes(world) { }) } +/** + * @param {World} world + */ +export function disposeDroppedMeshes(world) { + + /** @type {Events} */ + const dropped = world.getResourceByTypeId(typeidGeneric(Events, [MeshDropped])) + + /**@type {MeshCache} */ + const cache = world.getResource(MeshCache) + const windows = new Query(world, [Entity, Window, MainWindow]) + const canvases = world.getResource(Windows) + + // TODO: Find a way to decouple to allow multi-window support. + const window = windows.single() + + if (!window) return warn('Please define the main window for rendering.') + + const canvas = canvases.getWindow(window[0]) + + if (!canvas) return + + const gl = canvas.getContext('webgl2') + + if (!gl) return warn('WebGL 2.0 context is not created or is lost.') + dropped.each((drop) => { + const { id } = drop.data + const vao = cache.get(id) + + if(!vao) return + + gl.deleteVertexArray(vao) + + cache.delete(id) + }) +} + /** * @param {WebGL2RenderingContext} gl * @param {AssetId} id From 8def6952230c8858f32cc0e1ba9f2cd97578e997 Mon Sep 17 00:00:00 2001 From: waynemwashuma <94756970+waynemwashuma@users.noreply.github.com> Date: Wed, 17 Sep 2025 01:30:21 +0300 Subject: [PATCH 4/6] Fixed `MeshCache` to extend `Map` correctly --- src/render-webgl/resources/meshcache.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/render-webgl/resources/meshcache.js b/src/render-webgl/resources/meshcache.js index c9636db6..beadb109 100644 --- a/src/render-webgl/resources/meshcache.js +++ b/src/render-webgl/resources/meshcache.js @@ -1,5 +1,6 @@ +/** @import { AssetId } from '../../asset/index.js' */ /** * @template T - * @type {Map} + * @augments {Map} */ export class MeshCache extends Map {} \ No newline at end of file From bd9e469850fb9831fb8d2f925764b5715c08df6c Mon Sep 17 00:00:00 2001 From: waynemwashuma <94756970+waynemwashuma@users.noreply.github.com> Date: Wed, 17 Sep 2025 01:31:14 +0300 Subject: [PATCH 5/6] Registered new systems into `WebglRendererPlugin` --- src/render-webgl/plugin.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/render-webgl/plugin.js b/src/render-webgl/plugin.js index 2d1c72b8..5f82811c 100644 --- a/src/render-webgl/plugin.js +++ b/src/render-webgl/plugin.js @@ -6,6 +6,7 @@ import { MainWindow, Window, Windows } from '../window/index.js' import { WebglMaterialPlugin } from './plugins/index.js' import { AttributeMap, ClearColor, MeshCache, UBOCache, WebglProgramCache } from './resources/index.js' import { basicMaterial3DFragment, basicMaterial3DVertex } from './shaders/index.js' +import { disposeDroppedMeshes, queueMeshes } from './systems/index.js' export class WebglRendererPlugin extends Plugin { @@ -29,6 +30,8 @@ export class WebglRendererPlugin extends Plugin { vertex3d: basicMaterial3DVertex, fragment3d: basicMaterial3DFragment })) + .registerSystem(AppSchedule.Update, disposeDroppedMeshes) + .registerSystem(AppSchedule.Update, queueMeshes) } } From db9b7a1f6bdac8e399d37c5b98d5dcaa09e60082 Mon Sep 17 00:00:00 2001 From: waynemwashuma <94756970+waynemwashuma@users.noreply.github.com> Date: Wed, 17 Sep 2025 01:32:59 +0300 Subject: [PATCH 6/6] Lint files --- src/render-webgl/systems/index.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/render-webgl/systems/index.js b/src/render-webgl/systems/index.js index e82534b3..d28d2d92 100644 --- a/src/render-webgl/systems/index.js +++ b/src/render-webgl/systems/index.js @@ -10,7 +10,7 @@ import { typeid, typeidGeneric } from '../../reflect/index.js' import { AttributeMap, ClearColor, MeshCache, UBOCache, WebglProgramCache } from '../resources/index.js' import { Camera, Material, Mesh, MeshAdded, MeshModified, MeshDropped, RenderLists3D, ShaderStage } from '../../render-core/index.js' import { WebglRenderPipeline, createShader, validateShader, createProgram, validateProgram, createVAO } from '../core/index.js' -import { AssetEvent, Assets } from '../../asset/index.js' +import { Assets } from '../../asset/index.js' import { GlobalTransform3D } from '../../transform/index.js' import { Affine3 } from '../../math/index.js' import { Events } from '../../event/index.js' @@ -146,12 +146,12 @@ export function genRender(type) { const pipeline = programs.get(materialtypeid) - if(!pipeline) return + if (!pipeline) return const opaquepass = renderlist.getOpaquePass(materialtypeid) - if(!opaquepass) return - + if (!opaquepass) return + for (let i = 0; i < opaquepass.length; i++) { const { meshid, materialid, transform } = opaquepass[i] const meshTransform = Affine3.toMatrix4(transform) @@ -230,7 +230,8 @@ export function queueMeshes(world) { const { id } = event.data // SAFETY:Guaranteed to exist as it was added before modified. - const vao = /**@type {WebGLVertexArrayObject} */ (cache.get(id)) + const vao = /** @type {WebGLVertexArrayObject} */ (cache.get(id)) + gl.deleteVertexArray(vao) updateMesh(gl, id, meshes, cache, attributes) }) @@ -244,7 +245,7 @@ export function disposeDroppedMeshes(world) { /** @type {Events} */ const dropped = world.getResourceByTypeId(typeidGeneric(Events, [MeshDropped])) - /**@type {MeshCache} */ + /** @type {MeshCache} */ const cache = world.getResource(MeshCache) const windows = new Query(world, [Entity, Window, MainWindow]) const canvases = world.getResource(Windows) @@ -261,6 +262,7 @@ export function disposeDroppedMeshes(world) { const gl = canvas.getContext('webgl2') if (!gl) return warn('WebGL 2.0 context is not created or is lost.') + dropped.each((drop) => { const { id } = drop.data const vao = cache.get(id)