vulkano-shaders - proc macros 2.0 (#1062)

* vulkano_shaders_derive exposes a proc_macro instead of a proc_macro_derive
* move vulkano_shader out of vulkano_shaders_derive and deprecate vulkano_shaders_derive
* Update documentation
* Move vulkano_shader! to root of mod, so it works with rust 1.30
This commit is contained in:
Lucas Kent 2018-10-26 11:15:33 +11:00 committed by GitHub
parent 7a11120350
commit 494a0c30c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 919 additions and 936 deletions

View File

@ -1,6 +1,12 @@
# Unreleased (Breaking)
- `vulkano_shaders::reflect` now returns `Result<proc_macro2::TokenStream, Error>` instead of `Result<String, Error>`
- Export features and device extensions from the device module instead of the instance module
+ `instance::Features` -> `device::Features`
+ `instance::DeviceExtensions` -> `device::DeviceExtensions`
+ `instance::RawDeviceExtensions` -> `device::RawDeviceExtensions`
- Added `vulkano_shaders::vulkano_shader` proc macro, use this instead of `vulkano_shader_deriver::VulkanoShaders`.
- The entire `vulkano_shader_derive` crate is deprecated.
- `vulkano_shaders::{reflect, compile, Error}` are no longer public.
- Removed mir support, as it is being removed from the vulkan spec.
- Remove vulkano_shaders::build_glsl_shaders
- Split `PersistentDescriptorSetError::MissingUsage` into `MissingImageUsage` and `MissingBufferUsage`
@ -14,10 +20,6 @@
# Version 0.10.0 (2018-08-10)
- Export features and device extensions from the device module instead of the instance module
+ `instance::Features` -> `device::Features`
+ `instance::DeviceExtensions` -> `device::DeviceExtensions`
+ `instance::RawDeviceExtensions` -> `device::RawDeviceExtensions`
- Use dynamically loaded `libvulkan` like on other platforms instead of linking to MoltenVK on macOS
- Updated winit to version 0.17.
- Allow custom implementations of `RenderPassDesc` to specify `VK_SUBPASS_EXTERNAL` as a dependency source or destination

View File

@ -6,7 +6,7 @@ publish = false
[dependencies]
vulkano = { path = "../vulkano" }
vulkano-shader-derive = { path = "../vulkano-shader-derive" }
vulkano-shaders = { path = "../vulkano-shaders" }
vulkano-win = { path = "../vulkano-win" }
cgmath = "0.16.1"
image = "0.20.0"

View File

@ -15,8 +15,7 @@
// Note that since we don't create any window, fewer imports are needed.
extern crate vulkano;
#[macro_use]
extern crate vulkano_shader_derive;
extern crate vulkano_shaders;
use vulkano::buffer::BufferUsage;
use vulkano::buffer::CpuAccessibleBuffer;
@ -29,9 +28,28 @@ use vulkano::instance::InstanceExtensions;
use vulkano::pipeline::ComputePipeline;
use vulkano::sync::now;
use vulkano::sync::GpuFuture;
use vulkano_shaders::vulkano_shader;
use std::sync::Arc;
vulkano_shader!{
mod_name: cs,
ty: "compute",
src: "
#version 450
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
layout(set = 0, binding = 0) buffer Data {
uint data[];
} data;
void main() {
uint idx = gl_GlobalInvocationID.x;
data.data[idx] *= 12;
}"
}
fn main() {
// As with other examples, the first step is to create an instance.
let instance = Instance::new(None, &InstanceExtensions::none(), None)
@ -79,28 +97,6 @@ fn main() {
// If you are familiar with graphics pipeline, the principle is the same except that compute
// pipelines are much simpler to create.
let pipeline = Arc::new({
// TODO: explain
#[allow(dead_code)]
mod cs {
#[derive(VulkanoShader)]
#[ty = "compute"]
#[src = "
#version 450
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
layout(set = 0, binding = 0) buffer Data {
uint data[];
} data;
void main() {
uint idx = gl_GlobalInvocationID.x;
data.data[idx] *= 12;
}"
]
struct Dummy;
}
let shader = cs::Shader::load(device.clone())
.expect("failed to create shader module");
ComputePipeline::new(device.clone(), &shader.main_entry_point(), &())

View File

@ -23,6 +23,7 @@ use vulkano::pipeline::blend::BlendOp;
use vulkano::pipeline::GraphicsPipeline;
use vulkano::pipeline::GraphicsPipelineAbstract;
use vulkano::pipeline::viewport::Viewport;
use vulkano_shaders::vulkano_shader;
use std::sync::Arc;
@ -142,27 +143,23 @@ struct Vertex {
}
impl_vertex!(Vertex, position);
mod vs {
#[derive(VulkanoShader)]
#[allow(dead_code)]
#[ty = "vertex"]
#[src = "
vulkano_shader!{
mod_name: vs,
ty: "vertex",
src: "
#version 450
layout(location = 0) in vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
"]
struct Dummy;
}"
}
mod fs {
#[derive(VulkanoShader)]
#[allow(dead_code)]
#[ty = "fragment"]
#[src = "
vulkano_shader!{
mod_name: fs,
ty: "fragment",
src: "
#version 450
// The `color_input` parameter of the `draw` method.
@ -180,7 +177,5 @@ void main() {
vec3 in_diffuse = subpassLoad(u_diffuse).rgb;
f_color.rgb = push_constants.color.rgb * in_diffuse;
f_color.a = 1.0;
}
"]
struct Dummy;
}"
}

View File

@ -23,6 +23,7 @@ use vulkano::pipeline::blend::BlendOp;
use vulkano::pipeline::GraphicsPipeline;
use vulkano::pipeline::GraphicsPipelineAbstract;
use vulkano::pipeline::viewport::Viewport;
use vulkano_shaders::vulkano_shader;
use cgmath::Vector3;
use std::sync::Arc;
@ -154,27 +155,23 @@ struct Vertex {
}
impl_vertex!(Vertex, position);
mod vs {
#[derive(VulkanoShader)]
#[allow(dead_code)]
#[ty = "vertex"]
#[src = "
vulkano_shader!{
mod_name: vs,
ty: "vertex",
src: "
#version 450
layout(location = 0) in vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
"]
struct Dummy;
}"
}
mod fs {
#[derive(VulkanoShader)]
#[allow(dead_code)]
#[ty = "fragment"]
#[src = "
vulkano_shader!{
mod_name: fs,
ty: "fragment",
src: "
#version 450
// The `color_input` parameter of the `draw` method.
@ -203,7 +200,5 @@ void main() {
vec3 in_diffuse = subpassLoad(u_diffuse).rgb;
f_color.rgb = light_percent * push_constants.color.rgb * in_diffuse;
f_color.a = 1.0;
}
"]
struct Dummy;
}"
}

View File

@ -23,6 +23,7 @@ use vulkano::pipeline::blend::BlendOp;
use vulkano::pipeline::GraphicsPipeline;
use vulkano::pipeline::GraphicsPipelineAbstract;
use vulkano::pipeline::viewport::Viewport;
use vulkano_shaders::vulkano_shader;
use cgmath::Matrix4;
use cgmath::Vector3;
@ -168,11 +169,10 @@ struct Vertex {
}
impl_vertex!(Vertex, position);
mod vs {
#[derive(VulkanoShader)]
#[allow(dead_code)]
#[ty = "vertex"]
#[src = "
vulkano_shader!{
mod_name: vs,
ty: "vertex",
src: "
#version 450
layout(location = 0) in vec2 position;
@ -181,16 +181,13 @@ layout(location = 0) out vec2 v_screen_coords;
void main() {
v_screen_coords = position;
gl_Position = vec4(position, 0.0, 1.0);
}
"]
struct Dummy;
}"
}
mod fs {
#[derive(VulkanoShader)]
#[allow(dead_code)]
#[ty = "fragment"]
#[src = "
vulkano_shader!{
mod_name: fs,
ty: "fragment",
src: "
#version 450
// The `color_input` parameter of the `draw` method.
@ -236,7 +233,5 @@ void main() {
vec3 in_diffuse = subpassLoad(u_diffuse).rgb;
f_color.rgb = push_constants.color.rgb * light_percent * in_diffuse;
f_color.a = 1.0;
}
"]
struct Dummy;
}"
}

View File

@ -29,8 +29,7 @@
extern crate cgmath;
#[macro_use]
extern crate vulkano;
#[macro_use]
extern crate vulkano_shader_derive;
extern crate vulkano_shaders;
extern crate winit;
extern crate vulkano_win;

View File

@ -18,6 +18,7 @@ use vulkano::framebuffer::Subpass;
use vulkano::pipeline::GraphicsPipeline;
use vulkano::pipeline::GraphicsPipelineAbstract;
use vulkano::pipeline::viewport::Viewport;
use vulkano_shaders::vulkano_shader;
use std::sync::Arc;
@ -94,27 +95,23 @@ struct Vertex {
}
impl_vertex!(Vertex, position);
mod vs {
#[derive(VulkanoShader)]
#[allow(dead_code)]
#[ty = "vertex"]
#[src = "
vulkano_shader!{
mod_name: vs,
ty: "vertex",
src: "
#version 450
layout(location = 0) in vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
"]
struct Dummy;
}"
}
mod fs {
#[derive(VulkanoShader)]
#[allow(dead_code)]
#[ty = "fragment"]
#[src = "
vulkano_shader!{
mod_name: fs,
ty: "fragment",
src: "
#version 450
layout(location = 0) out vec4 f_color;
@ -123,7 +120,5 @@ layout(location = 1) out vec3 f_normal;
void main() {
f_color = vec4(1.0, 1.0, 1.0, 1.0);
f_normal = vec3(0.0, 0.0, 1.0);
}
"]
struct Dummy;
}"
}

View File

@ -13,12 +13,12 @@ extern crate winit;
#[macro_use]
extern crate vulkano;
#[macro_use]
extern crate vulkano_shader_derive;
extern crate vulkano_shaders;
extern crate vulkano_win;
use vulkano_win::VkSurfaceBuild;
use vulkano::sync::GpuFuture;
use vulkano_shaders::vulkano_shader;
use std::sync::Arc;
@ -242,10 +242,10 @@ fn main() {
}
}
mod vs {
#[derive(VulkanoShader)]
#[ty = "vertex"]
#[src = "
vulkano_shader!{
mod_name: vs,
ty: "vertex",
src: "
#version 450
layout(location = 0) in vec2 position;
@ -254,16 +254,13 @@ layout(location = 0) out vec2 tex_coords;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
tex_coords = position + vec2(0.5);
}
"]
#[allow(dead_code)]
struct Dummy;
}"
}
mod fs {
#[derive(VulkanoShader)]
#[ty = "fragment"]
#[src = "
vulkano_shader!{
mod_name: fs,
ty: "fragment",
src: "
#version 450
layout(location = 0) in vec2 tex_coords;
@ -273,8 +270,5 @@ layout(set = 0, binding = 0) uniform sampler2D tex;
void main() {
f_color = texture(tex, tex_coords);
}
"]
#[allow(dead_code)]
struct Dummy;
}"
}

View File

@ -67,8 +67,7 @@
extern crate image;
#[macro_use]
extern crate vulkano;
#[macro_use]
extern crate vulkano_shader_derive;
extern crate vulkano_shaders;
use std::sync::Arc;
use image::ImageBuffer;
@ -92,6 +91,33 @@ use vulkano::instance::PhysicalDevice;
use vulkano::pipeline::GraphicsPipeline;
use vulkano::pipeline::viewport::Viewport;
use vulkano::sync::GpuFuture;
use vulkano_shaders::vulkano_shader;
vulkano_shader!{
mod_name: vs,
ty: "vertex",
src: "
#version 450
layout(location = 0) in vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}"
}
vulkano_shader!{
mod_name: fs,
ty: "fragment",
src: "
#version 450
layout(location = 0) out vec4 f_color;
void main() {
f_color = vec4(1.0, 0.0, 0.0, 1.0);
}"
}
fn main() {
// The usual Vulkan initialization.
@ -165,38 +191,6 @@ fn main() {
// At the end of the example, we copy the content of `image` (ie. the final image) to a buffer,
// then read the content of that buffer and save it to a PNG file.
mod vs {
#[derive(VulkanoShader)]
#[ty = "vertex"]
#[src = "
#version 450
layout(location = 0) in vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
"]
#[allow(dead_code)]
struct Dummy;
}
mod fs {
#[derive(VulkanoShader)]
#[ty = "fragment"]
#[src = "
#version 450
layout(location = 0) out vec4 f_color;
void main() {
f_color = vec4(1.0, 0.0, 0.0, 1.0);
}
"]
#[allow(dead_code)]
struct Dummy;
}
let vs = vs::Shader::load(device.clone()).expect("failed to create shader module");
let fs = fs::Shader::load(device.clone()).expect("failed to create shader module");

View File

@ -9,8 +9,7 @@
// TODO: Give a paragraph about what push constants are and what problems they solve
extern crate vulkano;
#[macro_use]
extern crate vulkano_shader_derive;
extern crate vulkano_shaders;
use vulkano::buffer::BufferUsage;
use vulkano::buffer::CpuAccessibleBuffer;
@ -24,22 +23,14 @@ use vulkano::pipeline::ComputePipeline;
use vulkano::sync::now;
use vulkano::sync::GpuFuture;
use vulkano_shaders::vulkano_shader;
use std::sync::Arc;
fn main() {
let instance = Instance::new(None, &InstanceExtensions::none(), None).unwrap();
let physical = vulkano::instance::PhysicalDevice::enumerate(&instance).next().unwrap();
let queue_family = physical.queue_families().find(|&q| q.supports_compute()).unwrap();
let (device, mut queues) = {
Device::new(physical, physical.supported_features(), &DeviceExtensions::none(),
[(queue_family, 0.5)].iter().cloned()).expect("failed to create device")
};
let queue = queues.next().unwrap();
mod cs {
#[derive(VulkanoShader)]
#[ty = "compute"]
#[src = "
vulkano_shader!{
mod_name: cs,
ty: "compute",
src: "
#version 450
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
@ -61,11 +52,17 @@ void main() {
data.data[idx] += uint(pc.addend);
}
}"
]
#[allow(dead_code)]
struct Dummy;
}
}
fn main() {
let instance = Instance::new(None, &InstanceExtensions::none(), None).unwrap();
let physical = vulkano::instance::PhysicalDevice::enumerate(&instance).next().unwrap();
let queue_family = physical.queue_families().find(|&q| q.supports_compute()).unwrap();
let (device, mut queues) = {
Device::new(physical, physical.supported_features(), &DeviceExtensions::none(),
[(queue_family, 0.5)].iter().cloned()).expect("failed to create device")
};
let queue = queues.next().unwrap();
let shader = cs::Shader::load(device.clone())
.expect("failed to create shader module");
let pipeline = Arc::new(ComputePipeline::new(device.clone(), &shader.main_entry_point(), &()).unwrap());

View File

@ -9,8 +9,7 @@
// TODO: Give a paragraph about what specialization are and what problems they solve
extern crate vulkano;
#[macro_use]
extern crate vulkano_shader_derive;
extern crate vulkano_shaders;
use vulkano::buffer::BufferUsage;
use vulkano::buffer::CpuAccessibleBuffer;
@ -23,23 +22,14 @@ use vulkano::instance::InstanceExtensions;
use vulkano::pipeline::ComputePipeline;
use vulkano::sync::now;
use vulkano::sync::GpuFuture;
use vulkano_shaders::vulkano_shader;
use std::sync::Arc;
fn main() {
let instance = Instance::new(None, &InstanceExtensions::none(), None).unwrap();
let physical = vulkano::instance::PhysicalDevice::enumerate(&instance).next().unwrap();
let queue_family = physical.queue_families().find(|&q| q.supports_compute()).unwrap();
let (device, mut queues) = {
Device::new(physical, physical.supported_features(), &DeviceExtensions::none(),
[(queue_family, 0.5)].iter().cloned()).expect("failed to create device")
};
let queue = queues.next().unwrap();
mod cs {
#[derive(VulkanoShader)]
#[ty = "compute"]
#[src = "
vulkano_shader!{
mod_name: cs,
ty: "compute",
src: "
#version 450
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
@ -59,10 +49,17 @@ void main() {
data.data[idx] += uint(addend);
}
}"
]
#[allow(dead_code)]
struct Dummy;
}
}
fn main() {
let instance = Instance::new(None, &InstanceExtensions::none(), None).unwrap();
let physical = vulkano::instance::PhysicalDevice::enumerate(&instance).next().unwrap();
let queue_family = physical.queue_families().find(|&q| q.supports_compute()).unwrap();
let (device, mut queues) = {
Device::new(physical, physical.supported_features(), &DeviceExtensions::none(),
[(queue_family, 0.5)].iter().cloned()).expect("failed to create device")
};
let queue = queues.next().unwrap();
let shader = cs::Shader::load(device.clone())
.expect("failed to create shader module");

View File

@ -0,0 +1,14 @@
#version 450
layout(location = 0) in vec3 v_normal;
layout(location = 0) out vec4 f_color;
const vec3 LIGHT = vec3(0.0, 0.0, 1.0);
void main() {
float brightness = dot(normalize(v_normal), normalize(LIGHT));
vec3 dark_color = vec3(0.6, 0.0, 0.0);
vec3 regular_color = vec3(1.0, 0.0, 0.0);
f_color = vec4(mix(dark_color, regular_color, brightness), 1.0);
}

View File

@ -14,12 +14,12 @@ extern crate time;
#[macro_use]
extern crate vulkano;
#[macro_use]
extern crate vulkano_shader_derive;
extern crate vulkano_shaders;
extern crate vulkano_win;
use vulkano_win::VkSurfaceBuild;
use vulkano::sync::GpuFuture;
use vulkano_shaders::vulkano_shader;
use std::sync::Arc;
@ -263,52 +263,14 @@ fn main() {
}
}
mod vs {
#[derive(VulkanoShader)]
#[ty = "vertex"]
#[src = "
#version 450
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 0) out vec3 v_normal;
layout(set = 0, binding = 0) uniform Data {
mat4 world;
mat4 view;
mat4 proj;
} uniforms;
void main() {
mat4 worldview = uniforms.view * uniforms.world;
v_normal = transpose(inverse(mat3(worldview))) * normal;
gl_Position = uniforms.proj * worldview * vec4(position, 1.0);
}
"]
#[allow(dead_code)]
struct Dummy;
vulkano_shader!{
mod_name: vs,
ty: "vertex",
path: "src/bin/teapot/vert.glsl"
}
mod fs {
#[derive(VulkanoShader)]
#[ty = "fragment"]
#[src = "
#version 450
layout(location = 0) in vec3 v_normal;
layout(location = 0) out vec4 f_color;
const vec3 LIGHT = vec3(0.0, 0.0, 1.0);
void main() {
float brightness = dot(normalize(v_normal), normalize(LIGHT));
vec3 dark_color = vec3(0.6, 0.0, 0.0);
vec3 regular_color = vec3(1.0, 0.0, 0.0);
f_color = vec4(mix(dark_color, regular_color, brightness), 1.0);
}
"]
#[allow(dead_code)]
struct Dummy;
vulkano_shader!{
mod_name: fs,
ty: "fragment",
path: "src/bin/teapot/frag.glsl"
}

View File

@ -0,0 +1,18 @@
#version 450
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 0) out vec3 v_normal;
layout(set = 0, binding = 0) uniform Data {
mat4 world;
mat4 view;
mat4 proj;
} uniforms;
void main() {
mat4 worldview = uniforms.view * uniforms.world;
v_normal = transpose(inverse(mat3(worldview))) * normal;
gl_Position = uniforms.proj * worldview * vec4(position, 1.0);
}

View File

@ -20,8 +20,7 @@
#[macro_use]
extern crate vulkano;
#[macro_use]
extern crate vulkano_shader_derive;
extern crate vulkano_shaders;
extern crate winit;
extern crate vulkano_win;
@ -45,9 +44,98 @@ use vulkano::swapchain::AcquireError;
use vulkano::swapchain::SwapchainCreationError;
use vulkano::sync::now;
use vulkano::sync::GpuFuture;
use vulkano_shaders::vulkano_shader;
use std::sync::Arc;
vulkano_shader!{
mod_name: vs,
ty: "vertex",
src: "
#version 450
layout(location = 0) in vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}"
}
vulkano_shader!{
mod_name: tcs,
ty: "tess_ctrl",
src: "
#version 450
layout (vertices = 3) out; // a value of 3 means a patch consists of a single triangle
void main(void)
{
// save the position of the patch, so the tes can access it
// We could define our own output variables for this,
// but gl_out is handily provided.
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
gl_TessLevelInner[0] = 10; // many triangles are generated in the center
gl_TessLevelOuter[0] = 1; // no triangles are generated for this edge
gl_TessLevelOuter[1] = 10; // many triangles are generated for this edge
gl_TessLevelOuter[2] = 10; // many triangles are generated for this edge
// gl_TessLevelInner[1] = only used when tes uses layout(quads)
// gl_TessLevelOuter[3] = only used when tes uses layout(quads)
}"
}
// PG
// There is a stage in between tcs and tes called Primitive Generation (PG)
// Shaders cannot be defined for it.
// It takes gl_TessLevelInner and gl_TessLevelOuter and uses them to generate positions within
// the patch and pass them to tes via gl_TessCoord.
//
// When tes uses layout(triangles) then gl_TessCoord is in barrycentric coordinates.
// if layout(quads) is used then gl_TessCoord is in cartesian coordinates.
// Barrycentric coordinates are of the form (x, y, z) where x + y + z = 1
// and the values x, y and z represent the distance from a vertex of the triangle.
// http://mathworld.wolfram.com/BarycentricCoordinates.html
vulkano_shader!{
mod_name: tes,
ty: "tess_eval",
src: "
#version 450
layout(triangles, equal_spacing, cw) in;
void main(void)
{
// retrieve the vertex positions set by the tcs
vec4 vert_x = gl_in[0].gl_Position;
vec4 vert_y = gl_in[1].gl_Position;
vec4 vert_z = gl_in[2].gl_Position;
// convert gl_TessCoord from barycentric coordinates to cartesian coordinates
gl_Position = vec4(
gl_TessCoord.x * vert_x.x + gl_TessCoord.y * vert_y.x + gl_TessCoord.z * vert_z.x,
gl_TessCoord.x * vert_x.y + gl_TessCoord.y * vert_y.y + gl_TessCoord.z * vert_z.y,
gl_TessCoord.x * vert_x.z + gl_TessCoord.y * vert_y.z + gl_TessCoord.z * vert_z.z,
1.0
);
}"
}
vulkano_shader!{
mod_name: fs,
ty: "fragment",
src: "
#version 450
layout(location = 0) out vec4 f_color;
void main() {
f_color = vec4(1.0, 1.0, 1.0, 1.0);
}"
}
fn main() {
let instance = {
let extensions = vulkano_win::required_extensions();
@ -109,105 +197,6 @@ fn main() {
].iter().cloned()).expect("failed to create buffer")
};
mod vs {
#[derive(VulkanoShader)]
#[ty = "vertex"]
#[src = "
#version 450
layout(location = 0) in vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
"]
#[allow(dead_code)]
struct Dummy;
}
mod tcs {
#[derive(VulkanoShader)]
#[ty = "tess_ctrl"]
#[src = "
#version 450
layout (vertices = 3) out; // a value of 3 means a patch consists of a single triangle
void main(void)
{
// save the position of the patch, so the tes can access it
// We could define our own output variables for this,
// but gl_out is handily provided.
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
gl_TessLevelInner[0] = 10; // many triangles are generated in the center
gl_TessLevelOuter[0] = 1; // no triangles are generated for this edge
gl_TessLevelOuter[1] = 10; // many triangles are generated for this edge
gl_TessLevelOuter[2] = 10; // many triangles are generated for this edge
// gl_TessLevelInner[1] = only used when tes uses layout(quads)
// gl_TessLevelOuter[3] = only used when tes uses layout(quads)
}
"]
#[allow(dead_code)]
struct Dummy;
}
// PG
// There is a stage in between tcs and tes called Primitive Generation (PG)
// Shaders cannot be defined for it.
// It takes gl_TessLevelInner and gl_TessLevelOuter and uses them to generate positions within
// the patch and pass them to tes via gl_TessCoord.
//
// When tes uses layout(triangles) then gl_TessCoord is in barrycentric coordinates.
// if layout(quads) is used then gl_TessCoord is in cartesian coordinates.
// Barrycentric coordinates are of the form (x, y, z) where x + y + z = 1
// and the values x, y and z represent the distance from a vertex of the triangle.
// http://mathworld.wolfram.com/BarycentricCoordinates.html
mod tes {
#[derive(VulkanoShader)]
#[ty = "tess_eval"]
#[src = "
#version 450
layout(triangles, equal_spacing, cw) in;
void main(void)
{
// retrieve the vertex positions set by the tcs
vec4 vert_x = gl_in[0].gl_Position;
vec4 vert_y = gl_in[1].gl_Position;
vec4 vert_z = gl_in[2].gl_Position;
// convert gl_TessCoord from barycentric coordinates to cartesian coordinates
gl_Position = vec4(
gl_TessCoord.x * vert_x.x + gl_TessCoord.y * vert_y.x + gl_TessCoord.z * vert_z.x,
gl_TessCoord.x * vert_x.y + gl_TessCoord.y * vert_y.y + gl_TessCoord.z * vert_z.y,
gl_TessCoord.x * vert_x.z + gl_TessCoord.y * vert_y.z + gl_TessCoord.z * vert_z.z,
1.0
);
}
"]
#[allow(dead_code)]
struct Dummy;
}
mod fs {
#[derive(VulkanoShader)]
#[ty = "fragment"]
#[src = "
#version 450
layout(location = 0) out vec4 f_color;
void main() {
f_color = vec4(1.0, 1.0, 1.0, 1.0);
}
"]
#[allow(dead_code)]
struct Dummy;
}
let vs = vs::Shader::load(device.clone()).expect("failed to create shader module");
let tcs = tcs::Shader::load(device.clone()).expect("failed to create shader module");
let tes = tes::Shader::load(device.clone()).expect("failed to create shader module");

View File

@ -20,11 +20,9 @@
// The `vulkano` crate is the main crate that you must use to use Vulkan.
#[macro_use]
extern crate vulkano;
// The `vulkano_shader_derive` crate allows us to use the `VulkanoShader` custom derive that we use
// in this example.
#[macro_use]
extern crate vulkano_shader_derive;
// However the Vulkan library doesn't provide any functionality to create and handle windows, as
// Provides the `vulkano_shader` macro that is used to generate code for using shaders.
extern crate vulkano_shaders;
// The Vulkan library doesn't provide any functionality to create and handle windows, as
// this would be out of scope. In order to open a window, we are going to use the `winit` crate.
extern crate winit;
// The `vulkano_win` crate is the link between `vulkano` and `winit`. Vulkano doesn't know about
@ -53,8 +51,44 @@ use vulkano::swapchain::SwapchainCreationError;
use vulkano::sync::now;
use vulkano::sync::GpuFuture;
use vulkano_shaders::vulkano_shader;
use std::sync::Arc;
// TODO: Move this back to the middle of the example, it makes for a more coherent sequential explanation (check git history)
// The raw shader creation API provided by the vulkano library is unsafe, for various reasons.
//
// An overview of what the `vulkano_shader` macro generates can be found in the
// `vulkano-shaders` crate docs. You can view them at https://docs.rs/vulkano-shaders/
//
// TODO: explain this in details
vulkano_shader!{
mod_name: vs,
ty: "vertex",
src: "
#version 450
layout(location = 0) in vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}"
}
vulkano_shader!{
mod_name: fs,
ty: "fragment",
src: "
#version 450
layout(location = 0) out vec4 f_color;
void main() {
f_color = vec4(1.0, 0.0, 0.0, 1.0);
}
"
}
fn main() {
// The first step of any Vulkan program is to create an instance.
let instance = {
@ -197,47 +231,6 @@ fn main() {
].iter().cloned()).expect("failed to create buffer")
};
// The next step is to create the shaders.
//
// The raw shader creation API provided by the vulkano library is unsafe, for various reasons.
//
// An overview of what the `VulkanoShader` derive macro generates can be found in the
// `vulkano-shader-derive` crate docs. You can view them at
// https://docs.rs/vulkano-shader-derive/*/vulkano_shader_derive/
//
// TODO: explain this in details
mod vs {
#[derive(VulkanoShader)]
#[ty = "vertex"]
#[src = "
#version 450
layout(location = 0) in vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
"]
#[allow(dead_code)]
struct Dummy;
}
mod fs {
#[derive(VulkanoShader)]
#[ty = "fragment"]
#[src = "
#version 450
layout(location = 0) out vec4 f_color;
void main() {
f_color = vec4(1.0, 0.0, 0.0, 1.0);
}
"]
#[allow(dead_code)]
struct Dummy;
}
let vs = vs::Shader::load(device.clone()).expect("failed to create shader module");
let fs = fs::Shader::load(device.clone()).expect("failed to create shader module");

