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

Added GLBufferAttribute #13196

Merged
merged 41 commits into from
Aug 16, 2020
Merged

Added GLBufferAttribute #13196

merged 41 commits into from
Aug 16, 2020

Conversation

raub
Copy link
Contributor

@raub raub commented Jan 29, 2018

If present, would delegate VBO creation to the user-supplied function. See more info on this in #11883. The default behavior is untouched.

@pailhead
Copy link
Contributor

More hooks like this!!

@mrdoob mrdoob added this to the r91 milestone Feb 1, 2018
@raub raub changed the title Added onCreateCallback for WebGLAttributes #11883 Added DynamicBufferAttribute class #11883 Feb 10, 2018
@raub raub mentioned this pull request Feb 10, 2018
4 tasks
@raub
Copy link
Contributor Author

raub commented Feb 10, 2018

Now a new DynamicBufferAttribute class exists. Example: webgl_buffergeometry_points_dynamicbufferattribute.html.

new DynamicBufferAttribute(...) parameters:

  • gl - renderer.context,
  • buffer - an actual VBO,
  • type - e.g. gl.FLOAT,
  • itemSize - vector size, e.g. 3 for xyz position,
  • count - vertex count.

@mrdoob
Copy link
Owner

mrdoob commented Feb 12, 2018

Do you mind renaming it to GLBufferAttribute? BufferAttribute has a dynamic property (which sets gl.DYNAMIC_DRAW) and I could see people getting confused.

@raub
Copy link
Contributor Author

raub commented Feb 13, 2018

I see your point, but all of them (buffer types) are of GL kind... GLBufferAttribute is not a perfect name either. Could it be something else? Like

  • ManualBufferAttribute
  • ExternalBufferAttribute
  • SharedBufferAttribute
  • (other suggestions welcome)

I think prefix GL tells really nothing about what is special in this new attribute class, so it should be the last resort name of choise.

Edit: I found out that the name DynamicBufferAttribute has already been taken by a deprecated class. I personally like the idea of the ExternalBufferAttribute name, because this is all about the VBO's created elsewhere. Would you agree?

@mrdoob mrdoob modified the milestones: r91, r92 Mar 14, 2018
@raub
Copy link
Contributor Author

raub commented Mar 16, 2018

@mrdoob, since no answer from you, I've decided to take some action and finally rename the class to ExternalBufferAttribute. Is this fine? What else is to be done before the merge could hapen?

@mrdoob
Copy link
Owner

mrdoob commented Apr 18, 2018

I still prefer GLBufferAttribute. We're planning to also add GLLine for people that want to use raw gl.LINE instead of the new line with linewidth support. This naming also hints the developer that no other renderers will be able to handle these classes.

Sorry I couldn't elaborate in time for r92, moving to r93.

@mrdoob mrdoob modified the milestones: r92, r93 Apr 18, 2018
@raub
Copy link
Contributor Author

raub commented Apr 18, 2018

Ok, will do soon

@raub
Copy link
Contributor Author

raub commented Apr 18, 2018

Done, GLBufferAttribute. Seems to work fine with the latest dev branch.

@WestLangley
Copy link
Collaborator

Nice demo. :-)

Can you please: add your new example to examples/files.js, update the docs with your new class, and check your formatting. https://zz85.github.io/mrdoobapproves/

this.buffer = buffer;
this.type = type;
this.itemSize = itemSize;
this.elementSize = this.sizes[ type ];
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is elementSize also intended to be private?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is intended to be public. The idea is as follows: you know the type of your data, you pass it, the elementSize becomes available, carrying the size-in-bytes value for you.

It is not necessary. We could let the app do all the calculations and leave here only what's necessary for three.js to handle the attribute.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah wait, I found it here: src\renderers\webgl\WebGLAttributes.js

			buffers.set( attribute, {
				buffer: attribute.buffer,
				type: attribute.type,
				bytesPerElement: attribute.elementSize,
				version: attribute.version
			} );

