webgpuEngine.ts 177 KB

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