Skip to content

Commit

Permalink
Reimplement crate to use inherent impls over traits (#35)
Browse files Browse the repository at this point in the history
* Redesign API as inherent struct implementations with `Deref` and
  `DerefMut` for handling backwards compatibility

* Rename `Version` to `VersionCode`, `ApiVersion` trait to `Version`

* Use a single `Entry` type, since the aliases point to the same struct

* Add `HasPrevious` trait to recursively determine version compatibility
  at compile-time

* Remove the `prelude` module

* Remove the `api` module

* Move `CaptureOptions`, `InputButton`, `OverlayBits` to `settings.rs`

* Move `DeviceHandle` and `WindowHandle` to `handles.rs`

* Remove `RenderDocV###` trait boilerplate code from `renderdoc-derive`
  • Loading branch information
ebkalderon authored May 18, 2019
1 parent a555b4b commit d0d4ddf
Show file tree
Hide file tree
Showing 8 changed files with 555 additions and 632 deletions.
3 changes: 1 addition & 2 deletions examples/triangle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ extern crate renderdoc;
use gfx::traits::FactoryExt;
use gfx::Device;
use glutin::GlProfile;
use renderdoc::prelude::*;
use renderdoc::{RenderDoc, V110};
use renderdoc::{RenderDoc, V100, V110};

pub type ColorFormat = gfx::format::Rgba8;
pub type DepthFormat = gfx::format::DepthStencil;
Expand Down
110 changes: 24 additions & 86 deletions renderdoc-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ extern crate quote;
extern crate syn;

use proc_macro::TokenStream;
use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
use proc_macro2::{Ident, TokenStream as TokenStream2};
use syn::{Attribute, DeriveInput, Meta, MetaList, NestedMeta};

/// Generates API boilerplate for the `renderdoc` crate.
Expand All @@ -18,9 +18,9 @@ use syn::{Attribute, DeriveInput, Meta, MetaList, NestedMeta};
/// This macro expects a tuple struct of the form:
///
/// ```rust,ignore
/// use renderdoc::ApiVersion;
/// use renderdoc::Version;
///
/// struct Foo<T: ApiVersion>(T::Entry);
/// struct Foo<T: Version>(Entry, PhantomData<T>);
/// ```
///
/// Given the data structure above, this macro generates the following implementations:
Expand All @@ -37,13 +37,10 @@ pub fn renderdoc(input: TokenStream) -> TokenStream {
fn impl_renderdoc(ast: &DeriveInput) -> TokenStream {
let name = &ast.ident;
let apis = build_api_list(&ast.attrs);

let from_impls = gen_from_impls(&name, &apis, TokenStream2::new());
let rd_impls = gen_renderdoc_impls(&name, &apis, TokenStream2::new());

let expanded = quote! {
#from_impls
#rd_impls
};

expanded.into()
Expand Down Expand Up @@ -74,7 +71,9 @@ fn build_api_list(attrs: &[Attribute]) -> Vec<Ident> {
apis
}

/// Generates `From` implementations that permit downgrading of API versions.
/// Generates `From` implementations that permit downgrading of API versions. Unlike the
/// `downgrade()` method, these `From` implementations let any version to downgrade to any other
/// older backwards-compatible API version in a clean way.
///
/// This function takes a list of API versions sorted in ascending order and recursively generates
/// `From` implementations for them. For instance, given the following three API versions
Expand All @@ -83,21 +82,30 @@ fn build_api_list(attrs: &[Attribute]) -> Vec<Ident> {
/// ```rust,ignore
/// // V200 -> V110, V100
///
/// impl From<#name<V200>> for #name<V110> {
/// impl From<#name<V200>> for #name<V110>
/// where
/// Self: Sized,
/// {
/// fn from(newer: #name<V200>) -> Self {
/// // ...
/// }
/// }
///
/// impl From<#name<V200>> for #name<V100> {
/// impl From<#name<V200>> for #name<V100>
/// where
/// Self: Sized,
/// {
/// fn from(newer: #name<V200>) -> Self {
/// // ...
/// }
/// }
///
/// // V110 -> V100
///
/// impl From<#name<V110>> for #name<V100> {
/// impl From<#name<V110>> for #name<V100>
/// where
/// Self: Sized,
/// {
/// fn from(newer: #name<V200>) -> Self {
/// // ...
/// }
Expand All @@ -116,10 +124,13 @@ fn gen_from_impls(name: &Ident, apis: &[Ident], tokens: TokenStream2) -> TokenSt
.iter()
.map(|older| {
quote! {
impl From<#name<#newer>> for #name<#older> {
impl From<#name<#newer>> for #name<#older>
where
Self: Sized,
{
fn from(newer: #name<#newer>) -> Self {
let #name(entry, phantom) = newer;
#name(entry.into(), phantom)
let #name(entry, _) = newer;
#name(entry, PhantomData)
}
}
}
Expand All @@ -132,76 +143,3 @@ fn gen_from_impls(name: &Ident, apis: &[Ident], tokens: TokenStream2) -> TokenSt
tokens.into_iter().chain(impls).collect(),
)
}

/// Generates `RenderDocV###` implementations for statically typing the RenderDoc API.
///
/// This function takes a list of API versions sorted in ascending order and recursively generates
/// `RenderDocV###` implementations for them. For instance, given the following three API versions
/// `[V100, V110, V200]`, these trait implementations will be generated:
///
/// ```rust,ignore
/// // V200 -> Trait methods from V200 down.
///
/// impl RenderDocV200 for #name<V200> {
/// // ...
/// }
///
/// impl RenderDocV110 for #name<V200> {
/// // ...
/// }
///
/// impl RenderDocV100 for #name<V200> {
/// // ...
/// }
///
/// // V110 -> Trait methods from V110 down.
///
/// impl RenderDocV110 for #name<V110> {
/// // ...
/// }
///
/// impl RenderDocV100 for #name<V110> {
/// // ...
/// }
///
/// // V100 -> Trait methods from V100 down.
///
/// impl RenderDocV100 for #name<V100> {
/// // ...
/// }
/// ```
fn gen_renderdoc_impls(name: &Ident, apis: &[Ident], tokens: TokenStream2) -> TokenStream2 {
if apis.len() == 0 {
return tokens;
}

let last_idx = apis.len() - 1;
let version = &apis[last_idx];
let impls: TokenStream2 = apis[0..=last_idx]
.iter()
.map(|api| {
let span = Span::call_site();

let trait_name = Ident::new(&format!("RenderDoc{}", api), span.clone());
let method = Ident::new(
&format!("entry_{}", api.to_string().to_lowercase()),
span.clone(),
);
let ret_val = Ident::new(&format!("Entry{}", api), span);

quote! {
impl ::api::#trait_name for #name<#version> {
unsafe fn #method(&self) -> &::entry::#ret_val {
&self.0
}
}
}
})
.collect();

gen_renderdoc_impls(
name,
&apis[0..last_idx],
tokens.into_iter().chain(impls).collect(),
)
}
85 changes: 85 additions & 0 deletions src/handles.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//! Portable wrapper types around platform specific window and device handles.
use std::os::raw::c_void;

use glutin::os::ContextTraitExt;

/// Raw mutable pointer to the OS-provided window handle.
pub type WindowHandle = *const c_void;

/// Raw mutable pointer to the API's root handle.
///
/// For example, this could be a pointer to an `ID3D11Device`, `HGLRC`/`GLXContext`,
/// `ID3D12Device`, etc.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct DevicePointer(pub(crate) *const c_void);

impl From<*const c_void> for DevicePointer {
fn from(ptr: *const c_void) -> Self {
DevicePointer(ptr)
}
}

impl From<*mut c_void> for DevicePointer {
fn from(ptr: *mut c_void) -> Self {
DevicePointer(ptr)
}
}

#[cfg(windows)]
impl From<winapi::shared::windef::HGLRC> for DevicePointer {
fn from(ctx: winapi::shared::windef::HGLRC) -> Self {
DevicePointer(ctx as *mut _ as *const c_void)
}
}

#[cfg(windows)]
impl From<*mut winapi::um::d3d11::ID3D11Device> for DevicePointer {
fn from(ctx: *mut winapi::um::d3d11::ID3D11Device) -> Self {
DevicePointer(ctx as *mut _ as *const c_void)
}
}

#[cfg(windows)]
impl From<ComPtr<winapi::um::d3d11::ID3D11Device>> for DevicePointer {
fn from(ctx: ComPtr<winapi::um::d3d11::ID3D11Device>) -> Self {
unsafe { DevicePointer(ctx.as_raw() as *mut _ as *const c_void) }
}
}

#[cfg(windows)]
impl From<*mut winapi::um::d3d12::ID3D12Device> for DevicePointer {
fn from(ctx: *mut winapi::um::d3d12::ID3D12Device) -> Self {
DevicePointer(ctx as *mut _ as *const c_void)
}
}

#[cfg(windows)]
impl From<ComPtr<winapi::um::d3d12::ID3D12Device>> for DevicePointer {
fn from(ctx: ComPtr<winapi::um::d3d12::ID3D12Device>) -> Self {
unsafe { DevicePointer(ctx.as_raw() as *mut _ as *const c_void) }
}
}

#[cfg(feature = "glutin")]
impl<'a, T: glutin::ContextCurrentState> From<&'a glutin::Context<T>> for DevicePointer {
fn from(ctx: &'a glutin::Context<T>) -> Self {
#[cfg(unix)]
unsafe {
use glutin::os::unix::RawHandle;
match ctx.raw_handle() {
RawHandle::Glx(glx) => DevicePointer::from(glx),
_ => panic!("RenderDoc only supports GLX contexts on Unix!"),
}
}

#[cfg(windows)]
unsafe {
use glutin::os::windows::RawHandle;
match ctx.raw_handle() {
RawHandle::Wgl(wgl) => DevicePointer::from(wgl),
_ => panic!("RenderDoc only supports WGL contexts on Windows!"),
}
}
}
}
Loading

0 comments on commit d0d4ddf

Please sign in to comment.