rendy_graph/node/render/group/
simple.rs

1use {
2    super::{RenderGroup, RenderGroupDesc},
3    crate::{
4        command::{QueueId, RenderPassEncoder},
5        factory::Factory,
6        graph::GraphContext,
7        node::{
8            render::PrepareResult, BufferAccess, DescBuilder, ImageAccess, NodeBuffer, NodeImage,
9        },
10        resource::{DescriptorSetLayout, Handle},
11    },
12    rendy_core::hal::{device::Device as _, Backend},
13};
14
15pub use crate::core::types::{Layout, SetLayout};
16
17/// Pipeline info
18#[derive(Clone, Debug)]
19pub struct Pipeline {
20    /// Layout for pipeline.
21    pub layout: Layout,
22
23    /// Vertex input for pipeline.
24    pub vertices: Vec<(
25        Vec<rendy_core::hal::pso::Element<rendy_core::hal::format::Format>>,
26        rendy_core::hal::pso::ElemStride,
27        rendy_core::hal::pso::VertexInputRate,
28    )>,
29
30    /// Colors for pipeline.
31    pub colors: Vec<rendy_core::hal::pso::ColorBlendDesc>,
32
33    /// Depth stencil for pipeline.
34    pub depth_stencil: rendy_core::hal::pso::DepthStencilDesc,
35
36    /// Rasterizer for pipeline.
37    pub rasterizer: rendy_core::hal::pso::Rasterizer,
38
39    /// Primitive to use in the input assembler.
40    pub input_assembler_desc: rendy_core::hal::pso::InputAssemblerDesc,
41}
42
43/// Descriptor for simple graphics pipeline implementation.
44pub trait SimpleGraphicsPipelineDesc<B: Backend, T: ?Sized>: std::fmt::Debug {
45    /// Simple graphics pipeline implementation
46    type Pipeline: SimpleGraphicsPipeline<B, T>;
47
48    /// Make simple render group builder.
49    fn builder(self) -> DescBuilder<B, T, SimpleRenderGroupDesc<Self>>
50    where
51        Self: Sized,
52    {
53        SimpleRenderGroupDesc { inner: self }.builder()
54    }
55
56    /// Get set or buffer resources the node uses.
57    fn buffers(&self) -> Vec<BufferAccess> {
58        Vec::new()
59    }
60
61    /// Get set or image resources the node uses.
62    fn images(&self) -> Vec<ImageAccess> {
63        Vec::new()
64    }
65
66    /// Color blend descs.
67    fn colors(&self) -> Vec<rendy_core::hal::pso::ColorBlendDesc> {
68        vec![rendy_core::hal::pso::ColorBlendDesc {
69            mask: rendy_core::hal::pso::ColorMask::ALL,
70            blend: Some(rendy_core::hal::pso::BlendState::ALPHA),
71        }]
72    }
73
74    /// Depth stencil desc.
75    fn depth_stencil(&self) -> Option<rendy_core::hal::pso::DepthStencilDesc> {
76        Some(rendy_core::hal::pso::DepthStencilDesc {
77            depth: Some(rendy_core::hal::pso::DepthTest {
78                fun: rendy_core::hal::pso::Comparison::Less,
79                write: true,
80            }),
81            depth_bounds: false,
82            stencil: None,
83        })
84    }
85
86    /// Rasterizer desc.
87    fn rasterizer(&self) -> rendy_core::hal::pso::Rasterizer {
88        rendy_core::hal::pso::Rasterizer::FILL
89    }
90
91    /// Get vertex input.
92    fn vertices(
93        &self,
94    ) -> Vec<(
95        Vec<rendy_core::hal::pso::Element<rendy_core::hal::format::Format>>,
96        rendy_core::hal::pso::ElemStride,
97        rendy_core::hal::pso::VertexInputRate,
98    )> {
99        Vec::new()
100    }
101
102    /// Layout for graphics pipeline
103    /// Default implementation for `pipeline` will use this.
104    fn layout(&self) -> Layout {
105        Layout {
106            sets: Vec::new(),
107            push_constants: Vec::new(),
108        }
109    }
110
111    /// Returns the InputAssemblerDesc. Defaults to a TriangleList with Restart disabled, can be overriden.
112    fn input_assembler(&self) -> rendy_core::hal::pso::InputAssemblerDesc {
113        rendy_core::hal::pso::InputAssemblerDesc {
114            primitive: rendy_core::hal::pso::Primitive::TriangleList,
115            with_adjacency: false,
116            restart_index: None,
117        }
118    }
119
120    /// Graphics pipelines
121    fn pipeline(&self) -> Pipeline {
122        Pipeline {
123            layout: self.layout(),
124            vertices: self.vertices(),
125            colors: self.colors(),
126            depth_stencil: self
127                .depth_stencil()
128                .unwrap_or(rendy_core::hal::pso::DepthStencilDesc::default()),
129            rasterizer: self.rasterizer(),
130            input_assembler_desc: self.input_assembler(),
131        }
132    }
133
134    /// Load shader set.
135    /// This function should utilize the provided `ShaderSetBuilder` reflection class and return the compiled `ShaderSet`.
136    ///
137    /// # Parameters
138    ///
139    /// `factory`   - factory to create shader modules.
140    ///
141    /// `aux`       - auxiliary data container. May be anything the implementation desires.
142    ///
143    fn load_shader_set(&self, factory: &mut Factory<B>, aux: &T) -> rendy_shader::ShaderSet<B>;
144
145    /// Build pass instance.
146    fn build<'a>(
147        self,
148        ctx: &GraphContext<B>,
149        factory: &mut Factory<B>,
150        queue: QueueId,
151        aux: &T,
152        buffers: Vec<NodeBuffer>,
153        images: Vec<NodeImage>,
154        set_layouts: &[Handle<DescriptorSetLayout<B>>],
155    ) -> Result<Self::Pipeline, rendy_core::hal::pso::CreationError>;
156}
157
158/// Simple render pipeline.
159pub trait SimpleGraphicsPipeline<B: Backend, T: ?Sized>:
160    std::fmt::Debug + Sized + Send + Sync + 'static
161{
162    /// This pipeline descriptor.
163    type Desc: SimpleGraphicsPipelineDesc<B, T, Pipeline = Self>;
164
165    /// Make simple render group builder.
166    fn builder() -> DescBuilder<B, T, SimpleRenderGroupDesc<Self::Desc>>
167    where
168        Self::Desc: Default,
169    {
170        Self::Desc::default().builder()
171    }
172
173    /// Prepare to record drawing commands.
174    ///
175    /// Should return true if commands must be re-recorded.
176    fn prepare(
177        &mut self,
178        _factory: &Factory<B>,
179        _queue: QueueId,
180        _set_layouts: &[Handle<DescriptorSetLayout<B>>],
181        _index: usize,
182        _aux: &T,
183    ) -> PrepareResult {
184        PrepareResult::DrawRecord
185    }
186
187    /// Record drawing commands to the command buffer provided.
188    fn draw(
189        &mut self,
190        layout: &B::PipelineLayout,
191        encoder: RenderPassEncoder<'_, B>,
192        index: usize,
193        aux: &T,
194    );
195
196    /// Free all resources and destroy pipeline instance.
197    fn dispose(self, factory: &mut Factory<B>, aux: &T);
198}
199
200/// Render group that consist of simple graphics pipeline.
201#[derive(Debug)]
202pub struct SimpleRenderGroup<B: Backend, P> {
203    set_layouts: Vec<Handle<DescriptorSetLayout<B>>>,
204    pipeline_layout: B::PipelineLayout,
205    graphics_pipeline: B::GraphicsPipeline,
206    pipeline: P,
207}
208
209/// Descriptor for simple render group.
210#[derive(Debug)]
211pub struct SimpleRenderGroupDesc<P: std::fmt::Debug> {
212    inner: P,
213}
214
215impl<B, T, P> RenderGroupDesc<B, T> for SimpleRenderGroupDesc<P>
216where
217    B: Backend,
218    T: ?Sized,
219    P: SimpleGraphicsPipelineDesc<B, T>,
220{
221    fn buffers(&self) -> Vec<BufferAccess> {
222        self.inner.buffers()
223    }
224
225    fn images(&self) -> Vec<ImageAccess> {
226        self.inner.images()
227    }
228
229    fn colors(&self) -> usize {
230        self.inner.colors().len()
231    }
232
233    fn depth(&self) -> bool {
234        self.inner.depth_stencil().is_some()
235    }
236
237    fn build<'a>(
238        self,
239        ctx: &GraphContext<B>,
240        factory: &mut Factory<B>,
241        queue: QueueId,
242        aux: &T,
243        framebuffer_width: u32,
244        framebuffer_height: u32,
245        subpass: rendy_core::hal::pass::Subpass<'_, B>,
246        buffers: Vec<NodeBuffer>,
247        images: Vec<NodeImage>,
248    ) -> Result<Box<dyn RenderGroup<B, T>>, rendy_core::hal::pso::CreationError> {
249        log::trace!("Load shader sets for");
250
251        let mut shader_set = self.inner.load_shader_set(factory, aux);
252
253        let pipeline = self.inner.pipeline();
254
255        let set_layouts = pipeline
256            .layout
257            .sets
258            .into_iter()
259            .map(|set| {
260                factory
261                    .create_descriptor_set_layout(set.bindings)
262                    .map(Handle::from)
263            })
264            .collect::<Result<Vec<_>, _>>()
265            .map_err(|e| {
266                shader_set.dispose(factory);
267                e
268            })?;
269
270        let pipeline_layout = unsafe {
271            factory.device().create_pipeline_layout(
272                set_layouts.iter().map(|l| l.raw()),
273                pipeline.layout.push_constants,
274            )
275        }
276        .map_err(|e| {
277            shader_set.dispose(factory);
278            rendy_core::hal::pso::CreationError::OutOfMemory(e)
279        })?;
280
281        assert_eq!(pipeline.colors.len(), self.inner.colors().len());
282
283        let mut vertex_buffers = Vec::new();
284        let mut attributes = Vec::new();
285
286        for &(ref elemets, stride, rate) in &pipeline.vertices {
287            push_vertex_desc(elemets, stride, rate, &mut vertex_buffers, &mut attributes);
288        }
289
290        let rect = rendy_core::hal::pso::Rect {
291            x: 0,
292            y: 0,
293            w: framebuffer_width as i16,
294            h: framebuffer_height as i16,
295        };
296
297        let shaders = match shader_set.raw() {
298            Err(e) => {
299                shader_set.dispose(factory);
300                log::warn!("Shader error {:?}", e);
301                return Err(rendy_core::hal::pso::CreationError::Other);
302            }
303            Ok(s) => s,
304        };
305
306        let graphics_pipeline = unsafe {
307            factory.device().create_graphics_pipelines(
308                Some(rendy_core::hal::pso::GraphicsPipelineDesc {
309                    shaders,
310                    rasterizer: pipeline.rasterizer,
311                    vertex_buffers,
312                    attributes,
313                    input_assembler: pipeline.input_assembler_desc,
314                    blender: rendy_core::hal::pso::BlendDesc {
315                        logic_op: None,
316                        targets: pipeline.colors.clone(),
317                    },
318                    depth_stencil: pipeline.depth_stencil,
319                    multisampling: None,
320                    baked_states: rendy_core::hal::pso::BakedStates {
321                        viewport: Some(rendy_core::hal::pso::Viewport {
322                            rect,
323                            depth: 0.0..1.0,
324                        }),
325                        scissor: Some(rect),
326                        blend_color: None,
327                        depth_bounds: None,
328                    },
329                    layout: &pipeline_layout,
330                    subpass,
331                    flags: rendy_core::hal::pso::PipelineCreationFlags::empty(),
332                    parent: rendy_core::hal::pso::BasePipeline::None,
333                }),
334                None,
335            )
336        }
337        .remove(0)
338        .map_err(|e| {
339            shader_set.dispose(factory);
340            e
341        })?;
342
343        let pipeline = self
344            .inner
345            .build(ctx, factory, queue, aux, buffers, images, &set_layouts)
346            .map_err(|e| {
347                shader_set.dispose(factory);
348                e
349            })?;
350
351        shader_set.dispose(factory);
352
353        Ok(Box::new(SimpleRenderGroup::<B, _> {
354            set_layouts,
355            pipeline_layout,
356            graphics_pipeline,
357            pipeline,
358        }))
359    }
360}
361
362impl<B, T, P> RenderGroup<B, T> for SimpleRenderGroup<B, P>
363where
364    B: Backend,
365    T: ?Sized,
366    P: SimpleGraphicsPipeline<B, T>,
367{
368    fn prepare(
369        &mut self,
370        factory: &Factory<B>,
371        queue: QueueId,
372        index: usize,
373        _subpass: rendy_core::hal::pass::Subpass<'_, B>,
374        aux: &T,
375    ) -> PrepareResult {
376        self.pipeline
377            .prepare(factory, queue, &self.set_layouts, index, aux)
378    }
379
380    fn draw_inline(
381        &mut self,
382        mut encoder: RenderPassEncoder<'_, B>,
383        index: usize,
384        _subpass: rendy_core::hal::pass::Subpass<'_, B>,
385        aux: &T,
386    ) {
387        encoder.bind_graphics_pipeline(&self.graphics_pipeline);
388        self.pipeline
389            .draw(&self.pipeline_layout, encoder, index, aux);
390    }
391
392    fn dispose(self: Box<Self>, factory: &mut Factory<B>, aux: &T) {
393        self.pipeline.dispose(factory, aux);
394
395        unsafe {
396            factory
397                .device()
398                .destroy_graphics_pipeline(self.graphics_pipeline);
399            factory
400                .device()
401                .destroy_pipeline_layout(self.pipeline_layout);
402            drop(self.set_layouts);
403        }
404    }
405}
406
407fn push_vertex_desc(
408    elements: &[rendy_core::hal::pso::Element<rendy_core::hal::format::Format>],
409    stride: rendy_core::hal::pso::ElemStride,
410    rate: rendy_core::hal::pso::VertexInputRate,
411    vertex_buffers: &mut Vec<rendy_core::hal::pso::VertexBufferDesc>,
412    attributes: &mut Vec<rendy_core::hal::pso::AttributeDesc>,
413) {
414    let index = vertex_buffers.len() as rendy_core::hal::pso::BufferIndex;
415
416    vertex_buffers.push(rendy_core::hal::pso::VertexBufferDesc {
417        binding: index,
418        stride,
419        rate,
420    });
421
422    let mut location = attributes.last().map_or(0, |a| a.location + 1);
423    for &element in elements {
424        attributes.push(rendy_core::hal::pso::AttributeDesc {
425            location,
426            binding: index,
427            element,
428        });
429        location += 1;
430    }
431}