webgpuTextureHelper.ts 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003
  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 } from '../../Materials/Textures/internalTexture';
  24. import { HardwareTextureWrapper } from '../../Materials/Textures/hardwareTextureWrapper';
  25. import { BaseTexture } from '../../Materials/Textures/baseTexture';
  26. import { Engine } from '../engine';
  27. // TODO WEBGPU improve mipmap generation by not using the OutputAttachment flag
  28. // see https://github.com/toji/web-texture-tool/tree/main/src
  29. // TODO WEBGPU optimize, don't recreate things that can be cached (bind groups, descriptors, etc)
  30. // TODO WEBGPU use WGSL instead of GLSL
  31. const mipmapVertexSource = `
  32. 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));
  33. 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));
  34. layout(location = 0) out vec2 vTex;
  35. void main() {
  36. vTex = tex[gl_VertexIndex];
  37. gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0);
  38. }
  39. `;
  40. const mipmapFragmentSource = `
  41. layout(set = 0, binding = 0) uniform sampler imgSampler;
  42. layout(set = 0, binding = 1) uniform texture2D img;
  43. layout(location = 0) in vec2 vTex;
  44. layout(location = 0) out vec4 outColor;
  45. void main() {
  46. outColor = texture(sampler2D(img, imgSampler), vTex);
  47. }
  48. `;
  49. const invertYPreMultiplyAlphaVertexSource = `
  50. 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));
  51. 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));
  52. layout(location = 0) out vec2 vTex;
  53. void main() {
  54. vTex = tex[gl_VertexIndex];
  55. #ifdef INVERTY
  56. vTex.y = 1.0 - vTex.y;
  57. #endif
  58. gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0);
  59. }
  60. `;
  61. const invertYPreMultiplyAlphaFragmentSource = `
  62. layout(set = 0, binding = 0) uniform sampler imgSampler;
  63. layout(set = 0, binding = 1) uniform texture2D img;
  64. layout(location = 0) in vec2 vTex;
  65. layout(location = 0) out vec4 outColor;
  66. void main() {
  67. vec4 color = texture(sampler2D(img, imgSampler), vTex);
  68. #ifdef PREMULTIPLYALPHA
  69. color.rgb *= color.a;
  70. #endif
  71. outColor = color;
  72. }
  73. `;
  74. const filterToBits = [
  75. 0 | 0 << 1 | 0 << 2, // not used
  76. 0 | 0 << 1 | 0 << 2, // TEXTURE_NEAREST_SAMPLINGMODE / TEXTURE_NEAREST_NEAREST
  77. 1 | 1 << 1 | 0 << 2, // TEXTURE_BILINEAR_SAMPLINGMODE / TEXTURE_LINEAR_LINEAR
  78. 1 | 1 << 1 | 1 << 2, // TEXTURE_TRILINEAR_SAMPLINGMODE / TEXTURE_LINEAR_LINEAR_MIPLINEAR
  79. 0 | 0 << 1 | 0 << 2, // TEXTURE_NEAREST_NEAREST_MIPNEAREST
  80. 0 | 1 << 1 | 0 << 2, // TEXTURE_NEAREST_LINEAR_MIPNEAREST
  81. 0 | 1 << 1 | 1 << 2, // TEXTURE_NEAREST_LINEAR_MIPLINEAR
  82. 0 | 1 << 1 | 0 << 2, // TEXTURE_NEAREST_LINEAR
  83. 0 | 0 << 1 | 1 << 2, // TEXTURE_NEAREST_NEAREST_MIPLINEAR
  84. 1 | 0 << 1 | 0 << 2, // TEXTURE_LINEAR_NEAREST_MIPNEAREST
  85. 1 | 0 << 1 | 1 << 2, // TEXTURE_LINEAR_NEAREST_MIPLINEAR
  86. 1 | 1 << 1 | 0 << 2, // TEXTURE_LINEAR_LINEAR_MIPNEAREST
  87. 1 | 0 << 1 | 0 << 2, // TEXTURE_LINEAR_NEAREST
  88. ];
  89. // subtract 0x01FF from the comparison function value before indexing this array!
  90. const comparisonFunctionToBits = [
  91. 0 << 3 | 0 << 4 | 0 << 5 | 0 << 6, // undefined
  92. 0 << 3 | 0 << 4 | 0 << 5 | 1 << 6, // NEVER
  93. 0 << 3 | 0 << 4 | 1 << 5 | 0 << 6, // LESS
  94. 0 << 3 | 0 << 4 | 1 << 5 | 1 << 6, // EQUAL
  95. 0 << 3 | 1 << 4 | 0 << 5 | 0 << 6, // LEQUAL
  96. 0 << 3 | 1 << 4 | 0 << 5 | 1 << 6, // GREATER
  97. 0 << 3 | 1 << 4 | 1 << 5 | 0 << 6, // NOTEQUAL
  98. 0 << 3 | 1 << 4 | 1 << 5 | 1 << 6, // GEQUAL
  99. 1 << 3 | 0 << 4 | 0 << 5 | 0 << 6, // ALWAYS
  100. ];
  101. const filterNoMipToBits = [
  102. 0 << 7, // not used
  103. 1 << 7, // TEXTURE_NEAREST_SAMPLINGMODE / TEXTURE_NEAREST_NEAREST
  104. 1 << 7, // TEXTURE_BILINEAR_SAMPLINGMODE / TEXTURE_LINEAR_LINEAR
  105. 0 << 7, // TEXTURE_TRILINEAR_SAMPLINGMODE / TEXTURE_LINEAR_LINEAR_MIPLINEAR
  106. 0 << 7, // TEXTURE_NEAREST_NEAREST_MIPNEAREST
  107. 0 << 7, // TEXTURE_NEAREST_LINEAR_MIPNEAREST
  108. 0 << 7, // TEXTURE_NEAREST_LINEAR_MIPLINEAR
  109. 1 << 7, // TEXTURE_NEAREST_LINEAR
  110. 0 << 7, // TEXTURE_NEAREST_NEAREST_MIPLINEAR
  111. 0 << 7, // TEXTURE_LINEAR_NEAREST_MIPNEAREST
  112. 0 << 7, // TEXTURE_LINEAR_NEAREST_MIPLINEAR
  113. 0 << 7, // TEXTURE_LINEAR_LINEAR_MIPNEAREST
  114. 1 << 7, // TEXTURE_LINEAR_NEAREST
  115. ];
  116. export class WebGPUTextureHelper {
  117. private _device: GPUDevice;
  118. private _glslang: any;
  119. private _bufferManager: WebGPUBufferManager;
  120. private _mipmapSampler: GPUSampler;
  121. private _invertYPreMultiplyAlphaSampler: GPUSampler;
  122. private _pipelines: { [format: string]: Array<GPURenderPipeline> } = {};
  123. private _compiledShaders: GPUShaderModule[][] = [];
  124. private _deferredReleaseTextures: Array<[Nullable<InternalTexture>, Nullable<HardwareTextureWrapper | GPUTexture>, Nullable<BaseTexture>, Nullable<InternalTexture>]> = [];
  125. private _samplers: { [hash: number]: GPUSampler } = {};
  126. public static computeNumMipmapLevels(width: number, height: number) {
  127. return Scalar.ILog2(Math.max(width, height)) + 1;
  128. }
  129. private static _GetSamplerHashCode(texture: InternalTexture): number {
  130. let code =
  131. filterToBits[texture.samplingMode] +
  132. comparisonFunctionToBits[(texture._comparisonFunction || 0x0202) - 0x0200 + 1] +
  133. filterNoMipToBits[texture.samplingMode] + // handle the lodMinClamp = lodMaxClamp = 0 case when no filter used for mip mapping
  134. ((texture._cachedWrapU ?? 1) << 8) +
  135. ((texture._cachedWrapV ?? 1) << 10) +
  136. ((texture._cachedWrapR ?? 1) << 12) +
  137. ((texture._cachedAnisotropicFilteringLevel ?? 1) << 14);
  138. return code;
  139. }
  140. constructor(device: GPUDevice, glslang: any, bufferManager: WebGPUBufferManager) {
  141. this._device = device;
  142. this._glslang = glslang;
  143. this._bufferManager = bufferManager;
  144. this._mipmapSampler = device.createSampler({ minFilter: WebGPUConstants.FilterMode.Linear });
  145. this._invertYPreMultiplyAlphaSampler = device.createSampler({ minFilter: WebGPUConstants.FilterMode.Nearest, magFilter: WebGPUConstants.FilterMode.Nearest });
  146. this._getPipeline(WebGPUConstants.TextureFormat.RGBA8Unorm);
  147. }
  148. private _getPipeline(format: GPUTextureFormat, forMipMap = true, invertY = false, premultiplyAlpha = false): GPURenderPipeline {
  149. const index = (forMipMap ? 1 : 0) + ((invertY ? 1 : 0) << 1) + (premultiplyAlpha ? 1 : 0) << 2;
  150. if (!this._pipelines[format]) {
  151. this._pipelines[format] = [];
  152. }
  153. let pipeline = this._pipelines[format][index];
  154. if (!pipeline) {
  155. let defines = "#version 450\r\n";
  156. if (invertY) {
  157. defines += "#define INVERTY\r\n";
  158. }
  159. if (premultiplyAlpha) {
  160. defines += "define PREMULTIPLYALPHA\r\n";
  161. }
  162. let modules = this._compiledShaders[index];
  163. if (!modules) {
  164. const vertexModule = this._device.createShaderModule({
  165. code: this._glslang.compileGLSL(forMipMap ? defines + mipmapVertexSource : defines + invertYPreMultiplyAlphaVertexSource, 'vertex')
  166. });
  167. const fragmentModule = this._device.createShaderModule({
  168. code: this._glslang.compileGLSL(forMipMap ? defines + mipmapFragmentSource : defines + invertYPreMultiplyAlphaFragmentSource, 'fragment')
  169. });
  170. modules = this._compiledShaders[index] = [vertexModule, fragmentModule];
  171. }
  172. pipeline = this._pipelines[format][index] = this._device.createRenderPipeline({
  173. vertexStage: {
  174. module: modules[0],
  175. entryPoint: 'main'
  176. },
  177. fragmentStage: {
  178. module: modules[1],
  179. entryPoint: 'main'
  180. },
  181. primitiveTopology: WebGPUConstants.PrimitiveTopology.TriangleStrip,
  182. vertexState: {
  183. indexFormat: WebGPUConstants.IndexFormat.Uint16
  184. },
  185. colorStates: [{
  186. format,
  187. }]
  188. });
  189. }
  190. return pipeline;
  191. }
  192. private _isHardwareTexture(texture: HardwareTextureWrapper | GPUTexture): texture is HardwareTextureWrapper {
  193. return !!(texture as HardwareTextureWrapper).release;
  194. }
  195. public isImageBitmap(imageBitmap: ImageBitmap | { width: number, height: number }): imageBitmap is ImageBitmap {
  196. return (imageBitmap as ImageBitmap).close !== undefined;
  197. }
  198. public isImageBitmapArray(imageBitmap: ImageBitmap[] | { width: number, height: number }): imageBitmap is ImageBitmap[] {
  199. return Array.isArray(imageBitmap as ImageBitmap[]) && (imageBitmap as ImageBitmap[])[0].close !== undefined;
  200. }
  201. public isCompressedFormat(format: GPUTextureFormat): boolean {
  202. switch (format) {
  203. case WebGPUConstants.TextureFormat.BC7RGBAUnormSRGB:
  204. case WebGPUConstants.TextureFormat.BC7RGBAUnorm:
  205. case WebGPUConstants.TextureFormat.BC6HRGBFloat:
  206. case WebGPUConstants.TextureFormat.BC6HRGBUFloat:
  207. case WebGPUConstants.TextureFormat.BC5RGSnorm:
  208. case WebGPUConstants.TextureFormat.BC5RGUnorm:
  209. case WebGPUConstants.TextureFormat.BC4RSnorm:
  210. case WebGPUConstants.TextureFormat.BC4RUnorm:
  211. case WebGPUConstants.TextureFormat.BC3RGBAUnormSRGB:
  212. case WebGPUConstants.TextureFormat.BC3RGBAUnorm:
  213. case WebGPUConstants.TextureFormat.BC2RGBAUnormSRGB:
  214. case WebGPUConstants.TextureFormat.BC2RGBAUnorm:
  215. case WebGPUConstants.TextureFormat.BC1RGBAUnormSRGB:
  216. case WebGPUConstants.TextureFormat.BC1RGBAUNorm:
  217. return true;
  218. }
  219. return false;
  220. }
  221. 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,
  222. sampleCount = 1, commandEncoder?: GPUCommandEncoder, usage = -1): GPUTexture
  223. {
  224. const layerCount = (imageBitmap as any).layers || 1;
  225. let textureSize = {
  226. width: imageBitmap.width,
  227. height: imageBitmap.height,
  228. depth: layerCount,
  229. };
  230. const mipLevelCount = hasMipmaps ? WebGPUTextureHelper.computeNumMipmapLevels(imageBitmap.width, imageBitmap.height) : 1;
  231. const usages = usage >= 0 ? usage : WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.CopyDst | WebGPUConstants.TextureUsage.Sampled;
  232. const additionalUsages = hasMipmaps && !this.isCompressedFormat(format) ? WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.OutputAttachment : 0;
  233. const gpuTexture = this._device.createTexture({
  234. size: textureSize,
  235. dimension: is3D ? WebGPUConstants.TextureDimension.E3d : WebGPUConstants.TextureDimension.E2d,
  236. format,
  237. usage: usages | additionalUsages,
  238. sampleCount,
  239. mipLevelCount
  240. });
  241. if (this.isImageBitmap(imageBitmap)) {
  242. this.updateTexture(imageBitmap, gpuTexture, imageBitmap.width, imageBitmap.height, layerCount, format, 0, 0, invertY, premultiplyAlpha, 0, 0, commandEncoder);
  243. if (hasMipmaps && generateMipmaps) {
  244. this.generateMipmaps(gpuTexture, format, mipLevelCount, 0, commandEncoder);
  245. }
  246. }
  247. return gpuTexture;
  248. }
  249. public createCubeTexture(imageBitmaps: ImageBitmap[] | { width: number, height: number }, hasMipmaps = false, generateMipmaps = false, invertY = false, premultiplyAlpha = false, format: GPUTextureFormat = WebGPUConstants.TextureFormat.RGBA8Unorm,
  250. sampleCount = 1, commandEncoder?: GPUCommandEncoder, usage = -1): GPUTexture
  251. {
  252. const width = this.isImageBitmapArray(imageBitmaps) ? imageBitmaps[0].width : imageBitmaps.width;
  253. const height = this.isImageBitmapArray(imageBitmaps) ? imageBitmaps[0].height : imageBitmaps.height;
  254. const mipLevelCount = hasMipmaps ? WebGPUTextureHelper.computeNumMipmapLevels(width, height) : 1;
  255. const usages = usage >= 0 ? usage : WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.CopyDst | WebGPUConstants.TextureUsage.Sampled;
  256. const additionalUsages = hasMipmaps && !this.isCompressedFormat(format) ? WebGPUConstants.TextureUsage.CopySrc | WebGPUConstants.TextureUsage.OutputAttachment : 0;
  257. const gpuTexture = this._device.createTexture({
  258. size: {
  259. width,
  260. height,
  261. depth: 6,
  262. },
  263. dimension: WebGPUConstants.TextureDimension.E2d,
  264. format,
  265. usage: usages | additionalUsages,
  266. sampleCount,
  267. mipLevelCount
  268. });
  269. if (this.isImageBitmapArray(imageBitmaps)) {
  270. this.updateCubeTextures(imageBitmaps, gpuTexture, width, height, format, invertY, premultiplyAlpha, 0, 0, commandEncoder);
  271. if (hasMipmaps && generateMipmaps) {
  272. this.generateCubeMipmaps(gpuTexture, format, mipLevelCount, commandEncoder);
  273. }
  274. }
  275. return gpuTexture;
  276. }
  277. public generateCubeMipmaps(gpuTexture: GPUTexture, format: GPUTextureFormat, mipLevelCount: number, commandEncoder?: GPUCommandEncoder): void {
  278. const useOwnCommandEncoder = commandEncoder === undefined;
  279. if (useOwnCommandEncoder) {
  280. commandEncoder = this._device.createCommandEncoder({});
  281. }
  282. commandEncoder!.pushDebugGroup(`create cube mipmaps - ${mipLevelCount} levels`);
  283. for (let f = 0; f < 6; ++f) {
  284. this.generateMipmaps(gpuTexture, format, mipLevelCount, f, commandEncoder);
  285. }
  286. commandEncoder!.popDebugGroup();
  287. if (useOwnCommandEncoder) {
  288. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  289. commandEncoder = null as any;
  290. }
  291. }
  292. public invertYPreMultiplyAlpha(gpuTexture: GPUTexture, width: number, height: number, format: GPUTextureFormat, invertY = false, premultiplyAlpha = false, faceIndex= 0, commandEncoder?: GPUCommandEncoder): void {
  293. const useOwnCommandEncoder = commandEncoder === undefined;
  294. const pipeline = this._getPipeline(format, false, invertY, premultiplyAlpha);
  295. const bindGroupLayout = pipeline.getBindGroupLayout(0);
  296. if (useOwnCommandEncoder) {
  297. commandEncoder = this._device.createCommandEncoder({});
  298. }
  299. commandEncoder!.pushDebugGroup(`internal process texture - invertY=${invertY} premultiplyAlpha=${premultiplyAlpha}`);
  300. 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);
  301. const passEncoder = commandEncoder!.beginRenderPass({
  302. colorAttachments: [{
  303. attachment: outputTexture.createView({
  304. dimension: WebGPUConstants.TextureViewDimension.E2d,
  305. baseMipLevel: 0,
  306. mipLevelCount: 1,
  307. arrayLayerCount: 1,
  308. baseArrayLayer: 0,
  309. }),
  310. loadValue: WebGPUConstants.LoadOp.Load,
  311. }],
  312. });
  313. const bindGroup = this._device.createBindGroup({
  314. layout: bindGroupLayout,
  315. entries: [{
  316. binding: 0,
  317. resource: this._invertYPreMultiplyAlphaSampler,
  318. }, {
  319. binding: 1,
  320. resource: gpuTexture.createView({
  321. dimension: WebGPUConstants.TextureViewDimension.E2d,
  322. baseMipLevel: 0,
  323. mipLevelCount: 1,
  324. arrayLayerCount: 1,
  325. baseArrayLayer: faceIndex,
  326. }),
  327. }],
  328. });
  329. passEncoder.setPipeline(pipeline);
  330. passEncoder.setBindGroup(0, bindGroup);
  331. passEncoder.draw(4, 1, 0, 0);
  332. passEncoder.endPass();
  333. commandEncoder!.copyTextureToTexture({
  334. texture: outputTexture,
  335. }, {
  336. texture: gpuTexture,
  337. origin: {
  338. x: 0,
  339. y: 0,
  340. z: Math.max(faceIndex, 0),
  341. }
  342. }, {
  343. width,
  344. height,
  345. depth: 1,
  346. }
  347. );
  348. this._deferredReleaseTextures.push([null, outputTexture, null, null]);
  349. commandEncoder!.popDebugGroup();
  350. if (useOwnCommandEncoder) {
  351. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  352. commandEncoder = null as any;
  353. }
  354. }
  355. public generateMipmaps(gpuTexture: GPUTexture, format: GPUTextureFormat, mipLevelCount: number, faceIndex= 0, commandEncoder?: GPUCommandEncoder): void {
  356. const useOwnCommandEncoder = commandEncoder === undefined;
  357. const pipeline = this._getPipeline(format);
  358. const bindGroupLayout = pipeline.getBindGroupLayout(0);
  359. if (useOwnCommandEncoder) {
  360. commandEncoder = this._device.createCommandEncoder({});
  361. }
  362. commandEncoder!.pushDebugGroup(`create mipmaps for face #${faceIndex} - ${mipLevelCount} levels`);
  363. for (let i = 1; i < mipLevelCount; ++i) {
  364. const passEncoder = commandEncoder!.beginRenderPass({
  365. colorAttachments: [{
  366. attachment: gpuTexture.createView({
  367. dimension: WebGPUConstants.TextureViewDimension.E2d,
  368. baseMipLevel: i,
  369. mipLevelCount: 1,
  370. arrayLayerCount: 1,
  371. baseArrayLayer: faceIndex,
  372. }),
  373. loadValue: { r: 0.0, g: 0.0, b: 0.0, a: 0.0 },
  374. }],
  375. });
  376. const bindGroup = this._device.createBindGroup({
  377. layout: bindGroupLayout,
  378. entries: [{
  379. binding: 0,
  380. resource: this._mipmapSampler,
  381. }, {
  382. binding: 1,
  383. resource: gpuTexture.createView({
  384. dimension: WebGPUConstants.TextureViewDimension.E2d,
  385. baseMipLevel: i - 1,
  386. mipLevelCount: 1,
  387. arrayLayerCount: 1,
  388. baseArrayLayer: faceIndex,
  389. }),
  390. }],
  391. });
  392. passEncoder.setPipeline(pipeline);
  393. passEncoder.setBindGroup(0, bindGroup);
  394. passEncoder.draw(4, 1, 0, 0);
  395. passEncoder.endPass();
  396. }
  397. commandEncoder!.popDebugGroup();
  398. if (useOwnCommandEncoder) {
  399. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  400. commandEncoder = null as any;
  401. }
  402. }
  403. private _getTextureTypeFromFormat(format: GPUTextureFormat): number {
  404. switch (format) {
  405. // One Component = 8 bits
  406. case WebGPUConstants.TextureFormat.R8Unorm:
  407. case WebGPUConstants.TextureFormat.R8Snorm:
  408. case WebGPUConstants.TextureFormat.R8Uint:
  409. case WebGPUConstants.TextureFormat.R8Sint:
  410. case WebGPUConstants.TextureFormat.RG8Unorm:
  411. case WebGPUConstants.TextureFormat.RG8Snorm:
  412. case WebGPUConstants.TextureFormat.RG8Uint:
  413. case WebGPUConstants.TextureFormat.RG8Sint:
  414. case WebGPUConstants.TextureFormat.RGBA8Unorm:
  415. case WebGPUConstants.TextureFormat.RGBA8UnormSRGB:
  416. case WebGPUConstants.TextureFormat.RGBA8Snorm:
  417. case WebGPUConstants.TextureFormat.RGBA8Uint:
  418. case WebGPUConstants.TextureFormat.RGBA8Sint:
  419. case WebGPUConstants.TextureFormat.BGRA8Unorm:
  420. case WebGPUConstants.TextureFormat.BGRA8UnormSRGB:
  421. case WebGPUConstants.TextureFormat.RGB10A2Unorm: // composite format - let's say it's byte...
  422. case WebGPUConstants.TextureFormat.RGB9E5UFloat: // composite format - let's say it's byte...
  423. case WebGPUConstants.TextureFormat.RG11B10UFloat: // composite format - let's say it's byte...
  424. case WebGPUConstants.TextureFormat.Depth24UnormStencil8: // composite format - let's say it's byte...
  425. case WebGPUConstants.TextureFormat.Depth32FloatStencil8: // composite format - let's say it's byte...
  426. case WebGPUConstants.TextureFormat.BC7RGBAUnorm:
  427. case WebGPUConstants.TextureFormat.BC7RGBAUnormSRGB:
  428. case WebGPUConstants.TextureFormat.BC6HRGBUFloat:
  429. case WebGPUConstants.TextureFormat.BC6HRGBFloat:
  430. case WebGPUConstants.TextureFormat.BC5RGUnorm:
  431. case WebGPUConstants.TextureFormat.BC5RGSnorm:
  432. case WebGPUConstants.TextureFormat.BC3RGBAUnorm:
  433. case WebGPUConstants.TextureFormat.BC3RGBAUnormSRGB:
  434. case WebGPUConstants.TextureFormat.BC2RGBAUnorm:
  435. case WebGPUConstants.TextureFormat.BC2RGBAUnormSRGB:
  436. case WebGPUConstants.TextureFormat.BC4RUnorm:
  437. case WebGPUConstants.TextureFormat.BC4RSnorm:
  438. case WebGPUConstants.TextureFormat.BC1RGBAUNorm:
  439. case WebGPUConstants.TextureFormat.BC1RGBAUnormSRGB:
  440. return Constants.TEXTURETYPE_UNSIGNED_BYTE;
  441. // One component = 16 bits
  442. case WebGPUConstants.TextureFormat.R16Uint:
  443. case WebGPUConstants.TextureFormat.R16Sint:
  444. case WebGPUConstants.TextureFormat.RG16Uint:
  445. case WebGPUConstants.TextureFormat.RG16Sint:
  446. case WebGPUConstants.TextureFormat.RGBA16Uint:
  447. case WebGPUConstants.TextureFormat.RGBA16Sint:
  448. case WebGPUConstants.TextureFormat.Depth16Unorm:
  449. return Constants.TEXTURETYPE_UNSIGNED_SHORT;
  450. case WebGPUConstants.TextureFormat.R16Float:
  451. case WebGPUConstants.TextureFormat.RG16Float:
  452. case WebGPUConstants.TextureFormat.RGBA16Float:
  453. return Constants.TEXTURETYPE_HALF_FLOAT;
  454. // One component = 32 bits
  455. case WebGPUConstants.TextureFormat.R32Uint:
  456. case WebGPUConstants.TextureFormat.R32Sint:
  457. case WebGPUConstants.TextureFormat.RG32Uint:
  458. case WebGPUConstants.TextureFormat.RG32Sint:
  459. case WebGPUConstants.TextureFormat.RGBA32Uint:
  460. case WebGPUConstants.TextureFormat.RGBA32Sint:
  461. return Constants.TEXTURETYPE_UNSIGNED_INTEGER;
  462. case WebGPUConstants.TextureFormat.R32Float:
  463. case WebGPUConstants.TextureFormat.RG32Float:
  464. case WebGPUConstants.TextureFormat.RGBA32Float:
  465. case WebGPUConstants.TextureFormat.Depth32Float:
  466. return Constants.TEXTURETYPE_FLOAT;
  467. case WebGPUConstants.TextureFormat.Stencil8:
  468. throw "No fixed size for Stencil8 format!";
  469. case WebGPUConstants.TextureFormat.Depth24Plus:
  470. throw "No fixed size for Depth24Plus format!";
  471. case WebGPUConstants.TextureFormat.Depth24PlusStencil8:
  472. throw "No fixed size for Depth24PlusStencil8 format!";
  473. }
  474. return Constants.TEXTURETYPE_UNSIGNED_BYTE;
  475. }
  476. private _getBlockInformationFromFormat(format: GPUTextureFormat): { width: number, height: number, length: number } {
  477. switch (format) {
  478. // 8 bits formats
  479. case WebGPUConstants.TextureFormat.R8Unorm:
  480. case WebGPUConstants.TextureFormat.R8Snorm:
  481. case WebGPUConstants.TextureFormat.R8Uint:
  482. case WebGPUConstants.TextureFormat.R8Sint:
  483. return { width: 1, height: 1, length: 1 };
  484. // 16 bits formats
  485. case WebGPUConstants.TextureFormat.R16Uint:
  486. case WebGPUConstants.TextureFormat.R16Sint:
  487. case WebGPUConstants.TextureFormat.R16Float:
  488. case WebGPUConstants.TextureFormat.RG8Unorm:
  489. case WebGPUConstants.TextureFormat.RG8Snorm:
  490. case WebGPUConstants.TextureFormat.RG8Uint:
  491. case WebGPUConstants.TextureFormat.RG8Sint:
  492. return { width: 1, height: 1, length: 2 };
  493. // 32 bits formats
  494. case WebGPUConstants.TextureFormat.R32Uint:
  495. case WebGPUConstants.TextureFormat.R32Sint:
  496. case WebGPUConstants.TextureFormat.R32Float:
  497. case WebGPUConstants.TextureFormat.RG16Uint:
  498. case WebGPUConstants.TextureFormat.RG16Sint:
  499. case WebGPUConstants.TextureFormat.RG16Float:
  500. case WebGPUConstants.TextureFormat.RGBA8Unorm:
  501. case WebGPUConstants.TextureFormat.RGBA8UnormSRGB:
  502. case WebGPUConstants.TextureFormat.RGBA8Snorm:
  503. case WebGPUConstants.TextureFormat.RGBA8Uint:
  504. case WebGPUConstants.TextureFormat.RGBA8Sint:
  505. case WebGPUConstants.TextureFormat.BGRA8Unorm:
  506. case WebGPUConstants.TextureFormat.BGRA8UnormSRGB:
  507. case WebGPUConstants.TextureFormat.RGB9E5UFloat:
  508. case WebGPUConstants.TextureFormat.RGB10A2Unorm:
  509. case WebGPUConstants.TextureFormat.RG11B10UFloat:
  510. return { width: 1, height: 1, length: 4 };
  511. // 64 bits formats
  512. case WebGPUConstants.TextureFormat.RG32Uint:
  513. case WebGPUConstants.TextureFormat.RG32Sint:
  514. case WebGPUConstants.TextureFormat.RG32Float:
  515. case WebGPUConstants.TextureFormat.RGBA16Uint:
  516. case WebGPUConstants.TextureFormat.RGBA16Sint:
  517. case WebGPUConstants.TextureFormat.RGBA16Float:
  518. return { width: 1, height: 1, length: 8 };
  519. // 128 bits formats
  520. case WebGPUConstants.TextureFormat.RGBA32Uint:
  521. case WebGPUConstants.TextureFormat.RGBA32Sint:
  522. case WebGPUConstants.TextureFormat.RGBA32Float:
  523. return { width: 1, height: 1, length: 16 };
  524. // Depth and stencil formats
  525. case WebGPUConstants.TextureFormat.Stencil8:
  526. throw "No fixed size for Stencil8 format!";
  527. case WebGPUConstants.TextureFormat.Depth16Unorm:
  528. return { width: 1, height: 1, length: 2 };
  529. case WebGPUConstants.TextureFormat.Depth24Plus:
  530. throw "No fixed size for Depth24Plus format!";
  531. case WebGPUConstants.TextureFormat.Depth24PlusStencil8:
  532. throw "No fixed size for Depth24PlusStencil8 format!";
  533. case WebGPUConstants.TextureFormat.Depth32Float:
  534. return { width: 1, height: 1, length: 4 };
  535. case WebGPUConstants.TextureFormat.Depth24UnormStencil8:
  536. return { width: 1, height: 1, length: 4 };
  537. case WebGPUConstants.TextureFormat.Depth32FloatStencil8:
  538. return { width: 1, height: 1, length: 5 };
  539. // BC compressed formats usable if "texture-compression-bc" is both
  540. // supported by the device/user agent and enabled in requestDevice.
  541. case WebGPUConstants.TextureFormat.BC7RGBAUnorm:
  542. case WebGPUConstants.TextureFormat.BC7RGBAUnormSRGB:
  543. case WebGPUConstants.TextureFormat.BC6HRGBUFloat:
  544. case WebGPUConstants.TextureFormat.BC6HRGBFloat:
  545. case WebGPUConstants.TextureFormat.BC5RGUnorm:
  546. case WebGPUConstants.TextureFormat.BC5RGSnorm:
  547. case WebGPUConstants.TextureFormat.BC3RGBAUnorm:
  548. case WebGPUConstants.TextureFormat.BC3RGBAUnormSRGB:
  549. case WebGPUConstants.TextureFormat.BC2RGBAUnorm:
  550. case WebGPUConstants.TextureFormat.BC2RGBAUnormSRGB:
  551. return { width: 4, height: 4, length: 16 };
  552. case WebGPUConstants.TextureFormat.BC4RUnorm:
  553. case WebGPUConstants.TextureFormat.BC4RSnorm:
  554. case WebGPUConstants.TextureFormat.BC1RGBAUNorm:
  555. case WebGPUConstants.TextureFormat.BC1RGBAUnormSRGB:
  556. return { width: 4, height: 4, length: 8 };
  557. }
  558. return { width: 1, height: 1, length: 4 };
  559. }
  560. public updateCubeTextures(imageBitmaps: ImageBitmap[] | Uint8Array[], gpuTexture: GPUTexture, width: number, height: number, format: GPUTextureFormat, invertY = false, premultiplyAlpha = false, offsetX = 0, offsetY = 0,
  561. commandEncoder?: GPUCommandEncoder): void {
  562. const faces = [0, 3, 1, 4, 2, 5];
  563. for (let f = 0; f < faces.length; ++f) {
  564. let imageBitmap = imageBitmaps[faces[f]];
  565. this.updateTexture(imageBitmap, gpuTexture, width, height, 1, format, f, 0, invertY, premultiplyAlpha, offsetX, offsetY, commandEncoder);
  566. }
  567. }
  568. // TODO WEBGPU handle data source not being in the same format than the destination texture?
  569. 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,
  570. commandEncoder?: GPUCommandEncoder): void
  571. {
  572. const blockInformation = this._getBlockInformationFromFormat(format);
  573. const textureCopyView: GPUTextureCopyView = {
  574. texture: gpuTexture,
  575. origin: {
  576. x: offsetX,
  577. y: offsetY,
  578. z: Math.max(faceIndex, 0)
  579. },
  580. mipLevel: mipLevel
  581. };
  582. const textureExtent = {
  583. width: Math.ceil(width / blockInformation.width) * blockInformation.width,
  584. height: Math.ceil(height / blockInformation.height) * blockInformation.height,
  585. depth: layers || 1
  586. };
  587. if ((imageBitmap as Uint8Array).byteLength !== undefined) {
  588. imageBitmap = imageBitmap as Uint8Array;
  589. const bytesPerRow = Math.ceil(width / blockInformation.width) * blockInformation.length;
  590. const aligned = Math.ceil(bytesPerRow / 256) * 256 === bytesPerRow;
  591. if (aligned) {
  592. const useOwnCommandEncoder = commandEncoder === undefined;
  593. if (useOwnCommandEncoder) {
  594. commandEncoder = this._device.createCommandEncoder({});
  595. }
  596. const buffer = this._bufferManager.createRawBuffer(imageBitmap.byteLength, GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC, true);
  597. const arrayBuffer = buffer.getMappedRange();
  598. new Uint8Array(arrayBuffer).set(imageBitmap);
  599. buffer.unmap();
  600. commandEncoder!.copyBufferToTexture({
  601. buffer: buffer,
  602. offset: 0,
  603. bytesPerRow
  604. }, textureCopyView, textureExtent);
  605. if (useOwnCommandEncoder) {
  606. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  607. commandEncoder = null as any;
  608. }
  609. this._bufferManager.releaseBuffer(buffer);
  610. } else {
  611. this._device.defaultQueue.writeTexture(textureCopyView, imageBitmap, {
  612. offset: 0,
  613. bytesPerRow
  614. }, textureExtent);
  615. }
  616. if (invertY || premultiplyAlpha) {
  617. this.invertYPreMultiplyAlpha(gpuTexture, width, height, format, invertY, premultiplyAlpha, faceIndex, commandEncoder);
  618. }
  619. } else {
  620. imageBitmap = imageBitmap as ImageBitmap;
  621. if (invertY || premultiplyAlpha) {
  622. createImageBitmap(imageBitmap, { imageOrientation: invertY ? "flipY" : "none", premultiplyAlpha: premultiplyAlpha ? "premultiply" : "none" }).then((imageBitmap) => {
  623. this._device.defaultQueue.copyImageBitmapToTexture({ imageBitmap }, textureCopyView, textureExtent);
  624. });
  625. } else {
  626. this._device.defaultQueue.copyImageBitmapToTexture({ imageBitmap }, textureCopyView, textureExtent);
  627. }
  628. }
  629. }
  630. 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> {
  631. const blockInformation = this._getBlockInformationFromFormat(format);
  632. const bytesPerRow = Math.ceil(width / blockInformation.width) * blockInformation.length;
  633. const bytesPerRowAligned = Math.ceil(bytesPerRow / 256) * 256;
  634. const size = bytesPerRowAligned * height;
  635. const gpuBuffer = this._bufferManager.createRawBuffer(size, GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST);
  636. const commandEncoder = this._device.createCommandEncoder({});
  637. commandEncoder.copyTextureToBuffer({
  638. texture,
  639. mipLevel,
  640. origin: {
  641. x,
  642. y,
  643. z: Math.max(faceIndex, 0)
  644. }
  645. }, {
  646. buffer: gpuBuffer,
  647. offset: 0,
  648. bytesPerRow: bytesPerRowAligned
  649. }, {
  650. width,
  651. height,
  652. depth: 1
  653. });
  654. this._device.defaultQueue.submit([commandEncoder!.finish()]);
  655. const type = this._getTextureTypeFromFormat(format);
  656. const floatFormat = type === Constants.TEXTURETYPE_FLOAT ? 2 : type === Constants.TEXTURETYPE_HALF_FLOAT ? 1 : 0;
  657. return this._bufferManager.readDataFromBuffer(gpuBuffer, size, width, height, bytesPerRow, bytesPerRowAligned, floatFormat, 0, buffer);
  658. }
  659. public releaseTexture(texture: InternalTexture): void {
  660. const hardwareTexture = texture._hardwareTexture;
  661. const irradianceTexture = texture._irradianceTexture;
  662. const depthStencilTexture = texture._depthStencilTexture;
  663. // 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
  664. this._deferredReleaseTextures.push([texture, hardwareTexture, irradianceTexture, depthStencilTexture]);
  665. }
  666. public destroyDeferredTextures(): void {
  667. for (let i = 0; i < this._deferredReleaseTextures.length; ++i) {
  668. const [texture, hardwareTexture, irradianceTexture, depthStencilTexture] = this._deferredReleaseTextures[i];
  669. if (hardwareTexture) {
  670. if (this._isHardwareTexture(hardwareTexture)) {
  671. hardwareTexture.release();
  672. } else {
  673. hardwareTexture.destroy();
  674. }
  675. }
  676. irradianceTexture?.dispose();
  677. depthStencilTexture?.dispose();
  678. // TODO WEBGPU remove debug code
  679. if (texture) {
  680. if ((texture as any)._swapped) {
  681. delete (texture as any)._swapped;
  682. } else {
  683. (texture as any)._released = true;
  684. }
  685. }
  686. }
  687. this._deferredReleaseTextures.length = 0;
  688. }
  689. public getCompareFunction(compareFunction: Nullable<number>): GPUCompareFunction {
  690. switch (compareFunction) {
  691. case Constants.ALWAYS:
  692. return WebGPUConstants.CompareFunction.Always;
  693. case Constants.EQUAL:
  694. return WebGPUConstants.CompareFunction.Equal;
  695. case Constants.GREATER:
  696. return WebGPUConstants.CompareFunction.Greater;
  697. case Constants.GEQUAL:
  698. return WebGPUConstants.CompareFunction.GreaterEqual;
  699. case Constants.LESS:
  700. return WebGPUConstants.CompareFunction.Less;
  701. case Constants.LEQUAL:
  702. return WebGPUConstants.CompareFunction.LessEqual;
  703. case Constants.NEVER:
  704. return WebGPUConstants.CompareFunction.Never;
  705. case Constants.NOTEQUAL:
  706. return WebGPUConstants.CompareFunction.NotEqual;
  707. default:
  708. return WebGPUConstants.CompareFunction.Less;
  709. }
  710. }
  711. private _getSamplerFilterDescriptor(internalTexture: InternalTexture): {
  712. magFilter: GPUFilterMode,
  713. minFilter: GPUFilterMode,
  714. mipmapFilter: GPUFilterMode,
  715. lodMinClamp?: number,
  716. lodMaxClamp?: number,
  717. } {
  718. let magFilter: GPUFilterMode, minFilter: GPUFilterMode, mipmapFilter: GPUFilterMode, lodMinClamp: number | undefined, lodMaxClamp: number | undefined;
  719. const useMipMaps = internalTexture.generateMipMaps;
  720. switch (internalTexture.samplingMode) {
  721. case Constants.TEXTURE_LINEAR_LINEAR_MIPNEAREST:
  722. magFilter = WebGPUConstants.FilterMode.Linear;
  723. minFilter = WebGPUConstants.FilterMode.Linear;
  724. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  725. if (!useMipMaps) {
  726. lodMinClamp = lodMaxClamp = 0;
  727. }
  728. break;
  729. case Constants.TEXTURE_LINEAR_LINEAR_MIPLINEAR:
  730. case Constants.TEXTURE_TRILINEAR_SAMPLINGMODE:
  731. magFilter = WebGPUConstants.FilterMode.Linear;
  732. minFilter = WebGPUConstants.FilterMode.Linear;
  733. if (!useMipMaps) {
  734. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  735. lodMinClamp = lodMaxClamp = 0;
  736. } else {
  737. mipmapFilter = WebGPUConstants.FilterMode.Linear;
  738. }
  739. break;
  740. case Constants.TEXTURE_NEAREST_NEAREST_MIPLINEAR:
  741. magFilter = WebGPUConstants.FilterMode.Nearest;
  742. minFilter = WebGPUConstants.FilterMode.Nearest;
  743. if (!useMipMaps) {
  744. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  745. lodMinClamp = lodMaxClamp = 0;
  746. } else {
  747. mipmapFilter = WebGPUConstants.FilterMode.Linear;
  748. }
  749. break;
  750. case Constants.TEXTURE_NEAREST_NEAREST_MIPNEAREST:
  751. magFilter = WebGPUConstants.FilterMode.Nearest;
  752. minFilter = WebGPUConstants.FilterMode.Nearest;
  753. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  754. if (!useMipMaps) {
  755. lodMinClamp = lodMaxClamp = 0;
  756. }
  757. break;
  758. case Constants.TEXTURE_NEAREST_LINEAR_MIPNEAREST:
  759. magFilter = WebGPUConstants.FilterMode.Nearest;
  760. minFilter = WebGPUConstants.FilterMode.Linear;
  761. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  762. if (!useMipMaps) {
  763. lodMinClamp = lodMaxClamp = 0;
  764. }
  765. break;
  766. case Constants.TEXTURE_NEAREST_LINEAR_MIPLINEAR:
  767. magFilter = WebGPUConstants.FilterMode.Nearest;
  768. minFilter = WebGPUConstants.FilterMode.Linear;
  769. if (!useMipMaps) {
  770. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  771. lodMinClamp = lodMaxClamp = 0;
  772. } else {
  773. mipmapFilter = WebGPUConstants.FilterMode.Linear;
  774. }
  775. break;
  776. case Constants.TEXTURE_NEAREST_LINEAR:
  777. magFilter = WebGPUConstants.FilterMode.Nearest;
  778. minFilter = WebGPUConstants.FilterMode.Linear;
  779. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  780. lodMinClamp = lodMaxClamp = 0;
  781. break;
  782. case Constants.TEXTURE_NEAREST_NEAREST:
  783. case Constants.TEXTURE_NEAREST_SAMPLINGMODE:
  784. magFilter = WebGPUConstants.FilterMode.Nearest;
  785. minFilter = WebGPUConstants.FilterMode.Nearest;
  786. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  787. lodMinClamp = lodMaxClamp = 0;
  788. break;
  789. case Constants.TEXTURE_LINEAR_NEAREST_MIPNEAREST:
  790. magFilter = WebGPUConstants.FilterMode.Linear;
  791. minFilter = WebGPUConstants.FilterMode.Nearest;
  792. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  793. if (!useMipMaps) {
  794. lodMinClamp = lodMaxClamp = 0;
  795. }
  796. break;
  797. case Constants.TEXTURE_LINEAR_NEAREST_MIPLINEAR:
  798. magFilter = WebGPUConstants.FilterMode.Linear;
  799. minFilter = WebGPUConstants.FilterMode.Nearest;
  800. if (!useMipMaps) {
  801. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  802. lodMinClamp = lodMaxClamp = 0;
  803. } else {
  804. mipmapFilter = WebGPUConstants.FilterMode.Linear;
  805. }
  806. break;
  807. case Constants.TEXTURE_LINEAR_LINEAR:
  808. case Constants.TEXTURE_BILINEAR_SAMPLINGMODE:
  809. magFilter = WebGPUConstants.FilterMode.Linear;
  810. minFilter = WebGPUConstants.FilterMode.Linear;
  811. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  812. lodMinClamp = lodMaxClamp = 0;
  813. break;
  814. case Constants.TEXTURE_LINEAR_NEAREST:
  815. magFilter = WebGPUConstants.FilterMode.Linear;
  816. minFilter = WebGPUConstants.FilterMode.Nearest;
  817. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  818. lodMinClamp = lodMaxClamp = 0;
  819. break;
  820. default:
  821. magFilter = WebGPUConstants.FilterMode.Nearest;
  822. minFilter = WebGPUConstants.FilterMode.Nearest;
  823. mipmapFilter = WebGPUConstants.FilterMode.Nearest;
  824. lodMinClamp = lodMaxClamp = 0;
  825. break;
  826. }
  827. return {
  828. magFilter,
  829. minFilter,
  830. mipmapFilter,
  831. lodMinClamp,
  832. lodMaxClamp,
  833. };
  834. }
  835. private _getWrappingMode(mode: number): GPUAddressMode {
  836. switch (mode) {
  837. case Engine.TEXTURE_WRAP_ADDRESSMODE:
  838. return WebGPUConstants.AddressMode.Repeat;
  839. case Engine.TEXTURE_CLAMP_ADDRESSMODE:
  840. return WebGPUConstants.AddressMode.ClampToEdge;
  841. case Engine.TEXTURE_MIRROR_ADDRESSMODE:
  842. return WebGPUConstants.AddressMode.MirrorRepeat;
  843. }
  844. return WebGPUConstants.AddressMode.Repeat;
  845. }
  846. private _getSamplerWrappingDescriptor(internalTexture: InternalTexture): {
  847. addressModeU: GPUAddressMode,
  848. addressModeV: GPUAddressMode,
  849. addressModeW: GPUAddressMode
  850. } {
  851. return {
  852. addressModeU: this._getWrappingMode(internalTexture._cachedWrapU!),
  853. addressModeV: this._getWrappingMode(internalTexture._cachedWrapV!),
  854. addressModeW: this._getWrappingMode(internalTexture._cachedWrapR!),
  855. };
  856. }
  857. private _getSamplerDescriptor(internalTexture: InternalTexture): GPUSamplerDescriptor {
  858. return {
  859. ...this._getSamplerFilterDescriptor(internalTexture),
  860. ...this._getSamplerWrappingDescriptor(internalTexture),
  861. compare: internalTexture._comparisonFunction ? this.getCompareFunction(internalTexture._comparisonFunction) : undefined,
  862. maxAnisotropy: internalTexture._cachedAnisotropicFilteringLevel ?? 1,
  863. };
  864. }
  865. public getSampler(internalTexture: InternalTexture): GPUSampler {
  866. const hash = WebGPUTextureHelper._GetSamplerHashCode(internalTexture);
  867. let sampler = this._samplers[hash];
  868. if (!sampler) {
  869. sampler = this._samplers[hash] = this._device.createSampler(this._getSamplerDescriptor(internalTexture));
  870. }
  871. return sampler;
  872. }
  873. }