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

WIP implementation of UniformBuffer and BindGroup #4258

Merged
merged 3 commits into from
May 24, 2022
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
73 changes: 73 additions & 0 deletions src/graphics/bind-group-format.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/** @typedef {import('./graphics-device.js').GraphicsDevice} GraphicsDevice */

/**
* @ignore
*/
class BindBufferFormat {
constructor(name, visibility) {
/** @type {string} */
this.name = name;

// SHADER_STAGE_VERTEX, SHADER_STAGE_FRAGMENT, SHADER_STAGE_COMPUTE
this.visibility = visibility;
}
}

/**
* @ignore
*/
class BindTextureFormat {
constructor(name, visibility) {
/** @type {string} */
this.name = name;

// SHADER_STAGE_VERTEX, SHADER_STAGE_FRAGMENT, SHADER_STAGE_COMPUTE
this.visibility = visibility;
}
}

/**
* @ignore
*/
class BindGroupFormat {
/**
* @param {GraphicsDevice} graphicsDevice - The graphics device used to manage this vertex format.
* @param {BindBufferFormat[]} bufferFormats -
* @param {BindTextureFormat[]} textureFormats -
*/
constructor(graphicsDevice, bufferFormats, textureFormats) {
/** @type {GraphicsDevice} */
this.device = graphicsDevice;

/** @type {BindBufferFormat[]} */
this.bufferFormats = bufferFormats;

// maps a buffer format name to an index
/** @type Map<string, number> */
this.bufferFormatsMap = new Map();
bufferFormats.forEach((bf, i) => this.bufferFormatsMap.set(bf.name, i));

/** @type {BindTextureFormat[]} */
this.textureFormats = textureFormats;

// maps a texture format name to an index
/** @type Map<string, number> */
this.textureFormatsMap = new Map();
textureFormats.forEach((tf, i) => this.textureFormatsMap.set(tf.name, i));

this.impl = graphicsDevice.createBindGroupFormatImpl(this);
}

/**
* Frees resources associated with this bind group.
*/
destroy() {
this.impl.destroy();
}

loseContext() {
// TODO: implement
}
}

export { BindBufferFormat, BindTextureFormat, BindGroupFormat };
78 changes: 78 additions & 0 deletions src/graphics/bind-group.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/** @typedef {import('./graphics-device.js').GraphicsDevice} GraphicsDevice */
/** @typedef {import('./texture.js').Texture} Texture */
/** @typedef {import('./bind-group-format.js').BindGroupFormat} BindGroupFormat */

import { Debug } from '../core/debug.js';

/**
* A bind group represents an collection of {@link UniformBuffer} and {@link Texture} instance,
* which can be bind on a GPU for rendering.
*
* @ignore
*/
class BindGroup {
/**
* Create a new Bind Group.
*
* @param {GraphicsDevice} graphicsDevice - The graphics device used to manage this uniform buffer.
* @param {BindGroupFormat} format - Format of the bind group.
*/
constructor(graphicsDevice, format) {
this.device = graphicsDevice;
this.format = format;
this.dirty = true;
this.impl = graphicsDevice.createBindGroupImpl(this);

this.textures = [];
this.uniformBuffers = [];
}

/**
* Assign a uniform buffer to a slot.
*
* @param {*} name - The name of the uniform buffer slot
* @param {*} uniformBuffer - The Uniform buffer to assign to the slot.
*/
setUniformBuffer(name, uniformBuffer) {
const index = this.format.bufferFormatsMap.get(name);
Debug.assert(index !== undefined, `Setting a uniform [${name}] on a bind group which does not contain in.`);
if (this.uniformBuffers[index] !== uniformBuffer) {
this.uniformBuffers[index] = uniformBuffer;
this.dirty = true;
}
}

/**
* Assign a texture to a slot.
*
* @param {string} name - The name of the texture slot.
* @param {Texture} texture - Texture to assign to the slot.
*/
setTexture(name, texture) {
const index = this.format.textureFormatsMap.get(name);
Debug.assert(index !== undefined, `Setting a texture [${name}] on a bind group which does not contain in.`);
if (this.textures[index] !== texture) {
this.textures[index] = texture;
this.dirty = true;
}
}

/**
* Frees resources associated with this bind group.
*/
destroy() {
this.impl.destroy();
}

/**
* Applies any changes made to the bind group's properties.
*/
update() {
if (this.dirty) {
this.dirty = false;
this.impl.update(this);
}
}
}

