Add parallel compiler
This commit is contained in:
parent
adbd933955
commit
36c64554f5
14 changed files with 260 additions and 181 deletions
|
|
@ -1,13 +0,0 @@
|
||||||
import { checkShader } from './check-shader';
|
|
||||||
|
|
||||||
export const checkProgram = (gl: WebGL2RenderingContext, program: WebGLProgram) => {
|
|
||||||
const success = gl.getProgramParameter(program, gl.LINK_STATUS);
|
|
||||||
|
|
||||||
if (!success && !gl.isContextLost()) {
|
|
||||||
gl.getAttachedShaders(program)?.forEach((s) => {
|
|
||||||
checkShader(gl, s);
|
|
||||||
});
|
|
||||||
|
|
||||||
throw new Error(gl.getProgramInfoLog(program)!);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
export const checkShader = (gl: WebGL2RenderingContext, shader: WebGLShader) => {
|
|
||||||
const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
|
|
||||||
|
|
||||||
if (!success && !gl.isContextLost()) {
|
|
||||||
throw new Error(gl.getShaderInfoLog(shader)!);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
import { checkProgram } from './check-program';
|
|
||||||
import { createShader } from './create-shader';
|
|
||||||
|
|
||||||
export const createProgram = (
|
|
||||||
gl: WebGL2RenderingContext,
|
|
||||||
vertexShaderSource: string,
|
|
||||||
fragmentShaderSource: string,
|
|
||||||
substitutions: { [name: string]: string }
|
|
||||||
): WebGLProgram => {
|
|
||||||
// can only return null on lost context
|
|
||||||
const program = gl.createProgram()!;
|
|
||||||
|
|
||||||
// const extension = tryEnableExtension(gl, 'KHR_parallel_shader_compile');
|
|
||||||
|
|
||||||
const vertexShader = createShader(
|
|
||||||
gl,
|
|
||||||
gl.VERTEX_SHADER,
|
|
||||||
vertexShaderSource,
|
|
||||||
substitutions
|
|
||||||
);
|
|
||||||
gl.attachShader(program, vertexShader);
|
|
||||||
|
|
||||||
const fragmentShader = createShader(
|
|
||||||
gl,
|
|
||||||
gl.FRAGMENT_SHADER,
|
|
||||||
fragmentShaderSource,
|
|
||||||
substitutions
|
|
||||||
);
|
|
||||||
|
|
||||||
gl.attachShader(program, fragmentShader);
|
|
||||||
|
|
||||||
gl.linkProgram(program);
|
|
||||||
|
|
||||||
/*if (extension) {
|
|
||||||
const checkCompileStatus = () =>
|
|
||||||
gl.getProgramParameter(program, extension.COMPLETION_STATUS_KHR);
|
|
||||||
|
|
||||||
await waitWhileFalse(checkCompileStatus);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
checkProgram(gl, program);
|
|
||||||
|
|
||||||
gl.deleteShader(vertexShader);
|
|
||||||
gl.deleteShader(fragmentShader);
|
|
||||||
|
|
||||||
return program;
|
|
||||||
};
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
export const createShader = (
|
|
||||||
gl: WebGL2RenderingContext,
|
|
||||||
type: GLenum,
|
|
||||||
source: string,
|
|
||||||
substitutions: { [name: string]: string }
|
|
||||||
): WebGLShader => {
|
|
||||||
source = source.replace(/{(.+)}/gm, (_, name: string): string => {
|
|
||||||
const value = substitutions[name];
|
|
||||||
return Number.isInteger(value) ? `${value}.0` : value;
|
|
||||||
});
|
|
||||||
|
|
||||||
// can only return null on lost context
|
|
||||||
const shader = gl.createShader(type)!;
|
|
||||||
|
|
||||||
gl.shaderSource(shader, source);
|
|
||||||
gl.compileShader(shader);
|
|
||||||
|
|
||||||
return shader;
|
|
||||||
};
|
|
||||||
131
src/graphics/graphics-library/parallel-compiler.ts
Normal file
131
src/graphics/graphics-library/parallel-compiler.ts
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
import { wait } from '../../helper/wait';
|
||||||
|
import { Insights } from '../rendering/insights';
|
||||||
|
import { tryEnableExtension } from './helper/enable-extension';
|
||||||
|
|
||||||
|
type CompilingProgram = {
|
||||||
|
program: WebGLProgram;
|
||||||
|
resolvePromise: ((program: WebGLProgram) => void) | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export abstract class ParallelCompiler {
|
||||||
|
private static extension?: any;
|
||||||
|
private static gl: WebGL2RenderingContext;
|
||||||
|
private static programs: Array<CompilingProgram> = [];
|
||||||
|
|
||||||
|
public static initialize(gl: WebGL2RenderingContext) {
|
||||||
|
ParallelCompiler.gl = gl;
|
||||||
|
ParallelCompiler.extension = tryEnableExtension(gl, 'KHR_parallel_shader_compile');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static createProgram(
|
||||||
|
vertexShaderSource: string,
|
||||||
|
fragmentShaderSource: string,
|
||||||
|
substitutions: { [name: string]: string }
|
||||||
|
): Promise<WebGLProgram> {
|
||||||
|
let resolvePromise: ((program: WebGLProgram) => void) | null = null;
|
||||||
|
const promise = new Promise<WebGLProgram>((r) => (resolvePromise = r));
|
||||||
|
|
||||||
|
// can only return null on lost context
|
||||||
|
const program = ParallelCompiler.gl.createProgram()!;
|
||||||
|
|
||||||
|
ParallelCompiler.compileShader(
|
||||||
|
vertexShaderSource,
|
||||||
|
ParallelCompiler.gl.VERTEX_SHADER,
|
||||||
|
program,
|
||||||
|
substitutions
|
||||||
|
);
|
||||||
|
|
||||||
|
ParallelCompiler.compileShader(
|
||||||
|
fragmentShaderSource,
|
||||||
|
ParallelCompiler.gl.FRAGMENT_SHADER,
|
||||||
|
program,
|
||||||
|
substitutions
|
||||||
|
);
|
||||||
|
|
||||||
|
ParallelCompiler.programs.push({
|
||||||
|
program,
|
||||||
|
resolvePromise,
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async compilePrograms(): Promise<void> {
|
||||||
|
ParallelCompiler.programs.forEach((p) => ParallelCompiler.gl.linkProgram(p.program));
|
||||||
|
|
||||||
|
Insights.setValue('program count', ParallelCompiler.programs.length);
|
||||||
|
|
||||||
|
while (ParallelCompiler.programs.length > 0) {
|
||||||
|
ParallelCompiler.resolveFinishedPrograms();
|
||||||
|
await wait(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static compileShader(
|
||||||
|
source: string,
|
||||||
|
type: GLenum,
|
||||||
|
program: WebGLProgram,
|
||||||
|
substitutions: { [name: string]: string }
|
||||||
|
) {
|
||||||
|
const processedSource = source.replace(/{(.+)}/gm, (_, name: string): string => {
|
||||||
|
const value = substitutions[name];
|
||||||
|
return Number.isInteger(value) ? `${value}.0` : value;
|
||||||
|
});
|
||||||
|
|
||||||
|
// can only return null on lost context
|
||||||
|
const shader = ParallelCompiler.gl.createShader(type)!;
|
||||||
|
|
||||||
|
this.gl.shaderSource(shader, processedSource);
|
||||||
|
this.gl.compileShader(shader);
|
||||||
|
this.gl.attachShader(program, shader);
|
||||||
|
this.gl.deleteShader(shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static resolveFinishedPrograms() {
|
||||||
|
const done: Array<CompilingProgram> = [];
|
||||||
|
|
||||||
|
ParallelCompiler.programs.forEach((p) => {
|
||||||
|
if (
|
||||||
|
!ParallelCompiler.extension ||
|
||||||
|
ParallelCompiler.gl.getProgramParameter(
|
||||||
|
p.program,
|
||||||
|
ParallelCompiler.extension.COMPLETION_STATUS_KHR
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
ParallelCompiler.checkProgram(p.program);
|
||||||
|
done.push(p);
|
||||||
|
p.resolvePromise!(p.program);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ParallelCompiler.programs = ParallelCompiler.programs.filter(
|
||||||
|
(p1) => done.findIndex((p2) => p2 === p1) == -1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static checkProgram(program: WebGLProgram) {
|
||||||
|
const success = ParallelCompiler.gl.getProgramParameter(
|
||||||
|
program,
|
||||||
|
ParallelCompiler.gl.LINK_STATUS
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!success && !ParallelCompiler.gl.isContextLost()) {
|
||||||
|
ParallelCompiler.gl.getAttachedShaders(program)?.forEach((s) => {
|
||||||
|
ParallelCompiler.checkShader(s);
|
||||||
|
});
|
||||||
|
|
||||||
|
throw new Error(ParallelCompiler.gl.getProgramInfoLog(program)!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static checkShader(shader: WebGLShader) {
|
||||||
|
const success = ParallelCompiler.gl.getShaderParameter(
|
||||||
|
shader,
|
||||||
|
ParallelCompiler.gl.COMPILE_STATUS
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!success && !ParallelCompiler.gl.isContextLost()) {
|
||||||
|
throw new Error(ParallelCompiler.gl.getShaderInfoLog(shader)!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,12 +3,15 @@ import Program from './program';
|
||||||
export class FragmentShaderOnlyProgram extends Program {
|
export class FragmentShaderOnlyProgram extends Program {
|
||||||
private vao?: WebGLVertexArrayObject;
|
private vao?: WebGLVertexArrayObject;
|
||||||
|
|
||||||
constructor(
|
constructor(gl: WebGL2RenderingContext) {
|
||||||
gl: WebGL2RenderingContext,
|
super(gl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async initialize(
|
||||||
sources: [string, string],
|
sources: [string, string],
|
||||||
substitutions: { [name: string]: string }
|
substitutions: { [name: string]: string }
|
||||||
) {
|
): Promise<void> {
|
||||||
super(gl, sources, substitutions);
|
await super.initialize(sources, substitutions);
|
||||||
this.prepareScreenQuad('vertexPosition');
|
this.prepareScreenQuad('vertexPosition');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { mat2d, vec2 } from 'gl-matrix';
|
import { mat2d, vec2 } from 'gl-matrix';
|
||||||
import { createProgram } from '../compiling/create-program';
|
|
||||||
import { loadUniform } from '../helper/load-uniform';
|
import { loadUniform } from '../helper/load-uniform';
|
||||||
|
import { ParallelCompiler } from '../parallel-compiler';
|
||||||
import { IProgram } from './i-program';
|
import { IProgram } from './i-program';
|
||||||
|
|
||||||
export default abstract class Program implements IProgram {
|
export default abstract class Program implements IProgram {
|
||||||
|
|
@ -14,15 +14,15 @@ export default abstract class Program implements IProgram {
|
||||||
type: GLenum;
|
type: GLenum;
|
||||||
}> = [];
|
}> = [];
|
||||||
|
|
||||||
constructor(
|
constructor(protected gl: WebGL2RenderingContext) {}
|
||||||
protected gl: WebGL2RenderingContext,
|
|
||||||
|
public async initialize(
|
||||||
[vertexShaderSource, fragmentShaderSource]: [string, string],
|
[vertexShaderSource, fragmentShaderSource]: [string, string],
|
||||||
substitutions: { [name: string]: string }
|
substitutions: { [name: string]: string }
|
||||||
) {
|
): Promise<void> {
|
||||||
substitutions = { ...substitutions };
|
substitutions = { ...substitutions };
|
||||||
|
|
||||||
this.program = createProgram(
|
this.program = await ParallelCompiler.createProgram(
|
||||||
this.gl,
|
|
||||||
vertexShaderSource,
|
vertexShaderSource,
|
||||||
fragmentShaderSource,
|
fragmentShaderSource,
|
||||||
substitutions
|
substitutions
|
||||||
|
|
|
||||||
|
|
@ -12,24 +12,34 @@ export class UniformArrayAutoScalingProgram implements IProgram {
|
||||||
}> = [];
|
}> = [];
|
||||||
|
|
||||||
private current?: FragmentShaderOnlyProgram;
|
private current?: FragmentShaderOnlyProgram;
|
||||||
|
private descriptors?: Array<DrawableDescriptor>;
|
||||||
private drawingRectangleBottomLeft = vec2.fromValues(0, 0);
|
private drawingRectangleBottomLeft = vec2.fromValues(0, 0);
|
||||||
private drawingRectangleSize = vec2.fromValues(1, 1);
|
private drawingRectangleSize = vec2.fromValues(1, 1);
|
||||||
|
|
||||||
constructor(
|
constructor(private gl: WebGL2RenderingContext) {}
|
||||||
private gl: WebGL2RenderingContext,
|
|
||||||
|
public async initialize(
|
||||||
shaderSources: [string, string],
|
shaderSources: [string, string],
|
||||||
private descriptors: Array<DrawableDescriptor>,
|
descriptors: Array<DrawableDescriptor>,
|
||||||
private substitutions: { [name: string]: any }
|
substitutions: { [name: string]: any }
|
||||||
) {
|
): Promise<void> {
|
||||||
|
this.descriptors = descriptors;
|
||||||
|
|
||||||
|
const promises: Array<Promise<void>> = [];
|
||||||
|
|
||||||
for (const combination of getCombinations(
|
for (const combination of getCombinations(
|
||||||
descriptors.map((o) => o.shaderCombinationSteps)
|
descriptors.map((o) => o.shaderCombinationSteps)
|
||||||
)) {
|
)) {
|
||||||
this.createProgram(descriptors, combination, shaderSources);
|
promises.push(
|
||||||
|
this.createProgram(descriptors, substitutions, combination, shaderSources)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bindAndSetUniforms(uniforms: { [name: string]: any }): void {
|
public bindAndSetUniforms(uniforms: { [name: string]: any }): void {
|
||||||
const values = this.descriptors.map((d) =>
|
const values = this.descriptors!.map((d) =>
|
||||||
uniforms[d.uniformName] ? uniforms[d.uniformName].length : 0
|
uniforms[d.uniformName] ? uniforms[d.uniformName].length : 0
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -38,7 +48,7 @@ export class UniformArrayAutoScalingProgram implements IProgram {
|
||||||
this.current = closest ? closest.program : last(this.programs)?.program;
|
this.current = closest ? closest.program : last(this.programs)?.program;
|
||||||
|
|
||||||
if (closest) {
|
if (closest) {
|
||||||
this.descriptors.map((d, i) => {
|
this.descriptors!.map((d, i) => {
|
||||||
const difference = closest.values[i] - values[i];
|
const difference = closest.values[i] - values[i];
|
||||||
for (let i = 0; i < difference; i++) {
|
for (let i = 0; i < difference; i++) {
|
||||||
d.empty.serializeToUniforms(uniforms, mat2d.create(), 0);
|
d.empty.serializeToUniforms(uniforms, mat2d.create(), 0);
|
||||||
|
|
@ -70,26 +80,26 @@ export class UniformArrayAutoScalingProgram implements IProgram {
|
||||||
this.programs.forEach((p) => p.program.delete());
|
this.programs.forEach((p) => p.program.delete());
|
||||||
}
|
}
|
||||||
|
|
||||||
private createProgram(
|
private async createProgram(
|
||||||
descriptors: Array<DrawableDescriptor>,
|
descriptors: Array<DrawableDescriptor>,
|
||||||
|
substitutions: { [name: string]: any },
|
||||||
combination: Array<number>,
|
combination: Array<number>,
|
||||||
shaderSources: [string, string]
|
shaderSources: [string, string]
|
||||||
): FragmentShaderOnlyProgram {
|
): Promise<void> {
|
||||||
const substitutions = {
|
const processedSubstitutions = {
|
||||||
...this.substitutions,
|
...substitutions,
|
||||||
macroDefinitions: this.getMacroDefinitions(combination, descriptors),
|
macroDefinitions: this.getMacroDefinitions(combination, descriptors),
|
||||||
declarations: this.getDeclarations(combination, descriptors),
|
declarations: this.getDeclarations(combination, descriptors),
|
||||||
functionCalls: this.getFunctionCalls(combination, descriptors),
|
functionCalls: this.getFunctionCalls(combination, descriptors),
|
||||||
};
|
};
|
||||||
|
|
||||||
const program = new FragmentShaderOnlyProgram(this.gl, shaderSources, substitutions);
|
const program = new FragmentShaderOnlyProgram(this.gl);
|
||||||
|
await program.initialize(shaderSources, processedSubstitutions);
|
||||||
|
|
||||||
this.programs.push({
|
this.programs.push({
|
||||||
program,
|
program,
|
||||||
values: combination,
|
values: combination,
|
||||||
});
|
});
|
||||||
|
|
||||||
return program;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getMacroDefinitions(
|
private getMacroDefinitions(
|
||||||
|
|
@ -97,13 +107,7 @@ export class UniformArrayAutoScalingProgram implements IProgram {
|
||||||
descriptors: Array<DrawableDescriptor>
|
descriptors: Array<DrawableDescriptor>
|
||||||
): string {
|
): string {
|
||||||
return combination
|
return combination
|
||||||
.map(
|
.map((v, i) => `#define ${descriptors[i].uniformCountMacroName} ${v}`)
|
||||||
(v, i) => `
|
|
||||||
#ifndef ${descriptors[i].uniformCountMacroName}
|
|
||||||
#define ${descriptors[i].uniformCountMacroName} ${v}
|
|
||||||
#endif
|
|
||||||
`
|
|
||||||
)
|
|
||||||
.join('\n');
|
.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,18 +10,18 @@ export class RenderingPass {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
gl: WebGL2RenderingContext,
|
gl: WebGL2RenderingContext,
|
||||||
shaderSources: [string, string],
|
|
||||||
drawableDescriptors: Array<DrawableDescriptor>,
|
|
||||||
private frame: FrameBuffer,
|
private frame: FrameBuffer,
|
||||||
substitutions: { [name: string]: any },
|
|
||||||
private tileMultiplier: number
|
private tileMultiplier: number
|
||||||
) {
|
) {
|
||||||
this.program = new UniformArrayAutoScalingProgram(
|
this.program = new UniformArrayAutoScalingProgram(gl);
|
||||||
gl,
|
}
|
||||||
shaderSources,
|
|
||||||
drawableDescriptors,
|
public async initialize(
|
||||||
substitutions
|
shaderSources: [string, string],
|
||||||
);
|
descriptors: Array<DrawableDescriptor>,
|
||||||
|
substitutions: { [name: string]: any }
|
||||||
|
): Promise<void> {
|
||||||
|
await this.program.initialize(shaderSources, descriptors, substitutions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public addDrawable(drawable: Drawable) {
|
public addDrawable(drawable: Drawable) {
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ float shadowTransparency(float startingDistance, float lightCenterDistance, vec2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CIRCLE_LIGHT_COUNT
|
||||||
#if CIRCLE_LIGHT_COUNT > 0
|
#if CIRCLE_LIGHT_COUNT > 0
|
||||||
uniform struct CircleLight {
|
uniform struct CircleLight {
|
||||||
vec2 center;
|
vec2 center;
|
||||||
|
|
@ -91,7 +92,9 @@ float shadowTransparency(float startingDistance, float lightCenterDistance, vec2
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef FLASHLIGHT_COUNT
|
||||||
#if FLASHLIGHT_COUNT > 0
|
#if FLASHLIGHT_COUNT > 0
|
||||||
uniform struct Flashlight {
|
uniform struct Flashlight {
|
||||||
vec2 center;
|
vec2 center;
|
||||||
|
|
@ -120,6 +123,7 @@ float shadowTransparency(float startingDistance, float lightCenterDistance, vec2
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
out vec4 fragmentColor;
|
out vec4 fragmentColor;
|
||||||
void main() {
|
void main() {
|
||||||
|
|
@ -129,20 +133,25 @@ void main() {
|
||||||
float startingDistance = getDistance(uvCoordinates, colorAtPosition);
|
float startingDistance = getDistance(uvCoordinates, colorAtPosition);
|
||||||
|
|
||||||
if (startingDistance < 0.0) {
|
if (startingDistance < 0.0) {
|
||||||
|
#ifdef CIRCLE_LIGHT_COUNT
|
||||||
#if CIRCLE_LIGHT_COUNT > 0
|
#if CIRCLE_LIGHT_COUNT > 0
|
||||||
for (int i = 0; i < CIRCLE_LIGHT_COUNT; i++) {
|
for (int i = 0; i < CIRCLE_LIGHT_COUNT; i++) {
|
||||||
lighting += colorInPositionInside(circleLights[i]);
|
lighting += colorInPositionInside(circleLights[i]);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef FLASHLIGHT_COUNT
|
||||||
#if FLASHLIGHT_COUNT > 0
|
#if FLASHLIGHT_COUNT > 0
|
||||||
for (int i = 0; i < FLASHLIGHT_COUNT; i++) {
|
for (int i = 0; i < FLASHLIGHT_COUNT; i++) {
|
||||||
lighting += colorInPositionInside(flashlights[i], normalize(flashlightDirections[i]));
|
lighting += colorInPositionInside(flashlights[i], normalize(flashlightDirections[i]));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
colorAtPosition = vec3(1.0);
|
colorAtPosition = vec3(1.0);
|
||||||
|
|
||||||
|
#ifdef CIRCLE_LIGHT_COUNT
|
||||||
#if CIRCLE_LIGHT_COUNT > 0
|
#if CIRCLE_LIGHT_COUNT > 0
|
||||||
for (int i = 0; i < CIRCLE_LIGHT_COUNT; i++) {
|
for (int i = 0; i < CIRCLE_LIGHT_COUNT; i++) {
|
||||||
vec2 direction = normalize(circleLightDirections[i]) / squareToAspectRatioTimes2;
|
vec2 direction = normalize(circleLightDirections[i]) / squareToAspectRatioTimes2;
|
||||||
|
|
@ -153,7 +162,9 @@ void main() {
|
||||||
lighting += lightColorAtPosition * shadowTransparency(startingDistance, lightCenterDistance, direction);
|
lighting += lightColorAtPosition * shadowTransparency(startingDistance, lightCenterDistance, direction);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef FLASHLIGHT_COUNT
|
||||||
#if FLASHLIGHT_COUNT > 0
|
#if FLASHLIGHT_COUNT > 0
|
||||||
for (int i = 0; i < FLASHLIGHT_COUNT; i++) {
|
for (int i = 0; i < FLASHLIGHT_COUNT; i++) {
|
||||||
vec2 originalDirection = normalize(flashlightDirections[i]);
|
vec2 originalDirection = normalize(flashlightDirections[i]);
|
||||||
|
|
@ -169,6 +180,7 @@ void main() {
|
||||||
lighting += lightColorAtPosition * shadowTransparency(startingDistance, lightCenterDistance, direction);
|
lighting += lightColorAtPosition * shadowTransparency(startingDistance, lightCenterDistance, direction);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
fragmentColor = vec4(colorAtPosition * lighting, 1.0);
|
fragmentColor = vec4(colorAtPosition * lighting, 1.0);
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ out vec2 uvCoordinates;
|
||||||
|
|
||||||
uniform vec2 squareToAspectRatio;
|
uniform vec2 squareToAspectRatio;
|
||||||
|
|
||||||
|
#ifdef CIRCLE_LIGHT_COUNT
|
||||||
#if CIRCLE_LIGHT_COUNT > 0
|
#if CIRCLE_LIGHT_COUNT > 0
|
||||||
uniform struct CircleLight {
|
uniform struct CircleLight {
|
||||||
vec2 center;
|
vec2 center;
|
||||||
|
|
@ -21,7 +22,9 @@ uniform vec2 squareToAspectRatio;
|
||||||
|
|
||||||
out vec2[CIRCLE_LIGHT_COUNT] circleLightDirections;
|
out vec2[CIRCLE_LIGHT_COUNT] circleLightDirections;
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef FLASHLIGHT_COUNT
|
||||||
#if FLASHLIGHT_COUNT > 0
|
#if FLASHLIGHT_COUNT > 0
|
||||||
uniform struct Flashlight {
|
uniform struct Flashlight {
|
||||||
vec2 center;
|
vec2 center;
|
||||||
|
|
@ -32,6 +35,7 @@ uniform vec2 squareToAspectRatio;
|
||||||
|
|
||||||
out vec2[FLASHLIGHT_COUNT] flashlightDirections;
|
out vec2[FLASHLIGHT_COUNT] flashlightDirections;
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec3 vertexPosition2D = vec3(vertexPosition.xy, 1.0) * modelTransform;
|
vec3 vertexPosition2D = vec3(vertexPosition.xy, 1.0) * modelTransform;
|
||||||
|
|
@ -44,15 +48,19 @@ void main() {
|
||||||
0.0, 0.0, 1.0
|
0.0, 0.0, 1.0
|
||||||
)).xy;
|
)).xy;
|
||||||
|
|
||||||
|
#ifdef CIRCLE_LIGHT_COUNT
|
||||||
#if CIRCLE_LIGHT_COUNT > 0
|
#if CIRCLE_LIGHT_COUNT > 0
|
||||||
for (int i = 0; i < CIRCLE_LIGHT_COUNT; i++) {
|
for (int i = 0; i < CIRCLE_LIGHT_COUNT; i++) {
|
||||||
circleLightDirections[i] = circleLights[i].center - position;
|
circleLightDirections[i] = circleLights[i].center - position;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef FLASHLIGHT_COUNT
|
||||||
#if FLASHLIGHT_COUNT > 0
|
#if FLASHLIGHT_COUNT > 0
|
||||||
for (int i = 0; i < FLASHLIGHT_COUNT; i++) {
|
for (int i = 0; i < FLASHLIGHT_COUNT; i++) {
|
||||||
flashlightDirections[i] = flashlights[i].center - position;
|
flashlightDirections[i] = flashlights[i].center - position;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
import { vec2, vec3 } from 'gl-matrix';
|
import { vec2, vec3 } from 'gl-matrix';
|
||||||
import { Drawable } from '../../drawables/drawable';
|
import { Drawable } from '../../drawables/drawable';
|
||||||
import { DrawableDescriptor } from '../../drawables/drawable-descriptor';
|
import { DrawableDescriptor } from '../../drawables/drawable-descriptor';
|
||||||
import { CircleLight } from '../../drawables/lights/circle-light';
|
|
||||||
import { Flashlight } from '../../drawables/lights/flashlight';
|
|
||||||
import { DefaultFrameBuffer } from '../graphics-library/frame-buffer/default-frame-buffer';
|
import { DefaultFrameBuffer } from '../graphics-library/frame-buffer/default-frame-buffer';
|
||||||
import { IntermediateFrameBuffer } from '../graphics-library/frame-buffer/intermediate-frame-buffer';
|
import { IntermediateFrameBuffer } from '../graphics-library/frame-buffer/intermediate-frame-buffer';
|
||||||
import { getWebGl2Context } from '../graphics-library/helper/get-webgl2-context';
|
import { getWebGl2Context } from '../graphics-library/helper/get-webgl2-context';
|
||||||
import { WebGlStopwatch } from '../graphics-library/helper/stopwatch';
|
import { WebGlStopwatch } from '../graphics-library/helper/stopwatch';
|
||||||
|
import { ParallelCompiler } from '../graphics-library/parallel-compiler';
|
||||||
import { FpsAutoscaler } from './fps-autoscaler';
|
import { FpsAutoscaler } from './fps-autoscaler';
|
||||||
import { Insights } from './insights';
|
import { Insights } from './insights';
|
||||||
import { Renderer } from './renderer';
|
import { Renderer } from './renderer';
|
||||||
|
|
@ -29,6 +28,7 @@ export class WebGl2Renderer implements Renderer {
|
||||||
private lightingFrameBuffer: DefaultFrameBuffer;
|
private lightingFrameBuffer: DefaultFrameBuffer;
|
||||||
private passes: Passes;
|
private passes: Passes;
|
||||||
private autoscaler: FpsAutoscaler;
|
private autoscaler: FpsAutoscaler;
|
||||||
|
private settings: WebGl2RendererSettings;
|
||||||
|
|
||||||
private static defaultSettings: WebGl2RendererSettings = {
|
private static defaultSettings: WebGl2RendererSettings = {
|
||||||
enableHighDpiRendering: true,
|
enableHighDpiRendering: true,
|
||||||
|
|
@ -42,24 +42,26 @@ export class WebGl2Renderer implements Renderer {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private canvas: HTMLCanvasElement,
|
private canvas: HTMLCanvasElement,
|
||||||
descriptors: Array<DrawableDescriptor>,
|
private descriptors: Array<DrawableDescriptor>,
|
||||||
palette: Array<vec3>,
|
private palette: Array<vec3>,
|
||||||
settingsOverrides: Partial<WebGl2RendererSettings>
|
settingsOverrides: Partial<WebGl2RendererSettings>
|
||||||
) {
|
) {
|
||||||
const settings = { ...WebGl2Renderer.defaultSettings, ...settingsOverrides };
|
this.settings = { ...WebGl2Renderer.defaultSettings, ...settingsOverrides };
|
||||||
|
|
||||||
this.gl = getWebGl2Context(canvas);
|
this.gl = getWebGl2Context(canvas);
|
||||||
|
|
||||||
|
ParallelCompiler.initialize(this.gl);
|
||||||
|
|
||||||
this.distanceFieldFrameBuffer = new IntermediateFrameBuffer(
|
this.distanceFieldFrameBuffer = new IntermediateFrameBuffer(
|
||||||
this.gl,
|
this.gl,
|
||||||
settings.enableHighDpiRendering
|
this.settings.enableHighDpiRendering
|
||||||
);
|
);
|
||||||
this.lightingFrameBuffer = new DefaultFrameBuffer(
|
this.lightingFrameBuffer = new DefaultFrameBuffer(
|
||||||
this.gl,
|
this.gl,
|
||||||
settings.enableHighDpiRendering
|
this.settings.enableHighDpiRendering
|
||||||
);
|
);
|
||||||
|
|
||||||
this.passes = this.createPasses(descriptors, palette, settings);
|
this.passes = this.createPasses();
|
||||||
|
|
||||||
this.uniformsProvider = new UniformsProvider(this.gl);
|
this.uniformsProvider = new UniformsProvider(this.gl);
|
||||||
|
|
||||||
|
|
@ -71,7 +73,7 @@ export class WebGl2Renderer implements Renderer {
|
||||||
(this.uniformsProvider.softShadowsEnabled = v as boolean),
|
(this.uniformsProvider.softShadowsEnabled = v as boolean),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (settings.enableStopwatch) {
|
if (this.settings.enableStopwatch) {
|
||||||
try {
|
try {
|
||||||
this.stopwatch = new WebGlStopwatch(this.gl);
|
this.stopwatch = new WebGlStopwatch(this.gl);
|
||||||
} catch {
|
} catch {
|
||||||
|
|
@ -81,40 +83,49 @@ export class WebGl2Renderer implements Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Insights.measure('create render passes')
|
@Insights.measure('create render passes')
|
||||||
private createPasses(
|
private createPasses(): Passes {
|
||||||
descriptors: Array<DrawableDescriptor>,
|
|
||||||
palette: Array<vec3>,
|
|
||||||
settings: WebGl2RendererSettings
|
|
||||||
): Passes {
|
|
||||||
const allDescriptors = [
|
|
||||||
...descriptors,
|
|
||||||
CircleLight.descriptor,
|
|
||||||
Flashlight.descriptor,
|
|
||||||
];
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
[RenderingPassName.distance]: new RenderingPass(
|
[RenderingPassName.distance]: new RenderingPass(
|
||||||
this.gl,
|
this.gl,
|
||||||
[distanceVertexShader, distanceFragmentShader],
|
|
||||||
allDescriptors.filter(WebGl2Renderer.hasSdf),
|
|
||||||
this.distanceFieldFrameBuffer,
|
this.distanceFieldFrameBuffer,
|
||||||
settings.shaderMacros,
|
this.settings.tileMultiplier
|
||||||
settings.tileMultiplier
|
|
||||||
),
|
),
|
||||||
[RenderingPassName.pixel]: new RenderingPass(
|
[RenderingPassName.pixel]: new RenderingPass(
|
||||||
this.gl,
|
this.gl,
|
||||||
[lightsVertexShader, lightsFragmentShader],
|
|
||||||
allDescriptors.filter((d) => !WebGl2Renderer.hasSdf(d)),
|
|
||||||
this.lightingFrameBuffer,
|
this.lightingFrameBuffer,
|
||||||
{
|
this.settings.tileMultiplier
|
||||||
palette: this.generatePaletteCode(palette),
|
|
||||||
...settings.shaderMacros,
|
|
||||||
},
|
|
||||||
settings.tileMultiplier
|
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Insights.measure('initialize render passes')
|
||||||
|
public async initialize(): Promise<void> {
|
||||||
|
const promises: Array<Promise<void>> = [];
|
||||||
|
|
||||||
|
promises.push(
|
||||||
|
this.passes[RenderingPassName.distance].initialize(
|
||||||
|
[distanceVertexShader, distanceFragmentShader],
|
||||||
|
this.descriptors.filter(WebGl2Renderer.hasSdf),
|
||||||
|
this.settings.shaderMacros
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
promises.push(
|
||||||
|
this.passes[RenderingPassName.pixel].initialize(
|
||||||
|
[lightsVertexShader, lightsFragmentShader],
|
||||||
|
this.descriptors.filter((d) => !WebGl2Renderer.hasSdf(d)),
|
||||||
|
{
|
||||||
|
palette: this.generatePaletteCode(this.palette),
|
||||||
|
...this.settings.shaderMacros,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
ParallelCompiler.compilePrograms();
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
public get insights(): any {
|
public get insights(): any {
|
||||||
return Insights.values;
|
return Insights.values;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
// src
|
|
||||||
// https://stackoverflow.com/questions/521295/seeding-the-random-number-generator-in-javascript
|
|
||||||
// Mulberry32
|
|
||||||
|
|
||||||
export abstract class Random {
|
|
||||||
private static _seed = Math.random();
|
|
||||||
|
|
||||||
public static set seed(value: number) {
|
|
||||||
Random._seed = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static getRandom(): number {
|
|
||||||
let t = (Random._seed += 0x6d2b79f5);
|
|
||||||
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
||||||
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
||||||
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
24
src/main.ts
24
src/main.ts
|
|
@ -1,4 +1,4 @@
|
||||||
import { glMatrix, vec3 } from 'gl-matrix';
|
import { vec3 } from 'gl-matrix';
|
||||||
import { DrawableDescriptor } from './drawables/drawable-descriptor';
|
import { DrawableDescriptor } from './drawables/drawable-descriptor';
|
||||||
import { Renderer } from './graphics/rendering/renderer';
|
import { Renderer } from './graphics/rendering/renderer';
|
||||||
import { WebGl2Renderer } from './graphics/rendering/webgl2-renderer';
|
import { WebGl2Renderer } from './graphics/rendering/webgl2-renderer';
|
||||||
|
|
@ -14,13 +14,27 @@ export { Tunnel } from './drawables/shapes/tunnel';
|
||||||
export { Renderer } from './graphics/rendering/renderer';
|
export { Renderer } from './graphics/rendering/renderer';
|
||||||
export { RenderingPassName } from './graphics/rendering/rendering-pass-name';
|
export { RenderingPassName } from './graphics/rendering/rendering-pass-name';
|
||||||
|
|
||||||
export const compile = (
|
declare global {
|
||||||
|
interface Array<T> {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Float32Array {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const compile = async (
|
||||||
canvas: HTMLCanvasElement,
|
canvas: HTMLCanvasElement,
|
||||||
descriptors: Array<DrawableDescriptor>,
|
descriptors: Array<DrawableDescriptor>,
|
||||||
palette: Array<vec3>,
|
palette: Array<vec3>,
|
||||||
settings: Partial<WebGl2RendererSettings> = {}
|
settings: Partial<WebGl2RendererSettings> = {}
|
||||||
): Renderer => {
|
): Promise<Renderer> => {
|
||||||
glMatrix.setMatrixArrayType(Array);
|
|
||||||
applyArrayPlugins();
|
applyArrayPlugins();
|
||||||
return new WebGl2Renderer(canvas, descriptors, palette, settings);
|
|
||||||
|
const renderer = new WebGl2Renderer(canvas, descriptors, palette, settings);
|
||||||
|
await renderer.initialize();
|
||||||
|
return renderer;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue