Skip to main content

dear_imgui_rs/render/draw_data/
owned.rs

1use super::{DrawData, assert_draw_list_cloneable};
2use crate::internal::RawWrapper;
3use crate::sys;
4use std::marker::PhantomData;
5use std::rc::Rc;
6
7/// A container for a heap-allocated deep copy of a `DrawData` struct.
8///
9/// Notes on thread-safety:
10/// - This type intentionally does NOT implement `Send`/`Sync`. Although draw lists are cloned,
11///   draw commands can still carry raw texture references and backend-specific assumptions.
12/// - The context-owned texture request list is not copied. Use
13///   [`crate::render::snapshot::FrameSnapshot`] when you need texture requests or thread-safe
14///   rendering data detached from the ImGui context.
15///
16/// The underlying copy is released when this struct is dropped.
17pub struct OwnedDrawData {
18    draw_data: *mut sys::ImDrawData,
19    // Keep this conservative until OwnedDrawData has a fully specified thread-safe contract.
20    _no_send_sync: PhantomData<Rc<()>>,
21}
22
23impl OwnedDrawData {
24    /// If this struct contains a `DrawData` object, then this function returns a reference to it.
25    ///
26    /// Otherwise, this struct is empty and so this function returns `None`.
27    #[inline]
28    pub fn draw_data(&self) -> Option<&DrawData> {
29        if !self.draw_data.is_null() {
30            Some(unsafe { &*(self.draw_data as *const DrawData) })
31        } else {
32            None
33        }
34    }
35}
36
37impl Default for OwnedDrawData {
38    /// The default `OwnedDrawData` struct is empty.
39    #[inline]
40    fn default() -> Self {
41        Self {
42            draw_data: std::ptr::null_mut(),
43            _no_send_sync: PhantomData,
44        }
45    }
46}
47
48impl From<&DrawData> for OwnedDrawData {
49    /// Construct `OwnedDrawData` from `DrawData` by creating a heap-allocated deep copy of the given `DrawData`
50    fn from(value: &DrawData) -> Self {
51        unsafe {
52            let source_ptr = RawWrapper::raw(value);
53            if source_ptr.CmdListsCount > 0 && !source_ptr.CmdLists.Data.is_null() {
54                for i in 0..(source_ptr.CmdListsCount as usize) {
55                    let src_list = *source_ptr.CmdLists.Data.add(i);
56                    assert_draw_list_cloneable(src_list.cast_const(), "OwnedDrawData::from");
57                }
58            }
59
60            // Allocate a new ImDrawData using the constructor
61            let result = sys::ImDrawData_ImDrawData();
62            if result.is_null() {
63                panic!("Failed to allocate ImDrawData for OwnedDrawData");
64            }
65
66            // Copy basic fields from the source
67            (*result).Valid = source_ptr.Valid;
68            (*result).TotalIdxCount = source_ptr.TotalIdxCount;
69            (*result).TotalVtxCount = source_ptr.TotalVtxCount;
70            (*result).DisplayPos = source_ptr.DisplayPos;
71            (*result).DisplaySize = source_ptr.DisplaySize;
72            (*result).FramebufferScale = source_ptr.FramebufferScale;
73            (*result).OwnerViewport = source_ptr.OwnerViewport;
74
75            // Copy draw lists by cloning each list to ensure OwnedDrawData owns its memory
76            (*result).CmdListsCount = 0;
77            if source_ptr.CmdListsCount > 0 && !source_ptr.CmdLists.Data.is_null() {
78                for i in 0..(source_ptr.CmdListsCount as usize) {
79                    let src_list = *source_ptr.CmdLists.Data.add(i);
80                    if !src_list.is_null() {
81                        // Clone the output of the draw list to own it
82                        let cloned = sys::ImDrawList_CloneOutput(src_list);
83                        if !cloned.is_null() {
84                            sys::ImDrawData_AddDrawList(result, cloned);
85                        }
86                    }
87                }
88            }
89
90            // The texture request list belongs to the originating ImGui context. Copying the raw
91            // pointer would let a detached OwnedDrawData expose shared TextureData references while
92            // the live context mutates the same objects through PlatformIo/DrawData.
93            (*result).Textures = std::ptr::null_mut();
94
95            OwnedDrawData {
96                draw_data: result,
97                _no_send_sync: PhantomData,
98            }
99        }
100    }
101}
102
103impl From<&mut DrawData> for OwnedDrawData {
104    /// Construct `OwnedDrawData` from mutable draw data by reborrowing it as shared draw data.
105    fn from(value: &mut DrawData) -> Self {
106        OwnedDrawData::from(&*value)
107    }
108}
109
110impl Drop for OwnedDrawData {
111    /// Releases any heap-allocated memory consumed by this `OwnedDrawData` object
112    fn drop(&mut self) {
113        unsafe {
114            if !self.draw_data.is_null() {
115                // Destroy cloned draw lists if any
116                if !(*self.draw_data).CmdLists.Data.is_null() {
117                    for i in 0..(*self.draw_data).CmdListsCount as usize {
118                        let ptr = *(*self.draw_data).CmdLists.Data.add(i);
119                        if !ptr.is_null() {
120                            sys::ImDrawList_destroy(ptr);
121                        }
122                    }
123                }
124
125                // Destroy the ImDrawData itself
126                sys::ImDrawData_destroy(self.draw_data);
127                self.draw_data = std::ptr::null_mut();
128            }
129        }
130    }
131}