Skip to content

Commit

Permalink
Fix EBO modifying bound VAO when data is updated.
Browse files Browse the repository at this point in the history
  • Loading branch information
Lakuna committed Nov 29, 2024
1 parent 4ad7aa4 commit f885572
Show file tree
Hide file tree
Showing 9 changed files with 449 additions and 583 deletions.
902 changes: 409 additions & 493 deletions package-lock.json

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@lakuna/ugl",
"version": "23.3.1",
"version": "23.3.2",
"description": "A lightweight WebGL2 library.",
"keywords": [
"front-end"
Expand Down Expand Up @@ -45,13 +45,13 @@
"eslint": "^9.15.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-tsdoc": "^0.3.0",
"eslint-plugin-tsdoc": "^0.4.0",
"husky": "^9.1.7",
"lint-staged": "^15.2.10",
"prettier": "^3.3.3",
"typedoc": "^0.26.11",
"typescript": "^5.6.3",
"typescript-eslint": "^8.15.0"
"prettier": "^3.4.1",
"typedoc": "^0.27.1",
"typescript": "^5.7.2",
"typescript-eslint": "^8.16.0"
},
"exports": "./dist/index.js",
"typings": "./dist/index.d.ts",
Expand Down
10 changes: 1 addition & 9 deletions src/core/Framebuffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import Renderbuffer from "./Renderbuffer.js";
import Texture from "./textures/Texture.js";
import type Texture2d from "./textures/Texture2d.js";
import type TextureCubemap from "./textures/TextureCubemap.js";
import UnsupportedOperationError from "../utility/UnsupportedOperationError.js";
import getExtensionsForFramebufferAttachmentFormat from "../utility/internal/getExtensionsForFramebufferAttachmentFormat.js";
import getMipmapTargetForCubeFace from "../utility/internal/getMipmapTargetForCubeFace.js";
import getParameterForFramebufferTarget from "../utility/internal/getParameterForFramebufferTarget.js";
Expand Down Expand Up @@ -167,18 +166,11 @@ export default class Framebuffer extends ContextDependent {
* Create a framebuffer.
* @param context - The rendering context.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/createFramebuffer | createFramebuffer}
* @throws {@link UnsupportedOperationError} if a framebuffer cannot be created.
*/
public constructor(context: Context) {
super(context);

const framebuffer = this.gl.createFramebuffer();
if (framebuffer === null) {
throw new UnsupportedOperationError(
"The environment does not support framebuffers."
);
}
this.internal = framebuffer;
this.internal = this.gl.createFramebuffer();
this.targetCache = FramebufferTarget.FRAMEBUFFER;
this.attachmentsCache = [];
}
Expand Down
30 changes: 13 additions & 17 deletions src/core/Program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import ProgramLinkError from "../utility/ProgramLinkError.js";
import Shader from "./Shader.js";
import ShaderType from "../constants/ShaderType.js";
import type Uniform from "./variables/uniforms/Uniform.js";
import UnsupportedOperationError from "../utility/UnsupportedOperationError.js";
import Varying from "./variables/Varying.js";
import createAttribute from "./variables/attributes/createAttribute.js";
import createUniform from "./variables/uniforms/createUniform.js";
Expand Down Expand Up @@ -135,7 +134,6 @@ export default class Program extends ContextDependent {
* @param feedbackVaryings - The names of the varyings that should be tracked for transform feedback.
* @param feedbackMode - The mode to use when capturing transform feedback varyings.
* @throws {@link ProgramLinkError} if the shaders have different contexts, if either shader is not the correct type, or if there is an issue when linking the shader program.
* @throws {@link UnsupportedOperationError} if a shader program cannot be created.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/createProgram | createProgram}
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/attachShader | attachShader}
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/bindAttribLocation | bindAttribLocation}
Expand Down Expand Up @@ -169,42 +167,40 @@ export default class Program extends ContextDependent {
super(context);

// Create the shader program.
const program = this.gl.createProgram();
if (program === null) {
throw new UnsupportedOperationError(
"The environment does not support shader programs."
);
}
this.internal = program;
this.internal = this.gl.createProgram();

// Attach the vertex shader.
this.gl.attachShader(program, vertexShader.internal);
this.gl.attachShader(this.internal, vertexShader.internal);
this.vertexShader = vertexShader;

// Attach the fragment shader.
this.gl.attachShader(program, fragmentShader.internal);
this.gl.attachShader(this.internal, fragmentShader.internal);
this.fragmentShader = fragmentShader;

// Bind the attributes to their specified locations, if any.
if (attributeLocations) {
for (const [name, location] of attributeLocations) {
this.gl.bindAttribLocation(program, location, name);
this.gl.bindAttribLocation(this.internal, location, name);
}
}

// Specify the transform feedback varying names, if any.
this.gl.transformFeedbackVaryings(program, feedbackVaryings, feedbackMode);
this.gl.transformFeedbackVaryings(
this.internal,
feedbackVaryings,
feedbackMode
);

// Link the shader program.
this.gl.linkProgram(program);
this.gl.linkProgram(this.internal);
if (!this.linkStatus) {
throw new ProgramLinkError(this.infoLog ?? void 0);
}

// Create wrappers for every uniform.
const uniforms = new Map<string, Uniform>();
const uniformCount = this.gl.getProgramParameter(
program,
this.internal,
ACTIVE_UNIFORMS
) as number;
for (let i = 0; i < uniformCount; i++) {
Expand All @@ -216,7 +212,7 @@ export default class Program extends ContextDependent {
// Create wrappers for every attribute.
const attributes = new Map<string, Attribute>();
const attributeCount = this.gl.getProgramParameter(
program,
this.internal,
ACTIVE_ATTRIBUTES
) as number;
for (let i = 0; i < attributeCount; i++) {
Expand All @@ -228,7 +224,7 @@ export default class Program extends ContextDependent {
// Create wrappers for every transform feedback varying.
const varyings = new Map<string, Varying>();
const varyingCount = this.gl.getProgramParameter(
program,
this.internal,
TRANSFORM_FEEDBACK_VARYINGS
) as number;
for (let i = 0; i < varyingCount; i++) {
Expand Down
10 changes: 1 addition & 9 deletions src/core/Renderbuffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { RENDERBUFFER, RENDERBUFFER_BINDING } from "../constants/constants.js";
import Context from "./Context.js";
import ContextDependent from "./internal/ContextDependent.js";
import type RenderbufferFormat from "../constants/RenderbufferFormat.js";
import UnsupportedOperationError from "../utility/UnsupportedOperationError.js";
import getExtensionForRenderbufferFormat from "../utility/internal/getExtensionForRenderbufferFormat.js";

/**
Expand Down Expand Up @@ -95,7 +94,6 @@ export default class Renderbuffer extends ContextDependent {
* @param format - The format of the renderbuffer.
* @param width - The width of the renderbuffer.
* @param height - The height of the renderbuffer.
* @throws {@link UnsupportedOperationError} if a renderbuffer cannot be created.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/createRenderbuffer | createRenderbuffer}
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/renderbufferStorage | renderbufferStorage}
*/
Expand All @@ -107,13 +105,7 @@ export default class Renderbuffer extends ContextDependent {
) {
super(context);

const renderbuffer = this.gl.createRenderbuffer();
if (renderbuffer === null) {
throw new UnsupportedOperationError(
"The environment does not support renderbuffers."
);
}
this.internal = renderbuffer;
this.internal = this.gl.createRenderbuffer();

// Enable the extension that is required for the given format, if any.
const extension = getExtensionForRenderbufferFormat(format);
Expand Down
14 changes: 2 additions & 12 deletions src/core/VertexArray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import FramebufferTarget from "../constants/FramebufferTarget.js";
import Primitive from "../constants/Primitive.js";
import type Program from "./Program.js";
import type { UniformMap } from "../types/UniformMap.js";
import UnsupportedOperationError from "../utility/UnsupportedOperationError.js";
import { VERTEX_ARRAY_BINDING } from "../constants/constants.js";
import type VertexBuffer from "./buffers/VertexBuffer.js";
import getSizeOfDataType from "../utility/internal/getSizeOfDataType.js";
Expand Down Expand Up @@ -100,7 +99,6 @@ export default class VertexArray extends ContextDependent {
* @param program - The shader program associated with the VAO.
* @param attributes - The attributes to attach to the VAO.
* @param ebo - The element buffer object to attach to the VAO.
* @throws {@link UnsupportedOperationError} if a VAO cannot be created.
* @throws {@link BadValueError} if an attribute is passed `undefined` as a value or if an unknown attribute is specified.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext/createVertexArray | createVertexArray}
*/
Expand All @@ -112,13 +110,7 @@ export default class VertexArray extends ContextDependent {
super(program.context);
this.program = program;

const vao = this.gl.createVertexArray();
if (vao === null) {
throw new UnsupportedOperationError(
"The environment does not support VAOs."
);
}
this.internal = vao;
this.internal = this.gl.createVertexArray();

// Set the initial attribute values.
this.attributeCache = new Map<string, AttributeValue>();
Expand Down Expand Up @@ -200,8 +192,6 @@ export default class VertexArray extends ContextDependent {
return;
}

this.bind();

// Remove EBO.
if (!value) {
ElementBuffer.unbindGl(this.context, this.internal);
Expand All @@ -210,7 +200,7 @@ export default class VertexArray extends ContextDependent {
}

// Add or update EBO.
value.bind();
value.bind(this);
this.eboCache = value;
}

Expand Down
37 changes: 14 additions & 23 deletions src/core/buffers/Buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import BufferTarget from "../../constants/BufferTarget.js";
import BufferUsage from "../../constants/BufferUsage.js";
import type Context from "../Context.js";
import ContextDependent from "../internal/ContextDependent.js";
import type DataType from "../../constants/DataType.js";
import UnsupportedOperationError from "../../utility/UnsupportedOperationError.js";
import DataType from "../../constants/DataType.js";
import getDataTypeForTypedArray from "../../utility/internal/getDataTypeForTypedArray.js";

/**
Expand All @@ -20,7 +19,6 @@ export default abstract class Buffer extends ContextDependent {
* @param offset - The index of the element to start reading the buffer at.
* @param isHalf - Whether or not the data contains half floats if it contains floats.
* @param target - The target binding point of the buffer.
* @throws {@link UnsupportedOperationError} if a buffer cannot be created.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/createBuffer | createBuffer}
* @internal
*/
Expand All @@ -34,19 +32,19 @@ export default abstract class Buffer extends ContextDependent {
) {
super(context);

const buffer = this.gl.createBuffer();
if (buffer === null) {
throw new UnsupportedOperationError(
"The environment does not support buffers."
);
}

this.internal = buffer;
this.internal = this.gl.createBuffer();
this.targetCache = target;
this.usageCache = usage;
this.offsetCache = offset;
this.sizeCache = 0;
this.isHalfCache = isHalf;
if (typeof data === "number") {
this.sizeCache = data;
this.dataCache = new Uint8Array(data);
} else {
this.sizeCache = data.byteLength;
this.dataCache = data;
}

this.setData(data, usage, offset, isHalf);
}

Expand Down Expand Up @@ -102,15 +100,8 @@ export default abstract class Buffer extends ContextDependent {
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext/getBufferSubData | getBufferSubData}
*/
public get data(): ArrayBufferView {
// If the data cache isn't set, read the buffer.
if (!this.dataCache) {
this.bind();
this.dataCache = new Float32Array(this.size); // TODO: Don't assume `Float32Array` type.
this.gl.getBufferSubData(this.target, 0, this.dataCache, this.size); // TODO: "WebGL warning: `getBufferSubData`: Reading from a buffer with usage other than `*_READ` causes pipeline stalls. Copy through a `STREAM_READ` buffer."
// TODO: Add a `getData` method with more control over calling `getBufferSubData`.
}

return this.dataCache;
// TODO: If the data cache isn't set, read the buffer data with `getBufferSubData`. If the buffer's usage isn't a `READ` type, it must first be copied through a `STREAM_READ` buffer.
return (this.dataCache ??= new Uint8Array());
}

public set data(value) {
Expand Down Expand Up @@ -206,8 +197,8 @@ export default abstract class Buffer extends ContextDependent {
this.bind();
if (typeof data === "number") {
this.gl.bufferData(this.target, data, usage);
delete this.dataCache;
delete this.typeCache;
this.dataCache = new Uint8Array(data);
this.typeCache = DataType.UNSIGNED_BYTE;
this.sizeCache = data;
this.usageCache = usage;
this.isHalfCache = isHalf;
Expand Down
8 changes: 2 additions & 6 deletions src/core/buffers/ElementBuffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,11 @@ export default class ElementBuffer extends Buffer {

/**
* Bind this buffer to a VAO.
* @param vao - The new VAO to bind to. or `undefined` to bind to the currently-bound VAO.
* @param vao - The new VAO to bind to. or `undefined` to bind to the default VAO.
* @internal
*/
public override bind(vao?: VertexArray) {
ElementBuffer.bindGl(
this.context,
vao?.internal ?? VertexArray.getBound(this.context),
this.internal
);
ElementBuffer.bindGl(this.context, vao?.internal ?? null, this.internal);
}

/**
Expand Down
9 changes: 1 addition & 8 deletions src/core/textures/Texture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,6 @@ export default abstract class Texture extends ContextDependent {
* @param levels - The number of levels in the texture.
* @param format - The internal format of the texture.
* @param dims - The dimensions of the texture.
* @throws {@link UnsupportedOperationError} if a texture cannot be created.
* @throws {@link TextureFormatError} if the given format is unsized.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/createTexture | createTexture}
* @internal
Expand All @@ -382,13 +381,7 @@ export default abstract class Texture extends ContextDependent {
) {
super(context);

const texture = this.gl.createTexture();
if (texture === null) {
throw new UnsupportedOperationError(
"The environment does not support textures."
);
}
this.internal = texture;
this.internal = this.gl.createTexture();
this.target = target;
this.mipmaps = new Map();
this.dims = [];
Expand Down

0 comments on commit f885572

Please sign in to comment.