mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-22 06:45:23 +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)
|
# 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.
|
- Removed mir support, as it is being removed from the vulkan spec.
|
||||||
- Remove vulkano_shaders::build_glsl_shaders
|
- Remove vulkano_shaders::build_glsl_shaders
|
||||||
- Split `PersistentDescriptorSetError::MissingUsage` into `MissingImageUsage` and `MissingBufferUsage`
|
- Split `PersistentDescriptorSetError::MissingUsage` into `MissingImageUsage` and `MissingBufferUsage`
|
||||||
@ -14,10 +20,6 @@
|
|||||||
|
|
||||||
# Version 0.10.0 (2018-08-10)
|
# 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
|
- Use dynamically loaded `libvulkan` like on other platforms instead of linking to MoltenVK on macOS
|
||||||
- Updated winit to version 0.17.
|
- Updated winit to version 0.17.
|
||||||
- Allow custom implementations of `RenderPassDesc` to specify `VK_SUBPASS_EXTERNAL` as a dependency source or destination
|
- Allow custom implementations of `RenderPassDesc` to specify `VK_SUBPASS_EXTERNAL` as a dependency source or destination
|
||||||
|
@ -6,7 +6,7 @@ publish = false
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
vulkano = { path = "../vulkano" }
|
vulkano = { path = "../vulkano" }
|
||||||
vulkano-shader-derive = { path = "../vulkano-shader-derive" }
|
vulkano-shaders = { path = "../vulkano-shaders" }
|
||||||
vulkano-win = { path = "../vulkano-win" }
|
vulkano-win = { path = "../vulkano-win" }
|
||||||
cgmath = "0.16.1"
|
cgmath = "0.16.1"
|
||||||
image = "0.20.0"
|
image = "0.20.0"
|
||||||
|
@ -15,8 +15,7 @@
|
|||||||
|
|
||||||
// Note that since we don't create any window, fewer imports are needed.
|
// Note that since we don't create any window, fewer imports are needed.
|
||||||
extern crate vulkano;
|
extern crate vulkano;
|
||||||
#[macro_use]
|
extern crate vulkano_shaders;
|
||||||
extern crate vulkano_shader_derive;
|
|
||||||
|
|
||||||
use vulkano::buffer::BufferUsage;
|
use vulkano::buffer::BufferUsage;
|
||||||
use vulkano::buffer::CpuAccessibleBuffer;
|
use vulkano::buffer::CpuAccessibleBuffer;
|
||||||
@ -29,9 +28,28 @@ use vulkano::instance::InstanceExtensions;
|
|||||||
use vulkano::pipeline::ComputePipeline;
|
use vulkano::pipeline::ComputePipeline;
|
||||||
use vulkano::sync::now;
|
use vulkano::sync::now;
|
||||||
use vulkano::sync::GpuFuture;
|
use vulkano::sync::GpuFuture;
|
||||||
|
use vulkano_shaders::vulkano_shader;
|
||||||
|
|
||||||
use std::sync::Arc;
|
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() {
|
fn main() {
|
||||||
// As with other examples, the first step is to create an instance.
|
// As with other examples, the first step is to create an instance.
|
||||||
let instance = Instance::new(None, &InstanceExtensions::none(), None)
|
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
|
// If you are familiar with graphics pipeline, the principle is the same except that compute
|
||||||
// pipelines are much simpler to create.
|
// pipelines are much simpler to create.
|
||||||
let pipeline = Arc::new({
|
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())
|
let shader = cs::Shader::load(device.clone())
|
||||||
.expect("failed to create shader module");
|
.expect("failed to create shader module");
|
||||||
ComputePipeline::new(device.clone(), &shader.main_entry_point(), &())
|
ComputePipeline::new(device.clone(), &shader.main_entry_point(), &())
|
||||||
|
@ -23,6 +23,7 @@ use vulkano::pipeline::blend::BlendOp;
|
|||||||
use vulkano::pipeline::GraphicsPipeline;
|
use vulkano::pipeline::GraphicsPipeline;
|
||||||
use vulkano::pipeline::GraphicsPipelineAbstract;
|
use vulkano::pipeline::GraphicsPipelineAbstract;
|
||||||
use vulkano::pipeline::viewport::Viewport;
|
use vulkano::pipeline::viewport::Viewport;
|
||||||
|
use vulkano_shaders::vulkano_shader;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -142,27 +143,23 @@ struct Vertex {
|
|||||||
}
|
}
|
||||||
impl_vertex!(Vertex, position);
|
impl_vertex!(Vertex, position);
|
||||||
|
|
||||||
mod vs {
|
vulkano_shader!{
|
||||||
#[derive(VulkanoShader)]
|
mod_name: vs,
|
||||||
#[allow(dead_code)]
|
ty: "vertex",
|
||||||
#[ty = "vertex"]
|
src: "
|
||||||
#[src = "
|
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
layout(location = 0) in vec2 position;
|
layout(location = 0) in vec2 position;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = vec4(position, 0.0, 1.0);
|
gl_Position = vec4(position, 0.0, 1.0);
|
||||||
}
|
}"
|
||||||
"]
|
|
||||||
struct Dummy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mod fs {
|
vulkano_shader!{
|
||||||
#[derive(VulkanoShader)]
|
mod_name: fs,
|
||||||
#[allow(dead_code)]
|
ty: "fragment",
|
||||||
#[ty = "fragment"]
|
src: "
|
||||||
#[src = "
|
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
// The `color_input` parameter of the `draw` method.
|
// The `color_input` parameter of the `draw` method.
|
||||||
@ -180,7 +177,5 @@ void main() {
|
|||||||
vec3 in_diffuse = subpassLoad(u_diffuse).rgb;
|
vec3 in_diffuse = subpassLoad(u_diffuse).rgb;
|
||||||
f_color.rgb = push_constants.color.rgb * in_diffuse;
|
f_color.rgb = push_constants.color.rgb * in_diffuse;
|
||||||
f_color.a = 1.0;
|
f_color.a = 1.0;
|
||||||
}
|
}"
|
||||||
"]
|
|
||||||
struct Dummy;
|
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ use vulkano::pipeline::blend::BlendOp;
|
|||||||
use vulkano::pipeline::GraphicsPipeline;
|
use vulkano::pipeline::GraphicsPipeline;
|
||||||
use vulkano::pipeline::GraphicsPipelineAbstract;
|
use vulkano::pipeline::GraphicsPipelineAbstract;
|
||||||
use vulkano::pipeline::viewport::Viewport;
|
use vulkano::pipeline::viewport::Viewport;
|
||||||
|
use vulkano_shaders::vulkano_shader;
|
||||||
use cgmath::Vector3;
|
use cgmath::Vector3;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -154,27 +155,23 @@ struct Vertex {
|
|||||||
}
|
}
|
||||||
impl_vertex!(Vertex, position);
|
impl_vertex!(Vertex, position);
|
||||||
|
|
||||||
mod vs {
|
vulkano_shader!{
|
||||||
#[derive(VulkanoShader)]
|
mod_name: vs,
|
||||||
#[allow(dead_code)]
|
ty: "vertex",
|
||||||
#[ty = "vertex"]
|
src: "
|
||||||
#[src = "
|
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
layout(location = 0) in vec2 position;
|
layout(location = 0) in vec2 position;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = vec4(position, 0.0, 1.0);
|
gl_Position = vec4(position, 0.0, 1.0);
|
||||||
}
|
}"
|
||||||
"]
|
|
||||||
struct Dummy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mod fs {
|
vulkano_shader!{
|
||||||
#[derive(VulkanoShader)]
|
mod_name: fs,
|
||||||
#[allow(dead_code)]
|
ty: "fragment",
|
||||||
#[ty = "fragment"]
|
src: "
|
||||||
#[src = "
|
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
// The `color_input` parameter of the `draw` method.
|
// The `color_input` parameter of the `draw` method.
|
||||||
@ -203,7 +200,5 @@ void main() {
|
|||||||
vec3 in_diffuse = subpassLoad(u_diffuse).rgb;
|
vec3 in_diffuse = subpassLoad(u_diffuse).rgb;
|
||||||
f_color.rgb = light_percent * push_constants.color.rgb * in_diffuse;
|
f_color.rgb = light_percent * push_constants.color.rgb * in_diffuse;
|
||||||
f_color.a = 1.0;
|
f_color.a = 1.0;
|
||||||
}
|
}"
|
||||||
"]
|
|
||||||
struct Dummy;
|
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ use vulkano::pipeline::blend::BlendOp;
|
|||||||
use vulkano::pipeline::GraphicsPipeline;
|
use vulkano::pipeline::GraphicsPipeline;
|
||||||
use vulkano::pipeline::GraphicsPipelineAbstract;
|
use vulkano::pipeline::GraphicsPipelineAbstract;
|
||||||
use vulkano::pipeline::viewport::Viewport;
|
use vulkano::pipeline::viewport::Viewport;
|
||||||
|
use vulkano_shaders::vulkano_shader;
|
||||||
use cgmath::Matrix4;
|
use cgmath::Matrix4;
|
||||||
use cgmath::Vector3;
|
use cgmath::Vector3;
|
||||||
|
|
||||||
@ -168,11 +169,10 @@ struct Vertex {
|
|||||||
}
|
}
|
||||||
impl_vertex!(Vertex, position);
|
impl_vertex!(Vertex, position);
|
||||||
|
|
||||||
mod vs {
|
vulkano_shader!{
|
||||||
#[derive(VulkanoShader)]
|
mod_name: vs,
|
||||||
#[allow(dead_code)]
|
ty: "vertex",
|
||||||
#[ty = "vertex"]
|
src: "
|
||||||
#[src = "
|
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
layout(location = 0) in vec2 position;
|
layout(location = 0) in vec2 position;
|
||||||
@ -181,16 +181,13 @@ layout(location = 0) out vec2 v_screen_coords;
|
|||||||
void main() {
|
void main() {
|
||||||
v_screen_coords = position;
|
v_screen_coords = position;
|
||||||
gl_Position = vec4(position, 0.0, 1.0);
|
gl_Position = vec4(position, 0.0, 1.0);
|
||||||
}
|
}"
|
||||||
"]
|
|
||||||
struct Dummy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mod fs {
|
vulkano_shader!{
|
||||||
#[derive(VulkanoShader)]
|
mod_name: fs,
|
||||||
#[allow(dead_code)]
|
ty: "fragment",
|
||||||
#[ty = "fragment"]
|
src: "
|
||||||
#[src = "
|
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
// The `color_input` parameter of the `draw` method.
|
// The `color_input` parameter of the `draw` method.
|
||||||
@ -236,7 +233,5 @@ void main() {
|
|||||||
vec3 in_diffuse = subpassLoad(u_diffuse).rgb;
|
vec3 in_diffuse = subpassLoad(u_diffuse).rgb;
|
||||||
f_color.rgb = push_constants.color.rgb * light_percent * in_diffuse;
|
f_color.rgb = push_constants.color.rgb * light_percent * in_diffuse;
|
||||||
f_color.a = 1.0;
|
f_color.a = 1.0;
|
||||||
}
|
}"
|
||||||
"]
|
|
||||||
struct Dummy;
|
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,7 @@
|
|||||||
extern crate cgmath;
|
extern crate cgmath;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate vulkano;
|
extern crate vulkano;
|
||||||
#[macro_use]
|
extern crate vulkano_shaders;
|
||||||
extern crate vulkano_shader_derive;
|
|
||||||
extern crate winit;
|
extern crate winit;
|
||||||
extern crate vulkano_win;
|
extern crate vulkano_win;
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ use vulkano::framebuffer::Subpass;
|
|||||||
use vulkano::pipeline::GraphicsPipeline;
|
use vulkano::pipeline::GraphicsPipeline;
|
||||||
use vulkano::pipeline::GraphicsPipelineAbstract;
|
use vulkano::pipeline::GraphicsPipelineAbstract;
|
||||||
use vulkano::pipeline::viewport::Viewport;
|
use vulkano::pipeline::viewport::Viewport;
|
||||||
|
use vulkano_shaders::vulkano_shader;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -94,27 +95,23 @@ struct Vertex {
|
|||||||
}
|
}
|
||||||
impl_vertex!(Vertex, position);
|
impl_vertex!(Vertex, position);
|
||||||
|
|
||||||
mod vs {
|
vulkano_shader!{
|
||||||
#[derive(VulkanoShader)]
|
mod_name: vs,
|
||||||
#[allow(dead_code)]
|
ty: "vertex",
|
||||||
#[ty = "vertex"]
|
src: "
|
||||||
#[src = "
|
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
layout(location = 0) in vec2 position;
|
layout(location = 0) in vec2 position;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = vec4(position, 0.0, 1.0);
|
gl_Position = vec4(position, 0.0, 1.0);
|
||||||
}
|
}"
|
||||||
"]
|
|
||||||
struct Dummy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mod fs {
|
vulkano_shader!{
|
||||||
#[derive(VulkanoShader)]
|
mod_name: fs,
|
||||||
#[allow(dead_code)]
|
ty: "fragment",
|
||||||
#[ty = "fragment"]
|
src: "
|
||||||
#[src = "
|
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
layout(location = 0) out vec4 f_color;
|
layout(location = 0) out vec4 f_color;
|
||||||
@ -123,7 +120,5 @@ layout(location = 1) out vec3 f_normal;
|
|||||||
void main() {
|
void main() {
|
||||||
f_color = vec4(1.0, 1.0, 1.0, 1.0);
|
f_color = vec4(1.0, 1.0, 1.0, 1.0);
|
||||||
f_normal = vec3(0.0, 0.0, 1.0);
|
f_normal = vec3(0.0, 0.0, 1.0);
|
||||||
}
|
}"
|
||||||
"]
|
|
||||||
struct Dummy;
|
|
||||||
}
|
}
|
||||||
|
@ -13,12 +13,12 @@ extern crate winit;
|
|||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate vulkano;
|
extern crate vulkano;
|
||||||
#[macro_use]
|
extern crate vulkano_shaders;
|
||||||
extern crate vulkano_shader_derive;
|
|
||||||
extern crate vulkano_win;
|
extern crate vulkano_win;
|
||||||
|
|
||||||
use vulkano_win::VkSurfaceBuild;
|
use vulkano_win::VkSurfaceBuild;
|
||||||
use vulkano::sync::GpuFuture;
|
use vulkano::sync::GpuFuture;
|
||||||
|
use vulkano_shaders::vulkano_shader;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -242,10 +242,10 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod vs {
|
vulkano_shader!{
|
||||||
#[derive(VulkanoShader)]
|
mod_name: vs,
|
||||||
#[ty = "vertex"]
|
ty: "vertex",
|
||||||
#[src = "
|
src: "
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
layout(location = 0) in vec2 position;
|
layout(location = 0) in vec2 position;
|
||||||
@ -254,16 +254,13 @@ layout(location = 0) out vec2 tex_coords;
|
|||||||
void main() {
|
void main() {
|
||||||
gl_Position = vec4(position, 0.0, 1.0);
|
gl_Position = vec4(position, 0.0, 1.0);
|
||||||
tex_coords = position + vec2(0.5);
|
tex_coords = position + vec2(0.5);
|
||||||
}
|
}"
|
||||||
"]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
struct Dummy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mod fs {
|
vulkano_shader!{
|
||||||
#[derive(VulkanoShader)]
|
mod_name: fs,
|
||||||
#[ty = "fragment"]
|
ty: "fragment",
|
||||||
#[src = "
|
src: "
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
layout(location = 0) in vec2 tex_coords;
|
layout(location = 0) in vec2 tex_coords;
|
||||||
@ -273,8 +270,5 @@ layout(set = 0, binding = 0) uniform sampler2D tex;
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
f_color = texture(tex, tex_coords);
|
f_color = texture(tex, tex_coords);
|
||||||
}
|
}"
|
||||||
"]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
struct Dummy;
|
|
||||||
}
|
}
|
||||||
|
@ -67,8 +67,7 @@
|
|||||||
extern crate image;
|
extern crate image;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate vulkano;
|
extern crate vulkano;
|
||||||
#[macro_use]
|
extern crate vulkano_shaders;
|
||||||
extern crate vulkano_shader_derive;
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use image::ImageBuffer;
|
use image::ImageBuffer;
|
||||||
@ -92,6 +91,33 @@ use vulkano::instance::PhysicalDevice;
|
|||||||
use vulkano::pipeline::GraphicsPipeline;
|
use vulkano::pipeline::GraphicsPipeline;
|
||||||
use vulkano::pipeline::viewport::Viewport;
|
use vulkano::pipeline::viewport::Viewport;
|
||||||
use vulkano::sync::GpuFuture;
|
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() {
|
fn main() {
|
||||||
// The usual Vulkan initialization.
|
// 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,
|
// 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.
|
// 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 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");
|
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
|
// TODO: Give a paragraph about what push constants are and what problems they solve
|
||||||
extern crate vulkano;
|
extern crate vulkano;
|
||||||
#[macro_use]
|
extern crate vulkano_shaders;
|
||||||
extern crate vulkano_shader_derive;
|
|
||||||
|
|
||||||
use vulkano::buffer::BufferUsage;
|
use vulkano::buffer::BufferUsage;
|
||||||
use vulkano::buffer::CpuAccessibleBuffer;
|
use vulkano::buffer::CpuAccessibleBuffer;
|
||||||
@ -24,22 +23,14 @@ use vulkano::pipeline::ComputePipeline;
|
|||||||
use vulkano::sync::now;
|
use vulkano::sync::now;
|
||||||
use vulkano::sync::GpuFuture;
|
use vulkano::sync::GpuFuture;
|
||||||
|
|
||||||
|
use vulkano_shaders::vulkano_shader;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
fn main() {
|
vulkano_shader!{
|
||||||
let instance = Instance::new(None, &InstanceExtensions::none(), None).unwrap();
|
mod_name: cs,
|
||||||
let physical = vulkano::instance::PhysicalDevice::enumerate(&instance).next().unwrap();
|
ty: "compute",
|
||||||
let queue_family = physical.queue_families().find(|&q| q.supports_compute()).unwrap();
|
src: "
|
||||||
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 = "
|
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
|
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);
|
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())
|
let shader = cs::Shader::load(device.clone())
|
||||||
.expect("failed to create shader module");
|
.expect("failed to create shader module");
|
||||||
let pipeline = Arc::new(ComputePipeline::new(device.clone(), &shader.main_entry_point(), &()).unwrap());
|
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
|
// TODO: Give a paragraph about what specialization are and what problems they solve
|
||||||
extern crate vulkano;
|
extern crate vulkano;
|
||||||
#[macro_use]
|
extern crate vulkano_shaders;
|
||||||
extern crate vulkano_shader_derive;
|
|
||||||
|
|
||||||
use vulkano::buffer::BufferUsage;
|
use vulkano::buffer::BufferUsage;
|
||||||
use vulkano::buffer::CpuAccessibleBuffer;
|
use vulkano::buffer::CpuAccessibleBuffer;
|
||||||
@ -23,23 +22,14 @@ use vulkano::instance::InstanceExtensions;
|
|||||||
use vulkano::pipeline::ComputePipeline;
|
use vulkano::pipeline::ComputePipeline;
|
||||||
use vulkano::sync::now;
|
use vulkano::sync::now;
|
||||||
use vulkano::sync::GpuFuture;
|
use vulkano::sync::GpuFuture;
|
||||||
|
use vulkano_shaders::vulkano_shader;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
fn main() {
|
vulkano_shader!{
|
||||||
let instance = Instance::new(None, &InstanceExtensions::none(), None).unwrap();
|
mod_name: cs,
|
||||||
let physical = vulkano::instance::PhysicalDevice::enumerate(&instance).next().unwrap();
|
ty: "compute",
|
||||||
let queue_family = physical.queue_families().find(|&q| q.supports_compute()).unwrap();
|
src: "
|
||||||
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 = "
|
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
|
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);
|
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())
|
let shader = cs::Shader::load(device.clone())
|
||||||
.expect("failed to create shader module");
|
.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]
|
#[macro_use]
|
||||||
extern crate vulkano;
|
extern crate vulkano;
|
||||||
#[macro_use]
|
extern crate vulkano_shaders;
|
||||||
extern crate vulkano_shader_derive;
|
|
||||||
extern crate vulkano_win;
|
extern crate vulkano_win;
|
||||||
|
|
||||||
use vulkano_win::VkSurfaceBuild;
|
use vulkano_win::VkSurfaceBuild;
|
||||||
use vulkano::sync::GpuFuture;
|
use vulkano::sync::GpuFuture;
|
||||||
|
use vulkano_shaders::vulkano_shader;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -263,52 +263,14 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod vs {
|
vulkano_shader!{
|
||||||
#[derive(VulkanoShader)]
|
mod_name: vs,
|
||||||
#[ty = "vertex"]
|
ty: "vertex",
|
||||||
#[src = "
|
path: "src/bin/teapot/vert.glsl"
|
||||||
#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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mod fs {
|
vulkano_shader!{
|
||||||
#[derive(VulkanoShader)]
|
mod_name: fs,
|
||||||
#[ty = "fragment"]
|
ty: "fragment",
|
||||||
#[src = "
|
path: "src/bin/teapot/frag.glsl"
|
||||||
#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;
|
|
||||||
}
|
}
|
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]
|
#[macro_use]
|
||||||
extern crate vulkano;
|
extern crate vulkano;
|
||||||
#[macro_use]
|
extern crate vulkano_shaders;
|
||||||
extern crate vulkano_shader_derive;
|
|
||||||
extern crate winit;
|
extern crate winit;
|
||||||
extern crate vulkano_win;
|
extern crate vulkano_win;
|
||||||
|
|
||||||
@ -45,9 +44,98 @@ use vulkano::swapchain::AcquireError;
|
|||||||
use vulkano::swapchain::SwapchainCreationError;
|
use vulkano::swapchain::SwapchainCreationError;
|
||||||
use vulkano::sync::now;
|
use vulkano::sync::now;
|
||||||
use vulkano::sync::GpuFuture;
|
use vulkano::sync::GpuFuture;
|
||||||
|
use vulkano_shaders::vulkano_shader;
|
||||||
|
|
||||||
use std::sync::Arc;
|
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() {
|
fn main() {
|
||||||
let instance = {
|
let instance = {
|
||||||
let extensions = vulkano_win::required_extensions();
|
let extensions = vulkano_win::required_extensions();
|
||||||
@ -109,105 +197,6 @@ fn main() {
|
|||||||
].iter().cloned()).expect("failed to create buffer")
|
].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 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 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");
|
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.
|
// The `vulkano` crate is the main crate that you must use to use Vulkan.
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate vulkano;
|
extern crate vulkano;
|
||||||
// The `vulkano_shader_derive` crate allows us to use the `VulkanoShader` custom derive that we use
|
// Provides the `vulkano_shader` macro that is used to generate code for using shaders.
|
||||||
// in this example.
|
extern crate vulkano_shaders;
|
||||||
#[macro_use]
|
// The Vulkan library doesn't provide any functionality to create and handle windows, as
|
||||||
extern crate vulkano_shader_derive;
|
|
||||||
// However 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.
|
// this would be out of scope. In order to open a window, we are going to use the `winit` crate.
|
||||||
extern crate winit;
|
extern crate winit;
|
||||||
// The `vulkano_win` crate is the link between `vulkano` and `winit`. Vulkano doesn't know about
|
// 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::now;
|
||||||
use vulkano::sync::GpuFuture;
|
use vulkano::sync::GpuFuture;
|
||||||
|
|
||||||
|
use vulkano_shaders::vulkano_shader;
|
||||||
|
|
||||||
use std::sync::Arc;
|
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() {
|
fn main() {
|
||||||
// The first step of any Vulkan program is to create an instance.
|
// The first step of any Vulkan program is to create an instance.
|
||||||
let instance = {
|
let instance = {
|
||||||
@ -197,47 +231,6 @@ fn main() {
|
|||||||
].iter().cloned()).expect("failed to create buffer")
|
].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 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");
|
let fs = fs::Shader::load(device.clone()).expect("failed to create shader module");
|
||||||
|
|
||||||
|
@ -1,22 +1,9 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "vulkano-shader-derive"
|
name = "vulkano-shader-derive"
|
||||||
version = "0.10.0"
|
version = "0.11.0"
|
||||||
authors = ["Pierre Krieger <pierre.krieger1708@gmail.com>", "The vulkano contributors"]
|
authors = ["Pierre Krieger <pierre.krieger1708@gmail.com>", "The vulkano contributors"]
|
||||||
repository = "https://github.com/vulkano-rs/vulkano"
|
repository = "https://github.com/vulkano-rs/vulkano"
|
||||||
description = "Safe wrapper for the Vulkan graphics API"
|
description = "Deprecated"
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
documentation = "https://docs.rs/vulkano"
|
documentation = "https://docs.rs/vulkano"
|
||||||
categories = ["rendering::graphics-api"]
|
categories = []
|
||||||
|
|
||||||
[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" }
|
|
||||||
|
@ -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"
|
version = "0.10.0"
|
||||||
authors = ["Pierre Krieger <pierre.krieger1708@gmail.com>", "The vulkano contributors"]
|
authors = ["Pierre Krieger <pierre.krieger1708@gmail.com>", "The vulkano contributors"]
|
||||||
repository = "https://github.com/vulkano-rs/vulkano"
|
repository = "https://github.com/vulkano-rs/vulkano"
|
||||||
description = "Shaders "
|
description = "Shaders rust code generation macro"
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
documentation = "http://tomaka.github.io/vulkano/vulkano/index.html"
|
documentation = "http://tomaka.github.io/vulkano/vulkano/index.html"
|
||||||
categories = ["rendering::graphics-api"]
|
categories = ["rendering::graphics-api"]
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
shaderc = "0.3"
|
shaderc = "0.3"
|
||||||
syn = "0.15"
|
syn = "0.15"
|
||||||
quote = "0.6"
|
quote = "0.6"
|
||||||
proc-macro2 = "0.4"
|
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
|
//! The procedural macro for vulkano's shader system.
|
||||||
// Licensed under the Apache License, Version 2.0
|
//! Manages the compile-time compilation of GLSL into SPIR-V and generation of assosciated rust code.
|
||||||
// <LICENSE-APACHE or
|
//!
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
|
//! # Basic usage
|
||||||
// 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
|
//! extern crate vulkano_shaders;
|
||||||
// according to those terms.
|
//! 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")]
|
#![doc(html_logo_url = "https://raw.githubusercontent.com/vulkano-rs/vulkano/master/logo.png")]
|
||||||
|
|
||||||
#![recursion_limit = "1024"]
|
#![recursion_limit = "1024"]
|
||||||
#[macro_use] extern crate quote;
|
#[macro_use] extern crate quote;
|
||||||
extern crate shaderc;
|
extern crate shaderc;
|
||||||
|
extern crate proc_macro;
|
||||||
extern crate proc_macro2;
|
extern crate proc_macro2;
|
||||||
extern crate syn;
|
#[macro_use] extern crate syn;
|
||||||
|
|
||||||
use std::io::Error as IoError;
|
|
||||||
|
|
||||||
use syn::Ident;
|
use std::env;
|
||||||
use proc_macro2::{Span, TokenStream};
|
use std::fs::File;
|
||||||
use shaderc::{Compiler, CompileOptions};
|
use std::io::Read;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
pub use shaderc::{CompilationArtifact, ShaderKind};
|
use syn::parse::{Parse, ParseStream, Result};
|
||||||
pub use parse::ParseError;
|
use syn::{Ident, LitStr, LitBool};
|
||||||
|
|
||||||
use parse::Instruction;
|
|
||||||
use enums::Capability;
|
|
||||||
|
|
||||||
|
mod codegen;
|
||||||
mod descriptor_sets;
|
mod descriptor_sets;
|
||||||
mod entry_point;
|
mod entry_point;
|
||||||
mod enums;
|
mod enums;
|
||||||
@ -35,290 +178,131 @@ mod spec_consts;
|
|||||||
mod structs;
|
mod structs;
|
||||||
mod spirv_search;
|
mod spirv_search;
|
||||||
|
|
||||||
pub fn compile(code: &str, ty: ShaderKind) -> Result<CompilationArtifact, String> {
|
use codegen::ShaderKind;
|
||||||
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
|
enum SourceKind {
|
||||||
.compile_into_spirv(&code, ty, "shader.glsl", "main", Some(&compile_options))
|
Src(String),
|
||||||
.map_err(|e| e.to_string())?;
|
Path(String),
|
||||||
|
|
||||||
Ok(content)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reflect(name: &str, spirv: &[u32]) -> Result<TokenStream, Error> {
|
struct MacroInput {
|
||||||
let struct_name = Ident::new(&name, Span::call_site());
|
mod_ident: Ident,
|
||||||
let doc = parse::parse_spirv(spirv)?;
|
shader_kind: ShaderKind,
|
||||||
|
source_kind: SourceKind,
|
||||||
|
dump: bool,
|
||||||
|
}
|
||||||
|
|
||||||
// checking whether each required capability is enabled in the Vulkan device
|
impl Parse for MacroInput {
|
||||||
let mut cap_checks: Vec<TokenStream> = vec!();
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
for i in doc.instructions.iter() {
|
let mut dump = None;
|
||||||
if let &Instruction::Capability(ref cap) = i {
|
let mut mod_ident = None;
|
||||||
if let Some(cap_string) = capability_name(cap) {
|
let mut shader_kind = None;
|
||||||
let cap = Ident::new(cap_string, Span::call_site());
|
let mut source_kind = None;
|
||||||
cap_checks.push(quote!{
|
|
||||||
if !device.enabled_features().#cap {
|
while !input.is_empty() {
|
||||||
panic!("capability {:?} not enabled", #cap_string); // TODO: error
|
let name: Ident = input.parse()?;
|
||||||
//return Err(CapabilityNotEnabled);
|
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 mod_name: Ident = input.parse()?;
|
||||||
let mut entry_points_inside_impl: Vec<TokenStream> = vec!();
|
mod_ident = Some(mod_name);
|
||||||
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))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
"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.
|
if !input.is_empty() {
|
||||||
#[allow(dead_code)]
|
input.parse::<Token![,]>()?;
|
||||||
#[inline]
|
|
||||||
pub fn module(&self) -> &::std::sync::Arc<::vulkano::pipeline::shader::ShaderModule> {
|
|
||||||
&self.shader
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#( #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 {
|
let source_kind = match source_kind {
|
||||||
#structs
|
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());
|
let content = codegen::compile(&source_code, input.shader_kind).unwrap();
|
||||||
|
codegen::reflect("Shader", content.as_binary(), &input.mod_ident, input.dump).unwrap().into()
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user