WebGPUTextureUtils.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  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 { GPUTextureViewDimension, GPUIndexFormat, GPUFilterMode, GPUPrimitiveTopology, GPULoadOp, GPUStoreOp } 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 ) {
  22. this.device = device;
  23. const mipmapVertexSource = `
  24. struct VarysStruct {
  25. @builtin( position ) Position: vec4<f32>,
  26. @location( 0 ) vTex : vec2<f32>
  27. };
  28. @vertex
  29. fn main( @builtin( vertex_index ) vertexIndex : u32 ) -> VarysStruct {
  30. var Varys : VarysStruct;
  31. var pos = array< vec2<f32>, 4 >(
  32. vec2<f32>( -1.0, 1.0 ),
  33. vec2<f32>( 1.0, 1.0 ),
  34. vec2<f32>( -1.0, -1.0 ),
  35. vec2<f32>( 1.0, -1.0 )
  36. );
  37. var tex = array< vec2<f32>, 4 >(
  38. vec2<f32>( 0.0, 0.0 ),
  39. vec2<f32>( 1.0, 0.0 ),
  40. vec2<f32>( 0.0, 1.0 ),
  41. vec2<f32>( 1.0, 1.0 )
  42. );
  43. Varys.vTex = tex[ vertexIndex ];
  44. Varys.Position = vec4<f32>( pos[ vertexIndex ], 0.0, 1.0 );
  45. return Varys;
  46. }
  47. `;
  48. const mipmapFragmentSource = `
  49. @group( 0 ) @binding( 0 )
  50. var imgSampler : sampler;
  51. @group( 0 ) @binding( 1 )
  52. var img : texture_2d<f32>;
  53. @fragment
  54. fn main( @location( 0 ) vTex : vec2<f32> ) -> @location( 0 ) vec4<f32> {
  55. return textureSample( img, imgSampler, vTex );
  56. }
  57. `;
  58. this.sampler = device.createSampler( { minFilter: GPUFilterMode.Linear } );
  59. // We'll need a new pipeline for every texture format used.
  60. this.pipelines = {};
  61. this.mipmapVertexShaderModule = device.createShaderModule( {
  62. code: mipmapVertexSource
  63. } );
  64. this.mipmapFragmentShaderModule = device.createShaderModule( {
  65. code: mipmapFragmentSource
  66. } );
  67. }
  68. getMipmapPipeline( format ) {
  69. let pipeline = this.pipelines[ format ];
  70. if ( pipeline === undefined ) {
  71. pipeline = this.device.createRenderPipeline( {
  72. vertex: {
  73. module: this.mipmapVertexShaderModule,
  74. entryPoint: 'main'
  75. },
  76. fragment: {
  77. module: this.mipmapFragmentShaderModule,
  78. entryPoint: 'main',
  79. targets: [ { format } ]
  80. },
  81. primitive: {
  82. topology: GPUPrimitiveTopology.TriangleStrip,
  83. stripIndexFormat: GPUIndexFormat.Uint32
  84. },
  85. layout: 'auto'
  86. } );
  87. this.pipelines[ format ] = pipeline;
  88. }
  89. return pipeline;
  90. }
  91. generateMipmaps( textureGPU, textureGPUDescriptor, baseArrayLayer = 0 ) {
  92. const pipeline = this.getMipmapPipeline( textureGPUDescriptor.format );
  93. const commandEncoder = this.device.createCommandEncoder( {} );
  94. const bindGroupLayout = pipeline.getBindGroupLayout( 0 ); // @TODO: Consider making this static.
  95. let srcView = textureGPU.createView( {
  96. baseMipLevel: 0,
  97. mipLevelCount: 1,
  98. dimension: GPUTextureViewDimension.TwoD,
  99. baseArrayLayer
  100. } );
  101. for ( let i = 1; i < textureGPUDescriptor.mipLevelCount; i ++ ) {
  102. const dstView = textureGPU.createView( {
  103. baseMipLevel: i,
  104. mipLevelCount: 1,
  105. dimension: GPUTextureViewDimension.TwoD,
  106. baseArrayLayer
  107. } );
  108. const passEncoder = commandEncoder.beginRenderPass( {
  109. colorAttachments: [ {
  110. view: dstView,
  111. loadOp: GPULoadOp.Clear,
  112. storeOp: GPUStoreOp.Store,
  113. clearValue: [ 0, 0, 0, 0 ]
  114. } ]
  115. } );
  116. const bindGroup = this.device.createBindGroup( {
  117. layout: bindGroupLayout,
  118. entries: [ {
  119. binding: 0,
  120. resource: this.sampler
  121. }, {
  122. binding: 1,
  123. resource: srcView
  124. } ]
  125. } );
  126. passEncoder.setPipeline( pipeline );
  127. passEncoder.setBindGroup( 0, bindGroup );
  128. passEncoder.draw( 4, 1, 0, 0 );
  129. passEncoder.end();
  130. srcView = dstView;
  131. }
  132. this.device.queue.submit( [ commandEncoder.finish() ] );
  133. }
  134. }
  135. export default WebGPUTextureUtils;