Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implementation is pretty much complete #1

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
138 changes: 138 additions & 0 deletions examples/image.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { WebGLCanvas } from "../src/webgl/mod.ts";
import { _uniformLocation } from "../src/webgl/object.ts";

const window = new WebGLCanvas({
title: "Deno DWM + Native WebGL",
width: 1024,
height: 1024,
resizable: true,
});

const glsl = (strings: TemplateStringsArray, ...values: []) => String.raw({ raw: strings }, ...values);
const gl = window.getContext("webgl");

addEventListener("resize", (event) => {
gl.viewport(0, 0, event.width, event.height);
});

let vertBuffer: WebGLBuffer, shaderProg: WebGLShader, shaderVertPosAttr: number;

export async function loadImageArray(src: string) {
const image = new Image();
await new Promise(resolve => {
image.onload = resolve;
image.src = src;
});
return image.rawData;
}

function checkShaderCompile(shader: WebGLShader, type: number) {
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
const errmes = type === gl.VERTEX_SHADER ? 'vertex' : 'fragment' + " shader compile failed: " +
gl.getShaderInfoLog(shader);
throw new Error(errmes);
}
}

function initShader(shaderSource: string, type: number) {
const shader = gl.createShader(type);
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);
checkShaderCompile(shader, type);
return shader;
}

function initProgram(vertexShader: WebGLShader, fragmentShader: WebGLShader) {
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
return program;
}

function init() {
vertBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);

const vertices = [
-1.0, 1.0,
1.0, -1.0,
-1.0, -1.0,
-1.0, 1.0,
1.0, 1.0,
1.0, -1.0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

const vertShaderSource = glsl`
attribute vec2 vertPos;
varying vec2 imagePos;
void main(void) {
gl_Position = vec4(vertPos.xy, 0.0, 1.0);
imagePos = vertPos;
}`;
const vertShader = initShader(vertShaderSource, gl.VERTEX_SHADER);

const fragShaderSource = glsl`
precision mediump float;
varying vec2 imagePos;
uniform sampler2D imageTex;

void main(void) {
gl_FragColor = texture2D(imageTex, imagePos / 2.0 + 0.5);
}`;
const fragShader = initShader(fragShaderSource, gl.FRAGMENT_SHADER);

shaderProg = initProgram(vertShader, fragShader);

shaderVertPosAttr = gl.getAttribLocation(shaderProg, "vertPos");
gl.enableVertexAttribArray(shaderVertPosAttr);

const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);

gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([
255, 255, 0, 255,
255, 0, 0, 255,
0, 255, 0, 255,
0, 0, 255, 255,
]));
gl.useProgram(shaderProg);
}

function draw() {
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
gl.vertexAttribPointer(shaderVertPosAttr, 2, gl.FLOAT, false, 0, 0);

gl.drawArrays(gl.TRIANGLES, 0, 6);
}

function frame() {
draw();
requestAnimationFrame(frame);
}

const imageWorker = new Worker(new URL('./worker.ts', import.meta.url).href, { type: 'module' });
imageWorker.onmessage = (msg: MessageEvent<ArrayBufferView>) => {
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2048, 2048, 0, gl.RGBA, gl.UNSIGNED_BYTE, msg.data);
};

// loadImageArray("https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/Red_Circle(small).svg/2048px-Red_Circle(small).svg.png")
// .then(array => {
// gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2048, 2048, 0, gl.RGBA, gl.UNSIGNED_BYTE, array);
// });

init();
requestAnimationFrame(frame);


await window.run();
15 changes: 15 additions & 0 deletions examples/worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// deno-lint-ignore-file
import { Image } from '../src/dom/image.ts';
// @ts-ignore
declare const self = { postMessage: unknown, close: unknown };

async function loadImageArray(src: string) {
const image = new Image();
await new Promise(resolve => {
image.onload = resolve;
image.src = src;
});
return image.rawData;
}

self.postMessage(await loadImageArray("https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/Red_Circle(small).svg/2048px-Red_Circle(small).svg.png"));
48 changes: 48 additions & 0 deletions src/webgl/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,50 @@ export class WebGLUniformLocation {
}
}

export class WebGLActiveInfo {
#size: number;
#type: number;
#name: string;

constructor(size: number, type: number, name: string) {
this.#size = size;
this.#type = type;
this.#name = name;
}

get size() {
return this.#size;
}
get type() {
return this.#type;
}
get name() {
return this.#name;
}
}

export class WebGLShaderPrecisionFormat {
#rangeMin: number;
#rangeMax: number;
#precision: number;

constructor(rangeMin: number, rangeMax: number, precision: number) {
this.#rangeMin = rangeMin;
this.#rangeMax = rangeMax;
this.#precision = precision;
}

get rangeMin() {
return this.#rangeMin;
}
get rangeMax() {
return this.#rangeMax;
}
get precision() {
return this.#precision;
}
}

