Skip to content

Commit

Permalink
Add a way to allow apps to wait on shader compilation before adding o…
Browse files Browse the repository at this point in the history
…bjects to a scene.
  • Loading branch information
toji committed Jun 29, 2020
1 parent cea4a5a commit 4c96e36
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 5 deletions.
10 changes: 7 additions & 3 deletions examples/webgl_loader_gltf.html
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,15 @@

} );

scene.add( gltf.scene );

roughnessMipmapper.dispose();

render();
renderer.compileTarget( scene, gltf.scene, function() {

scene.add( gltf.scene );

render();

} );

} );

Expand Down
8 changes: 6 additions & 2 deletions examples/webgl_loader_gltf_extensions.html
Original file line number Diff line number Diff line change
Expand Up @@ -373,8 +373,12 @@

}

scene.add( object );
onWindowResize();
renderer.compileTarget( scene, object, function() {

scene.add( object );
onWindowResize();

} );

}, undefined, function ( error ) {

Expand Down
158 changes: 158 additions & 0 deletions src/renderers/WebGLRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,164 @@ function WebGLRenderer( parameters ) {

};

// compileTarget

this.compileTarget = function ( scene, target, callback ) {

// temporary camera just for the compile
const camera = new Camera();

currentRenderState = renderStates.get( scene, camera );
currentRenderState.init();

// if no explicit target was given use the full scene

if ( typeof target === 'unknown' ) {

target = scene;

}

let foundTarget = scene === target;

// Gather lights from both the scene and the new object that will be added
// to the scene.
scene.traverse( function ( object ) {

if ( object === target ) {

foundTarget = true;

}

if ( object.isLight ) {

currentRenderState.pushLight( object );

if ( object.castShadow ) {

currentRenderState.pushShadow( object );

}

}

} );

// If the target wasn't already part of the scene, add any lights it
// contains as well.

if ( !foundTarget ) {

target.traverse( function ( object ) {

if ( object.isLight ) {

currentRenderState.pushLight( object );

if ( object.castShadow ) {

currentRenderState.pushShadow( object );

}

}

} );

}

currentRenderState.setupLights( camera );

const compiling = new Set();

// only initialize materials in the new object

target.traverse( function ( object ) {

let material = object.material;

if ( material ) {

if ( Array.isArray( material ) ) {

for ( let i = 0; i < material.length; i ++ ) {

let material2 = material[ i ];

if ( compiling.has( material2 ) === false ) {

initMaterial( material2, scene, object );
compiling.add( material2 );

}

}

} else if ( compiling.has( material ) === false ) {

initMaterial( material, scene, object );
compiling.add( material );

}

}

} );

if ( typeof callback === 'undefined' ) return;

// if we've been given a callback, wait for all the materials in the new
// object to indicate that they're ready to be used before calling it

function checkMaterialsReady() {

compiling.forEach( function ( material ) {

const materialProperties = properties.get( material );
const program = materialProperties.program;

if ( program.isReady() ) {

// remove any programs that report they're ready to use from the list
compiling.delete( material );

}

} );

// once the list of compiling materials is empty, call the callback

if ( compiling.size === 0 ) {

callback( target );
return;

}

// if some materials are still not ready, wait a bit and check again

setTimeout( checkMaterialsReady, 10 );

}

if ( extensions.get( 'KHR_parallel_shader_compile' ) !== null ) {

// if we can check the compilation status of the materials without
// blocking then do so right away.

checkMaterialsReady();

} else {

// otherwise start by waiting a bit to give the materials we just
// initialized a chance to finish.

setTimeout( checkMaterialsReady, 10 );

}
};

// Animation Loop

let onAnimationFrameCallback = null;
Expand Down
21 changes: 21 additions & 0 deletions src/renderers/webgl/WebGLProgram.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { WebGLShader } from './WebGLShader.js';
import { ShaderChunk } from '../shaders/ShaderChunk.js';
import { NoToneMapping, AddOperation, MixOperation, MultiplyOperation, EquirectangularRefractionMapping, CubeRefractionMapping, EquirectangularReflectionMapping, CubeUVRefractionMapping, CubeUVReflectionMapping, CubeReflectionMapping, PCFSoftShadowMap, PCFShadowMap, VSMShadowMap, ACESFilmicToneMapping, CineonToneMapping, CustomToneMapping, ReinhardToneMapping, LinearToneMapping, GammaEncoding, RGBDEncoding, RGBM16Encoding, RGBM7Encoding, RGBEEncoding, sRGBEncoding, LinearEncoding, LogLuvEncoding } from '../../constants.js';

// From https://www.khronos.org/registry/webgl/extensions/KHR_parallel_shader_compile/
const COMPLETION_STATUS_KHR = 0x91B1;

let programIdCount = 0;

function addLineNumbers( string ) {
Expand Down Expand Up @@ -858,6 +861,24 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {

};

// indicate when the program is ready to be used

// if the KHR_parallel_shader_compile extension isn't supported, flag the
// program as ready immediately. It may cause a stall when it's first used.
let programReady = !parameters.rendererExtensionParallelShaderCompile;

this.isReady = function () {

if (!programReady) {

programReady = gl.getProgramParameter( program, COMPLETION_STATUS_KHR );

}

return programReady;

};

// free resource

this.destroy = function () {
Expand Down
1 change: 1 addition & 0 deletions src/renderers/webgl/WebGLPrograms.js
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ function WebGLPrograms( renderer, extensions, capabilities, bindingStates ) {
rendererExtensionFragDepth: isWebGL2 || extensions.get( 'EXT_frag_depth' ) !== null,
rendererExtensionDrawBuffers: isWebGL2 || extensions.get( 'WEBGL_draw_buffers' ) !== null,
rendererExtensionShaderTextureLod: isWebGL2 || extensions.get( 'EXT_shader_texture_lod' ) !== null,
rendererExtensionParallelShaderCompile: extensions.get( 'KHR_parallel_shader_compile' ) !== null,

customProgramCacheKey: material.customProgramCacheKey()

Expand Down

0 comments on commit 4c96e36

Please sign in to comment.