From 6ff8cbd73f622ac2d47a895e797dabfca37084aa Mon Sep 17 00:00:00 2001 From: 06wj <06wj@163.com> Date: Fri, 29 May 2020 15:12:02 +0800 Subject: [PATCH] perf: improve resource management performance (#10) * perf: update ResourceManager * fix resourceManager reset bug * fix typo * update examples & fix something... --- examples/resourceManagerTest.html | 105 +++++++++++++++++++++++++++ src/core/Mesh.js | 22 +++--- src/core/Stage.js | 1 + src/renderer/VertexArrayObject.js | 22 +++--- src/renderer/WebGLRenderer.js | 8 +- src/renderer/WebGLResourceManager.js | 96 ++++++++++++++---------- 6 files changed, 189 insertions(+), 65 deletions(-) create mode 100644 examples/resourceManagerTest.html diff --git a/examples/resourceManagerTest.html b/examples/resourceManagerTest.html new file mode 100644 index 00000000..901d3c5a --- /dev/null +++ b/examples/resourceManagerTest.html @@ -0,0 +1,105 @@ + + + + + + Hilo3d ResourceManager Test + + + +
+ + + + + + + \ No newline at end of file diff --git a/src/core/Mesh.js b/src/core/Mesh.js index 1c077424..de2ed41a 100644 --- a/src/core/Mesh.js +++ b/src/core/Mesh.js @@ -58,9 +58,6 @@ const Mesh = Class.create(/** @lends Mesh.prototype */ { */ constructor(params) { Mesh.superclass.constructor.call(this, params); - - // store webgl resource - this._usedResourceDict = {}; }, /** * clone 当前mesh @@ -108,9 +105,15 @@ const Mesh = Class.create(/** @lends Mesh.prototype */ { this.geometry.getRenderOption(opt); return opt; }, - useResource(res) { - if (res) { - this._usedResourceDict[res.className + ':' + res.id] = res; + + /** + * 是否被销毁 + * @readOnly + * @type {Boolean} + */ + isDestroyed: { + get() { + return this._isDestroyed; } }, @@ -128,18 +131,13 @@ const Mesh = Class.create(/** @lends Mesh.prototype */ { this.removeFromParent(); const resourceManager = renderer.resourceManager; - const _usedResourceDict = this._usedResourceDict; - - for (let id in _usedResourceDict) { - resourceManager.destroyIfNoRef(_usedResourceDict[id]); - } + resourceManager.destroyMesh(this); if (this.material && needDestroyTextures) { this.material.destroyTextures(); } this.off(); - this._usedResourceDict = null; this.geometry = null; this.material = null; this._isDestroyed = true; diff --git a/src/core/Stage.js b/src/core/Stage.js index 489842ad..4407535b 100644 --- a/src/core/Stage.js +++ b/src/core/Stage.js @@ -357,6 +357,7 @@ const Stage = Class.create(/** @lends Stage.prototype */ { * @return {Stage} this */ destroy() { + Stage.superclass.destroy.call(this, this.renderer); this.releaseGLResource(); this.traverse((child) => { child.off(); diff --git a/src/renderer/VertexArrayObject.js b/src/renderer/VertexArrayObject.js index 637a1810..9ec7aa60 100644 --- a/src/renderer/VertexArrayObject.js +++ b/src/renderer/VertexArrayObject.js @@ -365,22 +365,22 @@ const VertexArrayObject = Class.create(/** @lends VertexArrayObject.prototype */ attributeObject.useInstanced = true; }); }, + /** - * 使用了资源 - * @param {WebGLResourceManager} resourceManager - * @param {Mesh} mesh - * @return {VertexArrayObject} + * 获取资源 + * @param {Object[]} [resources=[]] + * @return {Object[]} */ - useResource(resourceManager, mesh) { + getResources(resources = []) { this.attributes.forEach((attributeObject) => { - resourceManager.useResource(attributeObject.buffer, mesh); + resources.push(attributeObject.buffer); }); if (this.indexBuffer) { - resourceManager.useResource(this.indexBuffer, mesh); + resources.push(this.indexBuffer); } - return this; + return resources; }, /** * 没有被引用时销毁资源 @@ -402,13 +402,17 @@ const VertexArrayObject = Class.create(/** @lends VertexArrayObject.prototype */ return this; } + this.instancedExtension = null; + if (this.useVao) { this.vaoExtension.deleteVertexArrayOES(this.vao); + this.vao = null; + this.vaoExtension = null; } this.gl = null; this.indexBuffer = null; this.attributes.forEach((attributeObject) => { - const attribute = attributeObject; + const attribute = attributeObject.attribute || {}; this[attribute.name] = null; }); this.attributes = null; diff --git a/src/renderer/WebGLRenderer.js b/src/renderer/WebGLRenderer.js index 7495b8ad..d6451bab 100644 --- a/src/renderer/WebGLRenderer.js +++ b/src/renderer/WebGLRenderer.js @@ -629,7 +629,6 @@ const WebGLRenderer = Class.create(/** @lends WebGLRenderer.prototype */ { const gl = this.gl; const state = this.state; const lightManager = this.lightManager; - const resourceManager = this.resourceManager; const geometry = mesh.geometry; const material = this.forceMaterial || mesh.material; const shader = Shader.getShader(mesh, material, useInstanced, lightManager, this.fog, this.useLogDepth); @@ -652,7 +651,9 @@ const WebGLRenderer = Class.create(/** @lends WebGLRenderer.prototype */ { this.setupVao(vao, program, mesh); - resourceManager.useResource(vao, mesh).useResource(shader, mesh).useResource(program, mesh); + mesh._vao = vao; + mesh._shader = shader; + mesh._program = program; return { vao, @@ -695,7 +696,6 @@ const WebGLRenderer = Class.create(/** @lends WebGLRenderer.prototype */ { lightManager.reset(); renderInfo.reset(); renderList.reset(); - resourceManager.reset(); semantic.init(this, state, camera, lightManager, this.fog); stage.updateMatrixWorld(); @@ -743,7 +743,7 @@ const WebGLRenderer = Class.create(/** @lends WebGLRenderer.prototype */ { this.fire('afterRender'); } - resourceManager.destroyUnsuedResource(); + resourceManager.destroyUnsuedResource(stage); }, /** * 渲染场景 diff --git a/src/renderer/WebGLResourceManager.js b/src/renderer/WebGLResourceManager.js index 6a238f88..6aa4b332 100644 --- a/src/renderer/WebGLResourceManager.js +++ b/src/renderer/WebGLResourceManager.js @@ -1,10 +1,14 @@ import Class from '../core/Class'; +import EventMixin from '../core/EventMixin'; /** * WebGLResourceManager 资源管理器 + * @mixes EventMixin + * @fires destroyResource 销毁资源 * @class */ const WebGLResourceManager = Class.create(/** @lends WebGLResourceManager.prototype */{ + Mixes: EventMixin, /** * 类名 * @type {String} @@ -25,37 +29,39 @@ const WebGLResourceManager = Class.create(/** @lends WebGLResourceManager.protot */ hasNeedDestroyResource: false, + /** * @constructs * @param {object} params 初始化参数,所有params都会复制到实例上 */ constructor(params) { + this._needDestroyResources = []; Object.assign(this, params); }, - /** - * 标记使用资源 - * @param {Object} res - * @param {Mesh} mesh 使用资源的mesh - * @return {WebGLResourceManager} this - */ - useResource(res, mesh) { - if (res) { - const key = res.className + ':' + res.id; - if (!this._usedResourceDict[key]) { - this._usedResourceDict[key] = res; - - if (res.useResource) { - res.useResource(this, mesh); - } - } + destroyMesh(mesh) { + const resources = this.getMeshResources(mesh); + resources.forEach((resource) => { + this.destroyIfNoRef(resource); + }); + mesh._vao = mesh._program = mesh._shader = null; + }, + + getMeshResources(mesh, resources = []) { + if (mesh._shader) { + resources.push(mesh._shader); } - if (mesh) { - mesh.useResource(res); + if (mesh._vao) { + resources.push(mesh._vao); + mesh._vao.getResources(resources); } - return this; + if (mesh._program) { + resources.push(mesh._program); + } + + return resources; }, /** @@ -64,40 +70,51 @@ const WebGLResourceManager = Class.create(/** @lends WebGLResourceManager.protot * @return {WebGLResourceManager} this */ destroyIfNoRef(res) { - if (!this._needDestroyDict) { - this._needDestroyDict = {}; + const _needDestroyResources = this._needDestroyResources; + if (res && _needDestroyResources.indexOf(res) < 0) { + _needDestroyResources.push(res); } + return this; + }, - if (res) { - this.hasNeedDestroyResource = true; - this._needDestroyDict[res.className + ':' + res.id] = res; - } + /** + * 获取用到的资源 + * @param {Stage} stage + * @return {Object[]} + */ + getUsedResources(stage) { + const resources = []; + stage.traverse((node) => { + if (node.isMesh && !node.isDestroyed) { + this.getMeshResources(node, resources); + } + }); - return this; + return resources; }, /** * 销毁没被使用的资源 * @return {WebGLResourceManager} this */ - destroyUnsuedResource() { - if (!this.hasNeedDestroyResource) { + destroyUnsuedResource(stage) { + const needDestroyResources = this._needDestroyResources; + if (needDestroyResources.length === 0) { return this; } - const _needDestroyDict = this._needDestroyDict; - const _usedResourceDict = this._usedResourceDict; - for (let key in _needDestroyDict) { - if (!_usedResourceDict[key]) { - const res = _needDestroyDict[key]; - if (res && !res.alwaysUse && res.destroy) { - res.destroy(); + const usedResources = this.getUsedResources(stage); + + needDestroyResources.forEach((resource) => { + if (usedResources.indexOf(resource) < 0) { + if (resource && resource.destroy && !resource.alwaysUse) { + this.fire('destroyResource', resource.id); + resource.destroy(); } } - } + }); - this._needDestroyDict = {}; - this.hasNeedDestroyResource = false; + this.reset(); return this; }, @@ -106,8 +123,7 @@ const WebGLResourceManager = Class.create(/** @lends WebGLResourceManager.protot * @return {WebGLResourceManager} this */ reset() { - this._usedResourceDict = {}; - + this._needDestroyResources.length = 0; return this; } });