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

RFC: WebGPU Occlusion queries #26335

Merged
merged 22 commits into from Aug 14, 2023
Merged

RFC: WebGPU Occlusion queries #26335

merged 22 commits into from Aug 14, 2023

Conversation

aardgoose
Copy link
Contributor

Occlusion query support for WebGPU

Use suggest API from #15450 comments.

Set object.occlusionTest = true to enable testing.
Object has an isOccluded flag set as expected after render pass. Note, the setting of the flag is asynchronous. Example included to demonstrate this.

@sunag sunag added this to the r154 milestone Jun 27, 2023
@sunag
Copy link
Collaborator

sunag commented Jun 27, 2023

Other very useful update, thanks.

Maybe we need to add to the example in

// Awaiting for WebGPU support


let readBuffer;

if ( occlusionQueryCount > renderContextData.occlusionQueryIndex ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this the correct condition?

Copy link
Contributor

Choose a reason for hiding this comment

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

Because I think endOcclusionQuery is also called in the draw method?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This should catch the case when the last queried object being rendered is the last draw call in the pass. In the draw call. The end is inserted when a new object is seen, to allow handling of multiple draw calls per object (array cameras etc). Otherwise the endOcclusionQuery call could be at the end of the draw method.

According to the docs you can't use a querySet index in multiple beginOcclusionQuery( index ) calls in the same pass, otherwise you could just have repeated begin/end pairs around each draw() call.


generate( builder, output ) {

return this.uniformNode.build( builder, output );
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think, use construct() instead of generate(). I update webgpu_instance_uniform example too. #26336

construct() {

	return this.uniformNode;

}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You are the expert here. I obviously copied the instance_uniform example.

@mrdoob mrdoob modified the milestones: r154, r155 Jun 29, 2023
@sunag
Copy link
Collaborator

sunag commented Jun 30, 2023

What do you think about object.isOccluded being defined by renderer?
Or should we add it in Renderer like renderer.getOccluded() returning an Array? Seems more common with the rest of the API.

EDIT: Maybe renderer.isOccluded( object )...

/cc @mrdoob @Mugen87 @LeviPesin

@aardgoose
Copy link
Contributor Author

Regarding object.isOccluded - maybe use 'null' to distinguish between objects that have been rendered and those culled before rendering.

Regarding returning an array - that has the problem of retaining references to objects and preventing GC if not careful. renderer.isOccluded() would be better of course with a weak set.

@sunag
Copy link
Collaborator

sunag commented Jul 8, 2023

I think we can create a function like renderer.getOccludedAsync() and return a WeakSet, and define WebGPU.finishRender() with sync again, so we can guarantee the right state.

The method bellow would only be read once and moved to renderContextData in renderer.getOccludedAsync(), we can have renderer.isOccludedAsync( object ) too, but it seems good to me to have the both options for performance resons.

await renderContextData.readBuffer.mapAsync( GPUMapMode.READ )

@mrdoob mrdoob modified the milestones: r155, r156 Jul 27, 2023
@sunag
Copy link
Collaborator

sunag commented Aug 14, 2023

I added the function Renderer.isOccluded( object ) in code, it seems within the context of Three.js. Normally the Renderer don't set values on objects "permanently" like object.isOccluded = true|false, Renderer.isOccluded( object ) I think sounds more compatible with the principle of multi-renderer and render-pass.

I also added the inner function resolveOccludedAsync() a cache for the queryResolveBuffer, I added some destroy().

Basically it does the Occlusion Query in the current rendering and uses the data possibly in the next frame, I say possibly because this is asynchronous, I added an internal backend function WebGPUBackend.resolveOccludedAsync() to facilitate this request in case any issue arises related to this, we could simply add it with await renderer.resolveOccludedAsync() after rendering (we can easily add this support if necessary).

@sunag sunag merged commit a0cb769 into mrdoob:dev Aug 14, 2023
17 of 18 checks passed
@linbingquan
Copy link
Contributor

linbingquan commented Aug 14, 2023

I found this strange place, at this time the plane should be blue. Maybe that's not a problem or not be easy to solve.

Repeat step: Rotate the canvas slowly with the mouse

strange place expect
图片 图片

@sunag
Copy link
Collaborator

sunag commented Aug 14, 2023

That's because it's asynchronous and the subsequent frame wasn't updating, I updated it here.

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.

None yet

5 participants