webgpuEngine.ts 101 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456
  1. import { Logger } from "../Misc/logger";
  2. import { Nullable, DataArray, IndicesArray, FloatArray } from "../types";
  3. import { Scene } from "../scene";
  4. import { Color4 } from "../Maths/math";
  5. import { Scalar } from "../Maths/math.scalar";
  6. import { Engine } from "../Engines/engine";
  7. import { InstancingAttributeInfo } from "../Engines/instancingAttributeInfo";
  8. import { RenderTargetCreationOptions } from "../Materials/Textures/renderTargetCreationOptions";
  9. import { InternalTexture, InternalTextureSource } from "../Materials/Textures/internalTexture";
  10. import { IEffectCreationOptions, Effect } from "../Materials/effect";
  11. import { EffectFallbacks } from "../Materials/effectFallbacks";
  12. import { _TimeToken } from "../Instrumentation/timeToken";
  13. import { Constants } from "./constants";
  14. import { WebGPUConstants } from "./WebGPU/webgpuConstants";
  15. import { VertexBuffer } from "../Meshes/buffer";
  16. import { WebGPUPipelineContext, IWebGPUPipelineContextVertexInputsCache } from './WebGPU/webgpuPipelineContext';
  17. import { IPipelineContext } from './IPipelineContext';
  18. import { DataBuffer } from '../Meshes/dataBuffer';
  19. import { WebGPUDataBuffer } from '../Meshes/WebGPU/webgpuDataBuffer';
  20. import { IInternalTextureLoader } from "../Materials/Textures/internalTextureLoader";
  21. import { BaseTexture } from "../Materials/Textures/baseTexture";
  22. import { IShaderProcessor } from "./Processors/iShaderProcessor";
  23. import { WebGPUShaderProcessor } from "./WebGPU/webgpuShaderProcessors";
  24. import { ShaderProcessingContext } from "./Processors/shaderProcessingOptions";
  25. import { WebGPUShaderProcessingContext } from "./WebGPU/webgpuShaderProcessingContext";
  26. import { Tools } from "../Misc/tools";
  27. /**
  28. * Options to load the associated Glslang library
  29. */
  30. export interface GlslangOptions {
  31. /**
  32. * Defines an existing instance of Glslang (usefull in modules who do not access the global instance).
  33. */
  34. glslang?: any;
  35. /**
  36. * Defines the URL of the glslang JS File.
  37. */
  38. jsPath?: string;
  39. /**
  40. * Defines the URL of the glslang WASM File.
  41. */
  42. wasmPath?: string;
  43. }
  44. /**
  45. * Options to create the WebGPU engine
  46. */
  47. export interface WebGPUEngineOptions extends GPURequestAdapterOptions {
  48. /**
  49. * If delta time between frames should be constant
  50. * @see https://doc.babylonjs.com/babylon101/animations#deterministic-lockstep
  51. */
  52. deterministicLockstep?: boolean;
  53. /**
  54. * Maximum about of steps between frames (Default: 4)
  55. * @see https://doc.babylonjs.com/babylon101/animations#deterministic-lockstep
  56. */
  57. lockstepMaxSteps?: number;
  58. /**
  59. * Defines the seconds between each deterministic lock step
  60. */
  61. timeStep?: number;
  62. /**
  63. * Defines that engine should ignore modifying touch action attribute and style
  64. * If not handle, you might need to set it up on your side for expected touch devices behavior.
  65. */
  66. doNotHandleTouchAction?: boolean;
  67. /**
  68. * Defines if webaudio should be initialized as well
  69. * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music
  70. */
  71. audioEngine?: boolean;
  72. /**
  73. * Defines the category of adapter to use.
  74. * Is it the discrete or integrated device.
  75. */
  76. powerPreference?: GPUPowerPreference;
  77. /**
  78. * Defines the device descriptor used to create a device.
  79. */
  80. deviceDescriptor?: GPUDeviceDescriptor;
  81. /**
  82. * Defines the requested Swap Chain Format.
  83. */
  84. swapChainFormat?: GPUTextureFormat;
  85. /**
  86. * Defines wether MSAA is enabled on the canvas.
  87. */
  88. antialiasing?: boolean;
  89. }
  90. /**
  91. * The web GPU engine class provides support for WebGPU version of babylon.js.
  92. */
  93. export class WebGPUEngine extends Engine {
  94. // Default glslang options.
  95. private static readonly _glslangDefaultOptions: GlslangOptions = {
  96. jsPath: "https://preview.babylonjs.com/glslang/glslang.js",
  97. wasmPath: "https://preview.babylonjs.com/glslang/glslang.wasm"
  98. };
  99. // Page Life cycle and constants
  100. private readonly _uploadEncoderDescriptor = { label: "upload" };
  101. private readonly _renderEncoderDescriptor = { label: "render" };
  102. private readonly _clearDepthValue = 1;
  103. private readonly _clearStencilValue = 0;
  104. private readonly _defaultSampleCount = 4; // Only supported value for now.
  105. // Engine Life Cycle
  106. private _canvas: HTMLCanvasElement;
  107. private _options: WebGPUEngineOptions;
  108. private _glslang: any = null;
  109. private _adapter: GPUAdapter;
  110. private _device: GPUDevice;
  111. private _context: GPUCanvasContext;
  112. private _swapChain: GPUSwapChain;
  113. private _mainPassSampleCount: number;
  114. // Some of the internal state might change during the render pass.
  115. // This happens mainly during clear for the state
  116. // And when the frame starts to swap the target texture from the swap chain
  117. private _mainTexture: GPUTexture;
  118. private _depthTexture: GPUTexture;
  119. private _mainColorAttachments: GPURenderPassColorAttachmentDescriptor[];
  120. private _mainTextureExtends: GPUExtent3D;
  121. private _mainDepthAttachment: GPURenderPassDepthStencilAttachmentDescriptor;
  122. // Frame Life Cycle (recreated each frame)
  123. private _uploadEncoder: GPUCommandEncoder;
  124. private _renderEncoder: GPUCommandEncoder;
  125. private _commandBuffers: GPUCommandBuffer[] = [null as any, null as any];
  126. // Frame Buffer Life Cycle (recreated for each render target pass)
  127. private _currentRenderPass: Nullable<GPURenderPassEncoder> = null;
  128. // DrawCall Life Cycle
  129. // Effect is on the parent class
  130. // protected _currentEffect: Nullable<Effect> = null;
  131. private _currentVertexBuffers: Nullable<{ [key: string]: Nullable<VertexBuffer> }> = null;
  132. private _currentIndexBuffer: Nullable<DataBuffer> = null;
  133. private __colorWrite = true;
  134. private _uniformsBuffers: { [name: string]: WebGPUDataBuffer } = {};
  135. private _maxBufferChunk = 1024 * 1024 * 15;
  136. // Caches
  137. private _compiledShaders: { [key: string]: {
  138. stages: GPURenderPipelineStageDescriptor,
  139. availableAttributes: { [key: string]: number },
  140. availableUBOs: { [key: string]: { setIndex: number, bindingIndex: number} },
  141. availableSamplers: { [key: string]: { setIndex: number, bindingIndex: number} },
  142. orderedAttributes: string[],
  143. orderedUBOsAndSamplers: { name: string, isSampler: boolean }[][],
  144. leftOverUniforms: { name: string, type: string, length: number }[],
  145. leftOverUniformsByName: { [name: string]: string },
  146. sources: {
  147. vertex: string
  148. fragment: string,
  149. }
  150. } } = {};
  151. // TODO WEBGPU. Texture Management. Temporary...
  152. private _decodeCanvas = document.createElement("canvas");
  153. private _decodeEngine = new Engine(this._decodeCanvas, false, {
  154. alpha: true,
  155. premultipliedAlpha: false,
  156. }, false);
  157. /**
  158. * Gets a boolean indicating that the engine supports uniform buffers
  159. * @see http://doc.babylonjs.com/features/webgl2#uniform-buffer-objets
  160. */
  161. public get supportsUniformBuffers(): boolean {
  162. return true;
  163. }
  164. /**
  165. * Create a new instance of the gpu engine.
  166. * @param canvas Defines the canvas to use to display the result
  167. * @param options Defines the options passed to the engine to create the GPU context dependencies
  168. */
  169. public constructor(canvas: HTMLCanvasElement, options: WebGPUEngineOptions = {}) {
  170. super(null);
  171. options.deviceDescriptor = options.deviceDescriptor || { };
  172. options.swapChainFormat = options.swapChainFormat || WebGPUConstants.GPUTextureFormat_bgra8unorm;
  173. options.antialiasing = options.antialiasing === undefined ? true : options.antialiasing;
  174. this._decodeEngine.getCaps().textureFloat = false;
  175. this._decodeEngine.getCaps().textureFloatRender = false;
  176. this._decodeEngine.getCaps().textureHalfFloat = false;
  177. this._decodeEngine.getCaps().textureHalfFloatRender = false;
  178. Logger.Log(`Babylon.js v${Engine.Version} - WebGPU engine`);
  179. if (!navigator.gpu) {
  180. Logger.Error("WebGPU is not supported by your browser.");
  181. return;
  182. }
  183. this._isWebGPU = true;
  184. this._shaderPlatformName = "WEBGPU";
  185. if (options.deterministicLockstep === undefined) {
  186. options.deterministicLockstep = false;
  187. }
  188. if (options.lockstepMaxSteps === undefined) {
  189. options.lockstepMaxSteps = 4;
  190. }
  191. if (options.audioEngine === undefined) {
  192. options.audioEngine = true;
  193. }
  194. this._deterministicLockstep = options.deterministicLockstep;
  195. this._lockstepMaxSteps = options.lockstepMaxSteps;
  196. this._timeStep = options.timeStep || 1 / 60;
  197. this._doNotHandleContextLost = true;
  198. this._canvas = canvas;
  199. this._options = options;
  200. this._hardwareScalingLevel = 1;
  201. this._mainPassSampleCount = options.antialiasing ? this._defaultSampleCount : 1;
  202. this._sharedInit(canvas, !!options.doNotHandleTouchAction, options.audioEngine);
  203. // TODO. WEBGPU. Use real way to do it.
  204. this._canvas.style.transform = "scaleY(-1)";
  205. }
  206. //------------------------------------------------------------------------------
  207. // Initialization
  208. //------------------------------------------------------------------------------
  209. /**
  210. * Initializes the WebGPU context and dependencies.
  211. * @param glslangOptions Defines the GLSLang compiler options if necessary
  212. * @returns a promise notifying the readiness of the engine.
  213. */
  214. public initAsync(glslangOptions?: GlslangOptions): Promise<void> {
  215. return this._initGlslang(glslangOptions)
  216. .then((glslang: any) => {
  217. this._glslang = glslang;
  218. return navigator.gpu!.requestAdapter(this._options);
  219. })
  220. .then((adapter: GPUAdapter) => {
  221. this._adapter = adapter;
  222. return this._adapter.requestDevice(this._options.deviceDescriptor);
  223. })
  224. .then((device: GPUDevice) => this._device = device)
  225. .then(() => {
  226. this._initializeLimits();
  227. this._initializeContextAndSwapChain();
  228. // this._initializeMainAttachments();
  229. // Initialization is in the resize :-)
  230. this.resize();
  231. })
  232. .catch((e: any) => {
  233. Logger.Error("Can not create WebGPU Device and/or context.");
  234. Logger.Error(e);
  235. });
  236. }
  237. private _initGlslang(glslangOptions?: GlslangOptions): Promise<any> {
  238. glslangOptions = glslangOptions || { };
  239. glslangOptions = {
  240. ...WebGPUEngine._glslangDefaultOptions,
  241. ...glslangOptions
  242. };
  243. if (glslangOptions.glslang) {
  244. return Promise.resolve(glslangOptions.glslang);
  245. }
  246. if ((window as any).glslang) {
  247. return (window as any).glslang(glslangOptions!.wasmPath);
  248. }
  249. if (glslangOptions.jsPath && glslangOptions.wasmPath) {
  250. return Tools.LoadScriptAsync(glslangOptions.jsPath)
  251. .then(() => {
  252. return (window as any).glslang(glslangOptions!.wasmPath);
  253. });
  254. }
  255. return Promise.reject("gslang is not available.");
  256. }
  257. private _initializeLimits(): void {
  258. // Init caps
  259. // TODO WEBGPU Real Capability check once limits will be working.
  260. this._caps = this._caps = {
  261. maxTexturesImageUnits: 16,
  262. maxVertexTextureImageUnits: 16,
  263. maxCombinedTexturesImageUnits: 32,
  264. maxTextureSize: 2048,
  265. maxCubemapTextureSize: 2048,
  266. maxRenderTextureSize: 2048,
  267. maxVertexAttribs: 16,
  268. maxVaryingVectors: 16,
  269. maxFragmentUniformVectors: 1024,
  270. maxVertexUniformVectors: 1024,
  271. standardDerivatives: true,
  272. astc: null,
  273. pvrtc: null,
  274. etc1: null,
  275. etc2: null,
  276. maxAnisotropy: 0, // TODO: Retrieve this smartly. Currently set to D3D11 maximum allowable value.
  277. uintIndices: false,
  278. fragmentDepthSupported: false,
  279. highPrecisionShaderSupported: true,
  280. colorBufferFloat: false,
  281. textureFloat: false,
  282. textureFloatLinearFiltering: false,
  283. textureFloatRender: false,
  284. textureHalfFloat: false,
  285. textureHalfFloatLinearFiltering: false,
  286. textureHalfFloatRender: false,
  287. textureLOD: true,
  288. drawBuffersExtension: true,
  289. depthTextureExtension: true,
  290. vertexArrayObject: false,
  291. instancedArrays: true,
  292. canUseTimestampForTimerQuery: false,
  293. blendMinMax: false,
  294. maxMSAASamples: 1
  295. };
  296. this._caps.parallelShaderCompile = null as any;
  297. }
  298. private _initializeContextAndSwapChain(): void {
  299. this._context = this._canvas.getContext('gpupresent') as unknown as GPUCanvasContext;
  300. this._swapChain = this._context.configureSwapChain({
  301. device: this._device,
  302. format: this._options.swapChainFormat!,
  303. usage: WebGPUConstants.GPUTextureUsage_OUTPUT_ATTACHMENT | WebGPUConstants.GPUTextureUsage_COPY_SRC,
  304. });
  305. }
  306. // Set default values as WebGL with depth and stencil attachment for the broadest Compat.
  307. private _initializeMainAttachments(): void {
  308. this._mainTextureExtends = {
  309. width: this.getRenderWidth(),
  310. height: this.getRenderHeight(),
  311. depth: 1
  312. };
  313. if (this._options.antialiasing) {
  314. const mainTextureDescriptor = {
  315. size: this._mainTextureExtends,
  316. arrayLayerCount: 1,
  317. mipLevelCount: 1,
  318. sampleCount: this._mainPassSampleCount,
  319. dimension: WebGPUConstants.GPUTextureDimension_2d,
  320. format: WebGPUConstants.GPUTextureFormat_bgra8unorm,
  321. usage: WebGPUConstants.GPUTextureUsage_OUTPUT_ATTACHMENT,
  322. };
  323. if (this._mainTexture) {
  324. this._mainTexture.destroy();
  325. }
  326. this._mainTexture = this._device.createTexture(mainTextureDescriptor);
  327. this._mainColorAttachments = [{
  328. attachment: this._mainTexture.createView(),
  329. loadValue: new Color4(0, 0, 0, 1),
  330. storeOp: WebGPUConstants.GPUStoreOp_store
  331. }];
  332. }
  333. else {
  334. this._mainColorAttachments = [{
  335. attachment: this._swapChain.getCurrentTexture().createView(),
  336. loadValue: new Color4(0, 0, 0, 1),
  337. storeOp: WebGPUConstants.GPUStoreOp_store
  338. }];
  339. }
  340. const depthTextureDescriptor = {
  341. size: this._mainTextureExtends,
  342. arrayLayerCount: 1,
  343. mipLevelCount: 1,
  344. sampleCount: this._mainPassSampleCount,
  345. dimension: WebGPUConstants.GPUTextureDimension_2d,
  346. format: WebGPUConstants.GPUTextureFormat_depth24plusStencil8,
  347. usage: WebGPUConstants.GPUTextureUsage_OUTPUT_ATTACHMENT
  348. };
  349. if (this._depthTexture) {
  350. this._depthTexture.destroy();
  351. }
  352. this._depthTexture = this._device.createTexture(depthTextureDescriptor);
  353. this._mainDepthAttachment = {
  354. attachment: this._depthTexture.createView(),
  355. depthLoadValue: this._clearDepthValue,
  356. depthStoreOp: WebGPUConstants.GPUStoreOp_store,
  357. stencilLoadValue: this._clearStencilValue,
  358. stencilStoreOp: WebGPUConstants.GPUStoreOp_store,
  359. };
  360. }
  361. /**
  362. * Gets a shader processor implementation fitting with the current engine type.
  363. * @returns The shader processor implementation.
  364. */
  365. protected _getShaderProcessor(): Nullable<IShaderProcessor> {
  366. return new WebGPUShaderProcessor();
  367. }
  368. /** @hidden */
  369. public _getShaderProcessingContext(): Nullable<ShaderProcessingContext> {
  370. return new WebGPUShaderProcessingContext();
  371. }
  372. //------------------------------------------------------------------------------
  373. // Static Pipeline WebGPU States
  374. //------------------------------------------------------------------------------
  375. public wipeCaches(bruteForce?: boolean): void {
  376. if (this.preventCacheWipeBetweenFrames) {
  377. return;
  378. }
  379. this.resetTextureCache();
  380. this._currentEffect = null;
  381. this._currentIndexBuffer = null;
  382. this._currentVertexBuffers = null;
  383. if (bruteForce) {
  384. this._currentProgram = null;
  385. this._stencilState.reset();
  386. this._depthCullingState.reset();
  387. this._alphaState.reset();
  388. }
  389. this._cachedVertexBuffers = null;
  390. this._cachedIndexBuffer = null;
  391. this._cachedEffectForVertexBuffers = null;
  392. }
  393. public setColorWrite(enable: boolean): void {
  394. this.__colorWrite = enable;
  395. }
  396. public getColorWrite(): boolean {
  397. return this.__colorWrite;
  398. }
  399. //------------------------------------------------------------------------------
  400. // Dynamic WebGPU States
  401. //------------------------------------------------------------------------------
  402. public _viewport(x: number, y: number, width: number, height: number): void {
  403. // TODO WEBGPU. Cache.
  404. // if (x !== this._viewportCached.x ||
  405. // y !== this._viewportCached.y ||
  406. // width !== this._viewportCached.z ||
  407. // height !== this._viewportCached.w) {
  408. // this._viewportCached.x = x;
  409. // this._viewportCached.y = y;
  410. // this._viewportCached.z = width;
  411. // this._viewportCached.w = height;
  412. // this._gl.viewport(x, y, width, height);
  413. // }
  414. if (!this._currentRenderPass) {
  415. this._startMainRenderPass();
  416. }
  417. // TODO WEBGPU. Viewport.
  418. // Use 0 1 like the default webgl values.
  419. // this._currentRenderPass!.setViewport(x, y, width, height, 0, 1);
  420. }
  421. public enableScissor(x: number, y: number, width: number, height: number): void {
  422. if (!this._currentRenderPass) {
  423. this._startMainRenderPass();
  424. }
  425. this._currentRenderPass!.setScissorRect(x, y, width, height);
  426. }
  427. public disableScissor() {
  428. if (!this._currentRenderPass) {
  429. this._startMainRenderPass();
  430. }
  431. this._currentRenderPass!.setScissorRect(0, 0, this.getRenderWidth(), this.getRenderHeight());
  432. }
  433. public clear(color: Color4, backBuffer: boolean, depth: boolean, stencil: boolean = false): void {
  434. // Some PGs are using color3...
  435. if (color.a === undefined) {
  436. color.a = 1;
  437. }
  438. this._mainColorAttachments[0].loadValue = backBuffer ? color : WebGPUConstants.GPULoadOp_load;
  439. this._mainDepthAttachment.depthLoadValue = depth ? this._clearDepthValue : WebGPUConstants.GPULoadOp_load;
  440. this._mainDepthAttachment.stencilLoadValue = stencil ? this._clearStencilValue : WebGPUConstants.GPULoadOp_load;
  441. this._startMainRenderPass();
  442. }
  443. //------------------------------------------------------------------------------
  444. // WebGPU Buffers
  445. //------------------------------------------------------------------------------
  446. private _createBuffer(view: ArrayBufferView, flags: GPUBufferUsageFlags): DataBuffer {
  447. if (view.byteLength == 0) {
  448. throw new Error("Unable to create WebGPU buffer"); // Zero size buffer would kill the tab in chrome
  449. }
  450. const padding = view.byteLength % 4;
  451. const verticesBufferDescriptor = {
  452. size: view.byteLength + padding,
  453. usage: flags
  454. };
  455. const [buffer, arrbuffer] = this._device.createBufferMapped(verticesBufferDescriptor)
  456. const dataBuffer = new WebGPUDataBuffer(buffer);
  457. dataBuffer.references = 1;
  458. dataBuffer.capacity = view.byteLength;
  459. if (padding == 0 && view.byteLength < this._maxBufferChunk) {
  460. new Uint8Array(arrbuffer).set(new Uint8Array(view.buffer));
  461. buffer.unmap();
  462. } else {
  463. buffer.unmap();
  464. this._setSubData(dataBuffer, 0, view);
  465. }
  466. return dataBuffer;
  467. }
  468. private _setSubData(dataBuffer: WebGPUDataBuffer, dstByteOffset: number, src: ArrayBufferView, srcByteOffset = 0, byteLength = 0): void {
  469. const buffer = dataBuffer.underlyingResource as GPUBuffer;
  470. byteLength = byteLength || src.byteLength;
  471. byteLength = Math.min(byteLength, dataBuffer.capacity - dstByteOffset);
  472. // After Migration to Canary
  473. // This would do from PR #261
  474. let chunkStart = src.byteOffset + srcByteOffset;
  475. let chunkEnd = chunkStart + byteLength;
  476. // 4 bytes alignments for upload
  477. const padding = byteLength % 4;
  478. if (padding !== 0) {
  479. const tempView = new Uint8Array(src.buffer.slice(chunkStart, chunkEnd));
  480. src = new Uint8Array(byteLength + padding);
  481. tempView.forEach((element, index) => {
  482. (src as Uint8Array)[index] = element;
  483. });
  484. srcByteOffset = 0;
  485. chunkStart = 0;
  486. chunkEnd = byteLength + padding;
  487. byteLength = byteLength + padding;
  488. }
  489. // Chunk
  490. const commandEncoder = this._device.createCommandEncoder();
  491. const tempBuffers: GPUBuffer[] = [];
  492. for (let offset = 0; offset < src.byteLength; offset += this._maxBufferChunk) {
  493. const uploadCount = Math.min(src.byteLength - offset, this._maxBufferChunk);
  494. const [uploadBuffer, uploadMapping] = this._device.createBufferMapped({
  495. usage: WebGPUConstants.GPUBufferUsage_TRANSFER_SRC,
  496. size: uploadCount,
  497. });
  498. tempBuffers.push(uploadBuffer);
  499. new Uint8Array(uploadMapping).set(new Uint8Array(src.buffer, srcByteOffset + offset, uploadCount));
  500. uploadBuffer.unmap();
  501. commandEncoder.copyBufferToBuffer(
  502. uploadBuffer, 0,
  503. buffer, dstByteOffset + offset,
  504. uploadCount);
  505. }
  506. this._device.defaultQueue.submit([commandEncoder.finish()]);
  507. tempBuffers.forEach((buff) => buff.destroy());
  508. }
  509. //------------------------------------------------------------------------------
  510. // Vertex/Index Buffers
  511. //------------------------------------------------------------------------------
  512. public createVertexBuffer(data: DataArray): DataBuffer {
  513. let view: ArrayBufferView;
  514. if (data instanceof Array) {
  515. view = new Float32Array(data);
  516. }
  517. else if (data instanceof ArrayBuffer) {
  518. view = new Uint8Array(data);
  519. }
  520. else {
  521. view = data;
  522. }
  523. const dataBuffer = this._createBuffer(view, WebGPUConstants.GPUBufferUsage_VERTEX | WebGPUConstants.GPUBufferUsage_TRANSFER_DST);
  524. return dataBuffer;
  525. }
  526. public createDynamicVertexBuffer(data: DataArray): DataBuffer {
  527. return this.createVertexBuffer(data);
  528. }
  529. public updateDynamicVertexBuffer(vertexBuffer: DataBuffer, data: DataArray, byteOffset?: number, byteLength?: number): void {
  530. const dataBuffer = vertexBuffer as WebGPUDataBuffer;
  531. if (byteOffset === undefined) {
  532. byteOffset = 0;
  533. }
  534. let view: ArrayBufferView;
  535. if (byteLength === undefined) {
  536. if (data instanceof Array) {
  537. view = new Float32Array(data);
  538. }
  539. else if (data instanceof ArrayBuffer) {
  540. view = new Uint8Array(data);
  541. }
  542. else {
  543. view = data;
  544. }
  545. byteLength = view.byteLength;
  546. } else {
  547. if (data instanceof Array) {
  548. view = new Float32Array(data);
  549. }
  550. else if (data instanceof ArrayBuffer) {
  551. view = new Uint8Array(data);
  552. }
  553. else {
  554. view = data;
  555. }
  556. }
  557. this._setSubData(dataBuffer, byteOffset, view, 0, byteLength);
  558. }
  559. public createIndexBuffer(data: IndicesArray): DataBuffer {
  560. let is32Bits = true;
  561. let view: ArrayBufferView;
  562. if (data instanceof Uint32Array || data instanceof Int32Array) {
  563. view = data;
  564. }
  565. else if (data instanceof Uint16Array) {
  566. view = data;
  567. is32Bits = false;
  568. }
  569. else {
  570. if (data.length > 65535) {
  571. view = new Uint32Array(data);
  572. }
  573. else {
  574. view = new Uint16Array(data);
  575. is32Bits = false;
  576. }
  577. }
  578. const dataBuffer = this._createBuffer(view, WebGPUConstants.GPUBufferUsage_INDEX | WebGPUConstants.GPUBufferUsage_TRANSFER_DST);
  579. dataBuffer.is32Bits = is32Bits;
  580. return dataBuffer;
  581. }
  582. public updateDynamicIndexBuffer(indexBuffer: DataBuffer, indices: IndicesArray, offset: number = 0): void {
  583. const gpuBuffer = indexBuffer as WebGPUDataBuffer;
  584. var view: ArrayBufferView;
  585. if (indices instanceof Uint16Array) {
  586. if (indexBuffer.is32Bits) {
  587. view = Uint32Array.from(indices);
  588. }
  589. else {
  590. view = indices;
  591. }
  592. }
  593. else if (indices instanceof Uint32Array) {
  594. if (indexBuffer.is32Bits) {
  595. view = indices;
  596. }
  597. else {
  598. view = Uint16Array.from(indices);
  599. }
  600. }
  601. else {
  602. if (indexBuffer.is32Bits) {
  603. view = new Uint32Array(indices);
  604. }
  605. else {
  606. view = new Uint16Array(indices);
  607. }
  608. }
  609. this._setSubData(gpuBuffer, offset, view);
  610. }
  611. public bindBuffersDirectly(vertexBuffer: DataBuffer, indexBuffer: DataBuffer, vertexDeclaration: number[], vertexStrideSize: number, effect: Effect): void {
  612. throw "Not implemented on WebGPU so far.";
  613. }
  614. public updateAndBindInstancesBuffer(instancesBuffer: DataBuffer, data: Float32Array, offsetLocations: number[] | InstancingAttributeInfo[]): void {
  615. throw "Not implemented on WebGPU so far.";
  616. }
  617. public bindBuffers(vertexBuffers: { [key: string]: Nullable<VertexBuffer> }, indexBuffer: Nullable<DataBuffer>, effect: Effect): void {
  618. this._currentIndexBuffer = indexBuffer;
  619. this._currentVertexBuffers = vertexBuffers;
  620. }
  621. /** @hidden */
  622. public _releaseBuffer(buffer: DataBuffer): boolean {
  623. buffer.references--;
  624. if (buffer.references === 0) {
  625. (buffer.underlyingResource as GPUBuffer).destroy();
  626. return true;
  627. }
  628. return false;
  629. }
  630. //------------------------------------------------------------------------------
  631. // UBO
  632. //------------------------------------------------------------------------------
  633. public createUniformBuffer(elements: FloatArray): DataBuffer {
  634. let view: Float32Array;
  635. if (elements instanceof Array) {
  636. view = new Float32Array(elements);
  637. }
  638. else {
  639. view = elements;
  640. }
  641. const dataBuffer = this._createBuffer(view, WebGPUConstants.GPUBufferUsage_UNIFORM | WebGPUConstants.GPUBufferUsage_TRANSFER_DST);
  642. return dataBuffer;
  643. }
  644. public createDynamicUniformBuffer(elements: FloatArray): DataBuffer {
  645. return this.createUniformBuffer(elements);
  646. }
  647. public updateUniformBuffer(uniformBuffer: DataBuffer, elements: FloatArray, offset?: number, count?: number): void {
  648. if (offset === undefined) {
  649. offset = 0;
  650. }
  651. const dataBuffer = uniformBuffer as WebGPUDataBuffer;
  652. let view: Float32Array;
  653. if (count === undefined) {
  654. if (elements instanceof Float32Array) {
  655. view = elements;
  656. } else {
  657. view = new Float32Array(elements);
  658. }
  659. count = view.byteLength;
  660. } else {
  661. if (elements instanceof Float32Array) {
  662. view = elements;
  663. } else {
  664. view = new Float32Array(elements);
  665. }
  666. }
  667. this._setSubData(dataBuffer, offset, view, 0, count);
  668. }
  669. public bindUniformBufferBase(buffer: DataBuffer, location: number, name: string): void {
  670. this._uniformsBuffers[name] = buffer as WebGPUDataBuffer;
  671. }
  672. //------------------------------------------------------------------------------
  673. // Effects
  674. //------------------------------------------------------------------------------
  675. public createEffect(baseName: any, attributesNamesOrOptions: string[] | IEffectCreationOptions, uniformsNamesOrEngine: string[] | Engine, samplers?: string[], defines?: string, fallbacks?: EffectFallbacks,
  676. onCompiled?: Nullable<(effect: Effect) => void>, onError?: Nullable<(effect: Effect, errors: string) => void>, indexParameters?: any): Effect {
  677. const vertex = baseName.vertexElement || baseName.vertex || baseName;
  678. const fragment = baseName.fragmentElement || baseName.fragment || baseName;
  679. const name = vertex + "+" + fragment + "@" + (defines ? defines : (<IEffectCreationOptions>attributesNamesOrOptions).defines);
  680. const shader = this._compiledShaders[name];
  681. if (shader) {
  682. return new Effect(baseName, attributesNamesOrOptions, uniformsNamesOrEngine, samplers, this, defines, fallbacks, onCompiled, onError, indexParameters, name, shader.sources);
  683. }
  684. else {
  685. return new Effect(baseName, attributesNamesOrOptions, uniformsNamesOrEngine, samplers, this, defines, fallbacks, onCompiled, onError, indexParameters, name);
  686. }
  687. }
  688. private _compileRawShaderToSpirV(source: string, type: string): Uint32Array {
  689. return this._glslang.compileGLSL(source, type);
  690. }
  691. private _compileShaderToSpirV(source: string, type: string, defines: Nullable<string>, shaderVersion: string): Uint32Array {
  692. return this._compileRawShaderToSpirV(shaderVersion + (defines ? defines + "\n" : "") + source, type);
  693. }
  694. private _createPipelineStageDescriptor(vertexShader: Uint32Array, fragmentShader: Uint32Array): GPURenderPipelineStageDescriptor {
  695. return {
  696. vertexStage: {
  697. module: this._device.createShaderModule({
  698. code: vertexShader,
  699. }),
  700. entryPoint: "main",
  701. },
  702. fragmentStage: {
  703. module: this._device.createShaderModule({
  704. code: fragmentShader,
  705. }),
  706. entryPoint: "main"
  707. }
  708. };
  709. }
  710. private _compileRawPipelineStageDescriptor(vertexCode: string, fragmentCode: string): GPURenderPipelineStageDescriptor {
  711. var vertexShader = this._compileRawShaderToSpirV(vertexCode, "vertex");
  712. var fragmentShader = this._compileRawShaderToSpirV(fragmentCode, "fragment");
  713. return this._createPipelineStageDescriptor(vertexShader, fragmentShader);
  714. }
  715. private _compilePipelineStageDescriptor(vertexCode: string, fragmentCode: string, defines: Nullable<string>): GPURenderPipelineStageDescriptor {
  716. this.onBeforeShaderCompilationObservable.notifyObservers(this);
  717. var shaderVersion = "#version 450\n";
  718. var vertexShader = this._compileShaderToSpirV(vertexCode, "vertex", defines, shaderVersion);
  719. var fragmentShader = this._compileShaderToSpirV(fragmentCode, "fragment", defines, shaderVersion);
  720. let program = this._createPipelineStageDescriptor(vertexShader, fragmentShader);
  721. this.onAfterShaderCompilationObservable.notifyObservers(this);
  722. return program;
  723. }
  724. public createRawShaderProgram(pipelineContext: IPipelineContext, vertexCode: string, fragmentCode: string, context?: WebGLRenderingContext, transformFeedbackVaryings: Nullable<string[]> = null): WebGLProgram {
  725. throw "Not available on WebGPU";
  726. }
  727. public createShaderProgram(pipelineContext: IPipelineContext, vertexCode: string, fragmentCode: string, defines: Nullable<string>, context?: WebGLRenderingContext, transformFeedbackVaryings: Nullable<string[]> = null): WebGLProgram {
  728. throw "Not available on WebGPU";
  729. }
  730. public createPipelineContext(shaderProcessingContext: Nullable<ShaderProcessingContext>): IPipelineContext {
  731. var pipelineContext = new WebGPUPipelineContext(shaderProcessingContext! as WebGPUShaderProcessingContext, this);
  732. pipelineContext.engine = this;
  733. return pipelineContext;
  734. }
  735. /** @hidden */
  736. public _preparePipelineContext(pipelineContext: IPipelineContext, vertexSourceCode: string, fragmentSourceCode: string, createAsRaw: boolean,
  737. rebuildRebind: any,
  738. defines: Nullable<string>,
  739. transformFeedbackVaryings: Nullable<string[]>,
  740. key: string) {
  741. const webGpuContext = pipelineContext as WebGPUPipelineContext;
  742. // TODO WEBGPU. Check if caches could be reuse from piepline ???
  743. const shader = this._compiledShaders[key];
  744. if (shader) {
  745. webGpuContext.stages = shader.stages;
  746. webGpuContext.availableAttributes = shader.availableAttributes;
  747. webGpuContext.availableUBOs = shader.availableUBOs;
  748. webGpuContext.availableSamplers = shader.availableSamplers;
  749. webGpuContext.orderedAttributes = shader.orderedAttributes;
  750. webGpuContext.orderedUBOsAndSamplers = shader.orderedUBOsAndSamplers;
  751. webGpuContext.leftOverUniforms = shader.leftOverUniforms;
  752. webGpuContext.leftOverUniformsByName = shader.leftOverUniformsByName;
  753. webGpuContext.sources = shader.sources;
  754. }
  755. else {
  756. if (createAsRaw) {
  757. webGpuContext.stages = this._compileRawPipelineStageDescriptor(vertexSourceCode, fragmentSourceCode);
  758. }
  759. else {
  760. webGpuContext.stages = this._compilePipelineStageDescriptor(vertexSourceCode, fragmentSourceCode, defines);
  761. }
  762. this._compiledShaders[key] = {
  763. stages: webGpuContext.stages,
  764. availableAttributes: webGpuContext.availableAttributes,
  765. availableUBOs: webGpuContext.availableUBOs,
  766. availableSamplers: webGpuContext.availableSamplers,
  767. orderedAttributes: webGpuContext.orderedAttributes,
  768. orderedUBOsAndSamplers: webGpuContext.orderedUBOsAndSamplers,
  769. leftOverUniforms: webGpuContext.leftOverUniforms,
  770. leftOverUniformsByName: webGpuContext.leftOverUniformsByName,
  771. sources: {
  772. fragment: fragmentSourceCode,
  773. vertex: vertexSourceCode
  774. }
  775. };
  776. }
  777. }
  778. public getAttributes(pipelineContext: IPipelineContext, attributesNames: string[]): number[] {
  779. const results = new Array(attributesNames.length);
  780. const gpuPipelineContext = (pipelineContext as WebGPUPipelineContext);
  781. // TODO WEBGPU. Hard coded for WebGPU until an introspection lib is available.
  782. // Should be done at processing time, not need to double the work in here.
  783. for (let i = 0; i < attributesNames.length; i++) {
  784. const attributeName = attributesNames[i];
  785. const attributeLocation = gpuPipelineContext.availableAttributes[attributeName];
  786. if (attributeLocation === undefined) {
  787. continue;
  788. }
  789. results[i] = attributeLocation;
  790. }
  791. return results;
  792. }
  793. public enableEffect(effect: Effect): void {
  794. this._currentEffect = effect;
  795. if (effect.onBind) {
  796. effect.onBind(effect);
  797. }
  798. if (effect._onBindObservable) {
  799. effect._onBindObservable.notifyObservers(effect);
  800. }
  801. }
  802. public _releaseEffect(effect: Effect): void {
  803. // Effect gets garbage collected without explicit destroy in WebGPU.
  804. }
  805. /**
  806. * Force the engine to release all cached effects. This means that next effect compilation will have to be done completely even if a similar effect was already compiled
  807. */
  808. public releaseEffects() {
  809. // Effect gets garbage collected without explicit destroy in WebGPU.
  810. }
  811. public _deletePipelineContext(pipelineContext: IPipelineContext): void {
  812. const webgpuPipelineContext = pipelineContext as WebGPUPipelineContext;
  813. if (webgpuPipelineContext) {
  814. pipelineContext.dispose();
  815. }
  816. }
  817. //------------------------------------------------------------------------------
  818. // Textures
  819. //------------------------------------------------------------------------------
  820. /** @hidden */
  821. public _createTexture(): WebGLTexture {
  822. // TODO WEBGPU. This should return the GPUTexture, WebGLTexture might need to be wrapped like the buffers.
  823. return { };
  824. }
  825. /** @hidden */
  826. public _releaseTexture(texture: InternalTexture): void {
  827. if (texture._webGPUTexture) {
  828. texture._webGPUTexture.destroy();
  829. }
  830. }
  831. private _uploadMipMapsFromWebglTexture(mipMaps: number, webglEngineTexture: InternalTexture, gpuTexture: GPUTexture, width: number, height: number, face: number) {
  832. this._uploadFromWebglTexture(webglEngineTexture, gpuTexture, width, height, face);
  833. let faceWidth = width;
  834. let faceHeight = height;
  835. for (let mip = 1; mip <= mipMaps; mip++) {
  836. faceWidth = Math.max(Math.floor(faceWidth / 2), 1);
  837. faceHeight = Math.max(Math.floor(faceHeight / 2), 1);
  838. this._uploadFromWebglTexture(webglEngineTexture, gpuTexture, faceWidth, faceHeight, face, mip);
  839. }
  840. }
  841. private _uploadFromWebglTexture(webglEngineTexture: InternalTexture, gpuTexture: GPUTexture, width: number, height: number, face: number, mip: number = 0): void {
  842. let pixels = this._decodeEngine._readTexturePixels(webglEngineTexture, width, height, face, mip);
  843. if (pixels instanceof Float32Array) {
  844. const newPixels = new Uint8ClampedArray(pixels.length);
  845. pixels.forEach((value, index) => newPixels[index] = value * 255);
  846. pixels = newPixels;
  847. }
  848. const textureView: GPUTextureCopyView = {
  849. texture: gpuTexture,
  850. origin: {
  851. x: 0,
  852. y: 0,
  853. z: 0
  854. },
  855. mipLevel: mip,
  856. arrayLayer: Math.max(face, 0),
  857. };
  858. const textureExtent = {
  859. width,
  860. height,
  861. depth: 1
  862. };
  863. const commandEncoder = this._device.createCommandEncoder({});
  864. const rowPitch = Math.ceil(width * 4 / 256) * 256;
  865. let dataBuffer: DataBuffer;
  866. if (rowPitch == width * 4) {
  867. dataBuffer = this._createBuffer(pixels, WebGPUConstants.GPUBufferUsage_TRANSFER_SRC | WebGPUConstants.GPUBufferUsage_TRANSFER_DST);
  868. const bufferView: GPUBufferCopyView = {
  869. buffer: dataBuffer.underlyingResource,
  870. rowPitch: rowPitch,
  871. imageHeight: height,
  872. offset: 0,
  873. };
  874. commandEncoder.copyBufferToTexture(bufferView, textureView, textureExtent);
  875. } else {
  876. const alignedPixels = new Uint8Array(rowPitch * height);
  877. let pixelsIndex = 0;
  878. for (let y = 0; y < height; ++y) {
  879. for (let x = 0; x < width; ++x) {
  880. let i = x * 4 + y * rowPitch;
  881. alignedPixels[i] = (pixels as any)[pixelsIndex];
  882. alignedPixels[i + 1] = (pixels as any)[pixelsIndex + 1];
  883. alignedPixels[i + 2] = (pixels as any)[pixelsIndex + 2];
  884. alignedPixels[i + 3] = (pixels as any)[pixelsIndex + 3];
  885. pixelsIndex += 4;
  886. }
  887. }
  888. dataBuffer = this._createBuffer(alignedPixels, WebGPUConstants.GPUBufferUsage_TRANSFER_SRC | WebGPUConstants.GPUBufferUsage_TRANSFER_DST);
  889. const bufferView: GPUBufferCopyView = {
  890. buffer: dataBuffer.underlyingResource,
  891. rowPitch: rowPitch,
  892. imageHeight: height,
  893. offset: 0,
  894. };
  895. commandEncoder.copyBufferToTexture(bufferView, textureView, textureExtent);
  896. }
  897. this._device.defaultQueue.submit([commandEncoder.finish()]);
  898. this._releaseBuffer(dataBuffer);
  899. }
  900. private _getSamplerFilterDescriptor(internalTexture: InternalTexture): {
  901. magFilter: GPUFilterMode,
  902. minFilter: GPUFilterMode,
  903. mipmapFilter: GPUFilterMode
  904. } {
  905. let magFilter: GPUFilterMode, minFilter: GPUFilterMode, mipmapFilter: GPUFilterMode;
  906. switch (internalTexture.samplingMode) {
  907. case Engine.TEXTURE_BILINEAR_SAMPLINGMODE:
  908. magFilter = WebGPUConstants.GPUFilterMode_linear;
  909. minFilter = WebGPUConstants.GPUFilterMode_linear;
  910. mipmapFilter = WebGPUConstants.GPUFilterMode_nearest;
  911. break;
  912. case Engine.TEXTURE_TRILINEAR_SAMPLINGMODE:
  913. magFilter = WebGPUConstants.GPUFilterMode_linear;
  914. minFilter = WebGPUConstants.GPUFilterMode_linear;
  915. mipmapFilter = WebGPUConstants.GPUFilterMode_linear;
  916. break;
  917. case Engine.TEXTURE_NEAREST_SAMPLINGMODE:
  918. magFilter = WebGPUConstants.GPUFilterMode_nearest;
  919. minFilter = WebGPUConstants.GPUFilterMode_nearest;
  920. mipmapFilter = WebGPUConstants.GPUFilterMode_linear;
  921. break;
  922. case Engine.TEXTURE_NEAREST_NEAREST_MIPNEAREST:
  923. magFilter = WebGPUConstants.GPUFilterMode_nearest;
  924. minFilter = WebGPUConstants.GPUFilterMode_nearest;
  925. mipmapFilter = WebGPUConstants.GPUFilterMode_nearest;
  926. break;
  927. case Engine.TEXTURE_NEAREST_LINEAR_MIPNEAREST:
  928. magFilter = WebGPUConstants.GPUFilterMode_nearest;
  929. minFilter = WebGPUConstants.GPUFilterMode_linear;
  930. mipmapFilter = WebGPUConstants.GPUFilterMode_nearest;
  931. break;
  932. case Engine.TEXTURE_NEAREST_LINEAR_MIPLINEAR:
  933. magFilter = WebGPUConstants.GPUFilterMode_nearest;
  934. minFilter = WebGPUConstants.GPUFilterMode_linear;
  935. mipmapFilter = WebGPUConstants.GPUFilterMode_linear;
  936. break;
  937. case Engine.TEXTURE_NEAREST_LINEAR:
  938. magFilter = WebGPUConstants.GPUFilterMode_nearest;
  939. minFilter = WebGPUConstants.GPUFilterMode_linear;
  940. mipmapFilter = WebGPUConstants.GPUFilterMode_nearest;
  941. break;
  942. case Engine.TEXTURE_NEAREST_NEAREST:
  943. magFilter = WebGPUConstants.GPUFilterMode_nearest;
  944. minFilter = WebGPUConstants.GPUFilterMode_nearest;
  945. mipmapFilter = WebGPUConstants.GPUFilterMode_nearest;
  946. break;
  947. case Engine.TEXTURE_LINEAR_NEAREST_MIPNEAREST:
  948. magFilter = WebGPUConstants.GPUFilterMode_linear;
  949. minFilter = WebGPUConstants.GPUFilterMode_nearest;
  950. mipmapFilter = WebGPUConstants.GPUFilterMode_nearest;
  951. break;
  952. case Engine.TEXTURE_LINEAR_NEAREST_MIPLINEAR:
  953. magFilter = WebGPUConstants.GPUFilterMode_linear;
  954. minFilter = WebGPUConstants.GPUFilterMode_nearest;
  955. mipmapFilter = WebGPUConstants.GPUFilterMode_linear;
  956. break;
  957. case Engine.TEXTURE_LINEAR_LINEAR:
  958. magFilter = WebGPUConstants.GPUFilterMode_linear;
  959. minFilter = WebGPUConstants.GPUFilterMode_linear;
  960. mipmapFilter = WebGPUConstants.GPUFilterMode_nearest;
  961. break;
  962. case Engine.TEXTURE_LINEAR_NEAREST:
  963. magFilter = WebGPUConstants.GPUFilterMode_linear;
  964. minFilter = WebGPUConstants.GPUFilterMode_nearest;
  965. mipmapFilter = WebGPUConstants.GPUFilterMode_nearest;
  966. break;
  967. default:
  968. magFilter = WebGPUConstants.GPUFilterMode_linear;
  969. minFilter = WebGPUConstants.GPUFilterMode_linear;
  970. mipmapFilter = WebGPUConstants.GPUFilterMode_linear;
  971. break;
  972. }
  973. return {
  974. magFilter,
  975. minFilter,
  976. mipmapFilter
  977. };
  978. }
  979. private _getWrappingMode(mode: number): GPUAddressMode {
  980. switch (mode) {
  981. case Engine.TEXTURE_WRAP_ADDRESSMODE:
  982. return WebGPUConstants.GPUAddressMode_repeat;
  983. case Engine.TEXTURE_CLAMP_ADDRESSMODE:
  984. return WebGPUConstants.GPUAddressMode_clampToEdge;
  985. case Engine.TEXTURE_MIRROR_ADDRESSMODE:
  986. return WebGPUConstants.GPUAddressMode_mirrorRepeat;
  987. }
  988. return WebGPUConstants.GPUAddressMode_repeat;
  989. }
  990. private _getSamplerWrappingDescriptor(internalTexture: InternalTexture): {
  991. addressModeU: GPUAddressMode,
  992. addressModeV: GPUAddressMode,
  993. addressModeW: GPUAddressMode
  994. } {
  995. return {
  996. addressModeU: this._getWrappingMode(internalTexture._cachedWrapU!),
  997. addressModeV: this._getWrappingMode(internalTexture._cachedWrapV!),
  998. addressModeW: this._getWrappingMode(internalTexture._cachedWrapR!),
  999. };
  1000. }
  1001. private _getSamplerDescriptor(internalTexture: InternalTexture): GPUSamplerDescriptor {
  1002. return {
  1003. ...this._getSamplerFilterDescriptor(internalTexture),
  1004. ...this._getSamplerWrappingDescriptor(internalTexture),
  1005. };
  1006. }
  1007. public createTexture(urlArg: string, noMipmap: boolean, invertY: boolean, scene: Scene, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, onLoad: Nullable<() => void> = null, onError: Nullable<(message: string, exception: any) => void> = null, buffer: Nullable<ArrayBuffer | HTMLImageElement> = null, fallBack?: InternalTexture, format?: number): InternalTexture {
  1008. const texture = new InternalTexture(this, InternalTextureSource.Url);
  1009. const url = String(urlArg);
  1010. // TODO WEBGPU. Find a better way.
  1011. // TODO WEBGPU. this._options.textureSize
  1012. texture.url = url;
  1013. texture.generateMipMaps = !noMipmap;
  1014. texture.samplingMode = samplingMode;
  1015. texture.invertY = invertY;
  1016. if (format) {
  1017. texture.format = format;
  1018. }
  1019. let webglEngineTexture: InternalTexture;
  1020. const onLoadInternal = () => {
  1021. texture.isReady = webglEngineTexture.isReady;
  1022. const width = webglEngineTexture.width;
  1023. const height = webglEngineTexture.height;
  1024. texture.width = width;
  1025. texture.height = height;
  1026. texture.baseWidth = width;
  1027. texture.baseHeight = height;
  1028. texture._isRGBD = texture._isRGBD || webglEngineTexture._isRGBD;
  1029. texture._sphericalPolynomial = webglEngineTexture._sphericalPolynomial;
  1030. let mipMaps = Scalar.Log2(Math.max(width, height));
  1031. mipMaps = Math.floor(mipMaps);
  1032. const textureExtent = {
  1033. width,
  1034. height,
  1035. depth: 1
  1036. };
  1037. const textureDescriptor: GPUTextureDescriptor = {
  1038. dimension: WebGPUConstants.GPUTextureDimension_2d,
  1039. format: WebGPUConstants.GPUTextureFormat_rgba8unorm,
  1040. arrayLayerCount: 1,
  1041. mipLevelCount: noMipmap ? 1 : mipMaps + 1,
  1042. sampleCount: 1,
  1043. size: textureExtent,
  1044. usage: WebGPUConstants.GPUTextureUsage_COPY_DST | WebGPUConstants.GPUTextureUsage_SAMPLED
  1045. };
  1046. const gpuTexture = this._device.createTexture(textureDescriptor);
  1047. texture._webGPUTexture = gpuTexture;
  1048. if (noMipmap) {
  1049. this._uploadFromWebglTexture(webglEngineTexture, gpuTexture, width, height, -1);
  1050. }
  1051. else {
  1052. this._uploadMipMapsFromWebglTexture(mipMaps, webglEngineTexture, gpuTexture, width, height, -1);
  1053. }
  1054. texture._webGPUTextureView = gpuTexture.createView();
  1055. webglEngineTexture.dispose();
  1056. texture.onLoadedObservable.notifyObservers(texture);
  1057. texture.onLoadedObservable.clear();
  1058. if (onLoad) {
  1059. onLoad();
  1060. }
  1061. };
  1062. webglEngineTexture = this._decodeEngine.createTexture(urlArg, noMipmap, invertY, scene, samplingMode,
  1063. onLoadInternal, onError, buffer, fallBack, format);
  1064. this._internalTexturesCache.push(texture);
  1065. return texture;
  1066. }
  1067. public createCubeTexture(rootUrl: string, scene: Nullable<Scene>, files: Nullable<string[]>, noMipmap?: boolean, onLoad: Nullable<(data?: any) => void> = null, onError: Nullable<(message?: string, exception?: any) => void> = null, format?: number, forcedExtension: any = null, createPolynomials: boolean = false, lodScale: number = 0, lodOffset: number = 0, fallback: Nullable<InternalTexture> = null, excludeLoaders: Array<IInternalTextureLoader> = []): InternalTexture {
  1068. var texture = fallback ? fallback : new InternalTexture(this, InternalTextureSource.Cube);
  1069. texture.isCube = true;
  1070. texture.url = rootUrl;
  1071. texture.generateMipMaps = !noMipmap;
  1072. // TODO WEBGPU. Cube Texture Sampling Mode.
  1073. texture.samplingMode = noMipmap ? Constants.TEXTURE_BILINEAR_SAMPLINGMODE : Constants.TEXTURE_TRILINEAR_SAMPLINGMODE;
  1074. texture._lodGenerationScale = lodScale;
  1075. texture._lodGenerationOffset = lodOffset;
  1076. if (!this._doNotHandleContextLost) {
  1077. texture._extension = forcedExtension;
  1078. texture._files = files;
  1079. }
  1080. let webglEngineTexture: InternalTexture;
  1081. const onLoadInternal = () => {
  1082. texture.isReady = webglEngineTexture.isReady;
  1083. const width = webglEngineTexture.width;
  1084. const height = webglEngineTexture.height;
  1085. const depth = 1;
  1086. texture.width = width;
  1087. texture.height = height;
  1088. texture.baseWidth = width;
  1089. texture.baseHeight = height;
  1090. texture.depth = depth;
  1091. texture.baseDepth = depth;
  1092. texture._isRGBD = texture._isRGBD || webglEngineTexture._isRGBD;
  1093. texture._sphericalPolynomial = webglEngineTexture._sphericalPolynomial;
  1094. let mipMaps = Scalar.Log2(width);
  1095. mipMaps = Math.round(mipMaps);
  1096. const textureExtent = {
  1097. width,
  1098. height,
  1099. depth,
  1100. };
  1101. const textureDescriptor: GPUTextureDescriptor = {
  1102. dimension: WebGPUConstants.GPUTextureDimension_2d,
  1103. format: WebGPUConstants.GPUTextureFormat_rgba8unorm,
  1104. arrayLayerCount: 6,
  1105. mipLevelCount: noMipmap ? 1 : mipMaps + 1,
  1106. sampleCount: 1,
  1107. size: textureExtent,
  1108. usage: WebGPUConstants.GPUTextureUsage_COPY_DST | WebGPUConstants.GPUTextureUsage_SAMPLED
  1109. };
  1110. const gpuTexture = this._device.createTexture(textureDescriptor);
  1111. texture._webGPUTexture = gpuTexture;
  1112. const faces = [0, 1, 2, 3, 4, 5];
  1113. for (let face of faces) {
  1114. if (noMipmap) {
  1115. this._uploadFromWebglTexture(webglEngineTexture, gpuTexture, width, height, face);
  1116. }
  1117. else {
  1118. this._uploadMipMapsFromWebglTexture(mipMaps, webglEngineTexture, gpuTexture, width, height, face);
  1119. }
  1120. }
  1121. texture._webGPUTextureView = gpuTexture.createView({
  1122. arrayLayerCount: 6,
  1123. dimension: WebGPUConstants.GPUTextureViewDimension_cube,
  1124. format: WebGPUConstants.GPUTextureFormat_rgba8unorm,
  1125. mipLevelCount: noMipmap ? 1 : mipMaps + 1,
  1126. baseArrayLayer: 0,
  1127. baseMipLevel: 0,
  1128. aspect: WebGPUConstants.GPUTextureAspect_all
  1129. } as any);
  1130. webglEngineTexture.dispose();
  1131. onLoad && onLoad();
  1132. };
  1133. webglEngineTexture = this._decodeEngine.createCubeTexture(rootUrl, scene, files, noMipmap, onLoadInternal, onError, format, forcedExtension, createPolynomials, lodScale, lodOffset, fallback, excludeLoaders);
  1134. this._internalTexturesCache.push(texture);
  1135. return texture;
  1136. }
  1137. public updateTextureSamplingMode(samplingMode: number, texture: InternalTexture): void {
  1138. texture.samplingMode = samplingMode;
  1139. }
  1140. public updateDynamicTexture(texture: Nullable<InternalTexture>, canvas: HTMLCanvasElement, invertY: boolean, premulAlpha: boolean = false, format?: number): void {
  1141. throw "Unimplemented updateDynamicTexture on WebGPU so far";
  1142. }
  1143. public setTexture(channel: number, _: Nullable<WebGLUniformLocation>, texture: Nullable<BaseTexture>, name: string): void {
  1144. if (this._currentEffect) {
  1145. const pipeline = this._currentEffect._pipelineContext as WebGPUPipelineContext;
  1146. if (!texture) {
  1147. pipeline.samplers[name] = null;
  1148. return;
  1149. }
  1150. const internalTexture = texture!.getInternalTexture();
  1151. if (internalTexture) {
  1152. internalTexture._cachedWrapU = texture.wrapU;
  1153. internalTexture._cachedWrapV = texture.wrapV;
  1154. internalTexture._cachedWrapR = texture.wrapR;
  1155. }
  1156. if (pipeline.samplers[name]) {
  1157. pipeline.samplers[name]!.texture = internalTexture!;
  1158. }
  1159. else {
  1160. // TODO WEBGPU. 121 mapping samplers <-> availableSamplers
  1161. const availableSampler = pipeline.availableSamplers[name];
  1162. if (availableSampler) {
  1163. pipeline.samplers[name] = {
  1164. setIndex: availableSampler.setIndex,
  1165. textureBinding: availableSampler.bindingIndex,
  1166. samplerBinding: availableSampler.bindingIndex + 1,
  1167. texture: internalTexture!
  1168. };
  1169. }
  1170. }
  1171. }
  1172. }
  1173. public bindSamplers(effect: Effect): void { }
  1174. public _bindTextureDirectly(target: number, texture: InternalTexture): boolean {
  1175. if (this._boundTexturesCache[this._activeChannel] !== texture) {
  1176. this._boundTexturesCache[this._activeChannel] = texture;
  1177. return true;
  1178. }
  1179. return false;
  1180. }
  1181. /** @hidden */
  1182. public _bindTexture(channel: number, texture: InternalTexture): void {
  1183. if (channel < 0) {
  1184. return;
  1185. }
  1186. this._bindTextureDirectly(0, texture);
  1187. }
  1188. /** @hidden */
  1189. public _uploadCompressedDataToTextureDirectly(texture: InternalTexture, internalFormat: number, width: number, height: number, data: ArrayBufferView, faceIndex: number = 0, lod: number = 0) {
  1190. }
  1191. /** @hidden */
  1192. public _uploadDataToTextureDirectly(texture: InternalTexture, imageData: ArrayBufferView, faceIndex: number = 0, lod: number = 0): void {
  1193. }
  1194. /** @hidden */
  1195. public _uploadArrayBufferViewToTexture(texture: InternalTexture, imageData: ArrayBufferView, faceIndex: number = 0, lod: number = 0): void {
  1196. }
  1197. /** @hidden */
  1198. public _uploadImageToTexture(texture: InternalTexture, image: HTMLImageElement, faceIndex: number = 0, lod: number = 0) {
  1199. }
  1200. //------------------------------------------------------------------------------
  1201. // Render Target Textures
  1202. //------------------------------------------------------------------------------
  1203. public createRenderTargetTexture(size: any, options: boolean | RenderTargetCreationOptions): InternalTexture {
  1204. let fullOptions = new RenderTargetCreationOptions();
  1205. if (options !== undefined && typeof options === "object") {
  1206. fullOptions.generateMipMaps = options.generateMipMaps;
  1207. fullOptions.generateDepthBuffer = options.generateDepthBuffer === undefined ? true : options.generateDepthBuffer;
  1208. fullOptions.generateStencilBuffer = fullOptions.generateDepthBuffer && options.generateStencilBuffer;
  1209. fullOptions.type = options.type === undefined ? Constants.TEXTURETYPE_UNSIGNED_INT : options.type;
  1210. fullOptions.samplingMode = options.samplingMode === undefined ? Constants.TEXTURE_TRILINEAR_SAMPLINGMODE : options.samplingMode;
  1211. } else {
  1212. fullOptions.generateMipMaps = <boolean>options;
  1213. fullOptions.generateDepthBuffer = true;
  1214. fullOptions.generateStencilBuffer = false;
  1215. fullOptions.type = Constants.TEXTURETYPE_UNSIGNED_INT;
  1216. fullOptions.samplingMode = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE;
  1217. }
  1218. var texture = new InternalTexture(this, InternalTextureSource.RenderTarget);
  1219. var width = size.width || size;
  1220. var height = size.height || size;
  1221. texture._depthStencilBuffer = {};
  1222. texture._framebuffer = {};
  1223. texture.baseWidth = width;
  1224. texture.baseHeight = height;
  1225. texture.width = width;
  1226. texture.height = height;
  1227. texture.isReady = true;
  1228. texture.samples = 1;
  1229. texture.generateMipMaps = fullOptions.generateMipMaps ? true : false;
  1230. texture.samplingMode = fullOptions.samplingMode;
  1231. texture.type = fullOptions.type;
  1232. texture._generateDepthBuffer = fullOptions.generateDepthBuffer;
  1233. texture._generateStencilBuffer = fullOptions.generateStencilBuffer ? true : false;
  1234. this._internalTexturesCache.push(texture);
  1235. return texture;
  1236. }
  1237. //------------------------------------------------------------------------------
  1238. // Render Commands
  1239. //------------------------------------------------------------------------------
  1240. /**
  1241. * Begin a new frame
  1242. */
  1243. public beginFrame(): void {
  1244. super.beginFrame();
  1245. this._uploadEncoder = this._device.createCommandEncoder(this._uploadEncoderDescriptor);
  1246. this._renderEncoder = this._device.createCommandEncoder(this._renderEncoderDescriptor);
  1247. }
  1248. /**
  1249. * End the current frame
  1250. */
  1251. public endFrame(): void {
  1252. this._endRenderPass();
  1253. this._commandBuffers[0] = this._uploadEncoder.finish();
  1254. this._commandBuffers[1] = this._renderEncoder.finish();
  1255. this._device.defaultQueue.submit(this._commandBuffers);
  1256. super.endFrame();
  1257. }
  1258. //------------------------------------------------------------------------------
  1259. // Render Pass
  1260. //------------------------------------------------------------------------------
  1261. private _startMainRenderPass(): void {
  1262. if (this._currentRenderPass) {
  1263. this._endRenderPass();
  1264. }
  1265. // Resolve in case of MSAA
  1266. if (this._options.antialiasing) {
  1267. this._mainColorAttachments[0].resolveTarget = this._swapChain.getCurrentTexture().createView();
  1268. }
  1269. else {
  1270. this._mainColorAttachments[0].attachment = this._swapChain.getCurrentTexture().createView();
  1271. }
  1272. this._currentRenderPass = this._renderEncoder.beginRenderPass({
  1273. colorAttachments: this._mainColorAttachments,
  1274. depthStencilAttachment: this._mainDepthAttachment
  1275. });
  1276. }
  1277. private _endRenderPass(): void {
  1278. if (this._currentRenderPass) {
  1279. this._currentRenderPass.endPass();
  1280. this._currentRenderPass = null;
  1281. }
  1282. }
  1283. public bindFramebuffer(texture: InternalTexture, faceIndex?: number, requiredWidth?: number, requiredHeight?: number, forceFullscreenViewport?: boolean): void {
  1284. if (this._currentRenderTarget) {
  1285. this.unBindFramebuffer(this._currentRenderTarget);
  1286. }
  1287. this._currentRenderTarget = texture;
  1288. this._currentFramebuffer = texture._MSAAFramebuffer ? texture._MSAAFramebuffer : texture._framebuffer;
  1289. if (this._cachedViewport && !forceFullscreenViewport) {
  1290. this.setViewport(this._cachedViewport, requiredWidth, requiredHeight);
  1291. }
  1292. }
  1293. public unBindFramebuffer(texture: InternalTexture, disableGenerateMipMaps = false, onBeforeUnbind?: () => void): void {
  1294. this._currentRenderTarget = null;
  1295. if (onBeforeUnbind) {
  1296. if (texture._MSAAFramebuffer) {
  1297. this._currentFramebuffer = texture._framebuffer;
  1298. }
  1299. onBeforeUnbind();
  1300. }
  1301. this._currentFramebuffer = null;
  1302. }
  1303. //------------------------------------------------------------------------------
  1304. // Render
  1305. //------------------------------------------------------------------------------
  1306. private _getTopology(fillMode: number): GPUPrimitiveTopology {
  1307. switch (fillMode) {
  1308. // Triangle views
  1309. case Constants.MATERIAL_TriangleFillMode:
  1310. return WebGPUConstants.GPUPrimitiveTopology_triangleList;
  1311. case Constants.MATERIAL_PointFillMode:
  1312. return WebGPUConstants.GPUPrimitiveTopology_pointList;
  1313. case Constants.MATERIAL_WireFrameFillMode:
  1314. return WebGPUConstants.GPUPrimitiveTopology_lineList;
  1315. // Draw modes
  1316. case Constants.MATERIAL_PointListDrawMode:
  1317. return WebGPUConstants.GPUPrimitiveTopology_pointList;
  1318. case Constants.MATERIAL_LineListDrawMode:
  1319. return WebGPUConstants.GPUPrimitiveTopology_lineList;
  1320. case Constants.MATERIAL_LineLoopDrawMode:
  1321. // return this._gl.LINE_LOOP;
  1322. // TODO WEBGPU. Line Loop Mode Fallback at buffer load time.
  1323. throw "LineLoop is an unsupported fillmode in WebGPU";
  1324. case Constants.MATERIAL_LineStripDrawMode:
  1325. return WebGPUConstants.GPUPrimitiveTopology_lineStrip;
  1326. case Constants.MATERIAL_TriangleStripDrawMode:
  1327. return WebGPUConstants.GPUPrimitiveTopology_triangleStrip;
  1328. case Constants.MATERIAL_TriangleFanDrawMode:
  1329. // return this._gl.TRIANGLE_FAN;
  1330. // TODO WEBGPU. Triangle Fan Mode Fallback at buffer load time.
  1331. throw "TriangleFan is an unsupported fillmode in WebGPU";
  1332. default:
  1333. return WebGPUConstants.GPUPrimitiveTopology_triangleList;
  1334. }
  1335. }
  1336. private _getCompareFunction(compareFunction: Nullable<number>): GPUCompareFunction {
  1337. switch (compareFunction) {
  1338. case Constants.ALWAYS:
  1339. return WebGPUConstants.GPUCompareFunction_always;
  1340. case Constants.EQUAL:
  1341. return WebGPUConstants.GPUCompareFunction_equal;
  1342. case Constants.GREATER:
  1343. return WebGPUConstants.GPUCompareFunction_greater;
  1344. case Constants.GEQUAL:
  1345. return WebGPUConstants.GPUCompareFunction_greaterEqual;
  1346. case Constants.LESS:
  1347. return WebGPUConstants.GPUCompareFunction_less;
  1348. case Constants.LEQUAL:
  1349. return WebGPUConstants.GPUCompareFunction_lessEqual;
  1350. case Constants.NEVER:
  1351. return WebGPUConstants.GPUCompareFunction_never;
  1352. case Constants.NOTEQUAL:
  1353. return WebGPUConstants.GPUCompareFunction_notEqual;
  1354. default:
  1355. return WebGPUConstants.GPUCompareFunction_less;
  1356. }
  1357. }
  1358. private _getOpFunction(operation: Nullable<number>, defaultOp: GPUStencilOperation): GPUStencilOperation {
  1359. switch (operation) {
  1360. case Constants.KEEP:
  1361. return WebGPUConstants.GPUStencilOperation_keep;
  1362. case Constants.ZERO:
  1363. return WebGPUConstants.GPUStencilOperation_zero;
  1364. case Constants.REPLACE:
  1365. return WebGPUConstants.GPUStencilOperation_replace;
  1366. case Constants.INVERT:
  1367. return WebGPUConstants.GPUStencilOperation_invert;
  1368. case Constants.INCR:
  1369. return WebGPUConstants.GPUStencilOperation_incrementClamp;
  1370. case Constants.DECR:
  1371. return WebGPUConstants.GPUStencilOperation_decrementClamp;
  1372. case Constants.INCR_WRAP:
  1373. return WebGPUConstants.GPUStencilOperation_incrementWrap;
  1374. case Constants.DECR_WRAP:
  1375. return WebGPUConstants.GPUStencilOperation_decrementWrap;
  1376. default:
  1377. return defaultOp;
  1378. }
  1379. }
  1380. private _getDepthStencilStateDescriptor(): GPUDepthStencilStateDescriptor {
  1381. // TODO WEBGPU. Depth State according to the cached state.
  1382. // And the current render pass attachment setup.
  1383. const stencilFrontBack: GPUStencilStateFaceDescriptor = {
  1384. compare: this._getCompareFunction(this._stencilState.stencilFunc),
  1385. depthFailOp: this._getOpFunction(this._stencilState.stencilOpDepthFail, WebGPUConstants.GPUStencilOperation_keep),
  1386. failOp: this._getOpFunction(this._stencilState.stencilOpStencilFail, WebGPUConstants.GPUStencilOperation_keep),
  1387. passOp: this._getOpFunction(this._stencilState.stencilOpStencilDepthPass, WebGPUConstants.GPUStencilOperation_replace)
  1388. };
  1389. return {
  1390. depthWriteEnabled: this.getDepthWrite(),
  1391. depthCompare: this._getCompareFunction(this.getDepthFunction()),
  1392. format: WebGPUConstants.GPUTextureFormat_depth24plusStencil8,
  1393. stencilFront: stencilFrontBack,
  1394. stencilBack: stencilFrontBack,
  1395. stencilReadMask: this._stencilState.stencilFuncMask,
  1396. stencilWriteMask: this._stencilState.stencilMask,
  1397. };
  1398. }
  1399. /**
  1400. * Set various states to the webGL context
  1401. * @param culling defines backface culling state
  1402. * @param zOffset defines the value to apply to zOffset (0 by default)
  1403. * @param force defines if states must be applied even if cache is up to date
  1404. * @param reverseSide defines if culling must be reversed (CCW instead of CW and CW instead of CCW)
  1405. */
  1406. public setState(culling: boolean, zOffset: number = 0, force?: boolean, reverseSide = false): void {
  1407. // Culling
  1408. if (this._depthCullingState.cull !== culling || force) {
  1409. this._depthCullingState.cull = culling;
  1410. }
  1411. // Cull face
  1412. // var cullFace = this.cullBackFaces ? this._gl.BACK : this._gl.FRONT;
  1413. var cullFace = this.cullBackFaces ? 1 : 2;
  1414. if (this._depthCullingState.cullFace !== cullFace || force) {
  1415. this._depthCullingState.cullFace = cullFace;
  1416. }
  1417. // Z offset
  1418. this.setZOffset(zOffset);
  1419. // Front face
  1420. // var frontFace = reverseSide ? this._gl.CW : this._gl.CCW;
  1421. var frontFace = reverseSide ? 1 : 2;
  1422. if (this._depthCullingState.frontFace !== frontFace || force) {
  1423. this._depthCullingState.frontFace = frontFace;
  1424. }
  1425. }
  1426. private _getFrontFace(): GPUFrontFace {
  1427. switch (this._depthCullingState.frontFace) {
  1428. case 1:
  1429. return WebGPUConstants.GPUFrontFace_ccw;
  1430. default:
  1431. return WebGPUConstants.GPUFrontFace_cw;
  1432. }
  1433. }
  1434. private _getCullMode(): GPUCullMode {
  1435. if (this._depthCullingState.cull === false) {
  1436. return WebGPUConstants.GPUCullMode_none;
  1437. }
  1438. if (this._depthCullingState.cullFace === 2) {
  1439. return WebGPUConstants.GPUCullMode_front;
  1440. }
  1441. else {
  1442. return WebGPUConstants.GPUCullMode_back;
  1443. }
  1444. }
  1445. private _getRasterizationStateDescriptor(): GPURasterizationStateDescriptor {
  1446. return {
  1447. frontFace: this._getFrontFace(),
  1448. cullMode: this._getCullMode(),
  1449. depthBias: this._depthCullingState.zOffset,
  1450. // depthBiasClamp: 0,
  1451. // depthBiasSlopeScale: 0,
  1452. };
  1453. }
  1454. private _getWriteMask(): number {
  1455. if (this.__colorWrite) {
  1456. return WebGPUConstants.GPUColorWriteBits_ALL;
  1457. }
  1458. return WebGPUConstants.GPUColorWriteBits_NONE;
  1459. }
  1460. /**
  1461. * Sets the current alpha mode
  1462. * @param mode defines the mode to use (one of the Engine.ALPHA_XXX)
  1463. * @param noDepthWriteChange defines if depth writing state should remains unchanged (false by default)
  1464. * @see http://doc.babylonjs.com/resources/transparency_and_how_meshes_are_rendered
  1465. */
  1466. public setAlphaMode(mode: number, noDepthWriteChange: boolean = false): void {
  1467. if (this._alphaMode === mode) {
  1468. return;
  1469. }
  1470. switch (mode) {
  1471. case Engine.ALPHA_DISABLE:
  1472. this._alphaState.alphaBlend = false;
  1473. break;
  1474. case Engine.ALPHA_PREMULTIPLIED:
  1475. // this._alphaState.setAlphaBlendFunctionParameters(this._gl.ONE, this._gl.ONE_MINUS_SRC_ALPHA, this._gl.ONE, this._gl.ONE);
  1476. this._alphaState.setAlphaBlendFunctionParameters(1, 0x0303, 1, 1);
  1477. this._alphaState.alphaBlend = true;
  1478. break;
  1479. case Engine.ALPHA_PREMULTIPLIED_PORTERDUFF:
  1480. // this._alphaState.setAlphaBlendFunctionParameters(this._gl.ONE, this._gl.ONE_MINUS_SRC_ALPHA, this._gl.ONE, this._gl.ONE_MINUS_SRC_ALPHA);
  1481. this._alphaState.setAlphaBlendFunctionParameters(1, 0x0303, 1, 0x0303);
  1482. this._alphaState.alphaBlend = true;
  1483. break;
  1484. case Engine.ALPHA_COMBINE:
  1485. // this._alphaState.setAlphaBlendFunctionParameters(this._gl.SRC_ALPHA, this._gl.ONE_MINUS_SRC_ALPHA, this._gl.ONE, this._gl.ONE);
  1486. this._alphaState.setAlphaBlendFunctionParameters(0x0302, 0x0303, 1, 1);
  1487. this._alphaState.alphaBlend = true;
  1488. break;
  1489. case Engine.ALPHA_ONEONE:
  1490. // this._alphaState.setAlphaBlendFunctionParameters(this._gl.ONE, this._gl.ONE, this._gl.ZERO, this._gl.ONE);
  1491. this._alphaState.setAlphaBlendFunctionParameters(1, 1, 0, 1);
  1492. this._alphaState.alphaBlend = true;
  1493. break;
  1494. case Engine.ALPHA_ADD:
  1495. // this._alphaState.setAlphaBlendFunctionParameters(this._gl.SRC_ALPHA, this._gl.ONE, this._gl.ZERO, this._gl.ONE);
  1496. this._alphaState.setAlphaBlendFunctionParameters(0x0302, 1, 0, 1);
  1497. this._alphaState.alphaBlend = true;
  1498. break;
  1499. case Engine.ALPHA_SUBTRACT:
  1500. // this._alphaState.setAlphaBlendFunctionParameters(this._gl.ZERO, this._gl.ONE_MINUS_SRC_COLOR, this._gl.ONE, this._gl.ONE);
  1501. this._alphaState.setAlphaBlendFunctionParameters(0, 0x0301, 1, 1);
  1502. this._alphaState.alphaBlend = true;
  1503. break;
  1504. case Engine.ALPHA_MULTIPLY:
  1505. // this._alphaState.setAlphaBlendFunctionParameters(this._gl.DST_COLOR, this._gl.ZERO, this._gl.ONE, this._gl.ONE);
  1506. this._alphaState.setAlphaBlendFunctionParameters(0x0306, 0, 1, 1);
  1507. this._alphaState.alphaBlend = true;
  1508. break;
  1509. case Engine.ALPHA_MAXIMIZED:
  1510. // this._alphaState.setAlphaBlendFunctionParameters(this._gl.SRC_ALPHA, this._gl.ONE_MINUS_SRC_COLOR, this._gl.ONE, this._gl.ONE);
  1511. this._alphaState.setAlphaBlendFunctionParameters(0x0302, 0x0301, 1, 1);
  1512. this._alphaState.alphaBlend = true;
  1513. break;
  1514. case Engine.ALPHA_INTERPOLATE:
  1515. // this._alphaState.setAlphaBlendFunctionParameters(this._gl.CONSTANT_COLOR, this._gl.ONE_MINUS_CONSTANT_COLOR, this._gl.CONSTANT_ALPHA, this._gl.ONE_MINUS_CONSTANT_ALPHA);
  1516. this._alphaState.setAlphaBlendFunctionParameters(0x8001, 0x8002, 0x8003, 0x8004);
  1517. this._alphaState.alphaBlend = true;
  1518. break;
  1519. case Engine.ALPHA_SCREENMODE:
  1520. // this._alphaState.setAlphaBlendFunctionParameters(this._gl.ONE, this._gl.ONE_MINUS_SRC_COLOR, this._gl.ONE, this._gl.ONE_MINUS_SRC_ALPHA);
  1521. this._alphaState.setAlphaBlendFunctionParameters(1, 0x0301, 1, 0x0303);
  1522. this._alphaState.alphaBlend = true;
  1523. break;
  1524. }
  1525. if (!noDepthWriteChange) {
  1526. this.setDepthWrite(mode === Engine.ALPHA_DISABLE);
  1527. }
  1528. this._alphaMode = mode;
  1529. }
  1530. private _getAphaBlendOperation(operation: Nullable<number>): GPUBlendOperation {
  1531. switch (operation) {
  1532. case 0x8006:
  1533. return WebGPUConstants.GPUBlendOperation_add;
  1534. case 0x800A:
  1535. return WebGPUConstants.GPUBlendOperation_substract;
  1536. case 0x800B:
  1537. return WebGPUConstants.GPUBlendOperation_reverseSubtract;
  1538. default:
  1539. return WebGPUConstants.GPUBlendOperation_add;
  1540. }
  1541. }
  1542. private _getAphaBlendFactor(factor: Nullable<number>): GPUBlendFactor {
  1543. switch (factor) {
  1544. case 0:
  1545. return WebGPUConstants.GPUBlendFactor_zero;
  1546. case 1:
  1547. return WebGPUConstants.GPUBlendFactor_one;
  1548. case 0x0300:
  1549. return WebGPUConstants.GPUBlendFactor_srcColor;
  1550. case 0x0301:
  1551. return WebGPUConstants.GPUBlendFactor_oneMinusSrcColor;
  1552. case 0x0302:
  1553. return WebGPUConstants.GPUBlendFactor_srcAlpha;
  1554. case 0x0303:
  1555. return WebGPUConstants.GPUBlendFactor_oneMinusSrcAlpha;
  1556. case 0x0304:
  1557. return WebGPUConstants.GPUBlendFactor_dstAlpha;
  1558. case 0x0305:
  1559. return WebGPUConstants.GPUBlendFactor_oneMinusDstAlpha;
  1560. case 0x0306:
  1561. return WebGPUConstants.GPUBlendFactor_dstColor;
  1562. case 0x0307:
  1563. return WebGPUConstants.GPUBlendFactor_oneMinusDstColor;
  1564. case 0x0308:
  1565. return WebGPUConstants.GPUBlendFactor_srcAlphaSaturated;
  1566. case 0x8001:
  1567. return WebGPUConstants.GPUBlendFactor_blendColor;
  1568. case 0x8002:
  1569. return WebGPUConstants.GPUBlendFactor_oneMinusBlendColor;
  1570. case 0x8003:
  1571. return WebGPUConstants.GPUBlendFactor_blendColor;
  1572. case 0x8004:
  1573. return WebGPUConstants.GPUBlendFactor_oneMinusBlendColor;
  1574. default:
  1575. return WebGPUConstants.GPUBlendFactor_one;
  1576. }
  1577. }
  1578. private _getAphaBlendState(): GPUBlendDescriptor {
  1579. if (!this._alphaState.alphaBlend) {
  1580. return { };
  1581. }
  1582. return {
  1583. srcFactor: this._getAphaBlendFactor(this._alphaState._blendFunctionParameters[2]),
  1584. dstFactor: this._getAphaBlendFactor(this._alphaState._blendFunctionParameters[3]),
  1585. operation: this._getAphaBlendOperation(this._alphaState._blendEquationParameters[1]),
  1586. };
  1587. }
  1588. private _getColorBlendState(): GPUBlendDescriptor {
  1589. if (!this._alphaState.alphaBlend) {
  1590. return { };
  1591. }
  1592. return {
  1593. srcFactor: this._getAphaBlendFactor(this._alphaState._blendFunctionParameters[0]),
  1594. dstFactor: this._getAphaBlendFactor(this._alphaState._blendFunctionParameters[1]),
  1595. operation: this._getAphaBlendOperation(this._alphaState._blendEquationParameters[0]),
  1596. };
  1597. }
  1598. private _getColorStateDescriptors(): GPUColorStateDescriptor[] {
  1599. // TODO WEBGPU. Manage Multi render target.
  1600. return [{
  1601. format: this._options.swapChainFormat!,
  1602. alphaBlend: this._getAphaBlendState(),
  1603. colorBlend: this._getColorBlendState(),
  1604. writeMask: this._getWriteMask(),
  1605. }];
  1606. }
  1607. private _getStages(): GPURenderPipelineStageDescriptor {
  1608. const gpuPipeline = this._currentEffect!._pipelineContext as WebGPUPipelineContext;
  1609. return gpuPipeline.stages!;
  1610. }
  1611. private _getVertexInputDescriptorFormat(vertexBuffer: VertexBuffer): GPUVertexFormat {
  1612. const kind = vertexBuffer.getKind();
  1613. const type = vertexBuffer.type;
  1614. const normalized = vertexBuffer.normalized;
  1615. const size = vertexBuffer.getSize();
  1616. switch (type) {
  1617. case VertexBuffer.BYTE:
  1618. switch (size) {
  1619. case 2:
  1620. return normalized ? WebGPUConstants.GPUVertexFormat_char2norm : WebGPUConstants.GPUVertexFormat_char2;
  1621. case 4:
  1622. return normalized ? WebGPUConstants.GPUVertexFormat_char4norm : WebGPUConstants.GPUVertexFormat_char4;
  1623. }
  1624. case VertexBuffer.UNSIGNED_BYTE:
  1625. switch (size) {
  1626. case 2:
  1627. return normalized ? WebGPUConstants.GPUVertexFormat_uchar2norm : WebGPUConstants.GPUVertexFormat_uchar2;
  1628. case 4:
  1629. return normalized ? WebGPUConstants.GPUVertexFormat_uchar4norm : WebGPUConstants.GPUVertexFormat_uchar4;
  1630. }
  1631. case VertexBuffer.SHORT:
  1632. switch (size) {
  1633. case 2:
  1634. return normalized ? WebGPUConstants.GPUVertexFormat_short2norm : WebGPUConstants.GPUVertexFormat_short2;
  1635. case 4:
  1636. return normalized ? WebGPUConstants.GPUVertexFormat_short4norm : WebGPUConstants.GPUVertexFormat_short4;
  1637. }
  1638. case VertexBuffer.UNSIGNED_SHORT:
  1639. switch (size) {
  1640. case 2:
  1641. return normalized ? WebGPUConstants.GPUVertexFormat_ushort2norm : WebGPUConstants.GPUVertexFormat_ushort2;
  1642. case 4:
  1643. return normalized ? WebGPUConstants.GPUVertexFormat_ushort4norm : WebGPUConstants.GPUVertexFormat_ushort4;
  1644. }
  1645. case VertexBuffer.INT:
  1646. switch (size) {
  1647. case 1:
  1648. return WebGPUConstants.GPUVertexFormat_int;
  1649. case 2:
  1650. return WebGPUConstants.GPUVertexFormat_int2;
  1651. case 3:
  1652. return WebGPUConstants.GPUVertexFormat_int3;
  1653. case 4:
  1654. return WebGPUConstants.GPUVertexFormat_int4;
  1655. }
  1656. case VertexBuffer.UNSIGNED_INT:
  1657. switch (size) {
  1658. case 1:
  1659. return WebGPUConstants.GPUVertexFormat_uint;
  1660. case 2:
  1661. return WebGPUConstants.GPUVertexFormat_uint2;
  1662. case 3:
  1663. return WebGPUConstants.GPUVertexFormat_uint3;
  1664. case 4:
  1665. return WebGPUConstants.GPUVertexFormat_uint4;
  1666. }
  1667. case VertexBuffer.FLOAT:
  1668. switch (size) {
  1669. case 1:
  1670. return WebGPUConstants.GPUVertexFormat_float;
  1671. case 2:
  1672. return WebGPUConstants.GPUVertexFormat_float2;
  1673. case 3:
  1674. return WebGPUConstants.GPUVertexFormat_float3;
  1675. case 4:
  1676. return WebGPUConstants.GPUVertexFormat_float4;
  1677. }
  1678. }
  1679. throw new Error("Invalid Format '" + kind + "'");
  1680. }
  1681. private _getVertexInputDescriptor(): GPUVertexStateDescriptor {
  1682. const descriptors: GPUVertexBufferLayoutDescriptor[] = [];
  1683. const effect = this._currentEffect!;
  1684. const attributes = effect.getAttributesNames();
  1685. for (var index = 0; index < attributes.length; index++) {
  1686. const location = effect.getAttributeLocation(index);
  1687. if (location >= 0) {
  1688. const vertexBuffer = this._currentVertexBuffers![attributes[index]];
  1689. if (!vertexBuffer) {
  1690. continue;
  1691. }
  1692. const positionAttributeDescriptor: GPUVertexAttributeDescriptor = {
  1693. shaderLocation: location,
  1694. offset: 0, // not available in WebGL
  1695. format: this._getVertexInputDescriptorFormat(vertexBuffer),
  1696. };
  1697. // TODO WEBGPU. Factorize the one with the same underlying buffer.
  1698. const vertexBufferDescriptor: GPUVertexBufferLayoutDescriptor = {
  1699. arrayStride: vertexBuffer.byteStride,
  1700. stepMode: vertexBuffer.getIsInstanced() ? WebGPUConstants.GPUInputStepMode_instance : WebGPUConstants.GPUInputStepMode_vertex,
  1701. attributes: [positionAttributeDescriptor]
  1702. };
  1703. descriptors.push(vertexBufferDescriptor);
  1704. }
  1705. }
  1706. if (!this._currentIndexBuffer) {
  1707. return {
  1708. indexFormat: WebGPUConstants.GPUIndexFormat_uint32,
  1709. vertexBuffers: descriptors
  1710. };
  1711. }
  1712. const inputStateDescriptor: GPUVertexStateDescriptor = {
  1713. indexFormat: this._currentIndexBuffer!.is32Bits ? WebGPUConstants.GPUIndexFormat_uint32 : WebGPUConstants.GPUIndexFormat_uint16,
  1714. vertexBuffers: descriptors
  1715. };
  1716. return inputStateDescriptor;
  1717. }
  1718. private _getPipelineLayout(): GPUPipelineLayout {
  1719. const bindGroupLayouts: GPUBindGroupLayout[] = [];
  1720. const webgpuPipelineContext = this._currentEffect!._pipelineContext as WebGPUPipelineContext;
  1721. for (let i = 0; i < webgpuPipelineContext.orderedUBOsAndSamplers.length; i++) {
  1722. const setDefinition = webgpuPipelineContext.orderedUBOsAndSamplers[i];
  1723. if (setDefinition === undefined) {
  1724. const bindings: GPUBindGroupLayoutBinding[] = [];
  1725. const uniformsBindGroupLayout = this._device.createBindGroupLayout({
  1726. bindings,
  1727. });
  1728. bindGroupLayouts[i] = uniformsBindGroupLayout;
  1729. continue;
  1730. }
  1731. const bindings: GPUBindGroupLayoutBinding[] = [];
  1732. for (let j = 0; j < setDefinition.length; j++) {
  1733. const bindingDefinition = webgpuPipelineContext.orderedUBOsAndSamplers[i][j];
  1734. if (bindingDefinition === undefined) {
  1735. continue;
  1736. }
  1737. // TODO WEBGPU. Optimize shared samplers visibility for vertex/framgent.
  1738. if (bindingDefinition.isSampler) {
  1739. bindings.push({
  1740. binding: j,
  1741. visibility: WebGPUConstants.GPUShaderStageBit_VERTEX | WebGPUConstants.GPUShaderStageBit_FRAGMENT,
  1742. type: WebGPUConstants.GPUBindingType_sampledTexture,
  1743. textureDimension: bindingDefinition.textureDimension,
  1744. // TODO WEBGPU. Handle texture component type properly.
  1745. // textureComponentType?: GPUTextureComponentType,
  1746. }, {
  1747. // TODO WEBGPU. No Magic + 1 (coming from current 1 texture 1 sampler startegy).
  1748. binding: j + 1,
  1749. visibility: WebGPUConstants.GPUShaderStageBit_VERTEX | WebGPUConstants.GPUShaderStageBit_FRAGMENT,
  1750. type: WebGPUConstants.GPUBindingType_sampler
  1751. });
  1752. }
  1753. else {
  1754. bindings.push({
  1755. binding: j,
  1756. visibility: WebGPUConstants.GPUShaderStageBit_VERTEX | WebGPUConstants.GPUShaderStageBit_FRAGMENT,
  1757. type: WebGPUConstants.GPUBindingType_uniformBuffer,
  1758. });
  1759. }
  1760. }
  1761. if (bindings.length > 0) {
  1762. const uniformsBindGroupLayout = this._device.createBindGroupLayout({
  1763. bindings,
  1764. });
  1765. bindGroupLayouts[i] = uniformsBindGroupLayout;
  1766. }
  1767. }
  1768. webgpuPipelineContext.bindGroupLayouts = bindGroupLayouts;
  1769. return this._device.createPipelineLayout({ bindGroupLayouts });
  1770. }
  1771. private _getRenderPipeline(fillMode: number): GPURenderPipeline {
  1772. // This is wrong to cache this way but workarounds the need of cache in the simple demo context.
  1773. const gpuPipeline = this._currentEffect!._pipelineContext as WebGPUPipelineContext;
  1774. if (gpuPipeline.renderPipeline) {
  1775. return gpuPipeline.renderPipeline;
  1776. }
  1777. // Unsupported at the moment but needs to be extracted from the MSAA param.
  1778. const topology = this._getTopology(fillMode);
  1779. const rasterizationStateDescriptor = this._getRasterizationStateDescriptor();
  1780. const depthStateDescriptor = this._getDepthStencilStateDescriptor();
  1781. const colorStateDescriptors = this._getColorStateDescriptors();
  1782. const stages = this._getStages();
  1783. const inputStateDescriptor = this._getVertexInputDescriptor();
  1784. const pipelineLayout = this._getPipelineLayout();
  1785. gpuPipeline.renderPipeline = this._device.createRenderPipeline({
  1786. sampleCount: this._mainPassSampleCount,
  1787. primitiveTopology: topology,
  1788. rasterizationState: rasterizationStateDescriptor,
  1789. depthStencilState: depthStateDescriptor,
  1790. colorStates: colorStateDescriptors,
  1791. ...stages,
  1792. vertexState: inputStateDescriptor,
  1793. layout: pipelineLayout,
  1794. });
  1795. return gpuPipeline.renderPipeline;
  1796. }
  1797. private _getVertexInputsToRender(): IWebGPUPipelineContextVertexInputsCache {
  1798. const effect = this._currentEffect!;
  1799. const gpuContext = this._currentEffect!._pipelineContext as WebGPUPipelineContext;
  1800. let vertexInputs = gpuContext.vertexInputs;
  1801. if (vertexInputs) {
  1802. return vertexInputs;
  1803. }
  1804. vertexInputs = {
  1805. indexBuffer: null,
  1806. indexOffset: 0,
  1807. vertexStartSlot: 0,
  1808. vertexBuffers: [],
  1809. vertexOffsets: [],
  1810. };
  1811. gpuContext.vertexInputs = vertexInputs;
  1812. if (this._currentIndexBuffer) {
  1813. // TODO WEBGPU. Check if cache would be worth it.
  1814. vertexInputs.indexBuffer = this._currentIndexBuffer.underlyingResource;
  1815. vertexInputs.indexOffset = 0;
  1816. }
  1817. else {
  1818. vertexInputs.indexBuffer = null;
  1819. }
  1820. const attributes = effect.getAttributesNames();
  1821. for (var index = 0; index < attributes.length; index++) {
  1822. const order = effect.getAttributeLocation(index);
  1823. if (order >= 0) {
  1824. const vertexBuffer = this._currentVertexBuffers![attributes[index]];
  1825. if (!vertexBuffer) {
  1826. continue;
  1827. }
  1828. var buffer = vertexBuffer.getBuffer();
  1829. if (buffer) {
  1830. vertexInputs.vertexBuffers.push(buffer.underlyingResource);
  1831. vertexInputs.vertexOffsets.push(vertexBuffer.byteOffset);
  1832. }
  1833. }
  1834. }
  1835. // TODO WEBGPU. Optimize buffer reusability and types as more are now allowed.
  1836. return vertexInputs;
  1837. }
  1838. private _getBindGroupsToRender(): GPUBindGroup[] {
  1839. const webgpuPipelineContext = this._currentEffect!._pipelineContext as WebGPUPipelineContext;
  1840. let bindGroups = webgpuPipelineContext.bindGroups;
  1841. if (bindGroups) {
  1842. if (webgpuPipelineContext.uniformBuffer) {
  1843. webgpuPipelineContext.uniformBuffer.update();
  1844. }
  1845. return bindGroups;
  1846. }
  1847. if (webgpuPipelineContext.uniformBuffer) {
  1848. this.bindUniformBufferBase(webgpuPipelineContext.uniformBuffer.getBuffer()!, 0, "LeftOver");
  1849. webgpuPipelineContext.uniformBuffer.update();
  1850. }
  1851. bindGroups = [];
  1852. webgpuPipelineContext.bindGroups = bindGroups;
  1853. const bindGroupLayouts = webgpuPipelineContext.bindGroupLayouts;
  1854. for (let i = 0; i < webgpuPipelineContext.orderedUBOsAndSamplers.length; i++) {
  1855. const setDefinition = webgpuPipelineContext.orderedUBOsAndSamplers[i];
  1856. if (setDefinition === undefined) {
  1857. const groupLayout = bindGroupLayouts[i];
  1858. if (groupLayout) {
  1859. bindGroups[i] = this._device.createBindGroup({
  1860. layout: groupLayout,
  1861. bindings: [],
  1862. });
  1863. }
  1864. continue;
  1865. }
  1866. const bindings: GPUBindGroupBinding[] = [];
  1867. for (let j = 0; j < setDefinition.length; j++) {
  1868. const bindingDefinition = webgpuPipelineContext.orderedUBOsAndSamplers[i][j];
  1869. if (bindingDefinition === undefined) {
  1870. continue;
  1871. }
  1872. // TODO WEBGPU. Authorize shared samplers and Vertex Textures.
  1873. if (bindingDefinition.isSampler) {
  1874. const bindingInfo = webgpuPipelineContext.samplers[bindingDefinition.name];
  1875. if (bindingInfo) {
  1876. if (!bindingInfo.texture._webGPUSampler) {
  1877. const samplerDescriptor: GPUSamplerDescriptor = this._getSamplerDescriptor(bindingInfo.texture!);
  1878. const gpuSampler = this._device.createSampler(samplerDescriptor);
  1879. bindingInfo.texture._webGPUSampler = gpuSampler;
  1880. }
  1881. bindings.push({
  1882. binding: bindingInfo.textureBinding,
  1883. resource: bindingInfo.texture._webGPUTextureView!,
  1884. }, {
  1885. binding: bindingInfo.samplerBinding,
  1886. resource: bindingInfo.texture._webGPUSampler!,
  1887. });
  1888. }
  1889. else {
  1890. Logger.Error("Sampler has not been bound: " + bindingDefinition.name);
  1891. }
  1892. }
  1893. else {
  1894. const dataBuffer = this._uniformsBuffers[bindingDefinition.name];
  1895. if (dataBuffer) {
  1896. const webgpuBuffer = dataBuffer.underlyingResource as GPUBuffer;
  1897. bindings.push({
  1898. binding: j,
  1899. resource: {
  1900. buffer: webgpuBuffer,
  1901. offset: 0,
  1902. size: dataBuffer.capacity,
  1903. },
  1904. });
  1905. }
  1906. else {
  1907. Logger.Error("UBO has not been bound: " + bindingDefinition.name);
  1908. }
  1909. }
  1910. }
  1911. const groupLayout = bindGroupLayouts[i];
  1912. if (groupLayout) {
  1913. bindGroups[i] = this._device.createBindGroup({
  1914. layout: groupLayout,
  1915. bindings,
  1916. });
  1917. }
  1918. }
  1919. return bindGroups;
  1920. }
  1921. private _bindVertexInputs(vertexInputs: IWebGPUPipelineContextVertexInputsCache): void {
  1922. const renderPass = this._bundleEncoder || this._currentRenderPass!;
  1923. if (vertexInputs.indexBuffer) {
  1924. // TODO WEBGPU. Check if cache would be worth it.
  1925. renderPass.setIndexBuffer(vertexInputs.indexBuffer, vertexInputs.indexOffset);
  1926. }
  1927. // TODO WEBGPU. Optimize buffer reusability and types as more are now allowed.
  1928. for (let i = 0; i < vertexInputs.vertexBuffers.length; i++) {
  1929. const buf = vertexInputs.vertexBuffers[i];
  1930. if (buf) {
  1931. renderPass.setVertexBuffer(vertexInputs.vertexStartSlot + i, vertexInputs.vertexBuffers[i], vertexInputs.vertexOffsets[i]);
  1932. }
  1933. }
  1934. }
  1935. private _setRenderBindGroups(bindGroups: GPUBindGroup[]): void {
  1936. // TODO WEBGPU. Only set groups if changes happened.
  1937. const renderPass = this._bundleEncoder || this._currentRenderPass!;
  1938. for (let i = 0; i < bindGroups.length; i++) {
  1939. renderPass.setBindGroup(i, bindGroups[i]);
  1940. }
  1941. }
  1942. private _setRenderPipeline(fillMode: number): void {
  1943. const renderPass = this._bundleEncoder || this._currentRenderPass!;
  1944. const pipeline = this._getRenderPipeline(fillMode);
  1945. renderPass.setPipeline(pipeline);
  1946. const vertexInputs = this._getVertexInputsToRender();
  1947. this._bindVertexInputs(vertexInputs);
  1948. const bindGroups = this._getBindGroupsToRender();
  1949. this._setRenderBindGroups(bindGroups);
  1950. if (this._alphaState.alphaBlend && this._alphaState._isBlendConstantsDirty) {
  1951. // TODO WebGPU. should use renderPass.
  1952. this._currentRenderPass!.setBlendColor(this._alphaState._blendConstants as any);
  1953. }
  1954. }
  1955. public drawElementsType(fillMode: number, indexStart: number, indexCount: number, instancesCount: number = 1): void {
  1956. const renderPass = this._bundleEncoder || this._currentRenderPass!;
  1957. this._setRenderPipeline(fillMode);
  1958. renderPass.drawIndexed(indexCount, instancesCount, indexStart, 0, 0);
  1959. }
  1960. public drawArraysType(fillMode: number, verticesStart: number, verticesCount: number, instancesCount: number = 1): void {
  1961. const renderPass = this._bundleEncoder || this._currentRenderPass!;
  1962. this._currentIndexBuffer = null;
  1963. this._setRenderPipeline(fillMode);
  1964. renderPass.draw(verticesCount, instancesCount, verticesStart, 0);
  1965. }
  1966. /**
  1967. * Force a specific size of the canvas
  1968. * @param width defines the new canvas' width
  1969. * @param height defines the new canvas' height
  1970. */
  1971. public setSize(width: number, height: number): void {
  1972. super.setSize(width, height);
  1973. this._initializeMainAttachments();
  1974. }
  1975. //------------------------------------------------------------------------------
  1976. // Render Bundle
  1977. //------------------------------------------------------------------------------
  1978. private _bundleEncoder: Nullable<GPURenderBundleEncoder>;
  1979. /**
  1980. * Start recording all the gpu calls into a bundle.
  1981. */
  1982. public startRecordBundle(): void {
  1983. // TODO. WebGPU. options should be dynamic.
  1984. this._bundleEncoder = this._device.createRenderBundleEncoder({
  1985. colorFormats: [ WebGPUConstants.GPUTextureFormat_bgra8unorm ],
  1986. depthStencilFormat: WebGPUConstants.GPUTextureFormat_depth24plusStencil8,
  1987. sampleCount: this._mainPassSampleCount,
  1988. });
  1989. }
  1990. /**
  1991. * Stops recording the bundle.
  1992. * @returns the recorded bundle
  1993. */
  1994. public stopRecordBundle(): GPURenderBundle {
  1995. const bundle = this._bundleEncoder!.finish();
  1996. this._bundleEncoder = null;
  1997. return bundle;
  1998. }
  1999. /**
  2000. * Execute the previously recorded bundle.
  2001. * @param bundles defines the bundle to replay
  2002. */
  2003. public executeBundles(bundles: GPURenderBundle[]): void {
  2004. if (!this._currentRenderPass) {
  2005. this._startMainRenderPass();
  2006. }
  2007. this._currentRenderPass!.executeBundles(bundles);
  2008. }
  2009. //------------------------------------------------------------------------------
  2010. // Dispose
  2011. //------------------------------------------------------------------------------
  2012. /**
  2013. * Dispose and release all associated resources
  2014. */
  2015. public dispose(): void {
  2016. this._decodeEngine.dispose();
  2017. this._compiledShaders = { };
  2018. if (this._mainTexture) {
  2019. this._mainTexture.destroy();
  2020. }
  2021. if (this._depthTexture) {
  2022. this._depthTexture.destroy();
  2023. }
  2024. super.dispose();
  2025. }
  2026. //------------------------------------------------------------------------------
  2027. // Misc
  2028. //------------------------------------------------------------------------------
  2029. public getRenderWidth(useScreen = false): number {
  2030. if (!useScreen && this._currentRenderTarget) {
  2031. return this._currentRenderTarget.width;
  2032. }
  2033. return this._canvas.width;
  2034. }
  2035. public getRenderHeight(useScreen = false): number {
  2036. if (!useScreen && this._currentRenderTarget) {
  2037. return this._currentRenderTarget.height;
  2038. }
  2039. return this._canvas.height;
  2040. }
  2041. public getRenderingCanvas(): Nullable<HTMLCanvasElement> {
  2042. return this._canvas;
  2043. }
  2044. //------------------------------------------------------------------------------
  2045. // Errors
  2046. //------------------------------------------------------------------------------
  2047. public getError(): number {
  2048. // TODO WEBGPU. from the webgpu errors.
  2049. return 0;
  2050. }
  2051. //------------------------------------------------------------------------------
  2052. // Unused WebGPU
  2053. //------------------------------------------------------------------------------
  2054. public areAllEffectsReady(): boolean {
  2055. // No parallel shader compilation.
  2056. return true;
  2057. }
  2058. public _executeWhenRenderingStateIsCompiled(pipelineContext: IPipelineContext, action: () => void) {
  2059. // No parallel shader compilation.
  2060. // No Async, so direct launch
  2061. action();
  2062. }
  2063. public _isRenderingStateCompiled(pipelineContext: IPipelineContext): boolean {
  2064. // No parallel shader compilation.
  2065. return true;
  2066. }
  2067. public _getUnpackAlignement(): number {
  2068. return 1;
  2069. }
  2070. public _unpackFlipY(value: boolean) { }
  2071. // TODO WEBGPU. All of the below should go once engine split with baseEngine.
  2072. public applyStates() {
  2073. // Apply States dynamically.
  2074. // This is done at the pipeline creation level for the moment...
  2075. }
  2076. /** @hidden */
  2077. public _getSamplingParameters(samplingMode: number, generateMipMaps: boolean): { min: number; mag: number } {
  2078. throw "_getSamplingParameters is not available in WebGPU";
  2079. }
  2080. public bindUniformBlock(pipelineContext: IPipelineContext, blockName: string, index: number): void {
  2081. }
  2082. public getUniforms(pipelineContext: IPipelineContext, uniformsNames: string[]): Nullable<WebGLUniformLocation>[] {
  2083. return [];
  2084. }
  2085. public setIntArray(uniform: WebGLUniformLocation, array: Int32Array): void {
  2086. }
  2087. public setIntArray2(uniform: WebGLUniformLocation, array: Int32Array): void {
  2088. }
  2089. public setIntArray3(uniform: WebGLUniformLocation, array: Int32Array): void {
  2090. }
  2091. public setIntArray4(uniform: WebGLUniformLocation, array: Int32Array): void {
  2092. }
  2093. public setArray(uniform: WebGLUniformLocation, array: number[]): void {
  2094. }
  2095. public setArray2(uniform: WebGLUniformLocation, array: number[]): void {
  2096. }
  2097. public setArray3(uniform: WebGLUniformLocation, array: number[]): void {
  2098. }
  2099. public setArray4(uniform: WebGLUniformLocation, array: number[]): void {
  2100. }
  2101. public setMatrices(uniform: WebGLUniformLocation, matrices: Float32Array): void {
  2102. }
  2103. public setMatrix3x3(uniform: WebGLUniformLocation, matrix: Float32Array): void {
  2104. }
  2105. public setMatrix2x2(uniform: WebGLUniformLocation, matrix: Float32Array): void {
  2106. }
  2107. public setFloat(uniform: WebGLUniformLocation, value: number): void {
  2108. }
  2109. public setFloat2(uniform: WebGLUniformLocation, x: number, y: number): void {
  2110. }
  2111. public setFloat3(uniform: WebGLUniformLocation, x: number, y: number, z: number): void {
  2112. }
  2113. public setFloat4(uniform: WebGLUniformLocation, x: number, y: number, z: number, w: number): void {
  2114. }
  2115. }