UNPKG

@babylonjs/core

Version:

Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.

247 lines 11.2 kB
import { WebGLPipelineContext } from "./WebGL/webGLPipelineContext.js"; import { _ConcatenateShader } from "./abstractEngine.functions.js"; const StateObject = new WeakMap(); /** * This will be used in cases where the engine doesn't have a context (like the nullengine) */ const SingleStateObject = { _webGLVersion: 2, cachedPipelines: {}, }; /** * get or create a state object for the given context * Note - Used in WebGL only at the moment. * @param context The context to get the state object from * @returns the state object * @internal */ export function getStateObject(context) { let state = StateObject.get(context); if (!state) { if (!context) { return SingleStateObject; } state = { // use feature detection. instanceof returns false. This only exists on WebGL2 context _webGLVersion: context.TEXTURE_BINDING_3D ? 2 : 1, _context: context, // when using the function without an engine we need to set it to enable parallel compilation parallelShaderCompile: context.getExtension("KHR_parallel_shader_compile") || undefined, cachedPipelines: {}, }; StateObject.set(context, state); } return state; } /** * Remove the state object that belongs to the specific context * @param context the context that is being */ export function deleteStateObject(context) { StateObject.delete(context); } /** * Directly creates a webGL program * @param pipelineContext defines the pipeline context to attach to * @param vertexCode defines the vertex shader code to use * @param fragmentCode defines the fragment shader code to use * @param context defines the webGL context to use (if not set, the current one will be used) * @param transformFeedbackVaryings defines the list of transform feedback varyings to use * @param _createShaderProgramInjection defines an optional injection to use to create the shader program * @returns the new webGL program */ export function createRawShaderProgram(pipelineContext, vertexCode, fragmentCode, context, transformFeedbackVaryings, _createShaderProgramInjection) { const stateObject = getStateObject(context); if (!_createShaderProgramInjection) { _createShaderProgramInjection = stateObject._createShaderProgramInjection ?? _createShaderProgram; } const vertexShader = CompileRawShader(vertexCode, "vertex", context, stateObject._contextWasLost); const fragmentShader = CompileRawShader(fragmentCode, "fragment", context, stateObject._contextWasLost); return _createShaderProgramInjection(pipelineContext, vertexShader, fragmentShader, context, transformFeedbackVaryings, stateObject.validateShaderPrograms); } /** * Creates a webGL program * @param pipelineContext defines the pipeline context to attach to * @param vertexCode defines the vertex shader code to use * @param fragmentCode defines the fragment shader code to use * @param defines defines the string containing the defines to use to compile the shaders * @param context defines the webGL context to use (if not set, the current one will be used) * @param transformFeedbackVaryings defines the list of transform feedback varyings to use * @param _createShaderProgramInjection defines an optional injection to use to create the shader program * @returns the new webGL program */ export function createShaderProgram(pipelineContext, vertexCode, fragmentCode, defines, context, transformFeedbackVaryings = null, _createShaderProgramInjection) { const stateObject = getStateObject(context); if (!_createShaderProgramInjection) { _createShaderProgramInjection = stateObject._createShaderProgramInjection ?? _createShaderProgram; } const shaderVersion = stateObject._webGLVersion > 1 ? "#version 300 es\n#define WEBGL2 \n" : ""; const vertexShader = CompileShader(vertexCode, "vertex", defines, shaderVersion, context, stateObject._contextWasLost); const fragmentShader = CompileShader(fragmentCode, "fragment", defines, shaderVersion, context, stateObject._contextWasLost); return _createShaderProgramInjection(pipelineContext, vertexShader, fragmentShader, context, transformFeedbackVaryings, stateObject.validateShaderPrograms); } /** * Creates a new pipeline context. Note, make sure to attach an engine instance to the created context * @param context defines the webGL context to use (if not set, the current one will be used) * @param _shaderProcessingContext defines the shader processing context used during the processing if available * @returns the new pipeline */ export function createPipelineContext(context, _shaderProcessingContext) { const pipelineContext = new WebGLPipelineContext(); const stateObject = getStateObject(context); if (stateObject.parallelShaderCompile && !stateObject.disableParallelShaderCompile) { pipelineContext.isParallelCompiled = true; } pipelineContext.context = stateObject._context; return pipelineContext; } /** * @internal */ export function _createShaderProgram(pipelineContext, vertexShader, fragmentShader, context, _transformFeedbackVaryings = null, validateShaderPrograms) { const shaderProgram = context.createProgram(); pipelineContext.program = shaderProgram; if (!shaderProgram) { throw new Error("Unable to create program"); } context.attachShader(shaderProgram, vertexShader); context.attachShader(shaderProgram, fragmentShader); context.linkProgram(shaderProgram); pipelineContext.context = context; pipelineContext.vertexShader = vertexShader; pipelineContext.fragmentShader = fragmentShader; if (!pipelineContext.isParallelCompiled) { _finalizePipelineContext(pipelineContext, context, validateShaderPrograms); } return shaderProgram; } /** * @internal */ export function _isRenderingStateCompiled(pipelineContext, gl, validateShaderPrograms) { const webGLPipelineContext = pipelineContext; if (webGLPipelineContext._isDisposed) { return false; } const stateObject = getStateObject(gl); if (stateObject && stateObject.parallelShaderCompile && stateObject.parallelShaderCompile.COMPLETION_STATUS_KHR && webGLPipelineContext.program) { if (gl.getProgramParameter(webGLPipelineContext.program, stateObject.parallelShaderCompile.COMPLETION_STATUS_KHR)) { _finalizePipelineContext(webGLPipelineContext, gl, validateShaderPrograms); return true; } } return false; } /** * @internal */ export function _finalizePipelineContext(pipelineContext, gl, validateShaderPrograms) { const context = pipelineContext.context; const vertexShader = pipelineContext.vertexShader; const fragmentShader = pipelineContext.fragmentShader; const program = pipelineContext.program; const linked = context.getProgramParameter(program, context.LINK_STATUS); if (!linked) { // Get more info // Vertex if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) { const log = gl.getShaderInfoLog(vertexShader); if (log) { pipelineContext.vertexCompilationError = log; throw new Error("VERTEX SHADER " + log); } } // Fragment if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) { const log = gl.getShaderInfoLog(fragmentShader); if (log) { pipelineContext.fragmentCompilationError = log; throw new Error("FRAGMENT SHADER " + log); } } const error = context.getProgramInfoLog(program); if (error) { pipelineContext.programLinkError = error; throw new Error(error); } } if ( /*this.*/validateShaderPrograms) { context.validateProgram(program); const validated = context.getProgramParameter(program, context.VALIDATE_STATUS); if (!validated) { const error = context.getProgramInfoLog(program); if (error) { pipelineContext.programValidationError = error; throw new Error(error); } } } context.deleteShader(vertexShader); context.deleteShader(fragmentShader); pipelineContext.vertexShader = undefined; pipelineContext.fragmentShader = undefined; if (pipelineContext.onCompiled) { pipelineContext.onCompiled(); pipelineContext.onCompiled = undefined; } } /** * @internal */ export function _preparePipelineContext(pipelineContext, vertexSourceCode, fragmentSourceCode, createAsRaw, _rawVertexSourceCode, _rawFragmentSourceCode, rebuildRebind, defines, transformFeedbackVaryings, _key = "", onReady, createRawShaderProgramInjection, createShaderProgramInjection) { const stateObject = getStateObject(pipelineContext.context); if (!createRawShaderProgramInjection) { createRawShaderProgramInjection = stateObject.createRawShaderProgramInjection ?? createRawShaderProgram; } if (!createShaderProgramInjection) { createShaderProgramInjection = stateObject.createShaderProgramInjection ?? createShaderProgram; } const webGLRenderingState = pipelineContext; if (createAsRaw) { webGLRenderingState.program = createRawShaderProgramInjection(webGLRenderingState, vertexSourceCode, fragmentSourceCode, webGLRenderingState.context, transformFeedbackVaryings); } else { webGLRenderingState.program = createShaderProgramInjection(webGLRenderingState, vertexSourceCode, fragmentSourceCode, defines, webGLRenderingState.context, transformFeedbackVaryings); } webGLRenderingState.program.__SPECTOR_rebuildProgram = rebuildRebind; onReady(); } function CompileShader(source, type, defines, shaderVersion, gl, _contextWasLost) { return CompileRawShader(_ConcatenateShader(source, defines, shaderVersion), type, gl, _contextWasLost); } function CompileRawShader(source, type, gl, _contextWasLost) { const shader = gl.createShader(type === "vertex" ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER); if (!shader) { let error = gl.NO_ERROR; let tempError = gl.NO_ERROR; while ((tempError = gl.getError()) !== gl.NO_ERROR) { error = tempError; } throw new Error(`Something went wrong while creating a gl ${type} shader object. gl error=${error}, gl isContextLost=${gl.isContextLost()}, _contextWasLost=${_contextWasLost}`); } gl.shaderSource(shader, source); gl.compileShader(shader); return shader; } /** * @internal */ export function _setProgram(program, gl) { gl.useProgram(program); } /** * @internal */ export function _executeWhenRenderingStateIsCompiled(pipelineContext, action) { const webGLPipelineContext = pipelineContext; if (!webGLPipelineContext.isParallelCompiled) { action(pipelineContext); return; } const oldHandler = webGLPipelineContext.onCompiled; webGLPipelineContext.onCompiled = () => { oldHandler?.(); action(pipelineContext); }; } //# sourceMappingURL=thinEngine.functions.js.map