so basically it is there for three.js to get the byte size, because there is no Array instance to do so (bytesPerElement: array.BYTES_PER_ELEMENT,).

Copy link
Collaborator

@Mugen87 Mugen87 Apr 27, 2020

Choose a reason for hiding this comment

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

Um, have you considered to let the user set this value? I mean GLBufferAttribute is obviously a low-level API not necessarily intended for beginners. Devs who use it should also know the correct bytes per element.

I would suggest to put as less logic as possible into the ctor and let instead the user defined such information. In this way, you probably don't even need a WebGL rendering context in GLBufferAttribute which I find a bit strange anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The logic behind this is that passing both type and elementSize to the constructor is redundant, because the type always implies the only possible elementSize. So it kind of creates a room for an error to occur when you ask the user to pass both. So I decided to calculate it on the ctor side.

But in general it would be fine either way. If the ctor takes elementSize, we get rid of all the logic and the contraversial this.sizes member. Also no need for gl then. Do you approve of the latter scenario?

Copy link
Collaborator

@Mugen87 Mugen87 Apr 27, 2020

Choose a reason for hiding this comment

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

I would say yes. Keeping GLBufferAttribute simple and let the app handle this bit sound like the better design to me. Especially since we can assume the user knows the relationship between type and elementSize if this class is going to be used.

The final decision is up to @mrdoob though 😉

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Any updates on that matter? @Mugen87 @mrdoob

Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's wait for @mrdoob's feedback.

@raub
Copy link
Contributor Author

raub commented Apr 27, 2020

sizes: [ [ number, number ] ]; I think should be sizes: { [number]: number }; something. Unsure if this is a fine piece of TS syntax though. How can we validate it?

@Mugen87
Copy link
Collaborator

Mugen87 commented Apr 27, 2020

If you are unsure about this, use any for the moment^^.

@mrdoob mrdoob modified the milestones: r116, r117 Apr 30, 2020
@mrdoob mrdoob modified the milestones: r117, r118 May 27, 2020
@mrdoob mrdoob modified the milestones: r118, r119 Jun 24, 2020
@pvaananen
Copy link

Sorry for bothering you but I'm really looking forward to this addition. So the current status is that the PR is waiting for @mrdoob's feedback?

Regarding that TypeScript question on Apr 27, may I suggest declaring an array of number pairs with sizes:Array<[number, number]> or sizes:[number, number][].

@mrdoob
Copy link
Owner

mrdoob commented Jul 27, 2020

Ah, too close to release now... Lets see if we can merge this at the beginning on the next dev cycle.

@mrdoob mrdoob modified the milestones: r119, r120 Jul 27, 2020
@Mugen87
Copy link
Collaborator

Mugen87 commented Jul 30, 2020

@mrdoob Before merging, can you please review https://github.com/mrdoob/three.js/pull/13196/files#r416037718? I'm not sure about the current public interface of GLBufferAttribute.

@raub
Copy link
Contributor Author

raub commented Aug 2, 2020

  1. Removed "gl" constructor param.
  2. Removed "normalized" constructor param.
  3. Added "elementSize" constructor param.
  4. Removed "sizes" property.
  5. Updated the TS class declaration.
  6. Updated the docs.
  7. Updated the example to fit new interface.
  8. Updated the example logic for e2e compliance.
  9. Debugged and fixed an issue when buffers don't change under VAO mode. (pls review!)
  10. Generated a screenshot and passed e2e.

@Mugen87
Copy link
Collaborator

Mugen87 commented Aug 4, 2020

Thanks @raub! This looks much better to me now!

@mrdoob
Copy link
Owner

mrdoob commented Aug 14, 2020

I think you can delete webgl_buffergeometry_points_glbufferattribute.png.

@mrdoob
Copy link
Owner

mrdoob commented Aug 16, 2020

I'll merge and clean up.

