Correcting some mistakes I had regarding error handling, enabling optimization on builds, added option handling to BasicSceneBuilder
This commit is contained in:
parent
661fe73867
commit
86151918e2
11 changed files with 197 additions and 50 deletions
14
Cargo.toml
14
Cargo.toml
|
|
@ -76,3 +76,17 @@ wrong_self_convention = "allow"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
|
# Renders run through `cargo test`, whose profile inherits from `dev`. Cargo's
|
||||||
|
# default there is opt-level = 0, which costs ~15x on ray throughput.
|
||||||
|
[profile.dev]
|
||||||
|
opt-level = 1
|
||||||
|
|
||||||
|
# Applies to dependencies only -- Cargo excludes workspace members from "*".
|
||||||
|
[profile.dev.package."*"]
|
||||||
|
opt-level = 3
|
||||||
|
|
||||||
|
# `shared` is a workspace member, so it needs naming explicitly. It holds the
|
||||||
|
# geometry/BSDF/sampling math, so it wants full optimisation.
|
||||||
|
[profile.dev.package.shared]
|
||||||
|
opt-level = 3
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,8 @@ version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.100"
|
|
||||||
bitflags = "2.10.0"
|
bitflags = "2.10.0"
|
||||||
half = "2.7.1"
|
half = { version = "2.7.1", default-features = false }
|
||||||
bytemuck = { version = "1.24.0", features = ["derive"] }
|
bytemuck = { version = "1.24.0", features = ["derive"] }
|
||||||
enum_dispatch = "0.3.13"
|
enum_dispatch = "0.3.13"
|
||||||
ash = { version = "0.38", optional = true }
|
ash = { version = "0.38", optional = true }
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use core::fmt;
|
||||||
use core::ops::{
|
use core::ops::{
|
||||||
Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
|
Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
|
||||||
};
|
};
|
||||||
use anyhow::{Result, bail};
|
use crate::utils::error::{Error, Result};
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
use num_traits::Float as NumFloat;
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
|
|
@ -686,7 +686,7 @@ impl ColorEncoding {
|
||||||
match name {
|
match name {
|
||||||
"sRGB" | "srgb" => Ok(ColorEncoding::SRGB(SRGBEncoding)),
|
"sRGB" | "srgb" => Ok(ColorEncoding::SRGB(SRGBEncoding)),
|
||||||
"linear" => Ok(ColorEncoding::Linear(LinearEncoding)),
|
"linear" => Ok(ColorEncoding::Linear(LinearEncoding)),
|
||||||
_ => bail!("Unknown color encoding: {}", name),
|
_ => Err(Error::UnknownColorEncoding),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use crate::core::color::{ColorEncoding, ColorEncodingTrait, LINEAR};
|
||||||
use crate::core::geometry::{Bounds2f, Point2f, Point2fi, Point2i};
|
use crate::core::geometry::{Bounds2f, Point2f, Point2fi, Point2i};
|
||||||
use crate::utils::math::{f16_to_f32_software, lerp, square};
|
use crate::utils::math::{f16_to_f32_software, lerp, square};
|
||||||
use crate::{gvec_with_capacity, Float, GVec, Ptr};
|
use crate::{gvec_with_capacity, Float, GVec, Ptr};
|
||||||
use anyhow::{bail, Result};
|
use crate::utils::error::{Error, Result};
|
||||||
use core::hash;
|
use core::hash;
|
||||||
use core::ops::{Deref, DerefMut};
|
use core::ops::{Deref, DerefMut};
|
||||||
use num_traits::Float as NumFloat;
|
use num_traits::Float as NumFloat;
|
||||||
|
|
@ -23,7 +23,7 @@ impl WrapMode {
|
||||||
"black" => Ok(WrapMode::Black),
|
"black" => Ok(WrapMode::Black),
|
||||||
"repeat" => Ok(WrapMode::Repeat),
|
"repeat" => Ok(WrapMode::Repeat),
|
||||||
"octahedralsphere" => Ok(WrapMode::OctahedralSphere),
|
"octahedralsphere" => Ok(WrapMode::OctahedralSphere),
|
||||||
_ => bail!("{:?}: wrap mode unknown", name),
|
_ => Err(Error::UnknownWrapMode),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -503,7 +503,7 @@ impl FilterFunction {
|
||||||
"trilinear" => Ok(FilterFunction::Trilinear),
|
"trilinear" => Ok(FilterFunction::Trilinear),
|
||||||
"bilinear" => Ok(FilterFunction::Bilinear),
|
"bilinear" => Ok(FilterFunction::Bilinear),
|
||||||
"point" => Ok(FilterFunction::Point),
|
"point" => Ok(FilterFunction::Point),
|
||||||
_ => bail!("Filter function unknown"),
|
_ => Err(Error::UnknownFilterFunction),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
39
shared/src/utils/error.rs
Normal file
39
shared/src/utils/error.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
//! Errors for the shared crate.
|
||||||
|
//!
|
||||||
|
//! `shared` is `#![no_std]` and is compiled for SPIR-V and CUDA, so it must not
|
||||||
|
//! depend on `anyhow` -- whose default features enable `std`. These variants are
|
||||||
|
//! `Copy` and allocation-free; the CPU-side caller holds the offending string and
|
||||||
|
//! the `FileLoc`, so it supplies those when reporting.
|
||||||
|
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum Error {
|
||||||
|
UnknownWrapMode,
|
||||||
|
UnknownFilterFunction,
|
||||||
|
UnknownColorEncoding,
|
||||||
|
/// `look_at` received an up vector parallel to the viewing direction.
|
||||||
|
DegenerateLookAt,
|
||||||
|
/// A transform matrix could not be inverted (pbrt's `InverseOrDie`).
|
||||||
|
SingularMatrix,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str(match self {
|
||||||
|
Self::UnknownWrapMode => "unknown wrap mode",
|
||||||
|
Self::UnknownFilterFunction => "unknown filter function",
|
||||||
|
Self::UnknownColorEncoding => "unknown color encoding",
|
||||||
|
Self::DegenerateLookAt => {
|
||||||
|
"LookAt: \"up\" vector and viewing direction are parallel"
|
||||||
|
}
|
||||||
|
Self::SingularMatrix => "matrix is not invertible",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stable in core since 1.81, and the same trait `std::error::Error` re-exports --
|
||||||
|
// so `?` and `anyhow::Context` keep working unchanged on the CPU side.
|
||||||
|
impl core::error::Error for Error {}
|
||||||
|
|
||||||
|
pub type Result<T> = core::result::Result<T, Error>;
|
||||||
|
|
@ -2,6 +2,7 @@ pub mod alloc;
|
||||||
pub mod atomic;
|
pub mod atomic;
|
||||||
pub mod complex;
|
pub mod complex;
|
||||||
pub mod containers;
|
pub mod containers;
|
||||||
|
pub mod error;
|
||||||
pub mod hash;
|
pub mod hash;
|
||||||
pub mod interval;
|
pub mod interval;
|
||||||
pub mod math;
|
pub mod math;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use alloc::string::String;
|
||||||
use crate::Float;
|
use crate::Float;
|
||||||
use crate::core::geometry::{Bounds2f, Bounds2i, Point2f, Point2i};
|
use crate::core::geometry::{Bounds2f, Bounds2i, Point2f, Point2i};
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
|
|
@ -17,6 +18,7 @@ pub struct BasicPBRTOptions {
|
||||||
pub disable_wavelength_jitter: bool,
|
pub disable_wavelength_jitter: bool,
|
||||||
pub disable_texture_filtering: bool,
|
pub disable_texture_filtering: bool,
|
||||||
pub force_diffuse: bool,
|
pub force_diffuse: bool,
|
||||||
|
pub record_pixel_statistics: bool,
|
||||||
pub use_gpu: bool,
|
pub use_gpu: bool,
|
||||||
pub wavefront: bool,
|
pub wavefront: bool,
|
||||||
pub interactive: bool,
|
pub interactive: bool,
|
||||||
|
|
@ -33,6 +35,7 @@ impl Default for BasicPBRTOptions {
|
||||||
disable_wavelength_jitter: false,
|
disable_wavelength_jitter: false,
|
||||||
disable_texture_filtering: false,
|
disable_texture_filtering: false,
|
||||||
force_diffuse: false,
|
force_diffuse: false,
|
||||||
|
record_pixel_statistics: false,
|
||||||
use_gpu: false,
|
use_gpu: false,
|
||||||
wavefront: false,
|
wavefront: false,
|
||||||
interactive: false,
|
interactive: false,
|
||||||
|
|
@ -42,7 +45,7 @@ impl Default for BasicPBRTOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PBRTOptions {
|
pub struct PBRTOptions {
|
||||||
pub basic: BasicPBRTOptions,
|
pub basic: BasicPBRTOptions,
|
||||||
|
|
||||||
|
|
@ -52,8 +55,8 @@ pub struct PBRTOptions {
|
||||||
pub image_file: &'static str,
|
pub image_file: &'static str,
|
||||||
pub pixel_samples: Option<i32>,
|
pub pixel_samples: Option<i32>,
|
||||||
pub gpu_device: Option<u32>,
|
pub gpu_device: Option<u32>,
|
||||||
pub mse_reference_image: Option<&'static str>,
|
pub mse_reference_image: Option<String>,
|
||||||
pub mse_reference_output: Option<&'static str>,
|
pub mse_reference_output: Option<String>,
|
||||||
pub debug_start: Option<(Point2i, i32)>,
|
pub debug_start: Option<(Point2i, i32)>,
|
||||||
pub quick_render: bool,
|
pub quick_render: bool,
|
||||||
pub upgrade: bool,
|
pub upgrade: bool,
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ use crate::core::interaction::{
|
||||||
};
|
};
|
||||||
use crate::utils::gpu_array_from_fn;
|
use crate::utils::gpu_array_from_fn;
|
||||||
use crate::{gamma, Float};
|
use crate::{gamma, Float};
|
||||||
use anyhow::{bail, Context, Result};
|
use crate::utils::error::{Error, Result};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
|
@ -2125,15 +2125,7 @@ pub fn look_at(
|
||||||
// Initialize first three columns of viewing matrix
|
// Initialize first three columns of viewing matrix
|
||||||
let dir = (look - pos).normalize();
|
let dir = (look - pos).normalize();
|
||||||
if Vector3f::from(up).normalize().cross(dir).norm() == 0. {
|
if Vector3f::from(up).normalize().cross(dir).norm() == 0. {
|
||||||
bail!(
|
return Err(Error::DegenerateLookAt);
|
||||||
"LookAt: \"up\" vector ({}, {}, {}) and viewing direction ({}, {}, {}) passed to LookAt are pointing in the same direction.",
|
|
||||||
up.x(),
|
|
||||||
up.y(),
|
|
||||||
up.z(),
|
|
||||||
dir.x(),
|
|
||||||
dir.y(),
|
|
||||||
dir.z()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
let right = Vector3f::from(up).normalize().cross(dir).normalize();
|
let right = Vector3f::from(up).normalize().cross(dir).normalize();
|
||||||
let new_up = dir.cross(right);
|
let new_up = dir.cross(right);
|
||||||
|
|
@ -2150,8 +2142,6 @@ pub fn look_at(
|
||||||
world_from_camera[2][2] = dir.z();
|
world_from_camera[2][2] = dir.z();
|
||||||
world_from_camera[3][2] = 0.;
|
world_from_camera[3][2] = 0.;
|
||||||
|
|
||||||
let camera_from_world = world_from_camera
|
let camera_from_world = world_from_camera.inverse().ok_or(Error::SingularMatrix)?;
|
||||||
.inverse()
|
|
||||||
.context("Failed to inverse viewing matrix")?;
|
|
||||||
Ok(TransformGeneric::new(camera_from_world, world_from_camera))
|
Ok(TransformGeneric::new(camera_from_world, world_from_camera))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,17 @@
|
||||||
use super::entities::*;
|
|
||||||
use super::BasicScene;
|
use super::BasicScene;
|
||||||
|
use super::entities::*;
|
||||||
|
use crate::Arena;
|
||||||
use crate::spectra::get_colorspace_device;
|
use crate::spectra::get_colorspace_device;
|
||||||
use crate::utils::error::FileLoc;
|
use crate::utils::error::FileLoc;
|
||||||
use crate::utils::parameters::{ParameterDictionary, ParsedParameterVector};
|
use crate::utils::parameters::{ParameterDictionary, ParsedParameterVector};
|
||||||
use crate::utils::parser::{ParserError, ParserTarget};
|
use crate::utils::parser::{AtLoc, ParserError, ParserTarget};
|
||||||
use crate::Arena;
|
use shared::Float;
|
||||||
use anyhow::Context;
|
|
||||||
use shared::core::camera::CameraTransform;
|
use shared::core::camera::CameraTransform;
|
||||||
use shared::core::geometry::Vector3f;
|
use shared::core::geometry::Vector3f;
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
use shared::utils::options::RenderingCoordinateSystem;
|
use shared::utils::options::{PBRTOptions, RenderingCoordinateSystem};
|
||||||
use shared::utils::transform;
|
use shared::utils::transform;
|
||||||
use shared::utils::transform::{AnimatedTransform, Transform};
|
use shared::utils::transform::{AnimatedTransform, Transform};
|
||||||
use shared::Float;
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
@ -24,6 +23,16 @@ fn normalize_utf8(input: &str) -> String {
|
||||||
input.nfc().collect::<String>()
|
input.nfc().collect::<String>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// pbrt's `normalizeArg` (util/args.h:22): downcase and drop `-`/`_` so option
|
||||||
|
/// names can be written a little loosely.
|
||||||
|
fn normalize_arg(input: &str) -> String {
|
||||||
|
input
|
||||||
|
.chars()
|
||||||
|
.filter(|c| *c != '-' && *c != '_')
|
||||||
|
.flat_map(|c| c.to_lowercase())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy)]
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
struct TransformSet {
|
struct TransformSet {
|
||||||
t: [Transform; MAX_TRANSFORMS],
|
t: [Transform; MAX_TRANSFORMS],
|
||||||
|
|
@ -125,6 +134,11 @@ pub struct BasicSceneBuilder {
|
||||||
named_material_names: HashSet<String>,
|
named_material_names: HashSet<String>,
|
||||||
medium_names: HashSet<String>,
|
medium_names: HashSet<String>,
|
||||||
|
|
||||||
|
/// `Option` directives accumulate here during parsing. The global options are
|
||||||
|
/// a `OnceLock` written after the parse, so they cannot be mutated in place
|
||||||
|
/// the way pbrt mutates its global `Options`.
|
||||||
|
pending_options: PBRTOptions,
|
||||||
|
|
||||||
current_camera: Option<CameraSceneEntity>,
|
current_camera: Option<CameraSceneEntity>,
|
||||||
current_film: Option<SceneEntity>,
|
current_film: Option<SceneEntity>,
|
||||||
current_integrator: Option<SceneEntity>,
|
current_integrator: Option<SceneEntity>,
|
||||||
|
|
@ -177,6 +191,7 @@ impl BasicSceneBuilder {
|
||||||
spectrum_texture_names: HashSet::new(),
|
spectrum_texture_names: HashSet::new(),
|
||||||
named_material_names: HashSet::new(),
|
named_material_names: HashSet::new(),
|
||||||
medium_names: HashSet::new(),
|
medium_names: HashSet::new(),
|
||||||
|
pending_options: PBRTOptions::default(),
|
||||||
current_camera: Some(CameraSceneEntity {
|
current_camera: Some(CameraSceneEntity {
|
||||||
base: SceneEntity {
|
base: SceneEntity {
|
||||||
name: "perspective".into(),
|
name: "perspective".into(),
|
||||||
|
|
@ -211,6 +226,12 @@ impl BasicSceneBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Options gathered from the scene's `Option` directives. Callers merge these
|
||||||
|
/// with any command-line options and pass the result to `init_pbrt`.
|
||||||
|
pub fn options(&self) -> &PBRTOptions {
|
||||||
|
&self.pending_options
|
||||||
|
}
|
||||||
|
|
||||||
fn for_active_transforms<F>(&mut self, f: F)
|
fn for_active_transforms<F>(&mut self, f: F)
|
||||||
where
|
where
|
||||||
F: Fn(&Transform) -> Transform,
|
F: Fn(&Transform) -> Transform,
|
||||||
|
|
@ -254,12 +275,6 @@ impl BasicSceneBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<anyhow::Error> for ParserError {
|
|
||||||
fn from(e: anyhow::Error) -> Self {
|
|
||||||
ParserError::Generic(e.to_string(), FileLoc::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ParserTarget for BasicSceneBuilder {
|
impl ParserTarget for BasicSceneBuilder {
|
||||||
fn reverse_orientation(&mut self, loc: FileLoc) -> Result<(), ParserError> {
|
fn reverse_orientation(&mut self, loc: FileLoc) -> Result<(), ParserError> {
|
||||||
self.verify_world("ReverseOrientation", &loc)?;
|
self.verify_world("ReverseOrientation", &loc)?;
|
||||||
|
|
@ -329,8 +344,7 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
uz: Float,
|
uz: Float,
|
||||||
loc: FileLoc,
|
loc: FileLoc,
|
||||||
) -> Result<(), ParserError> {
|
) -> Result<(), ParserError> {
|
||||||
let t = transform::look_at((ex, ey, ez), (lx, ly, lz), (ux, uy, uz))
|
let t = transform::look_at((ex, ey, ez), (lx, ly, lz), (ux, uy, uz)).at(&loc)?;
|
||||||
.with_context(|| format!("at {}", loc))?;
|
|
||||||
self.for_active_transforms(|cur| cur * &t);
|
self.for_active_transforms(|cur| cur * &t);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -447,8 +461,65 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn option(&mut self, _name: &str, _value: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
fn option(&mut self, name: &str, value: &str, loc: FileLoc) -> Result<(), ParserError> {
|
||||||
todo!()
|
let bad = |what: &str| {
|
||||||
|
Err(ParserError::Generic(
|
||||||
|
format!("{value:?}: expected {what} for option {name:?}"),
|
||||||
|
loc.clone(),
|
||||||
|
))
|
||||||
|
};
|
||||||
|
let as_bool = |b: &mut bool| match value {
|
||||||
|
"true" => {
|
||||||
|
*b = true;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
"false" => {
|
||||||
|
*b = false;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => bad("\"true\" or \"false\""),
|
||||||
|
};
|
||||||
|
|
||||||
|
let opts = &mut self.pending_options;
|
||||||
|
match normalize_arg(name).as_str() {
|
||||||
|
"disablepixeljitter" => as_bool(&mut opts.basic.disable_pixel_jitter)?,
|
||||||
|
"disabletexturefiltering" => as_bool(&mut opts.basic.disable_texture_filtering)?,
|
||||||
|
"disablewavelengthjitter" => as_bool(&mut opts.basic.disable_wavelength_jitter)?,
|
||||||
|
"forcediffuse" => as_bool(&mut opts.basic.force_diffuse)?,
|
||||||
|
"pixelstats" => as_bool(&mut opts.basic.record_pixel_statistics)?,
|
||||||
|
"wavefront" => as_bool(&mut opts.basic.wavefront)?,
|
||||||
|
"displacementedgescale" => match value.parse::<Float>() {
|
||||||
|
Ok(v) => opts.displacement_edge_scale = v,
|
||||||
|
Err(_) => return bad("a floating-point value"),
|
||||||
|
},
|
||||||
|
"seed" => match value.parse::<i32>() {
|
||||||
|
Ok(v) => opts.basic.seed = v,
|
||||||
|
Err(_) => return bad("an integer"),
|
||||||
|
},
|
||||||
|
// The tokenizer has already dequoted these, so unlike pbrt we do not
|
||||||
|
// re-check for surrounding quotes.
|
||||||
|
"msereferenceimage" => {
|
||||||
|
opts.mse_reference_image = Some(value.to_string())
|
||||||
|
}
|
||||||
|
"msereferenceout" => {
|
||||||
|
opts.mse_reference_output = Some(value.to_string())
|
||||||
|
}
|
||||||
|
"rendercoordsys" => {
|
||||||
|
opts.basic.rendering_space = match value {
|
||||||
|
"camera" => RenderingCoordinateSystem::Camera,
|
||||||
|
"cameraworld" => RenderingCoordinateSystem::CameraWorld,
|
||||||
|
"world" => RenderingCoordinateSystem::World,
|
||||||
|
_ => return bad("\"camera\", \"cameraworld\" or \"world\""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(ParserError::Generic(
|
||||||
|
format!("{name:?}: unknown option"),
|
||||||
|
loc,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pixel_filter(
|
fn pixel_filter(
|
||||||
|
|
@ -534,7 +605,8 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
params,
|
params,
|
||||||
&self.graphics_state.medium_attributes,
|
&self.graphics_state.medium_attributes,
|
||||||
self.graphics_state.color_space.clone(),
|
self.graphics_state.color_space.clone(),
|
||||||
)?;
|
)
|
||||||
|
.at(&loc)?;
|
||||||
let render_from_object = self.render_from_object();
|
let render_from_object = self.render_from_object();
|
||||||
let entity = MediumSceneEntity {
|
let entity = MediumSceneEntity {
|
||||||
base: SceneEntity {
|
base: SceneEntity {
|
||||||
|
|
@ -702,7 +774,8 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
params.clone(),
|
params.clone(),
|
||||||
&self.graphics_state.texture_attributes,
|
&self.graphics_state.texture_attributes,
|
||||||
self.graphics_state.color_space.clone(),
|
self.graphics_state.color_space.clone(),
|
||||||
)?;
|
)
|
||||||
|
.at(&loc)?;
|
||||||
|
|
||||||
if type_name != "float" && type_name != "spectrum" {
|
if type_name != "float" && type_name != "spectrum" {
|
||||||
return Err(ParserError::Generic(
|
return Err(ParserError::Generic(
|
||||||
|
|
@ -762,7 +835,8 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
params,
|
params,
|
||||||
&self.graphics_state.material_attributes,
|
&self.graphics_state.material_attributes,
|
||||||
self.graphics_state.color_space.clone(),
|
self.graphics_state.color_space.clone(),
|
||||||
)?;
|
)
|
||||||
|
.at(&loc)?;
|
||||||
let entity = SceneEntity {
|
let entity = SceneEntity {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
loc,
|
loc,
|
||||||
|
|
@ -794,7 +868,8 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
params,
|
params,
|
||||||
&self.graphics_state.material_attributes,
|
&self.graphics_state.material_attributes,
|
||||||
self.graphics_state.color_space.clone(),
|
self.graphics_state.color_space.clone(),
|
||||||
)?;
|
)
|
||||||
|
.at(&loc)?;
|
||||||
|
|
||||||
// pbrt stores an empty entity name here: the material type comes from the
|
// pbrt stores an empty entity name here: the material type comes from the
|
||||||
// "type" parameter (scene.cpp:719).
|
// "type" parameter (scene.cpp:719).
|
||||||
|
|
@ -827,7 +902,8 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
params.clone(),
|
params.clone(),
|
||||||
&self.graphics_state.medium_attributes,
|
&self.graphics_state.medium_attributes,
|
||||||
self.graphics_state.color_space.clone(),
|
self.graphics_state.color_space.clone(),
|
||||||
)?;
|
)
|
||||||
|
.at(&loc)?;
|
||||||
|
|
||||||
let render_from_light = self.render_from_object();
|
let render_from_light = self.render_from_object();
|
||||||
|
|
||||||
|
|
@ -874,7 +950,8 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
params.clone(),
|
params.clone(),
|
||||||
&self.graphics_state.shape_attributes,
|
&self.graphics_state.shape_attributes,
|
||||||
self.graphics_state.color_space.clone(),
|
self.graphics_state.color_space.clone(),
|
||||||
)?;
|
)
|
||||||
|
.at(&loc)?;
|
||||||
|
|
||||||
let render_from_object = self.render_from_object_at(0);
|
let render_from_object = self.render_from_object_at(0);
|
||||||
let object_from_render = render_from_object.inverse();
|
let object_from_render = render_from_object.inverse();
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use crate::core::texture::{
|
||||||
use crate::utils::mipmap::{MIPMap, MIPMapFilterOptions};
|
use crate::utils::mipmap::{MIPMap, MIPMapFilterOptions};
|
||||||
use crate::utils::{resolve_filename, FileLoc, TextureParameterDictionary};
|
use crate::utils::{resolve_filename, FileLoc, TextureParameterDictionary};
|
||||||
use crate::Arena;
|
use crate::Arena;
|
||||||
use anyhow::Result;
|
use anyhow::{Context, Result};
|
||||||
use shared::core::color::RGB;
|
use shared::core::color::RGB;
|
||||||
use shared::core::color::{ColorEncoding, SRGBEncoding};
|
use shared::core::color::{ColorEncoding, SRGBEncoding};
|
||||||
use shared::core::geometry::Vector2f;
|
use shared::core::geometry::Vector2f;
|
||||||
|
|
@ -271,10 +271,10 @@ impl CreateFloatTexture for FloatImageTexture {
|
||||||
let mut filter_options = MIPMapFilterOptions::default();
|
let mut filter_options = MIPMapFilterOptions::default();
|
||||||
filter_options.max_anisotropy = max_aniso;
|
filter_options.max_anisotropy = max_aniso;
|
||||||
|
|
||||||
let ff = FilterFunction::parse(&filter)?;
|
let ff = FilterFunction::parse(&filter).with_context(|| format!("{:?}", filter))?;
|
||||||
filter_options.filter = ff;
|
filter_options.filter = ff;
|
||||||
let wrap_string = parameters.get_one_string("wrap", "repeat")?;
|
let wrap_string = parameters.get_one_string("wrap", "repeat")?;
|
||||||
let wrap_mode = WrapMode::parse(&wrap_string)?;
|
let wrap_mode = WrapMode::parse(&wrap_string).with_context(|| format!("{:?}", wrap_string))?;
|
||||||
let scale = parameters.get_one_float("scale", 1.)?;
|
let scale = parameters.get_one_float("scale", 1.)?;
|
||||||
let invert = parameters.get_one_bool("invert", false)?;
|
let invert = parameters.get_one_bool("invert", false)?;
|
||||||
let filename = resolve_filename(¶meters.get_one_string("filename", "")?);
|
let filename = resolve_filename(¶meters.get_one_string("filename", "")?);
|
||||||
|
|
@ -287,7 +287,8 @@ impl CreateFloatTexture for FloatImageTexture {
|
||||||
"linear"
|
"linear"
|
||||||
};
|
};
|
||||||
let encoding_str = parameters.get_one_string("encoding", default_encoding)?;
|
let encoding_str = parameters.get_one_string("encoding", default_encoding)?;
|
||||||
let encoding = ColorEncoding::from_name(&encoding_str)?;
|
let encoding =
|
||||||
|
ColorEncoding::from_name(&encoding_str).with_context(|| format!("{:?}", encoding_str))?;
|
||||||
|
|
||||||
let tex = FloatImageTexture::new(
|
let tex = FloatImageTexture::new(
|
||||||
mapping,
|
mapping,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use thiserror::Error;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use flate2::read::GzDecoder;
|
use flate2::read::GzDecoder;
|
||||||
use memmap2::Mmap;
|
use memmap2::Mmap;
|
||||||
|
|
@ -247,17 +248,39 @@ impl Token {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Error)]
|
||||||
pub enum ParserError {
|
pub enum ParserError {
|
||||||
|
#[error("{0}")]
|
||||||
Io(String),
|
Io(String),
|
||||||
|
#[error("unexpected end of file")]
|
||||||
UnexpectedEof,
|
UnexpectedEof,
|
||||||
|
#[error("invalid UTF-8: {0}")]
|
||||||
InvalidUtf8(String),
|
InvalidUtf8(String),
|
||||||
|
#[error("{1}: {0}")]
|
||||||
Generic(String, FileLoc),
|
Generic(String, FileLoc),
|
||||||
|
#[error("{1}: expected an integer: {0}")]
|
||||||
ParseIntError(String, FileLoc),
|
ParseIntError(String, FileLoc),
|
||||||
|
#[error("{1}: expected a float: {0}")]
|
||||||
ParseFloatError(String, FileLoc),
|
ParseFloatError(String, FileLoc),
|
||||||
|
#[error("{1}: numeric overflow: {0}")]
|
||||||
NumericOverflow(String, FileLoc),
|
NumericOverflow(String, FileLoc),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attach a source location to any foreign error, converting it into a
|
||||||
|
/// `ParserError`. This is the only sanctioned direction: typed errors are
|
||||||
|
/// produced *at* the boundary that knows the `FileLoc`, never recovered from an
|
||||||
|
/// erased `anyhow::Error` after the fact.
|
||||||
|
pub trait AtLoc<T> {
|
||||||
|
fn at(self, loc: &FileLoc) -> Result<T, ParserError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E: std::fmt::Display> AtLoc<T> for Result<T, E> {
|
||||||
|
fn at(self, loc: &FileLoc) -> Result<T, ParserError> {
|
||||||
|
// `{:#}` renders anyhow's full context chain; harmless for plain errors.
|
||||||
|
self.map_err(|e| ParserError::Generic(format!("{e:#}"), loc.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub enum TokenizerBuffer {
|
pub enum TokenizerBuffer {
|
||||||
Ram(String),
|
Ram(String),
|
||||||
Mapped(Mmap),
|
Mapped(Mmap),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue