Skip to content

Commit b38ac17

Browse files
authored
WebGPURenderer: Persistent video texture approach (#31416)
1 parent f2e0aed commit b38ac17

File tree

5 files changed

+46
-123
lines changed

5 files changed

+46
-123
lines changed

src/renderers/common/SampledTexture.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,6 @@ class SampledTexture extends Binding {
7878
*/
7979
needsBindingsUpdate( generation ) {
8080

81-
const { texture } = this;
82-
8381
if ( generation !== this.generation ) {
8482

8583
this.generation = generation;
@@ -88,7 +86,7 @@ class SampledTexture extends Binding {
8886

8987
}
9088

91-
return texture.isVideoTexture;
89+
return false;
9290

9391
}
9492

src/renderers/common/Textures.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -366,9 +366,19 @@ class Textures extends DataMap {
366366

367367
if ( image.image !== undefined ) image = image.image;
368368

369-
target.width = image.width || 1;
370-
target.height = image.height || 1;
371-
target.depth = texture.isCubeTexture ? 6 : ( image.depth || 1 );
369+
if ( image instanceof HTMLVideoElement ) {
370+
371+
target.width = image.videoWidth || 1;
372+
target.height = image.videoHeight || 1;
373+
target.depth = 1;
374+
375+
} else {
376+
377+
target.width = image.width || 1;
378+
target.height = image.height || 1;
379+
target.depth = texture.isCubeTexture ? 6 : ( image.depth || 1 );
380+
381+
}
372382

373383
} else {
374384

src/renderers/webgpu/nodes/WGSLNodeBuilder.js

Lines changed: 21 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { NodeAccess } from '../../../nodes/core/constants.js';
1616
import VarNode from '../../../nodes/core/VarNode.js';
1717
import ExpressionNode from '../../../nodes/code/ExpressionNode.js';
1818

19-
import { NoColorSpace, FloatType, RepeatWrapping, ClampToEdgeWrapping, MirroredRepeatWrapping, NearestFilter } from '../../../constants.js';
19+
import { NoColorSpace, FloatType, RepeatWrapping, ClampToEdgeWrapping, MirroredRepeatWrapping, NearestFilter, SRGBColorSpace } from '../../../constants.js';
2020

2121
// GPUShaderStage is not defined in browsers not supporting WebGPU
2222
const GPUShaderStage = ( typeof self !== 'undefined' ) ? self.GPUShaderStage : { VERTEX: 1, FRAGMENT: 2, COMPUTE: 4 };
@@ -219,7 +219,14 @@ class WGSLNodeBuilder extends NodeBuilder {
219219
*/
220220
needsToWorkingColorSpace( texture ) {
221221

222-
return texture.isVideoTexture === true && texture.colorSpace !== NoColorSpace;
222+
if ( texture.isVideoTexture && texture.colorSpace === SRGBColorSpace ) {
223+
224+
// Video textures are always in sRGB color space, so no conversion is needed
225+
return false;
226+
227+
}
228+
229+
return texture.colorSpace !== NoColorSpace;
223230

224231
}
225232

@@ -250,30 +257,7 @@ class WGSLNodeBuilder extends NodeBuilder {
250257

251258
} else {
252259

253-
return this._generateTextureSampleLevel( texture, textureProperty, uvSnippet, '0', depthSnippet );
254-
255-
}
256-
257-
}
258-
259-
/**
260-
* Generates the WGSL snippet when sampling video textures.
261-
*
262-
* @private
263-
* @param {string} textureProperty - The name of the video texture uniform in the shader.
264-
* @param {string} uvSnippet - A WGSL snippet that represents texture coordinates used for sampling.
265-
* @param {string} [shaderStage=this.shaderStage] - The shader stage this code snippet is generated for.
266-
* @return {string} The WGSL snippet.
267-
*/
268-
_generateVideoSample( textureProperty, uvSnippet, shaderStage = this.shaderStage ) {
269-
270-
if ( shaderStage === 'fragment' ) {
271-
272-
return `textureSampleBaseClampToEdge( ${ textureProperty }, ${ textureProperty }_sampler, vec2<f32>( ${ uvSnippet }.x, 1.0 - ${ uvSnippet }.y ) )`;
273-
274-
} else {
275-
276-
console.error( `WebGPURenderer: THREE.VideoTexture does not support ${ shaderStage } shader.` );
260+
return this.generateTextureSampleLevel( texture, textureProperty, uvSnippet, '0', depthSnippet );
277261

278262
}
279263

@@ -290,7 +274,7 @@ class WGSLNodeBuilder extends NodeBuilder {
290274
* @param {string} depthSnippet - A WGSL snippet that represents 0-based texture array index to sample.
291275
* @return {string} The WGSL snippet.
292276
*/
293-
_generateTextureSampleLevel( texture, textureProperty, uvSnippet, levelSnippet, depthSnippet ) {
277+
generateTextureSampleLevel( texture, textureProperty, uvSnippet, levelSnippet, depthSnippet ) {
294278

295279
if ( this.isUnfilterable( texture ) === false ) {
296280

@@ -434,7 +418,7 @@ class WGSLNodeBuilder extends NodeBuilder {
434418
}
435419

436420
// Build parameters string based on texture type and multisampling
437-
if ( isMultisampled || texture.isVideoTexture || texture.isStorageTexture ) {
421+
if ( isMultisampled || texture.isStorageTexture ) {
438422

439423
textureDimensionsParams = textureProperty;
440424

@@ -531,11 +515,7 @@ class WGSLNodeBuilder extends NodeBuilder {
531515

532516
let snippet;
533517

534-
if ( texture.isVideoTexture === true ) {
535-
536-
snippet = `textureLoad( ${ textureProperty }, ${ uvIndexSnippet } )`;
537-
538-
} else if ( depthSnippet ) {
518+
if ( depthSnippet ) {
539519

540520
snippet = `textureLoad( ${ textureProperty }, ${ uvIndexSnippet }, ${ depthSnippet }, u32( ${ levelSnippet } ) )`;
541521

@@ -624,11 +604,7 @@ class WGSLNodeBuilder extends NodeBuilder {
624604

625605
let snippet = null;
626606

627-
if ( texture.isVideoTexture === true ) {
628-
629-
snippet = this._generateVideoSample( textureProperty, uvSnippet, shaderStage );
630-
631-
} else if ( this.isUnfilterable( texture ) ) {
607+
if ( this.isUnfilterable( texture ) ) {
632608

633609
snippet = this.generateTextureLod( texture, textureProperty, uvSnippet, depthSnippet, '0', shaderStage );
634610

@@ -711,22 +687,22 @@ class WGSLNodeBuilder extends NodeBuilder {
711687
* @param {string} [shaderStage=this.shaderStage] - The shader stage this code snippet is generated for.
712688
* @return {string} The WGSL snippet.
713689
*/
714-
generateTextureLevel( texture, textureProperty, uvSnippet, levelSnippet, depthSnippet, shaderStage = this.shaderStage ) {
690+
generateTextureLevel( texture, textureProperty, uvSnippet, levelSnippet, depthSnippet ) {
715691

716-
let snippet = null;
692+
if ( this.isUnfilterable( texture ) === false ) {
717693

718-
if ( texture.isVideoTexture === true ) {
694+
return `textureSampleLevel( ${ textureProperty }, ${ textureProperty }_sampler, ${ uvSnippet }, ${ levelSnippet } )`;
719695

720-
snippet = this._generateVideoSample( textureProperty, uvSnippet, shaderStage );
696+
} else if ( this.isFilteredTexture( texture ) ) {
697+
698+
return this.generateFilteredTexture( texture, textureProperty, uvSnippet, levelSnippet );
721699

722700
} else {
723701

724-
snippet = this._generateTextureSampleLevel( texture, textureProperty, uvSnippet, levelSnippet, depthSnippet );
702+
return this.generateTextureLod( texture, textureProperty, uvSnippet, depthSnippet, levelSnippet );
725703

726704
}
727705

728-
return snippet;
729-
730706
}
731707

732708
/**
@@ -1729,10 +1705,6 @@ ${ flowData.code }
17291705

17301706
textureType = 'texture_3d<f32>';
17311707

1732-
} else if ( texture.isVideoTexture === true ) {
1733-
1734-
textureType = 'texture_external';
1735-
17361708
} else {
17371709

17381710
const componentPrefix = this.getComponentTypeFromTexture( texture ).charAt( 0 );

src/renderers/webgpu/utils/WebGPUBindingUtils.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,6 @@ class WebGPUBindingUtils {
113113

114114
bindingGPU.sampler = sampler;
115115

116-
} else if ( binding.isSampledTexture && binding.texture.isVideoTexture ) {
117-
118-
bindingGPU.externalTexture = {}; // GPUExternalTextureBindingLayout
119-
120116
} else if ( binding.isSampledTexture && binding.store ) {
121117

122118
const storageTexture = {}; // GPUStorageTextureBindingLayout

src/renderers/webgpu/utils/WebGPUTextureUtils.js

Lines changed: 11 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,6 @@ class WebGPUTextureUtils {
159159

160160
textureGPU = this._getDefaultCubeTextureGPU( format );
161161

162-
} else if ( texture.isVideoTexture ) {
163-
164-
this.backend.get( texture ).externalTexture = this._getDefaultVideoFrame();
165-
166162
} else {
167163

168164
textureGPU = this._getDefaultTextureGPU( format );
@@ -247,39 +243,23 @@ class WebGPUTextureUtils {
247243

248244
// texture creation
249245

250-
if ( texture.isVideoTexture ) {
251-
252-
const video = texture.source.data;
253-
const videoFrame = new VideoFrame( video );
254-
255-
textureDescriptorGPU.size.width = videoFrame.displayWidth;
256-
textureDescriptorGPU.size.height = videoFrame.displayHeight;
257-
258-
videoFrame.close();
259-
260-
textureData.externalTexture = video;
261-
262-
} else {
263-
264-
if ( format === undefined ) {
246+
if ( format === undefined ) {
265247

266-
console.warn( 'WebGPURenderer: Texture format not supported.' );
248+
console.warn( 'WebGPURenderer: Texture format not supported.' );
267249

268-
this.createDefaultTexture( texture );
269-
return;
270-
271-
}
272-
273-
if ( texture.isCubeTexture ) {
250+
this.createDefaultTexture( texture );
251+
return;
274252

275-
textureDescriptorGPU.textureBindingViewDimension = GPUTextureViewDimension.Cube;
253+
}
276254

277-
}
255+
if ( texture.isCubeTexture ) {
278256

279-
textureData.texture = backend.device.createTexture( textureDescriptorGPU );
257+
textureDescriptorGPU.textureBindingViewDimension = GPUTextureViewDimension.Cube;
280258

281259
}
282260

261+
textureData.texture = backend.device.createTexture( textureDescriptorGPU );
262+
283263
if ( isMSAA ) {
284264

285265
const msaaTextureDescriptorGPU = Object.assign( {}, textureDescriptorGPU );
@@ -480,12 +460,6 @@ class WebGPUTextureUtils {
480460

481461
this._copyCubeMapToTexture( options.images, textureData.texture, textureDescriptorGPU, texture.flipY, texture.premultiplyAlpha );
482462

483-
} else if ( texture.isVideoTexture ) {
484-
485-
const video = texture.source.data;
486-
487-
textureData.externalTexture = video;
488-
489463
} else {
490464

491465
this._copyImageToTexture( options.image, textureData.texture, textureDescriptorGPU, 0, texture.flipY, texture.premultiplyAlpha );
@@ -615,33 +589,6 @@ class WebGPUTextureUtils {
615589

616590
}
617591

618-
/**
619-
* Returns the default video frame used as default data in context of video textures.
620-
*
621-
* @private
622-
* @return {VideoFrame} The video frame.
623-
*/
624-
_getDefaultVideoFrame() {
625-
626-
let defaultVideoFrame = this.defaultVideoFrame;
627-
628-
if ( defaultVideoFrame === null ) {
629-
630-
const init = {
631-
timestamp: 0,
632-
codedWidth: 1,
633-
codedHeight: 1,
634-
format: 'RGBA',
635-
};
636-
637-
this.defaultVideoFrame = defaultVideoFrame = new VideoFrame( new Uint8Array( [ 0, 0, 0, 0xff ] ), init );
638-
639-
}
640-
641-
return defaultVideoFrame;
642-
643-
}
644-
645592
/**
646593
* Uploads cube texture image data to the GPU memory.
647594
*
@@ -699,8 +646,8 @@ class WebGPUTextureUtils {
699646
origin: { x: 0, y: 0, z: originDepth },
700647
premultipliedAlpha: premultiplyAlpha
701648
}, {
702-
width: image.width,
703-
height: image.height,
649+
width: textureDescriptorGPU.size.width,
650+
height: textureDescriptorGPU.size.height,
704651
depthOrArrayLayers: 1
705652
}
706653
);

0 commit comments

Comments
 (0)