game: create viewport position to world position function, still wip

The viewport size is hard defined, and render target scale is not incorporated in the calculation
This commit is contained in:
SeanOMik 2025-04-05 10:08:53 -04:00
parent e223bf669b
commit d44eb3b5cf
Signed by: SeanOMik
GPG key ID: FEC9E2FC15235964
7 changed files with 272 additions and 94 deletions
crates
lyra-game/src
event.rs
render/graph/passes
scene
sprite
lyra-math/src
lyra-resource/src

View file

@ -146,6 +146,14 @@ impl<T: Event> EventReader<T> {
}
}
impl<T: Event> Iterator for EventReader<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
self.read()
}
}
pub struct EventWriter<T: Event> {
events: Arc<AtomicRefCell<WaterfallVec<T>>>,
}

View file

@ -9,10 +9,12 @@ use crate::{
render::{
camera::CameraUniform,
graph::{
Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext, SlotAttribute, SlotValue
Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext, SlotAttribute, SlotValue,
},
render_buffer::BufferWrapper, texture::RenderTexture,
}, scene::{Camera, CameraProjection},
render_buffer::BufferWrapper,
texture::RenderTexture,
},
scene::{Camera, CameraProjection},
};
#[derive(Debug, Hash, Clone, Default, PartialEq, RenderGraphLabel)]
@ -69,7 +71,8 @@ impl Node for BasePass {
let camera_bg = Arc::new(camera_bg);
// create the depth texture using the utility struct, then take all the required fields
let mut depth_texture = RenderTexture::create_depth_texture(graph.device(), self.screen_size, "depth_texture");
let mut depth_texture =
RenderTexture::create_depth_texture(graph.device(), self.screen_size, "depth_texture");
depth_texture.create_bind_group(&graph.device);
let dt_bg_pair = depth_texture.bindgroup_pair.unwrap();
@ -87,8 +90,16 @@ impl Node for BasePass {
// (RenderGraphLabel, wgpu::BindGroup, Option<wgpu::BindGroupLabel>)
//
// This could make it slightly easier to create this
(&BasePassSlots::DepthTexture, depth_texture_bg, Some(depth_texture_bgl)),
(&BasePassSlots::ScreenSize, screen_size_bg, Some(screen_size_bgl)),
(
&BasePassSlots::DepthTexture,
depth_texture_bg,
Some(depth_texture_bgl),
),
(
&BasePassSlots::ScreenSize,
screen_size_bg,
Some(screen_size_bgl),
),
(&BasePassSlots::Camera, camera_bg, Some(camera_bgl)),
],
);
@ -112,13 +123,24 @@ impl Node for BasePass {
desc
}
fn prepare(&mut self, graph: &mut RenderGraph, world: &mut lyra_ecs::World, context: &mut RenderGraphContext) {
fn prepare(
&mut self,
graph: &mut RenderGraph,
world: &mut lyra_ecs::World,
context: &mut RenderGraphContext,
) {
let mut found_camera = false;
for (camera, projection, transform) in world.view_iter::<(&Camera, &CameraProjection, &Transform)>() {
for (mut camera, projection, transform) in
world.view_iter::<(&mut Camera, &CameraProjection, &Transform)>()
{
if camera.is_active {
let screen_size = graph.view_target().size();
let uniform = CameraUniform::from_component(*transform, *projection, screen_size.as_vec2());
let uniform =
CameraUniform::from_component(*transform, *projection, screen_size.as_vec2());
camera.values.projection_matrix = uniform.projection;
camera.values.view_matrix = uniform.view;
context.queue_buffer_write_with(BasePassSlots::Camera, 0, uniform);
found_camera = true;

View file

@ -1,16 +1,19 @@
use glam::{Mat4, Vec2, Vec3};
use core::f32;
use glam::{Mat4, UVec2, Vec2, Vec3};
use lyra_ecs::{Bundle, Component};
use lyra_math::{Angle, Rect};
use lyra_math::{Angle, Dir3, Ray3, Rect};
use lyra_reflect::Reflect;
use lyra_scene::WorldTransform;
/// The axis to sort render jobs by.
///
///
/// By default, [`Camera2dBundle`] sets the sorting axis to the positive Y axis.
#[derive(Default, Debug, Clone, Copy, PartialEq, Reflect, Component)]
pub struct CameraSortingAxis(pub Vec3);
/// The offset to apply to the entity's position when sorting for rendering.
///
///
/// This is only used if [`CameraSortingAxis`] is spawned on an entity.
#[derive(Default, Debug, Clone, Copy, PartialEq, Reflect, Component)]
pub struct SortingOffset(pub Vec3);
@ -21,15 +24,15 @@ pub enum ScaleMode {
/// No scaling, keep everything the same size as the viewport.
Viewport,
/// The width of the projection in world units.
///
///
/// Height will be set based off of the viewport's aspect ratio.
Width(f32),
/// The height of the projection in world units.
///
///
/// Width will be set based off of the viewport's aspect ratio.
Height(f32),
/// The exact size of the projection in world units.
///
///
/// Ignoring viewport size, likely causes stretching.
Size(Vec2),
/// Keep the projection size below a maximum value in world units, while keeping the aspect ratio.
@ -73,26 +76,26 @@ impl OrthographicProjection {
match self.scale_mode {
ScaleMode::Viewport => {
size = viewport_size;
},
}
ScaleMode::Width(width) => {
let aspect = viewport_size.x / viewport_size.y;
let scaled_height = width / aspect;
size = Vec2::new(width, scaled_height);
},
}
ScaleMode::Height(height) => {
let aspect = viewport_size.x / viewport_size.y;
let scaled_width = height * aspect;
size = Vec2::new(scaled_width, height);
},
}
ScaleMode::Size(s) => {
size = s;
},
}
ScaleMode::MaxSize(s) => {
let clamped = s.min(viewport_size);
size = clamped;
},
}
ScaleMode::MinSize(s) => {
let clamped = s.max(viewport_size);
size = clamped;
@ -101,22 +104,19 @@ impl OrthographicProjection {
let origin = size * self.viewport_origin;
Rect::new(self.scale * -origin.x,
Rect::new(
self.scale * -origin.x,
self.scale * -origin.y,
self.scale * (size.x - origin.x),
self.scale * (size.y - origin.y))
self.scale * (size.y - origin.y),
)
}
pub fn to_mat(&self, viewport_size: Vec2) -> Mat4 {
let rect = self.get_rect(viewport_size);
glam::Mat4::orthographic_rh(
rect.min.x,
rect.max.x,
rect.min.y,
rect.max.y,
-1000.0,
1000.0,
rect.min.x, rect.max.x, rect.min.y, rect.max.y, -1000.0, 1000.0,
)
}
}
@ -171,14 +171,79 @@ impl CameraProjection {
#[derive(Clone, Default, Component, Reflect)]
pub struct Camera2d;
#[derive(Clone, Reflect)]
pub struct Viewport {
/// The physical position of the viewport on the window.
///
/// Top left is (0, 0).
physical_position: UVec2,
/// The physical size of the viewport in the window.
///
/// The origin of the rectangle is the top left corner.
physical_size: UVec2,
}
impl Default for Viewport {
fn default() -> Self {
Self {
physical_position: Default::default(),
// TODO: dont hard code this
physical_size: UVec2::new(1280, 720),
}
}
}
#[derive(Clone, Reflect, Default)]
pub struct CameraValues {
pub(crate) projection_matrix: Mat4,
pub(crate) view_matrix: Mat4,
}
#[derive(Clone, Component, Reflect)]
pub struct Camera {
pub viewport: Viewport,
pub is_active: bool,
pub values: CameraValues,
}
impl Default for Camera {
fn default() -> Self {
Self { is_active: true }
Self {
viewport: Default::default(),
is_active: true,
values: Default::default(),
}
}
}
impl Camera {
/// Creates a ray from the camera that passes through everything beyond `viewport_pos`.
///
/// The ray starts on the near plane of the camera.
///
/// If the camera's projection is orthographic the direction of the ray is always equal to
/// `camera_transform.forward()`.
pub fn viewport_to_world(
&self,
camera_transform: &WorldTransform,
mut viewport_pos: Vec2,
) -> Option<Ray3> {
let target_size = self.viewport.physical_size.as_vec2(); // TODO: divide by render target scale factor
// flip the Y coordinate origin
viewport_pos.y = target_size.y - viewport_pos.y;
let ndc = (viewport_pos / target_size) * 2.0 - Vec2::ONE;
let ndc_to_world =
camera_transform.calculate_mat4() * self.values.projection_matrix.inverse();
let world_near_plane = ndc_to_world.transform_point3(ndc.extend(1.0));
// Using EPSILON because an ndc with Z=0 returns NaNs
let world_far_plane = ndc_to_world.transform_point3(ndc.extend(f32::EPSILON));
Dir3::new(world_far_plane - world_near_plane).map(|dir| Ray3 {
origin: world_near_plane,
direction: dir,
})
}
}
@ -187,7 +252,7 @@ impl Default for Camera {
pub struct CameraBundle {
pub camera: Camera,
pub projection: CameraProjection,
pub sorting_axis: CameraSortingAxis
pub sorting_axis: CameraSortingAxis,
}
/// A component bundle for a 2d Camera entity.
@ -196,7 +261,7 @@ pub struct Camera2dBundle {
pub camera: Camera,
pub projection: CameraProjection,
pub camera_2d: Camera2d,
pub sorting_axis: CameraSortingAxis
pub sorting_axis: CameraSortingAxis,
}
impl Default for Camera2dBundle {

View file

@ -1,7 +1,7 @@
use lyra_ecs::Component;
use lyra_math::{Vec2, Vec3};
use lyra_reflect::Reflect;
use lyra_resource::ResHandle;
use lyra_math::{Vec3, Vec2};
mod texture_atlas;
pub use texture_atlas::*;
@ -13,7 +13,7 @@ mod tilemap;
pub use tilemap::*;
/// How the sprite is positioned and rotated relative to its [`Transform`].
///
///
/// Default pivot is `Pivot::Center`, this makes it easier to rotate the sprites.
#[derive(Debug, Copy, Clone, PartialEq, Default, Component, Reflect)]
pub enum Pivot {
@ -28,14 +28,14 @@ pub enum Pivot {
BottomRight,
BottomCenter,
/// A custom anchor point.
///
///
/// Top left is (-0.5, 0.5), center is (0.0, 0.0).
Custom(Vec2)
Custom(Vec2),
}
impl Pivot {
/// Get the pivot point as a Vec2.
///
///
/// The point is offset from the top left `(0.0, 0.0)`.
pub fn as_vec(&self) -> Vec2 {
match self {
@ -59,3 +59,13 @@ pub struct Sprite {
pub color: Vec3,
pub pivot: Pivot,
}
impl Default for Sprite {
fn default() -> Self {
Self {
texture: Default::default(),
color: Vec3::ONE,
pivot: Default::default(),
}
}
}

View file

@ -21,6 +21,9 @@ pub use i32::*;
pub mod transform;
pub use transform::*;
mod ray3;
pub use ray3::*;
/// A matrix used to convert an OpenGL matrix to a matrix compatible with Wgpu
#[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: glam::Mat4 = glam::Mat4::from_cols_array(&[
@ -28,4 +31,4 @@ pub const OPENGL_TO_WGPU_MATRIX: glam::Mat4 = glam::Mat4::from_cols_array(&[
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 0.5, 0.0,
0.0, 0.0, 0.5, 1.0,
]);
]);

View file

@ -0,0 +1,35 @@
use glam::Vec3;
#[derive(Clone, Copy)]
pub struct Dir3(Vec3);
impl Dir3 {
/// Create a direction from a finite, nonzero [`Vec3`], normalizing it.
///
/// Returns ``None` if the length of the given vector is zero or close to zero, infinite,
/// or `NaN`.
pub fn new(dir: Vec3) -> Option<Self> {
let length = dir.length();
let direction = (length.is_finite() && length > 0.0).then_some(dir / length);
direction.map(|d| Self(d))
}
pub fn x(&self) -> f32 {
self.0.x
}
pub fn y(&self) -> f32 {
self.0.y
}
pub fn z(&self) -> f32 {
self.0.z
}
}
#[derive(Clone, Copy)]
pub struct Ray3 {
pub origin: Vec3,
pub direction: Dir3,
}

View file

@ -1,13 +1,22 @@
use std::{any::{Any, TypeId}, marker::PhantomData, ops::{Deref, DerefMut}, sync::{Arc, Condvar, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}, time::Duration};
use std::{
any::{Any, TypeId},
marker::PhantomData,
ops::{Deref, DerefMut},
sync::{Arc, Condvar, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard},
time::Duration,
};
use crate::{loader::LoaderError, lyra_engine, DependencyState};
use lyra_ecs::Component;
use lyra_reflect::Reflect;
use crate::{loader::LoaderError, lyra_engine, DependencyState};
use uuid::Uuid;
use crate::ResourceStorage;
pub fn optionally_add_to_dep<R: ResourceData>(deps: &mut Vec<UntypedResHandle>, handle: &Option<ResHandle<R>>) {
pub fn optionally_add_to_dep<R: ResourceData>(
deps: &mut Vec<UntypedResHandle>,
handle: &Option<ResHandle<R>>,
) {
if let Some(h) = handle {
deps.push(h.untyped_clone());
}
@ -22,7 +31,7 @@ pub trait ResourceData: Send + Sync + Any + 'static {
fn dependencies(&self) -> Vec<UntypedResHandle>;
/// Recursively collect the dependencies of the Resource.
///
///
/// If a dependency has a child dependency, it will not show up in this list until its
/// parent is loaded.
fn recur_dependencies(&self) -> Vec<UntypedResHandle> {
@ -35,7 +44,7 @@ pub trait ResourceData: Send + Sync + Any + 'static {
ResourceState::Ready(data) => {
let mut deps_dep = data.dependencies();
all_deps.append(&mut deps_dep);
},
}
_ => {}
}
}
@ -81,8 +90,8 @@ impl<'a, T: 'static> std::ops::Deref for ResourceDataRefMut<'a, T> {
// for some reason, if I didn't use `.as_ref`, the downcast would fail.
let d = d.as_ref().as_any();
d.downcast_ref::<T>().unwrap()
},
_ => unreachable!() // ResHandler::data_ref shouldn't allow this to run
}
_ => unreachable!(), // ResHandler::data_ref shouldn't allow this to run
}
}
}
@ -94,8 +103,8 @@ impl<'a, T: 'static> std::ops::DerefMut for ResourceDataRefMut<'a, T> {
// for some reason, if I didn't use `.as_ref`, the downcast would fail.
let d = d.as_mut().as_any_mut();
d.downcast_mut::<T>().unwrap()
},
_ => unreachable!() // ResHandler::data_ref shouldn't allow this to run
}
_ => unreachable!(), // ResHandler::data_ref shouldn't allow this to run
}
}
}
@ -114,8 +123,8 @@ impl<'a, T: 'static> std::ops::Deref for ResourceDataRef<'a, T> {
// for some reason, if I didn't use `.as_ref`, the downcast would fail.
let d = d.as_ref().as_any();
d.downcast_ref::<T>().unwrap()
},
_ => unreachable!() // ResHandler::data_ref shouldn't allow this to run
}
_ => unreachable!(), // ResHandler::data_ref shouldn't allow this to run
}
}
}
@ -131,7 +140,7 @@ pub struct UntypedResource {
}
#[derive(Clone, Component, Reflect)]
pub struct UntypedResHandle{
pub struct UntypedResHandle {
#[reflect(skip)]
pub(crate) res: Arc<RwLock<UntypedResource>>,
tyid: TypeId,
@ -141,7 +150,7 @@ impl UntypedResHandle {
pub fn new(res: UntypedResource, tyid: TypeId) -> Self {
Self {
res: Arc::new(RwLock::new(res)),
tyid
tyid,
}
}
@ -194,7 +203,7 @@ impl UntypedResHandle {
/// Wait for the resource to be loaded, not including its dependencies
/// (see[`UntypedResHandle::wait_recurse_dependencies_load`]).
///
///
/// This blocks the thread without consuming CPU time; its backed by a
/// [`Condvar`](std::sync::Condvar).
pub fn wait_for_load(&self) -> Result<(), LoaderError> {
@ -203,14 +212,14 @@ impl UntypedResHandle {
}
/// Does the same as [`UntypedResHandle::wait_for_load`] but has a timeout.
///
///
/// Returns true if the resource was loaded before hitting the timeout.
pub fn wait_for_load_timeout(&self, timeout: Duration) -> Result<bool, LoaderError> {
self.wait_for_load_timeout_option_impl(Some(timeout))
}
/// Wait for the entire resource, including its dependencies to be loaded.
///
///
/// This blocks the thread without consuming CPU time; its backed by a
/// [`Condvar`](std::sync::Condvar).
pub fn wait_recurse_dependencies_load(&self) -> Result<(), LoaderError> {
@ -219,13 +228,19 @@ impl UntypedResHandle {
}
/// Does the same as [`UntypedResHandle::wait_recurse_dependencies_load`] but has a timeout.
///
///
/// Returns true if the resource was loaded before hitting the timeout.
pub fn wait_recurse_dependencies_load_timeout(&self, timeout: Duration) -> Result<bool, LoaderError> {
pub fn wait_recurse_dependencies_load_timeout(
&self,
timeout: Duration,
) -> Result<bool, LoaderError> {
self.wait_recurse_dependencies_load_timeout_option_impl(Some(timeout))
}
fn wait_for_load_timeout_option_impl(&self, timeout: Option<Duration>) -> Result<bool, LoaderError> {
fn wait_for_load_timeout_option_impl(
&self,
timeout: Option<Duration>,
) -> Result<bool, LoaderError> {
let d = self.read();
if matches!(d.state, ResourceState::Ready(_)) {
return Ok(true);
@ -252,7 +267,10 @@ impl UntypedResHandle {
}
}
fn wait_recurse_dependencies_load_timeout_option_impl(&self, timeout: Option<Duration>) -> Result<bool, LoaderError> {
fn wait_recurse_dependencies_load_timeout_option_impl(
&self,
timeout: Option<Duration>,
) -> Result<bool, LoaderError> {
if !self.wait_for_load_timeout_option_impl(timeout)? {
return Ok(false);
}
@ -270,16 +288,16 @@ impl UntypedResHandle {
}
}
}
Ok(true)
},
}
// self.wait_for_load at the start ensures that the state is ready
_ => unreachable!()
_ => unreachable!(),
}
}
/// Recursively get the state of the dependencies.
///
///
/// This doesn't return any resource data, it can be used to check if the resource and its
/// dependencies are loaded.
pub fn recurse_dependency_state(&self) -> DependencyState {
@ -287,14 +305,14 @@ impl UntypedResHandle {
}
/// Retrieve a typed handle to the resource.
///
///
/// Returns `None` if the types do not match
pub fn as_typed<T: ResourceData>(&self) -> Option<ResHandle<T>> {
self.clone().into_typed()
}
/// Convert `self` into a typed handle.
///
///
/// Returns `None` if the types do not match
pub fn into_typed<T: ResourceData>(self) -> Option<ResHandle<T>> {
if self.tyid == TypeId::of::<T>() {
@ -302,7 +320,9 @@ impl UntypedResHandle {
handle: self,
_marker: PhantomData::<T>,
})
} else { None }
} else {
None
}
}
/// Retrieve the type id of the resource in the handle.
@ -311,8 +331,8 @@ impl UntypedResHandle {
}
}
/// A handle to a resource.
///
/// A handle to a resource.
///
/// # Note
/// This struct has an inner [`RwLock`] to the resource data, so most methods may be blocking.
/// However, the only times it will be blocking is if another thread is reloading the resource
@ -333,7 +353,7 @@ impl<T: ResourceData> Clone for ResHandle<T> {
fn clone(&self) -> Self {
Self {
handle: self.handle.clone(),
_marker: PhantomData::<T>
_marker: PhantomData::<T>,
}
}
}
@ -387,7 +407,7 @@ impl<T: ResourceData> ResHandle<T> {
let d = self.handle.read();
Some(ResourceDataRef {
guard: d,
_marker: PhantomData::<T>
_marker: PhantomData::<T>,
})
} else {
None
@ -399,7 +419,7 @@ impl<T: ResourceData> ResHandle<T> {
let d = self.handle.write();
Some(ResourceDataRefMut {
guard: d,
_marker: PhantomData::<T>
_marker: PhantomData::<T>,
})
} else {
None
@ -407,7 +427,7 @@ impl<T: ResourceData> ResHandle<T> {
}
/// Same as [`on_load`] but consumes self, making it possible to chain from a request.
///
///
/// Example:
/// ```
/// let tree_tileset = resman
@ -418,18 +438,18 @@ impl<T: ResourceData> ResHandle<T> {
/// });
/// ```
pub fn then_on_load<F>(self, f: F) -> Self
where
F: for<'a> FnOnce(ResourceDataRefMut<'a, T>) + Send + 'static
where
F: for<'a> FnOnce(ResourceDataRefMut<'a, T>) + Send + 'static,
{
self.on_load(f);
self
}
/// When the asset is loaded, run a function with a mutable handle to it.
///
///
/// This uses async_std to spawn a blocking thread which will wait for the asset to load.
/// When its loaded, it will get a mutable handle to it and run `f`.
///
///
/// /// Example:
/// ```
/// let tree_tileset = resman
@ -440,20 +460,20 @@ impl<T: ResourceData> ResHandle<T> {
/// });
/// ```
pub fn on_load<F>(&self, f: F) -> &Self
where
F: for<'a> FnOnce(ResourceDataRefMut<'a, T>) + Send + 'static
where
F: for<'a> FnOnce(ResourceDataRefMut<'a, T>) + Send + 'static,
{
let handle = self.handle.clone();
async_std::task::spawn_blocking(move || {
let d = handle.read();
if matches!(d.state, ResourceState::Ready(_)) {
let d = handle.write();
f(ResourceDataRefMut {
guard: d,
_marker: PhantomData::<T>
_marker: PhantomData::<T>,
});
return;
}
@ -467,7 +487,7 @@ impl<T: ResourceData> ResHandle<T> {
let d = handle.write();
f(ResourceDataRefMut {
guard: d,
_marker: PhantomData::<T>
_marker: PhantomData::<T>,
});
});
@ -521,7 +541,7 @@ impl<T: ResourceData> ResourceStorage for ResHandle<T> {
let mut d = self.handle.write();
d.state = new;
}
fn clone_untyped(&self) -> UntypedResHandle {
self.handle.clone()
}
@ -569,17 +589,17 @@ mod tests {
&[]
}
fn load(&self, _: crate::ResourceManager, path: &str) -> crate::loader::PinedBoxLoaderFuture {
fn load(
&self,
_: crate::ResourceManager,
path: &str,
) -> crate::loader::PinedBoxLoaderFuture {
let path = path.to_string();
Box::pin(async move {
let path = PathBuf::from_str(&path).unwrap();
let file_name = path.file_name()
.and_then(|os| os.to_str())
.unwrap();
let path_ext = path.extension()
.and_then(|os| os.to_str())
.unwrap();
let file_name = path.file_name().and_then(|os| os.to_str()).unwrap();
let path_ext = path.extension().and_then(|os| os.to_str()).unwrap();
let res = rand::thread_rng().gen_range(500..1000);
@ -594,7 +614,13 @@ mod tests {
})
}
fn load_bytes(&self, _: crate::ResourceManager, _: Vec<u8>, _: usize, _: usize) -> crate::loader::PinedBoxLoaderFuture {
fn load_bytes(
&self,
_: crate::ResourceManager,
_: Vec<u8>,
_: usize,
_: usize,
) -> crate::loader::PinedBoxLoaderFuture {
unreachable!()
}
@ -603,7 +629,6 @@ mod tests {
}
}
struct SimpleResource {
depend_a: ResHandle<SimpleDepend>,
}
@ -634,7 +659,11 @@ mod tests {
&[]
}
fn load(&self, res_man: crate::ResourceManager, _: &str) -> crate::loader::PinedBoxLoaderFuture {
fn load(
&self,
res_man: crate::ResourceManager,
_: &str,
) -> crate::loader::PinedBoxLoaderFuture {
Box::pin(async move {
let res = rand::thread_rng().gen_range(500..1000);
@ -652,7 +681,13 @@ mod tests {
})
}
fn load_bytes(&self, _: crate::ResourceManager, _: Vec<u8>, _: usize, _: usize) -> crate::loader::PinedBoxLoaderFuture {
fn load_bytes(
&self,
_: crate::ResourceManager,
_: Vec<u8>,
_: usize,
_: usize,
) -> crate::loader::PinedBoxLoaderFuture {
unreachable!()
}
@ -678,4 +713,4 @@ mod tests {
let state = res.recurse_dependency_state();
assert!(!state.is_loading());
}
}
}