grr_imgui/
lib.rs

1const VERTEX_SRC: &str = r#"
2    #version 450 core
3    layout (location = 0) in vec2 v_pos;
4    layout (location = 1) in vec2 v_uv;
5    layout (location = 2) in vec4 v_color;
6
7    layout (location = 0) out vec2 a_uv;
8    layout (location = 1) out vec4 a_color;
9
10    layout (location = 0) uniform mat4 u_transform;
11
12    void main() {
13        a_uv = v_uv;
14        a_color = v_color;
15        gl_Position = u_transform * vec4(v_pos, 0.0, 1.0);
16    }
17"#;
18
19const FRAGMENT_SRC: &str = r#"
20    #version 450 core
21    layout (location = 0) in vec2 a_uv;
22    layout (location = 1) in vec4 a_color;
23
24    layout (location = 0) out vec4 f_color;
25
26    layout (binding = 0) uniform sampler2D u_texture;
27
28    void main() {
29       f_color = a_color * texture(u_texture, a_uv);
30    }
31"#;
32
33pub struct Renderer<'grr> {
34    device: &'grr grr::Device,
35    pipeline: grr::Pipeline,
36    textures: imgui::Textures<(grr::Image, grr::ImageView, grr::Sampler)>,
37    vertex_array: grr::VertexArray,
38}
39
40impl<'grr> Renderer<'grr> {
41    /// Create a new Rendered for an Imgui context.
42    ///
43    /// # Safety
44    ///
45    /// This function directly calls multiple unsafe `grr::Device`
46    /// calls.
47    pub unsafe fn new(
48        imgui: &mut imgui::Context,
49        grr: &'grr grr::Device,
50    ) -> Result<Self, grr::Error> {
51        {
52            // Fix incorrect colors with sRGB framebuffer
53            fn imgui_gamma_to_linear(col: [f32; 4]) -> [f32; 4] {
54                let x = col[0].powf(2.2);
55                let y = col[1].powf(2.2);
56                let z = col[2].powf(2.2);
57                let w = 1.0 - (1.0 - col[3]).powf(2.2);
58                [x, y, z, w]
59            }
60
61            let style = imgui.style_mut();
62            for col in 0..style.colors.len() {
63                style.colors[col] = imgui_gamma_to_linear(style.colors[col]);
64            }
65        }
66
67        let vs = grr.create_shader(
68            grr::ShaderStage::Vertex,
69            VERTEX_SRC.as_bytes(),
70            grr::ShaderFlags::empty(),
71        )?;
72        let fs = grr.create_shader(
73            grr::ShaderStage::Fragment,
74            FRAGMENT_SRC.as_bytes(),
75            grr::ShaderFlags::empty(),
76        )?;
77
78        let pipeline = grr.create_graphics_pipeline(
79            grr::VertexPipelineDesc {
80                vertex_shader: vs,
81                tessellation_control_shader: None,
82                tessellation_evaluation_shader: None,
83                geometry_shader: None,
84                fragment_shader: Some(fs),
85            },
86            grr::PipelineFlags::empty(),
87        )?;
88
89        let mut textures = imgui::Textures::new();
90        let mut fonts = imgui.fonts();
91        let image = {
92            let texture = fonts.build_rgba32_texture();
93            let image = grr
94                .create_image(
95                    grr::ImageType::D2 {
96                        width: texture.width,
97                        height: texture.height,
98                        layers: 1,
99                        samples: 1,
100                    },
101                    grr::Format::R8G8B8A8_SRGB,
102                    1,
103                )
104                .unwrap();
105            grr.object_name(image, "imgui-texture");
106            grr.copy_host_to_image(
107                &texture.data,
108                image,
109                grr::HostImageCopy {
110                    host_layout: grr::MemoryLayout {
111                        base_format: grr::BaseFormat::RGBA,
112                        format_layout: grr::FormatLayout::U8,
113                        row_length: texture.width,
114                        image_height: texture.height,
115                        alignment: 4,
116                    },
117                    image_subresource: grr::SubresourceLayers {
118                        level: 0,
119                        layers: 0..1,
120                    },
121                    image_offset: grr::Offset { x: 0, y: 0, z: 0 },
122                    image_extent: grr::Extent {
123                        width: texture.width,
124                        height: texture.height,
125                        depth: 1,
126                    },
127                },
128            );
129
130            image
131        };
132        let image_view = grr.create_image_view(
133            image,
134            grr::ImageViewType::D2,
135            grr::Format::R8G8B8A8_SRGB,
136            grr::SubresourceRange {
137                layers: 0..1,
138                levels: 0..1,
139            },
140        )?;
141        let sampler = grr.create_sampler(grr::SamplerDesc {
142            min_filter: grr::Filter::Linear,
143            mag_filter: grr::Filter::Linear,
144            mip_map: None,
145            address: (
146                grr::SamplerAddress::ClampEdge,
147                grr::SamplerAddress::ClampEdge,
148                grr::SamplerAddress::ClampEdge,
149            ),
150            lod_bias: 0.0,
151            lod: 0.0..10.0,
152            compare: None,
153            border_color: [0.0, 0.0, 0.0, 1.0],
154        })?;
155
156        fonts.tex_id = textures.insert((image, image_view, sampler));
157
158        let vertex_array = grr.create_vertex_array(&[
159            grr::VertexAttributeDesc {
160                location: 0,
161                binding: 0,
162                format: grr::VertexFormat::Xy32Float,
163                offset: 0,
164            },
165            grr::VertexAttributeDesc {
166                location: 1,
167                binding: 0,
168                format: grr::VertexFormat::Xy32Float,
169                offset: (2 * std::mem::size_of::<f32>()) as _,
170            },
171            grr::VertexAttributeDesc {
172                location: 2,
173                binding: 0,
174                format: grr::VertexFormat::Xyzw8Unorm,
175                offset: (4 * std::mem::size_of::<f32>()) as _,
176            },
177        ])?;
178
179        Ok(Renderer {
180            device: grr,
181            pipeline,
182            textures,
183            vertex_array,
184        })
185    }
186
187    /// Render the current Imgui frame.
188    ///
189    /// # Safety
190    ///
191    /// This function makes multiple unsafe calls to the underlying
192    /// `grr::Device`.
193    pub unsafe fn render(&self, draw_data: &imgui::DrawData) -> Result<(), grr::Error> {
194        let fb_width = draw_data.display_size[0] * draw_data.framebuffer_scale[0];
195        let fb_height = draw_data.display_size[1] * draw_data.framebuffer_scale[1];
196
197        if fb_width <= 0.0 || fb_height <= 0.0 {
198            return Ok(());
199        }
200
201        let left = draw_data.display_pos[0];
202        let right = draw_data.display_pos[0] + draw_data.display_size[0];
203        let top = draw_data.display_pos[1];
204        let bottom = draw_data.display_pos[1] + draw_data.display_size[1];
205
206        let transform = [
207            [2.0 / draw_data.display_size[0] as f32, 0.0, 0.0, 0.0],
208            [0.0, -2.0 / draw_data.display_size[1] as f32, 0.0, 0.0],
209            [0.0, 0.0, -1.0, 0.0],
210            [
211                -(right + left) / draw_data.display_size[0],
212                (top + bottom) / draw_data.display_size[1],
213                0.0,
214                1.0,
215            ],
216        ];
217
218        let clip_off = draw_data.display_pos;
219        let clip_scale = draw_data.framebuffer_scale;
220
221        for draw_list in draw_data.draw_lists() {
222            self.render_draw_list(
223                &draw_list,
224                (fb_width, fb_height),
225                &transform,
226                clip_off,
227                clip_scale,
228            )?;
229        }
230
231        Ok(())
232    }
233
234    unsafe fn render_draw_list(
235        &self,
236        draw_list: &imgui::DrawList,
237        fb_size: (f32, f32),
238        matrix: &[[f32; 4]; 4],
239        clip_off: [f32; 2],
240        clip_scale: [f32; 2],
241    ) -> Result<(), grr::Error> {
242        let vertex_buffer = self.device.create_buffer_from_host(
243            grr::as_u8_slice(&draw_list.vtx_buffer()),
244            grr::MemoryFlags::empty(),
245        )?;
246        let index_buffer = self.device.create_buffer_from_host(
247            grr::as_u8_slice(&draw_list.idx_buffer()),
248            grr::MemoryFlags::empty(),
249        )?;
250
251        self.device.bind_pipeline(self.pipeline);
252        self.device.bind_vertex_array(self.vertex_array);
253        self.device
254            .bind_index_buffer(self.vertex_array, index_buffer);
255        self.device.bind_vertex_buffers(
256            self.vertex_array,
257            0,
258            &[grr::VertexBufferView {
259                buffer: vertex_buffer,
260                offset: 0,
261                stride: std::mem::size_of::<imgui::DrawVert>() as _,
262                input_rate: grr::InputRate::Vertex,
263            }],
264        );
265
266        let color_blend = grr::ColorBlend {
267            attachments: vec![grr::ColorBlendAttachment {
268                blend_enable: true,
269                color: grr::BlendChannel {
270                    src_factor: grr::BlendFactor::SrcAlpha,
271                    dst_factor: grr::BlendFactor::OneMinusSrcAlpha,
272                    blend_op: grr::BlendOp::Add,
273                },
274                alpha: grr::BlendChannel {
275                    src_factor: grr::BlendFactor::One,
276                    dst_factor: grr::BlendFactor::One,
277                    blend_op: grr::BlendOp::Add,
278                },
279            }],
280        };
281        self.device.bind_color_blend_state(&color_blend);
282
283        self.device
284            .bind_uniform_constants(self.pipeline, 0, &[grr::Constant::Mat4x4(*matrix)]);
285
286        self.device.set_viewport(
287            0,
288            &[grr::Viewport {
289                x: 0.0,
290                y: 0.0,
291                w: fb_size.0,
292                h: fb_size.1,
293                n: 0.0,
294                f: 1.0,
295            }],
296        );
297
298        let mut index_start = 0;
299        for cmd in draw_list.commands() {
300            match cmd {
301                imgui::DrawCmd::Elements {
302                    count,
303                    cmd_params:
304                        imgui::DrawCmdParams {
305                            clip_rect,
306                            texture_id,
307                            ..
308                        },
309                } => {
310                    let clip_rect = [
311                        (clip_rect[0] - clip_off[0]) * clip_scale[0],
312                        (clip_rect[1] - clip_off[1]) * clip_scale[1],
313                        (clip_rect[2] - clip_off[0]) * clip_scale[0],
314                        (clip_rect[3] - clip_off[1]) * clip_scale[1],
315                    ];
316
317                    if clip_rect[0] < fb_size.0
318                        && clip_rect[1] < fb_size.1
319                        && clip_rect[2] >= 0.0
320                        && clip_rect[3] >= 0.0
321                    {
322                        let (_, image_view, sampler) = self.textures.get(texture_id).unwrap(); // TODO
323                        self.device.bind_image_views(0, &[*image_view]);
324                        self.device.bind_samplers(0, &[*sampler]);
325
326                        self.device.set_scissor(
327                            0,
328                            &[grr::Region {
329                                x: clip_rect[0] as _,
330                                y: (fb_size.1 - clip_rect[3]) as _,
331                                w: (clip_rect[2] - clip_rect[0]).abs().ceil() as _,
332                                h: (clip_rect[3] - clip_rect[1]).abs().ceil() as _,
333                            }],
334                        );
335
336                        self.device.draw_indexed(
337                            grr::Primitive::Triangles,
338                            grr::IndexTy::U16,
339                            index_start..index_start + count as u32,
340                            0..1,
341                            0,
342                        );
343                    }
344                    index_start += count as u32;
345                }
346
347                _ => unimplemented!(),
348            }
349        }
350
351        self.device.delete_buffer(vertex_buffer);
352        self.device.delete_buffer(index_buffer);
353
354        Ok(())
355    }
356}