WebGPUTextureUtils.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  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 { GPUIndexFormat, GPUFilterMode, GPUPrimitiveTopology } from './constants.js';
  19. // ported from https://github.com/toji/web-texture-tool/blob/master/src/webgpu-mipmap-generator.js
  20. class WebGPUTextureUtils {
  21. constructor( device, glslang ) {
  22. this.device = device;
  23. const mipmapVertexSource = `#version 450
  24. 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));
  25. 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));
  26. layout(location = 0) out vec2 vTex;
  27. void main() {
  28. vTex = tex[gl_VertexIndex];
  29. gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0);
  30. }
  31. `;
  32. const mipmapFragmentSource = `#version 450
  33. layout(set = 0, binding = 0) uniform sampler imgSampler;
  34. layout(set = 0, binding = 1) uniform texture2D img;
  35. layout(location = 0) in vec2 vTex;
  36. layout(location = 0) out vec4 outColor;
  37. void main() {
  38. outColor = texture(sampler2D(img, imgSampler), vTex);
  39. }`;
  40. this.sampler = device.createSampler( { minFilter: GPUFilterMode.Linear } );
  41. // We'll need a new pipeline for every texture format used.
  42. this.pipelines = {};
  43. this.mipmapVertexShaderModule = device.createShaderModule( {
  44. code: glslang.compileGLSL( mipmapVertexSource, 'vertex' ),
  45. } );
  46. this.mipmapFragmentShaderModule = device.createShaderModule( {
  47. code: glslang.compileGLSL( mipmapFragmentSource, 'fragment' ),
  48. } );
  49. }
  50. getMipmapPipeline( format ) {
  51. let pipeline = this.pipelines[ format ];
  52. if ( pipeline === undefined ) {
  53. pipeline = this.device.createRenderPipeline( {
  54. vertex: {
  55. module: this.mipmapVertexShaderModule,
  56. entryPoint: 'main',
  57. },
  58. fragment: {
  59. module: this.mipmapFragmentShaderModule,
  60. entryPoint: 'main',
  61. targets: [ { format } ],
  62. },
  63. primitive: {
  64. topology: GPUPrimitiveTopology.TriangleStrip,
  65. stripIndexFormat: GPUIndexFormat.Uint32
  66. }
  67. } );
  68. this.pipelines[ format ] = pipeline;
  69. }
  70. return pipeline;
  71. }
  72. generateMipmaps( textureGPU, textureGPUDescriptor ) {
  73. const pipeline = this.getMipmapPipeline( textureGPUDescriptor.format );
  74. const commandEncoder = this.device.createCommandEncoder( {} );
  75. const bindGroupLayout = pipeline.getBindGroupLayout( 0 ); // @TODO: Consider making this static.
  76. let srcView = textureGPU.createView( {
  77. baseMipLevel: 0,
  78. mipLevelCount: 1,
  79. } );
  80. for ( let i = 1; i < textureGPUDescriptor.mipLevelCount; i ++ ) {
  81. const dstView = textureGPU.createView( {
  82. baseMipLevel: i,
  83. mipLevelCount: 1,
  84. } );
  85. const passEncoder = commandEncoder.beginRenderPass( {
  86. colorAttachments: [ {
  87. view: dstView,
  88. loadValue: [ 0, 0, 0, 0 ],
  89. } ],
  90. } );
  91. const bindGroup = this.device.createBindGroup( {
  92. layout: bindGroupLayout,
  93. entries: [ {
  94. binding: 0,
  95. resource: this.sampler,
  96. }, {
  97. binding: 1,
  98. resource: srcView,
  99. } ],
  100. } );
  101. passEncoder.setPipeline( pipeline );
  102. passEncoder.setBindGroup( 0, bindGroup );
  103. passEncoder.draw( 4, 1, 0, 0 );
  104. passEncoder.endPass();
  105. srcView = dstView;
  106. }
  107. this.device.queue.submit( [ commandEncoder.finish() ] );
  108. }
  109. }
  110. export default WebGPUTextureUtils;