webgpuTextureHelper.ts 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  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. // TODO WEBGPU improve mipmap generation by not using the OutputAttachment flag
  24. // see https://github.com/toji/web-texture-tool/tree/main/src
  25. const mipmapVertexSource = `
  26. #version 450
  27. 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));
  28. 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));
  29. layout(location = 0) out vec2 vTex;
  30. void main() {
  31. vTex = tex[gl_VertexIndex];
  32. gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0);
  33. }
  34. `;
  35. const mipmapFragmentSource = `
  36. #version 450
  37. layout(set = 0, binding = 0) uniform sampler imgSampler;
  38. layout(set = 0, binding = 1) uniform texture2D img;
  39. layout(location = 0) in vec2 vTex;
  40. layout(location = 0) out vec4 outColor;
  41. void main() {
  42. outColor = texture(sampler2D(img, imgSampler), vTex);
  43. }
  44. `;
  45. export class WebGPUTextureHelper {
  46. private _device: GPUDevice;
  47. private _glslang: any;
  48. private _bufferManager: WebGPUBufferManager;
  49. private _mipmapSampler: GPUSampler;
  50. private _mipmapPipelines: { [format: string]: GPURenderPipeline } = {};
  51. public static computeNumMipmapLevels(width: number, height: number) {
  52. return Scalar.ILog2(Math.max(width, height)) + 1;
  53. }
  54. constructor(device: GPUDevice, glslang: any, bufferManager: WebGPUBufferManager) {
  55. this._device = device;
  56. this._glslang = glslang;
  57. this._bufferManager = bufferManager;
  58. this._mipmapSampler = device.createSampler({ minFilter: WebGPUConstants.FilterMode.Linear });
  59. this._getPipeline(WebGPUConstants.TextureFormat.RGBA8Unorm);
  60. }
  61. private _getPipeline(format: GPUTextureFormat): GPURenderPipeline {
  62. let pipeline = this._mipmapPipelines[format];
  63. if (!pipeline) {
  64. pipeline = this._mipmapPipelines[format] = this._device.createRenderPipeline({
  65. vertexStage: {
  66. module: this._device.createShaderModule({
  67. code: this._glslang.compileGLSL(mipmapVertexSource, 'vertex')
  68. }),
  69. entryPoint: 'main'
  70. },
  71. fragmentStage: {
  72. module: this._device.createShaderModule({
  73. code: this._glslang.compileGLSL(mipmapFragmentSource, 'fragment')
  74. }),
  75. entryPoint: 'main'
  76. },
  77. primitiveTopology: WebGPUConstants.PrimitiveTopology.TriangleStrip,
  78. vertexState: {
  79. indexFormat: WebGPUConstants.IndexFormat.Uint16
  80. },
  81. colorStates: [{
  82. format: format,
  83. }]
  84. });
  85. }
  86. return pipeline;
  87. }
  88. public isImageBitmap(imageBitmap: ImageBitmap | { width: number, height: number }): imageBitmap is ImageBitmap {
  89. return (imageBitmap as ImageBitmap).close !== undefined;
  90. }
  91. public isImageBitmapArray(imageBitmap: ImageBitmap[] | { width: number, height: number }): imageBitmap is ImageBitmap[] {
  92. return Array.isArray(imageBitmap as ImageBitmap[]) && (imageBitmap as ImageBitmap[])[0].close !== undefined;
  93. }
  94. public isCompressedFormat(format: GPUTextureFormat): boolean {
  95. switch (format) {
  96. case WebGPUConstants.TextureFormat.BC7RGBAUnormSRGB:
  97. case WebGPUConstants.TextureFormat.BC7RGBAUnorm:
  98. case WebGPUConstants.TextureFormat.BC6HRGBSFloat:
  99. case WebGPUConstants.TextureFormat.BC6HRGBUFloat:
  100. case WebGPUConstants.TextureFormat.BC5RGSnorm:
  101. case WebGPUConstants.TextureFormat.BC5RGUnorm:
  102. case WebGPUConstants.TextureFormat.BC4RSnorm:
  103. case WebGPUConstants.TextureFormat.BC4RUnorm:
  104. case WebGPUConstants.TextureFormat.BC3RGBAUnormSRGB:
  105. case WebGPUConstants.TextureFormat.BC3RGBAUnorm:
  106. case WebGPUConstants.TextureFormat.BC2RGBAUnormSRGB:
  107. case WebGPUConstants.TextureFormat.BC2RGBAUnorm:
  108. case WebGPUConstants.TextureFormat.BC1RGBAUnormSRGB:
  109. case WebGPUConstants.TextureFormat.BC1RGBAUNorm:
  110. return true;
  111. }
  112. return false;
  113. }
  114. public createTexture(imageBitmap: ImageBitmap | { width: number, height: number }, hasMipmaps = false, generateMipmaps = false, invertY = false, premultiplyAlpha = false, format: GPUTextureFormat = WebGPUConstants.TextureFormat.RGBA8Unorm,
  115. sampleCount = 1, commandEncoder?: GPUCommandEncoder, usage = -1): GPUTexture
  116. {
  117. let textureSize = {
  118. width: imageBitmap.width,
  119. height: imageBitmap.height,
  120. depth: 1,
  121. };
  122. const mipLevelCount = hasMipmaps ? WebGPUTextureHelper.computeNumMipmapLevels(imageBitmap.width, imageBitmap.height) : 1;
  123. const usages = usage >= 0 ? usage : WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.CopyDst | WebGPUConstants.TextureUsage.Sampled;
  124. const additionalUsages = hasMipmaps && !this.isCompressedFormat(format) ? WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.OutputAttachment : 0;
  125. const gpuTexture = this._device.createTexture({
  126. size: textureSize,
  127. dimension: WebGPUConstants.TextureDimension.E2d,
  128. format,
  129. usage: usages | additionalUsages,
  130. sampleCount,
  131. mipLevelCount
  132. });
  133. if (this.isImageBitmap(imageBitmap)) {
  134. this.updateTexture(imageBitmap, gpuTexture, imageBitmap.width, imageBitmap.height, format, 0, 0, invertY, premultiplyAlpha, 0, 0, commandEncoder);
  135. if (hasMipmaps && generateMipmaps) {
  136. this.generateMipmaps(gpuTexture, format, mipLevelCount, 0, commandEncoder);
  137. }
  138. }
  139. return gpuTexture;
  140. }
  141. public createCubeTexture(imageBitmaps: ImageBitmap[] | { width: number, height: number }, hasMipmaps = false, generateMipmaps = false, invertY = false, premultiplyAlpha = false, format: GPUTextureFormat = WebGPUConstants.TextureFormat.RGBA8Unorm,
  142. sampleCount = 1, commandEncoder?: GPUCommandEncoder, usage = -1): GPUTexture
  143. {
  144. const width = this.isImageBitmapArray(imageBitmaps) ? imageBitmaps[0].width : imageBitmaps.width;
  145. const height = this.isImageBitmapArray(imageBitmaps) ? imageBitmaps[0].height : imageBitmaps.height;
  146. const mipLevelCount = hasMipmaps ? WebGPUTextureHelper.computeNumMipmapLevels(width, height) : 1;
  147. const usages = usage >= 0 ? usage : WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.CopyDst | WebGPUConstants.TextureUsage.Sampled;
  148. const additionalUsages = hasMipmaps && !this.isCompressedFormat(format) ? WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.OutputAttachment : 0;
  149. const gpuTexture = this._device.createTexture({
  150. size: {
  151. width,
  152. height,
  153. depth: 6,
  154. },
  155. dimension: WebGPUConstants.TextureDimension.E2d,
  156. format,
  157. usage: usages | additionalUsages,
  158. sampleCount,
  159. mipLevelCount
  160. });
  161. if (this.isImageBitmapArray(imageBitmaps)) {
  162. this.updateCubeTextures(imageBitmaps, gpuTexture, width, height, format, invertY, premultiplyAlpha, 0, 0, commandEncoder);
  163. if (hasMipmaps && generateMipmaps) {
  164. this.generateCubeMipmaps(gpuTexture, format, mipLevelCount, commandEncoder);
  165. }
  166. }
  167. return gpuTexture;
  168. }
  169. public generateCubeMipmaps(gpuTexture: GPUTexture, format: GPUTextureFormat, mipLevelCount: number, commandEncoder?: GPUCommandEncoder): void {
  170. for (let f = 0; f < 6; ++f) {
  171. this.generateMipmaps(gpuTexture, format, mipLevelCount, f, commandEncoder);
  172. }
  173. }
  174. public generateMipmaps(gpuTexture: GPUTexture, format: GPUTextureFormat, mipLevelCount: number, faceIndex= 0, commandEncoder?: GPUCommandEncoder): void {
  175. const useOwnCommandEncoder = commandEncoder === undefined;
  176. const pipeline = this._getPipeline(format);
  177. const bindGroupLayout = pipeline.getBindGroupLayout(0);
  178. if (useOwnCommandEncoder) {
  179. commandEncoder = this._device.createCommandEncoder({});
  180. }
  181. commandEncoder!.pushDebugGroup("start create mipmaps");
  182. for (let i = 1; i < mipLevelCount; ++i) {
  183. const passEncoder = commandEncoder!.beginRenderPass({
  184. colorAttachments: [{
  185. attachment: gpuTexture.createView({
  186. dimension: WebGPUConstants.TextureViewDimension.E2d,
  187. baseMipLevel: i,
  188. mipLevelCount: 1,
  189. arrayLayerCount: 1,
  190. baseArrayLayer: faceIndex,
  191. }),
  192. loadValue: { r: 0.0, g: 0.0, b: 0.0, a: 0.0 },
  193. }],
  194. });
  195. const bindGroup = this._device.createBindGroup({
  196. layout: bindGroupLayout,
  197. entries: [{
  198. binding: 0,
  199. resource: this._mipmapSampler,
  200. }, {
  201. binding: 1,
  202. resource: gpuTexture.createView({
  203. dimension: WebGPUConstants.TextureViewDimension.E2d,
  204. baseMipLevel: i - 1,
  205. mipLevelCount: 1,
  206. arrayLayerCount: 1,
  207. baseArrayLayer: faceIndex,
  208. }),
  209. }],
  210. });
  211. passEncoder.setPipeline(pipeline);
  212. passEncoder.setBindGroup(0, bindGroup);
  213. passEncoder.draw(4, 1, 0, 0);
  214. passEncoder.endPass();
  215. }
  216. commandEncoder!.popDebugGroup();
  217. if (useOwnCommandEncoder) {
  218. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  219. commandEncoder = null as any;
  220. }
  221. }
  222. private _getTextureTypeFromFormat(format: GPUTextureFormat): number {
  223. switch (format) {
  224. // One Component = 8 bits
  225. case WebGPUConstants.TextureFormat.R8Unorm:
  226. case WebGPUConstants.TextureFormat.R8Snorm:
  227. case WebGPUConstants.TextureFormat.R8Uint:
  228. case WebGPUConstants.TextureFormat.R8Sint:
  229. case WebGPUConstants.TextureFormat.RG8Unorm:
  230. case WebGPUConstants.TextureFormat.RG8Snorm:
  231. case WebGPUConstants.TextureFormat.RG8Uint:
  232. case WebGPUConstants.TextureFormat.RG8Sint:
  233. case WebGPUConstants.TextureFormat.RGBA8Unorm:
  234. case WebGPUConstants.TextureFormat.RGBA8UnormSRGB:
  235. case WebGPUConstants.TextureFormat.RGBA8Snorm:
  236. case WebGPUConstants.TextureFormat.RGBA8Uint:
  237. case WebGPUConstants.TextureFormat.RGBA8Sint:
  238. case WebGPUConstants.TextureFormat.BGRA8Unorm:
  239. case WebGPUConstants.TextureFormat.BGRA8UnormSRGB:
  240. case WebGPUConstants.TextureFormat.RGB10A2Unorm: // composite format - let's say it's byte...
  241. case WebGPUConstants.TextureFormat.RGB9E5UFloat: // composite format - let's say it's byte...
  242. case WebGPUConstants.TextureFormat.RG11B10UFloat: // composite format - let's say it's byte...
  243. case WebGPUConstants.TextureFormat.Depth24UnormStencil8: // composite format - let's say it's byte...
  244. case WebGPUConstants.TextureFormat.Depth32FloatStencil8: // composite format - let's say it's byte...
  245. case WebGPUConstants.TextureFormat.BC7RGBAUnorm:
  246. case WebGPUConstants.TextureFormat.BC7RGBAUnormSRGB:
  247. case WebGPUConstants.TextureFormat.BC6HRGBUFloat:
  248. case WebGPUConstants.TextureFormat.BC6HRGBSFloat:
  249. case WebGPUConstants.TextureFormat.BC5RGUnorm:
  250. case WebGPUConstants.TextureFormat.BC5RGSnorm:
  251. case WebGPUConstants.TextureFormat.BC3RGBAUnorm:
  252. case WebGPUConstants.TextureFormat.BC3RGBAUnormSRGB:
  253. case WebGPUConstants.TextureFormat.BC2RGBAUnorm:
  254. case WebGPUConstants.TextureFormat.BC2RGBAUnormSRGB:
  255. case WebGPUConstants.TextureFormat.BC4RUnorm:
  256. case WebGPUConstants.TextureFormat.BC4RSnorm:
  257. case WebGPUConstants.TextureFormat.BC1RGBAUNorm:
  258. case WebGPUConstants.TextureFormat.BC1RGBAUnormSRGB:
  259. return Constants.TEXTURETYPE_UNSIGNED_BYTE;
  260. // One component = 16 bits
  261. case WebGPUConstants.TextureFormat.R16Uint:
  262. case WebGPUConstants.TextureFormat.R16Sint:
  263. case WebGPUConstants.TextureFormat.RG16Uint:
  264. case WebGPUConstants.TextureFormat.RG16Sint:
  265. case WebGPUConstants.TextureFormat.RGBA16Uint:
  266. case WebGPUConstants.TextureFormat.RGBA16Sint:
  267. case WebGPUConstants.TextureFormat.Depth16Unorm:
  268. return Constants.TEXTURETYPE_UNSIGNED_SHORT;
  269. case WebGPUConstants.TextureFormat.R16Float:
  270. case WebGPUConstants.TextureFormat.RG16Float:
  271. case WebGPUConstants.TextureFormat.RGBA16Float:
  272. return Constants.TEXTURETYPE_HALF_FLOAT;
  273. // One component = 32 bits
  274. case WebGPUConstants.TextureFormat.R32Uint:
  275. case WebGPUConstants.TextureFormat.R32Sint:
  276. case WebGPUConstants.TextureFormat.RG32Uint:
  277. case WebGPUConstants.TextureFormat.RG32Sint:
  278. case WebGPUConstants.TextureFormat.RGBA32Uint:
  279. case WebGPUConstants.TextureFormat.RGBA32Sint:
  280. return Constants.TEXTURETYPE_UNSIGNED_INTEGER;
  281. case WebGPUConstants.TextureFormat.R32Float:
  282. case WebGPUConstants.TextureFormat.RG32Float:
  283. case WebGPUConstants.TextureFormat.RGBA32Float:
  284. case WebGPUConstants.TextureFormat.Depth32Float:
  285. return Constants.TEXTURETYPE_FLOAT;
  286. case WebGPUConstants.TextureFormat.Stencil8:
  287. throw "No fixed size for Stencil8 format!";
  288. case WebGPUConstants.TextureFormat.Depth24Plus:
  289. throw "No fixed size for Depth24Plus format!";
  290. case WebGPUConstants.TextureFormat.Depth24PlusStencil8:
  291. throw "No fixed size for Depth24PlusStencil8 format!";
  292. }
  293. return Constants.TEXTURETYPE_UNSIGNED_BYTE;
  294. }
  295. private _getBlockInformationFromFormat(format: GPUTextureFormat): { width: number, height: number, length: number } {
  296. switch (format) {
  297. // 8 bits formats
  298. case WebGPUConstants.TextureFormat.R8Unorm:
  299. case WebGPUConstants.TextureFormat.R8Snorm:
  300. case WebGPUConstants.TextureFormat.R8Uint:
  301. case WebGPUConstants.TextureFormat.R8Sint:
  302. return { width: 1, height: 1, length: 1 };
  303. // 16 bits formats
  304. case WebGPUConstants.TextureFormat.R16Uint:
  305. case WebGPUConstants.TextureFormat.R16Sint:
  306. case WebGPUConstants.TextureFormat.R16Float:
  307. case WebGPUConstants.TextureFormat.RG8Unorm:
  308. case WebGPUConstants.TextureFormat.RG8Snorm:
  309. case WebGPUConstants.TextureFormat.RG8Uint:
  310. case WebGPUConstants.TextureFormat.RG8Sint:
  311. return { width: 1, height: 1, length: 2 };
  312. // 32 bits formats
  313. case WebGPUConstants.TextureFormat.R32Uint:
  314. case WebGPUConstants.TextureFormat.R32Sint:
  315. case WebGPUConstants.TextureFormat.R32Float:
  316. case WebGPUConstants.TextureFormat.RG16Uint:
  317. case WebGPUConstants.TextureFormat.RG16Sint:
  318. case WebGPUConstants.TextureFormat.RG16Float:
  319. case WebGPUConstants.TextureFormat.RGBA8Unorm:
  320. case WebGPUConstants.TextureFormat.RGBA8UnormSRGB:
  321. case WebGPUConstants.TextureFormat.RGBA8Snorm:
  322. case WebGPUConstants.TextureFormat.RGBA8Uint:
  323. case WebGPUConstants.TextureFormat.RGBA8Sint:
  324. case WebGPUConstants.TextureFormat.BGRA8Unorm:
  325. case WebGPUConstants.TextureFormat.BGRA8UnormSRGB:
  326. case WebGPUConstants.TextureFormat.RGB9E5UFloat:
  327. case WebGPUConstants.TextureFormat.RGB10A2Unorm:
  328. case WebGPUConstants.TextureFormat.RG11B10UFloat:
  329. return { width: 1, height: 1, length: 4 };
  330. // 64 bits formats
  331. case WebGPUConstants.TextureFormat.RG32Uint:
  332. case WebGPUConstants.TextureFormat.RG32Sint:
  333. case WebGPUConstants.TextureFormat.RG32Float:
  334. case WebGPUConstants.TextureFormat.RGBA16Uint:
  335. case WebGPUConstants.TextureFormat.RGBA16Sint:
  336. case WebGPUConstants.TextureFormat.RGBA16Float:
  337. return { width: 1, height: 1, length: 8 };
  338. // 128 bits formats
  339. case WebGPUConstants.TextureFormat.RGBA32Uint:
  340. case WebGPUConstants.TextureFormat.RGBA32Sint:
  341. case WebGPUConstants.TextureFormat.RGBA32Float:
  342. return { width: 1, height: 1, length: 16 };
  343. // Depth and stencil formats
  344. case WebGPUConstants.TextureFormat.Stencil8:
  345. throw "No fixed size for Stencil8 format!";
  346. case WebGPUConstants.TextureFormat.Depth16Unorm:
  347. return { width: 1, height: 1, length: 2 };
  348. case WebGPUConstants.TextureFormat.Depth24Plus:
  349. throw "No fixed size for Depth24Plus format!";
  350. case WebGPUConstants.TextureFormat.Depth24PlusStencil8:
  351. throw "No fixed size for Depth24PlusStencil8 format!";
  352. case WebGPUConstants.TextureFormat.Depth32Float:
  353. return { width: 1, height: 1, length: 4 };
  354. case WebGPUConstants.TextureFormat.Depth24UnormStencil8:
  355. return { width: 1, height: 1, length: 4 };
  356. case WebGPUConstants.TextureFormat.Depth32FloatStencil8:
  357. return { width: 1, height: 1, length: 5 };
  358. // BC compressed formats usable if "texture-compression-bc" is both
  359. // supported by the device/user agent and enabled in requestDevice.
  360. case WebGPUConstants.TextureFormat.BC7RGBAUnorm:
  361. case WebGPUConstants.TextureFormat.BC7RGBAUnormSRGB:
  362. case WebGPUConstants.TextureFormat.BC6HRGBUFloat:
  363. case WebGPUConstants.TextureFormat.BC6HRGBSFloat:
  364. case WebGPUConstants.TextureFormat.BC5RGUnorm:
  365. case WebGPUConstants.TextureFormat.BC5RGSnorm:
  366. case WebGPUConstants.TextureFormat.BC3RGBAUnorm:
  367. case WebGPUConstants.TextureFormat.BC3RGBAUnormSRGB:
  368. case WebGPUConstants.TextureFormat.BC2RGBAUnorm:
  369. case WebGPUConstants.TextureFormat.BC2RGBAUnormSRGB:
  370. return { width: 4, height: 4, length: 16 };
  371. case WebGPUConstants.TextureFormat.BC4RUnorm:
  372. case WebGPUConstants.TextureFormat.BC4RSnorm:
  373. case WebGPUConstants.TextureFormat.BC1RGBAUNorm:
  374. case WebGPUConstants.TextureFormat.BC1RGBAUnormSRGB:
  375. return { width: 4, height: 4, length: 8 };
  376. }
  377. return { width: 1, height: 1, length: 4 };
  378. }
  379. public updateCubeTextures(imageBitmaps: ImageBitmap[] | Uint8Array[], gpuTexture: GPUTexture, width: number, height: number, format: GPUTextureFormat, invertY = false, premultiplyAlpha = false, offsetX = 0, offsetY = 0,
  380. commandEncoder?: GPUCommandEncoder): void {
  381. const faces = [0, 3, 1, 4, 2, 5];
  382. for (let f = 0; f < faces.length; ++f) {
  383. let imageBitmap = imageBitmaps[faces[f]];
  384. this.updateTexture(imageBitmap, gpuTexture, width, height, format, f, 0, invertY, premultiplyAlpha, offsetX, offsetY, commandEncoder);
  385. }
  386. }
  387. public updateTexture(imageBitmap: ImageBitmap | Uint8Array, gpuTexture: GPUTexture, width: number, height: number, format: GPUTextureFormat, faceIndex: number = 0, mipLevel: number = 0, invertY = false, premultiplyAlpha = false, offsetX = 0, offsetY = 0,
  388. commandEncoder?: GPUCommandEncoder): void
  389. {
  390. const useOwnCommandEncoder = commandEncoder === undefined;
  391. if (useOwnCommandEncoder) {
  392. commandEncoder = this._device.createCommandEncoder({});
  393. }
  394. const blockInformation = this._getBlockInformationFromFormat(format);
  395. const textureCopyView: GPUTextureCopyView = {
  396. texture: gpuTexture,
  397. origin: {
  398. x: offsetX,
  399. y: offsetY,
  400. z: Math.max(faceIndex, 0)
  401. },
  402. mipLevel: mipLevel
  403. };
  404. const textureExtent = {
  405. width: Math.ceil(width / blockInformation.width) * blockInformation.width,
  406. height: Math.ceil(height / blockInformation.height) * blockInformation.height,
  407. depth: 1
  408. };
  409. if ((imageBitmap as Uint8Array).byteLength !== undefined) {
  410. // Note that we can't honor the invertY / premultiplyAlpha flags as we don't know the format of the array
  411. imageBitmap = imageBitmap as Uint8Array;
  412. const bytesPerRow = Math.ceil(width / blockInformation.width) * blockInformation.length;
  413. const aligned = Math.ceil(bytesPerRow / 256) * 256 === bytesPerRow;
  414. if (aligned) {
  415. const buffer = this._bufferManager.createRawBuffer(imageBitmap.byteLength, GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC, true);
  416. const arrayBuffer = buffer.getMappedRange();
  417. new Uint8Array(arrayBuffer).set(imageBitmap);
  418. buffer.unmap();
  419. commandEncoder!.copyBufferToTexture({
  420. buffer: buffer,
  421. offset: 0,
  422. bytesPerRow
  423. }, textureCopyView, textureExtent);
  424. if (useOwnCommandEncoder) {
  425. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  426. commandEncoder = null as any;
  427. }
  428. this._bufferManager.releaseBuffer(buffer);
  429. } else {
  430. this._device.defaultQueue.writeTexture(textureCopyView, imageBitmap, {
  431. offset: 0,
  432. bytesPerRow
  433. }, textureExtent);
  434. }
  435. } else {
  436. imageBitmap = imageBitmap as ImageBitmap;
  437. if (invertY || premultiplyAlpha) {
  438. createImageBitmap(imageBitmap, { imageOrientation: invertY ? "flipY" : "none", premultiplyAlpha: premultiplyAlpha ? "premultiply" : "none" }).then((imageBitmap) => {
  439. this._device.defaultQueue.copyImageBitmapToTexture({ imageBitmap }, textureCopyView, textureExtent);
  440. });
  441. } else {
  442. this._device.defaultQueue.copyImageBitmapToTexture({ imageBitmap }, textureCopyView, textureExtent);
  443. }
  444. }
  445. }
  446. 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> {
  447. const blockInformation = this._getBlockInformationFromFormat(format);
  448. const bytesPerRow = Math.ceil(width / blockInformation.width) * blockInformation.length;
  449. const size = bytesPerRow * height;
  450. const gpuBuffer = this._bufferManager.createRawBuffer(size, GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST);
  451. const commandEncoder = this._device.createCommandEncoder({});
  452. commandEncoder.copyTextureToBuffer({
  453. texture,
  454. mipLevel,
  455. origin: {
  456. x,
  457. y,
  458. z: Math.max(faceIndex, 0)
  459. }
  460. }, {
  461. buffer: gpuBuffer,
  462. offset: 0,
  463. bytesPerRow
  464. }, {
  465. width,
  466. height,
  467. depth: 1
  468. });
  469. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  470. const type = this._getTextureTypeFromFormat(format);
  471. const floatFormat = type === Constants.TEXTURETYPE_FLOAT ? 2 : type === Constants.TEXTURETYPE_HALF_FLOAT ? 1 : 0;
  472. return this._bufferManager.readDataFromBuffer(gpuBuffer, size, width, height, floatFormat, 0, buffer);
  473. }
  474. }