View File

@ -1,22 +1,9 @@
[package]
name = "vulkano-shader-derive"
version = "0.10.0"
version = "0.11.0"
authors = ["Pierre Krieger <pierre.krieger1708@gmail.com>", "The vulkano contributors"]
repository = "https://github.com/vulkano-rs/vulkano"
description = "Safe wrapper for the Vulkan graphics API"
description = "Deprecated"
license = "MIT/Apache-2.0"
documentation = "https://docs.rs/vulkano"
categories = ["rendering::graphics-api"]
[lib]
name = "vulkano_shader_derive"
proc-macro = true
[dependencies]
syn = "0.15"
quote = "0.6"
proc-macro2 = "0.4"
vulkano-shaders = { version = "0.10", path = "../vulkano-shaders" }
[dev-dependencies]
vulkano = { version = "0.10", path = "../vulkano" }
categories = []

View File

@ -1,248 +0,0 @@
//! Procedural Macro glue for compile-time compilation of GLSL into SPIR-V
//!
//! # Basic usage
//!
//! ```
//! #[macro_use]
//! extern crate vulkano_shader_derive;
//! extern crate vulkano;
//! # fn main() {}
//! #[allow(unused)]
//! mod vertex_shader {
//! #[derive(VulkanoShader)]
//! #[ty = "vertex"]
//! #[src = "
//! #version 450
//!
//! layout(location = 0) in vec3 position;
//!
//! void main() {
//! gl_Position = vec4(position, 1.0);
//! }
//! "]
//! struct Dummy;
//! }
//! ```
//!
//! # Details
//!
//! Due to the current limitations of procedural shaders in Rust, the current
//! functionality of this crate is to base everything off of deriving
//! `VulkanoShader` for a dummy struct that never actually gets used. When
//! derived, the unused struct itself will be replaced by the functionality
//! needed to use the shader in a Vulkano application. Due to the fact that
//! a lot of what is generated will never be used, it's a good idea to put
//! `#[allow(unused)]` on the module itself if you don't want to see irrelevant
//! errors.
//!
//! If you want to take a look at what the macro generates, your best options
//! are to either read through the code that handles the generation (the
//! [`reflect`][reflect] function in the `vulkano-shaders` crate) or use a tool
//! such as [cargo-expand][cargo-expand] to view the expansion of the macro in your
//! own code. It is unfortunately not possible to provide a `generated_example`
//! module like some normal macro crates do since derive macros cannot be used from
//! the crate they are declared in. On the other hand, if you are looking for a
//! high-level overview, you can see the below section.
//!
//! # Generated code overview
//!
//! The macro generates the following items of interest:
//! * The `Shader` struct. This contains a single field, `shader`, which is an
//! `Arc<ShaderModule>`.
//! * The `Shader::load` constructor. This method takes an `Arc<Device>`, calls
//! [`ShaderModule::new`][ShaderModule::new] with the passed-in device and the
//! shader data provided via the macro, and returns `Result<Shader, OomError>`.
//! Before doing so, it loops through every capability instruction in the shader
//! data, verifying that the passed-in `Device` has the appropriate features
//! enabled. **This function currently panics if a feature required by the shader
//! is not enabled on the device.** At some point in the future it will return
//! an error instead.
//! * The `Shader::module` method. This method simply returns a reference to the
//! `Arc<ShaderModule>` contained within the `shader` field of the `Shader`
//! struct.
//! * Methods for each entry point of the shader module. These construct and
//! return the various entry point structs that can be found in the
//! [vulkano::pipeline::shader][pipeline::shader] module.
//! * A Rust struct translated from each struct contained in the shader data.
//! * The `Layout` newtype. This contains a [`ShaderStages`][ShaderStages] struct.
//! An implementation of [`PipelineLayoutDesc`][PipelineLayoutDesc] is also
//! generated for the newtype.
//! * The `SpecializationConstants` struct. This contains a field for every
//! specialization constant found in the shader data. Implementations of
//! `Default` and [`SpecializationConstants`][SpecializationConstants] are also
//! generated for the struct.
//!
//! All of these generated items will be accessed through the module that you
//! wrote to use the derive macro in. If you wanted to store the `Shader` in
//! a struct of your own, you could do something like this:
//!
//! ```
//! # #[macro_use]
//! # extern crate vulkano_shader_derive;
//! # extern crate vulkano;
//! # fn main() {}
//! # use std::sync::Arc;
//! # use vulkano::OomError;
//! # use vulkano::device::Device;
//! #
//! # #[allow(unused)]
//! # mod vertex_shader {
//! # #[derive(VulkanoShader)]
//! # #[ty = "vertex"]
//! # #[src = "
//! # #version 450
//! #
//! # layout(location = 0) in vec3 position;
//! #
//! # void main() {
//! # gl_Position = vec4(position, 1.0);
//! # }
//! # "]
//! # struct Dummy;
//! # }
//! // various use statements
//! // `vertex_shader` module with shader derive
//!
//! pub struct Shaders {
//! pub vertex_shader: vertex_shader::Shader
//! }
//!
//! impl Shaders {
//! pub fn load(device: Arc<Device>) -> Result<Self, OomError> {
//! Ok(Self {
//! vertex_shader: vertex_shader::Shader::load(device)?,
//! })
//! }
//! }
//! ```
//!
//! # Options
//!
//! The options available are in the form of the following attributes:
//!
//! ## `#[ty = "..."]`
//!
//! This defines what shader type the given GLSL source will be compiled into.
//! The type can be any of the following:
//!
//! * `vertex`
//! * `fragment`
//! * `geometry`
//! * `tess_ctrl`
//! * `tess_eval`
//! * `compute`
//!
//! For details on what these shader types mean, [see Vulkano's documentation][pipeline].
//!
//! ## `#[src = "..."]`
//!
//! Provides the raw GLSL source to be compiled in the form of a string. Cannot
//! be used in conjunction with the `#[path]` attribute.
//!
//! ## `#[path = "..."]`
//!
//! Provides the path to the GLSL source to be compiled, relative to `Cargo.toml`.
//! Cannot be used in conjunction with the `#[src]` attribute.
//!
//! [reflect]: https://github.com/vulkano-rs/vulkano/blob/master/vulkano-shaders/src/lib.rs#L67
//! [cargo-expand]: https://github.com/dtolnay/cargo-expand
//! [ShaderModule::new]: https://docs.rs/vulkano/*/vulkano/pipeline/shader/struct.ShaderModule.html#method.new
//! [OomError]: https://docs.rs/vulkano/*/vulkano/enum.OomError.html
//! [pipeline::shader]: https://docs.rs/vulkano/*/vulkano/pipeline/shader/index.html
//! [descriptor]: https://docs.rs/vulkano/*/vulkano/descriptor/index.html
//! [ShaderStages]: https://docs.rs/vulkano/*/vulkano/descriptor/descriptor/struct.ShaderStages.html
//! [PipelineLayoutDesc]: https://docs.rs/vulkano/*/vulkano/descriptor/pipeline_layout/trait.PipelineLayoutDesc.html
//! [SpecializationConstants]: https://docs.rs/vulkano/*/vulkano/pipeline/shader/trait.SpecializationConstants.html
//! [pipeline]: https://docs.rs/vulkano/*/vulkano/pipeline/index.html
extern crate proc_macro;
extern crate proc_macro2;
extern crate quote;
extern crate syn;
extern crate vulkano_shaders;
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::Path;
enum SourceKind {
Src(String),
Path(String),
}
#[proc_macro_derive(VulkanoShader, attributes(src, path, ty))]
pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let syn_item: syn::DeriveInput = syn::parse(input).unwrap();
let source_code = {
let mut iter = syn_item.attrs.iter().filter_map(|attr| {
attr.interpret_meta().and_then(|meta| {
match meta {
syn::Meta::NameValue(syn::MetaNameValue { ident, lit: syn::Lit::Str(lit_str), .. }) => {
match ident.to_string().as_ref() {
"src" => Some(SourceKind::Src(lit_str.value())),
"path" => Some(SourceKind::Path(lit_str.value())),
_ => None,
}
},
_ => None
}
})
});
let source = iter.next().expect("No source attribute given ; put #[src = \"...\"] or #[path = \"...\"]");
if iter.next().is_some() {
panic!("Multiple src or path attributes given ; please provide only one");
}
match source {
SourceKind::Src(source) => source,
SourceKind::Path(path) => {
let root = env::var("CARGO_MANIFEST_DIR").unwrap_or(".".into());
let full_path = Path::new(&root).join(&path);
if full_path.is_file() {
let mut buf = String::new();
File::open(full_path)
.and_then(|mut file| file.read_to_string(&mut buf))
.expect(&format!("Error reading source from {:?}", path));
buf
} else {
panic!("File {:?} was not found ; note that the path must be relative to your Cargo.toml", path);
}
}
}
};
let ty_str = syn_item.attrs.iter().filter_map(|attr| {
attr.interpret_meta().and_then(|meta| {
match meta {
syn::Meta::NameValue(syn::MetaNameValue { ident, lit: syn::Lit::Str(lit_str), .. }) => {
match ident.to_string().as_ref() {
"ty" => Some(lit_str.value()),
_ => None
}
}
_ => None
}
})
}).next().expect("Can't find `ty` attribute ; put #[ty = \"vertex\"] for example.");
let ty = match &ty_str[..] {
"vertex" => vulkano_shaders::ShaderKind::Vertex,
"fragment" => vulkano_shaders::ShaderKind::Fragment,
"geometry" => vulkano_shaders::ShaderKind::Geometry,
"tess_ctrl" => vulkano_shaders::ShaderKind::TessControl,
"tess_eval" => vulkano_shaders::ShaderKind::TessEvaluation,
"compute" => vulkano_shaders::ShaderKind::Compute,
_ => panic!("Unexpected shader type ; valid values: vertex, fragment, geometry, tess_ctrl, tess_eval, compute")
};
let content = vulkano_shaders::compile(&source_code, ty).unwrap();
vulkano_shaders::reflect("Shader", content.as_binary()).unwrap().into()
}

