WebGPURenderer.js 27 KB

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