webgpuTextureHelper.ts 53 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139
  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. vertexState: {
  169. indexFormat: WebGPUConstants.IndexFormat.Uint16
  170. },
  171. colorStates: [{
  172. format,
  173. }]
  174. });
  175. }
  176. return pipeline;
  177. }
  178. private static _GetTextureTypeFromFormat(format: GPUTextureFormat): number {
  179. switch (format) {
  180. // One Component = 8 bits
  181. case WebGPUConstants.TextureFormat.R8Unorm:
  182. case WebGPUConstants.TextureFormat.R8Snorm:
  183. case WebGPUConstants.TextureFormat.R8Uint:
  184. case WebGPUConstants.TextureFormat.R8Sint:
  185. case WebGPUConstants.TextureFormat.RG8Unorm:
  186. case WebGPUConstants.TextureFormat.RG8Snorm:
  187. case WebGPUConstants.TextureFormat.RG8Uint:
  188. case WebGPUConstants.TextureFormat.RG8Sint:
  189. case WebGPUConstants.TextureFormat.RGBA8Unorm:
  190. case WebGPUConstants.TextureFormat.RGBA8UnormSRGB:
  191. case WebGPUConstants.TextureFormat.RGBA8Snorm:
  192. case WebGPUConstants.TextureFormat.RGBA8Uint:
  193. case WebGPUConstants.TextureFormat.RGBA8Sint:
  194. case WebGPUConstants.TextureFormat.BGRA8Unorm:
  195. case WebGPUConstants.TextureFormat.BGRA8UnormSRGB:
  196. case WebGPUConstants.TextureFormat.RGB10A2Unorm: // composite format - let's say it's byte...
  197. case WebGPUConstants.TextureFormat.RGB9E5UFloat: // composite format - let's say it's byte...
  198. case WebGPUConstants.TextureFormat.RG11B10UFloat: // composite format - let's say it's byte...
  199. case WebGPUConstants.TextureFormat.Depth24UnormStencil8: // composite format - let's say it's byte...
  200. case WebGPUConstants.TextureFormat.Depth32FloatStencil8: // composite format - let's say it's byte...
  201. case WebGPUConstants.TextureFormat.BC7RGBAUnorm:
  202. case WebGPUConstants.TextureFormat.BC7RGBAUnormSRGB:
  203. case WebGPUConstants.TextureFormat.BC6HRGBUFloat:
  204. case WebGPUConstants.TextureFormat.BC6HRGBFloat:
  205. case WebGPUConstants.TextureFormat.BC5RGUnorm:
  206. case WebGPUConstants.TextureFormat.BC5RGSnorm:
  207. case WebGPUConstants.TextureFormat.BC3RGBAUnorm:
  208. case WebGPUConstants.TextureFormat.BC3RGBAUnormSRGB:
  209. case WebGPUConstants.TextureFormat.BC2RGBAUnorm:
  210. case WebGPUConstants.TextureFormat.BC2RGBAUnormSRGB:
  211. case WebGPUConstants.TextureFormat.BC4RUnorm:
  212. case WebGPUConstants.TextureFormat.BC4RSnorm:
  213. case WebGPUConstants.TextureFormat.BC1RGBAUNorm:
  214. case WebGPUConstants.TextureFormat.BC1RGBAUnormSRGB:
  215. return Constants.TEXTURETYPE_UNSIGNED_BYTE;
  216. // One component = 16 bits
  217. case WebGPUConstants.TextureFormat.R16Uint:
  218. case WebGPUConstants.TextureFormat.R16Sint:
  219. case WebGPUConstants.TextureFormat.RG16Uint:
  220. case WebGPUConstants.TextureFormat.RG16Sint:
  221. case WebGPUConstants.TextureFormat.RGBA16Uint:
  222. case WebGPUConstants.TextureFormat.RGBA16Sint:
  223. case WebGPUConstants.TextureFormat.Depth16Unorm:
  224. return Constants.TEXTURETYPE_UNSIGNED_SHORT;
  225. case WebGPUConstants.TextureFormat.R16Float:
  226. case WebGPUConstants.TextureFormat.RG16Float:
  227. case WebGPUConstants.TextureFormat.RGBA16Float:
  228. return Constants.TEXTURETYPE_HALF_FLOAT;
  229. // One component = 32 bits
  230. case WebGPUConstants.TextureFormat.R32Uint:
  231. case WebGPUConstants.TextureFormat.R32Sint:
  232. case WebGPUConstants.TextureFormat.RG32Uint:
  233. case WebGPUConstants.TextureFormat.RG32Sint:
  234. case WebGPUConstants.TextureFormat.RGBA32Uint:
  235. case WebGPUConstants.TextureFormat.RGBA32Sint:
  236. return Constants.TEXTURETYPE_UNSIGNED_INTEGER;
  237. case WebGPUConstants.TextureFormat.R32Float:
  238. case WebGPUConstants.TextureFormat.RG32Float:
  239. case WebGPUConstants.TextureFormat.RGBA32Float:
  240. case WebGPUConstants.TextureFormat.Depth32Float:
  241. return Constants.TEXTURETYPE_FLOAT;
  242. case WebGPUConstants.TextureFormat.Stencil8:
  243. throw "No fixed size for Stencil8 format!";
  244. case WebGPUConstants.TextureFormat.Depth24Plus:
  245. throw "No fixed size for Depth24Plus format!";
  246. case WebGPUConstants.TextureFormat.Depth24PlusStencil8:
  247. throw "No fixed size for Depth24PlusStencil8 format!";
  248. }
  249. return Constants.TEXTURETYPE_UNSIGNED_BYTE;
  250. }
  251. private static _GetBlockInformationFromFormat(format: GPUTextureFormat): { width: number, height: number, length: number } {
  252. switch (format) {
  253. // 8 bits formats
  254. case WebGPUConstants.TextureFormat.R8Unorm:
  255. case WebGPUConstants.TextureFormat.R8Snorm:
  256. case WebGPUConstants.TextureFormat.R8Uint:
  257. case WebGPUConstants.TextureFormat.R8Sint:
  258. return { width: 1, height: 1, length: 1 };
  259. // 16 bits formats
  260. case WebGPUConstants.TextureFormat.R16Uint:
  261. case WebGPUConstants.TextureFormat.R16Sint:
  262. case WebGPUConstants.TextureFormat.R16Float:
  263. case WebGPUConstants.TextureFormat.RG8Unorm:
  264. case WebGPUConstants.TextureFormat.RG8Snorm:
  265. case WebGPUConstants.TextureFormat.RG8Uint:
  266. case WebGPUConstants.TextureFormat.RG8Sint:
  267. return { width: 1, height: 1, length: 2 };
  268. // 32 bits formats
  269. case WebGPUConstants.TextureFormat.R32Uint:
  270. case WebGPUConstants.TextureFormat.R32Sint:
  271. case WebGPUConstants.TextureFormat.R32Float:
  272. case WebGPUConstants.TextureFormat.RG16Uint:
  273. case WebGPUConstants.TextureFormat.RG16Sint:
  274. case WebGPUConstants.TextureFormat.RG16Float:
  275. case WebGPUConstants.TextureFormat.RGBA8Unorm:
  276. case WebGPUConstants.TextureFormat.RGBA8UnormSRGB:
  277. case WebGPUConstants.TextureFormat.RGBA8Snorm:
  278. case WebGPUConstants.TextureFormat.RGBA8Uint:
  279. case WebGPUConstants.TextureFormat.RGBA8Sint:
  280. case WebGPUConstants.TextureFormat.BGRA8Unorm:
  281. case WebGPUConstants.TextureFormat.BGRA8UnormSRGB:
  282. case WebGPUConstants.TextureFormat.RGB9E5UFloat:
  283. case WebGPUConstants.TextureFormat.RGB10A2Unorm:
  284. case WebGPUConstants.TextureFormat.RG11B10UFloat:
  285. return { width: 1, height: 1, length: 4 };
  286. // 64 bits formats
  287. case WebGPUConstants.TextureFormat.RG32Uint:
  288. case WebGPUConstants.TextureFormat.RG32Sint:
  289. case WebGPUConstants.TextureFormat.RG32Float:
  290. case WebGPUConstants.TextureFormat.RGBA16Uint:
  291. case WebGPUConstants.TextureFormat.RGBA16Sint:
  292. case WebGPUConstants.TextureFormat.RGBA16Float:
  293. return { width: 1, height: 1, length: 8 };
  294. // 128 bits formats
  295. case WebGPUConstants.TextureFormat.RGBA32Uint:
  296. case WebGPUConstants.TextureFormat.RGBA32Sint:
  297. case WebGPUConstants.TextureFormat.RGBA32Float:
  298. return { width: 1, height: 1, length: 16 };
  299. // Depth and stencil formats
  300. case WebGPUConstants.TextureFormat.Stencil8:
  301. throw "No fixed size for Stencil8 format!";
  302. case WebGPUConstants.TextureFormat.Depth16Unorm:
  303. return { width: 1, height: 1, length: 2 };
  304. case WebGPUConstants.TextureFormat.Depth24Plus:
  305. throw "No fixed size for Depth24Plus format!";
  306. case WebGPUConstants.TextureFormat.Depth24PlusStencil8:
  307. throw "No fixed size for Depth24PlusStencil8 format!";
  308. case WebGPUConstants.TextureFormat.Depth32Float:
  309. return { width: 1, height: 1, length: 4 };
  310. case WebGPUConstants.TextureFormat.Depth24UnormStencil8:
  311. return { width: 1, height: 1, length: 4 };
  312. case WebGPUConstants.TextureFormat.Depth32FloatStencil8:
  313. return { width: 1, height: 1, length: 5 };
  314. // BC compressed formats usable if "texture-compression-bc" is both
  315. // supported by the device/user agent and enabled in requestDevice.
  316. case WebGPUConstants.TextureFormat.BC7RGBAUnorm:
  317. case WebGPUConstants.TextureFormat.BC7RGBAUnormSRGB:
  318. case WebGPUConstants.TextureFormat.BC6HRGBUFloat:
  319. case WebGPUConstants.TextureFormat.BC6HRGBFloat:
  320. case WebGPUConstants.TextureFormat.BC5RGUnorm:
  321. case WebGPUConstants.TextureFormat.BC5RGSnorm:
  322. case WebGPUConstants.TextureFormat.BC3RGBAUnorm:
  323. case WebGPUConstants.TextureFormat.BC3RGBAUnormSRGB:
  324. case WebGPUConstants.TextureFormat.BC2RGBAUnorm:
  325. case WebGPUConstants.TextureFormat.BC2RGBAUnormSRGB:
  326. return { width: 4, height: 4, length: 16 };
  327. case WebGPUConstants.TextureFormat.BC4RUnorm:
  328. case WebGPUConstants.TextureFormat.BC4RSnorm:
  329. case WebGPUConstants.TextureFormat.BC1RGBAUNorm:
  330. case WebGPUConstants.TextureFormat.BC1RGBAUnormSRGB:
  331. return { width: 4, height: 4, length: 8 };
  332. }
  333. return { width: 1, height: 1, length: 4 };
  334. }
  335. private static _IsHardwareTexture(texture: HardwareTextureWrapper | GPUTexture): texture is HardwareTextureWrapper {
  336. return !!(texture as HardwareTextureWrapper).release;
  337. }
  338. private static _IsInternalTexture(texture: InternalTexture | GPUTexture): texture is InternalTexture {
  339. return !!(texture as InternalTexture).dispose;
  340. }
  341. public static GetCompareFunction(compareFunction: Nullable<number>): GPUCompareFunction {
  342. switch (compareFunction) {
  343. case Constants.ALWAYS:
  344. return WebGPUConstants.CompareFunction.Always;
  345. case Constants.EQUAL:
  346. return WebGPUConstants.CompareFunction.Equal;
  347. case Constants.GREATER:
  348. return WebGPUConstants.CompareFunction.Greater;
  349. case Constants.GEQUAL:
  350. return WebGPUConstants.CompareFunction.GreaterEqual;
  351. case Constants.LESS:
  352. return WebGPUConstants.CompareFunction.Less;
  353. case Constants.LEQUAL:
  354. return WebGPUConstants.CompareFunction.LessEqual;
  355. case Constants.NEVER:
  356. return WebGPUConstants.CompareFunction.Never;
  357. case Constants.NOTEQUAL:
  358. return WebGPUConstants.CompareFunction.NotEqual;
  359. default:
  360. return WebGPUConstants.CompareFunction.Less;
  361. }
  362. }
  363. public static IsImageBitmap(imageBitmap: ImageBitmap | { width: number, height: number }): imageBitmap is ImageBitmap {
  364. return (imageBitmap as ImageBitmap).close !== undefined;
  365. }
  366. public static IsImageBitmapArray(imageBitmap: ImageBitmap[] | { width: number, height: number }): imageBitmap is ImageBitmap[] {
  367. return Array.isArray(imageBitmap as ImageBitmap[]) && (imageBitmap as ImageBitmap[])[0].close !== undefined;
  368. }
  369. public setCommandEncoder(encoder: GPUCommandEncoder): void {
  370. this._commandEncoderForCreation = encoder;
  371. }
  372. public static IsCompressedFormat(format: GPUTextureFormat): boolean {
  373. switch (format) {
  374. case WebGPUConstants.TextureFormat.BC7RGBAUnormSRGB:
  375. case WebGPUConstants.TextureFormat.BC7RGBAUnorm:
  376. case WebGPUConstants.TextureFormat.BC6HRGBFloat:
  377. case WebGPUConstants.TextureFormat.BC6HRGBUFloat:
  378. case WebGPUConstants.TextureFormat.BC5RGSnorm:
  379. case WebGPUConstants.TextureFormat.BC5RGUnorm:
  380. case WebGPUConstants.TextureFormat.BC4RSnorm:
  381. case WebGPUConstants.TextureFormat.BC4RUnorm:
  382. case WebGPUConstants.TextureFormat.BC3RGBAUnormSRGB:
  383. case WebGPUConstants.TextureFormat.BC3RGBAUnorm:
  384. case WebGPUConstants.TextureFormat.BC2RGBAUnormSRGB:
  385. case WebGPUConstants.TextureFormat.BC2RGBAUnorm:
  386. case WebGPUConstants.TextureFormat.BC1RGBAUnormSRGB:
  387. case WebGPUConstants.TextureFormat.BC1RGBAUNorm:
  388. return true;
  389. }
  390. return false;
  391. }
  392. public static GetWebGPUTextureFormat(type: number, format: number): GPUTextureFormat {
  393. switch (format) {
  394. case Constants.TEXTUREFORMAT_DEPTH24_STENCIL8:
  395. return WebGPUConstants.TextureFormat.Depth24PlusStencil8;
  396. case Constants.TEXTUREFORMAT_DEPTH32_FLOAT:
  397. return WebGPUConstants.TextureFormat.Depth32Float;
  398. case Constants.TEXTUREFORMAT_COMPRESSED_RGBA_BPTC_UNORM:
  399. return WebGPUConstants.TextureFormat.BC7RGBAUnorm;
  400. case Constants.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
  401. return WebGPUConstants.TextureFormat.BC6HRGBUFloat;
  402. case Constants.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:
  403. return WebGPUConstants.TextureFormat.BC6HRGBFloat;
  404. case Constants.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT5:
  405. return WebGPUConstants.TextureFormat.BC3RGBAUnorm;
  406. case Constants.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT3:
  407. return WebGPUConstants.TextureFormat.BC2RGBAUnorm;
  408. case Constants.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT1:
  409. return WebGPUConstants.TextureFormat.BC1RGBAUNorm;
  410. }
  411. switch (type) {
  412. case Constants.TEXTURETYPE_BYTE:
  413. switch (format) {
  414. case Constants.TEXTUREFORMAT_RED:
  415. return WebGPUConstants.TextureFormat.R8Snorm;
  416. case Constants.TEXTUREFORMAT_RG:
  417. return WebGPUConstants.TextureFormat.RG8Snorm;
  418. case Constants.TEXTUREFORMAT_RGB:
  419. throw "RGB format not supported in WebGPU";
  420. case Constants.TEXTUREFORMAT_RED_INTEGER:
  421. return WebGPUConstants.TextureFormat.R8Sint;
  422. case Constants.TEXTUREFORMAT_RG_INTEGER:
  423. return WebGPUConstants.TextureFormat.RG8Sint;
  424. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  425. throw "RGB_INTEGER format not supported in WebGPU";
  426. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  427. return WebGPUConstants.TextureFormat.RGBA8Sint;
  428. default:
  429. return WebGPUConstants.TextureFormat.RGBA8Snorm;
  430. }
  431. case Constants.TEXTURETYPE_UNSIGNED_BYTE:
  432. switch (format) {
  433. case Constants.TEXTUREFORMAT_RED:
  434. return WebGPUConstants.TextureFormat.R8Unorm;
  435. case Constants.TEXTUREFORMAT_RG:
  436. return WebGPUConstants.TextureFormat.RG8Unorm;
  437. case Constants.TEXTUREFORMAT_RGB:
  438. throw "TEXTUREFORMAT_RGB format not supported in WebGPU";
  439. case Constants.TEXTUREFORMAT_RGBA:
  440. return WebGPUConstants.TextureFormat.RGBA8Unorm;
  441. case Constants.TEXTUREFORMAT_BGRA:
  442. return WebGPUConstants.TextureFormat.BGRA8Unorm;
  443. case Constants.TEXTUREFORMAT_RED_INTEGER:
  444. return WebGPUConstants.TextureFormat.R8Uint;
  445. case Constants.TEXTUREFORMAT_RG_INTEGER:
  446. return WebGPUConstants.TextureFormat.RG8Uint;
  447. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  448. throw "RGB_INTEGER format not supported in WebGPU";
  449. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  450. return WebGPUConstants.TextureFormat.RGBA8Uint;
  451. case Constants.TEXTUREFORMAT_ALPHA:
  452. throw "TEXTUREFORMAT_ALPHA format not supported in WebGPU";
  453. case Constants.TEXTUREFORMAT_LUMINANCE:
  454. throw "TEXTUREFORMAT_LUMINANCE format not supported in WebGPU";
  455. case Constants.TEXTUREFORMAT_LUMINANCE_ALPHA:
  456. throw "TEXTUREFORMAT_LUMINANCE_ALPHA format not supported in WebGPU";
  457. default:
  458. return WebGPUConstants.TextureFormat.RGBA8Unorm;
  459. }
  460. case Constants.TEXTURETYPE_SHORT:
  461. switch (format) {
  462. case Constants.TEXTUREFORMAT_RED_INTEGER:
  463. return WebGPUConstants.TextureFormat.R16Sint;
  464. case Constants.TEXTUREFORMAT_RG_INTEGER:
  465. return WebGPUConstants.TextureFormat.RG16Sint;
  466. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  467. throw "TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";
  468. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  469. return WebGPUConstants.TextureFormat.RGBA16Sint;
  470. default:
  471. return WebGPUConstants.TextureFormat.RGBA16Sint;
  472. }
  473. case Constants.TEXTURETYPE_UNSIGNED_SHORT:
  474. switch (format) {
  475. case Constants.TEXTUREFORMAT_RED_INTEGER:
  476. return WebGPUConstants.TextureFormat.R16Uint;
  477. case Constants.TEXTUREFORMAT_RG_INTEGER:
  478. return WebGPUConstants.TextureFormat.RG16Uint;
  479. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  480. throw "TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";
  481. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  482. return WebGPUConstants.TextureFormat.RGBA16Uint;
  483. default:
  484. return WebGPUConstants.TextureFormat.RGBA16Uint;
  485. }
  486. case Constants.TEXTURETYPE_INT:
  487. switch (format) {
  488. case Constants.TEXTUREFORMAT_RED_INTEGER:
  489. return WebGPUConstants.TextureFormat.R32Sint;
  490. case Constants.TEXTUREFORMAT_RG_INTEGER:
  491. return WebGPUConstants.TextureFormat.RG32Sint;
  492. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  493. throw "TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";
  494. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  495. return WebGPUConstants.TextureFormat.RGBA32Sint;
  496. default:
  497. return WebGPUConstants.TextureFormat.RGBA32Sint;
  498. }
  499. case Constants.TEXTURETYPE_UNSIGNED_INTEGER: // Refers to UNSIGNED_INT
  500. switch (format) {
  501. case Constants.TEXTUREFORMAT_RED_INTEGER:
  502. return WebGPUConstants.TextureFormat.R32Uint;
  503. case Constants.TEXTUREFORMAT_RG_INTEGER:
  504. return WebGPUConstants.TextureFormat.RG32Uint;
  505. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  506. throw "TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";
  507. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  508. return WebGPUConstants.TextureFormat.RGBA32Uint;
  509. default:
  510. return WebGPUConstants.TextureFormat.RGBA32Uint;
  511. }
  512. case Constants.TEXTURETYPE_FLOAT:
  513. switch (format) {
  514. case Constants.TEXTUREFORMAT_RED:
  515. return WebGPUConstants.TextureFormat.R32Float; // By default. Other possibility is R16Float.
  516. case Constants.TEXTUREFORMAT_RG:
  517. return WebGPUConstants.TextureFormat.RG32Float; // By default. Other possibility is RG16Float.
  518. case Constants.TEXTUREFORMAT_RGB:
  519. throw "TEXTUREFORMAT_RGB format not supported in WebGPU";
  520. case Constants.TEXTUREFORMAT_RGBA:
  521. return WebGPUConstants.TextureFormat.RGBA32Float; // By default. Other possibility is RGBA16Float.
  522. default:
  523. return WebGPUConstants.TextureFormat.RGBA32Float;
  524. }
  525. case Constants.TEXTURETYPE_HALF_FLOAT:
  526. switch (format) {
  527. case Constants.TEXTUREFORMAT_RED:
  528. return WebGPUConstants.TextureFormat.R16Float;
  529. case Constants.TEXTUREFORMAT_RG:
  530. return WebGPUConstants.TextureFormat.RG16Float;
  531. case Constants.TEXTUREFORMAT_RGB:
  532. throw "TEXTUREFORMAT_RGB format not supported in WebGPU";
  533. case Constants.TEXTUREFORMAT_RGBA:
  534. return WebGPUConstants.TextureFormat.RGBA16Float;
  535. default:
  536. return WebGPUConstants.TextureFormat.RGBA16Float;
  537. }
  538. case Constants.TEXTURETYPE_UNSIGNED_SHORT_5_6_5:
  539. throw "TEXTURETYPE_UNSIGNED_SHORT_5_6_5 format not supported in WebGPU";
  540. case Constants.TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV:
  541. throw "TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV format not supported in WebGPU";
  542. case Constants.TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV:
  543. throw "TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV format not supported in WebGPU";
  544. case Constants.TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4:
  545. throw "TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4 format not supported in WebGPU";
  546. case Constants.TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1:
  547. throw "TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1 format not supported in WebGPU";
  548. case Constants.TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV:
  549. switch (format) {
  550. case Constants.TEXTUREFORMAT_RGBA:
  551. return WebGPUConstants.TextureFormat.RGB10A2Unorm;
  552. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  553. throw "TEXTUREFORMAT_RGBA_INTEGER format not supported in WebGPU when type is TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV";
  554. default:
  555. return WebGPUConstants.TextureFormat.RGB10A2Unorm;
  556. }
  557. }
  558. return WebGPUConstants.TextureFormat.RGBA8Unorm;
  559. }
  560. public invertYPreMultiplyAlpha(gpuTexture: GPUTexture, width: number, height: number, format: GPUTextureFormat, invertY = false, premultiplyAlpha = false, faceIndex= 0, commandEncoder?: GPUCommandEncoder): void {
  561. const useOwnCommandEncoder = commandEncoder === undefined;
  562. const pipeline = this._getPipeline(format, PipelineType.InvertYPremultiplyAlpha, { invertY, premultiplyAlpha });
  563. const bindGroupLayout = pipeline.getBindGroupLayout(0);
  564. if (useOwnCommandEncoder) {
  565. commandEncoder = this._device.createCommandEncoder({});
  566. }
  567. commandEncoder!.pushDebugGroup(`internal process texture - invertY=${invertY} premultiplyAlpha=${premultiplyAlpha}`);
  568. 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);
  569. const passEncoder = commandEncoder!.beginRenderPass({
  570. colorAttachments: [{
  571. attachment: outputTexture.createView({
  572. dimension: WebGPUConstants.TextureViewDimension.E2d,
  573. baseMipLevel: 0,
  574. mipLevelCount: 1,
  575. arrayLayerCount: 1,
  576. baseArrayLayer: 0,
  577. }),
  578. loadValue: WebGPUConstants.LoadOp.Load,
  579. }],
  580. });
  581. const bindGroup = this._device.createBindGroup({
  582. layout: bindGroupLayout,
  583. entries: [{
  584. binding: 0,
  585. resource: this._invertYPreMultiplyAlphaSampler,
  586. }, {
  587. binding: 1,
  588. resource: gpuTexture.createView({
  589. dimension: WebGPUConstants.TextureViewDimension.E2d,
  590. baseMipLevel: 0,
  591. mipLevelCount: 1,
  592. arrayLayerCount: 1,
  593. baseArrayLayer: faceIndex,
  594. }),
  595. }],
  596. });
  597. passEncoder.setPipeline(pipeline);
  598. passEncoder.setBindGroup(0, bindGroup);
  599. passEncoder.draw(4, 1, 0, 0);
  600. passEncoder.endPass();
  601. commandEncoder!.copyTextureToTexture({
  602. texture: outputTexture,
  603. }, {
  604. texture: gpuTexture,
  605. origin: {
  606. x: 0,
  607. y: 0,
  608. z: Math.max(faceIndex, 0),
  609. }
  610. }, {
  611. width,
  612. height,
  613. depth: 1,
  614. }
  615. );
  616. this._deferredReleaseTextures.push([outputTexture, null, null]);
  617. commandEncoder!.popDebugGroup();
  618. if (useOwnCommandEncoder) {
  619. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  620. commandEncoder = null as any;
  621. }
  622. }
  623. public clear(format: GPUTextureFormat, color: IColor4Like, passEncoder: GPURenderPassEncoder): void {
  624. const pipeline = this._getPipeline(format, PipelineType.Clear);
  625. const bindGroupLayout = pipeline.getBindGroupLayout(0);
  626. const buffer = this._bufferManager.createRawBuffer(4 * 4, WebGPUConstants.BufferUsage.CopySrc | WebGPUConstants.BufferUsage.Uniform, true);
  627. /*const arrayBuffer = buffer.getMappedRange();
  628. new Float32Array(arrayBuffer).set([color.r, color.g, color.b, color.a]);
  629. buffer.unmap();*/
  630. const bindGroup = this._device.createBindGroup({
  631. layout: bindGroupLayout,
  632. entries: [{
  633. binding: 0,
  634. resource: {
  635. buffer,
  636. },
  637. }],
  638. });
  639. passEncoder.setPipeline(pipeline);
  640. passEncoder.setBindGroup(0, bindGroup);
  641. passEncoder.draw(4, 1, 0, 0);
  642. this._bufferManager.releaseBuffer(buffer);
  643. }
  644. //------------------------------------------------------------------------------
  645. // Creation
  646. //------------------------------------------------------------------------------
  647. 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,
  648. sampleCount = 1, commandEncoder?: GPUCommandEncoder, usage = -1): GPUTexture
  649. {
  650. if (sampleCount > 1) {
  651. // TODO WEBGPU for the time being, Chrome only accepts values of 1 or 4
  652. sampleCount = 4;
  653. }
  654. const layerCount = (imageBitmap as any).layers || 1;
  655. let textureSize = {
  656. width: imageBitmap.width,
  657. height: imageBitmap.height,
  658. depth: layerCount,
  659. };
  660. const mipLevelCount = hasMipmaps ? WebGPUTextureHelper.ComputeNumMipmapLevels(imageBitmap.width, imageBitmap.height) : 1;
  661. const usages = usage >= 0 ? usage : WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.CopyDst | WebGPUConstants.TextureUsage.Sampled;
  662. const additionalUsages = hasMipmaps && !WebGPUTextureHelper.IsCompressedFormat(format) ? WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.OutputAttachment : 0;
  663. const gpuTexture = this._device.createTexture({
  664. size: textureSize,
  665. dimension: is3D ? WebGPUConstants.TextureDimension.E3d : WebGPUConstants.TextureDimension.E2d,
  666. format,
  667. usage: usages | additionalUsages,
  668. sampleCount,
  669. mipLevelCount
  670. });
  671. if (WebGPUTextureHelper.IsImageBitmap(imageBitmap)) {
  672. this.updateTexture(imageBitmap, gpuTexture, imageBitmap.width, imageBitmap.height, layerCount, format, 0, 0, invertY, premultiplyAlpha, 0, 0, commandEncoder);
  673. if (hasMipmaps && generateMipmaps) {
  674. this.generateMipmaps(gpuTexture, format, mipLevelCount, 0, commandEncoder);
  675. }
  676. }
  677. return gpuTexture;
  678. }
  679. public createCubeTexture(imageBitmaps: ImageBitmap[] | { width: number, height: number }, hasMipmaps = false, generateMipmaps = false, invertY = false, premultiplyAlpha = false, format: GPUTextureFormat = WebGPUConstants.TextureFormat.RGBA8Unorm,
  680. sampleCount = 1, commandEncoder?: GPUCommandEncoder, usage = -1): GPUTexture
  681. {
  682. if (sampleCount > 1) {
  683. // TODO WEBGPU for the time being, Chrome only accepts values of 1 or 4
  684. sampleCount = 4;
  685. }
  686. const width = WebGPUTextureHelper.IsImageBitmapArray(imageBitmaps) ? imageBitmaps[0].width : imageBitmaps.width;
  687. const height = WebGPUTextureHelper.IsImageBitmapArray(imageBitmaps) ? imageBitmaps[0].height : imageBitmaps.height;
  688. const mipLevelCount = hasMipmaps ? WebGPUTextureHelper.ComputeNumMipmapLevels(width, height) : 1;
  689. const usages = usage >= 0 ? usage : WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.CopyDst | WebGPUConstants.TextureUsage.Sampled;
  690. const additionalUsages = hasMipmaps && !WebGPUTextureHelper.IsCompressedFormat(format) ? WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.OutputAttachment : 0;
  691. const gpuTexture = this._device.createTexture({
  692. size: {
  693. width,
  694. height,
  695. depth: 6,
  696. },
  697. dimension: WebGPUConstants.TextureDimension.E2d,
  698. format,
  699. usage: usages | additionalUsages,
  700. sampleCount,
  701. mipLevelCount
  702. });
  703. if (WebGPUTextureHelper.IsImageBitmapArray(imageBitmaps)) {
  704. this.updateCubeTextures(imageBitmaps, gpuTexture, width, height, format, invertY, premultiplyAlpha, 0, 0, commandEncoder);
  705. if (hasMipmaps && generateMipmaps) {
  706. this.generateCubeMipmaps(gpuTexture, format, mipLevelCount, commandEncoder);
  707. }
  708. }
  709. return gpuTexture;
  710. }
  711. public generateCubeMipmaps(gpuTexture: GPUTexture, format: GPUTextureFormat, mipLevelCount: number, commandEncoder?: GPUCommandEncoder): void {
  712. const useOwnCommandEncoder = commandEncoder === undefined;
  713. if (useOwnCommandEncoder) {
  714. commandEncoder = this._device.createCommandEncoder({});
  715. }
  716. commandEncoder!.pushDebugGroup(`create cube mipmaps - ${mipLevelCount} levels`);
  717. for (let f = 0; f < 6; ++f) {
  718. this.generateMipmaps(gpuTexture, format, mipLevelCount, f, commandEncoder);
  719. }
  720. commandEncoder!.popDebugGroup();
  721. if (useOwnCommandEncoder) {
  722. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  723. commandEncoder = null as any;
  724. }
  725. }
  726. public generateMipmaps(gpuTexture: GPUTexture, format: GPUTextureFormat, mipLevelCount: number, faceIndex= 0, commandEncoder?: GPUCommandEncoder): void {
  727. const useOwnCommandEncoder = commandEncoder === undefined;
  728. const pipeline = this._getPipeline(format);
  729. const bindGroupLayout = pipeline.getBindGroupLayout(0);
  730. if (useOwnCommandEncoder) {
  731. commandEncoder = this._device.createCommandEncoder({});
  732. }
  733. commandEncoder!.pushDebugGroup(`create mipmaps for face #${faceIndex} - ${mipLevelCount} levels`);
  734. for (let i = 1; i < mipLevelCount; ++i) {
  735. const passEncoder = commandEncoder!.beginRenderPass({
  736. colorAttachments: [{
  737. attachment: gpuTexture.createView({
  738. dimension: WebGPUConstants.TextureViewDimension.E2d,
  739. baseMipLevel: i,
  740. mipLevelCount: 1,
  741. arrayLayerCount: 1,
  742. baseArrayLayer: faceIndex,
  743. }),
  744. loadValue: WebGPUConstants.LoadOp.Load,
  745. }],
  746. });
  747. const bindGroup = this._device.createBindGroup({
  748. layout: bindGroupLayout,
  749. entries: [{
  750. binding: 0,
  751. resource: this._mipmapSampler,
  752. }, {
  753. binding: 1,
  754. resource: gpuTexture.createView({
  755. dimension: WebGPUConstants.TextureViewDimension.E2d,
  756. baseMipLevel: i - 1,
  757. mipLevelCount: 1,
  758. arrayLayerCount: 1,
  759. baseArrayLayer: faceIndex,
  760. }),
  761. }],
  762. });
  763. passEncoder.setPipeline(pipeline);
  764. passEncoder.setBindGroup(0, bindGroup);
  765. passEncoder.draw(4, 1, 0, 0);
  766. passEncoder.endPass();
  767. }
  768. commandEncoder!.popDebugGroup();
  769. if (useOwnCommandEncoder) {
  770. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  771. commandEncoder = null as any;
  772. }
  773. }
  774. public createGPUTextureForInternalTexture(texture: InternalTexture, width?: number, height?: number, depth?: number): WebGPUHardwareTexture {
  775. if (!texture._hardwareTexture) {
  776. texture._hardwareTexture = new WebGPUHardwareTexture();
  777. }
  778. if (width === undefined) {
  779. width = texture.width;
  780. }
  781. if (height === undefined) {
  782. height = texture.height;
  783. }
  784. if (depth === undefined) {
  785. depth = texture.depth;
  786. }
  787. const gpuTextureWrapper = texture._hardwareTexture as WebGPUHardwareTexture;
  788. gpuTextureWrapper.format = WebGPUTextureHelper.GetWebGPUTextureFormat(texture.type, texture.format);
  789. gpuTextureWrapper.textureUsages =
  790. texture._source === InternalTextureSource.RenderTarget || texture.source === InternalTextureSource.MultiRenderTarget ? WebGPUConstants.TextureUsage.Sampled | WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.OutputAttachment :
  791. texture._source === InternalTextureSource.Depth ? WebGPUConstants.TextureUsage.Sampled | WebGPUConstants.TextureUsage.OutputAttachment : -1;
  792. const hasMipMaps = texture.generateMipMaps;
  793. const layerCount = depth || 1;
  794. if (texture.isCube) {
  795. const gpuTexture = this.createCubeTexture({ width, height }, texture.generateMipMaps, texture.generateMipMaps, texture.invertY, false, gpuTextureWrapper.format, 1, this._commandEncoderForCreation, gpuTextureWrapper.textureUsages);
  796. gpuTextureWrapper.set(gpuTexture);
  797. gpuTextureWrapper.createView({
  798. dimension: WebGPUConstants.TextureViewDimension.Cube,
  799. mipLevelCount: hasMipMaps ? WebGPUTextureHelper.ComputeNumMipmapLevels(width!, height!) : 1,
  800. baseArrayLayer: 0,
  801. baseMipLevel: 0,
  802. aspect: WebGPUConstants.TextureAspect.All
  803. });
  804. } else {
  805. const gpuTexture = this.createTexture({ width, height, layers: layerCount }, texture.generateMipMaps, texture.generateMipMaps, texture.invertY, false, texture.is3D, gpuTextureWrapper.format, 1, this._commandEncoderForCreation, gpuTextureWrapper.textureUsages);
  806. gpuTextureWrapper.set(gpuTexture);
  807. gpuTextureWrapper.createView({
  808. dimension: texture.is2DArray ? WebGPUConstants.TextureViewDimension.E2dArray : texture.is3D ? WebGPUConstants.TextureDimension.E3d : WebGPUConstants.TextureViewDimension.E2d,
  809. mipLevelCount: hasMipMaps ? WebGPUTextureHelper.ComputeNumMipmapLevels(width!, height!) : 1,
  810. baseArrayLayer: 0,
  811. baseMipLevel: 0,
  812. arrayLayerCount: layerCount,
  813. aspect: WebGPUConstants.TextureAspect.All
  814. });
  815. }
  816. texture.width = texture.baseWidth = width;
  817. texture.height = texture.baseHeight = height;
  818. texture.depth = texture.baseDepth = depth;
  819. this.createMSAATexture(texture, texture.samples);
  820. return gpuTextureWrapper;
  821. }
  822. public createMSAATexture(texture: InternalTexture, samples: number): void {
  823. const gpuTextureWrapper = texture._hardwareTexture as Nullable<WebGPUHardwareTexture>;
  824. if (gpuTextureWrapper?.msaaTexture) {
  825. this.releaseTexture(gpuTextureWrapper.msaaTexture);
  826. gpuTextureWrapper.msaaTexture = null;
  827. }
  828. if (!gpuTextureWrapper || (samples ?? 1) <= 1) {
  829. return;
  830. }
  831. const width = texture.width;
  832. const height = texture.height;
  833. const layerCount = texture.depth || 1;
  834. if (texture.isCube) {
  835. const gpuMSAATexture = this.createCubeTexture({ width, height }, false, false, texture.invertY, false, gpuTextureWrapper.format, samples, this._commandEncoderForCreation, gpuTextureWrapper.textureUsages);
  836. gpuTextureWrapper.setMSAATexture(gpuMSAATexture);
  837. } else {
  838. const gpuMSAATexture = this.createTexture({ width, height, layers: layerCount }, false, false, texture.invertY, false, texture.is3D, gpuTextureWrapper.format, samples, this._commandEncoderForCreation, gpuTextureWrapper.textureUsages);
  839. gpuTextureWrapper.setMSAATexture(gpuMSAATexture);
  840. }
  841. }
  842. //------------------------------------------------------------------------------
  843. // Update
  844. //------------------------------------------------------------------------------
  845. public updateCubeTextures(imageBitmaps: ImageBitmap[] | Uint8Array[], gpuTexture: GPUTexture, width: number, height: number, format: GPUTextureFormat, invertY = false, premultiplyAlpha = false, offsetX = 0, offsetY = 0,
  846. commandEncoder?: GPUCommandEncoder): void {
  847. const faces = [0, 3, 1, 4, 2, 5];
  848. for (let f = 0; f < faces.length; ++f) {
  849. let imageBitmap = imageBitmaps[faces[f]];
  850. this.updateTexture(imageBitmap, gpuTexture, width, height, 1, format, f, 0, invertY, premultiplyAlpha, offsetX, offsetY, commandEncoder);
  851. }
  852. }
  853. // TODO WEBGPU handle data source not being in the same format than the destination texture?
  854. 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,
  855. commandEncoder?: GPUCommandEncoder): void
  856. {
  857. const blockInformation = WebGPUTextureHelper._GetBlockInformationFromFormat(format);
  858. const textureCopyView: GPUTextureCopyView = {
  859. texture: gpuTexture,
  860. origin: {
  861. x: offsetX,
  862. y: offsetY,
  863. z: Math.max(faceIndex, 0)
  864. },
  865. mipLevel: mipLevel
  866. };
  867. const textureExtent = {
  868. width: Math.ceil(width / blockInformation.width) * blockInformation.width,
  869. height: Math.ceil(height / blockInformation.height) * blockInformation.height,
  870. depth: layers || 1
  871. };
  872. if ((imageBitmap as Uint8Array).byteLength !== undefined) {
  873. imageBitmap = imageBitmap as Uint8Array;
  874. const bytesPerRow = Math.ceil(width / blockInformation.width) * blockInformation.length;
  875. const aligned = Math.ceil(bytesPerRow / 256) * 256 === bytesPerRow;
  876. if (aligned) {
  877. const useOwnCommandEncoder = commandEncoder === undefined;
  878. if (useOwnCommandEncoder) {
  879. commandEncoder = this._device.createCommandEncoder({});
  880. }
  881. const buffer = this._bufferManager.createRawBuffer(imageBitmap.byteLength, WebGPUConstants.BufferUsage.MapWrite | WebGPUConstants.BufferUsage.CopySrc, true);
  882. const arrayBuffer = buffer.getMappedRange();
  883. new Uint8Array(arrayBuffer).set(imageBitmap);
  884. buffer.unmap();
  885. commandEncoder!.copyBufferToTexture({
  886. buffer: buffer,
  887. offset: 0,
  888. bytesPerRow
  889. }, textureCopyView, textureExtent);
  890. if (useOwnCommandEncoder) {
  891. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  892. commandEncoder = null as any;
  893. }
  894. this._bufferManager.releaseBuffer(buffer);
  895. } else {
  896. this._device.defaultQueue.writeTexture(textureCopyView, imageBitmap, {
  897. offset: 0,
  898. bytesPerRow
  899. }, textureExtent);
  900. }
  901. if (invertY || premultiplyAlpha) {
  902. this.invertYPreMultiplyAlpha(gpuTexture, width, height, format, invertY, premultiplyAlpha, faceIndex, commandEncoder);
  903. }
  904. } else {
  905. imageBitmap = imageBitmap as ImageBitmap;
  906. if (invertY || premultiplyAlpha) {
  907. createImageBitmap(imageBitmap, { imageOrientation: invertY ? "flipY" : "none", premultiplyAlpha: premultiplyAlpha ? "premultiply" : "none" }).then((imageBitmap) => {
  908. this._device.defaultQueue.copyImageBitmapToTexture({ imageBitmap }, textureCopyView, textureExtent);
  909. });
  910. } else {
  911. this._device.defaultQueue.copyImageBitmapToTexture({ imageBitmap }, textureCopyView, textureExtent);
  912. }
  913. }
  914. }
  915. 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> {
  916. const blockInformation = WebGPUTextureHelper._GetBlockInformationFromFormat(format);
  917. const bytesPerRow = Math.ceil(width / blockInformation.width) * blockInformation.length;
  918. const bytesPerRowAligned = Math.ceil(bytesPerRow / 256) * 256;
  919. const size = bytesPerRowAligned * height;
  920. const gpuBuffer = this._bufferManager.createRawBuffer(size, WebGPUConstants.BufferUsage.MapRead | WebGPUConstants.BufferUsage.CopyDst);
  921. const commandEncoder = this._device.createCommandEncoder({});
  922. commandEncoder.copyTextureToBuffer({
  923. texture,
  924. mipLevel,
  925. origin: {
  926. x,
  927. y,
  928. z: Math.max(faceIndex, 0)
  929. }
  930. }, {
  931. buffer: gpuBuffer,
  932. offset: 0,
  933. bytesPerRow: bytesPerRowAligned
  934. }, {
  935. width,
  936. height,
  937. depth: 1
  938. });
  939. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  940. const type = WebGPUTextureHelper._GetTextureTypeFromFormat(format);
  941. const floatFormat = type === Constants.TEXTURETYPE_FLOAT ? 2 : type === Constants.TEXTURETYPE_HALF_FLOAT ? 1 : 0;
  942. return this._bufferManager.readDataFromBuffer(gpuBuffer, size, width, height, bytesPerRow, bytesPerRowAligned, floatFormat, 0, buffer);
  943. }
  944. //------------------------------------------------------------------------------
  945. // Dispose
  946. //------------------------------------------------------------------------------
  947. public releaseTexture(texture: InternalTexture | GPUTexture): void {
  948. if (WebGPUTextureHelper._IsInternalTexture(texture)) {
  949. const hardwareTexture = texture._hardwareTexture;
  950. const irradianceTexture = texture._irradianceTexture;
  951. const depthStencilTexture = texture._depthStencilTexture;
  952. // 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
  953. this._deferredReleaseTextures.push([hardwareTexture, irradianceTexture, depthStencilTexture]);
  954. } else {
  955. this._deferredReleaseTextures.push([texture, null, null]);
  956. }
  957. }
  958. public destroyDeferredTextures(): void {
  959. for (let i = 0; i < this._deferredReleaseTextures.length; ++i) {
  960. const [hardwareTexture, irradianceTexture, depthStencilTexture] = this._deferredReleaseTextures[i];
  961. if (hardwareTexture) {
  962. if (WebGPUTextureHelper._IsHardwareTexture(hardwareTexture)) {
  963. hardwareTexture.release();
  964. } else {
  965. hardwareTexture.destroy();
  966. }
  967. }
  968. irradianceTexture?.dispose();
  969. depthStencilTexture?.dispose();
  970. }
  971. this._deferredReleaseTextures.length = 0;
  972. }
  973. }