1use crate::Result;
2use crate::ffi;
3use crate::ffi::{
4 PDGEventInfo, PDGWorkItemInfo, PDGWorkItemOutputFile,
5 raw::{PdgEventType, PdgState},
6};
7use crate::node::{HoudiniNode, NodeHandle};
8use std::fmt::Formatter;
9use std::ops::ControlFlow;
10
11pub struct PDGWorkItem<'node> {
13 pub id: WorkItemId,
14 pub context_id: i32,
15 pub node: &'node HoudiniNode,
16}
17
18#[derive(Debug, Copy, Clone)]
19#[repr(transparent)]
20pub struct WorkItemId(pub(crate) i32);
21
22impl std::fmt::Debug for PDGWorkItem<'_> {
23 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
24 f.debug_struct("PDGWorkItem")
25 .field("id", &self.id)
26 .field("context", &self.context_id)
27 .finish()
28 }
29}
30
31impl From<WorkItemId> for i32 {
32 fn from(value: WorkItemId) -> Self {
33 value.0
34 }
35}
36
37impl From<TopNode> for NodeHandle {
38 fn from(value: TopNode) -> Self {
39 value.node.handle
40 }
41}
42
43impl<'session> PDGWorkItem<'session> {
44 pub fn get_info(&self) -> Result<PDGWorkItemInfo> {
45 ffi::get_workitem_info(&self.node.session, self.context_id, self.id.0).map(PDGWorkItemInfo)
46 }
47 pub fn get_results(&self) -> Result<Vec<PDGWorkItemOutputFile<'session>>> {
49 match self.get_info()?.output_file_count() {
50 0 => Ok(Vec::new()),
51 count => {
52 let results = ffi::get_workitem_result(
53 &self.node.session,
54 self.node.handle,
55 self.id.0,
56 count,
57 )?;
58 let results = results
59 .into_iter()
60 .map(|inner| PDGWorkItemOutputFile(inner, (&self.node.session).into()))
61 .collect();
62
63 Ok(results)
64 }
65 }
66 }
67
68 pub fn get_data_length(&self, data_name: &str) -> Result<i32> {
69 let data_name = std::ffi::CString::new(data_name)?;
70 ffi::get_workitem_data_length(&self.node.session, self.node.handle, self.id.0, &data_name)
71 }
72
73 pub fn set_int_data(&self, data_name: &str, data: &[i32]) -> Result<()> {
74 let data_name = std::ffi::CString::new(data_name)?;
75 ffi::set_workitem_int_data(
76 &self.node.session,
77 self.node.handle,
78 self.id.0,
79 &data_name,
80 data,
81 )
82 }
83
84 pub fn get_int_data(&self, data_name: &str) -> Result<Vec<i32>> {
85 let data_name = std::ffi::CString::new(data_name)?;
86 let data_size = ffi::get_workitem_data_length(
87 &self.node.session,
88 self.node.handle,
89 self.id.0,
90 &data_name,
91 )?;
92 let mut buffer = vec![0; data_size as usize];
93 ffi::get_workitem_int_data(
94 &self.node.session,
95 self.node.handle,
96 self.id.0,
97 &data_name,
98 buffer.as_mut_slice(),
99 )?;
100 Ok(buffer)
101 }
102
103 pub fn set_float_data(&self, data_name: &str, data: &[f32]) -> Result<()> {
104 let data_name = std::ffi::CString::new(data_name)?;
105 ffi::set_workitem_float_data(
106 &self.node.session,
107 self.node.handle,
108 self.id.0,
109 &data_name,
110 data,
111 )
112 }
113
114 pub fn get_float_data(&self, data_name: &str) -> Result<Vec<f32>> {
115 let data_name = std::ffi::CString::new(data_name)?;
116 let data_size = ffi::get_workitem_data_length(
117 &self.node.session,
118 self.node.handle,
119 self.id.0,
120 &data_name,
121 )?;
122 let mut buffer = Vec::new();
123 buffer.resize(data_size as usize, 0.0);
124 ffi::get_workitem_float_data(
125 &self.node.session,
126 self.node.handle,
127 self.id.0,
128 &data_name,
129 buffer.as_mut_slice(),
130 )?;
131 Ok(buffer)
132 }
133
134 pub fn set_int_attribute(&self, attrib_name: &str, value: &[i32]) -> Result<()> {
135 let attrib_name = std::ffi::CString::new(attrib_name)?;
136 ffi::set_workitem_int_attribute(
137 &self.node.session,
138 self.node.handle,
139 self.id.0,
140 &attrib_name,
141 value,
142 )
143 }
144 pub fn get_int_attribute(&self, attr_name: &str) -> Result<Vec<i32>> {
145 let attr_name = std::ffi::CString::new(attr_name)?;
146 let attr_size = ffi::get_workitem_attribute_size(
147 &self.node.session,
148 self.node.handle,
149 self.id.0,
150 &attr_name,
151 )?;
152 let mut buffer = vec![0; attr_size as usize];
153 ffi::get_workitem_int_attribute(
154 &self.node.session,
155 self.node.handle,
156 self.id.0,
157 &attr_name,
158 &mut buffer,
159 )?;
160 Ok(buffer)
161 }
162
163 pub fn set_float_attribute(&self, attrib_name: &str, value: &[f32]) -> Result<()> {
164 let attrib_name = std::ffi::CString::new(attrib_name)?;
165 ffi::set_workitem_float_attribute(
166 &self.node.session,
167 self.node.handle,
168 self.id.0,
169 &attrib_name,
170 value,
171 )
172 }
173
174 pub fn get_float_attribute(&self, attr_name: &str) -> Result<Vec<f32>> {
175 let attr_name = std::ffi::CString::new(attr_name)?;
176 let attr_size = ffi::get_workitem_attribute_size(
177 &self.node.session,
178 self.node.handle,
179 self.id.0,
180 &attr_name,
181 )?;
182 let mut buffer = Vec::new();
183 buffer.resize(attr_size as usize, 0.0);
184 ffi::get_workitem_float_attribute(
185 &self.node.session,
186 self.node.handle,
187 self.id.0,
188 &attr_name,
189 &mut buffer,
190 )?;
191 Ok(buffer)
192 }
193}
194
195#[derive(Debug, Clone)]
196pub struct TopNode {
198 pub node: HoudiniNode,
199}
200
201#[derive(Debug, Copy, Clone)]
203pub struct CookStep {
204 pub event: PDGEventInfo,
205 pub graph_id: i32,
206 pub graph_name: i32,
207}
208
209fn create_events() -> Vec<ffi::raw::HAPI_PDG_EventInfo> {
211 const NUM: usize = 32;
212 vec![
213 ffi::raw::HAPI_PDG_EventInfo {
214 nodeId: -1,
215 workItemId: -1,
216 dependencyId: -1,
217 currentState: -1,
218 lastState: -1,
219 eventType: -1,
220 msgSH: -1,
221 };
222 NUM
223 ]
224}
225
226impl TopNode {
227 pub fn cook_async<F>(
240 &self,
241 all_outputs: bool,
242 sleep: Option<std::time::Duration>,
243 mut func: F,
244 ) -> Result<()>
245 where
246 F: FnMut(CookStep) -> Result<ControlFlow<bool>>,
247 {
248 let session = &self.node.session;
249 log::debug!("Start cooking PDG node: {}", self.node.path()?);
250 debug_assert!(session.is_valid());
251 ffi::cook_pdg(session, self.node.handle, false, false, all_outputs)?;
252 let mut events = create_events();
253 'main: loop {
254 std::thread::sleep(sleep.unwrap_or_default());
255 let (graph_ids, graph_names) = ffi::get_pdg_contexts(session)?;
256 debug_assert_eq!(graph_ids.len(), graph_names.len());
257 for (graph_id, graph_name) in graph_ids.into_iter().zip(graph_names) {
258 for event in ffi::get_pdg_events(session, graph_id, &mut events)? {
259 let event = PDGEventInfo(*event);
260 match event.event_type() {
261 PdgEventType::EventCookComplete => break 'main,
262 _ => {
263 match func(CookStep {
264 event,
265 graph_id,
266 graph_name,
267 }) {
268 Err(e) => return Err(e),
269 Ok(ControlFlow::Continue(_)) => {}
270 Ok(ControlFlow::Break(stop_cooking)) => {
271 if stop_cooking {
272 ffi::cancel_pdg_cook(session, graph_id)?;
274 }
275 break 'main;
276 }
277 }
278 }
279 }
280 }
281 }
282 }
283 Ok(())
284 }
285
286 pub fn cook_pdg_blocking(&self, all_outputs: bool) -> Result<()> {
290 ffi::cook_pdg(
291 &self.node.session,
292 self.node.handle,
293 false,
294 true,
295 all_outputs,
296 )
297 }
298
299 pub fn generate_static_items(&self, all_outputs: bool) -> Result<()> {
300 ffi::cook_pdg(
301 &self.node.session,
302 self.node.handle,
303 true,
304 true,
305 all_outputs,
306 )
307 }
308
309 pub fn get_context_id(&self) -> Result<i32> {
311 ffi::get_pdg_context_id(&self.node.session, self.node.handle)
312 }
313
314 pub fn cancel_cooking(&self) -> Result<()> {
316 log::debug!("Cancel PDG cooking {}", self.node.path()?);
317 let context = self.get_context_id()?;
318 ffi::cancel_pdg_cook(&self.node.session, context)
319 }
320
321 pub fn pause_cooking(&self) -> Result<()> {
323 log::debug!("Pause PDG cooking {}", self.node.path()?);
324 let context = self.get_context_id()?;
325 ffi::pause_pdg_cook(&self.node.session, context)
326 }
327
328 pub fn dirty_node(&self, clean_results: bool) -> Result<()> {
330 log::debug!("Set PDG node dirty {}", self.node.path()?);
331 ffi::dirty_pdg_node(&self.node.session, self.node.handle, clean_results)
332 }
333
334 pub fn get_current_state(&self, context_id: Option<i32>) -> Result<PdgState> {
336 let context = match context_id {
337 Some(c) => c,
338 None => self.get_context_id()?,
339 };
340 ffi::get_pdg_state(&self.node.session, context)
341 }
342
343 pub fn get_workitem(&self, workitem_id: WorkItemId) -> Result<PDGWorkItem<'_>> {
345 let context_id = self.get_context_id()?;
346 ffi::get_workitem_info(&self.node.session, context_id, workitem_id.0).map(|_| PDGWorkItem {
347 id: workitem_id,
348 context_id,
349 node: &self.node,
350 })
351 }
352
353 pub fn get_all_workitems(&self) -> Result<Vec<PDGWorkItem<'_>>> {
354 let context_id = self.get_context_id()?;
355 ffi::get_pdg_workitems(&self.node.session, self.node.handle).map(|vec| {
356 vec.into_iter()
357 .map(|id| PDGWorkItem {
358 id: WorkItemId(id),
359 context_id,
360 node: &self.node,
361 })
362 .collect()
363 })
364 }
365
366 pub fn create_workitem(
367 &self,
368 name: &str,
369 index: i32,
370 context_id: Option<i32>,
371 ) -> Result<PDGWorkItem<'_>> {
372 let name = std::ffi::CString::new(name)?;
373 let context_id = match context_id {
374 Some(c) => c,
375 None => self.get_context_id()?,
376 };
377 let id = ffi::create_pdg_workitem(&self.node.session, self.node.handle, &name, index)?;
378 Ok(PDGWorkItem {
379 id: WorkItemId(id),
380 context_id,
381 node: &self.node,
382 })
383 }
384
385 pub fn commit_workitems(&self) -> Result<()> {
386 ffi::commit_pdg_workitems(&self.node.session, self.node.handle)
387 }
388}