1use crate::ffi;
2use crate::ffi::{
3 raw::{PdgEventType, PdgState},
4 PDGEventInfo, PDGWorkItemInfo, PDGWorkItemOutputFile,
5};
6use crate::node::{HoudiniNode, NodeHandle};
7use crate::Result;
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::new();
93 buffer.resize(data_size as usize, 0);
94 ffi::get_workitem_int_data(
95 &self.node.session,
96 self.node.handle,
97 self.id.0,
98 &data_name,
99 buffer.as_mut_slice(),
100 )?;
101 Ok(buffer)
102 }
103
104 pub fn set_float_data(&self, data_name: &str, data: &[f32]) -> Result<()> {
105 let data_name = std::ffi::CString::new(data_name)?;
106 ffi::set_workitem_float_data(
107 &self.node.session,
108 self.node.handle,
109 self.id.0,
110 &data_name,
111 data,
112 )
113 }
114
115 pub fn get_float_data(&self, data_name: &str) -> Result<Vec<f32>> {
116 let data_name = std::ffi::CString::new(data_name)?;
117 let data_size = ffi::get_workitem_data_length(
118 &self.node.session,
119 self.node.handle,
120 self.id.0,
121 &data_name,
122 )?;
123 let mut buffer = Vec::new();
124 buffer.resize(data_size as usize, 0.0);
125 ffi::get_workitem_float_data(
126 &self.node.session,
127 self.node.handle,
128 self.id.0,
129 &data_name,
130 buffer.as_mut_slice(),
131 )?;
132 Ok(buffer)
133 }
134
135 pub fn set_int_attribute(&self, attrib_name: &str, value: &[i32]) -> Result<()> {
136 let attrib_name = std::ffi::CString::new(attrib_name)?;
137 ffi::set_workitem_int_attribute(
138 &self.node.session,
139 self.node.handle,
140 self.id.0,
141 &attrib_name,
142 value,
143 )
144 }
145 pub fn get_int_attribute(&self, attr_name: &str) -> Result<Vec<i32>> {
146 let attr_name = std::ffi::CString::new(attr_name)?;
147 let attr_size = ffi::get_workitem_attribute_size(
148 &self.node.session,
149 self.node.handle,
150 self.id.0,
151 &attr_name,
152 )?;
153 let mut buffer = Vec::new();
154 buffer.resize(attr_size as usize, 0);
155 ffi::get_workitem_int_attribute(
156 &self.node.session,
157 self.node.handle,
158 self.id.0,
159 &attr_name,
160 &mut buffer,
161 )?;
162 Ok(buffer)
163 }
164
165 pub fn set_float_attribute(&self, attrib_name: &str, value: &[f32]) -> Result<()> {
166 let attrib_name = std::ffi::CString::new(attrib_name)?;
167 ffi::set_workitem_float_attribute(
168 &self.node.session,
169 self.node.handle,
170 self.id.0,
171 &attrib_name,
172 value,
173 )
174 }
175
176 pub fn get_float_attribute(&self, attr_name: &str) -> Result<Vec<f32>> {
177 let attr_name = std::ffi::CString::new(attr_name)?;
178 let attr_size = ffi::get_workitem_attribute_size(
179 &self.node.session,
180 self.node.handle,
181 self.id.0,
182 &attr_name,
183 )?;
184 let mut buffer = Vec::new();
185 buffer.resize(attr_size as usize, 0.0);
186 ffi::get_workitem_float_attribute(
187 &self.node.session,
188 self.node.handle,
189 self.id.0,
190 &attr_name,
191 &mut buffer,
192 )?;
193 Ok(buffer)
194 }
195}
196
197#[derive(Debug, Clone)]
198pub struct TopNode {
200 pub node: HoudiniNode,
201}
202
203#[derive(Debug, Copy, Clone)]
205pub struct CookStep {
206 pub event: PDGEventInfo,
207 pub graph_id: i32,
208 pub graph_name: i32,
209}
210
211fn create_events() -> Vec<ffi::raw::HAPI_PDG_EventInfo> {
213 const NUM: usize = 32;
214 vec![
215 ffi::raw::HAPI_PDG_EventInfo {
216 nodeId: -1,
217 workItemId: -1,
218 dependencyId: -1,
219 currentState: -1,
220 lastState: -1,
221 eventType: -1,
222 msgSH: -1,
223 };
224 NUM
225 ]
226}
227
228impl TopNode {
229 pub fn cook_async<F>(&self, all_outputs: bool, mut func: F) -> Result<()>
241 where
242 F: FnMut(CookStep) -> Result<ControlFlow<bool>>,
243 {
244 let session = &self.node.session;
245 log::debug!("Start cooking PDG node: {}", self.node.path()?);
246 debug_assert!(session.is_valid());
247 ffi::cook_pdg(session, self.node.handle, false, false, all_outputs)?;
248 let mut events = create_events();
249 'main: loop {
250 let (graph_ids, graph_names) = ffi::get_pdg_contexts(session)?;
251 debug_assert_eq!(graph_ids.len(), graph_names.len());
252 for (graph_id, graph_name) in graph_ids.into_iter().zip(graph_names) {
253 for event in ffi::get_pdg_events(session, graph_id, &mut events)? {
254 let event = PDGEventInfo(*event);
255 match event.event_type() {
256 PdgEventType::EventCookComplete => break 'main,
257 _ => {
258 match func(CookStep {
259 event,
260 graph_id,
261 graph_name,
262 }) {
263 Err(e) => return Err(e),
264 Ok(ControlFlow::Continue(_)) => {}
265 Ok(ControlFlow::Break(stop_cooking)) => {
266 if stop_cooking {
267 ffi::cancel_pdg_cook(session, graph_id)?;
269 }
270 break 'main;
271 }
272 }
273 }
274 }
275 }
276 }
277 }
278 Ok(())
279 }
280
281 pub fn cook_pdg_blocking(&self, all_outputs: bool) -> Result<()> {
285 ffi::cook_pdg(
286 &self.node.session,
287 self.node.handle,
288 false,
289 true,
290 all_outputs,
291 )
292 }
293
294 pub fn get_context_id(&self) -> Result<i32> {
296 ffi::get_pdg_context_id(&self.node.session, self.node.handle)
297 }
298
299 pub fn cancel_cooking(&self) -> Result<()> {
301 log::debug!("Cancel PDG cooking {}", self.node.path()?);
302 let context = self.get_context_id()?;
303 ffi::cancel_pdg_cook(&self.node.session, context)
304 }
305
306 pub fn pause_cooking(&self) -> Result<()> {
308 log::debug!("Pause PDG cooking {}", self.node.path()?);
309 let context = self.get_context_id()?;
310 ffi::pause_pdg_cook(&self.node.session, context)
311 }
312
313 pub fn dirty_node(&self, clean_results: bool) -> Result<()> {
315 log::debug!("Set PDG node dirty {}", self.node.path()?);
316 ffi::dirty_pdg_node(&self.node.session, self.node.handle, clean_results)
317 }
318
319 pub fn get_current_state(&self, context_id: Option<i32>) -> Result<PdgState> {
321 let context = match context_id {
322 Some(c) => c,
323 None => self.get_context_id()?,
324 };
325 ffi::get_pdg_state(&self.node.session, context)
326 }
327
328 pub fn get_workitem(&self, workitem_id: WorkItemId) -> Result<PDGWorkItem<'_>> {
330 let context_id = self.get_context_id()?;
331 ffi::get_workitem_info(&self.node.session, context_id, workitem_id.0).map(|_| PDGWorkItem {
332 id: workitem_id,
333 context_id,
334 node: &self.node,
335 })
336 }
337
338 pub fn get_all_workitems(&self) -> Result<Vec<PDGWorkItem<'_>>> {
339 let context_id = self.get_context_id()?;
340 ffi::get_pdg_workitems(&self.node.session, self.node.handle).map(|vec| {
341 vec.into_iter()
342 .map(|id| PDGWorkItem {
343 id: WorkItemId(id),
344 context_id,
345 node: &self.node,
346 })
347 .collect()
348 })
349 }
350
351 pub fn create_workitem(
352 &self,
353 name: &str,
354 index: i32,
355 context_id: Option<i32>,
356 ) -> Result<PDGWorkItem> {
357 let name = std::ffi::CString::new(name)?;
358 let context_id = match context_id {
359 Some(c) => c,
360 None => self.get_context_id()?,
361 };
362 let id = ffi::create_pdg_workitem(&self.node.session, self.node.handle, &name, index)?;
363 Ok(PDGWorkItem {
364 id: WorkItemId(id),
365 context_id,
366 node: &self.node,
367 })
368 }
369
370 pub fn commit_workitems(&self) -> Result<()> {
371 ffi::commit_pdg_workitems(&self.node.session, self.node.handle)
372 }
373}