imgui/render/
draw_data.rs

1use std::mem::size_of;
2use std::slice;
3
4use crate::internal::{RawCast, RawWrapper};
5use crate::math::MintVec2;
6use crate::render::renderer::TextureId;
7use crate::sys;
8
9/// All draw data to render a Dear ImGui frame.
10#[repr(C)]
11pub struct DrawData {
12    /// Only valid after render() is called and before the next new frame() is called.
13    valid: bool,
14    /// Number of DrawList to render.
15    cmd_lists_count: i32,
16    /// For convenience, sum of all draw list index buffer sizes.
17    pub total_idx_count: i32,
18    /// For convenience, sum of all draw list vertex buffer sizes.
19    pub total_vtx_count: i32,
20    // Array of DrawList.
21    cmd_lists: *mut *mut DrawList,
22    /// Upper-left position of the viewport to render.
23    ///
24    /// (= upper-left corner of the orthogonal projection matrix to use)
25    pub display_pos: [f32; 2],
26    /// Size of the viewport to render.
27    ///
28    /// (= display_pos + display_size == lower-right corner of the orthogonal matrix to use)
29    pub display_size: [f32; 2],
30    /// Amount of pixels for each unit of display_size.
31    ///
32    /// Based on io.display_frame_buffer_scale. Typically [1.0, 1.0] on normal displays, and
33    /// [2.0, 2.0] on Retina displays, but fractional values are also possible.
34    pub framebuffer_scale: [f32; 2],
35
36    #[cfg(feature = "docking")]
37    owner_viewport: *mut sys::ImGuiViewport,
38}
39
40unsafe impl RawCast<sys::ImDrawData> for DrawData {}
41
42impl DrawData {
43    /// Returns an iterator over the draw lists included in the draw data.
44    #[inline]
45    pub fn draw_lists(&self) -> DrawListIterator<'_> {
46        unsafe {
47            DrawListIterator {
48                iter: self.cmd_lists().iter(),
49            }
50        }
51    }
52    /// Returns the number of draw lists included in the draw data.
53    #[inline]
54    pub fn draw_lists_count(&self) -> usize {
55        self.cmd_lists_count.try_into().unwrap()
56    }
57    #[inline]
58    pub(crate) unsafe fn cmd_lists(&self) -> &[*const DrawList] {
59        slice::from_raw_parts(
60            self.cmd_lists as *const *const DrawList,
61            self.cmd_lists_count as usize,
62        )
63    }
64    /// Converts all buffers from indexed to non-indexed, in case you cannot render indexed
65    /// buffers.
66    ///
67    /// **This is slow and most likely a waste of resources. Always prefer indexed rendering!**
68    #[doc(alias = "DeIndexAllBuffers")]
69    pub fn deindex_all_buffers(&mut self) {
70        unsafe {
71            sys::ImDrawData_DeIndexAllBuffers(self.raw_mut());
72        }
73    }
74    /// Scales the clip rect of each draw command.
75    ///
76    /// Can be used if your final output buffer is at a different scale than imgui-rs expects, or
77    /// if there is a difference between your window resolution and framebuffer resolution.
78    #[doc(alias = "ScaleClipRects")]
79    pub fn scale_clip_rects(&mut self, fb_scale: MintVec2) {
80        unsafe {
81            sys::ImDrawData_ScaleClipRects(self.raw_mut(), fb_scale.into());
82        }
83    }
84}
85
86/// Iterator over draw lists
87pub struct DrawListIterator<'a> {
88    iter: std::slice::Iter<'a, *const DrawList>,
89}
90
91impl<'a> Iterator for DrawListIterator<'a> {
92    type Item = &'a DrawList;
93
94    fn next(&mut self) -> Option<Self::Item> {
95        self.iter.next().map(|&ptr| unsafe { &*ptr })
96    }
97}
98
99#[test]
100#[cfg(test)]
101fn test_drawdata_memory_layout() {
102    use std::mem;
103    assert_eq!(
104        mem::size_of::<DrawData>(),
105        mem::size_of::<sys::ImDrawData>()
106    );
107    assert_eq!(
108        mem::align_of::<DrawData>(),
109        mem::align_of::<sys::ImDrawData>()
110    );
111    use sys::ImDrawData;
112    macro_rules! assert_field_offset {
113        ($l:ident, $r:ident) => {
114            assert_eq!(
115                memoffset::offset_of!(DrawData, $l),
116                memoffset::offset_of!(ImDrawData, $r)
117            );
118        };
119    }
120    assert_field_offset!(valid, Valid);
121    assert_field_offset!(cmd_lists, CmdLists);
122    assert_field_offset!(cmd_lists_count, CmdListsCount);
123    assert_field_offset!(total_idx_count, TotalIdxCount);
124    assert_field_offset!(total_vtx_count, TotalVtxCount);
125    assert_field_offset!(display_pos, DisplayPos);
126    assert_field_offset!(display_size, DisplaySize);
127    assert_field_offset!(framebuffer_scale, FramebufferScale);
128}
129
130/// Draw command list
131#[repr(transparent)]
132pub struct DrawList(sys::ImDrawList);
133
134impl RawWrapper for DrawList {
135    type Raw = sys::ImDrawList;
136    #[inline]
137    unsafe fn raw(&self) -> &sys::ImDrawList {
138        &self.0
139    }
140    #[inline]
141    unsafe fn raw_mut(&mut self) -> &mut sys::ImDrawList {
142        &mut self.0
143    }
144}
145
146impl DrawList {
147    #[inline]
148    pub(crate) unsafe fn cmd_buffer(&self) -> &[sys::ImDrawCmd] {
149        slice::from_raw_parts(
150            self.0.CmdBuffer.Data as *const sys::ImDrawCmd,
151            self.0.CmdBuffer.Size as usize,
152        )
153    }
154    #[inline]
155    pub fn idx_buffer(&self) -> &[DrawIdx] {
156        unsafe {
157            slice::from_raw_parts(
158                self.0.IdxBuffer.Data as *const DrawIdx,
159                self.0.IdxBuffer.Size as usize,
160            )
161        }
162    }
163    #[inline]
164    pub fn vtx_buffer(&self) -> &[DrawVert] {
165        unsafe {
166            slice::from_raw_parts(
167                self.0.VtxBuffer.Data as *const DrawVert,
168                self.0.VtxBuffer.Size as usize,
169            )
170        }
171    }
172
173    /// # Safety
174    /// This is equivalent to `transmute(self.vtx_buffer())` with a little more
175    /// checking, and thus inherits the safety considerations of `transmute`ing
176    /// slices.
177    pub unsafe fn transmute_vtx_buffer<VTy: Copy>(&self) -> &[VTy] {
178        // these checks are constant and thus are removed from release builds
179        assert_eq!(
180            core::mem::size_of::<VTy>(),
181            core::mem::size_of::<DrawVert>(),
182        );
183        assert!(core::mem::align_of::<VTy>() <= core::mem::align_of::<DrawVert>());
184        slice::from_raw_parts(self.0.VtxBuffer.Data.cast(), self.0.VtxBuffer.Size as usize)
185    }
186
187    #[inline]
188    pub fn commands(&self) -> DrawCmdIterator<'_> {
189        unsafe {
190            DrawCmdIterator {
191                iter: self.cmd_buffer().iter(),
192            }
193        }
194    }
195}
196
197pub struct DrawCmdIterator<'a> {
198    iter: std::slice::Iter<'a, sys::ImDrawCmd>,
199}
200
201impl<'a> Iterator for DrawCmdIterator<'a> {
202    type Item = DrawCmd;
203
204    #[inline]
205    fn next(&mut self) -> Option<Self::Item> {
206        self.iter.next().map(|cmd| {
207            let cmd_params = DrawCmdParams {
208                clip_rect: cmd.ClipRect.into(),
209                texture_id: TextureId::from(cmd.TextureId),
210                vtx_offset: cmd.VtxOffset as usize,
211                idx_offset: cmd.IdxOffset as usize,
212            };
213            match cmd.UserCallback {
214                Some(raw_callback) if raw_callback as usize == -1isize as usize => {
215                    DrawCmd::ResetRenderState
216                }
217                Some(raw_callback) => DrawCmd::RawCallback {
218                    callback: raw_callback,
219                    raw_cmd: cmd,
220                },
221                None => DrawCmd::Elements {
222                    count: cmd.ElemCount as usize,
223                    cmd_params,
224                },
225            }
226        })
227    }
228}
229
230/// A vertex index
231pub type DrawIdx = sys::ImDrawIdx;
232
233#[derive(Copy, Clone, Debug, PartialEq)]
234pub struct DrawCmdParams {
235    /// left, up, right, down
236    pub clip_rect: [f32; 4],
237    pub texture_id: TextureId,
238    pub vtx_offset: usize,
239    pub idx_offset: usize,
240}
241
242/// A draw command
243pub enum DrawCmd {
244    Elements {
245        /// The number of indices used for this draw command
246        count: usize,
247        cmd_params: DrawCmdParams,
248    },
249    ResetRenderState,
250    RawCallback {
251        callback: unsafe extern "C" fn(*const sys::ImDrawList, cmd: *const sys::ImDrawCmd),
252        raw_cmd: *const sys::ImDrawCmd,
253    },
254}
255
256/// A single vertex
257#[repr(C)]
258#[derive(Copy, Clone, Debug, PartialEq)]
259pub struct DrawVert {
260    pub pos: [f32; 2],
261    pub uv: [f32; 2],
262    pub col: [u8; 4],
263}
264
265#[test]
266#[cfg(test)]
267fn test_drawvert_memory_layout() {
268    use std::mem;
269    assert_eq!(
270        mem::size_of::<DrawVert>(),
271        mem::size_of::<sys::ImDrawVert>()
272    );
273    assert_eq!(
274        mem::align_of::<DrawVert>(),
275        mem::align_of::<sys::ImDrawVert>()
276    );
277    use sys::ImDrawVert;
278    macro_rules! assert_field_offset {
279        ($l:ident, $r:ident) => {
280            assert_eq!(
281                memoffset::offset_of!(DrawVert, $l),
282                memoffset::offset_of!(ImDrawVert, $r)
283            );
284        };
285    }
286    assert_field_offset!(pos, pos);
287    assert_field_offset!(uv, uv);
288    assert_field_offset!(col, col);
289}
290
291/// A container for a heap-allocated deep copy of a `DrawData` struct.
292///
293/// Can be used to retain draw data for rendering on a different thread.
294/// The underlying copy is released when this struct is dropped.
295pub struct OwnedDrawData {
296    draw_data: *mut sys::ImDrawData,
297}
298
299impl OwnedDrawData {
300    /// If this struct contains a `DrawData` object, then this function returns a reference to it.
301    ///
302    /// Otherwise, this struct is empty and so this function returns `None`.
303    #[inline]
304    pub fn draw_data(&self) -> Option<&DrawData> {
305        if !self.draw_data.is_null() {
306            Some(unsafe { DrawData::from_raw(&*self.draw_data) })
307        } else {
308            None
309        }
310    }
311
312    #[cfg(feature = "docking")]
313    unsafe fn copy_docking_properties(source: &sys::ImDrawData, dest: *mut sys::ImDrawData) {
314        (*dest).OwnerViewport = source.OwnerViewport;
315    }
316    #[cfg(not(feature = "docking"))]
317    unsafe fn copy_docking_properties(_source: &sys::ImDrawData, _dest: *mut sys::ImDrawData) {}
318}
319
320impl Default for OwnedDrawData {
321    /// The default `OwnedDrawData` struct is empty.
322    #[inline]
323    fn default() -> Self {
324        Self {
325            draw_data: std::ptr::null_mut(),
326        }
327    }
328}
329
330impl From<&DrawData> for OwnedDrawData {
331    /// Construct `OwnedDrawData` from `DrawData` by creating a heap-allocated deep copy of the given `DrawData`
332    fn from(value: &DrawData) -> Self {
333        OwnedDrawData {
334            draw_data: unsafe {
335                let other_ptr = value.raw();
336                let result = sys::ImDrawData_ImDrawData();
337                (*result).Valid = other_ptr.Valid;
338                (*result).TotalIdxCount = other_ptr.TotalIdxCount;
339                (*result).TotalVtxCount = other_ptr.TotalVtxCount;
340                (*result).DisplayPos = other_ptr.DisplayPos;
341                (*result).DisplaySize = other_ptr.DisplaySize;
342                (*result).FramebufferScale = other_ptr.FramebufferScale;
343                (*result).CmdListsCount = other_ptr.CmdListsCount;
344                (*result).CmdLists = sys::igMemAlloc(
345                    size_of::<*mut sys::ImDrawList>() * other_ptr.CmdListsCount as usize,
346                ) as *mut *mut sys::ImDrawList;
347                OwnedDrawData::copy_docking_properties(other_ptr, result);
348
349                let mut current_draw_list = (*result).CmdLists;
350                for i in 0..other_ptr.CmdListsCount as usize {
351                    *current_draw_list = sys::ImDrawList_CloneOutput(*other_ptr.CmdLists.add(i));
352                    current_draw_list = current_draw_list.add(1);
353                }
354                result
355            },
356        }
357    }
358}
359
360impl Drop for OwnedDrawData {
361    /// Releases any heap-allocated memory consumed by this `OwnedDrawData` object
362    fn drop(&mut self) {
363        unsafe {
364            if !self.draw_data.is_null() {
365                if !(*self.draw_data).CmdLists.is_null() {
366                    for i in 0..(*self.draw_data).CmdListsCount as usize {
367                        let ptr = *(*self.draw_data).CmdLists.add(i);
368                        if !ptr.is_null() {
369                            sys::ImDrawList_destroy(ptr);
370                        }
371                    }
372                    sys::igMemFree((*self.draw_data).CmdLists as *mut std::ffi::c_void);
373                }
374                sys::ImDrawData_destroy(self.draw_data);
375                self.draw_data = std::ptr::null_mut();
376            }
377        }
378    }
379}
380
381#[test]
382#[cfg(test)]
383fn test_owneddrawdata_default() {
384    let default = OwnedDrawData::default();
385    assert!(default.draw_data().is_none());
386}
387
388#[test]
389#[cfg(test)]
390fn test_owneddrawdata_from_drawdata() {
391    let (_guard, _ctx) = crate::test::test_ctx();
392
393    // Build a dummy draw data object
394    let mut draw_list = sys::ImDrawList::default();
395    let mut draw_lists_raw = [std::ptr::addr_of_mut!(draw_list)];
396    let draw_data_raw = sys::ImDrawData {
397        Valid: true,
398        CmdListsCount: 1,
399        CmdLists: draw_lists_raw.as_mut_ptr(),
400        TotalIdxCount: 123,
401        TotalVtxCount: 456,
402        DisplayPos: sys::ImVec2 { x: 123.0, y: 456.0 },
403        DisplaySize: sys::ImVec2 { x: 789.0, y: 012.0 },
404        FramebufferScale: sys::ImVec2 { x: 3.0, y: 7.0 },
405        #[cfg(feature = "docking")]
406        OwnerViewport: unsafe { std::ptr::null_mut::<sys::ImGuiViewport>().offset(123) },
407    };
408    let draw_data = unsafe { DrawData::from_raw(&draw_data_raw) };
409
410    // Clone it, and ensure the underlying properties have been cloned
411    let owned_draw_data: OwnedDrawData = draw_data.into();
412    let inner_draw_data = owned_draw_data.draw_data();
413    assert!(inner_draw_data.is_some());
414    let owned_draw_data_raw = unsafe { inner_draw_data.unwrap().raw() };
415    assert_eq!(draw_data_raw.Valid, owned_draw_data_raw.Valid);
416    assert_eq!(
417        draw_data_raw.CmdListsCount,
418        owned_draw_data_raw.CmdListsCount
419    );
420    assert!(!draw_data_raw.CmdLists.is_null());
421    assert_eq!(
422        draw_data_raw.TotalIdxCount,
423        owned_draw_data_raw.TotalIdxCount
424    );
425    assert_eq!(
426        draw_data_raw.TotalVtxCount,
427        owned_draw_data_raw.TotalVtxCount
428    );
429    assert_eq!(draw_data_raw.DisplayPos, owned_draw_data_raw.DisplayPos);
430    assert_eq!(draw_data_raw.DisplaySize, owned_draw_data_raw.DisplaySize);
431    assert_eq!(
432        draw_data_raw.FramebufferScale,
433        owned_draw_data_raw.FramebufferScale
434    );
435
436    #[cfg(feature = "docking")]
437    assert_eq!(
438        draw_data_raw.OwnerViewport,
439        owned_draw_data_raw.OwnerViewport
440    );
441}
442
443#[test]
444#[cfg(test)]
445fn test_owneddrawdata_drop() {
446    let (_guard, _ctx) = crate::test::test_ctx();
447    let initial_allocation_count = unsafe { (*sys::igGetIO()).MetricsActiveAllocations };
448
449    // Build a dummy draw data object
450    let mut draw_list = sys::ImDrawList::default();
451    let mut draw_lists_raw = [std::ptr::addr_of_mut!(draw_list)];
452    let draw_data_raw = sys::ImDrawData {
453        Valid: true,
454        CmdListsCount: 1,
455        CmdLists: draw_lists_raw.as_mut_ptr(),
456        TotalIdxCount: 0,
457        TotalVtxCount: 0,
458        DisplayPos: sys::ImVec2 { x: 0.0, y: 0.0 },
459        DisplaySize: sys::ImVec2 { x: 800.0, y: 600.0 },
460        FramebufferScale: sys::ImVec2 { x: 1.0, y: 1.0 },
461        #[cfg(feature = "docking")]
462        OwnerViewport: std::ptr::null_mut(),
463    };
464    let draw_data = unsafe { DrawData::from_raw(&draw_data_raw) };
465
466    // Clone it, then drop it, and ensure all allocations are returned
467    {
468        let _owned_draw_data: OwnedDrawData = draw_data.into();
469        let cloned_allocation_count = unsafe { (*sys::igGetIO()).MetricsActiveAllocations };
470        assert!(cloned_allocation_count > initial_allocation_count);
471        // owned_draw_data is dropped here...
472    }
473    let final_allocation_count = unsafe { (*sys::igGetIO()).MetricsActiveAllocations };
474    assert_eq!(initial_allocation_count, final_allocation_count);
475}