Cleanup pass on example shader and vk example-runner (#109)

* get rid of vertex buffers in example

* get rid of unused attributes in example shader

* use negative viewport height to match wgpu

* remove depth buffer

* use SRGB surface

* improve tonemapping

remove 'gamma correction' in favor of hardware srgb support

* make clippy happy

* move tonemapping out of sky(), rename gl_pos to builtin_pos

* rename shaders and use sky shader in wgpu example runner

* apply srgb OETF and invert clip space for cpu example runner

* restructure example directory structure according to #170

* update winit in wgpu example runner

* fix deny.toml example crate refs

* fix ci example name on maOS

* example-shader -> sky-shader in docs

* update sky shader image

* re-enable clippy and make it happy
This commit is contained in:
Gray Olson 2020-10-30 11:38:47 -07:00 committed by GitHub
parent 5a7aabe836
commit b12a2f3f6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 281 additions and 826 deletions

View File

@ -35,11 +35,9 @@ clippy spirv-builder
# Examples # Examples
# disabled due to https://github.com/EmbarkStudios/rust-gpu/issues/186 clippy examples/runners/ash
#clippy examples/example-runner clippy examples/runners/wgpu
clippy examples/wgpu-example-runner clippy_no_features examples/runners/cpu
clippy_no_features examples/shaders/sky-shader
clippy_no_features examples/example-runner-cpu clippy_no_features examples/shaders/simplest-shader
clippy_no_features examples/example-shader
clippy_no_features examples/wgpu-example-shader

View File

@ -45,11 +45,11 @@ cargo_test spirv-builder
# Examples # Examples
# See: https://github.com/EmbarkStudios/rust-gpu/issues/84 # See: https://github.com/EmbarkStudios/rust-gpu/issues/84
if [[ -z "${CI}" && "$os" != "macOS" ]]; then if [[ -z "${CI}" && "$os" != "macOS" ]]; then
cargo_test examples/example-runner cargo_test examples/runners/ash
fi fi
cargo_test examples/wgpu-example-runner cargo_test examples/runners/wgpu
cargo_test_no_features examples/example-runner-cpu cargo_test_no_features examples/runners/cpu
cargo_test_no_features examples/example-shader cargo_test_no_features examples/shaders/sky-shader
cargo_test_no_features examples/wgpu-example-shader cargo_test_no_features examples/shaders/simplest-shader

569
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,10 @@
[workspace] [workspace]
members = [ members = [
"examples/example-runner-cpu", "examples/runners/cpu",
"examples/example-runner", "examples/runners/ash",
"examples/wgpu-example-runner", "examples/runners/wgpu",
"examples/example-shader", "examples/shaders/sky-shader",
"examples/wgpu-example-shader", "examples/shaders/simplest-shader",
"rustc_codegen_spirv", "rustc_codegen_spirv",
"spirv-builder", "spirv-builder",
"spirv-std", "spirv-std",

View File

@ -37,7 +37,7 @@ pub fn main_fs(input: Input<Vec4>, mut output: Output<Vec4>) {
} }
``` ```
See [source](examples/example-shader/src/lib.rs) for full details. See [source](examples/shaders/sky-shader/src/lib.rs) for full details.
## Background ## Background

View File

@ -37,9 +37,9 @@ skip = [
# by default infinite # by default infinite
skip-tree = [ skip-tree = [
# we don't really care if our example brings in some duplicate dependencies, for now # we don't really care if our example brings in some duplicate dependencies, for now
{ name = "example-runner", version = "*", depth = 20 }, { name = "example-runner-ash", version = "*", depth = 20 },
{ name = "example-runner-cpu", version = "*", depth = 20 }, { name = "example-runner-cpu", version = "*", depth = 20 },
{ name = "wgpu-example-runner", version = "*", depth = 20 }, { name = "example-runner-wgpu", version = "*", depth = 20 },
] ]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 315 KiB

View File

@ -29,17 +29,17 @@ how to use and develop on Rust-GPU.
1. **optional** Install [SPIRV-Tools](https://github.com/KhronosGroup/SPIRV-Tools#downloads) and add it to your `PATH`. You can skip this step if you just want to run examples with the defaults. See [Using installed SPIRV-Tools](#using-installed-spirv-tools) if you decide to go with this option. 1. **optional** Install [SPIRV-Tools](https://github.com/KhronosGroup/SPIRV-Tools#downloads) and add it to your `PATH`. You can skip this step if you just want to run examples with the defaults. See [Using installed SPIRV-Tools](#using-installed-spirv-tools) if you decide to go with this option.
1. Next, look at the [examples](examples) folder. There are two projects here: [examples/example-shader](examples/example-shader) and [examples/example-runner](examples/example-runner). The example-shader project is a "GPU crate", one that will be compiled to a SPIR-V module. The example-runner project is a normal, CPU crate that uses vulkan to consume the example-shader SPIR-V module to display a shader. 1. Next, look at the [examples](examples) folder. There are two kinds of targets here: [runners](examples/runners) and [shaders](examples/shaders). The projects inside `shaders` are "GPU crates", i.e. ones that will be compiled one or more SPIR-V modules. The `runner` projects are normal, CPU crates that use some graphics backend (currently, we have a [`wgpu` runner](examples/runners/wgpu), a [Vulkan runner](examples/runners/ash) through `ash`, and a barebones pure software [CPU runner](examples/runners/cpu)) to actually run one of the "GPU crate" shaders.
Run the example: Run the example:
```shell ```shell
cargo run --bin example-runner cargo run --bin example-runner-wgpu
``` ```
This will build `rustc_codegen_spirv`, the compiler, then use that compiler to build `example-shader` into a SPIR-V module, then finally, build a vulkan sample app (taken from [ash's examples](https://github.com/MaikKlein/ash/blob/master/examples/src/bin/triangle.rs)) using the built SPIR-V module to display the shader in a window. This will build `rustc_codegen_spirv`, the compiler, then use that compiler to build [`sky-shader`](examples/shaders/sky-shader) into a SPIR-V module, then finally, build a `wgpu` sample app (modified from [`wgpu`'s examples](https://github.com/gfx-rs/wgpu-rs/tree/master/examples/hello-triangle)) using the built SPIR-V module to display the shader in a window.
All of this is orchestrated by the [spirv-builder](spirv-builder) crate, which is used in example-runner's `build.rs` file. Please look at that file, as well as both example projects in general, to see how to set up your own shaders! All of this is orchestrated by the [spirv-builder](spirv-builder) crate, which is used in each of the example runners' [`build.rs` files](examples/runners/wgpu/build.rs). Please look at that file, as well as both example projects in general, to see how to set up your own shaders!
Be aware that this project is in a very early phase - if the above doesn't work, please [file an issue](https://github.com/EmbarkStudios/rust-gpu/issues)! Be aware that this project is in a very early phase - if the above doesn't work, please [file an issue](https://github.com/EmbarkStudios/rust-gpu/issues)!
@ -62,7 +62,7 @@ cargo build -Z build-std=core --target spirv-unknown-unknown --release
This will produce a `target/spirv-unknown-unknown/release/crate_name.spv` file. This will produce a `target/spirv-unknown-unknown/release/crate_name.spv` file.
To create a GPU crate, look at the [examples/example-shader](examples/example-shader) crate. In short, reference the `spirv-std` crate, and use intrinsics defined there to create your shader. To create a GPU crate, look at the crates in [examples/shaders](examples/shaders). In short, reference the `spirv-std` crate, and use intrinsics defined there to create your shader.
This is all a little convoluted, hence the [spirv-builder](spirv-builder) crate handles a lot of this. This is all a little convoluted, hence the [spirv-builder](spirv-builder) crate handles a lot of this.

View File

@ -1,8 +0,0 @@
use spirv_builder::SpirvBuilder;
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
// This will set the env var `example-shader.spv` to a spir-v file that can be include!()'d
SpirvBuilder::new("../example-shader").build()?;
Ok(())
}

View File

@ -1,5 +1,5 @@
[package] [package]
name = "example-runner" name = "example-runner-ash"
version = "0.1.0" version = "0.1.0"
authors = ["Embark <opensource@embark-studios.com>"] authors = ["Embark <opensource@embark-studios.com>"]
edition = "2018" edition = "2018"
@ -22,4 +22,4 @@ winit = "0.23.0"
ash-molten = { git = "https://github.com/EmbarkStudios/ash-molten", branch = "moltenvk-1.1.0" } ash-molten = { git = "https://github.com/EmbarkStudios/ash-molten", branch = "moltenvk-1.1.0" }
[build-dependencies] [build-dependencies]
spirv-builder = { path = "../../spirv-builder", default-features = false } spirv-builder = { path = "../../../spirv-builder", default-features = false }

View File

@ -0,0 +1,8 @@
use spirv_builder::SpirvBuilder;
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
// This will set the env var `sky-shader.spv` to a spir-v file that can be include!()'d
SpirvBuilder::new("../../shaders/sky-shader").build()?;
Ok(())
}

View File

@ -14,8 +14,6 @@ use std::borrow::Cow;
use std::default::Default; use std::default::Default;
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
use std::io::Cursor; use std::io::Cursor;
use std::mem;
use std::mem::align_of;
use std::ops::Drop; use std::ops::Drop;
use structopt::StructOpt; use structopt::StructOpt;
@ -188,10 +186,6 @@ pub struct ExampleBase {
pub draw_command_buffer: vk::CommandBuffer, pub draw_command_buffer: vk::CommandBuffer,
pub setup_command_buffer: vk::CommandBuffer, pub setup_command_buffer: vk::CommandBuffer,
pub depth_image: vk::Image,
pub depth_image_view: vk::ImageView,
pub depth_image_memory: vk::DeviceMemory,
pub present_complete_semaphore: vk::Semaphore, pub present_complete_semaphore: vk::Semaphore,
pub rendering_complete_semaphore: vk::Semaphore, pub rendering_complete_semaphore: vk::Semaphore,
@ -367,16 +361,18 @@ impl ExampleBase {
let surface_formats = surface_loader let surface_formats = surface_loader
.get_physical_device_surface_formats(pdevice, surface) .get_physical_device_surface_formats(pdevice, surface)
.unwrap(); .unwrap();
let surface_format = surface_formats let acceptable_formats = {
[
vk::Format::R8G8B8_SRGB,
vk::Format::B8G8R8_SRGB,
vk::Format::R8G8B8A8_SRGB,
vk::Format::B8G8R8A8_SRGB,
vk::Format::A8B8G8R8_SRGB_PACK32,
]
};
let surface_format = *surface_formats
.iter() .iter()
.map(|sfmt| match sfmt.format { .find(|sfmt| acceptable_formats.contains(&sfmt.format))
vk::Format::UNDEFINED => vk::SurfaceFormatKHR {
format: vk::Format::B8G8R8_UNORM,
color_space: sfmt.color_space,
},
_ => *sfmt,
})
.next()
.expect("Unable to find suitable surface format."); .expect("Unable to find suitable surface format.");
let surface_capabilities = surface_loader let surface_capabilities = surface_loader
.get_physical_device_surface_capabilities(pdevice, surface) .get_physical_device_surface_capabilities(pdevice, surface)
@ -472,41 +468,6 @@ impl ExampleBase {
}) })
.collect(); .collect();
let device_memory_properties = instance.get_physical_device_memory_properties(pdevice); let device_memory_properties = instance.get_physical_device_memory_properties(pdevice);
let depth_image_create_info = vk::ImageCreateInfo::builder()
.image_type(vk::ImageType::TYPE_2D)
.format(vk::Format::D16_UNORM)
.extent(vk::Extent3D {
width: surface_resolution.width,
height: surface_resolution.height,
depth: 1,
})
.mip_levels(1)
.array_layers(1)
.samples(vk::SampleCountFlags::TYPE_1)
.tiling(vk::ImageTiling::OPTIMAL)
.usage(vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT)
.sharing_mode(vk::SharingMode::EXCLUSIVE);
let depth_image = device.create_image(&depth_image_create_info, None).unwrap();
let depth_image_memory_req = device.get_image_memory_requirements(depth_image);
let depth_image_memory_index = find_memorytype_index(
&depth_image_memory_req,
&device_memory_properties,
vk::MemoryPropertyFlags::DEVICE_LOCAL,
)
.expect("Unable to find suitable memory index for depth image.");
let depth_image_allocate_info = vk::MemoryAllocateInfo::builder()
.allocation_size(depth_image_memory_req.size)
.memory_type_index(depth_image_memory_index);
let depth_image_memory = device
.allocate_memory(&depth_image_allocate_info, None)
.unwrap();
device
.bind_image_memory(depth_image, depth_image_memory, 0)
.expect("Unable to bind depth image memory");
let fence_create_info = let fence_create_info =
vk::FenceCreateInfo::builder().flags(vk::FenceCreateFlags::SIGNALED); vk::FenceCreateInfo::builder().flags(vk::FenceCreateFlags::SIGNALED);
@ -518,59 +479,6 @@ impl ExampleBase {
.create_fence(&fence_create_info, None) .create_fence(&fence_create_info, None)
.expect("Create fence failed."); .expect("Create fence failed.");
record_submit_commandbuffer(
&device,
setup_command_buffer,
setup_commands_reuse_fence,
present_queue,
&[],
&[],
&[],
|device, setup_command_buffer| {
let layout_transition_barriers = vk::ImageMemoryBarrier::builder()
.image(depth_image)
.dst_access_mask(
vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ
| vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE,
)
.new_layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
.old_layout(vk::ImageLayout::UNDEFINED)
.subresource_range(
vk::ImageSubresourceRange::builder()
.aspect_mask(vk::ImageAspectFlags::DEPTH)
.layer_count(1)
.level_count(1)
.build(),
);
device.cmd_pipeline_barrier(
setup_command_buffer,
vk::PipelineStageFlags::BOTTOM_OF_PIPE,
vk::PipelineStageFlags::LATE_FRAGMENT_TESTS,
vk::DependencyFlags::empty(),
&[],
&[],
&[layout_transition_barriers.build()],
);
},
);
let depth_image_view_info = vk::ImageViewCreateInfo::builder()
.subresource_range(
vk::ImageSubresourceRange::builder()
.aspect_mask(vk::ImageAspectFlags::DEPTH)
.level_count(1)
.layer_count(1)
.build(),
)
.image(depth_image)
.format(depth_image_create_info.format)
.view_type(vk::ImageViewType::TYPE_2D);
let depth_image_view = device
.create_image_view(&depth_image_view_info, None)
.unwrap();
let semaphore_create_info = vk::SemaphoreCreateInfo::default(); let semaphore_create_info = vk::SemaphoreCreateInfo::default();
let present_complete_semaphore = device let present_complete_semaphore = device
@ -599,8 +507,6 @@ impl ExampleBase {
pool, pool,
draw_command_buffer, draw_command_buffer,
setup_command_buffer, setup_command_buffer,
depth_image,
depth_image_view,
present_complete_semaphore, present_complete_semaphore,
rendering_complete_semaphore, rendering_complete_semaphore,
draw_commands_reuse_fence, draw_commands_reuse_fence,
@ -608,7 +514,6 @@ impl ExampleBase {
surface, surface,
debug_call_back, debug_call_back,
debug_utils_loader, debug_utils_loader,
depth_image_memory,
}; };
(result, events_loop) (result, events_loop)
} }
@ -627,9 +532,6 @@ impl Drop for ExampleBase {
.destroy_fence(self.draw_commands_reuse_fence, None); .destroy_fence(self.draw_commands_reuse_fence, None);
self.device self.device
.destroy_fence(self.setup_commands_reuse_fence, None); .destroy_fence(self.setup_commands_reuse_fence, None);
self.device.free_memory(self.depth_image_memory, None);
self.device.destroy_image_view(self.depth_image_view, None);
self.device.destroy_image(self.depth_image, None);
for &image_view in self.present_image_views.iter() { for &image_view in self.present_image_views.iter() {
self.device.destroy_image_view(image_view, None); self.device.destroy_image_view(image_view, None);
} }
@ -650,12 +552,6 @@ impl Drop for ExampleBase {
} }
} }
#[derive(Clone, Debug, Copy)]
struct Vertex {
pos: [f32; 4],
color: [f32; 4],
}
#[derive(Debug, StructOpt)] #[derive(Debug, StructOpt)]
#[structopt()] #[structopt()]
pub struct Options { pub struct Options {
@ -669,32 +565,18 @@ fn main() {
unsafe { unsafe {
let (base, events_loop) = ExampleBase::new(1920, 1080, &options); let (base, events_loop) = ExampleBase::new(1920, 1080, &options);
let renderpass_attachments = [ let renderpass_attachments = [vk::AttachmentDescription {
vk::AttachmentDescription { format: base.surface_format.format,
format: base.surface_format.format, samples: vk::SampleCountFlags::TYPE_1,
samples: vk::SampleCountFlags::TYPE_1, load_op: vk::AttachmentLoadOp::CLEAR,
load_op: vk::AttachmentLoadOp::CLEAR, store_op: vk::AttachmentStoreOp::STORE,
store_op: vk::AttachmentStoreOp::STORE, final_layout: vk::ImageLayout::PRESENT_SRC_KHR,
final_layout: vk::ImageLayout::PRESENT_SRC_KHR, ..Default::default()
..Default::default() }];
},
vk::AttachmentDescription {
format: vk::Format::D16_UNORM,
samples: vk::SampleCountFlags::TYPE_1,
load_op: vk::AttachmentLoadOp::CLEAR,
initial_layout: vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
final_layout: vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
..Default::default()
},
];
let color_attachment_refs = [vk::AttachmentReference { let color_attachment_refs = [vk::AttachmentReference {
attachment: 0, attachment: 0,
layout: vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL, layout: vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
}]; }];
let depth_attachment_ref = vk::AttachmentReference {
attachment: 1,
layout: vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
};
let dependencies = [vk::SubpassDependency { let dependencies = [vk::SubpassDependency {
src_subpass: vk::SUBPASS_EXTERNAL, src_subpass: vk::SUBPASS_EXTERNAL,
src_stage_mask: vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT, src_stage_mask: vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
@ -706,7 +588,6 @@ fn main() {
let subpasses = [vk::SubpassDescription::builder() let subpasses = [vk::SubpassDescription::builder()
.color_attachments(&color_attachment_refs) .color_attachments(&color_attachment_refs)
.depth_stencil_attachment(&depth_attachment_ref)
.pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS) .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS)
.build()]; .build()];
@ -724,7 +605,7 @@ fn main() {
.present_image_views .present_image_views
.iter() .iter()
.map(|&present_image_view| { .map(|&present_image_view| {
let framebuffer_attachments = [present_image_view, base.depth_image_view]; let framebuffer_attachments = [present_image_view];
let frame_buffer_create_info = vk::FramebufferCreateInfo::builder() let frame_buffer_create_info = vk::FramebufferCreateInfo::builder()
.render_pass(renderpass) .render_pass(renderpass)
.attachments(&framebuffer_attachments) .attachments(&framebuffer_attachments)
@ -738,124 +619,7 @@ fn main() {
}) })
.collect(); .collect();
let index_buffer_data = [0u32, 1, 2, 1, 2, 3]; let mut spv_file = Cursor::new(&include_bytes!(env!("sky_shader.spv"))[..]);
let index_buffer_info = vk::BufferCreateInfo::builder()
.size(std::mem::size_of_val(&index_buffer_data) as u64)
.usage(vk::BufferUsageFlags::INDEX_BUFFER)
.sharing_mode(vk::SharingMode::EXCLUSIVE);
let index_buffer = base.device.create_buffer(&index_buffer_info, None).unwrap();
let index_buffer_memory_req = base.device.get_buffer_memory_requirements(index_buffer);
let index_buffer_memory_index = find_memorytype_index(
&index_buffer_memory_req,
&base.device_memory_properties,
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT,
)
.expect("Unable to find suitable memorytype for the index buffer.");
let index_allocate_info = vk::MemoryAllocateInfo {
allocation_size: index_buffer_memory_req.size,
memory_type_index: index_buffer_memory_index,
..Default::default()
};
let index_buffer_memory = base
.device
.allocate_memory(&index_allocate_info, None)
.unwrap();
let index_ptr = base
.device
.map_memory(
index_buffer_memory,
0,
index_buffer_memory_req.size,
vk::MemoryMapFlags::empty(),
)
.unwrap();
let mut index_slice = Align::new(
index_ptr,
align_of::<u32>() as u64,
index_buffer_memory_req.size,
);
index_slice.copy_from_slice(&index_buffer_data);
base.device.unmap_memory(index_buffer_memory);
base.device
.bind_buffer_memory(index_buffer, index_buffer_memory, 0)
.unwrap();
let vertices = [
Vertex {
pos: [-1.0, 1.0, 0.0, 1.0],
color: [0.0, 1.0, 0.0, 1.0],
},
Vertex {
pos: [1.0, 1.0, 0.0, 1.0],
color: [0.0, 0.0, 1.0, 1.0],
},
Vertex {
pos: [-1.0, -1.0, 0.0, 1.0],
color: [1.0, 0.0, 0.0, 1.0],
},
Vertex {
pos: [1.0, -1.0, 0.0, 1.0],
color: [1.0, 1.0, 1.0, 1.0],
},
];
let vertex_input_buffer_info = vk::BufferCreateInfo {
size: std::mem::size_of_val(&vertices) as u64 as u64,
usage: vk::BufferUsageFlags::VERTEX_BUFFER,
sharing_mode: vk::SharingMode::EXCLUSIVE,
..Default::default()
};
let vertex_input_buffer = base
.device
.create_buffer(&vertex_input_buffer_info, None)
.unwrap();
let vertex_input_buffer_memory_req = base
.device
.get_buffer_memory_requirements(vertex_input_buffer);
let vertex_input_buffer_memory_index = find_memorytype_index(
&vertex_input_buffer_memory_req,
&base.device_memory_properties,
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT,
)
.expect("Unable to find suitable memorytype for the vertex buffer.");
let vertex_buffer_allocate_info = vk::MemoryAllocateInfo {
allocation_size: vertex_input_buffer_memory_req.size,
memory_type_index: vertex_input_buffer_memory_index,
..Default::default()
};
let vertex_input_buffer_memory = base
.device
.allocate_memory(&vertex_buffer_allocate_info, None)
.unwrap();
let vert_ptr = base
.device
.map_memory(
vertex_input_buffer_memory,
0,
vertex_input_buffer_memory_req.size,
vk::MemoryMapFlags::empty(),
)
.unwrap();
let mut vert_align = Align::new(
vert_ptr,
align_of::<Vertex>() as u64,
vertex_input_buffer_memory_req.size,
);
vert_align.copy_from_slice(&vertices);
base.device.unmap_memory(vertex_input_buffer_memory);
base.device
.bind_buffer_memory(vertex_input_buffer, vertex_input_buffer_memory, 0)
.unwrap();
let mut spv_file = Cursor::new(&include_bytes!(env!("example_shader.spv"))[..]);
let code = read_spv(&mut spv_file).expect("Failed to read spv file"); let code = read_spv(&mut spv_file).expect("Failed to read spv file");
let shader_info = vk::ShaderModuleCreateInfo::builder().code(&code); let shader_info = vk::ShaderModuleCreateInfo::builder().code(&code);
@ -888,31 +652,10 @@ fn main() {
..Default::default() ..Default::default()
}, },
]; ];
let vertex_input_binding_descriptions = [vk::VertexInputBindingDescription {
binding: 0,
stride: mem::size_of::<Vertex>() as u32,
input_rate: vk::VertexInputRate::VERTEX,
}];
let vertex_input_attribute_descriptions = [
vk::VertexInputAttributeDescription {
location: 0,
binding: 0,
format: vk::Format::R32G32B32A32_SFLOAT,
offset: offset_of!(Vertex, pos) as u32,
},
vk::VertexInputAttributeDescription {
location: 1,
binding: 0,
format: vk::Format::R32G32B32A32_SFLOAT,
offset: offset_of!(Vertex, color) as u32,
},
];
let vertex_input_state_info = vk::PipelineVertexInputStateCreateInfo { let vertex_input_state_info = vk::PipelineVertexInputStateCreateInfo {
vertex_attribute_description_count: vertex_input_attribute_descriptions.len() as u32, vertex_attribute_description_count: 0,
p_vertex_attribute_descriptions: vertex_input_attribute_descriptions.as_ptr(), vertex_binding_description_count: 0,
vertex_binding_description_count: vertex_input_binding_descriptions.len() as u32,
p_vertex_binding_descriptions: vertex_input_binding_descriptions.as_ptr(),
..Default::default() ..Default::default()
}; };
let vertex_input_assembly_state_info = vk::PipelineInputAssemblyStateCreateInfo { let vertex_input_assembly_state_info = vk::PipelineInputAssemblyStateCreateInfo {
@ -921,9 +664,9 @@ fn main() {
}; };
let viewports = [vk::Viewport { let viewports = [vk::Viewport {
x: 0.0, x: 0.0,
y: 0.0, y: base.surface_resolution.height as f32,
width: base.surface_resolution.width as f32, width: base.surface_resolution.width as f32,
height: base.surface_resolution.height as f32, height: -(base.surface_resolution.height as f32),
min_depth: 0.0, min_depth: 0.0,
max_depth: 1.0, max_depth: 1.0,
}]; }];
@ -953,9 +696,9 @@ fn main() {
..Default::default() ..Default::default()
}; };
let depth_state_info = vk::PipelineDepthStencilStateCreateInfo { let depth_state_info = vk::PipelineDepthStencilStateCreateInfo {
depth_test_enable: 1, depth_test_enable: 0,
depth_write_enable: 1, depth_write_enable: 0,
depth_compare_op: vk::CompareOp::LESS_OR_EQUAL, depth_compare_op: vk::CompareOp::ALWAYS,
front: noop_stencil_state, front: noop_stencil_state,
back: noop_stencil_state, back: noop_stencil_state,
max_depth_bounds: 1.0, max_depth_bounds: 1.0,
@ -1013,19 +756,11 @@ fn main() {
vk::Fence::null(), vk::Fence::null(),
) )
.unwrap(); .unwrap();
let clear_values = [ let clear_values = [vk::ClearValue {
vk::ClearValue { color: vk::ClearColorValue {
color: vk::ClearColorValue { float32: [0.0, 0.0, 0.0, 0.0],
float32: [0.0, 0.0, 0.0, 0.0],
},
}, },
vk::ClearValue { }];
depth_stencil: vk::ClearDepthStencilValue {
depth: 1.0,
stencil: 0,
},
},
];
let render_pass_begin_info = vk::RenderPassBeginInfo::builder() let render_pass_begin_info = vk::RenderPassBeginInfo::builder()
.render_pass(renderpass) .render_pass(renderpass)
@ -1057,26 +792,7 @@ fn main() {
); );
device.cmd_set_viewport(draw_command_buffer, 0, &viewports); device.cmd_set_viewport(draw_command_buffer, 0, &viewports);
device.cmd_set_scissor(draw_command_buffer, 0, &scissors); device.cmd_set_scissor(draw_command_buffer, 0, &scissors);
device.cmd_bind_vertex_buffers( device.cmd_draw(draw_command_buffer, 3, 1, 0, 0);
draw_command_buffer,
0,
&[vertex_input_buffer],
&[0],
);
device.cmd_bind_index_buffer(
draw_command_buffer,
index_buffer,
0,
vk::IndexType::UINT32,
);
device.cmd_draw_indexed(
draw_command_buffer,
index_buffer_data.len() as u32,
1,
0,
0,
0,
);
// Or draw without the index buffer // Or draw without the index buffer
// device.cmd_draw(draw_command_buffer, 3, 1, 0, 0); // device.cmd_draw(draw_command_buffer, 3, 1, 0, 0);
device.cmd_end_render_pass(draw_command_buffer); device.cmd_end_render_pass(draw_command_buffer);

View File

@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
[dependencies] [dependencies]
minifb = "0.19.1" minifb = "0.19.1"
# bring in the shader as natively compiled code # bring in the shader as natively compiled code
example-shader = { path = "../example-shader" } sky-shader = { path = "../../shaders/sky-shader" }
glam = { version = "0.9", default_features = false } glam = { version = "0.9", default_features = false }
# for parallelism, not really needed though # for parallelism, not really needed though

View File

@ -3,13 +3,22 @@ use minifb::{Key, Window, WindowOptions};
use rayon::prelude::*; use rayon::prelude::*;
use std::time::Instant; use std::time::Instant;
// apply the srgb OETF (i.e. do "linear to sRGB")
fn srgb_oetf(x: f32) -> f32 {
if x <= 0.0031308 {
x * 12.92
} else {
1.055 * x.powf(1.0 / 2.4) - 0.055
}
}
fn color_u32_from_vec4(v: Vec4) -> u32 { fn color_u32_from_vec4(v: Vec4) -> u32 {
let convert = |f: f32| -> u32 { (f.min(1.0).max(0.0) * 255.0).round() as u32 }; let convert = |f: f32| -> u32 { (f.min(1.0).max(0.0) * 255.0).round() as u32 };
// this later will also do linear -> sRGB gamma correction for output, convert(srgb_oetf(v.z()))
// but currently our shader output is in sRGB so no conversion needed | convert(srgb_oetf(v.y())) << 8
| convert(srgb_oetf(v.x())) << 16
convert(v.z()) | convert(v.y()) << 8 | convert(v.x()) << 16 | convert(v.w()) << 24 | convert(v.w()) << 24
} }
fn main() { fn main() {
@ -34,11 +43,11 @@ fn main() {
.map(|i| { .map(|i| {
let screen_pos = vec2( let screen_pos = vec2(
(i % WIDTH) as f32 / WIDTH as f32 * 2.0 - 1.0, (i % WIDTH) as f32 / WIDTH as f32 * 2.0 - 1.0,
(i / WIDTH) as f32 / HEIGHT as f32 * 2.0 - 1.0, -((i / WIDTH) as f32 / HEIGHT as f32 * 2.0 - 1.0),
); );
// evaluate the fragment shader for the specific pixel // evaluate the fragment shader for the specific pixel
let color = example_shader::fs(screen_pos); let color = sky_shader::fs(screen_pos);
color_u32_from_vec4(color) color_u32_from_vec4(color)
}) })

View File

@ -1,5 +1,5 @@
[package] [package]
name = "wgpu-example-runner" name = "example-runner-wgpu"
version = "0.1.0" version = "0.1.0"
authors = ["Embark <opensource@embark-studios.com>"] authors = ["Embark <opensource@embark-studios.com>"]
edition = "2018" edition = "2018"
@ -14,10 +14,10 @@ use-compiled-tools = ["spirv-builder/use-compiled-tools"]
[dependencies] [dependencies]
wgpu = "0.6.0" wgpu = "0.6.0"
futures = { version = "0.3", default-features = false, features = ["std", "executor"] } futures = { version = "0.3", default-features = false, features = ["std", "executor"] }
winit = { version = "0.22.1", features = ["web-sys"] } winit = { version = "0.23", features = ["web-sys"] }
[build-dependencies] [build-dependencies]
spirv-builder = { path = "../../spirv-builder", default-features = false } spirv-builder = { path = "../../../spirv-builder", default-features = false }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
wgpu-subscriber = "0.1.0" wgpu-subscriber = "0.1.0"

View File

@ -2,8 +2,8 @@ use spirv_builder::SpirvBuilder;
use std::error::Error; use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
// This will set the env var `wgpu-example-shader.spv` to a spir-v file that can be include!()'d // This will set the env var `sky-shader.spv` to a spir-v file that can be include!()'d
SpirvBuilder::new("../wgpu-example-shader") SpirvBuilder::new("../../shaders/sky-shader")
.spirv_version(1, 0) .spirv_version(1, 0)
.build()?; .build()?;
Ok(()) Ok(())

View File

@ -31,7 +31,7 @@ async fn run(event_loop: EventLoop<()>, window: Window, swapchain_format: wgpu::
.expect("Failed to create device"); .expect("Failed to create device");
// Load the shaders from disk // Load the shaders from disk
let module = device.create_shader_module(wgpu::include_spirv!(env!("wgpu_example_shader.spv"))); let module = device.create_shader_module(wgpu::include_spirv!(env!("sky_shader.spv")));
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: None, label: None,

View File

@ -1,5 +1,5 @@
[package] [package]
name = "wgpu-example-shader" name = "simplest-shader"
version = "0.1.0" version = "0.1.0"
authors = ["Embark <opensource@embark-studios.com>"] authors = ["Embark <opensource@embark-studios.com>"]
edition = "2018" edition = "2018"
@ -9,5 +9,5 @@ license = "MIT OR Apache-2.0"
crate-type = ["dylib"] crate-type = ["dylib"]
[dependencies] [dependencies]
spirv-std = { path = "../../spirv-std" } spirv-std = { path = "../../../spirv-std" }
glam = { version = "0.9", default_features = false } glam = { version = "0.9", default_features = false }

View File

@ -1,5 +1,5 @@
[package] [package]
name = "example-shader" name = "sky-shader"
version = "0.1.0" version = "0.1.0"
authors = ["Embark <opensource@embark-studios.com>"] authors = ["Embark <opensource@embark-studios.com>"]
edition = "2018" edition = "2018"
@ -9,5 +9,5 @@ license = "MIT OR Apache-2.0"
crate-type = ["dylib"] crate-type = ["dylib"]
[dependencies] [dependencies]
spirv-std = { path = "../../spirv-std" } spirv-std = { path = "../../../spirv-std" }
glam = { version = "0.9", default_features = false } glam = { version = "0.9", default_features = false }

View File

@ -10,7 +10,6 @@ use glam::{const_vec3, Mat4, Vec2, Vec3, Vec4};
use spirv_std::{Input, MathExt, Output}; use spirv_std::{Input, MathExt, Output};
const DEPOLARIZATION_FACTOR: f32 = 0.035; const DEPOLARIZATION_FACTOR: f32 = 0.035;
const LUMINANCE: f32 = 1.0;
const MIE_COEFFICIENT: f32 = 0.005; const MIE_COEFFICIENT: f32 = 0.005;
const MIE_DIRECTIONAL_G: f32 = 0.8; const MIE_DIRECTIONAL_G: f32 = 0.8;
const MIE_K_COEFFICIENT: Vec3 = const_vec3!([0.686, 0.678, 0.666]); const MIE_K_COEFFICIENT: Vec3 = const_vec3!([0.686, 0.678, 0.666]);
@ -24,19 +23,16 @@ const REFRACTIVE_INDEX: f32 = 1.0003;
const SUN_ANGULAR_DIAMETER_DEGREES: f32 = 0.0093333; const SUN_ANGULAR_DIAMETER_DEGREES: f32 = 0.0093333;
const SUN_INTENSITY_FACTOR: f32 = 1000.0; const SUN_INTENSITY_FACTOR: f32 = 1000.0;
const SUN_INTENSITY_FALLOFF_STEEPNESS: f32 = 1.5; const SUN_INTENSITY_FALLOFF_STEEPNESS: f32 = 1.5;
const TONEMAP_WEIGHTING: Vec3 = const_vec3!([9.50; 3]);
const TURBIDITY: f32 = 2.0; const TURBIDITY: f32 = 2.0;
// TODO: add this to glam? Rust std has it on f32/f64 // TODO: add this to glam? Rust std has it on f32/f64
fn pow(v: Vec3, power: f32) -> Vec3 { fn pow(v: Vec3, power: f32) -> Vec3 {
let v: [f32; 3] = v.into(); Vec3::new(v.x().pow(power), v.y().pow(power), v.z().pow(power))
Vec3::new(v[0].pow(power), v[1].pow(power), v[2].pow(power))
} }
// TODO: add this to glam? Rust std has it on f32/f64 // TODO: add this to glam? Rust std has it on f32/f64
fn exp(v: Vec3) -> Vec3 { fn exp(v: Vec3) -> Vec3 {
let v: [f32; 3] = v.into(); Vec3::new(v.x().exp(), v.y().exp(), v.z().exp())
Vec3::new(v[0].exp(), v[1].exp(), v[2].exp())
} }
/// Based on: https://seblagarde.wordpress.com/2014/12/01/inverse-trigonometric-functions-gpu-optimization-for-amd-gcn-architecture/ /// Based on: https://seblagarde.wordpress.com/2014/12/01/inverse-trigonometric-functions-gpu-optimization-for-amd-gcn-architecture/
@ -90,15 +86,15 @@ fn sun_intensity(zenith_angle_cos: f32) -> f32 {
) )
} }
fn uncharted2_tonemap(w: Vec3) -> Vec3 { fn tonemap(col: Vec3) -> Vec3 {
const A: Vec3 = const_vec3!([0.15; 3]); // Shoulder strength // see https://www.desmos.com/calculator/0eo9pzo1at
const B: Vec3 = const_vec3!([0.50; 3]); // Linear strength const A: f32 = 2.35;
const C: Vec3 = const_vec3!([0.10; 3]); // Linear angle const B: f32 = 2.8826666;
const D: Vec3 = const_vec3!([0.20; 3]); // Toe strength const C: f32 = 789.7459;
const E: Vec3 = const_vec3!([0.02; 3]); // Toe numerator const D: f32 = 0.935;
const F: Vec3 = const_vec3!([0.30; 3]); // Toe denominator
((w * (A * w + C * B) + D * E) / (w * (A * w + B) + D * F)) - E / F let z = pow(col, A);
z / (pow(z, D) * B + Vec3::splat(C))
} }
fn sky(dir: Vec3, sun_position: Vec3) -> Vec3 { fn sky(dir: Vec3, sun_position: Vec3) -> Vec3 {
@ -149,16 +145,8 @@ fn sky(dir: Vec3, sun_position: Vec3) -> Vec3 {
); );
let mut l0 = 0.1 * fex; let mut l0 = 0.1 * fex;
l0 += sun_e * 19000.0 * fex * sundisk; l0 += sun_e * 19000.0 * fex * sundisk;
let mut tex_color = lin + l0;
tex_color *= Vec3::splat(0.04);
tex_color += Vec3::new(0.0, 0.001, 0.0025) * 0.3;
// Tonemapping lin + l0
let white_scale = 1.0 / uncharted2_tonemap(TONEMAP_WEIGHTING);
let curr = uncharted2_tonemap(((2.0 / LUMINANCE.pow(4.0)).log2()) * tex_color);
let color = curr * white_scale;
pow(color, 1.0 / (1.2 + (1.2 * sunfade)))
} }
pub fn fs(screen_pos: Vec2) -> Vec4 { pub fn fs(screen_pos: Vec2) -> Vec4 {
@ -172,37 +160,45 @@ pub fn fs(screen_pos: Vec2) -> Vec4 {
Vec4::new(0.0, -0.14834046, -0.98893654, 0.0), Vec4::new(0.0, -0.14834046, -0.98893654, 0.0),
); );
let cs_pos = Vec4::new(screen_pos.x(), -screen_pos.y(), 1.0, 1.0); let clip_pos = screen_pos.extend(1.0).extend(1.0);
let ws_pos = { let world_pos = {
let p = clip_to_world.mul_vec4(cs_pos); let p = clip_to_world.mul_vec4(clip_pos);
p.truncate() / p.w() p.truncate() / p.w()
}; };
let dir = (ws_pos - eye_pos).normalize(); let dir = (world_pos - eye_pos).normalize();
// evaluate Preetham sky model // evaluate Preetham sky model
let color = sky(dir, sun_pos); let color = sky(dir, sun_pos);
color.extend(1.0) // Tonemapping
let color = color.max(Vec3::splat(0.0)).min(Vec3::splat(1024.0));
tonemap(color).extend(1.0)
} }
#[allow(unused_attributes)] #[allow(unused_attributes)]
#[spirv(fragment)] #[spirv(fragment)]
pub fn main_fs(input: Input<Vec4>, mut output: Output<Vec4>) { pub fn main_fs(in_pos: Input<Vec2>, mut output: Output<Vec4>) {
let v = input.load(); let color = fs(in_pos.load());
let color = fs(Vec2::new(v.x(), v.y())); output.store(color);
output.store(color)
} }
#[allow(unused_attributes)] #[allow(unused_attributes)]
#[spirv(vertex)] #[spirv(vertex)]
pub fn main_vs( pub fn main_vs(
in_pos: Input<Vec4>, #[spirv(vertex_index)] vert_idx: Input<i32>,
_in_color: Input<Vec4>, #[spirv(position)] mut builtin_pos: Output<Vec4>,
#[spirv(position)] mut out_pos: Output<Vec4>, mut out_pos: Output<Vec2>,
mut out_color: Output<Vec4>,
) { ) {
out_pos.store(in_pos.load()); let vert_idx = vert_idx.load();
out_color.store(in_pos.load());
// Create a "full screen triangle" by mapping the vertex index.
// ported from https://www.saschawillems.de/blog/2016/08/13/vulkan-tutorial-on-rendering-a-fullscreen-quad-without-buffers/
let uv = Vec2::new(((vert_idx << 1) & 2) as f32, (vert_idx & 2) as f32);
let pos = 2.0 * uv - Vec2::one();
builtin_pos.store(pos.extend(0.0).extend(1.0));
out_pos.store(pos);
} }
#[cfg(all(not(test), target_arch = "spirv"))] #[cfg(all(not(test), target_arch = "spirv"))]