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

BufferAttribute: Add support for multiple update ranges #27103

Merged
merged 19 commits into from
Nov 8, 2023

Conversation

gkjohnson
Copy link
Collaborator

@gkjohnson gkjohnson commented Nov 2, 2023

Related issue: #27080, #27078

Description

Adds support for updating multiple ranges in an attribute buffer. It's the users responsibility to make sure there are no overlapping or duplicate ranges uploaded.

  • Deprecates BufferAttribute.updateRange.
  • adds BufferAttribute.updateRanges and associated clear and add functions.
  • Update tests.
  • Update two example files.
  • Update WebGLAttributes to support updateRanges.
  • Update WebGPUAttributeUtils to support updateRanges.

I will update other docs once this is merged.

@gkjohnson gkjohnson changed the title BufferAttribute: Update ranges Nov 2, 2023
Copy link

github-actions bot commented Nov 2, 2023

📦 Bundle size

Full ESM build, minified and gzipped.

Filesize dev Filesize PR Diff
655.7 kB (162.4 kB) 656.4 kB (162.6 kB) +672 B

🌳 Bundle size after tree-shaking

Minimal build including a renderer, camera, empty scene, and dependencies.

Filesize dev Filesize PR Diff
448.4 kB (108.4 kB) 448.9 kB (108.6 kB) +484 B
@gkjohnson gkjohnson added this to the r159 milestone Nov 2, 2023
@@ -360,7 +380,8 @@ class BufferAttribute {

if ( this.name !== '' ) data.name = this.name;
if ( this.usage !== StaticDrawUsage ) data.usage = this.usage;
if ( this.updateRange.offset !== 0 || this.updateRange.count !== - 1 ) data.updateRange = this.updateRange;
if ( this._updateRange.offset !== 0 || this._updateRange.count !== - 1 ) data.updateRange = this._updateRange;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed if we're aiming for backwards compatibility for 10 releases?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, let's keep this until updateRange is completely removed.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't think of a use case when this is needed in the json though...
Maybe we can skip adding this.updateRanges this time?

Copy link
Collaborator

@Mugen87 Mugen87 Nov 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Update rangers are resetted after the render has processed the frame so it's not a typical data that you serialize for a scene graph structure.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great - I removed toJSON and BufferGeometryLoader support for both the old updateRange and updateRanges

@gkjohnson gkjohnson marked this pull request as ready for review November 2, 2023 13:49
src/core/BufferAttribute.js Outdated Show resolved Hide resolved
Copy link
Collaborator

@Mugen87 Mugen87 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me!

@mrdoob Are you okay with changing the API like proposed in this PR? This new feature will be useful for BatchedMesh since it enables more efficient updates without rearranging buffer data (which would be too expensive anyway, see #27080 (comment)).

@gkjohnson
Copy link
Collaborator Author

This new feature will be useful for BatchedMesh since it enables more efficient updates without rearranging buffer data

It's also useful for large dynamic geometries even without BatchedMesh. For example this another case where I was updating geometry in a ring-buffer style fashion but was unable to upload all the changes in one frame when reaching the end of the array:

https://twitter.com/garrettkjohnson/status/1642890723531055104

@aardgoose
Copy link
Contributor

Do you want to add support in the WebGPURenderer WebGL fallback?

examples/jsm/renderers/webgl/utils/WebGPUAttributeUtils.js

@gkjohnson
Copy link
Collaborator Author

gkjohnson commented Nov 3, 2023

Do you want to add support in the WebGPURenderer WebGL fallback?

examples/jsm/renderers/webgl/utils/WebGPUAttributeUtils.js

I'll add support in another PR since updateRange wasn't used, yet, and keep this PR focused on migrating existing usage.

@aardgoose
Copy link
Contributor

Incidentally is there a typical size of update where the overhead of a number of small updates exceeds any gains from reduction of bytes copied?

@gkjohnson gkjohnson requested a review from mrdoob November 4, 2023 00:15
@Mugen87
Copy link
Collaborator

Mugen87 commented Nov 6, 2023

Not that I am aware of. Concrete numbers will also vary from device to device. I think projects need to find out by their own whether multiple update ranges makes sense for their use case or not.

@gkjohnson
Copy link
Collaborator Author

gkjohnson commented Nov 8, 2023

cc @mrdoob is this API okay to merge?

Before

only one update range is allowed

attr.updateRange.offset = 100;
attr.updateRange.count = 100;

After

multiple update ranges are allowed

attr.addUpdateRange( 100, 100 );
attr.addUpdateRange( 300, 100 );

attr.updateRanges.length // 2
Comment on lines 61 to 65
addUpdateRange( offset, count ) {

this.updateRanges.push( { offset, count } );

}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we name it start instead of offset this time?

	addUpdateRange( start, count ) {

		this.updateRanges.push( { start, count } );

	}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

@mrdoob
Copy link
Owner

mrdoob commented Nov 8, 2023

I've added a couple of comments, but the new API looks good to me 👍

@gkjohnson
Copy link
Collaborator Author

gkjohnson commented Nov 8, 2023

Made the changes - merging!

@@ -42,6 +43,13 @@ class BufferAttribute {

}

get updateRange() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gkjohnson Please add a // @deprecated, r159 comment here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to make a PR. This one is merged

Copy link
Contributor

@lgarron lgarron Jan 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that since this only defines a getter, setting updateRange is in fact broken in r159.

This broke our project and we were able to work around it easily, but I think it would be nice if the release notes for r159 were updated to mention the sudden breaking change for setting the value. As of right now, they only mention a new feature without mentioning any breaking changes:

Add support for multiple update ranges. #27103, #27148, #27149 (@gkjohnson)

https://github.com/mrdoob/three.js/releases/tag/r159

(Or you could introduce a setter with a deprecation message.)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Breaking changes are separately listed in the migration guide along with the migration tasks: https://github.com/mrdoob/three.js/wiki/Migration-Guide#r158--r159

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aha! Unfortunately, that updateRange does not appear on this page when I search for it. I had to spend a few minutes trying to figure out why setting updateRange wasn't working, because nothing in the release notes actually mentioned the removal of the setter. While I understand the appeal of a condensed migration reference, it feels like the release notes themselves are sort of pretending that certain changes don't exist — when in fact those are the most impactful changes for existing codebases.

Would it be reasonable to inline a copy of the migration changes into the release notes, or to include the breaking changes directly as bullets in the list of changes? I think this would make the release notes a lot more intuitive, since other projects include breaking changes in the release notes themselves.

@@ -28,6 +29,13 @@ class InterleavedBuffer {

}

get updateRange() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.


}

// deprecated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
6 participants