dear_imgui_rs/context/
frame.rs1use crate::sys;
2
3use super::Context;
4use super::binding::{CTX_MUTEX, with_bound_context};
5
6#[derive(Copy, Clone, Debug, Eq, PartialEq)]
8pub enum FrameLifecycleState {
9 Idle,
11 InFrame,
13 Rendered,
15}
16
17#[derive(Copy, Clone, Debug)]
19pub struct FramePrepareOptions {
20 pub display_size: [f32; 2],
22 pub delta_time: f32,
24 pub framebuffer_scale: Option<[f32; 2]>,
26 pub backend_flags: crate::BackendFlags,
28}
29
30impl FramePrepareOptions {
31 pub fn new(display_size: [f32; 2], delta_time: f32) -> Self {
33 Self {
34 display_size,
35 delta_time,
36 framebuffer_scale: None,
37 backend_flags: crate::BackendFlags::empty(),
38 }
39 }
40
41 #[must_use]
43 pub fn framebuffer_scale(mut self, scale: [f32; 2]) -> Self {
44 self.framebuffer_scale = Some(scale);
45 self
46 }
47
48 #[must_use]
50 pub fn backend_flags(mut self, flags: crate::BackendFlags) -> Self {
51 self.backend_flags |= flags;
52 self
53 }
54
55 #[must_use]
57 pub fn renderer_has_textures(self) -> Self {
58 self.backend_flags(crate::BackendFlags::RENDERER_HAS_TEXTURES)
59 }
60}
61
62#[must_use = "dropping FrameToken ends the frame without rendering; call render() or render_snapshot() to produce draw data"]
69pub struct FrameToken<'ctx> {
70 ctx: &'ctx mut Context,
71 closed: bool,
72}
73
74pub struct FrameResult<'ctx, T> {
76 pub value: T,
78 pub draw_data: &'ctx mut crate::render::DrawData,
80}
81
82impl Context {
83 pub fn prepare_frame(&mut self, options: FramePrepareOptions) {
89 let io = self.io_mut();
90 io.set_display_size(options.display_size);
91 io.set_delta_time(options.delta_time);
92 if let Some(scale) = options.framebuffer_scale {
93 io.set_display_framebuffer_scale(scale);
94 }
95 if !options.backend_flags.is_empty() {
96 io.set_backend_flags(io.backend_flags() | options.backend_flags);
97 }
98 }
99
100 pub fn frame_lifecycle_state(&self) -> FrameLifecycleState {
102 let _guard = CTX_MUTEX.lock();
103 self.assert_current_context("Context::frame_lifecycle_state()");
104 self.frame_lifecycle_state_unlocked()
105 }
106
107 pub fn begin_frame(&mut self) -> FrameToken<'_> {
113 let _ = self.frame();
114 FrameToken {
115 ctx: self,
116 closed: false,
117 }
118 }
119
120 pub fn frame(&mut self) -> &mut crate::ui::Ui {
125 let _guard = CTX_MUTEX.lock();
126 self.assert_current_context("Context::frame()");
127 self.assert_can_begin_frame_unlocked("Context::frame()");
128
129 unsafe {
130 let io = sys::igGetIO_Nil();
135 if !io.is_null() && ((*io).DisplaySize.x < 0.0 || (*io).DisplaySize.y < 0.0) {
136 panic!(
137 "Context::frame() called with invalid io.DisplaySize ({}, {}). \
138Set io.DisplaySize (and typically io.DeltaTime) before starting a frame. \
139If you are using a windowing/event-loop library, prefer a platform backend such as \
140dear-imgui-winit::WinitPlatform::prepare_frame().",
141 (*io).DisplaySize.x,
142 (*io).DisplaySize.y
143 );
144 }
145 sys::igNewFrame();
146 }
147 &mut self.ui
148 }
149
150 pub fn frame_with<F, R>(&mut self, f: F) -> R
152 where
153 F: FnOnce(&crate::ui::Ui) -> R,
154 {
155 let ui = self.frame();
156 f(ui)
157 }
158
159 pub fn frame_with_result<F, R>(&mut self, f: F) -> FrameResult<'_, R>
165 where
166 F: FnOnce(&crate::ui::Ui) -> R,
167 {
168 let frame = self.begin_frame();
169 let value = f(frame.ui());
170 let draw_data = frame.render();
171 FrameResult { value, draw_data }
172 }
173
174 pub fn render(&mut self) -> &mut crate::render::DrawData {
182 let _guard = CTX_MUTEX.lock();
183 self.assert_current_context("Context::render()");
184 self.assert_can_render_unlocked("Context::render()");
185
186 unsafe {
187 sys::igRender();
188 let dd = sys::igGetDrawData();
189 if dd.is_null() {
190 panic!("Context::render() returned null draw data");
191 }
192 &mut *(dd as *mut crate::render::DrawData)
193 }
194 }
195
196 #[cfg(feature = "multi-viewport")]
205 pub fn render_platform_viewport_snapshot(
206 &mut self,
207 options: crate::render::snapshot::SnapshotOptions,
208 ) -> Result<crate::render::snapshot::FrameSnapshot, crate::render::snapshot::SnapshotError>
209 {
210 let _ = self.render();
211 self.platform_viewport_snapshot(options)
212 }
213
214 #[cfg(feature = "multi-viewport")]
221 pub fn platform_viewport_snapshot(
222 &mut self,
223 options: crate::render::snapshot::SnapshotOptions,
224 ) -> Result<crate::render::snapshot::FrameSnapshot, crate::render::snapshot::SnapshotError>
225 {
226 let _guard = CTX_MUTEX.lock();
227 self.assert_current_context("Context::platform_viewport_snapshot()");
228 if self.frame_lifecycle_state_unlocked() != FrameLifecycleState::Rendered {
229 panic!(
230 "Context::platform_viewport_snapshot() called before rendering the current frame"
231 );
232 }
233 crate::render::snapshot::FrameSnapshot::from_platform_io(self.platform_io(), options)
234 }
235
236 pub fn draw_data(&self) -> Option<&crate::render::DrawData> {
241 let _guard = CTX_MUTEX.lock();
242 self.assert_current_context("Context::draw_data()");
243
244 unsafe {
245 let draw_data = sys::igGetDrawData();
246 if draw_data.is_null() {
247 None
248 } else {
249 let data = &*(draw_data as *const crate::render::DrawData);
250 if data.valid() { Some(data) } else { None }
251 }
252 }
253 }
254
255 pub fn draw_data_mut(&mut self) -> Option<&mut crate::render::DrawData> {
261 let _guard = CTX_MUTEX.lock();
262 self.assert_current_context("Context::draw_data_mut()");
263
264 unsafe {
265 let draw_data = sys::igGetDrawData();
266 if draw_data.is_null() {
267 None
268 } else {
269 let data = &mut *(draw_data as *mut crate::render::DrawData);
270 if data.valid() { Some(data) } else { None }
271 }
272 }
273 }
274
275 fn frame_lifecycle_state_unlocked(&self) -> FrameLifecycleState {
276 unsafe {
277 let raw = &*self.raw;
278 if raw.WithinFrameScope {
279 FrameLifecycleState::InFrame
280 } else if raw.FrameCountRendered == raw.FrameCount && raw.FrameCount > 0 {
281 FrameLifecycleState::Rendered
282 } else {
283 FrameLifecycleState::Idle
284 }
285 }
286 }
287
288 fn assert_can_begin_frame_unlocked(&self, caller: &str) {
289 if self.frame_lifecycle_state_unlocked() == FrameLifecycleState::InFrame {
290 panic!("{caller} called while another Dear ImGui frame is already open");
291 }
292 }
293
294 fn assert_can_render_unlocked(&self, caller: &str) {
295 if self.frame_lifecycle_state_unlocked() != FrameLifecycleState::InFrame {
296 panic!("{caller} called without an open Dear ImGui frame");
297 }
298 }
299}
300
301impl<'ctx> FrameToken<'ctx> {
302 pub fn ui(&self) -> &crate::ui::Ui {
307 &self.ctx.ui
308 }
309
310 pub fn lifecycle_state(&self) -> FrameLifecycleState {
312 self.ctx.frame_lifecycle_state()
313 }
314
315 pub fn render(mut self) -> &'ctx mut crate::render::DrawData {
317 let ctx = self.ctx as *mut Context;
318 let draw_data = unsafe { (&mut *ctx).render() };
319 self.closed = true;
320 std::mem::forget(self);
321 draw_data
322 }
323
324 pub fn render_snapshot(
329 mut self,
330 options: crate::render::snapshot::SnapshotOptions,
331 ) -> Result<crate::render::snapshot::FrameSnapshot, crate::render::snapshot::SnapshotError>
332 {
333 let ctx = self.ctx as *mut Context;
334 let draw_data = unsafe { (&mut *ctx).render() };
335 self.closed = true;
336 std::mem::forget(self);
337 crate::render::snapshot::FrameSnapshot::from_draw_data(draw_data, options)
338 }
339
340 #[cfg(feature = "multi-viewport")]
342 pub fn render_platform_viewport_snapshot(
343 mut self,
344 options: crate::render::snapshot::SnapshotOptions,
345 ) -> Result<crate::render::snapshot::FrameSnapshot, crate::render::snapshot::SnapshotError>
346 {
347 let ctx = self.ctx as *mut Context;
348 let snapshot = unsafe { (&mut *ctx).render_platform_viewport_snapshot(options) };
349 self.closed = true;
350 std::mem::forget(self);
351 snapshot
352 }
353}
354
355impl Drop for FrameToken<'_> {
356 fn drop(&mut self) {
357 if self.closed {
358 return;
359 }
360
361 let _guard = CTX_MUTEX.lock();
362 if self.ctx.frame_lifecycle_state_unlocked() == FrameLifecycleState::InFrame {
363 unsafe { with_bound_context(self.ctx.raw, || sys::igEndFrame()) };
364 }
365 self.closed = true;
366 }
367}