Skip to content

Commit

Permalink
fix: type safety for WebGL constants and preliminary blend mode (#61)
Browse files Browse the repository at this point in the history
  • Loading branch information
timkurvers authored Apr 18, 2024
1 parent baf3387 commit 6ee55b8
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 99 deletions.
7 changes: 3 additions & 4 deletions src/gfx/apis/webgl2/WebGL2Device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@ import Device from '../../Device';
import ShaderRegistry from '../../ShaderRegistry';
import { ShaderType } from '../../Shader';

// import constantsFor from './constants';

import constantsFor from './constants';

class WebGL2Device extends Device {
gl: WebGL2RenderingContext;
shaders: ShaderRegistry;
constants;

constructor(canvas: HTMLCanvasElement) {
super();

// TODO: Handle context loss
this.gl = canvas.getContext('webgl2')!;
// TODO: Constants
// this.constants = constantsFor(this.gl);
this.constants = constantsFor(this.gl);

this.shaders = new ShaderRegistry((type, name) => {
const ext = type === 'pixel' ? 'frag' : 'vert';
Expand Down
152 changes: 69 additions & 83 deletions src/gfx/apis/webgl2/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,98 +5,84 @@ import {
PrimitiveType,
} from '../../types';

const cubeMapFaces = {
0: 'TEXTURE_CUBE_MAP_POSITIVE_X',
1: 'TEXTURE_CUBE_MAP_NEGATIVE_X',
2: 'TEXTURE_CUBE_MAP_POSITIVE_Y',
3: 'TEXTURE_CUBE_MAP_NEGATIVE_Y',
4: 'TEXTURE_CUBE_MAP_POSITIVE_Z',
5: 'TEXTURE_CUBE_MAP_NEGATIVE_Z',
} as const;
import { PickByType } from '../../../utils';

const blendDestinations = {
[BlendMode.Opaque]: 'ZERO',
[BlendMode.AlphaKey]: 'ZERO',
[BlendMode.Alpha]: 'ONE_MINUS_SRC_ALPHA',
[BlendMode.Add]: 'ONE',
[BlendMode.Mod]: 'ZERO',
[BlendMode.Mod2x]: 'SRC_COLOR',
[BlendMode.ModAdd]: 'ONE',
[BlendMode.InvSrcAlphaAdd]: 'ONE',
[BlendMode.InvSrcAlphaOpaque]: 'ZERO',
[BlendMode.SrcAlphaOpaque]: 'ZERO',
[BlendMode.NoAlphaAdd]: 'ONE',
[BlendMode.ConstantAlpha]: 'ONE_MINUS_CONSTANT_ALPHA',
} as const;
export default (gl: WebGL2RenderingContext) => {
const resolve = (prop: keyof PickByType<WebGL2RenderingContext, number>): number => {
const constant = gl[prop];
if (constant === undefined) {
throw new Error(`Could not find WebGL2 constant: ${prop}`);
}
return constant;
};

const blendSources = {
[BlendMode.Opaque]: 'ONE',
[BlendMode.AlphaKey]: 'ONE',
[BlendMode.Alpha]: 'SRC_ALPHA',
[BlendMode.Add]: 'SRC_ALPHA',
[BlendMode.Mod]: 'DST_COLOR',
[BlendMode.Mod2x]: 'DST_COLOR',
[BlendMode.ModAdd]: 'DST_COLOR',
[BlendMode.InvSrcAlphaAdd]: 'ONE_MINUS_SRC_ALPHA',
[BlendMode.InvSrcAlphaOpaque]: 'ONE_MINUS_SRC_ALPHA',
[BlendMode.SrcAlphaOpaque]: 'SRC_ALPHA',
[BlendMode.NoAlphaAdd]: 'ONE',
[BlendMode.ConstantAlpha]: 'CONSTANT_ALPHA',
} as const;
const constants = {
cubeMapFaces: {
0: resolve('TEXTURE_CUBE_MAP_POSITIVE_X'),
1: resolve('TEXTURE_CUBE_MAP_NEGATIVE_X'),
2: resolve('TEXTURE_CUBE_MAP_POSITIVE_Y'),
3: resolve('TEXTURE_CUBE_MAP_NEGATIVE_Y'),
4: resolve('TEXTURE_CUBE_MAP_POSITIVE_Z'),
5: resolve('TEXTURE_CUBE_MAP_NEGATIVE_Z'),
},

// TODO: Texture format
blendDestinations: {
[BlendMode.Opaque]: resolve('ZERO'),
[BlendMode.AlphaKey]: resolve('ZERO'),
[BlendMode.Alpha]: resolve('ONE_MINUS_SRC_ALPHA'),
[BlendMode.Add]: resolve('ONE'),
[BlendMode.Mod]: resolve('ZERO'),
[BlendMode.Mod2x]: resolve('SRC_COLOR'),
[BlendMode.ModAdd]: resolve('ONE'),
[BlendMode.InvSrcAlphaAdd]: resolve('ONE'),
[BlendMode.InvSrcAlphaOpaque]: resolve('ZERO'),
[BlendMode.SrcAlphaOpaque]: resolve('ZERO'),
[BlendMode.NoAlphaAdd]: resolve('ONE'),
[BlendMode.ConstantAlpha]: resolve('ONE_MINUS_CONSTANT_ALPHA'),
},

const bufferFormatByPoolTarget = {
[PoolTarget.Vertex]: 'ZERO',
[PoolTarget.Index]: 'UNSIGNED_SHORT',
} as const;
blendSources: {
[BlendMode.Opaque]: resolve('ONE'),
[BlendMode.AlphaKey]: resolve('ONE'),
[BlendMode.Alpha]: resolve('SRC_ALPHA'),
[BlendMode.Add]: resolve('SRC_ALPHA'),
[BlendMode.Mod]: resolve('DST_COLOR'),
[BlendMode.Mod2x]: resolve('DST_COLOR'),
[BlendMode.ModAdd]: resolve('DST_COLOR'),
[BlendMode.InvSrcAlphaAdd]: resolve('ONE_MINUS_SRC_ALPHA'),
[BlendMode.InvSrcAlphaOpaque]: resolve('ONE_MINUS_SRC_ALPHA'),
[BlendMode.SrcAlphaOpaque]: resolve('SRC_ALPHA'),
[BlendMode.NoAlphaAdd]: resolve('ONE'),
[BlendMode.ConstantAlpha]: resolve('CONSTANT_ALPHA'),
},

const bufferTypeByPooltarget = {
[PoolTarget.Vertex]: 'ARRAY_BUFFER',
[PoolTarget.Index]: 'ELEMENT_ARRAY_BUFFER',
} as const;
// TODO: Texture format

const bufferUsageByPoolTarget = {
[PoolUsage.Static]: 'STATIC_DRAW',
[PoolUsage.Dynamic]: 'DYNAMIC_DRAW',
[PoolUsage.Stream]: 'DYNAMIC_DRAW',
} as const;
bufferFormatByPoolTarget: {
[PoolTarget.Vertex]: resolve('ZERO'),
[PoolTarget.Index]: resolve('UNSIGNED_SHORT'),
},

const primitiveTypes = {
[PrimitiveType.Points]: 'POINTS',
[PrimitiveType.Lines]: 'LINES',
[PrimitiveType.LineStrip]: 'LINE_STRIP',
[PrimitiveType.Triangles]: 'TRIANGLES',
[PrimitiveType.TriangleStrip]: 'TRIANGLE_STRIP',
[PrimitiveType.TriangleFan]: 'TRIANGLE_FAN',
} as const;
bufferTypeByPooltarget: {
[PoolTarget.Vertex]: resolve('ARRAY_BUFFER'),
[PoolTarget.Index]: resolve('ELEMENT_ARRAY_BUFFER'),
},

export default (gl: WebGL2RenderingContext) => {
const constants = {};
bufferUsageByPoolTarget: {
[PoolUsage.Static]: resolve('STATIC_DRAW'),
[PoolUsage.Dynamic]: resolve('DYNAMIC_DRAW'),
[PoolUsage.Stream]: resolve('DYNAMIC_DRAW'),
},

const categories = {
cubeMapFaces,
blendDestinations,
blendSources,
bufferFormatByPoolTarget,
bufferTypeByPooltarget,
bufferUsageByPoolTarget,
primitiveTypes,
} as const;

for (const [name, category] of Object.entries(categories)) {
const entry = {};
for (const [index, prop] of Object.entries(category)) {
const constant = gl[prop];
if (constant === undefined) {
throw new Error(`Could not find WebGL2 constant: ${prop}`);
}
// @ts-expect-error: currently unused (and untyped)
entry[index] = constant;
}
// @ts-expect-error: currently unused (and untyped)
constants[name] = entry;
}
primitiveTypes: {
[PrimitiveType.Points]: resolve('POINTS'),
[PrimitiveType.Lines]: resolve('LINES'),
[PrimitiveType.LineStrip]: resolve('LINE_STRIP'),
[PrimitiveType.Triangles]: resolve('TRIANGLES'),
[PrimitiveType.TriangleStrip]: resolve('TRIANGLE_STRIP'),
[PrimitiveType.TriangleFan]: resolve('TRIANGLE_FAN'),
},
};

return constants;
};
6 changes: 0 additions & 6 deletions src/gfx/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export enum BlendMode {
SrcAlphaOpaque = 9,
NoAlphaAdd = 10,
ConstantAlpha = 11,
Last = 12,
}

export enum PoolHintBit {
Expand All @@ -26,14 +25,12 @@ export enum PoolHintBit {
export enum PoolTarget {
Vertex = 0,
Index = 1,
Last = 2,
}

export enum PoolUsage {
Static = 0,
Dynamic = 1,
Stream = 2,
Last = 3,
}

export enum PrimitiveType {
Expand All @@ -43,7 +40,6 @@ export enum PrimitiveType {
Triangles = 3,
TriangleStrip = 4,
TriangleFan = 5,
Last = 6,
}

export enum RenderStateType {
Expand Down Expand Up @@ -133,7 +129,6 @@ export enum RenderStateType {
PointSprite = 83,
Unk84 = 84,
ColorMaterial = 85,
Last = 86,
}

export enum TextureFilter {
Expand Down Expand Up @@ -192,5 +187,4 @@ export enum VertexBufferFormat {
PT2 = 11,
PBNT2 = 12,
PNC2T2 = 13,
Last = 14,
}
7 changes: 2 additions & 5 deletions src/ui/components/abstract/LayoutFrame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
NDCtoDDCHeight,
NDCtoDDCWidth,
Status,
enumRecordFor,
extractDimensionsFrom,
stringToBoolean,
} from '../../../utils';
Expand Down Expand Up @@ -82,11 +83,7 @@ class LayoutFrame {
this.resizeList = LinkedList.using('link');
this.resizeCounter = 0;

this.points = [
null, null, null,
null, null, null,
null, null, null,
];
this.points = enumRecordFor(FramePointType, (_type) => null);
}

// Note: LayoutFrame is used as an auxiliary baseclass using the `multipleClasses` utility, so creating this
Expand Down
10 changes: 9 additions & 1 deletion src/ui/rendering/Renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class Renderer {

draw(batch: RenderBatch) {
const root = UIRoot.instance;
const { gl } = (Device.instance as WebGL2Device);
const { constants, gl } = (Device.instance as WebGL2Device);
const { pixelShader, vertexShader } = this;

if (!this.program) {
Expand All @@ -49,6 +49,14 @@ class Renderer {
for (const mesh of batch.meshes) {
const { image } = mesh.texture;

// TODO: Is this correct?
if (mesh.blendMode) {
gl.enable(gl.BLEND);
gl.blendFunc(constants.blendSources[mesh.blendMode], constants.blendDestinations[mesh.blendMode]);
} else {
gl.disable(gl.BLEND);
}

const texture = gl.createTexture();
gl.activeTexture(gl.TEXTURE0 + 0);
gl.bindTexture(gl.TEXTURE_2D, texture);
Expand Down
5 changes: 5 additions & 0 deletions src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ export function enumSizeFor<T extends string | number>(enumeration: EnumType<T>)
return enumValuesFor(enumeration).length;
}

// See: https://stackoverflow.com/a/69756175
export type PickByType<T, Value> = {
[P in keyof T as T[P] extends Value | undefined ? P : never]: T[P]
}

// See: https://github.com/microsoft/TypeScript/issues/5863#issuecomment-1336204919
export type ThisConstructor<
T extends { prototype: unknown } = { prototype: unknown },
Expand Down

0 comments on commit 6ee55b8

Please sign in to comment.