webgpuTextureHelper.ts 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260
  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 { Engine } from '../engine';
  27. import { WebGPUHardwareTexture } from './webgpuHardwareTexture';
  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 filterToBits = [
  76. 0 | 0 << 1 | 0 << 2, // not used
  77. 0 | 0 << 1 | 0 << 2, // TEXTURE_NEAREST_SAMPLINGMODE / TEXTURE_NEAREST_NEAREST
  78. 1 | 1 << 1 | 0 << 2, // TEXTURE_BILINEAR_SAMPLINGMODE / TEXTURE_LINEAR_LINEAR
  79. 1 | 1 << 1 | 1 << 2, // TEXTURE_TRILINEAR_SAMPLINGMODE / TEXTURE_LINEAR_LINEAR_MIPLINEAR
  80. 0 | 0 << 1 | 0 << 2, // TEXTURE_NEAREST_NEAREST_MIPNEAREST
  81. 0 | 1 << 1 | 0 << 2, // TEXTURE_NEAREST_LINEAR_MIPNEAREST
  82. 0 | 1 << 1 | 1 << 2, // TEXTURE_NEAREST_LINEAR_MIPLINEAR
  83. 0 | 1 << 1 | 0 << 2, // TEXTURE_NEAREST_LINEAR
  84. 0 | 0 << 1 | 1 << 2, // TEXTURE_NEAREST_NEAREST_MIPLINEAR
  85. 1 | 0 << 1 | 0 << 2, // TEXTURE_LINEAR_NEAREST_MIPNEAREST
  86. 1 | 0 << 1 | 1 << 2, // TEXTURE_LINEAR_NEAREST_MIPLINEAR
  87. 1 | 1 << 1 | 0 << 2, // TEXTURE_LINEAR_LINEAR_MIPNEAREST
  88. 1 | 0 << 1 | 0 << 2, // TEXTURE_LINEAR_NEAREST
  89. ];
  90. // subtract 0x01FF from the comparison function value before indexing this array!
  91. const comparisonFunctionToBits = [
  92. 0 << 3 | 0 << 4 | 0 << 5 | 0 << 6, // undefined
  93. 0 << 3 | 0 << 4 | 0 << 5 | 1 << 6, // NEVER
  94. 0 << 3 | 0 << 4 | 1 << 5 | 0 << 6, // LESS
  95. 0 << 3 | 0 << 4 | 1 << 5 | 1 << 6, // EQUAL
  96. 0 << 3 | 1 << 4 | 0 << 5 | 0 << 6, // LEQUAL
  97. 0 << 3 | 1 << 4 | 0 << 5 | 1 << 6, // GREATER
  98. 0 << 3 | 1 << 4 | 1 << 5 | 0 << 6, // NOTEQUAL
  99. 0 << 3 | 1 << 4 | 1 << 5 | 1 << 6, // GEQUAL
  100. 1 << 3 | 0 << 4 | 0 << 5 | 0 << 6, // ALWAYS
  101. ];
  102. const filterNoMipToBits = [
  103. 0 << 7, // not used
  104. 1 << 7, // TEXTURE_NEAREST_SAMPLINGMODE / TEXTURE_NEAREST_NEAREST
  105. 1 << 7, // TEXTURE_BILINEAR_SAMPLINGMODE / TEXTURE_LINEAR_LINEAR
  106. 0 << 7, // TEXTURE_TRILINEAR_SAMPLINGMODE / TEXTURE_LINEAR_LINEAR_MIPLINEAR
  107. 0 << 7, // TEXTURE_NEAREST_NEAREST_MIPNEAREST
  108. 0 << 7, // TEXTURE_NEAREST_LINEAR_MIPNEAREST
  109. 0 << 7, // TEXTURE_NEAREST_LINEAR_MIPLINEAR
  110. 1 << 7, // TEXTURE_NEAREST_LINEAR
  111. 0 << 7, // TEXTURE_NEAREST_NEAREST_MIPLINEAR
  112. 0 << 7, // TEXTURE_LINEAR_NEAREST_MIPNEAREST
  113. 0 << 7, // TEXTURE_LINEAR_NEAREST_MIPLINEAR
  114. 0 << 7, // TEXTURE_LINEAR_LINEAR_MIPNEAREST
  115. 1 << 7, // TEXTURE_LINEAR_NEAREST
  116. ];
  117. export class WebGPUTextureHelper {
  118. private _device: GPUDevice;
  119. private _glslang: any;
  120. private _bufferManager: WebGPUBufferManager;
  121. private _mipmapSampler: GPUSampler;
  122. private _invertYPreMultiplyAlphaSampler: GPUSampler;
  123. private _pipelines: { [format: string]: Array<GPURenderPipeline> } = {};
  124. private _compiledShaders: GPUShaderModule[][] = [];
  125. private _deferredReleaseTextures: Array<[Nullable<InternalTexture>, Nullable<HardwareTextureWrapper | GPUTexture>, Nullable<BaseTexture>, Nullable<InternalTexture>]> = [];
  126. private _samplers: { [hash: number]: GPUSampler } = {};
  127. private _commandEncoderForCreation: GPUCommandEncoder;
  128. public static computeNumMipmapLevels(width: number, height: number) {
  129. return Scalar.ILog2(Math.max(width, height)) + 1;
  130. }
  131. //------------------------------------------------------------------------------
  132. // Initialization / Helpers
  133. //------------------------------------------------------------------------------
  134. constructor(device: GPUDevice, glslang: any, bufferManager: WebGPUBufferManager) {
  135. this._device = device;
  136. this._glslang = glslang;
  137. this._bufferManager = bufferManager;
  138. this._mipmapSampler = device.createSampler({ minFilter: WebGPUConstants.FilterMode.Linear });
  139. this._invertYPreMultiplyAlphaSampler = device.createSampler({ minFilter: WebGPUConstants.FilterMode.Nearest, magFilter: WebGPUConstants.FilterMode.Nearest });
  140. this._getPipeline(WebGPUConstants.TextureFormat.RGBA8Unorm);
  141. }
  142. private _getPipeline(format: GPUTextureFormat, forMipMap = true, invertY = false, premultiplyAlpha = false): GPURenderPipeline {
  143. const index = (forMipMap ? 1 : 0) + ((invertY ? 1 : 0) << 1) + (premultiplyAlpha ? 1 : 0) << 2;
  144. if (!this._pipelines[format]) {
  145. this._pipelines[format] = [];
  146. }
  147. let pipeline = this._pipelines[format][index];
  148. if (!pipeline) {
  149. let defines = "#version 450\r\n";
  150. if (invertY) {
  151. defines += "#define INVERTY\r\n";
  152. }
  153. if (premultiplyAlpha) {
  154. defines += "define PREMULTIPLYALPHA\r\n";
  155. }
  156. let modules = this._compiledShaders[index];
  157. if (!modules) {
  158. const vertexModule = this._device.createShaderModule({
  159. code: this._glslang.compileGLSL(forMipMap ? defines + mipmapVertexSource : defines + invertYPreMultiplyAlphaVertexSource, 'vertex')
  160. });
  161. const fragmentModule = this._device.createShaderModule({
  162. code: this._glslang.compileGLSL(forMipMap ? defines + mipmapFragmentSource : defines + invertYPreMultiplyAlphaFragmentSource, 'fragment')
  163. });
  164. modules = this._compiledShaders[index] = [vertexModule, fragmentModule];
  165. }
  166. pipeline = this._pipelines[format][index] = this._device.createRenderPipeline({
  167. vertexStage: {
  168. module: modules[0],
  169. entryPoint: 'main'
  170. },
  171. fragmentStage: {
  172. module: modules[1],
  173. entryPoint: 'main'
  174. },
  175. primitiveTopology: WebGPUConstants.PrimitiveTopology.TriangleStrip,
  176. vertexState: {
  177. indexFormat: WebGPUConstants.IndexFormat.Uint16
  178. },
  179. colorStates: [{
  180. format,
  181. }]
  182. });
  183. }
  184. return pipeline;
  185. }
  186. private _getTextureTypeFromFormat(format: GPUTextureFormat): number {
  187. switch (format) {
  188. // One Component = 8 bits
  189. case WebGPUConstants.TextureFormat.R8Unorm:
  190. case WebGPUConstants.TextureFormat.R8Snorm:
  191. case WebGPUConstants.TextureFormat.R8Uint:
  192. case WebGPUConstants.TextureFormat.R8Sint:
  193. case WebGPUConstants.TextureFormat.RG8Unorm:
  194. case WebGPUConstants.TextureFormat.RG8Snorm:
  195. case WebGPUConstants.TextureFormat.RG8Uint:
  196. case WebGPUConstants.TextureFormat.RG8Sint:
  197. case WebGPUConstants.TextureFormat.RGBA8Unorm:
  198. case WebGPUConstants.TextureFormat.RGBA8UnormSRGB:
  199. case WebGPUConstants.TextureFormat.RGBA8Snorm:
  200. case WebGPUConstants.TextureFormat.RGBA8Uint:
  201. case WebGPUConstants.TextureFormat.RGBA8Sint:
  202. case WebGPUConstants.TextureFormat.BGRA8Unorm:
  203. case WebGPUConstants.TextureFormat.BGRA8UnormSRGB:
  204. case WebGPUConstants.TextureFormat.RGB10A2Unorm: // composite format - let's say it's byte...
  205. case WebGPUConstants.TextureFormat.RGB9E5UFloat: // composite format - let's say it's byte...
  206. case WebGPUConstants.TextureFormat.RG11B10UFloat: // composite format - let's say it's byte...
  207. case WebGPUConstants.TextureFormat.Depth24UnormStencil8: // composite format - let's say it's byte...
  208. case WebGPUConstants.TextureFormat.Depth32FloatStencil8: // composite format - let's say it's byte...
  209. case WebGPUConstants.TextureFormat.BC7RGBAUnorm:
  210. case WebGPUConstants.TextureFormat.BC7RGBAUnormSRGB:
  211. case WebGPUConstants.TextureFormat.BC6HRGBUFloat:
  212. case WebGPUConstants.TextureFormat.BC6HRGBFloat:
  213. case WebGPUConstants.TextureFormat.BC5RGUnorm:
  214. case WebGPUConstants.TextureFormat.BC5RGSnorm:
  215. case WebGPUConstants.TextureFormat.BC3RGBAUnorm:
  216. case WebGPUConstants.TextureFormat.BC3RGBAUnormSRGB:
  217. case WebGPUConstants.TextureFormat.BC2RGBAUnorm:
  218. case WebGPUConstants.TextureFormat.BC2RGBAUnormSRGB:
  219. case WebGPUConstants.TextureFormat.BC4RUnorm:
  220. case WebGPUConstants.TextureFormat.BC4RSnorm:
  221. case WebGPUConstants.TextureFormat.BC1RGBAUNorm:
  222. case WebGPUConstants.TextureFormat.BC1RGBAUnormSRGB:
  223. return Constants.TEXTURETYPE_UNSIGNED_BYTE;
  224. // One component = 16 bits
  225. case WebGPUConstants.TextureFormat.R16Uint:
  226. case WebGPUConstants.TextureFormat.R16Sint:
  227. case WebGPUConstants.TextureFormat.RG16Uint:
  228. case WebGPUConstants.TextureFormat.RG16Sint:
  229. case WebGPUConstants.TextureFormat.RGBA16Uint:
  230. case WebGPUConstants.TextureFormat.RGBA16Sint:
  231. case WebGPUConstants.TextureFormat.Depth16Unorm:
  232. return Constants.TEXTURETYPE_UNSIGNED_SHORT;
  233. case WebGPUConstants.TextureFormat.R16Float:
  234. case WebGPUConstants.TextureFormat.RG16Float:
  235. case WebGPUConstants.TextureFormat.RGBA16Float:
  236. return Constants.TEXTURETYPE_HALF_FLOAT;
  237. // One component = 32 bits
  238. case WebGPUConstants.TextureFormat.R32Uint:
  239. case WebGPUConstants.TextureFormat.R32Sint:
  240. case WebGPUConstants.TextureFormat.RG32Uint:
  241. case WebGPUConstants.TextureFormat.RG32Sint:
  242. case WebGPUConstants.TextureFormat.RGBA32Uint:
  243. case WebGPUConstants.TextureFormat.RGBA32Sint:
  244. return Constants.TEXTURETYPE_UNSIGNED_INTEGER;
  245. case WebGPUConstants.TextureFormat.R32Float:
  246. case WebGPUConstants.TextureFormat.RG32Float:
  247. case WebGPUConstants.TextureFormat.RGBA32Float:
  248. case WebGPUConstants.TextureFormat.Depth32Float:
  249. return Constants.TEXTURETYPE_FLOAT;
  250. case WebGPUConstants.TextureFormat.Stencil8:
  251. throw "No fixed size for Stencil8 format!";
  252. case WebGPUConstants.TextureFormat.Depth24Plus:
  253. throw "No fixed size for Depth24Plus format!";
  254. case WebGPUConstants.TextureFormat.Depth24PlusStencil8:
  255. throw "No fixed size for Depth24PlusStencil8 format!";
  256. }
  257. return Constants.TEXTURETYPE_UNSIGNED_BYTE;
  258. }
  259. private _getBlockInformationFromFormat(format: GPUTextureFormat): { width: number, height: number, length: number } {
  260. switch (format) {
  261. // 8 bits formats
  262. case WebGPUConstants.TextureFormat.R8Unorm:
  263. case WebGPUConstants.TextureFormat.R8Snorm:
  264. case WebGPUConstants.TextureFormat.R8Uint:
  265. case WebGPUConstants.TextureFormat.R8Sint:
  266. return { width: 1, height: 1, length: 1 };
  267. // 16 bits formats
  268. case WebGPUConstants.TextureFormat.R16Uint:
  269. case WebGPUConstants.TextureFormat.R16Sint:
  270. case WebGPUConstants.TextureFormat.R16Float:
  271. case WebGPUConstants.TextureFormat.RG8Unorm:
  272. case WebGPUConstants.TextureFormat.RG8Snorm:
  273. case WebGPUConstants.TextureFormat.RG8Uint:
  274. case WebGPUConstants.TextureFormat.RG8Sint:
  275. return { width: 1, height: 1, length: 2 };
  276. // 32 bits formats
  277. case WebGPUConstants.TextureFormat.R32Uint:
  278. case WebGPUConstants.TextureFormat.R32Sint:
  279. case WebGPUConstants.TextureFormat.R32Float:
  280. case WebGPUConstants.TextureFormat.RG16Uint:
  281. case WebGPUConstants.TextureFormat.RG16Sint:
  282. case WebGPUConstants.TextureFormat.RG16Float:
  283. case WebGPUConstants.TextureFormat.RGBA8Unorm:
  284. case WebGPUConstants.TextureFormat.RGBA8UnormSRGB:
  285. case WebGPUConstants.TextureFormat.RGBA8Snorm:
  286. case WebGPUConstants.TextureFormat.RGBA8Uint:
  287. case WebGPUConstants.TextureFormat.RGBA8Sint:
  288. case WebGPUConstants.TextureFormat.BGRA8Unorm:
  289. case WebGPUConstants.TextureFormat.BGRA8UnormSRGB:
  290. case WebGPUConstants.TextureFormat.RGB9E5UFloat:
  291. case WebGPUConstants.TextureFormat.RGB10A2Unorm:
  292. case WebGPUConstants.TextureFormat.RG11B10UFloat:
  293. return { width: 1, height: 1, length: 4 };
  294. // 64 bits formats
  295. case WebGPUConstants.TextureFormat.RG32Uint:
  296. case WebGPUConstants.TextureFormat.RG32Sint:
  297. case WebGPUConstants.TextureFormat.RG32Float:
  298. case WebGPUConstants.TextureFormat.RGBA16Uint:
  299. case WebGPUConstants.TextureFormat.RGBA16Sint:
  300. case WebGPUConstants.TextureFormat.RGBA16Float:
  301. return { width: 1, height: 1, length: 8 };
  302. // 128 bits formats
  303. case WebGPUConstants.TextureFormat.RGBA32Uint:
  304. case WebGPUConstants.TextureFormat.RGBA32Sint:
  305. case WebGPUConstants.TextureFormat.RGBA32Float:
  306. return { width: 1, height: 1, length: 16 };
  307. // Depth and stencil formats
  308. case WebGPUConstants.TextureFormat.Stencil8:
  309. throw "No fixed size for Stencil8 format!";
  310. case WebGPUConstants.TextureFormat.Depth16Unorm:
  311. return { width: 1, height: 1, length: 2 };
  312. case WebGPUConstants.TextureFormat.Depth24Plus:
  313. throw "No fixed size for Depth24Plus format!";
  314. case WebGPUConstants.TextureFormat.Depth24PlusStencil8:
  315. throw "No fixed size for Depth24PlusStencil8 format!";
  316. case WebGPUConstants.TextureFormat.Depth32Float:
  317. return { width: 1, height: 1, length: 4 };
  318. case WebGPUConstants.TextureFormat.Depth24UnormStencil8:
  319. return { width: 1, height: 1, length: 4 };
  320. case WebGPUConstants.TextureFormat.Depth32FloatStencil8:
  321. return { width: 1, height: 1, length: 5 };
  322. // BC compressed formats usable if "texture-compression-bc" is both
  323. // supported by the device/user agent and enabled in requestDevice.
  324. case WebGPUConstants.TextureFormat.BC7RGBAUnorm:
  325. case WebGPUConstants.TextureFormat.BC7RGBAUnormSRGB:
  326. case WebGPUConstants.TextureFormat.BC6HRGBUFloat:
  327. case WebGPUConstants.TextureFormat.BC6HRGBFloat:
  328. case WebGPUConstants.TextureFormat.BC5RGUnorm:
  329. case WebGPUConstants.TextureFormat.BC5RGSnorm:
  330. case WebGPUConstants.TextureFormat.BC3RGBAUnorm:
  331. case WebGPUConstants.TextureFormat.BC3RGBAUnormSRGB:
  332. case WebGPUConstants.TextureFormat.BC2RGBAUnorm:
  333. case WebGPUConstants.TextureFormat.BC2RGBAUnormSRGB:
  334. return { width: 4, height: 4, length: 16 };
  335. case WebGPUConstants.TextureFormat.BC4RUnorm:
  336. case WebGPUConstants.TextureFormat.BC4RSnorm:
  337. case WebGPUConstants.TextureFormat.BC1RGBAUNorm:
  338. case WebGPUConstants.TextureFormat.BC1RGBAUnormSRGB:
  339. return { width: 4, height: 4, length: 8 };
  340. }
  341. return { width: 1, height: 1, length: 4 };
  342. }
  343. private _isHardwareTexture(texture: HardwareTextureWrapper | GPUTexture): texture is HardwareTextureWrapper {
  344. return !!(texture as HardwareTextureWrapper).release;
  345. }
  346. public isImageBitmap(imageBitmap: ImageBitmap | { width: number, height: number }): imageBitmap is ImageBitmap {
  347. return (imageBitmap as ImageBitmap).close !== undefined;
  348. }
  349. public isImageBitmapArray(imageBitmap: ImageBitmap[] | { width: number, height: number }): imageBitmap is ImageBitmap[] {
  350. return Array.isArray(imageBitmap as ImageBitmap[]) && (imageBitmap as ImageBitmap[])[0].close !== undefined;
  351. }
  352. public setCommandEncoder(encoder: GPUCommandEncoder): void {
  353. this._commandEncoderForCreation = encoder;
  354. }
  355. public isCompressedFormat(format: GPUTextureFormat): boolean {
  356. switch (format) {
  357. case WebGPUConstants.TextureFormat.BC7RGBAUnormSRGB:
  358. case WebGPUConstants.TextureFormat.BC7RGBAUnorm:
  359. case WebGPUConstants.TextureFormat.BC6HRGBFloat:
  360. case WebGPUConstants.TextureFormat.BC6HRGBUFloat:
  361. case WebGPUConstants.TextureFormat.BC5RGSnorm:
  362. case WebGPUConstants.TextureFormat.BC5RGUnorm:
  363. case WebGPUConstants.TextureFormat.BC4RSnorm:
  364. case WebGPUConstants.TextureFormat.BC4RUnorm:
  365. case WebGPUConstants.TextureFormat.BC3RGBAUnormSRGB:
  366. case WebGPUConstants.TextureFormat.BC3RGBAUnorm:
  367. case WebGPUConstants.TextureFormat.BC2RGBAUnormSRGB:
  368. case WebGPUConstants.TextureFormat.BC2RGBAUnorm:
  369. case WebGPUConstants.TextureFormat.BC1RGBAUnormSRGB:
  370. case WebGPUConstants.TextureFormat.BC1RGBAUNorm:
  371. return true;
  372. }
  373. return false;
  374. }
  375. public getWebGPUTextureFormat(type: number, format: number): GPUTextureFormat {
  376. switch (format) {
  377. case Constants.TEXTUREFORMAT_DEPTH24_STENCIL8:
  378. return WebGPUConstants.TextureFormat.Depth24PlusStencil8;
  379. case Constants.TEXTUREFORMAT_DEPTH32_FLOAT:
  380. return WebGPUConstants.TextureFormat.Depth32Float;
  381. case Constants.TEXTUREFORMAT_COMPRESSED_RGBA_BPTC_UNORM:
  382. return WebGPUConstants.TextureFormat.BC7RGBAUnorm;
  383. case Constants.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
  384. return WebGPUConstants.TextureFormat.BC6HRGBUFloat;
  385. case Constants.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:
  386. return WebGPUConstants.TextureFormat.BC6HRGBFloat;
  387. case Constants.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT5:
  388. return WebGPUConstants.TextureFormat.BC3RGBAUnorm;
  389. case Constants.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT3:
  390. return WebGPUConstants.TextureFormat.BC2RGBAUnorm;
  391. case Constants.TEXTUREFORMAT_COMPRESSED_RGBA_S3TC_DXT1:
  392. return WebGPUConstants.TextureFormat.BC1RGBAUNorm;
  393. }
  394. switch (type) {
  395. case Constants.TEXTURETYPE_BYTE:
  396. switch (format) {
  397. case Constants.TEXTUREFORMAT_RED:
  398. return WebGPUConstants.TextureFormat.R8Snorm;
  399. case Constants.TEXTUREFORMAT_RG:
  400. return WebGPUConstants.TextureFormat.RG8Snorm;
  401. case Constants.TEXTUREFORMAT_RGB:
  402. throw "RGB format not supported in WebGPU";
  403. case Constants.TEXTUREFORMAT_RED_INTEGER:
  404. return WebGPUConstants.TextureFormat.R8Sint;
  405. case Constants.TEXTUREFORMAT_RG_INTEGER:
  406. return WebGPUConstants.TextureFormat.RG8Sint;
  407. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  408. throw "RGB_INTEGER format not supported in WebGPU";
  409. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  410. return WebGPUConstants.TextureFormat.RGBA8Sint;
  411. default:
  412. return WebGPUConstants.TextureFormat.RGBA8Snorm;
  413. }
  414. case Constants.TEXTURETYPE_UNSIGNED_BYTE:
  415. switch (format) {
  416. case Constants.TEXTUREFORMAT_RED:
  417. return WebGPUConstants.TextureFormat.R8Unorm;
  418. case Constants.TEXTUREFORMAT_RG:
  419. return WebGPUConstants.TextureFormat.RG8Unorm;
  420. case Constants.TEXTUREFORMAT_RGB:
  421. throw "TEXTUREFORMAT_RGB format not supported in WebGPU";
  422. case Constants.TEXTUREFORMAT_RGBA:
  423. return WebGPUConstants.TextureFormat.RGBA8Unorm;
  424. case Constants.TEXTUREFORMAT_BGRA:
  425. return WebGPUConstants.TextureFormat.BGRA8Unorm;
  426. case Constants.TEXTUREFORMAT_RED_INTEGER:
  427. return WebGPUConstants.TextureFormat.R8Uint;
  428. case Constants.TEXTUREFORMAT_RG_INTEGER:
  429. return WebGPUConstants.TextureFormat.RG8Uint;
  430. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  431. throw "RGB_INTEGER format not supported in WebGPU";
  432. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  433. return WebGPUConstants.TextureFormat.RGBA8Uint;
  434. case Constants.TEXTUREFORMAT_ALPHA:
  435. throw "TEXTUREFORMAT_ALPHA format not supported in WebGPU";
  436. case Constants.TEXTUREFORMAT_LUMINANCE:
  437. throw "TEXTUREFORMAT_LUMINANCE format not supported in WebGPU";
  438. case Constants.TEXTUREFORMAT_LUMINANCE_ALPHA:
  439. throw "TEXTUREFORMAT_LUMINANCE_ALPHA format not supported in WebGPU";
  440. default:
  441. return WebGPUConstants.TextureFormat.RGBA8Unorm;
  442. }
  443. case Constants.TEXTURETYPE_SHORT:
  444. switch (format) {
  445. case Constants.TEXTUREFORMAT_RED_INTEGER:
  446. return WebGPUConstants.TextureFormat.R16Sint;
  447. case Constants.TEXTUREFORMAT_RG_INTEGER:
  448. return WebGPUConstants.TextureFormat.RG16Sint;
  449. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  450. throw "TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";
  451. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  452. return WebGPUConstants.TextureFormat.RGBA16Sint;
  453. default:
  454. return WebGPUConstants.TextureFormat.RGBA16Sint;
  455. }
  456. case Constants.TEXTURETYPE_UNSIGNED_SHORT:
  457. switch (format) {
  458. case Constants.TEXTUREFORMAT_RED_INTEGER:
  459. return WebGPUConstants.TextureFormat.R16Uint;
  460. case Constants.TEXTUREFORMAT_RG_INTEGER:
  461. return WebGPUConstants.TextureFormat.RG16Uint;
  462. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  463. throw "TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";
  464. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  465. return WebGPUConstants.TextureFormat.RGBA16Uint;
  466. default:
  467. return WebGPUConstants.TextureFormat.RGBA16Uint;
  468. }
  469. case Constants.TEXTURETYPE_INT:
  470. switch (format) {
  471. case Constants.TEXTUREFORMAT_RED_INTEGER:
  472. return WebGPUConstants.TextureFormat.R32Sint;
  473. case Constants.TEXTUREFORMAT_RG_INTEGER:
  474. return WebGPUConstants.TextureFormat.RG32Sint;
  475. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  476. throw "TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";
  477. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  478. return WebGPUConstants.TextureFormat.RGBA32Sint;
  479. default:
  480. return WebGPUConstants.TextureFormat.RGBA32Sint;
  481. }
  482. case Constants.TEXTURETYPE_UNSIGNED_INTEGER: // Refers to UNSIGNED_INT
  483. switch (format) {
  484. case Constants.TEXTUREFORMAT_RED_INTEGER:
  485. return WebGPUConstants.TextureFormat.R32Uint;
  486. case Constants.TEXTUREFORMAT_RG_INTEGER:
  487. return WebGPUConstants.TextureFormat.RG32Uint;
  488. case Constants.TEXTUREFORMAT_RGB_INTEGER:
  489. throw "TEXTUREFORMAT_RGB_INTEGER format not supported in WebGPU";
  490. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  491. return WebGPUConstants.TextureFormat.RGBA32Uint;
  492. default:
  493. return WebGPUConstants.TextureFormat.RGBA32Uint;
  494. }
  495. case Constants.TEXTURETYPE_FLOAT:
  496. switch (format) {
  497. case Constants.TEXTUREFORMAT_RED:
  498. return WebGPUConstants.TextureFormat.R32Float; // By default. Other possibility is R16Float.
  499. case Constants.TEXTUREFORMAT_RG:
  500. return WebGPUConstants.TextureFormat.RG32Float; // By default. Other possibility is RG16Float.
  501. case Constants.TEXTUREFORMAT_RGB:
  502. throw "TEXTUREFORMAT_RGB format not supported in WebGPU";
  503. case Constants.TEXTUREFORMAT_RGBA:
  504. return WebGPUConstants.TextureFormat.RGBA32Float; // By default. Other possibility is RGBA16Float.
  505. default:
  506. return WebGPUConstants.TextureFormat.RGBA32Float;
  507. }
  508. case Constants.TEXTURETYPE_HALF_FLOAT:
  509. switch (format) {
  510. case Constants.TEXTUREFORMAT_RED:
  511. return WebGPUConstants.TextureFormat.R16Float;
  512. case Constants.TEXTUREFORMAT_RG:
  513. return WebGPUConstants.TextureFormat.RG16Float;
  514. case Constants.TEXTUREFORMAT_RGB:
  515. throw "TEXTUREFORMAT_RGB format not supported in WebGPU";
  516. case Constants.TEXTUREFORMAT_RGBA:
  517. return WebGPUConstants.TextureFormat.RGBA16Float;
  518. default:
  519. return WebGPUConstants.TextureFormat.RGBA16Float;
  520. }
  521. case Constants.TEXTURETYPE_UNSIGNED_SHORT_5_6_5:
  522. throw "TEXTURETYPE_UNSIGNED_SHORT_5_6_5 format not supported in WebGPU";
  523. case Constants.TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV:
  524. throw "TEXTURETYPE_UNSIGNED_INT_10F_11F_11F_REV format not supported in WebGPU";
  525. case Constants.TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV:
  526. throw "TEXTURETYPE_UNSIGNED_INT_5_9_9_9_REV format not supported in WebGPU";
  527. case Constants.TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4:
  528. throw "TEXTURETYPE_UNSIGNED_SHORT_4_4_4_4 format not supported in WebGPU";
  529. case Constants.TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1:
  530. throw "TEXTURETYPE_UNSIGNED_SHORT_5_5_5_1 format not supported in WebGPU";
  531. case Constants.TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV:
  532. switch (format) {
  533. case Constants.TEXTUREFORMAT_RGBA:
  534. return WebGPUConstants.TextureFormat.RGB10A2Unorm;
  535. case Constants.TEXTUREFORMAT_RGBA_INTEGER:
  536. throw "TEXTUREFORMAT_RGBA_INTEGER format not supported in WebGPU when type is TEXTURETYPE_UNSIGNED_INT_2_10_10_10_REV";
  537. default:
  538. return WebGPUConstants.TextureFormat.RGB10A2Unorm;
  539. }
  540. }
  541. return WebGPUConstants.TextureFormat.RGBA8Unorm;
  542. }
  543. public invertYPreMultiplyAlpha(gpuTexture: GPUTexture, width: number, height: number, format: GPUTextureFormat, invertY = false, premultiplyAlpha = false, faceIndex= 0, commandEncoder?: GPUCommandEncoder): void {
  544. const useOwnCommandEncoder = commandEncoder === undefined;
  545. const pipeline = this._getPipeline(format, false, invertY, premultiplyAlpha);
  546. const bindGroupLayout = pipeline.getBindGroupLayout(0);
  547. if (useOwnCommandEncoder) {
  548. commandEncoder = this._device.createCommandEncoder({});
  549. }
  550. commandEncoder!.pushDebugGroup(`internal process texture - invertY=${invertY} premultiplyAlpha=${premultiplyAlpha}`);
  551. 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);
  552. const passEncoder = commandEncoder!.beginRenderPass({
  553. colorAttachments: [{
  554. attachment: outputTexture.createView({
  555. dimension: WebGPUConstants.TextureViewDimension.E2d,
  556. baseMipLevel: 0,
  557. mipLevelCount: 1,
  558. arrayLayerCount: 1,
  559. baseArrayLayer: 0,
  560. }),
  561. loadValue: WebGPUConstants.LoadOp.Load,
  562. }],
  563. });
  564. const bindGroup = this._device.createBindGroup({
  565. layout: bindGroupLayout,
  566. entries: [{
  567. binding: 0,
  568. resource: this._invertYPreMultiplyAlphaSampler,
  569. }, {
  570. binding: 1,
  571. resource: gpuTexture.createView({
  572. dimension: WebGPUConstants.TextureViewDimension.E2d,
  573. baseMipLevel: 0,
  574. mipLevelCount: 1,
  575. arrayLayerCount: 1,
  576. baseArrayLayer: faceIndex,
  577. }),
  578. }],
  579. });
  580. passEncoder.setPipeline(pipeline);
  581. passEncoder.setBindGroup(0, bindGroup);
  582. passEncoder.draw(4, 1, 0, 0);
  583. passEncoder.endPass();
  584. commandEncoder!.copyTextureToTexture({
  585. texture: outputTexture,
  586. }, {
  587. texture: gpuTexture,
  588. origin: {
  589. x: 0,
  590. y: 0,
  591. z: Math.max(faceIndex, 0),
  592. }
  593. }, {
  594. width,
  595. height,
  596. depth: 1,
  597. }
  598. );
  599. this._deferredReleaseTextures.push([null, outputTexture, null, null]);
  600. commandEncoder!.popDebugGroup();
  601. if (useOwnCommandEncoder) {
  602. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  603. commandEncoder = null as any;
  604. }
  605. }
  606. //------------------------------------------------------------------------------
  607. // Creation
  608. //------------------------------------------------------------------------------
  609. 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,
  610. sampleCount = 1, commandEncoder?: GPUCommandEncoder, usage = -1): GPUTexture
  611. {
  612. const layerCount = (imageBitmap as any).layers || 1;
  613. let textureSize = {
  614. width: imageBitmap.width,
  615. height: imageBitmap.height,
  616. depth: layerCount,
  617. };
  618. const mipLevelCount = hasMipmaps ? WebGPUTextureHelper.computeNumMipmapLevels(imageBitmap.width, imageBitmap.height) : 1;
  619. const usages = usage >= 0 ? usage : WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.CopyDst | WebGPUConstants.TextureUsage.Sampled;
  620. const additionalUsages = hasMipmaps && !this.isCompressedFormat(format) ? WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.OutputAttachment : 0;
  621. const gpuTexture = this._device.createTexture({
  622. size: textureSize,
  623. dimension: is3D ? WebGPUConstants.TextureDimension.E3d : WebGPUConstants.TextureDimension.E2d,
  624. format,
  625. usage: usages | additionalUsages,
  626. sampleCount,
  627. mipLevelCount
  628. });
  629. if (this.isImageBitmap(imageBitmap)) {
  630. this.updateTexture(imageBitmap, gpuTexture, imageBitmap.width, imageBitmap.height, layerCount, format, 0, 0, invertY, premultiplyAlpha, 0, 0, commandEncoder);
  631. if (hasMipmaps && generateMipmaps) {
  632. this.generateMipmaps(gpuTexture, format, mipLevelCount, 0, commandEncoder);
  633. }
  634. }
  635. return gpuTexture;
  636. }
  637. public createCubeTexture(imageBitmaps: ImageBitmap[] | { width: number, height: number }, hasMipmaps = false, generateMipmaps = false, invertY = false, premultiplyAlpha = false, format: GPUTextureFormat = WebGPUConstants.TextureFormat.RGBA8Unorm,
  638. sampleCount = 1, commandEncoder?: GPUCommandEncoder, usage = -1): GPUTexture
  639. {
  640. const width = this.isImageBitmapArray(imageBitmaps) ? imageBitmaps[0].width : imageBitmaps.width;
  641. const height = this.isImageBitmapArray(imageBitmaps) ? imageBitmaps[0].height : imageBitmaps.height;
  642. const mipLevelCount = hasMipmaps ? WebGPUTextureHelper.computeNumMipmapLevels(width, height) : 1;
  643. const usages = usage >= 0 ? usage : WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.CopyDst | WebGPUConstants.TextureUsage.Sampled;
  644. const additionalUsages = hasMipmaps && !this.isCompressedFormat(format) ? WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.OutputAttachment : 0;
  645. const gpuTexture = this._device.createTexture({
  646. size: {
  647. width,
  648. height,
  649. depth: 6,
  650. },
  651. dimension: WebGPUConstants.TextureDimension.E2d,
  652. format,
  653. usage: usages | additionalUsages,
  654. sampleCount,
  655. mipLevelCount
  656. });
  657. if (this.isImageBitmapArray(imageBitmaps)) {
  658. this.updateCubeTextures(imageBitmaps, gpuTexture, width, height, format, invertY, premultiplyAlpha, 0, 0, commandEncoder);
  659. if (hasMipmaps && generateMipmaps) {
  660. this.generateCubeMipmaps(gpuTexture, format, mipLevelCount, commandEncoder);
  661. }
  662. }
  663. return gpuTexture;
  664. }
  665. public generateCubeMipmaps(gpuTexture: GPUTexture, format: GPUTextureFormat, mipLevelCount: number, commandEncoder?: GPUCommandEncoder): void {
  666. const useOwnCommandEncoder = commandEncoder === undefined;
  667. if (useOwnCommandEncoder) {
  668. commandEncoder = this._device.createCommandEncoder({});
  669. }
  670. commandEncoder!.pushDebugGroup(`create cube mipmaps - ${mipLevelCount} levels`);
  671. for (let f = 0; f < 6; ++f) {
  672. this.generateMipmaps(gpuTexture, format, mipLevelCount, f, commandEncoder);
  673. }
  674. commandEncoder!.popDebugGroup();
  675. if (useOwnCommandEncoder) {
  676. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  677. commandEncoder = null as any;
  678. }
  679. }
  680. public generateMipmaps(gpuTexture: GPUTexture, format: GPUTextureFormat, mipLevelCount: number, faceIndex= 0, commandEncoder?: GPUCommandEncoder): void {
  681. const useOwnCommandEncoder = commandEncoder === undefined;
  682. const pipeline = this._getPipeline(format);
  683. const bindGroupLayout = pipeline.getBindGroupLayout(0);
  684. if (useOwnCommandEncoder) {
  685. commandEncoder = this._device.createCommandEncoder({});
  686. }
  687. commandEncoder!.pushDebugGroup(`create mipmaps for face #${faceIndex} - ${mipLevelCount} levels`);
  688. for (let i = 1; i < mipLevelCount; ++i) {
  689. const passEncoder = commandEncoder!.beginRenderPass({
  690. colorAttachments: [{
  691. attachment: gpuTexture.createView({
  692. dimension: WebGPUConstants.TextureViewDimension.E2d,
  693. baseMipLevel: i,
  694. mipLevelCount: 1,
  695. arrayLayerCount: 1,
  696. baseArrayLayer: faceIndex,
  697. }),
  698. loadValue: { r: 0.0, g: 0.0, b: 0.0, a: 0.0 },
  699. }],
  700. });
  701. const bindGroup = this._device.createBindGroup({
  702. layout: bindGroupLayout,
  703. entries: [{
  704. binding: 0,
  705. resource: this._mipmapSampler,
  706. }, {
  707. binding: 1,
  708. resource: gpuTexture.createView({
  709. dimension: WebGPUConstants.TextureViewDimension.E2d,
  710. baseMipLevel: i - 1,
  711. mipLevelCount: 1,
  712. arrayLayerCount: 1,
  713. baseArrayLayer: faceIndex,
  714. }),
  715. }],
  716. });
  717. passEncoder.setPipeline(pipeline);
  718. passEncoder.setBindGroup(0, bindGroup);
  719. passEncoder.draw(4, 1, 0, 0);
  720. passEncoder.endPass();
  721. }
  722. commandEncoder!.popDebugGroup();
  723. if (useOwnCommandEncoder) {
  724. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  725. commandEncoder = null as any;
  726. }
  727. }
  728. //------------------------------------------------------------------------------
  729. // Update
  730. //------------------------------------------------------------------------------
  731. public createGPUTextureForInternalTexture(texture: InternalTexture, width?: number, height?: number, depth?: number): WebGPUHardwareTexture {
  732. if (!texture._hardwareTexture) {
  733. texture._hardwareTexture = new WebGPUHardwareTexture();
  734. }
  735. if (width === undefined) {
  736. width = texture.width;
  737. }
  738. if (height === undefined) {
  739. height = texture.height;
  740. }
  741. if (depth === undefined) {
  742. depth = texture.depth;
  743. }
  744. const gpuTextureWrapper = texture._hardwareTexture as WebGPUHardwareTexture;
  745. gpuTextureWrapper.format = this.getWebGPUTextureFormat(texture.type, texture.format);
  746. const textureUsages =
  747. texture._source === InternalTextureSource.RenderTarget ? WebGPUConstants.TextureUsage.Sampled | WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.OutputAttachment :
  748. texture._source === InternalTextureSource.Depth ? WebGPUConstants.TextureUsage.Sampled | WebGPUConstants.TextureUsage.OutputAttachment : -1;
  749. const hasMipMaps = texture.generateMipMaps;
  750. const layerCount = depth || 1;
  751. if (texture.isCube) {
  752. // TODO WEBGPU handle depth for cube textures?
  753. const gpuTexture = this.createCubeTexture({ width, height }, texture.generateMipMaps, texture.generateMipMaps, texture.invertY, false, gpuTextureWrapper.format, texture.samples || 1, this._commandEncoderForCreation, textureUsages);
  754. gpuTextureWrapper.set(gpuTexture);
  755. gpuTextureWrapper.createView({
  756. dimension: WebGPUConstants.TextureViewDimension.Cube,
  757. mipLevelCount: hasMipMaps ? WebGPUTextureHelper.computeNumMipmapLevels(width!, height!) : 1,
  758. baseArrayLayer: 0,
  759. baseMipLevel: 0,
  760. aspect: WebGPUConstants.TextureAspect.All
  761. });
  762. } else {
  763. const gpuTexture = this.createTexture({ width, height, layers: layerCount }, texture.generateMipMaps, texture.generateMipMaps, texture.invertY, false, texture.is3D, gpuTextureWrapper.format, texture.samples || 1, this._commandEncoderForCreation, textureUsages);
  764. gpuTextureWrapper.set(gpuTexture);
  765. gpuTextureWrapper.createView({
  766. dimension: texture.is2DArray ? WebGPUConstants.TextureViewDimension.E2dArray : texture.is3D ? WebGPUConstants.TextureDimension.E3d : WebGPUConstants.TextureViewDimension.E2d,
  767. mipLevelCount: hasMipMaps ? WebGPUTextureHelper.computeNumMipmapLevels(width!, height!) : 1,
  768. baseArrayLayer: 0,
  769. baseMipLevel: 0,
  770. arrayLayerCount: layerCount,
  771. aspect: WebGPUConstants.TextureAspect.All
  772. });
  773. }
  774. texture.width = texture.baseWidth = width;
  775. texture.height = texture.baseHeight = height;
  776. texture.depth = texture.baseDepth = depth;
  777. return gpuTextureWrapper;
  778. }
  779. public updateCubeTextures(imageBitmaps: ImageBitmap[] | Uint8Array[], gpuTexture: GPUTexture, width: number, height: number, format: GPUTextureFormat, invertY = false, premultiplyAlpha = false, offsetX = 0, offsetY = 0,
  780. commandEncoder?: GPUCommandEncoder): void {
  781. const faces = [0, 3, 1, 4, 2, 5];
  782. for (let f = 0; f < faces.length; ++f) {
  783. let imageBitmap = imageBitmaps[faces[f]];
  784. this.updateTexture(imageBitmap, gpuTexture, width, height, 1, format, f, 0, invertY, premultiplyAlpha, offsetX, offsetY, commandEncoder);
  785. }
  786. }
  787. // TODO WEBGPU handle data source not being in the same format than the destination texture?
  788. 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,
  789. commandEncoder?: GPUCommandEncoder): void
  790. {
  791. const blockInformation = this._getBlockInformationFromFormat(format);
  792. const textureCopyView: GPUTextureCopyView = {
  793. texture: gpuTexture,
  794. origin: {
  795. x: offsetX,
  796. y: offsetY,
  797. z: Math.max(faceIndex, 0)
  798. },
  799. mipLevel: mipLevel
  800. };
  801. const textureExtent = {
  802. width: Math.ceil(width / blockInformation.width) * blockInformation.width,
  803. height: Math.ceil(height / blockInformation.height) * blockInformation.height,
  804. depth: layers || 1
  805. };
  806. if ((imageBitmap as Uint8Array).byteLength !== undefined) {
  807. imageBitmap = imageBitmap as Uint8Array;
  808. const bytesPerRow = Math.ceil(width / blockInformation.width) * blockInformation.length;
  809. const aligned = Math.ceil(bytesPerRow / 256) * 256 === bytesPerRow;
  810. if (aligned) {
  811. const useOwnCommandEncoder = commandEncoder === undefined;
  812. if (useOwnCommandEncoder) {
  813. commandEncoder = this._device.createCommandEncoder({});
  814. }
  815. const buffer = this._bufferManager.createRawBuffer(imageBitmap.byteLength, GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC, true);
  816. const arrayBuffer = buffer.getMappedRange();
  817. new Uint8Array(arrayBuffer).set(imageBitmap);
  818. buffer.unmap();
  819. commandEncoder!.copyBufferToTexture({
  820. buffer: buffer,
  821. offset: 0,
  822. bytesPerRow
  823. }, textureCopyView, textureExtent);
  824. if (useOwnCommandEncoder) {
  825. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  826. commandEncoder = null as any;
  827. }
  828. this._bufferManager.releaseBuffer(buffer);
  829. } else {
  830. this._device.defaultQueue.writeTexture(textureCopyView, imageBitmap, {
  831. offset: 0,
  832. bytesPerRow
  833. }, textureExtent);
  834. }
  835. if (invertY || premultiplyAlpha) {
  836. this.invertYPreMultiplyAlpha(gpuTexture, width, height, format, invertY, premultiplyAlpha, faceIndex, commandEncoder);
  837. }
  838. } else {
  839. imageBitmap = imageBitmap as ImageBitmap;
  840. if (invertY || premultiplyAlpha) {
  841. createImageBitmap(imageBitmap, { imageOrientation: invertY ? "flipY" : "none", premultiplyAlpha: premultiplyAlpha ? "premultiply" : "none" }).then((imageBitmap) => {
  842. this._device.defaultQueue.copyImageBitmapToTexture({ imageBitmap }, textureCopyView, textureExtent);
  843. });
  844. } else {
  845. this._device.defaultQueue.copyImageBitmapToTexture({ imageBitmap }, textureCopyView, textureExtent);
  846. }
  847. }
  848. }
  849. 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> {
  850. const blockInformation = this._getBlockInformationFromFormat(format);
  851. const bytesPerRow = Math.ceil(width / blockInformation.width) * blockInformation.length;
  852. const bytesPerRowAligned = Math.ceil(bytesPerRow / 256) * 256;
  853. const size = bytesPerRowAligned * height;
  854. const gpuBuffer = this._bufferManager.createRawBuffer(size, GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST);
  855. const commandEncoder = this._device.createCommandEncoder({});
  856. commandEncoder.copyTextureToBuffer({
  857. texture,
  858. mipLevel,
  859. origin: {
  860. x,
  861. y,
  862. z: Math.max(faceIndex, 0)
  863. }
  864. }, {
  865. buffer: gpuBuffer,
  866. offset: 0,
  867. bytesPerRow: bytesPerRowAligned
  868. }, {
  869. width,
  870. height,
  871. depth: 1
  872. });
  873. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  874. const type = this._getTextureTypeFromFormat(format);
  875. const floatFormat = type === Constants.TEXTURETYPE_FLOAT ? 2 : type === Constants.TEXTURETYPE_HALF_FLOAT ? 1 : 0;
  876. return this._bufferManager.readDataFromBuffer(gpuBuffer, size, width, height, bytesPerRow, bytesPerRowAligned, floatFormat, 0, buffer);
  877. }
  878. //------------------------------------------------------------------------------
  879. // Dispose
  880. //------------------------------------------------------------------------------
  881. public releaseTexture(texture: InternalTexture): void {
  882. const hardwareTexture = texture._hardwareTexture;
  883. const irradianceTexture = texture._irradianceTexture;
  884. const depthStencilTexture = texture._depthStencilTexture;
  885. // 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
  886. this._deferredReleaseTextures.push([texture, hardwareTexture, irradianceTexture, depthStencilTexture]);
  887. }
  888. public destroyDeferredTextures(): void {
  889. for (let i = 0; i < this._deferredReleaseTextures.length; ++i) {
  890. const [texture, hardwareTexture, irradianceTexture, depthStencilTexture] = this._deferredReleaseTextures[i];
  891. if (hardwareTexture) {
  892. if (this._isHardwareTexture(hardwareTexture)) {
  893. hardwareTexture.release();
  894. } else {
  895. hardwareTexture.destroy();
  896. }
  897. }
  898. irradianceTexture?.dispose();
  899. depthStencilTexture?.dispose();
  900. // TODO WEBGPU remove debug code
  901. if (texture) {
  902. if ((texture as any)._swapped) {
  903. delete (texture as any)._swapped;
  904. } else {
  905. (texture as any)._released = true;
  906. }
  907. }
  908. }
  909. this._deferredReleaseTextures.length = 0;
  910. }
  911. //------------------------------------------------------------------------------
  912. // Samplers
  913. //------------------------------------------------------------------------------
  914. private static _GetSamplerHashCode(texture: InternalTexture): number {
  915. let code =
  916. filterToBits[texture.samplingMode] +
  917. comparisonFunctionToBits[(texture._comparisonFunction || 0x0202) - 0x0200 + 1] +
  918. filterNoMipToBits[texture.samplingMode] + // handle the lodMinClamp = lodMaxClamp = 0 case when no filter used for mip mapping
  919. ((texture._cachedWrapU ?? 1) << 8) +
  920. ((texture._cachedWrapV ?? 1) << 10) +
  921. ((texture._cachedWrapR ?? 1) << 12) +
  922. ((texture._cachedAnisotropicFilteringLevel ?? 1) << 14);
  923. return code;
  924. }
  925. public getCompareFunction(compareFunction: Nullable<number>): GPUCompareFunction {
  926. switch (compareFunction) {
  927. case Constants.ALWAYS:
  928. return WebGPUConstants.CompareFunction.Always;
  929. case Constants.EQUAL:
  930. return WebGPUConstants.CompareFunction.Equal;
  931. case Constants.GREATER:
  932. return WebGPUConstants.CompareFunction.Greater;
  933. case Constants.GEQUAL:
  934. return WebGPUConstants.CompareFunction.GreaterEqual;
  935. case Constants.LESS:
  936. return WebGPUConstants.CompareFunction.Less;
  937. case Constants.LEQUAL:
  938. return WebGPUConstants.CompareFunction.LessEqual;
  939. case Constants.NEVER:
  940. return WebGPUConstants.CompareFunction.Never;
  941. case Constants.NOTEQUAL:
  942. return WebGPUConstants.CompareFunction.NotEqual;
  943. default:
  944. return WebGPUConstants.CompareFunction.Less;
  945. }
  946. }
  947. private _getSamplerFilterDescriptor(internalTexture: InternalTexture): {
  948. magFilter: GPUFilterMode,
  949. minFilter: GPUFilterMode,
  950. mipmapFilter: GPUFilterMode,
  951. lodMinClamp?: number,
  952. lodMaxClamp?: number,
  953. } {
  954. let magFilter: GPUFilterMode, minFilter: GPUFilterMode, mipmapFilter: GPUFilterMode, lodMinClamp: number | undefined, lodMaxClamp: number | undefined;
  955. const useMipMaps = internalTexture.generateMipMaps;
  956. switch (internalTexture.samplingMode) {
  957. case Constants.TEXTURE_LINEAR_LINEAR_MIPNEAREST:
  958. magFilter = WebGPUConstants.FilterMode.Linear;
  959. minFilter = WebGPUConstants.FilterMode.Linear;
  960. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  961. if (!useMipMaps) {
  962. lodMinClamp = lodMaxClamp = 0;
  963. }
  964. break;
  965. case Constants.TEXTURE_LINEAR_LINEAR_MIPLINEAR:
  966. case Constants.TEXTURE_TRILINEAR_SAMPLINGMODE:
  967. magFilter = WebGPUConstants.FilterMode.Linear;
  968. minFilter = WebGPUConstants.FilterMode.Linear;
  969. if (!useMipMaps) {
  970. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  971. lodMinClamp = lodMaxClamp = 0;
  972. } else {
  973. mipmapFilter = WebGPUConstants.FilterMode.Linear;
  974. }
  975. break;
  976. case Constants.TEXTURE_NEAREST_NEAREST_MIPLINEAR:
  977. magFilter = WebGPUConstants.FilterMode.Nearest;
  978. minFilter = WebGPUConstants.FilterMode.Nearest;
  979. if (!useMipMaps) {
  980. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  981. lodMinClamp = lodMaxClamp = 0;
  982. } else {
  983. mipmapFilter = WebGPUConstants.FilterMode.Linear;
  984. }
  985. break;
  986. case Constants.TEXTURE_NEAREST_NEAREST_MIPNEAREST:
  987. magFilter = WebGPUConstants.FilterMode.Nearest;
  988. minFilter = WebGPUConstants.FilterMode.Nearest;
  989. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  990. if (!useMipMaps) {
  991. lodMinClamp = lodMaxClamp = 0;
  992. }
  993. break;
  994. case Constants.TEXTURE_NEAREST_LINEAR_MIPNEAREST:
  995. magFilter = WebGPUConstants.FilterMode.Nearest;
  996. minFilter = WebGPUConstants.FilterMode.Linear;
  997. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  998. if (!useMipMaps) {
  999. lodMinClamp = lodMaxClamp = 0;
  1000. }
  1001. break;
  1002. case Constants.TEXTURE_NEAREST_LINEAR_MIPLINEAR:
  1003. magFilter = WebGPUConstants.FilterMode.Nearest;
  1004. minFilter = WebGPUConstants.FilterMode.Linear;
  1005. if (!useMipMaps) {
  1006. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  1007. lodMinClamp = lodMaxClamp = 0;
  1008. } else {
  1009. mipmapFilter = WebGPUConstants.FilterMode.Linear;
  1010. }
  1011. break;
  1012. case Constants.TEXTURE_NEAREST_LINEAR:
  1013. magFilter = WebGPUConstants.FilterMode.Nearest;
  1014. minFilter = WebGPUConstants.FilterMode.Linear;
  1015. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  1016. lodMinClamp = lodMaxClamp = 0;
  1017. break;
  1018. case Constants.TEXTURE_NEAREST_NEAREST:
  1019. case Constants.TEXTURE_NEAREST_SAMPLINGMODE:
  1020. magFilter = WebGPUConstants.FilterMode.Nearest;
  1021. minFilter = WebGPUConstants.FilterMode.Nearest;
  1022. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  1023. lodMinClamp = lodMaxClamp = 0;
  1024. break;
  1025. case Constants.TEXTURE_LINEAR_NEAREST_MIPNEAREST:
  1026. magFilter = WebGPUConstants.FilterMode.Linear;
  1027. minFilter = WebGPUConstants.FilterMode.Nearest;
  1028. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  1029. if (!useMipMaps) {
  1030. lodMinClamp = lodMaxClamp = 0;
  1031. }
  1032. break;
  1033. case Constants.TEXTURE_LINEAR_NEAREST_MIPLINEAR:
  1034. magFilter = WebGPUConstants.FilterMode.Linear;
  1035. minFilter = WebGPUConstants.FilterMode.Nearest;
  1036. if (!useMipMaps) {
  1037. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  1038. lodMinClamp = lodMaxClamp = 0;
  1039. } else {
  1040. mipmapFilter = WebGPUConstants.FilterMode.Linear;
  1041. }
  1042. break;
  1043. case Constants.TEXTURE_LINEAR_LINEAR:
  1044. case Constants.TEXTURE_BILINEAR_SAMPLINGMODE:
  1045. magFilter = WebGPUConstants.FilterMode.Linear;
  1046. minFilter = WebGPUConstants.FilterMode.Linear;
  1047. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  1048. lodMinClamp = lodMaxClamp = 0;
  1049. break;
  1050. case Constants.TEXTURE_LINEAR_NEAREST:
  1051. magFilter = WebGPUConstants.FilterMode.Linear;
  1052. minFilter = WebGPUConstants.FilterMode.Nearest;
  1053. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  1054. lodMinClamp = lodMaxClamp = 0;
  1055. break;
  1056. default:
  1057. magFilter = WebGPUConstants.FilterMode.Nearest;
  1058. minFilter = WebGPUConstants.FilterMode.Nearest;
  1059. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  1060. lodMinClamp = lodMaxClamp = 0;
  1061. break;
  1062. }
  1063. return {
  1064. magFilter,
  1065. minFilter,
  1066. mipmapFilter,
  1067. lodMinClamp,
  1068. lodMaxClamp,
  1069. };
  1070. }
  1071. private _getWrappingMode(mode: number): GPUAddressMode {
  1072. switch (mode) {
  1073. case Engine.TEXTURE_WRAP_ADDRESSMODE:
  1074. return WebGPUConstants.AddressMode.Repeat;
  1075. case Engine.TEXTURE_CLAMP_ADDRESSMODE:
  1076. return WebGPUConstants.AddressMode.ClampToEdge;
  1077. case Engine.TEXTURE_MIRROR_ADDRESSMODE:
  1078. return WebGPUConstants.AddressMode.MirrorRepeat;
  1079. }
  1080. return WebGPUConstants.AddressMode.Repeat;
  1081. }
  1082. private _getSamplerWrappingDescriptor(internalTexture: InternalTexture): {
  1083. addressModeU: GPUAddressMode,
  1084. addressModeV: GPUAddressMode,
  1085. addressModeW: GPUAddressMode
  1086. } {
  1087. return {
  1088. addressModeU: this._getWrappingMode(internalTexture._cachedWrapU!),
  1089. addressModeV: this._getWrappingMode(internalTexture._cachedWrapV!),
  1090. addressModeW: this._getWrappingMode(internalTexture._cachedWrapR!),
  1091. };
  1092. }
  1093. private _getSamplerDescriptor(internalTexture: InternalTexture): GPUSamplerDescriptor {
  1094. return {
  1095. ...this._getSamplerFilterDescriptor(internalTexture),
  1096. ...this._getSamplerWrappingDescriptor(internalTexture),
  1097. compare: internalTexture._comparisonFunction ? this.getCompareFunction(internalTexture._comparisonFunction) : undefined,
  1098. maxAnisotropy: internalTexture._cachedAnisotropicFilteringLevel ?? 1,
  1099. };
  1100. }
  1101. public getSampler(internalTexture: InternalTexture): GPUSampler {
  1102. const hash = WebGPUTextureHelper._GetSamplerHashCode(internalTexture);
  1103. let sampler = this._samplers[hash];
  1104. if (!sampler) {
  1105. sampler = this._samplers[hash] = this._device.createSampler(this._getSamplerDescriptor(internalTexture));
  1106. }
  1107. return sampler;
  1108. }
  1109. }