* -------------------------------------------------------------------------
* This file is part of the MindStudio project.
* Copyright (c) 2026 Huawei Technologies Co.,Ltd.
*
* MindStudio is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*/
import { GL_COLORS, GL_DIMMED_COLORS, GL_HIGHLIGHT_COLORS } from '@/leaksWorker/tools/color';
type ColorMode = 'normal' | 'highlight' | 'dimmed';
export abstract class Program {
readonly uniformLoc: Record<string, WebGLUniformLocation | null> = {};
gl: WebGL2RenderingContext;
program: WebGLProgram | null;
vao: WebGLVertexArrayObject | null = null;
instanceBuffer: WebGLBuffer | null = null;
readonly uniformData: Float32Array;
constructor(gl: WebGL2RenderingContext, uniformData: Float32Array, shader: Shader) {
this.gl = gl;
this.uniformData = uniformData;
this.program = this.createProgram(shader.vertexShader, shader.fragmentShader);
this.uniformLoc.uScale = gl.getUniformLocation(this.program, 'uScale');
this.uniformLoc.uTranslate = gl.getUniformLocation(this.program, 'uTranslate');
this.uniformLoc.uResolution = gl.getUniformLocation(this.program, 'uResolution');
this.uniformLoc.uZoom = gl.getUniformLocation(this.program, 'uZoom');
this.uniformLoc.uBorderColor = gl.getUniformLocation(this.program, 'uBorderColor');
for (let i = 0; i < GL_COLORS.length; i++) {
this.uniformLoc[`uColors[${i}]`] = gl.getUniformLocation(this.program, `uColors[${i}]`);
}
this.vao = gl.createVertexArray();
gl.bindVertexArray(this.vao);
}
protected createShader(type: number, source: string): WebGLShader {
const gl = this.gl;
const shader = gl.createShader(type);
if (shader === null) {
throw new Error('Failed to create shader');
}
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
const info = gl.getShaderInfoLog(shader);
gl.deleteShader(shader);
throw new Error(`Shader compilation failed: ${info}`);
}
return shader;
}
protected createProgram(vertexSource: string, fragmentSource: string): WebGLProgram {
const gl = this.gl;
const vertexShader = this.createShader(gl.VERTEX_SHADER, vertexSource);
const fragmentShader = this.createShader(gl.FRAGMENT_SHADER, fragmentSource);
const program = gl.createProgram();
if (program === null) {
throw new Error('Failed to create program');
}
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
const info = gl.getProgramInfoLog(program);
gl.deleteProgram(program);
throw new Error(`Program linking failed: ${info}`);
}
gl.deleteShader(vertexShader);
gl.deleteShader(fragmentShader);
return program;
}
createBuffer(size: number): WebGLBuffer {
const gl = this.gl;
const buffer = gl.createBuffer();
if (buffer === null) {
throw new Error('Failed to create buffer.');
}
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, size, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
return buffer;
}
setBaseUniforms(): void {
const gl = this.gl;
const d = this.uniformData;
gl.uniform2f(this.uniformLoc.uScale, d[0], d[1]);
gl.uniform2f(this.uniformLoc.uTranslate, d[2], d[3]);
gl.uniform2f(this.uniformLoc.uResolution, d[4], d[5]);
gl.uniform2f(this.uniformLoc.uZoom, d[6], d[7]);
}
setColorUniforms(mode: ColorMode = 'normal'): void {
const gl = this.gl;
const colors = mode === 'highlight' ? GL_HIGHLIGHT_COLORS : mode === 'dimmed' ? GL_DIMMED_COLORS : GL_COLORS;
for (let i = 0; i < colors.length; i++) {
gl.uniform4f(this.uniformLoc[`uColors[${i}]`], colors[i][0], colors[i][1], colors[i][2], colors[i][3]);
}
}
updateSubBuffer(data: Float32Array, size: number): void {
const gl = this.gl;
gl.bindBuffer(gl.ARRAY_BUFFER, this.instanceBuffer);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, data.subarray(0, size));
}
cleanupGL(): void {
this.gl.bindVertexArray(null);
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);
}
destroy(): void {
if (this.instanceBuffer) {
this.gl.deleteBuffer(this.instanceBuffer);
this.instanceBuffer = null;
}
if (this.vao) {
this.gl.deleteVertexArray(this.vao);
this.vao = null;
}
if (this.program) {
this.gl.deleteProgram(this.program);
this.program = null;
}
}
}