webgpuEngine.ts 170 KB

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