Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Three.TSL.js
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,7 @@ export const textureBicubicLevel = TSL.textureBicubicLevel;
export const textureCubeUV = TSL.textureCubeUV;
export const textureLoad = TSL.textureLoad;
export const textureSize = TSL.textureSize;
export const textureLevel = TSL.textureLevel;
export const textureStore = TSL.textureStore;
export const thickness = TSL.thickness;
export const time = TSL.time;
Expand Down
22 changes: 22 additions & 0 deletions src/nodes/accessors/StorageTextureNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ class StorageTextureNode extends TextureNode {
*/
this.storeNode = storeNode;

/**
* The mip level to write to for storage textures.
*
* @type {number}
* @default 0
*/
this.mipLevel = 0;

/**
* This flag can be used for type testing.
*
Expand Down Expand Up @@ -115,6 +123,19 @@ class StorageTextureNode extends TextureNode {

}

/**
* Sets the mip level to write to.
*
* @param {number} level - The mip level.
* @return {StorageTextureNode} A reference to this node.
*/
setMipLevel( level ) {

this.mipLevel = level;
return this;

}

/**
* Generates the code snippet of the storage node. If no `storeNode`
* is defined, the texture node is generated as normal texture.
Expand Down Expand Up @@ -200,6 +221,7 @@ class StorageTextureNode extends TextureNode {

const newNode = super.clone();
newNode.storeNode = this.storeNode;
newNode.mipLevel = this.mipLevel;
return newNode;

}
Expand Down
2 changes: 1 addition & 1 deletion src/nodes/accessors/TextureNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -904,7 +904,7 @@ export const uniformTexture = ( value = EmptyTexture ) => texture( value );
*/
export const textureLoad = ( ...params ) => texture( ...params ).setSampler( false );

//export const textureLevel = ( value, uv, level ) => texture( value, uv ).level( level );
export const textureLevel = ( value, uv, level ) => texture( value, uv ).level( level );

/**
* Converts a texture or texture node to a sampler.
Expand Down
2 changes: 1 addition & 1 deletion src/renderers/common/Bindings.js
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ class Bindings extends DataMap {

}

if ( texture.isStorageTexture === true ) {
if ( texture.isStorageTexture === true && texture.mipmapsAutoUpdate === true ) {

const textureData = this.get( texture );

Expand Down
8 changes: 8 additions & 0 deletions src/renderers/common/SampledTexture.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ class SampledTexture extends Sampler {
*/
this.store = false;

/**
* The mip level to bind for storage textures.
*
* @type {number}
* @default 0
*/
this.mipLevel = 0;

/**
* This flag can be used for type testing.
*
Expand Down
10 changes: 9 additions & 1 deletion src/renderers/common/StorageTexture.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,16 @@ class StorageTexture extends Texture {
*/
this.isStorageTexture = true;

}
/**
* When `true`, mipmaps will be auto-generated after compute writes.
* When `false`, mipmaps must be written manually via compute shaders.
*
* @type {boolean}
* @default true
*/
this.mipmapsAutoUpdate = true;

}
/**
* Sets the size of the storage texture.
*
Expand Down
8 changes: 7 additions & 1 deletion src/renderers/common/Textures.js
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,13 @@ class Textures extends DataMap {

if ( texture.source.dataReady === true ) backend.updateTexture( texture, options );

if ( options.needsMipmaps && texture.mipmaps.length === 0 ) backend.generateMipmaps( texture );
const skipAutoGeneration = texture.isStorageTexture === true && texture.mipmapsAutoUpdate === false;

if ( options.needsMipmaps && texture.mipmaps.length === 0 && ! skipAutoGeneration ) {

backend.generateMipmaps( texture );

}

if ( texture.onUpdate ) texture.onUpdate( texture );

Expand Down
4 changes: 3 additions & 1 deletion src/renderers/webgpu/nodes/WGSLNodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -951,7 +951,9 @@ class WGSLNodeBuilder extends NodeBuilder {

}

texture.store = node.isStorageTextureNode === true;
// Only mark as storage binding when actually writing (storeNode is set)
texture.store = node.isStorageTextureNode === true && node.storeNode !== null;
texture.mipLevel = texture.store ? node.mipLevel : 0;
texture.setVisibility( gpuShaderStageLib[ shaderStage ] );

if ( this.isUnfilterable( node.value ) === false && texture.store === false ) {
Expand Down
5 changes: 3 additions & 2 deletions src/renderers/webgpu/utils/WebGPUBindingUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ class WebGPUBindingUtils {
} else {

const mipLevelCount = binding.store ? 1 : textureData.texture.mipLevelCount;
const baseMipLevel = binding.store ? binding.mipLevel : 0;
let propertyName = `view-${ textureData.texture.width }-${ textureData.texture.height }`;

if ( textureData.texture.depthOrArrayLayers > 1 ) {
Expand All @@ -423,7 +424,7 @@ class WebGPUBindingUtils {

}

propertyName += `-${ mipLevelCount }`;
propertyName += `-${ mipLevelCount }-${ baseMipLevel }`;

resourceGPU = textureData[ propertyName ];

Expand Down Expand Up @@ -451,7 +452,7 @@ class WebGPUBindingUtils {

}

resourceGPU = textureData[ propertyName ] = textureData.texture.createView( { aspect: aspectGPU, dimension: dimensionViewGPU, mipLevelCount } );
resourceGPU = textureData[ propertyName ] = textureData.texture.createView( { aspect: aspectGPU, dimension: dimensionViewGPU, mipLevelCount, baseMipLevel } );

}

Expand Down