sim2d/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, mesh::Mesh},
13    anyhow::{Context, Result},
14    ash::vk,
15    demo_vk::graphics::vulkan::{
16        Frame, FramesInFlight, VulkanContext, raii, spirv_words,
17    },
18    dynamic_buffer::DynamicBuffer,
19    material::Material,
20    std::sync::Arc,
21};
22
23pub use self::{
24    mesh::{TrianglesMesh, Vertex},
25    texture::{Texture, TextureAtlas, TextureLoader},
26};
27
28#[derive(Debug, Clone)]
29struct DrawParams {
30    index_offset: u32,
31    vertex_offset: u32,
32    index_count: u32,
33    material: Arc<Material>,
34    transform_index: u32,
35    scissor: vk::Rect2D,
36}
37
38#[repr(C, align(16))]
39#[derive(Debug, Copy, Clone)]
40struct MeshTransform {
41    matrix: [[f32; 4]; 4],
42}
43
44/// All of the resources required to assemble draw commands for a frame.
45///
46/// The renderer keeps one instance per frame-in-flight so resources can be
47/// updated freely while constructing the frame.
48struct FrameDraw {
49    vertex_buffer: DynamicBuffer<Vertex>,
50    index_buffer: DynamicBuffer<u32>,
51    transforms: DynamicBuffer<MeshTransform>,
52    draw_params: Vec<DrawParams>,
53}
54
55impl FrameDraw {
56    pub fn new(ctx: &VulkanContext) -> Result<Self> {
57        Ok(Self {
58            vertex_buffer: DynamicBuffer::new(
59                ctx,
60                INITIAL_CAPACITY,
61                vk::BufferUsageFlags::STORAGE_BUFFER
62                    | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS,
63            )?,
64            index_buffer: DynamicBuffer::new(
65                ctx,
66                INITIAL_CAPACITY,
67                vk::BufferUsageFlags::INDEX_BUFFER,
68            )?,
69            transforms: DynamicBuffer::new(
70                ctx,
71                INITIAL_CAPACITY,
72                vk::BufferUsageFlags::STORAGE_BUFFER
73                    | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS,
74            )?,
75            draw_params: Vec::with_capacity(4),
76        })
77    }
78}
79
80/// A renderer optimized for streaming new vertex data to the GPU every frame.
81pub struct StreamingRenderer<PerFrameDataT: Copy = ()> {
82    frame_draw_resources: Vec<FrameDraw>,
83    frame_constants: FrameConstants<PerFrameDataT>,
84
85    pipeline_layout: raii::PipelineLayout,
86
87    default_vertex_shader_module: raii::ShaderModule,
88    default_fragment_shader_module: raii::ShaderModule,
89    default_material: Arc<Material>,
90    image_format: vk::Format,
91}
92
93const INITIAL_CAPACITY: usize = 16_384;
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 default material for use by meshes without special material
201    /// requirements.
202    pub fn default_material(&self) -> &Arc<Material> {
203        &self.default_material
204    }
205
206    /// Prepares the meshes for this frame.
207    ///
208    /// This should only be called once per frame, calling it repeatedly will
209    /// only render whatever meshes were provided last.
210    pub fn prepare_meshes(
211        &mut self,
212        ctx: &VulkanContext,
213        frame: &Frame,
214        meshes: &[&dyn Mesh],
215    ) -> Result<()> {
216        let frame_draw = &mut self.frame_draw_resources[frame.frame_index()];
217        frame_draw.draw_params.clear();
218
219        // collect the vertex and index references and assemble the draw params
220        let (vertex_data, index_data) = {
221            let mut vertex_data = Vec::with_capacity(meshes.len());
222            let mut index_data = Vec::with_capacity(meshes.len());
223            let mut index_offset = 0;
224            let mut vertex_offset = 0;
225
226            for (transform_index, mesh) in meshes.iter().enumerate() {
227                let vertices = mesh.vertices();
228                let indices = mesh.indices();
229                vertex_data.push(vertices);
230                index_data.push(indices);
231
232                frame_draw.draw_params.push(DrawParams {
233                    index_offset,
234                    vertex_offset,
235                    index_count: indices.len() as u32,
236                    material: mesh.material().clone(),
237                    transform_index: transform_index as u32,
238                    scissor: mesh.scissor(),
239                });
240
241                index_offset += indices.len() as u32;
242                vertex_offset += vertices.len() as u32;
243            }
244
245            (vertex_data, index_data)
246        };
247
248        // write mesh data into frame-specific buffers
249        unsafe {
250            frame_draw
251                .vertex_buffer
252                .write_chunked_data(ctx, &vertex_data)
253                .context("Unable to write frame vertex data!")?;
254            frame_draw
255                .index_buffer
256                .write_chunked_data(ctx, &index_data)
257                .context("Unable to write index data!")?;
258            frame_draw.transforms.write_iterated_data(
259                ctx,
260                meshes.iter().map(|mesh| MeshTransform {
261                    matrix: mesh.transform().data.0,
262                }),
263            )?;
264        }
265
266        Ok(())
267    }
268
269    pub fn set_frame_constants(
270        &mut self,
271        frame: &Frame,
272        data: PerFrameDataT,
273    ) -> Result<()> {
274        self.frame_constants.set_data(frame, data)
275    }
276
277    /// Binds the texture atlas for the frame.
278    ///
279    /// This only needs to be done once a frame, regardless of how many meshes
280    /// there are as mesh pipelines are required to have compatible pipeline
281    /// layouts.
282    pub fn bind_texture_atlas(
283        &mut self,
284        ctx: &VulkanContext,
285        frame: &Frame,
286        texture_atlas: &TextureAtlas,
287    ) {
288        unsafe {
289            ctx.cmd_bind_descriptor_sets(
290                frame.command_buffer(),
291                vk::PipelineBindPoint::GRAPHICS,
292                self.pipeline_layout.raw,
293                0,
294                &[texture_atlas.descriptor_set()],
295                &[],
296            );
297        }
298    }
299
300    /// Emits draw commands for all of the meshes in the current frame.
301    ///
302    /// NOTE: it is incorrect to call this multiple times for the same frame as
303    ///       there is only one internal vertex buffer per frame.
304    pub fn write_draw_commands(
305        &mut self,
306        ctx: &VulkanContext,
307        frame: &Frame,
308    ) -> Result<()> {
309        let frame_draw = &mut self.frame_draw_resources[frame.frame_index()];
310        unsafe {
311            ctx.cmd_bind_descriptor_sets(
312                frame.command_buffer(),
313                vk::PipelineBindPoint::GRAPHICS,
314                self.pipeline_layout.raw,
315                1,
316                &[self.frame_constants.descriptor_set_for_frame(frame)],
317                &[],
318            );
319            ctx.cmd_bind_index_buffer(
320                frame.command_buffer(),
321                frame_draw.index_buffer.raw(),
322                0,
323                vk::IndexType::UINT32,
324            );
325            ctx.cmd_push_constants(
326                frame.command_buffer(),
327                self.pipeline_layout.raw,
328                vk::ShaderStageFlags::VERTEX,
329                0,
330                &frame_draw
331                    .vertex_buffer
332                    .buffer_device_address()
333                    .to_le_bytes(),
334            );
335            ctx.cmd_push_constants(
336                frame.command_buffer(),
337                self.pipeline_layout.raw,
338                vk::ShaderStageFlags::VERTEX,
339                8,
340                &frame_draw.transforms.buffer_device_address().to_le_bytes(),
341            );
342        }
343
344        let mut last_bound_pipeline = vk::Pipeline::null();
345        for draw_params in frame_draw.draw_params.drain(0..) {
346            // Bind the pipeline for the current draw, but only if its
347            // actually different from the most recently used pipeline.
348            let pipeline = draw_params.material.pipeline().raw;
349            if pipeline != last_bound_pipeline {
350                unsafe {
351                    ctx.cmd_bind_pipeline(
352                        frame.command_buffer(),
353                        vk::PipelineBindPoint::GRAPHICS,
354                        pipeline,
355                    );
356                }
357                last_bound_pipeline = pipeline;
358            }
359            unsafe {
360                ctx.cmd_push_constants(
361                    frame.command_buffer(),
362                    self.pipeline_layout.raw,
363                    vk::ShaderStageFlags::VERTEX,
364                    16,
365                    &draw_params.transform_index.to_le_bytes(),
366                );
367                ctx.cmd_set_scissor(
368                    frame.command_buffer(),
369                    0,
370                    &[draw_params.scissor],
371                );
372                ctx.cmd_draw_indexed(
373                    frame.command_buffer(),
374                    draw_params.index_count, // index count
375                    1,                       // instance count
376                    draw_params.index_offset, // first index
377                    draw_params.vertex_offset as i32, // vertex offset
378                    0,                       // first instance
379                );
380            }
381        }
382
383        Ok(())
384    }
385}