Skip to main content

librashader_reflect/reflect/cross/
hlsl.rs

1use crate::back::hlsl::{CrossHlslContext, HlslBufferAssignment, HlslBufferAssignments};
2use crate::back::targets::HLSL;
3use crate::back::{CompileShader, ShaderCompilerOutput};
4use crate::error::ShaderCompileError;
5use crate::reflect::cross::{CompiledProgram, CrossReflect};
6use spirv::Decoration;
7
8use spirv_cross2::compile::hlsl::HlslShaderModel;
9use spirv_cross2::compile::CompilableTarget;
10use spirv_cross2::reflect::{DecorationValue, ResourceType};
11use spirv_cross2::{targets, SpirvCrossError};
12
13pub(crate) type HlslReflect = CrossReflect<targets::Hlsl>;
14
15impl CompileShader<HLSL> for CrossReflect<targets::Hlsl> {
16    type Options = Option<HlslShaderModel>;
17    type Context = CrossHlslContext;
18
19    fn compile(
20        mut self,
21        options: Self::Options,
22    ) -> Result<ShaderCompilerOutput<String, CrossHlslContext>, ShaderCompileError> {
23        let sm = options.unwrap_or(HlslShaderModel::ShaderModel5_0);
24
25        let mut options = targets::Hlsl::options();
26        options.shader_model = sm;
27
28        // todo: options
29
30        let vertex_resources = self.vertex.shader_resources()?;
31        let fragment_resources = self.fragment.shader_resources()?;
32
33        let mut vertex_buffer_assignment = HlslBufferAssignments::default();
34        let mut fragment_buffer_assignment = HlslBufferAssignments::default();
35
36        let mut vertex_ubo = vertex_resources.resources_for_type(ResourceType::UniformBuffer)?;
37        if vertex_ubo.len() > 1 {
38            return Err(ShaderCompileError::SpirvCrossCompileError(
39                SpirvCrossError::InvalidArgument(String::from(
40                    "Cannot have more than one uniform buffer",
41                )),
42            ));
43        }
44
45        if let Some(buf) = vertex_ubo.next() {
46            vertex_buffer_assignment.ubo = Some(HlslBufferAssignment {
47                name: buf.name.to_string(),
48                id: buf.id.id(),
49            })
50        }
51
52        let mut vertex_pcb = vertex_resources.resources_for_type(ResourceType::PushConstant)?;
53        if vertex_pcb.len() > 1 {
54            return Err(ShaderCompileError::SpirvCrossCompileError(
55                SpirvCrossError::InvalidArgument(String::from(
56                    "Cannot have more than one push constant buffer",
57                )),
58            ));
59        }
60        if let Some(buf) = vertex_pcb.next() {
61            vertex_buffer_assignment.push = Some(HlslBufferAssignment {
62                name: buf.name.to_string(),
63                id: buf.id.id(),
64            })
65        }
66
67        let mut fragment_ubo =
68            fragment_resources.resources_for_type(ResourceType::UniformBuffer)?;
69        if fragment_ubo.len() > 1 {
70            return Err(ShaderCompileError::SpirvCrossCompileError(
71                SpirvCrossError::InvalidArgument(String::from(
72                    "Cannot have more than one uniform buffer",
73                )),
74            ));
75        }
76
77        if let Some(buf) = fragment_ubo.next() {
78            fragment_buffer_assignment.ubo = Some(HlslBufferAssignment {
79                name: buf.name.to_string(),
80                id: buf.id.id(),
81            })
82        }
83
84        let mut fragment_pcb = fragment_resources.resources_for_type(ResourceType::PushConstant)?;
85        if fragment_pcb.len() > 1 {
86            return Err(ShaderCompileError::SpirvCrossCompileError(
87                SpirvCrossError::InvalidArgument(String::from(
88                    "Cannot have more than one push constant buffer",
89                )),
90            ));
91        }
92
93        if let Some(buf) = fragment_pcb.next() {
94            fragment_buffer_assignment.push = Some(HlslBufferAssignment {
95                name: buf.name.to_string(),
96                id: buf.id.id(),
97            })
98        }
99
100        if sm == HlslShaderModel::ShaderModel3_0 {
101            for res in fragment_resources.resources_for_type(ResourceType::SampledImage)? {
102                let Some(DecorationValue::Literal(binding)) =
103                    self.fragment.decoration(res.id, Decoration::Binding)?
104                else {
105                    continue;
106                };
107                self.fragment
108                    .set_name(res.id, format!("LIBRA_SAMPLER2D_{binding}"))?;
109                // self.fragment
110                //     .unset_decoration(res.id, Decoration::Binding)?;
111            }
112        }
113
114        let vertex_compiled = self.vertex.compile(&options)?;
115        let fragment_compiled = self.fragment.compile(&options)?;
116
117        Ok(ShaderCompilerOutput {
118            vertex: vertex_compiled.to_string(),
119            fragment: fragment_compiled.to_string(),
120            context: CrossHlslContext {
121                artifact: CompiledProgram {
122                    vertex: vertex_compiled,
123                    fragment: fragment_compiled,
124                },
125
126                vertex_buffers: vertex_buffer_assignment,
127                fragment_buffers: fragment_buffer_assignment,
128            },
129        })
130    }
131
132    fn compile_boxed(
133        self: Box<Self>,
134        options: Self::Options,
135    ) -> Result<ShaderCompilerOutput<String, Self::Context>, ShaderCompileError> {
136        <CrossReflect<targets::Hlsl> as CompileShader<HLSL>>::compile(*self, options)
137    }
138}