export { BindGroup };
9 changes: 9 additions & 0 deletions src/graphics/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,15 @@ export const UNIFORMTYPE_VEC2ARRAY = 21;
export const UNIFORMTYPE_VEC3ARRAY = 22;
export const UNIFORMTYPE_VEC4ARRAY = 23;

// (bit-flags) shader stages for resource visibility on the GPU
export const SHADERSTAGE_VERTEX = 1;
export const SHADERSTAGE_FRAGMENT = 2;
export const SHADERSTAGE_COMPUTE = 4;

// indices of commonly used bind groups
export const BINDGROUP_VIEW = 0;
export const BINDGROUP_MESH = 1;

// map of engine TYPE_*** enums to their corresponding typed array constructors and byte sizes
export const typedArrayTypes = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array];
export const typedArrayTypesByteSize = [1, 1, 2, 2, 4, 4, 4];
Expand Down
8 changes: 8 additions & 0 deletions src/graphics/graphics-device.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ class GraphicsDevice extends EventHandler {
*/
supportsInstancing;

/**
* True if the device supports uniform buffers.
*
* @type {boolean}
* @ignore
*/
supportsUniformBuffers = false;

/**
* True if 32-bit floating-point textures can be used as a frame buffer.
*
Expand Down
107 changes: 107 additions & 0 deletions src/graphics/uniform-buffer-format.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { Debug } from '../core/debug.js';
import {
UNIFORMTYPE_BOOL, UNIFORMTYPE_INT, UNIFORMTYPE_FLOAT, UNIFORMTYPE_VEC2, UNIFORMTYPE_VEC3,
UNIFORMTYPE_VEC4, UNIFORMTYPE_IVEC2, UNIFORMTYPE_IVEC3, UNIFORMTYPE_IVEC4, UNIFORMTYPE_BVEC2,
UNIFORMTYPE_BVEC3, UNIFORMTYPE_BVEC4, UNIFORMTYPE_MAT4
} from './constants.js';

/** @typedef {import('./uniform-buffer.js').UniformBuffer} UniformBuffer */

// map of UNIFORMTYPE_*** to byte size
const uniformTypeToByteSize = [];
uniformTypeToByteSize[UNIFORMTYPE_FLOAT] = 4;
uniformTypeToByteSize[UNIFORMTYPE_VEC2] = 8;
uniformTypeToByteSize[UNIFORMTYPE_VEC3] = 12;
uniformTypeToByteSize[UNIFORMTYPE_VEC4] = 16;
uniformTypeToByteSize[UNIFORMTYPE_INT] = 4;
uniformTypeToByteSize[UNIFORMTYPE_IVEC2] = 8;
uniformTypeToByteSize[UNIFORMTYPE_IVEC3] = 12;
uniformTypeToByteSize[UNIFORMTYPE_IVEC4] = 16;
uniformTypeToByteSize[UNIFORMTYPE_BOOL] = 4;
uniformTypeToByteSize[UNIFORMTYPE_BVEC2] = 8;
uniformTypeToByteSize[UNIFORMTYPE_BVEC3] = 12;
uniformTypeToByteSize[UNIFORMTYPE_BVEC4] = 16;
uniformTypeToByteSize[UNIFORMTYPE_MAT4] = 64;

// Handle additiona types:
// UNIFORMTYPE_MAT2 = 12;
// UNIFORMTYPE_MAT3 = 13;
// UNIFORMTYPE_TEXTURE2D = 15;
// UNIFORMTYPE_TEXTURECUBE = 16;
// UNIFORMTYPE_FLOATARRAY = 17;
// UNIFORMTYPE_TEXTURE2D_SHADOW = 18;
// UNIFORMTYPE_TEXTURECUBE_SHADOW = 19;
// UNIFORMTYPE_TEXTURE3D = 20;
// UNIFORMTYPE_VEC2ARRAY = 21;
// UNIFORMTYPE_VEC3ARRAY = 22;
// UNIFORMTYPE_VEC4ARRAY = 23;
willeastcott marked this conversation as resolved.
Show resolved Hide resolved

/**
* A class storing description of an individual uniform, stored inside a uniform buffer.
*
* @ignore
*/
class UniformFormat {
slimbuck marked this conversation as resolved.
Show resolved Hide resolved
/** @type {string} */
name;

// UNIFORMTYPE_***
/** @type {number} */
type;

/** @type {number} */
byteSize;

/**
* Index of the uniform in an array of 32bit values (Float32Array and similar)
*
* @type {number}
*/
offset;

// TODO: add count for arrays

constructor(name, type) {
this.name = name;
this.type = type;
this.byteSize = uniformTypeToByteSize[type];
Debug.assert(this.byteSize, `Unknown byte size for uniform format ${type} used for ${name}`);
}
}

/**
* A descriptor that defines the layout of of data inside the {@link UniformBuffer}.
*
* @ignore
*/
class UniformBufferFormat {
/** @type {number} */
byteSize = 0;

/** @type {Map<string,UniformFormat>} */
map = new Map();

/**
* Create a new UniformBufferFormat instance.
*
* @param {UniformFormat[]} uniforms - An array of uniforms to be stored in the buffer
*/
constructor(uniforms) {
/** @type {UniformFormat[]} */
this.uniforms = uniforms;

// TODO: optimize uniforms ordering

let byteSize = 0;
for (let i = 0; i < uniforms.length; i++) {
const uniform = uniforms[i];
uniform.offset = byteSize / 4;
byteSize += uniform.byteSize;

this.map.set(uniform.name, uniform);
}
this.byteSize = byteSize;
}
}

export { UniformFormat, UniformBufferFormat };
75 changes: 75 additions & 0 deletions src/graphics/uniform-buffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Debug } from '../core/debug.js';

