-
-
Notifications
You must be signed in to change notification settings - Fork 36.3k
StorageTextureNode: Add TSL read/write support #32734
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -222,6 +222,7 @@ class StorageTextureNode extends TextureNode { | |
| const newNode = super.clone(); | ||
| newNode.storeNode = this.storeNode; | ||
| newNode.mipLevel = this.mipLevel; | ||
| newNode.access = this.access; | ||
| return newNode; | ||
|
|
||
| } | ||
|
|
@@ -255,7 +256,20 @@ export const storageTexture = /*@__PURE__*/ nodeProxy( StorageTextureNode ).setP | |
| */ | ||
| export const textureStore = ( value, uvNode, storeNode ) => { | ||
|
|
||
| const node = storageTexture( value, uvNode, storeNode ); | ||
| let node; | ||
|
|
||
| if ( value.isStorageTextureNode === true ) { | ||
|
|
||
| // Derive new storage texture node from existing one | ||
| node = value.clone(); | ||
| node.uvNode = uvNode; | ||
| node.storeNode = storeNode; | ||
|
Comment on lines
+261
to
+266
|
||
|
|
||
| } else { | ||
|
|
||
| node = storageTexture( value, uvNode, storeNode ); | ||
|
|
||
| } | ||
|
|
||
| if ( storeNode !== null ) node.toStack(); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| import { storageTexture } from '../../../../../src/nodes/accessors/StorageTextureNode.js'; | ||
| import { NodeAccess } from '../../../../../src/nodes/core/constants.js'; | ||
| import StorageTexture from '../../../../../src/renderers/common/StorageTexture.js'; | ||
|
|
||
| export default QUnit.module( 'Nodes', () => { | ||
|
|
||
| QUnit.module( 'Accessors', () => { | ||
|
|
||
| QUnit.module( 'StorageTextureNode', () => { | ||
|
|
||
| QUnit.test( 'clone preserves access property', ( assert ) => { | ||
|
|
||
| const texture = new StorageTexture( 512, 512 ); | ||
| const node = storageTexture( texture ).setAccess( NodeAccess.READ_ONLY ); | ||
|
|
||
| assert.strictEqual( node.access, NodeAccess.READ_ONLY, 'original has READ_ONLY access' ); | ||
|
|
||
| const cloned = node.clone(); | ||
|
|
||
| assert.strictEqual( cloned.access, NodeAccess.READ_ONLY, 'cloned node preserves READ_ONLY access' ); | ||
|
|
||
| } ); | ||
|
|
||
| QUnit.test( 'clone preserves READ_WRITE access', ( assert ) => { | ||
|
|
||
| const texture = new StorageTexture( 512, 512 ); | ||
| const node = storageTexture( texture ).setAccess( NodeAccess.READ_WRITE ); | ||
|
|
||
| const cloned = node.clone(); | ||
|
|
||
| assert.strictEqual( cloned.access, NodeAccess.READ_WRITE, 'cloned node preserves READ_WRITE access' ); | ||
|
|
||
| } ); | ||
|
|
||
| } ); | ||
|
|
||
| } ); | ||
|
|
||
| } ); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| import WGSLNodeBuilder from '../../../../../../src/renderers/webgpu/nodes/WGSLNodeBuilder.js'; | ||
|
|
||
| export default QUnit.module( 'Renderers', () => { | ||
|
|
||
| QUnit.module( 'WebGPU', () => { | ||
|
|
||
| QUnit.module( 'Nodes', () => { | ||
|
|
||
| QUnit.module( 'WGSLNodeBuilder', () => { | ||
|
|
||
| // generateTextureLoad is essentially a pure function (texture info -> WGSL string) | ||
| // The only 'this' access is renderer.backend.compatibilityMode for a depth texture edge case | ||
| // We test the real method with minimal context to verify WGSL output | ||
|
|
||
| QUnit.test( 'generateTextureLoad omits level for storage textures', ( assert ) => { | ||
|
|
||
| const context = { | ||
| renderer: { backend: { compatibilityMode: false } } | ||
| }; | ||
|
|
||
| const storageTexture = { isStorageTexture: true }; | ||
|
|
||
| const snippet = WGSLNodeBuilder.prototype.generateTextureLoad.call( | ||
| context, | ||
| storageTexture, | ||
| 'testTexture', | ||
| 'uvec2(0, 0)', | ||
| null, // levelSnippet | ||
| null, // depthSnippet | ||
| null // offsetSnippet | ||
| ); | ||
|
|
||
| // Storage textures should NOT have level parameter (WGSL spec) | ||
| assert.notOk( snippet.includes( 'u32(' ), 'storage texture load should not include level parameter' ); | ||
| assert.strictEqual( snippet, 'textureLoad( testTexture, uvec2(0, 0) )', 'correct WGSL for storage texture' ); | ||
|
|
||
| } ); | ||
|
|
||
| QUnit.test( 'generateTextureLoad includes level for regular textures', ( assert ) => { | ||
|
|
||
| const context = { | ||
| renderer: { backend: { compatibilityMode: false } } | ||
| }; | ||
|
|
||
| const regularTexture = { isStorageTexture: false }; | ||
|
|
||
| const snippet = WGSLNodeBuilder.prototype.generateTextureLoad.call( | ||
| context, | ||
| regularTexture, | ||
| 'testTexture', | ||
| 'uvec2(0, 0)', | ||
| null, // levelSnippet - should default to '0u' | ||
| null, | ||
| null | ||
| ); | ||
|
|
||
| // Regular textures SHOULD have level parameter | ||
| assert.ok( snippet.includes( 'u32( 0u )' ), 'regular texture load should include default level parameter' ); | ||
| assert.strictEqual( snippet, 'textureLoad( testTexture, uvec2(0, 0), u32( 0u ) )' ); | ||
|
|
||
| } ); | ||
|
Comment on lines
+15
to
+61
|
||
|
|
||
| } ); | ||
|
|
||
| } ); | ||
|
|
||
| } ); | ||
|
|
||
| } ); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function documentation lacks clarity about its dual behavior: it can accept either a StorageTexture or a StorageTextureNode as the first parameter. When a StorageTextureNode is passed, it clones the node and updates the uvNode and storeNode properties, preserving the access mode. This important behavior should be documented to help API users understand when to use this pattern versus
storageTexture().