WebGLBackend.js 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372
  1. import { WebGLCoordinateSystem } from 'three';
  2. import GLSLNodeBuilder from './nodes/GLSLNodeBuilder.js';
  3. import Backend from '../common/Backend.js';
  4. import WebGLAttributeUtils from './utils/WebGLAttributeUtils.js';
  5. import WebGLState from './utils/WebGLState.js';
  6. import WebGLUtils from './utils/WebGLUtils.js';
  7. import WebGLTextureUtils from './utils/WebGLTextureUtils.js';
  8. import WebGLExtensions from './utils/WebGLExtensions.js';
  9. import WebGLCapabilities from './utils/WebGLCapabilities.js';
  10. import { GLFeatureName } from './utils/WebGLConstants.js';
  11. //
  12. class WebGLBackend extends Backend {
  13. constructor( parameters = {} ) {
  14. super( parameters );
  15. this.isWebGLBackend = true;
  16. }
  17. init( renderer ) {
  18. super.init( renderer );
  19. //
  20. const parameters = this.parameters;
  21. const glContext = ( parameters.context !== undefined ) ? parameters.context : renderer.domElement.getContext( 'webgl2' );
  22. this.gl = glContext;
  23. this.extensions = new WebGLExtensions( this );
  24. this.capabilities = new WebGLCapabilities( this );
  25. this.attributeUtils = new WebGLAttributeUtils( this );
  26. this.textureUtils = new WebGLTextureUtils( this );
  27. this.state = new WebGLState( this );
  28. this.utils = new WebGLUtils( this );
  29. this.vaoCache = {};
  30. this.transformFeedbackCache = {};
  31. this.discard = false;
  32. this.extensions.get( 'EXT_color_buffer_float' );
  33. this.parallel = this.extensions.get( 'KHR_parallel_shader_compile' );
  34. this._currentContext = null;
  35. }
  36. get coordinateSystem() {
  37. return WebGLCoordinateSystem;
  38. }
  39. async getArrayBufferAsync( attribute ) {
  40. return await this.attributeUtils.getArrayBufferAsync( attribute );
  41. }
  42. getContext() {
  43. return this.gl;
  44. }
  45. beginRender( renderContext ) {
  46. const { gl } = this;
  47. const renderContextData = this.get( renderContext );
  48. //
  49. //
  50. renderContextData.previousContext = this._currentContext;
  51. this._currentContext = renderContext;
  52. this._setFramebuffer( renderContext );
  53. this.clear( renderContext.clearColor, renderContext.clearDepth, renderContext.clearStencil, renderContext );
  54. //
  55. if ( renderContext.viewport ) {
  56. this.updateViewport( renderContext );
  57. } else {
  58. gl.viewport( 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight );
  59. }
  60. if ( renderContext.scissor ) {
  61. const { x, y, width, height } = renderContext.scissorValue;
  62. gl.scissor( x, y, width, height );
  63. }
  64. const occlusionQueryCount = renderContext.occlusionQueryCount;
  65. if ( occlusionQueryCount > 0 ) {
  66. // Get a reference to the array of objects with queries. The renderContextData property
  67. // can be changed by another render pass before the async reading of all previous queries complete
  68. renderContextData.currentOcclusionQueries = renderContextData.occlusionQueries;
  69. renderContextData.currentOcclusionQueryObjects = renderContextData.occlusionQueryObjects;
  70. renderContextData.lastOcclusionObject = null;
  71. renderContextData.occlusionQueries = new Array( occlusionQueryCount );
  72. renderContextData.occlusionQueryObjects = new Array( occlusionQueryCount );
  73. renderContextData.occlusionQueryIndex = 0;
  74. }
  75. }
  76. finishRender( renderContext ) {
  77. const { gl, state } = this;
  78. const renderContextData = this.get( renderContext );
  79. const previousContext = renderContextData.previousContext;
  80. const textures = renderContext.textures;
  81. if ( textures !== null ) {
  82. for ( let i = 0; i < textures.length; i ++ ) {
  83. const texture = textures[ i ];
  84. if ( texture.generateMipmaps ) {
  85. this.generateMipmaps( texture );
  86. }
  87. }
  88. }
  89. this._currentContext = previousContext;
  90. if ( renderContext.textures !== null && renderContext.renderTarget ) {
  91. const renderTargetContextData = this.get( renderContext.renderTarget );
  92. const { samples } = renderContext.renderTarget;
  93. const fb = renderTargetContextData.framebuffer;
  94. const mask = gl.COLOR_BUFFER_BIT;
  95. if ( samples > 0 ) {
  96. const msaaFrameBuffer = renderTargetContextData.msaaFrameBuffer;
  97. const textures = renderContext.textures;
  98. state.bindFramebuffer( gl.READ_FRAMEBUFFER, msaaFrameBuffer );
  99. state.bindFramebuffer( gl.DRAW_FRAMEBUFFER, fb );
  100. for ( let i = 0; i < textures.length; i ++ ) {
  101. // TODO Add support for MRT
  102. gl.blitFramebuffer( 0, 0, renderContext.width, renderContext.height, 0, 0, renderContext.width, renderContext.height, mask, gl.NEAREST );
  103. gl.invalidateFramebuffer( gl.READ_FRAMEBUFFER, renderTargetContextData.invalidationArray );
  104. }
  105. }
  106. }
  107. if ( previousContext !== null ) {
  108. this._setFramebuffer( previousContext );
  109. if ( previousContext.viewport ) {
  110. this.updateViewport( previousContext );
  111. } else {
  112. const gl = this.gl;
  113. gl.viewport( 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight );
  114. }
  115. }
  116. const occlusionQueryCount = renderContext.occlusionQueryCount;
  117. if ( occlusionQueryCount > 0 ) {
  118. const renderContextData = this.get( renderContext );
  119. if ( occlusionQueryCount > renderContextData.occlusionQueryIndex ) {
  120. const { gl } = this;
  121. gl.endQuery( gl.ANY_SAMPLES_PASSED );
  122. }
  123. this.resolveOccludedAsync( renderContext );
  124. }
  125. }
  126. resolveOccludedAsync( renderContext ) {
  127. const renderContextData = this.get( renderContext );
  128. // handle occlusion query results
  129. const { currentOcclusionQueries, currentOcclusionQueryObjects } = renderContextData;
  130. if ( currentOcclusionQueries && currentOcclusionQueryObjects ) {
  131. const occluded = new WeakSet();
  132. const { gl } = this;
  133. renderContextData.currentOcclusionQueryObjects = null;
  134. renderContextData.currentOcclusionQueries = null;
  135. const check = () => {
  136. let completed = 0;
  137. // check all queries and requeue as appropriate
  138. for ( let i = 0; i < currentOcclusionQueries.length; i ++ ) {
  139. const query = currentOcclusionQueries[ i ];
  140. if ( query === null ) continue;
  141. if ( gl.getQueryParameter( query, gl.QUERY_RESULT_AVAILABLE ) ) {
  142. if ( gl.getQueryParameter( query, gl.QUERY_RESULT ) > 0 ) occluded.add( currentOcclusionQueryObjects[ i ] );
  143. currentOcclusionQueries[ i ] = null;
  144. gl.deleteQuery( query );
  145. completed ++;
  146. }
  147. }
  148. if ( completed < currentOcclusionQueries.length ) {
  149. requestAnimationFrame( check );
  150. } else {
  151. renderContextData.occluded = occluded;
  152. }
  153. };
  154. check();
  155. }
  156. }
  157. isOccluded( renderContext, object ) {
  158. const renderContextData = this.get( renderContext );
  159. return renderContextData.occluded && renderContextData.occluded.has( object );
  160. }
  161. updateViewport( renderContext ) {
  162. const gl = this.gl;
  163. const { x, y, width, height } = renderContext.viewportValue;
  164. gl.viewport( x, y, width, height );
  165. }
  166. setScissorTest( boolean ) {
  167. const gl = this.gl;
  168. if ( boolean ) {
  169. gl.enable( gl.SCISSOR_TEST );
  170. } else {
  171. gl.disable( gl.SCISSOR_TEST );
  172. }
  173. }
  174. clear( color, depth, stencil, descriptor = null ) {
  175. const { gl } = this;
  176. if ( descriptor === null ) {
  177. descriptor = {
  178. textures: null,
  179. clearColorValue: this.getClearColor()
  180. };
  181. }
  182. //
  183. let clear = 0;
  184. if ( color ) clear |= gl.COLOR_BUFFER_BIT;
  185. if ( depth ) clear |= gl.DEPTH_BUFFER_BIT;
  186. if ( stencil ) clear |= gl.STENCIL_BUFFER_BIT;
  187. if ( clear !== 0 ) {
  188. const clearColor = descriptor.clearColorValue;
  189. if ( depth ) this.state.setDepthMask( true );
  190. if ( descriptor.textures === null ) {
  191. gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearColor.a );
  192. gl.clear( clear );
  193. } else {
  194. if ( color ) {
  195. for ( let i = 0; i < descriptor.textures.length; i ++ ) {
  196. gl.clearBufferfv( gl.COLOR, i, [ clearColor.r, clearColor.g, clearColor.b, clearColor.a ] );
  197. }
  198. }
  199. if ( depth && stencil ) {
  200. gl.clearBufferfi( gl.DEPTH_STENCIL, 0, 1, 0 );
  201. } else if ( depth ) {
  202. gl.clearBufferfv( gl.DEPTH, 0, [ 1.0 ] );
  203. } else if ( stencil ) {
  204. gl.clearBufferiv( gl.STENCIL, 0, [ 0 ] );
  205. }
  206. }
  207. }
  208. }
  209. beginCompute( /*computeGroup*/ ) {
  210. const gl = this.gl;
  211. gl.bindFramebuffer( gl.FRAMEBUFFER, null );
  212. }
  213. compute( computeGroup, computeNode, bindings, pipeline ) {
  214. const gl = this.gl;
  215. if ( ! this.discard ) {
  216. // required here to handle async behaviour of render.compute()
  217. gl.enable( gl.RASTERIZER_DISCARD );
  218. this.discard = true;
  219. }
  220. const { programGPU, transformBuffers, attributes } = this.get( pipeline );
  221. const vaoKey = this._getVaoKey( null, attributes );
  222. const vaoGPU = this.vaoCache[ vaoKey ];
  223. if ( vaoGPU === undefined ) {
  224. this._createVao( null, attributes );
  225. } else {
  226. gl.bindVertexArray( vaoGPU );
  227. }
  228. gl.useProgram( programGPU );
  229. this._bindUniforms( bindings );
  230. const transformFeedbackGPU = this._getTransformFeedback( transformBuffers );
  231. gl.bindTransformFeedback( gl.TRANSFORM_FEEDBACK, transformFeedbackGPU );
  232. gl.beginTransformFeedback( gl.POINTS );
  233. gl.drawArraysInstanced( gl.POINTS, 0, 1, computeNode.count );
  234. gl.endTransformFeedback();
  235. gl.bindTransformFeedback( gl.TRANSFORM_FEEDBACK, null );
  236. // switch active buffers
  237. for ( let i = 0; i < transformBuffers.length; i ++ ) {
  238. const dualAttributeData = transformBuffers[ i ];
  239. if ( dualAttributeData.pbo ) {
  240. this.textureUtils.copyBufferToTexture( dualAttributeData.transformBuffer, dualAttributeData.pbo );
  241. }
  242. dualAttributeData.switchBuffers();
  243. }
  244. }
  245. finishCompute( /*computeGroup*/ ) {
  246. const gl = this.gl;
  247. this.discard = false;
  248. gl.disable( gl.RASTERIZER_DISCARD );
  249. }
  250. draw( renderObject, info ) {
  251. const { pipeline, material, context } = renderObject;
  252. const { programGPU } = this.get( pipeline );
  253. const { gl, state } = this;
  254. const contextData = this.get( context );
  255. //
  256. this._bindUniforms( renderObject.getBindings() );
  257. state.setMaterial( material );
  258. gl.useProgram( programGPU );
  259. //
  260. let vaoGPU = renderObject.staticVao;
  261. if ( vaoGPU === undefined ) {
  262. const vaoKey = this._getVaoKey( renderObject.getIndex(), renderObject.getAttributes() );
  263. vaoGPU = this.vaoCache[ vaoKey ];
  264. if ( vaoGPU === undefined ) {
  265. let staticVao;
  266. ( { vaoGPU, staticVao } = this._createVao( renderObject.getIndex(), renderObject.getAttributes() ) );
  267. if ( staticVao ) renderObject.staticVao = vaoGPU;
  268. }
  269. }
  270. gl.bindVertexArray( vaoGPU );
  271. //
  272. const index = renderObject.getIndex();
  273. const object = renderObject.object;
  274. const geometry = renderObject.geometry;
  275. const drawRange = geometry.drawRange;
  276. const firstVertex = drawRange.start;
  277. //
  278. const lastObject = contextData.lastOcclusionObject;
  279. if ( lastObject !== object && lastObject !== undefined ) {
  280. if ( lastObject !== null && lastObject.occlusionTest === true ) {
  281. gl.endQuery( gl.ANY_SAMPLES_PASSED );
  282. contextData.occlusionQueryIndex ++;
  283. }
  284. if ( object.occlusionTest === true ) {
  285. const query = gl.createQuery();
  286. gl.beginQuery( gl.ANY_SAMPLES_PASSED, query );
  287. contextData.occlusionQueries[ contextData.occlusionQueryIndex ] = query;
  288. contextData.occlusionQueryObjects[ contextData.occlusionQueryIndex ] = object;
  289. }
  290. contextData.lastOcclusionObject = object;
  291. }
  292. //
  293. let mode;
  294. if ( object.isPoints ) mode = gl.POINTS;
  295. else if ( object.isLineSegments ) mode = gl.LINES;
  296. else if ( object.isLine ) mode = gl.LINE_STRIP;
  297. else if ( object.isLineLoop ) mode = gl.LINE_LOOP;
  298. else {
  299. if ( material.wireframe === true ) {
  300. state.setLineWidth( material.wireframeLinewidth * this.renderer.getPixelRatio() );
  301. mode = gl.LINES;
  302. } else {
  303. mode = gl.TRIANGLES;
  304. }
  305. }
  306. //
  307. const instanceCount = this.getInstanceCount( renderObject );
  308. if ( index !== null ) {
  309. const indexData = this.get( index );
  310. const indexCount = ( drawRange.count !== Infinity ) ? drawRange.count : index.count;
  311. if ( instanceCount > 1 ) {
  312. gl.drawElementsInstanced( mode, index.count, indexData.type, firstVertex, instanceCount );
  313. } else {
  314. gl.drawElements( mode, index.count, indexData.type, firstVertex );
  315. }
  316. info.update( object, indexCount, 1 );
  317. } else {
  318. const positionAttribute = geometry.attributes.position;
  319. const vertexCount = ( drawRange.count !== Infinity ) ? drawRange.count : positionAttribute.count;
  320. if ( instanceCount > 1 ) {
  321. gl.drawArraysInstanced( mode, 0, vertexCount, instanceCount );
  322. } else {
  323. gl.drawArrays( mode, 0, vertexCount );
  324. }
  325. //gl.drawArrays( mode, vertexCount, gl.UNSIGNED_SHORT, firstVertex );
  326. info.update( object, vertexCount, 1 );
  327. }
  328. //
  329. gl.bindVertexArray( null );
  330. }
  331. needsRenderUpdate( /*renderObject*/ ) {
  332. return false;
  333. }
  334. getRenderCacheKey( renderObject ) {
  335. return renderObject.id;
  336. }
  337. // textures
  338. createDefaultTexture( texture ) {
  339. this.textureUtils.createDefaultTexture( texture );
  340. }
  341. createTexture( texture, options ) {
  342. this.textureUtils.createTexture( texture, options );
  343. }
  344. updateTexture( texture, options ) {
  345. this.textureUtils.updateTexture( texture, options );
  346. }
  347. generateMipmaps( texture ) {
  348. this.textureUtils.generateMipmaps( texture );
  349. }
  350. destroyTexture( texture ) {
  351. this.textureUtils.destroyTexture( texture );
  352. }
  353. copyTextureToBuffer( texture, x, y, width, height ) {
  354. return this.textureUtils.copyTextureToBuffer( texture, x, y, width, height );
  355. }
  356. createSampler( /*texture*/ ) {
  357. //console.warn( 'Abstract class.' );
  358. }
  359. destroySampler() {}
  360. // node builder
  361. createNodeBuilder( object, renderer, scene = null ) {
  362. return new GLSLNodeBuilder( object, renderer, scene );
  363. }
  364. // program
  365. createProgram( program ) {
  366. const gl = this.gl;
  367. const { stage, code } = program;
  368. const shader = stage === 'fragment' ? gl.createShader( gl.FRAGMENT_SHADER ) : gl.createShader( gl.VERTEX_SHADER );
  369. gl.shaderSource( shader, code );
  370. gl.compileShader( shader );
  371. this.set( program, {
  372. shaderGPU: shader
  373. } );
  374. }
  375. destroyProgram( /*program*/ ) {
  376. console.warn( 'Abstract class.' );
  377. }
  378. createRenderPipeline( renderObject, promises ) {
  379. const gl = this.gl;
  380. const pipeline = renderObject.pipeline;
  381. // Program
  382. const { fragmentProgram, vertexProgram } = pipeline;
  383. const programGPU = gl.createProgram();
  384. const fragmentShader = this.get( fragmentProgram ).shaderGPU;
  385. const vertexShader = this.get( vertexProgram ).shaderGPU;
  386. gl.attachShader( programGPU, fragmentShader );
  387. gl.attachShader( programGPU, vertexShader );
  388. gl.linkProgram( programGPU );
  389. this.set( pipeline, {
  390. programGPU,
  391. fragmentShader,
  392. vertexShader
  393. } );
  394. if ( promises !== null && this.parallel ) {
  395. const p = new Promise( ( resolve /*, reject*/ ) => {
  396. const parallel = this.parallel;
  397. const checkStatus = () => {
  398. if ( gl.getProgramParameter( programGPU, parallel.COMPLETION_STATUS_KHR ) ) {
  399. this._completeCompile( renderObject, pipeline );
  400. resolve();
  401. } else {
  402. requestAnimationFrame( checkStatus );
  403. }
  404. };
  405. checkStatus();
  406. } );
  407. promises.push( p );
  408. return;
  409. }
  410. this._completeCompile( renderObject, pipeline );
  411. }
  412. _completeCompile( renderObject, pipeline ) {
  413. const gl = this.gl;
  414. const pipelineData = this.get( pipeline );
  415. const { programGPU, fragmentShader, vertexShader } = pipelineData;
  416. if ( gl.getProgramParameter( programGPU, gl.LINK_STATUS ) === false ) {
  417. console.error( 'THREE.WebGLBackend:', gl.getProgramInfoLog( programGPU ) );
  418. console.error( 'THREE.WebGLBackend:', gl.getShaderInfoLog( fragmentShader ) );
  419. console.error( 'THREE.WebGLBackend:', gl.getShaderInfoLog( vertexShader ) );
  420. }
  421. gl.useProgram( programGPU );
  422. // Bindings
  423. this._setupBindings( renderObject.getBindings(), programGPU );
  424. //
  425. this.set( pipeline, {
  426. programGPU
  427. } );
  428. }
  429. createComputePipeline( computePipeline, bindings ) {
  430. const gl = this.gl;
  431. // Program
  432. const fragmentProgram = {
  433. stage: 'fragment',
  434. code: '#version 300 es\nprecision highp float;\nvoid main() {}'
  435. };
  436. this.createProgram( fragmentProgram );
  437. const { computeProgram } = computePipeline;
  438. const programGPU = gl.createProgram();
  439. const fragmentShader = this.get( fragmentProgram ).shaderGPU;
  440. const vertexShader = this.get( computeProgram ).shaderGPU;
  441. const transforms = computeProgram.transforms;
  442. const transformVaryingNames = [];
  443. const transformAttributeNodes = [];
  444. for ( let i = 0; i < transforms.length; i ++ ) {
  445. const transform = transforms[ i ];
  446. transformVaryingNames.push( transform.varyingName );
  447. transformAttributeNodes.push( transform.attributeNode );
  448. }
  449. gl.attachShader( programGPU, fragmentShader );
  450. gl.attachShader( programGPU, vertexShader );
  451. gl.transformFeedbackVaryings(
  452. programGPU,
  453. transformVaryingNames,
  454. gl.SEPARATE_ATTRIBS,
  455. );
  456. gl.linkProgram( programGPU );
  457. if ( gl.getProgramParameter( programGPU, gl.LINK_STATUS ) === false ) {
  458. console.error( 'THREE.WebGLBackend:', gl.getProgramInfoLog( programGPU ) );
  459. console.error( 'THREE.WebGLBackend:', gl.getShaderInfoLog( fragmentShader ) );
  460. console.error( 'THREE.WebGLBackend:', gl.getShaderInfoLog( vertexShader ) );
  461. }
  462. gl.useProgram( programGPU );
  463. // Bindings
  464. this.createBindings( bindings );
  465. this._setupBindings( bindings, programGPU );
  466. const attributeNodes = computeProgram.attributes;
  467. const attributes = [];
  468. const transformBuffers = [];
  469. for ( let i = 0; i < attributeNodes.length; i ++ ) {
  470. const attribute = attributeNodes[ i ].node.attribute;
  471. attributes.push( attribute );
  472. if ( ! this.has( attribute ) ) this.attributeUtils.createAttribute( attribute, gl.ARRAY_BUFFER );
  473. }
  474. for ( let i = 0; i < transformAttributeNodes.length; i ++ ) {
  475. const attribute = transformAttributeNodes[ i ].attribute;
  476. if ( ! this.has( attribute ) ) this.attributeUtils.createAttribute( attribute, gl.ARRAY_BUFFER );
  477. const attributeData = this.get( attribute );
  478. transformBuffers.push( attributeData );
  479. }
  480. //
  481. this.set( computePipeline, {
  482. programGPU,
  483. transformBuffers,
  484. attributes
  485. } );
  486. }
  487. createBindings( bindings ) {
  488. this.updateBindings( bindings );
  489. }
  490. updateBindings( bindings ) {
  491. const { gl } = this;
  492. let groupIndex = 0;
  493. let textureIndex = 0;
  494. for ( const binding of bindings ) {
  495. if ( binding.isUniformsGroup || binding.isUniformBuffer ) {
  496. const bufferGPU = gl.createBuffer();
  497. const data = binding.buffer;
  498. gl.bindBuffer( gl.UNIFORM_BUFFER, bufferGPU );
  499. gl.bufferData( gl.UNIFORM_BUFFER, data, gl.DYNAMIC_DRAW );
  500. gl.bindBufferBase( gl.UNIFORM_BUFFER, groupIndex, bufferGPU );
  501. this.set( binding, {
  502. index: groupIndex ++,
  503. bufferGPU
  504. } );
  505. } else if ( binding.isSampledTexture ) {
  506. const { textureGPU, glTextureType } = this.get( binding.texture );
  507. this.set( binding, {
  508. index: textureIndex ++,
  509. textureGPU,
  510. glTextureType
  511. } );
  512. }
  513. }
  514. }
  515. updateBinding( binding ) {
  516. const gl = this.gl;
  517. if ( binding.isUniformsGroup || binding.isUniformBuffer ) {
  518. const bindingData = this.get( binding );
  519. const bufferGPU = bindingData.bufferGPU;
  520. const data = binding.buffer;
  521. gl.bindBuffer( gl.UNIFORM_BUFFER, bufferGPU );
  522. gl.bufferData( gl.UNIFORM_BUFFER, data, gl.DYNAMIC_DRAW );
  523. }
  524. }
  525. // attributes
  526. createIndexAttribute( attribute ) {
  527. const gl = this.gl;
  528. this.attributeUtils.createAttribute( attribute, gl.ELEMENT_ARRAY_BUFFER );
  529. }
  530. createAttribute( attribute ) {
  531. if ( this.has( attribute ) ) return;
  532. const gl = this.gl;
  533. this.attributeUtils.createAttribute( attribute, gl.ARRAY_BUFFER );
  534. }
  535. createStorageAttribute( attribute ) {
  536. //console.warn( 'Abstract class.' );
  537. }
  538. updateAttribute( attribute ) {
  539. this.attributeUtils.updateAttribute( attribute );
  540. }
  541. destroyAttribute( attribute ) {
  542. this.attributeUtils.destroyAttribute( attribute );
  543. }
  544. updateSize() {
  545. //console.warn( 'Abstract class.' );
  546. }
  547. async hasFeatureAsync( name ) {
  548. return this.hasFeature( name );
  549. }
  550. hasFeature( name ) {
  551. const keysMatching = Object.keys( GLFeatureName ).filter( key => GLFeatureName[ key ] === name );
  552. const extensions = this.extensions;
  553. for ( let i = 0; i < keysMatching.length; i ++ ) {
  554. if ( extensions.has( keysMatching[ i ] ) ) return true;
  555. }
  556. return false;
  557. }
  558. getMaxAnisotropy() {
  559. return this.capabilities.getMaxAnisotropy();
  560. }
  561. copyFramebufferToTexture( texture, renderContext ) {
  562. this.textureUtils.copyFramebufferToTexture( texture, renderContext );
  563. }
  564. _setFramebuffer( renderContext ) {
  565. const { gl, state } = this;
  566. let currentFrameBuffer = null;
  567. if ( renderContext.textures !== null ) {
  568. const renderTarget = renderContext.renderTarget;
  569. const renderTargetContextData = this.get( renderTarget );
  570. const { samples, depthBuffer, stencilBuffer } = renderTarget;
  571. const cubeFace = this.renderer._activeCubeFace;
  572. const isCube = renderTarget.isWebGLCubeRenderTarget === true;
  573. let msaaFb = renderTargetContextData.msaaFrameBuffer;
  574. let depthRenderbuffer = renderTargetContextData.depthRenderbuffer;
  575. let fb;
  576. if ( isCube ) {
  577. if ( renderTargetContextData.cubeFramebuffers === undefined ) {
  578. renderTargetContextData.cubeFramebuffers = [];
  579. }
  580. fb = renderTargetContextData.cubeFramebuffers[ cubeFace ];
  581. } else {
  582. fb = renderTargetContextData.framebuffer;
  583. }
  584. if ( fb === undefined ) {
  585. fb = gl.createFramebuffer();
  586. state.bindFramebuffer( gl.FRAMEBUFFER, fb );
  587. const textures = renderContext.textures;
  588. if ( isCube ) {
  589. renderTargetContextData.cubeFramebuffers[ cubeFace ] = fb;
  590. const { textureGPU } = this.get( textures[ 0 ] );
  591. gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + cubeFace, textureGPU, 0 );
  592. } else {
  593. for ( let i = 0; i < textures.length; i ++ ) {
  594. const texture = textures[ i ];
  595. const textureData = this.get( texture );
  596. textureData.renderTarget = renderContext.renderTarget;
  597. const attachment = gl.COLOR_ATTACHMENT0 + i;
  598. gl.framebufferTexture2D( gl.FRAMEBUFFER, attachment, gl.TEXTURE_2D, textureData.textureGPU, 0 );
  599. }
  600. renderTargetContextData.framebuffer = fb;
  601. state.drawBuffers( renderContext, fb );
  602. }
  603. if ( renderContext.depthTexture !== null ) {
  604. const textureData = this.get( renderContext.depthTexture );
  605. const depthStyle = stencilBuffer ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT;
  606. gl.framebufferTexture2D( gl.FRAMEBUFFER, depthStyle, gl.TEXTURE_2D, textureData.textureGPU, 0 );
  607. }
  608. }
  609. if ( samples > 0 ) {
  610. if ( msaaFb === undefined ) {
  611. const invalidationArray = [];
  612. msaaFb = gl.createFramebuffer();
  613. state.bindFramebuffer( gl.FRAMEBUFFER, msaaFb );
  614. const msaaRenderbuffers = [];
  615. const textures = renderContext.textures;
  616. for ( let i = 0; i < textures.length; i ++ ) {
  617. msaaRenderbuffers[ i ] = gl.createRenderbuffer();
  618. gl.bindRenderbuffer( gl.RENDERBUFFER, msaaRenderbuffers[ i ] );
  619. invalidationArray.push( gl.COLOR_ATTACHMENT0 + i );
  620. if ( depthBuffer ) {
  621. const depthStyle = stencilBuffer ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT;
  622. invalidationArray.push( depthStyle );
  623. }
  624. const texture = renderContext.textures[ i ];
  625. const textureData = this.get( texture );
  626. gl.renderbufferStorageMultisample( gl.RENDERBUFFER, samples, textureData.glInternalFormat, renderContext.width, renderContext.height );
  627. gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, gl.RENDERBUFFER, msaaRenderbuffers[ i ] );
  628. }
  629. renderTargetContextData.msaaFrameBuffer = msaaFb;
  630. renderTargetContextData.msaaRenderbuffers = msaaRenderbuffers;
  631. if ( depthRenderbuffer === undefined ) {
  632. depthRenderbuffer = gl.createRenderbuffer();
  633. this.textureUtils.setupRenderBufferStorage( depthRenderbuffer, renderContext );
  634. renderTargetContextData.depthRenderbuffer = depthRenderbuffer;
  635. const depthStyle = stencilBuffer ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT;
  636. invalidationArray.push( depthStyle );
  637. }
  638. renderTargetContextData.invalidationArray = invalidationArray;
  639. }
  640. currentFrameBuffer = renderTargetContextData.msaaFrameBuffer;
  641. } else {
  642. currentFrameBuffer = fb;
  643. }
  644. }
  645. state.bindFramebuffer( gl.FRAMEBUFFER, currentFrameBuffer );
  646. }
  647. _getVaoKey( index, attributes ) {
  648. let key = [];
  649. if ( index !== null ) {
  650. const indexData = this.get( index );
  651. key += ':' + indexData.id;
  652. }
  653. for ( let i = 0; i < attributes.length; i ++ ) {
  654. const attributeData = this.get( attributes[ i ] );
  655. key += ':' + attributeData.id;
  656. }
  657. return key;
  658. }
  659. _createVao( index, attributes ) {
  660. const { gl } = this;
  661. const vaoGPU = gl.createVertexArray();
  662. let key = '';
  663. let staticVao = true;
  664. gl.bindVertexArray( vaoGPU );
  665. if ( index !== null ) {
  666. const indexData = this.get( index );
  667. gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, indexData.bufferGPU );
  668. key += ':' + indexData.id;
  669. }
  670. for ( let i = 0; i < attributes.length; i ++ ) {
  671. const attribute = attributes[ i ];
  672. const attributeData = this.get( attribute );
  673. key += ':' + attributeData.id;
  674. gl.bindBuffer( gl.ARRAY_BUFFER, attributeData.bufferGPU );
  675. gl.enableVertexAttribArray( i );
  676. if ( attribute.isStorageBufferAttribute ) staticVao = false;
  677. let stride, offset;
  678. if ( attribute.isInterleavedBufferAttribute === true ) {
  679. stride = attribute.data.stride * attributeData.bytesPerElement;
  680. offset = attribute.offset * attributeData.bytesPerElement;
  681. } else {
  682. stride = 0;
  683. offset = 0;
  684. }
  685. if ( attributeData.isInteger ) {
  686. gl.vertexAttribIPointer( i, attribute.itemSize, attributeData.type, stride, offset );
  687. } else {
  688. gl.vertexAttribPointer( i, attribute.itemSize, attributeData.type, attribute.normalized, stride, offset );
  689. }
  690. if ( attribute.isInstancedBufferAttribute && ! attribute.isInterleavedBufferAttribute ) {
  691. gl.vertexAttribDivisor( i, attribute.meshPerAttribute );
  692. } else if ( attribute.isInterleavedBufferAttribute && attribute.data.isInstancedInterleavedBuffer ) {
  693. gl.vertexAttribDivisor( i, attribute.data.meshPerAttribute );
  694. }
  695. }
  696. gl.bindBuffer( gl.ARRAY_BUFFER, null );
  697. this.vaoCache[ key ] = vaoGPU;
  698. return { vaoGPU, staticVao };
  699. }
  700. _getTransformFeedback( transformBuffers ) {
  701. let key = '';
  702. for ( let i = 0; i < transformBuffers.length; i ++ ) {
  703. key += ':' + transformBuffers[ i ].id;
  704. }
  705. let transformFeedbackGPU = this.transformFeedbackCache[ key ];
  706. if ( transformFeedbackGPU !== undefined ) {
  707. return transformFeedbackGPU;
  708. }
  709. const gl = this.gl;
  710. transformFeedbackGPU = gl.createTransformFeedback();
  711. gl.bindTransformFeedback( gl.TRANSFORM_FEEDBACK, transformFeedbackGPU );
  712. for ( let i = 0; i < transformBuffers.length; i ++ ) {
  713. const attributeData = transformBuffers[ i ];
  714. gl.bindBufferBase( gl.TRANSFORM_FEEDBACK_BUFFER, i, attributeData.transformBuffer );
  715. }
  716. gl.bindTransformFeedback( gl.TRANSFORM_FEEDBACK, null );
  717. this.transformFeedbackCache[ key ] = transformFeedbackGPU;
  718. return transformFeedbackGPU;
  719. }
  720. _setupBindings( bindings, programGPU ) {
  721. const gl = this.gl;
  722. for ( const binding of bindings ) {
  723. const bindingData = this.get( binding );
  724. const index = bindingData.index;
  725. if ( binding.isUniformsGroup || binding.isUniformBuffer ) {
  726. const location = gl.getUniformBlockIndex( programGPU, binding.name );
  727. gl.uniformBlockBinding( programGPU, location, index );
  728. } else if ( binding.isSampledTexture ) {
  729. const location = gl.getUniformLocation( programGPU, binding.name );
  730. gl.uniform1i( location, index );
  731. }
  732. }
  733. }
  734. _bindUniforms( bindings ) {
  735. const { gl, state } = this;
  736. for ( const binding of bindings ) {
  737. const bindingData = this.get( binding );
  738. const index = bindingData.index;
  739. if ( binding.isUniformsGroup || binding.isUniformBuffer ) {
  740. gl.bindBufferBase( gl.UNIFORM_BUFFER, index, bindingData.bufferGPU );
  741. } else if ( binding.isSampledTexture ) {
  742. state.bindTexture( bindingData.glTextureType, bindingData.textureGPU, gl.TEXTURE0 + index );
  743. }
  744. }
  745. }
  746. }
  747. export default WebGLBackend;