mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-21 22:34:43 +00:00
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:
parent
7a11120350
commit
494a0c30c8
12
CHANGELOG.md
12
CHANGELOG.md
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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(), &())
|
||||
|
@ -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;
|
||||
}"
|
||||
}
|
||||
|
@ -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;
|
||||
}"
|
||||
}
|
||||
|
@ -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;
|
||||
}"
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}"
|
||||
}
|
||||
|
@ -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;
|
||||
}"
|
||||
}
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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");
|
||||
|
14
examples/src/bin/teapot/frag.glsl
Normal file
14
examples/src/bin/teapot/frag.glsl
Normal 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);
|
||||
}
|
@ -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"
|
||||
}
|
18
examples/src/bin/teapot/vert.glsl
Normal file
18
examples/src/bin/teapot/vert.glsl
Normal 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);
|
||||
}
|
@ -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");
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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 = []
|
||||
|
@ -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()
|
||||
}
|
@ -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" }
|
||||
|
319
vulkano-shaders/src/codegen.rs
Normal file
319
vulkano-shaders/src/codegen.rs
Normal 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);
|
||||
}
|
||||
}
|
@ -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 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))
|
||||
})
|
||||
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))
|
||||
}
|
||||
|
||||
/// Returns the module that was created.
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
pub fn module(&self) -> &::std::sync::Arc<::vulkano::pipeline::shader::ShaderModule> {
|
||||
&self.shader
|
||||
if !input.is_empty() {
|
||||
input.parse::<Token![,]>()?;
|
||||
}
|
||||
|
||||
#( #entry_points_inside_impl )*
|
||||
}
|
||||
|
||||
#( #entry_points_outside_impl )*
|
||||
let shader_kind = match shader_kind {
|
||||
Some(shader_kind) => shader_kind,
|
||||
None => panic!("Please provide a shader type e.g. `ty: \"vertex\"`")
|
||||
};
|
||||
|
||||
pub mod ty {
|
||||
#structs
|
||||
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 ...\"`")
|
||||
};
|
||||
|
||||
let mod_ident = match mod_ident {
|
||||
Some(mod_ident) => mod_ident,
|
||||
None => panic!("Please provide a mod e.g. `mod: fs` ")
|
||||
};
|
||||
|
||||
let dump = dump.unwrap_or(false);
|
||||
|
||||
Ok(MacroInput { shader_kind, source_kind, mod_ident, dump })
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn vulkano_shader(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as MacroInput);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
#descriptor_sets
|
||||
#specialization_constants
|
||||
};
|
||||
|
||||
//println!("{}", ast.to_string());
|
||||
|
||||
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);
|
||||
}
|
||||
let content = codegen::compile(&source_code, input.shader_kind).unwrap();
|
||||
codegen::reflect("Shader", content.as_binary(), &input.mod_ident, input.dump).unwrap().into()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user