Skip to content

Commit

Permalink
Defer blocking WebGLPrograms queries after linking (mrdoob#19745)
Browse files Browse the repository at this point in the history
Program linking can happen asynchronously, but is forced to block and wait for completion the first time information about that program is queried after linking. This includes calls to getAttribLocation/getUniformLocation/getUniform/getProgramParameter/getProgramLogInfo, etc.

This change defers most of those calls until the first time until the first time that they're actually needed by the page, which can allow the program linker time to complete and thus reduce blocking, especially when used in conjuntion with renderer.compile().

It's worth noting that many materials are initialized the first time they are encountered by the render loop and then immediately used for rendering, which negates most of the benefit that this could otherwise provide, but it lays the groundwork for more efficient patterns in the future and can significantly help classes like PMREMGenerator that are already using renderer.compile() internally.
  • Loading branch information
toji authored Oct 13, 2023
1 parent 5303ef2 commit da47522
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 56 deletions.
22 changes: 16 additions & 6 deletions src/renderers/WebGLRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1596,16 +1596,26 @@ class WebGLRenderer {

}

const progUniforms = program.getUniforms();
const uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms );

materialProperties.currentProgram = program;
materialProperties.uniformsList = uniformsList;
materialProperties.uniformsList = null;

return program;

}

function getUniformList( materialProperties ) {

if ( materialProperties.uniformsList === null ) {

const progUniforms = materialProperties.currentProgram.getUniforms();
materialProperties.uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, materialProperties.uniforms );

}

return materialProperties.uniformsList;

}

function updateCommonMaterialProperties( material, parameters ) {

const materialProperties = properties.get( material );
Expand Down Expand Up @@ -1933,13 +1943,13 @@ class WebGLRenderer {

materials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height, _transmissionRenderTarget );

WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures );
WebGLUniforms.upload( _gl, getUniformList( materialProperties ), m_uniforms, textures );

}

if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) {

WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures );
WebGLUniforms.upload( _gl, getUniformList( materialProperties ), m_uniforms, textures );
material.uniformsNeedUpdate = false;

}
Expand Down
109 changes: 59 additions & 50 deletions src/renderers/webgl/WebGLProgram.js
Original file line number Diff line number Diff line change
Expand Up @@ -891,87 +891,94 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {

gl.linkProgram( program );

// check for link errors
if ( renderer.debug.checkShaderErrors ) {
function onFirstUse( self ) {

const programLog = gl.getProgramInfoLog( program ).trim();
const vertexLog = gl.getShaderInfoLog( glVertexShader ).trim();
const fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim();
// check for link errors
if ( renderer.debug.checkShaderErrors ) {

let runnable = true;
let haveDiagnostics = true;
const programLog = gl.getProgramInfoLog( program ).trim();
const vertexLog = gl.getShaderInfoLog( glVertexShader ).trim();
const fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim();

if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) {
let runnable = true;
let haveDiagnostics = true;

runnable = false;
if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) {

if ( typeof renderer.debug.onShaderError === 'function' ) {
runnable = false;

renderer.debug.onShaderError( gl, program, glVertexShader, glFragmentShader );
if ( typeof renderer.debug.onShaderError === 'function' ) {

} else {
renderer.debug.onShaderError( gl, program, glVertexShader, glFragmentShader );

// default error reporting
} else {

const vertexErrors = getShaderErrors( gl, glVertexShader, 'vertex' );
const fragmentErrors = getShaderErrors( gl, glFragmentShader, 'fragment' );
// default error reporting

console.error(
'THREE.WebGLProgram: Shader Error ' + gl.getError() + ' - ' +
'VALIDATE_STATUS ' + gl.getProgramParameter( program, gl.VALIDATE_STATUS ) + '\n\n' +
'Program Info Log: ' + programLog + '\n' +
vertexErrors + '\n' +
fragmentErrors
);
const vertexErrors = getShaderErrors( gl, glVertexShader, 'vertex' );
const fragmentErrors = getShaderErrors( gl, glFragmentShader, 'fragment' );

}
console.error(
'THREE.WebGLProgram: Shader Error ' + gl.getError() + ' - ' +
'VALIDATE_STATUS ' + gl.getProgramParameter( program, gl.VALIDATE_STATUS ) + '\n\n' +
'Program Info Log: ' + programLog + '\n' +
vertexErrors + '\n' +
fragmentErrors
);

} else if ( programLog !== '' ) {
}

console.warn( 'THREE.WebGLProgram: Program Info Log:', programLog );
} else if ( programLog !== '' ) {

} else if ( vertexLog === '' || fragmentLog === '' ) {
console.warn( 'THREE.WebGLProgram: Program Info Log:', programLog );

haveDiagnostics = false;
} else if ( vertexLog === '' || fragmentLog === '' ) {

}
haveDiagnostics = false;

if ( haveDiagnostics ) {
}

this.diagnostics = {
if ( haveDiagnostics ) {

runnable: runnable,
self.diagnostics = {

programLog: programLog,
runnable: runnable,

vertexShader: {
programLog: programLog,

log: vertexLog,
prefix: prefixVertex
vertexShader: {

},
log: vertexLog,
prefix: prefixVertex

fragmentShader: {
},

log: fragmentLog,
prefix: prefixFragment
fragmentShader: {

}
log: fragmentLog,
prefix: prefixFragment

}

};
};

}

}

}
// Clean up

// Clean up
// Crashes in iOS9 and iOS10. #18402
// gl.detachShader( program, glVertexShader );
// gl.detachShader( program, glFragmentShader );

// Crashes in iOS9 and iOS10. #18402
// gl.detachShader( program, glVertexShader );
// gl.detachShader( program, glFragmentShader );
gl.deleteShader( glVertexShader );
gl.deleteShader( glFragmentShader );

gl.deleteShader( glVertexShader );
gl.deleteShader( glFragmentShader );
cachedUniforms = new WebGLUniforms( gl, program );
cachedAttributes = fetchAttributeLocations( gl, program );

}

// set up caching for uniform locations

Expand All @@ -981,7 +988,8 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {

if ( cachedUniforms === undefined ) {

cachedUniforms = new WebGLUniforms( gl, program );
// Populates cachedUniforms and cachedAttributes
onFirstUse( this );

}

Expand All @@ -997,7 +1005,8 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {

if ( cachedAttributes === undefined ) {

cachedAttributes = fetchAttributeLocations( gl, program );
// Populates cachedAttributes and cachedUniforms
onFirstUse( this );

}

Expand Down

0 comments on commit da47522

Please sign in to comment.