@mrdoob mrdoob merged commit ba9f859 into mrdoob:dev Aug 16, 2020
@mrdoob
Copy link
Owner

mrdoob commented Aug 16, 2020

Thanks!

@@ -176,6 +176,8 @@
const cachedAttribute = cachedAttributes[ key ];
const geometryAttribute = geometryAttributes[ key ];

if ( geometryAttribute.isGLBufferAttribute ) return true;
Copy link
Collaborator

@Mugen87 Mugen87 Aug 16, 2020

Choose a reason for hiding this comment

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

@raub Can you please explain why you have added this line? When removed, the respective demo does not seem to work correctly anymore.

It's actually not correct to always force a call of setupVertexAttributes() on each frame if e.g. only the buffer contents change.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't exactly understand what this part of the system is supposed to do. But my guess is:

  • if there is no VAO, bind appropriate buffers
  • if there is a VAO and the attributes were not changed, just switch to that VAO
  • if attributes changed, update the VAO, i.e. switch to it and bind the updated buffers

That all sounds good in theory. But when I noticed my demo was broken, I started debugging and ended up here. So basically I set needsUpdate to my attribute and expect the engine to do the job of updating whatever was changed (the buffer). That was not the case here.

For some reason, cachedAttribute always contained an already-updated version of my attribute. So the VAO never got updated (hence the demo was broken). I double-checked this fact by adding some custom properties directly into the buffer, like buffer.name=111.

So I decided to opt-out from this optimization with my attribute type. I expect that line 179 effectively makes VAO unused for GLBufferAttribute. That is fine with me. Although if you understand what is going wrong and how to make it work with this optimization, that would also be great.

Copy link
Collaborator

@Mugen87 Mugen87 Aug 17, 2020

Choose a reason for hiding this comment

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

The problem is that the current implementation is bad for performance since a lot of unnecessary state changes are done.

We definitely have to revisit this issue otherwise I vote to revert GLBufferAttribute before the next release.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The current implementation is vastly superior than having no implementation. And if I'm right, for webgl1 people the further discussion makes no sense, since they always get exactly this level of performance. So there is a considerable group of people who either get this feature or not, regardless of VAO optimizations.

Now to optimizations. I would gladly fixed the VAO issue, given it was at all possible for you (or anybody) to explan why the cached attribute is always the updated attribute itself. And if it is the correct behavior, how it can be possibly fixed.

If you want, I might go on and report this behavior as a bug in a separate issue. Because now it is my understanding, that when I set needsUpdate on my attribute, the cache should contain the old version. If I have just updated the attribute, and the "cached" version does already contain what I have updated, it is a bug in WebGLBindingStates unless proven otherwise.

Copy link
Collaborator

@Mugen87 Mugen87 Aug 18, 2020

Choose a reason for hiding this comment

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

This has nothing to do with WebGL 1 or 2. VAO is available for most WebGL 1 users. Besides, even without VAO the buffer attribute bindings should only be done if necessary. Not if just buffer data changes.

I think the problem in your demo is this line:

posAttr.buffer = ( posAttr.buffer === pos ) ? pos2 : pos;

You should exchange the entire attribute and not just the buffer. Or you keep the buffer the same and only change the contents. But just exchanging buffers is not something the engine wants to promote (it's not supported anyway and support will NOT be added).

Copy link
Collaborator

Choose a reason for hiding this comment

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

This pattern works without the hack in WebGLBindingStates:

var posAttr1 = new THREE.GLBufferAttribute( pos, gl.FLOAT, 3, 4, particles );
var posAttr2 = new THREE.GLBufferAttribute( pos2, gl.FLOAT, 3, 4, particles );
geometry.setAttribute( 'position', posAttr1 );

setInterval( function () {

    var attr = geometry.getAttribute( 'position' );

    geometry.setAttribute( 'position', ( attr === posAttr1 ) ? posAttr2 : posAttr1 );
 
}, 2000 );

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

Successfully merging this pull request may close these issues.

7 participants