From 3013686b2104e4b47ec913ce950ec40d401b4649 Mon Sep 17 00:00:00 2001 From: sunag Date: Wed, 20 Sep 2023 22:30:17 -0300 Subject: [PATCH 1/4] GPU FlipY --- .../webgpu/utils/WebGPUTextureMipmapUtils.js | 171 ++++++++++++++++-- .../webgpu/utils/WebGPUTextureUtils.js | 48 ++++- 2 files changed, 193 insertions(+), 26 deletions(-) diff --git a/examples/jsm/renderers/webgpu/utils/WebGPUTextureMipmapUtils.js b/examples/jsm/renderers/webgpu/utils/WebGPUTextureMipmapUtils.js index c638581db20d3f..baf396d70352de 100644 --- a/examples/jsm/renderers/webgpu/utils/WebGPUTextureMipmapUtils.js +++ b/examples/jsm/renderers/webgpu/utils/WebGPUTextureMipmapUtils.js @@ -54,10 +54,28 @@ fn main( @location( 0 ) vTex : vec2 ) -> @location( 0 ) vec4 { } `; - this.sampler = device.createSampler( { minFilter: GPUFilterMode.Linear } ); + const flipYFragmentSource = ` +@group( 0 ) @binding( 0 ) +var imgSampler : sampler; + +@group( 0 ) @binding( 1 ) +var img : texture_2d; + +@fragment +fn main( @location( 0 ) vTex : vec2 ) -> @location( 0 ) vec4 { + + return textureSample( img, imgSampler, vec2( vTex.x, 1.0 - vTex.y ) ); + +} +`; + this.mipmapSampler = device.createSampler( { minFilter: GPUFilterMode.Linear } ); + this.flipYSampler = device.createSampler( { minFilter: GPUFilterMode.Nearest } ); //@TODO?: Consider using textureLoad() // We'll need a new pipeline for every texture format used. - this.pipelines = {}; + this.transferPipelines = {}; + this.flipYPipelines = {}; + + this.textures = {}; this.mipmapVertexShaderModule = device.createShaderModule( { label: 'mipmapVertex', @@ -69,11 +87,16 @@ fn main( @location( 0 ) vTex : vec2 ) -> @location( 0 ) vec4 { code: mipmapFragmentSource } ); + this.flipYFragmentShaderModule = device.createShaderModule( { + label: 'flipYFragment', + code: flipYFragmentSource + } ); + } - getMipmapPipeline( format ) { + getTransferPipeline( format ) { - let pipeline = this.pipelines[ format ]; + let pipeline = this.transferPipelines[ format ]; if ( pipeline === undefined ) { @@ -94,7 +117,38 @@ fn main( @location( 0 ) vTex : vec2 ) -> @location( 0 ) vec4 { layout: 'auto' } ); - this.pipelines[ format ] = pipeline; + this.transferPipelines[ format ] = pipeline; + + } + + return pipeline; + + } + + getFlipYPipeline( format ) { + + let pipeline = this.flipYPipelines[ format ]; + + if ( pipeline === undefined ) { + + pipeline = this.device.createRenderPipeline( { + vertex: { + module: this.mipmapVertexShaderModule, + entryPoint: 'main' + }, + fragment: { + module: this.flipYFragmentShaderModule, + entryPoint: 'main', + targets: [ { format } ] + }, + primitive: { + topology: GPUPrimitiveTopology.TriangleStrip, + stripIndexFormat: GPUIndexFormat.Uint32 + }, + layout: 'auto' + } ); + + this.flipYPipelines[ format ] = pipeline; } @@ -102,9 +156,92 @@ fn main( @location( 0 ) vTex : vec2 ) -> @location( 0 ) vec4 { } + getTexture( format ) { + + let texture = this.textures[ format ]; + + if ( texture === undefined ) { + + const maxSize = this.device.limits.maxTextureDimension2D; + + texture = this.device.createTexture( { + size: { width: maxSize, height: maxSize, depthOrArrayLayers: 1 }, + format, + usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING + } ); + + this.textures[ format ] = texture; + + } + + return texture; + + } + + flipY( textureGPU, textureGPUDescriptor, baseArrayLayer = 0 ) { + + const transferPipeline = this.getTransferPipeline( textureGPUDescriptor.format ); + const flipYPipeline = this.getFlipYPipeline( textureGPUDescriptor.format ); + + const tempTexture = this.getTexture( textureGPUDescriptor.format ); + + const srcView = textureGPU.createView( { + baseMipLevel: 0, + mipLevelCount: 1, + dimension: GPUTextureViewDimension.TwoD, + baseArrayLayer + } ); + + const dstView = tempTexture.createView( { + baseMipLevel: 0, + mipLevelCount: 1, + dimension: GPUTextureViewDimension.TwoD, + baseArrayLayer: 0 + } ); + + const commandEncoder = this.device.createCommandEncoder( {} ); + + const pass = ( pipeline, sourceView, destinyView ) => { + + const bindGroupLayout = pipeline.getBindGroupLayout( 0 ); // @TODO: Consider making this static. + + const bindGroup = this.device.createBindGroup( { + layout: bindGroupLayout, + entries: [ { + binding: 0, + resource: this.flipYSampler + }, { + binding: 1, + resource: sourceView + } ] + } ); + + const passEncoder = commandEncoder.beginRenderPass( { + colorAttachments: [ { + view: destinyView, + loadOp: GPULoadOp.Clear, + storeOp: GPUStoreOp.Store, + clearValue: [ 0, 0, 0, 0 ] + } ] + } ); + + passEncoder.setPipeline( pipeline ); + passEncoder.setBindGroup( 0, bindGroup ); + passEncoder.draw( 4, 1, 0, 0 ); + passEncoder.end(); + + }; + + pass( transferPipeline, srcView, dstView ); + pass( flipYPipeline, dstView, srcView ); + + this.device.queue.submit( [ commandEncoder.finish() ] ); + + } + generateMipmaps( textureGPU, textureGPUDescriptor, baseArrayLayer = 0 ) { - const pipeline = this.getMipmapPipeline( textureGPUDescriptor.format ); + const pipeline = this.getTransferPipeline( textureGPUDescriptor.format ); const commandEncoder = this.device.createCommandEncoder( {} ); const bindGroupLayout = pipeline.getBindGroupLayout( 0 ); // @TODO: Consider making this static. @@ -118,6 +255,17 @@ fn main( @location( 0 ) vTex : vec2 ) -> @location( 0 ) vec4 { for ( let i = 1; i < textureGPUDescriptor.mipLevelCount; i ++ ) { + const bindGroup = this.device.createBindGroup( { + layout: bindGroupLayout, + entries: [ { + binding: 0, + resource: this.mipmapSampler + }, { + binding: 1, + resource: srcView + } ] + } ); + const dstView = textureGPU.createView( { baseMipLevel: i, mipLevelCount: 1, @@ -134,17 +282,6 @@ fn main( @location( 0 ) vTex : vec2 ) -> @location( 0 ) vec4 { } ] } ); - const bindGroup = this.device.createBindGroup( { - layout: bindGroupLayout, - entries: [ { - binding: 0, - resource: this.sampler - }, { - binding: 1, - resource: srcView - } ] - } ); - passEncoder.setPipeline( pipeline ); passEncoder.setBindGroup( 0, bindGroup ); passEncoder.draw( 4, 1, 0, 0 ); diff --git a/examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js b/examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js index 549021f9904027..514c19789dad8c 100644 --- a/examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js +++ b/examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js @@ -28,6 +28,8 @@ const _compareToWebGPU = { [ NotEqualCompare ]: 'not-equal' }; +const _flipMap = [ 0, 1, 3, 2, 4, 5 ]; + class WebGPUTextureUtils { constructor( backend ) { @@ -237,7 +239,7 @@ class WebGPUTextureUtils { if ( texture.isDataTexture || texture.isDataArrayTexture || texture.isData3DTexture ) { - this._copyBufferToTexture( options.image, textureData.texture, textureDescriptorGPU ); + this._copyBufferToTexture( options.image, textureData.texture, textureDescriptorGPU, 0, texture.flipY ); } else if ( texture.isCompressedTexture ) { @@ -245,7 +247,7 @@ class WebGPUTextureUtils { } else if ( texture.isCubeTexture ) { - this._copyCubeMapToTexture( options.images, texture, textureData.texture, textureDescriptorGPU ); + this._copyCubeMapToTexture( options.images, textureData.texture, textureDescriptorGPU, texture.flipY ); } else if ( texture.isVideoTexture ) { @@ -255,7 +257,7 @@ class WebGPUTextureUtils { } else { - this._copyImageToTexture( options.image, textureData.texture ); + this._copyImageToTexture( options.image, textureData.texture, textureDescriptorGPU, 0, texture.flipY ); } @@ -361,19 +363,21 @@ class WebGPUTextureUtils { } - _copyCubeMapToTexture( images, texture, textureGPU, textureDescriptorGPU ) { + _copyCubeMapToTexture( images, textureGPU, textureDescriptorGPU, flipY ) { for ( let i = 0; i < 6; i ++ ) { const image = images[ i ]; + const flipIndex = flipY === true ? _flipMap[ i ] : i; + if ( image.isDataTexture ) { - this._copyBufferToTexture( image.image, textureGPU, textureDescriptorGPU, i ); + this._copyBufferToTexture( image.image, textureGPU, textureDescriptorGPU, flipIndex, flipY ); } else { - this._copyImageToTexture( image, textureGPU, i ); + this._copyImageToTexture( image, textureGPU, textureDescriptorGPU, flipIndex, flipY ); } @@ -381,7 +385,7 @@ class WebGPUTextureUtils { } - _copyImageToTexture( image, textureGPU, originDepth = 0 ) { + _copyImageToTexture( image, textureGPU, textureDescriptorGPU, originDepth, flipY ) { const device = this.backend.device; @@ -399,9 +403,15 @@ class WebGPUTextureUtils { } ); + if ( flipY === true ) { + + this._flipY( textureGPU, textureDescriptorGPU, originDepth ); + + } + } - _generateMipmaps( textureGPU, textureDescriptorGPU, baseArrayLayer = 0 ) { + _initMipmapUtils() { if ( this.mipmapUtils === null ) { @@ -409,11 +419,25 @@ class WebGPUTextureUtils { } + } + + _generateMipmaps( textureGPU, textureDescriptorGPU, baseArrayLayer = 0 ) { + + this._initMipmapUtils(); + this.mipmapUtils.generateMipmaps( textureGPU, textureDescriptorGPU, baseArrayLayer ); } - _copyBufferToTexture( image, textureGPU, textureDescriptorGPU, originDepth = 0 ) { + _flipY( textureGPU, textureDescriptorGPU, originDepth = 0 ) { + + this._initMipmapUtils(); + + this.mipmapUtils.flipY( textureGPU, textureDescriptorGPU, originDepth ); + + } + + _copyBufferToTexture( image, textureGPU, textureDescriptorGPU, originDepth, flipY ) { // @TODO: Consider to use GPUCommandEncoder.copyBufferToTexture() // @TODO: Consider to support valid buffer layouts with other formats like RGB @@ -442,6 +466,12 @@ class WebGPUTextureUtils { depthOrArrayLayers: ( image.depth !== undefined ) ? image.depth : 1 } ); + if ( flipY === true ) { + + this._flipY( textureGPU, textureDescriptorGPU, originDepth ); + + } + } _copyCompressedBufferToTexture( mipmaps, textureGPU, textureDescriptorGPU ) { From 35bb2dd200ab468185d7722f80bf5ee3bff150e9 Mon Sep 17 00:00:00 2001 From: sunag Date: Wed, 20 Sep 2023 22:42:36 -0300 Subject: [PATCH 2/4] use tempTexture.destroy() --- .../webgpu/utils/WebGPUTextureMipmapUtils.js | 39 ++++++------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/examples/jsm/renderers/webgpu/utils/WebGPUTextureMipmapUtils.js b/examples/jsm/renderers/webgpu/utils/WebGPUTextureMipmapUtils.js index baf396d70352de..742b7b159b5b80 100644 --- a/examples/jsm/renderers/webgpu/utils/WebGPUTextureMipmapUtils.js +++ b/examples/jsm/renderers/webgpu/utils/WebGPUTextureMipmapUtils.js @@ -75,8 +75,6 @@ fn main( @location( 0 ) vTex : vec2 ) -> @location( 0 ) vec4 { this.transferPipelines = {}; this.flipYPipelines = {}; - this.textures = {}; - this.mipmapVertexShaderModule = device.createShaderModule( { label: 'mipmapVertex', code: mipmapVertexSource @@ -156,34 +154,19 @@ fn main( @location( 0 ) vTex : vec2 ) -> @location( 0 ) vec4 { } - getTexture( format ) { - - let texture = this.textures[ format ]; - - if ( texture === undefined ) { - - const maxSize = this.device.limits.maxTextureDimension2D; - - texture = this.device.createTexture( { - size: { width: maxSize, height: maxSize, depthOrArrayLayers: 1 }, - format, - usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING - } ); - - this.textures[ format ] = texture; - - } - - return texture; - - } - flipY( textureGPU, textureGPUDescriptor, baseArrayLayer = 0 ) { - const transferPipeline = this.getTransferPipeline( textureGPUDescriptor.format ); - const flipYPipeline = this.getFlipYPipeline( textureGPUDescriptor.format ); + const format = textureGPUDescriptor.format; + const { width, height } = textureGPUDescriptor.size; + + const transferPipeline = this.getTransferPipeline( format ); + const flipYPipeline = this.getFlipYPipeline( format ); - const tempTexture = this.getTexture( textureGPUDescriptor.format ); + const tempTexture = this.device.createTexture( { + size: { width, height, depthOrArrayLayers: 1 }, + format, + usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING + } ); const srcView = textureGPU.createView( { baseMipLevel: 0, @@ -237,6 +220,8 @@ fn main( @location( 0 ) vTex : vec2 ) -> @location( 0 ) vec4 { this.device.queue.submit( [ commandEncoder.finish() ] ); + tempTexture.destroy(); + } generateMipmaps( textureGPU, textureGPUDescriptor, baseArrayLayer = 0 ) { From 4f392dc5453ca0c34f6199995b76ad41ba8bb8cc Mon Sep 17 00:00:00 2001 From: sunag Date: Thu, 21 Sep 2023 02:07:52 -0300 Subject: [PATCH 3/4] fix name --- .../jsm/renderers/webgpu/utils/WebGPUTextureMipmapUtils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/jsm/renderers/webgpu/utils/WebGPUTextureMipmapUtils.js b/examples/jsm/renderers/webgpu/utils/WebGPUTextureMipmapUtils.js index 742b7b159b5b80..35fd264c849f85 100644 --- a/examples/jsm/renderers/webgpu/utils/WebGPUTextureMipmapUtils.js +++ b/examples/jsm/renderers/webgpu/utils/WebGPUTextureMipmapUtils.js @@ -184,7 +184,7 @@ fn main( @location( 0 ) vTex : vec2 ) -> @location( 0 ) vec4 { const commandEncoder = this.device.createCommandEncoder( {} ); - const pass = ( pipeline, sourceView, destinyView ) => { + const pass = ( pipeline, sourceView, destinationView ) => { const bindGroupLayout = pipeline.getBindGroupLayout( 0 ); // @TODO: Consider making this static. @@ -201,7 +201,7 @@ fn main( @location( 0 ) vTex : vec2 ) -> @location( 0 ) vec4 { const passEncoder = commandEncoder.beginRenderPass( { colorAttachments: [ { - view: destinyView, + view: destinationView, loadOp: GPULoadOp.Clear, storeOp: GPUStoreOp.Store, clearValue: [ 0, 0, 0, 0 ] From f5e744e63e13b963c2739d31998784f492513aa4 Mon Sep 17 00:00:00 2001 From: sunag Date: Mon, 25 Sep 2023 23:36:42 -0300 Subject: [PATCH 4/4] Rename to `WebGPUTexturePassUtils` and `_getPassUtils()` --- ...pmapUtils.js => WebGPUTexturePassUtils.js} | 4 ++-- .../webgpu/utils/WebGPUTextureUtils.js | 22 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) rename examples/jsm/renderers/webgpu/utils/{WebGPUTextureMipmapUtils.js => WebGPUTexturePassUtils.js} (98%) diff --git a/examples/jsm/renderers/webgpu/utils/WebGPUTextureMipmapUtils.js b/examples/jsm/renderers/webgpu/utils/WebGPUTexturePassUtils.js similarity index 98% rename from examples/jsm/renderers/webgpu/utils/WebGPUTextureMipmapUtils.js rename to examples/jsm/renderers/webgpu/utils/WebGPUTexturePassUtils.js index 35fd264c849f85..d46e43715ea722 100644 --- a/examples/jsm/renderers/webgpu/utils/WebGPUTextureMipmapUtils.js +++ b/examples/jsm/renderers/webgpu/utils/WebGPUTexturePassUtils.js @@ -1,6 +1,6 @@ import { GPUTextureViewDimension, GPUIndexFormat, GPUFilterMode, GPUPrimitiveTopology, GPULoadOp, GPUStoreOp } from './WebGPUConstants.js'; -class WebGPUTextureMipmapUtils { +class WebGPUTexturePassUtils { constructor( device ) { @@ -282,4 +282,4 @@ fn main( @location( 0 ) vTex : vec2 ) -> @location( 0 ) vec4 { } -export default WebGPUTextureMipmapUtils; +export default WebGPUTexturePassUtils; diff --git a/examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js b/examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js index 514c19789dad8c..d81dd92e092dee 100644 --- a/examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js +++ b/examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js @@ -15,7 +15,7 @@ import { import { CubeReflectionMapping, CubeRefractionMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping } from 'three'; -import WebGPUTextureMipmapUtils from './WebGPUTextureMipmapUtils.js'; +import WebGPUTexturePassUtils from './WebGPUTexturePassUtils.js'; const _compareToWebGPU = { [ NeverCompare ]: 'never', @@ -36,7 +36,7 @@ class WebGPUTextureUtils { this.backend = backend; - this.mipmapUtils = null; + this._passUtils = null; this.defaultTexture = null; this.defaultCubeTexture = null; @@ -411,29 +411,29 @@ class WebGPUTextureUtils { } - _initMipmapUtils() { + _getPassUtils() { - if ( this.mipmapUtils === null ) { + let passUtils = this._passUtils; - this.mipmapUtils = new WebGPUTextureMipmapUtils( this.backend.device ); + if ( passUtils === null ) { + + this._passUtils = passUtils = new WebGPUTexturePassUtils( this.backend.device ); } + return passUtils; + } _generateMipmaps( textureGPU, textureDescriptorGPU, baseArrayLayer = 0 ) { - this._initMipmapUtils(); - - this.mipmapUtils.generateMipmaps( textureGPU, textureDescriptorGPU, baseArrayLayer ); + this._getPassUtils().generateMipmaps( textureGPU, textureDescriptorGPU, baseArrayLayer ); } _flipY( textureGPU, textureDescriptorGPU, originDepth = 0 ) { - this._initMipmapUtils(); - - this.mipmapUtils.flipY( textureGPU, textureDescriptorGPU, originDepth ); + this._getPassUtils().flipY( textureGPU, textureDescriptorGPU, originDepth ); }