UNPKG

@lightningtv/renderer

Version:
195 lines 7.84 kB
import { Default } from '../../shaders/webgl/Default.js'; import { createProgram, createShader, } from './internal/ShaderUtils.js'; export class WebGlShaderProgram { boundBufferCollection = null; program; /** * Vertex Array Object * * @remarks * Used by WebGL2 Only */ vao; renderer; glw; attributeLocations; lifecycle; useSystemAlpha = false; useSystemDimensions = false; supportsIndexedTextures = false; constructor(renderer, config, resolvedProps) { this.renderer = renderer; const glw = (this.glw = renderer.glw); // Check that extensions are supported const webGl2 = glw.isWebGl2(); let requiredExtensions = []; this.supportsIndexedTextures = config.supportsIndexedTextures || this.supportsIndexedTextures; requiredExtensions = (webGl2 && config.webgl2Extensions) || (!webGl2 && config.webgl1Extensions) || []; const glVersion = webGl2 ? '2.0' : '1.0'; requiredExtensions.forEach((extensionName) => { if (!glw.getExtension(extensionName)) { throw new Error(`Shader "${this.constructor.name}" requires extension "${extensionName}" for WebGL ${glVersion} but wasn't found`); } }); let vertexSource = config.vertex instanceof Function ? config.vertex(renderer, resolvedProps) : config.vertex; if (vertexSource === undefined) { vertexSource = Default.vertex; } const fragmentSource = config.fragment instanceof Function ? config.fragment(renderer, resolvedProps) : config.fragment; const vertexShader = createShader(glw, glw.VERTEX_SHADER, vertexSource); if (!vertexShader) { throw new Error('Vertex shader creation failed'); } const fragmentShader = createShader(glw, glw.FRAGMENT_SHADER, fragmentSource); if (!fragmentShader) { throw new Error('fragment shader creation failed'); } const program = createProgram(glw, vertexShader, fragmentShader); if (!program) { throw new Error(); } this.program = program; this.attributeLocations = glw.getAttributeLocations(program); this.useSystemAlpha = this.glw.getUniformLocation(program, 'u_alpha') !== null; this.useSystemDimensions = this.glw.getUniformLocation(program, 'u_dimensions') !== null; this.lifecycle = { update: config.update, canBatch: config.canBatch, }; } disableAttribute(location) { this.glw.disableVertexAttribArray(location); } disableAttributes() { const { glw } = this; const attribs = Object.keys(this.attributeLocations); const attribLen = attribs.length; for (let i = 0; i < attribLen; i++) { glw.disableVertexAttribArray(i); } } reuseRenderOp(renderOpA, renderOpB) { const lifecycleCheck = this.lifecycle.canBatch ? this.lifecycle.canBatch(renderOpA, renderOpB) : true; if (!lifecycleCheck) { return false; } if (this.useSystemAlpha) { if (renderOpA.alpha !== renderOpB.alpha) { return false; } } if (this.useSystemDimensions) { if (renderOpA.width !== renderOpB.width || renderOpA.height !== renderOpB.height) { return false; } } const shaderPropsA = renderOpA.shader?.getResolvedProps(); const shaderPropsB = renderOpB.shader?.getResolvedProps(); if (shaderPropsA !== undefined && shaderPropsB !== undefined) { for (const key in shaderPropsA) { if (shaderPropsA[key] !== shaderPropsB[key]) { return false; } } } return true; } bindRenderOp(renderOp) { this.bindBufferCollection(renderOp.buffers); this.bindTextures(renderOp.textures); const { parentHasRenderTexture } = renderOp.quad; // Skip if the parent and current operation both have render textures if (renderOp.quad.rtt && parentHasRenderTexture) { return; } // Bind render texture framebuffer dimensions as resolution // if the parent has a render texture if (parentHasRenderTexture) { const { width, height } = renderOp.quad.framebufferDimensions; // Force pixel ratio to 1.0 for render textures since they are always 1:1 // the final render texture will be rendered to the screen with the correct pixel ratio this.glw.uniform1f('u_pixelRatio', 1.0); // Set resolution to the framebuffer dimensions this.glw.uniform2f('u_resolution', width, height); } else { this.glw.uniform1f('u_pixelRatio', renderOp.renderer.stage.pixelRatio); this.glw.uniform2f('u_resolution', this.glw.canvas.width, this.glw.canvas.height); } this.glw.uniform1f('u_rtt', renderOp.quad.rtt ? 1 : 0); if (this.useSystemAlpha) { this.glw.uniform1f('u_alpha', renderOp.quad.alpha); } if (this.useSystemDimensions) { this.glw.uniform2f('u_dimensions', renderOp.quad.width, renderOp.quad.height); } /**temporary fix to make sdf texts work */ if (renderOp.sdfShaderProps !== undefined) { renderOp.shader.shaderType.onSdfBind?.call(this.glw, renderOp.sdfShaderProps); return; } if (renderOp.shader.props) { /** * loop over all precalculated uniform types */ for (const key in renderOp.shader.uniforms.single) { const { method, value } = renderOp.shader.uniforms.single[key]; this.glw[method](key, value); } for (const key in renderOp.shader.uniforms.vec2) { const { method, value } = renderOp.shader.uniforms.vec2[key]; this.glw[method](key, value[0], value[1]); } for (const key in renderOp.shader.uniforms.vec3) { const { method, value } = renderOp.shader.uniforms.vec3[key]; this.glw[method](key, value[0], value[1], value[2]); } for (const key in renderOp.shader.uniforms.vec4) { const { method, value } = renderOp.shader.uniforms.vec4[key]; this.glw[method](key, value[0], value[1], value[2], value[3]); } } } bindBufferCollection(buffer) { const { glw } = this; const attribs = Object.keys(this.attributeLocations); const attribLen = attribs.length; for (let i = 0; i < attribLen; i++) { const name = attribs[i]; const resolvedBuffer = buffer.getBuffer(name); const resolvedInfo = buffer.getAttributeInfo(name); if (!resolvedBuffer || !resolvedInfo) { continue; } glw.enableVertexAttribArray(i); glw.vertexAttribPointer(resolvedBuffer, i, resolvedInfo.size, resolvedInfo.type, resolvedInfo.normalized, resolvedInfo.stride, resolvedInfo.offset); } } bindTextures(textures) { this.glw.activeTexture(0); this.glw.bindTexture(textures[0].ctxTexture); } attach() { this.glw.useProgram(this.program); if (this.glw.isWebGl2() && this.vao) { this.glw.bindVertexArray(this.vao); } } detach() { this.disableAttributes(); } } //# sourceMappingURL=WebGlShaderProgram.js.map