mirror of
https://github.com/EmbarkStudios/rust-gpu.git
synced 2024-11-21 14:24:13 +00:00
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:
parent
5a7aabe836
commit
b12a2f3f6a
12
.github/workflows/clippy.sh
vendored
12
.github/workflows/clippy.sh
vendored
@ -35,11 +35,9 @@ clippy spirv-builder
|
||||
|
||||
# Examples
|
||||
|
||||
# disabled due to https://github.com/EmbarkStudios/rust-gpu/issues/186
|
||||
#clippy examples/example-runner
|
||||
clippy examples/runners/ash
|
||||
clippy examples/runners/wgpu
|
||||
|
||||
clippy examples/wgpu-example-runner
|
||||
|
||||
clippy_no_features examples/example-runner-cpu
|
||||
clippy_no_features examples/example-shader
|
||||
clippy_no_features examples/wgpu-example-shader
|
||||
clippy_no_features examples/runners/cpu
|
||||
clippy_no_features examples/shaders/sky-shader
|
||||
clippy_no_features examples/shaders/simplest-shader
|
||||
|
10
.github/workflows/test.sh
vendored
10
.github/workflows/test.sh
vendored
@ -45,11 +45,11 @@ cargo_test spirv-builder
|
||||
# Examples
|
||||
# See: https://github.com/EmbarkStudios/rust-gpu/issues/84
|
||||
if [[ -z "${CI}" && "$os" != "macOS" ]]; then
|
||||
cargo_test examples/example-runner
|
||||
cargo_test examples/runners/ash
|
||||
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/example-shader
|
||||
cargo_test_no_features examples/wgpu-example-shader
|
||||
cargo_test_no_features examples/runners/cpu
|
||||
cargo_test_no_features examples/shaders/sky-shader
|
||||
cargo_test_no_features examples/shaders/simplest-shader
|
||||
|
569
Cargo.lock
generated
569
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
11
Cargo.toml
11
Cargo.toml
@ -1,11 +1,10 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"examples/example-runner-cpu",
|
||||
"examples/example-runner",
|
||||
"examples/wgpu-example-runner",
|
||||
"examples/example-shader",
|
||||
"examples/wgpu-example-shader",
|
||||
|
||||
"examples/runners/cpu",
|
||||
"examples/runners/ash",
|
||||
"examples/runners/wgpu",
|
||||
"examples/shaders/sky-shader",
|
||||
"examples/shaders/simplest-shader",
|
||||
"rustc_codegen_spirv",
|
||||
"spirv-builder",
|
||||
"spirv-std",
|
||||
|
@ -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
|
||||
|
||||
|
@ -37,9 +37,9 @@ skip = [
|
||||
# by default infinite
|
||||
skip-tree = [
|
||||
# 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 = "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 |
@ -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. 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:
|
||||
|
||||
```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)!
|
||||
|
||||
@ -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.
|
||||
|
||||
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.
|
||||
|
||||
|
@ -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(())
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "example-runner"
|
||||
name = "example-runner-ash"
|
||||
version = "0.1.0"
|
||||
authors = ["Embark <opensource@embark-studios.com>"]
|
||||
edition = "2018"
|
||||
@ -22,4 +22,4 @@ winit = "0.23.0"
|
||||
ash-molten = { git = "https://github.com/EmbarkStudios/ash-molten", branch = "moltenvk-1.1.0" }
|
||||
|
||||
[build-dependencies]
|
||||
spirv-builder = { path = "../../spirv-builder", default-features = false }
|
||||
spirv-builder = { path = "../../../spirv-builder", default-features = false }
|
8
examples/runners/ash/build.rs
Normal file
8
examples/runners/ash/build.rs
Normal 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(())
|
||||
}
|
@ -14,8 +14,6 @@ use std::borrow::Cow;
|
||||
use std::default::Default;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::io::Cursor;
|
||||
use std::mem;
|
||||
use std::mem::align_of;
|
||||
use std::ops::Drop;
|
||||
use structopt::StructOpt;
|
||||
|
||||
@ -188,10 +186,6 @@ pub struct ExampleBase {
|
||||
pub draw_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 rendering_complete_semaphore: vk::Semaphore,
|
||||
|
||||
@ -367,16 +361,18 @@ impl ExampleBase {
|
||||
let surface_formats = surface_loader
|
||||
.get_physical_device_surface_formats(pdevice, surface)
|
||||
.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()
|
||||
.map(|sfmt| match sfmt.format {
|
||||
vk::Format::UNDEFINED => vk::SurfaceFormatKHR {
|
||||
format: vk::Format::B8G8R8_UNORM,
|
||||
color_space: sfmt.color_space,
|
||||
},
|
||||
_ => *sfmt,
|
||||
})
|
||||
.next()
|
||||
.find(|sfmt| acceptable_formats.contains(&sfmt.format))
|
||||
.expect("Unable to find suitable surface format.");
|
||||
let surface_capabilities = surface_loader
|
||||
.get_physical_device_surface_capabilities(pdevice, surface)
|
||||
@ -472,41 +468,6 @@ impl ExampleBase {
|
||||
})
|
||||
.collect();
|
||||
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 =
|
||||
vk::FenceCreateInfo::builder().flags(vk::FenceCreateFlags::SIGNALED);
|
||||
@ -518,59 +479,6 @@ impl ExampleBase {
|
||||
.create_fence(&fence_create_info, None)
|
||||
.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 present_complete_semaphore = device
|
||||
@ -599,8 +507,6 @@ impl ExampleBase {
|
||||
pool,
|
||||
draw_command_buffer,
|
||||
setup_command_buffer,
|
||||
depth_image,
|
||||
depth_image_view,
|
||||
present_complete_semaphore,
|
||||
rendering_complete_semaphore,
|
||||
draw_commands_reuse_fence,
|
||||
@ -608,7 +514,6 @@ impl ExampleBase {
|
||||
surface,
|
||||
debug_call_back,
|
||||
debug_utils_loader,
|
||||
depth_image_memory,
|
||||
};
|
||||
(result, events_loop)
|
||||
}
|
||||
@ -627,9 +532,6 @@ impl Drop for ExampleBase {
|
||||
.destroy_fence(self.draw_commands_reuse_fence, None);
|
||||
self.device
|
||||
.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() {
|
||||
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)]
|
||||
#[structopt()]
|
||||
pub struct Options {
|
||||
@ -669,32 +565,18 @@ fn main() {
|
||||
|
||||
unsafe {
|
||||
let (base, events_loop) = ExampleBase::new(1920, 1080, &options);
|
||||
let renderpass_attachments = [
|
||||
vk::AttachmentDescription {
|
||||
format: base.surface_format.format,
|
||||
samples: vk::SampleCountFlags::TYPE_1,
|
||||
load_op: vk::AttachmentLoadOp::CLEAR,
|
||||
store_op: vk::AttachmentStoreOp::STORE,
|
||||
final_layout: vk::ImageLayout::PRESENT_SRC_KHR,
|
||||
..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 renderpass_attachments = [vk::AttachmentDescription {
|
||||
format: base.surface_format.format,
|
||||
samples: vk::SampleCountFlags::TYPE_1,
|
||||
load_op: vk::AttachmentLoadOp::CLEAR,
|
||||
store_op: vk::AttachmentStoreOp::STORE,
|
||||
final_layout: vk::ImageLayout::PRESENT_SRC_KHR,
|
||||
..Default::default()
|
||||
}];
|
||||
let color_attachment_refs = [vk::AttachmentReference {
|
||||
attachment: 0,
|
||||
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 {
|
||||
src_subpass: vk::SUBPASS_EXTERNAL,
|
||||
src_stage_mask: vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
|
||||
@ -706,7 +588,6 @@ fn main() {
|
||||
|
||||
let subpasses = [vk::SubpassDescription::builder()
|
||||
.color_attachments(&color_attachment_refs)
|
||||
.depth_stencil_attachment(&depth_attachment_ref)
|
||||
.pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS)
|
||||
.build()];
|
||||
|
||||
@ -724,7 +605,7 @@ fn main() {
|
||||
.present_image_views
|
||||
.iter()
|
||||
.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()
|
||||
.render_pass(renderpass)
|
||||
.attachments(&framebuffer_attachments)
|
||||
@ -738,124 +619,7 @@ fn main() {
|
||||
})
|
||||
.collect();
|
||||
|
||||
let index_buffer_data = [0u32, 1, 2, 1, 2, 3];
|
||||
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 mut spv_file = Cursor::new(&include_bytes!(env!("sky_shader.spv"))[..]);
|
||||
let code = read_spv(&mut spv_file).expect("Failed to read spv file");
|
||||
let shader_info = vk::ShaderModuleCreateInfo::builder().code(&code);
|
||||
|
||||
@ -888,31 +652,10 @@ fn main() {
|
||||
..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 {
|
||||
vertex_attribute_description_count: vertex_input_attribute_descriptions.len() as u32,
|
||||
p_vertex_attribute_descriptions: vertex_input_attribute_descriptions.as_ptr(),
|
||||
vertex_binding_description_count: vertex_input_binding_descriptions.len() as u32,
|
||||
p_vertex_binding_descriptions: vertex_input_binding_descriptions.as_ptr(),
|
||||
vertex_attribute_description_count: 0,
|
||||
vertex_binding_description_count: 0,
|
||||
..Default::default()
|
||||
};
|
||||
let vertex_input_assembly_state_info = vk::PipelineInputAssemblyStateCreateInfo {
|
||||
@ -921,9 +664,9 @@ fn main() {
|
||||
};
|
||||
let viewports = [vk::Viewport {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
y: base.surface_resolution.height 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,
|
||||
max_depth: 1.0,
|
||||
}];
|
||||
@ -953,9 +696,9 @@ fn main() {
|
||||
..Default::default()
|
||||
};
|
||||
let depth_state_info = vk::PipelineDepthStencilStateCreateInfo {
|
||||
depth_test_enable: 1,
|
||||
depth_write_enable: 1,
|
||||
depth_compare_op: vk::CompareOp::LESS_OR_EQUAL,
|
||||
depth_test_enable: 0,
|
||||
depth_write_enable: 0,
|
||||
depth_compare_op: vk::CompareOp::ALWAYS,
|
||||
front: noop_stencil_state,
|
||||
back: noop_stencil_state,
|
||||
max_depth_bounds: 1.0,
|
||||
@ -1013,19 +756,11 @@ fn main() {
|
||||
vk::Fence::null(),
|
||||
)
|
||||
.unwrap();
|
||||
let clear_values = [
|
||||
vk::ClearValue {
|
||||
color: vk::ClearColorValue {
|
||||
float32: [0.0, 0.0, 0.0, 0.0],
|
||||
},
|
||||
let clear_values = [vk::ClearValue {
|
||||
color: vk::ClearColorValue {
|
||||
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()
|
||||
.render_pass(renderpass)
|
||||
@ -1057,26 +792,7 @@ fn main() {
|
||||
);
|
||||
device.cmd_set_viewport(draw_command_buffer, 0, &viewports);
|
||||
device.cmd_set_scissor(draw_command_buffer, 0, &scissors);
|
||||
device.cmd_bind_vertex_buffers(
|
||||
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,
|
||||
);
|
||||
device.cmd_draw(draw_command_buffer, 3, 1, 0, 0);
|
||||
// Or draw without the index buffer
|
||||
// device.cmd_draw(draw_command_buffer, 3, 1, 0, 0);
|
||||
device.cmd_end_render_pass(draw_command_buffer);
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
[dependencies]
|
||||
minifb = "0.19.1"
|
||||
# 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 }
|
||||
|
||||
# for parallelism, not really needed though
|
@ -3,13 +3,22 @@ use minifb::{Key, Window, WindowOptions};
|
||||
use rayon::prelude::*;
|
||||
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 {
|
||||
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,
|
||||
// but currently our shader output is in sRGB so no conversion needed
|
||||
|
||||
convert(v.z()) | convert(v.y()) << 8 | convert(v.x()) << 16 | convert(v.w()) << 24
|
||||
convert(srgb_oetf(v.z()))
|
||||
| convert(srgb_oetf(v.y())) << 8
|
||||
| convert(srgb_oetf(v.x())) << 16
|
||||
| convert(v.w()) << 24
|
||||
}
|
||||
|
||||
fn main() {
|
||||
@ -34,11 +43,11 @@ fn main() {
|
||||
.map(|i| {
|
||||
let screen_pos = vec2(
|
||||
(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
|
||||
let color = example_shader::fs(screen_pos);
|
||||
let color = sky_shader::fs(screen_pos);
|
||||
|
||||
color_u32_from_vec4(color)
|
||||
})
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "wgpu-example-runner"
|
||||
name = "example-runner-wgpu"
|
||||
version = "0.1.0"
|
||||
authors = ["Embark <opensource@embark-studios.com>"]
|
||||
edition = "2018"
|
||||
@ -14,10 +14,10 @@ use-compiled-tools = ["spirv-builder/use-compiled-tools"]
|
||||
[dependencies]
|
||||
wgpu = "0.6.0"
|
||||
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]
|
||||
spirv-builder = { path = "../../spirv-builder", default-features = false }
|
||||
spirv-builder = { path = "../../../spirv-builder", default-features = false }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
wgpu-subscriber = "0.1.0"
|
@ -2,8 +2,8 @@ use spirv_builder::SpirvBuilder;
|
||||
use std::error::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
|
||||
SpirvBuilder::new("../wgpu-example-shader")
|
||||
// This will set the env var `sky-shader.spv` to a spir-v file that can be include!()'d
|
||||
SpirvBuilder::new("../../shaders/sky-shader")
|
||||
.spirv_version(1, 0)
|
||||
.build()?;
|
||||
Ok(())
|
@ -31,7 +31,7 @@ async fn run(event_loop: EventLoop<()>, window: Window, swapchain_format: wgpu::
|
||||
.expect("Failed to create device");
|
||||
|
||||
// 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 {
|
||||
label: None,
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "wgpu-example-shader"
|
||||
name = "simplest-shader"
|
||||
version = "0.1.0"
|
||||
authors = ["Embark <opensource@embark-studios.com>"]
|
||||
edition = "2018"
|
||||
@ -9,5 +9,5 @@ license = "MIT OR Apache-2.0"
|
||||
crate-type = ["dylib"]
|
||||
|
||||
[dependencies]
|
||||
spirv-std = { path = "../../spirv-std" }
|
||||
glam = { version = "0.9", default_features = false }
|
||||
spirv-std = { path = "../../../spirv-std" }
|
||||
glam = { version = "0.9", default_features = false }
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "example-shader"
|
||||
name = "sky-shader"
|
||||
version = "0.1.0"
|
||||
authors = ["Embark <opensource@embark-studios.com>"]
|
||||
edition = "2018"
|
||||
@ -9,5 +9,5 @@ license = "MIT OR Apache-2.0"
|
||||
crate-type = ["dylib"]
|
||||
|
||||
[dependencies]
|
||||
spirv-std = { path = "../../spirv-std" }
|
||||
glam = { version = "0.9", default_features = false }
|
||||
spirv-std = { path = "../../../spirv-std" }
|
||||
glam = { version = "0.9", default_features = false }
|
@ -10,7 +10,6 @@ use glam::{const_vec3, Mat4, Vec2, Vec3, Vec4};
|
||||
use spirv_std::{Input, MathExt, Output};
|
||||
|
||||
const DEPOLARIZATION_FACTOR: f32 = 0.035;
|
||||
const LUMINANCE: f32 = 1.0;
|
||||
const MIE_COEFFICIENT: f32 = 0.005;
|
||||
const MIE_DIRECTIONAL_G: f32 = 0.8;
|
||||
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_INTENSITY_FACTOR: f32 = 1000.0;
|
||||
const SUN_INTENSITY_FALLOFF_STEEPNESS: f32 = 1.5;
|
||||
const TONEMAP_WEIGHTING: Vec3 = const_vec3!([9.50; 3]);
|
||||
const TURBIDITY: f32 = 2.0;
|
||||
|
||||
// TODO: add this to glam? Rust std has it on f32/f64
|
||||
fn pow(v: Vec3, power: f32) -> Vec3 {
|
||||
let v: [f32; 3] = v.into();
|
||||
Vec3::new(v[0].pow(power), v[1].pow(power), v[2].pow(power))
|
||||
Vec3::new(v.x().pow(power), v.y().pow(power), v.z().pow(power))
|
||||
}
|
||||
|
||||
// TODO: add this to glam? Rust std has it on f32/f64
|
||||
fn exp(v: Vec3) -> Vec3 {
|
||||
let v: [f32; 3] = v.into();
|
||||
Vec3::new(v[0].exp(), v[1].exp(), v[2].exp())
|
||||
Vec3::new(v.x().exp(), v.y().exp(), v.z().exp())
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
const A: Vec3 = const_vec3!([0.15; 3]); // Shoulder strength
|
||||
const B: Vec3 = const_vec3!([0.50; 3]); // Linear strength
|
||||
const C: Vec3 = const_vec3!([0.10; 3]); // Linear angle
|
||||
const D: Vec3 = const_vec3!([0.20; 3]); // Toe strength
|
||||
const E: Vec3 = const_vec3!([0.02; 3]); // Toe numerator
|
||||
const F: Vec3 = const_vec3!([0.30; 3]); // Toe denominator
|
||||
fn tonemap(col: Vec3) -> Vec3 {
|
||||
// see https://www.desmos.com/calculator/0eo9pzo1at
|
||||
const A: f32 = 2.35;
|
||||
const B: f32 = 2.8826666;
|
||||
const C: f32 = 789.7459;
|
||||
const D: f32 = 0.935;
|
||||
|
||||
((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 {
|
||||
@ -149,16 +145,8 @@ fn sky(dir: Vec3, sun_position: Vec3) -> Vec3 {
|
||||
);
|
||||
let mut l0 = 0.1 * fex;
|
||||
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
|
||||
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)))
|
||||
lin + l0
|
||||
}
|
||||
|
||||
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),
|
||||
);
|
||||
|
||||
let cs_pos = Vec4::new(screen_pos.x(), -screen_pos.y(), 1.0, 1.0);
|
||||
let ws_pos = {
|
||||
let p = clip_to_world.mul_vec4(cs_pos);
|
||||
let clip_pos = screen_pos.extend(1.0).extend(1.0);
|
||||
let world_pos = {
|
||||
let p = clip_to_world.mul_vec4(clip_pos);
|
||||
p.truncate() / p.w()
|
||||
};
|
||||
let dir = (ws_pos - eye_pos).normalize();
|
||||
let dir = (world_pos - eye_pos).normalize();
|
||||
|
||||
// evaluate Preetham sky model
|
||||
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)]
|
||||
#[spirv(fragment)]
|
||||
pub fn main_fs(input: Input<Vec4>, mut output: Output<Vec4>) {
|
||||
let v = input.load();
|
||||
let color = fs(Vec2::new(v.x(), v.y()));
|
||||
output.store(color)
|
||||
pub fn main_fs(in_pos: Input<Vec2>, mut output: Output<Vec4>) {
|
||||
let color = fs(in_pos.load());
|
||||
output.store(color);
|
||||
}
|
||||
|
||||
#[allow(unused_attributes)]
|
||||
#[spirv(vertex)]
|
||||
pub fn main_vs(
|
||||
in_pos: Input<Vec4>,
|
||||
_in_color: Input<Vec4>,
|
||||
#[spirv(position)] mut out_pos: Output<Vec4>,
|
||||
mut out_color: Output<Vec4>,
|
||||
#[spirv(vertex_index)] vert_idx: Input<i32>,
|
||||
#[spirv(position)] mut builtin_pos: Output<Vec4>,
|
||||
mut out_pos: Output<Vec2>,
|
||||
) {
|
||||
out_pos.store(in_pos.load());
|
||||
out_color.store(in_pos.load());
|
||||
let vert_idx = vert_idx.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"))]
|
Loading…
Reference in New Issue
Block a user