Add parallel compiler

This commit is contained in:
schmelczerandras 2020-09-17 16:04:09 +02:00
parent adbd933955
commit 36c64554f5
14 changed files with 260 additions and 181 deletions

View file

@ -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)!);
}
};

View file

@ -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)!);
}
};

View file

@ -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;
};

View file

@ -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;
};

View 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)!);
}
}
}

View file

@ -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');
} }

View file

@ -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

View file

@ -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');
} }

View file

@ -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) {

View file

@ -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);

View file

@ -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
} }

View file

@ -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;
} }

View file

@ -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;
}
}

View file

@ -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;
}; };