/** @typedef {import('./graphics-device.js').GraphicsDevice} GraphicsDevice */
/** @typedef {import('./uniform-buffer-format.js').UniformBufferFormat} UniformBufferFormat */

/**
* A uniform buffer represents a GPU memory buffer storing the uniforms.
*
* @ignore
*/
class UniformBuffer {
/**
* Create a new UniformBuffer instance.
*
* @param {GraphicsDevice} graphicsDevice - The graphics device used to manage this uniform buffer.
* @param {UniformBufferFormat} format - Format of the uniform buffer
*/
constructor(graphicsDevice, format) {
this.device = graphicsDevice;
this.format = format;

this.impl = graphicsDevice.createUniformBufferImpl(this);


// TODO: maybe size rounding up should be done here and not per platform


this.storage = new ArrayBuffer(format.byteSize);
this.storageFloat32 = new Float32Array(this.storage);

// TODO: register with the device and handle lost context
// this.device.buffers.push(this);
}

/**
* Frees resources associated with this uniform buffer.
*/
destroy() {

// // stop tracking the vertex buffer
const device = this.device;

// TODO: remove the buffer from the list on the device (lost context handling)

this.impl.destroy(device);

// TODO: track used memory
// device._vram.vb -= this.storage.byteLength;
}

/**
* Called when the rendering context was lost. It releases all context related resources.
*
* @ignore
*/
loseContext() {
this.impl.loseContext();
}

set(name, value) {
const uniform = this.format.map.get(name);
Debug.assert(uniform, `Uniform [${name}] is not part of the Uniform buffer.`);
if (uniform) {
const offset = uniform.offset;
this.storageFloat32.set(value, offset);
slimbuck marked this conversation as resolved.
Show resolved Hide resolved
}
}

update() {
// Upload the new data
this.impl.unlock(this);
}
}

export { UniformBuffer };
3 changes: 2 additions & 1 deletion src/graphics/vertex-format.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,8 @@ class VertexFormat {
this.batchingHash = hashCode(stringElementsBatch.join());

// rendering hash
this.renderingingHash = hashCode(stringElementsRender.join());
this.renderingingHashString = stringElementsRender.join('_');
this.renderingingHash = hashCode(this.renderingingHashString);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/graphics/webgl/webgl-graphics-device.js
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ class WebglGraphicsDevice extends GraphicsDevice {
}

createRenderTargetImpl(renderTarget) {
return new WebglRenderTarget(renderTarget);
return new WebglRenderTarget();
}

// #if _DEBUG
Expand Down
Loading