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;
}
});