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}