@lightningtv/renderer
Version:
Lightning 3 Renderer
195 lines • 7.84 kB
JavaScript
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