From 463a584917cf80870e2d5de4956cd30224344322 Mon Sep 17 00:00:00 2001 From: chyyran Date: Fri, 3 Feb 2023 19:09:30 -0500 Subject: [PATCH] rs: initial implementation of safe wrapper --- Cargo.lock | 11 ++ spirv-to-dxil-sys/Cargo.toml | 2 - spirv-to-dxil-sys/build.rs | 15 ++- spirv-to-dxil/Cargo.toml | 7 ++ spirv-to-dxil/src/config.rs | 80 ++++++++++++++ spirv-to-dxil/src/enums.rs | 130 ++++++++++++++++++++++ spirv-to-dxil/src/lib.rs | 160 +++++++++++++++++++++++++++- spirv-to-dxil/src/logger.rs | 56 ++++++++++ spirv-to-dxil/src/object.rs | 33 ++++++ spirv-to-dxil/src/specialization.rs | 49 +++++++++ spirv-to-dxil/test/fragment.spv | Bin 0 -> 24428 bytes spirv-to-dxil/test/vertex.spv | Bin 0 -> 2100 bytes 12 files changed, 527 insertions(+), 16 deletions(-) create mode 100644 spirv-to-dxil/src/config.rs create mode 100644 spirv-to-dxil/src/enums.rs create mode 100644 spirv-to-dxil/src/logger.rs create mode 100644 spirv-to-dxil/src/object.rs create mode 100644 spirv-to-dxil/src/specialization.rs create mode 100644 spirv-to-dxil/test/fragment.spv create mode 100644 spirv-to-dxil/test/vertex.spv diff --git a/Cargo.lock b/Cargo.lock index 1973f3c..9a0ea84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,6 +30,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bytemuck" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393" + [[package]] name = "cc" version = "1.0.79" @@ -202,6 +208,11 @@ checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] name = "spirv-to-dxil" version = "0.1.0" +dependencies = [ + "bitflags", + "bytemuck", + "spirv-to-dxil-sys", +] [[package]] name = "spirv-to-dxil-sys" diff --git a/spirv-to-dxil-sys/Cargo.toml b/spirv-to-dxil-sys/Cargo.toml index a9f8432..989879d 100644 --- a/spirv-to-dxil-sys/Cargo.toml +++ b/spirv-to-dxil-sys/Cargo.toml @@ -4,8 +4,6 @@ version = "0.1.0" edition = "2021" [lib] -crate-type = ["cdylib"] - [build-dependencies] bindgen = "0.63.0" diff --git a/spirv-to-dxil-sys/build.rs b/spirv-to-dxil-sys/build.rs index ee5db38..8a40fcd 100644 --- a/spirv-to-dxil-sys/build.rs +++ b/spirv-to-dxil-sys/build.rs @@ -1,17 +1,15 @@ -use std::{fs::File, env, path::PathBuf}; use cmake::Config; +use std::{env, fs::File, path::PathBuf}; fn main() { let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); if env::var("DOCS_RS").is_ok() { - println!("cargo:warning=Skipping spirv-to-dxil native build for docs.rs."); - File::create(out_dir.join("bindings.rs")).unwrap(); - return; + println!("cargo:warning=Skipping spirv-to-dxil native build for docs.rs."); + File::create(out_dir.join("bindings.rs")).unwrap(); + return; } - let cmake_dst = Config::new("native") - .build_target("mesa") - .build(); + let cmake_dst = Config::new("native").build_target("mesa").build(); let object_dst = cmake_dst.join("build/mesa/lib"); @@ -36,5 +34,4 @@ fn main() { bindings .write_to_file(out_dir.join("bindings.rs")) .expect("Couldn't write bindings!"); - -} \ No newline at end of file +} diff --git a/spirv-to-dxil/Cargo.toml b/spirv-to-dxil/Cargo.toml index abfe5a7..91669a2 100644 --- a/spirv-to-dxil/Cargo.toml +++ b/spirv-to-dxil/Cargo.toml @@ -5,4 +5,11 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] + [dependencies] +spirv-to-dxil-sys = { version = "0.1.0", path = "../spirv-to-dxil-sys" } +bitflags = "1.3.2" + +[dev-dependencies] +bytemuck = "1.13.0" \ No newline at end of file diff --git a/spirv-to-dxil/src/config.rs b/spirv-to-dxil/src/config.rs new file mode 100644 index 0000000..fba37ac --- /dev/null +++ b/spirv-to-dxil/src/config.rs @@ -0,0 +1,80 @@ +/// Configuration options for CBVs +#[derive(Default)] +pub struct ConstantBufferConfig { + pub register_space: u32, + pub base_shader_register: u32, +} + +/// Configuration options for SPIR-V YZ flip mode. +#[derive(Default)] +pub struct FlipConfig { + pub mode: crate::FlipMode, + pub y_mask: u16, + pub z_mask: u16, +} + +/// Runtime configuration options for the SPIR-V compilation. +pub struct RuntimeConfig { + pub runtime_data_cbv: ConstantBufferConfig, + pub push_constant_cbv: ConstantBufferConfig, + /// Set true if vertex and instance ids have already been converted to + /// zero-based. Otherwise, runtime_data will be required to lower them. + pub zero_based_vertex_instance_id: bool, + /// Set true if workgroup base is known to be zero + pub zero_based_compute_workgroup_id: bool, + pub yz_flip: FlipConfig, + /// The caller supports read-only images to be turned into SRV accesses, + /// which allows us to run the nir_opt_access() pass + pub read_only_images_as_srvs: bool, + /// Force sample rate shading on a fragment shader + pub force_sample_rate_shading: bool, + /// View index needs to be lowered to a UBO lookup + pub lower_view_index: bool, + /// View index also needs to be forwarded to RT layer output + pub lower_view_index_to_rt_layer: bool, +} + +impl Default for RuntimeConfig { + fn default() -> Self { + RuntimeConfig { + runtime_data_cbv: ConstantBufferConfig { + register_space: 31, + base_shader_register: 0, + }, + push_constant_cbv: Default::default(), + zero_based_vertex_instance_id: true, + zero_based_compute_workgroup_id: false, + yz_flip: Default::default(), + read_only_images_as_srvs: false, + force_sample_rate_shading: false, + lower_view_index: false, + lower_view_index_to_rt_layer: false, + } + } +} + +impl From for spirv_to_dxil_sys::dxil_spirv_runtime_conf { + fn from(value: RuntimeConfig) -> Self { + spirv_to_dxil_sys::dxil_spirv_runtime_conf { + runtime_data_cbv: spirv_to_dxil_sys::dxil_spirv_runtime_conf__bindgen_ty_1 { + register_space: value.runtime_data_cbv.register_space, + base_shader_register: value.runtime_data_cbv.base_shader_register, + }, + push_constant_cbv: spirv_to_dxil_sys::dxil_spirv_runtime_conf__bindgen_ty_2 { + register_space: value.push_constant_cbv.register_space, + base_shader_register: value.push_constant_cbv.base_shader_register, + }, + zero_based_vertex_instance_id: value.zero_based_vertex_instance_id, + zero_based_compute_workgroup_id: value.zero_based_compute_workgroup_id, + yz_flip: spirv_to_dxil_sys::dxil_spirv_runtime_conf__bindgen_ty_3 { + mode: value.yz_flip.mode.bits(), + y_mask: value.yz_flip.y_mask, + z_mask: value.yz_flip.z_mask, + }, + read_only_images_as_srvs: value.read_only_images_as_srvs, + force_sample_rate_shading: value.force_sample_rate_shading, + lower_view_index: value.lower_view_index, + lower_view_index_to_rt_layer: value.lower_view_index_to_rt_layer, + } + } +} diff --git a/spirv-to-dxil/src/enums.rs b/spirv-to-dxil/src/enums.rs new file mode 100644 index 0000000..1a6df0d --- /dev/null +++ b/spirv-to-dxil/src/enums.rs @@ -0,0 +1,130 @@ +use bitflags::bitflags; + +pub enum ShaderStage { + None, + Vertex, + TesselationControl, + TesselationEvaluation, + Geometry, + Fragment, + Compute, + Kernel, +} + +impl From for spirv_to_dxil_sys::dxil_spirv_shader_stage { + fn from(value: ShaderStage) -> Self { + match value { + ShaderStage::None => spirv_to_dxil_sys::dxil_spirv_shader_stage_DXIL_SPIRV_SHADER_NONE, + ShaderStage::Vertex => { + spirv_to_dxil_sys::dxil_spirv_shader_stage_DXIL_SPIRV_SHADER_VERTEX + } + ShaderStage::TesselationControl => { + spirv_to_dxil_sys::dxil_spirv_shader_stage_DXIL_SPIRV_SHADER_TESS_CTRL + } + ShaderStage::TesselationEvaluation => { + spirv_to_dxil_sys::dxil_spirv_shader_stage_DXIL_SPIRV_SHADER_TESS_EVAL + } + ShaderStage::Geometry => { + spirv_to_dxil_sys::dxil_spirv_shader_stage_DXIL_SPIRV_SHADER_GEOMETRY + } + ShaderStage::Fragment => { + spirv_to_dxil_sys::dxil_spirv_shader_stage_DXIL_SPIRV_SHADER_FRAGMENT + } + ShaderStage::Compute => { + spirv_to_dxil_sys::dxil_spirv_shader_stage_DXIL_SPIRV_SHADER_COMPUTE + } + ShaderStage::Kernel => { + spirv_to_dxil_sys::dxil_spirv_shader_stage_DXIL_SPIRV_SHADER_KERNEL + } + } + } +} + +#[non_exhaustive] +pub enum ShaderModel { + ShaderModel6_0, + ShaderModel6_1, + ShaderModel6_2, + ShaderModel6_3, + ShaderModel6_4, + ShaderModel6_5, + ShaderModel6_6, + ShaderModel6_7, +} + +impl From for spirv_to_dxil_sys::dxil_shader_model { + fn from(value: ShaderModel) -> Self { + match value { + ShaderModel::ShaderModel6_0 => spirv_to_dxil_sys::dxil_shader_model_SHADER_MODEL_6_0, + ShaderModel::ShaderModel6_1 => spirv_to_dxil_sys::dxil_shader_model_SHADER_MODEL_6_1, + ShaderModel::ShaderModel6_2 => spirv_to_dxil_sys::dxil_shader_model_SHADER_MODEL_6_2, + ShaderModel::ShaderModel6_3 => spirv_to_dxil_sys::dxil_shader_model_SHADER_MODEL_6_3, + ShaderModel::ShaderModel6_4 => spirv_to_dxil_sys::dxil_shader_model_SHADER_MODEL_6_4, + ShaderModel::ShaderModel6_5 => spirv_to_dxil_sys::dxil_shader_model_SHADER_MODEL_6_5, + ShaderModel::ShaderModel6_6 => spirv_to_dxil_sys::dxil_shader_model_SHADER_MODEL_6_6, + ShaderModel::ShaderModel6_7 => spirv_to_dxil_sys::dxil_shader_model_SHADER_MODEL_6_7, + } + } +} + +#[non_exhaustive] +pub enum ValidatorVersion { + None, + Validator1_0, + Validator1_1, + Validator1_2, + Validator1_3, + Validator1_4, + Validator1_5, + Validator1_6, + Validator1_7, +} + +impl From for spirv_to_dxil_sys::dxil_validator_version { + fn from(value: ValidatorVersion) -> Self { + match value { + ValidatorVersion::None => spirv_to_dxil_sys::dxil_validator_version_NO_DXIL_VALIDATION, + ValidatorVersion::Validator1_0 => { + spirv_to_dxil_sys::dxil_validator_version_DXIL_VALIDATOR_1_0 + } + ValidatorVersion::Validator1_1 => { + spirv_to_dxil_sys::dxil_validator_version_DXIL_VALIDATOR_1_1 + } + ValidatorVersion::Validator1_2 => { + spirv_to_dxil_sys::dxil_validator_version_DXIL_VALIDATOR_1_2 + } + ValidatorVersion::Validator1_3 => { + spirv_to_dxil_sys::dxil_validator_version_DXIL_VALIDATOR_1_3 + } + ValidatorVersion::Validator1_4 => { + spirv_to_dxil_sys::dxil_validator_version_DXIL_VALIDATOR_1_4 + } + ValidatorVersion::Validator1_5 => { + spirv_to_dxil_sys::dxil_validator_version_DXIL_VALIDATOR_1_5 + } + ValidatorVersion::Validator1_6 => { + spirv_to_dxil_sys::dxil_validator_version_DXIL_VALIDATOR_1_6 + } + ValidatorVersion::Validator1_7 => { + spirv_to_dxil_sys::dxil_validator_version_DXIL_VALIDATOR_1_7 + } + } + } +} + +bitflags! { + #[derive(Default)] + pub struct FlipMode: spirv_to_dxil_sys::dxil_spirv_yz_flip_mode { + /// No YZ flip + const NONE = spirv_to_dxil_sys::dxil_spirv_yz_flip_mode_DXIL_SPIRV_YZ_FLIP_NONE; + /// Y-flip is unconditional: pos.y = -pos.y + const Y_FLIP_UNCONDITIONAL = spirv_to_dxil_sys::dxil_spirv_yz_flip_mode_DXIL_SPIRV_Y_FLIP_UNCONDITIONAL; + /// Z-flip is unconditional: pos.z = -pos.z + 1.0f + const Z_FLIP_UNCONDITIONAL = spirv_to_dxil_sys::dxil_spirv_yz_flip_mode_DXIL_SPIRV_Z_FLIP_UNCONDITIONAL; + const YZ_FLIP_UNCONDITIONAL = spirv_to_dxil_sys::dxil_spirv_yz_flip_mode_DXIL_SPIRV_YZ_FLIP_UNCONDITIONAL; + const Y_FLIP_CONDITIONAL = spirv_to_dxil_sys::dxil_spirv_yz_flip_mode_DXIL_SPIRV_Y_FLIP_CONDITIONAL; + /// Z-flip is unconditional: pos.z = -pos.z + 1.0f + const Z_FLIP_CONDITIONAL = spirv_to_dxil_sys::dxil_spirv_yz_flip_mode_DXIL_SPIRV_Z_FLIP_CONDITIONAL; + const YZ_FLIP_CONDITIONAL = spirv_to_dxil_sys::dxil_spirv_yz_flip_mode_DXIL_SPIRV_YZ_FLIP_CONDITIONAL; + } +} diff --git a/spirv-to-dxil/src/lib.rs b/spirv-to-dxil/src/lib.rs index 7d12d9a..5b4a511 100644 --- a/spirv-to-dxil/src/lib.rs +++ b/spirv-to-dxil/src/lib.rs @@ -1,5 +1,134 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right +mod config; +mod enums; +mod object; +mod specialization; +mod logger; + +pub use config::*; +pub use enums::*; +pub use object::*; +pub use specialization::*; + +use std::mem::MaybeUninit; + +use spirv_to_dxil_sys::dxil_spirv_object; +pub use spirv_to_dxil_sys::DXIL_SPIRV_MAX_VIEWPORT; +use crate::logger::Logger; + + +fn spirv_to_dxil_inner( + spirv_words: &[u32], + specializations: Option<&[Specialization]>, + entry_point: &[u8], + stage: ShaderStage, + shader_model_max: ShaderModel, + validator_version_max: ValidatorVersion, + runtime_conf: RuntimeConfig, + dump_nir: bool, + logger: &spirv_to_dxil_sys::dxil_spirv_logger, + out: &mut MaybeUninit, +) -> bool { + let num_specializations = specializations.map(|o| o.len()).unwrap_or(0) as u32; + let mut specializations: Option> = + specializations.map(|o| o.into_iter().map(|o| (*o).into()).collect()); + + let runtime_conf: spirv_to_dxil_sys::dxil_spirv_runtime_conf = + runtime_conf.into(); + + let debug = spirv_to_dxil_sys::dxil_spirv_debug_options { dump_nir }; + + unsafe { + spirv_to_dxil_sys::spirv_to_dxil( + spirv_words.as_ptr(), + spirv_words.len(), + specializations + .as_mut() + .map_or(std::ptr::null_mut(), |x| x.as_mut_ptr()), + num_specializations, + stage.into(), + entry_point.as_ptr().cast(), + shader_model_max.into(), + validator_version_max.into(), + &debug, + &runtime_conf, + logger, + out.as_mut_ptr(), + ) + } +} + +/// Dump NIR to stdout. +pub fn dump_nir( + spirv_words: &[u32], + specializations: Option<&[Specialization]>, + entry_point: impl AsRef, + stage: ShaderStage, + shader_model_max: ShaderModel, + validator_version_max: ValidatorVersion, + runtime_conf: RuntimeConfig, +) -> bool { + let entry_point = entry_point.as_ref(); + let mut entry_point = String::from(entry_point).into_bytes(); + entry_point.push(0); + + + let mut out = MaybeUninit::uninit(); + spirv_to_dxil_inner( + spirv_words, + specializations, + &entry_point, + stage, + shader_model_max, + validator_version_max, + runtime_conf, + true, + &logger::DEBUG_LOGGER, + &mut out, + ) +} + +/// Compile SPIR-V words to a DXIL blob. +pub fn spirv_to_dxil( + spirv_words: &[u32], + specializations: Option<&[Specialization]>, + entry_point: impl AsRef, + stage: ShaderStage, + shader_model_max: ShaderModel, + validator_version_max: ValidatorVersion, + runtime_conf: RuntimeConfig, +) -> Result { + let entry_point = entry_point.as_ref(); + let mut entry_point = String::from(entry_point).into_bytes(); + entry_point.push(0); + + let logger = Logger::new(); + let logger = logger.into_logger(); + let mut out = MaybeUninit::uninit(); + + let result = spirv_to_dxil_inner( + spirv_words, + specializations, + &entry_point, + stage, + shader_model_max, + validator_version_max, + runtime_conf, + false, + &logger, + &mut out, + ); + + let logger = unsafe { + Logger::finalize(logger) + }; + + if result { + let out = unsafe { out.assume_init() }; + + Ok(DxilObject::new(out)) + } else { + Err(logger) + } } #[cfg(test)] @@ -7,8 +136,29 @@ mod tests { use super::*; #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); + fn dump_nir() { + let fragment: &[u8] = include_bytes!("../test/fragment.spv"); + let fragment = Vec::from(fragment); + let fragment = bytemuck::cast_slice(&fragment); + + super::dump_nir(&fragment, + None, "main", + ShaderStage::Fragment, + ShaderModel::ShaderModel6_0, ValidatorVersion::None, + RuntimeConfig::default()); + } + + #[test] + fn test_compile() { + let fragment: &[u8] = include_bytes!("../test/fragment.spv"); + let fragment = Vec::from(fragment); + let fragment = bytemuck::cast_slice(&fragment); + + super::spirv_to_dxil(&fragment, + None, "main", + ShaderStage::Fragment, + ShaderModel::ShaderModel6_0, ValidatorVersion::None, + RuntimeConfig::default()) + .expect("failed to compile"); } } diff --git a/spirv-to-dxil/src/logger.rs b/spirv-to-dxil/src/logger.rs new file mode 100644 index 0000000..b285154 --- /dev/null +++ b/spirv-to-dxil/src/logger.rs @@ -0,0 +1,56 @@ +use std::ffi::CStr; +use std::os::raw::{c_char, c_void}; + +extern "C" fn stdout_logger(_: *mut c_void, msg: *const c_char) { + let msg = unsafe { + CStr::from_ptr(msg) + }; + + println!("[spirv-to-dxil] {:?}", msg) +} + +pub(crate) const DEBUG_LOGGER: spirv_to_dxil_sys::dxil_spirv_logger = spirv_to_dxil_sys::dxil_spirv_logger { + priv_: std::ptr::null_mut(), + log: Some(stdout_logger), +}; + +extern "C" fn string_logger(out: *mut c_void, msg: *const c_char) { + let logger = out.cast::(); + let str = unsafe { + std::ptr::addr_of_mut!((*logger).msg) + .as_mut().unwrap() + }; + + let msg = unsafe { + CStr::from_ptr(msg) + }; + + str.push_str(msg.to_string_lossy().as_ref()) +} + +pub(crate) struct Logger { + msg: String +} + +impl Logger { + pub fn new() -> Logger { + Logger { + msg: String::new() + } + } + + pub fn into_logger(self) -> spirv_to_dxil_sys::dxil_spirv_logger { + spirv_to_dxil_sys::dxil_spirv_logger { + priv_: Box::into_raw(Box::new(self)).cast(), + log: Some(string_logger), + } + } + + pub unsafe fn finalize(logger: spirv_to_dxil_sys::dxil_spirv_logger) -> String { + let logger: Box = unsafe { + Box::from_raw(logger.priv_.cast()) + }; + + logger.msg + } +} \ No newline at end of file diff --git a/spirv-to-dxil/src/object.rs b/spirv-to-dxil/src/object.rs new file mode 100644 index 0000000..6734f11 --- /dev/null +++ b/spirv-to-dxil/src/object.rs @@ -0,0 +1,33 @@ +use std::ops::Deref; + +/// A compiled DXIL artifact. +pub struct DxilObject { + inner: spirv_to_dxil_sys::dxil_spirv_object, +} + +impl Drop for DxilObject { + fn drop(&mut self) { + unsafe { + // SAFETY: + // spirv_to_dxil_free frees only the interior buffer. + // https://gitlab.freedesktop.org/mesa/mesa/-/blob/7b0d00034201f8284a41370c0c3326736ae1134c/src/microsoft/spirv_to_dxil/spirv_to_dxil.c#L118 + spirv_to_dxil_sys::spirv_to_dxil_free(&mut self.inner) + } + } +} + +impl DxilObject { + pub(crate) fn new(raw: spirv_to_dxil_sys::dxil_spirv_object) -> Self { + Self { inner: raw } + } +} + +impl Deref for DxilObject { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + unsafe { + std::slice::from_raw_parts(self.inner.binary.buffer.cast(), self.inner.binary.size) + } + } +} diff --git a/spirv-to-dxil/src/specialization.rs b/spirv-to-dxil/src/specialization.rs new file mode 100644 index 0000000..e18a6b7 --- /dev/null +++ b/spirv-to-dxil/src/specialization.rs @@ -0,0 +1,49 @@ +#[derive(Debug, Copy, Clone)] +pub enum ConstValue { + Bool(bool), + Float32(f32), + Float64(f64), + Int8(i8), + Uint8(u8), + Int16(i16), + Uint16(u16), + Int32(i32), + Uint32(u32), + Int64(i64), + Uint64(u64), +} + +impl From for spirv_to_dxil_sys::dxil_spirv_const_value { + fn from(value: ConstValue) -> Self { + match value { + ConstValue::Bool(b) => spirv_to_dxil_sys::dxil_spirv_const_value { b }, + ConstValue::Float32(f32_) => spirv_to_dxil_sys::dxil_spirv_const_value { f32_ }, + ConstValue::Float64(f64_) => spirv_to_dxil_sys::dxil_spirv_const_value { f64_ }, + ConstValue::Int8(i8_) => spirv_to_dxil_sys::dxil_spirv_const_value { i8_ }, + ConstValue::Uint8(u8_) => spirv_to_dxil_sys::dxil_spirv_const_value { u8_ }, + ConstValue::Int16(i16_) => spirv_to_dxil_sys::dxil_spirv_const_value { i16_ }, + ConstValue::Uint16(u16_) => spirv_to_dxil_sys::dxil_spirv_const_value { u16_ }, + ConstValue::Int32(i32_) => spirv_to_dxil_sys::dxil_spirv_const_value { i32_ }, + ConstValue::Uint32(u32_) => spirv_to_dxil_sys::dxil_spirv_const_value { u32_ }, + ConstValue::Int64(i64_) => spirv_to_dxil_sys::dxil_spirv_const_value { i64_ }, + ConstValue::Uint64(u64_) => spirv_to_dxil_sys::dxil_spirv_const_value { u64_ }, + } + } +} + +#[derive(Debug, Copy, Clone)] +pub struct Specialization { + pub id: u32, + pub value: ConstValue, + pub defined_on_module: bool, +} + +impl From for spirv_to_dxil_sys::dxil_spirv_specialization { + fn from(value: Specialization) -> Self { + Self { + id: value.id, + defined_on_module: value.defined_on_module, + value: value.value.into(), + } + } +} diff --git a/spirv-to-dxil/test/fragment.spv b/spirv-to-dxil/test/fragment.spv new file mode 100644 index 0000000000000000000000000000000000000000..1fb90cfbc996a307620e3fdc899bfc42ac8b7552 GIT binary patch literal 24428 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 literal 0 HcmV?d00001 diff --git a/spirv-to-dxil/test/vertex.spv b/spirv-to-dxil/test/vertex.spv new file mode 100644 index 0000000000000000000000000000000000000000..67b8491da4e141c08bac1388159e0660b9734874 GIT binary patch literal 2100 zcmb7^+iuf95Qa^2XlZFnDLnwCBrPB$P{joxBm|-dlp+NsQlNsX;3igL)v+VnX$x<` zEAdp^An|{;H{Dw{n(WRuvpci1BbR1&XC3F7Gw&=qkDYw1I5Q%Sv*1*-xzX%2_mZ@~ z_u}PqJeHhN=7`U-b3I#^^*?g`5M@!aDS05NOG^69X#TAYWkqT(frWujmO<2 z{SkQGzz@A{-;cds>Ysb~%~^ha*b63o+o2*4J7z_>E-1zz=(fH1#EVn!QmSaqS!0V5 zKlP(fbT;$n4jK2uwB!F&WcK7Xa+d8d@W)4flDc8fo7$)Vwn-37Z57>TdjHjEm=m<^+bFzRL)YZ!HF z$if=GV-b3WLGAE2I{7keMxAb2jJWu{w;1Qb+7=@|?7M{crcWD+Pyg*m7)Gz>YNqoo z*tT?f(bn-rH#Hy*!_4H*+T=b%bL5b>YuqTRw~%KR^(ZeY^eU^ht3_> z^iOUz3FB#@lN*pyDK zIhVYML(donM~HJvGk3&J9CUgLhMo6e&0h2k&BSCs{^-0H4EsIJ