-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
RenderTask #21603
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
base: main
Are you sure you want to change the base?
RenderTask #21603
Conversation
|
Is ordering always (before, myself, after)? If so is it enough to specify before and after? |
I suppose yeah, but before/after can be more than 1 thing. |
|
I think @torsteingrindvik was suggesting having type AfterNodeLabel = Node3d::EndPrepasses;
type RenderNodeLabel = node::graph::SolariLightingNode;
type BeforeNodeLabel = Node3d::EndMainPass;
// this is the same for all RenderTasks
fn render_node_ordering() -> impl IntoRenderNodeArray {
(
Self::AfterNodeLabel,
Self::RenderNodeLabel,
Self::BeforeNodeLabel,
)
} |
|
No yeah I get the idea. I just don't think we can do that because you could want more than 1 thing before/after. |
Further doc work
|
Need to review more closely, but I'm a little on the fence about this. On the one hand, I've wanted to implement something exactly this shape before for similar reasons. But I've hesitated in the past because this kind of approach feels a bit like patching over fundamental deficiencies of our render graph architecture rather than a real solution. I worry that these kinds of trait based solutions are brittle and often do not reduce as much boilerplate as we would like. I've often had the feeling when working with ECS that things can be ugly but them being ad-hoc is still preferable to being formalized prematurely into a clunky abstraction, which is what I worry about here. |
Same. Adding a new layer of abstraction should be a method of last resort, and I'm not (yet) convinced this is a net win. It would be helpful to port a few Bevy features over in this PR to illustrate the value here (and also show that the abstraction is functional). |
| todo!() | ||
| // self.bind_groups | ||
| // .entry(descriptor.clone()) | ||
| // .or_insert_with(|| render_device.wgpu_device().create_bind_group(&descriptor)) | ||
| // .clone() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't implemented caching for bind groups yet. Tricky to figure out how to do the hashing.
| textures: HashMap<(Entity, TextureDescriptor<'static>), TextureView>, | ||
| buffers: HashMap<(Entity, BufferDescriptor<'static>), Buffer>, | ||
| _bind_groups: HashMap<BindGroupDescriptor<'static>, BindGroup>, | ||
| compute_pipelines: HashMap<ComputePipelineDescriptor, CachedComputePipelineId>, | ||
| render_pipelines: HashMap<RenderPipelineDescriptor, CachedRenderPipelineId>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if some of these should be separate / global and not constrained to one render task instance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They could be, but I don't see much point in it, and it would reduce parallelism unless we used a parallel hashmap.
|
|
||
| /// Begin a new render pass. | ||
| pub fn render_pass(&mut self) { | ||
| todo!() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
D:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's easy enough to add later, but I wanted to focus on compute first.
| pub struct RenderTaskContext<'a> { | ||
| camera_entity: Entity, | ||
| command_encoder: &'a mut CommandEncoder, | ||
| compute_pass: Option<ComputePass<'static>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That this is 'static looks surprising to me. It feels like it should have its own lifetime that is bound to the lifetime of the CommandEncoder, which is 'a?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a little bit of a hack because I can't express the lifetime properly. Notice that I used forget_lifetime.
| /// Corresponds to `var<storage, read> my_buffer: T` in a WGSL shader. | ||
| #[derive(Clone, Deref)] | ||
| pub struct StorageBufferReadOnly<'a>(pub &'a Buffer); | ||
|
|
||
| /// Corresponds to `var<storage, read_write> my_buffer: T` in a WGSL shader. | ||
| #[derive(Clone, Deref)] | ||
| pub struct StorageBufferReadWrite<'a>(pub &'a Buffer); | ||
|
|
||
| /// Corresponds to `var my_texture: texture_2d<T>` in a WGSL shader. | ||
| #[derive(Clone, Deref)] | ||
| pub struct SampledTexture<'a>(pub &'a TextureView); | ||
|
|
||
| /// Corresponds to `var my_texture: texture_storage_2d<F, read_write>` in a WGSL shader. | ||
| #[derive(Clone, Deref)] | ||
| pub struct StorageTextureReadWrite<'a>(pub &'a TextureView); | ||
|
|
||
| /// Corresponds to `var my_texture: texture_storage_2d<F, write>` in a WGSL shader. | ||
| #[derive(Clone, Deref)] | ||
| pub struct StorageTextureWriteOnly<'a>(pub &'a TextureView); | ||
|
|
||
| /// Corresponds to `var my_texture: texture_storage_2d<F, read>` in a WGSL shader. | ||
| #[derive(Clone, Deref)] | ||
| pub struct StorageTextureReadOnly<'a>(pub &'a TextureView); | ||
|
|
||
| /// Corresponds to `var my_texture: texture_storage_2d<F, atomic>` in a WGSL shader. | ||
| #[derive(Clone, Deref)] | ||
| pub struct StorageTextureAtomic<'a>(pub &'a TextureView); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not keen on this. Adding separate types for all combinations of buffer and texture binding types feel 'undesirable'?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why so? You need to specify the bindings types somewhere.
| pub fn dispatch_1d(mut self, x: u32) -> Option<Self> { | ||
| self.setup_state()?; | ||
| self.pass.dispatch_workgroups(x, 1, 1); | ||
| Some(self) | ||
| } | ||
|
|
||
| pub fn dispatch_2d(mut self, x: u32, y: u32) -> Option<Self> { | ||
| self.setup_state()?; | ||
| self.pass.dispatch_workgroups(x, y, 1); | ||
| Some(self) | ||
| } | ||
|
|
||
| pub fn dispatch_3d(mut self, x: u32, y: u32, z: u32) -> Option<Self> { | ||
| self.setup_state()?; | ||
| self.pass.dispatch_workgroups(x, y, z); | ||
| Some(self) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there much value in these three instead of just dispatch_3d called dispatch?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean sure, but I think it's nice compared to doing dispatch(x, y, 1).
| use bevy_camera::Camera; | ||
| use bevy_ecs::system::{Commands, Query}; | ||
|
|
||
| // TODO: Use SyncComponentPlugin or ExtractComponentPlugin? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm 90% sure that ExtractComponentPlugin handles everything you're trying to do here. Including handling when a Camera is removed.
Maybe not mutating T in the main world, but then again. I'm not quite sure of all the things you're trying to do with RenderTask. If you have more precise questions, I think I can answer them though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I think you're right. And I think I can get away with not needing to handle main world mutations if I move temporal reset tracking to a dedicated component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Except handling camera.is_active, actually.
WIP RenderTask
Objective
MaterialandFullscreenMaterialSolution
Testing
Showcase