webgpuTextureHelper.ts 53 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138
  1. // Copyright 2020 Brandon Jones
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy
  4. // of this software and associated documentation files (the "Software"), to deal
  5. // in the Software without restriction, including without limitation the rights
  6. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. // copies of the Software, and to permit persons to whom the Software is
  8. // furnished to do so, subject to the following conditions:
  9. // The above copyright notice and this permission notice shall be included in
  10. // all copies or substantial portions of the Software.
  11. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  12. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  13. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  14. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  15. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  16. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  17. // SOFTWARE.
  18. import * as WebGPUConstants from './webgpuConstants';
  19. import { Scalar } from '../../Maths/math.scalar';
  20. import { WebGPUBufferManager } from './webgpuBufferManager';
  21. import { Constants } from '../constants';
  22. import { Nullable } from '../../types';
  23. import { InternalTexture, InternalTextureSource } from '../../Materials/Textures/internalTexture';
  24. import { HardwareTextureWrapper } from '../../Materials/Textures/hardwareTextureWrapper';
  25. import { BaseTexture } from '../../Materials/Textures/baseTexture';
  26. import { WebGPUHardwareTexture } from './webgpuHardwareTexture';
  27. import { IColor4Like } from '../../Maths/math.like';
  28. // TODO WEBGPU improve mipmap generation by not using the OutputAttachment flag
  29. // see https://github.com/toji/web-texture-tool/tree/main/src
  30. // TODO WEBGPU optimize, don't recreate things that can be cached (bind groups, descriptors, etc)
  31. // TODO WEBGPU use WGSL instead of GLSL
  32. const mipmapVertexSource = `
  33. const vec2 pos[4] = vec2[4](vec2(-1.0f, 1.0f), vec2(1.0f, 1.0f), vec2(-1.0f, -1.0f), vec2(1.0f, -1.0f));
  34. const vec2 tex[4] = vec2[4](vec2(0.0f, 0.0f), vec2(1.0f, 0.0f), vec2(0.0f, 1.0f), vec2(1.0f, 1.0f));
  35. layout(location = 0) out vec2 vTex;
  36. void main() {
  37. vTex = tex[gl_VertexIndex];
  38. gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0);
  39. }
  40. `;
  41. const mipmapFragmentSource = `
  42. layout(set = 0, binding = 0) uniform sampler imgSampler;
  43. layout(set = 0, binding = 1) uniform texture2D img;
  44. layout(location = 0) in vec2 vTex;
  45. layout(location = 0) out vec4 outColor;
  46. void main() {
  47. outColor = texture(sampler2D(img, imgSampler), vTex);
  48. }
  49. `;
  50. const invertYPreMultiplyAlphaVertexSource = `
  51. const vec2 pos[4] = vec2[4](vec2(-1.0f, 1.0f), vec2(1.0f, 1.0f), vec2(-1.0f, -1.0f), vec2(1.0f, -1.0f));
  52. const vec2 tex[4] = vec2[4](vec2(0.0f, 0.0f), vec2(1.0f, 0.0f), vec2(0.0f, 1.0f), vec2(1.0f, 1.0f));
  53. layout(location = 0) out vec2 vTex;
  54. void main() {
  55. vTex = tex[gl_VertexIndex];
  56. #ifdef INVERTY
  57. vTex.y = 1.0 - vTex.y;
  58. #endif
  59. gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0);
  60. }
  61. `;
  62. const invertYPreMultiplyAlphaFragmentSource = `
  63. layout(set = 0, binding = 0) uniform sampler imgSampler;
  64. layout(set = 0, binding = 1) uniform texture2D img;
  65. layout(location = 0) in vec2 vTex;
  66. layout(location = 0) out vec4 outColor;
  67. void main() {
  68. vec4 color = texture(sampler2D(img, imgSampler), vTex);
  69. #ifdef PREMULTIPLYALPHA
  70. color.rgb *= color.a;
  71. #endif
  72. outColor = color;
  73. }
  74. `;
  75. const clearVertexSource = `
  76. const vec2 pos[4] = vec2[4](vec2(-1.0f, 1.0f), vec2(1.0f, 1.0f), vec2(-1.0f, -1.0f), vec2(1.0f, -1.0f));
  77. void main() {
  78. gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0);
  79. }
  80. `;
  81. const clearFragmentSource = `
  82. layout(set = 0, binding = 0) uniform Uniforms {
  83. uniform vec4 color;
  84. };
  85. layout(location = 0) out vec4 outColor;
  86. void main() {
  87. outColor = color;
  88. }
  89. `;
  90. enum PipelineType {
  91. MipMap = 0,
  92. InvertYPremultiplyAlpha = 1,
  93. Clear = 2,
  94. }
  95. interface pipelineParameters {
  96. invertY?: boolean;
  97. premultiplyAlpha?: boolean;
  98. }
  99. const shadersForPipelineType = [
  100. { vertex: mipmapVertexSource, fragment: mipmapFragmentSource },
  101. { vertex: invertYPreMultiplyAlphaVertexSource, fragment: invertYPreMultiplyAlphaFragmentSource },
  102. { vertex: clearVertexSource, fragment: clearFragmentSource },
  103. ];
  104. /** @hidden */
  105. export class WebGPUTextureHelper {
  106. private _device: GPUDevice;
  107. private _glslang: any;
  108. private _bufferManager: WebGPUBufferManager;
  109. private _mipmapSampler: GPUSampler;
  110. private _invertYPreMultiplyAlphaSampler: GPUSampler;
  111. private _pipelines: { [format: string]: Array<GPURenderPipeline> } = {};
  112. private _compiledShaders: GPUShaderModule[][] = [];
  113. private _deferredReleaseTextures: Array<[Nullable<HardwareTextureWrapper | GPUTexture>, Nullable<BaseTexture>, Nullable<InternalTexture>]> = [];
  114. private _commandEncoderForCreation: GPUCommandEncoder;
  115. public static ComputeNumMipmapLevels(width: number, height: number) {
  116. return Scalar.ILog2(Math.max(width, height)) + 1;
  117. }
  118. //------------------------------------------------------------------------------
  119. // Initialization / Helpers
  120. //------------------------------------------------------------------------------
  121. constructor(device: GPUDevice, glslang: any, bufferManager: WebGPUBufferManager) {
  122. this._device = device;
  123. this._glslang = glslang;
  124. this._bufferManager = bufferManager;
  125. this._mipmapSampler = device.createSampler({ minFilter: WebGPUConstants.FilterMode.Linear });
  126. this._invertYPreMultiplyAlphaSampler = device.createSampler({ minFilter: WebGPUConstants.FilterMode.Nearest, magFilter: WebGPUConstants.FilterMode.Nearest });
  127. this._getPipeline(WebGPUConstants.TextureFormat.RGBA8Unorm);
  128. }
  129. private _getPipeline(format: GPUTextureFormat, type: PipelineType = PipelineType.MipMap, params?: pipelineParameters): GPURenderPipeline {
  130. const index =
  131. type === PipelineType.MipMap ? 1 << 0 :
  132. type === PipelineType.InvertYPremultiplyAlpha ? ((params!.invertY ? 1 : 0) << 1) + ((params!.premultiplyAlpha ? 1 : 0) << 2) :
  133. type === PipelineType.Clear ? 1 << 3 : 0;
  134. if (!this._pipelines[format]) {
  135. this._pipelines[format] = [];
  136. }
  137. let pipeline = this._pipelines[format][index];
  138. if (!pipeline) {
  139. let defines = "#version 450\r\n";
  140. if (type === PipelineType.InvertYPremultiplyAlpha) {
  141. if (params!.invertY) {
  142. defines += "#define INVERTY\r\n";
  143. }
  144. if (params!.premultiplyAlpha) {
  145. defines += "define PREMULTIPLYALPHA\r\n";
  146. }
  147. }
  148. let modules = this._compiledShaders[index];
  149. if (!modules) {
  150. const vertexModule = this._device.createShaderModule({
  151. code: this._glslang.compileGLSL(defines + shadersForPipelineType[type].vertex, 'vertex')
  152. });
  153. const fragmentModule = this._device.createShaderModule({
  154. code: this._glslang.compileGLSL(defines + shadersForPipelineType[type].fragment, 'fragment')
  155. });
  156. modules = this._compiledShaders[index] = [vertexModule, fragmentModule];
  157. }
  158. pipeline = this._pipelines[format][index] = this._device.createRenderPipeline({
  159. vertexStage: {
  160. module: modules[0],
  161. entryPoint: 'main'
  162. },
  163. fragmentStage: {
  164. module: modules[1],
  165. entryPoint: 'main'
  166. },
  167. primitiveTopology: WebGPUConstants.PrimitiveTopology.TriangleStrip,
  168. colorStates: [{
  169. format,
  170. }]
  171. });
  172. }
  173. return pipeline;
  174. }
  175. private static _GetTextureTypeFromFormat(format: GPUTextureFormat): number {
  176. switch (format) {
  177. // One Component = 8 bits
  178. case WebGPUConstants.TextureFormat.R8Unorm:
  179. case WebGPUConstants.TextureFormat.R8Snorm:
  180. case WebGPUConstants.TextureFormat.R8Uint:
  181. case WebGPUConstants.TextureFormat.R8Sint:
  182. case WebGPUConstants.TextureFormat.RG8Unorm:
  183. case WebGPUConstants.TextureFormat.RG8Snorm:
  184. case WebGPUConstants.TextureFormat.RG8Uint:
  185. case WebGPUConstants.TextureFormat.RG8Sint:
  186. case WebGPUConstants.TextureFormat.RGBA8Unorm:
  187. case WebGPUConstants.TextureFormat.RGBA8UnormSRGB:
  188. case WebGPUConstants.TextureFormat.RGBA8Snorm:
  189. case WebGPUConstants.TextureFormat.RGBA8Uint:
  190. case WebGPUConstants.TextureFormat.RGBA8Sint:
  191. case WebGPUConstants.TextureFormat.BGRA8Unorm:
  192. case WebGPUConstants.TextureFormat.BGRA8UnormSRGB:
  193. case WebGPUConstants.TextureFormat.RGB10A2Unorm: // composite format - let's say it's byte...
  194. case WebGPUConstants.TextureFormat.RGB9E5UFloat: // composite format - let's say it's byte...
  195. case WebGPUConstants.TextureFormat.RG11B10UFloat: // composite format - let's say it's byte...
  196. case WebGPUConstants.TextureFormat.Depth24UnormStencil8: // composite format - let's say it's byte...
  197. case WebGPUConstants.TextureFormat.Depth32FloatStencil8: // composite format - let's say it's byte...
  198. case WebGPUConstants.TextureFormat.BC7RGBAUnorm:
  199. case WebGPUConstants.TextureFormat.BC7RGBAUnormSRGB:
  200. case WebGPUConstants.TextureFormat.BC6HRGBUFloat:
  201. case WebGPUConstants.TextureFormat.BC6HRGBFloat:
  202. case WebGPUConstants.TextureFormat.BC5RGUnorm:
  203. case WebGPUConstants.TextureFormat.BC5RGSnorm:
  204. case WebGPUConstants.TextureFormat.BC3RGBAUnorm:
  205. case WebGPUConstants.TextureFormat.BC3RGBAUnormSRGB:
  206. case WebGPUConstants.TextureFormat.BC2RGBAUnorm:
  207. case WebGPUConstants.TextureFormat.BC2RGBAUnormSRGB:
  208. case WebGPUConstants.TextureFormat.BC4RUnorm:
  209. case WebGPUConstants.TextureFormat.BC4RSnorm:
  210. case WebGPUConstants.TextureFormat.BC1RGBAUNorm:
  211. case WebGPUConstants.TextureFormat.BC1RGBAUnormSRGB:
  212. return Constants.TEXTURETYPE_UNSIGNED_BYTE;
  213. // One component = 16 bits
  214. case WebGPUConstants.TextureFormat.R16Uint:
  215. case WebGPUConstants.TextureFormat.R16Sint:
  216. case WebGPUConstants.TextureFormat.RG16Uint:
  217. case WebGPUConstants.TextureFormat.RG16Sint:
  218. case WebGPUConstants.TextureFormat.RGBA16Uint:
  219. case WebGPUConstants.TextureFormat.RGBA16Sint:
  220. case WebGPUConstants.TextureFormat.Depth16Unorm:
  221. return Constants.TEXTURETYPE_UNSIGNED_SHORT;
  222. case WebGPUConstants.TextureFormat.R16Float:
  223. case WebGPUConstants.TextureFormat.RG16Float:
  224. case WebGPUConstants.TextureFormat.RGBA16Float:
  225. return Constants.TEXTURETYPE_HALF_FLOAT;
  226. // One component = 32 bits
  227. case WebGPUConstants.TextureFormat.R32Uint:
  228. case WebGPUConstants.TextureFormat.R32Sint:
  229. case WebGPUConstants.TextureFormat.RG32Uint:
  230. case WebGPUConstants.TextureFormat.RG32Sint:
  231. case WebGPUConstants.TextureFormat.RGBA32Uint:
  232. case WebGPUConstants.TextureFormat.RGBA32Sint:
  233. return Constants.TEXTURETYPE_UNSIGNED_INTEGER;
  234. case WebGPUConstants.TextureFormat.R32Float:
  235. case WebGPUConstants.TextureFormat.RG32Float:
  236. case WebGPUConstants.TextureFormat.RGBA32Float:
  237. case WebGPUConstants.TextureFormat.Depth32Float:
  238. return Constants.TEXTURETYPE_FLOAT;
  239. case WebGPUConstants.TextureFormat.Stencil8:
  240. throw "No fixed size for Stencil8 format!";
  241. case WebGPUConstants.TextureFormat.Depth24Plus:
  242. throw "No fixed size for Depth24Plus format!";
  243. case WebGPUConstants.TextureFormat.Depth24PlusStencil8:
  244. throw "No fixed size for Depth24PlusStencil8 format!";
  245. }
  246. return Constants.TEXTURETYPE_UNSIGNED_BYTE;
  247. }
  248. private static _GetBlockInformationFromFormat(format: GPUTextureFormat): { width: number, height: number, length: number } {
  249. switch (format) {
  250. // 8 bits formats
  251. case WebGPUConstants.TextureFormat.R8Unorm:
  252. case WebGPUConstants.TextureFormat.R8Snorm:
  253. case WebGPUConstants.TextureFormat.R8Uint:
  254. case WebGPUConstants.TextureFormat.R8Sint:
  255. return { width: 1, height: 1, length: 1 };
  256. // 16 bits formats
  257. case WebGPUConstants.TextureFormat.R16Uint:
  258. case WebGPUConstants.TextureFormat.R16Sint:
  259. case WebGPUConstants.TextureFormat.R16Float:
  260. case WebGPUConstants.TextureFormat.RG8Unorm:
  261. case WebGPUConstants.TextureFormat.RG8Snorm:
  262. case WebGPUConstants.TextureFormat.RG8Uint:
  263. case WebGPUConstants.TextureFormat.RG8Sint:
  264. return { width: 1, height: 1, length: 2 };
  265. // 32 bits formats
  266. case WebGPUConstants.TextureFormat.R32Uint:
  267. case WebGPUConstants.TextureFormat.R32Sint:
  268. case WebGPUConstants.TextureFormat.R32Float:
  269. case WebGPUConstants.TextureFormat.RG16Uint:
  270. case WebGPUConstants.TextureFormat.RG16Sint:
  271. case WebGPUConstants.TextureFormat.RG16Float:
  272. case WebGPUConstants.TextureFormat.RGBA8Unorm:
  273. case WebGPUConstants.TextureFormat.RGBA8UnormSRGB:
  274. case WebGPUConstants.TextureFormat.RGBA8Snorm:
  275. case WebGPUConstants.TextureFormat.RGBA8Uint:
  276. case WebGPUConstants.TextureFormat.RGBA8Sint:
  277. case WebGPUConstants.TextureFormat.BGRA8Unorm:
  278. case WebGPUConstants.TextureFormat.BGRA8UnormSRGB:
  279. case WebGPUConstants.TextureFormat.RGB9E5UFloat:
  280. case WebGPUConstants.TextureFormat.RGB10A2Unorm:
  281. case WebGPUConstants.TextureFormat.RG11B10UFloat:
  282. return { width: 1, height: 1, length: 4 };
  283. // 64 bits formats
  284. case WebGPUConstants.TextureFormat.RG32Uint:
  285. case WebGPUConstants.TextureFormat.RG32Sint:
  286. case WebGPUConstants.TextureFormat.RG32Float:
  287. case WebGPUConstants.TextureFormat.RGBA16Uint:
  288. case WebGPUConstants.TextureFormat.RGBA16Sint:
  289. case WebGPUConstants.TextureFormat.RGBA16Float:
  290. return { width: 1, height: 1, length: 8 };
  291. // 128 bits formats
  292. case WebGPUConstants.TextureFormat.RGBA32Uint:
  293. case WebGPUConstants.TextureFormat.RGBA32Sint:
  294. case WebGPUConstants.TextureFormat.RGBA32Float:
  295. return { width: 1, height: 1, length: 16 };
  296. // Depth and stencil formats
  297. case WebGPUConstants.TextureFormat.Stencil8:
  298. throw "No fixed size for Stencil8 format!";
  299. case WebGPUConstants.TextureFormat.Depth16Unorm:
  300. return { width: 1, height: 1, length: 2 };
  301. case WebGPUConstants.TextureFormat.Depth24Plus:
  302. throw "No fixed size for Depth24Plus format!";
  303. case WebGPUConstants.TextureFormat.Depth24PlusStencil8:
  304. throw "No fixed size for Depth24PlusStencil8 format!";
  305. case WebGPUConstants.TextureFormat.Depth32Float:
  306. return { width: 1, height: 1, length: 4 };
  307. case WebGPUConstants.TextureFormat.Depth24UnormStencil8:
  308. return { width: 1, height: 1, length: 4 };
  309. case WebGPUConstants.TextureFormat.Depth32FloatStencil8:
  310. return { width: 1, height: 1, length: 5 };
  311. // BC compressed formats usable if "texture-compression-bc" is both
  312. // supported by the device/user agent and enabled in requestDevice.
  313. case WebGPUConstants.TextureFormat.BC7RGBAUnorm:
  314. case WebGPUConstants.TextureFormat.BC7RGBAUnormSRGB:
  315. case WebGPUConstants.TextureFormat.BC6HRGBUFloat:
  316. case WebGPUConstants.TextureFormat.BC6HRGBFloat:
  317. case WebGPUConstants.TextureFormat.BC5RGUnorm:
  318. case WebGPUConstants.TextureFormat.BC5RGSnorm:
  319. case WebGPUConstants.TextureFormat.BC3RGBAUnorm:
  320. case WebGPUConstants.TextureFormat.BC3RGBAUnormSRGB:
  321. case WebGPUConstants.TextureFormat.BC2RGBAUnorm:
  322. case WebGPUConstants.TextureFormat.BC2RGBAUnormSRGB:
  323. return { width: 4, height: 4, length: 16 };
  324. case WebGPUConstants.TextureFormat.BC4RUnorm:
  325. case WebGPUConstants.TextureFormat.BC4RSnorm:
  326. case WebGPUConstants.TextureFormat.BC1RGBAUNorm:
  327. case WebGPUConstants.TextureFormat.BC1RGBAUnormSRGB:
  328. return { width: 4, height: 4, length: 8 };
  329. }
  330. return { width: 1, height: 1, length: 4 };
  331. }
  332. private static _IsHardwareTexture(texture: HardwareTextureWrapper | GPUTexture): texture is HardwareTextureWrapper {
  333. return !!(texture as HardwareTextureWrapper).release;
  334. }
  335. private static _IsInternalTexture(texture: InternalTexture | GPUTexture): texture is InternalTexture {
  336. return !!(texture as InternalTexture).dispose;
  337. }
  338. public static GetCompareFunction(compareFunction: Nullable<number>): GPUCompareFunction {
  339. switch (compareFunction) {
  340. case Constants.ALWAYS:
  341. return WebGPUConstants.CompareFunction.Always;
  342. case Constants.EQUAL:
  343. return WebGPUConstants.CompareFunction.Equal;
  344. case Constants.GREATER:
  345. return WebGPUConstants.CompareFunction.Greater;
  346. case Constants.GEQUAL:
  347. return WebGPUConstants.CompareFunction.GreaterEqual;
  348. case Constants.LESS:
  349. return WebGPUConstants.CompareFunction.Less;
  350. case Constants.LEQUAL:
  351. return WebGPUConstants.CompareFunction.LessEqual;
  352. case Constants.NEVER:
  353. return WebGPUConstants.CompareFunction.Never;
  354. case Constants.NOTEQUAL:
  355. return WebGPUConstants.CompareFunction.NotEqual;
  356. default:
  357. return WebGPUConstants.CompareFunction.Less;
  358. }
  359. }
  360. public static IsImageBitmap(imageBitmap: ImageBitmap | { width: number, height: number }): imageBitmap is ImageBitmap {
  361. return (imageBitmap as ImageBitmap).close !== undefined;
  362. }
  363. public static IsImageBitmapArray(imageBitmap: ImageBitmap[] | { width: number, height: number }): imageBitmap is ImageBitmap[] {
  364. return Array.isArray(imageBitmap as ImageBitmap[]) && (imageBitmap as ImageBitmap[])[0].close !== undefined;
  365. }
  366. public setCommandEncoder(encoder: GPUCommandEncoder): void {
  367. this._commandEncoderForCreation = encoder;
  368. }
  369. public static IsCompressedFormat(format: GPUTextureFormat): boolean {
  370. switch (format) {
  371. case WebGPUConstants.TextureFormat.BC7RGBAUnormSRGB:
  372. case WebGPUConstants.TextureFormat.BC7RGBAUnorm:
  373. case WebGPUConstants.TextureFormat.BC6HRGBFloat:
  374. case WebGPUConstants.TextureFormat.BC6HRGBUFloat:
  375. case WebGPUConstants.TextureFormat.BC5RGSnorm:
  376. case WebGPUConstants.TextureFormat.BC5RGUnorm:
  377. case WebGPUConstants.TextureFormat.BC4RSnorm:
  378. case WebGPUConstants.TextureFormat.BC4RUnorm:
  379. case WebGPUConstants.TextureFormat.BC3RGBAUnormSRGB:
  380. case WebGPUConstants.TextureFormat.BC3RGBAUnorm:
  381. case WebGPUConstants.TextureFormat.BC2RGBAUnormSRGB:
  382. case WebGPUConstants.TextureFormat.BC2RGBAUnorm:
  383. case WebGPUConstants.TextureFormat.BC1RGBAUnormSRGB:
  384. case WebGPUConstants.TextureFormat.BC1RGBAUNorm:
  385. return true;
  386. }
  387. return false;
  388. }
  389. public static GetWebGPUTextureFormat(type: number, format: number): GPUTextureFormat {
  390. switch (format) {
  391. case Constants.TEXTUREFORMAT_DEPTH24_STENCIL8:
  392. return WebGPUConstants.TextureFormat.Depth24PlusStencil8;
  393. case Constants.TEXTUREFORMAT_DEPTH32_FLOAT:
  394. return WebGPUConstants.TextureFormat.Depth32Float;
  395. case Constants.TEXTUREFORMAT_COMPRESSED_RGBA_BPTC_UNORM:
  396. return WebGPUConstants.TextureFormat.BC7RGBAUnorm;
  397. case Constants.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
  398. return WebGPUConstants.TextureFormat.BC6HRGBUFloat;
  399. case Constants.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:
  400. return WebGPUConstants.TextureFormat.BC6HRGBFloat;
  401. case Constants.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT5:
  402. return WebGPUConstants.TextureFormat.BC3RGBAUnorm;
  403. case Constants.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT3:
  404. return WebGPUConstants.TextureFormat.BC2RGBAUnorm;
  405. case Constants.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT1:
  406. return WebGPUConstants.TextureFormat.BC1RGBAUNorm;
  407. }
  408. switch (type) {
  409. case Constants.TEXTURETYPE_BYTE:
  410. switch (format) {
  411. case Constants.TEXTUREFORMAT_RED:
  412. return WebGPUConstants.TextureFormat.R8Snorm;
  413. case Constants.TEXTUREFORMAT_RG:
  414. return WebGPUConstants.TextureFormat.RG8Snorm;
  415. case Constants.TEXTUREFORMAT_RGB:
  416. throw "RGB format not supported in WebGPU";
  417. case Constants.TEXTUREFORMAT_RED_INTEGER:
  418. return WebGPUConstants.TextureFormat.R8Sint;
  419. case Constants.TEXTUREFORMAT_RG_INTEGER:
  420. return WebGPUConstants.TextureFormat.RG8Sint;
  421. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  422. throw "RGB_INTEGER format not supported in WebGPU";
  423. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  424. return WebGPUConstants.TextureFormat.RGBA8Sint;
  425. default:
  426. return WebGPUConstants.TextureFormat.RGBA8Snorm;
  427. }
  428. case Constants.TEXTURETYPE_UNSIGNED_BYTE:
  429. switch (format) {
  430. case Constants.TEXTUREFORMAT_RED:
  431. return WebGPUConstants.TextureFormat.R8Unorm;
  432. case Constants.TEXTUREFORMAT_RG:
  433. return WebGPUConstants.TextureFormat.RG8Unorm;
  434. case Constants.TEXTUREFORMAT_RGB:
  435. throw "TEXTUREFORMAT_RGB format not supported in WebGPU";
  436. case Constants.TEXTUREFORMAT_RGBA:
  437. return WebGPUConstants.TextureFormat.RGBA8Unorm;
  438. case Constants.TEXTUREFORMAT_BGRA:
  439. return WebGPUConstants.TextureFormat.BGRA8Unorm;
  440. case Constants.TEXTUREFORMAT_RED_INTEGER:
  441. return WebGPUConstants.TextureFormat.R8Uint;
  442. case Constants.TEXTUREFORMAT_RG_INTEGER:
  443. return WebGPUConstants.TextureFormat.RG8Uint;
  444. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  445. throw "RGB_INTEGER format not supported in WebGPU";
  446. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  447. return WebGPUConstants.TextureFormat.RGBA8Uint;
  448. case Constants.TEXTUREFORMAT_ALPHA:
  449. throw "TEXTUREFORMAT_ALPHA format not supported in WebGPU";
  450. case Constants.TEXTUREFORMAT_LUMINANCE:
  451. throw "TEXTUREFORMAT_LUMINANCE format not supported in WebGPU";
  452. case Constants.TEXTUREFORMAT_LUMINANCE_ALPHA:
  453. throw "TEXTUREFORMAT_LUMINANCE_ALPHA format not supported in WebGPU";
  454. default:
  455. return WebGPUConstants.TextureFormat.RGBA8Unorm;
  456. }
  457. case Constants.TEXTURETYPE_SHORT:
  458. switch (format) {
  459. case Constants.TEXTUREFORMAT_RED_INTEGER:
  460. return WebGPUConstants.TextureFormat.R16Sint;
  461. case Constants.TEXTUREFORMAT_RG_INTEGER:
  462. return WebGPUConstants.TextureFormat.RG16Sint;
  463. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  464. throw "TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";
  465. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  466. return WebGPUConstants.TextureFormat.RGBA16Sint;
  467. default:
  468. return WebGPUConstants.TextureFormat.RGBA16Sint;
  469. }
  470. case Constants.TEXTURETYPE_UNSIGNED_SHORT:
  471. switch (format) {
  472. case Constants.TEXTUREFORMAT_RED_INTEGER:
  473. return WebGPUConstants.TextureFormat.R16Uint;
  474. case Constants.TEXTUREFORMAT_RG_INTEGER:
  475. return WebGPUConstants.TextureFormat.RG16Uint;
  476. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  477. throw "TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";
  478. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  479. return WebGPUConstants.TextureFormat.RGBA16Uint;
  480. default:
  481. return WebGPUConstants.TextureFormat.RGBA16Uint;
  482. }
  483. case Constants.TEXTURETYPE_INT:
  484. switch (format) {
  485. case Constants.TEXTUREFORMAT_RED_INTEGER:
  486. return WebGPUConstants.TextureFormat.R32Sint;
  487. case Constants.TEXTUREFORMAT_RG_INTEGER:
  488. return WebGPUConstants.TextureFormat.RG32Sint;
  489. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  490. throw "TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";
  491. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  492. return WebGPUConstants.TextureFormat.RGBA32Sint;
  493. default:
  494. return WebGPUConstants.TextureFormat.RGBA32Sint;
  495. }
  496. case Constants.TEXTURETYPE_UNSIGNED_INTEGER: // Refers to UNSIGNED_INT
  497. switch (format) {
  498. case Constants.TEXTUREFORMAT_RED_INTEGER:
  499. return WebGPUConstants.TextureFormat.R32Uint;
  500. case Constants.TEXTUREFORMAT_RG_INTEGER:
  501. return WebGPUConstants.TextureFormat.RG32Uint;
  502. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  503. throw "TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";
  504. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  505. return WebGPUConstants.TextureFormat.RGBA32Uint;
  506. default:
  507. return WebGPUConstants.TextureFormat.RGBA32Uint;
  508. }
  509. case Constants.TEXTURETYPE_FLOAT:
  510. switch (format) {
  511. case Constants.TEXTUREFORMAT_RED:
  512. return WebGPUConstants.TextureFormat.R32Float; // By default. Other possibility is R16Float.
  513. case Constants.TEXTUREFORMAT_RG:
  514. return WebGPUConstants.TextureFormat.RG32Float; // By default. Other possibility is RG16Float.
  515. case Constants.TEXTUREFORMAT_RGB:
  516. throw "TEXTUREFORMAT_RGB format not supported in WebGPU";
  517. case Constants.TEXTUREFORMAT_RGBA:
  518. return WebGPUConstants.TextureFormat.RGBA32Float; // By default. Other possibility is RGBA16Float.
  519. default:
  520. return WebGPUConstants.TextureFormat.RGBA32Float;
  521. }
  522. case Constants.TEXTURETYPE_HALF_FLOAT:
  523. switch (format) {
  524. case Constants.TEXTUREFORMAT_RED:
  525. return WebGPUConstants.TextureFormat.R16Float;
  526. case Constants.TEXTUREFORMAT_RG:
  527. return WebGPUConstants.TextureFormat.RG16Float;
  528. case Constants.TEXTUREFORMAT_RGB:
  529. throw "TEXTUREFORMAT_RGB format not supported in WebGPU";
  530. case Constants.TEXTUREFORMAT_RGBA:
  531. return WebGPUConstants.TextureFormat.RGBA16Float;
  532. default:
  533. return WebGPUConstants.TextureFormat.RGBA16Float;
  534. }
  535. case Constants.TEXTURETYPE_UNSIGNED_SHORT_5_6_5:
  536. throw "TEXTURETYPE_UNSIGNED_SHORT_5_6_5 format not supported in WebGPU";
  537. case Constants.TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV:
  538. throw "TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV format not supported in WebGPU";
  539. case Constants.TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV:
  540. throw "TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV format not supported in WebGPU";
  541. case Constants.TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4:
  542. throw "TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4 format not supported in WebGPU";
  543. case Constants.TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1:
  544. throw "TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1 format not supported in WebGPU";
  545. case Constants.TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV:
  546. switch (format) {
  547. case Constants.TEXTUREFORMAT_RGBA:
  548. return WebGPUConstants.TextureFormat.RGB10A2Unorm;
  549. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  550. throw "TEXTUREFORMAT_RGBA_INTEGER format not supported in WebGPU when type is TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV";
  551. default:
  552. return WebGPUConstants.TextureFormat.RGB10A2Unorm;
  553. }
  554. }
  555. return WebGPUConstants.TextureFormat.RGBA8Unorm;
  556. }
  557. public invertYPreMultiplyAlpha(gpuTexture: GPUTexture, width: number, height: number, format: GPUTextureFormat, invertY = false, premultiplyAlpha = false, faceIndex= 0, commandEncoder?: GPUCommandEncoder): void {
  558. const useOwnCommandEncoder = commandEncoder === undefined;
  559. const pipeline = this._getPipeline(format, PipelineType.InvertYPremultiplyAlpha, { invertY, premultiplyAlpha });
  560. const bindGroupLayout = pipeline.getBindGroupLayout(0);
  561. if (useOwnCommandEncoder) {
  562. commandEncoder = this._device.createCommandEncoder({});
  563. }
  564. commandEncoder!.pushDebugGroup(`internal process texture - invertY=${invertY} premultiplyAlpha=${premultiplyAlpha}`);
  565. const outputTexture = this.createTexture({ width, height, layers: 1 }, false, false, false, false, false, format, 1, commandEncoder, WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.OutputAttachment | WebGPUConstants.TextureUsage.Sampled);
  566. const passEncoder = commandEncoder!.beginRenderPass({
  567. colorAttachments: [{
  568. attachment: outputTexture.createView({
  569. dimension: WebGPUConstants.TextureViewDimension.E2d,
  570. baseMipLevel: 0,
  571. mipLevelCount: 1,
  572. arrayLayerCount: 1,
  573. baseArrayLayer: 0,
  574. }),
  575. loadValue: WebGPUConstants.LoadOp.Load,
  576. }],
  577. });
  578. const bindGroup = this._device.createBindGroup({
  579. layout: bindGroupLayout,
  580. entries: [{
  581. binding: 0,
  582. resource: this._invertYPreMultiplyAlphaSampler,
  583. }, {
  584. binding: 1,
  585. resource: gpuTexture.createView({
  586. dimension: WebGPUConstants.TextureViewDimension.E2d,
  587. baseMipLevel: 0,
  588. mipLevelCount: 1,
  589. arrayLayerCount: 1,
  590. baseArrayLayer: faceIndex,
  591. }),
  592. }],
  593. });
  594. passEncoder.setPipeline(pipeline);
  595. passEncoder.setBindGroup(0, bindGroup);
  596. passEncoder.draw(4, 1, 0, 0);
  597. passEncoder.endPass();
  598. commandEncoder!.copyTextureToTexture({
  599. texture: outputTexture,
  600. }, {
  601. texture: gpuTexture,
  602. origin: {
  603. x: 0,
  604. y: 0,
  605. z: Math.max(faceIndex, 0),
  606. }
  607. }, {
  608. width,
  609. height,
  610. depth: 1,
  611. }
  612. );
  613. this._deferredReleaseTextures.push([outputTexture, null, null]);
  614. commandEncoder!.popDebugGroup();
  615. if (useOwnCommandEncoder) {
  616. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  617. commandEncoder = null as any;
  618. }
  619. }
  620. public clear(format: GPUTextureFormat, color: IColor4Like, passEncoder: GPURenderPassEncoder): void {
  621. const pipeline = this._getPipeline(format, PipelineType.Clear);
  622. const bindGroupLayout = pipeline.getBindGroupLayout(0);
  623. const buffer = this._bufferManager.createRawBuffer(4 * 4, WebGPUConstants.BufferUsage.CopySrc | WebGPUConstants.BufferUsage.Uniform, true);
  624. /*const arrayBuffer = buffer.getMappedRange();
  625. new Float32Array(arrayBuffer).set([color.r, color.g, color.b, color.a]);
  626. buffer.unmap();*/
  627. const bindGroup = this._device.createBindGroup({
  628. layout: bindGroupLayout,
  629. entries: [{
  630. binding: 0,
  631. resource: {
  632. buffer,
  633. },
  634. }],
  635. });
  636. passEncoder.setPipeline(pipeline);
  637. passEncoder.setBindGroup(0, bindGroup);
  638. passEncoder.draw(4, 1, 0, 0);
  639. this._bufferManager.releaseBuffer(buffer);
  640. }
  641. //------------------------------------------------------------------------------
  642. // Creation
  643. //------------------------------------------------------------------------------
  644. public createTexture(imageBitmap: ImageBitmap | { width: number, height: number, layers: number }, hasMipmaps = false, generateMipmaps = false, invertY = false, premultiplyAlpha = false, is3D = false, format: GPUTextureFormat = WebGPUConstants.TextureFormat.RGBA8Unorm,
  645. sampleCount = 1, commandEncoder?: GPUCommandEncoder, usage = -1): GPUTexture
  646. {
  647. if (sampleCount > 1) {
  648. // TODO WEBGPU for the time being, Chrome only accepts values of 1 or 4
  649. sampleCount = 4;
  650. }
  651. const layerCount = (imageBitmap as any).layers || 1;
  652. let textureSize = {
  653. width: imageBitmap.width,
  654. height: imageBitmap.height,
  655. depth: layerCount,
  656. };
  657. const mipLevelCount = hasMipmaps ? WebGPUTextureHelper.ComputeNumMipmapLevels(imageBitmap.width, imageBitmap.height) : 1;
  658. const usages = usage >= 0 ? usage : WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.CopyDst | WebGPUConstants.TextureUsage.Sampled;
  659. const additionalUsages = hasMipmaps && !WebGPUTextureHelper.IsCompressedFormat(format) ? WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.OutputAttachment : 0;
  660. const gpuTexture = this._device.createTexture({
  661. size: textureSize,
  662. dimension: is3D ? WebGPUConstants.TextureDimension.E3d : WebGPUConstants.TextureDimension.E2d,
  663. format,
  664. usage: usages | additionalUsages,
  665. sampleCount,
  666. mipLevelCount
  667. });
  668. if (WebGPUTextureHelper.IsImageBitmap(imageBitmap)) {
  669. this.updateTexture(imageBitmap, gpuTexture, imageBitmap.width, imageBitmap.height, layerCount, format, 0, 0, invertY, premultiplyAlpha, 0, 0, commandEncoder);
  670. if (hasMipmaps && generateMipmaps) {
  671. this.generateMipmaps(gpuTexture, format, mipLevelCount, 0, commandEncoder);
  672. }
  673. }
  674. return gpuTexture;
  675. }
  676. public createCubeTexture(imageBitmaps: ImageBitmap[] | { width: number, height: number }, hasMipmaps = false, generateMipmaps = false, invertY = false, premultiplyAlpha = false, format: GPUTextureFormat = WebGPUConstants.TextureFormat.RGBA8Unorm,
  677. sampleCount = 1, commandEncoder?: GPUCommandEncoder, usage = -1): GPUTexture
  678. {
  679. if (sampleCount > 1) {
  680. // TODO WEBGPU for the time being, Chrome only accepts values of 1 or 4
  681. sampleCount = 4;
  682. }
  683. const width = WebGPUTextureHelper.IsImageBitmapArray(imageBitmaps) ? imageBitmaps[0].width : imageBitmaps.width;
  684. const height = WebGPUTextureHelper.IsImageBitmapArray(imageBitmaps) ? imageBitmaps[0].height : imageBitmaps.height;
  685. const mipLevelCount = hasMipmaps ? WebGPUTextureHelper.ComputeNumMipmapLevels(width, height) : 1;
  686. const usages = usage >= 0 ? usage : WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.CopyDst | WebGPUConstants.TextureUsage.Sampled;
  687. const additionalUsages = hasMipmaps && !WebGPUTextureHelper.IsCompressedFormat(format) ? WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.OutputAttachment : 0;
  688. const gpuTexture = this._device.createTexture({
  689. size: {
  690. width,
  691. height,
  692. depth: 6,
  693. },
  694. dimension: WebGPUConstants.TextureDimension.E2d,
  695. format,
  696. usage: usages | additionalUsages,
  697. sampleCount,
  698. mipLevelCount
  699. });
  700. if (WebGPUTextureHelper.IsImageBitmapArray(imageBitmaps)) {
  701. this.updateCubeTextures(imageBitmaps, gpuTexture, width, height, format, invertY, premultiplyAlpha, 0, 0, commandEncoder);
  702. if (hasMipmaps && generateMipmaps) {
  703. this.generateCubeMipmaps(gpuTexture, format, mipLevelCount, commandEncoder);
  704. }
  705. }
  706. return gpuTexture;
  707. }
  708. public generateCubeMipmaps(gpuTexture: GPUTexture, format: GPUTextureFormat, mipLevelCount: number, commandEncoder?: GPUCommandEncoder): void {
  709. const useOwnCommandEncoder = commandEncoder === undefined;
  710. if (useOwnCommandEncoder) {
  711. commandEncoder = this._device.createCommandEncoder({});
  712. }
  713. commandEncoder!.pushDebugGroup(`create cube mipmaps - ${mipLevelCount} levels`);
  714. for (let f = 0; f < 6; ++f) {
  715. this.generateMipmaps(gpuTexture, format, mipLevelCount, f, commandEncoder);
  716. }
  717. commandEncoder!.popDebugGroup();
  718. if (useOwnCommandEncoder) {
  719. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  720. commandEncoder = null as any;
  721. }
  722. }
  723. public generateMipmaps(gpuTexture: GPUTexture, format: GPUTextureFormat, mipLevelCount: number, faceIndex= 0, commandEncoder?: GPUCommandEncoder): void {
  724. const useOwnCommandEncoder = commandEncoder === undefined;
  725. const pipeline = this._getPipeline(format);
  726. const bindGroupLayout = pipeline.getBindGroupLayout(0);
  727. if (useOwnCommandEncoder) {
  728. commandEncoder = this._device.createCommandEncoder({});
  729. }
  730. commandEncoder!.pushDebugGroup(`create mipmaps for face #${faceIndex} - ${mipLevelCount} levels`);
  731. for (let i = 1; i < mipLevelCount; ++i) {
  732. const passEncoder = commandEncoder!.beginRenderPass({
  733. colorAttachments: [{
  734. attachment: gpuTexture.createView({
  735. dimension: WebGPUConstants.TextureViewDimension.E2d,
  736. baseMipLevel: i,
  737. mipLevelCount: 1,
  738. arrayLayerCount: 1,
  739. baseArrayLayer: faceIndex,
  740. }),
  741. loadValue: WebGPUConstants.LoadOp.Load,
  742. }],
  743. });
  744. const bindGroup = this._device.createBindGroup({
  745. layout: bindGroupLayout,
  746. entries: [{
  747. binding: 0,
  748. resource: this._mipmapSampler,
  749. }, {
  750. binding: 1,
  751. resource: gpuTexture.createView({
  752. dimension: WebGPUConstants.TextureViewDimension.E2d,
  753. baseMipLevel: i - 1,
  754. mipLevelCount: 1,
  755. arrayLayerCount: 1,
  756. baseArrayLayer: faceIndex,
  757. }),
  758. }],
  759. });
  760. passEncoder.setPipeline(pipeline);
  761. passEncoder.setBindGroup(0, bindGroup);
  762. passEncoder.draw(4, 1, 0, 0);
  763. passEncoder.endPass();
  764. }
  765. commandEncoder!.popDebugGroup();
  766. if (useOwnCommandEncoder) {
  767. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  768. commandEncoder = null as any;
  769. }
  770. }
  771. public createGPUTextureForInternalTexture(texture: InternalTexture, width?: number, height?: number, depth?: number): WebGPUHardwareTexture {
  772. if (!texture._hardwareTexture) {
  773. texture._hardwareTexture = new WebGPUHardwareTexture();
  774. }
  775. if (width === undefined) {
  776. width = texture.width;
  777. }
  778. if (height === undefined) {
  779. height = texture.height;
  780. }
  781. if (depth === undefined) {
  782. depth = texture.depth;
  783. }
  784. const gpuTextureWrapper = texture._hardwareTexture as WebGPUHardwareTexture;
  785. gpuTextureWrapper.format = WebGPUTextureHelper.GetWebGPUTextureFormat(texture.type, texture.format);
  786. gpuTextureWrapper.textureUsages =
  787. texture._source === InternalTextureSource.RenderTarget || texture.source === InternalTextureSource.MultiRenderTarget ? WebGPUConstants.TextureUsage.Sampled | WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.OutputAttachment :
  788. texture._source === InternalTextureSource.Depth ? WebGPUConstants.TextureUsage.Sampled | WebGPUConstants.TextureUsage.OutputAttachment : -1;
  789. const hasMipMaps = texture.generateMipMaps;
  790. const layerCount = depth || 1;
  791. if (texture.isCube) {
  792. const gpuTexture = this.createCubeTexture({ width, height }, texture.generateMipMaps, texture.generateMipMaps, texture.invertY, false, gpuTextureWrapper.format, 1, this._commandEncoderForCreation, gpuTextureWrapper.textureUsages);
  793. gpuTextureWrapper.set(gpuTexture);
  794. gpuTextureWrapper.createView({
  795. dimension: WebGPUConstants.TextureViewDimension.Cube,
  796. mipLevelCount: hasMipMaps ? WebGPUTextureHelper.ComputeNumMipmapLevels(width!, height!) : 1,
  797. baseArrayLayer: 0,
  798. baseMipLevel: 0,
  799. aspect: WebGPUConstants.TextureAspect.All
  800. });
  801. } else {
  802. const gpuTexture = this.createTexture({ width, height, layers: layerCount }, texture.generateMipMaps, texture.generateMipMaps, texture.invertY, false, texture.is3D, gpuTextureWrapper.format, 1, this._commandEncoderForCreation, gpuTextureWrapper.textureUsages);
  803. gpuTextureWrapper.set(gpuTexture);
  804. gpuTextureWrapper.createView({
  805. dimension: texture.is2DArray ? WebGPUConstants.TextureViewDimension.E2dArray : texture.is3D ? WebGPUConstants.TextureDimension.E3d : WebGPUConstants.TextureViewDimension.E2d,
  806. mipLevelCount: hasMipMaps ? WebGPUTextureHelper.ComputeNumMipmapLevels(width!, height!) : 1,
  807. baseArrayLayer: 0,
  808. baseMipLevel: 0,
  809. arrayLayerCount: layerCount,
  810. aspect: WebGPUConstants.TextureAspect.All
  811. });
  812. }
  813. texture.width = texture.baseWidth = width;
  814. texture.height = texture.baseHeight = height;
  815. texture.depth = texture.baseDepth = depth;
  816. this.createMSAATexture(texture, texture.samples);
  817. return gpuTextureWrapper;
  818. }
  819. public createMSAATexture(texture: InternalTexture, samples: number): void {
  820. const gpuTextureWrapper = texture._hardwareTexture as Nullable<WebGPUHardwareTexture>;
  821. if (gpuTextureWrapper?.msaaTexture) {
  822. this.releaseTexture(gpuTextureWrapper.msaaTexture);
  823. gpuTextureWrapper.msaaTexture = null;
  824. }
  825. if (!gpuTextureWrapper || (samples ?? 1) <= 1) {
  826. return;
  827. }
  828. const width = texture.width;
  829. const height = texture.height;
  830. const layerCount = texture.depth || 1;
  831. if (texture.isCube) {
  832. const gpuMSAATexture = this.createCubeTexture({ width, height }, false, false, texture.invertY, false, gpuTextureWrapper.format, samples, this._commandEncoderForCreation, gpuTextureWrapper.textureUsages);
  833. gpuTextureWrapper.setMSAATexture(gpuMSAATexture);
  834. } else {
  835. const gpuMSAATexture = this.createTexture({ width, height, layers: layerCount }, false, false, texture.invertY, false, texture.is3D, gpuTextureWrapper.format, samples, this._commandEncoderForCreation, gpuTextureWrapper.textureUsages);
  836. gpuTextureWrapper.setMSAATexture(gpuMSAATexture);
  837. }
  838. }
  839. //------------------------------------------------------------------------------
  840. // Update
  841. //------------------------------------------------------------------------------
  842. public updateCubeTextures(imageBitmaps: ImageBitmap[] | Uint8Array[], gpuTexture: GPUTexture, width: number, height: number, format: GPUTextureFormat, invertY = false, premultiplyAlpha = false, offsetX = 0, offsetY = 0,
  843. commandEncoder?: GPUCommandEncoder): void {
  844. const faces = [0, 3, 1, 4, 2, 5];
  845. for (let f = 0; f < faces.length; ++f) {
  846. let imageBitmap = imageBitmaps[faces[f]];
  847. this.updateTexture(imageBitmap, gpuTexture, width, height, 1, format, f, 0, invertY, premultiplyAlpha, offsetX, offsetY, commandEncoder);
  848. }
  849. }
  850. // TODO WEBGPU handle data source not being in the same format than the destination texture?
  851. public updateTexture(imageBitmap: ImageBitmap | Uint8Array, gpuTexture: GPUTexture, width: number, height: number, layers: number, format: GPUTextureFormat, faceIndex: number = 0, mipLevel: number = 0, invertY = false, premultiplyAlpha = false, offsetX = 0, offsetY = 0,
  852. commandEncoder?: GPUCommandEncoder): void
  853. {
  854. const blockInformation = WebGPUTextureHelper._GetBlockInformationFromFormat(format);
  855. const textureCopyView: GPUTextureCopyView = {
  856. texture: gpuTexture,
  857. origin: {
  858. x: offsetX,
  859. y: offsetY,
  860. z: Math.max(faceIndex, 0)
  861. },
  862. mipLevel: mipLevel
  863. };
  864. const textureExtent = {
  865. width: Math.ceil(width / blockInformation.width) * blockInformation.width,
  866. height: Math.ceil(height / blockInformation.height) * blockInformation.height,
  867. depth: layers || 1
  868. };
  869. if ((imageBitmap as Uint8Array).byteLength !== undefined) {
  870. imageBitmap = imageBitmap as Uint8Array;
  871. const bytesPerRow = Math.ceil(width / blockInformation.width) * blockInformation.length;
  872. const aligned = Math.ceil(bytesPerRow / 256) * 256 === bytesPerRow;
  873. if (aligned) {
  874. const useOwnCommandEncoder = commandEncoder === undefined;
  875. if (useOwnCommandEncoder) {
  876. commandEncoder = this._device.createCommandEncoder({});
  877. }
  878. const buffer = this._bufferManager.createRawBuffer(imageBitmap.byteLength, WebGPUConstants.BufferUsage.MapWrite | WebGPUConstants.BufferUsage.CopySrc, true);
  879. const arrayBuffer = buffer.getMappedRange();
  880. new Uint8Array(arrayBuffer).set(imageBitmap);
  881. buffer.unmap();
  882. commandEncoder!.copyBufferToTexture({
  883. buffer: buffer,
  884. offset: 0,
  885. bytesPerRow,
  886. rowsPerImage: height,
  887. }, textureCopyView, textureExtent);
  888. if (useOwnCommandEncoder) {
  889. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  890. commandEncoder = null as any;
  891. }
  892. this._bufferManager.releaseBuffer(buffer);
  893. } else {
  894. this._device.defaultQueue.writeTexture(textureCopyView, imageBitmap, {
  895. offset: 0,
  896. bytesPerRow,
  897. rowsPerImage: height,
  898. }, textureExtent);
  899. }
  900. if (invertY || premultiplyAlpha) {
  901. this.invertYPreMultiplyAlpha(gpuTexture, width, height, format, invertY, premultiplyAlpha, faceIndex, commandEncoder);
  902. }
  903. } else {
  904. imageBitmap = imageBitmap as ImageBitmap;
  905. if (invertY || premultiplyAlpha) {
  906. createImageBitmap(imageBitmap, { imageOrientation: invertY ? "flipY" : "none", premultiplyAlpha: premultiplyAlpha ? "premultiply" : "none" }).then((imageBitmap) => {
  907. this._device.defaultQueue.copyImageBitmapToTexture({ imageBitmap }, textureCopyView, textureExtent);
  908. });
  909. } else {
  910. this._device.defaultQueue.copyImageBitmapToTexture({ imageBitmap }, textureCopyView, textureExtent);
  911. }
  912. }
  913. }
  914. public readPixels(texture: GPUTexture, x: number, y: number, width: number, height: number, format: GPUTextureFormat, faceIndex: number = 0, mipLevel: number = 0, buffer: Nullable<ArrayBufferView> = null): Promise<ArrayBufferView> {
  915. const blockInformation = WebGPUTextureHelper._GetBlockInformationFromFormat(format);
  916. const bytesPerRow = Math.ceil(width / blockInformation.width) * blockInformation.length;
  917. const bytesPerRowAligned = Math.ceil(bytesPerRow / 256) * 256;
  918. const size = bytesPerRowAligned * height;
  919. const gpuBuffer = this._bufferManager.createRawBuffer(size, WebGPUConstants.BufferUsage.MapRead | WebGPUConstants.BufferUsage.CopyDst);
  920. const commandEncoder = this._device.createCommandEncoder({});
  921. commandEncoder.copyTextureToBuffer({
  922. texture,
  923. mipLevel,
  924. origin: {
  925. x,
  926. y,
  927. z: Math.max(faceIndex, 0)
  928. }
  929. }, {
  930. buffer: gpuBuffer,
  931. offset: 0,
  932. bytesPerRow: bytesPerRowAligned
  933. }, {
  934. width,
  935. height,
  936. depth: 1
  937. });
  938. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  939. const type = WebGPUTextureHelper._GetTextureTypeFromFormat(format);
  940. const floatFormat = type === Constants.TEXTURETYPE_FLOAT ? 2 : type === Constants.TEXTURETYPE_HALF_FLOAT ? 1 : 0;
  941. return this._bufferManager.readDataFromBuffer(gpuBuffer, size, width, height, bytesPerRow, bytesPerRowAligned, floatFormat, 0, buffer);
  942. }
  943. //------------------------------------------------------------------------------
  944. // Dispose
  945. //------------------------------------------------------------------------------
  946. public releaseTexture(texture: InternalTexture | GPUTexture): void {
  947. if (WebGPUTextureHelper._IsInternalTexture(texture)) {
  948. const hardwareTexture = texture._hardwareTexture;
  949. const irradianceTexture = texture._irradianceTexture;
  950. const depthStencilTexture = texture._depthStencilTexture;
  951. // We can't destroy the objects just now because they could be used in the current frame - we delay the destroying after the end of the frame
  952. this._deferredReleaseTextures.push([hardwareTexture, irradianceTexture, depthStencilTexture]);
  953. } else {
  954. this._deferredReleaseTextures.push([texture, null, null]);
  955. }
  956. }
  957. public destroyDeferredTextures(): void {
  958. for (let i = 0; i < this._deferredReleaseTextures.length; ++i) {
  959. const [hardwareTexture, irradianceTexture, depthStencilTexture] = this._deferredReleaseTextures[i];
  960. if (hardwareTexture) {
  961. if (WebGPUTextureHelper._IsHardwareTexture(hardwareTexture)) {
  962. hardwareTexture.release();
  963. } else {
  964. hardwareTexture.destroy();
  965. }
  966. }
  967. irradianceTexture?.dispose();
  968. depthStencilTexture?.dispose();
  969. }
  970. this._deferredReleaseTextures.length = 0;
  971. }
  972. }