mod gen;
pub use gen::*;
use std::hash::{Hash, Hasher};
use std::path::Path;
use windows::core::{ComInterface, HSTRING, PCWSTR};
use windows::Win32::{
Graphics::Direct3D::Dxc::*, Graphics::Direct3D::*, Graphics::Direct3D12::*,
Graphics::Dxgi::Common::*,
};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(transparent)]
Api(#[from] windows::core::Error),
#[error("{0}")]
Compile(String),
#[error(transparent)]
FromUtf8(#[from] std::string::FromUtf8Error),
#[error("unsupported")]
Unsupported,
}
pub trait Source {
fn to_blob(&self, utils: &IDxcUtils) -> Result<IDxcBlob, Error>;
}
impl Source for str {
#[inline]
fn to_blob(&self, utils: &IDxcUtils) -> Result<IDxcBlob, Error> {
let ret = unsafe {
utils.CreateBlobFromPinned(self.as_ptr() as _, self.len() as _, DXC_CP_UTF8)?
};
Ok(ret.cast().unwrap())
}
}
impl Source for &str {
#[inline]
fn to_blob(&self, utils: &IDxcUtils) -> Result<IDxcBlob, Error> {
let ret = unsafe {
utils.CreateBlobFromPinned(self.as_ptr() as _, self.len() as _, DXC_CP_UTF8)?
};
Ok(ret.cast().unwrap())
}
}
impl Source for String {
#[inline]
fn to_blob(&self, utils: &IDxcUtils) -> Result<IDxcBlob, Error> {
let ret = unsafe {
utils.CreateBlobFromPinned(self.as_ptr() as _, self.len() as _, DXC_CP_UTF8)?
};
Ok(ret.cast().unwrap())
}
}
impl Source for &String {
#[inline]
fn to_blob(&self, utils: &IDxcUtils) -> Result<IDxcBlob, Error> {
let ret = unsafe {
utils.CreateBlobFromPinned(self.as_ptr() as _, self.len() as _, DXC_CP_UTF8)?
};
Ok(ret.cast().unwrap())
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Blob(IDxcBlob);
impl Blob {
#[inline]
pub fn as_ptr(&self) -> *const std::ffi::c_void {
unsafe { self.0.GetBufferPointer() }
}
#[inline]
pub fn len(&self) -> usize {
unsafe { self.0.GetBufferSize() }
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.as_ptr() as _, self.len()) }
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
#[repr(C)]
pub struct ShaderHash {
pub flags: u32,
pub digest: [u8; 16],
}
impl Hash for ShaderHash {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write(&self.digest);
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Version {
Hlsl2018,
Hlsl2021,
}
impl Version {
pub fn as_str(&self) -> &'static str {
match self {
Self::Hlsl2018 => "2018",
Self::Hlsl2021 => "2021",
}
}
}
impl Default for Version {
#[inline]
fn default() -> Self {
Self::Hlsl2018
}
}
#[derive(Debug)]
pub struct Arguments<'a>(Vec<&'a str>);
impl<'a> Arguments<'a> {
#[inline]
pub fn new() -> Self {
Self(vec![])
}
#[inline]
pub fn source_name(mut self, source: &'a str) -> Self {
self.0.push(source);
self
}
#[inline]
pub fn target(mut self, target: &'a str) -> Self {
self.0.push("-T");
self.0.push(target);
self
}
#[inline]
pub fn entry_point(mut self, entry_point: &'a str) -> Self {
self.0.push("-E");
self.0.push(entry_point);
self
}
#[inline]
pub fn include_dir(mut self, path: &'a Path) -> Self {
self.0.push("-I");
self.0.push(path.to_str().unwrap());
self
}
#[inline]
pub fn include_dirs(mut self, paths: &[&'a Path]) -> Self {
for path in paths {
self.0.push("-I");
self.0.push(path.to_str().unwrap());
}
self
}
#[inline]
pub fn define(mut self, d: &'a str) -> Self {
self.0.push("-D");
self.0.push(d);
self
}
#[inline]
pub fn defines(mut self, ds: &[&'a str]) -> Self {
for d in ds {
self.0.push("-D");
self.0.push(d);
}
self
}
#[inline]
pub fn optimization(mut self, level: Option<u32>) -> Self {
let arg = match level {
None => "-Od",
Some(0) => "-O0",
Some(1) => "-O1",
Some(2) => "-O2",
Some(3) => "-O3",
Some(_) => "-O3",
};
self.0.push(arg);
self
}
#[inline]
pub fn enable_strict_mode(mut self) -> Self {
self.0.push("-Ges");
self
}
#[inline]
pub fn no_legacy_cbuf_layout(mut self) -> Self {
self.0.push("-no-legacy-cbuf-layout");
self
}
#[inline]
pub fn no_warnings(mut self) -> Self {
self.0.push("-no-warnings");
self
}
#[inline]
pub fn disable_validation(mut self) -> Self {
self.0.push("-Vd");
self
}
#[inline]
pub fn enable_debug_info(mut self) -> Self {
self.0.push("-Zi");
self
}
#[inline]
pub fn hash_considering_only_binary(mut self) -> Self {
self.0.push("-Zsb");
self
}
#[inline]
pub fn hash_considering_source_info(mut self) -> Self {
self.0.push("-Zss");
self
}
#[inline]
pub fn embed_debug(mut self) -> Self {
self.0.push("-Qembed_debug");
self
}
#[inline]
pub fn warnings_as_errors(mut self) -> Self {
self.0.push("-WX");
self
}
#[inline]
pub fn hlsl_version(mut self, version: Version) -> Self {
self.0.push("-HV");
self.0.push(version.as_str());
self
}
#[inline]
pub fn extra(mut self, args: &[&'a str]) -> Self {
self.0.extend(args);
self
}
}
#[derive(Debug)]
pub struct CompileResult {
result: IDxcResult,
utils: IDxcUtils,
}
impl CompileResult {
fn new(result: IDxcResult, utils: &IDxcUtils) -> Self {
Self {
result,
utils: utils.clone(),
}
}
fn get_output<T>(&self, kind: DXC_OUT_KIND) -> Result<Option<T>, Error>
where
T: ComInterface,
{
unsafe {
self.result
.HasOutput(kind)
.as_bool()
.then(|| {
let mut p: Option<T> = None;
self.result
.GetOutput(kind, std::ptr::null_mut(), &mut p)
.map(|_| p.unwrap())
})
.transpose()
.map_err(|e| e.into())
}
}
fn get_blob(&self, kind: DXC_OUT_KIND) -> Result<Option<Blob>, Error> {
Ok(self.get_output::<IDxcBlob>(kind)?.map(|blob| Blob(blob)))
}
fn get_string(&self, kind: DXC_OUT_KIND) -> Result<Option<String>, Error> {
let ret = self.get_output::<IDxcBlobUtf8>(kind)?.map(|blob| unsafe {
String::from_utf8_lossy(std::slice::from_raw_parts(
blob.GetBufferPointer() as *const u8,
blob.GetBufferSize(),
))
.to_string()
});
Ok(ret)
}
#[inline]
pub fn object(&self) -> Result<Option<Blob>, Error> {
self.get_blob(DXC_OUT_OBJECT)
}
#[inline]
pub fn errors(&self) -> Result<Option<String>, Error> {
self.get_string(DXC_OUT_ERRORS)
}
#[inline]
pub fn pdb(&self) -> Result<Option<Blob>, Error> {
self.get_blob(DXC_OUT_PDB)
}
#[inline]
pub fn shader_hash(&self) -> Result<Option<ShaderHash>, Error> {
let ret = self
.get_output::<IDxcBlob>(DXC_OUT_SHADER_HASH)?
.map(|blob| unsafe {
(blob.GetBufferPointer() as *const ShaderHash)
.as_ref()
.unwrap()
.clone()
});
Ok(ret)
}
#[inline]
pub fn disassembly(&self) -> Result<Option<String>, Error> {
self.get_string(DXC_OUT_DISASSEMBLY)
}
#[inline]
pub fn hlsl(&self) -> Result<Option<String>, Error> {
self.get_string(DXC_OUT_HLSL)
}
#[inline]
pub fn text(&self) -> Result<Option<String>, Error> {
self.get_string(DXC_OUT_TEXT)
}
#[inline]
pub fn reflection(&self) -> Result<Option<Reflection>, Error> {
self.get_output::<IDxcBlob>(DXC_OUT_REFLECTION)?
.map(|blob| unsafe {
let mut p: Option<ID3D12ShaderReflection> = None;
self.utils
.CreateReflection(
&DxcBuffer {
Ptr: blob.GetBufferPointer(),
Size: blob.GetBufferSize(),
Encoding: 0,
},
&ID3D12ShaderReflection::IID,
&mut p as *mut _ as _,
)
.map(|_| Reflection(p.unwrap()))
})
.transpose()
.map_err(|e| e.into())
}
#[inline]
pub fn root_signature(&self) -> Result<Option<Blob>, Error> {
self.get_blob(DXC_OUT_ROOT_SIGNATURE)
}
}
#[derive(Clone, Debug)]
pub struct Dxc {
utils: IDxcUtils,
compiler: IDxcCompiler3,
}
impl Dxc {
#[inline]
pub fn new() -> Result<Self, Error> {
unsafe {
let utils: IDxcUtils = DxcCreateInstance(&CLSID_DxcLibrary)?;
let compiler: IDxcCompiler3 = DxcCreateInstance(&CLSID_DxcCompiler)?;
Ok(Self { utils, compiler })
}
}
#[inline]
pub fn compile(&self, src: impl Source, args: Arguments) -> Result<CompileResult, Error> {
let args: Vec<HSTRING> = args.0.into_iter().map(|arg| HSTRING::from(arg)).collect();
let args: Vec<PCWSTR> = args.iter().map(|arg| PCWSTR(arg.as_ptr())).collect();
let src = src.to_blob(&self.utils)?;
self.compile_impl(src, &args)
}
#[inline]
pub fn compile_from_file(
&self,
src: impl AsRef<Path>,
args: Arguments,
) -> Result<CompileResult, Error> {
let args: Vec<HSTRING> = args.0.into_iter().map(|arg| HSTRING::from(arg)).collect();
let args: Vec<PCWSTR> = args.iter().map(|arg| PCWSTR(arg.as_ptr())).collect();
let src = unsafe {
self.utils
.LoadFile(&HSTRING::from(src.as_ref().as_os_str()), Some(&DXC_CP_UTF8))?
};
self.compile_impl(src.cast().unwrap(), &args)
}
fn compile_impl(&self, src: IDxcBlob, args: &[PCWSTR]) -> Result<CompileResult, Error> {
unsafe {
let buffer = DxcBuffer {
Ptr: src.GetBufferPointer() as _,
Size: src.GetBufferSize(),
Encoding: DXC_CP_UTF8.0,
};
let include = self.utils.CreateDefaultIncludeHandler()?;
let ret: IDxcResult = self
.compiler
.Compile(&buffer, Some(&args), Some(&include))?;
if let Err(e) = ret.GetStatus()?.ok() {
let ret = CompileResult::new(ret, &self.utils);
let errors = ret.errors()?.unwrap_or_else(|| e.message().to_string());
return Err(Error::Compile(errors));
}
Ok(CompileResult::new(ret, &self.utils))
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ComponentType {
Unknown,
Float32,
Sint32,
Uint32,
}
#[derive(Clone, Debug)]
pub struct SignatureParameterDesc {
semantic_name: String,
semantic_index: u32,
system_value_type: Name,
component_type: ComponentType,
mask: u8,
read_write_mask: u8,
stream: u32,
min_precision: MinPrecision,
}
impl SignatureParameterDesc {
#[inline]
pub fn semantic_name(&self) -> &str {
&self.semantic_name
}
#[inline]
pub fn semantic_index(&self) -> u32 {
self.semantic_index
}
#[inline]
pub fn system_value_type(&self) -> Name {
self.system_value_type
}
#[inline]
pub fn component_type(&self) -> ComponentType {
self.component_type
}
#[inline]
pub fn mask(&self) -> u8 {
self.mask
}
#[inline]
pub fn read_write_mask(&self) -> u8 {
self.read_write_mask
}
#[inline]
pub fn stream(&self) -> u32 {
self.stream
}
#[inline]
pub fn min_precision(&self) -> MinPrecision {
self.min_precision
}
#[inline]
pub fn semantic_name_for_input_element(&self) -> Vec<u8> {
let mut b = self.semantic_name.as_bytes().to_vec();
b.push(0);
b
}
#[inline]
pub fn to_format(&self) -> DXGI_FORMAT {
let count = self.mask.count_ones();
match self.component_type {
ComponentType::Unknown => DXGI_FORMAT_UNKNOWN,
ComponentType::Float32 => match count {
1 => DXGI_FORMAT_R32_FLOAT,
2 => DXGI_FORMAT_R32G32_FLOAT,
3 => DXGI_FORMAT_R32G32B32_FLOAT,
4 => DXGI_FORMAT_R32G32B32A32_FLOAT,
_ => DXGI_FORMAT_UNKNOWN,
},
ComponentType::Uint32 => match count {
1 => DXGI_FORMAT_R32_UINT,
2 => DXGI_FORMAT_R32G32_UINT,
3 => DXGI_FORMAT_R32G32B32_UINT,
4 => DXGI_FORMAT_R32G32B32A32_UINT,
_ => DXGI_FORMAT_UNKNOWN,
},
ComponentType::Sint32 => match count {
1 => DXGI_FORMAT_R32_SINT,
2 => DXGI_FORMAT_R32G32_SINT,
3 => DXGI_FORMAT_R32G32B32_SINT,
4 => DXGI_FORMAT_R32G32B32A32_SINT,
_ => DXGI_FORMAT_UNKNOWN,
},
}
}
}
#[derive(Clone, Debug)]
pub struct ShaderInputBindDesc {
name: String,
ty: ShaderInputType,
bind_point: u32,
bind_count: u32,
flags: ShaderInputFlags,
return_type: Option<ResourceReturnType>,
dimension: SrvDimension,
num_samples: u32,
space: u32,
id: u32,
}
impl ShaderInputBindDesc {
#[inline]
pub fn name(&self) -> &str {
&self.name
}
#[inline]
pub fn input_type(&self) -> ShaderInputType {
self.ty
}
#[inline]
pub fn bind_point(&self) -> u32 {
self.bind_point
}
#[inline]
pub fn bind_count(&self) -> u32 {
self.bind_count
}
#[inline]
pub fn flags(&self) -> ShaderInputFlags {
self.flags
}
#[inline]
pub fn return_type(&self) -> Option<ResourceReturnType> {
self.return_type
}
#[inline]
pub fn dimension(&self) -> SrvDimension {
self.dimension
}
#[inline]
pub fn num_samples(&self) -> u32 {
self.num_samples
}
#[inline]
pub fn space(&self) -> u32 {
self.space
}
#[inline]
pub fn id(&self) -> u32 {
self.id
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct ThreadGroupSize {
pub x: u32,
pub y: u32,
pub z: u32,
}
impl ThreadGroupSize {
#[inline]
pub fn total_size(&self) -> u32 {
self.x * self.y * self.z
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum MatrixMajor {
Columns,
Rows,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Vector {
pub ty: Box<Type>,
pub len: usize,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Matrix {
pub ty: Box<Type>,
pub columns: usize,
pub rows: usize,
pub major: MatrixMajor,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Member {
pub name: String,
pub ty: Type,
pub offset: usize,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Struct {
pub name: String,
pub members: Vec<Member>,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Type {
Void,
Bool,
Int,
Uint,
Float,
String,
Min8Float,
Min10Float,
Min16Int,
Min12Int,
Min16Uint,
Vector(Vector),
Matrix(Matrix),
Struct(Struct),
Unsupported,
}
impl Type {
fn svt(t: D3D_SHADER_VARIABLE_TYPE) -> Self {
match t {
D3D_SVT_VOID => Type::Void,
D3D_SVT_BOOL => Type::Bool,
D3D_SVT_INT => Type::Int,
D3D_SVT_UINT => Type::Uint,
D3D_SVT_FLOAT => Type::Float,
D3D_SVT_STRING => Type::String,
D3D_SVT_MIN8FLOAT => Type::Min8Float,
D3D_SVT_MIN10FLOAT => Type::Min10Float,
D3D_SVT_MIN16INT => Type::Min16Int,
D3D_SVT_MIN12INT => Type::Min12Int,
D3D_SVT_MIN16UINT => Type::Min16Uint,
_ => Type::Unsupported,
}
}
fn from_reflection(t: &ID3D12ShaderReflectionType) -> Result<Self, Error> {
let ret = unsafe {
let mut desc = D3D12_SHADER_TYPE_DESC::default();
t.GetDesc(&mut desc)?;
match desc.Class {
D3D_SVC_SCALAR => Self::svt(desc.Type),
D3D_SVC_VECTOR => Self::Vector(Vector {
ty: Box::new(Self::svt(desc.Type)),
len: desc.Columns as usize,
}),
D3D_SVC_MATRIX_COLUMNS => Self::Matrix(Matrix {
ty: Box::new(Self::svt(desc.Type)),
columns: desc.Columns as usize,
rows: desc.Rows as usize,
major: MatrixMajor::Columns,
}),
D3D_SVC_MATRIX_ROWS => Self::Matrix(Matrix {
ty: Box::new(Self::svt(desc.Type)),
columns: desc.Columns as usize,
rows: desc.Rows as usize,
major: MatrixMajor::Rows,
}),
D3D_SVC_STRUCT => Self::Struct(Struct {
name: desc.Name.to_string()?,
members: (0..desc.Members)
.map(|i| -> Result<Member, Error> {
let u = t.GetMemberTypeByIndex(i).unwrap();
let ty = Self::from_reflection(&u)?;
let name = t.GetMemberTypeName(i).to_string()?;
let mut d = D3D12_SHADER_TYPE_DESC::default();
u.GetDesc(&mut d)?;
Ok(Member {
ty,
name,
offset: d.Offset as usize,
})
})
.collect::<Result<Vec<Member>, Error>>()?,
}),
_ => Type::Unsupported,
}
};
Ok(ret)
}
}
#[derive(Clone, Debug)]
pub struct ShaderBufferDesc {
name: String,
members: Vec<Type>,
}
impl ShaderBufferDesc {
#[inline]
pub fn name(&self) -> &str {
&self.name
}
#[inline]
pub fn members(&self) -> &[Type] {
&self.members
}
}
#[derive(Clone, Debug)]
pub struct Reflection(ID3D12ShaderReflection);
impl Reflection {
#[inline]
pub fn new(dxc: &Dxc, buffer: &[u8]) -> Result<Self, Error> {
let buffer = DxcBuffer {
Ptr: buffer.as_ptr() as *const std::ffi::c_void,
Size: buffer.len(),
Encoding: 0,
};
let reflection = unsafe {
let mut p: Option<ID3D12ShaderReflection> = None;
dxc.utils
.CreateReflection(
&buffer,
&ID3D12ShaderReflection::IID,
&mut p as *mut _ as *mut _,
)
.map(|_| p.unwrap())?
};
Ok(Self(reflection))
}
#[inline]
pub fn input_parameters(&self) -> Result<Vec<SignatureParameterDesc>, Error> {
let desc = self.get_desc()?;
unsafe {
(0..desc.InputParameters)
.map(|i| -> Result<SignatureParameterDesc, Error> {
let mut ipd = D3D12_SIGNATURE_PARAMETER_DESC::default();
self.0.GetInputParameterDesc(i, &mut ipd)?;
Ok(SignatureParameterDesc {
semantic_name: ipd.SemanticName.to_string()?,
semantic_index: ipd.SemanticIndex,
system_value_type: Name::new(ipd.SystemValueType)
.ok_or(Error::Unsupported)?,
component_type: match ipd.ComponentType {
D3D_REGISTER_COMPONENT_FLOAT32 => ComponentType::Float32,
D3D_REGISTER_COMPONENT_UINT32 => ComponentType::Uint32,
D3D_REGISTER_COMPONENT_SINT32 => ComponentType::Sint32,
_ => ComponentType::Unknown,
},
mask: ipd.Mask,
read_write_mask: ipd.ReadWriteMask,
stream: ipd.Stream,
min_precision: MinPrecision::new(ipd.MinPrecision)
.ok_or(Error::Unsupported)?,
})
})
.collect()
}
}
#[inline]
pub fn resource_bind_descs(&self) -> Result<Vec<ShaderInputBindDesc>, Error> {
let desc = self.get_desc()?;
unsafe {
(0..desc.BoundResources)
.map(|i| -> Result<ShaderInputBindDesc, Error> {
let mut sibd = D3D12_SHADER_INPUT_BIND_DESC::default();
self.0.GetResourceBindingDesc(i, &mut sibd)?;
Ok(ShaderInputBindDesc {
name: sibd.Name.to_string()?,
ty: ShaderInputType::new(sibd.Type).ok_or(Error::Unsupported)?,
bind_point: sibd.BindPoint,
bind_count: sibd.BindCount,
flags: ShaderInputFlags::new(sibd.uFlags),
return_type: ResourceReturnType::new(sibd.ReturnType),
dimension: SrvDimension::new(sibd.Dimension).ok_or(Error::Unsupported)?,
num_samples: sibd.NumSamples,
space: sibd.Space,
id: sibd.uID,
})
})
.collect()
}
}
#[inline]
pub fn constant_buffer_descs(&self) -> Result<Vec<ShaderBufferDesc>, Error> {
let desc = self.get_desc()?;
unsafe {
(0..desc.ConstantBuffers)
.map(|i| -> Result<ShaderBufferDesc, Error> {
let cb = self.0.GetConstantBufferByIndex(i).unwrap();
let mut cb_desc = D3D12_SHADER_BUFFER_DESC::default();
cb.GetDesc(&mut cb_desc)?;
Ok(ShaderBufferDesc {
name: cb_desc.Name.to_string()?,
members: (0..cb_desc.Variables)
.map(|i| -> Result<Type, Error> {
let v = cb.GetVariableByIndex(i).unwrap();
let mut vd = D3D12_SHADER_VARIABLE_DESC::default();
v.GetDesc(&mut vd)?;
let t = v.GetType().unwrap();
Type::from_reflection(&t)
})
.collect::<Result<Vec<Type>, Error>>()?,
})
})
.collect()
}
}
#[inline]
pub fn thread_group_size(&self) -> ThreadGroupSize {
let mut size = ThreadGroupSize { x: 0, y: 0, z: 0 };
unsafe {
self.0
.GetThreadGroupSize(Some(&mut size.x), Some(&mut size.y), Some(&mut size.z));
}
size
}
#[inline]
pub fn as_raw_handle(&self) -> &ID3D12ShaderReflection {
&self.0
}
fn get_desc(&self) -> Result<D3D12_SHADER_DESC, Error> {
unsafe {
let mut desc = D3D12_SHADER_DESC::default();
self.0.GetDesc(&mut desc)?;
Ok(desc)
}
}
}
#[cfg(test)]
mod tests {
use super::{
Arguments, CompileResult, Matrix, MatrixMajor, Reflection, Struct, Type, Vector,
};
use std::path::Path;
#[test]
fn compile_from_str() {
let dxc = super::Dxc::new().unwrap();
dxc.compile(
include_str!("../assets/example.hlsl"),
Arguments::new().target("vs_6_0").entry_point("vs_main"),
)
.unwrap();
}
#[test]
fn compile_from_string() {
let dxc = super::Dxc::new().unwrap();
dxc.compile(
include_str!("../assets/example.hlsl").to_string(),
Arguments::new().target("vs_6_0").entry_point("vs_main"),
)
.unwrap();
}
#[test]
fn compile_from_string_ref() {
let dxc = super::Dxc::new().unwrap();
dxc.compile(
&include_str!("../assets/example.hlsl").to_string(),
Arguments::new().target("vs_6_0").entry_point("vs_main"),
)
.unwrap();
}
#[test]
fn compile_from_file() {
let dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let path = dir.join("assets/example.hlsl");
let dxc = super::Dxc::new().unwrap();
dxc.compile_from_file(
path.as_path(),
Arguments::new().target("vs_6_0").entry_point("vs_main"),
)
.unwrap();
}
#[test]
fn compile_error() {
let dxc = super::Dxc::new().unwrap();
dxc.compile(
include_str!("../assets/error.hlsl"),
Arguments::new().target("vs_6_0").entry_point("vs_main"),
)
.unwrap_err();
}
#[test]
fn constant_buffer_descs() {
let dxc = super::Dxc::new().unwrap();
let ret: CompileResult = dxc
.compile(
include_str!("../assets/example2.hlsl"),
Arguments::new().target("vs_6_0").entry_point("vs_main"),
)
.unwrap();
let reflection: Reflection = ret.reflection().unwrap().unwrap();
let cb = reflection.constant_buffer_descs().unwrap();
assert!(cb[0].name() == "tf");
let Type::Struct(Struct { name, members }) = &cb[0].members()[0] else {
panic!()
};
assert!(name == "Transform");
let float4x4 = Type::Matrix(Matrix {
ty: Box::new(Type::Float),
columns: 4,
rows: 4,
major: MatrixMajor::Columns,
});
assert!(members[0].name == "view");
assert!(members[0].ty == float4x4);
assert!(members[1].name == "proj");
assert!(members[1].ty == float4x4);
}
#[test]
fn constant_buffer_descs_nest() {
let dxc = super::Dxc::new().unwrap();
let ret: CompileResult = dxc
.compile(
include_str!("../assets/nest.hlsl"),
Arguments::new().target("vs_6_0").entry_point("vs_main"),
)
.unwrap();
let reflection: Reflection = ret.reflection().unwrap().unwrap();
let cb = reflection.constant_buffer_descs().unwrap();
assert!(cb[0].name() == "cb");
let Type::Struct(Struct { name, members }) = &cb[0].members()[0] else {
panic!()
};
assert!(name == "Root");
let float4 = Type::Vector(Vector {
ty: Box::new(Type::Float),
len: 4,
});
let float4x4 = Type::Matrix(Matrix {
ty: Box::new(Type::Float),
columns: 4,
rows: 4,
major: MatrixMajor::Columns,
});
let nest = &members[0];
assert!(nest.name == "n");
{
let Type::Struct(Struct { name, members }) = &nest.ty else {
panic!()
};
assert!(name == "Nest");
assert!(members[0].name == "a");
assert!(members[0].ty == Type::Float);
assert!(members[1].name == "b");
assert!(members[1].ty == float4);
assert!(members[2].name == "c");
assert!(members[2].ty == float4x4);
}
assert!(members[1].name == "p");
assert!(members[1].ty == Type::Float);
}
#[test]
fn reflection_from_compiled_file() {
let dxc = super::Dxc::new().unwrap();
Reflection::new(&dxc, include_bytes!("../assets/example.vs")).unwrap();
}
}