WebGPURenderer.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236
  1. import { GPUIndexFormat, GPUTextureFormat, GPUFeatureName, GPULoadOp } from './constants.js';
  2. import WebGPUAnimation from './WebGPUAnimation.js';
  3. import WebGPURenderObjects from './WebGPURenderObjects.js';
  4. import WebGPUAttributes from './WebGPUAttributes.js';
  5. import WebGPUGeometries from './WebGPUGeometries.js';
  6. import WebGPUInfo from './WebGPUInfo.js';
  7. import WebGPUProperties from './WebGPUProperties.js';
  8. import WebGPURenderPipelines from './WebGPURenderPipelines.js';
  9. import WebGPUComputePipelines from './WebGPUComputePipelines.js';
  10. import WebGPUBindings from './WebGPUBindings.js';
  11. import WebGPURenderLists from './WebGPURenderLists.js';
  12. import WebGPURenderStates from './WebGPURenderStates.js';
  13. import WebGPUTextures from './WebGPUTextures.js';
  14. import WebGPUBackground from './WebGPUBackground.js';
  15. import WebGPUNodes from './nodes/WebGPUNodes.js';
  16. import WebGPUUtils from './WebGPUUtils.js';
  17. import { Frustum, Matrix4, Vector3, Color, SRGBColorSpace, NoToneMapping, DepthFormat } from 'three';
  18. let staticAdapter = null;
  19. if ( navigator.gpu !== undefined ) {
  20. staticAdapter = await navigator.gpu.requestAdapter();
  21. }
  22. console.info( 'THREE.WebGPURenderer: Modified Matrix4.makePerspective() and Matrix4.makeOrtographic() to work with WebGPU, see https://github.com/mrdoob/three.js/issues/20276.' );
  23. Matrix4.prototype.makePerspective = function ( left, right, top, bottom, near, far ) {
  24. const te = this.elements;
  25. const x = 2 * near / ( right - left );
  26. const y = 2 * near / ( top - bottom );
  27. const a = ( right + left ) / ( right - left );
  28. const b = ( top + bottom ) / ( top - bottom );
  29. const c = - far / ( far - near );
  30. const d = - far * near / ( far - near );
  31. te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0;
  32. te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0;
  33. te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d;
  34. te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0;
  35. return this;
  36. };
  37. Matrix4.prototype.makeOrthographic = function ( left, right, top, bottom, near, far ) {
  38. const te = this.elements;
  39. const w = 1.0 / ( right - left );
  40. const h = 1.0 / ( top - bottom );
  41. const p = 1.0 / ( far - near );
  42. const x = ( right + left ) * w;
  43. const y = ( top + bottom ) * h;
  44. const z = near * p;
  45. te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x;
  46. te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y;
  47. te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 1 * p; te[ 14 ] = - z;
  48. te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1;
  49. return this;
  50. };
  51. Frustum.prototype.setFromProjectionMatrix = function ( m ) {
  52. const planes = this.planes;
  53. const me = m.elements;
  54. const me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ];
  55. const me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ];
  56. const me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ];
  57. const me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ];
  58. planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize();
  59. planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize();
  60. planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize();
  61. planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize();
  62. planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize();
  63. planes[ 5 ].setComponents( me2, me6, me10, me14 ).normalize();
  64. return this;
  65. };
  66. const _frustum = new Frustum();
  67. const _projScreenMatrix = new Matrix4();
  68. const _vector3 = new Vector3();
  69. class WebGPURenderer {
  70. constructor( parameters = {} ) {
  71. this.isWebGPURenderer = true;
  72. // public
  73. this.domElement = ( parameters.canvas !== undefined ) ? parameters.canvas : this._createCanvasElement();
  74. this.autoClear = true;
  75. this.autoClearColor = true;
  76. this.autoClearDepth = true;
  77. this.autoClearStencil = true;
  78. this.outputColorSpace = SRGBColorSpace;
  79. this.toneMapping = NoToneMapping;
  80. this.toneMappingExposure = 1.0;
  81. this.sortObjects = true;
  82. // internals
  83. this._parameters = Object.assign( {}, parameters );
  84. this._pixelRatio = 1;
  85. this._width = this.domElement.width;
  86. this._height = this.domElement.height;
  87. this._viewport = null;
  88. this._scissor = null;
  89. this._adapter = null;
  90. this._device = null;
  91. this._context = null;
  92. this._colorBuffer = null;
  93. this._depthBuffer = null;
  94. this._info = null;
  95. this._properties = null;
  96. this._attributes = null;
  97. this._geometries = null;
  98. this._nodes = null;
  99. this._bindings = null;
  100. this._objects = null;
  101. this._renderPipelines = null;
  102. this._computePipelines = null;
  103. this._renderLists = null;
  104. this._renderStates = null;
  105. this._textures = null;
  106. this._background = null;
  107. this._animation = new WebGPUAnimation();
  108. this._currentRenderState = null;
  109. this._lastRenderState = null;
  110. this._opaqueSort = null;
  111. this._transparentSort = null;
  112. this._clearAlpha = 1;
  113. this._clearColor = new Color( 0x000000 );
  114. this._clearDepth = 1;
  115. this._clearStencil = 0;
  116. this._renderTarget = null;
  117. this._initialized = false;
  118. this._initPromise = null;
  119. // some parameters require default values other than "undefined"
  120. this._parameters.antialias = ( parameters.antialias === true );
  121. if ( this._parameters.antialias === true ) {
  122. this._parameters.sampleCount = ( parameters.sampleCount === undefined ) ? 4 : parameters.sampleCount;
  123. } else {
  124. this._parameters.sampleCount = 1;
  125. }
  126. this._parameters.requiredLimits = ( parameters.requiredLimits === undefined ) ? {} : parameters.requiredLimits;
  127. // backwards compatibility
  128. this.shadow = {
  129. shadowMap: {}
  130. };
  131. }
  132. async init() {
  133. if ( this._initialized === true ) {
  134. throw new Error( 'WebGPURenderer: Device has already been initialized.' );
  135. }
  136. if ( this._initPromise !== null ) {
  137. return this._initPromise;
  138. }
  139. this._initPromise = new Promise( async ( resolve, reject ) => {
  140. const parameters = this._parameters;
  141. const adapterOptions = {
  142. powerPreference: parameters.powerPreference
  143. };
  144. const adapter = await navigator.gpu.requestAdapter( adapterOptions );
  145. if ( adapter === null ) {
  146. reject( new Error( 'WebGPURenderer: Unable to create WebGPU adapter.' ) );
  147. return;
  148. }
  149. // feature support
  150. const features = Object.values( GPUFeatureName );
  151. const supportedFeatures = [];
  152. for ( const name of features ) {
  153. if ( adapter.features.has( name ) ) {
  154. supportedFeatures.push( name );
  155. }
  156. }
  157. const deviceDescriptor = {
  158. requiredFeatures: supportedFeatures,
  159. requiredLimits: parameters.requiredLimits
  160. };
  161. const device = await adapter.requestDevice( deviceDescriptor );
  162. const context = ( parameters.context !== undefined ) ? parameters.context : this.domElement.getContext( 'webgpu' );
  163. this._adapter = adapter;
  164. this._device = device;
  165. this._context = context;
  166. this._configureContext();
  167. this._info = new WebGPUInfo();
  168. this._properties = new WebGPUProperties();
  169. this._attributes = new WebGPUAttributes( device );
  170. this._geometries = new WebGPUGeometries( this._attributes, this._properties, this._info );
  171. this._textures = new WebGPUTextures( device, this._properties, this._info );
  172. this._utils = new WebGPUUtils( this );
  173. this._nodes = new WebGPUNodes( this, this._properties );
  174. this._objects = new WebGPURenderObjects( this, this._nodes, this._geometries, this._info );
  175. this._computePipelines = new WebGPUComputePipelines( device, this._nodes );
  176. this._renderPipelines = new WebGPURenderPipelines( device, this._nodes, this._utils );
  177. this._bindings = this._renderPipelines.bindings = new WebGPUBindings( device, this._info, this._properties, this._textures, this._renderPipelines, this._computePipelines, this._attributes, this._nodes );
  178. this._renderLists = new WebGPURenderLists();
  179. this._renderStates = new WebGPURenderStates();
  180. this._background = new WebGPUBackground( this, this._properties );
  181. //
  182. this._setupColorBuffer();
  183. this._setupDepthBuffer();
  184. this._animation.setNodes( this._nodes );
  185. this._animation.start();
  186. this._initialized = true;
  187. resolve();
  188. } );
  189. return this._initPromise;
  190. }
  191. async render( scene, camera ) {
  192. if ( this._initialized === false ) await this.init();
  193. // preserve render tree
  194. const nodeFrame = this._nodes.nodeFrame;
  195. const previousRenderId = nodeFrame.renderId;
  196. const previousRenderState = this._currentRenderState;
  197. //
  198. const renderState = this._renderStates.get( scene, camera );
  199. const renderTarget = this._renderTarget;
  200. this._currentRenderState = renderState;
  201. nodeFrame.renderId ++;
  202. //
  203. if ( this._animation.isAnimating === false ) nodeFrame.update();
  204. if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
  205. if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
  206. if ( this._info.autoReset === true ) this._info.reset();
  207. this._info.render.frame ++;
  208. _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
  209. _frustum.setFromProjectionMatrix( _projScreenMatrix );
  210. const renderList = this._renderLists.get( scene, camera );
  211. renderList.init();
  212. this._projectObject( scene, camera, 0, renderList );
  213. renderList.finish();
  214. if ( this.sortObjects === true ) {
  215. renderList.sort( this._opaqueSort, this._transparentSort );
  216. }
  217. // prepare render pass descriptor
  218. renderState.descriptorGPU = {
  219. colorAttachments: [ {
  220. view: null
  221. } ],
  222. depthStencilAttachment: {
  223. view: null
  224. }
  225. };
  226. const colorAttachment = renderState.descriptorGPU.colorAttachments[ 0 ];
  227. const depthStencilAttachment = renderState.descriptorGPU.depthStencilAttachment;
  228. if ( renderTarget !== null ) {
  229. this._textures.initRenderTarget( renderTarget );
  230. // @TODO: Support RenderTarget with antialiasing.
  231. const renderTargetProperties = this._properties.get( renderTarget );
  232. colorAttachment.view = renderTargetProperties.colorTextureGPU.createView();
  233. depthStencilAttachment.view = renderTargetProperties.depthTextureGPU.createView();
  234. renderState.stencil = renderTarget.depthTexture ? renderTarget.depthTexture.format !== DepthFormat : true;
  235. } else {
  236. if ( this._parameters.antialias === true ) {
  237. colorAttachment.view = this._colorBuffer.createView();
  238. colorAttachment.resolveTarget = this._context.getCurrentTexture().createView();
  239. } else {
  240. colorAttachment.view = this._context.getCurrentTexture().createView();
  241. colorAttachment.resolveTarget = undefined;
  242. }
  243. depthStencilAttachment.view = this._depthBuffer.createView();
  244. }
  245. //
  246. this._nodes.updateEnvironment( scene );
  247. this._nodes.updateFog( scene );
  248. this._nodes.updateBackground( scene );
  249. this._nodes.updateToneMapping();
  250. //
  251. this._background.update( scene, renderList, renderState );
  252. // start render pass
  253. const device = this._device;
  254. renderState.encoderGPU = device.createCommandEncoder( {} );
  255. renderState.currentPassGPU = renderState.encoderGPU.beginRenderPass( renderState.descriptorGPU );
  256. // global rasterization settings for all renderable objects
  257. const vp = this._viewport;
  258. if ( vp !== null ) {
  259. const width = Math.floor( vp.width * this._pixelRatio );
  260. const height = Math.floor( vp.height * this._pixelRatio );
  261. renderState.currentPassGPU.setViewport( vp.x, vp.y, width, height, vp.minDepth, vp.maxDepth );
  262. }
  263. const sc = this._scissor;
  264. if ( sc !== null ) {
  265. const width = Math.floor( sc.width * this._pixelRatio );
  266. const height = Math.floor( sc.height * this._pixelRatio );
  267. renderState.currentPassGPU.setScissorRect( sc.x, sc.y, width, height );
  268. }
  269. // process render lists
  270. const opaqueObjects = renderList.opaque;
  271. const transparentObjects = renderList.transparent;
  272. const lightsNode = renderList.lightsNode;
  273. if ( opaqueObjects.length > 0 ) this._renderObjects( opaqueObjects, camera, scene, lightsNode, renderState );
  274. if ( transparentObjects.length > 0 ) this._renderObjects( transparentObjects, camera, scene, lightsNode, renderState );
  275. // finish render pass
  276. renderState.currentPassGPU.end();
  277. device.queue.submit( [ renderState.encoderGPU.finish() ] );
  278. // restore render tree
  279. nodeFrame.renderId = previousRenderId;
  280. this._currentRenderState = previousRenderState;
  281. this._lastRenderState = renderState;
  282. }
  283. setAnimationLoop( callback ) {
  284. if ( this._initialized === false ) this.init();
  285. const animation = this._animation;
  286. animation.setAnimationLoop( callback );
  287. ( callback === null ) ? animation.stop() : animation.start();
  288. }
  289. async getArrayBuffer( attribute ) {
  290. return await this._attributes.getArrayBuffer( attribute );
  291. }
  292. getContext() {
  293. return this._context;
  294. }
  295. getPixelRatio() {
  296. return this._pixelRatio;
  297. }
  298. getDrawingBufferSize( target ) {
  299. return target.set( this._width * this._pixelRatio, this._height * this._pixelRatio ).floor();
  300. }
  301. getSize( target ) {
  302. return target.set( this._width, this._height );
  303. }
  304. setPixelRatio( value = 1 ) {
  305. this._pixelRatio = value;
  306. this.setSize( this._width, this._height, false );
  307. }
  308. setDrawingBufferSize( width, height, pixelRatio ) {
  309. this._width = width;
  310. this._height = height;
  311. this._pixelRatio = pixelRatio;
  312. this.domElement.width = Math.floor( width * pixelRatio );
  313. this.domElement.height = Math.floor( height * pixelRatio );
  314. this._configureContext();
  315. this._setupColorBuffer();
  316. this._setupDepthBuffer();
  317. }
  318. setSize( width, height, updateStyle = true ) {
  319. this._width = width;
  320. this._height = height;
  321. this.domElement.width = Math.floor( width * this._pixelRatio );
  322. this.domElement.height = Math.floor( height * this._pixelRatio );
  323. if ( updateStyle === true ) {
  324. this.domElement.style.width = width + 'px';
  325. this.domElement.style.height = height + 'px';
  326. }
  327. this._configureContext();
  328. this._setupColorBuffer();
  329. this._setupDepthBuffer();
  330. }
  331. setOpaqueSort( method ) {
  332. this._opaqueSort = method;
  333. }
  334. setTransparentSort( method ) {
  335. this._transparentSort = method;
  336. }
  337. getScissor( target ) {
  338. const scissor = this._scissor;
  339. target.x = scissor.x;
  340. target.y = scissor.y;
  341. target.width = scissor.width;
  342. target.height = scissor.height;
  343. return target;
  344. }
  345. setScissor( x, y, width, height ) {
  346. if ( x === null ) {
  347. this._scissor = null;
  348. } else {
  349. this._scissor = {
  350. x: x,
  351. y: y,
  352. width: width,
  353. height: height
  354. };
  355. }
  356. }
  357. copyFramebufferToRenderTarget( renderTarget ) {
  358. const renderState = this._currentRenderState;
  359. const { encoderGPU, descriptorGPU } = renderState;
  360. const texture = renderTarget.texture;
  361. texture.internalFormat = GPUTextureFormat.BGRA8Unorm;
  362. this._textures.initRenderTarget( renderTarget );
  363. const sourceGPU = this._context.getCurrentTexture();
  364. const destinationGPU = this._textures.getTextureGPU( texture );
  365. renderState.currentPassGPU.end();
  366. encoderGPU.copyTextureToTexture(
  367. {
  368. texture: sourceGPU
  369. },
  370. {
  371. texture: destinationGPU
  372. },
  373. [
  374. texture.image.width,
  375. texture.image.height
  376. ]
  377. );
  378. descriptorGPU.colorAttachments[ 0 ].loadOp = GPULoadOp.Load;
  379. if ( renderState.depth ) descriptorGPU.depthStencilAttachment.depthLoadOp = GPULoadOp.Load;
  380. if ( renderState.stencil ) descriptorGPU.depthStencilAttachment.stencilLoadOp = GPULoadOp.Load;
  381. renderState.currentPassGPU = encoderGPU.beginRenderPass( descriptorGPU );
  382. }
  383. getViewport( target ) {
  384. const viewport = this._viewport;
  385. target.x = viewport.x;
  386. target.y = viewport.y;
  387. target.width = viewport.width;
  388. target.height = viewport.height;
  389. target.minDepth = viewport.minDepth;
  390. target.maxDepth = viewport.maxDepth;
  391. return target;
  392. }
  393. setViewport( x, y, width, height, minDepth = 0, maxDepth = 1 ) {
  394. if ( x === null ) {
  395. this._viewport = null;
  396. } else {
  397. this._viewport = {
  398. x: x,
  399. y: y,
  400. width: width,
  401. height: height,
  402. minDepth: minDepth,
  403. maxDepth: maxDepth
  404. };
  405. }
  406. }
  407. getClearColor( target ) {
  408. return target.copy( this._clearColor );
  409. }
  410. setClearColor( color, alpha = 1 ) {
  411. this._clearColor.set( color );
  412. this._clearAlpha = alpha;
  413. }
  414. getClearAlpha() {
  415. return this._clearAlpha;
  416. }
  417. setClearAlpha( alpha ) {
  418. this._clearAlpha = alpha;
  419. }
  420. getClearDepth() {
  421. return this._clearDepth;
  422. }
  423. setClearDepth( depth ) {
  424. this._clearDepth = depth;
  425. }
  426. getClearStencil() {
  427. return this._clearStencil;
  428. }
  429. setClearStencil( stencil ) {
  430. this._clearStencil = stencil;
  431. }
  432. clear( color = true, depth = true, stencil = true ) {
  433. const renderState = this._currentRenderState || this._lastRenderState;
  434. if ( renderState === null ) return;
  435. depth = depth && renderState.depth;
  436. stencil = stencil && renderState.stencil;
  437. const descriptorGPU = renderState.descriptorGPU;
  438. const colorAttachment = descriptorGPU.colorAttachments[ 0 ];
  439. // @TODO: Include render target in clear operation.
  440. if ( this._parameters.antialias === true ) {
  441. colorAttachment.view = this._colorBuffer.createView();
  442. colorAttachment.resolveTarget = this._context.getCurrentTexture().createView();
  443. } else {
  444. colorAttachment.view = this._context.getCurrentTexture().createView();
  445. colorAttachment.resolveTarget = undefined;
  446. }
  447. descriptorGPU.depthStencilAttachment.view = this._depthBuffer.createView();
  448. if ( color ) {
  449. colorAttachment.loadOp = GPULoadOp.Clear;
  450. colorAttachment.clearValue = { r: this._clearColor.r, g: this._clearColor.g, b: this._clearColor.b, a: this._clearAlpha };
  451. }
  452. if ( depth ) {
  453. descriptorGPU.depthStencilAttachment.depthLoadOp = GPULoadOp.Clear;
  454. descriptorGPU.depthStencilAttachment.depthClearValue = this._clearDepth;
  455. }
  456. if ( stencil ) {
  457. descriptorGPU.depthStencilAttachment.stencilLoadOp = GPULoadOp.Clear;
  458. descriptorGPU.depthStencilAttachment.stencilClearValue = this._clearStencil;
  459. }
  460. renderState.encoderGPU = this._device.createCommandEncoder( {} );
  461. renderState.currentPassGPU = renderState.encoderGPU.beginRenderPass( renderState.descriptorGPU );
  462. renderState.currentPassGPU.end();
  463. this._device.queue.submit( [ renderState.encoderGPU.finish() ] );
  464. }
  465. clearColor() {
  466. this.clear( true, false, false );
  467. }
  468. clearDepth() {
  469. this.clear( false, true, false );
  470. }
  471. clearStencil() {
  472. this.clear( false, false, true );
  473. }
  474. dispose() {
  475. this._objects.dispose();
  476. this._properties.dispose();
  477. this._renderPipelines.dispose();
  478. this._computePipelines.dispose();
  479. this._nodes.dispose();
  480. this._bindings.dispose();
  481. this._info.dispose();
  482. this._renderLists.dispose();
  483. this._renderStates.dispose();
  484. this._textures.dispose();
  485. this.setRenderTarget( null );
  486. this.setAnimationLoop( null );
  487. }
  488. setRenderTarget( renderTarget ) {
  489. this._renderTarget = renderTarget;
  490. }
  491. async compute( ...computeNodes ) {
  492. if ( this._initialized === false ) await this.init();
  493. const device = this._device;
  494. const computePipelines = this._computePipelines;
  495. const cmdEncoder = device.createCommandEncoder( {} );
  496. const passEncoder = cmdEncoder.beginComputePass();
  497. for ( const computeNode of computeNodes ) {
  498. // onInit
  499. if ( computePipelines.has( computeNode ) === false ) {
  500. computeNode.onInit( { renderer: this } );
  501. }
  502. // pipeline
  503. const pipeline = computePipelines.get( computeNode );
  504. passEncoder.setPipeline( pipeline );
  505. // node
  506. //this._nodes.update( computeNode );
  507. // bind group
  508. const bindGroup = this._bindings.getForCompute( computeNode ).group;
  509. this._bindings.update( computeNode );
  510. passEncoder.setBindGroup( 0, bindGroup );
  511. passEncoder.dispatchWorkgroups( computeNode.dispatchCount );
  512. }
  513. passEncoder.end();
  514. device.queue.submit( [ cmdEncoder.finish() ] );
  515. }
  516. getRenderTarget() {
  517. return this._renderTarget;
  518. }
  519. hasFeature( name ) {
  520. const adapter = this._adapter || staticAdapter;
  521. //
  522. const features = Object.values( GPUFeatureName );
  523. if ( features.includes( name ) === false ) {
  524. throw new Error( 'THREE.WebGPURenderer: Unknown WebGPU GPU feature: ' + name );
  525. }
  526. //
  527. return adapter.features.has( name );
  528. }
  529. _projectObject( object, camera, groupOrder, renderList ) {
  530. if ( object.visible === false ) return;
  531. const visible = object.layers.test( camera.layers );
  532. if ( visible ) {
  533. if ( object.isGroup ) {
  534. groupOrder = object.renderOrder;
  535. } else if ( object.isLOD ) {
  536. if ( object.autoUpdate === true ) object.update( camera );
  537. } else if ( object.isLight ) {
  538. renderList.pushLight( object );
  539. } else if ( object.isSprite ) {
  540. if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) {
  541. if ( this.sortObjects === true ) {
  542. _vector3.setFromMatrixPosition( object.matrixWorld ).applyMatrix4( _projScreenMatrix );
  543. }
  544. const geometry = object.geometry;
  545. const material = object.material;
  546. if ( material.visible ) {
  547. renderList.push( object, geometry, material, groupOrder, _vector3.z, null );
  548. }
  549. }
  550. } else if ( object.isLineLoop ) {
  551. console.error( 'THREE.WebGPURenderer: Objects of type THREE.LineLoop are not supported. Please use THREE.Line or THREE.LineSegments.' );
  552. } else if ( object.isMesh || object.isLine || object.isPoints ) {
  553. if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) {
  554. const geometry = object.geometry;
  555. const material = object.material;
  556. if ( this.sortObjects === true ) {
  557. if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
  558. _vector3
  559. .copy( geometry.boundingSphere.center )
  560. .applyMatrix4( object.matrixWorld )
  561. .applyMatrix4( _projScreenMatrix );
  562. }
  563. if ( Array.isArray( material ) ) {
  564. const groups = geometry.groups;
  565. for ( let i = 0, l = groups.length; i < l; i ++ ) {
  566. const group = groups[ i ];
  567. const groupMaterial = material[ group.materialIndex ];
  568. if ( groupMaterial && groupMaterial.visible ) {
  569. renderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group );
  570. }
  571. }
  572. } else if ( material.visible ) {
  573. renderList.push( object, geometry, material, groupOrder, _vector3.z, null );
  574. }
  575. }
  576. }
  577. }
  578. const children = object.children;
  579. for ( let i = 0, l = children.length; i < l; i ++ ) {
  580. this._projectObject( children[ i ], camera, groupOrder, renderList );
  581. }
  582. }
  583. _renderObjects( renderList, camera, scene, lightsNode ) {
  584. // process renderable objects
  585. for ( let i = 0, il = renderList.length; i < il; i ++ ) {
  586. const renderItem = renderList[ i ];
  587. // @TODO: Add support for multiple materials per object. This will require to extract
  588. // the material from the renderItem object and pass it with its group data to _renderObject().
  589. const { object, geometry, material, group } = renderItem;
  590. if ( camera.isArrayCamera ) {
  591. const cameras = camera.cameras;
  592. for ( let j = 0, jl = cameras.length; j < jl; j ++ ) {
  593. const camera2 = cameras[ j ];
  594. if ( object.layers.test( camera2.layers ) ) {
  595. const vp = camera2.viewport;
  596. const minDepth = ( vp.minDepth === undefined ) ? 0 : vp.minDepth;
  597. const maxDepth = ( vp.maxDepth === undefined ) ? 1 : vp.maxDepth;
  598. this._currentRenderState.currentPassGPU.setViewport( vp.x, vp.y, vp.width, vp.height, minDepth, maxDepth );
  599. this._renderObject( object, scene, camera2, geometry, material, group, lightsNode );
  600. }
  601. }
  602. } else {
  603. this._renderObject( object, scene, camera, geometry, material, group, lightsNode );
  604. }
  605. }
  606. }
  607. _renderObject( object, scene, camera, geometry, material, group, lightsNode ) {
  608. material = scene.overrideMaterial !== null ? scene.overrideMaterial : material;
  609. //
  610. object.onBeforeRender( this, scene, camera, geometry, material, group );
  611. //
  612. const renderObject = this._getRenderObject( object, material, scene, camera, lightsNode );
  613. //
  614. this._nodes.updateBefore( renderObject );
  615. //
  616. const passEncoder = this._currentRenderState.currentPassGPU;
  617. const info = this._info;
  618. //
  619. object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
  620. object.normalMatrix.getNormalMatrix( object.modelViewMatrix );
  621. // updates
  622. this._nodes.update( renderObject );
  623. this._geometries.update( renderObject );
  624. this._bindings.update( renderObject );
  625. // pipeline
  626. const renderPipeline = this._renderPipelines.get( renderObject );
  627. passEncoder.setPipeline( renderPipeline.pipeline );
  628. // bind group
  629. const bindGroup = this._bindings.get( renderObject ).group;
  630. passEncoder.setBindGroup( 0, bindGroup );
  631. // index
  632. const index = this._geometries.getIndex( renderObject );
  633. const hasIndex = ( index !== null );
  634. if ( hasIndex === true ) {
  635. this._setupIndexBuffer( renderObject );
  636. }
  637. // vertex buffers
  638. this._setupVertexBuffers( renderObject );
  639. // draw
  640. const drawRange = geometry.drawRange;
  641. const firstVertex = drawRange.start;
  642. const instanceCount = geometry.isInstancedBufferGeometry ? geometry.instanceCount : ( object.isInstancedMesh ? object.count : 1 );
  643. if ( hasIndex === true ) {
  644. const indexCount = ( drawRange.count !== Infinity ) ? drawRange.count : index.count;
  645. passEncoder.drawIndexed( indexCount, instanceCount, firstVertex, 0, 0 );
  646. info.update( object, indexCount, instanceCount );
  647. } else {
  648. const positionAttribute = geometry.attributes.position;
  649. const vertexCount = ( drawRange.count !== Infinity ) ? drawRange.count : positionAttribute.count;
  650. passEncoder.draw( vertexCount, instanceCount, firstVertex, 0 );
  651. info.update( object, vertexCount, instanceCount );
  652. }
  653. }
  654. _getRenderObject( object, material, scene, camera, lightsNode ) {
  655. const renderObject = this._objects.get( object, material, scene, camera, lightsNode );
  656. const renderObjectProperties = this._properties.get( renderObject );
  657. if ( renderObjectProperties.initialized !== true ) {
  658. renderObjectProperties.initialized = true;
  659. const dispose = () => {
  660. this._renderPipelines.remove( renderObject );
  661. this._nodes.remove( renderObject );
  662. this._properties.remove( renderObject );
  663. this._objects.remove( object, material, scene, camera, lightsNode );
  664. renderObject.material.removeEventListener( 'dispose', dispose );
  665. };
  666. renderObject.material.addEventListener( 'dispose', dispose );
  667. }
  668. const cacheKey = renderObject.getCacheKey();
  669. if ( renderObjectProperties.cacheKey !== cacheKey ) {
  670. renderObjectProperties.cacheKey = cacheKey;
  671. this._renderPipelines.remove( renderObject );
  672. this._nodes.remove( renderObject );
  673. }
  674. return renderObject;
  675. }
  676. _setupIndexBuffer( renderObject ) {
  677. const index = this._geometries.getIndex( renderObject );
  678. const passEncoder = this._currentRenderState.currentPassGPU;
  679. const buffer = this._attributes.get( index ).buffer;
  680. const indexFormat = ( index.array instanceof Uint16Array ) ? GPUIndexFormat.Uint16 : GPUIndexFormat.Uint32;
  681. passEncoder.setIndexBuffer( buffer, indexFormat );
  682. }
  683. _setupVertexBuffers( renderObject ) {
  684. const passEncoder = this._currentRenderState.currentPassGPU;
  685. const attributes = renderObject.getAttributes();
  686. for ( let i = 0, l = attributes.length; i < l; i ++ ) {
  687. const buffer = this._attributes.get( attributes[ i ] ).buffer;
  688. passEncoder.setVertexBuffer( i, buffer );
  689. }
  690. }
  691. _setupColorBuffer() {
  692. const device = this._device;
  693. if ( device ) {
  694. if ( this._colorBuffer ) this._colorBuffer.destroy();
  695. this._colorBuffer = this._device.createTexture( {
  696. label: 'colorBuffer',
  697. size: {
  698. width: Math.floor( this._width * this._pixelRatio ),
  699. height: Math.floor( this._height * this._pixelRatio ),
  700. depthOrArrayLayers: 1
  701. },
  702. sampleCount: this._parameters.sampleCount,
  703. format: GPUTextureFormat.BGRA8Unorm,
  704. usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC
  705. } );
  706. }
  707. }
  708. _setupDepthBuffer() {
  709. const device = this._device;
  710. if ( device ) {
  711. if ( this._depthBuffer ) this._depthBuffer.destroy();
  712. this._depthBuffer = this._device.createTexture( {
  713. label: 'depthBuffer',
  714. size: {
  715. width: Math.floor( this._width * this._pixelRatio ),
  716. height: Math.floor( this._height * this._pixelRatio ),
  717. depthOrArrayLayers: 1
  718. },
  719. sampleCount: this._parameters.sampleCount,
  720. format: GPUTextureFormat.Depth24PlusStencil8,
  721. usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC
  722. } );
  723. }
  724. }
  725. _configureContext() {
  726. const device = this._device;
  727. if ( device ) {
  728. this._context.configure( {
  729. device: device,
  730. format: GPUTextureFormat.BGRA8Unorm,
  731. usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
  732. alphaMode: 'premultiplied'
  733. } );
  734. }
  735. }
  736. _createCanvasElement() {
  737. const canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );
  738. canvas.style.display = 'block';
  739. return canvas;
  740. }
  741. }
  742. export default WebGPURenderer;