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 pub unsafe fn new(
48 imgui: &mut imgui::Context,
49 grr: &'grr grr::Device,
50 ) -> Result<Self, grr::Error> {
51 {
52 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 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(); 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}