Skip to content

Commit

Permalink
Record the Cost in EvaluatedBlock and EvalBlockError.
Browse files Browse the repository at this point in the history
  • Loading branch information
kpreid committed Dec 24, 2023
1 parent 2e9ad43 commit 9213b3d
Show file tree
Hide file tree
Showing 10 changed files with 247 additions and 77 deletions.
14 changes: 5 additions & 9 deletions all-is-cubes/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,24 +457,20 @@ impl Block {
/// TODO: Placeholder name. At some point we may expose `EvalFilter` directly and make
/// this be just `evaluate()`.
pub(crate) fn evaluate2(&self, filter: &EvalFilter) -> Result<EvaluatedBlock, EvalBlockError> {
match self.evaluate_impl(filter) {
Ok(ev) => Ok(ev.into()),
Err(err) => Err(err.into_eval_error()),
}
finish_evaluation(filter.budget.get(), self.evaluate_impl(filter), filter)
}

/// Equivalent to `Evoxel::from_block(block.evaluate2(filter))` except for the error type.
/// For use when blocks contain other blocks as voxels.
fn evaluate_to_evoxel_internal(&self, filter: &EvalFilter) -> Result<Evoxel, InEvalError> {
match self.evaluate_impl(filter) {
// TODO: Make this more efficient by not building the full `EvaluatedBlock`
Ok(ev) => Ok(Evoxel::from_block(&ev.into())),
Err(err) => Err(err),
}
// TODO: Make this more efficient by not building the full `EvaluatedBlock`
self.evaluate_impl(filter)
.map(|minev| Evoxel::from_block(&minev.finish(Cost::ZERO /* ignored */)))
}

#[inline]
fn evaluate_impl(&self, filter: &EvalFilter) -> Result<MinEval, InEvalError> {
// The block's primitive counts as 1 component.
Budget::decrement_components(&filter.budget)?;

let mut value: MinEval = match *self.primitive() {
Expand Down
42 changes: 30 additions & 12 deletions all-is-cubes/src/block/block_def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,18 @@ impl BlockDef {
/// This returns the same success or error as `Block::from(ref_to_self).evaluate()` would, not
/// the same as `.block().evaluate()` would.
pub fn evaluate(&self) -> Result<block::EvaluatedBlock, EvalBlockError> {
match self.evaluate_impl(&block::EvalFilter::default()) {
Ok(t) => Ok(block::EvaluatedBlock::from(t)),
Err(e) => Err(e.into_eval_error()),
}
let filter = block::EvalFilter::default();
block::finish_evaluation(
filter.budget.get(),
{
// This decrement makes the cost consistent with evaluating a
// block with Primitive::Indirect.
block::Budget::decrement_components(&filter.budget).unwrap();

self.evaluate_impl(&filter)
},
&filter,
)
}

/// Implementation of block evaluation used by a [`Primitive::Indirect`] pointing to this.
Expand Down Expand Up @@ -460,10 +468,10 @@ impl manyfmt::Fmt<crate::util::StatusText> for BlockDefStepInfo {

#[cfg(test)]
mod tests {
use super::*;
use crate::math::Rgba;
use crate::universe::Universe;

use super::*;
use pretty_assertions::assert_eq;

/// Quick more-than-nothing test for [`BlockDef::evaluate()`] being the same as more usual
/// options.
Expand All @@ -476,13 +484,23 @@ mod tests {
.color(Rgba::new(1.0, 0.0, 0.0, 1.0))
.build();

let eval_bare = block.evaluate();
let eval_bare = block.evaluate().unwrap();
let block_def = BlockDef::new(block);
let eval_def = block_def.evaluate();
let eval_def = block_def.evaluate().unwrap();
let block_def_ref = universe.insert_anonymous(block_def);
let eval_indirect = Block::from(block_def_ref).evaluate();

assert_eq!(eval_bare, eval_def);
assert_eq!(eval_bare, eval_indirect);
let eval_indirect = Block::from(block_def_ref).evaluate().unwrap();

assert_eq!(
eval_def, eval_indirect,
"BlockDef::evaluate() same as Primitive::Indirect"
);
assert_eq!(
block::EvaluatedBlock {
cost: eval_bare.cost,
..eval_def
},
eval_bare,
"BlockDef::evaluate() same except for cost as the def block"
);
}
}
62 changes: 46 additions & 16 deletions all-is-cubes/src/block/evaluated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use euclid::Vector3D;
use ordered_float::NotNan;

use crate::block::{
self, BlockAttributes, BlockCollision,
self, BlockAttributes, BlockCollision, Cost,
Resolution::{self, R1},
};
use crate::math::{
Expand Down Expand Up @@ -79,6 +79,9 @@ pub struct EvaluatedBlock {
/// It doesn't harm normal operation because the point of having this is to compare
/// block shapes, which is trivial if the block is invisible.)
pub voxel_opacity_mask: Option<Vol<Arc<[OpacityCategory]>>>,

/// Cost of performing the evaluation.
pub(crate) cost: Cost,
}

impl fmt::Debug for EvaluatedBlock {
Expand All @@ -93,6 +96,7 @@ impl fmt::Debug for EvaluatedBlock {
visible,
uniform_collision,
voxel_opacity_mask,
cost,
} = self;
let mut ds = fmt.debug_struct("EvaluatedBlock");
if *attributes != BlockAttributes::default() {
Expand Down Expand Up @@ -123,6 +127,7 @@ impl fmt::Debug for EvaluatedBlock {
"voxel_opacity_mask",
&format_args!("{:?}", voxel_opacity_mask.as_ref().map(Vol::bounds)),
);
ds.field("cost", cost);
ds.finish()
}
}
Expand All @@ -132,8 +137,12 @@ impl EvaluatedBlock {

/// Computes the derived values of a voxel block.
///
/// This is also available as `impl From<MinEval> for EvaluatedBlock`.
pub(crate) fn from_voxels(attributes: BlockAttributes, voxels: Evoxels) -> EvaluatedBlock {
/// This is also available as [`MinEval::finish()`].
pub(crate) fn from_voxels(
attributes: BlockAttributes,
voxels: Evoxels,
cost: Cost,
) -> EvaluatedBlock {
// Optimization for single voxels:
// don't allocate any `Vol`s or perform any generalized scans.
if let Some(Evoxel {
Expand Down Expand Up @@ -163,6 +172,7 @@ impl EvaluatedBlock {
} else {
Some(Vol::from_element(color.opacity_category()))
},
cost,
};
}

Expand Down Expand Up @@ -277,6 +287,7 @@ impl EvaluatedBlock {
uniform_collision,
voxel_opacity_mask,
voxels,
cost,
}
}

Expand Down Expand Up @@ -340,7 +351,8 @@ impl EvaluatedBlock {
#[doc(hidden)]
#[track_caller]
pub fn consistency_check(&self) {
let regenerated = EvaluatedBlock::from_voxels(self.attributes.clone(), self.voxels.clone());
let regenerated =
EvaluatedBlock::from_voxels(self.attributes.clone(), self.voxels.clone(), self.cost);
assert_eq!(self, &regenerated);
}
}
Expand Down Expand Up @@ -668,6 +680,11 @@ pub const AIR_EVALUATED: EvaluatedBlock = EvaluatedBlock {
visible: false,
uniform_collision: Some(BlockCollision::None),
voxel_opacity_mask: None,
cost: Cost {
components: 1,
voxels: 0,
recursion: 0,
},
};

/// This separate item is needed to convince the compiler that `AIR_ATTRIBUTES.display_name`
Expand Down Expand Up @@ -701,14 +718,6 @@ pub(crate) struct MinEval {
pub(crate) voxels: Evoxels,
}

impl From<MinEval> for EvaluatedBlock {
fn from(value: MinEval) -> Self {
let MinEval { attributes, voxels } = value;
// TODO: EvaluatedBlock::from* should probably be entirely replaced with this
EvaluatedBlock::from_voxels(attributes, voxels)
}
}

impl From<&EvaluatedBlock> for MinEval {
fn from(value: &EvaluatedBlock) -> Self {
Self {
Expand All @@ -727,6 +736,13 @@ impl From<EvaluatedBlock> for MinEval {
}

impl MinEval {
/// Converts this into an [`EvaluatedBlock`], computing the derived values.
pub(crate) fn finish(self, cost: Cost) -> EvaluatedBlock {
let MinEval { attributes, voxels } = self;
// TODO: EvaluatedBlock::from* should probably be entirely replaced with this
EvaluatedBlock::from_voxels(attributes, voxels, cost)
}

pub fn resolution(&self) -> Resolution {
self.voxels.resolution()
}
Expand Down Expand Up @@ -774,6 +790,11 @@ mod tests {
collision: Hard,
},
voxel_opacity_mask: Some(GridAab(0..1, 0..1, 0..1)),
cost: Cost {
components: 1,
voxels: 0,
recursion: 0,
},
}
"}
);
Expand Down Expand Up @@ -815,6 +836,11 @@ mod tests {
resolution: 2,
voxels: GridAab(0..2, 0..2, 0..2),
voxel_opacity_mask: Some(GridAab(0..2, 0..2, 0..2)),
cost: Cost {
components: 1,
voxels: 8,
recursion: 0,
},
}
"}
);
Expand Down Expand Up @@ -854,7 +880,8 @@ mod tests {
assert_eq!(
EvaluatedBlock::from_voxels(
attributes.clone(),
Evoxels::Many(resolution, Vol::from_fn(bounds, |_| unreachable!()))
Evoxels::Many(resolution, Vol::from_fn(bounds, |_| unreachable!())),
Cost::ZERO
),
EvaluatedBlock {
attributes,
Expand All @@ -865,7 +892,8 @@ mod tests {
opaque: FaceMap::repeat(false),
visible: false,
uniform_collision: Some(BlockCollision::None),
voxel_opacity_mask: None
voxel_opacity_mask: None,
cost: Cost::ZERO, // TODO wrong
}
);
}
Expand All @@ -882,10 +910,12 @@ mod tests {
Rgba::new(0.0, 0.5, 1.0, 0.5),
] {
let voxel = Evoxel::from_color(color);
let ev_one = EvaluatedBlock::from_voxels(attributes.clone(), Evoxels::One(voxel));
let ev_one =
EvaluatedBlock::from_voxels(attributes.clone(), Evoxels::One(voxel), Cost::ZERO);
let ev_many = EvaluatedBlock::from_voxels(
attributes.clone(),
Evoxels::Many(R2, Vol::from_fn(GridAab::for_block(R2), |_| voxel)),
Cost::ZERO,
);

// Check that they are identical except for the voxel data
Expand Down Expand Up @@ -925,7 +955,7 @@ mod tests {
);

// The inner_color should be ignored because it is not visible.
let ev = EvaluatedBlock::from_voxels(BlockAttributes::default(), voxels);
let ev = EvaluatedBlock::from_voxels(BlockAttributes::default(), voxels, Cost::ZERO);

assert_eq!(ev.color, outer_color);
}
Expand Down
Loading

0 comments on commit 9213b3d

Please sign in to comment.