Object.assign(globalThis, {
WebGLBuffer,
WebGLShader,
Expand All @@ -82,6 +126,8 @@ Object.assign(globalThis, {
});

const WebGLUniformLocationAlias = WebGLUniformLocation;
const WebGLActiveInfoAlias = WebGLActiveInfo;
const WebGLShaderPrecisionFormatAlias = WebGLShaderPrecisionFormat;

declare global {
class WebGLBuffer extends WebGLObject {}
Expand All @@ -91,4 +137,6 @@ declare global {
class WebGLFramebuffer extends WebGLObject {}
class WebGLRenderbuffer extends WebGLObject {}
class WebGLUniformLocation extends WebGLUniformLocationAlias {}
class WebGLActiveInfo extends WebGLActiveInfoAlias {}
class WebGLShaderPrecisionFormat extends WebGLShaderPrecisionFormatAlias {}
}
85 changes: 61 additions & 24 deletions src/webgl/renderingContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

import { DwmWindow, getProcAddress } from "./deps.ts";
import * as gl from "../../api/gles22.ts";
import { _uniformLocation, glObjectName } from "./object.ts";
import {
_uniformLocation,
glObjectName,
WebGLActiveInfo,
WebGLShaderPrecisionFormat,
} from "./object.ts";
import { cstr } from "./utils.ts";

export interface WebGLContextAttributes {
Expand Down Expand Up @@ -370,14 +375,19 @@ export class WebGLRenderingContext {
gl.BufferData(target, size, new Uint8Array(size), usage);
}

#bufferData2(target: number, data: ArrayBufferView, usage: number) {
gl.BufferData(target, data.byteLength, new Uint8Array(data.buffer), usage);
#bufferData2(target: number, data: BufferSource, usage: number) {
gl.BufferData(
target,
data.byteLength,
data instanceof ArrayBuffer ? data : new Uint8Array(data.buffer),
usage,
);
}

/**
* Initializes and creates the buffer object's data store.
*/
bufferData(target: number, data: ArrayBufferView | number, usage: number) {
bufferData(target: number, data: BufferSource | number, usage: number) {
if (typeof data === "number") {
this.#bufferData1(target, data, usage);
} else {
Expand Down Expand Up @@ -532,6 +542,14 @@ export class WebGLRenderingContext {
gl.TexParameteri(target, pname, param);
}

texImage2D(
target: number,
level: number,
internalformat: number,
format: number,
type: number,
pixels: Image,
): void;
texImage2D(
target: number,
level: number,
Expand All @@ -542,10 +560,20 @@ export class WebGLRenderingContext {
format: number,
type: number,
pixels: ArrayBufferView | null,
): void;
texImage2D(
target: number,
level: number,
internalformat: number,
width: number,
height: number,
border: number | Image,
format?: number,
type?: number,
pixels?: ArrayBufferView | null,
) {
if (arguments.length === 6) {
// deno-lint-ignore no-explicit-any
const img = border as any;
const img = border as unknown as Image;
gl.TexImage2D(
target,
level,
Expand All @@ -564,9 +592,9 @@ export class WebGLRenderingContext {
internalformat,
width,
height,
border,
format,
type,
border as number,
format!,
type!,
pixels?.buffer ? Deno.UnsafePointer.of(pixels.buffer) : 0,
);
}
Expand Down Expand Up @@ -643,11 +671,11 @@ export class WebGLRenderingContext {
const range = new Int32Array(2);
const precision = new Int32Array(1);
gl.GetShaderPrecisionFormat(shaderType, precisionType, range, precision);
return {
rangeMin: range[0],
rangeMax: range[1],
precision,
};
return new WebGLShaderPrecisionFormat(
range[0],
range[1],
precision[0],
);
}

getProgramInfoLog(program: WebGLProgram) {
Expand Down Expand Up @@ -743,6 +771,7 @@ export class WebGLRenderingContext {
/**
* Sets the specified {@link WebGLProgram} as part of the current rendering state.
*/
/// @ts-ignore-error
useProgram(program: WebGLProgram | null) {
gl.UseProgram(program?.[glObjectName] ?? 0);
}
Expand Down Expand Up @@ -774,11 +803,11 @@ export class WebGLRenderingContext {
type,
name,
);
return {
name: new TextDecoder().decode(name.subarray(0, length[0])),
size: size[0],
type: type[0],
};
return new WebGLActiveInfo(
size[0],
type[0],
new TextDecoder().decode(name.subarray(0, length[0])),
);
}

getActiveUniform(program: WebGLProgram, index: number) {
Expand All @@ -795,11 +824,11 @@ export class WebGLRenderingContext {
type,
name,
);
return {
name: new TextDecoder().decode(name.subarray(0, length[0])),
size: size[0],
type: type[0],
};
return new WebGLActiveInfo(
size[0],
type[0],
new TextDecoder().decode(name.subarray(0, length[0])),
);
}

/**
Expand All @@ -809,6 +838,14 @@ export class WebGLRenderingContext {
return gl.GetAttribLocation(program[glObjectName], cstr(name));
}

getUniform(program: WebGLProgram, name: string) {
// const params = new ArrayBuffer();
// gl.GetUniform(program[glObjectName], cstr(name));
// return params;
console.log("STUB: getUniform:", program[glObjectName], name);
return null;
}

getUniformLocation(program: WebGLProgram, name: string) {
const location = gl.GetUniformLocation(program[glObjectName], cstr(name));
return location < 0 ? null : new WebGLUniformLocation(location);
Expand Down