webgpuEngine.ts 168 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797
  1. import { Logger } from "../Misc/logger";
  2. import { Nullable, DataArray, IndicesArray, FloatArray, Immutable } 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 { WebGPUBufferManager } from './WebGPU/webgpuBufferManager';
  28. import { DepthTextureCreationOptions } from './depthTextureCreationOptions';
  29. import { HardwareTextureWrapper } from '../Materials/Textures/hardwareTextureWrapper';
  30. import { WebGPUHardwareTexture } from './WebGPU/webgpuHardwareTexture';
  31. import { IColor4Like } from '../Maths/math.like';
  32. import { IWebRequest } from '../Misc/interfaces/iWebRequest';
  33. import { UniformBuffer } from '../Materials/uniformBuffer';
  34. declare type VideoTexture = import("../Materials/Textures/videoTexture").VideoTexture;
  35. declare type RenderTargetTexture = import("../Materials/Textures/renderTargetTexture").RenderTargetTexture;
  36. // TODO WEBGPU remove when not needed anymore
  37. function assert(condition: any, msg?: string): asserts condition {
  38. if (!condition) {
  39. throw new Error(msg);
  40. }
  41. }
  42. const dbgShowShaderCode = false;
  43. const dbgSanityChecks = true;
  44. const dbgGenerateLogs = true;
  45. const dbgVerboseLogsForFirstFrames = true;
  46. const dbgVerboseLogsNumFrames = 20;
  47. const dbgShowWarningsNotImplemented = true;
  48. export const dbgShowDebugInliningProcess = false;
  49. /**
  50. * Options to load the associated Glslang library
  51. */
  52. export interface GlslangOptions {
  53. /**
  54. * Defines an existing instance of Glslang (usefull in modules who do not access the global instance).
  55. */
  56. glslang?: any;
  57. /**
  58. * Defines the URL of the glslang JS File.
  59. */
  60. jsPath?: string;
  61. /**
  62. * Defines the URL of the glslang WASM File.
  63. */
  64. wasmPath?: string;
  65. }
  66. /**
  67. * Options to create the WebGPU engine
  68. */
  69. export interface WebGPUEngineOptions extends GPURequestAdapterOptions {
  70. /**
  71. * If delta time between frames should be constant
  72. * @see https://doc.babylonjs.com/babylon101/animations#deterministic-lockstep
  73. */
  74. deterministicLockstep?: boolean;
  75. /**
  76. * Maximum about of steps between frames (Default: 4)
  77. * @see https://doc.babylonjs.com/babylon101/animations#deterministic-lockstep
  78. */
  79. lockstepMaxSteps?: number;
  80. /**
  81. * Defines the seconds between each deterministic lock step
  82. */
  83. timeStep?: number;
  84. /**
  85. * Defines that engine should ignore modifying touch action attribute and style
  86. * If not handle, you might need to set it up on your side for expected touch devices behavior.
  87. */
  88. doNotHandleTouchAction?: boolean;
  89. /**
  90. * Defines if webaudio should be initialized as well
  91. * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music
  92. */
  93. audioEngine?: boolean;
  94. /**
  95. * Defines the category of adapter to use.
  96. * Is it the discrete or integrated device.
  97. */
  98. powerPreference?: GPUPowerPreference;
  99. /**
  100. * Defines the device descriptor used to create a device.
  101. */
  102. deviceDescriptor?: GPUDeviceDescriptor;
  103. /**
  104. * Defines the requested Swap Chain Format.
  105. */
  106. swapChainFormat?: GPUTextureFormat;
  107. /**
  108. * Defines wether MSAA is enabled on the canvas.
  109. */
  110. antialiasing?: boolean;
  111. /**
  112. * Defines wether the stencil buffer should be enabled.
  113. */
  114. stencil?: boolean;
  115. }
  116. /**
  117. * The web GPU engine class provides support for WebGPU version of babylon.js.
  118. */
  119. export class WebGPUEngine extends Engine {
  120. // Default glslang options.
  121. private static readonly _glslangDefaultOptions: GlslangOptions = {
  122. jsPath: "https://preview.babylonjs.com/glslang/glslang.js",
  123. wasmPath: "https://preview.babylonjs.com/glslang/glslang.wasm"
  124. };
  125. // Page Life cycle and constants
  126. private readonly _uploadEncoderDescriptor = { label: "upload" };
  127. private readonly _renderEncoderDescriptor = { label: "render" };
  128. private readonly _renderTargetEncoderDescriptor = { label: "renderTarget" };
  129. private readonly _clearDepthValue = 1;
  130. private readonly _clearReverseDepthValue = 0;
  131. private readonly _clearStencilValue = 0;
  132. private readonly _defaultSampleCount = 4; // Only supported value for now.
  133. // Engine Life Cycle
  134. private _canvas: HTMLCanvasElement;
  135. private _options: WebGPUEngineOptions;
  136. private _glslang: any = null;
  137. private _adapter: GPUAdapter;
  138. private _adapterSupportedExtensions: GPUExtensionName[];
  139. private _device: GPUDevice;
  140. private _deviceEnabledExtensions: GPUExtensionName[];
  141. private _context: GPUCanvasContext;
  142. private _swapChain: GPUSwapChain;
  143. private _swapChainTexture: GPUTexture;
  144. private _mainPassSampleCount: number;
  145. private _textureHelper: WebGPUTextureHelper;
  146. private _bufferManager: WebGPUBufferManager;
  147. private _deferredReleaseTextures: Array<[InternalTexture, Nullable<HardwareTextureWrapper>, Nullable<BaseTexture>, Nullable<InternalTexture>]> = [];
  148. private _counters: {
  149. numPipelineDescriptorCreation: number;
  150. numBindGroupsCreation: number;
  151. } = {
  152. numPipelineDescriptorCreation: 0,
  153. numBindGroupsCreation: 0,
  154. };
  155. // Some of the internal state might change during the render pass.
  156. // This happens mainly during clear for the state
  157. // And when the frame starts to swap the target texture from the swap chain
  158. private _mainTexture: GPUTexture;
  159. private _depthTexture: GPUTexture;
  160. private _mainColorAttachments: GPURenderPassColorAttachmentDescriptor[];
  161. private _mainTextureExtends: GPUExtent3D;
  162. private _mainDepthAttachment: GPURenderPassDepthStencilAttachmentDescriptor;
  163. private _depthTextureFormat: GPUTextureFormat | undefined;
  164. private _colorFormat: GPUTextureFormat;
  165. // Frame Life Cycle (recreated each frame)
  166. private _uploadEncoder: GPUCommandEncoder;
  167. private _renderEncoder: GPUCommandEncoder;
  168. private _renderTargetEncoder: GPUCommandEncoder;
  169. private _commandBuffers: GPUCommandBuffer[] = [null as any, null as any, null as any];
  170. // Frame Buffer Life Cycle (recreated for each render target pass)
  171. private _currentRenderPass: Nullable<GPURenderPassEncoder> = null;
  172. private _mainRenderPass: Nullable<GPURenderPassEncoder> = null;
  173. private _currentRenderTargetViewDescriptor: GPUTextureViewDescriptor;
  174. // DrawCall Life Cycle
  175. // Effect is on the parent class
  176. // protected _currentEffect: Nullable<Effect> = null;
  177. private _currentVertexBuffers: Nullable<{ [key: string]: Nullable<VertexBuffer> }> = null;
  178. private _currentIndexBuffer: Nullable<DataBuffer> = null;
  179. private __colorWrite = true;
  180. private _uniformsBuffers: { [name: string]: WebGPUDataBuffer } = {};
  181. // Caches
  182. private _compiledShaders: { [key: string]: {
  183. stages: IWebGPURenderPipelineStageDescriptor,
  184. availableAttributes: { [key: string]: number },
  185. availableUBOs: { [key: string]: { setIndex: number, bindingIndex: number} },
  186. availableSamplers: { [key: string]: { setIndex: number, bindingIndex: number} },
  187. orderedAttributes: string[],
  188. orderedUBOsAndSamplers: { name: string, isSampler: boolean }[][],
  189. leftOverUniforms: { name: string, type: string, length: number }[],
  190. leftOverUniformsByName: { [name: string]: string },
  191. sources: {
  192. vertex: string,
  193. fragment: string,
  194. rawVertex: string,
  195. rawFragment: string,
  196. }
  197. } } = {};
  198. /**
  199. * Gets a boolean indicating that the engine supports uniform buffers
  200. * @see http://doc.babylonjs.com/features/webgl2#uniform-buffer-objets
  201. */
  202. public get supportsUniformBuffers(): boolean {
  203. return true;
  204. }
  205. /** Gets the supported extensions by the WebGPU adapter */
  206. public get supportedExtensions(): Immutable<GPUExtensionName[]> {
  207. return this._adapterSupportedExtensions;
  208. }
  209. /** Gets the currently enabled extensions on the WebGPU device */
  210. public get enabledExtensions(): Immutable<GPUExtensionName[]> {
  211. return this._deviceEnabledExtensions;
  212. }
  213. /**
  214. * Create a new instance of the gpu engine.
  215. * @param canvas Defines the canvas to use to display the result
  216. * @param options Defines the options passed to the engine to create the GPU context dependencies
  217. */
  218. public constructor(canvas: HTMLCanvasElement, options: WebGPUEngineOptions = {}) {
  219. super(null);
  220. ThinEngine.Features.forceBitmapOverHTMLImageElement = true;
  221. ThinEngine.Features.supportRenderAndCopyToLodForFloatTextures = true;
  222. ThinEngine.Features.framebuffersHaveYTopToBottom = true;
  223. ThinEngine.Features.supportDepthStencilTexture = true;
  224. ThinEngine.Features.supportShadowSamplers = true;
  225. ThinEngine.Features.uniformBufferHardCheckMatrix = true;
  226. ThinEngine.Features.allowTexturePrefiltering = true;
  227. ThinEngine.Features.trackUbosInFrame = true;
  228. ThinEngine.Features._collectUbosUpdatedInFrame = true;
  229. options.deviceDescriptor = options.deviceDescriptor || { };
  230. options.swapChainFormat = options.swapChainFormat || WebGPUConstants.TextureFormat.BGRA8Unorm;
  231. options.antialiasing = false; //options.antialiasing === undefined ? true : options.antialiasing;
  232. options.stencil = options.stencil ?? true;
  233. Logger.Log(`Babylon.js v${Engine.Version} - WebGPU engine`);
  234. if (!navigator.gpu) {
  235. Logger.Error("WebGPU is not supported by your browser.");
  236. return;
  237. }
  238. this._isWebGPU = true;
  239. this._shaderPlatformName = "WEBGPU";
  240. if (options.deterministicLockstep === undefined) {
  241. options.deterministicLockstep = false;
  242. }
  243. if (options.lockstepMaxSteps === undefined) {
  244. options.lockstepMaxSteps = 4;
  245. }
  246. if (options.audioEngine === undefined) {
  247. options.audioEngine = true;
  248. }
  249. this._deterministicLockstep = options.deterministicLockstep;
  250. this._lockstepMaxSteps = options.lockstepMaxSteps;
  251. this._timeStep = options.timeStep || 1 / 60;
  252. this._doNotHandleContextLost = true;
  253. this._canvas = canvas;
  254. this._options = options;
  255. this.premultipliedAlpha = false;
  256. this._hardwareScalingLevel = 1;
  257. this._mainPassSampleCount = options.antialiasing ? this._defaultSampleCount : 1;
  258. this._isStencilEnable = options.stencil;
  259. this._depthCullingState.depthTest = true;
  260. this._depthCullingState.depthFunc = Constants.LEQUAL;
  261. this._depthCullingState.depthMask = true;
  262. this._sharedInit(canvas, !!options.doNotHandleTouchAction, options.audioEngine);
  263. // TODO. WEBGPU. Use real way to do it.
  264. this._canvas.style.transform = "scaleY(-1)";
  265. }
  266. //------------------------------------------------------------------------------
  267. // Initialization
  268. //------------------------------------------------------------------------------
  269. /**
  270. * Initializes the WebGPU context and dependencies.
  271. * @param glslangOptions Defines the GLSLang compiler options if necessary
  272. * @returns a promise notifying the readiness of the engine.
  273. */
  274. public initAsync(glslangOptions?: GlslangOptions): Promise<void> {
  275. return this._initGlslang(glslangOptions)
  276. .then((glslang: any) => {
  277. this._glslang = glslang;
  278. return navigator.gpu!.requestAdapter(this._options);
  279. })
  280. .then((adapter: GPUAdapter | null) => {
  281. this._adapter = adapter!;
  282. this._adapterSupportedExtensions = this._adapter.extensions.slice(0);
  283. const deviceDescriptor = this._options.deviceDescriptor;
  284. if (deviceDescriptor?.extensions) {
  285. const requestedExtensions = deviceDescriptor.extensions;
  286. const validExtensions = [];
  287. const iterator = requestedExtensions[Symbol.iterator]();
  288. while (true) {
  289. const { done, value : extension } = iterator.next();
  290. if (done) {
  291. break;
  292. }
  293. if (this._adapterSupportedExtensions.indexOf(extension) >= 0) {
  294. validExtensions.push(extension);
  295. }
  296. }
  297. deviceDescriptor.extensions = validExtensions;
  298. }
  299. return this._adapter.requestDevice(this._options.deviceDescriptor);
  300. })
  301. .then((device: GPUDevice | null) => {
  302. this._device = device!;
  303. this._deviceEnabledExtensions = this._device.extensions.slice(0);
  304. })
  305. .then(() => {
  306. this._bufferManager = new WebGPUBufferManager(this._device);
  307. this._textureHelper = new WebGPUTextureHelper(this._device, this._glslang, this._bufferManager);
  308. this._uploadEncoder = this._device.createCommandEncoder(this._uploadEncoderDescriptor);
  309. this._renderEncoder = this._device.createCommandEncoder(this._renderEncoderDescriptor);
  310. this._renderTargetEncoder = this._device.createCommandEncoder(this._renderTargetEncoderDescriptor);
  311. this._initializeLimits();
  312. this._initializeContextAndSwapChain();
  313. this._initializeMainAttachments();
  314. this.resize();
  315. })
  316. .catch((e: any) => {
  317. Logger.Error("Can not create WebGPU Device and/or context.");
  318. Logger.Error(e);
  319. });
  320. }
  321. private _initGlslang(glslangOptions?: GlslangOptions): Promise<any> {
  322. glslangOptions = glslangOptions || { };
  323. glslangOptions = {
  324. ...WebGPUEngine._glslangDefaultOptions,
  325. ...glslangOptions
  326. };
  327. if (glslangOptions.glslang) {
  328. return Promise.resolve(glslangOptions.glslang);
  329. }
  330. if ((window as any).glslang) {
  331. return (window as any).glslang(glslangOptions!.wasmPath);
  332. }
  333. if (glslangOptions.jsPath && glslangOptions.wasmPath) {
  334. return Tools.LoadScriptAsync(glslangOptions.jsPath)
  335. .then(() => {
  336. return (window as any).glslang(glslangOptions!.wasmPath);
  337. });
  338. }
  339. return Promise.reject("gslang is not available.");
  340. }
  341. private _initializeLimits(): void {
  342. // Init caps
  343. // TODO WEBGPU Real Capability check once limits will be working.
  344. this._caps = {
  345. maxTexturesImageUnits: 16,
  346. maxVertexTextureImageUnits: 16,
  347. maxCombinedTexturesImageUnits: 32,
  348. maxTextureSize: 2048,
  349. maxCubemapTextureSize: 2048,
  350. maxRenderTextureSize: 2048,
  351. maxVertexAttribs: 16,
  352. maxVaryingVectors: 16,
  353. maxFragmentUniformVectors: 1024,
  354. maxVertexUniformVectors: 1024,
  355. standardDerivatives: true,
  356. astc: null,
  357. s3tc: (this._deviceEnabledExtensions.indexOf(WebGPUConstants.ExtensionName.TextureCompressionBC) >= 0 ? true : undefined) as any,
  358. pvrtc: null,
  359. etc1: null,
  360. etc2: null,
  361. bptc: this._deviceEnabledExtensions.indexOf(WebGPUConstants.ExtensionName.TextureCompressionBC) >= 0 ? true : undefined,
  362. maxAnisotropy: 0, // TODO: Retrieve this smartly. Currently set to D3D11 maximum allowable value.
  363. uintIndices: true,
  364. fragmentDepthSupported: true,
  365. highPrecisionShaderSupported: true,
  366. colorBufferFloat: true,
  367. textureFloat: true,
  368. textureFloatLinearFiltering: true,
  369. textureFloatRender: true,
  370. textureHalfFloat: true,
  371. textureHalfFloatLinearFiltering: true,
  372. textureHalfFloatRender: true,
  373. textureLOD: true,
  374. drawBuffersExtension: true,
  375. depthTextureExtension: true,
  376. vertexArrayObject: false,
  377. instancedArrays: true,
  378. canUseTimestampForTimerQuery: false,
  379. blendMinMax: true,
  380. maxMSAASamples: 8 // TODO WEBGPU what is the right value?
  381. };
  382. this._caps.parallelShaderCompile = null as any;
  383. }
  384. private _initializeContextAndSwapChain(): void {
  385. this._context = this._canvas.getContext('gpupresent') as unknown as GPUCanvasContext;
  386. this._swapChain = this._context.configureSwapChain({
  387. device: this._device,
  388. format: this._options.swapChainFormat!,
  389. usage: WebGPUConstants.TextureUsage.OutputAttachment | WebGPUConstants.TextureUsage.CopySrc,
  390. });
  391. this._colorFormat = this._options.swapChainFormat!;
  392. if (dbgGenerateLogs) {
  393. this._context.getSwapChainPreferredFormat(this._device).then((format) => {
  394. console.log("Swap chain preferred format:", format);
  395. });
  396. }
  397. }
  398. // Set default values as WebGL with depth and stencil attachment for the broadest Compat.
  399. private _initializeMainAttachments(): void {
  400. this._mainTextureExtends = {
  401. width: this.getRenderWidth(),
  402. height: this.getRenderHeight(),
  403. depth: 1
  404. };
  405. if (this._options.antialiasing) {
  406. const mainTextureDescriptor: GPUTextureDescriptor = {
  407. size: this._mainTextureExtends,
  408. mipLevelCount: 1,
  409. sampleCount: this._mainPassSampleCount,
  410. dimension: WebGPUConstants.TextureDimension.E2d,
  411. format: WebGPUConstants.TextureFormat.BGRA8Unorm,
  412. usage: WebGPUConstants.TextureUsage.OutputAttachment,
  413. };
  414. if (this._mainTexture) {
  415. this._mainTexture.destroy();
  416. }
  417. this._mainTexture = this._device.createTexture(mainTextureDescriptor);
  418. this._mainColorAttachments = [{
  419. attachment: this._mainTexture.createView(),
  420. loadValue: new Color4(0, 0, 0, 1),
  421. storeOp: WebGPUConstants.StoreOp.Clear // Better than "Store" as we don't need to reuse the content of the multisampled texture
  422. }];
  423. }
  424. else {
  425. this._mainColorAttachments = [{
  426. attachment: undefined as any,
  427. loadValue: new Color4(0, 0, 0, 1),
  428. storeOp: WebGPUConstants.StoreOp.Store
  429. }];
  430. }
  431. this._depthTextureFormat = this._getMainDepthTextureFormat();
  432. const depthTextureDescriptor: GPUTextureDescriptor = {
  433. size: this._mainTextureExtends,
  434. mipLevelCount: 1,
  435. sampleCount: this._mainPassSampleCount,
  436. dimension: WebGPUConstants.TextureDimension.E2d,
  437. format: this._depthTextureFormat,
  438. usage: WebGPUConstants.TextureUsage.OutputAttachment
  439. };
  440. if (this._depthTexture) {
  441. this._depthTexture.destroy();
  442. }
  443. this._depthTexture = this._device.createTexture(depthTextureDescriptor);
  444. this._mainDepthAttachment = {
  445. attachment: this._depthTexture.createView(),
  446. depthLoadValue: this._clearDepthValue,
  447. depthStoreOp: WebGPUConstants.StoreOp.Store,
  448. stencilLoadValue: this._clearStencilValue,
  449. stencilStoreOp: WebGPUConstants.StoreOp.Store,
  450. };
  451. if (this._mainRenderPass !== null) {
  452. this._endMainRenderPass();
  453. }
  454. }
  455. /**
  456. * Gets a shader processor implementation fitting with the current engine type.
  457. * @returns The shader processor implementation.
  458. */
  459. protected _getShaderProcessor(): Nullable<IShaderProcessor> {
  460. return new WebGPUShaderProcessor();
  461. }
  462. /** @hidden */
  463. public _getShaderProcessingContext(): Nullable<ShaderProcessingContext> {
  464. return new WebGPUShaderProcessingContext();
  465. }
  466. //------------------------------------------------------------------------------
  467. // Static Pipeline WebGPU States
  468. //------------------------------------------------------------------------------
  469. public wipeCaches(bruteForce?: boolean): void {
  470. if (this.preventCacheWipeBetweenFrames && !bruteForce) {
  471. return;
  472. }
  473. this.resetTextureCache();
  474. //this._currentEffect = null; // can't reset _currentEffect, else some crashes can occur (for eg in ProceduralTexture which calls bindFrameBuffer (which calls wipeCaches) after having called enableEffect and before drawing into the texture)
  475. this._resetCachedViewport();
  476. this._currentIndexBuffer = null;
  477. this._currentVertexBuffers = null;
  478. if (bruteForce) {
  479. this._currentProgram = null;
  480. this._stencilState.reset();
  481. this._depthCullingState.reset();
  482. this._depthCullingState.depthFunc = Constants.LEQUAL;
  483. this._alphaState.reset();
  484. this._alphaMode = Constants.ALPHA_ADD;
  485. this._alphaEquation = Constants.ALPHA_DISABLE;
  486. this.__colorWrite = true;
  487. }
  488. this._cachedVertexBuffers = null;
  489. this._cachedIndexBuffer = null;
  490. this._cachedEffectForVertexBuffers = null;
  491. }
  492. public setColorWrite(enable: boolean): void {
  493. this.__colorWrite = enable;
  494. }
  495. public getColorWrite(): boolean {
  496. return this.__colorWrite;
  497. }
  498. //------------------------------------------------------------------------------
  499. // Dynamic WebGPU States
  500. //------------------------------------------------------------------------------
  501. private _resetCachedViewport() {
  502. this._viewportCached.x = 0;
  503. this._viewportCached.y = 0;
  504. this._viewportCached.z = 0;
  505. this._viewportCached.w = 0;
  506. }
  507. private _transientViewport: { x: number, y: number, z: number, w: number } = { x: Infinity, y: 0, z: 0, w: 0 };
  508. /** @hidden */
  509. public _viewport(x: number, y: number, width: number, height: number): void {
  510. if (!this._currentRenderPass) {
  511. // we want to set the viewport for the main pass or for a render target but the render pass has not been created yet: let's postpone the update of the viewport when the pass is created
  512. this._transientViewport.x = x;
  513. this._transientViewport.y = y;
  514. this._transientViewport.z = width;
  515. this._transientViewport.w = height;
  516. if (dbgVerboseLogsForFirstFrames) {
  517. if (!(this as any)._count || (this as any)._count < dbgVerboseLogsNumFrames) {
  518. console.log("_viewport called but _currentRenderPass is null frame #" + (this as any)._count, x, y, width, height);
  519. }
  520. }
  521. } else if (this._transientViewport.x !== Infinity) {
  522. const renderPass = this._getCurrentRenderPass();
  523. renderPass.setViewport(this._transientViewport.x, this._transientViewport.y, this._transientViewport.z, this._transientViewport.w, 0, 1);
  524. if (dbgVerboseLogsForFirstFrames) {
  525. if (!(this as any)._count || (this as any)._count < dbgVerboseLogsNumFrames) {
  526. console.log("_viewport called and transient viewport set frame #" + (this as any)._count, this._transientViewport.x, this._transientViewport.y, this._transientViewport.z, this._transientViewport.w, "current pass is main pass=" + (renderPass === this._mainRenderPass));
  527. }
  528. }
  529. this._transientViewport.x = Infinity;
  530. } else if (x !== this._viewportCached.x || y !== this._viewportCached.y || width !== this._viewportCached.z || height !== this._viewportCached.w) {
  531. this._viewportCached.x = x;
  532. this._viewportCached.y = y;
  533. this._viewportCached.z = width;
  534. this._viewportCached.w = height;
  535. const renderPass = this._getCurrentRenderPass();
  536. renderPass.setViewport(x, y, width, height, 0, 1);
  537. if (dbgVerboseLogsForFirstFrames) {
  538. if (!(this as any)._count || (this as any)._count < dbgVerboseLogsNumFrames) {
  539. console.log("_viewport called and viewport set frame #" + (this as any)._count, x, y, width, height, "current pass is main pass=" + (renderPass === this._mainRenderPass));
  540. }
  541. }
  542. } else {
  543. if (dbgVerboseLogsForFirstFrames) {
  544. if (!(this as any)._count || (this as any)._count < dbgVerboseLogsNumFrames) {
  545. console.log("_viewport called and viewport NOT set because cached frame #" + (this as any)._count, x, y, width, height, "current pass is main pass=" + (this._currentRenderPass === this._mainRenderPass));
  546. }
  547. }
  548. }
  549. }
  550. public enableScissor(x: number, y: number, width: number, height: number): void {
  551. const renderPass = this._getCurrentRenderPass();
  552. renderPass.setScissorRect(x, y, width, height);
  553. }
  554. public disableScissor() {
  555. const renderPass = this._getCurrentRenderPass();
  556. renderPass.setScissorRect(0, 0, this.getRenderWidth(), this.getRenderHeight());
  557. }
  558. public clear(color: Nullable<IColor4Like>, backBuffer: boolean, depth: boolean, stencil: boolean = false): void {
  559. // Some PGs are using color3...
  560. if (color && color.a === undefined) {
  561. color.a = 1;
  562. }
  563. // We need to recreate the render pass so that the new parameters for clear color / depth / stencil are taken into account
  564. if (this._currentRenderTarget) {
  565. if (this._currentRenderPass) {
  566. this._endRenderTargetRenderPass();
  567. }
  568. this._startRenderTargetRenderPass(this._currentRenderTarget!, backBuffer ? color : null, depth, stencil);
  569. } else {
  570. if (this.useReverseDepthBuffer) {
  571. this._depthCullingState.depthFunc = Constants.GREATER;
  572. }
  573. this._mainColorAttachments[0].loadValue = backBuffer && color ? color : WebGPUConstants.LoadOp.Load;
  574. this._mainDepthAttachment.depthLoadValue = depth ? (this.useReverseDepthBuffer ? this._clearReverseDepthValue : this._clearDepthValue) : WebGPUConstants.LoadOp.Load;
  575. this._mainDepthAttachment.stencilLoadValue = stencil ? this._clearStencilValue : WebGPUConstants.LoadOp.Load;
  576. if (this._mainRenderPass) {
  577. this._endMainRenderPass();
  578. }
  579. this._startMainRenderPass();
  580. }
  581. }
  582. //------------------------------------------------------------------------------
  583. // Vertex/Index Buffers
  584. //------------------------------------------------------------------------------
  585. public createVertexBuffer(data: DataArray): DataBuffer {
  586. let view: ArrayBufferView;
  587. if (data instanceof Array) {
  588. view = new Float32Array(data);
  589. }
  590. else if (data instanceof ArrayBuffer) {
  591. view = new Uint8Array(data);
  592. }
  593. else {
  594. view = data;
  595. }
  596. const dataBuffer = this._bufferManager.createBuffer(view, WebGPUConstants.BufferUsage.Vertex | WebGPUConstants.BufferUsage.CopyDst);
  597. return dataBuffer;
  598. }
  599. public createDynamicVertexBuffer(data: DataArray): DataBuffer {
  600. return this.createVertexBuffer(data);
  601. }
  602. public updateDynamicVertexBuffer(vertexBuffer: DataBuffer, data: DataArray, byteOffset?: number, byteLength?: number): void {
  603. const dataBuffer = vertexBuffer as WebGPUDataBuffer;
  604. if (byteOffset === undefined) {
  605. byteOffset = 0;
  606. }
  607. let view: ArrayBufferView;
  608. if (byteLength === undefined) {
  609. if (data instanceof Array) {
  610. view = new Float32Array(data);
  611. }
  612. else if (data instanceof ArrayBuffer) {
  613. view = new Uint8Array(data);
  614. }
  615. else {
  616. view = data;
  617. }
  618. byteLength = view.byteLength;
  619. } else {
  620. if (data instanceof Array) {
  621. view = new Float32Array(data);
  622. }
  623. else if (data instanceof ArrayBuffer) {
  624. view = new Uint8Array(data);
  625. }
  626. else {
  627. view = data;
  628. }
  629. }
  630. this._bufferManager.setSubData(dataBuffer, byteOffset, view, 0, byteLength);
  631. }
  632. public createIndexBuffer(data: IndicesArray): DataBuffer {
  633. let is32Bits = true;
  634. let view: ArrayBufferView;
  635. if (data instanceof Uint32Array || data instanceof Int32Array) {
  636. view = data;
  637. }
  638. else if (data instanceof Uint16Array) {
  639. view = data;
  640. is32Bits = false;
  641. }
  642. else {
  643. if (data.length > 65535) {
  644. view = new Uint32Array(data);
  645. }
  646. else {
  647. view = new Uint16Array(data);
  648. is32Bits = false;
  649. }
  650. }
  651. const dataBuffer = this._bufferManager.createBuffer(view, WebGPUConstants.BufferUsage.Index | WebGPUConstants.BufferUsage.CopyDst);
  652. dataBuffer.is32Bits = is32Bits;
  653. return dataBuffer;
  654. }
  655. public updateDynamicIndexBuffer(indexBuffer: DataBuffer, indices: IndicesArray, offset: number = 0): void {
  656. const gpuBuffer = indexBuffer as WebGPUDataBuffer;
  657. var view: ArrayBufferView;
  658. if (indices instanceof Uint16Array) {
  659. if (indexBuffer.is32Bits) {
  660. view = Uint32Array.from(indices);
  661. }
  662. else {
  663. view = indices;
  664. }
  665. }
  666. else if (indices instanceof Uint32Array) {
  667. if (indexBuffer.is32Bits) {
  668. view = indices;
  669. }
  670. else {
  671. view = Uint16Array.from(indices);
  672. }
  673. }
  674. else {
  675. if (indexBuffer.is32Bits) {
  676. view = new Uint32Array(indices);
  677. }
  678. else {
  679. view = new Uint16Array(indices);
  680. }
  681. }
  682. this._bufferManager.setSubData(gpuBuffer, offset, view);
  683. }
  684. public bindBuffersDirectly(vertexBuffer: DataBuffer, indexBuffer: DataBuffer, vertexDeclaration: number[], vertexStrideSize: number, effect: Effect): void {
  685. throw "Not implemented on WebGPU so far.";
  686. }
  687. public updateAndBindInstancesBuffer(instancesBuffer: DataBuffer, data: Float32Array, offsetLocations: number[] | InstancingAttributeInfo[]): void {
  688. throw "Not implemented on WebGPU so far.";
  689. }
  690. public bindBuffers(vertexBuffers: { [key: string]: Nullable<VertexBuffer> }, indexBuffer: Nullable<DataBuffer>, effect: Effect): void {
  691. this._currentIndexBuffer = indexBuffer;
  692. this._currentVertexBuffers = vertexBuffers;
  693. }
  694. /** @hidden */
  695. public _releaseBuffer(buffer: DataBuffer): boolean {
  696. return this._bufferManager.releaseBuffer(buffer);
  697. }
  698. //------------------------------------------------------------------------------
  699. // UBO
  700. //------------------------------------------------------------------------------
  701. public createUniformBuffer(elements: FloatArray): DataBuffer {
  702. let view: Float32Array;
  703. if (elements instanceof Array) {
  704. view = new Float32Array(elements);
  705. }
  706. else {
  707. view = elements;
  708. }
  709. const dataBuffer = this._bufferManager.createBuffer(view, WebGPUConstants.BufferUsage.Uniform | WebGPUConstants.BufferUsage.CopyDst);
  710. return dataBuffer;
  711. }
  712. public createDynamicUniformBuffer(elements: FloatArray): DataBuffer {
  713. return this.createUniformBuffer(elements);
  714. }
  715. public updateUniformBuffer(uniformBuffer: DataBuffer, elements: FloatArray, offset?: number, count?: number): void {
  716. if (offset === undefined) {
  717. offset = 0;
  718. }
  719. const dataBuffer = uniformBuffer as WebGPUDataBuffer;
  720. let view: Float32Array;
  721. if (count === undefined) {
  722. if (elements instanceof Float32Array) {
  723. view = elements;
  724. } else {
  725. view = new Float32Array(elements);
  726. }
  727. count = view.byteLength;
  728. } else {
  729. if (elements instanceof Float32Array) {
  730. view = elements;
  731. } else {
  732. view = new Float32Array(elements);
  733. }
  734. }
  735. this._bufferManager.setSubData(dataBuffer, offset, view, 0, count);
  736. }
  737. public bindUniformBufferBase(buffer: DataBuffer, location: number, name: string): void {
  738. this._uniformsBuffers[name] = buffer as WebGPUDataBuffer;
  739. }
  740. //------------------------------------------------------------------------------
  741. // Effects
  742. //------------------------------------------------------------------------------
  743. public createEffect(baseName: any, attributesNamesOrOptions: string[] | IEffectCreationOptions, uniformsNamesOrEngine: string[] | Engine, samplers?: string[], defines?: string, fallbacks?: EffectFallbacks,
  744. onCompiled?: Nullable<(effect: Effect) => void>, onError?: Nullable<(effect: Effect, errors: string) => void>, indexParameters?: any): Effect {
  745. const vertex = baseName.vertexElement || baseName.vertex || baseName.vertexToken || baseName.vertexSource || baseName;
  746. const fragment = baseName.fragmentElement || baseName.fragment || baseName.fragmentToken || baseName.fragmentSource || baseName;
  747. const name = vertex + "+" + fragment + "@" + (defines ? defines : (<IEffectCreationOptions>attributesNamesOrOptions).defines);
  748. /*const shader = this._compiledShaders[name];
  749. if (shader) {
  750. return new Effect(baseName, attributesNamesOrOptions, uniformsNamesOrEngine, samplers, this, defines, fallbacks, onCompiled, onError, indexParameters, name, shader.sources);
  751. }
  752. else {
  753. return new Effect(baseName, attributesNamesOrOptions, uniformsNamesOrEngine, samplers, this, defines, fallbacks, onCompiled, onError, indexParameters, name);
  754. }*/
  755. if (this._compiledEffects[name]) {
  756. var compiledEffect = <Effect>this._compiledEffects[name];
  757. if (onCompiled && compiledEffect.isReady()) {
  758. onCompiled(compiledEffect);
  759. }
  760. return compiledEffect;
  761. }
  762. var effect = new Effect(baseName, attributesNamesOrOptions, uniformsNamesOrEngine, samplers, this, defines, fallbacks, onCompiled, onError, indexParameters, name);
  763. this._compiledEffects[name] = effect;
  764. return effect;
  765. }
  766. private _compileRawShaderToSpirV(source: string, type: string): Uint32Array {
  767. return this._glslang.compileGLSL(source, type);
  768. }
  769. private _compileShaderToSpirV(source: string, type: string, defines: Nullable<string>, shaderVersion: string): Uint32Array {
  770. return this._compileRawShaderToSpirV(shaderVersion + (defines ? defines + "\n" : "") + source, type);
  771. }
  772. private _createPipelineStageDescriptor(vertexShader: Uint32Array, fragmentShader: Uint32Array): IWebGPURenderPipelineStageDescriptor {
  773. return {
  774. vertexStage: {
  775. module: this._device.createShaderModule({
  776. code: vertexShader,
  777. }),
  778. entryPoint: "main",
  779. },
  780. fragmentStage: {
  781. module: this._device.createShaderModule({
  782. code: fragmentShader,
  783. }),
  784. entryPoint: "main"
  785. }
  786. };
  787. }
  788. private _compileRawPipelineStageDescriptor(vertexCode: string, fragmentCode: string): IWebGPURenderPipelineStageDescriptor {
  789. var vertexShader = this._compileRawShaderToSpirV(vertexCode, "vertex");
  790. var fragmentShader = this._compileRawShaderToSpirV(fragmentCode, "fragment");
  791. return this._createPipelineStageDescriptor(vertexShader, fragmentShader);
  792. }
  793. private _compilePipelineStageDescriptor(vertexCode: string, fragmentCode: string, defines: Nullable<string>): IWebGPURenderPipelineStageDescriptor {
  794. this.onBeforeShaderCompilationObservable.notifyObservers(this);
  795. var shaderVersion = "#version 450\n";
  796. var vertexShader = this._compileShaderToSpirV(vertexCode, "vertex", defines, shaderVersion);
  797. var fragmentShader = this._compileShaderToSpirV(fragmentCode, "fragment", defines, shaderVersion);
  798. let program = this._createPipelineStageDescriptor(vertexShader, fragmentShader);
  799. this.onAfterShaderCompilationObservable.notifyObservers(this);
  800. return program;
  801. }
  802. public createRawShaderProgram(pipelineContext: IPipelineContext, vertexCode: string, fragmentCode: string, context?: WebGLRenderingContext, transformFeedbackVaryings: Nullable<string[]> = null): WebGLProgram {
  803. throw "Not available on WebGPU";
  804. }
  805. public createShaderProgram(pipelineContext: IPipelineContext, vertexCode: string, fragmentCode: string, defines: Nullable<string>, context?: WebGLRenderingContext, transformFeedbackVaryings: Nullable<string[]> = null): WebGLProgram {
  806. throw "Not available on WebGPU";
  807. }
  808. public createPipelineContext(shaderProcessingContext: Nullable<ShaderProcessingContext>): IPipelineContext {
  809. var pipelineContext = new WebGPUPipelineContext(shaderProcessingContext! as WebGPUShaderProcessingContext, this);
  810. pipelineContext.engine = this;
  811. return pipelineContext;
  812. }
  813. /** @hidden */
  814. public _preparePipelineContext(pipelineContext: IPipelineContext, vertexSourceCode: string, fragmentSourceCode: string, createAsRaw: boolean, rawVertexSourceCode: string, rawFragmentSourceCode: string,
  815. rebuildRebind: any,
  816. defines: Nullable<string>,
  817. transformFeedbackVaryings: Nullable<string[]>,
  818. key: string) {
  819. const webGpuContext = pipelineContext as WebGPUPipelineContext;
  820. // TODO WEBGPU. Check if caches could be reuse from piepline ???
  821. const shader = this._compiledShaders[key];
  822. if (shader) {
  823. webGpuContext.stages = shader.stages;
  824. webGpuContext.availableAttributes = shader.availableAttributes;
  825. webGpuContext.availableUBOs = shader.availableUBOs;
  826. webGpuContext.availableSamplers = shader.availableSamplers;
  827. webGpuContext.orderedAttributes = shader.orderedAttributes;
  828. webGpuContext.orderedUBOsAndSamplers = shader.orderedUBOsAndSamplers;
  829. webGpuContext.leftOverUniforms = shader.leftOverUniforms;
  830. webGpuContext.leftOverUniformsByName = shader.leftOverUniformsByName;
  831. webGpuContext.sources = shader.sources;
  832. }
  833. else {
  834. if (dbgShowShaderCode) {
  835. console.log(defines);
  836. console.log(vertexSourceCode);
  837. console.log(fragmentSourceCode);
  838. }
  839. webGpuContext.sources = {
  840. fragment: fragmentSourceCode,
  841. vertex: vertexSourceCode,
  842. rawVertex: rawVertexSourceCode,
  843. rawFragment: rawFragmentSourceCode,
  844. };
  845. if (createAsRaw) {
  846. webGpuContext.stages = this._compileRawPipelineStageDescriptor(vertexSourceCode, fragmentSourceCode);
  847. }
  848. else {
  849. webGpuContext.stages = this._compilePipelineStageDescriptor(vertexSourceCode, fragmentSourceCode, defines);
  850. }
  851. this._compiledShaders[key] = {
  852. stages: webGpuContext.stages,
  853. availableAttributes: webGpuContext.availableAttributes,
  854. availableUBOs: webGpuContext.availableUBOs,
  855. availableSamplers: webGpuContext.availableSamplers,
  856. orderedAttributes: webGpuContext.orderedAttributes,
  857. orderedUBOsAndSamplers: webGpuContext.orderedUBOsAndSamplers,
  858. leftOverUniforms: webGpuContext.leftOverUniforms,
  859. leftOverUniformsByName: webGpuContext.leftOverUniformsByName,
  860. sources: webGpuContext.sources
  861. };
  862. }
  863. }
  864. public getAttributes(pipelineContext: IPipelineContext, attributesNames: string[]): number[] {
  865. const results = new Array(attributesNames.length);
  866. const gpuPipelineContext = (pipelineContext as WebGPUPipelineContext);
  867. // TODO WEBGPU. Hard coded for WebGPU until an introspection lib is available.
  868. // Should be done at processing time, not need to double the work in here.
  869. for (let i = 0; i < attributesNames.length; i++) {
  870. const attributeName = attributesNames[i];
  871. const attributeLocation = gpuPipelineContext.availableAttributes[attributeName];
  872. if (attributeLocation === undefined) {
  873. continue;
  874. }
  875. results[i] = attributeLocation;
  876. }
  877. return results;
  878. }
  879. public enableEffect(effect: Nullable<Effect>): void {
  880. if (!effect || effect === this._currentEffect) {
  881. return;
  882. }
  883. this._currentEffect = effect;
  884. if (effect.onBind) {
  885. effect.onBind(effect);
  886. }
  887. if (effect._onBindObservable) {
  888. effect._onBindObservable.notifyObservers(effect);
  889. }
  890. }
  891. public _releaseEffect(effect: Effect): void {
  892. // Effect gets garbage collected without explicit destroy in WebGPU.
  893. }
  894. /**
  895. * 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
  896. */
  897. public releaseEffects() {
  898. // Effect gets garbage collected without explicit destroy in WebGPU.
  899. }
  900. public _deletePipelineContext(pipelineContext: IPipelineContext): void {
  901. const webgpuPipelineContext = pipelineContext as WebGPUPipelineContext;
  902. if (webgpuPipelineContext) {
  903. pipelineContext.dispose();
  904. }
  905. }
  906. //------------------------------------------------------------------------------
  907. // Textures
  908. //------------------------------------------------------------------------------
  909. private _getMainDepthTextureFormat(): GPUTextureFormat {
  910. return this.isStencilEnable ? WebGPUConstants.TextureFormat.Depth24PlusStencil8 : WebGPUConstants.TextureFormat.Depth32Float;
  911. }
  912. /** @hidden */
  913. public _createHardwareTexture(): HardwareTextureWrapper {
  914. return new WebGPUHardwareTexture();
  915. }
  916. /** @hidden */
  917. public _releaseTexture(texture: InternalTexture): void {
  918. const hardwareTexture = texture._hardwareTexture;
  919. const irradianceTexture = texture._irradianceTexture;
  920. const depthStencilTexture = texture._depthStencilTexture;
  921. const index = this._internalTexturesCache.indexOf(texture);
  922. if (index !== -1) {
  923. this._internalTexturesCache.splice(index, 1);
  924. }
  925. // We can't destroy the objects just now because they could be used in the current frame - we delay the destroying after the end of the frame
  926. this._deferredReleaseTextures.push([texture, hardwareTexture, irradianceTexture, depthStencilTexture]);
  927. }
  928. private _getSamplerFilterDescriptor(internalTexture: InternalTexture): {
  929. magFilter: GPUFilterMode,
  930. minFilter: GPUFilterMode,
  931. mipmapFilter: GPUFilterMode
  932. } {
  933. let magFilter: GPUFilterMode, minFilter: GPUFilterMode, mipmapFilter: GPUFilterMode;
  934. switch (internalTexture.samplingMode) {
  935. case Engine.TEXTURE_BILINEAR_SAMPLINGMODE:
  936. magFilter = WebGPUConstants.FilterMode.Linear;
  937. minFilter = WebGPUConstants.FilterMode.Linear;
  938. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  939. break;
  940. case Engine.TEXTURE_TRILINEAR_SAMPLINGMODE:
  941. magFilter = WebGPUConstants.FilterMode.Linear;
  942. minFilter = WebGPUConstants.FilterMode.Linear;
  943. mipmapFilter = WebGPUConstants.FilterMode.Linear;
  944. break;
  945. case Engine.TEXTURE_NEAREST_SAMPLINGMODE:
  946. magFilter = WebGPUConstants.FilterMode.Nearest;
  947. minFilter = WebGPUConstants.FilterMode.Nearest;
  948. mipmapFilter = WebGPUConstants.FilterMode.Linear;
  949. break;
  950. case Engine.TEXTURE_NEAREST_NEAREST_MIPNEAREST:
  951. magFilter = WebGPUConstants.FilterMode.Nearest;
  952. minFilter = WebGPUConstants.FilterMode.Nearest;
  953. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  954. break;
  955. case Engine.TEXTURE_NEAREST_LINEAR_MIPNEAREST:
  956. magFilter = WebGPUConstants.FilterMode.Nearest;
  957. minFilter = WebGPUConstants.FilterMode.Linear;
  958. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  959. break;
  960. case Engine.TEXTURE_NEAREST_LINEAR_MIPLINEAR:
  961. magFilter = WebGPUConstants.FilterMode.Nearest;
  962. minFilter = WebGPUConstants.FilterMode.Linear;
  963. mipmapFilter = WebGPUConstants.FilterMode.Linear;
  964. break;
  965. case Engine.TEXTURE_NEAREST_LINEAR:
  966. magFilter = WebGPUConstants.FilterMode.Nearest;
  967. minFilter = WebGPUConstants.FilterMode.Linear;
  968. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  969. break;
  970. case Engine.TEXTURE_NEAREST_NEAREST:
  971. magFilter = WebGPUConstants.FilterMode.Nearest;
  972. minFilter = WebGPUConstants.FilterMode.Nearest;
  973. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  974. break;
  975. case Engine.TEXTURE_LINEAR_NEAREST_MIPNEAREST:
  976. magFilter = WebGPUConstants.FilterMode.Linear;
  977. minFilter = WebGPUConstants.FilterMode.Nearest;
  978. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  979. break;
  980. case Engine.TEXTURE_LINEAR_NEAREST_MIPLINEAR:
  981. magFilter = WebGPUConstants.FilterMode.Linear;
  982. minFilter = WebGPUConstants.FilterMode.Nearest;
  983. mipmapFilter = WebGPUConstants.FilterMode.Linear;
  984. break;
  985. case Engine.TEXTURE_LINEAR_LINEAR:
  986. magFilter = WebGPUConstants.FilterMode.Linear;
  987. minFilter = WebGPUConstants.FilterMode.Linear;
  988. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  989. break;
  990. case Engine.TEXTURE_LINEAR_NEAREST:
  991. magFilter = WebGPUConstants.FilterMode.Linear;
  992. minFilter = WebGPUConstants.FilterMode.Nearest;
  993. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  994. break;
  995. default:
  996. magFilter = WebGPUConstants.FilterMode.Linear;
  997. minFilter = WebGPUConstants.FilterMode.Linear;
  998. mipmapFilter = WebGPUConstants.FilterMode.Linear;
  999. break;
  1000. }
  1001. return {
  1002. magFilter,
  1003. minFilter,
  1004. mipmapFilter
  1005. };
  1006. }
  1007. /** @hidden */
  1008. public _getWebGPUInternalFormat(format: number): GPUTextureFormat {
  1009. let internalFormat = WebGPUConstants.TextureFormat.RGBA8Unorm;
  1010. switch (format) {
  1011. case Constants.TEXTUREFORMAT_ALPHA:
  1012. throw "TEXTUREFORMAT_ALPHA format not supported in WebGPU";
  1013. case Constants.TEXTUREFORMAT_LUMINANCE:
  1014. throw "TEXTUREFORMAT_LUMINANCE format not supported in WebGPU";
  1015. case Constants.TEXTUREFORMAT_LUMINANCE_ALPHA:
  1016. throw "TEXTUREFORMAT_LUMINANCE_ALPHA format not supported in WebGPU";
  1017. case Constants.TEXTUREFORMAT_RED:
  1018. internalFormat = WebGPUConstants.TextureFormat.R8Snorm;
  1019. case Constants.TEXTUREFORMAT_RG:
  1020. internalFormat = WebGPUConstants.TextureFormat.RG8Snorm;
  1021. case Constants.TEXTUREFORMAT_RGB:
  1022. throw "RGB format not supported in WebGPU";
  1023. case Constants.TEXTUREFORMAT_RGBA:
  1024. internalFormat = WebGPUConstants.TextureFormat.RGBA8Unorm;
  1025. }
  1026. return internalFormat;
  1027. }
  1028. /** @hidden */
  1029. public _getRGBABufferInternalSizedFormat(type: number, format?: number): number {
  1030. return Constants.TEXTUREFORMAT_RGBA;
  1031. }
  1032. private _getWebGPUTextureFormat(type: number, format: number): GPUTextureFormat {
  1033. switch (format) {
  1034. case Constants.TEXTUREFORMAT_DEPTH24_STENCIL8:
  1035. return WebGPUConstants.TextureFormat.Depth24PlusStencil8;
  1036. case Constants.TEXTUREFORMAT_DEPTH32_FLOAT:
  1037. return WebGPUConstants.TextureFormat.Depth32Float;
  1038. case Constants.TEXTUREFORMAT_COMPRESSED_RGBA_BPTC_UNORM:
  1039. return WebGPUConstants.TextureFormat.BC7RGBAUnorm;
  1040. case Constants.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
  1041. return WebGPUConstants.TextureFormat.BC6HRGBUFloat;
  1042. case Constants.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:
  1043. return WebGPUConstants.TextureFormat.BC6HRGBSFloat;
  1044. case Constants.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT5:
  1045. return WebGPUConstants.TextureFormat.BC3RGBAUnorm;
  1046. case Constants.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT3:
  1047. return WebGPUConstants.TextureFormat.BC2RGBAUnorm;
  1048. case Constants.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT1:
  1049. return WebGPUConstants.TextureFormat.BC1RGBAUNorm;
  1050. }
  1051. switch (type) {
  1052. case Constants.TEXTURETYPE_BYTE:
  1053. switch (format) {
  1054. case Constants.TEXTUREFORMAT_RED:
  1055. return WebGPUConstants.TextureFormat.R8Snorm;
  1056. case Constants.TEXTUREFORMAT_RG:
  1057. return WebGPUConstants.TextureFormat.RG8Snorm;
  1058. case Constants.TEXTUREFORMAT_RGB:
  1059. throw "RGB format not supported in WebGPU";
  1060. case Constants.TEXTUREFORMAT_RED_INTEGER:
  1061. return WebGPUConstants.TextureFormat.R8Sint;
  1062. case Constants.TEXTUREFORMAT_RG_INTEGER:
  1063. return WebGPUConstants.TextureFormat.RG8Sint;
  1064. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  1065. throw "RGB_INTEGER format not supported in WebGPU";
  1066. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  1067. return WebGPUConstants.TextureFormat.RGBA8Sint;
  1068. default:
  1069. return WebGPUConstants.TextureFormat.RGBA8Snorm;
  1070. }
  1071. case Constants.TEXTURETYPE_UNSIGNED_BYTE:
  1072. switch (format) {
  1073. case Constants.TEXTUREFORMAT_RED:
  1074. return WebGPUConstants.TextureFormat.R8Unorm;
  1075. case Constants.TEXTUREFORMAT_RG:
  1076. return WebGPUConstants.TextureFormat.RG8Unorm;
  1077. case Constants.TEXTUREFORMAT_RGB:
  1078. throw "TEXTUREFORMAT_RGB format not supported in WebGPU";
  1079. case Constants.TEXTUREFORMAT_RGBA:
  1080. return WebGPUConstants.TextureFormat.RGBA8Unorm;
  1081. case Constants.TEXTUREFORMAT_BGRA:
  1082. return WebGPUConstants.TextureFormat.BGRA8Unorm;
  1083. case Constants.TEXTUREFORMAT_RED_INTEGER:
  1084. return WebGPUConstants.TextureFormat.R8Uint;
  1085. case Constants.TEXTUREFORMAT_RG_INTEGER:
  1086. return WebGPUConstants.TextureFormat.RG8Uint;
  1087. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  1088. throw "RGB_INTEGER format not supported in WebGPU";
  1089. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  1090. return WebGPUConstants.TextureFormat.RGBA8Uint;
  1091. case Constants.TEXTUREFORMAT_ALPHA:
  1092. throw "TEXTUREFORMAT_ALPHA format not supported in WebGPU";
  1093. case Constants.TEXTUREFORMAT_LUMINANCE:
  1094. throw "TEXTUREFORMAT_LUMINANCE format not supported in WebGPU";
  1095. case Constants.TEXTUREFORMAT_LUMINANCE_ALPHA:
  1096. throw "TEXTUREFORMAT_LUMINANCE_ALPHA format not supported in WebGPU";
  1097. default:
  1098. return WebGPUConstants.TextureFormat.RGBA8Unorm;
  1099. }
  1100. case Constants.TEXTURETYPE_SHORT:
  1101. switch (format) {
  1102. case Constants.TEXTUREFORMAT_RED_INTEGER:
  1103. return WebGPUConstants.TextureFormat.R16Sint;
  1104. case Constants.TEXTUREFORMAT_RG_INTEGER:
  1105. return WebGPUConstants.TextureFormat.RG16Sint;
  1106. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  1107. throw "TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";
  1108. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  1109. return WebGPUConstants.TextureFormat.RGBA16Sint;
  1110. default:
  1111. return WebGPUConstants.TextureFormat.RGBA16Sint;
  1112. }
  1113. case Constants.TEXTURETYPE_UNSIGNED_SHORT:
  1114. switch (format) {
  1115. case Constants.TEXTUREFORMAT_RED_INTEGER:
  1116. return WebGPUConstants.TextureFormat.R16Uint;
  1117. case Constants.TEXTUREFORMAT_RG_INTEGER:
  1118. return WebGPUConstants.TextureFormat.RG16Uint;
  1119. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  1120. throw "TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";
  1121. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  1122. return WebGPUConstants.TextureFormat.RGBA16Uint;
  1123. default:
  1124. return WebGPUConstants.TextureFormat.RGBA16Uint;
  1125. }
  1126. case Constants.TEXTURETYPE_INT:
  1127. switch (format) {
  1128. case Constants.TEXTUREFORMAT_RED_INTEGER:
  1129. return WebGPUConstants.TextureFormat.R32Sint;
  1130. case Constants.TEXTUREFORMAT_RG_INTEGER:
  1131. return WebGPUConstants.TextureFormat.RG32Sint;
  1132. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  1133. throw "TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";
  1134. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  1135. return WebGPUConstants.TextureFormat.RGBA32Sint;
  1136. default:
  1137. return WebGPUConstants.TextureFormat.RGBA32Sint;
  1138. }
  1139. case Constants.TEXTURETYPE_UNSIGNED_INTEGER: // Refers to UNSIGNED_INT
  1140. switch (format) {
  1141. case Constants.TEXTUREFORMAT_RED_INTEGER:
  1142. return WebGPUConstants.TextureFormat.R32Uint;
  1143. case Constants.TEXTUREFORMAT_RG_INTEGER:
  1144. return WebGPUConstants.TextureFormat.RG32Uint;
  1145. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  1146. throw "TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";
  1147. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  1148. return WebGPUConstants.TextureFormat.RGBA32Uint;
  1149. default:
  1150. return WebGPUConstants.TextureFormat.RGBA32Uint;
  1151. }
  1152. case Constants.TEXTURETYPE_FLOAT:
  1153. switch (format) {
  1154. case Constants.TEXTUREFORMAT_RED:
  1155. return WebGPUConstants.TextureFormat.R32Float; // By default. Other possibility is R16Float.
  1156. case Constants.TEXTUREFORMAT_RG:
  1157. return WebGPUConstants.TextureFormat.RG32Float; // By default. Other possibility is RG16Float.
  1158. case Constants.TEXTUREFORMAT_RGB:
  1159. throw "TEXTUREFORMAT_RGB format not supported in WebGPU";
  1160. case Constants.TEXTUREFORMAT_RGBA:
  1161. return WebGPUConstants.TextureFormat.RGBA32Float; // By default. Other possibility is RGBA16Float.
  1162. default:
  1163. return WebGPUConstants.TextureFormat.RGBA32Float;
  1164. }
  1165. case Constants.TEXTURETYPE_HALF_FLOAT:
  1166. switch (format) {
  1167. case Constants.TEXTUREFORMAT_RED:
  1168. return WebGPUConstants.TextureFormat.R16Float;
  1169. case Constants.TEXTUREFORMAT_RG:
  1170. return WebGPUConstants.TextureFormat.RG16Float;
  1171. case Constants.TEXTUREFORMAT_RGB:
  1172. throw "TEXTUREFORMAT_RGB format not supported in WebGPU";
  1173. case Constants.TEXTUREFORMAT_RGBA:
  1174. return WebGPUConstants.TextureFormat.RGBA16Float;
  1175. default:
  1176. return WebGPUConstants.TextureFormat.RGBA16Float;
  1177. }
  1178. case Constants.TEXTURETYPE_UNSIGNED_SHORT_5_6_5:
  1179. throw "TEXTURETYPE_UNSIGNED_SHORT_5_6_5 format not supported in WebGPU";
  1180. case Constants.TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV:
  1181. throw "TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV format not supported in WebGPU";
  1182. case Constants.TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV:
  1183. throw "TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV format not supported in WebGPU";
  1184. case Constants.TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4:
  1185. throw "TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4 format not supported in WebGPU";
  1186. case Constants.TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1:
  1187. throw "TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1 format not supported in WebGPU";
  1188. case Constants.TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV:
  1189. switch (format) {
  1190. case Constants.TEXTUREFORMAT_RGBA:
  1191. return WebGPUConstants.TextureFormat.RGB10A2Unorm;
  1192. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  1193. throw "TEXTUREFORMAT_RGBA_INTEGER format not supported in WebGPU when type is TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV";
  1194. default:
  1195. return WebGPUConstants.TextureFormat.RGB10A2Unorm;
  1196. }
  1197. }
  1198. return WebGPUConstants.TextureFormat.RGBA8Unorm;
  1199. }
  1200. private _getWrappingMode(mode: number): GPUAddressMode {
  1201. switch (mode) {
  1202. case Engine.TEXTURE_WRAP_ADDRESSMODE:
  1203. return WebGPUConstants.AddressMode.Repeat;
  1204. case Engine.TEXTURE_CLAMP_ADDRESSMODE:
  1205. return WebGPUConstants.AddressMode.ClampToEdge;
  1206. case Engine.TEXTURE_MIRROR_ADDRESSMODE:
  1207. return WebGPUConstants.AddressMode.MirrorRepeat;
  1208. }
  1209. return WebGPUConstants.AddressMode.Repeat;
  1210. }
  1211. private _getSamplerWrappingDescriptor(internalTexture: InternalTexture): {
  1212. addressModeU: GPUAddressMode,
  1213. addressModeV: GPUAddressMode,
  1214. addressModeW: GPUAddressMode
  1215. } {
  1216. return {
  1217. addressModeU: this._getWrappingMode(internalTexture._cachedWrapU!),
  1218. addressModeV: this._getWrappingMode(internalTexture._cachedWrapV!),
  1219. addressModeW: this._getWrappingMode(internalTexture._cachedWrapR!),
  1220. };
  1221. }
  1222. private _getSamplerDescriptor(internalTexture: InternalTexture): GPUSamplerDescriptor {
  1223. return {
  1224. ...this._getSamplerFilterDescriptor(internalTexture),
  1225. ...this._getSamplerWrappingDescriptor(internalTexture),
  1226. compare: internalTexture._comparisonFunction ? this._getCompareFunction(internalTexture._comparisonFunction) : undefined,
  1227. };
  1228. }
  1229. public createTexture(url: Nullable<string>, noMipmap: boolean, invertY: boolean, scene: Nullable<ISceneLike>, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE,
  1230. onLoad: Nullable<() => void> = null, onError: Nullable<(message: string, exception: any) => void> = null,
  1231. buffer: Nullable<string | ArrayBuffer | ArrayBufferView | HTMLImageElement | Blob | ImageBitmap> = null, fallback: Nullable<InternalTexture> = null, format: Nullable<number> = null,
  1232. forcedExtension: Nullable<string> = null, mimeType?: string): InternalTexture {
  1233. // TODO WEBGPU. this._options.textureSize
  1234. return this._createTextureBase(
  1235. url, noMipmap, invertY, scene, samplingMode, onLoad, onError,
  1236. (texture: InternalTexture, extension: string, scene: Nullable<ISceneLike>, img: HTMLImageElement | ImageBitmap | { width: number, height: number }, invertY: boolean, noMipmap: boolean, isCompressed: boolean,
  1237. processFunction: (width: number, height: number, img: HTMLImageElement | ImageBitmap | { width: number, height: number }, extension: string, texture: InternalTexture, continuationCallback: () => void) => boolean, samplingMode: number) => {
  1238. const imageBitmap = img as (ImageBitmap | { width: number, height: number}); // we will never get an HTMLImageElement in WebGPU
  1239. texture.baseWidth = imageBitmap.width;
  1240. texture.baseHeight = imageBitmap.height;
  1241. texture.width = imageBitmap.width;
  1242. texture.height = imageBitmap.height;
  1243. texture.format = format ?? -1;
  1244. processFunction(texture.width, texture.height, imageBitmap, extension, texture, () => {});
  1245. if (!texture._hardwareTexture?.underlyingResource) { // the texture could have been created before reaching this point so don't recreate it if already existing
  1246. const gpuTextureWrapper = this._createGPUTextureForInternalTexture(texture, imageBitmap.width, imageBitmap.height);
  1247. if (this._textureHelper.isImageBitmap(imageBitmap)) {
  1248. this._textureHelper.updateTexture(imageBitmap, gpuTextureWrapper.underlyingResource!, imageBitmap.width, imageBitmap.height, gpuTextureWrapper.format, 0, 0, invertY, false, 0, 0, this._uploadEncoder);
  1249. if (!noMipmap && !isCompressed) {
  1250. this._generateMipmaps(texture);
  1251. }
  1252. }
  1253. } else if (!noMipmap && !isCompressed) {
  1254. this._generateMipmaps(texture);
  1255. }
  1256. if (scene) {
  1257. scene._removePendingData(texture);
  1258. }
  1259. texture.isReady = true;
  1260. texture.onLoadedObservable.notifyObservers(texture);
  1261. texture.onLoadedObservable.clear();
  1262. },
  1263. () => false,
  1264. buffer, fallback, format, forcedExtension, mimeType
  1265. );
  1266. }
  1267. /** @hidden */
  1268. public _setCubeMapTextureParams(texture: InternalTexture, loadMipmap: boolean) {
  1269. texture.samplingMode = loadMipmap ? Engine.TEXTURE_TRILINEAR_SAMPLINGMODE : Engine.TEXTURE_BILINEAR_SAMPLINGMODE;
  1270. texture._cachedWrapU = Constants.TEXTURE_CLAMP_ADDRESSMODE;
  1271. texture._cachedWrapV = Constants.TEXTURE_CLAMP_ADDRESSMODE;
  1272. }
  1273. public createCubeTexture(rootUrl: string, scene: Nullable<Scene>, files: Nullable<string[]>, noMipmap?: boolean, onLoad: Nullable<(data?: any) => void> = null,
  1274. 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 {
  1275. return this.createCubeTextureBase(
  1276. rootUrl, scene, files, !!noMipmap, onLoad, onError, format, forcedExtension, createPolynomials, lodScale, lodOffset, fallback,
  1277. null,
  1278. (texture: InternalTexture, imgs: HTMLImageElement[] | ImageBitmap[]) => {
  1279. const imageBitmaps = imgs as ImageBitmap[]; // we will always get an ImageBitmap array in WebGPU
  1280. const width = imageBitmaps[0].width;
  1281. const height = width;
  1282. this._setCubeMapTextureParams(texture, !noMipmap);
  1283. texture.format = format ?? -1;
  1284. const gpuTextureWrapper = this._createGPUTextureForInternalTexture(texture, width, height);
  1285. this._textureHelper.updateCubeTextures(imageBitmaps, gpuTextureWrapper.underlyingResource!, width, height, gpuTextureWrapper.format, false, false, 0, 0, this._uploadEncoder);
  1286. if (!noMipmap) {
  1287. this._generateMipmaps(texture);
  1288. }
  1289. texture.isReady = true;
  1290. texture.onLoadedObservable.notifyObservers(texture);
  1291. texture.onLoadedObservable.clear();
  1292. if (onLoad) {
  1293. onLoad();
  1294. }
  1295. }
  1296. );
  1297. }
  1298. public createRawTexture(data: Nullable<ArrayBufferView>, width: number, height: number, format: number, generateMipMaps: boolean, invertY: boolean, samplingMode: number,
  1299. compression: Nullable<string> = null, type: number = Constants.TEXTURETYPE_UNSIGNED_INT): InternalTexture
  1300. {
  1301. const texture = new InternalTexture(this, InternalTextureSource.Raw);
  1302. texture.baseWidth = width;
  1303. texture.baseHeight = height;
  1304. texture.width = width;
  1305. texture.height = height;
  1306. texture.format = format;
  1307. texture.generateMipMaps = generateMipMaps;
  1308. texture.samplingMode = samplingMode;
  1309. texture.invertY = invertY;
  1310. texture._compression = compression;
  1311. texture.type = type;
  1312. if (!this._doNotHandleContextLost) {
  1313. texture._bufferView = data;
  1314. }
  1315. this._createGPUTextureForInternalTexture(texture, width, height);
  1316. this.updateRawTexture(texture, data, format, invertY, compression, type);
  1317. this._internalTexturesCache.push(texture);
  1318. return texture;
  1319. }
  1320. public createRawCubeTexture(data: Nullable<ArrayBufferView[]>, size: number, format: number, type: number,
  1321. generateMipMaps: boolean, invertY: boolean, samplingMode: number,
  1322. compression: Nullable<string> = null): InternalTexture
  1323. {
  1324. const texture = new InternalTexture(this, InternalTextureSource.CubeRaw);
  1325. texture.isCube = true;
  1326. texture.format = format === Constants.TEXTUREFORMAT_RGB ? Constants.TEXTUREFORMAT_RGBA : format;
  1327. texture.type = type;
  1328. texture.generateMipMaps = generateMipMaps;
  1329. texture.width = size;
  1330. texture.height = size;
  1331. if (!this._doNotHandleContextLost) {
  1332. texture._bufferViewArray = data;
  1333. }
  1334. this._createGPUTextureForInternalTexture(texture);
  1335. if (data) {
  1336. this.updateRawCubeTexture(texture, data, format, type, invertY, compression);
  1337. }
  1338. return texture;
  1339. }
  1340. public createRawCubeTextureFromUrl(url: string, scene: Nullable<Scene>, size: number, format: number, type: number, noMipmap: boolean,
  1341. callback: (ArrayBuffer: ArrayBuffer) => Nullable<ArrayBufferView[]>,
  1342. mipmapGenerator: Nullable<((faces: ArrayBufferView[]) => ArrayBufferView[][])>,
  1343. onLoad: Nullable<() => void> = null,
  1344. onError: Nullable<(message?: string, exception?: any) => void> = null,
  1345. samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE,
  1346. invertY: boolean = false): InternalTexture
  1347. {
  1348. const texture = this.createRawCubeTexture(null, size, format, type, !noMipmap, invertY, samplingMode, null);
  1349. scene?._addPendingData(texture);
  1350. texture.url = url;
  1351. this._internalTexturesCache.push(texture);
  1352. const onerror = (request?: IWebRequest, exception?: any) => {
  1353. scene?._removePendingData(texture);
  1354. if (onError && request) {
  1355. onError(request.status + " " + request.statusText, exception);
  1356. }
  1357. };
  1358. const internalCallback = (data: any) => {
  1359. const width = texture.width;
  1360. const faceDataArrays = callback(data);
  1361. if (!faceDataArrays) {
  1362. return;
  1363. }
  1364. const faces = [0, 2, 4, 1, 3, 5];
  1365. if (mipmapGenerator) {
  1366. const needConversion = format === Constants.TEXTUREFORMAT_RGB;
  1367. const mipData = mipmapGenerator(faceDataArrays);
  1368. const gpuTextureWrapper = texture._hardwareTexture as WebGPUHardwareTexture;
  1369. const faces = [0, 1, 2, 3, 4, 5];
  1370. for (let level = 0; level < mipData.length; level++) {
  1371. const mipSize = width >> level;
  1372. const allFaces = [];
  1373. for (let faceIndex = 0; faceIndex < 6; faceIndex++) {
  1374. let mipFaceData = mipData[level][faces[faceIndex]];
  1375. if (needConversion) {
  1376. mipFaceData = _convertRGBtoRGBATextureData(mipFaceData, mipSize, mipSize, type);
  1377. }
  1378. allFaces.push(new Uint8Array(mipFaceData.buffer, mipFaceData.byteOffset, mipFaceData.byteLength));
  1379. }
  1380. this._textureHelper.updateCubeTextures(allFaces, gpuTextureWrapper.underlyingResource!, mipSize, mipSize, gpuTextureWrapper.format, invertY, false, 0, 0, this._uploadEncoder);
  1381. }
  1382. }
  1383. else {
  1384. const allFaces = [];
  1385. for (let faceIndex = 0; faceIndex < 6; faceIndex++) {
  1386. allFaces.push(faceDataArrays[faces[faceIndex]]);
  1387. }
  1388. this.updateRawCubeTexture(texture, allFaces, format, type, invertY);
  1389. }
  1390. texture.isReady = true;
  1391. scene?._removePendingData(texture);
  1392. if (onLoad) {
  1393. onLoad();
  1394. }
  1395. };
  1396. this._loadFile(url, (data) => {
  1397. internalCallback(data);
  1398. }, undefined, scene?.offlineProvider, true, onerror);
  1399. return texture;
  1400. }
  1401. public createRawTexture2DArray(data: Nullable<ArrayBufferView>, width: number, height: number, depth: number, format: number, generateMipMaps: boolean, invertY: boolean, samplingMode: number,
  1402. compression: Nullable<string> = null, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT): InternalTexture
  1403. {
  1404. var source = InternalTextureSource.Raw2DArray;
  1405. var texture = new InternalTexture(this, source);
  1406. if (dbgShowWarningsNotImplemented) {
  1407. console.warn("createRawTexture2DArray not implemented yet in WebGPU");
  1408. }
  1409. return texture;
  1410. }
  1411. public createRawTexture3D(data: Nullable<ArrayBufferView>, width: number, height: number, depth: number, format: number, generateMipMaps: boolean, invertY: boolean, samplingMode: number,
  1412. compression: Nullable<string> = null, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT): InternalTexture
  1413. {
  1414. var source = InternalTextureSource.Raw2DArray;
  1415. var texture = new InternalTexture(this, source);
  1416. if (dbgShowWarningsNotImplemented) {
  1417. console.warn("createRawTexture3D not implemented yet in WebGPU");
  1418. }
  1419. return texture;
  1420. }
  1421. public generateMipMapsForCubemap(texture: InternalTexture, unbind = true) {
  1422. if (texture.generateMipMaps) {
  1423. let gpuTexture = texture._hardwareTexture?.underlyingResource;
  1424. if (!gpuTexture) {
  1425. this._createGPUTextureForInternalTexture(texture);
  1426. }
  1427. this._generateMipmaps(texture);
  1428. }
  1429. }
  1430. public updateTextureSamplingMode(samplingMode: number, texture: InternalTexture, generateMipMaps: boolean = false): void {
  1431. if (generateMipMaps) {
  1432. texture.generateMipMaps = true;
  1433. this._generateMipmaps(texture);
  1434. }
  1435. texture.samplingMode = samplingMode;
  1436. }
  1437. public updateTextureWrappingMode(texture: InternalTexture, wrapU: Nullable<number>, wrapV: Nullable<number> = null, wrapR: Nullable<number> = null): void {
  1438. if (wrapU !== null) {
  1439. texture._cachedWrapU = wrapU;
  1440. }
  1441. if (wrapV !== null) {
  1442. texture._cachedWrapV = wrapV;
  1443. }
  1444. if ((texture.is2DArray || texture.is3D) && (wrapR !== null)) {
  1445. texture._cachedWrapR = wrapR;
  1446. }
  1447. }
  1448. private _setInternalTexture(name: string, internalTexture: Nullable<InternalTexture>): void {
  1449. if (this._currentEffect) {
  1450. const webgpuPipelineContext = this._currentEffect._pipelineContext as WebGPUPipelineContext;
  1451. if (webgpuPipelineContext.samplers[name]) {
  1452. if (webgpuPipelineContext.samplers[name]!.texture !== internalTexture) {
  1453. webgpuPipelineContext.bindGroups = null as any; // the bind groups need to be rebuilt (at least the bind group owning this texture, but it's easier to just have them all rebuilt)
  1454. }
  1455. webgpuPipelineContext.samplers[name]!.texture = internalTexture!;
  1456. }
  1457. else {
  1458. // TODO WEBGPU. 121 mapping samplers <-> availableSamplers
  1459. const availableSampler = webgpuPipelineContext.availableSamplers[name];
  1460. if (availableSampler) {
  1461. webgpuPipelineContext.samplers[name] = {
  1462. setIndex: availableSampler.setIndex,
  1463. textureBinding: availableSampler.bindingIndex,
  1464. samplerBinding: availableSampler.bindingIndex + 1,
  1465. texture: internalTexture!
  1466. };
  1467. }
  1468. }
  1469. }
  1470. }
  1471. public setTexture(channel: number, _: Nullable<WebGLUniformLocation>, texture: Nullable<BaseTexture>, name: string): void {
  1472. this._setTexture(channel, texture, false, false, name);
  1473. }
  1474. protected _setTexture(channel: number, texture: Nullable<BaseTexture>, isPartOfTextureArray = false, depthStencilTexture = false, name = ""): boolean {
  1475. if (this._currentEffect) {
  1476. const webgpuPipelineContext = this._currentEffect._pipelineContext as WebGPUPipelineContext;
  1477. if (!texture) {
  1478. if (webgpuPipelineContext.samplers[name] && webgpuPipelineContext.samplers[name]!.texture) {
  1479. webgpuPipelineContext.bindGroups = null as any; // the bind groups need to be rebuilt (at least the bind group owning this texture, but it's easier to just have them all rebuilt)
  1480. }
  1481. webgpuPipelineContext.samplers[name] = null;
  1482. return false;
  1483. }
  1484. // Video
  1485. if ((<VideoTexture>texture).video) {
  1486. this._activeChannel = channel;
  1487. (<VideoTexture>texture).update();
  1488. } else if (texture.delayLoadState === Constants.DELAYLOADSTATE_NOTLOADED) { // Delay loading
  1489. texture.delayLoad();
  1490. return false;
  1491. }
  1492. let internalTexture: Nullable<InternalTexture> = null;
  1493. if (depthStencilTexture) {
  1494. internalTexture = (<RenderTargetTexture>texture).depthStencilTexture!;
  1495. }
  1496. else if (texture.isReady()) {
  1497. internalTexture = <InternalTexture>texture.getInternalTexture();
  1498. }
  1499. else if (texture.isCube) {
  1500. internalTexture = this.emptyCubeTexture;
  1501. if (dbgGenerateLogs) {
  1502. console.log("Using a temporary empty cube texture. internalTexture.uniqueId=", texture.uniqueId, texture);
  1503. }
  1504. }
  1505. else if (texture.is3D) {
  1506. internalTexture = this.emptyTexture3D;
  1507. if (dbgGenerateLogs) {
  1508. console.log("Using a temporary empty 3D texture. internalTexture.uniqueId=", texture.uniqueId, texture);
  1509. }
  1510. }
  1511. else if (texture.is2DArray) {
  1512. internalTexture = this.emptyTexture2DArray;
  1513. if (dbgGenerateLogs) {
  1514. console.log("Using a temporary empty 2D array texture. internalTexture.uniqueId=", texture.uniqueId, texture);
  1515. }
  1516. }
  1517. else {
  1518. internalTexture = this.emptyTexture;
  1519. if (dbgGenerateLogs) {
  1520. console.log("Using a temporary empty texture. internalTexture.uniqueId=", texture.uniqueId, texture);
  1521. }
  1522. }
  1523. if (internalTexture && !internalTexture.isMultiview) {
  1524. // CUBIC_MODE and SKYBOX_MODE both require CLAMP_TO_EDGE. All other modes use REPEAT.
  1525. if (internalTexture.isCube && internalTexture._cachedCoordinatesMode !== texture.coordinatesMode) {
  1526. internalTexture._cachedCoordinatesMode = texture.coordinatesMode;
  1527. const textureWrapMode = (texture.coordinatesMode !== Constants.TEXTURE_CUBIC_MODE && texture.coordinatesMode !== Constants.TEXTURE_SKYBOX_MODE) ? Constants.TEXTURE_WRAP_ADDRESSMODE : Constants.TEXTURE_CLAMP_ADDRESSMODE;
  1528. texture.wrapU = textureWrapMode;
  1529. texture.wrapV = textureWrapMode;
  1530. }
  1531. this._setAnisotropicLevel(0, internalTexture, texture.anisotropicFilteringLevel);
  1532. }
  1533. if (internalTexture) {
  1534. internalTexture._cachedWrapU = texture.wrapU;
  1535. internalTexture._cachedWrapV = texture.wrapV;
  1536. internalTexture._cachedWrapR = texture.wrapR;
  1537. }
  1538. if (dbgSanityChecks && internalTexture && (internalTexture as any)._released) {
  1539. console.error("using a released texture in engine.setTexture!", internalTexture);
  1540. debugger;
  1541. }
  1542. this._setInternalTexture(name, internalTexture);
  1543. } else {
  1544. if (dbgVerboseLogsForFirstFrames) {
  1545. if (!(this as any)._count || (this as any)._count < dbgVerboseLogsNumFrames) {
  1546. console.log("_setTexture called with a null _currentEffect! frame #" + (this as any)._count, " - texture=", texture);
  1547. }
  1548. }
  1549. }
  1550. return true;
  1551. }
  1552. /** @hidden */
  1553. public _setAnisotropicLevel(target: number, internalTexture: InternalTexture, anisotropicFilteringLevel: number) {
  1554. if (internalTexture.samplingMode !== Constants.TEXTURE_LINEAR_LINEAR_MIPNEAREST
  1555. && internalTexture.samplingMode !== Constants.TEXTURE_LINEAR_LINEAR_MIPLINEAR
  1556. && internalTexture.samplingMode !== Constants.TEXTURE_LINEAR_LINEAR) {
  1557. anisotropicFilteringLevel = 1; // Forcing the anisotropic to 1 because else webgl will force filters to linear
  1558. }
  1559. if (internalTexture._cachedAnisotropicFilteringLevel !== anisotropicFilteringLevel) {
  1560. //this._setTextureParameterFloat(target, anisotropicFilterExtension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min(anisotropicFilteringLevel, this._caps.maxAnisotropy), internalTexture);
  1561. internalTexture._cachedAnisotropicFilteringLevel = anisotropicFilteringLevel;
  1562. if (dbgShowWarningsNotImplemented) {
  1563. console.warn("_setAnisotropicLevel not implemented yet");
  1564. }
  1565. }
  1566. }
  1567. public bindSamplers(effect: Effect): void { }
  1568. public _bindTextureDirectly(target: number, texture: InternalTexture, forTextureDataUpdate = false, force = false): boolean {
  1569. if (this._boundTexturesCache[this._activeChannel] !== texture) {
  1570. this._boundTexturesCache[this._activeChannel] = texture;
  1571. return true;
  1572. }
  1573. return false;
  1574. }
  1575. /** @hidden */
  1576. public _bindTexture(channel: number, texture: InternalTexture, name: string): void {
  1577. if (channel === undefined) {
  1578. return;
  1579. }
  1580. if (texture) {
  1581. texture._associatedChannel = channel;
  1582. }
  1583. this._activeChannel = channel;
  1584. this._setInternalTexture(name, texture);
  1585. }
  1586. private _createGPUTextureForInternalTexture(texture: InternalTexture, width?: number, height?: number): WebGPUHardwareTexture {
  1587. if (!texture._hardwareTexture) {
  1588. texture._hardwareTexture = this._createHardwareTexture();
  1589. }
  1590. if (width === undefined) {
  1591. width = texture.width;
  1592. }
  1593. if (height === undefined) {
  1594. height = texture.height;
  1595. }
  1596. const gpuTextureWrapper = texture._hardwareTexture as WebGPUHardwareTexture;
  1597. gpuTextureWrapper.format = this._getWebGPUTextureFormat(texture.type, texture.format);
  1598. const textureUsages =
  1599. texture._source === InternalTextureSource.RenderTarget ? WebGPUConstants.TextureUsage.Sampled | WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.OutputAttachment :
  1600. texture._source === InternalTextureSource.Depth ? WebGPUConstants.TextureUsage.Sampled | WebGPUConstants.TextureUsage.OutputAttachment : -1;
  1601. const generateMipMaps = texture._source === InternalTextureSource.RenderTarget ? false : texture.generateMipMaps;
  1602. if (texture.isCube) {
  1603. const gpuTexture = this._textureHelper.createCubeTexture({ width, height }, texture.generateMipMaps, texture.generateMipMaps, texture.invertY, false, gpuTextureWrapper.format, texture.samples || 1, this._uploadEncoder, textureUsages);
  1604. gpuTextureWrapper.set(gpuTexture);
  1605. gpuTextureWrapper.createView({
  1606. dimension: WebGPUConstants.TextureViewDimension.Cube,
  1607. mipLevelCount: generateMipMaps ? WebGPUTextureHelper.computeNumMipmapLevels(width!, height!) : 1,
  1608. baseArrayLayer: 0,
  1609. baseMipLevel: 0,
  1610. arrayLayerCount: 0,
  1611. aspect: WebGPUConstants.TextureAspect.All
  1612. });
  1613. } else {
  1614. const gpuTexture = this._textureHelper.createTexture({ width, height }, texture.generateMipMaps, texture.generateMipMaps, texture.invertY, false, gpuTextureWrapper.format, texture.samples || 1, this._uploadEncoder, textureUsages);
  1615. gpuTextureWrapper.set(gpuTexture);
  1616. gpuTextureWrapper.createView({
  1617. dimension: WebGPUConstants.TextureViewDimension.E2d,
  1618. mipLevelCount: generateMipMaps ? WebGPUTextureHelper.computeNumMipmapLevels(width!, height!) : 1,
  1619. baseArrayLayer: 0,
  1620. baseMipLevel: 0,
  1621. aspect: WebGPUConstants.TextureAspect.All
  1622. });
  1623. }
  1624. texture.width = texture.baseWidth = width;
  1625. texture.height = texture.baseHeight = height;
  1626. return gpuTextureWrapper;
  1627. }
  1628. private _generateMipmaps(texture: InternalTexture) {
  1629. const gpuTexture = texture._hardwareTexture?.underlyingResource;
  1630. if (!gpuTexture) {
  1631. return;
  1632. }
  1633. const format = (texture._hardwareTexture as WebGPUHardwareTexture).format;
  1634. const mipmapCount = WebGPUTextureHelper.computeNumMipmapLevels(texture.width, texture.height);
  1635. if (dbgVerboseLogsForFirstFrames) {
  1636. if (!(this as any)._count || (this as any)._count < dbgVerboseLogsNumFrames) {
  1637. console.log("generate mipmaps called frame #" + (this as any)._count, " - width=", texture.width, "height=", texture.height, "isCube=", texture.isCube);
  1638. }
  1639. }
  1640. if (texture.isCube) {
  1641. this._textureHelper.generateCubeMipmaps(gpuTexture, format, mipmapCount, this._uploadEncoder);
  1642. } else {
  1643. this._textureHelper.generateMipmaps(gpuTexture, format, mipmapCount, 0, this._uploadEncoder);
  1644. }
  1645. }
  1646. public updateDynamicTexture(texture: Nullable<InternalTexture>, canvas: HTMLCanvasElement | OffscreenCanvas, invertY: boolean, premulAlpha: boolean = false, format?: number, forceBindTexture?: boolean): void {
  1647. if (!texture) {
  1648. return;
  1649. }
  1650. const width = canvas.width, height = canvas.height;
  1651. let gpuTextureWrapper = texture._hardwareTexture as WebGPUHardwareTexture;
  1652. if (!texture._hardwareTexture?.underlyingResource) {
  1653. gpuTextureWrapper = this._createGPUTextureForInternalTexture(texture, width, height);
  1654. }
  1655. if (dbgSanityChecks && (texture as any)._released) {
  1656. console.error("Using a released texture in updateDynamicTexture!", texture, gpuTextureWrapper);
  1657. }
  1658. createImageBitmap(canvas).then((bitmap) => {
  1659. this._textureHelper.updateTexture(bitmap, gpuTextureWrapper.underlyingResource!, width, height, gpuTextureWrapper.format, 0, 0, invertY, premulAlpha, 0, 0, this._uploadEncoder);
  1660. if (texture.generateMipMaps) {
  1661. this._generateMipmaps(texture);
  1662. }
  1663. texture.isReady = true;
  1664. });
  1665. }
  1666. public updateTextureData(texture: InternalTexture, imageData: ArrayBufferView, xOffset: number, yOffset: number, width: number, height: number, faceIndex: number = 0, lod: number = 0): void {
  1667. let gpuTextureWrapper = texture._hardwareTexture as WebGPUHardwareTexture;
  1668. if (!texture._hardwareTexture?.underlyingResource) {
  1669. gpuTextureWrapper = this._createGPUTextureForInternalTexture(texture);
  1670. }
  1671. const data = new Uint8Array(imageData.buffer, imageData.byteOffset, imageData.byteLength);
  1672. this._textureHelper.updateTexture(data, gpuTextureWrapper.underlyingResource!, width, height, gpuTextureWrapper.format, faceIndex, lod, texture.invertY, false, xOffset, yOffset, this._uploadEncoder);
  1673. }
  1674. public updateVideoTexture(texture: Nullable<InternalTexture>, video: HTMLVideoElement, invertY: boolean): void {
  1675. if (!texture || texture._isDisabled) {
  1676. return;
  1677. }
  1678. if (this._videoTextureSupported === undefined) {
  1679. this._videoTextureSupported = true;
  1680. }
  1681. let gpuTextureWrapper = texture._hardwareTexture as WebGPUHardwareTexture;
  1682. if (!texture._hardwareTexture?.underlyingResource) {
  1683. gpuTextureWrapper = this._createGPUTextureForInternalTexture(texture);
  1684. }
  1685. createImageBitmap(video).then((bitmap) => {
  1686. this._textureHelper.updateTexture(bitmap, gpuTextureWrapper.underlyingResource!, texture.width, texture.height, gpuTextureWrapper.format, 0, 0, !invertY, false, 0, 0, this._uploadEncoder);
  1687. if (texture.generateMipMaps) {
  1688. this._generateMipmaps(texture);
  1689. }
  1690. texture.isReady = true;
  1691. }).catch((msg) => {
  1692. // Sometimes createImageBitmap(video) fails with "Failed to execute 'createImageBitmap' on 'Window': The provided element's player has no current data."
  1693. // Just keep going on
  1694. texture.isReady = true;
  1695. });
  1696. }
  1697. /** @hidden */
  1698. public _uploadCompressedDataToTextureDirectly(texture: InternalTexture, internalFormat: number, width: number, height: number, imageData: ArrayBufferView, faceIndex: number = 0, lod: number = 0) {
  1699. let gpuTextureWrapper = texture._hardwareTexture as WebGPUHardwareTexture;
  1700. if (!texture._hardwareTexture?.underlyingResource) {
  1701. texture.format = internalFormat;
  1702. gpuTextureWrapper = this._createGPUTextureForInternalTexture(texture, width, height);
  1703. }
  1704. const data = new Uint8Array(imageData.buffer, imageData.byteOffset, imageData.byteLength);
  1705. this._textureHelper.updateTexture(data, gpuTextureWrapper.underlyingResource!, width, height, gpuTextureWrapper.format, faceIndex, lod, texture.invertY, false, 0, 0, this._uploadEncoder);
  1706. }
  1707. /** @hidden */
  1708. public _uploadDataToTextureDirectly(texture: InternalTexture, imageData: ArrayBufferView, faceIndex: number = 0, lod: number = 0, babylonInternalFormat?: number, useTextureWidthAndHeight = false): void {
  1709. // TODO WEBPU babylonInternalFormat not handled.
  1710. // Note that it is used only by BasisTools.LoadTextureFromTranscodeResult when transcoding could not be done, and in that case the texture format used (TEXTURETYPE_UNSIGNED_SHORT_5_6_5) is not compatible with WebGPU...
  1711. const lodMaxWidth = Math.round(Math.log(texture.width) * Math.LOG2E);
  1712. const lodMaxHeight = Math.round(Math.log(texture.height) * Math.LOG2E);
  1713. const width = useTextureWidthAndHeight ? texture.width : Math.pow(2, Math.max(lodMaxWidth - lod, 0));
  1714. const height = useTextureWidthAndHeight ? texture.height : Math.pow(2, Math.max(lodMaxHeight - lod, 0));
  1715. let gpuTextureWrapper = texture._hardwareTexture as WebGPUHardwareTexture;
  1716. if (!texture._hardwareTexture?.underlyingResource) {
  1717. gpuTextureWrapper = this._createGPUTextureForInternalTexture(texture, width, height);
  1718. }
  1719. const data = new Uint8Array(imageData.buffer, imageData.byteOffset, imageData.byteLength);
  1720. this._textureHelper.updateTexture(data, gpuTextureWrapper.underlyingResource!, width, height, gpuTextureWrapper.format, faceIndex, lod, texture.invertY, false, 0, 0, this._uploadEncoder);
  1721. }
  1722. /** @hidden */
  1723. public _uploadArrayBufferViewToTexture(texture: InternalTexture, imageData: ArrayBufferView, faceIndex: number = 0, lod: number = 0): void {
  1724. this._uploadDataToTextureDirectly(texture, imageData, faceIndex, lod);
  1725. }
  1726. /** @hidden */
  1727. public _uploadImageToTexture(texture: InternalTexture, image: HTMLImageElement | ImageBitmap, faceIndex: number = 0, lod: number = 0) {
  1728. let gpuTextureWrapper = texture._hardwareTexture as WebGPUHardwareTexture;
  1729. if (!texture._hardwareTexture?.underlyingResource) {
  1730. gpuTextureWrapper = this._createGPUTextureForInternalTexture(texture);
  1731. }
  1732. const bitmap = image as ImageBitmap; // in WebGPU we will always get an ImageBitmap, not an HTMLImageElement
  1733. const width = Math.ceil(texture.width / (1 << lod));
  1734. const height = Math.ceil(texture.height / (1 << lod));
  1735. this._textureHelper.updateTexture(bitmap, gpuTextureWrapper.underlyingResource!, width, height, gpuTextureWrapper.format, faceIndex, lod, texture.invertY, false, 0, 0, this._uploadEncoder);
  1736. }
  1737. public updateRawTexture(texture: Nullable<InternalTexture>, bufferView: Nullable<ArrayBufferView>, format: number, invertY: boolean, compression: Nullable<string> = null, type: number = Constants.TEXTURETYPE_UNSIGNED_INT): void {
  1738. if (!texture) {
  1739. return;
  1740. }
  1741. if (!this._doNotHandleContextLost) {
  1742. texture._bufferView = bufferView;
  1743. texture.invertY = invertY;
  1744. texture._compression = compression;
  1745. }
  1746. if (bufferView) {
  1747. const gpuTextureWrapper = texture._hardwareTexture as WebGPUHardwareTexture;
  1748. const needConversion = format === Constants.TEXTUREFORMAT_RGB;
  1749. if (needConversion) {
  1750. bufferView = _convertRGBtoRGBATextureData(bufferView, texture.width, texture.height, type);
  1751. }
  1752. const data = new Uint8Array(bufferView.buffer, bufferView.byteOffset, bufferView.byteLength);
  1753. this._textureHelper.updateTexture(data, gpuTextureWrapper.underlyingResource!, texture.width, texture.height, gpuTextureWrapper.format, 0, 0, invertY, false, 0, 0, this._uploadEncoder);
  1754. if (texture.generateMipMaps) {
  1755. this._generateMipmaps(texture);
  1756. }
  1757. }
  1758. texture.isReady = true;
  1759. }
  1760. public updateRawCubeTexture(texture: InternalTexture, bufferView: ArrayBufferView[], format: number, type: number, invertY: boolean, compression: Nullable<string> = null, level: number = 0): void {
  1761. texture._bufferViewArray = bufferView;
  1762. texture.invertY = invertY;
  1763. texture._compression = compression;
  1764. const gpuTextureWrapper = texture._hardwareTexture as WebGPUHardwareTexture;
  1765. const needConversion = format === Constants.TEXTUREFORMAT_RGB;
  1766. const data = [];
  1767. for (let i = 0; i < bufferView.length; ++i) {
  1768. let faceData = bufferView[i];
  1769. if (needConversion) {
  1770. faceData = _convertRGBtoRGBATextureData(bufferView[i], texture.width, texture.height, type);
  1771. }
  1772. data.push(new Uint8Array(faceData.buffer, faceData.byteOffset, faceData.byteLength));
  1773. }
  1774. this._textureHelper.updateCubeTextures(data, gpuTextureWrapper.underlyingResource!, texture.width, texture.height, gpuTextureWrapper.format, invertY, false, 0, 0, this._uploadEncoder);
  1775. if (texture.generateMipMaps) {
  1776. this._generateMipmaps(texture);
  1777. }
  1778. texture.isReady = true;
  1779. }
  1780. public updateRawTexture2DArray(texture: InternalTexture, data: Nullable<ArrayBufferView>, format: number, invertY: boolean, compression: Nullable<string> = null, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT): void {
  1781. if (dbgShowWarningsNotImplemented) {
  1782. console.warn("updateRawTexture2DArray not implemented yet in WebGPU");
  1783. }
  1784. }
  1785. public updateRawTexture3D(texture: InternalTexture, data: Nullable<ArrayBufferView>, format: number, invertY: boolean, compression: Nullable<string> = null, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT): void {
  1786. if (dbgShowWarningsNotImplemented) {
  1787. console.warn("updateRawTexture2DArray not implemented yet in WebGPU");
  1788. }
  1789. }
  1790. public readPixels(x: number, y: number, width: number, height: number, hasAlpha = true): Promise<ArrayBufferView> {
  1791. return this._textureHelper.readPixels(this._swapChainTexture, x, y, width, height, this._options.swapChainFormat!);
  1792. }
  1793. /** @hidden */
  1794. public _readTexturePixels(texture: InternalTexture, width: number, height: number, faceIndex = -1, level = 0, buffer: Nullable<ArrayBufferView> = null): Promise<ArrayBufferView> {
  1795. let gpuTextureWrapper = texture._hardwareTexture as WebGPUHardwareTexture;
  1796. return this._textureHelper.readPixels(gpuTextureWrapper.underlyingResource!, 0, 0, width, height, gpuTextureWrapper.format, faceIndex, level, buffer);
  1797. }
  1798. //------------------------------------------------------------------------------
  1799. // Render Target Textures
  1800. //------------------------------------------------------------------------------
  1801. public createRenderTargetTexture(size: any, options: boolean | RenderTargetCreationOptions): InternalTexture {
  1802. let fullOptions = new RenderTargetCreationOptions();
  1803. if (options !== undefined && typeof options === "object") {
  1804. fullOptions.generateMipMaps = options.generateMipMaps;
  1805. fullOptions.generateDepthBuffer = options.generateDepthBuffer === undefined ? true : options.generateDepthBuffer;
  1806. fullOptions.generateStencilBuffer = fullOptions.generateDepthBuffer && options.generateStencilBuffer;
  1807. fullOptions.type = options.type === undefined ? Constants.TEXTURETYPE_UNSIGNED_INT : options.type;
  1808. fullOptions.samplingMode = options.samplingMode === undefined ? Constants.TEXTURE_TRILINEAR_SAMPLINGMODE : options.samplingMode;
  1809. fullOptions.format = options.format === undefined ? Constants.TEXTUREFORMAT_RGBA : options.format;
  1810. fullOptions.samples = options.samples ?? this._mainPassSampleCount;
  1811. } else {
  1812. fullOptions.generateMipMaps = <boolean>options;
  1813. fullOptions.generateDepthBuffer = true;
  1814. fullOptions.generateStencilBuffer = false;
  1815. fullOptions.type = Constants.TEXTURETYPE_UNSIGNED_INT;
  1816. fullOptions.samplingMode = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE;
  1817. fullOptions.format = Constants.TEXTUREFORMAT_RGBA;
  1818. fullOptions.samples = this._mainPassSampleCount;
  1819. }
  1820. const texture = new InternalTexture(this, InternalTextureSource.RenderTarget);
  1821. const width = size.width || size;
  1822. const height = size.height || size;
  1823. const layers = size.layers || 0;
  1824. texture._depthStencilBuffer = {};
  1825. texture._framebuffer = {};
  1826. texture.baseWidth = width;
  1827. texture.baseHeight = height;
  1828. texture.width = width;
  1829. texture.height = height;
  1830. texture.depth = layers;
  1831. texture.isReady = true;
  1832. texture.samples = fullOptions.samples;
  1833. texture.generateMipMaps = fullOptions.generateMipMaps ? true : false;
  1834. texture.samplingMode = fullOptions.samplingMode;
  1835. texture.type = fullOptions.type;
  1836. texture.format = fullOptions.format;
  1837. texture._generateDepthBuffer = fullOptions.generateDepthBuffer;
  1838. texture._generateStencilBuffer = fullOptions.generateStencilBuffer ? true : false;
  1839. this._internalTexturesCache.push(texture);
  1840. if (texture._generateDepthBuffer || texture._generateStencilBuffer) {
  1841. texture._depthStencilTexture = this.createDepthStencilTexture({ width, height, layers }, {
  1842. bilinearFiltering:
  1843. fullOptions.samplingMode === undefined ||
  1844. fullOptions.samplingMode === Constants.TEXTURE_BILINEAR_SAMPLINGMODE || fullOptions.samplingMode === Constants.TEXTURE_LINEAR_LINEAR ||
  1845. fullOptions.samplingMode === Constants.TEXTURE_TRILINEAR_SAMPLINGMODE || fullOptions.samplingMode === Constants.TEXTURE_LINEAR_LINEAR_MIPLINEAR ||
  1846. fullOptions.samplingMode === Constants.TEXTURE_NEAREST_LINEAR_MIPNEAREST || fullOptions.samplingMode === Constants.TEXTURE_NEAREST_LINEAR_MIPLINEAR ||
  1847. fullOptions.samplingMode === Constants.TEXTURE_NEAREST_LINEAR || fullOptions.samplingMode === Constants.TEXTURE_LINEAR_LINEAR_MIPNEAREST,
  1848. comparisonFunction: 0,
  1849. generateStencil: texture._generateStencilBuffer,
  1850. isCube: texture.isCube,
  1851. samples: texture.samples,
  1852. });
  1853. }
  1854. if (options !== undefined && typeof options === "object" && options.createMipMaps && !fullOptions.generateMipMaps) {
  1855. texture.generateMipMaps = true;
  1856. }
  1857. this._createGPUTextureForInternalTexture(texture);
  1858. if (options !== undefined && typeof options === "object" && options.createMipMaps && !fullOptions.generateMipMaps) {
  1859. texture.generateMipMaps = false;
  1860. }
  1861. return texture;
  1862. }
  1863. public createRenderTargetCubeTexture(size: number, options?: Partial<RenderTargetCreationOptions>): InternalTexture {
  1864. let fullOptions = {
  1865. generateMipMaps: true,
  1866. generateDepthBuffer: true,
  1867. generateStencilBuffer: false,
  1868. type: Constants.TEXTURETYPE_UNSIGNED_INT,
  1869. samplingMode: Constants.TEXTURE_TRILINEAR_SAMPLINGMODE,
  1870. format: Constants.TEXTUREFORMAT_RGBA,
  1871. samples: this._mainPassSampleCount,
  1872. ...options
  1873. };
  1874. fullOptions.generateStencilBuffer = fullOptions.generateDepthBuffer && fullOptions.generateStencilBuffer;
  1875. const texture = new InternalTexture(this, InternalTextureSource.RenderTarget);
  1876. texture.width = size;
  1877. texture.height = size;
  1878. texture.depth = 0;
  1879. texture.isReady = true;
  1880. texture.isCube = true;
  1881. texture.samples = fullOptions.samples;
  1882. texture.generateMipMaps = fullOptions.generateMipMaps;
  1883. texture.samplingMode = fullOptions.samplingMode;
  1884. texture.type = fullOptions.type;
  1885. texture.format = fullOptions.format;
  1886. texture._generateDepthBuffer = fullOptions.generateDepthBuffer;
  1887. texture._generateStencilBuffer = fullOptions.generateStencilBuffer;
  1888. this._internalTexturesCache.push(texture);
  1889. if (texture._generateDepthBuffer || texture._generateStencilBuffer) {
  1890. texture._depthStencilTexture = this.createDepthStencilTexture({ width: texture.width, height: texture.height, layers: texture.depth }, {
  1891. bilinearFiltering:
  1892. fullOptions.samplingMode === undefined ||
  1893. fullOptions.samplingMode === Constants.TEXTURE_BILINEAR_SAMPLINGMODE || fullOptions.samplingMode === Constants.TEXTURE_LINEAR_LINEAR ||
  1894. fullOptions.samplingMode === Constants.TEXTURE_TRILINEAR_SAMPLINGMODE || fullOptions.samplingMode === Constants.TEXTURE_LINEAR_LINEAR_MIPLINEAR ||
  1895. fullOptions.samplingMode === Constants.TEXTURE_NEAREST_LINEAR_MIPNEAREST || fullOptions.samplingMode === Constants.TEXTURE_NEAREST_LINEAR_MIPLINEAR ||
  1896. fullOptions.samplingMode === Constants.TEXTURE_NEAREST_LINEAR || fullOptions.samplingMode === Constants.TEXTURE_LINEAR_LINEAR_MIPNEAREST,
  1897. comparisonFunction: 0,
  1898. generateStencil: texture._generateStencilBuffer,
  1899. isCube: texture.isCube,
  1900. samples: texture.samples,
  1901. });
  1902. }
  1903. if (options && options.createMipMaps && !fullOptions.generateMipMaps) {
  1904. texture.generateMipMaps = true;
  1905. }
  1906. this._createGPUTextureForInternalTexture(texture);
  1907. if (options && options.createMipMaps && !fullOptions.generateMipMaps) {
  1908. texture.generateMipMaps = false;
  1909. }
  1910. return texture;
  1911. }
  1912. /** @hidden */
  1913. public _setupDepthStencilTexture(internalTexture: InternalTexture, size: number | { width: number, height: number, layers?: number }, generateStencil: boolean, bilinearFiltering: boolean, comparisonFunction: number, samples = 1): void {
  1914. const width = (<{ width: number, height: number, layers?: number }>size).width || <number>size;
  1915. const height = (<{ width: number, height: number, layers?: number }>size).height || <number>size;
  1916. const layers = (<{ width: number, height: number, layers?: number }>size).layers || 0;
  1917. internalTexture.baseWidth = width;
  1918. internalTexture.baseHeight = height;
  1919. internalTexture.width = width;
  1920. internalTexture.height = height;
  1921. internalTexture.is2DArray = layers > 0;
  1922. internalTexture.depth = layers;
  1923. internalTexture.isReady = true;
  1924. internalTexture.samples = samples;
  1925. internalTexture.generateMipMaps = false;
  1926. internalTexture._generateDepthBuffer = true;
  1927. internalTexture._generateStencilBuffer = generateStencil;
  1928. internalTexture.samplingMode = bilinearFiltering ? Constants.TEXTURE_BILINEAR_SAMPLINGMODE : Constants.TEXTURE_NEAREST_SAMPLINGMODE;
  1929. internalTexture.type = Constants.TEXTURETYPE_UNSIGNED_INT;
  1930. internalTexture._comparisonFunction = comparisonFunction;
  1931. }
  1932. /** @hidden */
  1933. public _createDepthStencilTexture(size: number | { width: number, height: number, layers?: number }, options: DepthTextureCreationOptions): InternalTexture {
  1934. const internalTexture = new InternalTexture(this, InternalTextureSource.Depth);
  1935. const internalOptions = {
  1936. bilinearFiltering: false,
  1937. comparisonFunction: 0,
  1938. generateStencil: false,
  1939. samples: this._mainPassSampleCount,
  1940. ...options
  1941. };
  1942. internalTexture.format = internalOptions.generateStencil ? Constants.TEXTUREFORMAT_DEPTH24_STENCIL8 : Constants.TEXTUREFORMAT_DEPTH32_FLOAT;
  1943. this._setupDepthStencilTexture(internalTexture, size, internalOptions.generateStencil, internalOptions.bilinearFiltering, internalOptions.comparisonFunction, internalOptions.samples);
  1944. this._createGPUTextureForInternalTexture(internalTexture);
  1945. return internalTexture;
  1946. }
  1947. /** @hidden */
  1948. public _createDepthStencilCubeTexture(size: number, options: DepthTextureCreationOptions): InternalTexture {
  1949. const internalTexture = new InternalTexture(this, InternalTextureSource.Depth);
  1950. internalTexture.isCube = true;
  1951. const internalOptions = {
  1952. bilinearFiltering: false,
  1953. comparisonFunction: 0,
  1954. generateStencil: false,
  1955. samples: this._mainPassSampleCount,
  1956. ...options
  1957. };
  1958. internalTexture.format = internalOptions.generateStencil ? Constants.TEXTUREFORMAT_DEPTH24_STENCIL8 : Constants.TEXTUREFORMAT_DEPTH32_FLOAT;
  1959. this._setupDepthStencilTexture(internalTexture, size, internalOptions.generateStencil, internalOptions.bilinearFiltering, internalOptions.comparisonFunction, internalOptions.samples);
  1960. this._createGPUTextureForInternalTexture(internalTexture);
  1961. return internalTexture;
  1962. }
  1963. public updateRenderTargetTextureSampleCount(texture: Nullable<InternalTexture>, samples: number): number {
  1964. // samples is used at creation time in WebGPU, you can't change it afterwards
  1965. return samples;
  1966. }
  1967. //------------------------------------------------------------------------------
  1968. // Render Commands
  1969. //------------------------------------------------------------------------------
  1970. /**
  1971. * Begin a new frame
  1972. */
  1973. public beginFrame(): void {
  1974. super.beginFrame();
  1975. if (dbgVerboseLogsForFirstFrames) {
  1976. if ((this as any)._count === undefined) {
  1977. (this as any)._count = 1;
  1978. console.log("begin frame #" + (this as any)._count);
  1979. }
  1980. }
  1981. }
  1982. /**
  1983. * End the current frame
  1984. */
  1985. public endFrame() {
  1986. this._endMainRenderPass();
  1987. this._commandBuffers[0] = this._uploadEncoder.finish();
  1988. this._commandBuffers[1] = this._renderTargetEncoder.finish();
  1989. this._commandBuffers[2] = this._renderEncoder.finish();
  1990. this._device.defaultQueue.submit(this._commandBuffers);
  1991. if (dbgVerboseLogsForFirstFrames) {
  1992. if (!(this as any)._count || (this as any)._count < dbgVerboseLogsNumFrames) {
  1993. console.log("counters frame #" + (this as any)._count, " - numPipelineDescriptorCreation=", this._counters.numPipelineDescriptorCreation, ", numBindGroupsCreation=", this._counters.numBindGroupsCreation);
  1994. }
  1995. }
  1996. for (let i = 0; i < this._deferredReleaseTextures.length; ++i) {
  1997. const [texture, hardwareTexture, irradianceTexture, depthStencilTexture] = this._deferredReleaseTextures[i];
  1998. hardwareTexture?.release();
  1999. irradianceTexture?.dispose();
  2000. depthStencilTexture?.dispose();
  2001. // TODO WEBGPU remove debug code
  2002. if ((texture as any)._swapped) {
  2003. delete (texture as any)._swapped;
  2004. } else {
  2005. (texture as any)._released = true;
  2006. }
  2007. }
  2008. this._deferredReleaseTextures.length = 0;
  2009. this._bufferManager.destroyDeferredBuffers();
  2010. this._uploadEncoder = this._device.createCommandEncoder(this._uploadEncoderDescriptor);
  2011. this._renderEncoder = this._device.createCommandEncoder(this._renderEncoderDescriptor);
  2012. this._renderTargetEncoder = this._device.createCommandEncoder(this._renderTargetEncoderDescriptor);
  2013. if (ThinEngine.Features._collectUbosUpdatedInFrame) {
  2014. if (dbgVerboseLogsForFirstFrames) {
  2015. if (!(this as any)._count || (this as any)._count < dbgVerboseLogsNumFrames) {
  2016. const list: Array<string> = [];
  2017. for (const name in UniformBuffer._updatedUbosInFrame) {
  2018. list.push(name + ":" + UniformBuffer._updatedUbosInFrame[name]);
  2019. }
  2020. console.log("updated ubos in frame #" + (this as any)._count, " -", list.join(", "));
  2021. }
  2022. }
  2023. UniformBuffer._updatedUbosInFrame = {};
  2024. }
  2025. this._counters.numPipelineDescriptorCreation = 0;
  2026. this._counters.numBindGroupsCreation = 0;
  2027. super.endFrame();
  2028. if (dbgVerboseLogsForFirstFrames) {
  2029. if ((this as any)._count < dbgVerboseLogsNumFrames) {
  2030. console.log("end frame #" + (this as any)._count);
  2031. }
  2032. if ((this as any)._count < dbgVerboseLogsNumFrames) {
  2033. (this as any)._count++;
  2034. console.log("begin frame #" + (this as any)._count);
  2035. }
  2036. }
  2037. }
  2038. //------------------------------------------------------------------------------
  2039. // Render Pass
  2040. //------------------------------------------------------------------------------
  2041. private _startRenderTargetRenderPass(internalTexture: InternalTexture, clearColor: Nullable<IColor4Like>, clearDepth: boolean, clearStencil: boolean = false) {
  2042. const gpuTexture = (internalTexture._hardwareTexture as WebGPUHardwareTexture).underlyingResource!;
  2043. const colorTextureView = gpuTexture.createView(this._currentRenderTargetViewDescriptor);
  2044. const depthStencilTexture = internalTexture._depthStencilTexture;
  2045. const gpuDepthStencilTexture = depthStencilTexture?._hardwareTexture?.underlyingResource;
  2046. this._renderTargetEncoder.pushDebugGroup("start render target rendering");
  2047. const renderPassDescriptor = {
  2048. colorAttachments: [{
  2049. attachment: colorTextureView,
  2050. loadValue: clearColor !== null ? clearColor : WebGPUConstants.LoadOp.Load,
  2051. storeOp: WebGPUConstants.StoreOp.Store
  2052. }],
  2053. depthStencilAttachment: depthStencilTexture && gpuDepthStencilTexture ? {
  2054. attachment: (depthStencilTexture._hardwareTexture as WebGPUHardwareTexture).view!,
  2055. depthLoadValue: clearDepth && depthStencilTexture._generateDepthBuffer ? this._clearDepthValue : WebGPUConstants.LoadOp.Load,
  2056. depthStoreOp: WebGPUConstants.StoreOp.Store,
  2057. stencilLoadValue: clearStencil && depthStencilTexture._generateStencilBuffer ? this._clearStencilValue : WebGPUConstants.LoadOp.Load,
  2058. stencilStoreOp: WebGPUConstants.StoreOp.Store,
  2059. } : undefined
  2060. };
  2061. const renderPass = this._renderTargetEncoder.beginRenderPass(renderPassDescriptor);
  2062. if (dbgVerboseLogsForFirstFrames) {
  2063. if (!(this as any)._count || (this as any)._count < dbgVerboseLogsNumFrames) {
  2064. console.log("render target begin pass frame #" + (this as any)._count, " - internalTexture.uniqueId=", internalTexture.uniqueId, renderPassDescriptor);
  2065. }
  2066. }
  2067. this._currentRenderPass = renderPass;
  2068. if (this._cachedViewport) {
  2069. this.setViewport(this._cachedViewport);
  2070. }
  2071. // TODO WEBGPU set the scissor rect and the stencil reference value
  2072. }
  2073. private _endRenderTargetRenderPass() {
  2074. if (this._currentRenderPass) {
  2075. this._currentRenderPass.endPass();
  2076. if (dbgVerboseLogsForFirstFrames) {
  2077. if (!(this as any)._count || (this as any)._count < dbgVerboseLogsNumFrames) {
  2078. console.log("render target end pass frame #" + (this as any)._count, " - internalTexture.uniqueId=", this._currentRenderTarget?.uniqueId);
  2079. }
  2080. }
  2081. this._renderTargetEncoder.popDebugGroup();
  2082. this._resetCachedViewport();
  2083. }
  2084. }
  2085. private _getCurrentRenderPass(): GPURenderPassEncoder {
  2086. if (this._currentRenderTarget && !this._currentRenderPass) {
  2087. this._startRenderTargetRenderPass(this._currentRenderTarget, null, false, false);
  2088. } else if (!this._currentRenderPass) {
  2089. this._startMainRenderPass();
  2090. }
  2091. return this._currentRenderPass!;
  2092. }
  2093. private _startMainRenderPass(): void {
  2094. if (this._mainRenderPass) {
  2095. this._endMainRenderPass();
  2096. }
  2097. this._swapChainTexture = this._swapChain.getCurrentTexture();
  2098. // Resolve in case of MSAA
  2099. if (this._options.antialiasing) {
  2100. this._mainColorAttachments[0].resolveTarget = this._swapChainTexture.createView();
  2101. }
  2102. else {
  2103. this._mainColorAttachments[0].attachment = this._swapChainTexture.createView();
  2104. }
  2105. if (dbgVerboseLogsForFirstFrames) {
  2106. if (!(this as any)._count || (this as any)._count < dbgVerboseLogsNumFrames) {
  2107. console.log("main begin pass frame #" + (this as any)._count, "texture width=" + (this._mainTextureExtends as any).width, " height=" + (this._mainTextureExtends as any).height, this._mainColorAttachments, this._mainDepthAttachment);
  2108. }
  2109. }
  2110. this._renderEncoder.pushDebugGroup("start main rendering");
  2111. this._currentRenderPass = this._renderEncoder.beginRenderPass({
  2112. colorAttachments: this._mainColorAttachments,
  2113. depthStencilAttachment: this._mainDepthAttachment
  2114. });
  2115. this._mainRenderPass = this._currentRenderPass;
  2116. if (this._cachedViewport) {
  2117. this.setViewport(this._cachedViewport);
  2118. }
  2119. this._currentRenderPass.setBlendColor(this._alphaState._blendConstants as any);
  2120. // TODO WEBGPU set the scissor rect and the stencil reference value
  2121. }
  2122. private _endMainRenderPass(): void {
  2123. if (this._mainRenderPass !== null) {
  2124. this._mainRenderPass.endPass();
  2125. if (dbgVerboseLogsForFirstFrames) {
  2126. if (!(this as any)._count || (this as any)._count < dbgVerboseLogsNumFrames) {
  2127. console.log("main end pass frame #" + (this as any)._count);
  2128. }
  2129. }
  2130. this._renderEncoder.popDebugGroup();
  2131. this._resetCachedViewport();
  2132. if (this._mainRenderPass === this._currentRenderPass) {
  2133. this._currentRenderPass = null;
  2134. }
  2135. this._mainRenderPass = null;
  2136. }
  2137. }
  2138. public bindFramebuffer(texture: InternalTexture, faceIndex: number = 0, requiredWidth?: number, requiredHeight?: number, forceFullscreenViewport?: boolean, lodLevel = 0, layer = 0): void {
  2139. const hardwareTexture = texture._hardwareTexture as Nullable<WebGPUHardwareTexture>;
  2140. const gpuTexture = hardwareTexture?.underlyingResource as Nullable<GPUTexture>;
  2141. if (!hardwareTexture || !gpuTexture) {
  2142. if (dbgSanityChecks) {
  2143. console.error("bindFramebuffer: Trying to bind a texture that does not have a hardware texture or that has a webgpu texture empty!", texture, hardwareTexture, gpuTexture);
  2144. }
  2145. return;
  2146. }
  2147. if (this._currentRenderTarget) {
  2148. this.unBindFramebuffer(this._currentRenderTarget);
  2149. }
  2150. this._currentRenderTarget = texture;
  2151. this._setDepthTextureFormat(this._currentRenderTarget._depthStencilTexture ? this._getWebGPUTextureFormat(-1, this._currentRenderTarget._depthStencilTexture.format) : undefined);
  2152. this._setColorFormat(this._getWebGPUTextureFormat(this._currentRenderTarget.type, this._currentRenderTarget.format));
  2153. // TODO WEBGPU handle array layer
  2154. const bindWithMipMaps = texture.generateMipMaps && texture._source !== InternalTextureSource.RenderTarget;
  2155. this._currentRenderTargetViewDescriptor = {
  2156. format: this._colorFormat,
  2157. dimension: WebGPUConstants.TextureViewDimension.E2d,
  2158. mipLevelCount: bindWithMipMaps ? WebGPUTextureHelper.computeNumMipmapLevels(texture.width, texture.height) - lodLevel : 1,
  2159. baseArrayLayer: faceIndex,
  2160. baseMipLevel: lodLevel,
  2161. arrayLayerCount: 1,
  2162. aspect: WebGPUConstants.TextureAspect.All
  2163. };
  2164. if (dbgVerboseLogsForFirstFrames) {
  2165. if (!(this as any)._count || (this as any)._count < dbgVerboseLogsNumFrames) {
  2166. console.log("bindFramebuffer called in frame #" + (this as any)._count, "face=", faceIndex, "lodLevel=", lodLevel, "bindWithMipMaps=", bindWithMipMaps, "texture.generateMipMaps=", texture.generateMipMaps, this._currentRenderTargetViewDescriptor);
  2167. }
  2168. }
  2169. this._currentRenderPass = null; // lazy creation of the render pass, hoping the render pass will be created by a call to clear()...
  2170. if (this._cachedViewport && !forceFullscreenViewport) {
  2171. this.setViewport(this._cachedViewport, requiredWidth, requiredHeight);
  2172. } else {
  2173. if (!requiredWidth) {
  2174. requiredWidth = texture.width;
  2175. if (lodLevel) {
  2176. requiredWidth = requiredWidth / Math.pow(2, lodLevel);
  2177. }
  2178. }
  2179. if (!requiredHeight) {
  2180. requiredHeight = texture.height;
  2181. if (lodLevel) {
  2182. requiredHeight = requiredHeight / Math.pow(2, lodLevel);
  2183. }
  2184. }
  2185. this._viewport(0, 0, requiredWidth, requiredHeight);
  2186. }
  2187. this.wipeCaches();
  2188. }
  2189. public unBindFramebuffer(texture: InternalTexture, disableGenerateMipMaps = false, onBeforeUnbind?: () => void): void {
  2190. // TODO WEBGPU remove the assert debugging code
  2191. assert(this._currentRenderTarget === null || (this._currentRenderTarget !== null && texture === this._currentRenderTarget), "unBindFramebuffer - the texture we wan't to unbind is not the same than the currentRenderTarget! texture=" + texture + ", this._currentRenderTarget=" + this._currentRenderTarget);
  2192. if (this._currentRenderPass && this._currentRenderPass !== this._mainRenderPass) {
  2193. this._endRenderTargetRenderPass();
  2194. }
  2195. this._transientViewport.x = Infinity;
  2196. this._currentRenderTarget = null;
  2197. if (texture.generateMipMaps && !disableGenerateMipMaps && !texture.isCube) {
  2198. this._generateMipmaps(texture);
  2199. }
  2200. if (onBeforeUnbind) {
  2201. onBeforeUnbind();
  2202. }
  2203. this._currentRenderPass = this._mainRenderPass;
  2204. this._setDepthTextureFormat(this._getMainDepthTextureFormat());
  2205. this._setColorFormat(this._options.swapChainFormat!);
  2206. }
  2207. public restoreDefaultFramebuffer(): void {
  2208. if (this._currentRenderTarget) {
  2209. this.unBindFramebuffer(this._currentRenderTarget);
  2210. } else {
  2211. this._currentRenderPass = this._mainRenderPass;
  2212. this._setDepthTextureFormat(this._getMainDepthTextureFormat());
  2213. this._setColorFormat(this._options.swapChainFormat!);
  2214. }
  2215. this._transientViewport.x = Infinity;
  2216. if (this._currentRenderPass) {
  2217. if (this._cachedViewport) {
  2218. this.setViewport(this._cachedViewport);
  2219. }
  2220. }
  2221. this.wipeCaches();
  2222. }
  2223. /** @hidden */
  2224. public _releaseFramebufferObjects(texture: InternalTexture): void {
  2225. }
  2226. //------------------------------------------------------------------------------
  2227. // Render
  2228. //------------------------------------------------------------------------------
  2229. public setZOffset(value: number): void {
  2230. if (value !== this._depthCullingState.zOffset) {
  2231. this._depthCullingState.zOffset = value;
  2232. }
  2233. }
  2234. private _setColorFormat(format: GPUTextureFormat): void {
  2235. if (this._colorFormat === format) {
  2236. return;
  2237. }
  2238. this._colorFormat = format;
  2239. }
  2240. private _setDepthTextureFormat(format: GPUTextureFormat | undefined): void {
  2241. if (this._depthTextureFormat === format) {
  2242. return;
  2243. }
  2244. this._depthTextureFormat = format;
  2245. }
  2246. public setDepthBuffer(enable: boolean): void {
  2247. if (this._depthCullingState.depthTest !== enable) {
  2248. this._depthCullingState.depthTest = enable;
  2249. }
  2250. }
  2251. public setDepthWrite(enable: boolean): void {
  2252. if (this._depthCullingState.depthMask !== enable) {
  2253. this._depthCullingState.depthMask = enable;
  2254. }
  2255. }
  2256. public setStencilBuffer(enable: boolean): void {
  2257. if (this._stencilState.stencilTest !== enable) {
  2258. this._stencilState.stencilTest = enable;
  2259. }
  2260. }
  2261. public setStencilMask(mask: number): void {
  2262. if (this._stencilState.stencilMask !== mask) {
  2263. this._stencilState.stencilMask = mask;
  2264. }
  2265. }
  2266. public setStencilFunction(stencilFunc: number) {
  2267. if (this._stencilState.stencilFunc !== stencilFunc) {
  2268. this._stencilState.stencilFunc = stencilFunc;
  2269. }
  2270. }
  2271. public setStencilFunctionReference(reference: number) {
  2272. if (this._stencilState.stencilFuncRef !== reference) {
  2273. this._stencilState.stencilFuncRef = reference;
  2274. }
  2275. }
  2276. public setStencilFunctionMask(mask: number) {
  2277. if (this._stencilState.stencilFuncMask !== mask) {
  2278. this._stencilState.stencilFuncMask = mask;
  2279. }
  2280. }
  2281. public setStencilOperationFail(operation: number): void {
  2282. if (this._stencilState.stencilOpStencilFail !== operation) {
  2283. this._stencilState.stencilOpStencilFail = operation;
  2284. }
  2285. }
  2286. public setStencilOperationDepthFail(operation: number): void {
  2287. if (this._stencilState.stencilOpDepthFail !== operation) {
  2288. this._stencilState.stencilOpDepthFail = operation;
  2289. }
  2290. }
  2291. public setStencilOperationPass(operation: number): void {
  2292. if (this._stencilState.stencilOpStencilDepthPass !== operation) {
  2293. this._stencilState.stencilOpStencilDepthPass = operation;
  2294. }
  2295. }
  2296. public setDitheringState(value: boolean): void {
  2297. // Does not exist in WebGPU
  2298. }
  2299. public setRasterizerState(value: boolean): void {
  2300. // Does not exist in WebGPU
  2301. }
  2302. public setDepthFunction(depthFunc: number) {
  2303. if (this._depthCullingState.depthFunc !== depthFunc) {
  2304. this._depthCullingState.depthFunc = depthFunc;
  2305. }
  2306. }
  2307. public setDepthFunctionToGreater(): void {
  2308. if (this._depthCullingState.depthFunc !== Constants.GREATER) {
  2309. this._depthCullingState.depthFunc = Constants.GREATER;
  2310. }
  2311. }
  2312. public setDepthFunctionToGreaterOrEqual(): void {
  2313. if (this._depthCullingState.depthFunc !== Constants.GEQUAL) {
  2314. this._depthCullingState.depthFunc = Constants.GEQUAL;
  2315. }
  2316. }
  2317. public setDepthFunctionToLess(): void {
  2318. if (this._depthCullingState.depthFunc !== Constants.LESS) {
  2319. this._depthCullingState.depthFunc = Constants.LESS;
  2320. }
  2321. }
  2322. public setDepthFunctionToLessOrEqual(): void {
  2323. if (this._depthCullingState.depthFunc !== Constants.LEQUAL) {
  2324. this._depthCullingState.depthFunc = Constants.LEQUAL;
  2325. }
  2326. }
  2327. private _indexFormatInRenderPass(topology: GPUPrimitiveTopology): boolean {
  2328. return topology === WebGPUConstants.PrimitiveTopology.PointList ||
  2329. topology === WebGPUConstants.PrimitiveTopology.LineList ||
  2330. topology === WebGPUConstants.PrimitiveTopology.TriangleList;
  2331. }
  2332. private _getTopology(fillMode: number): GPUPrimitiveTopology {
  2333. switch (fillMode) {
  2334. // Triangle views
  2335. case Constants.MATERIAL_TriangleFillMode:
  2336. return WebGPUConstants.PrimitiveTopology.TriangleList;
  2337. case Constants.MATERIAL_PointFillMode:
  2338. return WebGPUConstants.PrimitiveTopology.PointList;
  2339. case Constants.MATERIAL_WireFrameFillMode:
  2340. return WebGPUConstants.PrimitiveTopology.LineList;
  2341. // Draw modes
  2342. case Constants.MATERIAL_PointListDrawMode:
  2343. return WebGPUConstants.PrimitiveTopology.PointList;
  2344. case Constants.MATERIAL_LineListDrawMode:
  2345. return WebGPUConstants.PrimitiveTopology.LineList;
  2346. case Constants.MATERIAL_LineLoopDrawMode:
  2347. // return this._gl.LINE_LOOP;
  2348. // TODO WEBGPU. Line Loop Mode Fallback at buffer load time.
  2349. throw "LineLoop is an unsupported fillmode in WebGPU";
  2350. case Constants.MATERIAL_LineStripDrawMode:
  2351. return WebGPUConstants.PrimitiveTopology.LineStrip;
  2352. case Constants.MATERIAL_TriangleStripDrawMode:
  2353. return WebGPUConstants.PrimitiveTopology.TriangleStrip;
  2354. case Constants.MATERIAL_TriangleFanDrawMode:
  2355. // return this._gl.TRIANGLE_FAN;
  2356. // TODO WEBGPU. Triangle Fan Mode Fallback at buffer load time.
  2357. throw "TriangleFan is an unsupported fillmode in WebGPU";
  2358. default:
  2359. return WebGPUConstants.PrimitiveTopology.TriangleList;
  2360. }
  2361. }
  2362. private _getCompareFunction(compareFunction: Nullable<number>): GPUCompareFunction {
  2363. switch (compareFunction) {
  2364. case Constants.ALWAYS:
  2365. return WebGPUConstants.CompareFunction.Always;
  2366. case Constants.EQUAL:
  2367. return WebGPUConstants.CompareFunction.Equal;
  2368. case Constants.GREATER:
  2369. return WebGPUConstants.CompareFunction.Greater;
  2370. case Constants.GEQUAL:
  2371. return WebGPUConstants.CompareFunction.GreaterEqual;
  2372. case Constants.LESS:
  2373. return WebGPUConstants.CompareFunction.Less;
  2374. case Constants.LEQUAL:
  2375. return WebGPUConstants.CompareFunction.LessEqual;
  2376. case Constants.NEVER:
  2377. return WebGPUConstants.CompareFunction.Never;
  2378. case Constants.NOTEQUAL:
  2379. return WebGPUConstants.CompareFunction.NotEqual;
  2380. default:
  2381. return WebGPUConstants.CompareFunction.Less;
  2382. }
  2383. }
  2384. private _getOpFunction(operation: Nullable<number>, defaultOp: GPUStencilOperation): GPUStencilOperation {
  2385. switch (operation) {
  2386. case Constants.KEEP:
  2387. return WebGPUConstants.StencilOperation.Keep;
  2388. case Constants.ZERO:
  2389. return WebGPUConstants.StencilOperation.Zero;
  2390. case Constants.REPLACE:
  2391. return WebGPUConstants.StencilOperation.Replace;
  2392. case Constants.INVERT:
  2393. return WebGPUConstants.StencilOperation.Invert;
  2394. case Constants.INCR:
  2395. return WebGPUConstants.StencilOperation.IncrementClamp;
  2396. case Constants.DECR:
  2397. return WebGPUConstants.StencilOperation.DecrementClamp;
  2398. case Constants.INCR_WRAP:
  2399. return WebGPUConstants.StencilOperation.IncrementWrap;
  2400. case Constants.DECR_WRAP:
  2401. return WebGPUConstants.StencilOperation.DecrementWrap;
  2402. default:
  2403. return defaultOp;
  2404. }
  2405. }
  2406. private _getDepthStencilStateDescriptor(): GPUDepthStencilStateDescriptor | undefined {
  2407. if (this._depthTextureFormat === undefined) {
  2408. return undefined;
  2409. }
  2410. const stencilFrontBack: GPUStencilStateFaceDescriptor = {
  2411. compare: this._getCompareFunction(this._stencilState.stencilFunc),
  2412. depthFailOp: this._getOpFunction(this._stencilState.stencilOpDepthFail, WebGPUConstants.StencilOperation.Keep),
  2413. failOp: this._getOpFunction(this._stencilState.stencilOpStencilFail, WebGPUConstants.StencilOperation.Keep),
  2414. passOp: this._getOpFunction(this._stencilState.stencilOpStencilDepthPass, WebGPUConstants.StencilOperation.Replace)
  2415. };
  2416. return {
  2417. depthWriteEnabled: this.getDepthWrite(),
  2418. depthCompare: this.getDepthBuffer() ? this._getCompareFunction(this.getDepthFunction()) : WebGPUConstants.CompareFunction.Always,
  2419. format: this._depthTextureFormat,
  2420. stencilFront: stencilFrontBack,
  2421. stencilBack: stencilFrontBack,
  2422. stencilReadMask: this._stencilState.stencilFuncMask,
  2423. stencilWriteMask: this._stencilState.stencilMask,
  2424. };
  2425. }
  2426. /**
  2427. * Set various states to the webGL context
  2428. * @param culling defines backface culling state
  2429. * @param zOffset defines the value to apply to zOffset (0 by default)
  2430. * @param force defines if states must be applied even if cache is up to date
  2431. * @param reverseSide defines if culling must be reversed (CCW instead of CW and CW instead of CCW)
  2432. */
  2433. public setState(culling: boolean, zOffset: number = 0, force?: boolean, reverseSide = false): void {
  2434. // Culling
  2435. if (this._depthCullingState.cull !== culling || force) {
  2436. this._depthCullingState.cull = culling;
  2437. }
  2438. // Cull face
  2439. // var cullFace = this.cullBackFaces ? this._gl.BACK : this._gl.FRONT;
  2440. var cullFace = this.cullBackFaces ? 1 : 2;
  2441. if (this._depthCullingState.cullFace !== cullFace || force) {
  2442. this._depthCullingState.cullFace = cullFace;
  2443. }
  2444. // Z offset
  2445. this.setZOffset(zOffset);
  2446. // Front face
  2447. // var frontFace = reverseSide ? this._gl.CW : this._gl.CCW;
  2448. var frontFace = reverseSide ? 1 : 2;
  2449. if (this._depthCullingState.frontFace !== frontFace || force) {
  2450. this._depthCullingState.frontFace = frontFace;
  2451. }
  2452. }
  2453. private _getFrontFace(): GPUFrontFace {
  2454. switch (this._depthCullingState.frontFace) {
  2455. case 1:
  2456. return WebGPUConstants.FrontFace.CCW;
  2457. default:
  2458. return WebGPUConstants.FrontFace.CW;
  2459. }
  2460. }
  2461. private _getCullMode(): GPUCullMode {
  2462. if (this._depthCullingState.cull === false) {
  2463. return WebGPUConstants.CullMode.None;
  2464. }
  2465. if (this._depthCullingState.cullFace === 2) {
  2466. return WebGPUConstants.CullMode.Front;
  2467. }
  2468. else {
  2469. return WebGPUConstants.CullMode.Back;
  2470. }
  2471. }
  2472. private _getRasterizationStateDescriptor(): GPURasterizationStateDescriptor {
  2473. return {
  2474. frontFace: this._getFrontFace(),
  2475. cullMode: this._getCullMode(),
  2476. depthBias: 0,
  2477. depthBiasClamp: 0,
  2478. depthBiasSlopeScale: this._depthCullingState.zOffset,
  2479. };
  2480. }
  2481. private _getWriteMask(): number {
  2482. if (this.__colorWrite) {
  2483. return WebGPUConstants.ColorWrite.All;
  2484. }
  2485. return 0;
  2486. }
  2487. /**
  2488. * Sets the current alpha mode
  2489. * @param mode defines the mode to use (one of the Engine.ALPHA_XXX)
  2490. * @param noDepthWriteChange defines if depth writing state should remains unchanged (false by default)
  2491. * @see http://doc.babylonjs.com/resources/transparency_and_how_meshes_are_rendered
  2492. */
  2493. public setAlphaMode(mode: number, noDepthWriteChange: boolean = false): void {
  2494. if (this._alphaMode === mode) {
  2495. return;
  2496. }
  2497. switch (mode) {
  2498. case Engine.ALPHA_DISABLE:
  2499. this._alphaState.alphaBlend = false;
  2500. break;
  2501. case Engine.ALPHA_PREMULTIPLIED:
  2502. // this._alphaState.setAlphaBlendFunctionParameters(this._gl.ONE, this._gl.ONE_MINUS_SRC_ALPHA, this._gl.ONE, this._gl.ONE);
  2503. this._alphaState.setAlphaBlendFunctionParameters(1, 0x0303, 1, 1);
  2504. this._alphaState.alphaBlend = true;
  2505. break;
  2506. case Engine.ALPHA_PREMULTIPLIED_PORTERDUFF:
  2507. // this._alphaState.setAlphaBlendFunctionParameters(this._gl.ONE, this._gl.ONE_MINUS_SRC_ALPHA, this._gl.ONE, this._gl.ONE_MINUS_SRC_ALPHA);
  2508. this._alphaState.setAlphaBlendFunctionParameters(1, 0x0303, 1, 0x0303);
  2509. this._alphaState.alphaBlend = true;
  2510. break;
  2511. case Engine.ALPHA_COMBINE:
  2512. // this._alphaState.setAlphaBlendFunctionParameters(this._gl.SRC_ALPHA, this._gl.ONE_MINUS_SRC_ALPHA, this._gl.ONE, this._gl.ONE);
  2513. this._alphaState.setAlphaBlendFunctionParameters(0x0302, 0x0303, 1, 1);
  2514. this._alphaState.alphaBlend = true;
  2515. break;
  2516. case Engine.ALPHA_ONEONE:
  2517. // this._alphaState.setAlphaBlendFunctionParameters(this._gl.ONE, this._gl.ONE, this._gl.ZERO, this._gl.ONE);
  2518. this._alphaState.setAlphaBlendFunctionParameters(1, 1, 0, 1);
  2519. this._alphaState.alphaBlend = true;
  2520. break;
  2521. case Engine.ALPHA_ADD:
  2522. // this._alphaState.setAlphaBlendFunctionParameters(this._gl.SRC_ALPHA, this._gl.ONE, this._gl.ZERO, this._gl.ONE);
  2523. this._alphaState.setAlphaBlendFunctionParameters(0x0302, 1, 0, 1);
  2524. this._alphaState.alphaBlend = true;
  2525. break;
  2526. case Engine.ALPHA_SUBTRACT:
  2527. // this._alphaState.setAlphaBlendFunctionParameters(this._gl.ZERO, this._gl.ONE_MINUS_SRC_COLOR, this._gl.ONE, this._gl.ONE);
  2528. this._alphaState.setAlphaBlendFunctionParameters(0, 0x0301, 1, 1);
  2529. this._alphaState.alphaBlend = true;
  2530. break;
  2531. case Engine.ALPHA_MULTIPLY:
  2532. // this._alphaState.setAlphaBlendFunctionParameters(this._gl.DST_COLOR, this._gl.ZERO, this._gl.ONE, this._gl.ONE);
  2533. this._alphaState.setAlphaBlendFunctionParameters(0x0306, 0, 1, 1);
  2534. this._alphaState.alphaBlend = true;
  2535. break;
  2536. case Engine.ALPHA_MAXIMIZED:
  2537. // this._alphaState.setAlphaBlendFunctionParameters(this._gl.SRC_ALPHA, this._gl.ONE_MINUS_SRC_COLOR, this._gl.ONE, this._gl.ONE);
  2538. this._alphaState.setAlphaBlendFunctionParameters(0x0302, 0x0301, 1, 1);
  2539. this._alphaState.alphaBlend = true;
  2540. break;
  2541. case Engine.ALPHA_INTERPOLATE:
  2542. // this._alphaState.setAlphaBlendFunctionParameters(this._gl.CONSTANT_COLOR, this._gl.ONE_MINUS_CONSTANT_COLOR, this._gl.CONSTANT_ALPHA, this._gl.ONE_MINUS_CONSTANT_ALPHA);
  2543. this._alphaState.setAlphaBlendFunctionParameters(0x8001, 0x8002, 0x8003, 0x8004);
  2544. this._alphaState.alphaBlend = true;
  2545. break;
  2546. case Engine.ALPHA_SCREENMODE:
  2547. // this._alphaState.setAlphaBlendFunctionParameters(this._gl.ONE, this._gl.ONE_MINUS_SRC_COLOR, this._gl.ONE, this._gl.ONE_MINUS_SRC_ALPHA);
  2548. this._alphaState.setAlphaBlendFunctionParameters(1, 0x0301, 1, 0x0303);
  2549. this._alphaState.alphaBlend = true;
  2550. break;
  2551. }
  2552. if (!noDepthWriteChange) {
  2553. this.setDepthWrite(mode === Engine.ALPHA_DISABLE);
  2554. }
  2555. this._alphaMode = mode;
  2556. }
  2557. private _getAphaBlendOperation(operation: Nullable<number>): GPUBlendOperation {
  2558. switch (operation) {
  2559. case 0x8006:
  2560. return WebGPUConstants.BlendOperation.Add;
  2561. case 0x800A:
  2562. return WebGPUConstants.BlendOperation.Subtract;
  2563. case 0x800B:
  2564. return WebGPUConstants.BlendOperation.ReverseSubtract;
  2565. default:
  2566. return WebGPUConstants.BlendOperation.Add;
  2567. }
  2568. }
  2569. private _getAphaBlendFactor(factor: Nullable<number>): GPUBlendFactor {
  2570. switch (factor) {
  2571. case 0:
  2572. return WebGPUConstants.BlendFactor.Zero;
  2573. case 1:
  2574. return WebGPUConstants.BlendFactor.One;
  2575. case 0x0300:
  2576. return WebGPUConstants.BlendFactor.SrcColor;
  2577. case 0x0301:
  2578. return WebGPUConstants.BlendFactor.OneMinusSrcColor;
  2579. case 0x0302:
  2580. return WebGPUConstants.BlendFactor.SrcAlpha;
  2581. case 0x0303:
  2582. return WebGPUConstants.BlendFactor.OneMinusSrcAlpha;
  2583. case 0x0304:
  2584. return WebGPUConstants.BlendFactor.DstAlpha;
  2585. case 0x0305:
  2586. return WebGPUConstants.BlendFactor.OneMinusDstAlpha;
  2587. case 0x0306:
  2588. return WebGPUConstants.BlendFactor.DstColor;
  2589. case 0x0307:
  2590. return WebGPUConstants.BlendFactor.OneMinusDstColor;
  2591. case 0x0308:
  2592. return WebGPUConstants.BlendFactor.SrcAlphaSaturated;
  2593. case 0x8001:
  2594. return WebGPUConstants.BlendFactor.BlendColor;
  2595. case 0x8002:
  2596. return WebGPUConstants.BlendFactor.OneMinusBlendColor;
  2597. case 0x8003:
  2598. return WebGPUConstants.BlendFactor.BlendColor;
  2599. case 0x8004:
  2600. return WebGPUConstants.BlendFactor.OneMinusBlendColor;
  2601. default:
  2602. return WebGPUConstants.BlendFactor.One;
  2603. }
  2604. }
  2605. private _getAphaBlendState(): GPUBlendDescriptor {
  2606. if (!this._alphaState.alphaBlend) {
  2607. return { };
  2608. }
  2609. return {
  2610. srcFactor: this._getAphaBlendFactor(this._alphaState._blendFunctionParameters[2]),
  2611. dstFactor: this._getAphaBlendFactor(this._alphaState._blendFunctionParameters[3]),
  2612. operation: this._getAphaBlendOperation(this._alphaState._blendEquationParameters[1]),
  2613. };
  2614. }
  2615. private _getColorBlendState(): GPUBlendDescriptor {
  2616. if (!this._alphaState.alphaBlend) {
  2617. return { };
  2618. }
  2619. return {
  2620. srcFactor: this._getAphaBlendFactor(this._alphaState._blendFunctionParameters[0]),
  2621. dstFactor: this._getAphaBlendFactor(this._alphaState._blendFunctionParameters[1]),
  2622. operation: this._getAphaBlendOperation(this._alphaState._blendEquationParameters[0]),
  2623. };
  2624. }
  2625. private _getColorStateDescriptors(): GPUColorStateDescriptor[] {
  2626. return [{
  2627. format: this._colorFormat,
  2628. alphaBlend: this._getAphaBlendState(),
  2629. colorBlend: this._getColorBlendState(),
  2630. writeMask: this._getWriteMask(),
  2631. }];
  2632. }
  2633. private _getStages(): IWebGPURenderPipelineStageDescriptor {
  2634. const webgpuPipelineContext = this._currentEffect!._pipelineContext as WebGPUPipelineContext;
  2635. return webgpuPipelineContext.stages!;
  2636. }
  2637. private _getVertexInputDescriptorFormat(vertexBuffer: VertexBuffer): GPUVertexFormat {
  2638. const kind = vertexBuffer.getKind();
  2639. const type = vertexBuffer.type;
  2640. const normalized = vertexBuffer.normalized;
  2641. const size = vertexBuffer.getSize();
  2642. switch (type) {
  2643. case VertexBuffer.BYTE:
  2644. switch (size) {
  2645. case 2:
  2646. return normalized ? WebGPUConstants.VertexFormat.Char2Norm : WebGPUConstants.VertexFormat.Char2;
  2647. case 4:
  2648. return normalized ? WebGPUConstants.VertexFormat.Char4Norm : WebGPUConstants.VertexFormat.Char4;
  2649. }
  2650. case VertexBuffer.UNSIGNED_BYTE:
  2651. switch (size) {
  2652. case 2:
  2653. return normalized ? WebGPUConstants.VertexFormat.Uchar2Norm : WebGPUConstants.VertexFormat.Uchar2;
  2654. case 4:
  2655. return normalized ? WebGPUConstants.VertexFormat.Uchar4Norm : WebGPUConstants.VertexFormat.Uchar4;
  2656. }
  2657. case VertexBuffer.SHORT:
  2658. switch (size) {
  2659. case 2:
  2660. return normalized ? WebGPUConstants.VertexFormat.Short2Norm : WebGPUConstants.VertexFormat.Short2;
  2661. case 4:
  2662. return normalized ? WebGPUConstants.VertexFormat.Short4Norm : WebGPUConstants.VertexFormat.Short4;
  2663. }
  2664. case VertexBuffer.UNSIGNED_SHORT:
  2665. switch (size) {
  2666. case 2:
  2667. return normalized ? WebGPUConstants.VertexFormat.Ushort2Norm : WebGPUConstants.VertexFormat.Ushort2;
  2668. case 4:
  2669. return normalized ? WebGPUConstants.VertexFormat.Ushort4Norm : WebGPUConstants.VertexFormat.Ushort4;
  2670. }
  2671. case VertexBuffer.INT:
  2672. switch (size) {
  2673. case 1:
  2674. return WebGPUConstants.VertexFormat.Int;
  2675. case 2:
  2676. return WebGPUConstants.VertexFormat.Int2;
  2677. case 3:
  2678. return WebGPUConstants.VertexFormat.Int3;
  2679. case 4:
  2680. return WebGPUConstants.VertexFormat.Int4;
  2681. }
  2682. case VertexBuffer.UNSIGNED_INT:
  2683. switch (size) {
  2684. case 1:
  2685. return WebGPUConstants.VertexFormat.Uint;
  2686. case 2:
  2687. return WebGPUConstants.VertexFormat.Uint2;
  2688. case 3:
  2689. return WebGPUConstants.VertexFormat.Uint3;
  2690. case 4:
  2691. return WebGPUConstants.VertexFormat.Uint4;
  2692. }
  2693. case VertexBuffer.FLOAT:
  2694. switch (size) {
  2695. case 1:
  2696. return WebGPUConstants.VertexFormat.Float;
  2697. case 2:
  2698. return WebGPUConstants.VertexFormat.Float2;
  2699. case 3:
  2700. return WebGPUConstants.VertexFormat.Float3;
  2701. case 4:
  2702. return WebGPUConstants.VertexFormat.Float4;
  2703. }
  2704. }
  2705. throw new Error("Invalid Format '" + kind + "'");
  2706. }
  2707. private _getVertexInputDescriptor(topology: GPUPrimitiveTopology): GPUVertexStateDescriptor {
  2708. const descriptors: GPUVertexBufferLayoutDescriptor[] = [];
  2709. const effect = this._currentEffect!;
  2710. const attributes = effect.getAttributesNames();
  2711. for (var index = 0; index < attributes.length; index++) {
  2712. const location = effect.getAttributeLocation(index);
  2713. if (location >= 0) {
  2714. const vertexBuffer = this._currentVertexBuffers![attributes[index]];
  2715. if (!vertexBuffer) {
  2716. continue;
  2717. }
  2718. const positionAttributeDescriptor: GPUVertexAttributeDescriptor = {
  2719. shaderLocation: location,
  2720. offset: 0, // not available in WebGL
  2721. format: this._getVertexInputDescriptorFormat(vertexBuffer),
  2722. };
  2723. // TODO WEBGPU. Factorize the one with the same underlying buffer.
  2724. const vertexBufferDescriptor: GPUVertexBufferLayoutDescriptor = {
  2725. arrayStride: vertexBuffer.byteStride,
  2726. stepMode: vertexBuffer.getIsInstanced() ? WebGPUConstants.InputStepMode.Instance : WebGPUConstants.InputStepMode.Vertex,
  2727. attributes: [positionAttributeDescriptor]
  2728. };
  2729. descriptors.push(vertexBufferDescriptor);
  2730. }
  2731. }
  2732. if (!this._currentIndexBuffer) {
  2733. return {
  2734. indexFormat: !this._indexFormatInRenderPass(topology) ? WebGPUConstants.IndexFormat.Uint32 : undefined,
  2735. vertexBuffers: descriptors
  2736. };
  2737. }
  2738. const inputStateDescriptor: GPUVertexStateDescriptor = {
  2739. vertexBuffers: descriptors
  2740. };
  2741. if (!this._indexFormatInRenderPass(topology)) {
  2742. inputStateDescriptor.indexFormat = this._currentIndexBuffer!.is32Bits ? WebGPUConstants.IndexFormat.Uint32 : WebGPUConstants.IndexFormat.Uint16;
  2743. }
  2744. return inputStateDescriptor;
  2745. }
  2746. private _getPipelineLayout(): GPUPipelineLayout {
  2747. const bindGroupLayouts: GPUBindGroupLayout[] = [];
  2748. const webgpuPipelineContext = this._currentEffect!._pipelineContext as WebGPUPipelineContext;
  2749. for (let i = 0; i < webgpuPipelineContext.orderedUBOsAndSamplers.length; i++) {
  2750. const setDefinition = webgpuPipelineContext.orderedUBOsAndSamplers[i];
  2751. if (setDefinition === undefined) {
  2752. const entries: GPUBindGroupLayoutEntry[] = [];
  2753. const uniformsBindGroupLayout = this._device.createBindGroupLayout({
  2754. entries,
  2755. });
  2756. bindGroupLayouts[i] = uniformsBindGroupLayout;
  2757. continue;
  2758. }
  2759. const entries: GPUBindGroupLayoutEntry[] = [];
  2760. for (let j = 0; j < setDefinition.length; j++) {
  2761. const bindingDefinition = webgpuPipelineContext.orderedUBOsAndSamplers[i][j];
  2762. if (bindingDefinition === undefined) {
  2763. continue;
  2764. }
  2765. // TODO WEBGPU. Optimize shared samplers visibility for vertex/framgent.
  2766. if (bindingDefinition.isSampler) {
  2767. entries.push({
  2768. binding: j,
  2769. visibility: WebGPUConstants.ShaderStage.Vertex | WebGPUConstants.ShaderStage.Fragment,
  2770. type: WebGPUConstants.BindingType.SampledTexture,
  2771. viewDimension: bindingDefinition.textureDimension,
  2772. // TODO WEBGPU. Handle texture component type properly.
  2773. // textureComponentType?: GPUTextureComponentType,
  2774. // multisampled?: boolean;
  2775. // hasDynamicOffset?: boolean;
  2776. // storageTextureFormat?: GPUTextureFormat;
  2777. // minBufferBindingSize?: number;
  2778. }, {
  2779. // TODO WEBGPU. No Magic + 1 (coming from current 1 texture 1 sampler startegy).
  2780. binding: j + 1,
  2781. visibility: WebGPUConstants.ShaderStage.Vertex | WebGPUConstants.ShaderStage.Fragment,
  2782. type: bindingDefinition.isComparisonSampler ? WebGPUConstants.BindingType.ComparisonSampler : WebGPUConstants.BindingType.Sampler
  2783. });
  2784. }
  2785. else {
  2786. entries.push({
  2787. binding: j,
  2788. visibility: WebGPUConstants.ShaderStage.Vertex | WebGPUConstants.ShaderStage.Fragment,
  2789. type: WebGPUConstants.BindingType.UniformBuffer,
  2790. });
  2791. }
  2792. }
  2793. if (entries.length > 0) {
  2794. const uniformsBindGroupLayout = this._device.createBindGroupLayout({
  2795. entries,
  2796. });
  2797. bindGroupLayouts[i] = uniformsBindGroupLayout;
  2798. }
  2799. }
  2800. webgpuPipelineContext.bindGroupLayouts = bindGroupLayouts;
  2801. return this._device.createPipelineLayout({ bindGroupLayouts });
  2802. }
  2803. private _getRenderPipeline(topology: GPUPrimitiveTopology): GPURenderPipeline {
  2804. // This is wrong to cache this way but workarounds the need of cache in the simple demo context.
  2805. const webgpuPipelineContext = this._currentEffect!._pipelineContext as WebGPUPipelineContext;
  2806. /*if (webgpuPipelineContext.renderPipeline) {
  2807. return webgpuPipelineContext.renderPipeline;
  2808. }*/
  2809. this._counters.numPipelineDescriptorCreation++;
  2810. // Unsupported at the moment but needs to be extracted from the MSAA param.
  2811. const rasterizationStateDescriptor = this._getRasterizationStateDescriptor();
  2812. const depthStateDescriptor = this._getDepthStencilStateDescriptor();
  2813. const colorStateDescriptors = this._getColorStateDescriptors();
  2814. const stages = this._getStages();
  2815. const inputStateDescriptor = this._getVertexInputDescriptor(topology);
  2816. const pipelineLayout = this._getPipelineLayout();
  2817. webgpuPipelineContext.renderPipeline = this._device.createRenderPipeline({
  2818. sampleCount: this._currentRenderTarget ? this._currentRenderTarget.samples : this._mainPassSampleCount,
  2819. primitiveTopology: topology,
  2820. rasterizationState: rasterizationStateDescriptor,
  2821. depthStencilState: depthStateDescriptor,
  2822. colorStates: colorStateDescriptors,
  2823. ...stages,
  2824. vertexState: inputStateDescriptor,
  2825. layout: pipelineLayout,
  2826. });
  2827. return webgpuPipelineContext.renderPipeline;
  2828. }
  2829. private _getVertexInputsToRender(): IWebGPUPipelineContextVertexInputsCache {
  2830. const effect = this._currentEffect!;
  2831. const webgpuPipelineContext = this._currentEffect!._pipelineContext as WebGPUPipelineContext;
  2832. let vertexInputs = webgpuPipelineContext.vertexInputs;
  2833. /*!!if (vertexInputs) {
  2834. return vertexInputs;
  2835. }*/
  2836. vertexInputs = {
  2837. indexBuffer: null,
  2838. indexOffset: 0,
  2839. vertexStartSlot: 0,
  2840. vertexBuffers: [],
  2841. vertexOffsets: [],
  2842. };
  2843. webgpuPipelineContext.vertexInputs = vertexInputs;
  2844. if (this._currentIndexBuffer) {
  2845. // TODO WEBGPU. Check if cache would be worth it.
  2846. vertexInputs.indexBuffer = this._currentIndexBuffer.underlyingResource;
  2847. vertexInputs.indexOffset = 0;
  2848. }
  2849. else {
  2850. vertexInputs.indexBuffer = null;
  2851. }
  2852. const attributes = effect.getAttributesNames();
  2853. for (var index = 0; index < attributes.length; index++) {
  2854. const order = effect.getAttributeLocation(index);
  2855. if (order >= 0) {
  2856. const vertexBuffer = this._currentVertexBuffers![attributes[index]];
  2857. if (!vertexBuffer) {
  2858. continue;
  2859. }
  2860. var buffer = vertexBuffer.getBuffer();
  2861. if (buffer) {
  2862. vertexInputs.vertexBuffers.push(buffer.underlyingResource);
  2863. vertexInputs.vertexOffsets.push(vertexBuffer.byteOffset);
  2864. }
  2865. }
  2866. }
  2867. // TODO WEBGPU. Optimize buffer reusability and types as more are now allowed.
  2868. return vertexInputs;
  2869. }
  2870. private _getBindGroupsToRender(): GPUBindGroup[] {
  2871. const webgpuPipelineContext = this._currentEffect!._pipelineContext as WebGPUPipelineContext;
  2872. let bindGroups = webgpuPipelineContext.bindGroups;
  2873. /*if (bindGroups) {
  2874. if (webgpuPipelineContext.uniformBuffer) {
  2875. webgpuPipelineContext.uniformBuffer.update();
  2876. }
  2877. return bindGroups;
  2878. }*/
  2879. this._counters.numBindGroupsCreation++;
  2880. if (webgpuPipelineContext.uniformBuffer) {
  2881. this.bindUniformBufferBase(webgpuPipelineContext.uniformBuffer.getBuffer()!, 0, "LeftOver");
  2882. webgpuPipelineContext.uniformBuffer.update();
  2883. }
  2884. bindGroups = [];
  2885. webgpuPipelineContext.bindGroups = bindGroups;
  2886. const bindGroupLayouts = webgpuPipelineContext.bindGroupLayouts;
  2887. for (let i = 0; i < webgpuPipelineContext.orderedUBOsAndSamplers.length; i++) {
  2888. const setDefinition = webgpuPipelineContext.orderedUBOsAndSamplers[i];
  2889. if (setDefinition === undefined) {
  2890. let groupLayout: GPUBindGroupLayout;
  2891. if (bindGroupLayouts && bindGroupLayouts[i]) {
  2892. groupLayout = bindGroupLayouts[i];
  2893. }
  2894. else {
  2895. groupLayout = webgpuPipelineContext.renderPipeline.getBindGroupLayout(i);
  2896. }
  2897. bindGroups[i] = this._device.createBindGroup({
  2898. layout: groupLayout,
  2899. entries: [],
  2900. });
  2901. continue;
  2902. }
  2903. const entries: GPUBindGroupEntry[] = [];
  2904. for (let j = 0; j < setDefinition.length; j++) {
  2905. const bindingDefinition = webgpuPipelineContext.orderedUBOsAndSamplers[i][j];
  2906. if (bindingDefinition === undefined) {
  2907. continue;
  2908. }
  2909. // TODO WEBGPU. Authorize shared samplers and Vertex Textures.
  2910. if (bindingDefinition.isSampler) {
  2911. const bindingInfo = webgpuPipelineContext.samplers[bindingDefinition.name];
  2912. if (bindingInfo) {
  2913. if (dbgSanityChecks && bindingInfo.texture === null) {
  2914. console.error("Trying to bind a null texture! bindingDefinition=", bindingDefinition, " | bindingInfo=", bindingInfo);
  2915. debugger;
  2916. }
  2917. const hardwareTexture = bindingInfo.texture._hardwareTexture as WebGPUHardwareTexture;
  2918. if (!hardwareTexture.sampler) {
  2919. const samplerDescriptor: GPUSamplerDescriptor = this._getSamplerDescriptor(bindingInfo.texture!);
  2920. const gpuSampler = this._device.createSampler(samplerDescriptor);
  2921. hardwareTexture.sampler = gpuSampler;
  2922. }
  2923. if (dbgSanityChecks && !hardwareTexture.view) {
  2924. console.error("Trying to bind a null gpu texture! bindingDefinition=", bindingDefinition, " | bindingInfo=", bindingInfo, " | isReady=", bindingInfo.texture.isReady);
  2925. debugger;
  2926. }
  2927. // TODO WEBGPU remove this code
  2928. if ((bindingInfo.texture as any)._released) {
  2929. console.error("Trying to bind a released texture!", bindingInfo.texture, bindingInfo);
  2930. debugger;
  2931. }
  2932. entries.push({
  2933. binding: bindingInfo.textureBinding,
  2934. resource: hardwareTexture.view!,
  2935. }, {
  2936. binding: bindingInfo.samplerBinding,
  2937. resource: hardwareTexture.sampler!,
  2938. });
  2939. }
  2940. else {
  2941. Logger.Error("Sampler has not been bound: " + bindingDefinition.name);
  2942. }
  2943. }
  2944. else {
  2945. const dataBuffer = this._uniformsBuffers[bindingDefinition.name];
  2946. if (dataBuffer) {
  2947. const webgpuBuffer = dataBuffer.underlyingResource as GPUBuffer;
  2948. entries.push({
  2949. binding: j,
  2950. resource: {
  2951. buffer: webgpuBuffer,
  2952. offset: 0,
  2953. size: dataBuffer.capacity,
  2954. },
  2955. });
  2956. }
  2957. else {
  2958. Logger.Error("UBO has not been bound: " + bindingDefinition.name);
  2959. }
  2960. }
  2961. }
  2962. if (entries.length > 0) {
  2963. let groupLayout: GPUBindGroupLayout;
  2964. if (bindGroupLayouts && bindGroupLayouts[i]) {
  2965. groupLayout = bindGroupLayouts[i];
  2966. }
  2967. else {
  2968. groupLayout = webgpuPipelineContext.renderPipeline.getBindGroupLayout(i);
  2969. }
  2970. bindGroups[i] = this._device.createBindGroup({
  2971. layout: groupLayout,
  2972. entries,
  2973. });
  2974. }
  2975. }
  2976. return bindGroups;
  2977. }
  2978. private _bindVertexInputs(vertexInputs: IWebGPUPipelineContextVertexInputsCache, setIndexFormat: boolean): void {
  2979. const renderPass = this._bundleEncoder || this._getCurrentRenderPass();
  2980. if (vertexInputs.indexBuffer) {
  2981. // TODO WEBGPU. Check if cache would be worth it.
  2982. if (setIndexFormat) {
  2983. renderPass.setIndexBuffer(vertexInputs.indexBuffer, this._currentIndexBuffer!.is32Bits ? WebGPUConstants.IndexFormat.Uint32 : WebGPUConstants.IndexFormat.Uint16, vertexInputs.indexOffset);
  2984. } else {
  2985. renderPass.setIndexBuffer(vertexInputs.indexBuffer, vertexInputs.indexOffset);
  2986. }
  2987. }
  2988. // TODO WEBGPU. Optimize buffer reusability and types as more are now allowed.
  2989. for (let i = 0; i < vertexInputs.vertexBuffers.length; i++) {
  2990. const buf = vertexInputs.vertexBuffers[i];
  2991. if (buf) {
  2992. renderPass.setVertexBuffer(vertexInputs.vertexStartSlot + i, buf, vertexInputs.vertexOffsets[i]);
  2993. }
  2994. }
  2995. }
  2996. private _setRenderBindGroups(bindGroups: GPUBindGroup[]): void {
  2997. // TODO WEBGPU. Only set groups if changes happened.
  2998. const renderPass = this._bundleEncoder || this._getCurrentRenderPass();
  2999. for (let i = 0; i < bindGroups.length; i++) {
  3000. renderPass.setBindGroup(i, bindGroups[i]);
  3001. }
  3002. }
  3003. private _setRenderPipeline(fillMode: number): void {
  3004. const renderPass = this._bundleEncoder || this._getCurrentRenderPass();
  3005. const topology = this._getTopology(fillMode);
  3006. const setIndexFormatInRenderPass = this._indexFormatInRenderPass(topology);
  3007. const pipeline = this._getRenderPipeline(topology);
  3008. renderPass.setPipeline(pipeline);
  3009. const vertexInputs = this._getVertexInputsToRender();
  3010. this._bindVertexInputs(vertexInputs, setIndexFormatInRenderPass);
  3011. const bindGroups = this._getBindGroupsToRender();
  3012. this._setRenderBindGroups(bindGroups);
  3013. if (this._stencilState.stencilTest) {
  3014. this._getCurrentRenderPass().setStencilReference(this._stencilState.stencilFuncRef);
  3015. }
  3016. if (this._alphaState.alphaBlend/* && this._alphaState._isBlendConstantsDirty*/) {
  3017. // TODO WebGPU. should use renderPass.
  3018. this._getCurrentRenderPass().setBlendColor(this._alphaState._blendConstants as any);
  3019. }
  3020. }
  3021. public drawElementsType(fillMode: number, indexStart: number, indexCount: number, instancesCount: number = 1): void {
  3022. const renderPass = this._bundleEncoder || this._getCurrentRenderPass();
  3023. this._setRenderPipeline(fillMode);
  3024. renderPass.drawIndexed(indexCount, instancesCount || 1, indexStart, 0, 0);
  3025. }
  3026. public drawArraysType(fillMode: number, verticesStart: number, verticesCount: number, instancesCount: number = 1): void {
  3027. const renderPass = this._bundleEncoder || this._getCurrentRenderPass();
  3028. this._currentIndexBuffer = null;
  3029. this._setRenderPipeline(fillMode);
  3030. renderPass.draw(verticesCount, instancesCount || 1, verticesStart, 0);
  3031. }
  3032. /**
  3033. * Force a specific size of the canvas
  3034. * @param width defines the new canvas' width
  3035. * @param height defines the new canvas' height
  3036. * @returns true if the size was changed
  3037. */
  3038. public setSize(width: number, height: number): boolean {
  3039. if (!super.setSize(width, height)) {
  3040. return false;
  3041. }
  3042. if (dbgVerboseLogsForFirstFrames) {
  3043. if (!(this as any)._count || (this as any)._count < dbgVerboseLogsNumFrames) {
  3044. console.log("setSize called frame #" + (this as any)._count, width, height);
  3045. }
  3046. }
  3047. this._initializeMainAttachments();
  3048. return true;
  3049. }
  3050. //------------------------------------------------------------------------------
  3051. // Render Bundle
  3052. //------------------------------------------------------------------------------
  3053. private _bundleEncoder: Nullable<GPURenderBundleEncoder>;
  3054. /**
  3055. * Start recording all the gpu calls into a bundle.
  3056. */
  3057. public startRecordBundle(): void {
  3058. // TODO. WebGPU. options should be dynamic.
  3059. this._bundleEncoder = this._device.createRenderBundleEncoder({
  3060. colorFormats: [ WebGPUConstants.TextureFormat.BGRA8Unorm ],
  3061. depthStencilFormat: WebGPUConstants.TextureFormat.Depth24PlusStencil8,
  3062. sampleCount: this._mainPassSampleCount,
  3063. });
  3064. }
  3065. /**
  3066. * Stops recording the bundle.
  3067. * @returns the recorded bundle
  3068. */
  3069. public stopRecordBundle(): GPURenderBundle {
  3070. const bundle = this._bundleEncoder!.finish();
  3071. this._bundleEncoder = null;
  3072. return bundle;
  3073. }
  3074. /**
  3075. * Execute the previously recorded bundle.
  3076. * @param bundles defines the bundle to replay
  3077. */
  3078. public executeBundles(bundles: GPURenderBundle[]): void {
  3079. const renderPass = this._getCurrentRenderPass();
  3080. renderPass.executeBundles(bundles);
  3081. }
  3082. //------------------------------------------------------------------------------
  3083. // Dispose
  3084. //------------------------------------------------------------------------------
  3085. /**
  3086. * Dispose and release all associated resources
  3087. */
  3088. public dispose(): void {
  3089. this._compiledShaders = { };
  3090. if (this._mainTexture) {
  3091. this._mainTexture.destroy();
  3092. }
  3093. if (this._depthTexture) {
  3094. this._depthTexture.destroy();
  3095. }
  3096. super.dispose();
  3097. }
  3098. //------------------------------------------------------------------------------
  3099. // Misc
  3100. //------------------------------------------------------------------------------
  3101. public getRenderWidth(useScreen = false): number {
  3102. if (!useScreen && this._currentRenderTarget) {
  3103. return this._currentRenderTarget.width;
  3104. }
  3105. return this._canvas.width;
  3106. }
  3107. public getRenderHeight(useScreen = false): number {
  3108. if (!useScreen && this._currentRenderTarget) {
  3109. return this._currentRenderTarget.height;
  3110. }
  3111. return this._canvas.height;
  3112. }
  3113. public getRenderingCanvas(): Nullable<HTMLCanvasElement> {
  3114. return this._canvas;
  3115. }
  3116. //------------------------------------------------------------------------------
  3117. // Errors
  3118. //------------------------------------------------------------------------------
  3119. public getError(): number {
  3120. // TODO WEBGPU. from the webgpu errors.
  3121. return 0;
  3122. }
  3123. //------------------------------------------------------------------------------
  3124. // Unused WebGPU
  3125. //------------------------------------------------------------------------------
  3126. public areAllEffectsReady(): boolean {
  3127. // No parallel shader compilation.
  3128. return true;
  3129. }
  3130. public _executeWhenRenderingStateIsCompiled(pipelineContext: IPipelineContext, action: () => void) {
  3131. // No parallel shader compilation.
  3132. // No Async, so direct launch
  3133. action();
  3134. }
  3135. public _isRenderingStateCompiled(pipelineContext: IPipelineContext): boolean {
  3136. // No parallel shader compilation.
  3137. return true;
  3138. }
  3139. public _getUnpackAlignement(): number {
  3140. return 1;
  3141. }
  3142. public _unpackFlipY(value: boolean) { }
  3143. // TODO WEBGPU. All of the below should go once engine split with baseEngine.
  3144. /** @hidden */
  3145. public _getSamplingParameters(samplingMode: number, generateMipMaps: boolean): { min: number; mag: number } {
  3146. throw "_getSamplingParameters is not available in WebGPU";
  3147. }
  3148. public bindUniformBlock(pipelineContext: IPipelineContext, blockName: string, index: number): void {
  3149. }
  3150. public getUniforms(pipelineContext: IPipelineContext, uniformsNames: string[]): Nullable<WebGLUniformLocation>[] {
  3151. return [];
  3152. }
  3153. public setIntArray(uniform: WebGLUniformLocation, array: Int32Array): boolean {
  3154. return false;
  3155. }
  3156. public setIntArray2(uniform: WebGLUniformLocation, array: Int32Array): boolean {
  3157. return false;
  3158. }
  3159. public setIntArray3(uniform: WebGLUniformLocation, array: Int32Array): boolean {
  3160. return false;
  3161. }
  3162. public setIntArray4(uniform: WebGLUniformLocation, array: Int32Array): boolean {
  3163. return false;
  3164. }
  3165. public setArray(uniform: WebGLUniformLocation, array: number[]): boolean {
  3166. return false;
  3167. }
  3168. public setArray2(uniform: WebGLUniformLocation, array: number[]): boolean {
  3169. return false;
  3170. }
  3171. public setArray3(uniform: WebGLUniformLocation, array: number[]): boolean {
  3172. return false;
  3173. }
  3174. public setArray4(uniform: WebGLUniformLocation, array: number[]): boolean {
  3175. return false;
  3176. }
  3177. public setMatrices(uniform: WebGLUniformLocation, matrices: Float32Array): boolean {
  3178. return false;
  3179. }
  3180. public setMatrix3x3(uniform: WebGLUniformLocation, matrix: Float32Array): boolean {
  3181. return false;
  3182. }
  3183. public setMatrix2x2(uniform: WebGLUniformLocation, matrix: Float32Array): boolean {
  3184. return false;
  3185. }
  3186. public setFloat(uniform: WebGLUniformLocation, value: number): boolean {
  3187. return false;
  3188. }
  3189. public setFloat2(uniform: WebGLUniformLocation, x: number, y: number): boolean {
  3190. return false;
  3191. }
  3192. public setFloat3(uniform: WebGLUniformLocation, x: number, y: number, z: number): boolean {
  3193. return false;
  3194. }
  3195. public setFloat4(uniform: WebGLUniformLocation, x: number, y: number, z: number, w: number): boolean {
  3196. return false;
  3197. }
  3198. }
  3199. /** @hidden */
  3200. function _convertRGBtoRGBATextureData(rgbData: any, width: number, height: number, textureType: number): ArrayBufferView {
  3201. // Create new RGBA data container.
  3202. var rgbaData: any;
  3203. if (textureType === Constants.TEXTURETYPE_FLOAT) {
  3204. rgbaData = new Float32Array(width * height * 4);
  3205. }
  3206. else {
  3207. rgbaData = new Uint32Array(width * height * 4);
  3208. }
  3209. // Convert each pixel.
  3210. for (let x = 0; x < width; x++) {
  3211. for (let y = 0; y < height; y++) {
  3212. let index = (y * width + x) * 3;
  3213. let newIndex = (y * width + x) * 4;
  3214. // Map Old Value to new value.
  3215. rgbaData[newIndex + 0] = rgbData[index + 0];
  3216. rgbaData[newIndex + 1] = rgbData[index + 1];
  3217. rgbaData[newIndex + 2] = rgbData[index + 2];
  3218. // Add fully opaque alpha channel.
  3219. rgbaData[newIndex + 3] = 1;
  3220. }
  3221. }
  3222. return rgbaData;
  3223. }