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

Add DirectXShaderCompiler example #802

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ exclude = [".github", ".windows", "docs", "examples"]
[dependencies]
windows_macros = { path = "crates/macros", version = "0.10.0", optional = true }
gen = { package = "windows_gen", path = "crates/gen", version = "0.10.0", optional = true }

const-sha1 = "0.2"
widestring = "0.4"

[dev-dependencies]
gen = { package = "windows_gen", path = "crates/gen" }
Expand Down
74 changes: 44 additions & 30 deletions crates/gen/src/types/bstr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,83 +4,95 @@ pub fn gen_bstr() -> TokenStream {
quote! {
#[repr(transparent)]
#[derive(::std::cmp::Eq)]
pub struct BSTR(*mut u16);
/// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/automat/bstr#remarks
/// Uses [`::windows::widestring::UStr`] and not [`::windows::widestring::UCstr`], the latter checks for internal nulls.
pub struct BSTR(*mut ::windows::widestring::WideChar);
impl BSTR {
pub fn is_empty(&self) -> bool {
// TODO: Should possibly also check length!
self.0.is_null()
}
fn from_wide(value: &[u16]) -> Self {
if value.len() == 0 {
pub fn len(&self) -> usize {
unsafe { SysStringLen(self) as usize }
}
fn from_wide(value: &::windows::widestring::WideStr) -> Self {
if value.is_empty() {
return Self(::std::ptr::null_mut());
}

unsafe { SysAllocStringLen(super::SystemServices::PWSTR(value.as_ptr() as _), value.len() as u32) }
unsafe {
SysAllocStringLen(
super::SystemServices::PWSTR(value.as_ptr() as _),
value.len() as u32,
)
}
}
fn as_wide(&self) -> &[u16] {
fn as_wide(&self) -> &::windows::widestring::WideStr {
if self.0.is_null() {
return &[];
// `UStr` unlike `UCStr` doesn't implement an empty-string default yet
::windows::widestring::WideStr::from_slice(&[])
} else {
unsafe { ::windows::widestring::WideStr::from_ptr(self.0, self.len()) }
}

unsafe { ::std::slice::from_raw_parts(self.0 as *const u16, SysStringLen(self) as usize) }
}
}
impl ::std::clone::Clone for BSTR {
fn clone(&self) -> Self {
Self::from_wide(self.as_wide())
}
}

impl ::std::convert::From<&str> for BSTR {
fn from(value: &str) -> Self {
let value: ::std::vec::Vec<u16> = value.encode_utf16().collect();
// TODO: This allocates+copies twice.
let value = ::windows::widestring::WideString::from_str(value);
Self::from_wide(&value)
}
}

impl ::std::convert::From<::std::string::String> for BSTR {
fn from(value: ::std::string::String) -> Self {
value.as_str().into()
}
}

impl ::std::convert::From<&::std::string::String> for BSTR {
fn from(value: &::std::string::String) -> Self {
value.as_str().into()
}
}
impl<'a> ::std::convert::TryFrom<&'a BSTR> for ::std::string::String {
type Error = ::std::string::FromUtf16Error;

#[cfg(windows)]
type FromWidestringError = ::std::string::FromUtf16Error;
#[cfg(not(windows))]
type FromWidestringError = ::windows::widestring::FromUtf32Error;
impl<'a> ::std::convert::TryFrom<&'a BSTR> for ::std::string::String {
type Error = FromWidestringError;
fn try_from(value: &BSTR) -> ::std::result::Result<Self, Self::Error> {
::std::string::String::from_utf16(value.as_wide())
value.as_wide().to_string()
}
}

impl ::std::convert::TryFrom<BSTR> for ::std::string::String {
type Error = ::std::string::FromUtf16Error;

type Error = FromWidestringError;
fn try_from(value: BSTR) -> ::std::result::Result<Self, Self::Error> {
::std::string::String::try_from(&value)
value.as_wide().to_string()
}
}

impl ::std::default::Default for BSTR {
fn default() -> Self {
Self(::std::ptr::null_mut())
}
}

impl ::std::fmt::Display for BSTR {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
use ::std::fmt::Write;
for c in ::std::char::decode_utf16(self.as_wide().iter().cloned()) {
f.write_char(c.map_err(|_| ::std::fmt::Error)?)?
}
Ok(())
f.write_str(&self.as_wide().to_string().unwrap())
}
}
impl ::std::fmt::Debug for BSTR {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::std::write!(f, "{}", self)
}
}

impl ::std::cmp::PartialEq for BSTR {
fn eq(&self, other: &Self) -> bool {
self.as_wide() == other.as_wide()
Expand All @@ -98,30 +110,32 @@ pub fn gen_bstr() -> TokenStream {
}
impl ::std::cmp::PartialEq<&str> for BSTR {
fn eq(&self, other: &&str) -> bool {
self.as_wide().iter().copied().eq(other.encode_utf16())
let other = ::windows::widestring::WideString::from_str(other);
self.as_wide().eq(&other)
}
}

impl ::std::cmp::PartialEq<BSTR> for &str {
fn eq(&self, other: &BSTR) -> bool {
other == self
}
}

impl ::std::ops::Drop for BSTR {
fn drop(&mut self) {
if !self.0.is_null() {
unsafe { SysFreeString(self as &Self); }
unsafe { SysFreeString(self as &Self) };
}
}
}

unsafe impl ::windows::Abi for BSTR {
type Abi = *mut u16;
type Abi = *mut ::windows::widestring::WideChar;

fn set_abi(&mut self) -> *mut *mut u16 {
fn set_abi(&mut self) -> *mut *mut ::windows::widestring::WideChar {
debug_assert!(self.0.is_null());
&mut self.0 as *mut _ as _
}
}
pub type BSTR_abi = *mut u16;
pub type BSTR_abi = *mut ::windows::widestring::WideChar;
}
}
16 changes: 16 additions & 0 deletions crates/gen/src/types/callback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,24 @@ impl Callback {
quote! {}
};

let query_interface_fn = if signature.has_query_interface() {
let constraints = signature.gen_constraints(&signature.params);
let leading_params = &signature.params[..signature.params.len() - 2];
let params = signature.gen_win32_params(leading_params, gen);
let args = leading_params.iter().map(|p| p.gen_win32_abi_arg());
quote! {
pub unsafe fn #name<#constraints T: ::windows::Interface>(func: &#name, #params) -> ::windows::Result<T> {
let mut result__ = ::std::option::Option::None;
(func)(#(#args,)* &<T as ::windows::Interface>::IID, ::windows::Abi::set_abi(&mut result__)).and_some(result__)
}
}
} else {
quote!()
};

quote! {
pub type #name = unsafe extern "system" fn(#(#params),*) #return_type;
#query_interface_fn
}
}
}
64 changes: 55 additions & 9 deletions crates/gen/src/types/pwstr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,92 @@ use super::*;
pub fn gen_pwstr() -> TokenStream {
quote! {
#[repr(transparent)]
#[derive(::std::clone::Clone, ::std::marker::Copy, ::std::cmp::Eq, ::std::fmt::Debug)]
pub struct PWSTR(pub *mut u16);
#[derive(::std::clone::Clone, ::std::marker::Copy, ::std::cmp::Eq)]
/// Uses [`::windows::widestring::UCStr`] for null-terminated, checked strings.
pub struct PWSTR(pub *mut ::windows::widestring::WideChar);
impl PWSTR {
pub const NULL: Self = Self(::std::ptr::null_mut());
pub fn is_null(&self) -> bool {
self.0.is_null()
}
pub fn is_empty(&self) -> bool {
// TODO: Should possibly also check length!
self.is_null()
}
pub fn len(&self) -> usize {
self.as_wide().len()
}
fn as_wide(&self) -> &::windows::widestring::WideCStr {
if self.is_null() {
Default::default()
} else {
unsafe { ::windows::widestring::WideCStr::from_ptr_str(self.0) }
}
}
}

impl ::std::default::Default for PWSTR {
fn default() -> Self {
Self(::std::ptr::null_mut())
}
}
// TODO: impl Debug and Display to display value and PartialEq etc

impl ::std::fmt::Display for PWSTR {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
f.write_str(&self.as_wide().to_string().unwrap())
}
}
impl ::std::fmt::Debug for PWSTR {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::std::write!(f, "{}", self)
}
}

impl ::std::cmp::PartialEq for PWSTR {
fn eq(&self, other: &Self) -> bool {
// TODO: do value compare
self.0 == other.0
self.as_wide().eq(other.as_wide())
}
}
impl ::std::cmp::PartialEq<&str> for PWSTR {
fn eq(&self, other: &&str) -> bool {
let other = unsafe { ::windows::widestring::WideCString::from_str_unchecked(other) };
self.as_wide().eq(&other)
}
}
impl ::std::cmp::PartialEq<PWSTR> for &str {
fn eq(&self, other: &PWSTR) -> bool {
other.eq(self)
}
}

unsafe impl ::windows::Abi for PWSTR {
type Abi = Self;

fn drop_param(param: &mut ::windows::Param<Self>) {
if let ::windows::Param::Boxed(value) = param {
if !value.0.is_null() {
unsafe { ::std::boxed::Box::from_raw(value.0); }
if !value.is_null() {
unsafe { ::windows::widestring::WideCString::from_raw(value.0) };
}
}
}
}
impl<'a> ::windows::IntoParam<'a, PWSTR> for &'a str {
fn into_param(self) -> ::windows::Param<'a, PWSTR> {
::windows::Param::Boxed(PWSTR(::std::boxed::Box::<[u16]>::into_raw(self.encode_utf16().chain(::std::iter::once(0)).collect::<std::vec::Vec<u16>>().into_boxed_slice()) as _))
::windows::Param::Boxed(PWSTR(
::windows::widestring::WideCString::from_str(self)
.unwrap()
.into_raw(),
))
}
}
impl<'a> ::windows::IntoParam<'a, PWSTR> for String {
fn into_param(self) -> ::windows::Param<'a, PWSTR> {
// TODO: call variant above
::windows::Param::Boxed(PWSTR(::std::boxed::Box::<[u16]>::into_raw(self.encode_utf16().chain(::std::iter::once(0)).collect::<std::vec::Vec<u16>>().into_boxed_slice()) as _))
::windows::Param::Boxed(PWSTR(
::windows::widestring::WideCString::from_str(self)
.unwrap()
.into_raw(),
))
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions examples/dxc/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "dxc"
version = "0.0.0"
authors = ["Microsoft"]
edition = "2018"

[dependencies]
bindings = { package = "dxc_bindings", path = "bindings" }
libloading = "0.7"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately this adds a dependency on winapi :/

windows = { path = "../.." }
11 changes: 11 additions & 0 deletions examples/dxc/bindings/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "dxc_bindings"
version = "0.0.0"
authors = ["Microsoft"]
edition = "2018"

[dependencies]
windows = { path = "../../.." }

[build-dependencies]
windows = { path = "../../.." }
7 changes: 7 additions & 0 deletions examples/dxc/bindings/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
fn main() {
windows::build!(
Windows::Win32::Globalization::CP_UTF8,
Windows::Win32::Graphics::Hlsl::*,
Windows::Win32::System::Diagnostics::Debug::ERROR_FILE_NOT_FOUND,
);
}
1 change: 1 addition & 0 deletions examples/dxc/bindings/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
windows::include_bindings!();
8 changes: 8 additions & 0 deletions examples/dxc/src/copy.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Texture2D<float4> g_input : register(t0, space0);
RWTexture2D<float4> g_output : register(u0, space0);

[numthreads(8, 8, 1)]
void copyCs(uint3 dispatchThreadId : SV_DispatchThreadID)
{
g_output[dispatchThreadId.xy] = g_input[dispatchThreadId.xy];
}
Loading