Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/assets/assetLoaderTypes/AssetLoaderTypeMesh.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class AssetLoaderTypeMesh extends AssetLoaderType {
assertInstanceType: VertexState,
},
});
mesh.setVertexState(vertexState);
mesh.setVertexState(vertexState, { deleteUnusedBuffers: true });
}

const indexFormat = decomposer.getUint8();
Expand Down
31 changes: 24 additions & 7 deletions src/core/Mesh.js
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ export class Mesh {
/**
* @param {InternalMeshAttributeBuffer} attributeBuffer
*/
copyAttributeBufferData(attributeBuffer) {
copyAttributeBufferData(attributeBuffer, allowUnusedBufferCreation = true) {
// todo: there's probably still some performance that can be gained here
// currently it's decomposing the buffer into vectors and turning
// it back into a buffer, if the buffer doesn't need to be changed it
Expand All @@ -355,12 +355,24 @@ export class Mesh {
// TODO: handle converting attribute data when the attribute type is not specified
continue;
}
const array = Array.from(attributeBuffer.getVertexData(attribute.attributeType));
const castArray = /** @type {number[] | import("../math/Vec2.js").Vec2[] | import("../math/Vec3.js").Vec3[]} */ (array);
this.setVertexData(attribute.attributeType, castArray, {

/** @type {UnusedAttributeBufferOptions<any>} */
const opts = {
unusedFormat: attribute.format,
unusedComponentCount: attribute.componentCount,
});
};

if (!allowUnusedBufferCreation) {
const existingBuffer = this.#getInternalAttributeBuffer(attribute.attributeType, {
...opts,
createUnused: false,
});
if (!existingBuffer) continue;
}

const array = Array.from(attributeBuffer.getVertexData(attribute.attributeType));
const castArray = /** @type {number[] | import("../math/Vec2.js").Vec2[] | import("../math/Vec3.js").Vec3[]} */ (array);
this.setVertexData(attribute.attributeType, castArray, opts);
}
}

