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

WebGLRenderer: Fix feedback loop with RTT read pixels async #29320

Merged
merged 3 commits into from
Sep 4, 2024

Conversation

gkjohnson
Copy link
Collaborator

@gkjohnson gkjohnson commented Sep 4, 2024

Related issue: #29302 (comment)

Description

Fixes the feedback error reported by readRenderTargetPixelsAsync caused by modifying the active frame buffer and not resetting it to the actively set one before awaiting the sync fence. I also removed the try / catch statements since we're not so defensive anywhere else in the project and could result in the function silently exiting with no returned data so it may be easiest to look at the diff without whitespace changes. Also throw an error in the branch where we can't return any data.

@gkjohnson gkjohnson added this to the r169 milestone Sep 4, 2024
@gkjohnson gkjohnson requested a review from Mugen87 September 4, 2024 12:06
Copy link

github-actions bot commented Sep 4, 2024

📦 Bundle size

Full ESM build, minified and gzipped.

Before After Diff
WebGL 685.1 kB
169.6 kB
685.2 kB
169.6 kB
+82 B
+56 B
WebGPU 823 kB
220.9 kB
823 kB
220.9 kB
+0 B
+0 B
WebGPU Nodes 822.6 kB
220.8 kB
822.6 kB
220.8 kB
+419 B
+94 B

🌳 Bundle size after tree-shaking

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

Before After Diff
WebGL 461.9 kB
111.4 kB
462 kB
111.5 kB
+82 B
+57 B
WebGPU 522.1 kB
140.8 kB
522.1 kB
140.8 kB
+0 B
+0 B
WebGPU Nodes 478.8 kB
130.6 kB
478.8 kB
130.6 kB
-43.34 kB
+0 B

renderer.readRenderTargetPixels( rtt, 0, 0, info.width, info.height, dataBuffer );
await renderer.readRenderTargetPixelsAsync( rtt, 0, 0, info.width, info.height, dataBuffer );
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I notice that the function signature for readRenderTargetPixelsAsync in WebGPURenderer doesn't take a dataBuffer argument which should probably be fixed to align with the WebGLRenderer implementation. Taking a databuffer was a deliberate decision to avoid memory allocation in cases where this is run ever frame or every few frames and is common pattern use throughout the library, as well, and I think it's worth keeping.

It looks like WebGPURenderer will now create a new typed array on every call.

Copy link
Collaborator

@Mugen87 Mugen87 Sep 4, 2024

Choose a reason for hiding this comment

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

Do you mind updating KTX2Exporter as well?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've updated KTX2Exporter but haven't tested it since I don't see an example.

Also is it worth creating a new issue to track this function signature difference?

Copy link
Contributor

@aardgoose aardgoose Sep 4, 2024

Choose a reason for hiding this comment

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

The WebGPU allocates the buffer returned internally, we just pass the required size etc. We effectively cast it to the correct type on return, but can't provide a buffer to be used, I think to ensure that the buffer alignment and location within memory is suitable for efficient CPU <-> GPU transfers.

The buffer is created at

const readBuffer = device.createBuffer(

And made available to JS here:

const buffer = readBuffer.getMappedRange();

Copy link
Collaborator

Choose a reason for hiding this comment

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

Also is it worth creating a new issue to track this function signature difference?

Sounds good. Then we have a dedicated place to discuss this topic.

Copy link
Contributor

@aardgoose aardgoose Sep 4, 2024

Choose a reason for hiding this comment

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

If there is a use case that requires constant readback the recommended method would be to create a pool of WebGPU buffer objects in a ring buffer arrangement to avoid repeated allocations, something that doesn't match the current renderer API.

Copy link
Collaborator

@Mugen87 Mugen87 Sep 4, 2024

Choose a reason for hiding this comment

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

I have also realized that WebGPURenderer.readRenderTargetPixelsAsync() can not handle cube render targets so far since you can't assign an active cube face index. This is required for a bunch of use cases like e.g. in LightProbeGenerator.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'll take a look.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Relevant line:

renderer.readRenderTargetPixels( cubeRenderTarget, 0, 0, imageWidth, imageWidth, data, faceIndex );

@Mugen87 Mugen87 merged commit 428bab4 into mrdoob:dev Sep 4, 2024
12 checks passed
@gkjohnson gkjohnson deleted the read-rtt-pixels-fix branch September 4, 2024 12:48
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.

3 participants