demo_vk/graphics/streaming_renderer/
mod.rs

1//! Mesh, Material, Texture, and pipeline resources for rendering geometry
2//! that's expected to change every frame.
3
4mod dynamic_buffer;
5mod frame_constants;
6mod material;
7mod mesh;
8mod texture;
9pub(crate) mod utility;
10
11use {
12    self::frame_constants::FrameConstants,
13    crate::graphics::vulkan::{
14        raii, spirv_words, Frame, FramesInFlight, VulkanContext,
15    },
16    anyhow::{Context, Result},
17    ash::vk,
18    dynamic_buffer::DynamicBuffer,
19    material::Material,
20    std::sync::Arc,
21};
22
23pub use self::{
24    mesh::{Mesh, TrianglesMesh, Vertex},
25    texture::{Texture, TextureAtlas, TextureLoader},
26};
27
28const INITIAL_CAPACITY: usize = 4096;
29
30#[derive(Debug, Clone)]
31struct DrawParams {
32    index_offset: u32,
33    vertex_offset: u32,
34    index_count: u32,
35    material: Arc<Material>,
36    transform_index: u32,
37    scissor: vk::Rect2D,
38}
39
40#[repr(C, align(16))]
41#[derive(Debug, Copy, Clone)]
42struct MeshTransform {
43    matrix: [[f32; 4]; 4],
44}
45
46/// All of the resources required to assemble draw commands for a frame.
47///
48/// The renderer keeps one instance per frame-in-flight so resources can be
49/// updated freely while constructing the frame.
50struct FrameDraw {
51    vertex_buffer: DynamicBuffer<Vertex>,
52    index_buffer: DynamicBuffer<u32>,
53    transforms: DynamicBuffer<MeshTransform>,
54    draw_params: Vec<DrawParams>,
55}
56
57impl FrameDraw {
58    pub fn new(ctx: &VulkanContext) -> Result<Self> {
59        Ok(Self {
60            vertex_buffer: DynamicBuffer::new(
61                ctx,
62                INITIAL_CAPACITY,
63                vk::BufferUsageFlags::STORAGE_BUFFER
64                    | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS,
65            )?,
66            index_buffer: DynamicBuffer::new(
67                ctx,
68                INITIAL_CAPACITY,
69                vk::BufferUsageFlags::INDEX_BUFFER,
70            )?,
71            transforms: DynamicBuffer::new(
72                ctx,
73                INITIAL_CAPACITY,
74                vk::BufferUsageFlags::STORAGE_BUFFER
75                    | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS,
76            )?,
77            draw_params: Vec::with_capacity(4),
78        })
79    }
80}
81
82/// A renderer optimized for streaming new vertex data to the GPU every frame.
83pub struct StreamingRenderer<PerFrameDataT: Copy = ()> {
84    frame_draw_resources: Vec<FrameDraw>,
85    frame_constants: FrameConstants<PerFrameDataT>,
86
87    pipeline_layout: raii::PipelineLayout,
88
89    default_vertex_shader_module: raii::ShaderModule,
90    default_fragment_shader_module: raii::ShaderModule,
91    default_material: Arc<Material>,
92    image_format: vk::Format,
93}
94
95impl<PerFrameDataT: Copy> StreamingRenderer<PerFrameDataT> {
96    pub fn new(
97        ctx: &VulkanContext,
98        image_format: vk::Format,
99        frames_in_flight: &FramesInFlight,
100        texture_atlas: &TextureAtlas,
101    ) -> Result<Self> {
102        let frame_constants = FrameConstants::new(ctx, frames_in_flight)
103            .context("Unable to create FrameData instance")?;
104
105        // create pipeline resources
106        let pipeline_layout = Material::create_pipeline_layout(
107            ctx,
108            texture_atlas.descriptor_set_layout(),
109            frame_constants.descriptor_set_layout(),
110        )
111        .context("Unable to create pipeline layout")?;
112
113        let frame_draw_resources = {
114            let mut resources =
115                Vec::with_capacity(frames_in_flight.frame_count());
116            for frame_index in 0..frames_in_flight.frame_count() {
117                resources.push(FrameDraw::new(ctx).context(format!(
118                    "Unable to create draw resources for frame {}",
119                    frame_index
120                ))?);
121            }
122            resources
123        };
124
125        let default_vertex_shader_module = {
126            let vertex_shader_words =
127                spirv_words(include_bytes!("./shaders/triangle.vert.spv"))
128                    .context("Unable to pack default vertex shader source")?;
129            raii::ShaderModule::new(
130                "DefaultVertexShader",
131                ctx.device.clone(),
132                &vk::ShaderModuleCreateInfo {
133                    code_size: vertex_shader_words.len() * 4,
134                    p_code: vertex_shader_words.as_ptr(),
135                    ..Default::default()
136                },
137            )
138            .context("Unable to create default vertex shader module")?
139        };
140        let default_fragment_shader_module = {
141            let fragment_shader_words =
142                spirv_words(include_bytes!("./shaders/triangle.frag.spv"))
143                    .context("Unable to pack default fragment shader source")?;
144            raii::ShaderModule::new(
145                "DefaultFragmentShader",
146                ctx.device.clone(),
147                &vk::ShaderModuleCreateInfo {
148                    code_size: fragment_shader_words.len() * 4,
149                    p_code: fragment_shader_words.as_ptr(),
150                    ..Default::default()
151                },
152            )
153            .context("Unable to create default fragment shader module")?
154        };
155        let default_material = Arc::new(
156            Material::new(
157                ctx,
158                image_format,
159                &pipeline_layout,
160                &default_vertex_shader_module,
161                &default_fragment_shader_module,
162            )
163            .context("Unable to create default material")?,
164        );
165
166        Ok(Self {
167            frame_draw_resources,
168            frame_constants,
169
170            pipeline_layout,
171            default_vertex_shader_module,
172            default_fragment_shader_module,
173            default_material,
174            image_format,
175        })
176    }
177
178    /// Creates a new rendering material. See the documentation for [Material]
179    /// for details on allowed shader inputs and outputs.
180    ///
181    /// Default vertex and fragment shaders are used automatically if either
182    /// is omitted.
183    pub fn new_material(
184        &self,
185        ctx: &VulkanContext,
186        vertex_shader: Option<&raii::ShaderModule>,
187        fragment_shader: Option<&raii::ShaderModule>,
188    ) -> Result<Arc<Material>> {
189        let material = Material::new(
190            ctx,
191            self.image_format,
192            &self.pipeline_layout,
193            vertex_shader.unwrap_or(&self.default_vertex_shader_module),
194            fragment_shader.unwrap_or(&self.default_fragment_shader_module),
195        )
196        .context("Unable to create new material!")?;
197        Ok(Arc::new(material))
198    }
199
200    /// Returns the image format this renderer is compatible with.
201    pub fn image_format(&self) -> vk::Format {
202        self.image_format
203    }
204
205    /// Returns the default material for use by meshes without special material
206    /// requirements.
207    pub fn default_material(&self) -> &Arc<Material> {
208        &self.default_material
209    }
210
211    /// Prepares the meshes for this frame.
212    ///
213    /// This should only be called once per frame, calling it repeatedly will
214    /// only render whatever meshes were provided last.
215    pub fn prepare_meshes(
216        &mut self,
217        ctx: &VulkanContext,
218        frame: &Frame,
219        meshes: &[&dyn Mesh],
220    ) -> Result<()> {
221        let frame_draw = &mut self.frame_draw_resources[frame.frame_index()];
222        frame_draw.draw_params.clear();
223
224        // collect the vertex and index references and assemble the draw params
225        let (vertex_data, index_data) = {
226            let mut vertex_data = Vec::with_capacity(meshes.len());
227            let mut index_data = Vec::with_capacity(meshes.len());
228            let mut index_offset = 0;
229            let mut vertex_offset = 0;
230
231            for (transform_index, mesh) in meshes.iter().enumerate() {
232                let vertices = mesh.vertices();
233                let indices = mesh.indices();
234                vertex_data.push(vertices);
235                index_data.push(indices);
236
237                frame_draw.draw_params.push(DrawParams {
238                    index_offset,
239                    vertex_offset,
240                    index_count: indices.len() as u32,
241                    material: mesh.material().clone(),
242                    transform_index: transform_index as u32,
243                    scissor: mesh.scissor(),
244                });
245
246                index_offset += indices.len() as u32;
247                vertex_offset += vertices.len() as u32;
248            }
249
250            (vertex_data, index_data)
251        };
252
253        // write mesh data into frame-specific buffers
254        unsafe {
255            frame_draw
256                .vertex_buffer
257                .write_chunked_data(ctx, &vertex_data)
258                .context("Unable to write frame vertex data!")?;
259            frame_draw
260                .index_buffer
261                .write_chunked_data(ctx, &index_data)
262                .context("Unable to write index data!")?;
263            frame_draw.transforms.write_iterated_data(
264                ctx,
265                meshes.iter().map(|mesh| MeshTransform {
266                    matrix: mesh.transform().data.0,
267                }),
268            )?;
269        }
270
271        Ok(())
272    }
273
274    pub fn set_frame_constants(
275        &mut self,
276        frame: &Frame,
277        data: PerFrameDataT,
278    ) -> Result<()> {
279        self.frame_constants.set_data(frame, data)
280    }
281
282    /// Binds the texture atlas for the frame.
283    ///
284    /// This only needs to be done once a frame, regardless of how many meshes
285    /// there are as mesh pipelines are required to have compatible pipeline
286    /// layouts.
287    pub fn bind_texture_atlas(
288        &mut self,
289        ctx: &VulkanContext,
290        frame: &Frame,
291        texture_atlas: &TextureAtlas,
292    ) {
293        unsafe {
294            ctx.cmd_bind_descriptor_sets(
295                frame.command_buffer(),
296                vk::PipelineBindPoint::GRAPHICS,
297                self.pipeline_layout.raw,
298                0,
299                &[texture_atlas.descriptor_set()],
300                &[],
301            );
302        }
303    }
304
305    /// Emits draw commands for all of the meshes in the current frame.
306    ///
307    /// NOTE: it is incorrect to call this multiple times for the same frame as
308    ///       there is only one internal vertex buffer per frame.
309    pub fn write_draw_commands(
310        &mut self,
311        ctx: &VulkanContext,
312        frame: &Frame,
313    ) -> Result<()> {
314        let frame_draw = &mut self.frame_draw_resources[frame.frame_index()];
315        unsafe {
316            ctx.cmd_bind_descriptor_sets(
317                frame.command_buffer(),
318                vk::PipelineBindPoint::GRAPHICS,
319                self.pipeline_layout.raw,
320                1,
321                &[self.frame_constants.descriptor_set_for_frame(frame)],
322                &[],
323            );
324            ctx.cmd_bind_index_buffer(
325                frame.command_buffer(),
326                frame_draw.index_buffer.raw(),
327                0,
328                vk::IndexType::UINT32,
329            );
330            ctx.cmd_push_constants(
331                frame.command_buffer(),
332                self.pipeline_layout.raw,
333                vk::ShaderStageFlags::VERTEX,
334                0,
335                &frame_draw
336                    .vertex_buffer
337                    .buffer_device_address()
338                    .to_le_bytes(),
339            );
340            ctx.cmd_push_constants(
341                frame.command_buffer(),
342                self.pipeline_layout.raw,
343                vk::ShaderStageFlags::VERTEX,
344                8,
345                &frame_draw.transforms.buffer_device_address().to_le_bytes(),
346            );
347        }
348
349        let mut last_bound_pipeline = vk::Pipeline::null();
350        for draw_params in frame_draw.draw_params.drain(0..) {
351            // Bind the pipeline for the current draw, but only if its
352            // actually different from the most recently used pipeline.
353            let pipeline = draw_params.material.pipeline().raw;
354            if pipeline != last_bound_pipeline {
355                unsafe {
356                    ctx.cmd_bind_pipeline(
357                        frame.command_buffer(),
358                        vk::PipelineBindPoint::GRAPHICS,
359                        pipeline,
360                    );
361                }
362                last_bound_pipeline = pipeline;
363            }
364            unsafe {
365                ctx.cmd_push_constants(
366                    frame.command_buffer(),
367                    self.pipeline_layout.raw,
368                    vk::ShaderStageFlags::VERTEX,
369                    16,
370                    &draw_params.transform_index.to_le_bytes(),
371                );
372                ctx.cmd_set_scissor(
373                    frame.command_buffer(),
374                    0,
375                    &[draw_params.scissor],
376                );
377                ctx.cmd_draw_indexed(
378                    frame.command_buffer(),
379                    draw_params.index_count, // index count
380                    1,                       // instance count
381                    draw_params.index_offset, // first index
382                    draw_params.vertex_offset as i32, // vertex offset
383                    0,                       // first instance
384                );
385            }
386        }
387
388        Ok(())
389    }
390}