View File

@ -3,13 +3,19 @@ name = "vulkano-shaders"
version = "0.10.0"
authors = ["Pierre Krieger <pierre.krieger1708@gmail.com>", "The vulkano contributors"]
repository = "https://github.com/vulkano-rs/vulkano"
description = "Shaders "
description = "Shaders rust code generation macro"
license = "MIT/Apache-2.0"
documentation = "http://tomaka.github.io/vulkano/vulkano/index.html"
categories = ["rendering::graphics-api"]
[lib]
proc-macro = true
[dependencies]
shaderc = "0.3"
syn = "0.15"
quote = "0.6"
proc-macro2 = "0.4"
[dev-dependencies]
vulkano = { version = "0.10", path = "../vulkano" }

View File

@ -0,0 +1,319 @@
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
use std::io::Error as IoError;
use syn::Ident;
use proc_macro2::{Span, TokenStream};
use shaderc::{Compiler, CompileOptions};
pub use shaderc::{CompilationArtifact, ShaderKind};
pub use parse::ParseError;
use parse::Instruction;
use enums::Capability;
use parse;
use entry_point;
use structs;
use descriptor_sets;
use spec_consts;
pub fn compile(code: &str, ty: ShaderKind) -> Result<CompilationArtifact, String> {
let mut compiler = Compiler::new().ok_or("failed to create GLSL compiler")?;
let compile_options = CompileOptions::new().ok_or("failed to initialize compile option")?;
let content = compiler
.compile_into_spirv(&code, ty, "shader.glsl", "main", Some(&compile_options))
.map_err(|e| e.to_string())?;
Ok(content)
}
pub fn reflect(name: &str, spirv: &[u32], mod_name: &Ident, dump: bool) -> Result<TokenStream, Error> {
let struct_name = Ident::new(&name, Span::call_site());
let doc = parse::parse_spirv(spirv)?;
// checking whether each required capability is enabled in the Vulkan device
let mut cap_checks: Vec<TokenStream> = vec!();
for i in doc.instructions.iter() {
if let &Instruction::Capability(ref cap) = i {
if let Some(cap_string) = capability_name(cap) {
let cap = Ident::new(cap_string, Span::call_site());
cap_checks.push(quote!{
if !device.enabled_features().#cap {
panic!("capability {:?} not enabled", #cap_string); // TODO: error
//return Err(CapabilityNotEnabled);
}
});
}
}
}
// writing one method for each entry point of this module
let mut entry_points_inside_impl: Vec<TokenStream> = vec!();
let mut entry_points_outside_impl: Vec<TokenStream> = vec!();
for instruction in doc.instructions.iter() {
if let &Instruction::EntryPoint { .. } = instruction {
let (outside, entry_point) = entry_point::write_entry_point(&doc, instruction);
entry_points_inside_impl.push(entry_point);
entry_points_outside_impl.push(outside);
}
}
let structs = structs::write_structs(&doc);
let descriptor_sets = descriptor_sets::write_descriptor_sets(&doc);
let specialization_constants = spec_consts::write_specialization_constants(&doc);
let ast = quote!{
mod #mod_name {
#[allow(unused_imports)]
use std::sync::Arc;
#[allow(unused_imports)]
use std::vec::IntoIter as VecIntoIter;
#[allow(unused_imports)]
use vulkano::device::Device;
#[allow(unused_imports)]
use vulkano::descriptor::descriptor::DescriptorDesc;
#[allow(unused_imports)]
use vulkano::descriptor::descriptor::DescriptorDescTy;
#[allow(unused_imports)]
use vulkano::descriptor::descriptor::DescriptorBufferDesc;
#[allow(unused_imports)]
use vulkano::descriptor::descriptor::DescriptorImageDesc;
#[allow(unused_imports)]
use vulkano::descriptor::descriptor::DescriptorImageDescDimensions;
#[allow(unused_imports)]
use vulkano::descriptor::descriptor::DescriptorImageDescArray;
#[allow(unused_imports)]
use vulkano::descriptor::descriptor::ShaderStages;
#[allow(unused_imports)]
use vulkano::descriptor::descriptor_set::DescriptorSet;
#[allow(unused_imports)]
use vulkano::descriptor::descriptor_set::UnsafeDescriptorSet;
#[allow(unused_imports)]
use vulkano::descriptor::descriptor_set::UnsafeDescriptorSetLayout;
#[allow(unused_imports)]
use vulkano::descriptor::pipeline_layout::PipelineLayout;
#[allow(unused_imports)]
use vulkano::descriptor::pipeline_layout::PipelineLayoutDesc;
#[allow(unused_imports)]
use vulkano::descriptor::pipeline_layout::PipelineLayoutDescPcRange;
#[allow(unused_imports)]
use vulkano::pipeline::shader::SpecializationConstants as SpecConstsTrait;
#[allow(unused_imports)]
use vulkano::pipeline::shader::SpecializationMapEntry;
pub struct #struct_name {
shader: ::std::sync::Arc<::vulkano::pipeline::shader::ShaderModule>,
}
impl #struct_name {
/// Loads the shader in Vulkan as a `ShaderModule`.
#[inline]
#[allow(unsafe_code)]
pub fn load(device: ::std::sync::Arc<::vulkano::device::Device>)
-> Result<#struct_name, ::vulkano::OomError>
{
#( #cap_checks )*
let words = [ #( #spirv ),* ];
unsafe {
Ok(#struct_name {
shader: try!(::vulkano::pipeline::shader::ShaderModule::from_words(device, &words))
})
}
}
/// Returns the module that was created.
#[allow(dead_code)]
#[inline]
pub fn module(&self) -> &::std::sync::Arc<::vulkano::pipeline::shader::ShaderModule> {
&self.shader
}
#( #entry_points_inside_impl )*
}
#( #entry_points_outside_impl )*
pub mod ty {
#structs
}
#descriptor_sets
#specialization_constants
}
};
if dump {
println!("{}", ast.to_string());
panic!("vulkano_shader! rust codegen dumped") // TODO: use span from dump
}
Ok(ast)
}
#[derive(Debug)]
pub enum Error {
IoError(IoError),
ParseError(ParseError),
}
impl From<IoError> for Error {
#[inline]
fn from(err: IoError) -> Error {
Error::IoError(err)
}
}
impl From<ParseError> for Error {
#[inline]
fn from(err: ParseError) -> Error {
Error::ParseError(err)
}
}
/// Returns the name of the Vulkan something that corresponds to an `OpCapability`.
///
/// Returns `None` if irrelevant.
// TODO: this function is a draft, as the actual names may not be the same
fn capability_name(cap: &Capability) -> Option<&'static str> {
match *cap {
Capability::CapabilityMatrix => None, // always supported
Capability::CapabilityShader => None, // always supported
Capability::CapabilityGeometry => Some("geometry_shader"),
Capability::CapabilityTessellation => Some("tessellation_shader"),
Capability::CapabilityAddresses => panic!(), // not supported
Capability::CapabilityLinkage => panic!(), // not supported
Capability::CapabilityKernel => panic!(), // not supported
Capability::CapabilityVector16 => panic!(), // not supported
Capability::CapabilityFloat16Buffer => panic!(), // not supported
Capability::CapabilityFloat16 => panic!(), // not supported
Capability::CapabilityFloat64 => Some("shader_f3264"),
Capability::CapabilityInt64 => Some("shader_int64"),
Capability::CapabilityInt64Atomics => panic!(), // not supported
Capability::CapabilityImageBasic => panic!(), // not supported
Capability::CapabilityImageReadWrite => panic!(), // not supported
Capability::CapabilityImageMipmap => panic!(), // not supported
Capability::CapabilityPipes => panic!(), // not supported
Capability::CapabilityGroups => panic!(), // not supported
Capability::CapabilityDeviceEnqueue => panic!(), // not supported
Capability::CapabilityLiteralSampler => panic!(), // not supported
Capability::CapabilityAtomicStorage => panic!(), // not supported
Capability::CapabilityInt16 => Some("shader_int16"),
Capability::CapabilityTessellationPointSize =>
Some("shader_tessellation_and_geometry_point_size"),
Capability::CapabilityGeometryPointSize =>
Some("shader_tessellation_and_geometry_point_size"),
Capability::CapabilityImageGatherExtended => Some("shader_image_gather_extended"),
Capability::CapabilityStorageImageMultisample =>
Some("shader_storage_image_multisample"),
Capability::CapabilityUniformBufferArrayDynamicIndexing =>
Some("shader_uniform_buffer_array_dynamic_indexing"),
Capability::CapabilitySampledImageArrayDynamicIndexing =>
Some("shader_sampled_image_array_dynamic_indexing"),
Capability::CapabilityStorageBufferArrayDynamicIndexing =>
Some("shader_storage_buffer_array_dynamic_indexing"),
Capability::CapabilityStorageImageArrayDynamicIndexing =>
Some("shader_storage_image_array_dynamic_indexing"),
Capability::CapabilityClipDistance => Some("shader_clip_distance"),
Capability::CapabilityCullDistance => Some("shader_cull_distance"),
Capability::CapabilityImageCubeArray => Some("image_cube_array"),
Capability::CapabilitySampleRateShading => Some("sample_rate_shading"),
Capability::CapabilityImageRect => panic!(), // not supported
Capability::CapabilitySampledRect => panic!(), // not supported
Capability::CapabilityGenericPointer => panic!(), // not supported
Capability::CapabilityInt8 => panic!(), // not supported
Capability::CapabilityInputAttachment => None, // always supported
Capability::CapabilitySparseResidency => Some("shader_resource_residency"),
Capability::CapabilityMinLod => Some("shader_resource_min_lod"),
Capability::CapabilitySampled1D => None, // always supported
Capability::CapabilityImage1D => None, // always supported
Capability::CapabilitySampledCubeArray => Some("image_cube_array"),
Capability::CapabilitySampledBuffer => None, // always supported
Capability::CapabilityImageBuffer => None, // always supported
Capability::CapabilityImageMSArray => Some("shader_storage_image_multisample"),
Capability::CapabilityStorageImageExtendedFormats =>
Some("shader_storage_image_extended_formats"),
Capability::CapabilityImageQuery => None, // always supported
Capability::CapabilityDerivativeControl => None, // always supported
Capability::CapabilityInterpolationFunction => Some("sample_rate_shading"),
Capability::CapabilityTransformFeedback => panic!(), // not supported
Capability::CapabilityGeometryStreams => panic!(), // not supported
Capability::CapabilityStorageImageReadWithoutFormat =>
Some("shader_storage_image_read_without_format"),
Capability::CapabilityStorageImageWriteWithoutFormat =>
Some("shader_storage_image_write_without_format"),
Capability::CapabilityMultiViewport => Some("multi_viewport"),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bad_alignment() {
// vec3/mat3/mat3x* are problematic in arrays since their rust
// representations don't have the same array stride as the SPIR-V
// ones. E.g. in a vec3[2], the second element starts on the 16th
// byte, but in a rust [[f32;3];2], the second element starts on the
// 12th byte. Since we can't generate code for these types, we should
// create an error instead of generating incorrect code.
let comp = compile("
#version 450
struct MyStruct {
vec3 vs[2];
};
layout(binding=0) uniform UBO {
MyStruct s;
};
void main() {}
", ShaderKind::Vertex).unwrap();
let doc = parse::parse_spirv(comp.as_binary()).unwrap();
let res = std::panic::catch_unwind(|| structs::write_structs(&doc));
assert!(res.is_err());
}
#[test]
fn test_trivial_alignment() {
let comp = compile("
#version 450
struct MyStruct {
vec4 vs[2];
};
layout(binding=0) uniform UBO {
MyStruct s;
};
void main() {}
", ShaderKind::Vertex).unwrap();
let doc = parse::parse_spirv(comp.as_binary()).unwrap();
structs::write_structs(&doc);
}
#[test]
fn test_wrap_alignment() {
// This is a workaround suggested in the case of test_bad_alignment,
// so we should make sure it works.
let comp = compile("
#version 450
struct Vec3Wrap {
vec3 v;
};
struct MyStruct {
Vec3Wrap vs[2];
};
layout(binding=0) uniform UBO {
MyStruct s;
};
void main() {}
", ShaderKind::Vertex).unwrap();
let doc = parse::parse_spirv(comp.as_binary()).unwrap();
structs::write_structs(&doc);
}
}

View File

@ -1,32 +1,175 @@
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
//! The procedural macro for vulkano's shader system.
//! Manages the compile-time compilation of GLSL into SPIR-V and generation of assosciated rust code.
//!
//! # Basic usage
//!
//! ```
//! extern crate vulkano_shaders;
//! extern crate vulkano;
//!
//! use vulkano_shaders::vulkano_shader;
//!
//! vulkano_shader!{
//! mod_name: vertex_shader,
//! ty: "vertex",
//! src: "
//! #version 450
//!
//! layout(location = 0) in vec3 position;
//!
//! void main() {
//! gl_Position = vec4(position, 1.0);
//! }"
//! }
//! # fn main() {}
//! ```
//!
//! # Details
//!
//! If you want to take a look at what the macro generates, your best options
//! are to either read through the code that handles the generation (the
//! [`reflect`][reflect] function in the `vulkano-shaders` crate) or use a tool
//! such as [cargo-expand][cargo-expand] to view the expansion of the macro in your
//! own code. It is unfortunately not possible to provide a `generated_example`
//! module like some normal macro crates do since derive macros cannot be used from
//! the crate they are declared in. On the other hand, if you are looking for a
//! high-level overview, you can see the below section.
//!
//! # Generated code overview
//!
//! The macro generates the following items of interest:
//! * The `Shader` struct. This contains a single field, `shader`, which is an
//! `Arc<ShaderModule>`.
//! * The `Shader::load` constructor. This method takes an `Arc<Device>`, calls
//! [`ShaderModule::new`][ShaderModule::new] with the passed-in device and the
//! shader data provided via the macro, and returns `Result<Shader, OomError>`.
//! Before doing so, it loops through every capability instruction in the shader
//! data, verifying that the passed-in `Device` has the appropriate features
//! enabled. **This function currently panics if a feature required by the shader
//! is not enabled on the device.** At some point in the future it will return
//! an error instead.
//! * The `Shader::module` method. This method simply returns a reference to the
//! `Arc<ShaderModule>` contained within the `shader` field of the `Shader`
//! struct.
//! * Methods for each entry point of the shader module. These construct and
//! return the various entry point structs that can be found in the
//! [vulkano::pipeline::shader][pipeline::shader] module.
//! * A Rust struct translated from each struct contained in the shader data.
//! * The `Layout` newtype. This contains a [`ShaderStages`][ShaderStages] struct.
//! An implementation of [`PipelineLayoutDesc`][PipelineLayoutDesc] is also
//! generated for the newtype.
//! * The `SpecializationConstants` struct. This contains a field for every
//! specialization constant found in the shader data. Implementations of
//! `Default` and [`SpecializationConstants`][SpecializationConstants] are also
//! generated for the struct.
//!
//! All of these generated items will be accessed through the module specified
//! by `mod_name: foo` If you wanted to store the `Shader` in a struct of your own,
//! you could do something like this:
//!
//! ```
//! # extern crate vulkano_shaders;
//! # extern crate vulkano;
//! # fn main() {}
//! # use std::sync::Arc;
//! # use vulkano::OomError;
//! # use vulkano::device::Device;
//! # use vulkano_shaders::vulkano_shader;
//! #
//! # vulkano_shader!{
//! # mod_name: vertex_shader,
//! # ty: "vertex",
//! # src: "
//! # #version 450
//! #
//! # layout(location = 0) in vec3 position;
//! #
//! # void main() {
//! # gl_Position = vec4(position, 1.0);
//! # }"
//! # }
//! // various use statements
//! // `vertex_shader` module with shader derive
//!
//! pub struct Shaders {
//! pub vertex_shader: vertex_shader::Shader
//! }
//!
//! impl Shaders {
//! pub fn load(device: Arc<Device>) -> Result<Self, OomError> {
//! Ok(Self {
//! vertex_shader: vertex_shader::Shader::load(device)?,
//! })
//! }
//! }
//! ```
//!
//! # Options
//!
//! The options available are in the form of the following attributes:
//!
//! ## `mod_name: ...`
//!
//! Specifies the identifier for the module that the generated code goes in.
//!
//! ## `ty: "..."`
//!
//! This defines what shader type the given GLSL source will be compiled into.
//! The type can be any of the following:
//!
//! * `vertex`
//! * `fragment`
//! * `geometry`
//! * `tess_ctrl`
//! * `tess_eval`
//! * `compute`
//!
//! For details on what these shader types mean, [see Vulkano's documentation][pipeline].
//!
//! ## `src: "..."`
//!
//! Provides the raw GLSL source to be compiled in the form of a string. Cannot
//! be used in conjunction with the `path` field.
//!
//! ## `path: "..."`
//!
//! Provides the path to the GLSL source to be compiled, relative to `Cargo.toml`.
//! Cannot be used in conjunction with the `src` field.
//!
//! ## `dump: true`
//!
//! The crate fails to compile but prints the generated rust code to stdout.
//!
//! [reflect]: https://github.com/vulkano-rs/vulkano/blob/master/vulkano-shaders/src/lib.rs#L67
//! [cargo-expand]: https://github.com/dtolnay/cargo-expand
//! [ShaderModule::new]: https://docs.rs/vulkano/*/vulkano/pipeline/shader/struct.ShaderModule.html#method.new
//! [OomError]: https://docs.rs/vulkano/*/vulkano/enum.OomError.html
//! [pipeline::shader]: https://docs.rs/vulkano/*/vulkano/pipeline/shader/index.html
//! [descriptor]: https://docs.rs/vulkano/*/vulkano/descriptor/index.html
//! [ShaderStages]: https://docs.rs/vulkano/*/vulkano/descriptor/descriptor/struct.ShaderStages.html
//! [PipelineLayoutDesc]: https://docs.rs/vulkano/*/vulkano/descriptor/pipeline_layout/trait.PipelineLayoutDesc.html
//! [SpecializationConstants]: https://docs.rs/vulkano/*/vulkano/pipeline/shader/trait.SpecializationConstants.html
//! [pipeline]: https://docs.rs/vulkano/*/vulkano/pipeline/index.html
#![doc(html_logo_url = "https://raw.githubusercontent.com/vulkano-rs/vulkano/master/logo.png")]
#![recursion_limit = "1024"]
#[macro_use] extern crate quote;
extern crate shaderc;
extern crate proc_macro;
extern crate proc_macro2;
extern crate syn;
#[macro_use] extern crate syn;
use std::io::Error as IoError;
use syn::Ident;
use proc_macro2::{Span, TokenStream};
use shaderc::{Compiler, CompileOptions};
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::Path;
pub use shaderc::{CompilationArtifact, ShaderKind};
pub use parse::ParseError;
use parse::Instruction;
use enums::Capability;
use syn::parse::{Parse, ParseStream, Result};
use syn::{Ident, LitStr, LitBool};
mod codegen;
mod descriptor_sets;
mod entry_point;
mod enums;
@ -35,290 +178,131 @@ mod spec_consts;
mod structs;
mod spirv_search;
pub fn compile(code: &str, ty: ShaderKind) -> Result<CompilationArtifact, String> {
let mut compiler = Compiler::new().ok_or("failed to create GLSL compiler")?;
let compile_options = CompileOptions::new().ok_or("failed to initialize compile option")?;
use codegen::ShaderKind;
let content = compiler
.compile_into_spirv(&code, ty, "shader.glsl", "main", Some(&compile_options))
.map_err(|e| e.to_string())?;
Ok(content)
enum SourceKind {
Src(String),
Path(String),
}
pub fn reflect(name: &str, spirv: &[u32]) -> Result<TokenStream, Error> {
let struct_name = Ident::new(&name, Span::call_site());
let doc = parse::parse_spirv(spirv)?;
struct MacroInput {
mod_ident: Ident,
shader_kind: ShaderKind,
source_kind: SourceKind,
dump: bool,
}
// checking whether each required capability is enabled in the Vulkan device
let mut cap_checks: Vec<TokenStream> = vec!();
for i in doc.instructions.iter() {
if let &Instruction::Capability(ref cap) = i {
if let Some(cap_string) = capability_name(cap) {
let cap = Ident::new(cap_string, Span::call_site());
cap_checks.push(quote!{
if !device.enabled_features().#cap {
panic!("capability {:?} not enabled", #cap_string); // TODO: error
//return Err(CapabilityNotEnabled);
}
});
}
}
impl Parse for MacroInput {
fn parse(input: ParseStream) -> Result<Self> {
let mut dump = None;
let mut mod_ident = None;
let mut shader_kind = None;
let mut source_kind = None;
while !input.is_empty() {
let name: Ident = input.parse()?;
input.parse::<Token![:]>()?;
match name.to_string().as_ref() {
"mod_name" => {
if mod_ident.is_some() {
panic!("Only one `mod` can be defined")
}
// writing one method for each entry point of this module
let mut entry_points_inside_impl: Vec<TokenStream> = vec!();
let mut entry_points_outside_impl: Vec<TokenStream> = vec!();
for instruction in doc.instructions.iter() {
if let &Instruction::EntryPoint { .. } = instruction {
let (outside, entry_point) = entry_point::write_entry_point(&doc, instruction);
entry_points_inside_impl.push(entry_point);
entry_points_outside_impl.push(outside);
let mod_name: Ident = input.parse()?;
mod_ident = Some(mod_name);
}
"ty" => {
if shader_kind.is_some() {
panic!("Only one `ty` can be defined")
}
let ty: LitStr = input.parse()?;
let ty = match ty.value().as_ref() {
"vertex" => ShaderKind::Vertex,
"fragment" => ShaderKind::Fragment,
"geometry" => ShaderKind::Geometry,
"tess_ctrl" => ShaderKind::TessControl,
"tess_eval" => ShaderKind::TessEvaluation,
"compute" => ShaderKind::Compute,
_ => panic!("Unexpected shader type, valid values: vertex, fragment, geometry, tess_ctrl, tess_eval, compute")
};
shader_kind = Some(ty);
}
"src" => {
if source_kind.is_some() {
panic!("Only one `src` or `path` can be defined")
}
let src: LitStr = input.parse()?;
source_kind = Some(SourceKind::Src(src.value()));
}
"path" => {
if source_kind.is_some() {
panic!("Only one `src` or `path` can be defined")
}
let path: LitStr = input.parse()?;
source_kind = Some(SourceKind::Path(path.value()));
}
"dump" => {
if dump.is_some() {
panic!("Only one `dump` can be defined")
}
let dump_lit: LitBool = input.parse()?;
dump = Some(dump_lit.value);
}
name => panic!(format!("Unknown field name: {}", name))
}
if !input.is_empty() {
input.parse::<Token![,]>()?;
}
}
let structs = structs::write_structs(&doc);
let descriptor_sets = descriptor_sets::write_descriptor_sets(&doc);
let specialization_constants = spec_consts::write_specialization_constants(&doc);
let ast = quote!{
#[allow(unused_imports)]
use std::sync::Arc;
#[allow(unused_imports)]
use std::vec::IntoIter as VecIntoIter;
#[allow(unused_imports)]
use vulkano::device::Device;
#[allow(unused_imports)]
use vulkano::descriptor::descriptor::DescriptorDesc;
#[allow(unused_imports)]
use vulkano::descriptor::descriptor::DescriptorDescTy;
#[allow(unused_imports)]
use vulkano::descriptor::descriptor::DescriptorBufferDesc;
#[allow(unused_imports)]
use vulkano::descriptor::descriptor::DescriptorImageDesc;
#[allow(unused_imports)]
use vulkano::descriptor::descriptor::DescriptorImageDescDimensions;
#[allow(unused_imports)]
use vulkano::descriptor::descriptor::DescriptorImageDescArray;
#[allow(unused_imports)]
use vulkano::descriptor::descriptor::ShaderStages;
#[allow(unused_imports)]
use vulkano::descriptor::descriptor_set::DescriptorSet;
#[allow(unused_imports)]
use vulkano::descriptor::descriptor_set::UnsafeDescriptorSet;
#[allow(unused_imports)]
use vulkano::descriptor::descriptor_set::UnsafeDescriptorSetLayout;
#[allow(unused_imports)]
use vulkano::descriptor::pipeline_layout::PipelineLayout;
#[allow(unused_imports)]
use vulkano::descriptor::pipeline_layout::PipelineLayoutDesc;
#[allow(unused_imports)]
use vulkano::descriptor::pipeline_layout::PipelineLayoutDescPcRange;
#[allow(unused_imports)]
use vulkano::pipeline::shader::SpecializationConstants as SpecConstsTrait;
#[allow(unused_imports)]
use vulkano::pipeline::shader::SpecializationMapEntry;
pub struct #struct_name {
shader: ::std::sync::Arc<::vulkano::pipeline::shader::ShaderModule>,
}
impl #struct_name {
/// Loads the shader in Vulkan as a `ShaderModule`.
#[inline]
#[allow(unsafe_code)]
pub fn load(device: ::std::sync::Arc<::vulkano::device::Device>)
-> Result<#struct_name, ::vulkano::OomError>
{
#( #cap_checks )*
let words = [ #( #spirv ),* ];
unsafe {
Ok(#struct_name {
shader: try!(::vulkano::pipeline::shader::ShaderModule::from_words(device, &words))
})
}
}
/// Returns the module that was created.
#[allow(dead_code)]
#[inline]
pub fn module(&self) -> &::std::sync::Arc<::vulkano::pipeline::shader::ShaderModule> {
&self.shader
}
#( #entry_points_inside_impl )*
}
#( #entry_points_outside_impl )*
pub mod ty {
#structs
}
#descriptor_sets
#specialization_constants
let shader_kind = match shader_kind {
Some(shader_kind) => shader_kind,
None => panic!("Please provide a shader type e.g. `ty: \"vertex\"`")
};
//println!("{}", ast.to_string());
let source_kind = match source_kind {
Some(source_kind) => source_kind,
None => panic!("Please provide a source e.g. `path: \"foo.glsl\"` or `src: \"glsl source code here ...\"`")
};
Ok(ast)
}
let mod_ident = match mod_ident {
Some(mod_ident) => mod_ident,
None => panic!("Please provide a mod e.g. `mod: fs` ")
};
#[derive(Debug)]
pub enum Error {
IoError(IoError),
ParseError(ParseError),
}
let dump = dump.unwrap_or(false);
impl From<IoError> for Error {
#[inline]
fn from(err: IoError) -> Error {
Error::IoError(err)
Ok(MacroInput { shader_kind, source_kind, mod_ident, dump })
}
}
impl From<ParseError> for Error {
#[inline]
fn from(err: ParseError) -> Error {
Error::ParseError(err)
}
}
#[proc_macro]
pub fn vulkano_shader(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as MacroInput);
/// Returns the name of the Vulkan something that corresponds to an `OpCapability`.
///
/// Returns `None` if irrelevant.
// TODO: this function is a draft, as the actual names may not be the same
fn capability_name(cap: &Capability) -> Option<&'static str> {
match *cap {
Capability::CapabilityMatrix => None, // always supported
Capability::CapabilityShader => None, // always supported
Capability::CapabilityGeometry => Some("geometry_shader"),
Capability::CapabilityTessellation => Some("tessellation_shader"),
Capability::CapabilityAddresses => panic!(), // not supported
Capability::CapabilityLinkage => panic!(), // not supported
Capability::CapabilityKernel => panic!(), // not supported
Capability::CapabilityVector16 => panic!(), // not supported
Capability::CapabilityFloat16Buffer => panic!(), // not supported
Capability::CapabilityFloat16 => panic!(), // not supported
Capability::CapabilityFloat64 => Some("shader_f3264"),
Capability::CapabilityInt64 => Some("shader_int64"),
Capability::CapabilityInt64Atomics => panic!(), // not supported
Capability::CapabilityImageBasic => panic!(), // not supported
Capability::CapabilityImageReadWrite => panic!(), // not supported
Capability::CapabilityImageMipmap => panic!(), // not supported
Capability::CapabilityPipes => panic!(), // not supported
Capability::CapabilityGroups => panic!(), // not supported
Capability::CapabilityDeviceEnqueue => panic!(), // not supported
Capability::CapabilityLiteralSampler => panic!(), // not supported
Capability::CapabilityAtomicStorage => panic!(), // not supported
Capability::CapabilityInt16 => Some("shader_int16"),
Capability::CapabilityTessellationPointSize =>
Some("shader_tessellation_and_geometry_point_size"),
Capability::CapabilityGeometryPointSize =>
Some("shader_tessellation_and_geometry_point_size"),
Capability::CapabilityImageGatherExtended => Some("shader_image_gather_extended"),
Capability::CapabilityStorageImageMultisample =>
Some("shader_storage_image_multisample"),
Capability::CapabilityUniformBufferArrayDynamicIndexing =>
Some("shader_uniform_buffer_array_dynamic_indexing"),
Capability::CapabilitySampledImageArrayDynamicIndexing =>
Some("shader_sampled_image_array_dynamic_indexing"),
Capability::CapabilityStorageBufferArrayDynamicIndexing =>
Some("shader_storage_buffer_array_dynamic_indexing"),
Capability::CapabilityStorageImageArrayDynamicIndexing =>
Some("shader_storage_image_array_dynamic_indexing"),
Capability::CapabilityClipDistance => Some("shader_clip_distance"),
Capability::CapabilityCullDistance => Some("shader_cull_distance"),
Capability::CapabilityImageCubeArray => Some("image_cube_array"),
Capability::CapabilitySampleRateShading => Some("sample_rate_shading"),
Capability::CapabilityImageRect => panic!(), // not supported
Capability::CapabilitySampledRect => panic!(), // not supported
Capability::CapabilityGenericPointer => panic!(), // not supported
Capability::CapabilityInt8 => panic!(), // not supported
Capability::CapabilityInputAttachment => None, // always supported
Capability::CapabilitySparseResidency => Some("shader_resource_residency"),
Capability::CapabilityMinLod => Some("shader_resource_min_lod"),
Capability::CapabilitySampled1D => None, // always supported
Capability::CapabilityImage1D => None, // always supported
Capability::CapabilitySampledCubeArray => Some("image_cube_array"),
Capability::CapabilitySampledBuffer => None, // always supported
Capability::CapabilityImageBuffer => None, // always supported
Capability::CapabilityImageMSArray => Some("shader_storage_image_multisample"),
Capability::CapabilityStorageImageExtendedFormats =>
Some("shader_storage_image_extended_formats"),
Capability::CapabilityImageQuery => None, // always supported
Capability::CapabilityDerivativeControl => None, // always supported
Capability::CapabilityInterpolationFunction => Some("sample_rate_shading"),
Capability::CapabilityTransformFeedback => panic!(), // not supported
Capability::CapabilityGeometryStreams => panic!(), // not supported
Capability::CapabilityStorageImageReadWithoutFormat =>
Some("shader_storage_image_read_without_format"),
Capability::CapabilityStorageImageWriteWithoutFormat =>
Some("shader_storage_image_write_without_format"),
Capability::CapabilityMultiViewport => Some("multi_viewport"),
}
}
let source_code = match input.source_kind {
SourceKind::Src(source) => source,
SourceKind::Path(path) => {
let root = env::var("CARGO_MANIFEST_DIR").unwrap_or(".".into());
let full_path = Path::new(&root).join(&path);
#[cfg(test)]
mod tests {
use super::*;
if full_path.is_file() {
let mut buf = String::new();
File::open(full_path)
.and_then(|mut file| file.read_to_string(&mut buf))
.expect(&format!("Error reading source from {:?}", path));
buf
} else {
panic!("File {:?} was not found ; note that the path must be relative to your Cargo.toml", path);
}
}
};
#[test]
fn test_bad_alignment() {
// vec3/mat3/mat3x* are problematic in arrays since their rust
// representations don't have the same array stride as the SPIR-V
// ones. E.g. in a vec3[2], the second element starts on the 16th
// byte, but in a rust [[f32;3];2], the second element starts on the
// 12th byte. Since we can't generate code for these types, we should
// create an error instead of generating incorrect code.
let comp = compile("
#version 450
struct MyStruct {
vec3 vs[2];
};
layout(binding=0) uniform UBO {
MyStruct s;
};
void main() {}
", ShaderKind::Vertex).unwrap();
let doc = parse::parse_spirv(comp.as_binary()).unwrap();
let res = std::panic::catch_unwind(|| structs::write_structs(&doc));
assert!(res.is_err());
}
#[test]
fn test_trivial_alignment() {
let comp = compile("
#version 450
struct MyStruct {
vec4 vs[2];
};
layout(binding=0) uniform UBO {
MyStruct s;
};
void main() {}
", ShaderKind::Vertex).unwrap();
let doc = parse::parse_spirv(comp.as_binary()).unwrap();
structs::write_structs(&doc);
}
#[test]
fn test_wrap_alignment() {
// This is a workaround suggested in the case of test_bad_alignment,
// so we should make sure it works.
let comp = compile("
#version 450
struct Vec3Wrap {
vec3 v;
};
struct MyStruct {
Vec3Wrap vs[2];
};
layout(binding=0) uniform UBO {
MyStruct s;
};
void main() {}
", ShaderKind::Vertex).unwrap();
let doc = parse::parse_spirv(comp.as_binary()).unwrap();
structs::write_structs(&doc);
}
let content = codegen::compile(&source_code, input.shader_kind).unwrap();
codegen::reflect("Shader", content.as_binary(), &input.mod_ident, input.dump).unwrap().into()
}