WebGLBindingStates.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. function WebGLBindingStates( gl, extensions, attributes, capabilities ) {
  2. const maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS );
  3. const extension = capabilities.isWebGL2 ? null : extensions.get( 'OES_vertex_array_object' );
  4. const vaoAvailable = capabilities.isWebGL2 || extension !== null;
  5. const bindingStates = {};
  6. const defaultState = createBindingState( null );
  7. let currentState = defaultState;
  8. function setup( object, material, program, geometry, index ) {
  9. let updateBuffers = false;
  10. if ( vaoAvailable ) {
  11. const state = getBindingState( geometry, program, material );
  12. if ( currentState !== state ) {
  13. currentState = state;
  14. bindVertexArrayObject( currentState.object );
  15. }
  16. updateBuffers = needsUpdate( geometry );
  17. if ( updateBuffers ) saveCache( geometry );
  18. } else {
  19. const wireframe = ( material.wireframe === true );
  20. if ( currentState.geometry !== geometry.id ||
  21. currentState.program !== program.id ||
  22. currentState.wireframe !== wireframe ) {
  23. currentState.geometry = geometry.id;
  24. currentState.program = program.id;
  25. currentState.wireframe = wireframe;
  26. updateBuffers = true;
  27. }
  28. }
  29. if ( object.isInstancedMesh === true ) {
  30. updateBuffers = true;
  31. }
  32. if ( index !== null ) {
  33. attributes.update( index, gl.ELEMENT_ARRAY_BUFFER );
  34. }
  35. if ( updateBuffers ) {
  36. setupVertexAttributes( object, material, program, geometry );
  37. if ( index !== null ) {
  38. gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, attributes.get( index ).buffer );
  39. }
  40. }
  41. }
  42. function createVertexArrayObject() {
  43. if ( capabilities.isWebGL2 ) return gl.createVertexArray();
  44. return extension.createVertexArrayOES();
  45. }
  46. function bindVertexArrayObject( vao ) {
  47. if ( capabilities.isWebGL2 ) return gl.bindVertexArray( vao );
  48. return extension.bindVertexArrayOES( vao );
  49. }
  50. function deleteVertexArrayObject( vao ) {
  51. if ( capabilities.isWebGL2 ) return gl.deleteVertexArray( vao );
  52. return extension.deleteVertexArrayOES( vao );
  53. }
  54. function getBindingState( geometry, program, material ) {
  55. const wireframe = ( material.wireframe === true );
  56. let programMap = bindingStates[ geometry.id ];
  57. if ( programMap === undefined ) {
  58. programMap = {};
  59. bindingStates[ geometry.id ] = programMap;
  60. }
  61. let stateMap = programMap[ program.id ];
  62. if ( stateMap === undefined ) {
  63. stateMap = {};
  64. programMap[ program.id ] = stateMap;
  65. }
  66. let state = stateMap[ wireframe ];
  67. if ( state === undefined ) {
  68. state = createBindingState( createVertexArrayObject() );
  69. stateMap[ wireframe ] = state;
  70. }
  71. return state;
  72. }
  73. function createBindingState( vao ) {
  74. const newAttributes = [];
  75. const enabledAttributes = [];
  76. const attributeDivisors = [];
  77. for ( let i = 0; i < maxVertexAttributes; i ++ ) {
  78. newAttributes[ i ] = 0;
  79. enabledAttributes[ i ] = 0;
  80. attributeDivisors[ i ] = 0;
  81. }
  82. return {
  83. // for backward compatibility on non-VAO support browser
  84. geometry: null,
  85. program: null,
  86. wireframe: false,
  87. newAttributes: newAttributes,
  88. enabledAttributes: enabledAttributes,
  89. attributeDivisors: attributeDivisors,
  90. object: vao,
  91. attributes: {}
  92. };
  93. }
  94. function needsUpdate( geometry ) {
  95. const cachedAttributes = currentState.attributes;
  96. const geometryAttributes = geometry.attributes;
  97. if ( Object.keys( cachedAttributes ).length !== Object.keys( geometryAttributes ).length ) return true;
  98. for ( const key in geometryAttributes ) {
  99. const cachedAttribute = cachedAttributes[ key ];
  100. const geometryAttribute = geometryAttributes[ key ];
  101. if ( cachedAttribute === undefined ) return true;
  102. if ( cachedAttribute.attribute !== geometryAttribute ) return true;
  103. if ( cachedAttribute.data !== geometryAttribute.data ) return true;
  104. }
  105. return false;
  106. }
  107. function saveCache( geometry ) {
  108. const cache = {};
  109. const attributes = geometry.attributes;
  110. for ( const key in attributes ) {
  111. const attribute = attributes[ key ];
  112. const data = {};
  113. data.attribute = attribute;
  114. if ( attribute.data ) {
  115. data.data = attribute.data;
  116. }
  117. cache[ key ] = data;
  118. }
  119. currentState.attributes = cache;
  120. }
  121. function initAttributes() {
  122. const newAttributes = currentState.newAttributes;
  123. for ( let i = 0, il = newAttributes.length; i < il; i ++ ) {
  124. newAttributes[ i ] = 0;
  125. }
  126. }
  127. function enableAttribute( attribute ) {
  128. enableAttributeAndDivisor( attribute, 0 );
  129. }
  130. function enableAttributeAndDivisor( attribute, meshPerAttribute ) {
  131. const newAttributes = currentState.newAttributes;
  132. const enabledAttributes = currentState.enabledAttributes;
  133. const attributeDivisors = currentState.attributeDivisors;
  134. newAttributes[ attribute ] = 1;
  135. if ( enabledAttributes[ attribute ] === 0 ) {
  136. gl.enableVertexAttribArray( attribute );
  137. enabledAttributes[ attribute ] = 1;
  138. }
  139. if ( attributeDivisors[ attribute ] !== meshPerAttribute ) {
  140. const extension = capabilities.isWebGL2 ? gl : extensions.get( 'ANGLE_instanced_arrays' );
  141. extension[ capabilities.isWebGL2 ? 'vertexAttribDivisor' : 'vertexAttribDivisorANGLE' ]( attribute, meshPerAttribute );
  142. attributeDivisors[ attribute ] = meshPerAttribute;
  143. }
  144. }
  145. function disableUnusedAttributes() {
  146. const newAttributes = currentState.newAttributes;
  147. const enabledAttributes = currentState.enabledAttributes;
  148. for ( let i = 0, il = enabledAttributes.length; i < il; i ++ ) {
  149. if ( enabledAttributes[ i ] !== newAttributes[ i ] ) {
  150. gl.disableVertexAttribArray( i );
  151. enabledAttributes[ i ] = 0;
  152. }
  153. }
  154. }
  155. function vertexAttribPointer( index, size, type, normalized, stride, offset ) {
  156. if ( capabilities.isWebGL2 === true && ( type === gl.INT || type === gl.UNSIGNED_INT ) ) {
  157. gl.vertexAttribIPointer( index, size, type, stride, offset );
  158. } else {
  159. gl.vertexAttribPointer( index, size, type, normalized, stride, offset );
  160. }
  161. }
  162. function setupVertexAttributes( object, material, program, geometry ) {
  163. if ( capabilities.isWebGL2 === false && ( object.isInstancedMesh || geometry.isInstancedBufferGeometry ) ) {
  164. if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) return;
  165. }
  166. initAttributes();
  167. const geometryAttributes = geometry.attributes;
  168. const programAttributes = program.getAttributes();
  169. const materialDefaultAttributeValues = material.defaultAttributeValues;
  170. for ( const name in programAttributes ) {
  171. const programAttribute = programAttributes[ name ];
  172. if ( programAttribute >= 0 ) {
  173. const geometryAttribute = geometryAttributes[ name ];
  174. if ( geometryAttribute !== undefined ) {
  175. const normalized = geometryAttribute.normalized;
  176. const size = geometryAttribute.itemSize;
  177. const attribute = attributes.get( geometryAttribute );
  178. // TODO Attribute may not be available on context restore
  179. if ( attribute === undefined ) continue;
  180. const buffer = attribute.buffer;
  181. const type = attribute.type;
  182. const bytesPerElement = attribute.bytesPerElement;
  183. if ( geometryAttribute.isInterleavedBufferAttribute ) {
  184. const data = geometryAttribute.data;
  185. const stride = data.stride;
  186. const offset = geometryAttribute.offset;
  187. if ( data && data.isInstancedInterleavedBuffer ) {
  188. enableAttributeAndDivisor( programAttribute, data.meshPerAttribute );
  189. if ( geometry._maxInstanceCount === undefined ) {
  190. geometry._maxInstanceCount = data.meshPerAttribute * data.count;
  191. }
  192. } else {
  193. enableAttribute( programAttribute );
  194. }
  195. gl.bindBuffer( gl.ARRAY_BUFFER, buffer );
  196. vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, offset * bytesPerElement );
  197. } else {
  198. if ( geometryAttribute.isInstancedBufferAttribute ) {
  199. enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute );
  200. if ( geometry._maxInstanceCount === undefined ) {
  201. geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count;
  202. }
  203. } else {
  204. enableAttribute( programAttribute );
  205. }
  206. gl.bindBuffer( gl.ARRAY_BUFFER, buffer );
  207. vertexAttribPointer( programAttribute, size, type, normalized, 0, 0 );
  208. }
  209. } else if ( name === 'instanceMatrix' ) {
  210. const attribute = attributes.get( object.instanceMatrix );
  211. // TODO Attribute may not be available on context restore
  212. if ( attribute === undefined ) continue;
  213. const buffer = attribute.buffer;
  214. const type = attribute.type;
  215. enableAttributeAndDivisor( programAttribute + 0, 1 );
  216. enableAttributeAndDivisor( programAttribute + 1, 1 );
  217. enableAttributeAndDivisor( programAttribute + 2, 1 );
  218. enableAttributeAndDivisor( programAttribute + 3, 1 );
  219. gl.bindBuffer( gl.ARRAY_BUFFER, buffer );
  220. gl.vertexAttribPointer( programAttribute + 0, 4, type, false, 64, 0 );
  221. gl.vertexAttribPointer( programAttribute + 1, 4, type, false, 64, 16 );
  222. gl.vertexAttribPointer( programAttribute + 2, 4, type, false, 64, 32 );
  223. gl.vertexAttribPointer( programAttribute + 3, 4, type, false, 64, 48 );
  224. } else if ( name === 'instanceColor' ) {
  225. const attribute = attributes.get( object.instanceColor );
  226. // TODO Attribute may not be available on context restore
  227. if ( attribute === undefined ) continue;
  228. const buffer = attribute.buffer;
  229. const type = attribute.type;
  230. enableAttributeAndDivisor( programAttribute, 1 );
  231. gl.bindBuffer( gl.ARRAY_BUFFER, buffer );
  232. gl.vertexAttribPointer( programAttribute, 3, type, false, 12, 0 );
  233. } else if ( materialDefaultAttributeValues !== undefined ) {
  234. const value = materialDefaultAttributeValues[ name ];
  235. if ( value !== undefined ) {
  236. switch ( value.length ) {
  237. case 2:
  238. gl.vertexAttrib2fv( programAttribute, value );
  239. break;
  240. case 3:
  241. gl.vertexAttrib3fv( programAttribute, value );
  242. break;
  243. case 4:
  244. gl.vertexAttrib4fv( programAttribute, value );
  245. break;
  246. default:
  247. gl.vertexAttrib1fv( programAttribute, value );
  248. }
  249. }
  250. }
  251. }
  252. }
  253. disableUnusedAttributes();
  254. }
  255. function dispose() {
  256. reset();
  257. for ( const geometryId in bindingStates ) {
  258. const programMap = bindingStates[ geometryId ];
  259. for ( const programId in programMap ) {
  260. const stateMap = programMap[ programId ];
  261. for ( const wireframe in stateMap ) {
  262. deleteVertexArrayObject( stateMap[ wireframe ].object );
  263. delete stateMap[ wireframe ];
  264. }
  265. delete programMap[ programId ];
  266. }
  267. delete bindingStates[ geometryId ];
  268. }
  269. }
  270. function releaseStatesOfGeometry( geometry ) {
  271. if ( bindingStates[ geometry.id ] === undefined ) return;
  272. const programMap = bindingStates[ geometry.id ];
  273. for ( const programId in programMap ) {
  274. const stateMap = programMap[ programId ];
  275. for ( const wireframe in stateMap ) {
  276. deleteVertexArrayObject( stateMap[ wireframe ].object );
  277. delete stateMap[ wireframe ];
  278. }
  279. delete programMap[ programId ];
  280. }
  281. delete bindingStates[ geometry.id ];
  282. }
  283. function releaseStatesOfProgram( program ) {
  284. for ( const geometryId in bindingStates ) {
  285. const programMap = bindingStates[ geometryId ];
  286. if ( programMap[ program.id ] === undefined ) continue;
  287. const stateMap = programMap[ program.id ];
  288. for ( const wireframe in stateMap ) {
  289. deleteVertexArrayObject( stateMap[ wireframe ].object );
  290. delete stateMap[ wireframe ];
  291. }
  292. delete programMap[ program.id ];
  293. }
  294. }
  295. function reset() {
  296. resetDefaultState();
  297. if ( currentState === defaultState ) return;
  298. currentState = defaultState;
  299. bindVertexArrayObject( currentState.object );
  300. }
  301. // for backward-compatilibity
  302. function resetDefaultState() {
  303. defaultState.geometry = null;
  304. defaultState.program = null;
  305. defaultState.wireframe = false;
  306. }
  307. return {
  308. setup: setup,
  309. reset: reset,
  310. resetDefaultState: resetDefaultState,
  311. dispose: dispose,
  312. releaseStatesOfGeometry: releaseStatesOfGeometry,
  313. releaseStatesOfProgram: releaseStatesOfProgram,
  314. initAttributes: initAttributes,
  315. enableAttribute: enableAttribute,
  316. disableUnusedAttributes: disableUnusedAttributes
  317. };
  318. }
  319. export { WebGLBindingStates };