webgpuEngine.ts 110 KB

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