Skip to content

Commit

Permalink
Changed render_control() signature to reflect API requirements better.
Browse files Browse the repository at this point in the history
  • Loading branch information
virtualritz committed Apr 27, 2023
1 parent e46d5ad commit dc9795e
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 93 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ exr = "1.6"
nsi-3delight = { version = "0.7", path = "crates/nsi-3delight" }
nsi-toolbelt = { version = "0.7", path = "crates/nsi-toolbelt" }
png = "0.17"
# polyhedron-ops = { version = "0.2.7", features = ["nsi"] }
#polyhedron-ops = { version = "0.2.7", features = ["nsi"] }

[[example]]
path = "examples/interactive/main.rs"
Expand Down
20 changes: 12 additions & 8 deletions crates/nsi-core/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
#[cfg(feature = "download_lib3delight")]
#[allow(unused_variables)]
let lib_path = {
use std::io::Write;
use std::{
fs::File,
io::Write,
path::{Path, PathBuf},
};

let lib_path = std::path::PathBuf::from(&std::env::var("OUT_DIR")?);
let lib_path = PathBuf::from(&std::env::var("OUT_DIR")?);

eprintln!("Building against 3Delight 2.9.30");

Expand All @@ -20,8 +24,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
#[cfg(target_os = "linux")]
let lib = "https://www.dropbox.com/s/wfw6w6p41lqd8ko/lib3delight.so";

let lib_path =
lib_path.join(std::path::Path::new(lib).file_name().unwrap());
let lib_path = lib_path.join(Path::new(lib).file_name().unwrap());

eprintln!("lib: {}", lib_path.display());

Expand All @@ -35,7 +38,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.ok()?
.bytes()
.ok()?;
std::fs::File::create(lib_path.clone())
File::create(lib_path.clone())
.expect(&format!("Could not create {}", lib_path.display()))
.write_all(&lib_data)
.expect(&format!(
Expand Down Expand Up @@ -63,14 +66,15 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {

lib_path
} else {
eprintln!("No 3Delight installation found. Make sure $DELIGHT is set.");
return Err(Box::new(std::fmt::Error));
std::path::PathBuf::new()
};

#[cfg(feature = "link_lib3delight")]
{
// Emit linker searchpath.
println!("cargo:rustc-link-search={}", lib_path.display());
if !lib_path.as_path().as_os_str().is_empty() {
println!("cargo:rustc-link-search={}", lib_path.display());
}

// Link to lib3delight.
println!("cargo:rustc-link-lib=dylib=3delight");
Expand Down
167 changes: 113 additions & 54 deletions crates/nsi-core/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,10 @@ impl<'a> Context<'a> {
///
/// # Optional Arguments
///
/// * `"recursive"` ([`Integer`]) -- Specifies whether deletion is recursive.
/// By default, only the specified node is deleted. If a value of `1` is
/// given, then nodes which connect to the specified node are recursively
/// removed. Unless they meet one of the following conditions:
/// * `"recursive"` ([`Integer`]) -- Specifies whether deletion is
/// recursive. By default, only the specified node is deleted. If a value
/// of `1` is given, then nodes which connect to the specified node are
/// recursively removed. Unless they meet one of the following conditions:
/// * They also have connections which do not eventually lead to the
/// specified node.
/// * Their connection to the node to be deleted was created with a
Expand Down Expand Up @@ -316,15 +316,15 @@ impl<'a> Context<'a> {
///
/// * `to` -- The handle of the node to which the connection is made.
///
/// * `to_attr` -- The name of the attribute to which the connection is made.
/// If this is an empty string then the connection is made to the node
/// instead of to a specific attribute of the node.
/// * `to_attr` -- The name of the attribute to which the connection is
/// made. If this is an empty string then the connection is made to the
/// node instead of to a specific attribute of the node.
///
/// # Optional Arguments
///
/// * `"value"` -- This can be used to change the value of a node's attribute
/// in some contexts. Refer to guidelines on inter-object visibility for
/// more information about the utility of this parameter.
/// * `"value"` -- This can be used to change the value of a node's
/// attribute in some contexts. Refer to guidelines on inter-object
/// visibility for more information about the utility of this parameter.
///
/// * `"priority"` ([`Integer`]) -- When connecting attribute nodes,
/// indicates in which order the nodes should be considered when
Expand Down Expand Up @@ -361,9 +361,9 @@ impl<'a> Context<'a> {

/// This function removes a connection between two elements.
///
/// The handle for either node may be the special value [`.all`](crate::node::ALL).
/// This will remove all connections which match the other three
/// arguments.
/// The handle for either node may be the special value
/// [`.all`](crate::node::ALL). This will remove all connections which
/// match the other three arguments.
///
/// # Examples
///
Expand Down Expand Up @@ -459,24 +459,13 @@ impl<'a> Context<'a> {
/// also allows for synchronizing the render with interactive calls that
/// might have been issued.
///
/// # Optional Arguments
///
/// * `"action"` ([`String`]) -- Specifies the operation to be performed,
/// which should be one of the following:
/// * `"start"` -- This starts rendering the scene in the provided context.
/// The render starts in parallel and the control flow is not blocked.
///
/// * `"wait"` -- Wait for a render to finish.
///
/// * `"synchronize"` -- For an interactive render, apply all the buffered
/// calls to scene’s state.
/// # Arguments
///
/// * `"suspend"` -- Suspends render in the provided context.
/// * `action` -- Specifies the render [`Action`] to be performed on the
/// scene.
///
/// * `"resume"` -- Resumes a previously suspended render.
/// # Optional Arguments
///
/// * `"stop"` -- Stops rendering in the provided context without
/// destroying the scene.
/// * `"progressive"` ([`Integer`]) -- If set to `1`, render the image in a
/// progressive fashion.
///
Expand All @@ -486,33 +475,82 @@ impl<'a> Context<'a> {
/// rendering is finished. Interactive renders are by definition
/// progressive.
///
/// * `"frame"` -- Specifies the frame number of this render.
/// * `"callback"` ([`FnStatus`]) -- A closure that will be called be when
/// the status of the render changes.
///
/// # Example
///
/// ```
/// # use nsi_core as nsi;
/// # let ctx = nsi::Context::new(None).unwrap();
/// let status_callback = nsi::StatusCallback::new(
/// |_ctx: &nsi::Context, status: nsi::RenderStatus| {
/// println!("Status: {:?}", status);
/// },
/// );
///
/// /// The renderer will abort because we didn't define an output driver.
/// /// So our status_callback() above will receive RenderStatus::Aborted.
/// ctx.render_control(
/// nsi::Action::Start,
/// Some(&[
/// nsi::integer!("interactive", true as _),
/// nsi::callback!("callback", status_callback),
/// ]),
/// );
///
/// // Block until the renderer is really done.
/// ctx.render_control(nsi::Action::Wait, None);
/// ```
#[inline]
pub fn render_control(&self, args: &ArgSlice<'_, 'a>) {
let (_, _, mut args_out) = get_c_param_vec(Some(args));
pub fn render_control(
&self,
action: Action,
args: Option<&ArgSlice<'_, 'a>>,
) {
let (_, _, mut args_out) = get_c_param_vec(args);

let fn_pointer: nsi_sys::NSIRenderStopped =
Some(render_status as extern "C" fn(*mut c_void, i32, i32));

if let Some(arg) =
args.iter().find(|arg| Ustr::from("callback") == arg.name)
{
args_out.push(nsi_sys::NSIParam {
name: Ustr::from("stoppedcallback").as_char_ptr(),
data: &fn_pointer as *const _ as _,
type_: NSIType::Pointer as _,
arraylength: 0,
count: 1,
flags: 0,
});
args_out.push(nsi_sys::NSIParam {
name: Ustr::from("stoppedcallbackdata").as_char_ptr(),
data: &arg.data.as_c_ptr() as *const _ as _,
type_: NSIType::Pointer as _,
arraylength: 1,
count: 1,
flags: 0,
});
args_out.push(nsi_sys::NSIParam {
name: Ustr::from("action").as_char_ptr(),
data: &Ustr::from(match action {
Action::Start => "start",
Action::Wait => "wait",
Action::Synchronize => "synchronize",
Action::Suspend => "suspend",
Action::Resume => "resume",
Action::Stop => "stop",
})
.as_char_ptr() as *const _ as _,
type_: NSIType::String as _,
arraylength: 0,
count: 1,
flags: 0,
});

if let Some(args) = args {
if let Some(arg) =
args.iter().find(|arg| Ustr::from("callback") == arg.name)
{
args_out.push(nsi_sys::NSIParam {
name: Ustr::from("stoppedcallback").as_char_ptr(),
data: &fn_pointer as *const _ as _,
type_: NSIType::Pointer as _,
arraylength: 0,
count: 1,
flags: 0,
});
args_out.push(nsi_sys::NSIParam {
name: Ustr::from("stoppedcallbackdata").as_char_ptr(),
data: &arg.data.as_c_ptr() as *const _ as _,
type_: NSIType::Pointer as _,
arraylength: 1,
count: 1,
flags: 0,
});
}
}

NSI_API.NSIRenderControl(
Expand All @@ -523,6 +561,27 @@ impl<'a> Context<'a> {
}
}

/// The render action to perform when calling
/// [`render_control()`](Context::render_control()).
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Action {
/// Starts rendering the scene in the provided context. The render starts
/// in parallel -- this does not block.
Start,
/// Wait for a render to finish. This blocks.
Wait,
/// For an interactive render, apply all the buffered calls to scene's
/// state.
Synchronize,
/// Suspends render. Does not block.
Suspend,
/// Resumes a previously suspended render. Does not block.
Resume,
/// Stops rendering in the provided context without destroying the scene.
/// Does not block.
Stop,
}

/// The status of a *interactive* render session.
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, num_enum::FromPrimitive)]
Expand Down Expand Up @@ -550,10 +609,10 @@ pub enum RenderStatus {
/// },
/// );
///
/// ctx.render_control(&[
/// nsi::string!("action", "start"),
/// nsi::callback!("callback", status_callback),
/// ]);
/// ctx.render_control(
/// nsi::Action::Start,
/// Some(&[nsi::callback!("callback", status_callback)]),
/// );
/// ```
pub trait FnStatus<'a>: Fn(
// The [`Context`] for which this closure was called.
Expand Down
4 changes: 2 additions & 2 deletions crates/nsi-core/src/output/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
//! pixels during and/or after a render, in-memory.
//!
//! There are three types of closure:
//! * [`FnOpen`] is called once when the
//! [`OutputDriver`](crate::OUTPUT_LAYER) is *opened* by the renderer.
//! * [`FnOpen`] is called once when the [`OutputDriver`](crate::OUTPUT_LAYER)
//! is *opened* by the renderer.
//!
//! * [`FnWrite`] is called for each bucket of pixel data the renderer sends to
//! the [`OutputDriver`](crate::OUTPUT_DRIVER).
Expand Down
9 changes: 2 additions & 7 deletions crates/nsi-core/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,13 +214,8 @@ fn live_edit() {
None,
);

// Start interactive render.
c.render_control(&[nsi::string!("action", "start")]); //, interactive=1)

// Let it render a while.
//thread::sleep(time::Duration::from_secs(5));

c.render_control(&[nsi::string!("action", "wait")]);
c.render_control(nsi::Action::Start, None);
c.render_control(nsi::Action::Wait, None);
}

// FIXME: port rest of live_edit example from Python
4 changes: 2 additions & 2 deletions crates/nsi-jupyter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ pub fn as_jupyter(ctx: &nsi::Context, screen: &str) {
);

// And now, render it!
ctx.render_control(&[nsi::string!("action", "start")]);
ctx.render_control(nsi::Action::Start, None);
// Block until render is finished.
ctx.render_control(&[nsi::string!("action", "wait")]);
ctx.render_control(nsi::Action::Wait, None);

// Make our Context pristine again.
ctx.delete("jupyter_beauty", Some(&[nsi::integer!("recursive", 1)]));
Expand Down
18 changes: 10 additions & 8 deletions examples/interactive/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,22 @@ use nsi_core as nsi;
fn main() {
let ctx = nsi::Context::new(None).unwrap();

let status_callback = nsi::context::StatusCallback::new(
|_ctx: &nsi::Context, status: nsi::context::RenderStatus| {
let status_callback = nsi::StatusCallback::new(
|_ctx: &nsi::Context, status: nsi::RenderStatus| {
println!("Status: {:?}", status);
},
);

// The renderer will abort because we didn't define an output driver.
// So our status_callback() above will receive RenderStatus::Aborted.
ctx.render_control(&[
nsi::string!("action", "start"),
nsi::integer!("interactive", true as _),
nsi::callback!("callback", status_callback),
]);
ctx.render_control(
nsi::Action::Start,
Some(&[
nsi::integer!("interactive", true as _),
nsi::callback!("callback", status_callback),
]),
);

// Block until the renderer is really done.
ctx.render_control(&[nsi::string!("action", "wait")]);
ctx.render_control(nsi::Action::Wait, None);
}
4 changes: 2 additions & 2 deletions examples/output/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,6 @@ pub(crate) fn nsi_render<'a>(
nsi_reflective_ground(&ctx);

// And now, render it!
ctx.render_control(&[nsi::string!("action", "start")]);
ctx.render_control(&[nsi::string!("action", "wait")]);
ctx.render_control(nsi::Action::Start, None);
ctx.render_control(nsi::Action::Wait, None);
}
4 changes: 2 additions & 2 deletions examples/volume/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,6 @@ pub fn main() {
);

// And now, render it!
ctx.render_control(&[nsi::string!("action", "start")]);
ctx.render_control(&[nsi::string!("action", "wait")]);
ctx.render_control(nsi::Action::Start, None);
ctx.render_control(nsi::Action::Wait, None);
}
Loading

0 comments on commit dc9795e

Please sign in to comment.