webgpuEngine.ts 102 KB

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