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

Render bundles implementation #88

Merged
merged 7 commits into from
Dec 2, 2024
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
- Lights & Shadows
- Shader passes
- PingPongPlane
- RenderBundle
- Basic CacheManager
- Scroll + resize, frustum culling check
- GPUCurtains
Expand All @@ -36,6 +37,5 @@
- Add/improve GLTFScenesManager features (sparse accessors, animations, morphing, skinning...)
- Add more lights (SpotLight...)
- Improve typedoc documentation?
- Use render bundles? Probably not suited to the library tho
- Use indirect draw calls?
- More examples & tests?
52 changes: 42 additions & 10 deletions dist/esm/core/bindGroups/BindGroup.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ class BindGroup {
this.consumers = /* @__PURE__ */ new Set();
for (const binding of this.bufferBindings) {
if ("buffer" in binding) {
binding.buffer.consumers.add(this.uuid);
if ("parent" in binding && binding.parent) {
binding.parent.buffer.consumers.add(this.uuid);
} else {
binding.buffer.consumers.add(this.uuid);
}
}
if ("resultBuffer" in binding) {
binding.resultBuffer.consumers.add(this.uuid);
Expand All @@ -57,8 +61,13 @@ class BindGroup {
addBindings(bindings = []) {
bindings.forEach((binding) => {
if ("buffer" in binding) {
this.renderer.deviceManager.bufferBindings.set(binding.cacheKey, binding);
binding.buffer.consumers.add(this.uuid);
if ("parent" in binding && binding.parent) {
this.renderer.deviceManager.bufferBindings.set(binding.parent.cacheKey, binding.parent);
binding.parent.buffer.consumers.add(this.uuid);
} else {
this.renderer.deviceManager.bufferBindings.set(binding.cacheKey, binding);
binding.buffer.consumers.add(this.uuid);
}
}
});
this.bindings = [...this.bindings, ...bindings];
Expand All @@ -81,6 +90,13 @@ class BindGroup {
if (!binding.buffer.consumers.size) {
binding.buffer.destroy();
}
if ("parent" in binding && binding.parent) {
binding.parent.buffer.consumers.delete(this.uuid);
if (!binding.parent.buffer.consumers.size) {
this.renderer.removeBuffer(binding.parent.buffer);
binding.parent.buffer.destroy();
}
}
}
if ("resultBuffer" in binding) {
this.renderer.removeBuffer(binding.resultBuffer);
Expand Down Expand Up @@ -230,6 +246,9 @@ class BindGroup {
this.resetEntries();
for (const binding of this.bufferBindings) {
binding.buffer.reset();
if ("parent" in binding && binding.parent) {
binding.parent.buffer.reset();
}
if ("resultBuffer" in binding) {
binding.resultBuffer.reset();
}
Expand Down Expand Up @@ -258,12 +277,13 @@ class BindGroup {
);
}
/**
* Creates binding GPUBuffer with correct params
* @param binding - the binding element
* Creates binding GPUBuffer with correct params.
* @param binding - The binding element.
* @param optionalLabel - Optional label to use for the {@link GPUBuffer}.
*/
createBindingBuffer(binding) {
createBindingBuffer(binding, optionalLabel = null) {
binding.buffer.createBuffer(this.renderer, {
label: this.options.label + ": " + binding.bindingType + " buffer from: " + binding.label,
label: optionalLabel || this.options.label + ": " + binding.bindingType + " buffer from: " + binding.label,
usage: [...["copySrc", "copyDst", binding.bindingType], ...binding.options.usage]
});
if ("resultBuffer" in binding) {
Expand All @@ -284,7 +304,13 @@ class BindGroup {
binding.visibility = GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT | GPUShaderStage.COMPUTE;
}
if ("buffer" in binding) {
if (!binding.buffer.GPUBuffer) {
const isChildBuffer = "parent" in binding && binding.parent;
if (isChildBuffer && !binding.parent.buffer.GPUBuffer) {
this.createBindingBuffer(
binding.parent,
binding.parent.options.label
);
} else if (!binding.buffer.GPUBuffer && !isChildBuffer) {
this.createBindingBuffer(binding);
}
}
Expand Down Expand Up @@ -393,10 +419,16 @@ class BindGroup {
for (const binding of bindingsRef) {
bindGroupCopy.addBinding(binding);
if ("buffer" in binding) {
if (!binding.buffer.GPUBuffer) {
const isChildBuffer = "parent" in binding && binding.parent;
if (isChildBuffer && !binding.parent.buffer.GPUBuffer) {
this.createBindingBuffer(
binding.parent,
binding.parent.options.label
);
binding.parent.buffer.consumers.add(bindGroupCopy.uuid);
} else if (!binding.buffer.GPUBuffer && !isChildBuffer) {
this.createBindingBuffer(binding);
}
binding.buffer.consumers.add(bindGroupCopy.uuid);
if ("resultBuffer" in binding) {
binding.resultBuffer.consumers.add(bindGroupCopy.uuid);
}
Expand Down
2 changes: 1 addition & 1 deletion dist/esm/core/bindings/BufferBinding.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ class BufferBinding extends Binding {
}
/**
* Executed at the beginning of a Material render call.
* If any of the {@link inputs} has changed, run its onBeforeUpdate callback then updates our {@link arrayBuffer} array.
* If any of the {@link inputs} has changed, run its `onBeforeUpdate` callback then updates our {@link arrayBuffer} array.
* Also sets the {@link shouldUpdate} property to true so the {@link core/bindGroups/BindGroup.BindGroup | BindGroup} knows it will need to update the {@link GPUBuffer}.
*/
update() {
Expand Down
145 changes: 145 additions & 0 deletions dist/esm/core/bindings/BufferBindingOffsetChild.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { BufferBinding } from './BufferBinding.mjs';
import { getBindGroupLayoutBindingType } from './utils.mjs';

var __accessCheck = (obj, member, msg) => {
if (!member.has(obj))
throw TypeError("Cannot " + msg);
};
var __privateGet = (obj, member, getter) => {
__accessCheck(obj, member, "read from private field");
return getter ? getter.call(obj) : member.get(obj);
};
var __privateAdd = (obj, member, value) => {
if (member.has(obj))
throw TypeError("Cannot add the same private member more than once");
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
};
var __privateSet = (obj, member, value, setter) => {
__accessCheck(obj, member, "write to private field");
setter ? setter.call(obj, value) : member.set(obj, value);
return value;
};
var _parent;
class BufferBindingOffsetChild extends BufferBinding {
/**
* BufferBindingOffsetChild constructor
* @param parameters - {@link BufferBindingOffsetChildParams | parameters} used to create this {@link BufferBindingOffsetChild}.
*/
constructor({
label = "Uniform",
name = "uniform",
bindingType,
visibility,
useStruct = true,
access = "read",
usage = [],
struct = {},
bindings = [],
parent = null,
minOffset = 256,
offset = 0
}) {
super({ label, name, bindingType, visibility, useStruct, access, usage, struct, bindings });
/** @ignore */
__privateAdd(this, _parent, void 0);
this.options = {
...this.options,
minOffset,
offset
};
this.parent = parent;
}
/**
* Get the {@link BufferBinding} parent if any.
* @readonly
* @returns - The {@link BufferBinding} parent if any.
*/
get parent() {
return __privateGet(this, _parent);
}
/**
* Set the new {@link BufferBinding} parent.
* @param value - New {@link BufferBinding} parent to set if any.
*/
set parent(value) {
__privateSet(this, _parent, value);
if (!!value) {
this.parentView = new DataView(value.arrayBuffer, this.offset, this.getMinOffsetSize(this.arrayBufferSize));
this.viewSetFunctions = this.bufferElements.map((bufferElement) => {
switch (bufferElement.bufferLayout.View) {
case Int32Array:
return this.parentView.setInt32.bind(this.parentView);
case Uint16Array:
return this.parentView.setUint16.bind(this.parentView);
case Uint32Array:
return this.parentView.setUint32.bind(this.parentView);
case Float32Array:
default:
return this.parentView.setFloat32.bind(this.parentView);
}
});
} else {
this.parentView = null;
this.viewSetFunctions = null;
}
}
/**
* Round the given size value to the nearest minimum {@link GPUDevice} buffer offset alignment.
* @param value - Size to round.
*/
getMinOffsetSize(value) {
return Math.ceil(value / this.options.minOffset) * this.options.minOffset;
}
/**
* Get this {@link BufferBindingOffsetChild} offset in bytes inside the {@link arrayBuffer | parent arrayBuffer}.
* @readonly
* @returns - The offset in bytes inside the {@link arrayBuffer | parent arrayBuffer}
*/
get offset() {
return this.getMinOffsetSize(this.options.offset * this.getMinOffsetSize(this.arrayBufferSize));
}
/**
* Get {@link GPUBindGroupLayoutEntry#buffer | bind group layout entry resource}.
* @readonly
*/
get resourceLayout() {
return {
buffer: {
type: getBindGroupLayoutBindingType(this)
},
...this.parent && { offset: this.offset, size: this.arrayBufferSize }
};
}
/**
* Get {@link GPUBindGroupEntry#resource | bind group resource}
* @readonly
*/
get resource() {
return {
buffer: this.parent ? this.parent.buffer.GPUBuffer : this.buffer.GPUBuffer,
...this.parent && { offset: this.offset, size: this.arrayBufferSize }
};
}
/**
* Update the {@link BufferBindingOffsetChild} at the beginning of a Material render call.
*
* If a {@link parent} is set, then update its {@link arrayBuffer | arrayBuffer} using our {@link viewSetFunctions}.
*/
update() {
super.update();
if (this.shouldUpdate && this.parent && this.viewSetFunctions) {
let index = 0;
this.bufferElements.forEach((bufferElement, i) => {
bufferElement.view.forEach((value) => {
this.viewSetFunctions[i](index * bufferElement.view.BYTES_PER_ELEMENT, value, true);
index++;
});
});
this.parent.shouldUpdate = true;
this.shouldUpdate = false;
}
}
}
_parent = new WeakMap();

export { BufferBindingOffsetChild };
1 change: 1 addition & 0 deletions dist/esm/core/lights/AmbientLight.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class AmbientLight extends Light {
*/
constructor(renderer, { color = new Vec3(1), intensity = 0.1 } = {}) {
const type = "ambientLights";
renderer = renderer && renderer.renderer || renderer;
const index = renderer.lights.filter((light) => light.type === type).length;
super(renderer, { color, intensity, index, type });
if (this.index + 1 > this.renderer.lightsBindingParams[this.type].max) {
Expand Down
1 change: 1 addition & 0 deletions dist/esm/core/lights/DirectionalLight.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class DirectionalLight extends Light {
shadow = null
} = {}) {
const type = "directionalLights";
renderer = renderer && renderer.renderer || renderer;
const index = renderer.lights.filter((light) => light.type === type).length;
super(renderer, { color, intensity, index, type });
/** @ignore */
Expand Down
5 changes: 3 additions & 2 deletions dist/esm/core/lights/Light.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -114,16 +114,17 @@ class Light extends Object3D {
}
}
/**
* Remove this {@link Light} from the {@link renderer}.
* Remove this {@link Light} from the {@link renderer} and destroy it.
*/
remove() {
this.renderer.removeLight(this);
this.destroy();
}
/**
* Destroy this {@link Light}.
*/
destroy() {
this.parent = null;
super.destroy();
}
}
_intensity = new WeakMap();
Expand Down
1 change: 1 addition & 0 deletions dist/esm/core/lights/PointLight.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class PointLight extends Light {
*/
constructor(renderer, { color = new Vec3(1), intensity = 1, position = new Vec3(), range = 0, shadow = null } = {}) {
const type = "pointLights";
renderer = renderer && renderer.renderer || renderer;
const index = renderer.lights.filter((light) => light.type === type).length;
super(renderer, { color, intensity, index, type });
/** @ignore */
Expand Down
4 changes: 4 additions & 0 deletions dist/esm/core/materials/RenderMaterial.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Material } from './Material.mjs';
import { isRenderer } from '../renderers/utils.mjs';
import { RenderPipelineEntry } from '../pipelines/RenderPipelineEntry.mjs';
import { throwWarning } from '../../utils/utils.mjs';
import { compareRenderingOptions } from './utils.mjs';
import default_projected_vsWgsl from '../shaders/chunks/default/default_projected_vs.wgsl.mjs';
Expand Down Expand Up @@ -121,6 +122,9 @@ class RenderMaterial extends Material {
* @param renderingOptions - new {@link RenderMaterialRenderingOptions | rendering options} properties to be set
*/
setRenderingOptions(renderingOptions = {}) {
if (renderingOptions.transparent && renderingOptions.targets.length && !renderingOptions.targets[0].blend) {
renderingOptions.targets[0].blend = RenderPipelineEntry.getDefaultTransparentBlending();
}
const newProperties = compareRenderingOptions(renderingOptions, this.options.rendering);
const oldRenderingOptions = { ...this.options.rendering };
this.options.rendering = { ...this.options.rendering, ...renderingOptions };
Expand Down
2 changes: 1 addition & 1 deletion dist/esm/core/meshes/FullscreenPlane.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class FullscreenPlane extends MeshBaseMixin(class {
}) {
/**
* FullscreenPlane constructor
* @param renderer - {@link Renderer} object or {@link GPUCurtains} class object used to create this {@link FullscreenPlane}
* @param renderer - {@link Renderer} or {@link GPUCurtains} class object used to create this {@link FullscreenPlane}
* @param parameters - {@link MeshBaseRenderParams | parameters} use to create this {@link FullscreenPlane}
*/
constructor(renderer, parameters = {}) {
Expand Down
Loading