Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deref support. #99

Open
cheako opened this issue Aug 29, 2021 · 0 comments
Open

Deref support. #99

cheako opened this issue Aug 29, 2021 · 0 comments
Labels
bug The crate does not work as expected

Comments

@cheako
Copy link

cheako commented Aug 29, 2021

Describe the bug
The Rust Pattern of wrapping one type in another was suggested to me as a solution of working with reference counting.

struct RcA(a-sys::A);

pub struct A(Rc<RcA>);

The Deref trait would remove .0 from all over the place. The point is to implement Drop for RcA such that the destructor for a-sys::A would be called. My target is ash/Vulkan.

A real world example: Much of the boilerplate is hidden inside macros, so only the unique parts are shown.

impl<T> Device<T> {
    pub fn new(
        instance: Instance<T>,
        physical_device: vk::PhysicalDevice,
        create_info: &vk::DeviceCreateInfo,
        user: T,
    ) -> Result<Self> {
        let inner = unsafe { instance.create_device(physical_device, create_info, None) }
            .context(VkCreateDevice {})?;
        let acceleration_structure_fn = AccelerationStructure::new(&**instance, &inner);
        let ray_tracing_fn = RayTracingPipeline::new(&**instance, &inner);
        Ok(Self(Rc::new(RcDevice {
            inner,
            instance,
            acceleration_structure_fn,
            ray_tracing_fn,
            user,
        })))
    }
}

pub struct RcDevice<T> {
    pub inner: VkDevice,
    pub instance: Instance<T>,
    pub acceleration_structure_fn: AccelerationStructure,
    pub ray_tracing_fn: RayTracingPipeline,
    pub user: T,
}

impl<T> Deref for RcDevice<T> {
    type Target = VkDevice;

    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

impl<T> Drop for RcDevice<T> {
    fn drop(&mut self) {
        unsafe {
            self.inner.destroy_device(None);
        };
    }
}

impl<T> Fence<T> {
    pub fn new(device: Device<T>, create_info: &vk::FenceCreateInfo, user: T) -> Result<Self> {
        Ok(Self(Rc::new(RcFence {
            inner: unsafe { device.create_fence(create_info, None) }.context(VkCreateFence {})?,
            device,
            user,
        })))
    }
}

pub struct RcFence<T> {
    pub inner: vk::Fence,
    pub device: Device<T>,
    pub user: T,
}

impl<T> Drop for RcFence<T> {
    fn drop(&mut self) {
        unsafe {
            self.device.inner.destroy_fence(**self, None);
        };
    }
}

Fence and CommandBuffer https://gitlab.com/cheako/ash-tray-rs/-/blob/c9a1ea4db239fca214f0cea09030096fc0390c3b/src/vk_helper.rs#L598-925 are two extremes and DescriptoSet is a complex example as well.

Expected behavior

    macro_rules! vk_subinner_types {
        ($($t:ident)*) => ($(
            concat_idents::concat_idents! (RcName = Rc, $t {
                impl<T> Deref for RcName<T> {
                    type Target = vk::$t;

                    fn deref(&self) -> &Self::Target {
                        &self.inner
                    }
                }
            });
        )*)
    }

    macro_rules! vk_inner_types {
        ($($t:ident)*) => ($(
            concat_idents::concat_idents! (RcName = Rc, $t {
                impl<T> Deref for $t<T> {
                    type Target = RcName<T>;

                    fn deref(&self) -> &Self::Target {
                        &self.inner
                    }
                }
            });
        )*)
    }

Most of the types(notably Device and Fence from above) are defined with this macro:

macro_rules! vk_tuple_types {
    ($($t:ident)*) => ($(
        concat_idents::concat_idents! (RcName = Rc, $t {
            #[derive(Derivative)]
            #[derivative(Clone(bound=""))]
            pub struct $t<T> (pub Rc<RcName<T>>);
            impl<T> Deref for $t<T> {
                type Target = RcName<T>;

                fn deref(&self) -> &Self::Target {
                    &self.0
                }
            }
        });
    )*)
}

A keen eye would have noticed that Device has a one-of Deref, actually two types like that. It's because "use ash::Device as VkDevice" != "use ash::vk; vk::Device"

Version (please complete the following information):

rustup --version
cargo --version
rustc --version
$ exit
rustup 1.24.3 (ce5817a94 2021-05-31)
info: This is the version for the rustup toolchain manager, not the rustc compiler.
info: The currently active `rustc` version is `rustc 1.56.0-nightly (ad02dc46b 2021-08-26)`
cargo 1.56.0-nightly (e96bdb0c3 2021-08-17)
rustc 1.56.0-nightly (ad02dc46b 2021-08-26)
  • Version of derivative: 2.2.0

Additional context
I also do a lot of testing with CI and it's obviously not easy to run the above --version script. Though I need that information as well, so if you look at https://gitlab.com/cheako/ash-tray-rs/-/pipelines and https://gitlab.com/cheako/hazel-rs/-/pipelines you'll get more examples of version info and resulting errors. Thought I don't believe you'll find errors there, even for the fixed errors because generic is not Clone where I don't think I pushed any.

Also note that I'd love any criticism of this code, it often keeps me up at night wondering if this code is any good... And now I can add wondering if it's too much Golf.

@cheako cheako added the bug The crate does not work as expected label Aug 29, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug The crate does not work as expected
Projects
None yet
Development

No branches or pull requests

1 participant