Expand Down Expand Up @@ -453,8 +465,13 @@ export class Mesh {
* This means that old references obtained via {@linkcode getAttributeBufferForType}
* will no longer be part of this mesh. Only when the format stays the same, will old references be reused.
* @param {import("../rendering/VertexState.js").VertexState?} vertexState
* @param {object} [options]
* @param {boolean} [options.deleteUnusedBuffers] Defaults to true, deletes all old buffers that are not present in
* the provided vertexState.
*/
setVertexState(vertexState) {
setVertexState(vertexState, {
deleteUnusedBuffers = true,
} = {}) {
if (vertexState == this.#vertexState) return;
this.#vertexState = vertexState;

Expand Down Expand Up @@ -504,7 +521,7 @@ export class Mesh {
}

for (const buffer of buffersNeedingCopy) {
this.copyAttributeBufferData(buffer);
this.copyAttributeBufferData(buffer, !deleteUnusedBuffers);
}
}

Expand Down
75 changes: 74 additions & 1 deletion test/unit/src/core/Mesh/Mesh.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { assertEquals, assertExists, assertNotStrictEquals, assertStrictEquals, assertThrows } from "std/testing/asserts.ts";
import { Mesh, Vec2, Vec3, Vec4 } from "../../../../../src/mod.js";
import { FakeVertexState, mockVertexStateColor, mockVertexStateSingleAttribute, mockVertexStateSingleAttributeSameFormat, mockVertexStateTwoAttributes, mockVertexStateUv } from "./shared.js";
import { FakeVertexState, mockVertexStateColor, mockVertexStateSingleAttribute, mockVertexStateSingleAttributeSameFormat, mockVertexStateTwoAttributes, mockVertexStateTwoBuffers, mockVertexStateUv } from "./shared.js";
import { assertVecAlmostEquals } from "../../../../../src/util/asserts.js";

Deno.test({
Expand Down Expand Up @@ -543,6 +543,79 @@ Deno.test({
assertEquals(result2.isUnused, true);
assertEquals(Array.from(mesh.getAttributeBuffers()).length, 2);

mesh.setVertexState(mockVertexStateTwoAttributes, { deleteUnusedBuffers: false });

assertEquals(Array.from(mesh.getAttributeBuffers()).length, 1);

const positions = Array.from(mesh.getVertexData(Mesh.AttributeType.POSITION));
assertEquals(positions.length, 2);
assertVecAlmostEquals(positions[0], [1, 2, 3]);
assertVecAlmostEquals(positions[1], [4, 5, 6]);

const normals = Array.from(mesh.getVertexData(Mesh.AttributeType.NORMAL));
assertEquals(normals.length, 2);
assertVecAlmostEquals(normals[0], [1, 2, 3]);
assertVecAlmostEquals(normals[1], [4, 5, 6]);
},
});

Deno.test({
name: "setVertexState() deletes old unused buffers by default.",
fn() {
const mesh = new Mesh();
mesh.setVertexState(mockVertexStateTwoBuffers);
mesh.setVertexCount(2);
mesh.setVertexData(Mesh.AttributeType.POSITION, [
new Vec3(1, 2, 3),
new Vec3(4, 5, 6),
]);
mesh.setVertexData(Mesh.AttributeType.NORMAL, [
new Vec3(1, 2, 3),
new Vec3(4, 5, 6),
]);

const result1 = mesh.getAttributeBufferForType(Mesh.AttributeType.POSITION);
assertExists(result1);
assertEquals(result1.isUnused, false);
const result2 = mesh.getAttributeBufferForType(Mesh.AttributeType.NORMAL);
assertExists(result2);
assertEquals(result2.isUnused, false);
assertEquals(Array.from(mesh.getAttributeBuffers()).length, 2);

mesh.setVertexState(mockVertexStateSingleAttribute);

assertEquals(Array.from(mesh.getAttributeBuffers()).length, 1);

const positions = Array.from(mesh.getVertexData(Mesh.AttributeType.POSITION));
assertEquals(positions.length, 2);
assertVecAlmostEquals(positions[0], [1, 2, 3]);
assertVecAlmostEquals(positions[1], [4, 5, 6]);
},
});

Deno.test({
name: "setVertexState() converts two separate buffers into a single interleaved one.",
fn() {
const mesh = new Mesh();
mesh.setVertexState(mockVertexStateTwoBuffers);
mesh.setVertexCount(2);
mesh.setVertexData(Mesh.AttributeType.POSITION, [
new Vec3(1, 2, 3),
new Vec3(4, 5, 6),
]);
mesh.setVertexData(Mesh.AttributeType.NORMAL, [
new Vec3(1, 2, 3),
new Vec3(4, 5, 6),
]);

const result1 = mesh.getAttributeBufferForType(Mesh.AttributeType.POSITION);
assertExists(result1);
assertEquals(result1.isUnused, false);
const result2 = mesh.getAttributeBufferForType(Mesh.AttributeType.NORMAL);
assertExists(result2);
assertEquals(result2.isUnused, false);
assertEquals(Array.from(mesh.getAttributeBuffers()).length, 2);

mesh.setVertexState(mockVertexStateTwoAttributes);

assertEquals(Array.from(mesh.getAttributeBuffers()).length, 1);
Expand Down
29 changes: 29 additions & 0 deletions test/unit/src/core/Mesh/shared.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,32 @@ export const mockVertexStateColor = /** @type {import("../../../../../src/mod.js
]),
},
]));

export const mockVertexStateTwoBuffers = /** @type {import("../../../../../src/mod.js").VertexState} */ (new FakeVertexState([
{
attributes: new Map([
[
Mesh.AttributeType.POSITION,
{
attributeType: Mesh.AttributeType.POSITION,
offset: 0,
format: Mesh.AttributeFormat.FLOAT32,
componentCount: 3,
},
],
]),
},
{
attributes: new Map([
[
Mesh.AttributeType.NORMAL,
{
attributeType: Mesh.AttributeType.NORMAL,
offset: 12,
format: Mesh.AttributeFormat.FLOAT32,
componentCount: 3,
},
],
]),
},
]));
Loading