From ba8c618b9ba032b445685e962b6f2b0d6760c203 Mon Sep 17 00:00:00 2001 From: John Wells Date: Fri, 13 Feb 2026 09:27:23 -0500 Subject: [PATCH 01/86] rename --- CONTRIBUTING.md | 2 +- Cargo.toml | 22 +- README.md | 39 ++- contrib/.vscode/launch.json | 8 +- contrib/.vscode/tasks.json | 2 +- contrib/README.md | 18 +- contrib/rel-mgmt/check | 40 +-- contrib/rel-mgmt/run-all-examples | 16 +- contrib/screen-13-fx/README.md | 3 - .../Cargo.toml | 6 +- .../README.md | 4 +- .../shaders/frag.glsl | 0 .../shaders/vert.glsl | 0 .../src/lib.rs | 2 +- .../{screen-13-fx => vk-graph-fx}/Cargo.toml | 12 +- contrib/vk-graph-fx/README.md | 3 + .../shader/compute/decode_bitmap_r_rg.comp | 0 .../compute/decode_bitmap_rgb_rgba.comp | 0 .../res/shader/compute/present1.comp | 0 .../res/shader/compute/present2.comp | 0 .../res/shader/graphic/font.frag | 0 .../res/shader/graphic/font.vert | 0 .../res/shader/graphic/present.frag | 0 .../res/shader/graphic/present.vert | 0 .../res/shader/inc/catmull_rom.glsl | 0 .../res/shader/inc/color_space.glsl | 0 .../res/shader/inc/content.glsl | 0 .../res/shader/inc/quad.glsl | 0 .../res/shader/transition/_defs.glsl | 0 .../res/shader/transition/_main.glsl | 0 .../res/shader/transition/angular.comp | 0 .../res/shader/transition/bounce.comp | 0 .../shader/transition/bow_tie_horizontal.comp | 0 .../shader/transition/bow_tie_vertical.comp | 0 .../transition/bow_tie_with_parameter.comp | 0 .../res/shader/transition/burn.comp | 0 .../transition/butterfly_wave_scrawler.comp | 0 .../res/shader/transition/cannabis_leaf.comp | 0 .../res/shader/transition/circle.comp | 0 .../res/shader/transition/circle_crop.comp | 0 .../res/shader/transition/circle_open.comp | 0 .../res/shader/transition/color_distance.comp | 0 .../res/shader/transition/color_phase.comp | 0 .../res/shader/transition/coord_from_in.comp | 0 .../transition/crazy_parametric_fun.comp | 0 .../res/shader/transition/cross_warp.comp | 0 .../res/shader/transition/cross_zoom.comp | 0 .../res/shader/transition/crosshatch.comp | 0 .../res/shader/transition/cube.comp | 0 .../res/shader/transition/directional.comp | 0 .../shader/transition/directional_easing.comp | 0 .../shader/transition/directional_warp.comp | 0 .../shader/transition/directional_wipe.comp | 0 .../res/shader/transition/displacement.comp | 0 .../res/shader/transition/doom_screen.comp | 0 .../res/shader/transition/doorway.comp | 0 .../res/shader/transition/dreamy.comp | 0 .../res/shader/transition/dreamy_zoom.comp | 0 .../res/shader/transition/fade.comp | 0 .../res/shader/transition/fade_color.comp | 0 .../res/shader/transition/fade_grayscale.comp | 0 .../res/shader/transition/film_burn.comp | 0 .../res/shader/transition/flyeye.comp | 0 .../shader/transition/glitch_displace.comp | 0 .../shader/transition/glitch_memories.comp | 0 .../res/shader/transition/grid_flip.comp | 0 .../res/shader/transition/heart.comp | 0 .../res/shader/transition/hexagonalize.comp | 0 .../shader/transition/inverted_page_curl.comp | 0 .../res/shader/transition/kaleidoscope.comp | 0 .../res/shader/transition/left_right.comp | 0 .../res/shader/transition/linear_blur.comp | 0 .../res/shader/transition/luma.comp | 0 .../res/shader/transition/luminance_melt.comp | 0 .../res/shader/transition/morph.comp | 0 .../res/shader/transition/mosaic.comp | 0 .../res/shader/transition/multiply.comp | 0 .../res/shader/transition/overexposure.comp | 0 .../res/shader/transition/perlin.comp | 0 .../res/shader/transition/pinwheel.comp | 0 .../res/shader/transition/pixelize.comp | 0 .../res/shader/transition/polar_function.comp | 0 .../shader/transition/polka_dots_curtain.comp | 0 .../res/shader/transition/power_kaleido.comp | 0 .../res/shader/transition/radial.comp | 0 .../res/shader/transition/random_noisex.comp | 0 .../res/shader/transition/random_squares.comp | 0 .../res/shader/transition/ripple.comp | 0 .../res/shader/transition/rotate.comp | 0 .../res/shader/transition/rotate_scale.comp | 0 .../res/shader/transition/scale_in.comp | 0 .../res/shader/transition/simple_zoom.comp | 0 .../res/shader/transition/squares_wire.comp | 0 .../res/shader/transition/squeeze.comp | 0 .../res/shader/transition/stereo_viewer.comp | 0 .../res/shader/transition/swap.comp | 0 .../res/shader/transition/swirl.comp | 0 .../transition/tangent_motion_blur.comp | 0 .../res/shader/transition/top_bottom.comp | 0 .../res/shader/transition/tv_static.comp | 0 .../transition/undulating_burn_out.comp | 0 .../res/shader/transition/water_drop.comp | 0 .../res/shader/transition/wind.comp | 0 .../res/shader/transition/window_blinds.comp | 0 .../res/shader/transition/window_slice.comp | 0 .../res/shader/transition/wipe_down.comp | 0 .../res/shader/transition/wipe_left.comp | 0 .../res/shader/transition/wipe_right.comp | 0 .../res/shader/transition/wipe_up.comp | 0 .../shader/transition/zoom_in_circles.comp | 0 .../res/shader/transition/zoom_left_wipe.comp | 0 .../shader/transition/zoom_right_wipe.comp | 0 .../src/bitmap_font.rs | 2 +- .../src/image_loader.rs | 2 +- .../{screen-13-fx => vk-graph-fx}/src/lib.rs | 0 .../src/presenter.rs | 2 +- .../src/transition.rs | 2 +- .../.github/img/noise.png | Bin .../Cargo.toml | 12 +- .../{screen-13-hot => vk-graph-hot}/README.md | 8 +- .../examples/README.md | 4 +- .../examples/glsl.rs | 6 +- .../examples/hlsl.rs | 6 +- .../examples/res/fill_image.comp | 0 .../examples/res/fill_image.hlsl | 0 .../examples/res/noise.glsl | 0 .../examples/res/noise.hlsl | 0 .../src/compute.rs | 2 +- .../src/graphic.rs | 2 +- .../src/lib.rs | 2 +- .../src/ray_trace.rs | 2 +- .../src/shader.rs | 10 +- contrib/vk-graph-imgui/Cargo.toml | 14 + .../README.md | 4 +- .../res/font/mplus-1p/LICENSE | 0 .../res/font/mplus-1p/mplus-1p-regular.ttf | Bin .../res/font/roboto/LICENSE | 0 .../res/font/roboto/roboto-regular.ttf | Bin .../res/shader/imgui.frag | 0 .../res/shader/imgui.vert | 0 contrib/vk-graph-imgui/src/lib.rs | 290 ++++++++++++++++++ .../Cargo.toml | 4 +- .../README.md | 2 +- .../examples/hello_world.rs | 2 +- .../src/frame.rs | 2 +- .../src/lib.rs | 2 +- examples/README.md | 10 +- examples/aliasing.rs | 4 +- examples/app.rs | 6 +- examples/bindless.rs | 4 +- examples/cpu_readback.rs | 2 +- examples/debugger.rs | 14 +- examples/egui.rs | 4 +- examples/font_bmp.rs | 10 +- examples/fuzzer.rs | 10 +- examples/getting-started.md | 12 +- examples/image_sampler.rs | 6 +- examples/imgui.rs | 10 +- examples/min_max.rs | 2 +- examples/mip_compute.rs | 4 +- examples/mip_graphic.rs | 4 +- examples/msaa.rs | 4 +- examples/multipass.rs | 4 +- examples/multithread.rs | 6 +- examples/profile_with_puffin/mod.rs | 2 +- examples/ray_omni.rs | 4 +- examples/ray_trace.rs | 4 +- examples/rt_triangle.rs | 4 +- examples/shader-toy/Cargo.toml | 6 +- examples/shader-toy/src/main.rs | 6 +- examples/skeletal-anim/Cargo.toml | 4 +- examples/skeletal-anim/README.md | 4 +- examples/skeletal-anim/src/main.rs | 6 +- examples/subgroup_ops.rs | 2 +- examples/transitions.rs | 10 +- examples/triangle.rs | 4 +- examples/vertex_layout.rs | 4 +- examples/vr/Cargo.toml | 4 +- examples/vr/src/driver/instance.rs | 22 +- examples/vr/src/driver/swapchain.rs | 8 +- examples/vr/src/main.rs | 26 +- examples/vsm_omni.rs | 4 +- src/driver/accel_struct.rs | 32 +- src/driver/buffer.rs | 50 +-- src/driver/compute.rs | 8 +- src/driver/graphic.rs | 8 +- src/driver/image.rs | 20 +- src/driver/mod.rs | 4 +- src/driver/ray_trace.rs | 10 +- src/driver/shader.rs | 6 +- src/graph/pass_ref.rs | 216 ++++++------- src/lib.rs | 74 ++--- src/pool/alias.rs | 2 +- src/pool/mod.rs | 14 +- 194 files changed, 773 insertions(+), 470 deletions(-) delete mode 100644 contrib/screen-13-fx/README.md rename contrib/{screen-13-egui => vk-graph-egui}/Cargo.toml (84%) rename contrib/{screen-13-egui => vk-graph-egui}/README.md (51%) rename contrib/{screen-13-egui => vk-graph-egui}/shaders/frag.glsl (100%) rename contrib/{screen-13-egui => vk-graph-egui}/shaders/vert.glsl (100%) rename contrib/{screen-13-egui => vk-graph-egui}/src/lib.rs (99%) rename contrib/{screen-13-fx => vk-graph-fx}/Cargo.toml (71%) create mode 100644 contrib/vk-graph-fx/README.md rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/compute/decode_bitmap_r_rg.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/compute/decode_bitmap_rgb_rgba.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/compute/present1.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/compute/present2.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/graphic/font.frag (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/graphic/font.vert (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/graphic/present.frag (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/graphic/present.vert (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/inc/catmull_rom.glsl (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/inc/color_space.glsl (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/inc/content.glsl (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/inc/quad.glsl (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/_defs.glsl (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/_main.glsl (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/angular.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/bounce.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/bow_tie_horizontal.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/bow_tie_vertical.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/bow_tie_with_parameter.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/burn.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/butterfly_wave_scrawler.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/cannabis_leaf.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/circle.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/circle_crop.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/circle_open.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/color_distance.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/color_phase.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/coord_from_in.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/crazy_parametric_fun.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/cross_warp.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/cross_zoom.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/crosshatch.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/cube.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/directional.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/directional_easing.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/directional_warp.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/directional_wipe.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/displacement.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/doom_screen.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/doorway.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/dreamy.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/dreamy_zoom.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/fade.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/fade_color.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/fade_grayscale.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/film_burn.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/flyeye.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/glitch_displace.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/glitch_memories.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/grid_flip.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/heart.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/hexagonalize.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/inverted_page_curl.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/kaleidoscope.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/left_right.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/linear_blur.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/luma.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/luminance_melt.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/morph.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/mosaic.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/multiply.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/overexposure.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/perlin.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/pinwheel.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/pixelize.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/polar_function.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/polka_dots_curtain.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/power_kaleido.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/radial.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/random_noisex.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/random_squares.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/ripple.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/rotate.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/rotate_scale.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/scale_in.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/simple_zoom.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/squares_wire.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/squeeze.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/stereo_viewer.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/swap.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/swirl.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/tangent_motion_blur.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/top_bottom.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/tv_static.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/undulating_burn_out.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/water_drop.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/wind.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/window_blinds.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/window_slice.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/wipe_down.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/wipe_left.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/wipe_right.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/wipe_up.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/zoom_in_circles.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/zoom_left_wipe.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/res/shader/transition/zoom_right_wipe.comp (100%) rename contrib/{screen-13-fx => vk-graph-fx}/src/bitmap_font.rs (99%) rename contrib/{screen-13-fx => vk-graph-fx}/src/image_loader.rs (99%) rename contrib/{screen-13-fx => vk-graph-fx}/src/lib.rs (100%) rename contrib/{screen-13-fx => vk-graph-fx}/src/presenter.rs (99%) rename contrib/{screen-13-fx => vk-graph-fx}/src/transition.rs (99%) rename contrib/{screen-13-hot => vk-graph-hot}/.github/img/noise.png (100%) rename contrib/{screen-13-hot => vk-graph-hot}/Cargo.toml (61%) rename contrib/{screen-13-hot => vk-graph-hot}/README.md (85%) rename contrib/{screen-13-hot => vk-graph-hot}/examples/README.md (93%) rename contrib/{screen-13-hot => vk-graph-hot}/examples/glsl.rs (93%) rename contrib/{screen-13-hot => vk-graph-hot}/examples/hlsl.rs (94%) rename contrib/{screen-13-hot => vk-graph-hot}/examples/res/fill_image.comp (100%) rename contrib/{screen-13-hot => vk-graph-hot}/examples/res/fill_image.hlsl (100%) rename contrib/{screen-13-hot => vk-graph-hot}/examples/res/noise.glsl (100%) rename contrib/{screen-13-hot => vk-graph-hot}/examples/res/noise.hlsl (100%) rename contrib/{screen-13-hot => vk-graph-hot}/src/compute.rs (98%) rename contrib/{screen-13-hot => vk-graph-hot}/src/graphic.rs (98%) rename contrib/{screen-13-hot => vk-graph-hot}/src/lib.rs (99%) rename contrib/{screen-13-hot => vk-graph-hot}/src/ray_trace.rs (99%) rename contrib/{screen-13-hot => vk-graph-hot}/src/shader.rs (97%) create mode 100644 contrib/vk-graph-imgui/Cargo.toml rename contrib/{screen-13-imgui => vk-graph-imgui}/README.md (50%) rename contrib/{screen-13-imgui => vk-graph-imgui}/res/font/mplus-1p/LICENSE (100%) rename contrib/{screen-13-imgui => vk-graph-imgui}/res/font/mplus-1p/mplus-1p-regular.ttf (100%) rename contrib/{screen-13-imgui => vk-graph-imgui}/res/font/roboto/LICENSE (100%) rename contrib/{screen-13-imgui => vk-graph-imgui}/res/font/roboto/roboto-regular.ttf (100%) rename contrib/{screen-13-imgui => vk-graph-imgui}/res/shader/imgui.frag (100%) rename contrib/{screen-13-imgui => vk-graph-imgui}/res/shader/imgui.vert (100%) create mode 100644 contrib/vk-graph-imgui/src/lib.rs rename contrib/{screen-13-window => vk-graph-window}/Cargo.toml (81%) rename contrib/{screen-13-window => vk-graph-window}/README.md (87%) rename contrib/{screen-13-window => vk-graph-window}/examples/hello_world.rs (86%) rename contrib/{screen-13-window => vk-graph-window}/src/frame.rs (99%) rename contrib/{screen-13-window => vk-graph-window}/src/lib.rs (99%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ce066eb9..e6fe4cc5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contributing to Screen 13 +# Contributing to vk-graph Thank you for taking the time to look over this document. You are encouraged to open issues, submit PRs, suggest changes, or anything else you feel might move this project forward. diff --git a/Cargo.toml b/Cargo.toml index 6d7cf8fd..deaec9b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,16 +1,16 @@ [package] -name = "screen-13" -version = "0.13.0" +name = "vk-graph" +version = "0.14.0+alpha" authors = ["John Wells "] edition = "2024" license = "MIT OR Apache-2.0" readme = "README.md" -repository = "https://github.com/attackgoat/screen-13" -homepage = "https://github.com/attackgoat/screen-13" -documentation = "https://docs.rs/screen-13" +repository = "https://github.com/attackgoat/vk-graph" +homepage = "https://github.com/attackgoat/vk-graph" +documentation = "https://docs.rs/vk-graph" keywords = ["gamedev", "vulkan"] categories = ["game-development", "multimedia::images", "rendering::engine"] -description = "An easy-to-use Vulkan rendering engine in the spirit of QBasic." +description = "A high-performance Vulkan driver with automatic resource management and execution." [features] default = [] @@ -31,7 +31,7 @@ paste = "1.0" profiling = "1.0" raw-window-handle = "0.6" spirq = "1.2" -vk-sync = { version = "0.5", package = "vk-sync-fork" } # // SEE: https://github.com/gwihlidal/vk-sync-rs/pull/4 -> https://github.com/expenses/vk-sync-rs +vk-sync = { version = "0.5", package = "vk-sync-fork" } # // SEE: https://github.com/gwihlidal/vk-sync-rs/pull/4 -> https://github.com/expenses/vk-sync-rs [target.'cfg(target_os = "macos")'.dependencies] ash-molten = "0.20" @@ -54,10 +54,10 @@ puffin = "0.19" puffin_http = "0.16" rand = "0.9" reqwest = { version = "0.12", features = ["blocking"] } -screen-13-fx = { path = "contrib/screen-13-fx" } -screen-13-imgui = { path = "contrib/screen-13-imgui" } -screen-13-egui = { path = "contrib/screen-13-egui" } -screen-13-window = { path = "contrib/screen-13-window" } +vk-graph-fx = { path = "contrib/vk-graph-fx" } +vk-graph-imgui = { path = "contrib/vk-graph-imgui" } +vk-graph-egui = { path = "contrib/vk-graph-egui" } +vk-graph-window = { path = "contrib/vk-graph-window" } tobj = "4.0" winit = "0.30" winit_input_helper = { git = "https://github.com/stefnotch/winit_input_helper.git", rev = "6e76a79d01ce836c01b9cdeaa98846a6f0955dc4" } #"0.16" diff --git a/README.md b/README.md index 64a1f22a..a8b388f8 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,24 @@ -# Screen 13 +# vk-graph -[![Crates.io](https://img.shields.io/crates/v/screen-13.svg)](https://crates.io/crates/screen-13) -[![Docs.rs](https://docs.rs/screen-13/badge.svg)](https://docs.rs/screen-13) -[![LoC](https://tokei.rs/b1/github/attackgoat/screen-13?category=code)](https://github.com/attackgoat/screen-13) +[![Crates.io](https://img.shields.io/crates/v/vk-graph.svg)](https://crates.io/crates/vk-graph) +[![Docs.rs](https://docs.rs/vk-graph/badge.svg)](https://docs.rs/vk-graph) +[![LoC](https://tokei.rs/b1/github/attackgoat/vk-graph?category=code)](https://github.com/attackgoat/vk-graph) -_Screen 13_ is an easy-to-use Vulkan rendering engine in the spirit of -_[QBasic](https://en.wikipedia.org/wiki/QBasic)_. +_vk-graph_ is a high-performance Vulkan driver with automatic resource management and execution. ```toml [dependencies] -screen-13 = "0.12" +vk-graph = "0.14" ``` ## Overview -_Screen 13_ provides a high performance [Vulkan](https://www.vulkan.org/) driver using smart +_vk-graph_ provides a high performance [Vulkan](https://www.vulkan.org/) driver using smart pointers. The driver may be created manually for headless rendering or automatically using the built-in window abstraction: ```rust -use screen_13_window::{Window, WindowError}; +use vk_graph_window::{Window, WindowError}; fn main() -> Result<(), WindowError> { Window::new()?.run(|frame| { @@ -30,7 +29,7 @@ fn main() -> Result<(), WindowError> { ## Usage -_Screen 13_ provides a fully-generic render graph structure for simple and statically +_vk-graph_ provides a fully-generic render graph structure for simple and statically typed access to all the resources used while rendering. The `RenderGraph` structure allows Vulkan smart pointer resources to be bound as "nodes" which may be used anywhere in a graph. The graph itself is not tied to swapchain access and may be used to execute general command streams. @@ -41,7 +40,7 @@ Features of the render graph: - Automatic Vulkan management (render passes, subpasses, descriptors, pools, _etc._) - Automatic render pass scheduling, re-ordering, merging, with resource aliasing - Interoperable with existing Vulkan code - - Optional [shader hot-reload](contrib/screen-13-hot/README.md) from disk + - Optional [shader hot-reload](contrib/vk-graph-hot/README.md) from disk ```rust render_graph @@ -68,15 +67,15 @@ To enable logging, set the `RUST_LOG` environment variable to `trace`, `debug`, _You may also filter messages, for example:_ ```bash -RUST_LOG=screen_13::driver=trace,screen_13=warn cargo run --example ray_trace +RUST_LOG=vk_graph::driver=trace,vk_graph=warn cargo run --example ray_trace ``` ``` -TRACE screen_13::driver::instance > created a Vulkan instance -DEBUG screen_13::driver::physical_device > physical device: NVIDIA GeForce RTX 3090 -DEBUG screen_13::driver::physical_device > extension "VK_KHR_16bit_storage" v1 -DEBUG screen_13::driver::physical_device > extension "VK_KHR_8bit_storage" v1 -DEBUG screen_13::driver::physical_device > extension "VK_KHR_acceleration_structure" v13 +TRACE vk_graph::driver::instance > created a Vulkan instance +DEBUG vk_graph::driver::physical_device > physical device: NVIDIA GeForce RTX 3090 +DEBUG vk_graph::driver::physical_device > extension "VK_KHR_16bit_storage" v1 +DEBUG vk_graph::driver::physical_device > extension "VK_KHR_8bit_storage" v1 +DEBUG vk_graph::driver::physical_device > extension "VK_KHR_acceleration_structure" v13 ... ``` @@ -100,13 +99,13 @@ cargo run --features profile-with-puffin --release --example vsm_omni Included are some examples you might find helpful: -- [`hello_world.rs`](contrib/screen-13-window/examples/hello_world.rs) — Displays a window on the screen. Please start here. +- [`hello_world.rs`](contrib/vk-graph-window/examples/hello_world.rs) — Displays a window on the screen. Please start here. - [`triangle.rs`](examples/triangle.rs) — Shaders and full setup of index/vertex buffers; < 100 LOC. - [`shader-toy/`](examples/shader-toy) — Recreation of a two-pass shader toy using the original shader code. See the [example code](examples/README.md), -[documentation](https://docs.rs/screen-13/latest/screen_13/), or helpful +[documentation](https://docs.rs/vk-graph/latest/vk_graph/), or helpful [getting started guide](examples/getting-started.md) for more information. **_NOTE:_** Required development packages and libraries are listed in the _getting started guide_. @@ -132,7 +131,7 @@ ability to get things done quickly while using modern tools. ### Inspirations -_Screen 13_ was built from the learnings and lessons shared by others throughout our community. In +_vk-graph_ was built from the learnings and lessons shared by others throughout our community. In particular, here are some of the repositories I found useful: - [Bevy](https://bevyengine.org/): A refreshingly simple data-driven game engine built in Rust diff --git a/contrib/.vscode/launch.json b/contrib/.vscode/launch.json index ff7fedd4..d94b9e66 100644 --- a/contrib/.vscode/launch.json +++ b/contrib/.vscode/launch.json @@ -7,16 +7,16 @@ { "type": "lldb", "request": "launch", - "name": "Debug unit tests in library 'screen-13'", + "name": "Debug unit tests in library 'vk-graph'", "cargo": { "args": [ "test", "--no-run", "--lib", - "--package=screen-13" + "--package=vk-graph" ], "filter": { - "name": "screen-13", + "name": "vk-graph", "kind": "lib" } }, @@ -31,7 +31,7 @@ "args": [ "build", "--example=hello_world", - "--package=screen-13" + "--package=vk-graph" ], "filter": { "name": "hello_world", diff --git a/contrib/.vscode/tasks.json b/contrib/.vscode/tasks.json index 0f79ddf9..ddea6c4c 100644 --- a/contrib/.vscode/tasks.json +++ b/contrib/.vscode/tasks.json @@ -19,7 +19,7 @@ "showReuseMessage": false, "clear": true }, - "label": "Build Screen 13" + "label": "Build vk-graph" } ] } diff --git a/contrib/README.md b/contrib/README.md index 53d4696d..b2ea5a0c 100644 --- a/contrib/README.md +++ b/contrib/README.md @@ -1,12 +1,12 @@ -# User Contributions to Screen 13 +# User Contributions to vk-graph These subdirectories contain additions, changes, and other things you might find useful while -using _Screen 13_. These user-provided contributions are not guaranteed to work and are untested. +using _vk-graph_. These user-provided contributions are not guaranteed to work and are untested. ## [`.vscode/`](.vscode/) Configuration files for users of _[Visual Studio Code](https://code.visualstudio.com/)_. Copy the -`.vscode/` directory into the root _Screen 13_ project directory in order to enable build and debug +`.vscode/` directory into the root _vk-graph_ project directory in order to enable build and debug configurations. **_NOTE:_** Requires installation of the @@ -18,21 +18,21 @@ debugging. A script which exercises all test cases and build conditions which must succeed prior to merging new code into the main branch. -### [`screen-13-egui/`](screen-13-egui/README.md) +### [`vk-graph-egui/`](vk-graph-egui/README.md) Renderer for [egui](https://github.com/emilk/egui); a simple, fast, and highly portable immediate mode GUI library. -### [`screen-13-fx/`](screen-13-fx/README.md) +### [`vk-graph-fx/`](vk-graph-fx/README.md) -Pre-defined effects and tools built using _Screen 13_ features. Generally anything that requires +Pre-defined effects and tools built using _vk-graph_ features. Generally anything that requires shaders or other physical data which shouldn't be part of the main library. -### [`screen-13-hot/`](screen-13-hot/README.md) +### [`vk-graph-hot/`](vk-graph-hot/README.md) Adds a hot-reload feature to compute, graphic and ray-trace shader pipelines. -### [`screen-13-imgui/`](screen-13-imgui/README.md) +### [`vk-graph-imgui/`](vk-graph-imgui/README.md) Renderer for [Dear ImGui](https://github.com/imgui-rs/imgui-rs). Provides a graphical user interface -useful for debug purposes. \ No newline at end of file +useful for debug purposes. diff --git a/contrib/rel-mgmt/check b/contrib/rel-mgmt/check index 2bf3112d..6b8d4e19 100755 --- a/contrib/rel-mgmt/check +++ b/contrib/rel-mgmt/check @@ -16,29 +16,29 @@ diff || fail "Uncommitted changes" # Unformatted rust code cargo fmt && diff || fail "Unformatted rust code" -cargo fmt --manifest-path contrib/screen-13-egui/Cargo.toml && diff || fail "Unformatted rust code (screen-13-egui)" -cargo fmt --manifest-path contrib/screen-13-fx/Cargo.toml && diff || fail "Unformatted rust code (screen-13-fx)" -cargo fmt --manifest-path contrib/screen-13-hot/Cargo.toml && diff || fail "Unformatted rust code (screen-13-hot)" -cargo fmt --manifest-path contrib/screen-13-imgui/Cargo.toml && diff || fail "Unformatted rust code (screen-13-imgui)" +cargo fmt --manifest-path contrib/vk-graph-egui/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-egui)" +cargo fmt --manifest-path contrib/vk-graph-fx/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-fx)" +cargo fmt --manifest-path contrib/vk-graph-hot/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-hot)" +cargo fmt --manifest-path contrib/vk-graph-imgui/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-imgui)" cargo fmt --manifest-path examples/shader-toy/Cargo.toml && diff || fail "Unformatted rust code (shader-toy)" cargo fmt --manifest-path examples/skeletal-anim/Cargo.toml && diff || fail "Unformatted rust code (skeletal-anim)" cargo fmt --manifest-path examples/vr/Cargo.toml && diff || fail "Unformatted rust code (vr)" # Rust code errors -echo "Checking screen-13" +echo "Checking vk-graph" cargo check --all-targets -echo "Checking screen-13 (w/ parking_lot)" +echo "Checking vk-graph (w/ parking_lot)" cargo check --all-targets --features parking_lot -echo "Checking contrib/screen-13-egui" -cargo check --manifest-path contrib/screen-13-egui/Cargo.toml --all-targets --all-features -echo "Checking contrib/screen-13-fx" -cargo check --manifest-path contrib/screen-13-fx/Cargo.toml --all-targets --all-features -echo "Checking contrib/screen-13-hot" -cargo check --manifest-path contrib/screen-13-hot/Cargo.toml --all-targets --all-features -#echo "Checking contrib/screen-13-imgui" -#cargo check --manifest-path contrib/screen-13-imgui/Cargo.toml --all-targets --all-features -echo "Checking contrib/screen-13-window" -cargo check --manifest-path contrib/screen-13-window/Cargo.toml --all-targets --all-features +echo "Checking contrib/vk-graph-egui" +cargo check --manifest-path contrib/vk-graph-egui/Cargo.toml --all-targets --all-features +echo "Checking contrib/vk-graph-fx" +cargo check --manifest-path contrib/vk-graph-fx/Cargo.toml --all-targets --all-features +echo "Checking contrib/vk-graph-hot" +cargo check --manifest-path contrib/vk-graph-hot/Cargo.toml --all-targets --all-features +#echo "Checking contrib/vk-graph-imgui" +#cargo check --manifest-path contrib/vk-graph-imgui/Cargo.toml --all-targets --all-features +echo "Checking contrib/vk-graph-window" +cargo check --manifest-path contrib/vk-graph-window/Cargo.toml --all-targets --all-features echo "Checking examples/shader-toy" cargo check --manifest-path examples/shader-toy/Cargo.toml --all-targets --all-features echo "Checking examples/skeletal-anim" @@ -49,10 +49,10 @@ cargo check --manifest-path examples/vr/Cargo.toml --all-targets --all-features # Rust code lints cargo clippy --all-targets cargo clippy --all-targets --features parking_lot -cargo clippy --manifest-path contrib/screen-13-egui/Cargo.toml --all-targets --all-features -cargo clippy --manifest-path contrib/screen-13-fx/Cargo.toml --all-targets --all-features -cargo clippy --manifest-path contrib/screen-13-hot/Cargo.toml --all-targets --all-features -#cargo clippy --manifest-path contrib/screen-13-imgui/Cargo.toml --all-targets --all-features +cargo clippy --manifest-path contrib/vk-graph-egui/Cargo.toml --all-targets --all-features +cargo clippy --manifest-path contrib/vk-graph-fx/Cargo.toml --all-targets --all-features +cargo clippy --manifest-path contrib/vk-graph-hot/Cargo.toml --all-targets --all-features +#cargo clippy --manifest-path contrib/vk-graph-imgui/Cargo.toml --all-targets --all-features cargo clippy --manifest-path examples/shader-toy/Cargo.toml --all-targets --all-features cargo clippy --manifest-path examples/skeletal-anim/Cargo.toml --all-targets --all-features cargo clippy --manifest-path examples/vr/Cargo.toml --all-targets --all-features diff --git a/contrib/rel-mgmt/run-all-examples b/contrib/rel-mgmt/run-all-examples index 66811a4e..4fa2d722 100755 --- a/contrib/rel-mgmt/run-all-examples +++ b/contrib/rel-mgmt/run-all-examples @@ -4,11 +4,11 @@ set -e # Update everything cargo update -cargo update --manifest-path contrib/screen-13-egui/Cargo.toml -cargo update --manifest-path contrib/screen-13-fx/Cargo.toml -cargo update --manifest-path contrib/screen-13-hot/Cargo.toml -cargo update --manifest-path contrib/screen-13-imgui/Cargo.toml -cargo update --manifest-path contrib/screen-13-window/Cargo.toml +cargo update --manifest-path contrib/vk-graph-egui/Cargo.toml +cargo update --manifest-path contrib/vk-graph-fx/Cargo.toml +cargo update --manifest-path contrib/vk-graph-hot/Cargo.toml +cargo update --manifest-path contrib/vk-graph-imgui/Cargo.toml +cargo update --manifest-path contrib/vk-graph-window/Cargo.toml cargo update --manifest-path examples/skeletal-anim/Cargo.toml cargo update --manifest-path examples/shader-toy/Cargo.toml cargo update --manifest-path examples/vr/Cargo.toml @@ -20,7 +20,7 @@ cargo build --examples cargo run --example fuzzer # Run all regular examples, in debug mode, next -cargo run --manifest-path contrib/screen-13-window/Cargo.toml --example hello_world -- --debug +cargo run --manifest-path contrib/vk-graph-window/Cargo.toml --example hello_world -- --debug cargo run --example app -- --debug cargo run --example aliasing -- --debug cargo run --example cpu_readback -- --debug @@ -50,8 +50,8 @@ cargo run --example ray_omni -- --debug cargo run --manifest-path examples/skeletal-anim/Cargo.toml -- --debug # Hot-reload examples -cargo run --manifest-path contrib/screen-13-hot/Cargo.toml --example glsl -- --debug -cargo run --manifest-path contrib/screen-13-hot/Cargo.toml --example hlsl -- --debug +cargo run --manifest-path contrib/vk-graph-hot/Cargo.toml --example glsl -- --debug +cargo run --manifest-path contrib/vk-graph-hot/Cargo.toml --example hlsl -- --debug # Run this one in release mode cargo run --manifest-path examples/shader-toy/Cargo.toml --release -- --debug diff --git a/contrib/screen-13-fx/README.md b/contrib/screen-13-fx/README.md deleted file mode 100644 index 83e86744..00000000 --- a/contrib/screen-13-fx/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# _Screen 13_ Fx - -A bunch of pre-built things you might need when using _Screen 13_. \ No newline at end of file diff --git a/contrib/screen-13-egui/Cargo.toml b/contrib/vk-graph-egui/Cargo.toml similarity index 84% rename from contrib/screen-13-egui/Cargo.toml rename to contrib/vk-graph-egui/Cargo.toml index b66a5e06..d229dc5b 100644 --- a/contrib/screen-13-egui/Cargo.toml +++ b/contrib/vk-graph-egui/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "screen-13-egui" +name = "vk-graph-egui" version = "0.1.0" authors = ["Christian Döring "] edition = "2021" @@ -12,5 +12,5 @@ bytemuck = "1.14" egui = { git = "https://github.com/emilk/egui.git", rev = "3777b8d2741f298eaa1409dc08062902f7541990" } #{ version = "0.28", features = ["bytemuck"] } egui-winit = { git = "https://github.com/emilk/egui.git", rev = "3777b8d2741f298eaa1409dc08062902f7541990" } #"0.28" inline-spirv = "0.2" -screen-13 = { path = "../.." } -screen-13-fx = { path = "../screen-13-fx" } +vk-graph = { path = "../.." } +vk-graph-fx = { path = "../vk-graph-fx" } diff --git a/contrib/screen-13-egui/README.md b/contrib/vk-graph-egui/README.md similarity index 51% rename from contrib/screen-13-egui/README.md rename to contrib/vk-graph-egui/README.md index 87943ef4..6b5938c3 100644 --- a/contrib/screen-13-egui/README.md +++ b/contrib/vk-graph-egui/README.md @@ -1,5 +1,5 @@ -# _Screen 13_ _egui_ Integration +# _vk-graph_ _egui_ Integration Preview -[Example code](../../examples/README.md) \ No newline at end of file +[Example code](../../examples/README.md) diff --git a/contrib/screen-13-egui/shaders/frag.glsl b/contrib/vk-graph-egui/shaders/frag.glsl similarity index 100% rename from contrib/screen-13-egui/shaders/frag.glsl rename to contrib/vk-graph-egui/shaders/frag.glsl diff --git a/contrib/screen-13-egui/shaders/vert.glsl b/contrib/vk-graph-egui/shaders/vert.glsl similarity index 100% rename from contrib/screen-13-egui/shaders/vert.glsl rename to contrib/vk-graph-egui/shaders/vert.glsl diff --git a/contrib/screen-13-egui/src/lib.rs b/contrib/vk-graph-egui/src/lib.rs similarity index 99% rename from contrib/screen-13-egui/src/lib.rs rename to contrib/vk-graph-egui/src/lib.rs index 90cde08f..d62151ec 100644 --- a/contrib/screen-13-egui/src/lib.rs +++ b/contrib/vk-graph-egui/src/lib.rs @@ -8,8 +8,8 @@ use egui_winit::winit::raw_window_handle::HasDisplayHandle; use { bytemuck::cast_slice, egui_winit::winit::{event::Event, window::Window}, - screen_13::prelude::*, std::{borrow::Cow, collections::HashMap, sync::Arc}, + vk_graph::prelude::*, }; pub struct Egui { diff --git a/contrib/screen-13-fx/Cargo.toml b/contrib/vk-graph-fx/Cargo.toml similarity index 71% rename from contrib/screen-13-fx/Cargo.toml rename to contrib/vk-graph-fx/Cargo.toml index e067d2ae..e96400a2 100644 --- a/contrib/screen-13-fx/Cargo.toml +++ b/contrib/vk-graph-fx/Cargo.toml @@ -1,16 +1,16 @@ [package] -name = "screen-13-fx" +name = "vk-graph-fx" version = "0.1.0" authors = ["John Wells "] edition = "2021" license = "MIT OR Apache-2.0" readme = "README.md" -repository = "https://github.com/attackgoat/screen-13" -homepage = "https://github.com/attackgoat/screen-13/contrib/screen-13-fx" -documentation = "https://docs.rs/screen-13" +repository = "https://github.com/attackgoat/vk-graph" +homepage = "https://github.com/attackgoat/vk-graph/contrib/vk-graph-fx" +documentation = "https://docs.rs/vk-graph" keywords = ["gamedev", "vulkan"] categories = ["game-development", "multimedia::images", "rendering::engine"] -description = "A bunch of pre-built utilities that are helpful when using Screen 13" +description = "A bunch of pre-built utilities that are helpful when using vk-graph" [features] default = [ @@ -28,6 +28,6 @@ bytemuck = "1.14" parking_lot = "0.12" inline-spirv = "0.2" log = "0.4" -screen-13 = { path = "../.."} +vk-graph = { path = "../.."} anyhow = "1.0" glam = "0.27" diff --git a/contrib/vk-graph-fx/README.md b/contrib/vk-graph-fx/README.md new file mode 100644 index 00000000..650a74fa --- /dev/null +++ b/contrib/vk-graph-fx/README.md @@ -0,0 +1,3 @@ +# _vk-graph_ Fx + +A bunch of pre-built things you might need when using _vk-graph_. diff --git a/contrib/screen-13-fx/res/shader/compute/decode_bitmap_r_rg.comp b/contrib/vk-graph-fx/res/shader/compute/decode_bitmap_r_rg.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/compute/decode_bitmap_r_rg.comp rename to contrib/vk-graph-fx/res/shader/compute/decode_bitmap_r_rg.comp diff --git a/contrib/screen-13-fx/res/shader/compute/decode_bitmap_rgb_rgba.comp b/contrib/vk-graph-fx/res/shader/compute/decode_bitmap_rgb_rgba.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/compute/decode_bitmap_rgb_rgba.comp rename to contrib/vk-graph-fx/res/shader/compute/decode_bitmap_rgb_rgba.comp diff --git a/contrib/screen-13-fx/res/shader/compute/present1.comp b/contrib/vk-graph-fx/res/shader/compute/present1.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/compute/present1.comp rename to contrib/vk-graph-fx/res/shader/compute/present1.comp diff --git a/contrib/screen-13-fx/res/shader/compute/present2.comp b/contrib/vk-graph-fx/res/shader/compute/present2.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/compute/present2.comp rename to contrib/vk-graph-fx/res/shader/compute/present2.comp diff --git a/contrib/screen-13-fx/res/shader/graphic/font.frag b/contrib/vk-graph-fx/res/shader/graphic/font.frag similarity index 100% rename from contrib/screen-13-fx/res/shader/graphic/font.frag rename to contrib/vk-graph-fx/res/shader/graphic/font.frag diff --git a/contrib/screen-13-fx/res/shader/graphic/font.vert b/contrib/vk-graph-fx/res/shader/graphic/font.vert similarity index 100% rename from contrib/screen-13-fx/res/shader/graphic/font.vert rename to contrib/vk-graph-fx/res/shader/graphic/font.vert diff --git a/contrib/screen-13-fx/res/shader/graphic/present.frag b/contrib/vk-graph-fx/res/shader/graphic/present.frag similarity index 100% rename from contrib/screen-13-fx/res/shader/graphic/present.frag rename to contrib/vk-graph-fx/res/shader/graphic/present.frag diff --git a/contrib/screen-13-fx/res/shader/graphic/present.vert b/contrib/vk-graph-fx/res/shader/graphic/present.vert similarity index 100% rename from contrib/screen-13-fx/res/shader/graphic/present.vert rename to contrib/vk-graph-fx/res/shader/graphic/present.vert diff --git a/contrib/screen-13-fx/res/shader/inc/catmull_rom.glsl b/contrib/vk-graph-fx/res/shader/inc/catmull_rom.glsl similarity index 100% rename from contrib/screen-13-fx/res/shader/inc/catmull_rom.glsl rename to contrib/vk-graph-fx/res/shader/inc/catmull_rom.glsl diff --git a/contrib/screen-13-fx/res/shader/inc/color_space.glsl b/contrib/vk-graph-fx/res/shader/inc/color_space.glsl similarity index 100% rename from contrib/screen-13-fx/res/shader/inc/color_space.glsl rename to contrib/vk-graph-fx/res/shader/inc/color_space.glsl diff --git a/contrib/screen-13-fx/res/shader/inc/content.glsl b/contrib/vk-graph-fx/res/shader/inc/content.glsl similarity index 100% rename from contrib/screen-13-fx/res/shader/inc/content.glsl rename to contrib/vk-graph-fx/res/shader/inc/content.glsl diff --git a/contrib/screen-13-fx/res/shader/inc/quad.glsl b/contrib/vk-graph-fx/res/shader/inc/quad.glsl similarity index 100% rename from contrib/screen-13-fx/res/shader/inc/quad.glsl rename to contrib/vk-graph-fx/res/shader/inc/quad.glsl diff --git a/contrib/screen-13-fx/res/shader/transition/_defs.glsl b/contrib/vk-graph-fx/res/shader/transition/_defs.glsl similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/_defs.glsl rename to contrib/vk-graph-fx/res/shader/transition/_defs.glsl diff --git a/contrib/screen-13-fx/res/shader/transition/_main.glsl b/contrib/vk-graph-fx/res/shader/transition/_main.glsl similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/_main.glsl rename to contrib/vk-graph-fx/res/shader/transition/_main.glsl diff --git a/contrib/screen-13-fx/res/shader/transition/angular.comp b/contrib/vk-graph-fx/res/shader/transition/angular.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/angular.comp rename to contrib/vk-graph-fx/res/shader/transition/angular.comp diff --git a/contrib/screen-13-fx/res/shader/transition/bounce.comp b/contrib/vk-graph-fx/res/shader/transition/bounce.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/bounce.comp rename to contrib/vk-graph-fx/res/shader/transition/bounce.comp diff --git a/contrib/screen-13-fx/res/shader/transition/bow_tie_horizontal.comp b/contrib/vk-graph-fx/res/shader/transition/bow_tie_horizontal.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/bow_tie_horizontal.comp rename to contrib/vk-graph-fx/res/shader/transition/bow_tie_horizontal.comp diff --git a/contrib/screen-13-fx/res/shader/transition/bow_tie_vertical.comp b/contrib/vk-graph-fx/res/shader/transition/bow_tie_vertical.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/bow_tie_vertical.comp rename to contrib/vk-graph-fx/res/shader/transition/bow_tie_vertical.comp diff --git a/contrib/screen-13-fx/res/shader/transition/bow_tie_with_parameter.comp b/contrib/vk-graph-fx/res/shader/transition/bow_tie_with_parameter.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/bow_tie_with_parameter.comp rename to contrib/vk-graph-fx/res/shader/transition/bow_tie_with_parameter.comp diff --git a/contrib/screen-13-fx/res/shader/transition/burn.comp b/contrib/vk-graph-fx/res/shader/transition/burn.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/burn.comp rename to contrib/vk-graph-fx/res/shader/transition/burn.comp diff --git a/contrib/screen-13-fx/res/shader/transition/butterfly_wave_scrawler.comp b/contrib/vk-graph-fx/res/shader/transition/butterfly_wave_scrawler.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/butterfly_wave_scrawler.comp rename to contrib/vk-graph-fx/res/shader/transition/butterfly_wave_scrawler.comp diff --git a/contrib/screen-13-fx/res/shader/transition/cannabis_leaf.comp b/contrib/vk-graph-fx/res/shader/transition/cannabis_leaf.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/cannabis_leaf.comp rename to contrib/vk-graph-fx/res/shader/transition/cannabis_leaf.comp diff --git a/contrib/screen-13-fx/res/shader/transition/circle.comp b/contrib/vk-graph-fx/res/shader/transition/circle.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/circle.comp rename to contrib/vk-graph-fx/res/shader/transition/circle.comp diff --git a/contrib/screen-13-fx/res/shader/transition/circle_crop.comp b/contrib/vk-graph-fx/res/shader/transition/circle_crop.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/circle_crop.comp rename to contrib/vk-graph-fx/res/shader/transition/circle_crop.comp diff --git a/contrib/screen-13-fx/res/shader/transition/circle_open.comp b/contrib/vk-graph-fx/res/shader/transition/circle_open.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/circle_open.comp rename to contrib/vk-graph-fx/res/shader/transition/circle_open.comp diff --git a/contrib/screen-13-fx/res/shader/transition/color_distance.comp b/contrib/vk-graph-fx/res/shader/transition/color_distance.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/color_distance.comp rename to contrib/vk-graph-fx/res/shader/transition/color_distance.comp diff --git a/contrib/screen-13-fx/res/shader/transition/color_phase.comp b/contrib/vk-graph-fx/res/shader/transition/color_phase.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/color_phase.comp rename to contrib/vk-graph-fx/res/shader/transition/color_phase.comp diff --git a/contrib/screen-13-fx/res/shader/transition/coord_from_in.comp b/contrib/vk-graph-fx/res/shader/transition/coord_from_in.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/coord_from_in.comp rename to contrib/vk-graph-fx/res/shader/transition/coord_from_in.comp diff --git a/contrib/screen-13-fx/res/shader/transition/crazy_parametric_fun.comp b/contrib/vk-graph-fx/res/shader/transition/crazy_parametric_fun.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/crazy_parametric_fun.comp rename to contrib/vk-graph-fx/res/shader/transition/crazy_parametric_fun.comp diff --git a/contrib/screen-13-fx/res/shader/transition/cross_warp.comp b/contrib/vk-graph-fx/res/shader/transition/cross_warp.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/cross_warp.comp rename to contrib/vk-graph-fx/res/shader/transition/cross_warp.comp diff --git a/contrib/screen-13-fx/res/shader/transition/cross_zoom.comp b/contrib/vk-graph-fx/res/shader/transition/cross_zoom.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/cross_zoom.comp rename to contrib/vk-graph-fx/res/shader/transition/cross_zoom.comp diff --git a/contrib/screen-13-fx/res/shader/transition/crosshatch.comp b/contrib/vk-graph-fx/res/shader/transition/crosshatch.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/crosshatch.comp rename to contrib/vk-graph-fx/res/shader/transition/crosshatch.comp diff --git a/contrib/screen-13-fx/res/shader/transition/cube.comp b/contrib/vk-graph-fx/res/shader/transition/cube.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/cube.comp rename to contrib/vk-graph-fx/res/shader/transition/cube.comp diff --git a/contrib/screen-13-fx/res/shader/transition/directional.comp b/contrib/vk-graph-fx/res/shader/transition/directional.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/directional.comp rename to contrib/vk-graph-fx/res/shader/transition/directional.comp diff --git a/contrib/screen-13-fx/res/shader/transition/directional_easing.comp b/contrib/vk-graph-fx/res/shader/transition/directional_easing.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/directional_easing.comp rename to contrib/vk-graph-fx/res/shader/transition/directional_easing.comp diff --git a/contrib/screen-13-fx/res/shader/transition/directional_warp.comp b/contrib/vk-graph-fx/res/shader/transition/directional_warp.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/directional_warp.comp rename to contrib/vk-graph-fx/res/shader/transition/directional_warp.comp diff --git a/contrib/screen-13-fx/res/shader/transition/directional_wipe.comp b/contrib/vk-graph-fx/res/shader/transition/directional_wipe.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/directional_wipe.comp rename to contrib/vk-graph-fx/res/shader/transition/directional_wipe.comp diff --git a/contrib/screen-13-fx/res/shader/transition/displacement.comp b/contrib/vk-graph-fx/res/shader/transition/displacement.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/displacement.comp rename to contrib/vk-graph-fx/res/shader/transition/displacement.comp diff --git a/contrib/screen-13-fx/res/shader/transition/doom_screen.comp b/contrib/vk-graph-fx/res/shader/transition/doom_screen.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/doom_screen.comp rename to contrib/vk-graph-fx/res/shader/transition/doom_screen.comp diff --git a/contrib/screen-13-fx/res/shader/transition/doorway.comp b/contrib/vk-graph-fx/res/shader/transition/doorway.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/doorway.comp rename to contrib/vk-graph-fx/res/shader/transition/doorway.comp diff --git a/contrib/screen-13-fx/res/shader/transition/dreamy.comp b/contrib/vk-graph-fx/res/shader/transition/dreamy.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/dreamy.comp rename to contrib/vk-graph-fx/res/shader/transition/dreamy.comp diff --git a/contrib/screen-13-fx/res/shader/transition/dreamy_zoom.comp b/contrib/vk-graph-fx/res/shader/transition/dreamy_zoom.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/dreamy_zoom.comp rename to contrib/vk-graph-fx/res/shader/transition/dreamy_zoom.comp diff --git a/contrib/screen-13-fx/res/shader/transition/fade.comp b/contrib/vk-graph-fx/res/shader/transition/fade.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/fade.comp rename to contrib/vk-graph-fx/res/shader/transition/fade.comp diff --git a/contrib/screen-13-fx/res/shader/transition/fade_color.comp b/contrib/vk-graph-fx/res/shader/transition/fade_color.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/fade_color.comp rename to contrib/vk-graph-fx/res/shader/transition/fade_color.comp diff --git a/contrib/screen-13-fx/res/shader/transition/fade_grayscale.comp b/contrib/vk-graph-fx/res/shader/transition/fade_grayscale.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/fade_grayscale.comp rename to contrib/vk-graph-fx/res/shader/transition/fade_grayscale.comp diff --git a/contrib/screen-13-fx/res/shader/transition/film_burn.comp b/contrib/vk-graph-fx/res/shader/transition/film_burn.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/film_burn.comp rename to contrib/vk-graph-fx/res/shader/transition/film_burn.comp diff --git a/contrib/screen-13-fx/res/shader/transition/flyeye.comp b/contrib/vk-graph-fx/res/shader/transition/flyeye.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/flyeye.comp rename to contrib/vk-graph-fx/res/shader/transition/flyeye.comp diff --git a/contrib/screen-13-fx/res/shader/transition/glitch_displace.comp b/contrib/vk-graph-fx/res/shader/transition/glitch_displace.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/glitch_displace.comp rename to contrib/vk-graph-fx/res/shader/transition/glitch_displace.comp diff --git a/contrib/screen-13-fx/res/shader/transition/glitch_memories.comp b/contrib/vk-graph-fx/res/shader/transition/glitch_memories.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/glitch_memories.comp rename to contrib/vk-graph-fx/res/shader/transition/glitch_memories.comp diff --git a/contrib/screen-13-fx/res/shader/transition/grid_flip.comp b/contrib/vk-graph-fx/res/shader/transition/grid_flip.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/grid_flip.comp rename to contrib/vk-graph-fx/res/shader/transition/grid_flip.comp diff --git a/contrib/screen-13-fx/res/shader/transition/heart.comp b/contrib/vk-graph-fx/res/shader/transition/heart.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/heart.comp rename to contrib/vk-graph-fx/res/shader/transition/heart.comp diff --git a/contrib/screen-13-fx/res/shader/transition/hexagonalize.comp b/contrib/vk-graph-fx/res/shader/transition/hexagonalize.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/hexagonalize.comp rename to contrib/vk-graph-fx/res/shader/transition/hexagonalize.comp diff --git a/contrib/screen-13-fx/res/shader/transition/inverted_page_curl.comp b/contrib/vk-graph-fx/res/shader/transition/inverted_page_curl.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/inverted_page_curl.comp rename to contrib/vk-graph-fx/res/shader/transition/inverted_page_curl.comp diff --git a/contrib/screen-13-fx/res/shader/transition/kaleidoscope.comp b/contrib/vk-graph-fx/res/shader/transition/kaleidoscope.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/kaleidoscope.comp rename to contrib/vk-graph-fx/res/shader/transition/kaleidoscope.comp diff --git a/contrib/screen-13-fx/res/shader/transition/left_right.comp b/contrib/vk-graph-fx/res/shader/transition/left_right.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/left_right.comp rename to contrib/vk-graph-fx/res/shader/transition/left_right.comp diff --git a/contrib/screen-13-fx/res/shader/transition/linear_blur.comp b/contrib/vk-graph-fx/res/shader/transition/linear_blur.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/linear_blur.comp rename to contrib/vk-graph-fx/res/shader/transition/linear_blur.comp diff --git a/contrib/screen-13-fx/res/shader/transition/luma.comp b/contrib/vk-graph-fx/res/shader/transition/luma.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/luma.comp rename to contrib/vk-graph-fx/res/shader/transition/luma.comp diff --git a/contrib/screen-13-fx/res/shader/transition/luminance_melt.comp b/contrib/vk-graph-fx/res/shader/transition/luminance_melt.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/luminance_melt.comp rename to contrib/vk-graph-fx/res/shader/transition/luminance_melt.comp diff --git a/contrib/screen-13-fx/res/shader/transition/morph.comp b/contrib/vk-graph-fx/res/shader/transition/morph.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/morph.comp rename to contrib/vk-graph-fx/res/shader/transition/morph.comp diff --git a/contrib/screen-13-fx/res/shader/transition/mosaic.comp b/contrib/vk-graph-fx/res/shader/transition/mosaic.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/mosaic.comp rename to contrib/vk-graph-fx/res/shader/transition/mosaic.comp diff --git a/contrib/screen-13-fx/res/shader/transition/multiply.comp b/contrib/vk-graph-fx/res/shader/transition/multiply.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/multiply.comp rename to contrib/vk-graph-fx/res/shader/transition/multiply.comp diff --git a/contrib/screen-13-fx/res/shader/transition/overexposure.comp b/contrib/vk-graph-fx/res/shader/transition/overexposure.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/overexposure.comp rename to contrib/vk-graph-fx/res/shader/transition/overexposure.comp diff --git a/contrib/screen-13-fx/res/shader/transition/perlin.comp b/contrib/vk-graph-fx/res/shader/transition/perlin.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/perlin.comp rename to contrib/vk-graph-fx/res/shader/transition/perlin.comp diff --git a/contrib/screen-13-fx/res/shader/transition/pinwheel.comp b/contrib/vk-graph-fx/res/shader/transition/pinwheel.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/pinwheel.comp rename to contrib/vk-graph-fx/res/shader/transition/pinwheel.comp diff --git a/contrib/screen-13-fx/res/shader/transition/pixelize.comp b/contrib/vk-graph-fx/res/shader/transition/pixelize.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/pixelize.comp rename to contrib/vk-graph-fx/res/shader/transition/pixelize.comp diff --git a/contrib/screen-13-fx/res/shader/transition/polar_function.comp b/contrib/vk-graph-fx/res/shader/transition/polar_function.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/polar_function.comp rename to contrib/vk-graph-fx/res/shader/transition/polar_function.comp diff --git a/contrib/screen-13-fx/res/shader/transition/polka_dots_curtain.comp b/contrib/vk-graph-fx/res/shader/transition/polka_dots_curtain.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/polka_dots_curtain.comp rename to contrib/vk-graph-fx/res/shader/transition/polka_dots_curtain.comp diff --git a/contrib/screen-13-fx/res/shader/transition/power_kaleido.comp b/contrib/vk-graph-fx/res/shader/transition/power_kaleido.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/power_kaleido.comp rename to contrib/vk-graph-fx/res/shader/transition/power_kaleido.comp diff --git a/contrib/screen-13-fx/res/shader/transition/radial.comp b/contrib/vk-graph-fx/res/shader/transition/radial.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/radial.comp rename to contrib/vk-graph-fx/res/shader/transition/radial.comp diff --git a/contrib/screen-13-fx/res/shader/transition/random_noisex.comp b/contrib/vk-graph-fx/res/shader/transition/random_noisex.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/random_noisex.comp rename to contrib/vk-graph-fx/res/shader/transition/random_noisex.comp diff --git a/contrib/screen-13-fx/res/shader/transition/random_squares.comp b/contrib/vk-graph-fx/res/shader/transition/random_squares.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/random_squares.comp rename to contrib/vk-graph-fx/res/shader/transition/random_squares.comp diff --git a/contrib/screen-13-fx/res/shader/transition/ripple.comp b/contrib/vk-graph-fx/res/shader/transition/ripple.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/ripple.comp rename to contrib/vk-graph-fx/res/shader/transition/ripple.comp diff --git a/contrib/screen-13-fx/res/shader/transition/rotate.comp b/contrib/vk-graph-fx/res/shader/transition/rotate.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/rotate.comp rename to contrib/vk-graph-fx/res/shader/transition/rotate.comp diff --git a/contrib/screen-13-fx/res/shader/transition/rotate_scale.comp b/contrib/vk-graph-fx/res/shader/transition/rotate_scale.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/rotate_scale.comp rename to contrib/vk-graph-fx/res/shader/transition/rotate_scale.comp diff --git a/contrib/screen-13-fx/res/shader/transition/scale_in.comp b/contrib/vk-graph-fx/res/shader/transition/scale_in.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/scale_in.comp rename to contrib/vk-graph-fx/res/shader/transition/scale_in.comp diff --git a/contrib/screen-13-fx/res/shader/transition/simple_zoom.comp b/contrib/vk-graph-fx/res/shader/transition/simple_zoom.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/simple_zoom.comp rename to contrib/vk-graph-fx/res/shader/transition/simple_zoom.comp diff --git a/contrib/screen-13-fx/res/shader/transition/squares_wire.comp b/contrib/vk-graph-fx/res/shader/transition/squares_wire.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/squares_wire.comp rename to contrib/vk-graph-fx/res/shader/transition/squares_wire.comp diff --git a/contrib/screen-13-fx/res/shader/transition/squeeze.comp b/contrib/vk-graph-fx/res/shader/transition/squeeze.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/squeeze.comp rename to contrib/vk-graph-fx/res/shader/transition/squeeze.comp diff --git a/contrib/screen-13-fx/res/shader/transition/stereo_viewer.comp b/contrib/vk-graph-fx/res/shader/transition/stereo_viewer.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/stereo_viewer.comp rename to contrib/vk-graph-fx/res/shader/transition/stereo_viewer.comp diff --git a/contrib/screen-13-fx/res/shader/transition/swap.comp b/contrib/vk-graph-fx/res/shader/transition/swap.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/swap.comp rename to contrib/vk-graph-fx/res/shader/transition/swap.comp diff --git a/contrib/screen-13-fx/res/shader/transition/swirl.comp b/contrib/vk-graph-fx/res/shader/transition/swirl.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/swirl.comp rename to contrib/vk-graph-fx/res/shader/transition/swirl.comp diff --git a/contrib/screen-13-fx/res/shader/transition/tangent_motion_blur.comp b/contrib/vk-graph-fx/res/shader/transition/tangent_motion_blur.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/tangent_motion_blur.comp rename to contrib/vk-graph-fx/res/shader/transition/tangent_motion_blur.comp diff --git a/contrib/screen-13-fx/res/shader/transition/top_bottom.comp b/contrib/vk-graph-fx/res/shader/transition/top_bottom.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/top_bottom.comp rename to contrib/vk-graph-fx/res/shader/transition/top_bottom.comp diff --git a/contrib/screen-13-fx/res/shader/transition/tv_static.comp b/contrib/vk-graph-fx/res/shader/transition/tv_static.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/tv_static.comp rename to contrib/vk-graph-fx/res/shader/transition/tv_static.comp diff --git a/contrib/screen-13-fx/res/shader/transition/undulating_burn_out.comp b/contrib/vk-graph-fx/res/shader/transition/undulating_burn_out.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/undulating_burn_out.comp rename to contrib/vk-graph-fx/res/shader/transition/undulating_burn_out.comp diff --git a/contrib/screen-13-fx/res/shader/transition/water_drop.comp b/contrib/vk-graph-fx/res/shader/transition/water_drop.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/water_drop.comp rename to contrib/vk-graph-fx/res/shader/transition/water_drop.comp diff --git a/contrib/screen-13-fx/res/shader/transition/wind.comp b/contrib/vk-graph-fx/res/shader/transition/wind.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/wind.comp rename to contrib/vk-graph-fx/res/shader/transition/wind.comp diff --git a/contrib/screen-13-fx/res/shader/transition/window_blinds.comp b/contrib/vk-graph-fx/res/shader/transition/window_blinds.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/window_blinds.comp rename to contrib/vk-graph-fx/res/shader/transition/window_blinds.comp diff --git a/contrib/screen-13-fx/res/shader/transition/window_slice.comp b/contrib/vk-graph-fx/res/shader/transition/window_slice.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/window_slice.comp rename to contrib/vk-graph-fx/res/shader/transition/window_slice.comp diff --git a/contrib/screen-13-fx/res/shader/transition/wipe_down.comp b/contrib/vk-graph-fx/res/shader/transition/wipe_down.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/wipe_down.comp rename to contrib/vk-graph-fx/res/shader/transition/wipe_down.comp diff --git a/contrib/screen-13-fx/res/shader/transition/wipe_left.comp b/contrib/vk-graph-fx/res/shader/transition/wipe_left.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/wipe_left.comp rename to contrib/vk-graph-fx/res/shader/transition/wipe_left.comp diff --git a/contrib/screen-13-fx/res/shader/transition/wipe_right.comp b/contrib/vk-graph-fx/res/shader/transition/wipe_right.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/wipe_right.comp rename to contrib/vk-graph-fx/res/shader/transition/wipe_right.comp diff --git a/contrib/screen-13-fx/res/shader/transition/wipe_up.comp b/contrib/vk-graph-fx/res/shader/transition/wipe_up.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/wipe_up.comp rename to contrib/vk-graph-fx/res/shader/transition/wipe_up.comp diff --git a/contrib/screen-13-fx/res/shader/transition/zoom_in_circles.comp b/contrib/vk-graph-fx/res/shader/transition/zoom_in_circles.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/zoom_in_circles.comp rename to contrib/vk-graph-fx/res/shader/transition/zoom_in_circles.comp diff --git a/contrib/screen-13-fx/res/shader/transition/zoom_left_wipe.comp b/contrib/vk-graph-fx/res/shader/transition/zoom_left_wipe.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/zoom_left_wipe.comp rename to contrib/vk-graph-fx/res/shader/transition/zoom_left_wipe.comp diff --git a/contrib/screen-13-fx/res/shader/transition/zoom_right_wipe.comp b/contrib/vk-graph-fx/res/shader/transition/zoom_right_wipe.comp similarity index 100% rename from contrib/screen-13-fx/res/shader/transition/zoom_right_wipe.comp rename to contrib/vk-graph-fx/res/shader/transition/zoom_right_wipe.comp diff --git a/contrib/screen-13-fx/src/bitmap_font.rs b/contrib/vk-graph-fx/src/bitmap_font.rs similarity index 99% rename from contrib/screen-13-fx/src/bitmap_font.rs rename to contrib/vk-graph-fx/src/bitmap_font.rs index 407de9b5..9fe70825 100644 --- a/contrib/screen-13-fx/src/bitmap_font.rs +++ b/contrib/vk-graph-fx/src/bitmap_font.rs @@ -4,8 +4,8 @@ use { bytemuck::{cast, cast_slice}, glam::{vec3, Mat4}, inline_spirv::include_spirv, - screen_13::prelude::*, std::sync::Arc, + vk_graph::prelude::*, }; type Color = [u8; 4]; diff --git a/contrib/screen-13-fx/src/image_loader.rs b/contrib/vk-graph-fx/src/image_loader.rs similarity index 99% rename from contrib/screen-13-fx/src/image_loader.rs rename to contrib/vk-graph-fx/src/image_loader.rs index 7cabbe5f..98fc13df 100644 --- a/contrib/screen-13-fx/src/image_loader.rs +++ b/contrib/vk-graph-fx/src/image_loader.rs @@ -1,6 +1,6 @@ use { super::BitmapFont, anyhow::Context, bmfont::BMFont, inline_spirv::include_spirv, log::info, - screen_13::prelude::*, std::sync::Arc, + std::sync::Arc, vk_graph::prelude::*, }; #[cfg(debug_assertions)] diff --git a/contrib/screen-13-fx/src/lib.rs b/contrib/vk-graph-fx/src/lib.rs similarity index 100% rename from contrib/screen-13-fx/src/lib.rs rename to contrib/vk-graph-fx/src/lib.rs diff --git a/contrib/screen-13-fx/src/presenter.rs b/contrib/vk-graph-fx/src/presenter.rs similarity index 99% rename from contrib/screen-13-fx/src/presenter.rs rename to contrib/vk-graph-fx/src/presenter.rs index 426d21dd..4c72080e 100644 --- a/contrib/screen-13-fx/src/presenter.rs +++ b/contrib/vk-graph-fx/src/presenter.rs @@ -2,8 +2,8 @@ use { bytemuck::cast_slice, glam::{vec3, Mat4}, inline_spirv::include_spirv, - screen_13::prelude::*, std::sync::Arc, + vk_graph::prelude::*, }; pub struct ComputePresenter([Arc; 2]); diff --git a/contrib/screen-13-fx/src/transition.rs b/contrib/vk-graph-fx/src/transition.rs similarity index 99% rename from contrib/screen-13-fx/src/transition.rs rename to contrib/vk-graph-fx/src/transition.rs index 7f2b5cd5..2c5f3976 100644 --- a/contrib/screen-13-fx/src/transition.rs +++ b/contrib/vk-graph-fx/src/transition.rs @@ -5,8 +5,8 @@ use { inline_spirv::include_spirv, log::trace, - screen_13::prelude::*, std::{collections::HashMap, sync::Arc}, + vk_graph::prelude::*, }; #[derive(Clone, Copy, Debug)] diff --git a/contrib/screen-13-hot/.github/img/noise.png b/contrib/vk-graph-hot/.github/img/noise.png similarity index 100% rename from contrib/screen-13-hot/.github/img/noise.png rename to contrib/vk-graph-hot/.github/img/noise.png diff --git a/contrib/screen-13-hot/Cargo.toml b/contrib/vk-graph-hot/Cargo.toml similarity index 61% rename from contrib/screen-13-hot/Cargo.toml rename to contrib/vk-graph-hot/Cargo.toml index 327afa8f..d39f6210 100644 --- a/contrib/screen-13-hot/Cargo.toml +++ b/contrib/vk-graph-hot/Cargo.toml @@ -1,23 +1,23 @@ [package] -name = "screen-13-hot" +name = "vk-graph-hot" version = "0.1.0" authors = ["John Wells "] edition = "2021" license = "MIT OR Apache-2.0" readme = "README.md" -repository = "https://github.com/attackgoat/screen-13" -homepage = "https://github.com/attackgoat/screen-13/contrib/screen-13-hot" +repository = "https://github.com/attackgoat/vk-graph" +homepage = "https://github.com/attackgoat/vk-graph/contrib/vk-graph-hot" keywords = ["gamedev", "vulkan"] categories = ["game-development", "multimedia::images", "rendering::engine"] -description = "Hot-reloading shader pipelines for Screen-13" +description = "Hot-reloading shader pipelines for vk-graph" [dependencies] anyhow = "1.0" derive_builder = "0.13" log = "0.4" notify = "6.1" -screen-13 = { path = "../.."} -screen-13-window = { path = "../screen-13-window" } +vk-graph = { path = "../.."} +vk-graph-window = { path = "../vk-graph-window" } shader-prepper = "0.3.0-pre.3" shaderc = "0.8" diff --git a/contrib/screen-13-hot/README.md b/contrib/vk-graph-hot/README.md similarity index 85% rename from contrib/screen-13-hot/README.md rename to contrib/vk-graph-hot/README.md index a42cd39b..abdfac9c 100644 --- a/contrib/screen-13-hot/README.md +++ b/contrib/vk-graph-hot/README.md @@ -1,6 +1,6 @@ -# Screen 13 Hot +# vk-graph Hot -Hot-reloading shader pipelines for _Screen 13_. Supports compute, graphic, and ray-trace shader +Hot-reloading shader pipelines for _vk-graph_. Supports compute, graphic, and ray-trace shader pipelines. Based on shaderc. Feel free to submit PRs for other compilers. @@ -12,7 +12,7 @@ See the [example code](examples/README.md), ## Basic usage See the [GLSL](examples/glsl.rs) and [HLSL](examples/hlsl.rs) examples for usage - the hot pipelines -are drop-in replacements for the regular shader pipelines offered by _Screen 13_. +are drop-in replacements for the regular shader pipelines offered by _vk-graph_. After creating a pipeline two functions are available, `hot` or `cold`. The result of each may be bound to a render graph for any sort of regular use. @@ -28,4 +28,4 @@ level and warnings-as-errors, among other things. ## More infomation -Run `cargo doc --open` to view detailed API documentation and find available compilation options. \ No newline at end of file +Run `cargo doc --open` to view detailed API documentation and find available compilation options. diff --git a/contrib/screen-13-hot/examples/README.md b/contrib/vk-graph-hot/examples/README.md similarity index 93% rename from contrib/screen-13-hot/examples/README.md rename to contrib/vk-graph-hot/examples/README.md index 92dd719a..36c25e64 100644 --- a/contrib/screen-13-hot/examples/README.md +++ b/contrib/vk-graph-hot/examples/README.md @@ -1,4 +1,4 @@ -# _Screen 13 Hot_ Example Code +# _vk-graph Hot_ Example Code ## Getting Started @@ -12,4 +12,4 @@ See the [README](../README.md) for more information. Example | Instructions | Preview --- | --- | :---: [glsl.rs](glsl.rs) |
cargo run --example glsl
| Preview -[hlsl.rs](hlsl.rs) |
cargo run --example hlsl
| Preview \ No newline at end of file +[hlsl.rs](hlsl.rs) |
cargo run --example hlsl
| Preview diff --git a/contrib/screen-13-hot/examples/glsl.rs b/contrib/vk-graph-hot/examples/glsl.rs similarity index 93% rename from contrib/screen-13-hot/examples/glsl.rs rename to contrib/vk-graph-hot/examples/glsl.rs index b7f3d358..41582409 100644 --- a/contrib/screen-13-hot/examples/glsl.rs +++ b/contrib/vk-graph-hot/examples/glsl.rs @@ -1,9 +1,9 @@ use { clap::Parser, - screen_13::prelude::*, - screen_13_hot::prelude::*, - screen_13_window::{Window, WindowError}, std::path::PathBuf, + vk_graph::prelude::*, + vk_graph_hot::prelude::*, + vk_graph_window::{Window, WindowError}, }; /// This program draws a noise signal to the swapchain - make changes to fill_image.comp or the diff --git a/contrib/screen-13-hot/examples/hlsl.rs b/contrib/vk-graph-hot/examples/hlsl.rs similarity index 94% rename from contrib/screen-13-hot/examples/hlsl.rs rename to contrib/vk-graph-hot/examples/hlsl.rs index c7192112..45100985 100644 --- a/contrib/screen-13-hot/examples/hlsl.rs +++ b/contrib/vk-graph-hot/examples/hlsl.rs @@ -1,9 +1,9 @@ use { clap::Parser, - screen_13::prelude::*, - screen_13_hot::prelude::*, - screen_13_window::{Window, WindowError}, std::path::PathBuf, + vk_graph::prelude::*, + vk_graph_hot::prelude::*, + vk_graph_window::{Window, WindowError}, }; /// This program draws a noise signal to the swapchain - make changes to fill_image.hlsl or the diff --git a/contrib/screen-13-hot/examples/res/fill_image.comp b/contrib/vk-graph-hot/examples/res/fill_image.comp similarity index 100% rename from contrib/screen-13-hot/examples/res/fill_image.comp rename to contrib/vk-graph-hot/examples/res/fill_image.comp diff --git a/contrib/screen-13-hot/examples/res/fill_image.hlsl b/contrib/vk-graph-hot/examples/res/fill_image.hlsl similarity index 100% rename from contrib/screen-13-hot/examples/res/fill_image.hlsl rename to contrib/vk-graph-hot/examples/res/fill_image.hlsl diff --git a/contrib/screen-13-hot/examples/res/noise.glsl b/contrib/vk-graph-hot/examples/res/noise.glsl similarity index 100% rename from contrib/screen-13-hot/examples/res/noise.glsl rename to contrib/vk-graph-hot/examples/res/noise.glsl diff --git a/contrib/screen-13-hot/examples/res/noise.hlsl b/contrib/vk-graph-hot/examples/res/noise.hlsl similarity index 100% rename from contrib/screen-13-hot/examples/res/noise.hlsl rename to contrib/vk-graph-hot/examples/res/noise.hlsl diff --git a/contrib/screen-13-hot/src/compute.rs b/contrib/vk-graph-hot/src/compute.rs similarity index 98% rename from contrib/screen-13-hot/src/compute.rs rename to contrib/vk-graph-hot/src/compute.rs index 59258f7d..0ac793f9 100644 --- a/contrib/screen-13-hot/src/compute.rs +++ b/contrib/vk-graph-hot/src/compute.rs @@ -2,11 +2,11 @@ use { super::{compile_shader_and_watch, create_watcher, shader::HotShader}, log::info, notify::RecommendedWatcher, - screen_13::prelude::*, std::sync::{ atomic::{AtomicBool, Ordering}, Arc, }, + vk_graph::prelude::*, }; #[derive(Debug)] diff --git a/contrib/screen-13-hot/src/graphic.rs b/contrib/vk-graph-hot/src/graphic.rs similarity index 98% rename from contrib/screen-13-hot/src/graphic.rs rename to contrib/vk-graph-hot/src/graphic.rs index 92ca7b1b..f5d21c9f 100644 --- a/contrib/screen-13-hot/src/graphic.rs +++ b/contrib/vk-graph-hot/src/graphic.rs @@ -2,11 +2,11 @@ use { super::{compile_shader_and_watch, create_watcher, shader::HotShader}, log::info, notify::RecommendedWatcher, - screen_13::prelude::*, std::sync::{ atomic::{AtomicBool, Ordering}, Arc, }, + vk_graph::prelude::*, }; #[derive(Debug)] diff --git a/contrib/screen-13-hot/src/lib.rs b/contrib/vk-graph-hot/src/lib.rs similarity index 99% rename from contrib/screen-13-hot/src/lib.rs rename to contrib/vk-graph-hot/src/lib.rs index 4ed2cedb..6339b2d3 100644 --- a/contrib/screen-13-hot/src/lib.rs +++ b/contrib/vk-graph-hot/src/lib.rs @@ -16,7 +16,6 @@ use { self::shader::HotShader, log::{error, info}, notify::{recommended_watcher, Event, EventKind, RecommendedWatcher}, - screen_13::prelude::*, shader_prepper::{ process_file, BoxedIncludeProviderError, IncludeProvider, ResolvedInclude, ResolvedIncludePath, @@ -32,6 +31,7 @@ use { Arc, OnceLock, }, }, + vk_graph::prelude::*, }; struct CompiledShader { diff --git a/contrib/screen-13-hot/src/ray_trace.rs b/contrib/vk-graph-hot/src/ray_trace.rs similarity index 99% rename from contrib/screen-13-hot/src/ray_trace.rs rename to contrib/vk-graph-hot/src/ray_trace.rs index c7a8ab7d..b136562a 100644 --- a/contrib/screen-13-hot/src/ray_trace.rs +++ b/contrib/vk-graph-hot/src/ray_trace.rs @@ -2,11 +2,11 @@ use { super::{compile_shader_and_watch, create_watcher, shader::HotShader}, log::info, notify::RecommendedWatcher, - screen_13::prelude::*, std::sync::{ atomic::{AtomicBool, Ordering}, Arc, }, + vk_graph::prelude::*, }; #[derive(Debug)] diff --git a/contrib/screen-13-hot/src/shader.rs b/contrib/vk-graph-hot/src/shader.rs similarity index 97% rename from contrib/screen-13-hot/src/shader.rs rename to contrib/vk-graph-hot/src/shader.rs index c5bb685f..7a9df7dc 100644 --- a/contrib/screen-13-hot/src/shader.rs +++ b/contrib/vk-graph-hot/src/shader.rs @@ -5,9 +5,9 @@ use { derive_builder::{Builder, UninitializedFieldError}, log::{debug, error}, notify::{RecommendedWatcher, RecursiveMode, Watcher}, - screen_13::prelude::*, shaderc::{CompileOptions, EnvVersion, ShaderKind, TargetEnv}, std::path::{Path, PathBuf}, + vk_graph::prelude::*, }; /// Describes a shader program which runs on some pipeline stage. @@ -69,10 +69,10 @@ pub struct HotShader { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::shader::{SpecializationInfo}; - /// # use screen_13_hot::shader::HotShader; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::shader::{SpecializationInfo}; + /// # use vk_graph_hot::shader::HotShader; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let my_shader_code = [0u8; 1]; diff --git a/contrib/vk-graph-imgui/Cargo.toml b/contrib/vk-graph-imgui/Cargo.toml new file mode 100644 index 00000000..7b397e69 --- /dev/null +++ b/contrib/vk-graph-imgui/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "vk-graph-imgui" +version = "0.1.0" +authors = ["John Wells "] +edition = "2021" +license = "MIT OR Apache-2.0" +readme = "README.md" + +[dependencies] +bytemuck = "1.24" +imgui = "0.12" +imgui-winit-support = "0.13" +inline-spirv = "0.2" +vk-graph = { path = "../.." } diff --git a/contrib/screen-13-imgui/README.md b/contrib/vk-graph-imgui/README.md similarity index 50% rename from contrib/screen-13-imgui/README.md rename to contrib/vk-graph-imgui/README.md index 884ddb9e..71f787db 100644 --- a/contrib/screen-13-imgui/README.md +++ b/contrib/vk-graph-imgui/README.md @@ -1,5 +1,5 @@ -# _Screen 13_ _Dear imGui_ Integration +# _vk-graph_ _Dear imGui_ Integration Preview -[Example code](../../examples/README.md) \ No newline at end of file +[Example code](../../examples/README.md) diff --git a/contrib/screen-13-imgui/res/font/mplus-1p/LICENSE b/contrib/vk-graph-imgui/res/font/mplus-1p/LICENSE similarity index 100% rename from contrib/screen-13-imgui/res/font/mplus-1p/LICENSE rename to contrib/vk-graph-imgui/res/font/mplus-1p/LICENSE diff --git a/contrib/screen-13-imgui/res/font/mplus-1p/mplus-1p-regular.ttf b/contrib/vk-graph-imgui/res/font/mplus-1p/mplus-1p-regular.ttf similarity index 100% rename from contrib/screen-13-imgui/res/font/mplus-1p/mplus-1p-regular.ttf rename to contrib/vk-graph-imgui/res/font/mplus-1p/mplus-1p-regular.ttf diff --git a/contrib/screen-13-imgui/res/font/roboto/LICENSE b/contrib/vk-graph-imgui/res/font/roboto/LICENSE similarity index 100% rename from contrib/screen-13-imgui/res/font/roboto/LICENSE rename to contrib/vk-graph-imgui/res/font/roboto/LICENSE diff --git a/contrib/screen-13-imgui/res/font/roboto/roboto-regular.ttf b/contrib/vk-graph-imgui/res/font/roboto/roboto-regular.ttf similarity index 100% rename from contrib/screen-13-imgui/res/font/roboto/roboto-regular.ttf rename to contrib/vk-graph-imgui/res/font/roboto/roboto-regular.ttf diff --git a/contrib/screen-13-imgui/res/shader/imgui.frag b/contrib/vk-graph-imgui/res/shader/imgui.frag similarity index 100% rename from contrib/screen-13-imgui/res/shader/imgui.frag rename to contrib/vk-graph-imgui/res/shader/imgui.frag diff --git a/contrib/screen-13-imgui/res/shader/imgui.vert b/contrib/vk-graph-imgui/res/shader/imgui.vert similarity index 100% rename from contrib/screen-13-imgui/res/shader/imgui.vert rename to contrib/vk-graph-imgui/res/shader/imgui.vert diff --git a/contrib/vk-graph-imgui/src/lib.rs b/contrib/vk-graph-imgui/src/lib.rs new file mode 100644 index 00000000..29b75acd --- /dev/null +++ b/contrib/vk-graph-imgui/src/lib.rs @@ -0,0 +1,290 @@ +pub mod prelude { + pub use super::{imgui, Condition, ImGui, Ui}; +} + +pub use imgui::{self, Condition, Ui}; + +use { + bytemuck::cast_slice, + imgui::{Context, DrawCmd, DrawCmdParams}, + imgui_winit_support::{ + winit::{event::Event, window::Window}, + {HiDpiMode, WinitPlatform}, + }, + inline_spirv::include_spirv, + std::{sync::Arc, time::Duration}, + vk_graph::prelude::*, +}; + +#[derive(Debug)] +pub struct ImGui { + context: Context, + font_atlas_image: Option>>, + pipeline: Arc, + platform: WinitPlatform, +} + +impl ImGui { + pub fn new(device: &Arc) -> Self { + let mut context = Context::create(); + let platform = WinitPlatform::new(&mut context); + let pipeline = Arc::new( + GraphicPipeline::create( + device, + GraphicPipelineInfoBuilder::default() + .blend(BlendMode::PRE_MULTIPLIED_ALPHA) + .cull_mode(vk::CullModeFlags::NONE), + [ + Shader::new_vertex(include_spirv!("res/shader/imgui.vert", vert).as_slice()), + Shader::new_fragment(include_spirv!("res/shader/imgui.frag", frag).as_slice()), + ], + ) + .unwrap(), + ); + + Self { + context, + font_atlas_image: None, + pipeline, + platform, + } + } + + // TODO: This produces an image which is RGBA8 UNORM and has STORAGE set. *We* don't need storage here and should instead ask the user what settings to give the output image..... + pub fn draw

( + &mut self, + dt: f32, + events: &[Event<()>], + window: &Window, + pool: &mut P, + render_graph: &mut RenderGraph, + ui_func: impl FnOnce(&mut Ui, &mut P, &mut RenderGraph), + ) -> ImageLeaseNode + where + P: Pool + Pool, + { + let hidpi = self.platform.hidpi_factor(); + + self.platform + .attach_window(self.context.io_mut(), window, HiDpiMode::Default); + + if self.font_atlas_image.is_none() || self.platform.hidpi_factor() != hidpi { + self.lease_font_atlas_image(pool, render_graph); + } + + let io = self.context.io_mut(); + io.update_delta_time(Duration::from_secs_f32(dt)); + + for event in events { + self.platform.handle_event(io, window, event); + } + + self.platform + .prepare_frame(io, window) + .expect("Unable to prepare ImGui frame"); + + // Let the caller draw the GUI + let ui = self.context.frame(); + + ui_func(ui, pool, render_graph); + + self.platform.prepare_render(ui, window); + let draw_data = self.context.render(); + + let image = render_graph.bind_node({ + let mut image = pool + .lease(ImageInfo::image_2d( + window.inner_size().width, + window.inner_size().height, + vk::Format::R8G8B8A8_UNORM, + vk::ImageUsageFlags::COLOR_ATTACHMENT + | vk::ImageUsageFlags::SAMPLED + | vk::ImageUsageFlags::STORAGE + | vk::ImageUsageFlags::TRANSFER_DST + | vk::ImageUsageFlags::TRANSFER_SRC, // TODO: Make TRANSFER_SRC an "extra flags" + )) + .unwrap(); + image.as_mut().name = Some("ImGui Output".to_string()); + + image + }); + let font_atlas_image = render_graph.bind_node(self.font_atlas_image.as_ref().unwrap()); + let display_pos = draw_data.display_pos; + let framebuffer_scale = draw_data.framebuffer_scale; + + if draw_data.draw_lists_count() == 0 { + render_graph.clear_color_image(image); + + return image; + } + + for draw_list in draw_data.draw_lists() { + let indices = cast_slice(draw_list.idx_buffer()); + let mut index_buf = pool + .lease(BufferInfo::host_mem( + indices.len() as _, + vk::BufferUsageFlags::INDEX_BUFFER, + )) + .unwrap(); + + { + Buffer::mapped_slice_mut(&mut index_buf)[0..indices.len()].copy_from_slice(indices); + } + + let index_buf = render_graph.bind_node(index_buf); + + let vertices = draw_list.vtx_buffer(); + let vertex_buf_len = vertices.len() * 20; + let mut vertex_buf = pool + .lease(BufferInfo::host_mem( + vertex_buf_len as _, + vk::BufferUsageFlags::VERTEX_BUFFER, + )) + .unwrap(); + + { + let vertex_buf = Buffer::mapped_slice_mut(&mut vertex_buf); + for (idx, vertex) in vertices.iter().enumerate() { + let offset = idx * 20; + vertex_buf[offset..offset + 8].copy_from_slice(cast_slice(&vertex.pos)); + vertex_buf[offset + 8..offset + 16].copy_from_slice(cast_slice(&vertex.uv)); + vertex_buf[offset + 16..offset + 20].copy_from_slice(&vertex.col); + } + } + + let vertex_buf = render_graph.bind_node(vertex_buf); + + let draw_cmds = draw_list + .commands() + .map(|draw_cmd| match draw_cmd { + DrawCmd::Elements { + count, + cmd_params: + DrawCmdParams { + clip_rect, + idx_offset, + vtx_offset, + .. + }, + } => (count, clip_rect, idx_offset, vtx_offset), + _ => unimplemented!(), + }) + .collect::>(); + + let window_width = + self.platform.hidpi_factor() as f32 / window.inner_size().width as f32; + let window_height = + self.platform.hidpi_factor() as f32 / window.inner_size().height as f32; + + render_graph + .begin_pass("imgui") + .bind_pipeline(&self.pipeline) + .access_node(index_buf, AccessType::IndexBuffer) + .access_node(vertex_buf, AccessType::VertexBuffer) + .read_descriptor(0, font_atlas_image) + .clear_color(0, image) + .store_color(0, image) + .record_subpass(move |subpass, _| { + subpass + .push_constants_offset(0, &window_width.to_ne_bytes()) + .push_constants_offset(4, &window_height.to_ne_bytes()) + .bind_index_buffer(index_buf, vk::IndexType::UINT16) + .bind_vertex_buffer(vertex_buf); + + for (index_count, clip_rect, first_index, vertex_offset) in draw_cmds { + let clip_rect = [ + (clip_rect[0] - display_pos[0]) * framebuffer_scale[0], + (clip_rect[1] - display_pos[1]) * framebuffer_scale[1], + (clip_rect[2] - display_pos[0]) * framebuffer_scale[0], + (clip_rect[3] - display_pos[1]) * framebuffer_scale[1], + ]; + let x = clip_rect[0].floor() as i32; + let y = clip_rect[1].floor() as i32; + let width = (clip_rect[2] - clip_rect[0]).ceil() as u32; + let height = (clip_rect[3] - clip_rect[1]).ceil() as u32; + subpass.set_scissor(x, y, width, height); + subpass.draw_indexed( + index_count as _, + 1, + first_index as _, + vertex_offset as _, + 0, + ); + } + }); + } + + image + } + + fn lease_font_atlas_image

(&mut self, pool: &mut P, render_graph: &mut RenderGraph) + where + P: Pool + Pool, + { + use imgui::{FontConfig, FontGlyphRanges, FontSource}; + + let hidpi_factor = self.platform.hidpi_factor(); + self.context.io_mut().font_global_scale = (1.0 / hidpi_factor) as f32; + + let font_size = (14.0 * hidpi_factor) as f32; + let fonts = self.context.fonts(); + fonts.clear_fonts(); + fonts.add_font(&[ + FontSource::TtfData { + data: include_bytes!("../res/font/roboto/roboto-regular.ttf"), + size_pixels: font_size, + config: Some(FontConfig { + rasterizer_multiply: 2.0, + glyph_ranges: FontGlyphRanges::japanese(), + ..FontConfig::default() + }), + }, + FontSource::TtfData { + data: include_bytes!("../res/font/mplus-1p/mplus-1p-regular.ttf"), + size_pixels: font_size, + config: Some(FontConfig { + oversample_h: 2, + oversample_v: 2, + // Range of glyphs to rasterize + glyph_ranges: FontGlyphRanges::japanese(), + ..FontConfig::default() + }), + }, + ]); + + let texture = fonts.build_rgba32_texture(); // TODO: Fix fb channel writes and use alpha8! + let temp_buf_len = texture.data.len(); + let mut temp_buf = pool + .lease(BufferInfo::host_mem( + temp_buf_len as _, + vk::BufferUsageFlags::TRANSFER_SRC, + )) + .unwrap(); + + { + let temp_buf = Buffer::mapped_slice_mut(&mut temp_buf); + temp_buf[0..temp_buf_len].copy_from_slice(texture.data); + } + + let temp_buf = render_graph.bind_node(temp_buf); + let image = render_graph.bind_node({ + let mut image = pool + .lease(ImageInfo::image_2d( + texture.width, + texture.height, + vk::Format::R8G8B8A8_UNORM, + vk::ImageUsageFlags::SAMPLED + | vk::ImageUsageFlags::STORAGE + | vk::ImageUsageFlags::TRANSFER_DST, + )) + .unwrap(); + image.as_mut().name = Some("ImGui Font Atlas".to_string()); + + image + }); + + render_graph.copy_buffer_to_image(temp_buf, image); + + self.font_atlas_image = Some(render_graph.unbind_node(image)); + } +} diff --git a/contrib/screen-13-window/Cargo.toml b/contrib/vk-graph-window/Cargo.toml similarity index 81% rename from contrib/screen-13-window/Cargo.toml rename to contrib/vk-graph-window/Cargo.toml index 6782eb88..1830137d 100644 --- a/contrib/screen-13-window/Cargo.toml +++ b/contrib/vk-graph-window/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "screen-13-window" +name = "vk-graph-window" version = "0.1.0" authors = ["John Wells "] edition = "2021" @@ -9,7 +9,7 @@ readme = "README.md" [dependencies] log = "0.4" profiling = "1.0" -screen-13 = { path = "../.." } +vk-graph = { path = "../.." } winit = "0.30" [dev-dependencies] diff --git a/contrib/screen-13-window/README.md b/contrib/vk-graph-window/README.md similarity index 87% rename from contrib/screen-13-window/README.md rename to contrib/vk-graph-window/README.md index c8af5d02..a6bad6db 100644 --- a/contrib/screen-13-window/README.md +++ b/contrib/vk-graph-window/README.md @@ -1,4 +1,4 @@ -# _Screen 13_ Window Helper +# _vk-graph_ Window Helper This crate may be used as a pre-built window handler for examples and simple programs. diff --git a/contrib/screen-13-window/examples/hello_world.rs b/contrib/vk-graph-window/examples/hello_world.rs similarity index 86% rename from contrib/screen-13-window/examples/hello_world.rs rename to contrib/vk-graph-window/examples/hello_world.rs index 93abe253..04a83957 100644 --- a/contrib/screen-13-window/examples/hello_world.rs +++ b/contrib/vk-graph-window/examples/hello_world.rs @@ -1,4 +1,4 @@ -use screen_13_window::{Window, WindowError}; +use vk_graph_window::{Window, WindowError}; /// This example requires a color graphics adapter. fn main() -> Result<(), WindowError> { diff --git a/contrib/screen-13-window/src/frame.rs b/contrib/vk-graph-window/src/frame.rs similarity index 99% rename from contrib/screen-13-window/src/frame.rs rename to contrib/vk-graph-window/src/frame.rs index 7773181e..4a68fdbf 100644 --- a/contrib/screen-13-window/src/frame.rs +++ b/contrib/vk-graph-window/src/frame.rs @@ -1,5 +1,5 @@ use { - screen_13::{ + vk_graph::{ driver::device::Device, graph::{node::SwapchainImageNode, RenderGraph}, }, diff --git a/contrib/screen-13-window/src/lib.rs b/contrib/vk-graph-window/src/lib.rs similarity index 99% rename from contrib/screen-13-window/src/lib.rs rename to contrib/vk-graph-window/src/lib.rs index 5a291d19..86f58990 100644 --- a/contrib/screen-13-window/src/lib.rs +++ b/contrib/vk-graph-window/src/lib.rs @@ -4,7 +4,7 @@ pub use self::frame::FrameContext; use { log::{info, trace, warn}, - screen_13::{ + vk_graph::{ driver::{ ash::vk, device::{Device, DeviceInfo}, diff --git a/examples/README.md b/examples/README.md index a0c5d97e..be964ba3 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,8 +1,8 @@ -# _Screen 13_ Example Code +# _vk-graph_ Example Code ## Getting Started -A helpful [getting started](getting-started.md) guide is available which describes basic _Screen 13_ +A helpful [getting started](getting-started.md) guide is available which describes basic _vk-graph_ types and functions. See the [README](../README.md) for more information. @@ -17,7 +17,7 @@ Example | Instructions | Preview [min_max.rs](min_max.rs) |

cargo run --example min_max
| _See console output_ [mip_compute.rs](mip_compute.rs) |
cargo run --example mip_compute
| _See console output_ [subgroup_ops.rs](subgroup_ops.rs) |
cargo run --example subgroup_ops
| _See console output_ -[hello_world.rs](../contrib/screen-13-window/examples/hello_world.rs) | _See [screen-13-window](../contrib/screen-13-window/README.md)_ | hello_world.rs +[hello_world.rs](../contrib/vk-graph-window/examples/hello_world.rs) | _See [vk-graph-window](../contrib/vk-graph-window/README.md)_ | hello_world.rs [app.rs](app.rs) |
cargo run --example app
| app.rs [triangle.rs](triangle.rs) |
cargo run --example triangle
| triangle.rs [vertex_layout.rs](vertex_layout.rs) |
cargo run --example vertex_layout
| vertex_layout.rs @@ -43,8 +43,8 @@ Example | Instructions | Preview The following packages offer examples for specific cases not listed here: -- [contrib/screen-13-hot](../contrib/screen-13-hot/examples/README.md): Shader pipeline hot-reload +- [contrib/vk-graph-hot](../contrib/vk-graph-hot/examples/README.md): Shader pipeline hot-reload - [attackgoat/mood](https://github.com/attackgoat/mood): FPS game prototype with level loading and multiple rendering backends - [attackgoat/jw-basic](https://github.com/attackgoat/jw-basic): BASIC interpreter with graphics - commands powered by _Screen 13_ + commands powered by _vk-graph_ diff --git a/examples/aliasing.rs b/examples/aliasing.rs index 26ba27fb..67abfc9c 100644 --- a/examples/aliasing.rs +++ b/examples/aliasing.rs @@ -1,10 +1,10 @@ use { clap::Parser, - screen_13::{ + std::sync::Arc, + vk_graph::{ pool::alias::{Alias, AliasPool}, prelude::*, }, - std::sync::Arc, }; /// This example demonstrates resource aliasing. Aliasing is a memory-efficiency optimization that diff --git a/examples/app.rs b/examples/app.rs index 653695a4..0b869586 100644 --- a/examples/app.rs +++ b/examples/app.rs @@ -3,7 +3,8 @@ mod profile_with_puffin; use { clap::Parser, log::error, - screen_13::{ + std::sync::Arc, + vk_graph::{ Display, DisplayError, DisplayInfo, driver::{ device::{Device, DeviceInfoBuilder}, @@ -13,7 +14,6 @@ use { graph::RenderGraph, pool::hash::HashPool, }, - std::sync::Arc, winit::{ application::ApplicationHandler, error::EventLoopError, @@ -39,7 +39,7 @@ impl ApplicationHandler for Application { } fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window_attributes = Window::default_attributes().with_title("Screen 13"); + let window_attributes = Window::default_attributes().with_title("vk-graph"); let window = event_loop.create_window(window_attributes).unwrap(); let args = Args::parse(); diff --git a/examples/bindless.rs b/examples/bindless.rs index 9612e93f..1f478e2c 100644 --- a/examples/bindless.rs +++ b/examples/bindless.rs @@ -4,9 +4,9 @@ use { bytemuck::{Pod, Zeroable, cast_slice}, clap::Parser, inline_spirv::inline_spirv, - screen_13::prelude::*, - screen_13_window::{WindowBuilder, WindowError}, std::sync::Arc, + vk_graph::prelude::*, + vk_graph_window::{WindowBuilder, WindowError}, winit::dpi::LogicalSize, }; diff --git a/examples/cpu_readback.rs b/examples/cpu_readback.rs index 09ec55cc..8cca4718 100644 --- a/examples/cpu_readback.rs +++ b/examples/cpu_readback.rs @@ -1,7 +1,7 @@ use { clap::Parser, - screen_13::prelude::*, std::{sync::Arc, time::Instant}, + vk_graph::prelude::*, }; /// Example demonstrating the steps to take when reading the results of buffer or image operations diff --git a/examples/debugger.rs b/examples/debugger.rs index e319332a..ef7283c1 100644 --- a/examples/debugger.rs +++ b/examples/debugger.rs @@ -5,7 +5,7 @@ I hope you enjoy this choose-your-own-debugger adventure! First you will want to read this: - https://github.com/attackgoat/screen-13/blob/master/examples/getting-started.md + https://github.com/attackgoat/vk-graph/blob/master/examples/getting-started.md Enter your "name" to begin: cargo run --example debugger @@ -23,8 +23,8 @@ To continue, uncomment line 30. */ -fn main() -> Result<(), screen_13_window::WindowError> { - use {log::debug, screen_13::prelude::*, screen_13_window::Window, std::sync::Arc}; +fn main() -> Result<(), vk_graph_window::WindowError> { + use {log::debug, std::sync::Arc, vk_graph::prelude::*, vk_graph_window::Window}; // 👋, 🌎! //pretty_env_logger::init(); @@ -52,7 +52,7 @@ fn main() -> Result<(), screen_13_window::WindowError> { When something goes wrong, it is probably *not* during this frame closure. The reason is that during this scope nearly everything is deferred until frame resolution where we try to schedule the work and get it displayed on the screen. - Typically, as here, we let Screen 13 handle all graph resolution (no code or + Typically, as here, we let vk-graph handle all graph resolution (no code or concerns here) - but it is valid to control the process manually, see the available functions in the API docs. @@ -80,7 +80,7 @@ fn main() -> Result<(), screen_13_window::WindowError> { } - Run `cargo run --example debugger` - You should see the PID in the console output - - Enter the VS Code Debugger; click `[>] Attach (screen-13)` + - Enter the VS Code Debugger; click `[>] Attach (vk-graph)` - Enter the PID - In the call stack pane, select the first thread; pause it - You are now parked on a syscall @@ -151,7 +151,7 @@ fn main() -> Result<(), screen_13_window::WindowError> { /* Case #2: - We are about to record a compute pass which causes Screen 13 to panic + We are about to record a compute pass which causes vk-graph to panic Note: You'll see a panic here: thread 'main' panicked at 'uninitialized swapchain image ...' @@ -205,7 +205,7 @@ fn main() -> Result<(), screen_13_window::WindowError> { Where to next? Fire up RenderDoc, capture a frame and have fun! But beware - RenderDoc does a replay of the capture it created; and it resubmits things ever so slightly differently at times - you most likely will NOT see any synchronization issues in RenderDoc if you DO see - them in Screen 13. + them in vk-graph. If you ever get stuck, switch between `vkconfig` settings of API dump and synchronization; those usually say exactly what is going wrong, and usually you need to use multiple layers diff --git a/examples/egui.rs b/examples/egui.rs index 2af1da8d..89900bd7 100644 --- a/examples/egui.rs +++ b/examples/egui.rs @@ -1,8 +1,8 @@ mod profile_with_puffin; use { - clap::Parser, screen_13::prelude::*, screen_13_egui::prelude::*, - screen_13_window::WindowBuilder, winit::dpi::LogicalSize, + clap::Parser, vk_graph::prelude::*, vk_graph_egui::prelude::*, vk_graph_window::WindowBuilder, + winit::dpi::LogicalSize, }; fn main() -> anyhow::Result<()> { diff --git a/examples/font_bmp.rs b/examples/font_bmp.rs index b5be0d5e..dc345645 100644 --- a/examples/font_bmp.rs +++ b/examples/font_bmp.rs @@ -5,17 +5,17 @@ use { clap::Parser, image::ImageReader, inline_spirv::inline_spirv, - screen_13::prelude::*, - screen_13_fx::*, - screen_13_window::WindowBuilder, std::{io::Cursor, sync::Arc, time::Instant}, + vk_graph::prelude::*, + vk_graph_fx::*, + vk_graph_window::WindowBuilder, }; fn main() -> anyhow::Result<()> { pretty_env_logger::init(); profile_with_puffin::init(); - // Standard Screen 13 stuff + // Standard vk-graph stuff let args = Args::parse(); let window = WindowBuilder::default().debug(args.debug).build()?; let display = GraphicPresenter::new(&window.device)?; @@ -148,7 +148,7 @@ fn main() -> anyhow::Result<()> { }); // Print some text onto the image - let text = "Screen 13"; + let text = "vk-graph"; let (_offset, [width, height]) = small_10px_font.measure(text); let scale = 4.0; let x = 320f32 * 0.5 / scale - width as f32 * 0.5; diff --git a/examples/fuzzer.rs b/examples/fuzzer.rs index 0fb7a741..57451c78 100644 --- a/examples/fuzzer.rs +++ b/examples/fuzzer.rs @@ -26,9 +26,9 @@ use { inline_spirv::inline_spirv, log::debug, rand::{Rng, rng, seq::IndexedRandom}, - screen_13::prelude::*, - screen_13_window::{FrameContext, WindowBuilder, WindowError}, std::{mem::size_of, sync::Arc}, + vk_graph::prelude::*, + vk_graph_window::{FrameContext, WindowBuilder, WindowError}, }; type Operation = fn(&mut FrameContext, &mut HashPool); @@ -57,14 +57,14 @@ fn main() -> Result<(), WindowError> { let mut rng = rng(); - let screen_13 = WindowBuilder::default().debug(true).build()?; - let mut pool = HashPool::new(&screen_13.device); + let vk_graph = WindowBuilder::default().debug(true).build()?; + let mut pool = HashPool::new(&vk_graph.device); let mut frame_count = 0; let args = Args::parse(); - screen_13.run(|mut frame| { + vk_graph.run(|mut frame| { if frame_count == args.frame_count { *frame.will_exit = true; return; diff --git a/examples/getting-started.md b/examples/getting-started.md index be20a759..c7602147 100644 --- a/examples/getting-started.md +++ b/examples/getting-started.md @@ -1,7 +1,7 @@ -# Getting Started with _Screen 13_ +# Getting Started with _vk-graph_ -This guide is intended for developers who are new to _Screen 13_ and want a step-by-step introduction. For further details on these -topics refer to the online [documentation](https://docs.rs/screen-13/latest/screen_13/). +This guide is intended for developers who are new to _vk-graph_ and want a step-by-step introduction. For further details on these +topics refer to the online [documentation](https://docs.rs/vk-graph/latest/vk_graph/). ## Required Packages @@ -18,7 +18,7 @@ _Windows_: ## Documentation -Read the generated [documentation](https://docs.rs/screen-13/latest/screen_13/) online, or run the +Read the generated [documentation](https://docs.rs/vk-graph/latest/vk_graph/) online, or run the following command locally: ``` @@ -27,8 +27,8 @@ cargo doc --open ## Changes -Stay informed of recent changes to _Screen 13_ using the -[change log](https://github.com/attackgoat/screen-13/blob/master/CHANGELOG.md) file. +Stay informed of recent changes to _vk-graph_ using the +[change log](https://github.com/attackgoat/vk-graph/blob/master/CHANGELOG.md) file. ## Performance Profiling diff --git a/examples/image_sampler.rs b/examples/image_sampler.rs index e64d16b8..f024bffc 100644 --- a/examples/image_sampler.rs +++ b/examples/image_sampler.rs @@ -4,12 +4,12 @@ use { clap::Parser, hassle_rs::compile_hlsl, inline_spirv::inline_spirv, - screen_13::prelude::*, - screen_13_window::WindowBuilder, std::{ path::{Path, PathBuf}, sync::Arc, }, + vk_graph::prelude::*, + vk_graph_window::WindowBuilder, }; /// Displays a sequence of image samplers. @@ -224,7 +224,7 @@ fn create_pipeline( } fn read_image(device: &Arc, path: impl AsRef) -> anyhow::Result> { - // For another way to loading images, see screen_13_fx::ImageLoader + // For another way to loading images, see vk_graph_fx::ImageLoader let gulf_jpg = image::open(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(path))?; let image = Arc::new(Image::create( device, diff --git a/examples/imgui.rs b/examples/imgui.rs index de1623ec..188640b2 100644 --- a/examples/imgui.rs +++ b/examples/imgui.rs @@ -2,10 +2,10 @@ mod profile_with_puffin; use { clap::Parser, - screen_13::prelude::*, - screen_13_fx::*, - screen_13_imgui::{Condition, ImGui}, - screen_13_window::{WindowBuilder, WindowError}, + vk_graph::prelude::*, + vk_graph_fx::*, + vk_graph_imgui::{Condition, ImGui}, + vk_graph_window::{WindowBuilder, WindowError}, winit::dpi::LogicalSize, }; @@ -13,7 +13,7 @@ fn main() -> Result<(), WindowError> { pretty_env_logger::init(); profile_with_puffin::init(); - // Screen 13 things we need for this demo + // vk-graph things we need for this demo let args = Args::parse(); let window = WindowBuilder::default() .debug(args.debug) diff --git a/examples/min_max.rs b/examples/min_max.rs index cb26019f..5856b94b 100644 --- a/examples/min_max.rs +++ b/examples/min_max.rs @@ -3,8 +3,8 @@ use { clap::Parser, inline_spirv::inline_spirv, log::warn, - screen_13::prelude::*, std::{mem::size_of, sync::Arc}, + vk_graph::prelude::*, }; // Min/max sampler reduction is commonly used to create depth buffer mip-maps for use with gpu-based diff --git a/examples/mip_compute.rs b/examples/mip_compute.rs index a75c3a9b..19fbd516 100644 --- a/examples/mip_compute.rs +++ b/examples/mip_compute.rs @@ -1,8 +1,8 @@ mod profile_with_puffin; use { - bytemuck::cast_slice, clap::Parser, inline_spirv::inline_spirv, screen_13::prelude::*, - std::sync::Arc, + bytemuck::cast_slice, clap::Parser, inline_spirv::inline_spirv, std::sync::Arc, + vk_graph::prelude::*, }; /// This program demonstrates a single render pass which uses multiple executions to record a chain diff --git a/examples/mip_graphic.rs b/examples/mip_graphic.rs index 9698d0df..0d69e9e9 100644 --- a/examples/mip_graphic.rs +++ b/examples/mip_graphic.rs @@ -6,9 +6,9 @@ use { core::f32, glam::{Vec4, vec3}, inline_spirv::inline_spirv, - screen_13::prelude::*, - screen_13_window::{WindowBuilder, WindowError}, std::sync::Arc, + vk_graph::prelude::*, + vk_graph_window::{WindowBuilder, WindowError}, }; // TODO: Add texelFetch option diff --git a/examples/msaa.rs b/examples/msaa.rs index 6c4a0f27..b2df482d 100644 --- a/examples/msaa.rs +++ b/examples/msaa.rs @@ -6,9 +6,9 @@ use { glam::{Mat4, Vec3}, inline_spirv::inline_spirv, log::warn, - screen_13::prelude::*, - screen_13_window::WindowBuilder, std::{mem::size_of, sync::Arc}, + vk_graph::prelude::*, + vk_graph_window::WindowBuilder, winit::{event::Event, keyboard::KeyCode}, winit_input_helper::WinitInputHelper, }; diff --git a/examples/multipass.rs b/examples/multipass.rs index 6224e245..a6c48809 100644 --- a/examples/multipass.rs +++ b/examples/multipass.rs @@ -5,9 +5,9 @@ use { clap::Parser, glam::{Mat4, Vec3, Vec4, vec3}, inline_spirv::inline_spirv, - screen_13::prelude::*, - screen_13_window::WindowBuilder, std::sync::Arc, + vk_graph::prelude::*, + vk_graph_window::WindowBuilder, }; #[derive(Clone, Copy)] diff --git a/examples/multithread.rs b/examples/multithread.rs index dd66ba33..271154f3 100644 --- a/examples/multithread.rs +++ b/examples/multithread.rs @@ -5,9 +5,6 @@ use { clap::Parser, image::ImageReader, log::info, - screen_13::prelude::*, - screen_13_fx::BitmapFont, - screen_13_window::WindowBuilder, std::{ collections::VecDeque, io::Cursor, @@ -19,6 +16,9 @@ use { thread::{available_parallelism, sleep, spawn}, time::{Duration, Instant}, }, + vk_graph::prelude::*, + vk_graph_fx::BitmapFont, + vk_graph_window::WindowBuilder, }; const COLOR_SUBRESOURCE_LAYER: vk::ImageSubresourceLayers = vk::ImageSubresourceLayers { diff --git a/examples/profile_with_puffin/mod.rs b/examples/profile_with_puffin/mod.rs index bae98e06..0ad58989 100644 --- a/examples/profile_with_puffin/mod.rs +++ b/examples/profile_with_puffin/mod.rs @@ -8,7 +8,7 @@ //! ``` //! //! For more information see: -//! https://github.com/attackgoat/screen-13/blob/master/examples/getting-started.md +//! https://github.com/attackgoat/vk-graph/blob/master/examples/getting-started.md #[cfg(feature = "profile-with-puffin")] use { diff --git a/examples/ray_omni.rs b/examples/ray_omni.rs index 70340d7e..7c148f8b 100644 --- a/examples/ray_omni.rs +++ b/examples/ray_omni.rs @@ -7,8 +7,6 @@ use { inline_spirv::inline_spirv, log::info, meshopt::remap::{generate_vertex_remap, remap_index_buffer, remap_vertex_buffer}, - screen_13::prelude::*, - screen_13_window::WindowBuilder, std::{ env::current_exe, fs::{metadata, write}, @@ -17,6 +15,8 @@ use { sync::Arc, }, tobj::{GPU_LOAD_OPTIONS, load_obj}, + vk_graph::prelude::*, + vk_graph_window::WindowBuilder, }; fn main() -> anyhow::Result<()> { diff --git a/examples/ray_trace.rs b/examples/ray_trace.rs index 53dff06b..0884126e 100644 --- a/examples/ray_trace.rs +++ b/examples/ray_trace.rs @@ -5,10 +5,10 @@ use { clap::Parser, inline_spirv::inline_spirv, log::warn, - screen_13::prelude::*, - screen_13_window::WindowBuilder, std::{io::BufReader, mem::size_of, sync::Arc}, tobj::{GPU_LOAD_OPTIONS, load_mtl_buf, load_obj_buf}, + vk_graph::prelude::*, + vk_graph_window::WindowBuilder, winit::{event::Event, keyboard::KeyCode}, winit_input_helper::WinitInputHelper, }; diff --git a/examples/rt_triangle.rs b/examples/rt_triangle.rs index 6024f7d2..1ad46fbe 100644 --- a/examples/rt_triangle.rs +++ b/examples/rt_triangle.rs @@ -4,9 +4,9 @@ use { bytemuck::{NoUninit, cast_slice}, clap::Parser, inline_spirv::inline_spirv, - screen_13::prelude::*, - screen_13_window::WindowBuilder, std::sync::Arc, + vk_graph::prelude::*, + vk_graph_window::WindowBuilder, }; static SHADER_RAY_GEN: &[u32] = inline_spirv!( diff --git a/examples/shader-toy/Cargo.toml b/examples/shader-toy/Cargo.toml index 44bab96b..2c72ba7f 100644 --- a/examples/shader-toy/Cargo.toml +++ b/examples/shader-toy/Cargo.toml @@ -17,9 +17,9 @@ bytemuck = "1.14" clap = { version = "4.5", features = ["derive"] } pak = "0.5" pretty_env_logger = "0.5" -screen-13 = { path = "../.." } -screen-13-fx = { path = "../../contrib/screen-13-fx" } -screen-13-window = { path = "../../contrib/screen-13-window" } +vk-graph = { path = "../.." } +vk-graph-fx = { path = "../../contrib/vk-graph-fx" } +vk-graph-window = { path = "../../contrib/vk-graph-window" } winit = "0.30" [build-dependencies] diff --git a/examples/shader-toy/src/main.rs b/examples/shader-toy/src/main.rs index ea1de611..83ae3a46 100644 --- a/examples/shader-toy/src/main.rs +++ b/examples/shader-toy/src/main.rs @@ -37,10 +37,10 @@ use { bytemuck::{bytes_of, Pod, Zeroable}, clap::Parser, pak::{Pak, PakBuf}, - screen_13::prelude::*, - screen_13_fx::*, - screen_13_window::WindowBuilder, std::{sync::Arc, time::Instant}, + vk_graph::prelude::*, + vk_graph_fx::*, + vk_graph_window::WindowBuilder, winit::dpi::PhysicalSize, }; diff --git a/examples/skeletal-anim/Cargo.toml b/examples/skeletal-anim/Cargo.toml index 115cfa32..84c60f3b 100644 --- a/examples/skeletal-anim/Cargo.toml +++ b/examples/skeletal-anim/Cargo.toml @@ -12,8 +12,8 @@ clap = { version = "4.5", features = ["derive"] } glam = { version = "0.27", features = ["bytemuck"] } pak = "=0.5.0" pretty_env_logger = "0.5" -screen-13 = { path = "../.." } -screen-13-window = { path = "../../contrib/screen-13-window" } +vk-graph = { path = "../.." } +vk-graph-window = { path = "../../contrib/vk-graph-window" } [build-dependencies] anyhow = "1.0" diff --git a/examples/skeletal-anim/README.md b/examples/skeletal-anim/README.md index 960ddbf1..f6f1be12 100644 --- a/examples/skeletal-anim/README.md +++ b/examples/skeletal-anim/README.md @@ -2,10 +2,10 @@ # Animation Example -Demonstrates loading and rendering skeletal mesh animation using [`pak`] and [`screen-13`]. +Demonstrates loading and rendering skeletal mesh animation using [`pak`] and [`vk-graph`]. [`pak`]: https://github.com/attackgoat/pak -[`screen-13`]: https://github.com/attackgoat/screen-13 +[`vk-graph`]: https://github.com/attackgoat/vk-graph # Assets diff --git a/examples/skeletal-anim/src/main.rs b/examples/skeletal-anim/src/main.rs index 684c0602..5d872600 100644 --- a/examples/skeletal-anim/src/main.rs +++ b/examples/skeletal-anim/src/main.rs @@ -8,8 +8,6 @@ use { model::{Joint, Vertex}, Pak, PakBuf, }, - screen_13::prelude::*, - screen_13_window::{WindowBuilder, WindowError}, std::{ cmp::Ordering, env::current_exe, @@ -18,6 +16,8 @@ use { sync::Arc, time::{Duration, Instant}, }, + vk_graph::prelude::*, + vk_graph_window::{WindowBuilder, WindowError}, }; // This blog has a really good overview of what is happening here: @@ -163,7 +163,7 @@ fn load_texture( assert_eq!(bitmap.height().count_ones(), 1); // NOTE: This is the most basic way to load an image; you probably want to use something like - // screen-13-fx::ImageLoader instead! + // vk-graph-fx::ImageLoader instead! // We will stage the pixels in a host-accessible buffer let buffer = Arc::new(Buffer::create_from_slice( diff --git a/examples/subgroup_ops.rs b/examples/subgroup_ops.rs index bc26363e..eba85130 100644 --- a/examples/subgroup_ops.rs +++ b/examples/subgroup_ops.rs @@ -2,8 +2,8 @@ use { bytemuck::cast_slice, clap::Parser, inline_spirv::inline_spirv, - screen_13::prelude::*, std::{mem::size_of, sync::Arc, time::Instant}, + vk_graph::prelude::*, }; /// Advanced example demonstrating subgroup operations (arithmetic and ballot). diff --git a/examples/transitions.rs b/examples/transitions.rs index eef529b4..fb1b46ea 100644 --- a/examples/transitions.rs +++ b/examples/transitions.rs @@ -4,11 +4,11 @@ use { clap::Parser, image::ImageReader, log::info, - screen_13::prelude::LazyPool, - screen_13_fx::*, - screen_13_imgui::prelude::*, - screen_13_window::WindowBuilder, std::{io::Cursor, time::Instant}, + vk_graph::prelude::LazyPool, + vk_graph_fx::*, + vk_graph_imgui::prelude::*, + vk_graph_window::WindowBuilder, winit::dpi::LogicalSize, }; @@ -16,7 +16,7 @@ fn main() -> anyhow::Result<()> { pretty_env_logger::init(); profile_with_puffin::init(); - // Create Screen 13 things any similar program might need + // Create vk-graph things any similar program might need let args = Args::parse(); let window = WindowBuilder::default() .debug(args.debug) diff --git a/examples/triangle.rs b/examples/triangle.rs index e66654ea..b50246f9 100644 --- a/examples/triangle.rs +++ b/examples/triangle.rs @@ -4,9 +4,9 @@ use { bytemuck::cast_slice, clap::Parser, inline_spirv::inline_spirv, - screen_13::prelude::*, - screen_13_window::{WindowBuilder, WindowError}, std::sync::Arc, + vk_graph::prelude::*, + vk_graph_window::{WindowBuilder, WindowError}, }; // A Vulkan triangle using a graphic pipeline, vertex/fragment shaders, and index/vertex buffers. diff --git a/examples/vertex_layout.rs b/examples/vertex_layout.rs index e43e4e47..760fc065 100644 --- a/examples/vertex_layout.rs +++ b/examples/vertex_layout.rs @@ -5,9 +5,9 @@ use { clap::Parser, half::f16, inline_spirv::inline_spirv, - screen_13::prelude::*, - screen_13_window::{FrameContext, WindowBuilder}, std::{mem::size_of, sync::Arc}, + vk_graph::prelude::*, + vk_graph_window::{FrameContext, WindowBuilder}, }; /// This example draws two triangles using two different vertex formats. diff --git a/examples/vr/Cargo.toml b/examples/vr/Cargo.toml index b67bf1c8..b68eff4f 100644 --- a/examples/vr/Cargo.toml +++ b/examples/vr/Cargo.toml @@ -20,6 +20,6 @@ mikktspace = "0.3" mint = "0.5" openxr = { version = "0.18", features = ["mint", "static"] } pretty_env_logger = "0.5" -screen-13 = { path = "../.." } -screen-13-hot = { path = "../../contrib/screen-13-hot" } +vk-graph = { path = "../.." } +vk-graph-hot = { path = "../../contrib/vk-graph-hot" } tobj = "4.0" diff --git a/examples/vr/src/driver/instance.rs b/examples/vr/src/driver/instance.rs index 1cfca1ab..43d22a6a 100644 --- a/examples/vr/src/driver/instance.rs +++ b/examples/vr/src/driver/instance.rs @@ -1,14 +1,6 @@ use { log::{debug, error}, openxr as xr, - screen_13::driver::{ - ash::{ - self, - vk::{self, Handle as _}, - }, - device::Device, - physical_device::PhysicalDevice, - }, std::{ ffi::c_void, fmt::{Debug, Formatter}, @@ -16,6 +8,14 @@ use { ops::Deref, sync::Arc, }, + vk_graph::driver::{ + ash::{ + self, + vk::{self, Handle as _}, + }, + device::Device, + physical_device::PhysicalDevice, + }, }; pub struct Instance { @@ -51,9 +51,9 @@ impl Instance { } let app_info = xr::ApplicationInfo { - application_name: "screen-13-example-vr", + application_name: "vk-graph-example-vr", application_version: 0, - engine_name: "screen-13-example-vr", + engine_name: "vk-graph-example-vr", engine_version: 0, }; let xr_instance = xr_entry @@ -155,7 +155,7 @@ impl Instance { })?; let vk_instance = vk::Instance::from_raw(vk_instance as _); - screen_13::driver::Instance::load(vk_entry, vk_instance).map_err(|err| { + vk_graph::driver::Instance::load(vk_entry, vk_instance).map_err(|err| { error!("Vulkan instance load: {err}"); InstanceCreateError::VulkanUnsupported diff --git a/examples/vr/src/driver/swapchain.rs b/examples/vr/src/driver/swapchain.rs index e851af4d..0e9db25c 100644 --- a/examples/vr/src/driver/swapchain.rs +++ b/examples/vr/src/driver/swapchain.rs @@ -1,14 +1,14 @@ use { super::Instance, openxr as xr, - screen_13::driver::{ - ash::vk::{self, Handle as _}, - image::{Image, ImageInfo}, - }, std::{ ops::{Deref, DerefMut}, sync::Arc, }, + vk_graph::driver::{ + ash::vk::{self, Handle as _}, + image::{Image, ImageInfo}, + }, }; pub struct Swapchain { diff --git a/examples/vr/src/main.rs b/examples/vr/src/main.rs index a4f9c782..e408a4f1 100644 --- a/examples/vr/src/main.rs +++ b/examples/vr/src/main.rs @@ -7,19 +7,6 @@ use { log::{debug, error, trace}, meshopt::{generate_vertex_remap, remap_index_buffer, remap_vertex_buffer}, openxr::{self as xr, EnvironmentBlendMode, ViewConfigurationType}, - screen_13::{ - driver::{ - ash::vk::{self}, - buffer::{Buffer, BufferInfo}, - device::Device, - graphic::{DepthStencilMode, GraphicPipelineInfo}, - image::{Image, ImageInfo}, - AccessType, - }, - graph::RenderGraph, - pool::{lazy::LazyPool, Pool as _}, - }, - screen_13_hot::{graphic::HotGraphicPipeline, shader::HotShader}, std::{ fs::{metadata, File}, io::BufReader, @@ -33,6 +20,19 @@ use { time::Duration, }, tobj::{load_obj, GPU_LOAD_OPTIONS}, + vk_graph::{ + driver::{ + ash::vk::{self}, + buffer::{Buffer, BufferInfo}, + device::Device, + graphic::{DepthStencilMode, GraphicPipelineInfo}, + image::{Image, ImageInfo}, + AccessType, + }, + graph::RenderGraph, + pool::{lazy::LazyPool, Pool as _}, + }, + vk_graph_hot::{graphic::HotGraphicPipeline, shader::HotShader}, }; // Sets bits with index 0 and 1 for stereoscopic rendering diff --git a/examples/vsm_omni.rs b/examples/vsm_omni.rs index 8f471585..1808ef59 100644 --- a/examples/vsm_omni.rs +++ b/examples/vsm_omni.rs @@ -7,8 +7,6 @@ use { inline_spirv::inline_spirv, log::info, meshopt::remap::{generate_vertex_remap, remap_index_buffer, remap_vertex_buffer}, - screen_13::prelude::*, - screen_13_window::WindowBuilder, std::{ env::current_exe, fs::{metadata, write}, @@ -16,6 +14,8 @@ use { sync::Arc, }, tobj::{GPU_LOAD_OPTIONS, load_obj}, + vk_graph::prelude::*, + vk_graph_window::WindowBuilder, winit::{dpi::LogicalSize, event::Event, keyboard::KeyCode, window::Fullscreen}, winit_input_helper::WinitInputHelper, }; diff --git a/src/driver/accel_struct.rs b/src/driver/accel_struct.rs index d2624fee..d1d367ec 100644 --- a/src/driver/accel_struct.rs +++ b/src/driver/accel_struct.rs @@ -36,9 +36,9 @@ use std::sync::Mutex; /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; -/// # use screen_13::driver::{AccessType, DriverError}; -/// # use screen_13::driver::device::{Device, DeviceInfo}; -/// # use screen_13::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; +/// # use vk_graph::driver::{AccessType, DriverError}; +/// # use vk_graph::driver::device::{Device, DeviceInfo}; +/// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # const SIZE: vk::DeviceSize = 1024; @@ -71,9 +71,9 @@ impl AccelerationStructure { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// const SIZE: vk::DeviceSize = 1024; @@ -142,7 +142,7 @@ impl AccelerationStructure { /// /// # Note /// - /// Used to maintain object state when passing a _Screen 13_-created + /// Used to maintain object state when passing a _vk-graph_-created /// `vk::AccelerationStructureKHR` handle to external code such as [_Ash_] or [_Erupt_] /// bindings. /// @@ -153,9 +153,9 @@ impl AccelerationStructure { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::{AccessType, DriverError}; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; + /// # use vk_graph::driver::{AccessType, DriverError}; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # const SIZE: vk::DeviceSize = 1024; @@ -199,9 +199,9 @@ impl AccelerationStructure { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::{AccessType, DriverError}; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; + /// # use vk_graph::driver::{AccessType, DriverError}; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # const SIZE: vk::DeviceSize = 1024; @@ -241,9 +241,9 @@ impl AccelerationStructure { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::accel_struct::{AccelerationStructure, AccelerationStructureGeometry, AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, DeviceOrHostAddress}; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureGeometry, AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, DeviceOrHostAddress}; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let my_geom_triangles = AccelerationStructureGeometryData::Triangles { diff --git a/src/driver/buffer.rs b/src/driver/buffer.rs index d8ba332a..f37878c6 100644 --- a/src/driver/buffer.rs +++ b/src/driver/buffer.rs @@ -40,9 +40,9 @@ use std::sync::Mutex; /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; -/// # use screen_13::driver::{AccessType, DriverError}; -/// # use screen_13::driver::device::{Device, DeviceInfo}; -/// # use screen_13::driver::buffer::{Buffer, BufferInfo}; +/// # use vk_graph::driver::{AccessType, DriverError}; +/// # use vk_graph::driver::device::{Device, DeviceInfo}; +/// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let info = BufferInfo::device_mem(8, vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS); @@ -77,9 +77,9 @@ impl Buffer { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// const SIZE: vk::DeviceSize = 1024; @@ -187,9 +187,9 @@ impl Buffer { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// const DATA: [u8; 4] = [0xfe, 0xed, 0xbe, 0xef]; @@ -222,7 +222,7 @@ impl Buffer { /// /// # Note /// - /// Used to maintain object state when passing a _Screen 13_-created `vk::Buffer` handle to + /// Used to maintain object state when passing a _vk-graph_-created `vk::Buffer` handle to /// external code such as [_Ash_] or [_Erupt_] bindings. /// /// # Examples @@ -232,9 +232,9 @@ impl Buffer { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::{AccessType, DriverError}; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::buffer::{Buffer, BufferInfo, BufferSubresourceRange}; + /// # use vk_graph::driver::{AccessType, DriverError}; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo, BufferSubresourceRange}; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # const SIZE: vk::DeviceSize = 1024; @@ -297,9 +297,9 @@ impl Buffer { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let info = BufferInfo::host_mem(4, vk::BufferUsageFlags::empty()); @@ -330,9 +330,9 @@ impl Buffer { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let info = BufferInfo::host_mem(4, vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS); @@ -370,9 +370,9 @@ impl Buffer { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # const DATA: [u8; 4] = [0; 4]; @@ -408,9 +408,9 @@ impl Buffer { /// # use std::sync::Arc; /// # use ash::vk; /// # use glam::Mat4; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # const DATA: [u8; 4] = [0; 4]; diff --git a/src/driver/compute.rs b/src/driver/compute.rs index 7569e9c9..52982418 100644 --- a/src/driver/compute.rs +++ b/src/driver/compute.rs @@ -54,10 +54,10 @@ impl ComputePipeline { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::compute::{ComputePipeline, ComputePipelineInfo}; - /// # use screen_13::driver::shader::{Shader}; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; + /// # use vk_graph::driver::shader::{Shader}; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let my_shader_code = [0u8; 1]; diff --git a/src/driver/graphic.rs b/src/driver/graphic.rs index ac8051e7..ccb9dcf8 100644 --- a/src/driver/graphic.rs +++ b/src/driver/graphic.rs @@ -384,10 +384,10 @@ impl GraphicPipeline { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; - /// # use screen_13::driver::shader::Shader; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; + /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let my_frag_code = [0u8; 1]; diff --git a/src/driver/image.rs b/src/driver/image.rs index 536a3827..3b34dd50 100644 --- a/src/driver/image.rs +++ b/src/driver/image.rs @@ -77,9 +77,9 @@ pub(crate) fn image_subresource_range_intersects( /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; -/// # use screen_13::driver::{AccessType, DriverError}; -/// # use screen_13::driver::device::{Device, DeviceInfo}; -/// # use screen_13::driver::image::{Image, ImageInfo}; +/// # use vk_graph::driver::{AccessType, DriverError}; +/// # use vk_graph::driver::device::{Device, DeviceInfo}; +/// # use vk_graph::driver::image::{Image, ImageInfo}; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let info = ImageInfo::image_1d(1, vk::Format::R8_UINT, vk::ImageUsageFlags::STORAGE); @@ -117,9 +117,9 @@ impl Image { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::image::{Image, ImageInfo}; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::image::{Image, ImageInfo}; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); @@ -224,7 +224,7 @@ impl Image { /// /// # Note /// - /// Used to maintain object state when passing a _Screen 13_-created `vk::Image` handle to + /// Used to maintain object state when passing a _vk-graph_-created `vk::Image` handle to /// external code such as [_Ash_] or [_Erupt_] bindings. /// /// # Examples @@ -234,9 +234,9 @@ impl Image { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::{AccessType, DriverError}; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::image::{Image, ImageInfo}; + /// # use vk_graph::driver::{AccessType, DriverError}; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::image::{Image, ImageInfo}; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let info = ImageInfo::image_1d(1, vk::Format::R8_UINT, vk::ImageUsageFlags::STORAGE); diff --git a/src/driver/mod.rs b/src/driver/mod.rs index 6a011fb7..00b572ca 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -1100,12 +1100,12 @@ pub(super) const fn pipeline_stage_access_flags( /// Describes the general category of all graphics driver failure cases. /// -/// In the event of a failure you should follow the _Screen 13_ code to the responsible Vulkan API +/// In the event of a failure you should follow the _vk-graph_ code to the responsible Vulkan API /// and then to the `Ash` stub call; it will generally contain a link to the appropriate /// specification. The specifications provide a table of possible error conditions which can be a /// good starting point to debug the issue. /// -/// Feel free to open an issue on GitHub, [here](https://github.com/attackgoat/screen-13/issues) for +/// Feel free to open an issue on GitHub, [here](https://github.com/attackgoat/vk-graph/issues) for /// help debugging the issue. #[derive(Debug)] pub enum DriverError { diff --git a/src/driver/ray_trace.rs b/src/driver/ray_trace.rs index 9c309780..667b2b36 100644 --- a/src/driver/ray_trace.rs +++ b/src/driver/ray_trace.rs @@ -68,10 +68,10 @@ impl RayTracePipeline { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; - /// # use screen_13::driver::shader::Shader; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; + /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let my_rgen_code = [0u8; 1]; @@ -314,7 +314,7 @@ impl RayTracePipeline { /// # Examples /// /// See - /// [ray_trace.rs](https://github.com/attackgoat/screen-13/blob/master/examples/ray_trace.rs) + /// [ray_trace.rs](https://github.com/attackgoat/vk-graph/blob/master/examples/ray_trace.rs) /// for a detail example which constructs a shader binding table buffer using this function. pub fn group_handle(this: &Self, idx: usize) -> Result<&[u8], DriverError> { let &RayTraceProperties { diff --git a/src/driver/shader.rs b/src/driver/shader.rs index 30decda5..52f6f50f 100644 --- a/src/driver/shader.rs +++ b/src/driver/shader.rs @@ -710,9 +710,9 @@ pub struct Shader { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::shader::{Shader, SpecializationInfo}; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::shader::{Shader, SpecializationInfo}; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let my_shader_code = [0u8; 1]; diff --git a/src/graph/pass_ref.rs b/src/graph/pass_ref.rs index 08ea4bef..5278e03a 100644 --- a/src/graph/pass_ref.rs +++ b/src/graph/pass_ref.rs @@ -60,11 +60,11 @@ pub type DescriptorSetIndex = u32; /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; -/// # use screen_13::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; -/// # use screen_13::driver::DriverError; -/// # use screen_13::driver::device::{Device, DeviceInfo}; -/// # use screen_13::graph::RenderGraph; -/// # use screen_13::driver::shader::Shader; +/// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; +/// # use vk_graph::driver::DriverError; +/// # use vk_graph::driver::device::{Device, DeviceInfo}; +/// # use vk_graph::graph::RenderGraph; +/// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let mut my_graph = RenderGraph::new(); @@ -99,12 +99,12 @@ impl Acceleration<'_> { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::accel_struct::{AccelerationStructure, AccelerationStructureGeometry, AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, AccelerationStructureInfo, DeviceOrHostAddress}; - /// # use screen_13::driver::buffer::{Buffer, BufferInfo}; - /// # use screen_13::graph::RenderGraph; - /// # use screen_13::driver::shader::Shader; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureGeometry, AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, AccelerationStructureInfo, DeviceOrHostAddress}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::graph::RenderGraph; + /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let mut my_graph = RenderGraph::new(); @@ -1021,11 +1021,11 @@ bind!(RayTrace); /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; -/// # use screen_13::driver::DriverError; -/// # use screen_13::driver::device::{Device, DeviceInfo}; -/// # use screen_13::driver::image::{Image, ImageInfo}; -/// # use screen_13::graph::RenderGraph; -/// # use screen_13::graph::node::ImageNode; +/// # use vk_graph::driver::DriverError; +/// # use vk_graph::driver::device::{Device, DeviceInfo}; +/// # use vk_graph::driver::image::{Image, ImageInfo}; +/// # use vk_graph::graph::RenderGraph; +/// # use vk_graph::graph::node::ImageNode; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); @@ -1160,11 +1160,11 @@ impl Index for Bindings<'_> { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; -/// # use screen_13::driver::DriverError; -/// # use screen_13::driver::device::{Device, DeviceInfo}; -/// # use screen_13::driver::compute::{ComputePipeline, ComputePipelineInfo}; -/// # use screen_13::driver::shader::{Shader}; -/// # use screen_13::graph::RenderGraph; +/// # use vk_graph::driver::DriverError; +/// # use vk_graph::driver::device::{Device, DeviceInfo}; +/// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; +/// # use vk_graph::driver::shader::{Shader}; +/// # use vk_graph::graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let info = ComputePipelineInfo::default(); @@ -1213,12 +1213,12 @@ impl Compute<'_> { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::buffer::{Buffer, BufferInfo}; - /// # use screen_13::driver::compute::{ComputePipeline, ComputePipelineInfo}; - /// # use screen_13::driver::shader::{Shader}; - /// # use screen_13::graph::RenderGraph; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; + /// # use vk_graph::driver::shader::{Shader}; + /// # use vk_graph::graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::STORAGE_BUFFER); @@ -1299,12 +1299,12 @@ impl Compute<'_> { /// # use std::sync::Arc; /// # use std::mem::size_of; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::buffer::{Buffer, BufferInfo}; - /// # use screen_13::driver::compute::{ComputePipeline, ComputePipelineInfo}; - /// # use screen_13::driver::shader::{Shader}; - /// # use screen_13::graph::RenderGraph; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; + /// # use vk_graph::driver::shader::{Shader}; + /// # use vk_graph::graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::STORAGE_BUFFER); @@ -1395,12 +1395,12 @@ impl Compute<'_> { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::buffer::{Buffer, BufferInfo}; - /// # use screen_13::driver::compute::{ComputePipeline, ComputePipelineInfo}; - /// # use screen_13::driver::shader::{Shader}; - /// # use screen_13::graph::RenderGraph; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; + /// # use vk_graph::driver::shader::{Shader}; + /// # use vk_graph::graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let info = ComputePipelineInfo::default(); @@ -1457,12 +1457,12 @@ impl Compute<'_> { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::buffer::{Buffer, BufferInfo}; - /// # use screen_13::driver::compute::{ComputePipeline, ComputePipelineInfo}; - /// # use screen_13::driver::shader::{Shader}; - /// # use screen_13::graph::RenderGraph; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; + /// # use vk_graph::driver::shader::{Shader}; + /// # use vk_graph::graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let info = ComputePipelineInfo::default(); @@ -1592,12 +1592,12 @@ impl From<(DescriptorSetIndex, BindingIndex, [BindingOffset; 1])> for Descriptor /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; -/// # use screen_13::driver::DriverError; -/// # use screen_13::driver::device::{Device, DeviceInfo}; -/// # use screen_13::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; -/// # use screen_13::driver::image::{Image, ImageInfo}; -/// # use screen_13::graph::RenderGraph; -/// # use screen_13::driver::shader::Shader; +/// # use vk_graph::driver::DriverError; +/// # use vk_graph::driver::device::{Device, DeviceInfo}; +/// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; +/// # use vk_graph::driver::image::{Image, ImageInfo}; +/// # use vk_graph::graph::RenderGraph; +/// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let my_frag_code = [0u8; 1]; @@ -1634,13 +1634,13 @@ impl Draw<'_> { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::buffer::{Buffer, BufferInfo}; - /// # use screen_13::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; - /// # use screen_13::driver::image::{Image, ImageInfo}; - /// # use screen_13::driver::shader::Shader; - /// # use screen_13::graph::RenderGraph; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; + /// # use vk_graph::driver::image::{Image, ImageInfo}; + /// # use vk_graph::driver::shader::Shader; + /// # use vk_graph::graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let my_frag_code = [0u8; 1]; @@ -1712,13 +1712,13 @@ impl Draw<'_> { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::buffer::{Buffer, BufferInfo}; - /// # use screen_13::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; - /// # use screen_13::driver::image::{Image, ImageInfo}; - /// # use screen_13::driver::shader::Shader; - /// # use screen_13::graph::RenderGraph; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; + /// # use vk_graph::driver::image::{Image, ImageInfo}; + /// # use vk_graph::driver::shader::Shader; + /// # use vk_graph::graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::VERTEX_BUFFER); @@ -1891,13 +1891,13 @@ impl Draw<'_> { /// # use std::sync::Arc; /// # use std::mem::size_of; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::buffer::{Buffer, BufferInfo}; - /// # use screen_13::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; - /// # use screen_13::driver::image::{Image, ImageInfo}; - /// # use screen_13::driver::shader::Shader; - /// # use screen_13::graph::RenderGraph; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; + /// # use vk_graph::driver::image::{Image, ImageInfo}; + /// # use vk_graph::driver::shader::Shader; + /// # use vk_graph::graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let my_frag_code = [0u8; 1]; @@ -2103,12 +2103,12 @@ impl Draw<'_> { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; - /// # use screen_13::driver::image::{Image, ImageInfo}; - /// # use screen_13::graph::RenderGraph; - /// # use screen_13::driver::shader::Shader; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; + /// # use vk_graph::driver::image::{Image, ImageInfo}; + /// # use vk_graph::graph::RenderGraph; + /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let my_frag_code = [0u8; 1]; @@ -2172,12 +2172,12 @@ impl Draw<'_> { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; - /// # use screen_13::driver::image::{Image, ImageInfo}; - /// # use screen_13::graph::RenderGraph; - /// # use screen_13::driver::shader::Shader; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; + /// # use vk_graph::driver::image::{Image, ImageInfo}; + /// # use vk_graph::graph::RenderGraph; + /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let my_frag_code = [0u8; 1]; @@ -4683,11 +4683,11 @@ impl PipelinePassRef<'_, RayTracePipeline> { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; -/// # use screen_13::driver::DriverError; -/// # use screen_13::driver::device::{Device, DeviceInfo}; -/// # use screen_13::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; -/// # use screen_13::driver::shader::Shader; -/// # use screen_13::graph::RenderGraph; +/// # use vk_graph::driver::DriverError; +/// # use vk_graph::driver::device::{Device, DeviceInfo}; +/// # use vk_graph::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; +/// # use vk_graph::driver::shader::Shader; +/// # use vk_graph::graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let info = RayTracePipelineInfo::default(); @@ -4753,12 +4753,12 @@ impl RayTrace<'_> { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::buffer::{Buffer, BufferInfo}; - /// # use screen_13::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; - /// # use screen_13::driver::shader::Shader; - /// # use screen_13::graph::RenderGraph; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; + /// # use vk_graph::driver::shader::Shader; + /// # use vk_graph::graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let shader = [0u8; 1]; @@ -4823,12 +4823,12 @@ impl RayTrace<'_> { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::buffer::{Buffer, BufferInfo}; - /// # use screen_13::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; - /// # use screen_13::driver::shader::Shader; - /// # use screen_13::graph::RenderGraph; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; + /// # use vk_graph::driver::shader::Shader; + /// # use vk_graph::graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let shader = [0u8; 1]; @@ -4920,12 +4920,12 @@ impl RayTrace<'_> { /// ```no_run /// # use std::sync::Arc; /// # use ash::vk; - /// # use screen_13::driver::DriverError; - /// # use screen_13::driver::device::{Device, DeviceInfo}; - /// # use screen_13::driver::buffer::{Buffer, BufferInfo}; - /// # use screen_13::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; - /// # use screen_13::driver::shader::Shader; - /// # use screen_13::graph::RenderGraph; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; + /// # use vk_graph::driver::shader::Shader; + /// # use vk_graph::graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let shader = [0u8; 1]; @@ -4948,7 +4948,7 @@ impl RayTrace<'_> { /// # Ok(()) } /// ``` /// - /// [example]: https://github.com/attackgoat/screen-13/blob/master/examples/ray_trace.rs + /// [example]: https://github.com/attackgoat/vk-graph/blob/master/examples/ray_trace.rs #[allow(clippy::too_many_arguments)] #[profiling::function] pub fn trace_rays( diff --git a/src/lib.rs b/src/lib.rs index 48531c5a..edc61484 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ using the provided [`FrameContext`] closure. The [`EventLoop`] builder handles c of the [`Device`] driver, however you may construct one manually for headless rendering. ```no_run -use screen_13_window::{Window, WindowError}; +use vk_graph_window::{Window, WindowError}; fn main() -> Result<(), WindowError> { let window = Window::new()?; @@ -34,7 +34,7 @@ fn main() -> Result<(), WindowError> { # Resources and Pipelines All resources and pipelines, as well as the driver itself, use shared reference tracking to keep -pointers alive. _Screen 13_ uses `std::sync::Arc` to track references. +pointers alive. _vk-graph_ uses `std::sync::Arc` to track references. ## Information @@ -52,9 +52,9 @@ For example, a typical host-mappable buffer: ```no_run # use std::sync::Arc; # use ash::vk; -# use screen_13::driver::DriverError; -# use screen_13::driver::device::{Device, DeviceInfo}; -# use screen_13::driver::buffer::{Buffer, BufferInfo}; +# use vk_graph::driver::DriverError; +# use vk_graph::driver::device::{Device, DeviceInfo}; +# use vk_graph::driver::buffer::{Buffer, BufferInfo}; # fn main() -> Result<(), DriverError> { # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); let info = BufferInfo::host_mem(1024, vk::BufferUsageFlags::STORAGE_BUFFER); @@ -73,10 +73,10 @@ For example, a graphics pipeline: ```no_run # use std::sync::Arc; # use ash::vk; -# use screen_13::driver::DriverError; -# use screen_13::driver::device::{Device, DeviceInfo}; -# use screen_13::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; -# use screen_13::driver::shader::Shader; +# use vk_graph::driver::DriverError; +# use vk_graph::driver::device::{Device, DeviceInfo}; +# use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; +# use vk_graph::driver::shader::Shader; # fn main() -> Result<(), DriverError> { # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); # let my_frag_code = [0u8; 1]; @@ -102,11 +102,11 @@ For example, leasing an image: ```no_run # use std::sync::Arc; # use ash::vk; -# use screen_13::driver::DriverError; -# use screen_13::driver::device::{Device, DeviceInfo}; -# use screen_13::driver::image::{ImageInfo}; -# use screen_13::pool::{Pool}; -# use screen_13::pool::lazy::{LazyPool}; +# use vk_graph::driver::DriverError; +# use vk_graph::driver::device::{Device, DeviceInfo}; +# use vk_graph::driver::image::{ImageInfo}; +# use vk_graph::pool::{Pool}; +# use vk_graph::pool::lazy::{LazyPool}; # fn main() -> Result<(), DriverError> { # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); let mut pool = LazyPool::new(&device); @@ -118,7 +118,7 @@ let my_image = pool.lease(info)?; # Render Graph Operations -All rendering in _Screen 13_ is performed using a [`RenderGraph`] composed of user-specified passes, +All rendering in _vk-graph_ is performed using a [`RenderGraph`] composed of user-specified passes, which may include pipelines and read/write access to resources. Recorded passes are automatically optimized before submission to the graphics hardware. @@ -140,13 +140,13 @@ it as a node. Bound nodes may only be used with the graphs they were bound to. N ```no_run # use std::sync::Arc; # use ash::vk; -# use screen_13::driver::DriverError; -# use screen_13::driver::device::{Device, DeviceInfo}; -# use screen_13::driver::buffer::{Buffer, BufferInfo}; -# use screen_13::driver::image::{Image, ImageInfo}; -# use screen_13::graph::RenderGraph; -# use screen_13::pool::{Pool}; -# use screen_13::pool::lazy::{LazyPool}; +# use vk_graph::driver::DriverError; +# use vk_graph::driver::device::{Device, DeviceInfo}; +# use vk_graph::driver::buffer::{Buffer, BufferInfo}; +# use vk_graph::driver::image::{Image, ImageInfo}; +# use vk_graph::graph::RenderGraph; +# use vk_graph::pool::{Pool}; +# use vk_graph::pool::lazy::{LazyPool}; # fn main() -> Result<(), DriverError> { # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); # let info = BufferInfo::host_mem(1024, vk::BufferUsageFlags::STORAGE_BUFFER); @@ -175,7 +175,7 @@ println!("{:?}", image); // Arc # Ok(()) } ``` -_Note:_ See [this code](https://github.com/attackgoat/screen-13/blob/master/src/graph/edge.rs#L34) +_Note:_ See [this code](https://github.com/attackgoat/vk-graph/blob/master/src/graph/edge.rs#L34) for all the things that can be bound or unbound from a graph. _Note:_ Once unbound, the node is invalid and should be dropped. @@ -192,13 +192,13 @@ Example: ```no_run # use std::sync::Arc; # use ash::vk; -# use screen_13::driver::DriverError; -# use screen_13::driver::device::{Device, DeviceInfo}; -# use screen_13::driver::buffer::{Buffer, BufferInfo}; -# use screen_13::driver::image::{Image, ImageInfo}; -# use screen_13::graph::RenderGraph; -# use screen_13::pool::{Pool}; -# use screen_13::pool::lazy::{LazyPool}; +# use vk_graph::driver::DriverError; +# use vk_graph::driver::device::{Device, DeviceInfo}; +# use vk_graph::driver::buffer::{Buffer, BufferInfo}; +# use vk_graph::driver::image::{Image, ImageInfo}; +# use vk_graph::graph::RenderGraph; +# use vk_graph::pool::{Pool}; +# use vk_graph::pool::lazy::{LazyPool}; # fn main() -> Result<(), DriverError> { # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); # let info = BufferInfo::host_mem(1024, vk::BufferUsageFlags::STORAGE_BUFFER); @@ -234,11 +234,11 @@ Pipeline instances may be bound to a [`PassRef`] in order to execute the associa ```no_run # use std::sync::Arc; # use ash::vk; -# use screen_13::driver::DriverError; -# use screen_13::driver::device::{Device, DeviceInfo}; -# use screen_13::driver::compute::{ComputePipeline, ComputePipelineInfo}; -# use screen_13::driver::shader::{Shader}; -# use screen_13::graph::RenderGraph; +# use vk_graph::driver::DriverError; +# use vk_graph::driver::device::{Device, DeviceInfo}; +# use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; +# use vk_graph::driver::shader::{Shader}; +# use vk_graph::graph::RenderGraph; # fn main() -> Result<(), DriverError> { # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); # let my_shader_code = [0u8; 1]; @@ -258,7 +258,7 @@ graph ## Image samplers -By default, _Screen 13_ will use "linear repeat-mode" samplers unless a special suffix appears as +By default, _vk-graph_ will use "linear repeat-mode" samplers unless a special suffix appears as part of the name within GLSL or HLSL shader code. The `_sampler_123` suffix should be used where `1`, `2`, and `3` are replaced with: @@ -326,7 +326,7 @@ pub mod pool; mod display; -/// Things which are used in almost every single _Screen 13_ program. +/// Things which are used in almost every single _vk-graph_ program. pub mod prelude { pub use super::{ display::{Display, DisplayError, DisplayInfo, DisplayInfoBuilder, ResolverPool}, diff --git a/src/pool/alias.rs b/src/pool/alias.rs index dad1bf92..d1f7b75c 100644 --- a/src/pool/alias.rs +++ b/src/pool/alias.rs @@ -62,7 +62,7 @@ alias_builder!(ImageInfo => Image); /// /// # Examples /// -/// See [`aliasing.rs`](https://github.com/attackgoat/screen-13/blob/master/examples/aliasing.rs) +/// See [`aliasing.rs`](https://github.com/attackgoat/vk-graph/blob/master/examples/aliasing.rs) pub struct AliasPool { accel_structs: Vec<( AccelerationStructureInfo, diff --git a/src/pool/mod.rs b/src/pool/mod.rs index e5d82959..dcee1e1a 100644 --- a/src/pool/mod.rs +++ b/src/pool/mod.rs @@ -1,6 +1,6 @@ //! Resource leasing and pooling types. //! -//! _Screen 13_ provides caching for acceleration structure, buffer and image resources which may be +//! _vk-graph_ provides caching for acceleration structure, buffer and image resources which may be //! leased from configurable pools using their corresponding information structure. Most programs //! will do fine with a single [`FifoPool`](self::fifo::FifoPool). //! @@ -13,7 +13,7 @@ //! offering a different strategy which balances performance (_more buckets_) with memory efficiency //! (_fewer buckets_). //! -//! _Screen 13_'s pools can be grouped into two major categories: +//! _vk-graph_'s pools can be grouped into two major categories: //! //! * Single-bucket: [`FifoPool`](self::fifo::FifoPool) //! * Multi-bucket: [`LazyPool`](self::lazy::LazyPool), [`HashPool`](self::hash::HashPool) @@ -25,11 +25,11 @@ //! ```no_run //! # use std::sync::Arc; //! # use ash::vk; -//! # use screen_13::driver::DriverError; -//! # use screen_13::driver::device::{Device, DeviceInfo}; -//! # use screen_13::driver::image::{ImageInfo}; -//! # use screen_13::pool::{Pool}; -//! # use screen_13::pool::lazy::{LazyPool}; +//! # use vk_graph::driver::DriverError; +//! # use vk_graph::driver::device::{Device, DeviceInfo}; +//! # use vk_graph::driver::image::{ImageInfo}; +//! # use vk_graph::pool::{Pool}; +//! # use vk_graph::pool::lazy::{LazyPool}; //! # fn main() -> Result<(), DriverError> { //! # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); //! let mut pool = LazyPool::new(&device); From 3bf4dea378c3d96bbaed5916eef27ca91f069905 Mon Sep 17 00:00:00 2001 From: John Wells Date: Fri, 13 Feb 2026 09:46:12 -0500 Subject: [PATCH 02/86] Remove old version --- contrib/screen-13-imgui/Cargo.toml | 14 -- contrib/screen-13-imgui/src/lib.rs | 290 ----------------------------- 2 files changed, 304 deletions(-) delete mode 100644 contrib/screen-13-imgui/Cargo.toml delete mode 100644 contrib/screen-13-imgui/src/lib.rs diff --git a/contrib/screen-13-imgui/Cargo.toml b/contrib/screen-13-imgui/Cargo.toml deleted file mode 100644 index dee5fc06..00000000 --- a/contrib/screen-13-imgui/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "screen-13-imgui" -version = "0.1.0" -authors = ["John Wells "] -edition = "2021" -license = "MIT OR Apache-2.0" -readme = "README.md" - -[dependencies] -bytemuck = "1.24" -imgui = "0.12" -imgui-winit-support = "0.13" -inline-spirv = "0.2" -screen-13 = { path = "../.." } diff --git a/contrib/screen-13-imgui/src/lib.rs b/contrib/screen-13-imgui/src/lib.rs deleted file mode 100644 index af12d4e3..00000000 --- a/contrib/screen-13-imgui/src/lib.rs +++ /dev/null @@ -1,290 +0,0 @@ -pub mod prelude { - pub use super::{imgui, Condition, ImGui, Ui}; -} - -pub use imgui::{self, Condition, Ui}; - -use { - bytemuck::cast_slice, - imgui::{Context, DrawCmd, DrawCmdParams}, - imgui_winit_support::{ - winit::{event::Event, window::Window}, - {HiDpiMode, WinitPlatform}, - }, - inline_spirv::include_spirv, - screen_13::prelude::*, - std::{sync::Arc, time::Duration}, -}; - -#[derive(Debug)] -pub struct ImGui { - context: Context, - font_atlas_image: Option>>, - pipeline: Arc, - platform: WinitPlatform, -} - -impl ImGui { - pub fn new(device: &Arc) -> Self { - let mut context = Context::create(); - let platform = WinitPlatform::new(&mut context); - let pipeline = Arc::new( - GraphicPipeline::create( - device, - GraphicPipelineInfoBuilder::default() - .blend(BlendMode::PRE_MULTIPLIED_ALPHA) - .cull_mode(vk::CullModeFlags::NONE), - [ - Shader::new_vertex(include_spirv!("res/shader/imgui.vert", vert).as_slice()), - Shader::new_fragment(include_spirv!("res/shader/imgui.frag", frag).as_slice()), - ], - ) - .unwrap(), - ); - - Self { - context, - font_atlas_image: None, - pipeline, - platform, - } - } - - // TODO: This produces an image which is RGBA8 UNORM and has STORAGE set. *We* don't need storage here and should instead ask the user what settings to give the output image..... - pub fn draw

( - &mut self, - dt: f32, - events: &[Event<()>], - window: &Window, - pool: &mut P, - render_graph: &mut RenderGraph, - ui_func: impl FnOnce(&mut Ui, &mut P, &mut RenderGraph), - ) -> ImageLeaseNode - where - P: Pool + Pool, - { - let hidpi = self.platform.hidpi_factor(); - - self.platform - .attach_window(self.context.io_mut(), window, HiDpiMode::Default); - - if self.font_atlas_image.is_none() || self.platform.hidpi_factor() != hidpi { - self.lease_font_atlas_image(pool, render_graph); - } - - let io = self.context.io_mut(); - io.update_delta_time(Duration::from_secs_f32(dt)); - - for event in events { - self.platform.handle_event(io, window, event); - } - - self.platform - .prepare_frame(io, window) - .expect("Unable to prepare ImGui frame"); - - // Let the caller draw the GUI - let ui = self.context.frame(); - - ui_func(ui, pool, render_graph); - - self.platform.prepare_render(ui, window); - let draw_data = self.context.render(); - - let image = render_graph.bind_node({ - let mut image = pool - .lease(ImageInfo::image_2d( - window.inner_size().width, - window.inner_size().height, - vk::Format::R8G8B8A8_UNORM, - vk::ImageUsageFlags::COLOR_ATTACHMENT - | vk::ImageUsageFlags::SAMPLED - | vk::ImageUsageFlags::STORAGE - | vk::ImageUsageFlags::TRANSFER_DST - | vk::ImageUsageFlags::TRANSFER_SRC, // TODO: Make TRANSFER_SRC an "extra flags" - )) - .unwrap(); - image.as_mut().name = Some("ImGui Output".to_string()); - - image - }); - let font_atlas_image = render_graph.bind_node(self.font_atlas_image.as_ref().unwrap()); - let display_pos = draw_data.display_pos; - let framebuffer_scale = draw_data.framebuffer_scale; - - if draw_data.draw_lists_count() == 0 { - render_graph.clear_color_image(image); - - return image; - } - - for draw_list in draw_data.draw_lists() { - let indices = cast_slice(draw_list.idx_buffer()); - let mut index_buf = pool - .lease(BufferInfo::host_mem( - indices.len() as _, - vk::BufferUsageFlags::INDEX_BUFFER, - )) - .unwrap(); - - { - Buffer::mapped_slice_mut(&mut index_buf)[0..indices.len()].copy_from_slice(indices); - } - - let index_buf = render_graph.bind_node(index_buf); - - let vertices = draw_list.vtx_buffer(); - let vertex_buf_len = vertices.len() * 20; - let mut vertex_buf = pool - .lease(BufferInfo::host_mem( - vertex_buf_len as _, - vk::BufferUsageFlags::VERTEX_BUFFER, - )) - .unwrap(); - - { - let vertex_buf = Buffer::mapped_slice_mut(&mut vertex_buf); - for (idx, vertex) in vertices.iter().enumerate() { - let offset = idx * 20; - vertex_buf[offset..offset + 8].copy_from_slice(cast_slice(&vertex.pos)); - vertex_buf[offset + 8..offset + 16].copy_from_slice(cast_slice(&vertex.uv)); - vertex_buf[offset + 16..offset + 20].copy_from_slice(&vertex.col); - } - } - - let vertex_buf = render_graph.bind_node(vertex_buf); - - let draw_cmds = draw_list - .commands() - .map(|draw_cmd| match draw_cmd { - DrawCmd::Elements { - count, - cmd_params: - DrawCmdParams { - clip_rect, - idx_offset, - vtx_offset, - .. - }, - } => (count, clip_rect, idx_offset, vtx_offset), - _ => unimplemented!(), - }) - .collect::>(); - - let window_width = - self.platform.hidpi_factor() as f32 / window.inner_size().width as f32; - let window_height = - self.platform.hidpi_factor() as f32 / window.inner_size().height as f32; - - render_graph - .begin_pass("imgui") - .bind_pipeline(&self.pipeline) - .access_node(index_buf, AccessType::IndexBuffer) - .access_node(vertex_buf, AccessType::VertexBuffer) - .read_descriptor(0, font_atlas_image) - .clear_color(0, image) - .store_color(0, image) - .record_subpass(move |subpass, _| { - subpass - .push_constants_offset(0, &window_width.to_ne_bytes()) - .push_constants_offset(4, &window_height.to_ne_bytes()) - .bind_index_buffer(index_buf, vk::IndexType::UINT16) - .bind_vertex_buffer(vertex_buf); - - for (index_count, clip_rect, first_index, vertex_offset) in draw_cmds { - let clip_rect = [ - (clip_rect[0] - display_pos[0]) * framebuffer_scale[0], - (clip_rect[1] - display_pos[1]) * framebuffer_scale[1], - (clip_rect[2] - display_pos[0]) * framebuffer_scale[0], - (clip_rect[3] - display_pos[1]) * framebuffer_scale[1], - ]; - let x = clip_rect[0].floor() as i32; - let y = clip_rect[1].floor() as i32; - let width = (clip_rect[2] - clip_rect[0]).ceil() as u32; - let height = (clip_rect[3] - clip_rect[1]).ceil() as u32; - subpass.set_scissor(x, y, width, height); - subpass.draw_indexed( - index_count as _, - 1, - first_index as _, - vertex_offset as _, - 0, - ); - } - }); - } - - image - } - - fn lease_font_atlas_image

(&mut self, pool: &mut P, render_graph: &mut RenderGraph) - where - P: Pool + Pool, - { - use imgui::{FontConfig, FontGlyphRanges, FontSource}; - - let hidpi_factor = self.platform.hidpi_factor(); - self.context.io_mut().font_global_scale = (1.0 / hidpi_factor) as f32; - - let font_size = (14.0 * hidpi_factor) as f32; - let fonts = self.context.fonts(); - fonts.clear_fonts(); - fonts.add_font(&[ - FontSource::TtfData { - data: include_bytes!("../res/font/roboto/roboto-regular.ttf"), - size_pixels: font_size, - config: Some(FontConfig { - rasterizer_multiply: 2.0, - glyph_ranges: FontGlyphRanges::japanese(), - ..FontConfig::default() - }), - }, - FontSource::TtfData { - data: include_bytes!("../res/font/mplus-1p/mplus-1p-regular.ttf"), - size_pixels: font_size, - config: Some(FontConfig { - oversample_h: 2, - oversample_v: 2, - // Range of glyphs to rasterize - glyph_ranges: FontGlyphRanges::japanese(), - ..FontConfig::default() - }), - }, - ]); - - let texture = fonts.build_rgba32_texture(); // TODO: Fix fb channel writes and use alpha8! - let temp_buf_len = texture.data.len(); - let mut temp_buf = pool - .lease(BufferInfo::host_mem( - temp_buf_len as _, - vk::BufferUsageFlags::TRANSFER_SRC, - )) - .unwrap(); - - { - let temp_buf = Buffer::mapped_slice_mut(&mut temp_buf); - temp_buf[0..temp_buf_len].copy_from_slice(texture.data); - } - - let temp_buf = render_graph.bind_node(temp_buf); - let image = render_graph.bind_node({ - let mut image = pool - .lease(ImageInfo::image_2d( - texture.width, - texture.height, - vk::Format::R8G8B8A8_UNORM, - vk::ImageUsageFlags::SAMPLED - | vk::ImageUsageFlags::STORAGE - | vk::ImageUsageFlags::TRANSFER_DST, - )) - .unwrap(); - image.as_mut().name = Some("ImGui Font Atlas".to_string()); - - image - }); - - render_graph.copy_buffer_to_image(temp_buf, image); - - self.font_atlas_image = Some(render_graph.unbind_node(image)); - } -} From 719df4c16042d376efdf8ce07774e9779410c5bb Mon Sep 17 00:00:00 2001 From: John Wells Date: Fri, 13 Feb 2026 11:50:41 -0500 Subject: [PATCH 03/86] switch glsl compilers --- Cargo.toml | 2 +- contrib/vk-graph-egui/Cargo.toml | 2 +- .../shaders/{frag.glsl => egui.frag} | 0 .../shaders/{vert.glsl => egui.vert} | 1 + contrib/vk-graph-egui/src/lib.rs | 11 +- contrib/vk-graph-fx/Cargo.toml | 4 +- contrib/vk-graph-fx/src/bitmap_font.rs | 26 +- contrib/vk-graph-fx/src/image_loader.rs | 9 +- contrib/vk-graph-fx/src/presenter.rs | 16 +- contrib/vk-graph-fx/src/transition.rs | 212 ++++++-------- contrib/vk-graph-hot/src/shader.rs | 4 +- contrib/vk-graph-imgui/Cargo.toml | 2 +- contrib/vk-graph-imgui/src/lib.rs | 6 +- examples/bindless.rs | 14 +- examples/debugger.rs | 6 +- examples/font_bmp.rs | 7 +- examples/fuzzer.rs | 274 ++++++++++-------- examples/image_sampler.rs | 86 +++--- examples/min_max.rs | 12 +- examples/mip_compute.rs | 11 +- examples/mip_graphic.rs | 26 +- examples/msaa.rs | 14 +- examples/multipass.rs | 65 ++--- examples/ray_omni.rs | 17 +- examples/ray_trace.rs | 34 +-- examples/rt_triangle.rs | 26 +- examples/subgroup_ops.rs | 18 +- examples/triangle.rs | 14 +- examples/vertex_layout.rs | 45 +-- examples/vsm_omni.rs | 68 ++--- src/driver/compute.rs | 5 +- src/driver/graphic.rs | 8 +- src/driver/ray_trace.rs | 8 +- src/driver/shader.rs | 5 +- src/graph/pass_ref.rs | 50 ++-- 35 files changed, 543 insertions(+), 565 deletions(-) rename contrib/vk-graph-egui/shaders/{frag.glsl => egui.frag} (100%) rename contrib/vk-graph-egui/shaders/{vert.glsl => egui.vert} (96%) diff --git a/Cargo.toml b/Cargo.toml index deaec9b2..829b8eac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,6 @@ glam = { version = "0.30", features = ["bytemuck"] } half = { version = "2.4", features = ["bytemuck"] } hassle-rs = "0.11" image = "0.25" -inline-spirv = "0.2" log = "0.4" meshopt = "0.2" polyhedron-ops = ">=0.2, <=0.2.4" @@ -58,6 +57,7 @@ vk-graph-fx = { path = "contrib/vk-graph-fx" } vk-graph-imgui = { path = "contrib/vk-graph-imgui" } vk-graph-egui = { path = "contrib/vk-graph-egui" } vk-graph-window = { path = "contrib/vk-graph-window" } +vk-shader-macros = "0.2" tobj = "4.0" winit = "0.30" winit_input_helper = { git = "https://github.com/stefnotch/winit_input_helper.git", rev = "6e76a79d01ce836c01b9cdeaa98846a6f0955dc4" } #"0.16" diff --git a/contrib/vk-graph-egui/Cargo.toml b/contrib/vk-graph-egui/Cargo.toml index d229dc5b..b2e94c90 100644 --- a/contrib/vk-graph-egui/Cargo.toml +++ b/contrib/vk-graph-egui/Cargo.toml @@ -11,6 +11,6 @@ bytemuck = "1.14" # TODO: Waiting for egui to update winit version egui = { git = "https://github.com/emilk/egui.git", rev = "3777b8d2741f298eaa1409dc08062902f7541990" } #{ version = "0.28", features = ["bytemuck"] } egui-winit = { git = "https://github.com/emilk/egui.git", rev = "3777b8d2741f298eaa1409dc08062902f7541990" } #"0.28" -inline-spirv = "0.2" vk-graph = { path = "../.." } vk-graph-fx = { path = "../vk-graph-fx" } +vk-shader-macros = "0.2" diff --git a/contrib/vk-graph-egui/shaders/frag.glsl b/contrib/vk-graph-egui/shaders/egui.frag similarity index 100% rename from contrib/vk-graph-egui/shaders/frag.glsl rename to contrib/vk-graph-egui/shaders/egui.frag diff --git a/contrib/vk-graph-egui/shaders/vert.glsl b/contrib/vk-graph-egui/shaders/egui.vert similarity index 96% rename from contrib/vk-graph-egui/shaders/vert.glsl rename to contrib/vk-graph-egui/shaders/egui.vert index 8f3611e2..b21983c6 100644 --- a/contrib/vk-graph-egui/shaders/vert.glsl +++ b/contrib/vk-graph-egui/shaders/egui.vert @@ -1,4 +1,5 @@ #version 450 +#pragma shader_stage(vertex) layout(location = 0) in vec2 i_pos; layout(location = 1) in vec2 i_uv; diff --git a/contrib/vk-graph-egui/src/lib.rs b/contrib/vk-graph-egui/src/lib.rs index d62151ec..8aae6607 100644 --- a/contrib/vk-graph-egui/src/lib.rs +++ b/contrib/vk-graph-egui/src/lib.rs @@ -10,6 +10,7 @@ use { egui_winit::winit::{event::Event, window::Window}, std::{borrow::Cow, collections::HashMap, sync::Arc}, vk_graph::prelude::*, + vk_shader_macros::include_glsl, }; pub struct Egui { @@ -43,14 +44,8 @@ impl Egui { }) .cull_mode(vk::CullModeFlags::NONE), [ - Shader::new_vertex( - inline_spirv::include_spirv!("shaders/vert.glsl", vert, vulkan1_2) - .as_slice(), - ), - Shader::new_fragment( - inline_spirv::include_spirv!("shaders/frag.glsl", frag, vulkan1_2) - .as_slice(), - ), + Shader::new_vertex(include_glsl!("shaders/egui.vert").as_slice()), + Shader::new_fragment(include_glsl!("shaders/egui.frag").as_slice()), ], ) .unwrap(), diff --git a/contrib/vk-graph-fx/Cargo.toml b/contrib/vk-graph-fx/Cargo.toml index e96400a2..09946a29 100644 --- a/contrib/vk-graph-fx/Cargo.toml +++ b/contrib/vk-graph-fx/Cargo.toml @@ -26,8 +26,8 @@ matte-modes = [] bmfont = { version = "0.3", default-features = false } bytemuck = "1.14" parking_lot = "0.12" -inline-spirv = "0.2" log = "0.4" -vk-graph = { path = "../.."} anyhow = "1.0" glam = "0.27" +vk-graph = { path = "../.."} +vk-shader-macros = "0.2" diff --git a/contrib/vk-graph-fx/src/bitmap_font.rs b/contrib/vk-graph-fx/src/bitmap_font.rs index 9fe70825..9ee23869 100644 --- a/contrib/vk-graph-fx/src/bitmap_font.rs +++ b/contrib/vk-graph-fx/src/bitmap_font.rs @@ -3,9 +3,9 @@ use { bmfont::BMFont, bytemuck::{cast, cast_slice}, glam::{vec3, Mat4}, - inline_spirv::include_spirv, std::sync::Arc, vk_graph::prelude::*, + vk_shader_macros::include_glsl, }; type Color = [u8; 4]; @@ -42,20 +42,16 @@ impl BitmapFont { device, GraphicPipelineInfoBuilder::default().blend(BlendMode::ALPHA), [ - Shader::new_vertex( - include_spirv!("res/shader/graphic/font.vert", vert).as_slice(), - ), - Shader::new_fragment( - include_spirv!("res/shader/graphic/font.frag", frag).as_slice(), - ) - .specialization_info(SpecializationInfo::new( - [vk::SpecializationMapEntry { - constant_id: 0, - offset: 0, - size: 4, - }], - num_pages.to_ne_bytes(), - )), + Shader::new_vertex(include_glsl!("res/shader/graphic/font.vert").as_slice()), + Shader::new_fragment(include_glsl!("res/shader/graphic/font.frag").as_slice()) + .specialization_info(SpecializationInfo::new( + [vk::SpecializationMapEntry { + constant_id: 0, + offset: 0, + size: 4, + }], + num_pages.to_ne_bytes(), + )), ], ) .context("Unable to create bitmap font pipeline")?, diff --git a/contrib/vk-graph-fx/src/image_loader.rs b/contrib/vk-graph-fx/src/image_loader.rs index 98fc13df..7d378236 100644 --- a/contrib/vk-graph-fx/src/image_loader.rs +++ b/contrib/vk-graph-fx/src/image_loader.rs @@ -1,6 +1,6 @@ use { - super::BitmapFont, anyhow::Context, bmfont::BMFont, inline_spirv::include_spirv, log::info, - std::sync::Arc, vk_graph::prelude::*, + super::BitmapFont, anyhow::Context, bmfont::BMFont, log::info, std::sync::Arc, + vk_graph::prelude::*, vk_shader_macros::include_glsl, }; #[cfg(debug_assertions)] @@ -42,15 +42,14 @@ impl ImageLoader { device, ComputePipelineInfo::default(), Shader::new_compute( - include_spirv!("res/shader/compute/decode_bitmap_r_rg.comp", comp).as_slice(), + include_glsl!("res/shader/compute/decode_bitmap_r_rg.comp").as_slice(), ), )?), decode_rgb_rgba: Arc::new(ComputePipeline::create( device, ComputePipelineInfo::default(), Shader::new_compute( - include_spirv!("res/shader/compute/decode_bitmap_rgb_rgba.comp", comp) - .as_slice(), + include_glsl!("res/shader/compute/decode_bitmap_rgb_rgba.comp").as_slice(), ), )?), device: Arc::clone(device), diff --git a/contrib/vk-graph-fx/src/presenter.rs b/contrib/vk-graph-fx/src/presenter.rs index 4c72080e..8b949d24 100644 --- a/contrib/vk-graph-fx/src/presenter.rs +++ b/contrib/vk-graph-fx/src/presenter.rs @@ -1,9 +1,9 @@ use { bytemuck::cast_slice, glam::{vec3, Mat4}, - inline_spirv::include_spirv, std::sync::Arc, vk_graph::prelude::*, + vk_shader_macros::include_glsl, }; pub struct ComputePresenter([Arc; 2]); @@ -13,16 +13,12 @@ impl ComputePresenter { let pipeline1 = Arc::new(ComputePipeline::create( device, ComputePipelineInfo::default(), - Shader::new_compute( - include_spirv!("res/shader/compute/present1.comp", comp).as_slice(), - ), + Shader::new_compute(include_glsl!("res/shader/compute/present1.comp").as_slice()), )?); let pipeline2 = Arc::new(ComputePipeline::create( device, ComputePipelineInfo::default(), - Shader::new_compute( - include_spirv!("res/shader/compute/present2.comp", comp).as_slice(), - ), + Shader::new_compute(include_glsl!("res/shader/compute/present2.comp").as_slice()), )?); Ok(Self([pipeline1, pipeline2])) @@ -88,11 +84,9 @@ impl GraphicPresenter { device, GraphicPipelineInfo::default(), [ - Shader::new_vertex( - include_spirv!("res/shader/graphic/present.vert", vert).as_slice(), - ), + Shader::new_vertex(include_glsl!("res/shader/graphic/present.vert").as_slice()), Shader::new_fragment( - include_spirv!("res/shader/graphic/present.frag", frag).as_slice(), + include_glsl!("res/shader/graphic/present.frag").as_slice(), ), ], )?), diff --git a/contrib/vk-graph-fx/src/transition.rs b/contrib/vk-graph-fx/src/transition.rs index 2c5f3976..b2f20414 100644 --- a/contrib/vk-graph-fx/src/transition.rs +++ b/contrib/vk-graph-fx/src/transition.rs @@ -3,10 +3,10 @@ // use. use { - inline_spirv::include_spirv, log::trace, std::{collections::HashMap, sync::Arc}, vk_graph::prelude::*, + vk_shader_macros::include_glsl, }; #[derive(Clone, Copy, Debug)] @@ -485,295 +485,259 @@ impl TransitionPipeline { ComputePipelineInfo::default(), Shader::new_compute(match transition_ty { TransitionType::Angular => { - include_spirv!("res/shader/transition/angular.comp", comp).as_slice() + include_glsl!("res/shader/transition/angular.comp").as_slice() } TransitionType::Bounce => { - include_spirv!("res/shader/transition/bounce.comp", comp).as_slice() + include_glsl!("res/shader/transition/bounce.comp").as_slice() } TransitionType::BowTieHorizontal => { - include_spirv!("res/shader/transition/bow_tie_horizontal.comp", comp) + include_glsl!("res/shader/transition/bow_tie_horizontal.comp") .as_slice() } TransitionType::BowTieVertical => { - include_spirv!("res/shader/transition/bow_tie_vertical.comp", comp) + include_glsl!("res/shader/transition/bow_tie_vertical.comp").as_slice() + } + TransitionType::BowTieWithParameter => { + include_glsl!("res/shader/transition/bow_tie_with_parameter.comp",) .as_slice() } - TransitionType::BowTieWithParameter => include_spirv!( - "res/shader/transition/bow_tie_with_parameter.comp", - comp - ) - .as_slice(), TransitionType::Burn => { - include_spirv!("res/shader/transition/burn.comp", comp).as_slice() + include_glsl!("res/shader/transition/burn.comp").as_slice() } - TransitionType::ButterflyWaveScrawler => include_spirv!( - "res/shader/transition/butterfly_wave_scrawler.comp", - comp - ) - .as_slice(), - TransitionType::CannabisLeaf => { - include_spirv!("res/shader/transition/cannabis_leaf.comp", comp) + TransitionType::ButterflyWaveScrawler => { + include_glsl!("res/shader/transition/butterfly_wave_scrawler.comp",) .as_slice() } + TransitionType::CannabisLeaf => { + include_glsl!("res/shader/transition/cannabis_leaf.comp").as_slice() + } TransitionType::Circle => { - include_spirv!("res/shader/transition/circle.comp", comp).as_slice() + include_glsl!("res/shader/transition/circle.comp").as_slice() } TransitionType::CircleCrop => { - include_spirv!("res/shader/transition/circle_crop.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/circle_crop.comp").as_slice() } TransitionType::CircleOpen => { - include_spirv!("res/shader/transition/circle_open.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/circle_open.comp").as_slice() } TransitionType::ColorDistance => { - include_spirv!("res/shader/transition/color_distance.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/color_distance.comp").as_slice() } TransitionType::ColorPhase => { - include_spirv!("res/shader/transition/color_phase.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/color_phase.comp").as_slice() } TransitionType::CoordFromIn => { - include_spirv!("res/shader/transition/coord_from_in.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/coord_from_in.comp").as_slice() } TransitionType::CrazyParametricFun => { - include_spirv!("res/shader/transition/crazy_parametric_fun.comp", comp) + include_glsl!("res/shader/transition/crazy_parametric_fun.comp") .as_slice() } TransitionType::Crosshatch => { - include_spirv!("res/shader/transition/crosshatch.comp", comp).as_slice() + include_glsl!("res/shader/transition/crosshatch.comp").as_slice() } TransitionType::CrossWarp => { - include_spirv!("res/shader/transition/cross_warp.comp", comp).as_slice() + include_glsl!("res/shader/transition/cross_warp.comp").as_slice() } TransitionType::CrossZoom => { - include_spirv!("res/shader/transition/cross_zoom.comp", comp).as_slice() + include_glsl!("res/shader/transition/cross_zoom.comp").as_slice() } TransitionType::Cube => { - include_spirv!("res/shader/transition/cube.comp", comp).as_slice() + include_glsl!("res/shader/transition/cube.comp").as_slice() } TransitionType::Directional => { - include_spirv!("res/shader/transition/directional.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/directional.comp").as_slice() } TransitionType::DirectionalEasing => { - include_spirv!("res/shader/transition/directional_easing.comp", comp) + include_glsl!("res/shader/transition/directional_easing.comp") .as_slice() } TransitionType::DirectionalWarp => { - include_spirv!("res/shader/transition/directional_warp.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/directional_warp.comp").as_slice() } TransitionType::DirectionalWipe => { - include_spirv!("res/shader/transition/directional_wipe.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/directional_wipe.comp").as_slice() } TransitionType::Displacement => { - include_spirv!("res/shader/transition/displacement.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/displacement.comp").as_slice() } TransitionType::DoomScreen => { - include_spirv!("res/shader/transition/doom_screen.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/doom_screen.comp").as_slice() } TransitionType::Doorway => { - include_spirv!("res/shader/transition/doorway.comp", comp).as_slice() + include_glsl!("res/shader/transition/doorway.comp").as_slice() } TransitionType::Dreamy => { - include_spirv!("res/shader/transition/dreamy.comp", comp).as_slice() + include_glsl!("res/shader/transition/dreamy.comp").as_slice() } TransitionType::DreamyZoom => { - include_spirv!("res/shader/transition/dreamy_zoom.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/dreamy_zoom.comp").as_slice() } TransitionType::FadeColor => { - include_spirv!("res/shader/transition/fade_color.comp", comp).as_slice() + include_glsl!("res/shader/transition/fade_color.comp").as_slice() } TransitionType::Fade => { - include_spirv!("res/shader/transition/fade.comp", comp).as_slice() + include_glsl!("res/shader/transition/fade.comp").as_slice() } TransitionType::FadeGrayscale => { - include_spirv!("res/shader/transition/fade_grayscale.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/fade_grayscale.comp").as_slice() } TransitionType::FilmBurn => { - include_spirv!("res/shader/transition/film_burn.comp", comp).as_slice() + include_glsl!("res/shader/transition/film_burn.comp").as_slice() } TransitionType::Flyeye => { - include_spirv!("res/shader/transition/flyeye.comp", comp).as_slice() + include_glsl!("res/shader/transition/flyeye.comp").as_slice() } TransitionType::GlitchDisplace => { - include_spirv!("res/shader/transition/glitch_displace.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/glitch_displace.comp").as_slice() } TransitionType::GlitchMemories => { - include_spirv!("res/shader/transition/glitch_memories.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/glitch_memories.comp").as_slice() } TransitionType::GridFlip => { - include_spirv!("res/shader/transition/grid_flip.comp", comp).as_slice() + include_glsl!("res/shader/transition/grid_flip.comp").as_slice() } TransitionType::Heart => { - include_spirv!("res/shader/transition/heart.comp", comp).as_slice() + include_glsl!("res/shader/transition/heart.comp").as_slice() } TransitionType::Hexagonalize => { - include_spirv!("res/shader/transition/hexagonalize.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/hexagonalize.comp").as_slice() } TransitionType::InvertedPageCurl => { - include_spirv!("res/shader/transition/inverted_page_curl.comp", comp) + include_glsl!("res/shader/transition/inverted_page_curl.comp") .as_slice() } TransitionType::Kaleidoscope => { - include_spirv!("res/shader/transition/kaleidoscope.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/kaleidoscope.comp").as_slice() } TransitionType::LeftRight => { - include_spirv!("res/shader/transition/left_right.comp", comp).as_slice() + include_glsl!("res/shader/transition/left_right.comp").as_slice() } TransitionType::LinearBlur => { - include_spirv!("res/shader/transition/linear_blur.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/linear_blur.comp").as_slice() } TransitionType::Luma => { - include_spirv!("res/shader/transition/luma.comp", comp).as_slice() + include_glsl!("res/shader/transition/luma.comp").as_slice() } TransitionType::LuminanceMelt => { - include_spirv!("res/shader/transition/luminance_melt.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/luminance_melt.comp").as_slice() } TransitionType::Morph => { - include_spirv!("res/shader/transition/morph.comp", comp).as_slice() + include_glsl!("res/shader/transition/morph.comp").as_slice() } TransitionType::Mosaic => { - include_spirv!("res/shader/transition/mosaic.comp", comp).as_slice() + include_glsl!("res/shader/transition/mosaic.comp").as_slice() } TransitionType::Multiply => { - include_spirv!("res/shader/transition/multiply.comp", comp).as_slice() + include_glsl!("res/shader/transition/multiply.comp").as_slice() } TransitionType::Overexposure => { - include_spirv!("res/shader/transition/overexposure.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/overexposure.comp").as_slice() } TransitionType::Perlin => { - include_spirv!("res/shader/transition/perlin.comp", comp).as_slice() + include_glsl!("res/shader/transition/perlin.comp").as_slice() } TransitionType::Pinwheel => { - include_spirv!("res/shader/transition/pinwheel.comp", comp).as_slice() + include_glsl!("res/shader/transition/pinwheel.comp").as_slice() } TransitionType::Pixelize => { - include_spirv!("res/shader/transition/pixelize.comp", comp).as_slice() + include_glsl!("res/shader/transition/pixelize.comp").as_slice() } TransitionType::PolarFunction => { - include_spirv!("res/shader/transition/polar_function.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/polar_function.comp").as_slice() } TransitionType::PolkaDotsCurtain => { - include_spirv!("res/shader/transition/polka_dots_curtain.comp", comp) + include_glsl!("res/shader/transition/polka_dots_curtain.comp") .as_slice() } TransitionType::PowerKaleido => { - include_spirv!("res/shader/transition/power_kaleido.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/power_kaleido.comp").as_slice() } TransitionType::Radial => { - include_spirv!("res/shader/transition/radial.comp", comp).as_slice() + include_glsl!("res/shader/transition/radial.comp").as_slice() } TransitionType::RandomNoisex => { - include_spirv!("res/shader/transition/random_noisex.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/random_noisex.comp").as_slice() } TransitionType::RandomSquares => { - include_spirv!("res/shader/transition/random_squares.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/random_squares.comp").as_slice() } TransitionType::Ripple => { - include_spirv!("res/shader/transition/ripple.comp", comp).as_slice() + include_glsl!("res/shader/transition/ripple.comp").as_slice() } TransitionType::Rotate => { - include_spirv!("res/shader/transition/rotate.comp", comp).as_slice() + include_glsl!("res/shader/transition/rotate.comp").as_slice() } TransitionType::RotateScale => { - include_spirv!("res/shader/transition/rotate_scale.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/rotate_scale.comp").as_slice() } TransitionType::ScaleIn => { - include_spirv!("res/shader/transition/scale_in.comp", comp).as_slice() + include_glsl!("res/shader/transition/scale_in.comp").as_slice() } TransitionType::SimpleZoom => { - include_spirv!("res/shader/transition/simple_zoom.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/simple_zoom.comp").as_slice() } TransitionType::SquaresWire => { - include_spirv!("res/shader/transition/squares_wire.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/squares_wire.comp").as_slice() } TransitionType::Squeeze => { - include_spirv!("res/shader/transition/squeeze.comp", comp).as_slice() + include_glsl!("res/shader/transition/squeeze.comp").as_slice() } TransitionType::StereoViewer => { - include_spirv!("res/shader/transition/stereo_viewer.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/stereo_viewer.comp").as_slice() } TransitionType::Swap => { - include_spirv!("res/shader/transition/swap.comp", comp).as_slice() + include_glsl!("res/shader/transition/swap.comp").as_slice() } TransitionType::Swirl => { - include_spirv!("res/shader/transition/swirl.comp", comp).as_slice() + include_glsl!("res/shader/transition/swirl.comp").as_slice() } TransitionType::TangentMotionBlur => { - include_spirv!("res/shader/transition/tangent_motion_blur.comp", comp) + include_glsl!("res/shader/transition/tangent_motion_blur.comp") .as_slice() } TransitionType::TopBottom => { - include_spirv!("res/shader/transition/top_bottom.comp", comp).as_slice() + include_glsl!("res/shader/transition/top_bottom.comp").as_slice() } TransitionType::TvStatic => { - include_spirv!("res/shader/transition/tv_static.comp", comp).as_slice() + include_glsl!("res/shader/transition/tv_static.comp").as_slice() } TransitionType::UndulatingBurnOut => { - include_spirv!("res/shader/transition/undulating_burn_out.comp", comp) + include_glsl!("res/shader/transition/undulating_burn_out.comp") .as_slice() } TransitionType::WaterDrop => { - include_spirv!("res/shader/transition/water_drop.comp", comp).as_slice() + include_glsl!("res/shader/transition/water_drop.comp").as_slice() } TransitionType::Wind => { - include_spirv!("res/shader/transition/wind.comp", comp).as_slice() + include_glsl!("res/shader/transition/wind.comp").as_slice() } TransitionType::WindowBlinds => { - include_spirv!("res/shader/transition/window_blinds.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/window_blinds.comp").as_slice() } TransitionType::WindowSlice => { - include_spirv!("res/shader/transition/window_slice.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/window_slice.comp").as_slice() } TransitionType::WipeDown => { - include_spirv!("res/shader/transition/wipe_down.comp", comp).as_slice() + include_glsl!("res/shader/transition/wipe_down.comp").as_slice() } TransitionType::WipeLeft => { - include_spirv!("res/shader/transition/wipe_left.comp", comp).as_slice() + include_glsl!("res/shader/transition/wipe_left.comp").as_slice() } TransitionType::WipeRight => { - include_spirv!("res/shader/transition/wipe_right.comp", comp).as_slice() + include_glsl!("res/shader/transition/wipe_right.comp").as_slice() } TransitionType::WipeUp => { - include_spirv!("res/shader/transition/wipe_up.comp", comp).as_slice() + include_glsl!("res/shader/transition/wipe_up.comp").as_slice() } TransitionType::ZoomInCircles => { - include_spirv!("res/shader/transition/zoom_in_circles.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/zoom_in_circles.comp").as_slice() } TransitionType::ZoomLeftWipe => { - include_spirv!("res/shader/transition/zoom_left_wipe.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/zoom_left_wipe.comp").as_slice() } TransitionType::ZoomRightWipe => { - include_spirv!("res/shader/transition/zoom_right_wipe.comp", comp) - .as_slice() + include_glsl!("res/shader/transition/zoom_right_wipe.comp").as_slice() } }), ) diff --git a/contrib/vk-graph-hot/src/shader.rs b/contrib/vk-graph-hot/src/shader.rs index 7a9df7dc..e51ff352 100644 --- a/contrib/vk-graph-hot/src/shader.rs +++ b/contrib/vk-graph-hot/src/shader.rs @@ -51,7 +51,7 @@ pub struct HotShader { /// Basic usage (GLSL): /// /// ``` - /// # inline_spirv::inline_spirv!(r#" + /// # vk_shader_macros::glsl!(kind: comp, r#" /// #version 460 core /// /// // Defaults to 6 if not set using HotShader specialization_info! @@ -63,7 +63,7 @@ pub struct HotShader { /// { /// // Code uses MY_COUNT number of my_samplers here /// } - /// # "#, comp); + /// # "#); /// ``` /// /// ```no_run diff --git a/contrib/vk-graph-imgui/Cargo.toml b/contrib/vk-graph-imgui/Cargo.toml index 7b397e69..1d52f072 100644 --- a/contrib/vk-graph-imgui/Cargo.toml +++ b/contrib/vk-graph-imgui/Cargo.toml @@ -10,5 +10,5 @@ readme = "README.md" bytemuck = "1.24" imgui = "0.12" imgui-winit-support = "0.13" -inline-spirv = "0.2" vk-graph = { path = "../.." } +vk-shader-macros = "0.2" diff --git a/contrib/vk-graph-imgui/src/lib.rs b/contrib/vk-graph-imgui/src/lib.rs index 29b75acd..5851fe95 100644 --- a/contrib/vk-graph-imgui/src/lib.rs +++ b/contrib/vk-graph-imgui/src/lib.rs @@ -11,9 +11,9 @@ use { winit::{event::Event, window::Window}, {HiDpiMode, WinitPlatform}, }, - inline_spirv::include_spirv, std::{sync::Arc, time::Duration}, vk_graph::prelude::*, + vk_shader_macros::include_glsl, }; #[derive(Debug)] @@ -35,8 +35,8 @@ impl ImGui { .blend(BlendMode::PRE_MULTIPLIED_ALPHA) .cull_mode(vk::CullModeFlags::NONE), [ - Shader::new_vertex(include_spirv!("res/shader/imgui.vert", vert).as_slice()), - Shader::new_fragment(include_spirv!("res/shader/imgui.frag", frag).as_slice()), + Shader::new_vertex(include_glsl!("res/shader/imgui.vert").as_slice()), + Shader::new_fragment(include_glsl!("res/shader/imgui.frag").as_slice()), ], ) .unwrap(), diff --git a/examples/bindless.rs b/examples/bindless.rs index 1f478e2c..0f6f283f 100644 --- a/examples/bindless.rs +++ b/examples/bindless.rs @@ -3,10 +3,10 @@ mod profile_with_puffin; use { bytemuck::{Pod, Zeroable, cast_slice}, clap::Parser, - inline_spirv::inline_spirv, std::sync::Arc, vk_graph::prelude::*, vk_graph_window::{WindowBuilder, WindowError}, + vk_shader_macros::glsl, winit::dpi::LogicalSize, }; @@ -100,9 +100,10 @@ fn create_graphic_pipeline(device: &Arc) -> Result, GraphicPipelineInfo::default(), [ Shader::new_vertex( - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(vertex) const vec2 QUAD[] = { vec2(0, 0), @@ -125,16 +126,16 @@ fn create_graphic_pipeline(device: &Arc) -> Result, gl_Position = vec4(QUAD[gl_VertexIndex] * scale + offset, 0, 1); instance_index_out = gl_InstanceIndex; } - "#, - vert + "# ) .as_slice(), ), Shader::new_fragment( - inline_spirv!( + glsl!( r#" #version 460 core #extension GL_EXT_nonuniform_qualifier : require + #pragma shader_stage(fragment) layout(set = 0, binding = 0) uniform sampler2D sampler_nnr[]; @@ -145,8 +146,7 @@ fn create_graphic_pipeline(device: &Arc) -> Result, void main() { color_out = texture(sampler_nnr[nonuniformEXT(instance_index)], vec2(0.5, 0.5)); } - "#, - frag + "# ) .as_slice(), ), diff --git a/examples/debugger.rs b/examples/debugger.rs index ef7283c1..dbc95335 100644 --- a/examples/debugger.rs +++ b/examples/debugger.rs @@ -131,17 +131,17 @@ fn main() -> Result<(), vk_graph_window::WindowError> { frame.device, ComputePipelineInfo::default(), Shader::new_compute( - inline_spirv::inline_spirv!( + vk_shader_macros::glsl!( r#" #version 460 core + #pragma shader_stage(compute) layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; layout(set = 0, binding = 42, rgba8) restrict readonly uniform image2D an_image; void main() {/* TODO: 📈...💰! */} - "#, - comp + "# ) .as_slice(), ), diff --git a/examples/font_bmp.rs b/examples/font_bmp.rs index dc345645..e52099cb 100644 --- a/examples/font_bmp.rs +++ b/examples/font_bmp.rs @@ -4,11 +4,11 @@ use { bmfont::{BMFont, OrdinateOrientation}, clap::Parser, image::ImageReader, - inline_spirv::inline_spirv, std::{io::Cursor, sync::Arc, time::Instant}, vk_graph::prelude::*, vk_graph_fx::*, vk_graph_window::WindowBuilder, + vk_shader_macros::glsl, }; fn main() -> anyhow::Result<()> { @@ -51,10 +51,11 @@ fn main() -> anyhow::Result<()> { let smoke_pipeline = Arc::new(ComputePipeline::create(&window.device, ComputePipelineInfo::default(), Shader::new_compute( - inline_spirv!( + glsl!( r#" // Derived from https://www.shadertoy.com/view/Xl2XWz #version 460 core + #pragma shader_stage(compute) layout(local_size_x_id = 0, local_size_y = 1, local_size_z = 1) in; @@ -107,8 +108,6 @@ fn main() -> anyhow::Result<()> { imageStore(image, ivec2(gl_GlobalInvocationID.xy), fragColor); } "#, - comp, - vulkan1_2 ) .as_slice()).specialization_info(SpecializationInfo { data: subgroup_size.to_ne_bytes().to_vec(), diff --git a/examples/fuzzer.rs b/examples/fuzzer.rs index 57451c78..132464da 100644 --- a/examples/fuzzer.rs +++ b/examples/fuzzer.rs @@ -23,12 +23,12 @@ Also helpful to run with valgrind: */ use { clap::Parser, - inline_spirv::inline_spirv, log::debug, rand::{Rng, rng, seq::IndexedRandom}, std::{mem::size_of, sync::Arc}, vk_graph::prelude::*, vk_graph_window::{FrameContext, WindowBuilder, WindowError}, + vk_shader_macros::glsl, }; type Operation = fn(&mut FrameContext, &mut HashPool); @@ -311,9 +311,10 @@ fn record_compute_array_bind(frame: &mut FrameContext, pool: &mut HashPool) { frame.device, ComputePipelineInfo::default(), Shader::new_compute( - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(compute) layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; @@ -327,8 +328,7 @@ fn record_compute_array_bind(frame: &mut FrameContext, pool: &mut HashPool) { void main() { } - "#, - comp + "# ) .as_slice(), ) @@ -393,10 +393,11 @@ fn record_compute_bindless(frame: &mut FrameContext, pool: &mut HashPool) { frame.device, ComputePipelineInfo::default(), Shader::new_compute( - inline_spirv!( + glsl!( r#" #version 460 core #extension GL_EXT_nonuniform_qualifier : require + #pragma shader_stage(compute) layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; @@ -415,8 +416,7 @@ fn record_compute_bindless(frame: &mut FrameContext, pool: &mut HashPool) { ); } } - "#, - comp + "# ) .as_slice(), ), @@ -468,14 +468,14 @@ fn record_compute_no_op(frame: &mut FrameContext, _: &mut HashPool) { frame.device, ComputePipelineInfo::default(), Shader::new_compute( - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(compute) void main() { } - "#, - comp + "# ) .as_slice(), ), @@ -493,20 +493,21 @@ fn record_graphic_bindless(frame: &mut FrameContext, pool: &mut HashPool) { let pipeline = graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(vertex) void main() { } - "#, - vert + "# ) .as_slice(), - inline_spirv!( + glsl!( r#" #version 460 core #extension GL_EXT_nonuniform_qualifier : require + #pragma shader_stage(fragment) layout(push_constant) uniform PushConstants { layout(offset = 0) uint count; @@ -524,8 +525,7 @@ fn record_graphic_bindless(frame: &mut FrameContext, pool: &mut HashPool) { ); } } - "#, - frag + "# ) .as_slice(), ); @@ -590,27 +590,27 @@ fn record_graphic_load_store(frame: &mut FrameContext, _: &mut HashPool) { let pipeline = graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(vertex) void main() { } - "#, - vert + "# ) .as_slice(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(fragment) layout(location = 0) out vec4 color_out; void main() { color_out = vec4(0); } - "#, - frag + "# ) .as_slice(), ); @@ -697,9 +697,10 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo let pipeline = graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfoBuilder::default().samples(sample_count), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(vertex) const vec2 UV[3] = { vec2(-1, -1), @@ -710,21 +711,20 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo void main() { gl_Position = vec4(UV[gl_VertexIndex], 0, 1); } - "#, - vert + "# ) .as_slice(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(fragment) layout(location = 0) out vec4 color_out; void main() { color_out = vec4(1); } - "#, - frag + "# ) .as_slice(), ); @@ -824,23 +824,26 @@ fn record_graphic_will_merge_common_color1(frame: &mut FrameContext, pool: &mut .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(vertex) + void main() { } - "#, - vert + "# ) .as_slice(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(fragment) + layout(location = 0) out vec4 color0; + void main() { color0 = vec4(0); } - "#, - frag + "# ) .as_slice(), )) @@ -854,23 +857,26 @@ fn record_graphic_will_merge_common_color1(frame: &mut FrameContext, pool: &mut .bind_pipeline(&graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(vertex) + void main() { } - "#, - vert + "# ) .as_slice(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(fragment) + layout(location = 0) out vec4 color0; + void main() { color0 = vec4(0); } - "#, - frag + "# ) .as_slice(), )) @@ -907,23 +913,26 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(vertex) + void main() { } - "#, - vert + "# ) .as_slice(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(fragment) + layout(location = 0) out vec4 color0; + void main() { color0 = vec4(0); } - "#, - frag + "# ) .as_slice(), )) @@ -937,25 +946,28 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut .bind_pipeline(&graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(vertex) + void main() { } - "#, - vert + "# ) .as_slice(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(fragment) + layout(location = 0) out vec4 color0; layout(location = 1) out vec4 color1; + void main() { color0 = vec4(0); color1 = vec4(0); } - "#, - frag + "# ) .as_slice(), )) @@ -971,23 +983,26 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut .bind_pipeline(&graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), - inline_spirv!( + glsl!( r#" - #version 460 core - void main() { } - "#, - vert + #version 460 core + #pragma shader_stage(vertex) + + void main() { } + "# ) .as_slice(), - inline_spirv!( + glsl!( r#" - #version 460 core - layout(location = 0) out vec4 color0; - void main() { - color0 = vec4(0); - } - "#, - frag + #version 460 core + #pragma shader_stage(fragment) + + layout(location = 0) out vec4 color0; + + void main() { + color0 = vec4(0); + } + "# ) .as_slice(), )) @@ -1025,23 +1040,26 @@ fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(vertex) + void main() { } - "#, - vert + "# ) .as_slice(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(fragment) + layout(location = 0) out vec4 color_out; + void main() { color_out = vec4(0); } - "#, - frag + "# ) .as_slice(), )) @@ -1056,22 +1074,24 @@ fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(vertex) + void main() { } - "#, - vert + "# ) .as_slice(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(fragment) + void main() { gl_FragDepth = 0.0; } - "#, - frag + "# ) .as_slice(), )) @@ -1109,22 +1129,24 @@ fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(vertex) + void main() { } - "#, - vert + "# ) .as_slice(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(fragment) + void main() { gl_FragDepth = 0.0; } - "#, - frag + "# ) .as_slice(), )) @@ -1138,23 +1160,26 @@ fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(vertex) + void main() { } - "#, - vert + "# ) .as_slice(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(fragment) + layout(location = 0) out vec4 color_out; + void main() { color_out = vec4(0); } - "#, - frag + "# ) .as_slice(), )) @@ -1183,22 +1208,24 @@ fn record_graphic_will_merge_common_depth3(frame: &mut FrameContext, pool: &mut .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(vertex) + void main() { } - "#, - vert + "# ) .as_slice(), - inline_spirv!( + glsl!( kind: frag, r#" #version 460 core + #pragma shader_stage(fragment) + void main() { gl_FragDepth = 0.0; } - "#, - frag + "# ) .as_slice(), )) @@ -1212,22 +1239,24 @@ fn record_graphic_will_merge_common_depth3(frame: &mut FrameContext, pool: &mut .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(vertex) + void main() { } - "#, - vert + "# ) .as_slice(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(fragment) + void main() { gl_FragDepth = 0.0; } - "#, - frag + "# ) .as_slice(), )) @@ -1239,31 +1268,31 @@ fn record_graphic_will_merge_common_depth3(frame: &mut FrameContext, pool: &mut } fn record_graphic_will_merge_subpass_input(frame: &mut FrameContext, pool: &mut HashPool) { - let vertex = inline_spirv!( + let vertex = glsl!( r#" #version 460 core + #pragma shader_stage(vertex) void main() { } - "#, - vert + "# ) .as_slice(); let pipeline_a = graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), vertex, - inline_spirv!( + glsl!( kind: frag, r#" #version 460 core + #pragma shader_stage(fragment) layout(location = 0) out vec4 color_out; void main() { color_out = vec4(0); } - "#, - frag + "# ) .as_slice(), ); @@ -1271,9 +1300,11 @@ fn record_graphic_will_merge_subpass_input(frame: &mut FrameContext, pool: &mut frame.device, GraphicPipelineInfo::default(), vertex, - inline_spirv!( + glsl!( + kind: frag, r#" #version 460 core + #pragma shader_stage(fragment) layout(input_attachment_index = 0, binding = 0) uniform subpassInput color_in; layout(location = 0) out vec4 color_out; @@ -1281,8 +1312,7 @@ fn record_graphic_will_merge_subpass_input(frame: &mut FrameContext, pool: &mut void main() { color_out = subpassLoad(color_in); } - "#, - frag + "# ) .as_slice(), ); @@ -1322,26 +1352,26 @@ fn record_graphic_wont_merge(frame: &mut FrameContext, pool: &mut HashPool) { let pipeline = graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(vertex) void main() { } - "#, - vert + "# ) .as_slice(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(fragment) layout(location = 0) out vec4 color; void main() { } - "#, - frag + "# ) .as_slice(), ); @@ -1379,19 +1409,20 @@ fn record_transfer_graphic_multipass(frame: &mut FrameContext, pool: &mut HashPo let pipeline = graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(vertex) void main() { } - "#, - vert + "# ) .as_slice(), - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(fragment) layout(binding = 0) uniform sampler2D my_sampler_lle; @@ -1400,8 +1431,7 @@ fn record_transfer_graphic_multipass(frame: &mut FrameContext, pool: &mut HashPo void main() { color_out = texture(my_sampler_lle, vec2(0)); } - "#, - frag + "# ) .as_slice(), ); diff --git a/examples/image_sampler.rs b/examples/image_sampler.rs index f024bffc..e087810a 100644 --- a/examples/image_sampler.rs +++ b/examples/image_sampler.rs @@ -3,13 +3,13 @@ mod profile_with_puffin; use { clap::Parser, hassle_rs::compile_hlsl, - inline_spirv::inline_spirv, std::{ path::{Path, PathBuf}, sync::Arc, }, vk_graph::prelude::*, vk_graph_window::WindowBuilder, + vk_shader_macros::glsl, }; /// Displays a sequence of image samplers. @@ -89,32 +89,36 @@ fn create_pipeline( let mut frag_shader = match (args.hlsl, args.separate) { (true, true) => { // HLSL separate image sampler - Shader::new_fragment( - inline_spirv!( - r#" - struct FullscreenVertexOutput - { - float4 position : SV_Position; - [[vk::location(0)]] float2 uv : TEXCOORD0; - }; + // Shader::new_fragment( + // glsl!( + // kind: frag, - [[vk::binding(0, 0)]] Texture2D screenTexture : register(t0); - [[vk::binding(1, 0)]] SamplerState textureSampler : register(s0); + // r#" + // struct FullscreenVertexOutput + // { + // float4 position : SV_Position; + // [[vk::location(0)]] float2 uv : TEXCOORD0; + // }; - float4 main(FullscreenVertexOutput input) - : SV_Target - { - return screenTexture.Sample(textureSampler, input.uv); - } - "#, - frag, - hlsl - ) - .as_slice(), - ) + // [[vk::binding(0, 0)]] Texture2D screenTexture : register(t0); + // [[vk::binding(1, 0)]] SamplerState textureSampler : register(s0); + + // float4 main(FullscreenVertexOutput input) + // : SV_Target + // { + // return screenTexture.Sample(textureSampler, input.uv); + // } + // "#, + // frag, + // hlsl + // ) + // .as_slice(), + // ) + + todo!() } (true, false) => { - // HLSL combined image sampler: inline_spirv uses shaderc which does not support this, so + // HLSL combined image sampler: include_glsl uses shaderc which does not support this, so // we are using hassle_rs which uses dxc. You must follow the instructions listed here to // use hassle_rs: // See: https://github.com/Traverse-Research/hassle-rs @@ -147,20 +151,20 @@ fn create_pipeline( (false, true) => { // GLSL separate image sampler Shader::new_fragment( - inline_spirv!( + glsl!( r#" - #version 460 core + #version 460 core + #pragma shader_stage(fragment) - layout(binding = 0) uniform texture2D image; - layout(binding = 1) uniform sampler image_sampler; - layout(location = 0) in vec2 vk_TexCoord; - layout(location = 0) out vec4 vk_Color; + layout(binding = 0) uniform texture2D image; + layout(binding = 1) uniform sampler image_sampler; + layout(location = 0) in vec2 vk_TexCoord; + layout(location = 0) out vec4 vk_Color; - void main() { - vk_Color = texture(sampler2D(image, image_sampler), vk_TexCoord); - } - "#, - frag + void main() { + vk_Color = texture(sampler2D(image, image_sampler), vk_TexCoord); + } + "# ) .as_slice(), ) @@ -168,9 +172,10 @@ fn create_pipeline( (false, false) => { // GLSL combined image sampler Shader::new_fragment( - inline_spirv!( + glsl!( r#" - #version 460 core + #version 460 core + #pragma shader_stage(fragment) layout(binding = 0) uniform sampler2D image; layout(location = 0) in vec2 vk_TexCoord; @@ -179,8 +184,7 @@ fn create_pipeline( void main() { vk_Color = texture(image, vk_TexCoord); } - "#, - frag + "# ) .as_slice(), ) @@ -197,9 +201,10 @@ fn create_pipeline( GraphicPipelineInfo::default(), [ Shader::new_vertex( - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(vertex) const vec2[3] VERTICES = { vec2(-1, -1), @@ -213,8 +218,7 @@ fn create_pipeline( gl_Position = vec4(VERTICES[gl_VertexIndex], 0, 1); vk_TexCoord = 0.75 * gl_Position.xy + vec2(0.5); } - "#, - vert + "# ) .as_slice(), ), diff --git a/examples/min_max.rs b/examples/min_max.rs index 5856b94b..5552fafa 100644 --- a/examples/min_max.rs +++ b/examples/min_max.rs @@ -1,10 +1,10 @@ use { bytemuck::cast_slice, clap::Parser, - inline_spirv::inline_spirv, log::warn, std::{mem::size_of, sync::Arc}, vk_graph::prelude::*, + vk_shader_macros::glsl, }; // Min/max sampler reduction is commonly used to create depth buffer mip-maps for use with gpu-based @@ -179,8 +179,10 @@ fn reduce_depth_image( device, ComputePipelineInfo::default(), Shader::new_compute( - inline_spirv!( - r#"#version 460 core + glsl!( + r#" + #version 460 core + #pragma shader_stage(compute) layout(binding = 0) uniform sampler2D depth_image; layout(binding = 1) writeonly uniform image2D reduced_image; @@ -192,8 +194,8 @@ fn reduce_depth_image( ivec2 store_xy = ivec2(gl_GlobalInvocationID.xy); imageStore(reduced_image, store_xy, sample_val); - }"#, - comp + } + "# ) .as_slice(), ) diff --git a/examples/mip_compute.rs b/examples/mip_compute.rs index 19fbd516..e84583c2 100644 --- a/examples/mip_compute.rs +++ b/examples/mip_compute.rs @@ -1,8 +1,8 @@ mod profile_with_puffin; use { - bytemuck::cast_slice, clap::Parser, inline_spirv::inline_spirv, std::sync::Arc, - vk_graph::prelude::*, + bytemuck::cast_slice, clap::Parser, std::sync::Arc, vk_graph::prelude::*, + vk_shader_macros::glsl, }; /// This program demonstrates a single render pass which uses multiple executions to record a chain @@ -57,9 +57,10 @@ fn main() -> Result<(), DriverError> { &device, ComputePipelineInfo::default(), Shader::new_compute( - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(compute) layout(binding = 0) uniform sampler2D src_mip; layout(binding = 1, r32f) writeonly uniform image2D dst_mip; @@ -68,9 +69,7 @@ fn main() -> Result<(), DriverError> { vec4 depth = texture(src_mip, vec2(gl_GlobalInvocationID.xy << 1) + 1.0); imageStore(dst_mip, ivec2(gl_GlobalInvocationID.xy), depth); } - "#, - comp, - vulkan1_2 + "# ) .as_slice(), ) diff --git a/examples/mip_graphic.rs b/examples/mip_graphic.rs index 0d69e9e9..034e812b 100644 --- a/examples/mip_graphic.rs +++ b/examples/mip_graphic.rs @@ -5,10 +5,10 @@ use { clap::Parser, core::f32, glam::{Vec4, vec3}, - inline_spirv::inline_spirv, std::sync::Arc, vk_graph::prelude::*, vk_graph_window::{WindowBuilder, WindowError}, + vk_shader_macros::glsl, }; // TODO: Add texelFetch option @@ -96,9 +96,10 @@ fn fill_mip_levels(device: &Arc, image: &Arc) -> Result<(), Drive GraphicPipelineInfo::default(), [ Shader::new_vertex( - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(vertex) const vec2 POSITION[] = { vec2(-1, -1), @@ -116,15 +117,15 @@ fn fill_mip_levels(device: &Arc, image: &Arc) -> Result<(), Drive ab = max(position.y, 0); gl_Position = vec4(position, 0, 1); } - "#, - vert + "# ) .as_slice(), ), Shader::new_fragment( - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(fragment) layout(push_constant) uniform PushConstants { layout(offset = 0) vec3 a; @@ -137,8 +138,7 @@ fn fill_mip_levels(device: &Arc, image: &Arc) -> Result<(), Drive void main() { color = vec4(mix(a, b, ab), 1); } - "#, - frag + "# ) .as_slice(), ), @@ -202,9 +202,10 @@ fn splat(device: &Arc) -> Result, DriverError> { GraphicPipelineInfo::default(), [ Shader::new_vertex( - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(vertex) const vec2 POSITION[] = { vec2(-1, -1), @@ -229,15 +230,15 @@ fn splat(device: &Arc) -> Result, DriverError> { texcoord = TEXCOORD[gl_VertexIndex]; gl_Position = vec4(POSITION[gl_VertexIndex], 0, 1); } - "#, - vert + "# ) .as_slice(), ), Shader::new_fragment( - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(fragment) layout(binding = 0) uniform sampler2D image; @@ -247,8 +248,7 @@ fn splat(device: &Arc) -> Result, DriverError> { void main() { color = texture(image, texcoord); } - "#, - frag + "# ) .as_slice(), ) diff --git a/examples/msaa.rs b/examples/msaa.rs index b2df482d..8d9dfbf5 100644 --- a/examples/msaa.rs +++ b/examples/msaa.rs @@ -4,11 +4,11 @@ use { bytemuck::{NoUninit, bytes_of, cast_slice}, clap::Parser, glam::{Mat4, Vec3}, - inline_spirv::inline_spirv, log::warn, std::{mem::size_of, sync::Arc}, vk_graph::prelude::*, vk_graph_window::WindowBuilder, + vk_shader_macros::glsl, winit::{event::Event, keyboard::KeyCode}, winit_input_helper::WinitInputHelper, }; @@ -317,9 +317,10 @@ fn create_mesh_pipeline( device: &Arc, sample_count: SampleCount, ) -> Result, DriverError> { - let vert = inline_spirv!( + let vert = glsl!( r#" #version 460 core + #pragma shader_stage(vertex) layout(push_constant) uniform PushConstants { mat4 world; @@ -345,12 +346,12 @@ fn create_mesh_pipeline( normal_out = (push_const.world * vec4(normal, 1.0)).xyz; color_out = color; } - "#, - vert + "# ); - let frag = inline_spirv!( + let frag = glsl!( r#" #version 460 core + #pragma shader_stage(fragment) layout(set = 0, binding = 0) uniform Scene { mat4 view; @@ -368,8 +369,7 @@ fn create_mesh_pipeline( color_out = vec4(color * lambertian, 1.0); } - "#, - frag + "# ); let info = GraphicPipelineInfoBuilder::default().samples(sample_count); diff --git a/examples/multipass.rs b/examples/multipass.rs index a6c48809..08967255 100644 --- a/examples/multipass.rs +++ b/examples/multipass.rs @@ -4,10 +4,10 @@ use { bytemuck::{bytes_of, cast_slice}, clap::Parser, glam::{Mat4, Vec3, Vec4, vec3}, - inline_spirv::inline_spirv, std::sync::Arc, vk_graph::prelude::*, vk_graph_window::WindowBuilder, + vk_shader_macros::glsl, }; #[derive(Clone, Copy)] @@ -304,44 +304,41 @@ fn create_funky_shape(device: &Arc, pool: &mut LazyPool) -> Result) -> Arc { let vertex_shader = Shader::new_vertex( - inline_spirv!( + glsl!( r#" #version 450 core + #pragma shader_stage(vertex) const float X[6] = {-1, -1, 1, 1, 1, -1}; const float Y[6] = {-1, 1, -1, 1, -1, 1}; - vec2 vertex_pos() - { + vec2 vertex_pos() { float x = X[gl_VertexIndex]; float y = Y[gl_VertexIndex]; return vec2(x, y); } - void main() - { + void main() { gl_Position = vec4(vertex_pos(), 0, 1); } - "#, - vert + "# ) .as_slice(), ); let fragment_shader = Shader::new_fragment( - inline_spirv!( + glsl!( r#" #version 450 + #pragma shader_stage(fragment) layout(location = 0) out vec4 color; - void main() - { + void main() { color = vec4(vec3(0.75), 1.0); } - "#, - frag + "# ) .as_slice(), ); @@ -358,9 +355,10 @@ fn create_fill_background_pipeline(device: &Arc) -> Arc fn create_prepass_pipeline(device: &Arc) -> Arc { let vertex_shader = Shader::new_vertex( - inline_spirv!( + glsl!( r#" #version 450 + #pragma shader_stage(vertex) layout (location = 0) in vec3 inPos; layout (location = 1) in vec3 inNormal; @@ -380,45 +378,40 @@ fn create_prepass_pipeline(device: &Arc) -> Arc { vec3 objPos; } pushConsts; - out gl_PerVertex - { + out gl_PerVertex { vec4 gl_Position; }; - void main() - { + void main() { vec3 locPos = vec3(ubo.model * vec4(inPos, 1.0)); outWorldPos = locPos + pushConsts.objPos; outNormal = mat3(ubo.model) * inNormal; gl_Position = ubo.projection * ubo.view * vec4(outWorldPos, 1.0); } - "#, - vert + "# ) .as_slice(), ); let fragment_shader = Shader::new_fragment( - inline_spirv!( + glsl!( r#" #version 450 + #pragma shader_stage(fragment) layout (location = 0) in vec3 inWorldPos; layout (location = 1) in vec3 inNormal; - layout (binding = 0) uniform UBO - { + layout (binding = 0) uniform UBO { mat4 projection; mat4 model; mat4 view; vec3 camPos; } ubo; - void main() - { + void main() { } - "#, - frag + "# ) .as_slice(), ); @@ -436,9 +429,10 @@ fn create_prepass_pipeline(device: &Arc) -> Arc { fn create_pbr_pipeline(device: &Arc) -> Arc { // See: https://github.com/SaschaWillems/Vulkan/blob/master/data/shaders/glsl/pbrbasic/pbr.vert let vertex_shader = Shader::new_vertex( - inline_spirv!( + glsl!( r#" #version 450 + #pragma shader_stage(vertex) layout (location = 0) in vec3 inPos; layout (location = 1) in vec3 inNormal; @@ -458,29 +452,27 @@ fn create_pbr_pipeline(device: &Arc) -> Arc { vec3 objPos; } pushConsts; - out gl_PerVertex - { + out gl_PerVertex { vec4 gl_Position; }; - void main() - { + void main() { vec3 locPos = vec3(ubo.model * vec4(inPos, 1.0)); outWorldPos = locPos + pushConsts.objPos; outNormal = mat3(ubo.model) * inNormal; gl_Position = ubo.projection * ubo.view * vec4(outWorldPos, 1.0); } - "#, - vert + "# ) .as_slice(), ); // See: https://github.com/SaschaWillems/Vulkan/blob/master/data/shaders/glsl/pbrbasic/pbr.frag let fragment_shader = Shader::new_fragment( - inline_spirv!( + glsl!( r#" #version 450 + #pragma shader_stage(fragment) layout (location = 0) in vec3 inWorldPos; layout (location = 1) in vec3 inNormal; @@ -606,8 +598,7 @@ fn create_pbr_pipeline(device: &Arc) -> Arc { outColor = vec4(color, 1.0); } - "#, - frag + "# ) .as_slice(), ); diff --git a/examples/ray_omni.rs b/examples/ray_omni.rs index 7c148f8b..5bb807c6 100644 --- a/examples/ray_omni.rs +++ b/examples/ray_omni.rs @@ -4,7 +4,6 @@ use { bytemuck::{Pod, Zeroable, bytes_of, cast_slice}, clap::Parser, glam::{Mat4, Vec3, Vec4, vec3, vec4}, - inline_spirv::inline_spirv, log::info, meshopt::remap::{generate_vertex_remap, remap_index_buffer, remap_vertex_buffer}, std::{ @@ -17,6 +16,7 @@ use { tobj::{GPU_LOAD_OPTIONS, load_obj}, vk_graph::prelude::*, vk_graph_window::WindowBuilder, + vk_shader_macros::glsl, }; fn main() -> anyhow::Result<()> { @@ -227,9 +227,10 @@ fn create_blas( } fn create_pipeline(device: &Arc) -> Result, DriverError> { - let vert = inline_spirv!( + let vert = glsl!( r#" #version 460 core + #pragma shader_stage(vertex) layout (location = 0) in vec3 inPos; layout (location = 1) in vec3 inNormal; @@ -257,15 +258,15 @@ fn create_pipeline(device: &Arc) -> Result, DriverE outLightVec = normalize(ubo.lightPos - inPos); outViewVec = -pos.xyz; } - "#, - vert, - vulkan1_2 + "# ); - let frag = inline_spirv!( + let frag = glsl!( + target: vulkan1_2, r#" #version 460 core #extension GL_EXT_ray_tracing : enable #extension GL_EXT_ray_query : enable + #pragma shader_stage(fragment) layout (binding = 1) uniform accelerationStructureEXT topLevelAS; @@ -299,9 +300,7 @@ fn create_pipeline(device: &Arc) -> Result, DriverE outFragColor *= 0.1; } } - "#, - frag, - vulkan1_2 + "# ); Ok(Arc::new(GraphicPipeline::create( diff --git a/examples/ray_trace.rs b/examples/ray_trace.rs index 0884126e..26fb7cb7 100644 --- a/examples/ray_trace.rs +++ b/examples/ray_trace.rs @@ -3,20 +3,22 @@ mod profile_with_puffin; use { bytemuck::cast_slice, clap::Parser, - inline_spirv::inline_spirv, log::warn, std::{io::BufReader, mem::size_of, sync::Arc}, tobj::{GPU_LOAD_OPTIONS, load_mtl_buf, load_obj_buf}, vk_graph::prelude::*, vk_graph_window::WindowBuilder, + vk_shader_macros::glsl, winit::{event::Event, keyboard::KeyCode}, winit_input_helper::WinitInputHelper, }; -static SHADER_RAY_GEN: &[u32] = inline_spirv!( +static SHADER_RAY_GEN: &[u32] = glsl!( + target: vulkan1_2, r#" #version 460 #extension GL_EXT_ray_tracing : require + #pragma shader_stage(raygen) #define M_PI 3.1415926535897932384626433832795 @@ -83,17 +85,17 @@ static SHADER_RAY_GEN: &[u32] = inline_spirv!( imageStore(image, ivec2(gl_LaunchIDEXT.xy), color); } - "#, - rgen, - vulkan1_2 + "# ) .as_slice(); -static SHADER_CLOSEST_HIT: &[u32] = inline_spirv!( +static SHADER_CLOSEST_HIT: &[u32] = glsl!( + target: vulkan1_2, r#" #version 460 #extension GL_EXT_ray_tracing : require #extension GL_EXT_nonuniform_qualifier : enable + #pragma shader_stage(closest) #define M_PI 3.1415926535897932384626433832795 @@ -281,16 +283,16 @@ static SHADER_CLOSEST_HIT: &[u32] = inline_spirv!( payload.rayDepth += 1; } - "#, - rchit, - vulkan1_2 + "# ) .as_slice(); -static SHADER_MISS: &[u32] = inline_spirv!( +static SHADER_MISS: &[u32] = glsl!( + target: vulkan1_2, r#" #version 460 #extension GL_EXT_ray_tracing : require + #pragma shader_stage(miss) layout(location = 0) rayPayloadInEXT Payload { vec3 rayOrigin; @@ -307,25 +309,23 @@ static SHADER_MISS: &[u32] = inline_spirv!( void main() { payload.rayActive = 0; } - "#, - rmiss, - vulkan1_2 + "# ) .as_slice(); -static SHADER_SHADOW_MISS: &[u32] = inline_spirv!( +static SHADER_SHADOW_MISS: &[u32] = glsl!( + target: vulkan1_2, r#" #version 460 #extension GL_EXT_ray_tracing : require + #pragma shader_stage(miss) layout(location = 1) rayPayloadInEXT bool isShadow; void main() { isShadow = false; } - "#, - rmiss, - vulkan1_2 + "# ) .as_slice(); diff --git a/examples/rt_triangle.rs b/examples/rt_triangle.rs index 1ad46fbe..e7a31a49 100644 --- a/examples/rt_triangle.rs +++ b/examples/rt_triangle.rs @@ -3,16 +3,18 @@ mod profile_with_puffin; use { bytemuck::{NoUninit, cast_slice}, clap::Parser, - inline_spirv::inline_spirv, std::sync::Arc, vk_graph::prelude::*, vk_graph_window::WindowBuilder, + vk_shader_macros::glsl, }; -static SHADER_RAY_GEN: &[u32] = inline_spirv!( +static SHADER_RAY_GEN: &[u32] = glsl!( + target: vulkan1_2, r#" #version 460 #extension GL_EXT_ray_tracing : enable + #pragma shader_stage(raygen) layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS; layout(binding = 1, set = 0, rgba32f) uniform image2D image; @@ -35,17 +37,17 @@ static SHADER_RAY_GEN: &[u32] = inline_spirv!( imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(hitValue, 0.0)); } - "#, - rgen, - vulkan1_2 + "# ) .as_slice(); -static SHADER_CLOSEST_HIT: &[u32] = inline_spirv!( +static SHADER_CLOSEST_HIT: &[u32] = glsl!( + target: vulkan1_2, r#" #version 460 #extension GL_EXT_ray_tracing : enable #extension GL_EXT_nonuniform_qualifier : enable + #pragma shader_stage(closest) layout(location = 0) rayPayloadInEXT vec3 resultColor; hitAttributeEXT vec2 attribs; @@ -54,25 +56,23 @@ static SHADER_CLOSEST_HIT: &[u32] = inline_spirv!( const vec3 barycentricCoords = vec3(1.0f - attribs.x - attribs.y, attribs.x, attribs.y); resultColor = barycentricCoords; } - "#, - rchit, - vulkan1_2 + "# ) .as_slice(); -static SHADER_MISS: &[u32] = inline_spirv!( +static SHADER_MISS: &[u32] = glsl!( + target: vulkan1_2, r#" #version 460 #extension GL_EXT_ray_tracing : enable + #pragma shader_stage(miss) layout(location = 0) rayPayloadInEXT vec3 hitValue; void main() { hitValue = vec3(0.0, 0.0, 0.2); } - "#, - rmiss, - vulkan1_2 + "# ) .as_slice(); diff --git a/examples/subgroup_ops.rs b/examples/subgroup_ops.rs index eba85130..86dff946 100644 --- a/examples/subgroup_ops.rs +++ b/examples/subgroup_ops.rs @@ -1,9 +1,9 @@ use { bytemuck::cast_slice, clap::Parser, - inline_spirv::inline_spirv, std::{mem::size_of, sync::Arc, time::Instant}, vk_graph::prelude::*, + vk_shader_macros::glsl, }; /// Advanced example demonstrating subgroup operations (arithmetic and ballot). @@ -155,11 +155,13 @@ fn create_reduce_pipeline(device: &Arc) -> Result, device, ComputePipelineInfo::default(), Shader::new_compute( - inline_spirv!( + glsl!( + target: vulkan1_2, r#" #version 460 core #extension GL_EXT_shader_explicit_arithmetic_types_int32 : require #extension GL_KHR_shader_subgroup_arithmetic : require + #pragma shader_stage(compute) layout(local_size_x_id = 0, local_size_y = 1, local_size_z = 1) in; @@ -178,9 +180,7 @@ fn create_reduce_pipeline(device: &Arc) -> Result, workgroup_buf[gl_WorkGroupID.x] = sum; } } - "#, - comp, - vulkan1_2, + "# ) .as_slice(), ) @@ -207,11 +207,13 @@ fn create_exclusive_sum_pipeline( ComputePipeline::create( device, ComputePipelineInfo::default(), - Shader::new_compute(inline_spirv!( + Shader::new_compute(glsl!( + target: vulkan1_2, r#" #version 460 core #extension GL_EXT_shader_explicit_arithmetic_types_int32 : require #extension GL_KHR_shader_subgroup_arithmetic : require + #pragma shader_stage(compute) layout(local_size_x_id = 0, local_size_y = 1, local_size_z = 1) in; @@ -242,9 +244,7 @@ fn create_exclusive_sum_pipeline( output_buf[gl_GlobalInvocationID.x] = subgroup_sum + workgroup_sum; } - "#, - comp, - vulkan1_2 + "# ).as_slice()) .specialization_info(SpecializationInfo { data: device.physical_device.properties_v1_1.subgroup_size.to_ne_bytes().to_vec(), diff --git a/examples/triangle.rs b/examples/triangle.rs index b50246f9..5cc78b73 100644 --- a/examples/triangle.rs +++ b/examples/triangle.rs @@ -3,10 +3,10 @@ mod profile_with_puffin; use { bytemuck::cast_slice, clap::Parser, - inline_spirv::inline_spirv, std::sync::Arc, vk_graph::prelude::*, vk_graph_window::{WindowBuilder, WindowError}, + vk_shader_macros::glsl, }; // A Vulkan triangle using a graphic pipeline, vertex/fragment shaders, and index/vertex buffers. @@ -21,9 +21,10 @@ fn main() -> Result<(), WindowError> { GraphicPipelineInfo::default(), [ Shader::new_vertex( - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(vertex) layout(location = 0) in vec3 position; layout(location = 1) in vec3 color; @@ -34,15 +35,15 @@ fn main() -> Result<(), WindowError> { gl_Position = vec4(position, 1); vk_Color = color; } - "#, - vert + "# ) .as_slice(), ), Shader::new_fragment( - inline_spirv!( + glsl!( r#" #version 460 core + #pragma shader_stage(fragment) layout(location = 0) in vec3 color; @@ -51,8 +52,7 @@ fn main() -> Result<(), WindowError> { void main() { vk_Color = vec4(color, 1); } - "#, - frag + "# ) .as_slice(), ), diff --git a/examples/vertex_layout.rs b/examples/vertex_layout.rs index 760fc065..6dfa44f5 100644 --- a/examples/vertex_layout.rs +++ b/examples/vertex_layout.rs @@ -4,10 +4,10 @@ use { bytemuck::{Pod, Zeroable, cast_slice}, clap::Parser, half::f16, - inline_spirv::inline_spirv, std::{mem::size_of, sync::Arc}, vk_graph::prelude::*, vk_graph_window::{FrameContext, WindowBuilder}, + vk_shader_macros::glsl, }; /// This example draws two triangles using two different vertex formats. @@ -195,26 +195,27 @@ fn create_vertex_shader(is_double: bool) -> ShaderBuilder { // dvec2 when using 64-bit positions; and for the purposes of this example we don't want to // duplicate this shader code. You probably don't want to do this, or you may have different // facilities for generating SPIR-V code - either way ignore the macro unless you're interested - // in the inline_spirv! wizardry it contains which is unrelated to this example. + // in the include_glsl! wizardry it contains which is unrelated to this example. macro_rules! compile_vert { ($vec2_ty:literal) => { - inline_spirv!( - r#" - #version 460 core - - layout(location = 0) in VEC2_TY position_in; - layout(location = 1) in vec3 color_in; - - layout(location = 0) out vec3 color_out; - - void main() { - gl_Position = vec4(position_in, 0, 1); - color_out = color_in; - } - "#, - vert, - D VEC2_TY = $vec2_ty, - )}; + glsl!( + define: VEC2_TY $vec2_ty, + r#" + #version 460 core + #pragma shader_stage(vertex) + + layout(location = 0) in VEC2_TY position_in; + layout(location = 1) in vec3 color_in; + + layout(location = 0) out vec3 color_out; + + void main() { + gl_Position = vec4(position_in, 0, 1); + color_out = color_in; + } + "# + ) + }; } let spirv = if is_double { @@ -230,9 +231,10 @@ fn create_pipeline( device: &Arc, vertex: ShaderBuilder, ) -> Result, DriverError> { - let fragment_spirv = inline_spirv!( + let fragment_spirv = glsl!( r#" #version 460 core + #pragma shader_stage(fragment) layout(location = 0) in vec3 color_in; @@ -241,8 +243,7 @@ fn create_pipeline( void main() { color_out = vec4(color_in, 1.0); } - "#, - frag + "# ); Ok(Arc::new(GraphicPipeline::create( diff --git a/examples/vsm_omni.rs b/examples/vsm_omni.rs index 1808ef59..9ff69179 100644 --- a/examples/vsm_omni.rs +++ b/examples/vsm_omni.rs @@ -4,7 +4,6 @@ use { bytemuck::{NoUninit, Pod, Zeroable, bytes_of, cast_slice}, clap::Parser, glam::{Mat4, Quat, Vec3, vec3}, - inline_spirv::inline_spirv, log::info, meshopt::remap::{generate_vertex_remap, remap_index_buffer, remap_vertex_buffer}, std::{ @@ -16,6 +15,7 @@ use { tobj::{GPU_LOAD_OPTIONS, load_obj}, vk_graph::prelude::*, vk_graph_window::WindowBuilder, + vk_shader_macros::glsl, winit::{dpi::LogicalSize, event::Event, keyboard::KeyCode, window::Fullscreen}, winit_input_helper::WinitInputHelper, }; @@ -444,9 +444,10 @@ fn best_2d_optimal_format( } fn create_blur_x_pipeline(device: &Arc) -> Result, DriverError> { - let comp = inline_spirv!( + let comp = glsl!( r#" #version 450 core + #pragma shader_stage(compute) #define POS_X 0 #define NEG_X 1 @@ -536,8 +537,7 @@ fn create_blur_x_pipeline(device: &Arc) -> Result, accumulator -= imageLoad(image, ivec3(x - RADIUS, y, face)).rg; } } - "#, - comp + "# ); let shader = Shader::new_compute(comp.as_slice()).specialization_info(SpecializationInfo::new( @@ -567,9 +567,10 @@ fn create_blur_x_pipeline(device: &Arc) -> Result, } fn create_blur_y_pipeline(device: &Arc) -> Result, DriverError> { - let comp = inline_spirv!( + let comp = glsl!( r#" #version 450 core + #pragma shader_stage(compute) #define POS_X 0 #define NEG_X 1 @@ -659,8 +660,7 @@ fn create_blur_y_pipeline(device: &Arc) -> Result, accumulator -= imageLoad(image, ivec3(x, y - RADIUS, face)).rg; } } - "#, - comp + "# ); let shader = Shader::new_compute(comp.as_slice()).specialization_info(SpecializationInfo::new( @@ -690,9 +690,10 @@ fn create_blur_y_pipeline(device: &Arc) -> Result, } fn create_debug_pipeline(device: &Arc) -> Result, DriverError> { - let vert = inline_spirv!( + let vert = glsl!( r#" #version 450 core + #pragma shader_stage(vertex) layout(push_constant) uniform PushConstants { layout(offset = 0) mat4 model; @@ -714,12 +715,12 @@ fn create_debug_pipeline(device: &Arc) -> Result, D world_normal_out = normalize((push_const.model * vec4(normal, 1)).xyz); gl_Position = camera.projection * camera.view * vec4(world_position_out, 1); } - "#, - vert + "# ); - let frag = inline_spirv!( + let frag = glsl!( r#" #version 450 core + #pragma shader_stage(fragment) layout(binding = 1) uniform Light { vec3 position; @@ -749,8 +750,7 @@ fn create_debug_pipeline(device: &Arc) -> Result, D color_out.rgb = vec3(lambertian * attenuation); } } - "#, - frag + "# ); let info = GraphicPipelineInfo::default(); @@ -766,9 +766,10 @@ fn create_debug_pipeline(device: &Arc) -> Result, D } fn create_mesh_pipeline(device: &Arc) -> Result, DriverError> { - let vert = inline_spirv!( + let vert = glsl!( r#" #version 450 core + #pragma shader_stage(vertex) layout(push_constant) uniform PushConstants { layout(offset = 0) mat4 model; @@ -794,12 +795,12 @@ fn create_mesh_pipeline(device: &Arc) -> Result, Dr world_position_out = push_const.model * vec4(position, 1.0); gl_Position = camera.projection * camera.view * world_position_out; } - "#, - vert + "# ); - let frag = inline_spirv!( + let frag = glsl!( r#" #version 450 core + #pragma shader_stage(fragment) #define EPSILON 0.0001 #define MIN_SHADOW 0.1 @@ -861,8 +862,7 @@ fn create_mesh_pipeline(device: &Arc) -> Result, Dr color_out.rgb = vec3(attenuation * lambertian * shadow); } } - "#, - frag + "# ); let info = GraphicPipelineInfo::default(); @@ -885,9 +885,10 @@ fn create_mesh_pipeline(device: &Arc) -> Result, Dr } fn create_shadow_pipeline(device: &Arc) -> Result, DriverError> { - let vert = inline_spirv!( + let vert = glsl!( r#" #version 450 core + #pragma shader_stage(vertex) layout(push_constant) uniform PushConstants { layout(offset = 0) mat4 model; @@ -914,12 +915,12 @@ fn create_shadow_pipeline(device: &Arc) -> Result, gl_Position = light.projection * light.view * world_position; world_position_out = world_position.xyz; } - "#, - vert + "# ); - let frag = inline_spirv!( + let frag = glsl!( r#" #version 450 core + #pragma shader_stage(fragment) layout(binding = 0) uniform Light { vec3 position; @@ -937,8 +938,7 @@ fn create_shadow_pipeline(device: &Arc) -> Result, color_out.x = dist; color_out.y = dist * dist; } - "#, - frag + "# ); let info = GraphicPipelineInfo::default(); @@ -956,9 +956,10 @@ fn create_shadow_pipeline(device: &Arc) -> Result, fn create_shadow_pipeline_with_geometry_shader( device: &Arc, ) -> Result, DriverError> { - let vert = inline_spirv!( + let vert = glsl!( r#" #version 450 core + #pragma shader_stage(vertex) #define TAN_HALF_FOVY 1.0 // tan(45deg) #define ASPECT_RATIO 1.0 @@ -1040,12 +1041,12 @@ fn create_shadow_pipeline_with_geometry_shader( view_positions_out.positions[4] = view4_pos; view_positions_out.positions[5] = view5_pos; } - "#, - vert + "# ); - let geom = inline_spirv!( + let geom = glsl!( r#" #version 450 core + #pragma shader_stage(geometry) #define CLIP mat4(1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.5, 1.0) @@ -1111,12 +1112,12 @@ fn create_shadow_pipeline_with_geometry_shader( gl_Layer = 5; emit(0x64, 5); } - "#, - geom + "# ); - let frag = inline_spirv!( + let frag = glsl!( r#" #version 450 core + #pragma shader_stage(fragment) layout(binding = 0) uniform Light { vec3 position; @@ -1134,8 +1135,7 @@ fn create_shadow_pipeline_with_geometry_shader( color_out.x = dist; color_out.y = dist * dist; } - "#, - frag + "# ); let info = GraphicPipelineInfo::default(); diff --git a/src/driver/compute.rs b/src/driver/compute.rs index 52982418..b55d5103 100644 --- a/src/driver/compute.rs +++ b/src/driver/compute.rs @@ -220,9 +220,10 @@ pub struct ComputePipelineInfo { /// Basic usage (GLSL): /// /// ``` - /// # inline_spirv::inline_spirv!(r#" + /// # vk_shader_macros::glsl!(r#" /// #version 460 core /// #extension GL_EXT_nonuniform_qualifier : require + /// #pragma shader_stage(compute) /// /// layout(set = 0, binding = 0, rgba8) writeonly uniform image2D my_binding[]; /// @@ -230,7 +231,7 @@ pub struct ComputePipelineInfo { /// { /// // my_binding will have space for 8,192 images by default /// } - /// # "#, comp); + /// # "#); /// ``` #[builder(default = "8192")] pub bindless_descriptor_count: u32, diff --git a/src/driver/graphic.rs b/src/driver/graphic.rs index ccb9dcf8..ea193eae 100644 --- a/src/driver/graphic.rs +++ b/src/driver/graphic.rs @@ -649,17 +649,17 @@ pub struct GraphicPipelineInfo { /// Basic usage (GLSL): /// /// ``` - /// # inline_spirv::inline_spirv!(r#" + /// # vk_shader_macros::glsl!(r#" /// #version 460 core /// #extension GL_EXT_nonuniform_qualifier : require + /// #pragma shader_stage(fragment) /// /// layout(set = 0, binding = 0) uniform sampler2D my_binding[]; /// - /// void main() - /// { + /// void main() { /// // my_binding will have space for 8,192 images by default /// } - /// # "#, frag); + /// # "#); /// ``` #[builder(default = "8192")] pub bindless_descriptor_count: u32, diff --git a/src/driver/ray_trace.rs b/src/driver/ray_trace.rs index 667b2b36..d2ed5bc5 100644 --- a/src/driver/ray_trace.rs +++ b/src/driver/ray_trace.rs @@ -410,17 +410,17 @@ pub struct RayTracePipelineInfo { /// Basic usage (GLSL): /// /// ``` - /// # inline_spirv::inline_spirv!(r#" + /// # vk_shader_macros::glsl!(target: vulkan1_2, r#" /// #version 460 core /// #extension GL_EXT_nonuniform_qualifier : require + /// #pragma shader_stage(closest) /// /// layout(set = 0, binding = 0, rgba8) readonly uniform image2D my_binding[]; /// - /// void main() - /// { + /// void main() { /// // my_binding will have space for 8,192 images by default /// } - /// # "#, rchit, vulkan1_2); + /// # "#); /// ``` #[builder(default = "8192")] pub bindless_descriptor_count: u32, diff --git a/src/driver/shader.rs b/src/driver/shader.rs index 52f6f50f..e62d7ea3 100644 --- a/src/driver/shader.rs +++ b/src/driver/shader.rs @@ -692,8 +692,9 @@ pub struct Shader { /// Basic usage (GLSL): /// /// ``` - /// # inline_spirv::inline_spirv!(r#" + /// # vk_shader_macros::glsl!(r#" /// #version 460 core + /// #pragma shader_stage(compute) /// /// // Defaults to 6 if not set using Shader specialization_info! /// layout(constant_id = 0) const uint MY_COUNT = 6; @@ -704,7 +705,7 @@ pub struct Shader { /// { /// // Code uses MY_COUNT number of my_samplers here /// } - /// # "#, comp); + /// # "#); /// ``` /// /// ```no_run diff --git a/src/graph/pass_ref.rs b/src/graph/pass_ref.rs index 5278e03a..f394c886 100644 --- a/src/graph/pass_ref.rs +++ b/src/graph/pass_ref.rs @@ -1196,18 +1196,18 @@ impl Compute<'_> { /// Basic usage: /// /// ``` - /// # inline_spirv::inline_spirv!(r#" + /// # vk_shader_macros::glsl!(r#" /// #version 450 + /// #pragma shader_stage(compute) /// /// layout(set = 0, binding = 0, std430) restrict writeonly buffer MyBufer { /// uint my_buf[]; /// }; /// - /// void main() - /// { + /// void main() { /// // TODO /// } - /// # "#, comp); + /// # "#); /// ``` /// /// ```no_run @@ -1378,8 +1378,9 @@ impl Compute<'_> { /// Basic usage: /// /// ``` - /// # inline_spirv::inline_spirv!(r#" + /// # vk_shader_macros::glsl!(r#" /// #version 450 + /// #pragma shader_stage(compute) /// /// layout(push_constant) uniform PushConstants { /// layout(offset = 0) uint the_answer; @@ -1389,7 +1390,7 @@ impl Compute<'_> { /// { /// // TODO: Add bindings to read/write things! /// } - /// # "#, comp); + /// # "#); /// ``` /// /// ```no_run @@ -1439,19 +1440,19 @@ impl Compute<'_> { /// Basic usage: /// /// ``` - /// # inline_spirv::inline_spirv!(r#" + /// # vk_shader_macros::glsl!(r#" /// #version 450 + /// #pragma shader_stage(compute) /// /// layout(push_constant) uniform PushConstants { /// layout(offset = 0) uint some_val1; /// layout(offset = 4) uint some_val2; /// } push_constants; /// - /// void main() - /// { + /// void main() { /// // TODO: Add bindings to read/write things! /// } - /// # "#, comp); + /// # "#); /// ``` /// /// ```no_run @@ -2086,18 +2087,18 @@ impl Draw<'_> { /// Basic usage: /// /// ``` - /// # inline_spirv::inline_spirv!(r#" + /// # vk_shader_macros::glsl!(r#" /// #version 450 + /// #pragma shader_stage(compute) /// /// layout(push_constant) uniform PushConstants { /// layout(offset = 0) uint the_answer; /// } push_constants; /// - /// void main() - /// { + /// void main() { /// // TODO: Add code! /// } - /// # "#, vert); + /// # "#); /// ``` /// /// ```no_run @@ -2154,19 +2155,19 @@ impl Draw<'_> { /// Basic usage: /// /// ``` - /// # inline_spirv::inline_spirv!(r#" + /// # vk_shader_macros::glsl!(r#" /// #version 450 + /// #pragma shader_stage(compute) /// /// layout(push_constant) uniform PushConstants { /// layout(offset = 0) uint some_val1; /// layout(offset = 4) uint some_val2; /// } push_constants; /// - /// void main() - /// { + /// void main() { /// // TODO: Add code! /// } - /// # "#, vert); + /// # "#); /// ``` /// /// ```no_run @@ -4736,18 +4737,18 @@ impl RayTrace<'_> { /// Basic usage: /// /// ``` - /// # inline_spirv::inline_spirv!(r#" + /// # vk_shader_macros::glsl!(target: vulkan1_2, r#" /// #version 460 + /// #pragma shader_stage(closest) /// /// layout(push_constant) uniform PushConstants { /// layout(offset = 0) uint some_val; /// } push_constants; /// - /// void main() - /// { + /// void main() { /// // TODO: Add bindings to write things! /// } - /// # "#, rchit, vulkan1_2); + /// # "#); /// ``` /// /// ```no_run @@ -4805,8 +4806,9 @@ impl RayTrace<'_> { /// Basic usage: /// /// ``` - /// # inline_spirv::inline_spirv!(r#" + /// # vk_shader_macros::glsl!(target: vulkan1_2, r#" /// #version 460 + /// #pragma shader_stage(closest) /// /// layout(push_constant) uniform PushConstants { /// layout(offset = 0) uint some_val1; @@ -4817,7 +4819,7 @@ impl RayTrace<'_> { /// { /// // TODO: Add bindings to write things! /// } - /// # "#, rchit, vulkan1_2); + /// # "#); /// ``` /// /// ```no_run From 44ffb3179721651d12a6c0d95f5afaa8f8f24de5 Mon Sep 17 00:00:00 2001 From: John Wells Date: Sat, 14 Feb 2026 12:37:59 -0500 Subject: [PATCH 04/86] Update basic info structs --- contrib/vk-graph-window/src/lib.rs | 34 ++++++++----- src/driver/accel_struct.rs | 2 +- src/driver/buffer.rs | 77 +++++++++++++++++------------- src/driver/swapchain.rs | 24 ++++------ src/pool/alias.rs | 4 +- src/pool/fifo.rs | 6 ++- src/pool/lazy.rs | 8 +++- 7 files changed, 89 insertions(+), 66 deletions(-) diff --git a/contrib/vk-graph-window/src/lib.rs b/contrib/vk-graph-window/src/lib.rs index 86f58990..b074164a 100644 --- a/contrib/vk-graph-window/src/lib.rs +++ b/contrib/vk-graph-window/src/lib.rs @@ -4,6 +4,7 @@ pub use self::frame::FrameContext; use { log::{info, trace, warn}, + std::{error, fmt, sync::Arc}, vk_graph::{ driver::{ ash::vk, @@ -16,7 +17,6 @@ use { pool::hash::HashPool, Display, DisplayError, DisplayInfoBuilder, }, - std::{error, fmt, sync::Arc}, winit::{ application::ApplicationHandler, error::EventLoopError, @@ -90,17 +90,27 @@ impl Window { } if let Some(v_sync) = self.data.v_sync { - swapchain_info = if v_sync { - swapchain_info.present_modes(vec![ - vk::PresentModeKHR::FIFO_RELAXED, - vk::PresentModeKHR::FIFO, - ]) - } else { - swapchain_info.present_modes(vec![ - vk::PresentModeKHR::MAILBOX, - vk::PresentModeKHR::IMMEDIATE, - ]) - }; + let present_modes = Surface::present_modes(&surface)?; + if !present_modes.is_empty() { + let best_modes = if v_sync { + [vk::PresentModeKHR::FIFO_RELAXED, vk::PresentModeKHR::FIFO].as_slice() + } else { + [vk::PresentModeKHR::MAILBOX, vk::PresentModeKHR::IMMEDIATE].as_slice() + }; + + swapchain_info = swapchain_info.present_mode( + best_modes + .iter() + .copied() + .find(|best| present_modes.contains(best)) + .or_else(|| present_modes.get(0).copied()) + .ok_or_else(|| { + warn!("unsupported present modes: {present_modes:?}"); + + DriverError::Unsupported + })?, + ); + } } let swapchain = Swapchain::new(&self.device, surface, swapchain_info)?; diff --git a/src/driver/accel_struct.rs b/src/driver/accel_struct.rs index d1d367ec..541a07a1 100644 --- a/src/driver/accel_struct.rs +++ b/src/driver/accel_struct.rs @@ -127,7 +127,7 @@ impl AccelerationStructure { let device = Arc::clone(device); - Ok(AccelerationStructure { + Ok(Self { access: Mutex::new(AccessType::Nothing), accel_struct: (accel_struct, buffer), device, diff --git a/src/driver/buffer.rs b/src/driver/buffer.rs index f37878c6..bf9a81db 100644 --- a/src/driver/buffer.rs +++ b/src/driver/buffer.rs @@ -114,8 +114,15 @@ impl Buffer { let mut requirements = unsafe { device.get_buffer_memory_requirements(buffer) }; requirements.alignment = requirements.alignment.max(info.alignment); - let memory_location = if info.mappable { + let allocation_scheme = if info.dedicated { + AllocationScheme::DedicatedBuffer(buffer) + } else { + AllocationScheme::GpuAllocatorManaged + }; + let location = if info.host_write { MemoryLocation::CpuToGpu + } else if info.host_read { + MemoryLocation::GpuToCpu } else { MemoryLocation::GpuOnly }; @@ -132,9 +139,9 @@ impl Buffer { .allocate(&AllocationCreateDesc { name: "buffer", requirements, - location: memory_location, + location, linear: true, // Buffers are always linear - allocation_scheme: AllocationScheme::GpuAllocatorManaged, + allocation_scheme, }) .map_err(|err| { warn!("unable to allocate buffer memory: {err}"); @@ -387,8 +394,8 @@ impl Buffer { #[profiling::function] pub fn mapped_slice(this: &Self) -> &[u8] { debug_assert!( - this.info.mappable, - "Buffer is not mappable - create using mappable flag" + this.info.host_read, + "Buffer is not readable - create using host_read flag" ); &this.allocation.mapped_slice().unwrap()[0..this.info.size as usize] @@ -425,8 +432,8 @@ impl Buffer { #[profiling::function] pub fn mapped_slice_mut(this: &mut Self) -> &mut [u8] { debug_assert!( - this.info.mappable, - "Buffer is not mappable - create using mappable flag" + this.info.host_write, + "Buffer is not writable - create using host_write flag" ); &mut this.allocation.mapped_slice_mut().unwrap()[0..this.info.size as usize] @@ -710,9 +717,25 @@ pub struct BufferInfo { #[builder(default = "1")] pub alignment: vk::DeviceSize, + /// Specifies a dedicated memory allocation managed by the Vulkan driver and not by the internal + /// memory allocation pool transient resources share. + /// + /// The driver may optimize access to dedicated buffers. + #[builder(default)] + pub dedicated: bool, + /// Specifies a buffer whose memory is host visible and may be mapped. + /// + /// Memory optimal for CPU readback of data may be used. + #[builder(default)] + pub host_read: bool, + + /// Specifies a buffer whose memory is host visible and may be mapped. + /// + /// Memory optimal for uploading data to the GPU and potentially for constant buffers may be + /// used. #[builder(default)] - pub mappable: bool, + pub host_write: bool, /// Size in bytes of the buffer to be created. pub size: vk::DeviceSize, @@ -730,7 +753,9 @@ impl BufferInfo { pub const fn device_mem(size: vk::DeviceSize, usage: vk::BufferUsageFlags) -> BufferInfo { BufferInfo { alignment: 1, - mappable: false, + dedicated: false, + host_read: false, + host_write: false, size, usage, } @@ -754,30 +779,17 @@ impl BufferInfo { BufferInfo { alignment: 1, - mappable: true, + dedicated: false, + host_read: true, + host_write: true, size, usage, } } - /// Specifies a non-mappable buffer with the given `size` and `usage` values. - #[allow(clippy::new_ret_no_self)] - #[deprecated = "Use BufferInfo::device_mem()"] - #[doc(hidden)] - pub fn new(size: vk::DeviceSize, usage: vk::BufferUsageFlags) -> BufferInfoBuilder { - Self::device_mem(size, usage).to_builder() - } - - /// Specifies a mappable buffer with the given `size` and `usage` values. - /// - /// # Note - /// - /// For convenience the given usage value will be bitwise OR'd with - /// `TRANSFER_DST | TRANSFER_SRC`. - #[deprecated = "Use BufferInfo::host_mem()"] - #[doc(hidden)] - pub fn new_mappable(size: vk::DeviceSize, usage: vk::BufferUsageFlags) -> BufferInfoBuilder { - Self::host_mem(size, usage).to_builder() + /// Returns `true` if this information specifies host-accessible memory. + pub fn is_host_mem(&self) -> bool { + self.host_read | self.host_write } /// Converts a `BufferInfo` into a `BufferInfoBuilder`. @@ -785,7 +797,9 @@ impl BufferInfo { pub fn to_builder(self) -> BufferInfoBuilder { BufferInfoBuilder { alignment: Some(self.alignment), - mappable: Some(self.mappable), + dedicated: Some(self.dedicated), + host_read: Some(self.host_read), + host_write: Some(self.host_write), size: Some(self.size), usage: Some(self.usage), } @@ -809,9 +823,8 @@ impl BufferInfoBuilder { Ok(info) => info, }; - assert_eq!( - res.alignment.count_ones(), - 1, + assert!( + res.alignment.is_power_of_two(), "Alignment must be a power of two" ); diff --git a/src/driver/swapchain.rs b/src/driver/swapchain.rs index 528514e0..3186cb53 100644 --- a/src/driver/swapchain.rs +++ b/src/driver/swapchain.rs @@ -251,7 +251,6 @@ impl Swapchain { Self::destroy_swapchain(&self.device, &mut self.old_swapchain); let surface_caps = Surface::capabilities(&self.surface)?; - let present_modes = Surface::present_modes(&self.surface)?; let desired_image_count = Self::clamp_desired_image_count(self.info.desired_image_count, surface_caps); @@ -280,14 +279,6 @@ impl Swapchain { return Err(DriverError::Unsupported); } - let present_mode = self - .info - .present_modes - .iter() - .copied() - .find(|mode| present_modes.contains(mode)) - .unwrap_or(vk::PresentModeKHR::FIFO); - let pre_transform = if surface_caps .supported_transforms .contains(vk::SurfaceTransformFlagsKHR::IDENTITY) @@ -311,7 +302,7 @@ impl Swapchain { .image_sharing_mode(vk::SharingMode::EXCLUSIVE) .pre_transform(pre_transform) .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE) - .present_mode(present_mode) + .present_mode(self.info.present_mode) .clipped(true) .old_swapchain(self.swapchain) .image_array_layers(1); @@ -364,9 +355,10 @@ impl Swapchain { self.suboptimal = false; info!( - "swapchain {}x{} {present_mode:?}x{} {:?} {image_usage:#?}", + "swapchain {}x{} {:?}x{} {:?} {image_usage:#?}", self.info.width, self.info.height, + self.info.present_mode, self.images.len(), self.info.surface.format, ); @@ -506,7 +498,7 @@ pub struct SwapchainInfo { /// The desired, but not guaranteed, number of images that will be in the created swapchain. /// /// More images introduces more display lag, but smoother animation. - #[builder(default = "3")] + #[builder(default = "2")] pub desired_image_count: u32, /// The initial height of the surface. @@ -555,8 +547,8 @@ pub struct SwapchainInfo { /// /// * **Tearing**: No tearing will be observed. /// * **Also known as**: "Fast Vsync" - #[builder(default = vec![vk::PresentModeKHR::FIFO_RELAXED, vk::PresentModeKHR::FIFO])] - pub present_modes: Vec, + #[builder(default = vk::PresentModeKHR::MAILBOX)] + pub present_mode: vk::PresentModeKHR, /// The initial width of the surface. pub width: u32, @@ -571,7 +563,7 @@ impl SwapchainInfo { height, surface, desired_image_count: 3, - present_modes: vec![vk::PresentModeKHR::FIFO_RELAXED, vk::PresentModeKHR::FIFO], + present_mode: vk::PresentModeKHR::MAILBOX, } } @@ -582,7 +574,7 @@ impl SwapchainInfo { desired_image_count: Some(self.desired_image_count), height: Some(self.height), surface: Some(self.surface), - present_modes: Some(self.present_modes), + present_mode: Some(self.present_mode), width: Some(self.width), } } diff --git a/src/pool/alias.rs b/src/pool/alias.rs index d1f7b75c..789a68d7 100644 --- a/src/pool/alias.rs +++ b/src/pool/alias.rs @@ -147,7 +147,9 @@ where profiling::scope!("check aliases"); for (item_info, item) in &self.buffers { - if item_info.mappable == info.mappable + if (item_info.dedicated & info.dedicated) == info.dedicated + && item_info.host_read == info.host_read + && item_info.host_write == info.host_write && item_info.alignment >= info.alignment && item_info.size >= info.size && item_info.usage.contains(info.usage) diff --git a/src/pool/fifo.rs b/src/pool/fifo.rs index 9e825bb2..c9d60266 100644 --- a/src/pool/fifo.rs +++ b/src/pool/fifo.rs @@ -152,8 +152,10 @@ impl Pool for FifoPool { // superset of usage flags) for idx in 0..cache.len() { let item = unsafe { cache.get_unchecked(idx) }; - if item.info.alignment >= info.alignment - && item.info.mappable == info.mappable + if (item.info.dedicated & info.dedicated) == info.dedicated + && item.info.host_read == info.host_read + && item.info.host_write == info.host_write + && item.info.alignment >= info.alignment && item.info.size >= info.size && item.info.usage.contains(info.usage) { diff --git a/src/pool/lazy.rs b/src/pool/lazy.rs index 0c9f8c09..27d66586 100644 --- a/src/pool/lazy.rs +++ b/src/pool/lazy.rs @@ -201,7 +201,7 @@ impl Pool for LazyPool { fn lease(&mut self, info: BufferInfo) -> Result, DriverError> { let cache = self .buffer_cache - .entry((info.mappable, info.alignment)) + .entry((info.host_read | info.host_write, info.alignment)) .or_insert_with(|| PoolInfo::explicit_cache(self.info.buffer_capacity)); let cache_ref = Arc::downgrade(cache); @@ -217,7 +217,11 @@ impl Pool for LazyPool { // Look for a compatible buffer (big enough and superset of usage flags) for idx in 0..cache.len() { let item = unsafe { cache.get_unchecked(idx) }; - if item.info.size >= info.size && item.info.usage.contains(info.usage) { + if (item.info.dedicated & info.dedicated) == info.dedicated + && (item.info.host_read & info.host_read) == info.host_read + && (item.info.host_write & info.host_write) == info.host_write + && item.info.alignment >= info.alignment + && item.info.size >= info.size && item.info.usage.contains(info.usage) { let item = cache.swap_remove(idx); return Ok(Lease::new(cache_ref, item)); From 61e8874c1f62efdb53ef1cbd1ab3b713095e2eb4 Mon Sep 17 00:00:00 2001 From: John Wells Date: Sat, 14 Feb 2026 17:49:37 -0500 Subject: [PATCH 05/86] read-only info --- contrib/vk-graph-window/src/lib.rs | 26 +++--- examples/app.rs | 2 +- examples/shader-toy/src/main.rs | 2 +- src/display.rs | 4 +- src/driver/accel_struct.rs | 67 ++++++++++++-- src/driver/buffer.rs | 71 +++++++++++---- src/driver/compute.rs | 54 +++++++++-- src/driver/graphic.rs | 39 +++++++- src/driver/image.rs | 79 +++++++++++----- src/driver/ray_trace.rs | 55 ++++++++++-- src/driver/surface.rs | 51 ++++++++--- src/driver/swapchain.rs | 139 +++++++++++++++++++---------- src/graph/mod.rs | 28 +++--- src/graph/pass_ref.rs | 51 ++++++----- src/graph/resolver.rs | 18 ++-- src/lib.rs | 8 +- src/pool/lazy.rs | 4 +- 17 files changed, 505 insertions(+), 193 deletions(-) diff --git a/contrib/vk-graph-window/src/lib.rs b/contrib/vk-graph-window/src/lib.rs index b074164a..346f5ccb 100644 --- a/contrib/vk-graph-window/src/lib.rs +++ b/contrib/vk-graph-window/src/lib.rs @@ -71,7 +71,7 @@ impl Window { &mut self, window: &winit::window::Window, ) -> Result { - let surface = Surface::create(&self.device, &window)?; + let surface = Surface::create(&self.device, &window, &window)?; let surface_formats = Surface::formats(&surface)?; let surface_format = self .data @@ -85,8 +85,8 @@ impl Window { SwapchainInfo::new(window_size.width, window_size.height, surface_format) .to_builder(); - if let Some(image_count) = self.data.image_count { - swapchain_info = swapchain_info.desired_image_count(image_count); + if let Some(min_image_count) = self.data.min_image_count { + swapchain_info = swapchain_info.min_image_count(min_image_count); } if let Some(v_sync) = self.data.v_sync { @@ -400,7 +400,7 @@ pub struct WindowBuilder { attributes: WindowAttributes, cmd_buf_count: usize, device_info: DeviceInfo, - image_count: Option, + min_image_count: Option, surface_format_fn: Option vk::SurfaceFormatKHR>>, v_sync: Option, window_mode_override: Option>, @@ -415,7 +415,7 @@ impl WindowBuilder { data: WindowData { attributes: self.attributes, cmd_buf_count: self.cmd_buf_count, - image_count: self.image_count, + min_image_count: self.min_image_count, surface_format_fn: self.surface_format_fn, v_sync: self.v_sync, window_mode_override: self.window_mode_override, @@ -463,11 +463,13 @@ impl WindowBuilder { self } - /// The desired, but not guaranteed, number of images that will be in the created swapchain. + /// The minimum number of presentable images that the application needs. The implementation will + /// either create the swapchain with at least that many images, or it will fail to create the + /// swapchain. /// - /// More images introduces more display lag, but smoother animation. - pub fn desired_image_count(mut self, count: u32) -> Self { - self.image_count = Some(count); + /// More images introduce more display lag, but smoother animation. + pub fn min_image_count(mut self, count: u32) -> Self { + self.min_image_count = Some(count); self } @@ -521,7 +523,7 @@ impl fmt::Debug for WindowBuilder { .field("attributes", &self.attributes) .field("cmd_buffer_count", &self.cmd_buf_count) .field("device_info", &self.device_info) - .field("image_count", &self.image_count) + .field("min_image_count", &self.min_image_count) .field( "surface_format_fn", &self.surface_format_fn.as_ref().map(|_| ()), @@ -538,7 +540,7 @@ impl Default for WindowBuilder { attributes: Default::default(), cmd_buf_count: 5, device_info: Default::default(), - image_count: None, + min_image_count: None, surface_format_fn: None, v_sync: None, window_mode_override: None, @@ -549,7 +551,7 @@ impl Default for WindowBuilder { struct WindowData { attributes: WindowAttributes, cmd_buf_count: usize, - image_count: Option, + min_image_count: Option, surface_format_fn: Option vk::SurfaceFormatKHR>>, v_sync: Option, window_mode_override: Option>, diff --git a/examples/app.rs b/examples/app.rs index 0b869586..f14e031b 100644 --- a/examples/app.rs +++ b/examples/app.rs @@ -46,7 +46,7 @@ impl ApplicationHandler for Application { let device_info = DeviceInfoBuilder::default().debug(args.debug); let device = Arc::new(Device::create_display(device_info, &window).unwrap()); - let surface = Surface::create(&device, &window).unwrap(); + let surface = Surface::create(&device, &window, &window).unwrap(); let surface_formats = Surface::formats(&surface).unwrap(); let surface_format = Surface::linear_or_default(&surface_formats); let window_size = window.inner_size(); diff --git a/examples/shader-toy/src/main.rs b/examples/shader-toy/src/main.rs index 83ae3a46..ecbe16e2 100644 --- a/examples/shader-toy/src/main.rs +++ b/examples/shader-toy/src/main.rs @@ -50,7 +50,7 @@ fn main() -> anyhow::Result<()> { let args = Args::parse(); let window = WindowBuilder::default() .debug(args.debug) - .desired_image_count(3) + .min_image_count(3) .window(|builder| builder.with_inner_size(PhysicalSize::new(1280.0f64, 720.0f64))) .build()?; let display = GraphicPresenter::new(&window.device).context("Presenter")?; diff --git a/src/display.rs b/src/display.rs index 22f7ad44..5f870798 100644 --- a/src/display.rs +++ b/src/display.rs @@ -189,7 +189,7 @@ impl Display { discard_contents: false, src_queue_family_index: vk::QUEUE_FAMILY_IGNORED, dst_queue_family_index: vk::QUEUE_FAMILY_IGNORED, - image: ***swapchain_image, + image: swapchain_image.handle, range, }), ); @@ -266,7 +266,7 @@ impl Display { /// Gets information about the swapchain. pub fn swapchain_info(&self) -> SwapchainInfo { - self.swapchain.info() + self.swapchain.info } } diff --git a/src/driver/accel_struct.rs b/src/driver/accel_struct.rs index 541a07a1..96f14164 100644 --- a/src/driver/accel_struct.rs +++ b/src/driver/accel_struct.rs @@ -52,13 +52,59 @@ use std::sync::Mutex; /// [deref]: core::ops::Deref /// [fully qualified syntax]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name #[derive(Debug)] +#[repr(C)] pub struct AccelerationStructure { access: Mutex, - accel_struct: (vk::AccelerationStructureKHR, Buffer), + + /// The native Vulkan resource handle of the buffer which supports this acceleration structure. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub buffer: Buffer, + + #[cfg(not(doc))] + buffer: Buffer, + + /// The device which owns this buffer resource. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub device: Arc, + + #[cfg(not(doc))] device: Arc, + /// The native Vulkan resource handle of this acceleration structure. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub handle: vk::AccelerationStructureKHR, + + #[cfg(not(doc))] + handle: vk::AccelerationStructureKHR, + /// Information used to create this object. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub info: AccelerationStructureInfo, + + #[cfg(not(doc))] + info: AccelerationStructureInfo, + + /// A name for debugging purposes. + pub name: Option, +} + +#[doc(hidden)] +#[repr(C)] +pub struct AccelerationStructureRef { + access: Mutex, + pub buffer: Buffer, + pub device: Arc, + pub handle: vk::AccelerationStructureKHR, pub info: AccelerationStructureInfo, + pub name: Option, } impl AccelerationStructure { @@ -80,7 +126,7 @@ impl AccelerationStructure { /// let info = AccelerationStructureInfo::blas(SIZE); /// let accel_struct = AccelerationStructure::create(&device, info)?; /// - /// assert_ne!(*accel_struct, vk::AccelerationStructureKHR::null()); + /// assert_ne!(accel_struct.handle, vk::AccelerationStructureKHR::null()); /// assert_eq!(accel_struct.info.size, SIZE); /// # Ok(()) } /// ``` @@ -102,10 +148,10 @@ impl AccelerationStructure { ), )?; - let accel_struct = { + let handle = { let create_info = vk::AccelerationStructureCreateInfoKHR::default() .ty(info.ty) - .buffer(*buffer) + .buffer(buffer.handle) .size(info.size); let accel_struct_ext = Device::expect_accel_struct_ext(device); @@ -129,9 +175,11 @@ impl AccelerationStructure { Ok(Self { access: Mutex::new(AccessType::Nothing), - accel_struct: (accel_struct, buffer), + buffer, device, + handle, info, + name: None, }) } @@ -219,7 +267,7 @@ impl AccelerationStructure { unsafe { accel_struct_ext.get_acceleration_structure_device_address( &vk::AccelerationStructureDeviceAddressInfoKHR::default() - .acceleration_structure(this.accel_struct.0), + .acceleration_structure(this.handle), ) } } @@ -325,11 +373,12 @@ impl AccelerationStructure { } } +#[doc(hidden)] impl Deref for AccelerationStructure { - type Target = vk::AccelerationStructureKHR; + type Target = AccelerationStructureRef; fn deref(&self) -> &Self::Target { - &self.accel_struct.0 + unsafe { &*(self as *const Self as *const Self::Target) } } } @@ -343,7 +392,7 @@ impl Drop for AccelerationStructure { let accel_struct_ext = Device::expect_accel_struct_ext(&self.device); unsafe { - accel_struct_ext.destroy_acceleration_structure(self.accel_struct.0, None); + accel_struct_ext.destroy_acceleration_structure(self.handle, None); } } } diff --git a/src/driver/buffer.rs b/src/driver/buffer.rs index bf9a81db..6a2a6cd8 100644 --- a/src/driver/buffer.rs +++ b/src/driver/buffer.rs @@ -54,19 +54,53 @@ use std::sync::Mutex; /// [buffer]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBuffer.html /// [deref]: core::ops::Deref /// [fully qualified syntax]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name +#[repr(C)] pub struct Buffer { accesses: Mutex, allocation: ManuallyDrop, - buffer: vk::Buffer, + + /// The device which owns this buffer resource. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub device: Arc, + + #[cfg(not(doc))] device: Arc, - /// Information used to create this object. + /// The native Vulkan resource handle of this buffer. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub handle: vk::Buffer, + + #[cfg(not(doc))] + handle: vk::Buffer, + + /// Information used to create this resource. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] pub info: BufferInfo, + #[cfg(not(doc))] + info: BufferInfo, + /// A name for debugging purposes. pub name: Option, } +#[doc(hidden)] +#[repr(C)] +pub struct BufferRef { + accesses: Mutex, + allocation: ManuallyDrop, + pub device: Arc, + pub handle: vk::Buffer, + pub info: BufferInfo, + pub name: Option, +} + impl Buffer { /// Creates a new buffer on the given device. /// @@ -86,7 +120,7 @@ impl Buffer { /// let info = BufferInfo::host_mem(SIZE, vk::BufferUsageFlags::UNIFORM_BUFFER); /// let buf = Buffer::create(&device, info)?; /// - /// assert_ne!(*buf, vk::Buffer::null()); + /// assert_ne!(buf.handle, vk::Buffer::null()); /// assert_eq!(buf.info.size, SIZE); /// # Ok(()) } /// ``` @@ -104,18 +138,18 @@ impl Buffer { .usage(info.usage) .sharing_mode(vk::SharingMode::CONCURRENT) .queue_family_indices(&device.physical_device.queue_family_indices); - let buffer = unsafe { + let handle = unsafe { device.create_buffer(&buffer_info, None).map_err(|err| { warn!("unable to create buffer: {err}"); DriverError::Unsupported })? }; - let mut requirements = unsafe { device.get_buffer_memory_requirements(buffer) }; + let mut requirements = unsafe { device.get_buffer_memory_requirements(handle) }; requirements.alignment = requirements.alignment.max(info.alignment); let allocation_scheme = if info.dedicated { - AllocationScheme::DedicatedBuffer(buffer) + AllocationScheme::DedicatedBuffer(handle) } else { AllocationScheme::GpuAllocatorManaged }; @@ -147,14 +181,14 @@ impl Buffer { warn!("unable to allocate buffer memory: {err}"); unsafe { - device.destroy_buffer(buffer, None); + device.destroy_buffer(handle, None); } DriverError::from_alloc_err(err) }) .and_then(|allocation| { if let Err(err) = unsafe { - device.bind_buffer_memory(buffer, allocation.memory(), allocation.offset()) + device.bind_buffer_memory(handle, allocation.memory(), allocation.offset()) } { warn!("unable to bind buffer memory: {err}"); @@ -163,7 +197,7 @@ impl Buffer { } unsafe { - device.destroy_buffer(buffer, None); + device.destroy_buffer(handle, None); } Err(DriverError::OutOfMemory) @@ -173,13 +207,13 @@ impl Buffer { }) }?; - debug_assert_ne!(buffer, vk::Buffer::null()); + debug_assert_ne!(handle, vk::Buffer::null()); Ok(Self { accesses: Mutex::new(BufferAccess::new(info.size)), allocation: ManuallyDrop::new(allocation), - buffer, device, + handle, info, name: None, }) @@ -202,7 +236,7 @@ impl Buffer { /// const DATA: [u8; 4] = [0xfe, 0xed, 0xbe, 0xef]; /// let buf = Buffer::create_from_slice(&device, vk::BufferUsageFlags::UNIFORM_BUFFER, &DATA)?; /// - /// assert_ne!(*buf, vk::Buffer::null()); + /// assert_ne!(buf.handle, vk::Buffer::null()); /// assert_eq!(buf.info.size, 4); /// assert_eq!(Buffer::mapped_slice(&buf), &DATA); /// # Ok(()) } @@ -359,7 +393,7 @@ impl Buffer { unsafe { this.device.get_buffer_device_address( - &vk::BufferDeviceAddressInfo::default().buffer(this.buffer), + &vk::BufferDeviceAddressInfo::default().buffer(this.handle), ) } } @@ -443,18 +477,19 @@ impl Buffer { impl Debug for Buffer { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { if let Some(name) = &self.name { - write!(f, "{} ({:?})", name, self.buffer) + write!(f, "{} ({:?})", name, self.handle) } else { - write!(f, "{:?}", self.buffer) + write!(f, "{:?}", self.handle) } } } +#[doc(hidden)] impl Deref for Buffer { - type Target = vk::Buffer; + type Target = BufferRef; fn deref(&self) -> &Self::Target { - &self.buffer + unsafe { &*(self as *const Self as *const Self::Target) } } } @@ -479,7 +514,7 @@ impl Drop for Buffer { .unwrap_or_else(|err| warn!("unable to free buffer allocation: {err}")); unsafe { - self.device.destroy_buffer(self.buffer, None); + self.device.destroy_buffer(self.handle, None); } } } diff --git a/src/driver/compute.rs b/src/driver/compute.rs index b55d5103..b203eb20 100644 --- a/src/driver/compute.rs +++ b/src/driver/compute.rs @@ -24,19 +24,54 @@ use { /// [pipeline]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipeline.html /// [deref]: core::ops::Deref #[derive(Debug)] +#[repr(C)] pub struct ComputePipeline { pub(crate) descriptor_bindings: DescriptorBindingMap, pub(crate) descriptor_info: PipelineDescriptorInfo, + + /// The device which owns this buffer resource. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub device: Arc, + + #[cfg(not(doc))] device: Arc, + pub(crate) layout: vk::PipelineLayout, + /// The native Vulkan resource handle of this pipeline. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub handle: vk::Pipeline, + + #[cfg(not(doc))] + handle: vk::Pipeline, + /// Information used to create this object. + #[cfg(doc)] pub info: ComputePipelineInfo, + #[cfg(not(doc))] + info: ComputePipelineInfo, + /// A descriptive name used in debugging messages. pub name: Option, - pipeline: vk::Pipeline, + pub(crate) push_constants: Option, +} + +#[doc(hidden)] +#[repr(C)] +pub struct ComputePipelineRef { + pub(crate) descriptor_bindings: DescriptorBindingMap, + pub(crate) descriptor_info: PipelineDescriptorInfo, + pub device: Arc, + pub(crate) layout: vk::PipelineLayout, + pub handle: vk::Pipeline, + pub info: ComputePipelineInfo, + pub name: Option, pub(crate) push_constants: Option, } @@ -65,7 +100,7 @@ impl ComputePipeline { /// let shader = Shader::new_compute(my_shader_code.as_slice()); /// let pipeline = ComputePipeline::create(&device, ComputePipelineInfo::default(), shader)?; /// - /// assert_ne!(*pipeline, vk::Pipeline::null()); + /// assert_ne!(pipeline.handle, vk::Pipeline::null()); /// # Ok(()) } /// ``` #[profiling::function] @@ -136,12 +171,14 @@ impl ComputePipeline { .map_err(|err| { warn!("{err}"); + device.destroy_shader_module(shader_module, None); + DriverError::Unsupported })?; let pipeline_info = vk::ComputePipelineCreateInfo::default() .stage(stage_create_info) .layout(layout); - let pipeline = device + let handle = device .create_compute_pipelines( Device::pipeline_cache(&device), from_ref(&pipeline_info), @@ -150,6 +187,8 @@ impl ComputePipeline { .map_err(|(_, err)| { warn!("{err}"); + device.destroy_shader_module(shader_module, None); + DriverError::Unsupported })?[0]; @@ -159,10 +198,10 @@ impl ComputePipeline { descriptor_bindings, descriptor_info, device, + handle, info, layout, name: None, - pipeline, push_constants, }) } @@ -175,11 +214,12 @@ impl ComputePipeline { } } +#[doc(hidden)] impl Deref for ComputePipeline { - type Target = vk::Pipeline; + type Target = ComputePipelineRef; fn deref(&self) -> &Self::Target { - &self.pipeline + unsafe { &*(self as *const Self as *const Self::Target) } } } @@ -191,7 +231,7 @@ impl Drop for ComputePipeline { } unsafe { - self.device.destroy_pipeline(self.pipeline, None); + self.device.destroy_pipeline(self.handle, None); self.device.destroy_pipeline_layout(self.layout, None); } } diff --git a/src/driver/graphic.rs b/src/driver/graphic.rs index ea193eae..080e51e5 100644 --- a/src/driver/graphic.rs +++ b/src/driver/graphic.rs @@ -14,7 +14,7 @@ use { derive_builder::{Builder, UninitializedFieldError}, log::{Level::Trace, log_enabled, trace, warn}, ordered_float::OrderedFloat, - std::{collections::HashSet, ffi::CString, sync::Arc, thread::panicking}, + std::{collections::HashSet, ffi::CString, ops::Deref, sync::Arc, thread::panicking}, }; const RGBA_COLOR_COMPONENTS: vk::ColorComponentFlags = vk::ColorComponentFlags::from_raw( @@ -348,14 +348,27 @@ impl From for DepthStencilModeBuilderError { /// /// [pipeline]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipeline.html #[derive(Debug)] +#[repr(C)] pub struct GraphicPipeline { pub(crate) descriptor_bindings: DescriptorBindingMap, pub(crate) descriptor_info: PipelineDescriptorInfo, + + /// The device which owns this buffer resource. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub device: Arc, + + #[cfg(not(doc))] device: Arc, /// Information used to create this object. + #[cfg(doc)] pub info: GraphicPipelineInfo, + #[cfg(not(doc))] + info: GraphicPipelineInfo, + pub(crate) input_attachments: Box<[u32]>, pub(crate) layout: vk::PipelineLayout, @@ -367,6 +380,21 @@ pub struct GraphicPipeline { pub(super) state: GraphicPipelineState, } +#[doc(hidden)] +#[repr(C)] +pub struct GraphicPipelineRef { + pub(crate) descriptor_bindings: DescriptorBindingMap, + pub(crate) descriptor_info: PipelineDescriptorInfo, + pub device: Arc, + pub info: GraphicPipelineInfo, + pub(crate) input_attachments: Box<[u32]>, + pub(crate) layout: vk::PipelineLayout, + pub name: Option, + pub(crate) push_constants: Vec, + pub(crate) shader_modules: Vec, + pub(super) state: GraphicPipelineState, +} + impl GraphicPipeline { /// Creates a new graphic pipeline on the given device. /// @@ -597,6 +625,15 @@ impl GraphicPipeline { } } +#[doc(hidden)] +impl Deref for GraphicPipeline { + type Target = GraphicPipelineRef; + + fn deref(&self) -> &Self::Target { + unsafe { &*(self as *const Self as *const Self::Target) } + } +} + impl Drop for GraphicPipeline { #[profiling::function] fn drop(&mut self) { diff --git a/src/driver/image.rs b/src/driver/image.rs index 3b34dd50..dab35911 100644 --- a/src/driver/image.rs +++ b/src/driver/image.rs @@ -92,21 +92,57 @@ pub(crate) fn image_subresource_range_intersects( /// [image]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImage.html /// [deref]: core::ops::Deref /// [fully qualified syntax]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name +#[repr(C)] pub struct Image { accesses: Mutex>, allocation: Option, // None when we don't own the image (Swapchain images) - pub(super) device: Arc, - image: vk::Image, + + /// The device which owns this image resource. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub device: Arc, + + #[cfg(not(doc))] + device: Arc, + + /// The native Vulkan resource handle of this image. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub handle: vk::Image, + + #[cfg(not(doc))] + handle: vk::Image, + #[allow(clippy::type_complexity)] image_view_cache: Mutex>, - /// Information used to create this object. + /// Information used to create this resource. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] pub info: ImageInfo, + #[cfg(not(doc))] + info: ImageInfo, + /// A name for debugging purposes. pub name: Option, } +#[doc(hidden)] +#[repr(C)] +pub struct ImageRef { + accesses: Mutex>, + allocation: Option, // None when we don't own the image (Swapchain images) + pub device: Arc, + pub handle: vk::Image, + image_view_cache: Mutex>, + pub info: ImageInfo, + pub name: Option, +} + impl Image { /// Creates a new image on the given device. /// @@ -125,7 +161,7 @@ impl Image { /// let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); /// let image = Image::create(&device, info)?; /// - /// assert_ne!(*image, vk::Image::null()); + /// assert_ne!(image.handle, vk::Image::null()); /// assert_eq!(image.info.width, 32); /// assert_eq!(image.info.height, 32); /// # Ok(()) } @@ -149,14 +185,14 @@ impl Image { let create_info: ImageCreateInfo = info.into(); let create_info = create_info.queue_family_indices(&device.physical_device.queue_family_indices); - let image = unsafe { + let handle = unsafe { device.create_image(&create_info, None).map_err(|err| { warn!("unable to create image: {err}"); DriverError::Unsupported })? }; - let requirements = unsafe { device.get_image_memory_requirements(image) }; + let requirements = unsafe { device.get_image_memory_requirements(handle) }; let allocation = { profiling::scope!("allocate"); @@ -178,14 +214,14 @@ impl Image { warn!("unable to allocate image memory: {err}"); unsafe { - device.destroy_image(image, None); + device.destroy_image(handle, None); } DriverError::from_alloc_err(err) }) .and_then(|allocation| { if let Err(err) = unsafe { - device.bind_image_memory(image, allocation.memory(), allocation.offset()) + device.bind_image_memory(handle, allocation.memory(), allocation.offset()) } { warn!("unable to bind image memory: {err}"); @@ -194,7 +230,7 @@ impl Image { } unsafe { - device.destroy_image(image, None); + device.destroy_image(handle, None); } Err(DriverError::OutOfMemory) @@ -204,13 +240,13 @@ impl Image { }) }?; - debug_assert_ne!(image, vk::Image::null()); + debug_assert_ne!(handle, vk::Image::null()); Ok(Self { accesses, allocation: Some(allocation), device, - image, + handle, image_view_cache: Mutex::new(Default::default()), info, name: None, @@ -314,7 +350,7 @@ impl Image { // Does NOT copy over the image accesses! // Force previous access to general to wait for presentation - let Self { image, info, .. } = *this; + let Self { handle, info, .. } = *this; let accesses = ImageAccess::new(info, AccessType::General); let accesses = Mutex::new(accesses); @@ -322,7 +358,7 @@ impl Image { accesses, allocation: None, device: Arc::clone(&this.device), - image, + handle, image_view_cache: Mutex::new(image_view_cache), info, name: this.name.clone(), @@ -344,7 +380,7 @@ impl Image { } unsafe { - this.device.destroy_image(this.image, None); + this.device.destroy_image(this.handle, None); } { @@ -366,7 +402,7 @@ impl Image { /// The image is not destroyed automatically on drop, unlike images created through the /// [`Image::create`] function. #[profiling::function] - pub fn from_raw(device: &Arc, image: vk::Image, info: impl Into) -> Self { + pub fn from_raw(device: &Arc, handle: vk::Image, info: impl Into) -> Self { let device = Arc::clone(device); let info = info.into(); @@ -380,7 +416,7 @@ impl Image { accesses: Mutex::new(accesses), allocation: None, device, - image, + handle, image_view_cache: Mutex::new(Default::default()), info, name: None, @@ -399,7 +435,7 @@ impl Image { Entry::Occupied(entry) => entry.get().image_view, Entry::Vacant(entry) => { entry - .insert(ImageView::create(&this.device, info, this.image)?) + .insert(ImageView::create(&this.device, info, this.handle)?) .image_view } }) @@ -409,18 +445,19 @@ impl Image { impl Debug for Image { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { if let Some(name) = &self.name { - write!(f, "{} ({:?})", name, self.image) + write!(f, "{} ({:?})", name, self.handle) } else { - write!(f, "{:?}", self.image) + write!(f, "{:?}", self.handle) } } } +#[doc(hidden)] impl Deref for Image { - type Target = vk::Image; + type Target = ImageRef; fn deref(&self) -> &Self::Target { - &self.image + unsafe { &*(self as *const Self as *const Self::Target) } } } diff --git a/src/driver/ray_trace.rs b/src/driver/ray_trace.rs index d2ed5bc5..0a304449 100644 --- a/src/driver/ray_trace.rs +++ b/src/driver/ray_trace.rs @@ -29,21 +29,57 @@ use { /// [deref]: core::ops::Deref /// [fully qualified syntax]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name #[derive(Debug)] +#[repr(C)] pub struct RayTracePipeline { pub(crate) descriptor_bindings: DescriptorBindingMap, pub(crate) descriptor_info: PipelineDescriptorInfo, + + /// The device which owns this buffer resource. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub device: Arc, + + #[cfg(not(doc))] device: Arc, + /// The native Vulkan resource handle of this pipeline. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub handle: vk::Pipeline, + + #[cfg(not(doc))] + handle: vk::Pipeline, + /// Information used to create this object. + #[cfg(doc)] pub info: RayTracePipelineInfo, + #[cfg(not(doc))] + info: RayTracePipelineInfo, + pub(crate) layout: vk::PipelineLayout, /// A descriptive name used in debugging messages. pub name: Option, pub(crate) push_constants: Vec, - pipeline: vk::Pipeline, + shader_modules: Vec, + shader_group_handles: Vec, +} + +#[doc(hidden)] +#[repr(C)] +pub struct RayTracePipelineRef { + pub(crate) descriptor_bindings: DescriptorBindingMap, + pub(crate) descriptor_info: PipelineDescriptorInfo, + pub device: Arc, + pub handle: vk::Pipeline, + pub info: RayTracePipelineInfo, + pub(crate) layout: vk::PipelineLayout, + pub name: Option, + pub(crate) push_constants: Vec, shader_modules: Vec, shader_group_handles: Vec, } @@ -97,7 +133,7 @@ impl RayTracePipeline { /// ], /// )?; /// - /// assert_ne!(*pipeline, vk::Pipeline::null()); + /// assert_ne!(pipeline.handle, vk::Pipeline::null()); /// assert_eq!(pipeline.info.max_ray_recursion_depth, 1); /// # Ok(()) } /// ``` @@ -222,7 +258,7 @@ impl RayTracePipeline { .ray_trace_ext .as_ref() .ok_or(DriverError::Unsupported)?; - let pipeline = ray_trace_ext + let handle = ray_trace_ext .create_ray_tracing_pipelines( vk::DeferredOperationKHR::null(), Device::pipeline_cache(device), @@ -285,7 +321,7 @@ impl RayTracePipeline { // let shader_group_handles = { ray_trace_ext.get_ray_tracing_shader_group_handles( - pipeline, + handle, 0, group_count as u32, group_count * shader_group_handle_size as usize, @@ -297,11 +333,11 @@ impl RayTracePipeline { descriptor_bindings, descriptor_info, device, + handle, info, layout, name: None, push_constants, - pipeline, shader_modules, shader_group_handles, }) @@ -348,7 +384,7 @@ impl RayTracePipeline { .ray_trace_ext .as_ref() .unwrap_unchecked() - .get_ray_tracing_shader_group_stack_size(this.pipeline, group, group_shader) + .get_ray_tracing_shader_group_stack_size(this.handle, group, group_shader) } } @@ -359,11 +395,12 @@ impl RayTracePipeline { } } +#[doc(hidden)] impl Deref for RayTracePipeline { - type Target = vk::Pipeline; + type Target = RayTracePipelineRef; fn deref(&self) -> &Self::Target { - &self.pipeline + unsafe { &*(self as *const Self as *const Self::Target) } } } @@ -375,7 +412,7 @@ impl Drop for RayTracePipeline { } unsafe { - self.device.destroy_pipeline(self.pipeline, None); + self.device.destroy_pipeline(self.handle, None); self.device.destroy_pipeline_layout(self.layout, None); } diff --git a/src/driver/surface.rs b/src/driver/surface.rs index e5e4a7ef..1a040e26 100644 --- a/src/driver/surface.rs +++ b/src/driver/surface.rs @@ -15,9 +15,32 @@ use { }; /// Smart pointer handle to a [`vk::SurfaceKHR`] object. +#[repr(C)] pub struct Surface { + /// The device which owns this buffer resource. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub device: Arc, + + #[cfg(not(doc))] device: Arc, - surface: vk::SurfaceKHR, + + /// The native Vulkan resource handle of this surface. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub handle: vk::SurfaceKHR, + + #[cfg(not(doc))] + handle: vk::SurfaceKHR, +} + +#[doc(hidden)] +#[repr(C)] +pub struct SurfaceRef { + pub device: Arc, + pub handle: vk::SurfaceKHR, } impl Surface { @@ -26,10 +49,8 @@ impl Surface { let surface_ext = Device::expect_surface_ext(&this.device); unsafe { - surface_ext.get_physical_device_surface_capabilities( - *this.device.physical_device, - this.surface, - ) + surface_ext + .get_physical_device_surface_capabilities(*this.device.physical_device, this.handle) } .inspect_err(|err| warn!("unable to get surface capabilities: {err}")) .or(Err(DriverError::Unsupported)) @@ -42,11 +63,12 @@ impl Surface { #[profiling::function] pub fn create( device: &Arc, - window: &(impl HasDisplayHandle + HasWindowHandle), + display: impl HasDisplayHandle, + window: impl HasWindowHandle, ) -> Result { let device = Arc::clone(device); let instance = Device::instance(&device); - let display_handle = window.display_handle().map_err(|err| { + let display_handle = display.display_handle().map_err(|err| { warn!("{err}"); DriverError::Unsupported @@ -56,7 +78,7 @@ impl Surface { DriverError::Unsupported })?; - let surface = unsafe { + let handle = unsafe { create_surface( Instance::entry(instance), instance, @@ -71,7 +93,7 @@ impl Surface { DriverError::Unsupported })?; - Ok(Self { device, surface }) + Ok(Self { device, handle }) } /// Lists the supported surface formats. @@ -82,7 +104,7 @@ impl Surface { .surface_ext .as_ref() .unwrap() - .get_physical_device_surface_formats(*this.device.physical_device, this.surface) + .get_physical_device_surface_formats(*this.device.physical_device, this.handle) .map_err(|err| { warn!("Unable to get surface formats: {err}"); @@ -121,7 +143,7 @@ impl Surface { unsafe { surface_ext.get_physical_device_surface_present_modes( *this.device.physical_device, - this.surface, + this.handle, ) } .inspect_err(|err| warn!("unable to get surface present modes: {err}")) @@ -164,11 +186,12 @@ impl Debug for Surface { } } +#[doc(hidden)] impl Deref for Surface { - type Target = vk::SurfaceKHR; + type Target = SurfaceRef; fn deref(&self) -> &Self::Target { - &self.surface + unsafe { &*(self as *const Self as *const Self::Target) } } } @@ -182,7 +205,7 @@ impl Drop for Surface { let surface_ext = Device::expect_surface_ext(&self.device); unsafe { - surface_ext.destroy_surface(self.surface, None); + surface_ext.destroy_surface(self.handle, None); } } } diff --git a/src/driver/swapchain.rs b/src/driver/swapchain.rs index 3186cb53..e01c742e 100644 --- a/src/driver/swapchain.rs +++ b/src/driver/swapchain.rs @@ -16,14 +16,60 @@ use { /// Provides the ability to present rendering results to a [`Surface`]. #[derive(Debug)] +#[repr(C)] pub struct Swapchain { + /// The device which owns this buffer resource. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub device: Arc, + + #[cfg(not(doc))] device: Arc, + + /// The native Vulkan resource handle of this swapchain. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub handle: vk::SwapchainKHR, + + #[cfg(not(doc))] + handle: vk::SwapchainKHR, + + handle_prev: vk::SwapchainKHR, images: Box<[SwapchainImage]>, + + /// Information used to create this resource. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub info: SwapchainInfo, + + #[cfg(not(doc))] info: SwapchainInfo, - old_swapchain: vk::SwapchainKHR, + suboptimal: bool, + + /// The surface which supports this swapchain. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub surface: Surface, + + #[cfg(not(doc))] surface: Surface, - swapchain: vk::SwapchainKHR, +} + +#[doc(hidden)] +#[repr(C)] +pub struct SwapchainRef { + pub device: Arc, + pub handle: vk::SwapchainKHR, + handle_prev: vk::SwapchainKHR, + images: Box<[SwapchainImage]>, + pub info: SwapchainInfo, + suboptimal: bool, + pub surface: Surface, } impl Swapchain { @@ -41,11 +87,11 @@ impl Swapchain { Ok(Swapchain { device, images: Default::default(), + handle: vk::SwapchainKHR::null(), + handle_prev: vk::SwapchainKHR::null(), info, - old_swapchain: vk::SwapchainKHR::null(), suboptimal: true, surface, - swapchain: vk::SwapchainKHR::null(), }) } @@ -70,12 +116,7 @@ impl Swapchain { let swapchain_ext = Device::expect_swapchain_ext(&self.device); let image_idx = unsafe { - swapchain_ext.acquire_next_image( - self.swapchain, - u64::MAX, - acquired, - vk::Fence::null(), - ) + swapchain_ext.acquire_next_image(self.handle, u64::MAX, acquired, vk::Fence::null()) } .map(|(idx, suboptimal)| { if suboptimal { @@ -146,17 +187,14 @@ impl Swapchain { Err(SwapchainError::Suboptimal) } - fn clamp_desired_image_count( - desired_image_count: u32, - surface_capabilities: vk::SurfaceCapabilitiesKHR, - ) -> u32 { - let mut desired_image_count = desired_image_count.max(surface_capabilities.min_image_count); + fn clamp_min_image_count(min_image_count: u32, surface: vk::SurfaceCapabilitiesKHR) -> u32 { + let min_image_count = min_image_count.max(surface.min_image_count); - if surface_capabilities.max_image_count != 0 { - desired_image_count = desired_image_count.min(surface_capabilities.max_image_count); + if surface.max_image_count == 0 { + return min_image_count; } - desired_image_count.min(u8::MAX as u32) + min_image_count.min(surface.max_image_count) } #[profiling::function] @@ -179,11 +217,6 @@ impl Swapchain { } } - /// Gets information about this swapchain. - pub fn info(&self) -> SwapchainInfo { - self.info.clone() - } - /// Presents an image which has been previously acquired using /// [`acquire_next_image`][Self::acquire_next_image]. #[profiling::function] @@ -210,7 +243,7 @@ impl Swapchain { let present_info = vk::PresentInfoKHR::default() .wait_semaphores(wait_semaphores) - .swapchains(slice::from_ref(&self.swapchain)) + .swapchains(slice::from_ref(&self.handle)) .image_indices(slice::from_ref(&image.image_idx)); let swapchain_ext = Device::expect_swapchain_ext(&self.device); @@ -221,7 +254,7 @@ impl Swapchain { &present_info, ) { Ok(_) => { - Self::destroy_swapchain(&self.device, &mut self.old_swapchain); + Self::destroy_swapchain(&self.device, &mut self.handle_prev); } Err(err) if err == vk::Result::ERROR_DEVICE_LOST @@ -248,12 +281,11 @@ impl Swapchain { #[profiling::function] fn recreate_swapchain(&mut self) -> Result<(), DriverError> { - Self::destroy_swapchain(&self.device, &mut self.old_swapchain); + Self::destroy_swapchain(&self.device, &mut self.handle_prev); let surface_caps = Surface::capabilities(&self.surface)?; - let desired_image_count = - Self::clamp_desired_image_count(self.info.desired_image_count, surface_caps); + let min_image_count = Self::clamp_min_image_count(self.info.min_image_count, surface_caps); let image_usage = self.supported_surface_usage(surface_caps.supported_usage_flags)?; @@ -290,8 +322,8 @@ impl Swapchain { let swapchain_ext = Device::expect_swapchain_ext(&self.device); let swapchain_create_info = vk::SwapchainCreateInfoKHR::default() - .surface(*self.surface) - .min_image_count(desired_image_count) + .surface(self.surface.handle) + .min_image_count(min_image_count) .image_color_space(self.info.surface.color_space) .image_format(self.info.surface.format) .image_extent(vk::Extent2D { @@ -304,7 +336,7 @@ impl Swapchain { .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE) .present_mode(self.info.present_mode) .clipped(true) - .old_swapchain(self.swapchain) + .old_swapchain(self.handle) .image_array_layers(1); let swapchain = unsafe { swapchain_ext.create_swapchain(&swapchain_create_info, None) } .map_err(|err| { @@ -350,8 +382,8 @@ impl Swapchain { self.info.height = surface_height; self.info.width = surface_width; self.images = images; - self.old_swapchain = self.swapchain; - self.swapchain = swapchain; + self.handle_prev = self.handle; + self.handle = swapchain; self.suboptimal = false; info!( @@ -429,6 +461,15 @@ impl Swapchain { } } +#[doc(hidden)] +impl Deref for Swapchain { + type Target = SwapchainRef; + + fn deref(&self) -> &Self::Target { + unsafe { &*(self as *const Self as *const Self::Target) } + } +} + impl Drop for Swapchain { #[profiling::function] fn drop(&mut self) { @@ -436,8 +477,8 @@ impl Drop for Swapchain { return; } - Self::destroy_swapchain(&self.device, &mut self.old_swapchain); - Self::destroy_swapchain(&self.device, &mut self.swapchain); + Self::destroy_swapchain(&self.device, &mut self.handle_prev); + Self::destroy_swapchain(&self.device, &mut self.handle); } } @@ -487,7 +528,7 @@ impl Deref for SwapchainImage { } /// Information used to create a [`Swapchain`] instance. -#[derive(Builder, Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)] #[builder( build_fn(private, name = "fallible_build", error = "SwapchainInfoBuilderError"), derive(Clone, Debug), @@ -495,15 +536,17 @@ impl Deref for SwapchainImage { )] #[non_exhaustive] pub struct SwapchainInfo { - /// The desired, but not guaranteed, number of images that will be in the created swapchain. - /// - /// More images introduces more display lag, but smoother animation. - #[builder(default = "2")] - pub desired_image_count: u32, - /// The initial height of the surface. pub height: u32, + /// The minimum number of presentable images that the application needs. The implementation will + /// either create the swapchain with at least that many images, or it will fail to create the + /// swapchain. + /// + /// More images introduce more display lag, but smoother animation. + #[builder(default = "2")] + pub min_image_count: u32, + /// The format and color space of the surface. pub surface: vk::SurfaceFormatKHR, @@ -559,11 +602,11 @@ impl SwapchainInfo { #[inline(always)] pub fn new(width: u32, height: u32, surface: vk::SurfaceFormatKHR) -> SwapchainInfo { Self { - width, height, - surface, - desired_image_count: 3, + min_image_count: 2, present_mode: vk::PresentModeKHR::MAILBOX, + surface, + width, } } @@ -571,10 +614,10 @@ impl SwapchainInfo { #[inline(always)] pub fn to_builder(self) -> SwapchainInfoBuilder { SwapchainInfoBuilder { - desired_image_count: Some(self.desired_image_count), height: Some(self.height), - surface: Some(self.surface), + min_image_count: Some(self.min_image_count), present_mode: Some(self.present_mode), + surface: Some(self.surface), width: Some(self.width), } } @@ -624,7 +667,7 @@ mod tests { #[test] pub fn swapchain_info() { let info = Info::new(20, 24, vk::SurfaceFormatKHR::default()); - let builder = info.clone().to_builder().build(); + let builder = info.to_builder().build(); assert_eq!(info, builder); } diff --git a/src/graph/mod.rs b/src/graph/mod.rs index 7d9e8809..4fb82725 100644 --- a/src/graph/mod.rs +++ b/src/graph/mod.rs @@ -442,8 +442,8 @@ impl RenderGraph { } pass.record_cmd_buf(move |device, cmd_buf, bindings| { - let src_image = *bindings[src_node]; - let dst_image = *bindings[dst_node]; + let src_image = bindings[src_node].handle; + let dst_image = bindings[dst_node].handle; unsafe { device.cmd_blit_image( @@ -482,7 +482,7 @@ impl RenderGraph { .record_cmd_buf(move |device, cmd_buf, bindings| unsafe { device.cmd_clear_color_image( cmd_buf, - *bindings[image_node], + bindings[image_node].handle, vk::ImageLayout::TRANSFER_DST_OPTIMAL, &vk::ClearColorValue { float32: color_value.0, @@ -515,7 +515,7 @@ impl RenderGraph { .record_cmd_buf(move |device, cmd_buf, bindings| unsafe { device.cmd_clear_depth_stencil_image( cmd_buf, - *bindings[image_node], + bindings[image_node].handle, vk::ImageLayout::TRANSFER_DST_OPTIMAL, &vk::ClearDepthStencilValue { depth, stencil }, &[image_view_info.into()], @@ -601,8 +601,8 @@ impl RenderGraph { } pass.record_cmd_buf(move |device, cmd_buf, bindings| { - let src_buf = *bindings[src_node]; - let dst_buf = *bindings[dst_node]; + let src_buf = bindings[src_node].handle; + let dst_buf = bindings[dst_node].handle; unsafe { device.cmd_copy_buffer(cmd_buf, src_buf, dst_buf, regions.as_ref()); @@ -688,8 +688,8 @@ impl RenderGraph { } pass.record_cmd_buf(move |device, cmd_buf, bindings| { - let src_buf = *bindings[src_node]; - let dst_image = *bindings[dst_node]; + let src_buf = bindings[src_node].handle; + let dst_image = bindings[dst_node].handle; unsafe { device.cmd_copy_buffer_to_image( @@ -781,8 +781,8 @@ impl RenderGraph { } pass.record_cmd_buf(move |device, cmd_buf, bindings| { - let src_image = *bindings[src_node]; - let dst_image = *bindings[dst_node]; + let src_image = bindings[src_node].handle; + let dst_image = bindings[dst_node].handle; unsafe { device.cmd_copy_image( @@ -877,8 +877,8 @@ impl RenderGraph { } pass.record_cmd_buf(move |device, cmd_buf, bindings| { - let src_image = *bindings[src_node]; - let dst_buf = *bindings[dst_node]; + let src_image = bindings[src_node].handle; + let dst_buf = bindings[dst_node].handle; unsafe { device.cmd_copy_image_to_buffer( @@ -915,7 +915,7 @@ impl RenderGraph { self.begin_pass("fill buffer") .access_node_subrange(buffer_node, AccessType::TransferWrite, region.clone()) .record_cmd_buf(move |device, cmd_buf, bindings| { - let buffer = *bindings[buffer_node]; + let buffer = bindings[buffer_node].handle; unsafe { device.cmd_fill_buffer( @@ -1024,7 +1024,7 @@ impl RenderGraph { self.begin_pass("update buffer") .access_node_subrange(buffer_node, AccessType::TransferWrite, offset..data_end) .record_cmd_buf(move |device, cmd_buf, bindings| { - let buffer = *bindings[buffer_node]; + let buffer = bindings[buffer_node].handle; unsafe { device.cmd_update_buffer(cmd_buf, buffer, offset, data.as_ref()); diff --git a/src/graph/pass_ref.rs b/src/graph/pass_ref.rs index f394c886..5627f2b5 100644 --- a/src/graph/pass_ref.rs +++ b/src/graph/pass_ref.rs @@ -193,7 +193,7 @@ impl Acceleration<'_> { .ty(info.ty) .flags(info.flags) .mode(vk::BuildAccelerationStructureModeKHR::BUILD) - .dst_acceleration_structure(*self.bindings[accel_struct]) + .dst_acceleration_structure(self.bindings[accel_struct].handle) .geometries(&tls.geometries) .scratch_data(scratch_addr)], &[&tls.ranges], @@ -247,7 +247,7 @@ impl Acceleration<'_> { .ty(info.ty) .flags(info.flags) .mode(vk::BuildAccelerationStructureModeKHR::BUILD) - .dst_acceleration_structure(*self.bindings[accel_struct]) + .dst_acceleration_structure(self.bindings[accel_struct].handle) .geometries(&tls.geometries) .scratch_data(scratch_addr)], &[range_base], @@ -314,7 +314,7 @@ impl Acceleration<'_> { .ty(info.build_data.ty) .flags(info.build_data.flags) .mode(vk::BuildAccelerationStructureModeKHR::BUILD) - .dst_acceleration_structure(*self.bindings[info.accel_struct]) + .dst_acceleration_structure(self.bindings[info.accel_struct].handle) .geometries(&tls.geometries[start..end]) .scratch_data(info.scratch_addr.into()), ); @@ -390,7 +390,7 @@ impl Acceleration<'_> { .ty(info.build_data.ty) .flags(info.build_data.flags) .mode(vk::BuildAccelerationStructureModeKHR::BUILD) - .dst_acceleration_structure(*self.bindings[info.accel_struct]) + .dst_acceleration_structure(self.bindings[info.accel_struct].handle) .geometries(&tls.geometries[start..end]) .scratch_data(info.scratch_data.into()), ); @@ -468,8 +468,8 @@ impl Acceleration<'_> { .ty(info.ty) .flags(info.flags) .mode(vk::BuildAccelerationStructureModeKHR::UPDATE) - .dst_acceleration_structure(*self.bindings[dst_accel_struct]) - .src_acceleration_structure(*self.bindings[src_accel_struct]) + .dst_acceleration_structure(self.bindings[dst_accel_struct].handle) + .src_acceleration_structure(self.bindings[src_accel_struct].handle) .geometries(&tls.geometries) .scratch_data(scratch_addr)], &[&tls.ranges], @@ -525,8 +525,8 @@ impl Acceleration<'_> { .ty(info.ty) .flags(info.flags) .mode(vk::BuildAccelerationStructureModeKHR::UPDATE) - .src_acceleration_structure(*self.bindings[src_accel_struct]) - .dst_acceleration_structure(*self.bindings[dst_accel_struct]) + .src_acceleration_structure(self.bindings[src_accel_struct].handle) + .dst_acceleration_structure(self.bindings[dst_accel_struct].handle) .geometries(&tls.geometries) .scratch_data(scratch_addr)], &[range_base], @@ -593,8 +593,8 @@ impl Acceleration<'_> { .ty(info.update_data.ty) .flags(info.update_data.flags) .mode(vk::BuildAccelerationStructureModeKHR::UPDATE) - .dst_acceleration_structure(*self.bindings[info.dst_accel_struct]) - .src_acceleration_structure(*self.bindings[info.src_accel_struct]) + .dst_acceleration_structure(self.bindings[info.dst_accel_struct].handle) + .src_acceleration_structure(self.bindings[info.src_accel_struct].handle) .geometries(&tls.geometries[start..end]) .scratch_data(info.scratch_addr.into()), ); @@ -670,8 +670,8 @@ impl Acceleration<'_> { .ty(info.update_data.ty) .flags(info.update_data.flags) .mode(vk::BuildAccelerationStructureModeKHR::UPDATE) - .src_acceleration_structure(*self.bindings[info.src_accel_struct]) - .dst_acceleration_structure(*self.bindings[info.dst_accel_struct]) + .src_acceleration_structure(self.bindings[info.src_accel_struct].handle) + .dst_acceleration_structure(self.bindings[info.dst_accel_struct].handle) .geometries(&tls.geometries[start..end]) .scratch_data(info.scratch_addr.into()), ); @@ -1036,7 +1036,7 @@ bind!(RayTrace); /// .record_cmd_buf(move |device, cmd_buf, bindings| { /// let my_image = &bindings[my_image_node]; /// -/// assert_ne!(**my_image, vk::Image::null()); +/// assert_ne!(my_image.handle, vk::Image::null()); /// assert_eq!(my_image.info.width, 32); /// }); /// # Ok(()) } @@ -1350,8 +1350,11 @@ impl Compute<'_> { let args_buf = args_buf.into(); unsafe { - self.device - .cmd_dispatch_indirect(self.cmd_buf, *self.bindings[args_buf], args_offset); + self.device.cmd_dispatch_indirect( + self.cmd_buf, + self.bindings[args_buf].handle, + args_offset, + ); } self @@ -1695,7 +1698,7 @@ impl Draw<'_> { unsafe { self.device.cmd_bind_index_buffer( self.cmd_buf, - *self.bindings[buffer], + self.bindings[buffer].handle, offset, index_ty, ); @@ -1766,7 +1769,7 @@ impl Draw<'_> { self.device.cmd_bind_vertex_buffers( self.cmd_buf, 0, - from_ref(&self.bindings[buffer]), + from_ref(&self.bindings[buffer].handle), from_ref(&offset), ); } @@ -1801,7 +1804,7 @@ impl Draw<'_> { for (buffer, offset) in buffer_offsets { let buffer = buffer.into(); - buffers.push(*self.bindings[buffer]); + buffers.push(self.bindings[buffer].handle); offsets.push(offset); } @@ -1959,7 +1962,7 @@ impl Draw<'_> { unsafe { self.device.cmd_draw_indexed_indirect( self.cmd_buf, - *self.bindings[buffer], + self.bindings[buffer].handle, offset, draw_count, stride, @@ -1997,9 +2000,9 @@ impl Draw<'_> { unsafe { self.device.cmd_draw_indexed_indirect_count( self.cmd_buf, - *self.bindings[buffer], + self.bindings[buffer].handle, offset, - *self.bindings[count_buf], + self.bindings[count_buf].handle, count_buf_offset, max_draw_count, stride, @@ -2025,7 +2028,7 @@ impl Draw<'_> { unsafe { self.device.cmd_draw_indirect( self.cmd_buf, - *self.bindings[buffer], + self.bindings[buffer].handle, offset, draw_count, stride, @@ -2054,9 +2057,9 @@ impl Draw<'_> { unsafe { self.device.cmd_draw_indirect_count( self.cmd_buf, - *self.bindings[buffer], + self.bindings[buffer].handle, offset, - *self.bindings[count_buf], + self.bindings[count_buf].handle, count_buf_offset, max_draw_count, stride, diff --git a/src/graph/resolver.rs b/src/graph/resolver.rs index d54952e6..4cd70797 100644 --- a/src/graph/resolver.rs +++ b/src/graph/resolver.rs @@ -633,13 +633,13 @@ impl Resolver { if log_enabled!(Trace) { let (ty, name, vk_pipeline) = match pipeline { ExecutionPipeline::Compute(pipeline) => { - ("compute", pipeline.name.as_ref(), ***pipeline) + ("compute", pipeline.name.as_ref(), pipeline.handle) } ExecutionPipeline::Graphic(pipeline) => { ("graphic", pipeline.name.as_ref(), vk::Pipeline::null()) } ExecutionPipeline::RayTrace(pipeline) => { - ("ray trace", pipeline.name.as_ref(), ***pipeline) + ("ray trace", pipeline.name.as_ref(), pipeline.handle) } }; if let Some(name) = name { @@ -652,14 +652,14 @@ impl Resolver { // We store a shared reference to this pipeline inside the command buffer! let pipeline_bind_point = pipeline.bind_point(); let pipeline = match pipeline { - ExecutionPipeline::Compute(pipeline) => ***pipeline, + ExecutionPipeline::Compute(pipeline) => pipeline.handle, ExecutionPipeline::Graphic(pipeline) => RenderPass::graphic_pipeline( physical_pass.render_pass.as_mut().unwrap(), pipeline, depth_stencil, exec_idx as _, )?, - ExecutionPipeline::RayTrace(pipeline) => ***pipeline, + ExecutionPipeline::RayTrace(pipeline) => pipeline.handle, }; unsafe { @@ -1969,7 +1969,7 @@ impl Resolver { next_access: access, prev_access, resource: BufferResource { - buffer: **buffer, + buffer: buffer.handle, offset: range.start as _, size: (range.end - range.start) as _, }, @@ -2002,7 +2002,7 @@ impl Resolver { next_access: access, prev_access, resource: ImageResource { - image: **image, + image: image.handle, range, }, }) @@ -2248,7 +2248,7 @@ impl Resolver { { if initial_layout { tls.images.push(ImageResourceBarrier { - image: **image, + image: image.handle, next_access: initial_image_layout_access(access), prev_access, range, @@ -3084,7 +3084,7 @@ impl Resolver { tls.buffer_infos.push( vk::DescriptorBufferInfo::default() - .buffer(**buffer) + .buffer(buffer.handle) .offset(buffer_view_info.start) .range(buffer_view_info.end - buffer_view_info.start), ); @@ -3108,7 +3108,7 @@ impl Resolver { tls.accel_struct_infos.push( vk::WriteDescriptorSetAccelerationStructureKHR::default() - .acceleration_structures(std::slice::from_ref(accel_struct)), + .acceleration_structures(std::slice::from_ref(&accel_struct.handle)), ); } else { unimplemented!(); diff --git a/src/lib.rs b/src/lib.rs index edc61484..560c93b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,6 +89,10 @@ let my_pipeline = GraphicPipeline::create(&device, info, [vert, frag])?; # Ok(()) } ``` +_Note:_ dtolnay's read-only public field deref pattern +(_[Link](https://github.com/dtolnay/case-studies/blob/master/readonly-fields/README.md)_) is used to +make the information of each resource easily available and immutable. + ## Pooling Multiple [`pool`] types are available to reduce the impact of frequently creating and dropping @@ -219,8 +223,8 @@ graph // device is &ash::Device // cmd_buf is vk::CommandBuffer // bindings is a magical object you can retrieve the Vulkan resource from - let vk_buffer: vk::Buffer = *bindings[buffer_node]; - let vk_image: vk::Image = *bindings[image_node]; + let vk_buffer: vk::Buffer = bindings[buffer_node].handle; + let vk_image: vk::Image = bindings[image_node].handle; // You are free to READ vk_buffer and WRITE vk_image! }); diff --git a/src/pool/lazy.rs b/src/pool/lazy.rs index 27d66586..95b5c998 100644 --- a/src/pool/lazy.rs +++ b/src/pool/lazy.rs @@ -221,7 +221,9 @@ impl Pool for LazyPool { && (item.info.host_read & info.host_read) == info.host_read && (item.info.host_write & info.host_write) == info.host_write && item.info.alignment >= info.alignment - && item.info.size >= info.size && item.info.usage.contains(info.usage) { + && item.info.size >= info.size + && item.info.usage.contains(info.usage) + { let item = cache.swap_remove(idx); return Ok(Lease::new(cache_ref, item)); From 07648e072b9a4e8ebf4b22077781528117588f04 Mon Sep 17 00:00:00 2001 From: John Wells Date: Sat, 14 Feb 2026 18:05:06 -0500 Subject: [PATCH 06/86] Remove shader code trait --- src/driver/compute.rs | 4 +- src/driver/graphic.rs | 7 +-- src/driver/ray_trace.rs | 4 +- src/driver/shader.rs | 101 ++++++++++------------------------------ src/lib.rs | 5 +- 5 files changed, 31 insertions(+), 90 deletions(-) diff --git a/src/driver/compute.rs b/src/driver/compute.rs index b203eb20..1c821047 100644 --- a/src/driver/compute.rs +++ b/src/driver/compute.rs @@ -4,7 +4,7 @@ use { super::{ DriverError, device::Device, - shader::{DescriptorBindingMap, PipelineDescriptorInfo, Shader, align_spriv}, + shader::{DescriptorBindingMap, PipelineDescriptorInfo, Shader}, }, ash::vk, derive_builder::{Builder, UninitializedFieldError}, @@ -135,7 +135,7 @@ impl ComputePipeline { unsafe { let shader_module = device .create_shader_module( - &vk::ShaderModuleCreateInfo::default().code(align_spriv(&shader.spirv)?), + &vk::ShaderModuleCreateInfo::default().code(shader.spirv.words()), None, ) .map_err(|err| { diff --git a/src/driver/graphic.rs b/src/driver/graphic.rs index 080e51e5..e06f77fe 100644 --- a/src/driver/graphic.rs +++ b/src/driver/graphic.rs @@ -6,9 +6,7 @@ use { device::Device, image::SampleCount, merge_push_constant_ranges, - shader::{ - DescriptorBindingMap, PipelineDescriptorInfo, Shader, SpecializationInfo, align_spriv, - }, + shader::{DescriptorBindingMap, PipelineDescriptorInfo, Shader, SpecializationInfo}, }, ash::vk, derive_builder::{Builder, UninitializedFieldError}, @@ -542,8 +540,7 @@ impl GraphicPipeline { .map(|shader| { let shader_module = device .create_shader_module( - &vk::ShaderModuleCreateInfo::default() - .code(align_spriv(&shader.spirv)?), + &vk::ShaderModuleCreateInfo::default().code(shader.spirv.words()), None, ) .map_err(|err| { diff --git a/src/driver/ray_trace.rs b/src/driver/ray_trace.rs index 0a304449..d136e6bb 100644 --- a/src/driver/ray_trace.rs +++ b/src/driver/ray_trace.rs @@ -6,7 +6,7 @@ use { device::Device, merge_push_constant_ranges, physical_device::RayTraceProperties, - shader::{DescriptorBindingMap, PipelineDescriptorInfo, Shader, align_spriv}, + shader::{DescriptorBindingMap, PipelineDescriptorInfo, Shader}, }, ash::vk, derive_builder::{Builder, UninitializedFieldError}, @@ -219,7 +219,7 @@ impl RayTracePipeline { for (idx, shader) in shaders.iter().enumerate() { let module = device .create_shader_module( - &vk::ShaderModuleCreateInfo::default().code(align_spriv(&shader.spirv)?), + &vk::ShaderModuleCreateInfo::default().code(shader.spirv.words()), None, ) .map_err(|err| { diff --git a/src/driver/shader.rs b/src/driver/shader.rs index e62d7ea3..fe8fcefa 100644 --- a/src/driver/shader.rs +++ b/src/driver/shader.rs @@ -9,6 +9,7 @@ use { spirq::{ ReflectConfig, entry_point::EntryPoint, + parse::SpirvBinary, ty::{DescriptorType, ScalarType, Type, VectorType}, var::Variable, }, @@ -16,7 +17,6 @@ use { collections::{BTreeMap, HashMap}, fmt::{Debug, Formatter}, iter::repeat_n, - mem::size_of_val, ops::Deref, sync::Arc, thread::panicking, @@ -25,18 +25,6 @@ use { pub(crate) type DescriptorBindingMap = HashMap; -pub(crate) fn align_spriv(code: &[u8]) -> Result<&[u32], DriverError> { - let (prefix, code, suffix) = unsafe { code.align_to() }; - - if prefix.len() + suffix.len() == 0 { - Ok(code) - } else { - warn!("Invalid SPIR-V code"); - - Err(DriverError::InvalidData) - } -} - #[profiling::function] fn guess_immutable_sampler(binding_name: &str) -> SamplerInfo { const INVALID_ERR: &str = "Invalid sampler specification"; @@ -737,7 +725,8 @@ pub struct Shader { /// Although SPIR-V code is specified as `u32` values, this field uses `u8` in order to make /// loading from file simpler. You should always have a SPIR-V code length which is a multiple /// of four bytes, or an error will be returned during pipeline creation. - pub spirv: Vec, + #[builder(setter(into))] + pub spirv: SpirvBinary, /// The shader stage this structure applies to. pub stage: vk::ShaderStageFlags, @@ -755,10 +744,8 @@ pub struct Shader { impl Shader { /// Specifies a shader with the given `stage` and shader code values. #[allow(clippy::new_ret_no_self)] - pub fn new(stage: vk::ShaderStageFlags, spirv: impl ShaderCode) -> ShaderBuilder { - ShaderBuilder::default() - .spirv(spirv.into_vec()) - .stage(stage) + pub fn new(stage: vk::ShaderStageFlags, spirv: impl Into) -> ShaderBuilder { + ShaderBuilder::default().spirv(spirv).stage(stage) } /// Creates a new ray trace shader. @@ -766,7 +753,7 @@ impl Shader { /// # Panics /// /// If the shader code is invalid or not a multiple of four bytes in length. - pub fn new_any_hit(spirv: impl ShaderCode) -> ShaderBuilder { + pub fn new_any_hit(spirv: impl Into) -> ShaderBuilder { Self::new(vk::ShaderStageFlags::ANY_HIT_KHR, spirv) } @@ -775,7 +762,7 @@ impl Shader { /// # Panics /// /// If the shader code is invalid or not a multiple of four bytes in length. - pub fn new_callable(spirv: impl ShaderCode) -> ShaderBuilder { + pub fn new_callable(spirv: impl Into) -> ShaderBuilder { Self::new(vk::ShaderStageFlags::CALLABLE_KHR, spirv) } @@ -784,7 +771,7 @@ impl Shader { /// # Panics /// /// If the shader code is invalid or not a multiple of four bytes in length. - pub fn new_closest_hit(spirv: impl ShaderCode) -> ShaderBuilder { + pub fn new_closest_hit(spirv: impl Into) -> ShaderBuilder { Self::new(vk::ShaderStageFlags::CLOSEST_HIT_KHR, spirv) } @@ -793,7 +780,7 @@ impl Shader { /// # Panics /// /// If the shader code is invalid or not a multiple of four bytes in length. - pub fn new_compute(spirv: impl ShaderCode) -> ShaderBuilder { + pub fn new_compute(spirv: impl Into) -> ShaderBuilder { Self::new(vk::ShaderStageFlags::COMPUTE, spirv) } @@ -802,7 +789,7 @@ impl Shader { /// # Panics /// /// If the shader code is invalid or not a multiple of four bytes in length. - pub fn new_fragment(spirv: impl ShaderCode) -> ShaderBuilder { + pub fn new_fragment(spirv: impl Into) -> ShaderBuilder { Self::new(vk::ShaderStageFlags::FRAGMENT, spirv) } @@ -811,7 +798,7 @@ impl Shader { /// # Panics /// /// If the shader code is invalid or not a multiple of four bytes in length. - pub fn new_geometry(spirv: impl ShaderCode) -> ShaderBuilder { + pub fn new_geometry(spirv: impl Into) -> ShaderBuilder { Self::new(vk::ShaderStageFlags::GEOMETRY, spirv) } @@ -820,7 +807,7 @@ impl Shader { /// # Panics /// /// If the shader code is invalid or not a multiple of four bytes in length. - pub fn new_intersection(spirv: impl ShaderCode) -> ShaderBuilder { + pub fn new_intersection(spirv: impl Into) -> ShaderBuilder { Self::new(vk::ShaderStageFlags::INTERSECTION_KHR, spirv) } @@ -829,7 +816,7 @@ impl Shader { /// # Panics /// /// If the shader code is invalid. - pub fn new_mesh(spirv: impl ShaderCode) -> ShaderBuilder { + pub fn new_mesh(spirv: impl Into) -> ShaderBuilder { Self::new(vk::ShaderStageFlags::MESH_EXT, spirv) } @@ -838,7 +825,7 @@ impl Shader { /// # Panics /// /// If the shader code is invalid or not a multiple of four bytes in length. - pub fn new_miss(spirv: impl ShaderCode) -> ShaderBuilder { + pub fn new_miss(spirv: impl Into) -> ShaderBuilder { Self::new(vk::ShaderStageFlags::MISS_KHR, spirv) } @@ -847,7 +834,7 @@ impl Shader { /// # Panics /// /// If the shader code is invalid or not a multiple of four bytes in length. - pub fn new_ray_gen(spirv: impl ShaderCode) -> ShaderBuilder { + pub fn new_ray_gen(spirv: impl Into) -> ShaderBuilder { Self::new(vk::ShaderStageFlags::RAYGEN_KHR, spirv) } @@ -856,7 +843,7 @@ impl Shader { /// # Panics /// /// If the shader code is invalid. - pub fn new_task(spirv: impl ShaderCode) -> ShaderBuilder { + pub fn new_task(spirv: impl Into) -> ShaderBuilder { Self::new(vk::ShaderStageFlags::TASK_EXT, spirv) } @@ -865,7 +852,7 @@ impl Shader { /// # Panics /// /// If the shader code is invalid or not a multiple of four bytes in length. - pub fn new_tesselation_ctrl(spirv: impl ShaderCode) -> ShaderBuilder { + pub fn new_tesselation_ctrl(spirv: impl Into) -> ShaderBuilder { Self::new(vk::ShaderStageFlags::TESSELLATION_CONTROL, spirv) } @@ -874,7 +861,7 @@ impl Shader { /// # Panics /// /// If the shader code is invalid or not a multiple of four bytes in length. - pub fn new_tesselation_eval(spirv: impl ShaderCode) -> ShaderBuilder { + pub fn new_tesselation_eval(spirv: impl Into) -> ShaderBuilder { Self::new(vk::ShaderStageFlags::TESSELLATION_EVALUATION, spirv) } @@ -883,7 +870,7 @@ impl Shader { /// # Panics /// /// If the shader code is invalid or not a multiple of four bytes in length. - pub fn new_vertex(spirv: impl ShaderCode) -> ShaderBuilder { + pub fn new_vertex(spirv: impl Into) -> ShaderBuilder { Self::new(vk::ShaderStageFlags::VERTEX, spirv) } @@ -1159,7 +1146,7 @@ impl Shader { #[profiling::function] fn reflect_entry_point( entry_name: &str, - spirv: &[u8], + spirv: impl Into, specialization_info: Option<&SpecializationInfo>, ) -> Result { let mut config = ReflectConfig::new(); @@ -1420,7 +1407,10 @@ impl ShaderBuilder { self.entry_point = Some( Shader::reflect_entry_point( entry_name, - self.spirv.as_deref().unwrap(), + self.spirv + .as_ref() + .map(|spirv| spirv.words()) + .expect("spirv code must be set at initialization"), self.specialization_info .as_ref() .map(|opt| opt.as_ref()) @@ -1507,49 +1497,6 @@ impl From for ShaderBuilderError { } } -/// Trait for types which can be converted into shader code. -pub trait ShaderCode { - /// Converts the instance into SPIR-V shader code specified as a byte array. - fn into_vec(self) -> Vec; -} - -impl ShaderCode for &[u8] { - fn into_vec(self) -> Vec { - debug_assert_eq!(self.len() % 4, 0, "invalid spir-v code"); - - self.to_vec() - } -} - -impl ShaderCode for &[u32] { - fn into_vec(self) -> Vec { - pub fn into_u8_slice(t: &[T]) -> &[u8] - where - T: Sized, - { - use std::slice::from_raw_parts; - - unsafe { from_raw_parts(t.as_ptr() as *const _, size_of_val(t)) } - } - - into_u8_slice(self).into_vec() - } -} - -impl ShaderCode for Vec { - fn into_vec(self) -> Vec { - debug_assert_eq!(self.len() % 4, 0, "invalid spir-v code"); - - self - } -} - -impl ShaderCode for Vec { - fn into_vec(self) -> Vec { - self.as_slice().into_vec() - } -} - /// Describes specialized constant values. #[derive(Clone, Debug)] pub struct SpecializationInfo { diff --git a/src/lib.rs b/src/lib.rs index 560c93b7..434b712a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -365,10 +365,7 @@ pub mod prelude { RayTraceShaderGroup, RayTraceShaderGroupType, }, render_pass::ResolveMode, - shader::{ - SamplerInfo, SamplerInfoBuilder, Shader, ShaderBuilder, ShaderCode, - SpecializationInfo, - }, + shader::{SamplerInfo, SamplerInfoBuilder, Shader, ShaderBuilder, SpecializationInfo}, surface::Surface, swapchain::{ Swapchain, SwapchainError, SwapchainImage, SwapchainInfo, SwapchainInfoBuilder, From af8b5b38ddc645d82ae22b7705d0f556da50065c Mon Sep 17 00:00:00 2001 From: John Wells Date: Sun, 15 Feb 2026 07:08:21 -0500 Subject: [PATCH 07/86] Clean up Display and prelude --- Cargo.toml | 1 + contrib/rel-mgmt/check | 78 ++++++++++++++----------- contrib/vk-graph-egui/Cargo.toml | 4 +- contrib/vk-graph-egui/src/lib.rs | 13 ++++- contrib/vk-graph-fx/Cargo.toml | 2 +- contrib/vk-graph-fx/src/bitmap_font.rs | 13 ++++- contrib/vk-graph-fx/src/image_loader.rs | 17 +++++- contrib/vk-graph-fx/src/lib.rs | 5 ++ contrib/vk-graph-fx/src/presenter.rs | 9 ++- contrib/vk-graph-fx/src/transition.rs | 7 ++- contrib/vk-graph-hot/Cargo.toml | 5 +- contrib/vk-graph-hot/examples/glsl.rs | 2 +- contrib/vk-graph-hot/examples/hlsl.rs | 2 +- contrib/vk-graph-hot/src/compute.rs | 10 +++- contrib/vk-graph-hot/src/graphic.rs | 10 +++- contrib/vk-graph-hot/src/lib.rs | 10 +++- contrib/vk-graph-hot/src/ray_trace.rs | 10 +++- contrib/vk-graph-hot/src/shader.rs | 8 ++- contrib/vk-graph-imgui/Cargo.toml | 4 +- contrib/vk-graph-imgui/src/lib.rs | 10 +++- contrib/vk-graph-prelude/Cargo.toml | 10 ++++ contrib/vk-graph-prelude/README.md | 3 + contrib/vk-graph-prelude/src/lib.rs | 58 ++++++++++++++++++ contrib/vk-graph-window/Cargo.toml | 2 +- contrib/vk-graph-window/src/frame.rs | 2 +- contrib/vk-graph-window/src/lib.rs | 36 +++++++++--- examples/aliasing.rs | 9 +-- examples/app.rs | 6 +- examples/bindless.rs | 2 +- examples/cpu_readback.rs | 2 +- examples/debugger.rs | 2 +- examples/egui.rs | 2 +- examples/font_bmp.rs | 2 +- examples/fuzzer.rs | 2 +- examples/image_sampler.rs | 2 +- examples/imgui.rs | 2 +- examples/min_max.rs | 2 +- examples/mip_compute.rs | 3 +- examples/mip_graphic.rs | 2 +- examples/msaa.rs | 2 +- examples/multipass.rs | 2 +- examples/multithread.rs | 2 +- examples/ray_omni.rs | 2 +- examples/ray_trace.rs | 2 +- examples/rt_triangle.rs | 2 +- examples/shader-toy/src/main.rs | 11 +++- examples/skeletal-anim/src/main.rs | 14 ++++- examples/subgroup_ops.rs | 2 +- examples/transitions.rs | 2 +- examples/triangle.rs | 2 +- examples/vertex_layout.rs | 2 +- examples/vsm_omni.rs | 2 +- src/display.rs | 60 ++++++++++++++----- src/driver/swapchain.rs | 42 ++++++------- src/lib.rs | 65 +-------------------- 55 files changed, 383 insertions(+), 200 deletions(-) create mode 100644 contrib/vk-graph-prelude/Cargo.toml create mode 100644 contrib/vk-graph-prelude/README.md create mode 100644 contrib/vk-graph-prelude/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 829b8eac..c14d2e41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,7 @@ reqwest = { version = "0.12", features = ["blocking"] } vk-graph-fx = { path = "contrib/vk-graph-fx" } vk-graph-imgui = { path = "contrib/vk-graph-imgui" } vk-graph-egui = { path = "contrib/vk-graph-egui" } +vk-graph-prelude = { path = "contrib/vk-graph-prelude" } vk-graph-window = { path = "contrib/vk-graph-window" } vk-shader-macros = "0.2" tobj = "4.0" diff --git a/contrib/rel-mgmt/check b/contrib/rel-mgmt/check index 6b8d4e19..5b57cc41 100755 --- a/contrib/rel-mgmt/check +++ b/contrib/rel-mgmt/check @@ -20,45 +20,57 @@ cargo fmt --manifest-path contrib/vk-graph-egui/Cargo.toml && diff || fail "Unfo cargo fmt --manifest-path contrib/vk-graph-fx/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-fx)" cargo fmt --manifest-path contrib/vk-graph-hot/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-hot)" cargo fmt --manifest-path contrib/vk-graph-imgui/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-imgui)" +cargo fmt --manifest-path contrib/vk-graph-prelude/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-prelude)" +cargo fmt --manifest-path contrib/vk-graph-window/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-window)" cargo fmt --manifest-path examples/shader-toy/Cargo.toml && diff || fail "Unformatted rust code (shader-toy)" cargo fmt --manifest-path examples/skeletal-anim/Cargo.toml && diff || fail "Unformatted rust code (skeletal-anim)" cargo fmt --manifest-path examples/vr/Cargo.toml && diff || fail "Unformatted rust code (vr)" -# Rust code errors -echo "Checking vk-graph" -cargo check --all-targets -echo "Checking vk-graph (w/ parking_lot)" -cargo check --all-targets --features parking_lot -echo "Checking contrib/vk-graph-egui" -cargo check --manifest-path contrib/vk-graph-egui/Cargo.toml --all-targets --all-features -echo "Checking contrib/vk-graph-fx" -cargo check --manifest-path contrib/vk-graph-fx/Cargo.toml --all-targets --all-features -echo "Checking contrib/vk-graph-hot" -cargo check --manifest-path contrib/vk-graph-hot/Cargo.toml --all-targets --all-features -#echo "Checking contrib/vk-graph-imgui" -#cargo check --manifest-path contrib/vk-graph-imgui/Cargo.toml --all-targets --all-features -echo "Checking contrib/vk-graph-window" -cargo check --manifest-path contrib/vk-graph-window/Cargo.toml --all-targets --all-features -echo "Checking examples/shader-toy" -cargo check --manifest-path examples/shader-toy/Cargo.toml --all-targets --all-features -echo "Checking examples/skeletal-anim" -cargo check --manifest-path examples/skeletal-anim/Cargo.toml --all-targets --all-features -echo "Checking examples/vr" -cargo check --manifest-path examples/vr/Cargo.toml --all-targets --all-features - -# Rust code lints -cargo clippy --all-targets -cargo clippy --all-targets --features parking_lot -cargo clippy --manifest-path contrib/vk-graph-egui/Cargo.toml --all-targets --all-features -cargo clippy --manifest-path contrib/vk-graph-fx/Cargo.toml --all-targets --all-features -cargo clippy --manifest-path contrib/vk-graph-hot/Cargo.toml --all-targets --all-features -#cargo clippy --manifest-path contrib/vk-graph-imgui/Cargo.toml --all-targets --all-features -cargo clippy --manifest-path examples/shader-toy/Cargo.toml --all-targets --all-features -cargo clippy --manifest-path examples/skeletal-anim/Cargo.toml --all-targets --all-features -cargo clippy --manifest-path examples/vr/Cargo.toml --all-targets --all-features +echo "Checking vk-graph" \ + && cargo check --all-targets || fail \ + && cargo clippy --all-targets || fail \ + && echo "Checking vk-graph (w/ parking_lot)" \ + && cargo check --all-targets --features parking_lot || fail \ + && cargo clippy --all-targets --features parking_lot || fail + +echo "Checking contrib/vk-graph-egui" \ + && cargo check --manifest-path contrib/vk-graph-egui/Cargo.toml || fail \ + && cargo clippy --manifest-path contrib/vk-graph-egui/Cargo.toml || fail + +echo "Checking contrib/vk-graph-fx" \ + && cargo check --manifest-path contrib/vk-graph-fx/Cargo.toml || fail \ + && cargo clippy --manifest-path contrib/vk-graph-fx/Cargo.toml || fail + +echo "Checking contrib/vk-graph-hot" \ + && cargo check --manifest-path contrib/vk-graph-hot/Cargo.toml || fail \ + && cargo clippy --manifest-path contrib/vk-graph-hot/Cargo.toml || fail + +# echo "Checking contrib/vk-graph-imgui" \ +# && cargo check --manifest-path contrib/vk-graph-imgui/Cargo.toml || fail \ +# && cargo clippy --manifest-path contrib/vk-graph-imgui/Cargo.toml || fail + +echo "Checking contrib/vk-graph-prelude" \ + && cargo check --manifest-path contrib/vk-graph-prelude/Cargo.toml || fail \ + && cargo clippy --manifest-path contrib/vk-graph-prelude/Cargo.toml || fail + +echo "Checking contrib/vk-graph-window" \ + && cargo check --manifest-path contrib/vk-graph-window/Cargo.toml || fail \ + && cargo clippy --manifest-path contrib/vk-graph-window/Cargo.toml || fail + +echo "Checking examples/shader-toy" \ + && cargo check --manifest-path examples/shader-toy/Cargo.toml || fail \ + && cargo clippy --manifest-path examples/shader-toy/Cargo.toml || fail + +echo "Checking examples/skeletal-anim" \ + && cargo check --manifest-path examples/skeletal-anim/Cargo.toml || fail \ + && cargo clippy --manifest-path examples/skeletal-anim/Cargo.toml || fail + +echo "Checking examples/vr" \ + && cargo check --manifest-path examples/vr/Cargo.toml || fail \ + && cargo clippy --manifest-path examples/vr/Cargo.toml || fail # Rust code tests -cargo test +cargo test || fail # Check for semver breaking changes: if this fails you must update the crate version or fix the code cargo semver-checks check-release --default-features diff --git a/contrib/vk-graph-egui/Cargo.toml b/contrib/vk-graph-egui/Cargo.toml index b2e94c90..f4f1c391 100644 --- a/contrib/vk-graph-egui/Cargo.toml +++ b/contrib/vk-graph-egui/Cargo.toml @@ -7,10 +7,10 @@ license = "MIT OR Apache-2.0" readme = "README.md" [dependencies] -bytemuck = "1.14" +bytemuck = "1.25" # TODO: Waiting for egui to update winit version egui = { git = "https://github.com/emilk/egui.git", rev = "3777b8d2741f298eaa1409dc08062902f7541990" } #{ version = "0.28", features = ["bytemuck"] } egui-winit = { git = "https://github.com/emilk/egui.git", rev = "3777b8d2741f298eaa1409dc08062902f7541990" } #"0.28" -vk-graph = { path = "../.." } +vk-graph-prelude = { path = "../vk-graph-prelude" } vk-graph-fx = { path = "../vk-graph-fx" } vk-shader-macros = "0.2" diff --git a/contrib/vk-graph-egui/src/lib.rs b/contrib/vk-graph-egui/src/lib.rs index 8aae6607..e3fe9a07 100644 --- a/contrib/vk-graph-egui/src/lib.rs +++ b/contrib/vk-graph-egui/src/lib.rs @@ -1,3 +1,8 @@ +//! TODO + +#![warn(missing_docs)] + +/// TODO pub mod prelude { pub use super::{egui, Egui}; } @@ -9,12 +14,15 @@ use { bytemuck::cast_slice, egui_winit::winit::{event::Event, window::Window}, std::{borrow::Cow, collections::HashMap, sync::Arc}, - vk_graph::prelude::*, + vk_graph_prelude::*, vk_shader_macros::include_glsl, }; +/// TODO pub struct Egui { + /// TODO pub ctx: egui::Context, + egui_winit: egui_winit::State, textures: HashMap>>, cache: HashPool, @@ -24,6 +32,7 @@ pub struct Egui { } impl Egui { + /// TODO pub fn new(device: &Arc, display_target: &dyn HasDisplayHandle) -> Self { let ppl = Arc::new( GraphicPipeline::create( @@ -296,6 +305,7 @@ impl Egui { } } + /// TODO pub fn run( &mut self, window: &Window, @@ -328,6 +338,7 @@ impl Egui { self.unbind_and_free(bound_tex, render_graph, &deltas); } + /// TODO pub fn register_texture(&mut self, tex: impl Into) -> egui::TextureId { let id = egui::TextureId::User(self.next_tex_id); self.next_tex_id += 1; diff --git a/contrib/vk-graph-fx/Cargo.toml b/contrib/vk-graph-fx/Cargo.toml index 09946a29..41bb9402 100644 --- a/contrib/vk-graph-fx/Cargo.toml +++ b/contrib/vk-graph-fx/Cargo.toml @@ -29,5 +29,5 @@ parking_lot = "0.12" log = "0.4" anyhow = "1.0" glam = "0.27" -vk-graph = { path = "../.."} +vk-graph-prelude = { path = "../vk-graph-prelude" } vk-shader-macros = "0.2" diff --git a/contrib/vk-graph-fx/src/bitmap_font.rs b/contrib/vk-graph-fx/src/bitmap_font.rs index 9ee23869..d3bc76c2 100644 --- a/contrib/vk-graph-fx/src/bitmap_font.rs +++ b/contrib/vk-graph-fx/src/bitmap_font.rs @@ -4,7 +4,7 @@ use { bytemuck::{cast, cast_slice}, glam::{vec3, Mat4}, std::sync::Arc, - vk_graph::prelude::*, + vk_graph_prelude::*, vk_shader_macros::include_glsl, }; @@ -29,6 +29,7 @@ pub struct BitmapFont { } impl BitmapFont { + /// TODO pub fn new( device: &Arc, font: BMFont, @@ -104,6 +105,7 @@ impl BitmapFont { (position, size) } + /// TODO pub fn print( &mut self, graph: &mut RenderGraph, @@ -117,6 +119,7 @@ impl BitmapFont { } // TODO: Better API, but not sure what, probably builder-something + /// TODO #[allow(clippy::too_many_arguments)] pub fn print_scale( &mut self, @@ -132,6 +135,7 @@ impl BitmapFont { } // TODO: Better API, but not sure what, probably builder-something + /// TODO #[allow(clippy::too_many_arguments)] pub fn print_scale_scissor( &mut self, @@ -229,9 +233,15 @@ impl BitmapFont { } } +/// TODO pub enum BitmapGlyphColor { + /// TODO Outline(Color), + + /// TODO Solid(Color), + + /// TODO SolidOutline(Color, Color), } @@ -290,6 +300,7 @@ impl From<[u8; 4]> for BitmapGlyphColor { pub use bmfont::CharPosition as BitmapGlyph; +/// TODO pub trait Glyph { fn page_height(&self) -> u32; fn page_width(&self) -> u32; diff --git a/contrib/vk-graph-fx/src/image_loader.rs b/contrib/vk-graph-fx/src/image_loader.rs index 7d378236..58caaf7f 100644 --- a/contrib/vk-graph-fx/src/image_loader.rs +++ b/contrib/vk-graph-fx/src/image_loader.rs @@ -1,6 +1,6 @@ use { super::BitmapFont, anyhow::Context, bmfont::BMFont, log::info, std::sync::Arc, - vk_graph::prelude::*, vk_shader_macros::include_glsl, + vk_graph_prelude::*, vk_shader_macros::include_glsl, }; #[cfg(debug_assertions)] @@ -9,9 +9,16 @@ use log::warn; /// Describes the channels and pixel stride of an image format #[derive(Clone, Copy, Debug)] pub enum ImageFormat { + /// TODO R8, + + /// TODO R8G8, + + /// TODO R8G8B8, + + /// TODO R8G8B8A8, } @@ -26,15 +33,19 @@ impl ImageFormat { } } +/// TODO #[derive(Debug)] pub struct ImageLoader { pool: HashPool, _decode_r_rg: Arc, decode_rgb_rgba: Arc, + + /// TODO pub device: Arc, } impl ImageLoader { + /// TODO pub fn new(device: &Arc) -> Result { Ok(Self { pool: HashPool::new(device), @@ -103,6 +114,7 @@ impl ImageLoader { )) } + /// TODO #[allow(clippy::too_many_arguments)] pub fn decode_bitmap( &mut self, @@ -231,6 +243,7 @@ impl ImageLoader { Ok(image) } + /// TODO pub fn decode_linear( &mut self, queue_family_index: usize, @@ -251,6 +264,7 @@ impl ImageLoader { ) } + /// TODO pub fn decode_srgb( &mut self, queue_family_index: usize, @@ -271,6 +285,7 @@ impl ImageLoader { ) } + /// TODO pub fn load_bitmap_font<'a>( &mut self, queue_family_index: usize, diff --git a/contrib/vk-graph-fx/src/lib.rs b/contrib/vk-graph-fx/src/lib.rs index 75f815fc..1af35656 100644 --- a/contrib/vk-graph-fx/src/lib.rs +++ b/contrib/vk-graph-fx/src/lib.rs @@ -1,3 +1,8 @@ +//! TODO + +#![warn(missing_docs)] + +/// TODO pub mod prelude { pub use super::{ BitmapFont, BitmapGlyphColor, ComputePresenter, GraphicPresenter, ImageFormat, ImageLoader, diff --git a/contrib/vk-graph-fx/src/presenter.rs b/contrib/vk-graph-fx/src/presenter.rs index 8b949d24..00b6d4dc 100644 --- a/contrib/vk-graph-fx/src/presenter.rs +++ b/contrib/vk-graph-fx/src/presenter.rs @@ -2,13 +2,15 @@ use { bytemuck::cast_slice, glam::{vec3, Mat4}, std::sync::Arc, - vk_graph::prelude::*, + vk_graph_prelude::*, vk_shader_macros::include_glsl, }; +/// TODO pub struct ComputePresenter([Arc; 2]); impl ComputePresenter { + /// TODO pub fn new(device: &Arc) -> Result { let pipeline1 = Arc::new(ComputePipeline::create( device, @@ -24,6 +26,7 @@ impl ComputePresenter { Ok(Self([pipeline1, pipeline2])) } + /// TODO pub fn present_image( &self, graph: &mut RenderGraph, @@ -46,6 +49,7 @@ impl ComputePresenter { }); } + /// TODO pub fn present_images( &self, graph: &mut RenderGraph, @@ -73,11 +77,13 @@ impl ComputePresenter { } } +/// TODO pub struct GraphicPresenter { pipeline: Arc, } impl GraphicPresenter { + /// TODO pub fn new(device: &Arc) -> Result { Ok(Self { pipeline: Arc::new(GraphicPipeline::create( @@ -93,6 +99,7 @@ impl GraphicPresenter { }) } + /// TODO pub fn present_image( &self, graph: &mut RenderGraph, diff --git a/contrib/vk-graph-fx/src/transition.rs b/contrib/vk-graph-fx/src/transition.rs index b2f20414..bae37647 100644 --- a/contrib/vk-graph-fx/src/transition.rs +++ b/contrib/vk-graph-fx/src/transition.rs @@ -5,10 +5,11 @@ use { log::trace, std::{collections::HashMap, sync::Arc}, - vk_graph::prelude::*, + vk_graph_prelude::*, vk_shader_macros::include_glsl, }; +#[allow(missing_docs)] #[derive(Clone, Copy, Debug)] pub enum Transition { Angular { @@ -381,6 +382,7 @@ impl Transition { } } +/// TODO pub struct TransitionPipeline { cache: HashPool, device: Arc, @@ -388,6 +390,7 @@ pub struct TransitionPipeline { } impl TransitionPipeline { + /// TODO pub fn new(device: &Arc) -> Self { let cache = HashPool::new(device); let device = Arc::clone(device); @@ -400,6 +403,7 @@ impl TransitionPipeline { } } + /// TODO pub fn apply( &mut self, render_graph: &mut RenderGraph, @@ -437,6 +441,7 @@ impl TransitionPipeline { dest_image } + /// TODO pub fn apply_to( &mut self, render_graph: &mut RenderGraph, diff --git a/contrib/vk-graph-hot/Cargo.toml b/contrib/vk-graph-hot/Cargo.toml index d39f6210..64e69e9e 100644 --- a/contrib/vk-graph-hot/Cargo.toml +++ b/contrib/vk-graph-hot/Cargo.toml @@ -16,11 +16,12 @@ anyhow = "1.0" derive_builder = "0.13" log = "0.4" notify = "6.1" -vk-graph = { path = "../.."} +vk-graph = { path = "../..", default-features = false } vk-graph-window = { path = "../vk-graph-window" } shader-prepper = "0.3.0-pre.3" -shaderc = "0.8" +shaderc = "0.10" [dev-dependencies] clap = { version = "4.5", features = ["derive"] } pretty_env_logger = "0.5" +vk-graph-prelude = { path = "../vk-graph-prelude" } diff --git a/contrib/vk-graph-hot/examples/glsl.rs b/contrib/vk-graph-hot/examples/glsl.rs index 41582409..8885ef99 100644 --- a/contrib/vk-graph-hot/examples/glsl.rs +++ b/contrib/vk-graph-hot/examples/glsl.rs @@ -1,8 +1,8 @@ use { clap::Parser, std::path::PathBuf, - vk_graph::prelude::*, vk_graph_hot::prelude::*, + vk_graph_prelude::*, vk_graph_window::{Window, WindowError}, }; diff --git a/contrib/vk-graph-hot/examples/hlsl.rs b/contrib/vk-graph-hot/examples/hlsl.rs index 45100985..f59f24f9 100644 --- a/contrib/vk-graph-hot/examples/hlsl.rs +++ b/contrib/vk-graph-hot/examples/hlsl.rs @@ -1,8 +1,8 @@ use { clap::Parser, std::path::PathBuf, - vk_graph::prelude::*, vk_graph_hot::prelude::*, + vk_graph_prelude::*, vk_graph_window::{Window, WindowError}, }; diff --git a/contrib/vk-graph-hot/src/compute.rs b/contrib/vk-graph-hot/src/compute.rs index 0ac793f9..22d95961 100644 --- a/contrib/vk-graph-hot/src/compute.rs +++ b/contrib/vk-graph-hot/src/compute.rs @@ -1,3 +1,5 @@ +//! TODO + use { super::{compile_shader_and_watch, create_watcher, shader::HotShader}, log::info, @@ -6,9 +8,14 @@ use { atomic::{AtomicBool, Ordering}, Arc, }, - vk_graph::prelude::*, + vk_graph::driver::{ + compute::{ComputePipeline, ComputePipelineInfo}, + device::Device, + DriverError, + }, }; +/// TODO #[derive(Debug)] pub struct HotComputePipeline { device: Arc, @@ -19,6 +26,7 @@ pub struct HotComputePipeline { } impl HotComputePipeline { + /// TODO pub fn create( device: &Arc, info: impl Into, diff --git a/contrib/vk-graph-hot/src/graphic.rs b/contrib/vk-graph-hot/src/graphic.rs index f5d21c9f..3aedec8c 100644 --- a/contrib/vk-graph-hot/src/graphic.rs +++ b/contrib/vk-graph-hot/src/graphic.rs @@ -1,3 +1,5 @@ +//! TODO + use { super::{compile_shader_and_watch, create_watcher, shader::HotShader}, log::info, @@ -6,9 +8,14 @@ use { atomic::{AtomicBool, Ordering}, Arc, }, - vk_graph::prelude::*, + vk_graph::driver::{ + device::Device, + graphic::{GraphicPipeline, GraphicPipelineInfo}, + DriverError, + }, }; +/// TODO #[derive(Debug)] pub struct HotGraphicPipeline { device: Arc, @@ -19,6 +26,7 @@ pub struct HotGraphicPipeline { } impl HotGraphicPipeline { + /// TODO pub fn create( device: &Arc, info: impl Into, diff --git a/contrib/vk-graph-hot/src/lib.rs b/contrib/vk-graph-hot/src/lib.rs index 6339b2d3..9e7df688 100644 --- a/contrib/vk-graph-hot/src/lib.rs +++ b/contrib/vk-graph-hot/src/lib.rs @@ -1,8 +1,13 @@ +//! TODO + +#![warn(missing_docs)] + pub mod compute; pub mod graphic; pub mod ray_trace; pub mod shader; +/// TODO pub mod prelude { pub use super::{ compute::HotComputePipeline, @@ -31,7 +36,10 @@ use { Arc, OnceLock, }, }, - vk_graph::prelude::*, + vk_graph::driver::{ + shader::{Shader, ShaderBuilder}, + DriverError, + }, }; struct CompiledShader { diff --git a/contrib/vk-graph-hot/src/ray_trace.rs b/contrib/vk-graph-hot/src/ray_trace.rs index b136562a..d02c79ef 100644 --- a/contrib/vk-graph-hot/src/ray_trace.rs +++ b/contrib/vk-graph-hot/src/ray_trace.rs @@ -1,3 +1,5 @@ +//! TODO + use { super::{compile_shader_and_watch, create_watcher, shader::HotShader}, log::info, @@ -6,9 +8,14 @@ use { atomic::{AtomicBool, Ordering}, Arc, }, - vk_graph::prelude::*, + vk_graph::driver::{ + device::Device, + ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}, + DriverError, + }, }; +/// TODO #[derive(Debug)] pub struct HotRayTracePipeline { device: Arc, @@ -20,6 +27,7 @@ pub struct HotRayTracePipeline { } impl HotRayTracePipeline { + /// TODO pub fn create( device: &Arc, info: impl Into, diff --git a/contrib/vk-graph-hot/src/shader.rs b/contrib/vk-graph-hot/src/shader.rs index e51ff352..395dfcf3 100644 --- a/contrib/vk-graph-hot/src/shader.rs +++ b/contrib/vk-graph-hot/src/shader.rs @@ -1,3 +1,5 @@ +//! TODO + pub use shaderc::{OptimizationLevel, SourceLanguage, SpirvVersion}; use { @@ -7,7 +9,7 @@ use { notify::{RecommendedWatcher, RecursiveMode, Watcher}, shaderc::{CompileOptions, EnvVersion, ShaderKind, TargetEnv}, std::path::{Path, PathBuf}, - vk_graph::prelude::*, + vk_graph::driver::{ash::vk, shader::SpecializationInfo, DriverError}, }; /// Describes a shader program which runs on some pipeline stage. @@ -257,8 +259,8 @@ impl HotShader { _ => unimplemented!("{:?}", self.stage), }; - let mut additional_opts = CompileOptions::new().ok_or_else(|| { - error!("Unable to initialize compiler options"); + let mut additional_opts = CompileOptions::new().map_err(|err| { + error!("Unable to initialize compiler options: {err:?}"); DriverError::Unsupported })?; diff --git a/contrib/vk-graph-imgui/Cargo.toml b/contrib/vk-graph-imgui/Cargo.toml index 1d52f072..1c578f04 100644 --- a/contrib/vk-graph-imgui/Cargo.toml +++ b/contrib/vk-graph-imgui/Cargo.toml @@ -7,8 +7,8 @@ license = "MIT OR Apache-2.0" readme = "README.md" [dependencies] -bytemuck = "1.24" +bytemuck = "1.25" imgui = "0.12" imgui-winit-support = "0.13" -vk-graph = { path = "../.." } +vk-graph-prelude = { path = "../vk-graph-prelude" } vk-shader-macros = "0.2" diff --git a/contrib/vk-graph-imgui/src/lib.rs b/contrib/vk-graph-imgui/src/lib.rs index 5851fe95..aa63e444 100644 --- a/contrib/vk-graph-imgui/src/lib.rs +++ b/contrib/vk-graph-imgui/src/lib.rs @@ -1,3 +1,8 @@ +//! TODO + +#![warn(missing_docs)] + +/// TODO pub mod prelude { pub use super::{imgui, Condition, ImGui, Ui}; } @@ -12,10 +17,11 @@ use { {HiDpiMode, WinitPlatform}, }, std::{sync::Arc, time::Duration}, - vk_graph::prelude::*, + vk_graph_prelude::*, vk_shader_macros::include_glsl, }; +/// TODO #[derive(Debug)] pub struct ImGui { context: Context, @@ -25,6 +31,7 @@ pub struct ImGui { } impl ImGui { + /// TODO pub fn new(device: &Arc) -> Self { let mut context = Context::create(); let platform = WinitPlatform::new(&mut context); @@ -51,6 +58,7 @@ impl ImGui { } // TODO: This produces an image which is RGBA8 UNORM and has STORAGE set. *We* don't need storage here and should instead ask the user what settings to give the output image..... + /// TODO pub fn draw

( &mut self, dt: f32, diff --git a/contrib/vk-graph-prelude/Cargo.toml b/contrib/vk-graph-prelude/Cargo.toml new file mode 100644 index 00000000..0bd1f4dc --- /dev/null +++ b/contrib/vk-graph-prelude/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "vk-graph-prelude" +version = "0.1.0" +authors = ["John Wells "] +edition = "2024" +license = "MIT OR Apache-2.0" +readme = "README.md" + +[dependencies] +vk-graph = { path = "../..", default-features = false } diff --git a/contrib/vk-graph-prelude/README.md b/contrib/vk-graph-prelude/README.md new file mode 100644 index 00000000..482506c0 --- /dev/null +++ b/contrib/vk-graph-prelude/README.md @@ -0,0 +1,3 @@ +# vk-graph-prelude + +Things which are used in almost every single _vk-graph_ program. diff --git a/contrib/vk-graph-prelude/src/lib.rs b/contrib/vk-graph-prelude/src/lib.rs new file mode 100644 index 00000000..0755f555 --- /dev/null +++ b/contrib/vk-graph-prelude/src/lib.rs @@ -0,0 +1,58 @@ +//! TODO + +#![warn(missing_docs)] + +pub use vk_graph::{ + display::{Display, DisplayError, DisplayInfo, DisplayInfoBuilder, ResolverPool}, + driver::{ + AccessType, CommandBuffer, DriverError, Instance, + accel_struct::{ + AccelerationStructure, AccelerationStructureGeometry, + AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, + AccelerationStructureInfo, AccelerationStructureInfoBuilder, AccelerationStructureSize, + DeviceOrHostAddress, + }, + ash::vk, + buffer::{Buffer, BufferInfo, BufferInfoBuilder, BufferSubresourceRange}, + compute::{ComputePipeline, ComputePipelineInfo, ComputePipelineInfoBuilder}, + device::{Device, DeviceInfo, DeviceInfoBuilder}, + graphic::{ + BlendMode, BlendModeBuilder, DepthStencilMode, DepthStencilModeBuilder, + GraphicPipeline, GraphicPipelineInfo, GraphicPipelineInfoBuilder, StencilMode, + }, + image::{ + Image, ImageInfo, ImageInfoBuilder, ImageViewInfo, ImageViewInfoBuilder, SampleCount, + }, + physical_device::{ + AccelerationStructureProperties, PhysicalDevice, RayQueryFeatures, RayTraceFeatures, + RayTraceProperties, Vulkan10Features, Vulkan10Limits, Vulkan10Properties, + Vulkan11Features, Vulkan11Properties, Vulkan12Features, Vulkan12Properties, + }, + ray_trace::{ + RayTracePipeline, RayTracePipelineInfo, RayTracePipelineInfoBuilder, + RayTraceShaderGroup, RayTraceShaderGroupType, + }, + render_pass::ResolveMode, + shader::{SamplerInfo, SamplerInfoBuilder, Shader, ShaderBuilder, SpecializationInfo}, + surface::Surface, + swapchain::{ + Swapchain, SwapchainError, SwapchainImage, SwapchainInfo, SwapchainInfoBuilder, + }, + }, + graph::{ + Bind, ClearColorValue, RenderGraph, Unbind, + node::{ + AccelerationStructureLeaseNode, AccelerationStructureNode, + AnyAccelerationStructureNode, AnyBufferNode, AnyImageNode, BufferLeaseNode, BufferNode, + ImageLeaseNode, ImageNode, SwapchainImageNode, + }, + pass_ref::{PassRef, PipelinePassRef}, + }, + pool::{ + Lease, Pool, PoolInfo, PoolInfoBuilder, + alias::{Alias, AliasPool}, + fifo::FifoPool, + hash::HashPool, + lazy::LazyPool, + }, +}; diff --git a/contrib/vk-graph-window/Cargo.toml b/contrib/vk-graph-window/Cargo.toml index 1830137d..dad1a98a 100644 --- a/contrib/vk-graph-window/Cargo.toml +++ b/contrib/vk-graph-window/Cargo.toml @@ -9,7 +9,7 @@ readme = "README.md" [dependencies] log = "0.4" profiling = "1.0" -vk-graph = { path = "../.." } +vk-graph = { path = "../..", default-features = false } winit = "0.30" [dev-dependencies] diff --git a/contrib/vk-graph-window/src/frame.rs b/contrib/vk-graph-window/src/frame.rs index 4a68fdbf..8ef4b15c 100644 --- a/contrib/vk-graph-window/src/frame.rs +++ b/contrib/vk-graph-window/src/frame.rs @@ -1,9 +1,9 @@ use { + std::sync::Arc, vk_graph::{ driver::device::Device, graph::{node::SwapchainImageNode, RenderGraph}, }, - std::sync::Arc, winit::{dpi::PhysicalPosition, event::Event, window::Window}, }; diff --git a/contrib/vk-graph-window/src/lib.rs b/contrib/vk-graph-window/src/lib.rs index 346f5ccb..c2704518 100644 --- a/contrib/vk-graph-window/src/lib.rs +++ b/contrib/vk-graph-window/src/lib.rs @@ -1,11 +1,16 @@ +//! TODO + +#![warn(missing_docs)] + mod frame; pub use self::frame::FrameContext; use { - log::{info, trace, warn}, + log::{error, info, trace, warn}, std::{error, fmt, sync::Arc}, vk_graph::{ + display::{Display, DisplayError, DisplayInfoBuilder}, driver::{ ash::vk, device::{Device, DeviceInfo}, @@ -15,7 +20,6 @@ use { }, graph::RenderGraph, pool::hash::HashPool, - Display, DisplayError, DisplayInfoBuilder, }, winit::{ application::ApplicationHandler, @@ -37,22 +41,29 @@ pub enum FullscreenMode { Exclusive, } +/// TODO // #[derive(Debug)] pub struct Window { data: WindowData, + + /// TODO pub device: Arc, + event_loop: EventLoop<()>, } impl Window { + /// TODO pub fn new() -> Result { Self::builder().build() } + /// TODO pub fn builder() -> WindowBuilder { WindowBuilder::default() } + /// TODO pub fn run(self, draw_fn: F) -> Result<(), WindowError> where F: FnMut(FrameContext), @@ -71,7 +82,7 @@ impl Window { &mut self, window: &winit::window::Window, ) -> Result { - let surface = Surface::create(&self.device, &window, &window)?; + let surface = Surface::create(&self.device, window, window)?; let surface_formats = Surface::formats(&surface)?; let surface_format = self .data @@ -103,9 +114,13 @@ impl Window { .iter() .copied() .find(|best| present_modes.contains(best)) - .or_else(|| present_modes.get(0).copied()) + .or_else(|| { + warn!("requested present modes unsupported: {best_modes:?}"); + + present_modes.first().copied() + }) .ok_or_else(|| { - warn!("unsupported present modes: {present_modes:?}"); + error!("display does not support presentation"); DriverError::Unsupported })?, @@ -311,16 +326,16 @@ impl Window { mut f: impl FnMut(FrameContext), ) -> Result { if let Some((width, height)) = self.display_resize.take() { - let mut swapchain_info = self.display.swapchain_info(); + let mut swapchain_info = self.display.swapchain.info; swapchain_info.width = width; swapchain_info.height = height; - self.display.set_swapchain_info(swapchain_info); + self.display.update_swapchain(swapchain_info); } if let Some(swapchain_image) = self.display.acquire_next_image()? { let mut render_graph = RenderGraph::new(); let swapchain_image = render_graph.bind_node(swapchain_image); - let swapchain_info = self.display.swapchain_info(); + let swapchain_info = self.display.swapchain.info; let mut will_exit = false; @@ -396,6 +411,7 @@ impl AsRef> for Window { } } +/// TODO pub struct WindowBuilder { attributes: WindowAttributes, cmd_buf_count: usize, @@ -407,6 +423,7 @@ pub struct WindowBuilder { } impl WindowBuilder { + /// TODO pub fn build(self) -> Result { let event_loop = EventLoop::new()?; let device = Arc::new(Device::create_display(self.device_info, &event_loop)?); @@ -557,9 +574,12 @@ struct WindowData { window_mode_override: Option>, } +/// TODO #[derive(Debug)] pub enum WindowError { + /// TODO Driver(DriverError), + /// TODO EventLoop(EventLoopError), } diff --git a/examples/aliasing.rs b/examples/aliasing.rs index 67abfc9c..719c2296 100644 --- a/examples/aliasing.rs +++ b/examples/aliasing.rs @@ -1,11 +1,4 @@ -use { - clap::Parser, - std::sync::Arc, - vk_graph::{ - pool::alias::{Alias, AliasPool}, - prelude::*, - }, -}; +use {clap::Parser, std::sync::Arc, vk_graph_prelude::*}; /// This example demonstrates resource aliasing. Aliasing is a memory-efficiency optimization that /// may be used anywhere resources are leased and used in a render graph. Aliasing allows complex diff --git a/examples/app.rs b/examples/app.rs index f14e031b..d7acb1e5 100644 --- a/examples/app.rs +++ b/examples/app.rs @@ -5,7 +5,7 @@ use { log::error, std::sync::Arc, vk_graph::{ - Display, DisplayError, DisplayInfo, + display::{Display, DisplayError, DisplayInfo}, driver::{ device::{Device, DeviceInfoBuilder}, surface::Surface, @@ -78,10 +78,10 @@ impl ApplicationHandler for Application { match event { WindowEvent::CloseRequested => event_loop.exit(), WindowEvent::Resized(size) => { - let mut swapchain_info = context.display.swapchain_info(); + let mut swapchain_info = context.display.swapchain.info; swapchain_info.width = size.width; swapchain_info.height = size.height; - context.display.set_swapchain_info(swapchain_info); + context.display.update_swapchain(swapchain_info); } WindowEvent::RedrawRequested => { if let Err(err) = context.draw() { diff --git a/examples/bindless.rs b/examples/bindless.rs index 0f6f283f..911c9acd 100644 --- a/examples/bindless.rs +++ b/examples/bindless.rs @@ -4,7 +4,7 @@ use { bytemuck::{Pod, Zeroable, cast_slice}, clap::Parser, std::sync::Arc, - vk_graph::prelude::*, + vk_graph_prelude::*, vk_graph_window::{WindowBuilder, WindowError}, vk_shader_macros::glsl, winit::dpi::LogicalSize, diff --git a/examples/cpu_readback.rs b/examples/cpu_readback.rs index 8cca4718..89ecf152 100644 --- a/examples/cpu_readback.rs +++ b/examples/cpu_readback.rs @@ -1,7 +1,7 @@ use { clap::Parser, std::{sync::Arc, time::Instant}, - vk_graph::prelude::*, + vk_graph_prelude::*, }; /// Example demonstrating the steps to take when reading the results of buffer or image operations diff --git a/examples/debugger.rs b/examples/debugger.rs index dbc95335..00d16d0c 100644 --- a/examples/debugger.rs +++ b/examples/debugger.rs @@ -24,7 +24,7 @@ To continue, uncomment line 30. */ fn main() -> Result<(), vk_graph_window::WindowError> { - use {log::debug, std::sync::Arc, vk_graph::prelude::*, vk_graph_window::Window}; + use {log::debug, std::sync::Arc, vk_graph_prelude::*, vk_graph_window::Window}; // 👋, 🌎! //pretty_env_logger::init(); diff --git a/examples/egui.rs b/examples/egui.rs index 89900bd7..9f73ecee 100644 --- a/examples/egui.rs +++ b/examples/egui.rs @@ -1,7 +1,7 @@ mod profile_with_puffin; use { - clap::Parser, vk_graph::prelude::*, vk_graph_egui::prelude::*, vk_graph_window::WindowBuilder, + clap::Parser, vk_graph_egui::prelude::*, vk_graph_prelude::*, vk_graph_window::WindowBuilder, winit::dpi::LogicalSize, }; diff --git a/examples/font_bmp.rs b/examples/font_bmp.rs index e52099cb..a81aab2b 100644 --- a/examples/font_bmp.rs +++ b/examples/font_bmp.rs @@ -5,8 +5,8 @@ use { clap::Parser, image::ImageReader, std::{io::Cursor, sync::Arc, time::Instant}, - vk_graph::prelude::*, vk_graph_fx::*, + vk_graph_prelude::*, vk_graph_window::WindowBuilder, vk_shader_macros::glsl, }; diff --git a/examples/fuzzer.rs b/examples/fuzzer.rs index 132464da..8744acd0 100644 --- a/examples/fuzzer.rs +++ b/examples/fuzzer.rs @@ -26,7 +26,7 @@ use { log::debug, rand::{Rng, rng, seq::IndexedRandom}, std::{mem::size_of, sync::Arc}, - vk_graph::prelude::*, + vk_graph_prelude::*, vk_graph_window::{FrameContext, WindowBuilder, WindowError}, vk_shader_macros::glsl, }; diff --git a/examples/image_sampler.rs b/examples/image_sampler.rs index e087810a..1b24ea71 100644 --- a/examples/image_sampler.rs +++ b/examples/image_sampler.rs @@ -7,7 +7,7 @@ use { path::{Path, PathBuf}, sync::Arc, }, - vk_graph::prelude::*, + vk_graph_prelude::*, vk_graph_window::WindowBuilder, vk_shader_macros::glsl, }; diff --git a/examples/imgui.rs b/examples/imgui.rs index 188640b2..c4975ecb 100644 --- a/examples/imgui.rs +++ b/examples/imgui.rs @@ -2,9 +2,9 @@ mod profile_with_puffin; use { clap::Parser, - vk_graph::prelude::*, vk_graph_fx::*, vk_graph_imgui::{Condition, ImGui}, + vk_graph_prelude::*, vk_graph_window::{WindowBuilder, WindowError}, winit::dpi::LogicalSize, }; diff --git a/examples/min_max.rs b/examples/min_max.rs index 5552fafa..434dacf5 100644 --- a/examples/min_max.rs +++ b/examples/min_max.rs @@ -3,7 +3,7 @@ use { clap::Parser, log::warn, std::{mem::size_of, sync::Arc}, - vk_graph::prelude::*, + vk_graph_prelude::*, vk_shader_macros::glsl, }; diff --git a/examples/mip_compute.rs b/examples/mip_compute.rs index e84583c2..e195fc36 100644 --- a/examples/mip_compute.rs +++ b/examples/mip_compute.rs @@ -1,8 +1,7 @@ mod profile_with_puffin; use { - bytemuck::cast_slice, clap::Parser, std::sync::Arc, vk_graph::prelude::*, - vk_shader_macros::glsl, + bytemuck::cast_slice, clap::Parser, std::sync::Arc, vk_graph_prelude::*, vk_shader_macros::glsl, }; /// This program demonstrates a single render pass which uses multiple executions to record a chain diff --git a/examples/mip_graphic.rs b/examples/mip_graphic.rs index 034e812b..841cb17b 100644 --- a/examples/mip_graphic.rs +++ b/examples/mip_graphic.rs @@ -6,7 +6,7 @@ use { core::f32, glam::{Vec4, vec3}, std::sync::Arc, - vk_graph::prelude::*, + vk_graph_prelude::*, vk_graph_window::{WindowBuilder, WindowError}, vk_shader_macros::glsl, }; diff --git a/examples/msaa.rs b/examples/msaa.rs index 8d9dfbf5..c389bfa3 100644 --- a/examples/msaa.rs +++ b/examples/msaa.rs @@ -6,7 +6,7 @@ use { glam::{Mat4, Vec3}, log::warn, std::{mem::size_of, sync::Arc}, - vk_graph::prelude::*, + vk_graph_prelude::*, vk_graph_window::WindowBuilder, vk_shader_macros::glsl, winit::{event::Event, keyboard::KeyCode}, diff --git a/examples/multipass.rs b/examples/multipass.rs index 08967255..07ab013b 100644 --- a/examples/multipass.rs +++ b/examples/multipass.rs @@ -5,7 +5,7 @@ use { clap::Parser, glam::{Mat4, Vec3, Vec4, vec3}, std::sync::Arc, - vk_graph::prelude::*, + vk_graph_prelude::*, vk_graph_window::WindowBuilder, vk_shader_macros::glsl, }; diff --git a/examples/multithread.rs b/examples/multithread.rs index 271154f3..02937ba6 100644 --- a/examples/multithread.rs +++ b/examples/multithread.rs @@ -16,8 +16,8 @@ use { thread::{available_parallelism, sleep, spawn}, time::{Duration, Instant}, }, - vk_graph::prelude::*, vk_graph_fx::BitmapFont, + vk_graph_prelude::*, vk_graph_window::WindowBuilder, }; diff --git a/examples/ray_omni.rs b/examples/ray_omni.rs index 5bb807c6..fbd23eeb 100644 --- a/examples/ray_omni.rs +++ b/examples/ray_omni.rs @@ -14,7 +14,7 @@ use { sync::Arc, }, tobj::{GPU_LOAD_OPTIONS, load_obj}, - vk_graph::prelude::*, + vk_graph_prelude::*, vk_graph_window::WindowBuilder, vk_shader_macros::glsl, }; diff --git a/examples/ray_trace.rs b/examples/ray_trace.rs index 26fb7cb7..72ad091b 100644 --- a/examples/ray_trace.rs +++ b/examples/ray_trace.rs @@ -6,7 +6,7 @@ use { log::warn, std::{io::BufReader, mem::size_of, sync::Arc}, tobj::{GPU_LOAD_OPTIONS, load_mtl_buf, load_obj_buf}, - vk_graph::prelude::*, + vk_graph_prelude::*, vk_graph_window::WindowBuilder, vk_shader_macros::glsl, winit::{event::Event, keyboard::KeyCode}, diff --git a/examples/rt_triangle.rs b/examples/rt_triangle.rs index e7a31a49..5084c33d 100644 --- a/examples/rt_triangle.rs +++ b/examples/rt_triangle.rs @@ -4,7 +4,7 @@ use { bytemuck::{NoUninit, cast_slice}, clap::Parser, std::sync::Arc, - vk_graph::prelude::*, + vk_graph_prelude::*, vk_graph_window::WindowBuilder, vk_shader_macros::glsl, }; diff --git a/examples/shader-toy/src/main.rs b/examples/shader-toy/src/main.rs index ecbe16e2..9856bf39 100644 --- a/examples/shader-toy/src/main.rs +++ b/examples/shader-toy/src/main.rs @@ -38,7 +38,16 @@ use { clap::Parser, pak::{Pak, PakBuf}, std::{sync::Arc, time::Instant}, - vk_graph::prelude::*, + vk_graph::{ + driver::{ + ash::vk, + graphic::{GraphicPipeline, GraphicPipelineInfo}, + image::ImageInfo, + shader::Shader, + }, + graph::RenderGraph, + pool::{lazy::LazyPool, Pool as _}, + }, vk_graph_fx::*, vk_graph_window::WindowBuilder, winit::dpi::PhysicalSize, diff --git a/examples/skeletal-anim/src/main.rs b/examples/skeletal-anim/src/main.rs index 5d872600..f99c4c1f 100644 --- a/examples/skeletal-anim/src/main.rs +++ b/examples/skeletal-anim/src/main.rs @@ -16,7 +16,19 @@ use { sync::Arc, time::{Duration, Instant}, }, - vk_graph::prelude::*, + vk_graph::{ + driver::{ + ash::vk, + buffer::{Buffer, BufferInfo}, + device::Device, + graphic::{DepthStencilMode, GraphicPipeline, GraphicPipelineInfoBuilder}, + image::{Image, ImageInfo}, + shader::Shader, + AccessType, DriverError, + }, + graph::RenderGraph, + pool::{hash::HashPool, lazy::LazyPool, Pool as _}, + }, vk_graph_window::{WindowBuilder, WindowError}, }; diff --git a/examples/subgroup_ops.rs b/examples/subgroup_ops.rs index 86dff946..2ad2bacf 100644 --- a/examples/subgroup_ops.rs +++ b/examples/subgroup_ops.rs @@ -2,7 +2,7 @@ use { bytemuck::cast_slice, clap::Parser, std::{mem::size_of, sync::Arc, time::Instant}, - vk_graph::prelude::*, + vk_graph_prelude::*, vk_shader_macros::glsl, }; diff --git a/examples/transitions.rs b/examples/transitions.rs index fb1b46ea..01b3455d 100644 --- a/examples/transitions.rs +++ b/examples/transitions.rs @@ -5,7 +5,7 @@ use { image::ImageReader, log::info, std::{io::Cursor, time::Instant}, - vk_graph::prelude::LazyPool, + vk_graph::pool::lazy::LazyPool, vk_graph_fx::*, vk_graph_imgui::prelude::*, vk_graph_window::WindowBuilder, diff --git a/examples/triangle.rs b/examples/triangle.rs index 5cc78b73..9d555eb8 100644 --- a/examples/triangle.rs +++ b/examples/triangle.rs @@ -4,7 +4,7 @@ use { bytemuck::cast_slice, clap::Parser, std::sync::Arc, - vk_graph::prelude::*, + vk_graph_prelude::*, vk_graph_window::{WindowBuilder, WindowError}, vk_shader_macros::glsl, }; diff --git a/examples/vertex_layout.rs b/examples/vertex_layout.rs index 6dfa44f5..60510706 100644 --- a/examples/vertex_layout.rs +++ b/examples/vertex_layout.rs @@ -5,7 +5,7 @@ use { clap::Parser, half::f16, std::{mem::size_of, sync::Arc}, - vk_graph::prelude::*, + vk_graph_prelude::*, vk_graph_window::{FrameContext, WindowBuilder}, vk_shader_macros::glsl, }; diff --git a/examples/vsm_omni.rs b/examples/vsm_omni.rs index 9ff69179..0d124dbc 100644 --- a/examples/vsm_omni.rs +++ b/examples/vsm_omni.rs @@ -13,7 +13,7 @@ use { sync::Arc, }, tobj::{GPU_LOAD_OPTIONS, load_obj}, - vk_graph::prelude::*, + vk_graph_prelude::*, vk_graph_window::WindowBuilder, vk_shader_macros::glsl, winit::{dpi::LogicalSize, event::Event, keyboard::KeyCode, window::Fullscreen}, diff --git a/src/display.rs b/src/display.rs index 5f870798..efd87b73 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1,3 +1,5 @@ +//! Resource leasing and pooling types. + use { super::{ driver::{ @@ -6,18 +8,18 @@ use { device::Device, image::Image, image_access_layout, - swapchain::{Swapchain, SwapchainImage, SwapchainInfo}, + swapchain::{Swapchain, SwapchainError, SwapchainImage, SwapchainInfo}, }, graph::{RenderGraph, node::SwapchainImageNode}, pool::Pool, }, - crate::prelude::SwapchainError, ash::vk, derive_builder::{Builder, UninitializedFieldError}, log::{trace, warn}, std::{ error::Error, fmt::{Debug, Formatter}, + ops::Deref, slice, sync::Arc, thread::panicking, @@ -27,13 +29,39 @@ use { }; /// A physical display interface. +#[repr(C)] pub struct Display { exec_idx: usize, execs: Box<[Execution]>, - queue_family_idx: u32, + + /// Information used to create this resource. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub info: DisplayInfo, + + #[cfg(not(doc))] + info: DisplayInfo, + + /// The swapchain which supports this display. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub swapchain: Swapchain, + + #[cfg(not(doc))] swapchain: Swapchain, } +#[doc(hidden)] +#[repr(C)] +pub struct DisplayRef { + exec_idx: usize, + execs: Box<[Execution]>, + pub info: DisplayInfo, + pub swapchain: Swapchain, +} + impl Display { /// Constructs a new `Display` object. pub fn new( @@ -64,7 +92,7 @@ impl Display { Ok(Self { exec_idx: info.command_buffer_count, execs, - queue_family_idx: info.queue_family_index, + info, swapchain, }) } @@ -203,7 +231,7 @@ impl Display { resolver.record_unscheduled_passes(pool, &mut exec.cmd_buf)?; let queue = - exec.cmd_buf.device.queues[self.queue_family_idx as usize][queue_index as usize]; + exec.cmd_buf.device.queues[self.info.queue_family_index as usize][queue_index as usize]; unsafe { exec.cmd_buf @@ -246,7 +274,7 @@ impl Display { self.swapchain.present_image( swapchain_image, slice::from_ref(&exec.swapchain_rendered), - self.queue_family_idx, + self.info.queue_family_index, queue_index, ); @@ -257,16 +285,11 @@ impl Display { Ok(()) } - /// Sets information about the swapchain. + /// Updates the information which controls the swapchain. /// /// Previously acquired swapchain images should be discarded after calling this function. - pub fn set_swapchain_info(&mut self, info: impl Into) { - self.swapchain.set_info(info); - } - - /// Gets information about the swapchain. - pub fn swapchain_info(&self) -> SwapchainInfo { - self.swapchain.info + pub fn update_swapchain(&mut self, info: impl Into) { + self.swapchain.update(info); } } @@ -276,6 +299,15 @@ impl Debug for Display { } } +#[doc(hidden)] +impl Deref for Display { + type Target = DisplayRef; + + fn deref(&self) -> &Self::Target { + unsafe { &*(self as *const Self as *const Self::Target) } + } +} + impl Drop for Display { fn drop(&mut self) { if panicking() { diff --git a/src/driver/swapchain.rs b/src/driver/swapchain.rs index e01c742e..791ba60b 100644 --- a/src/driver/swapchain.rs +++ b/src/driver/swapchain.rs @@ -398,27 +398,6 @@ impl Swapchain { Ok(()) } - /// Sets information about this swapchain. - /// - /// Previously acquired swapchain images should be discarded after calling this function. - pub fn set_info(&mut self, info: impl Into) { - let info: SwapchainInfo = info.into(); - - if self.info != info { - // attempt to reducing flickering when resizing windows on mac - #[cfg(target_os = "macos")] - if let Err(err) = unsafe { self.device.device_wait_idle() } { - warn!("device_wait_idle() failed: {err}"); - } - - self.info = info; - - trace!("info: {:?}", self.info); - - self.suboptimal = true; - } - } - fn supported_surface_usage( &mut self, surface_capabilities: vk::ImageUsageFlags, @@ -459,6 +438,27 @@ impl Swapchain { Ok(res) } + + /// Updates the information which controls this swapchain. + /// + /// Previously acquired swapchain images should be discarded after calling this function. + pub fn update(&mut self, info: impl Into) { + let info: SwapchainInfo = info.into(); + + if self.info != info { + // attempt to reducing flickering when resizing windows on mac + #[cfg(target_os = "macos")] + if let Err(err) = unsafe { self.device.device_wait_idle() } { + warn!("device_wait_idle() failed: {err}"); + } + + self.info = info; + + trace!("info: {:?}", self.info); + + self.suboptimal = true; + } + } } #[doc(hidden)] diff --git a/src/lib.rs b/src/lib.rs index 434b712a..eca67582 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -324,70 +324,7 @@ layout. #![warn(missing_docs)] +pub mod display; pub mod driver; pub mod graph; pub mod pool; - -mod display; - -/// Things which are used in almost every single _vk-graph_ program. -pub mod prelude { - pub use super::{ - display::{Display, DisplayError, DisplayInfo, DisplayInfoBuilder, ResolverPool}, - driver::{ - AccessType, CommandBuffer, DriverError, Instance, - accel_struct::{ - AccelerationStructure, AccelerationStructureGeometry, - AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, - AccelerationStructureInfo, AccelerationStructureInfoBuilder, - AccelerationStructureSize, DeviceOrHostAddress, - }, - ash::vk, - buffer::{Buffer, BufferInfo, BufferInfoBuilder, BufferSubresourceRange}, - compute::{ComputePipeline, ComputePipelineInfo, ComputePipelineInfoBuilder}, - device::{Device, DeviceInfo, DeviceInfoBuilder}, - graphic::{ - BlendMode, BlendModeBuilder, DepthStencilMode, DepthStencilModeBuilder, - GraphicPipeline, GraphicPipelineInfo, GraphicPipelineInfoBuilder, StencilMode, - }, - image::{ - Image, ImageInfo, ImageInfoBuilder, ImageViewInfo, ImageViewInfoBuilder, - SampleCount, - }, - physical_device::{ - AccelerationStructureProperties, PhysicalDevice, RayQueryFeatures, - RayTraceFeatures, RayTraceProperties, Vulkan10Features, Vulkan10Limits, - Vulkan10Properties, Vulkan11Features, Vulkan11Properties, Vulkan12Features, - Vulkan12Properties, - }, - ray_trace::{ - RayTracePipeline, RayTracePipelineInfo, RayTracePipelineInfoBuilder, - RayTraceShaderGroup, RayTraceShaderGroupType, - }, - render_pass::ResolveMode, - shader::{SamplerInfo, SamplerInfoBuilder, Shader, ShaderBuilder, SpecializationInfo}, - surface::Surface, - swapchain::{ - Swapchain, SwapchainError, SwapchainImage, SwapchainInfo, SwapchainInfoBuilder, - }, - }, - graph::{ - Bind, ClearColorValue, RenderGraph, Unbind, - node::{ - AccelerationStructureLeaseNode, AccelerationStructureNode, - AnyAccelerationStructureNode, AnyBufferNode, AnyImageNode, BufferLeaseNode, - BufferNode, ImageLeaseNode, ImageNode, SwapchainImageNode, - }, - pass_ref::{PassRef, PipelinePassRef}, - }, - pool::{ - Lease, Pool, PoolInfo, PoolInfoBuilder, - alias::{Alias, AliasPool}, - fifo::FifoPool, - hash::HashPool, - lazy::LazyPool, - }, - }; -} - -pub use self::display::{Display, DisplayError, DisplayInfo, DisplayInfoBuilder, ResolverPool}; From 58b2783d441a89a3466c0a78afe0ba358b8c16df Mon Sep 17 00:00:00 2001 From: John Wells Date: Sun, 15 Feb 2026 07:32:17 -0500 Subject: [PATCH 08/86] Remove associated function syntax --- examples/ray_trace.rs | 8 ++++---- examples/rt_triangle.rs | 6 +++--- src/driver/accel_struct.rs | 10 ++++----- src/driver/buffer.rs | 31 ++++++++++++++-------------- src/driver/compute.rs | 6 +++--- src/driver/graphic.rs | 6 +++--- src/driver/image.rs | 42 +++++++++++++++++++------------------- src/driver/ray_trace.rs | 20 +++++++++--------- src/driver/surface.rs | 20 +++++++++--------- 9 files changed, 74 insertions(+), 75 deletions(-) diff --git a/examples/ray_trace.rs b/examples/ray_trace.rs index 72ad091b..415e2f48 100644 --- a/examples/ray_trace.rs +++ b/examples/ray_trace.rs @@ -533,17 +533,17 @@ fn main() -> anyhow::Result<()> { .unwrap(); let data = Buffer::mapped_slice_mut(&mut buf); - let rgen_handle = RayTracePipeline::group_handle(&ray_trace_pipeline, 0)?; + let rgen_handle = RayTracePipeline::group_handle(&ray_trace_pipeline, 0); data[0..rgen_handle.len()].copy_from_slice(rgen_handle); - let hit_handle = RayTracePipeline::group_handle(&ray_trace_pipeline, 1)?; + let hit_handle = RayTracePipeline::group_handle(&ray_trace_pipeline, 1); data[sbt_hit_start as usize..sbt_hit_start as usize + hit_handle.len()] .copy_from_slice(hit_handle); - let miss_handle = RayTracePipeline::group_handle(&ray_trace_pipeline, 2)?; + let miss_handle = RayTracePipeline::group_handle(&ray_trace_pipeline, 2); data[sbt_miss_start as usize..sbt_miss_start as usize + miss_handle.len()] .copy_from_slice(miss_handle); - let miss_shadow_handle = RayTracePipeline::group_handle(&ray_trace_pipeline, 3)?; + let miss_shadow_handle = RayTracePipeline::group_handle(&ray_trace_pipeline, 3); let sbt_miss_shadow_start = sbt_miss_start + shader_group_handle_alignment; data[sbt_miss_shadow_start as usize ..sbt_miss_shadow_start as usize + miss_shadow_handle.len()] diff --git a/examples/rt_triangle.rs b/examples/rt_triangle.rs index 5084c33d..66e9fadb 100644 --- a/examples/rt_triangle.rs +++ b/examples/rt_triangle.rs @@ -143,14 +143,14 @@ fn main() -> anyhow::Result<()> { let data = Buffer::mapped_slice_mut(&mut buf); - let rgen_handle = RayTracePipeline::group_handle(&ray_trace_pipeline, 0)?; + let rgen_handle = RayTracePipeline::group_handle(&ray_trace_pipeline, 0); data[0..rgen_handle.len()].copy_from_slice(rgen_handle); - let hit_handle = RayTracePipeline::group_handle(&ray_trace_pipeline, 1)?; + let hit_handle = RayTracePipeline::group_handle(&ray_trace_pipeline, 1); data[sbt_hit_start as usize..sbt_hit_start as usize + hit_handle.len()] .copy_from_slice(hit_handle); - let miss_handle = RayTracePipeline::group_handle(&ray_trace_pipeline, 2)?; + let miss_handle = RayTracePipeline::group_handle(&ray_trace_pipeline, 2); data[sbt_miss_start as usize..sbt_miss_start as usize + miss_handle.len()] .copy_from_slice(miss_handle); diff --git a/src/driver/accel_struct.rs b/src/driver/accel_struct.rs index 96f14164..18fe7fa1 100644 --- a/src/driver/accel_struct.rs +++ b/src/driver/accel_struct.rs @@ -228,9 +228,9 @@ impl AccelerationStructure { /// [_Ash_]: https://crates.io/crates/ash /// [_Erupt_]: https://crates.io/crates/erupt #[profiling::function] - pub fn access(this: &Self, access: AccessType) -> AccessType { + pub fn access(&self, access: AccessType) -> AccessType { #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))] - let mut access_guard = this.access.lock(); + let mut access_guard = self.access.lock(); #[cfg(not(feature = "parking_lot"))] let mut access_guard = access_guard.unwrap(); @@ -261,13 +261,13 @@ impl AccelerationStructure { /// # Ok(()) } /// ``` #[profiling::function] - pub fn device_address(this: &Self) -> vk::DeviceAddress { - let accel_struct_ext = Device::expect_accel_struct_ext(&this.device); + pub fn device_address(&self) -> vk::DeviceAddress { + let accel_struct_ext = Device::expect_accel_struct_ext(&self.device); unsafe { accel_struct_ext.get_acceleration_structure_device_address( &vk::AccelerationStructureDeviceAddressInfoKHR::default() - .acceleration_structure(this.handle), + .acceleration_structure(self.handle), ) } } diff --git a/src/driver/buffer.rs b/src/driver/buffer.rs index 6a2a6cd8..3a5dd49f 100644 --- a/src/driver/buffer.rs +++ b/src/driver/buffer.rs @@ -307,17 +307,17 @@ impl Buffer { /// [_Erupt_]: https://crates.io/crates/erupt #[profiling::function] pub fn access( - this: &Self, + &self, access: AccessType, access_range: impl Into, ) -> impl Iterator + '_ { let mut access_range: BufferSubresourceRange = access_range.into(); if access_range.end == vk::WHOLE_SIZE { - access_range.end = this.info.size; + access_range.end = self.info.size; } - let accesses = this.accesses.lock(); + let accesses = self.accesses.lock(); #[cfg(not(feature = "parking_lot"))] let accesses = accesses.unwrap(); @@ -352,10 +352,9 @@ impl Buffer { /// # Ok(()) } /// ``` #[profiling::function] - pub fn copy_from_slice(this: &mut Self, offset: vk::DeviceSize, slice: impl AsRef<[u8]>) { + pub fn copy_from_slice(&mut self, offset: vk::DeviceSize, slice: impl AsRef<[u8]>) { let slice = slice.as_ref(); - Self::mapped_slice_mut(this)[offset as _..offset as usize + slice.len()] - .copy_from_slice(slice); + self.mapped_slice_mut()[offset as _..offset as usize + slice.len()].copy_from_slice(slice); } /// Returns the device address of this object. @@ -384,16 +383,16 @@ impl Buffer { /// # Ok(()) } /// ``` #[profiling::function] - pub fn device_address(this: &Self) -> vk::DeviceAddress { + pub fn device_address(&self) -> vk::DeviceAddress { debug_assert!( - this.info + self.info .usage .contains(vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS) ); unsafe { - this.device.get_buffer_device_address( - &vk::BufferDeviceAddressInfo::default().buffer(this.handle), + self.device.get_buffer_device_address( + &vk::BufferDeviceAddressInfo::default().buffer(self.handle), ) } } @@ -426,13 +425,13 @@ impl Buffer { /// # Ok(()) } /// ``` #[profiling::function] - pub fn mapped_slice(this: &Self) -> &[u8] { + pub fn mapped_slice(&self) -> &[u8] { debug_assert!( - this.info.host_read, + self.info.host_read, "Buffer is not readable - create using host_read flag" ); - &this.allocation.mapped_slice().unwrap()[0..this.info.size as usize] + &self.allocation.mapped_slice().unwrap()[0..self.info.size as usize] } /// Returns a mapped mutable slice. @@ -464,13 +463,13 @@ impl Buffer { /// # Ok(()) } /// ``` #[profiling::function] - pub fn mapped_slice_mut(this: &mut Self) -> &mut [u8] { + pub fn mapped_slice_mut(&mut self) -> &mut [u8] { debug_assert!( - this.info.host_write, + self.info.host_write, "Buffer is not writable - create using host_write flag" ); - &mut this.allocation.mapped_slice_mut().unwrap()[0..this.info.size as usize] + &mut self.allocation.mapped_slice_mut().unwrap()[0..self.info.size as usize] } } diff --git a/src/driver/compute.rs b/src/driver/compute.rs index 1c821047..f74c6109 100644 --- a/src/driver/compute.rs +++ b/src/driver/compute.rs @@ -208,9 +208,9 @@ impl ComputePipeline { } /// Sets the debugging name assigned to this pipeline. - pub fn with_name(mut this: Self, name: impl Into) -> Self { - this.name = Some(name.into()); - this + pub fn with_name(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); + self } } diff --git a/src/driver/graphic.rs b/src/driver/graphic.rs index e06f77fe..a32fcf8e 100644 --- a/src/driver/graphic.rs +++ b/src/driver/graphic.rs @@ -616,9 +616,9 @@ impl GraphicPipeline { } /// Sets the debugging name assigned to this pipeline. - pub fn with_name(mut this: Self, name: impl Into) -> Self { - this.name = Some(name.into()); - this + pub fn with_name(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); + self } } diff --git a/src/driver/image.rs b/src/driver/image.rs index dab35911..037accbb 100644 --- a/src/driver/image.rs +++ b/src/driver/image.rs @@ -298,7 +298,7 @@ impl Image { /// [_Erupt_]: https://crates.io/crates/erupt #[profiling::function] pub fn access( - this: &Self, + &self, access: AccessType, mut access_range: vk::ImageSubresourceRange, ) -> impl Iterator + '_ { @@ -306,30 +306,30 @@ impl Image { { assert_aspect_mask_supported(access_range.aspect_mask); - assert!(format_aspect_mask(this.info.fmt).contains(access_range.aspect_mask)); + assert!(format_aspect_mask(self.info.fmt).contains(access_range.aspect_mask)); } if access_range.layer_count == vk::REMAINING_ARRAY_LAYERS { - debug_assert!(access_range.base_array_layer < this.info.array_layer_count); + debug_assert!(access_range.base_array_layer < self.info.array_layer_count); - access_range.layer_count = this.info.array_layer_count - access_range.base_array_layer + access_range.layer_count = self.info.array_layer_count - access_range.base_array_layer } debug_assert!( - access_range.base_array_layer + access_range.layer_count <= this.info.array_layer_count + access_range.base_array_layer + access_range.layer_count <= self.info.array_layer_count ); if access_range.level_count == vk::REMAINING_MIP_LEVELS { - debug_assert!(access_range.base_mip_level < this.info.mip_level_count); + debug_assert!(access_range.base_mip_level < self.info.mip_level_count); - access_range.level_count = this.info.mip_level_count - access_range.base_mip_level + access_range.level_count = self.info.mip_level_count - access_range.base_mip_level } debug_assert!( - access_range.base_mip_level + access_range.level_count <= this.info.mip_level_count + access_range.base_mip_level + access_range.level_count <= self.info.mip_level_count ); - let accesses = this.accesses.lock(); + let accesses = self.accesses.lock(); #[cfg(not(feature = "parking_lot"))] let accesses = accesses.unwrap(); @@ -338,10 +338,10 @@ impl Image { } #[profiling::function] - pub(super) fn clone_swapchain(this: &Self) -> Self { + pub(super) fn clone_swapchain(&self) -> Self { // Moves the image view cache from the current instance to the clone! #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))] - let mut image_view_cache = this.image_view_cache.lock(); + let mut image_view_cache = self.image_view_cache.lock(); #[cfg(not(feature = "parking_lot"))] let mut image_view_cache = image_view_cache.unwrap(); @@ -350,28 +350,28 @@ impl Image { // Does NOT copy over the image accesses! // Force previous access to general to wait for presentation - let Self { handle, info, .. } = *this; + let Self { handle, info, .. } = *self; let accesses = ImageAccess::new(info, AccessType::General); let accesses = Mutex::new(accesses); Self { accesses, allocation: None, - device: Arc::clone(&this.device), + device: Arc::clone(&self.device), handle, image_view_cache: Mutex::new(image_view_cache), info, - name: this.name.clone(), + name: self.name.clone(), } } #[profiling::function] - fn drop_allocation(this: &Self, allocation: Allocation) { + fn drop_allocation(&self, allocation: Allocation) { { profiling::scope!("views"); #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))] - let mut image_view_cache = this.image_view_cache.lock(); + let mut image_view_cache = self.image_view_cache.lock(); #[cfg(not(feature = "parking_lot"))] let mut image_view_cache = image_view_cache.unwrap(); @@ -380,14 +380,14 @@ impl Image { } unsafe { - this.device.destroy_image(this.handle, None); + self.device.destroy_image(self.handle, None); } { profiling::scope!("deallocate"); #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))] - let mut allocator = this.device.allocator.lock(); + let mut allocator = self.device.allocator.lock(); #[cfg(not(feature = "parking_lot"))] let mut allocator = allocator.unwrap(); @@ -424,9 +424,9 @@ impl Image { } #[profiling::function] - pub(crate) fn view(this: &Self, info: ImageViewInfo) -> Result { + pub(crate) fn view(&self, info: ImageViewInfo) -> Result { #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))] - let mut image_view_cache = this.image_view_cache.lock(); + let mut image_view_cache = self.image_view_cache.lock(); #[cfg(not(feature = "parking_lot"))] let mut image_view_cache = image_view_cache.unwrap(); @@ -435,7 +435,7 @@ impl Image { Entry::Occupied(entry) => entry.get().image_view, Entry::Vacant(entry) => { entry - .insert(ImageView::create(&this.device, info, this.handle)?) + .insert(ImageView::create(&self.device, info, self.handle)?) .image_view } }) diff --git a/src/driver/ray_trace.rs b/src/driver/ray_trace.rs index d136e6bb..1959276e 100644 --- a/src/driver/ray_trace.rs +++ b/src/driver/ray_trace.rs @@ -352,20 +352,20 @@ impl RayTracePipeline { /// See /// [ray_trace.rs](https://github.com/attackgoat/vk-graph/blob/master/examples/ray_trace.rs) /// for a detail example which constructs a shader binding table buffer using this function. - pub fn group_handle(this: &Self, idx: usize) -> Result<&[u8], DriverError> { + pub fn group_handle(&self, idx: usize) -> &[u8] { let &RayTraceProperties { shader_group_handle_size, .. - } = this + } = self .device .physical_device .ray_trace_properties .as_ref() - .ok_or(DriverError::Unsupported)?; + .unwrap(); let start = idx * shader_group_handle_size as usize; let end = start + shader_group_handle_size as usize; - Ok(&this.shader_group_handles[start..end]) + &self.shader_group_handles[start..end] } /// Query ray trace pipeline shader group shader stack size. @@ -374,24 +374,24 @@ impl RayTracePipeline { /// called from the specified shader group. #[profiling::function] pub fn group_stack_size( - this: &Self, + &self, group: u32, group_shader: vk::ShaderGroupShaderKHR, ) -> vk::DeviceSize { unsafe { // Safely use unchecked because ray_trace_ext is checked during pipeline creation - this.device + self.device .ray_trace_ext .as_ref() .unwrap_unchecked() - .get_ray_tracing_shader_group_stack_size(this.handle, group, group_shader) + .get_ray_tracing_shader_group_stack_size(self.handle, group, group_shader) } } /// Sets the debugging name assigned to this pipeline. - pub fn with_name(mut this: Self, name: impl Into) -> Self { - this.name = Some(name.into()); - this + pub fn with_name(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); + self } } diff --git a/src/driver/surface.rs b/src/driver/surface.rs index 1a040e26..ec44ca4a 100644 --- a/src/driver/surface.rs +++ b/src/driver/surface.rs @@ -45,12 +45,12 @@ pub struct SurfaceRef { impl Surface { /// Query surface capabilities - pub fn capabilities(this: &Self) -> Result { - let surface_ext = Device::expect_surface_ext(&this.device); + pub fn capabilities(&self) -> Result { + let surface_ext = Device::expect_surface_ext(&self.device); unsafe { surface_ext - .get_physical_device_surface_capabilities(*this.device.physical_device, this.handle) + .get_physical_device_surface_capabilities(*self.device.physical_device, self.handle) } .inspect_err(|err| warn!("unable to get surface capabilities: {err}")) .or(Err(DriverError::Unsupported)) @@ -98,13 +98,13 @@ impl Surface { /// Lists the supported surface formats. #[profiling::function] - pub fn formats(this: &Self) -> Result, DriverError> { + pub fn formats(&self) -> Result, DriverError> { unsafe { - this.device + self.device .surface_ext .as_ref() .unwrap() - .get_physical_device_surface_formats(*this.device.physical_device, this.handle) + .get_physical_device_surface_formats(*self.device.physical_device, self.handle) .map_err(|err| { warn!("Unable to get surface formats: {err}"); @@ -137,13 +137,13 @@ impl Surface { } /// Query supported presentation modes. - pub fn present_modes(this: &Self) -> Result, DriverError> { - let surface_ext = Device::expect_surface_ext(&this.device); + pub fn present_modes(&self) -> Result, DriverError> { + let surface_ext = Device::expect_surface_ext(&self.device); unsafe { surface_ext.get_physical_device_surface_present_modes( - *this.device.physical_device, - this.handle, + *self.device.physical_device, + self.handle, ) } .inspect_err(|err| warn!("unable to get surface present modes: {err}")) From d5fce49317203c9926147b7ea9085df438bc745c Mon Sep 17 00:00:00 2001 From: John Wells Date: Tue, 17 Feb 2026 08:58:45 -0500 Subject: [PATCH 09/86] Clean up instance/device init --- contrib/vk-graph-prelude/src/lib.rs | 3 +- examples/min_max.rs | 2 +- examples/vertex_layout.rs | 4 +- examples/vr/src/driver/instance.rs | 40 ++- examples/vr/src/driver/mod.rs | 2 +- examples/vr/src/driver/swapchain.rs | 8 +- examples/vr/src/main.rs | 10 +- src/driver/device.rs | 500 +++++++++++----------------- src/driver/instance.rs | 344 +++++++++++++++---- src/driver/mod.rs | 6 +- src/driver/physical_device.rs | 277 ++++++++++++++- src/driver/ray_trace.rs | 19 +- src/driver/surface.rs | 38 +-- src/driver/swapchain.rs | 2 +- src/graph/pass_ref.rs | 52 ++- 15 files changed, 831 insertions(+), 476 deletions(-) diff --git a/contrib/vk-graph-prelude/src/lib.rs b/contrib/vk-graph-prelude/src/lib.rs index 0755f555..1d025d64 100644 --- a/contrib/vk-graph-prelude/src/lib.rs +++ b/contrib/vk-graph-prelude/src/lib.rs @@ -5,7 +5,7 @@ pub use vk_graph::{ display::{Display, DisplayError, DisplayInfo, DisplayInfoBuilder, ResolverPool}, driver::{ - AccessType, CommandBuffer, DriverError, Instance, + AccessType, CommandBuffer, DriverError, accel_struct::{ AccelerationStructure, AccelerationStructureGeometry, AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, @@ -23,6 +23,7 @@ pub use vk_graph::{ image::{ Image, ImageInfo, ImageInfoBuilder, ImageViewInfo, ImageViewInfoBuilder, SampleCount, }, + instance::{Instance, InstanceInfo, InstanceInfoBuilder}, physical_device::{ AccelerationStructureProperties, PhysicalDevice, RayQueryFeatures, RayTraceFeatures, RayTraceProperties, Vulkan10Features, Vulkan10Limits, Vulkan10Properties, diff --git a/examples/min_max.rs b/examples/min_max.rs index 434dacf5..26484a32 100644 --- a/examples/min_max.rs +++ b/examples/min_max.rs @@ -107,7 +107,7 @@ fn fill_depth_image( // Sometimes required because support is not 100% common: Check min/max reduction support // https://vulkan.gpuinfo.org/listdevicescoverage.php?extension=VK_EXT_sampler_filter_minmax&platform=all - let fmt_props = Device::format_properties(device, fmt); + let fmt_props = device.physical_device.format_properties(fmt); if !fmt_props.optimal_tiling_features.contains( vk::FormatFeatureFlags::SAMPLED_IMAGE | vk::FormatFeatureFlags::SAMPLED_IMAGE_FILTER_LINEAR diff --git a/examples/vertex_layout.rs b/examples/vertex_layout.rs index 60510706..0293a7db 100644 --- a/examples/vertex_layout.rs +++ b/examples/vertex_layout.rs @@ -254,7 +254,9 @@ fn create_pipeline( } fn supports_vertex_buffer(device: &Device, format: vk::Format) -> bool { - Device::format_properties(device, format) + device + .physical_device + .format_properties(format) .buffer_features .contains(vk::FormatFeatureFlags::VERTEX_BUFFER) } diff --git a/examples/vr/src/driver/instance.rs b/examples/vr/src/driver/instance.rs index 43d22a6a..c4a7eaa3 100644 --- a/examples/vr/src/driver/instance.rs +++ b/examples/vr/src/driver/instance.rs @@ -14,18 +14,18 @@ use { vk::{self, Handle as _}, }, device::Device, - physical_device::PhysicalDevice, + instance::Instance as VkInstance, }, }; -pub struct Instance { +pub struct XrInstance { device: Arc, event_buf: xr::EventDataBuffer, instance: xr::Instance, system: xr::SystemId, } -impl Instance { +impl XrInstance { const VK_TARGET_VERSION: u32 = vk::make_api_version( 0, Self::XR_TARGET_VERSION.major() as _, @@ -121,7 +121,7 @@ impl Instance { let create_info = vk::InstanceCreateInfo::default().application_info(&app_info); unsafe { - let vk_entry = ash::Entry::load().map_err(|err| { + let entry = ash::Entry::load().map_err(|err| { error!("Vulkan entry point: {err}"); InstanceCreateError::VulkanUnsupported @@ -132,7 +132,7 @@ impl Instance { unsafe extern "system" fn(T, *const i8) -> Option; type AshFn = Fn; type OpenXrFn = Fn<*const c_void>; - transmute::(vk_entry.static_fn().get_instance_proc_addr) + transmute::(entry.static_fn().get_instance_proc_addr) }; let vk_instance = { @@ -155,14 +155,14 @@ impl Instance { })?; let vk_instance = vk::Instance::from_raw(vk_instance as _); - vk_graph::driver::Instance::load(vk_entry, vk_instance).map_err(|err| { + VkInstance::from_entry(entry, vk_instance).map_err(|err| { error!("Vulkan instance load: {err}"); InstanceCreateError::VulkanUnsupported })? }; - let vk_physical_device = vk::PhysicalDevice::from_raw( + let physical_device = vk::PhysicalDevice::from_raw( xr_instance .vulkan_graphics_device(system, vk_instance.handle().as_raw() as _) .map_err(|err| { @@ -171,20 +171,20 @@ impl Instance { InstanceCreateError::OpenXRUnsupported })? as _, ); - let vk_physical_device = PhysicalDevice::new(&vk_instance, vk_physical_device) + let physical_device = VkInstance::physical_device(&vk_instance, physical_device) .map_err(|err| { error!("Vulkan physical device: {err}"); InstanceCreateError::VulkanUnsupported })?; - let device = - Device::create_ash_device(&vk_instance, &vk_physical_device, true, |create_info| { + let ash_device = physical_device + .create_ash_device(true, |create_info| { let device = xr_instance .create_vulkan_device( system, get_instance_proc_addr, - vk_physical_device.as_raw() as _, + physical_device.handle.as_raw() as _, &create_info as *const _ as *const _, ) .map_err(|err| { @@ -202,13 +202,13 @@ impl Instance { InstanceCreateError::VulkanUnsupported })?; - let device = Arc::new( - Device::load(vk_instance, vk_physical_device, device, true).map_err(|err| { + let device = Arc::new(Device::load(physical_device, ash_device, true).map_err( + |err| { error!("Vulkan device: {err}"); InstanceCreateError::VulkanUnsupported - })?, - ); + }, + )?); let event_buf = xr::EventDataBuffer::new(); Ok(Self { @@ -230,14 +230,12 @@ impl Instance { xr::FrameWaiter, xr::FrameStream, )> { - let vk_instance = Device::instance(&this.device); - unsafe { this.instance.create_session::( this.system, &xr::vulkan::SessionCreateInfo { - instance: vk_instance.handle().as_raw() as _, - physical_device: this.device.physical_device.as_raw() as _, + instance: this.device.physical_device.instance.handle().as_raw() as _, + physical_device: this.device.physical_device.handle.as_raw() as _, device: this.device.handle().as_raw() as _, queue_family_index, queue_index, @@ -272,13 +270,13 @@ impl Instance { } } -impl Debug for Instance { +impl Debug for XrInstance { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str("Instance") } } -impl Deref for Instance { +impl Deref for XrInstance { type Target = xr::Instance; fn deref(&self) -> &Self::Target { diff --git a/examples/vr/src/driver/mod.rs b/examples/vr/src/driver/mod.rs index fc48fb76..0b0b7230 100644 --- a/examples/vr/src/driver/mod.rs +++ b/examples/vr/src/driver/mod.rs @@ -1,4 +1,4 @@ mod instance; mod swapchain; -pub use self::{instance::Instance, swapchain::Swapchain}; +pub use self::{instance::XrInstance, swapchain::Swapchain}; diff --git a/examples/vr/src/driver/swapchain.rs b/examples/vr/src/driver/swapchain.rs index 0e9db25c..58424fe8 100644 --- a/examples/vr/src/driver/swapchain.rs +++ b/examples/vr/src/driver/swapchain.rs @@ -1,5 +1,5 @@ use { - super::Instance, + super::XrInstance, openxr as xr, std::{ ops::{Deref, DerefMut}, @@ -18,10 +18,10 @@ pub struct Swapchain { } impl Swapchain { - pub fn new(instance: &Instance, session: &xr::Session) -> Self { - let device = Instance::device(instance); + pub fn new(instance: &XrInstance, session: &xr::Session) -> Self { + let device = XrInstance::device(instance); - let views = Instance::enumerate_view_configuration_views( + let views = XrInstance::enumerate_view_configuration_views( instance, xr::ViewConfigurationType::PRIMARY_STEREO, ) diff --git a/examples/vr/src/main.rs b/examples/vr/src/main.rs index e408a4f1..17133b7f 100644 --- a/examples/vr/src/main.rs +++ b/examples/vr/src/main.rs @@ -1,7 +1,7 @@ mod driver; use { - self::driver::{Instance, Swapchain}, + self::driver::{Swapchain, XrInstance}, bytemuck::{bytes_of, cast_slice, Pod, Zeroable}, glam::{vec3, vec4, Mat3, Mat4, Quat, Vec2, Vec3}, log::{debug, error, trace}, @@ -53,15 +53,15 @@ fn main() -> anyhow::Result<()> { trace!("Starting"); // Initialize OpenXR and Vulkan - let mut instance = Instance::new().unwrap(); - let device = Instance::device(&instance); + let mut instance = XrInstance::new().unwrap(); + let device = XrInstance::device(&instance); let queue_family_index = device_queue_family_index(device, vk::QueueFlags::GRAPHICS | vk::QueueFlags::TRANSFER) .unwrap(); // Start a VR session let (session, mut frame_wait, mut frame_stream) = - Instance::create_session(&instance, queue_family_index, 0).unwrap(); + XrInstance::create_session(&instance, queue_family_index, 0).unwrap(); let action_set = instance .create_action_set("input", "input pose information", 0) .unwrap(); @@ -217,7 +217,7 @@ fn main() -> anyhow::Result<()> { } } - while let Some(event) = Instance::poll_event(&mut instance).unwrap() { + while let Some(event) = XrInstance::poll_event(&mut instance).unwrap() { use xr::Event::*; match event { SessionStateChanged(e) => { diff --git a/src/driver/device.rs b/src/driver/device.rs index 24a77a5d..e3ba82ff 100644 --- a/src/driver/device.rs +++ b/src/driver/device.rs @@ -1,21 +1,22 @@ //! Logical device resource types use { - super::{DriverError, Instance, physical_device::PhysicalDevice}, + super::{ + DriverError, + instance::{Instance, InstanceInfoBuilder}, + physical_device::PhysicalDevice, + }, ash::{ext, khr, vk}, - ash_window::enumerate_required_extensions, derive_builder::{Builder, UninitializedFieldError}, gpu_allocator::{ AllocatorDebugSettings, vulkan::{Allocator, AllocatorCreateDesc}, }, log::{error, info, trace, warn}, - raw_window_handle::HasDisplayHandle, + raw_window_handle::{HasDisplayHandle, RawDisplayHandle}, std::{ - cmp::Ordering, ffi::CStr, fmt::{Debug, Formatter}, - iter::{empty, repeat_n}, mem::{ManuallyDrop, forget}, ops::Deref, thread::panicking, @@ -29,174 +30,117 @@ use parking_lot::Mutex; #[cfg(not(feature = "parking_lot"))] use std::sync::Mutex; -/// Function type for selection of physical devices. -pub type SelectPhysicalDeviceFn = dyn FnOnce(&[PhysicalDevice]) -> usize; +// Copied from ash-window to change the signature +fn enumerate_required_extensions( + display_handle: RawDisplayHandle, +) -> Result<&'static [&'static CStr], DriverError> { + let extensions = match display_handle { + RawDisplayHandle::Windows(_) => { + const WINDOWS_EXTS: [&CStr; 2] = [khr::surface::NAME, khr::win32_surface::NAME]; + &WINDOWS_EXTS + } + RawDisplayHandle::Wayland(_) => { + const WAYLAND_EXTS: [&CStr; 2] = [khr::surface::NAME, khr::wayland_surface::NAME]; + &WAYLAND_EXTS + } + RawDisplayHandle::Xlib(_) => { + const XLIB_EXTS: [&CStr; 2] = [khr::surface::NAME, khr::xlib_surface::NAME]; + &XLIB_EXTS + } + RawDisplayHandle::Xcb(_) => { + const XCB_EXTS: [&CStr; 2] = [khr::surface::NAME, khr::xcb_surface::NAME]; + &XCB_EXTS + } + RawDisplayHandle::Android(_) => { + const ANDROID_EXTS: [&CStr; 2] = [khr::surface::NAME, khr::android_surface::NAME]; + &ANDROID_EXTS + } + RawDisplayHandle::AppKit(_) | RawDisplayHandle::UiKit(_) => { + const METAL_EXTS: [&CStr; 2] = [khr::surface::NAME, ext::metal_surface::NAME]; + &METAL_EXTS + } + _ => return Err(DriverError::Unsupported), + }; + + Ok(extensions) +} + +fn select_physical_device( + instance: &Instance, + mut index: usize, +) -> Result { + let mut physical_devices = Instance::physical_devices(instance)?; + if physical_devices.is_empty() { + warn!("no physical devices found"); + + return Err(DriverError::Unsupported); + } + + if index >= physical_devices.len() { + index = 0; + } + + let physical_device = physical_devices.remove(index); + + Ok(physical_device) +} /// Opaque handle to a device object. +#[repr(C)] pub struct Device { accel_struct_ext: Option, pub(super) allocator: ManuallyDrop>, device: ash::Device, - - /// Vulkan instance pointer, which includes useful functions. - instance: Instance, - pipeline_cache: vk::PipelineCache, /// The physical device, which contains useful data about features, properties, and limits. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] pub physical_device: PhysicalDevice, + #[cfg(not(doc))] + physical_device: PhysicalDevice, + /// The physical execution queues which all work will be submitted to. pub(crate) queues: Vec>, - pub(crate) ray_trace_ext: Option, + ray_trace_ext: Option, + surface_ext: Option, + swapchain_ext: Option, +} - pub(super) surface_ext: Option, - pub(super) swapchain_ext: Option, +#[doc(hidden)] +#[repr(C)] +pub struct DeviceRef { + accel_struct_ext: Option, + pub(super) allocator: ManuallyDrop>, + device: ash::Device, + pipeline_cache: vk::PipelineCache, + pub physical_device: PhysicalDevice, + pub(crate) queues: Vec>, + ray_trace_ext: Option, + surface_ext: Option, + swapchain_ext: Option, } impl Device { - /// Prepares device creation information and calls the provided callback to allow an application - /// to control the device creation process. - /// - /// # Safety - /// - /// This is only required for interoperting with other libraries and comes with all the caveats - /// of using `ash` builder types, which are inherently dangerous. Use with extreme caution. - #[profiling::function] - pub unsafe fn create_ash_device( - instance: &Instance, - physical_device: &PhysicalDevice, - display_window: bool, - create_fn: F, - ) -> ash::prelude::VkResult - where - F: FnOnce(vk::DeviceCreateInfo) -> ash::prelude::VkResult, - { - let mut enabled_ext_names = Vec::with_capacity(6); - - if display_window { - enabled_ext_names.push(khr::swapchain::NAME.as_ptr()); - } - - if physical_device.accel_struct_properties.is_some() { - enabled_ext_names.push(khr::acceleration_structure::NAME.as_ptr()); - enabled_ext_names.push(khr::deferred_host_operations::NAME.as_ptr()); - } - - if physical_device.ray_query_features.ray_query { - enabled_ext_names.push(khr::ray_query::NAME.as_ptr()); - } - - if physical_device.ray_trace_features.ray_tracing_pipeline { - enabled_ext_names.push(khr::ray_tracing_pipeline::NAME.as_ptr()); - } - - if physical_device.index_type_uint8_features.index_type_uint8 { - enabled_ext_names.push(ext::index_type_uint8::NAME.as_ptr()); - } - - let priorities = repeat_n( - 1.0, - physical_device - .queue_families - .iter() - .map(|family| family.queue_count) - .max() - .unwrap_or_default() as _, - ) - .collect::>(); - - let queue_infos = physical_device - .queue_families - .iter() - .enumerate() - .map(|(idx, family)| { - let mut queue_info = vk::DeviceQueueCreateInfo::default() - .queue_family_index(idx as _) - .queue_priorities(&priorities[0..family.queue_count as usize]); - queue_info.queue_count = family.queue_count; - - queue_info - }) - .collect::>(); - - let ash::InstanceFnV1_1 { - get_physical_device_features2, - .. - } = instance.fp_v1_1(); - let mut features_v1_1 = vk::PhysicalDeviceVulkan11Features::default(); - let mut features_v1_2 = vk::PhysicalDeviceVulkan12Features::default(); - let mut acceleration_structure_features = - vk::PhysicalDeviceAccelerationStructureFeaturesKHR::default(); - let mut index_type_uint8_features = vk::PhysicalDeviceIndexTypeUint8FeaturesEXT::default(); - let mut ray_query_features = vk::PhysicalDeviceRayQueryFeaturesKHR::default(); - let mut ray_trace_features = vk::PhysicalDeviceRayTracingPipelineFeaturesKHR::default(); - let mut features = vk::PhysicalDeviceFeatures2::default() - .push_next(&mut features_v1_1) - .push_next(&mut features_v1_2); - - if physical_device.accel_struct_properties.is_some() { - features = features.push_next(&mut acceleration_structure_features); - } - - if physical_device.ray_query_features.ray_query { - features = features.push_next(&mut ray_query_features); - } - - if physical_device.ray_trace_features.ray_tracing_pipeline { - features = features.push_next(&mut ray_trace_features); - } - - if physical_device.index_type_uint8_features.index_type_uint8 { - features = features.push_next(&mut index_type_uint8_features); - } - - unsafe { get_physical_device_features2(**physical_device, &mut features) }; - - let device_create_info = vk::DeviceCreateInfo::default() - .queue_create_infos(&queue_infos) - .enabled_extension_names(&enabled_ext_names) - .push_next(&mut features); - - create_fn(device_create_info) - } - + /// Constructs a new device using the given physical device. #[profiling::function] - fn create( - instance: Instance, - select_physical_device: Box, + pub fn create( + physical_device: PhysicalDevice, display_window: bool, ) -> Result { - let mut physical_devices = Instance::physical_devices(&instance)?; - - if physical_devices.is_empty() { - error!("no supported devices found"); - - return Err(DriverError::Unsupported); - } - - let mut phyical_device_idx = select_physical_device(&physical_devices); - - if phyical_device_idx >= physical_devices.len() { - warn!("invalid device selected"); - - phyical_device_idx = 0; - } - - let physical_device = physical_devices.remove(phyical_device_idx); - let device = unsafe { - Self::create_ash_device( - &instance, - &physical_device, - display_window, - |device_create_info| { - instance.create_device(*physical_device, &device_create_info, None) - }, - ) + physical_device.create_ash_device(display_window, |device_create_info| { + physical_device.instance.create_device( + physical_device.handle, + &device_create_info, + None, + ) + }) } .map_err(|err| { error!("unable to create device: {err}"); @@ -206,7 +150,7 @@ impl Device { info!("created {}", physical_device.properties_v1_0.device_name); - Self::load(instance, physical_device, device, display_window) + Self::load(physical_device, device, display_window) } /// Constructs a new device using the given configuration. @@ -214,39 +158,43 @@ impl Device { pub fn create_headless(info: impl Into) -> Result { let DeviceInfo { debug, - select_physical_device, + physical_device_index, } = info.into(); - let instance = Instance::create(debug, empty())?; + let instance_info = InstanceInfoBuilder::default().debug(debug); + let instance = Instance::load(instance_info)?; + let physical_device = select_physical_device(&instance, physical_device_index)?; - Self::create(instance, select_physical_device, false) + Self::create(physical_device, false) } /// Constructs a new device using the given configuration. #[profiling::function] pub fn create_display( info: impl Into, - display_handle: &impl HasDisplayHandle, + display_handle: impl HasDisplayHandle, ) -> Result { let DeviceInfo { debug, - select_physical_device, + physical_device_index, } = info.into(); let display_handle = display_handle.display_handle().map_err(|err| { - warn!("{err}"); + warn!("unable to get display handle: {err}"); DriverError::Unsupported })?; - let required_extensions = enumerate_required_extensions(display_handle.as_raw()) - .map_err(|err| { - warn!("{err}"); + let extension_names = + enumerate_required_extensions(display_handle.as_raw()).map_err(|err| { + warn!("unable to enumerate window extensions: {err}"); DriverError::Unsupported - })? - .iter() - .map(|ext| unsafe { CStr::from_ptr(*ext as *const _) }); - let instance = Instance::create(debug, required_extensions)?; - - Self::create(instance, select_physical_device, true) + })?; + let instance_info = InstanceInfoBuilder::default() + .debug(debug) + .extension_names(extension_names); + let instance = Instance::load(instance_info)?; + let physical_device = select_physical_device(&instance, physical_device_index)?; + + Self::create(physical_device, true) } pub(crate) fn create_fence(this: &Self, signaled: bool) -> Result { @@ -289,6 +237,18 @@ impl Device { .expect("VK_KHR_acceleration_structure") } + /// Helper for times when you already know that the device supports the ray tracing pipeline + /// extension. + /// + /// # Panics + /// + /// Panics if [Self.physical_device.ray_trace_properties] is `None`. + pub(crate) fn expect_ray_trace_ext(this: &Self) -> &khr::ray_tracing_pipeline::Device { + this.ray_trace_ext + .as_ref() + .expect("VK_KHR_ray_tracing_pipeline") + } + /// Helper for times when you already know that the instance supports the surface extension. /// /// # Panics @@ -310,21 +270,20 @@ impl Device { /// Loads and existing `ash` Vulkan device that may have been created by other means. #[profiling::function] pub fn load( - instance: Instance, physical_device: PhysicalDevice, device: ash::Device, display_window: bool, ) -> Result { - let debug = Instance::is_debug(&instance); + let debug = physical_device.instance.info.debug; let mut debug_settings = AllocatorDebugSettings::default(); debug_settings.log_leaks_on_shutdown = debug; debug_settings.log_memory_information = debug; debug_settings.log_allocations = debug; let allocator = Allocator::new(&AllocatorCreateDesc { - instance: (*instance).clone(), + instance: (*physical_device.instance).clone(), device: device.clone(), - physical_device: *physical_device, + physical_device: physical_device.handle, debug_settings, buffer_device_address: true, allocation_sizes: Default::default(), @@ -348,17 +307,19 @@ impl Device { queues.push(queue_family); } - let surface_ext = display_window - .then(|| khr::surface::Instance::new(Instance::entry(&instance), &instance)); - let swapchain_ext = display_window.then(|| khr::swapchain::Device::new(&instance, &device)); + let surface_ext = display_window.then(|| { + khr::surface::Instance::new(&physical_device.instance.entry, &physical_device.instance) + }); + let swapchain_ext = + display_window.then(|| khr::swapchain::Device::new(&physical_device.instance, &device)); let accel_struct_ext = physical_device .accel_struct_properties .is_some() - .then(|| khr::acceleration_structure::Device::new(&instance, &device)); + .then(|| khr::acceleration_structure::Device::new(&physical_device.instance, &device)); let ray_trace_ext = physical_device .ray_trace_features .ray_tracing_pipeline - .then(|| khr::ray_tracing_pipeline::Device::new(&instance, &device)); + .then(|| khr::ray_tracing_pipeline::Device::new(&physical_device.instance, &device)); let pipeline_cache = unsafe { device.create_pipeline_cache(&vk::PipelineCacheCreateInfo::default(), None) } @@ -372,7 +333,6 @@ impl Device { accel_struct_ext, allocator: ManuallyDrop::new(Mutex::new(allocator)), device, - instance, pipeline_cache, physical_device, queues, @@ -382,15 +342,6 @@ impl Device { }) } - /// Lists the physical device's format capabilities. - #[profiling::function] - pub fn format_properties(this: &Self, format: vk::Format) -> vk::FormatProperties { - unsafe { - this.instance - .get_physical_device_format_properties(*this.physical_device, format) - } - } - /// Lists the physical device's image format capabilities. /// /// A result of `None` indicates the format is not supported. @@ -404,14 +355,17 @@ impl Device { flags: vk::ImageCreateFlags, ) -> Result, DriverError> { unsafe { - match this.instance.get_physical_device_image_format_properties( - *this.physical_device, - format, - ty, - tiling, - usage, - flags, - ) { + match this + .physical_device + .instance + .get_physical_device_image_format_properties( + this.physical_device.handle, + format, + ty, + tiling, + usage, + flags, + ) { Ok(properties) => Ok(Some(properties)), Err(err) if err == vk::Result::ERROR_FORMAT_NOT_SUPPORTED => { // We don't log this condition because it is normal for unsupported @@ -425,11 +379,6 @@ impl Device { } } - /// Provides a reference to the Vulkan instance used by this device. - pub fn instance(this: &Self) -> &Instance { - &this.instance - } - pub(crate) fn pipeline_cache(this: &Self) -> vk::PipelineCache { this.pipeline_cache } @@ -487,7 +436,16 @@ impl Debug for Device { } } +#[doc(hidden)] impl Deref for Device { + type Target = DeviceRef; + + fn deref(&self) -> &Self::Target { + unsafe { &*(self as *const Self as *const Self::Target) } + } +} + +impl Deref for DeviceRef { type Target = ash::Device; fn deref(&self) -> &Self::Target { @@ -527,9 +485,10 @@ impl Drop for Device { } /// Information used to create a [`Device`] instance. -#[derive(Builder)] +#[derive(Builder, Clone, Copy, Debug, Default, Eq, PartialEq, Hash)] #[builder( build_fn(private, name = "fallible_build", error = "DeviceInfoBuilderError"), + derive(Clone, Copy, Debug), pattern = "owned" )] #[non_exhaustive] @@ -546,105 +505,60 @@ pub struct DeviceInfo { /// /// ## Platform-specific /// - /// **macOS:** Has no effect. + /// **macOS:** Has no effect unless the `loaded` feature is enabled. #[builder(default)] pub debug: bool, - /// Callback function used to select a [`PhysicalDevice`] from the available devices. The - /// callback must return the index of the selected device. - #[builder(default = "Box::new(DeviceInfo::discrete_gpu)")] - pub select_physical_device: Box, + /// Index of the [`PhysicalDevice`] from the available devices. See + /// [`Instance::physical_devices`]. + #[builder(default)] + pub physical_device_index: usize, } impl DeviceInfo { - /// Specifies default device information. - #[allow(clippy::new_ret_no_self)] - #[deprecated = "Use DeviceInfo::default()"] - #[doc(hidden)] - pub fn new() -> DeviceInfoBuilder { - Default::default() - } - - /// A builtin [`DeviceInfo::select_physical_device`] function which prioritizes selection of - /// lower-power integrated GPU devices. + /// A helper function which prioritizes selection of lower-power integrated GPU devices. #[profiling::function] - pub fn integrated_gpu(physical_devices: &[PhysicalDevice]) -> usize { - assert!(!physical_devices.is_empty()); - - let mut physical_devices = physical_devices.iter().enumerate().collect::>(); - - if physical_devices.len() == 1 { - return 0; - } - - fn device_type(ty: vk::PhysicalDeviceType) -> usize { - match ty { - vk::PhysicalDeviceType::INTEGRATED_GPU => 0, - vk::PhysicalDeviceType::VIRTUAL_GPU => 1, - vk::PhysicalDeviceType::CPU => 2, - vk::PhysicalDeviceType::DISCRETE_GPU => 3, - _ => 4, - } - } - - physical_devices.sort_unstable_by(|(_, lhs), (_, rhs)| { - let lhs_device_ty = device_type(lhs.properties_v1_0.device_type); - let rhs_device_ty = device_type(rhs.properties_v1_0.device_type); - let device_ty = lhs_device_ty.cmp(&rhs_device_ty); - - if device_ty != Ordering::Equal { - return device_ty; - } - - // TODO: Select the device with the most memory - - Ordering::Equal - }); - - let (idx, _) = physical_devices[0]; - - idx + pub fn integrated_gpu_index(physical_devices: &[PhysicalDevice]) -> usize { + Self::pick_best_gpu( + physical_devices, + &[ + vk::PhysicalDeviceType::INTEGRATED_GPU, + vk::PhysicalDeviceType::DISCRETE_GPU, + vk::PhysicalDeviceType::VIRTUAL_GPU, + vk::PhysicalDeviceType::CPU, + vk::PhysicalDeviceType::OTHER, + ], + ) } - /// A builtin [`DeviceInfo::select_physical_device`] function which prioritizes selection of - /// higher-performance discrete GPU devices. + /// A helper function which prioritizes selection of higher-performance discrete GPU devices. #[profiling::function] - pub fn discrete_gpu(physical_devices: &[PhysicalDevice]) -> usize { - assert!(!physical_devices.is_empty()); - - let mut physical_devices = physical_devices.iter().enumerate().collect::>(); - - if physical_devices.len() == 1 { - return 0; - } + pub fn discrete_gpu_index(physical_devices: &[PhysicalDevice]) -> usize { + Self::pick_best_gpu( + physical_devices, + &[ + vk::PhysicalDeviceType::DISCRETE_GPU, + vk::PhysicalDeviceType::VIRTUAL_GPU, + vk::PhysicalDeviceType::INTEGRATED_GPU, + vk::PhysicalDeviceType::CPU, + vk::PhysicalDeviceType::OTHER, + ], + ) + } - fn device_type(ty: vk::PhysicalDeviceType) -> usize { - match ty { - vk::PhysicalDeviceType::DISCRETE_GPU => 0, - vk::PhysicalDeviceType::INTEGRATED_GPU => 1, - vk::PhysicalDeviceType::VIRTUAL_GPU => 2, - vk::PhysicalDeviceType::CPU => 3, - _ => 4, + fn pick_best_gpu( + physical_devices: &[PhysicalDevice], + best: &[vk::PhysicalDeviceType], + ) -> usize { + for best in best.iter().copied() { + for (idx, physical_device) in physical_devices.iter().enumerate() { + if physical_device.properties_v1_0.device_type == best { + return idx; + } } } - physical_devices.sort_unstable_by(|(_, lhs), (_, rhs)| { - let lhs_device_ty = device_type(lhs.properties_v1_0.device_type); - let rhs_device_ty = device_type(rhs.properties_v1_0.device_type); - let device_ty = lhs_device_ty.cmp(&rhs_device_ty); - - if device_ty != Ordering::Equal { - return device_ty; - } - - // TODO: Select the device with the most memory - - Ordering::Equal - }); - - let (idx, _) = physical_devices[0]; - - idx + 0 } /// Converts a `DeviceInfo` into a `DeviceInfoBuilder`. @@ -652,25 +566,7 @@ impl DeviceInfo { pub fn to_builder(self) -> DeviceInfoBuilder { DeviceInfoBuilder { debug: Some(self.debug), - select_physical_device: Some(self.select_physical_device), - } - } -} - -impl Debug for DeviceInfo { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("DeviceInfo") - .field("debug", &self.debug) - .field("select_physical_device", &"fn") - .finish() - } -} - -impl Default for DeviceInfo { - fn default() -> Self { - Self { - debug: false, - select_physical_device: Box::new(DeviceInfo::discrete_gpu), + physical_device_index: Some(self.physical_device_index), } } } diff --git a/src/driver/instance.rs b/src/driver/instance.rs index 37d68b3c..d7126d5f 100644 --- a/src/driver/instance.rs +++ b/src/driver/instance.rs @@ -1,12 +1,16 @@ +//! TODO + use { super::{DriverError, physical_device::PhysicalDevice}, - ash::{Entry, ext, vk}, + ash::{ext, vk}, + derive_builder::{Builder, UninitializedFieldError}, log::{debug, error, trace, warn}, std::{ - ffi::{CStr, CString}, + ffi::CStr, fmt::{Debug, Formatter}, ops::Deref, os::raw::c_char, + sync::Arc, thread::panicking, }, }; @@ -109,27 +113,65 @@ unsafe extern "system" fn vulkan_debug_callback( vk::FALSE } +/// Vulkan API version. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)] +pub enum ApiVersion { + /// Version 1.2 + Vulkan12, + + /// Version 1.3 + #[default] + Vulkan13, +} + +impl ApiVersion { + /// The most recent supported version of Vulkan + pub const MAX: Self = Self::Vulkan13; +} + /// There is no global state in Vulkan and all per-application state is stored in a VkInstance /// object. /// /// Creating an Instance initializes the Vulkan library and allows the application to pass /// information about itself to the implementation. +#[derive(Clone)] +#[repr(C)] pub struct Instance { - _debug_callback: Option, - #[allow(deprecated)] // TODO: Remove? Look into this.... - _debug_loader: Option, - debug_utils: Option, - entry: Entry, - instance: ash::Instance, + /// The ash entrypoint. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub entry: ash::Entry, + + #[cfg(not(doc))] + entry: ash::Entry, + + /// Information used to create this resource. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub info: InstanceInfo, + + #[cfg(not(doc))] + info: InstanceInfo, + + inner: Arc, +} + +#[doc(hidden)] +#[repr(C)] +pub struct InstanceRef { + pub entry: ash::Entry, + pub info: InstanceInfo, + inner: Arc, } impl Instance { /// Creates a new Vulkan instance. #[profiling::function] - pub fn create<'a>( - debug: bool, - required_extensions: impl Iterator, - ) -> Result { + pub fn load(info: impl Into) -> Result { + let info = info.into(); + // Required to enable non-uniform descriptor indexing (bindless) #[cfg(target_os = "macos")] unsafe { @@ -138,7 +180,7 @@ impl Instance { #[cfg(not(target_os = "macos"))] let entry = unsafe { - Entry::load().map_err(|err| { + ash::Entry::load().map_err(|err| { error!("Vulkan driver not found: {err}"); DriverError::Unsupported @@ -148,36 +190,49 @@ impl Instance { #[cfg(target_os = "macos")] let entry = ash_molten::load(); - let required_extensions = required_extensions.collect::>(); - let instance_extensions = required_extensions + let mut extension_names = Self::debug_extension_names(info.debug); + extension_names.extend(info.extension_names); + let extension_name_ptrs = extension_names .iter() - .map(|ext| ext.as_ptr()) - .chain(unsafe { Self::extension_names(debug).into_iter() }) + .copied() + .map(CStr::as_ptr) .collect::>(); - let layer_names = Self::layer_names(debug); - let layer_names: Vec<*const c_char> = layer_names + + let layer_names = Self::debug_layer_names(info.debug); + let layer_name_ptrs = layer_names .iter() - .map(|raw_name| raw_name.as_ptr()) - .collect(); - let app_desc = vk::ApplicationInfo::default().api_version(vk::API_VERSION_1_2); + .copied() + .map(CStr::as_ptr) + .collect::>(); + + let app_desc = vk::ApplicationInfo::default().api_version(match info.api_version { + ApiVersion::Vulkan12 => vk::API_VERSION_1_2, + ApiVersion::Vulkan13 => vk::API_VERSION_1_3, + }); let instance_desc = vk::InstanceCreateInfo::default() .application_info(&app_desc) - .enabled_layer_names(&layer_names) - .enabled_extension_names(&instance_extensions); + .enabled_layer_names(&layer_name_ptrs) + .enabled_extension_names(&extension_name_ptrs); let instance = unsafe { entry.create_instance(&instance_desc, None).map_err(|_| { - if debug { + if info.debug { warn!("debug may only be enabled with a valid Vulkan SDK installation"); } - error!("Vulkan driver does not support API v1.2"); + error!( + "Vulkan driver does not support API v{}", + match info.api_version { + ApiVersion::Vulkan12 => "1.2", + ApiVersion::Vulkan13 => "1.3", + } + ); - for layer_name in Self::layer_names(debug) { + for layer_name in &layer_names { debug!("Layer: {:?}", layer_name); } - for extension_name in required_extensions { + for extension_name in &extension_names { debug!("Extension: {:?}", extension_name); } @@ -191,7 +246,7 @@ impl Instance { let (debug_loader, debug_callback, debug_utils) = (None, None, None); #[cfg(not(target_os = "macos"))] - let (debug_loader, debug_callback, debug_utils) = if debug { + let (debug_loader, debug_callback, debug_utils) = if info.debug { let debug_info = vk::DebugReportCallbackCreateInfoEXT { flags: vk::DebugReportFlagsEXT::ERROR | vk::DebugReportFlagsEXT::WARNING @@ -218,11 +273,14 @@ impl Instance { }; Ok(Self { - _debug_callback: debug_callback, - _debug_loader: debug_loader, - debug_utils, entry, - instance, + info, + inner: Arc::new(InstanceInner { + _debug_callback: debug_callback, + _debug_loader: debug_loader, + debug_utils, + instance, + }), }) } @@ -231,77 +289,139 @@ impl Instance { /// This is useful when you want to use a Vulkan instance created by some other library, such /// as OpenXR. #[profiling::function] - pub fn load(entry: Entry, instance: vk::Instance) -> Result { + pub fn from_entry(entry: ash::Entry, instance: vk::Instance) -> Result { if instance == vk::Instance::null() { return Err(DriverError::InvalidData); } + let api_version = unsafe { entry.try_enumerate_instance_version() } + .map_err(|err| match err { + vk::Result::ERROR_OUT_OF_HOST_MEMORY => DriverError::OutOfMemory, + vk::Result::ERROR_VALIDATION_FAILED_EXT => DriverError::InvalidData, + err => { + warn!("unable to enumerate instance version: {err}"); + + DriverError::Unsupported + } + })? + .map(|version| { + match ( + vk::api_version_major(version), + vk::api_version_minor(version), + ) { + (1, x) if x >= 3 => Some(ApiVersion::Vulkan13), + (1, 2) => Some(ApiVersion::Vulkan12), + (major, minor) => { + warn!("unsupported Vulkan version: {major}.{minor}"); + + None + } + } + }) + .ok_or(DriverError::Unsupported)? + .unwrap_or(ApiVersion::MAX); + let instance = unsafe { ash::Instance::load(entry.static_fn(), instance) }; Ok(Self { - _debug_callback: None, - _debug_loader: None, - debug_utils: None, entry, - instance, + info: InstanceInfo { + api_version, + ..Default::default() + }, + inner: Arc::new(InstanceInner { + _debug_callback: None, + _debug_loader: None, + debug_utils: None, + instance, + }), }) } /// Returns the `ash` entrypoint for Vulkan functions. - pub fn entry(this: &Self) -> &Entry { + pub fn entry(this: &Self) -> &ash::Entry { &this.entry } - unsafe fn extension_names( + fn debug_extension_names( #[cfg_attr(target_os = "macos", allow(unused_variables))] debug: bool, - ) -> Vec<*const c_char> { + ) -> Vec<&'static CStr> { #[cfg_attr(target_os = "macos", allow(unused_mut))] let mut res = vec![]; #[cfg(not(target_os = "macos"))] if debug { #[allow(deprecated)] - res.push(ext::debug_report::NAME.as_ptr()); - res.push(ext::debug_utils::NAME.as_ptr()); + res.push(ext::debug_report::NAME); + res.push(ext::debug_utils::NAME); } res } - /// Returns `true` if this instance was created with debug layers enabled. - pub fn is_debug(this: &Self) -> bool { - this.debug_utils.is_some() - } - - fn layer_names( + fn debug_layer_names( #[cfg_attr(target_os = "macos", allow(unused_variables))] debug: bool, - ) -> Vec { + ) -> Vec<&'static CStr> { #[cfg_attr(target_os = "macos", allow(unused_mut))] let mut res = vec![]; #[cfg(not(target_os = "macos"))] if debug { - res.push(CString::new("VK_LAYER_KHRONOS_validation").unwrap()); + res.push(c"VK_LAYER_KHRONOS_validation"); } res } + /// Returns `true` if this instance was created with debug layers enabled. + pub fn is_debug(this: &Self) -> bool { + this.inner.debug_utils.is_some() + } + + /// Returns a wrapper structure for a physical device of this instance. + #[profiling::function] + pub fn physical_device( + this: &Self, + physical_device: vk::PhysicalDevice, + ) -> Result { + let physical_device = PhysicalDevice::new(this.clone(), physical_device)?; + let major = vk::api_version_major(physical_device.properties_v1_0.api_version); + let minor = vk::api_version_minor(physical_device.properties_v1_0.api_version); + let supports_vulkan_1_2 = major == 1 && minor >= 2; + + if !supports_vulkan_1_2 { + warn!( + "physical device `{}` does not support Vulkan v1.2", + physical_device.properties_v1_0.device_name + ); + + return Err(DriverError::Unsupported); + } + + Ok(physical_device) + } + /// Returns the available physical devices of this instance. #[profiling::function] pub fn physical_devices(this: &Self) -> Result, DriverError> { - let physical_devices = unsafe { this.enumerate_physical_devices() }; + let physical_devices = unsafe { this.enumerate_physical_devices() }.map_err(|err| { + error!("unable to enumerate physical devices: {err}"); - Ok(physical_devices - .map_err(|err| { - error!("unable to enumerate physical devices: {err}"); + match err { + vk::Result::ERROR_INITIALIZATION_FAILED => DriverError::Unsupported, + vk::Result::ERROR_OUT_OF_DEVICE_MEMORY | vk::Result::ERROR_OUT_OF_HOST_MEMORY => { + DriverError::OutOfMemory + } + vk::Result::ERROR_VALIDATION_FAILED_EXT => DriverError::InvalidData, + _ => DriverError::Unsupported, + } + })?; - DriverError::Unsupported - })? + Ok(physical_devices .into_iter() .enumerate() .filter_map(|(idx, physical_device)| { - let res = PhysicalDevice::new(this, physical_device); + let res = PhysicalDevice::new(this.clone(), physical_device); if let Err(err) = &res { warn!("unable to create physical device at index {idx}: {err}"); @@ -310,7 +430,7 @@ impl Instance { res.ok().filter(|physical_device| { let major = vk::api_version_major(physical_device.properties_v1_0.api_version); let minor = vk::api_version_minor(physical_device.properties_v1_0.api_version); - let supports_vulkan_1_2 = major > 1 || (major == 1 && minor >= 2); + let supports_vulkan_1_2 = major == 1 && minor >= 2; if !supports_vulkan_1_2 { warn!( @@ -332,15 +452,117 @@ impl Debug for Instance { } } +#[doc(hidden)] impl Deref for Instance { + type Target = InstanceRef; + + fn deref(&self) -> &Self::Target { + unsafe { &*(self as *const Self as *const Self::Target) } + } +} + +#[doc(hidden)] +impl Deref for InstanceRef { type Target = ash::Instance; fn deref(&self) -> &Self::Target { - &self.instance + &self.inner.instance + } +} + +/// Information used to create an [`Instance`] instance. +#[derive(Builder, Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +#[builder( + build_fn(private, name = "fallible_build", error = "InstanceInfoBuilderError"), + derive(Clone, Debug), + pattern = "owned" +)] +#[non_exhaustive] +pub struct InstanceInfo { + /// The Vulkan API version to target + #[builder(default = "ApiVersion::Vulkan13")] + pub api_version: ApiVersion, + + /// Enables Vulkan validation layers. + /// + /// This requires a Vulkan SDK installation and will cause validation errors to introduce + /// panics as they happen. + /// + /// _NOTE:_ Consider turning OFF debug if you discover an unknown issue. Often the validation + /// layers will throw an error before other layers can provide additional context such as the + /// API dump info or other messages. You might find the "actual" issue is detailed in those + /// subsequent details. + /// + /// ## Platform-specific + /// + /// **macOS:** Has no effect unless the `loaded` feature is enabled. + #[builder(default)] + pub debug: bool, + + /// Required Vulkan instance extension names to load + #[builder(default)] + pub extension_names: &'static [&'static CStr], +} + +impl InstanceInfo { + /// Converts a `InstanceInfo` into a `InstanceInfoBuilder`. + #[inline(always)] + pub fn to_builder(self) -> InstanceInfoBuilder { + InstanceInfoBuilder { + api_version: Some(self.api_version), + debug: Some(self.debug), + extension_names: Some(self.extension_names), + } + } +} + +impl InstanceInfoBuilder { + /// Builds a new `InstanceInfo`. + #[inline(always)] + pub fn build(mut self) -> InstanceInfo { + if self.api_version.is_none() { + self.api_version = Some(ApiVersion::MAX); + } + + if self.debug.is_none() { + self.debug = Some(false); + } + + if self.extension_names.is_none() { + self.extension_names = Some(&[]); + } + + match self.fallible_build() { + Err(InstanceInfoBuilderError(err)) => panic!("{err}"), + Ok(info) => info, + } + } +} + +impl From for InstanceInfo { + fn from(info: InstanceInfoBuilder) -> Self { + info.build() } } -impl Drop for Instance { +#[derive(Debug)] +struct InstanceInfoBuilderError(UninitializedFieldError); + +impl From for InstanceInfoBuilderError { + fn from(err: UninitializedFieldError) -> Self { + Self(err) + } +} + +struct InstanceInner { + _debug_callback: Option, + #[allow(deprecated)] // TODO: Remove? Look into this.... + _debug_loader: Option, + debug_utils: Option, + instance: ash::Instance, +} + +impl Drop for InstanceInner { #[profiling::function] fn drop(&mut self) { if panicking() { diff --git a/src/driver/mod.rs b/src/driver/mod.rs index 00b572ca..d56e5eee 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -32,6 +32,7 @@ pub mod compute; pub mod device; pub mod graphic; pub mod image; +pub mod instance; pub mod physical_device; pub mod ray_trace; pub mod render_pass; @@ -42,10 +43,9 @@ pub mod swapchain; mod cmd_buf; mod descriptor_set; mod descriptor_set_layout; -mod instance; pub use { - self::{cmd_buf::CommandBuffer, instance::Instance}, + self::cmd_buf::CommandBuffer, ash::{self}, vk_sync::AccessType, }; @@ -82,6 +82,8 @@ use { vk_sync::ImageLayout, }; +//pub type Result = Result; + pub(super) const fn format_aspect_mask(fmt: vk::Format) -> vk::ImageAspectFlags { match fmt { vk::Format::D16_UNORM | vk::Format::D32_SFLOAT | vk::Format::X8_D24_UNORM_PACK32 => { diff --git a/src/driver/physical_device.rs b/src/driver/physical_device.rs index f8b09f3d..01a65f06 100644 --- a/src/driver/physical_device.rs +++ b/src/driver/physical_device.rs @@ -1,13 +1,14 @@ //! Physical device resource types use { - super::{DriverError, Instance}, + super::{DriverError, instance::Instance}, ash::{ext, khr, vk}, log::{debug, error}, std::{ collections::HashSet, ffi::{CStr, c_char}, fmt::{Debug, Formatter}, + iter::repeat_n, ops::Deref, }, }; @@ -139,75 +140,202 @@ impl From> for IndexTypeUint8Fea } /// Structure which holds data about the physical hardware selected by the current device. +#[repr(C)] pub struct PhysicalDevice { /// Describes the properties of the device which relate to acceleration structures, if /// available. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] pub accel_struct_properties: Option, + #[cfg(not(doc))] + accel_struct_properties: Option, + /// Describes the properties of the device which relate to depth/stencil resolve operations. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] pub depth_stencil_resolve_properties: DepthStencilResolveProperties, + #[cfg(not(doc))] + depth_stencil_resolve_properties: DepthStencilResolveProperties, + /// Describes the features of the physical device which are part of the Vulkan 1.0 base feature set. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] pub features_v1_0: Vulkan10Features, + #[cfg(not(doc))] + features_v1_0: Vulkan10Features, + /// Describes the features of the physical device which are part of the Vulkan 1.1 base feature set. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] pub features_v1_1: Vulkan11Features, + #[cfg(not(doc))] + features_v1_1: Vulkan11Features, + /// Describes the features of the physical device which are part of the Vulkan 1.2 base feature set. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] pub features_v1_2: Vulkan12Features, + #[cfg(not(doc))] + features_v1_2: Vulkan12Features, + + /// The native Vulkan resource handle of this buffer. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub handle: vk::PhysicalDevice, + + #[cfg(not(doc))] + handle: vk::PhysicalDevice, + /// Describes the features of the physical device which relate to vertex indexing. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] pub index_type_uint8_features: IndexTypeUint8Features, + #[cfg(not(doc))] + index_type_uint8_features: IndexTypeUint8Features, + + /// The Vulkan instance which owns this device. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub instance: Instance, + + #[cfg(not(doc))] + instance: Instance, + /// Memory properties of the physical device. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] pub memory_properties: vk::PhysicalDeviceMemoryProperties, + #[cfg(not(doc))] + memory_properties: vk::PhysicalDeviceMemoryProperties, + /// Device properties of the physical device which are part of the Vulkan 1.0 base feature set. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] pub properties_v1_0: Vulkan10Properties, + #[cfg(not(doc))] + properties_v1_0: Vulkan10Properties, + /// Describes the properties of the physical device which are part of the Vulkan 1.1 base /// feature set. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] pub properties_v1_1: Vulkan11Properties, + #[cfg(not(doc))] + properties_v1_1: Vulkan11Properties, + /// Describes the properties of the physical device which are part of the Vulkan 1.2 base /// feature set. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] pub properties_v1_2: Vulkan12Properties, - physical_device: vk::PhysicalDevice, + #[cfg(not(doc))] + properties_v1_2: Vulkan12Properties, /// Describes the queues offered by this physical device. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] pub queue_families: Box<[vk::QueueFamilyProperties]>, + #[cfg(not(doc))] + queue_families: Box<[vk::QueueFamilyProperties]>, + pub(crate) queue_family_indices: Box<[u32]>, /// Describes the features of the device which relate to ray query, if available. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] pub ray_query_features: RayQueryFeatures, + #[cfg(not(doc))] + ray_query_features: RayQueryFeatures, + /// Describes the features of the device which relate to ray tracing, if available. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] pub ray_trace_features: RayTraceFeatures, + #[cfg(not(doc))] + ray_trace_features: RayTraceFeatures, + /// Describes the properties of the device which relate to ray tracing, if available. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] pub ray_trace_properties: Option, + #[cfg(not(doc))] + ray_trace_properties: Option, + /// Describes the properties of the device which relate to min/max sampler filtering. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub sampler_filter_minmax_properties: SamplerFilterMinmaxProperties, + + #[cfg(not(doc))] + sampler_filter_minmax_properties: SamplerFilterMinmaxProperties, +} + +#[allow(missing_docs)] +#[repr(C)] +pub struct PhysicalDeviceRef { + pub accel_struct_properties: Option, + pub depth_stencil_resolve_properties: DepthStencilResolveProperties, + pub features_v1_0: Vulkan10Features, + pub features_v1_1: Vulkan11Features, + pub features_v1_2: Vulkan12Features, + pub handle: vk::PhysicalDevice, + pub index_type_uint8_features: IndexTypeUint8Features, + pub instance: Instance, + pub memory_properties: vk::PhysicalDeviceMemoryProperties, + pub properties_v1_0: Vulkan10Properties, + pub properties_v1_1: Vulkan11Properties, + pub properties_v1_2: Vulkan12Properties, + pub queue_families: Box<[vk::QueueFamilyProperties]>, + pub(crate) queue_family_indices: Box<[u32]>, + pub ray_query_features: RayQueryFeatures, + pub ray_trace_features: RayTraceFeatures, + pub ray_trace_properties: Option, pub sampler_filter_minmax_properties: SamplerFilterMinmaxProperties, } impl PhysicalDevice { /// Creates a physical device wrapper which reports features and properties. #[profiling::function] - pub fn new( - instance: &Instance, - physical_device: vk::PhysicalDevice, - ) -> Result { - if physical_device == vk::PhysicalDevice::null() { + pub(super) fn new(instance: Instance, handle: vk::PhysicalDevice) -> Result { + if handle == vk::PhysicalDevice::null() { return Err(DriverError::InvalidData); } let (memory_properties, queue_families) = unsafe { ( - instance.get_physical_device_memory_properties(physical_device), - instance.get_physical_device_queue_family_properties(physical_device), + instance.get_physical_device_memory_properties(handle), + instance.get_physical_device_queue_family_properties(handle), ) }; @@ -241,7 +369,7 @@ impl PhysicalDevice { .push_next(&mut ray_query_features) .push_next(&mut ray_trace_features); unsafe { - get_physical_device_features2(physical_device, &mut features); + get_physical_device_features2(handle, &mut features); } let features_v1_0 = features.features.into(); let features_v1_1 = features_v1_1.into(); @@ -265,7 +393,7 @@ impl PhysicalDevice { .push_next(&mut ray_trace_properties) .push_next(&mut sampler_filter_minmax_properties); unsafe { - get_physical_device_properties2(physical_device, &mut properties); + get_physical_device_properties2(handle, &mut properties); } let properties_v1_0: Vulkan10Properties = properties.properties.into(); let properties_v1_1 = properties_v1_1.into(); @@ -275,7 +403,7 @@ impl PhysicalDevice { let extensions = unsafe { instance - .enumerate_device_extension_properties(physical_device) + .enumerate_device_extension_properties(handle) .map_err(|err| { error!("Unable to enumerate device extensions {err}"); @@ -335,9 +463,10 @@ impl PhysicalDevice { features_v1_0, features_v1_1, features_v1_2, + handle, index_type_uint8_features, + instance, memory_properties, - physical_device, properties_v1_0, properties_v1_1, properties_v1_2, @@ -349,6 +478,123 @@ impl PhysicalDevice { sampler_filter_minmax_properties, }) } + + /// Prepares device creation information and calls the provided callback to allow an application + /// to control the device creation process. + /// + /// _Note:_ This is only useful for interoperting with other libraries as device creation is + /// normally handled by the [`Device::create_display`] and [`Device::create_headless`] + /// functions. + /// + /// # Safety + /// + /// This comes with all the caveats of using `ash` builder types, which are inherently + /// dangerous. Use with extreme caution. + #[profiling::function] + pub unsafe fn create_ash_device( + &self, + display_window: bool, + create_fn: F, + ) -> ash::prelude::VkResult + where + F: FnOnce(vk::DeviceCreateInfo) -> ash::prelude::VkResult, + { + let mut enabled_ext_names = Vec::with_capacity(6); + + if display_window { + enabled_ext_names.push(khr::swapchain::NAME.as_ptr()); + } + + if self.accel_struct_properties.is_some() { + enabled_ext_names.push(khr::acceleration_structure::NAME.as_ptr()); + enabled_ext_names.push(khr::deferred_host_operations::NAME.as_ptr()); + } + + if self.ray_query_features.ray_query { + enabled_ext_names.push(khr::ray_query::NAME.as_ptr()); + } + + if self.ray_trace_features.ray_tracing_pipeline { + enabled_ext_names.push(khr::ray_tracing_pipeline::NAME.as_ptr()); + } + + if self.index_type_uint8_features.index_type_uint8 { + enabled_ext_names.push(ext::index_type_uint8::NAME.as_ptr()); + } + + let priorities = repeat_n( + 1.0, + self.queue_families + .iter() + .map(|family| family.queue_count) + .max() + .unwrap_or_default() as _, + ) + .collect::>(); + + let queue_infos = self + .queue_families + .iter() + .enumerate() + .map(|(idx, family)| { + let mut queue_info = vk::DeviceQueueCreateInfo::default() + .queue_family_index(idx as _) + .queue_priorities(&priorities[0..family.queue_count as usize]); + queue_info.queue_count = family.queue_count; + + queue_info + }) + .collect::>(); + + let ash::InstanceFnV1_1 { + get_physical_device_features2, + .. + } = self.instance.fp_v1_1(); + let mut features_v1_1 = vk::PhysicalDeviceVulkan11Features::default(); + let mut features_v1_2 = vk::PhysicalDeviceVulkan12Features::default(); + let mut acceleration_structure_features = + vk::PhysicalDeviceAccelerationStructureFeaturesKHR::default(); + let mut index_type_uint8_features = vk::PhysicalDeviceIndexTypeUint8FeaturesEXT::default(); + let mut ray_query_features = vk::PhysicalDeviceRayQueryFeaturesKHR::default(); + let mut ray_trace_features = vk::PhysicalDeviceRayTracingPipelineFeaturesKHR::default(); + let mut features = vk::PhysicalDeviceFeatures2::default() + .push_next(&mut features_v1_1) + .push_next(&mut features_v1_2); + + if self.accel_struct_properties.is_some() { + features = features.push_next(&mut acceleration_structure_features); + } + + if self.ray_query_features.ray_query { + features = features.push_next(&mut ray_query_features); + } + + if self.ray_trace_features.ray_tracing_pipeline { + features = features.push_next(&mut ray_trace_features); + } + + if self.index_type_uint8_features.index_type_uint8 { + features = features.push_next(&mut index_type_uint8_features); + } + + unsafe { get_physical_device_features2(self.handle, &mut features) }; + + let device_create_info = vk::DeviceCreateInfo::default() + .queue_create_infos(&queue_infos) + .enabled_extension_names(&enabled_ext_names) + .push_next(&mut features); + + create_fn(device_create_info) + } + + /// Lists the capabilities of a given format. + #[profiling::function] + pub fn format_properties(&self, format: vk::Format) -> vk::FormatProperties { + unsafe { + self.instance + .get_physical_device_format_properties(self.handle, format) + } + } } impl Debug for PhysicalDevice { @@ -361,11 +607,12 @@ impl Debug for PhysicalDevice { } } +#[doc(hidden)] impl Deref for PhysicalDevice { - type Target = vk::PhysicalDevice; + type Target = PhysicalDeviceRef; fn deref(&self) -> &Self::Target { - &self.physical_device + unsafe { &*(self as *const Self as *const Self::Target) } } } diff --git a/src/driver/ray_trace.rs b/src/driver/ray_trace.rs index 1959276e..87d34a4a 100644 --- a/src/driver/ray_trace.rs +++ b/src/driver/ray_trace.rs @@ -147,6 +147,10 @@ impl RayTracePipeline { where S: Into, { + if device.physical_device.ray_trace_properties.is_none() { + return Err(DriverError::Unsupported); + } + let info = info.into(); let shader_groups = shader_groups .into_iter() @@ -254,10 +258,7 @@ impl RayTracePipeline { dynamic_states.push(vk::DynamicState::RAY_TRACING_PIPELINE_STACK_SIZE_KHR); } - let ray_trace_ext = device - .ray_trace_ext - .as_ref() - .ok_or(DriverError::Unsupported)?; + let ray_trace_ext = Device::expect_ray_trace_ext(device); let handle = ray_trace_ext .create_ray_tracing_pipelines( vk::DeferredOperationKHR::null(), @@ -380,11 +381,11 @@ impl RayTracePipeline { ) -> vk::DeviceSize { unsafe { // Safely use unchecked because ray_trace_ext is checked during pipeline creation - self.device - .ray_trace_ext - .as_ref() - .unwrap_unchecked() - .get_ray_tracing_shader_group_stack_size(self.handle, group, group_shader) + Device::expect_ray_trace_ext(&self.device).get_ray_tracing_shader_group_stack_size( + self.handle, + group, + group_shader, + ) } } diff --git a/src/driver/surface.rs b/src/driver/surface.rs index ec44ca4a..e9c74fe1 100644 --- a/src/driver/surface.rs +++ b/src/driver/surface.rs @@ -1,7 +1,7 @@ //! Native platform window surface types. use { - super::{DriverError, Instance, device::Device}, + super::{DriverError, device::Device}, ash::vk, ash_window::create_surface, log::warn, @@ -49,8 +49,10 @@ impl Surface { let surface_ext = Device::expect_surface_ext(&self.device); unsafe { - surface_ext - .get_physical_device_surface_capabilities(*self.device.physical_device, self.handle) + surface_ext.get_physical_device_surface_capabilities( + self.device.physical_device.handle, + self.handle, + ) } .inspect_err(|err| warn!("unable to get surface capabilities: {err}")) .or(Err(DriverError::Unsupported)) @@ -67,7 +69,7 @@ impl Surface { window: impl HasWindowHandle, ) -> Result { let device = Arc::clone(device); - let instance = Device::instance(&device); + let display_handle = display.display_handle().map_err(|err| { warn!("{err}"); @@ -78,10 +80,11 @@ impl Surface { DriverError::Unsupported })?; + let handle = unsafe { create_surface( - Instance::entry(instance), - instance, + &device.physical_device.instance.entry, + &device.physical_device.instance, display_handle.as_raw(), window_handle.as_raw(), None, @@ -100,11 +103,11 @@ impl Surface { #[profiling::function] pub fn formats(&self) -> Result, DriverError> { unsafe { - self.device - .surface_ext - .as_ref() - .unwrap() - .get_physical_device_surface_formats(*self.device.physical_device, self.handle) + Device::expect_surface_ext(&self.device) + .get_physical_device_surface_formats( + self.device.physical_device.handle, + self.handle, + ) .map_err(|err| { warn!("Unable to get surface formats: {err}"); @@ -138,16 +141,13 @@ impl Surface { /// Query supported presentation modes. pub fn present_modes(&self) -> Result, DriverError> { + let physical_device = self.device.physical_device.handle; let surface_ext = Device::expect_surface_ext(&self.device); + let surface = self.handle; - unsafe { - surface_ext.get_physical_device_surface_present_modes( - *self.device.physical_device, - self.handle, - ) - } - .inspect_err(|err| warn!("unable to get surface present modes: {err}")) - .or(Err(DriverError::Unsupported)) + unsafe { surface_ext.get_physical_device_surface_present_modes(physical_device, surface) } + .inspect_err(|err| warn!("unable to get surface present modes: {err}")) + .or(Err(DriverError::Unsupported)) } /// Helper function to automatically select the best sRGB format, if one is available. diff --git a/src/driver/swapchain.rs b/src/driver/swapchain.rs index 791ba60b..34a4face 100644 --- a/src/driver/swapchain.rs +++ b/src/driver/swapchain.rs @@ -531,7 +531,7 @@ impl Deref for SwapchainImage { #[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)] #[builder( build_fn(private, name = "fallible_build", error = "SwapchainInfoBuilderError"), - derive(Clone, Debug), + derive(Clone, Copy, Debug), pattern = "owned" )] #[non_exhaustive] diff --git a/src/graph/pass_ref.rs b/src/graph/pass_ref.rs index 5627f2b5..f4e581d3 100644 --- a/src/graph/pass_ref.rs +++ b/src/graph/pass_ref.rs @@ -4900,11 +4900,7 @@ impl RayTrace<'_> { assert!(self.dynamic_stack_size); unsafe { - // Safely use unchecked because ray_trace_ext is checked during pipeline creation - self.device - .ray_trace_ext - .as_ref() - .unwrap_unchecked() + Device::expect_ray_trace_ext(self.device) .cmd_set_ray_tracing_pipeline_stack_size(self.cmd_buf, pipeline_stack_size); } @@ -4967,21 +4963,16 @@ impl RayTrace<'_> { depth: u32, ) -> &Self { unsafe { - // Safely use unchecked because ray_trace_ext is checked during pipeline creation - self.device - .ray_trace_ext - .as_ref() - .unwrap_unchecked() - .cmd_trace_rays( - self.cmd_buf, - raygen_shader_binding_table, - miss_shader_binding_table, - hit_shader_binding_table, - callable_shader_binding_table, - width, - height, - depth, - ); + Device::expect_ray_trace_ext(self.device).cmd_trace_rays( + self.cmd_buf, + raygen_shader_binding_table, + miss_shader_binding_table, + hit_shader_binding_table, + callable_shader_binding_table, + width, + height, + depth, + ); } self @@ -5006,19 +4997,14 @@ impl RayTrace<'_> { indirect_device_address: vk::DeviceAddress, ) -> &Self { unsafe { - // Safely use unchecked because ray_trace_ext is checked during pipeline creation - self.device - .ray_trace_ext - .as_ref() - .unwrap_unchecked() - .cmd_trace_rays_indirect( - self.cmd_buf, - raygen_shader_binding_table, - miss_shader_binding_table, - hit_shader_binding_table, - callable_shader_binding_table, - indirect_device_address, - ) + Device::expect_ray_trace_ext(self.device).cmd_trace_rays_indirect( + self.cmd_buf, + raygen_shader_binding_table, + miss_shader_binding_table, + hit_shader_binding_table, + callable_shader_binding_table, + indirect_device_address, + ) } self From 3cbd30f6d00926fff6309391841e231cf6de590f Mon Sep 17 00:00:00 2001 From: John Wells Date: Tue, 17 Feb 2026 22:40:05 -0500 Subject: [PATCH 10/86] Merge graph module into super --- contrib/vk-graph-prelude/src/lib.rs | 14 +- contrib/vk-graph-window/src/frame.rs | 5 +- contrib/vk-graph-window/src/lib.rs | 2 +- examples/app.rs | 2 +- examples/shader-toy/src/main.rs | 2 +- examples/skeletal-anim/src/main.rs | 2 +- examples/vr/src/main.rs | 2 +- src/{graph => }/binding.rs | 0 src/display.rs | 3 +- src/{graph => }/edge.rs | 0 src/graph/mod.rs | 1035 ------------------------- src/{graph => }/info.rs | 0 src/lib.rs | 1067 +++++++++++++++++++++++++- src/mod.rs | 3 + src/{graph => }/node.rs | 0 src/{graph => }/pass_ref.rs | 38 +- src/{graph => }/resolver.rs | 0 src/{graph => }/swapchain.rs | 0 18 files changed, 1089 insertions(+), 1086 deletions(-) rename src/{graph => }/binding.rs (100%) rename src/{graph => }/edge.rs (100%) delete mode 100644 src/graph/mod.rs rename src/{graph => }/info.rs (100%) create mode 100644 src/mod.rs rename src/{graph => }/node.rs (100%) rename src/{graph => }/pass_ref.rs (99%) rename src/{graph => }/resolver.rs (100%) rename src/{graph => }/swapchain.rs (100%) diff --git a/contrib/vk-graph-prelude/src/lib.rs b/contrib/vk-graph-prelude/src/lib.rs index 1d025d64..9bea36dd 100644 --- a/contrib/vk-graph-prelude/src/lib.rs +++ b/contrib/vk-graph-prelude/src/lib.rs @@ -3,6 +3,7 @@ #![warn(missing_docs)] pub use vk_graph::{ + Bind, ClearColorValue, RenderGraph, Unbind, display::{Display, DisplayError, DisplayInfo, DisplayInfoBuilder, ResolverPool}, driver::{ AccessType, CommandBuffer, DriverError, @@ -40,15 +41,12 @@ pub use vk_graph::{ Swapchain, SwapchainError, SwapchainImage, SwapchainInfo, SwapchainInfoBuilder, }, }, - graph::{ - Bind, ClearColorValue, RenderGraph, Unbind, - node::{ - AccelerationStructureLeaseNode, AccelerationStructureNode, - AnyAccelerationStructureNode, AnyBufferNode, AnyImageNode, BufferLeaseNode, BufferNode, - ImageLeaseNode, ImageNode, SwapchainImageNode, - }, - pass_ref::{PassRef, PipelinePassRef}, + node::{ + AccelerationStructureLeaseNode, AccelerationStructureNode, AnyAccelerationStructureNode, + AnyBufferNode, AnyImageNode, BufferLeaseNode, BufferNode, ImageLeaseNode, ImageNode, + SwapchainImageNode, }, + pass_ref::{PassRef, PipelinePassRef}, pool::{ Lease, Pool, PoolInfo, PoolInfoBuilder, alias::{Alias, AliasPool}, diff --git a/contrib/vk-graph-window/src/frame.rs b/contrib/vk-graph-window/src/frame.rs index 8ef4b15c..cb210476 100644 --- a/contrib/vk-graph-window/src/frame.rs +++ b/contrib/vk-graph-window/src/frame.rs @@ -1,9 +1,6 @@ use { std::sync::Arc, - vk_graph::{ - driver::device::Device, - graph::{node::SwapchainImageNode, RenderGraph}, - }, + vk_graph::{driver::device::Device, node::SwapchainImageNode, RenderGraph}, winit::{dpi::PhysicalPosition, event::Event, window::Window}, }; diff --git a/contrib/vk-graph-window/src/lib.rs b/contrib/vk-graph-window/src/lib.rs index c2704518..073fab43 100644 --- a/contrib/vk-graph-window/src/lib.rs +++ b/contrib/vk-graph-window/src/lib.rs @@ -18,8 +18,8 @@ use { swapchain::{Swapchain, SwapchainInfo}, DriverError, }, - graph::RenderGraph, pool::hash::HashPool, + RenderGraph, }, winit::{ application::ApplicationHandler, diff --git a/examples/app.rs b/examples/app.rs index d7acb1e5..56aed0c8 100644 --- a/examples/app.rs +++ b/examples/app.rs @@ -5,13 +5,13 @@ use { log::error, std::sync::Arc, vk_graph::{ + RenderGraph, display::{Display, DisplayError, DisplayInfo}, driver::{ device::{Device, DeviceInfoBuilder}, surface::Surface, swapchain::{Swapchain, SwapchainInfo}, }, - graph::RenderGraph, pool::hash::HashPool, }, winit::{ diff --git a/examples/shader-toy/src/main.rs b/examples/shader-toy/src/main.rs index 9856bf39..490cf2cc 100644 --- a/examples/shader-toy/src/main.rs +++ b/examples/shader-toy/src/main.rs @@ -45,8 +45,8 @@ use { image::ImageInfo, shader::Shader, }, - graph::RenderGraph, pool::{lazy::LazyPool, Pool as _}, + RenderGraph, }, vk_graph_fx::*, vk_graph_window::WindowBuilder, diff --git a/examples/skeletal-anim/src/main.rs b/examples/skeletal-anim/src/main.rs index f99c4c1f..7ccff60c 100644 --- a/examples/skeletal-anim/src/main.rs +++ b/examples/skeletal-anim/src/main.rs @@ -26,8 +26,8 @@ use { shader::Shader, AccessType, DriverError, }, - graph::RenderGraph, pool::{hash::HashPool, lazy::LazyPool, Pool as _}, + RenderGraph, }, vk_graph_window::{WindowBuilder, WindowError}, }; diff --git a/examples/vr/src/main.rs b/examples/vr/src/main.rs index 17133b7f..5365fa8b 100644 --- a/examples/vr/src/main.rs +++ b/examples/vr/src/main.rs @@ -29,8 +29,8 @@ use { image::{Image, ImageInfo}, AccessType, }, - graph::RenderGraph, pool::{lazy::LazyPool, Pool as _}, + RenderGraph, }, vk_graph_hot::{graphic::HotGraphicPipeline, shader::HotShader}, }; diff --git a/src/graph/binding.rs b/src/binding.rs similarity index 100% rename from src/graph/binding.rs rename to src/binding.rs diff --git a/src/display.rs b/src/display.rs index efd87b73..b7be744e 100644 --- a/src/display.rs +++ b/src/display.rs @@ -2,6 +2,7 @@ use { super::{ + RenderGraph, driver::{ CommandBuffer, CommandBufferInfo, DescriptorPool, DescriptorPoolInfo, DriverError, RenderPass, RenderPassInfo, @@ -10,7 +11,7 @@ use { image_access_layout, swapchain::{Swapchain, SwapchainError, SwapchainImage, SwapchainInfo}, }, - graph::{RenderGraph, node::SwapchainImageNode}, + node::SwapchainImageNode, pool::Pool, }, ash::vk, diff --git a/src/graph/edge.rs b/src/edge.rs similarity index 100% rename from src/graph/edge.rs rename to src/edge.rs diff --git a/src/graph/mod.rs b/src/graph/mod.rs deleted file mode 100644 index 4fb82725..00000000 --- a/src/graph/mod.rs +++ /dev/null @@ -1,1035 +0,0 @@ -//! Rendering operations and command submission. -//! -//! - -pub mod node; -pub mod pass_ref; - -mod binding; -mod edge; -mod info; -mod resolver; -mod swapchain; - -pub use self::{ - binding::{Bind, Unbind}, - resolver::Resolver, -}; - -use { - self::{ - binding::Binding, - edge::Edge, - info::Information, - node::Node, - node::{ - AccelerationStructureLeaseNode, AccelerationStructureNode, - AnyAccelerationStructureNode, AnyBufferNode, AnyImageNode, BufferLeaseNode, BufferNode, - ImageLeaseNode, ImageNode, SwapchainImageNode, - }, - pass_ref::{AttachmentIndex, Bindings, Descriptor, PassRef, SubresourceAccess, ViewType}, - }, - crate::driver::{ - DescriptorBindingMap, - buffer::Buffer, - compute::ComputePipeline, - device::Device, - format_aspect_mask, format_texel_block_extent, format_texel_block_size, - graphic::{DepthStencilMode, GraphicPipeline}, - image::{ImageInfo, ImageViewInfo, SampleCount}, - image_subresource_range_from_layers, - ray_trace::RayTracePipeline, - render_pass::ResolveMode, - shader::PipelineDescriptorInfo, - }, - ash::vk, - std::{ - cmp::Ord, - collections::{BTreeMap, HashMap}, - fmt::{Debug, Formatter}, - ops::Range, - sync::Arc, - }, - vk_sync::AccessType, -}; - -type ExecFn = Box) + Send>; -type NodeIndex = usize; - -#[derive(Clone, Copy, Debug)] -struct Area { - height: u32, - width: u32, - x: i32, - y: i32, -} - -#[derive(Clone, Copy, Debug)] -struct Attachment { - array_layer_count: u32, - aspect_mask: vk::ImageAspectFlags, - base_array_layer: u32, - base_mip_level: u32, - format: vk::Format, - mip_level_count: u32, - sample_count: SampleCount, - target: NodeIndex, -} - -impl Attachment { - fn new(image_view_info: ImageViewInfo, sample_count: SampleCount, target: NodeIndex) -> Self { - Self { - array_layer_count: image_view_info.array_layer_count, - aspect_mask: image_view_info.aspect_mask, - base_array_layer: image_view_info.base_array_layer, - base_mip_level: image_view_info.base_mip_level, - format: image_view_info.fmt, - mip_level_count: image_view_info.mip_level_count, - sample_count, - target, - } - } - - fn are_compatible(lhs: Option, rhs: Option) -> bool { - // Two attachment references are compatible if they have matching format and sample - // count, or are both VK_ATTACHMENT_UNUSED or the pointer that would contain the - // reference is NULL. - if lhs.is_none() || rhs.is_none() { - return true; - } - - Self::are_identical(lhs.unwrap(), rhs.unwrap()) - } - - fn are_identical(lhs: Self, rhs: Self) -> bool { - lhs.array_layer_count == rhs.array_layer_count - && lhs.base_array_layer == rhs.base_array_layer - && lhs.base_mip_level == rhs.base_mip_level - && lhs.format == rhs.format - && lhs.mip_level_count == rhs.mip_level_count - && lhs.sample_count == rhs.sample_count - && lhs.target == rhs.target - } - - fn image_view_info(self, image_info: ImageInfo) -> ImageViewInfo { - image_info - .to_builder() - .array_layer_count(self.array_layer_count) - .mip_level_count(self.mip_level_count) - .fmt(self.format) - .build() - .default_view_info() - .to_builder() - .aspect_mask(self.aspect_mask) - .base_array_layer(self.base_array_layer) - .base_mip_level(self.base_mip_level) - .build() - } -} - -/// Specifies a color attachment clear value which can be used to initliaze an image. -#[derive(Clone, Copy, Debug)] -pub struct ClearColorValue(pub [f32; 4]); - -impl From<[f32; 3]> for ClearColorValue { - fn from(color: [f32; 3]) -> Self { - [color[0], color[1], color[2], 1.0].into() - } -} - -impl From<[f32; 4]> for ClearColorValue { - fn from(color: [f32; 4]) -> Self { - Self(color) - } -} - -impl From<[u8; 3]> for ClearColorValue { - fn from(color: [u8; 3]) -> Self { - [color[0], color[1], color[2], u8::MAX].into() - } -} - -impl From<[u8; 4]> for ClearColorValue { - fn from(color: [u8; 4]) -> Self { - [ - color[0] as f32 / u8::MAX as f32, - color[1] as f32 / u8::MAX as f32, - color[2] as f32 / u8::MAX as f32, - color[3] as f32 / u8::MAX as f32, - ] - .into() - } -} - -#[derive(Default)] -struct Execution { - accesses: HashMap>, - bindings: BTreeMap)>, - - correlated_view_mask: u32, - depth_stencil: Option, - render_area: Option, - view_mask: u32, - - color_attachments: HashMap, - color_clears: HashMap, - color_loads: HashMap, - color_resolves: HashMap, - color_stores: HashMap, - depth_stencil_attachment: Option, - depth_stencil_clear: Option<(Attachment, vk::ClearDepthStencilValue)>, - depth_stencil_load: Option, - depth_stencil_resolve: Option<( - Attachment, - AttachmentIndex, - Option, - Option, - )>, - depth_stencil_store: Option, - - func: Option, - pipeline: Option, -} - -impl Debug for Execution { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - // The only field missing is func which cannot easily be implemented because it is a - // FnOnce. - f.debug_struct("Execution") - .field("accesses", &self.accesses) - .field("bindings", &self.bindings) - .field("depth_stencil", &self.depth_stencil) - .field("color_attachments", &self.color_attachments) - .field("color_clears", &self.color_clears) - .field("color_loads", &self.color_loads) - .field("color_resolves", &self.color_resolves) - .field("color_stores", &self.color_stores) - .field("depth_stencil_attachment", &self.depth_stencil_attachment) - .field("depth_stencil_clear", &self.depth_stencil_clear) - .field("depth_stencil_load", &self.depth_stencil_load) - .field("depth_stencil_resolve", &self.depth_stencil_resolve) - .field("depth_stencil_store", &self.depth_stencil_store) - .field("pipeline", &self.pipeline) - .finish() - } -} - -struct ExecutionFunction(ExecFn); - -#[derive(Debug)] -enum ExecutionPipeline { - Compute(Arc), - Graphic(Arc), - RayTrace(Arc), -} - -impl ExecutionPipeline { - fn as_graphic(&self) -> Option<&GraphicPipeline> { - if let Self::Graphic(pipeline) = self { - Some(pipeline) - } else { - None - } - } - - fn bind_point(&self) -> vk::PipelineBindPoint { - match self { - ExecutionPipeline::Compute(_) => vk::PipelineBindPoint::COMPUTE, - ExecutionPipeline::Graphic(_) => vk::PipelineBindPoint::GRAPHICS, - ExecutionPipeline::RayTrace(_) => vk::PipelineBindPoint::RAY_TRACING_KHR, - } - } - - fn descriptor_bindings(&self) -> &DescriptorBindingMap { - match self { - ExecutionPipeline::Compute(pipeline) => &pipeline.descriptor_bindings, - ExecutionPipeline::Graphic(pipeline) => &pipeline.descriptor_bindings, - ExecutionPipeline::RayTrace(pipeline) => &pipeline.descriptor_bindings, - } - } - - fn descriptor_info(&self) -> &PipelineDescriptorInfo { - match self { - ExecutionPipeline::Compute(pipeline) => &pipeline.descriptor_info, - ExecutionPipeline::Graphic(pipeline) => &pipeline.descriptor_info, - ExecutionPipeline::RayTrace(pipeline) => &pipeline.descriptor_info, - } - } - - fn layout(&self) -> vk::PipelineLayout { - match self { - ExecutionPipeline::Compute(pipeline) => pipeline.layout, - ExecutionPipeline::Graphic(pipeline) => pipeline.layout, - ExecutionPipeline::RayTrace(pipeline) => pipeline.layout, - } - } - - fn stage(&self) -> vk::PipelineStageFlags { - match self { - ExecutionPipeline::Compute(_) => vk::PipelineStageFlags::COMPUTE_SHADER, - ExecutionPipeline::Graphic(_) => vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT, - ExecutionPipeline::RayTrace(_) => vk::PipelineStageFlags::RAY_TRACING_SHADER_KHR, - } - } -} - -impl Clone for ExecutionPipeline { - fn clone(&self) -> Self { - match self { - Self::Compute(pipeline) => Self::Compute(Arc::clone(pipeline)), - Self::Graphic(pipeline) => Self::Graphic(Arc::clone(pipeline)), - Self::RayTrace(pipeline) => Self::RayTrace(Arc::clone(pipeline)), - } - } -} - -#[derive(Debug)] -struct Pass { - execs: Vec, - name: String, -} - -impl Pass { - fn descriptor_pools_sizes( - &self, - ) -> impl Iterator>> { - self.execs - .iter() - .flat_map(|exec| exec.pipeline.as_ref()) - .map(|pipeline| &pipeline.descriptor_info().pool_sizes) - } -} - -/// A composable graph of render pass operations. -/// -/// `RenderGraph` instances are are intended for one-time use. -/// -/// The design of this code originated with a combination of -/// [`PassBuilder`](https://github.com/EmbarkStudios/kajiya/blob/main/crates/lib/kajiya-rg/src/pass_builder.rs) -/// and -/// [`render_graph.cpp`](https://github.com/Themaister/Granite/blob/master/renderer/render_graph.cpp). -#[derive(Debug)] -pub struct RenderGraph { - bindings: Vec, - passes: Vec, - - /// Set to true (when in debug mode) in order to get a breakpoint hit where you want. - #[cfg(debug_assertions)] - pub debug: bool, -} - -impl RenderGraph { - /// Constructs a new `RenderGraph`. - #[allow(clippy::new_without_default)] - pub fn new() -> Self { - let bindings = vec![]; - let passes = vec![]; - - #[cfg(debug_assertions)] - let debug = false; - - Self { - bindings, - passes, - #[cfg(debug_assertions)] - debug, - } - } - - /// Begins a new pass. - pub fn begin_pass(&mut self, name: impl AsRef) -> PassRef<'_> { - PassRef::new(self, name.as_ref().to_string()) - } - - /// Binds a Vulkan acceleration structure, buffer, or image to this graph. - /// - /// Bound nodes may be used in passes for pipeline and shader operations. - pub fn bind_node<'a, B>(&'a mut self, binding: B) -> >::Result - where - B: Edge, - B: Bind<&'a mut Self, >::Result>, - { - binding.bind(self) - } - - /// Copy an image, potentially performing format conversion. - pub fn blit_image( - &mut self, - src_node: impl Into, - dst_node: impl Into, - filter: vk::Filter, - ) -> &mut Self { - let src_node = src_node.into(); - let dst_node = dst_node.into(); - - let src_info = self.node_info(src_node); - let dst_info = self.node_info(dst_node); - - self.blit_image_region( - src_node, - dst_node, - filter, - vk::ImageBlit { - src_subresource: vk::ImageSubresourceLayers { - aspect_mask: format_aspect_mask(src_info.fmt), - mip_level: 0, - base_array_layer: 0, - layer_count: 1, - }, - src_offsets: [ - vk::Offset3D { x: 0, y: 0, z: 0 }, - vk::Offset3D { - x: src_info.width as _, - y: src_info.height as _, - z: src_info.depth as _, - }, - ], - dst_subresource: vk::ImageSubresourceLayers { - aspect_mask: format_aspect_mask(dst_info.fmt), - mip_level: 0, - base_array_layer: 0, - layer_count: 1, - }, - dst_offsets: [ - vk::Offset3D { x: 0, y: 0, z: 0 }, - vk::Offset3D { - x: dst_info.width as _, - y: dst_info.height as _, - z: dst_info.depth as _, - }, - ], - }, - ) - } - - /// Copy a region of an image, potentially performing format conversion. - pub fn blit_image_region( - &mut self, - src_node: impl Into, - dst_node: impl Into, - filter: vk::Filter, - region: vk::ImageBlit, - ) -> &mut Self { - self.blit_image_regions(src_node, dst_node, filter, [region]) - } - - /// Copy regions of an image, potentially performing format conversion. - #[profiling::function] - pub fn blit_image_regions( - &mut self, - src_node: impl Into, - dst_node: impl Into, - filter: vk::Filter, - regions: impl AsRef<[vk::ImageBlit]> + 'static + Send, - ) -> &mut Self { - let src_node = src_node.into(); - let dst_node = dst_node.into(); - - let mut pass = self.begin_pass("blit image"); - - for region in regions.as_ref() { - pass = pass - .access_node_subrange( - src_node, - AccessType::TransferRead, - image_subresource_range_from_layers(region.src_subresource), - ) - .access_node_subrange( - dst_node, - AccessType::TransferWrite, - image_subresource_range_from_layers(region.dst_subresource), - ); - } - - pass.record_cmd_buf(move |device, cmd_buf, bindings| { - let src_image = bindings[src_node].handle; - let dst_image = bindings[dst_node].handle; - - unsafe { - device.cmd_blit_image( - cmd_buf, - src_image, - vk::ImageLayout::TRANSFER_SRC_OPTIMAL, - dst_image, - vk::ImageLayout::TRANSFER_DST_OPTIMAL, - regions.as_ref(), - filter, - ); - } - }) - .submit_pass() - } - - /// Clear a color image. - pub fn clear_color_image(&mut self, image_node: impl Into) -> &mut Self { - self.clear_color_image_value(image_node, [0, 0, 0, 0]) - } - - /// Clear a color image. - #[profiling::function] - pub fn clear_color_image_value( - &mut self, - image_node: impl Into, - color_value: impl Into, - ) -> &mut Self { - let color_value = color_value.into(); - let image_node = image_node.into(); - let image_info = self.node_info(image_node); - let image_view_info = image_info.default_view_info(); - - self.begin_pass("clear color") - .access_node_subrange(image_node, AccessType::TransferWrite, image_view_info) - .record_cmd_buf(move |device, cmd_buf, bindings| unsafe { - device.cmd_clear_color_image( - cmd_buf, - bindings[image_node].handle, - vk::ImageLayout::TRANSFER_DST_OPTIMAL, - &vk::ClearColorValue { - float32: color_value.0, - }, - &[image_view_info.into()], - ); - }) - .submit_pass() - } - - /// Clears a depth/stencil image. - pub fn clear_depth_stencil_image(&mut self, image_node: impl Into) -> &mut Self { - self.clear_depth_stencil_image_value(image_node, 1.0, 0) - } - - /// Clears a depth/stencil image. - #[profiling::function] - pub fn clear_depth_stencil_image_value( - &mut self, - image_node: impl Into, - depth: f32, - stencil: u32, - ) -> &mut Self { - let image_node = image_node.into(); - let image_info = self.node_info(image_node); - let image_view_info = image_info.default_view_info(); - - self.begin_pass("clear depth/stencil") - .access_node_subrange(image_node, AccessType::TransferWrite, image_view_info) - .record_cmd_buf(move |device, cmd_buf, bindings| unsafe { - device.cmd_clear_depth_stencil_image( - cmd_buf, - bindings[image_node].handle, - vk::ImageLayout::TRANSFER_DST_OPTIMAL, - &vk::ClearDepthStencilValue { depth, stencil }, - &[image_view_info.into()], - ); - }) - .submit_pass() - } - - /// Copy data between buffers - pub fn copy_buffer( - &mut self, - src_node: impl Into, - dst_node: impl Into, - ) -> &mut Self { - let src_node = src_node.into(); - let dst_node = dst_node.into(); - let src_info = self.node_info(src_node); - let dst_info = self.node_info(dst_node); - - self.copy_buffer_region( - src_node, - dst_node, - vk::BufferCopy { - src_offset: 0, - dst_offset: 0, - size: src_info.size.min(dst_info.size), - }, - ) - } - - /// Copy data between buffer regions. - pub fn copy_buffer_region( - &mut self, - src_node: impl Into, - dst_node: impl Into, - region: vk::BufferCopy, - ) -> &mut Self { - self.copy_buffer_regions(src_node, dst_node, [region]) - } - - /// Copy data between buffer regions. - #[profiling::function] - pub fn copy_buffer_regions( - &mut self, - src_node: impl Into, - dst_node: impl Into, - regions: impl AsRef<[vk::BufferCopy]> + 'static + Send, - ) -> &mut Self { - let src_node = src_node.into(); - let dst_node = dst_node.into(); - - #[cfg(debug_assertions)] - let (src_size, dst_size) = (self.node_info(src_node).size, self.node_info(dst_node).size); - - let mut pass = self.begin_pass("copy buffer"); - - for region in regions.as_ref() { - #[cfg(debug_assertions)] - { - assert!( - region.src_offset + region.size <= src_size, - "source range end ({}) exceeds source size ({src_size})", - region.src_offset + region.size - ); - assert!( - region.dst_offset + region.size <= dst_size, - "destination range end ({}) exceeds destination size ({dst_size})", - region.dst_offset + region.size - ); - }; - - pass = pass - .access_node_subrange( - src_node, - AccessType::TransferRead, - region.src_offset..region.src_offset + region.size, - ) - .access_node_subrange( - dst_node, - AccessType::TransferWrite, - region.dst_offset..region.dst_offset + region.size, - ); - } - - pass.record_cmd_buf(move |device, cmd_buf, bindings| { - let src_buf = bindings[src_node].handle; - let dst_buf = bindings[dst_node].handle; - - unsafe { - device.cmd_copy_buffer(cmd_buf, src_buf, dst_buf, regions.as_ref()); - } - }) - .submit_pass() - } - - /// Copy data from a buffer into an image. - pub fn copy_buffer_to_image( - &mut self, - src_node: impl Into, - dst_node: impl Into, - ) -> &mut Self { - let dst_node = dst_node.into(); - let dst_info = self.node_info(dst_node); - - self.copy_buffer_to_image_region( - src_node, - dst_node, - vk::BufferImageCopy { - buffer_offset: 0, - buffer_row_length: dst_info.width, - buffer_image_height: dst_info.height, - image_subresource: vk::ImageSubresourceLayers { - aspect_mask: format_aspect_mask(dst_info.fmt), - mip_level: 0, - base_array_layer: 0, - layer_count: 1, - }, - image_offset: Default::default(), - image_extent: vk::Extent3D { - depth: dst_info.depth, - height: dst_info.height, - width: dst_info.width, - }, - }, - ) - } - - /// Copy data from a buffer into an image. - pub fn copy_buffer_to_image_region( - &mut self, - src_node: impl Into, - dst_node: impl Into, - region: vk::BufferImageCopy, - ) -> &mut Self { - self.copy_buffer_to_image_regions(src_node, dst_node, [region]) - } - - /// Copy data from a buffer into an image. - #[profiling::function] - pub fn copy_buffer_to_image_regions( - &mut self, - src_node: impl Into, - dst_node: impl Into, - regions: impl AsRef<[vk::BufferImageCopy]> + 'static + Send, - ) -> &mut Self { - let src_node = src_node.into(); - let dst_node = dst_node.into(); - let dst_info = self.node_info(dst_node); - - let mut pass = self.begin_pass("copy buffer to image"); - - for region in regions.as_ref() { - let block_bytes_size = format_texel_block_size(dst_info.fmt); - let (block_height, block_width) = format_texel_block_extent(dst_info.fmt); - let data_size = block_bytes_size - * (region.buffer_row_length / block_width) - * (region.buffer_image_height / block_height); - - pass = pass - .access_node_subrange( - src_node, - AccessType::TransferRead, - region.buffer_offset..region.buffer_offset + data_size as vk::DeviceSize, - ) - .access_node_subrange( - dst_node, - AccessType::TransferWrite, - image_subresource_range_from_layers(region.image_subresource), - ); - } - - pass.record_cmd_buf(move |device, cmd_buf, bindings| { - let src_buf = bindings[src_node].handle; - let dst_image = bindings[dst_node].handle; - - unsafe { - device.cmd_copy_buffer_to_image( - cmd_buf, - src_buf, - dst_image, - vk::ImageLayout::TRANSFER_DST_OPTIMAL, - regions.as_ref(), - ); - } - }) - .submit_pass() - } - - /// Copy all layers of a source image to a destination image. - pub fn copy_image( - &mut self, - src_node: impl Into, - dst_node: impl Into, - ) -> &mut Self { - let src_node = src_node.into(); - let src_info = self.node_info(src_node); - - let dst_node = dst_node.into(); - let dst_info = self.node_info(dst_node); - - self.copy_image_region( - src_node, - dst_node, - vk::ImageCopy { - src_subresource: vk::ImageSubresourceLayers { - aspect_mask: format_aspect_mask(src_info.fmt), - mip_level: 0, - base_array_layer: 0, - layer_count: src_info.array_layer_count, - }, - src_offset: vk::Offset3D { x: 0, y: 0, z: 0 }, - dst_subresource: vk::ImageSubresourceLayers { - aspect_mask: format_aspect_mask(dst_info.fmt), - mip_level: 0, - base_array_layer: 0, - layer_count: src_info.array_layer_count, - }, - dst_offset: vk::Offset3D { x: 0, y: 0, z: 0 }, - extent: vk::Extent3D { - depth: src_info.depth.clamp(1, dst_info.depth), - height: src_info.height.clamp(1, dst_info.height), - width: src_info.width.min(dst_info.width), - }, - }, - ) - } - - /// Copy data between images. - pub fn copy_image_region( - &mut self, - src_node: impl Into, - dst_node: impl Into, - region: vk::ImageCopy, - ) -> &mut Self { - self.copy_image_regions(src_node, dst_node, [region]) - } - - /// Copy data between images. - #[profiling::function] - pub fn copy_image_regions( - &mut self, - src_node: impl Into, - dst_node: impl Into, - regions: impl AsRef<[vk::ImageCopy]> + 'static + Send, - ) -> &mut Self { - let src_node = src_node.into(); - let dst_node = dst_node.into(); - - let mut pass = self.begin_pass("copy image"); - - for region in regions.as_ref() { - pass = pass - .access_node_subrange( - src_node, - AccessType::TransferRead, - image_subresource_range_from_layers(region.src_subresource), - ) - .access_node_subrange( - dst_node, - AccessType::TransferWrite, - image_subresource_range_from_layers(region.dst_subresource), - ); - } - - pass.record_cmd_buf(move |device, cmd_buf, bindings| { - let src_image = bindings[src_node].handle; - let dst_image = bindings[dst_node].handle; - - unsafe { - device.cmd_copy_image( - cmd_buf, - src_image, - vk::ImageLayout::TRANSFER_SRC_OPTIMAL, - dst_image, - vk::ImageLayout::TRANSFER_DST_OPTIMAL, - regions.as_ref(), - ); - } - }) - .submit_pass() - } - - /// Copy image data into a buffer. - pub fn copy_image_to_buffer( - &mut self, - src_node: impl Into, - dst_node: impl Into, - ) -> &mut Self { - let src_node = src_node.into(); - let dst_node = dst_node.into(); - - let src_info = self.node_info(src_node); - - self.copy_image_to_buffer_region( - src_node, - dst_node, - vk::BufferImageCopy { - buffer_offset: 0, - buffer_row_length: src_info.width, - buffer_image_height: src_info.height, - image_subresource: vk::ImageSubresourceLayers { - aspect_mask: format_aspect_mask(src_info.fmt), - mip_level: 0, - base_array_layer: 0, - layer_count: 1, - }, - image_offset: Default::default(), - image_extent: vk::Extent3D { - depth: src_info.depth, - height: src_info.height, - width: src_info.width, - }, - }, - ) - } - - /// Copy image data into a buffer. - pub fn copy_image_to_buffer_region( - &mut self, - src_node: impl Into, - dst_node: impl Into, - region: vk::BufferImageCopy, - ) -> &mut Self { - self.copy_image_to_buffer_regions(src_node, dst_node, [region]) - } - - /// Copy image data into a buffer. - #[profiling::function] - pub fn copy_image_to_buffer_regions( - &mut self, - src_node: impl Into, - dst_node: impl Into, - regions: impl AsRef<[vk::BufferImageCopy]> + 'static + Send, - ) -> &mut Self { - let src_node = src_node.into(); - let src_info = self.node_info(src_node); - let dst_node = dst_node.into(); - - let mut pass = self.begin_pass("copy image to buffer"); - - for region in regions.as_ref() { - let block_bytes_size = format_texel_block_size(src_info.fmt); - let (block_height, block_width) = format_texel_block_extent(src_info.fmt); - let data_size = block_bytes_size - * (region.buffer_row_length / block_width) - * (region.buffer_image_height / block_height); - - pass = pass - .access_node_subrange( - src_node, - AccessType::TransferRead, - image_subresource_range_from_layers(region.image_subresource), - ) - .access_node_subrange( - dst_node, - AccessType::TransferWrite, - region.buffer_offset..region.buffer_offset + data_size as vk::DeviceSize, - ); - } - - pass.record_cmd_buf(move |device, cmd_buf, bindings| { - let src_image = bindings[src_node].handle; - let dst_buf = bindings[dst_node].handle; - - unsafe { - device.cmd_copy_image_to_buffer( - cmd_buf, - src_image, - vk::ImageLayout::TRANSFER_SRC_OPTIMAL, - dst_buf, - regions.as_ref(), - ); - } - }) - .submit_pass() - } - - /// Fill a region of a buffer with a fixed value. - pub fn fill_buffer(&mut self, buffer_node: impl Into, data: u32) -> &mut Self { - let buffer_node = buffer_node.into(); - - let buffer_info = self.node_info(buffer_node); - - self.fill_buffer_region(buffer_node, data, 0..buffer_info.size) - } - - /// Fill a region of a buffer with a fixed value. - #[profiling::function] - pub fn fill_buffer_region( - &mut self, - buffer_node: impl Into, - data: u32, - region: Range, - ) -> &mut Self { - let buffer_node = buffer_node.into(); - - self.begin_pass("fill buffer") - .access_node_subrange(buffer_node, AccessType::TransferWrite, region.clone()) - .record_cmd_buf(move |device, cmd_buf, bindings| { - let buffer = bindings[buffer_node].handle; - - unsafe { - device.cmd_fill_buffer( - cmd_buf, - buffer, - region.start, - region.end - region.start, - data, - ); - } - }) - .submit_pass() - } - - /// Returns the index of the first pass which accesses a given node - #[profiling::function] - fn first_node_access_pass_index(&self, node: impl Node) -> Option { - let node_idx = node.index(); - - for (pass_idx, pass) in self.passes.iter().enumerate() { - for exec in pass.execs.iter() { - if exec.accesses.contains_key(&node_idx) { - return Some(pass_idx); - } - } - } - - None - } - - /// Returns the device address of a buffer node. - /// - /// # Panics - /// - /// Panics if the buffer is not currently bound or was not created with the - /// `SHADER_DEVICE_ADDRESS` usage flag. - pub fn node_device_address(&self, node: impl Into) -> vk::DeviceAddress { - let node: AnyBufferNode = node.into(); - let buffer = self.bindings[node.index()].as_driver_buffer().unwrap(); - - Buffer::device_address(buffer) - } - - /// Returns information used to crate a node. - pub fn node_info(&self, node: N) -> ::Info - where - N: Information, - { - node.get(self) - } - - /// Finalizes the graph and provides an object with functions for submitting the resulting - /// commands. - #[profiling::function] - pub fn resolve(mut self) -> Resolver { - // The final execution of each pass has no function - for pass in &mut self.passes { - pass.execs.pop(); - } - - Resolver::new(self) - } - - /// Removes a node from this graph. - /// - /// Future access to `node` on this graph will return invalid results. - pub fn unbind_node(&mut self, node: N) -> >::Result - where - N: Edge, - N: Unbind>::Result>, - { - node.unbind(self) - } - - /// Note: `data` must not exceed 65536 bytes. - pub fn update_buffer( - &mut self, - buffer_node: impl Into, - data: impl AsRef<[u8]> + 'static + Send, - ) -> &mut Self { - self.update_buffer_offset(buffer_node, 0, data) - } - - /// Note: `data` must not exceed 65536 bytes. - #[profiling::function] - pub fn update_buffer_offset( - &mut self, - buffer_node: impl Into, - offset: vk::DeviceSize, - data: impl AsRef<[u8]> + 'static + Send, - ) -> &mut Self { - let buffer_node = buffer_node.into(); - let data_end = offset + data.as_ref().len() as vk::DeviceSize; - - #[cfg(debug_assertions)] - { - let buffer_info = self.node_info(buffer_node); - - assert!( - data_end <= buffer_info.size, - "data range end ({data_end}) exceeds buffer size ({})", - buffer_info.size - ); - } - - self.begin_pass("update buffer") - .access_node_subrange(buffer_node, AccessType::TransferWrite, offset..data_end) - .record_cmd_buf(move |device, cmd_buf, bindings| { - let buffer = bindings[buffer_node].handle; - - unsafe { - device.cmd_update_buffer(cmd_buf, buffer, offset, data.as_ref()); - } - }) - .submit_pass() - } -} diff --git a/src/graph/info.rs b/src/info.rs similarity index 100% rename from src/graph/info.rs rename to src/info.rs diff --git a/src/lib.rs b/src/lib.rs index eca67582..94ce6536 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,19 +1,16 @@ /*! -Provides a high performance [Vulkan 1.2](https://www.vulkan.org/) driver using smart pointers. -Supports windowed and headless rendering with a fully-featured render graph and resource pooling -types. +This crate provides a high performance [Vulkan](https://www.vulkan.org/) graphics driver using smart +pointers. -This crate allows graphics programmers to focus on acceleration structure, buffer, and image -resources and the shader pipelines they are accessed from. There are no restrictions or opinions -placed on the types of graphics algorithms you might create. Some implementations of common graphics -patterns are provided in the `contrib` directory. +Driver resources (_buffers, images, and acceleration structures_) and shader pipelines are used with +the provided graph structure to compose any type of graphics algorithm. Some implementations of +common graphics patterns are provided in the `contrib` directory. # Getting Sarted -Typical usage involves creating an operating system window event loop, and rendering frames -using the provided [`FrameContext`] closure. The [`EventLoop`] builder handles creating an instance -of the [`Device`] driver, however you may construct one manually for headless rendering. +Typical usage begins by displaying a winit [`Window`()]. The provided example code displays a window +and creates Vulkan [`Device`] driver automatically: ```no_run use vk_graph_window::{Window, WindowError}; @@ -31,6 +28,18 @@ fn main() -> Result<(), WindowError> { } ``` +## _Optional_: Headless Rendering + +```no_run +use vk_graph::driver::{device::{Device, DeviceInfo}, DriverError}; + +fn main() -> Result<(), DriverError> { + let device = Device::create_headless(DeviceInfo::default())? + + // Do stuff... +} +``` + # Resources and Pipelines All resources and pipelines, as well as the driver itself, use shared reference tracking to keep @@ -148,7 +157,7 @@ it as a node. Bound nodes may only be used with the graphs they were bound to. N # use vk_graph::driver::device::{Device, DeviceInfo}; # use vk_graph::driver::buffer::{Buffer, BufferInfo}; # use vk_graph::driver::image::{Image, ImageInfo}; -# use vk_graph::graph::RenderGraph; +# use vk_graph::RenderGraph; # use vk_graph::pool::{Pool}; # use vk_graph::pool::lazy::{LazyPool}; # fn main() -> Result<(), DriverError> { @@ -200,7 +209,7 @@ Example: # use vk_graph::driver::device::{Device, DeviceInfo}; # use vk_graph::driver::buffer::{Buffer, BufferInfo}; # use vk_graph::driver::image::{Image, ImageInfo}; -# use vk_graph::graph::RenderGraph; +# use vk_graph::RenderGraph; # use vk_graph::pool::{Pool}; # use vk_graph::pool::lazy::{LazyPool}; # fn main() -> Result<(), DriverError> { @@ -242,7 +251,7 @@ Pipeline instances may be bound to a [`PassRef`] in order to execute the associa # use vk_graph::driver::device::{Device, DeviceInfo}; # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; # use vk_graph::driver::shader::{Shader}; -# use vk_graph::graph::RenderGraph; +# use vk_graph::RenderGraph; # fn main() -> Result<(), DriverError> { # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); # let my_shader_code = [0u8; 1]; @@ -326,5 +335,1035 @@ layout. pub mod display; pub mod driver; -pub mod graph; +pub mod node; +pub mod pass_ref; pub mod pool; + +mod binding; +mod edge; +mod info; +mod resolver; +mod swapchain; + +pub use self::{ + binding::{Bind, Unbind}, + resolver::Resolver, +}; + +use { + self::{ + binding::Binding, + edge::Edge, + info::Information, + node::Node, + node::{ + AccelerationStructureLeaseNode, AccelerationStructureNode, + AnyAccelerationStructureNode, AnyBufferNode, AnyImageNode, BufferLeaseNode, BufferNode, + ImageLeaseNode, ImageNode, SwapchainImageNode, + }, + pass_ref::{AttachmentIndex, Bindings, Descriptor, PassRef, SubresourceAccess, ViewType}, + }, + crate::driver::{ + DescriptorBindingMap, + buffer::Buffer, + compute::ComputePipeline, + device::Device, + format_aspect_mask, format_texel_block_extent, format_texel_block_size, + graphic::{DepthStencilMode, GraphicPipeline}, + image::{ImageInfo, ImageViewInfo, SampleCount}, + image_subresource_range_from_layers, + ray_trace::RayTracePipeline, + render_pass::ResolveMode, + shader::PipelineDescriptorInfo, + }, + ash::vk, + std::{ + cmp::Ord, + collections::{BTreeMap, HashMap}, + fmt::{Debug, Formatter}, + ops::Range, + sync::Arc, + }, + vk_sync::AccessType, +}; + +type ExecFn = Box) + Send>; +type NodeIndex = usize; + +#[derive(Clone, Copy, Debug)] +struct Area { + height: u32, + width: u32, + x: i32, + y: i32, +} + +#[derive(Clone, Copy, Debug)] +struct Attachment { + array_layer_count: u32, + aspect_mask: vk::ImageAspectFlags, + base_array_layer: u32, + base_mip_level: u32, + format: vk::Format, + mip_level_count: u32, + sample_count: SampleCount, + target: NodeIndex, +} + +impl Attachment { + fn new(image_view_info: ImageViewInfo, sample_count: SampleCount, target: NodeIndex) -> Self { + Self { + array_layer_count: image_view_info.array_layer_count, + aspect_mask: image_view_info.aspect_mask, + base_array_layer: image_view_info.base_array_layer, + base_mip_level: image_view_info.base_mip_level, + format: image_view_info.fmt, + mip_level_count: image_view_info.mip_level_count, + sample_count, + target, + } + } + + fn are_compatible(lhs: Option, rhs: Option) -> bool { + // Two attachment references are compatible if they have matching format and sample + // count, or are both VK_ATTACHMENT_UNUSED or the pointer that would contain the + // reference is NULL. + if lhs.is_none() || rhs.is_none() { + return true; + } + + Self::are_identical(lhs.unwrap(), rhs.unwrap()) + } + + fn are_identical(lhs: Self, rhs: Self) -> bool { + lhs.array_layer_count == rhs.array_layer_count + && lhs.base_array_layer == rhs.base_array_layer + && lhs.base_mip_level == rhs.base_mip_level + && lhs.format == rhs.format + && lhs.mip_level_count == rhs.mip_level_count + && lhs.sample_count == rhs.sample_count + && lhs.target == rhs.target + } + + fn image_view_info(self, image_info: ImageInfo) -> ImageViewInfo { + image_info + .to_builder() + .array_layer_count(self.array_layer_count) + .mip_level_count(self.mip_level_count) + .fmt(self.format) + .build() + .default_view_info() + .to_builder() + .aspect_mask(self.aspect_mask) + .base_array_layer(self.base_array_layer) + .base_mip_level(self.base_mip_level) + .build() + } +} + +/// Specifies a color attachment clear value which can be used to initliaze an image. +#[derive(Clone, Copy, Debug)] +pub struct ClearColorValue(pub [f32; 4]); + +impl From<[f32; 3]> for ClearColorValue { + fn from(color: [f32; 3]) -> Self { + [color[0], color[1], color[2], 1.0].into() + } +} + +impl From<[f32; 4]> for ClearColorValue { + fn from(color: [f32; 4]) -> Self { + Self(color) + } +} + +impl From<[u8; 3]> for ClearColorValue { + fn from(color: [u8; 3]) -> Self { + [color[0], color[1], color[2], u8::MAX].into() + } +} + +impl From<[u8; 4]> for ClearColorValue { + fn from(color: [u8; 4]) -> Self { + [ + color[0] as f32 / u8::MAX as f32, + color[1] as f32 / u8::MAX as f32, + color[2] as f32 / u8::MAX as f32, + color[3] as f32 / u8::MAX as f32, + ] + .into() + } +} + +#[derive(Default)] +struct Execution { + accesses: HashMap>, + bindings: BTreeMap)>, + + correlated_view_mask: u32, + depth_stencil: Option, + render_area: Option, + view_mask: u32, + + color_attachments: HashMap, + color_clears: HashMap, + color_loads: HashMap, + color_resolves: HashMap, + color_stores: HashMap, + depth_stencil_attachment: Option, + depth_stencil_clear: Option<(Attachment, vk::ClearDepthStencilValue)>, + depth_stencil_load: Option, + depth_stencil_resolve: Option<( + Attachment, + AttachmentIndex, + Option, + Option, + )>, + depth_stencil_store: Option, + + func: Option, + pipeline: Option, +} + +impl Debug for Execution { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + // The only field missing is func which cannot easily be implemented because it is a + // FnOnce. + f.debug_struct("Execution") + .field("accesses", &self.accesses) + .field("bindings", &self.bindings) + .field("depth_stencil", &self.depth_stencil) + .field("color_attachments", &self.color_attachments) + .field("color_clears", &self.color_clears) + .field("color_loads", &self.color_loads) + .field("color_resolves", &self.color_resolves) + .field("color_stores", &self.color_stores) + .field("depth_stencil_attachment", &self.depth_stencil_attachment) + .field("depth_stencil_clear", &self.depth_stencil_clear) + .field("depth_stencil_load", &self.depth_stencil_load) + .field("depth_stencil_resolve", &self.depth_stencil_resolve) + .field("depth_stencil_store", &self.depth_stencil_store) + .field("pipeline", &self.pipeline) + .finish() + } +} + +struct ExecutionFunction(ExecFn); + +#[derive(Debug)] +enum ExecutionPipeline { + Compute(Arc), + Graphic(Arc), + RayTrace(Arc), +} + +impl ExecutionPipeline { + fn as_graphic(&self) -> Option<&GraphicPipeline> { + if let Self::Graphic(pipeline) = self { + Some(pipeline) + } else { + None + } + } + + fn bind_point(&self) -> vk::PipelineBindPoint { + match self { + ExecutionPipeline::Compute(_) => vk::PipelineBindPoint::COMPUTE, + ExecutionPipeline::Graphic(_) => vk::PipelineBindPoint::GRAPHICS, + ExecutionPipeline::RayTrace(_) => vk::PipelineBindPoint::RAY_TRACING_KHR, + } + } + + fn descriptor_bindings(&self) -> &DescriptorBindingMap { + match self { + ExecutionPipeline::Compute(pipeline) => &pipeline.descriptor_bindings, + ExecutionPipeline::Graphic(pipeline) => &pipeline.descriptor_bindings, + ExecutionPipeline::RayTrace(pipeline) => &pipeline.descriptor_bindings, + } + } + + fn descriptor_info(&self) -> &PipelineDescriptorInfo { + match self { + ExecutionPipeline::Compute(pipeline) => &pipeline.descriptor_info, + ExecutionPipeline::Graphic(pipeline) => &pipeline.descriptor_info, + ExecutionPipeline::RayTrace(pipeline) => &pipeline.descriptor_info, + } + } + + fn layout(&self) -> vk::PipelineLayout { + match self { + ExecutionPipeline::Compute(pipeline) => pipeline.layout, + ExecutionPipeline::Graphic(pipeline) => pipeline.layout, + ExecutionPipeline::RayTrace(pipeline) => pipeline.layout, + } + } + + fn stage(&self) -> vk::PipelineStageFlags { + match self { + ExecutionPipeline::Compute(_) => vk::PipelineStageFlags::COMPUTE_SHADER, + ExecutionPipeline::Graphic(_) => vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT, + ExecutionPipeline::RayTrace(_) => vk::PipelineStageFlags::RAY_TRACING_SHADER_KHR, + } + } +} + +impl Clone for ExecutionPipeline { + fn clone(&self) -> Self { + match self { + Self::Compute(pipeline) => Self::Compute(Arc::clone(pipeline)), + Self::Graphic(pipeline) => Self::Graphic(Arc::clone(pipeline)), + Self::RayTrace(pipeline) => Self::RayTrace(Arc::clone(pipeline)), + } + } +} + +#[derive(Debug)] +struct Pass { + execs: Vec, + name: String, +} + +impl Pass { + fn descriptor_pools_sizes( + &self, + ) -> impl Iterator>> { + self.execs + .iter() + .flat_map(|exec| exec.pipeline.as_ref()) + .map(|pipeline| &pipeline.descriptor_info().pool_sizes) + } +} + +/// A composable graph of render pass operations. +/// +/// `RenderGraph` instances are are intended for one-time use. +/// +/// The design of this code originated with a combination of +/// [`PassBuilder`](https://github.com/EmbarkStudios/kajiya/blob/main/crates/lib/kajiya-rg/src/pass_builder.rs) +/// and +/// [`render_graph.cpp`](https://github.com/Themaister/Granite/blob/master/renderer/render_graph.cpp). +#[derive(Debug)] +pub struct RenderGraph { + bindings: Vec, + passes: Vec, + + /// Set to true (when in debug mode) in order to get a breakpoint hit where you want. + #[cfg(debug_assertions)] + pub debug: bool, +} + +impl RenderGraph { + /// Constructs a new `RenderGraph`. + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + let bindings = vec![]; + let passes = vec![]; + + #[cfg(debug_assertions)] + let debug = false; + + Self { + bindings, + passes, + #[cfg(debug_assertions)] + debug, + } + } + + /// Begins a new pass. + pub fn begin_pass(&mut self, name: impl AsRef) -> PassRef<'_> { + PassRef::new(self, name.as_ref().to_string()) + } + + /// Binds a Vulkan acceleration structure, buffer, or image to this graph. + /// + /// Bound nodes may be used in passes for pipeline and shader operations. + pub fn bind_node<'a, B>(&'a mut self, binding: B) -> >::Result + where + B: Edge, + B: Bind<&'a mut Self, >::Result>, + { + binding.bind(self) + } + + /// Copy an image, potentially performing format conversion. + pub fn blit_image( + &mut self, + src_node: impl Into, + dst_node: impl Into, + filter: vk::Filter, + ) -> &mut Self { + let src_node = src_node.into(); + let dst_node = dst_node.into(); + + let src_info = self.node_info(src_node); + let dst_info = self.node_info(dst_node); + + self.blit_image_region( + src_node, + dst_node, + filter, + vk::ImageBlit { + src_subresource: vk::ImageSubresourceLayers { + aspect_mask: format_aspect_mask(src_info.fmt), + mip_level: 0, + base_array_layer: 0, + layer_count: 1, + }, + src_offsets: [ + vk::Offset3D { x: 0, y: 0, z: 0 }, + vk::Offset3D { + x: src_info.width as _, + y: src_info.height as _, + z: src_info.depth as _, + }, + ], + dst_subresource: vk::ImageSubresourceLayers { + aspect_mask: format_aspect_mask(dst_info.fmt), + mip_level: 0, + base_array_layer: 0, + layer_count: 1, + }, + dst_offsets: [ + vk::Offset3D { x: 0, y: 0, z: 0 }, + vk::Offset3D { + x: dst_info.width as _, + y: dst_info.height as _, + z: dst_info.depth as _, + }, + ], + }, + ) + } + + /// Copy a region of an image, potentially performing format conversion. + pub fn blit_image_region( + &mut self, + src_node: impl Into, + dst_node: impl Into, + filter: vk::Filter, + region: vk::ImageBlit, + ) -> &mut Self { + self.blit_image_regions(src_node, dst_node, filter, [region]) + } + + /// Copy regions of an image, potentially performing format conversion. + #[profiling::function] + pub fn blit_image_regions( + &mut self, + src_node: impl Into, + dst_node: impl Into, + filter: vk::Filter, + regions: impl AsRef<[vk::ImageBlit]> + 'static + Send, + ) -> &mut Self { + let src_node = src_node.into(); + let dst_node = dst_node.into(); + + let mut pass = self.begin_pass("blit image"); + + for region in regions.as_ref() { + pass = pass + .access_node_subrange( + src_node, + AccessType::TransferRead, + image_subresource_range_from_layers(region.src_subresource), + ) + .access_node_subrange( + dst_node, + AccessType::TransferWrite, + image_subresource_range_from_layers(region.dst_subresource), + ); + } + + pass.record_cmd_buf(move |device, cmd_buf, bindings| { + let src_image = bindings[src_node].handle; + let dst_image = bindings[dst_node].handle; + + unsafe { + device.cmd_blit_image( + cmd_buf, + src_image, + vk::ImageLayout::TRANSFER_SRC_OPTIMAL, + dst_image, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + regions.as_ref(), + filter, + ); + } + }) + .submit_pass() + } + + /// Clear a color image. + pub fn clear_color_image(&mut self, image_node: impl Into) -> &mut Self { + self.clear_color_image_value(image_node, [0, 0, 0, 0]) + } + + /// Clear a color image. + #[profiling::function] + pub fn clear_color_image_value( + &mut self, + image_node: impl Into, + color_value: impl Into, + ) -> &mut Self { + let color_value = color_value.into(); + let image_node = image_node.into(); + let image_info = self.node_info(image_node); + let image_view_info = image_info.default_view_info(); + + self.begin_pass("clear color") + .access_node_subrange(image_node, AccessType::TransferWrite, image_view_info) + .record_cmd_buf(move |device, cmd_buf, bindings| unsafe { + device.cmd_clear_color_image( + cmd_buf, + bindings[image_node].handle, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + &vk::ClearColorValue { + float32: color_value.0, + }, + &[image_view_info.into()], + ); + }) + .submit_pass() + } + + /// Clears a depth/stencil image. + pub fn clear_depth_stencil_image(&mut self, image_node: impl Into) -> &mut Self { + self.clear_depth_stencil_image_value(image_node, 1.0, 0) + } + + /// Clears a depth/stencil image. + #[profiling::function] + pub fn clear_depth_stencil_image_value( + &mut self, + image_node: impl Into, + depth: f32, + stencil: u32, + ) -> &mut Self { + let image_node = image_node.into(); + let image_info = self.node_info(image_node); + let image_view_info = image_info.default_view_info(); + + self.begin_pass("clear depth/stencil") + .access_node_subrange(image_node, AccessType::TransferWrite, image_view_info) + .record_cmd_buf(move |device, cmd_buf, bindings| unsafe { + device.cmd_clear_depth_stencil_image( + cmd_buf, + bindings[image_node].handle, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + &vk::ClearDepthStencilValue { depth, stencil }, + &[image_view_info.into()], + ); + }) + .submit_pass() + } + + /// Copy data between buffers + pub fn copy_buffer( + &mut self, + src_node: impl Into, + dst_node: impl Into, + ) -> &mut Self { + let src_node = src_node.into(); + let dst_node = dst_node.into(); + let src_info = self.node_info(src_node); + let dst_info = self.node_info(dst_node); + + self.copy_buffer_region( + src_node, + dst_node, + vk::BufferCopy { + src_offset: 0, + dst_offset: 0, + size: src_info.size.min(dst_info.size), + }, + ) + } + + /// Copy data between buffer regions. + pub fn copy_buffer_region( + &mut self, + src_node: impl Into, + dst_node: impl Into, + region: vk::BufferCopy, + ) -> &mut Self { + self.copy_buffer_regions(src_node, dst_node, [region]) + } + + /// Copy data between buffer regions. + #[profiling::function] + pub fn copy_buffer_regions( + &mut self, + src_node: impl Into, + dst_node: impl Into, + regions: impl AsRef<[vk::BufferCopy]> + 'static + Send, + ) -> &mut Self { + let src_node = src_node.into(); + let dst_node = dst_node.into(); + + #[cfg(debug_assertions)] + let (src_size, dst_size) = (self.node_info(src_node).size, self.node_info(dst_node).size); + + let mut pass = self.begin_pass("copy buffer"); + + for region in regions.as_ref() { + #[cfg(debug_assertions)] + { + assert!( + region.src_offset + region.size <= src_size, + "source range end ({}) exceeds source size ({src_size})", + region.src_offset + region.size + ); + assert!( + region.dst_offset + region.size <= dst_size, + "destination range end ({}) exceeds destination size ({dst_size})", + region.dst_offset + region.size + ); + }; + + pass = pass + .access_node_subrange( + src_node, + AccessType::TransferRead, + region.src_offset..region.src_offset + region.size, + ) + .access_node_subrange( + dst_node, + AccessType::TransferWrite, + region.dst_offset..region.dst_offset + region.size, + ); + } + + pass.record_cmd_buf(move |device, cmd_buf, bindings| { + let src_buf = bindings[src_node].handle; + let dst_buf = bindings[dst_node].handle; + + unsafe { + device.cmd_copy_buffer(cmd_buf, src_buf, dst_buf, regions.as_ref()); + } + }) + .submit_pass() + } + + /// Copy data from a buffer into an image. + pub fn copy_buffer_to_image( + &mut self, + src_node: impl Into, + dst_node: impl Into, + ) -> &mut Self { + let dst_node = dst_node.into(); + let dst_info = self.node_info(dst_node); + + self.copy_buffer_to_image_region( + src_node, + dst_node, + vk::BufferImageCopy { + buffer_offset: 0, + buffer_row_length: dst_info.width, + buffer_image_height: dst_info.height, + image_subresource: vk::ImageSubresourceLayers { + aspect_mask: format_aspect_mask(dst_info.fmt), + mip_level: 0, + base_array_layer: 0, + layer_count: 1, + }, + image_offset: Default::default(), + image_extent: vk::Extent3D { + depth: dst_info.depth, + height: dst_info.height, + width: dst_info.width, + }, + }, + ) + } + + /// Copy data from a buffer into an image. + pub fn copy_buffer_to_image_region( + &mut self, + src_node: impl Into, + dst_node: impl Into, + region: vk::BufferImageCopy, + ) -> &mut Self { + self.copy_buffer_to_image_regions(src_node, dst_node, [region]) + } + + /// Copy data from a buffer into an image. + #[profiling::function] + pub fn copy_buffer_to_image_regions( + &mut self, + src_node: impl Into, + dst_node: impl Into, + regions: impl AsRef<[vk::BufferImageCopy]> + 'static + Send, + ) -> &mut Self { + let src_node = src_node.into(); + let dst_node = dst_node.into(); + let dst_info = self.node_info(dst_node); + + let mut pass = self.begin_pass("copy buffer to image"); + + for region in regions.as_ref() { + let block_bytes_size = format_texel_block_size(dst_info.fmt); + let (block_height, block_width) = format_texel_block_extent(dst_info.fmt); + let data_size = block_bytes_size + * (region.buffer_row_length / block_width) + * (region.buffer_image_height / block_height); + + pass = pass + .access_node_subrange( + src_node, + AccessType::TransferRead, + region.buffer_offset..region.buffer_offset + data_size as vk::DeviceSize, + ) + .access_node_subrange( + dst_node, + AccessType::TransferWrite, + image_subresource_range_from_layers(region.image_subresource), + ); + } + + pass.record_cmd_buf(move |device, cmd_buf, bindings| { + let src_buf = bindings[src_node].handle; + let dst_image = bindings[dst_node].handle; + + unsafe { + device.cmd_copy_buffer_to_image( + cmd_buf, + src_buf, + dst_image, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + regions.as_ref(), + ); + } + }) + .submit_pass() + } + + /// Copy all layers of a source image to a destination image. + pub fn copy_image( + &mut self, + src_node: impl Into, + dst_node: impl Into, + ) -> &mut Self { + let src_node = src_node.into(); + let src_info = self.node_info(src_node); + + let dst_node = dst_node.into(); + let dst_info = self.node_info(dst_node); + + self.copy_image_region( + src_node, + dst_node, + vk::ImageCopy { + src_subresource: vk::ImageSubresourceLayers { + aspect_mask: format_aspect_mask(src_info.fmt), + mip_level: 0, + base_array_layer: 0, + layer_count: src_info.array_layer_count, + }, + src_offset: vk::Offset3D { x: 0, y: 0, z: 0 }, + dst_subresource: vk::ImageSubresourceLayers { + aspect_mask: format_aspect_mask(dst_info.fmt), + mip_level: 0, + base_array_layer: 0, + layer_count: src_info.array_layer_count, + }, + dst_offset: vk::Offset3D { x: 0, y: 0, z: 0 }, + extent: vk::Extent3D { + depth: src_info.depth.clamp(1, dst_info.depth), + height: src_info.height.clamp(1, dst_info.height), + width: src_info.width.min(dst_info.width), + }, + }, + ) + } + + /// Copy data between images. + pub fn copy_image_region( + &mut self, + src_node: impl Into, + dst_node: impl Into, + region: vk::ImageCopy, + ) -> &mut Self { + self.copy_image_regions(src_node, dst_node, [region]) + } + + /// Copy data between images. + #[profiling::function] + pub fn copy_image_regions( + &mut self, + src_node: impl Into, + dst_node: impl Into, + regions: impl AsRef<[vk::ImageCopy]> + 'static + Send, + ) -> &mut Self { + let src_node = src_node.into(); + let dst_node = dst_node.into(); + + let mut pass = self.begin_pass("copy image"); + + for region in regions.as_ref() { + pass = pass + .access_node_subrange( + src_node, + AccessType::TransferRead, + image_subresource_range_from_layers(region.src_subresource), + ) + .access_node_subrange( + dst_node, + AccessType::TransferWrite, + image_subresource_range_from_layers(region.dst_subresource), + ); + } + + pass.record_cmd_buf(move |device, cmd_buf, bindings| { + let src_image = bindings[src_node].handle; + let dst_image = bindings[dst_node].handle; + + unsafe { + device.cmd_copy_image( + cmd_buf, + src_image, + vk::ImageLayout::TRANSFER_SRC_OPTIMAL, + dst_image, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + regions.as_ref(), + ); + } + }) + .submit_pass() + } + + /// Copy image data into a buffer. + pub fn copy_image_to_buffer( + &mut self, + src_node: impl Into, + dst_node: impl Into, + ) -> &mut Self { + let src_node = src_node.into(); + let dst_node = dst_node.into(); + + let src_info = self.node_info(src_node); + + self.copy_image_to_buffer_region( + src_node, + dst_node, + vk::BufferImageCopy { + buffer_offset: 0, + buffer_row_length: src_info.width, + buffer_image_height: src_info.height, + image_subresource: vk::ImageSubresourceLayers { + aspect_mask: format_aspect_mask(src_info.fmt), + mip_level: 0, + base_array_layer: 0, + layer_count: 1, + }, + image_offset: Default::default(), + image_extent: vk::Extent3D { + depth: src_info.depth, + height: src_info.height, + width: src_info.width, + }, + }, + ) + } + + /// Copy image data into a buffer. + pub fn copy_image_to_buffer_region( + &mut self, + src_node: impl Into, + dst_node: impl Into, + region: vk::BufferImageCopy, + ) -> &mut Self { + self.copy_image_to_buffer_regions(src_node, dst_node, [region]) + } + + /// Copy image data into a buffer. + #[profiling::function] + pub fn copy_image_to_buffer_regions( + &mut self, + src_node: impl Into, + dst_node: impl Into, + regions: impl AsRef<[vk::BufferImageCopy]> + 'static + Send, + ) -> &mut Self { + let src_node = src_node.into(); + let src_info = self.node_info(src_node); + let dst_node = dst_node.into(); + + let mut pass = self.begin_pass("copy image to buffer"); + + for region in regions.as_ref() { + let block_bytes_size = format_texel_block_size(src_info.fmt); + let (block_height, block_width) = format_texel_block_extent(src_info.fmt); + let data_size = block_bytes_size + * (region.buffer_row_length / block_width) + * (region.buffer_image_height / block_height); + + pass = pass + .access_node_subrange( + src_node, + AccessType::TransferRead, + image_subresource_range_from_layers(region.image_subresource), + ) + .access_node_subrange( + dst_node, + AccessType::TransferWrite, + region.buffer_offset..region.buffer_offset + data_size as vk::DeviceSize, + ); + } + + pass.record_cmd_buf(move |device, cmd_buf, bindings| { + let src_image = bindings[src_node].handle; + let dst_buf = bindings[dst_node].handle; + + unsafe { + device.cmd_copy_image_to_buffer( + cmd_buf, + src_image, + vk::ImageLayout::TRANSFER_SRC_OPTIMAL, + dst_buf, + regions.as_ref(), + ); + } + }) + .submit_pass() + } + + /// Fill a region of a buffer with a fixed value. + pub fn fill_buffer(&mut self, buffer_node: impl Into, data: u32) -> &mut Self { + let buffer_node = buffer_node.into(); + + let buffer_info = self.node_info(buffer_node); + + self.fill_buffer_region(buffer_node, data, 0..buffer_info.size) + } + + /// Fill a region of a buffer with a fixed value. + #[profiling::function] + pub fn fill_buffer_region( + &mut self, + buffer_node: impl Into, + data: u32, + region: Range, + ) -> &mut Self { + let buffer_node = buffer_node.into(); + + self.begin_pass("fill buffer") + .access_node_subrange(buffer_node, AccessType::TransferWrite, region.clone()) + .record_cmd_buf(move |device, cmd_buf, bindings| { + let buffer = bindings[buffer_node].handle; + + unsafe { + device.cmd_fill_buffer( + cmd_buf, + buffer, + region.start, + region.end - region.start, + data, + ); + } + }) + .submit_pass() + } + + /// Returns the index of the first pass which accesses a given node + #[profiling::function] + fn first_node_access_pass_index(&self, node: impl Node) -> Option { + let node_idx = node.index(); + + for (pass_idx, pass) in self.passes.iter().enumerate() { + for exec in pass.execs.iter() { + if exec.accesses.contains_key(&node_idx) { + return Some(pass_idx); + } + } + } + + None + } + + /// Returns the device address of a buffer node. + /// + /// # Panics + /// + /// Panics if the buffer is not currently bound or was not created with the + /// `SHADER_DEVICE_ADDRESS` usage flag. + pub fn node_device_address(&self, node: impl Into) -> vk::DeviceAddress { + let node: AnyBufferNode = node.into(); + let buffer = self.bindings[node.index()].as_driver_buffer().unwrap(); + + Buffer::device_address(buffer) + } + + /// Returns information used to crate a node. + pub fn node_info(&self, node: N) -> ::Info + where + N: Information, + { + node.get(self) + } + + /// Finalizes the graph and provides an object with functions for submitting the resulting + /// commands. + #[profiling::function] + pub fn resolve(mut self) -> Resolver { + // The final execution of each pass has no function + for pass in &mut self.passes { + pass.execs.pop(); + } + + Resolver::new(self) + } + + /// Removes a node from this graph. + /// + /// Future access to `node` on this graph will return invalid results. + pub fn unbind_node(&mut self, node: N) -> >::Result + where + N: Edge, + N: Unbind>::Result>, + { + node.unbind(self) + } + + /// Note: `data` must not exceed 65536 bytes. + pub fn update_buffer( + &mut self, + buffer_node: impl Into, + data: impl AsRef<[u8]> + 'static + Send, + ) -> &mut Self { + self.update_buffer_offset(buffer_node, 0, data) + } + + /// Note: `data` must not exceed 65536 bytes. + #[profiling::function] + pub fn update_buffer_offset( + &mut self, + buffer_node: impl Into, + offset: vk::DeviceSize, + data: impl AsRef<[u8]> + 'static + Send, + ) -> &mut Self { + let buffer_node = buffer_node.into(); + let data_end = offset + data.as_ref().len() as vk::DeviceSize; + + #[cfg(debug_assertions)] + { + let buffer_info = self.node_info(buffer_node); + + assert!( + data_end <= buffer_info.size, + "data range end ({data_end}) exceeds buffer size ({})", + buffer_info.size + ); + } + + self.begin_pass("update buffer") + .access_node_subrange(buffer_node, AccessType::TransferWrite, offset..data_end) + .record_cmd_buf(move |device, cmd_buf, bindings| { + let buffer = bindings[buffer_node].handle; + + unsafe { + device.cmd_update_buffer(cmd_buf, buffer, offset, data.as_ref()); + } + }) + .submit_pass() + } +} diff --git a/src/mod.rs b/src/mod.rs new file mode 100644 index 00000000..32ba0c6b --- /dev/null +++ b/src/mod.rs @@ -0,0 +1,3 @@ +//! Rendering operations and command submission. +//! +//! diff --git a/src/graph/node.rs b/src/node.rs similarity index 100% rename from src/graph/node.rs rename to src/node.rs diff --git a/src/graph/pass_ref.rs b/src/pass_ref.rs similarity index 99% rename from src/graph/pass_ref.rs rename to src/pass_ref.rs index f4e581d3..cb3420f8 100644 --- a/src/graph/pass_ref.rs +++ b/src/pass_ref.rs @@ -63,7 +63,7 @@ pub type DescriptorSetIndex = u32; /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; /// # use vk_graph::driver::DriverError; /// # use vk_graph::driver::device::{Device, DeviceInfo}; -/// # use vk_graph::graph::RenderGraph; +/// # use vk_graph::RenderGraph; /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); @@ -103,7 +103,7 @@ impl Acceleration<'_> { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureGeometry, AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, AccelerationStructureInfo, DeviceOrHostAddress}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; - /// # use vk_graph::graph::RenderGraph; + /// # use vk_graph::RenderGraph; /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); @@ -1024,8 +1024,8 @@ bind!(RayTrace); /// # use vk_graph::driver::DriverError; /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::image::{Image, ImageInfo}; -/// # use vk_graph::graph::RenderGraph; -/// # use vk_graph::graph::node::ImageNode; +/// # use vk_graph::RenderGraph; +/// # use vk_graph::node::ImageNode; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); @@ -1164,7 +1164,7 @@ impl Index for Bindings<'_> { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; /// # use vk_graph::driver::shader::{Shader}; -/// # use vk_graph::graph::RenderGraph; +/// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let info = ComputePipelineInfo::default(); @@ -1218,7 +1218,7 @@ impl Compute<'_> { /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; /// # use vk_graph::driver::shader::{Shader}; - /// # use vk_graph::graph::RenderGraph; + /// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::STORAGE_BUFFER); @@ -1304,7 +1304,7 @@ impl Compute<'_> { /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; /// # use vk_graph::driver::shader::{Shader}; - /// # use vk_graph::graph::RenderGraph; + /// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::STORAGE_BUFFER); @@ -1404,7 +1404,7 @@ impl Compute<'_> { /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; /// # use vk_graph::driver::shader::{Shader}; - /// # use vk_graph::graph::RenderGraph; + /// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let info = ComputePipelineInfo::default(); @@ -1466,7 +1466,7 @@ impl Compute<'_> { /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; /// # use vk_graph::driver::shader::{Shader}; - /// # use vk_graph::graph::RenderGraph; + /// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let info = ComputePipelineInfo::default(); @@ -1600,7 +1600,7 @@ impl From<(DescriptorSetIndex, BindingIndex, [BindingOffset; 1])> for Descriptor /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; /// # use vk_graph::driver::image::{Image, ImageInfo}; -/// # use vk_graph::graph::RenderGraph; +/// # use vk_graph::RenderGraph; /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); @@ -1644,7 +1644,7 @@ impl Draw<'_> { /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; /// # use vk_graph::driver::image::{Image, ImageInfo}; /// # use vk_graph::driver::shader::Shader; - /// # use vk_graph::graph::RenderGraph; + /// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let my_frag_code = [0u8; 1]; @@ -1722,7 +1722,7 @@ impl Draw<'_> { /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; /// # use vk_graph::driver::image::{Image, ImageInfo}; /// # use vk_graph::driver::shader::Shader; - /// # use vk_graph::graph::RenderGraph; + /// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::VERTEX_BUFFER); @@ -1901,7 +1901,7 @@ impl Draw<'_> { /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; /// # use vk_graph::driver::image::{Image, ImageInfo}; /// # use vk_graph::driver::shader::Shader; - /// # use vk_graph::graph::RenderGraph; + /// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let my_frag_code = [0u8; 1]; @@ -2111,7 +2111,7 @@ impl Draw<'_> { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; /// # use vk_graph::driver::image::{Image, ImageInfo}; - /// # use vk_graph::graph::RenderGraph; + /// # use vk_graph::RenderGraph; /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); @@ -2180,7 +2180,7 @@ impl Draw<'_> { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; /// # use vk_graph::driver::image::{Image, ImageInfo}; - /// # use vk_graph::graph::RenderGraph; + /// # use vk_graph::RenderGraph; /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); @@ -4691,7 +4691,7 @@ impl PipelinePassRef<'_, RayTracePipeline> { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; /// # use vk_graph::driver::shader::Shader; -/// # use vk_graph::graph::RenderGraph; +/// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let info = RayTracePipelineInfo::default(); @@ -4762,7 +4762,7 @@ impl RayTrace<'_> { /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # use vk_graph::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; /// # use vk_graph::driver::shader::Shader; - /// # use vk_graph::graph::RenderGraph; + /// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let shader = [0u8; 1]; @@ -4833,7 +4833,7 @@ impl RayTrace<'_> { /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # use vk_graph::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; /// # use vk_graph::driver::shader::Shader; - /// # use vk_graph::graph::RenderGraph; + /// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let shader = [0u8; 1]; @@ -4926,7 +4926,7 @@ impl RayTrace<'_> { /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # use vk_graph::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; /// # use vk_graph::driver::shader::Shader; - /// # use vk_graph::graph::RenderGraph; + /// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); /// # let shader = [0u8; 1]; diff --git a/src/graph/resolver.rs b/src/resolver.rs similarity index 100% rename from src/graph/resolver.rs rename to src/resolver.rs diff --git a/src/graph/swapchain.rs b/src/swapchain.rs similarity index 100% rename from src/graph/swapchain.rs rename to src/swapchain.rs From 37825a2611a4d73dcee52fd2cd498a2fdeb9939f Mon Sep 17 00:00:00 2001 From: John Wells Date: Wed, 18 Feb 2026 15:33:48 -0500 Subject: [PATCH 11/86] Clean up --- Cargo.toml | 1 + README.md | 2 +- contrib/vk-graph-egui/src/lib.rs | 3 +- contrib/vk-graph-fx/src/bitmap_font.rs | 3 +- contrib/vk-graph-fx/src/image_loader.rs | 7 +- contrib/vk-graph-fx/src/presenter.rs | 9 +- contrib/vk-graph-fx/src/transition.rs | 3 +- contrib/vk-graph-hot/examples/glsl.rs | 3 +- contrib/vk-graph-hot/examples/hlsl.rs | 3 +- contrib/vk-graph-hot/src/shader.rs | 2 +- contrib/vk-graph-imgui/src/lib.rs | 5 +- .../vk-graph-window/examples/hello_world.rs | 2 +- contrib/vk-graph-window/src/lib.rs | 4 +- examples/aliasing.rs | 4 +- examples/app.rs | 6 +- examples/bindless.rs | 7 +- examples/cpu_readback.rs | 4 +- examples/debugger.rs | 2 +- examples/egui.rs | 6 +- examples/font_bmp.rs | 3 +- examples/fuzzer.rs | 107 +++++--- examples/image_sampler.rs | 5 +- examples/imgui.rs | 2 +- examples/min_max.rs | 11 +- examples/mip_compute.rs | 7 +- examples/mip_graphic.rs | 8 +- examples/msaa.rs | 6 +- examples/multipass.rs | 14 +- examples/multithread.rs | 10 +- examples/ray_omni.rs | 13 +- examples/ray_trace.rs | 15 +- examples/rt_triangle.rs | 13 +- examples/shader-toy/src/main.rs | 14 +- examples/skeletal-anim/src/main.rs | 7 +- examples/subgroup_ops.rs | 10 +- examples/triangle.rs | 3 +- examples/vertex_layout.rs | 3 +- examples/vr/src/driver/instance.rs | 10 +- examples/vr/src/main.rs | 15 +- examples/vsm_omni.rs | 23 +- src/display.rs | 3 +- src/driver/accel_struct.rs | 53 +--- src/driver/buffer.rs | 55 +--- src/driver/compute.rs | 45 +--- src/driver/descriptor_set.rs | 2 +- src/driver/descriptor_set_layout.rs | 25 +- src/driver/device.rs | 250 +++++++++--------- src/driver/graphic.rs | 44 +-- src/driver/image.rs | 52 +--- src/driver/instance.rs | 81 +++--- src/driver/mod.rs | 2 +- src/driver/physical_device.rs | 154 ++++------- src/driver/ray_trace.rs | 51 +--- src/driver/shader.rs | 2 +- src/driver/surface.rs | 27 +- src/driver/swapchain.rs | 78 ++---- src/lib.rs | 140 ++++------ src/mod.rs | 3 - src/pass_ref.rs | 146 +++++----- src/pool/mod.rs | 2 +- src/resolver.rs | 51 ++-- 61 files changed, 696 insertions(+), 945 deletions(-) delete mode 100644 src/mod.rs diff --git a/Cargo.toml b/Cargo.toml index c14d2e41..3fdc6a69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ parking_lot = { version = "0.12", optional = true } paste = "1.0" profiling = "1.0" raw-window-handle = "0.6" +readonly = "0.2" spirq = "1.2" vk-sync = { version = "0.5", package = "vk-sync-fork" } # // SEE: https://github.com/gwihlidal/vk-sync-rs/pull/4 -> https://github.com/expenses/vk-sync-rs diff --git a/README.md b/README.md index a8b388f8..871d3815 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Features of the render graph: ```rust render_graph - .begin_pass("Fancy new algorithm for shading a moving character who is actively on fire") + .begin_cmd_buf().with_name("Fancy new algorithm for shading a moving character who is actively on fire") .bind_pipeline(&gfx_pipeline) .read_descriptor(0, some_image) .read_descriptor(1, another_image) diff --git a/contrib/vk-graph-egui/src/lib.rs b/contrib/vk-graph-egui/src/lib.rs index e3fe9a07..854d541d 100644 --- a/contrib/vk-graph-egui/src/lib.rs +++ b/contrib/vk-graph-egui/src/lib.rs @@ -285,7 +285,8 @@ impl Egui { ((clip_rect.max.y - clip_rect.min.y) * pixels_per_point) as u32; render_graph - .begin_pass("Egui pass") + .begin_cmd_buf() + .with_name("Egui pass") .bind_pipeline(&self.ppl) .access_node(idx_buf, AccessType::IndexBuffer) .access_node(vert_buf, AccessType::VertexBuffer) diff --git a/contrib/vk-graph-fx/src/bitmap_font.rs b/contrib/vk-graph-fx/src/bitmap_font.rs index d3bc76c2..c9a88083 100644 --- a/contrib/vk-graph-fx/src/bitmap_font.rs +++ b/contrib/vk-graph-fx/src/bitmap_font.rs @@ -206,7 +206,8 @@ impl BitmapFont { } let mut pass = graph - .begin_pass("text") + .begin_cmd_buf() + .with_name("text") .bind_pipeline(&self.pipeline) .access_node(vertex_buf, AccessType::IndexBuffer) .load_color(0, image) diff --git a/contrib/vk-graph-fx/src/image_loader.rs b/contrib/vk-graph-fx/src/image_loader.rs index 58caaf7f..a9dbfc67 100644 --- a/contrib/vk-graph-fx/src/image_loader.rs +++ b/contrib/vk-graph-fx/src/image_loader.rs @@ -144,7 +144,7 @@ impl ImageLoader { warn!("unused data"); } - let mut render_graph = RenderGraph::new(); + let mut render_graph = RenderGraph::default(); let image = render_graph.bind_node(self.create_image(format, width, height, is_srgb, false)?); @@ -204,7 +204,8 @@ impl ImageLoader { let dispatch_x = (width + 3) >> 2; let dispatch_y = height; render_graph - .begin_pass("Decode RGB image") + .begin_cmd_buf() + .with_name("Decode RGB image") .bind_pipeline(&self.decode_rgb_rgba) .read_descriptor(0, pixel_buf) .write_descriptor(1, temp_image) @@ -213,7 +214,7 @@ impl ImageLoader { .push_constants(&(pixel_buf_stride >> 2).to_ne_bytes()) .dispatch(dispatch_x, dispatch_y, 1); }) - .submit_pass() + .end_cmd_buf() .copy_image(temp_image, image); } ImageFormat::R8G8 | ImageFormat::R8G8B8A8 => { diff --git a/contrib/vk-graph-fx/src/presenter.rs b/contrib/vk-graph-fx/src/presenter.rs index 00b6d4dc..9d0bbdd5 100644 --- a/contrib/vk-graph-fx/src/presenter.rs +++ b/contrib/vk-graph-fx/src/presenter.rs @@ -40,7 +40,8 @@ impl ComputePresenter { // TODO: Notice non-sRGB images and run a different pipeline graph - .begin_pass("present (from compute)") + .begin_cmd_buf() + .with_name("present (from compute)") .bind_pipeline(&self.0[0]) .read_descriptor(0, image) .write_descriptor(1, swapchain) @@ -66,7 +67,8 @@ impl ComputePresenter { // TODO: Notice non-sRGB images and run a different pipeline graph - .begin_pass("present (from compute)") + .begin_cmd_buf() + .with_name("present (from compute)") .bind_pipeline(&self.0[1]) .read_descriptor((0, [0]), top_image) .read_descriptor((0, [1]), bottom_image) @@ -122,7 +124,8 @@ impl GraphicPresenter { )); graph - .begin_pass("present (from graphic)") + .begin_cmd_buf() + .with_name("present (from graphic)") .bind_pipeline(&self.pipeline) .read_descriptor(0, image) .store_color(0, swapchain) diff --git a/contrib/vk-graph-fx/src/transition.rs b/contrib/vk-graph-fx/src/transition.rs index bae37647..90b4b2fa 100644 --- a/contrib/vk-graph-fx/src/transition.rs +++ b/contrib/vk-graph-fx/src/transition.rs @@ -469,7 +469,8 @@ impl TransitionPipeline { // TODO: Handle displacement and luma in an if case, below render_graph - .begin_pass(format!("transition {transition_ty:?}")) + .begin_cmd_buf() + .with_name(format!("transition {transition_ty:?}")) .bind_pipeline(&pipeline) .read_descriptor(0, a_image) .read_descriptor(1, b_image) diff --git a/contrib/vk-graph-hot/examples/glsl.rs b/contrib/vk-graph-hot/examples/glsl.rs index 8885ef99..42daf79d 100644 --- a/contrib/vk-graph-hot/examples/glsl.rs +++ b/contrib/vk-graph-hot/examples/glsl.rs @@ -30,7 +30,8 @@ fn main() -> Result<(), WindowError> { window.run(|frame| { frame .render_graph - .begin_pass("make some noise") + .begin_cmd_buf() + .with_name("make some noise") .bind_pipeline(pipeline.hot()) .write_descriptor(0, frame.swapchain_image) .record_compute(move |compute, _| { diff --git a/contrib/vk-graph-hot/examples/hlsl.rs b/contrib/vk-graph-hot/examples/hlsl.rs index f59f24f9..c45b506c 100644 --- a/contrib/vk-graph-hot/examples/hlsl.rs +++ b/contrib/vk-graph-hot/examples/hlsl.rs @@ -34,7 +34,8 @@ fn main() -> Result<(), WindowError> { window.run(|frame| { frame .render_graph - .begin_pass("make some noise") + .begin_cmd_buf() + .with_name("make some noise") .bind_pipeline(pipeline.hot()) .clear_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) diff --git a/contrib/vk-graph-hot/src/shader.rs b/contrib/vk-graph-hot/src/shader.rs index 395dfcf3..15e02c2a 100644 --- a/contrib/vk-graph-hot/src/shader.rs +++ b/contrib/vk-graph-hot/src/shader.rs @@ -76,7 +76,7 @@ pub struct HotShader { /// # use vk_graph::driver::shader::{SpecializationInfo}; /// # use vk_graph_hot::shader::HotShader; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new_headless(DeviceInfo::default())?); /// # let my_shader_code = [0u8; 1]; /// // We instead specify 42 for MY_COUNT: /// let shader = HotShader::new_fragment(my_shader_code.as_slice()) diff --git a/contrib/vk-graph-imgui/src/lib.rs b/contrib/vk-graph-imgui/src/lib.rs index aa63e444..c56db517 100644 --- a/contrib/vk-graph-imgui/src/lib.rs +++ b/contrib/vk-graph-imgui/src/lib.rs @@ -121,7 +121,7 @@ impl ImGui { let framebuffer_scale = draw_data.framebuffer_scale; if draw_data.draw_lists_count() == 0 { - render_graph.clear_color_image(image); + render_graph.clear_color_image(image, [0f32; 4]); return image; } @@ -185,7 +185,8 @@ impl ImGui { self.platform.hidpi_factor() as f32 / window.inner_size().height as f32; render_graph - .begin_pass("imgui") + .begin_cmd_buf() + .with_name("imgui") .bind_pipeline(&self.pipeline) .access_node(index_buf, AccessType::IndexBuffer) .access_node(vertex_buf, AccessType::VertexBuffer) diff --git a/contrib/vk-graph-window/examples/hello_world.rs b/contrib/vk-graph-window/examples/hello_world.rs index 04a83957..97534926 100644 --- a/contrib/vk-graph-window/examples/hello_world.rs +++ b/contrib/vk-graph-window/examples/hello_world.rs @@ -7,6 +7,6 @@ fn main() -> Result<(), WindowError> { Window::new()?.run(|frame| { frame .render_graph - .clear_color_image_value(frame.swapchain_image, [100u8, 149, 237]); + .clear_color_image(frame.swapchain_image, [100u8, 149, 237]); }) } diff --git a/contrib/vk-graph-window/src/lib.rs b/contrib/vk-graph-window/src/lib.rs index 073fab43..830d101a 100644 --- a/contrib/vk-graph-window/src/lib.rs +++ b/contrib/vk-graph-window/src/lib.rs @@ -333,7 +333,7 @@ impl Window { } if let Some(swapchain_image) = self.display.acquire_next_image()? { - let mut render_graph = RenderGraph::new(); + let mut render_graph = RenderGraph::default(); let swapchain_image = render_graph.bind_node(swapchain_image); let swapchain_info = self.display.swapchain.info; @@ -426,7 +426,7 @@ impl WindowBuilder { /// TODO pub fn build(self) -> Result { let event_loop = EventLoop::new()?; - let device = Arc::new(Device::create_display(self.device_info, &event_loop)?); + let device = Arc::new(Device::from_display(&event_loop, self.device_info)?); Ok(Window { data: WindowData { diff --git a/examples/aliasing.rs b/examples/aliasing.rs index 719c2296..b8613dda 100644 --- a/examples/aliasing.rs +++ b/examples/aliasing.rs @@ -18,7 +18,7 @@ fn main() -> Result<(), DriverError> { let args = Args::parse(); let device_info = DeviceInfoBuilder::default().debug(args.debug); - let device = Arc::new(Device::create_headless(device_info)?); + let device = Arc::new(Device::new(device_info)?); // We wrap HashPool in an AliasPool container to enable resource aliasing let mut pool = AliasPool::new(HashPool::new(&device)); @@ -36,7 +36,7 @@ fn main() -> Result<(), DriverError> { let image2 = pool.alias(image_info)?; assert!(Arc::ptr_eq(&image1, &image2)); - let mut render_graph = RenderGraph::new(); + let mut render_graph = RenderGraph::default(); // Binding these images to any render graph will produce the same physical nodes let image1 = render_graph.bind_node(image1); diff --git a/examples/app.rs b/examples/app.rs index 56aed0c8..c1d9e690 100644 --- a/examples/app.rs +++ b/examples/app.rs @@ -44,7 +44,7 @@ impl ApplicationHandler for Application { let args = Args::parse(); let device_info = DeviceInfoBuilder::default().debug(args.debug); - let device = Arc::new(Device::create_display(device_info, &window).unwrap()); + let device = Arc::new(Device::from_display(&window, device_info).unwrap()); let surface = Surface::create(&device, &window, &window).unwrap(); let surface_formats = Surface::formats(&surface).unwrap(); @@ -114,11 +114,11 @@ struct Context { impl Context { fn draw(&mut self) -> Result<(), DisplayError> { if let Some(swapchain_image) = self.display.acquire_next_image()? { - let mut render_graph = RenderGraph::new(); + let mut render_graph = RenderGraph::default(); let swapchain_image = render_graph.bind_node(swapchain_image); // Rendering goes here! - render_graph.clear_color_image_value(swapchain_image, [1.0, 0.0, 1.0]); + render_graph.clear_color_image(swapchain_image, [1.0, 0.0, 1.0]); self.window.pre_present_notify(); self.display diff --git a/examples/bindless.rs b/examples/bindless.rs index 911c9acd..4d5ac947 100644 --- a/examples/bindless.rs +++ b/examples/bindless.rs @@ -28,7 +28,8 @@ fn main() -> Result<(), WindowError> { let mut pass = frame .render_graph - .begin_pass("Test") + .begin_cmd_buf() + .with_name("Test") .bind_pipeline(&pipeline) .access_node(draw_buf_node, AccessType::IndirectBuffer); @@ -49,7 +50,7 @@ fn create_images(device: &Arc) -> Result>, DriverError> { let mut textures = Vec::with_capacity(64); let (b, a) = (0.0, 1.0); - let mut graph = RenderGraph::new(); + let mut graph = RenderGraph::default(); for y in 0..8 { for x in 0..8 { let texture = Arc::new(Image::create( @@ -64,7 +65,7 @@ fn create_images(device: &Arc) -> Result>, DriverError> { let texture_node = graph.bind_node(&texture); let r = y as f32 / 7.0; let g = x as f32 / 7.0; - graph.clear_color_image_value(texture_node, [r, g, b, a]); + graph.clear_color_image(texture_node, [r, g, b, a]); textures.push(texture); } } diff --git a/examples/cpu_readback.rs b/examples/cpu_readback.rs index 89ecf152..82bbf3ed 100644 --- a/examples/cpu_readback.rs +++ b/examples/cpu_readback.rs @@ -12,9 +12,9 @@ fn main() -> Result<(), DriverError> { // For this example we create a headless device, but the same thing works using a window let args = Args::parse(); let device_info = DeviceInfoBuilder::default().debug(args.debug); - let device = Arc::new(Device::create_headless(device_info)?); + let device = Arc::new(Device::new(device_info)?); - let mut render_graph = RenderGraph::new(); + let mut render_graph = RenderGraph::default(); let src_buf = render_graph.bind_node(Buffer::create_from_slice( &device, diff --git a/examples/debugger.rs b/examples/debugger.rs index 00d16d0c..597a4e64 100644 --- a/examples/debugger.rs +++ b/examples/debugger.rs @@ -178,7 +178,7 @@ fn main() -> Result<(), vk_graph_window::WindowError> { */ frame .render_graph - .begin_pass("This doesn't look good...") + .begin_cmd_buf().with_name("This doesn't look good...") .bind_pipeline(&compute_pipeline) .write_descriptor(42, image) .record_compute(|compute, _| { diff --git a/examples/egui.rs b/examples/egui.rs index 9f73ecee..e0084c1f 100644 --- a/examples/egui.rs +++ b/examples/egui.rs @@ -30,12 +30,10 @@ fn main() -> anyhow::Result<()> { )) .unwrap(), ); + frame.render_graph.clear_color_image(img, [0., 1., 0., 1.]); frame .render_graph - .clear_color_image_value(img, [0., 1., 0., 1.]); - frame - .render_graph - .clear_color_image_value(frame.swapchain_image, [0., 0., 0., 1.]); + .clear_color_image(frame.swapchain_image, [0., 0., 0., 1.]); let id = egui.register_texture(img); diff --git a/examples/font_bmp.rs b/examples/font_bmp.rs index a81aab2b..b6c4f558 100644 --- a/examples/font_bmp.rs +++ b/examples/font_bmp.rs @@ -137,7 +137,8 @@ fn main() -> anyhow::Result<()> { let elapsed_time = Instant::now() - start_time; frame .render_graph - .begin_pass("smoke") + .begin_cmd_buf() + .with_name("smoke") .bind_pipeline(&smoke_pipeline) .write_descriptor(0, image_node) .record_compute(move |compute, _| { diff --git a/examples/fuzzer.rs b/examples/fuzzer.rs index 8744acd0..d887de1f 100644 --- a/examples/fuzzer.rs +++ b/examples/fuzzer.rs @@ -76,7 +76,9 @@ fn main() -> Result<(), WindowError> { let clear_before: bool = rng.random(); if clear_before { - frame.render_graph.clear_color_image(frame.swapchain_image); + frame + .render_graph + .clear_color_image(frame.swapchain_image, [0f32; 4]); } for _ in 0..args.ops_per_frame { @@ -85,7 +87,9 @@ fn main() -> Result<(), WindowError> { } if !clear_before { - frame.render_graph.clear_color_image(frame.swapchain_image); + frame + .render_graph + .clear_color_image(frame.swapchain_image, [0f32; 4]); } })?; @@ -264,7 +268,8 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { let pass = frame .render_graph - .begin_pass("build acceleration structures"); + .begin_cmd_buf() + .with_name("build acceleration structures"); // TODO: AccessType for these is funky, should be access_node? let mut pass = pass.read_node(index_node).read_node(vertex_node); @@ -368,12 +373,13 @@ fn record_compute_array_bind(frame: &mut FrameContext, pool: &mut HashPool) { frame .render_graph - .clear_color_image(images[0]) - .clear_color_image(images[1]) - .clear_color_image(images[2]) - .clear_color_image(images[3]) - .clear_color_image(images[4]) - .begin_pass("array-bind") + .clear_color_image(images[0], [0f32; 4]) + .clear_color_image(images[1], [0f32; 4]) + .clear_color_image(images[2], [0f32; 4]) + .clear_color_image(images[3], [0f32; 4]) + .clear_color_image(images[4], [0f32; 4]) + .begin_cmd_buf() + .with_name("array-bind") .bind_pipeline(&pipeline) .read_descriptor((0, [0]), images[0]) .read_descriptor((0, [1]), images[1]) @@ -448,7 +454,8 @@ fn record_compute_bindless(frame: &mut FrameContext, pool: &mut HashPool) { frame .render_graph - .begin_pass("compute-bindless") + .begin_cmd_buf() + .with_name("compute-bindless") .bind_pipeline(&pipeline) .write_descriptor((0, [0]), images[0]) .write_descriptor((0, [1]), images[1]) @@ -482,7 +489,8 @@ fn record_compute_no_op(frame: &mut FrameContext, _: &mut HashPool) { ); frame .render_graph - .begin_pass("no-op") + .begin_cmd_buf() + .with_name("no-op") .bind_pipeline(&pipeline) .record_compute(|compute, _| { compute.dispatch(1, 1, 1); @@ -567,12 +575,13 @@ fn record_graphic_bindless(frame: &mut FrameContext, pool: &mut HashPool) { frame .render_graph - .clear_color_image(images[0]) - .clear_color_image(images[1]) - .clear_color_image(images[2]) - .clear_color_image(images[3]) - .clear_color_image(images[4]) - .begin_pass("graphic-bindless") + .clear_color_image(images[0], [0f32; 4]) + .clear_color_image(images[1], [0f32; 4]) + .clear_color_image(images[2], [0f32; 4]) + .clear_color_image(images[3], [0f32; 4]) + .clear_color_image(images[4], [0f32; 4]) + .begin_cmd_buf() + .with_name("graphic-bindless") .bind_pipeline(&pipeline) .read_descriptor((0, [0]), images[0]) .read_descriptor((0, [1]), images[1]) @@ -617,7 +626,8 @@ fn record_graphic_load_store(frame: &mut FrameContext, _: &mut HashPool) { frame .render_graph - .begin_pass("load-store") + .begin_cmd_buf() + .with_name("load-store") .bind_pipeline(&pipeline) .load_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) @@ -655,8 +665,7 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo vk::Format::D16_UNORM_S8_UINT, vk::Format::D32_SFLOAT_S8_UINT, ] { - let format_props = Device::image_format_properties( - frame.device, + let format_props = frame.device.physical_device.image_format_properties( format, vk::ImageType::TYPE_2D, vk::ImageTiling::OPTIMAL, @@ -789,7 +798,8 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo frame .render_graph - .begin_pass("msaa-depth-stencil") + .begin_cmd_buf() + .with_name("msaa-depth-stencil") .bind_pipeline(&pipeline) .set_depth_stencil(depth_stencil_mode) .clear_color(0, msaa_color_image) @@ -820,7 +830,8 @@ fn record_graphic_will_merge_common_color1(frame: &mut FrameContext, pool: &mut // Pass "a" stores color0 which "b" compatibly loads; so these two will get merged frame .render_graph - .begin_pass("a") + .begin_cmd_buf() + .with_name("a") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), @@ -853,7 +864,8 @@ fn record_graphic_will_merge_common_color1(frame: &mut FrameContext, pool: &mut }); frame .render_graph - .begin_pass("b") + .begin_cmd_buf() + .with_name("b") .bind_pipeline(&graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), @@ -909,7 +921,8 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut frame .render_graph - .begin_pass("a") + .begin_cmd_buf() + .with_name("a") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), @@ -942,7 +955,8 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut }); frame .render_graph - .begin_pass("b") + .begin_cmd_buf() + .with_name("b") .bind_pipeline(&graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), @@ -979,7 +993,8 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut }); frame .render_graph - .begin_pass("c") + .begin_cmd_buf() + .with_name("c") .bind_pipeline(&graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), @@ -1036,7 +1051,8 @@ fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut // Pass "a" stores color0+depth which "b" compatibly loads; so these two will get merged frame .render_graph - .begin_pass("a") + .begin_cmd_buf() + .with_name("a") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), @@ -1070,7 +1086,8 @@ fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut }); frame .render_graph - .begin_pass("b") + .begin_cmd_buf() + .with_name("b") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), @@ -1125,7 +1142,8 @@ fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut // Pass "a" stores color0+depth which "b" compatibly loads; so these two will get merged frame .render_graph - .begin_pass("a") + .begin_cmd_buf() + .with_name("a") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), @@ -1156,7 +1174,8 @@ fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut }); frame .render_graph - .begin_pass("b") + .begin_cmd_buf() + .with_name("b") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), @@ -1204,7 +1223,8 @@ fn record_graphic_will_merge_common_depth3(frame: &mut FrameContext, pool: &mut frame .render_graph - .begin_pass("a") + .begin_cmd_buf() + .with_name("a") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), @@ -1235,7 +1255,8 @@ fn record_graphic_will_merge_common_depth3(frame: &mut FrameContext, pool: &mut }); frame .render_graph - .begin_pass("b") + .begin_cmd_buf() + .with_name("b") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), @@ -1331,7 +1352,8 @@ fn record_graphic_will_merge_subpass_input(frame: &mut FrameContext, pool: &mut // Pass "a" stores color 0 which "b" compatibly inputs; so these two will get merged frame .render_graph - .begin_pass("a") + .begin_cmd_buf() + .with_name("a") .bind_pipeline(&pipeline_a) .clear_color(0, image) .store_color(0, image) @@ -1340,7 +1362,8 @@ fn record_graphic_will_merge_subpass_input(frame: &mut FrameContext, pool: &mut }); frame .render_graph - .begin_pass("b") + .begin_cmd_buf() + .with_name("b") .bind_pipeline(&pipeline_b) .store_color(0, image) .record_subpass(|subpass, _| { @@ -1389,7 +1412,8 @@ fn record_graphic_wont_merge(frame: &mut FrameContext, pool: &mut HashPool) { // These two passes have common writes but are otherwise regular - they won't get merged frame .render_graph - .begin_pass("c") + .begin_cmd_buf() + .with_name("c") .bind_pipeline(&pipeline) .store_color(0, image) .record_subpass(|subpass, _| { @@ -1397,7 +1421,8 @@ fn record_graphic_wont_merge(frame: &mut FrameContext, pool: &mut HashPool) { }); frame .render_graph - .begin_pass("d") + .begin_cmd_buf() + .with_name("d") .bind_pipeline(&pipeline) .store_color(0, image) .record_subpass(|subpass, _| { @@ -1456,14 +1481,15 @@ fn record_transfer_graphic_multipass(frame: &mut FrameContext, pool: &mut HashPo ), ]; - frame.render_graph.clear_color_image(images[0]); - frame.render_graph.clear_color_image(images[1]); + frame.render_graph.clear_color_image(images[0], [0f32; 4]); + frame.render_graph.clear_color_image(images[1], [0f32; 4]); // a and b should merge into one renderpass with two subpasses; however the use of images[1] in // b should have a pipeline barrier (on the clear we just did) before the pass starts. frame .render_graph - .begin_pass("a") + .begin_cmd_buf() + .with_name("a") .bind_pipeline(&pipeline) .clear_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) @@ -1473,7 +1499,8 @@ fn record_transfer_graphic_multipass(frame: &mut FrameContext, pool: &mut HashPo }); frame .render_graph - .begin_pass("b") + .begin_cmd_buf() + .with_name("b") .bind_pipeline(&pipeline) .load_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) diff --git a/examples/image_sampler.rs b/examples/image_sampler.rs index 1b24ea71..da903797 100644 --- a/examples/image_sampler.rs +++ b/examples/image_sampler.rs @@ -68,7 +68,8 @@ fn main() -> anyhow::Result<()> { let gulf_image = frame.render_graph.bind_node(&gulf_image); frame .render_graph - .begin_pass("Draw gulf image to swapchain") + .begin_cmd_buf() + .with_name("Draw gulf image to swapchain") .bind_pipeline(&pipelines[pipeline_index]) .read_descriptor(0, gulf_image) .store_color(0, frame.swapchain_image) @@ -241,7 +242,7 @@ fn read_image(device: &Arc, path: impl AsRef) -> anyhow::Result Result<(), WindowError> { ); frame .render_graph - .clear_color_image_value(app_image, [0.2, 0.22, 0.2, 1.0]); + .clear_color_image(app_image, [0.2, 0.22, 0.2, 1.0]); // Use the draw function callback to do some fun meant-for-debug-mode GUI stuff let gui_image = imgui.draw( diff --git a/examples/min_max.rs b/examples/min_max.rs index 26484a32..c3c4227d 100644 --- a/examples/min_max.rs +++ b/examples/min_max.rs @@ -19,10 +19,10 @@ use { fn main() -> Result<(), DriverError> { pretty_env_logger::init(); - let mut render_graph = RenderGraph::new(); + let mut render_graph = RenderGraph::default(); let args = Args::parse(); let device_info = DeviceInfoBuilder::default().debug(args.debug); - let device = Arc::new(Device::create_headless(device_info)?); + let device = Arc::new(Device::new(device_info)?); let size = 4; // The 4x4 depth image will have pixels that look like this: @@ -129,7 +129,9 @@ fn fill_depth_image( ); // Not required, but good practice: Check image format support - let image_fmt_props = Device::image_format_properties(device, fmt, ty, tiling, usage, flags)? + let image_fmt_props = device + .physical_device + .image_format_properties(fmt, ty, tiling, usage, flags)? .ok_or(DriverError::Unsupported)?; if size > image_fmt_props.max_extent.width || size > image_fmt_props.max_extent.height { // In this case you might use a smaller image @@ -174,7 +176,8 @@ fn reduce_depth_image( let reduced_image = render_graph.bind_node(Image::create(device, reduced_info)?); render_graph - .begin_pass("Reduce depth image") + .begin_cmd_buf() + .with_name("Reduce depth image") .bind_pipeline(&Arc::new(ComputePipeline::create( device, ComputePipelineInfo::default(), diff --git a/examples/mip_compute.rs b/examples/mip_compute.rs index e195fc36..e6664634 100644 --- a/examples/mip_compute.rs +++ b/examples/mip_compute.rs @@ -16,9 +16,9 @@ fn main() -> Result<(), DriverError> { let args = Args::parse(); let device_info = DeviceInfoBuilder::default().debug(args.debug); - let device = Arc::new(Device::create_headless(device_info)?); + let device = Arc::new(Device::new(device_info)?); - let mut render_graph = RenderGraph::new(); + let mut render_graph = RenderGraph::default(); let depth_pyramid = render_graph.bind_node(Image::create( &device, @@ -51,7 +51,8 @@ fn main() -> Result<(), DriverError> { render_graph.copy_buffer_to_image(depth_buf, depth_pyramid); let mut pass = render_graph - .begin_pass("update depth pyramid") + .begin_cmd_buf() + .with_name("update depth pyramid") .bind_pipeline(ComputePipeline::create( &device, ComputePipelineInfo::default(), diff --git a/examples/mip_graphic.rs b/examples/mip_graphic.rs index 841cb17b..037411df 100644 --- a/examples/mip_graphic.rs +++ b/examples/mip_graphic.rs @@ -58,7 +58,8 @@ fn main() -> Result<(), WindowError> { let mut pass = frame .render_graph - .begin_pass("splat mips") + .begin_cmd_buf() + .with_name("splat mips") .bind_pipeline(&splat); for mip_level in 0..mip_level_count { @@ -145,7 +146,7 @@ fn fill_mip_levels(device: &Arc, image: &Arc) -> Result<(), Drive ], )?); - let mut render_graph = RenderGraph::new(); + let mut render_graph = RenderGraph::default(); let image_info = image.info; let image = render_graph.bind_node(image); @@ -154,7 +155,8 @@ fn fill_mip_levels(device: &Arc, image: &Arc) -> Result<(), Drive // new pass for each level the Vulkan framebuffer would be set to the size of the first image. for mip_level in 0..image_info.mip_level_count { render_graph - .begin_pass("fill mip levels") + .begin_cmd_buf() + .with_name("fill mip levels") .bind_pipeline(&vertical_gradient) .store_color_as( 0, diff --git a/examples/msaa.rs b/examples/msaa.rs index c389bfa3..af81d713 100644 --- a/examples/msaa.rs +++ b/examples/msaa.rs @@ -91,7 +91,8 @@ fn main() -> anyhow::Result<()> { let mut pass = frame .render_graph - .begin_pass("cube") + .begin_cmd_buf() + .with_name("cube") .bind_pipeline(if will_render_msaa { &mesh_msaa_pipeline } else { @@ -168,8 +169,7 @@ fn main() -> anyhow::Result<()> { fn best_depth_format(device: &Device) -> vk::Format { for format in [vk::Format::D32_SFLOAT, vk::Format::D16_UNORM] { - let format_props = Device::image_format_properties( - device, + let format_props = device.physical_device.image_format_properties( format, vk::ImageType::TYPE_2D, vk::ImageTiling::OPTIMAL, diff --git a/examples/multipass.rs b/examples/multipass.rs index 07ab013b..7b660190 100644 --- a/examples/multipass.rs +++ b/examples/multipass.rs @@ -88,7 +88,8 @@ fn main() -> anyhow::Result<()> { // Depth Prepass frame .render_graph - .begin_pass("Depth Prepass") + .begin_cmd_buf() + .with_name("Depth Prepass") .bind_pipeline(&prepass) .set_depth_stencil(write) .read_descriptor(0, camera_buf) @@ -118,7 +119,8 @@ fn main() -> anyhow::Result<()> { // Renders a golden orb on an un-cleared swapchain image frame .render_graph - .begin_pass("funky shape PBR") + .begin_cmd_buf() + .with_name("funky shape PBR") .bind_pipeline(&pbr) .set_depth_stencil(write) .read_descriptor(0, camera_buf) @@ -146,7 +148,8 @@ fn main() -> anyhow::Result<()> { // Renders a solid color wherever the golden orb did not draw frame .render_graph - .begin_pass("fill background") + .begin_cmd_buf() + .with_name("fill background") .bind_pipeline(&fill_background) .set_depth_stencil(read) .load_depth_stencil(depth_stencil) @@ -166,8 +169,7 @@ fn best_depth_stencil_format(device: &Device) -> vk::Format { vk::Format::D16_UNORM_S8_UINT, vk::Format::D32_SFLOAT_S8_UINT, ] { - let format_props = Device::image_format_properties( - device, + let format_props = device.physical_device.image_format_properties( format, vk::ImageType::TYPE_2D, vk::ImageTiling::OPTIMAL, @@ -277,7 +279,7 @@ fn create_funky_shape(device: &Arc, pool: &mut LazyPool) -> Result anyhow::Result<()> { let t = 12.0 * ((Instant::now() - started_at).as_millis() % 32) as f32; // Clear a new image to a cycling color - let mut render_graph = RenderGraph::new(); + let mut render_graph = RenderGraph::default(); let image = render_graph.bind_node( pool.lease(ImageInfo::image_2d( 10, @@ -111,7 +111,7 @@ fn main() -> anyhow::Result<()> { )) .unwrap(), ); - render_graph.clear_color_image_value( + render_graph.clear_color_image( image, [ (t.sin() * 127.0 + 128.0) as u8, @@ -152,7 +152,9 @@ fn main() -> anyhow::Result<()> { } } - frame.render_graph.clear_color_image(frame.swapchain_image); + frame + .render_graph + .clear_color_image(frame.swapchain_image, [0f32; 4]); for (image_idx, image) in images.iter().enumerate() { let image = frame.render_graph.bind_node(image); @@ -246,7 +248,7 @@ fn load_font(device: &Arc) -> anyhow::Result { ) .unwrap(); - let mut render_graph = RenderGraph::new(); + let mut render_graph = RenderGraph::default(); let page_0 = render_graph.bind_node(page_0); let temp_buf = render_graph.bind_node(temp_buf); render_graph.copy_buffer_to_image(temp_buf, page_0); diff --git a/examples/ray_omni.rs b/examples/ray_omni.rs index fbd23eeb..29b26573 100644 --- a/examples/ray_omni.rs +++ b/examples/ray_omni.rs @@ -90,7 +90,8 @@ fn main() -> anyhow::Result<()> { frame .render_graph - .begin_pass("Mesh with ray-query shadows") + .begin_cmd_buf() + .with_name("Mesh with ray-query shadows") .bind_pipeline(&gfx_pipeline) .access_node(ground_mesh_index_buf, AccessType::IndexBuffer) .access_node(ground_mesh_vertex_buf, AccessType::VertexBuffer) @@ -129,8 +130,7 @@ fn best_2d_optimal_format( flags: vk::ImageCreateFlags, ) -> vk::Format { for format in formats { - let format_props = Device::image_format_properties( - device, + let format_props = device.physical_device.image_format_properties( *format, vk::ImageType::TYPE_2D, vk::ImageTiling::OPTIMAL, @@ -177,7 +177,7 @@ fn create_blas( .flags(vk::BuildAccelerationStructureFlagsKHR::PREFER_FAST_TRACE); let size = AccelerationStructure::size_of(device, &info); - let mut render_graph = RenderGraph::new(); + let mut render_graph = RenderGraph::default(); let blas = render_graph.bind_node(AccelerationStructure::create( device, AccelerationStructureInfo::blas(size.create_size), @@ -201,7 +201,7 @@ fn create_blas( )?); let scratch_data = render_graph.node_device_address(scratch_buf); - let mut pass = render_graph.begin_pass("Build BLAS"); + let mut pass = render_graph.begin_cmd_buf().with_name("Build BLAS"); for model in models.iter().copied() { let index_buf = pass.bind_node(&model.index_buf); @@ -386,7 +386,8 @@ fn create_tlas( let instance_buf = render_graph.bind_node(instance_buf); render_graph - .begin_pass("Build TLAS") + .begin_cmd_buf() + .with_name("Build TLAS") .access_node(blas, AccessType::AccelerationStructureBuildRead) .access_node(instance_buf, AccessType::AccelerationStructureBuildRead) .access_node(scratch_buf, AccessType::AccelerationStructureBufferWrite) diff --git a/examples/ray_trace.rs b/examples/ray_trace.rs index 415e2f48..685c114d 100644 --- a/examples/ray_trace.rs +++ b/examples/ray_trace.rs @@ -668,7 +668,7 @@ fn main() -> anyhow::Result<()> { .unwrap() .min_accel_struct_scratch_offset_alignment as vk::DeviceSize; - let mut render_graph = RenderGraph::new(); + let mut render_graph = RenderGraph::default(); let index_node = render_graph.bind_node(&index_buf); let vertex_node = render_graph.bind_node(&vertex_buf); let blas_node = render_graph.bind_node(&blas); @@ -687,7 +687,8 @@ fn main() -> anyhow::Result<()> { let scratch_data = render_graph.node_device_address(scratch_buf); render_graph - .begin_pass("Build BLAS") + .begin_cmd_buf() + .with_name("Build BLAS") .access_node(index_node, AccessType::AccelerationStructureBuildRead) .access_node(vertex_node, AccessType::AccelerationStructureBuildRead) .access_node(scratch_buf, AccessType::AccelerationStructureBufferWrite) @@ -713,7 +714,8 @@ fn main() -> anyhow::Result<()> { let tlas_node = render_graph.bind_node(&tlas); render_graph - .begin_pass("Build TLAS") + .begin_cmd_buf() + .with_name("Build TLAS") .access_node(blas_node, AccessType::AccelerationStructureBuildRead) .access_node(instance_node, AccessType::AccelerationStructureBuildRead) .access_node(scratch_buf, AccessType::AccelerationStructureBufferWrite) @@ -801,7 +803,7 @@ fn main() -> anyhow::Result<()> { if input.key_pressed(KeyCode::Escape) { frame_count = 0; - frame.render_graph.clear_color_image(image_node); + frame.render_graph.clear_color_image(image_node, [0f32; 4]); } else { frame_count += 1; } @@ -848,7 +850,8 @@ fn main() -> anyhow::Result<()> { frame .render_graph - .begin_pass("basic ray tracer") + .begin_cmd_buf() + .with_name("basic ray tracer") .bind_pipeline(&ray_trace_pipeline) .access_node( blas_node, @@ -881,7 +884,7 @@ fn main() -> anyhow::Result<()> { 1, ); }) - .submit_pass() + .end_cmd_buf() .copy_image(image_node, frame.swapchain_image); })?; diff --git a/examples/rt_triangle.rs b/examples/rt_triangle.rs index 66e9fadb..55dbc778 100644 --- a/examples/rt_triangle.rs +++ b/examples/rt_triangle.rs @@ -322,7 +322,7 @@ fn main() -> anyhow::Result<()> { .unwrap() .min_accel_struct_scratch_offset_alignment as vk::DeviceSize; - let mut render_graph = RenderGraph::new(); + let mut render_graph = RenderGraph::default(); let index_node = render_graph.bind_node(&index_buf); let vertex_node = render_graph.bind_node(&vertex_buf); let blas_node = render_graph.bind_node(&blas); @@ -341,7 +341,8 @@ fn main() -> anyhow::Result<()> { let scratch_data = render_graph.node_device_address(scratch_buf); render_graph - .begin_pass("Build BLAS") + .begin_cmd_buf() + .with_name("Build BLAS") .access_node(index_node, AccessType::AccelerationStructureBuildRead) .access_node(vertex_node, AccessType::AccelerationStructureBuildRead) .access_node(scratch_buf, AccessType::AccelerationStructureBufferWrite) @@ -367,7 +368,8 @@ fn main() -> anyhow::Result<()> { let tlas_node = render_graph.bind_node(&tlas); render_graph - .begin_pass("Build TLAS") + .begin_cmd_buf() + .with_name("Build TLAS") .access_node(blas_node, AccessType::AccelerationStructureBuildRead) .access_node(instance_node, AccessType::AccelerationStructureBuildRead) .access_node(scratch_buf, AccessType::AccelerationStructureBufferWrite) @@ -394,7 +396,8 @@ fn main() -> anyhow::Result<()> { frame .render_graph - .begin_pass("ray-traced triangle") + .begin_cmd_buf() + .with_name("ray-traced triangle") .bind_pipeline(&ray_trace_pipeline) .access_node( blas_node, @@ -418,7 +421,7 @@ fn main() -> anyhow::Result<()> { 1, ); }) - .submit_pass(); + .end_cmd_buf(); })?; Ok(()) diff --git a/examples/shader-toy/src/main.rs b/examples/shader-toy/src/main.rs index 490cf2cc..6c6d7244 100644 --- a/examples/shader-toy/src/main.rs +++ b/examples/shader-toy/src/main.rs @@ -126,7 +126,7 @@ fn main() -> anyhow::Result<()> { .context("FLOCKAROO_IMG_FRAG")?, ); - let mut render_graph = RenderGraph::new(); + let mut render_graph = RenderGraph::default(); let blank_image = render_graph.bind_node( cache .lease(ImageInfo::image_2d( @@ -167,9 +167,9 @@ fn main() -> anyhow::Result<()> { ); render_graph - .clear_color_image_value(framebuffer_image, [1.0, 1.0, 0.0, 1.0]) - .clear_color_image_value(blank_image, [0.0, 0.0, 0.0, 1.0]) - .clear_color_image_value(temp_image, [0.0, 1.0, 0.0, 1.0]); + .clear_color_image(framebuffer_image, [1.0, 1.0, 0.0, 1.0]) + .clear_color_image(blank_image, [0.0, 0.0, 0.0, 1.0]) + .clear_color_image(temp_image, [0.0, 1.0, 0.0, 1.0]); let mut framebuffer_image_binding = Some(render_graph.unbind_node(framebuffer_image)); let mut blank_image_binding = Some(render_graph.unbind_node(blank_image)); @@ -290,7 +290,8 @@ fn main() -> anyhow::Result<()> { // Fill a buffer using a single-pass CFD pipeline where previous output feeds next input frame .render_graph - .begin_pass("Buffer A") + .begin_cmd_buf() + .with_name("Buffer A") .bind_pipeline(&buffer_pipeline) .read_descriptor(0, input) .read_descriptor(1, noise_image) @@ -305,7 +306,8 @@ fn main() -> anyhow::Result<()> { // Make the CFD look more like paint with a second pass frame .render_graph - .begin_pass("Image") + .begin_cmd_buf() + .with_name("Image") .bind_pipeline(&image_pipeline) .read_descriptor(0, output) .store_color(0, input) diff --git a/examples/skeletal-anim/src/main.rs b/examples/skeletal-anim/src/main.rs index 7ccff60c..de24f86c 100644 --- a/examples/skeletal-anim/src/main.rs +++ b/examples/skeletal-anim/src/main.rs @@ -125,7 +125,8 @@ fn main() -> Result<(), WindowError> { frame .render_graph - .begin_pass("🦴") + .begin_cmd_buf() + .with_name("🦴") .bind_pipeline(&pipeline) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) .access_node(index_buf, AccessType::IndexBuffer) @@ -194,7 +195,7 @@ fn load_texture( )?); // Copy the host-accessible pixels into the device-only image - let mut render_graph = RenderGraph::new(); + let mut render_graph = RenderGraph::default(); let image_node = render_graph.bind_node(&image); let buffer_node = render_graph.bind_node(&buffer); render_graph.copy_buffer_to_image(buffer_node, image_node); @@ -447,7 +448,7 @@ impl Model { )?); // Copy the host-accessible staging buffers to device-only buffers - let mut render_graph = RenderGraph::new(); + let mut render_graph = RenderGraph::default(); let index_staging_buf_node = render_graph.bind_node(index_staging_buf); let vertex_staging_buf_node = render_graph.bind_node(vertex_staging_buf); let index_buf_node = render_graph.bind_node(&index_buf); diff --git a/examples/subgroup_ops.rs b/examples/subgroup_ops.rs index 2ad2bacf..b2c90e94 100644 --- a/examples/subgroup_ops.rs +++ b/examples/subgroup_ops.rs @@ -31,7 +31,7 @@ fn main() -> Result<(), DriverError> { let args = Args::parse(); let device_info = DeviceInfoBuilder::default().debug(args.debug); - let device = Arc::new(Device::create_headless(device_info)?); + let device = Arc::new(Device::new(device_info)?); let Vulkan11Properties { subgroup_size, subgroup_supported_operations, @@ -66,7 +66,7 @@ fn exclusive_sum( scan_pipeline: &Arc, input_data: &[u32], ) -> Result, DriverError> { - let mut render_graph = RenderGraph::new(); + let mut render_graph = RenderGraph::default(); let input_buf = render_graph.bind_node(Buffer::create_from_slice( device, @@ -95,7 +95,8 @@ fn exclusive_sum( if reduce_count > 0 { render_graph - .begin_pass("exclusive sum reduce") + .begin_cmd_buf() + .with_name("exclusive sum reduce") .bind_pipeline(reduce_pipeline) .read_descriptor(0, input_buf) .write_descriptor(1, workgroup_buf) @@ -105,7 +106,8 @@ fn exclusive_sum( } render_graph - .begin_pass("exclusive sum scan") + .begin_cmd_buf() + .with_name("exclusive sum scan") .bind_pipeline(scan_pipeline) .read_descriptor(0, workgroup_buf) .read_descriptor(1, input_buf) diff --git a/examples/triangle.rs b/examples/triangle.rs index 9d555eb8..b8fb6dd7 100644 --- a/examples/triangle.rs +++ b/examples/triangle.rs @@ -84,7 +84,8 @@ fn main() -> Result<(), WindowError> { frame .render_graph - .begin_pass("Triangle Example") + .begin_cmd_buf() + .with_name("Triangle Example") .bind_pipeline(&triangle_pipeline) .access_node(index_node, AccessType::IndexBuffer) .access_node(vertex_node, AccessType::VertexBuffer) diff --git a/examples/vertex_layout.rs b/examples/vertex_layout.rs index 0293a7db..f94f9a3e 100644 --- a/examples/vertex_layout.rs +++ b/examples/vertex_layout.rs @@ -101,7 +101,8 @@ fn draw_triangle( frame .render_graph - .begin_pass("Triangle") + .begin_cmd_buf() + .with_name("Triangle") .bind_pipeline(pipeline) .load_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) diff --git a/examples/vr/src/driver/instance.rs b/examples/vr/src/driver/instance.rs index c4a7eaa3..abdc37a2 100644 --- a/examples/vr/src/driver/instance.rs +++ b/examples/vr/src/driver/instance.rs @@ -179,7 +179,7 @@ impl XrInstance { })?; let ash_device = physical_device - .create_ash_device(true, |create_info| { + .create_ash_device(|create_info| { let device = xr_instance .create_vulkan_device( system, @@ -202,13 +202,13 @@ impl XrInstance { InstanceCreateError::VulkanUnsupported })?; - let device = Arc::new(Device::load(physical_device, ash_device, true).map_err( - |err| { + let device = Arc::new( + Device::from_ash_device(ash_device, physical_device).map_err(|err| { error!("Vulkan device: {err}"); InstanceCreateError::VulkanUnsupported - }, - )?); + })?, + ); let event_buf = xr::EventDataBuffer::new(); Ok(Self { diff --git a/examples/vr/src/main.rs b/examples/vr/src/main.rs index 5365fa8b..522a3d1f 100644 --- a/examples/vr/src/main.rs +++ b/examples/vr/src/main.rs @@ -268,7 +268,7 @@ fn main() -> anyhow::Result<()> { continue; } - let mut render_graph = RenderGraph::new(); + let mut render_graph = RenderGraph::default(); let depth_image = render_graph.bind_node( pool.lease(ImageInfo::image_2d_array( resolution.width, @@ -340,7 +340,7 @@ fn main() -> anyhow::Result<()> { render_graph.bind_node(buf) }; - render_graph.clear_color_image_value(swapchain_image, [0x00, 0x00, 0x00, 0xff]); + render_graph.clear_color_image(swapchain_image, [0x00, 0x00, 0x00, 0xff]); if let Some(location) = left_hand_location { let index_buf = render_graph.bind_node(&lincoln_hand_left.index_buf); @@ -352,7 +352,8 @@ fn main() -> anyhow::Result<()> { let push_consts = PushConstants::new(model_transform); render_graph - .begin_pass("Left hand") + .begin_cmd_buf() + .with_name("Left hand") .bind_pipeline(hands_pipeline.hot()) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) .set_multiview(VIEW_MASK, VIEW_MASK) @@ -396,7 +397,8 @@ fn main() -> anyhow::Result<()> { let push_consts = PushConstants::new(model_transform); render_graph - .begin_pass("Right hand") + .begin_cmd_buf() + .with_name("Right hand") .bind_pipeline(hands_pipeline.hot()) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) .set_multiview(VIEW_MASK, VIEW_MASK) @@ -438,7 +440,8 @@ fn main() -> anyhow::Result<()> { let push_consts = PushConstants::new(Mat4::IDENTITY); render_graph - .begin_pass("Woolly Mammoth") + .begin_cmd_buf() + .with_name("Woolly Mammoth") .bind_pipeline(mammoth_pipeline.hot()) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) .set_multiview(VIEW_MASK, VIEW_MASK) @@ -759,7 +762,7 @@ fn load_texture( ), )?); - let mut render_graph = RenderGraph::new(); + let mut render_graph = RenderGraph::default(); let staging_buf = render_graph.bind_node(staging_buf); let texture_image = render_graph.bind_node(&texture); render_graph.copy_buffer_to_image(staging_buf, texture_image); diff --git a/examples/vsm_omni.rs b/examples/vsm_omni.rs index 0d124dbc..e107e6a9 100644 --- a/examples/vsm_omni.rs +++ b/examples/vsm_omni.rs @@ -241,7 +241,8 @@ fn main() -> anyhow::Result<()> { if input.key_held(KeyCode::Tab) { frame .render_graph - .begin_pass("DEBUG") + .begin_cmd_buf() + .with_name("DEBUG") .bind_pipeline(&debug_pipeline) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) .access_descriptor( @@ -274,7 +275,8 @@ fn main() -> anyhow::Result<()> { if use_geometry_shader { frame .render_graph - .begin_pass("Shadow (Using geometry shader)") + .begin_cmd_buf() + .with_name("Shadow (Using geometry shader)") .bind_pipeline(&shadow_pipeline) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) .access_descriptor(0, light_uniform_buf, AccessType::AnyShaderReadUniformBuffer) @@ -318,7 +320,8 @@ fn main() -> anyhow::Result<()> { frame .render_graph - .begin_pass("Shadow") + .begin_cmd_buf() + .with_name("Shadow") .bind_pipeline(&shadow_pipeline) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) .access_descriptor( @@ -358,15 +361,17 @@ fn main() -> anyhow::Result<()> { // separable box blur filter which approximates a gaussian blur frame .render_graph - .begin_pass("Blur X") + .begin_cmd_buf() + .with_name("Blur X") .bind_pipeline(&blur_x_pipeline) .read_descriptor(0, shadow_faces_node) .write_descriptor(1, temp_image) .record_compute(move |compute, _| { compute.dispatch(1, CUBEMAP_SIZE, 6); }) - .submit_pass() - .begin_pass("Blur Y") + .end_cmd_buf() + .begin_cmd_buf() + .with_name("Blur Y") .bind_pipeline(&blur_y_pipeline) .read_descriptor(0, temp_image) .write_descriptor(1, shadow_faces_node) @@ -379,7 +384,8 @@ fn main() -> anyhow::Result<()> { // Render the scene directly to the swapchain using the shadow map from the above pass frame .render_graph - .begin_pass("Mesh objects") + .begin_cmd_buf() + .with_name("Mesh objects") .bind_pipeline(&mesh_pipeline) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) .access_descriptor( @@ -426,8 +432,7 @@ fn best_2d_optimal_format( flags: vk::ImageCreateFlags, ) -> vk::Format { for format in formats { - let format_props = Device::image_format_properties( - device, + let format_props = device.physical_device.image_format_properties( *format, vk::ImageType::TYPE_2D, vk::ImageTiling::OPTIMAL, diff --git a/src/display.rs b/src/display.rs index b7be744e..c87cbb1e 100644 --- a/src/display.rs +++ b/src/display.rs @@ -269,8 +269,7 @@ impl Display { let elapsed = Instant::now() - started; trace!("🔜🔜🔜 vkQueueSubmit took {} μs", elapsed.as_micros(),); - let swapchain_image = - SwapchainImage::clone_swapchain(resolver.swapchain_image(swapchain_image)); + let swapchain_image = resolver.swapchain_image(swapchain_image).clone(); self.swapchain.present_image( swapchain_image, diff --git a/src/driver/accel_struct.rs b/src/driver/accel_struct.rs index 18fe7fa1..c0836ac7 100644 --- a/src/driver/accel_struct.rs +++ b/src/driver/accel_struct.rs @@ -8,7 +8,6 @@ use { std::{ ffi::c_void, mem::{replace, size_of_val}, - ops::Deref, sync::Arc, thread::panicking, }, @@ -40,7 +39,7 @@ use std::sync::Mutex; /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; /// # fn main() -> Result<(), DriverError> { -/// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); +/// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # const SIZE: vk::DeviceSize = 1024; /// # let info = AccelerationStructureInfo::blas(SIZE); /// # let my_accel_struct = AccelerationStructure::create(&device, info)?; @@ -52,61 +51,38 @@ use std::sync::Mutex; /// [deref]: core::ops::Deref /// [fully qualified syntax]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name #[derive(Debug)] -#[repr(C)] +#[readonly::make] pub struct AccelerationStructure { access: Mutex, /// The native Vulkan resource handle of the buffer which supports this acceleration structure. /// /// _Note:_ This field is read-only. - #[cfg(doc)] + #[readonly] pub buffer: Buffer, - #[cfg(not(doc))] - buffer: Buffer, - /// The device which owns this buffer resource. /// /// _Note:_ This field is read-only. - #[cfg(doc)] + #[readonly] pub device: Arc, - #[cfg(not(doc))] - device: Arc, - /// The native Vulkan resource handle of this acceleration structure. /// /// _Note:_ This field is read-only. - #[cfg(doc)] + #[readonly] pub handle: vk::AccelerationStructureKHR, - #[cfg(not(doc))] - handle: vk::AccelerationStructureKHR, - /// Information used to create this object. /// /// _Note:_ This field is read-only. - #[cfg(doc)] + #[readonly] pub info: AccelerationStructureInfo, - #[cfg(not(doc))] - info: AccelerationStructureInfo, - /// A name for debugging purposes. pub name: Option, } -#[doc(hidden)] -#[repr(C)] -pub struct AccelerationStructureRef { - access: Mutex, - pub buffer: Buffer, - pub device: Arc, - pub handle: vk::AccelerationStructureKHR, - pub info: AccelerationStructureInfo, - pub name: Option, -} - impl AccelerationStructure { /// Creates a new acceleration structure on the given device. /// @@ -121,7 +97,7 @@ impl AccelerationStructure { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// const SIZE: vk::DeviceSize = 1024; /// let info = AccelerationStructureInfo::blas(SIZE); /// let accel_struct = AccelerationStructure::create(&device, info)?; @@ -205,7 +181,7 @@ impl AccelerationStructure { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # const SIZE: vk::DeviceSize = 1024; /// # let info = AccelerationStructureInfo::blas(SIZE); /// # let my_accel_struct = AccelerationStructure::create(&device, info)?; @@ -251,7 +227,7 @@ impl AccelerationStructure { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # const SIZE: vk::DeviceSize = 1024; /// # let info = AccelerationStructureInfo::blas(SIZE); /// # let my_accel_struct = AccelerationStructure::create(&device, info)?; @@ -293,7 +269,7 @@ impl AccelerationStructure { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureGeometry, AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, DeviceOrHostAddress}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let my_geom_triangles = AccelerationStructureGeometryData::Triangles { /// # index_addr: DeviceOrHostAddress::DeviceAddress(0), /// # index_type: vk::IndexType::UINT32, @@ -373,15 +349,6 @@ impl AccelerationStructure { } } -#[doc(hidden)] -impl Deref for AccelerationStructure { - type Target = AccelerationStructureRef; - - fn deref(&self) -> &Self::Target { - unsafe { &*(self as *const Self as *const Self::Target) } - } -} - impl Drop for AccelerationStructure { #[profiling::function] fn drop(&mut self) { diff --git a/src/driver/buffer.rs b/src/driver/buffer.rs index 3a5dd49f..64ca6187 100644 --- a/src/driver/buffer.rs +++ b/src/driver/buffer.rs @@ -13,7 +13,7 @@ use { std::{ fmt::{Debug, Formatter}, mem::ManuallyDrop, - ops::{Deref, DerefMut, Range}, + ops::{DerefMut, Range}, sync::Arc, thread::panicking, }, @@ -44,7 +44,7 @@ use std::sync::Mutex; /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # fn main() -> Result<(), DriverError> { -/// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); +/// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let info = BufferInfo::device_mem(8, vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS); /// # let my_buf = Buffer::create(&device, info)?; /// let addr = Buffer::device_address(&my_buf); @@ -54,7 +54,7 @@ use std::sync::Mutex; /// [buffer]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBuffer.html /// [deref]: core::ops::Deref /// [fully qualified syntax]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name -#[repr(C)] +#[readonly::make] pub struct Buffer { accesses: Mutex, allocation: ManuallyDrop, @@ -62,45 +62,25 @@ pub struct Buffer { /// The device which owns this buffer resource. /// /// _Note:_ This field is read-only. - #[cfg(doc)] + #[readonly] pub device: Arc, - #[cfg(not(doc))] - device: Arc, - /// The native Vulkan resource handle of this buffer. /// /// _Note:_ This field is read-only. - #[cfg(doc)] + #[readonly] pub handle: vk::Buffer, - #[cfg(not(doc))] - handle: vk::Buffer, - /// Information used to create this resource. /// /// _Note:_ This field is read-only. - #[cfg(doc)] + #[readonly] pub info: BufferInfo, - #[cfg(not(doc))] - info: BufferInfo, - /// A name for debugging purposes. pub name: Option, } -#[doc(hidden)] -#[repr(C)] -pub struct BufferRef { - accesses: Mutex, - allocation: ManuallyDrop, - pub device: Arc, - pub handle: vk::Buffer, - pub info: BufferInfo, - pub name: Option, -} - impl Buffer { /// Creates a new buffer on the given device. /// @@ -115,7 +95,7 @@ impl Buffer { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// const SIZE: vk::DeviceSize = 1024; /// let info = BufferInfo::host_mem(SIZE, vk::BufferUsageFlags::UNIFORM_BUFFER); /// let buf = Buffer::create(&device, info)?; @@ -232,7 +212,7 @@ impl Buffer { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// const DATA: [u8; 4] = [0xfe, 0xed, 0xbe, 0xef]; /// let buf = Buffer::create_from_slice(&device, vk::BufferUsageFlags::UNIFORM_BUFFER, &DATA)?; /// @@ -277,7 +257,7 @@ impl Buffer { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo, BufferSubresourceRange}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # const SIZE: vk::DeviceSize = 1024; /// # let info = BufferInfo::device_mem(SIZE, vk::BufferUsageFlags::STORAGE_BUFFER); /// # let my_buf = Buffer::create(&device, info)?; @@ -342,7 +322,7 @@ impl Buffer { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let info = BufferInfo::host_mem(4, vk::BufferUsageFlags::empty()); /// # let mut my_buf = Buffer::create(&device, info)?; /// const DATA: [u8; 4] = [0xde, 0xad, 0xc0, 0xde]; @@ -374,7 +354,7 @@ impl Buffer { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let info = BufferInfo::host_mem(4, vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS); /// # let my_buf = Buffer::create(&device, info)?; /// let addr = Buffer::device_address(&my_buf); @@ -414,7 +394,7 @@ impl Buffer { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # const DATA: [u8; 4] = [0; 4]; /// # let my_buf = Buffer::create_from_slice(&device, vk::BufferUsageFlags::empty(), &DATA)?; /// // my_buf is mappable and filled with four zeroes @@ -452,7 +432,7 @@ impl Buffer { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # const DATA: [u8; 4] = [0; 4]; /// # let mut my_buf = Buffer::create_from_slice(&device, vk::BufferUsageFlags::empty(), &DATA)?; /// let mut data = Buffer::mapped_slice_mut(&mut my_buf); @@ -483,15 +463,6 @@ impl Debug for Buffer { } } -#[doc(hidden)] -impl Deref for Buffer { - type Target = BufferRef; - - fn deref(&self) -> &Self::Target { - unsafe { &*(self as *const Self as *const Self::Target) } - } -} - impl Drop for Buffer { #[profiling::function] fn drop(&mut self) { diff --git a/src/driver/compute.rs b/src/driver/compute.rs index f74c6109..6bbc6fea 100644 --- a/src/driver/compute.rs +++ b/src/driver/compute.rs @@ -9,7 +9,7 @@ use { ash::vk, derive_builder::{Builder, UninitializedFieldError}, log::{trace, warn}, - std::{ffi::CString, ops::Deref, sync::Arc, thread::panicking}, + std::{ffi::CString, sync::Arc, thread::panicking}, }; /// Smart pointer handle to a [pipeline] object. @@ -24,7 +24,7 @@ use { /// [pipeline]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipeline.html /// [deref]: core::ops::Deref #[derive(Debug)] -#[repr(C)] +#[readonly::make] pub struct ComputePipeline { pub(crate) descriptor_bindings: DescriptorBindingMap, pub(crate) descriptor_info: PipelineDescriptorInfo, @@ -32,49 +32,27 @@ pub struct ComputePipeline { /// The device which owns this buffer resource. /// /// _Note:_ This field is read-only. - #[cfg(doc)] + #[readonly] pub device: Arc, - #[cfg(not(doc))] - device: Arc, - pub(crate) layout: vk::PipelineLayout, /// The native Vulkan resource handle of this pipeline. /// /// _Note:_ This field is read-only. - #[cfg(doc)] + #[readonly] pub handle: vk::Pipeline, - #[cfg(not(doc))] - handle: vk::Pipeline, - /// Information used to create this object. - #[cfg(doc)] + #[readonly] pub info: ComputePipelineInfo, - #[cfg(not(doc))] - info: ComputePipelineInfo, - /// A descriptive name used in debugging messages. pub name: Option, pub(crate) push_constants: Option, } -#[doc(hidden)] -#[repr(C)] -pub struct ComputePipelineRef { - pub(crate) descriptor_bindings: DescriptorBindingMap, - pub(crate) descriptor_info: PipelineDescriptorInfo, - pub device: Arc, - pub(crate) layout: vk::PipelineLayout, - pub handle: vk::Pipeline, - pub info: ComputePipelineInfo, - pub name: Option, - pub(crate) push_constants: Option, -} - impl ComputePipeline { /// Creates a new compute pipeline on the given device. /// @@ -94,7 +72,7 @@ impl ComputePipeline { /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; /// # use vk_graph::driver::shader::{Shader}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let my_shader_code = [0u8; 1]; /// // my_shader_code is raw SPIR-V code as bytes /// let shader = Shader::new_compute(my_shader_code.as_slice()); @@ -129,7 +107,7 @@ impl ComputePipeline { let descriptor_set_layouts = descriptor_info .layouts .values() - .map(|descriptor_set_layout| **descriptor_set_layout) + .map(|descriptor_set_layout| descriptor_set_layout.handle) .collect::>(); unsafe { @@ -214,15 +192,6 @@ impl ComputePipeline { } } -#[doc(hidden)] -impl Deref for ComputePipeline { - type Target = ComputePipelineRef; - - fn deref(&self) -> &Self::Target { - unsafe { &*(self as *const Self as *const Self::Target) } - } -} - impl Drop for ComputePipeline { #[profiling::function] fn drop(&mut self) { diff --git a/src/driver/descriptor_set.rs b/src/driver/descriptor_set.rs index 06b3c08a..26c5ef99 100644 --- a/src/driver/descriptor_set.rs +++ b/src/driver/descriptor_set.rs @@ -164,7 +164,7 @@ impl DescriptorPool { let mut create_info = vk::DescriptorSetAllocateInfo::default() .descriptor_pool(this.descriptor_pool) - .set_layouts(from_ref(layout)); + .set_layouts(from_ref(&layout.handle)); create_info.descriptor_set_count = count; Ok(unsafe { diff --git a/src/driver/descriptor_set_layout.rs b/src/driver/descriptor_set_layout.rs index 31051ea0..629b2847 100644 --- a/src/driver/descriptor_set_layout.rs +++ b/src/driver/descriptor_set_layout.rs @@ -2,13 +2,14 @@ use { super::{DriverError, device::Device}, ash::vk, log::warn, - std::{ops::Deref, sync::Arc, thread::panicking}, + std::{sync::Arc, thread::panicking}, }; #[derive(Debug)] +#[readonly::make] pub struct DescriptorSetLayout { - device: Arc, - descriptor_set_layout: vk::DescriptorSetLayout, + pub device: Arc, + pub handle: vk::DescriptorSetLayout, } impl DescriptorSetLayout { @@ -18,7 +19,7 @@ impl DescriptorSetLayout { info: &vk::DescriptorSetLayoutCreateInfo, ) -> Result { let device = Arc::clone(device); - let descriptor_set_layout = unsafe { + let handle = unsafe { device .create_descriptor_set_layout(info, None) .map_err(|err| { @@ -28,18 +29,7 @@ impl DescriptorSetLayout { }) }?; - Ok(Self { - device, - descriptor_set_layout, - }) - } -} - -impl Deref for DescriptorSetLayout { - type Target = vk::DescriptorSetLayout; - - fn deref(&self) -> &Self::Target { - &self.descriptor_set_layout + Ok(Self { device, handle }) } } @@ -51,8 +41,7 @@ impl Drop for DescriptorSetLayout { } unsafe { - self.device - .destroy_descriptor_set_layout(self.descriptor_set_layout, None); + self.device.destroy_descriptor_set_layout(self.handle, None); } } } diff --git a/src/driver/device.rs b/src/driver/device.rs index e3ba82ff..1ad0f1a7 100644 --- a/src/driver/device.rs +++ b/src/driver/device.rs @@ -69,7 +69,9 @@ fn select_physical_device( instance: &Instance, mut index: usize, ) -> Result { - let mut physical_devices = Instance::physical_devices(instance)?; + let mut physical_devices = Instance::physical_devices(instance)? + .into_iter() + .collect::>(); if physical_devices.is_empty() { warn!("no physical devices found"); @@ -112,89 +114,19 @@ pub struct Device { swapchain_ext: Option, } -#[doc(hidden)] -#[repr(C)] -pub struct DeviceRef { - accel_struct_ext: Option, - pub(super) allocator: ManuallyDrop>, - device: ash::Device, - pipeline_cache: vk::PipelineCache, - pub physical_device: PhysicalDevice, - pub(crate) queues: Vec>, - ray_trace_ext: Option, - surface_ext: Option, - swapchain_ext: Option, -} - impl Device { - /// Constructs a new device using the given physical device. - #[profiling::function] - pub fn create( - physical_device: PhysicalDevice, - display_window: bool, - ) -> Result { - let device = unsafe { - physical_device.create_ash_device(display_window, |device_create_info| { - physical_device.instance.create_device( - physical_device.handle, - &device_create_info, - None, - ) - }) - } - .map_err(|err| { - error!("unable to create device: {err}"); - - DriverError::Unsupported - })?; - - info!("created {}", physical_device.properties_v1_0.device_name); - - Self::load(physical_device, device, display_window) - } - /// Constructs a new device using the given configuration. #[profiling::function] - pub fn create_headless(info: impl Into) -> Result { + pub fn new(info: impl Into) -> Result { let DeviceInfo { debug, physical_device_index, } = info.into(); let instance_info = InstanceInfoBuilder::default().debug(debug); - let instance = Instance::load(instance_info)?; + let instance = Instance::new(instance_info)?; let physical_device = select_physical_device(&instance, physical_device_index)?; - Self::create(physical_device, false) - } - - /// Constructs a new device using the given configuration. - #[profiling::function] - pub fn create_display( - info: impl Into, - display_handle: impl HasDisplayHandle, - ) -> Result { - let DeviceInfo { - debug, - physical_device_index, - } = info.into(); - let display_handle = display_handle.display_handle().map_err(|err| { - warn!("unable to get display handle: {err}"); - - DriverError::Unsupported - })?; - let extension_names = - enumerate_required_extensions(display_handle.as_raw()).map_err(|err| { - warn!("unable to enumerate window extensions: {err}"); - - DriverError::Unsupported - })?; - let instance_info = InstanceInfoBuilder::default() - .debug(debug) - .extension_names(extension_names); - let instance = Instance::load(instance_info)?; - let physical_device = select_physical_device(&instance, physical_device_index)?; - - Self::create(physical_device, true) + Self::from_physical_device(physical_device) } pub(crate) fn create_fence(this: &Self, signaled: bool) -> Result { @@ -269,10 +201,9 @@ impl Device { /// Loads and existing `ash` Vulkan device that may have been created by other means. #[profiling::function] - pub fn load( - physical_device: PhysicalDevice, + pub fn from_ash_device( device: ash::Device, - display_window: bool, + physical_device: PhysicalDevice, ) -> Result { let debug = physical_device.instance.info.debug; let mut debug_settings = AllocatorDebugSettings::default(); @@ -307,11 +238,12 @@ impl Device { queues.push(queue_family); } - let surface_ext = display_window.then(|| { + let surface_ext = physical_device.display.then(|| { khr::surface::Instance::new(&physical_device.instance.entry, &physical_device.instance) }); - let swapchain_ext = - display_window.then(|| khr::swapchain::Device::new(&physical_device.instance, &device)); + let swapchain_ext = physical_device + .display + .then(|| khr::swapchain::Device::new(&physical_device.instance, &device)); let accel_struct_ext = physical_device .accel_struct_properties .is_some() @@ -342,41 +274,57 @@ impl Device { }) } - /// Lists the physical device's image format capabilities. - /// - /// A result of `None` indicates the format is not supported. + /// Constructs a new device using the given configuration. #[profiling::function] - pub fn image_format_properties( - this: &Self, - format: vk::Format, - ty: vk::ImageType, - tiling: vk::ImageTiling, - usage: vk::ImageUsageFlags, - flags: vk::ImageCreateFlags, - ) -> Result, DriverError> { - unsafe { - match this - .physical_device - .instance - .get_physical_device_image_format_properties( - this.physical_device.handle, - format, - ty, - tiling, - usage, - flags, - ) { - Ok(properties) => Ok(Some(properties)), - Err(err) if err == vk::Result::ERROR_FORMAT_NOT_SUPPORTED => { - // We don't log this condition because it is normal for unsupported - // formats to be checked - we use the result to inform callers they - // cannot use those formats. - - Ok(None) - } - _ => Err(DriverError::OutOfMemory), - } + pub fn from_display( + display_handle: impl HasDisplayHandle, + info: impl Into, + ) -> Result { + let DeviceInfo { + debug, + physical_device_index, + } = info.into(); + let display_handle = display_handle.display_handle().map_err(|err| { + warn!("unable to get display handle: {err}"); + + DriverError::Unsupported + })?; + let extension_names = + enumerate_required_extensions(display_handle.as_raw()).map_err(|err| { + warn!("unable to enumerate window extensions: {err}"); + + DriverError::Unsupported + })?; + let instance_info = InstanceInfoBuilder::default() + .debug(debug) + .extension_names(extension_names); + let instance = Instance::new(instance_info)?; + let physical_device = select_physical_device(&instance, physical_device_index)?; + + Self::from_physical_device(physical_device) + } + + /// Constructs a new device using the given physical device. + #[profiling::function] + pub fn from_physical_device(physical_device: PhysicalDevice) -> Result { + let device = unsafe { + physical_device.create_ash_device(|device_create_info| { + physical_device.instance.create_device( + physical_device.handle, + &device_create_info, + None, + ) + }) } + .map_err(|err| { + error!("unable to create device: {err}"); + + DriverError::Unsupported + })?; + + info!("created {}", physical_device.properties_v1_0.device_name); + + Self::from_ash_device(device, physical_device) } pub(crate) fn pipeline_cache(this: &Self) -> vk::PipelineCache { @@ -438,21 +386,13 @@ impl Debug for Device { #[doc(hidden)] impl Deref for Device { - type Target = DeviceRef; + type Target = ReadOnlyDevice; fn deref(&self) -> &Self::Target { unsafe { &*(self as *const Self as *const Self::Target) } } } -impl Deref for DeviceRef { - type Target = ash::Device; - - fn deref(&self) -> &Self::Target { - &self.device - } -} - impl Drop for Device { #[profiling::function] fn drop(&mut self) { @@ -602,13 +542,77 @@ impl From for DeviceInfoBuilderError { } } +#[doc(hidden)] +#[repr(C)] +pub struct ReadOnlyDevice { + accel_struct_ext: Option, + pub(super) allocator: ManuallyDrop>, + device: ash::Device, + pipeline_cache: vk::PipelineCache, + pub physical_device: PhysicalDevice, + pub(crate) queues: Vec>, + ray_trace_ext: Option, + surface_ext: Option, + swapchain_ext: Option, +} + +impl Deref for ReadOnlyDevice { + type Target = ash::Device; + + fn deref(&self) -> &Self::Target { + &self.device + } +} + #[cfg(test)] mod tests { - use super::*; + use {super::*, std::mem::offset_of}; type Info = DeviceInfo; type Builder = DeviceInfoBuilder; + #[test] + pub fn device_repr_c() { + // HACK: The readonly crate uses a private implementation and so we can't further deref it + // into the native object type. Because of this the ReadOnly part is manually implemented. + assert_eq!( + offset_of!(Device, accel_struct_ext), + offset_of!(ReadOnlyDevice, accel_struct_ext), + ); + assert_eq!( + offset_of!(Device, allocator), + offset_of!(ReadOnlyDevice, allocator), + ); + assert_eq!( + offset_of!(Device, device), + offset_of!(ReadOnlyDevice, device), + ); + assert_eq!( + offset_of!(Device, pipeline_cache), + offset_of!(ReadOnlyDevice, pipeline_cache), + ); + assert_eq!( + offset_of!(Device, physical_device), + offset_of!(ReadOnlyDevice, physical_device), + ); + assert_eq!( + offset_of!(Device, queues), + offset_of!(ReadOnlyDevice, queues), + ); + assert_eq!( + offset_of!(Device, ray_trace_ext), + offset_of!(ReadOnlyDevice, ray_trace_ext), + ); + assert_eq!( + offset_of!(Device, surface_ext), + offset_of!(ReadOnlyDevice, surface_ext), + ); + assert_eq!( + offset_of!(Device, swapchain_ext), + offset_of!(ReadOnlyDevice, swapchain_ext), + ); + } + #[test] pub fn device_info() { Info::default().to_builder().build(); diff --git a/src/driver/graphic.rs b/src/driver/graphic.rs index a32fcf8e..cbbd51b3 100644 --- a/src/driver/graphic.rs +++ b/src/driver/graphic.rs @@ -12,7 +12,7 @@ use { derive_builder::{Builder, UninitializedFieldError}, log::{Level::Trace, log_enabled, trace, warn}, ordered_float::OrderedFloat, - std::{collections::HashSet, ffi::CString, ops::Deref, sync::Arc, thread::panicking}, + std::{collections::HashSet, ffi::CString, sync::Arc, thread::panicking}, }; const RGBA_COLOR_COMPONENTS: vk::ColorComponentFlags = vk::ColorComponentFlags::from_raw( @@ -346,7 +346,7 @@ impl From for DepthStencilModeBuilderError { /// /// [pipeline]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipeline.html #[derive(Debug)] -#[repr(C)] +#[readonly::make] pub struct GraphicPipeline { pub(crate) descriptor_bindings: DescriptorBindingMap, pub(crate) descriptor_info: PipelineDescriptorInfo, @@ -354,19 +354,13 @@ pub struct GraphicPipeline { /// The device which owns this buffer resource. /// /// _Note:_ This field is read-only. - #[cfg(doc)] + #[readonly] pub device: Arc, - #[cfg(not(doc))] - device: Arc, - /// Information used to create this object. - #[cfg(doc)] + #[readonly] pub info: GraphicPipelineInfo, - #[cfg(not(doc))] - info: GraphicPipelineInfo, - pub(crate) input_attachments: Box<[u32]>, pub(crate) layout: vk::PipelineLayout, @@ -378,21 +372,6 @@ pub struct GraphicPipeline { pub(super) state: GraphicPipelineState, } -#[doc(hidden)] -#[repr(C)] -pub struct GraphicPipelineRef { - pub(crate) descriptor_bindings: DescriptorBindingMap, - pub(crate) descriptor_info: PipelineDescriptorInfo, - pub device: Arc, - pub info: GraphicPipelineInfo, - pub(crate) input_attachments: Box<[u32]>, - pub(crate) layout: vk::PipelineLayout, - pub name: Option, - pub(crate) push_constants: Vec, - pub(crate) shader_modules: Vec, - pub(super) state: GraphicPipelineState, -} - impl GraphicPipeline { /// Creates a new graphic pipeline on the given device. /// @@ -415,7 +394,7 @@ impl GraphicPipeline { /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let my_frag_code = [0u8; 1]; /// # let my_vert_code = [0u8; 1]; /// // shader code is raw SPIR-V code as bytes @@ -486,7 +465,7 @@ impl GraphicPipeline { let descriptor_sets_layouts = descriptor_info .layouts .values() - .map(|descriptor_set_layout| **descriptor_set_layout) + .map(|descriptor_set_layout| descriptor_set_layout.handle) .collect::>(); let push_constants = shaders @@ -622,15 +601,6 @@ impl GraphicPipeline { } } -#[doc(hidden)] -impl Deref for GraphicPipeline { - type Target = GraphicPipelineRef; - - fn deref(&self) -> &Self::Target { - unsafe { &*(self as *const Self as *const Self::Target) } - } -} - impl Drop for GraphicPipeline { #[profiling::function] fn drop(&mut self) { @@ -838,7 +808,7 @@ pub(super) struct MultisampleState { pub(super) struct Stage { pub flags: vk::ShaderStageFlags, pub module: vk::ShaderModule, - pub name: CString, + pub name: CString, // TODO pub specialization_info: Option, } diff --git a/src/driver/image.rs b/src/driver/image.rs index 037accbb..6750bd8e 100644 --- a/src/driver/image.rs +++ b/src/driver/image.rs @@ -13,7 +13,7 @@ use { collections::{HashMap, hash_map::Entry}, fmt::{Debug, Formatter}, mem::{replace, take}, - ops::{Deref, DerefMut}, + ops::DerefMut, sync::Arc, thread::panicking, }, @@ -81,7 +81,7 @@ pub(crate) fn image_subresource_range_intersects( /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::image::{Image, ImageInfo}; /// # fn main() -> Result<(), DriverError> { -/// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); +/// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let info = ImageInfo::image_1d(1, vk::Format::R8_UINT, vk::ImageUsageFlags::STORAGE); /// # let my_image = Image::create(&device, info)?; /// # let my_subresource_range = vk::ImageSubresourceRange::default(); @@ -92,7 +92,7 @@ pub(crate) fn image_subresource_range_intersects( /// [image]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImage.html /// [deref]: core::ops::Deref /// [fully qualified syntax]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name -#[repr(C)] +#[readonly::make] pub struct Image { accesses: Mutex>, allocation: Option, // None when we don't own the image (Swapchain images) @@ -100,49 +100,28 @@ pub struct Image { /// The device which owns this image resource. /// /// _Note:_ This field is read-only. - #[cfg(doc)] + #[readonly] pub device: Arc, - #[cfg(not(doc))] - device: Arc, - /// The native Vulkan resource handle of this image. /// /// _Note:_ This field is read-only. - #[cfg(doc)] + #[readonly] pub handle: vk::Image, - #[cfg(not(doc))] - handle: vk::Image, - #[allow(clippy::type_complexity)] image_view_cache: Mutex>, /// Information used to create this resource. /// /// _Note:_ This field is read-only. - #[cfg(doc)] + #[readonly] pub info: ImageInfo, - #[cfg(not(doc))] - info: ImageInfo, - /// A name for debugging purposes. pub name: Option, } -#[doc(hidden)] -#[repr(C)] -pub struct ImageRef { - accesses: Mutex>, - allocation: Option, // None when we don't own the image (Swapchain images) - pub device: Arc, - pub handle: vk::Image, - image_view_cache: Mutex>, - pub info: ImageInfo, - pub name: Option, -} - impl Image { /// Creates a new image on the given device. /// @@ -157,7 +136,7 @@ impl Image { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::image::{Image, ImageInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); /// let image = Image::create(&device, info)?; /// @@ -274,7 +253,7 @@ impl Image { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::image::{Image, ImageInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let info = ImageInfo::image_1d(1, vk::Format::R8_UINT, vk::ImageUsageFlags::STORAGE); /// # let my_image = Image::create(&device, info)?; /// # let my_subresource_range = vk::ImageSubresourceRange::default(); @@ -339,6 +318,8 @@ impl Image { #[profiling::function] pub(super) fn clone_swapchain(&self) -> Self { + debug_assert!(self.allocation.is_none()); + // Moves the image view cache from the current instance to the clone! #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))] let mut image_view_cache = self.image_view_cache.lock(); @@ -351,11 +332,9 @@ impl Image { // Does NOT copy over the image accesses! // Force previous access to general to wait for presentation let Self { handle, info, .. } = *self; - let accesses = ImageAccess::new(info, AccessType::General); - let accesses = Mutex::new(accesses); Self { - accesses, + accesses: Mutex::new(ImageAccess::new(info, AccessType::General)), allocation: None, device: Arc::clone(&self.device), handle, @@ -452,15 +431,6 @@ impl Debug for Image { } } -#[doc(hidden)] -impl Deref for Image { - type Target = ImageRef; - - fn deref(&self) -> &Self::Target { - unsafe { &*(self as *const Self as *const Self::Target) } - } -} - impl Drop for Image { // This function is not profiled because drop_allocation is fn drop(&mut self) { diff --git a/src/driver/instance.rs b/src/driver/instance.rs index d7126d5f..a0074d02 100644 --- a/src/driver/instance.rs +++ b/src/driver/instance.rs @@ -1,4 +1,4 @@ -//! TODO +//! Vulkan initialization types use { super::{DriverError, physical_device::PhysicalDevice}, @@ -158,18 +158,10 @@ pub struct Instance { inner: Arc, } -#[doc(hidden)] -#[repr(C)] -pub struct InstanceRef { - pub entry: ash::Entry, - pub info: InstanceInfo, - inner: Arc, -} - impl Instance { /// Creates a new Vulkan instance. #[profiling::function] - pub fn load(info: impl Into) -> Result { + pub fn new(info: impl Into) -> Result { let info = info.into(); // Required to enable non-uniform descriptor indexing (bindless) @@ -338,11 +330,6 @@ impl Instance { }) } - /// Returns the `ash` entrypoint for Vulkan functions. - pub fn entry(this: &Self) -> &ash::Entry { - &this.entry - } - fn debug_extension_names( #[cfg_attr(target_os = "macos", allow(unused_variables))] debug: bool, ) -> Vec<&'static CStr> { @@ -373,11 +360,6 @@ impl Instance { res } - /// Returns `true` if this instance was created with debug layers enabled. - pub fn is_debug(this: &Self) -> bool { - this.inner.debug_utils.is_some() - } - /// Returns a wrapper structure for a physical device of this instance. #[profiling::function] pub fn physical_device( @@ -403,7 +385,9 @@ impl Instance { /// Returns the available physical devices of this instance. #[profiling::function] - pub fn physical_devices(this: &Self) -> Result, DriverError> { + pub fn physical_devices( + this: &Self, + ) -> Result, DriverError> { let physical_devices = unsafe { this.enumerate_physical_devices() }.map_err(|err| { error!("unable to enumerate physical devices: {err}"); @@ -441,8 +425,7 @@ impl Instance { supports_vulkan_1_2 }) - }) - .collect()) + })) } } @@ -454,22 +437,13 @@ impl Debug for Instance { #[doc(hidden)] impl Deref for Instance { - type Target = InstanceRef; + type Target = ReadOnlyInstance; fn deref(&self) -> &Self::Target { unsafe { &*(self as *const Self as *const Self::Target) } } } -#[doc(hidden)] -impl Deref for InstanceRef { - type Target = ash::Instance; - - fn deref(&self) -> &Self::Target { - &self.inner.instance - } -} - /// Information used to create an [`Instance`] instance. #[derive(Builder, Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] #[builder( @@ -558,6 +532,7 @@ struct InstanceInner { _debug_callback: Option, #[allow(deprecated)] // TODO: Remove? Look into this.... _debug_loader: Option, + #[allow(dead_code)] debug_utils: Option, instance: ash::Instance, } @@ -580,3 +555,43 @@ impl Drop for InstanceInner { } } } + +#[doc(hidden)] +#[repr(C)] +pub struct ReadOnlyInstance { + pub entry: ash::Entry, + pub info: InstanceInfo, + inner: Arc, +} + +#[doc(hidden)] +impl Deref for ReadOnlyInstance { + type Target = ash::Instance; + + fn deref(&self) -> &Self::Target { + &self.inner.instance + } +} + +#[cfg(test)] +mod tests { + use {super::*, std::mem::offset_of}; + + #[test] + pub fn instance_repr_c() { + // HACK: The readonly crate uses a private implementation and so we can't further deref it + // into the native object type. Because of this the ReadOnly part is manually implemented. + assert_eq!( + offset_of!(Instance, entry), + offset_of!(ReadOnlyInstance, entry), + ); + assert_eq!( + offset_of!(Instance, info), + offset_of!(ReadOnlyInstance, info), + ); + assert_eq!( + offset_of!(Instance, inner), + offset_of!(ReadOnlyInstance, inner), + ); + } +} diff --git a/src/driver/mod.rs b/src/driver/mod.rs index d56e5eee..554d0bac 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -1,4 +1,4 @@ -//! [Vulkan 1.2](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/index.html) interface +//! [Vulkan](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/index.html) interface //! based on smart pointers. //! //! # Resources diff --git a/src/driver/physical_device.rs b/src/driver/physical_device.rs index 01a65f06..c612e6b7 100644 --- a/src/driver/physical_device.rs +++ b/src/driver/physical_device.rs @@ -9,7 +9,6 @@ use { ffi::{CStr, c_char}, fmt::{Debug, Formatter}, iter::repeat_n, - ops::Deref, }, }; @@ -140,187 +139,101 @@ impl From> for IndexTypeUint8Fea } /// Structure which holds data about the physical hardware selected by the current device. -#[repr(C)] +#[readonly::make] pub struct PhysicalDevice { /// Describes the properties of the device which relate to acceleration structures, if /// available. /// /// _Note:_ This field is read-only. - #[cfg(doc)] pub accel_struct_properties: Option, - #[cfg(not(doc))] - accel_struct_properties: Option, - /// Describes the properties of the device which relate to depth/stencil resolve operations. /// /// _Note:_ This field is read-only. - #[cfg(doc)] pub depth_stencil_resolve_properties: DepthStencilResolveProperties, - #[cfg(not(doc))] - depth_stencil_resolve_properties: DepthStencilResolveProperties, + /// True if the device may be used for windowed or full-screen display. + /// + /// _Note:_ This field is read-only. + pub display: bool, /// Describes the features of the physical device which are part of the Vulkan 1.0 base feature set. /// /// _Note:_ This field is read-only. - #[cfg(doc)] pub features_v1_0: Vulkan10Features, - #[cfg(not(doc))] - features_v1_0: Vulkan10Features, - /// Describes the features of the physical device which are part of the Vulkan 1.1 base feature set. /// /// _Note:_ This field is read-only. - #[cfg(doc)] pub features_v1_1: Vulkan11Features, - #[cfg(not(doc))] - features_v1_1: Vulkan11Features, - /// Describes the features of the physical device which are part of the Vulkan 1.2 base feature set. /// /// _Note:_ This field is read-only. - #[cfg(doc)] pub features_v1_2: Vulkan12Features, - #[cfg(not(doc))] - features_v1_2: Vulkan12Features, - /// The native Vulkan resource handle of this buffer. /// /// _Note:_ This field is read-only. - #[cfg(doc)] pub handle: vk::PhysicalDevice, - #[cfg(not(doc))] - handle: vk::PhysicalDevice, - /// Describes the features of the physical device which relate to vertex indexing. /// /// _Note:_ This field is read-only. - #[cfg(doc)] pub index_type_uint8_features: IndexTypeUint8Features, - #[cfg(not(doc))] - index_type_uint8_features: IndexTypeUint8Features, - /// The Vulkan instance which owns this device. /// /// _Note:_ This field is read-only. - #[cfg(doc)] pub instance: Instance, - #[cfg(not(doc))] - instance: Instance, - /// Memory properties of the physical device. /// /// _Note:_ This field is read-only. - #[cfg(doc)] pub memory_properties: vk::PhysicalDeviceMemoryProperties, - #[cfg(not(doc))] - memory_properties: vk::PhysicalDeviceMemoryProperties, - /// Device properties of the physical device which are part of the Vulkan 1.0 base feature set. /// /// _Note:_ This field is read-only. - #[cfg(doc)] pub properties_v1_0: Vulkan10Properties, - #[cfg(not(doc))] - properties_v1_0: Vulkan10Properties, - /// Describes the properties of the physical device which are part of the Vulkan 1.1 base /// feature set. /// /// _Note:_ This field is read-only. - #[cfg(doc)] pub properties_v1_1: Vulkan11Properties, - #[cfg(not(doc))] - properties_v1_1: Vulkan11Properties, - /// Describes the properties of the physical device which are part of the Vulkan 1.2 base /// feature set. /// /// _Note:_ This field is read-only. - #[cfg(doc)] pub properties_v1_2: Vulkan12Properties, - #[cfg(not(doc))] - properties_v1_2: Vulkan12Properties, - /// Describes the queues offered by this physical device. /// /// _Note:_ This field is read-only. - #[cfg(doc)] pub queue_families: Box<[vk::QueueFamilyProperties]>, - #[cfg(not(doc))] - queue_families: Box<[vk::QueueFamilyProperties]>, - pub(crate) queue_family_indices: Box<[u32]>, /// Describes the features of the device which relate to ray query, if available. /// /// _Note:_ This field is read-only. - #[cfg(doc)] pub ray_query_features: RayQueryFeatures, - #[cfg(not(doc))] - ray_query_features: RayQueryFeatures, - /// Describes the features of the device which relate to ray tracing, if available. /// /// _Note:_ This field is read-only. - #[cfg(doc)] pub ray_trace_features: RayTraceFeatures, - #[cfg(not(doc))] - ray_trace_features: RayTraceFeatures, - /// Describes the properties of the device which relate to ray tracing, if available. /// /// _Note:_ This field is read-only. - #[cfg(doc)] pub ray_trace_properties: Option, - #[cfg(not(doc))] - ray_trace_properties: Option, - /// Describes the properties of the device which relate to min/max sampler filtering. /// /// _Note:_ This field is read-only. - #[cfg(doc)] - pub sampler_filter_minmax_properties: SamplerFilterMinmaxProperties, - - #[cfg(not(doc))] - sampler_filter_minmax_properties: SamplerFilterMinmaxProperties, -} - -#[allow(missing_docs)] -#[repr(C)] -pub struct PhysicalDeviceRef { - pub accel_struct_properties: Option, - pub depth_stencil_resolve_properties: DepthStencilResolveProperties, - pub features_v1_0: Vulkan10Features, - pub features_v1_1: Vulkan11Features, - pub features_v1_2: Vulkan12Features, - pub handle: vk::PhysicalDevice, - pub index_type_uint8_features: IndexTypeUint8Features, - pub instance: Instance, - pub memory_properties: vk::PhysicalDeviceMemoryProperties, - pub properties_v1_0: Vulkan10Properties, - pub properties_v1_1: Vulkan11Properties, - pub properties_v1_2: Vulkan12Properties, - pub queue_families: Box<[vk::QueueFamilyProperties]>, - pub(crate) queue_family_indices: Box<[u32]>, - pub ray_query_features: RayQueryFeatures, - pub ray_trace_features: RayTraceFeatures, - pub ray_trace_properties: Option, pub sampler_filter_minmax_properties: SamplerFilterMinmaxProperties, } @@ -437,6 +350,8 @@ impl PhysicalDevice { let supports_index_type_uint8 = extensions.contains(ext::index_type_uint8::NAME); let supports_ray_query = extensions.contains(khr::ray_query::NAME); let supports_ray_trace = extensions.contains(khr::ray_tracing_pipeline::NAME); + let supports_surface = extensions.contains(khr::surface::NAME); + let supports_swapchain = extensions.contains(khr::swapchain::NAME); // Gather optional features and properties of the physical device let index_type_uint8_features = if supports_index_type_uint8 { @@ -456,10 +371,12 @@ impl PhysicalDevice { }; let accel_struct_properties = supports_accel_struct.then(|| accel_struct_properties.into()); let ray_trace_properties = supports_ray_trace.then(|| ray_trace_properties.into()); + let display = supports_surface && supports_swapchain; Ok(Self { accel_struct_properties, depth_stencil_resolve_properties, + display, features_v1_0, features_v1_1, features_v1_2, @@ -483,7 +400,7 @@ impl PhysicalDevice { /// to control the device creation process. /// /// _Note:_ This is only useful for interoperting with other libraries as device creation is - /// normally handled by the [`Device::create_display`] and [`Device::create_headless`] + /// normally handled by the [`Device::create_display`] and [`Device::new`] /// functions. /// /// # Safety @@ -491,17 +408,13 @@ impl PhysicalDevice { /// This comes with all the caveats of using `ash` builder types, which are inherently /// dangerous. Use with extreme caution. #[profiling::function] - pub unsafe fn create_ash_device( - &self, - display_window: bool, - create_fn: F, - ) -> ash::prelude::VkResult + pub unsafe fn create_ash_device(&self, create_fn: F) -> ash::prelude::VkResult where F: FnOnce(vk::DeviceCreateInfo) -> ash::prelude::VkResult, { let mut enabled_ext_names = Vec::with_capacity(6); - if display_window { + if self.display { enabled_ext_names.push(khr::swapchain::NAME.as_ptr()); } @@ -595,6 +508,40 @@ impl PhysicalDevice { .get_physical_device_format_properties(self.handle, format) } } + + /// Lists the physical device's image format capabilities. + /// + /// A result of `None` indicates the format is not supported. + #[profiling::function] + pub fn image_format_properties( + &self, + format: vk::Format, + ty: vk::ImageType, + tiling: vk::ImageTiling, + usage: vk::ImageUsageFlags, + flags: vk::ImageCreateFlags, + ) -> Result, DriverError> { + unsafe { + match self.instance.get_physical_device_image_format_properties( + self.handle, + format, + ty, + tiling, + usage, + flags, + ) { + Ok(properties) => Ok(Some(properties)), + Err(err) if err == vk::Result::ERROR_FORMAT_NOT_SUPPORTED => { + // We don't log this condition because it is normal for unsupported + // formats to be checked - we use the result to inform callers they + // cannot use those formats. + + Ok(None) + } + _ => Err(DriverError::OutOfMemory), + } + } + } } impl Debug for PhysicalDevice { @@ -607,15 +554,6 @@ impl Debug for PhysicalDevice { } } -#[doc(hidden)] -impl Deref for PhysicalDevice { - type Target = PhysicalDeviceRef; - - fn deref(&self) -> &Self::Target { - unsafe { &*(self as *const Self as *const Self::Target) } - } -} - /// Features of the physical device for ray query. /// /// See diff --git a/src/driver/ray_trace.rs b/src/driver/ray_trace.rs index 87d34a4a..9e567d05 100644 --- a/src/driver/ray_trace.rs +++ b/src/driver/ray_trace.rs @@ -11,7 +11,7 @@ use { ash::vk, derive_builder::{Builder, UninitializedFieldError}, log::warn, - std::{ffi::CString, ops::Deref, sync::Arc, thread::panicking}, + std::{ffi::CString, sync::Arc, thread::panicking}, }; /// Smart pointer handle to a [pipeline] object. @@ -29,7 +29,7 @@ use { /// [deref]: core::ops::Deref /// [fully qualified syntax]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name #[derive(Debug)] -#[repr(C)] +#[readonly::make] pub struct RayTracePipeline { pub(crate) descriptor_bindings: DescriptorBindingMap, pub(crate) descriptor_info: PipelineDescriptorInfo, @@ -37,28 +37,19 @@ pub struct RayTracePipeline { /// The device which owns this buffer resource. /// /// _Note:_ This field is read-only. - #[cfg(doc)] + #[readonly] pub device: Arc, - #[cfg(not(doc))] - device: Arc, - /// The native Vulkan resource handle of this pipeline. /// /// _Note:_ This field is read-only. - #[cfg(doc)] + #[readonly] pub handle: vk::Pipeline, - #[cfg(not(doc))] - handle: vk::Pipeline, - /// Information used to create this object. - #[cfg(doc)] + #[readonly] pub info: RayTracePipelineInfo, - #[cfg(not(doc))] - info: RayTracePipelineInfo, - pub(crate) layout: vk::PipelineLayout, /// A descriptive name used in debugging messages. @@ -69,21 +60,6 @@ pub struct RayTracePipeline { shader_group_handles: Vec, } -#[doc(hidden)] -#[repr(C)] -pub struct RayTracePipelineRef { - pub(crate) descriptor_bindings: DescriptorBindingMap, - pub(crate) descriptor_info: PipelineDescriptorInfo, - pub device: Arc, - pub handle: vk::Pipeline, - pub info: RayTracePipelineInfo, - pub(crate) layout: vk::PipelineLayout, - pub name: Option, - pub(crate) push_constants: Vec, - shader_modules: Vec, - shader_group_handles: Vec, -} - impl RayTracePipeline { /// Creates a new ray trace pipeline on the given device. /// @@ -109,7 +85,7 @@ impl RayTracePipeline { /// # use vk_graph::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let my_rgen_code = [0u8; 1]; /// # let my_chit_code = [0u8; 1]; /// # let my_miss_code = [0u8; 1]; @@ -179,17 +155,17 @@ impl RayTracePipeline { } let descriptor_info = PipelineDescriptorInfo::create(device, &descriptor_bindings)?; - let descriptor_set_layout_handles = descriptor_info + let layouts = descriptor_info .layouts .values() - .map(|descriptor_set_layout| **descriptor_set_layout) + .map(|layout| layout.handle) .collect::>(); unsafe { let layout = device .create_pipeline_layout( &vk::PipelineLayoutCreateInfo::default() - .set_layouts(&descriptor_set_layout_handles) + .set_layouts(&layouts) .push_constant_ranges(&push_constants), None, ) @@ -396,15 +372,6 @@ impl RayTracePipeline { } } -#[doc(hidden)] -impl Deref for RayTracePipeline { - type Target = RayTracePipelineRef; - - fn deref(&self) -> &Self::Target { - unsafe { &*(self as *const Self as *const Self::Target) } - } -} - impl Drop for RayTracePipeline { #[profiling::function] fn drop(&mut self) { diff --git a/src/driver/shader.rs b/src/driver/shader.rs index fe8fcefa..3165e11e 100644 --- a/src/driver/shader.rs +++ b/src/driver/shader.rs @@ -703,7 +703,7 @@ pub struct Shader { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::shader::{Shader, SpecializationInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let my_shader_code = [0u8; 1]; /// // We instead specify 42 for MY_COUNT: /// let shader = Shader::new_fragment(my_shader_code.as_slice()) diff --git a/src/driver/surface.rs b/src/driver/surface.rs index e9c74fe1..28b911a1 100644 --- a/src/driver/surface.rs +++ b/src/driver/surface.rs @@ -8,38 +8,22 @@ use { raw_window_handle::{HasDisplayHandle, HasWindowHandle}, std::{ fmt::{Debug, Formatter}, - ops::Deref, sync::Arc, thread::panicking, }, }; /// Smart pointer handle to a [`vk::SurfaceKHR`] object. -#[repr(C)] +#[readonly::make] pub struct Surface { /// The device which owns this buffer resource. /// /// _Note:_ This field is read-only. - #[cfg(doc)] pub device: Arc, - #[cfg(not(doc))] - device: Arc, - /// The native Vulkan resource handle of this surface. /// /// _Note:_ This field is read-only. - #[cfg(doc)] - pub handle: vk::SurfaceKHR, - - #[cfg(not(doc))] - handle: vk::SurfaceKHR, -} - -#[doc(hidden)] -#[repr(C)] -pub struct SurfaceRef { - pub device: Arc, pub handle: vk::SurfaceKHR, } @@ -186,15 +170,6 @@ impl Debug for Surface { } } -#[doc(hidden)] -impl Deref for Surface { - type Target = SurfaceRef; - - fn deref(&self) -> &Self::Target { - unsafe { &*(self as *const Self as *const Self::Target) } - } -} - impl Drop for Surface { #[profiling::function] fn drop(&mut self) { diff --git a/src/driver/swapchain.rs b/src/driver/swapchain.rs index 34a4face..fd3e1888 100644 --- a/src/driver/swapchain.rs +++ b/src/driver/swapchain.rs @@ -16,59 +16,31 @@ use { /// Provides the ability to present rendering results to a [`Surface`]. #[derive(Debug)] -#[repr(C)] +#[readonly::make] pub struct Swapchain { /// The device which owns this buffer resource. /// /// _Note:_ This field is read-only. - #[cfg(doc)] pub device: Arc, - #[cfg(not(doc))] - device: Arc, - /// The native Vulkan resource handle of this swapchain. /// /// _Note:_ This field is read-only. - #[cfg(doc)] pub handle: vk::SwapchainKHR, - #[cfg(not(doc))] - handle: vk::SwapchainKHR, - handle_prev: vk::SwapchainKHR, images: Box<[SwapchainImage]>, /// Information used to create this resource. /// /// _Note:_ This field is read-only. - #[cfg(doc)] pub info: SwapchainInfo, - #[cfg(not(doc))] - info: SwapchainInfo, - suboptimal: bool, /// The surface which supports this swapchain. /// /// _Note:_ This field is read-only. - #[cfg(doc)] - pub surface: Surface, - - #[cfg(not(doc))] - surface: Surface, -} - -#[doc(hidden)] -#[repr(C)] -pub struct SwapchainRef { - pub device: Arc, - pub handle: vk::SwapchainKHR, - handle_prev: vk::SwapchainKHR, - images: Box<[SwapchainImage]>, - pub info: SwapchainInfo, - suboptimal: bool, pub surface: Surface, } @@ -134,8 +106,7 @@ impl Swapchain { assert!(image_idx < self.images.len()); - let image = unsafe { self.images.get_unchecked(image_idx) }; - let image = SwapchainImage::clone_swapchain(image); + let image = unsafe { self.images.get_unchecked(image_idx) }.clone(); return Ok(replace( unsafe { self.images.get_unchecked_mut(image_idx) }, @@ -410,21 +381,23 @@ impl Swapchain { continue; } - if Device::image_format_properties( - &self.device, - self.info.surface.format, - vk::ImageType::TYPE_2D, - vk::ImageTiling::OPTIMAL, - usage, - vk::ImageCreateFlags::empty(), - ) - .inspect_err(|err| { - warn!( - "unable to get image format properties: {:?} {:?} {err}", - self.info.surface.format, usage + if self + .device + .physical_device + .image_format_properties( + self.info.surface.format, + vk::ImageType::TYPE_2D, + vk::ImageTiling::OPTIMAL, + usage, + vk::ImageCreateFlags::empty(), ) - })? - .is_none() + .inspect_err(|err| { + warn!( + "unable to get image format properties: {:?} {:?} {err}", + self.info.surface.format, usage + ) + })? + .is_none() { continue; } @@ -461,15 +434,6 @@ impl Swapchain { } } -#[doc(hidden)] -impl Deref for Swapchain { - type Target = SwapchainRef; - - fn deref(&self) -> &Self::Target { - unsafe { &*(self as *const Self as *const Self::Target) } - } -} - impl Drop for Swapchain { #[profiling::function] fn drop(&mut self) { @@ -503,13 +467,13 @@ pub struct SwapchainImage { image_idx: u32, } -impl SwapchainImage { - pub(crate) fn clone_swapchain(this: &Self) -> Self { +impl Clone for SwapchainImage { + fn clone(&self) -> Self { let Self { exec_idx, image, image_idx, - } = this; + } = self; Self { exec_idx: *exec_idx, diff --git a/src/lib.rs b/src/lib.rs index 94ce6536..49599291 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,9 +34,10 @@ fn main() -> Result<(), WindowError> { use vk_graph::driver::{device::{Device, DeviceInfo}, DriverError}; fn main() -> Result<(), DriverError> { - let device = Device::create_headless(DeviceInfo::default())? + let device = Device::new(DeviceInfo::default())?; // Do stuff... + # Ok(()) } ``` @@ -65,7 +66,7 @@ For example, a typical host-mappable buffer: # use vk_graph::driver::device::{Device, DeviceInfo}; # use vk_graph::driver::buffer::{Buffer, BufferInfo}; # fn main() -> Result<(), DriverError> { -# let device = Arc::new(Device::create_headless(DeviceInfo::default())?); +# let device = Arc::new(Device::new(DeviceInfo::default())?); let info = BufferInfo::host_mem(1024, vk::BufferUsageFlags::STORAGE_BUFFER); let my_buf = Buffer::create(&device, info)?; # Ok(()) } @@ -87,7 +88,7 @@ For example, a graphics pipeline: # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; # use vk_graph::driver::shader::Shader; # fn main() -> Result<(), DriverError> { -# let device = Arc::new(Device::create_headless(DeviceInfo::default())?); +# let device = Arc::new(Device::new(DeviceInfo::default())?); # let my_frag_code = [0u8; 1]; # let my_vert_code = [0u8; 1]; // shader code is raw SPIR-V code as bytes @@ -121,7 +122,7 @@ For example, leasing an image: # use vk_graph::pool::{Pool}; # use vk_graph::pool::lazy::{LazyPool}; # fn main() -> Result<(), DriverError> { -# let device = Arc::new(Device::create_headless(DeviceInfo::default())?); +# let device = Arc::new(Device::new(DeviceInfo::default())?); let mut pool = LazyPool::new(&device); let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::STORAGE); @@ -161,12 +162,12 @@ it as a node. Bound nodes may only be used with the graphs they were bound to. N # use vk_graph::pool::{Pool}; # use vk_graph::pool::lazy::{LazyPool}; # fn main() -> Result<(), DriverError> { -# let device = Arc::new(Device::create_headless(DeviceInfo::default())?); +# let device = Arc::new(Device::new(DeviceInfo::default())?); # let info = BufferInfo::host_mem(1024, vk::BufferUsageFlags::STORAGE_BUFFER); # let buffer = Buffer::create(&device, info)?; # let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::STORAGE); # let image = Image::create(&device, info)?; -# let mut graph = RenderGraph::new(); +# let mut graph = RenderGraph::default(); println!("{:?}", buffer); // Buffer println!("{:?}", image); // Image @@ -213,16 +214,16 @@ Example: # use vk_graph::pool::{Pool}; # use vk_graph::pool::lazy::{LazyPool}; # fn main() -> Result<(), DriverError> { -# let device = Arc::new(Device::create_headless(DeviceInfo::default())?); +# let device = Arc::new(Device::new(DeviceInfo::default())?); # let info = BufferInfo::host_mem(1024, vk::BufferUsageFlags::STORAGE_BUFFER); # let buffer = Buffer::create(&device, info)?; # let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::STORAGE); # let image = Image::create(&device, info)?; -let mut graph = RenderGraph::new(); +let mut graph = RenderGraph::default(); let buffer_node = graph.bind_node(buffer); let image_node = graph.bind_node(image); graph - .begin_pass("Do some raw Vulkan or interop with another Vulkan library") + .begin_cmd_buf().with_name("Do some raw Vulkan or interop with another Vulkan library") .record_cmd_buf(move |device, cmd_buf, bindings| unsafe { // I always run first! }) @@ -253,14 +254,14 @@ Pipeline instances may be bound to a [`PassRef`] in order to execute the associa # use vk_graph::driver::shader::{Shader}; # use vk_graph::RenderGraph; # fn main() -> Result<(), DriverError> { -# let device = Arc::new(Device::create_headless(DeviceInfo::default())?); +# let device = Arc::new(Device::new(DeviceInfo::default())?); # let my_shader_code = [0u8; 1]; # let info = ComputePipelineInfo::default(); # let shader = Shader::new_compute(my_shader_code.as_slice()); # let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); -# let mut graph = RenderGraph::new(); +# let mut graph = RenderGraph::default(); graph - .begin_pass("My compute pass") + .begin_cmd_buf().with_name("My compute pass") .bind_pipeline(&my_compute_pipeline) .record_compute(|compute, _| { compute.push_constants(&42u32.to_ne_bytes()) @@ -365,7 +366,6 @@ use { }, crate::driver::{ DescriptorBindingMap, - buffer::Buffer, compute::ComputePipeline, device::Device, format_aspect_mask, format_texel_block_extent, format_texel_block_size, @@ -550,7 +550,7 @@ impl Debug for Execution { struct ExecutionFunction(ExecFn); -#[derive(Debug)] +#[derive(Clone, Debug)] enum ExecutionPipeline { Compute(Arc), Graphic(Arc), @@ -607,20 +607,10 @@ impl ExecutionPipeline { } } -impl Clone for ExecutionPipeline { - fn clone(&self) -> Self { - match self { - Self::Compute(pipeline) => Self::Compute(Arc::clone(pipeline)), - Self::Graphic(pipeline) => Self::Graphic(Arc::clone(pipeline)), - Self::RayTrace(pipeline) => Self::RayTrace(Arc::clone(pipeline)), - } - } -} - #[derive(Debug)] struct Pass { execs: Vec, - name: String, + name: Option, } impl Pass { @@ -632,6 +622,10 @@ impl Pass { .flat_map(|exec| exec.pipeline.as_ref()) .map(|pipeline| &pipeline.descriptor_info().pool_sizes) } + + fn name(&self) -> &str { + self.name.as_deref().unwrap_or("pass") + } } /// A composable graph of render pass operations. @@ -642,37 +636,22 @@ impl Pass { /// [`PassBuilder`](https://github.com/EmbarkStudios/kajiya/blob/main/crates/lib/kajiya-rg/src/pass_builder.rs) /// and /// [`render_graph.cpp`](https://github.com/Themaister/Granite/blob/master/renderer/render_graph.cpp). -#[derive(Debug)] +#[derive(Debug, Default)] pub struct RenderGraph { bindings: Vec, passes: Vec, - - /// Set to true (when in debug mode) in order to get a breakpoint hit where you want. - #[cfg(debug_assertions)] - pub debug: bool, } impl RenderGraph { /// Constructs a new `RenderGraph`. - #[allow(clippy::new_without_default)] + #[deprecated = "use default function instead"] pub fn new() -> Self { - let bindings = vec![]; - let passes = vec![]; - - #[cfg(debug_assertions)] - let debug = false; - - Self { - bindings, - passes, - #[cfg(debug_assertions)] - debug, - } + Default::default() } /// Begins a new pass. - pub fn begin_pass(&mut self, name: impl AsRef) -> PassRef<'_> { - PassRef::new(self, name.as_ref().to_string()) + pub fn begin_cmd_buf(&mut self) -> PassRef<'_> { + PassRef::new(self) } /// Binds a Vulkan acceleration structure, buffer, or image to this graph. @@ -759,7 +738,7 @@ impl RenderGraph { let src_node = src_node.into(); let dst_node = dst_node.into(); - let mut pass = self.begin_pass("blit image"); + let mut pass = self.begin_cmd_buf().with_name("blit image"); for region in regions.as_ref() { pass = pass @@ -791,17 +770,12 @@ impl RenderGraph { ); } }) - .submit_pass() - } - - /// Clear a color image. - pub fn clear_color_image(&mut self, image_node: impl Into) -> &mut Self { - self.clear_color_image_value(image_node, [0, 0, 0, 0]) + .end_cmd_buf() } /// Clear a color image. #[profiling::function] - pub fn clear_color_image_value( + pub fn clear_color_image( &mut self, image_node: impl Into, color_value: impl Into, @@ -811,7 +785,8 @@ impl RenderGraph { let image_info = self.node_info(image_node); let image_view_info = image_info.default_view_info(); - self.begin_pass("clear color") + self.begin_cmd_buf() + .with_name("clear color") .access_node_subrange(image_node, AccessType::TransferWrite, image_view_info) .record_cmd_buf(move |device, cmd_buf, bindings| unsafe { device.cmd_clear_color_image( @@ -824,17 +799,12 @@ impl RenderGraph { &[image_view_info.into()], ); }) - .submit_pass() - } - - /// Clears a depth/stencil image. - pub fn clear_depth_stencil_image(&mut self, image_node: impl Into) -> &mut Self { - self.clear_depth_stencil_image_value(image_node, 1.0, 0) + .end_cmd_buf() } /// Clears a depth/stencil image. #[profiling::function] - pub fn clear_depth_stencil_image_value( + pub fn clear_depth_stencil_image( &mut self, image_node: impl Into, depth: f32, @@ -844,7 +814,8 @@ impl RenderGraph { let image_info = self.node_info(image_node); let image_view_info = image_info.default_view_info(); - self.begin_pass("clear depth/stencil") + self.begin_cmd_buf() + .with_name("clear depth/stencil") .access_node_subrange(image_node, AccessType::TransferWrite, image_view_info) .record_cmd_buf(move |device, cmd_buf, bindings| unsafe { device.cmd_clear_depth_stencil_image( @@ -855,7 +826,7 @@ impl RenderGraph { &[image_view_info.into()], ); }) - .submit_pass() + .end_cmd_buf() } /// Copy data between buffers @@ -904,7 +875,7 @@ impl RenderGraph { #[cfg(debug_assertions)] let (src_size, dst_size) = (self.node_info(src_node).size, self.node_info(dst_node).size); - let mut pass = self.begin_pass("copy buffer"); + let mut pass = self.begin_cmd_buf().with_name("copy buffer"); for region in regions.as_ref() { #[cfg(debug_assertions)] @@ -942,7 +913,7 @@ impl RenderGraph { device.cmd_copy_buffer(cmd_buf, src_buf, dst_buf, regions.as_ref()); } }) - .submit_pass() + .end_cmd_buf() } /// Copy data from a buffer into an image. @@ -999,7 +970,7 @@ impl RenderGraph { let dst_node = dst_node.into(); let dst_info = self.node_info(dst_node); - let mut pass = self.begin_pass("copy buffer to image"); + let mut pass = self.begin_cmd_buf().with_name("copy buffer to image"); for region in regions.as_ref() { let block_bytes_size = format_texel_block_size(dst_info.fmt); @@ -1035,7 +1006,7 @@ impl RenderGraph { ); } }) - .submit_pass() + .end_cmd_buf() } /// Copy all layers of a source image to a destination image. @@ -1098,7 +1069,7 @@ impl RenderGraph { let src_node = src_node.into(); let dst_node = dst_node.into(); - let mut pass = self.begin_pass("copy image"); + let mut pass = self.begin_cmd_buf().with_name("copy image"); for region in regions.as_ref() { pass = pass @@ -1129,7 +1100,7 @@ impl RenderGraph { ); } }) - .submit_pass() + .end_cmd_buf() } /// Copy image data into a buffer. @@ -1188,7 +1159,7 @@ impl RenderGraph { let src_info = self.node_info(src_node); let dst_node = dst_node.into(); - let mut pass = self.begin_pass("copy image to buffer"); + let mut pass = self.begin_cmd_buf().with_name("copy image to buffer"); for region in regions.as_ref() { let block_bytes_size = format_texel_block_size(src_info.fmt); @@ -1224,7 +1195,7 @@ impl RenderGraph { ); } }) - .submit_pass() + .end_cmd_buf() } /// Fill a region of a buffer with a fixed value. @@ -1246,7 +1217,8 @@ impl RenderGraph { ) -> &mut Self { let buffer_node = buffer_node.into(); - self.begin_pass("fill buffer") + self.begin_cmd_buf() + .with_name("fill buffer") .access_node_subrange(buffer_node, AccessType::TransferWrite, region.clone()) .record_cmd_buf(move |device, cmd_buf, bindings| { let buffer = bindings[buffer_node].handle; @@ -1261,7 +1233,7 @@ impl RenderGraph { ); } }) - .submit_pass() + .end_cmd_buf() } /// Returns the index of the first pass which accesses a given node @@ -1288,9 +1260,11 @@ impl RenderGraph { /// `SHADER_DEVICE_ADDRESS` usage flag. pub fn node_device_address(&self, node: impl Into) -> vk::DeviceAddress { let node: AnyBufferNode = node.into(); - let buffer = self.bindings[node.index()].as_driver_buffer().unwrap(); - Buffer::device_address(buffer) + self.bindings[node.index()] + .as_driver_buffer() + .unwrap() + .device_address() } /// Returns information used to crate a node. @@ -1324,18 +1298,9 @@ impl RenderGraph { node.unbind(self) } - /// Note: `data` must not exceed 65536 bytes. - pub fn update_buffer( - &mut self, - buffer_node: impl Into, - data: impl AsRef<[u8]> + 'static + Send, - ) -> &mut Self { - self.update_buffer_offset(buffer_node, 0, data) - } - /// Note: `data` must not exceed 65536 bytes. #[profiling::function] - pub fn update_buffer_offset( + pub fn update_buffer( &mut self, buffer_node: impl Into, offset: vk::DeviceSize, @@ -1355,7 +1320,8 @@ impl RenderGraph { ); } - self.begin_pass("update buffer") + self.begin_cmd_buf() + .with_name("update buffer") .access_node_subrange(buffer_node, AccessType::TransferWrite, offset..data_end) .record_cmd_buf(move |device, cmd_buf, bindings| { let buffer = bindings[buffer_node].handle; @@ -1364,6 +1330,6 @@ impl RenderGraph { device.cmd_update_buffer(cmd_buf, buffer, offset, data.as_ref()); } }) - .submit_pass() + .end_cmd_buf() } } diff --git a/src/mod.rs b/src/mod.rs deleted file mode 100644 index 32ba0c6b..00000000 --- a/src/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! Rendering operations and command submission. -//! -//! diff --git a/src/pass_ref.rs b/src/pass_ref.rs index cb3420f8..d9a1c179 100644 --- a/src/pass_ref.rs +++ b/src/pass_ref.rs @@ -66,10 +66,10 @@ pub type DescriptorSetIndex = u32; /// # use vk_graph::RenderGraph; /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { -/// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); -/// # let mut my_graph = RenderGraph::new(); +/// # let device = Arc::new(Device::new(DeviceInfo::default())?); +/// # let mut my_graph = RenderGraph::default(); /// # let info = AccelerationStructureInfo::blas(1); -/// my_graph.begin_pass("my acceleration pass") +/// my_graph.begin_cmd_buf().with_name("my acceleration pass") /// .record_acceleration(move |acceleration, bindings| { /// // During this closure we have access to the acceleration methods! /// }); @@ -106,8 +106,8 @@ impl Acceleration<'_> { /// # use vk_graph::RenderGraph; /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); - /// # let mut my_graph = RenderGraph::new(); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let mut my_graph = RenderGraph::default(); /// # let info = AccelerationStructureInfo::blas(1); /// # let blas_accel_struct = AccelerationStructure::create(&device, info)?; /// # let blas_node = my_graph.bind_node(blas_accel_struct); @@ -120,7 +120,7 @@ impl Acceleration<'_> { /// # let my_vtx_buf = Buffer::create(&device, buf_info)?; /// # let index_node = my_graph.bind_node(my_idx_buf); /// # let vertex_node = my_graph.bind_node(my_vtx_buf); - /// my_graph.begin_pass("my acceleration pass") + /// my_graph.begin_cmd_buf().with_name("my acceleration pass") /// .read_node(index_node) /// .read_node(vertex_node) /// .write_node(blas_node) @@ -1027,12 +1027,12 @@ bind!(RayTrace); /// # use vk_graph::RenderGraph; /// # use vk_graph::node::ImageNode; /// # fn main() -> Result<(), DriverError> { -/// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); +/// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); /// # let image = Image::create(&device, info)?; -/// # let mut my_graph = RenderGraph::new(); +/// # let mut my_graph = RenderGraph::default(); /// # let my_image_node = my_graph.bind_node(image); -/// my_graph.begin_pass("custom vulkan commands") +/// my_graph.begin_cmd_buf().with_name("custom vulkan commands") /// .record_cmd_buf(move |device, cmd_buf, bindings| { /// let my_image = &bindings[my_image_node]; /// @@ -1166,12 +1166,12 @@ impl Index for Bindings<'_> { /// # use vk_graph::driver::shader::{Shader}; /// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { -/// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); +/// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let info = ComputePipelineInfo::default(); /// # let shader = Shader::new_compute([0u8; 1].as_slice()); /// # let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); -/// # let mut my_graph = RenderGraph::new(); -/// my_graph.begin_pass("my compute pass") +/// # let mut my_graph = RenderGraph::default(); +/// my_graph.begin_cmd_buf().with_name("my compute pass") /// .bind_pipeline(&my_compute_pipeline) /// .record_compute(move |compute, bindings| { /// // During this closure we have access to the compute methods! @@ -1220,15 +1220,15 @@ impl Compute<'_> { /// # use vk_graph::driver::shader::{Shader}; /// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::STORAGE_BUFFER); /// # let my_buf = Buffer::create(&device, buf_info)?; /// # let info = ComputePipelineInfo::default(); /// # let shader = Shader::new_compute([0u8; 1].as_slice()); /// # let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); - /// # let mut my_graph = RenderGraph::new(); + /// # let mut my_graph = RenderGraph::default(); /// # let my_buf_node = my_graph.bind_node(my_buf); - /// my_graph.begin_pass("fill my_buf_node with data") + /// my_graph.begin_cmd_buf().with_name("fill my_buf_node with data") /// .bind_pipeline(&my_compute_pipeline) /// .write_descriptor(0, my_buf_node) /// .record_compute(move |compute, bindings| { @@ -1306,13 +1306,13 @@ impl Compute<'_> { /// # use vk_graph::driver::shader::{Shader}; /// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::STORAGE_BUFFER); /// # let my_buf = Buffer::create(&device, buf_info)?; /// # let info = ComputePipelineInfo::default(); /// # let shader = Shader::new_compute([0u8; 1].as_slice()); /// # let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); - /// # let mut my_graph = RenderGraph::new(); + /// # let mut my_graph = RenderGraph::default(); /// # let my_buf_node = my_graph.bind_node(my_buf); /// const CMD_SIZE: usize = size_of::(); /// @@ -1329,7 +1329,7 @@ impl Compute<'_> { /// let args_buf = Buffer::create_from_slice(&device, args_buf_flags, cmd_data)?; /// let args_buf_node = my_graph.bind_node(args_buf); /// - /// my_graph.begin_pass("fill my_buf_node with data") + /// my_graph.begin_cmd_buf().with_name("fill my_buf_node with data") /// .bind_pipeline(&my_compute_pipeline) /// .read_node(args_buf_node) /// .write_descriptor(0, my_buf_node) @@ -1406,12 +1406,12 @@ impl Compute<'_> { /// # use vk_graph::driver::shader::{Shader}; /// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let info = ComputePipelineInfo::default(); /// # let shader = Shader::new_compute([0u8; 1].as_slice()); /// # let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); - /// # let mut my_graph = RenderGraph::new(); - /// my_graph.begin_pass("compute the ultimate question") + /// # let mut my_graph = RenderGraph::default(); + /// my_graph.begin_cmd_buf().with_name("compute the ultimate question") /// .bind_pipeline(&my_compute_pipeline) /// .record_compute(move |compute, bindings| { /// compute.push_constants(&[42]) @@ -1468,12 +1468,12 @@ impl Compute<'_> { /// # use vk_graph::driver::shader::{Shader}; /// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let info = ComputePipelineInfo::default(); /// # let shader = Shader::new_compute([0u8; 1].as_slice()); /// # let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); - /// # let mut my_graph = RenderGraph::new(); - /// my_graph.begin_pass("calculate the wow factor") + /// # let mut my_graph = RenderGraph::default(); + /// my_graph.begin_cmd_buf().with_name("calculate the wow factor") /// .bind_pipeline(&my_compute_pipeline) /// .record_compute(move |compute, bindings| { /// compute.push_constants(&[0x00, 0x00]) @@ -1603,17 +1603,17 @@ impl From<(DescriptorSetIndex, BindingIndex, [BindingOffset; 1])> for Descriptor /// # use vk_graph::RenderGraph; /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { -/// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); +/// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let my_frag_code = [0u8; 1]; /// # let my_vert_code = [0u8; 1]; /// # let vert = Shader::new_vertex(my_vert_code.as_slice()); /// # let frag = Shader::new_fragment(my_frag_code.as_slice()); /// # let info = GraphicPipelineInfo::default(); /// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); -/// # let mut my_graph = RenderGraph::new(); +/// # let mut my_graph = RenderGraph::default(); /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); /// # let swapchain_image = my_graph.bind_node(Image::create(&device, info)?); -/// my_graph.begin_pass("my draw pass") +/// my_graph.begin_cmd_buf().with_name("my draw pass") /// .bind_pipeline(&my_graphic_pipeline) /// .store_color(0, swapchain_image) /// .record_subpass(move |subpass, bindings| { @@ -1646,14 +1646,14 @@ impl Draw<'_> { /// # use vk_graph::driver::shader::Shader; /// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let my_frag_code = [0u8; 1]; /// # let my_vert_code = [0u8; 1]; /// # let vert = Shader::new_vertex(my_vert_code.as_slice()); /// # let frag = Shader::new_fragment(my_frag_code.as_slice()); /// # let info = GraphicPipelineInfo::default(); /// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); - /// # let mut my_graph = RenderGraph::new(); + /// # let mut my_graph = RenderGraph::default(); /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); /// # let swapchain_image = my_graph.bind_node(Image::create(&device, info)?); /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::INDEX_BUFFER); @@ -1662,7 +1662,7 @@ impl Draw<'_> { /// # let my_vtx_buf = Buffer::create(&device, buf_info)?; /// # let my_idx_buf = my_graph.bind_node(my_idx_buf); /// # let my_vtx_buf = my_graph.bind_node(my_vtx_buf); - /// my_graph.begin_pass("my indexed geometry draw pass") + /// my_graph.begin_cmd_buf().with_name("my indexed geometry draw pass") /// .bind_pipeline(&my_graphic_pipeline) /// .store_color(0, swapchain_image) /// .read_node(my_idx_buf) @@ -1724,7 +1724,7 @@ impl Draw<'_> { /// # use vk_graph::driver::shader::Shader; /// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::VERTEX_BUFFER); /// # let my_vtx_buf = Buffer::create(&device, buf_info)?; /// # let my_frag_code = [0u8; 1]; @@ -1733,11 +1733,11 @@ impl Draw<'_> { /// # let frag = Shader::new_fragment(my_frag_code.as_slice()); /// # let info = GraphicPipelineInfo::default(); /// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); - /// # let mut my_graph = RenderGraph::new(); + /// # let mut my_graph = RenderGraph::default(); /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); /// # let swapchain_image = my_graph.bind_node(Image::create(&device, info)?); /// # let my_vtx_buf = my_graph.bind_node(my_vtx_buf); - /// my_graph.begin_pass("my unindexed geometry draw pass") + /// my_graph.begin_cmd_buf().with_name("my unindexed geometry draw pass") /// .bind_pipeline(&my_graphic_pipeline) /// .store_color(0, swapchain_image) /// .read_node(my_vtx_buf) @@ -1903,14 +1903,14 @@ impl Draw<'_> { /// # use vk_graph::driver::shader::Shader; /// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let my_frag_code = [0u8; 1]; /// # let my_vert_code = [0u8; 1]; /// # let vert = Shader::new_vertex(my_vert_code.as_slice()); /// # let frag = Shader::new_fragment(my_frag_code.as_slice()); /// # let info = GraphicPipelineInfo::default(); /// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); - /// # let mut my_graph = RenderGraph::new(); + /// # let mut my_graph = RenderGraph::default(); /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::INDEX_BUFFER); /// # let my_idx_buf = Buffer::create(&device, buf_info)?; /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::VERTEX_BUFFER); @@ -1936,7 +1936,7 @@ impl Draw<'_> { /// let buf = Buffer::create_from_slice(&device, buf_flags, cmd_data)?; /// let buf_node = my_graph.bind_node(buf); /// - /// my_graph.begin_pass("draw a single triangle") + /// my_graph.begin_cmd_buf().with_name("draw a single triangle") /// .bind_pipeline(&my_graphic_pipeline) /// .store_color(0, swapchain_image) /// .read_node(my_idx_buf) @@ -2114,7 +2114,7 @@ impl Draw<'_> { /// # use vk_graph::RenderGraph; /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let my_frag_code = [0u8; 1]; /// # let my_vert_code = [0u8; 1]; /// # let vert = Shader::new_vertex(my_vert_code.as_slice()); @@ -2123,9 +2123,9 @@ impl Draw<'_> { /// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); /// # let swapchain_image = Image::create(&device, info)?; - /// # let mut my_graph = RenderGraph::new(); + /// # let mut my_graph = RenderGraph::default(); /// # let swapchain_image = my_graph.bind_node(swapchain_image); - /// my_graph.begin_pass("draw a quad") + /// my_graph.begin_cmd_buf().with_name("draw a quad") /// .bind_pipeline(&my_graphic_pipeline) /// .store_color(0, swapchain_image) /// .record_subpass(move |subpass, bindings| { @@ -2183,7 +2183,7 @@ impl Draw<'_> { /// # use vk_graph::RenderGraph; /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let my_frag_code = [0u8; 1]; /// # let my_vert_code = [0u8; 1]; /// # let vert = Shader::new_vertex(my_vert_code.as_slice()); @@ -2192,9 +2192,9 @@ impl Draw<'_> { /// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); /// # let swapchain_image = Image::create(&device, info)?; - /// # let mut my_graph = RenderGraph::new(); + /// # let mut my_graph = RenderGraph::default(); /// # let swapchain_image = my_graph.bind_node(swapchain_image); - /// my_graph.begin_pass("draw a quad") + /// my_graph.begin_cmd_buf().with_name("draw a quad") /// .bind_pipeline(&my_graphic_pipeline) /// .store_color(0, swapchain_image) /// .record_subpass(move |subpass, bindings| { @@ -2355,11 +2355,11 @@ pub struct PassRef<'a> { } impl<'a> PassRef<'a> { - pub(super) fn new(graph: &'a mut RenderGraph, name: String) -> PassRef<'a> { + pub(super) fn new(graph: &'a mut RenderGraph) -> PassRef<'a> { let pass_idx = graph.passes.len(); graph.passes.push(Pass { execs: vec![Default::default()], // We start off with a default execution! - name, + name: None, }); Self { @@ -2474,6 +2474,17 @@ impl<'a> PassRef<'a> { binding.bind(self) } + /// Finalize the recording of this pass and return to the `RenderGraph` where you may record + /// additional passes. + pub fn end_cmd_buf(self) -> &'a mut RenderGraph { + // If nothing was done in this pass we can just ignore it + if self.exec_idx == 0 { + self.graph.passes.pop(); + } + + self.graph + } + /// Returns information used to crate a node. pub fn node_info(&self, node: N) -> ::Info where @@ -2576,15 +2587,14 @@ impl<'a> PassRef<'a> { self } - /// Finalize the recording of this pass and return to the `RenderGraph` where you may record - /// additional passes. - pub fn submit_pass(self) -> &'a mut RenderGraph { - // If nothing was done in this pass we can just ignore it - if self.exec_idx == 0 { - self.graph.passes.pop(); + /// Sets a debugging name, but only in debug builds + pub fn with_name(mut self, name: impl Into) -> Self { + #[cfg(debug_assertions)] + { + self.as_mut().name = Some(name.into()); } - self.graph + self } /// Informs the pass that the next recorded command buffer will write the given `node` using @@ -2780,6 +2790,11 @@ where self.pass.graph.bind_node(binding) } + /// Finalizes a pass and returns the render graph so that additional passes may be added. + pub fn end_cmd_buf(self) -> &'a mut RenderGraph { + self.pass.end_cmd_buf() + } + /// Returns information used to crate a node. pub fn node_info(&self, node: N) -> ::Info where @@ -2929,11 +2944,6 @@ where self.access_node_subrange_mut(node, access, subresource); } - /// Finalizes a pass and returns the render graph so that additional passes may be added. - pub fn submit_pass(self) -> &'a mut RenderGraph { - self.pass.submit_pass() - } - /// Informs the pass that the next recorded command buffer will write the given `node` at the /// specified shader descriptor. /// @@ -4693,15 +4703,15 @@ impl PipelinePassRef<'_, RayTracePipeline> { /// # use vk_graph::driver::shader::Shader; /// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { -/// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); +/// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let info = RayTracePipelineInfo::default(); /// # let my_miss_code = [0u8; 1]; /// # let my_ray_trace_pipeline = Arc::new(RayTracePipeline::create(&device, info, /// [Shader::new_miss(my_miss_code.as_slice())], /// [RayTraceShaderGroup::new_general(0)], /// )?); -/// # let mut my_graph = RenderGraph::new(); -/// my_graph.begin_pass("my ray trace pass") +/// # let mut my_graph = RenderGraph::default(); +/// my_graph.begin_cmd_buf().with_name("my ray trace pass") /// .bind_pipeline(&my_ray_trace_pipeline) /// .record_ray_trace(move |ray_trace, bindings| { /// // During this closure we have access to the ray trace methods! @@ -4764,7 +4774,7 @@ impl RayTrace<'_> { /// # use vk_graph::driver::shader::Shader; /// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let shader = [0u8; 1]; /// # let info = RayTracePipelineInfo::default(); /// # let my_miss_code = [0u8; 1]; @@ -4776,8 +4786,8 @@ impl RayTrace<'_> { /// # let hit_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; /// # let miss_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; /// # let call_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; - /// # let mut my_graph = RenderGraph::new(); - /// my_graph.begin_pass("draw a cornell box") + /// # let mut my_graph = RenderGraph::default(); + /// my_graph.begin_cmd_buf().with_name("draw a cornell box") /// .bind_pipeline(&my_ray_trace_pipeline) /// .record_ray_trace(move |ray_trace, bindings| { /// ray_trace.push_constants(&[0xcb]) @@ -4835,7 +4845,7 @@ impl RayTrace<'_> { /// # use vk_graph::driver::shader::Shader; /// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let shader = [0u8; 1]; /// # let info = RayTracePipelineInfo::default(); /// # let my_miss_code = [0u8; 1]; @@ -4847,8 +4857,8 @@ impl RayTrace<'_> { /// # let hit_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; /// # let miss_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; /// # let call_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; - /// # let mut my_graph = RenderGraph::new(); - /// my_graph.begin_pass("draw a cornell box") + /// # let mut my_graph = RenderGraph::default(); + /// my_graph.begin_cmd_buf().with_name("draw a cornell box") /// .bind_pipeline(&my_ray_trace_pipeline) /// .record_ray_trace(move |ray_trace, bindings| { /// ray_trace.push_constants(&[0xcb, 0xff]) @@ -4928,7 +4938,7 @@ impl RayTrace<'_> { /// # use vk_graph::driver::shader::Shader; /// # use vk_graph::RenderGraph; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); /// # let shader = [0u8; 1]; /// # let info = RayTracePipelineInfo::default(); /// # let my_miss_code = [0u8; 1]; @@ -4940,8 +4950,8 @@ impl RayTrace<'_> { /// # let hit_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; /// # let miss_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; /// # let call_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; - /// # let mut my_graph = RenderGraph::new(); - /// my_graph.begin_pass("draw a cornell box") + /// # let mut my_graph = RenderGraph::default(); + /// my_graph.begin_cmd_buf().with_name("draw a cornell box") /// .bind_pipeline(&my_ray_trace_pipeline) /// .record_ray_trace(move |ray_trace, bindings| { /// ray_trace.trace_rays(&rgen_sbt, &hit_sbt, &miss_sbt, &call_sbt, 320, 200, 1); diff --git a/src/pool/mod.rs b/src/pool/mod.rs index dcee1e1a..357f590e 100644 --- a/src/pool/mod.rs +++ b/src/pool/mod.rs @@ -31,7 +31,7 @@ //! # use vk_graph::pool::{Pool}; //! # use vk_graph::pool::lazy::{LazyPool}; //! # fn main() -> Result<(), DriverError> { -//! # let device = Arc::new(Device::create_headless(DeviceInfo::default())?); +//! # let device = Arc::new(Device::new(DeviceInfo::default())?); //! let mut pool = LazyPool::new(&device); //! //! let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::STORAGE); diff --git a/src/resolver.rs b/src/resolver.rs index 4cd70797..b40dc811 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -204,14 +204,14 @@ impl Resolver { let lhs_pipeline = first_graphic_pipeline(lhs); if lhs_pipeline.is_none() { - trace!(" {} is not graphic", lhs.name,); + trace!(" {} is not graphic", lhs.name()); return false; } let rhs_pipeline = first_graphic_pipeline(rhs); if rhs_pipeline.is_none() { - trace!(" {} is not graphic", rhs.name,); + trace!(" {} is not graphic", rhs.name()); return false; } @@ -1669,7 +1669,7 @@ impl Resolver { // only care about one pass at a time here let pass = &mut self.graph.passes[pass_idx]; - trace!("leasing [{pass_idx}: {}]", pass.name); + trace!("leasing [{pass_idx}: {}]", pass.name()); let descriptor_pool = Self::lease_descriptor_pool(pool, pass)?; let mut exec_descriptor_sets = HashMap::with_capacity( @@ -1770,7 +1770,8 @@ impl Resolver { debug!( "attempting to merge [{idx}: {}] with [{end}: {}]", - pass.name, other.name + pass.name(), + other.name() ); if Self::allow_merge_passes(&pass, other) { @@ -1781,30 +1782,38 @@ impl Resolver { } if log_enabled!(Trace) && start != end { - trace!("merging {} passes into [{idx}: {}]", end - start, pass.name); + trace!( + "merging {} passes into [{idx}: {}]", + end - start, + pass.name() + ); } + let mut name = pass.name.take().unwrap_or_default(); + // Grow the merged pass once, not per merge { let mut name_additional = 0; let mut execs_additional = 0; for idx in start..end { let other = passes[schedule[idx]].as_ref().unwrap(); - name_additional += other.name.len() + 3; + name_additional += other.name().len() + 3; execs_additional += other.execs.len(); } - pass.name.reserve(name_additional); + name.reserve(name_additional); pass.execs.reserve(execs_additional); } for idx in start..end { let mut other = passes[schedule[idx]].take().unwrap(); - pass.name.push_str(" + "); - pass.name.push_str(other.name.as_str()); + name.push_str(" + "); + name.push_str(other.name()); pass.execs.append(&mut other.execs); } + pass.name = Some(name); + self.graph.passes.push(pass); idx += 1 + end - start; } @@ -2395,11 +2404,11 @@ impl Resolver { return Ok(()); } - // Print some handy details or hit a breakpoint if you set the flag - #[cfg(debug_assertions)] - if log_enabled!(Debug) && self.graph.debug { - debug!("resolving the following graph:\n\n{:#?}\n\n", self.graph); - } + // // Print some handy details or hit a breakpoint if you set the flag + // #[cfg(debug_assertions)] + // if log_enabled!(Debug) && self.graph.debug { + // debug!("resolving the following graph:\n\n{:#?}\n\n", self.graph); + // } debug_assert!( schedule.passes.windows(2).all(|w| w[0] <= w[1]), @@ -2419,7 +2428,7 @@ impl Resolver { let physical_pass = &mut self.physical_passes[pass_idx]; let is_graphic = physical_pass.render_pass.is_some(); - trace!("recording pass [{}: {}]", pass_idx, pass.name); + trace!("recording pass [{}: {}]", pass_idx, pass.name()); if !physical_pass.exec_descriptor_sets.is_empty() { Self::write_descriptor_sets(cmd_buf, &self.graph.bindings, pass, physical_pass)?; @@ -2726,7 +2735,7 @@ impl Resolver { { trace!( " pass [{pass_idx}: {}] is dependent", - self.graph.passes[pass_idx].name + self.graph.passes[pass_idx].name() ); debug_assert!(unscheduled[pass_idx]); @@ -2762,7 +2771,7 @@ impl Resolver { trace!( " pass [{pass_idx}: {}] is dependent", - self.graph.passes[pass_idx].name + self.graph.passes[pass_idx].name() ); for node_idx in schedule.access_cache.dependent_nodes(pass_idx) { @@ -2789,7 +2798,7 @@ impl Resolver { .passes .iter() .copied() - .map(|idx| format!("[{}: {}]", idx, self.graph.passes[idx].name)) + .map(|idx| format!("[{}: {}]", idx, self.graph.passes[idx].name())) .collect::>() .join(", ") ); @@ -2808,7 +2817,7 @@ impl Resolver { unscheduled .iter() .copied() - .map(|idx| format!("[{}: {}]", idx, self.graph.passes[idx].name)) + .map(|idx| format!("[{}: {}]", idx, self.graph.passes[idx].name())) .collect::>() .join(", ") ); @@ -2825,7 +2834,7 @@ impl Resolver { .map(|(idx, pass)| format!( "[{}: {}]", idx + end_pass_idx, - pass.name + pass.name() )) .collect::>() .join(", ") @@ -3005,7 +3014,7 @@ impl Resolver { let (descriptor_info, _) = pipeline .descriptor_bindings() .get(&Descriptor { set: descriptor_set_idx, binding: dst_binding }) - .unwrap_or_else(|| panic!("descriptor {descriptor_set_idx}.{dst_binding}[{binding_offset}] specified in recorded execution of pass \"{}\" was not discovered through shader reflection", &pass.name)); + .unwrap_or_else(|| panic!("descriptor {descriptor_set_idx}.{dst_binding}[{binding_offset}] specified in recorded execution of pass \"{}\" was not discovered through shader reflection", pass.name())); let descriptor_type = descriptor_info.descriptor_type(); let bound_node = &bindings[*node_idx]; if let Some(image) = bound_node.as_driver_image() { From a401f60da5ea67d180bdaaa36a9dd29a4753ae6d Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 19 Feb 2026 08:14:14 -0500 Subject: [PATCH 12/86] Rename RenderGraph -> Graph and Pass -> Command --- README.md | 14 +- contrib/vk-graph-egui/src/lib.rs | 23 +- contrib/vk-graph-fx/src/bitmap_font.rs | 14 +- contrib/vk-graph-fx/src/image_loader.rs | 8 +- contrib/vk-graph-fx/src/presenter.rs | 23 +- contrib/vk-graph-fx/src/transition.rs | 8 +- contrib/vk-graph-hot/examples/glsl.rs | 4 +- contrib/vk-graph-hot/examples/hlsl.rs | 4 +- contrib/vk-graph-imgui/src/lib.rs | 15 +- contrib/vk-graph-prelude/src/lib.rs | 3 +- contrib/vk-graph-window/src/frame.rs | 4 +- contrib/vk-graph-window/src/lib.rs | 4 +- examples/aliasing.rs | 2 +- examples/app.rs | 4 +- examples/bindless.rs | 8 +- examples/cpu_readback.rs | 2 +- examples/debugger.rs | 4 +- examples/font_bmp.rs | 4 +- examples/fuzzer.rs | 148 +- examples/image_sampler.rs | 8 +- examples/min_max.rs | 12 +- examples/mip_compute.rs | 6 +- examples/mip_graphic.rs | 14 +- examples/msaa.rs | 6 +- examples/multipass.rs | 26 +- examples/multithread.rs | 4 +- examples/ray_omni.rs | 16 +- examples/ray_trace.rs | 14 +- examples/rt_triangle.rs | 14 +- examples/shader-toy/src/main.rs | 22 +- examples/skeletal-anim/src/main.rs | 12 +- examples/subgroup_ops.rs | 10 +- examples/triangle.rs | 11 +- examples/vertex_layout.rs | 6 +- examples/vr/src/main.rs | 24 +- examples/vsm_omni.rs | 34 +- src/{binding.rs => bind.rs} | 34 +- src/cmd_ref/accel.rs | 865 ++++ src/cmd_ref/bind.rs | 0 src/cmd_ref/compute.rs | 415 ++ src/cmd_ref/graphic.rs | 2339 ++++++++++ src/cmd_ref/mod.rs | 681 +++ src/cmd_ref/pipeline.rs | 463 ++ src/cmd_ref/ray_trace.rs | 386 ++ src/cmd_ref/view.rs | 124 + src/display.rs | 4 +- src/edge.rs | 26 +- src/info.rs | 30 +- src/lib.rs | 101 +- src/node.rs | 38 +- src/pass_ref.rs | 5185 ----------------------- src/resolver.rs | 84 +- src/swapchain.rs | 6 +- 53 files changed, 5706 insertions(+), 5610 deletions(-) rename src/{binding.rs => bind.rs} (88%) create mode 100644 src/cmd_ref/accel.rs create mode 100644 src/cmd_ref/bind.rs create mode 100644 src/cmd_ref/compute.rs create mode 100644 src/cmd_ref/graphic.rs create mode 100644 src/cmd_ref/mod.rs create mode 100644 src/cmd_ref/pipeline.rs create mode 100644 src/cmd_ref/ray_trace.rs create mode 100644 src/cmd_ref/view.rs delete mode 100644 src/pass_ref.rs diff --git a/README.md b/README.md index 871d3815..14523bc8 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ fn main() -> Result<(), WindowError> { ## Usage _vk-graph_ provides a fully-generic render graph structure for simple and statically -typed access to all the resources used while rendering. The `RenderGraph` structure allows Vulkan +typed access to all the resources used while rendering. The `Graph` structure allows Vulkan smart pointer resources to be bound as "nodes" which may be used anywhere in a graph. The graph itself is not tied to swapchain access and may be used to execute general command streams. @@ -43,17 +43,19 @@ Features of the render graph: - Optional [shader hot-reload](contrib/vk-graph-hot/README.md) from disk ```rust -render_graph - .begin_cmd_buf().with_name("Fancy new algorithm for shading a moving character who is actively on fire") +graph + .begin_cmd() + .with_name("Fancy new algorithm for shading a moving character who is actively on fire") .bind_pipeline(&gfx_pipeline) .read_descriptor(0, some_image) .read_descriptor(1, another_image) .read_descriptor(3, some_buf) .clear_color(0, swapchain_image) .store_color(0, swapchain_image) - .record_subpass(move |subpass| { - subpass.push_constants(some_u8_slice); - subpass.draw(6, 1, 0, 0); + .record_pipeline(move |pipeline| { + pipeline + .push_constants(some_u8_slice) + .draw(6, 1, 0, 0); }); ``` ### Debug Logging diff --git a/contrib/vk-graph-egui/src/lib.rs b/contrib/vk-graph-egui/src/lib.rs index 854d541d..7c4eaa50 100644 --- a/contrib/vk-graph-egui/src/lib.rs +++ b/contrib/vk-graph-egui/src/lib.rs @@ -91,7 +91,7 @@ impl Egui { fn bind_and_update_textures( &mut self, deltas: &egui::TexturesDelta, - render_graph: &mut RenderGraph, + render_graph: &mut Graph, ) -> HashMap { let mut bound_tex = deltas .set @@ -189,7 +189,7 @@ impl Egui { fn unbind_and_free( &mut self, bound_tex: HashMap, - render_graph: &mut RenderGraph, + render_graph: &mut Graph, deltas: &egui::TexturesDelta, ) { // Unbind textures @@ -213,7 +213,7 @@ impl Egui { &mut self, shapes: Vec, bound_tex: &HashMap, - render_graph: &mut RenderGraph, + render_graph: &mut Graph, target: impl Into, ) { let target = target.into(); @@ -285,7 +285,7 @@ impl Egui { ((clip_rect.max.y - clip_rect.min.y) * pixels_per_point) as u32; render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Egui pass") .bind_pipeline(&self.ppl) .access_node(idx_buf, AccessType::IndexBuffer) @@ -293,12 +293,13 @@ impl Egui { .access_descriptor((0, 0), *texture, AccessType::FragmentShaderReadOther) .load_color(0, target) .store_color(0, target) - .record_subpass(move |subpass, _| { - subpass.bind_index_buffer(idx_buf, vk::IndexType::UINT32); - subpass.bind_vertex_buffer(vert_buf); - subpass.push_constants(cast_slice(&[push_constants])); - subpass.set_scissor(clip_x, clip_y, clip_width, clip_height); - subpass.draw_indexed(num_indices, 1, 0, 0, 0); + .record_pipeline(move |pipeline, _| { + pipeline + .bind_index_buffer(idx_buf, vk::IndexType::UINT32) + .bind_vertex_buffer(vert_buf) + .push_constants(cast_slice(&[push_constants])) + .set_scissor(clip_x, clip_y, clip_width, clip_height) + .draw_indexed(num_indices, 1, 0, 0, 0); }); } _ => panic!("Primitiv callback not yet supported."), @@ -312,7 +313,7 @@ impl Egui { window: &Window, events: &[Event<()>], target: impl Into, - render_graph: &mut RenderGraph, + render_graph: &mut Graph, ui_fn: impl FnMut(&egui::Context), ) { // Update events and generate shapes and texture deltas. diff --git a/contrib/vk-graph-fx/src/bitmap_font.rs b/contrib/vk-graph-fx/src/bitmap_font.rs index c9a88083..c8801249 100644 --- a/contrib/vk-graph-fx/src/bitmap_font.rs +++ b/contrib/vk-graph-fx/src/bitmap_font.rs @@ -108,7 +108,7 @@ impl BitmapFont { /// TODO pub fn print( &mut self, - graph: &mut RenderGraph, + graph: &mut Graph, image: impl Into, x: f32, y: f32, @@ -123,7 +123,7 @@ impl BitmapFont { #[allow(clippy::too_many_arguments)] pub fn print_scale( &mut self, - graph: &mut RenderGraph, + graph: &mut Graph, image: impl Into, x: f32, y: f32, @@ -139,7 +139,7 @@ impl BitmapFont { #[allow(clippy::too_many_arguments)] pub fn print_scale_scissor( &mut self, - graph: &mut RenderGraph, + graph: &mut Graph, image: impl Into, x: f32, y: f32, @@ -206,7 +206,7 @@ impl BitmapFont { } let mut pass = graph - .begin_cmd_buf() + .begin_cmd() .with_name("text") .bind_pipeline(&self.pipeline) .access_node(vertex_buf, AccessType::IndexBuffer) @@ -217,12 +217,12 @@ impl BitmapFont { pass = pass.read_descriptor((0, [idx as _]), *page_node); } - pass.record_subpass(move |subpass, _| { + pass.record_pipeline(move |pipeline, _| { if let Some((x, y, width, height)) = scissor { - subpass.set_scissor(x, y, width, height); + pipeline.set_scissor(x, y, width, height); } - subpass + pipeline .push_constants(cast_slice(&transform.to_cols_array())) .push_constants_offset(64, &(1.0 / image_info.width as f32).to_ne_bytes()) .push_constants_offset(68, &(1.0 / image_info.height as f32).to_ne_bytes()) diff --git a/contrib/vk-graph-fx/src/image_loader.rs b/contrib/vk-graph-fx/src/image_loader.rs index a9dbfc67..22bb3355 100644 --- a/contrib/vk-graph-fx/src/image_loader.rs +++ b/contrib/vk-graph-fx/src/image_loader.rs @@ -144,7 +144,7 @@ impl ImageLoader { warn!("unused data"); } - let mut render_graph = RenderGraph::default(); + let mut render_graph = Graph::default(); let image = render_graph.bind_node(self.create_image(format, width, height, is_srgb, false)?); @@ -204,17 +204,17 @@ impl ImageLoader { let dispatch_x = (width + 3) >> 2; let dispatch_y = height; render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Decode RGB image") .bind_pipeline(&self.decode_rgb_rgba) .read_descriptor(0, pixel_buf) .write_descriptor(1, temp_image) - .record_compute(move |compute, _| { + .record_pipeline(move |compute, _| { compute .push_constants(&(pixel_buf_stride >> 2).to_ne_bytes()) .dispatch(dispatch_x, dispatch_y, 1); }) - .end_cmd_buf() + .end_cmd() .copy_image(temp_image, image); } ImageFormat::R8G8 | ImageFormat::R8G8B8A8 => { diff --git a/contrib/vk-graph-fx/src/presenter.rs b/contrib/vk-graph-fx/src/presenter.rs index 9d0bbdd5..365a7861 100644 --- a/contrib/vk-graph-fx/src/presenter.rs +++ b/contrib/vk-graph-fx/src/presenter.rs @@ -29,7 +29,7 @@ impl ComputePresenter { /// TODO pub fn present_image( &self, - graph: &mut RenderGraph, + graph: &mut Graph, image: impl Into, swapchain: SwapchainImageNode, ) { @@ -40,12 +40,12 @@ impl ComputePresenter { // TODO: Notice non-sRGB images and run a different pipeline graph - .begin_cmd_buf() + .begin_cmd() .with_name("present (from compute)") .bind_pipeline(&self.0[0]) .read_descriptor(0, image) .write_descriptor(1, swapchain) - .record_compute(move |compute, _| { + .record_pipeline(move |compute, _| { compute.dispatch(swapchain_info.width, swapchain_info.height, 1); }); } @@ -53,7 +53,7 @@ impl ComputePresenter { /// TODO pub fn present_images( &self, - graph: &mut RenderGraph, + graph: &mut Graph, top_image: impl Into, bottom_image: impl Into, swapchain: SwapchainImageNode, @@ -67,13 +67,13 @@ impl ComputePresenter { // TODO: Notice non-sRGB images and run a different pipeline graph - .begin_cmd_buf() + .begin_cmd() .with_name("present (from compute)") .bind_pipeline(&self.0[1]) .read_descriptor((0, [0]), top_image) .read_descriptor((0, [1]), bottom_image) .write_descriptor(1, swapchain) - .record_compute(move |compute, _| { + .record_pipeline(move |compute, _| { compute.dispatch(swapchain_info.width, swapchain_info.height, 1); }); } @@ -104,7 +104,7 @@ impl GraphicPresenter { /// TODO pub fn present_image( &self, - graph: &mut RenderGraph, + graph: &mut Graph, image: impl Into, swapchain: SwapchainImageNode, ) { @@ -124,15 +124,16 @@ impl GraphicPresenter { )); graph - .begin_cmd_buf() + .begin_cmd() .with_name("present (from graphic)") .bind_pipeline(&self.pipeline) .read_descriptor(0, image) .store_color(0, swapchain) - .record_subpass(move |subpass, _| { + .record_pipeline(move |pipeline, _| { // Draw a quad with implicit vertices (no buffer) - subpass.push_constants(cast_slice(&transform.to_cols_array())); - subpass.draw(6, 1, 0, 0); + pipeline + .push_constants(cast_slice(&transform.to_cols_array())) + .draw(6, 1, 0, 0); }); } } diff --git a/contrib/vk-graph-fx/src/transition.rs b/contrib/vk-graph-fx/src/transition.rs index 90b4b2fa..0d386a37 100644 --- a/contrib/vk-graph-fx/src/transition.rs +++ b/contrib/vk-graph-fx/src/transition.rs @@ -406,7 +406,7 @@ impl TransitionPipeline { /// TODO pub fn apply( &mut self, - render_graph: &mut RenderGraph, + render_graph: &mut Graph, a_image: impl Into, b_image: impl Into, transition: Transition, @@ -444,7 +444,7 @@ impl TransitionPipeline { /// TODO pub fn apply_to( &mut self, - render_graph: &mut RenderGraph, + render_graph: &mut Graph, a_image: impl Into, b_image: impl Into, dest_image: impl Into, @@ -469,13 +469,13 @@ impl TransitionPipeline { // TODO: Handle displacement and luma in an if case, below render_graph - .begin_cmd_buf() + .begin_cmd() .with_name(format!("transition {transition_ty:?}")) .bind_pipeline(&pipeline) .read_descriptor(0, a_image) .read_descriptor(1, b_image) .write_descriptor(2, dest_image) - .record_compute(move |compute, _| { + .record_pipeline(move |compute, _| { compute.push_constants(push_consts.as_slice()); compute.dispatch(dest_info.width, dest_info.height, 1); }); diff --git a/contrib/vk-graph-hot/examples/glsl.rs b/contrib/vk-graph-hot/examples/glsl.rs index 42daf79d..44351c7d 100644 --- a/contrib/vk-graph-hot/examples/glsl.rs +++ b/contrib/vk-graph-hot/examples/glsl.rs @@ -30,11 +30,11 @@ fn main() -> Result<(), WindowError> { window.run(|frame| { frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("make some noise") .bind_pipeline(pipeline.hot()) .write_descriptor(0, frame.swapchain_image) - .record_compute(move |compute, _| { + .record_pipeline(move |compute, _| { compute.push_constants(&frame_index.to_ne_bytes()).dispatch( frame.width, frame.height, diff --git a/contrib/vk-graph-hot/examples/hlsl.rs b/contrib/vk-graph-hot/examples/hlsl.rs index c45b506c..b6a590f2 100644 --- a/contrib/vk-graph-hot/examples/hlsl.rs +++ b/contrib/vk-graph-hot/examples/hlsl.rs @@ -34,12 +34,12 @@ fn main() -> Result<(), WindowError> { window.run(|frame| { frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("make some noise") .bind_pipeline(pipeline.hot()) .clear_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) - .record_subpass(move |subpass, _| { + .record_pipeline(move |graphic, _| { subpass .push_constants_offset(0, &frame_index.to_ne_bytes()) .push_constants_offset(4, &frame.width.to_ne_bytes()) diff --git a/contrib/vk-graph-imgui/src/lib.rs b/contrib/vk-graph-imgui/src/lib.rs index c56db517..63b0884a 100644 --- a/contrib/vk-graph-imgui/src/lib.rs +++ b/contrib/vk-graph-imgui/src/lib.rs @@ -65,8 +65,8 @@ impl ImGui { events: &[Event<()>], window: &Window, pool: &mut P, - render_graph: &mut RenderGraph, - ui_func: impl FnOnce(&mut Ui, &mut P, &mut RenderGraph), + render_graph: &mut Graph, + ui_func: impl FnOnce(&mut Ui, &mut P, &mut Graph), ) -> ImageLeaseNode where P: Pool + Pool, @@ -185,7 +185,7 @@ impl ImGui { self.platform.hidpi_factor() as f32 / window.inner_size().height as f32; render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("imgui") .bind_pipeline(&self.pipeline) .access_node(index_buf, AccessType::IndexBuffer) @@ -193,8 +193,8 @@ impl ImGui { .read_descriptor(0, font_atlas_image) .clear_color(0, image) .store_color(0, image) - .record_subpass(move |subpass, _| { - subpass + .record_pipeline(move |pipeline, _| { + pipeline .push_constants_offset(0, &window_width.to_ne_bytes()) .push_constants_offset(4, &window_height.to_ne_bytes()) .bind_index_buffer(index_buf, vk::IndexType::UINT16) @@ -211,8 +211,7 @@ impl ImGui { let y = clip_rect[1].floor() as i32; let width = (clip_rect[2] - clip_rect[0]).ceil() as u32; let height = (clip_rect[3] - clip_rect[1]).ceil() as u32; - subpass.set_scissor(x, y, width, height); - subpass.draw_indexed( + pipeline.set_scissor(x, y, width, height).draw_indexed( index_count as _, 1, first_index as _, @@ -226,7 +225,7 @@ impl ImGui { image } - fn lease_font_atlas_image

(&mut self, pool: &mut P, render_graph: &mut RenderGraph) + fn lease_font_atlas_image

(&mut self, pool: &mut P, render_graph: &mut Graph) where P: Pool + Pool, { diff --git a/contrib/vk-graph-prelude/src/lib.rs b/contrib/vk-graph-prelude/src/lib.rs index 9bea36dd..006403f7 100644 --- a/contrib/vk-graph-prelude/src/lib.rs +++ b/contrib/vk-graph-prelude/src/lib.rs @@ -3,7 +3,7 @@ #![warn(missing_docs)] pub use vk_graph::{ - Bind, ClearColorValue, RenderGraph, Unbind, + Bind, ClearColorValue, CommandRef, Graph, Unbind, display::{Display, DisplayError, DisplayInfo, DisplayInfoBuilder, ResolverPool}, driver::{ AccessType, CommandBuffer, DriverError, @@ -46,7 +46,6 @@ pub use vk_graph::{ AnyBufferNode, AnyImageNode, BufferLeaseNode, BufferNode, ImageLeaseNode, ImageNode, SwapchainImageNode, }, - pass_ref::{PassRef, PipelinePassRef}, pool::{ Lease, Pool, PoolInfo, PoolInfoBuilder, alias::{Alias, AliasPool}, diff --git a/contrib/vk-graph-window/src/frame.rs b/contrib/vk-graph-window/src/frame.rs index cb210476..62743a2d 100644 --- a/contrib/vk-graph-window/src/frame.rs +++ b/contrib/vk-graph-window/src/frame.rs @@ -1,6 +1,6 @@ use { std::sync::Arc, - vk_graph::{driver::device::Device, node::SwapchainImageNode, RenderGraph}, + vk_graph::{driver::device::Device, node::SwapchainImageNode, Graph}, winit::{dpi::PhysicalPosition, event::Event, window::Window}, }; @@ -32,7 +32,7 @@ pub struct FrameContext<'a> { /// A render graph which rendering commands should be recorded into. /// /// Make sure to write to `swapchain_image` as part of this graph. - pub render_graph: &'a mut RenderGraph, + pub render_graph: &'a mut Graph, /// A pre-bound image node for the swapchain image to be drawn. pub swapchain_image: SwapchainImageNode, diff --git a/contrib/vk-graph-window/src/lib.rs b/contrib/vk-graph-window/src/lib.rs index 830d101a..5ccf1318 100644 --- a/contrib/vk-graph-window/src/lib.rs +++ b/contrib/vk-graph-window/src/lib.rs @@ -19,7 +19,7 @@ use { DriverError, }, pool::hash::HashPool, - RenderGraph, + Graph, }, winit::{ application::ApplicationHandler, @@ -333,7 +333,7 @@ impl Window { } if let Some(swapchain_image) = self.display.acquire_next_image()? { - let mut render_graph = RenderGraph::default(); + let mut render_graph = Graph::default(); let swapchain_image = render_graph.bind_node(swapchain_image); let swapchain_info = self.display.swapchain.info; diff --git a/examples/aliasing.rs b/examples/aliasing.rs index b8613dda..81ce3020 100644 --- a/examples/aliasing.rs +++ b/examples/aliasing.rs @@ -36,7 +36,7 @@ fn main() -> Result<(), DriverError> { let image2 = pool.alias(image_info)?; assert!(Arc::ptr_eq(&image1, &image2)); - let mut render_graph = RenderGraph::default(); + let mut render_graph = Graph::default(); // Binding these images to any render graph will produce the same physical nodes let image1 = render_graph.bind_node(image1); diff --git a/examples/app.rs b/examples/app.rs index c1d9e690..6cbeaf84 100644 --- a/examples/app.rs +++ b/examples/app.rs @@ -5,7 +5,7 @@ use { log::error, std::sync::Arc, vk_graph::{ - RenderGraph, + Graph, display::{Display, DisplayError, DisplayInfo}, driver::{ device::{Device, DeviceInfoBuilder}, @@ -114,7 +114,7 @@ struct Context { impl Context { fn draw(&mut self) -> Result<(), DisplayError> { if let Some(swapchain_image) = self.display.acquire_next_image()? { - let mut render_graph = RenderGraph::default(); + let mut render_graph = Graph::default(); let swapchain_image = render_graph.bind_node(swapchain_image); // Rendering goes here! diff --git a/examples/bindless.rs b/examples/bindless.rs index 4d5ac947..3fb98847 100644 --- a/examples/bindless.rs +++ b/examples/bindless.rs @@ -28,7 +28,7 @@ fn main() -> Result<(), WindowError> { let mut pass = frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Test") .bind_pipeline(&pipeline) .access_node(draw_buf_node, AccessType::IndirectBuffer); @@ -40,8 +40,8 @@ fn main() -> Result<(), WindowError> { pass.clear_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) - .record_subpass(move |subpass, _| { - subpass.draw_indirect(draw_buf_node, 0, 64, 16); + .record_pipeline(move |pipeline, _| { + pipeline.draw_indirect(draw_buf_node, 0, 64, 16); }); }) } @@ -50,7 +50,7 @@ fn create_images(device: &Arc) -> Result>, DriverError> { let mut textures = Vec::with_capacity(64); let (b, a) = (0.0, 1.0); - let mut graph = RenderGraph::default(); + let mut graph = Graph::default(); for y in 0..8 { for x in 0..8 { let texture = Arc::new(Image::create( diff --git a/examples/cpu_readback.rs b/examples/cpu_readback.rs index 82bbf3ed..6ca36c8f 100644 --- a/examples/cpu_readback.rs +++ b/examples/cpu_readback.rs @@ -14,7 +14,7 @@ fn main() -> Result<(), DriverError> { let device_info = DeviceInfoBuilder::default().debug(args.debug); let device = Arc::new(Device::new(device_info)?); - let mut render_graph = RenderGraph::default(); + let mut render_graph = Graph::default(); let src_buf = render_graph.bind_node(Buffer::create_from_slice( &device, diff --git a/examples/debugger.rs b/examples/debugger.rs index 597a4e64..6c73ed68 100644 --- a/examples/debugger.rs +++ b/examples/debugger.rs @@ -178,10 +178,10 @@ fn main() -> Result<(), vk_graph_window::WindowError> { */ frame .render_graph - .begin_cmd_buf().with_name("This doesn't look good...") + .begin_cmd().with_name("This doesn't look good...") .bind_pipeline(&compute_pipeline) .write_descriptor(42, image) - .record_compute(|compute, _| { + .record_pipeline(|compute, _| { compute.dispatch(1024, 1024, 1); }); diff --git a/examples/font_bmp.rs b/examples/font_bmp.rs index b6c4f558..81263c91 100644 --- a/examples/font_bmp.rs +++ b/examples/font_bmp.rs @@ -137,11 +137,11 @@ fn main() -> anyhow::Result<()> { let elapsed_time = Instant::now() - start_time; frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("smoke") .bind_pipeline(&smoke_pipeline) .write_descriptor(0, image_node) - .record_compute(move |compute, _| { + .record_pipeline(move |compute, _| { compute .push_constants(&elapsed_time.as_secs_f32().to_ne_bytes()) .dispatch(frame.width.div_ceil(subgroup_size), frame.height, 1); diff --git a/examples/fuzzer.rs b/examples/fuzzer.rs index d887de1f..1772c725 100644 --- a/examples/fuzzer.rs +++ b/examples/fuzzer.rs @@ -34,9 +34,9 @@ use { type Operation = fn(&mut FrameContext, &mut HashPool); static OPERATIONS: &[Operation] = &[ - record_compute_array_bind, - record_compute_bindless, - record_compute_no_op, + record_pipeline_array_bind, + record_pipeline_bindless, + record_pipeline_no_op, record_graphic_bindless, record_graphic_load_store, record_graphic_msaa_depth_stencil, @@ -268,7 +268,7 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { let pass = frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("build acceleration structures"); // TODO: AccessType for these is funky, should be access_node? @@ -310,7 +310,7 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { }); } -fn record_compute_array_bind(frame: &mut FrameContext, pool: &mut HashPool) { +fn record_pipeline_array_bind(frame: &mut FrameContext, pool: &mut HashPool) { let pipeline = compute_pipeline( "array_bind", frame.device, @@ -378,7 +378,7 @@ fn record_compute_array_bind(frame: &mut FrameContext, pool: &mut HashPool) { .clear_color_image(images[2], [0f32; 4]) .clear_color_image(images[3], [0f32; 4]) .clear_color_image(images[4], [0f32; 4]) - .begin_cmd_buf() + .begin_cmd() .with_name("array-bind") .bind_pipeline(&pipeline) .read_descriptor((0, [0]), images[0]) @@ -386,14 +386,14 @@ fn record_compute_array_bind(frame: &mut FrameContext, pool: &mut HashPool) { .read_descriptor((0, [2]), images[2]) .read_descriptor((0, [3]), images[3]) .read_descriptor((0, [4]), images[4]) - .record_compute(|compute, _| { + .record_pipeline(|compute, _| { compute .push_constants(&0f32.to_ne_bytes()) .dispatch(64, 64, 1); }); } -fn record_compute_bindless(frame: &mut FrameContext, pool: &mut HashPool) { +fn record_pipeline_bindless(frame: &mut FrameContext, pool: &mut HashPool) { let pipeline = compute_pipeline( "bindless", frame.device, @@ -454,7 +454,7 @@ fn record_compute_bindless(frame: &mut FrameContext, pool: &mut HashPool) { frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("compute-bindless") .bind_pipeline(&pipeline) .write_descriptor((0, [0]), images[0]) @@ -462,14 +462,14 @@ fn record_compute_bindless(frame: &mut FrameContext, pool: &mut HashPool) { .write_descriptor((0, [2]), images[2]) .write_descriptor((0, [3]), images[3]) .write_descriptor((0, [4]), images[4]) - .record_compute(|compute, _| { + .record_pipeline(|compute, _| { compute .push_constants(&5u32.to_ne_bytes()) .dispatch(64, 64, 1); }); } -fn record_compute_no_op(frame: &mut FrameContext, _: &mut HashPool) { +fn record_pipeline_no_op(frame: &mut FrameContext, _: &mut HashPool) { let pipeline = compute_pipeline( "no_op", frame.device, @@ -489,10 +489,10 @@ fn record_compute_no_op(frame: &mut FrameContext, _: &mut HashPool) { ); frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("no-op") .bind_pipeline(&pipeline) - .record_compute(|compute, _| { + .record_pipeline(|compute, _| { compute.dispatch(1, 1, 1); }); } @@ -580,7 +580,7 @@ fn record_graphic_bindless(frame: &mut FrameContext, pool: &mut HashPool) { .clear_color_image(images[2], [0f32; 4]) .clear_color_image(images[3], [0f32; 4]) .clear_color_image(images[4], [0f32; 4]) - .begin_cmd_buf() + .begin_cmd() .with_name("graphic-bindless") .bind_pipeline(&pipeline) .read_descriptor((0, [0]), images[0]) @@ -590,8 +590,10 @@ fn record_graphic_bindless(frame: &mut FrameContext, pool: &mut HashPool) { .read_descriptor((0, [4]), images[4]) .clear_color(0, image) .store_color(0, image) - .record_subpass(|subpass, _| { - subpass.push_constants(&5u32.to_ne_bytes()).draw(1, 1, 0, 0); + .record_pipeline(|pipeline, _| { + pipeline + .push_constants(&5u32.to_ne_bytes()) + .draw(1, 1, 0, 0); }); } @@ -626,13 +628,13 @@ fn record_graphic_load_store(frame: &mut FrameContext, _: &mut HashPool) { frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("load-store") .bind_pipeline(&pipeline) .load_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) - .record_subpass(|subpass, _| { - subpass.draw(1, 1, 0, 0); + .record_pipeline(|pipeline, _| { + pipeline.draw(1, 1, 0, 0); }); } @@ -798,7 +800,7 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("msaa-depth-stencil") .bind_pipeline(&pipeline) .set_depth_stencil(depth_stencil_mode) @@ -811,8 +813,8 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo Some(depth_resolve_mode), Some(ResolveMode::SampleZero), ) - .record_subpass(|subpass, _| { - subpass.draw(3, 1, 0, 0); + .record_pipeline(|pipeline, _| { + pipeline.draw(3, 1, 0, 0); }); } @@ -830,7 +832,7 @@ fn record_graphic_will_merge_common_color1(frame: &mut FrameContext, pool: &mut // Pass "a" stores color0 which "b" compatibly loads; so these two will get merged frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("a") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, @@ -859,12 +861,12 @@ fn record_graphic_will_merge_common_color1(frame: &mut FrameContext, pool: &mut .as_slice(), )) .store_color(0, image) - .record_subpass(|subpass, _| { - subpass.draw(1, 1, 0, 0); + .record_pipeline(|pipeline, _| { + pipeline.draw(1, 1, 0, 0); }); frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("b") .bind_pipeline(&graphic_vert_frag_pipeline( frame.device, @@ -894,8 +896,8 @@ fn record_graphic_will_merge_common_color1(frame: &mut FrameContext, pool: &mut )) .load_color(0, image) .store_color(0, image) - .record_subpass(|subpass, _| { - subpass.draw(1, 1, 0, 0); + .record_pipeline(|pipeline, _| { + pipeline.draw(1, 1, 0, 0); }); } @@ -921,7 +923,7 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("a") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, @@ -950,12 +952,12 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut .as_slice(), )) .store_color(0, image_0) - .record_subpass(|subpass, _| { - subpass.draw(1, 1, 0, 0); + .record_pipeline(|pipeline, _| { + pipeline.draw(1, 1, 0, 0); }); frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("b") .bind_pipeline(&graphic_vert_frag_pipeline( frame.device, @@ -988,12 +990,12 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut .load_color(0, image_0) .store_color(0, image_0) .store_color(1, image_1) - .record_subpass(|subpass, _| { - subpass.draw(1, 1, 0, 0); + .record_pipeline(|pipeline, _| { + pipeline.draw(1, 1, 0, 0); }); frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("c") .bind_pipeline(&graphic_vert_frag_pipeline( frame.device, @@ -1023,8 +1025,8 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut )) .clear_color(0, image_0) .store_color(0, image_0) - .record_subpass(|subpass, _| { - subpass.draw(1, 1, 0, 0); + .record_pipeline(|pipeline, _| { + pipeline.draw(1, 1, 0, 0); }); } @@ -1051,7 +1053,7 @@ fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut // Pass "a" stores color0+depth which "b" compatibly loads; so these two will get merged frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("a") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, @@ -1081,12 +1083,12 @@ fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut )) .store_color(0, color_image) .store_depth_stencil(depth_image) - .record_subpass(|subpass, _| { - subpass.draw(1, 1, 0, 0); + .record_pipeline(|pipeline, _| { + pipeline.draw(1, 1, 0, 0); }); frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("b") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, @@ -1114,8 +1116,8 @@ fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut )) .load_depth_stencil(depth_image) .store_depth_stencil(depth_image) - .record_subpass(|subpass, _| { - subpass.draw(1, 1, 0, 0); + .record_pipeline(|pipeline, _| { + pipeline.draw(1, 1, 0, 0); }); } @@ -1142,7 +1144,7 @@ fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut // Pass "a" stores color0+depth which "b" compatibly loads; so these two will get merged frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("a") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, @@ -1169,12 +1171,12 @@ fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut .as_slice(), )) .store_depth_stencil(depth_image) - .record_subpass(|subpass, _| { - subpass.draw(1, 1, 0, 0); + .record_pipeline(|pipeline, _| { + pipeline.draw(1, 1, 0, 0); }); frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("b") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, @@ -1205,8 +1207,8 @@ fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut .store_color(0, color_image) .load_depth_stencil(depth_image) .store_depth_stencil(depth_image) - .record_subpass(|subpass, _| { - subpass.draw(1, 1, 0, 0); + .record_pipeline(|pipeline, _| { + pipeline.draw(1, 1, 0, 0); }); } @@ -1223,7 +1225,7 @@ fn record_graphic_will_merge_common_depth3(frame: &mut FrameContext, pool: &mut frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("a") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, @@ -1250,12 +1252,12 @@ fn record_graphic_will_merge_common_depth3(frame: &mut FrameContext, pool: &mut .as_slice(), )) .store_depth_stencil(depth_image) - .record_subpass(|subpass, _| { - subpass.draw(1, 1, 0, 0); + .record_pipeline(|pipeline, _| { + pipeline.draw(1, 1, 0, 0); }); frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("b") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, @@ -1283,8 +1285,8 @@ fn record_graphic_will_merge_common_depth3(frame: &mut FrameContext, pool: &mut )) .load_depth_stencil(depth_image) .store_depth_stencil(depth_image) - .record_subpass(|subpass, _| { - subpass.draw(1, 1, 0, 0); + .record_pipeline(|pipeline, _| { + pipeline.draw(1, 1, 0, 0); }); } @@ -1352,22 +1354,22 @@ fn record_graphic_will_merge_subpass_input(frame: &mut FrameContext, pool: &mut // Pass "a" stores color 0 which "b" compatibly inputs; so these two will get merged frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("a") .bind_pipeline(&pipeline_a) .clear_color(0, image) .store_color(0, image) - .record_subpass(|subpass, _| { - subpass.draw(1, 1, 0, 0); + .record_pipeline(|pipeline, _| { + pipeline.draw(1, 1, 0, 0); }); frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("b") .bind_pipeline(&pipeline_b) .store_color(0, image) - .record_subpass(|subpass, _| { - subpass.draw(1, 1, 0, 0); + .record_pipeline(|pipeline, _| { + pipeline.draw(1, 1, 0, 0); }); } @@ -1412,21 +1414,21 @@ fn record_graphic_wont_merge(frame: &mut FrameContext, pool: &mut HashPool) { // These two passes have common writes but are otherwise regular - they won't get merged frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("c") .bind_pipeline(&pipeline) .store_color(0, image) - .record_subpass(|subpass, _| { - subpass.draw(1, 1, 0, 0); + .record_pipeline(|pipeline, _| { + pipeline.draw(1, 1, 0, 0); }); frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("d") .bind_pipeline(&pipeline) .store_color(0, image) - .record_subpass(|subpass, _| { - subpass.draw(1, 1, 0, 0); + .record_pipeline(|pipeline, _| { + pipeline.draw(1, 1, 0, 0); }); } @@ -1488,25 +1490,25 @@ fn record_transfer_graphic_multipass(frame: &mut FrameContext, pool: &mut HashPo // b should have a pipeline barrier (on the clear we just did) before the pass starts. frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("a") .bind_pipeline(&pipeline) .clear_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) .read_descriptor(0, images[0]) - .record_subpass(|subpass, _| { - subpass.draw(1, 1, 0, 0); + .record_pipeline(|pipeline, _| { + pipeline.draw(1, 1, 0, 0); }); frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("b") .bind_pipeline(&pipeline) .load_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) .read_descriptor(0, images[1]) - .record_subpass(|subpass, _| { - subpass.draw(1, 1, 0, 0); + .record_pipeline(|pipeline, _| { + pipeline.draw(1, 1, 0, 0); }); } diff --git a/examples/image_sampler.rs b/examples/image_sampler.rs index da903797..9ccf7805 100644 --- a/examples/image_sampler.rs +++ b/examples/image_sampler.rs @@ -68,13 +68,13 @@ fn main() -> anyhow::Result<()> { let gulf_image = frame.render_graph.bind_node(&gulf_image); frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Draw gulf image to swapchain") .bind_pipeline(&pipelines[pipeline_index]) .read_descriptor(0, gulf_image) .store_color(0, frame.swapchain_image) - .record_subpass(|subpass, _| { - subpass.draw(3, 1, 0, 0); + .record_pipeline(|pipeline, _| { + pipeline.draw(3, 1, 0, 0); }); })?; @@ -242,7 +242,7 @@ fn read_image(device: &Arc, path: impl AsRef) -> anyhow::Result Result<(), DriverError> { pretty_env_logger::init(); - let mut render_graph = RenderGraph::default(); + let mut render_graph = Graph::default(); let args = Args::parse(); let device_info = DeviceInfoBuilder::default().debug(args.debug); let device = Arc::new(Device::new(device_info)?); @@ -87,7 +87,7 @@ fn main() -> Result<(), DriverError> { fn fill_depth_image( device: &Arc, - render_graph: &mut RenderGraph, + render_graph: &mut Graph, size: u32, ) -> Result { let info = ImageInfo::image_2d( @@ -157,7 +157,7 @@ fn fill_depth_image( fn reduce_depth_image( device: &Arc, - render_graph: &mut RenderGraph, + render_graph: &mut Graph, depth_image: ImageNode, reduction_mode: vk::SamplerReductionMode, ) -> Result { @@ -176,7 +176,7 @@ fn reduce_depth_image( let reduced_image = render_graph.bind_node(Image::create(device, reduced_info)?); render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Reduce depth image") .bind_pipeline(&Arc::new(ComputePipeline::create( device, @@ -206,7 +206,7 @@ fn reduce_depth_image( )?)) .read_descriptor(0, depth_image) .write_descriptor(1, reduced_image) - .record_compute(move |compute, _| { + .record_pipeline(move |compute, _| { compute.dispatch(reduced_info.width, reduced_info.height, 1); }); @@ -215,7 +215,7 @@ fn reduce_depth_image( fn copy_image_to_buffer( device: &Arc, - render_graph: &mut RenderGraph, + render_graph: &mut Graph, reduced_image: ImageNode, ) -> Result, DriverError> { let reduced_info = render_graph.node_info(reduced_image); diff --git a/examples/mip_compute.rs b/examples/mip_compute.rs index e6664634..4d60ac91 100644 --- a/examples/mip_compute.rs +++ b/examples/mip_compute.rs @@ -18,7 +18,7 @@ fn main() -> Result<(), DriverError> { let device_info = DeviceInfoBuilder::default().debug(args.debug); let device = Arc::new(Device::new(device_info)?); - let mut render_graph = RenderGraph::default(); + let mut render_graph = Graph::default(); let depth_pyramid = render_graph.bind_node(Image::create( &device, @@ -51,7 +51,7 @@ fn main() -> Result<(), DriverError> { render_graph.copy_buffer_to_image(depth_buf, depth_pyramid); let mut pass = render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("update depth pyramid") .bind_pipeline(ComputePipeline::create( &device, @@ -105,7 +105,7 @@ fn main() -> Result<(), DriverError> { .base_mip_level(mip_level) .mip_level_count(1), ) - .record_compute(move |compute, _| { + .record_pipeline(move |compute, _| { compute.dispatch( depth_info.width >> mip_level, depth_info.height >> mip_level, diff --git a/examples/mip_graphic.rs b/examples/mip_graphic.rs index 037411df..1787c4a0 100644 --- a/examples/mip_graphic.rs +++ b/examples/mip_graphic.rs @@ -58,7 +58,7 @@ fn main() -> Result<(), WindowError> { let mut pass = frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("splat mips") .bind_pipeline(&splat); @@ -77,8 +77,8 @@ fn main() -> Result<(), WindowError> { .load_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) .set_render_area(stripe_x as _, 0, stripe_width, swapchain_info.height) - .record_subpass(|subpass, _| { - subpass.draw(6, 1, 0, 0); + .record_pipeline(|pipeline, _| { + pipeline.draw(6, 1, 0, 0); }); } }) @@ -146,7 +146,7 @@ fn fill_mip_levels(device: &Arc, image: &Arc) -> Result<(), Drive ], )?); - let mut render_graph = RenderGraph::default(); + let mut render_graph = Graph::default(); let image_info = image.info; let image = render_graph.bind_node(image); @@ -155,7 +155,7 @@ fn fill_mip_levels(device: &Arc, image: &Arc) -> Result<(), Drive // new pass for each level the Vulkan framebuffer would be set to the size of the first image. for mip_level in 0..image_info.mip_level_count { render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("fill mip levels") .bind_pipeline(&vertical_gradient) .store_color_as( @@ -167,8 +167,8 @@ fn fill_mip_levels(device: &Arc, image: &Arc) -> Result<(), Drive .base_mip_level(mip_level) .mip_level_count(1), ) - .record_subpass(|subpass, _| { - subpass + .record_pipeline(|pipeline, _| { + pipeline .push_constants(bytes_of(&PushConstants { a: vec3(0.0, 1.0, 1.0).extend(f32::NAN), b: vec3(1.0, 0.0, 1.0).extend(f32::NAN), diff --git a/examples/msaa.rs b/examples/msaa.rs index af81d713..e8bd2cb6 100644 --- a/examples/msaa.rs +++ b/examples/msaa.rs @@ -91,7 +91,7 @@ fn main() -> anyhow::Result<()> { let mut pass = frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("cube") .bind_pipeline(if will_render_msaa { &mesh_msaa_pipeline @@ -156,8 +156,8 @@ fn main() -> anyhow::Result<()> { .store_color(0, frame.swapchain_image); } - pass.record_subpass(move |subpass, _| { - subpass + pass.record_pipeline(move |pipeline, _| { + pipeline .bind_vertex_buffer(cube_vertex_buf) .push_constants(bytes_of(&world_transform)) .draw(cube_mesh.vertex_count, 1, 0, 0); diff --git a/examples/multipass.rs b/examples/multipass.rs index 7b660190..d27b9278 100644 --- a/examples/multipass.rs +++ b/examples/multipass.rs @@ -37,7 +37,7 @@ const GOLD: Material = Material { }; /// The example demonstrates leasing resources (images and buffers) and composing rendering -/// operations with just a few lines of RenderGraph builder-pattern code. +/// operations with just a few lines of Graph builder-pattern code. /// /// Also shown: /// - Basic PBR rendering (from Sascha Willems) @@ -88,7 +88,7 @@ fn main() -> anyhow::Result<()> { // Depth Prepass frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Depth Prepass") .bind_pipeline(&prepass) .set_depth_stencil(write) @@ -97,8 +97,8 @@ fn main() -> anyhow::Result<()> { .access_node(vertex_buf, AccessType::VertexBuffer) .clear_depth_stencil(depth_stencil) .store_depth_stencil(depth_stencil) - .record_subpass(move |subpass, _| { - subpass + .record_pipeline(move |pipeline, _| { + pipeline .bind_index_buffer(index_buf, vk::IndexType::UINT16) .bind_vertex_buffer(vertex_buf) .push_constants(bytes_of(&obj_pos)) @@ -119,7 +119,7 @@ fn main() -> anyhow::Result<()> { // Renders a golden orb on an un-cleared swapchain image frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("funky shape PBR") .bind_pipeline(&pbr) .set_depth_stencil(write) @@ -130,8 +130,8 @@ fn main() -> anyhow::Result<()> { .load_depth_stencil(depth_stencil) .store_depth_stencil(depth_stencil) .store_color(0, frame.swapchain_image) - .record_subpass(move |subpass, _| { - subpass + .record_pipeline(move |pipeline, _| { + pipeline .bind_index_buffer(index_buf, vk::IndexType::UINT16) .bind_vertex_buffer(vertex_buf) .push_constants(&push_const_data) @@ -148,15 +148,15 @@ fn main() -> anyhow::Result<()> { // Renders a solid color wherever the golden orb did not draw frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("fill background") .bind_pipeline(&fill_background) .set_depth_stencil(read) .load_depth_stencil(depth_stencil) .load_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) - .record_subpass(move |subpass, _| { - subpass.draw(6, 1, 0, 0); + .record_pipeline(move |pipeline, _| { + pipeline.draw(6, 1, 0, 0); }); })?; @@ -187,7 +187,7 @@ fn best_depth_stencil_format(device: &Device) -> vk::Format { } fn bind_camera_buf( - render_graph: &mut RenderGraph, + render_graph: &mut Graph, pool: &mut LazyPool, camera: Camera, model: Mat4, @@ -203,7 +203,7 @@ fn bind_camera_buf( render_graph.bind_node(buf) } -fn bind_light_buf(render_graph: &mut RenderGraph, pool: &mut LazyPool) -> BufferLeaseNode { +fn bind_light_buf(render_graph: &mut Graph, pool: &mut LazyPool) -> BufferLeaseNode { let mut buf = pool .lease(BufferInfo::host_mem( 64, @@ -279,7 +279,7 @@ fn create_funky_shape(device: &Arc, pool: &mut LazyPool) -> Result anyhow::Result<()> { let t = 12.0 * ((Instant::now() - started_at).as_millis() % 32) as f32; // Clear a new image to a cycling color - let mut render_graph = RenderGraph::default(); + let mut render_graph = Graph::default(); let image = render_graph.bind_node( pool.lease(ImageInfo::image_2d( 10, @@ -248,7 +248,7 @@ fn load_font(device: &Arc) -> anyhow::Result { ) .unwrap(); - let mut render_graph = RenderGraph::default(); + let mut render_graph = Graph::default(); let page_0 = render_graph.bind_node(page_0); let temp_buf = render_graph.bind_node(temp_buf); render_graph.copy_buffer_to_image(temp_buf, page_0); diff --git a/examples/ray_omni.rs b/examples/ray_omni.rs index 29b26573..ca62675e 100644 --- a/examples/ray_omni.rs +++ b/examples/ray_omni.rs @@ -90,7 +90,7 @@ fn main() -> anyhow::Result<()> { frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Mesh with ray-query shadows") .bind_pipeline(&gfx_pipeline) .access_node(ground_mesh_index_buf, AccessType::IndexBuffer) @@ -107,13 +107,13 @@ fn main() -> anyhow::Result<()> { .clear_depth_stencil(depth_image) .clear_color_value(0, frame.swapchain_image, [0xff, 0xff, 0xff, 0xff]) .store_color(0, frame.swapchain_image) - .record_subpass(move |subpass, _| { - subpass + .record_pipeline(move |pipeline, _| { + pipeline .bind_index_buffer(model_mesh_index_buf, vk::IndexType::UINT32) .bind_vertex_buffer(model_mesh_vertex_buf) .draw_indexed(model_mesh.index_count, 1, 0, 0, 0); - subpass + pipeline .bind_index_buffer(ground_mesh_index_buf, vk::IndexType::UINT32) .bind_vertex_buffer(ground_mesh_vertex_buf) .draw_indexed(ground_mesh.index_count, 1, 0, 0, 0); @@ -177,7 +177,7 @@ fn create_blas( .flags(vk::BuildAccelerationStructureFlagsKHR::PREFER_FAST_TRACE); let size = AccelerationStructure::size_of(device, &info); - let mut render_graph = RenderGraph::default(); + let mut render_graph = Graph::default(); let blas = render_graph.bind_node(AccelerationStructure::create( device, AccelerationStructureInfo::blas(size.create_size), @@ -201,7 +201,7 @@ fn create_blas( )?); let scratch_data = render_graph.node_device_address(scratch_buf); - let mut pass = render_graph.begin_cmd_buf().with_name("Build BLAS"); + let mut pass = render_graph.begin_cmd().with_name("Build BLAS"); for model in models.iter().copied() { let index_buf = pass.bind_node(&model.index_buf); @@ -316,7 +316,7 @@ fn create_pipeline(device: &Arc) -> Result, DriverE fn create_tlas( device: &Arc, pool: &mut LazyPool, - render_graph: &mut RenderGraph, + render_graph: &mut Graph, blas: &Arc, ) -> Result { let instances = [vk::AccelerationStructureInstanceKHR { @@ -386,7 +386,7 @@ fn create_tlas( let instance_buf = render_graph.bind_node(instance_buf); render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Build TLAS") .access_node(blas, AccessType::AccelerationStructureBuildRead) .access_node(instance_buf, AccessType::AccelerationStructureBuildRead) diff --git a/examples/ray_trace.rs b/examples/ray_trace.rs index 685c114d..fdc658dc 100644 --- a/examples/ray_trace.rs +++ b/examples/ray_trace.rs @@ -668,7 +668,7 @@ fn main() -> anyhow::Result<()> { .unwrap() .min_accel_struct_scratch_offset_alignment as vk::DeviceSize; - let mut render_graph = RenderGraph::default(); + let mut render_graph = Graph::default(); let index_node = render_graph.bind_node(&index_buf); let vertex_node = render_graph.bind_node(&vertex_buf); let blas_node = render_graph.bind_node(&blas); @@ -687,7 +687,7 @@ fn main() -> anyhow::Result<()> { let scratch_data = render_graph.node_device_address(scratch_buf); render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Build BLAS") .access_node(index_node, AccessType::AccelerationStructureBuildRead) .access_node(vertex_node, AccessType::AccelerationStructureBuildRead) @@ -714,7 +714,7 @@ fn main() -> anyhow::Result<()> { let tlas_node = render_graph.bind_node(&tlas); render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Build TLAS") .access_node(blas_node, AccessType::AccelerationStructureBuildRead) .access_node(instance_node, AccessType::AccelerationStructureBuildRead) @@ -850,7 +850,7 @@ fn main() -> anyhow::Result<()> { frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("basic ray tracer") .bind_pipeline(&ray_trace_pipeline) .access_node( @@ -873,8 +873,8 @@ fn main() -> anyhow::Result<()> { AccessType::RayTracingShaderReadOther, ) .access_descriptor(6, material_buf_node, AccessType::RayTracingShaderReadOther) - .record_ray_trace(move |ray_trace, _| { - ray_trace.trace_rays( + .record_pipeline(move |pipeline, _| { + pipeline.trace_rays( &sbt_rgen, &sbt_miss, &sbt_hit, @@ -884,7 +884,7 @@ fn main() -> anyhow::Result<()> { 1, ); }) - .end_cmd_buf() + .end_cmd() .copy_image(image_node, frame.swapchain_image); })?; diff --git a/examples/rt_triangle.rs b/examples/rt_triangle.rs index 55dbc778..de9a37c3 100644 --- a/examples/rt_triangle.rs +++ b/examples/rt_triangle.rs @@ -322,7 +322,7 @@ fn main() -> anyhow::Result<()> { .unwrap() .min_accel_struct_scratch_offset_alignment as vk::DeviceSize; - let mut render_graph = RenderGraph::default(); + let mut render_graph = Graph::default(); let index_node = render_graph.bind_node(&index_buf); let vertex_node = render_graph.bind_node(&vertex_buf); let blas_node = render_graph.bind_node(&blas); @@ -341,7 +341,7 @@ fn main() -> anyhow::Result<()> { let scratch_data = render_graph.node_device_address(scratch_buf); render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Build BLAS") .access_node(index_node, AccessType::AccelerationStructureBuildRead) .access_node(vertex_node, AccessType::AccelerationStructureBuildRead) @@ -368,7 +368,7 @@ fn main() -> anyhow::Result<()> { let tlas_node = render_graph.bind_node(&tlas); render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Build TLAS") .access_node(blas_node, AccessType::AccelerationStructureBuildRead) .access_node(instance_node, AccessType::AccelerationStructureBuildRead) @@ -396,7 +396,7 @@ fn main() -> anyhow::Result<()> { frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("ray-traced triangle") .bind_pipeline(&ray_trace_pipeline) .access_node( @@ -410,8 +410,8 @@ fn main() -> anyhow::Result<()> { AccessType::RayTracingShaderReadAccelerationStructure, ) .write_descriptor(1, frame.swapchain_image) - .record_ray_trace(move |ray_trace, _| { - ray_trace.trace_rays( + .record_pipeline(move |pipeline, _| { + pipeline.trace_rays( &sbt_rgen, &sbt_miss, &sbt_hit, @@ -421,7 +421,7 @@ fn main() -> anyhow::Result<()> { 1, ); }) - .end_cmd_buf(); + .end_cmd(); })?; Ok(()) diff --git a/examples/shader-toy/src/main.rs b/examples/shader-toy/src/main.rs index 6c6d7244..34089450 100644 --- a/examples/shader-toy/src/main.rs +++ b/examples/shader-toy/src/main.rs @@ -46,7 +46,7 @@ use { shader::Shader, }, pool::{lazy::LazyPool, Pool as _}, - RenderGraph, + Graph, }, vk_graph_fx::*, vk_graph_window::WindowBuilder, @@ -126,7 +126,7 @@ fn main() -> anyhow::Result<()> { .context("FLOCKAROO_IMG_FRAG")?, ); - let mut render_graph = RenderGraph::default(); + let mut render_graph = Graph::default(); let blank_image = render_graph.bind_node( cache .lease(ImageInfo::image_2d( @@ -290,7 +290,7 @@ fn main() -> anyhow::Result<()> { // Fill a buffer using a single-pass CFD pipeline where previous output feeds next input frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Buffer A") .bind_pipeline(&buffer_pipeline) .read_descriptor(0, input) @@ -298,22 +298,24 @@ fn main() -> anyhow::Result<()> { .read_descriptor(2, flowers_image) .read_descriptor(3, blank_image) .store_color(0, output) - .record_subpass(move |subpass, _| { - subpass.push_constants(bytes_of(&push_consts)); - subpass.draw(6, 1, 0, 0); + .record_pipeline(move |pipeline, _| { + pipeline + .push_constants(bytes_of(&push_consts)) + .draw(6, 1, 0, 0); }); // Make the CFD look more like paint with a second pass frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Image") .bind_pipeline(&image_pipeline) .read_descriptor(0, output) .store_color(0, input) - .record_subpass(move |subpass, _| { - subpass.push_constants(bytes_of(&push_consts)); - subpass.draw(6, 1, 0, 0); + .record_pipeline(move |pipeline, _| { + pipeline + .push_constants(bytes_of(&push_consts)) + .draw(6, 1, 0, 0); }); // Done! diff --git a/examples/skeletal-anim/src/main.rs b/examples/skeletal-anim/src/main.rs index de24f86c..99c48511 100644 --- a/examples/skeletal-anim/src/main.rs +++ b/examples/skeletal-anim/src/main.rs @@ -27,7 +27,7 @@ use { AccessType, DriverError, }, pool::{hash::HashPool, lazy::LazyPool, Pool as _}, - RenderGraph, + Graph, }, vk_graph_window::{WindowBuilder, WindowError}, }; @@ -125,7 +125,7 @@ fn main() -> Result<(), WindowError> { frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("🦴") .bind_pipeline(&pipeline) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) @@ -137,8 +137,8 @@ fn main() -> Result<(), WindowError> { .clear_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) .clear_depth_stencil(depth_image) - .record_subpass(move |subpass, _| { - subpass + .record_pipeline(move |pipeline, _| { + pipeline .bind_index_buffer(index_buf, vk::IndexType::UINT16) .bind_vertex_buffer(vertex_buf) .push_constants(bytes_of(&Mat4::IDENTITY)) @@ -195,7 +195,7 @@ fn load_texture( )?); // Copy the host-accessible pixels into the device-only image - let mut render_graph = RenderGraph::default(); + let mut render_graph = Graph::default(); let image_node = render_graph.bind_node(&image); let buffer_node = render_graph.bind_node(&buffer); render_graph.copy_buffer_to_image(buffer_node, image_node); @@ -448,7 +448,7 @@ impl Model { )?); // Copy the host-accessible staging buffers to device-only buffers - let mut render_graph = RenderGraph::default(); + let mut render_graph = Graph::default(); let index_staging_buf_node = render_graph.bind_node(index_staging_buf); let vertex_staging_buf_node = render_graph.bind_node(vertex_staging_buf); let index_buf_node = render_graph.bind_node(&index_buf); diff --git a/examples/subgroup_ops.rs b/examples/subgroup_ops.rs index b2c90e94..3d829063 100644 --- a/examples/subgroup_ops.rs +++ b/examples/subgroup_ops.rs @@ -66,7 +66,7 @@ fn exclusive_sum( scan_pipeline: &Arc, input_data: &[u32], ) -> Result, DriverError> { - let mut render_graph = RenderGraph::default(); + let mut render_graph = Graph::default(); let input_buf = render_graph.bind_node(Buffer::create_from_slice( device, @@ -95,24 +95,24 @@ fn exclusive_sum( if reduce_count > 0 { render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("exclusive sum reduce") .bind_pipeline(reduce_pipeline) .read_descriptor(0, input_buf) .write_descriptor(1, workgroup_buf) - .record_compute(move |compute, _| { + .record_pipeline(move |compute, _| { compute.dispatch(reduce_count, 1, 1); }); } render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("exclusive sum scan") .bind_pipeline(scan_pipeline) .read_descriptor(0, workgroup_buf) .read_descriptor(1, input_buf) .write_descriptor(2, output_buf) - .record_compute(move |compute, _| { + .record_pipeline(move |compute, _| { compute.dispatch(workgroup_count, 1, 1); }); diff --git a/examples/triangle.rs b/examples/triangle.rs index b8fb6dd7..932a17be 100644 --- a/examples/triangle.rs +++ b/examples/triangle.rs @@ -84,17 +84,18 @@ fn main() -> Result<(), WindowError> { frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Triangle Example") .bind_pipeline(&triangle_pipeline) .access_node(index_node, AccessType::IndexBuffer) .access_node(vertex_node, AccessType::VertexBuffer) .clear_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) - .record_subpass(move |subpass, _| { - subpass.bind_index_buffer(index_node, vk::IndexType::UINT16); - subpass.bind_vertex_buffer(vertex_node); - subpass.draw_indexed(3, 1, 0, 0, 0); + .record_pipeline(move |pipeline, _| { + pipeline + .bind_index_buffer(index_node, vk::IndexType::UINT16) + .bind_vertex_buffer(vertex_node) + .draw_indexed(3, 1, 0, 0, 0); }); }) } diff --git a/examples/vertex_layout.rs b/examples/vertex_layout.rs index f94f9a3e..c68d1375 100644 --- a/examples/vertex_layout.rs +++ b/examples/vertex_layout.rs @@ -101,14 +101,14 @@ fn draw_triangle( frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Triangle") .bind_pipeline(pipeline) .load_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) .access_node(vertex_buf, AccessType::VertexBuffer) - .record_subpass(move |subpass, _| { - subpass.bind_vertex_buffer(vertex_buf).draw(3, 1, 0, 0); + .record_pipeline(move |pipeline, _| { + pipeline.bind_vertex_buffer(vertex_buf).draw(3, 1, 0, 0); }); } diff --git a/examples/vr/src/main.rs b/examples/vr/src/main.rs index 522a3d1f..740a5571 100644 --- a/examples/vr/src/main.rs +++ b/examples/vr/src/main.rs @@ -30,7 +30,7 @@ use { AccessType, }, pool::{lazy::LazyPool, Pool as _}, - RenderGraph, + Graph, }, vk_graph_hot::{graphic::HotGraphicPipeline, shader::HotShader}, }; @@ -268,7 +268,7 @@ fn main() -> anyhow::Result<()> { continue; } - let mut render_graph = RenderGraph::default(); + let mut render_graph = Graph::default(); let depth_image = render_graph.bind_node( pool.lease(ImageInfo::image_2d_array( resolution.width, @@ -352,7 +352,7 @@ fn main() -> anyhow::Result<()> { let push_consts = PushConstants::new(model_transform); render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Left hand") .bind_pipeline(hands_pipeline.hot()) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) @@ -378,8 +378,8 @@ fn main() -> anyhow::Result<()> { occlusion_texture, AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, ) - .record_subpass(move |subpass, _| { - subpass + .record_pipeline(move |pipeline, _| { + pipeline .bind_index_buffer(index_buf, vk::IndexType::UINT32) .bind_vertex_buffer(vertex_buf) .push_constants(bytes_of(&push_consts)) @@ -397,7 +397,7 @@ fn main() -> anyhow::Result<()> { let push_consts = PushConstants::new(model_transform); render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Right hand") .bind_pipeline(hands_pipeline.hot()) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) @@ -423,8 +423,8 @@ fn main() -> anyhow::Result<()> { occlusion_texture, AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, ) - .record_subpass(move |subpass, _| { - subpass + .record_pipeline(move |pipeline, _| { + pipeline .bind_index_buffer(index_buf, vk::IndexType::UINT32) .bind_vertex_buffer(vertex_buf) .push_constants(bytes_of(&push_consts)) @@ -440,7 +440,7 @@ fn main() -> anyhow::Result<()> { let push_consts = PushConstants::new(Mat4::IDENTITY); render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Woolly Mammoth") .bind_pipeline(mammoth_pipeline.hot()) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) @@ -461,8 +461,8 @@ fn main() -> anyhow::Result<()> { occlusion_texture, AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, ) - .record_subpass(move |subpass, _| { - subpass + .record_pipeline(move |pipeline, _| { + pipeline .bind_index_buffer(index_buf, vk::IndexType::UINT32) .bind_vertex_buffer(vertex_buf) .push_constants(bytes_of(&push_consts)) @@ -762,7 +762,7 @@ fn load_texture( ), )?); - let mut render_graph = RenderGraph::default(); + let mut render_graph = Graph::default(); let staging_buf = render_graph.bind_node(staging_buf); let texture_image = render_graph.bind_node(&texture); render_graph.copy_buffer_to_image(staging_buf, texture_image); diff --git a/examples/vsm_omni.rs b/examples/vsm_omni.rs index e107e6a9..91aa663a 100644 --- a/examples/vsm_omni.rs +++ b/examples/vsm_omni.rs @@ -241,7 +241,7 @@ fn main() -> anyhow::Result<()> { if input.key_held(KeyCode::Tab) { frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("DEBUG") .bind_pipeline(&debug_pipeline) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) @@ -259,8 +259,8 @@ fn main() -> anyhow::Result<()> { .store_color(0, frame.swapchain_image) .clear_depth_stencil(depth_image) .store_depth_stencil(depth_image) - .record_subpass(move |subpass, _| { - subpass + .record_pipeline(move |pipeline, _| { + pipeline .bind_index_buffer(model_mesh_index_buf, vk::IndexType::UINT32) .bind_vertex_buffer(model_mesh_vertex_buf) .push_constants(cast_slice(&model_transform)) @@ -275,7 +275,7 @@ fn main() -> anyhow::Result<()> { if use_geometry_shader { frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Shadow (Using geometry shader)") .bind_pipeline(&shadow_pipeline) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) @@ -287,8 +287,8 @@ fn main() -> anyhow::Result<()> { .clear_color_value(0, shadow_faces_node, [light.range, light.range, 0.0, 0.0]) .store_color(0, shadow_faces_node) .clear_depth_stencil(shadow_depth_image) - .record_subpass(move |subpass, _| { - subpass + .record_pipeline(move |pipeline, _| { + pipeline .bind_index_buffer(model_shadow_index_buf, vk::IndexType::UINT32) .bind_vertex_buffer(model_shadow_vertex_buf) .push_constants(cast_slice(&model_transform)) @@ -320,7 +320,7 @@ fn main() -> anyhow::Result<()> { frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Shadow") .bind_pipeline(&shadow_pipeline) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) @@ -341,8 +341,8 @@ fn main() -> anyhow::Result<()> { ) .store_color_as(0, shadow_faces_node, shadow_faces_view_info) .clear_depth_stencil(shadow_depth_image) - .record_subpass(move |subpass, _| { - subpass + .record_pipeline(move |pipeline, _| { + pipeline .bind_index_buffer(model_shadow_index_buf, vk::IndexType::UINT32) .bind_vertex_buffer(model_shadow_vertex_buf) .push_constants(cast_slice(&model_transform)) @@ -361,21 +361,21 @@ fn main() -> anyhow::Result<()> { // separable box blur filter which approximates a gaussian blur frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Blur X") .bind_pipeline(&blur_x_pipeline) .read_descriptor(0, shadow_faces_node) .write_descriptor(1, temp_image) - .record_compute(move |compute, _| { + .record_pipeline(move |compute, _| { compute.dispatch(1, CUBEMAP_SIZE, 6); }) - .end_cmd_buf() - .begin_cmd_buf() + .end_cmd() + .begin_cmd() .with_name("Blur Y") .bind_pipeline(&blur_y_pipeline) .read_descriptor(0, temp_image) .write_descriptor(1, shadow_faces_node) - .record_compute(move |compute, _| { + .record_pipeline(move |compute, _| { compute.dispatch(CUBEMAP_SIZE, 1, 6); }); } @@ -384,7 +384,7 @@ fn main() -> anyhow::Result<()> { // Render the scene directly to the swapchain using the shadow map from the above pass frame .render_graph - .begin_cmd_buf() + .begin_cmd() .with_name("Mesh objects") .bind_pipeline(&mesh_pipeline) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) @@ -408,8 +408,8 @@ fn main() -> anyhow::Result<()> { .clear_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) .clear_depth_stencil(depth_image) - .record_subpass(move |subpass, _| { - subpass + .record_pipeline(move |pipeline, _| { + pipeline .bind_index_buffer(model_mesh_index_buf, vk::IndexType::UINT32) .bind_vertex_buffer(model_mesh_vertex_buf) .push_constants(cast_slice(&model_transform)) diff --git a/src/binding.rs b/src/bind.rs similarity index 88% rename from src/binding.rs rename to src/bind.rs index 6aa42981..9d78989e 100644 --- a/src/binding.rs +++ b/src/bind.rs @@ -1,7 +1,7 @@ use { super::{ AccelerationStructureLeaseNode, AccelerationStructureNode, BufferLeaseNode, BufferNode, - ImageLeaseNode, ImageNode, RenderGraph, + Graph, ImageLeaseNode, ImageNode, }, crate::{ driver::{ @@ -56,9 +56,9 @@ use { // } // } -/// A trait for resources which may be bound to a `RenderGraph`. +/// A trait for resources which may be bound to a `Graph`. /// -/// See [`RenderGraph::bind_node`] and +/// See [`Graph::bind_node`] and /// [`PassRef::bind_pipeline`](super::pass_ref::PassRef::bind_pipeline) for details. pub trait Bind { /// Binds the resource to a graph-like object. @@ -132,9 +132,9 @@ impl Binding { macro_rules! bind { ($name:ident) => { paste::paste! { - impl Bind<&mut RenderGraph, [<$name Node>]> for $name { + impl Bind<&mut Graph, [<$name Node>]> for $name { #[profiling::function] - fn bind(self, graph: &mut RenderGraph) -> [<$name Node>] { + fn bind(self, graph: &mut Graph) -> [<$name Node>] { // In this function we are binding a new item (Image or Buffer or etc) // We will return a new node @@ -146,8 +146,8 @@ macro_rules! bind { } } - impl<'a> Bind<&mut RenderGraph, [<$name Node>]> for &'a Arc<$name> { - fn bind(self, graph: &mut RenderGraph) -> [<$name Node>] { + impl<'a> Bind<&mut Graph, [<$name Node>]> for &'a Arc<$name> { + fn bind(self, graph: &mut Graph) -> [<$name Node>] { // In this function we are binding a borrowed binding (&Arc or // &Arc or etc) @@ -155,9 +155,9 @@ macro_rules! bind { } } - impl Bind<&mut RenderGraph, [<$name Node>]> for Arc<$name> { + impl Bind<&mut Graph, [<$name Node>]> for Arc<$name> { #[profiling::function] - fn bind(self, graph: &mut RenderGraph) -> [<$name Node>] { + fn bind(self, graph: &mut Graph) -> [<$name Node>] { // In this function we are binding an existing binding (Arc or // Arc or etc) @@ -210,9 +210,9 @@ bind!(Buffer); macro_rules! bind_lease { ($name:ident) => { paste::paste! { - impl Bind<&mut RenderGraph, [<$name LeaseNode>]> for Lease<$name> { + impl Bind<&mut Graph, [<$name LeaseNode>]> for Lease<$name> { #[profiling::function] - fn bind(self, graph: &mut RenderGraph) -> [<$name LeaseNode>] { + fn bind(self, graph: &mut Graph) -> [<$name LeaseNode>] { // In this function we are binding a new lease (Lease or Lease or // etc) @@ -225,8 +225,8 @@ macro_rules! bind_lease { } } - impl<'a> Bind<&mut RenderGraph, [<$name LeaseNode>]> for &'a Arc> { - fn bind(self, graph: &mut RenderGraph) -> [<$name LeaseNode>] { + impl<'a> Bind<&mut Graph, [<$name LeaseNode>]> for &'a Arc> { + fn bind(self, graph: &mut Graph) -> [<$name LeaseNode>] { // In this function we are binding a borrowed binding (&Arc> or // &Arc> or etc) @@ -234,9 +234,9 @@ macro_rules! bind_lease { } } - impl Bind<&mut RenderGraph, [<$name LeaseNode>]> for Arc> { + impl Bind<&mut Graph, [<$name LeaseNode>]> for Arc> { #[profiling::function] - fn bind(self, graph: &mut RenderGraph) -> [<$name LeaseNode>] { + fn bind(self, graph: &mut Graph) -> [<$name LeaseNode>] { // In this function we are binding an existing lease binding // (Arc> or Arc> or etc) @@ -286,9 +286,9 @@ bind_lease!(AccelerationStructure); bind_lease!(Image); bind_lease!(Buffer); -/// A trait for resources which may be unbound from a `RenderGraph`. +/// A trait for resources which may be unbound from a `Graph`. /// -/// See [`RenderGraph::unbind_node`] for details. +/// See [`Graph::unbind_node`] for details. pub trait Unbind { /// Unbinds the resource from a graph-like object. /// diff --git a/src/cmd_ref/accel.rs b/src/cmd_ref/accel.rs new file mode 100644 index 00000000..447fdde9 --- /dev/null +++ b/src/cmd_ref/accel.rs @@ -0,0 +1,865 @@ +use { + super::Bindings, + crate::{ + AnyAccelerationStructureNode, + driver::{ + accel_struct::{ + AccelerationStructureGeometry, AccelerationStructureGeometryInfo, + DeviceOrHostAddress, + }, + device::Device, + }, + }, + ash::vk, + std::cell::RefCell, +}; + +/// Recording interface for acceleration structure commands. +/// +/// This structure provides a strongly-typed set of methods which allow acceleration structures to +/// be built and updated. An instance of `Acceleration` is provided to the closure parameter of +/// [`PassRef::record_acceleration`]. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ```no_run +/// # use std::sync::Arc; +/// # use ash::vk; +/// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; +/// # use vk_graph::driver::DriverError; +/// # use vk_graph::driver::device::{Device, DeviceInfo}; +/// # use vk_graph::Graph; +/// # use vk_graph::driver::shader::Shader; +/// # fn main() -> Result<(), DriverError> { +/// # let device = Arc::new(Device::new(DeviceInfo::default())?); +/// # let mut my_graph = Graph::default(); +/// # let info = AccelerationStructureInfo::blas(1); +/// my_graph.begin_cmd().with_name("my acceleration command") +/// .record_acceleration(move |acceleration, bindings| { +/// // During this closure we have access to the acceleration methods! +/// }); +/// # Ok(()) } +/// ``` +pub struct Acceleration<'a> { + pub(super) bindings: Bindings<'a>, + pub(super) cmd_buf: vk::CommandBuffer, + pub(super) device: &'a Device, +} + +impl Acceleration<'_> { + /// Build an acceleration structure. + /// + /// Requires a scratch buffer which was created with the following requirements: + /// + /// - Flags must include [`vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS`] + /// - Size must be equal to or greater than the `build_size` value returned by + /// [`AccelerationStructure::size_of`] aligned to `min_accel_struct_scratch_offset_alignment` + /// of + /// [`PhysicalDevice::accel_struct_properties`](crate::driver::physical_device::PhysicalDevice::accel_struct_properties). + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// # use std::sync::Arc; + /// # use ash::vk; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureGeometry, AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, AccelerationStructureInfo, DeviceOrHostAddress}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::Graph; + /// # use vk_graph::driver::shader::Shader; + /// # fn main() -> Result<(), DriverError> { + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let mut my_graph = Graph::default(); + /// # let info = AccelerationStructureInfo::blas(1); + /// # let blas_accel_struct = AccelerationStructure::create(&device, info)?; + /// # let blas_node = my_graph.bind_node(blas_accel_struct); + /// # let scratch_buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS); + /// # let scratch_buf = Buffer::create(&device, scratch_buf_info)?; + /// # let scratch_buf = my_graph.bind_node(scratch_buf); + /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::INDEX_BUFFER); + /// # let my_idx_buf = Buffer::create(&device, buf_info)?; + /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::VERTEX_BUFFER); + /// # let my_vtx_buf = Buffer::create(&device, buf_info)?; + /// # let index_node = my_graph.bind_node(my_idx_buf); + /// # let vertex_node = my_graph.bind_node(my_vtx_buf); + /// my_graph.begin_cmd().with_name("my acceleration pass") + /// .read_node(index_node) + /// .read_node(vertex_node) + /// .write_node(blas_node) + /// .write_node(scratch_buf) + /// .record_acceleration(move |acceleration, bindings| { + /// let geom = AccelerationStructureGeometry { + /// max_primitive_count: 64, + /// flags: vk::GeometryFlagsKHR::OPAQUE, + /// geometry: AccelerationStructureGeometryData::Triangles { + /// index_addr: DeviceOrHostAddress::DeviceAddress( + /// bindings[index_node].device_address() + /// ), + /// index_type: vk::IndexType::UINT32, + /// max_vertex: 42, + /// transform_addr: None, + /// vertex_addr: DeviceOrHostAddress::DeviceAddress( + /// bindings[vertex_node].device_address(), + /// ), + /// vertex_format: vk::Format::R32G32B32_SFLOAT, + /// vertex_stride: 12, + /// }, + /// }; + /// let build_range = vk::AccelerationStructureBuildRangeInfoKHR { + /// first_vertex: 0, + /// primitive_count: 1, + /// primitive_offset: 0, + /// transform_offset: 0, + /// }; + /// let info = AccelerationStructureGeometryInfo::blas([(geom, build_range)]); + /// + /// acceleration.build_structure(&info, blas_node, bindings[scratch_buf].device_address()); + /// }); + /// # Ok(()) } + /// ``` + pub fn build_structure( + &self, + info: &AccelerationStructureGeometryInfo<( + AccelerationStructureGeometry, + vk::AccelerationStructureBuildRangeInfoKHR, + )>, + accel_struct: impl Into, + scratch_addr: impl Into, + ) -> &Self { + #[derive(Default)] + struct Tls { + geometries: Vec>, + ranges: Vec, + } + + thread_local! { + static TLS: RefCell = Default::default(); + } + + let accel_struct = accel_struct.into(); + let scratch_addr = scratch_addr.into().into(); + + TLS.with_borrow_mut(|tls| { + tls.geometries.clear(); + tls.ranges.clear(); + + for (geometry, range) in info.geometries.iter() { + tls.geometries.push(geometry.into()); + tls.ranges.push(*range); + } + + unsafe { + Device::expect_accel_struct_ext(self.device).cmd_build_acceleration_structures( + self.cmd_buf, + &[vk::AccelerationStructureBuildGeometryInfoKHR::default() + .ty(info.ty) + .flags(info.flags) + .mode(vk::BuildAccelerationStructureModeKHR::BUILD) + .dst_acceleration_structure(self.bindings[accel_struct].handle) + .geometries(&tls.geometries) + .scratch_data(scratch_addr)], + &[&tls.ranges], + ); + } + }); + + self + } + + /// Build an acceleration structure with some parameters provided on the device. + /// + /// `range` is a buffer device address which points to `info.geometry.len()` + /// [vk::VkAccelerationStructureBuildRangeInfoKHR] structures defining dynamic offsets to the + /// addresses where geometry data is stored, as defined by `info`. + pub fn build_structure_indirect( + &self, + info: &AccelerationStructureGeometryInfo, + accel_struct: impl Into, + scratch_addr: impl Into, + range_base: vk::DeviceAddress, + range_stride: u32, + ) -> &Self { + #[derive(Default)] + struct Tls { + geometries: Vec>, + max_primitive_counts: Vec, + } + + thread_local! { + static TLS: RefCell = Default::default(); + } + + let accel_struct = accel_struct.into(); + let scratch_addr = scratch_addr.into().into(); + + TLS.with_borrow_mut(|tls| { + tls.geometries.clear(); + tls.max_primitive_counts.clear(); + + for geometry in info.geometries.iter() { + tls.geometries.push(geometry.into()); + tls.max_primitive_counts.push(geometry.max_primitive_count); + } + + unsafe { + Device::expect_accel_struct_ext(self.device) + .cmd_build_acceleration_structures_indirect( + self.cmd_buf, + &[vk::AccelerationStructureBuildGeometryInfoKHR::default() + .ty(info.ty) + .flags(info.flags) + .mode(vk::BuildAccelerationStructureModeKHR::BUILD) + .dst_acceleration_structure(self.bindings[accel_struct].handle) + .geometries(&tls.geometries) + .scratch_data(scratch_addr)], + &[range_base], + &[range_stride], + &[&tls.max_primitive_counts], + ); + } + }); + + self + } + + /// Build acceleration structures. + /// + /// There is no ordering or synchronization implied between any of the individual acceleration + /// structure builds. + pub fn build_structures(&self, infos: &[AccelerationStructureBuildInfo]) -> &Self { + #[derive(Default)] + struct Tls { + geometries: Vec>, + ranges: Vec, + } + + thread_local! { + static TLS: RefCell = Default::default(); + } + + TLS.with_borrow_mut(|tls| { + tls.geometries.clear(); + tls.geometries.extend(infos.iter().flat_map(|info| { + info.build_data.geometries.iter().map(|(geometry, _)| { + <&AccelerationStructureGeometry as Into< + vk::AccelerationStructureGeometryKHR, + >>::into(geometry) + }) + })); + + tls.ranges.clear(); + tls.ranges.extend( + infos + .iter() + .flat_map(|info| info.build_data.geometries.iter().map(|(_, range)| *range)), + ); + + let vk_ranges = { + let mut start = 0; + let mut vk_ranges = Vec::with_capacity(infos.len()); + for info in infos { + let end = start + info.build_data.geometries.len(); + vk_ranges.push(&tls.ranges[start..end]); + start = end; + } + + vk_ranges + }; + + let vk_infos = { + let mut start = 0; + let mut vk_infos = Vec::with_capacity(infos.len()); + for info in infos { + let end = start + info.build_data.geometries.len(); + vk_infos.push( + vk::AccelerationStructureBuildGeometryInfoKHR::default() + .ty(info.build_data.ty) + .flags(info.build_data.flags) + .mode(vk::BuildAccelerationStructureModeKHR::BUILD) + .dst_acceleration_structure(self.bindings[info.accel_struct].handle) + .geometries(&tls.geometries[start..end]) + .scratch_data(info.scratch_addr.into()), + ); + start = end; + } + + vk_infos + }; + + unsafe { + Device::expect_accel_struct_ext(self.device).cmd_build_acceleration_structures( + self.cmd_buf, + &vk_infos, + &vk_ranges, + ); + } + }); + + self + } + + /// Builds acceleration structures with some parameters provided on the device. + /// + /// There is no ordering or synchronization implied between any of the individual acceleration + /// structure builds. + /// + /// See [Self::build_structure_indirect] + pub fn build_structures_indirect( + &self, + infos: &[AccelerationStructureIndirectBuildInfo], + ) -> &Self { + #[derive(Default)] + struct Tls { + geometries: Vec>, + max_primitive_counts: Vec, + range_bases: Vec, + range_strides: Vec, + } + + thread_local! { + static TLS: RefCell = Default::default(); + } + + TLS.with_borrow_mut(|tls| { + tls.geometries.clear(); + tls.geometries.extend(infos.iter().flat_map(|info| { + info.build_data.geometries.iter().map( + <&AccelerationStructureGeometry as Into< + vk::AccelerationStructureGeometryKHR, + >>::into, + ) + })); + + tls.max_primitive_counts.clear(); + tls.max_primitive_counts + .extend(infos.iter().flat_map(|info| { + info.build_data + .geometries + .iter() + .map(|geometry| geometry.max_primitive_count) + })); + + tls.range_bases.clear(); + tls.range_strides.clear(); + let (vk_infos, vk_max_primitive_counts) = { + let mut start = 0; + let mut vk_infos = Vec::with_capacity(infos.len()); + let mut vk_max_primitive_counts = Vec::with_capacity(infos.len()); + for info in infos { + let end = start + info.build_data.geometries.len(); + vk_infos.push( + vk::AccelerationStructureBuildGeometryInfoKHR::default() + .ty(info.build_data.ty) + .flags(info.build_data.flags) + .mode(vk::BuildAccelerationStructureModeKHR::BUILD) + .dst_acceleration_structure(self.bindings[info.accel_struct].handle) + .geometries(&tls.geometries[start..end]) + .scratch_data(info.scratch_data.into()), + ); + vk_max_primitive_counts.push(&tls.max_primitive_counts[start..end]); + start = end; + + tls.range_bases.push(info.range_base); + tls.range_strides.push(info.range_stride); + } + + (vk_infos, vk_max_primitive_counts) + }; + + unsafe { + Device::expect_accel_struct_ext(self.device) + .cmd_build_acceleration_structures_indirect( + self.cmd_buf, + &vk_infos, + &tls.range_bases, + &tls.range_strides, + &vk_max_primitive_counts, + ); + } + }); + + self + } + + /// Update an acceleration structure. + /// + /// Requires a scratch buffer which was created with the following requirements: + /// + /// - Flags must include [`vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS`] + /// - Size must be equal to or greater than the `update_size` value returned by + /// [`AccelerationStructure::size_of`] aligned to `min_accel_struct_scratch_offset_alignment` + /// of + /// [`PhysicalDevice::accel_struct_properties`](crate::driver::physical_device::PhysicalDevice::accel_struct_properties). + pub fn update_structure( + &self, + info: &AccelerationStructureGeometryInfo<( + AccelerationStructureGeometry, + vk::AccelerationStructureBuildRangeInfoKHR, + )>, + src_accel_struct: impl Into, + dst_accel_struct: impl Into, + scratch_addr: impl Into, + ) -> &Self { + #[derive(Default)] + struct Tls { + geometries: Vec>, + ranges: Vec, + } + + thread_local! { + static TLS: RefCell = Default::default(); + } + + let src_accel_struct = src_accel_struct.into(); + let dst_accel_struct = dst_accel_struct.into(); + let scratch_addr = scratch_addr.into().into(); + + TLS.with_borrow_mut(|tls| { + tls.geometries.clear(); + tls.ranges.clear(); + + for (geometry, range) in info.geometries.iter() { + tls.geometries.push(geometry.into()); + tls.ranges.push(*range); + } + + unsafe { + Device::expect_accel_struct_ext(self.device).cmd_build_acceleration_structures( + self.cmd_buf, + &[vk::AccelerationStructureBuildGeometryInfoKHR::default() + .ty(info.ty) + .flags(info.flags) + .mode(vk::BuildAccelerationStructureModeKHR::UPDATE) + .dst_acceleration_structure(self.bindings[dst_accel_struct].handle) + .src_acceleration_structure(self.bindings[src_accel_struct].handle) + .geometries(&tls.geometries) + .scratch_data(scratch_addr)], + &[&tls.ranges], + ); + } + }); + + self + } + + /// Update an acceleration structure with some parameters provided on the device. + /// + /// `range` is a buffer device address which points to `info.geometry.len()` + /// [vk::VkAccelerationStructureBuildRangeInfoKHR] structures defining dynamic offsets to the + /// addresses where geometry data is stored, as defined by `info`. + pub fn update_structure_indirect( + &self, + info: &AccelerationStructureGeometryInfo, + src_accel_struct: impl Into, + dst_accel_struct: impl Into, + scratch_addr: impl Into, + range_base: vk::DeviceAddress, + range_stride: u32, + ) -> &Self { + #[derive(Default)] + struct Tls { + geometries: Vec>, + max_primitive_counts: Vec, + } + + thread_local! { + static TLS: RefCell = Default::default(); + } + + let src_accel_struct = src_accel_struct.into(); + let dst_accel_struct = dst_accel_struct.into(); + let scratch_addr = scratch_addr.into().into(); + + TLS.with_borrow_mut(|tls| { + tls.geometries.clear(); + tls.max_primitive_counts.clear(); + + for geometry in info.geometries.iter() { + tls.geometries.push(geometry.into()); + tls.max_primitive_counts.push(geometry.max_primitive_count); + } + + unsafe { + Device::expect_accel_struct_ext(self.device) + .cmd_build_acceleration_structures_indirect( + self.cmd_buf, + &[vk::AccelerationStructureBuildGeometryInfoKHR::default() + .ty(info.ty) + .flags(info.flags) + .mode(vk::BuildAccelerationStructureModeKHR::UPDATE) + .src_acceleration_structure(self.bindings[src_accel_struct].handle) + .dst_acceleration_structure(self.bindings[dst_accel_struct].handle) + .geometries(&tls.geometries) + .scratch_data(scratch_addr)], + &[range_base], + &[range_stride], + &[&tls.max_primitive_counts], + ); + } + }); + + self + } + + /// Update acceleration structures. + /// + /// There is no ordering or synchronization implied between any of the individual acceleration + /// structure updates. + pub fn update_structures(&self, infos: &[AccelerationStructureUpdateInfo]) -> &Self { + #[derive(Default)] + struct Tls { + geometries: Vec>, + ranges: Vec, + } + + thread_local! { + static TLS: RefCell = Default::default(); + } + + TLS.with_borrow_mut(|tls| { + tls.geometries.clear(); + tls.geometries.extend(infos.iter().flat_map(|info| { + info.update_data.geometries.iter().map(|(geometry, _)| { + <&AccelerationStructureGeometry as Into< + vk::AccelerationStructureGeometryKHR, + >>::into(geometry) + }) + })); + + tls.ranges.clear(); + tls.ranges.extend( + infos + .iter() + .flat_map(|info| info.update_data.geometries.iter().map(|(_, range)| *range)), + ); + + let vk_ranges = { + let mut start = 0; + let mut vk_ranges = Vec::with_capacity(infos.len()); + for info in infos { + let end = start + info.update_data.geometries.len(); + vk_ranges.push(&tls.ranges[start..end]); + start = end; + } + + vk_ranges + }; + + let vk_infos = { + let mut start = 0; + let mut vk_infos = Vec::with_capacity(infos.len()); + for info in infos { + let end = start + info.update_data.geometries.len(); + vk_infos.push( + vk::AccelerationStructureBuildGeometryInfoKHR::default() + .ty(info.update_data.ty) + .flags(info.update_data.flags) + .mode(vk::BuildAccelerationStructureModeKHR::UPDATE) + .dst_acceleration_structure(self.bindings[info.dst_accel_struct].handle) + .src_acceleration_structure(self.bindings[info.src_accel_struct].handle) + .geometries(&tls.geometries[start..end]) + .scratch_data(info.scratch_addr.into()), + ); + start = end; + } + + vk_infos + }; + + unsafe { + Device::expect_accel_struct_ext(self.device).cmd_build_acceleration_structures( + self.cmd_buf, + &vk_infos, + &vk_ranges, + ); + } + }); + + self + } + + /// Updates acceleration structures with some parameters provided on the device. + /// + /// There is no ordering or synchronization implied between any of the individual acceleration + /// structure updates. + /// + /// See [Self::update_structure_indirect] + pub fn update_structures_indirect( + &self, + infos: &[AccelerationStructureIndirectUpdateInfo], + ) -> &Self { + #[derive(Default)] + struct Tls { + geometries: Vec>, + max_primitive_counts: Vec, + range_bases: Vec, + range_strides: Vec, + } + + thread_local! { + static TLS: RefCell = Default::default(); + } + + TLS.with_borrow_mut(|tls| { + tls.geometries.clear(); + tls.geometries.extend(infos.iter().flat_map(|info| { + info.update_data.geometries.iter().map( + <&AccelerationStructureGeometry as Into< + vk::AccelerationStructureGeometryKHR, + >>::into, + ) + })); + + tls.max_primitive_counts.clear(); + tls.max_primitive_counts + .extend(infos.iter().flat_map(|info| { + info.update_data + .geometries + .iter() + .map(|geometry| geometry.max_primitive_count) + })); + + tls.range_bases.clear(); + tls.range_strides.clear(); + let (vk_infos, vk_max_primitive_counts) = { + let mut start = 0; + let mut vk_infos = Vec::with_capacity(infos.len()); + let mut vk_max_primitive_counts = Vec::with_capacity(infos.len()); + for info in infos { + let end = start + info.update_data.geometries.len(); + vk_infos.push( + vk::AccelerationStructureBuildGeometryInfoKHR::default() + .ty(info.update_data.ty) + .flags(info.update_data.flags) + .mode(vk::BuildAccelerationStructureModeKHR::UPDATE) + .src_acceleration_structure(self.bindings[info.src_accel_struct].handle) + .dst_acceleration_structure(self.bindings[info.dst_accel_struct].handle) + .geometries(&tls.geometries[start..end]) + .scratch_data(info.scratch_addr.into()), + ); + vk_max_primitive_counts.push(&tls.max_primitive_counts[start..end]); + start = end; + + tls.range_bases.push(info.range_base); + tls.range_strides.push(info.range_stride); + } + + (vk_infos, vk_max_primitive_counts) + }; + + unsafe { + Device::expect_accel_struct_ext(self.device) + .cmd_build_acceleration_structures_indirect( + self.cmd_buf, + &vk_infos, + &tls.range_bases, + &tls.range_strides, + &vk_max_primitive_counts, + ); + } + }); + + self + } +} + +/// Specifies the information and data used to build an acceleration structure. +/// +/// See +/// [VkAccelerationStructureBuildGeometryInfoKHR](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkAccelerationStructureBuildGeometryInfoKHR.html) +/// for more information. +#[derive(Clone, Debug)] +pub struct AccelerationStructureBuildInfo { + /// The acceleration structure to be written. + pub accel_struct: AnyAccelerationStructureNode, + + /// Specifies the geometry data to use when building the acceleration structure. + pub build_data: AccelerationStructureGeometryInfo<( + AccelerationStructureGeometry, + vk::AccelerationStructureBuildRangeInfoKHR, + )>, + + /// The temporary buffer or host address (with enough capacity per + /// [AccelerationStructure::size_of]). + pub scratch_addr: DeviceOrHostAddress, +} + +impl AccelerationStructureBuildInfo { + /// Constructs new acceleration structure build information. + pub fn new( + accel_struct: impl Into, + build_data: AccelerationStructureGeometryInfo<( + AccelerationStructureGeometry, + vk::AccelerationStructureBuildRangeInfoKHR, + )>, + scratch_addr: impl Into, + ) -> Self { + let accel_struct = accel_struct.into(); + let scratch_addr = scratch_addr.into(); + + Self { + accel_struct, + build_data, + scratch_addr, + } + } +} + +/// Specifies the information and data used to build an acceleration structure with some parameters +/// sourced on the device. +/// +/// See +/// [VkAccelerationStructureBuildGeometryInfoKHR](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkAccelerationStructureBuildGeometryInfoKHR.html) +/// for more information. +#[derive(Clone, Debug)] +pub struct AccelerationStructureIndirectBuildInfo { + /// The acceleration structure to be written. + pub accel_struct: AnyAccelerationStructureNode, + + /// Specifies the geometry data to use when building the acceleration structure. + pub build_data: AccelerationStructureGeometryInfo, + + /// A buffer device addresses which points to `data.geometry.len()` + /// [vk::VkAccelerationStructureBuildRangeInfoKHR] structures defining dynamic offsets to the + /// addresses where geometry data is stored. + pub range_base: vk::DeviceAddress, + + /// Byte stride between elements of [range]. + pub range_stride: u32, + + /// The temporary buffer or host address (with enough capacity per + /// [AccelerationStructure::size_of]). + pub scratch_data: DeviceOrHostAddress, +} + +impl AccelerationStructureIndirectBuildInfo { + /// Constructs new acceleration structure indirect build information. + pub fn new( + accel_struct: impl Into, + build_data: AccelerationStructureGeometryInfo, + range_base: vk::DeviceAddress, + + range_stride: u32, + scratch_data: impl Into, + ) -> Self { + let accel_struct = accel_struct.into(); + let scratch_data = scratch_data.into(); + + Self { + accel_struct, + build_data, + range_base, + range_stride, + scratch_data, + } + } +} + +/// Specifies the information and data used to update an acceleration structure with some parameters +/// sourced on the device. +/// +/// See +/// [VkAccelerationStructureBuildGeometryInfoKHR](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkAccelerationStructureBuildGeometryInfoKHR.html) +/// for more information. +#[derive(Clone, Debug)] +pub struct AccelerationStructureIndirectUpdateInfo { + /// The acceleration structure to be written. + pub dst_accel_struct: AnyAccelerationStructureNode, + + /// A buffer device addresses which points to `data.geometry.len()` + /// [vk::VkAccelerationStructureBuildRangeInfoKHR] structures defining dynamic offsets to the + /// addresses where geometry data is stored. + pub range_base: vk::DeviceAddress, + + /// Byte stride between elements of [range]. + pub range_stride: u32, + + /// The temporary buffer or host address (with enough capacity per + /// [AccelerationStructure::size_of]). + pub scratch_addr: DeviceOrHostAddress, + + /// The source acceleration structure to be read. + pub src_accel_struct: AnyAccelerationStructureNode, + + /// Specifies the geometry data to use when building the acceleration structure. + pub update_data: AccelerationStructureGeometryInfo, +} + +impl AccelerationStructureIndirectUpdateInfo { + /// Constructs new acceleration structure indirect update information. + pub fn new( + src_accel_struct: impl Into, + dst_accel_struct: impl Into, + update_data: AccelerationStructureGeometryInfo, + range_base: vk::DeviceAddress, + + range_stride: u32, + scratch_addr: impl Into, + ) -> Self { + let src_accel_struct = src_accel_struct.into(); + let dst_accel_struct = dst_accel_struct.into(); + let scratch_addr = scratch_addr.into(); + + Self { + dst_accel_struct, + range_base, + range_stride, + scratch_addr, + src_accel_struct, + update_data, + } + } +} + +/// Specifies the information and data used to update an acceleration structure. +/// +/// See +/// [VkAccelerationStructureBuildGeometryInfoKHR](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkAccelerationStructureBuildGeometryInfoKHR.html) +/// for more information. +#[derive(Clone, Debug)] +pub struct AccelerationStructureUpdateInfo { + /// The acceleration structure to be written. + pub dst_accel_struct: AnyAccelerationStructureNode, + + /// The temporary buffer or host address (with enough capacity per + /// [AccelerationStructure::size_of]). + pub scratch_addr: DeviceOrHostAddress, + + /// The source acceleration structure to be read. + pub src_accel_struct: AnyAccelerationStructureNode, + + /// Specifies the geometry data to use when updating the acceleration structure. + pub update_data: AccelerationStructureGeometryInfo<( + AccelerationStructureGeometry, + vk::AccelerationStructureBuildRangeInfoKHR, + )>, +} + +impl AccelerationStructureUpdateInfo { + /// Constructs new acceleration structure update information. + pub fn new( + src_accel_struct: impl Into, + dst_accel_struct: impl Into, + update_data: AccelerationStructureGeometryInfo<( + AccelerationStructureGeometry, + vk::AccelerationStructureBuildRangeInfoKHR, + )>, + scratch_addr: impl Into, + ) -> Self { + let src_accel_struct = src_accel_struct.into(); + let dst_accel_struct = dst_accel_struct.into(); + let scratch_addr = scratch_addr.into(); + + Self { + dst_accel_struct, + scratch_addr, + src_accel_struct, + update_data, + } + } +} diff --git a/src/cmd_ref/bind.rs b/src/cmd_ref/bind.rs new file mode 100644 index 00000000..e69de29b diff --git a/src/cmd_ref/compute.rs b/src/cmd_ref/compute.rs new file mode 100644 index 00000000..1469a1e0 --- /dev/null +++ b/src/cmd_ref/compute.rs @@ -0,0 +1,415 @@ +use { + super::{Bindings, pipeline::PipelineCommandRef}, + crate::{ + AnyBufferNode, + driver::{compute::ComputePipeline, device::Device}, + }, + ash::vk, + log::trace, + std::sync::Arc, +}; + +/// Recording interface for computing commands. +/// +/// This structure provides a strongly-typed set of methods which allow compute shader code to be +/// executed. An instance of `Compute` is provided to the closure parameter of +/// [`PipelineCommandRef::record_pipeline`] which may be accessed by binding a [`ComputePipeline`] to a +/// render pass. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ```no_run +/// # use std::sync::Arc; +/// # use ash::vk; +/// # use vk_graph::driver::DriverError; +/// # use vk_graph::driver::device::{Device, DeviceInfo}; +/// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; +/// # use vk_graph::driver::shader::{Shader}; +/// # use vk_graph::Graph; +/// # fn main() -> Result<(), DriverError> { +/// # let device = Arc::new(Device::new(DeviceInfo::default())?); +/// # let info = ComputePipelineInfo::default(); +/// # let shader = Shader::new_compute([0u8; 1].as_slice()); +/// # let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); +/// # let mut my_graph = Graph::default(); +/// my_graph.begin_cmd().with_name("my compute pass") +/// .bind_pipeline(&my_compute_pipeline) +/// .record_pipeline(move |compute, bindings| { +/// // During this closure we have access to the compute methods! +/// }); +/// # Ok(()) } +/// ``` +pub struct Compute<'a> { + pub(super) bindings: Bindings<'a>, + pub(super) cmd_buf: vk::CommandBuffer, + pub(super) device: &'a Device, + pub(super) pipeline: Arc, +} + +impl Compute<'_> { + /// [Dispatch] compute work items. + /// + /// When the command is executed, a global workgroup consisting of + /// `group_count_x × group_count_y × group_count_z` local workgroups is assembled. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # vk_shader_macros::glsl!(r#" + /// #version 450 + /// #pragma shader_stage(compute) + /// + /// layout(set = 0, binding = 0, std430) restrict writeonly buffer MyBufer { + /// uint my_buf[]; + /// }; + /// + /// void main() { + /// // TODO + /// } + /// # "#); + /// ``` + /// + /// ```no_run + /// # use std::sync::Arc; + /// # use ash::vk; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; + /// # use vk_graph::driver::shader::{Shader}; + /// # use vk_graph::Graph; + /// # fn main() -> Result<(), DriverError> { + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::STORAGE_BUFFER); + /// # let my_buf = Buffer::create(&device, buf_info)?; + /// # let info = ComputePipelineInfo::default(); + /// # let shader = Shader::new_compute([0u8; 1].as_slice()); + /// # let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); + /// # let mut my_graph = Graph::default(); + /// # let my_buf_node = my_graph.bind_node(my_buf); + /// my_graph.begin_cmd().with_name("fill my_buf_node with data") + /// .bind_pipeline(&my_compute_pipeline) + /// .write_descriptor(0, my_buf_node) + /// .record_pipeline(move |compute, bindings| { + /// compute.dispatch(128, 64, 32); + /// }); + /// # Ok(()) } + /// ``` + /// + /// [Dispatch]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCmdDispatch.html + #[profiling::function] + pub fn dispatch(&self, group_count_x: u32, group_count_y: u32, group_count_z: u32) -> &Self { + unsafe { + self.device + .cmd_dispatch(self.cmd_buf, group_count_x, group_count_y, group_count_z); + } + + self + } + + /// [Dispatch] compute work items with non-zero base values for the workgroup IDs. + /// + /// When the command is executed, a global workgroup consisting of + /// `group_count_x × group_count_y × group_count_z` local workgroups is assembled, with + /// WorkgroupId values ranging from `[base_group*, base_group* + group_count*)` in each + /// component. + /// + /// [`Compute::dispatch`] is equivalent to + /// `dispatch_base(0, 0, 0, group_count_x, group_count_y, group_count_z)`. + /// + /// [Dispatch]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCmdDispatchBase.html + #[profiling::function] + pub fn dispatch_base( + &self, + base_group_x: u32, + base_group_y: u32, + base_group_z: u32, + group_count_x: u32, + group_count_y: u32, + group_count_z: u32, + ) -> &Self { + unsafe { + self.device.cmd_dispatch_base( + self.cmd_buf, + base_group_x, + base_group_y, + base_group_z, + group_count_x, + group_count_y, + group_count_z, + ); + } + + self + } + + /// Dispatch compute work items with indirect parameters. + /// + /// `dispatch_indirect` behaves similarly to [`Compute::dispatch`] except that the parameters + /// are read by the device from `args_buf` during execution. The parameters of the dispatch are + /// encoded in a [`vk::DispatchIndirectCommand`] structure taken from `args_buf` starting at + /// `args_offset`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// # use std::sync::Arc; + /// # use std::mem::size_of; + /// # use ash::vk; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; + /// # use vk_graph::driver::shader::{Shader}; + /// # use vk_graph::Graph; + /// # fn main() -> Result<(), DriverError> { + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::STORAGE_BUFFER); + /// # let my_buf = Buffer::create(&device, buf_info)?; + /// # let info = ComputePipelineInfo::default(); + /// # let shader = Shader::new_compute([0u8; 1].as_slice()); + /// # let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); + /// # let mut my_graph = Graph::default(); + /// # let my_buf_node = my_graph.bind_node(my_buf); + /// const CMD_SIZE: usize = size_of::(); + /// + /// let cmd = vk::DispatchIndirectCommand { + /// x: 1, + /// y: 2, + /// z: 3, + /// }; + /// let cmd_data = unsafe { + /// std::slice::from_raw_parts(&cmd as *const _ as *const _, CMD_SIZE) + /// }; + /// + /// let args_buf_flags = vk::BufferUsageFlags::STORAGE_BUFFER; + /// let args_buf = Buffer::create_from_slice(&device, args_buf_flags, cmd_data)?; + /// let args_buf_node = my_graph.bind_node(args_buf); + /// + /// my_graph.begin_cmd().with_name("fill my_buf_node with data") + /// .bind_pipeline(&my_compute_pipeline) + /// .read_node(args_buf_node) + /// .write_descriptor(0, my_buf_node) + /// .record_pipeline(move |compute, bindings| { + /// compute.dispatch_indirect(args_buf_node, 0); + /// }); + /// # Ok(()) } + /// ``` + /// + /// [Dispatch]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCmdDispatchIndirect.html + /// [VkDispatchIndirectCommand]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkDispatchIndirectCommand.html + #[profiling::function] + pub fn dispatch_indirect( + &self, + args_buf: impl Into, + args_offset: vk::DeviceSize, + ) -> &Self { + let args_buf = args_buf.into(); + + unsafe { + self.device.cmd_dispatch_indirect( + self.cmd_buf, + self.bindings[args_buf].handle, + args_offset, + ); + } + + self + } + + /// Updates push constants. + /// + /// Push constants represent a high speed path to modify constant data in pipelines that is + /// expected to outperform memory-backed resource updates. + /// + /// Push constant values can be updated incrementally, causing shader stages to read the new + /// data for push constants modified by this command, while still reading the previous data for + /// push constants not modified by this command. + /// + /// # Device limitations + /// + /// See + /// [`device.physical_device.props.limits.max_push_constants_size`](vk::PhysicalDeviceLimits) + /// for the limits of the current device. You may also check [gpuinfo.org] for a listing of + /// reported limits on other devices. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # vk_shader_macros::glsl!(r#" + /// #version 450 + /// #pragma shader_stage(compute) + /// + /// layout(push_constant) uniform PushConstants { + /// layout(offset = 0) uint the_answer; + /// } push_constants; + /// + /// void main() + /// { + /// // TODO: Add bindings to read/write things! + /// } + /// # "#); + /// ``` + /// + /// ```no_run + /// # use std::sync::Arc; + /// # use ash::vk; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; + /// # use vk_graph::driver::shader::{Shader}; + /// # use vk_graph::Graph; + /// # fn main() -> Result<(), DriverError> { + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let info = ComputePipelineInfo::default(); + /// # let shader = Shader::new_compute([0u8; 1].as_slice()); + /// # let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); + /// # let mut my_graph = Graph::default(); + /// my_graph.begin_cmd().with_name("compute the ultimate question") + /// .bind_pipeline(&my_compute_pipeline) + /// .record_pipeline(move |compute, bindings| { + /// compute.push_constants(&[42]) + /// .dispatch(1, 1, 1); + /// }); + /// # Ok(()) } + /// ``` + /// + /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all + pub fn push_constants(&self, data: &[u8]) -> &Self { + self.push_constants_offset(0, data) + } + + /// Updates push constants starting at the given `offset`. + /// + /// Behaves similary to [`Compute::push_constants`] except that `offset` describes the position + /// at which `data` updates the push constants of the currently bound pipeline. This may be used + /// to update a subset or single field of previously set push constant data. + /// + /// # Device limitations + /// + /// See + /// [`device.physical_device.props.limits.max_push_constants_size`](vk::PhysicalDeviceLimits) + /// for the limits of the current device. You may also check [gpuinfo.org] for a listing of + /// reported limits on other devices. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # vk_shader_macros::glsl!(r#" + /// #version 450 + /// #pragma shader_stage(compute) + /// + /// layout(push_constant) uniform PushConstants { + /// layout(offset = 0) uint some_val1; + /// layout(offset = 4) uint some_val2; + /// } push_constants; + /// + /// void main() { + /// // TODO: Add bindings to read/write things! + /// } + /// # "#); + /// ``` + /// + /// ```no_run + /// # use std::sync::Arc; + /// # use ash::vk; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; + /// # use vk_graph::driver::shader::{Shader}; + /// # use vk_graph::Graph; + /// # fn main() -> Result<(), DriverError> { + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let info = ComputePipelineInfo::default(); + /// # let shader = Shader::new_compute([0u8; 1].as_slice()); + /// # let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); + /// # let mut my_graph = Graph::default(); + /// my_graph.begin_cmd().with_name("calculate the wow factor") + /// .bind_pipeline(&my_compute_pipeline) + /// .record_pipeline(move |compute, bindings| { + /// compute.push_constants(&[0x00, 0x00]) + /// .dispatch(1, 1, 1) + /// .push_constants_offset(4, &[0xff]) + /// .dispatch(1, 1, 1); + /// }); + /// # Ok(()) } + /// ``` + /// + /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all + #[profiling::function] + pub fn push_constants_offset(&self, offset: u32, data: &[u8]) -> &Self { + if let Some(push_const) = self.pipeline.push_constants { + // Determine the range of the overall pipline push constants which overlap with `data` + let push_const_end = push_const.offset + push_const.size; + let data_end = offset + data.len() as u32; + let end = data_end.min(push_const_end); + let start = offset.max(push_const.offset); + + if end > start { + trace!( + " push constants {:?} {}..{}", + push_const.stage_flags, start, end + ); + + unsafe { + self.device.cmd_push_constants( + self.cmd_buf, + self.pipeline.layout, + vk::ShaderStageFlags::COMPUTE, + push_const.offset, + &data[(start - offset) as usize..(end - offset) as usize], + ); + } + } + } + + self + } +} + +// NOTE: local implementation of type from super module +impl PipelineCommandRef<'_, ComputePipeline> { + /// Begin recording a computing command buffer. + pub fn record_pipeline( + mut self, + func: impl FnOnce(Compute<'_>, Bindings<'_>) + Send + 'static, + ) -> Self { + let pipeline = Arc::clone( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .pipeline + .as_ref() + .unwrap() + .unwrap_compute(), + ); + + self.cmd.push_execute(move |device, cmd_buf, bindings| { + func( + Compute { + bindings, + cmd_buf, + device, + pipeline, + }, + bindings, + ); + }); + + self + } +} diff --git a/src/cmd_ref/graphic.rs b/src/cmd_ref/graphic.rs new file mode 100644 index 00000000..eefe7db9 --- /dev/null +++ b/src/cmd_ref/graphic.rs @@ -0,0 +1,2339 @@ +use { + super::{AttachmentIndex, Bindings, Info, PipelineCommandRef, Subresource, SubresourceAccess}, + crate::{ + AnyBufferNode, AnyImageNode, Area, Attachment, ClearColorValue, Node, NodeIndex, + SampleCount, + driver::{ + device::Device, + graphic::{DepthStencilMode, GraphicPipeline}, + image::{ + ImageViewInfo, image_subresource_range_contains, image_subresource_range_intersects, + }, + render_pass::ResolveMode, + }, + }, + ash::vk, + log::trace, + std::{cell::RefCell, ops::Range, sync::Arc}, + vk_sync::AccessType, +}; + +/// Recording interface for drawing commands. +/// +/// This structure provides a strongly-typed set of methods which allow rasterization shader code to +/// be executed. An instance of `Draw` is provided to the closure parameter of +/// [`PipelineCommandRef::record_pipeline`] which may be accessed by binding a [`GraphicPipeline`] to a +/// render pass. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ```no_run +/// # use std::sync::Arc; +/// # use ash::vk; +/// # use vk_graph::driver::DriverError; +/// # use vk_graph::driver::device::{Device, DeviceInfo}; +/// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; +/// # use vk_graph::driver::image::{Image, ImageInfo}; +/// # use vk_graph::Graph; +/// # use vk_graph::driver::shader::Shader; +/// # fn main() -> Result<(), DriverError> { +/// # let device = Arc::new(Device::new(DeviceInfo::default())?); +/// # let my_frag_code = [0u8; 1]; +/// # let my_vert_code = [0u8; 1]; +/// # let vert = Shader::new_vertex(my_vert_code.as_slice()); +/// # let frag = Shader::new_fragment(my_frag_code.as_slice()); +/// # let info = GraphicPipelineInfo::default(); +/// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); +/// # let mut my_graph = Graph::default(); +/// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); +/// # let swapchain_image = my_graph.bind_node(Image::create(&device, info)?); +/// my_graph.begin_cmd().with_name("my draw pass") +/// .bind_pipeline(&my_graphic_pipeline) +/// .store_color(0, swapchain_image) +/// .record_pipeline(move |graphic, bindings| { +/// // During this closure we have access to the draw methods! +/// }); +/// # Ok(()) } +/// ``` +pub struct Graphic<'a> { + pub(super) bindings: Bindings<'a>, + pub(super) cmd_buf: vk::CommandBuffer, + pub(super) device: &'a Device, + pub(super) pipeline: Arc, +} + +impl Graphic<'_> { + /// Bind an index buffer to the current pass. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// # use std::sync::Arc; + /// # use ash::vk; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; + /// # use vk_graph::driver::image::{Image, ImageInfo}; + /// # use vk_graph::driver::shader::Shader; + /// # use vk_graph::Graph; + /// # fn main() -> Result<(), DriverError> { + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let my_frag_code = [0u8; 1]; + /// # let my_vert_code = [0u8; 1]; + /// # let vert = Shader::new_vertex(my_vert_code.as_slice()); + /// # let frag = Shader::new_fragment(my_frag_code.as_slice()); + /// # let info = GraphicPipelineInfo::default(); + /// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); + /// # let mut my_graph = Graph::default(); + /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); + /// # let swapchain_image = my_graph.bind_node(Image::create(&device, info)?); + /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::INDEX_BUFFER); + /// # let my_idx_buf = Buffer::create(&device, buf_info)?; + /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::VERTEX_BUFFER); + /// # let my_vtx_buf = Buffer::create(&device, buf_info)?; + /// # let my_idx_buf = my_graph.bind_node(my_idx_buf); + /// # let my_vtx_buf = my_graph.bind_node(my_vtx_buf); + /// my_graph.begin_cmd().with_name("my indexed geometry draw pass") + /// .bind_pipeline(&my_graphic_pipeline) + /// .store_color(0, swapchain_image) + /// .read_node(my_idx_buf) + /// .read_node(my_vtx_buf) + /// .record_pipeline(move |pipeline, bindings| { + /// pipeline.bind_index_buffer(my_idx_buf, vk::IndexType::UINT16) + /// .bind_vertex_buffer(my_vtx_buf) + /// .draw_indexed(42, 1, 0, 0, 0); + /// }); + /// # Ok(()) } + /// ``` + pub fn bind_index_buffer( + &self, + buffer: impl Into, + index_ty: vk::IndexType, + ) -> &Self { + self.bind_index_buffer_offset(buffer, index_ty, 0) + } + + /// Bind an index buffer to the current pass. + /// + /// Behaves similarly to `bind_index_buffer` except that `offset` is the starting offset in + /// bytes within `buffer` used in index buffer address calculations. + #[profiling::function] + pub fn bind_index_buffer_offset( + &self, + buffer: impl Into, + index_ty: vk::IndexType, + offset: vk::DeviceSize, + ) -> &Self { + let buffer = buffer.into(); + + unsafe { + self.device.cmd_bind_index_buffer( + self.cmd_buf, + self.bindings[buffer].handle, + offset, + index_ty, + ); + } + + self + } + + /// Bind a vertex buffer to the current pass. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// # use std::sync::Arc; + /// # use ash::vk; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; + /// # use vk_graph::driver::image::{Image, ImageInfo}; + /// # use vk_graph::driver::shader::Shader; + /// # use vk_graph::Graph; + /// # fn main() -> Result<(), DriverError> { + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::VERTEX_BUFFER); + /// # let my_vtx_buf = Buffer::create(&device, buf_info)?; + /// # let my_frag_code = [0u8; 1]; + /// # let my_vert_code = [0u8; 1]; + /// # let vert = Shader::new_vertex(my_vert_code.as_slice()); + /// # let frag = Shader::new_fragment(my_frag_code.as_slice()); + /// # let info = GraphicPipelineInfo::default(); + /// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); + /// # let mut my_graph = Graph::default(); + /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); + /// # let swapchain_image = my_graph.bind_node(Image::create(&device, info)?); + /// # let my_vtx_buf = my_graph.bind_node(my_vtx_buf); + /// my_graph.begin_cmd().with_name("my unindexed geometry draw pass") + /// .bind_pipeline(&my_graphic_pipeline) + /// .store_color(0, swapchain_image) + /// .read_node(my_vtx_buf) + /// .record_pipeline(move |pipeline, bindings| { + /// pipeline.bind_vertex_buffer(my_vtx_buf) + /// .draw(42, 1, 0, 0); + /// }); + /// # Ok(()) } + /// ``` + pub fn bind_vertex_buffer(&self, buffer: impl Into) -> &Self { + self.bind_vertex_buffer_offset(buffer, 0) + } + + /// Bind a vertex buffer to the current pass. + /// + /// Behaves similarly to `bind_vertex_buffer` except the vertex input binding is updated to + /// start at `offset` from the start of `buffer`. + #[profiling::function] + pub fn bind_vertex_buffer_offset( + &self, + buffer: impl Into, + offset: vk::DeviceSize, + ) -> &Self { + use std::slice::from_ref; + + let buffer = buffer.into(); + + unsafe { + self.device.cmd_bind_vertex_buffers( + self.cmd_buf, + 0, + from_ref(&self.bindings[buffer].handle), + from_ref(&offset), + ); + } + + self + } + + /// Binds multiple vertex buffers to the current pass, starting at the given `first_binding`. + /// + /// Each vertex input binding in `buffers` specifies an offset from the start of the + /// corresponding buffer. + /// + /// The vertex input attributes that use each of these bindings will use these updated addresses + /// in their address calculations for subsequent drawing commands. + #[profiling::function] + pub fn bind_vertex_buffers( + &self, + first_binding: u32, + buffer_offsets: impl IntoIterator, + ) -> &Self + where + B: Into, + { + thread_local! { + static BUFFERS_OFFSETS: RefCell<(Vec, Vec)> = Default::default(); + } + + BUFFERS_OFFSETS.with_borrow_mut(|(buffers, offsets)| { + buffers.clear(); + offsets.clear(); + + for (buffer, offset) in buffer_offsets { + let buffer = buffer.into(); + + buffers.push(self.bindings[buffer].handle); + offsets.push(offset); + } + + unsafe { + self.device.cmd_bind_vertex_buffers( + self.cmd_buf, + first_binding, + buffers.as_slice(), + offsets.as_slice(), + ); + } + }); + + self + } + + /// Draw unindexed primitives. + /// + /// When the command is executed, primitives are assembled using the current primitive topology + /// and `vertex_count` consecutive vertex indices with the first `vertex_index` value equal to + /// `first_vertex`. The primitives are drawn `instance_count` times with `instance_index` + /// starting with `first_instance` and increasing sequentially for each instance. + #[profiling::function] + pub fn draw( + &self, + vertex_count: u32, + instance_count: u32, + first_vertex: u32, + first_instance: u32, + ) -> &Self { + unsafe { + self.device.cmd_draw( + self.cmd_buf, + vertex_count, + instance_count, + first_vertex, + first_instance, + ); + } + + self + } + + /// Draw indexed primitives. + /// + /// When the command is executed, primitives are assembled using the current primitive topology + /// and `index_count` vertices whose indices are retrieved from the index buffer. The index + /// buffer is treated as an array of tightly packed unsigned integers of size defined by the + /// `index_ty` parameter with which the buffer was bound. + #[profiling::function] + pub fn draw_indexed( + &self, + index_count: u32, + instance_count: u32, + first_index: u32, + vertex_offset: i32, + first_instance: u32, + ) -> &Self { + unsafe { + self.device.cmd_draw_indexed( + self.cmd_buf, + index_count, + instance_count, + first_index, + vertex_offset, + first_instance, + ); + } + + self + } + + /// Draw primitives with indirect parameters and indexed vertices. + /// + /// `draw_indexed_indirect` behaves similarly to `draw_indexed` except that the parameters are + /// read by the device from `buffer` during execution. `draw_count` draws are executed by the + /// command, with parameters taken from `buffer` starting at `offset` and increasing by `stride` + /// bytes for each successive draw. The parameters of each draw are encoded in an array of + /// [`vk::DrawIndexedIndirectCommand`] structures. + /// + /// If `draw_count` is less than or equal to one, `stride` is ignored. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// # use std::sync::Arc; + /// # use std::mem::size_of; + /// # use ash::vk; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; + /// # use vk_graph::driver::image::{Image, ImageInfo}; + /// # use vk_graph::driver::shader::Shader; + /// # use vk_graph::Graph; + /// # fn main() -> Result<(), DriverError> { + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let my_frag_code = [0u8; 1]; + /// # let my_vert_code = [0u8; 1]; + /// # let vert = Shader::new_vertex(my_vert_code.as_slice()); + /// # let frag = Shader::new_fragment(my_frag_code.as_slice()); + /// # let info = GraphicPipelineInfo::default(); + /// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); + /// # let mut my_graph = Graph::default(); + /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::INDEX_BUFFER); + /// # let my_idx_buf = Buffer::create(&device, buf_info)?; + /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::VERTEX_BUFFER); + /// # let my_vtx_buf = Buffer::create(&device, buf_info)?; + /// # let my_idx_buf = my_graph.bind_node(my_idx_buf); + /// # let my_vtx_buf = my_graph.bind_node(my_vtx_buf); + /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); + /// # let swapchain_image = my_graph.bind_node(Image::create(&device, info)?); + /// const CMD_SIZE: usize = size_of::(); + /// + /// let cmd = vk::DrawIndexedIndirectCommand { + /// index_count: 3, + /// instance_count: 1, + /// first_index: 0, + /// vertex_offset: 0, + /// first_instance: 0, + /// }; + /// let cmd_data = unsafe { + /// std::slice::from_raw_parts(&cmd as *const _ as *const _, CMD_SIZE) + /// }; + /// + /// let buf_flags = vk::BufferUsageFlags::STORAGE_BUFFER; + /// let buf = Buffer::create_from_slice(&device, buf_flags, cmd_data)?; + /// let buf_node = my_graph.bind_node(buf); + /// + /// my_graph.begin_cmd().with_name("draw a single triangle") + /// .bind_pipeline(&my_graphic_pipeline) + /// .store_color(0, swapchain_image) + /// .read_node(my_idx_buf) + /// .read_node(my_vtx_buf) + /// .read_node(buf_node) + /// .record_pipeline(move |pipeline, bindings| { + /// pipeline.bind_index_buffer(my_idx_buf, vk::IndexType::UINT16) + /// .bind_vertex_buffer(my_vtx_buf) + /// .draw_indexed_indirect(buf_node, 0, 1, 0); + /// }); + /// # Ok(()) } + /// ``` + #[profiling::function] + pub fn draw_indexed_indirect( + &self, + buffer: impl Into, + offset: vk::DeviceSize, + draw_count: u32, + stride: u32, + ) -> &Self { + let buffer = buffer.into(); + + unsafe { + self.device.cmd_draw_indexed_indirect( + self.cmd_buf, + self.bindings[buffer].handle, + offset, + draw_count, + stride, + ); + } + + self + } + + /// Draw primitives with indirect parameters, indexed vertices, and draw count. + /// + /// `draw_indexed_indirect_count` behaves similarly to `draw_indexed_indirect` except that the + /// draw count is read by the device from `buffer` during execution. The command will read an + /// unsigned 32-bit integer from `count_buf` located at `count_buf_offset` and use this as the + /// draw count. + /// + /// `max_draw_count` specifies the maximum number of draws that will be executed. The actual + /// number of executed draw calls is the minimum of the count specified in `count_buf` and + /// `max_draw_count`. + /// + /// `stride` is the byte stride between successive sets of draw parameters. + #[profiling::function] + pub fn draw_indexed_indirect_count( + &self, + buffer: impl Into, + offset: vk::DeviceSize, + count_buf: impl Into, + count_buf_offset: vk::DeviceSize, + max_draw_count: u32, + stride: u32, + ) -> &Self { + let buffer = buffer.into(); + let count_buf = count_buf.into(); + + unsafe { + self.device.cmd_draw_indexed_indirect_count( + self.cmd_buf, + self.bindings[buffer].handle, + offset, + self.bindings[count_buf].handle, + count_buf_offset, + max_draw_count, + stride, + ); + } + + self + } + + /// Draw primitives with indirect parameters and unindexed vertices. + /// + /// Behaves otherwise similar to [`Draw::draw_indexed_indirect`]. + #[profiling::function] + pub fn draw_indirect( + &self, + buffer: impl Into, + offset: vk::DeviceSize, + draw_count: u32, + stride: u32, + ) -> &Self { + let buffer = buffer.into(); + + unsafe { + self.device.cmd_draw_indirect( + self.cmd_buf, + self.bindings[buffer].handle, + offset, + draw_count, + stride, + ); + } + + self + } + + /// Draw primitives with indirect parameters, unindexed vertices, and draw count. + /// + /// Behaves otherwise similar to [`Draw::draw_indexed_indirect_count`]. + #[profiling::function] + pub fn draw_indirect_count( + &self, + buffer: impl Into, + offset: vk::DeviceSize, + count_buf: impl Into, + count_buf_offset: vk::DeviceSize, + max_draw_count: u32, + stride: u32, + ) -> &Self { + let buffer = buffer.into(); + let count_buf = count_buf.into(); + + unsafe { + self.device.cmd_draw_indirect_count( + self.cmd_buf, + self.bindings[buffer].handle, + offset, + self.bindings[count_buf].handle, + count_buf_offset, + max_draw_count, + stride, + ); + } + + self + } + + /// Updates push constants. + /// + /// Push constants represent a high speed path to modify constant data in pipelines that is + /// expected to outperform memory-backed resource updates. + /// + /// Push constant values can be updated incrementally, causing shader stages to read the new + /// data for push constants modified by this command, while still reading the previous data for + /// push constants not modified by this command. + /// + /// # Device limitations + /// + /// See + /// [`device.physical_device.props.limits.max_push_constants_size`](vk::PhysicalDeviceLimits) + /// for the limits of the current device. You may also check [gpuinfo.org] for a listing of + /// reported limits on other devices. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # vk_shader_macros::glsl!(r#" + /// #version 450 + /// #pragma shader_stage(compute) + /// + /// layout(push_constant) uniform PushConstants { + /// layout(offset = 0) uint the_answer; + /// } push_constants; + /// + /// void main() { + /// // TODO: Add code! + /// } + /// # "#); + /// ``` + /// + /// ```no_run + /// # use std::sync::Arc; + /// # use ash::vk; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; + /// # use vk_graph::driver::image::{Image, ImageInfo}; + /// # use vk_graph::Graph; + /// # use vk_graph::driver::shader::Shader; + /// # fn main() -> Result<(), DriverError> { + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let my_frag_code = [0u8; 1]; + /// # let my_vert_code = [0u8; 1]; + /// # let vert = Shader::new_vertex(my_vert_code.as_slice()); + /// # let frag = Shader::new_fragment(my_frag_code.as_slice()); + /// # let info = GraphicPipelineInfo::default(); + /// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); + /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); + /// # let swapchain_image = Image::create(&device, info)?; + /// # let mut my_graph = Graph::default(); + /// # let swapchain_image = my_graph.bind_node(swapchain_image); + /// my_graph.begin_cmd().with_name("draw a quad") + /// .bind_pipeline(&my_graphic_pipeline) + /// .store_color(0, swapchain_image) + /// .record_pipeline(move |pipeline, bindings| { + /// pipeline.push_constants(&[42]) + /// .draw(6, 1, 0, 0); + /// }); + /// # Ok(()) } + /// ``` + /// + /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all + pub fn push_constants(&self, data: &[u8]) -> &Self { + self.push_constants_offset(0, data) + } + + /// Updates push constants starting at the given `offset`. + /// + /// Behaves similary to [`Draw::push_constants`] except that `offset` describes the position at + /// which `data` updates the push constants of the currently bound pipeline. This may be used to + /// update a subset or single field of previously set push constant data. + /// + /// # Device limitations + /// + /// See + /// [`device.physical_device.props.limits.max_push_constants_size`](vk::PhysicalDeviceLimits) + /// for the limits of the current device. You may also check [gpuinfo.org] for a listing of + /// reported limits on other devices. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # vk_shader_macros::glsl!(r#" + /// #version 450 + /// #pragma shader_stage(compute) + /// + /// layout(push_constant) uniform PushConstants { + /// layout(offset = 0) uint some_val1; + /// layout(offset = 4) uint some_val2; + /// } push_constants; + /// + /// void main() { + /// // TODO: Add code! + /// } + /// # "#); + /// ``` + /// + /// ```no_run + /// # use std::sync::Arc; + /// # use ash::vk; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; + /// # use vk_graph::driver::image::{Image, ImageInfo}; + /// # use vk_graph::Graph; + /// # use vk_graph::driver::shader::Shader; + /// # fn main() -> Result<(), DriverError> { + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let my_frag_code = [0u8; 1]; + /// # let my_vert_code = [0u8; 1]; + /// # let vert = Shader::new_vertex(my_vert_code.as_slice()); + /// # let frag = Shader::new_fragment(my_frag_code.as_slice()); + /// # let info = GraphicPipelineInfo::default(); + /// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); + /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); + /// # let swapchain_image = Image::create(&device, info)?; + /// # let mut my_graph = Graph::default(); + /// # let swapchain_image = my_graph.bind_node(swapchain_image); + /// my_graph.begin_cmd().with_name("draw a quad") + /// .bind_pipeline(&my_graphic_pipeline) + /// .store_color(0, swapchain_image) + /// .record_pipeline(move |pipeline, bindings| { + /// pipeline.push_constants(&[0x00, 0x00]) + /// .draw(6, 1, 0, 0) + /// .push_constants_offset(4, &[0xff]) + /// .draw(6, 1, 0, 0); + /// }); + /// # Ok(()) } + /// ``` + /// + /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all + #[profiling::function] + pub fn push_constants_offset(&self, offset: u32, data: &[u8]) -> &Self { + for push_const in self.pipeline.push_constants.iter() { + // Determine the range of the overall pipline push constants which overlap with `data` + let push_const_end = push_const.offset + push_const.size; + let data_end = offset + data.len() as u32; + let end = data_end.min(push_const_end); + let start = offset.max(push_const.offset); + + if end > start { + trace!( + " push constants {:?} {}..{}", + push_const.stage_flags, start, end + ); + + unsafe { + self.device.cmd_push_constants( + self.cmd_buf, + self.pipeline.layout, + push_const.stage_flags, + start, + &data[(start - offset) as usize..(end - offset) as usize], + ); + } + } + } + + self + } + + /// Set scissor rectangle dynamically for a pass. + #[profiling::function] + pub fn set_scissor(&self, x: i32, y: i32, width: u32, height: u32) -> &Self { + unsafe { + self.device.cmd_set_scissor( + self.cmd_buf, + 0, + &[vk::Rect2D { + extent: vk::Extent2D { width, height }, + offset: vk::Offset2D { x, y }, + }], + ); + } + + self + } + + /// Set scissor rectangles dynamically for a pass. + #[profiling::function] + pub fn set_scissors( + &self, + first_scissor: u32, + scissors: impl IntoIterator, + ) -> &Self + where + S: Into, + { + thread_local! { + static SCISSORS: RefCell> = Default::default(); + } + + SCISSORS.with_borrow_mut(|scissors_vec| { + scissors_vec.clear(); + + for scissor in scissors { + scissors_vec.push(scissor.into()); + } + + unsafe { + self.device + .cmd_set_scissor(self.cmd_buf, first_scissor, scissors_vec.as_slice()); + } + }); + + self + } + + /// Set the viewport dynamically for a pass. + #[profiling::function] + pub fn set_viewport( + &self, + x: f32, + y: f32, + width: f32, + height: f32, + depth: Range, + ) -> &Self { + unsafe { + self.device.cmd_set_viewport( + self.cmd_buf, + 0, + &[vk::Viewport { + x, + y, + width, + height, + min_depth: depth.start, + max_depth: depth.end, + }], + ); + } + + self + } + + /// Set the viewports dynamically for a pass. + #[profiling::function] + pub fn set_viewports( + &self, + first_viewport: u32, + viewports: impl IntoIterator, + ) -> &Self + where + V: Into, + { + thread_local! { + static VIEWPORTS: RefCell> = Default::default(); + } + + VIEWPORTS.with_borrow_mut(|viewports_vec| { + viewports_vec.clear(); + + for viewport in viewports { + viewports_vec.push(viewport.into()); + } + + unsafe { + self.device.cmd_set_viewport( + self.cmd_buf, + first_viewport, + viewports_vec.as_slice(), + ); + } + }); + + self + } +} + +// NOTE: local implementation of type from super module +impl PipelineCommandRef<'_, GraphicPipeline> { + /// Specifies `VK_ATTACHMENT_LOAD_OP_DONT_CARE` for the render pass attachment, and loads an + /// image into the framebuffer. + pub fn attach_color( + self, + attachment_idx: AttachmentIndex, + image: impl Into, + ) -> Self { + let image: AnyImageNode = image.into(); + let image_info = image.info(&self.cmd.graph.bindings); + let image_view_info: ImageViewInfo = image_info.into(); + + self.attach_color_as(attachment_idx, image, image_view_info) + } + + /// Specifies `VK_ATTACHMENT_LOAD_OP_DONT_CARE` for the render pass attachment, and loads an + /// image into the framebuffer. + pub fn attach_color_as( + mut self, + attachment_idx: AttachmentIndex, + image: impl Into, + image_view_info: impl Into, + ) -> Self { + let image = image.into(); + let image_view_info = image_view_info.into(); + let node_idx = image.index(); + let (_, sample_count) = self.image_info(node_idx); + + debug_assert!( + !self + .cmd + .as_ref() + .execs + .last() + .unwrap() + .color_clears + .contains_key(&attachment_idx), + "color attachment {attachment_idx} already attached via clear" + ); + debug_assert!( + !self + .cmd + .as_ref() + .execs + .last() + .unwrap() + .color_loads + .contains_key(&attachment_idx), + "color attachment {attachment_idx} already attached via load" + ); + + self.cmd + .as_mut() + .execs + .last_mut() + .unwrap() + .color_attachments + .insert( + attachment_idx, + Attachment::new(image_view_info, sample_count, node_idx), + ); + + debug_assert!( + Attachment::are_compatible( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_resolves + .get(&attachment_idx) + .map(|(attachment, _)| *attachment), + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_attachments + .get(&attachment_idx) + .copied() + ), + "color attachment {attachment_idx} incompatible with existing resolve" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_stores + .get(&attachment_idx) + .copied(), + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_attachments + .get(&attachment_idx) + .copied() + ), + "color attachment {attachment_idx} incompatible with existing store" + ); + + self.cmd.push_node_access( + image, + AccessType::ColorAttachmentWrite, + Subresource::Image(image_view_info.into()), + ); + + self + } + + /// Specifies `VK_ATTACHMENT_LOAD_OP_DONT_CARE` for the render pass attachment, and loads an + /// image into the framebuffer. + pub fn attach_depth_stencil(self, image: impl Into) -> Self { + let image: AnyImageNode = image.into(); + let image_info = image.info(&self.cmd.graph.bindings); + let image_view_info: ImageViewInfo = image_info.into(); + + self.attach_depth_stencil_as(image, image_view_info) + } + + /// Specifies `VK_ATTACHMENT_LOAD_OP_DONT_CARE` for the render pass attachment, and loads an + /// image into the framebuffer. + pub fn attach_depth_stencil_as( + mut self, + image: impl Into, + image_view_info: impl Into, + ) -> Self { + let image = image.into(); + let image_view_info = image_view_info.into(); + let node_idx = image.index(); + let (_, sample_count) = self.image_info(node_idx); + + debug_assert!( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .depth_stencil_clear + .is_none(), + "depth/stencil attachment already attached via clear" + ); + debug_assert!( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .depth_stencil_load + .is_none(), + "depth/stencil attachment already attached via load" + ); + + self.cmd + .as_mut() + .execs + .last_mut() + .unwrap() + .depth_stencil_attachment = + Some(Attachment::new(image_view_info, sample_count, node_idx)); + + debug_assert!( + Attachment::are_compatible( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .depth_stencil_resolve + .map(|(attachment, ..)| attachment), + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .depth_stencil_attachment + ), + "depth/stencil attachment incompatible with existing resolve" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd.as_ref().execs.last().unwrap().depth_stencil_store, + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .depth_stencil_attachment + ), + "depth/stencil attachment incompatible with existing store" + ); + + self.cmd.push_node_access( + image, + if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL) + { + AccessType::DepthStencilAttachmentWrite + } else if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH) + { + AccessType::DepthAttachmentWriteStencilReadOnly + } else { + AccessType::StencilAttachmentWriteDepthReadOnly + }, + Subresource::Image(image_view_info.into()), + ); + + self + } + + /// Clears the render pass attachment of any existing data. + pub fn clear_color( + self, + attachment_idx: AttachmentIndex, + image: impl Into, + ) -> Self { + self.clear_color_value(attachment_idx, image, [0.0, 0.0, 0.0, 0.0]) + } + + /// Clears the render pass attachment of any existing data. + pub fn clear_color_value( + self, + attachment_idx: AttachmentIndex, + image: impl Into, + color: impl Into, + ) -> Self { + let image: AnyImageNode = image.into(); + let image_info = image.info(&self.cmd.graph.bindings); + let image_view_info: ImageViewInfo = image_info.into(); + + self.clear_color_value_as(attachment_idx, image, color, image_view_info) + } + + /// Clears the render pass attachment of any existing data. + pub fn clear_color_value_as( + mut self, + attachment_idx: AttachmentIndex, + image: impl Into, + color: impl Into, + image_view_info: impl Into, + ) -> Self { + let image = image.into(); + let image_view_info = image_view_info.into(); + let node_idx = image.index(); + let (_, sample_count) = self.image_info(node_idx); + + let color = color.into(); + + debug_assert!( + !self + .cmd + .as_ref() + .execs + .last() + .unwrap() + .color_attachments + .contains_key(&attachment_idx), + "color attachment {attachment_idx} already attached" + ); + debug_assert!( + !self + .cmd + .as_ref() + .execs + .last() + .unwrap() + .color_loads + .contains_key(&attachment_idx), + "color attachment {attachment_idx} already attached via load" + ); + + self.cmd + .as_mut() + .execs + .last_mut() + .unwrap() + .color_clears + .insert( + attachment_idx, + ( + Attachment::new(image_view_info, sample_count, node_idx), + color, + ), + ); + + debug_assert!( + Attachment::are_compatible( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_resolves + .get(&attachment_idx) + .map(|(attachment, _)| *attachment), + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_clears + .get(&attachment_idx) + .map(|(attachment, _)| *attachment) + ), + "color attachment {attachment_idx} clear incompatible with existing resolve" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_stores + .get(&attachment_idx) + .copied(), + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_clears + .get(&attachment_idx) + .map(|(attachment, _)| *attachment) + ), + "color attachment {attachment_idx} clear incompatible with existing store" + ); + + let mut image_access = AccessType::ColorAttachmentWrite; + let image_range = image_view_info.into(); + + // Upgrade existing read access to read-write + if let Some(accesses) = self + .cmd + .as_mut() + .execs + .last_mut() + .unwrap() + .accesses + .get_mut(&node_idx) + { + for SubresourceAccess { + access, + subresource, + } in accesses + { + let access_image_range = *subresource.as_image().unwrap(); + if !image_subresource_range_intersects(access_image_range, image_range) { + continue; + } + + image_access = match *access { + AccessType::ColorAttachmentRead | AccessType::ColorAttachmentReadWrite => { + AccessType::ColorAttachmentReadWrite + } + AccessType::ColorAttachmentWrite => AccessType::ColorAttachmentWrite, + _ => continue, + }; + + *access = image_access; + + // If the clear access is a subset of the existing access range there is no need + // to push a new access + if image_subresource_range_contains(access_image_range, image_range) { + return self; + } + } + } + + self.cmd + .push_node_access(image, image_access, Subresource::Image(image_range)); + + self + } + + /// Clears the render pass attachment of any existing data. + pub fn clear_depth_stencil(self, image: impl Into) -> Self { + self.clear_depth_stencil_value(image, 1.0, 0) + } + + /// Clears the render pass attachment of any existing data. + pub fn clear_depth_stencil_value( + self, + image: impl Into, + depth: f32, + stencil: u32, + ) -> Self { + let image: AnyImageNode = image.into(); + let image_info = image.info(&self.cmd.graph.bindings); + let image_view_info: ImageViewInfo = image_info.into(); + + self.clear_depth_stencil_value_as(image, depth, stencil, image_view_info) + } + + /// Clears the render pass attachment of any existing data. + pub fn clear_depth_stencil_value_as( + mut self, + image: impl Into, + depth: f32, + stencil: u32, + image_view_info: impl Into, + ) -> Self { + let image = image.into(); + let image_view_info = image_view_info.into(); + let node_idx = image.index(); + let (_, sample_count) = self.image_info(node_idx); + + debug_assert!( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .depth_stencil_attachment + .is_none(), + "depth/stencil attachment already attached" + ); + debug_assert!( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .depth_stencil_load + .is_none(), + "depth/stencil attachment already attached via load" + ); + + self.cmd + .as_mut() + .execs + .last_mut() + .unwrap() + .depth_stencil_clear = Some(( + Attachment::new(image_view_info, sample_count, node_idx), + vk::ClearDepthStencilValue { depth, stencil }, + )); + + debug_assert!( + Attachment::are_compatible( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .depth_stencil_resolve + .map(|(attachment, ..)| attachment), + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .depth_stencil_clear + .map(|(attachment, _)| attachment) + ), + "depth/stencil attachment clear incompatible with existing resolve" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd.as_ref().execs.last().unwrap().depth_stencil_store, + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .depth_stencil_clear + .map(|(attachment, _)| attachment) + ), + "depth/stencil attachment clear incompatible with existing store" + ); + + let mut image_access = if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL) + { + AccessType::DepthStencilAttachmentWrite + } else if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH) + { + AccessType::DepthAttachmentWriteStencilReadOnly + } else { + debug_assert!( + image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::STENCIL) + ); + + AccessType::StencilAttachmentWriteDepthReadOnly + }; + let image_range = image_view_info.into(); + + // Upgrade existing read access to read-write + if let Some(accesses) = self + .cmd + .as_mut() + .execs + .last_mut() + .unwrap() + .accesses + .get_mut(&node_idx) + { + for SubresourceAccess { + access, + subresource, + } in accesses + { + let access_image_range = *subresource.as_image().unwrap(); + if !image_subresource_range_intersects(access_image_range, image_range) { + continue; + } + + image_access = match *access { + AccessType::DepthAttachmentWriteStencilReadOnly => { + if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::STENCIL) + { + AccessType::DepthStencilAttachmentReadWrite + } else { + AccessType::DepthAttachmentWriteStencilReadOnly + } + } + AccessType::DepthStencilAttachmentRead => { + if !image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH) + { + AccessType::StencilAttachmentWriteDepthReadOnly + } else { + AccessType::DepthAttachmentWriteStencilReadOnly + } + } + AccessType::DepthStencilAttachmentWrite => { + AccessType::DepthStencilAttachmentWrite + } + AccessType::StencilAttachmentWriteDepthReadOnly => { + if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH) + { + AccessType::DepthStencilAttachmentReadWrite + } else { + AccessType::StencilAttachmentWriteDepthReadOnly + } + } + _ => continue, + }; + + *access = image_access; + + // If the clear access is a subset of the existing access range there is no need + // to push a new access + if image_subresource_range_contains(access_image_range, image_range) { + return self; + } + } + } + + self.cmd + .push_node_access(image, image_access, Subresource::Image(image_range)); + + self + } + + fn image_info(&self, node_idx: NodeIndex) -> (vk::Format, SampleCount) { + let image_info = self.cmd.graph.bindings[node_idx] + .as_driver_image() + .unwrap() + .info; + + (image_info.fmt, image_info.sample_count) + } + + /// Specifies `VK_ATTACHMENT_LOAD_OP_LOAD` for the render pass attachment, and loads an image + /// into the framebuffer. + pub fn load_color( + self, + attachment_idx: AttachmentIndex, + image: impl Into, + ) -> Self { + let image: AnyImageNode = image.into(); + let image_info = self.node_info(image); + + // Use the plain node information as the whole view of the node + let image_view_info = image_info; + + self.load_color_as(attachment_idx, image, image_view_info) + } + + /// Specifies `VK_ATTACHMENT_LOAD_OP_LOAD` for the render pass attachment, and loads an image + /// into the framebuffer. + pub fn load_color_as( + mut self, + attachment_idx: AttachmentIndex, + image: impl Into, + image_view_info: impl Into, + ) -> Self { + let image = image.into(); + let image_view_info = image_view_info.into(); + let node_idx = image.index(); + let (_, sample_count) = self.image_info(node_idx); + + debug_assert!( + !self + .cmd + .as_ref() + .execs + .last() + .unwrap() + .color_attachments + .contains_key(&attachment_idx), + "color attachment {attachment_idx} already attached" + ); + debug_assert!( + !self + .cmd + .as_ref() + .execs + .last() + .unwrap() + .color_clears + .contains_key(&attachment_idx), + "color attachment {attachment_idx} already attached via clear" + ); + + self.cmd + .as_mut() + .execs + .last_mut() + .unwrap() + .color_loads + .insert( + attachment_idx, + Attachment::new(image_view_info, sample_count, node_idx), + ); + + debug_assert!( + Attachment::are_compatible( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_resolves + .get(&attachment_idx) + .map(|(attachment, _)| *attachment), + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_loads + .get(&attachment_idx) + .copied() + ), + "color attachment {attachment_idx} load incompatible with existing resolve" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_stores + .get(&attachment_idx) + .copied(), + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_loads + .get(&attachment_idx) + .copied() + ), + "color attachment {attachment_idx} load incompatible with existing store" + ); + + let mut image_access = AccessType::ColorAttachmentRead; + let image_range = image_view_info.into(); + + // Upgrade existing write access to read-write + if let Some(accesses) = self + .cmd + .as_mut() + .execs + .last_mut() + .unwrap() + .accesses + .get_mut(&node_idx) + { + for SubresourceAccess { + access, + subresource, + } in accesses + { + let access_image_range = *subresource.as_image().unwrap(); + if !image_subresource_range_intersects(access_image_range, image_range) { + continue; + } + + image_access = match *access { + AccessType::ColorAttachmentRead => AccessType::ColorAttachmentRead, + AccessType::ColorAttachmentReadWrite | AccessType::ColorAttachmentWrite => { + AccessType::ColorAttachmentReadWrite + } + _ => continue, + }; + + *access = image_access; + + // If the load access is a subset of the existing access range there is no need + // to push a new access + if image_subresource_range_contains(access_image_range, image_range) { + return self; + } + } + } + + self.cmd + .push_node_access(image, image_access, Subresource::Image(image_range)); + + self + } + + /// Specifies `VK_ATTACHMENT_LOAD_OP_LOAD` for the render pass attachment, and loads an image + /// into the framebuffer. + pub fn load_depth_stencil(self, image: impl Into) -> Self { + let image: AnyImageNode = image.into(); + let image_info = self.node_info(image); + let image_view_info: ImageViewInfo = image_info.into(); + + self.load_depth_stencil_as(image, image_view_info) + } + + /// Specifies `VK_ATTACHMENT_LOAD_OP_LOAD` for the render pass attachment, and loads an image + /// into the framebuffer. + pub fn load_depth_stencil_as( + mut self, + image: impl Into, + image_view_info: impl Into, + ) -> Self { + let image = image.into(); + let image_view_info = image_view_info.into(); + let node_idx = image.index(); + let (_, sample_count) = self.image_info(node_idx); + + debug_assert!( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .depth_stencil_attachment + .is_none(), + "depth/stencil attachment already attached" + ); + debug_assert!( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .depth_stencil_clear + .is_none(), + "depth/stencil attachment already attached via clear" + ); + + self.cmd + .as_mut() + .execs + .last_mut() + .unwrap() + .depth_stencil_load = Some(Attachment::new(image_view_info, sample_count, node_idx)); + + debug_assert!( + Attachment::are_compatible( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .depth_stencil_resolve + .map(|(attachment, ..)| attachment), + self.cmd.as_ref().execs.last().unwrap().depth_stencil_load + ), + "depth/stencil attachment load incompatible with existing resolve" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd.as_ref().execs.last().unwrap().depth_stencil_store, + self.cmd.as_ref().execs.last().unwrap().depth_stencil_load + ), + "depth/stencil attachment load incompatible with existing store" + ); + + let mut image_access = AccessType::DepthStencilAttachmentRead; + let image_range = image_view_info.into(); + + // Upgrade existing write access to read-write + if let Some(accesses) = self + .cmd + .as_mut() + .execs + .last_mut() + .unwrap() + .accesses + .get_mut(&node_idx) + { + for SubresourceAccess { + access, + subresource, + } in accesses + { + let access_image_range = *subresource.as_image().unwrap(); + if !image_subresource_range_intersects(access_image_range, image_range) { + continue; + } + + image_access = match *access { + AccessType::DepthAttachmentWriteStencilReadOnly => { + AccessType::DepthAttachmentWriteStencilReadOnly + } + AccessType::DepthStencilAttachmentRead => { + AccessType::DepthStencilAttachmentRead + } + AccessType::DepthStencilAttachmentWrite => { + AccessType::DepthStencilAttachmentReadWrite + } + AccessType::StencilAttachmentWriteDepthReadOnly => { + AccessType::StencilAttachmentWriteDepthReadOnly + } + _ => continue, + }; + + *access = image_access; + + // If the load access is a subset of the existing access range there is no need + // to push a new access + if image_subresource_range_contains(access_image_range, image_range) { + return self; + } + } + } + + self.cmd + .push_node_access(image, image_access, Subresource::Image(image_range)); + + self + } + + /// Begin recording a graphics command buffer. + pub fn record_pipeline( + mut self, + func: impl FnOnce(Graphic<'_>, Bindings<'_>) + Send + 'static, + ) -> Self { + let pipeline = Arc::clone( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .pipeline + .as_ref() + .unwrap() + .unwrap_graphic(), + ); + + self.cmd.push_execute(move |device, cmd_buf, bindings| { + func( + Graphic { + bindings, + cmd_buf, + device, + pipeline, + }, + bindings, + ); + }); + + self + } + + /// Resolves a multisample framebuffer to a non-multisample image for the render pass + /// attachment. + pub fn resolve_color( + self, + src_attachment_idx: AttachmentIndex, + dst_attachment_idx: AttachmentIndex, + image: impl Into, + ) -> Self { + let image: AnyImageNode = image.into(); + let image_info = self.node_info(image); + + // Use the plain node information as the whole view of the node + let image_view_info = image_info; + + self.resolve_color_as( + src_attachment_idx, + dst_attachment_idx, + image, + image_view_info, + ) + } + + /// Resolves a multisample framebuffer to a non-multisample image for the render pass + /// attachment. + pub fn resolve_color_as( + mut self, + src_attachment_idx: AttachmentIndex, + dst_attachment_idx: AttachmentIndex, + image: impl Into, + image_view_info: impl Into, + ) -> Self { + let image = image.into(); + let image_view_info = image_view_info.into(); + let node_idx = image.index(); + let (_, sample_count) = self.image_info(node_idx); + + self.cmd + .as_mut() + .execs + .last_mut() + .unwrap() + .color_resolves + .insert( + dst_attachment_idx, + ( + Attachment::new(image_view_info, sample_count, node_idx), + src_attachment_idx, + ), + ); + + debug_assert!( + Attachment::are_compatible( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_attachments + .get(&dst_attachment_idx) + .copied(), + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_resolves + .get(&dst_attachment_idx) + .map(|(attachment, _)| *attachment) + ), + "color attachment {dst_attachment_idx} resolve incompatible with existing attachment" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_clears + .get(&dst_attachment_idx) + .map(|(attachment, _)| *attachment), + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_resolves + .get(&dst_attachment_idx) + .map(|(attachment, _)| *attachment) + ), + "color attachment {dst_attachment_idx} resolve incompatible with existing clear" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_loads + .get(&dst_attachment_idx) + .copied(), + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_resolves + .get(&dst_attachment_idx) + .map(|(attachment, _)| *attachment) + ), + "color attachment {dst_attachment_idx} resolve incompatible with existing load" + ); + + let mut image_access = AccessType::ColorAttachmentWrite; + let image_range = image_view_info.into(); + + // Upgrade existing read access to read-write + if let Some(accesses) = self + .cmd + .as_mut() + .execs + .last_mut() + .unwrap() + .accesses + .get_mut(&node_idx) + { + for SubresourceAccess { + access, + subresource, + } in accesses + { + let access_image_range = *subresource.as_image().unwrap(); + if !image_subresource_range_intersects(access_image_range, image_range) { + continue; + } + + image_access = match *access { + AccessType::ColorAttachmentRead | AccessType::ColorAttachmentReadWrite => { + AccessType::ColorAttachmentReadWrite + } + AccessType::ColorAttachmentWrite => AccessType::ColorAttachmentWrite, + _ => continue, + }; + + *access = image_access; + + // If the resolve access is a subset of the existing access range there is no need + // to push a new access + if image_subresource_range_contains(access_image_range, image_range) { + return self; + } + } + } + + self.cmd + .push_node_access(image, image_access, Subresource::Image(image_range)); + + self + } + + /// Resolves a multisample framebuffer to a non-multisample image for the render pass + /// attachment. + pub fn resolve_depth_stencil( + self, + dst_attachment_idx: AttachmentIndex, + image: impl Into, + depth_mode: Option, + stencil_mode: Option, + ) -> Self { + let image: AnyImageNode = image.into(); + let image_info = self.node_info(image); + + // Use the plain node information as the whole view of the node + let image_view_info = image_info; + + self.resolve_depth_stencil_as( + dst_attachment_idx, + image, + image_view_info, + depth_mode, + stencil_mode, + ) + } + + /// Resolves a multisample framebuffer to a non-multisample image for the render pass + /// attachment. + pub fn resolve_depth_stencil_as( + mut self, + dst_attachment_idx: AttachmentIndex, + image: impl Into, + image_view_info: impl Into, + depth_mode: Option, + stencil_mode: Option, + ) -> Self { + let image = image.into(); + let image_view_info = image_view_info.into(); + let node_idx = image.index(); + let (_, sample_count) = self.image_info(node_idx); + + self.cmd + .as_mut() + .execs + .last_mut() + .unwrap() + .depth_stencil_resolve = Some(( + Attachment::new(image_view_info, sample_count, node_idx), + dst_attachment_idx, + depth_mode, + stencil_mode, + )); + + let mut image_access = if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL) + { + AccessType::DepthStencilAttachmentWrite + } else if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH) + { + AccessType::DepthAttachmentWriteStencilReadOnly + } else { + debug_assert!( + image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::STENCIL) + ); + + AccessType::StencilAttachmentWriteDepthReadOnly + }; + let image_range = image_view_info.into(); + + // Upgrade existing read access to read-write + if let Some(accesses) = self + .cmd + .as_mut() + .execs + .last_mut() + .unwrap() + .accesses + .get_mut(&node_idx) + { + for SubresourceAccess { + access, + subresource, + } in accesses + { + let access_image_range = *subresource.as_image().unwrap(); + if !image_subresource_range_intersects(access_image_range, image_range) { + continue; + } + + image_access = match *access { + AccessType::DepthAttachmentWriteStencilReadOnly => { + if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::STENCIL) + { + AccessType::DepthStencilAttachmentReadWrite + } else { + AccessType::DepthAttachmentWriteStencilReadOnly + } + } + AccessType::DepthStencilAttachmentRead => { + if !image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH) + { + AccessType::StencilAttachmentWriteDepthReadOnly + } else { + AccessType::DepthStencilAttachmentReadWrite + } + } + AccessType::DepthStencilAttachmentWrite => { + AccessType::DepthStencilAttachmentWrite + } + AccessType::StencilAttachmentWriteDepthReadOnly => { + if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH) + { + AccessType::DepthStencilAttachmentReadWrite + } else { + AccessType::StencilAttachmentWriteDepthReadOnly + } + } + _ => continue, + }; + + *access = image_access; + + // If the resolve access is a subset of the existing access range there is no need + // to push a new access + if image_subresource_range_contains(access_image_range, image_range) { + return self; + } + } + } + + self.cmd + .push_node_access(image, image_access, Subresource::Image(image_range)); + + self + } + + /// Sets a particular depth/stencil mode. + pub fn set_depth_stencil(mut self, depth_stencil: DepthStencilMode) -> Self { + let pass = self.cmd.as_mut(); + let exec = pass.execs.last_mut().unwrap(); + + assert!(exec.depth_stencil.is_none()); + + exec.depth_stencil = Some(depth_stencil); + + self + } + + /// Sets multiview view and correlation masks. + /// + /// See [`VkRenderPassMultiviewCreateInfo`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkRenderPassMultiviewCreateInfo.html#_description). + pub fn set_multiview(mut self, view_mask: u32, correlated_view_mask: u32) -> Self { + let pass = self.cmd.as_mut(); + let exec = pass.execs.last_mut().unwrap(); + + exec.correlated_view_mask = correlated_view_mask; + exec.view_mask = view_mask; + + self + } + + /// Sets the [`renderArea`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkRenderPassBeginInfo.html#_c_specification) + /// field when beginning a render pass. + /// + /// NOTE: Setting this value will cause the viewport and scissor to be unset, which is not the default + /// behavior. When this value is set you should call `set_viewport` and `set_scissor` on the subpass. + /// + /// If not set, this value defaults to the first loaded, resolved, or stored attachment dimensions and + /// sets the viewport and scissor to the same values, with a `0..1` depth if not specified by + /// `set_depth_stencil`. + pub fn set_render_area(mut self, x: i32, y: i32, width: u32, height: u32) -> Self { + self.cmd.as_mut().execs.last_mut().unwrap().render_area = Some(Area { + height, + width, + x, + y, + }); + + self + } + + /// Specifies `VK_ATTACHMENT_STORE_OP_STORE` for the render pass attachment, and stores the + /// rendered pixels into an image. + pub fn store_color( + self, + attachment_idx: AttachmentIndex, + image: impl Into, + ) -> Self { + let image: AnyImageNode = image.into(); + let image_info = self.node_info(image); + + // Use the plain node information as the whole view of the node + let image_view_info = image_info; + + self.store_color_as(attachment_idx, image, image_view_info) + } + + /// Specifies `VK_ATTACHMENT_STORE_OP_STORE` for the render pass attachment, and stores the + /// rendered pixels into an image. + pub fn store_color_as( + mut self, + attachment_idx: AttachmentIndex, + image: impl Into, + image_view_info: impl Into, + ) -> Self { + let image = image.into(); + let image_view_info = image_view_info.into(); + let node_idx = image.index(); + let (_, sample_count) = self.image_info(node_idx); + + self.cmd + .as_mut() + .execs + .last_mut() + .unwrap() + .color_stores + .insert( + attachment_idx, + Attachment::new(image_view_info, sample_count, node_idx), + ); + + debug_assert!( + Attachment::are_compatible( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_attachments + .get(&attachment_idx) + .copied(), + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_stores + .get(&attachment_idx) + .copied() + ), + "color attachment {attachment_idx} store incompatible with existing attachment" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_clears + .get(&attachment_idx) + .map(|(attachment, _)| *attachment), + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_stores + .get(&attachment_idx) + .copied() + ), + "color attachment {attachment_idx} store incompatible with existing clear" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_loads + .get(&attachment_idx) + .copied(), + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .color_stores + .get(&attachment_idx) + .copied() + ), + "color attachment {attachment_idx} store incompatible with existing load" + ); + + let mut image_access = AccessType::ColorAttachmentWrite; + let image_range = image_view_info.into(); + + // Upgrade existing read access to read-write + if let Some(accesses) = self + .cmd + .as_mut() + .execs + .last_mut() + .unwrap() + .accesses + .get_mut(&node_idx) + { + for SubresourceAccess { + access, + subresource, + } in accesses + { + let access_image_range = *subresource.as_image().unwrap(); + if !image_subresource_range_intersects(access_image_range, image_range) { + continue; + } + + image_access = match *access { + AccessType::ColorAttachmentRead | AccessType::ColorAttachmentReadWrite => { + AccessType::ColorAttachmentReadWrite + } + AccessType::ColorAttachmentWrite => AccessType::ColorAttachmentWrite, + _ => continue, + }; + + *access = image_access; + + // If the store access is a subset of the existing access range there is no need + // to push a new access + if image_subresource_range_contains(access_image_range, image_range) { + return self; + } + } + } + + self.cmd + .push_node_access(image, image_access, Subresource::Image(image_range)); + + self + } + + /// Specifies `VK_ATTACHMENT_STORE_OP_STORE` for the render pass attachment, and stores the + /// rendered pixels into an image. + pub fn store_depth_stencil(self, image: impl Into) -> Self { + let image: AnyImageNode = image.into(); + let image_info = self.node_info(image); + + // Use the plain node information as the whole view of the node + let image_view_info = image_info; + + self.store_depth_stencil_as(image, image_view_info) + } + + /// Specifies `VK_ATTACHMENT_STORE_OP_STORE` for the render pass attachment, and stores the + /// rendered pixels into an image. + /// + /// _NOTE:_ Order matters, call store after clear or load. + pub fn store_depth_stencil_as( + mut self, + image: impl Into, + image_view_info: impl Into, + ) -> Self { + let image = image.into(); + let image_view_info = image_view_info.into(); + let node_idx = image.index(); + let (_, sample_count) = self.image_info(node_idx); + + self.cmd + .as_mut() + .execs + .last_mut() + .unwrap() + .depth_stencil_store = Some(Attachment::new(image_view_info, sample_count, node_idx)); + + debug_assert!( + Attachment::are_compatible( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .depth_stencil_attachment, + self.cmd.as_ref().execs.last().unwrap().depth_stencil_store + ), + "depth/stencil attachment store incompatible with existing attachment" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .depth_stencil_clear + .map(|(attachment, _)| attachment), + self.cmd.as_ref().execs.last().unwrap().depth_stencil_store + ), + "depth/stencil attachment store incompatible with existing clear" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd.as_ref().execs.last().unwrap().depth_stencil_load, + self.cmd.as_ref().execs.last().unwrap().depth_stencil_store + ), + "depth/stencil attachment store incompatible with existing load" + ); + + let mut image_access = if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL) + { + AccessType::DepthStencilAttachmentWrite + } else if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH) + { + AccessType::DepthAttachmentWriteStencilReadOnly + } else { + debug_assert!( + image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::STENCIL) + ); + + AccessType::StencilAttachmentWriteDepthReadOnly + }; + let image_range = image_view_info.into(); + + // Upgrade existing read access to read-write + if let Some(accesses) = self + .cmd + .as_mut() + .execs + .last_mut() + .unwrap() + .accesses + .get_mut(&node_idx) + { + for SubresourceAccess { + access, + subresource, + } in accesses + { + let access_image_range = *subresource.as_image().unwrap(); + if !image_subresource_range_intersects(access_image_range, image_range) { + continue; + } + + image_access = match *access { + AccessType::DepthAttachmentWriteStencilReadOnly => { + if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::STENCIL) + { + AccessType::DepthStencilAttachmentReadWrite + } else { + AccessType::DepthAttachmentWriteStencilReadOnly + } + } + AccessType::DepthStencilAttachmentRead => { + if !image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH) + { + AccessType::StencilAttachmentWriteDepthReadOnly + } else { + AccessType::DepthStencilAttachmentReadWrite + } + } + AccessType::DepthStencilAttachmentWrite => { + AccessType::DepthStencilAttachmentWrite + } + AccessType::StencilAttachmentWriteDepthReadOnly => { + if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH) + { + AccessType::DepthStencilAttachmentReadWrite + } else { + AccessType::StencilAttachmentWriteDepthReadOnly + } + } + _ => continue, + }; + + *access = image_access; + + // If the store access is a subset of the existing access range there is no need + // to push a new access + if image_subresource_range_contains(access_image_range, image_range) { + return self; + } + } + } + + self.cmd + .push_node_access(image, image_access, Subresource::Image(image_range)); + + self + } +} diff --git a/src/cmd_ref/mod.rs b/src/cmd_ref/mod.rs new file mode 100644 index 00000000..cf826b13 --- /dev/null +++ b/src/cmd_ref/mod.rs @@ -0,0 +1,681 @@ +//! Strongly-typed rendering commands. + +mod accel; +mod compute; +mod graphic; +mod pipeline; +mod ray_trace; +mod view; + +pub use self::{ + pipeline::PipelineCommandRef, + view::{View, ViewType}, +}; + +use { + self::accel::Acceleration, + super::{ + AccelerationStructureLeaseNode, AccelerationStructureNode, AnyAccelerationStructureNode, + AnyBufferNode, AnyImageNode, Bind, Binding, BufferLeaseNode, BufferNode, Command, Edge, + Execution, ExecutionFunction, ExecutionPipeline, Graph, ImageLeaseNode, ImageNode, Info, + Node, SwapchainImageNode, + }, + crate::driver::{ + accel_struct::AccelerationStructure, + buffer::{Buffer, BufferSubresourceRange}, + compute::ComputePipeline, + device::Device, + graphic::GraphicPipeline, + image::{Image, ImageViewInfo}, + ray_trace::RayTracePipeline, + }, + ash::vk, + std::{marker::PhantomData, ops::Index, sync::Arc}, + vk_sync::AccessType, +}; + +/// Alias for the index of a framebuffer attachment. +pub type AttachmentIndex = u32; + +/// Alias for the binding index of a shader descriptor. +pub type BindingIndex = u32; + +/// Alias for the binding offset of a shader descriptor array element. +pub type BindingOffset = u32; + +/// Alias for the descriptor set index of a shader descriptor. +pub type DescriptorSetIndex = u32; + +/// Associated type trait which enables default values for read and write methods. +pub trait Access { + /// The default `AccessType` for read operations, if not specified explicitly. + const DEFAULT_READ: AccessType; + + /// The default `AccessType` for write operations, if not specified explicitly. + const DEFAULT_WRITE: AccessType; +} + +impl Access for ComputePipeline { + const DEFAULT_READ: AccessType = AccessType::ComputeShaderReadOther; + const DEFAULT_WRITE: AccessType = AccessType::ComputeShaderWrite; +} + +impl Access for GraphicPipeline { + const DEFAULT_READ: AccessType = AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer; + const DEFAULT_WRITE: AccessType = AccessType::AnyShaderWrite; +} + +impl Access for RayTracePipeline { + const DEFAULT_READ: AccessType = + AccessType::RayTracingShaderReadSampledImageOrUniformTexelBuffer; + const DEFAULT_WRITE: AccessType = AccessType::AnyShaderWrite; +} + +macro_rules! bind { + ($name:ident) => { + paste::paste! { + impl<'a> Bind, PipelineCommandRef<'a, [<$name Pipeline>]>> for &'a Arc<[<$name Pipeline>]> { + // TODO: Allow binding as explicit secondary command buffers? like with compute/raytrace stuff + fn bind(self, mut cmd: CommandRef<'a>) -> PipelineCommandRef<'a, [<$name Pipeline>]> { + let cmd_ref = cmd.as_mut(); + if cmd_ref.execs.last().unwrap().pipeline.is_some() { + // Binding from PipelinePass -> PipelinePass (changing shaders) + cmd_ref.execs.push(Default::default()); + } + + cmd_ref.execs.last_mut().unwrap().pipeline = Some(ExecutionPipeline::$name(Arc::clone(self))); + + PipelineCommandRef { + __: PhantomData, + cmd, + } + } + } + + impl<'a> Bind, PipelineCommandRef<'a, [<$name Pipeline>]>> for Arc<[<$name Pipeline>]> { + // TODO: Allow binding as explicit secondary command buffers? like with compute/raytrace stuff + fn bind(self, mut cmd: CommandRef<'a>) -> PipelineCommandRef<'a, [<$name Pipeline>]> { + let cmd_ref = cmd.as_mut(); + if cmd_ref.execs.last().unwrap().pipeline.is_some() { + // Binding from PipelinePass -> PipelinePass (changing shaders) + cmd_ref.execs.push(Default::default()); + } + + cmd_ref.execs.last_mut().unwrap().pipeline = Some(ExecutionPipeline::$name(self)); + + PipelineCommandRef { + __: PhantomData, + cmd, + } + } + } + + impl<'a> Bind, PipelineCommandRef<'a, [<$name Pipeline>]>> for [<$name Pipeline>] { + // TODO: Allow binding as explicit secondary command buffers? like with compute/raytrace stuff + fn bind(self, mut cmd: CommandRef<'a>) -> PipelineCommandRef<'a, [<$name Pipeline>]> { + let cmd_ref = cmd.as_mut(); + if cmd_ref.execs.last().unwrap().pipeline.is_some() { + // Binding from PipelinePass -> PipelinePass (changing shaders) + cmd_ref.execs.push(Default::default()); + } + + cmd_ref.execs.last_mut().unwrap().pipeline = Some(ExecutionPipeline::$name(Arc::new(self))); + + PipelineCommandRef { + __: PhantomData, + cmd, + } + } + } + + impl ExecutionPipeline { + #[allow(unused)] + pub(super) fn [](&self) -> bool { + matches!(self, Self::$name(_)) + } + + #[allow(unused)] + pub(super) fn [](&self) -> &Arc<[<$name Pipeline>]> { + if let Self::$name(binding) = self { + &binding + } else { + panic!(); + } + } + } + } + }; +} + +// Pipelines you can bind to a pass +bind!(Compute); +bind!(Graphic); +bind!(RayTrace); + +/// An indexable structure will provides access to Vulkan smart-pointer resources inside a record +/// closure. +/// +/// This type is available while recording commands in the following closures: +/// +/// - [`PassRef::record_acceleration`] for building and updating acceleration structures +/// - [`PassRef::record_cmd_buf`] for general command streams +/// - [`PipelineCommandRef::record_pipeline`] for dispatched compute operations +/// - [`PipelineCommandRef::record_pipeline`] for raster drawing operations, such as triangles streams +/// - [`PipelineCommandRef::record_ray_trace`] for ray-traced operations +/// +/// # Examples +/// +/// Basic usage: +/// +/// ```no_run +/// # use std::sync::Arc; +/// # use ash::vk; +/// # use vk_graph::driver::DriverError; +/// # use vk_graph::driver::device::{Device, DeviceInfo}; +/// # use vk_graph::driver::image::{Image, ImageInfo}; +/// # use vk_graph::Graph; +/// # use vk_graph::node::ImageNode; +/// # fn main() -> Result<(), DriverError> { +/// # let device = Arc::new(Device::new(DeviceInfo::default())?); +/// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); +/// # let image = Image::create(&device, info)?; +/// # let mut my_graph = Graph::default(); +/// # let my_image_node = my_graph.bind_node(image); +/// my_graph.begin_cmd().with_name("custom vulkan commands") +/// .record_cmd_buf(move |device, cmd_buf, bindings| { +/// let my_image = &bindings[my_image_node]; +/// +/// assert_ne!(my_image.handle, vk::Image::null()); +/// assert_eq!(my_image.info.width, 32); +/// }); +/// # Ok(()) } +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct Bindings<'a> { + bindings: &'a [Binding], + exec: &'a Execution, +} + +impl<'a> Bindings<'a> { + pub(super) fn new(bindings: &'a [Binding], exec: &'a Execution) -> Self { + Self { bindings, exec } + } + + fn binding_ref(&self, node_idx: usize) -> &Binding { + // You must have called read or write for this node on this execution before indexing + // into the bindings data! + debug_assert!( + self.exec.accesses.contains_key(&node_idx), + "unexpected node access: call access, read, or write first" + ); + + &self.bindings[node_idx] + } +} + +macro_rules! index { + ($name:ident, $handle:ident) => { + paste::paste! { + impl<'a> Index<[<$name Node>]> for Bindings<'a> + { + type Output = $handle; + + fn index(&self, node: [<$name Node>]) -> &Self::Output { + &*self.binding_ref(node.idx).[]().unwrap() + } + } + } + }; +} + +// Allow indexing the Bindings data during command execution: +// (This gets you access to the driver images or other resources) +index!(AccelerationStructure, AccelerationStructure); +index!(AccelerationStructureLease, AccelerationStructure); +index!(Buffer, Buffer); +index!(BufferLease, Buffer); +index!(Image, Image); +index!(ImageLease, Image); +index!(SwapchainImage, Image); + +impl Index for Bindings<'_> { + type Output = AccelerationStructure; + + fn index(&self, node: AnyAccelerationStructureNode) -> &Self::Output { + let node_idx = match node { + AnyAccelerationStructureNode::AccelerationStructure(node) => node.idx, + AnyAccelerationStructureNode::AccelerationStructureLease(node) => node.idx, + }; + let binding = self.binding_ref(node_idx); + + match node { + AnyAccelerationStructureNode::AccelerationStructure(_) => { + binding.as_acceleration_structure().unwrap() + } + AnyAccelerationStructureNode::AccelerationStructureLease(_) => { + binding.as_acceleration_structure_lease().unwrap() + } + } + } +} + +impl Index for Bindings<'_> { + type Output = Buffer; + + fn index(&self, node: AnyBufferNode) -> &Self::Output { + let node_idx = match node { + AnyBufferNode::Buffer(node) => node.idx, + AnyBufferNode::BufferLease(node) => node.idx, + }; + let binding = self.binding_ref(node_idx); + + match node { + AnyBufferNode::Buffer(_) => binding.as_buffer().unwrap(), + AnyBufferNode::BufferLease(_) => binding.as_buffer_lease().unwrap(), + } + } +} + +impl Index for Bindings<'_> { + type Output = Image; + + fn index(&self, node: AnyImageNode) -> &Self::Output { + let node_idx = match node { + AnyImageNode::Image(node) => node.idx, + AnyImageNode::ImageLease(node) => node.idx, + AnyImageNode::SwapchainImage(node) => node.idx, + }; + let binding = self.binding_ref(node_idx); + + match node { + AnyImageNode::Image(_) => binding.as_image().unwrap(), + AnyImageNode::ImageLease(_) => binding.as_image_lease().unwrap(), + AnyImageNode::SwapchainImage(_) => binding.as_swapchain_image().unwrap(), + } + } +} + +/// A general render pass which may contain acceleration structure commands, general commands, or +/// have pipeline bound to then record commands specific to those pipeline types. +pub struct CommandRef<'a> { + pub(super) cmd_idx: usize, + pub(super) exec_idx: usize, + pub(super) graph: &'a mut Graph, +} + +impl<'a> CommandRef<'a> { + pub(super) fn new(graph: &'a mut Graph) -> Self { + let cmd_idx = graph.cmds.len(); + graph.cmds.push(Command { + execs: vec![Default::default()], // We start off with a default execution! + name: None, + }); + + Self { + cmd_idx, + exec_idx: 0, + graph, + } + } + + /// Informs the pass that the next recorded command buffer will read or write the given `node` + /// using `access`. + /// + /// This function must be called for `node` before it is read or written within a `record` + /// function. For general purpose access, see [`PassRef::read_node`] or [`PassRef::write_node`]. + pub fn access_node(mut self, node: impl Node + Info, access: AccessType) -> Self { + self.access_node_mut(node, access); + + self + } + + /// Informs the pass that the next recorded command buffer will read or write the given `node` + /// using `access`. + /// + /// This function must be called for `node` before it is read or written within a `record` + /// function. For general purpose access, see [`PassRef::read_node_mut`] or + /// [`PassRef::write_node_mut`]. + pub fn access_node_mut(&mut self, node: impl Node + Info, access: AccessType) { + self.assert_bound_graph_node(node); + + let idx = node.index(); + let binding = &self.graph.bindings[idx]; + + let node_access_range = if let Some(buf) = binding.as_driver_buffer() { + Subresource::Buffer((0..buf.info.size).into()) + } else if let Some(image) = binding.as_driver_image() { + Subresource::Image(image.info.default_view_info().into()) + } else { + Subresource::AccelerationStructure + }; + + self.push_node_access(node, access, node_access_range); + } + + /// Informs the pass that the next recorded command buffer will read or write the `subresource` + /// of `node` using `access`. + /// + /// This function must be called for `node` before it is read or written within a `record` + /// function. For general purpose access, see [`PassRef::read_node`] or [`PassRef::write_node`]. + pub fn access_node_subrange( + mut self, + node: N, + access: AccessType, + subresource: impl Into, + ) -> Self + where + N: View, + { + self.access_node_subrange_mut(node, access, subresource); + + self + } + + /// Informs the pass that the next recorded command buffer will read or write the `subresource` + /// of `node` using `access`. + /// + /// This function must be called for `node` before it is read or written within a `record` + /// function. For general purpose access, see [`PassRef::read_node`] or [`PassRef::write_node`]. + pub fn access_node_subrange_mut( + &mut self, + node: N, + access: AccessType, + subresource: impl Into, + ) where + N: View, + { + self.push_node_access(node, access, subresource.into().into()); + } + + fn as_mut(&mut self) -> &mut Command { + &mut self.graph.cmds[self.cmd_idx] + } + + fn as_ref(&self) -> &Command { + &self.graph.cmds[self.cmd_idx] + } + + fn assert_bound_graph_node(&self, node: impl Node) { + let idx = node.index(); + + assert!(self.graph.bindings[idx].is_bound()); + } + + /// Binds a Vulkan acceleration structure, buffer, or image to the graph associated with this + /// pass. + /// + /// Bound nodes may be used in passes for pipeline and shader operations. + pub fn bind_node<'b, B>(&'b mut self, binding: B) -> >::Result + where + B: Edge, + B: Bind<&'b mut Graph, >::Result>, + { + self.graph.bind_node(binding) + } + + /// Binds a [`ComputePipeline`], [`GraphicPipeline`], or [`RayTracePipeline`] to the current + /// pass, allowing for strongly typed access to the related functions. + pub fn bind_pipeline(self, binding: B) -> >::Result + where + B: Edge, + B: Bind>::Result>, + { + binding.bind(self) + } + + /// Finalize the recording of this command and return to the `Graph` where you may record + /// additional commands. + pub fn end_cmd(self) -> &'a mut Graph { + // If nothing was done in this pass we can just ignore it + if self.exec_idx == 0 { + self.graph.cmds.pop(); + } + + self.graph + } + + /// Returns Info used to crate a node. + pub fn node_info(&self, node: N) -> ::Info + where + N: Info, + { + node.info(&self.graph.bindings) + } + + fn push_execute( + &mut self, + func: impl FnOnce(&Device, vk::CommandBuffer, Bindings<'_>) + Send + 'static, + ) { + let pass = self.as_mut(); + let exec = { + let last_exec = pass.execs.last_mut().unwrap(); + last_exec.func = Some(ExecutionFunction(Box::new(func))); + + Execution { + pipeline: last_exec.pipeline.clone(), + ..Default::default() + } + }; + + pass.execs.push(exec); + self.exec_idx += 1; + } + + fn push_node_access(&mut self, node: impl Node, access: AccessType, subresource: Subresource) { + let node_idx = node.index(); + self.assert_bound_graph_node(node); + + let access = SubresourceAccess { + access, + subresource, + }; + self.as_mut() + .execs + .last_mut() + .unwrap() + .accesses + .entry(node_idx) + .and_modify(|accesses| accesses.push(access)) + .or_insert(vec![access]); + } + + /// Informs the pass that the next recorded command buffer will read the given `node` using + /// [`AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer`]. + /// + /// This function must be called for `node` before it is read within a `record` function. For + /// more specific access, see [`PassRef::access_node`]. + pub fn read_node(mut self, node: impl Node + Info) -> Self { + self.read_node_mut(node); + + self + } + + /// Informs the pass that the next recorded command buffer will read the given `node` using + /// [`AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer`]. + /// + /// This function must be called for `node` before it is read within a `record` function. For + /// more specific access, see [`PassRef::access_node`]. + pub fn read_node_mut(&mut self, node: impl Node + Info) { + self.access_node_mut( + node, + AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, + ); + } + + /// Begin recording an acceleration structure command buffer. + /// + /// This is the entry point for building and updating an [`AccelerationStructure`] instance. + pub fn record_acceleration( + mut self, + func: impl FnOnce(Acceleration<'_>, Bindings<'_>) + Send + 'static, + ) -> Self { + self.push_execute(move |device, cmd_buf, bindings| { + func( + Acceleration { + bindings, + cmd_buf, + device, + }, + bindings, + ); + }); + + self + } + + /// Begin recording a general command buffer. + /// + /// The provided closure allows you to run any Vulkan code, or interoperate with other Vulkan + /// code and interfaces. + pub fn record_cmd_buf( + mut self, + func: impl FnOnce(&Device, vk::CommandBuffer, Bindings<'_>) + Send + 'static, + ) -> Self { + self.push_execute(func); + + self + } + + /// Sets a debugging name, but only in debug builds + pub fn with_name(mut self, name: impl Into) -> Self { + #[cfg(debug_assertions)] + { + self.as_mut().name = Some(name.into()); + } + + self + } + + /// Informs the pass that the next recorded command buffer will write the given `node` using + /// [`AccessType::AnyShaderWrite`]. + /// + /// This function must be called for `node` before it is written within a `record` function. For + /// more specific access, see [`PassRef::access_node`]. + pub fn write_node(mut self, node: impl Node + Info) -> Self { + self.write_node_mut(node); + + self + } + + /// Informs the pass that the next recorded command buffer will write the given `node` using + /// [`AccessType::AnyShaderWrite`]. + /// + /// This function must be called for `node` before it is written within a `record` function. For + /// more specific access, see [`PassRef::access_node`]. + pub fn write_node_mut(&mut self, node: impl Node + Info) { + self.access_node_mut(node, AccessType::AnyShaderWrite); + } +} + +/// Describes the SPIR-V binding index, and optionally a specific descriptor set +/// and array index. +/// +/// Generally you might pass a function a descriptor using a simple integer: +/// +/// ```rust +/// # fn my_func(_: usize, _: ()) {} +/// # let image = (); +/// let descriptor = 42; +/// my_func(descriptor, image); +/// ``` +/// +/// But also: +/// +/// - `(0, 42)` for descriptor set `0` and binding index `42` +/// - `(42, [8])` for the same binding, but the 8th element +/// - `(0, 42, [8])` same as the previous example +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum Descriptor { + /// An array binding which includes an `offset` argument for the bound element. + ArrayBinding(DescriptorSetIndex, BindingIndex, BindingOffset), + + /// A single binding. + Binding(DescriptorSetIndex, BindingIndex), +} + +impl Descriptor { + pub(super) fn into_tuple(self) -> (DescriptorSetIndex, BindingIndex, BindingOffset) { + match self { + Self::ArrayBinding(descriptor_set_idx, binding_idx, binding_offset) => { + (descriptor_set_idx, binding_idx, binding_offset) + } + Self::Binding(descriptor_set_idx, binding_idx) => (descriptor_set_idx, binding_idx, 0), + } + } + + pub(super) fn set(self) -> DescriptorSetIndex { + let (res, _, _) = self.into_tuple(); + res + } +} + +impl From for Descriptor { + fn from(val: BindingIndex) -> Self { + Self::Binding(0, val) + } +} + +impl From<(DescriptorSetIndex, BindingIndex)> for Descriptor { + fn from(tuple: (DescriptorSetIndex, BindingIndex)) -> Self { + Self::Binding(tuple.0, tuple.1) + } +} + +impl From<(BindingIndex, [BindingOffset; 1])> for Descriptor { + fn from(tuple: (BindingIndex, [BindingOffset; 1])) -> Self { + Self::ArrayBinding(0, tuple.0, tuple.1[0]) + } +} + +impl From<(DescriptorSetIndex, BindingIndex, [BindingOffset; 1])> for Descriptor { + fn from(tuple: (DescriptorSetIndex, BindingIndex, [BindingOffset; 1])) -> Self { + Self::ArrayBinding(tuple.0, tuple.1, tuple.2[0]) + } +} + +/// Describes a portion of a resource which is bound. +#[derive(Clone, Copy, Debug)] +pub enum Subresource { + /// Acceleration structures are bound whole. + AccelerationStructure, + + /// Images may be partially bound. + Image(vk::ImageSubresourceRange), + + /// Buffers may be partially bound. + Buffer(BufferSubresourceRange), +} + +impl Subresource { + pub(super) fn as_image(&self) -> Option<&vk::ImageSubresourceRange> { + if let Self::Image(subresource) = self { + Some(subresource) + } else { + None + } + } +} + +impl From<()> for Subresource { + fn from(_: ()) -> Self { + Self::AccelerationStructure + } +} + +impl From for Subresource { + fn from(subresource: vk::ImageSubresourceRange) -> Self { + Self::Image(subresource) + } +} + +impl From for Subresource { + fn from(subresource: BufferSubresourceRange) -> Self { + Self::Buffer(subresource) + } +} + +#[derive(Clone, Copy, Debug)] +pub(super) struct SubresourceAccess { + pub access: AccessType, + pub subresource: Subresource, +} diff --git a/src/cmd_ref/pipeline.rs b/src/cmd_ref/pipeline.rs new file mode 100644 index 00000000..b4d1c309 --- /dev/null +++ b/src/cmd_ref/pipeline.rs @@ -0,0 +1,463 @@ +use { + super::{ + Access, AccessType, Bind, CommandRef, Descriptor, Edge, Graph, Info, Node, Subresource, + View, ViewType, + }, + std::marker::PhantomData, +}; + +/// A render pass which has been bound to a particular compute, graphic, or ray-trace pipeline. +pub struct PipelineCommandRef<'a, T> { + pub(super) __: PhantomData, + pub(super) cmd: CommandRef<'a>, +} + +// NOTE: There are specific implementations of T in the compute, graphic, and ray trace modules +impl<'a, T> PipelineCommandRef<'a, T> +where + T: Access, +{ + /// Informs the pass that the next recorded command buffer will read or write the given `node` + /// at the specified shader descriptor using `access`. + /// + /// This function must be called for `node` before it is read or written within a `record` + /// function. For general purpose access, see [`PipelineCommandRef::read_descriptor`] or + /// [`PipelineCommandRef::write_descriptor`]. + pub fn access_descriptor( + self, + descriptor: impl Into, + node: N, + access: AccessType, + ) -> Self + where + N: Info, + N: View, + ViewType: From<::Info>, + ::Info: From<::Info>, + ::Subresource: From<::Info>, + { + let node_info = self.node_info(node); + + // Use the plain node information as the whole view of the node + let view_info = node_info; + + self.access_descriptor_as(descriptor, node, access, view_info) + } + + /// Informs the pass that the next recorded command buffer will read or write the given `node` + /// at the specified shader descriptor using `access`. The node will be interpreted using + /// `view_info`. + /// + /// This function must be called for `node` before it is read or written within a `record` + /// function. For general purpose access, see [`PipelineCommandRef::read_descriptor_as`] or + /// [`PipelineCommandRef::write_descriptor_as`]. + pub fn access_descriptor_as( + self, + descriptor: impl Into, + node: N, + access: AccessType, + view_info: impl Into, + ) -> Self + where + N: View, + ::Info: Into, + ::Subresource: From<::Info>, + { + let view_info = view_info.into(); + let subresource = ::Subresource::from(view_info); + + self.access_descriptor_subrange(descriptor, node, access, view_info, subresource) + } + + /// Informs the pass that the next recorded command buffer will read or write the `subresource` + /// of `node` at the specified shader descriptor using `access`. The node will be interpreted + /// using `view_info`. + /// + /// This function must be called for `node` before it is read or written within a `record` + /// function. For general purpose access, see [`PipelineCommandRef::read_descriptor_subrange`] or + /// [`PipelineCommandRef::write_descriptor_subrange`]. + pub fn access_descriptor_subrange( + mut self, + descriptor: impl Into, + node: N, + access: AccessType, + view_info: impl Into, + subresource: impl Into, + ) -> Self + where + N: View, + ::Info: Into, + { + self.cmd + .push_node_access(node, access, subresource.into().into()); + self.push_node_view_bind(node, view_info.into(), descriptor.into()); + + self + } + + /// Informs the pass that the next recorded command buffer will read or write the given `node` + /// using `access`. + /// + /// This function must be called for `node` before it is read or written within a `record` + /// function. For general purpose access, see [`PipelineCommandRef::read_node`] or + /// [`PipelineCommandRef::write_node`]. + pub fn access_node(mut self, node: impl Node + Info, access: AccessType) -> Self { + self.access_node_mut(node, access); + + self + } + + /// Informs the pass that the next recorded command buffer will read or write the given `node` + /// using `access`. + /// + /// This function must be called for `node` before it is read or written within a `record` + /// function. For general purpose access, see [`PipelineCommandRef::read_node_mut`] or + /// [`PipelineCommandRef::write_node_mut`]. + pub fn access_node_mut(&mut self, node: impl Node + Info, access: AccessType) { + self.cmd.assert_bound_graph_node(node); + + let idx = node.index(); + let binding = &self.cmd.graph.bindings[idx]; + + let node_access_range = if let Some(buf) = binding.as_driver_buffer() { + Subresource::Buffer((0..buf.info.size).into()) + } else if let Some(image) = binding.as_driver_image() { + Subresource::Image(image.info.default_view_info().into()) + } else { + Subresource::AccelerationStructure + }; + + self.cmd.push_node_access(node, access, node_access_range); + } + + /// Informs the pass that the next recorded command buffer will read or write the `subresource` + /// of `node` using `access`. + /// + /// This function must be called for `node` before it is read or written within a `record` + /// function. For general purpose access, see [`PipelineCommandRef::read_node_subrange`] or + /// [`PipelineCommandRef::write_node_subrange`]. + pub fn access_node_subrange( + mut self, + node: N, + access: AccessType, + subresource: impl Into, + ) -> Self + where + N: View, + { + self.access_node_subrange_mut(node, access, subresource); + + self + } + + /// Informs the pass that the next recorded command buffer will read or write the `subresource` + /// of `node` using `access`. + /// + /// This function must be called for `node` before it is read or written within a `record` + /// function. For general purpose access, see [`PipelineCommandRef::read_node_subrange_mut`] or + /// [`PipelineCommandRef::write_node_subrange_mut`]. + pub fn access_node_subrange_mut( + &mut self, + node: N, + access: AccessType, + subresource: impl Into, + ) where + N: View, + { + self.cmd + .push_node_access(node, access, subresource.into().into()); + } + + /// Binds a Vulkan acceleration structure, buffer, or image to the graph associated with this + /// pass. + /// + /// Bound nodes may be used in passes for pipeline and shader operations. + pub fn bind_node<'b, B>(&'b mut self, binding: B) -> >::Result + where + B: Edge, + B: Bind<&'b mut Graph, >::Result>, + { + self.cmd.graph.bind_node(binding) + } + + /// Finalizes a command and returns the render graph so that additional commands may be added. + pub fn end_cmd(self) -> &'a mut Graph { + self.cmd.end_cmd() + } + + /// Returns Info used to crate a node. + pub fn node_info(&self, node: N) -> ::Info + where + N: Info, + { + node.info(&self.cmd.graph.bindings) + } + + fn push_node_view_bind( + &mut self, + node: impl Node, + view_info: impl Into, + binding: Descriptor, + ) { + let node_idx = node.index(); + self.cmd.assert_bound_graph_node(node); + + assert!( + self.cmd + .as_mut() + .execs + .last_mut() + .unwrap() + .bindings + .insert(binding, (node_idx, Some(view_info.into()))) + .is_none(), + "descriptor {binding:?} has already been bound" + ); + } + + /// Informs the pass that the next recorded command buffer will read the given `node` at the + /// specified shader descriptor. + /// + /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. + /// + /// This function must be called for `node` before it is read within a `record` function. For + /// more specific access, see [`PipelineCommandRef::access_descriptor`]. + pub fn read_descriptor(self, descriptor: impl Into, node: N) -> Self + where + N: Info, + N: View, + ViewType: From<::Info>, + ::Info: From<::Info>, + ::Subresource: From<::Info>, + { + let node_info = self.node_info(node); + + // Use the plain node information as the whole view of the node + let view_info = node_info; + + self.read_descriptor_as(descriptor, node, view_info) + } + + /// Informs the pass that the next recorded command buffer will read the given `node` at the + /// specified shader descriptor. The node will be interpreted using `view_info`. + /// + /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. + /// + /// This function must be called for `node` before it is read within a `record` function. For + /// more specific access, see [`PipelineCommandRef::access_descriptor_as`]. + pub fn read_descriptor_as( + self, + descriptor: impl Into, + node: N, + view_info: impl Into, + ) -> Self + where + N: View, + ::Info: Into, + ::Subresource: From<::Info>, + { + let view_info = view_info.into(); + let subresource = ::Subresource::from(view_info); + + self.read_descriptor_subrange(descriptor, node, view_info, subresource) + } + + /// Informs the pass that the next recorded command buffer will read the `subresource` of `node` + /// at the specified shader descriptor. The node will be interpreted using `view_info`. + /// + /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. + /// + /// This function must be called for `node` before it is read within a `record` function. For + /// more specific access, see [`PipelineCommandRef::access_descriptor_subrange`]. + pub fn read_descriptor_subrange( + self, + descriptor: impl Into, + node: N, + view_info: impl Into, + subresource: impl Into, + ) -> Self + where + N: View, + ::Info: Into, + { + let access = ::DEFAULT_READ; + self.access_descriptor_subrange(descriptor, node, access, view_info, subresource) + } + + /// Informs the pass that the next recorded command buffer will read the given `node`. + /// + /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. + /// + /// This function must be called for `node` before it is read within a `record` function. For + /// more specific access, see [`PipelineCommandRef::access_node`]. + pub fn read_node(mut self, node: impl Node + Info) -> Self { + self.read_node_mut(node); + + self + } + + /// Informs the pass that the next recorded command buffer will read the given `node`. + /// + /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. + /// + /// This function must be called for `node` before it is read within a `record` function. For + /// more specific access, see [`PipelineCommandRef::access_node_mut`]. + pub fn read_node_mut(&mut self, node: impl Node + Info) { + let access = ::DEFAULT_READ; + self.access_node_mut(node, access); + } + + /// Informs the pass that the next recorded command buffer will read the `subresource` of + /// `node`. + /// + /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. + /// + /// This function must be called for `node` before it is read within a `record` function. For + /// more specific access, see [`PipelineCommandRef::access_node_subrange`]. + pub fn read_node_subrange(mut self, node: N, subresource: impl Into) -> Self + where + N: View, + { + self.read_node_subrange_mut(node, subresource); + + self + } + + /// Informs the pass that the next recorded command buffer will read the `subresource` of + /// `node`. + /// + /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. + /// + /// This function must be called for `node` before it is read within a `record` function. For + /// more specific access, see [`PipelineCommandRef::access_node_subrange_mut`]. + pub fn read_node_subrange_mut(&mut self, node: N, subresource: impl Into) + where + N: View, + { + let access = ::DEFAULT_READ; + self.access_node_subrange_mut(node, access, subresource); + } + + /// Informs the pass that the next recorded command buffer will write the given `node` at the + /// specified shader descriptor. + /// + /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. + /// + /// This function must be called for `node` before it is written within a `record` function. For + /// more specific access, see [`PipelineCommandRef::access_descriptor`]. + pub fn write_descriptor(self, descriptor: impl Into, node: N) -> Self + where + N: Info, + N: View, + ::Info: Into, + ::Info: From<::Info>, + ::Subresource: From<::Info>, + { + let node_info = self.node_info(node); + + // Use the plain node information as the whole view of the node + let view_info = node_info; + + self.write_descriptor_as(descriptor, node, view_info) + } + + /// Informs the pass that the next recorded command buffer will write the given `node` at the + /// specified shader descriptor. The node will be interpreted using `view_info`. + /// + /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. + /// + /// This function must be called for `node` before it is written within a `record` function. For + /// more specific access, see [`PipelineCommandRef::access_descriptor_as`]. + pub fn write_descriptor_as( + self, + descriptor: impl Into, + node: N, + view_info: impl Into, + ) -> Self + where + N: View, + ::Info: Into, + ::Subresource: From<::Info>, + { + let view_info = view_info.into(); + let subresource = ::Subresource::from(view_info); + + self.write_descriptor_subrange(descriptor, node, view_info, subresource) + } + + /// Informs the pass that the next recorded command buffer will write the `subresource` of + /// `node` at the specified shader descriptor. The node will be interpreted using `view_info`. + /// + /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. + /// + /// This function must be called for `node` before it is written within a `record` function. For + /// more specific access, see [`PipelineCommandRef::access_descriptor_subrange`]. + pub fn write_descriptor_subrange( + self, + descriptor: impl Into, + node: N, + view_info: impl Into, + subresource: impl Into, + ) -> Self + where + N: View, + ::Info: Into, + { + let access = ::DEFAULT_WRITE; + self.access_descriptor_subrange(descriptor, node, access, view_info, subresource) + } + + /// Informs the pass that the next recorded command buffer will write the given `node`. + /// + /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. + /// + /// This function must be called for `node` before it is written within a `record` function. For + /// more specific access, see [`PipelineCommandRef::access_node`]. + pub fn write_node(mut self, node: impl Node + Info) -> Self { + self.write_node_mut(node); + + self + } + + /// Informs the pass that the next recorded command buffer will write the given `node`. + /// + /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. + /// + /// This function must be called for `node` before it is written within a `record` function. For + /// more specific access, see [`PipelineCommandRef::access_node_mut`]. + pub fn write_node_mut(&mut self, node: impl Node + Info) { + let access = ::DEFAULT_WRITE; + self.access_node_mut(node, access); + } + + /// Informs the pass that the next recorded command buffer will write the `subresource` of + /// `node`. + /// + /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. + /// + /// This function must be called for `node` before it is written within a `record` function. For + /// more specific access, see [`PipelineCommandRef::access_node_subrange`]. + pub fn write_node_subrange(mut self, node: N, subresource: impl Into) -> Self + where + N: View, + { + self.write_node_subrange_mut(node, subresource); + + self + } + + /// Informs the pass that the next recorded command buffer will write the `subresource` of + /// `node`. + /// + /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. + /// + /// This function must be called for `node` before it is written within a `record` function. For + /// more specific access, see [`PipelineCommandRef::access_node_subrange_mut`]. + pub fn write_node_subrange_mut(&mut self, node: N, subresource: impl Into) + where + N: View, + { + let access = ::DEFAULT_WRITE; + self.access_node_subrange_mut(node, access, subresource); + } +} diff --git a/src/cmd_ref/ray_trace.rs b/src/cmd_ref/ray_trace.rs new file mode 100644 index 00000000..b8535a8d --- /dev/null +++ b/src/cmd_ref/ray_trace.rs @@ -0,0 +1,386 @@ +use { + super::{Bindings, PipelineCommandRef}, + crate::driver::{device::Device, ray_trace::RayTracePipeline}, + ash::vk, + log::trace, + std::sync::Arc, +}; + +// NOTE: local implementation of type from super module +impl PipelineCommandRef<'_, RayTracePipeline> { + /// Begin recording a ray tracing command buffer. + pub fn record_pipeline( + mut self, + func: impl FnOnce(RayTrace<'_>, Bindings<'_>) + Send + 'static, + ) -> Self { + let pipeline = Arc::clone( + self.cmd + .as_ref() + .execs + .last() + .unwrap() + .pipeline + .as_ref() + .unwrap() + .unwrap_ray_trace(), + ); + + #[cfg(debug_assertions)] + let dynamic_stack_size = pipeline.info.dynamic_stack_size; + + self.cmd.push_execute(move |device, cmd_buf, bindings| { + func( + RayTrace { + cmd_buf, + device, + + #[cfg(debug_assertions)] + dynamic_stack_size, + + pipeline, + }, + bindings, + ); + }); + + self + } +} + +/// Recording interface for ray tracing commands. +/// +/// This structure provides a strongly-typed set of methods which allow ray trace shader code to be +/// executed. An instance of `RayTrace` is provided to the closure parameter of +/// [`PipelineCommandRef::record_pipeline`] which may be accessed by binding a [`RayTracePipeline`] to +/// a render pass. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ```no_run +/// # use std::sync::Arc; +/// # use ash::vk; +/// # use vk_graph::driver::DriverError; +/// # use vk_graph::driver::device::{Device, DeviceInfo}; +/// # use vk_graph::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; +/// # use vk_graph::driver::shader::Shader; +/// # use vk_graph::Graph; +/// # fn main() -> Result<(), DriverError> { +/// # let device = Arc::new(Device::new(DeviceInfo::default())?); +/// # let info = RayTracePipelineInfo::default(); +/// # let my_miss_code = [0u8; 1]; +/// # let my_ray_trace_pipeline = Arc::new(RayTracePipeline::create(&device, info, +/// [Shader::new_miss(my_miss_code.as_slice())], +/// [RayTraceShaderGroup::new_general(0)], +/// )?); +/// # let mut my_graph = Graph::default(); +/// my_graph.begin_cmd().with_name("my ray trace pass") +/// .bind_pipeline(&my_ray_trace_pipeline) +/// .record_pipeline(move |pipeline, bindings| { +/// // During this closure we have access to the ray trace methods! +/// }); +/// # Ok(()) } +/// ``` +pub struct RayTrace<'a> { + cmd_buf: vk::CommandBuffer, + device: &'a Device, + + #[cfg(debug_assertions)] + dynamic_stack_size: bool, + + pipeline: Arc, +} + +impl RayTrace<'_> { + /// Updates push constants. + /// + /// Push constants represent a high speed path to modify constant data in pipelines that is + /// expected to outperform memory-backed resource updates. + /// + /// Push constant values can be updated incrementally, causing shader stages to read the new + /// data for push constants modified by this command, while still reading the previous data for + /// push constants not modified by this command. + /// + /// # Device limitations + /// + /// See + /// [`device.physical_device.props.limits.max_push_constants_size`](vk::PhysicalDeviceLimits) + /// for the limits of the current device. You may also check [gpuinfo.org] for a listing of + /// reported limits on other devices. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # vk_shader_macros::glsl!(target: vulkan1_2, r#" + /// #version 460 + /// #pragma shader_stage(closest) + /// + /// layout(push_constant) uniform PushConstants { + /// layout(offset = 0) uint some_val; + /// } push_constants; + /// + /// void main() { + /// // TODO: Add bindings to write things! + /// } + /// # "#); + /// ``` + /// + /// ```no_run + /// # use std::sync::Arc; + /// # use ash::vk; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; + /// # use vk_graph::driver::shader::Shader; + /// # use vk_graph::Graph; + /// # fn main() -> Result<(), DriverError> { + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let shader = [0u8; 1]; + /// # let info = RayTracePipelineInfo::default(); + /// # let my_miss_code = [0u8; 1]; + /// # let my_ray_trace_pipeline = Arc::new(RayTracePipeline::create(&device, info, + /// # [Shader::new_miss(my_miss_code.as_slice())], + /// # [RayTraceShaderGroup::new_general(0)], + /// # )?); + /// # let rgen_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; + /// # let hit_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; + /// # let miss_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; + /// # let call_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; + /// # let mut my_graph = Graph::default(); + /// my_graph.begin_cmd().with_name("draw a cornell box") + /// .bind_pipeline(&my_ray_trace_pipeline) + /// .record_pipeline(move |pipeline, bindings| { + /// pipeline.push_constants(&[0xcb]) + /// .trace_rays(&rgen_sbt, &hit_sbt, &miss_sbt, &call_sbt, 320, 200, 1); + /// }); + /// # Ok(()) } + /// ``` + /// + /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all + pub fn push_constants(&self, data: &[u8]) -> &Self { + self.push_constants_offset(0, data) + } + + /// Updates push constants starting at the given `offset`. + /// + /// Behaves similary to [`RayTrace::push_constants`] except that `offset` describes the position + /// at which `data` updates the push constants of the currently bound pipeline. This may be used + /// to update a subset or single field of previously set push constant data. + /// + /// # Device limitations + /// + /// See + /// [`device.physical_device.props.limits.max_push_constants_size`](vk::PhysicalDeviceLimits) + /// for the limits of the current device. You may also check [gpuinfo.org] for a listing of + /// reported limits on other devices. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # vk_shader_macros::glsl!(target: vulkan1_2, r#" + /// #version 460 + /// #pragma shader_stage(closest) + /// + /// layout(push_constant) uniform PushConstants { + /// layout(offset = 0) uint some_val1; + /// layout(offset = 4) uint some_val2; + /// } push_constants; + /// + /// void main() + /// { + /// // TODO: Add bindings to write things! + /// } + /// # "#); + /// ``` + /// + /// ```no_run + /// # use std::sync::Arc; + /// # use ash::vk; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; + /// # use vk_graph::driver::shader::Shader; + /// # use vk_graph::Graph; + /// # fn main() -> Result<(), DriverError> { + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let shader = [0u8; 1]; + /// # let info = RayTracePipelineInfo::default(); + /// # let my_miss_code = [0u8; 1]; + /// # let my_ray_trace_pipeline = Arc::new(RayTracePipeline::create(&device, info, + /// # [Shader::new_miss(my_miss_code.as_slice())], + /// # [RayTraceShaderGroup::new_general(0)], + /// # )?); + /// # let rgen_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; + /// # let hit_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; + /// # let miss_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; + /// # let call_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; + /// # let mut my_graph = Graph::default(); + /// my_graph.begin_cmd().with_name("draw a cornell box") + /// .bind_pipeline(&my_ray_trace_pipeline) + /// .record_pipeline(move |pipeline, bindings| { + /// pipeline.push_constants(&[0xcb, 0xff]) + /// .trace_rays(&rgen_sbt, &hit_sbt, &miss_sbt, &call_sbt, 320, 200, 1) + /// .push_constants_offset(4, &[0xae]) + /// .trace_rays(&rgen_sbt, &hit_sbt, &miss_sbt, &call_sbt, 320, 200, 1); + /// }); + /// # Ok(()) } + /// ``` + /// + /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all + #[profiling::function] + pub fn push_constants_offset(&self, offset: u32, data: &[u8]) -> &Self { + for push_const in self.pipeline.push_constants.iter() { + let push_const_end = push_const.offset + push_const.size; + let data_end = offset + data.len() as u32; + let end = data_end.min(push_const_end); + let start = offset.max(push_const.offset); + + if end > start { + trace!( + " push constants {:?} {}..{}", + push_const.stage_flags, start, end + ); + + unsafe { + self.device.cmd_push_constants( + self.cmd_buf, + self.pipeline.layout, + push_const.stage_flags, + start, + &data[(start - offset) as usize..(end - offset) as usize], + ); + } + } + } + self + } + + /// Set the stack size dynamically for a ray trace pipeline. + /// + /// See + /// [`RayTracePipelineInfo::dynamic_stack_size`](crate::driver::ray_trace::RayTracePipelineInfo::dynamic_stack_size) + /// and + /// [`vkCmdSetRayTracingPipelineStackSizeKHR`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCmdSetRayTracingPipelineStackSizeKHR.html). + #[profiling::function] + pub fn set_stack_size(&self, pipeline_stack_size: u32) -> &Self { + #[cfg(debug_assertions)] + assert!(self.dynamic_stack_size); + + unsafe { + Device::expect_ray_trace_ext(self.device) + .cmd_set_ray_tracing_pipeline_stack_size(self.cmd_buf, pipeline_stack_size); + } + + self + } + + // TODO: If the rayTraversalPrimitiveCulling or rayQuery features are enabled, the SkipTrianglesKHR and SkipAABBsKHR ray flags can be specified when tracing a ray. SkipTrianglesKHR and SkipAABBsKHR are mutually exclusive. + + /// Ray traces using the currently-bound [`RayTracePipeline`] and the given shader binding + /// tables. + /// + /// Shader binding tables must be constructed according to this [example]. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// # use std::sync::Arc; + /// # use ash::vk; + /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::device::{Device, DeviceInfo}; + /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; + /// # use vk_graph::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; + /// # use vk_graph::driver::shader::Shader; + /// # use vk_graph::Graph; + /// # fn main() -> Result<(), DriverError> { + /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let shader = [0u8; 1]; + /// # let info = RayTracePipelineInfo::default(); + /// # let my_miss_code = [0u8; 1]; + /// # let my_ray_trace_pipeline = Arc::new(RayTracePipeline::create(&device, info, + /// # [Shader::new_miss(my_miss_code.as_slice())], + /// # [RayTraceShaderGroup::new_general(0)], + /// # )?); + /// # let rgen_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; + /// # let hit_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; + /// # let miss_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; + /// # let call_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; + /// # let mut my_graph = Graph::default(); + /// my_graph.begin_cmd().with_name("draw a cornell box") + /// .bind_pipeline(&my_ray_trace_pipeline) + /// .record_pipeline(move |pipeline, bindings| { + /// pipeline.trace_rays(&rgen_sbt, &hit_sbt, &miss_sbt, &call_sbt, 320, 200, 1); + /// }); + /// # Ok(()) } + /// ``` + /// + /// [example]: https://github.com/attackgoat/vk-graph/blob/master/examples/ray_trace.rs + #[allow(clippy::too_many_arguments)] + #[profiling::function] + pub fn trace_rays( + &self, + raygen_shader_binding_table: &vk::StridedDeviceAddressRegionKHR, + miss_shader_binding_table: &vk::StridedDeviceAddressRegionKHR, + hit_shader_binding_table: &vk::StridedDeviceAddressRegionKHR, + callable_shader_binding_table: &vk::StridedDeviceAddressRegionKHR, + width: u32, + height: u32, + depth: u32, + ) -> &Self { + unsafe { + Device::expect_ray_trace_ext(self.device).cmd_trace_rays( + self.cmd_buf, + raygen_shader_binding_table, + miss_shader_binding_table, + hit_shader_binding_table, + callable_shader_binding_table, + width, + height, + depth, + ); + } + + self + } + + /// Ray traces using the currently-bound [`RayTracePipeline`] and the given shader binding + /// tables. + /// + /// `indirect_device_address` is a [buffer device address] which is a pointer to a + /// [`vk::TraceRaysIndirectCommandKHR`] structure containing the trace ray parameters. + /// + /// See [`vkCmdTraceRaysIndirectKHR`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCmdTraceRaysIndirectKHR.html). + /// + /// [buffer device address]: Buffer::device_address + #[profiling::function] + pub fn trace_rays_indirect( + &self, + raygen_shader_binding_table: &vk::StridedDeviceAddressRegionKHR, + miss_shader_binding_table: &vk::StridedDeviceAddressRegionKHR, + hit_shader_binding_table: &vk::StridedDeviceAddressRegionKHR, + callable_shader_binding_table: &vk::StridedDeviceAddressRegionKHR, + indirect_device_address: vk::DeviceAddress, + ) -> &Self { + unsafe { + Device::expect_ray_trace_ext(self.device).cmd_trace_rays_indirect( + self.cmd_buf, + raygen_shader_binding_table, + miss_shader_binding_table, + hit_shader_binding_table, + callable_shader_binding_table, + indirect_device_address, + ) + } + + self + } +} diff --git a/src/cmd_ref/view.rs b/src/cmd_ref/view.rs new file mode 100644 index 00000000..a8ac9440 --- /dev/null +++ b/src/cmd_ref/view.rs @@ -0,0 +1,124 @@ +use { + super::{ + AccelerationStructureLeaseNode, AccelerationStructureNode, AnyAccelerationStructureNode, + AnyBufferNode, AnyImageNode, BufferLeaseNode, BufferNode, BufferSubresourceRange, + ImageLeaseNode, ImageNode, ImageViewInfo, Node, Subresource, SwapchainImageNode, vk, + }, + std::ops::Range, +}; + +/// Allows for a resource to be reinterpreted as differently formatted data. +pub trait View: Node +where + Self::Info: Copy, + Self::Subresource: Into, +{ + /// The Info about the resource interpretation. + type Info; + + /// The portion of the resource which is bound. + type Subresource; +} + +impl View for AccelerationStructureNode { + type Info = (); + type Subresource = (); +} + +impl View for AccelerationStructureLeaseNode { + type Info = (); + type Subresource = (); +} + +impl View for AnyAccelerationStructureNode { + type Info = (); + type Subresource = (); +} + +impl View for AnyBufferNode { + type Info = BufferSubresourceRange; + type Subresource = BufferSubresourceRange; +} + +impl View for AnyImageNode { + type Info = ImageViewInfo; + type Subresource = vk::ImageSubresourceRange; +} + +impl View for BufferLeaseNode { + type Info = BufferSubresourceRange; + type Subresource = BufferSubresourceRange; +} + +impl View for BufferNode { + type Info = BufferSubresourceRange; + type Subresource = BufferSubresourceRange; +} + +impl View for ImageLeaseNode { + type Info = ImageViewInfo; + type Subresource = vk::ImageSubresourceRange; +} + +impl View for ImageNode { + type Info = ImageViewInfo; + type Subresource = vk::ImageSubresourceRange; +} + +impl View for SwapchainImageNode { + type Info = ImageViewInfo; + type Subresource = vk::ImageSubresourceRange; +} + +/// Describes the interpretation of a resource. +#[derive(Debug)] +pub enum ViewType { + /// Acceleration structures are not reinterpreted. + AccelerationStructure, + + /// Images may be interpreted as differently formatted images. + Image(ImageViewInfo), + + /// Buffers may be interpreted as subregions of the same buffer. + Buffer(Range), +} + +impl ViewType { + pub(crate) fn as_buffer(&self) -> Option<&Range> { + match self { + Self::Buffer(view_info) => Some(view_info), + _ => None, + } + } + + pub(crate) fn as_image(&self) -> Option<&ImageViewInfo> { + match self { + Self::Image(view_info) => Some(view_info), + _ => None, + } + } +} + +impl From<()> for ViewType { + fn from(_: ()) -> Self { + Self::AccelerationStructure + } +} + +impl From for ViewType { + fn from(subresource: BufferSubresourceRange) -> Self { + Self::Buffer(subresource.start..subresource.end) + } +} + +impl From for ViewType { + fn from(info: ImageViewInfo) -> Self { + Self::Image(info) + } +} + +impl From> for ViewType { + fn from(range: Range) -> Self { + Self::Buffer(range) + } +} diff --git a/src/display.rs b/src/display.rs index c87cbb1e..59c9984d 100644 --- a/src/display.rs +++ b/src/display.rs @@ -2,7 +2,7 @@ use { super::{ - RenderGraph, + Graph, driver::{ CommandBuffer, CommandBufferInfo, DescriptorPool, DescriptorPoolInfo, DriverError, RenderPass, RenderPassInfo, @@ -148,7 +148,7 @@ impl Display { pub fn present_image( &mut self, pool: &mut impl ResolverPool, - render_graph: RenderGraph, + render_graph: Graph, swapchain_image: SwapchainImageNode, queue_index: u32, ) -> Result<(), DisplayError> { diff --git a/src/edge.rs b/src/edge.rs index 69e17fe9..8487ec00 100644 --- a/src/edge.rs +++ b/src/edge.rs @@ -1,8 +1,8 @@ use { super::{ AccelerationStructureLeaseNode, AccelerationStructureNode, BufferLeaseNode, BufferNode, - ImageLeaseNode, ImageNode, RenderGraph, Resolver, SwapchainImageNode, - pass_ref::{PassRef, PipelinePassRef}, + Graph, ImageLeaseNode, ImageNode, Resolver, SwapchainImageNode, + cmd_ref::{CommandRef, PipelineCommandRef}, }, crate::{ driver::{ @@ -24,14 +24,14 @@ pub trait Edge { macro_rules! graph_edge { ($src:ty => $dst:ty) => { - impl Edge for $src { + impl Edge for $src { type Result = $dst; } }; } // Edges that can be bound as nodes to the render graph: -// Ex: RenderGraph::bind_node(&mut self, binding: X) -> Y +// Ex: Graph::bind_node(&mut self, binding: X) -> Y graph_edge!(AccelerationStructure => AccelerationStructureNode); graph_edge!(Arc => AccelerationStructureNode); graph_edge!(Lease => AccelerationStructureLeaseNode); @@ -47,7 +47,7 @@ graph_edge!(Arc> => ImageLeaseNode); graph_edge!(SwapchainImage => SwapchainImageNode); // Edges that can be unbound from the render graph: -// Ex: RenderGraph::unbind_node(&mut self, node: X) -> Y +// Ex: Graph::unbind_node(&mut self, node: X) -> Y graph_edge!(AccelerationStructureNode => Arc); graph_edge!(AccelerationStructureLeaseNode => Arc>); graph_edge!(BufferNode => Arc); @@ -58,7 +58,7 @@ graph_edge!(SwapchainImageNode => SwapchainImage); macro_rules! graph_edge_borrow { ($src:ty => $dst:ty) => { - impl<'a> Edge for &'a $src { + impl<'a> Edge for &'a $src { type Result = $dst; } }; @@ -72,20 +72,20 @@ graph_edge_borrow!(Arc => ImageNode); graph_edge_borrow!(Arc> => ImageLeaseNode); // Specialized edges for pipelines added to a pass: -// Ex: PassRef::bind_pipeline(&mut self, pipeline: X) -> PipelinePassRef +// Ex: PassRef::bind_pipeline(&mut self, pipeline: X) -> PipelineCommandRef macro_rules! pipeline_edge { ($name:ident) => { paste::paste! { - impl<'a> Edge> for &'a Arc<[<$name Pipeline>]> { - type Result = PipelinePassRef<'a, [<$name Pipeline>]>; + impl<'a> Edge> for &'a Arc<[<$name Pipeline>]> { + type Result = PipelineCommandRef<'a, [<$name Pipeline>]>; } - impl<'a> Edge> for Arc<[<$name Pipeline>]> { - type Result = PipelinePassRef<'a, [<$name Pipeline>]>; + impl<'a> Edge> for Arc<[<$name Pipeline>]> { + type Result = PipelineCommandRef<'a, [<$name Pipeline>]>; } - impl<'a> Edge> for [<$name Pipeline>] { - type Result = PipelinePassRef<'a, [<$name Pipeline>]>; + impl<'a> Edge> for [<$name Pipeline>] { + type Result = PipelineCommandRef<'a, [<$name Pipeline>]>; } } }; diff --git a/src/info.rs b/src/info.rs index 834337f2..8e9e6643 100644 --- a/src/info.rs +++ b/src/info.rs @@ -1,37 +1,37 @@ use { super::{ - AccelerationStructureLeaseNode, AccelerationStructureNode, BufferLeaseNode, BufferNode, - ImageLeaseNode, ImageNode, RenderGraph, SwapchainImageNode, + AccelerationStructureLeaseNode, AccelerationStructureNode, Binding, BufferLeaseNode, + BufferNode, ImageLeaseNode, ImageNode, SwapchainImageNode, }, crate::driver::{ accel_struct::AccelerationStructureInfo, buffer::BufferInfo, image::ImageInfo, }, }; -pub trait Information { +pub trait Info { type Info; - fn get(self, graph: &RenderGraph) -> Self::Info; + fn info(self, bindings: &[Binding]) -> Self::Info; } -macro_rules! information { +macro_rules! info { ($name:ident: $src:ident -> $dst:ident) => { paste::paste! { - impl Information for $src { + impl Info for $src { type Info = $dst; - fn get(self, graph: &RenderGraph) -> $dst { - graph.bindings[self.idx].[]().unwrap().info + fn info(self, bindings: &[Binding]) -> $dst { + bindings[self.idx].[]().unwrap().info } } } }; } -information!(acceleration_structure: AccelerationStructureNode -> AccelerationStructureInfo); -information!(acceleration_structure_lease: AccelerationStructureLeaseNode -> AccelerationStructureInfo); -information!(buffer: BufferNode -> BufferInfo); -information!(buffer_lease: BufferLeaseNode -> BufferInfo); -information!(image: ImageNode -> ImageInfo); -information!(image_lease: ImageLeaseNode -> ImageInfo); -information!(swapchain_image: SwapchainImageNode -> ImageInfo); +info!(acceleration_structure: AccelerationStructureNode -> AccelerationStructureInfo); +info!(acceleration_structure_lease: AccelerationStructureLeaseNode -> AccelerationStructureInfo); +info!(buffer: BufferNode -> BufferInfo); +info!(buffer_lease: BufferLeaseNode -> BufferInfo); +info!(image: ImageNode -> ImageInfo); +info!(image_lease: ImageLeaseNode -> ImageInfo); +info!(swapchain_image: SwapchainImageNode -> ImageInfo); diff --git a/src/lib.rs b/src/lib.rs index 49599291..090382b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -132,7 +132,7 @@ let my_image = pool.lease(info)?; # Render Graph Operations -All rendering in _vk-graph_ is performed using a [`RenderGraph`] composed of user-specified passes, +All rendering in _vk-graph_ is performed using a [`Graph`] composed of user-specified passes, which may include pipelines and read/write access to resources. Recorded passes are automatically optimized before submission to the graphics hardware. @@ -158,7 +158,7 @@ it as a node. Bound nodes may only be used with the graphs they were bound to. N # use vk_graph::driver::device::{Device, DeviceInfo}; # use vk_graph::driver::buffer::{Buffer, BufferInfo}; # use vk_graph::driver::image::{Image, ImageInfo}; -# use vk_graph::RenderGraph; +# use vk_graph::Graph; # use vk_graph::pool::{Pool}; # use vk_graph::pool::lazy::{LazyPool}; # fn main() -> Result<(), DriverError> { @@ -167,7 +167,7 @@ it as a node. Bound nodes may only be used with the graphs they were bound to. N # let buffer = Buffer::create(&device, info)?; # let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::STORAGE); # let image = Image::create(&device, info)?; -# let mut graph = RenderGraph::default(); +# let mut graph = Graph::default(); println!("{:?}", buffer); // Buffer println!("{:?}", image); // Image @@ -210,7 +210,7 @@ Example: # use vk_graph::driver::device::{Device, DeviceInfo}; # use vk_graph::driver::buffer::{Buffer, BufferInfo}; # use vk_graph::driver::image::{Image, ImageInfo}; -# use vk_graph::RenderGraph; +# use vk_graph::Graph; # use vk_graph::pool::{Pool}; # use vk_graph::pool::lazy::{LazyPool}; # fn main() -> Result<(), DriverError> { @@ -219,11 +219,11 @@ Example: # let buffer = Buffer::create(&device, info)?; # let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::STORAGE); # let image = Image::create(&device, info)?; -let mut graph = RenderGraph::default(); +let mut graph = Graph::default(); let buffer_node = graph.bind_node(buffer); let image_node = graph.bind_node(image); graph - .begin_cmd_buf().with_name("Do some raw Vulkan or interop with another Vulkan library") + .begin_cmd().with_name("Do some raw Vulkan or interop with another Vulkan library") .record_cmd_buf(move |device, cmd_buf, bindings| unsafe { // I always run first! }) @@ -252,18 +252,18 @@ Pipeline instances may be bound to a [`PassRef`] in order to execute the associa # use vk_graph::driver::device::{Device, DeviceInfo}; # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; # use vk_graph::driver::shader::{Shader}; -# use vk_graph::RenderGraph; +# use vk_graph::Graph; # fn main() -> Result<(), DriverError> { # let device = Arc::new(Device::new(DeviceInfo::default())?); # let my_shader_code = [0u8; 1]; # let info = ComputePipelineInfo::default(); # let shader = Shader::new_compute(my_shader_code.as_slice()); # let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); -# let mut graph = RenderGraph::default(); +# let mut graph = Graph::default(); graph - .begin_cmd_buf().with_name("My compute pass") + .begin_cmd().with_name("My compute pass") .bind_pipeline(&my_compute_pipeline) - .record_compute(|compute, _| { + .record_pipeline(|compute, _| { compute.push_constants(&42u32.to_ne_bytes()) .dispatch(128, 1, 1); }); @@ -326,7 +326,7 @@ layout. [`PassRef`]: graph::pass_ref::PassRef [`RayTracePipeline::create`]: driver::ray_trace::RayTracePipeline::create [`RayTracePipelineInfo`]: driver::ray_trace::RayTracePipelineInfo -[`RenderGraph`]: graph::RenderGraph +[`Graph`]: graph::Graph [`ShaderBuilder::image_sampler`]: driver::shader::ShaderBuilder::image_sampler [`ShaderBuilder::vertex_input`]: driver::shader::ShaderBuilder::vertex_input @@ -337,32 +337,33 @@ layout. pub mod display; pub mod driver; pub mod node; -pub mod pass_ref; pub mod pool; -mod binding; +mod bind; +mod cmd_ref; mod edge; mod info; mod resolver; mod swapchain; pub use self::{ - binding::{Bind, Unbind}, + bind::{Bind, Unbind}, + cmd_ref::CommandRef, resolver::Resolver, }; use { self::{ - binding::Binding, + bind::Binding, + cmd_ref::{AttachmentIndex, Bindings, Descriptor, SubresourceAccess, ViewType}, edge::Edge, - info::Information, + info::Info, node::Node, node::{ AccelerationStructureLeaseNode, AccelerationStructureNode, AnyAccelerationStructureNode, AnyBufferNode, AnyImageNode, BufferLeaseNode, BufferNode, ImageLeaseNode, ImageNode, SwapchainImageNode, }, - pass_ref::{AttachmentIndex, Bindings, Descriptor, PassRef, SubresourceAccess, ViewType}, }, crate::driver::{ DescriptorBindingMap, @@ -608,12 +609,12 @@ impl ExecutionPipeline { } #[derive(Debug)] -struct Pass { +struct Command { execs: Vec, name: Option, } -impl Pass { +impl Command { fn descriptor_pools_sizes( &self, ) -> impl Iterator>> { @@ -630,28 +631,28 @@ impl Pass { /// A composable graph of render pass operations. /// -/// `RenderGraph` instances are are intended for one-time use. +/// `Graph` instances are are intended for one-time use. /// /// The design of this code originated with a combination of /// [`PassBuilder`](https://github.com/EmbarkStudios/kajiya/blob/main/crates/lib/kajiya-rg/src/pass_builder.rs) /// and /// [`render_graph.cpp`](https://github.com/Themaister/Granite/blob/master/renderer/render_graph.cpp). #[derive(Debug, Default)] -pub struct RenderGraph { +pub struct Graph { bindings: Vec, - passes: Vec, + cmds: Vec, } -impl RenderGraph { - /// Constructs a new `RenderGraph`. +impl Graph { + /// Constructs a new `Graph`. #[deprecated = "use default function instead"] pub fn new() -> Self { Default::default() } - /// Begins a new pass. - pub fn begin_cmd_buf(&mut self) -> PassRef<'_> { - PassRef::new(self) + /// Allocates and begins writing a new command. + pub fn begin_cmd(&mut self) -> CommandRef<'_> { + CommandRef::new(self) } /// Binds a Vulkan acceleration structure, buffer, or image to this graph. @@ -738,7 +739,7 @@ impl RenderGraph { let src_node = src_node.into(); let dst_node = dst_node.into(); - let mut pass = self.begin_cmd_buf().with_name("blit image"); + let mut pass = self.begin_cmd().with_name("blit image"); for region in regions.as_ref() { pass = pass @@ -770,7 +771,7 @@ impl RenderGraph { ); } }) - .end_cmd_buf() + .end_cmd() } /// Clear a color image. @@ -785,7 +786,7 @@ impl RenderGraph { let image_info = self.node_info(image_node); let image_view_info = image_info.default_view_info(); - self.begin_cmd_buf() + self.begin_cmd() .with_name("clear color") .access_node_subrange(image_node, AccessType::TransferWrite, image_view_info) .record_cmd_buf(move |device, cmd_buf, bindings| unsafe { @@ -799,7 +800,7 @@ impl RenderGraph { &[image_view_info.into()], ); }) - .end_cmd_buf() + .end_cmd() } /// Clears a depth/stencil image. @@ -814,7 +815,7 @@ impl RenderGraph { let image_info = self.node_info(image_node); let image_view_info = image_info.default_view_info(); - self.begin_cmd_buf() + self.begin_cmd() .with_name("clear depth/stencil") .access_node_subrange(image_node, AccessType::TransferWrite, image_view_info) .record_cmd_buf(move |device, cmd_buf, bindings| unsafe { @@ -826,7 +827,7 @@ impl RenderGraph { &[image_view_info.into()], ); }) - .end_cmd_buf() + .end_cmd() } /// Copy data between buffers @@ -875,7 +876,7 @@ impl RenderGraph { #[cfg(debug_assertions)] let (src_size, dst_size) = (self.node_info(src_node).size, self.node_info(dst_node).size); - let mut pass = self.begin_cmd_buf().with_name("copy buffer"); + let mut pass = self.begin_cmd().with_name("copy buffer"); for region in regions.as_ref() { #[cfg(debug_assertions)] @@ -913,7 +914,7 @@ impl RenderGraph { device.cmd_copy_buffer(cmd_buf, src_buf, dst_buf, regions.as_ref()); } }) - .end_cmd_buf() + .end_cmd() } /// Copy data from a buffer into an image. @@ -970,7 +971,7 @@ impl RenderGraph { let dst_node = dst_node.into(); let dst_info = self.node_info(dst_node); - let mut pass = self.begin_cmd_buf().with_name("copy buffer to image"); + let mut pass = self.begin_cmd().with_name("copy buffer to image"); for region in regions.as_ref() { let block_bytes_size = format_texel_block_size(dst_info.fmt); @@ -1006,7 +1007,7 @@ impl RenderGraph { ); } }) - .end_cmd_buf() + .end_cmd() } /// Copy all layers of a source image to a destination image. @@ -1069,7 +1070,7 @@ impl RenderGraph { let src_node = src_node.into(); let dst_node = dst_node.into(); - let mut pass = self.begin_cmd_buf().with_name("copy image"); + let mut pass = self.begin_cmd().with_name("copy image"); for region in regions.as_ref() { pass = pass @@ -1100,7 +1101,7 @@ impl RenderGraph { ); } }) - .end_cmd_buf() + .end_cmd() } /// Copy image data into a buffer. @@ -1159,7 +1160,7 @@ impl RenderGraph { let src_info = self.node_info(src_node); let dst_node = dst_node.into(); - let mut pass = self.begin_cmd_buf().with_name("copy image to buffer"); + let mut pass = self.begin_cmd().with_name("copy image to buffer"); for region in regions.as_ref() { let block_bytes_size = format_texel_block_size(src_info.fmt); @@ -1195,7 +1196,7 @@ impl RenderGraph { ); } }) - .end_cmd_buf() + .end_cmd() } /// Fill a region of a buffer with a fixed value. @@ -1217,7 +1218,7 @@ impl RenderGraph { ) -> &mut Self { let buffer_node = buffer_node.into(); - self.begin_cmd_buf() + self.begin_cmd() .with_name("fill buffer") .access_node_subrange(buffer_node, AccessType::TransferWrite, region.clone()) .record_cmd_buf(move |device, cmd_buf, bindings| { @@ -1233,7 +1234,7 @@ impl RenderGraph { ); } }) - .end_cmd_buf() + .end_cmd() } /// Returns the index of the first pass which accesses a given node @@ -1241,7 +1242,7 @@ impl RenderGraph { fn first_node_access_pass_index(&self, node: impl Node) -> Option { let node_idx = node.index(); - for (pass_idx, pass) in self.passes.iter().enumerate() { + for (pass_idx, pass) in self.cmds.iter().enumerate() { for exec in pass.execs.iter() { if exec.accesses.contains_key(&node_idx) { return Some(pass_idx); @@ -1268,11 +1269,11 @@ impl RenderGraph { } /// Returns information used to crate a node. - pub fn node_info(&self, node: N) -> ::Info + pub fn node_info(&self, node: N) -> ::Info where - N: Information, + N: Info, { - node.get(self) + node.info(&self.bindings) } /// Finalizes the graph and provides an object with functions for submitting the resulting @@ -1280,7 +1281,7 @@ impl RenderGraph { #[profiling::function] pub fn resolve(mut self) -> Resolver { // The final execution of each pass has no function - for pass in &mut self.passes { + for pass in &mut self.cmds { pass.execs.pop(); } @@ -1320,7 +1321,7 @@ impl RenderGraph { ); } - self.begin_cmd_buf() + self.begin_cmd() .with_name("update buffer") .access_node_subrange(buffer_node, AccessType::TransferWrite, offset..data_end) .record_cmd_buf(move |device, cmd_buf, bindings| { @@ -1330,6 +1331,6 @@ impl RenderGraph { device.cmd_update_buffer(cmd_buf, buffer, offset, data.as_ref()); } }) - .end_cmd_buf() + .end_cmd() } } diff --git a/src/node.rs b/src/node.rs index fcf8b269..79547151 100644 --- a/src/node.rs +++ b/src/node.rs @@ -1,7 +1,7 @@ //! Bindings for Vulkan smart-pointer resources. use { - super::{Information, NodeIndex, RenderGraph, Unbind}, + super::{Binding, Graph, Info, NodeIndex, Unbind}, crate::{ driver::{ accel_struct::{AccelerationStructure, AccelerationStructureInfo}, @@ -32,13 +32,13 @@ impl Clone for AnyAccelerationStructureNode { impl Copy for AnyAccelerationStructureNode {} -impl Information for AnyAccelerationStructureNode { +impl Info for AnyAccelerationStructureNode { type Info = AccelerationStructureInfo; - fn get(self, graph: &RenderGraph) -> Self::Info { + fn info(self, bindings: &[Binding]) -> Self::Info { match self { - Self::AccelerationStructure(node) => node.get(graph), - Self::AccelerationStructureLease(node) => node.get(graph), + Self::AccelerationStructure(node) => node.info(bindings), + Self::AccelerationStructureLease(node) => node.info(bindings), } } } @@ -82,13 +82,13 @@ impl Clone for AnyBufferNode { impl Copy for AnyBufferNode {} -impl Information for AnyBufferNode { +impl Info for AnyBufferNode { type Info = BufferInfo; - fn get(self, graph: &RenderGraph) -> Self::Info { + fn info(self, bindings: &[Binding]) -> Self::Info { match self { - Self::Buffer(node) => node.get(graph), - Self::BufferLease(node) => node.get(graph), + Self::Buffer(node) => node.info(bindings), + Self::BufferLease(node) => node.info(bindings), } } } @@ -137,14 +137,14 @@ impl Clone for AnyImageNode { impl Copy for AnyImageNode {} -impl Information for AnyImageNode { +impl Info for AnyImageNode { type Info = ImageInfo; - fn get(self, graph: &RenderGraph) -> Self::Info { + fn info(self, bindings: &[Binding]) -> Self::Info { match self { - Self::Image(node) => node.get(graph), - Self::ImageLease(node) => node.get(graph), - Self::SwapchainImage(node) => node.get(graph), + Self::Image(node) => node.info(bindings), + Self::ImageLease(node) => node.info(bindings), + Self::SwapchainImage(node) => node.info(bindings), } } } @@ -177,7 +177,7 @@ impl Node for AnyImageNode { } } -/// A Vulkan resource which has been bound to a [`RenderGraph`] using [`RenderGraph::bind_node`]. +/// A Vulkan resource which has been bound to a [`Graph`] using [`Graph::bind_node`]. pub trait Node: Copy { /// The internal node index of this bound resource. fn index(self) -> NodeIndex; @@ -228,8 +228,8 @@ node!(SwapchainImage); macro_rules! node_unbind { ($name:ident) => { paste::paste! { - impl Unbind> for [<$name Node>] { - fn unbind(self, graph: &mut RenderGraph) -> Arc<$name> { + impl Unbind> for [<$name Node>] { + fn unbind(self, graph: &mut Graph) -> Arc<$name> { let binding = &mut graph.bindings[self.idx]; let res = Arc::clone( binding @@ -252,8 +252,8 @@ node_unbind!(Image); macro_rules! node_unbind_lease { ($name:ident) => { paste::paste! { - impl Unbind>> for [<$name LeaseNode>] { - fn unbind(self, graph: &mut RenderGraph) -> Arc> { + impl Unbind>> for [<$name LeaseNode>] { + fn unbind(self, graph: &mut Graph) -> Arc> { let binding = &mut graph.bindings[self.idx]; let res = Arc::clone( binding diff --git a/src/pass_ref.rs b/src/pass_ref.rs deleted file mode 100644 index d9a1c179..00000000 --- a/src/pass_ref.rs +++ /dev/null @@ -1,5185 +0,0 @@ -//! Strongly-typed rendering commands. - -use { - super::{ - AccelerationStructureLeaseNode, AccelerationStructureNode, AnyAccelerationStructureNode, - AnyBufferNode, AnyImageNode, Area, Attachment, Bind, Binding, BufferLeaseNode, BufferNode, - ClearColorValue, Edge, Execution, ExecutionFunction, ExecutionPipeline, ImageLeaseNode, - ImageNode, Information, Node, NodeIndex, Pass, RenderGraph, SampleCount, - SwapchainImageNode, - }, - crate::driver::{ - accel_struct::{ - AccelerationStructure, AccelerationStructureGeometry, - AccelerationStructureGeometryInfo, DeviceOrHostAddress, - }, - buffer::{Buffer, BufferSubresourceRange}, - compute::ComputePipeline, - device::Device, - graphic::{DepthStencilMode, GraphicPipeline}, - image::{ - Image, ImageViewInfo, image_subresource_range_contains, - image_subresource_range_intersects, - }, - ray_trace::RayTracePipeline, - render_pass::ResolveMode, - }, - ash::vk, - log::trace, - std::{ - cell::RefCell, - marker::PhantomData, - ops::{Index, Range}, - sync::Arc, - }, - vk_sync::AccessType, -}; - -/// Alias for the index of a framebuffer attachment. -pub type AttachmentIndex = u32; - -/// Alias for the binding index of a shader descriptor. -pub type BindingIndex = u32; - -/// Alias for the binding offset of a shader descriptor array element. -pub type BindingOffset = u32; - -/// Alias for the descriptor set index of a shader descriptor. -pub type DescriptorSetIndex = u32; - -/// Recording interface for acceleration structure commands. -/// -/// This structure provides a strongly-typed set of methods which allow acceleration structures to -/// be built and updated. An instance of `Acceleration` is provided to the closure parameter of -/// [`PassRef::record_acceleration`]. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```no_run -/// # use std::sync::Arc; -/// # use ash::vk; -/// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; -/// # use vk_graph::driver::DriverError; -/// # use vk_graph::driver::device::{Device, DeviceInfo}; -/// # use vk_graph::RenderGraph; -/// # use vk_graph::driver::shader::Shader; -/// # fn main() -> Result<(), DriverError> { -/// # let device = Arc::new(Device::new(DeviceInfo::default())?); -/// # let mut my_graph = RenderGraph::default(); -/// # let info = AccelerationStructureInfo::blas(1); -/// my_graph.begin_cmd_buf().with_name("my acceleration pass") -/// .record_acceleration(move |acceleration, bindings| { -/// // During this closure we have access to the acceleration methods! -/// }); -/// # Ok(()) } -/// ``` -pub struct Acceleration<'a> { - bindings: Bindings<'a>, - cmd_buf: vk::CommandBuffer, - device: &'a Device, -} - -impl Acceleration<'_> { - /// Build an acceleration structure. - /// - /// Requires a scratch buffer which was created with the following requirements: - /// - /// - Flags must include [`vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS`] - /// - Size must be equal to or greater than the `build_size` value returned by - /// [`AccelerationStructure::size_of`] aligned to `min_accel_struct_scratch_offset_alignment` - /// of - /// [`PhysicalDevice::accel_struct_properties`](crate::driver::physical_device::PhysicalDevice::accel_struct_properties). - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// # use std::sync::Arc; - /// # use ash::vk; - /// # use vk_graph::driver::DriverError; - /// # use vk_graph::driver::device::{Device, DeviceInfo}; - /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureGeometry, AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, AccelerationStructureInfo, DeviceOrHostAddress}; - /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; - /// # use vk_graph::RenderGraph; - /// # use vk_graph::driver::shader::Shader; - /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); - /// # let mut my_graph = RenderGraph::default(); - /// # let info = AccelerationStructureInfo::blas(1); - /// # let blas_accel_struct = AccelerationStructure::create(&device, info)?; - /// # let blas_node = my_graph.bind_node(blas_accel_struct); - /// # let scratch_buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS); - /// # let scratch_buf = Buffer::create(&device, scratch_buf_info)?; - /// # let scratch_buf = my_graph.bind_node(scratch_buf); - /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::INDEX_BUFFER); - /// # let my_idx_buf = Buffer::create(&device, buf_info)?; - /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::VERTEX_BUFFER); - /// # let my_vtx_buf = Buffer::create(&device, buf_info)?; - /// # let index_node = my_graph.bind_node(my_idx_buf); - /// # let vertex_node = my_graph.bind_node(my_vtx_buf); - /// my_graph.begin_cmd_buf().with_name("my acceleration pass") - /// .read_node(index_node) - /// .read_node(vertex_node) - /// .write_node(blas_node) - /// .write_node(scratch_buf) - /// .record_acceleration(move |acceleration, bindings| { - /// let geom = AccelerationStructureGeometry { - /// max_primitive_count: 64, - /// flags: vk::GeometryFlagsKHR::OPAQUE, - /// geometry: AccelerationStructureGeometryData::Triangles { - /// index_addr: DeviceOrHostAddress::DeviceAddress( - /// Buffer::device_address(&bindings[index_node]) - /// ), - /// index_type: vk::IndexType::UINT32, - /// max_vertex: 42, - /// transform_addr: None, - /// vertex_addr: DeviceOrHostAddress::DeviceAddress(Buffer::device_address( - /// &bindings[vertex_node], - /// )), - /// vertex_format: vk::Format::R32G32B32_SFLOAT, - /// vertex_stride: 12, - /// }, - /// }; - /// let build_range = vk::AccelerationStructureBuildRangeInfoKHR { - /// first_vertex: 0, - /// primitive_count: 1, - /// primitive_offset: 0, - /// transform_offset: 0, - /// }; - /// let info = AccelerationStructureGeometryInfo::blas([(geom, build_range)]); - /// - /// acceleration.build_structure(&info, blas_node, Buffer::device_address(&bindings[scratch_buf])); - /// }); - /// # Ok(()) } - /// ``` - pub fn build_structure( - &self, - info: &AccelerationStructureGeometryInfo<( - AccelerationStructureGeometry, - vk::AccelerationStructureBuildRangeInfoKHR, - )>, - accel_struct: impl Into, - scratch_addr: impl Into, - ) -> &Self { - #[derive(Default)] - struct Tls { - geometries: Vec>, - ranges: Vec, - } - - thread_local! { - static TLS: RefCell = Default::default(); - } - - let accel_struct = accel_struct.into(); - let scratch_addr = scratch_addr.into().into(); - - TLS.with_borrow_mut(|tls| { - tls.geometries.clear(); - tls.ranges.clear(); - - for (geometry, range) in info.geometries.iter() { - tls.geometries.push(geometry.into()); - tls.ranges.push(*range); - } - - unsafe { - Device::expect_accel_struct_ext(self.device).cmd_build_acceleration_structures( - self.cmd_buf, - &[vk::AccelerationStructureBuildGeometryInfoKHR::default() - .ty(info.ty) - .flags(info.flags) - .mode(vk::BuildAccelerationStructureModeKHR::BUILD) - .dst_acceleration_structure(self.bindings[accel_struct].handle) - .geometries(&tls.geometries) - .scratch_data(scratch_addr)], - &[&tls.ranges], - ); - } - }); - - self - } - - /// Build an acceleration structure with some parameters provided on the device. - /// - /// `range` is a buffer device address which points to `info.geometry.len()` - /// [vk::VkAccelerationStructureBuildRangeInfoKHR] structures defining dynamic offsets to the - /// addresses where geometry data is stored, as defined by `info`. - pub fn build_structure_indirect( - &self, - info: &AccelerationStructureGeometryInfo, - accel_struct: impl Into, - scratch_addr: impl Into, - range_base: vk::DeviceAddress, - range_stride: u32, - ) -> &Self { - #[derive(Default)] - struct Tls { - geometries: Vec>, - max_primitive_counts: Vec, - } - - thread_local! { - static TLS: RefCell = Default::default(); - } - - let accel_struct = accel_struct.into(); - let scratch_addr = scratch_addr.into().into(); - - TLS.with_borrow_mut(|tls| { - tls.geometries.clear(); - tls.max_primitive_counts.clear(); - - for geometry in info.geometries.iter() { - tls.geometries.push(geometry.into()); - tls.max_primitive_counts.push(geometry.max_primitive_count); - } - - unsafe { - Device::expect_accel_struct_ext(self.device) - .cmd_build_acceleration_structures_indirect( - self.cmd_buf, - &[vk::AccelerationStructureBuildGeometryInfoKHR::default() - .ty(info.ty) - .flags(info.flags) - .mode(vk::BuildAccelerationStructureModeKHR::BUILD) - .dst_acceleration_structure(self.bindings[accel_struct].handle) - .geometries(&tls.geometries) - .scratch_data(scratch_addr)], - &[range_base], - &[range_stride], - &[&tls.max_primitive_counts], - ); - } - }); - - self - } - - /// Build acceleration structures. - /// - /// There is no ordering or synchronization implied between any of the individual acceleration - /// structure builds. - pub fn build_structures(&self, infos: &[AccelerationStructureBuildInfo]) -> &Self { - #[derive(Default)] - struct Tls { - geometries: Vec>, - ranges: Vec, - } - - thread_local! { - static TLS: RefCell = Default::default(); - } - - TLS.with_borrow_mut(|tls| { - tls.geometries.clear(); - tls.geometries.extend(infos.iter().flat_map(|info| { - info.build_data.geometries.iter().map(|(geometry, _)| { - <&AccelerationStructureGeometry as Into< - vk::AccelerationStructureGeometryKHR, - >>::into(geometry) - }) - })); - - tls.ranges.clear(); - tls.ranges.extend( - infos - .iter() - .flat_map(|info| info.build_data.geometries.iter().map(|(_, range)| *range)), - ); - - let vk_ranges = { - let mut start = 0; - let mut vk_ranges = Vec::with_capacity(infos.len()); - for info in infos { - let end = start + info.build_data.geometries.len(); - vk_ranges.push(&tls.ranges[start..end]); - start = end; - } - - vk_ranges - }; - - let vk_infos = { - let mut start = 0; - let mut vk_infos = Vec::with_capacity(infos.len()); - for info in infos { - let end = start + info.build_data.geometries.len(); - vk_infos.push( - vk::AccelerationStructureBuildGeometryInfoKHR::default() - .ty(info.build_data.ty) - .flags(info.build_data.flags) - .mode(vk::BuildAccelerationStructureModeKHR::BUILD) - .dst_acceleration_structure(self.bindings[info.accel_struct].handle) - .geometries(&tls.geometries[start..end]) - .scratch_data(info.scratch_addr.into()), - ); - start = end; - } - - vk_infos - }; - - unsafe { - Device::expect_accel_struct_ext(self.device).cmd_build_acceleration_structures( - self.cmd_buf, - &vk_infos, - &vk_ranges, - ); - } - }); - - self - } - - /// Builds acceleration structures with some parameters provided on the device. - /// - /// There is no ordering or synchronization implied between any of the individual acceleration - /// structure builds. - /// - /// See [Self::build_structure_indirect] - pub fn build_structures_indirect( - &self, - infos: &[AccelerationStructureIndirectBuildInfo], - ) -> &Self { - #[derive(Default)] - struct Tls { - geometries: Vec>, - max_primitive_counts: Vec, - range_bases: Vec, - range_strides: Vec, - } - - thread_local! { - static TLS: RefCell = Default::default(); - } - - TLS.with_borrow_mut(|tls| { - tls.geometries.clear(); - tls.geometries.extend(infos.iter().flat_map(|info| { - info.build_data.geometries.iter().map( - <&AccelerationStructureGeometry as Into< - vk::AccelerationStructureGeometryKHR, - >>::into, - ) - })); - - tls.max_primitive_counts.clear(); - tls.max_primitive_counts - .extend(infos.iter().flat_map(|info| { - info.build_data - .geometries - .iter() - .map(|geometry| geometry.max_primitive_count) - })); - - tls.range_bases.clear(); - tls.range_strides.clear(); - let (vk_infos, vk_max_primitive_counts) = { - let mut start = 0; - let mut vk_infos = Vec::with_capacity(infos.len()); - let mut vk_max_primitive_counts = Vec::with_capacity(infos.len()); - for info in infos { - let end = start + info.build_data.geometries.len(); - vk_infos.push( - vk::AccelerationStructureBuildGeometryInfoKHR::default() - .ty(info.build_data.ty) - .flags(info.build_data.flags) - .mode(vk::BuildAccelerationStructureModeKHR::BUILD) - .dst_acceleration_structure(self.bindings[info.accel_struct].handle) - .geometries(&tls.geometries[start..end]) - .scratch_data(info.scratch_data.into()), - ); - vk_max_primitive_counts.push(&tls.max_primitive_counts[start..end]); - start = end; - - tls.range_bases.push(info.range_base); - tls.range_strides.push(info.range_stride); - } - - (vk_infos, vk_max_primitive_counts) - }; - - unsafe { - Device::expect_accel_struct_ext(self.device) - .cmd_build_acceleration_structures_indirect( - self.cmd_buf, - &vk_infos, - &tls.range_bases, - &tls.range_strides, - &vk_max_primitive_counts, - ); - } - }); - - self - } - - /// Update an acceleration structure. - /// - /// Requires a scratch buffer which was created with the following requirements: - /// - /// - Flags must include [`vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS`] - /// - Size must be equal to or greater than the `update_size` value returned by - /// [`AccelerationStructure::size_of`] aligned to `min_accel_struct_scratch_offset_alignment` - /// of - /// [`PhysicalDevice::accel_struct_properties`](crate::driver::physical_device::PhysicalDevice::accel_struct_properties). - pub fn update_structure( - &self, - info: &AccelerationStructureGeometryInfo<( - AccelerationStructureGeometry, - vk::AccelerationStructureBuildRangeInfoKHR, - )>, - src_accel_struct: impl Into, - dst_accel_struct: impl Into, - scratch_addr: impl Into, - ) -> &Self { - #[derive(Default)] - struct Tls { - geometries: Vec>, - ranges: Vec, - } - - thread_local! { - static TLS: RefCell = Default::default(); - } - - let src_accel_struct = src_accel_struct.into(); - let dst_accel_struct = dst_accel_struct.into(); - let scratch_addr = scratch_addr.into().into(); - - TLS.with_borrow_mut(|tls| { - tls.geometries.clear(); - tls.ranges.clear(); - - for (geometry, range) in info.geometries.iter() { - tls.geometries.push(geometry.into()); - tls.ranges.push(*range); - } - - unsafe { - Device::expect_accel_struct_ext(self.device).cmd_build_acceleration_structures( - self.cmd_buf, - &[vk::AccelerationStructureBuildGeometryInfoKHR::default() - .ty(info.ty) - .flags(info.flags) - .mode(vk::BuildAccelerationStructureModeKHR::UPDATE) - .dst_acceleration_structure(self.bindings[dst_accel_struct].handle) - .src_acceleration_structure(self.bindings[src_accel_struct].handle) - .geometries(&tls.geometries) - .scratch_data(scratch_addr)], - &[&tls.ranges], - ); - } - }); - - self - } - - /// Update an acceleration structure with some parameters provided on the device. - /// - /// `range` is a buffer device address which points to `info.geometry.len()` - /// [vk::VkAccelerationStructureBuildRangeInfoKHR] structures defining dynamic offsets to the - /// addresses where geometry data is stored, as defined by `info`. - pub fn update_structure_indirect( - &self, - info: &AccelerationStructureGeometryInfo, - src_accel_struct: impl Into, - dst_accel_struct: impl Into, - scratch_addr: impl Into, - range_base: vk::DeviceAddress, - range_stride: u32, - ) -> &Self { - #[derive(Default)] - struct Tls { - geometries: Vec>, - max_primitive_counts: Vec, - } - - thread_local! { - static TLS: RefCell = Default::default(); - } - - let src_accel_struct = src_accel_struct.into(); - let dst_accel_struct = dst_accel_struct.into(); - let scratch_addr = scratch_addr.into().into(); - - TLS.with_borrow_mut(|tls| { - tls.geometries.clear(); - tls.max_primitive_counts.clear(); - - for geometry in info.geometries.iter() { - tls.geometries.push(geometry.into()); - tls.max_primitive_counts.push(geometry.max_primitive_count); - } - - unsafe { - Device::expect_accel_struct_ext(self.device) - .cmd_build_acceleration_structures_indirect( - self.cmd_buf, - &[vk::AccelerationStructureBuildGeometryInfoKHR::default() - .ty(info.ty) - .flags(info.flags) - .mode(vk::BuildAccelerationStructureModeKHR::UPDATE) - .src_acceleration_structure(self.bindings[src_accel_struct].handle) - .dst_acceleration_structure(self.bindings[dst_accel_struct].handle) - .geometries(&tls.geometries) - .scratch_data(scratch_addr)], - &[range_base], - &[range_stride], - &[&tls.max_primitive_counts], - ); - } - }); - - self - } - - /// Update acceleration structures. - /// - /// There is no ordering or synchronization implied between any of the individual acceleration - /// structure updates. - pub fn update_structures(&self, infos: &[AccelerationStructureUpdateInfo]) -> &Self { - #[derive(Default)] - struct Tls { - geometries: Vec>, - ranges: Vec, - } - - thread_local! { - static TLS: RefCell = Default::default(); - } - - TLS.with_borrow_mut(|tls| { - tls.geometries.clear(); - tls.geometries.extend(infos.iter().flat_map(|info| { - info.update_data.geometries.iter().map(|(geometry, _)| { - <&AccelerationStructureGeometry as Into< - vk::AccelerationStructureGeometryKHR, - >>::into(geometry) - }) - })); - - tls.ranges.clear(); - tls.ranges.extend( - infos - .iter() - .flat_map(|info| info.update_data.geometries.iter().map(|(_, range)| *range)), - ); - - let vk_ranges = { - let mut start = 0; - let mut vk_ranges = Vec::with_capacity(infos.len()); - for info in infos { - let end = start + info.update_data.geometries.len(); - vk_ranges.push(&tls.ranges[start..end]); - start = end; - } - - vk_ranges - }; - - let vk_infos = { - let mut start = 0; - let mut vk_infos = Vec::with_capacity(infos.len()); - for info in infos { - let end = start + info.update_data.geometries.len(); - vk_infos.push( - vk::AccelerationStructureBuildGeometryInfoKHR::default() - .ty(info.update_data.ty) - .flags(info.update_data.flags) - .mode(vk::BuildAccelerationStructureModeKHR::UPDATE) - .dst_acceleration_structure(self.bindings[info.dst_accel_struct].handle) - .src_acceleration_structure(self.bindings[info.src_accel_struct].handle) - .geometries(&tls.geometries[start..end]) - .scratch_data(info.scratch_addr.into()), - ); - start = end; - } - - vk_infos - }; - - unsafe { - Device::expect_accel_struct_ext(self.device).cmd_build_acceleration_structures( - self.cmd_buf, - &vk_infos, - &vk_ranges, - ); - } - }); - - self - } - - /// Updates acceleration structures with some parameters provided on the device. - /// - /// There is no ordering or synchronization implied between any of the individual acceleration - /// structure updates. - /// - /// See [Self::update_structure_indirect] - pub fn update_structures_indirect( - &self, - infos: &[AccelerationStructureIndirectUpdateInfo], - ) -> &Self { - #[derive(Default)] - struct Tls { - geometries: Vec>, - max_primitive_counts: Vec, - range_bases: Vec, - range_strides: Vec, - } - - thread_local! { - static TLS: RefCell = Default::default(); - } - - TLS.with_borrow_mut(|tls| { - tls.geometries.clear(); - tls.geometries.extend(infos.iter().flat_map(|info| { - info.update_data.geometries.iter().map( - <&AccelerationStructureGeometry as Into< - vk::AccelerationStructureGeometryKHR, - >>::into, - ) - })); - - tls.max_primitive_counts.clear(); - tls.max_primitive_counts - .extend(infos.iter().flat_map(|info| { - info.update_data - .geometries - .iter() - .map(|geometry| geometry.max_primitive_count) - })); - - tls.range_bases.clear(); - tls.range_strides.clear(); - let (vk_infos, vk_max_primitive_counts) = { - let mut start = 0; - let mut vk_infos = Vec::with_capacity(infos.len()); - let mut vk_max_primitive_counts = Vec::with_capacity(infos.len()); - for info in infos { - let end = start + info.update_data.geometries.len(); - vk_infos.push( - vk::AccelerationStructureBuildGeometryInfoKHR::default() - .ty(info.update_data.ty) - .flags(info.update_data.flags) - .mode(vk::BuildAccelerationStructureModeKHR::UPDATE) - .src_acceleration_structure(self.bindings[info.src_accel_struct].handle) - .dst_acceleration_structure(self.bindings[info.dst_accel_struct].handle) - .geometries(&tls.geometries[start..end]) - .scratch_data(info.scratch_addr.into()), - ); - vk_max_primitive_counts.push(&tls.max_primitive_counts[start..end]); - start = end; - - tls.range_bases.push(info.range_base); - tls.range_strides.push(info.range_stride); - } - - (vk_infos, vk_max_primitive_counts) - }; - - unsafe { - Device::expect_accel_struct_ext(self.device) - .cmd_build_acceleration_structures_indirect( - self.cmd_buf, - &vk_infos, - &tls.range_bases, - &tls.range_strides, - &vk_max_primitive_counts, - ); - } - }); - - self - } -} - -/// Specifies the information and data used to build an acceleration structure. -/// -/// See -/// [VkAccelerationStructureBuildGeometryInfoKHR](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkAccelerationStructureBuildGeometryInfoKHR.html) -/// for more information. -#[derive(Clone, Debug)] -pub struct AccelerationStructureBuildInfo { - /// The acceleration structure to be written. - pub accel_struct: AnyAccelerationStructureNode, - - /// Specifies the geometry data to use when building the acceleration structure. - pub build_data: AccelerationStructureGeometryInfo<( - AccelerationStructureGeometry, - vk::AccelerationStructureBuildRangeInfoKHR, - )>, - - /// The temporary buffer or host address (with enough capacity per - /// [AccelerationStructure::size_of]). - pub scratch_addr: DeviceOrHostAddress, -} - -impl AccelerationStructureBuildInfo { - /// Constructs new acceleration structure build information. - pub fn new( - accel_struct: impl Into, - build_data: AccelerationStructureGeometryInfo<( - AccelerationStructureGeometry, - vk::AccelerationStructureBuildRangeInfoKHR, - )>, - scratch_addr: impl Into, - ) -> Self { - let accel_struct = accel_struct.into(); - let scratch_addr = scratch_addr.into(); - - Self { - accel_struct, - build_data, - scratch_addr, - } - } -} - -/// Specifies the information and data used to build an acceleration structure with some parameters -/// sourced on the device. -/// -/// See -/// [VkAccelerationStructureBuildGeometryInfoKHR](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkAccelerationStructureBuildGeometryInfoKHR.html) -/// for more information. -#[derive(Clone, Debug)] -pub struct AccelerationStructureIndirectBuildInfo { - /// The acceleration structure to be written. - pub accel_struct: AnyAccelerationStructureNode, - - /// Specifies the geometry data to use when building the acceleration structure. - pub build_data: AccelerationStructureGeometryInfo, - - /// A buffer device addresses which points to `data.geometry.len()` - /// [vk::VkAccelerationStructureBuildRangeInfoKHR] structures defining dynamic offsets to the - /// addresses where geometry data is stored. - pub range_base: vk::DeviceAddress, - - /// Byte stride between elements of [range]. - pub range_stride: u32, - - /// The temporary buffer or host address (with enough capacity per - /// [AccelerationStructure::size_of]). - pub scratch_data: DeviceOrHostAddress, -} - -impl AccelerationStructureIndirectBuildInfo { - /// Constructs new acceleration structure indirect build information. - pub fn new( - accel_struct: impl Into, - build_data: AccelerationStructureGeometryInfo, - range_base: vk::DeviceAddress, - - range_stride: u32, - scratch_data: impl Into, - ) -> Self { - let accel_struct = accel_struct.into(); - let scratch_data = scratch_data.into(); - - Self { - accel_struct, - build_data, - range_base, - range_stride, - scratch_data, - } - } -} - -/// Specifies the information and data used to update an acceleration structure with some parameters -/// sourced on the device. -/// -/// See -/// [VkAccelerationStructureBuildGeometryInfoKHR](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkAccelerationStructureBuildGeometryInfoKHR.html) -/// for more information. -#[derive(Clone, Debug)] -pub struct AccelerationStructureIndirectUpdateInfo { - /// The acceleration structure to be written. - pub dst_accel_struct: AnyAccelerationStructureNode, - - /// A buffer device addresses which points to `data.geometry.len()` - /// [vk::VkAccelerationStructureBuildRangeInfoKHR] structures defining dynamic offsets to the - /// addresses where geometry data is stored. - pub range_base: vk::DeviceAddress, - - /// Byte stride between elements of [range]. - pub range_stride: u32, - - /// The temporary buffer or host address (with enough capacity per - /// [AccelerationStructure::size_of]). - pub scratch_addr: DeviceOrHostAddress, - - /// The source acceleration structure to be read. - pub src_accel_struct: AnyAccelerationStructureNode, - - /// Specifies the geometry data to use when building the acceleration structure. - pub update_data: AccelerationStructureGeometryInfo, -} - -impl AccelerationStructureIndirectUpdateInfo { - /// Constructs new acceleration structure indirect update information. - pub fn new( - src_accel_struct: impl Into, - dst_accel_struct: impl Into, - update_data: AccelerationStructureGeometryInfo, - range_base: vk::DeviceAddress, - - range_stride: u32, - scratch_addr: impl Into, - ) -> Self { - let src_accel_struct = src_accel_struct.into(); - let dst_accel_struct = dst_accel_struct.into(); - let scratch_addr = scratch_addr.into(); - - Self { - dst_accel_struct, - range_base, - range_stride, - scratch_addr, - src_accel_struct, - update_data, - } - } -} - -/// Specifies the information and data used to update an acceleration structure. -/// -/// See -/// [VkAccelerationStructureBuildGeometryInfoKHR](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkAccelerationStructureBuildGeometryInfoKHR.html) -/// for more information. -#[derive(Clone, Debug)] -pub struct AccelerationStructureUpdateInfo { - /// The acceleration structure to be written. - pub dst_accel_struct: AnyAccelerationStructureNode, - - /// The temporary buffer or host address (with enough capacity per - /// [AccelerationStructure::size_of]). - pub scratch_addr: DeviceOrHostAddress, - - /// The source acceleration structure to be read. - pub src_accel_struct: AnyAccelerationStructureNode, - - /// Specifies the geometry data to use when updating the acceleration structure. - pub update_data: AccelerationStructureGeometryInfo<( - AccelerationStructureGeometry, - vk::AccelerationStructureBuildRangeInfoKHR, - )>, -} - -impl AccelerationStructureUpdateInfo { - /// Constructs new acceleration structure update information. - pub fn new( - src_accel_struct: impl Into, - dst_accel_struct: impl Into, - update_data: AccelerationStructureGeometryInfo<( - AccelerationStructureGeometry, - vk::AccelerationStructureBuildRangeInfoKHR, - )>, - scratch_addr: impl Into, - ) -> Self { - let src_accel_struct = src_accel_struct.into(); - let dst_accel_struct = dst_accel_struct.into(); - let scratch_addr = scratch_addr.into(); - - Self { - dst_accel_struct, - scratch_addr, - src_accel_struct, - update_data, - } - } -} - -/// Associated type trait which enables default values for read and write methods. -pub trait Access { - /// The default `AccessType` for read operations, if not specified explicitly. - const DEFAULT_READ: AccessType; - - /// The default `AccessType` for write operations, if not specified explicitly. - const DEFAULT_WRITE: AccessType; -} - -impl Access for ComputePipeline { - const DEFAULT_READ: AccessType = AccessType::ComputeShaderReadOther; - const DEFAULT_WRITE: AccessType = AccessType::ComputeShaderWrite; -} - -impl Access for GraphicPipeline { - const DEFAULT_READ: AccessType = AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer; - const DEFAULT_WRITE: AccessType = AccessType::AnyShaderWrite; -} - -impl Access for RayTracePipeline { - const DEFAULT_READ: AccessType = - AccessType::RayTracingShaderReadSampledImageOrUniformTexelBuffer; - const DEFAULT_WRITE: AccessType = AccessType::AnyShaderWrite; -} - -macro_rules! bind { - ($name:ident) => { - paste::paste! { - impl<'a> Bind, PipelinePassRef<'a, [<$name Pipeline>]>> for &'a Arc<[<$name Pipeline>]> { - // TODO: Allow binding as explicit secondary command buffers? like with compute/raytrace stuff - fn bind(self, mut pass: PassRef<'a>) -> PipelinePassRef<'a, [<$name Pipeline>]> { - let pass_ref = pass.as_mut(); - if pass_ref.execs.last().unwrap().pipeline.is_some() { - // Binding from PipelinePass -> PipelinePass (changing shaders) - pass_ref.execs.push(Default::default()); - } - - pass_ref.execs.last_mut().unwrap().pipeline = Some(ExecutionPipeline::$name(Arc::clone(self))); - - PipelinePassRef { - __: PhantomData, - pass, - } - } - } - - impl<'a> Bind, PipelinePassRef<'a, [<$name Pipeline>]>> for Arc<[<$name Pipeline>]> { - // TODO: Allow binding as explicit secondary command buffers? like with compute/raytrace stuff - fn bind(self, mut pass: PassRef<'a>) -> PipelinePassRef<'a, [<$name Pipeline>]> { - let pass_ref = pass.as_mut(); - if pass_ref.execs.last().unwrap().pipeline.is_some() { - // Binding from PipelinePass -> PipelinePass (changing shaders) - pass_ref.execs.push(Default::default()); - } - - pass_ref.execs.last_mut().unwrap().pipeline = Some(ExecutionPipeline::$name(self)); - - PipelinePassRef { - __: PhantomData, - pass, - } - } - } - - impl<'a> Bind, PipelinePassRef<'a, [<$name Pipeline>]>> for [<$name Pipeline>] { - // TODO: Allow binding as explicit secondary command buffers? like with compute/raytrace stuff - fn bind(self, mut pass: PassRef<'a>) -> PipelinePassRef<'a, [<$name Pipeline>]> { - let pass_ref = pass.as_mut(); - if pass_ref.execs.last().unwrap().pipeline.is_some() { - // Binding from PipelinePass -> PipelinePass (changing shaders) - pass_ref.execs.push(Default::default()); - } - - pass_ref.execs.last_mut().unwrap().pipeline = Some(ExecutionPipeline::$name(Arc::new(self))); - - PipelinePassRef { - __: PhantomData, - pass, - } - } - } - - impl ExecutionPipeline { - #[allow(unused)] - pub(super) fn [](&self) -> bool { - matches!(self, Self::$name(_)) - } - - #[allow(unused)] - pub(super) fn [](&self) -> &Arc<[<$name Pipeline>]> { - if let Self::$name(binding) = self { - &binding - } else { - panic!(); - } - } - } - } - }; -} - -// Pipelines you can bind to a pass -bind!(Compute); -bind!(Graphic); -bind!(RayTrace); - -/// An indexable structure will provides access to Vulkan smart-pointer resources inside a record -/// closure. -/// -/// This type is available while recording commands in the following closures: -/// -/// - [`PassRef::record_acceleration`] for building and updating acceleration structures -/// - [`PassRef::record_cmd_buf`] for general command streams -/// - [`PipelinePassRef::record_compute`] for dispatched compute operations -/// - [`PipelinePassRef::record_subpass`] for raster drawing operations, such as triangles streams -/// - [`PipelinePassRef::record_ray_trace`] for ray-traced operations -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```no_run -/// # use std::sync::Arc; -/// # use ash::vk; -/// # use vk_graph::driver::DriverError; -/// # use vk_graph::driver::device::{Device, DeviceInfo}; -/// # use vk_graph::driver::image::{Image, ImageInfo}; -/// # use vk_graph::RenderGraph; -/// # use vk_graph::node::ImageNode; -/// # fn main() -> Result<(), DriverError> { -/// # let device = Arc::new(Device::new(DeviceInfo::default())?); -/// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); -/// # let image = Image::create(&device, info)?; -/// # let mut my_graph = RenderGraph::default(); -/// # let my_image_node = my_graph.bind_node(image); -/// my_graph.begin_cmd_buf().with_name("custom vulkan commands") -/// .record_cmd_buf(move |device, cmd_buf, bindings| { -/// let my_image = &bindings[my_image_node]; -/// -/// assert_ne!(my_image.handle, vk::Image::null()); -/// assert_eq!(my_image.info.width, 32); -/// }); -/// # Ok(()) } -/// ``` -#[derive(Clone, Copy, Debug)] -pub struct Bindings<'a> { - bindings: &'a [Binding], - exec: &'a Execution, -} - -impl<'a> Bindings<'a> { - pub(super) fn new(bindings: &'a [Binding], exec: &'a Execution) -> Self { - Self { bindings, exec } - } - - fn binding_ref(&self, node_idx: usize) -> &Binding { - // You must have called read or write for this node on this execution before indexing - // into the bindings data! - debug_assert!( - self.exec.accesses.contains_key(&node_idx), - "unexpected node access: call access, read, or write first" - ); - - &self.bindings[node_idx] - } -} - -macro_rules! index { - ($name:ident, $handle:ident) => { - paste::paste! { - impl<'a> Index<[<$name Node>]> for Bindings<'a> - { - type Output = $handle; - - fn index(&self, node: [<$name Node>]) -> &Self::Output { - &*self.binding_ref(node.idx).[]().unwrap() - } - } - } - }; -} - -// Allow indexing the Bindings data during command execution: -// (This gets you access to the driver images or other resources) -index!(AccelerationStructure, AccelerationStructure); -index!(AccelerationStructureLease, AccelerationStructure); -index!(Buffer, Buffer); -index!(BufferLease, Buffer); -index!(Image, Image); -index!(ImageLease, Image); -index!(SwapchainImage, Image); - -impl Index for Bindings<'_> { - type Output = AccelerationStructure; - - fn index(&self, node: AnyAccelerationStructureNode) -> &Self::Output { - let node_idx = match node { - AnyAccelerationStructureNode::AccelerationStructure(node) => node.idx, - AnyAccelerationStructureNode::AccelerationStructureLease(node) => node.idx, - }; - let binding = self.binding_ref(node_idx); - - match node { - AnyAccelerationStructureNode::AccelerationStructure(_) => { - binding.as_acceleration_structure().unwrap() - } - AnyAccelerationStructureNode::AccelerationStructureLease(_) => { - binding.as_acceleration_structure_lease().unwrap() - } - } - } -} - -impl Index for Bindings<'_> { - type Output = Buffer; - - fn index(&self, node: AnyBufferNode) -> &Self::Output { - let node_idx = match node { - AnyBufferNode::Buffer(node) => node.idx, - AnyBufferNode::BufferLease(node) => node.idx, - }; - let binding = self.binding_ref(node_idx); - - match node { - AnyBufferNode::Buffer(_) => binding.as_buffer().unwrap(), - AnyBufferNode::BufferLease(_) => binding.as_buffer_lease().unwrap(), - } - } -} - -impl Index for Bindings<'_> { - type Output = Image; - - fn index(&self, node: AnyImageNode) -> &Self::Output { - let node_idx = match node { - AnyImageNode::Image(node) => node.idx, - AnyImageNode::ImageLease(node) => node.idx, - AnyImageNode::SwapchainImage(node) => node.idx, - }; - let binding = self.binding_ref(node_idx); - - match node { - AnyImageNode::Image(_) => binding.as_image().unwrap(), - AnyImageNode::ImageLease(_) => binding.as_image_lease().unwrap(), - AnyImageNode::SwapchainImage(_) => binding.as_swapchain_image().unwrap(), - } - } -} - -/// Recording interface for computing commands. -/// -/// This structure provides a strongly-typed set of methods which allow compute shader code to be -/// executed. An instance of `Compute` is provided to the closure parameter of -/// [`PipelinePassRef::record_compute`] which may be accessed by binding a [`ComputePipeline`] to a -/// render pass. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```no_run -/// # use std::sync::Arc; -/// # use ash::vk; -/// # use vk_graph::driver::DriverError; -/// # use vk_graph::driver::device::{Device, DeviceInfo}; -/// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; -/// # use vk_graph::driver::shader::{Shader}; -/// # use vk_graph::RenderGraph; -/// # fn main() -> Result<(), DriverError> { -/// # let device = Arc::new(Device::new(DeviceInfo::default())?); -/// # let info = ComputePipelineInfo::default(); -/// # let shader = Shader::new_compute([0u8; 1].as_slice()); -/// # let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); -/// # let mut my_graph = RenderGraph::default(); -/// my_graph.begin_cmd_buf().with_name("my compute pass") -/// .bind_pipeline(&my_compute_pipeline) -/// .record_compute(move |compute, bindings| { -/// // During this closure we have access to the compute methods! -/// }); -/// # Ok(()) } -/// ``` -pub struct Compute<'a> { - bindings: Bindings<'a>, - cmd_buf: vk::CommandBuffer, - device: &'a Device, - pipeline: Arc, -} - -impl Compute<'_> { - /// [Dispatch] compute work items. - /// - /// When the command is executed, a global workgroup consisting of - /// `group_count_x × group_count_y × group_count_z` local workgroups is assembled. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # vk_shader_macros::glsl!(r#" - /// #version 450 - /// #pragma shader_stage(compute) - /// - /// layout(set = 0, binding = 0, std430) restrict writeonly buffer MyBufer { - /// uint my_buf[]; - /// }; - /// - /// void main() { - /// // TODO - /// } - /// # "#); - /// ``` - /// - /// ```no_run - /// # use std::sync::Arc; - /// # use ash::vk; - /// # use vk_graph::driver::DriverError; - /// # use vk_graph::driver::device::{Device, DeviceInfo}; - /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; - /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; - /// # use vk_graph::driver::shader::{Shader}; - /// # use vk_graph::RenderGraph; - /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); - /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::STORAGE_BUFFER); - /// # let my_buf = Buffer::create(&device, buf_info)?; - /// # let info = ComputePipelineInfo::default(); - /// # let shader = Shader::new_compute([0u8; 1].as_slice()); - /// # let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); - /// # let mut my_graph = RenderGraph::default(); - /// # let my_buf_node = my_graph.bind_node(my_buf); - /// my_graph.begin_cmd_buf().with_name("fill my_buf_node with data") - /// .bind_pipeline(&my_compute_pipeline) - /// .write_descriptor(0, my_buf_node) - /// .record_compute(move |compute, bindings| { - /// compute.dispatch(128, 64, 32); - /// }); - /// # Ok(()) } - /// ``` - /// - /// [Dispatch]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCmdDispatch.html - #[profiling::function] - pub fn dispatch(&self, group_count_x: u32, group_count_y: u32, group_count_z: u32) -> &Self { - unsafe { - self.device - .cmd_dispatch(self.cmd_buf, group_count_x, group_count_y, group_count_z); - } - - self - } - - /// [Dispatch] compute work items with non-zero base values for the workgroup IDs. - /// - /// When the command is executed, a global workgroup consisting of - /// `group_count_x × group_count_y × group_count_z` local workgroups is assembled, with - /// WorkgroupId values ranging from `[base_group*, base_group* + group_count*)` in each - /// component. - /// - /// [`Compute::dispatch`] is equivalent to - /// `dispatch_base(0, 0, 0, group_count_x, group_count_y, group_count_z)`. - /// - /// [Dispatch]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCmdDispatchBase.html - #[profiling::function] - pub fn dispatch_base( - &self, - base_group_x: u32, - base_group_y: u32, - base_group_z: u32, - group_count_x: u32, - group_count_y: u32, - group_count_z: u32, - ) -> &Self { - unsafe { - self.device.cmd_dispatch_base( - self.cmd_buf, - base_group_x, - base_group_y, - base_group_z, - group_count_x, - group_count_y, - group_count_z, - ); - } - - self - } - - /// Dispatch compute work items with indirect parameters. - /// - /// `dispatch_indirect` behaves similarly to [`Compute::dispatch`] except that the parameters - /// are read by the device from `args_buf` during execution. The parameters of the dispatch are - /// encoded in a [`vk::DispatchIndirectCommand`] structure taken from `args_buf` starting at - /// `args_offset`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// # use std::sync::Arc; - /// # use std::mem::size_of; - /// # use ash::vk; - /// # use vk_graph::driver::DriverError; - /// # use vk_graph::driver::device::{Device, DeviceInfo}; - /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; - /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; - /// # use vk_graph::driver::shader::{Shader}; - /// # use vk_graph::RenderGraph; - /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); - /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::STORAGE_BUFFER); - /// # let my_buf = Buffer::create(&device, buf_info)?; - /// # let info = ComputePipelineInfo::default(); - /// # let shader = Shader::new_compute([0u8; 1].as_slice()); - /// # let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); - /// # let mut my_graph = RenderGraph::default(); - /// # let my_buf_node = my_graph.bind_node(my_buf); - /// const CMD_SIZE: usize = size_of::(); - /// - /// let cmd = vk::DispatchIndirectCommand { - /// x: 1, - /// y: 2, - /// z: 3, - /// }; - /// let cmd_data = unsafe { - /// std::slice::from_raw_parts(&cmd as *const _ as *const _, CMD_SIZE) - /// }; - /// - /// let args_buf_flags = vk::BufferUsageFlags::STORAGE_BUFFER; - /// let args_buf = Buffer::create_from_slice(&device, args_buf_flags, cmd_data)?; - /// let args_buf_node = my_graph.bind_node(args_buf); - /// - /// my_graph.begin_cmd_buf().with_name("fill my_buf_node with data") - /// .bind_pipeline(&my_compute_pipeline) - /// .read_node(args_buf_node) - /// .write_descriptor(0, my_buf_node) - /// .record_compute(move |compute, bindings| { - /// compute.dispatch_indirect(args_buf_node, 0); - /// }); - /// # Ok(()) } - /// ``` - /// - /// [Dispatch]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCmdDispatchIndirect.html - /// [VkDispatchIndirectCommand]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkDispatchIndirectCommand.html - #[profiling::function] - pub fn dispatch_indirect( - &self, - args_buf: impl Into, - args_offset: vk::DeviceSize, - ) -> &Self { - let args_buf = args_buf.into(); - - unsafe { - self.device.cmd_dispatch_indirect( - self.cmd_buf, - self.bindings[args_buf].handle, - args_offset, - ); - } - - self - } - - /// Updates push constants. - /// - /// Push constants represent a high speed path to modify constant data in pipelines that is - /// expected to outperform memory-backed resource updates. - /// - /// Push constant values can be updated incrementally, causing shader stages to read the new - /// data for push constants modified by this command, while still reading the previous data for - /// push constants not modified by this command. - /// - /// # Device limitations - /// - /// See - /// [`device.physical_device.props.limits.max_push_constants_size`](vk::PhysicalDeviceLimits) - /// for the limits of the current device. You may also check [gpuinfo.org] for a listing of - /// reported limits on other devices. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # vk_shader_macros::glsl!(r#" - /// #version 450 - /// #pragma shader_stage(compute) - /// - /// layout(push_constant) uniform PushConstants { - /// layout(offset = 0) uint the_answer; - /// } push_constants; - /// - /// void main() - /// { - /// // TODO: Add bindings to read/write things! - /// } - /// # "#); - /// ``` - /// - /// ```no_run - /// # use std::sync::Arc; - /// # use ash::vk; - /// # use vk_graph::driver::DriverError; - /// # use vk_graph::driver::device::{Device, DeviceInfo}; - /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; - /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; - /// # use vk_graph::driver::shader::{Shader}; - /// # use vk_graph::RenderGraph; - /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); - /// # let info = ComputePipelineInfo::default(); - /// # let shader = Shader::new_compute([0u8; 1].as_slice()); - /// # let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); - /// # let mut my_graph = RenderGraph::default(); - /// my_graph.begin_cmd_buf().with_name("compute the ultimate question") - /// .bind_pipeline(&my_compute_pipeline) - /// .record_compute(move |compute, bindings| { - /// compute.push_constants(&[42]) - /// .dispatch(1, 1, 1); - /// }); - /// # Ok(()) } - /// ``` - /// - /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all - pub fn push_constants(&self, data: &[u8]) -> &Self { - self.push_constants_offset(0, data) - } - - /// Updates push constants starting at the given `offset`. - /// - /// Behaves similary to [`Compute::push_constants`] except that `offset` describes the position - /// at which `data` updates the push constants of the currently bound pipeline. This may be used - /// to update a subset or single field of previously set push constant data. - /// - /// # Device limitations - /// - /// See - /// [`device.physical_device.props.limits.max_push_constants_size`](vk::PhysicalDeviceLimits) - /// for the limits of the current device. You may also check [gpuinfo.org] for a listing of - /// reported limits on other devices. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # vk_shader_macros::glsl!(r#" - /// #version 450 - /// #pragma shader_stage(compute) - /// - /// layout(push_constant) uniform PushConstants { - /// layout(offset = 0) uint some_val1; - /// layout(offset = 4) uint some_val2; - /// } push_constants; - /// - /// void main() { - /// // TODO: Add bindings to read/write things! - /// } - /// # "#); - /// ``` - /// - /// ```no_run - /// # use std::sync::Arc; - /// # use ash::vk; - /// # use vk_graph::driver::DriverError; - /// # use vk_graph::driver::device::{Device, DeviceInfo}; - /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; - /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; - /// # use vk_graph::driver::shader::{Shader}; - /// # use vk_graph::RenderGraph; - /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); - /// # let info = ComputePipelineInfo::default(); - /// # let shader = Shader::new_compute([0u8; 1].as_slice()); - /// # let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); - /// # let mut my_graph = RenderGraph::default(); - /// my_graph.begin_cmd_buf().with_name("calculate the wow factor") - /// .bind_pipeline(&my_compute_pipeline) - /// .record_compute(move |compute, bindings| { - /// compute.push_constants(&[0x00, 0x00]) - /// .dispatch(1, 1, 1) - /// .push_constants_offset(4, &[0xff]) - /// .dispatch(1, 1, 1); - /// }); - /// # Ok(()) } - /// ``` - /// - /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all - #[profiling::function] - pub fn push_constants_offset(&self, offset: u32, data: &[u8]) -> &Self { - if let Some(push_const) = self.pipeline.push_constants { - // Determine the range of the overall pipline push constants which overlap with `data` - let push_const_end = push_const.offset + push_const.size; - let data_end = offset + data.len() as u32; - let end = data_end.min(push_const_end); - let start = offset.max(push_const.offset); - - if end > start { - trace!( - " push constants {:?} {}..{}", - push_const.stage_flags, start, end - ); - - unsafe { - self.device.cmd_push_constants( - self.cmd_buf, - self.pipeline.layout, - vk::ShaderStageFlags::COMPUTE, - push_const.offset, - &data[(start - offset) as usize..(end - offset) as usize], - ); - } - } - } - - self - } -} - -/// Describes the SPIR-V binding index, and optionally a specific descriptor set -/// and array index. -/// -/// Generally you might pass a function a descriptor using a simple integer: -/// -/// ```rust -/// # fn my_func(_: usize, _: ()) {} -/// # let image = (); -/// let descriptor = 42; -/// my_func(descriptor, image); -/// ``` -/// -/// But also: -/// -/// - `(0, 42)` for descriptor set `0` and binding index `42` -/// - `(42, [8])` for the same binding, but the 8th element -/// - `(0, 42, [8])` same as the previous example -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub enum Descriptor { - /// An array binding which includes an `offset` argument for the bound element. - ArrayBinding(DescriptorSetIndex, BindingIndex, BindingOffset), - - /// A single binding. - Binding(DescriptorSetIndex, BindingIndex), -} - -impl Descriptor { - pub(super) fn into_tuple(self) -> (DescriptorSetIndex, BindingIndex, BindingOffset) { - match self { - Self::ArrayBinding(descriptor_set_idx, binding_idx, binding_offset) => { - (descriptor_set_idx, binding_idx, binding_offset) - } - Self::Binding(descriptor_set_idx, binding_idx) => (descriptor_set_idx, binding_idx, 0), - } - } - - pub(super) fn set(self) -> DescriptorSetIndex { - let (res, _, _) = self.into_tuple(); - res - } -} - -impl From for Descriptor { - fn from(val: BindingIndex) -> Self { - Self::Binding(0, val) - } -} - -impl From<(DescriptorSetIndex, BindingIndex)> for Descriptor { - fn from(tuple: (DescriptorSetIndex, BindingIndex)) -> Self { - Self::Binding(tuple.0, tuple.1) - } -} - -impl From<(BindingIndex, [BindingOffset; 1])> for Descriptor { - fn from(tuple: (BindingIndex, [BindingOffset; 1])) -> Self { - Self::ArrayBinding(0, tuple.0, tuple.1[0]) - } -} - -impl From<(DescriptorSetIndex, BindingIndex, [BindingOffset; 1])> for Descriptor { - fn from(tuple: (DescriptorSetIndex, BindingIndex, [BindingOffset; 1])) -> Self { - Self::ArrayBinding(tuple.0, tuple.1, tuple.2[0]) - } -} - -/// Recording interface for drawing commands. -/// -/// This structure provides a strongly-typed set of methods which allow rasterization shader code to -/// be executed. An instance of `Draw` is provided to the closure parameter of -/// [`PipelinePassRef::record_subpass`] which may be accessed by binding a [`GraphicPipeline`] to a -/// render pass. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```no_run -/// # use std::sync::Arc; -/// # use ash::vk; -/// # use vk_graph::driver::DriverError; -/// # use vk_graph::driver::device::{Device, DeviceInfo}; -/// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; -/// # use vk_graph::driver::image::{Image, ImageInfo}; -/// # use vk_graph::RenderGraph; -/// # use vk_graph::driver::shader::Shader; -/// # fn main() -> Result<(), DriverError> { -/// # let device = Arc::new(Device::new(DeviceInfo::default())?); -/// # let my_frag_code = [0u8; 1]; -/// # let my_vert_code = [0u8; 1]; -/// # let vert = Shader::new_vertex(my_vert_code.as_slice()); -/// # let frag = Shader::new_fragment(my_frag_code.as_slice()); -/// # let info = GraphicPipelineInfo::default(); -/// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); -/// # let mut my_graph = RenderGraph::default(); -/// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); -/// # let swapchain_image = my_graph.bind_node(Image::create(&device, info)?); -/// my_graph.begin_cmd_buf().with_name("my draw pass") -/// .bind_pipeline(&my_graphic_pipeline) -/// .store_color(0, swapchain_image) -/// .record_subpass(move |subpass, bindings| { -/// // During this closure we have access to the draw methods! -/// }); -/// # Ok(()) } -/// ``` -pub struct Draw<'a> { - bindings: Bindings<'a>, - cmd_buf: vk::CommandBuffer, - device: &'a Device, - pipeline: Arc, -} - -impl Draw<'_> { - /// Bind an index buffer to the current pass. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// # use std::sync::Arc; - /// # use ash::vk; - /// # use vk_graph::driver::DriverError; - /// # use vk_graph::driver::device::{Device, DeviceInfo}; - /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; - /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; - /// # use vk_graph::driver::image::{Image, ImageInfo}; - /// # use vk_graph::driver::shader::Shader; - /// # use vk_graph::RenderGraph; - /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); - /// # let my_frag_code = [0u8; 1]; - /// # let my_vert_code = [0u8; 1]; - /// # let vert = Shader::new_vertex(my_vert_code.as_slice()); - /// # let frag = Shader::new_fragment(my_frag_code.as_slice()); - /// # let info = GraphicPipelineInfo::default(); - /// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); - /// # let mut my_graph = RenderGraph::default(); - /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); - /// # let swapchain_image = my_graph.bind_node(Image::create(&device, info)?); - /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::INDEX_BUFFER); - /// # let my_idx_buf = Buffer::create(&device, buf_info)?; - /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::VERTEX_BUFFER); - /// # let my_vtx_buf = Buffer::create(&device, buf_info)?; - /// # let my_idx_buf = my_graph.bind_node(my_idx_buf); - /// # let my_vtx_buf = my_graph.bind_node(my_vtx_buf); - /// my_graph.begin_cmd_buf().with_name("my indexed geometry draw pass") - /// .bind_pipeline(&my_graphic_pipeline) - /// .store_color(0, swapchain_image) - /// .read_node(my_idx_buf) - /// .read_node(my_vtx_buf) - /// .record_subpass(move |subpass, bindings| { - /// subpass.bind_index_buffer(my_idx_buf, vk::IndexType::UINT16) - /// .bind_vertex_buffer(my_vtx_buf) - /// .draw_indexed(42, 1, 0, 0, 0); - /// }); - /// # Ok(()) } - /// ``` - pub fn bind_index_buffer( - &self, - buffer: impl Into, - index_ty: vk::IndexType, - ) -> &Self { - self.bind_index_buffer_offset(buffer, index_ty, 0) - } - - /// Bind an index buffer to the current pass. - /// - /// Behaves similarly to `bind_index_buffer` except that `offset` is the starting offset in - /// bytes within `buffer` used in index buffer address calculations. - #[profiling::function] - pub fn bind_index_buffer_offset( - &self, - buffer: impl Into, - index_ty: vk::IndexType, - offset: vk::DeviceSize, - ) -> &Self { - let buffer = buffer.into(); - - unsafe { - self.device.cmd_bind_index_buffer( - self.cmd_buf, - self.bindings[buffer].handle, - offset, - index_ty, - ); - } - - self - } - - /// Bind a vertex buffer to the current pass. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// # use std::sync::Arc; - /// # use ash::vk; - /// # use vk_graph::driver::DriverError; - /// # use vk_graph::driver::device::{Device, DeviceInfo}; - /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; - /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; - /// # use vk_graph::driver::image::{Image, ImageInfo}; - /// # use vk_graph::driver::shader::Shader; - /// # use vk_graph::RenderGraph; - /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); - /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::VERTEX_BUFFER); - /// # let my_vtx_buf = Buffer::create(&device, buf_info)?; - /// # let my_frag_code = [0u8; 1]; - /// # let my_vert_code = [0u8; 1]; - /// # let vert = Shader::new_vertex(my_vert_code.as_slice()); - /// # let frag = Shader::new_fragment(my_frag_code.as_slice()); - /// # let info = GraphicPipelineInfo::default(); - /// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); - /// # let mut my_graph = RenderGraph::default(); - /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); - /// # let swapchain_image = my_graph.bind_node(Image::create(&device, info)?); - /// # let my_vtx_buf = my_graph.bind_node(my_vtx_buf); - /// my_graph.begin_cmd_buf().with_name("my unindexed geometry draw pass") - /// .bind_pipeline(&my_graphic_pipeline) - /// .store_color(0, swapchain_image) - /// .read_node(my_vtx_buf) - /// .record_subpass(move |subpass, bindings| { - /// subpass.bind_vertex_buffer(my_vtx_buf) - /// .draw(42, 1, 0, 0); - /// }); - /// # Ok(()) } - /// ``` - pub fn bind_vertex_buffer(&self, buffer: impl Into) -> &Self { - self.bind_vertex_buffer_offset(buffer, 0) - } - - /// Bind a vertex buffer to the current pass. - /// - /// Behaves similarly to `bind_vertex_buffer` except the vertex input binding is updated to - /// start at `offset` from the start of `buffer`. - #[profiling::function] - pub fn bind_vertex_buffer_offset( - &self, - buffer: impl Into, - offset: vk::DeviceSize, - ) -> &Self { - use std::slice::from_ref; - - let buffer = buffer.into(); - - unsafe { - self.device.cmd_bind_vertex_buffers( - self.cmd_buf, - 0, - from_ref(&self.bindings[buffer].handle), - from_ref(&offset), - ); - } - - self - } - - /// Binds multiple vertex buffers to the current pass, starting at the given `first_binding`. - /// - /// Each vertex input binding in `buffers` specifies an offset from the start of the - /// corresponding buffer. - /// - /// The vertex input attributes that use each of these bindings will use these updated addresses - /// in their address calculations for subsequent drawing commands. - #[profiling::function] - pub fn bind_vertex_buffers( - &self, - first_binding: u32, - buffer_offsets: impl IntoIterator, - ) -> &Self - where - B: Into, - { - thread_local! { - static BUFFERS_OFFSETS: RefCell<(Vec, Vec)> = Default::default(); - } - - BUFFERS_OFFSETS.with_borrow_mut(|(buffers, offsets)| { - buffers.clear(); - offsets.clear(); - - for (buffer, offset) in buffer_offsets { - let buffer = buffer.into(); - - buffers.push(self.bindings[buffer].handle); - offsets.push(offset); - } - - unsafe { - self.device.cmd_bind_vertex_buffers( - self.cmd_buf, - first_binding, - buffers.as_slice(), - offsets.as_slice(), - ); - } - }); - - self - } - - /// Draw unindexed primitives. - /// - /// When the command is executed, primitives are assembled using the current primitive topology - /// and `vertex_count` consecutive vertex indices with the first `vertex_index` value equal to - /// `first_vertex`. The primitives are drawn `instance_count` times with `instance_index` - /// starting with `first_instance` and increasing sequentially for each instance. - #[profiling::function] - pub fn draw( - &self, - vertex_count: u32, - instance_count: u32, - first_vertex: u32, - first_instance: u32, - ) -> &Self { - unsafe { - self.device.cmd_draw( - self.cmd_buf, - vertex_count, - instance_count, - first_vertex, - first_instance, - ); - } - - self - } - - /// Draw indexed primitives. - /// - /// When the command is executed, primitives are assembled using the current primitive topology - /// and `index_count` vertices whose indices are retrieved from the index buffer. The index - /// buffer is treated as an array of tightly packed unsigned integers of size defined by the - /// `index_ty` parameter with which the buffer was bound. - #[profiling::function] - pub fn draw_indexed( - &self, - index_count: u32, - instance_count: u32, - first_index: u32, - vertex_offset: i32, - first_instance: u32, - ) -> &Self { - unsafe { - self.device.cmd_draw_indexed( - self.cmd_buf, - index_count, - instance_count, - first_index, - vertex_offset, - first_instance, - ); - } - - self - } - - /// Draw primitives with indirect parameters and indexed vertices. - /// - /// `draw_indexed_indirect` behaves similarly to `draw_indexed` except that the parameters are - /// read by the device from `buffer` during execution. `draw_count` draws are executed by the - /// command, with parameters taken from `buffer` starting at `offset` and increasing by `stride` - /// bytes for each successive draw. The parameters of each draw are encoded in an array of - /// [`vk::DrawIndexedIndirectCommand`] structures. - /// - /// If `draw_count` is less than or equal to one, `stride` is ignored. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// # use std::sync::Arc; - /// # use std::mem::size_of; - /// # use ash::vk; - /// # use vk_graph::driver::DriverError; - /// # use vk_graph::driver::device::{Device, DeviceInfo}; - /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; - /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; - /// # use vk_graph::driver::image::{Image, ImageInfo}; - /// # use vk_graph::driver::shader::Shader; - /// # use vk_graph::RenderGraph; - /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); - /// # let my_frag_code = [0u8; 1]; - /// # let my_vert_code = [0u8; 1]; - /// # let vert = Shader::new_vertex(my_vert_code.as_slice()); - /// # let frag = Shader::new_fragment(my_frag_code.as_slice()); - /// # let info = GraphicPipelineInfo::default(); - /// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); - /// # let mut my_graph = RenderGraph::default(); - /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::INDEX_BUFFER); - /// # let my_idx_buf = Buffer::create(&device, buf_info)?; - /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::VERTEX_BUFFER); - /// # let my_vtx_buf = Buffer::create(&device, buf_info)?; - /// # let my_idx_buf = my_graph.bind_node(my_idx_buf); - /// # let my_vtx_buf = my_graph.bind_node(my_vtx_buf); - /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); - /// # let swapchain_image = my_graph.bind_node(Image::create(&device, info)?); - /// const CMD_SIZE: usize = size_of::(); - /// - /// let cmd = vk::DrawIndexedIndirectCommand { - /// index_count: 3, - /// instance_count: 1, - /// first_index: 0, - /// vertex_offset: 0, - /// first_instance: 0, - /// }; - /// let cmd_data = unsafe { - /// std::slice::from_raw_parts(&cmd as *const _ as *const _, CMD_SIZE) - /// }; - /// - /// let buf_flags = vk::BufferUsageFlags::STORAGE_BUFFER; - /// let buf = Buffer::create_from_slice(&device, buf_flags, cmd_data)?; - /// let buf_node = my_graph.bind_node(buf); - /// - /// my_graph.begin_cmd_buf().with_name("draw a single triangle") - /// .bind_pipeline(&my_graphic_pipeline) - /// .store_color(0, swapchain_image) - /// .read_node(my_idx_buf) - /// .read_node(my_vtx_buf) - /// .read_node(buf_node) - /// .record_subpass(move |subpass, bindings| { - /// subpass.bind_index_buffer(my_idx_buf, vk::IndexType::UINT16) - /// .bind_vertex_buffer(my_vtx_buf) - /// .draw_indexed_indirect(buf_node, 0, 1, 0); - /// }); - /// # Ok(()) } - /// ``` - #[profiling::function] - pub fn draw_indexed_indirect( - &self, - buffer: impl Into, - offset: vk::DeviceSize, - draw_count: u32, - stride: u32, - ) -> &Self { - let buffer = buffer.into(); - - unsafe { - self.device.cmd_draw_indexed_indirect( - self.cmd_buf, - self.bindings[buffer].handle, - offset, - draw_count, - stride, - ); - } - - self - } - - /// Draw primitives with indirect parameters, indexed vertices, and draw count. - /// - /// `draw_indexed_indirect_count` behaves similarly to `draw_indexed_indirect` except that the - /// draw count is read by the device from `buffer` during execution. The command will read an - /// unsigned 32-bit integer from `count_buf` located at `count_buf_offset` and use this as the - /// draw count. - /// - /// `max_draw_count` specifies the maximum number of draws that will be executed. The actual - /// number of executed draw calls is the minimum of the count specified in `count_buf` and - /// `max_draw_count`. - /// - /// `stride` is the byte stride between successive sets of draw parameters. - #[profiling::function] - pub fn draw_indexed_indirect_count( - &self, - buffer: impl Into, - offset: vk::DeviceSize, - count_buf: impl Into, - count_buf_offset: vk::DeviceSize, - max_draw_count: u32, - stride: u32, - ) -> &Self { - let buffer = buffer.into(); - let count_buf = count_buf.into(); - - unsafe { - self.device.cmd_draw_indexed_indirect_count( - self.cmd_buf, - self.bindings[buffer].handle, - offset, - self.bindings[count_buf].handle, - count_buf_offset, - max_draw_count, - stride, - ); - } - - self - } - - /// Draw primitives with indirect parameters and unindexed vertices. - /// - /// Behaves otherwise similar to [`Draw::draw_indexed_indirect`]. - #[profiling::function] - pub fn draw_indirect( - &self, - buffer: impl Into, - offset: vk::DeviceSize, - draw_count: u32, - stride: u32, - ) -> &Self { - let buffer = buffer.into(); - - unsafe { - self.device.cmd_draw_indirect( - self.cmd_buf, - self.bindings[buffer].handle, - offset, - draw_count, - stride, - ); - } - - self - } - - /// Draw primitives with indirect parameters, unindexed vertices, and draw count. - /// - /// Behaves otherwise similar to [`Draw::draw_indexed_indirect_count`]. - #[profiling::function] - pub fn draw_indirect_count( - &self, - buffer: impl Into, - offset: vk::DeviceSize, - count_buf: impl Into, - count_buf_offset: vk::DeviceSize, - max_draw_count: u32, - stride: u32, - ) -> &Self { - let buffer = buffer.into(); - let count_buf = count_buf.into(); - - unsafe { - self.device.cmd_draw_indirect_count( - self.cmd_buf, - self.bindings[buffer].handle, - offset, - self.bindings[count_buf].handle, - count_buf_offset, - max_draw_count, - stride, - ); - } - - self - } - - /// Updates push constants. - /// - /// Push constants represent a high speed path to modify constant data in pipelines that is - /// expected to outperform memory-backed resource updates. - /// - /// Push constant values can be updated incrementally, causing shader stages to read the new - /// data for push constants modified by this command, while still reading the previous data for - /// push constants not modified by this command. - /// - /// # Device limitations - /// - /// See - /// [`device.physical_device.props.limits.max_push_constants_size`](vk::PhysicalDeviceLimits) - /// for the limits of the current device. You may also check [gpuinfo.org] for a listing of - /// reported limits on other devices. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # vk_shader_macros::glsl!(r#" - /// #version 450 - /// #pragma shader_stage(compute) - /// - /// layout(push_constant) uniform PushConstants { - /// layout(offset = 0) uint the_answer; - /// } push_constants; - /// - /// void main() { - /// // TODO: Add code! - /// } - /// # "#); - /// ``` - /// - /// ```no_run - /// # use std::sync::Arc; - /// # use ash::vk; - /// # use vk_graph::driver::DriverError; - /// # use vk_graph::driver::device::{Device, DeviceInfo}; - /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; - /// # use vk_graph::driver::image::{Image, ImageInfo}; - /// # use vk_graph::RenderGraph; - /// # use vk_graph::driver::shader::Shader; - /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); - /// # let my_frag_code = [0u8; 1]; - /// # let my_vert_code = [0u8; 1]; - /// # let vert = Shader::new_vertex(my_vert_code.as_slice()); - /// # let frag = Shader::new_fragment(my_frag_code.as_slice()); - /// # let info = GraphicPipelineInfo::default(); - /// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); - /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); - /// # let swapchain_image = Image::create(&device, info)?; - /// # let mut my_graph = RenderGraph::default(); - /// # let swapchain_image = my_graph.bind_node(swapchain_image); - /// my_graph.begin_cmd_buf().with_name("draw a quad") - /// .bind_pipeline(&my_graphic_pipeline) - /// .store_color(0, swapchain_image) - /// .record_subpass(move |subpass, bindings| { - /// subpass.push_constants(&[42]) - /// .draw(6, 1, 0, 0); - /// }); - /// # Ok(()) } - /// ``` - /// - /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all - pub fn push_constants(&self, data: &[u8]) -> &Self { - self.push_constants_offset(0, data) - } - - /// Updates push constants starting at the given `offset`. - /// - /// Behaves similary to [`Draw::push_constants`] except that `offset` describes the position at - /// which `data` updates the push constants of the currently bound pipeline. This may be used to - /// update a subset or single field of previously set push constant data. - /// - /// # Device limitations - /// - /// See - /// [`device.physical_device.props.limits.max_push_constants_size`](vk::PhysicalDeviceLimits) - /// for the limits of the current device. You may also check [gpuinfo.org] for a listing of - /// reported limits on other devices. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # vk_shader_macros::glsl!(r#" - /// #version 450 - /// #pragma shader_stage(compute) - /// - /// layout(push_constant) uniform PushConstants { - /// layout(offset = 0) uint some_val1; - /// layout(offset = 4) uint some_val2; - /// } push_constants; - /// - /// void main() { - /// // TODO: Add code! - /// } - /// # "#); - /// ``` - /// - /// ```no_run - /// # use std::sync::Arc; - /// # use ash::vk; - /// # use vk_graph::driver::DriverError; - /// # use vk_graph::driver::device::{Device, DeviceInfo}; - /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; - /// # use vk_graph::driver::image::{Image, ImageInfo}; - /// # use vk_graph::RenderGraph; - /// # use vk_graph::driver::shader::Shader; - /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); - /// # let my_frag_code = [0u8; 1]; - /// # let my_vert_code = [0u8; 1]; - /// # let vert = Shader::new_vertex(my_vert_code.as_slice()); - /// # let frag = Shader::new_fragment(my_frag_code.as_slice()); - /// # let info = GraphicPipelineInfo::default(); - /// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); - /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); - /// # let swapchain_image = Image::create(&device, info)?; - /// # let mut my_graph = RenderGraph::default(); - /// # let swapchain_image = my_graph.bind_node(swapchain_image); - /// my_graph.begin_cmd_buf().with_name("draw a quad") - /// .bind_pipeline(&my_graphic_pipeline) - /// .store_color(0, swapchain_image) - /// .record_subpass(move |subpass, bindings| { - /// subpass.push_constants(&[0x00, 0x00]) - /// .draw(6, 1, 0, 0) - /// .push_constants_offset(4, &[0xff]) - /// .draw(6, 1, 0, 0); - /// }); - /// # Ok(()) } - /// ``` - /// - /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all - #[profiling::function] - pub fn push_constants_offset(&self, offset: u32, data: &[u8]) -> &Self { - for push_const in self.pipeline.push_constants.iter() { - // Determine the range of the overall pipline push constants which overlap with `data` - let push_const_end = push_const.offset + push_const.size; - let data_end = offset + data.len() as u32; - let end = data_end.min(push_const_end); - let start = offset.max(push_const.offset); - - if end > start { - trace!( - " push constants {:?} {}..{}", - push_const.stage_flags, start, end - ); - - unsafe { - self.device.cmd_push_constants( - self.cmd_buf, - self.pipeline.layout, - push_const.stage_flags, - start, - &data[(start - offset) as usize..(end - offset) as usize], - ); - } - } - } - - self - } - - /// Set scissor rectangle dynamically for a pass. - #[profiling::function] - pub fn set_scissor(&self, x: i32, y: i32, width: u32, height: u32) -> &Self { - unsafe { - self.device.cmd_set_scissor( - self.cmd_buf, - 0, - &[vk::Rect2D { - extent: vk::Extent2D { width, height }, - offset: vk::Offset2D { x, y }, - }], - ); - } - - self - } - - /// Set scissor rectangles dynamically for a pass. - #[profiling::function] - pub fn set_scissors( - &self, - first_scissor: u32, - scissors: impl IntoIterator, - ) -> &Self - where - S: Into, - { - thread_local! { - static SCISSORS: RefCell> = Default::default(); - } - - SCISSORS.with_borrow_mut(|scissors_vec| { - scissors_vec.clear(); - - for scissor in scissors { - scissors_vec.push(scissor.into()); - } - - unsafe { - self.device - .cmd_set_scissor(self.cmd_buf, first_scissor, scissors_vec.as_slice()); - } - }); - - self - } - - /// Set the viewport dynamically for a pass. - #[profiling::function] - pub fn set_viewport( - &self, - x: f32, - y: f32, - width: f32, - height: f32, - depth: Range, - ) -> &Self { - unsafe { - self.device.cmd_set_viewport( - self.cmd_buf, - 0, - &[vk::Viewport { - x, - y, - width, - height, - min_depth: depth.start, - max_depth: depth.end, - }], - ); - } - - self - } - - /// Set the viewports dynamically for a pass. - #[profiling::function] - pub fn set_viewports( - &self, - first_viewport: u32, - viewports: impl IntoIterator, - ) -> &Self - where - V: Into, - { - thread_local! { - static VIEWPORTS: RefCell> = Default::default(); - } - - VIEWPORTS.with_borrow_mut(|viewports_vec| { - viewports_vec.clear(); - - for viewport in viewports { - viewports_vec.push(viewport.into()); - } - - unsafe { - self.device.cmd_set_viewport( - self.cmd_buf, - first_viewport, - viewports_vec.as_slice(), - ); - } - }); - - self - } -} - -/// A general render pass which may contain acceleration structure commands, general commands, or -/// have pipeline bound to then record commands specific to those pipeline types. -pub struct PassRef<'a> { - pub(super) exec_idx: usize, - pub(super) graph: &'a mut RenderGraph, - pub(super) pass_idx: usize, -} - -impl<'a> PassRef<'a> { - pub(super) fn new(graph: &'a mut RenderGraph) -> PassRef<'a> { - let pass_idx = graph.passes.len(); - graph.passes.push(Pass { - execs: vec![Default::default()], // We start off with a default execution! - name: None, - }); - - Self { - exec_idx: 0, - graph, - pass_idx, - } - } - - /// Informs the pass that the next recorded command buffer will read or write the given `node` - /// using `access`. - /// - /// This function must be called for `node` before it is read or written within a `record` - /// function. For general purpose access, see [`PassRef::read_node`] or [`PassRef::write_node`]. - pub fn access_node(mut self, node: impl Node + Information, access: AccessType) -> Self { - self.access_node_mut(node, access); - - self - } - - /// Informs the pass that the next recorded command buffer will read or write the given `node` - /// using `access`. - /// - /// This function must be called for `node` before it is read or written within a `record` - /// function. For general purpose access, see [`PassRef::read_node_mut`] or - /// [`PassRef::write_node_mut`]. - pub fn access_node_mut(&mut self, node: impl Node + Information, access: AccessType) { - self.assert_bound_graph_node(node); - - let idx = node.index(); - let binding = &self.graph.bindings[idx]; - - let node_access_range = if let Some(buf) = binding.as_driver_buffer() { - Subresource::Buffer((0..buf.info.size).into()) - } else if let Some(image) = binding.as_driver_image() { - Subresource::Image(image.info.default_view_info().into()) - } else { - Subresource::AccelerationStructure - }; - - self.push_node_access(node, access, node_access_range); - } - - /// Informs the pass that the next recorded command buffer will read or write the `subresource` - /// of `node` using `access`. - /// - /// This function must be called for `node` before it is read or written within a `record` - /// function. For general purpose access, see [`PassRef::read_node`] or [`PassRef::write_node`]. - pub fn access_node_subrange( - mut self, - node: N, - access: AccessType, - subresource: impl Into, - ) -> Self - where - N: View, - { - self.access_node_subrange_mut(node, access, subresource); - - self - } - - /// Informs the pass that the next recorded command buffer will read or write the `subresource` - /// of `node` using `access`. - /// - /// This function must be called for `node` before it is read or written within a `record` - /// function. For general purpose access, see [`PassRef::read_node`] or [`PassRef::write_node`]. - pub fn access_node_subrange_mut( - &mut self, - node: N, - access: AccessType, - subresource: impl Into, - ) where - N: View, - { - self.push_node_access(node, access, subresource.into().into()); - } - - fn as_mut(&mut self) -> &mut Pass { - &mut self.graph.passes[self.pass_idx] - } - - fn as_ref(&self) -> &Pass { - &self.graph.passes[self.pass_idx] - } - - fn assert_bound_graph_node(&self, node: impl Node) { - let idx = node.index(); - - assert!(self.graph.bindings[idx].is_bound()); - } - - /// Binds a Vulkan acceleration structure, buffer, or image to the graph associated with this - /// pass. - /// - /// Bound nodes may be used in passes for pipeline and shader operations. - pub fn bind_node<'b, B>(&'b mut self, binding: B) -> >::Result - where - B: Edge, - B: Bind<&'b mut RenderGraph, >::Result>, - { - self.graph.bind_node(binding) - } - - /// Binds a [`ComputePipeline`], [`GraphicPipeline`], or [`RayTracePipeline`] to the current - /// pass, allowing for strongly typed access to the related functions. - pub fn bind_pipeline(self, binding: B) -> >::Result - where - B: Edge, - B: Bind>::Result>, - { - binding.bind(self) - } - - /// Finalize the recording of this pass and return to the `RenderGraph` where you may record - /// additional passes. - pub fn end_cmd_buf(self) -> &'a mut RenderGraph { - // If nothing was done in this pass we can just ignore it - if self.exec_idx == 0 { - self.graph.passes.pop(); - } - - self.graph - } - - /// Returns information used to crate a node. - pub fn node_info(&self, node: N) -> ::Info - where - N: Information, - { - node.get(self.graph) - } - - fn push_execute( - &mut self, - func: impl FnOnce(&Device, vk::CommandBuffer, Bindings<'_>) + Send + 'static, - ) { - let pass = self.as_mut(); - let exec = { - let last_exec = pass.execs.last_mut().unwrap(); - last_exec.func = Some(ExecutionFunction(Box::new(func))); - - Execution { - pipeline: last_exec.pipeline.clone(), - ..Default::default() - } - }; - - pass.execs.push(exec); - self.exec_idx += 1; - } - - fn push_node_access(&mut self, node: impl Node, access: AccessType, subresource: Subresource) { - let node_idx = node.index(); - self.assert_bound_graph_node(node); - - let access = SubresourceAccess { - access, - subresource, - }; - self.as_mut() - .execs - .last_mut() - .unwrap() - .accesses - .entry(node_idx) - .and_modify(|accesses| accesses.push(access)) - .or_insert(vec![access]); - } - - /// Informs the pass that the next recorded command buffer will read the given `node` using - /// [`AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer`]. - /// - /// This function must be called for `node` before it is read within a `record` function. For - /// more specific access, see [`PassRef::access_node`]. - pub fn read_node(mut self, node: impl Node + Information) -> Self { - self.read_node_mut(node); - - self - } - - /// Informs the pass that the next recorded command buffer will read the given `node` using - /// [`AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer`]. - /// - /// This function must be called for `node` before it is read within a `record` function. For - /// more specific access, see [`PassRef::access_node`]. - pub fn read_node_mut(&mut self, node: impl Node + Information) { - self.access_node_mut( - node, - AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, - ); - } - - /// Begin recording an acceleration structure command buffer. - /// - /// This is the entry point for building and updating an [`AccelerationStructure`] instance. - pub fn record_acceleration( - mut self, - func: impl FnOnce(Acceleration<'_>, Bindings<'_>) + Send + 'static, - ) -> Self { - self.push_execute(move |device, cmd_buf, bindings| { - func( - Acceleration { - bindings, - cmd_buf, - device, - }, - bindings, - ); - }); - - self - } - - /// Begin recording a general command buffer. - /// - /// The provided closure allows you to run any Vulkan code, or interoperate with other Vulkan - /// code and interfaces. - pub fn record_cmd_buf( - mut self, - func: impl FnOnce(&Device, vk::CommandBuffer, Bindings<'_>) + Send + 'static, - ) -> Self { - self.push_execute(func); - - self - } - - /// Sets a debugging name, but only in debug builds - pub fn with_name(mut self, name: impl Into) -> Self { - #[cfg(debug_assertions)] - { - self.as_mut().name = Some(name.into()); - } - - self - } - - /// Informs the pass that the next recorded command buffer will write the given `node` using - /// [`AccessType::AnyShaderWrite`]. - /// - /// This function must be called for `node` before it is written within a `record` function. For - /// more specific access, see [`PassRef::access_node`]. - pub fn write_node(mut self, node: impl Node + Information) -> Self { - self.write_node_mut(node); - - self - } - - /// Informs the pass that the next recorded command buffer will write the given `node` using - /// [`AccessType::AnyShaderWrite`]. - /// - /// This function must be called for `node` before it is written within a `record` function. For - /// more specific access, see [`PassRef::access_node`]. - pub fn write_node_mut(&mut self, node: impl Node + Information) { - self.access_node_mut(node, AccessType::AnyShaderWrite); - } -} - -/// A render pass which has been bound to a particular compute, graphic, or ray-trace pipeline. -pub struct PipelinePassRef<'a, T> -where - T: Access, -{ - __: PhantomData, - pass: PassRef<'a>, -} - -impl<'a, T> PipelinePassRef<'a, T> -where - T: Access, -{ - /// Informs the pass that the next recorded command buffer will read or write the given `node` - /// at the specified shader descriptor using `access`. - /// - /// This function must be called for `node` before it is read or written within a `record` - /// function. For general purpose access, see [`PipelinePassRef::read_descriptor`] or - /// [`PipelinePassRef::write_descriptor`]. - pub fn access_descriptor( - self, - descriptor: impl Into, - node: N, - access: AccessType, - ) -> Self - where - N: Information, - N: View, - ViewType: From<::Information>, - ::Information: From<::Info>, - ::Subresource: From<::Information>, - { - let view_info = node.get(self.pass.graph); - self.access_descriptor_as(descriptor, node, access, view_info) - } - - /// Informs the pass that the next recorded command buffer will read or write the given `node` - /// at the specified shader descriptor using `access`. The node will be interpreted using - /// `view_info`. - /// - /// This function must be called for `node` before it is read or written within a `record` - /// function. For general purpose access, see [`PipelinePassRef::read_descriptor_as`] or - /// [`PipelinePassRef::write_descriptor_as`]. - pub fn access_descriptor_as( - self, - descriptor: impl Into, - node: N, - access: AccessType, - view_info: impl Into, - ) -> Self - where - N: View, - ::Information: Into, - ::Subresource: From<::Information>, - { - let view_info = view_info.into(); - let subresource = ::Subresource::from(view_info); - - self.access_descriptor_subrange(descriptor, node, access, view_info, subresource) - } - - /// Informs the pass that the next recorded command buffer will read or write the `subresource` - /// of `node` at the specified shader descriptor using `access`. The node will be interpreted - /// using `view_info`. - /// - /// This function must be called for `node` before it is read or written within a `record` - /// function. For general purpose access, see [`PipelinePassRef::read_descriptor_subrange`] or - /// [`PipelinePassRef::write_descriptor_subrange`]. - pub fn access_descriptor_subrange( - mut self, - descriptor: impl Into, - node: N, - access: AccessType, - view_info: impl Into, - subresource: impl Into, - ) -> Self - where - N: View, - ::Information: Into, - { - self.pass - .push_node_access(node, access, subresource.into().into()); - self.push_node_view_bind(node, view_info.into(), descriptor.into()); - - self - } - - /// Informs the pass that the next recorded command buffer will read or write the given `node` - /// using `access`. - /// - /// This function must be called for `node` before it is read or written within a `record` - /// function. For general purpose access, see [`PipelinePassRef::read_node`] or - /// [`PipelinePassRef::write_node`]. - pub fn access_node(mut self, node: impl Node + Information, access: AccessType) -> Self { - self.access_node_mut(node, access); - - self - } - - /// Informs the pass that the next recorded command buffer will read or write the given `node` - /// using `access`. - /// - /// This function must be called for `node` before it is read or written within a `record` - /// function. For general purpose access, see [`PipelinePassRef::read_node_mut`] or - /// [`PipelinePassRef::write_node_mut`]. - pub fn access_node_mut(&mut self, node: impl Node + Information, access: AccessType) { - self.pass.assert_bound_graph_node(node); - - let idx = node.index(); - let binding = &self.pass.graph.bindings[idx]; - - let node_access_range = if let Some(buf) = binding.as_driver_buffer() { - Subresource::Buffer((0..buf.info.size).into()) - } else if let Some(image) = binding.as_driver_image() { - Subresource::Image(image.info.default_view_info().into()) - } else { - Subresource::AccelerationStructure - }; - - self.pass.push_node_access(node, access, node_access_range); - } - - /// Informs the pass that the next recorded command buffer will read or write the `subresource` - /// of `node` using `access`. - /// - /// This function must be called for `node` before it is read or written within a `record` - /// function. For general purpose access, see [`PipelinePassRef::read_node_subrange`] or - /// [`PipelinePassRef::write_node_subrange`]. - pub fn access_node_subrange( - mut self, - node: N, - access: AccessType, - subresource: impl Into, - ) -> Self - where - N: View, - { - self.access_node_subrange_mut(node, access, subresource); - - self - } - - /// Informs the pass that the next recorded command buffer will read or write the `subresource` - /// of `node` using `access`. - /// - /// This function must be called for `node` before it is read or written within a `record` - /// function. For general purpose access, see [`PipelinePassRef::read_node_subrange_mut`] or - /// [`PipelinePassRef::write_node_subrange_mut`]. - pub fn access_node_subrange_mut( - &mut self, - node: N, - access: AccessType, - subresource: impl Into, - ) where - N: View, - { - self.pass - .push_node_access(node, access, subresource.into().into()); - } - - /// Binds a Vulkan acceleration structure, buffer, or image to the graph associated with this - /// pass. - /// - /// Bound nodes may be used in passes for pipeline and shader operations. - pub fn bind_node<'b, B>(&'b mut self, binding: B) -> >::Result - where - B: Edge, - B: Bind<&'b mut RenderGraph, >::Result>, - { - self.pass.graph.bind_node(binding) - } - - /// Finalizes a pass and returns the render graph so that additional passes may be added. - pub fn end_cmd_buf(self) -> &'a mut RenderGraph { - self.pass.end_cmd_buf() - } - - /// Returns information used to crate a node. - pub fn node_info(&self, node: N) -> ::Info - where - N: Information, - { - node.get(self.pass.graph) - } - - fn push_node_view_bind( - &mut self, - node: impl Node, - view_info: impl Into, - binding: Descriptor, - ) { - let node_idx = node.index(); - self.pass.assert_bound_graph_node(node); - - assert!( - self.pass - .as_mut() - .execs - .last_mut() - .unwrap() - .bindings - .insert(binding, (node_idx, Some(view_info.into()))) - .is_none(), - "descriptor {binding:?} has already been bound" - ); - } - - /// Informs the pass that the next recorded command buffer will read the given `node` at the - /// specified shader descriptor. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is read within a `record` function. For - /// more specific access, see [`PipelinePassRef::access_descriptor`]. - pub fn read_descriptor(self, descriptor: impl Into, node: N) -> Self - where - N: Information, - N: View, - ViewType: From<::Information>, - ::Information: From<::Info>, - ::Subresource: From<::Information>, - { - let view_info = node.get(self.pass.graph); - self.read_descriptor_as(descriptor, node, view_info) - } - - /// Informs the pass that the next recorded command buffer will read the given `node` at the - /// specified shader descriptor. The node will be interpreted using `view_info`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is read within a `record` function. For - /// more specific access, see [`PipelinePassRef::access_descriptor_as`]. - pub fn read_descriptor_as( - self, - descriptor: impl Into, - node: N, - view_info: impl Into, - ) -> Self - where - N: View, - ::Information: Into, - ::Subresource: From<::Information>, - { - let view_info = view_info.into(); - let subresource = ::Subresource::from(view_info); - - self.read_descriptor_subrange(descriptor, node, view_info, subresource) - } - - /// Informs the pass that the next recorded command buffer will read the `subresource` of `node` - /// at the specified shader descriptor. The node will be interpreted using `view_info`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is read within a `record` function. For - /// more specific access, see [`PipelinePassRef::access_descriptor_subrange`]. - pub fn read_descriptor_subrange( - self, - descriptor: impl Into, - node: N, - view_info: impl Into, - subresource: impl Into, - ) -> Self - where - N: View, - ::Information: Into, - { - let access = ::DEFAULT_READ; - self.access_descriptor_subrange(descriptor, node, access, view_info, subresource) - } - - /// Informs the pass that the next recorded command buffer will read the given `node`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is read within a `record` function. For - /// more specific access, see [`PipelinePassRef::access_node`]. - pub fn read_node(mut self, node: impl Node + Information) -> Self { - self.read_node_mut(node); - - self - } - - /// Informs the pass that the next recorded command buffer will read the given `node`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is read within a `record` function. For - /// more specific access, see [`PipelinePassRef::access_node_mut`]. - pub fn read_node_mut(&mut self, node: impl Node + Information) { - let access = ::DEFAULT_READ; - self.access_node_mut(node, access); - } - - /// Informs the pass that the next recorded command buffer will read the `subresource` of - /// `node`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is read within a `record` function. For - /// more specific access, see [`PipelinePassRef::access_node_subrange`]. - pub fn read_node_subrange(mut self, node: N, subresource: impl Into) -> Self - where - N: View, - { - self.read_node_subrange_mut(node, subresource); - - self - } - - /// Informs the pass that the next recorded command buffer will read the `subresource` of - /// `node`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is read within a `record` function. For - /// more specific access, see [`PipelinePassRef::access_node_subrange_mut`]. - pub fn read_node_subrange_mut(&mut self, node: N, subresource: impl Into) - where - N: View, - { - let access = ::DEFAULT_READ; - self.access_node_subrange_mut(node, access, subresource); - } - - /// Informs the pass that the next recorded command buffer will write the given `node` at the - /// specified shader descriptor. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is written within a `record` function. For - /// more specific access, see [`PipelinePassRef::access_descriptor`]. - pub fn write_descriptor(self, descriptor: impl Into, node: N) -> Self - where - N: Information, - N: View, - ::Information: Into, - ::Information: From<::Info>, - ::Subresource: From<::Information>, - { - let view_info = node.get(self.pass.graph); - self.write_descriptor_as(descriptor, node, view_info) - } - - /// Informs the pass that the next recorded command buffer will write the given `node` at the - /// specified shader descriptor. The node will be interpreted using `view_info`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is written within a `record` function. For - /// more specific access, see [`PipelinePassRef::access_descriptor_as`]. - pub fn write_descriptor_as( - self, - descriptor: impl Into, - node: N, - view_info: impl Into, - ) -> Self - where - N: View, - ::Information: Into, - ::Subresource: From<::Information>, - { - let view_info = view_info.into(); - let subresource = ::Subresource::from(view_info); - - self.write_descriptor_subrange(descriptor, node, view_info, subresource) - } - - /// Informs the pass that the next recorded command buffer will write the `subresource` of - /// `node` at the specified shader descriptor. The node will be interpreted using `view_info`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is written within a `record` function. For - /// more specific access, see [`PipelinePassRef::access_descriptor_subrange`]. - pub fn write_descriptor_subrange( - self, - descriptor: impl Into, - node: N, - view_info: impl Into, - subresource: impl Into, - ) -> Self - where - N: View, - ::Information: Into, - { - let access = ::DEFAULT_WRITE; - self.access_descriptor_subrange(descriptor, node, access, view_info, subresource) - } - - /// Informs the pass that the next recorded command buffer will write the given `node`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is written within a `record` function. For - /// more specific access, see [`PipelinePassRef::access_node`]. - pub fn write_node(mut self, node: impl Node + Information) -> Self { - self.write_node_mut(node); - - self - } - - /// Informs the pass that the next recorded command buffer will write the given `node`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is written within a `record` function. For - /// more specific access, see [`PipelinePassRef::access_node_mut`]. - pub fn write_node_mut(&mut self, node: impl Node + Information) { - let access = ::DEFAULT_WRITE; - self.access_node_mut(node, access); - } - - /// Informs the pass that the next recorded command buffer will write the `subresource` of - /// `node`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is written within a `record` function. For - /// more specific access, see [`PipelinePassRef::access_node_subrange`]. - pub fn write_node_subrange(mut self, node: N, subresource: impl Into) -> Self - where - N: View, - { - self.write_node_subrange_mut(node, subresource); - - self - } - - /// Informs the pass that the next recorded command buffer will write the `subresource` of - /// `node`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is written within a `record` function. For - /// more specific access, see [`PipelinePassRef::access_node_subrange_mut`]. - pub fn write_node_subrange_mut(&mut self, node: N, subresource: impl Into) - where - N: View, - { - let access = ::DEFAULT_WRITE; - self.access_node_subrange_mut(node, access, subresource); - } -} - -impl PipelinePassRef<'_, ComputePipeline> { - /// Begin recording a computing command buffer. - pub fn record_compute( - mut self, - func: impl FnOnce(Compute<'_>, Bindings<'_>) + Send + 'static, - ) -> Self { - let pipeline = Arc::clone( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .pipeline - .as_ref() - .unwrap() - .unwrap_compute(), - ); - - self.pass.push_execute(move |device, cmd_buf, bindings| { - func( - Compute { - bindings, - cmd_buf, - device, - pipeline, - }, - bindings, - ); - }); - - self - } -} - -impl PipelinePassRef<'_, GraphicPipeline> { - /// Specifies `VK_ATTACHMENT_LOAD_OP_DONT_CARE` for the render pass attachment, and loads an - /// image into the framebuffer. - pub fn attach_color( - self, - attachment_idx: AttachmentIndex, - image: impl Into, - ) -> Self { - let image: AnyImageNode = image.into(); - let image_info = image.get(self.pass.graph); - let image_view_info: ImageViewInfo = image_info.into(); - - self.attach_color_as(attachment_idx, image, image_view_info) - } - - /// Specifies `VK_ATTACHMENT_LOAD_OP_DONT_CARE` for the render pass attachment, and loads an - /// image into the framebuffer. - pub fn attach_color_as( - mut self, - attachment_idx: AttachmentIndex, - image: impl Into, - image_view_info: impl Into, - ) -> Self { - let image = image.into(); - let image_view_info = image_view_info.into(); - let node_idx = image.index(); - let (_, sample_count) = self.image_info(node_idx); - - debug_assert!( - !self - .pass - .as_ref() - .execs - .last() - .unwrap() - .color_clears - .contains_key(&attachment_idx), - "color attachment {attachment_idx} already attached via clear" - ); - debug_assert!( - !self - .pass - .as_ref() - .execs - .last() - .unwrap() - .color_loads - .contains_key(&attachment_idx), - "color attachment {attachment_idx} already attached via load" - ); - - self.pass - .as_mut() - .execs - .last_mut() - .unwrap() - .color_attachments - .insert( - attachment_idx, - Attachment::new(image_view_info, sample_count, node_idx), - ); - - debug_assert!( - Attachment::are_compatible( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_resolves - .get(&attachment_idx) - .map(|(attachment, _)| *attachment), - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_attachments - .get(&attachment_idx) - .copied() - ), - "color attachment {attachment_idx} incompatible with existing resolve" - ); - debug_assert!( - Attachment::are_compatible( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_stores - .get(&attachment_idx) - .copied(), - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_attachments - .get(&attachment_idx) - .copied() - ), - "color attachment {attachment_idx} incompatible with existing store" - ); - - self.pass.push_node_access( - image, - AccessType::ColorAttachmentWrite, - Subresource::Image(image_view_info.into()), - ); - - self - } - - /// Specifies `VK_ATTACHMENT_LOAD_OP_DONT_CARE` for the render pass attachment, and loads an - /// image into the framebuffer. - pub fn attach_depth_stencil(self, image: impl Into) -> Self { - let image: AnyImageNode = image.into(); - let image_info = image.get(self.pass.graph); - let image_view_info: ImageViewInfo = image_info.into(); - - self.attach_depth_stencil_as(image, image_view_info) - } - - /// Specifies `VK_ATTACHMENT_LOAD_OP_DONT_CARE` for the render pass attachment, and loads an - /// image into the framebuffer. - pub fn attach_depth_stencil_as( - mut self, - image: impl Into, - image_view_info: impl Into, - ) -> Self { - let image = image.into(); - let image_view_info = image_view_info.into(); - let node_idx = image.index(); - let (_, sample_count) = self.image_info(node_idx); - - debug_assert!( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .depth_stencil_clear - .is_none(), - "depth/stencil attachment already attached via clear" - ); - debug_assert!( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .depth_stencil_load - .is_none(), - "depth/stencil attachment already attached via load" - ); - - self.pass - .as_mut() - .execs - .last_mut() - .unwrap() - .depth_stencil_attachment = - Some(Attachment::new(image_view_info, sample_count, node_idx)); - - debug_assert!( - Attachment::are_compatible( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .depth_stencil_resolve - .map(|(attachment, ..)| attachment), - self.pass - .as_ref() - .execs - .last() - .unwrap() - .depth_stencil_attachment - ), - "depth/stencil attachment incompatible with existing resolve" - ); - debug_assert!( - Attachment::are_compatible( - self.pass.as_ref().execs.last().unwrap().depth_stencil_store, - self.pass - .as_ref() - .execs - .last() - .unwrap() - .depth_stencil_attachment - ), - "depth/stencil attachment incompatible with existing store" - ); - - self.pass.push_node_access( - image, - if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL) - { - AccessType::DepthStencilAttachmentWrite - } else if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH) - { - AccessType::DepthAttachmentWriteStencilReadOnly - } else { - AccessType::StencilAttachmentWriteDepthReadOnly - }, - Subresource::Image(image_view_info.into()), - ); - - self - } - - /// Clears the render pass attachment of any existing data. - pub fn clear_color( - self, - attachment_idx: AttachmentIndex, - image: impl Into, - ) -> Self { - self.clear_color_value(attachment_idx, image, [0.0, 0.0, 0.0, 0.0]) - } - - /// Clears the render pass attachment of any existing data. - pub fn clear_color_value( - self, - attachment_idx: AttachmentIndex, - image: impl Into, - color: impl Into, - ) -> Self { - let image: AnyImageNode = image.into(); - let image_info = image.get(self.pass.graph); - let image_view_info: ImageViewInfo = image_info.into(); - - self.clear_color_value_as(attachment_idx, image, color, image_view_info) - } - - /// Clears the render pass attachment of any existing data. - pub fn clear_color_value_as( - mut self, - attachment_idx: AttachmentIndex, - image: impl Into, - color: impl Into, - image_view_info: impl Into, - ) -> Self { - let image = image.into(); - let image_view_info = image_view_info.into(); - let node_idx = image.index(); - let (_, sample_count) = self.image_info(node_idx); - - let color = color.into(); - - debug_assert!( - !self - .pass - .as_ref() - .execs - .last() - .unwrap() - .color_attachments - .contains_key(&attachment_idx), - "color attachment {attachment_idx} already attached" - ); - debug_assert!( - !self - .pass - .as_ref() - .execs - .last() - .unwrap() - .color_loads - .contains_key(&attachment_idx), - "color attachment {attachment_idx} already attached via load" - ); - - self.pass - .as_mut() - .execs - .last_mut() - .unwrap() - .color_clears - .insert( - attachment_idx, - ( - Attachment::new(image_view_info, sample_count, node_idx), - color, - ), - ); - - debug_assert!( - Attachment::are_compatible( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_resolves - .get(&attachment_idx) - .map(|(attachment, _)| *attachment), - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_clears - .get(&attachment_idx) - .map(|(attachment, _)| *attachment) - ), - "color attachment {attachment_idx} clear incompatible with existing resolve" - ); - debug_assert!( - Attachment::are_compatible( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_stores - .get(&attachment_idx) - .copied(), - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_clears - .get(&attachment_idx) - .map(|(attachment, _)| *attachment) - ), - "color attachment {attachment_idx} clear incompatible with existing store" - ); - - let mut image_access = AccessType::ColorAttachmentWrite; - let image_range = image_view_info.into(); - - // Upgrade existing read access to read-write - if let Some(accesses) = self - .pass - .as_mut() - .execs - .last_mut() - .unwrap() - .accesses - .get_mut(&node_idx) - { - for SubresourceAccess { - access, - subresource, - } in accesses - { - let access_image_range = *subresource.as_image().unwrap(); - if !image_subresource_range_intersects(access_image_range, image_range) { - continue; - } - - image_access = match *access { - AccessType::ColorAttachmentRead | AccessType::ColorAttachmentReadWrite => { - AccessType::ColorAttachmentReadWrite - } - AccessType::ColorAttachmentWrite => AccessType::ColorAttachmentWrite, - _ => continue, - }; - - *access = image_access; - - // If the clear access is a subset of the existing access range there is no need - // to push a new access - if image_subresource_range_contains(access_image_range, image_range) { - return self; - } - } - } - - self.pass - .push_node_access(image, image_access, Subresource::Image(image_range)); - - self - } - - /// Clears the render pass attachment of any existing data. - pub fn clear_depth_stencil(self, image: impl Into) -> Self { - self.clear_depth_stencil_value(image, 1.0, 0) - } - - /// Clears the render pass attachment of any existing data. - pub fn clear_depth_stencil_value( - self, - image: impl Into, - depth: f32, - stencil: u32, - ) -> Self { - let image: AnyImageNode = image.into(); - let image_info = image.get(self.pass.graph); - let image_view_info: ImageViewInfo = image_info.into(); - - self.clear_depth_stencil_value_as(image, depth, stencil, image_view_info) - } - - /// Clears the render pass attachment of any existing data. - pub fn clear_depth_stencil_value_as( - mut self, - image: impl Into, - depth: f32, - stencil: u32, - image_view_info: impl Into, - ) -> Self { - let image = image.into(); - let image_view_info = image_view_info.into(); - let node_idx = image.index(); - let (_, sample_count) = self.image_info(node_idx); - - debug_assert!( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .depth_stencil_attachment - .is_none(), - "depth/stencil attachment already attached" - ); - debug_assert!( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .depth_stencil_load - .is_none(), - "depth/stencil attachment already attached via load" - ); - - self.pass - .as_mut() - .execs - .last_mut() - .unwrap() - .depth_stencil_clear = Some(( - Attachment::new(image_view_info, sample_count, node_idx), - vk::ClearDepthStencilValue { depth, stencil }, - )); - - debug_assert!( - Attachment::are_compatible( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .depth_stencil_resolve - .map(|(attachment, ..)| attachment), - self.pass - .as_ref() - .execs - .last() - .unwrap() - .depth_stencil_clear - .map(|(attachment, _)| attachment) - ), - "depth/stencil attachment clear incompatible with existing resolve" - ); - debug_assert!( - Attachment::are_compatible( - self.pass.as_ref().execs.last().unwrap().depth_stencil_store, - self.pass - .as_ref() - .execs - .last() - .unwrap() - .depth_stencil_clear - .map(|(attachment, _)| attachment) - ), - "depth/stencil attachment clear incompatible with existing store" - ); - - let mut image_access = if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL) - { - AccessType::DepthStencilAttachmentWrite - } else if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH) - { - AccessType::DepthAttachmentWriteStencilReadOnly - } else { - debug_assert!( - image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::STENCIL) - ); - - AccessType::StencilAttachmentWriteDepthReadOnly - }; - let image_range = image_view_info.into(); - - // Upgrade existing read access to read-write - if let Some(accesses) = self - .pass - .as_mut() - .execs - .last_mut() - .unwrap() - .accesses - .get_mut(&node_idx) - { - for SubresourceAccess { - access, - subresource, - } in accesses - { - let access_image_range = *subresource.as_image().unwrap(); - if !image_subresource_range_intersects(access_image_range, image_range) { - continue; - } - - image_access = match *access { - AccessType::DepthAttachmentWriteStencilReadOnly => { - if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::STENCIL) - { - AccessType::DepthStencilAttachmentReadWrite - } else { - AccessType::DepthAttachmentWriteStencilReadOnly - } - } - AccessType::DepthStencilAttachmentRead => { - if !image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH) - { - AccessType::StencilAttachmentWriteDepthReadOnly - } else { - AccessType::DepthAttachmentWriteStencilReadOnly - } - } - AccessType::DepthStencilAttachmentWrite => { - AccessType::DepthStencilAttachmentWrite - } - AccessType::StencilAttachmentWriteDepthReadOnly => { - if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH) - { - AccessType::DepthStencilAttachmentReadWrite - } else { - AccessType::StencilAttachmentWriteDepthReadOnly - } - } - _ => continue, - }; - - *access = image_access; - - // If the clear access is a subset of the existing access range there is no need - // to push a new access - if image_subresource_range_contains(access_image_range, image_range) { - return self; - } - } - } - - self.pass - .push_node_access(image, image_access, Subresource::Image(image_range)); - - self - } - - fn image_info(&self, node_idx: NodeIndex) -> (vk::Format, SampleCount) { - let image_info = self.pass.graph.bindings[node_idx] - .as_driver_image() - .unwrap() - .info; - - (image_info.fmt, image_info.sample_count) - } - - /// Specifies `VK_ATTACHMENT_LOAD_OP_LOAD` for the render pass attachment, and loads an image - /// into the framebuffer. - pub fn load_color( - self, - attachment_idx: AttachmentIndex, - image: impl Into, - ) -> Self { - let image: AnyImageNode = image.into(); - let image_info = image.get(self.pass.graph); - let image_view_info: ImageViewInfo = image_info.into(); - - self.load_color_as(attachment_idx, image, image_view_info) - } - - /// Specifies `VK_ATTACHMENT_LOAD_OP_LOAD` for the render pass attachment, and loads an image - /// into the framebuffer. - pub fn load_color_as( - mut self, - attachment_idx: AttachmentIndex, - image: impl Into, - image_view_info: impl Into, - ) -> Self { - let image = image.into(); - let image_view_info = image_view_info.into(); - let node_idx = image.index(); - let (_, sample_count) = self.image_info(node_idx); - - debug_assert!( - !self - .pass - .as_ref() - .execs - .last() - .unwrap() - .color_attachments - .contains_key(&attachment_idx), - "color attachment {attachment_idx} already attached" - ); - debug_assert!( - !self - .pass - .as_ref() - .execs - .last() - .unwrap() - .color_clears - .contains_key(&attachment_idx), - "color attachment {attachment_idx} already attached via clear" - ); - - self.pass - .as_mut() - .execs - .last_mut() - .unwrap() - .color_loads - .insert( - attachment_idx, - Attachment::new(image_view_info, sample_count, node_idx), - ); - - debug_assert!( - Attachment::are_compatible( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_resolves - .get(&attachment_idx) - .map(|(attachment, _)| *attachment), - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_loads - .get(&attachment_idx) - .copied() - ), - "color attachment {attachment_idx} load incompatible with existing resolve" - ); - debug_assert!( - Attachment::are_compatible( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_stores - .get(&attachment_idx) - .copied(), - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_loads - .get(&attachment_idx) - .copied() - ), - "color attachment {attachment_idx} load incompatible with existing store" - ); - - let mut image_access = AccessType::ColorAttachmentRead; - let image_range = image_view_info.into(); - - // Upgrade existing write access to read-write - if let Some(accesses) = self - .pass - .as_mut() - .execs - .last_mut() - .unwrap() - .accesses - .get_mut(&node_idx) - { - for SubresourceAccess { - access, - subresource, - } in accesses - { - let access_image_range = *subresource.as_image().unwrap(); - if !image_subresource_range_intersects(access_image_range, image_range) { - continue; - } - - image_access = match *access { - AccessType::ColorAttachmentRead => AccessType::ColorAttachmentRead, - AccessType::ColorAttachmentReadWrite | AccessType::ColorAttachmentWrite => { - AccessType::ColorAttachmentReadWrite - } - _ => continue, - }; - - *access = image_access; - - // If the load access is a subset of the existing access range there is no need - // to push a new access - if image_subresource_range_contains(access_image_range, image_range) { - return self; - } - } - } - - self.pass - .push_node_access(image, image_access, Subresource::Image(image_range)); - - self - } - - /// Specifies `VK_ATTACHMENT_LOAD_OP_LOAD` for the render pass attachment, and loads an image - /// into the framebuffer. - pub fn load_depth_stencil(self, image: impl Into) -> Self { - let image: AnyImageNode = image.into(); - let image_info = image.get(self.pass.graph); - let image_view_info: ImageViewInfo = image_info.into(); - - self.load_depth_stencil_as(image, image_view_info) - } - - /// Specifies `VK_ATTACHMENT_LOAD_OP_LOAD` for the render pass attachment, and loads an image - /// into the framebuffer. - pub fn load_depth_stencil_as( - mut self, - image: impl Into, - image_view_info: impl Into, - ) -> Self { - let image = image.into(); - let image_view_info = image_view_info.into(); - let node_idx = image.index(); - let (_, sample_count) = self.image_info(node_idx); - - debug_assert!( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .depth_stencil_attachment - .is_none(), - "depth/stencil attachment already attached" - ); - debug_assert!( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .depth_stencil_clear - .is_none(), - "depth/stencil attachment already attached via clear" - ); - - self.pass - .as_mut() - .execs - .last_mut() - .unwrap() - .depth_stencil_load = Some(Attachment::new(image_view_info, sample_count, node_idx)); - - debug_assert!( - Attachment::are_compatible( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .depth_stencil_resolve - .map(|(attachment, ..)| attachment), - self.pass.as_ref().execs.last().unwrap().depth_stencil_load - ), - "depth/stencil attachment load incompatible with existing resolve" - ); - debug_assert!( - Attachment::are_compatible( - self.pass.as_ref().execs.last().unwrap().depth_stencil_store, - self.pass.as_ref().execs.last().unwrap().depth_stencil_load - ), - "depth/stencil attachment load incompatible with existing store" - ); - - let mut image_access = AccessType::DepthStencilAttachmentRead; - let image_range = image_view_info.into(); - - // Upgrade existing write access to read-write - if let Some(accesses) = self - .pass - .as_mut() - .execs - .last_mut() - .unwrap() - .accesses - .get_mut(&node_idx) - { - for SubresourceAccess { - access, - subresource, - } in accesses - { - let access_image_range = *subresource.as_image().unwrap(); - if !image_subresource_range_intersects(access_image_range, image_range) { - continue; - } - - image_access = match *access { - AccessType::DepthAttachmentWriteStencilReadOnly => { - AccessType::DepthAttachmentWriteStencilReadOnly - } - AccessType::DepthStencilAttachmentRead => { - AccessType::DepthStencilAttachmentRead - } - AccessType::DepthStencilAttachmentWrite => { - AccessType::DepthStencilAttachmentReadWrite - } - AccessType::StencilAttachmentWriteDepthReadOnly => { - AccessType::StencilAttachmentWriteDepthReadOnly - } - _ => continue, - }; - - *access = image_access; - - // If the load access is a subset of the existing access range there is no need - // to push a new access - if image_subresource_range_contains(access_image_range, image_range) { - return self; - } - } - } - - self.pass - .push_node_access(image, image_access, Subresource::Image(image_range)); - - self - } - - /// Begin recording a graphics command buffer. - pub fn record_subpass( - mut self, - func: impl FnOnce(Draw<'_>, Bindings<'_>) + Send + 'static, - ) -> Self { - let pipeline = Arc::clone( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .pipeline - .as_ref() - .unwrap() - .unwrap_graphic(), - ); - - self.pass.push_execute(move |device, cmd_buf, bindings| { - func( - Draw { - bindings, - cmd_buf, - device, - pipeline, - }, - bindings, - ); - }); - - self - } - - /// Resolves a multisample framebuffer to a non-multisample image for the render pass - /// attachment. - pub fn resolve_color( - self, - src_attachment_idx: AttachmentIndex, - dst_attachment_idx: AttachmentIndex, - image: impl Into, - ) -> Self { - let image: AnyImageNode = image.into(); - let image_info = image.get(self.pass.graph); - let image_view_info: ImageViewInfo = image_info.into(); - - self.resolve_color_as( - src_attachment_idx, - dst_attachment_idx, - image, - image_view_info, - ) - } - - /// Resolves a multisample framebuffer to a non-multisample image for the render pass - /// attachment. - pub fn resolve_color_as( - mut self, - src_attachment_idx: AttachmentIndex, - dst_attachment_idx: AttachmentIndex, - image: impl Into, - image_view_info: impl Into, - ) -> Self { - let image = image.into(); - let image_view_info = image_view_info.into(); - let node_idx = image.index(); - let (_, sample_count) = self.image_info(node_idx); - - self.pass - .as_mut() - .execs - .last_mut() - .unwrap() - .color_resolves - .insert( - dst_attachment_idx, - ( - Attachment::new(image_view_info, sample_count, node_idx), - src_attachment_idx, - ), - ); - - debug_assert!( - Attachment::are_compatible( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_attachments - .get(&dst_attachment_idx) - .copied(), - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_resolves - .get(&dst_attachment_idx) - .map(|(attachment, _)| *attachment) - ), - "color attachment {dst_attachment_idx} resolve incompatible with existing attachment" - ); - debug_assert!( - Attachment::are_compatible( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_clears - .get(&dst_attachment_idx) - .map(|(attachment, _)| *attachment), - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_resolves - .get(&dst_attachment_idx) - .map(|(attachment, _)| *attachment) - ), - "color attachment {dst_attachment_idx} resolve incompatible with existing clear" - ); - debug_assert!( - Attachment::are_compatible( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_loads - .get(&dst_attachment_idx) - .copied(), - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_resolves - .get(&dst_attachment_idx) - .map(|(attachment, _)| *attachment) - ), - "color attachment {dst_attachment_idx} resolve incompatible with existing load" - ); - - let mut image_access = AccessType::ColorAttachmentWrite; - let image_range = image_view_info.into(); - - // Upgrade existing read access to read-write - if let Some(accesses) = self - .pass - .as_mut() - .execs - .last_mut() - .unwrap() - .accesses - .get_mut(&node_idx) - { - for SubresourceAccess { - access, - subresource, - } in accesses - { - let access_image_range = *subresource.as_image().unwrap(); - if !image_subresource_range_intersects(access_image_range, image_range) { - continue; - } - - image_access = match *access { - AccessType::ColorAttachmentRead | AccessType::ColorAttachmentReadWrite => { - AccessType::ColorAttachmentReadWrite - } - AccessType::ColorAttachmentWrite => AccessType::ColorAttachmentWrite, - _ => continue, - }; - - *access = image_access; - - // If the resolve access is a subset of the existing access range there is no need - // to push a new access - if image_subresource_range_contains(access_image_range, image_range) { - return self; - } - } - } - - self.pass - .push_node_access(image, image_access, Subresource::Image(image_range)); - - self - } - - /// Resolves a multisample framebuffer to a non-multisample image for the render pass - /// attachment. - pub fn resolve_depth_stencil( - self, - dst_attachment_idx: AttachmentIndex, - image: impl Into, - depth_mode: Option, - stencil_mode: Option, - ) -> Self { - let image: AnyImageNode = image.into(); - let image_info = image.get(self.pass.graph); - let image_view_info: ImageViewInfo = image_info.into(); - - self.resolve_depth_stencil_as( - dst_attachment_idx, - image, - image_view_info, - depth_mode, - stencil_mode, - ) - } - - /// Resolves a multisample framebuffer to a non-multisample image for the render pass - /// attachment. - pub fn resolve_depth_stencil_as( - mut self, - dst_attachment_idx: AttachmentIndex, - image: impl Into, - image_view_info: impl Into, - depth_mode: Option, - stencil_mode: Option, - ) -> Self { - let image = image.into(); - let image_view_info = image_view_info.into(); - let node_idx = image.index(); - let (_, sample_count) = self.image_info(node_idx); - - self.pass - .as_mut() - .execs - .last_mut() - .unwrap() - .depth_stencil_resolve = Some(( - Attachment::new(image_view_info, sample_count, node_idx), - dst_attachment_idx, - depth_mode, - stencil_mode, - )); - - let mut image_access = if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL) - { - AccessType::DepthStencilAttachmentWrite - } else if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH) - { - AccessType::DepthAttachmentWriteStencilReadOnly - } else { - debug_assert!( - image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::STENCIL) - ); - - AccessType::StencilAttachmentWriteDepthReadOnly - }; - let image_range = image_view_info.into(); - - // Upgrade existing read access to read-write - if let Some(accesses) = self - .pass - .as_mut() - .execs - .last_mut() - .unwrap() - .accesses - .get_mut(&node_idx) - { - for SubresourceAccess { - access, - subresource, - } in accesses - { - let access_image_range = *subresource.as_image().unwrap(); - if !image_subresource_range_intersects(access_image_range, image_range) { - continue; - } - - image_access = match *access { - AccessType::DepthAttachmentWriteStencilReadOnly => { - if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::STENCIL) - { - AccessType::DepthStencilAttachmentReadWrite - } else { - AccessType::DepthAttachmentWriteStencilReadOnly - } - } - AccessType::DepthStencilAttachmentRead => { - if !image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH) - { - AccessType::StencilAttachmentWriteDepthReadOnly - } else { - AccessType::DepthStencilAttachmentReadWrite - } - } - AccessType::DepthStencilAttachmentWrite => { - AccessType::DepthStencilAttachmentWrite - } - AccessType::StencilAttachmentWriteDepthReadOnly => { - if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH) - { - AccessType::DepthStencilAttachmentReadWrite - } else { - AccessType::StencilAttachmentWriteDepthReadOnly - } - } - _ => continue, - }; - - *access = image_access; - - // If the resolve access is a subset of the existing access range there is no need - // to push a new access - if image_subresource_range_contains(access_image_range, image_range) { - return self; - } - } - } - - self.pass - .push_node_access(image, image_access, Subresource::Image(image_range)); - - self - } - - /// Sets a particular depth/stencil mode. - pub fn set_depth_stencil(mut self, depth_stencil: DepthStencilMode) -> Self { - let pass = self.pass.as_mut(); - let exec = pass.execs.last_mut().unwrap(); - - assert!(exec.depth_stencil.is_none()); - - exec.depth_stencil = Some(depth_stencil); - - self - } - - /// Sets multiview view and correlation masks. - /// - /// See [`VkRenderPassMultiviewCreateInfo`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkRenderPassMultiviewCreateInfo.html#_description). - pub fn set_multiview(mut self, view_mask: u32, correlated_view_mask: u32) -> Self { - let pass = self.pass.as_mut(); - let exec = pass.execs.last_mut().unwrap(); - - exec.correlated_view_mask = correlated_view_mask; - exec.view_mask = view_mask; - - self - } - - /// Sets the [`renderArea`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkRenderPassBeginInfo.html#_c_specification) - /// field when beginning a render pass. - /// - /// NOTE: Setting this value will cause the viewport and scissor to be unset, which is not the default - /// behavior. When this value is set you should call `set_viewport` and `set_scissor` on the subpass. - /// - /// If not set, this value defaults to the first loaded, resolved, or stored attachment dimensions and - /// sets the viewport and scissor to the same values, with a `0..1` depth if not specified by - /// `set_depth_stencil`. - pub fn set_render_area(mut self, x: i32, y: i32, width: u32, height: u32) -> Self { - self.pass.as_mut().execs.last_mut().unwrap().render_area = Some(Area { - height, - width, - x, - y, - }); - - self - } - - /// Specifies `VK_ATTACHMENT_STORE_OP_STORE` for the render pass attachment, and stores the - /// rendered pixels into an image. - pub fn store_color( - self, - attachment_idx: AttachmentIndex, - image: impl Into, - ) -> Self { - let image: AnyImageNode = image.into(); - let image_info = image.get(self.pass.graph); - let image_view_info: ImageViewInfo = image_info.into(); - - self.store_color_as(attachment_idx, image, image_view_info) - } - - /// Specifies `VK_ATTACHMENT_STORE_OP_STORE` for the render pass attachment, and stores the - /// rendered pixels into an image. - pub fn store_color_as( - mut self, - attachment_idx: AttachmentIndex, - image: impl Into, - image_view_info: impl Into, - ) -> Self { - let image = image.into(); - let image_view_info = image_view_info.into(); - let node_idx = image.index(); - let (_, sample_count) = self.image_info(node_idx); - - self.pass - .as_mut() - .execs - .last_mut() - .unwrap() - .color_stores - .insert( - attachment_idx, - Attachment::new(image_view_info, sample_count, node_idx), - ); - - debug_assert!( - Attachment::are_compatible( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_attachments - .get(&attachment_idx) - .copied(), - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_stores - .get(&attachment_idx) - .copied() - ), - "color attachment {attachment_idx} store incompatible with existing attachment" - ); - debug_assert!( - Attachment::are_compatible( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_clears - .get(&attachment_idx) - .map(|(attachment, _)| *attachment), - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_stores - .get(&attachment_idx) - .copied() - ), - "color attachment {attachment_idx} store incompatible with existing clear" - ); - debug_assert!( - Attachment::are_compatible( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_loads - .get(&attachment_idx) - .copied(), - self.pass - .as_ref() - .execs - .last() - .unwrap() - .color_stores - .get(&attachment_idx) - .copied() - ), - "color attachment {attachment_idx} store incompatible with existing load" - ); - - let mut image_access = AccessType::ColorAttachmentWrite; - let image_range = image_view_info.into(); - - // Upgrade existing read access to read-write - if let Some(accesses) = self - .pass - .as_mut() - .execs - .last_mut() - .unwrap() - .accesses - .get_mut(&node_idx) - { - for SubresourceAccess { - access, - subresource, - } in accesses - { - let access_image_range = *subresource.as_image().unwrap(); - if !image_subresource_range_intersects(access_image_range, image_range) { - continue; - } - - image_access = match *access { - AccessType::ColorAttachmentRead | AccessType::ColorAttachmentReadWrite => { - AccessType::ColorAttachmentReadWrite - } - AccessType::ColorAttachmentWrite => AccessType::ColorAttachmentWrite, - _ => continue, - }; - - *access = image_access; - - // If the store access is a subset of the existing access range there is no need - // to push a new access - if image_subresource_range_contains(access_image_range, image_range) { - return self; - } - } - } - - self.pass - .push_node_access(image, image_access, Subresource::Image(image_range)); - - self - } - - /// Specifies `VK_ATTACHMENT_STORE_OP_STORE` for the render pass attachment, and stores the - /// rendered pixels into an image. - pub fn store_depth_stencil(self, image: impl Into) -> Self { - let image: AnyImageNode = image.into(); - let image_info = image.get(self.pass.graph); - let image_view_info: ImageViewInfo = image_info.into(); - - self.store_depth_stencil_as(image, image_view_info) - } - - /// Specifies `VK_ATTACHMENT_STORE_OP_STORE` for the render pass attachment, and stores the - /// rendered pixels into an image. - /// - /// _NOTE:_ Order matters, call store after clear or load. - pub fn store_depth_stencil_as( - mut self, - image: impl Into, - image_view_info: impl Into, - ) -> Self { - let image = image.into(); - let image_view_info = image_view_info.into(); - let node_idx = image.index(); - let (_, sample_count) = self.image_info(node_idx); - - self.pass - .as_mut() - .execs - .last_mut() - .unwrap() - .depth_stencil_store = Some(Attachment::new(image_view_info, sample_count, node_idx)); - - debug_assert!( - Attachment::are_compatible( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .depth_stencil_attachment, - self.pass.as_ref().execs.last().unwrap().depth_stencil_store - ), - "depth/stencil attachment store incompatible with existing attachment" - ); - debug_assert!( - Attachment::are_compatible( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .depth_stencil_clear - .map(|(attachment, _)| attachment), - self.pass.as_ref().execs.last().unwrap().depth_stencil_store - ), - "depth/stencil attachment store incompatible with existing clear" - ); - debug_assert!( - Attachment::are_compatible( - self.pass.as_ref().execs.last().unwrap().depth_stencil_load, - self.pass.as_ref().execs.last().unwrap().depth_stencil_store - ), - "depth/stencil attachment store incompatible with existing load" - ); - - let mut image_access = if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL) - { - AccessType::DepthStencilAttachmentWrite - } else if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH) - { - AccessType::DepthAttachmentWriteStencilReadOnly - } else { - debug_assert!( - image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::STENCIL) - ); - - AccessType::StencilAttachmentWriteDepthReadOnly - }; - let image_range = image_view_info.into(); - - // Upgrade existing read access to read-write - if let Some(accesses) = self - .pass - .as_mut() - .execs - .last_mut() - .unwrap() - .accesses - .get_mut(&node_idx) - { - for SubresourceAccess { - access, - subresource, - } in accesses - { - let access_image_range = *subresource.as_image().unwrap(); - if !image_subresource_range_intersects(access_image_range, image_range) { - continue; - } - - image_access = match *access { - AccessType::DepthAttachmentWriteStencilReadOnly => { - if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::STENCIL) - { - AccessType::DepthStencilAttachmentReadWrite - } else { - AccessType::DepthAttachmentWriteStencilReadOnly - } - } - AccessType::DepthStencilAttachmentRead => { - if !image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH) - { - AccessType::StencilAttachmentWriteDepthReadOnly - } else { - AccessType::DepthStencilAttachmentReadWrite - } - } - AccessType::DepthStencilAttachmentWrite => { - AccessType::DepthStencilAttachmentWrite - } - AccessType::StencilAttachmentWriteDepthReadOnly => { - if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH) - { - AccessType::DepthStencilAttachmentReadWrite - } else { - AccessType::StencilAttachmentWriteDepthReadOnly - } - } - _ => continue, - }; - - *access = image_access; - - // If the store access is a subset of the existing access range there is no need - // to push a new access - if image_subresource_range_contains(access_image_range, image_range) { - return self; - } - } - } - - self.pass - .push_node_access(image, image_access, Subresource::Image(image_range)); - - self - } -} - -impl PipelinePassRef<'_, RayTracePipeline> { - /// Begin recording a ray tracing command buffer. - pub fn record_ray_trace( - mut self, - func: impl FnOnce(RayTrace<'_>, Bindings<'_>) + Send + 'static, - ) -> Self { - let pipeline = Arc::clone( - self.pass - .as_ref() - .execs - .last() - .unwrap() - .pipeline - .as_ref() - .unwrap() - .unwrap_ray_trace(), - ); - - #[cfg(debug_assertions)] - let dynamic_stack_size = pipeline.info.dynamic_stack_size; - - self.pass.push_execute(move |device, cmd_buf, bindings| { - func( - RayTrace { - cmd_buf, - device, - - #[cfg(debug_assertions)] - dynamic_stack_size, - - pipeline, - }, - bindings, - ); - }); - - self - } -} - -/// Recording interface for ray tracing commands. -/// -/// This structure provides a strongly-typed set of methods which allow ray trace shader code to be -/// executed. An instance of `RayTrace` is provided to the closure parameter of -/// [`PipelinePassRef::record_ray_trace`] which may be accessed by binding a [`RayTracePipeline`] to -/// a render pass. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```no_run -/// # use std::sync::Arc; -/// # use ash::vk; -/// # use vk_graph::driver::DriverError; -/// # use vk_graph::driver::device::{Device, DeviceInfo}; -/// # use vk_graph::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; -/// # use vk_graph::driver::shader::Shader; -/// # use vk_graph::RenderGraph; -/// # fn main() -> Result<(), DriverError> { -/// # let device = Arc::new(Device::new(DeviceInfo::default())?); -/// # let info = RayTracePipelineInfo::default(); -/// # let my_miss_code = [0u8; 1]; -/// # let my_ray_trace_pipeline = Arc::new(RayTracePipeline::create(&device, info, -/// [Shader::new_miss(my_miss_code.as_slice())], -/// [RayTraceShaderGroup::new_general(0)], -/// )?); -/// # let mut my_graph = RenderGraph::default(); -/// my_graph.begin_cmd_buf().with_name("my ray trace pass") -/// .bind_pipeline(&my_ray_trace_pipeline) -/// .record_ray_trace(move |ray_trace, bindings| { -/// // During this closure we have access to the ray trace methods! -/// }); -/// # Ok(()) } -/// ``` -pub struct RayTrace<'a> { - cmd_buf: vk::CommandBuffer, - device: &'a Device, - - #[cfg(debug_assertions)] - dynamic_stack_size: bool, - - pipeline: Arc, -} - -impl RayTrace<'_> { - /// Updates push constants. - /// - /// Push constants represent a high speed path to modify constant data in pipelines that is - /// expected to outperform memory-backed resource updates. - /// - /// Push constant values can be updated incrementally, causing shader stages to read the new - /// data for push constants modified by this command, while still reading the previous data for - /// push constants not modified by this command. - /// - /// # Device limitations - /// - /// See - /// [`device.physical_device.props.limits.max_push_constants_size`](vk::PhysicalDeviceLimits) - /// for the limits of the current device. You may also check [gpuinfo.org] for a listing of - /// reported limits on other devices. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # vk_shader_macros::glsl!(target: vulkan1_2, r#" - /// #version 460 - /// #pragma shader_stage(closest) - /// - /// layout(push_constant) uniform PushConstants { - /// layout(offset = 0) uint some_val; - /// } push_constants; - /// - /// void main() { - /// // TODO: Add bindings to write things! - /// } - /// # "#); - /// ``` - /// - /// ```no_run - /// # use std::sync::Arc; - /// # use ash::vk; - /// # use vk_graph::driver::DriverError; - /// # use vk_graph::driver::device::{Device, DeviceInfo}; - /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; - /// # use vk_graph::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; - /// # use vk_graph::driver::shader::Shader; - /// # use vk_graph::RenderGraph; - /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); - /// # let shader = [0u8; 1]; - /// # let info = RayTracePipelineInfo::default(); - /// # let my_miss_code = [0u8; 1]; - /// # let my_ray_trace_pipeline = Arc::new(RayTracePipeline::create(&device, info, - /// # [Shader::new_miss(my_miss_code.as_slice())], - /// # [RayTraceShaderGroup::new_general(0)], - /// # )?); - /// # let rgen_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; - /// # let hit_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; - /// # let miss_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; - /// # let call_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; - /// # let mut my_graph = RenderGraph::default(); - /// my_graph.begin_cmd_buf().with_name("draw a cornell box") - /// .bind_pipeline(&my_ray_trace_pipeline) - /// .record_ray_trace(move |ray_trace, bindings| { - /// ray_trace.push_constants(&[0xcb]) - /// .trace_rays(&rgen_sbt, &hit_sbt, &miss_sbt, &call_sbt, 320, 200, 1); - /// }); - /// # Ok(()) } - /// ``` - /// - /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all - pub fn push_constants(&self, data: &[u8]) -> &Self { - self.push_constants_offset(0, data) - } - - /// Updates push constants starting at the given `offset`. - /// - /// Behaves similary to [`RayTrace::push_constants`] except that `offset` describes the position - /// at which `data` updates the push constants of the currently bound pipeline. This may be used - /// to update a subset or single field of previously set push constant data. - /// - /// # Device limitations - /// - /// See - /// [`device.physical_device.props.limits.max_push_constants_size`](vk::PhysicalDeviceLimits) - /// for the limits of the current device. You may also check [gpuinfo.org] for a listing of - /// reported limits on other devices. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # vk_shader_macros::glsl!(target: vulkan1_2, r#" - /// #version 460 - /// #pragma shader_stage(closest) - /// - /// layout(push_constant) uniform PushConstants { - /// layout(offset = 0) uint some_val1; - /// layout(offset = 4) uint some_val2; - /// } push_constants; - /// - /// void main() - /// { - /// // TODO: Add bindings to write things! - /// } - /// # "#); - /// ``` - /// - /// ```no_run - /// # use std::sync::Arc; - /// # use ash::vk; - /// # use vk_graph::driver::DriverError; - /// # use vk_graph::driver::device::{Device, DeviceInfo}; - /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; - /// # use vk_graph::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; - /// # use vk_graph::driver::shader::Shader; - /// # use vk_graph::RenderGraph; - /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); - /// # let shader = [0u8; 1]; - /// # let info = RayTracePipelineInfo::default(); - /// # let my_miss_code = [0u8; 1]; - /// # let my_ray_trace_pipeline = Arc::new(RayTracePipeline::create(&device, info, - /// # [Shader::new_miss(my_miss_code.as_slice())], - /// # [RayTraceShaderGroup::new_general(0)], - /// # )?); - /// # let rgen_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; - /// # let hit_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; - /// # let miss_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; - /// # let call_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; - /// # let mut my_graph = RenderGraph::default(); - /// my_graph.begin_cmd_buf().with_name("draw a cornell box") - /// .bind_pipeline(&my_ray_trace_pipeline) - /// .record_ray_trace(move |ray_trace, bindings| { - /// ray_trace.push_constants(&[0xcb, 0xff]) - /// .trace_rays(&rgen_sbt, &hit_sbt, &miss_sbt, &call_sbt, 320, 200, 1) - /// .push_constants_offset(4, &[0xae]) - /// .trace_rays(&rgen_sbt, &hit_sbt, &miss_sbt, &call_sbt, 320, 200, 1); - /// }); - /// # Ok(()) } - /// ``` - /// - /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all - #[profiling::function] - pub fn push_constants_offset(&self, offset: u32, data: &[u8]) -> &Self { - for push_const in self.pipeline.push_constants.iter() { - let push_const_end = push_const.offset + push_const.size; - let data_end = offset + data.len() as u32; - let end = data_end.min(push_const_end); - let start = offset.max(push_const.offset); - - if end > start { - trace!( - " push constants {:?} {}..{}", - push_const.stage_flags, start, end - ); - - unsafe { - self.device.cmd_push_constants( - self.cmd_buf, - self.pipeline.layout, - push_const.stage_flags, - start, - &data[(start - offset) as usize..(end - offset) as usize], - ); - } - } - } - self - } - - /// Set the stack size dynamically for a ray trace pipeline. - /// - /// See - /// [`RayTracePipelineInfo::dynamic_stack_size`](crate::driver::ray_trace::RayTracePipelineInfo::dynamic_stack_size) - /// and - /// [`vkCmdSetRayTracingPipelineStackSizeKHR`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCmdSetRayTracingPipelineStackSizeKHR.html). - #[profiling::function] - pub fn set_stack_size(&self, pipeline_stack_size: u32) -> &Self { - #[cfg(debug_assertions)] - assert!(self.dynamic_stack_size); - - unsafe { - Device::expect_ray_trace_ext(self.device) - .cmd_set_ray_tracing_pipeline_stack_size(self.cmd_buf, pipeline_stack_size); - } - - self - } - - // TODO: If the rayTraversalPrimitiveCulling or rayQuery features are enabled, the SkipTrianglesKHR and SkipAABBsKHR ray flags can be specified when tracing a ray. SkipTrianglesKHR and SkipAABBsKHR are mutually exclusive. - - /// Ray traces using the currently-bound [`RayTracePipeline`] and the given shader binding - /// tables. - /// - /// Shader binding tables must be constructed according to this [example]. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// # use std::sync::Arc; - /// # use ash::vk; - /// # use vk_graph::driver::DriverError; - /// # use vk_graph::driver::device::{Device, DeviceInfo}; - /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; - /// # use vk_graph::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; - /// # use vk_graph::driver::shader::Shader; - /// # use vk_graph::RenderGraph; - /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); - /// # let shader = [0u8; 1]; - /// # let info = RayTracePipelineInfo::default(); - /// # let my_miss_code = [0u8; 1]; - /// # let my_ray_trace_pipeline = Arc::new(RayTracePipeline::create(&device, info, - /// # [Shader::new_miss(my_miss_code.as_slice())], - /// # [RayTraceShaderGroup::new_general(0)], - /// # )?); - /// # let rgen_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; - /// # let hit_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; - /// # let miss_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; - /// # let call_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; - /// # let mut my_graph = RenderGraph::default(); - /// my_graph.begin_cmd_buf().with_name("draw a cornell box") - /// .bind_pipeline(&my_ray_trace_pipeline) - /// .record_ray_trace(move |ray_trace, bindings| { - /// ray_trace.trace_rays(&rgen_sbt, &hit_sbt, &miss_sbt, &call_sbt, 320, 200, 1); - /// }); - /// # Ok(()) } - /// ``` - /// - /// [example]: https://github.com/attackgoat/vk-graph/blob/master/examples/ray_trace.rs - #[allow(clippy::too_many_arguments)] - #[profiling::function] - pub fn trace_rays( - &self, - raygen_shader_binding_table: &vk::StridedDeviceAddressRegionKHR, - miss_shader_binding_table: &vk::StridedDeviceAddressRegionKHR, - hit_shader_binding_table: &vk::StridedDeviceAddressRegionKHR, - callable_shader_binding_table: &vk::StridedDeviceAddressRegionKHR, - width: u32, - height: u32, - depth: u32, - ) -> &Self { - unsafe { - Device::expect_ray_trace_ext(self.device).cmd_trace_rays( - self.cmd_buf, - raygen_shader_binding_table, - miss_shader_binding_table, - hit_shader_binding_table, - callable_shader_binding_table, - width, - height, - depth, - ); - } - - self - } - - /// Ray traces using the currently-bound [`RayTracePipeline`] and the given shader binding - /// tables. - /// - /// `indirect_device_address` is a [buffer device address] which is a pointer to a - /// [`vk::TraceRaysIndirectCommandKHR`] structure containing the trace ray parameters. - /// - /// See [`vkCmdTraceRaysIndirectKHR`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCmdTraceRaysIndirectKHR.html). - /// - /// [buffer device address]: Buffer::device_address - #[profiling::function] - pub fn trace_rays_indirect( - &self, - raygen_shader_binding_table: &vk::StridedDeviceAddressRegionKHR, - miss_shader_binding_table: &vk::StridedDeviceAddressRegionKHR, - hit_shader_binding_table: &vk::StridedDeviceAddressRegionKHR, - callable_shader_binding_table: &vk::StridedDeviceAddressRegionKHR, - indirect_device_address: vk::DeviceAddress, - ) -> &Self { - unsafe { - Device::expect_ray_trace_ext(self.device).cmd_trace_rays_indirect( - self.cmd_buf, - raygen_shader_binding_table, - miss_shader_binding_table, - hit_shader_binding_table, - callable_shader_binding_table, - indirect_device_address, - ) - } - - self - } -} - -/// Describes a portion of a resource which is bound. -#[derive(Clone, Copy, Debug)] -pub enum Subresource { - /// Acceleration structures are bound whole. - AccelerationStructure, - - /// Images may be partially bound. - Image(vk::ImageSubresourceRange), - - /// Buffers may be partially bound. - Buffer(BufferSubresourceRange), -} - -impl Subresource { - pub(super) fn as_image(&self) -> Option<&vk::ImageSubresourceRange> { - if let Self::Image(subresource) = self { - Some(subresource) - } else { - None - } - } -} - -impl From<()> for Subresource { - fn from(_: ()) -> Self { - Self::AccelerationStructure - } -} - -impl From for Subresource { - fn from(subresource: vk::ImageSubresourceRange) -> Self { - Self::Image(subresource) - } -} - -impl From for Subresource { - fn from(subresource: BufferSubresourceRange) -> Self { - Self::Buffer(subresource) - } -} - -#[derive(Clone, Copy, Debug)] -pub(super) struct SubresourceAccess { - pub access: AccessType, - pub subresource: Subresource, -} - -/// Allows for a resource to be reinterpreted as differently formatted data. -pub trait View: Node -where - Self::Information: Copy, - Self::Subresource: Into, -{ - /// The information about the resource interpretation. - type Information; - - /// The portion of the resource which is bound. - type Subresource; -} - -impl View for AccelerationStructureNode { - type Information = (); - type Subresource = (); -} - -impl View for AccelerationStructureLeaseNode { - type Information = (); - type Subresource = (); -} - -impl View for AnyAccelerationStructureNode { - type Information = (); - type Subresource = (); -} - -impl View for AnyBufferNode { - type Information = BufferSubresourceRange; - type Subresource = BufferSubresourceRange; -} - -impl View for AnyImageNode { - type Information = ImageViewInfo; - type Subresource = vk::ImageSubresourceRange; -} - -impl View for BufferLeaseNode { - type Information = BufferSubresourceRange; - type Subresource = BufferSubresourceRange; -} - -impl View for BufferNode { - type Information = BufferSubresourceRange; - type Subresource = BufferSubresourceRange; -} - -impl View for ImageLeaseNode { - type Information = ImageViewInfo; - type Subresource = vk::ImageSubresourceRange; -} - -impl View for ImageNode { - type Information = ImageViewInfo; - type Subresource = vk::ImageSubresourceRange; -} - -impl View for SwapchainImageNode { - type Information = ImageViewInfo; - type Subresource = vk::ImageSubresourceRange; -} - -/// Describes the interpretation of a resource. -#[derive(Debug)] -pub enum ViewType { - /// Acceleration structures are not reinterpreted. - AccelerationStructure, - - /// Images may be interpreted as differently formatted images. - Image(ImageViewInfo), - - /// Buffers may be interpreted as subregions of the same buffer. - Buffer(Range), -} - -impl ViewType { - pub(super) fn as_buffer(&self) -> Option<&Range> { - match self { - Self::Buffer(view_info) => Some(view_info), - _ => None, - } - } - - pub(super) fn as_image(&self) -> Option<&ImageViewInfo> { - match self { - Self::Image(view_info) => Some(view_info), - _ => None, - } - } -} - -impl From<()> for ViewType { - fn from(_: ()) -> Self { - Self::AccelerationStructure - } -} - -impl From for ViewType { - fn from(subresource: BufferSubresourceRange) -> Self { - Self::Buffer(subresource.start..subresource.end) - } -} - -impl From for ViewType { - fn from(info: ImageViewInfo) -> Self { - Self::Image(info) - } -} - -impl From> for ViewType { - fn from(range: Range) -> Self { - Self::Buffer(range) - } -} diff --git a/src/resolver.rs b/src/resolver.rs index b40dc811..27ba3339 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1,8 +1,8 @@ use { super::{ - Area, Attachment, Binding, Bindings, ExecutionPipeline, Node, NodeIndex, Pass, RenderGraph, + Area, Attachment, Binding, Bindings, Command, ExecutionPipeline, Graph, Node, NodeIndex, + cmd_ref::{Subresource, SubresourceAccess}, node::SwapchainImageNode, - pass_ref::{Subresource, SubresourceAccess}, }, crate::{ driver::{ @@ -88,7 +88,7 @@ impl AccessCache { .flat_map(move |node_idx| self.dependent_passes(node_idx, end_pass_idx)) } - fn update(&mut self, graph: &RenderGraph, end_pass_idx: usize) { + fn update(&mut self, graph: &Graph, end_pass_idx: usize) { self.binding_count = graph.bindings.len(); let cache_len = self.binding_count * end_pass_idx; @@ -112,7 +112,7 @@ impl AccessCache { nodes.fill(true); nodes.resize(self.binding_count, true); - for (pass_idx, pass) in graph.passes[0..end_pass_idx].iter().enumerate() { + for (pass_idx, pass) in graph.cmds[0..end_pass_idx].iter().enumerate() { let pass_start = pass_idx * self.binding_count; let mut read_count = 0; @@ -175,13 +175,13 @@ impl Drop for PhysicalPass { /// #[derive(Debug)] pub struct Resolver { - pub(super) graph: RenderGraph, + pub(super) graph: Graph, physical_passes: Vec, } impl Resolver { - pub(super) fn new(graph: RenderGraph) -> Self { - let physical_passes = Vec::with_capacity(graph.passes.len()); + pub(super) fn new(graph: Graph) -> Self { + let physical_passes = Vec::with_capacity(graph.cmds.len()); Self { graph, @@ -190,8 +190,8 @@ impl Resolver { } #[profiling::function] - fn allow_merge_passes(lhs: &Pass, rhs: &Pass) -> bool { - fn first_graphic_pipeline(pass: &Pass) -> Option<&GraphicPipeline> { + fn allow_merge_passes(lhs: &Command, rhs: &Command) -> bool { + fn first_graphic_pipeline(pass: &Command) -> Option<&GraphicPipeline> { pass.execs .first() .and_then(|exec| exec.pipeline.as_ref().map(ExecutionPipeline::as_graphic)) @@ -388,7 +388,7 @@ impl Resolver { fn begin_render_pass( cmd_buf: &CommandBuffer, bindings: &[Binding], - pass: &Pass, + pass: &Command, physical_pass: &mut PhysicalPass, render_area: Area, ) -> Result<(), DriverError> { @@ -685,14 +685,14 @@ impl Resolver { /// will stall the GPU while the fences are waited on. It is preferrable to wait a few frame so /// that the fences will have already been signalled. pub fn is_resolved(&self) -> bool { - self.graph.passes.is_empty() + self.graph.cmds.is_empty() } #[allow(clippy::type_complexity)] #[profiling::function] fn lease_descriptor_pool

( pool: &mut P, - pass: &Pass, + pass: &Command, ) -> Result>, DriverError> where P: Pool, @@ -799,7 +799,7 @@ impl Resolver { where P: Pool, { - let pass = &self.graph.passes[pass_idx]; + let pass = &self.graph.cmds[pass_idx]; let (mut color_attachment_count, mut depth_stencil_attachment_count) = (0, 0); for exec in &pass.execs { color_attachment_count = color_attachment_count @@ -1277,7 +1277,7 @@ impl Resolver { } // Second look in previous passes of the entire render graph - for prev_subpass in self.graph.passes[0..pass_idx] + for prev_subpass in self.graph.cmds[0..pass_idx] .iter() .rev() .flat_map(|pass| pass.execs.iter().rev()) @@ -1667,7 +1667,7 @@ impl Resolver { // At the time this function runs the pass will already have been optimized into a // larger pass made out of anything that might have been merged into it - so we // only care about one pass at a time here - let pass = &mut self.graph.passes[pass_idx]; + let pass = &mut self.graph.cmds[pass_idx]; trace!("leasing [{pass_idx}: {}]", pass.name()); @@ -1747,13 +1747,13 @@ impl Resolver { #[profiling::function] fn merge_scheduled_passes(&mut self, schedule: &mut Vec) { thread_local! { - static PASSES: RefCell>> = Default::default(); + static PASSES: RefCell>> = Default::default(); } PASSES.with_borrow_mut(|passes| { debug_assert!(passes.is_empty()); - passes.extend(self.graph.passes.drain(..).map(Some)); + passes.extend(self.graph.cmds.drain(..).map(Some)); let mut idx = 0; @@ -1814,12 +1814,12 @@ impl Resolver { pass.name = Some(name); - self.graph.passes.push(pass); + self.graph.cmds.push(pass); idx += 1 + end - start; } // Reschedule passes - schedule.truncate(self.graph.passes.len()); + schedule.truncate(self.graph.cmds.len()); for (idx, pass_idx) in schedule.iter_mut().enumerate() { *pass_idx = idx; @@ -1827,7 +1827,7 @@ impl Resolver { // Add the remaining passes back into the graph for later for pass in passes.drain(..).flatten() { - self.graph.passes.push(pass); + self.graph.cmds.push(pass); } }); } @@ -1851,7 +1851,7 @@ impl Resolver { let node_idx = node.index(); let mut res = Default::default(); - 'pass: for pass in self.graph.passes.iter() { + 'pass: for pass in self.graph.cmds.iter() { for exec in pass.execs.iter() { if exec.accesses.contains_key(&node_idx) { res |= pass @@ -2131,7 +2131,7 @@ impl Resolver { fn record_image_layout_transitions( cmd_buf: &CommandBuffer, bindings: &mut [Binding], - pass: &mut Pass, + pass: &mut Command, ) { use std::slice::from_ref; @@ -2357,11 +2357,11 @@ impl Resolver { debug_assert!(self.graph.bindings.get(node_idx).is_some()); - if self.graph.passes.is_empty() { + if self.graph.cmds.is_empty() { return Ok(()); } - let end_pass_idx = self.graph.passes.len(); + let end_pass_idx = self.graph.cmds.len(); self.record_node_passes(pool, cmd_buf, node_idx, end_pass_idx) } @@ -2421,7 +2421,7 @@ impl Resolver { self.lease_scheduled_resources(pool, &schedule.passes)?; for pass_idx in schedule.passes.iter().copied() { - let pass = &mut self.graph.passes[pass_idx]; + let pass = &mut self.graph.cmds[pass_idx]; profiling::scope!("Pass", &pass.name); @@ -2532,7 +2532,7 @@ impl Resolver { } thread_local! { - static PASSES: RefCell> = Default::default(); + static PASSES: RefCell> = Default::default(); } PASSES.with_borrow_mut(|passes| { @@ -2541,10 +2541,10 @@ impl Resolver { // We have to keep the bindings and pipelines alive until the gpu is done schedule.passes.sort_unstable(); while let Some(schedule_idx) = schedule.passes.pop() { - debug_assert!(!self.graph.passes.is_empty()); + debug_assert!(!self.graph.cmds.is_empty()); - while let Some(pass) = self.graph.passes.pop() { - let pass_idx = self.graph.passes.len(); + while let Some(pass) = self.graph.cmds.pop() { + let pass_idx = self.graph.cmds.len(); if pass_idx == schedule_idx { // This was a scheduled pass - store it! @@ -2564,7 +2564,7 @@ impl Resolver { debug_assert!(self.physical_passes.is_empty()); // Put the other passes back for future resolves - self.graph.passes.extend(passes.drain(..).rev()); + self.graph.cmds.extend(passes.drain(..).rev()); }); log::trace!("Recorded passes"); @@ -2582,7 +2582,7 @@ impl Resolver { where P: Pool + Pool, { - if self.graph.passes.is_empty() { + if self.graph.cmds.is_empty() { return Ok(()); } @@ -2593,16 +2593,16 @@ impl Resolver { SCHEDULE.with_borrow_mut(|schedule| { schedule .access_cache - .update(&self.graph, self.graph.passes.len()); + .update(&self.graph, self.graph.cmds.len()); schedule.passes.clear(); - schedule.passes.extend(0..self.graph.passes.len()); + schedule.passes.extend(0..self.graph.cmds.len()); - self.record_scheduled_passes(pool, cmd_buf, schedule, self.graph.passes.len()) + self.record_scheduled_passes(pool, cmd_buf, schedule, self.graph.cmds.len()) }) } #[profiling::function] - fn render_area(bindings: &[Binding], pass: &Pass) -> Area { + fn render_area(bindings: &[Binding], pass: &Command) -> Area { // set_render_area was not specified so we're going to guess using the minimum common // attachment extents let first_exec = pass.execs.first().unwrap(); @@ -2735,7 +2735,7 @@ impl Resolver { { trace!( " pass [{pass_idx}: {}] is dependent", - self.graph.passes[pass_idx].name() + self.graph.cmds[pass_idx].name() ); debug_assert!(unscheduled[pass_idx]); @@ -2771,7 +2771,7 @@ impl Resolver { trace!( " pass [{pass_idx}: {}] is dependent", - self.graph.passes[pass_idx].name() + self.graph.cmds[pass_idx].name() ); for node_idx in schedule.access_cache.dependent_nodes(pass_idx) { @@ -2798,7 +2798,7 @@ impl Resolver { .passes .iter() .copied() - .map(|idx| format!("[{}: {}]", idx, self.graph.passes[idx].name())) + .map(|idx| format!("[{}: {}]", idx, self.graph.cmds[idx].name())) .collect::>() .join(", ") ); @@ -2817,18 +2817,18 @@ impl Resolver { unscheduled .iter() .copied() - .map(|idx| format!("[{}: {}]", idx, self.graph.passes[idx].name())) + .map(|idx| format!("[{}: {}]", idx, self.graph.cmds[idx].name())) .collect::>() .join(", ") ); } - if end_pass_idx < self.graph.passes.len() { + if end_pass_idx < self.graph.cmds.len() { // These passes existing on the graph but are not being considered right // now because we've been told to stop work at the "end_pass_idx" point trace!( "ignoring: {}", - self.graph.passes[end_pass_idx..] + self.graph.cmds[end_pass_idx..] .iter() .enumerate() .map(|(idx, pass)| format!( @@ -2974,7 +2974,7 @@ impl Resolver { fn write_descriptor_sets( cmd_buf: &CommandBuffer, bindings: &[Binding], - pass: &Pass, + pass: &Command, physical_pass: &PhysicalPass, ) -> Result<(), DriverError> { struct IndexWrite<'a> { diff --git a/src/swapchain.rs b/src/swapchain.rs index fa8a9e25..c387edbe 100644 --- a/src/swapchain.rs +++ b/src/swapchain.rs @@ -1,10 +1,10 @@ use { - super::{Bind, Binding, RenderGraph, SwapchainImageNode}, + super::{Bind, Binding, Graph, SwapchainImageNode}, crate::driver::swapchain::SwapchainImage, }; -impl Bind<&mut RenderGraph, SwapchainImageNode> for SwapchainImage { - fn bind(self, graph: &mut RenderGraph) -> SwapchainImageNode { +impl Bind<&mut Graph, SwapchainImageNode> for SwapchainImage { + fn bind(self, graph: &mut Graph) -> SwapchainImageNode { // We will return a new node let res = SwapchainImageNode::new(graph.bindings.len()); From 346c359290423823ad2b89f41e5f745257423c98 Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 19 Feb 2026 12:29:22 -0500 Subject: [PATCH 13/86] clean-up --- README.md | 3 +- contrib/vk-graph-egui/src/lib.rs | 21 +-- contrib/vk-graph-fx/src/bitmap_font.rs | 17 ++- contrib/vk-graph-fx/src/image_loader.rs | 2 +- contrib/vk-graph-fx/src/presenter.rs | 2 +- contrib/vk-graph-fx/src/transition.rs | 2 +- contrib/vk-graph-imgui/src/lib.rs | 27 ++-- examples/font_bmp.rs | 2 +- examples/fuzzer.rs | 6 +- examples/mip_graphic.rs | 11 +- examples/msaa.rs | 4 +- examples/multipass.rs | 12 +- examples/ray_omni.rs | 8 +- examples/shader-toy/src/main.rs | 4 +- examples/skeletal-anim/src/main.rs | 6 +- examples/triangle.rs | 4 +- examples/vertex_layout.rs | 4 +- examples/vr/src/main.rs | 22 +-- examples/vsm_omni.rs | 48 +++--- src/bind.rs | 74 ++++----- src/cmd_ref/compute.rs | 68 +-------- src/cmd_ref/graphic.rs | 193 +++++------------------- src/cmd_ref/ray_trace.rs | 89 ++--------- src/edge.rs | 70 ++++----- src/lib.rs | 55 +++++-- src/swapchain.rs | 33 ---- 26 files changed, 271 insertions(+), 516 deletions(-) delete mode 100644 src/swapchain.rs diff --git a/README.md b/README.md index 14523bc8..62a630bf 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ [![Docs.rs](https://docs.rs/vk-graph/badge.svg)](https://docs.rs/vk-graph) [![LoC](https://tokei.rs/b1/github/attackgoat/vk-graph?category=code)](https://github.com/attackgoat/vk-graph) -_vk-graph_ is a high-performance Vulkan driver with automatic resource management and execution. +_vk-graph_ is a high-performance Vulkan graphics driver with automatic resource management and +execution. ```toml [dependencies] diff --git a/contrib/vk-graph-egui/src/lib.rs b/contrib/vk-graph-egui/src/lib.rs index 7c4eaa50..eca7d8ac 100644 --- a/contrib/vk-graph-egui/src/lib.rs +++ b/contrib/vk-graph-egui/src/lib.rs @@ -276,13 +276,11 @@ impl Egui { let num_indices = mesh.indices.len() as u32; - let clip_x = (clip_rect.min.x * pixels_per_point) as i32; - let clip_y = (clip_rect.min.y * pixels_per_point) as i32; + let x = (clip_rect.min.x * pixels_per_point) as i32; + let y = (clip_rect.min.y * pixels_per_point) as i32; - let clip_width = - ((clip_rect.max.x - clip_rect.min.x) * pixels_per_point) as u32; - let clip_height = - ((clip_rect.max.y - clip_rect.min.y) * pixels_per_point) as u32; + let width = ((clip_rect.max.x - clip_rect.min.x) * pixels_per_point) as u32; + let height = ((clip_rect.max.y - clip_rect.min.y) * pixels_per_point) as u32; render_graph .begin_cmd() @@ -295,10 +293,13 @@ impl Egui { .store_color(0, target) .record_pipeline(move |pipeline, _| { pipeline - .bind_index_buffer(idx_buf, vk::IndexType::UINT32) - .bind_vertex_buffer(vert_buf) - .push_constants(cast_slice(&[push_constants])) - .set_scissor(clip_x, clip_y, clip_width, clip_height) + .bind_index_buffer(idx_buf, 0, vk::IndexType::UINT32) + .bind_vertex_buffer(0, vert_buf, 0) + .push_constants(0, cast_slice(&[push_constants])) + .set_scissor(&vk::Rect2D { + offset: vk::Offset2D { x, y }, + extent: vk::Extent2D { width, height }, + }) .draw_indexed(num_indices, 1, 0, 0, 0); }); } diff --git a/contrib/vk-graph-fx/src/bitmap_font.rs b/contrib/vk-graph-fx/src/bitmap_font.rs index c8801249..d3730075 100644 --- a/contrib/vk-graph-fx/src/bitmap_font.rs +++ b/contrib/vk-graph-fx/src/bitmap_font.rs @@ -219,16 +219,19 @@ impl BitmapFont { pass.record_pipeline(move |pipeline, _| { if let Some((x, y, width, height)) = scissor { - pipeline.set_scissor(x, y, width, height); + pipeline.set_scissor(&vk::Rect2D { + offset: vk::Offset2D { x, y }, + extent: vk::Extent2D { width, height }, + }); } pipeline - .push_constants(cast_slice(&transform.to_cols_array())) - .push_constants_offset(64, &(1.0 / image_info.width as f32).to_ne_bytes()) - .push_constants_offset(68, &(1.0 / image_info.height as f32).to_ne_bytes()) - .push_constants_offset(80, &color_to_unorm(color.solid())) - .push_constants_offset(96, &color_to_unorm(color.outline())) - .bind_vertex_buffer(vertex_buf) + .push_constants(0, cast_slice(&transform.to_cols_array())) + .push_constants(64, &(1.0 / image_info.width as f32).to_ne_bytes()) + .push_constants(68, &(1.0 / image_info.height as f32).to_ne_bytes()) + .push_constants(80, &color_to_unorm(color.solid())) + .push_constants(96, &color_to_unorm(color.outline())) + .bind_vertex_buffer(0, vertex_buf, 0) .draw(vertex_count, 1, 0, 0); }); } diff --git a/contrib/vk-graph-fx/src/image_loader.rs b/contrib/vk-graph-fx/src/image_loader.rs index 22bb3355..bad15d68 100644 --- a/contrib/vk-graph-fx/src/image_loader.rs +++ b/contrib/vk-graph-fx/src/image_loader.rs @@ -211,7 +211,7 @@ impl ImageLoader { .write_descriptor(1, temp_image) .record_pipeline(move |compute, _| { compute - .push_constants(&(pixel_buf_stride >> 2).to_ne_bytes()) + .push_constants(0, &(pixel_buf_stride >> 2).to_ne_bytes()) .dispatch(dispatch_x, dispatch_y, 1); }) .end_cmd() diff --git a/contrib/vk-graph-fx/src/presenter.rs b/contrib/vk-graph-fx/src/presenter.rs index 365a7861..a773c7b1 100644 --- a/contrib/vk-graph-fx/src/presenter.rs +++ b/contrib/vk-graph-fx/src/presenter.rs @@ -132,7 +132,7 @@ impl GraphicPresenter { .record_pipeline(move |pipeline, _| { // Draw a quad with implicit vertices (no buffer) pipeline - .push_constants(cast_slice(&transform.to_cols_array())) + .push_constants(0, cast_slice(&transform.to_cols_array())) .draw(6, 1, 0, 0); }); } diff --git a/contrib/vk-graph-fx/src/transition.rs b/contrib/vk-graph-fx/src/transition.rs index 0d386a37..a46a8fdb 100644 --- a/contrib/vk-graph-fx/src/transition.rs +++ b/contrib/vk-graph-fx/src/transition.rs @@ -476,7 +476,7 @@ impl TransitionPipeline { .read_descriptor(1, b_image) .write_descriptor(2, dest_image) .record_pipeline(move |compute, _| { - compute.push_constants(push_consts.as_slice()); + compute.push_constants(0, &push_consts); compute.dispatch(dest_info.width, dest_info.height, 1); }); } diff --git a/contrib/vk-graph-imgui/src/lib.rs b/contrib/vk-graph-imgui/src/lib.rs index 63b0884a..1e0c6122 100644 --- a/contrib/vk-graph-imgui/src/lib.rs +++ b/contrib/vk-graph-imgui/src/lib.rs @@ -195,10 +195,10 @@ impl ImGui { .store_color(0, image) .record_pipeline(move |pipeline, _| { pipeline - .push_constants_offset(0, &window_width.to_ne_bytes()) - .push_constants_offset(4, &window_height.to_ne_bytes()) - .bind_index_buffer(index_buf, vk::IndexType::UINT16) - .bind_vertex_buffer(vertex_buf); + .push_constants(0, &window_width.to_ne_bytes()) + .push_constants(4, &window_height.to_ne_bytes()) + .bind_index_buffer(index_buf, 0, vk::IndexType::UINT16) + .bind_vertex_buffer(0, vertex_buf, 0); for (index_count, clip_rect, first_index, vertex_offset) in draw_cmds { let clip_rect = [ @@ -211,13 +211,18 @@ impl ImGui { let y = clip_rect[1].floor() as i32; let width = (clip_rect[2] - clip_rect[0]).ceil() as u32; let height = (clip_rect[3] - clip_rect[1]).ceil() as u32; - pipeline.set_scissor(x, y, width, height).draw_indexed( - index_count as _, - 1, - first_index as _, - vertex_offset as _, - 0, - ); + pipeline + .set_scissor(&vk::Rect2D { + offset: vk::Offset2D { x, y }, + extent: vk::Extent2D { width, height }, + }) + .draw_indexed( + index_count as _, + 1, + first_index as _, + vertex_offset as _, + 0, + ); } }); } diff --git a/examples/font_bmp.rs b/examples/font_bmp.rs index 81263c91..7a92268d 100644 --- a/examples/font_bmp.rs +++ b/examples/font_bmp.rs @@ -143,7 +143,7 @@ fn main() -> anyhow::Result<()> { .write_descriptor(0, image_node) .record_pipeline(move |compute, _| { compute - .push_constants(&elapsed_time.as_secs_f32().to_ne_bytes()) + .push_constants(0, &elapsed_time.as_secs_f32().to_ne_bytes()) .dispatch(frame.width.div_ceil(subgroup_size), frame.height, 1); }); diff --git a/examples/fuzzer.rs b/examples/fuzzer.rs index 1772c725..357105b1 100644 --- a/examples/fuzzer.rs +++ b/examples/fuzzer.rs @@ -388,7 +388,7 @@ fn record_pipeline_array_bind(frame: &mut FrameContext, pool: &mut HashPool) { .read_descriptor((0, [4]), images[4]) .record_pipeline(|compute, _| { compute - .push_constants(&0f32.to_ne_bytes()) + .push_constants(0, &0f32.to_ne_bytes()) .dispatch(64, 64, 1); }); } @@ -464,7 +464,7 @@ fn record_pipeline_bindless(frame: &mut FrameContext, pool: &mut HashPool) { .write_descriptor((0, [4]), images[4]) .record_pipeline(|compute, _| { compute - .push_constants(&5u32.to_ne_bytes()) + .push_constants(0, &5u32.to_ne_bytes()) .dispatch(64, 64, 1); }); } @@ -592,7 +592,7 @@ fn record_graphic_bindless(frame: &mut FrameContext, pool: &mut HashPool) { .store_color(0, image) .record_pipeline(|pipeline, _| { pipeline - .push_constants(&5u32.to_ne_bytes()) + .push_constants(0, &5u32.to_ne_bytes()) .draw(1, 1, 0, 0); }); } diff --git a/examples/mip_graphic.rs b/examples/mip_graphic.rs index 1787c4a0..267a4083 100644 --- a/examples/mip_graphic.rs +++ b/examples/mip_graphic.rs @@ -169,10 +169,13 @@ fn fill_mip_levels(device: &Arc, image: &Arc) -> Result<(), Drive ) .record_pipeline(|pipeline, _| { pipeline - .push_constants(bytes_of(&PushConstants { - a: vec3(0.0, 1.0, 1.0).extend(f32::NAN), - b: vec3(1.0, 0.0, 1.0).extend(f32::NAN), - })) + .push_constants( + 0, + bytes_of(&PushConstants { + a: vec3(0.0, 1.0, 1.0).extend(f32::NAN), + b: vec3(1.0, 0.0, 1.0).extend(f32::NAN), + }), + ) .draw(6, 1, 0, 0); }); } diff --git a/examples/msaa.rs b/examples/msaa.rs index e8bd2cb6..5d8697f3 100644 --- a/examples/msaa.rs +++ b/examples/msaa.rs @@ -158,8 +158,8 @@ fn main() -> anyhow::Result<()> { pass.record_pipeline(move |pipeline, _| { pipeline - .bind_vertex_buffer(cube_vertex_buf) - .push_constants(bytes_of(&world_transform)) + .bind_vertex_buffer(0, cube_vertex_buf, 0) + .push_constants(0, bytes_of(&world_transform)) .draw(cube_mesh.vertex_count, 1, 0, 0); }); })?; diff --git a/examples/multipass.rs b/examples/multipass.rs index d27b9278..23521f6b 100644 --- a/examples/multipass.rs +++ b/examples/multipass.rs @@ -99,9 +99,9 @@ fn main() -> anyhow::Result<()> { .store_depth_stencil(depth_stencil) .record_pipeline(move |pipeline, _| { pipeline - .bind_index_buffer(index_buf, vk::IndexType::UINT16) - .bind_vertex_buffer(vertex_buf) - .push_constants(bytes_of(&obj_pos)) + .bind_index_buffer(index_buf, 0, vk::IndexType::UINT16) + .bind_vertex_buffer(0, vertex_buf, 0) + .push_constants(0, bytes_of(&obj_pos)) .draw_indexed(funky_shape.index_count, 1, 0, 0, 0); }); @@ -132,9 +132,9 @@ fn main() -> anyhow::Result<()> { .store_color(0, frame.swapchain_image) .record_pipeline(move |pipeline, _| { pipeline - .bind_index_buffer(index_buf, vk::IndexType::UINT16) - .bind_vertex_buffer(vertex_buf) - .push_constants(&push_const_data) + .bind_index_buffer(index_buf, 0, vk::IndexType::UINT16) + .bind_vertex_buffer(0, vertex_buf, 0) + .push_constants(0, &push_const_data) .draw_indexed(funky_shape.index_count, 1, 0, 0, 0); }); diff --git a/examples/ray_omni.rs b/examples/ray_omni.rs index ca62675e..1f305a68 100644 --- a/examples/ray_omni.rs +++ b/examples/ray_omni.rs @@ -109,13 +109,13 @@ fn main() -> anyhow::Result<()> { .store_color(0, frame.swapchain_image) .record_pipeline(move |pipeline, _| { pipeline - .bind_index_buffer(model_mesh_index_buf, vk::IndexType::UINT32) - .bind_vertex_buffer(model_mesh_vertex_buf) + .bind_index_buffer(model_mesh_index_buf, 0, vk::IndexType::UINT32) + .bind_vertex_buffer(0, model_mesh_vertex_buf, 0) .draw_indexed(model_mesh.index_count, 1, 0, 0, 0); pipeline - .bind_index_buffer(ground_mesh_index_buf, vk::IndexType::UINT32) - .bind_vertex_buffer(ground_mesh_vertex_buf) + .bind_index_buffer(ground_mesh_index_buf, 0, vk::IndexType::UINT32) + .bind_vertex_buffer(0, ground_mesh_vertex_buf, 0) .draw_indexed(ground_mesh.index_count, 1, 0, 0, 0); }); })?; diff --git a/examples/shader-toy/src/main.rs b/examples/shader-toy/src/main.rs index 34089450..1eea8b27 100644 --- a/examples/shader-toy/src/main.rs +++ b/examples/shader-toy/src/main.rs @@ -300,7 +300,7 @@ fn main() -> anyhow::Result<()> { .store_color(0, output) .record_pipeline(move |pipeline, _| { pipeline - .push_constants(bytes_of(&push_consts)) + .push_constants(0, bytes_of(&push_consts)) .draw(6, 1, 0, 0); }); @@ -314,7 +314,7 @@ fn main() -> anyhow::Result<()> { .store_color(0, input) .record_pipeline(move |pipeline, _| { pipeline - .push_constants(bytes_of(&push_consts)) + .push_constants(0, bytes_of(&push_consts)) .draw(6, 1, 0, 0); }); diff --git a/examples/skeletal-anim/src/main.rs b/examples/skeletal-anim/src/main.rs index 99c48511..e852a666 100644 --- a/examples/skeletal-anim/src/main.rs +++ b/examples/skeletal-anim/src/main.rs @@ -139,9 +139,9 @@ fn main() -> Result<(), WindowError> { .clear_depth_stencil(depth_image) .record_pipeline(move |pipeline, _| { pipeline - .bind_index_buffer(index_buf, vk::IndexType::UINT16) - .bind_vertex_buffer(vertex_buf) - .push_constants(bytes_of(&Mat4::IDENTITY)) + .bind_index_buffer(index_buf, 0, vk::IndexType::UINT16) + .bind_vertex_buffer(0, vertex_buf, 0) + .push_constants(0, bytes_of(&Mat4::IDENTITY)) .draw_indexed(character.index_count, 1, 0, 0, 0); }); }) diff --git a/examples/triangle.rs b/examples/triangle.rs index 932a17be..4ec401a1 100644 --- a/examples/triangle.rs +++ b/examples/triangle.rs @@ -93,8 +93,8 @@ fn main() -> Result<(), WindowError> { .store_color(0, frame.swapchain_image) .record_pipeline(move |pipeline, _| { pipeline - .bind_index_buffer(index_node, vk::IndexType::UINT16) - .bind_vertex_buffer(vertex_node) + .bind_index_buffer(index_node, 0, vk::IndexType::UINT16) + .bind_vertex_buffer(0, vertex_node, 0) .draw_indexed(3, 1, 0, 0, 0); }); }) diff --git a/examples/vertex_layout.rs b/examples/vertex_layout.rs index c68d1375..a828dd3f 100644 --- a/examples/vertex_layout.rs +++ b/examples/vertex_layout.rs @@ -108,7 +108,9 @@ fn draw_triangle( .store_color(0, frame.swapchain_image) .access_node(vertex_buf, AccessType::VertexBuffer) .record_pipeline(move |pipeline, _| { - pipeline.bind_vertex_buffer(vertex_buf).draw(3, 1, 0, 0); + pipeline + .bind_vertex_buffer(0, vertex_buf, 0) + .draw(3, 1, 0, 0); }); } diff --git a/examples/vr/src/main.rs b/examples/vr/src/main.rs index 740a5571..ae320b43 100644 --- a/examples/vr/src/main.rs +++ b/examples/vr/src/main.rs @@ -144,7 +144,9 @@ fn main() -> anyhow::Result<()> { let woolly_mammoth_dir = example_assets_dir.join("woolly-mammoth"); if metadata(&lincoln_hands_dir).is_err() { - panic!("Asset submodule missing! You must first initialize the submodules and then update them using:\ngit submodule init\ngit submodule update"); + panic!( + "Asset submodule missing! You must first initialize the submodules and then update them using:\ngit submodule init\ngit submodule update" + ); } // Load a model and textures for the left hand @@ -380,9 +382,9 @@ fn main() -> anyhow::Result<()> { ) .record_pipeline(move |pipeline, _| { pipeline - .bind_index_buffer(index_buf, vk::IndexType::UINT32) - .bind_vertex_buffer(vertex_buf) - .push_constants(bytes_of(&push_consts)) + .bind_index_buffer(index_buf, 0, vk::IndexType::UINT32) + .bind_vertex_buffer(0, vertex_buf, 0) + .push_constants(0, bytes_of(&push_consts)) .draw_indexed(lincoln_hand_left.index_count, 1, 0, 0, 0); }); } @@ -425,9 +427,9 @@ fn main() -> anyhow::Result<()> { ) .record_pipeline(move |pipeline, _| { pipeline - .bind_index_buffer(index_buf, vk::IndexType::UINT32) - .bind_vertex_buffer(vertex_buf) - .push_constants(bytes_of(&push_consts)) + .bind_index_buffer(index_buf, 0, vk::IndexType::UINT32) + .bind_vertex_buffer(0, vertex_buf, 0) + .push_constants(0, bytes_of(&push_consts)) .draw_indexed(lincoln_hand_right.index_count, 1, 0, 0, 0); }); } @@ -463,9 +465,9 @@ fn main() -> anyhow::Result<()> { ) .record_pipeline(move |pipeline, _| { pipeline - .bind_index_buffer(index_buf, vk::IndexType::UINT32) - .bind_vertex_buffer(vertex_buf) - .push_constants(bytes_of(&push_consts)) + .bind_index_buffer(index_buf, 0, vk::IndexType::UINT32) + .bind_vertex_buffer(0, vertex_buf, 0) + .push_constants(0, bytes_of(&push_consts)) .draw_indexed(lincoln_hand_right.index_count, 1, 0, 0, 0); }); } diff --git a/examples/vsm_omni.rs b/examples/vsm_omni.rs index 91aa663a..0815da77 100644 --- a/examples/vsm_omni.rs +++ b/examples/vsm_omni.rs @@ -261,13 +261,13 @@ fn main() -> anyhow::Result<()> { .store_depth_stencil(depth_image) .record_pipeline(move |pipeline, _| { pipeline - .bind_index_buffer(model_mesh_index_buf, vk::IndexType::UINT32) - .bind_vertex_buffer(model_mesh_vertex_buf) - .push_constants(cast_slice(&model_transform)) + .bind_index_buffer(model_mesh_index_buf, 0, vk::IndexType::UINT32) + .bind_vertex_buffer(0, model_mesh_vertex_buf, 0) + .push_constants(0, cast_slice(&model_transform)) .draw_indexed(model_mesh.index_count, 1, 0, 0, 0) - .bind_index_buffer(cube_mesh_index_buf, vk::IndexType::UINT32) - .bind_vertex_buffer(cube_mesh_vertex_buf) - .push_constants(cast_slice(&cube_transform)) + .bind_index_buffer(cube_mesh_index_buf, 0, vk::IndexType::UINT32) + .bind_vertex_buffer(0, cube_mesh_vertex_buf, 0) + .push_constants(0, cast_slice(&cube_transform)) .draw_indexed(cube_mesh.index_count, 1, 0, 0, 0); }); } else { @@ -289,13 +289,13 @@ fn main() -> anyhow::Result<()> { .clear_depth_stencil(shadow_depth_image) .record_pipeline(move |pipeline, _| { pipeline - .bind_index_buffer(model_shadow_index_buf, vk::IndexType::UINT32) - .bind_vertex_buffer(model_shadow_vertex_buf) - .push_constants(cast_slice(&model_transform)) + .bind_index_buffer(model_shadow_index_buf, 0, vk::IndexType::UINT32) + .bind_vertex_buffer(0, model_shadow_vertex_buf, 0) + .push_constants(0, cast_slice(&model_transform)) .draw_indexed(model_shadow.index_count, 1, 0, 0, 0) - .bind_index_buffer(cube_shadow_index_buf, vk::IndexType::UINT32) - .bind_vertex_buffer(cube_shadow_vertex_buf) - .push_constants(cast_slice(&cube_transform)) + .bind_index_buffer(cube_shadow_index_buf, 0, vk::IndexType::UINT32) + .bind_vertex_buffer(0, cube_shadow_vertex_buf, 0) + .push_constants(0, cast_slice(&cube_transform)) .draw_indexed(cube_shadow.index_count, 1, 0, 0, 0); }); } else { @@ -343,13 +343,13 @@ fn main() -> anyhow::Result<()> { .clear_depth_stencil(shadow_depth_image) .record_pipeline(move |pipeline, _| { pipeline - .bind_index_buffer(model_shadow_index_buf, vk::IndexType::UINT32) - .bind_vertex_buffer(model_shadow_vertex_buf) - .push_constants(cast_slice(&model_transform)) + .bind_index_buffer(model_shadow_index_buf, 0, vk::IndexType::UINT32) + .bind_vertex_buffer(0, model_shadow_vertex_buf, 0) + .push_constants(0, cast_slice(&model_transform)) .draw_indexed(model_shadow.index_count, 1, 0, 0, 0) - .bind_index_buffer(cube_shadow_index_buf, vk::IndexType::UINT32) - .bind_vertex_buffer(cube_shadow_vertex_buf) - .push_constants(cast_slice(&cube_transform)) + .bind_index_buffer(cube_shadow_index_buf, 0, vk::IndexType::UINT32) + .bind_vertex_buffer(0, cube_shadow_vertex_buf, 0) + .push_constants(0, cast_slice(&cube_transform)) .draw_indexed(cube_shadow.index_count, 1, 0, 0, 0); }); } @@ -410,13 +410,13 @@ fn main() -> anyhow::Result<()> { .clear_depth_stencil(depth_image) .record_pipeline(move |pipeline, _| { pipeline - .bind_index_buffer(model_mesh_index_buf, vk::IndexType::UINT32) - .bind_vertex_buffer(model_mesh_vertex_buf) - .push_constants(cast_slice(&model_transform)) + .bind_index_buffer(model_mesh_index_buf, 0, vk::IndexType::UINT32) + .bind_vertex_buffer(0, model_mesh_vertex_buf, 0) + .push_constants(0, cast_slice(&model_transform)) .draw_indexed(model_mesh.index_count, 1, 0, 0, 0) - .bind_index_buffer(cube_mesh_index_buf, vk::IndexType::UINT32) - .bind_vertex_buffer(cube_mesh_vertex_buf) - .push_constants(cast_slice(&cube_transform)) + .bind_index_buffer(cube_mesh_index_buf, 0, vk::IndexType::UINT32) + .bind_vertex_buffer(0, cube_mesh_vertex_buf, 0) + .push_constants(0, cast_slice(&cube_transform)) .draw_indexed(cube_mesh.index_count, 1, 0, 0, 0); }); } diff --git a/src/bind.rs b/src/bind.rs index 9d78989e..e8b628ba 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -1,7 +1,7 @@ use { super::{ AccelerationStructureLeaseNode, AccelerationStructureNode, BufferLeaseNode, BufferNode, - Graph, ImageLeaseNode, ImageNode, + Graph, ImageLeaseNode, ImageNode, SwapchainImageNode, }, crate::{ driver::{ @@ -13,49 +13,6 @@ use { std::{fmt::Debug, sync::Arc}, }; -// #[derive(Debug)] -// pub enum AnyBufferBinding<'a> { -// Buffer(&'a Arc), -// BufferLease(&'a Lease), -// } - -// impl<'a> From<&'a Arc> for AnyBufferBinding<'a> { -// fn from(buffer: &'a Arc) -> Self { -// Self::Buffer(buffer) -// } -// } - -// impl<'a> From<&'a Lease> for AnyBufferBinding<'a> { -// fn from(buffer: &'a Lease) -> Self { -// Self::BufferLease(buffer) -// } -// } - -// #[derive(Debug)] -// pub enum AnyImageBinding<'a> { -// Image(&'a Arc), -// ImageLease(&'a Lease), -// SwapchainImage(&'a SwapchainImage), -// } - -// impl<'a> From<&'a Arc> for AnyImageBinding<'a> { -// fn from(image: &'a Arc) -> Self { -// Self::Image(image) -// } -// } - -// impl<'a> From<&'a Lease> for AnyImageBinding<'a> { -// fn from(image: &'a Lease) -> Self { -// Self::ImageLease(image) -// } -// } - -// impl<'a> From<&'a SwapchainImage> for AnyImageBinding<'a> { -// fn from(image: &'a SwapchainImage) -> Self { -// Self::SwapchainImage(image) -// } -// } - /// A trait for resources which may be bound to a `Graph`. /// /// See [`Graph::bind_node`] and @@ -104,6 +61,19 @@ impl Binding { }) } + pub(super) fn as_swapchain_image(&self) -> Option<&SwapchainImage> { + if let Self::SwapchainImage(binding, true) = self { + Some(binding) + } else if let Self::SwapchainImage(_, false) = self { + // User code might try this - but it is a programmer error + // to access a binding after it has been unbound so dont + None + } else { + // The private code in this module should prevent this branch + unreachable!(); + } + } + pub(super) fn is_bound(&self) -> bool { match self { Self::AccelerationStructure(_, is_bound) => *is_bound, @@ -129,6 +99,20 @@ impl Binding { } } +impl Bind<&mut Graph, SwapchainImageNode> for SwapchainImage { + fn bind(self, graph: &mut Graph) -> SwapchainImageNode { + // We will return a new node + let res = SwapchainImageNode::new(graph.bindings.len()); + + //trace!("Node {}: {:?}", res.idx, &self); + + let binding = Binding::SwapchainImage(Box::new(self), true); + graph.bindings.push(binding); + + res + } +} + macro_rules! bind { ($name:ident) => { paste::paste! { @@ -290,7 +274,7 @@ bind_lease!(Buffer); /// /// See [`Graph::unbind_node`] for details. pub trait Unbind { - /// Unbinds the resource from a graph-like object. + /// Unbinds the resource from a graph. /// /// Returns the original Binding object. fn unbind(self, graph: &mut Graph) -> Binding; diff --git a/src/cmd_ref/compute.rs b/src/cmd_ref/compute.rs index 1469a1e0..3e2b9b42 100644 --- a/src/cmd_ref/compute.rs +++ b/src/cmd_ref/compute.rs @@ -277,71 +277,7 @@ impl Compute<'_> { /// my_graph.begin_cmd().with_name("compute the ultimate question") /// .bind_pipeline(&my_compute_pipeline) /// .record_pipeline(move |compute, bindings| { - /// compute.push_constants(&[42]) - /// .dispatch(1, 1, 1); - /// }); - /// # Ok(()) } - /// ``` - /// - /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all - pub fn push_constants(&self, data: &[u8]) -> &Self { - self.push_constants_offset(0, data) - } - - /// Updates push constants starting at the given `offset`. - /// - /// Behaves similary to [`Compute::push_constants`] except that `offset` describes the position - /// at which `data` updates the push constants of the currently bound pipeline. This may be used - /// to update a subset or single field of previously set push constant data. - /// - /// # Device limitations - /// - /// See - /// [`device.physical_device.props.limits.max_push_constants_size`](vk::PhysicalDeviceLimits) - /// for the limits of the current device. You may also check [gpuinfo.org] for a listing of - /// reported limits on other devices. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # vk_shader_macros::glsl!(r#" - /// #version 450 - /// #pragma shader_stage(compute) - /// - /// layout(push_constant) uniform PushConstants { - /// layout(offset = 0) uint some_val1; - /// layout(offset = 4) uint some_val2; - /// } push_constants; - /// - /// void main() { - /// // TODO: Add bindings to read/write things! - /// } - /// # "#); - /// ``` - /// - /// ```no_run - /// # use std::sync::Arc; - /// # use ash::vk; - /// # use vk_graph::driver::DriverError; - /// # use vk_graph::driver::device::{Device, DeviceInfo}; - /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; - /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; - /// # use vk_graph::driver::shader::{Shader}; - /// # use vk_graph::Graph; - /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); - /// # let info = ComputePipelineInfo::default(); - /// # let shader = Shader::new_compute([0u8; 1].as_slice()); - /// # let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); - /// # let mut my_graph = Graph::default(); - /// my_graph.begin_cmd().with_name("calculate the wow factor") - /// .bind_pipeline(&my_compute_pipeline) - /// .record_pipeline(move |compute, bindings| { - /// compute.push_constants(&[0x00, 0x00]) - /// .dispatch(1, 1, 1) - /// .push_constants_offset(4, &[0xff]) + /// compute.push_constants(0, &[42]) /// .dispatch(1, 1, 1); /// }); /// # Ok(()) } @@ -349,7 +285,7 @@ impl Compute<'_> { /// /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all #[profiling::function] - pub fn push_constants_offset(&self, offset: u32, data: &[u8]) -> &Self { + pub fn push_constants(&self, offset: u32, data: &[u8]) -> &Self { if let Some(push_const) = self.pipeline.push_constants { // Determine the range of the overall pipline push constants which overlap with `data` let push_const_end = push_const.offset + push_const.size; diff --git a/src/cmd_ref/graphic.rs b/src/cmd_ref/graphic.rs index eefe7db9..c83222e9 100644 --- a/src/cmd_ref/graphic.rs +++ b/src/cmd_ref/graphic.rs @@ -14,7 +14,7 @@ use { }, ash::vk, log::trace, - std::{cell::RefCell, ops::Range, sync::Arc}, + std::{cell::RefCell, slice, sync::Arc}, vk_sync::AccessType, }; @@ -67,6 +67,9 @@ pub struct Graphic<'a> { impl Graphic<'_> { /// Bind an index buffer to the current pass. /// + /// `offset` is the starting offset in bytes within `buffer` used in index buffer address + /// calculations. + /// /// # Examples /// /// Basic usage: @@ -104,30 +107,18 @@ impl Graphic<'_> { /// .read_node(my_idx_buf) /// .read_node(my_vtx_buf) /// .record_pipeline(move |pipeline, bindings| { - /// pipeline.bind_index_buffer(my_idx_buf, vk::IndexType::UINT16) - /// .bind_vertex_buffer(my_vtx_buf) + /// pipeline.bind_index_buffer(my_idx_buf, 0, vk::IndexType::UINT16) + /// .bind_vertex_buffer(0, my_vtx_buf, 0) /// .draw_indexed(42, 1, 0, 0, 0); /// }); /// # Ok(()) } /// ``` - pub fn bind_index_buffer( - &self, - buffer: impl Into, - index_ty: vk::IndexType, - ) -> &Self { - self.bind_index_buffer_offset(buffer, index_ty, 0) - } - - /// Bind an index buffer to the current pass. - /// - /// Behaves similarly to `bind_index_buffer` except that `offset` is the starting offset in - /// bytes within `buffer` used in index buffer address calculations. #[profiling::function] - pub fn bind_index_buffer_offset( + pub fn bind_index_buffer( &self, buffer: impl Into, - index_ty: vk::IndexType, offset: vk::DeviceSize, + index_ty: vk::IndexType, ) -> &Self { let buffer = buffer.into(); @@ -145,6 +136,8 @@ impl Graphic<'_> { /// Bind a vertex buffer to the current pass. /// + /// The vertex input binding is updated to start at `offset` from the start of `buffer`. + /// /// # Examples /// /// Basic usage: @@ -178,35 +171,26 @@ impl Graphic<'_> { /// .store_color(0, swapchain_image) /// .read_node(my_vtx_buf) /// .record_pipeline(move |pipeline, bindings| { - /// pipeline.bind_vertex_buffer(my_vtx_buf) + /// pipeline.bind_vertex_buffer(0, my_vtx_buf, 0) /// .draw(42, 1, 0, 0); /// }); /// # Ok(()) } /// ``` - pub fn bind_vertex_buffer(&self, buffer: impl Into) -> &Self { - self.bind_vertex_buffer_offset(buffer, 0) - } - - /// Bind a vertex buffer to the current pass. - /// - /// Behaves similarly to `bind_vertex_buffer` except the vertex input binding is updated to - /// start at `offset` from the start of `buffer`. #[profiling::function] - pub fn bind_vertex_buffer_offset( + pub fn bind_vertex_buffer( &self, + binding: u32, buffer: impl Into, offset: vk::DeviceSize, ) -> &Self { - use std::slice::from_ref; - let buffer = buffer.into(); unsafe { self.device.cmd_bind_vertex_buffers( self.cmd_buf, - 0, - from_ref(&self.bindings[buffer].handle), - from_ref(&offset), + binding, + slice::from_ref(&self.bindings[buffer].handle), + slice::from_ref(&offset), ); } @@ -379,8 +363,8 @@ impl Graphic<'_> { /// .read_node(my_vtx_buf) /// .read_node(buf_node) /// .record_pipeline(move |pipeline, bindings| { - /// pipeline.bind_index_buffer(my_idx_buf, vk::IndexType::UINT16) - /// .bind_vertex_buffer(my_vtx_buf) + /// pipeline.bind_index_buffer(my_idx_buf, 0, vk::IndexType::UINT16) + /// .bind_vertex_buffer(0, my_vtx_buf, 0) /// .draw_indexed_indirect(buf_node, 0, 1, 0); /// }); /// # Ok(()) } @@ -565,78 +549,7 @@ impl Graphic<'_> { /// .bind_pipeline(&my_graphic_pipeline) /// .store_color(0, swapchain_image) /// .record_pipeline(move |pipeline, bindings| { - /// pipeline.push_constants(&[42]) - /// .draw(6, 1, 0, 0); - /// }); - /// # Ok(()) } - /// ``` - /// - /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all - pub fn push_constants(&self, data: &[u8]) -> &Self { - self.push_constants_offset(0, data) - } - - /// Updates push constants starting at the given `offset`. - /// - /// Behaves similary to [`Draw::push_constants`] except that `offset` describes the position at - /// which `data` updates the push constants of the currently bound pipeline. This may be used to - /// update a subset or single field of previously set push constant data. - /// - /// # Device limitations - /// - /// See - /// [`device.physical_device.props.limits.max_push_constants_size`](vk::PhysicalDeviceLimits) - /// for the limits of the current device. You may also check [gpuinfo.org] for a listing of - /// reported limits on other devices. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # vk_shader_macros::glsl!(r#" - /// #version 450 - /// #pragma shader_stage(compute) - /// - /// layout(push_constant) uniform PushConstants { - /// layout(offset = 0) uint some_val1; - /// layout(offset = 4) uint some_val2; - /// } push_constants; - /// - /// void main() { - /// // TODO: Add code! - /// } - /// # "#); - /// ``` - /// - /// ```no_run - /// # use std::sync::Arc; - /// # use ash::vk; - /// # use vk_graph::driver::DriverError; - /// # use vk_graph::driver::device::{Device, DeviceInfo}; - /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; - /// # use vk_graph::driver::image::{Image, ImageInfo}; - /// # use vk_graph::Graph; - /// # use vk_graph::driver::shader::Shader; - /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); - /// # let my_frag_code = [0u8; 1]; - /// # let my_vert_code = [0u8; 1]; - /// # let vert = Shader::new_vertex(my_vert_code.as_slice()); - /// # let frag = Shader::new_fragment(my_frag_code.as_slice()); - /// # let info = GraphicPipelineInfo::default(); - /// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); - /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); - /// # let swapchain_image = Image::create(&device, info)?; - /// # let mut my_graph = Graph::default(); - /// # let swapchain_image = my_graph.bind_node(swapchain_image); - /// my_graph.begin_cmd().with_name("draw a quad") - /// .bind_pipeline(&my_graphic_pipeline) - /// .store_color(0, swapchain_image) - /// .record_pipeline(move |pipeline, bindings| { - /// pipeline.push_constants(&[0x00, 0x00]) - /// .draw(6, 1, 0, 0) - /// .push_constants_offset(4, &[0xff]) + /// pipeline.push_constants(0, &[42]) /// .draw(6, 1, 0, 0); /// }); /// # Ok(()) } @@ -644,7 +557,7 @@ impl Graphic<'_> { /// /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all #[profiling::function] - pub fn push_constants_offset(&self, offset: u32, data: &[u8]) -> &Self { + pub fn push_constants(&self, offset: u32, data: &[u8]) -> &Self { for push_const in self.pipeline.push_constants.iter() { // Determine the range of the overall pipline push constants which overlap with `data` let push_const_end = push_const.offset + push_const.size; @@ -675,16 +588,10 @@ impl Graphic<'_> { /// Set scissor rectangle dynamically for a pass. #[profiling::function] - pub fn set_scissor(&self, x: i32, y: i32, width: u32, height: u32) -> &Self { + pub fn set_scissor(&self, scissor: &vk::Rect2D) -> &Self { unsafe { - self.device.cmd_set_scissor( - self.cmd_buf, - 0, - &[vk::Rect2D { - extent: vk::Extent2D { width, height }, - offset: vk::Offset2D { x, y }, - }], - ); + self.device + .cmd_set_scissor(self.cmd_buf, 0, slice::from_ref(scissor)); } self @@ -701,19 +608,16 @@ impl Graphic<'_> { S: Into, { thread_local! { - static SCISSORS: RefCell> = Default::default(); + static TLS: RefCell> = Default::default(); } - SCISSORS.with_borrow_mut(|scissors_vec| { - scissors_vec.clear(); - - for scissor in scissors { - scissors_vec.push(scissor.into()); - } + TLS.with_borrow_mut(|tls| { + tls.clear(); + tls.extend(scissors.into_iter().map(Into::into)); unsafe { self.device - .cmd_set_scissor(self.cmd_buf, first_scissor, scissors_vec.as_slice()); + .cmd_set_scissor(self.cmd_buf, first_scissor, tls); } }); @@ -722,27 +626,10 @@ impl Graphic<'_> { /// Set the viewport dynamically for a pass. #[profiling::function] - pub fn set_viewport( - &self, - x: f32, - y: f32, - width: f32, - height: f32, - depth: Range, - ) -> &Self { + pub fn set_viewport(&self, viewport: &vk::Viewport) -> &Self { unsafe { - self.device.cmd_set_viewport( - self.cmd_buf, - 0, - &[vk::Viewport { - x, - y, - width, - height, - min_depth: depth.start, - max_depth: depth.end, - }], - ); + self.device + .cmd_set_viewport(self.cmd_buf, 0, slice::from_ref(viewport)); } self @@ -759,22 +646,16 @@ impl Graphic<'_> { V: Into, { thread_local! { - static VIEWPORTS: RefCell> = Default::default(); + static TLS: RefCell> = Default::default(); } - VIEWPORTS.with_borrow_mut(|viewports_vec| { - viewports_vec.clear(); - - for viewport in viewports { - viewports_vec.push(viewport.into()); - } + TLS.with_borrow_mut(|tls| { + tls.clear(); + tls.extend(viewports.into_iter().map(Into::into)); unsafe { - self.device.cmd_set_viewport( - self.cmd_buf, - first_viewport, - viewports_vec.as_slice(), - ); + self.device + .cmd_set_viewport(self.cmd_buf, first_viewport, tls); } }); diff --git a/src/cmd_ref/ray_trace.rs b/src/cmd_ref/ray_trace.rs index b8535a8d..08b6c081 100644 --- a/src/cmd_ref/ray_trace.rs +++ b/src/cmd_ref/ray_trace.rs @@ -154,80 +154,7 @@ impl RayTrace<'_> { /// my_graph.begin_cmd().with_name("draw a cornell box") /// .bind_pipeline(&my_ray_trace_pipeline) /// .record_pipeline(move |pipeline, bindings| { - /// pipeline.push_constants(&[0xcb]) - /// .trace_rays(&rgen_sbt, &hit_sbt, &miss_sbt, &call_sbt, 320, 200, 1); - /// }); - /// # Ok(()) } - /// ``` - /// - /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all - pub fn push_constants(&self, data: &[u8]) -> &Self { - self.push_constants_offset(0, data) - } - - /// Updates push constants starting at the given `offset`. - /// - /// Behaves similary to [`RayTrace::push_constants`] except that `offset` describes the position - /// at which `data` updates the push constants of the currently bound pipeline. This may be used - /// to update a subset or single field of previously set push constant data. - /// - /// # Device limitations - /// - /// See - /// [`device.physical_device.props.limits.max_push_constants_size`](vk::PhysicalDeviceLimits) - /// for the limits of the current device. You may also check [gpuinfo.org] for a listing of - /// reported limits on other devices. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # vk_shader_macros::glsl!(target: vulkan1_2, r#" - /// #version 460 - /// #pragma shader_stage(closest) - /// - /// layout(push_constant) uniform PushConstants { - /// layout(offset = 0) uint some_val1; - /// layout(offset = 4) uint some_val2; - /// } push_constants; - /// - /// void main() - /// { - /// // TODO: Add bindings to write things! - /// } - /// # "#); - /// ``` - /// - /// ```no_run - /// # use std::sync::Arc; - /// # use ash::vk; - /// # use vk_graph::driver::DriverError; - /// # use vk_graph::driver::device::{Device, DeviceInfo}; - /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; - /// # use vk_graph::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; - /// # use vk_graph::driver::shader::Shader; - /// # use vk_graph::Graph; - /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); - /// # let shader = [0u8; 1]; - /// # let info = RayTracePipelineInfo::default(); - /// # let my_miss_code = [0u8; 1]; - /// # let my_ray_trace_pipeline = Arc::new(RayTracePipeline::create(&device, info, - /// # [Shader::new_miss(my_miss_code.as_slice())], - /// # [RayTraceShaderGroup::new_general(0)], - /// # )?); - /// # let rgen_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; - /// # let hit_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; - /// # let miss_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; - /// # let call_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; - /// # let mut my_graph = Graph::default(); - /// my_graph.begin_cmd().with_name("draw a cornell box") - /// .bind_pipeline(&my_ray_trace_pipeline) - /// .record_pipeline(move |pipeline, bindings| { - /// pipeline.push_constants(&[0xcb, 0xff]) - /// .trace_rays(&rgen_sbt, &hit_sbt, &miss_sbt, &call_sbt, 320, 200, 1) - /// .push_constants_offset(4, &[0xae]) + /// pipeline.push_constants(0, &[0xcb]) /// .trace_rays(&rgen_sbt, &hit_sbt, &miss_sbt, &call_sbt, 320, 200, 1); /// }); /// # Ok(()) } @@ -235,7 +162,7 @@ impl RayTrace<'_> { /// /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all #[profiling::function] - pub fn push_constants_offset(&self, offset: u32, data: &[u8]) -> &Self { + pub fn push_constants(&self, offset: u32, data: &[u8]) -> &Self { for push_const in self.pipeline.push_constants.iter() { let push_const_end = push_const.offset + push_const.size; let data_end = offset + data.len() as u32; @@ -273,8 +200,10 @@ impl RayTrace<'_> { #[cfg(debug_assertions)] assert!(self.dynamic_stack_size); + let ray_trace_ext = Device::expect_ray_trace_ext(self.device); + unsafe { - Device::expect_ray_trace_ext(self.device) + ray_trace_ext .cmd_set_ray_tracing_pipeline_stack_size(self.cmd_buf, pipeline_stack_size); } @@ -336,8 +265,10 @@ impl RayTrace<'_> { height: u32, depth: u32, ) -> &Self { + let ray_trace_ext = Device::expect_ray_trace_ext(self.device); + unsafe { - Device::expect_ray_trace_ext(self.device).cmd_trace_rays( + ray_trace_ext.cmd_trace_rays( self.cmd_buf, raygen_shader_binding_table, miss_shader_binding_table, @@ -370,8 +301,10 @@ impl RayTrace<'_> { callable_shader_binding_table: &vk::StridedDeviceAddressRegionKHR, indirect_device_address: vk::DeviceAddress, ) -> &Self { + let ray_trace_ext = Device::expect_ray_trace_ext(self.device); + unsafe { - Device::expect_ray_trace_ext(self.device).cmd_trace_rays_indirect( + ray_trace_ext.cmd_trace_rays_indirect( self.cmd_buf, raygen_shader_binding_table, miss_shader_binding_table, diff --git a/src/edge.rs b/src/edge.rs index 8487ec00..5c16b0e5 100644 --- a/src/edge.rs +++ b/src/edge.rs @@ -22,7 +22,7 @@ pub trait Edge { type Result; } -macro_rules! graph_edge { +macro_rules! node { ($src:ty => $dst:ty) => { impl Edge for $src { type Result = $dst; @@ -32,31 +32,31 @@ macro_rules! graph_edge { // Edges that can be bound as nodes to the render graph: // Ex: Graph::bind_node(&mut self, binding: X) -> Y -graph_edge!(AccelerationStructure => AccelerationStructureNode); -graph_edge!(Arc => AccelerationStructureNode); -graph_edge!(Lease => AccelerationStructureLeaseNode); -graph_edge!(Arc> => AccelerationStructureLeaseNode); -graph_edge!(Buffer => BufferNode); -graph_edge!(Arc => BufferNode); -graph_edge!(Lease => BufferLeaseNode); -graph_edge!(Arc> => BufferLeaseNode); -graph_edge!(Image => ImageNode); -graph_edge!(Arc => ImageNode); -graph_edge!(Lease => ImageLeaseNode); -graph_edge!(Arc> => ImageLeaseNode); -graph_edge!(SwapchainImage => SwapchainImageNode); +node!(AccelerationStructure => AccelerationStructureNode); +node!(Arc => AccelerationStructureNode); +node!(Lease => AccelerationStructureLeaseNode); +node!(Arc> => AccelerationStructureLeaseNode); +node!(Buffer => BufferNode); +node!(Arc => BufferNode); +node!(Lease => BufferLeaseNode); +node!(Arc> => BufferLeaseNode); +node!(Image => ImageNode); +node!(Arc => ImageNode); +node!(Lease => ImageLeaseNode); +node!(Arc> => ImageLeaseNode); +node!(SwapchainImage => SwapchainImageNode); // Edges that can be unbound from the render graph: // Ex: Graph::unbind_node(&mut self, node: X) -> Y -graph_edge!(AccelerationStructureNode => Arc); -graph_edge!(AccelerationStructureLeaseNode => Arc>); -graph_edge!(BufferNode => Arc); -graph_edge!(BufferLeaseNode => Arc>); -graph_edge!(ImageNode => Arc); -graph_edge!(ImageLeaseNode => Arc>); -graph_edge!(SwapchainImageNode => SwapchainImage); +node!(AccelerationStructureNode => Arc); +node!(AccelerationStructureLeaseNode => Arc>); +node!(BufferNode => Arc); +node!(BufferLeaseNode => Arc>); +node!(ImageNode => Arc); +node!(ImageLeaseNode => Arc>); +node!(SwapchainImageNode => SwapchainImage); -macro_rules! graph_edge_borrow { +macro_rules! node_ref { ($src:ty => $dst:ty) => { impl<'a> Edge for &'a $src { type Result = $dst; @@ -64,16 +64,16 @@ macro_rules! graph_edge_borrow { }; } -graph_edge_borrow!(Arc => AccelerationStructureNode); -graph_edge_borrow!(Arc> => AccelerationStructureLeaseNode); -graph_edge_borrow!(Arc => BufferNode); -graph_edge_borrow!(Arc> => BufferLeaseNode); -graph_edge_borrow!(Arc => ImageNode); -graph_edge_borrow!(Arc> => ImageLeaseNode); +node_ref!(Arc => AccelerationStructureNode); +node_ref!(Arc> => AccelerationStructureLeaseNode); +node_ref!(Arc => BufferNode); +node_ref!(Arc> => BufferLeaseNode); +node_ref!(Arc => ImageNode); +node_ref!(Arc> => ImageLeaseNode); // Specialized edges for pipelines added to a pass: // Ex: PassRef::bind_pipeline(&mut self, pipeline: X) -> PipelineCommandRef -macro_rules! pipeline_edge { +macro_rules! pipeline { ($name:ident) => { paste::paste! { impl<'a> Edge> for &'a Arc<[<$name Pipeline>]> { @@ -91,11 +91,11 @@ macro_rules! pipeline_edge { }; } -pipeline_edge!(Compute); -pipeline_edge!(Graphic); -pipeline_edge!(RayTrace); +pipeline!(Compute); +pipeline!(Graphic); +pipeline!(RayTrace); -macro_rules! resolver_edge { +macro_rules! resolve { ($src:ident -> $dst:ident) => { impl Edge for $src { type Result = $dst; @@ -103,6 +103,6 @@ macro_rules! resolver_edge { }; } -// Edges that can be unbound from a resolved render graph: +// Edges that can be unbound from a resolved graph: // (You get the full real actual swapchain image woo hoo!) -resolver_edge!(SwapchainImageNode -> SwapchainImage); +resolve!(SwapchainImageNode -> SwapchainImage); diff --git a/src/lib.rs b/src/lib.rs index 090382b5..e52c77e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,11 @@ /*! -This crate provides a high performance [Vulkan](https://www.vulkan.org/) graphics driver using smart -pointers. +This crate provides a high performance [Vulkan](https://www.vulkan.org/) graphics driver with +automatic resource management and execution. -Driver resources (_buffers, images, and acceleration structures_) and shader pipelines are used with -the provided graph structure to compose any type of graphics algorithm. Some implementations of -common graphics patterns are provided in the `contrib` directory. +The provided graph structure may be used to compose any type of graphics algorithm using driver +resources (_buffers, images, and acceleration structures_) and shader pipelines. Some +implementations of common graphics patterns are provided in the `contrib` directory. # Getting Sarted @@ -41,6 +41,29 @@ fn main() -> Result<(), DriverError> { } ``` +## _Optional_: Full control of device selection, window handling, etc + +```no_run +use vk_graph::driver::device::{Device, DeviceInfo}; +use vk_graph::driver::instance::{Instance, InstanceInfo}; +use vk_graph::driver::DriverError; + +fn main() -> Result<(), DriverError> { + let instance = Instance::new(InstanceInfo::default())?; + let physical_devices = Instance::physical_devices(&instance)?; + let best_index = physical_devices.iter().enumerate()...; + let best_gpu = physical_device[best_index]; + + assert!(best_gpu.properties_v1_0.limits.max_push_constants_size > 128); + + let device = Device::from_physical_device(best_gpu)?; + + // ... Or use winit manually similar to the usage in vk_graph_window ... + // let info = DeviceInfoBuilder::default().physical_device_index(0); + // let device = Device::from_display(&my_window, info)?; +} +``` + # Resources and Pipelines All resources and pipelines, as well as the driver itself, use shared reference tracking to keep @@ -91,7 +114,7 @@ For example, a graphics pipeline: # let device = Arc::new(Device::new(DeviceInfo::default())?); # let my_frag_code = [0u8; 1]; # let my_vert_code = [0u8; 1]; -// shader code is raw SPIR-V code as bytes +// shader code is SPIR-V in u32 format let vert = Shader::new_vertex(my_vert_code.as_slice()); let frag = Shader::new_fragment(my_frag_code.as_slice()); let info = GraphicPipelineInfo::default(); @@ -100,9 +123,24 @@ let my_pipeline = GraphicPipeline::create(&device, info, [vert, frag])?; ``` _Note:_ dtolnay's read-only public field deref pattern -(_[Link](https://github.com/dtolnay/case-studies/blob/master/readonly-fields/README.md)_) is used to +(_[link](https://github.com/dtolnay/case-studies/blob/master/readonly-fields/README.md)_) is used to make the information of each resource easily available and immutable. +```no_run +# use std::sync::Arc; +# use ash::vk; +# use vk_graph::driver::DriverError; +# use vk_graph::driver::device::{Device, DeviceInfo}; +# use vk_graph::driver::image::{Image, ImageInfo}; +# fn main() -> Result<(), DriverError> { +# let device = Arc::new(Device::new(DeviceInfo::default())?); +let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::empty()); +let my_image = Image::create(&device, info)?; + +// Note: info is a field provided through the Deref trait and is immutable! +assert_eq!(8, my_image.info.width); +# Ok(()) } + ## Pooling Multiple [`pool`] types are available to reduce the impact of frequently creating and dropping @@ -264,7 +302,7 @@ graph .begin_cmd().with_name("My compute pass") .bind_pipeline(&my_compute_pipeline) .record_pipeline(|compute, _| { - compute.push_constants(&42u32.to_ne_bytes()) + compute.push_constants(0, &42u32.to_ne_bytes()) .dispatch(128, 1, 1); }); # Ok(()) } @@ -344,7 +382,6 @@ mod cmd_ref; mod edge; mod info; mod resolver; -mod swapchain; pub use self::{ bind::{Bind, Unbind}, diff --git a/src/swapchain.rs b/src/swapchain.rs deleted file mode 100644 index c387edbe..00000000 --- a/src/swapchain.rs +++ /dev/null @@ -1,33 +0,0 @@ -use { - super::{Bind, Binding, Graph, SwapchainImageNode}, - crate::driver::swapchain::SwapchainImage, -}; - -impl Bind<&mut Graph, SwapchainImageNode> for SwapchainImage { - fn bind(self, graph: &mut Graph) -> SwapchainImageNode { - // We will return a new node - let res = SwapchainImageNode::new(graph.bindings.len()); - - //trace!("Node {}: {:?}", res.idx, &self); - - let binding = Binding::SwapchainImage(Box::new(self), true); - graph.bindings.push(binding); - - res - } -} - -impl Binding { - pub(super) fn as_swapchain_image(&self) -> Option<&SwapchainImage> { - if let Self::SwapchainImage(binding, true) = self { - Some(binding) - } else if let Self::SwapchainImage(_, false) = self { - // User code might try this - but it is a programmer error - // to access a binding after it has been unbound so dont - None - } else { - // The private code in this module should prevent this branch - unreachable!(); - } - } -} From b0fc94170f097915dada47462a0d328b2bd52c5d Mon Sep 17 00:00:00 2001 From: John Wells Date: Fri, 20 Feb 2026 08:11:01 -0500 Subject: [PATCH 14/86] clean-up --- src/display.rs | 14 ++-- src/driver/accel_struct.rs | 6 ++ src/driver/buffer.rs | 12 +-- src/driver/cmd_buf.rs | 150 ++++++++++++++++++++++------------- src/driver/compute.rs | 27 ++----- src/driver/descriptor_set.rs | 59 +++++++------- src/driver/device.rs | 30 ++----- src/driver/graphic.rs | 15 +--- src/driver/instance.rs | 41 +++------- src/driver/ray_trace.rs | 17 +--- src/driver/render_pass.rs | 122 ++++++++++++++-------------- src/lib.rs | 23 ------ src/pool/fifo.rs | 2 +- src/pool/hash.rs | 2 +- src/pool/lazy.rs | 2 +- src/resolver.rs | 70 ++++++++-------- 16 files changed, 268 insertions(+), 324 deletions(-) diff --git a/src/display.rs b/src/display.rs index 59c9984d..05f05c85 100644 --- a/src/display.rs +++ b/src/display.rs @@ -106,14 +106,14 @@ impl Display { let exec = &mut self.execs[self.exec_idx]; if exec.queue.is_some() { - CommandBuffer::wait_until_executed(&mut exec.cmd_buf).inspect_err(|err| { + exec.cmd_buf.wait_until_executed().inspect_err(|err| { warn!("unable to wait for display fence: {err}"); })?; exec.queue = None; } - CommandBuffer::drop_fenced(&mut exec.cmd_buf); + exec.cmd_buf.drop_fenced(); unsafe { exec.cmd_buf @@ -174,7 +174,7 @@ impl Display { exec.cmd_buf .device .begin_command_buffer( - *exec.cmd_buf, + exec.cmd_buf.handle, &vk::CommandBufferBeginInfo::default() .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT), ) @@ -207,7 +207,7 @@ impl Display { // Force a presentation layout transition pipeline_barrier( &exec.cmd_buf.device, - *exec.cmd_buf, + exec.cmd_buf.handle, None, &[], slice::from_ref(&ImageBarrier { @@ -237,7 +237,7 @@ impl Display { unsafe { exec.cmd_buf .device - .end_command_buffer(*exec.cmd_buf) + .end_command_buffer(exec.cmd_buf.handle) .map_err(|err| { warn!("unable to end display command buffer: {err}"); @@ -249,7 +249,7 @@ impl Display { queue, slice::from_ref( &vk::SubmitInfo::default() - .command_buffers(slice::from_ref(&exec.cmd_buf)) + .command_buffers(slice::from_ref(&exec.cmd_buf.handle)) .wait_semaphores(slice::from_ref(&exec.swapchain_acquired)) .wait_dst_stage_mask(slice::from_ref(&wait_dst_stage_mask)) .signal_semaphores(slice::from_ref(&exec.swapchain_rendered)), @@ -280,7 +280,7 @@ impl Display { // Store the resolved graph because it contains bindings, leases, and other shared resources // that need to be kept alive until the fence is waited upon. - CommandBuffer::push_fenced_drop(&mut exec.cmd_buf, resolver); + exec.cmd_buf.push_fenced_drop(resolver); Ok(()) } diff --git a/src/driver/accel_struct.rs b/src/driver/accel_struct.rs index c0836ac7..ed3c1e5a 100644 --- a/src/driver/accel_struct.rs +++ b/src/driver/accel_struct.rs @@ -712,6 +712,12 @@ impl AccelerationStructureInfo { } } +impl From for AccelerationStructureInfo { + fn from(info: AccelerationStructureInfoBuilder) -> Self { + info.build() + } +} + impl From for () { fn from(_: AccelerationStructureInfo) -> Self {} } diff --git a/src/driver/buffer.rs b/src/driver/buffer.rs index 64ca6187..5618e2fc 100644 --- a/src/driver/buffer.rs +++ b/src/driver/buffer.rs @@ -811,6 +811,12 @@ impl BufferInfo { } } +impl From for BufferInfo { + fn from(info: BufferInfoBuilder) -> Self { + info.build() + } +} + impl BufferInfoBuilder { /// Builds a new `BufferInfo`. /// @@ -837,12 +843,6 @@ impl BufferInfoBuilder { } } -impl From for BufferInfo { - fn from(info: BufferInfoBuilder) -> Self { - info.build() - } -} - #[derive(Debug)] struct BufferInfoBuilderError(UninitializedFieldError); diff --git a/src/driver/cmd_buf.rs b/src/driver/cmd_buf.rs index 45d27f71..e9c8b3f8 100644 --- a/src/driver/cmd_buf.rs +++ b/src/driver/cmd_buf.rs @@ -1,8 +1,9 @@ use { super::{DriverError, device::Device}, ash::vk, + derive_builder::{Builder, UninitializedFieldError}, log::{error, trace, warn}, - std::{fmt::Debug, ops::Deref, sync::Arc, thread::panicking}, + std::{fmt::Debug, slice, sync::Arc, thread::panicking}, }; // TODO: Expose command functions so the fence, device, waiting flags do not @@ -10,13 +11,25 @@ use { /// Represents a Vulkan command buffer to which some work has been submitted. #[derive(Debug)] +#[readonly::make] pub struct CommandBuffer { - cmd_buf: vk::CommandBuffer, - pub(crate) device: Arc, + /// The device which owns this command buffer resource. + /// + /// _Note:_ This field is read-only. + #[readonly] + pub device: Arc, + droppables: Vec>, pub(crate) fence: vk::Fence, // Keeps state because everyone wants this + /// The native Vulkan resource handle of this command buffer. + /// + /// _Note:_ This field is read-only. + #[readonly] + pub handle: vk::CommandBuffer, + /// Information used to create this object. + #[readonly] pub info: CommandBufferInfo, pub(crate) pool: vk::CommandPool, @@ -30,41 +43,45 @@ impl CommandBuffer { info: CommandBufferInfo, ) -> Result { let device = Arc::clone(device); - let cmd_pool_info = vk::CommandPoolCreateInfo::default() - .flags( - vk::CommandPoolCreateFlags::TRANSIENT - | vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER, - ) - .queue_family_index(info.queue_family_index); + let pool = unsafe { - device - .create_command_pool(&cmd_pool_info, None) - .map_err(|err| { - warn!("{err}"); - - DriverError::Unsupported - })? - }; - let cmd_buf_info = vk::CommandBufferAllocateInfo::default() - .command_buffer_count(1) - .command_pool(pool) - .level(vk::CommandBufferLevel::PRIMARY); - let cmd_buf = unsafe { - device - .allocate_command_buffers(&cmd_buf_info) - .map_err(|err| { - warn!("{err}"); - - DriverError::Unsupported - })? - }[0]; + device.create_command_pool( + &vk::CommandPoolCreateInfo::default() + .flags( + vk::CommandPoolCreateFlags::TRANSIENT + | vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER, + ) + .queue_family_index(info.queue_family_index), + None, + ) + } + .map_err(|err| { + warn!("unable to create command pool: {err}"); + + DriverError::Unsupported + })?; + + let handle = unsafe { + device.allocate_command_buffers( + &vk::CommandBufferAllocateInfo::default() + .command_buffer_count(1) + .command_pool(pool) + .level(vk::CommandBufferLevel::PRIMARY), + ) + } + .map_err(|err| { + warn!("unable to allocate command buffer: {err}"); + + DriverError::Unsupported + })?[0]; + let fence = Device::create_fence(&device, false)?; Ok(Self { - cmd_buf, device, droppables: vec![], fence, + handle, info, pool, waiting: false, @@ -73,12 +90,12 @@ impl CommandBuffer { /// Signals that execution has completed and it is time to drop anything we collected. #[profiling::function] - pub(crate) fn drop_fenced(this: &mut Self) { - if !this.droppables.is_empty() { - trace!("dropping {} shared references", this.droppables.len()); + pub(crate) fn drop_fenced(&mut self) { + if !self.droppables.is_empty() { + trace!("dropping {} shared references", self.droppables.len()); } - this.droppables.clear(); + self.droppables.clear(); } /// Returns `true` after the GPU has executed the previous submission to this command buffer. @@ -106,8 +123,8 @@ impl CommandBuffer { } /// Drops an item after execution has been completed - pub(crate) fn push_fenced_drop(this: &mut Self, thing_to_drop: impl Debug + Send + 'static) { - this.droppables.push(Box::new(thing_to_drop)); + pub(crate) fn push_fenced_drop(&mut self, thing_to_drop: impl Debug + Send + 'static) { + self.droppables.push(Box::new(thing_to_drop)); } /// Stalls by blocking the current thread until the GPU has executed the previous submission to @@ -127,40 +144,41 @@ impl CommandBuffer { } } -impl Deref for CommandBuffer { - type Target = vk::CommandBuffer; - - fn deref(&self) -> &Self::Target { - &self.cmd_buf - } -} - impl Drop for CommandBuffer { #[profiling::function] fn drop(&mut self) { - use std::slice::from_ref; - if panicking() { return; } - unsafe { - if self.waiting && Device::wait_for_fence(&self.device, &self.fence).is_err() { - return; - } + if self.waiting && Device::wait_for_fence(&self.device, &self.fence).is_err() { + return; + } - Self::drop_fenced(self); + self.drop_fenced(); + unsafe { self.device - .free_command_buffers(self.pool, from_ref(&self.cmd_buf)); + .free_command_buffers(self.pool, slice::from_ref(&self.handle)); self.device.destroy_command_pool(self.pool, None); self.device.destroy_fence(self.fence, None); } } } -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] +/// Information used to create a [`CommandBuffer`] instance. +#[derive(Builder, Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +#[builder( + build_fn(private, name = "fallible_build", error = "UninitializedFieldError"), + derive(Clone, Copy, Debug), + pattern = "owned" +)] +#[non_exhaustive] pub struct CommandBufferInfo { + /// Designates a queue family as described in section + /// [Queue Family Properties](https://docs.vulkan.org/spec/latest/chapters/devsandqueues.html#devsandqueues-queueprops). + /// All command buffers allocated from this command pool must be submitted on queues from the + /// same queue family pub queue_family_index: u32, } @@ -169,3 +187,27 @@ impl CommandBufferInfo { Self { queue_family_index } } } + +impl CommandBufferInfo { + /// Converts a `CommandBufferInfo` into a `CommandBufferInfoBuilder`. + #[inline(always)] + pub fn to_builder(self) -> CommandBufferInfoBuilder { + CommandBufferInfoBuilder { + queue_family_index: Some(self.queue_family_index), + } + } +} + +impl From for CommandBufferInfo { + fn from(info: CommandBufferInfoBuilder) -> Self { + info.build() + } +} + +impl CommandBufferInfoBuilder { + /// Builds a new `CommandBufferInfo`. + #[inline(always)] + pub fn build(self) -> CommandBufferInfo { + self.fallible_build().unwrap() + } +} diff --git a/src/driver/compute.rs b/src/driver/compute.rs index 6bbc6fea..a49ad922 100644 --- a/src/driver/compute.rs +++ b/src/driver/compute.rs @@ -9,7 +9,7 @@ use { ash::vk, derive_builder::{Builder, UninitializedFieldError}, log::{trace, warn}, - std::{ffi::CString, sync::Arc, thread::panicking}, + std::{ffi::CString, slice, sync::Arc, thread::panicking}, }; /// Smart pointer handle to a [pipeline] object. @@ -87,8 +87,6 @@ impl ComputePipeline { info: impl Into, shader: impl Into, ) -> Result { - use std::slice::from_ref; - trace!("create"); let device = Arc::clone(device); @@ -141,7 +139,7 @@ impl ComputePipeline { let push_constants = shader.push_constant_range(); if let Some(push_constants) = &push_constants { - layout_info = layout_info.push_constant_ranges(from_ref(push_constants)); + layout_info = layout_info.push_constant_ranges(slice::from_ref(push_constants)); } let layout = device @@ -153,13 +151,13 @@ impl ComputePipeline { DriverError::Unsupported })?; - let pipeline_info = vk::ComputePipelineCreateInfo::default() + let create_info = vk::ComputePipelineCreateInfo::default() .stage(stage_create_info) .layout(layout); let handle = device .create_compute_pipelines( - Device::pipeline_cache(&device), - from_ref(&pipeline_info), + device.pipeline_cache, + slice::from_ref(&create_info), None, ) .map_err(|(_, err)| { @@ -209,11 +207,7 @@ impl Drop for ComputePipeline { /// Information used to create a [`ComputePipeline`] instance. #[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)] #[builder( - build_fn( - private, - name = "fallible_build", - error = "ComputePipelineInfoBuilderError" - ), + build_fn(private, name = "fallible_build", error = "UninitializedFieldError"), derive(Clone, Copy, Debug), pattern = "owned" )] @@ -286,15 +280,6 @@ impl ComputePipelineInfoBuilder { } } -#[derive(Debug)] -struct ComputePipelineInfoBuilderError; - -impl From for ComputePipelineInfoBuilderError { - fn from(_: UninitializedFieldError) -> Self { - Self - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/driver/descriptor_set.rs b/src/driver/descriptor_set.rs index 26c5ef99..a01b8403 100644 --- a/src/driver/descriptor_set.rs +++ b/src/driver/descriptor_set.rs @@ -2,14 +2,29 @@ use { super::{DescriptorSetLayout, DriverError, device::Device}, ash::vk, log::warn, - std::{ops::Deref, sync::Arc, thread::panicking}, + std::{ops::Deref, slice, sync::Arc, thread::panicking}, }; #[derive(Debug)] +#[readonly::make] pub struct DescriptorPool { - pub info: DescriptorPoolInfo, - descriptor_pool: vk::DescriptorPool, + /// The device which owns this descriptor pool resource. + /// + /// _Note:_ This field is read-only. + #[readonly] pub device: Arc, + + /// The native Vulkan resource handle of this descriptor pool. + /// + /// _Note:_ This field is read-only. + #[readonly] + pub handle: vk::DescriptorPool, + + /// Information used to create this descriptor pool resource. + /// + /// _Note:_ This field is read-only. + #[readonly] + pub info: DescriptorPoolInfo, } impl DescriptorPool { @@ -123,7 +138,7 @@ impl DescriptorPool { pool_size_count += 1; } - let descriptor_pool = unsafe { + let handle = unsafe { device.create_descriptor_pool( &vk::DescriptorPoolCreateInfo::default() .flags(vk::DescriptorPoolCreateFlags::FREE_DESCRIPTOR_SET) @@ -139,8 +154,8 @@ impl DescriptorPool { })?; Ok(Self { - descriptor_pool, device, + handle, info, }) } @@ -156,19 +171,17 @@ impl DescriptorPool { #[profiling::function] pub fn allocate_descriptor_sets<'a>( - this: &'a Self, + &'a self, layout: &DescriptorSetLayout, count: u32, ) -> Result + 'a, DriverError> { - use std::slice::from_ref; - let mut create_info = vk::DescriptorSetAllocateInfo::default() - .descriptor_pool(this.descriptor_pool) - .set_layouts(from_ref(&layout.handle)); + .descriptor_pool(self.handle) + .set_layouts(slice::from_ref(&layout.handle)); create_info.descriptor_set_count = count; Ok(unsafe { - this.device + self.device .allocate_descriptor_sets(&create_info) .map_err(|err| { use {DriverError::*, vk::Result as vk}; @@ -185,22 +198,14 @@ impl DescriptorPool { })? .into_iter() .map(move |descriptor_set| DescriptorSet { - descriptor_pool: this.descriptor_pool, + descriptor_pool: self.handle, descriptor_set, - device: Arc::clone(&this.device), + device: Arc::clone(&self.device), }) }) } } -impl Deref for DescriptorPool { - type Target = vk::DescriptorPool; - - fn deref(&self) -> &Self::Target { - &self.descriptor_pool - } -} - impl Drop for DescriptorPool { #[profiling::function] fn drop(&mut self) { @@ -209,8 +214,7 @@ impl Drop for DescriptorPool { } unsafe { - self.device - .destroy_descriptor_pool(self.descriptor_pool, None); + self.device.destroy_descriptor_pool(self.handle, None); } } } @@ -268,16 +272,15 @@ impl Deref for DescriptorSet { impl Drop for DescriptorSet { #[profiling::function] fn drop(&mut self) { - use std::slice::from_ref; - if panicking() { return; } - unsafe { + if let Err(err) = unsafe { self.device - .free_descriptor_sets(self.descriptor_pool, from_ref(&self.descriptor_set)) - .unwrap_or_else(|_| warn!("Unable to free descriptor set")) + .free_descriptor_sets(self.descriptor_pool, slice::from_ref(&self.descriptor_set)) + } { + warn!("unable to free descriptor set: {err}"); } } } diff --git a/src/driver/device.rs b/src/driver/device.rs index 1ad0f1a7..cca01b9a 100644 --- a/src/driver/device.rs +++ b/src/driver/device.rs @@ -19,6 +19,7 @@ use { fmt::{Debug, Formatter}, mem::{ManuallyDrop, forget}, ops::Deref, + slice, thread::panicking, time::Instant, }, @@ -95,7 +96,7 @@ pub struct Device { pub(super) allocator: ManuallyDrop>, device: ash::Device, - pipeline_cache: vk::PipelineCache, + pub(crate) pipeline_cache: vk::PipelineCache, /// The physical device, which contains useful data about features, properties, and limits. /// @@ -107,7 +108,7 @@ pub struct Device { physical_device: PhysicalDevice, /// The physical execution queues which all work will be submitted to. - pub(crate) queues: Vec>, + pub(crate) queues: Box<[Box<[vk::Queue]>]>, ray_trace_ext: Option, surface_ext: Option, @@ -235,7 +236,7 @@ impl Device { .push(unsafe { device.get_device_queue(queue_family_index as _, queue_index) }); } - queues.push(queue_family); + queues.push(queue_family.into_boxed_slice()); } let surface_ext = physical_device.display.then(|| { @@ -267,7 +268,7 @@ impl Device { device, pipeline_cache, physical_device, - queues, + queues: queues.into_boxed_slice(), ray_trace_ext, surface_ext, swapchain_ext, @@ -327,15 +328,9 @@ impl Device { Self::from_ash_device(device, physical_device) } - pub(crate) fn pipeline_cache(this: &Self) -> vk::PipelineCache { - this.pipeline_cache - } - #[profiling::function] pub(crate) fn wait_for_fence(this: &Self, fence: &vk::Fence) -> Result<(), DriverError> { - use std::slice::from_ref; - - Device::wait_for_fences(this, from_ref(fence)) + Device::wait_for_fences(this, slice::from_ref(fence)) } #[profiling::function] @@ -427,7 +422,7 @@ impl Drop for Device { /// Information used to create a [`Device`] instance. #[derive(Builder, Clone, Copy, Debug, Default, Eq, PartialEq, Hash)] #[builder( - build_fn(private, name = "fallible_build", error = "DeviceInfoBuilderError"), + build_fn(private, name = "fallible_build", error = "UninitializedFieldError"), derive(Clone, Copy, Debug), pattern = "owned" )] @@ -533,15 +528,6 @@ impl DeviceInfoBuilder { } } -#[derive(Debug)] -struct DeviceInfoBuilderError; - -impl From for DeviceInfoBuilderError { - fn from(_: UninitializedFieldError) -> Self { - Self - } -} - #[doc(hidden)] #[repr(C)] pub struct ReadOnlyDevice { @@ -550,7 +536,7 @@ pub struct ReadOnlyDevice { device: ash::Device, pipeline_cache: vk::PipelineCache, pub physical_device: PhysicalDevice, - pub(crate) queues: Vec>, + pub(crate) queues: Box<[Box<[vk::Queue]>]>, ray_trace_ext: Option, surface_ext: Option, swapchain_ext: Option, diff --git a/src/driver/graphic.rs b/src/driver/graphic.rs index cbbd51b3..589e4f03 100644 --- a/src/driver/graphic.rs +++ b/src/driver/graphic.rs @@ -623,11 +623,7 @@ impl Drop for GraphicPipeline { /// Information used to create a [`GraphicPipeline`] instance. #[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)] #[builder( - build_fn( - private, - name = "fallible_build", - error = "GraphicPipelineInfoBuilderError" - ), + build_fn(private, name = "fallible_build", error = "UninitializedFieldError"), derive(Clone, Copy, Debug), pattern = "owned" )] @@ -776,15 +772,6 @@ impl GraphicPipelineInfoBuilder { } } -#[derive(Debug)] -struct GraphicPipelineInfoBuilderError; - -impl From for GraphicPipelineInfoBuilderError { - fn from(_: UninitializedFieldError) -> Self { - Self - } -} - #[derive(Debug)] pub(super) struct GraphicPipelineState { pub layout: vk::PipelineLayout, diff --git a/src/driver/instance.rs b/src/driver/instance.rs index a0074d02..48046478 100644 --- a/src/driver/instance.rs +++ b/src/driver/instance.rs @@ -268,7 +268,7 @@ impl Instance { entry, info, inner: Arc::new(InstanceInner { - _debug_callback: debug_callback, + debug_callback, _debug_loader: debug_loader, debug_utils, instance, @@ -322,7 +322,7 @@ impl Instance { ..Default::default() }, inner: Arc::new(InstanceInner { - _debug_callback: None, + debug_callback: None, _debug_loader: None, debug_utils: None, instance, @@ -447,7 +447,7 @@ impl Deref for Instance { /// Information used to create an [`Instance`] instance. #[derive(Builder, Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] #[builder( - build_fn(private, name = "fallible_build", error = "InstanceInfoBuilderError"), + build_fn(private, name = "fallible_build", error = "UninitializedFieldError"), derive(Clone, Debug), pattern = "owned" )] @@ -493,23 +493,8 @@ impl InstanceInfo { impl InstanceInfoBuilder { /// Builds a new `InstanceInfo`. #[inline(always)] - pub fn build(mut self) -> InstanceInfo { - if self.api_version.is_none() { - self.api_version = Some(ApiVersion::MAX); - } - - if self.debug.is_none() { - self.debug = Some(false); - } - - if self.extension_names.is_none() { - self.extension_names = Some(&[]); - } - - match self.fallible_build() { - Err(InstanceInfoBuilderError(err)) => panic!("{err}"), - Ok(info) => info, - } + pub fn build(self) -> InstanceInfo { + self.fallible_build().unwrap() } } @@ -519,21 +504,15 @@ impl From for InstanceInfo { } } -#[derive(Debug)] -struct InstanceInfoBuilderError(UninitializedFieldError); - -impl From for InstanceInfoBuilderError { - fn from(err: UninitializedFieldError) -> Self { - Self(err) - } -} - struct InstanceInner { - _debug_callback: Option, + debug_callback: Option, + #[allow(deprecated)] // TODO: Remove? Look into this.... _debug_loader: Option, + #[allow(dead_code)] debug_utils: Option, + instance: ash::Instance, } @@ -547,7 +526,7 @@ impl Drop for InstanceInner { unsafe { #[allow(deprecated)] if let Some(debug_loader) = &self._debug_loader { - let debug_callback = self._debug_callback.unwrap(); + let debug_callback = self.debug_callback.unwrap(); debug_loader.destroy_debug_report_callback(debug_callback, None); } diff --git a/src/driver/ray_trace.rs b/src/driver/ray_trace.rs index 9e567d05..a635c880 100644 --- a/src/driver/ray_trace.rs +++ b/src/driver/ray_trace.rs @@ -238,7 +238,7 @@ impl RayTracePipeline { let handle = ray_trace_ext .create_ray_tracing_pipelines( vk::DeferredOperationKHR::null(), - Device::pipeline_cache(device), + device.pipeline_cache, &[vk::RayTracingPipelineCreateInfoKHR::default() .stages(&shader_stages) .groups(&shader_groups) @@ -395,11 +395,7 @@ impl Drop for RayTracePipeline { /// Information used to create a [`RayTracePipeline`] instance. #[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)] #[builder( - build_fn( - private, - name = "fallible_build", - error = "RayTracePipelineInfoBuilderError" - ), + build_fn(private, name = "fallible_build", error = "UninitializedFieldError"), derive(Clone, Copy, Debug), pattern = "owned" )] @@ -497,15 +493,6 @@ impl RayTracePipelineInfoBuilder { } } -#[derive(Debug)] -struct RayTracePipelineInfoBuilderError; - -impl From for RayTracePipelineInfoBuilderError { - fn from(_: UninitializedFieldError) -> Self { - Self - } -} - /// Describes the set of the shader stages to be included in each shader group in the ray trace /// pipeline. /// diff --git a/src/driver/render_pass.rs b/src/driver/render_pass.rs index 239026df..5100dd24 100644 --- a/src/driver/render_pass.rs +++ b/src/driver/render_pass.rs @@ -6,7 +6,7 @@ use { log::{trace, warn}, std::{ collections::{HashMap, hash_map::Entry}, - ops::Deref, + slice, sync::Arc, thread::panicking, }, @@ -103,12 +103,28 @@ pub(crate) struct RenderPassInfo { } #[derive(Debug)] +#[readonly::make] pub(crate) struct RenderPass { - device: Arc, + /// The device which owns this render pass resource. + /// + /// _Note:_ This field is read-only. + #[readonly] + pub device: Arc, + framebuffers: HashMap, graphic_pipelines: HashMap, + + /// The native Vulkan resource handle of this render pass. + /// + /// _Note:_ This field is read-only. + #[readonly] + pub handle: vk::RenderPass, + + /// Information used to create this render pass resource. + /// + /// _Note:_ This field is read-only. + #[readonly] pub info: RenderPassInfo, - render_pass: vk::RenderPass, } impl RenderPass { @@ -222,40 +238,36 @@ impl RenderPass { ); } - let render_pass = unsafe { - device - .create_render_pass2( - &vk::RenderPassCreateInfo2::default() - .attachments(&attachments) - .correlated_view_masks(&correlated_view_masks) - .dependencies(&dependencies) - .subpasses(&subpasses), - None, - ) - .map_err(|err| { - warn!("{err}"); + let handle = unsafe { + device.create_render_pass2( + &vk::RenderPassCreateInfo2::default() + .attachments(&attachments) + .correlated_view_masks(&correlated_view_masks) + .dependencies(&dependencies) + .subpasses(&subpasses), + None, + ) + } + .map_err(|err| { + warn!("{err}"); - DriverError::Unsupported - })? - }; + DriverError::Unsupported + })?; Ok(Self { - info, device, framebuffers: Default::default(), graphic_pipelines: Default::default(), - render_pass, + handle, + info, }) } #[profiling::function] - pub fn framebuffer( - this: &mut Self, - info: FramebufferInfo, - ) -> Result { + pub fn framebuffer(&mut self, info: FramebufferInfo) -> Result { debug_assert!(!info.attachments.is_empty()); - let entry = this.framebuffers.entry(info); + let entry = self.framebuffers.entry(info); if let Entry::Occupied(entry) = entry { return Ok(*entry.get()); } @@ -289,22 +301,19 @@ impl RenderPass { vk::FramebufferAttachmentsCreateInfoKHR::default().attachment_image_infos(&attachments); let mut create_info = vk::FramebufferCreateInfo::default() .flags(vk::FramebufferCreateFlags::IMAGELESS) - .render_pass(this.render_pass) + .render_pass(self.handle) .width(attachments[0].width) .height(attachments[0].height) .layers(layers) .push_next(&mut imageless_info); - create_info.attachment_count = this.info.attachments.len() as _; + create_info.attachment_count = self.info.attachments.len() as _; - let framebuffer = unsafe { - this.device - .create_framebuffer(&create_info, None) - .map_err(|err| { - warn!("{err}"); + let framebuffer = + unsafe { self.device.create_framebuffer(&create_info, None) }.map_err(|err| { + warn!("{err}"); - DriverError::Unsupported - })? - }; + DriverError::Unsupported + })?; entry.insert(framebuffer); @@ -313,14 +322,12 @@ impl RenderPass { #[profiling::function] pub fn graphic_pipeline( - this: &mut Self, + &mut self, pipeline: &Arc, depth_stencil: Option, subpass_idx: u32, ) -> Result { - use std::slice::from_ref; - - let entry = this.graphic_pipelines.entry(GraphicPipelineKey { + let entry = self.graphic_pipelines.entry(GraphicPipelineKey { depth_stencil, layout: pipeline.layout, shader_modules: pipeline.shader_modules.clone(), @@ -335,7 +342,7 @@ impl RenderPass { _ => unreachable!(), }; - let color_blend_attachment_states = this.info.subpasses[subpass_idx as usize] + let color_blend_attachment_states = self.info.subpasses[subpass_idx as usize] .color_attachments .iter() .map(|_| pipeline.info.blend.into()) @@ -406,7 +413,7 @@ impl RenderPass { cull_mode: pipeline.info.cull_mode, ..Default::default() }; - let graphic_pipeline_info = vk::GraphicsPipelineCreateInfo::default() + let create_info = vk::GraphicsPipelineCreateInfo::default() .color_blend_state(&color_blend_state) .depth_stencil_state(&depth_stencil) .dynamic_state(&dynamic_state) @@ -414,24 +421,21 @@ impl RenderPass { .layout(pipeline.state.layout) .multisample_state(&multisample_state) .rasterization_state(&rasterization_state) - .render_pass(this.render_pass) + .render_pass(self.handle) .stages(&stages) .subpass(subpass_idx) .vertex_input_state(&vertex_input_state) .viewport_state(&viewport_state); let pipeline = unsafe { - this.device.create_graphics_pipelines( - Device::pipeline_cache(&this.device), - from_ref(&graphic_pipeline_info), + self.device.create_graphics_pipelines( + self.device.pipeline_cache, + slice::from_ref(&create_info), None, ) } .map_err(|(_, err)| { - warn!( - "create_graphics_pipelines: {err}\n{:#?}", - graphic_pipeline_info - ); + warn!("create_graphics_pipelines: {err}\n{:#?}", create_info); DriverError::Unsupported })?[0]; @@ -442,14 +446,6 @@ impl RenderPass { } } -impl Deref for RenderPass { - type Target = vk::RenderPass; - - fn deref(&self) -> &Self::Target { - &self.render_pass - } -} - impl Drop for RenderPass { #[profiling::function] fn drop(&mut self) { @@ -457,16 +453,20 @@ impl Drop for RenderPass { return; } - unsafe { - for (_, framebuffer) in self.framebuffers.drain() { + for (_, framebuffer) in self.framebuffers.drain() { + unsafe { self.device.destroy_framebuffer(framebuffer, None); } + } - for (_, pipeline) in self.graphic_pipelines.drain() { + for (_, pipeline) in self.graphic_pipelines.drain() { + unsafe { self.device.destroy_pipeline(pipeline, None); } + } - self.device.destroy_render_pass(self.render_pass, None); + unsafe { + self.device.destroy_render_pass(self.handle, None); } } } diff --git a/src/lib.rs b/src/lib.rs index e52c77e8..433bc6fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,29 +41,6 @@ fn main() -> Result<(), DriverError> { } ``` -## _Optional_: Full control of device selection, window handling, etc - -```no_run -use vk_graph::driver::device::{Device, DeviceInfo}; -use vk_graph::driver::instance::{Instance, InstanceInfo}; -use vk_graph::driver::DriverError; - -fn main() -> Result<(), DriverError> { - let instance = Instance::new(InstanceInfo::default())?; - let physical_devices = Instance::physical_devices(&instance)?; - let best_index = physical_devices.iter().enumerate()...; - let best_gpu = physical_device[best_index]; - - assert!(best_gpu.properties_v1_0.limits.max_push_constants_size > 128); - - let device = Device::from_physical_device(best_gpu)?; - - // ... Or use winit manually similar to the usage in vk_graph_window ... - // let info = DeviceInfoBuilder::default().physical_device_index(0); - // let device = Device::from_display(&my_window, info)?; -} -``` - # Resources and Pipelines All resources and pipelines, as well as the driver itself, use shared reference tracking to keep diff --git a/src/pool/fifo.rs b/src/pool/fifo.rs index c9d60266..1f0cc4be 100644 --- a/src/pool/fifo.rs +++ b/src/pool/fifo.rs @@ -199,7 +199,7 @@ impl Pool for FifoPool { })?; // Drop anything we were holding from the last submission - CommandBuffer::drop_fenced(&mut item); + item.drop_fenced(); Ok(Lease::new(Arc::downgrade(cache_ref), item)) } diff --git a/src/pool/hash.rs b/src/pool/hash.rs index ed752c42..663100b2 100644 --- a/src/pool/hash.rs +++ b/src/pool/hash.rs @@ -150,7 +150,7 @@ impl Pool for HashPool { })?; // Drop anything we were holding from the last submission - CommandBuffer::drop_fenced(&mut item); + item.drop_fenced(); Ok(Lease::new(Arc::downgrade(cache_ref), item)) } diff --git a/src/pool/lazy.rs b/src/pool/lazy.rs index 95b5c998..4b6d04a0 100644 --- a/src/pool/lazy.rs +++ b/src/pool/lazy.rs @@ -263,7 +263,7 @@ impl Pool for LazyPool { })?; // Drop anything we were holding from the last submission - CommandBuffer::drop_fenced(&mut item); + item.drop_fenced(); Ok(Lease::new(Arc::downgrade(cache_ref), item)) } diff --git a/src/resolver.rs b/src/resolver.rs index 27ba3339..0fc0ed35 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -31,6 +31,7 @@ use { collections::{BTreeMap, HashMap, VecDeque}, iter::repeat_n, ops::Range, + slice, }, vk_sync::{AccessType, BufferBarrier, GlobalBarrier, ImageBarrier, cmd::pipeline_barrier}, }; @@ -555,9 +556,9 @@ impl Resolver { unsafe { cmd_buf.device.cmd_begin_render_pass( - **cmd_buf, + cmd_buf.handle, &vk::RenderPassBeginInfo::default() - .render_pass(***render_pass) + .render_pass(render_pass.handle) .framebuffer(framebuffer) .render_area(vk::Rect2D { offset: vk::Offset2D { @@ -610,7 +611,7 @@ impl Resolver { unsafe { cmd_buf.device.cmd_bind_descriptor_sets( - **cmd_buf, + cmd_buf.handle, pipeline.bind_point(), pipeline.layout(), 0, @@ -665,7 +666,7 @@ impl Resolver { unsafe { cmd_buf .device - .cmd_bind_pipeline(**cmd_buf, pipeline_bind_point, pipeline); + .cmd_bind_pipeline(cmd_buf.handle, pipeline_bind_point, pipeline); } Ok(()) @@ -675,7 +676,7 @@ impl Resolver { trace!(" end render pass"); unsafe { - cmd_buf.device.cmd_end_render_pass(**cmd_buf); + cmd_buf.device.cmd_end_render_pass(cmd_buf.handle); } } @@ -1838,7 +1839,7 @@ impl Resolver { unsafe { cmd_buf .device - .cmd_next_subpass(**cmd_buf, vk::SubpassContents::INLINE); + .cmd_next_subpass(cmd_buf.handle, vk::SubpassContents::INLINE); } } @@ -1883,8 +1884,6 @@ impl Resolver { bindings: &mut [Binding], accesses: impl Iterator)>, ) { - use std::slice::from_ref; - // We store a Barriers in TLS to save an alloc; contents are POD thread_local! { static TLS: RefCell = Default::default(); @@ -2056,8 +2055,8 @@ impl Resolver { ); BufferBarrier { - next_accesses: from_ref(next_access), - previous_accesses: from_ref(prev_access), + next_accesses: slice::from_ref(next_access), + previous_accesses: slice::from_ref(prev_access), src_queue_family_index: vk::QUEUE_FAMILY_IGNORED, dst_queue_family_index: vk::QUEUE_FAMILY_IGNORED, buffer, @@ -2103,9 +2102,9 @@ impl Resolver { ); ImageBarrier { - next_accesses: from_ref(next_access), + next_accesses: slice::from_ref(next_access), next_layout: image_access_layout(*next_access), - previous_accesses: from_ref(prev_access), + previous_accesses: slice::from_ref(prev_access), previous_layout: image_access_layout(*prev_access), discard_contents: *prev_access == AccessType::Nothing || is_write_access(*next_access), @@ -2119,7 +2118,7 @@ impl Resolver { pipeline_barrier( &cmd_buf.device, - **cmd_buf, + cmd_buf.handle, global_barrier, &buffer_barriers.collect::>(), &image_barriers.collect::>(), @@ -2133,8 +2132,6 @@ impl Resolver { bindings: &mut [Binding], pass: &mut Command, ) { - use std::slice::from_ref; - // We store a Barriers in TLS to save an alloc; contents are POD thread_local! { static TLS: RefCell = Default::default(); @@ -2291,9 +2288,9 @@ impl Resolver { *prev_access == AccessType::Nothing || !is_read_access(*next_access); ImageBarrier { - next_accesses: from_ref(next_access), + next_accesses: slice::from_ref(next_access), next_layout: image_access_layout(*next_access), - previous_accesses: from_ref(prev_access), + previous_accesses: slice::from_ref(prev_access), previous_layout: image_access_layout(*prev_access), discard_contents, src_queue_family_index: vk::QUEUE_FAMILY_IGNORED, @@ -2306,7 +2303,7 @@ impl Resolver { pipeline_barrier( &cmd_buf.device, - **cmd_buf, + cmd_buf.handle, None, &[], &image_barriers.collect::>(), @@ -2520,7 +2517,7 @@ impl Resolver { let exec_func = exec.func.take().unwrap().0; exec_func( &cmd_buf.device, - **cmd_buf, + cmd_buf.handle, Bindings::new(&self.graph.bindings, exec), ); } @@ -2548,10 +2545,8 @@ impl Resolver { if pass_idx == schedule_idx { // This was a scheduled pass - store it! - CommandBuffer::push_fenced_drop( - cmd_buf, - (pass, self.physical_passes.pop().unwrap()), - ); + + cmd_buf.push_fenced_drop((pass, self.physical_passes.pop().unwrap())); break; } else { debug_assert!(pass_idx > schedule_idx); @@ -2846,13 +2841,11 @@ impl Resolver { } fn set_scissor(cmd_buf: &CommandBuffer, x: i32, y: i32, width: u32, height: u32) { - use std::slice::from_ref; - unsafe { cmd_buf.device.cmd_set_scissor( - **cmd_buf, + cmd_buf.handle, 0, - from_ref(&vk::Rect2D { + slice::from_ref(&vk::Rect2D { extent: vk::Extent2D { width, height }, offset: vk::Offset2D { x, y }, }), @@ -2868,13 +2861,11 @@ impl Resolver { height: f32, depth: Range, ) { - use std::slice::from_ref; - unsafe { cmd_buf.device.cmd_set_viewport( - **cmd_buf, + cmd_buf.handle, 0, - from_ref(&vk::Viewport { + slice::from_ref(&vk::Viewport { x, y, width, @@ -2899,8 +2890,6 @@ impl Resolver { + Pool + Pool, { - use std::slice::from_ref; - trace!("submit"); let mut cmd_buf = pool.lease(CommandBufferInfo::new(queue_family_index as _))?; @@ -2916,13 +2905,13 @@ impl Resolver { "Queue index must be within the range of the available queues created by the device." ); - CommandBuffer::wait_until_executed(&mut cmd_buf)?; + cmd_buf.wait_until_executed()?; unsafe { cmd_buf .device .begin_command_buffer( - **cmd_buf, + cmd_buf.handle, &vk::CommandBufferBeginInfo::default() .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT), ) @@ -2934,17 +2923,20 @@ impl Resolver { unsafe { cmd_buf .device - .end_command_buffer(**cmd_buf) + .end_command_buffer(cmd_buf.handle) .map_err(|_| DriverError::OutOfMemory)?; cmd_buf .device - .reset_fences(from_ref(&cmd_buf.fence)) + .reset_fences(slice::from_ref(&cmd_buf.fence)) .map_err(|_| DriverError::OutOfMemory)?; cmd_buf .device .queue_submit( cmd_buf.device.queues[queue_family_index][queue_index], - from_ref(&vk::SubmitInfo::default().command_buffers(from_ref(&cmd_buf))), + slice::from_ref( + &vk::SubmitInfo::default() + .command_buffers(slice::from_ref(&cmd_buf.handle)), + ), cmd_buf.fence, ) .map_err(|_| DriverError::OutOfMemory)?; @@ -2957,7 +2949,7 @@ impl Resolver { // they will return to the pool for other things to use. The drop will happen the next time // someone tries to lease a command buffer and we notice this one has returned and the fence // has been signalled. - CommandBuffer::push_fenced_drop(&mut cmd_buf, self); + cmd_buf.push_fenced_drop(self); Ok(cmd_buf) } From 6161162ae4aa81e7f46cb2378b59e0e4bb782bb1 Mon Sep 17 00:00:00 2001 From: John Wells Date: Fri, 20 Feb 2026 08:40:26 -0500 Subject: [PATCH 15/86] Use inner-arc on device --- contrib/vk-graph-egui/src/lib.rs | 2 +- contrib/vk-graph-fx/src/bitmap_font.rs | 2 +- contrib/vk-graph-fx/src/image_loader.rs | 6 +- contrib/vk-graph-fx/src/presenter.rs | 4 +- contrib/vk-graph-fx/src/transition.rs | 6 +- contrib/vk-graph-hot/src/compute.rs | 6 +- contrib/vk-graph-hot/src/graphic.rs | 6 +- contrib/vk-graph-hot/src/ray_trace.rs | 6 +- contrib/vk-graph-imgui/src/lib.rs | 2 +- contrib/vk-graph-window/src/frame.rs | 3 +- contrib/vk-graph-window/src/lib.rs | 10 +-- examples/bindless.rs | 6 +- examples/fuzzer.rs | 4 +- examples/image_sampler.rs | 4 +- examples/min_max.rs | 6 +- examples/mip_graphic.rs | 4 +- examples/msaa.rs | 4 +- examples/multipass.rs | 8 +- examples/multithread.rs | 4 +- examples/ray_omni.rs | 12 +-- examples/ray_trace.rs | 4 +- examples/rt_triangle.rs | 2 +- examples/skeletal-anim/src/main.rs | 13 +-- examples/subgroup_ops.rs | 8 +- examples/vertex_layout.rs | 8 +- examples/vr/src/driver/instance.rs | 15 ++-- examples/vr/src/main.rs | 4 +- examples/vsm_omni.rs | 22 ++--- src/display.rs | 3 +- src/driver/accel_struct.rs | 7 +- src/driver/buffer.rs | 13 ++- src/driver/cmd_buf.rs | 11 +-- src/driver/compute.rs | 10 +-- src/driver/descriptor_set.rs | 12 +-- src/driver/descriptor_set_layout.rs | 8 +- src/driver/device.rs | 115 ++++++++++++------------ src/driver/graphic.rs | 8 +- src/driver/image.rs | 23 +++-- src/driver/physical_device.rs | 29 +++--- src/driver/ray_trace.rs | 10 +-- src/driver/render_pass.rs | 8 +- src/driver/shader.rs | 9 +- src/driver/surface.rs | 7 +- src/driver/swapchain.rs | 8 +- src/pool/fifo.rs | 8 +- src/pool/hash.rs | 8 +- src/pool/lazy.rs | 8 +- 47 files changed, 233 insertions(+), 253 deletions(-) diff --git a/contrib/vk-graph-egui/src/lib.rs b/contrib/vk-graph-egui/src/lib.rs index eca7d8ac..00b4326e 100644 --- a/contrib/vk-graph-egui/src/lib.rs +++ b/contrib/vk-graph-egui/src/lib.rs @@ -33,7 +33,7 @@ pub struct Egui { impl Egui { /// TODO - pub fn new(device: &Arc, display_target: &dyn HasDisplayHandle) -> Self { + pub fn new(device: &Device, display_target: &dyn HasDisplayHandle) -> Self { let ppl = Arc::new( GraphicPipeline::create( device, diff --git a/contrib/vk-graph-fx/src/bitmap_font.rs b/contrib/vk-graph-fx/src/bitmap_font.rs index d3730075..8b06d123 100644 --- a/contrib/vk-graph-fx/src/bitmap_font.rs +++ b/contrib/vk-graph-fx/src/bitmap_font.rs @@ -31,7 +31,7 @@ pub struct BitmapFont { impl BitmapFont { /// TODO pub fn new( - device: &Arc, + device: &Device, font: BMFont, pages: impl Into>>, ) -> anyhow::Result { diff --git a/contrib/vk-graph-fx/src/image_loader.rs b/contrib/vk-graph-fx/src/image_loader.rs index bad15d68..23b62158 100644 --- a/contrib/vk-graph-fx/src/image_loader.rs +++ b/contrib/vk-graph-fx/src/image_loader.rs @@ -41,12 +41,12 @@ pub struct ImageLoader { decode_rgb_rgba: Arc, /// TODO - pub device: Arc, + pub device: Device, } impl ImageLoader { /// TODO - pub fn new(device: &Arc) -> Result { + pub fn new(device: &Device) -> Result { Ok(Self { pool: HashPool::new(device), _decode_r_rg: Arc::new(ComputePipeline::create( @@ -63,7 +63,7 @@ impl ImageLoader { include_glsl!("res/shader/compute/decode_bitmap_rgb_rgba.comp").as_slice(), ), )?), - device: Arc::clone(device), + device: device.clone(), }) } diff --git a/contrib/vk-graph-fx/src/presenter.rs b/contrib/vk-graph-fx/src/presenter.rs index a773c7b1..44d821f4 100644 --- a/contrib/vk-graph-fx/src/presenter.rs +++ b/contrib/vk-graph-fx/src/presenter.rs @@ -11,7 +11,7 @@ pub struct ComputePresenter([Arc; 2]); impl ComputePresenter { /// TODO - pub fn new(device: &Arc) -> Result { + pub fn new(device: &Device) -> Result { let pipeline1 = Arc::new(ComputePipeline::create( device, ComputePipelineInfo::default(), @@ -86,7 +86,7 @@ pub struct GraphicPresenter { impl GraphicPresenter { /// TODO - pub fn new(device: &Arc) -> Result { + pub fn new(device: &Device) -> Result { Ok(Self { pipeline: Arc::new(GraphicPipeline::create( device, diff --git a/contrib/vk-graph-fx/src/transition.rs b/contrib/vk-graph-fx/src/transition.rs index a46a8fdb..79bfcccb 100644 --- a/contrib/vk-graph-fx/src/transition.rs +++ b/contrib/vk-graph-fx/src/transition.rs @@ -385,15 +385,15 @@ impl Transition { /// TODO pub struct TransitionPipeline { cache: HashPool, - device: Arc, + device: Device, pipelines: HashMap>, } impl TransitionPipeline { /// TODO - pub fn new(device: &Arc) -> Self { + pub fn new(device: &Device) -> Self { let cache = HashPool::new(device); - let device = Arc::clone(device); + let device = device.clone(); let pipelines = Default::default(); Self { diff --git a/contrib/vk-graph-hot/src/compute.rs b/contrib/vk-graph-hot/src/compute.rs index 22d95961..d2a46fdf 100644 --- a/contrib/vk-graph-hot/src/compute.rs +++ b/contrib/vk-graph-hot/src/compute.rs @@ -18,7 +18,7 @@ use { /// TODO #[derive(Debug)] pub struct HotComputePipeline { - device: Arc, + device: Device, has_changes: Arc, instance: Arc, shader: HotShader, @@ -28,7 +28,7 @@ pub struct HotComputePipeline { impl HotComputePipeline { /// TODO pub fn create( - device: &Arc, + device: &Device, info: impl Into, shader: impl Into, ) -> Result { @@ -39,7 +39,7 @@ impl HotComputePipeline { let instance = Arc::new(ComputePipeline::create(device, info, compiled_shader)?); - let device = Arc::clone(device); + let device = device.clone(); Ok(Self { device, diff --git a/contrib/vk-graph-hot/src/graphic.rs b/contrib/vk-graph-hot/src/graphic.rs index 3aedec8c..44d7ba63 100644 --- a/contrib/vk-graph-hot/src/graphic.rs +++ b/contrib/vk-graph-hot/src/graphic.rs @@ -18,7 +18,7 @@ use { /// TODO #[derive(Debug)] pub struct HotGraphicPipeline { - device: Arc, + device: Device, has_changes: Arc, instance: Arc, shaders: Box<[HotShader]>, @@ -28,7 +28,7 @@ pub struct HotGraphicPipeline { impl HotGraphicPipeline { /// TODO pub fn create( - device: &Arc, + device: &Device, info: impl Into, shaders: impl IntoIterator, ) -> Result @@ -48,7 +48,7 @@ impl HotGraphicPipeline { let instance = Arc::new(GraphicPipeline::create(device, info, compiled_shaders)?); - let device = Arc::clone(device); + let device = device.clone(); Ok(Self { device, diff --git a/contrib/vk-graph-hot/src/ray_trace.rs b/contrib/vk-graph-hot/src/ray_trace.rs index d02c79ef..df149414 100644 --- a/contrib/vk-graph-hot/src/ray_trace.rs +++ b/contrib/vk-graph-hot/src/ray_trace.rs @@ -18,7 +18,7 @@ use { /// TODO #[derive(Debug)] pub struct HotRayTracePipeline { - device: Arc, + device: Device, has_changes: Arc, instance: Arc, shader_groups: Box<[RayTraceShaderGroup]>, @@ -29,7 +29,7 @@ pub struct HotRayTracePipeline { impl HotRayTracePipeline { /// TODO pub fn create( - device: &Arc, + device: &Device, info: impl Into, shaders: impl IntoIterator, shader_groups: impl IntoIterator, @@ -56,7 +56,7 @@ impl HotRayTracePipeline { shader_groups.iter().copied(), )?); - let device = Arc::clone(device); + let device = device.clone(); Ok(Self { device, diff --git a/contrib/vk-graph-imgui/src/lib.rs b/contrib/vk-graph-imgui/src/lib.rs index 1e0c6122..28ba4710 100644 --- a/contrib/vk-graph-imgui/src/lib.rs +++ b/contrib/vk-graph-imgui/src/lib.rs @@ -32,7 +32,7 @@ pub struct ImGui { impl ImGui { /// TODO - pub fn new(device: &Arc) -> Self { + pub fn new(device: &Device) -> Self { let mut context = Context::create(); let platform = WinitPlatform::new(&mut context); let pipeline = Arc::new( diff --git a/contrib/vk-graph-window/src/frame.rs b/contrib/vk-graph-window/src/frame.rs index 62743a2d..4d8217b5 100644 --- a/contrib/vk-graph-window/src/frame.rs +++ b/contrib/vk-graph-window/src/frame.rs @@ -1,5 +1,4 @@ use { - std::sync::Arc, vk_graph::{driver::device::Device, node::SwapchainImageNode, Graph}, winit::{dpi::PhysicalPosition, event::Event, window::Window}, }; @@ -21,7 +20,7 @@ pub fn set_cursor_position(window: &Window, x: u32, y: u32) { /// A request to render a single frame to the provided render graph. pub struct FrameContext<'a> { /// The device this frame belongs to. - pub device: &'a Arc, + pub device: &'a Device, /// A slice of events that have occurred since the previous frame. pub events: &'a [Event<()>], diff --git a/contrib/vk-graph-window/src/lib.rs b/contrib/vk-graph-window/src/lib.rs index 5ccf1318..b24bc415 100644 --- a/contrib/vk-graph-window/src/lib.rs +++ b/contrib/vk-graph-window/src/lib.rs @@ -8,7 +8,7 @@ pub use self::frame::FrameContext; use { log::{error, info, trace, warn}, - std::{error, fmt, sync::Arc}, + std::{error, fmt}, vk_graph::{ display::{Display, DisplayError, DisplayInfoBuilder}, driver::{ @@ -47,7 +47,7 @@ pub struct Window { data: WindowData, /// TODO - pub device: Arc, + pub device: Device, event_loop: EventLoop<()>, } @@ -71,7 +71,7 @@ impl Window { struct Application { active_window: Option, data: WindowData, - device: Arc, + device: Device, draw_fn: F, error: Option, primary_monitor: Option, @@ -322,7 +322,7 @@ impl Window { impl ActiveWindow { fn draw( &mut self, - device: &Arc, + device: &Device, mut f: impl FnMut(FrameContext), ) -> Result { if let Some((width, height)) = self.display_resize.take() { @@ -426,7 +426,7 @@ impl WindowBuilder { /// TODO pub fn build(self) -> Result { let event_loop = EventLoop::new()?; - let device = Arc::new(Device::from_display(&event_loop, self.device_info)?); + let device = Device::from_display(&event_loop, self.device_info)?; Ok(Window { data: WindowData { diff --git a/examples/bindless.rs b/examples/bindless.rs index 3fb98847..838191ef 100644 --- a/examples/bindless.rs +++ b/examples/bindless.rs @@ -46,7 +46,7 @@ fn main() -> Result<(), WindowError> { }) } -fn create_images(device: &Arc) -> Result>, DriverError> { +fn create_images(device: &Device) -> Result>, DriverError> { let mut textures = Vec::with_capacity(64); let (b, a) = (0.0, 1.0); @@ -77,7 +77,7 @@ fn create_images(device: &Arc) -> Result>, DriverError> { Ok(textures) } -fn create_indirect_buffer(device: &Arc) -> Result, DriverError> { +fn create_indirect_buffer(device: &Device) -> Result, DriverError> { let mut draw_cmds = Vec::with_capacity(64); for first_instance in 0..64 { draw_cmds.push(DrawIndirectCommand { @@ -95,7 +95,7 @@ fn create_indirect_buffer(device: &Arc) -> Result, DriverErr Ok(draw_buf) } -fn create_graphic_pipeline(device: &Arc) -> Result, DriverError> { +fn create_graphic_pipeline(device: &Device) -> Result, DriverError> { Ok(Arc::new(GraphicPipeline::create( device, GraphicPipelineInfo::default(), diff --git a/examples/fuzzer.rs b/examples/fuzzer.rs index 357105b1..fdfdaca3 100644 --- a/examples/fuzzer.rs +++ b/examples/fuzzer.rs @@ -1516,7 +1516,7 @@ fn record_transfer_graphic_multipass(frame: &mut FrameContext, pool: &mut HashPo fn compute_pipeline( key: &'static str, - device: &Arc, + device: &Device, info: impl Into, shader: impl Into, ) -> Arc { @@ -1536,7 +1536,7 @@ fn compute_pipeline( } fn graphic_vert_frag_pipeline( - device: &Arc, + device: &Device, info: impl Into, vert_source: &'static [u32], frag_source: &'static [u32], diff --git a/examples/image_sampler.rs b/examples/image_sampler.rs index 9ccf7805..908577aa 100644 --- a/examples/image_sampler.rs +++ b/examples/image_sampler.rs @@ -82,7 +82,7 @@ fn main() -> anyhow::Result<()> { } fn create_pipeline( - device: &Arc, + device: &Device, sampler_info: impl Into, ) -> anyhow::Result> { let args = Args::parse(); @@ -228,7 +228,7 @@ fn create_pipeline( )?)) } -fn read_image(device: &Arc, path: impl AsRef) -> anyhow::Result> { +fn read_image(device: &Device, path: impl AsRef) -> anyhow::Result> { // For another way to loading images, see vk_graph_fx::ImageLoader let gulf_jpg = image::open(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(path))?; let image = Arc::new(Image::create( diff --git a/examples/min_max.rs b/examples/min_max.rs index 483ec0ef..14af8f06 100644 --- a/examples/min_max.rs +++ b/examples/min_max.rs @@ -86,7 +86,7 @@ fn main() -> Result<(), DriverError> { } fn fill_depth_image( - device: &Arc, + device: &Device, render_graph: &mut Graph, size: u32, ) -> Result { @@ -156,7 +156,7 @@ fn fill_depth_image( } fn reduce_depth_image( - device: &Arc, + device: &Device, render_graph: &mut Graph, depth_image: ImageNode, reduction_mode: vk::SamplerReductionMode, @@ -214,7 +214,7 @@ fn reduce_depth_image( } fn copy_image_to_buffer( - device: &Arc, + device: &Device, render_graph: &mut Graph, reduced_image: ImageNode, ) -> Result, DriverError> { diff --git a/examples/mip_graphic.rs b/examples/mip_graphic.rs index 267a4083..30c7de05 100644 --- a/examples/mip_graphic.rs +++ b/examples/mip_graphic.rs @@ -84,7 +84,7 @@ fn main() -> Result<(), WindowError> { }) } -fn fill_mip_levels(device: &Arc, image: &Arc) -> Result<(), DriverError> { +fn fill_mip_levels(device: &Device, image: &Arc) -> Result<(), DriverError> { #[derive(Clone, Copy, Pod, Zeroable)] #[repr(C)] struct PushConstants { @@ -201,7 +201,7 @@ fn fill_mip_levels(device: &Arc, image: &Arc) -> Result<(), Drive .map(|_| ()) } -fn splat(device: &Arc) -> Result, DriverError> { +fn splat(device: &Device) -> Result, DriverError> { Ok(Arc::new(GraphicPipeline::create( device, GraphicPipelineInfo::default(), diff --git a/examples/msaa.rs b/examples/msaa.rs index 5d8697f3..c06a4612 100644 --- a/examples/msaa.rs +++ b/examples/msaa.rs @@ -298,7 +298,7 @@ fn load_cube_data() -> [CubeVertex; 36] { } /// Loads a cube as unindexed position, normal and color vertices -fn load_cube_mesh(device: &Arc) -> Result { +fn load_cube_mesh(device: &Device) -> Result { let vertices = load_cube_data(); let vertex_buf = Arc::new(Buffer::create_from_slice( @@ -314,7 +314,7 @@ fn load_cube_mesh(device: &Arc) -> Result { } fn create_mesh_pipeline( - device: &Arc, + device: &Device, sample_count: SampleCount, ) -> Result, DriverError> { let vert = glsl!( diff --git a/examples/multipass.rs b/examples/multipass.rs index 23521f6b..315bb64b 100644 --- a/examples/multipass.rs +++ b/examples/multipass.rs @@ -245,7 +245,7 @@ fn camera(width: u32, height: u32) -> Camera { /// Returns ready-to-use index and vertex buffers. Index count is also returned. The shape data uses /// temporary staging buffers which are not required but are fun. -fn create_funky_shape(device: &Arc, pool: &mut LazyPool) -> Result { +fn create_funky_shape(device: &Device, pool: &mut LazyPool) -> Result { // Static index/vertex data courtesy of the polyhedron-ops library let (indices, vertices) = funky_shape_data(); let index_count = indices.len() as u32; @@ -304,7 +304,7 @@ fn create_funky_shape(device: &Arc, pool: &mut LazyPool) -> Result) -> Arc { +fn create_fill_background_pipeline(device: &Device) -> Arc { let vertex_shader = Shader::new_vertex( glsl!( r#" @@ -355,7 +355,7 @@ fn create_fill_background_pipeline(device: &Arc) -> Arc ) } -fn create_prepass_pipeline(device: &Arc) -> Arc { +fn create_prepass_pipeline(device: &Device) -> Arc { let vertex_shader = Shader::new_vertex( glsl!( r#" @@ -428,7 +428,7 @@ fn create_prepass_pipeline(device: &Arc) -> Arc { ) } -fn create_pbr_pipeline(device: &Arc) -> Arc { +fn create_pbr_pipeline(device: &Device) -> Arc { // See: https://github.com/SaschaWillems/Vulkan/blob/master/data/shaders/glsl/pbrbasic/pbr.vert let vertex_shader = Shader::new_vertex( glsl!( diff --git a/examples/multithread.rs b/examples/multithread.rs index c0eec377..e837c26f 100644 --- a/examples/multithread.rs +++ b/examples/multithread.rs @@ -88,7 +88,7 @@ fn main() -> anyhow::Result<()> { for thread_index in 0..thread_count { let running = Arc::clone(&running); - let device = Arc::clone(&window.device); + let device = window.device.clone(); let tx = tx.clone(); threads.push(spawn(move || { let queue_index = thread_index; @@ -215,7 +215,7 @@ fn main() -> anyhow::Result<()> { Ok(()) } -fn load_font(device: &Arc) -> anyhow::Result { +fn load_font(device: &Device) -> anyhow::Result { // Load the font definition file using the bmfont crate let font = BMFont::new( Cursor::new(include_bytes!("res/font/small/small_10px.fnt")), diff --git a/examples/ray_omni.rs b/examples/ray_omni.rs index 1f305a68..86caddcf 100644 --- a/examples/ray_omni.rs +++ b/examples/ray_omni.rs @@ -147,7 +147,7 @@ fn best_2d_optimal_format( } fn create_blas( - device: &Arc, + device: &Device, models: &[&Model], ) -> Result, DriverError> { let info = AccelerationStructureGeometryInfo::blas( @@ -226,7 +226,7 @@ fn create_blas( Ok(blas) } -fn create_pipeline(device: &Arc) -> Result, DriverError> { +fn create_pipeline(device: &Device) -> Result, DriverError> { let vert = glsl!( r#" #version 460 core @@ -314,7 +314,7 @@ fn create_pipeline(device: &Arc) -> Result, DriverE } fn create_tlas( - device: &Arc, + device: &Device, pool: &mut LazyPool, render_graph: &mut Graph, blas: &Arc, @@ -418,7 +418,7 @@ fn download_model_from_github(model_name: &str) -> anyhow::Result { Ok(model_path) } -fn load_ground_mesh(device: &Arc) -> Result { +fn load_ground_mesh(device: &Device) -> Result { let extent = 100f32; let v0 = [-extent, 0.0, -extent]; let v1 = [extent, 0.0, -extent]; @@ -450,7 +450,7 @@ fn load_ground_mesh(device: &Arc) -> Result { } fn load_model( - device: &Arc, + device: &Device, path: impl AsRef, face_fn: fn(a: Vec3, b: Vec3, c: Vec3) -> [T; 3], ) -> anyhow::Result @@ -525,7 +525,7 @@ where } /// Loads an .obj model as indexed position and normal vertices -fn load_model_mesh(device: &Arc, path: impl AsRef) -> anyhow::Result { +fn load_model_mesh(device: &Device, path: impl AsRef) -> anyhow::Result { #[repr(C)] #[derive(Clone, Copy, Default, Pod, Zeroable)] struct Vertex { diff --git a/examples/ray_trace.rs b/examples/ray_trace.rs index fdc658dc..b37e5e71 100644 --- a/examples/ray_trace.rs +++ b/examples/ray_trace.rs @@ -329,7 +329,7 @@ static SHADER_SHADOW_MISS: &[u32] = glsl!( ) .as_slice(); -fn create_ray_trace_pipeline(device: &Arc) -> Result, DriverError> { +fn create_ray_trace_pipeline(device: &Device) -> Result, DriverError> { Ok(Arc::new(RayTracePipeline::create( device, RayTracePipelineInfoBuilder::default().max_ray_recursion_depth(1), @@ -350,7 +350,7 @@ fn create_ray_trace_pipeline(device: &Arc) -> Result, + device: &Device, ) -> Result<(Arc, Arc, u32, u32, Arc, Arc), DriverError> { use std::slice::from_raw_parts; diff --git a/examples/rt_triangle.rs b/examples/rt_triangle.rs index de9a37c3..e3c460db 100644 --- a/examples/rt_triangle.rs +++ b/examples/rt_triangle.rs @@ -76,7 +76,7 @@ static SHADER_MISS: &[u32] = glsl!( ) .as_slice(); -fn create_ray_trace_pipeline(device: &Arc) -> Result, DriverError> { +fn create_ray_trace_pipeline(device: &Device) -> Result, DriverError> { Ok(Arc::new(RayTracePipeline::create( device, RayTracePipelineInfoBuilder::default().max_ray_recursion_depth(1), diff --git a/examples/skeletal-anim/src/main.rs b/examples/skeletal-anim/src/main.rs index e852a666..bb42039a 100644 --- a/examples/skeletal-anim/src/main.rs +++ b/examples/skeletal-anim/src/main.rs @@ -147,10 +147,7 @@ fn main() -> Result<(), WindowError> { }) } -fn create_pipeline( - device: &Arc, - pak: &mut PakBuf, -) -> Result, DriverError> { +fn create_pipeline(device: &Device, pak: &mut PakBuf) -> Result, DriverError> { let vert_spirv = pak.read_blob("shader/animated_mesh_vert.spirv").unwrap(); let frag_spirv = pak.read_blob("shader/mesh_frag.spirv").unwrap(); @@ -164,11 +161,7 @@ fn create_pipeline( )?)) } -fn load_texture( - device: &Arc, - pak: &mut PakBuf, - key: &str, -) -> Result, DriverError> { +fn load_texture(device: &Device, pak: &mut PakBuf, key: &str) -> Result, DriverError> { let bitmap = pak.read_bitmap(key).unwrap(); assert_eq!(bitmap.format(), BitmapFormat::Rgba); @@ -386,7 +379,7 @@ struct Model { } impl Model { - fn load(device: &Arc, pak: &mut PakBuf, key: &str) -> Result { + fn load(device: &Device, pak: &mut PakBuf, key: &str) -> Result { let model = pak.read_model(key).unwrap(); // This obviously makes some assumptions about the input model! diff --git a/examples/subgroup_ops.rs b/examples/subgroup_ops.rs index 3d829063..eb519767 100644 --- a/examples/subgroup_ops.rs +++ b/examples/subgroup_ops.rs @@ -61,7 +61,7 @@ fn main() -> Result<(), DriverError> { } fn exclusive_sum( - device: &Arc, + device: &Device, reduce_pipeline: &Arc, scan_pipeline: &Arc, input_data: &[u32], @@ -152,7 +152,7 @@ fn assert_output_data(input_data: &[u32], output_data: &[u32]) { } } -fn create_reduce_pipeline(device: &Arc) -> Result, DriverError> { +fn create_reduce_pipeline(device: &Device) -> Result, DriverError> { Ok(Arc::new(ComputePipeline::create( device, ComputePipelineInfo::default(), @@ -202,9 +202,7 @@ fn create_reduce_pipeline(device: &Arc) -> Result, )?)) } -fn create_exclusive_sum_pipeline( - device: &Arc, -) -> Result, DriverError> { +fn create_exclusive_sum_pipeline(device: &Device) -> Result, DriverError> { Ok( Arc::new( ComputePipeline::create( device, diff --git a/examples/vertex_layout.rs b/examples/vertex_layout.rs index a828dd3f..2d1235ee 100644 --- a/examples/vertex_layout.rs +++ b/examples/vertex_layout.rs @@ -114,7 +114,7 @@ fn draw_triangle( }); } -fn create_f16_pipeline(device: &Arc) -> Result, DriverError> { +fn create_f16_pipeline(device: &Device) -> Result, DriverError> { if !supports_vertex_buffer(device, vk::Format::R16G16_SFLOAT) { return Err(DriverError::Unsupported); } @@ -147,14 +147,14 @@ fn create_f16_pipeline(device: &Arc) -> Result, Dri create_pipeline(device, vertex) } -fn create_f32_pipeline(device: &Arc) -> Result, DriverError> { +fn create_f32_pipeline(device: &Device) -> Result, DriverError> { // Uses automatic vertex input layout let vertex = create_vertex_shader(false); create_pipeline(device, vertex) } -fn create_f64_pipeline(device: &Arc) -> Result, DriverError> { +fn create_f64_pipeline(device: &Device) -> Result, DriverError> { if !supports_vertex_buffer(device, vk::Format::R64G64_SFLOAT) { return Err(DriverError::Unsupported); } @@ -231,7 +231,7 @@ fn create_vertex_shader(is_double: bool) -> ShaderBuilder { } fn create_pipeline( - device: &Arc, + device: &Device, vertex: ShaderBuilder, ) -> Result, DriverError> { let fragment_spirv = glsl!( diff --git a/examples/vr/src/driver/instance.rs b/examples/vr/src/driver/instance.rs index abdc37a2..1e6106b7 100644 --- a/examples/vr/src/driver/instance.rs +++ b/examples/vr/src/driver/instance.rs @@ -6,7 +6,6 @@ use { fmt::{Debug, Formatter}, mem::transmute, ops::Deref, - sync::Arc, }, vk_graph::driver::{ ash::{ @@ -19,7 +18,7 @@ use { }; pub struct XrInstance { - device: Arc, + device: Device, event_buf: xr::EventDataBuffer, instance: xr::Instance, system: xr::SystemId, @@ -202,13 +201,11 @@ impl XrInstance { InstanceCreateError::VulkanUnsupported })?; - let device = Arc::new( - Device::from_ash_device(ash_device, physical_device).map_err(|err| { - error!("Vulkan device: {err}"); + let device = Device::from_ash_device(ash_device, physical_device).map_err(|err| { + error!("Vulkan device: {err}"); - InstanceCreateError::VulkanUnsupported - })?, - ); + InstanceCreateError::VulkanUnsupported + })?; let event_buf = xr::EventDataBuffer::new(); Ok(Self { @@ -244,7 +241,7 @@ impl XrInstance { } } - pub fn device(this: &Self) -> &Arc { + pub fn device(this: &Self) -> &Device { &this.device } diff --git a/examples/vr/src/main.rs b/examples/vr/src/main.rs index ae320b43..4ba38ac0 100644 --- a/examples/vr/src/main.rs +++ b/examples/vr/src/main.rs @@ -561,7 +561,7 @@ fn device_queue_family_index(device: &Device, flags: vk::QueueFlags) -> Option, path: impl AsRef) -> anyhow::Result { +fn load_model(device: &Device, path: impl AsRef) -> anyhow::Result { trace!("Loading model {}", path.as_ref().display()); let (mut models, _) = load_obj(path.as_ref(), &GPU_LOAD_OPTIONS)?; @@ -722,7 +722,7 @@ fn load_model(device: &Arc, path: impl AsRef) -> anyhow::Result, + device: &Device, path: impl AsRef, fmt: vk::Format, ) -> anyhow::Result> { diff --git a/examples/vsm_omni.rs b/examples/vsm_omni.rs index 0815da77..381afe7d 100644 --- a/examples/vsm_omni.rs +++ b/examples/vsm_omni.rs @@ -448,7 +448,7 @@ fn best_2d_optimal_format( panic!("Unsupported format"); } -fn create_blur_x_pipeline(device: &Arc) -> Result, DriverError> { +fn create_blur_x_pipeline(device: &Device) -> Result, DriverError> { let comp = glsl!( r#" #version 450 core @@ -571,7 +571,7 @@ fn create_blur_x_pipeline(device: &Arc) -> Result, )?)) } -fn create_blur_y_pipeline(device: &Arc) -> Result, DriverError> { +fn create_blur_y_pipeline(device: &Device) -> Result, DriverError> { let comp = glsl!( r#" #version 450 core @@ -694,7 +694,7 @@ fn create_blur_y_pipeline(device: &Arc) -> Result, )?)) } -fn create_debug_pipeline(device: &Arc) -> Result, DriverError> { +fn create_debug_pipeline(device: &Device) -> Result, DriverError> { let vert = glsl!( r#" #version 450 core @@ -770,7 +770,7 @@ fn create_debug_pipeline(device: &Arc) -> Result, D )?)) } -fn create_mesh_pipeline(device: &Arc) -> Result, DriverError> { +fn create_mesh_pipeline(device: &Device) -> Result, DriverError> { let vert = glsl!( r#" #version 450 core @@ -889,7 +889,7 @@ fn create_mesh_pipeline(device: &Arc) -> Result, Dr )?)) } -fn create_shadow_pipeline(device: &Arc) -> Result, DriverError> { +fn create_shadow_pipeline(device: &Device) -> Result, DriverError> { let vert = glsl!( r#" #version 450 core @@ -959,7 +959,7 @@ fn create_shadow_pipeline(device: &Arc) -> Result, } fn create_shadow_pipeline_with_geometry_shader( - device: &Arc, + device: &Device, ) -> Result, DriverError> { let vert = glsl!( r#" @@ -1275,7 +1275,7 @@ fn load_cube_data() -> [[f32; 6]; 36] { } /// Loads a cube as indexed position and normal vertices -fn load_cube_mesh(device: &Arc) -> Result { +fn load_cube_mesh(device: &Device) -> Result { let vertices = load_cube_data(); let indices = (0u32..vertices.len() as u32).collect::>(); @@ -1298,7 +1298,7 @@ fn load_cube_mesh(device: &Arc) -> Result { } /// Loads a cube as indexed position vertices -fn load_cube_shadow(device: &Arc) -> Result { +fn load_cube_shadow(device: &Device) -> Result { let vertices = load_cube_data() .iter() .map(|vertex| [vertex[0], vertex[1], vertex[2]]) @@ -1324,7 +1324,7 @@ fn load_cube_shadow(device: &Arc) -> Result { } fn load_model( - device: &Arc, + device: &Device, path: impl AsRef, face_fn: fn(a: Vec3, b: Vec3, c: Vec3) -> [T; 3], ) -> anyhow::Result @@ -1394,7 +1394,7 @@ where } /// Loads an .obj model as indexed position and normal vertices -fn load_model_mesh(device: &Arc, path: impl AsRef) -> anyhow::Result { +fn load_model_mesh(device: &Device, path: impl AsRef) -> anyhow::Result { #[repr(C)] #[derive(Clone, Copy, Default, NoUninit)] struct Vertex { @@ -1431,7 +1431,7 @@ fn load_model_mesh(device: &Arc, path: impl AsRef) -> anyhow::Resu } /// Loads an .obj model as indexed position vertices -fn load_model_shadow(device: &Arc, path: impl AsRef) -> anyhow::Result { +fn load_model_shadow(device: &Device, path: impl AsRef) -> anyhow::Result { #[repr(C)] #[derive(Clone, Copy, Default, NoUninit)] struct Vertex { diff --git a/src/display.rs b/src/display.rs index 05f05c85..06d044c9 100644 --- a/src/display.rs +++ b/src/display.rs @@ -22,7 +22,6 @@ use { fmt::{Debug, Formatter}, ops::Deref, slice, - sync::Arc, thread::panicking, time::Instant, }, @@ -66,7 +65,7 @@ pub struct DisplayRef { impl Display { /// Constructs a new `Display` object. pub fn new( - device: &Arc, + device: &Device, swapchain: Swapchain, info: impl Into, ) -> Result { diff --git a/src/driver/accel_struct.rs b/src/driver/accel_struct.rs index ed3c1e5a..6e0ae3b0 100644 --- a/src/driver/accel_struct.rs +++ b/src/driver/accel_struct.rs @@ -8,7 +8,6 @@ use { std::{ ffi::c_void, mem::{replace, size_of_val}, - sync::Arc, thread::panicking, }, vk_sync::AccessType, @@ -65,7 +64,7 @@ pub struct AccelerationStructure { /// /// _Note:_ This field is read-only. #[readonly] - pub device: Arc, + pub device: Device, /// The native Vulkan resource handle of this acceleration structure. /// @@ -108,7 +107,7 @@ impl AccelerationStructure { /// ``` #[profiling::function] pub fn create( - device: &Arc, + device: &Device, info: impl Into, ) -> Result { debug_assert!(device.physical_device.accel_struct_properties.is_some()); @@ -147,7 +146,7 @@ impl AccelerationStructure { )? }; - let device = Arc::clone(device); + let device = device.clone(); Ok(Self { access: Mutex::new(AccessType::Nothing), diff --git a/src/driver/buffer.rs b/src/driver/buffer.rs index 5618e2fc..26cdddf5 100644 --- a/src/driver/buffer.rs +++ b/src/driver/buffer.rs @@ -14,7 +14,6 @@ use { fmt::{Debug, Formatter}, mem::ManuallyDrop, ops::{DerefMut, Range}, - sync::Arc, thread::panicking, }, vk_sync::AccessType, @@ -63,7 +62,7 @@ pub struct Buffer { /// /// _Note:_ This field is read-only. #[readonly] - pub device: Arc, + pub device: Device, /// The native Vulkan resource handle of this buffer. /// @@ -105,14 +104,14 @@ impl Buffer { /// # Ok(()) } /// ``` #[profiling::function] - pub fn create(device: &Arc, info: impl Into) -> Result { + pub fn create(device: &Device, info: impl Into) -> Result { let info = info.into(); trace!("create: {:?}", info); debug_assert_ne!(info.size, 0, "Size must be non-zero"); - let device = Arc::clone(device); + let device = device.clone(); let buffer_info = vk::BufferCreateInfo::default() .size(info.size) .usage(info.usage) @@ -144,7 +143,7 @@ impl Buffer { profiling::scope!("allocate"); #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))] - let mut allocator = device.allocator.lock(); + let mut allocator = Device::allocator(&device).lock(); #[cfg(not(feature = "parking_lot"))] let mut allocator = allocator.unwrap(); @@ -223,7 +222,7 @@ impl Buffer { /// ``` #[profiling::function] pub fn create_from_slice( - device: &Arc, + device: &Device, usage: vk::BufferUsageFlags, slice: impl AsRef<[u8]>, ) -> Result { @@ -474,7 +473,7 @@ impl Drop for Buffer { profiling::scope!("deallocate"); #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))] - let mut allocator = self.device.allocator.lock(); + let mut allocator = Device::allocator(&self.device).lock(); #[cfg(not(feature = "parking_lot"))] let mut allocator = allocator.unwrap(); diff --git a/src/driver/cmd_buf.rs b/src/driver/cmd_buf.rs index e9c8b3f8..665de5e5 100644 --- a/src/driver/cmd_buf.rs +++ b/src/driver/cmd_buf.rs @@ -3,7 +3,7 @@ use { ash::vk, derive_builder::{Builder, UninitializedFieldError}, log::{error, trace, warn}, - std::{fmt::Debug, slice, sync::Arc, thread::panicking}, + std::{fmt::Debug, slice, thread::panicking}, }; // TODO: Expose command functions so the fence, device, waiting flags do not @@ -17,7 +17,7 @@ pub struct CommandBuffer { /// /// _Note:_ This field is read-only. #[readonly] - pub device: Arc, + pub device: Device, droppables: Vec>, pub(crate) fence: vk::Fence, // Keeps state because everyone wants this @@ -38,11 +38,8 @@ pub struct CommandBuffer { impl CommandBuffer { #[profiling::function] - pub(crate) fn create( - device: &Arc, - info: CommandBufferInfo, - ) -> Result { - let device = Arc::clone(device); + pub(crate) fn create(device: &Device, info: CommandBufferInfo) -> Result { + let device = device.clone(); let pool = unsafe { device.create_command_pool( diff --git a/src/driver/compute.rs b/src/driver/compute.rs index a49ad922..5b263fde 100644 --- a/src/driver/compute.rs +++ b/src/driver/compute.rs @@ -9,7 +9,7 @@ use { ash::vk, derive_builder::{Builder, UninitializedFieldError}, log::{trace, warn}, - std::{ffi::CString, slice, sync::Arc, thread::panicking}, + std::{ffi::CString, slice, thread::panicking}, }; /// Smart pointer handle to a [pipeline] object. @@ -33,7 +33,7 @@ pub struct ComputePipeline { /// /// _Note:_ This field is read-only. #[readonly] - pub device: Arc, + pub device: Device, pub(crate) layout: vk::PipelineLayout, @@ -83,13 +83,13 @@ impl ComputePipeline { /// ``` #[profiling::function] pub fn create( - device: &Arc, + device: &Device, info: impl Into, shader: impl Into, ) -> Result { trace!("create"); - let device = Arc::clone(device); + let device = device.clone(); let info: ComputePipelineInfo = info.into(); let shader = shader.into(); @@ -156,7 +156,7 @@ impl ComputePipeline { .layout(layout); let handle = device .create_compute_pipelines( - device.pipeline_cache, + Device::pipeline_cache(&device), slice::from_ref(&create_info), None, ) diff --git a/src/driver/descriptor_set.rs b/src/driver/descriptor_set.rs index a01b8403..33d04cb3 100644 --- a/src/driver/descriptor_set.rs +++ b/src/driver/descriptor_set.rs @@ -2,7 +2,7 @@ use { super::{DescriptorSetLayout, DriverError, device::Device}, ash::vk, log::warn, - std::{ops::Deref, slice, sync::Arc, thread::panicking}, + std::{ops::Deref, slice, thread::panicking}, }; #[derive(Debug)] @@ -12,7 +12,7 @@ pub struct DescriptorPool { /// /// _Note:_ This field is read-only. #[readonly] - pub device: Arc, + pub device: Device, /// The native Vulkan resource handle of this descriptor pool. /// @@ -30,10 +30,10 @@ pub struct DescriptorPool { impl DescriptorPool { #[profiling::function] pub fn create( - device: &Arc, + device: &Device, info: impl Into, ) -> Result { - let device = Arc::clone(device); + let device = device.clone(); let info = info.into(); let mut pool_sizes = [vk::DescriptorPoolSize { @@ -200,7 +200,7 @@ impl DescriptorPool { .map(move |descriptor_set| DescriptorSet { descriptor_pool: self.handle, descriptor_set, - device: Arc::clone(&self.device), + device: self.device.clone(), }) }) } @@ -258,7 +258,7 @@ impl DescriptorPoolInfo { pub struct DescriptorSet { descriptor_pool: vk::DescriptorPool, descriptor_set: vk::DescriptorSet, - device: Arc, + device: Device, } impl Deref for DescriptorSet { diff --git a/src/driver/descriptor_set_layout.rs b/src/driver/descriptor_set_layout.rs index 629b2847..eca1856f 100644 --- a/src/driver/descriptor_set_layout.rs +++ b/src/driver/descriptor_set_layout.rs @@ -2,23 +2,23 @@ use { super::{DriverError, device::Device}, ash::vk, log::warn, - std::{sync::Arc, thread::panicking}, + std::thread::panicking, }; #[derive(Debug)] #[readonly::make] pub struct DescriptorSetLayout { - pub device: Arc, + pub device: Device, pub handle: vk::DescriptorSetLayout, } impl DescriptorSetLayout { #[profiling::function] pub fn create( - device: &Arc, + device: &Device, info: &vk::DescriptorSetLayoutCreateInfo, ) -> Result { - let device = Arc::clone(device); + let device = device.clone(); let handle = unsafe { device .create_descriptor_set_layout(info, None) diff --git a/src/driver/device.rs b/src/driver/device.rs index cca01b9a..86de3789 100644 --- a/src/driver/device.rs +++ b/src/driver/device.rs @@ -20,6 +20,7 @@ use { mem::{ManuallyDrop, forget}, ops::Deref, slice, + sync::Arc, thread::panicking, time::Instant, }, @@ -89,14 +90,11 @@ fn select_physical_device( } /// Opaque handle to a device object. +#[derive(Clone)] #[repr(C)] pub struct Device { accel_struct_ext: Option, - - pub(super) allocator: ManuallyDrop>, - - device: ash::Device, - pub(crate) pipeline_cache: vk::PipelineCache, + inner: Arc, /// The physical device, which contains useful data about features, properties, and limits. /// @@ -130,6 +128,10 @@ impl Device { Self::from_physical_device(physical_device) } + pub(crate) fn allocator(this: &Self) -> &Mutex { + &this.inner.allocator + } + pub(crate) fn create_fence(this: &Self, signaled: bool) -> Result { let mut flags = vk::FenceCreateFlags::empty(); @@ -264,9 +266,11 @@ impl Device { Ok(Self { accel_struct_ext, - allocator: ManuallyDrop::new(Mutex::new(allocator)), - device, - pipeline_cache, + inner: Arc::new(DeviceInner { + allocator: ManuallyDrop::new(Mutex::new(allocator)), + device, + pipeline_cache, + }), physical_device, queues: queues.into_boxed_slice(), ray_trace_ext, @@ -328,6 +332,10 @@ impl Device { Self::from_ash_device(device, physical_device) } + pub(crate) fn pipeline_cache(this: &Self) -> vk::PipelineCache { + this.inner.pipeline_cache + } + #[profiling::function] pub(crate) fn wait_for_fence(this: &Self, fence: &vk::Fence) -> Result<(), DriverError> { Device::wait_for_fences(this, slice::from_ref(fence)) @@ -336,7 +344,7 @@ impl Device { #[profiling::function] pub(crate) fn wait_for_fences(this: &Self, fences: &[vk::Fence]) -> Result<(), DriverError> { unsafe { - match this.device.wait_for_fences(fences, true, 100) { + match this.wait_for_fences(fences, true, 100) { Ok(_) => return Ok(()), Err(err) if err == vk::Result::ERROR_DEVICE_LOST => { error!("Device lost"); @@ -351,7 +359,7 @@ impl Device { let started = Instant::now(); - match this.device.wait_for_fences(fences, true, u64::MAX) { + match this.wait_for_fences(fences, true, u64::MAX) { Ok(_) => (), Err(err) if err == vk::Result::ERROR_DEVICE_LOST => { error!("Device lost"); @@ -388,37 +396,6 @@ impl Deref for Device { } } -impl Drop for Device { - #[profiling::function] - fn drop(&mut self) { - if panicking() { - // When panicking we don't want the GPU allocator to complain about leaks - unsafe { - forget(ManuallyDrop::take(&mut self.allocator)); - } - - return; - } - - // trace!("drop"); - - if let Err(err) = unsafe { self.device.device_wait_idle() } { - warn!("device_wait_idle() failed: {err}"); - } - - unsafe { - self.device - .destroy_pipeline_cache(self.pipeline_cache, None); - - ManuallyDrop::drop(&mut self.allocator); - } - - unsafe { - self.device.destroy_device(None); - } - } -} - /// Information used to create a [`Device`] instance. #[derive(Builder, Clone, Copy, Debug, Default, Eq, PartialEq, Hash)] #[builder( @@ -528,13 +505,48 @@ impl DeviceInfoBuilder { } } +struct DeviceInner { + allocator: ManuallyDrop>, + device: ash::Device, + pipeline_cache: vk::PipelineCache, +} + +impl Drop for DeviceInner { + #[profiling::function] + fn drop(&mut self) { + if panicking() { + // When panicking we don't want the GPU allocator to complain about leaks + unsafe { + forget(ManuallyDrop::take(&mut self.allocator)); + } + + return; + } + + // trace!("drop"); + + if let Err(err) = unsafe { self.device.device_wait_idle() } { + warn!("device_wait_idle() failed: {err}"); + } + + unsafe { + self.device + .destroy_pipeline_cache(self.pipeline_cache, None); + + ManuallyDrop::drop(&mut self.allocator); + } + + unsafe { + self.device.destroy_device(None); + } + } +} + #[doc(hidden)] #[repr(C)] pub struct ReadOnlyDevice { accel_struct_ext: Option, - pub(super) allocator: ManuallyDrop>, - device: ash::Device, - pipeline_cache: vk::PipelineCache, + inner: Arc, pub physical_device: PhysicalDevice, pub(crate) queues: Box<[Box<[vk::Queue]>]>, ray_trace_ext: Option, @@ -546,7 +558,7 @@ impl Deref for ReadOnlyDevice { type Target = ash::Device; fn deref(&self) -> &Self::Target { - &self.device + &self.inner.device } } @@ -565,18 +577,7 @@ mod tests { offset_of!(Device, accel_struct_ext), offset_of!(ReadOnlyDevice, accel_struct_ext), ); - assert_eq!( - offset_of!(Device, allocator), - offset_of!(ReadOnlyDevice, allocator), - ); - assert_eq!( - offset_of!(Device, device), - offset_of!(ReadOnlyDevice, device), - ); - assert_eq!( - offset_of!(Device, pipeline_cache), - offset_of!(ReadOnlyDevice, pipeline_cache), - ); + assert_eq!(offset_of!(Device, inner), offset_of!(ReadOnlyDevice, inner),); assert_eq!( offset_of!(Device, physical_device), offset_of!(ReadOnlyDevice, physical_device), diff --git a/src/driver/graphic.rs b/src/driver/graphic.rs index 589e4f03..d13b34cf 100644 --- a/src/driver/graphic.rs +++ b/src/driver/graphic.rs @@ -12,7 +12,7 @@ use { derive_builder::{Builder, UninitializedFieldError}, log::{Level::Trace, log_enabled, trace, warn}, ordered_float::OrderedFloat, - std::{collections::HashSet, ffi::CString, sync::Arc, thread::panicking}, + std::{collections::HashSet, ffi::CString, thread::panicking}, }; const RGBA_COLOR_COMPONENTS: vk::ColorComponentFlags = vk::ColorComponentFlags::from_raw( @@ -355,7 +355,7 @@ pub struct GraphicPipeline { /// /// _Note:_ This field is read-only. #[readonly] - pub device: Arc, + pub device: Device, /// Information used to create this object. #[readonly] @@ -408,7 +408,7 @@ impl GraphicPipeline { /// ``` #[profiling::function] pub fn create( - device: &Arc, + device: &Device, info: impl Into, shaders: impl IntoIterator, ) -> Result @@ -417,7 +417,7 @@ impl GraphicPipeline { { trace!("create"); - let device = Arc::clone(device); + let device = device.clone(); let info = info.into(); let shaders = shaders .into_iter() diff --git a/src/driver/image.rs b/src/driver/image.rs index 6750bd8e..d046d705 100644 --- a/src/driver/image.rs +++ b/src/driver/image.rs @@ -14,7 +14,6 @@ use { fmt::{Debug, Formatter}, mem::{replace, take}, ops::DerefMut, - sync::Arc, thread::panicking, }, vk_sync::AccessType, @@ -101,7 +100,7 @@ pub struct Image { /// /// _Note:_ This field is read-only. #[readonly] - pub device: Arc, + pub device: Device, /// The native Vulkan resource handle of this image. /// @@ -146,7 +145,7 @@ impl Image { /// # Ok(()) } /// ``` #[profiling::function] - pub fn create(device: &Arc, info: impl Into) -> Result { + pub fn create(device: &Device, info: impl Into) -> Result { let info: ImageInfo = info.into(); //trace!("create: {:?}", &info); @@ -160,7 +159,7 @@ impl Image { let accesses = Mutex::new(ImageAccess::new(info, AccessType::Nothing)); - let device = Arc::clone(device); + let device = device.clone(); let create_info: ImageCreateInfo = info.into(); let create_info = create_info.queue_family_indices(&device.physical_device.queue_family_indices); @@ -176,7 +175,7 @@ impl Image { profiling::scope!("allocate"); #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))] - let mut allocator = device.allocator.lock(); + let mut allocator = Device::allocator(&device).lock(); #[cfg(not(feature = "parking_lot"))] let mut allocator = allocator.unwrap(); @@ -336,7 +335,7 @@ impl Image { Self { accesses: Mutex::new(ImageAccess::new(info, AccessType::General)), allocation: None, - device: Arc::clone(&self.device), + device: self.device.clone(), handle, image_view_cache: Mutex::new(image_view_cache), info, @@ -366,7 +365,7 @@ impl Image { profiling::scope!("deallocate"); #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))] - let mut allocator = self.device.allocator.lock(); + let mut allocator = Device::allocator(&self.device).lock(); #[cfg(not(feature = "parking_lot"))] let mut allocator = allocator.unwrap(); @@ -381,8 +380,8 @@ impl Image { /// The image is not destroyed automatically on drop, unlike images created through the /// [`Image::create`] function. #[profiling::function] - pub fn from_raw(device: &Arc, handle: vk::Image, info: impl Into) -> Self { - let device = Arc::clone(device); + pub fn from_raw(device: &Device, handle: vk::Image, info: impl Into) -> Self { + let device = device.clone(); let info = info.into(); // For now default all image access to general, but maybe make this configurable later. @@ -959,19 +958,19 @@ impl From for vk::ImageSubresourceRange { } struct ImageView { - device: Arc, + device: Device, image_view: vk::ImageView, } impl ImageView { #[profiling::function] fn create( - device: &Arc, + device: &Device, info: impl Into, image: vk::Image, ) -> Result { let info = info.into(); - let device = Arc::clone(device); + let device = device.clone(); let create_info = vk::ImageViewCreateInfo::default() .view_type(info.ty) .format(info.fmt) diff --git a/src/driver/physical_device.rs b/src/driver/physical_device.rs index c612e6b7..56e72500 100644 --- a/src/driver/physical_device.rs +++ b/src/driver/physical_device.rs @@ -25,7 +25,7 @@ fn vk_cstr_to_string_lossy(cstr: &[c_char]) -> String { /// See /// [`VkPhysicalDeviceAccelerationStructurePropertiesKHR`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceAccelerationStructurePropertiesKHR.html) /// manual page. -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] pub struct AccelerationStructureProperties { /// The maximum number of geometries in a bottom level acceleration structure. pub max_geometry_count: u64, @@ -79,7 +79,7 @@ impl From> /// See /// [`VkPhysicalDeviceDepthStencilResolveProperties`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceDepthStencilResolveProperties.html) /// manual page. -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] pub struct DepthStencilResolveProperties { /// A bitmask indicating the set of supported depth resolve modes. /// @@ -123,7 +123,7 @@ impl From> for DepthStencilR /// See /// [`VkPhysicalDeviceIndexTypeUint8FeaturesEXT`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceIndexTypeUint8FeaturesEXT.html) /// manual page. -#[derive(Debug, Default)] +#[derive(Clone, Copy, Debug, Default)] pub struct IndexTypeUint8Features { /// Indicates that VK_INDEX_TYPE_UINT8_EXT can be used with vkCmdBindIndexBuffer2KHR and /// vkCmdBindIndexBuffer. @@ -139,6 +139,7 @@ impl From> for IndexTypeUint8Fea } /// Structure which holds data about the physical hardware selected by the current device. +#[derive(Clone)] #[readonly::make] pub struct PhysicalDevice { /// Describes the properties of the device which relate to acceleration structures, if @@ -559,7 +560,7 @@ impl Debug for PhysicalDevice { /// See /// [`VkPhysicalDeviceRayQueryFeaturesKHR`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceRayQueryFeaturesKHR.html) /// manual page. -#[derive(Debug, Default)] +#[derive(Clone, Copy, Debug, Default)] pub struct RayQueryFeatures { /// Indicates whether the implementation supports ray query (`OpRayQueryProceedKHR`) /// functionality. @@ -579,7 +580,7 @@ impl From> for RayQueryFeatures { /// See /// [`VkPhysicalDeviceRayTracingPipelineFeaturesKHR`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceRayTracingPipelineFeaturesKHR.html) /// manual page. -#[derive(Debug, Default)] +#[derive(Clone, Copy, Debug, Default)] pub struct RayTraceFeatures { /// Indicates whether the implementation supports the ray tracing pipeline functionality. /// @@ -629,7 +630,7 @@ impl From> for RayTraceFeatu /// See /// [`VkPhysicalDeviceRayTracingPipelinePropertiesKHR`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceRayTracingPipelinePropertiesKHR.html) /// manual page. -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] pub struct RayTraceProperties { /// The size in bytes of the shader header. pub shader_group_handle_size: u32, @@ -679,7 +680,7 @@ impl From> for RayTracePro /// /// See /// [`VkPhysicalDeviceSamplerFilterMinmaxProperties`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceSamplerFilterMinmaxPropertiesEXT.html) -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] pub struct SamplerFilterMinmaxProperties { /// When `false` the component mapping of the image view used with min/max filtering must have /// been created with the r component set to the identity swizzle. Only the r component of the @@ -725,7 +726,7 @@ impl From> for SamplerFilter /// See /// [`VkPhysicalDeviceFeatures`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceFeatures.html) /// manual page. -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] pub struct Vulkan10Features { /// Specifies that accesses to buffers are bounds-checked against the range of the buffer /// descriptor. @@ -1289,7 +1290,7 @@ impl From for Vulkan10Features { /// [`VkPhysicalDeviceLimits`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceLimits.html) /// manual page. #[allow(missing_docs)] // TODO: Finish docs! -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] pub struct Vulkan10Limits { /// The largest dimension (width) that is guaranteed to be supported for all images created with /// an image type of [`ImageType::Texture1D`](super::image::ImageType). @@ -1568,7 +1569,7 @@ impl From for Vulkan10Limits { /// See /// [`VkPhysicalDeviceProperties`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceProperties.html) /// manual page. -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Vulkan10Properties { /// The version of Vulkan supported by the device, encoded as described /// [here](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#extendingvulkan-coreversions-versionnumbers). @@ -1626,7 +1627,7 @@ impl From for Vulkan10Properties { /// See /// [`VkPhysicalDeviceVulkan11Features`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceVulkan11Features.html) /// manual page. -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] pub struct Vulkan11Features { /// Specifies whether objects in the StorageBuffer, ShaderRecordBufferKHR, or /// PhysicalStorageBuffer storage class with the Block decoration can have 16-bit integer and @@ -1735,7 +1736,7 @@ impl From> for Vulkan11Features { /// See /// [`VkPhysicalDeviceVulkan11Properties`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceVulkan11Properties.html) /// manual page. -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] pub struct Vulkan11Properties { /// An array of `VK_UUID_SIZE` `u8` values representing a universally unique identifier for /// the device @@ -1844,7 +1845,7 @@ impl From> for Vulkan11Properties { /// See /// [`VkPhysicalDeviceVulkan12Features`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceVulkan12Features.html) /// manual page. -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] pub struct Vulkan12Features { /// Indicates whether the implementation supports the /// `VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE` sampler address mode. @@ -2280,7 +2281,7 @@ impl From> for Vulkan12Features { /// See /// [`VkPhysicalDeviceVulkan12Properties`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceVulkan12Properties.html) /// manual page. -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Vulkan12Properties { /// A unique identifier for the driver of the physical device. pub driver_id: vk::DriverId, diff --git a/src/driver/ray_trace.rs b/src/driver/ray_trace.rs index a635c880..8287a9d3 100644 --- a/src/driver/ray_trace.rs +++ b/src/driver/ray_trace.rs @@ -11,7 +11,7 @@ use { ash::vk, derive_builder::{Builder, UninitializedFieldError}, log::warn, - std::{ffi::CString, sync::Arc, thread::panicking}, + std::{ffi::CString, thread::panicking}, }; /// Smart pointer handle to a [pipeline] object. @@ -38,7 +38,7 @@ pub struct RayTracePipeline { /// /// _Note:_ This field is read-only. #[readonly] - pub device: Arc, + pub device: Device, /// The native Vulkan resource handle of this pipeline. /// @@ -115,7 +115,7 @@ impl RayTracePipeline { /// ``` #[profiling::function] pub fn create( - device: &Arc, + device: &Device, info: impl Into, shaders: impl IntoIterator, shader_groups: impl IntoIterator, @@ -238,7 +238,7 @@ impl RayTracePipeline { let handle = ray_trace_ext .create_ray_tracing_pipelines( vk::DeferredOperationKHR::null(), - device.pipeline_cache, + Device::pipeline_cache(device), &[vk::RayTracingPipelineCreateInfoKHR::default() .stages(&shader_stages) .groups(&shader_groups) @@ -274,7 +274,7 @@ impl RayTracePipeline { DriverError::Unsupported })?[0]; - let device = Arc::clone(device); + let device = device.clone(); let &RayTraceProperties { shader_group_handle_size, .. diff --git a/src/driver/render_pass.rs b/src/driver/render_pass.rs index 5100dd24..c658b732 100644 --- a/src/driver/render_pass.rs +++ b/src/driver/render_pass.rs @@ -109,7 +109,7 @@ pub(crate) struct RenderPass { /// /// _Note:_ This field is read-only. #[readonly] - pub device: Arc, + pub device: Device, framebuffers: HashMap, graphic_pipelines: HashMap, @@ -129,11 +129,11 @@ pub(crate) struct RenderPass { impl RenderPass { #[profiling::function] - pub fn create(device: &Arc, info: RenderPassInfo) -> Result { + pub fn create(device: &Device, info: RenderPassInfo) -> Result { //trace!("create: \n{:#?}", &info); trace!("create"); - let device = Arc::clone(device); + let device = device.clone(); let attachments = info .attachments .iter() @@ -429,7 +429,7 @@ impl RenderPass { let pipeline = unsafe { self.device.create_graphics_pipelines( - self.device.pipeline_cache, + Device::pipeline_cache(&self.device), slice::from_ref(&create_info), None, ) diff --git a/src/driver/shader.rs b/src/driver/shader.rs index 3165e11e..8cd296dc 100644 --- a/src/driver/shader.rs +++ b/src/driver/shader.rs @@ -18,7 +18,6 @@ use { fmt::{Debug, Formatter}, iter::repeat_n, ops::Deref, - sync::Arc, thread::panicking, }, }; @@ -186,7 +185,7 @@ pub(crate) struct PipelineDescriptorInfo { impl PipelineDescriptorInfo { #[profiling::function] pub fn create( - device: &Arc, + device: &Device, descriptor_bindings: &DescriptorBindingMap, ) -> Result { let descriptor_set_count = descriptor_bindings @@ -325,14 +324,14 @@ impl PipelineDescriptorInfo { } pub(crate) struct Sampler { - device: Arc, + device: Device, sampler: vk::Sampler, } impl Sampler { #[profiling::function] - pub fn create(device: &Arc, info: impl Into) -> Result { - let device = Arc::clone(device); + pub fn create(device: &Device, info: impl Into) -> Result { + let device = device.clone(); let info = info.into(); let sampler = unsafe { diff --git a/src/driver/surface.rs b/src/driver/surface.rs index 28b911a1..6a1e82b7 100644 --- a/src/driver/surface.rs +++ b/src/driver/surface.rs @@ -8,7 +8,6 @@ use { raw_window_handle::{HasDisplayHandle, HasWindowHandle}, std::{ fmt::{Debug, Formatter}, - sync::Arc, thread::panicking, }, }; @@ -19,7 +18,7 @@ pub struct Surface { /// The device which owns this buffer resource. /// /// _Note:_ This field is read-only. - pub device: Arc, + pub device: Device, /// The native Vulkan resource handle of this surface. /// @@ -48,11 +47,11 @@ impl Surface { /// through [`Device::create_display_window`]. #[profiling::function] pub fn create( - device: &Arc, + device: &Device, display: impl HasDisplayHandle, window: impl HasWindowHandle, ) -> Result { - let device = Arc::clone(device); + let device = device.clone(); let display_handle = display.display_handle().map_err(|err| { warn!("{err}"); diff --git a/src/driver/swapchain.rs b/src/driver/swapchain.rs index fd3e1888..567785d2 100644 --- a/src/driver/swapchain.rs +++ b/src/driver/swapchain.rs @@ -9,7 +9,7 @@ use { ash::vk, derive_builder::{Builder, UninitializedFieldError}, log::{debug, info, trace, warn}, - std::{mem::replace, ops::Deref, slice, sync::Arc, thread::panicking}, + std::{mem::replace, ops::Deref, slice, thread::panicking}, }; // TODO: This needs to track completed command buffers and not constantly create semaphores @@ -21,7 +21,7 @@ pub struct Swapchain { /// The device which owns this buffer resource. /// /// _Note:_ This field is read-only. - pub device: Arc, + pub device: Device, /// The native Vulkan resource handle of this swapchain. /// @@ -49,11 +49,11 @@ impl Swapchain { /// [`acquire_next_image`][Self::acquire_next_image]. #[profiling::function] pub fn new( - device: &Arc, + device: &Device, surface: Surface, info: impl Into, ) -> Result { - let device = Arc::clone(device); + let device = device.clone(); let info = info.into(); Ok(Swapchain { diff --git a/src/pool/fifo.rs b/src/pool/fifo.rs index 1f0cc4be..10c194e1 100644 --- a/src/pool/fifo.rs +++ b/src/pool/fifo.rs @@ -46,7 +46,7 @@ pub struct FifoPool { buffer_cache: Cache, command_buffer_cache: HashMap>, descriptor_pool_cache: Cache, - device: Arc, + device: Device, image_cache: Cache, info: PoolInfo, render_pass_cache: HashMap>, @@ -54,14 +54,14 @@ pub struct FifoPool { impl FifoPool { /// Constructs a new `FifoPool`. - pub fn new(device: &Arc) -> Self { + pub fn new(device: &Device) -> Self { Self::with_capacity(device, PoolInfo::default()) } /// Constructs a new `FifoPool` with the given capacity information. - pub fn with_capacity(device: &Arc, info: impl Into) -> Self { + pub fn with_capacity(device: &Device, info: impl Into) -> Self { let info: PoolInfo = info.into(); - let device = Arc::clone(device); + let device = device.clone(); Self { accel_struct_cache: PoolInfo::explicit_cache(info.accel_struct_capacity), diff --git a/src/pool/hash.rs b/src/pool/hash.rs index 663100b2..f9279c4a 100644 --- a/src/pool/hash.rs +++ b/src/pool/hash.rs @@ -42,7 +42,7 @@ pub struct HashPool { buffer_cache: HashMap>, command_buffer_cache: HashMap>, descriptor_pool_cache: HashMap>, - device: Arc, + device: Device, image_cache: HashMap>, info: PoolInfo, render_pass_cache: HashMap>, @@ -50,14 +50,14 @@ pub struct HashPool { impl HashPool { /// Constructs a new `HashPool`. - pub fn new(device: &Arc) -> Self { + pub fn new(device: &Device) -> Self { Self::with_capacity(device, PoolInfo::default()) } /// Constructs a new `HashPool` with the given capacity information. - pub fn with_capacity(device: &Arc, info: impl Into) -> Self { + pub fn with_capacity(device: &Device, info: impl Into) -> Self { let info: PoolInfo = info.into(); - let device = Arc::clone(device); + let device = device.clone(); Self { acceleration_structure_cache: Default::default(), diff --git a/src/pool/lazy.rs b/src/pool/lazy.rs index 4b6d04a0..a1d9c428 100644 --- a/src/pool/lazy.rs +++ b/src/pool/lazy.rs @@ -76,7 +76,7 @@ pub struct LazyPool { buffer_cache: HashMap<(bool, vk::DeviceSize), Cache>, command_buffer_cache: HashMap>, descriptor_pool_cache: Cache, - device: Arc, + device: Device, image_cache: HashMap>, info: PoolInfo, render_pass_cache: HashMap>, @@ -84,14 +84,14 @@ pub struct LazyPool { impl LazyPool { /// Constructs a new `LazyPool`. - pub fn new(device: &Arc) -> Self { + pub fn new(device: &Device) -> Self { Self::with_capacity(device, PoolInfo::default()) } /// Constructs a new `LazyPool` with the given capacity information. - pub fn with_capacity(device: &Arc, info: impl Into) -> Self { + pub fn with_capacity(device: &Device, info: impl Into) -> Self { let info: PoolInfo = info.into(); - let device = Arc::clone(device); + let device = device.clone(); Self { accel_struct_cache: Default::default(), From 21d5b434b574aef63e9ebc7c573434a0bae6d431 Mon Sep 17 00:00:00 2001 From: John Wells Date: Fri, 20 Feb 2026 18:31:03 -0500 Subject: [PATCH 16/86] HOLD Use inner-arc on pipelines --- src/cmd_ref/compute.rs | 9 +-- src/cmd_ref/graphic.rs | 8 +- src/cmd_ref/mod.rs | 26 +----- src/cmd_ref/ray_trace.rs | 9 +-- src/driver/compute.rs | 78 +++++++++--------- src/driver/ray_trace.rs | 166 +++++++++++++++++--------------------- src/driver/render_pass.rs | 5 +- src/driver/shader.rs | 30 +++++++ src/edge.rs | 6 +- src/lib.rs | 15 ++-- src/resolver.rs | 8 +- 11 files changed, 169 insertions(+), 191 deletions(-) diff --git a/src/cmd_ref/compute.rs b/src/cmd_ref/compute.rs index 3e2b9b42..29b25837 100644 --- a/src/cmd_ref/compute.rs +++ b/src/cmd_ref/compute.rs @@ -45,7 +45,7 @@ pub struct Compute<'a> { pub(super) bindings: Bindings<'a>, pub(super) cmd_buf: vk::CommandBuffer, pub(super) device: &'a Device, - pub(super) pipeline: Arc, + pub(super) pipeline: ComputePipeline, } impl Compute<'_> { @@ -302,7 +302,7 @@ impl Compute<'_> { unsafe { self.device.cmd_push_constants( self.cmd_buf, - self.pipeline.layout, + self.pipeline.layout(), vk::ShaderStageFlags::COMPUTE, push_const.offset, &data[(start - offset) as usize..(end - offset) as usize], @@ -322,7 +322,7 @@ impl PipelineCommandRef<'_, ComputePipeline> { mut self, func: impl FnOnce(Compute<'_>, Bindings<'_>) + Send + 'static, ) -> Self { - let pipeline = Arc::clone( + let pipeline = self.cmd .as_ref() .execs @@ -331,8 +331,7 @@ impl PipelineCommandRef<'_, ComputePipeline> { .pipeline .as_ref() .unwrap() - .unwrap_compute(), - ); + .unwrap_compute().clone(); self.cmd.push_execute(move |device, cmd_buf, bindings| { func( diff --git a/src/cmd_ref/graphic.rs b/src/cmd_ref/graphic.rs index c83222e9..5bf15b27 100644 --- a/src/cmd_ref/graphic.rs +++ b/src/cmd_ref/graphic.rs @@ -61,7 +61,7 @@ pub struct Graphic<'a> { pub(super) bindings: Bindings<'a>, pub(super) cmd_buf: vk::CommandBuffer, pub(super) device: &'a Device, - pub(super) pipeline: Arc, + pub(super) pipeline: GraphicPipeline, } impl Graphic<'_> { @@ -1529,7 +1529,7 @@ impl PipelineCommandRef<'_, GraphicPipeline> { mut self, func: impl FnOnce(Graphic<'_>, Bindings<'_>) + Send + 'static, ) -> Self { - let pipeline = Arc::clone( + let pipeline = self.cmd .as_ref() .execs @@ -1538,8 +1538,8 @@ impl PipelineCommandRef<'_, GraphicPipeline> { .pipeline .as_ref() .unwrap() - .unwrap_graphic(), - ); + .unwrap_graphic().clone() + ; self.cmd.push_execute(move |device, cmd_buf, bindings| { func( diff --git a/src/cmd_ref/mod.rs b/src/cmd_ref/mod.rs index cf826b13..5f68de6a 100644 --- a/src/cmd_ref/mod.rs +++ b/src/cmd_ref/mod.rs @@ -74,7 +74,7 @@ impl Access for RayTracePipeline { macro_rules! bind { ($name:ident) => { paste::paste! { - impl<'a> Bind, PipelineCommandRef<'a, [<$name Pipeline>]>> for &'a Arc<[<$name Pipeline>]> { + impl<'a> Bind, PipelineCommandRef<'a, [<$name Pipeline>]>> for &'a [<$name Pipeline>] { // TODO: Allow binding as explicit secondary command buffers? like with compute/raytrace stuff fn bind(self, mut cmd: CommandRef<'a>) -> PipelineCommandRef<'a, [<$name Pipeline>]> { let cmd_ref = cmd.as_mut(); @@ -83,25 +83,7 @@ macro_rules! bind { cmd_ref.execs.push(Default::default()); } - cmd_ref.execs.last_mut().unwrap().pipeline = Some(ExecutionPipeline::$name(Arc::clone(self))); - - PipelineCommandRef { - __: PhantomData, - cmd, - } - } - } - - impl<'a> Bind, PipelineCommandRef<'a, [<$name Pipeline>]>> for Arc<[<$name Pipeline>]> { - // TODO: Allow binding as explicit secondary command buffers? like with compute/raytrace stuff - fn bind(self, mut cmd: CommandRef<'a>) -> PipelineCommandRef<'a, [<$name Pipeline>]> { - let cmd_ref = cmd.as_mut(); - if cmd_ref.execs.last().unwrap().pipeline.is_some() { - // Binding from PipelinePass -> PipelinePass (changing shaders) - cmd_ref.execs.push(Default::default()); - } - - cmd_ref.execs.last_mut().unwrap().pipeline = Some(ExecutionPipeline::$name(self)); + cmd_ref.execs.last_mut().unwrap().pipeline = Some(ExecutionPipeline::$name(self.clone())); PipelineCommandRef { __: PhantomData, @@ -119,7 +101,7 @@ macro_rules! bind { cmd_ref.execs.push(Default::default()); } - cmd_ref.execs.last_mut().unwrap().pipeline = Some(ExecutionPipeline::$name(Arc::new(self))); + cmd_ref.execs.last_mut().unwrap().pipeline = Some(ExecutionPipeline::$name(self)); PipelineCommandRef { __: PhantomData, @@ -135,7 +117,7 @@ macro_rules! bind { } #[allow(unused)] - pub(super) fn [](&self) -> &Arc<[<$name Pipeline>]> { + pub(super) fn [](&self) -> &[<$name Pipeline>] { if let Self::$name(binding) = self { &binding } else { diff --git a/src/cmd_ref/ray_trace.rs b/src/cmd_ref/ray_trace.rs index 08b6c081..84de822b 100644 --- a/src/cmd_ref/ray_trace.rs +++ b/src/cmd_ref/ray_trace.rs @@ -13,7 +13,7 @@ impl PipelineCommandRef<'_, RayTracePipeline> { mut self, func: impl FnOnce(RayTrace<'_>, Bindings<'_>) + Send + 'static, ) -> Self { - let pipeline = Arc::clone( + let pipeline = self.cmd .as_ref() .execs @@ -22,8 +22,7 @@ impl PipelineCommandRef<'_, RayTracePipeline> { .pipeline .as_ref() .unwrap() - .unwrap_ray_trace(), - ); + .unwrap_ray_trace().clone(); #[cfg(debug_assertions)] let dynamic_stack_size = pipeline.info.dynamic_stack_size; @@ -89,7 +88,7 @@ pub struct RayTrace<'a> { #[cfg(debug_assertions)] dynamic_stack_size: bool, - pipeline: Arc, + pipeline: RayTracePipeline, } impl RayTrace<'_> { @@ -178,7 +177,7 @@ impl RayTrace<'_> { unsafe { self.device.cmd_push_constants( self.cmd_buf, - self.pipeline.layout, + self.pipeline.layout(), push_const.stage_flags, start, &data[(start - offset) as usize..(end - offset) as usize], diff --git a/src/driver/compute.rs b/src/driver/compute.rs index 5b263fde..c5119159 100644 --- a/src/driver/compute.rs +++ b/src/driver/compute.rs @@ -4,12 +4,12 @@ use { super::{ DriverError, device::Device, - shader::{DescriptorBindingMap, PipelineDescriptorInfo, Shader}, + shader::{DescriptorBindingMap, PipelineDescriptorInfo, PipelineHandle, PipelineInner, Shader}, }, ash::vk, derive_builder::{Builder, UninitializedFieldError}, log::{trace, warn}, - std::{ffi::CString, slice, thread::panicking}, + std::{ffi::CString, slice, sync::Arc}, }; /// Smart pointer handle to a [pipeline] object. @@ -23,30 +23,17 @@ use { /// /// [pipeline]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipeline.html /// [deref]: core::ops::Deref -#[derive(Debug)] +#[derive(Clone, Debug)] #[readonly::make] pub struct ComputePipeline { pub(crate) descriptor_bindings: DescriptorBindingMap, - pub(crate) descriptor_info: PipelineDescriptorInfo, - - /// The device which owns this buffer resource. - /// - /// _Note:_ This field is read-only. - #[readonly] - pub device: Device, - - pub(crate) layout: vk::PipelineLayout, - - /// The native Vulkan resource handle of this pipeline. - /// - /// _Note:_ This field is read-only. - #[readonly] - pub handle: vk::Pipeline, /// Information used to create this object. #[readonly] pub info: ComputePipelineInfo, + inner: Arc, + /// A descriptive name used in debugging messages. pub name: Option, @@ -78,7 +65,7 @@ impl ComputePipeline { /// let shader = Shader::new_compute(my_shader_code.as_slice()); /// let pipeline = ComputePipeline::create(&device, ComputePipelineInfo::default(), shader)?; /// - /// assert_ne!(pipeline.handle, vk::Pipeline::null()); + /// assert_ne!(pipeline.handle(), vk::Pipeline::null()); /// # Ok(()) } /// ``` #[profiling::function] @@ -89,8 +76,7 @@ impl ComputePipeline { ) -> Result { trace!("create"); - let device = device.clone(); - let info: ComputePipelineInfo = info.into(); + let info = info.into(); let shader = shader.into(); // Use SPIR-V reflection to get the types and counts of all descriptors @@ -101,7 +87,7 @@ impl ComputePipeline { } } - let descriptor_info = PipelineDescriptorInfo::create(&device, &descriptor_bindings)?; + let descriptor_info = PipelineDescriptorInfo::create(device, &descriptor_bindings)?; let descriptor_set_layouts = descriptor_info .layouts .values() @@ -156,7 +142,7 @@ impl ComputePipeline { .layout(layout); let handle = device .create_compute_pipelines( - Device::pipeline_cache(&device), + Device::pipeline_cache(device), slice::from_ref(&create_info), None, ) @@ -172,17 +158,41 @@ impl ComputePipeline { Ok(ComputePipeline { descriptor_bindings, - descriptor_info, - device, - handle, info, - layout, + inner: Arc::new(PipelineInner { + descriptor_info, + device: device.clone(), + handle: PipelineHandle::Handle(handle), + layout, + }), name: None, push_constants, }) } } + pub(crate) fn descriptor_info(&self) -> &PipelineDescriptorInfo { + &self.inner.descriptor_info + } + + /// The device which owns this compute pipeline. + pub fn device(&self) -> &Device { + &self.inner.device + } + + /// The native Vulkan pipeline handle of this compute pipeline. + pub fn handle(&self) -> vk::Pipeline { + let PipelineHandle::Handle(handle) = self.inner.handle else { + unreachable!(); + }; + + handle + } + + pub(crate) fn layout(&self) -> vk::PipelineLayout { + self.inner.layout + } + /// Sets the debugging name assigned to this pipeline. pub fn with_name(mut self, name: impl Into) -> Self { self.name = Some(name.into()); @@ -190,20 +200,6 @@ impl ComputePipeline { } } -impl Drop for ComputePipeline { - #[profiling::function] - fn drop(&mut self) { - if panicking() { - return; - } - - unsafe { - self.device.destroy_pipeline(self.handle, None); - self.device.destroy_pipeline_layout(self.layout, None); - } - } -} - /// Information used to create a [`ComputePipeline`] instance. #[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)] #[builder( diff --git a/src/driver/ray_trace.rs b/src/driver/ray_trace.rs index 8287a9d3..c4381a86 100644 --- a/src/driver/ray_trace.rs +++ b/src/driver/ray_trace.rs @@ -6,12 +6,12 @@ use { device::Device, merge_push_constant_ranges, physical_device::RayTraceProperties, - shader::{DescriptorBindingMap, PipelineDescriptorInfo, Shader}, + shader::{DescriptorBindingMap, PipelineDescriptorInfo,PipelineHandle, PipelineInner, Shader}, }, ash::vk, derive_builder::{Builder, UninitializedFieldError}, log::warn, - std::{ffi::CString, thread::panicking}, + std::{ffi::CString, sync::Arc}, }; /// Smart pointer handle to a [pipeline] object. @@ -28,35 +28,21 @@ use { /// [pipeline]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipeline.html /// [deref]: core::ops::Deref /// [fully qualified syntax]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name -#[derive(Debug)] +#[derive(Clone, Debug)] #[readonly::make] pub struct RayTracePipeline { pub(crate) descriptor_bindings: DescriptorBindingMap, - pub(crate) descriptor_info: PipelineDescriptorInfo, - - /// The device which owns this buffer resource. - /// - /// _Note:_ This field is read-only. - #[readonly] - pub device: Device, - - /// The native Vulkan resource handle of this pipeline. - /// - /// _Note:_ This field is read-only. - #[readonly] - pub handle: vk::Pipeline, /// Information used to create this object. #[readonly] pub info: RayTracePipelineInfo, - pub(crate) layout: vk::PipelineLayout, + inner: Arc, /// A descriptive name used in debugging messages. pub name: Option, pub(crate) push_constants: Vec, - shader_modules: Vec, shader_group_handles: Vec, } @@ -109,7 +95,7 @@ impl RayTracePipeline { /// ], /// )?; /// - /// assert_ne!(pipeline.handle, vk::Pipeline::null()); + /// assert_ne!(pipeline.handle(), vk::Pipeline::null()); /// assert_eq!(pipeline.info.max_ray_recursion_depth, 1); /// # Ok(()) } /// ``` @@ -235,46 +221,45 @@ impl RayTracePipeline { } let ray_trace_ext = Device::expect_ray_trace_ext(device); - let handle = ray_trace_ext - .create_ray_tracing_pipelines( - vk::DeferredOperationKHR::null(), - Device::pipeline_cache(device), - &[vk::RayTracingPipelineCreateInfoKHR::default() - .stages(&shader_stages) - .groups(&shader_groups) - .max_pipeline_ray_recursion_depth( - info.max_ray_recursion_depth.min( - device - .physical_device - .ray_trace_properties - .as_ref() - .unwrap() - .max_ray_recursion_depth, - ), - ) - .layout(layout) - .dynamic_state( - &vk::PipelineDynamicStateCreateInfo::default() - .dynamic_states(&dynamic_states), - )], - None, - ) - .map_err(|(pipelines, err)| { - warn!("{err}"); + let handle = ray_trace_ext.create_ray_tracing_pipelines( + vk::DeferredOperationKHR::null(), + Device::pipeline_cache(device), + &[vk::RayTracingPipelineCreateInfoKHR::default() + .stages(&shader_stages) + .groups(&shader_groups) + .max_pipeline_ray_recursion_depth( + info.max_ray_recursion_depth.min( + device + .physical_device + .ray_trace_properties + .as_ref() + .unwrap() + .max_ray_recursion_depth, + ), + ) + .layout(layout) + .dynamic_state( + &vk::PipelineDynamicStateCreateInfo::default() + .dynamic_states(&dynamic_states), + )], + None, + ); + + for shader_module in shader_modules.iter().copied() { + device.destroy_shader_module(shader_module, None); + } - for pipeline in pipelines { - device.destroy_pipeline(pipeline, None); - } + let handle = handle.map_err(|(pipelines, err)| { + warn!("{err}"); - device.destroy_pipeline_layout(layout, None); + for pipeline in pipelines { + device.destroy_pipeline(pipeline, None); + } - for shader_module in shader_modules.iter().copied() { - device.destroy_shader_module(shader_module, None); - } + device.destroy_pipeline_layout(layout, None); - DriverError::Unsupported - })?[0]; - let device = device.clone(); + DriverError::Unsupported + })?[0]; let &RayTraceProperties { shader_group_handle_size, .. @@ -308,19 +293,29 @@ impl RayTracePipeline { Ok(Self { descriptor_bindings, - descriptor_info, - device, - handle, + inner: Arc::new(PipelineInner { + descriptor_info, + device: device.clone(), + handle: PipelineHandle::Handle(handle), + layout, + }), info, - layout, name: None, push_constants, - shader_modules, shader_group_handles, }) } } + pub(crate) fn descriptor_info(&self) -> &PipelineDescriptorInfo { + &self.inner.descriptor_info + } + + /// The device which owns this compute pipeline. + pub fn device(&self) -> &Device { + &self.inner.device + } + /// Function returning a handle to a shader group of this pipeline. /// This can be used to construct a sbt. /// @@ -334,6 +329,7 @@ impl RayTracePipeline { shader_group_handle_size, .. } = self + .inner .device .physical_device .ray_trace_properties @@ -357,14 +353,24 @@ impl RayTracePipeline { ) -> vk::DeviceSize { unsafe { // Safely use unchecked because ray_trace_ext is checked during pipeline creation - Device::expect_ray_trace_ext(&self.device).get_ray_tracing_shader_group_stack_size( - self.handle, - group, - group_shader, - ) + Device::expect_ray_trace_ext(&self.inner.device) + .get_ray_tracing_shader_group_stack_size(self.handle(), group, group_shader) } } + /// The native Vulkan pipeline handle of this compute pipeline. + pub fn handle(&self) -> vk::Pipeline { + let PipelineHandle::Handle(handle) = self.inner.handle else { + unreachable!(); + }; + + handle + } + + pub(crate) fn layout(&self) -> vk::PipelineLayout { + self.inner.layout + } + /// Sets the debugging name assigned to this pipeline. pub fn with_name(mut self, name: impl Into) -> Self { self.name = Some(name.into()); @@ -372,26 +378,6 @@ impl RayTracePipeline { } } -impl Drop for RayTracePipeline { - #[profiling::function] - fn drop(&mut self) { - if panicking() { - return; - } - - unsafe { - self.device.destroy_pipeline(self.handle, None); - self.device.destroy_pipeline_layout(self.layout, None); - } - - for shader_module in self.shader_modules.drain(..) { - unsafe { - self.device.destroy_shader_module(shader_module, None); - } - } - } -} - /// Information used to create a [`RayTracePipeline`] instance. #[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)] #[builder( @@ -481,15 +467,7 @@ impl RayTracePipelineInfoBuilder { /// Builds a new `RayTracePipelineInfo`. #[inline(always)] pub fn build(self) -> RayTracePipelineInfo { - let res = self.fallible_build(); - - #[cfg(test)] - let res = res.unwrap(); - - #[cfg(not(test))] - let res = unsafe { res.unwrap_unchecked() }; - - res + self.fallible_build().unwrap() } } diff --git a/src/driver/render_pass.rs b/src/driver/render_pass.rs index c658b732..2d65b32b 100644 --- a/src/driver/render_pass.rs +++ b/src/driver/render_pass.rs @@ -91,7 +91,6 @@ pub(crate) struct FramebufferInfo { struct GraphicPipelineKey { depth_stencil: Option, layout: vk::PipelineLayout, - shader_modules: Vec, subpass_idx: u32, } @@ -330,7 +329,6 @@ impl RenderPass { let entry = self.graphic_pipelines.entry(GraphicPipelineKey { depth_stencil, layout: pipeline.layout, - shader_modules: pipeline.shader_modules.clone(), subpass_idx, }); if let Entry::Occupied(entry) = entry { @@ -349,9 +347,8 @@ impl RenderPass { .collect::>(); let color_blend_state = vk::PipelineColorBlendStateCreateInfo::default() .attachments(&color_blend_attachment_states); - let dynamic_states = [vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR]; let dynamic_state = - vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&dynamic_states); + vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&[vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR]); let multisample_state = vk::PipelineMultisampleStateCreateInfo::default() .alpha_to_coverage_enable(pipeline.state.multisample.alpha_to_coverage_enable) .alpha_to_one_enable(pipeline.state.multisample.alpha_to_one_enable) diff --git a/src/driver/shader.rs b/src/driver/shader.rs index 8cd296dc..08c892e7 100644 --- a/src/driver/shader.rs +++ b/src/driver/shader.rs @@ -323,6 +323,36 @@ impl PipelineDescriptorInfo { } } +#[derive(Debug)] +pub(crate) enum PipelineHandle { + Handle(vk::Pipeline), +} + +#[derive(Debug)] +pub(crate) struct PipelineInner { + pub descriptor_info: PipelineDescriptorInfo, + pub device: Device, + pub handle: PipelineHandle, + pub layout: vk::PipelineLayout, +} + +impl Drop for PipelineInner { + #[profiling::function] + fn drop(&mut self) { + if panicking() { + return; + } + + unsafe { + match self.handle { + PipelineHandle::Handle(handle) => self.device.destroy_pipeline(handle, None), + } + + self.device.destroy_pipeline_layout(self.layout, None); + } + } +} + pub(crate) struct Sampler { device: Device, sampler: vk::Sampler, diff --git a/src/edge.rs b/src/edge.rs index 5c16b0e5..51c6094e 100644 --- a/src/edge.rs +++ b/src/edge.rs @@ -76,11 +76,7 @@ node_ref!(Arc> => ImageLeaseNode); macro_rules! pipeline { ($name:ident) => { paste::paste! { - impl<'a> Edge> for &'a Arc<[<$name Pipeline>]> { - type Result = PipelineCommandRef<'a, [<$name Pipeline>]>; - } - - impl<'a> Edge> for Arc<[<$name Pipeline>]> { + impl<'a> Edge> for &'a [<$name Pipeline>] { type Result = PipelineCommandRef<'a, [<$name Pipeline>]>; } diff --git a/src/lib.rs b/src/lib.rs index 433bc6fd..47aedccb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -117,6 +117,7 @@ let my_image = Image::create(&device, info)?; // Note: info is a field provided through the Deref trait and is immutable! assert_eq!(8, my_image.info.width); # Ok(()) } +``` ## Pooling @@ -567,9 +568,9 @@ struct ExecutionFunction(ExecFn); #[derive(Clone, Debug)] enum ExecutionPipeline { - Compute(Arc), - Graphic(Arc), - RayTrace(Arc), + Compute(ComputePipeline), + Graphic(GraphicPipeline), + RayTrace(RayTracePipeline), } impl ExecutionPipeline { @@ -599,17 +600,17 @@ impl ExecutionPipeline { fn descriptor_info(&self) -> &PipelineDescriptorInfo { match self { - ExecutionPipeline::Compute(pipeline) => &pipeline.descriptor_info, + ExecutionPipeline::Compute(pipeline) => pipeline.descriptor_info(), ExecutionPipeline::Graphic(pipeline) => &pipeline.descriptor_info, - ExecutionPipeline::RayTrace(pipeline) => &pipeline.descriptor_info, + ExecutionPipeline::RayTrace(pipeline) => pipeline.descriptor_info(), } } fn layout(&self) -> vk::PipelineLayout { match self { - ExecutionPipeline::Compute(pipeline) => pipeline.layout, + ExecutionPipeline::Compute(pipeline) => pipeline.layout(), ExecutionPipeline::Graphic(pipeline) => pipeline.layout, - ExecutionPipeline::RayTrace(pipeline) => pipeline.layout, + ExecutionPipeline::RayTrace(pipeline) => pipeline.layout(), } } diff --git a/src/resolver.rs b/src/resolver.rs index 0fc0ed35..21596f20 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -634,13 +634,13 @@ impl Resolver { if log_enabled!(Trace) { let (ty, name, vk_pipeline) = match pipeline { ExecutionPipeline::Compute(pipeline) => { - ("compute", pipeline.name.as_ref(), pipeline.handle) + ("compute", pipeline.name.as_ref(), pipeline.handle()) } ExecutionPipeline::Graphic(pipeline) => { ("graphic", pipeline.name.as_ref(), vk::Pipeline::null()) } ExecutionPipeline::RayTrace(pipeline) => { - ("ray trace", pipeline.name.as_ref(), pipeline.handle) + ("ray trace", pipeline.name.as_ref(), pipeline.handle()) } }; if let Some(name) = name { @@ -653,14 +653,14 @@ impl Resolver { // We store a shared reference to this pipeline inside the command buffer! let pipeline_bind_point = pipeline.bind_point(); let pipeline = match pipeline { - ExecutionPipeline::Compute(pipeline) => pipeline.handle, + ExecutionPipeline::Compute(pipeline) => pipeline.handle(), ExecutionPipeline::Graphic(pipeline) => RenderPass::graphic_pipeline( physical_pass.render_pass.as_mut().unwrap(), pipeline, depth_stencil, exec_idx as _, )?, - ExecutionPipeline::RayTrace(pipeline) => pipeline.handle, + ExecutionPipeline::RayTrace(pipeline) => pipeline.handle(), }; unsafe { From ace8a0f57c324239b29e89826a0c3f6fd126c6f3 Mon Sep 17 00:00:00 2001 From: John Wells Date: Sat, 21 Feb 2026 13:24:18 -0500 Subject: [PATCH 17/86] Add inner-arc pattern to pipelines --- contrib/vk-graph-egui/src/lib.rs | 50 ++- contrib/vk-graph-fx/src/bitmap_font.rs | 38 +- contrib/vk-graph-fx/src/image_loader.rs | 28 +- contrib/vk-graph-fx/src/presenter.rs | 35 +- contrib/vk-graph-fx/src/transition.rs | 536 ++++++++++++------------ contrib/vk-graph-hot/src/compute.rs | 28 +- contrib/vk-graph-hot/src/graphic.rs | 28 +- contrib/vk-graph-hot/src/ray_trace.rs | 28 +- contrib/vk-graph-imgui/src/lib.rs | 26 +- examples/bindless.rs | 6 +- examples/debugger.rs | 8 +- examples/font_bmp.rs | 6 +- examples/fuzzer.rs | 56 ++- examples/image_sampler.rs | 6 +- examples/min_max.rs | 4 +- examples/mip_graphic.rs | 12 +- examples/msaa.rs | 6 +- examples/multipass.rs | 42 +- examples/multithread.rs | 8 +- examples/ray_omni.rs | 6 +- examples/ray_trace.rs | 6 +- examples/rt_triangle.rs | 6 +- examples/shader-toy/src/main.rs | 42 +- examples/skeletal-anim/src/main.rs | 6 +- examples/subgroup_ops.rs | 18 +- examples/triangle.rs | 4 +- examples/vertex_layout.rs | 57 ++- examples/vsm_omni.rs | 52 +-- src/cmd_ref/accel.rs | 4 +- src/cmd_ref/compute.rs | 46 +- src/cmd_ref/graphic.rs | 53 ++- src/cmd_ref/mod.rs | 4 +- src/cmd_ref/ray_trace.rs | 49 +-- src/display.rs | 7 +- src/driver/accel_struct.rs | 23 +- src/driver/buffer.rs | 16 +- src/driver/compute.rs | 100 +++-- src/driver/device.rs | 83 ++-- src/driver/graphic.rs | 159 +++---- src/driver/image.rs | 6 +- src/driver/ray_trace.rs | 113 +++-- src/driver/render_pass.rs | 60 ++- src/driver/shader.rs | 40 +- src/driver/swapchain.rs | 16 +- src/lib.rs | 36 +- src/pool/mod.rs | 2 +- src/resolver.rs | 47 +-- 47 files changed, 973 insertions(+), 1039 deletions(-) diff --git a/contrib/vk-graph-egui/src/lib.rs b/contrib/vk-graph-egui/src/lib.rs index 00b4326e..208f2c93 100644 --- a/contrib/vk-graph-egui/src/lib.rs +++ b/contrib/vk-graph-egui/src/lib.rs @@ -26,7 +26,7 @@ pub struct Egui { egui_winit: egui_winit::State, textures: HashMap>>, cache: HashPool, - ppl: Arc, + ppl: GraphicPipeline, next_tex_id: u64, user_textures: HashMap, } @@ -34,31 +34,29 @@ pub struct Egui { impl Egui { /// TODO pub fn new(device: &Device, display_target: &dyn HasDisplayHandle) -> Self { - let ppl = Arc::new( - GraphicPipeline::create( - device, - GraphicPipelineInfoBuilder::default() - .blend(BlendMode { - blend_enable: true, - src_color_blend_factor: vk::BlendFactor::ONE, - dst_color_blend_factor: vk::BlendFactor::ONE_MINUS_SRC_ALPHA, - color_blend_op: vk::BlendOp::ADD, - src_alpha_blend_factor: vk::BlendFactor::ONE, - dst_alpha_blend_factor: vk::BlendFactor::ONE, - alpha_blend_op: vk::BlendOp::ADD, - color_write_mask: vk::ColorComponentFlags::R - | vk::ColorComponentFlags::G - | vk::ColorComponentFlags::B - | vk::ColorComponentFlags::A, - }) - .cull_mode(vk::CullModeFlags::NONE), - [ - Shader::new_vertex(include_glsl!("shaders/egui.vert").as_slice()), - Shader::new_fragment(include_glsl!("shaders/egui.frag").as_slice()), - ], - ) - .unwrap(), - ); + let ppl = GraphicPipeline::create( + device, + GraphicPipelineInfoBuilder::default() + .blend(BlendMode { + blend_enable: true, + src_color_blend_factor: vk::BlendFactor::ONE, + dst_color_blend_factor: vk::BlendFactor::ONE_MINUS_SRC_ALPHA, + color_blend_op: vk::BlendOp::ADD, + src_alpha_blend_factor: vk::BlendFactor::ONE, + dst_alpha_blend_factor: vk::BlendFactor::ONE, + alpha_blend_op: vk::BlendOp::ADD, + color_write_mask: vk::ColorComponentFlags::R + | vk::ColorComponentFlags::G + | vk::ColorComponentFlags::B + | vk::ColorComponentFlags::A, + }) + .cull_mode(vk::CullModeFlags::NONE), + [ + Shader::new_vertex(include_glsl!("shaders/egui.vert").as_slice()), + Shader::new_fragment(include_glsl!("shaders/egui.frag").as_slice()), + ], + ) + .unwrap(); let ctx = egui::Context::default(); let max_texture_side = Some( diff --git a/contrib/vk-graph-fx/src/bitmap_font.rs b/contrib/vk-graph-fx/src/bitmap_font.rs index 8b06d123..307524c1 100644 --- a/contrib/vk-graph-fx/src/bitmap_font.rs +++ b/contrib/vk-graph-fx/src/bitmap_font.rs @@ -25,7 +25,7 @@ pub struct BitmapFont { cache: HashPool, font: BMFont, pages: Vec>, - pipeline: Arc, + pipeline: GraphicPipeline, } impl BitmapFont { @@ -38,25 +38,23 @@ impl BitmapFont { let cache = HashPool::new(device); let pages = pages.into(); let num_pages = pages.len() as u32; - let pipeline = Arc::new( - GraphicPipeline::create( - device, - GraphicPipelineInfoBuilder::default().blend(BlendMode::ALPHA), - [ - Shader::new_vertex(include_glsl!("res/shader/graphic/font.vert").as_slice()), - Shader::new_fragment(include_glsl!("res/shader/graphic/font.frag").as_slice()) - .specialization_info(SpecializationInfo::new( - [vk::SpecializationMapEntry { - constant_id: 0, - offset: 0, - size: 4, - }], - num_pages.to_ne_bytes(), - )), - ], - ) - .context("Unable to create bitmap font pipeline")?, - ); + let pipeline = GraphicPipeline::create( + device, + GraphicPipelineInfoBuilder::default().blend(BlendMode::ALPHA), + [ + Shader::new_vertex(include_glsl!("res/shader/graphic/font.vert").as_slice()), + Shader::new_fragment(include_glsl!("res/shader/graphic/font.frag").as_slice()) + .specialization_info(SpecializationInfo::new( + [vk::SpecializationMapEntry { + constant_id: 0, + offset: 0, + size: 4, + }], + num_pages.to_ne_bytes(), + )), + ], + ) + .context("Unable to create bitmap font pipeline")?; Ok(Self { cache, diff --git a/contrib/vk-graph-fx/src/image_loader.rs b/contrib/vk-graph-fx/src/image_loader.rs index 23b62158..4fce830e 100644 --- a/contrib/vk-graph-fx/src/image_loader.rs +++ b/contrib/vk-graph-fx/src/image_loader.rs @@ -37,8 +37,8 @@ impl ImageFormat { #[derive(Debug)] pub struct ImageLoader { pool: HashPool, - _decode_r_rg: Arc, - decode_rgb_rgba: Arc, + _decode_r_rg: ComputePipeline, + decode_rgb_rgba: ComputePipeline, /// TODO pub device: Device, @@ -49,20 +49,20 @@ impl ImageLoader { pub fn new(device: &Device) -> Result { Ok(Self { pool: HashPool::new(device), - _decode_r_rg: Arc::new(ComputePipeline::create( + _decode_r_rg: ComputePipeline::create( device, ComputePipelineInfo::default(), Shader::new_compute( include_glsl!("res/shader/compute/decode_bitmap_r_rg.comp").as_slice(), ), - )?), - decode_rgb_rgba: Arc::new(ComputePipeline::create( + )?, + decode_rgb_rgba: ComputePipeline::create( device, ComputePipelineInfo::default(), Shader::new_compute( include_glsl!("res/shader/compute/decode_bitmap_rgb_rgba.comp").as_slice(), ), - )?), + )?, device: device.clone(), }) } @@ -118,8 +118,8 @@ impl ImageLoader { #[allow(clippy::too_many_arguments)] pub fn decode_bitmap( &mut self, - queue_family_index: usize, - queue_index: usize, + queue_family_index: u32, + queue_index: u32, pixels: &[u8], format: ImageFormat, width: u32, @@ -247,8 +247,8 @@ impl ImageLoader { /// TODO pub fn decode_linear( &mut self, - queue_family_index: usize, - queue_index: usize, + queue_family_index: u32, + queue_index: u32, pixels: &[u8], format: ImageFormat, width: u32, @@ -268,8 +268,8 @@ impl ImageLoader { /// TODO pub fn decode_srgb( &mut self, - queue_family_index: usize, - queue_index: usize, + queue_family_index: u32, + queue_index: u32, pixels: &[u8], format: ImageFormat, width: u32, @@ -289,8 +289,8 @@ impl ImageLoader { /// TODO pub fn load_bitmap_font<'a>( &mut self, - queue_family_index: usize, - queue_index: usize, + queue_family_index: u32, + queue_index: u32, font: BMFont, pages: impl IntoIterator, ) -> anyhow::Result { diff --git a/contrib/vk-graph-fx/src/presenter.rs b/contrib/vk-graph-fx/src/presenter.rs index 44d821f4..8e117a2b 100644 --- a/contrib/vk-graph-fx/src/presenter.rs +++ b/contrib/vk-graph-fx/src/presenter.rs @@ -1,27 +1,26 @@ use { bytemuck::cast_slice, glam::{vec3, Mat4}, - std::sync::Arc, vk_graph_prelude::*, vk_shader_macros::include_glsl, }; /// TODO -pub struct ComputePresenter([Arc; 2]); +pub struct ComputePresenter([ComputePipeline; 2]); impl ComputePresenter { /// TODO pub fn new(device: &Device) -> Result { - let pipeline1 = Arc::new(ComputePipeline::create( + let pipeline1 = ComputePipeline::create( device, ComputePipelineInfo::default(), Shader::new_compute(include_glsl!("res/shader/compute/present1.comp").as_slice()), - )?); - let pipeline2 = Arc::new(ComputePipeline::create( + )?; + let pipeline2 = ComputePipeline::create( device, ComputePipelineInfo::default(), Shader::new_compute(include_glsl!("res/shader/compute/present2.comp").as_slice()), - )?); + )?; Ok(Self([pipeline1, pipeline2])) } @@ -81,24 +80,22 @@ impl ComputePresenter { /// TODO pub struct GraphicPresenter { - pipeline: Arc, + pipeline: GraphicPipeline, } impl GraphicPresenter { /// TODO pub fn new(device: &Device) -> Result { - Ok(Self { - pipeline: Arc::new(GraphicPipeline::create( - device, - GraphicPipelineInfo::default(), - [ - Shader::new_vertex(include_glsl!("res/shader/graphic/present.vert").as_slice()), - Shader::new_fragment( - include_glsl!("res/shader/graphic/present.frag").as_slice(), - ), - ], - )?), - }) + let pipeline = GraphicPipeline::create( + device, + GraphicPipelineInfo::default(), + [ + Shader::new_vertex(include_glsl!("res/shader/graphic/present.vert").as_slice()), + Shader::new_fragment(include_glsl!("res/shader/graphic/present.frag").as_slice()), + ], + )?; + + Ok(Self { pipeline }) } /// TODO diff --git a/contrib/vk-graph-fx/src/transition.rs b/contrib/vk-graph-fx/src/transition.rs index 79bfcccb..7fc06b2b 100644 --- a/contrib/vk-graph-fx/src/transition.rs +++ b/contrib/vk-graph-fx/src/transition.rs @@ -2,12 +2,7 @@ // NOTE: Some are rough or broken and need a bit of care - others should be optimized for production // use. -use { - log::trace, - std::{collections::HashMap, sync::Arc}, - vk_graph_prelude::*, - vk_shader_macros::include_glsl, -}; +use {log::trace, std::collections::HashMap, vk_graph_prelude::*, vk_shader_macros::include_glsl}; #[allow(missing_docs)] #[derive(Clone, Copy, Debug)] @@ -386,7 +381,7 @@ impl Transition { pub struct TransitionPipeline { cache: HashPool, device: Device, - pipelines: HashMap>, + pipelines: HashMap, } impl TransitionPipeline { @@ -471,7 +466,7 @@ impl TransitionPipeline { render_graph .begin_cmd() .with_name(format!("transition {transition_ty:?}")) - .bind_pipeline(&pipeline) + .bind_pipeline(pipeline) .read_descriptor(0, a_image) .read_descriptor(1, b_image) .write_descriptor(2, dest_image) @@ -481,277 +476,266 @@ impl TransitionPipeline { }); } - fn pipeline(&mut self, transition_ty: TransitionType) -> Arc { - let pipeline = self.pipelines.entry(transition_ty).or_insert_with(|| { + fn pipeline(&mut self, transition_ty: TransitionType) -> &ComputePipeline { + self.pipelines.entry(transition_ty).or_insert_with(|| { trace!("creating {transition_ty:?}"); - Arc::new( - ComputePipeline::create( - &self.device, - ComputePipelineInfo::default(), - Shader::new_compute(match transition_ty { - TransitionType::Angular => { - include_glsl!("res/shader/transition/angular.comp").as_slice() - } - TransitionType::Bounce => { - include_glsl!("res/shader/transition/bounce.comp").as_slice() - } - TransitionType::BowTieHorizontal => { - include_glsl!("res/shader/transition/bow_tie_horizontal.comp") - .as_slice() - } - TransitionType::BowTieVertical => { - include_glsl!("res/shader/transition/bow_tie_vertical.comp").as_slice() - } - TransitionType::BowTieWithParameter => { - include_glsl!("res/shader/transition/bow_tie_with_parameter.comp",) - .as_slice() - } - TransitionType::Burn => { - include_glsl!("res/shader/transition/burn.comp").as_slice() - } - TransitionType::ButterflyWaveScrawler => { - include_glsl!("res/shader/transition/butterfly_wave_scrawler.comp",) - .as_slice() - } - TransitionType::CannabisLeaf => { - include_glsl!("res/shader/transition/cannabis_leaf.comp").as_slice() - } - TransitionType::Circle => { - include_glsl!("res/shader/transition/circle.comp").as_slice() - } - TransitionType::CircleCrop => { - include_glsl!("res/shader/transition/circle_crop.comp").as_slice() - } - TransitionType::CircleOpen => { - include_glsl!("res/shader/transition/circle_open.comp").as_slice() - } - TransitionType::ColorDistance => { - include_glsl!("res/shader/transition/color_distance.comp").as_slice() - } - TransitionType::ColorPhase => { - include_glsl!("res/shader/transition/color_phase.comp").as_slice() - } - TransitionType::CoordFromIn => { - include_glsl!("res/shader/transition/coord_from_in.comp").as_slice() - } - TransitionType::CrazyParametricFun => { - include_glsl!("res/shader/transition/crazy_parametric_fun.comp") - .as_slice() - } - TransitionType::Crosshatch => { - include_glsl!("res/shader/transition/crosshatch.comp").as_slice() - } - TransitionType::CrossWarp => { - include_glsl!("res/shader/transition/cross_warp.comp").as_slice() - } - TransitionType::CrossZoom => { - include_glsl!("res/shader/transition/cross_zoom.comp").as_slice() - } - TransitionType::Cube => { - include_glsl!("res/shader/transition/cube.comp").as_slice() - } - TransitionType::Directional => { - include_glsl!("res/shader/transition/directional.comp").as_slice() - } - TransitionType::DirectionalEasing => { - include_glsl!("res/shader/transition/directional_easing.comp") - .as_slice() - } - TransitionType::DirectionalWarp => { - include_glsl!("res/shader/transition/directional_warp.comp").as_slice() - } - TransitionType::DirectionalWipe => { - include_glsl!("res/shader/transition/directional_wipe.comp").as_slice() - } - TransitionType::Displacement => { - include_glsl!("res/shader/transition/displacement.comp").as_slice() - } - TransitionType::DoomScreen => { - include_glsl!("res/shader/transition/doom_screen.comp").as_slice() - } - TransitionType::Doorway => { - include_glsl!("res/shader/transition/doorway.comp").as_slice() - } - TransitionType::Dreamy => { - include_glsl!("res/shader/transition/dreamy.comp").as_slice() - } - TransitionType::DreamyZoom => { - include_glsl!("res/shader/transition/dreamy_zoom.comp").as_slice() - } - TransitionType::FadeColor => { - include_glsl!("res/shader/transition/fade_color.comp").as_slice() - } - TransitionType::Fade => { - include_glsl!("res/shader/transition/fade.comp").as_slice() - } - TransitionType::FadeGrayscale => { - include_glsl!("res/shader/transition/fade_grayscale.comp").as_slice() - } - TransitionType::FilmBurn => { - include_glsl!("res/shader/transition/film_burn.comp").as_slice() - } - TransitionType::Flyeye => { - include_glsl!("res/shader/transition/flyeye.comp").as_slice() - } - TransitionType::GlitchDisplace => { - include_glsl!("res/shader/transition/glitch_displace.comp").as_slice() - } - TransitionType::GlitchMemories => { - include_glsl!("res/shader/transition/glitch_memories.comp").as_slice() - } - TransitionType::GridFlip => { - include_glsl!("res/shader/transition/grid_flip.comp").as_slice() - } - TransitionType::Heart => { - include_glsl!("res/shader/transition/heart.comp").as_slice() - } - TransitionType::Hexagonalize => { - include_glsl!("res/shader/transition/hexagonalize.comp").as_slice() - } - TransitionType::InvertedPageCurl => { - include_glsl!("res/shader/transition/inverted_page_curl.comp") - .as_slice() - } - TransitionType::Kaleidoscope => { - include_glsl!("res/shader/transition/kaleidoscope.comp").as_slice() - } - TransitionType::LeftRight => { - include_glsl!("res/shader/transition/left_right.comp").as_slice() - } - TransitionType::LinearBlur => { - include_glsl!("res/shader/transition/linear_blur.comp").as_slice() - } - TransitionType::Luma => { - include_glsl!("res/shader/transition/luma.comp").as_slice() - } - TransitionType::LuminanceMelt => { - include_glsl!("res/shader/transition/luminance_melt.comp").as_slice() - } - TransitionType::Morph => { - include_glsl!("res/shader/transition/morph.comp").as_slice() - } - TransitionType::Mosaic => { - include_glsl!("res/shader/transition/mosaic.comp").as_slice() - } - TransitionType::Multiply => { - include_glsl!("res/shader/transition/multiply.comp").as_slice() - } - TransitionType::Overexposure => { - include_glsl!("res/shader/transition/overexposure.comp").as_slice() - } - TransitionType::Perlin => { - include_glsl!("res/shader/transition/perlin.comp").as_slice() - } - TransitionType::Pinwheel => { - include_glsl!("res/shader/transition/pinwheel.comp").as_slice() - } - TransitionType::Pixelize => { - include_glsl!("res/shader/transition/pixelize.comp").as_slice() - } - TransitionType::PolarFunction => { - include_glsl!("res/shader/transition/polar_function.comp").as_slice() - } - TransitionType::PolkaDotsCurtain => { - include_glsl!("res/shader/transition/polka_dots_curtain.comp") - .as_slice() - } - TransitionType::PowerKaleido => { - include_glsl!("res/shader/transition/power_kaleido.comp").as_slice() - } - TransitionType::Radial => { - include_glsl!("res/shader/transition/radial.comp").as_slice() - } - TransitionType::RandomNoisex => { - include_glsl!("res/shader/transition/random_noisex.comp").as_slice() - } - TransitionType::RandomSquares => { - include_glsl!("res/shader/transition/random_squares.comp").as_slice() - } - TransitionType::Ripple => { - include_glsl!("res/shader/transition/ripple.comp").as_slice() - } - TransitionType::Rotate => { - include_glsl!("res/shader/transition/rotate.comp").as_slice() - } - TransitionType::RotateScale => { - include_glsl!("res/shader/transition/rotate_scale.comp").as_slice() - } - TransitionType::ScaleIn => { - include_glsl!("res/shader/transition/scale_in.comp").as_slice() - } - TransitionType::SimpleZoom => { - include_glsl!("res/shader/transition/simple_zoom.comp").as_slice() - } - TransitionType::SquaresWire => { - include_glsl!("res/shader/transition/squares_wire.comp").as_slice() - } - TransitionType::Squeeze => { - include_glsl!("res/shader/transition/squeeze.comp").as_slice() - } - TransitionType::StereoViewer => { - include_glsl!("res/shader/transition/stereo_viewer.comp").as_slice() - } - TransitionType::Swap => { - include_glsl!("res/shader/transition/swap.comp").as_slice() - } - TransitionType::Swirl => { - include_glsl!("res/shader/transition/swirl.comp").as_slice() - } - TransitionType::TangentMotionBlur => { - include_glsl!("res/shader/transition/tangent_motion_blur.comp") - .as_slice() - } - TransitionType::TopBottom => { - include_glsl!("res/shader/transition/top_bottom.comp").as_slice() - } - TransitionType::TvStatic => { - include_glsl!("res/shader/transition/tv_static.comp").as_slice() - } - TransitionType::UndulatingBurnOut => { - include_glsl!("res/shader/transition/undulating_burn_out.comp") - .as_slice() - } - TransitionType::WaterDrop => { - include_glsl!("res/shader/transition/water_drop.comp").as_slice() - } - TransitionType::Wind => { - include_glsl!("res/shader/transition/wind.comp").as_slice() - } - TransitionType::WindowBlinds => { - include_glsl!("res/shader/transition/window_blinds.comp").as_slice() - } - TransitionType::WindowSlice => { - include_glsl!("res/shader/transition/window_slice.comp").as_slice() - } - TransitionType::WipeDown => { - include_glsl!("res/shader/transition/wipe_down.comp").as_slice() - } - TransitionType::WipeLeft => { - include_glsl!("res/shader/transition/wipe_left.comp").as_slice() - } - TransitionType::WipeRight => { - include_glsl!("res/shader/transition/wipe_right.comp").as_slice() - } - TransitionType::WipeUp => { - include_glsl!("res/shader/transition/wipe_up.comp").as_slice() - } - TransitionType::ZoomInCircles => { - include_glsl!("res/shader/transition/zoom_in_circles.comp").as_slice() - } - TransitionType::ZoomLeftWipe => { - include_glsl!("res/shader/transition/zoom_left_wipe.comp").as_slice() - } - TransitionType::ZoomRightWipe => { - include_glsl!("res/shader/transition/zoom_right_wipe.comp").as_slice() - } - }), - ) - .unwrap(), + ComputePipeline::create( + &self.device, + ComputePipelineInfo::default(), + Shader::new_compute(match transition_ty { + TransitionType::Angular => { + include_glsl!("res/shader/transition/angular.comp").as_slice() + } + TransitionType::Bounce => { + include_glsl!("res/shader/transition/bounce.comp").as_slice() + } + TransitionType::BowTieHorizontal => { + include_glsl!("res/shader/transition/bow_tie_horizontal.comp").as_slice() + } + TransitionType::BowTieVertical => { + include_glsl!("res/shader/transition/bow_tie_vertical.comp").as_slice() + } + TransitionType::BowTieWithParameter => { + include_glsl!("res/shader/transition/bow_tie_with_parameter.comp",) + .as_slice() + } + TransitionType::Burn => { + include_glsl!("res/shader/transition/burn.comp").as_slice() + } + TransitionType::ButterflyWaveScrawler => { + include_glsl!("res/shader/transition/butterfly_wave_scrawler.comp",) + .as_slice() + } + TransitionType::CannabisLeaf => { + include_glsl!("res/shader/transition/cannabis_leaf.comp").as_slice() + } + TransitionType::Circle => { + include_glsl!("res/shader/transition/circle.comp").as_slice() + } + TransitionType::CircleCrop => { + include_glsl!("res/shader/transition/circle_crop.comp").as_slice() + } + TransitionType::CircleOpen => { + include_glsl!("res/shader/transition/circle_open.comp").as_slice() + } + TransitionType::ColorDistance => { + include_glsl!("res/shader/transition/color_distance.comp").as_slice() + } + TransitionType::ColorPhase => { + include_glsl!("res/shader/transition/color_phase.comp").as_slice() + } + TransitionType::CoordFromIn => { + include_glsl!("res/shader/transition/coord_from_in.comp").as_slice() + } + TransitionType::CrazyParametricFun => { + include_glsl!("res/shader/transition/crazy_parametric_fun.comp").as_slice() + } + TransitionType::Crosshatch => { + include_glsl!("res/shader/transition/crosshatch.comp").as_slice() + } + TransitionType::CrossWarp => { + include_glsl!("res/shader/transition/cross_warp.comp").as_slice() + } + TransitionType::CrossZoom => { + include_glsl!("res/shader/transition/cross_zoom.comp").as_slice() + } + TransitionType::Cube => { + include_glsl!("res/shader/transition/cube.comp").as_slice() + } + TransitionType::Directional => { + include_glsl!("res/shader/transition/directional.comp").as_slice() + } + TransitionType::DirectionalEasing => { + include_glsl!("res/shader/transition/directional_easing.comp").as_slice() + } + TransitionType::DirectionalWarp => { + include_glsl!("res/shader/transition/directional_warp.comp").as_slice() + } + TransitionType::DirectionalWipe => { + include_glsl!("res/shader/transition/directional_wipe.comp").as_slice() + } + TransitionType::Displacement => { + include_glsl!("res/shader/transition/displacement.comp").as_slice() + } + TransitionType::DoomScreen => { + include_glsl!("res/shader/transition/doom_screen.comp").as_slice() + } + TransitionType::Doorway => { + include_glsl!("res/shader/transition/doorway.comp").as_slice() + } + TransitionType::Dreamy => { + include_glsl!("res/shader/transition/dreamy.comp").as_slice() + } + TransitionType::DreamyZoom => { + include_glsl!("res/shader/transition/dreamy_zoom.comp").as_slice() + } + TransitionType::FadeColor => { + include_glsl!("res/shader/transition/fade_color.comp").as_slice() + } + TransitionType::Fade => { + include_glsl!("res/shader/transition/fade.comp").as_slice() + } + TransitionType::FadeGrayscale => { + include_glsl!("res/shader/transition/fade_grayscale.comp").as_slice() + } + TransitionType::FilmBurn => { + include_glsl!("res/shader/transition/film_burn.comp").as_slice() + } + TransitionType::Flyeye => { + include_glsl!("res/shader/transition/flyeye.comp").as_slice() + } + TransitionType::GlitchDisplace => { + include_glsl!("res/shader/transition/glitch_displace.comp").as_slice() + } + TransitionType::GlitchMemories => { + include_glsl!("res/shader/transition/glitch_memories.comp").as_slice() + } + TransitionType::GridFlip => { + include_glsl!("res/shader/transition/grid_flip.comp").as_slice() + } + TransitionType::Heart => { + include_glsl!("res/shader/transition/heart.comp").as_slice() + } + TransitionType::Hexagonalize => { + include_glsl!("res/shader/transition/hexagonalize.comp").as_slice() + } + TransitionType::InvertedPageCurl => { + include_glsl!("res/shader/transition/inverted_page_curl.comp").as_slice() + } + TransitionType::Kaleidoscope => { + include_glsl!("res/shader/transition/kaleidoscope.comp").as_slice() + } + TransitionType::LeftRight => { + include_glsl!("res/shader/transition/left_right.comp").as_slice() + } + TransitionType::LinearBlur => { + include_glsl!("res/shader/transition/linear_blur.comp").as_slice() + } + TransitionType::Luma => { + include_glsl!("res/shader/transition/luma.comp").as_slice() + } + TransitionType::LuminanceMelt => { + include_glsl!("res/shader/transition/luminance_melt.comp").as_slice() + } + TransitionType::Morph => { + include_glsl!("res/shader/transition/morph.comp").as_slice() + } + TransitionType::Mosaic => { + include_glsl!("res/shader/transition/mosaic.comp").as_slice() + } + TransitionType::Multiply => { + include_glsl!("res/shader/transition/multiply.comp").as_slice() + } + TransitionType::Overexposure => { + include_glsl!("res/shader/transition/overexposure.comp").as_slice() + } + TransitionType::Perlin => { + include_glsl!("res/shader/transition/perlin.comp").as_slice() + } + TransitionType::Pinwheel => { + include_glsl!("res/shader/transition/pinwheel.comp").as_slice() + } + TransitionType::Pixelize => { + include_glsl!("res/shader/transition/pixelize.comp").as_slice() + } + TransitionType::PolarFunction => { + include_glsl!("res/shader/transition/polar_function.comp").as_slice() + } + TransitionType::PolkaDotsCurtain => { + include_glsl!("res/shader/transition/polka_dots_curtain.comp").as_slice() + } + TransitionType::PowerKaleido => { + include_glsl!("res/shader/transition/power_kaleido.comp").as_slice() + } + TransitionType::Radial => { + include_glsl!("res/shader/transition/radial.comp").as_slice() + } + TransitionType::RandomNoisex => { + include_glsl!("res/shader/transition/random_noisex.comp").as_slice() + } + TransitionType::RandomSquares => { + include_glsl!("res/shader/transition/random_squares.comp").as_slice() + } + TransitionType::Ripple => { + include_glsl!("res/shader/transition/ripple.comp").as_slice() + } + TransitionType::Rotate => { + include_glsl!("res/shader/transition/rotate.comp").as_slice() + } + TransitionType::RotateScale => { + include_glsl!("res/shader/transition/rotate_scale.comp").as_slice() + } + TransitionType::ScaleIn => { + include_glsl!("res/shader/transition/scale_in.comp").as_slice() + } + TransitionType::SimpleZoom => { + include_glsl!("res/shader/transition/simple_zoom.comp").as_slice() + } + TransitionType::SquaresWire => { + include_glsl!("res/shader/transition/squares_wire.comp").as_slice() + } + TransitionType::Squeeze => { + include_glsl!("res/shader/transition/squeeze.comp").as_slice() + } + TransitionType::StereoViewer => { + include_glsl!("res/shader/transition/stereo_viewer.comp").as_slice() + } + TransitionType::Swap => { + include_glsl!("res/shader/transition/swap.comp").as_slice() + } + TransitionType::Swirl => { + include_glsl!("res/shader/transition/swirl.comp").as_slice() + } + TransitionType::TangentMotionBlur => { + include_glsl!("res/shader/transition/tangent_motion_blur.comp").as_slice() + } + TransitionType::TopBottom => { + include_glsl!("res/shader/transition/top_bottom.comp").as_slice() + } + TransitionType::TvStatic => { + include_glsl!("res/shader/transition/tv_static.comp").as_slice() + } + TransitionType::UndulatingBurnOut => { + include_glsl!("res/shader/transition/undulating_burn_out.comp").as_slice() + } + TransitionType::WaterDrop => { + include_glsl!("res/shader/transition/water_drop.comp").as_slice() + } + TransitionType::Wind => { + include_glsl!("res/shader/transition/wind.comp").as_slice() + } + TransitionType::WindowBlinds => { + include_glsl!("res/shader/transition/window_blinds.comp").as_slice() + } + TransitionType::WindowSlice => { + include_glsl!("res/shader/transition/window_slice.comp").as_slice() + } + TransitionType::WipeDown => { + include_glsl!("res/shader/transition/wipe_down.comp").as_slice() + } + TransitionType::WipeLeft => { + include_glsl!("res/shader/transition/wipe_left.comp").as_slice() + } + TransitionType::WipeRight => { + include_glsl!("res/shader/transition/wipe_right.comp").as_slice() + } + TransitionType::WipeUp => { + include_glsl!("res/shader/transition/wipe_up.comp").as_slice() + } + TransitionType::ZoomInCircles => { + include_glsl!("res/shader/transition/zoom_in_circles.comp").as_slice() + } + TransitionType::ZoomLeftWipe => { + include_glsl!("res/shader/transition/zoom_left_wipe.comp").as_slice() + } + TransitionType::ZoomRightWipe => { + include_glsl!("res/shader/transition/zoom_right_wipe.comp").as_slice() + } + }), ) - }); - - Arc::clone(pipeline) + .unwrap() + }) } } diff --git a/contrib/vk-graph-hot/src/compute.rs b/contrib/vk-graph-hot/src/compute.rs index d2a46fdf..1f040909 100644 --- a/contrib/vk-graph-hot/src/compute.rs +++ b/contrib/vk-graph-hot/src/compute.rs @@ -18,9 +18,8 @@ use { /// TODO #[derive(Debug)] pub struct HotComputePipeline { - device: Device, has_changes: Arc, - instance: Arc, + pipeline: ComputePipeline, shader: HotShader, watcher: RecommendedWatcher, } @@ -37,14 +36,11 @@ impl HotComputePipeline { let (mut watcher, has_changes) = create_watcher(); let compiled_shader = compile_shader_and_watch(&shader, &mut watcher)?; - let instance = Arc::new(ComputePipeline::create(device, info, compiled_shader)?); - - let device = device.clone(); + let pipeline = ComputePipeline::create(device, info, compiled_shader)?; Ok(Self { - device, has_changes, - instance, + pipeline, shader, watcher, }) @@ -52,13 +48,13 @@ impl HotComputePipeline { /// Returns the most recent compilation without checking for changes or re-compiling the shader /// source code. - pub fn cold(&self) -> &Arc { - &self.instance + pub fn cold(&self) -> &ComputePipeline { + &self.pipeline } /// Returns the most recent compilation after checking for changes, and if needed re-compiling /// the shader source code. - pub fn hot(&mut self) -> &Arc { + pub fn hot(&mut self) -> &ComputePipeline { let has_changes = self.has_changes.swap(false, Ordering::Relaxed); if has_changes { @@ -66,12 +62,14 @@ impl HotComputePipeline { let (mut watcher, has_changes) = create_watcher(); if let Ok(compiled_shader) = compile_shader_and_watch(&self.shader, &mut watcher) { - if let Ok(instance) = - ComputePipeline::create(&self.device, self.instance.info, compiled_shader) - { + if let Ok(pipeline) = ComputePipeline::create( + self.pipeline.device(), + self.pipeline.info(), + compiled_shader, + ) { + self.pipeline = pipeline; self.has_changes = has_changes; self.watcher = watcher; - self.instance = Arc::new(instance); } } } @@ -82,6 +80,6 @@ impl HotComputePipeline { impl AsRef for HotComputePipeline { fn as_ref(&self) -> &ComputePipeline { - self.instance.as_ref() + &self.pipeline } } diff --git a/contrib/vk-graph-hot/src/graphic.rs b/contrib/vk-graph-hot/src/graphic.rs index 44d7ba63..3b964888 100644 --- a/contrib/vk-graph-hot/src/graphic.rs +++ b/contrib/vk-graph-hot/src/graphic.rs @@ -18,9 +18,8 @@ use { /// TODO #[derive(Debug)] pub struct HotGraphicPipeline { - device: Device, has_changes: Arc, - instance: Arc, + pipeline: GraphicPipeline, shaders: Box<[HotShader]>, watcher: RecommendedWatcher, } @@ -46,14 +45,11 @@ impl HotGraphicPipeline { .map(|shader| compile_shader_and_watch(shader, &mut watcher)) .collect::, _>>()?; - let instance = Arc::new(GraphicPipeline::create(device, info, compiled_shaders)?); - - let device = device.clone(); + let pipeline = GraphicPipeline::create(device, info, compiled_shaders)?; Ok(Self { - device, has_changes, - instance, + pipeline, shaders, watcher, }) @@ -61,13 +57,13 @@ impl HotGraphicPipeline { /// Returns the most recent compilation without checking for changes or re-compiling the shader /// source code. - pub fn cold(&self) -> &Arc { - &self.instance + pub fn cold(&self) -> &GraphicPipeline { + &self.pipeline } /// Returns the most recent compilation after checking for changes, and if needed re-compiling /// the shader source code. - pub fn hot(&mut self) -> &Arc { + pub fn hot(&mut self) -> &GraphicPipeline { let has_changes = self.has_changes.swap(false, Ordering::Relaxed); if has_changes { @@ -80,12 +76,14 @@ impl HotGraphicPipeline { .map(|shader| compile_shader_and_watch(shader, &mut watcher)) .collect::, DriverError>>() { - if let Ok(instance) = - GraphicPipeline::create(&self.device, self.instance.info, compiled_shaders) - { + if let Ok(pipeline) = GraphicPipeline::create( + self.pipeline.device(), + self.pipeline.info(), + compiled_shaders, + ) { + self.pipeline = pipeline; self.has_changes = has_changes; self.watcher = watcher; - self.instance = Arc::new(instance); } } } @@ -96,6 +94,6 @@ impl HotGraphicPipeline { impl AsRef for HotGraphicPipeline { fn as_ref(&self) -> &GraphicPipeline { - self.instance.as_ref() + &self.pipeline } } diff --git a/contrib/vk-graph-hot/src/ray_trace.rs b/contrib/vk-graph-hot/src/ray_trace.rs index df149414..0ba29d24 100644 --- a/contrib/vk-graph-hot/src/ray_trace.rs +++ b/contrib/vk-graph-hot/src/ray_trace.rs @@ -18,9 +18,8 @@ use { /// TODO #[derive(Debug)] pub struct HotRayTracePipeline { - device: Device, has_changes: Arc, - instance: Arc, + pipeline: RayTracePipeline, shader_groups: Box<[RayTraceShaderGroup]>, shaders: Box<[HotShader]>, watcher: RecommendedWatcher, @@ -49,19 +48,16 @@ impl HotRayTracePipeline { .map(|shader| compile_shader_and_watch(shader, &mut watcher)) .collect::, _>>()?; - let instance = Arc::new(RayTracePipeline::create( + let pipeline = RayTracePipeline::create( device, info, compiled_shaders, shader_groups.iter().copied(), - )?); - - let device = device.clone(); + )?; Ok(Self { - device, has_changes, - instance, + pipeline, shader_groups, shaders, watcher, @@ -70,13 +66,13 @@ impl HotRayTracePipeline { /// Returns the most recent compilation without checking for changes or re-compiling the shader /// source code. - pub fn cold(&self) -> &Arc { - &self.instance + pub fn cold(&self) -> &RayTracePipeline { + &self.pipeline } /// Returns the most recent compilation after checking for changes, and if needed re-compiling /// the shader source code. - pub fn hot(&mut self) -> &Arc { + pub fn hot(&mut self) -> &RayTracePipeline { let has_changes = self.has_changes.swap(false, Ordering::Relaxed); if has_changes { @@ -89,15 +85,15 @@ impl HotRayTracePipeline { .map(|shader| compile_shader_and_watch(shader, &mut watcher)) .collect::, DriverError>>() { - if let Ok(instance) = RayTracePipeline::create( - &self.device, - self.instance.info, + if let Ok(pipeline) = RayTracePipeline::create( + self.pipeline.device(), + self.pipeline.info(), compiled_shaders, self.shader_groups.iter().copied(), ) { + self.pipeline = pipeline; self.has_changes = has_changes; self.watcher = watcher; - self.instance = Arc::new(instance); } } } @@ -108,6 +104,6 @@ impl HotRayTracePipeline { impl AsRef for HotRayTracePipeline { fn as_ref(&self) -> &RayTracePipeline { - self.instance.as_ref() + &self.pipeline } } diff --git a/contrib/vk-graph-imgui/src/lib.rs b/contrib/vk-graph-imgui/src/lib.rs index 28ba4710..89ba644b 100644 --- a/contrib/vk-graph-imgui/src/lib.rs +++ b/contrib/vk-graph-imgui/src/lib.rs @@ -26,7 +26,7 @@ use { pub struct ImGui { context: Context, font_atlas_image: Option>>, - pipeline: Arc, + pipeline: GraphicPipeline, platform: WinitPlatform, } @@ -35,19 +35,17 @@ impl ImGui { pub fn new(device: &Device) -> Self { let mut context = Context::create(); let platform = WinitPlatform::new(&mut context); - let pipeline = Arc::new( - GraphicPipeline::create( - device, - GraphicPipelineInfoBuilder::default() - .blend(BlendMode::PRE_MULTIPLIED_ALPHA) - .cull_mode(vk::CullModeFlags::NONE), - [ - Shader::new_vertex(include_glsl!("res/shader/imgui.vert").as_slice()), - Shader::new_fragment(include_glsl!("res/shader/imgui.frag").as_slice()), - ], - ) - .unwrap(), - ); + let pipeline = GraphicPipeline::create( + device, + GraphicPipelineInfoBuilder::default() + .blend(BlendMode::PRE_MULTIPLIED_ALPHA) + .cull_mode(vk::CullModeFlags::NONE), + [ + Shader::new_vertex(include_glsl!("res/shader/imgui.vert").as_slice()), + Shader::new_fragment(include_glsl!("res/shader/imgui.frag").as_slice()), + ], + ) + .unwrap(); Self { context, diff --git a/examples/bindless.rs b/examples/bindless.rs index 838191ef..b5ae61ef 100644 --- a/examples/bindless.rs +++ b/examples/bindless.rs @@ -95,8 +95,8 @@ fn create_indirect_buffer(device: &Device) -> Result, DriverError> { Ok(draw_buf) } -fn create_graphic_pipeline(device: &Device) -> Result, DriverError> { - Ok(Arc::new(GraphicPipeline::create( +fn create_graphic_pipeline(device: &Device) -> Result { + GraphicPipeline::create( device, GraphicPipelineInfo::default(), [ @@ -152,7 +152,7 @@ fn create_graphic_pipeline(device: &Device) -> Result, Driv .as_slice(), ), ], - )?)) + ) } #[derive(Parser)] diff --git a/examples/debugger.rs b/examples/debugger.rs index 6c73ed68..b3119391 100644 --- a/examples/debugger.rs +++ b/examples/debugger.rs @@ -24,7 +24,7 @@ To continue, uncomment line 30. */ fn main() -> Result<(), vk_graph_window::WindowError> { - use {log::debug, std::sync::Arc, vk_graph_prelude::*, vk_graph_window::Window}; + use {log::debug, vk_graph_prelude::*, vk_graph_window::Window}; // 👋, 🌎! //pretty_env_logger::init(); @@ -126,8 +126,7 @@ fn main() -> Result<(), vk_graph_window::WindowError> { ); // Note: This is just for example - let compute_pipeline = Arc::new( - ComputePipeline::create( + let compute_pipeline = ComputePipeline::create( frame.device, ComputePipelineInfo::default(), Shader::new_compute( @@ -146,8 +145,7 @@ fn main() -> Result<(), vk_graph_window::WindowError> { .as_slice(), ), ) - .unwrap(), - ); + .unwrap(); /* Case #2: diff --git a/examples/font_bmp.rs b/examples/font_bmp.rs index 7a92268d..b15d7c42 100644 --- a/examples/font_bmp.rs +++ b/examples/font_bmp.rs @@ -4,7 +4,7 @@ use { bmfont::{BMFont, OrdinateOrientation}, clap::Parser, image::ImageReader, - std::{io::Cursor, sync::Arc, time::Instant}, + std::{io::Cursor, time::Instant}, vk_graph_fx::*, vk_graph_prelude::*, vk_graph_window::WindowBuilder, @@ -48,7 +48,7 @@ fn main() -> anyhow::Result<()> { // A neato smoke effect just for fun let Vulkan11Properties { subgroup_size, .. } = window.device.physical_device.properties_v1_1; let start_time = Instant::now(); - let smoke_pipeline = Arc::new(ComputePipeline::create(&window.device, + let smoke_pipeline = ComputePipeline::create(&window.device, ComputePipelineInfo::default(), Shader::new_compute( glsl!( @@ -117,7 +117,7 @@ fn main() -> anyhow::Result<()> { size: 4, }], }), - )?); + )?; window.run(|frame| { let image_node = frame.render_graph.bind_node( diff --git a/examples/fuzzer.rs b/examples/fuzzer.rs index fdfdaca3..d8b0a5a3 100644 --- a/examples/fuzzer.rs +++ b/examples/fuzzer.rs @@ -25,7 +25,7 @@ use { clap::Parser, log::debug, rand::{Rng, rng, seq::IndexedRandom}, - std::{mem::size_of, sync::Arc}, + std::mem::size_of, vk_graph_prelude::*, vk_graph_window::{FrameContext, WindowBuilder, WindowError}, vk_shader_macros::glsl, @@ -1519,19 +1519,18 @@ fn compute_pipeline( device: &Device, info: impl Into, shader: impl Into, -) -> Arc { +) -> ComputePipeline { use std::{cell::RefCell, collections::HashMap}; thread_local! { - static TLS: RefCell>> = Default::default(); + static TLS: RefCell> = Default::default(); } TLS.with(|tls| { - Arc::clone( - tls.borrow_mut().entry(key).or_insert_with(|| { - Arc::new(ComputePipeline::create(device, info, shader).unwrap()) - }), - ) + tls.borrow_mut() + .entry(key) + .or_insert_with(|| ComputePipeline::create(device, info, shader).unwrap()) + .clone() }) } @@ -1540,7 +1539,7 @@ fn graphic_vert_frag_pipeline( info: impl Into, vert_source: &'static [u32], frag_source: &'static [u32], -) -> Arc { +) -> GraphicPipeline { use std::{cell::RefCell, collections::HashMap}; #[derive(Eq, Hash, PartialEq)] @@ -1551,33 +1550,30 @@ fn graphic_vert_frag_pipeline( } thread_local! { - static TLS: RefCell>> = Default::default(); + static TLS: RefCell> = Default::default(); } let info = info.into(); TLS.with(|tls| { - Arc::clone( - tls.borrow_mut() - .entry(Key { + tls.borrow_mut() + .entry(Key { + info, + vert_source, + frag_source, + }) + .or_insert_with(move || { + GraphicPipeline::create( + device, info, - vert_source, - frag_source, - }) - .or_insert_with(move || { - Arc::new( - GraphicPipeline::create( - device, - info, - [ - Shader::new_vertex(vert_source), - Shader::new_fragment(frag_source), - ], - ) - .unwrap(), - ) - }), - ) + [ + Shader::new_vertex(vert_source), + Shader::new_fragment(frag_source), + ], + ) + .unwrap() + }) + .clone() }) } diff --git a/examples/image_sampler.rs b/examples/image_sampler.rs index 908577aa..6adf982f 100644 --- a/examples/image_sampler.rs +++ b/examples/image_sampler.rs @@ -84,7 +84,7 @@ fn main() -> anyhow::Result<()> { fn create_pipeline( device: &Device, sampler_info: impl Into, -) -> anyhow::Result> { +) -> anyhow::Result { let args = Args::parse(); let mut frag_shader = match (args.hlsl, args.separate) { @@ -197,7 +197,7 @@ fn create_pipeline( let sampler_binding = args.separate as u32; frag_shader = frag_shader.image_sampler(sampler_binding, sampler_info); - Ok(Arc::new(GraphicPipeline::create( + Ok(GraphicPipeline::create( device, GraphicPipelineInfo::default(), [ @@ -225,7 +225,7 @@ fn create_pipeline( ), frag_shader, ], - )?)) + )?) } fn read_image(device: &Device, path: impl AsRef) -> anyhow::Result> { diff --git a/examples/min_max.rs b/examples/min_max.rs index 14af8f06..5c3a5940 100644 --- a/examples/min_max.rs +++ b/examples/min_max.rs @@ -178,7 +178,7 @@ fn reduce_depth_image( render_graph .begin_cmd() .with_name("Reduce depth image") - .bind_pipeline(&Arc::new(ComputePipeline::create( + .bind_pipeline(ComputePipeline::create( device, ComputePipelineInfo::default(), Shader::new_compute( @@ -203,7 +203,7 @@ fn reduce_depth_image( .as_slice(), ) .image_sampler(0, SamplerInfo::LINEAR.reduction_mode(reduction_mode)), - )?)) + )?) .read_descriptor(0, depth_image) .write_descriptor(1, reduced_image) .record_pipeline(move |compute, _| { diff --git a/examples/mip_graphic.rs b/examples/mip_graphic.rs index 30c7de05..3f581ae4 100644 --- a/examples/mip_graphic.rs +++ b/examples/mip_graphic.rs @@ -92,7 +92,7 @@ fn fill_mip_levels(device: &Device, image: &Arc) -> Result<(), DriverErro b: Vec4, } - let vertical_gradient = Arc::new(GraphicPipeline::create( + let vertical_gradient = GraphicPipeline::create( device, GraphicPipelineInfo::default(), [ @@ -144,7 +144,7 @@ fn fill_mip_levels(device: &Device, image: &Arc) -> Result<(), DriverErro .as_slice(), ), ], - )?); + )?; let mut render_graph = Graph::default(); let image_info = image.info; @@ -190,7 +190,7 @@ fn fill_mip_levels(device: &Device, image: &Arc) -> Result<(), DriverErro family .queue_flags .contains(vk::QueueFlags::GRAPHICS) - .then_some(idx) + .then_some(idx as u32) }) .ok_or(DriverError::Unsupported)?; @@ -201,8 +201,8 @@ fn fill_mip_levels(device: &Device, image: &Arc) -> Result<(), DriverErro .map(|_| ()) } -fn splat(device: &Device) -> Result, DriverError> { - Ok(Arc::new(GraphicPipeline::create( +fn splat(device: &Device) -> Result { + GraphicPipeline::create( device, GraphicPipelineInfo::default(), [ @@ -262,7 +262,7 @@ fn splat(device: &Device) -> Result, DriverError> { SamplerInfoBuilder::default().mipmap_mode(vk::SamplerMipmapMode::LINEAR), ), ], - )?)) + ) } #[derive(Parser)] diff --git a/examples/msaa.rs b/examples/msaa.rs index c06a4612..eaaaefd8 100644 --- a/examples/msaa.rs +++ b/examples/msaa.rs @@ -316,7 +316,7 @@ fn load_cube_mesh(device: &Device) -> Result { fn create_mesh_pipeline( device: &Device, sample_count: SampleCount, -) -> Result, DriverError> { +) -> Result { let vert = glsl!( r#" #version 460 core @@ -374,14 +374,14 @@ fn create_mesh_pipeline( let info = GraphicPipelineInfoBuilder::default().samples(sample_count); - Ok(Arc::new(GraphicPipeline::create( + GraphicPipeline::create( device, info, [ Shader::new_vertex(vert.as_slice()), Shader::new_fragment(frag.as_slice()), ], - )?)) + ) } #[derive(Parser)] diff --git a/examples/multipass.rs b/examples/multipass.rs index 315bb64b..899bdd31 100644 --- a/examples/multipass.rs +++ b/examples/multipass.rs @@ -304,7 +304,7 @@ fn create_funky_shape(device: &Device, pool: &mut LazyPool) -> Result Arc { +fn create_fill_background_pipeline(device: &Device) -> GraphicPipeline { let vertex_shader = Shader::new_vertex( glsl!( r#" @@ -345,17 +345,15 @@ fn create_fill_background_pipeline(device: &Device) -> Arc { .as_slice(), ); - Arc::new( - GraphicPipeline::create( - device, - GraphicPipelineInfo::default(), - [vertex_shader, fragment_shader], - ) - .unwrap(), + GraphicPipeline::create( + device, + GraphicPipelineInfo::default(), + [vertex_shader, fragment_shader], ) + .unwrap() } -fn create_prepass_pipeline(device: &Device) -> Arc { +fn create_prepass_pipeline(device: &Device) -> GraphicPipeline { let vertex_shader = Shader::new_vertex( glsl!( r#" @@ -418,17 +416,15 @@ fn create_prepass_pipeline(device: &Device) -> Arc { .as_slice(), ); - Arc::new( - GraphicPipeline::create( - device, - GraphicPipelineInfo::default(), - [vertex_shader, fragment_shader], - ) - .unwrap(), + GraphicPipeline::create( + device, + GraphicPipelineInfo::default(), + [vertex_shader, fragment_shader], ) + .unwrap() } -fn create_pbr_pipeline(device: &Device) -> Arc { +fn create_pbr_pipeline(device: &Device) -> GraphicPipeline { // See: https://github.com/SaschaWillems/Vulkan/blob/master/data/shaders/glsl/pbrbasic/pbr.vert let vertex_shader = Shader::new_vertex( glsl!( @@ -605,14 +601,12 @@ fn create_pbr_pipeline(device: &Device) -> Arc { .as_slice(), ); - Arc::new( - GraphicPipeline::create( - device, - GraphicPipelineInfo::default(), - [vertex_shader, fragment_shader], - ) - .unwrap(), + GraphicPipeline::create( + device, + GraphicPipelineInfo::default(), + [vertex_shader, fragment_shader], ) + .unwrap() } /// Returns index and position/normal data (polyhedron_ops you are 🥇🏆🥂💯) diff --git a/examples/multithread.rs b/examples/multithread.rs index e837c26f..132a54fb 100644 --- a/examples/multithread.rs +++ b/examples/multithread.rs @@ -63,7 +63,8 @@ fn main() -> anyhow::Result<()> { || queue_family_properties .queue_flags .contains(vk::QueueFlags::GRAPHICS) - }); + }) + .map(|(idx, queue_family_properties)| (idx as u32, queue_family_properties)); assert!( secondary_queue_family.is_some(), @@ -72,8 +73,7 @@ fn main() -> anyhow::Result<()> { let (secondary_queue_family_index, secondary_queue_family_properties) = secondary_queue_family.unwrap(); - let queue_count = - desired_queue_count.min(secondary_queue_family_properties.queue_count) as usize; + let queue_count = desired_queue_count.min(secondary_queue_family_properties.queue_count); assert!(queue_count > 0, "GPU does not support secondary queues"); @@ -81,7 +81,7 @@ fn main() -> anyhow::Result<()> { let running = Arc::new(AtomicBool::new(true)); let thread_count = queue_count; - let mut threads = Vec::with_capacity(thread_count); + let mut threads = Vec::with_capacity(thread_count as _); let (tx, rx) = channel(); info!("Launching {thread_count} threads"); diff --git a/examples/ray_omni.rs b/examples/ray_omni.rs index 86caddcf..1a053d09 100644 --- a/examples/ray_omni.rs +++ b/examples/ray_omni.rs @@ -226,7 +226,7 @@ fn create_blas( Ok(blas) } -fn create_pipeline(device: &Device) -> Result, DriverError> { +fn create_pipeline(device: &Device) -> Result { let vert = glsl!( r#" #version 460 core @@ -303,14 +303,14 @@ fn create_pipeline(device: &Device) -> Result, DriverError> "# ); - Ok(Arc::new(GraphicPipeline::create( + GraphicPipeline::create( device, GraphicPipelineInfo::default(), [ Shader::new_vertex(vert.as_slice()), Shader::new_fragment(frag.as_slice()), ], - )?)) + ) } fn create_tlas( diff --git a/examples/ray_trace.rs b/examples/ray_trace.rs index b37e5e71..f2570b5d 100644 --- a/examples/ray_trace.rs +++ b/examples/ray_trace.rs @@ -329,8 +329,8 @@ static SHADER_SHADOW_MISS: &[u32] = glsl!( ) .as_slice(); -fn create_ray_trace_pipeline(device: &Device) -> Result, DriverError> { - Ok(Arc::new(RayTracePipeline::create( +fn create_ray_trace_pipeline(device: &Device) -> Result { + RayTracePipeline::create( device, RayTracePipelineInfoBuilder::default().max_ray_recursion_depth(1), [ @@ -345,7 +345,7 @@ fn create_ray_trace_pipeline(device: &Device) -> Result, D RayTraceShaderGroup::new_general(2), RayTraceShaderGroup::new_general(3), ], - )?)) + ) } #[allow(clippy::type_complexity)] diff --git a/examples/rt_triangle.rs b/examples/rt_triangle.rs index e3c460db..92b0960b 100644 --- a/examples/rt_triangle.rs +++ b/examples/rt_triangle.rs @@ -76,8 +76,8 @@ static SHADER_MISS: &[u32] = glsl!( ) .as_slice(); -fn create_ray_trace_pipeline(device: &Device) -> Result, DriverError> { - Ok(Arc::new(RayTracePipeline::create( +fn create_ray_trace_pipeline(device: &Device) -> Result { + RayTracePipeline::create( device, RayTracePipelineInfoBuilder::default().max_ray_recursion_depth(1), [ @@ -90,7 +90,7 @@ fn create_ray_trace_pipeline(device: &Device) -> Result, D RayTraceShaderGroup::new_triangles(1, None), RayTraceShaderGroup::new_general(2), ], - )?)) + ) } /// Adapted from https://iorange.github.io/p01/HappyTriangle.html diff --git a/examples/shader-toy/src/main.rs b/examples/shader-toy/src/main.rs index 1eea8b27..f76e4607 100644 --- a/examples/shader-toy/src/main.rs +++ b/examples/shader-toy/src/main.rs @@ -37,7 +37,7 @@ use { bytemuck::{bytes_of, Pod, Zeroable}, clap::Parser, pak::{Pak, PakBuf}, - std::{sync::Arc, time::Instant}, + std::time::Instant, vk_graph::{ driver::{ ash::vk, @@ -103,28 +103,24 @@ fn main() -> anyhow::Result<()> { // no depth/stencil // 1x sample count // one-sided - let buffer_pipeline = Arc::new( - GraphicPipeline::create( - &window.device, - GraphicPipelineInfo::default(), - [ - Shader::new_vertex(res::shader::QUAD_VERT), - Shader::new_fragment(res::shader::FLOCKAROO_BUF_FRAG), - ], - ) - .context("FLOCKAROO_BUF_FRAG")?, - ); - let image_pipeline = Arc::new( - GraphicPipeline::create( - &window.device, - GraphicPipelineInfo::default(), - [ - Shader::new_vertex(res::shader::QUAD_VERT), - Shader::new_fragment(res::shader::FLOCKAROO_IMG_FRAG), - ], - ) - .context("FLOCKAROO_IMG_FRAG")?, - ); + let buffer_pipeline = GraphicPipeline::create( + &window.device, + GraphicPipelineInfo::default(), + [ + Shader::new_vertex(res::shader::QUAD_VERT), + Shader::new_fragment(res::shader::FLOCKAROO_BUF_FRAG), + ], + ) + .context("FLOCKAROO_BUF_FRAG")?; + let image_pipeline = GraphicPipeline::create( + &window.device, + GraphicPipelineInfo::default(), + [ + Shader::new_vertex(res::shader::QUAD_VERT), + Shader::new_fragment(res::shader::FLOCKAROO_IMG_FRAG), + ], + ) + .context("FLOCKAROO_IMG_FRAG")?; let mut render_graph = Graph::default(); let blank_image = render_graph.bind_node( diff --git a/examples/skeletal-anim/src/main.rs b/examples/skeletal-anim/src/main.rs index bb42039a..1d4e937c 100644 --- a/examples/skeletal-anim/src/main.rs +++ b/examples/skeletal-anim/src/main.rs @@ -147,18 +147,18 @@ fn main() -> Result<(), WindowError> { }) } -fn create_pipeline(device: &Device, pak: &mut PakBuf) -> Result, DriverError> { +fn create_pipeline(device: &Device, pak: &mut PakBuf) -> Result { let vert_spirv = pak.read_blob("shader/animated_mesh_vert.spirv").unwrap(); let frag_spirv = pak.read_blob("shader/mesh_frag.spirv").unwrap(); - Ok(Arc::new(GraphicPipeline::create( + GraphicPipeline::create( device, GraphicPipelineInfoBuilder::default().front_face(vk::FrontFace::CLOCKWISE), [ Shader::new_vertex(vert_spirv.as_slice()), Shader::new_fragment(frag_spirv.as_slice()), ], - )?)) + ) } fn load_texture(device: &Device, pak: &mut PakBuf, key: &str) -> Result, DriverError> { diff --git a/examples/subgroup_ops.rs b/examples/subgroup_ops.rs index eb519767..3157a988 100644 --- a/examples/subgroup_ops.rs +++ b/examples/subgroup_ops.rs @@ -62,8 +62,8 @@ fn main() -> Result<(), DriverError> { fn exclusive_sum( device: &Device, - reduce_pipeline: &Arc, - scan_pipeline: &Arc, + reduce_pipeline: &ComputePipeline, + scan_pipeline: &ComputePipeline, input_data: &[u32], ) -> Result, DriverError> { let mut render_graph = Graph::default(); @@ -152,8 +152,8 @@ fn assert_output_data(input_data: &[u32], output_data: &[u32]) { } } -fn create_reduce_pipeline(device: &Device) -> Result, DriverError> { - Ok(Arc::new(ComputePipeline::create( +fn create_reduce_pipeline(device: &Device) -> Result { + ComputePipeline::create( device, ComputePipelineInfo::default(), Shader::new_compute( @@ -199,12 +199,11 @@ fn create_reduce_pipeline(device: &Device) -> Result, Drive size: size_of::(), }], }), - )?)) + ) } -fn create_exclusive_sum_pipeline(device: &Device) -> Result, DriverError> { - Ok( Arc::new( - ComputePipeline::create( +fn create_exclusive_sum_pipeline(device: &Device) -> Result { + ComputePipeline::create( device, ComputePipelineInfo::default(), Shader::new_compute(glsl!( @@ -254,8 +253,7 @@ fn create_exclusive_sum_pipeline(device: &Device) -> Result size: size_of::(), }], }), - )? - )) + ) } #[derive(Parser)] diff --git a/examples/triangle.rs b/examples/triangle.rs index 4ec401a1..db475df3 100644 --- a/examples/triangle.rs +++ b/examples/triangle.rs @@ -16,7 +16,7 @@ fn main() -> Result<(), WindowError> { let args = Args::parse(); let window = WindowBuilder::default().debug(args.debug).build()?; - let triangle_pipeline = Arc::new(GraphicPipeline::create( + let triangle_pipeline = GraphicPipeline::create( &window.device, GraphicPipelineInfo::default(), [ @@ -57,7 +57,7 @@ fn main() -> Result<(), WindowError> { .as_slice(), ), ], - )?); + )?; let index_buf = Arc::new(Buffer::create_from_slice( &window.device, diff --git a/examples/vertex_layout.rs b/examples/vertex_layout.rs index 2d1235ee..f84be4f1 100644 --- a/examples/vertex_layout.rs +++ b/examples/vertex_layout.rs @@ -92,11 +92,7 @@ fn main() -> anyhow::Result<()> { Ok(()) } -fn draw_triangle( - frame: &mut FrameContext, - pipeline: &Arc, - vertex_buf: &Arc, -) { +fn draw_triangle(frame: &mut FrameContext, pipeline: &GraphicPipeline, vertex_buf: &Arc) { let vertex_buf = frame.render_graph.bind_node(vertex_buf); frame @@ -114,7 +110,7 @@ fn draw_triangle( }); } -fn create_f16_pipeline(device: &Device) -> Result, DriverError> { +fn create_f16_pipeline(device: &Device) -> Result { if !supports_vertex_buffer(device, vk::Format::R16G16_SFLOAT) { return Err(DriverError::Unsupported); } @@ -147,14 +143,14 @@ fn create_f16_pipeline(device: &Device) -> Result, DriverEr create_pipeline(device, vertex) } -fn create_f32_pipeline(device: &Device) -> Result, DriverError> { +fn create_f32_pipeline(device: &Device) -> Result { // Uses automatic vertex input layout let vertex = create_vertex_shader(false); create_pipeline(device, vertex) } -fn create_f64_pipeline(device: &Device) -> Result, DriverError> { +fn create_f64_pipeline(device: &Device) -> Result { if !supports_vertex_buffer(device, vk::Format::R64G64_SFLOAT) { return Err(DriverError::Unsupported); } @@ -230,30 +226,31 @@ fn create_vertex_shader(is_double: bool) -> ShaderBuilder { Shader::new_vertex(spirv) } -fn create_pipeline( - device: &Device, - vertex: ShaderBuilder, -) -> Result, DriverError> { - let fragment_spirv = glsl!( - r#" - #version 460 core - #pragma shader_stage(fragment) - - layout(location = 0) in vec3 color_in; - - layout(location = 0) out vec4 color_out; - - void main() { - color_out = vec4(color_in, 1.0); - } - "# - ); - - Ok(Arc::new(GraphicPipeline::create( +fn create_pipeline(device: &Device, vertex: ShaderBuilder) -> Result { + GraphicPipeline::create( device, GraphicPipelineInfo::default(), - [vertex, Shader::new_fragment(fragment_spirv.as_slice())], - )?)) + [ + vertex, + Shader::new_fragment( + glsl!( + r#" + #version 460 core + #pragma shader_stage(fragment) + + layout(location = 0) in vec3 color_in; + + layout(location = 0) out vec4 color_out; + + void main() { + color_out = vec4(color_in, 1.0); + } + "# + ) + .as_slice(), + ), + ], + ) } fn supports_vertex_buffer(device: &Device, format: vk::Format) -> bool { diff --git a/examples/vsm_omni.rs b/examples/vsm_omni.rs index 381afe7d..e59fd8c9 100644 --- a/examples/vsm_omni.rs +++ b/examples/vsm_omni.rs @@ -448,7 +448,7 @@ fn best_2d_optimal_format( panic!("Unsupported format"); } -fn create_blur_x_pipeline(device: &Device) -> Result, DriverError> { +fn create_blur_x_pipeline(device: &Device) -> Result { let comp = glsl!( r#" #version 450 core @@ -564,14 +564,10 @@ fn create_blur_x_pipeline(device: &Device) -> Result, Drive }), )); - Ok(Arc::new(ComputePipeline::create( - device, - ComputePipelineInfo::default(), - shader, - )?)) + ComputePipeline::create(device, ComputePipelineInfo::default(), shader) } -fn create_blur_y_pipeline(device: &Device) -> Result, DriverError> { +fn create_blur_y_pipeline(device: &Device) -> Result { let comp = glsl!( r#" #version 450 core @@ -687,14 +683,10 @@ fn create_blur_y_pipeline(device: &Device) -> Result, Drive }), )); - Ok(Arc::new(ComputePipeline::create( - device, - ComputePipelineInfo::default(), - shader, - )?)) + ComputePipeline::create(device, ComputePipelineInfo::default(), shader) } -fn create_debug_pipeline(device: &Device) -> Result, DriverError> { +fn create_debug_pipeline(device: &Device) -> Result { let vert = glsl!( r#" #version 450 core @@ -758,19 +750,17 @@ fn create_debug_pipeline(device: &Device) -> Result, Driver "# ); - let info = GraphicPipelineInfo::default(); - - Ok(Arc::new(GraphicPipeline::create( + GraphicPipeline::create( device, - info, + GraphicPipelineInfo::default(), [ Shader::new_vertex(vert.as_slice()), Shader::new_fragment(frag.as_slice()), ], - )?)) + ) } -fn create_mesh_pipeline(device: &Device) -> Result, DriverError> { +fn create_mesh_pipeline(device: &Device) -> Result { let vert = glsl!( r#" #version 450 core @@ -870,11 +860,9 @@ fn create_mesh_pipeline(device: &Device) -> Result, DriverE "# ); - let info = GraphicPipelineInfo::default(); - - Ok(Arc::new(GraphicPipeline::create( + GraphicPipeline::create( device, - info, + GraphicPipelineInfo::default(), [ Shader::new_vertex(vert.as_slice()), Shader::new_fragment(frag.as_slice()).specialization_info(SpecializationInfo::new( @@ -886,10 +874,10 @@ fn create_mesh_pipeline(device: &Device) -> Result, DriverE bytes_of(&SHADOW_BIAS), )), ], - )?)) + ) } -fn create_shadow_pipeline(device: &Device) -> Result, DriverError> { +fn create_shadow_pipeline(device: &Device) -> Result { let vert = glsl!( r#" #version 450 core @@ -946,21 +934,19 @@ fn create_shadow_pipeline(device: &Device) -> Result, Drive "# ); - let info = GraphicPipelineInfo::default(); - - Ok(Arc::new(GraphicPipeline::create( + GraphicPipeline::create( device, - info, + GraphicPipelineInfo::default(), [ Shader::new_vertex(vert.as_slice()), Shader::new_fragment(frag.as_slice()), ], - )?)) + ) } fn create_shadow_pipeline_with_geometry_shader( device: &Device, -) -> Result, DriverError> { +) -> Result { let vert = glsl!( r#" #version 450 core @@ -1145,7 +1131,7 @@ fn create_shadow_pipeline_with_geometry_shader( let info = GraphicPipelineInfo::default(); - Ok(Arc::new(GraphicPipeline::create( + GraphicPipeline::create( device, info, [ @@ -1153,7 +1139,7 @@ fn create_shadow_pipeline_with_geometry_shader( Shader::new_geometry(geom.as_slice()), Shader::new_fragment(frag.as_slice()), ], - )?)) + ) } fn download_model_from_github(model_name: &str) -> anyhow::Result { diff --git a/src/cmd_ref/accel.rs b/src/cmd_ref/accel.rs index 447fdde9..e43ae0c9 100644 --- a/src/cmd_ref/accel.rs +++ b/src/cmd_ref/accel.rs @@ -33,7 +33,7 @@ use { /// # use vk_graph::Graph; /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { -/// # let device = Arc::new(Device::new(DeviceInfo::default())?); +/// # let device = Device::new(DeviceInfo::default())?; /// # let mut my_graph = Graph::default(); /// # let info = AccelerationStructureInfo::blas(1); /// my_graph.begin_cmd().with_name("my acceleration command") @@ -73,7 +73,7 @@ impl Acceleration<'_> { /// # use vk_graph::Graph; /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # let mut my_graph = Graph::default(); /// # let info = AccelerationStructureInfo::blas(1); /// # let blas_accel_struct = AccelerationStructure::create(&device, info)?; diff --git a/src/cmd_ref/compute.rs b/src/cmd_ref/compute.rs index 29b25837..49aaf16a 100644 --- a/src/cmd_ref/compute.rs +++ b/src/cmd_ref/compute.rs @@ -6,7 +6,6 @@ use { }, ash::vk, log::trace, - std::sync::Arc, }; /// Recording interface for computing commands. @@ -21,7 +20,6 @@ use { /// Basic usage: /// /// ```no_run -/// # use std::sync::Arc; /// # use ash::vk; /// # use vk_graph::driver::DriverError; /// # use vk_graph::driver::device::{Device, DeviceInfo}; @@ -29,10 +27,10 @@ use { /// # use vk_graph::driver::shader::{Shader}; /// # use vk_graph::Graph; /// # fn main() -> Result<(), DriverError> { -/// # let device = Arc::new(Device::new(DeviceInfo::default())?); +/// # let device = Device::new(DeviceInfo::default())?; /// # let info = ComputePipelineInfo::default(); /// # let shader = Shader::new_compute([0u8; 1].as_slice()); -/// # let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); +/// # let my_compute_pipeline = ComputePipeline::create(&device, info, shader)?; /// # let mut my_graph = Graph::default(); /// my_graph.begin_cmd().with_name("my compute pass") /// .bind_pipeline(&my_compute_pipeline) @@ -74,7 +72,6 @@ impl Compute<'_> { /// ``` /// /// ```no_run - /// # use std::sync::Arc; /// # use ash::vk; /// # use vk_graph::driver::DriverError; /// # use vk_graph::driver::device::{Device, DeviceInfo}; @@ -83,12 +80,12 @@ impl Compute<'_> { /// # use vk_graph::driver::shader::{Shader}; /// # use vk_graph::Graph; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::STORAGE_BUFFER); /// # let my_buf = Buffer::create(&device, buf_info)?; /// # let info = ComputePipelineInfo::default(); /// # let shader = Shader::new_compute([0u8; 1].as_slice()); - /// # let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); + /// # let my_compute_pipeline = ComputePipeline::create(&device, info, shader)?; /// # let mut my_graph = Graph::default(); /// # let my_buf_node = my_graph.bind_node(my_buf); /// my_graph.begin_cmd().with_name("fill my_buf_node with data") @@ -159,7 +156,6 @@ impl Compute<'_> { /// Basic usage: /// /// ```no_run - /// # use std::sync::Arc; /// # use std::mem::size_of; /// # use ash::vk; /// # use vk_graph::driver::DriverError; @@ -169,12 +165,12 @@ impl Compute<'_> { /// # use vk_graph::driver::shader::{Shader}; /// # use vk_graph::Graph; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::STORAGE_BUFFER); /// # let my_buf = Buffer::create(&device, buf_info)?; /// # let info = ComputePipelineInfo::default(); /// # let shader = Shader::new_compute([0u8; 1].as_slice()); - /// # let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); + /// # let my_compute_pipeline = ComputePipeline::create(&device, info, shader)?; /// # let mut my_graph = Graph::default(); /// # let my_buf_node = my_graph.bind_node(my_buf); /// const CMD_SIZE: usize = size_of::(); @@ -260,7 +256,6 @@ impl Compute<'_> { /// ``` /// /// ```no_run - /// # use std::sync::Arc; /// # use ash::vk; /// # use vk_graph::driver::DriverError; /// # use vk_graph::driver::device::{Device, DeviceInfo}; @@ -269,10 +264,10 @@ impl Compute<'_> { /// # use vk_graph::driver::shader::{Shader}; /// # use vk_graph::Graph; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # let info = ComputePipelineInfo::default(); /// # let shader = Shader::new_compute([0u8; 1].as_slice()); - /// # let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); + /// # let my_compute_pipeline = ComputePipeline::create(&device, info, shader)?; /// # let mut my_graph = Graph::default(); /// my_graph.begin_cmd().with_name("compute the ultimate question") /// .bind_pipeline(&my_compute_pipeline) @@ -286,7 +281,7 @@ impl Compute<'_> { /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all #[profiling::function] pub fn push_constants(&self, offset: u32, data: &[u8]) -> &Self { - if let Some(push_const) = self.pipeline.push_constants { + if let Some(push_const) = self.pipeline.inner.push_constants { // Determine the range of the overall pipline push constants which overlap with `data` let push_const_end = push_const.offset + push_const.size; let data_end = offset + data.len() as u32; @@ -302,7 +297,7 @@ impl Compute<'_> { unsafe { self.device.cmd_push_constants( self.cmd_buf, - self.pipeline.layout(), + self.pipeline.inner.layout, vk::ShaderStageFlags::COMPUTE, push_const.offset, &data[(start - offset) as usize..(end - offset) as usize], @@ -322,16 +317,17 @@ impl PipelineCommandRef<'_, ComputePipeline> { mut self, func: impl FnOnce(Compute<'_>, Bindings<'_>) + Send + 'static, ) -> Self { - let pipeline = - self.cmd - .as_ref() - .execs - .last() - .unwrap() - .pipeline - .as_ref() - .unwrap() - .unwrap_compute().clone(); + let pipeline = self + .cmd + .as_ref() + .execs + .last() + .unwrap() + .pipeline + .as_ref() + .unwrap() + .unwrap_compute() + .clone(); self.cmd.push_execute(move |device, cmd_buf, bindings| { func( diff --git a/src/cmd_ref/graphic.rs b/src/cmd_ref/graphic.rs index 5bf15b27..15fead4c 100644 --- a/src/cmd_ref/graphic.rs +++ b/src/cmd_ref/graphic.rs @@ -14,7 +14,7 @@ use { }, ash::vk, log::trace, - std::{cell::RefCell, slice, sync::Arc}, + std::{cell::RefCell, slice}, vk_sync::AccessType, }; @@ -30,7 +30,6 @@ use { /// Basic usage: /// /// ```no_run -/// # use std::sync::Arc; /// # use ash::vk; /// # use vk_graph::driver::DriverError; /// # use vk_graph::driver::device::{Device, DeviceInfo}; @@ -39,13 +38,13 @@ use { /// # use vk_graph::Graph; /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { -/// # let device = Arc::new(Device::new(DeviceInfo::default())?); +/// # let device = Device::new(DeviceInfo::default())?; /// # let my_frag_code = [0u8; 1]; /// # let my_vert_code = [0u8; 1]; /// # let vert = Shader::new_vertex(my_vert_code.as_slice()); /// # let frag = Shader::new_fragment(my_frag_code.as_slice()); /// # let info = GraphicPipelineInfo::default(); -/// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); +/// # let my_graphic_pipeline = GraphicPipeline::create(&device, info, [vert, frag])?; /// # let mut my_graph = Graph::default(); /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); /// # let swapchain_image = my_graph.bind_node(Image::create(&device, info)?); @@ -75,7 +74,6 @@ impl Graphic<'_> { /// Basic usage: /// /// ```no_run - /// # use std::sync::Arc; /// # use ash::vk; /// # use vk_graph::driver::DriverError; /// # use vk_graph::driver::device::{Device, DeviceInfo}; @@ -85,13 +83,13 @@ impl Graphic<'_> { /// # use vk_graph::driver::shader::Shader; /// # use vk_graph::Graph; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # let my_frag_code = [0u8; 1]; /// # let my_vert_code = [0u8; 1]; /// # let vert = Shader::new_vertex(my_vert_code.as_slice()); /// # let frag = Shader::new_fragment(my_frag_code.as_slice()); /// # let info = GraphicPipelineInfo::default(); - /// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); + /// # let my_graphic_pipeline = GraphicPipeline::create(&device, info, [vert, frag])?; /// # let mut my_graph = Graph::default(); /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); /// # let swapchain_image = my_graph.bind_node(Image::create(&device, info)?); @@ -143,7 +141,6 @@ impl Graphic<'_> { /// Basic usage: /// /// ```no_run - /// # use std::sync::Arc; /// # use ash::vk; /// # use vk_graph::driver::DriverError; /// # use vk_graph::driver::device::{Device, DeviceInfo}; @@ -153,7 +150,7 @@ impl Graphic<'_> { /// # use vk_graph::driver::shader::Shader; /// # use vk_graph::Graph; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::VERTEX_BUFFER); /// # let my_vtx_buf = Buffer::create(&device, buf_info)?; /// # let my_frag_code = [0u8; 1]; @@ -161,7 +158,7 @@ impl Graphic<'_> { /// # let vert = Shader::new_vertex(my_vert_code.as_slice()); /// # let frag = Shader::new_fragment(my_frag_code.as_slice()); /// # let info = GraphicPipelineInfo::default(); - /// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); + /// # let my_graphic_pipeline = GraphicPipeline::create(&device, info, [vert, frag])?; /// # let mut my_graph = Graph::default(); /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); /// # let swapchain_image = my_graph.bind_node(Image::create(&device, info)?); @@ -312,7 +309,6 @@ impl Graphic<'_> { /// Basic usage: /// /// ```no_run - /// # use std::sync::Arc; /// # use std::mem::size_of; /// # use ash::vk; /// # use vk_graph::driver::DriverError; @@ -323,13 +319,13 @@ impl Graphic<'_> { /// # use vk_graph::driver::shader::Shader; /// # use vk_graph::Graph; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # let my_frag_code = [0u8; 1]; /// # let my_vert_code = [0u8; 1]; /// # let vert = Shader::new_vertex(my_vert_code.as_slice()); /// # let frag = Shader::new_fragment(my_frag_code.as_slice()); /// # let info = GraphicPipelineInfo::default(); - /// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); + /// # let my_graphic_pipeline = GraphicPipeline::create(&device, info, [vert, frag])?; /// # let mut my_graph = Graph::default(); /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::INDEX_BUFFER); /// # let my_idx_buf = Buffer::create(&device, buf_info)?; @@ -525,7 +521,6 @@ impl Graphic<'_> { /// ``` /// /// ```no_run - /// # use std::sync::Arc; /// # use ash::vk; /// # use vk_graph::driver::DriverError; /// # use vk_graph::driver::device::{Device, DeviceInfo}; @@ -534,13 +529,13 @@ impl Graphic<'_> { /// # use vk_graph::Graph; /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # let my_frag_code = [0u8; 1]; /// # let my_vert_code = [0u8; 1]; /// # let vert = Shader::new_vertex(my_vert_code.as_slice()); /// # let frag = Shader::new_fragment(my_frag_code.as_slice()); /// # let info = GraphicPipelineInfo::default(); - /// # let my_graphic_pipeline = Arc::new(GraphicPipeline::create(&device, info, [vert, frag])?); + /// # let my_graphic_pipeline = GraphicPipeline::create(&device, info, [vert, frag])?; /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); /// # let swapchain_image = Image::create(&device, info)?; /// # let mut my_graph = Graph::default(); @@ -558,7 +553,7 @@ impl Graphic<'_> { /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all #[profiling::function] pub fn push_constants(&self, offset: u32, data: &[u8]) -> &Self { - for push_const in self.pipeline.push_constants.iter() { + for push_const in self.pipeline.inner.push_constants.iter() { // Determine the range of the overall pipline push constants which overlap with `data` let push_const_end = push_const.offset + push_const.size; let data_end = offset + data.len() as u32; @@ -574,7 +569,7 @@ impl Graphic<'_> { unsafe { self.device.cmd_push_constants( self.cmd_buf, - self.pipeline.layout, + self.pipeline.inner.layout, push_const.stage_flags, start, &data[(start - offset) as usize..(end - offset) as usize], @@ -1529,17 +1524,17 @@ impl PipelineCommandRef<'_, GraphicPipeline> { mut self, func: impl FnOnce(Graphic<'_>, Bindings<'_>) + Send + 'static, ) -> Self { - let pipeline = - self.cmd - .as_ref() - .execs - .last() - .unwrap() - .pipeline - .as_ref() - .unwrap() - .unwrap_graphic().clone() - ; + let pipeline = self + .cmd + .as_ref() + .execs + .last() + .unwrap() + .pipeline + .as_ref() + .unwrap() + .unwrap_graphic() + .clone(); self.cmd.push_execute(move |device, cmd_buf, bindings| { func( diff --git a/src/cmd_ref/mod.rs b/src/cmd_ref/mod.rs index 5f68de6a..bbd4ff23 100644 --- a/src/cmd_ref/mod.rs +++ b/src/cmd_ref/mod.rs @@ -30,7 +30,7 @@ use { ray_trace::RayTracePipeline, }, ash::vk, - std::{marker::PhantomData, ops::Index, sync::Arc}, + std::{marker::PhantomData, ops::Index}, vk_sync::AccessType, }; @@ -158,7 +158,7 @@ bind!(RayTrace); /// # use vk_graph::Graph; /// # use vk_graph::node::ImageNode; /// # fn main() -> Result<(), DriverError> { -/// # let device = Arc::new(Device::new(DeviceInfo::default())?); +/// # let device = Device::new(DeviceInfo::default())?; /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); /// # let image = Image::create(&device, info)?; /// # let mut my_graph = Graph::default(); diff --git a/src/cmd_ref/ray_trace.rs b/src/cmd_ref/ray_trace.rs index 84de822b..e5fcca15 100644 --- a/src/cmd_ref/ray_trace.rs +++ b/src/cmd_ref/ray_trace.rs @@ -3,7 +3,6 @@ use { crate::driver::{device::Device, ray_trace::RayTracePipeline}, ash::vk, log::trace, - std::sync::Arc, }; // NOTE: local implementation of type from super module @@ -13,19 +12,20 @@ impl PipelineCommandRef<'_, RayTracePipeline> { mut self, func: impl FnOnce(RayTrace<'_>, Bindings<'_>) + Send + 'static, ) -> Self { - let pipeline = - self.cmd - .as_ref() - .execs - .last() - .unwrap() - .pipeline - .as_ref() - .unwrap() - .unwrap_ray_trace().clone(); + let pipeline = self + .cmd + .as_ref() + .execs + .last() + .unwrap() + .pipeline + .as_ref() + .unwrap() + .unwrap_ray_trace() + .clone(); #[cfg(debug_assertions)] - let dynamic_stack_size = pipeline.info.dynamic_stack_size; + let dynamic_stack_size = pipeline.inner.info.dynamic_stack_size; self.cmd.push_execute(move |device, cmd_buf, bindings| { func( @@ -58,7 +58,6 @@ impl PipelineCommandRef<'_, RayTracePipeline> { /// Basic usage: /// /// ```no_run -/// # use std::sync::Arc; /// # use ash::vk; /// # use vk_graph::driver::DriverError; /// # use vk_graph::driver::device::{Device, DeviceInfo}; @@ -66,13 +65,13 @@ impl PipelineCommandRef<'_, RayTracePipeline> { /// # use vk_graph::driver::shader::Shader; /// # use vk_graph::Graph; /// # fn main() -> Result<(), DriverError> { -/// # let device = Arc::new(Device::new(DeviceInfo::default())?); +/// # let device = Device::new(DeviceInfo::default())?; /// # let info = RayTracePipelineInfo::default(); /// # let my_miss_code = [0u8; 1]; -/// # let my_ray_trace_pipeline = Arc::new(RayTracePipeline::create(&device, info, +/// # let my_ray_trace_pipeline = RayTracePipeline::create(&device, info, /// [Shader::new_miss(my_miss_code.as_slice())], /// [RayTraceShaderGroup::new_general(0)], -/// )?); +/// )?; /// # let mut my_graph = Graph::default(); /// my_graph.begin_cmd().with_name("my ray trace pass") /// .bind_pipeline(&my_ray_trace_pipeline) @@ -128,7 +127,6 @@ impl RayTrace<'_> { /// ``` /// /// ```no_run - /// # use std::sync::Arc; /// # use ash::vk; /// # use vk_graph::driver::DriverError; /// # use vk_graph::driver::device::{Device, DeviceInfo}; @@ -137,14 +135,14 @@ impl RayTrace<'_> { /// # use vk_graph::driver::shader::Shader; /// # use vk_graph::Graph; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # let shader = [0u8; 1]; /// # let info = RayTracePipelineInfo::default(); /// # let my_miss_code = [0u8; 1]; - /// # let my_ray_trace_pipeline = Arc::new(RayTracePipeline::create(&device, info, + /// # let my_ray_trace_pipeline = RayTracePipeline::create(&device, info, /// # [Shader::new_miss(my_miss_code.as_slice())], /// # [RayTraceShaderGroup::new_general(0)], - /// # )?); + /// # )?; /// # let rgen_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; /// # let hit_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; /// # let miss_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; @@ -162,7 +160,7 @@ impl RayTrace<'_> { /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all #[profiling::function] pub fn push_constants(&self, offset: u32, data: &[u8]) -> &Self { - for push_const in self.pipeline.push_constants.iter() { + for push_const in self.pipeline.inner.push_constants.iter() { let push_const_end = push_const.offset + push_const.size; let data_end = offset + data.len() as u32; let end = data_end.min(push_const_end); @@ -177,7 +175,7 @@ impl RayTrace<'_> { unsafe { self.device.cmd_push_constants( self.cmd_buf, - self.pipeline.layout(), + self.pipeline.inner.layout, push_const.stage_flags, start, &data[(start - offset) as usize..(end - offset) as usize], @@ -221,7 +219,6 @@ impl RayTrace<'_> { /// Basic usage: /// /// ```no_run - /// # use std::sync::Arc; /// # use ash::vk; /// # use vk_graph::driver::DriverError; /// # use vk_graph::driver::device::{Device, DeviceInfo}; @@ -230,14 +227,14 @@ impl RayTrace<'_> { /// # use vk_graph::driver::shader::Shader; /// # use vk_graph::Graph; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # let shader = [0u8; 1]; /// # let info = RayTracePipelineInfo::default(); /// # let my_miss_code = [0u8; 1]; - /// # let my_ray_trace_pipeline = Arc::new(RayTracePipeline::create(&device, info, + /// # let my_ray_trace_pipeline = RayTracePipeline::create(&device, info, /// # [Shader::new_miss(my_miss_code.as_slice())], /// # [RayTraceShaderGroup::new_general(0)], - /// # )?); + /// # )?; /// # let rgen_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; /// # let hit_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; /// # let miss_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; diff --git a/src/display.rs b/src/display.rs index 06d044c9..faecf299 100644 --- a/src/display.rs +++ b/src/display.rs @@ -230,8 +230,11 @@ impl Display { // things so we do them last resolver.record_unscheduled_passes(pool, &mut exec.cmd_buf)?; - let queue = - exec.cmd_buf.device.queues[self.info.queue_family_index as usize][queue_index as usize]; + let queue = Device::queue( + &exec.cmd_buf.device, + self.info.queue_family_index, + queue_index, + ); unsafe { exec.cmd_buf diff --git a/src/driver/accel_struct.rs b/src/driver/accel_struct.rs index 6e0ae3b0..413c74bf 100644 --- a/src/driver/accel_struct.rs +++ b/src/driver/accel_struct.rs @@ -38,7 +38,7 @@ use std::sync::Mutex; /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; /// # fn main() -> Result<(), DriverError> { -/// # let device = Arc::new(Device::new(DeviceInfo::default())?); +/// # let device = Device::new(DeviceInfo::default())?; /// # const SIZE: vk::DeviceSize = 1024; /// # let info = AccelerationStructureInfo::blas(SIZE); /// # let my_accel_struct = AccelerationStructure::create(&device, info)?; @@ -60,12 +60,6 @@ pub struct AccelerationStructure { #[readonly] pub buffer: Buffer, - /// The device which owns this buffer resource. - /// - /// _Note:_ This field is read-only. - #[readonly] - pub device: Device, - /// The native Vulkan resource handle of this acceleration structure. /// /// _Note:_ This field is read-only. @@ -96,7 +90,7 @@ impl AccelerationStructure { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// const SIZE: vk::DeviceSize = 1024; /// let info = AccelerationStructureInfo::blas(SIZE); /// let accel_struct = AccelerationStructure::create(&device, info)?; @@ -146,12 +140,9 @@ impl AccelerationStructure { )? }; - let device = device.clone(); - Ok(Self { access: Mutex::new(AccessType::Nothing), buffer, - device, handle, info, name: None, @@ -180,7 +171,7 @@ impl AccelerationStructure { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # const SIZE: vk::DeviceSize = 1024; /// # let info = AccelerationStructureInfo::blas(SIZE); /// # let my_accel_struct = AccelerationStructure::create(&device, info)?; @@ -226,7 +217,7 @@ impl AccelerationStructure { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # const SIZE: vk::DeviceSize = 1024; /// # let info = AccelerationStructureInfo::blas(SIZE); /// # let my_accel_struct = AccelerationStructure::create(&device, info)?; @@ -237,7 +228,7 @@ impl AccelerationStructure { /// ``` #[profiling::function] pub fn device_address(&self) -> vk::DeviceAddress { - let accel_struct_ext = Device::expect_accel_struct_ext(&self.device); + let accel_struct_ext = Device::expect_accel_struct_ext(&self.buffer.device); unsafe { accel_struct_ext.get_acceleration_structure_device_address( @@ -268,7 +259,7 @@ impl AccelerationStructure { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureGeometry, AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, DeviceOrHostAddress}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # let my_geom_triangles = AccelerationStructureGeometryData::Triangles { /// # index_addr: DeviceOrHostAddress::DeviceAddress(0), /// # index_type: vk::IndexType::UINT32, @@ -355,7 +346,7 @@ impl Drop for AccelerationStructure { return; } - let accel_struct_ext = Device::expect_accel_struct_ext(&self.device); + let accel_struct_ext = Device::expect_accel_struct_ext(&self.buffer.device); unsafe { accel_struct_ext.destroy_acceleration_structure(self.handle, None); diff --git a/src/driver/buffer.rs b/src/driver/buffer.rs index 26cdddf5..80c5d644 100644 --- a/src/driver/buffer.rs +++ b/src/driver/buffer.rs @@ -43,7 +43,7 @@ use std::sync::Mutex; /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # fn main() -> Result<(), DriverError> { -/// # let device = Arc::new(Device::new(DeviceInfo::default())?); +/// # let device = Device::new(DeviceInfo::default())?; /// # let info = BufferInfo::device_mem(8, vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS); /// # let my_buf = Buffer::create(&device, info)?; /// let addr = Buffer::device_address(&my_buf); @@ -94,7 +94,7 @@ impl Buffer { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// const SIZE: vk::DeviceSize = 1024; /// let info = BufferInfo::host_mem(SIZE, vk::BufferUsageFlags::UNIFORM_BUFFER); /// let buf = Buffer::create(&device, info)?; @@ -211,7 +211,7 @@ impl Buffer { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// const DATA: [u8; 4] = [0xfe, 0xed, 0xbe, 0xef]; /// let buf = Buffer::create_from_slice(&device, vk::BufferUsageFlags::UNIFORM_BUFFER, &DATA)?; /// @@ -256,7 +256,7 @@ impl Buffer { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo, BufferSubresourceRange}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # const SIZE: vk::DeviceSize = 1024; /// # let info = BufferInfo::device_mem(SIZE, vk::BufferUsageFlags::STORAGE_BUFFER); /// # let my_buf = Buffer::create(&device, info)?; @@ -321,7 +321,7 @@ impl Buffer { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # let info = BufferInfo::host_mem(4, vk::BufferUsageFlags::empty()); /// # let mut my_buf = Buffer::create(&device, info)?; /// const DATA: [u8; 4] = [0xde, 0xad, 0xc0, 0xde]; @@ -353,7 +353,7 @@ impl Buffer { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # let info = BufferInfo::host_mem(4, vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS); /// # let my_buf = Buffer::create(&device, info)?; /// let addr = Buffer::device_address(&my_buf); @@ -393,7 +393,7 @@ impl Buffer { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # const DATA: [u8; 4] = [0; 4]; /// # let my_buf = Buffer::create_from_slice(&device, vk::BufferUsageFlags::empty(), &DATA)?; /// // my_buf is mappable and filled with four zeroes @@ -431,7 +431,7 @@ impl Buffer { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # const DATA: [u8; 4] = [0; 4]; /// # let mut my_buf = Buffer::create_from_slice(&device, vk::BufferUsageFlags::empty(), &DATA)?; /// let mut data = Buffer::mapped_slice_mut(&mut my_buf); diff --git a/src/driver/compute.rs b/src/driver/compute.rs index c5119159..746ce7bf 100644 --- a/src/driver/compute.rs +++ b/src/driver/compute.rs @@ -4,12 +4,17 @@ use { super::{ DriverError, device::Device, - shader::{DescriptorBindingMap, PipelineDescriptorInfo, PipelineHandle, PipelineInner, Shader}, + shader::{DescriptorBindingMap, PipelineDescriptorInfo, Shader}, }, ash::vk, derive_builder::{Builder, UninitializedFieldError}, log::{trace, warn}, - std::{ffi::CString, slice, sync::Arc}, + std::{ + ffi::CString, + slice, + sync::{Arc, OnceLock}, + thread::panicking, + }, }; /// Smart pointer handle to a [pipeline] object. @@ -24,20 +29,8 @@ use { /// [pipeline]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipeline.html /// [deref]: core::ops::Deref #[derive(Clone, Debug)] -#[readonly::make] pub struct ComputePipeline { - pub(crate) descriptor_bindings: DescriptorBindingMap, - - /// Information used to create this object. - #[readonly] - pub info: ComputePipelineInfo, - - inner: Arc, - - /// A descriptive name used in debugging messages. - pub name: Option, - - pub(crate) push_constants: Option, + pub(crate) inner: Arc, } impl ComputePipeline { @@ -59,7 +52,7 @@ impl ComputePipeline { /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; /// # use vk_graph::driver::shader::{Shader}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # let my_shader_code = [0u8; 1]; /// // my_shader_code is raw SPIR-V code as bytes /// let shader = Shader::new_compute(my_shader_code.as_slice()); @@ -157,24 +150,20 @@ impl ComputePipeline { device.destroy_shader_module(shader_module, None); Ok(ComputePipeline { - descriptor_bindings, - info, - inner: Arc::new(PipelineInner { + inner: Arc::new(ComputePipelineInner { + descriptor_bindings, descriptor_info, device: device.clone(), - handle: PipelineHandle::Handle(handle), + handle, + info, layout, + name: Default::default(), + push_constants, }), - name: None, - push_constants, }) } } - pub(crate) fn descriptor_info(&self) -> &PipelineDescriptorInfo { - &self.inner.descriptor_info - } - /// The device which owns this compute pipeline. pub fn device(&self) -> &Device { &self.inner.device @@ -182,20 +171,39 @@ impl ComputePipeline { /// The native Vulkan pipeline handle of this compute pipeline. pub fn handle(&self) -> vk::Pipeline { - let PipelineHandle::Handle(handle) = self.inner.handle else { - unreachable!(); - }; + self.inner.handle + } + + /// Gets the information used to create this object. + pub fn info(&self) -> ComputePipelineInfo { + self.inner.info + } - handle + /// Gets the debugging name assigned to this pipeline, if one has been set. + pub fn name(&self) -> Option<&str> { + self.inner.name.get().map(String::as_str) } - pub(crate) fn layout(&self) -> vk::PipelineLayout { - self.inner.layout + /// Sets the debugging name assigned to this pipeline. + /// + /// _Note:_ The pipeline name may only be assigned once. Subsequent calls will not update the + /// previously set name value. + pub fn set_name(&mut self, name: impl Into) { + if !self.inner.device.physical_device.instance.info.debug { + return; + } + + // Both Ok and Err are valid conditions + let _ = self.inner.name.set(name.into()); } /// Sets the debugging name assigned to this pipeline. + /// + /// _Note:_ The pipeline name may only be assigned once. Subsequent calls will not update the + /// previously set name value. pub fn with_name(mut self, name: impl Into) -> Self { - self.name = Some(name.into()); + self.set_name(name); + self } } @@ -276,6 +284,32 @@ impl ComputePipelineInfoBuilder { } } +#[derive(Debug)] +pub(crate) struct ComputePipelineInner { + pub descriptor_bindings: DescriptorBindingMap, + pub descriptor_info: PipelineDescriptorInfo, + pub device: Device, + pub handle: vk::Pipeline, + pub info: ComputePipelineInfo, + pub layout: vk::PipelineLayout, + pub name: OnceLock, + pub push_constants: Option, +} + +impl Drop for ComputePipelineInner { + #[profiling::function] + fn drop(&mut self) { + if panicking() { + return; + } + + unsafe { + self.device.destroy_pipeline(self.handle, None); + self.device.destroy_pipeline_layout(self.layout, None); + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/driver/device.rs b/src/driver/device.rs index 86de3789..4fe6274c 100644 --- a/src/driver/device.rs +++ b/src/driver/device.rs @@ -93,24 +93,16 @@ fn select_physical_device( #[derive(Clone)] #[repr(C)] pub struct Device { - accel_struct_ext: Option, inner: Arc, /// The physical device, which contains useful data about features, properties, and limits. /// /// _Note:_ This field is read-only. #[cfg(doc)] - pub physical_device: PhysicalDevice, + pub physical_device: Box, #[cfg(not(doc))] - physical_device: PhysicalDevice, - - /// The physical execution queues which all work will be submitted to. - pub(crate) queues: Box<[Box<[vk::Queue]>]>, - - ray_trace_ext: Option, - surface_ext: Option, - swapchain_ext: Option, + physical_device: Box, } impl Device { @@ -167,7 +159,8 @@ impl Device { /// /// Panics if [Self.physical_device.accel_struct_properties] is `None`. pub(crate) fn expect_accel_struct_ext(this: &Self) -> &khr::acceleration_structure::Device { - this.accel_struct_ext + this.inner + .accel_struct_ext .as_ref() .expect("VK_KHR_acceleration_structure") } @@ -179,7 +172,8 @@ impl Device { /// /// Panics if [Self.physical_device.ray_trace_properties] is `None`. pub(crate) fn expect_ray_trace_ext(this: &Self) -> &khr::ray_tracing_pipeline::Device { - this.ray_trace_ext + this.inner + .ray_trace_ext .as_ref() .expect("VK_KHR_ray_tracing_pipeline") } @@ -190,7 +184,7 @@ impl Device { /// /// Panics if the device was not created for display window access. pub(crate) fn expect_surface_ext(this: &Self) -> &khr::surface::Instance { - this.surface_ext.as_ref().expect("VK_KHR_surface") + this.inner.surface_ext.as_ref().expect("VK_KHR_surface") } /// Helper for times when you already know that the device supports the swapchain extension. @@ -199,7 +193,7 @@ impl Device { /// /// Panics if the device was not created for display window access. pub(crate) fn expect_swapchain_ext(this: &Self) -> &khr::swapchain::Device { - this.swapchain_ext.as_ref().expect("VK_KHR_swapchain") + this.inner.swapchain_ext.as_ref().expect("VK_KHR_swapchain") } /// Loads and existing `ash` Vulkan device that may have been created by other means. @@ -265,17 +259,17 @@ impl Device { })?; Ok(Self { - accel_struct_ext, inner: Arc::new(DeviceInner { + accel_struct_ext, allocator: ManuallyDrop::new(Mutex::new(allocator)), device, pipeline_cache, + queues: queues.into_boxed_slice(), + ray_trace_ext, + surface_ext, + swapchain_ext, }), - physical_device, - queues: queues.into_boxed_slice(), - ray_trace_ext, - surface_ext, - swapchain_ext, + physical_device: Box::new(physical_device), }) } @@ -336,6 +330,23 @@ impl Device { this.inner.pipeline_cache } + /// TODO + /// + /// Panics if the indicies are invalid. + pub fn queue(this: &Self, queue_family_index: u32, queue_index: u32) -> vk::Queue { + debug_assert!( + (queue_family_index as usize) < this.physical_device.queue_families.len(), + "Queue family index must be within the range of the available queues created by the device." + ); + debug_assert!( + queue_index + < this.physical_device.queue_families[queue_family_index as usize].queue_count, + "Queue index must be within the range of the available queues created by the device." + ); + + this.inner.queues[queue_family_index as usize][queue_index as usize] + } + #[profiling::function] pub(crate) fn wait_for_fence(this: &Self, fence: &vk::Fence) -> Result<(), DriverError> { Device::wait_for_fences(this, slice::from_ref(fence)) @@ -506,9 +517,14 @@ impl DeviceInfoBuilder { } struct DeviceInner { + accel_struct_ext: Option, allocator: ManuallyDrop>, device: ash::Device, pipeline_cache: vk::PipelineCache, + pub queues: Box<[Box<[vk::Queue]>]>, + ray_trace_ext: Option, + surface_ext: Option, + swapchain_ext: Option, } impl Drop for DeviceInner { @@ -545,13 +561,8 @@ impl Drop for DeviceInner { #[doc(hidden)] #[repr(C)] pub struct ReadOnlyDevice { - accel_struct_ext: Option, inner: Arc, - pub physical_device: PhysicalDevice, - pub(crate) queues: Box<[Box<[vk::Queue]>]>, - ray_trace_ext: Option, - surface_ext: Option, - swapchain_ext: Option, + pub physical_device: Box, } impl Deref for ReadOnlyDevice { @@ -573,31 +584,11 @@ mod tests { pub fn device_repr_c() { // HACK: The readonly crate uses a private implementation and so we can't further deref it // into the native object type. Because of this the ReadOnly part is manually implemented. - assert_eq!( - offset_of!(Device, accel_struct_ext), - offset_of!(ReadOnlyDevice, accel_struct_ext), - ); assert_eq!(offset_of!(Device, inner), offset_of!(ReadOnlyDevice, inner),); assert_eq!( offset_of!(Device, physical_device), offset_of!(ReadOnlyDevice, physical_device), ); - assert_eq!( - offset_of!(Device, queues), - offset_of!(ReadOnlyDevice, queues), - ); - assert_eq!( - offset_of!(Device, ray_trace_ext), - offset_of!(ReadOnlyDevice, ray_trace_ext), - ); - assert_eq!( - offset_of!(Device, surface_ext), - offset_of!(ReadOnlyDevice, surface_ext), - ); - assert_eq!( - offset_of!(Device, swapchain_ext), - offset_of!(ReadOnlyDevice, swapchain_ext), - ); } #[test] diff --git a/src/driver/graphic.rs b/src/driver/graphic.rs index d13b34cf..4984431f 100644 --- a/src/driver/graphic.rs +++ b/src/driver/graphic.rs @@ -12,7 +12,12 @@ use { derive_builder::{Builder, UninitializedFieldError}, log::{Level::Trace, log_enabled, trace, warn}, ordered_float::OrderedFloat, - std::{collections::HashSet, ffi::CString, thread::panicking}, + std::{ + collections::HashSet, + ffi::CString, + sync::{Arc, OnceLock}, + thread::panicking, + }, }; const RGBA_COLOR_COMPONENTS: vk::ColorComponentFlags = vk::ColorComponentFlags::from_raw( @@ -345,31 +350,10 @@ impl From for DepthStencilModeBuilderError { /// Also contains information about the object. /// /// [pipeline]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipeline.html -#[derive(Debug)] +#[derive(Clone, Debug)] #[readonly::make] pub struct GraphicPipeline { - pub(crate) descriptor_bindings: DescriptorBindingMap, - pub(crate) descriptor_info: PipelineDescriptorInfo, - - /// The device which owns this buffer resource. - /// - /// _Note:_ This field is read-only. - #[readonly] - pub device: Device, - - /// Information used to create this object. - #[readonly] - pub info: GraphicPipelineInfo, - - pub(crate) input_attachments: Box<[u32]>, - pub(crate) layout: vk::PipelineLayout, - - /// A descriptive name used in debugging messages. - pub name: Option, - - pub(crate) push_constants: Vec, - pub(crate) shader_modules: Vec, - pub(super) state: GraphicPipelineState, + pub(crate) inner: Arc, } impl GraphicPipeline { @@ -394,7 +378,7 @@ impl GraphicPipeline { /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # let my_frag_code = [0u8; 1]; /// # let my_vert_code = [0u8; 1]; /// // shader code is raw SPIR-V code as bytes @@ -403,7 +387,7 @@ impl GraphicPipeline { /// let info = GraphicPipelineInfo::default(); /// let pipeline = GraphicPipeline::create(&device, info, [vert, frag])?; /// - /// assert_eq!(pipeline.info.front_face, vk::FrontFace::COUNTER_CLOCKWISE); + /// assert_eq!(pipeline.info().front_face, vk::FrontFace::COUNTER_CLOCKWISE); /// # Ok(()) } /// ``` #[profiling::function] @@ -514,7 +498,7 @@ impl GraphicPipeline { DriverError::Unsupported })?; - let shader_info = shaders + let shader_stages = shaders .into_iter() .map(|shader| { let shader_module = device @@ -527,24 +511,16 @@ impl GraphicPipeline { DriverError::Unsupported })?; - let shader_stage = Stage { + let shader_stage = ShaderStage { flags: shader.stage, module: shader_module, name: CString::new(shader.entry_name.as_str()).unwrap(), specialization_info: shader.specialization_info, }; - Result::<_, DriverError>::Ok((shader_module, shader_stage)) + Result::<_, DriverError>::Ok(shader_stage) }) - .collect::, _>>()?; - let mut shader_modules = vec![]; - let mut stages = vec![]; - shader_info - .into_iter() - .for_each(|(shader_module, shader_stage)| { - shader_modules.push(shader_module); - stages.push(shader_stage); - }); + .collect::, _>>()?; let mut multisample = MultisampleState { alpha_to_coverage_enable: info.alpha_to_coverage, @@ -572,51 +548,62 @@ impl GraphicPipeline { multisample.min_sample_shading = min_sample_shading; } - let push_constants = merge_push_constant_ranges(&push_constants); + let push_constants = merge_push_constant_ranges(&push_constants).into_boxed_slice(); Ok(Self { - descriptor_bindings, - descriptor_info, - device, - info, - input_attachments, - layout, - name: None, - push_constants, - shader_modules, - state: GraphicPipelineState { + inner: Arc::new(GraphicPipelineInner { + descriptor_bindings, + descriptor_info, + device, + info, + input_attachments, layout, multisample, - stages, + name: Default::default(), + push_constants, + shader_stages, vertex_input, - }, + }), }) } } - /// Sets the debugging name assigned to this pipeline. - pub fn with_name(mut self, name: impl Into) -> Self { - self.name = Some(name.into()); - self + /// The device which owns this compute pipeline. + pub fn device(&self) -> &Device { + &self.inner.device } -} -impl Drop for GraphicPipeline { - #[profiling::function] - fn drop(&mut self) { - if panicking() { + /// Gets the information used to create this object. + pub fn info(&self) -> GraphicPipelineInfo { + self.inner.info + } + + /// Gets the debugging name assigned to this pipeline, if one has been set. + pub fn name(&self) -> Option<&str> { + self.inner.name.get().map(String::as_str) + } + + /// Sets the debugging name assigned to this pipeline. + /// + /// _Note:_ The pipeline name may only be assigned once. Subsequent calls will not update the + /// previously set name value. + pub fn set_name(&mut self, name: impl Into) { + if !self.inner.device.physical_device.instance.info.debug { return; } - unsafe { - self.device.destroy_pipeline_layout(self.layout, None); - } + // Both Ok and Err are valid conditions + let _ = self.inner.name.set(name.into()); + } - for shader_module in self.shader_modules.drain(..) { - unsafe { - self.device.destroy_shader_module(shader_module, None); - } - } + /// Sets the debugging name assigned to this pipeline. + /// + /// _Note:_ The pipeline name may only be assigned once. Subsequent calls will not update the + /// previously set name value. + pub fn with_name(mut self, name: impl Into) -> Self { + self.set_name(name); + + self } } @@ -773,15 +760,41 @@ impl GraphicPipelineInfoBuilder { } #[derive(Debug)] -pub(super) struct GraphicPipelineState { +pub(crate) struct GraphicPipelineInner { + pub descriptor_bindings: DescriptorBindingMap, + pub descriptor_info: PipelineDescriptorInfo, + pub device: Device, + pub info: GraphicPipelineInfo, + pub input_attachments: Box<[u32]>, pub layout: vk::PipelineLayout, pub multisample: MultisampleState, - pub stages: Vec, + pub name: OnceLock, + pub push_constants: Box<[vk::PushConstantRange]>, + pub shader_stages: Box<[ShaderStage]>, pub vertex_input: VertexInputState, } +impl Drop for GraphicPipelineInner { + #[profiling::function] + fn drop(&mut self) { + if panicking() { + return; + } + + unsafe { + self.device.destroy_pipeline_layout(self.layout, None); + } + + for shader_stage in &mut self.shader_stages { + unsafe { + self.device.destroy_shader_module(shader_stage.module, None); + } + } + } +} + #[derive(Debug, Default)] -pub(super) struct MultisampleState { +pub(crate) struct MultisampleState { pub alpha_to_coverage_enable: bool, pub alpha_to_one_enable: bool, pub flags: vk::PipelineMultisampleStateCreateFlags, @@ -792,7 +805,7 @@ pub(super) struct MultisampleState { } #[derive(Debug)] -pub(super) struct Stage { +pub(crate) struct ShaderStage { pub flags: vk::ShaderStageFlags, pub module: vk::ShaderModule, pub name: CString, // TODO @@ -862,7 +875,7 @@ impl From for vk::StencilOpState { } #[derive(Clone, Debug, Default)] -pub(super) struct VertexInputState { +pub(crate) struct VertexInputState { pub vertex_binding_descriptions: Vec, pub vertex_attribute_descriptions: Vec, } diff --git a/src/driver/image.rs b/src/driver/image.rs index d046d705..39f41385 100644 --- a/src/driver/image.rs +++ b/src/driver/image.rs @@ -80,7 +80,7 @@ pub(crate) fn image_subresource_range_intersects( /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::image::{Image, ImageInfo}; /// # fn main() -> Result<(), DriverError> { -/// # let device = Arc::new(Device::new(DeviceInfo::default())?); +/// # let device = Device::new(DeviceInfo::default())?; /// # let info = ImageInfo::image_1d(1, vk::Format::R8_UINT, vk::ImageUsageFlags::STORAGE); /// # let my_image = Image::create(&device, info)?; /// # let my_subresource_range = vk::ImageSubresourceRange::default(); @@ -135,7 +135,7 @@ impl Image { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::image::{Image, ImageInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); /// let image = Image::create(&device, info)?; /// @@ -252,7 +252,7 @@ impl Image { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::image::{Image, ImageInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # let info = ImageInfo::image_1d(1, vk::Format::R8_UINT, vk::ImageUsageFlags::STORAGE); /// # let my_image = Image::create(&device, info)?; /// # let my_subresource_range = vk::ImageSubresourceRange::default(); diff --git a/src/driver/ray_trace.rs b/src/driver/ray_trace.rs index c4381a86..b36e32d6 100644 --- a/src/driver/ray_trace.rs +++ b/src/driver/ray_trace.rs @@ -6,12 +6,16 @@ use { device::Device, merge_push_constant_ranges, physical_device::RayTraceProperties, - shader::{DescriptorBindingMap, PipelineDescriptorInfo,PipelineHandle, PipelineInner, Shader}, + shader::{DescriptorBindingMap, PipelineDescriptorInfo, Shader}, }, ash::vk, derive_builder::{Builder, UninitializedFieldError}, log::warn, - std::{ffi::CString, sync::Arc}, + std::{ + ffi::CString, + sync::{Arc, OnceLock}, + thread::panicking, + }, }; /// Smart pointer handle to a [pipeline] object. @@ -31,19 +35,7 @@ use { #[derive(Clone, Debug)] #[readonly::make] pub struct RayTracePipeline { - pub(crate) descriptor_bindings: DescriptorBindingMap, - - /// Information used to create this object. - #[readonly] - pub info: RayTracePipelineInfo, - - inner: Arc, - - /// A descriptive name used in debugging messages. - pub name: Option, - - pub(crate) push_constants: Vec, - shader_group_handles: Vec, + pub(crate) inner: Arc, } impl RayTracePipeline { @@ -71,7 +63,7 @@ impl RayTracePipeline { /// # use vk_graph::driver::ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}; /// # use vk_graph::driver::shader::Shader; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # let my_rgen_code = [0u8; 1]; /// # let my_chit_code = [0u8; 1]; /// # let my_miss_code = [0u8; 1]; @@ -96,7 +88,7 @@ impl RayTracePipeline { /// )?; /// /// assert_ne!(pipeline.handle(), vk::Pipeline::null()); - /// assert_eq!(pipeline.info.max_ray_recursion_depth, 1); + /// assert_eq!(pipeline.info().max_ray_recursion_depth, 1); /// # Ok(()) } /// ``` #[profiling::function] @@ -269,7 +261,7 @@ impl RayTracePipeline { .as_ref() .unwrap(); - let push_constants = merge_push_constant_ranges(&push_constants); + let push_constants = merge_push_constant_ranges(&push_constants).into_boxed_slice(); // SAFETY: // According to [vulkan spec](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkGetRayTracingShaderGroupHandlesKHR.html) @@ -289,28 +281,25 @@ impl RayTracePipeline { group_count * shader_group_handle_size as usize, ) } - .map_err(|_| DriverError::InvalidData)?; + .map_err(|_| DriverError::InvalidData)? + .into_boxed_slice(); Ok(Self { - descriptor_bindings, - inner: Arc::new(PipelineInner { + inner: Arc::new(RayTracePipelineInner { + descriptor_bindings, descriptor_info, device: device.clone(), - handle: PipelineHandle::Handle(handle), + handle, + info, layout, + name: Default::default(), + push_constants, + shader_group_handles, }), - info, - name: None, - push_constants, - shader_group_handles, }) } } - pub(crate) fn descriptor_info(&self) -> &PipelineDescriptorInfo { - &self.inner.descriptor_info - } - /// The device which owns this compute pipeline. pub fn device(&self) -> &Device { &self.inner.device @@ -338,7 +327,7 @@ impl RayTracePipeline { let start = idx * shader_group_handle_size as usize; let end = start + shader_group_handle_size as usize; - &self.shader_group_handles[start..end] + &self.inner.shader_group_handles[start..end] } /// Query ray trace pipeline shader group shader stack size. @@ -358,22 +347,41 @@ impl RayTracePipeline { } } - /// The native Vulkan pipeline handle of this compute pipeline. + /// The native Vulkan pipeline handle of this ray trace pipeline. pub fn handle(&self) -> vk::Pipeline { - let PipelineHandle::Handle(handle) = self.inner.handle else { - unreachable!(); - }; + self.inner.handle + } - handle + /// Gets the information used to create this object. + pub fn info(&self) -> RayTracePipelineInfo { + self.inner.info } - pub(crate) fn layout(&self) -> vk::PipelineLayout { - self.inner.layout + /// Gets the debugging name assigned to this pipeline, if one has been set. + pub fn name(&self) -> Option<&str> { + self.inner.name.get().map(String::as_str) } /// Sets the debugging name assigned to this pipeline. + /// + /// _Note:_ The pipeline name may only be assigned once. Subsequent calls will not update the + /// previously set name value. + pub fn set_name(&mut self, name: impl Into) { + if !self.inner.device.physical_device.instance.info.debug { + return; + } + + // Both Ok and Err are valid conditions + let _ = self.inner.name.set(name.into()); + } + + /// Sets the debugging name assigned to this pipeline. + /// + /// _Note:_ The pipeline name may only be assigned once. Subsequent calls will not update the + /// previously set name value. pub fn with_name(mut self, name: impl Into) -> Self { - self.name = Some(name.into()); + self.set_name(name); + self } } @@ -471,6 +479,33 @@ impl RayTracePipelineInfoBuilder { } } +#[derive(Debug)] +pub(crate) struct RayTracePipelineInner { + pub descriptor_bindings: DescriptorBindingMap, + pub descriptor_info: PipelineDescriptorInfo, + pub device: Device, + pub handle: vk::Pipeline, + pub info: RayTracePipelineInfo, + pub layout: vk::PipelineLayout, + pub name: OnceLock, + pub push_constants: Box<[vk::PushConstantRange]>, + pub shader_group_handles: Box<[u8]>, +} + +impl Drop for RayTracePipelineInner { + #[profiling::function] + fn drop(&mut self) { + if panicking() { + return; + } + + unsafe { + self.device.destroy_pipeline(self.handle, None); + self.device.destroy_pipeline_layout(self.layout, None); + } + } +} + /// Describes the set of the shader stages to be included in each shader group in the ray trace /// pipeline. /// diff --git a/src/driver/render_pass.rs b/src/driver/render_pass.rs index 2d65b32b..deff6cae 100644 --- a/src/driver/render_pass.rs +++ b/src/driver/render_pass.rs @@ -7,7 +7,6 @@ use { std::{ collections::{HashMap, hash_map::Entry}, slice, - sync::Arc, thread::panicking, }, }; @@ -320,15 +319,15 @@ impl RenderPass { } #[profiling::function] - pub fn graphic_pipeline( + pub fn pipeline_handle( &mut self, - pipeline: &Arc, + pipeline: &GraphicPipeline, depth_stencil: Option, subpass_idx: u32, ) -> Result { let entry = self.graphic_pipelines.entry(GraphicPipelineKey { depth_stencil, - layout: pipeline.layout, + layout: pipeline.inner.layout, subpass_idx, }); if let Entry::Occupied(entry) = entry { @@ -343,38 +342,29 @@ impl RenderPass { let color_blend_attachment_states = self.info.subpasses[subpass_idx as usize] .color_attachments .iter() - .map(|_| pipeline.info.blend.into()) + .map(|_| pipeline.inner.info.blend.into()) .collect::>(); let color_blend_state = vk::PipelineColorBlendStateCreateInfo::default() .attachments(&color_blend_attachment_states); - let dynamic_state = - vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&[vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR]); + let dynamic_state = vk::PipelineDynamicStateCreateInfo::default() + .dynamic_states(&[vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR]); let multisample_state = vk::PipelineMultisampleStateCreateInfo::default() - .alpha_to_coverage_enable(pipeline.state.multisample.alpha_to_coverage_enable) - .alpha_to_one_enable(pipeline.state.multisample.alpha_to_one_enable) - .flags(pipeline.state.multisample.flags) - .min_sample_shading(pipeline.state.multisample.min_sample_shading) - .rasterization_samples(pipeline.state.multisample.rasterization_samples.into()) - .sample_shading_enable(pipeline.state.multisample.sample_shading_enable) - .sample_mask(&pipeline.state.multisample.sample_mask); + .alpha_to_coverage_enable(pipeline.inner.multisample.alpha_to_coverage_enable) + .alpha_to_one_enable(pipeline.inner.multisample.alpha_to_one_enable) + .flags(pipeline.inner.multisample.flags) + .min_sample_shading(pipeline.inner.multisample.min_sample_shading) + .rasterization_samples(pipeline.inner.multisample.rasterization_samples.into()) + .sample_shading_enable(pipeline.inner.multisample.sample_shading_enable) + .sample_mask(&pipeline.inner.multisample.sample_mask); let specializations = pipeline - .state - .stages + .inner + .shader_stages .iter() - .map(|stage| { - stage - .specialization_info - .as_ref() - .map(|specialization_info| { - vk::SpecializationInfo::default() - .map_entries(&specialization_info.map_entries) - .data(&specialization_info.data) - }) - }) + .map(|stage| stage.specialization_info.as_ref().map(Into::into)) .collect::>(); let stages = pipeline - .state - .stages + .inner + .shader_stages .iter() .zip(specializations.iter()) .map(|(stage, specialization)| { @@ -392,22 +382,22 @@ impl RenderPass { .collect::>(); let vertex_input_state = vk::PipelineVertexInputStateCreateInfo::default() .vertex_attribute_descriptions( - &pipeline.state.vertex_input.vertex_attribute_descriptions, + &pipeline.inner.vertex_input.vertex_attribute_descriptions, ) - .vertex_binding_descriptions(&pipeline.state.vertex_input.vertex_binding_descriptions); + .vertex_binding_descriptions(&pipeline.inner.vertex_input.vertex_binding_descriptions); let viewport_state = vk::PipelineViewportStateCreateInfo::default() .viewport_count(1) .scissor_count(1); let input_assembly_state = vk::PipelineInputAssemblyStateCreateInfo { - topology: pipeline.info.topology, + topology: pipeline.inner.info.topology, ..Default::default() }; let depth_stencil = depth_stencil.map(Into::into).unwrap_or_default(); let rasterization_state = vk::PipelineRasterizationStateCreateInfo { - front_face: pipeline.info.front_face, + front_face: pipeline.inner.info.front_face, line_width: 1.0, - polygon_mode: pipeline.info.polygon_mode, - cull_mode: pipeline.info.cull_mode, + polygon_mode: pipeline.inner.info.polygon_mode, + cull_mode: pipeline.inner.info.cull_mode, ..Default::default() }; let create_info = vk::GraphicsPipelineCreateInfo::default() @@ -415,7 +405,7 @@ impl RenderPass { .depth_stencil_state(&depth_stencil) .dynamic_state(&dynamic_state) .input_assembly_state(&input_assembly_state) - .layout(pipeline.state.layout) + .layout(pipeline.inner.layout) .multisample_state(&multisample_state) .rasterization_state(&rasterization_state) .render_pass(self.handle) diff --git a/src/driver/shader.rs b/src/driver/shader.rs index 08c892e7..28eaaf94 100644 --- a/src/driver/shader.rs +++ b/src/driver/shader.rs @@ -323,36 +323,6 @@ impl PipelineDescriptorInfo { } } -#[derive(Debug)] -pub(crate) enum PipelineHandle { - Handle(vk::Pipeline), -} - -#[derive(Debug)] -pub(crate) struct PipelineInner { - pub descriptor_info: PipelineDescriptorInfo, - pub device: Device, - pub handle: PipelineHandle, - pub layout: vk::PipelineLayout, -} - -impl Drop for PipelineInner { - #[profiling::function] - fn drop(&mut self) { - if panicking() { - return; - } - - unsafe { - match self.handle { - PipelineHandle::Handle(handle) => self.device.destroy_pipeline(handle, None), - } - - self.device.destroy_pipeline_layout(self.layout, None); - } - } -} - pub(crate) struct Sampler { device: Device, sampler: vk::Sampler, @@ -732,7 +702,7 @@ pub struct Shader { /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::shader::{Shader, SpecializationInfo}; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # let my_shader_code = [0u8; 1]; /// // We instead specify 42 for MY_COUNT: /// let shader = Shader::new_fragment(my_shader_code.as_slice()) @@ -1549,6 +1519,14 @@ impl SpecializationInfo { } } +impl<'a> From<&'a SpecializationInfo> for vk::SpecializationInfo<'a> { + fn from(value: &'a SpecializationInfo) -> Self { + vk::SpecializationInfo::default() + .map_entries(&value.map_entries) + .data(&value.data) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/driver/swapchain.rs b/src/driver/swapchain.rs index 567785d2..88e418e0 100644 --- a/src/driver/swapchain.rs +++ b/src/driver/swapchain.rs @@ -198,20 +198,6 @@ impl Swapchain { queue_family_index: u32, queue_index: u32, ) { - let queue_family_index = queue_family_index as usize; - let queue_index = queue_index as usize; - - debug_assert!( - queue_family_index < self.device.physical_device.queue_families.len(), - "Queue family index must be within the range of the available queues created by the device." - ); - debug_assert!( - queue_index - < self.device.physical_device.queue_families[queue_family_index].queue_count - as usize, - "Queue index must be within the range of the available queues created by the device." - ); - let present_info = vk::PresentInfoKHR::default() .wait_semaphores(wait_semaphores) .swapchains(slice::from_ref(&self.handle)) @@ -221,7 +207,7 @@ impl Swapchain { unsafe { match swapchain_ext.queue_present( - self.device.queues[queue_family_index][queue_index], + Device::queue(&self.device, queue_family_index, queue_index), &present_info, ) { Ok(_) => { diff --git a/src/lib.rs b/src/lib.rs index 47aedccb..d25ed218 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,7 +66,7 @@ For example, a typical host-mappable buffer: # use vk_graph::driver::device::{Device, DeviceInfo}; # use vk_graph::driver::buffer::{Buffer, BufferInfo}; # fn main() -> Result<(), DriverError> { -# let device = Arc::new(Device::new(DeviceInfo::default())?); +# let device = Device::new(DeviceInfo::default())?; let info = BufferInfo::host_mem(1024, vk::BufferUsageFlags::STORAGE_BUFFER); let my_buf = Buffer::create(&device, info)?; # Ok(()) } @@ -88,7 +88,7 @@ For example, a graphics pipeline: # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; # use vk_graph::driver::shader::Shader; # fn main() -> Result<(), DriverError> { -# let device = Arc::new(Device::new(DeviceInfo::default())?); +# let device = Device::new(DeviceInfo::default())?; # let my_frag_code = [0u8; 1]; # let my_vert_code = [0u8; 1]; // shader code is SPIR-V in u32 format @@ -110,7 +110,7 @@ make the information of each resource easily available and immutable. # use vk_graph::driver::device::{Device, DeviceInfo}; # use vk_graph::driver::image::{Image, ImageInfo}; # fn main() -> Result<(), DriverError> { -# let device = Arc::new(Device::new(DeviceInfo::default())?); +# let device = Device::new(DeviceInfo::default())?; let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::empty()); let my_image = Image::create(&device, info)?; @@ -138,7 +138,7 @@ For example, leasing an image: # use vk_graph::pool::{Pool}; # use vk_graph::pool::lazy::{LazyPool}; # fn main() -> Result<(), DriverError> { -# let device = Arc::new(Device::new(DeviceInfo::default())?); +# let device = Device::new(DeviceInfo::default())?; let mut pool = LazyPool::new(&device); let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::STORAGE); @@ -178,7 +178,7 @@ it as a node. Bound nodes may only be used with the graphs they were bound to. N # use vk_graph::pool::{Pool}; # use vk_graph::pool::lazy::{LazyPool}; # fn main() -> Result<(), DriverError> { -# let device = Arc::new(Device::new(DeviceInfo::default())?); +# let device = Device::new(DeviceInfo::default())?; # let info = BufferInfo::host_mem(1024, vk::BufferUsageFlags::STORAGE_BUFFER); # let buffer = Buffer::create(&device, info)?; # let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::STORAGE); @@ -230,7 +230,7 @@ Example: # use vk_graph::pool::{Pool}; # use vk_graph::pool::lazy::{LazyPool}; # fn main() -> Result<(), DriverError> { -# let device = Arc::new(Device::new(DeviceInfo::default())?); +# let device = Device::new(DeviceInfo::default())?; # let info = BufferInfo::host_mem(1024, vk::BufferUsageFlags::STORAGE_BUFFER); # let buffer = Buffer::create(&device, info)?; # let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::STORAGE); @@ -262,7 +262,6 @@ graph Pipeline instances may be bound to a [`PassRef`] in order to execute the associated shader code: ```no_run -# use std::sync::Arc; # use ash::vk; # use vk_graph::driver::DriverError; # use vk_graph::driver::device::{Device, DeviceInfo}; @@ -270,11 +269,11 @@ Pipeline instances may be bound to a [`PassRef`] in order to execute the associa # use vk_graph::driver::shader::{Shader}; # use vk_graph::Graph; # fn main() -> Result<(), DriverError> { -# let device = Arc::new(Device::new(DeviceInfo::default())?); +# let device = Device::new(DeviceInfo::default())?; # let my_shader_code = [0u8; 1]; # let info = ComputePipelineInfo::default(); # let shader = Shader::new_compute(my_shader_code.as_slice()); -# let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?); +# let my_compute_pipeline = ComputePipeline::create(&device, info, shader)?; # let mut graph = Graph::default(); graph .begin_cmd().with_name("My compute pass") @@ -398,7 +397,6 @@ use { collections::{BTreeMap, HashMap}, fmt::{Debug, Formatter}, ops::Range, - sync::Arc, }, vk_sync::AccessType, }; @@ -592,25 +590,25 @@ impl ExecutionPipeline { fn descriptor_bindings(&self) -> &DescriptorBindingMap { match self { - ExecutionPipeline::Compute(pipeline) => &pipeline.descriptor_bindings, - ExecutionPipeline::Graphic(pipeline) => &pipeline.descriptor_bindings, - ExecutionPipeline::RayTrace(pipeline) => &pipeline.descriptor_bindings, + ExecutionPipeline::Compute(pipeline) => &pipeline.inner.descriptor_bindings, + ExecutionPipeline::Graphic(pipeline) => &pipeline.inner.descriptor_bindings, + ExecutionPipeline::RayTrace(pipeline) => &pipeline.inner.descriptor_bindings, } } fn descriptor_info(&self) -> &PipelineDescriptorInfo { match self { - ExecutionPipeline::Compute(pipeline) => pipeline.descriptor_info(), - ExecutionPipeline::Graphic(pipeline) => &pipeline.descriptor_info, - ExecutionPipeline::RayTrace(pipeline) => pipeline.descriptor_info(), + ExecutionPipeline::Compute(pipeline) => &pipeline.inner.descriptor_info, + ExecutionPipeline::Graphic(pipeline) => &pipeline.inner.descriptor_info, + ExecutionPipeline::RayTrace(pipeline) => &pipeline.inner.descriptor_info, } } fn layout(&self) -> vk::PipelineLayout { match self { - ExecutionPipeline::Compute(pipeline) => pipeline.layout(), - ExecutionPipeline::Graphic(pipeline) => pipeline.layout, - ExecutionPipeline::RayTrace(pipeline) => pipeline.layout(), + ExecutionPipeline::Compute(pipeline) => pipeline.inner.layout, + ExecutionPipeline::Graphic(pipeline) => pipeline.inner.layout, + ExecutionPipeline::RayTrace(pipeline) => pipeline.inner.layout, } } diff --git a/src/pool/mod.rs b/src/pool/mod.rs index 357f590e..e431040f 100644 --- a/src/pool/mod.rs +++ b/src/pool/mod.rs @@ -31,7 +31,7 @@ //! # use vk_graph::pool::{Pool}; //! # use vk_graph::pool::lazy::{LazyPool}; //! # fn main() -> Result<(), DriverError> { -//! # let device = Arc::new(Device::new(DeviceInfo::default())?); +//! # let device = Device::new(DeviceInfo::default())?; //! let mut pool = LazyPool::new(&device); //! //! let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::STORAGE); diff --git a/src/resolver.rs b/src/resolver.rs index 21596f20..0a7f59ec 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -12,6 +12,7 @@ use { SubpassDependency, SubpassInfo, accel_struct::AccelerationStructure, buffer::Buffer, + device::Device, format_aspect_mask, graphic::{DepthStencilMode, GraphicPipeline}, image::{Image, ImageAccess}, @@ -221,11 +222,13 @@ impl Resolver { let rhs_pipeline = unsafe { rhs_pipeline.unwrap_unchecked() }; // Must be same general rasterization modes - if lhs_pipeline.info.blend != rhs_pipeline.info.blend - || lhs_pipeline.info.cull_mode != rhs_pipeline.info.cull_mode - || lhs_pipeline.info.front_face != rhs_pipeline.info.front_face - || lhs_pipeline.info.polygon_mode != rhs_pipeline.info.polygon_mode - || lhs_pipeline.info.samples != rhs_pipeline.info.samples + let lhs_info = lhs_pipeline.inner.info; + let rhs_info = rhs_pipeline.inner.info; + if lhs_info.blend != rhs_info.blend + || lhs_info.cull_mode != rhs_info.cull_mode + || lhs_info.front_face != rhs_info.front_face + || lhs_info.polygon_mode != rhs_info.polygon_mode + || lhs_info.samples != rhs_info.samples { trace!(" different rasterization modes",); @@ -325,7 +328,7 @@ impl Resolver { } // Keep input on tile - if !rhs_pipeline.input_attachments.is_empty() { + if !rhs_pipeline.inner.input_attachments.is_empty() { trace!(" merging due to subpass input"); return true; @@ -634,13 +637,13 @@ impl Resolver { if log_enabled!(Trace) { let (ty, name, vk_pipeline) = match pipeline { ExecutionPipeline::Compute(pipeline) => { - ("compute", pipeline.name.as_ref(), pipeline.handle()) + ("compute", pipeline.name(), pipeline.handle()) } ExecutionPipeline::Graphic(pipeline) => { - ("graphic", pipeline.name.as_ref(), vk::Pipeline::null()) + ("graphic", pipeline.name(), vk::Pipeline::null()) } ExecutionPipeline::RayTrace(pipeline) => { - ("ray trace", pipeline.name.as_ref(), pipeline.handle()) + ("ray trace", pipeline.name(), pipeline.handle()) } }; if let Some(name) = name { @@ -654,7 +657,7 @@ impl Resolver { let pipeline_bind_point = pipeline.bind_point(); let pipeline = match pipeline { ExecutionPipeline::Compute(pipeline) => pipeline.handle(), - ExecutionPipeline::Graphic(pipeline) => RenderPass::graphic_pipeline( + ExecutionPipeline::Graphic(pipeline) => RenderPass::pipeline_handle( physical_pass.render_pass.as_mut().unwrap(), pipeline, depth_stencil, @@ -1062,7 +1065,7 @@ impl Resolver { let mut subpass_info = SubpassInfo::with_capacity(attachment_count); // Add input attachments - for attachment_idx in pipeline.input_attachments.iter() { + for attachment_idx in pipeline.inner.input_attachments.iter() { debug_assert!( !exec.color_clears.contains_key(attachment_idx), "cannot clear color attachment index {attachment_idx} because it uses subpass input", @@ -1713,6 +1716,7 @@ impl Resolver { .as_ref() .unwrap() .unwrap_graphic() + .inner .descriptor_info .pool_sizes .values() @@ -2882,8 +2886,8 @@ impl Resolver { pub fn submit

( mut self, pool: &mut P, - queue_family_index: usize, - queue_index: usize, + queue_family_index: u32, + queue_index: u32, ) -> Result, DriverError> where P: Pool @@ -2894,17 +2898,6 @@ impl Resolver { let mut cmd_buf = pool.lease(CommandBufferInfo::new(queue_family_index as _))?; - debug_assert!( - queue_family_index < cmd_buf.device.physical_device.queue_families.len(), - "Queue family index must be within the range of the available queues created by the device." - ); - debug_assert!( - queue_index - < cmd_buf.device.physical_device.queue_families[queue_family_index].queue_count - as usize, - "Queue index must be within the range of the available queues created by the device." - ); - cmd_buf.wait_until_executed()?; unsafe { @@ -2920,6 +2913,8 @@ impl Resolver { self.record_unscheduled_passes(pool, &mut cmd_buf)?; + let queue = Device::queue(&cmd_buf.device, queue_family_index, queue_index); + unsafe { cmd_buf .device @@ -2932,7 +2927,7 @@ impl Resolver { cmd_buf .device .queue_submit( - cmd_buf.device.queues[queue_family_index][queue_index], + queue, slice::from_ref( &vk::SubmitInfo::default() .command_buffers(slice::from_ref(&cmd_buf.handle)), @@ -3125,7 +3120,7 @@ impl Resolver { binding: dst_binding, }, (descriptor_info, _), - ) in &pipeline.descriptor_bindings + ) in &pipeline.inner.descriptor_bindings { if let DescriptorInfo::InputAttachment(_, attachment_idx) = *descriptor_info { From 75ee32e895a4d9625e585d3ebb03ad7dc14c820d Mon Sep 17 00:00:00 2001 From: John Wells Date: Sat, 21 Feb 2026 15:40:45 -0500 Subject: [PATCH 18/86] remove pluralized helper functions --- contrib/vk-graph-egui/src/lib.rs | 11 +- contrib/vk-graph-fx/src/bitmap_font.rs | 11 +- contrib/vk-graph-imgui/src/lib.rs | 11 +- contrib/vk-graph-prelude/src/lib.rs | 6 +- examples/fuzzer.rs | 29 +- examples/ray_omni.rs | 16 +- examples/ray_trace.rs | 16 +- examples/rt_triangle.rs | 16 +- src/cmd_ref/{accel.rs => accel_struct.rs} | 307 ++++------------------ src/cmd_ref/compute.rs | 14 +- src/cmd_ref/graphic.rs | 82 +----- src/cmd_ref/mod.rs | 30 ++- src/cmd_ref/pipeline.rs | 4 +- src/cmd_ref/ray_trace.rs | 16 +- src/edge.rs | 8 +- src/lib.rs | 5 +- 16 files changed, 180 insertions(+), 402 deletions(-) rename src/cmd_ref/{accel.rs => accel_struct.rs} (72%) diff --git a/contrib/vk-graph-egui/src/lib.rs b/contrib/vk-graph-egui/src/lib.rs index 208f2c93..b8ab4fc1 100644 --- a/contrib/vk-graph-egui/src/lib.rs +++ b/contrib/vk-graph-egui/src/lib.rs @@ -294,10 +294,13 @@ impl Egui { .bind_index_buffer(idx_buf, 0, vk::IndexType::UINT32) .bind_vertex_buffer(0, vert_buf, 0) .push_constants(0, cast_slice(&[push_constants])) - .set_scissor(&vk::Rect2D { - offset: vk::Offset2D { x, y }, - extent: vk::Extent2D { width, height }, - }) + .set_scissor( + 0, + &[vk::Rect2D { + offset: vk::Offset2D { x, y }, + extent: vk::Extent2D { width, height }, + }], + ) .draw_indexed(num_indices, 1, 0, 0, 0); }); } diff --git a/contrib/vk-graph-fx/src/bitmap_font.rs b/contrib/vk-graph-fx/src/bitmap_font.rs index 307524c1..913ede2a 100644 --- a/contrib/vk-graph-fx/src/bitmap_font.rs +++ b/contrib/vk-graph-fx/src/bitmap_font.rs @@ -217,10 +217,13 @@ impl BitmapFont { pass.record_pipeline(move |pipeline, _| { if let Some((x, y, width, height)) = scissor { - pipeline.set_scissor(&vk::Rect2D { - offset: vk::Offset2D { x, y }, - extent: vk::Extent2D { width, height }, - }); + pipeline.set_scissor( + 0, + &[vk::Rect2D { + offset: vk::Offset2D { x, y }, + extent: vk::Extent2D { width, height }, + }], + ); } pipeline diff --git a/contrib/vk-graph-imgui/src/lib.rs b/contrib/vk-graph-imgui/src/lib.rs index 89ba644b..6a30b4ab 100644 --- a/contrib/vk-graph-imgui/src/lib.rs +++ b/contrib/vk-graph-imgui/src/lib.rs @@ -210,10 +210,13 @@ impl ImGui { let width = (clip_rect[2] - clip_rect[0]).ceil() as u32; let height = (clip_rect[3] - clip_rect[1]).ceil() as u32; pipeline - .set_scissor(&vk::Rect2D { - offset: vk::Offset2D { x, y }, - extent: vk::Extent2D { width, height }, - }) + .set_scissor( + 0, + &[vk::Rect2D { + offset: vk::Offset2D { x, y }, + extent: vk::Extent2D { width, height }, + }], + ) .draw_indexed( index_count as _, 1, diff --git a/contrib/vk-graph-prelude/src/lib.rs b/contrib/vk-graph-prelude/src/lib.rs index 006403f7..e469d34c 100644 --- a/contrib/vk-graph-prelude/src/lib.rs +++ b/contrib/vk-graph-prelude/src/lib.rs @@ -3,7 +3,11 @@ #![warn(missing_docs)] pub use vk_graph::{ - Bind, ClearColorValue, CommandRef, Graph, Unbind, + Bind, ClearColorValue, Graph, Unbind, + cmd_ref::{ + BuildAccelerationStructureIndirectInfo, BuildAccelerationStructureInfo, CommandRef, + PipelineRef, UpdateAccelerationStructureIndirectInfo, UpdateAccelerationStructureInfo, + }, display::{Display, DisplayError, DisplayInfo, DisplayInfoBuilder, ResolverPool}, driver::{ AccessType, CommandBuffer, DriverError, diff --git a/examples/fuzzer.rs b/examples/fuzzer.rs index d8b0a5a3..ed5b1092 100644 --- a/examples/fuzzer.rs +++ b/examples/fuzzer.rs @@ -26,6 +26,7 @@ use { log::debug, rand::{Rng, rng, seq::IndexedRandom}, std::mem::size_of, + vk_graph::cmd_ref::BuildAccelerationStructureInfo, vk_graph_prelude::*, vk_graph_window::{FrameContext, WindowBuilder, WindowError}, vk_shader_macros::glsl, @@ -230,7 +231,7 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { .unwrap(), ); - blas_nodes.push((scratch_buf, blas_node)); + blas_nodes.push((blas_node, scratch_buf, blas_geometry_info.clone())); } // Lease and bind a single top-level acceleration structure @@ -275,21 +276,25 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { let mut pass = pass.read_node(index_node).read_node(vertex_node); // TODO: Like this: - for (scratch_buf, blas_node) in &blas_nodes { - pass.access_node_mut(*scratch_buf, AccessType::AccelerationStructureBufferWrite); + for (blas_node, scratch_buf, _) in &blas_nodes { pass.access_node_mut(*blas_node, AccessType::AccelerationStructureBuildWrite); + pass.access_node_mut(*scratch_buf, AccessType::AccelerationStructureBufferWrite); } // Ugly copy of the nodes that I want to figure out a way around while not being confusing let blas_nodes_copy = blas_nodes .iter() - .map(|(_, blas_node)| *blas_node) + .map(|(blas_node, _, _)| *blas_node) .collect::>(); - let mut pass = pass.record_acceleration(move |accel, bindings| { - for (scratch_buf, blas_node) in blas_nodes { - let scratch_data = Buffer::device_address(&bindings[scratch_buf]); - accel.build_structure(&blas_geometry_info, blas_node, scratch_data); + let mut pass = pass.record_accel_struct(move |accel_struct, bindings| { + for (blas_node, scratch_buf, build_data) in blas_nodes { + let scratch_addr = bindings[scratch_buf].device_address(); + accel_struct.build(&[BuildAccelerationStructureInfo::new( + blas_node, + scratch_addr, + build_data, + )]); } }); @@ -304,9 +309,13 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { ); pass.access_node_mut(tlas_node, AccessType::AccelerationStructureBuildWrite); - pass.record_acceleration(move |accel, bindings| { + pass.record_accel_struct(move |accel_struct, bindings| { let scratch_data = Buffer::device_address(&bindings[tlas_scratch_buf]); - accel.build_structure(&tlas_geometry_info, tlas_node, scratch_data); + accel_struct.build(&[BuildAccelerationStructureInfo::new( + tlas_node, + scratch_data, + tlas_geometry_info, + )]); }); } diff --git a/examples/ray_omni.rs b/examples/ray_omni.rs index 1a053d09..fbd93a09 100644 --- a/examples/ray_omni.rs +++ b/examples/ray_omni.rs @@ -213,8 +213,12 @@ fn create_blas( pass.access_node(blas, AccessType::AccelerationStructureBuildWrite) .access_node(scratch_buf, AccessType::AccelerationStructureBufferWrite) - .record_acceleration(move |accel, _| { - accel.build_structure(&info, blas, scratch_data); + .record_accel_struct(move |accel_struct, _| { + accel_struct.build(&[BuildAccelerationStructureInfo::new( + blas, + scratch_data, + info, + )]); }); let blas = render_graph.unbind_node(blas); @@ -392,8 +396,12 @@ fn create_tlas( .access_node(instance_buf, AccessType::AccelerationStructureBuildRead) .access_node(scratch_buf, AccessType::AccelerationStructureBufferWrite) .access_node(tlas, AccessType::AccelerationStructureBuildWrite) - .record_acceleration(move |accel, _| { - accel.build_structure(&info, tlas, scratch_data); + .record_accel_struct(move |accel_struct, _| { + accel_struct.build(&[BuildAccelerationStructureInfo::new( + tlas, + scratch_data, + info, + )]); }); Ok(tlas) diff --git a/examples/ray_trace.rs b/examples/ray_trace.rs index f2570b5d..c90318a1 100644 --- a/examples/ray_trace.rs +++ b/examples/ray_trace.rs @@ -693,8 +693,12 @@ fn main() -> anyhow::Result<()> { .access_node(vertex_node, AccessType::AccelerationStructureBuildRead) .access_node(scratch_buf, AccessType::AccelerationStructureBufferWrite) .access_node(blas_node, AccessType::AccelerationStructureBuildWrite) - .record_acceleration(move |accel, _| { - accel.build_structure(&blas_geometry_info, blas_node, scratch_data); + .record_accel_struct(move |accel_struct, _| { + accel_struct.build(&[BuildAccelerationStructureInfo::new( + blas_node, + scratch_data, + blas_geometry_info, + )]); }); } @@ -720,8 +724,12 @@ fn main() -> anyhow::Result<()> { .access_node(instance_node, AccessType::AccelerationStructureBuildRead) .access_node(scratch_buf, AccessType::AccelerationStructureBufferWrite) .access_node(tlas_node, AccessType::AccelerationStructureBuildWrite) - .record_acceleration(move |accel, _| { - accel.build_structure(&tlas_geometry_info, tlas_node, scratch_data); + .record_accel_struct(move |accel_struct, _| { + accel_struct.build(&[BuildAccelerationStructureInfo::new( + tlas_node, + scratch_data, + tlas_geometry_info, + )]); }); } diff --git a/examples/rt_triangle.rs b/examples/rt_triangle.rs index 92b0960b..a3ef8bd1 100644 --- a/examples/rt_triangle.rs +++ b/examples/rt_triangle.rs @@ -347,8 +347,12 @@ fn main() -> anyhow::Result<()> { .access_node(vertex_node, AccessType::AccelerationStructureBuildRead) .access_node(scratch_buf, AccessType::AccelerationStructureBufferWrite) .access_node(blas_node, AccessType::AccelerationStructureBuildWrite) - .record_acceleration(move |accel, _| { - accel.build_structure(&blas_geometry_info, blas_node, scratch_data); + .record_accel_struct(move |accel_struct, _| { + accel_struct.build(&[BuildAccelerationStructureInfo::new( + blas_node, + scratch_data, + blas_geometry_info, + )]); }); } @@ -374,8 +378,12 @@ fn main() -> anyhow::Result<()> { .access_node(instance_node, AccessType::AccelerationStructureBuildRead) .access_node(scratch_buf, AccessType::AccelerationStructureBufferWrite) .access_node(tlas_node, AccessType::AccelerationStructureBuildWrite) - .record_acceleration(move |accel, _| { - accel.build_structure(&tlas_geometry_info, tlas_node, scratch_data); + .record_accel_struct(move |accel_struct, _| { + accel_struct.build(&[BuildAccelerationStructureInfo::new( + tlas_node, + scratch_data, + tlas_geometry_info, + )]); }); } diff --git a/src/cmd_ref/accel.rs b/src/cmd_ref/accel_struct.rs similarity index 72% rename from src/cmd_ref/accel.rs rename to src/cmd_ref/accel_struct.rs index e43ae0c9..5f202e39 100644 --- a/src/cmd_ref/accel.rs +++ b/src/cmd_ref/accel_struct.rs @@ -18,7 +18,7 @@ use { /// /// This structure provides a strongly-typed set of methods which allow acceleration structures to /// be built and updated. An instance of `Acceleration` is provided to the closure parameter of -/// [`PassRef::record_acceleration`]. +/// [`PassRef::record_accel_struct`]. /// /// # Examples /// @@ -36,20 +36,23 @@ use { /// # let device = Device::new(DeviceInfo::default())?; /// # let mut my_graph = Graph::default(); /// # let info = AccelerationStructureInfo::blas(1); -/// my_graph.begin_cmd().with_name("my acceleration command") -/// .record_acceleration(move |acceleration, bindings| { -/// // During this closure we have access to the acceleration methods! +/// my_graph.begin_cmd() +/// .record_accel_struct(move |accel_struct, bindings| { +/// // During this closure we have access to the build and update methods /// }); /// # Ok(()) } /// ``` -pub struct Acceleration<'a> { +pub struct AccelerationStructureRef<'a> { pub(super) bindings: Bindings<'a>, pub(super) cmd_buf: vk::CommandBuffer, pub(super) device: &'a Device, } -impl Acceleration<'_> { - /// Build an acceleration structure. +impl AccelerationStructureRef<'_> { + /// Build acceleration structures. + /// + /// There is no ordering or synchronization implied between any of the individual acceleration + /// structure builds. /// /// Requires a scratch buffer which was created with the following requirements: /// @@ -59,13 +62,15 @@ impl Acceleration<'_> { /// of /// [`PhysicalDevice::accel_struct_properties`](crate::driver::physical_device::PhysicalDevice::accel_struct_properties). /// + /// TODO: Link to somewhere else for a full example of the scratch buffer steps + /// /// # Examples /// /// Basic usage: /// /// ```no_run - /// # use std::sync::Arc; /// # use ash::vk; + /// # use vk_graph::cmd_ref::BuildAccelerationStructureInfo; /// # use vk_graph::driver::DriverError; /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureGeometry, AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, AccelerationStructureInfo, DeviceOrHostAddress}; @@ -87,12 +92,13 @@ impl Acceleration<'_> { /// # let my_vtx_buf = Buffer::create(&device, buf_info)?; /// # let index_node = my_graph.bind_node(my_idx_buf); /// # let vertex_node = my_graph.bind_node(my_vtx_buf); - /// my_graph.begin_cmd().with_name("my acceleration pass") + /// my_graph.begin_cmd() /// .read_node(index_node) /// .read_node(vertex_node) /// .write_node(blas_node) /// .write_node(scratch_buf) - /// .record_acceleration(move |acceleration, bindings| { + /// .record_accel_struct(move |accel_struct, bindings| { + /// let scratch_addr = bindings[scratch_buf].device_address(); /// let geom = AccelerationStructureGeometry { /// max_primitive_count: 64, /// flags: vk::GeometryFlagsKHR::OPAQUE, @@ -118,120 +124,13 @@ impl Acceleration<'_> { /// }; /// let info = AccelerationStructureGeometryInfo::blas([(geom, build_range)]); /// - /// acceleration.build_structure(&info, blas_node, bindings[scratch_buf].device_address()); + /// accel_struct.build(&[ + /// BuildAccelerationStructureInfo::new(blas_node, scratch_addr, info) + /// ]); /// }); /// # Ok(()) } /// ``` - pub fn build_structure( - &self, - info: &AccelerationStructureGeometryInfo<( - AccelerationStructureGeometry, - vk::AccelerationStructureBuildRangeInfoKHR, - )>, - accel_struct: impl Into, - scratch_addr: impl Into, - ) -> &Self { - #[derive(Default)] - struct Tls { - geometries: Vec>, - ranges: Vec, - } - - thread_local! { - static TLS: RefCell = Default::default(); - } - - let accel_struct = accel_struct.into(); - let scratch_addr = scratch_addr.into().into(); - - TLS.with_borrow_mut(|tls| { - tls.geometries.clear(); - tls.ranges.clear(); - - for (geometry, range) in info.geometries.iter() { - tls.geometries.push(geometry.into()); - tls.ranges.push(*range); - } - - unsafe { - Device::expect_accel_struct_ext(self.device).cmd_build_acceleration_structures( - self.cmd_buf, - &[vk::AccelerationStructureBuildGeometryInfoKHR::default() - .ty(info.ty) - .flags(info.flags) - .mode(vk::BuildAccelerationStructureModeKHR::BUILD) - .dst_acceleration_structure(self.bindings[accel_struct].handle) - .geometries(&tls.geometries) - .scratch_data(scratch_addr)], - &[&tls.ranges], - ); - } - }); - - self - } - - /// Build an acceleration structure with some parameters provided on the device. - /// - /// `range` is a buffer device address which points to `info.geometry.len()` - /// [vk::VkAccelerationStructureBuildRangeInfoKHR] structures defining dynamic offsets to the - /// addresses where geometry data is stored, as defined by `info`. - pub fn build_structure_indirect( - &self, - info: &AccelerationStructureGeometryInfo, - accel_struct: impl Into, - scratch_addr: impl Into, - range_base: vk::DeviceAddress, - range_stride: u32, - ) -> &Self { - #[derive(Default)] - struct Tls { - geometries: Vec>, - max_primitive_counts: Vec, - } - - thread_local! { - static TLS: RefCell = Default::default(); - } - - let accel_struct = accel_struct.into(); - let scratch_addr = scratch_addr.into().into(); - - TLS.with_borrow_mut(|tls| { - tls.geometries.clear(); - tls.max_primitive_counts.clear(); - - for geometry in info.geometries.iter() { - tls.geometries.push(geometry.into()); - tls.max_primitive_counts.push(geometry.max_primitive_count); - } - - unsafe { - Device::expect_accel_struct_ext(self.device) - .cmd_build_acceleration_structures_indirect( - self.cmd_buf, - &[vk::AccelerationStructureBuildGeometryInfoKHR::default() - .ty(info.ty) - .flags(info.flags) - .mode(vk::BuildAccelerationStructureModeKHR::BUILD) - .dst_acceleration_structure(self.bindings[accel_struct].handle) - .geometries(&tls.geometries) - .scratch_data(scratch_addr)], - &[range_base], - &[range_stride], - &[&tls.max_primitive_counts], - ); - } - }); - - self - } - - /// Build acceleration structures. - /// - /// There is no ordering or synchronization implied between any of the individual acceleration - /// structure builds. - pub fn build_structures(&self, infos: &[AccelerationStructureBuildInfo]) -> &Self { + pub fn build(&self, infos: &[BuildAccelerationStructureInfo]) -> &Self { #[derive(Default)] struct Tls { geometries: Vec>, @@ -308,11 +207,10 @@ impl Acceleration<'_> { /// There is no ordering or synchronization implied between any of the individual acceleration /// structure builds. /// - /// See [Self::build_structure_indirect] - pub fn build_structures_indirect( - &self, - infos: &[AccelerationStructureIndirectBuildInfo], - ) -> &Self { + /// `range` is a buffer device address which points to `info.geometry.len()` + /// [vk::VkAccelerationStructureBuildRangeInfoKHR] structures defining dynamic offsets to the + /// addresses where geometry data is stored, as defined by `info`. + pub fn build_indirect(&self, infos: &[BuildAccelerationStructureIndirectInfo]) -> &Self { #[derive(Default)] struct Tls { geometries: Vec>, @@ -386,7 +284,10 @@ impl Acceleration<'_> { self } - /// Update an acceleration structure. + /// Update acceleration structures. + /// + /// There is no ordering or synchronization implied between any of the individual acceleration + /// structure updates. /// /// Requires a scratch buffer which was created with the following requirements: /// @@ -395,122 +296,7 @@ impl Acceleration<'_> { /// [`AccelerationStructure::size_of`] aligned to `min_accel_struct_scratch_offset_alignment` /// of /// [`PhysicalDevice::accel_struct_properties`](crate::driver::physical_device::PhysicalDevice::accel_struct_properties). - pub fn update_structure( - &self, - info: &AccelerationStructureGeometryInfo<( - AccelerationStructureGeometry, - vk::AccelerationStructureBuildRangeInfoKHR, - )>, - src_accel_struct: impl Into, - dst_accel_struct: impl Into, - scratch_addr: impl Into, - ) -> &Self { - #[derive(Default)] - struct Tls { - geometries: Vec>, - ranges: Vec, - } - - thread_local! { - static TLS: RefCell = Default::default(); - } - - let src_accel_struct = src_accel_struct.into(); - let dst_accel_struct = dst_accel_struct.into(); - let scratch_addr = scratch_addr.into().into(); - - TLS.with_borrow_mut(|tls| { - tls.geometries.clear(); - tls.ranges.clear(); - - for (geometry, range) in info.geometries.iter() { - tls.geometries.push(geometry.into()); - tls.ranges.push(*range); - } - - unsafe { - Device::expect_accel_struct_ext(self.device).cmd_build_acceleration_structures( - self.cmd_buf, - &[vk::AccelerationStructureBuildGeometryInfoKHR::default() - .ty(info.ty) - .flags(info.flags) - .mode(vk::BuildAccelerationStructureModeKHR::UPDATE) - .dst_acceleration_structure(self.bindings[dst_accel_struct].handle) - .src_acceleration_structure(self.bindings[src_accel_struct].handle) - .geometries(&tls.geometries) - .scratch_data(scratch_addr)], - &[&tls.ranges], - ); - } - }); - - self - } - - /// Update an acceleration structure with some parameters provided on the device. - /// - /// `range` is a buffer device address which points to `info.geometry.len()` - /// [vk::VkAccelerationStructureBuildRangeInfoKHR] structures defining dynamic offsets to the - /// addresses where geometry data is stored, as defined by `info`. - pub fn update_structure_indirect( - &self, - info: &AccelerationStructureGeometryInfo, - src_accel_struct: impl Into, - dst_accel_struct: impl Into, - scratch_addr: impl Into, - range_base: vk::DeviceAddress, - range_stride: u32, - ) -> &Self { - #[derive(Default)] - struct Tls { - geometries: Vec>, - max_primitive_counts: Vec, - } - - thread_local! { - static TLS: RefCell = Default::default(); - } - - let src_accel_struct = src_accel_struct.into(); - let dst_accel_struct = dst_accel_struct.into(); - let scratch_addr = scratch_addr.into().into(); - - TLS.with_borrow_mut(|tls| { - tls.geometries.clear(); - tls.max_primitive_counts.clear(); - - for geometry in info.geometries.iter() { - tls.geometries.push(geometry.into()); - tls.max_primitive_counts.push(geometry.max_primitive_count); - } - - unsafe { - Device::expect_accel_struct_ext(self.device) - .cmd_build_acceleration_structures_indirect( - self.cmd_buf, - &[vk::AccelerationStructureBuildGeometryInfoKHR::default() - .ty(info.ty) - .flags(info.flags) - .mode(vk::BuildAccelerationStructureModeKHR::UPDATE) - .src_acceleration_structure(self.bindings[src_accel_struct].handle) - .dst_acceleration_structure(self.bindings[dst_accel_struct].handle) - .geometries(&tls.geometries) - .scratch_data(scratch_addr)], - &[range_base], - &[range_stride], - &[&tls.max_primitive_counts], - ); - } - }); - - self - } - - /// Update acceleration structures. - /// - /// There is no ordering or synchronization implied between any of the individual acceleration - /// structure updates. - pub fn update_structures(&self, infos: &[AccelerationStructureUpdateInfo]) -> &Self { + pub fn update(&self, infos: &[UpdateAccelerationStructureInfo]) -> &Self { #[derive(Default)] struct Tls { geometries: Vec>, @@ -588,11 +374,10 @@ impl Acceleration<'_> { /// There is no ordering or synchronization implied between any of the individual acceleration /// structure updates. /// - /// See [Self::update_structure_indirect] - pub fn update_structures_indirect( - &self, - infos: &[AccelerationStructureIndirectUpdateInfo], - ) -> &Self { + /// `range` is a buffer device address which points to `info.geometry.len()` + /// [vk::VkAccelerationStructureBuildRangeInfoKHR] structures defining dynamic offsets to the + /// addresses where geometry data is stored, as defined by `info`. + pub fn update_indirect(&self, infos: &[UpdateAccelerationStructureIndirectInfo]) -> &Self { #[derive(Default)] struct Tls { geometries: Vec>, @@ -674,7 +459,7 @@ impl Acceleration<'_> { /// [VkAccelerationStructureBuildGeometryInfoKHR](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkAccelerationStructureBuildGeometryInfoKHR.html) /// for more information. #[derive(Clone, Debug)] -pub struct AccelerationStructureBuildInfo { +pub struct BuildAccelerationStructureInfo { /// The acceleration structure to be written. pub accel_struct: AnyAccelerationStructureNode, @@ -689,15 +474,15 @@ pub struct AccelerationStructureBuildInfo { pub scratch_addr: DeviceOrHostAddress, } -impl AccelerationStructureBuildInfo { +impl BuildAccelerationStructureInfo { /// Constructs new acceleration structure build information. pub fn new( accel_struct: impl Into, + scratch_addr: impl Into, build_data: AccelerationStructureGeometryInfo<( AccelerationStructureGeometry, vk::AccelerationStructureBuildRangeInfoKHR, )>, - scratch_addr: impl Into, ) -> Self { let accel_struct = accel_struct.into(); let scratch_addr = scratch_addr.into(); @@ -717,7 +502,7 @@ impl AccelerationStructureBuildInfo { /// [VkAccelerationStructureBuildGeometryInfoKHR](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkAccelerationStructureBuildGeometryInfoKHR.html) /// for more information. #[derive(Clone, Debug)] -pub struct AccelerationStructureIndirectBuildInfo { +pub struct BuildAccelerationStructureIndirectInfo { /// The acceleration structure to be written. pub accel_struct: AnyAccelerationStructureNode, @@ -737,15 +522,14 @@ pub struct AccelerationStructureIndirectBuildInfo { pub scratch_data: DeviceOrHostAddress, } -impl AccelerationStructureIndirectBuildInfo { +impl BuildAccelerationStructureIndirectInfo { /// Constructs new acceleration structure indirect build information. pub fn new( accel_struct: impl Into, + scratch_data: impl Into, build_data: AccelerationStructureGeometryInfo, range_base: vk::DeviceAddress, - range_stride: u32, - scratch_data: impl Into, ) -> Self { let accel_struct = accel_struct.into(); let scratch_data = scratch_data.into(); @@ -767,7 +551,7 @@ impl AccelerationStructureIndirectBuildInfo { /// [VkAccelerationStructureBuildGeometryInfoKHR](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkAccelerationStructureBuildGeometryInfoKHR.html) /// for more information. #[derive(Clone, Debug)] -pub struct AccelerationStructureIndirectUpdateInfo { +pub struct UpdateAccelerationStructureIndirectInfo { /// The acceleration structure to be written. pub dst_accel_struct: AnyAccelerationStructureNode, @@ -790,16 +574,15 @@ pub struct AccelerationStructureIndirectUpdateInfo { pub update_data: AccelerationStructureGeometryInfo, } -impl AccelerationStructureIndirectUpdateInfo { +impl UpdateAccelerationStructureIndirectInfo { /// Constructs new acceleration structure indirect update information. pub fn new( src_accel_struct: impl Into, dst_accel_struct: impl Into, + scratch_addr: impl Into, update_data: AccelerationStructureGeometryInfo, range_base: vk::DeviceAddress, - range_stride: u32, - scratch_addr: impl Into, ) -> Self { let src_accel_struct = src_accel_struct.into(); let dst_accel_struct = dst_accel_struct.into(); @@ -822,7 +605,7 @@ impl AccelerationStructureIndirectUpdateInfo { /// [VkAccelerationStructureBuildGeometryInfoKHR](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkAccelerationStructureBuildGeometryInfoKHR.html) /// for more information. #[derive(Clone, Debug)] -pub struct AccelerationStructureUpdateInfo { +pub struct UpdateAccelerationStructureInfo { /// The acceleration structure to be written. pub dst_accel_struct: AnyAccelerationStructureNode, @@ -840,16 +623,16 @@ pub struct AccelerationStructureUpdateInfo { )>, } -impl AccelerationStructureUpdateInfo { +impl UpdateAccelerationStructureInfo { /// Constructs new acceleration structure update information. pub fn new( src_accel_struct: impl Into, dst_accel_struct: impl Into, + scratch_addr: impl Into, update_data: AccelerationStructureGeometryInfo<( AccelerationStructureGeometry, vk::AccelerationStructureBuildRangeInfoKHR, )>, - scratch_addr: impl Into, ) -> Self { let src_accel_struct = src_accel_struct.into(); let dst_accel_struct = dst_accel_struct.into(); diff --git a/src/cmd_ref/compute.rs b/src/cmd_ref/compute.rs index 49aaf16a..29020da4 100644 --- a/src/cmd_ref/compute.rs +++ b/src/cmd_ref/compute.rs @@ -1,5 +1,5 @@ use { - super::{Bindings, pipeline::PipelineCommandRef}, + super::{Bindings, pipeline::PipelineRef}, crate::{ AnyBufferNode, driver::{compute::ComputePipeline, device::Device}, @@ -39,14 +39,14 @@ use { /// }); /// # Ok(()) } /// ``` -pub struct Compute<'a> { +pub struct ComputePipelineRef<'a> { pub(super) bindings: Bindings<'a>, pub(super) cmd_buf: vk::CommandBuffer, pub(super) device: &'a Device, pub(super) pipeline: ComputePipeline, } -impl Compute<'_> { +impl ComputePipelineRef<'_> { /// [Dispatch] compute work items. /// /// When the command is executed, a global workgroup consisting of @@ -311,11 +311,11 @@ impl Compute<'_> { } // NOTE: local implementation of type from super module -impl PipelineCommandRef<'_, ComputePipeline> { - /// Begin recording a computing command buffer. +impl PipelineRef<'_, ComputePipeline> { + /// Begin recording a compute pipeline command buffer. pub fn record_pipeline( mut self, - func: impl FnOnce(Compute<'_>, Bindings<'_>) + Send + 'static, + func: impl FnOnce(ComputePipelineRef<'_>, Bindings<'_>) + Send + 'static, ) -> Self { let pipeline = self .cmd @@ -331,7 +331,7 @@ impl PipelineCommandRef<'_, ComputePipeline> { self.cmd.push_execute(move |device, cmd_buf, bindings| { func( - Compute { + ComputePipelineRef { bindings, cmd_buf, device, diff --git a/src/cmd_ref/graphic.rs b/src/cmd_ref/graphic.rs index 15fead4c..99b36b97 100644 --- a/src/cmd_ref/graphic.rs +++ b/src/cmd_ref/graphic.rs @@ -1,5 +1,5 @@ use { - super::{AttachmentIndex, Bindings, Info, PipelineCommandRef, Subresource, SubresourceAccess}, + super::{AttachmentIndex, Bindings, Info, PipelineRef, Subresource, SubresourceAccess}, crate::{ AnyBufferNode, AnyImageNode, Area, Attachment, ClearColorValue, Node, NodeIndex, SampleCount, @@ -22,7 +22,7 @@ use { /// /// This structure provides a strongly-typed set of methods which allow rasterization shader code to /// be executed. An instance of `Draw` is provided to the closure parameter of -/// [`PipelineCommandRef::record_pipeline`] which may be accessed by binding a [`GraphicPipeline`] to a +/// [`PipelineRef::record_pipeline`] which may be accessed by binding a [`GraphicPipeline`] to a /// render pass. /// /// # Examples @@ -56,14 +56,14 @@ use { /// }); /// # Ok(()) } /// ``` -pub struct Graphic<'a> { +pub struct GraphicPipelineRef<'a> { pub(super) bindings: Bindings<'a>, pub(super) cmd_buf: vk::CommandBuffer, pub(super) device: &'a Device, pub(super) pipeline: GraphicPipeline, } -impl Graphic<'_> { +impl GraphicPipelineRef<'_> { /// Bind an index buffer to the current pass. /// /// `offset` is the starting offset in bytes within `buffer` used in index buffer address @@ -581,85 +581,31 @@ impl Graphic<'_> { self } - /// Set scissor rectangle dynamically for a pass. + /// Set scissor rectangle dynamically for the current command. #[profiling::function] - pub fn set_scissor(&self, scissor: &vk::Rect2D) -> &Self { + pub fn set_scissor(&self, first_scissor: u32, scissors: &[vk::Rect2D]) -> &Self { unsafe { self.device - .cmd_set_scissor(self.cmd_buf, 0, slice::from_ref(scissor)); + .cmd_set_scissor(self.cmd_buf, first_scissor, scissors); } self } - /// Set scissor rectangles dynamically for a pass. + /// Set the viewport dynamically for the current command. #[profiling::function] - pub fn set_scissors( - &self, - first_scissor: u32, - scissors: impl IntoIterator, - ) -> &Self - where - S: Into, - { - thread_local! { - static TLS: RefCell> = Default::default(); - } - - TLS.with_borrow_mut(|tls| { - tls.clear(); - tls.extend(scissors.into_iter().map(Into::into)); - - unsafe { - self.device - .cmd_set_scissor(self.cmd_buf, first_scissor, tls); - } - }); - - self - } - - /// Set the viewport dynamically for a pass. - #[profiling::function] - pub fn set_viewport(&self, viewport: &vk::Viewport) -> &Self { + pub fn set_viewport(&self, first_viewport: u32, viewports: &[vk::Viewport]) -> &Self { unsafe { self.device - .cmd_set_viewport(self.cmd_buf, 0, slice::from_ref(viewport)); + .cmd_set_viewport(self.cmd_buf, first_viewport, viewports); } self } - - /// Set the viewports dynamically for a pass. - #[profiling::function] - pub fn set_viewports( - &self, - first_viewport: u32, - viewports: impl IntoIterator, - ) -> &Self - where - V: Into, - { - thread_local! { - static TLS: RefCell> = Default::default(); - } - - TLS.with_borrow_mut(|tls| { - tls.clear(); - tls.extend(viewports.into_iter().map(Into::into)); - - unsafe { - self.device - .cmd_set_viewport(self.cmd_buf, first_viewport, tls); - } - }); - - self - } } // NOTE: local implementation of type from super module -impl PipelineCommandRef<'_, GraphicPipeline> { +impl PipelineRef<'_, GraphicPipeline> { /// Specifies `VK_ATTACHMENT_LOAD_OP_DONT_CARE` for the render pass attachment, and loads an /// image into the framebuffer. pub fn attach_color( @@ -1519,10 +1465,10 @@ impl PipelineCommandRef<'_, GraphicPipeline> { self } - /// Begin recording a graphics command buffer. + /// Begin recording a graphics pipeline command buffer. pub fn record_pipeline( mut self, - func: impl FnOnce(Graphic<'_>, Bindings<'_>) + Send + 'static, + func: impl FnOnce(GraphicPipelineRef<'_>, Bindings<'_>) + Send + 'static, ) -> Self { let pipeline = self .cmd @@ -1538,7 +1484,7 @@ impl PipelineCommandRef<'_, GraphicPipeline> { self.cmd.push_execute(move |device, cmd_buf, bindings| { func( - Graphic { + GraphicPipelineRef { bindings, cmd_buf, device, diff --git a/src/cmd_ref/mod.rs b/src/cmd_ref/mod.rs index bbd4ff23..89371355 100644 --- a/src/cmd_ref/mod.rs +++ b/src/cmd_ref/mod.rs @@ -1,6 +1,6 @@ //! Strongly-typed rendering commands. -mod accel; +mod accel_struct; mod compute; mod graphic; mod pipeline; @@ -8,12 +8,16 @@ mod ray_trace; mod view; pub use self::{ - pipeline::PipelineCommandRef, + accel_struct::{ + AccelerationStructureRef, BuildAccelerationStructureIndirectInfo, + BuildAccelerationStructureInfo, UpdateAccelerationStructureIndirectInfo, + UpdateAccelerationStructureInfo, + }, + pipeline::PipelineRef, view::{View, ViewType}, }; use { - self::accel::Acceleration, super::{ AccelerationStructureLeaseNode, AccelerationStructureNode, AnyAccelerationStructureNode, AnyBufferNode, AnyImageNode, Bind, Binding, BufferLeaseNode, BufferNode, Command, Edge, @@ -74,9 +78,9 @@ impl Access for RayTracePipeline { macro_rules! bind { ($name:ident) => { paste::paste! { - impl<'a> Bind, PipelineCommandRef<'a, [<$name Pipeline>]>> for &'a [<$name Pipeline>] { + impl<'a> Bind, PipelineRef<'a, [<$name Pipeline>]>> for &'a [<$name Pipeline>] { // TODO: Allow binding as explicit secondary command buffers? like with compute/raytrace stuff - fn bind(self, mut cmd: CommandRef<'a>) -> PipelineCommandRef<'a, [<$name Pipeline>]> { + fn bind(self, mut cmd: CommandRef<'a>) -> PipelineRef<'a, [<$name Pipeline>]> { let cmd_ref = cmd.as_mut(); if cmd_ref.execs.last().unwrap().pipeline.is_some() { // Binding from PipelinePass -> PipelinePass (changing shaders) @@ -85,16 +89,16 @@ macro_rules! bind { cmd_ref.execs.last_mut().unwrap().pipeline = Some(ExecutionPipeline::$name(self.clone())); - PipelineCommandRef { + PipelineRef { __: PhantomData, cmd, } } } - impl<'a> Bind, PipelineCommandRef<'a, [<$name Pipeline>]>> for [<$name Pipeline>] { + impl<'a> Bind, PipelineRef<'a, [<$name Pipeline>]>> for [<$name Pipeline>] { // TODO: Allow binding as explicit secondary command buffers? like with compute/raytrace stuff - fn bind(self, mut cmd: CommandRef<'a>) -> PipelineCommandRef<'a, [<$name Pipeline>]> { + fn bind(self, mut cmd: CommandRef<'a>) -> PipelineRef<'a, [<$name Pipeline>]> { let cmd_ref = cmd.as_mut(); if cmd_ref.execs.last().unwrap().pipeline.is_some() { // Binding from PipelinePass -> PipelinePass (changing shaders) @@ -103,7 +107,7 @@ macro_rules! bind { cmd_ref.execs.last_mut().unwrap().pipeline = Some(ExecutionPipeline::$name(self)); - PipelineCommandRef { + PipelineRef { __: PhantomData, cmd, } @@ -139,7 +143,7 @@ bind!(RayTrace); /// /// This type is available while recording commands in the following closures: /// -/// - [`PassRef::record_acceleration`] for building and updating acceleration structures +/// - [`PassRef::record_accel_struct`] for building and updating acceleration structures /// - [`PassRef::record_cmd_buf`] for general command streams /// - [`PipelineCommandRef::record_pipeline`] for dispatched compute operations /// - [`PipelineCommandRef::record_pipeline`] for raster drawing operations, such as triangles streams @@ -487,13 +491,13 @@ impl<'a> CommandRef<'a> { /// Begin recording an acceleration structure command buffer. /// /// This is the entry point for building and updating an [`AccelerationStructure`] instance. - pub fn record_acceleration( + pub fn record_accel_struct( mut self, - func: impl FnOnce(Acceleration<'_>, Bindings<'_>) + Send + 'static, + func: impl FnOnce(AccelerationStructureRef<'_>, Bindings<'_>) + Send + 'static, ) -> Self { self.push_execute(move |device, cmd_buf, bindings| { func( - Acceleration { + AccelerationStructureRef { bindings, cmd_buf, device, diff --git a/src/cmd_ref/pipeline.rs b/src/cmd_ref/pipeline.rs index b4d1c309..96fc048f 100644 --- a/src/cmd_ref/pipeline.rs +++ b/src/cmd_ref/pipeline.rs @@ -7,13 +7,13 @@ use { }; /// A render pass which has been bound to a particular compute, graphic, or ray-trace pipeline. -pub struct PipelineCommandRef<'a, T> { +pub struct PipelineRef<'a, T> { pub(super) __: PhantomData, pub(super) cmd: CommandRef<'a>, } // NOTE: There are specific implementations of T in the compute, graphic, and ray trace modules -impl<'a, T> PipelineCommandRef<'a, T> +impl<'a, T> PipelineRef<'a, T> where T: Access, { diff --git a/src/cmd_ref/ray_trace.rs b/src/cmd_ref/ray_trace.rs index e5fcca15..4fbc2106 100644 --- a/src/cmd_ref/ray_trace.rs +++ b/src/cmd_ref/ray_trace.rs @@ -1,16 +1,16 @@ use { - super::{Bindings, PipelineCommandRef}, + super::{Bindings, PipelineRef}, crate::driver::{device::Device, ray_trace::RayTracePipeline}, ash::vk, log::trace, }; // NOTE: local implementation of type from super module -impl PipelineCommandRef<'_, RayTracePipeline> { - /// Begin recording a ray tracing command buffer. +impl PipelineRef<'_, RayTracePipeline> { + /// Begin recording a ray trace pipeline command buffer. pub fn record_pipeline( mut self, - func: impl FnOnce(RayTrace<'_>, Bindings<'_>) + Send + 'static, + func: impl FnOnce(RayTracePipelineRef<'_>, Bindings<'_>) + Send + 'static, ) -> Self { let pipeline = self .cmd @@ -29,7 +29,7 @@ impl PipelineCommandRef<'_, RayTracePipeline> { self.cmd.push_execute(move |device, cmd_buf, bindings| { func( - RayTrace { + RayTracePipelineRef { cmd_buf, device, @@ -50,7 +50,7 @@ impl PipelineCommandRef<'_, RayTracePipeline> { /// /// This structure provides a strongly-typed set of methods which allow ray trace shader code to be /// executed. An instance of `RayTrace` is provided to the closure parameter of -/// [`PipelineCommandRef::record_pipeline`] which may be accessed by binding a [`RayTracePipeline`] to +/// [`PipelineRef::record_pipeline`] which may be accessed by binding a [`RayTracePipeline`] to /// a render pass. /// /// # Examples @@ -80,7 +80,7 @@ impl PipelineCommandRef<'_, RayTracePipeline> { /// }); /// # Ok(()) } /// ``` -pub struct RayTrace<'a> { +pub struct RayTracePipelineRef<'a> { cmd_buf: vk::CommandBuffer, device: &'a Device, @@ -90,7 +90,7 @@ pub struct RayTrace<'a> { pipeline: RayTracePipeline, } -impl RayTrace<'_> { +impl RayTracePipelineRef<'_> { /// Updates push constants. /// /// Push constants represent a high speed path to modify constant data in pipelines that is diff --git a/src/edge.rs b/src/edge.rs index 51c6094e..2598f569 100644 --- a/src/edge.rs +++ b/src/edge.rs @@ -2,7 +2,7 @@ use { super::{ AccelerationStructureLeaseNode, AccelerationStructureNode, BufferLeaseNode, BufferNode, Graph, ImageLeaseNode, ImageNode, Resolver, SwapchainImageNode, - cmd_ref::{CommandRef, PipelineCommandRef}, + cmd_ref::{CommandRef, PipelineRef}, }, crate::{ driver::{ @@ -72,16 +72,16 @@ node_ref!(Arc => ImageNode); node_ref!(Arc> => ImageLeaseNode); // Specialized edges for pipelines added to a pass: -// Ex: PassRef::bind_pipeline(&mut self, pipeline: X) -> PipelineCommandRef +// Ex: PassRef::bind_pipeline(&mut self, pipeline: X) -> PipelineRef macro_rules! pipeline { ($name:ident) => { paste::paste! { impl<'a> Edge> for &'a [<$name Pipeline>] { - type Result = PipelineCommandRef<'a, [<$name Pipeline>]>; + type Result = PipelineRef<'a, [<$name Pipeline>]>; } impl<'a> Edge> for [<$name Pipeline>] { - type Result = PipelineCommandRef<'a, [<$name Pipeline>]>; + type Result = PipelineRef<'a, [<$name Pipeline>]>; } } }; diff --git a/src/lib.rs b/src/lib.rs index d25ed218..11ae466c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -349,27 +349,26 @@ layout. #![warn(missing_docs)] +pub mod cmd_ref; pub mod display; pub mod driver; pub mod node; pub mod pool; mod bind; -mod cmd_ref; mod edge; mod info; mod resolver; pub use self::{ bind::{Bind, Unbind}, - cmd_ref::CommandRef, resolver::Resolver, }; use { self::{ bind::Binding, - cmd_ref::{AttachmentIndex, Bindings, Descriptor, SubresourceAccess, ViewType}, + cmd_ref::{AttachmentIndex, Bindings, CommandRef, Descriptor, SubresourceAccess, ViewType}, edge::Edge, info::Info, node::Node, From e114739f0b365629edc8ecd65a2cd69d89ddcec6 Mon Sep 17 00:00:00 2001 From: John Wells Date: Sun, 22 Feb 2026 06:15:46 -0500 Subject: [PATCH 19/86] Bindings -> Nodes --- contrib/vk-graph-egui/src/lib.rs | 38 +-- contrib/vk-graph-fx/src/image_loader.rs | 19 +- contrib/vk-graph-fx/src/transition.rs | 23 +- contrib/vk-graph-hot/examples/glsl.rs | 2 +- contrib/vk-graph-hot/examples/hlsl.rs | 2 +- contrib/vk-graph-imgui/src/lib.rs | 28 +- .../vk-graph-window/examples/hello_world.rs | 2 +- contrib/vk-graph-window/src/frame.rs | 2 +- contrib/vk-graph-window/src/lib.rs | 8 +- examples/aliasing.rs | 10 +- examples/app.rs | 8 +- examples/bindless.rs | 4 +- examples/cpu_readback.rs | 14 +- examples/debugger.rs | 8 +- examples/egui.rs | 8 +- examples/font_bmp.rs | 8 +- examples/fuzzer.rs | 172 ++++------ examples/image_sampler.rs | 16 +- examples/imgui.rs | 13 +- examples/min_max.rs | 40 +-- examples/mip_compute.rs | 20 +- examples/mip_graphic.rs | 16 +- examples/msaa.rs | 6 +- examples/multipass.rs | 24 +- examples/multithread.rs | 32 +- examples/ray_omni.rs | 48 ++- examples/ray_trace.rs | 50 +-- examples/rt_triangle.rs | 34 +- examples/shader-toy/src/main.rs | 52 ++- examples/skeletal-anim/src/main.rs | 44 ++- examples/subgroup_ops.rs | 18 +- examples/transitions.rs | 22 +- examples/triangle.rs | 6 +- examples/vertex_layout.rs | 4 +- examples/vr/src/main.rs | 58 ++-- examples/vsm_omni.rs | 40 +-- src/cmd_ref/accel_struct.rs | 26 +- src/cmd_ref/compute.rs | 22 +- src/cmd_ref/graphic.rs | 40 +-- src/cmd_ref/mod.rs | 310 +++++++++--------- src/cmd_ref/ray_trace.rs | 14 +- src/display.rs | 6 +- src/lib.rs | 60 ++-- src/resolver.rs | 10 +- 44 files changed, 663 insertions(+), 724 deletions(-) diff --git a/contrib/vk-graph-egui/src/lib.rs b/contrib/vk-graph-egui/src/lib.rs index b8ab4fc1..8e44efdb 100644 --- a/contrib/vk-graph-egui/src/lib.rs +++ b/contrib/vk-graph-egui/src/lib.rs @@ -89,7 +89,7 @@ impl Egui { fn bind_and_update_textures( &mut self, deltas: &egui::TexturesDelta, - render_graph: &mut Graph, + graph: &mut Graph, ) -> HashMap { let mut bound_tex = deltas .set @@ -114,7 +114,7 @@ impl Egui { )) .unwrap(); Buffer::copy_from_slice(&mut buf, 0, cast_slice(&pixels)); - render_graph.bind_node(buf) + graph.bind_node(buf) }; if let Some(pos) = delta.pos { @@ -122,10 +122,10 @@ impl Egui { self.textures .remove(id) .expect("Tried updating undefined texture.") - .bind(render_graph), + .bind(graph), ); - render_graph.copy_buffer_to_image_region( + graph.copy_buffer_to_image_region( tmp_buf, image, vk::BufferImageCopy { @@ -161,11 +161,11 @@ impl Egui { vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST, )) .unwrap() - .bind(render_graph), + .bind(graph), ); - render_graph.copy_buffer_to_image(tmp_buf, image); - render_graph.unbind_node(tmp_buf); + graph.copy_buffer_to_image(tmp_buf, image); + graph.unbind_node(tmp_buf); (*id, image) } }) @@ -173,7 +173,7 @@ impl Egui { // Bind the rest of the textures. for (id, image) in self.textures.drain() { - bound_tex.insert(id, AnyImageNode::ImageLease(render_graph.bind_node(image))); + bound_tex.insert(id, AnyImageNode::ImageLease(graph.bind_node(image))); } // Add user textures. @@ -187,14 +187,14 @@ impl Egui { fn unbind_and_free( &mut self, bound_tex: HashMap, - render_graph: &mut Graph, + graph: &mut Graph, deltas: &egui::TexturesDelta, ) { // Unbind textures for (id, tex) in bound_tex.iter() { if let AnyImageNode::ImageLease(tex) = tex { if let egui::TextureId::Managed(_) = *id { - self.textures.insert(*id, render_graph.unbind_node(*tex)); + self.textures.insert(*id, graph.unbind_node(*tex)); } } } @@ -211,11 +211,11 @@ impl Egui { &mut self, shapes: Vec, bound_tex: &HashMap, - render_graph: &mut Graph, + graph: &mut Graph, target: impl Into, ) { let target = target.into(); - let target_info = render_graph.node_info(target); + let target_info = graph.node_info(target); for egui::ClippedPrimitive { clip_rect, primitive, @@ -241,7 +241,7 @@ impl Egui { Buffer::copy_from_slice(&mut buf, 0, cast_slice(&mesh.indices)); buf }; - let idx_buf = render_graph.bind_node(idx_buf); + let idx_buf = graph.bind_node(idx_buf); let vert_buf = { let mut buf = self @@ -255,7 +255,7 @@ impl Egui { Buffer::copy_from_slice(&mut buf, 0, cast_slice(&mesh.vertices)); buf }; - let vert_buf = render_graph.bind_node(vert_buf); + let vert_buf = graph.bind_node(vert_buf); #[repr(C)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] @@ -280,7 +280,7 @@ impl Egui { let width = ((clip_rect.max.x - clip_rect.min.x) * pixels_per_point) as u32; let height = ((clip_rect.max.y - clip_rect.min.y) * pixels_per_point) as u32; - render_graph + graph .begin_cmd() .with_name("Egui pass") .bind_pipeline(&self.ppl) @@ -315,7 +315,7 @@ impl Egui { window: &Window, events: &[Event<()>], target: impl Into, - render_graph: &mut Graph, + graph: &mut Graph, ui_fn: impl FnMut(&egui::Context), ) { // Update events and generate shapes and texture deltas. @@ -335,11 +335,11 @@ impl Egui { let deltas = full_output.textures_delta; - let bound_tex = self.bind_and_update_textures(&deltas, render_graph); + let bound_tex = self.bind_and_update_textures(&deltas, graph); - self.draw_primitive(full_output.shapes, &bound_tex, render_graph, target); + self.draw_primitive(full_output.shapes, &bound_tex, graph, target); - self.unbind_and_free(bound_tex, render_graph, &deltas); + self.unbind_and_free(bound_tex, graph, &deltas); } /// TODO diff --git a/contrib/vk-graph-fx/src/image_loader.rs b/contrib/vk-graph-fx/src/image_loader.rs index 4fce830e..469abaf4 100644 --- a/contrib/vk-graph-fx/src/image_loader.rs +++ b/contrib/vk-graph-fx/src/image_loader.rs @@ -144,9 +144,8 @@ impl ImageLoader { warn!("unused data"); } - let mut render_graph = Graph::default(); - let image = - render_graph.bind_node(self.create_image(format, width, height, is_srgb, false)?); + let mut graph = Graph::default(); + let image = graph.bind_node(self.create_image(format, width, height, is_srgb, false)?); // Fill the image from the temporary buffer match format { @@ -191,19 +190,19 @@ impl ImageLoader { } } - let pixel_buf = render_graph.bind_node(pixel_buf); + let pixel_buf = graph.bind_node(pixel_buf); // We create a temporary storage image because SRGB support isn't wide enough to // have SRGB storage images directly let temp_image = - render_graph.bind_node(self.create_image(format, width, height, false, true)?); + graph.bind_node(self.create_image(format, width, height, false, true)?); // Copy host-local data in the buffer to the temporary buffer on the GPU and then // use a compute shader to decode it before copying it over the output image let dispatch_x = (width + 3) >> 2; let dispatch_y = height; - render_graph + graph .begin_cmd() .with_name("Decode RGB image") .bind_pipeline(&self.decode_rgb_rgba) @@ -230,14 +229,14 @@ impl ImageLoader { pixel_buf.copy_from_slice(pixels); } - let pixel_buf = render_graph.bind_node(pixel_buf); - render_graph.copy_buffer_to_image(pixel_buf, image); + let pixel_buf = graph.bind_node(pixel_buf); + graph.copy_buffer_to_image(pixel_buf, image); } } - let image = render_graph.unbind_node(image); + let image = graph.unbind_node(image); - render_graph + graph .resolve() .submit(&mut self.pool, queue_family_index, queue_index)?; diff --git a/contrib/vk-graph-fx/src/transition.rs b/contrib/vk-graph-fx/src/transition.rs index 7fc06b2b..c1ace1b0 100644 --- a/contrib/vk-graph-fx/src/transition.rs +++ b/contrib/vk-graph-fx/src/transition.rs @@ -401,7 +401,7 @@ impl TransitionPipeline { /// TODO pub fn apply( &mut self, - render_graph: &mut Graph, + graph: &mut Graph, a_image: impl Into, b_image: impl Into, transition: Transition, @@ -410,8 +410,8 @@ impl TransitionPipeline { let a_image = a_image.into(); let b_image = b_image.into(); - let a_info = render_graph.node_info(a_image); - let b_info = render_graph.node_info(b_image); + let a_info = graph.node_info(a_image); + let b_info = graph.node_info(b_image); let dest_info = ImageInfo::image_2d( a_info.width.max(b_info.width), @@ -422,16 +422,9 @@ impl TransitionPipeline { | vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::TRANSFER_SRC, ); - let dest_image = render_graph.bind_node(self.cache.lease(dest_info).unwrap()); + let dest_image = graph.bind_node(self.cache.lease(dest_info).unwrap()); - self.apply_to( - render_graph, - a_image, - b_image, - dest_image, - transition, - progress, - ); + self.apply_to(graph, a_image, b_image, dest_image, transition, progress); dest_image } @@ -439,7 +432,7 @@ impl TransitionPipeline { /// TODO pub fn apply_to( &mut self, - render_graph: &mut Graph, + graph: &mut Graph, a_image: impl Into, b_image: impl Into, dest_image: impl Into, @@ -451,7 +444,7 @@ impl TransitionPipeline { let dest_image = dest_image.into(); let progress = progress.clamp(0.0, 1.0); - let dest_info = render_graph.node_info(dest_image); + let dest_info = graph.node_info(dest_image); // Lazy-initialize the compute pipeline for this transition let transition_ty = transition.ty(); @@ -463,7 +456,7 @@ impl TransitionPipeline { extend_push_constants(transition, &mut push_consts); // TODO: Handle displacement and luma in an if case, below - render_graph + graph .begin_cmd() .with_name(format!("transition {transition_ty:?}")) .bind_pipeline(pipeline) diff --git a/contrib/vk-graph-hot/examples/glsl.rs b/contrib/vk-graph-hot/examples/glsl.rs index 44351c7d..0e38020a 100644 --- a/contrib/vk-graph-hot/examples/glsl.rs +++ b/contrib/vk-graph-hot/examples/glsl.rs @@ -29,7 +29,7 @@ fn main() -> Result<(), WindowError> { window.run(|frame| { frame - .render_graph + .graph .begin_cmd() .with_name("make some noise") .bind_pipeline(pipeline.hot()) diff --git a/contrib/vk-graph-hot/examples/hlsl.rs b/contrib/vk-graph-hot/examples/hlsl.rs index b6a590f2..7c1b0730 100644 --- a/contrib/vk-graph-hot/examples/hlsl.rs +++ b/contrib/vk-graph-hot/examples/hlsl.rs @@ -33,7 +33,7 @@ fn main() -> Result<(), WindowError> { window.run(|frame| { frame - .render_graph + .graph .begin_cmd() .with_name("make some noise") .bind_pipeline(pipeline.hot()) diff --git a/contrib/vk-graph-imgui/src/lib.rs b/contrib/vk-graph-imgui/src/lib.rs index 6a30b4ab..0f82ddbe 100644 --- a/contrib/vk-graph-imgui/src/lib.rs +++ b/contrib/vk-graph-imgui/src/lib.rs @@ -63,7 +63,7 @@ impl ImGui { events: &[Event<()>], window: &Window, pool: &mut P, - render_graph: &mut Graph, + graph: &mut Graph, ui_func: impl FnOnce(&mut Ui, &mut P, &mut Graph), ) -> ImageLeaseNode where @@ -75,7 +75,7 @@ impl ImGui { .attach_window(self.context.io_mut(), window, HiDpiMode::Default); if self.font_atlas_image.is_none() || self.platform.hidpi_factor() != hidpi { - self.lease_font_atlas_image(pool, render_graph); + self.lease_font_atlas_image(pool, graph); } let io = self.context.io_mut(); @@ -92,12 +92,12 @@ impl ImGui { // Let the caller draw the GUI let ui = self.context.frame(); - ui_func(ui, pool, render_graph); + ui_func(ui, pool, graph); self.platform.prepare_render(ui, window); let draw_data = self.context.render(); - let image = render_graph.bind_node({ + let image = graph.bind_node({ let mut image = pool .lease(ImageInfo::image_2d( window.inner_size().width, @@ -114,12 +114,12 @@ impl ImGui { image }); - let font_atlas_image = render_graph.bind_node(self.font_atlas_image.as_ref().unwrap()); + let font_atlas_image = graph.bind_node(self.font_atlas_image.as_ref().unwrap()); let display_pos = draw_data.display_pos; let framebuffer_scale = draw_data.framebuffer_scale; if draw_data.draw_lists_count() == 0 { - render_graph.clear_color_image(image, [0f32; 4]); + graph.clear_color_image(image, [0f32; 4]); return image; } @@ -137,7 +137,7 @@ impl ImGui { Buffer::mapped_slice_mut(&mut index_buf)[0..indices.len()].copy_from_slice(indices); } - let index_buf = render_graph.bind_node(index_buf); + let index_buf = graph.bind_node(index_buf); let vertices = draw_list.vtx_buffer(); let vertex_buf_len = vertices.len() * 20; @@ -158,7 +158,7 @@ impl ImGui { } } - let vertex_buf = render_graph.bind_node(vertex_buf); + let vertex_buf = graph.bind_node(vertex_buf); let draw_cmds = draw_list .commands() @@ -182,7 +182,7 @@ impl ImGui { let window_height = self.platform.hidpi_factor() as f32 / window.inner_size().height as f32; - render_graph + graph .begin_cmd() .with_name("imgui") .bind_pipeline(&self.pipeline) @@ -231,7 +231,7 @@ impl ImGui { image } - fn lease_font_atlas_image

(&mut self, pool: &mut P, render_graph: &mut Graph) + fn lease_font_atlas_image

(&mut self, pool: &mut P, graph: &mut Graph) where P: Pool + Pool, { @@ -280,8 +280,8 @@ impl ImGui { temp_buf[0..temp_buf_len].copy_from_slice(texture.data); } - let temp_buf = render_graph.bind_node(temp_buf); - let image = render_graph.bind_node({ + let temp_buf = graph.bind_node(temp_buf); + let image = graph.bind_node({ let mut image = pool .lease(ImageInfo::image_2d( texture.width, @@ -297,8 +297,8 @@ impl ImGui { image }); - render_graph.copy_buffer_to_image(temp_buf, image); + graph.copy_buffer_to_image(temp_buf, image); - self.font_atlas_image = Some(render_graph.unbind_node(image)); + self.font_atlas_image = Some(graph.unbind_node(image)); } } diff --git a/contrib/vk-graph-window/examples/hello_world.rs b/contrib/vk-graph-window/examples/hello_world.rs index 97534926..56604c2f 100644 --- a/contrib/vk-graph-window/examples/hello_world.rs +++ b/contrib/vk-graph-window/examples/hello_world.rs @@ -6,7 +6,7 @@ fn main() -> Result<(), WindowError> { Window::new()?.run(|frame| { frame - .render_graph + .graph .clear_color_image(frame.swapchain_image, [100u8, 149, 237]); }) } diff --git a/contrib/vk-graph-window/src/frame.rs b/contrib/vk-graph-window/src/frame.rs index 4d8217b5..ddd73fc5 100644 --- a/contrib/vk-graph-window/src/frame.rs +++ b/contrib/vk-graph-window/src/frame.rs @@ -31,7 +31,7 @@ pub struct FrameContext<'a> { /// A render graph which rendering commands should be recorded into. /// /// Make sure to write to `swapchain_image` as part of this graph. - pub render_graph: &'a mut Graph, + pub graph: &'a mut Graph, /// A pre-bound image node for the swapchain image to be drawn. pub swapchain_image: SwapchainImageNode, diff --git a/contrib/vk-graph-window/src/lib.rs b/contrib/vk-graph-window/src/lib.rs index b24bc415..445c61aa 100644 --- a/contrib/vk-graph-window/src/lib.rs +++ b/contrib/vk-graph-window/src/lib.rs @@ -333,8 +333,8 @@ impl Window { } if let Some(swapchain_image) = self.display.acquire_next_image()? { - let mut render_graph = Graph::default(); - let swapchain_image = render_graph.bind_node(swapchain_image); + let mut graph = Graph::default(); + let swapchain_image = graph.bind_node(swapchain_image); let swapchain_info = self.display.swapchain.info; let mut will_exit = false; @@ -345,7 +345,7 @@ impl Window { device, events: &self.events, height: swapchain_info.height, - render_graph: &mut render_graph, + graph: &mut graph, swapchain_image, width: swapchain_info.width, will_exit: &mut will_exit, @@ -362,7 +362,7 @@ impl Window { self.window.pre_present_notify(); self.display - .present_image(&mut self.display_pool, render_graph, swapchain_image, 0) + .present_image(&mut self.display_pool, graph, swapchain_image, 0) .inspect_err(|err| { warn!("unable to present swapchain image: {err}"); })?; diff --git a/examples/aliasing.rs b/examples/aliasing.rs index 81ce3020..1128f95e 100644 --- a/examples/aliasing.rs +++ b/examples/aliasing.rs @@ -36,11 +36,11 @@ fn main() -> Result<(), DriverError> { let image2 = pool.alias(image_info)?; assert!(Arc::ptr_eq(&image1, &image2)); - let mut render_graph = Graph::default(); + let mut graph = Graph::default(); // Binding these images to any render graph will produce the same physical nodes - let image1 = render_graph.bind_node(image1); - let image2 = render_graph.bind_node(image2); + let image1 = graph.bind_node(image1); + let image2 = graph.bind_node(image2); assert_eq!(image1, image2); // Let's make up some different, yet compatible, image information: @@ -52,11 +52,11 @@ fn main() -> Result<(), DriverError> { ); // We alias the compatible information and still produce the same physical image and node - let image3 = render_graph.bind_node(pool.alias(image_info)?); + let image3 = graph.bind_node(pool.alias(image_info)?); assert_eq!(image1, image3); // Using the same information for a new LEASE will generate an entirely different image!! - let image4 = render_graph.bind_node(pool.lease(image_info)?); + let image4 = graph.bind_node(pool.lease(image_info)?); assert_ne!(image1, image4); Ok(()) diff --git a/examples/app.rs b/examples/app.rs index 6cbeaf84..5abe305b 100644 --- a/examples/app.rs +++ b/examples/app.rs @@ -114,15 +114,15 @@ struct Context { impl Context { fn draw(&mut self) -> Result<(), DisplayError> { if let Some(swapchain_image) = self.display.acquire_next_image()? { - let mut render_graph = Graph::default(); - let swapchain_image = render_graph.bind_node(swapchain_image); + let mut graph = Graph::default(); + let swapchain_image = graph.bind_node(swapchain_image); // Rendering goes here! - render_graph.clear_color_image(swapchain_image, [1.0, 0.0, 1.0]); + graph.clear_color_image(swapchain_image, [1.0, 0.0, 1.0]); self.window.pre_present_notify(); self.display - .present_image(&mut self.display_pool, render_graph, swapchain_image, 0)?; + .present_image(&mut self.display_pool, graph, swapchain_image, 0)?; } Ok(()) diff --git a/examples/bindless.rs b/examples/bindless.rs index b5ae61ef..a627e09a 100644 --- a/examples/bindless.rs +++ b/examples/bindless.rs @@ -24,10 +24,10 @@ fn main() -> Result<(), WindowError> { let draw_buf = create_indirect_buffer(&window.device)?; window.run(|frame| { - let draw_buf_node = frame.render_graph.bind_node(&draw_buf); + let draw_buf_node = frame.graph.bind_node(&draw_buf); let mut pass = frame - .render_graph + .graph .begin_cmd() .with_name("Test") .bind_pipeline(&pipeline) diff --git a/examples/cpu_readback.rs b/examples/cpu_readback.rs index 6ca36c8f..d278ed4c 100644 --- a/examples/cpu_readback.rs +++ b/examples/cpu_readback.rs @@ -14,14 +14,14 @@ fn main() -> Result<(), DriverError> { let device_info = DeviceInfoBuilder::default().debug(args.debug); let device = Arc::new(Device::new(device_info)?); - let mut render_graph = Graph::default(); + let mut graph = Graph::default(); - let src_buf = render_graph.bind_node(Buffer::create_from_slice( + let src_buf = graph.bind_node(Buffer::create_from_slice( &device, vk::BufferUsageFlags::TRANSFER_SRC, [1, 2, 3, 4], )?); - let dst_buf = render_graph.bind_node(Buffer::create_from_slice( + let dst_buf = graph.bind_node(Buffer::create_from_slice( &device, vk::BufferUsageFlags::TRANSFER_DST, [0, 0, 0, 0], @@ -31,17 +31,15 @@ fn main() -> Result<(), DriverError> { // such as a ComputePipeline to run some shader code which writes to a buffer or image. It is // important to note that dst_buf does not contain the new data until we submit this render // graph and wait on the result - render_graph.copy_buffer(src_buf, dst_buf); + graph.copy_buffer(src_buf, dst_buf); // This line is optional - just bind a reference of Arc or a leased buffer so you retain // the actual buffer for later use and you could then remove this unbind_node line - let dst_buf = render_graph.unbind_node(dst_buf); + let dst_buf = graph.unbind_node(dst_buf); // Resolve and wait (or you can check has_executed without blocking) - alternatively you might // use device.queue_wait_idle(0) or device.device_wait_idle() - but those block on larger scopes - let mut cmd_buf = render_graph - .resolve() - .submit(&mut HashPool::new(&device), 0, 0)?; + let mut cmd_buf = graph.resolve().submit(&mut HashPool::new(&device), 0, 0)?; println!("Has executed? {}", cmd_buf.has_executed()?); let started = Instant::now(); diff --git a/examples/debugger.rs b/examples/debugger.rs index b3119391..27d94b78 100644 --- a/examples/debugger.rs +++ b/examples/debugger.rs @@ -44,7 +44,7 @@ fn main() -> Result<(), vk_graph_window::WindowError> { Note: This callback runs each time the operating system requests a new window image and it expects you to render something to `frame.swapchain_image` using - `frame.render_graph`. Note that this scope is infalliable. You may create additional + `frame.graph`. Note that this scope is infalliable. You may create additional images and graphs if you choose. You can resolve multiple render graphs per frame - but you only need to do that if you have a hot-section that is part of a VERY large graph. @@ -100,7 +100,7 @@ fn main() -> Result<(), vk_graph_window::WindowError> { It is left as an excerise to the reader to determine *what* might have gone wrong here. */ #[allow(unused_variables)] - let image = frame.render_graph.bind_node( + let image = frame.graph.bind_node( Image::create( frame.device, ImageInfo::image_2d( @@ -112,7 +112,7 @@ fn main() -> Result<(), vk_graph_window::WindowError> { ) .unwrap(), ); - let image = frame.render_graph.bind_node( + let image = frame.graph.bind_node( Image::create( frame.device, ImageInfo::image_2d( @@ -175,7 +175,7 @@ fn main() -> Result<(), vk_graph_window::WindowError> { .write_descriptor(42, frame.swapchain_image) */ frame - .render_graph + .graph .begin_cmd().with_name("This doesn't look good...") .bind_pipeline(&compute_pipeline) .write_descriptor(42, image) diff --git a/examples/egui.rs b/examples/egui.rs index e0084c1f..1291d763 100644 --- a/examples/egui.rs +++ b/examples/egui.rs @@ -20,7 +20,7 @@ fn main() -> anyhow::Result<()> { let mut cache = LazyPool::new(&window.device); window.run(|frame| { - let img = frame.render_graph.bind_node( + let img = frame.graph.bind_node( cache .lease(ImageInfo::image_2d( 100, @@ -30,9 +30,9 @@ fn main() -> anyhow::Result<()> { )) .unwrap(), ); - frame.render_graph.clear_color_image(img, [0., 1., 0., 1.]); + frame.graph.clear_color_image(img, [0., 1., 0., 1.]); frame - .render_graph + .graph .clear_color_image(frame.swapchain_image, [0., 0., 0., 1.]); let id = egui.register_texture(img); @@ -41,7 +41,7 @@ fn main() -> anyhow::Result<()> { frame.window, frame.events, frame.swapchain_image, - frame.render_graph, + frame.graph, |ui| { egui::Window::new("Test") .resizable(true) diff --git a/examples/font_bmp.rs b/examples/font_bmp.rs index b15d7c42..1c15a57b 100644 --- a/examples/font_bmp.rs +++ b/examples/font_bmp.rs @@ -120,7 +120,7 @@ fn main() -> anyhow::Result<()> { )?; window.run(|frame| { - let image_node = frame.render_graph.bind_node( + let image_node = frame.graph.bind_node( pool.lease(ImageInfo::image_2d( 320, 200, @@ -136,7 +136,7 @@ fn main() -> anyhow::Result<()> { // Fill the image with a smoke effect let elapsed_time = Instant::now() - start_time; frame - .render_graph + .graph .begin_cmd() .with_name("smoke") .bind_pipeline(&smoke_pipeline) @@ -154,9 +154,9 @@ fn main() -> anyhow::Result<()> { let x = 320f32 * 0.5 / scale - width as f32 * 0.5; let y = 200f32 * 0.5 / scale - height as f32 * 0.5; let color = [196, 172, 230u8]; - small_10px_font.print_scale(frame.render_graph, image_node, x, y, color, text, scale); + small_10px_font.print_scale(frame.graph, image_node, x, y, color, text, scale); - display.present_image(frame.render_graph, image_node, frame.swapchain_image); + display.present_image(frame.graph, image_node, frame.swapchain_image); })?; Ok(()) diff --git a/examples/fuzzer.rs b/examples/fuzzer.rs index ed5b1092..03bc83e9 100644 --- a/examples/fuzzer.rs +++ b/examples/fuzzer.rs @@ -78,7 +78,7 @@ fn main() -> Result<(), WindowError> { if clear_before { frame - .render_graph + .graph .clear_color_image(frame.swapchain_image, [0f32; 4]); } @@ -89,7 +89,7 @@ fn main() -> Result<(), WindowError> { if !clear_before { frame - .render_graph + .graph .clear_color_image(frame.swapchain_image, [0f32; 4]); } })?; @@ -217,8 +217,8 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { }]), ); - let blas_node = frame.render_graph.bind_node(blas); - let scratch_buf = frame.render_graph.bind_node( + let blas_node = frame.graph.bind_node(blas); + let scratch_buf = frame.graph.bind_node( pool.lease( BufferInfo::device_mem( blas_size.build_size, @@ -246,13 +246,13 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { }, vk::AccelerationStructureBuildRangeInfoKHR::default().primitive_count(1), )]); - let instance_buf = frame.render_graph.bind_node(instance_buf); + let instance_buf = frame.graph.bind_node(instance_buf); let tlas_size = AccelerationStructure::size_of(frame.device, &tlas_geometry_info); let tlas = pool .lease(AccelerationStructureInfo::tlas(tlas_size.create_size)) .unwrap(); - let tlas_node = frame.render_graph.bind_node(tlas); - let tlas_scratch_buf = frame.render_graph.bind_node( + let tlas_node = frame.graph.bind_node(tlas); + let tlas_scratch_buf = frame.graph.bind_node( pool.lease( BufferInfo::device_mem( tlas_size.build_size, @@ -264,11 +264,11 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { .unwrap(), ); - let index_node = frame.render_graph.bind_node(index_buf); - let vertex_node = frame.render_graph.bind_node(vertex_buf); + let index_node = frame.graph.bind_node(index_buf); + let vertex_node = frame.graph.bind_node(vertex_buf); let pass = frame - .render_graph + .graph .begin_cmd() .with_name("build acceleration structures"); @@ -287,9 +287,9 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { .map(|(blas_node, _, _)| *blas_node) .collect::>(); - let mut pass = pass.record_accel_struct(move |accel_struct, bindings| { + let mut pass = pass.record_accel_struct(move |accel_struct, nodes| { for (blas_node, scratch_buf, build_data) in blas_nodes { - let scratch_addr = bindings[scratch_buf].device_address(); + let scratch_addr = nodes[scratch_buf].device_address(); accel_struct.build(&[BuildAccelerationStructureInfo::new( blas_node, scratch_addr, @@ -309,8 +309,8 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { ); pass.access_node_mut(tlas_node, AccessType::AccelerationStructureBuildWrite); - pass.record_accel_struct(move |accel_struct, bindings| { - let scratch_data = Buffer::device_address(&bindings[tlas_scratch_buf]); + pass.record_accel_struct(move |accel_struct, nodes| { + let scratch_data = Buffer::device_address(&nodes[tlas_scratch_buf]); accel_struct.build(&[BuildAccelerationStructureInfo::new( tlas_node, scratch_data, @@ -363,25 +363,15 @@ fn record_pipeline_array_bind(frame: &mut FrameContext, pool: &mut HashPool) { vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST, ); let images = [ - frame - .render_graph - .bind_node(pool.lease(image_info).unwrap()), - frame - .render_graph - .bind_node(pool.lease(image_info).unwrap()), - frame - .render_graph - .bind_node(pool.lease(image_info).unwrap()), - frame - .render_graph - .bind_node(pool.lease(image_info).unwrap()), - frame - .render_graph - .bind_node(pool.lease(image_info).unwrap()), + frame.graph.bind_node(pool.lease(image_info).unwrap()), + frame.graph.bind_node(pool.lease(image_info).unwrap()), + frame.graph.bind_node(pool.lease(image_info).unwrap()), + frame.graph.bind_node(pool.lease(image_info).unwrap()), + frame.graph.bind_node(pool.lease(image_info).unwrap()), ]; frame - .render_graph + .graph .clear_color_image(images[0], [0f32; 4]) .clear_color_image(images[1], [0f32; 4]) .clear_color_image(images[2], [0f32; 4]) @@ -444,25 +434,15 @@ fn record_pipeline_bindless(frame: &mut FrameContext, pool: &mut HashPool) { vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::STORAGE, ); let images = [ - frame - .render_graph - .bind_node(pool.lease(image_info).unwrap()), - frame - .render_graph - .bind_node(pool.lease(image_info).unwrap()), - frame - .render_graph - .bind_node(pool.lease(image_info).unwrap()), - frame - .render_graph - .bind_node(pool.lease(image_info).unwrap()), - frame - .render_graph - .bind_node(pool.lease(image_info).unwrap()), + frame.graph.bind_node(pool.lease(image_info).unwrap()), + frame.graph.bind_node(pool.lease(image_info).unwrap()), + frame.graph.bind_node(pool.lease(image_info).unwrap()), + frame.graph.bind_node(pool.lease(image_info).unwrap()), + frame.graph.bind_node(pool.lease(image_info).unwrap()), ]; frame - .render_graph + .graph .begin_cmd() .with_name("compute-bindless") .bind_pipeline(&pipeline) @@ -497,7 +477,7 @@ fn record_pipeline_no_op(frame: &mut FrameContext, _: &mut HashPool) { ), ); frame - .render_graph + .graph .begin_cmd() .with_name("no-op") .bind_pipeline(&pipeline) @@ -547,7 +527,7 @@ fn record_graphic_bindless(frame: &mut FrameContext, pool: &mut HashPool) { .as_slice(), ); - let image = frame.render_graph.bind_node( + let image = frame.graph.bind_node( pool.lease(ImageInfo::image_2d( 256, 256, @@ -565,25 +545,15 @@ fn record_graphic_bindless(frame: &mut FrameContext, pool: &mut HashPool) { | vk::ImageUsageFlags::TRANSFER_DST, ); let images = [ - frame - .render_graph - .bind_node(pool.lease(image_info).unwrap()), - frame - .render_graph - .bind_node(pool.lease(image_info).unwrap()), - frame - .render_graph - .bind_node(pool.lease(image_info).unwrap()), - frame - .render_graph - .bind_node(pool.lease(image_info).unwrap()), - frame - .render_graph - .bind_node(pool.lease(image_info).unwrap()), + frame.graph.bind_node(pool.lease(image_info).unwrap()), + frame.graph.bind_node(pool.lease(image_info).unwrap()), + frame.graph.bind_node(pool.lease(image_info).unwrap()), + frame.graph.bind_node(pool.lease(image_info).unwrap()), + frame.graph.bind_node(pool.lease(image_info).unwrap()), ]; frame - .render_graph + .graph .clear_color_image(images[0], [0f32; 4]) .clear_color_image(images[1], [0f32; 4]) .clear_color_image(images[2], [0f32; 4]) @@ -636,7 +606,7 @@ fn record_graphic_load_store(frame: &mut FrameContext, _: &mut HashPool) { ); frame - .render_graph + .graph .begin_cmd() .with_name("load-store") .bind_pipeline(&pipeline) @@ -749,8 +719,8 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo .as_slice(), ); - let swapchain_format = frame.render_graph.node_info(frame.swapchain_image).fmt; - let msaa_color_image = frame.render_graph.bind_node( + let swapchain_format = frame.graph.node_info(frame.swapchain_image).fmt; + let msaa_color_image = frame.graph.bind_node( pool.lease( ImageInfo::image_2d( frame.width, @@ -763,7 +733,7 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo ) .unwrap(), ); - let msaa_depth_stencil_image = frame.render_graph.bind_node( + let msaa_depth_stencil_image = frame.graph.bind_node( pool.lease( ImageInfo::image_2d( frame.width, @@ -777,7 +747,7 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo ) .unwrap(), ); - let depth_stencil_image = frame.render_graph.bind_node( + let depth_stencil_image = frame.graph.bind_node( pool.lease(ImageInfo::image_2d( frame.width, frame.height, @@ -808,7 +778,7 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo }; frame - .render_graph + .graph .begin_cmd() .with_name("msaa-depth-stencil") .bind_pipeline(&pipeline) @@ -828,7 +798,7 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo } fn record_graphic_will_merge_common_color1(frame: &mut FrameContext, pool: &mut HashPool) { - let image = frame.render_graph.bind_node( + let image = frame.graph.bind_node( pool.lease(ImageInfo::image_2d( 256, 256, @@ -840,7 +810,7 @@ fn record_graphic_will_merge_common_color1(frame: &mut FrameContext, pool: &mut // Pass "a" stores color0 which "b" compatibly loads; so these two will get merged frame - .render_graph + .graph .begin_cmd() .with_name("a") .bind_pipeline(graphic_vert_frag_pipeline( @@ -874,7 +844,7 @@ fn record_graphic_will_merge_common_color1(frame: &mut FrameContext, pool: &mut pipeline.draw(1, 1, 0, 0); }); frame - .render_graph + .graph .begin_cmd() .with_name("b") .bind_pipeline(&graphic_vert_frag_pipeline( @@ -911,7 +881,7 @@ fn record_graphic_will_merge_common_color1(frame: &mut FrameContext, pool: &mut } fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut HashPool) { - let image_0 = frame.render_graph.bind_node( + let image_0 = frame.graph.bind_node( pool.lease(ImageInfo::image_2d( 256, 256, @@ -920,7 +890,7 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut )) .unwrap(), ); - let image_1 = frame.render_graph.bind_node( + let image_1 = frame.graph.bind_node( pool.lease(ImageInfo::image_2d( 256, 256, @@ -931,7 +901,7 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut ); frame - .render_graph + .graph .begin_cmd() .with_name("a") .bind_pipeline(graphic_vert_frag_pipeline( @@ -965,7 +935,7 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut pipeline.draw(1, 1, 0, 0); }); frame - .render_graph + .graph .begin_cmd() .with_name("b") .bind_pipeline(&graphic_vert_frag_pipeline( @@ -1003,7 +973,7 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut pipeline.draw(1, 1, 0, 0); }); frame - .render_graph + .graph .begin_cmd() .with_name("c") .bind_pipeline(&graphic_vert_frag_pipeline( @@ -1040,7 +1010,7 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut } fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut HashPool) { - let color_image = frame.render_graph.bind_node( + let color_image = frame.graph.bind_node( pool.lease(ImageInfo::image_2d( 256, 256, @@ -1049,7 +1019,7 @@ fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut )) .unwrap(), ); - let depth_image = frame.render_graph.bind_node( + let depth_image = frame.graph.bind_node( pool.lease(ImageInfo::image_2d( 256, 256, @@ -1061,7 +1031,7 @@ fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut // Pass "a" stores color0+depth which "b" compatibly loads; so these two will get merged frame - .render_graph + .graph .begin_cmd() .with_name("a") .bind_pipeline(graphic_vert_frag_pipeline( @@ -1096,7 +1066,7 @@ fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut pipeline.draw(1, 1, 0, 0); }); frame - .render_graph + .graph .begin_cmd() .with_name("b") .bind_pipeline(graphic_vert_frag_pipeline( @@ -1131,7 +1101,7 @@ fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut } fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut HashPool) { - let color_image = frame.render_graph.bind_node( + let color_image = frame.graph.bind_node( pool.lease(ImageInfo::image_2d( 256, 256, @@ -1140,7 +1110,7 @@ fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut )) .unwrap(), ); - let depth_image = frame.render_graph.bind_node( + let depth_image = frame.graph.bind_node( pool.lease(ImageInfo::image_2d( 256, 256, @@ -1152,7 +1122,7 @@ fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut // Pass "a" stores color0+depth which "b" compatibly loads; so these two will get merged frame - .render_graph + .graph .begin_cmd() .with_name("a") .bind_pipeline(graphic_vert_frag_pipeline( @@ -1184,7 +1154,7 @@ fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut pipeline.draw(1, 1, 0, 0); }); frame - .render_graph + .graph .begin_cmd() .with_name("b") .bind_pipeline(graphic_vert_frag_pipeline( @@ -1222,7 +1192,7 @@ fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut } fn record_graphic_will_merge_common_depth3(frame: &mut FrameContext, pool: &mut HashPool) { - let depth_image = frame.render_graph.bind_node( + let depth_image = frame.graph.bind_node( pool.lease(ImageInfo::image_2d( 256, 256, @@ -1233,7 +1203,7 @@ fn record_graphic_will_merge_common_depth3(frame: &mut FrameContext, pool: &mut ); frame - .render_graph + .graph .begin_cmd() .with_name("a") .bind_pipeline(graphic_vert_frag_pipeline( @@ -1265,7 +1235,7 @@ fn record_graphic_will_merge_common_depth3(frame: &mut FrameContext, pool: &mut pipeline.draw(1, 1, 0, 0); }); frame - .render_graph + .graph .begin_cmd() .with_name("b") .bind_pipeline(graphic_vert_frag_pipeline( @@ -1348,7 +1318,7 @@ fn record_graphic_will_merge_subpass_input(frame: &mut FrameContext, pool: &mut ) .as_slice(), ); - let image = frame.render_graph.bind_node( + let image = frame.graph.bind_node( pool.lease(ImageInfo::image_2d( 256, 256, @@ -1362,7 +1332,7 @@ fn record_graphic_will_merge_subpass_input(frame: &mut FrameContext, pool: &mut // Pass "a" stores color 0 which "b" compatibly inputs; so these two will get merged frame - .render_graph + .graph .begin_cmd() .with_name("a") .bind_pipeline(&pipeline_a) @@ -1372,7 +1342,7 @@ fn record_graphic_will_merge_subpass_input(frame: &mut FrameContext, pool: &mut pipeline.draw(1, 1, 0, 0); }); frame - .render_graph + .graph .begin_cmd() .with_name("b") .bind_pipeline(&pipeline_b) @@ -1410,7 +1380,7 @@ fn record_graphic_wont_merge(frame: &mut FrameContext, pool: &mut HashPool) { .as_slice(), ); - let image = frame.render_graph.bind_node( + let image = frame.graph.bind_node( pool.lease(ImageInfo::image_2d( 256, 256, @@ -1422,7 +1392,7 @@ fn record_graphic_wont_merge(frame: &mut FrameContext, pool: &mut HashPool) { // These two passes have common writes but are otherwise regular - they won't get merged frame - .render_graph + .graph .begin_cmd() .with_name("c") .bind_pipeline(&pipeline) @@ -1431,7 +1401,7 @@ fn record_graphic_wont_merge(frame: &mut FrameContext, pool: &mut HashPool) { pipeline.draw(1, 1, 0, 0); }); frame - .render_graph + .graph .begin_cmd() .with_name("d") .bind_pipeline(&pipeline) @@ -1472,7 +1442,7 @@ fn record_transfer_graphic_multipass(frame: &mut FrameContext, pool: &mut HashPo .as_slice(), ); let images = [ - frame.render_graph.bind_node( + frame.graph.bind_node( pool.lease(ImageInfo::image_2d( 256, 256, @@ -1481,7 +1451,7 @@ fn record_transfer_graphic_multipass(frame: &mut FrameContext, pool: &mut HashPo )) .unwrap(), ), - frame.render_graph.bind_node( + frame.graph.bind_node( pool.lease(ImageInfo::image_2d( 256, 256, @@ -1492,13 +1462,13 @@ fn record_transfer_graphic_multipass(frame: &mut FrameContext, pool: &mut HashPo ), ]; - frame.render_graph.clear_color_image(images[0], [0f32; 4]); - frame.render_graph.clear_color_image(images[1], [0f32; 4]); + frame.graph.clear_color_image(images[0], [0f32; 4]); + frame.graph.clear_color_image(images[1], [0f32; 4]); // a and b should merge into one renderpass with two subpasses; however the use of images[1] in // b should have a pipeline barrier (on the clear we just did) before the pass starts. frame - .render_graph + .graph .begin_cmd() .with_name("a") .bind_pipeline(&pipeline) @@ -1509,7 +1479,7 @@ fn record_transfer_graphic_multipass(frame: &mut FrameContext, pool: &mut HashPo pipeline.draw(1, 1, 0, 0); }); frame - .render_graph + .graph .begin_cmd() .with_name("b") .bind_pipeline(&pipeline) diff --git a/examples/image_sampler.rs b/examples/image_sampler.rs index 6adf982f..cb9e0a47 100644 --- a/examples/image_sampler.rs +++ b/examples/image_sampler.rs @@ -65,9 +65,9 @@ fn main() -> anyhow::Result<()> { } // Draw gulf.jpg using the active pipeline - let gulf_image = frame.render_graph.bind_node(&gulf_image); + let gulf_image = frame.graph.bind_node(&gulf_image); frame - .render_graph + .graph .begin_cmd() .with_name("Draw gulf image to swapchain") .bind_pipeline(&pipelines[pipeline_index]) @@ -242,17 +242,15 @@ fn read_image(device: &Device, path: impl AsRef) -> anyhow::Result Result<(), WindowError> { window.run(|frame| { // Lease and clear an image as a stand-in for some real game or program output - let app_image = frame.render_graph.bind_node( + let app_image = frame.graph.bind_node( pool.lease(ImageInfo::image_2d( frame.width, frame.height, @@ -42,7 +42,7 @@ fn main() -> Result<(), WindowError> { .unwrap(), ); frame - .render_graph + .graph .clear_color_image(app_image, [0.2, 0.22, 0.2, 1.0]); // Use the draw function callback to do some fun meant-for-debug-mode GUI stuff @@ -51,7 +51,7 @@ fn main() -> Result<(), WindowError> { frame.events, frame.window, &mut pool, - frame.render_graph, + frame.graph, |ui, _, _| { ui.window("Hello world") .position([10.0, 10.0], Condition::FirstUseEver) @@ -76,12 +76,7 @@ fn main() -> Result<(), WindowError> { ); // Present "gui_image" on top of "app_image" onto "frame.swapchain" - display.present_images( - frame.render_graph, - gui_image, - app_image, - frame.swapchain_image, - ); + display.present_images(frame.graph, gui_image, app_image, frame.swapchain_image); }) } diff --git a/examples/min_max.rs b/examples/min_max.rs index 5c3a5940..fa12590e 100644 --- a/examples/min_max.rs +++ b/examples/min_max.rs @@ -19,7 +19,7 @@ use { fn main() -> Result<(), DriverError> { pretty_env_logger::init(); - let mut render_graph = Graph::default(); + let mut graph = Graph::default(); let args = Args::parse(); let device_info = DeviceInfoBuilder::default().debug(args.debug); let device = Arc::new(Device::new(device_info)?); @@ -30,27 +30,27 @@ fn main() -> Result<(), DriverError> { // 4.0 5.0 6.0 7.0 // 8.0 9.0 10.0 11.0 // 12.0 13.0 14.0 15.0 - let depth_image = fill_depth_image(&device, &mut render_graph, size)?; + let depth_image = fill_depth_image(&device, &mut graph, size)?; // These 2x2 reduced images have undefined data until we wait on the results later let min_reduced_image = reduce_depth_image( &device, - &mut render_graph, + &mut graph, depth_image, vk::SamplerReductionMode::MIN, )?; let max_reduced_image = reduce_depth_image( &device, - &mut render_graph, + &mut graph, depth_image, vk::SamplerReductionMode::MAX, )?; // Create result buffers so we can read back the results - let min_result_buf = copy_image_to_buffer(&device, &mut render_graph, min_reduced_image)?; - let max_result_buf = copy_image_to_buffer(&device, &mut render_graph, max_reduced_image)?; + let min_result_buf = copy_image_to_buffer(&device, &mut graph, min_reduced_image)?; + let max_result_buf = copy_image_to_buffer(&device, &mut graph, max_reduced_image)?; - render_graph + graph .resolve() .submit(&mut HashPool::new(&device), 0, 0)? .wait_until_executed()?; @@ -87,7 +87,7 @@ fn main() -> Result<(), DriverError> { fn fill_depth_image( device: &Device, - render_graph: &mut Graph, + graph: &mut Graph, size: u32, ) -> Result { let info = ImageInfo::image_2d( @@ -144,24 +144,24 @@ fn fill_depth_image( // device.physical_device.sampler_filter_minmax_properties.image_component_mapping let depth_data = (0..size.pow(2)).map(|x| x as f32).collect::>(); - let depth_data = render_graph.bind_node(Buffer::create_from_slice( + let depth_data = graph.bind_node(Buffer::create_from_slice( device, vk::BufferUsageFlags::TRANSFER_SRC, cast_slice(&depth_data), )?); - let depth_image = render_graph.bind_node(Image::create(device, info)?); - render_graph.copy_buffer_to_image(depth_data, depth_image); + let depth_image = graph.bind_node(Image::create(device, info)?); + graph.copy_buffer_to_image(depth_data, depth_image); Ok(depth_image) } fn reduce_depth_image( device: &Device, - render_graph: &mut Graph, + graph: &mut Graph, depth_image: ImageNode, reduction_mode: vk::SamplerReductionMode, ) -> Result { - let depth_info = render_graph.node_info(depth_image); + let depth_info = graph.node_info(depth_image); assert_eq!(depth_info.width, depth_info.height); @@ -173,9 +173,9 @@ fn reduce_depth_image( vk::Format::R32_SFLOAT, vk::ImageUsageFlags::STORAGE | vk::ImageUsageFlags::TRANSFER_SRC, ); - let reduced_image = render_graph.bind_node(Image::create(device, reduced_info)?); + let reduced_image = graph.bind_node(Image::create(device, reduced_info)?); - render_graph + graph .begin_cmd() .with_name("Reduce depth image") .bind_pipeline(ComputePipeline::create( @@ -215,20 +215,20 @@ fn reduce_depth_image( fn copy_image_to_buffer( device: &Device, - render_graph: &mut Graph, + graph: &mut Graph, reduced_image: ImageNode, ) -> Result, DriverError> { - let reduced_info = render_graph.node_info(reduced_image); + let reduced_info = graph.node_info(reduced_image); let result_len = (reduced_info.width * reduced_info.height) as vk::DeviceSize * size_of::() as vk::DeviceSize; - let result_buf = render_graph.bind_node(Buffer::create( + let result_buf = graph.bind_node(Buffer::create( device, BufferInfo::host_mem(result_len, vk::BufferUsageFlags::TRANSFER_DST), )?); - render_graph.copy_image_to_buffer(reduced_image, result_buf); + graph.copy_image_to_buffer(reduced_image, result_buf); - Ok(render_graph.unbind_node(result_buf)) + Ok(graph.unbind_node(result_buf)) } #[derive(Parser)] diff --git a/examples/mip_compute.rs b/examples/mip_compute.rs index 4d60ac91..837a2951 100644 --- a/examples/mip_compute.rs +++ b/examples/mip_compute.rs @@ -18,9 +18,9 @@ fn main() -> Result<(), DriverError> { let device_info = DeviceInfoBuilder::default().debug(args.debug); let device = Arc::new(Device::new(device_info)?); - let mut render_graph = Graph::default(); + let mut graph = Graph::default(); - let depth_pyramid = render_graph.bind_node(Image::create( + let depth_pyramid = graph.bind_node(Image::create( &device, ImageInfo::image_2d( 4, @@ -34,11 +34,11 @@ fn main() -> Result<(), DriverError> { .to_builder() .mip_level_count(3), )?); - let depth_info = render_graph.node_info(depth_pyramid); + let depth_info = graph.node_info(depth_pyramid); // You would normally create this buffer by copying the depth attachment image #[allow(clippy::inconsistent_digit_grouping)] - let depth_buf = render_graph.bind_node(Buffer::create_from_slice( + let depth_buf = graph.bind_node(Buffer::create_from_slice( &device, vk::BufferUsageFlags::TRANSFER_SRC, cast_slice(&[ @@ -48,9 +48,9 @@ fn main() -> Result<(), DriverError> { [13.0__, 14.0, 15.0, 16.0], ]), )?); - render_graph.copy_buffer_to_image(depth_buf, depth_pyramid); + graph.copy_buffer_to_image(depth_buf, depth_pyramid); - let mut pass = render_graph + let mut pass = graph .begin_cmd() .with_name("update depth pyramid") .bind_pipeline(ComputePipeline::create( @@ -114,11 +114,11 @@ fn main() -> Result<(), DriverError> { }); } - let depth_pixel = render_graph.bind_node(Buffer::create( + let depth_pixel = graph.bind_node(Buffer::create( &device, BufferInfo::host_mem(size_of::() as _, vk::BufferUsageFlags::TRANSFER_DST), )?); - render_graph.copy_image_to_buffer_region( + graph.copy_image_to_buffer_region( depth_pyramid, depth_pixel, vk::BufferImageCopy { @@ -140,9 +140,9 @@ fn main() -> Result<(), DriverError> { }, ); - let depth_pixel = render_graph.unbind_node(depth_pixel); + let depth_pixel = graph.unbind_node(depth_pixel); - render_graph + graph .resolve() .submit(&mut HashPool::new(&device), 0, 0)? .wait_until_executed()?; diff --git a/examples/mip_graphic.rs b/examples/mip_graphic.rs index 3f581ae4..8a304b3c 100644 --- a/examples/mip_graphic.rs +++ b/examples/mip_graphic.rs @@ -46,18 +46,18 @@ fn main() -> Result<(), WindowError> { // https://vulkan.gpuinfo.org/listsurfaceusageflags.php assert!( frame - .render_graph + .graph .node_info(frame.swapchain_image) .usage .contains(vk::ImageUsageFlags::COLOR_ATTACHMENT) ); - let image = frame.render_graph.bind_node(&image); - let swapchain_info = frame.render_graph.node_info(frame.swapchain_image); + let image = frame.graph.bind_node(&image); + let swapchain_info = frame.graph.node_info(frame.swapchain_image); let stripe_width = swapchain_info.width / mip_level_count; let mut pass = frame - .render_graph + .graph .begin_cmd() .with_name("splat mips") .bind_pipeline(&splat); @@ -146,15 +146,15 @@ fn fill_mip_levels(device: &Device, image: &Arc) -> Result<(), DriverErro ], )?; - let mut render_graph = Graph::default(); + let mut graph = Graph::default(); let image_info = image.info; - let image = render_graph.bind_node(image); + let image = graph.bind_node(image); // NOTE: Each pass writes to a different mip level, and so although it's the same image they are // unable to be used as a single pass so we must call begin_pass for each. Without starting a // new pass for each level the Vulkan framebuffer would be set to the size of the first image. for mip_level in 0..image_info.mip_level_count { - render_graph + graph .begin_cmd() .with_name("fill mip levels") .bind_pipeline(&vertical_gradient) @@ -195,7 +195,7 @@ fn fill_mip_levels(device: &Device, image: &Arc) -> Result<(), DriverErro .ok_or(DriverError::Unsupported)?; // Submits to the GPU but does not wait for anything to be finished - render_graph + graph .resolve() .submit(&mut LazyPool::new(device), queue_family_index, 0) .map(|_| ()) diff --git a/examples/msaa.rs b/examples/msaa.rs index eaaaefd8..fe5e0467 100644 --- a/examples/msaa.rs +++ b/examples/msaa.rs @@ -86,11 +86,11 @@ fn main() -> anyhow::Result<()> { }), ); - let cube_vertex_buf = frame.render_graph.bind_node(&cube_mesh.vertex_buf); - let scene_uniform_buf = frame.render_graph.bind_node(scene_uniform_buf); + let cube_vertex_buf = frame.graph.bind_node(&cube_mesh.vertex_buf); + let scene_uniform_buf = frame.graph.bind_node(scene_uniform_buf); let mut pass = frame - .render_graph + .graph .begin_cmd() .with_name("cube") .bind_pipeline(if will_render_msaa { diff --git a/examples/multipass.rs b/examples/multipass.rs index 899bdd31..e5c157c7 100644 --- a/examples/multipass.rs +++ b/examples/multipass.rs @@ -60,10 +60,10 @@ fn main() -> anyhow::Result<()> { window.run(|frame| { t += 0.016; - let index_buf = frame.render_graph.bind_node(&funky_shape.index_buf); - let vertex_buf = frame.render_graph.bind_node(&funky_shape.vertex_buf); + let index_buf = frame.graph.bind_node(&funky_shape.index_buf); + let vertex_buf = frame.graph.bind_node(&funky_shape.vertex_buf); - let depth_stencil = frame.render_graph.bind_node( + let depth_stencil = frame.graph.bind_node( pool.lease(ImageInfo::image_2d( frame.width, frame.height, @@ -79,15 +79,15 @@ fn main() -> anyhow::Result<()> { let obj_pos = Vec3::ZERO; let material = GOLD; - let camera_buf = bind_camera_buf(frame.render_graph, &mut pool, camera, model); - let light_buf = bind_light_buf(frame.render_graph, &mut pool); + let camera_buf = bind_camera_buf(frame.graph, &mut pool, camera, model); + let light_buf = bind_light_buf(frame.graph, &mut pool); let push_const_data = write_push_consts(obj_pos, material); let mut write = DepthStencilMode::DEPTH_WRITE; // Depth Prepass frame - .render_graph + .graph .begin_cmd() .with_name("Depth Prepass") .bind_pipeline(&prepass) @@ -118,7 +118,7 @@ fn main() -> anyhow::Result<()> { // Renders a golden orb on an un-cleared swapchain image frame - .render_graph + .graph .begin_cmd() .with_name("funky shape PBR") .bind_pipeline(&pbr) @@ -147,7 +147,7 @@ fn main() -> anyhow::Result<()> { // Renders a solid color wherever the golden orb did not draw frame - .render_graph + .graph .begin_cmd() .with_name("fill background") .bind_pipeline(&fill_background) @@ -187,7 +187,7 @@ fn best_depth_stencil_format(device: &Device) -> vk::Format { } fn bind_camera_buf( - render_graph: &mut Graph, + graph: &mut Graph, pool: &mut LazyPool, camera: Camera, model: Mat4, @@ -200,10 +200,10 @@ fn bind_camera_buf( .unwrap(); write_camera_buf(&mut buf, camera, model); - render_graph.bind_node(buf) + graph.bind_node(buf) } -fn bind_light_buf(render_graph: &mut Graph, pool: &mut LazyPool) -> BufferLeaseNode { +fn bind_light_buf(graph: &mut Graph, pool: &mut LazyPool) -> BufferLeaseNode { let mut buf = pool .lease(BufferInfo::host_mem( 64, @@ -212,7 +212,7 @@ fn bind_light_buf(render_graph: &mut Graph, pool: &mut LazyPool) -> BufferLeaseN .unwrap(); write_light_buf(&mut buf); - render_graph.bind_node(buf) + graph.bind_node(buf) } fn write_push_consts(obj_pos: Vec3, material: Material) -> [u8; 32] { diff --git a/examples/multithread.rs b/examples/multithread.rs index 132a54fb..f760d6e4 100644 --- a/examples/multithread.rs +++ b/examples/multithread.rs @@ -101,8 +101,8 @@ fn main() -> anyhow::Result<()> { let t = 12.0 * ((Instant::now() - started_at).as_millis() % 32) as f32; // Clear a new image to a cycling color - let mut render_graph = Graph::default(); - let image = render_graph.bind_node( + let mut graph = Graph::default(); + let image = graph.bind_node( pool.lease(ImageInfo::image_2d( 10, 10, @@ -111,7 +111,7 @@ fn main() -> anyhow::Result<()> { )) .unwrap(), ); - render_graph.clear_color_image( + graph.clear_color_image( image, [ (t.sin() * 127.0 + 128.0) as u8, @@ -121,10 +121,10 @@ fn main() -> anyhow::Result<()> { ], ); - let image = render_graph.unbind_node(image); + let image = graph.unbind_node(image); // Submit on a queue we are reserving for only this thread to use - render_graph + graph .resolve() .submit(&mut pool, secondary_queue_family_index, queue_index) .unwrap(); @@ -153,11 +153,11 @@ fn main() -> anyhow::Result<()> { } frame - .render_graph + .graph .clear_color_image(frame.swapchain_image, [0f32; 4]); for (image_idx, image) in images.iter().enumerate() { - let image = frame.render_graph.bind_node(image); + let image = frame.graph.bind_node(image); let x = (image_idx % 8) as f32; let y = (image_idx / 8) as f32; @@ -165,7 +165,7 @@ fn main() -> anyhow::Result<()> { let j = frame.width as f32 / 10.0; let k = frame.height as f32 / 10.0; - frame.render_graph.blit_image_region( + frame.graph.blit_image_region( image, frame.swapchain_image, vk::Filter::NEAREST, @@ -195,7 +195,7 @@ fn main() -> anyhow::Result<()> { let fps = (1.0 / elapsed.as_secs_f32()).round(); let message = format!("FPS: {fps}"); font.print_scale( - frame.render_graph, + frame.graph, frame.swapchain_image, 0.0, 0.0, @@ -248,19 +248,17 @@ fn load_font(device: &Device) -> anyhow::Result { ) .unwrap(); - let mut render_graph = Graph::default(); - let page_0 = render_graph.bind_node(page_0); - let temp_buf = render_graph.bind_node(temp_buf); - render_graph.copy_buffer_to_image(temp_buf, page_0); + let mut graph = Graph::default(); + let page_0 = graph.bind_node(page_0); + let temp_buf = graph.bind_node(temp_buf); + graph.copy_buffer_to_image(temp_buf, page_0); // Unbind page_0 to get the Arc but we could have just bound a reference (with no unbind) - let page_0 = render_graph.unbind_node(page_0); + let page_0 = graph.unbind_node(page_0); // This copy happens in queue index 0! Notice the unbind above is OK because we already asked // for the copy to happen first! - render_graph - .resolve() - .submit(&mut HashPool::new(device), 0, 0)?; + graph.resolve().submit(&mut HashPool::new(device), 0, 0)?; BitmapFont::new(device, font, [page_0]) } diff --git a/examples/ray_omni.rs b/examples/ray_omni.rs index fbd93a09..d4b627b7 100644 --- a/examples/ray_omni.rs +++ b/examples/ray_omni.rs @@ -45,15 +45,14 @@ fn main() -> anyhow::Result<()> { window.run(|frame| { angle += 0.016; - let scene_tlas = - create_tlas(frame.device, &mut pool, frame.render_graph, &scene_blas).unwrap(); + let scene_tlas = create_tlas(frame.device, &mut pool, frame.graph, &scene_blas).unwrap(); - let ground_mesh_index_buf = frame.render_graph.bind_node(&ground_mesh.index_buf); - let ground_mesh_vertex_buf = frame.render_graph.bind_node(&ground_mesh.vertex_buf); - let model_mesh_index_buf = frame.render_graph.bind_node(&model_mesh.index_buf); - let model_mesh_vertex_buf = frame.render_graph.bind_node(&model_mesh.vertex_buf); + let ground_mesh_index_buf = frame.graph.bind_node(&ground_mesh.index_buf); + let ground_mesh_vertex_buf = frame.graph.bind_node(&ground_mesh.vertex_buf); + let model_mesh_index_buf = frame.graph.bind_node(&model_mesh.index_buf); + let model_mesh_vertex_buf = frame.graph.bind_node(&model_mesh.vertex_buf); - let depth_image = frame.render_graph.bind_node( + let depth_image = frame.graph.bind_node( pool.lease(ImageInfo::image_2d( frame.width, frame.height, @@ -62,7 +61,7 @@ fn main() -> anyhow::Result<()> { )) .unwrap(), ); - let camera_buf = frame.render_graph.bind_node({ + let camera_buf = frame.graph.bind_node({ let mut buf = pool .lease(BufferInfo::host_mem( size_of::() as _, @@ -89,7 +88,7 @@ fn main() -> anyhow::Result<()> { }); frame - .render_graph + .graph .begin_cmd() .with_name("Mesh with ray-query shadows") .bind_pipeline(&gfx_pipeline) @@ -177,8 +176,8 @@ fn create_blas( .flags(vk::BuildAccelerationStructureFlagsKHR::PREFER_FAST_TRACE); let size = AccelerationStructure::size_of(device, &info); - let mut render_graph = Graph::default(); - let blas = render_graph.bind_node(AccelerationStructure::create( + let mut graph = Graph::default(); + let blas = graph.bind_node(AccelerationStructure::create( device, AccelerationStructureInfo::blas(size.create_size), )?); @@ -190,7 +189,7 @@ fn create_blas( .unwrap() .min_accel_struct_scratch_offset_alignment as vk::DeviceSize; - let scratch_buf = render_graph.bind_node(Buffer::create( + let scratch_buf = graph.bind_node(Buffer::create( device, BufferInfo::device_mem( size.build_size, @@ -199,9 +198,9 @@ fn create_blas( .to_builder() .alignment(accel_struct_scratch_offset_alignment), )?); - let scratch_data = render_graph.node_device_address(scratch_buf); + let scratch_data = graph.node_device_address(scratch_buf); - let mut pass = render_graph.begin_cmd().with_name("Build BLAS"); + let mut pass = graph.begin_cmd().with_name("Build BLAS"); for model in models.iter().copied() { let index_buf = pass.bind_node(&model.index_buf); @@ -221,11 +220,9 @@ fn create_blas( )]); }); - let blas = render_graph.unbind_node(blas); + let blas = graph.unbind_node(blas); - render_graph - .resolve() - .submit(&mut LazyPool::new(device), 0, 0)?; + graph.resolve().submit(&mut LazyPool::new(device), 0, 0)?; Ok(blas) } @@ -320,7 +317,7 @@ fn create_pipeline(device: &Device) -> Result { fn create_tlas( device: &Device, pool: &mut LazyPool, - render_graph: &mut Graph, + graph: &mut Graph, blas: &Arc, ) -> Result { let instances = [vk::AccelerationStructureInstanceKHR { @@ -365,8 +362,7 @@ fn create_tlas( )]) .flags(vk::BuildAccelerationStructureFlagsKHR::PREFER_FAST_TRACE); let size = AccelerationStructure::size_of(device, &info); - let tlas = - render_graph.bind_node(pool.lease(AccelerationStructureInfo::tlas(size.create_size))?); + let tlas = graph.bind_node(pool.lease(AccelerationStructureInfo::tlas(size.create_size))?); let accel_struct_scratch_offset_alignment = device .physical_device @@ -375,7 +371,7 @@ fn create_tlas( .unwrap() .min_accel_struct_scratch_offset_alignment as vk::DeviceSize; - let scratch_buf = render_graph.bind_node( + let scratch_buf = graph.bind_node( pool.lease( BufferInfo::device_mem( size.build_size, @@ -385,11 +381,11 @@ fn create_tlas( .alignment(accel_struct_scratch_offset_alignment), )?, ); - let scratch_data = render_graph.node_device_address(scratch_buf); - let blas = render_graph.bind_node(blas); - let instance_buf = render_graph.bind_node(instance_buf); + let scratch_data = graph.node_device_address(scratch_buf); + let blas = graph.bind_node(blas); + let instance_buf = graph.bind_node(instance_buf); - render_graph + graph .begin_cmd() .with_name("Build TLAS") .access_node(blas, AccessType::AccelerationStructureBuildRead) diff --git a/examples/ray_trace.rs b/examples/ray_trace.rs index c90318a1..48b39ff8 100644 --- a/examples/ray_trace.rs +++ b/examples/ray_trace.rs @@ -668,13 +668,13 @@ fn main() -> anyhow::Result<()> { .unwrap() .min_accel_struct_scratch_offset_alignment as vk::DeviceSize; - let mut render_graph = Graph::default(); - let index_node = render_graph.bind_node(&index_buf); - let vertex_node = render_graph.bind_node(&vertex_buf); - let blas_node = render_graph.bind_node(&blas); + let mut graph = Graph::default(); + let index_node = graph.bind_node(&index_buf); + let vertex_node = graph.bind_node(&vertex_buf); + let blas_node = graph.bind_node(&blas); { - let scratch_buf = render_graph.bind_node(Buffer::create( + let scratch_buf = graph.bind_node(Buffer::create( &window.device, BufferInfo::device_mem( blas_size.build_size, @@ -684,9 +684,9 @@ fn main() -> anyhow::Result<()> { .to_builder() .alignment(accel_struct_scratch_offset_alignment), )?); - let scratch_data = render_graph.node_device_address(scratch_buf); + let scratch_data = graph.node_device_address(scratch_buf); - render_graph + graph .begin_cmd() .with_name("Build BLAS") .access_node(index_node, AccessType::AccelerationStructureBuildRead) @@ -703,7 +703,7 @@ fn main() -> anyhow::Result<()> { } { - let scratch_buf = render_graph.bind_node(Buffer::create( + let scratch_buf = graph.bind_node(Buffer::create( &window.device, BufferInfo::device_mem( tlas_size.build_size, @@ -713,11 +713,11 @@ fn main() -> anyhow::Result<()> { .to_builder() .alignment(accel_struct_scratch_offset_alignment), )?); - let scratch_data = render_graph.node_device_address(scratch_buf); - let instance_node = render_graph.bind_node(&instance_buf); - let tlas_node = render_graph.bind_node(&tlas); + let scratch_data = graph.node_device_address(scratch_buf); + let instance_node = graph.bind_node(&instance_buf); + let tlas_node = graph.bind_node(&tlas); - render_graph + graph .begin_cmd() .with_name("Build TLAS") .access_node(blas_node, AccessType::AccelerationStructureBuildRead) @@ -733,7 +733,7 @@ fn main() -> anyhow::Result<()> { }); } - render_graph.resolve().submit(&mut cache, 0, 0)?; + graph.resolve().submit(&mut cache, 0, 0)?; } // ------------------------------------------------------------------------------------------ // @@ -761,7 +761,7 @@ fn main() -> anyhow::Result<()> { .lease(ImageInfo::image_2d( frame.width, frame.height, - frame.render_graph.node_info(frame.swapchain_image).fmt, + frame.graph.node_info(frame.swapchain_image).fmt, vk::ImageUsageFlags::STORAGE | vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::TRANSFER_SRC, @@ -770,7 +770,7 @@ fn main() -> anyhow::Result<()> { )); } - let image_node = frame.render_graph.bind_node(image.as_ref().unwrap()); + let image_node = frame.graph.bind_node(image.as_ref().unwrap()); { input.step_with_window_events( @@ -811,13 +811,13 @@ fn main() -> anyhow::Result<()> { if input.key_pressed(KeyCode::Escape) { frame_count = 0; - frame.render_graph.clear_color_image(image_node, [0f32; 4]); + frame.graph.clear_color_image(image_node, [0f32; 4]); } else { frame_count += 1; } } - let camera_buf = frame.render_graph.bind_node({ + let camera_buf = frame.graph.bind_node({ #[repr(C)] struct Camera { position: [f32; 4], @@ -848,16 +848,16 @@ fn main() -> anyhow::Result<()> { buf }); - let blas_node = frame.render_graph.bind_node(&blas); - let tlas_node = frame.render_graph.bind_node(&tlas); - let index_buf_node = frame.render_graph.bind_node(&index_buf); - let vertex_buf_node = frame.render_graph.bind_node(&vertex_buf); - let material_id_buf_node = frame.render_graph.bind_node(&material_id_buf); - let material_buf_node = frame.render_graph.bind_node(&material_buf); - let sbt_node = frame.render_graph.bind_node(&sbt_buf); + let blas_node = frame.graph.bind_node(&blas); + let tlas_node = frame.graph.bind_node(&tlas); + let index_buf_node = frame.graph.bind_node(&index_buf); + let vertex_buf_node = frame.graph.bind_node(&vertex_buf); + let material_id_buf_node = frame.graph.bind_node(&material_id_buf); + let material_buf_node = frame.graph.bind_node(&material_buf); + let sbt_node = frame.graph.bind_node(&sbt_buf); frame - .render_graph + .graph .begin_cmd() .with_name("basic ray tracer") .bind_pipeline(&ray_trace_pipeline) diff --git a/examples/rt_triangle.rs b/examples/rt_triangle.rs index a3ef8bd1..a77adc40 100644 --- a/examples/rt_triangle.rs +++ b/examples/rt_triangle.rs @@ -322,13 +322,13 @@ fn main() -> anyhow::Result<()> { .unwrap() .min_accel_struct_scratch_offset_alignment as vk::DeviceSize; - let mut render_graph = Graph::default(); - let index_node = render_graph.bind_node(&index_buf); - let vertex_node = render_graph.bind_node(&vertex_buf); - let blas_node = render_graph.bind_node(&blas); + let mut graph = Graph::default(); + let index_node = graph.bind_node(&index_buf); + let vertex_node = graph.bind_node(&vertex_buf); + let blas_node = graph.bind_node(&blas); { - let scratch_buf = render_graph.bind_node(Buffer::create( + let scratch_buf = graph.bind_node(Buffer::create( &window.device, BufferInfo::device_mem( blas_size.build_size, @@ -338,9 +338,9 @@ fn main() -> anyhow::Result<()> { .to_builder() .alignment(accel_struct_scratch_offset_alignment), )?); - let scratch_data = render_graph.node_device_address(scratch_buf); + let scratch_data = graph.node_device_address(scratch_buf); - render_graph + graph .begin_cmd() .with_name("Build BLAS") .access_node(index_node, AccessType::AccelerationStructureBuildRead) @@ -357,8 +357,8 @@ fn main() -> anyhow::Result<()> { } { - let instance_node = render_graph.bind_node(instance_buf); - let scratch_buf = render_graph.bind_node(Buffer::create( + let instance_node = graph.bind_node(instance_buf); + let scratch_buf = graph.bind_node(Buffer::create( &window.device, BufferInfo::device_mem( tlas_size.build_size, @@ -368,10 +368,10 @@ fn main() -> anyhow::Result<()> { .to_builder() .alignment(accel_struct_scratch_offset_alignment), )?); - let scratch_data = render_graph.node_device_address(scratch_buf); - let tlas_node = render_graph.bind_node(&tlas); + let scratch_data = graph.node_device_address(scratch_buf); + let tlas_node = graph.bind_node(&tlas); - render_graph + graph .begin_cmd() .with_name("Build TLAS") .access_node(blas_node, AccessType::AccelerationStructureBuildRead) @@ -387,7 +387,7 @@ fn main() -> anyhow::Result<()> { }); } - render_graph.resolve().submit(&mut pool, 0, 0)?; + graph.resolve().submit(&mut pool, 0, 0)?; } // ------------------------------------------------------------------------------------------ // @@ -398,12 +398,12 @@ fn main() -> anyhow::Result<()> { // - Trace the image // - Copy image to the swapchain window.run(|frame| { - let blas_node = frame.render_graph.bind_node(&blas); - let tlas_node = frame.render_graph.bind_node(&tlas); - let sbt_node = frame.render_graph.bind_node(&sbt_buf); + let blas_node = frame.graph.bind_node(&blas); + let tlas_node = frame.graph.bind_node(&tlas); + let sbt_node = frame.graph.bind_node(&sbt_buf); frame - .render_graph + .graph .begin_cmd() .with_name("ray-traced triangle") .bind_pipeline(&ray_trace_pipeline) diff --git a/examples/shader-toy/src/main.rs b/examples/shader-toy/src/main.rs index f76e4607..bf3533a0 100644 --- a/examples/shader-toy/src/main.rs +++ b/examples/shader-toy/src/main.rs @@ -122,8 +122,8 @@ fn main() -> anyhow::Result<()> { ) .context("FLOCKAROO_IMG_FRAG")?; - let mut render_graph = Graph::default(); - let blank_image = render_graph.bind_node( + let mut graph = Graph::default(); + let blank_image = graph.bind_node( cache .lease(ImageInfo::image_2d( 8, @@ -135,7 +135,7 @@ fn main() -> anyhow::Result<()> { ); let (width, height) = (1280, 720); - let framebuffer_image = render_graph.bind_node( + let framebuffer_image = graph.bind_node( cache .lease(ImageInfo::image_2d( width, @@ -148,7 +148,7 @@ fn main() -> anyhow::Result<()> { )) .context("Framebuffer image")?, ); - let temp_image = render_graph.bind_node( + let temp_image = graph.bind_node( cache .lease(ImageInfo::image_2d( width, @@ -162,16 +162,16 @@ fn main() -> anyhow::Result<()> { .context("Temp image")?, ); - render_graph + graph .clear_color_image(framebuffer_image, [1.0, 1.0, 0.0, 1.0]) .clear_color_image(blank_image, [0.0, 0.0, 0.0, 1.0]) .clear_color_image(temp_image, [0.0, 1.0, 0.0, 1.0]); - let mut framebuffer_image_binding = Some(render_graph.unbind_node(framebuffer_image)); - let mut blank_image_binding = Some(render_graph.unbind_node(blank_image)); - let mut temp_image_binding = Some(render_graph.unbind_node(temp_image)); + let mut framebuffer_image_binding = Some(graph.unbind_node(framebuffer_image)); + let mut blank_image_binding = Some(graph.unbind_node(blank_image)); + let mut temp_image_binding = Some(graph.unbind_node(temp_image)); - render_graph.resolve().submit(&mut cache, 0, 0)?; + graph.resolve().submit(&mut cache, 0, 0)?; let started_at = Instant::now(); let mut count = 0i32; @@ -188,21 +188,13 @@ fn main() -> anyhow::Result<()> { count += 1; // Bind things to this graph (the graph will own our things until we unbind them) - let flowers_image = frame - .render_graph - .bind_node(flowers_image_binding.take().unwrap()); - let noise_image = frame - .render_graph - .bind_node(noise_image_binding.take().unwrap()); + let flowers_image = frame.graph.bind_node(flowers_image_binding.take().unwrap()); + let noise_image = frame.graph.bind_node(noise_image_binding.take().unwrap()); let framebuffer_image = frame - .render_graph + .graph .bind_node(framebuffer_image_binding.take().unwrap()); - let blank_image = frame - .render_graph - .bind_node(blank_image_binding.take().unwrap()); - let temp_image = frame - .render_graph - .bind_node(temp_image_binding.take().unwrap()); + let blank_image = frame.graph.bind_node(blank_image_binding.take().unwrap()); + let temp_image = frame.graph.bind_node(temp_image_binding.take().unwrap()); // We need to push a shader-toy defined set of constants to each pipeline - any copy // type will do but we are getting fancy here by defining a struct to be super precise @@ -285,7 +277,7 @@ fn main() -> anyhow::Result<()> { // Fill a buffer using a single-pass CFD pipeline where previous output feeds next input frame - .render_graph + .graph .begin_cmd() .with_name("Buffer A") .bind_pipeline(&buffer_pipeline) @@ -302,7 +294,7 @@ fn main() -> anyhow::Result<()> { // Make the CFD look more like paint with a second pass frame - .render_graph + .graph .begin_cmd() .with_name("Image") .bind_pipeline(&image_pipeline) @@ -315,14 +307,14 @@ fn main() -> anyhow::Result<()> { }); // Done! - display.present_image(frame.render_graph, input, frame.swapchain_image); + display.present_image(frame.graph, input, frame.swapchain_image); // Unbind things from this graph (we want them back for the next frame!) - flowers_image_binding = Some(frame.render_graph.unbind_node(flowers_image)); - noise_image_binding = Some(frame.render_graph.unbind_node(noise_image)); - framebuffer_image_binding = Some(frame.render_graph.unbind_node(framebuffer_image)); - blank_image_binding = Some(frame.render_graph.unbind_node(blank_image)); - temp_image_binding = Some(frame.render_graph.unbind_node(temp_image)); + flowers_image_binding = Some(frame.graph.unbind_node(flowers_image)); + noise_image_binding = Some(frame.graph.unbind_node(noise_image)); + framebuffer_image_binding = Some(frame.graph.unbind_node(framebuffer_image)); + blank_image_binding = Some(frame.graph.unbind_node(blank_image)); + temp_image_binding = Some(frame.graph.unbind_node(temp_image)); }) .context("Unable to run event loop")?; diff --git a/examples/skeletal-anim/src/main.rs b/examples/skeletal-anim/src/main.rs index 1d4e937c..51724dbb 100644 --- a/examples/skeletal-anim/src/main.rs +++ b/examples/skeletal-anim/src/main.rs @@ -59,9 +59,9 @@ fn main() -> Result<(), WindowError> { window.run(|frame| { let elapsed = (Instant::now() - started).as_secs_f32(); - let index_buf = frame.render_graph.bind_node(&character.index_buf); - let vertex_buf = frame.render_graph.bind_node(&character.vertex_buf); - let depth_image = frame.render_graph.bind_node( + let index_buf = frame.graph.bind_node(&character.index_buf); + let vertex_buf = frame.graph.bind_node(&character.vertex_buf); + let depth_image = frame.graph.bind_node( pool.lease(ImageInfo::image_2d( frame.width, frame.height, @@ -72,7 +72,7 @@ fn main() -> Result<(), WindowError> { ); let texture = frame - .render_graph + .graph .bind_node(match (elapsed / 2.0).rem_euclid(4.0) { t if t < 1.0 => &human_female, t if t < 2.0 => &human_male, @@ -80,7 +80,7 @@ fn main() -> Result<(), WindowError> { _ => &zombie_male, }); - let camera_buf = frame.render_graph.bind_node({ + let camera_buf = frame.graph.bind_node({ let position = Vec3::ONE * 3.0; let aspect_ratio = frame.render_aspect_ratio(); let projection = Mat4::perspective_rh(45.0, aspect_ratio, 0.1, 100.0); @@ -105,7 +105,7 @@ fn main() -> Result<(), WindowError> { buf }); - let animation_buf = frame.render_graph.bind_node({ + let animation_buf = frame.graph.bind_node({ let animation = match (elapsed / 4.0).rem_euclid(2.0) { t if t < 1.0 => &mut run, _ => &mut idle, @@ -124,7 +124,7 @@ fn main() -> Result<(), WindowError> { }); frame - .render_graph + .graph .begin_cmd() .with_name("🦴") .bind_pipeline(&pipeline) @@ -188,13 +188,11 @@ fn load_texture(device: &Device, pak: &mut PakBuf, key: &str) -> Result Result, DriverError> { - let mut render_graph = Graph::default(); + let mut graph = Graph::default(); - let input_buf = render_graph.bind_node(Buffer::create_from_slice( + let input_buf = graph.bind_node(Buffer::create_from_slice( device, vk::BufferUsageFlags::STORAGE_BUFFER, cast_slice(input_data), )?); - let output_buf = render_graph.bind_node(Arc::new(Buffer::create( + let output_buf = graph.bind_node(Arc::new(Buffer::create( device, BufferInfo::host_mem( input_data.len() as vk::DeviceSize * size_of::() as vk::DeviceSize, @@ -85,7 +85,7 @@ fn exclusive_sum( let workgroup_count = input_data.len() as u32 / device.physical_device.properties_v1_1.subgroup_size; let reduce_count = workgroup_count - 1; - let workgroup_buf = render_graph.bind_node(Buffer::create( + let workgroup_buf = graph.bind_node(Buffer::create( device, BufferInfo::device_mem( reduce_count.max(1) as vk::DeviceSize * size_of::() as vk::DeviceSize, @@ -94,7 +94,7 @@ fn exclusive_sum( )?); if reduce_count > 0 { - render_graph + graph .begin_cmd() .with_name("exclusive sum reduce") .bind_pipeline(reduce_pipeline) @@ -105,7 +105,7 @@ fn exclusive_sum( }); } - render_graph + graph .begin_cmd() .with_name("exclusive sum scan") .bind_pipeline(scan_pipeline) @@ -116,10 +116,8 @@ fn exclusive_sum( compute.dispatch(workgroup_count, 1, 1); }); - let output_buf = render_graph.unbind_node(output_buf); - let mut cmd_buf = render_graph - .resolve() - .submit(&mut HashPool::new(device), 0, 0)?; + let output_buf = graph.unbind_node(output_buf); + let mut cmd_buf = graph.resolve().submit(&mut HashPool::new(device), 0, 0)?; let started = Instant::now(); cmd_buf.wait_until_executed()?; diff --git a/examples/transitions.rs b/examples/transitions.rs index 01b3455d..207fdf52 100644 --- a/examples/transitions.rs +++ b/examples/transitions.rs @@ -78,18 +78,13 @@ fn main() -> anyhow::Result<()> { }; // Bind images so we can graph them - let bart_image = frame.render_graph.bind_node(&bart_image); - let gulf_image = frame.render_graph.bind_node(&gulf_image); + let bart_image = frame.graph.bind_node(&bart_image); + let gulf_image = frame.graph.bind_node(&gulf_image); // Apply the current transition to the images and get a resultant image out; "blend_image" let transition = TRANSITIONS[curr_transition_idx]; - let blend_image = transition_pipeline.apply( - frame.render_graph, - bart_image, - gulf_image, - transition, - progress, - ); + let blend_image = + transition_pipeline.apply(frame.graph, bart_image, gulf_image, transition, progress); // Draw UI: TODO: Sliders and value setters? That would be fun. let gui_image = imgui.draw( @@ -97,7 +92,7 @@ fn main() -> anyhow::Result<()> { frame.events, frame.window, &mut pool, - frame.render_graph, + frame.graph, |ui, _, _| { ui.window("Transitions example") .position([10.0, 10.0], Condition::FirstUseEver) @@ -121,12 +116,7 @@ fn main() -> anyhow::Result<()> { ); // Display the GUI + Blend images on screen - display.present_images( - frame.render_graph, - gui_image, - blend_image, - frame.swapchain_image, - ); + display.present_images(frame.graph, gui_image, blend_image, frame.swapchain_image); })?; Ok(()) diff --git a/examples/triangle.rs b/examples/triangle.rs index db475df3..be429003 100644 --- a/examples/triangle.rs +++ b/examples/triangle.rs @@ -79,11 +79,11 @@ fn main() -> Result<(), WindowError> { )?); window.run(|frame| { - let index_node = frame.render_graph.bind_node(&index_buf); - let vertex_node = frame.render_graph.bind_node(&vertex_buf); + let index_node = frame.graph.bind_node(&index_buf); + let vertex_node = frame.graph.bind_node(&vertex_buf); frame - .render_graph + .graph .begin_cmd() .with_name("Triangle Example") .bind_pipeline(&triangle_pipeline) diff --git a/examples/vertex_layout.rs b/examples/vertex_layout.rs index f84be4f1..f36c51ca 100644 --- a/examples/vertex_layout.rs +++ b/examples/vertex_layout.rs @@ -93,10 +93,10 @@ fn main() -> anyhow::Result<()> { } fn draw_triangle(frame: &mut FrameContext, pipeline: &GraphicPipeline, vertex_buf: &Arc) { - let vertex_buf = frame.render_graph.bind_node(vertex_buf); + let vertex_buf = frame.graph.bind_node(vertex_buf); frame - .render_graph + .graph .begin_cmd() .with_name("Triangle") .bind_pipeline(pipeline) diff --git a/examples/vr/src/main.rs b/examples/vr/src/main.rs index 4ba38ac0..3c018126 100644 --- a/examples/vr/src/main.rs +++ b/examples/vr/src/main.rs @@ -270,8 +270,8 @@ fn main() -> anyhow::Result<()> { continue; } - let mut render_graph = Graph::default(); - let depth_image = render_graph.bind_node( + let mut graph = Graph::default(); + let depth_image = graph.bind_node( pool.lease(ImageInfo::image_2d_array( resolution.width, resolution.height, @@ -284,7 +284,7 @@ fn main() -> anyhow::Result<()> { let swapchain_image_index = swapchain.acquire_image().unwrap(); let swapchain_image = Swapchain::image(&swapchain, swapchain_image_index as _); - let swapchain_image = render_graph.bind_node(swapchain_image); + let swapchain_image = graph.bind_node(swapchain_image); // Get the XR views and copy them into a leased uniform buffer let (_, views) = session.locate_views( @@ -300,7 +300,7 @@ fn main() -> anyhow::Result<()> { vk::BufferUsageFlags::UNIFORM_BUFFER, ))?; Buffer::copy_from_slice(&mut buf, 0, data); - render_graph.bind_node(buf) + graph.bind_node(buf) }; session.sync_actions(&[(&action_set).into()]).unwrap(); @@ -339,21 +339,21 @@ fn main() -> anyhow::Result<()> { vk::BufferUsageFlags::UNIFORM_BUFFER, ))?; Buffer::copy_from_slice(&mut buf, 0, data); - render_graph.bind_node(buf) + graph.bind_node(buf) }; - render_graph.clear_color_image(swapchain_image, [0x00, 0x00, 0x00, 0xff]); + graph.clear_color_image(swapchain_image, [0x00, 0x00, 0x00, 0xff]); if let Some(location) = left_hand_location { - let index_buf = render_graph.bind_node(&lincoln_hand_left.index_buf); - let vertex_buf = render_graph.bind_node(&lincoln_hand_left.vertex_buf); - let diffuse_texture = render_graph.bind_node(&lincoln_hand_left_diffuse); - let normal_texture = render_graph.bind_node(&lincoln_hand_left_normal); - let occlusion_texture = render_graph.bind_node(&lincoln_hand_left_occlusion); + let index_buf = graph.bind_node(&lincoln_hand_left.index_buf); + let vertex_buf = graph.bind_node(&lincoln_hand_left.vertex_buf); + let diffuse_texture = graph.bind_node(&lincoln_hand_left_diffuse); + let normal_texture = graph.bind_node(&lincoln_hand_left_normal); + let occlusion_texture = graph.bind_node(&lincoln_hand_left_occlusion); let model_transform = pose_transform(location.pose); let push_consts = PushConstants::new(model_transform); - render_graph + graph .begin_cmd() .with_name("Left hand") .bind_pipeline(hands_pipeline.hot()) @@ -390,15 +390,15 @@ fn main() -> anyhow::Result<()> { } if let Some(location) = right_hand_location { - let index_buf = render_graph.bind_node(&lincoln_hand_right.index_buf); - let vertex_buf = render_graph.bind_node(&lincoln_hand_right.vertex_buf); - let diffuse_texture = render_graph.bind_node(&lincoln_hand_right_diffuse); - let normal_texture = render_graph.bind_node(&lincoln_hand_right_normal); - let occlusion_texture = render_graph.bind_node(&lincoln_hand_right_occlusion); + let index_buf = graph.bind_node(&lincoln_hand_right.index_buf); + let vertex_buf = graph.bind_node(&lincoln_hand_right.vertex_buf); + let diffuse_texture = graph.bind_node(&lincoln_hand_right_diffuse); + let normal_texture = graph.bind_node(&lincoln_hand_right_normal); + let occlusion_texture = graph.bind_node(&lincoln_hand_right_occlusion); let model_transform = pose_transform(location.pose); let push_consts = PushConstants::new(model_transform); - render_graph + graph .begin_cmd() .with_name("Right hand") .bind_pipeline(hands_pipeline.hot()) @@ -435,13 +435,13 @@ fn main() -> anyhow::Result<()> { } { - let index_buf = render_graph.bind_node(&woolly_mammoth.index_buf); - let vertex_buf = render_graph.bind_node(&woolly_mammoth.vertex_buf); - let normal_texture = render_graph.bind_node(&woolly_mammoth_normal); - let occlusion_texture = render_graph.bind_node(&woolly_mammoth_occlusion); + let index_buf = graph.bind_node(&woolly_mammoth.index_buf); + let vertex_buf = graph.bind_node(&woolly_mammoth.vertex_buf); + let normal_texture = graph.bind_node(&woolly_mammoth_normal); + let occlusion_texture = graph.bind_node(&woolly_mammoth_occlusion); let push_consts = PushConstants::new(Mat4::IDENTITY); - render_graph + graph .begin_cmd() .with_name("Woolly Mammoth") .bind_pipeline(mammoth_pipeline.hot()) @@ -476,7 +476,7 @@ fn main() -> anyhow::Result<()> { // the image - afterwards we keep the submitted command buffer around (including all // in-flight resources) so that nothing is dropped until that image is actually done. swapchain.wait_image(xr::Duration::INFINITE).unwrap(); - let cmd_buf = render_graph + let cmd_buf = graph .resolve() .submit(&mut pool, queue_family_index as _, 0) .unwrap(); @@ -764,13 +764,13 @@ fn load_texture( ), )?); - let mut render_graph = Graph::default(); - let staging_buf = render_graph.bind_node(staging_buf); - let texture_image = render_graph.bind_node(&texture); - render_graph.copy_buffer_to_image(staging_buf, texture_image); + let mut graph = Graph::default(); + let staging_buf = graph.bind_node(staging_buf); + let texture_image = graph.bind_node(&texture); + graph.copy_buffer_to_image(staging_buf, texture_image); let queue_family_index = device_queue_family_index(device, vk::QueueFlags::TRANSFER).unwrap(); - render_graph + graph .resolve() .submit(&mut LazyPool::new(device), queue_family_index as _, 0)? .wait_until_executed()?; diff --git a/examples/vsm_omni.rs b/examples/vsm_omni.rs index e59fd8c9..a63041dc 100644 --- a/examples/vsm_omni.rs +++ b/examples/vsm_omni.rs @@ -177,19 +177,19 @@ fn main() -> anyhow::Result<()> { }; // Bind resources to the render graph of the current frame - let cube_mesh_index_buf = frame.render_graph.bind_node(&cube_mesh.index_buf); - let cube_mesh_vertex_buf = frame.render_graph.bind_node(&cube_mesh.vertex_buf); - let cube_shadow_index_buf = frame.render_graph.bind_node(&cube_shadow.index_buf); - let cube_shadow_vertex_buf = frame.render_graph.bind_node(&cube_shadow.vertex_buf); - let model_mesh_index_buf = frame.render_graph.bind_node(&model_mesh.index_buf); - let model_mesh_vertex_buf = frame.render_graph.bind_node(&model_mesh.vertex_buf); - let model_shadow_index_buf = frame.render_graph.bind_node(&model_shadow.index_buf); - let model_shadow_vertex_buf = frame.render_graph.bind_node(&model_shadow.vertex_buf); + let cube_mesh_index_buf = frame.graph.bind_node(&cube_mesh.index_buf); + let cube_mesh_vertex_buf = frame.graph.bind_node(&cube_mesh.vertex_buf); + let cube_shadow_index_buf = frame.graph.bind_node(&cube_shadow.index_buf); + let cube_shadow_vertex_buf = frame.graph.bind_node(&cube_shadow.vertex_buf); + let model_mesh_index_buf = frame.graph.bind_node(&model_mesh.index_buf); + let model_mesh_vertex_buf = frame.graph.bind_node(&model_mesh.vertex_buf); + let model_shadow_index_buf = frame.graph.bind_node(&model_shadow.index_buf); + let model_shadow_vertex_buf = frame.graph.bind_node(&model_shadow.vertex_buf); let camera_uniform_buf = frame - .render_graph + .graph .bind_node(lease_uniform_buffer(&mut pool, &camera).unwrap()); let light_uniform_buf = frame - .render_graph + .graph .bind_node(lease_uniform_buffer(&mut pool, &light).unwrap()); // Lease and bind a cube-compatible shadow 2D image array to the graph of the current frame @@ -209,15 +209,15 @@ fn main() -> anyhow::Result<()> { ) .unwrap(); let shadow_faces_info = shadow_faces_image.info; - let shadow_faces_node = frame.render_graph.bind_node(shadow_faces_image); + let shadow_faces_node = frame.graph.bind_node(shadow_faces_image); // Lease and bind a temporary image we'll use during blur passes let temp_image = frame - .render_graph + .graph .bind_node(pool.lease(shadow_faces_info).unwrap()); // Lastly we lease and bind depth images needed for rendering - let shadow_depth_image = frame.render_graph.bind_node( + let shadow_depth_image = frame.graph.bind_node( pool.lease(ImageInfo::image_2d_array( frame.width, frame.height, @@ -227,7 +227,7 @@ fn main() -> anyhow::Result<()> { )) .unwrap(), ); - let depth_image = frame.render_graph.bind_node( + let depth_image = frame.graph.bind_node( pool.lease(ImageInfo::image_2d( frame.width, frame.height, @@ -240,7 +240,7 @@ fn main() -> anyhow::Result<()> { // Hold tab to view a debug mode if input.key_held(KeyCode::Tab) { frame - .render_graph + .graph .begin_cmd() .with_name("DEBUG") .bind_pipeline(&debug_pipeline) @@ -274,7 +274,7 @@ fn main() -> anyhow::Result<()> { // Render the omni light point of view into the six-layer image we leased above if use_geometry_shader { frame - .render_graph + .graph .begin_cmd() .with_name("Shadow (Using geometry shader)") .bind_pipeline(&shadow_pipeline) @@ -315,11 +315,11 @@ fn main() -> anyhow::Result<()> { }; let light_uniform_buf = frame - .render_graph + .graph .bind_node(lease_uniform_buffer(&mut pool, &light).unwrap()); frame - .render_graph + .graph .begin_cmd() .with_name("Shadow") .bind_pipeline(&shadow_pipeline) @@ -360,7 +360,7 @@ fn main() -> anyhow::Result<()> { // Flip-flop between the shadow image and a temporary image using a // separable box blur filter which approximates a gaussian blur frame - .render_graph + .graph .begin_cmd() .with_name("Blur X") .bind_pipeline(&blur_x_pipeline) @@ -383,7 +383,7 @@ fn main() -> anyhow::Result<()> { // Render the scene directly to the swapchain using the shadow map from the above pass frame - .render_graph + .graph .begin_cmd() .with_name("Mesh objects") .bind_pipeline(&mesh_pipeline) diff --git a/src/cmd_ref/accel_struct.rs b/src/cmd_ref/accel_struct.rs index 5f202e39..62fa4192 100644 --- a/src/cmd_ref/accel_struct.rs +++ b/src/cmd_ref/accel_struct.rs @@ -1,5 +1,5 @@ use { - super::Bindings, + super::Nodes, crate::{ AnyAccelerationStructureNode, driver::{ @@ -37,15 +37,15 @@ use { /// # let mut my_graph = Graph::default(); /// # let info = AccelerationStructureInfo::blas(1); /// my_graph.begin_cmd() -/// .record_accel_struct(move |accel_struct, bindings| { +/// .record_accel_struct(move |accel_struct, nodes| { /// // During this closure we have access to the build and update methods /// }); /// # Ok(()) } /// ``` pub struct AccelerationStructureRef<'a> { - pub(super) bindings: Bindings<'a>, pub(super) cmd_buf: vk::CommandBuffer, pub(super) device: &'a Device, + pub(super) nodes: Nodes<'a>, } impl AccelerationStructureRef<'_> { @@ -97,20 +97,20 @@ impl AccelerationStructureRef<'_> { /// .read_node(vertex_node) /// .write_node(blas_node) /// .write_node(scratch_buf) - /// .record_accel_struct(move |accel_struct, bindings| { - /// let scratch_addr = bindings[scratch_buf].device_address(); + /// .record_accel_struct(move |accel_struct, nodes| { + /// let scratch_addr = nodes[scratch_buf].device_address(); /// let geom = AccelerationStructureGeometry { /// max_primitive_count: 64, /// flags: vk::GeometryFlagsKHR::OPAQUE, /// geometry: AccelerationStructureGeometryData::Triangles { /// index_addr: DeviceOrHostAddress::DeviceAddress( - /// bindings[index_node].device_address() + /// nodes[index_node].device_address() /// ), /// index_type: vk::IndexType::UINT32, /// max_vertex: 42, /// transform_addr: None, /// vertex_addr: DeviceOrHostAddress::DeviceAddress( - /// bindings[vertex_node].device_address(), + /// nodes[vertex_node].device_address(), /// ), /// vertex_format: vk::Format::R32G32B32_SFLOAT, /// vertex_stride: 12, @@ -180,7 +180,7 @@ impl AccelerationStructureRef<'_> { .ty(info.build_data.ty) .flags(info.build_data.flags) .mode(vk::BuildAccelerationStructureModeKHR::BUILD) - .dst_acceleration_structure(self.bindings[info.accel_struct].handle) + .dst_acceleration_structure(self.nodes[info.accel_struct].handle) .geometries(&tls.geometries[start..end]) .scratch_data(info.scratch_addr.into()), ); @@ -255,7 +255,7 @@ impl AccelerationStructureRef<'_> { .ty(info.build_data.ty) .flags(info.build_data.flags) .mode(vk::BuildAccelerationStructureModeKHR::BUILD) - .dst_acceleration_structure(self.bindings[info.accel_struct].handle) + .dst_acceleration_structure(self.nodes[info.accel_struct].handle) .geometries(&tls.geometries[start..end]) .scratch_data(info.scratch_data.into()), ); @@ -346,8 +346,8 @@ impl AccelerationStructureRef<'_> { .ty(info.update_data.ty) .flags(info.update_data.flags) .mode(vk::BuildAccelerationStructureModeKHR::UPDATE) - .dst_acceleration_structure(self.bindings[info.dst_accel_struct].handle) - .src_acceleration_structure(self.bindings[info.src_accel_struct].handle) + .dst_acceleration_structure(self.nodes[info.dst_accel_struct].handle) + .src_acceleration_structure(self.nodes[info.src_accel_struct].handle) .geometries(&tls.geometries[start..end]) .scratch_data(info.scratch_addr.into()), ); @@ -422,8 +422,8 @@ impl AccelerationStructureRef<'_> { .ty(info.update_data.ty) .flags(info.update_data.flags) .mode(vk::BuildAccelerationStructureModeKHR::UPDATE) - .src_acceleration_structure(self.bindings[info.src_accel_struct].handle) - .dst_acceleration_structure(self.bindings[info.dst_accel_struct].handle) + .src_acceleration_structure(self.nodes[info.src_accel_struct].handle) + .dst_acceleration_structure(self.nodes[info.dst_accel_struct].handle) .geometries(&tls.geometries[start..end]) .scratch_data(info.scratch_addr.into()), ); diff --git a/src/cmd_ref/compute.rs b/src/cmd_ref/compute.rs index 29020da4..ea210f80 100644 --- a/src/cmd_ref/compute.rs +++ b/src/cmd_ref/compute.rs @@ -1,5 +1,5 @@ use { - super::{Bindings, pipeline::PipelineRef}, + super::{Nodes, pipeline::PipelineRef}, crate::{ AnyBufferNode, driver::{compute::ComputePipeline, device::Device}, @@ -34,15 +34,15 @@ use { /// # let mut my_graph = Graph::default(); /// my_graph.begin_cmd().with_name("my compute pass") /// .bind_pipeline(&my_compute_pipeline) -/// .record_pipeline(move |compute, bindings| { +/// .record_pipeline(move |compute, nodes| { /// // During this closure we have access to the compute methods! /// }); /// # Ok(()) } /// ``` pub struct ComputePipelineRef<'a> { - pub(super) bindings: Bindings<'a>, pub(super) cmd_buf: vk::CommandBuffer, pub(super) device: &'a Device, + pub(super) nodes: Nodes<'a>, pub(super) pipeline: ComputePipeline, } @@ -91,7 +91,7 @@ impl ComputePipelineRef<'_> { /// my_graph.begin_cmd().with_name("fill my_buf_node with data") /// .bind_pipeline(&my_compute_pipeline) /// .write_descriptor(0, my_buf_node) - /// .record_pipeline(move |compute, bindings| { + /// .record_pipeline(move |compute, nodes| { /// compute.dispatch(128, 64, 32); /// }); /// # Ok(()) } @@ -192,7 +192,7 @@ impl ComputePipelineRef<'_> { /// .bind_pipeline(&my_compute_pipeline) /// .read_node(args_buf_node) /// .write_descriptor(0, my_buf_node) - /// .record_pipeline(move |compute, bindings| { + /// .record_pipeline(move |compute, nodes| { /// compute.dispatch_indirect(args_buf_node, 0); /// }); /// # Ok(()) } @@ -211,7 +211,7 @@ impl ComputePipelineRef<'_> { unsafe { self.device.cmd_dispatch_indirect( self.cmd_buf, - self.bindings[args_buf].handle, + self.nodes[args_buf].handle, args_offset, ); } @@ -271,7 +271,7 @@ impl ComputePipelineRef<'_> { /// # let mut my_graph = Graph::default(); /// my_graph.begin_cmd().with_name("compute the ultimate question") /// .bind_pipeline(&my_compute_pipeline) - /// .record_pipeline(move |compute, bindings| { + /// .record_pipeline(move |compute, nodes| { /// compute.push_constants(0, &[42]) /// .dispatch(1, 1, 1); /// }); @@ -315,7 +315,7 @@ impl PipelineRef<'_, ComputePipeline> { /// Begin recording a compute pipeline command buffer. pub fn record_pipeline( mut self, - func: impl FnOnce(ComputePipelineRef<'_>, Bindings<'_>) + Send + 'static, + func: impl FnOnce(ComputePipelineRef<'_>, Nodes<'_>) + Send + 'static, ) -> Self { let pipeline = self .cmd @@ -329,15 +329,15 @@ impl PipelineRef<'_, ComputePipeline> { .unwrap_compute() .clone(); - self.cmd.push_execute(move |device, cmd_buf, bindings| { + self.cmd.push_execute(move |device, cmd_buf, nodes| { func( ComputePipelineRef { - bindings, + nodes, cmd_buf, device, pipeline, }, - bindings, + nodes, ); }); diff --git a/src/cmd_ref/graphic.rs b/src/cmd_ref/graphic.rs index 99b36b97..fb7ceeb2 100644 --- a/src/cmd_ref/graphic.rs +++ b/src/cmd_ref/graphic.rs @@ -1,5 +1,5 @@ use { - super::{AttachmentIndex, Bindings, Info, PipelineRef, Subresource, SubresourceAccess}, + super::{AttachmentIndex, Info, Nodes, PipelineRef, Subresource, SubresourceAccess}, crate::{ AnyBufferNode, AnyImageNode, Area, Attachment, ClearColorValue, Node, NodeIndex, SampleCount, @@ -51,15 +51,15 @@ use { /// my_graph.begin_cmd().with_name("my draw pass") /// .bind_pipeline(&my_graphic_pipeline) /// .store_color(0, swapchain_image) -/// .record_pipeline(move |graphic, bindings| { +/// .record_pipeline(move |graphic, nodes| { /// // During this closure we have access to the draw methods! /// }); /// # Ok(()) } /// ``` pub struct GraphicPipelineRef<'a> { - pub(super) bindings: Bindings<'a>, pub(super) cmd_buf: vk::CommandBuffer, pub(super) device: &'a Device, + pub(super) nodes: Nodes<'a>, pub(super) pipeline: GraphicPipeline, } @@ -104,7 +104,7 @@ impl GraphicPipelineRef<'_> { /// .store_color(0, swapchain_image) /// .read_node(my_idx_buf) /// .read_node(my_vtx_buf) - /// .record_pipeline(move |pipeline, bindings| { + /// .record_pipeline(move |pipeline, nodes| { /// pipeline.bind_index_buffer(my_idx_buf, 0, vk::IndexType::UINT16) /// .bind_vertex_buffer(0, my_vtx_buf, 0) /// .draw_indexed(42, 1, 0, 0, 0); @@ -123,7 +123,7 @@ impl GraphicPipelineRef<'_> { unsafe { self.device.cmd_bind_index_buffer( self.cmd_buf, - self.bindings[buffer].handle, + self.nodes[buffer].handle, offset, index_ty, ); @@ -167,7 +167,7 @@ impl GraphicPipelineRef<'_> { /// .bind_pipeline(&my_graphic_pipeline) /// .store_color(0, swapchain_image) /// .read_node(my_vtx_buf) - /// .record_pipeline(move |pipeline, bindings| { + /// .record_pipeline(move |pipeline, nodes| { /// pipeline.bind_vertex_buffer(0, my_vtx_buf, 0) /// .draw(42, 1, 0, 0); /// }); @@ -186,7 +186,7 @@ impl GraphicPipelineRef<'_> { self.device.cmd_bind_vertex_buffers( self.cmd_buf, binding, - slice::from_ref(&self.bindings[buffer].handle), + slice::from_ref(&self.nodes[buffer].handle), slice::from_ref(&offset), ); } @@ -221,7 +221,7 @@ impl GraphicPipelineRef<'_> { for (buffer, offset) in buffer_offsets { let buffer = buffer.into(); - buffers.push(self.bindings[buffer].handle); + buffers.push(self.nodes[buffer].handle); offsets.push(offset); } @@ -358,7 +358,7 @@ impl GraphicPipelineRef<'_> { /// .read_node(my_idx_buf) /// .read_node(my_vtx_buf) /// .read_node(buf_node) - /// .record_pipeline(move |pipeline, bindings| { + /// .record_pipeline(move |pipeline, nodes| { /// pipeline.bind_index_buffer(my_idx_buf, 0, vk::IndexType::UINT16) /// .bind_vertex_buffer(0, my_vtx_buf, 0) /// .draw_indexed_indirect(buf_node, 0, 1, 0); @@ -378,7 +378,7 @@ impl GraphicPipelineRef<'_> { unsafe { self.device.cmd_draw_indexed_indirect( self.cmd_buf, - self.bindings[buffer].handle, + self.nodes[buffer].handle, offset, draw_count, stride, @@ -416,9 +416,9 @@ impl GraphicPipelineRef<'_> { unsafe { self.device.cmd_draw_indexed_indirect_count( self.cmd_buf, - self.bindings[buffer].handle, + self.nodes[buffer].handle, offset, - self.bindings[count_buf].handle, + self.nodes[count_buf].handle, count_buf_offset, max_draw_count, stride, @@ -444,7 +444,7 @@ impl GraphicPipelineRef<'_> { unsafe { self.device.cmd_draw_indirect( self.cmd_buf, - self.bindings[buffer].handle, + self.nodes[buffer].handle, offset, draw_count, stride, @@ -473,9 +473,9 @@ impl GraphicPipelineRef<'_> { unsafe { self.device.cmd_draw_indirect_count( self.cmd_buf, - self.bindings[buffer].handle, + self.nodes[buffer].handle, offset, - self.bindings[count_buf].handle, + self.nodes[count_buf].handle, count_buf_offset, max_draw_count, stride, @@ -543,7 +543,7 @@ impl GraphicPipelineRef<'_> { /// my_graph.begin_cmd().with_name("draw a quad") /// .bind_pipeline(&my_graphic_pipeline) /// .store_color(0, swapchain_image) - /// .record_pipeline(move |pipeline, bindings| { + /// .record_pipeline(move |pipeline, nodes| { /// pipeline.push_constants(0, &[42]) /// .draw(6, 1, 0, 0); /// }); @@ -1468,7 +1468,7 @@ impl PipelineRef<'_, GraphicPipeline> { /// Begin recording a graphics pipeline command buffer. pub fn record_pipeline( mut self, - func: impl FnOnce(GraphicPipelineRef<'_>, Bindings<'_>) + Send + 'static, + func: impl FnOnce(GraphicPipelineRef<'_>, Nodes<'_>) + Send + 'static, ) -> Self { let pipeline = self .cmd @@ -1482,15 +1482,15 @@ impl PipelineRef<'_, GraphicPipeline> { .unwrap_graphic() .clone(); - self.cmd.push_execute(move |device, cmd_buf, bindings| { + self.cmd.push_execute(move |device, cmd_buf, nodes| { func( GraphicPipelineRef { - bindings, + nodes, cmd_buf, device, pipeline, }, - bindings, + nodes, ); }); diff --git a/src/cmd_ref/mod.rs b/src/cmd_ref/mod.rs index 89371355..6138ea78 100644 --- a/src/cmd_ref/mod.rs +++ b/src/cmd_ref/mod.rs @@ -138,149 +138,6 @@ bind!(Compute); bind!(Graphic); bind!(RayTrace); -/// An indexable structure will provides access to Vulkan smart-pointer resources inside a record -/// closure. -/// -/// This type is available while recording commands in the following closures: -/// -/// - [`PassRef::record_accel_struct`] for building and updating acceleration structures -/// - [`PassRef::record_cmd_buf`] for general command streams -/// - [`PipelineCommandRef::record_pipeline`] for dispatched compute operations -/// - [`PipelineCommandRef::record_pipeline`] for raster drawing operations, such as triangles streams -/// - [`PipelineCommandRef::record_ray_trace`] for ray-traced operations -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```no_run -/// # use std::sync::Arc; -/// # use ash::vk; -/// # use vk_graph::driver::DriverError; -/// # use vk_graph::driver::device::{Device, DeviceInfo}; -/// # use vk_graph::driver::image::{Image, ImageInfo}; -/// # use vk_graph::Graph; -/// # use vk_graph::node::ImageNode; -/// # fn main() -> Result<(), DriverError> { -/// # let device = Device::new(DeviceInfo::default())?; -/// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); -/// # let image = Image::create(&device, info)?; -/// # let mut my_graph = Graph::default(); -/// # let my_image_node = my_graph.bind_node(image); -/// my_graph.begin_cmd().with_name("custom vulkan commands") -/// .record_cmd_buf(move |device, cmd_buf, bindings| { -/// let my_image = &bindings[my_image_node]; -/// -/// assert_ne!(my_image.handle, vk::Image::null()); -/// assert_eq!(my_image.info.width, 32); -/// }); -/// # Ok(()) } -/// ``` -#[derive(Clone, Copy, Debug)] -pub struct Bindings<'a> { - bindings: &'a [Binding], - exec: &'a Execution, -} - -impl<'a> Bindings<'a> { - pub(super) fn new(bindings: &'a [Binding], exec: &'a Execution) -> Self { - Self { bindings, exec } - } - - fn binding_ref(&self, node_idx: usize) -> &Binding { - // You must have called read or write for this node on this execution before indexing - // into the bindings data! - debug_assert!( - self.exec.accesses.contains_key(&node_idx), - "unexpected node access: call access, read, or write first" - ); - - &self.bindings[node_idx] - } -} - -macro_rules! index { - ($name:ident, $handle:ident) => { - paste::paste! { - impl<'a> Index<[<$name Node>]> for Bindings<'a> - { - type Output = $handle; - - fn index(&self, node: [<$name Node>]) -> &Self::Output { - &*self.binding_ref(node.idx).[]().unwrap() - } - } - } - }; -} - -// Allow indexing the Bindings data during command execution: -// (This gets you access to the driver images or other resources) -index!(AccelerationStructure, AccelerationStructure); -index!(AccelerationStructureLease, AccelerationStructure); -index!(Buffer, Buffer); -index!(BufferLease, Buffer); -index!(Image, Image); -index!(ImageLease, Image); -index!(SwapchainImage, Image); - -impl Index for Bindings<'_> { - type Output = AccelerationStructure; - - fn index(&self, node: AnyAccelerationStructureNode) -> &Self::Output { - let node_idx = match node { - AnyAccelerationStructureNode::AccelerationStructure(node) => node.idx, - AnyAccelerationStructureNode::AccelerationStructureLease(node) => node.idx, - }; - let binding = self.binding_ref(node_idx); - - match node { - AnyAccelerationStructureNode::AccelerationStructure(_) => { - binding.as_acceleration_structure().unwrap() - } - AnyAccelerationStructureNode::AccelerationStructureLease(_) => { - binding.as_acceleration_structure_lease().unwrap() - } - } - } -} - -impl Index for Bindings<'_> { - type Output = Buffer; - - fn index(&self, node: AnyBufferNode) -> &Self::Output { - let node_idx = match node { - AnyBufferNode::Buffer(node) => node.idx, - AnyBufferNode::BufferLease(node) => node.idx, - }; - let binding = self.binding_ref(node_idx); - - match node { - AnyBufferNode::Buffer(_) => binding.as_buffer().unwrap(), - AnyBufferNode::BufferLease(_) => binding.as_buffer_lease().unwrap(), - } - } -} - -impl Index for Bindings<'_> { - type Output = Image; - - fn index(&self, node: AnyImageNode) -> &Self::Output { - let node_idx = match node { - AnyImageNode::Image(node) => node.idx, - AnyImageNode::ImageLease(node) => node.idx, - AnyImageNode::SwapchainImage(node) => node.idx, - }; - let binding = self.binding_ref(node_idx); - - match node { - AnyImageNode::Image(_) => binding.as_image().unwrap(), - AnyImageNode::ImageLease(_) => binding.as_image_lease().unwrap(), - AnyImageNode::SwapchainImage(_) => binding.as_swapchain_image().unwrap(), - } - } -} - /// A general render pass which may contain acceleration structure commands, general commands, or /// have pipeline bound to then record commands specific to those pipeline types. pub struct CommandRef<'a> { @@ -430,7 +287,7 @@ impl<'a> CommandRef<'a> { fn push_execute( &mut self, - func: impl FnOnce(&Device, vk::CommandBuffer, Bindings<'_>) + Send + 'static, + func: impl FnOnce(&Device, vk::CommandBuffer, Nodes<'_>) + Send + 'static, ) { let pass = self.as_mut(); let exec = { @@ -493,16 +350,16 @@ impl<'a> CommandRef<'a> { /// This is the entry point for building and updating an [`AccelerationStructure`] instance. pub fn record_accel_struct( mut self, - func: impl FnOnce(AccelerationStructureRef<'_>, Bindings<'_>) + Send + 'static, + func: impl FnOnce(AccelerationStructureRef<'_>, Nodes<'_>) + Send + 'static, ) -> Self { - self.push_execute(move |device, cmd_buf, bindings| { + self.push_execute(move |device, cmd_buf, nodes| { func( AccelerationStructureRef { - bindings, + nodes, cmd_buf, device, }, - bindings, + nodes, ); }); @@ -515,7 +372,7 @@ impl<'a> CommandRef<'a> { /// code and interfaces. pub fn record_cmd_buf( mut self, - func: impl FnOnce(&Device, vk::CommandBuffer, Bindings<'_>) + Send + 'static, + func: impl FnOnce(&Device, vk::CommandBuffer, Nodes<'_>) + Send + 'static, ) -> Self { self.push_execute(func); @@ -619,6 +476,161 @@ impl From<(DescriptorSetIndex, BindingIndex, [BindingOffset; 1])> for Descriptor } } +/// An indexable structure will provides access to Vulkan resources inside a command closure. +/// +/// This type is available while recording commands in the following closures: +/// +/// - [`PassRef::record_accel_struct`] for building and updating acceleration structures +/// - [`PassRef::record_cmd_buf`] for general command streams +/// - [`PipelineRef::record_pipeline`] for dispatched compute operations +/// - [`PipelineRef::record_pipeline`] for raster drawing operations, such as triangle streams +/// - [`PipelineRef::record_pipeline`] for ray-traced operations +/// +/// # Examples +/// +/// Basic usage: +/// +/// ```no_run +/// # use std::sync::Arc; +/// # use ash::vk; +/// # use vk_graph::driver::DriverError; +/// # use vk_graph::driver::device::{Device, DeviceInfo}; +/// # use vk_graph::driver::image::{Image, ImageInfo}; +/// # use vk_graph::Graph; +/// # use vk_graph::node::ImageNode; +/// # fn main() -> Result<(), DriverError> { +/// # let device = Device::new(DeviceInfo::default())?; +/// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); +/// # let image = Image::create(&device, info)?; +/// # let mut my_graph = Graph::default(); +/// # let my_image_node = my_graph.bind_node(image); +/// my_graph.begin_cmd().with_name("custom vulkan commands") +/// .record_cmd_buf(move |device, cmd_buf, nodes| { +/// let my_image: &Image = &nodes[my_image_node]; +/// +/// assert_ne!(my_image.handle, vk::Image::null()); +/// assert_eq!(my_image.info.width, 32); +/// }); +/// # Ok(()) } +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct Nodes<'a> { + bindings: &'a [Binding], + + #[cfg(debug_assertions)] + exec: &'a Execution, +} + +impl<'a> Nodes<'a> { + pub(super) fn new( + bindings: &'a [Binding], + #[cfg(debug_assertions)] exec: &'a Execution, + ) -> Self { + Self { + bindings, + #[cfg(debug_assertions)] + exec, + } + } + + fn binding(&self, node_idx: usize) -> &Binding { + // You must have called read or write for this node on this execution before indexing + // into the bindings data! + // + // Why: Code that attempts to access this function is attempting to get access to the Vulkan + // resource (buffer, image, or acceleration structure). In order to access any resources the + // access type must first be specified so the correct barriers may be added. + debug_assert!( + self.exec.accesses.contains_key(&node_idx), + "unexpected node access: call access, read, or write first" + ); + + &self.bindings[node_idx] + } +} + +macro_rules! index { + ($name:ident, $handle:ident) => { + paste::paste! { + impl<'a> Index<[<$name Node>]> for Nodes<'a> + { + type Output = $handle; + + fn index(&self, node: [<$name Node>]) -> &Self::Output { + &*self.binding(node.idx).[]().unwrap() + } + } + } + }; +} + +// Allow indexing the Nodes data during command execution: +// (This gets you access to the driver images or other resources) +index!(AccelerationStructure, AccelerationStructure); +index!(AccelerationStructureLease, AccelerationStructure); +index!(Buffer, Buffer); +index!(BufferLease, Buffer); +index!(Image, Image); +index!(ImageLease, Image); +index!(SwapchainImage, Image); + +impl Index for Nodes<'_> { + type Output = AccelerationStructure; + + fn index(&self, node: AnyAccelerationStructureNode) -> &Self::Output { + let node_idx = match node { + AnyAccelerationStructureNode::AccelerationStructure(node) => node.idx, + AnyAccelerationStructureNode::AccelerationStructureLease(node) => node.idx, + }; + let binding = self.binding(node_idx); + + match node { + AnyAccelerationStructureNode::AccelerationStructure(_) => { + binding.as_acceleration_structure().unwrap() + } + AnyAccelerationStructureNode::AccelerationStructureLease(_) => { + binding.as_acceleration_structure_lease().unwrap() + } + } + } +} + +impl Index for Nodes<'_> { + type Output = Buffer; + + fn index(&self, node: AnyBufferNode) -> &Self::Output { + let node_idx = match node { + AnyBufferNode::Buffer(node) => node.idx, + AnyBufferNode::BufferLease(node) => node.idx, + }; + let binding = self.binding(node_idx); + + match node { + AnyBufferNode::Buffer(_) => binding.as_buffer().unwrap(), + AnyBufferNode::BufferLease(_) => binding.as_buffer_lease().unwrap(), + } + } +} + +impl Index for Nodes<'_> { + type Output = Image; + + fn index(&self, node: AnyImageNode) -> &Self::Output { + let node_idx = match node { + AnyImageNode::Image(node) => node.idx, + AnyImageNode::ImageLease(node) => node.idx, + AnyImageNode::SwapchainImage(node) => node.idx, + }; + let binding = self.binding(node_idx); + + match node { + AnyImageNode::Image(_) => binding.as_image().unwrap(), + AnyImageNode::ImageLease(_) => binding.as_image_lease().unwrap(), + AnyImageNode::SwapchainImage(_) => binding.as_swapchain_image().unwrap(), + } + } +} + /// Describes a portion of a resource which is bound. #[derive(Clone, Copy, Debug)] pub enum Subresource { diff --git a/src/cmd_ref/ray_trace.rs b/src/cmd_ref/ray_trace.rs index 4fbc2106..bd191bcb 100644 --- a/src/cmd_ref/ray_trace.rs +++ b/src/cmd_ref/ray_trace.rs @@ -1,5 +1,5 @@ use { - super::{Bindings, PipelineRef}, + super::{Nodes, PipelineRef}, crate::driver::{device::Device, ray_trace::RayTracePipeline}, ash::vk, log::trace, @@ -10,7 +10,7 @@ impl PipelineRef<'_, RayTracePipeline> { /// Begin recording a ray trace pipeline command buffer. pub fn record_pipeline( mut self, - func: impl FnOnce(RayTracePipelineRef<'_>, Bindings<'_>) + Send + 'static, + func: impl FnOnce(RayTracePipelineRef<'_>, Nodes<'_>) + Send + 'static, ) -> Self { let pipeline = self .cmd @@ -27,7 +27,7 @@ impl PipelineRef<'_, RayTracePipeline> { #[cfg(debug_assertions)] let dynamic_stack_size = pipeline.inner.info.dynamic_stack_size; - self.cmd.push_execute(move |device, cmd_buf, bindings| { + self.cmd.push_execute(move |device, cmd_buf, nodes| { func( RayTracePipelineRef { cmd_buf, @@ -38,7 +38,7 @@ impl PipelineRef<'_, RayTracePipeline> { pipeline, }, - bindings, + nodes, ); }); @@ -75,7 +75,7 @@ impl PipelineRef<'_, RayTracePipeline> { /// # let mut my_graph = Graph::default(); /// my_graph.begin_cmd().with_name("my ray trace pass") /// .bind_pipeline(&my_ray_trace_pipeline) -/// .record_pipeline(move |pipeline, bindings| { +/// .record_pipeline(move |pipeline, nodes| { /// // During this closure we have access to the ray trace methods! /// }); /// # Ok(()) } @@ -150,7 +150,7 @@ impl RayTracePipelineRef<'_> { /// # let mut my_graph = Graph::default(); /// my_graph.begin_cmd().with_name("draw a cornell box") /// .bind_pipeline(&my_ray_trace_pipeline) - /// .record_pipeline(move |pipeline, bindings| { + /// .record_pipeline(move |pipeline, nodes| { /// pipeline.push_constants(0, &[0xcb]) /// .trace_rays(&rgen_sbt, &hit_sbt, &miss_sbt, &call_sbt, 320, 200, 1); /// }); @@ -242,7 +242,7 @@ impl RayTracePipelineRef<'_> { /// # let mut my_graph = Graph::default(); /// my_graph.begin_cmd().with_name("draw a cornell box") /// .bind_pipeline(&my_ray_trace_pipeline) - /// .record_pipeline(move |pipeline, bindings| { + /// .record_pipeline(move |pipeline, nodes| { /// pipeline.trace_rays(&rgen_sbt, &hit_sbt, &miss_sbt, &call_sbt, 320, 200, 1); /// }); /// # Ok(()) } diff --git a/src/display.rs b/src/display.rs index faecf299..15b79da0 100644 --- a/src/display.rs +++ b/src/display.rs @@ -142,18 +142,18 @@ impl Display { Ok(Some(swapchain_image)) } - /// Displays the given swapchain image using passes specified in `render_graph`, if possible. + /// Displays the given swapchain image using passes specified in `graph`, if possible. #[profiling::function] pub fn present_image( &mut self, pool: &mut impl ResolverPool, - render_graph: Graph, + graph: Graph, swapchain_image: SwapchainImageNode, queue_index: u32, ) -> Result<(), DisplayError> { trace!("present_image"); - let mut resolver = render_graph.resolve(); + let mut resolver = graph.resolve(); let wait_dst_stage_mask = resolver.node_pipeline_stages(swapchain_image); // The swapchain should have been written to, otherwise it would be noise and that's a panic diff --git a/src/lib.rs b/src/lib.rs index 11ae466c..1048a9b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -240,17 +240,17 @@ let buffer_node = graph.bind_node(buffer); let image_node = graph.bind_node(image); graph .begin_cmd().with_name("Do some raw Vulkan or interop with another Vulkan library") - .record_cmd_buf(move |device, cmd_buf, bindings| unsafe { + .record_cmd_buf(move |device, cmd_buf, nodes| unsafe { // I always run first! }) .read_node(buffer_node) // <-- These two functions, read_node/write_node, completely .write_node(image_node) // handle vulkan synchronization. - .record_cmd_buf(move |device, cmd_buf, bindings| unsafe { + .record_cmd_buf(move |device, cmd_buf, nodes| unsafe { // device is &ash::Device // cmd_buf is vk::CommandBuffer // bindings is a magical object you can retrieve the Vulkan resource from - let vk_buffer: vk::Buffer = bindings[buffer_node].handle; - let vk_image: vk::Image = bindings[image_node].handle; + let vk_buffer: vk::Buffer = nodes[buffer_node].handle; + let vk_image: vk::Image = nodes[image_node].handle; // You are free to READ vk_buffer and WRITE vk_image! }); @@ -368,7 +368,7 @@ pub use self::{ use { self::{ bind::Binding, - cmd_ref::{AttachmentIndex, Bindings, CommandRef, Descriptor, SubresourceAccess, ViewType}, + cmd_ref::{AttachmentIndex, CommandRef, Descriptor, Nodes, SubresourceAccess, ViewType}, edge::Edge, info::Info, node::Node, @@ -400,7 +400,7 @@ use { vk_sync::AccessType, }; -type ExecFn = Box) + Send>; +type ExecFn = Box) + Send>; type NodeIndex = usize; #[derive(Clone, Copy, Debug)] @@ -648,7 +648,7 @@ impl Command { /// The design of this code originated with a combination of /// [`PassBuilder`](https://github.com/EmbarkStudios/kajiya/blob/main/crates/lib/kajiya-rg/src/pass_builder.rs) /// and -/// [`render_graph.cpp`](https://github.com/Themaister/Granite/blob/master/renderer/render_graph.cpp). +/// [`graph.cpp`](https://github.com/Themaister/Granite/blob/master/renderer/graph.cpp). #[derive(Debug, Default)] pub struct Graph { bindings: Vec, @@ -767,9 +767,9 @@ impl Graph { ); } - pass.record_cmd_buf(move |device, cmd_buf, bindings| { - let src_image = bindings[src_node].handle; - let dst_image = bindings[dst_node].handle; + pass.record_cmd_buf(move |device, cmd_buf, nodes| { + let src_image = nodes[src_node].handle; + let dst_image = nodes[dst_node].handle; unsafe { device.cmd_blit_image( @@ -801,10 +801,10 @@ impl Graph { self.begin_cmd() .with_name("clear color") .access_node_subrange(image_node, AccessType::TransferWrite, image_view_info) - .record_cmd_buf(move |device, cmd_buf, bindings| unsafe { + .record_cmd_buf(move |device, cmd_buf, nodes| unsafe { device.cmd_clear_color_image( cmd_buf, - bindings[image_node].handle, + nodes[image_node].handle, vk::ImageLayout::TRANSFER_DST_OPTIMAL, &vk::ClearColorValue { float32: color_value.0, @@ -830,10 +830,10 @@ impl Graph { self.begin_cmd() .with_name("clear depth/stencil") .access_node_subrange(image_node, AccessType::TransferWrite, image_view_info) - .record_cmd_buf(move |device, cmd_buf, bindings| unsafe { + .record_cmd_buf(move |device, cmd_buf, nodes| unsafe { device.cmd_clear_depth_stencil_image( cmd_buf, - bindings[image_node].handle, + nodes[image_node].handle, vk::ImageLayout::TRANSFER_DST_OPTIMAL, &vk::ClearDepthStencilValue { depth, stencil }, &[image_view_info.into()], @@ -918,9 +918,9 @@ impl Graph { ); } - pass.record_cmd_buf(move |device, cmd_buf, bindings| { - let src_buf = bindings[src_node].handle; - let dst_buf = bindings[dst_node].handle; + pass.record_cmd_buf(move |device, cmd_buf, nodes| { + let src_buf = nodes[src_node].handle; + let dst_buf = nodes[dst_node].handle; unsafe { device.cmd_copy_buffer(cmd_buf, src_buf, dst_buf, regions.as_ref()); @@ -1005,9 +1005,9 @@ impl Graph { ); } - pass.record_cmd_buf(move |device, cmd_buf, bindings| { - let src_buf = bindings[src_node].handle; - let dst_image = bindings[dst_node].handle; + pass.record_cmd_buf(move |device, cmd_buf, nodes| { + let src_buf = nodes[src_node].handle; + let dst_image = nodes[dst_node].handle; unsafe { device.cmd_copy_buffer_to_image( @@ -1098,9 +1098,9 @@ impl Graph { ); } - pass.record_cmd_buf(move |device, cmd_buf, bindings| { - let src_image = bindings[src_node].handle; - let dst_image = bindings[dst_node].handle; + pass.record_cmd_buf(move |device, cmd_buf, nodes| { + let src_image = nodes[src_node].handle; + let dst_image = nodes[dst_node].handle; unsafe { device.cmd_copy_image( @@ -1194,9 +1194,9 @@ impl Graph { ); } - pass.record_cmd_buf(move |device, cmd_buf, bindings| { - let src_image = bindings[src_node].handle; - let dst_buf = bindings[dst_node].handle; + pass.record_cmd_buf(move |device, cmd_buf, nodes| { + let src_image = nodes[src_node].handle; + let dst_buf = nodes[dst_node].handle; unsafe { device.cmd_copy_image_to_buffer( @@ -1233,8 +1233,8 @@ impl Graph { self.begin_cmd() .with_name("fill buffer") .access_node_subrange(buffer_node, AccessType::TransferWrite, region.clone()) - .record_cmd_buf(move |device, cmd_buf, bindings| { - let buffer = bindings[buffer_node].handle; + .record_cmd_buf(move |device, cmd_buf, nodes| { + let buffer = nodes[buffer_node].handle; unsafe { device.cmd_fill_buffer( @@ -1336,8 +1336,8 @@ impl Graph { self.begin_cmd() .with_name("update buffer") .access_node_subrange(buffer_node, AccessType::TransferWrite, offset..data_end) - .record_cmd_buf(move |device, cmd_buf, bindings| { - let buffer = bindings[buffer_node].handle; + .record_cmd_buf(move |device, cmd_buf, nodes| { + let buffer = nodes[buffer_node].handle; unsafe { device.cmd_update_buffer(cmd_buf, buffer, offset, data.as_ref()); diff --git a/src/resolver.rs b/src/resolver.rs index 0a7f59ec..dfb716a6 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1,7 +1,7 @@ use { super::{ - Area, Attachment, Binding, Bindings, Command, ExecutionPipeline, Graph, Node, NodeIndex, - cmd_ref::{Subresource, SubresourceAccess}, + Area, Attachment, Binding, Command, ExecutionPipeline, Graph, Node, NodeIndex, + cmd_ref::{Nodes, Subresource, SubresourceAccess}, node::SwapchainImageNode, }, crate::{ @@ -2522,7 +2522,11 @@ impl Resolver { exec_func( &cmd_buf.device, cmd_buf.handle, - Bindings::new(&self.graph.bindings, exec), + Nodes::new( + &self.graph.bindings, + #[cfg(debug_assertions)] + exec, + ), ); } } From d284f031b6bfb4e4d12f06258f23c50e8e55390f Mon Sep 17 00:00:00 2001 From: John Wells Date: Sun, 22 Feb 2026 08:37:01 -0500 Subject: [PATCH 20/86] Clean up node binding/unbinding --- contrib/vk-graph-egui/src/lib.rs | 3 +- contrib/vk-graph-fx/src/image_loader.rs | 2 +- contrib/vk-graph-hot/src/shader.rs | 3 +- contrib/vk-graph-imgui/src/lib.rs | 29 +++--- contrib/vk-graph-prelude/src/lib.rs | 2 +- examples/aliasing.rs | 2 +- examples/app.rs | 3 +- examples/cpu_readback.rs | 12 +-- examples/min_max.rs | 4 +- examples/mip_compute.rs | 8 +- examples/multithread.rs | 39 ++++---- examples/ray_omni.rs | 2 +- examples/shader-toy/src/main.rs | 16 ++-- examples/subgroup_ops.rs | 4 +- src/bind.rs | 115 +++++++++--------------- src/cmd_ref/mod.rs | 2 +- src/driver/accel_struct.rs | 7 ++ src/driver/buffer.rs | 7 ++ src/driver/image.rs | 7 ++ src/lib.rs | 33 +++---- src/node.rs | 52 +++++------ src/pool/mod.rs | 27 ++++++ 22 files changed, 182 insertions(+), 197 deletions(-) diff --git a/contrib/vk-graph-egui/src/lib.rs b/contrib/vk-graph-egui/src/lib.rs index 8e44efdb..352bbc07 100644 --- a/contrib/vk-graph-egui/src/lib.rs +++ b/contrib/vk-graph-egui/src/lib.rs @@ -165,7 +165,6 @@ impl Egui { ); graph.copy_buffer_to_image(tmp_buf, image); - graph.unbind_node(tmp_buf); (*id, image) } }) @@ -194,7 +193,7 @@ impl Egui { for (id, tex) in bound_tex.iter() { if let AnyImageNode::ImageLease(tex) = tex { if let egui::TextureId::Managed(_) = *id { - self.textures.insert(*id, graph.unbind_node(*tex)); + self.textures.insert(*id, graph.node(*tex).clone()); } } } diff --git a/contrib/vk-graph-fx/src/image_loader.rs b/contrib/vk-graph-fx/src/image_loader.rs index 469abaf4..d88d40c3 100644 --- a/contrib/vk-graph-fx/src/image_loader.rs +++ b/contrib/vk-graph-fx/src/image_loader.rs @@ -234,7 +234,7 @@ impl ImageLoader { } } - let image = graph.unbind_node(image); + let image = graph.node(image).clone(); graph .resolve() diff --git a/contrib/vk-graph-hot/src/shader.rs b/contrib/vk-graph-hot/src/shader.rs index 15e02c2a..e222ed65 100644 --- a/contrib/vk-graph-hot/src/shader.rs +++ b/contrib/vk-graph-hot/src/shader.rs @@ -69,14 +69,13 @@ pub struct HotShader { /// ``` /// /// ```no_run - /// # use std::sync::Arc; /// # use ash::vk; /// # use vk_graph::driver::DriverError; /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::shader::{SpecializationInfo}; /// # use vk_graph_hot::shader::HotShader; /// # fn main() -> Result<(), DriverError> { - /// # let device = Arc::new(Device::new_headless(DeviceInfo::default())?); + /// # let device = Device::new(DeviceInfo::default())?; /// # let my_shader_code = [0u8; 1]; /// // We instead specify 42 for MY_COUNT: /// let shader = HotShader::new_fragment(my_shader_code.as_slice()) diff --git a/contrib/vk-graph-imgui/src/lib.rs b/contrib/vk-graph-imgui/src/lib.rs index 0f82ddbe..08577cbc 100644 --- a/contrib/vk-graph-imgui/src/lib.rs +++ b/contrib/vk-graph-imgui/src/lib.rs @@ -281,24 +281,21 @@ impl ImGui { } let temp_buf = graph.bind_node(temp_buf); - let image = graph.bind_node({ - let mut image = pool - .lease(ImageInfo::image_2d( - texture.width, - texture.height, - vk::Format::R8G8B8A8_UNORM, - vk::ImageUsageFlags::SAMPLED - | vk::ImageUsageFlags::STORAGE - | vk::ImageUsageFlags::TRANSFER_DST, - )) - .unwrap(); - image.as_mut().name = Some("ImGui Font Atlas".to_string()); - - image - }); + let image = graph.bind_node( + pool.lease(ImageInfo::image_2d( + texture.width, + texture.height, + vk::Format::R8G8B8A8_UNORM, + vk::ImageUsageFlags::SAMPLED + | vk::ImageUsageFlags::STORAGE + | vk::ImageUsageFlags::TRANSFER_DST, + )) + .unwrap() + .with_name("ImGui Font Atlas"), + ); graph.copy_buffer_to_image(temp_buf, image); - self.font_atlas_image = Some(graph.unbind_node(image)); + self.font_atlas_image = Some(graph.node(image).clone()); } } diff --git a/contrib/vk-graph-prelude/src/lib.rs b/contrib/vk-graph-prelude/src/lib.rs index e469d34c..0171c724 100644 --- a/contrib/vk-graph-prelude/src/lib.rs +++ b/contrib/vk-graph-prelude/src/lib.rs @@ -3,7 +3,7 @@ #![warn(missing_docs)] pub use vk_graph::{ - Bind, ClearColorValue, Graph, Unbind, + Bind, Bound, ClearColorValue, Graph, cmd_ref::{ BuildAccelerationStructureIndirectInfo, BuildAccelerationStructureInfo, CommandRef, PipelineRef, UpdateAccelerationStructureIndirectInfo, UpdateAccelerationStructureInfo, diff --git a/examples/aliasing.rs b/examples/aliasing.rs index 1128f95e..3248720b 100644 --- a/examples/aliasing.rs +++ b/examples/aliasing.rs @@ -18,7 +18,7 @@ fn main() -> Result<(), DriverError> { let args = Args::parse(); let device_info = DeviceInfoBuilder::default().debug(args.debug); - let device = Arc::new(Device::new(device_info)?); + let device = Device::new(device_info)?; // We wrap HashPool in an AliasPool container to enable resource aliasing let mut pool = AliasPool::new(HashPool::new(&device)); diff --git a/examples/app.rs b/examples/app.rs index 5abe305b..bec46ffc 100644 --- a/examples/app.rs +++ b/examples/app.rs @@ -3,7 +3,6 @@ mod profile_with_puffin; use { clap::Parser, log::error, - std::sync::Arc, vk_graph::{ Graph, display::{Display, DisplayError, DisplayInfo}, @@ -44,7 +43,7 @@ impl ApplicationHandler for Application { let args = Args::parse(); let device_info = DeviceInfoBuilder::default().debug(args.debug); - let device = Arc::new(Device::from_display(&window, device_info).unwrap()); + let device = Device::from_display(&window, device_info).unwrap(); let surface = Surface::create(&device, &window, &window).unwrap(); let surface_formats = Surface::formats(&surface).unwrap(); diff --git a/examples/cpu_readback.rs b/examples/cpu_readback.rs index d278ed4c..2e13f610 100644 --- a/examples/cpu_readback.rs +++ b/examples/cpu_readback.rs @@ -1,8 +1,4 @@ -use { - clap::Parser, - std::{sync::Arc, time::Instant}, - vk_graph_prelude::*, -}; +use {clap::Parser, std::time::Instant, vk_graph_prelude::*}; /// Example demonstrating the steps to take when reading the results of buffer or image operations /// on the CPU. These operations take time to submit and the GPU will execute them asynchronously. @@ -12,7 +8,7 @@ fn main() -> Result<(), DriverError> { // For this example we create a headless device, but the same thing works using a window let args = Args::parse(); let device_info = DeviceInfoBuilder::default().debug(args.debug); - let device = Arc::new(Device::new(device_info)?); + let device = Device::new(device_info)?; let mut graph = Graph::default(); @@ -34,8 +30,8 @@ fn main() -> Result<(), DriverError> { graph.copy_buffer(src_buf, dst_buf); // This line is optional - just bind a reference of Arc or a leased buffer so you retain - // the actual buffer for later use and you could then remove this unbind_node line - let dst_buf = graph.unbind_node(dst_buf); + // the actual buffer for later use and you could then remove this line + let dst_buf = graph.node(dst_buf).clone(); // Resolve and wait (or you can check has_executed without blocking) - alternatively you might // use device.queue_wait_idle(0) or device.device_wait_idle() - but those block on larger scopes diff --git a/examples/min_max.rs b/examples/min_max.rs index fa12590e..6223c36a 100644 --- a/examples/min_max.rs +++ b/examples/min_max.rs @@ -22,7 +22,7 @@ fn main() -> Result<(), DriverError> { let mut graph = Graph::default(); let args = Args::parse(); let device_info = DeviceInfoBuilder::default().debug(args.debug); - let device = Arc::new(Device::new(device_info)?); + let device = Device::new(device_info)?; let size = 4; // The 4x4 depth image will have pixels that look like this: @@ -228,7 +228,7 @@ fn copy_image_to_buffer( graph.copy_image_to_buffer(reduced_image, result_buf); - Ok(graph.unbind_node(result_buf)) + Ok(graph.node(result_buf).clone()) } #[derive(Parser)] diff --git a/examples/mip_compute.rs b/examples/mip_compute.rs index 837a2951..4bfa4308 100644 --- a/examples/mip_compute.rs +++ b/examples/mip_compute.rs @@ -1,8 +1,6 @@ mod profile_with_puffin; -use { - bytemuck::cast_slice, clap::Parser, std::sync::Arc, vk_graph_prelude::*, vk_shader_macros::glsl, -}; +use {bytemuck::cast_slice, clap::Parser, vk_graph_prelude::*, vk_shader_macros::glsl}; /// This program demonstrates a single render pass which uses multiple executions to record a chain /// of image copies which reduce an input image from 4x4 into 2x2 and finally 1x1. This is useful @@ -16,7 +14,7 @@ fn main() -> Result<(), DriverError> { let args = Args::parse(); let device_info = DeviceInfoBuilder::default().debug(args.debug); - let device = Arc::new(Device::new(device_info)?); + let device = Device::new(device_info)?; let mut graph = Graph::default(); @@ -140,7 +138,7 @@ fn main() -> Result<(), DriverError> { }, ); - let depth_pixel = graph.unbind_node(depth_pixel); + let depth_pixel = graph.node(depth_pixel).clone(); graph .resolve() diff --git a/examples/multithread.rs b/examples/multithread.rs index f760d6e4..525e6780 100644 --- a/examples/multithread.rs +++ b/examples/multithread.rs @@ -121,7 +121,7 @@ fn main() -> anyhow::Result<()> { ], ); - let image = graph.unbind_node(image); + let image = graph.node(image).clone(); // Submit on a queue we are reserving for only this thread to use graph @@ -222,8 +222,10 @@ fn load_font(device: &Device) -> anyhow::Result { OrdinateOrientation::TopToBottom, )?; + let mut graph = Graph::default(); + // We happen to know this font only requires a single image, this uses the image crate - let temp_buf = Buffer::create_from_slice( + let temp_buf = graph.bind_node(Buffer::create_from_slice( device, vk::BufferUsageFlags::TRANSFER_SRC, ImageReader::new(Cursor::new( @@ -234,30 +236,27 @@ fn load_font(device: &Device) -> anyhow::Result { .into_rgba8() .to_vec() .as_slice(), - )?; + )?); // This image will hold the font glyphs - let page_0 = Image::create( - device, - ImageInfo::image_2d( - 64, - 64, - vk::Format::R8G8B8A8_UNORM, - vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST, - ), - ) - .unwrap(); + let page_0 = graph.bind_node( + Image::create( + device, + ImageInfo::image_2d( + 64, + 64, + vk::Format::R8G8B8A8_UNORM, + vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST, + ), + ) + .unwrap(), + ); - let mut graph = Graph::default(); - let page_0 = graph.bind_node(page_0); - let temp_buf = graph.bind_node(temp_buf); graph.copy_buffer_to_image(temp_buf, page_0); - // Unbind page_0 to get the Arc but we could have just bound a reference (with no unbind) - let page_0 = graph.unbind_node(page_0); + let page_0 = graph.node(page_0).clone(); - // This copy happens in queue index 0! Notice the unbind above is OK because we already asked - // for the copy to happen first! + // This copy happens in queue index 0! graph.resolve().submit(&mut HashPool::new(device), 0, 0)?; BitmapFont::new(device, font, [page_0]) diff --git a/examples/ray_omni.rs b/examples/ray_omni.rs index d4b627b7..1d2d21e0 100644 --- a/examples/ray_omni.rs +++ b/examples/ray_omni.rs @@ -220,7 +220,7 @@ fn create_blas( )]); }); - let blas = graph.unbind_node(blas); + let blas = graph.node(blas).clone(); graph.resolve().submit(&mut LazyPool::new(device), 0, 0)?; diff --git a/examples/shader-toy/src/main.rs b/examples/shader-toy/src/main.rs index bf3533a0..6fb85582 100644 --- a/examples/shader-toy/src/main.rs +++ b/examples/shader-toy/src/main.rs @@ -167,9 +167,9 @@ fn main() -> anyhow::Result<()> { .clear_color_image(blank_image, [0.0, 0.0, 0.0, 1.0]) .clear_color_image(temp_image, [0.0, 1.0, 0.0, 1.0]); - let mut framebuffer_image_binding = Some(graph.unbind_node(framebuffer_image)); - let mut blank_image_binding = Some(graph.unbind_node(blank_image)); - let mut temp_image_binding = Some(graph.unbind_node(temp_image)); + let mut framebuffer_image_binding = Some(graph.node(framebuffer_image).clone()); + let mut blank_image_binding = Some(graph.node(blank_image).clone()); + let mut temp_image_binding = Some(graph.node(temp_image).clone()); graph.resolve().submit(&mut cache, 0, 0)?; @@ -310,11 +310,11 @@ fn main() -> anyhow::Result<()> { display.present_image(frame.graph, input, frame.swapchain_image); // Unbind things from this graph (we want them back for the next frame!) - flowers_image_binding = Some(frame.graph.unbind_node(flowers_image)); - noise_image_binding = Some(frame.graph.unbind_node(noise_image)); - framebuffer_image_binding = Some(frame.graph.unbind_node(framebuffer_image)); - blank_image_binding = Some(frame.graph.unbind_node(blank_image)); - temp_image_binding = Some(frame.graph.unbind_node(temp_image)); + flowers_image_binding = Some(frame.graph.node(flowers_image).clone()); + noise_image_binding = Some(frame.graph.node(noise_image).clone()); + framebuffer_image_binding = Some(frame.graph.node(framebuffer_image).clone()); + blank_image_binding = Some(frame.graph.node(blank_image).clone()); + temp_image_binding = Some(frame.graph.node(temp_image).clone()); }) .context("Unable to run event loop")?; diff --git a/examples/subgroup_ops.rs b/examples/subgroup_ops.rs index bdd7a041..0fbac447 100644 --- a/examples/subgroup_ops.rs +++ b/examples/subgroup_ops.rs @@ -31,7 +31,7 @@ fn main() -> Result<(), DriverError> { let args = Args::parse(); let device_info = DeviceInfoBuilder::default().debug(args.debug); - let device = Arc::new(Device::new(device_info)?); + let device = Device::new(device_info)?; let Vulkan11Properties { subgroup_size, subgroup_supported_operations, @@ -116,7 +116,7 @@ fn exclusive_sum( compute.dispatch(workgroup_count, 1, 1); }); - let output_buf = graph.unbind_node(output_buf); + let output_buf = graph.node(output_buf).clone(); let mut cmd_buf = graph.resolve().submit(&mut HashPool::new(device), 0, 0)?; let started = Instant::now(); diff --git a/src/bind.rs b/src/bind.rs index e8b628ba..e848a177 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -16,86 +16,58 @@ use { /// A trait for resources which may be bound to a `Graph`. /// /// See [`Graph::bind_node`] and -/// [`PassRef::bind_pipeline`](super::pass_ref::PassRef::bind_pipeline) for details. +/// [`CommandRef::bind_pipeline`](super::cmd_ref::CommandRef::bind_pipeline) for details. pub trait Bind { - /// Binds the resource to a graph-like object. + /// Binds the resource to a graph. /// - /// Returns a reference Node object. + /// Returns a node handle. fn bind(self, graph: Graph) -> Node; } #[derive(Debug)] pub enum Binding { - AccelerationStructure(Arc, bool), - AccelerationStructureLease(Arc>, bool), - Buffer(Arc, bool), - BufferLease(Arc>, bool), - Image(Arc, bool), - ImageLease(Arc>, bool), - SwapchainImage(Box, bool), + AccelerationStructure(Arc), + AccelerationStructureLease(Arc>), + Buffer(Arc), + BufferLease(Arc>), + Image(Arc), + ImageLease(Arc>), + SwapchainImage(Box), } impl Binding { pub(super) fn as_driver_acceleration_structure(&self) -> Option<&AccelerationStructure> { Some(match self { - Self::AccelerationStructure(binding, _) => binding, - Self::AccelerationStructureLease(binding, _) => binding, + Self::AccelerationStructure(binding) => binding, + Self::AccelerationStructureLease(binding) => binding, _ => return None, }) } pub(super) fn as_driver_buffer(&self) -> Option<&Buffer> { Some(match self { - Self::Buffer(binding, _) => binding, - Self::BufferLease(binding, _) => binding, + Self::Buffer(binding) => binding, + Self::BufferLease(binding) => binding, _ => return None, }) } pub(super) fn as_driver_image(&self) -> Option<&Image> { Some(match self { - Self::Image(binding, _) => binding, - Self::ImageLease(binding, _) => binding, - Self::SwapchainImage(binding, _) => binding, + Self::Image(binding) => binding, + Self::ImageLease(binding) => binding, + Self::SwapchainImage(binding) => binding, _ => return None, }) } pub(super) fn as_swapchain_image(&self) -> Option<&SwapchainImage> { - if let Self::SwapchainImage(binding, true) = self { - Some(binding) - } else if let Self::SwapchainImage(_, false) = self { - // User code might try this - but it is a programmer error - // to access a binding after it has been unbound so dont - None - } else { + let Self::SwapchainImage(binding) = self else { // The private code in this module should prevent this branch unreachable!(); - } - } - - pub(super) fn is_bound(&self) -> bool { - match self { - Self::AccelerationStructure(_, is_bound) => *is_bound, - Self::AccelerationStructureLease(_, is_bound) => *is_bound, - Self::Buffer(_, is_bound) => *is_bound, - Self::BufferLease(_, is_bound) => *is_bound, - Self::Image(_, is_bound) => *is_bound, - Self::ImageLease(_, is_bound) => *is_bound, - Self::SwapchainImage(_, is_bound) => *is_bound, - } - } + }; - pub(super) fn unbind(&mut self) { - *match self { - Self::AccelerationStructure(_, is_bound) => is_bound, - Self::AccelerationStructureLease(_, is_bound) => is_bound, - Self::Buffer(_, is_bound) => is_bound, - Self::BufferLease(_, is_bound) => is_bound, - Self::Image(_, is_bound) => is_bound, - Self::ImageLease(_, is_bound) => is_bound, - Self::SwapchainImage(_, is_bound) => is_bound, - } = false; + Some(binding) } } @@ -106,8 +78,7 @@ impl Bind<&mut Graph, SwapchainImageNode> for SwapchainImage { //trace!("Node {}: {:?}", res.idx, &self); - let binding = Binding::SwapchainImage(Box::new(self), true); - graph.bindings.push(binding); + graph.bindings.push(Binding::SwapchainImage(Box::new(self))); res } @@ -123,7 +94,7 @@ macro_rules! bind { // We will return a new node let res = [<$name Node>]::new(graph.bindings.len()); - let binding = Binding::$name(Arc::new(self), true); + let binding = Binding::$name(Arc::new(self)); graph.bindings.push(binding); res @@ -148,10 +119,8 @@ macro_rules! bind { // We will return an existing node, if possible // TODO: Could store a sorted list of these shared pointers to avoid the O(N) for (idx, existing_binding) in graph.bindings.iter_mut().enumerate() { - if let Some((existing_binding, is_bound)) = existing_binding.[]() { + if let Some(existing_binding) = existing_binding.[]() { if Arc::ptr_eq(existing_binding, &self) { - *is_bound = true; - return [<$name Node>]::new(idx); } } @@ -159,7 +128,7 @@ macro_rules! bind { // Return a new node let res = [<$name Node>]::new(graph.bindings.len()); - let binding = Binding::$name(self, true); + let binding = Binding::$name(self); graph.bindings.push(binding); res @@ -168,16 +137,16 @@ macro_rules! bind { impl Binding { pub(super) fn [](&self) -> Option<&Arc<$name>> { - if let Self::$name(binding, _) = self { + if let Self::$name(binding) = self { Some(&binding) } else { None } } - pub(super) fn [](&mut self) -> Option<(&mut Arc<$name>, &mut bool)> { - if let Self::$name(binding, is_bound) = self { - Some((binding, is_bound)) + pub(super) fn [](&mut self) -> Option<&mut Arc<$name>> { + if let Self::$name(binding) = self { + Some(binding) } else { None } @@ -202,7 +171,7 @@ macro_rules! bind_lease { // We will return a new node let res = [<$name LeaseNode>]::new(graph.bindings.len()); - let binding = Binding::[<$name Lease>](Arc::new(self), true); + let binding = Binding::[<$name Lease>](Arc::new(self)); graph.bindings.push(binding); res @@ -227,10 +196,8 @@ macro_rules! bind_lease { // We will return an existing node, if possible // TODO: Could store a sorted list of these shared pointers to avoid the O(N) for (idx, existing_binding) in graph.bindings.iter_mut().enumerate() { - if let Some((existing_binding, is_bound)) = existing_binding.[]() { + if let Some(existing_binding) = existing_binding.[]() { if Arc::ptr_eq(existing_binding, &self) { - *is_bound = true; - return [<$name LeaseNode>]::new(idx); } } @@ -238,7 +205,7 @@ macro_rules! bind_lease { // We will return a new node let res = [<$name LeaseNode>]::new(graph.bindings.len()); - let binding = Binding::[<$name Lease>](self, true); + let binding = Binding::[<$name Lease>](self); graph.bindings.push(binding); res @@ -247,16 +214,16 @@ macro_rules! bind_lease { impl Binding { pub(super) fn [](&self) -> Option<&Arc>> { - if let Self::[<$name Lease>](binding, _) = self { + if let Self::[<$name Lease>](binding) = self { Some(binding) } else { None } } - pub(super) fn [](&mut self) -> Option<(&Arc>, &mut bool)> { - if let Self::[<$name Lease>](binding, is_bound) = self { - Some((binding, is_bound)) + pub(super) fn [](&mut self) -> Option<&Arc>> { + if let Self::[<$name Lease>](binding) = self { + Some(binding) } else { None } @@ -270,12 +237,10 @@ bind_lease!(AccelerationStructure); bind_lease!(Image); bind_lease!(Buffer); -/// A trait for resources which may be unbound from a `Graph`. +/// A trait for resources which may be borrowed from a `Graph`. /// -/// See [`Graph::unbind_node`] for details. -pub trait Unbind { - /// Unbinds the resource from a graph. - /// - /// Returns the original Binding object. - fn unbind(self, graph: &mut Graph) -> Binding; +/// See [`Graph::node`] for details. +pub trait Bound { + /// Borrows the resource from a graph. + fn borrow(self, graph: &Graph) -> &Binding; } diff --git a/src/cmd_ref/mod.rs b/src/cmd_ref/mod.rs index 6138ea78..e87c7174 100644 --- a/src/cmd_ref/mod.rs +++ b/src/cmd_ref/mod.rs @@ -241,7 +241,7 @@ impl<'a> CommandRef<'a> { fn assert_bound_graph_node(&self, node: impl Node) { let idx = node.index(); - assert!(self.graph.bindings[idx].is_bound()); + assert!(idx < self.graph.bindings.len()); } /// Binds a Vulkan acceleration structure, buffer, or image to the graph associated with this diff --git a/src/driver/accel_struct.rs b/src/driver/accel_struct.rs index 413c74bf..79f4d1b4 100644 --- a/src/driver/accel_struct.rs +++ b/src/driver/accel_struct.rs @@ -337,6 +337,13 @@ impl AccelerationStructure { } }) } + + /// Sets the debugging name assigned to this acceleration structure. + pub fn with_name(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); + + self + } } impl Drop for AccelerationStructure { diff --git a/src/driver/buffer.rs b/src/driver/buffer.rs index 80c5d644..dbc380c0 100644 --- a/src/driver/buffer.rs +++ b/src/driver/buffer.rs @@ -450,6 +450,13 @@ impl Buffer { &mut self.allocation.mapped_slice_mut().unwrap()[0..self.info.size as usize] } + + /// Sets the debugging name assigned to this buffer. + pub fn with_name(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); + + self + } } impl Debug for Buffer { diff --git a/src/driver/image.rs b/src/driver/image.rs index 39f41385..0ee7a378 100644 --- a/src/driver/image.rs +++ b/src/driver/image.rs @@ -418,6 +418,13 @@ impl Image { } }) } + + /// Sets the debugging name assigned to this image. + pub fn with_name(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); + + self + } } impl Debug for Image { diff --git a/src/lib.rs b/src/lib.rs index 1048a9b2..7a7538ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -195,13 +195,9 @@ let image = graph.bind_node(image); println!("{:?}", buffer); // BufferNode println!("{:?}", image); // ImageNode -// Unbind nodes back into resources (Optional!) -let buffer = graph.unbind_node(buffer); -let image = graph.unbind_node(image); - -// Magically, they return to the correct types! (the graph wrapped them in Arc for us) -println!("{:?}", buffer); // Arc -println!("{:?}", image); // Arc +// Borrow resources using nodes (Optional!) +println!("{:?}", graph.node(buffer)); // &Arc +println!("{:?}", graph.node(image)); // &Arc # Ok(()) } ``` @@ -361,7 +357,7 @@ mod info; mod resolver; pub use self::{ - bind::{Bind, Unbind}, + bind::{Bind, Bound}, resolver::Resolver, }; @@ -1265,6 +1261,16 @@ impl Graph { None } + /// Returns a borrow of the original Vulkan resource (buffer, image or acceleration structure) + /// which the given node represents. + pub fn node(&mut self, node: N) -> &>::Result + where + N: Edge, + N: Bound>::Result>, + { + node.borrow(self) + } + /// Returns the device address of a buffer node. /// /// # Panics @@ -1300,17 +1306,6 @@ impl Graph { Resolver::new(self) } - /// Removes a node from this graph. - /// - /// Future access to `node` on this graph will return invalid results. - pub fn unbind_node(&mut self, node: N) -> >::Result - where - N: Edge, - N: Unbind>::Result>, - { - node.unbind(self) - } - /// Note: `data` must not exceed 65536 bytes. #[profiling::function] pub fn update_buffer( diff --git a/src/node.rs b/src/node.rs index 79547151..a1d0bd3c 100644 --- a/src/node.rs +++ b/src/node.rs @@ -1,7 +1,7 @@ //! Bindings for Vulkan smart-pointer resources. use { - super::{Binding, Graph, Info, NodeIndex, Unbind}, + super::{Binding, Bound, Graph, Info, NodeIndex}, crate::{ driver::{ accel_struct::{AccelerationStructure, AccelerationStructureInfo}, @@ -225,50 +225,40 @@ node!(Image); node!(ImageLease); node!(SwapchainImage); -macro_rules! node_unbind { +macro_rules! node_bound { ($name:ident) => { paste::paste! { - impl Unbind> for [<$name Node>] { - fn unbind(self, graph: &mut Graph) -> Arc<$name> { - let binding = &mut graph.bindings[self.idx]; - let res = Arc::clone( - binding - .[]() - .unwrap() - ); - binding.unbind(); - - res + impl Bound> for [<$name Node>] { + fn borrow(self, graph: &Graph) -> &Arc<$name> { + graph + .bindings[self.idx] + .[]() + .unwrap() } } } }; } -node_unbind!(AccelerationStructure); -node_unbind!(Buffer); -node_unbind!(Image); +node_bound!(AccelerationStructure); +node_bound!(Buffer); +node_bound!(Image); -macro_rules! node_unbind_lease { +macro_rules! node_lease_bound { ($name:ident) => { paste::paste! { - impl Unbind>> for [<$name LeaseNode>] { - fn unbind(self, graph: &mut Graph) -> Arc> { - let binding = &mut graph.bindings[self.idx]; - let res = Arc::clone( - binding - .[]() - .unwrap() - ); - binding.unbind(); - - res + impl Bound>> for [<$name LeaseNode>] { + fn borrow(self, graph: &Graph) -> &Arc> { + graph + .bindings[self.idx] + .[]() + .unwrap() } } } }; } -node_unbind_lease!(AccelerationStructure); -node_unbind_lease!(Buffer); -node_unbind_lease!(Image); +node_lease_bound!(AccelerationStructure); +node_lease_bound!(Buffer); +node_lease_bound!(Image); diff --git a/src/pool/mod.rs b/src/pool/mod.rs index e431040f..b80eb59f 100644 --- a/src/pool/mod.rs +++ b/src/pool/mod.rs @@ -147,6 +147,33 @@ pub struct Lease { item: ManuallyDrop, } +impl Lease { + /// Sets the debugging name assigned to this acceleration structure. + pub fn with_name(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); + + self + } +} + +impl Lease { + /// Sets the debugging name assigned to this buffer. + pub fn with_name(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); + + self + } +} + +impl Lease { + /// Sets the debugging name assigned to this image. + pub fn with_name(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); + + self + } +} + impl Lease { #[inline(always)] fn new(cache_ref: CacheRef, item: T) -> Self { From 0b003b7cb1c49ccc63fa11085dbf93167493e5ec Mon Sep 17 00:00:00 2001 From: John Wells Date: Wed, 25 Feb 2026 06:42:58 -0500 Subject: [PATCH 21/86] Rename and refactor command ref and related --- README.md | 10 +- contrib/vk-graph-egui/src/lib.rs | 44 +- contrib/vk-graph-fx/src/bitmap_font.rs | 30 +- contrib/vk-graph-fx/src/image_loader.rs | 20 +- contrib/vk-graph-fx/src/presenter.rs | 38 +- contrib/vk-graph-fx/src/transition.rs | 25 +- contrib/vk-graph-hot/examples/glsl.rs | 12 +- contrib/vk-graph-hot/examples/hlsl.rs | 6 +- contrib/vk-graph-imgui/src/lib.rs | 28 +- contrib/vk-graph-prelude/src/lib.rs | 4 +- contrib/vk-graph-window/src/frame.rs | 4 +- contrib/vk-graph-window/src/lib.rs | 2 +- examples/aliasing.rs | 29 +- examples/app.rs | 2 +- examples/bindless.rs | 25 +- examples/cpu_readback.rs | 8 +- examples/debugger.rs | 13 +- examples/egui.rs | 2 +- examples/font_bmp.rs | 19 +- examples/fuzzer.rs | 253 ++++----- examples/image_sampler.rs | 10 +- examples/imgui.rs | 2 +- examples/min_max.rs | 12 +- examples/mip_compute.rs | 10 +- examples/mip_graphic.rs | 21 +- examples/msaa.rs | 16 +- examples/multipass.rs | 50 +- examples/multithread.rs | 16 +- examples/ray_omni.rs | 72 +-- examples/ray_trace.rs | 80 +-- examples/rt_triangle.rs | 54 +- examples/shader-toy/src/main.rs | 28 +- examples/skeletal-anim/src/main.rs | 34 +- examples/subgroup_ops.rs | 53 +- examples/transitions.rs | 4 +- examples/triangle.rs | 10 +- examples/vertex_layout.rs | 6 +- examples/vr/src/main.rs | 86 ++-- examples/vsm_omni.rs | 175 +++---- src/bind.rs | 336 ++++++++---- src/cmd_ref/accel_struct.rs | 34 +- src/cmd_ref/bind.rs | 87 ++++ src/cmd_ref/compute.rs | 129 ++++- src/cmd_ref/graphic.rs | 433 +++++++++------- src/cmd_ref/mod.rs | 659 +++++++++++++----------- src/cmd_ref/pipeline.rs | 508 +++++------------- src/cmd_ref/ray_trace.rs | 112 +++- src/cmd_ref/view.rs | 124 ----- src/driver/accel_struct.rs | 6 +- src/driver/buffer.rs | 8 +- src/driver/compute.rs | 8 +- src/driver/graphic.rs | 8 +- src/driver/image.rs | 53 +- src/driver/ray_trace.rs | 10 +- src/driver/render_pass.rs | 2 +- src/driver/shader.rs | 61 ++- src/edge.rs | 17 +- src/info.rs | 37 -- src/lib.rs | 630 +++++++++++----------- src/node.rs | 139 +---- src/pool/mod.rs | 6 +- src/resolver.rs | 79 +-- 62 files changed, 2414 insertions(+), 2385 deletions(-) delete mode 100644 src/cmd_ref/view.rs delete mode 100644 src/info.rs diff --git a/README.md b/README.md index 62a630bf..f09bce52 100644 --- a/README.md +++ b/README.md @@ -46,14 +46,14 @@ Features of the render graph: ```rust graph .begin_cmd() - .with_name("Fancy new algorithm for shading a moving character who is actively on fire") + .debug_name("Fancy new algorithm for shading a moving character who is actively on fire") .bind_pipeline(&gfx_pipeline) - .read_descriptor(0, some_image) - .read_descriptor(1, another_image) - .read_descriptor(3, some_buf) + .shader_resource_access(0, prev_image, AccessType::FragmentShaderReadColorInputAttachment) + .shader_resource_access(1, some_image, AccessType::FragmentShaderReadOther) + .shader_resource_access(3, fire_buffer, Access::FragmentShaderReadUniformBuffer) .clear_color(0, swapchain_image) .store_color(0, swapchain_image) - .record_pipeline(move |pipeline| { + .record_pipeline(move |pipeline, _| { pipeline .push_constants(some_u8_slice) .draw(6, 1, 0, 0); diff --git a/contrib/vk-graph-egui/src/lib.rs b/contrib/vk-graph-egui/src/lib.rs index 352bbc07..c83f25f4 100644 --- a/contrib/vk-graph-egui/src/lib.rs +++ b/contrib/vk-graph-egui/src/lib.rs @@ -114,21 +114,20 @@ impl Egui { )) .unwrap(); Buffer::copy_from_slice(&mut buf, 0, cast_slice(&pixels)); - graph.bind_node(buf) + graph.bind_resource(buf) }; if let Some(pos) = delta.pos { - let image = AnyImageNode::ImageLease( + let image = graph.bind_resource( self.textures .remove(id) - .expect("Tried updating undefined texture.") - .bind(graph), + .expect("Tried updating undefined texture."), ); graph.copy_buffer_to_image_region( tmp_buf, image, - vk::BufferImageCopy { + [vk::BufferImageCopy { buffer_offset: 0, buffer_row_length: delta.image.width() as u32, buffer_image_height: delta.image.height() as u32, @@ -148,11 +147,11 @@ impl Egui { base_array_layer: 0, layer_count: 1, }, - }, + }], ); - (*id, image) + (*id, AnyImageNode::from(image)) } else { - let image = AnyImageNode::ImageLease( + let image = graph.bind_resource( self.cache .lease(ImageInfo::image_2d( delta.image.width() as u32, @@ -160,24 +159,23 @@ impl Egui { vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST, )) - .unwrap() - .bind(graph), + .unwrap(), ); graph.copy_buffer_to_image(tmp_buf, image); - (*id, image) + (*id, AnyImageNode::from(image)) } }) .collect::>(); // Bind the rest of the textures. for (id, image) in self.textures.drain() { - bound_tex.insert(id, AnyImageNode::ImageLease(graph.bind_node(image))); + bound_tex.insert(id, AnyImageNode::from(graph.bind_resource(image))); } // Add user textures. for (id, node) in self.user_textures.drain() { - bound_tex.insert(id, node); + bound_tex.insert(id, AnyImageNode::from(node)); } bound_tex @@ -193,7 +191,7 @@ impl Egui { for (id, tex) in bound_tex.iter() { if let AnyImageNode::ImageLease(tex) = tex { if let egui::TextureId::Managed(_) = *id { - self.textures.insert(*id, graph.node(*tex).clone()); + self.textures.insert(*id, graph.resource(*tex).clone()); } } } @@ -214,7 +212,7 @@ impl Egui { target: impl Into, ) { let target = target.into(); - let target_info = graph.node_info(target); + let target_info = graph.resource(target).info; for egui::ClippedPrimitive { clip_rect, primitive, @@ -240,7 +238,7 @@ impl Egui { Buffer::copy_from_slice(&mut buf, 0, cast_slice(&mesh.indices)); buf }; - let idx_buf = graph.bind_node(idx_buf); + let idx_buf = graph.bind_resource(idx_buf); let vert_buf = { let mut buf = self @@ -254,7 +252,7 @@ impl Egui { Buffer::copy_from_slice(&mut buf, 0, cast_slice(&mesh.vertices)); buf }; - let vert_buf = graph.bind_node(vert_buf); + let vert_buf = graph.bind_resource(vert_buf); #[repr(C)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] @@ -281,11 +279,15 @@ impl Egui { graph .begin_cmd() - .with_name("Egui pass") + .debug_name("Egui pass") .bind_pipeline(&self.ppl) - .access_node(idx_buf, AccessType::IndexBuffer) - .access_node(vert_buf, AccessType::VertexBuffer) - .access_descriptor((0, 0), *texture, AccessType::FragmentShaderReadOther) + .resource_access(idx_buf, AccessType::IndexBuffer) + .resource_access(vert_buf, AccessType::VertexBuffer) + .shader_resource_access( + (0, 0), + *texture, + AccessType::FragmentShaderReadOther, + ) .load_color(0, target) .store_color(0, target) .record_pipeline(move |pipeline, _| { diff --git a/contrib/vk-graph-fx/src/bitmap_font.rs b/contrib/vk-graph-fx/src/bitmap_font.rs index 913ede2a..c36d79ba 100644 --- a/contrib/vk-graph-fx/src/bitmap_font.rs +++ b/contrib/vk-graph-fx/src/bitmap_font.rs @@ -44,14 +44,9 @@ impl BitmapFont { [ Shader::new_vertex(include_glsl!("res/shader/graphic/font.vert").as_slice()), Shader::new_fragment(include_glsl!("res/shader/graphic/font.frag").as_slice()) - .specialization_info(SpecializationInfo::new( - [vk::SpecializationMapEntry { - constant_id: 0, - offset: 0, - size: 4, - }], - num_pages.to_ne_bytes(), - )), + .specialization( + SpecializationMap::new(num_pages.to_ne_bytes()).constant(0, 0, 4), + ), ], ) .context("Unable to create bitmap font pipeline")?; @@ -149,7 +144,7 @@ impl BitmapFont { let color = color.into(); let image = image.into(); let text = text.as_ref(); - let image_info = graph.node_info(image); + let image_info = graph.resource(image).info; let transform = Mat4::from_translation(vec3(-1.0, -1.0, 0.0)) * Mat4::from_scale(vec3(2.0 * scale, 2.0 * scale, 1.0)) * Mat4::from_translation(vec3( @@ -196,23 +191,28 @@ impl BitmapFont { } } - let vertex_buf = graph.bind_node(vertex_buf); + let vertex_buf = graph.bind_resource(vertex_buf); let mut page_nodes: Vec = Vec::with_capacity(self.pages.len()); for page in self.pages.iter() { - page_nodes.push(graph.bind_node(page)); + page_nodes.push(graph.bind_resource(page)); } let mut pass = graph .begin_cmd() - .with_name("text") + .debug_name("text") .bind_pipeline(&self.pipeline) - .access_node(vertex_buf, AccessType::IndexBuffer) + .resource_access(vertex_buf, AccessType::IndexBuffer) .load_color(0, image) .store_color(0, image); - for (idx, page_node) in page_nodes.iter().enumerate() { - pass = pass.read_descriptor((0, [idx as _]), *page_node); + for (idx, page_node) in page_nodes.iter().copied().enumerate() { + let descriptor = (0, [idx as _]); + pass.set_shader_resource_access( + descriptor, + page_node, + AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, + ); } pass.record_pipeline(move |pipeline, _| { diff --git a/contrib/vk-graph-fx/src/image_loader.rs b/contrib/vk-graph-fx/src/image_loader.rs index d88d40c3..459e849a 100644 --- a/contrib/vk-graph-fx/src/image_loader.rs +++ b/contrib/vk-graph-fx/src/image_loader.rs @@ -145,7 +145,7 @@ impl ImageLoader { } let mut graph = Graph::default(); - let image = graph.bind_node(self.create_image(format, width, height, is_srgb, false)?); + let image = graph.bind_resource(self.create_image(format, width, height, is_srgb, false)?); // Fill the image from the temporary buffer match format { @@ -190,12 +190,12 @@ impl ImageLoader { } } - let pixel_buf = graph.bind_node(pixel_buf); + let pixel_buf = graph.bind_resource(pixel_buf); // We create a temporary storage image because SRGB support isn't wide enough to // have SRGB storage images directly let temp_image = - graph.bind_node(self.create_image(format, width, height, false, true)?); + graph.bind_resource(self.create_image(format, width, height, false, true)?); // Copy host-local data in the buffer to the temporary buffer on the GPU and then // use a compute shader to decode it before copying it over the output image @@ -204,12 +204,12 @@ impl ImageLoader { let dispatch_y = height; graph .begin_cmd() - .with_name("Decode RGB image") + .debug_name("Decode RGB image") .bind_pipeline(&self.decode_rgb_rgba) - .read_descriptor(0, pixel_buf) - .write_descriptor(1, temp_image) - .record_pipeline(move |compute, _| { - compute + .shader_resource_access(0, pixel_buf, AccessType::ComputeShaderReadOther) + .shader_resource_access(1, temp_image, AccessType::ComputeShaderWrite) + .record_pipeline(move |pipeline, _| { + pipeline .push_constants(0, &(pixel_buf_stride >> 2).to_ne_bytes()) .dispatch(dispatch_x, dispatch_y, 1); }) @@ -229,12 +229,12 @@ impl ImageLoader { pixel_buf.copy_from_slice(pixels); } - let pixel_buf = graph.bind_node(pixel_buf); + let pixel_buf = graph.bind_resource(pixel_buf); graph.copy_buffer_to_image(pixel_buf, image); } } - let image = graph.node(image).clone(); + let image = graph.resource(image).clone(); graph .resolve() diff --git a/contrib/vk-graph-fx/src/presenter.rs b/contrib/vk-graph-fx/src/presenter.rs index 8e117a2b..d2b14ecd 100644 --- a/contrib/vk-graph-fx/src/presenter.rs +++ b/contrib/vk-graph-fx/src/presenter.rs @@ -34,18 +34,18 @@ impl ComputePresenter { ) { let image = image.into(); // let image_info = graph.node_info(image); - let swapchain_info = graph.node_info(swapchain); + let swapchain_info = graph.resource(swapchain).info; // TODO: Notice non-sRGB images and run a different pipeline graph .begin_cmd() - .with_name("present (from compute)") + .debug_name("present (from compute)") .bind_pipeline(&self.0[0]) - .read_descriptor(0, image) - .write_descriptor(1, swapchain) - .record_pipeline(move |compute, _| { - compute.dispatch(swapchain_info.width, swapchain_info.height, 1); + .shader_resource_access(0, image, AccessType::ComputeShaderReadOther) + .shader_resource_access(1, swapchain, AccessType::ComputeShaderWrite) + .record_pipeline(move |pipeline, _| { + pipeline.dispatch(swapchain_info.width, swapchain_info.height, 1); }); } @@ -61,19 +61,19 @@ impl ComputePresenter { let bottom_image = bottom_image.into(); // let top_image_info = graph.node_info(top_image); // let bottom_image_info = graph.node_info(bottom_image); - let swapchain_info = graph.node_info(swapchain); + let swapchain_info = graph.resource(swapchain).info; // TODO: Notice non-sRGB images and run a different pipeline graph .begin_cmd() - .with_name("present (from compute)") + .debug_name("present (from compute)") .bind_pipeline(&self.0[1]) - .read_descriptor((0, [0]), top_image) - .read_descriptor((0, [1]), bottom_image) - .write_descriptor(1, swapchain) - .record_pipeline(move |compute, _| { - compute.dispatch(swapchain_info.width, swapchain_info.height, 1); + .shader_resource_access((0, [0]), top_image, AccessType::ComputeShaderReadOther) + .shader_resource_access((0, [1]), bottom_image, AccessType::ComputeShaderReadOther) + .shader_resource_access(1, swapchain, AccessType::ComputeShaderWrite) + .record_pipeline(move |pipeline, _| { + pipeline.dispatch(swapchain_info.width, swapchain_info.height, 1); }); } } @@ -106,8 +106,8 @@ impl GraphicPresenter { swapchain: SwapchainImageNode, ) { let image = image.into(); - let image_info = graph.node_info(image); - let swapchain_info = graph.node_info(swapchain); + let image_info = graph.resource(image).info; + let swapchain_info = graph.resource(swapchain).info; let (image_width, image_height) = (image_info.width as f32, image_info.height as f32); let (swapchain_width, swapchain_height) = @@ -122,9 +122,13 @@ impl GraphicPresenter { graph .begin_cmd() - .with_name("present (from graphic)") + .debug_name("present (from graphic)") .bind_pipeline(&self.pipeline) - .read_descriptor(0, image) + .shader_resource_access( + 0, + image, + AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, + ) .store_color(0, swapchain) .record_pipeline(move |pipeline, _| { // Draw a quad with implicit vertices (no buffer) diff --git a/contrib/vk-graph-fx/src/transition.rs b/contrib/vk-graph-fx/src/transition.rs index c1ace1b0..702700d2 100644 --- a/contrib/vk-graph-fx/src/transition.rs +++ b/contrib/vk-graph-fx/src/transition.rs @@ -410,8 +410,8 @@ impl TransitionPipeline { let a_image = a_image.into(); let b_image = b_image.into(); - let a_info = graph.node_info(a_image); - let b_info = graph.node_info(b_image); + let a_info = graph.resource(a_image).info; + let b_info = graph.resource(b_image).info; let dest_info = ImageInfo::image_2d( a_info.width.max(b_info.width), @@ -422,7 +422,7 @@ impl TransitionPipeline { | vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::TRANSFER_SRC, ); - let dest_image = graph.bind_node(self.cache.lease(dest_info).unwrap()); + let dest_image = graph.bind_resource(self.cache.lease(dest_info).unwrap()); self.apply_to(graph, a_image, b_image, dest_image, transition, progress); @@ -444,7 +444,7 @@ impl TransitionPipeline { let dest_image = dest_image.into(); let progress = progress.clamp(0.0, 1.0); - let dest_info = graph.node_info(dest_image); + let dest_info = graph.resource(dest_image).info; // Lazy-initialize the compute pipeline for this transition let transition_ty = transition.ty(); @@ -458,14 +458,17 @@ impl TransitionPipeline { // TODO: Handle displacement and luma in an if case, below graph .begin_cmd() - .with_name(format!("transition {transition_ty:?}")) + .debug_name(format!("transition {transition_ty:?}")) .bind_pipeline(pipeline) - .read_descriptor(0, a_image) - .read_descriptor(1, b_image) - .write_descriptor(2, dest_image) - .record_pipeline(move |compute, _| { - compute.push_constants(0, &push_consts); - compute.dispatch(dest_info.width, dest_info.height, 1); + .shader_resource_access(0, a_image, AccessType::ComputeShaderReadOther) + .shader_resource_access(1, b_image, AccessType::ComputeShaderReadOther) + .shader_resource_access(2, dest_image, AccessType::ComputeShaderWrite) + .record_pipeline(move |pipeline, _| { + pipeline.push_constants(0, &push_consts).dispatch( + dest_info.width, + dest_info.height, + 1, + ); }); } diff --git a/contrib/vk-graph-hot/examples/glsl.rs b/contrib/vk-graph-hot/examples/glsl.rs index 0e38020a..40c26882 100644 --- a/contrib/vk-graph-hot/examples/glsl.rs +++ b/contrib/vk-graph-hot/examples/glsl.rs @@ -31,15 +31,13 @@ fn main() -> Result<(), WindowError> { frame .graph .begin_cmd() - .with_name("make some noise") + .debug_name("make some noise") .bind_pipeline(pipeline.hot()) .write_descriptor(0, frame.swapchain_image) - .record_pipeline(move |compute, _| { - compute.push_constants(&frame_index.to_ne_bytes()).dispatch( - frame.width, - frame.height, - 1, - ); + .record_pipeline(move |pipeline, _| { + pipeline + .push_constants(&frame_index.to_ne_bytes()) + .dispatch(frame.width, frame.height, 1); }); frame_index += 1; diff --git a/contrib/vk-graph-hot/examples/hlsl.rs b/contrib/vk-graph-hot/examples/hlsl.rs index 7c1b0730..9f3abf63 100644 --- a/contrib/vk-graph-hot/examples/hlsl.rs +++ b/contrib/vk-graph-hot/examples/hlsl.rs @@ -35,12 +35,12 @@ fn main() -> Result<(), WindowError> { frame .graph .begin_cmd() - .with_name("make some noise") + .debug_name("make some noise") .bind_pipeline(pipeline.hot()) .clear_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) - .record_pipeline(move |graphic, _| { - subpass + .record_pipeline(move |pipeline, _| { + pipeline .push_constants_offset(0, &frame_index.to_ne_bytes()) .push_constants_offset(4, &frame.width.to_ne_bytes()) .push_constants_offset(8, &frame.height.to_ne_bytes()) diff --git a/contrib/vk-graph-imgui/src/lib.rs b/contrib/vk-graph-imgui/src/lib.rs index 08577cbc..f3a7cb2d 100644 --- a/contrib/vk-graph-imgui/src/lib.rs +++ b/contrib/vk-graph-imgui/src/lib.rs @@ -97,7 +97,7 @@ impl ImGui { self.platform.prepare_render(ui, window); let draw_data = self.context.render(); - let image = graph.bind_node({ + let image = graph.bind_resource({ let mut image = pool .lease(ImageInfo::image_2d( window.inner_size().width, @@ -114,7 +114,7 @@ impl ImGui { image }); - let font_atlas_image = graph.bind_node(self.font_atlas_image.as_ref().unwrap()); + let font_atlas_image = graph.bind_resource(self.font_atlas_image.as_ref().unwrap()); let display_pos = draw_data.display_pos; let framebuffer_scale = draw_data.framebuffer_scale; @@ -137,7 +137,7 @@ impl ImGui { Buffer::mapped_slice_mut(&mut index_buf)[0..indices.len()].copy_from_slice(indices); } - let index_buf = graph.bind_node(index_buf); + let index_buf = graph.bind_resource(index_buf); let vertices = draw_list.vtx_buffer(); let vertex_buf_len = vertices.len() * 20; @@ -158,7 +158,7 @@ impl ImGui { } } - let vertex_buf = graph.bind_node(vertex_buf); + let vertex_buf = graph.bind_resource(vertex_buf); let draw_cmds = draw_list .commands() @@ -184,11 +184,15 @@ impl ImGui { graph .begin_cmd() - .with_name("imgui") + .debug_name("imgui") .bind_pipeline(&self.pipeline) - .access_node(index_buf, AccessType::IndexBuffer) - .access_node(vertex_buf, AccessType::VertexBuffer) - .read_descriptor(0, font_atlas_image) + .resource_access(index_buf, AccessType::IndexBuffer) + .resource_access(vertex_buf, AccessType::VertexBuffer) + .shader_resource_access( + 0, + font_atlas_image, + AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, + ) .clear_color(0, image) .store_color(0, image) .record_pipeline(move |pipeline, _| { @@ -280,8 +284,8 @@ impl ImGui { temp_buf[0..temp_buf_len].copy_from_slice(texture.data); } - let temp_buf = graph.bind_node(temp_buf); - let image = graph.bind_node( + let temp_buf = graph.bind_resource(temp_buf); + let image = graph.bind_resource( pool.lease(ImageInfo::image_2d( texture.width, texture.height, @@ -291,11 +295,11 @@ impl ImGui { | vk::ImageUsageFlags::TRANSFER_DST, )) .unwrap() - .with_name("ImGui Font Atlas"), + .debug_name("ImGui Font Atlas"), ); graph.copy_buffer_to_image(temp_buf, image); - self.font_atlas_image = Some(graph.node(image).clone()); + self.font_atlas_image = Some(graph.resource(image).clone()); } } diff --git a/contrib/vk-graph-prelude/src/lib.rs b/contrib/vk-graph-prelude/src/lib.rs index 0171c724..67401147 100644 --- a/contrib/vk-graph-prelude/src/lib.rs +++ b/contrib/vk-graph-prelude/src/lib.rs @@ -3,7 +3,7 @@ #![warn(missing_docs)] pub use vk_graph::{ - Bind, Bound, ClearColorValue, Graph, + BindGraph, Bound, ClearColorValue, Graph, cmd_ref::{ BuildAccelerationStructureIndirectInfo, BuildAccelerationStructureInfo, CommandRef, PipelineRef, UpdateAccelerationStructureIndirectInfo, UpdateAccelerationStructureInfo, @@ -39,7 +39,7 @@ pub use vk_graph::{ RayTraceShaderGroup, RayTraceShaderGroupType, }, render_pass::ResolveMode, - shader::{SamplerInfo, SamplerInfoBuilder, Shader, ShaderBuilder, SpecializationInfo}, + shader::{SamplerInfo, SamplerInfoBuilder, Shader, ShaderBuilder, SpecializationMap}, surface::Surface, swapchain::{ Swapchain, SwapchainError, SwapchainImage, SwapchainInfo, SwapchainInfoBuilder, diff --git a/contrib/vk-graph-window/src/frame.rs b/contrib/vk-graph-window/src/frame.rs index ddd73fc5..e56fa62a 100644 --- a/contrib/vk-graph-window/src/frame.rs +++ b/contrib/vk-graph-window/src/frame.rs @@ -17,7 +17,7 @@ pub fn set_cursor_position(window: &Window, x: u32, y: u32) { window.set_cursor_position(position).unwrap_or_default(); } -/// A request to render a single frame to the provided render graph. +/// A request to render a single frame to the provided graph. pub struct FrameContext<'a> { /// The device this frame belongs to. pub device: &'a Device, @@ -28,7 +28,7 @@ pub struct FrameContext<'a> { /// The height, in pixels, of the current frame. pub height: u32, - /// A render graph which rendering commands should be recorded into. + /// A graph which rendering commands should be recorded into. /// /// Make sure to write to `swapchain_image` as part of this graph. pub graph: &'a mut Graph, diff --git a/contrib/vk-graph-window/src/lib.rs b/contrib/vk-graph-window/src/lib.rs index 445c61aa..00b6cf38 100644 --- a/contrib/vk-graph-window/src/lib.rs +++ b/contrib/vk-graph-window/src/lib.rs @@ -334,7 +334,7 @@ impl Window { if let Some(swapchain_image) = self.display.acquire_next_image()? { let mut graph = Graph::default(); - let swapchain_image = graph.bind_node(swapchain_image); + let swapchain_image = graph.bind_resource(swapchain_image); let swapchain_info = self.display.swapchain.info; let mut will_exit = false; diff --git a/examples/aliasing.rs b/examples/aliasing.rs index 3248720b..2ecadc84 100644 --- a/examples/aliasing.rs +++ b/examples/aliasing.rs @@ -1,14 +1,14 @@ use {clap::Parser, std::sync::Arc, vk_graph_prelude::*}; /// This example demonstrates resource aliasing. Aliasing is a memory-efficiency optimization that -/// may be used anywhere resources are leased and used in a render graph. Aliasing allows complex +/// may be used anywhere resources are leased and used in a graph. Aliasing allows complex /// graphs to require fewer individual resources. /// /// The performance overhead of aliasing is an atomic load for each actively aliased item and one /// check per active alias to see if it is compatible with the requested resource. /// /// Acceleration structures, buffers and images may be "aliased" by different parts of any one or -/// more render graphs. The process involves wrapping any pool type (FifoPool, LazyPool, HashPool) +/// more graphs. The process involves wrapping any pool type (FifoPool, LazyPool, HashPool) /// in an AliasPool container. AliasPool offers an alias(..) function which operates exactly the /// same as a regular pool lease(..) except that the result is wrapped in an Arc<>. /// @@ -38,10 +38,19 @@ fn main() -> Result<(), DriverError> { let mut graph = Graph::default(); - // Binding these images to any render graph will produce the same physical nodes - let image1 = graph.bind_node(image1); - let image2 = graph.bind_node(image2); - assert_eq!(image1, image2); + // Binding these images to any single graph will produce the same physical nodes + let image1_node = graph.bind_resource(&image1); + let image2_node = graph.bind_resource(&image2); + assert_eq!(image1_node, image2_node); + + // Even if re-bound + assert_eq!(image2_node, graph.bind_resource(&image2)); + + { + // To be clear: other graphs will produce different nodes + let mut graph = Graph::default(); + assert_ne!(image2_node, graph.bind_resource(&image2)); + } // Let's make up some different, yet compatible, image information: let image_info = ImageInfo::image_2d( @@ -52,12 +61,12 @@ fn main() -> Result<(), DriverError> { ); // We alias the compatible information and still produce the same physical image and node - let image3 = graph.bind_node(pool.alias(image_info)?); - assert_eq!(image1, image3); + let image3_node = graph.bind_resource(pool.alias(image_info)?); + assert_eq!(image1_node, image3_node); // Using the same information for a new LEASE will generate an entirely different image!! - let image4 = graph.bind_node(pool.lease(image_info)?); - assert_ne!(image1, image4); + let image4_node = graph.bind_resource(pool.lease(image_info)?); + assert_ne!(image1_node, image4_node); Ok(()) } diff --git a/examples/app.rs b/examples/app.rs index bec46ffc..8ef28005 100644 --- a/examples/app.rs +++ b/examples/app.rs @@ -114,7 +114,7 @@ impl Context { fn draw(&mut self) -> Result<(), DisplayError> { if let Some(swapchain_image) = self.display.acquire_next_image()? { let mut graph = Graph::default(); - let swapchain_image = graph.bind_node(swapchain_image); + let swapchain_image = graph.bind_resource(swapchain_image); // Rendering goes here! graph.clear_color_image(swapchain_image, [1.0, 0.0, 1.0]); diff --git a/examples/bindless.rs b/examples/bindless.rs index a627e09a..94d2da1e 100644 --- a/examples/bindless.rs +++ b/examples/bindless.rs @@ -24,21 +24,25 @@ fn main() -> Result<(), WindowError> { let draw_buf = create_indirect_buffer(&window.device)?; window.run(|frame| { - let draw_buf_node = frame.graph.bind_node(&draw_buf); + let draw_buf_node = frame.graph.bind_resource(&draw_buf); - let mut pass = frame + let mut cmd = frame .graph .begin_cmd() - .with_name("Test") + .debug_name("Test") .bind_pipeline(&pipeline) - .access_node(draw_buf_node, AccessType::IndirectBuffer); + .resource_access(draw_buf_node, AccessType::IndirectBuffer); for (idx, image) in images.iter().enumerate() { - let image_node = pass.bind_node(image); - pass = pass.read_descriptor((0, [idx as u32]), image_node); + let image = cmd.bind_resource(image); + cmd.set_shader_resource_access( + (0, [idx as u32]), + image, + AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, + ); } - pass.clear_color(0, frame.swapchain_image) + cmd.clear_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) .record_pipeline(move |pipeline, _| { pipeline.draw_indirect(draw_buf_node, 0, 64, 16); @@ -53,7 +57,7 @@ fn create_images(device: &Device) -> Result>, DriverError> { let mut graph = Graph::default(); for y in 0..8 { for x in 0..8 { - let texture = Arc::new(Image::create( + let texture = graph.bind_resource(Image::create( device, ImageInfo::image_2d( 100, @@ -62,11 +66,10 @@ fn create_images(device: &Device) -> Result>, DriverError> { vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST, ), )?); - let texture_node = graph.bind_node(&texture); let r = y as f32 / 7.0; let g = x as f32 / 7.0; - graph.clear_color_image(texture_node, [r, g, b, a]); - textures.push(texture); + graph.clear_color_image(texture, [r, g, b, a]); + textures.push(graph.resource(texture).clone()); } } diff --git a/examples/cpu_readback.rs b/examples/cpu_readback.rs index 2e13f610..f0f95bf6 100644 --- a/examples/cpu_readback.rs +++ b/examples/cpu_readback.rs @@ -12,12 +12,12 @@ fn main() -> Result<(), DriverError> { let mut graph = Graph::default(); - let src_buf = graph.bind_node(Buffer::create_from_slice( + let src_buf = graph.bind_resource(Buffer::create_from_slice( &device, vk::BufferUsageFlags::TRANSFER_SRC, [1, 2, 3, 4], )?); - let dst_buf = graph.bind_node(Buffer::create_from_slice( + let dst_buf = graph.bind_resource(Buffer::create_from_slice( &device, vk::BufferUsageFlags::TRANSFER_DST, [0, 0, 0, 0], @@ -29,9 +29,9 @@ fn main() -> Result<(), DriverError> { // graph and wait on the result graph.copy_buffer(src_buf, dst_buf); - // This line is optional - just bind a reference of Arc or a leased buffer so you retain + // This line is optional - just bind a borrow of Arc or a leased buffer so you retain // the actual buffer for later use and you could then remove this line - let dst_buf = graph.node(dst_buf).clone(); + let dst_buf = graph.resource(dst_buf).clone(); // Resolve and wait (or you can check has_executed without blocking) - alternatively you might // use device.queue_wait_idle(0) or device.device_wait_idle() - but those block on larger scopes diff --git a/examples/debugger.rs b/examples/debugger.rs index 27d94b78..385006d5 100644 --- a/examples/debugger.rs +++ b/examples/debugger.rs @@ -100,7 +100,7 @@ fn main() -> Result<(), vk_graph_window::WindowError> { It is left as an excerise to the reader to determine *what* might have gone wrong here. */ #[allow(unused_variables)] - let image = frame.graph.bind_node( + let image = frame.graph.bind_resource( Image::create( frame.device, ImageInfo::image_2d( @@ -112,7 +112,7 @@ fn main() -> Result<(), vk_graph_window::WindowError> { ) .unwrap(), ); - let image = frame.graph.bind_node( + let image = frame.graph.bind_resource( Image::create( frame.device, ImageInfo::image_2d( @@ -176,11 +176,12 @@ fn main() -> Result<(), vk_graph_window::WindowError> { */ frame .graph - .begin_cmd().with_name("This doesn't look good...") + .begin_cmd() + .debug_name("This doesn't look good...") .bind_pipeline(&compute_pipeline) - .write_descriptor(42, image) - .record_pipeline(|compute, _| { - compute.dispatch(1024, 1024, 1); + .shader_resource_access(42, image, AccessType::ComputeShaderWrite) + .record_pipeline(|pipeline, _| { + pipeline.dispatch(1024, 1024, 1); }); // Growing tired of your advenutes, you signal that it is time to close the window and exit diff --git a/examples/egui.rs b/examples/egui.rs index 1291d763..a79aa62d 100644 --- a/examples/egui.rs +++ b/examples/egui.rs @@ -20,7 +20,7 @@ fn main() -> anyhow::Result<()> { let mut cache = LazyPool::new(&window.device); window.run(|frame| { - let img = frame.graph.bind_node( + let img = frame.graph.bind_resource( cache .lease(ImageInfo::image_2d( 100, diff --git a/examples/font_bmp.rs b/examples/font_bmp.rs index 1c15a57b..62532f2c 100644 --- a/examples/font_bmp.rs +++ b/examples/font_bmp.rs @@ -109,18 +109,11 @@ fn main() -> anyhow::Result<()> { } "#, ) - .as_slice()).specialization_info(SpecializationInfo { - data: subgroup_size.to_ne_bytes().to_vec(), - map_entries: vec![vk::SpecializationMapEntry { - constant_id: 0, - offset: 0, - size: 4, - }], - }), + .as_slice()).specialization(SpecializationMap::new(subgroup_size.to_ne_bytes()).constant(0,0,4)), )?; window.run(|frame| { - let image_node = frame.graph.bind_node( + let image_node = frame.graph.bind_resource( pool.lease(ImageInfo::image_2d( 320, 200, @@ -138,11 +131,11 @@ fn main() -> anyhow::Result<()> { frame .graph .begin_cmd() - .with_name("smoke") + .debug_name("smoke") .bind_pipeline(&smoke_pipeline) - .write_descriptor(0, image_node) - .record_pipeline(move |compute, _| { - compute + .shader_resource_access(0, image_node, AccessType::ComputeShaderWrite) + .record_pipeline(move |pipeline, _| { + pipeline .push_constants(0, &elapsed_time.as_secs_f32().to_ne_bytes()) .dispatch(frame.width.div_ceil(subgroup_size), frame.height, 1); }); diff --git a/examples/fuzzer.rs b/examples/fuzzer.rs index 03bc83e9..1c50fb33 100644 --- a/examples/fuzzer.rs +++ b/examples/fuzzer.rs @@ -217,8 +217,8 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { }]), ); - let blas_node = frame.graph.bind_node(blas); - let scratch_buf = frame.graph.bind_node( + let blas_node = frame.graph.bind_resource(blas); + let scratch_buf = frame.graph.bind_resource( pool.lease( BufferInfo::device_mem( blas_size.build_size, @@ -246,13 +246,13 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { }, vk::AccelerationStructureBuildRangeInfoKHR::default().primitive_count(1), )]); - let instance_buf = frame.graph.bind_node(instance_buf); + let instance_buf = frame.graph.bind_resource(instance_buf); let tlas_size = AccelerationStructure::size_of(frame.device, &tlas_geometry_info); let tlas = pool .lease(AccelerationStructureInfo::tlas(tlas_size.create_size)) .unwrap(); - let tlas_node = frame.graph.bind_node(tlas); - let tlas_scratch_buf = frame.graph.bind_node( + let tlas_node = frame.graph.bind_resource(tlas); + let tlas_scratch_buf = frame.graph.bind_resource( pool.lease( BufferInfo::device_mem( tlas_size.build_size, @@ -264,21 +264,20 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { .unwrap(), ); - let index_node = frame.graph.bind_node(index_buf); - let vertex_node = frame.graph.bind_node(vertex_buf); + let index_node = frame.graph.bind_resource(index_buf); + let vertex_node = frame.graph.bind_resource(vertex_buf); - let pass = frame + let mut cmd = frame .graph .begin_cmd() - .with_name("build acceleration structures"); + .debug_name("build acceleration structures"); - // TODO: AccessType for these is funky, should be access_node? - let mut pass = pass.read_node(index_node).read_node(vertex_node); + cmd.set_resource_access(index_node, AccessType::IndexBuffer); + cmd.set_resource_access(vertex_node, AccessType::VertexBuffer); - // TODO: Like this: for (blas_node, scratch_buf, _) in &blas_nodes { - pass.access_node_mut(*blas_node, AccessType::AccelerationStructureBuildWrite); - pass.access_node_mut(*scratch_buf, AccessType::AccelerationStructureBufferWrite); + cmd.set_resource_access(*blas_node, AccessType::AccelerationStructureBuildWrite); + cmd.set_resource_access(*scratch_buf, AccessType::AccelerationStructureBufferWrite); } // Ugly copy of the nodes that I want to figure out a way around while not being confusing @@ -287,7 +286,7 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { .map(|(blas_node, _, _)| *blas_node) .collect::>(); - let mut pass = pass.record_accel_struct(move |accel_struct, nodes| { + let mut cmd = cmd.record_accel_struct(move |accel_struct, nodes| { for (blas_node, scratch_buf, build_data) in blas_nodes { let scratch_addr = nodes[scratch_buf].device_address(); accel_struct.build(&[BuildAccelerationStructureInfo::new( @@ -299,24 +298,23 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { }); for blas_node in blas_nodes_copy { - pass.access_node_mut(blas_node, AccessType::AccelerationStructureBuildRead); + cmd.set_resource_access(blas_node, AccessType::AccelerationStructureBuildRead); } - pass.access_node_mut(instance_buf, AccessType::AccelerationStructureBuildRead); - pass.access_node_mut( - tlas_scratch_buf, - AccessType::AccelerationStructureBufferWrite, - ); - pass.access_node_mut(tlas_node, AccessType::AccelerationStructureBuildWrite); - - pass.record_accel_struct(move |accel_struct, nodes| { - let scratch_data = Buffer::device_address(&nodes[tlas_scratch_buf]); - accel_struct.build(&[BuildAccelerationStructureInfo::new( - tlas_node, - scratch_data, - tlas_geometry_info, - )]); - }); + cmd.resource_access(instance_buf, AccessType::AccelerationStructureBuildRead) + .resource_access( + tlas_scratch_buf, + AccessType::AccelerationStructureBufferWrite, + ) + .resource_access(tlas_node, AccessType::AccelerationStructureBuildWrite) + .record_accel_struct(move |accel_struct, nodes| { + let scratch_data = Buffer::device_address(&nodes[tlas_scratch_buf]); + accel_struct.build(&[BuildAccelerationStructureInfo::new( + tlas_node, + scratch_data, + tlas_geometry_info, + )]); + }); } fn record_pipeline_array_bind(frame: &mut FrameContext, pool: &mut HashPool) { @@ -346,14 +344,7 @@ fn record_pipeline_array_bind(frame: &mut FrameContext, pool: &mut HashPool) { ) .as_slice(), ) - .specialization_info(SpecializationInfo::new( - vec![vk::SpecializationMapEntry { - constant_id: 0, - offset: 0, - size: 4, - }], - 5u32.to_ne_bytes(), - )), + .specialization(SpecializationMap::new(5u32.to_ne_bytes()).constant(0,0,4)), ); let image_info = ImageInfo::image_2d( @@ -363,11 +354,11 @@ fn record_pipeline_array_bind(frame: &mut FrameContext, pool: &mut HashPool) { vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST, ); let images = [ - frame.graph.bind_node(pool.lease(image_info).unwrap()), - frame.graph.bind_node(pool.lease(image_info).unwrap()), - frame.graph.bind_node(pool.lease(image_info).unwrap()), - frame.graph.bind_node(pool.lease(image_info).unwrap()), - frame.graph.bind_node(pool.lease(image_info).unwrap()), + frame.graph.bind_resource(pool.lease(image_info).unwrap()), + frame.graph.bind_resource(pool.lease(image_info).unwrap()), + frame.graph.bind_resource(pool.lease(image_info).unwrap()), + frame.graph.bind_resource(pool.lease(image_info).unwrap()), + frame.graph.bind_resource(pool.lease(image_info).unwrap()), ]; frame @@ -378,15 +369,15 @@ fn record_pipeline_array_bind(frame: &mut FrameContext, pool: &mut HashPool) { .clear_color_image(images[3], [0f32; 4]) .clear_color_image(images[4], [0f32; 4]) .begin_cmd() - .with_name("array-bind") + .debug_name("array-bind") .bind_pipeline(&pipeline) - .read_descriptor((0, [0]), images[0]) - .read_descriptor((0, [1]), images[1]) - .read_descriptor((0, [2]), images[2]) - .read_descriptor((0, [3]), images[3]) - .read_descriptor((0, [4]), images[4]) - .record_pipeline(|compute, _| { - compute + .shader_resource_access((0, [0]), images[0], AccessType::ComputeShaderReadOther) + .shader_resource_access((0, [1]), images[1], AccessType::ComputeShaderReadOther) + .shader_resource_access((0, [2]), images[2], AccessType::ComputeShaderReadOther) + .shader_resource_access((0, [3]), images[3], AccessType::ComputeShaderReadOther) + .shader_resource_access((0, [4]), images[4], AccessType::ComputeShaderReadOther) + .record_pipeline(|pipeline, _| { + pipeline .push_constants(0, &0f32.to_ne_bytes()) .dispatch(64, 64, 1); }); @@ -434,25 +425,25 @@ fn record_pipeline_bindless(frame: &mut FrameContext, pool: &mut HashPool) { vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::STORAGE, ); let images = [ - frame.graph.bind_node(pool.lease(image_info).unwrap()), - frame.graph.bind_node(pool.lease(image_info).unwrap()), - frame.graph.bind_node(pool.lease(image_info).unwrap()), - frame.graph.bind_node(pool.lease(image_info).unwrap()), - frame.graph.bind_node(pool.lease(image_info).unwrap()), + frame.graph.bind_resource(pool.lease(image_info).unwrap()), + frame.graph.bind_resource(pool.lease(image_info).unwrap()), + frame.graph.bind_resource(pool.lease(image_info).unwrap()), + frame.graph.bind_resource(pool.lease(image_info).unwrap()), + frame.graph.bind_resource(pool.lease(image_info).unwrap()), ]; frame .graph .begin_cmd() - .with_name("compute-bindless") + .debug_name("compute-bindless") .bind_pipeline(&pipeline) - .write_descriptor((0, [0]), images[0]) - .write_descriptor((0, [1]), images[1]) - .write_descriptor((0, [2]), images[2]) - .write_descriptor((0, [3]), images[3]) - .write_descriptor((0, [4]), images[4]) - .record_pipeline(|compute, _| { - compute + .shader_resource_access((0, [0]), images[0], AccessType::ComputeShaderWrite) + .shader_resource_access((0, [1]), images[1], AccessType::ComputeShaderWrite) + .shader_resource_access((0, [2]), images[2], AccessType::ComputeShaderWrite) + .shader_resource_access((0, [3]), images[3], AccessType::ComputeShaderWrite) + .shader_resource_access((0, [4]), images[4], AccessType::ComputeShaderWrite) + .record_pipeline(|pipeline, _| { + pipeline .push_constants(0, &5u32.to_ne_bytes()) .dispatch(64, 64, 1); }); @@ -479,10 +470,10 @@ fn record_pipeline_no_op(frame: &mut FrameContext, _: &mut HashPool) { frame .graph .begin_cmd() - .with_name("no-op") + .debug_name("no-op") .bind_pipeline(&pipeline) - .record_pipeline(|compute, _| { - compute.dispatch(1, 1, 1); + .record_pipeline(|pipeline, _| { + pipeline.dispatch(1, 1, 1); }); } @@ -527,7 +518,7 @@ fn record_graphic_bindless(frame: &mut FrameContext, pool: &mut HashPool) { .as_slice(), ); - let image = frame.graph.bind_node( + let image = frame.graph.bind_resource( pool.lease(ImageInfo::image_2d( 256, 256, @@ -545,11 +536,11 @@ fn record_graphic_bindless(frame: &mut FrameContext, pool: &mut HashPool) { | vk::ImageUsageFlags::TRANSFER_DST, ); let images = [ - frame.graph.bind_node(pool.lease(image_info).unwrap()), - frame.graph.bind_node(pool.lease(image_info).unwrap()), - frame.graph.bind_node(pool.lease(image_info).unwrap()), - frame.graph.bind_node(pool.lease(image_info).unwrap()), - frame.graph.bind_node(pool.lease(image_info).unwrap()), + frame.graph.bind_resource(pool.lease(image_info).unwrap()), + frame.graph.bind_resource(pool.lease(image_info).unwrap()), + frame.graph.bind_resource(pool.lease(image_info).unwrap()), + frame.graph.bind_resource(pool.lease(image_info).unwrap()), + frame.graph.bind_resource(pool.lease(image_info).unwrap()), ]; frame @@ -560,13 +551,33 @@ fn record_graphic_bindless(frame: &mut FrameContext, pool: &mut HashPool) { .clear_color_image(images[3], [0f32; 4]) .clear_color_image(images[4], [0f32; 4]) .begin_cmd() - .with_name("graphic-bindless") + .debug_name("graphic-bindless") .bind_pipeline(&pipeline) - .read_descriptor((0, [0]), images[0]) - .read_descriptor((0, [1]), images[1]) - .read_descriptor((0, [2]), images[2]) - .read_descriptor((0, [3]), images[3]) - .read_descriptor((0, [4]), images[4]) + .shader_resource_access( + (0, [0]), + images[0], + AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, + ) + .shader_resource_access( + (0, [1]), + images[1], + AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, + ) + .shader_resource_access( + (0, [2]), + images[2], + AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, + ) + .shader_resource_access( + (0, [3]), + images[3], + AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, + ) + .shader_resource_access( + (0, [4]), + images[4], + AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, + ) .clear_color(0, image) .store_color(0, image) .record_pipeline(|pipeline, _| { @@ -608,7 +619,7 @@ fn record_graphic_load_store(frame: &mut FrameContext, _: &mut HashPool) { frame .graph .begin_cmd() - .with_name("load-store") + .debug_name("load-store") .bind_pipeline(&pipeline) .load_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) @@ -719,8 +730,8 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo .as_slice(), ); - let swapchain_format = frame.graph.node_info(frame.swapchain_image).fmt; - let msaa_color_image = frame.graph.bind_node( + let swapchain_format = frame.graph.resource(frame.swapchain_image).info.fmt; + let msaa_color_image = frame.graph.bind_resource( pool.lease( ImageInfo::image_2d( frame.width, @@ -733,7 +744,7 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo ) .unwrap(), ); - let msaa_depth_stencil_image = frame.graph.bind_node( + let msaa_depth_stencil_image = frame.graph.bind_resource( pool.lease( ImageInfo::image_2d( frame.width, @@ -747,7 +758,7 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo ) .unwrap(), ); - let depth_stencil_image = frame.graph.bind_node( + let depth_stencil_image = frame.graph.bind_resource( pool.lease(ImageInfo::image_2d( frame.width, frame.height, @@ -780,7 +791,7 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo frame .graph .begin_cmd() - .with_name("msaa-depth-stencil") + .debug_name("msaa-depth-stencil") .bind_pipeline(&pipeline) .set_depth_stencil(depth_stencil_mode) .clear_color(0, msaa_color_image) @@ -798,7 +809,7 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo } fn record_graphic_will_merge_common_color1(frame: &mut FrameContext, pool: &mut HashPool) { - let image = frame.graph.bind_node( + let image = frame.graph.bind_resource( pool.lease(ImageInfo::image_2d( 256, 256, @@ -812,7 +823,7 @@ fn record_graphic_will_merge_common_color1(frame: &mut FrameContext, pool: &mut frame .graph .begin_cmd() - .with_name("a") + .debug_name("a") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), @@ -846,7 +857,7 @@ fn record_graphic_will_merge_common_color1(frame: &mut FrameContext, pool: &mut frame .graph .begin_cmd() - .with_name("b") + .debug_name("b") .bind_pipeline(&graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), @@ -881,7 +892,7 @@ fn record_graphic_will_merge_common_color1(frame: &mut FrameContext, pool: &mut } fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut HashPool) { - let image_0 = frame.graph.bind_node( + let image_0 = frame.graph.bind_resource( pool.lease(ImageInfo::image_2d( 256, 256, @@ -890,7 +901,7 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut )) .unwrap(), ); - let image_1 = frame.graph.bind_node( + let image_1 = frame.graph.bind_resource( pool.lease(ImageInfo::image_2d( 256, 256, @@ -903,7 +914,7 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut frame .graph .begin_cmd() - .with_name("a") + .debug_name("a") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), @@ -937,7 +948,7 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut frame .graph .begin_cmd() - .with_name("b") + .debug_name("b") .bind_pipeline(&graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), @@ -975,7 +986,7 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut frame .graph .begin_cmd() - .with_name("c") + .debug_name("c") .bind_pipeline(&graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), @@ -1010,7 +1021,7 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut } fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut HashPool) { - let color_image = frame.graph.bind_node( + let color_image = frame.graph.bind_resource( pool.lease(ImageInfo::image_2d( 256, 256, @@ -1019,7 +1030,7 @@ fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut )) .unwrap(), ); - let depth_image = frame.graph.bind_node( + let depth_image = frame.graph.bind_resource( pool.lease(ImageInfo::image_2d( 256, 256, @@ -1033,7 +1044,7 @@ fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut frame .graph .begin_cmd() - .with_name("a") + .debug_name("a") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), @@ -1068,7 +1079,7 @@ fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut frame .graph .begin_cmd() - .with_name("b") + .debug_name("b") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), @@ -1101,7 +1112,7 @@ fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut } fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut HashPool) { - let color_image = frame.graph.bind_node( + let color_image = frame.graph.bind_resource( pool.lease(ImageInfo::image_2d( 256, 256, @@ -1110,7 +1121,7 @@ fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut )) .unwrap(), ); - let depth_image = frame.graph.bind_node( + let depth_image = frame.graph.bind_resource( pool.lease(ImageInfo::image_2d( 256, 256, @@ -1124,7 +1135,7 @@ fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut frame .graph .begin_cmd() - .with_name("a") + .debug_name("a") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), @@ -1156,7 +1167,7 @@ fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut frame .graph .begin_cmd() - .with_name("b") + .debug_name("b") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), @@ -1192,7 +1203,7 @@ fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut } fn record_graphic_will_merge_common_depth3(frame: &mut FrameContext, pool: &mut HashPool) { - let depth_image = frame.graph.bind_node( + let depth_image = frame.graph.bind_resource( pool.lease(ImageInfo::image_2d( 256, 256, @@ -1205,7 +1216,7 @@ fn record_graphic_will_merge_common_depth3(frame: &mut FrameContext, pool: &mut frame .graph .begin_cmd() - .with_name("a") + .debug_name("a") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), @@ -1237,7 +1248,7 @@ fn record_graphic_will_merge_common_depth3(frame: &mut FrameContext, pool: &mut frame .graph .begin_cmd() - .with_name("b") + .debug_name("b") .bind_pipeline(graphic_vert_frag_pipeline( frame.device, GraphicPipelineInfo::default(), @@ -1318,7 +1329,7 @@ fn record_graphic_will_merge_subpass_input(frame: &mut FrameContext, pool: &mut ) .as_slice(), ); - let image = frame.graph.bind_node( + let image = frame.graph.bind_resource( pool.lease(ImageInfo::image_2d( 256, 256, @@ -1334,7 +1345,7 @@ fn record_graphic_will_merge_subpass_input(frame: &mut FrameContext, pool: &mut frame .graph .begin_cmd() - .with_name("a") + .debug_name("a") .bind_pipeline(&pipeline_a) .clear_color(0, image) .store_color(0, image) @@ -1344,7 +1355,7 @@ fn record_graphic_will_merge_subpass_input(frame: &mut FrameContext, pool: &mut frame .graph .begin_cmd() - .with_name("b") + .debug_name("b") .bind_pipeline(&pipeline_b) .store_color(0, image) .record_pipeline(|pipeline, _| { @@ -1380,7 +1391,7 @@ fn record_graphic_wont_merge(frame: &mut FrameContext, pool: &mut HashPool) { .as_slice(), ); - let image = frame.graph.bind_node( + let image = frame.graph.bind_resource( pool.lease(ImageInfo::image_2d( 256, 256, @@ -1394,7 +1405,7 @@ fn record_graphic_wont_merge(frame: &mut FrameContext, pool: &mut HashPool) { frame .graph .begin_cmd() - .with_name("c") + .debug_name("c") .bind_pipeline(&pipeline) .store_color(0, image) .record_pipeline(|pipeline, _| { @@ -1403,7 +1414,7 @@ fn record_graphic_wont_merge(frame: &mut FrameContext, pool: &mut HashPool) { frame .graph .begin_cmd() - .with_name("d") + .debug_name("d") .bind_pipeline(&pipeline) .store_color(0, image) .record_pipeline(|pipeline, _| { @@ -1442,7 +1453,7 @@ fn record_transfer_graphic_multipass(frame: &mut FrameContext, pool: &mut HashPo .as_slice(), ); let images = [ - frame.graph.bind_node( + frame.graph.bind_resource( pool.lease(ImageInfo::image_2d( 256, 256, @@ -1451,7 +1462,7 @@ fn record_transfer_graphic_multipass(frame: &mut FrameContext, pool: &mut HashPo )) .unwrap(), ), - frame.graph.bind_node( + frame.graph.bind_resource( pool.lease(ImageInfo::image_2d( 256, 256, @@ -1470,22 +1481,30 @@ fn record_transfer_graphic_multipass(frame: &mut FrameContext, pool: &mut HashPo frame .graph .begin_cmd() - .with_name("a") + .debug_name("a") .bind_pipeline(&pipeline) .clear_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) - .read_descriptor(0, images[0]) + .shader_resource_access( + 0, + images[0], + AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, + ) .record_pipeline(|pipeline, _| { pipeline.draw(1, 1, 0, 0); }); frame .graph .begin_cmd() - .with_name("b") + .debug_name("b") .bind_pipeline(&pipeline) .load_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) - .read_descriptor(0, images[1]) + .shader_resource_access( + 0, + images[1], + AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, + ) .record_pipeline(|pipeline, _| { pipeline.draw(1, 1, 0, 0); }); diff --git a/examples/image_sampler.rs b/examples/image_sampler.rs index cb9e0a47..92074140 100644 --- a/examples/image_sampler.rs +++ b/examples/image_sampler.rs @@ -65,13 +65,13 @@ fn main() -> anyhow::Result<()> { } // Draw gulf.jpg using the active pipeline - let gulf_image = frame.graph.bind_node(&gulf_image); + let gulf_image = frame.graph.bind_resource(&gulf_image); frame .graph .begin_cmd() - .with_name("Draw gulf image to swapchain") + .debug_name("Draw gulf image to swapchain") .bind_pipeline(&pipelines[pipeline_index]) - .read_descriptor(0, gulf_image) + .shader_resource_access(0, gulf_image, AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer) .store_color(0, frame.swapchain_image) .record_pipeline(|pipeline, _| { pipeline.draw(3, 1, 0, 0); @@ -243,8 +243,8 @@ fn read_image(device: &Device, path: impl AsRef) -> anyhow::Result Result<(), WindowError> { window.run(|frame| { // Lease and clear an image as a stand-in for some real game or program output - let app_image = frame.graph.bind_node( + let app_image = frame.graph.bind_resource( pool.lease(ImageInfo::image_2d( frame.width, frame.height, diff --git a/examples/min_max.rs b/examples/min_max.rs index 6223c36a..ee8262d6 100644 --- a/examples/min_max.rs +++ b/examples/min_max.rs @@ -144,12 +144,12 @@ fn fill_depth_image( // device.physical_device.sampler_filter_minmax_properties.image_component_mapping let depth_data = (0..size.pow(2)).map(|x| x as f32).collect::>(); - let depth_data = graph.bind_node(Buffer::create_from_slice( + let depth_data = graph.bind_resource(Buffer::create_from_slice( device, vk::BufferUsageFlags::TRANSFER_SRC, cast_slice(&depth_data), )?); - let depth_image = graph.bind_node(Image::create(device, info)?); + let depth_image = graph.bind_resource(Image::create(device, info)?); graph.copy_buffer_to_image(depth_data, depth_image); Ok(depth_image) @@ -173,11 +173,11 @@ fn reduce_depth_image( vk::Format::R32_SFLOAT, vk::ImageUsageFlags::STORAGE | vk::ImageUsageFlags::TRANSFER_SRC, ); - let reduced_image = graph.bind_node(Image::create(device, reduced_info)?); + let reduced_image = graph.bind_resource(Image::create(device, reduced_info)?); graph .begin_cmd() - .with_name("Reduce depth image") + .debug_name("Reduce depth image") .bind_pipeline(ComputePipeline::create( device, ComputePipelineInfo::default(), @@ -221,14 +221,14 @@ fn copy_image_to_buffer( let reduced_info = graph.node_info(reduced_image); let result_len = (reduced_info.width * reduced_info.height) as vk::DeviceSize * size_of::() as vk::DeviceSize; - let result_buf = graph.bind_node(Buffer::create( + let result_buf = graph.bind_resource(Buffer::create( device, BufferInfo::host_mem(result_len, vk::BufferUsageFlags::TRANSFER_DST), )?); graph.copy_image_to_buffer(reduced_image, result_buf); - Ok(graph.node(result_buf).clone()) + Ok(graph.resource(result_buf).clone()) } #[derive(Parser)] diff --git a/examples/mip_compute.rs b/examples/mip_compute.rs index 4bfa4308..ac0a137c 100644 --- a/examples/mip_compute.rs +++ b/examples/mip_compute.rs @@ -18,7 +18,7 @@ fn main() -> Result<(), DriverError> { let mut graph = Graph::default(); - let depth_pyramid = graph.bind_node(Image::create( + let depth_pyramid = graph.bind_resource(Image::create( &device, ImageInfo::image_2d( 4, @@ -36,7 +36,7 @@ fn main() -> Result<(), DriverError> { // You would normally create this buffer by copying the depth attachment image #[allow(clippy::inconsistent_digit_grouping)] - let depth_buf = graph.bind_node(Buffer::create_from_slice( + let depth_buf = graph.bind_resource(Buffer::create_from_slice( &device, vk::BufferUsageFlags::TRANSFER_SRC, cast_slice(&[ @@ -50,7 +50,7 @@ fn main() -> Result<(), DriverError> { let mut pass = graph .begin_cmd() - .with_name("update depth pyramid") + .debug_name("update depth pyramid") .bind_pipeline(ComputePipeline::create( &device, ComputePipelineInfo::default(), @@ -112,7 +112,7 @@ fn main() -> Result<(), DriverError> { }); } - let depth_pixel = graph.bind_node(Buffer::create( + let depth_pixel = graph.bind_resource(Buffer::create( &device, BufferInfo::host_mem(size_of::() as _, vk::BufferUsageFlags::TRANSFER_DST), )?); @@ -138,7 +138,7 @@ fn main() -> Result<(), DriverError> { }, ); - let depth_pixel = graph.node(depth_pixel).clone(); + let depth_pixel = graph.resource(depth_pixel).clone(); graph .resolve() diff --git a/examples/mip_graphic.rs b/examples/mip_graphic.rs index 8a304b3c..a71e9469 100644 --- a/examples/mip_graphic.rs +++ b/examples/mip_graphic.rs @@ -47,38 +47,39 @@ fn main() -> Result<(), WindowError> { assert!( frame .graph - .node_info(frame.swapchain_image) + .resource(frame.swapchain_image).info .usage .contains(vk::ImageUsageFlags::COLOR_ATTACHMENT) ); - let image = frame.graph.bind_node(&image); + let image = frame.graph.bind_resource(&image); let swapchain_info = frame.graph.node_info(frame.swapchain_image); let stripe_width = swapchain_info.width / mip_level_count; let mut pass = frame .graph .begin_cmd() - .with_name("splat mips") + .debug_name("splat mips") .bind_pipeline(&splat); for mip_level in 0..mip_level_count { let stripe_x = mip_level * stripe_width; pass = pass - .read_descriptor_as( + .shader_subresource_access( 0, image, image_info - .default_view_info() + .into_image_view() .to_builder() .base_mip_level(mip_level) .mip_level_count(1), + AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, ) .load_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) .set_render_area(stripe_x as _, 0, stripe_width, swapchain_info.height) - .record_pipeline(|pipeline, _| { - pipeline.draw(6, 1, 0, 0); + .record_pipeline(|cmd, _| { + cmd.draw(6, 1, 0, 0); }); } }) @@ -148,7 +149,7 @@ fn fill_mip_levels(device: &Device, image: &Arc) -> Result<(), DriverErro let mut graph = Graph::default(); let image_info = image.info; - let image = graph.bind_node(image); + let image = graph.bind_resource(image); // NOTE: Each pass writes to a different mip level, and so although it's the same image they are // unable to be used as a single pass so we must call begin_pass for each. Without starting a @@ -156,13 +157,13 @@ fn fill_mip_levels(device: &Device, image: &Arc) -> Result<(), DriverErro for mip_level in 0..image_info.mip_level_count { graph .begin_cmd() - .with_name("fill mip levels") + .debug_name("fill mip levels") .bind_pipeline(&vertical_gradient) .store_color_as( 0, image, image_info - .default_view_info() + .into_image_view() .to_builder() .base_mip_level(mip_level) .mip_level_count(1), diff --git a/examples/msaa.rs b/examples/msaa.rs index fe5e0467..0738d4b7 100644 --- a/examples/msaa.rs +++ b/examples/msaa.rs @@ -86,24 +86,24 @@ fn main() -> anyhow::Result<()> { }), ); - let cube_vertex_buf = frame.graph.bind_node(&cube_mesh.vertex_buf); - let scene_uniform_buf = frame.graph.bind_node(scene_uniform_buf); + let cube_vertex_buf = frame.graph.bind_resource(&cube_mesh.vertex_buf); + let scene_uniform_buf = frame.graph.bind_resource(scene_uniform_buf); let mut pass = frame .graph .begin_cmd() - .with_name("cube") + .debug_name("cube") .bind_pipeline(if will_render_msaa { &mesh_msaa_pipeline } else { &mesh_noaa_pipeline }) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) - .access_node(cube_vertex_buf, AccessType::VertexBuffer) - .access_descriptor(0, scene_uniform_buf, AccessType::AnyShaderReadUniformBuffer); + .resource_access(cube_vertex_buf, AccessType::VertexBuffer) + .shader_resource_access(0, scene_uniform_buf, AccessType::AnyShaderReadUniformBuffer); if will_render_msaa { - let msaa_color_image = pass.bind_node( + let msaa_color_image = pass.bind_resource( pool.lease( ImageInfo::image_2d( frame.width, @@ -117,7 +117,7 @@ fn main() -> anyhow::Result<()> { ) .unwrap(), ); - let msaa_depth_image = pass.bind_node( + let msaa_depth_image = pass.bind_resource( pool.lease( ImageInfo::image_2d( frame.width, @@ -138,7 +138,7 @@ fn main() -> anyhow::Result<()> { .clear_depth_stencil(msaa_depth_image) .resolve_color(0, 1, frame.swapchain_image); } else { - let noaa_depth_image = pass.bind_node( + let noaa_depth_image = pass.bind_resource( pool.lease(ImageInfo::image_2d( frame.width, frame.height, diff --git a/examples/multipass.rs b/examples/multipass.rs index e5c157c7..1640c9b7 100644 --- a/examples/multipass.rs +++ b/examples/multipass.rs @@ -60,10 +60,10 @@ fn main() -> anyhow::Result<()> { window.run(|frame| { t += 0.016; - let index_buf = frame.graph.bind_node(&funky_shape.index_buf); - let vertex_buf = frame.graph.bind_node(&funky_shape.vertex_buf); + let index_buf = frame.graph.bind_resource(&funky_shape.index_buf); + let vertex_buf = frame.graph.bind_resource(&funky_shape.vertex_buf); - let depth_stencil = frame.graph.bind_node( + let depth_stencil = frame.graph.bind_resource( pool.lease(ImageInfo::image_2d( frame.width, frame.height, @@ -89,12 +89,16 @@ fn main() -> anyhow::Result<()> { frame .graph .begin_cmd() - .with_name("Depth Prepass") + .debug_name("Depth Prepass") .bind_pipeline(&prepass) .set_depth_stencil(write) - .read_descriptor(0, camera_buf) - .access_node(index_buf, AccessType::IndexBuffer) - .access_node(vertex_buf, AccessType::VertexBuffer) + .shader_resource_access( + 0, + camera_buf, + AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, + ) + .resource_access(index_buf, AccessType::IndexBuffer) + .resource_access(vertex_buf, AccessType::VertexBuffer) .clear_depth_stencil(depth_stencil) .store_depth_stencil(depth_stencil) .record_pipeline(move |pipeline, _| { @@ -120,13 +124,21 @@ fn main() -> anyhow::Result<()> { frame .graph .begin_cmd() - .with_name("funky shape PBR") + .debug_name("funky shape PBR") .bind_pipeline(&pbr) .set_depth_stencil(write) - .read_descriptor(0, camera_buf) - .read_descriptor(1, light_buf) - .access_node(index_buf, AccessType::IndexBuffer) - .access_node(vertex_buf, AccessType::VertexBuffer) + .shader_resource_access( + 0, + camera_buf, + AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, + ) + .shader_resource_access( + 1, + light_buf, + AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, + ) + .resource_access(index_buf, AccessType::IndexBuffer) + .resource_access(vertex_buf, AccessType::VertexBuffer) .load_depth_stencil(depth_stencil) .store_depth_stencil(depth_stencil) .store_color(0, frame.swapchain_image) @@ -149,7 +161,7 @@ fn main() -> anyhow::Result<()> { frame .graph .begin_cmd() - .with_name("fill background") + .debug_name("fill background") .bind_pipeline(&fill_background) .set_depth_stencil(read) .load_depth_stencil(depth_stencil) @@ -200,7 +212,7 @@ fn bind_camera_buf( .unwrap(); write_camera_buf(&mut buf, camera, model); - graph.bind_node(buf) + graph.bind_resource(buf) } fn bind_light_buf(graph: &mut Graph, pool: &mut LazyPool) -> BufferLeaseNode { @@ -212,7 +224,7 @@ fn bind_light_buf(graph: &mut Graph, pool: &mut LazyPool) -> BufferLeaseNode { .unwrap(); write_light_buf(&mut buf); - graph.bind_node(buf) + graph.bind_resource(buf) } fn write_push_consts(obj_pos: Vec3, material: Material) -> [u8; 32] { @@ -282,10 +294,10 @@ fn create_funky_shape(device: &Device, pool: &mut LazyPool) -> Result anyhow::Result<()> { // Clear a new image to a cycling color let mut graph = Graph::default(); - let image = graph.bind_node( + let image = graph.bind_resource( pool.lease(ImageInfo::image_2d( 10, 10, @@ -121,7 +121,7 @@ fn main() -> anyhow::Result<()> { ], ); - let image = graph.node(image).clone(); + let image = graph.resource(image).clone(); // Submit on a queue we are reserving for only this thread to use graph @@ -157,7 +157,7 @@ fn main() -> anyhow::Result<()> { .clear_color_image(frame.swapchain_image, [0f32; 4]); for (image_idx, image) in images.iter().enumerate() { - let image = frame.graph.bind_node(image); + let image = frame.graph.bind_resource(image); let x = (image_idx % 8) as f32; let y = (image_idx / 8) as f32; @@ -169,7 +169,7 @@ fn main() -> anyhow::Result<()> { image, frame.swapchain_image, vk::Filter::NEAREST, - vk::ImageBlit { + [vk::ImageBlit { src_subresource: COLOR_SUBRESOURCE_LAYER, src_offsets: [ vk::Offset3D { x: 0, y: 0, z: 0 }, @@ -188,7 +188,7 @@ fn main() -> anyhow::Result<()> { z: 1, }, ], - }, + }], ); } @@ -225,7 +225,7 @@ fn load_font(device: &Device) -> anyhow::Result { let mut graph = Graph::default(); // We happen to know this font only requires a single image, this uses the image crate - let temp_buf = graph.bind_node(Buffer::create_from_slice( + let temp_buf = graph.bind_resource(Buffer::create_from_slice( device, vk::BufferUsageFlags::TRANSFER_SRC, ImageReader::new(Cursor::new( @@ -239,7 +239,7 @@ fn load_font(device: &Device) -> anyhow::Result { )?); // This image will hold the font glyphs - let page_0 = graph.bind_node( + let page_0 = graph.bind_resource( Image::create( device, ImageInfo::image_2d( @@ -254,7 +254,7 @@ fn load_font(device: &Device) -> anyhow::Result { graph.copy_buffer_to_image(temp_buf, page_0); - let page_0 = graph.node(page_0).clone(); + let page_0 = graph.resource(page_0).clone(); // This copy happens in queue index 0! graph.resolve().submit(&mut HashPool::new(device), 0, 0)?; diff --git a/examples/ray_omni.rs b/examples/ray_omni.rs index 1d2d21e0..d5a0cb5d 100644 --- a/examples/ray_omni.rs +++ b/examples/ray_omni.rs @@ -47,12 +47,12 @@ fn main() -> anyhow::Result<()> { let scene_tlas = create_tlas(frame.device, &mut pool, frame.graph, &scene_blas).unwrap(); - let ground_mesh_index_buf = frame.graph.bind_node(&ground_mesh.index_buf); - let ground_mesh_vertex_buf = frame.graph.bind_node(&ground_mesh.vertex_buf); - let model_mesh_index_buf = frame.graph.bind_node(&model_mesh.index_buf); - let model_mesh_vertex_buf = frame.graph.bind_node(&model_mesh.vertex_buf); + let ground_mesh_index_buf = frame.graph.bind_resource(&ground_mesh.index_buf); + let ground_mesh_vertex_buf = frame.graph.bind_resource(&ground_mesh.vertex_buf); + let model_mesh_index_buf = frame.graph.bind_resource(&model_mesh.index_buf); + let model_mesh_vertex_buf = frame.graph.bind_resource(&model_mesh.vertex_buf); - let depth_image = frame.graph.bind_node( + let depth_image = frame.graph.bind_resource( pool.lease(ImageInfo::image_2d( frame.width, frame.height, @@ -61,7 +61,7 @@ fn main() -> anyhow::Result<()> { )) .unwrap(), ); - let camera_buf = frame.graph.bind_node({ + let camera_buf = frame.graph.bind_resource({ let mut buf = pool .lease(BufferInfo::host_mem( size_of::() as _, @@ -90,14 +90,14 @@ fn main() -> anyhow::Result<()> { frame .graph .begin_cmd() - .with_name("Mesh with ray-query shadows") + .debug_name("Mesh with ray-query shadows") .bind_pipeline(&gfx_pipeline) - .access_node(ground_mesh_index_buf, AccessType::IndexBuffer) - .access_node(ground_mesh_vertex_buf, AccessType::VertexBuffer) - .access_node(model_mesh_index_buf, AccessType::IndexBuffer) - .access_node(model_mesh_vertex_buf, AccessType::VertexBuffer) - .access_descriptor(0, camera_buf, AccessType::AnyShaderReadUniformBuffer) - .access_descriptor( + .resource_access(ground_mesh_index_buf, AccessType::IndexBuffer) + .resource_access(ground_mesh_vertex_buf, AccessType::VertexBuffer) + .resource_access(model_mesh_index_buf, AccessType::IndexBuffer) + .resource_access(model_mesh_vertex_buf, AccessType::VertexBuffer) + .shader_resource_access(0, camera_buf, AccessType::AnyShaderReadUniformBuffer) + .shader_resource_access( 1, scene_tlas, AccessType::RayTracingShaderReadAccelerationStructure, @@ -177,7 +177,7 @@ fn create_blas( let size = AccelerationStructure::size_of(device, &info); let mut graph = Graph::default(); - let blas = graph.bind_node(AccelerationStructure::create( + let blas = graph.bind_resource(AccelerationStructure::create( device, AccelerationStructureInfo::blas(size.create_size), )?); @@ -189,7 +189,7 @@ fn create_blas( .unwrap() .min_accel_struct_scratch_offset_alignment as vk::DeviceSize; - let scratch_buf = graph.bind_node(Buffer::create( + let scratch_buf = graph.bind_resource(Buffer::create( device, BufferInfo::device_mem( size.build_size, @@ -198,29 +198,29 @@ fn create_blas( .to_builder() .alignment(accel_struct_scratch_offset_alignment), )?); - let scratch_data = graph.node_device_address(scratch_buf); + let scratch_addr = graph.resource(scratch_buf).device_address(); - let mut pass = graph.begin_cmd().with_name("Build BLAS"); + let mut pass = graph.begin_cmd().debug_name("Build BLAS"); for model in models.iter().copied() { - let index_buf = pass.bind_node(&model.index_buf); - let vertex_buf = pass.bind_node(&model.vertex_buf); + let index_buf = pass.bind_resource(&model.index_buf); + let vertex_buf = pass.bind_resource(&model.vertex_buf); - pass.access_node_mut(index_buf, AccessType::AccelerationStructureBuildRead); - pass.access_node_mut(vertex_buf, AccessType::AccelerationStructureBuildRead); + pass.set_resource_access(index_buf, AccessType::AccelerationStructureBuildRead); + pass.set_resource_access(vertex_buf, AccessType::AccelerationStructureBuildRead); } - pass.access_node(blas, AccessType::AccelerationStructureBuildWrite) - .access_node(scratch_buf, AccessType::AccelerationStructureBufferWrite) + pass.resource_access(blas, AccessType::AccelerationStructureBuildWrite) + .resource_access(scratch_buf, AccessType::AccelerationStructureBufferWrite) .record_accel_struct(move |accel_struct, _| { accel_struct.build(&[BuildAccelerationStructureInfo::new( blas, - scratch_data, + scratch_addr, info, )]); }); - let blas = graph.node(blas).clone(); + let blas = graph.resource(blas).clone(); graph.resolve().submit(&mut LazyPool::new(device), 0, 0)?; @@ -362,7 +362,7 @@ fn create_tlas( )]) .flags(vk::BuildAccelerationStructureFlagsKHR::PREFER_FAST_TRACE); let size = AccelerationStructure::size_of(device, &info); - let tlas = graph.bind_node(pool.lease(AccelerationStructureInfo::tlas(size.create_size))?); + let tlas = graph.bind_resource(pool.lease(AccelerationStructureInfo::tlas(size.create_size))?); let accel_struct_scratch_offset_alignment = device .physical_device @@ -371,7 +371,7 @@ fn create_tlas( .unwrap() .min_accel_struct_scratch_offset_alignment as vk::DeviceSize; - let scratch_buf = graph.bind_node( + let scratch_buf = graph.bind_resource( pool.lease( BufferInfo::device_mem( size.build_size, @@ -381,21 +381,21 @@ fn create_tlas( .alignment(accel_struct_scratch_offset_alignment), )?, ); - let scratch_data = graph.node_device_address(scratch_buf); - let blas = graph.bind_node(blas); - let instance_buf = graph.bind_node(instance_buf); + let scratch_addr = graph.resource(scratch_buf).device_address(); + let blas = graph.bind_resource(blas); + let instance_buf = graph.bind_resource(instance_buf); graph .begin_cmd() - .with_name("Build TLAS") - .access_node(blas, AccessType::AccelerationStructureBuildRead) - .access_node(instance_buf, AccessType::AccelerationStructureBuildRead) - .access_node(scratch_buf, AccessType::AccelerationStructureBufferWrite) - .access_node(tlas, AccessType::AccelerationStructureBuildWrite) + .debug_name("Build TLAS") + .resource_access(blas, AccessType::AccelerationStructureBuildRead) + .resource_access(instance_buf, AccessType::AccelerationStructureBuildRead) + .resource_access(scratch_buf, AccessType::AccelerationStructureBufferWrite) + .resource_access(tlas, AccessType::AccelerationStructureBuildWrite) .record_accel_struct(move |accel_struct, _| { accel_struct.build(&[BuildAccelerationStructureInfo::new( tlas, - scratch_data, + scratch_addr, info, )]); }); diff --git a/examples/ray_trace.rs b/examples/ray_trace.rs index 48b39ff8..88b33b10 100644 --- a/examples/ray_trace.rs +++ b/examples/ray_trace.rs @@ -669,12 +669,12 @@ fn main() -> anyhow::Result<()> { .min_accel_struct_scratch_offset_alignment as vk::DeviceSize; let mut graph = Graph::default(); - let index_node = graph.bind_node(&index_buf); - let vertex_node = graph.bind_node(&vertex_buf); - let blas_node = graph.bind_node(&blas); + let index_node = graph.bind_resource(&index_buf); + let vertex_node = graph.bind_resource(&vertex_buf); + let blas_node = graph.bind_resource(&blas); { - let scratch_buf = graph.bind_node(Buffer::create( + let scratch_buf = graph.bind_resource(Buffer::create( &window.device, BufferInfo::device_mem( blas_size.build_size, @@ -684,15 +684,15 @@ fn main() -> anyhow::Result<()> { .to_builder() .alignment(accel_struct_scratch_offset_alignment), )?); - let scratch_data = graph.node_device_address(scratch_buf); + let scratch_data = graph.resource(scratch_buf).device_address(); graph .begin_cmd() - .with_name("Build BLAS") - .access_node(index_node, AccessType::AccelerationStructureBuildRead) - .access_node(vertex_node, AccessType::AccelerationStructureBuildRead) - .access_node(scratch_buf, AccessType::AccelerationStructureBufferWrite) - .access_node(blas_node, AccessType::AccelerationStructureBuildWrite) + .debug_name("Build BLAS") + .resource_access(index_node, AccessType::AccelerationStructureBuildRead) + .resource_access(vertex_node, AccessType::AccelerationStructureBuildRead) + .resource_access(scratch_buf, AccessType::AccelerationStructureBufferWrite) + .resource_access(blas_node, AccessType::AccelerationStructureBuildWrite) .record_accel_struct(move |accel_struct, _| { accel_struct.build(&[BuildAccelerationStructureInfo::new( blas_node, @@ -703,7 +703,7 @@ fn main() -> anyhow::Result<()> { } { - let scratch_buf = graph.bind_node(Buffer::create( + let scratch_buf = graph.bind_resource(Buffer::create( &window.device, BufferInfo::device_mem( tlas_size.build_size, @@ -713,21 +713,21 @@ fn main() -> anyhow::Result<()> { .to_builder() .alignment(accel_struct_scratch_offset_alignment), )?); - let scratch_data = graph.node_device_address(scratch_buf); - let instance_node = graph.bind_node(&instance_buf); - let tlas_node = graph.bind_node(&tlas); + let scratch_addr = graph.resource(scratch_buf).device_address(); + let instance_node = graph.bind_resource(&instance_buf); + let tlas_node = graph.bind_resource(&tlas); graph .begin_cmd() - .with_name("Build TLAS") - .access_node(blas_node, AccessType::AccelerationStructureBuildRead) - .access_node(instance_node, AccessType::AccelerationStructureBuildRead) - .access_node(scratch_buf, AccessType::AccelerationStructureBufferWrite) - .access_node(tlas_node, AccessType::AccelerationStructureBuildWrite) + .debug_name("Build TLAS") + .resource_access(blas_node, AccessType::AccelerationStructureBuildRead) + .resource_access(instance_node, AccessType::AccelerationStructureBuildRead) + .resource_access(scratch_buf, AccessType::AccelerationStructureBufferWrite) + .resource_access(tlas_node, AccessType::AccelerationStructureBuildWrite) .record_accel_struct(move |accel_struct, _| { accel_struct.build(&[BuildAccelerationStructureInfo::new( tlas_node, - scratch_data, + scratch_addr, tlas_geometry_info, )]); }); @@ -761,7 +761,7 @@ fn main() -> anyhow::Result<()> { .lease(ImageInfo::image_2d( frame.width, frame.height, - frame.graph.node_info(frame.swapchain_image).fmt, + frame.graph.resource(frame.swapchain_image).info.fmt, vk::ImageUsageFlags::STORAGE | vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::TRANSFER_SRC, @@ -770,7 +770,7 @@ fn main() -> anyhow::Result<()> { )); } - let image_node = frame.graph.bind_node(image.as_ref().unwrap()); + let image_node = frame.graph.bind_resource(image.as_ref().unwrap()); { input.step_with_window_events( @@ -817,7 +817,7 @@ fn main() -> anyhow::Result<()> { } } - let camera_buf = frame.graph.bind_node({ + let camera_buf = frame.graph.bind_resource({ #[repr(C)] struct Camera { position: [f32; 4], @@ -848,39 +848,39 @@ fn main() -> anyhow::Result<()> { buf }); - let blas_node = frame.graph.bind_node(&blas); - let tlas_node = frame.graph.bind_node(&tlas); - let index_buf_node = frame.graph.bind_node(&index_buf); - let vertex_buf_node = frame.graph.bind_node(&vertex_buf); - let material_id_buf_node = frame.graph.bind_node(&material_id_buf); - let material_buf_node = frame.graph.bind_node(&material_buf); - let sbt_node = frame.graph.bind_node(&sbt_buf); + let blas_node = frame.graph.bind_resource(&blas); + let tlas_node = frame.graph.bind_resource(&tlas); + let index_buf_node = frame.graph.bind_resource(&index_buf); + let vertex_buf_node = frame.graph.bind_resource(&vertex_buf); + let material_id_buf_node = frame.graph.bind_resource(&material_id_buf); + let material_buf_node = frame.graph.bind_resource(&material_buf); + let sbt_node = frame.graph.bind_resource(&sbt_buf); frame .graph .begin_cmd() - .with_name("basic ray tracer") + .debug_name("basic ray tracer") .bind_pipeline(&ray_trace_pipeline) - .access_node( + .resource_access( blas_node, AccessType::RayTracingShaderReadAccelerationStructure, ) - .access_node(sbt_node, AccessType::RayTracingShaderReadOther) - .access_descriptor( + .resource_access(sbt_node, AccessType::RayTracingShaderReadOther) + .shader_resource_access( 0, tlas_node, AccessType::RayTracingShaderReadAccelerationStructure, ) - .access_descriptor(1, camera_buf, AccessType::RayTracingShaderReadOther) - .access_descriptor(2, index_buf_node, AccessType::RayTracingShaderReadOther) - .access_descriptor(3, vertex_buf_node, AccessType::RayTracingShaderReadOther) - .write_descriptor(4, image_node) - .access_descriptor( + .shader_resource_access(1, camera_buf, AccessType::RayTracingShaderReadOther) + .shader_resource_access(2, index_buf_node, AccessType::RayTracingShaderReadOther) + .shader_resource_access(3, vertex_buf_node, AccessType::RayTracingShaderReadOther) + .shader_resource_access(4, image_node, AccessType::AnyShaderWrite) + .shader_resource_access( 5, material_id_buf_node, AccessType::RayTracingShaderReadOther, ) - .access_descriptor(6, material_buf_node, AccessType::RayTracingShaderReadOther) + .shader_resource_access(6, material_buf_node, AccessType::RayTracingShaderReadOther) .record_pipeline(move |pipeline, _| { pipeline.trace_rays( &sbt_rgen, diff --git a/examples/rt_triangle.rs b/examples/rt_triangle.rs index a77adc40..1ffdadfc 100644 --- a/examples/rt_triangle.rs +++ b/examples/rt_triangle.rs @@ -323,12 +323,12 @@ fn main() -> anyhow::Result<()> { .min_accel_struct_scratch_offset_alignment as vk::DeviceSize; let mut graph = Graph::default(); - let index_node = graph.bind_node(&index_buf); - let vertex_node = graph.bind_node(&vertex_buf); - let blas_node = graph.bind_node(&blas); + let index_node = graph.bind_resource(&index_buf); + let vertex_node = graph.bind_resource(&vertex_buf); + let blas_node = graph.bind_resource(&blas); { - let scratch_buf = graph.bind_node(Buffer::create( + let scratch_buf = graph.bind_resource(Buffer::create( &window.device, BufferInfo::device_mem( blas_size.build_size, @@ -338,15 +338,15 @@ fn main() -> anyhow::Result<()> { .to_builder() .alignment(accel_struct_scratch_offset_alignment), )?); - let scratch_data = graph.node_device_address(scratch_buf); + let scratch_data = graph.resource(scratch_buf).device_address(); graph .begin_cmd() - .with_name("Build BLAS") - .access_node(index_node, AccessType::AccelerationStructureBuildRead) - .access_node(vertex_node, AccessType::AccelerationStructureBuildRead) - .access_node(scratch_buf, AccessType::AccelerationStructureBufferWrite) - .access_node(blas_node, AccessType::AccelerationStructureBuildWrite) + .debug_name("Build BLAS") + .resource_access(index_node, AccessType::AccelerationStructureBuildRead) + .resource_access(vertex_node, AccessType::AccelerationStructureBuildRead) + .resource_access(scratch_buf, AccessType::AccelerationStructureBufferWrite) + .resource_access(blas_node, AccessType::AccelerationStructureBuildWrite) .record_accel_struct(move |accel_struct, _| { accel_struct.build(&[BuildAccelerationStructureInfo::new( blas_node, @@ -357,8 +357,8 @@ fn main() -> anyhow::Result<()> { } { - let instance_node = graph.bind_node(instance_buf); - let scratch_buf = graph.bind_node(Buffer::create( + let instance_node = graph.bind_resource(instance_buf); + let scratch_buf = graph.bind_resource(Buffer::create( &window.device, BufferInfo::device_mem( tlas_size.build_size, @@ -368,16 +368,16 @@ fn main() -> anyhow::Result<()> { .to_builder() .alignment(accel_struct_scratch_offset_alignment), )?); - let scratch_data = graph.node_device_address(scratch_buf); - let tlas_node = graph.bind_node(&tlas); + let scratch_data = graph.resource(scratch_buf).device_address(); + let tlas_node = graph.bind_resource(&tlas); graph .begin_cmd() - .with_name("Build TLAS") - .access_node(blas_node, AccessType::AccelerationStructureBuildRead) - .access_node(instance_node, AccessType::AccelerationStructureBuildRead) - .access_node(scratch_buf, AccessType::AccelerationStructureBufferWrite) - .access_node(tlas_node, AccessType::AccelerationStructureBuildWrite) + .debug_name("Build TLAS") + .resource_access(blas_node, AccessType::AccelerationStructureBuildRead) + .resource_access(instance_node, AccessType::AccelerationStructureBuildRead) + .resource_access(scratch_buf, AccessType::AccelerationStructureBufferWrite) + .resource_access(tlas_node, AccessType::AccelerationStructureBuildWrite) .record_accel_struct(move |accel_struct, _| { accel_struct.build(&[BuildAccelerationStructureInfo::new( tlas_node, @@ -398,26 +398,26 @@ fn main() -> anyhow::Result<()> { // - Trace the image // - Copy image to the swapchain window.run(|frame| { - let blas_node = frame.graph.bind_node(&blas); - let tlas_node = frame.graph.bind_node(&tlas); - let sbt_node = frame.graph.bind_node(&sbt_buf); + let blas_node = frame.graph.bind_resource(&blas); + let tlas_node = frame.graph.bind_resource(&tlas); + let sbt_node = frame.graph.bind_resource(&sbt_buf); frame .graph .begin_cmd() - .with_name("ray-traced triangle") + .debug_name("ray-traced triangle") .bind_pipeline(&ray_trace_pipeline) - .access_node( + .resource_access( blas_node, AccessType::RayTracingShaderReadAccelerationStructure, ) - .access_node(sbt_node, AccessType::RayTracingShaderReadOther) - .access_descriptor( + .resource_access(sbt_node, AccessType::RayTracingShaderReadOther) + .shader_resource_access( 0, tlas_node, AccessType::RayTracingShaderReadAccelerationStructure, ) - .write_descriptor(1, frame.swapchain_image) + .shader_resource_access(1, frame.swapchain_image, AccessType::AnyShaderWrite) .record_pipeline(move |pipeline, _| { pipeline.trace_rays( &sbt_rgen, diff --git a/examples/shader-toy/src/main.rs b/examples/shader-toy/src/main.rs index 6fb85582..e4126ae5 100644 --- a/examples/shader-toy/src/main.rs +++ b/examples/shader-toy/src/main.rs @@ -123,7 +123,7 @@ fn main() -> anyhow::Result<()> { .context("FLOCKAROO_IMG_FRAG")?; let mut graph = Graph::default(); - let blank_image = graph.bind_node( + let blank_image = graph.bind_resource( cache .lease(ImageInfo::image_2d( 8, @@ -135,7 +135,7 @@ fn main() -> anyhow::Result<()> { ); let (width, height) = (1280, 720); - let framebuffer_image = graph.bind_node( + let framebuffer_image = graph.bind_resource( cache .lease(ImageInfo::image_2d( width, @@ -148,7 +148,7 @@ fn main() -> anyhow::Result<()> { )) .context("Framebuffer image")?, ); - let temp_image = graph.bind_node( + let temp_image = graph.bind_resource( cache .lease(ImageInfo::image_2d( width, @@ -188,13 +188,21 @@ fn main() -> anyhow::Result<()> { count += 1; // Bind things to this graph (the graph will own our things until we unbind them) - let flowers_image = frame.graph.bind_node(flowers_image_binding.take().unwrap()); - let noise_image = frame.graph.bind_node(noise_image_binding.take().unwrap()); + let flowers_image = frame + .graph + .bind_resource(flowers_image_binding.take().unwrap()); + let noise_image = frame + .graph + .bind_resource(noise_image_binding.take().unwrap()); let framebuffer_image = frame .graph - .bind_node(framebuffer_image_binding.take().unwrap()); - let blank_image = frame.graph.bind_node(blank_image_binding.take().unwrap()); - let temp_image = frame.graph.bind_node(temp_image_binding.take().unwrap()); + .bind_resource(framebuffer_image_binding.take().unwrap()); + let blank_image = frame + .graph + .bind_resource(blank_image_binding.take().unwrap()); + let temp_image = frame + .graph + .bind_resource(temp_image_binding.take().unwrap()); // We need to push a shader-toy defined set of constants to each pipeline - any copy // type will do but we are getting fancy here by defining a struct to be super precise @@ -279,7 +287,7 @@ fn main() -> anyhow::Result<()> { frame .graph .begin_cmd() - .with_name("Buffer A") + .debug_name("Buffer A") .bind_pipeline(&buffer_pipeline) .read_descriptor(0, input) .read_descriptor(1, noise_image) @@ -296,7 +304,7 @@ fn main() -> anyhow::Result<()> { frame .graph .begin_cmd() - .with_name("Image") + .debug_name("Image") .bind_pipeline(&image_pipeline) .read_descriptor(0, output) .store_color(0, input) diff --git a/examples/skeletal-anim/src/main.rs b/examples/skeletal-anim/src/main.rs index 51724dbb..0687f985 100644 --- a/examples/skeletal-anim/src/main.rs +++ b/examples/skeletal-anim/src/main.rs @@ -59,9 +59,9 @@ fn main() -> Result<(), WindowError> { window.run(|frame| { let elapsed = (Instant::now() - started).as_secs_f32(); - let index_buf = frame.graph.bind_node(&character.index_buf); - let vertex_buf = frame.graph.bind_node(&character.vertex_buf); - let depth_image = frame.graph.bind_node( + let index_buf = frame.graph.bind_resource(&character.index_buf); + let vertex_buf = frame.graph.bind_resource(&character.vertex_buf); + let depth_image = frame.graph.bind_resource( pool.lease(ImageInfo::image_2d( frame.width, frame.height, @@ -73,14 +73,14 @@ fn main() -> Result<(), WindowError> { let texture = frame .graph - .bind_node(match (elapsed / 2.0).rem_euclid(4.0) { + .bind_resource(match (elapsed / 2.0).rem_euclid(4.0) { t if t < 1.0 => &human_female, t if t < 2.0 => &human_male, t if t < 3.0 => &zombie_female, _ => &zombie_male, }); - let camera_buf = frame.graph.bind_node({ + let camera_buf = frame.graph.bind_resource({ let position = Vec3::ONE * 3.0; let aspect_ratio = frame.render_aspect_ratio(); let projection = Mat4::perspective_rh(45.0, aspect_ratio, 0.1, 100.0); @@ -105,7 +105,7 @@ fn main() -> Result<(), WindowError> { buf }); - let animation_buf = frame.graph.bind_node({ + let animation_buf = frame.graph.bind_resource({ let animation = match (elapsed / 4.0).rem_euclid(2.0) { t if t < 1.0 => &mut run, _ => &mut idle, @@ -126,13 +126,13 @@ fn main() -> Result<(), WindowError> { frame .graph .begin_cmd() - .with_name("🦴") + .debug_name("🦴") .bind_pipeline(&pipeline) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) - .access_node(index_buf, AccessType::IndexBuffer) - .access_node(vertex_buf, AccessType::VertexBuffer) - .access_descriptor(0, camera_buf, AccessType::VertexShaderReadUniformBuffer) - .access_descriptor(1, animation_buf, AccessType::VertexShaderReadOther) + .resource_access(index_buf, AccessType::IndexBuffer) + .resource_access(vertex_buf, AccessType::VertexBuffer) + .shader_resource_access(0, camera_buf, AccessType::VertexShaderReadUniformBuffer) + .shader_resource_access(1, animation_buf, AccessType::VertexShaderReadOther) .read_descriptor(2, texture) .clear_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) @@ -189,8 +189,8 @@ fn load_texture(device: &Device, pak: &mut PakBuf, key: &str) -> Result Result, DriverError> { let mut graph = Graph::default(); - let input_buf = graph.bind_node(Buffer::create_from_slice( + let input_buf = graph.bind_resource(Buffer::create_from_slice( device, vk::BufferUsageFlags::STORAGE_BUFFER, cast_slice(input_data), )?); - let output_buf = graph.bind_node(Arc::new(Buffer::create( + let output_buf = graph.bind_resource(Arc::new(Buffer::create( device, BufferInfo::host_mem( input_data.len() as vk::DeviceSize * size_of::() as vk::DeviceSize, @@ -85,7 +85,7 @@ fn exclusive_sum( let workgroup_count = input_data.len() as u32 / device.physical_device.properties_v1_1.subgroup_size; let reduce_count = workgroup_count - 1; - let workgroup_buf = graph.bind_node(Buffer::create( + let workgroup_buf = graph.bind_resource(Buffer::create( device, BufferInfo::device_mem( reduce_count.max(1) as vk::DeviceSize * size_of::() as vk::DeviceSize, @@ -96,7 +96,7 @@ fn exclusive_sum( if reduce_count > 0 { graph .begin_cmd() - .with_name("exclusive sum reduce") + .debug_name("exclusive sum reduce") .bind_pipeline(reduce_pipeline) .read_descriptor(0, input_buf) .write_descriptor(1, workgroup_buf) @@ -107,7 +107,7 @@ fn exclusive_sum( graph .begin_cmd() - .with_name("exclusive sum scan") + .debug_name("exclusive sum scan") .bind_pipeline(scan_pipeline) .read_descriptor(0, workgroup_buf) .read_descriptor(1, input_buf) @@ -116,7 +116,7 @@ fn exclusive_sum( compute.dispatch(workgroup_count, 1, 1); }); - let output_buf = graph.node(output_buf).clone(); + let output_buf = graph.resource(output_buf).clone(); let mut cmd_buf = graph.resolve().submit(&mut HashPool::new(device), 0, 0)?; let started = Instant::now(); @@ -184,19 +184,16 @@ fn create_reduce_pipeline(device: &Device) -> Result(), - }], - }), + .specialization( + SpecializationMap::new( + device + .physical_device + .properties_v1_1 + .subgroup_size + .to_ne_bytes(), + ) + .constant(0, 0, 4), + ), ) } @@ -243,14 +240,16 @@ fn create_exclusive_sum_pipeline(device: &Device) -> Result(), - }], - }), + .specialization( + SpecializationMap::new( + device + .physical_device + .properties_v1_1 + .subgroup_size + .to_ne_bytes(), + ) + .constant(0, 0, 4), + ), ) } diff --git a/examples/transitions.rs b/examples/transitions.rs index 207fdf52..7b00dc90 100644 --- a/examples/transitions.rs +++ b/examples/transitions.rs @@ -78,8 +78,8 @@ fn main() -> anyhow::Result<()> { }; // Bind images so we can graph them - let bart_image = frame.graph.bind_node(&bart_image); - let gulf_image = frame.graph.bind_node(&gulf_image); + let bart_image = frame.graph.bind_resource(&bart_image); + let gulf_image = frame.graph.bind_resource(&gulf_image); // Apply the current transition to the images and get a resultant image out; "blend_image" let transition = TRANSITIONS[curr_transition_idx]; diff --git a/examples/triangle.rs b/examples/triangle.rs index be429003..ea0c4204 100644 --- a/examples/triangle.rs +++ b/examples/triangle.rs @@ -79,16 +79,16 @@ fn main() -> Result<(), WindowError> { )?); window.run(|frame| { - let index_node = frame.graph.bind_node(&index_buf); - let vertex_node = frame.graph.bind_node(&vertex_buf); + let index_node = frame.graph.bind_resource(&index_buf); + let vertex_node = frame.graph.bind_resource(&vertex_buf); frame .graph .begin_cmd() - .with_name("Triangle Example") + .debug_name("Triangle Example") .bind_pipeline(&triangle_pipeline) - .access_node(index_node, AccessType::IndexBuffer) - .access_node(vertex_node, AccessType::VertexBuffer) + .resource_access(index_node, AccessType::IndexBuffer) + .resource_access(vertex_node, AccessType::VertexBuffer) .clear_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) .record_pipeline(move |pipeline, _| { diff --git a/examples/vertex_layout.rs b/examples/vertex_layout.rs index f36c51ca..b2390817 100644 --- a/examples/vertex_layout.rs +++ b/examples/vertex_layout.rs @@ -93,16 +93,16 @@ fn main() -> anyhow::Result<()> { } fn draw_triangle(frame: &mut FrameContext, pipeline: &GraphicPipeline, vertex_buf: &Arc) { - let vertex_buf = frame.graph.bind_node(vertex_buf); + let vertex_buf = frame.graph.bind_resource(vertex_buf); frame .graph .begin_cmd() - .with_name("Triangle") + .debug_name("Triangle") .bind_pipeline(pipeline) .load_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) - .access_node(vertex_buf, AccessType::VertexBuffer) + .resource_access(vertex_buf, AccessType::VertexBuffer) .record_pipeline(move |pipeline, _| { pipeline .bind_vertex_buffer(0, vertex_buf, 0) diff --git a/examples/vr/src/main.rs b/examples/vr/src/main.rs index 3c018126..73a5fecc 100644 --- a/examples/vr/src/main.rs +++ b/examples/vr/src/main.rs @@ -271,7 +271,7 @@ fn main() -> anyhow::Result<()> { } let mut graph = Graph::default(); - let depth_image = graph.bind_node( + let depth_image = graph.bind_resource( pool.lease(ImageInfo::image_2d_array( resolution.width, resolution.height, @@ -284,7 +284,7 @@ fn main() -> anyhow::Result<()> { let swapchain_image_index = swapchain.acquire_image().unwrap(); let swapchain_image = Swapchain::image(&swapchain, swapchain_image_index as _); - let swapchain_image = graph.bind_node(swapchain_image); + let swapchain_image = graph.bind_resource(swapchain_image); // Get the XR views and copy them into a leased uniform buffer let (_, views) = session.locate_views( @@ -300,7 +300,7 @@ fn main() -> anyhow::Result<()> { vk::BufferUsageFlags::UNIFORM_BUFFER, ))?; Buffer::copy_from_slice(&mut buf, 0, data); - graph.bind_node(buf) + graph.bind_resource(buf) }; session.sync_actions(&[(&action_set).into()]).unwrap(); @@ -339,43 +339,43 @@ fn main() -> anyhow::Result<()> { vk::BufferUsageFlags::UNIFORM_BUFFER, ))?; Buffer::copy_from_slice(&mut buf, 0, data); - graph.bind_node(buf) + graph.bind_resource(buf) }; graph.clear_color_image(swapchain_image, [0x00, 0x00, 0x00, 0xff]); if let Some(location) = left_hand_location { - let index_buf = graph.bind_node(&lincoln_hand_left.index_buf); - let vertex_buf = graph.bind_node(&lincoln_hand_left.vertex_buf); - let diffuse_texture = graph.bind_node(&lincoln_hand_left_diffuse); - let normal_texture = graph.bind_node(&lincoln_hand_left_normal); - let occlusion_texture = graph.bind_node(&lincoln_hand_left_occlusion); + let index_buf = graph.bind_resource(&lincoln_hand_left.index_buf); + let vertex_buf = graph.bind_resource(&lincoln_hand_left.vertex_buf); + let diffuse_texture = graph.bind_resource(&lincoln_hand_left_diffuse); + let normal_texture = graph.bind_resource(&lincoln_hand_left_normal); + let occlusion_texture = graph.bind_resource(&lincoln_hand_left_occlusion); let model_transform = pose_transform(location.pose); let push_consts = PushConstants::new(model_transform); graph .begin_cmd() - .with_name("Left hand") + .debug_name("Left hand") .bind_pipeline(hands_pipeline.hot()) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) .set_multiview(VIEW_MASK, VIEW_MASK) .store_color(0, swapchain_image) .clear_depth_stencil(depth_image) - .access_node(index_buf, AccessType::IndexBuffer) - .access_node(vertex_buf, AccessType::VertexBuffer) - .access_descriptor(0, camera_buf, AccessType::VertexShaderReadUniformBuffer) - .access_descriptor(1, light_buf, AccessType::VertexShaderReadUniformBuffer) - .access_descriptor( + .resource_access(index_buf, AccessType::IndexBuffer) + .resource_access(vertex_buf, AccessType::VertexBuffer) + .shader_resource_access(0, camera_buf, AccessType::VertexShaderReadUniformBuffer) + .shader_resource_access(1, light_buf, AccessType::VertexShaderReadUniformBuffer) + .shader_resource_access( 2, diffuse_texture, AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, ) - .access_descriptor( + .shader_resource_access( 3, normal_texture, AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, ) - .access_descriptor( + .shader_resource_access( 4, occlusion_texture, AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, @@ -390,37 +390,37 @@ fn main() -> anyhow::Result<()> { } if let Some(location) = right_hand_location { - let index_buf = graph.bind_node(&lincoln_hand_right.index_buf); - let vertex_buf = graph.bind_node(&lincoln_hand_right.vertex_buf); - let diffuse_texture = graph.bind_node(&lincoln_hand_right_diffuse); - let normal_texture = graph.bind_node(&lincoln_hand_right_normal); - let occlusion_texture = graph.bind_node(&lincoln_hand_right_occlusion); + let index_buf = graph.bind_resource(&lincoln_hand_right.index_buf); + let vertex_buf = graph.bind_resource(&lincoln_hand_right.vertex_buf); + let diffuse_texture = graph.bind_resource(&lincoln_hand_right_diffuse); + let normal_texture = graph.bind_resource(&lincoln_hand_right_normal); + let occlusion_texture = graph.bind_resource(&lincoln_hand_right_occlusion); let model_transform = pose_transform(location.pose); let push_consts = PushConstants::new(model_transform); graph .begin_cmd() - .with_name("Right hand") + .debug_name("Right hand") .bind_pipeline(hands_pipeline.hot()) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) .set_multiview(VIEW_MASK, VIEW_MASK) .store_color(0, swapchain_image) .clear_depth_stencil(depth_image) - .access_node(index_buf, AccessType::IndexBuffer) - .access_node(vertex_buf, AccessType::VertexBuffer) - .access_descriptor(0, camera_buf, AccessType::VertexShaderReadUniformBuffer) - .access_descriptor(1, light_buf, AccessType::VertexShaderReadUniformBuffer) - .access_descriptor( + .resource_access(index_buf, AccessType::IndexBuffer) + .resource_access(vertex_buf, AccessType::VertexBuffer) + .shader_resource_access(0, camera_buf, AccessType::VertexShaderReadUniformBuffer) + .shader_resource_access(1, light_buf, AccessType::VertexShaderReadUniformBuffer) + .shader_resource_access( 2, diffuse_texture, AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, ) - .access_descriptor( + .shader_resource_access( 3, normal_texture, AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, ) - .access_descriptor( + .shader_resource_access( 4, occlusion_texture, AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, @@ -435,30 +435,30 @@ fn main() -> anyhow::Result<()> { } { - let index_buf = graph.bind_node(&woolly_mammoth.index_buf); - let vertex_buf = graph.bind_node(&woolly_mammoth.vertex_buf); - let normal_texture = graph.bind_node(&woolly_mammoth_normal); - let occlusion_texture = graph.bind_node(&woolly_mammoth_occlusion); + let index_buf = graph.bind_resource(&woolly_mammoth.index_buf); + let vertex_buf = graph.bind_resource(&woolly_mammoth.vertex_buf); + let normal_texture = graph.bind_resource(&woolly_mammoth_normal); + let occlusion_texture = graph.bind_resource(&woolly_mammoth_occlusion); let push_consts = PushConstants::new(Mat4::IDENTITY); graph .begin_cmd() - .with_name("Woolly Mammoth") + .debug_name("Woolly Mammoth") .bind_pipeline(mammoth_pipeline.hot()) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) .set_multiview(VIEW_MASK, VIEW_MASK) .store_color(0, swapchain_image) .clear_depth_stencil(depth_image) - .access_node(index_buf, AccessType::IndexBuffer) - .access_node(vertex_buf, AccessType::VertexBuffer) - .access_descriptor(0, camera_buf, AccessType::VertexShaderReadUniformBuffer) - .access_descriptor(1, light_buf, AccessType::VertexShaderReadUniformBuffer) - .access_descriptor( + .resource_access(index_buf, AccessType::IndexBuffer) + .resource_access(vertex_buf, AccessType::VertexBuffer) + .shader_resource_access(0, camera_buf, AccessType::VertexShaderReadUniformBuffer) + .shader_resource_access(1, light_buf, AccessType::VertexShaderReadUniformBuffer) + .shader_resource_access( 2, normal_texture, AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, ) - .access_descriptor( + .shader_resource_access( 3, occlusion_texture, AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, @@ -765,8 +765,8 @@ fn load_texture( )?); let mut graph = Graph::default(); - let staging_buf = graph.bind_node(staging_buf); - let texture_image = graph.bind_node(&texture); + let staging_buf = graph.bind_resource(staging_buf); + let texture_image = graph.bind_resource(&texture); graph.copy_buffer_to_image(staging_buf, texture_image); let queue_family_index = device_queue_family_index(device, vk::QueueFlags::TRANSFER).unwrap(); diff --git a/examples/vsm_omni.rs b/examples/vsm_omni.rs index a63041dc..e6a18d97 100644 --- a/examples/vsm_omni.rs +++ b/examples/vsm_omni.rs @@ -177,20 +177,20 @@ fn main() -> anyhow::Result<()> { }; // Bind resources to the render graph of the current frame - let cube_mesh_index_buf = frame.graph.bind_node(&cube_mesh.index_buf); - let cube_mesh_vertex_buf = frame.graph.bind_node(&cube_mesh.vertex_buf); - let cube_shadow_index_buf = frame.graph.bind_node(&cube_shadow.index_buf); - let cube_shadow_vertex_buf = frame.graph.bind_node(&cube_shadow.vertex_buf); - let model_mesh_index_buf = frame.graph.bind_node(&model_mesh.index_buf); - let model_mesh_vertex_buf = frame.graph.bind_node(&model_mesh.vertex_buf); - let model_shadow_index_buf = frame.graph.bind_node(&model_shadow.index_buf); - let model_shadow_vertex_buf = frame.graph.bind_node(&model_shadow.vertex_buf); + let cube_mesh_index_buf = frame.graph.bind_resource(&cube_mesh.index_buf); + let cube_mesh_vertex_buf = frame.graph.bind_resource(&cube_mesh.vertex_buf); + let cube_shadow_index_buf = frame.graph.bind_resource(&cube_shadow.index_buf); + let cube_shadow_vertex_buf = frame.graph.bind_resource(&cube_shadow.vertex_buf); + let model_mesh_index_buf = frame.graph.bind_resource(&model_mesh.index_buf); + let model_mesh_vertex_buf = frame.graph.bind_resource(&model_mesh.vertex_buf); + let model_shadow_index_buf = frame.graph.bind_resource(&model_shadow.index_buf); + let model_shadow_vertex_buf = frame.graph.bind_resource(&model_shadow.vertex_buf); let camera_uniform_buf = frame .graph - .bind_node(lease_uniform_buffer(&mut pool, &camera).unwrap()); + .bind_resource(lease_uniform_buffer(&mut pool, &camera).unwrap()); let light_uniform_buf = frame .graph - .bind_node(lease_uniform_buffer(&mut pool, &light).unwrap()); + .bind_resource(lease_uniform_buffer(&mut pool, &light).unwrap()); // Lease and bind a cube-compatible shadow 2D image array to the graph of the current frame let shadow_faces_image = pool @@ -209,15 +209,15 @@ fn main() -> anyhow::Result<()> { ) .unwrap(); let shadow_faces_info = shadow_faces_image.info; - let shadow_faces_node = frame.graph.bind_node(shadow_faces_image); + let shadow_faces_node = frame.graph.bind_resource(shadow_faces_image); // Lease and bind a temporary image we'll use during blur passes let temp_image = frame .graph - .bind_node(pool.lease(shadow_faces_info).unwrap()); + .bind_resource(pool.lease(shadow_faces_info).unwrap()); // Lastly we lease and bind depth images needed for rendering - let shadow_depth_image = frame.graph.bind_node( + let shadow_depth_image = frame.graph.bind_resource( pool.lease(ImageInfo::image_2d_array( frame.width, frame.height, @@ -227,7 +227,7 @@ fn main() -> anyhow::Result<()> { )) .unwrap(), ); - let depth_image = frame.graph.bind_node( + let depth_image = frame.graph.bind_resource( pool.lease(ImageInfo::image_2d( frame.width, frame.height, @@ -242,19 +242,23 @@ fn main() -> anyhow::Result<()> { frame .graph .begin_cmd() - .with_name("DEBUG") + .debug_name("DEBUG") .bind_pipeline(&debug_pipeline) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) - .access_descriptor( + .shader_resource_access( 0, camera_uniform_buf, AccessType::AnyShaderReadUniformBuffer, ) - .access_descriptor(1, light_uniform_buf, AccessType::AnyShaderReadUniformBuffer) - .access_node(model_mesh_index_buf, AccessType::IndexBuffer) - .access_node(model_mesh_vertex_buf, AccessType::VertexBuffer) - .access_node(cube_mesh_index_buf, AccessType::IndexBuffer) - .access_node(cube_mesh_vertex_buf, AccessType::VertexBuffer) + .shader_resource_access( + 1, + light_uniform_buf, + AccessType::AnyShaderReadUniformBuffer, + ) + .resource_access(model_mesh_index_buf, AccessType::IndexBuffer) + .resource_access(model_mesh_vertex_buf, AccessType::VertexBuffer) + .resource_access(cube_mesh_index_buf, AccessType::IndexBuffer) + .resource_access(cube_mesh_vertex_buf, AccessType::VertexBuffer) .clear_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) .clear_depth_stencil(depth_image) @@ -276,14 +280,18 @@ fn main() -> anyhow::Result<()> { frame .graph .begin_cmd() - .with_name("Shadow (Using geometry shader)") + .debug_name("Shadow (Using geometry shader)") .bind_pipeline(&shadow_pipeline) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) - .access_descriptor(0, light_uniform_buf, AccessType::AnyShaderReadUniformBuffer) - .access_node(model_shadow_index_buf, AccessType::IndexBuffer) - .access_node(model_shadow_vertex_buf, AccessType::VertexBuffer) - .access_node(cube_shadow_index_buf, AccessType::IndexBuffer) - .access_node(cube_shadow_vertex_buf, AccessType::VertexBuffer) + .shader_resource_access( + 0, + light_uniform_buf, + AccessType::AnyShaderReadUniformBuffer, + ) + .resource_access(model_shadow_index_buf, AccessType::IndexBuffer) + .resource_access(model_shadow_vertex_buf, AccessType::VertexBuffer) + .resource_access(cube_shadow_index_buf, AccessType::IndexBuffer) + .resource_access(cube_shadow_vertex_buf, AccessType::VertexBuffer) .clear_color_value(0, shadow_faces_node, [light.range, light.range, 0.0, 0.0]) .store_color(0, shadow_faces_node) .clear_depth_stencil(shadow_depth_image) @@ -300,7 +308,7 @@ fn main() -> anyhow::Result<()> { }); } else { for array_layer in 0..6 { - let mut shadow_faces_view_info = shadow_faces_info.default_view_info(); + let mut shadow_faces_view_info = shadow_faces_info.into_image_view(); shadow_faces_view_info.array_layer_count = 1; shadow_faces_view_info.base_array_layer = array_layer; @@ -316,23 +324,23 @@ fn main() -> anyhow::Result<()> { let light_uniform_buf = frame .graph - .bind_node(lease_uniform_buffer(&mut pool, &light).unwrap()); + .bind_resource(lease_uniform_buffer(&mut pool, &light).unwrap()); frame .graph .begin_cmd() - .with_name("Shadow") + .debug_name("Shadow") .bind_pipeline(&shadow_pipeline) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) - .access_descriptor( + .shader_resource_access( 0, light_uniform_buf, AccessType::AnyShaderReadUniformBuffer, ) - .access_node(model_shadow_index_buf, AccessType::IndexBuffer) - .access_node(model_shadow_vertex_buf, AccessType::VertexBuffer) - .access_node(cube_shadow_index_buf, AccessType::IndexBuffer) - .access_node(cube_shadow_vertex_buf, AccessType::VertexBuffer) + .resource_access(model_shadow_index_buf, AccessType::IndexBuffer) + .resource_access(model_shadow_vertex_buf, AccessType::VertexBuffer) + .resource_access(cube_shadow_index_buf, AccessType::IndexBuffer) + .resource_access(cube_shadow_vertex_buf, AccessType::VertexBuffer) .clear_color_value_as( 0, shadow_faces_node, @@ -362,19 +370,27 @@ fn main() -> anyhow::Result<()> { frame .graph .begin_cmd() - .with_name("Blur X") + .debug_name("Blur X") .bind_pipeline(&blur_x_pipeline) - .read_descriptor(0, shadow_faces_node) - .write_descriptor(1, temp_image) + .shader_resource_access( + 0, + shadow_faces_node, + AccessType::ComputeShaderReadOther, + ) + .shader_resource_access(1, temp_image, AccessType::ComputeShaderWrite) .record_pipeline(move |compute, _| { compute.dispatch(1, CUBEMAP_SIZE, 6); }) .end_cmd() .begin_cmd() - .with_name("Blur Y") + .debug_name("Blur Y") .bind_pipeline(&blur_y_pipeline) - .read_descriptor(0, temp_image) - .write_descriptor(1, shadow_faces_node) + .shader_resource_access(0, temp_image, AccessType::ComputeShaderReadOther) + .shader_resource_access( + 1, + shadow_faces_node, + AccessType::ComputeShaderWrite, + ) .record_pipeline(move |compute, _| { compute.dispatch(CUBEMAP_SIZE, 1, 6); }); @@ -385,26 +401,31 @@ fn main() -> anyhow::Result<()> { frame .graph .begin_cmd() - .with_name("Mesh objects") + .debug_name("Mesh objects") .bind_pipeline(&mesh_pipeline) .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) - .access_descriptor( + .shader_resource_access( 0, camera_uniform_buf, AccessType::AnyShaderReadUniformBuffer, ) - .access_descriptor(1, light_uniform_buf, AccessType::AnyShaderReadUniformBuffer) - .read_descriptor_as( + .shader_resource_access( + 1, + light_uniform_buf, + AccessType::AnyShaderReadUniformBuffer, + ) + .shader_subresource_access( 2, shadow_faces_node, shadow_faces_info - .default_view_info() + .into_image_view() .with_type(vk::ImageViewType::CUBE), + AccessType::AnyShaderReadUniformBuffer, ) - .access_node(model_mesh_index_buf, AccessType::IndexBuffer) - .access_node(model_mesh_vertex_buf, AccessType::VertexBuffer) - .access_node(cube_mesh_index_buf, AccessType::IndexBuffer) - .access_node(cube_mesh_vertex_buf, AccessType::VertexBuffer) + .resource_access(model_mesh_index_buf, AccessType::IndexBuffer) + .resource_access(model_mesh_vertex_buf, AccessType::VertexBuffer) + .resource_access(cube_mesh_index_buf, AccessType::IndexBuffer) + .resource_access(cube_mesh_vertex_buf, AccessType::VertexBuffer) .clear_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) .clear_depth_stencil(depth_image) @@ -545,24 +566,14 @@ fn create_blur_x_pipeline(device: &Device) -> Result Result Result GraphicPipelineInfo::default(), [ Shader::new_vertex(vert.as_slice()), - Shader::new_fragment(frag.as_slice()).specialization_info(SpecializationInfo::new( - vec![vk::SpecializationMapEntry { - constant_id: 0, - offset: 0, - size: 4, - }], - bytes_of(&SHADOW_BIAS), - )), + Shader::new_fragment(frag.as_slice()) + .specialization(SpecializationMap::new(bytes_of(&SHADOW_BIAS)).constant(0, 0, 4)), ], ) } diff --git a/src/bind.rs b/src/bind.rs index e848a177..fc4e2687 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -1,13 +1,15 @@ use { super::{ - AccelerationStructureLeaseNode, AccelerationStructureNode, BufferLeaseNode, BufferNode, - Graph, ImageLeaseNode, ImageNode, SwapchainImageNode, + AccelerationStructureLeaseNode, AccelerationStructureNode, AnyAccelerationStructureNode, + AnyBufferNode, AnyImageNode, BufferLeaseNode, BufferNode, Graph, ImageLeaseNode, ImageNode, + SwapchainImageNode, }, crate::{ driver::{ accel_struct::AccelerationStructure, buffer::Buffer, image::Image, swapchain::SwapchainImage, }, + node::Node, pool::Lease, }, std::{fmt::Debug, sync::Arc}, @@ -15,17 +17,26 @@ use { /// A trait for resources which may be bound to a `Graph`. /// -/// See [`Graph::bind_node`] and -/// [`CommandRef::bind_pipeline`](super::cmd_ref::CommandRef::bind_pipeline) for details. -pub trait Bind { +/// See [`Graph::bind_resource`] and +/// [`CommandRef::bind_resource`](super::cmd_ref::CommandRef::bind_resource) for details. +pub trait BindGraph { + /// The resource handle type. + type Node; + /// Binds the resource to a graph. /// - /// Returns a node handle. - fn bind(self, graph: Graph) -> Node; + /// Returns a resource node handle. + fn bind_graph(self, graph: &mut Graph) -> Self::Node; + + #[deprecated = "use bind_graph function"] + #[doc(hidden)] + fn bind(self, graph: &mut Graph) -> Self::Node; } +/// TODO +#[allow(missing_docs)] #[derive(Debug)] -pub enum Binding { +pub enum Resource { AccelerationStructure(Arc), AccelerationStructureLease(Arc>), Buffer(Arc), @@ -35,212 +46,311 @@ pub enum Binding { SwapchainImage(Box), } -impl Binding { - pub(super) fn as_driver_acceleration_structure(&self) -> Option<&AccelerationStructure> { +impl Resource { + pub(super) fn as_driver_accel_struct(&self) -> Option<&AccelerationStructure> { Some(match self { - Self::AccelerationStructure(binding) => binding, - Self::AccelerationStructureLease(binding) => binding, + Self::AccelerationStructure(resource) => resource, + Self::AccelerationStructureLease(resource) => resource, _ => return None, }) } pub(super) fn as_driver_buffer(&self) -> Option<&Buffer> { Some(match self { - Self::Buffer(binding) => binding, - Self::BufferLease(binding) => binding, + Self::Buffer(resource) => resource, + Self::BufferLease(resource) => resource, _ => return None, }) } pub(super) fn as_driver_image(&self) -> Option<&Image> { Some(match self { - Self::Image(binding) => binding, - Self::ImageLease(binding) => binding, - Self::SwapchainImage(binding) => binding, + Self::Image(resource) => resource, + Self::ImageLease(resource) => resource, + Self::SwapchainImage(resource) => resource, _ => return None, }) } pub(super) fn as_swapchain_image(&self) -> Option<&SwapchainImage> { - let Self::SwapchainImage(binding) = self else { - // The private code in this module should prevent this branch - unreachable!(); - }; - - Some(binding) + Some(match self { + Self::SwapchainImage(resource) => resource, + _ => return None, + }) } } -impl Bind<&mut Graph, SwapchainImageNode> for SwapchainImage { - fn bind(self, graph: &mut Graph) -> SwapchainImageNode { +impl BindGraph for SwapchainImage { + type Node = SwapchainImageNode; + + fn bind_graph(self, graph: &mut Graph) -> Self::Node { // We will return a new node - let res = SwapchainImageNode::new(graph.bindings.len()); + let res = Self::Node::new(graph.resources.len()); //trace!("Node {}: {:?}", res.idx, &self); - graph.bindings.push(Binding::SwapchainImage(Box::new(self))); + graph + .resources + .push(Resource::SwapchainImage(Box::new(self))); res } + + fn bind(self, graph: &mut Graph) -> Self::Node { + self.bind_graph(graph) + } } -macro_rules! bind { +macro_rules! bind_graph_resource { ($name:ident) => { paste::paste! { - impl Bind<&mut Graph, [<$name Node>]> for $name { + impl BindGraph for $name { + type Node = [<$name Node>]; + #[profiling::function] - fn bind(self, graph: &mut Graph) -> [<$name Node>] { - // In this function we are binding a new item (Image or Buffer or etc) + fn bind_graph(self, graph: &mut Graph) -> Self::Node { + // In this function we are resource a new item (Image or Buffer or etc) // We will return a new node - let res = [<$name Node>]::new(graph.bindings.len()); - let binding = Binding::$name(Arc::new(self)); - graph.bindings.push(binding); + let res = Self::Node::new(graph.resources.len()); + let resource = Resource::$name(Arc::new(self)); + graph.resources.push(resource); res } - } - impl<'a> Bind<&mut Graph, [<$name Node>]> for &'a Arc<$name> { - fn bind(self, graph: &mut Graph) -> [<$name Node>] { - // In this function we are binding a borrowed binding (&Arc or - // &Arc or etc) - - Arc::clone(self).bind(graph) + fn bind(self, graph: &mut Graph) -> Self::Node { + self.bind_graph(graph) } } - impl Bind<&mut Graph, [<$name Node>]> for Arc<$name> { + impl BindGraph for Arc<$name> { + type Node = [<$name Node>]; + #[profiling::function] - fn bind(self, graph: &mut Graph) -> [<$name Node>] { - // In this function we are binding an existing binding (Arc or + fn bind_graph(self, graph: &mut Graph) -> Self::Node { + // In this function we are resource an existing resource (Arc or // Arc or etc) // We will return an existing node, if possible // TODO: Could store a sorted list of these shared pointers to avoid the O(N) - for (idx, existing_binding) in graph.bindings.iter_mut().enumerate() { - if let Some(existing_binding) = existing_binding.[]() { - if Arc::ptr_eq(existing_binding, &self) { - return [<$name Node>]::new(idx); + for (idx, existing_resource) in graph.resources.iter_mut().enumerate() { + if let Some(existing_resource) = existing_resource.[]() { + if Arc::ptr_eq(existing_resource, &self) { + return Self::Node::new(idx); } } } // Return a new node - let res = [<$name Node>]::new(graph.bindings.len()); - let binding = Binding::$name(self); - graph.bindings.push(binding); + let res = Self::Node::new(graph.resources.len()); + let resource = Resource::$name(self); + graph.resources.push(resource); res } + + fn bind(self, graph: &mut Graph) -> Self::Node { + self.bind_graph(graph) + } } - impl Binding { - pub(super) fn [](&self) -> Option<&Arc<$name>> { - if let Self::$name(binding) = self { - Some(&binding) - } else { - None - } + impl<'a> BindGraph for &'a Arc<$name> { + type Node = [<$name Node>]; + + fn bind_graph(self, graph: &mut Graph) -> Self::Node { + // In this function we are resource a borrowed resource (&Arc or + // &Arc or etc) + + Arc::clone(self).bind_graph(graph) } - pub(super) fn [](&mut self) -> Option<&mut Arc<$name>> { - if let Self::$name(binding) = self { - Some(binding) - } else { - None - } + fn bind(self, graph: &mut Graph) -> Self::Node { + self.bind_graph(graph) } } - } - }; -} -bind!(AccelerationStructure); -bind!(Image); -bind!(Buffer); + impl BindGraph for Lease<$name> { + type Node = [<$name LeaseNode>]; -macro_rules! bind_lease { - ($name:ident) => { - paste::paste! { - impl Bind<&mut Graph, [<$name LeaseNode>]> for Lease<$name> { #[profiling::function] - fn bind(self, graph: &mut Graph) -> [<$name LeaseNode>] { - // In this function we are binding a new lease (Lease or Lease or + fn bind_graph(self, graph: &mut Graph) -> Self::Node { + // In this function we are resource a new lease (Lease or Lease or // etc) // We will return a new node - let res = [<$name LeaseNode>]::new(graph.bindings.len()); - let binding = Binding::[<$name Lease>](Arc::new(self)); - graph.bindings.push(binding); + let res = Self::Node::new(graph.resources.len()); + let resource = Resource::[<$name Lease>](Arc::new(self)); + graph.resources.push(resource); res } - } - - impl<'a> Bind<&mut Graph, [<$name LeaseNode>]> for &'a Arc> { - fn bind(self, graph: &mut Graph) -> [<$name LeaseNode>] { - // In this function we are binding a borrowed binding (&Arc> or - // &Arc> or etc) - Arc::clone(self).bind(graph) + fn bind(self, graph: &mut Graph) -> Self::Node { + self.bind_graph(graph) } } - impl Bind<&mut Graph, [<$name LeaseNode>]> for Arc> { + impl BindGraph for Arc> { + type Node = [<$name LeaseNode>]; + #[profiling::function] - fn bind(self, graph: &mut Graph) -> [<$name LeaseNode>] { - // In this function we are binding an existing lease binding + fn bind_graph(self, graph: &mut Graph) -> Self::Node { + // In this function we are resource an existing lease resource // (Arc> or Arc> or etc) // We will return an existing node, if possible // TODO: Could store a sorted list of these shared pointers to avoid the O(N) - for (idx, existing_binding) in graph.bindings.iter_mut().enumerate() { - if let Some(existing_binding) = existing_binding.[]() { - if Arc::ptr_eq(existing_binding, &self) { - return [<$name LeaseNode>]::new(idx); + for (idx, existing_resource) in graph.resources.iter_mut().enumerate() { + if let Some(existing_resource) = existing_resource.[]() { + if Arc::ptr_eq(existing_resource, &self) { + return Self::Node::new(idx); } } } // We will return a new node - let res = [<$name LeaseNode>]::new(graph.bindings.len()); - let binding = Binding::[<$name Lease>](self); - graph.bindings.push(binding); + let res = Self::Node::new(graph.resources.len()); + let resource = Resource::[<$name Lease>](self); + graph.resources.push(resource); res } + + fn bind(self, graph: &mut Graph) -> Self::Node { + self.bind_graph(graph) + } + } + + impl<'a> BindGraph for &'a Arc> { + type Node = [<$name LeaseNode>]; + + fn bind_graph(self, graph: &mut Graph) -> Self::Node { + // In this function we are resource a borrowed resource (&Arc> or + // &Arc> or etc) + + Arc::clone(self).bind_graph(graph) + } + + fn bind(self, graph: &mut Graph) -> Self::Node { + self.bind_graph(graph) + } } - impl Binding { + impl Resource { + pub(super) fn [](&self) -> Option<&Arc<$name>> { + let Self::$name(resource) = self else { + return None; + }; + + Some(resource) + } + + pub(super) fn [](&mut self) -> Option<&mut Arc<$name>> { + let Self::$name(resource) = self else { + return None; + }; + + Some(resource) + } + pub(super) fn [](&self) -> Option<&Arc>> { - if let Self::[<$name Lease>](binding) = self { - Some(binding) - } else { - None - } + let Self::[<$name Lease>](resource) = self else { + return None + }; + + Some(resource) } pub(super) fn [](&mut self) -> Option<&Arc>> { - if let Self::[<$name Lease>](binding) = self { - Some(binding) - } else { - None - } + let Self::[<$name Lease>](resource) = self else { + return None; + }; + + Some(resource) } } } - } + }; } -bind_lease!(AccelerationStructure); -bind_lease!(Image); -bind_lease!(Buffer); +bind_graph_resource!(AccelerationStructure); +bind_graph_resource!(Image); +bind_graph_resource!(Buffer); /// A trait for resources which may be borrowed from a `Graph`. /// /// See [`Graph::node`] for details. -pub trait Bound { +pub trait Bound { + /// The Vulkan buffer, image, or acceleration struction type. + type Resource; + /// Borrows the resource from a graph. - fn borrow(self, graph: &Graph) -> &Binding; + fn borrow(self, graph: &Graph) -> &Self::Resource; +} + +impl Bound for AnyAccelerationStructureNode { + type Resource = AccelerationStructure; + + fn borrow(self, graph: &Graph) -> &Self::Resource { + graph.resources[self.index()] + .as_driver_accel_struct() + .unwrap() + } +} + +impl Bound for AnyBufferNode { + type Resource = Buffer; + + fn borrow(self, graph: &Graph) -> &Self::Resource { + graph.resources[self.index()].as_driver_buffer().unwrap() + } } + +impl Bound for AnyImageNode { + type Resource = Image; + + fn borrow(self, graph: &Graph) -> &Self::Resource { + graph.resources[self.index()].as_driver_image().unwrap() + } +} + +impl Bound for SwapchainImageNode { + type Resource = Image; + + fn borrow(self, graph: &Graph) -> &Self::Resource { + graph.resources[self.idx].as_swapchain_image().unwrap() + } +} + +macro_rules! bound { + ($name:ident) => { + paste::paste! { + impl Bound for [<$name Node>] { + type Resource = Arc<$name>; + + fn borrow(self, graph: &Graph) -> &Self::Resource { + graph + .resources[self.idx] + .[]() + .unwrap() + } + } + + impl Bound for [<$name LeaseNode>] { + type Resource = Arc>; + + fn borrow(self, graph: &Graph) -> &Self::Resource { + graph + .resources[self.idx] + .[]() + .unwrap() + } + } + } + }; +} + +bound!(AccelerationStructure); +bound!(Buffer); +bound!(Image); diff --git a/src/cmd_ref/accel_struct.rs b/src/cmd_ref/accel_struct.rs index 62fa4192..0979a684 100644 --- a/src/cmd_ref/accel_struct.rs +++ b/src/cmd_ref/accel_struct.rs @@ -3,6 +3,7 @@ use { crate::{ AnyAccelerationStructureNode, driver::{ + CommandBuffer, accel_struct::{ AccelerationStructureGeometry, AccelerationStructureGeometryInfo, DeviceOrHostAddress, @@ -43,8 +44,7 @@ use { /// # Ok(()) } /// ``` pub struct AccelerationStructureRef<'a> { - pub(super) cmd_buf: vk::CommandBuffer, - pub(super) device: &'a Device, + pub(super) cmd_buf: &'a CommandBuffer, pub(super) nodes: Nodes<'a>, } @@ -82,16 +82,16 @@ impl AccelerationStructureRef<'_> { /// # let mut my_graph = Graph::default(); /// # let info = AccelerationStructureInfo::blas(1); /// # let blas_accel_struct = AccelerationStructure::create(&device, info)?; - /// # let blas_node = my_graph.bind_node(blas_accel_struct); + /// # let blas_node = my_graph.bind_resource(blas_accel_struct); /// # let scratch_buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS); /// # let scratch_buf = Buffer::create(&device, scratch_buf_info)?; - /// # let scratch_buf = my_graph.bind_node(scratch_buf); + /// # let scratch_buf = my_graph.bind_resource(scratch_buf); /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::INDEX_BUFFER); /// # let my_idx_buf = Buffer::create(&device, buf_info)?; /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::VERTEX_BUFFER); /// # let my_vtx_buf = Buffer::create(&device, buf_info)?; - /// # let index_node = my_graph.bind_node(my_idx_buf); - /// # let vertex_node = my_graph.bind_node(my_vtx_buf); + /// # let index_node = my_graph.bind_resource(my_idx_buf); + /// # let vertex_node = my_graph.bind_resource(my_vtx_buf); /// my_graph.begin_cmd() /// .read_node(index_node) /// .read_node(vertex_node) @@ -191,11 +191,8 @@ impl AccelerationStructureRef<'_> { }; unsafe { - Device::expect_accel_struct_ext(self.device).cmd_build_acceleration_structures( - self.cmd_buf, - &vk_infos, - &vk_ranges, - ); + Device::expect_accel_struct_ext(&self.cmd_buf.device) + .cmd_build_acceleration_structures(self.cmd_buf.handle, &vk_infos, &vk_ranges); } }); @@ -270,9 +267,9 @@ impl AccelerationStructureRef<'_> { }; unsafe { - Device::expect_accel_struct_ext(self.device) + Device::expect_accel_struct_ext(&self.cmd_buf.device) .cmd_build_acceleration_structures_indirect( - self.cmd_buf, + self.cmd_buf.handle, &vk_infos, &tls.range_bases, &tls.range_strides, @@ -358,11 +355,8 @@ impl AccelerationStructureRef<'_> { }; unsafe { - Device::expect_accel_struct_ext(self.device).cmd_build_acceleration_structures( - self.cmd_buf, - &vk_infos, - &vk_ranges, - ); + Device::expect_accel_struct_ext(&self.cmd_buf.device) + .cmd_build_acceleration_structures(self.cmd_buf.handle, &vk_infos, &vk_ranges); } }); @@ -438,9 +432,9 @@ impl AccelerationStructureRef<'_> { }; unsafe { - Device::expect_accel_struct_ext(self.device) + Device::expect_accel_struct_ext(&self.cmd_buf.device) .cmd_build_acceleration_structures_indirect( - self.cmd_buf, + self.cmd_buf.handle, &vk_infos, &tls.range_bases, &tls.range_strides, diff --git a/src/cmd_ref/bind.rs b/src/cmd_ref/bind.rs index e69de29b..1653b595 100644 --- a/src/cmd_ref/bind.rs +++ b/src/cmd_ref/bind.rs @@ -0,0 +1,87 @@ +use { + super::{CommandRef, pipeline::PipelineRef}, + crate::{ + ExecutionPipeline, + driver::{compute::ComputePipeline, graphic::GraphicPipeline, ray_trace::RayTracePipeline}, + }, + std::marker::PhantomData, +}; + +/// A trait for pipelines which may be bound to a `CommandRef`. +/// +/// See [`CommandRef::bind_pipeline`](super::cmd_ref::CommandRef::bind_pipeline) for details. +pub trait BindCommand<'a> { + /// The resource reference type. + type Ref; + + /// Binds the resource to a command. + /// + /// Returns a reference type. + fn bind_cmd(self, _: CommandRef<'a>) -> Self::Ref; +} + +macro_rules! bind_cmd_pipeline { + ($name:ident) => { + paste::paste! { + impl<'a> BindCommand<'a> for &'a [<$name Pipeline>] { + type Ref = PipelineRef<'a, [<$name Pipeline>]>; + + // TODO: Allow binding as explicit secondary command buffers? like with compute/raytrace stuff + fn bind_cmd(self, mut cmd: CommandRef<'a>) -> Self::Ref { + let cmd_ref = cmd.cmd_mut(); + if cmd_ref.execs.last().unwrap().pipeline.is_some() { + // Binding from PipelinePass -> PipelinePass (changing shaders) + cmd_ref.execs.push(Default::default()); + } + + cmd_ref.execs.last_mut().unwrap().pipeline = Some(ExecutionPipeline::$name(self.clone())); + + Self::Ref { + __: PhantomData, + cmd, + } + } + } + + impl<'a> BindCommand<'a> for [<$name Pipeline>] { + type Ref = PipelineRef<'a, [<$name Pipeline>]>; + + fn bind_cmd(self, mut cmd: CommandRef<'a>) -> Self::Ref { + let cmd_ref = cmd.cmd_mut(); + if cmd_ref.execs.last().unwrap().pipeline.is_some() { + // Binding from PipelinePass -> PipelinePass (changing shaders) + cmd_ref.execs.push(Default::default()); + } + + cmd_ref.execs.last_mut().unwrap().pipeline = Some(ExecutionPipeline::$name(self)); + + Self::Ref { + __: PhantomData, + cmd, + } + } + } + + impl ExecutionPipeline { + #[allow(unused)] + pub(crate) fn [](&self) -> bool { + matches!(self, Self::$name(_)) + } + + #[allow(unused)] + pub(crate) fn [](&self) -> &[<$name Pipeline>] { + if let Self::$name(binding) = self { + &binding + } else { + panic!(); + } + } + } + } + }; +} + +// Pipelines you can bind to a pass +bind_cmd_pipeline!(Compute); +bind_cmd_pipeline!(Graphic); +bind_cmd_pipeline!(RayTrace); diff --git a/src/cmd_ref/compute.rs b/src/cmd_ref/compute.rs index ea210f80..371640e0 100644 --- a/src/cmd_ref/compute.rs +++ b/src/cmd_ref/compute.rs @@ -2,7 +2,7 @@ use { super::{Nodes, pipeline::PipelineRef}, crate::{ AnyBufferNode, - driver::{compute::ComputePipeline, device::Device}, + driver::{CommandBuffer, compute::ComputePipeline}, }, ash::vk, log::trace, @@ -32,16 +32,15 @@ use { /// # let shader = Shader::new_compute([0u8; 1].as_slice()); /// # let my_compute_pipeline = ComputePipeline::create(&device, info, shader)?; /// # let mut my_graph = Graph::default(); -/// my_graph.begin_cmd().with_name("my compute pass") +/// my_graph.begin_cmd() /// .bind_pipeline(&my_compute_pipeline) -/// .record_pipeline(move |compute, nodes| { -/// // During this closure we have access to the compute methods! +/// .record_pipeline(move |pipeline, nodes| { +/// // During this closure we have access to the compute dispatch methods! /// }); /// # Ok(()) } /// ``` pub struct ComputePipelineRef<'a> { - pub(super) cmd_buf: vk::CommandBuffer, - pub(super) device: &'a Device, + pub(super) cmd_buf: &'a CommandBuffer, pub(super) nodes: Nodes<'a>, pub(super) pipeline: ComputePipeline, } @@ -87,8 +86,8 @@ impl ComputePipelineRef<'_> { /// # let shader = Shader::new_compute([0u8; 1].as_slice()); /// # let my_compute_pipeline = ComputePipeline::create(&device, info, shader)?; /// # let mut my_graph = Graph::default(); - /// # let my_buf_node = my_graph.bind_node(my_buf); - /// my_graph.begin_cmd().with_name("fill my_buf_node with data") + /// # let my_buf_node = my_graph.bind_resource(my_buf); + /// my_graph.begin_cmd().debug_name("fill my_buf_node with data") /// .bind_pipeline(&my_compute_pipeline) /// .write_descriptor(0, my_buf_node) /// .record_pipeline(move |compute, nodes| { @@ -101,8 +100,12 @@ impl ComputePipelineRef<'_> { #[profiling::function] pub fn dispatch(&self, group_count_x: u32, group_count_y: u32, group_count_z: u32) -> &Self { unsafe { - self.device - .cmd_dispatch(self.cmd_buf, group_count_x, group_count_y, group_count_z); + self.cmd_buf.device.cmd_dispatch( + self.cmd_buf.handle, + group_count_x, + group_count_y, + group_count_z, + ); } self @@ -130,8 +133,8 @@ impl ComputePipelineRef<'_> { group_count_z: u32, ) -> &Self { unsafe { - self.device.cmd_dispatch_base( - self.cmd_buf, + self.cmd_buf.device.cmd_dispatch_base( + self.cmd_buf.handle, base_group_x, base_group_y, base_group_z, @@ -172,7 +175,7 @@ impl ComputePipelineRef<'_> { /// # let shader = Shader::new_compute([0u8; 1].as_slice()); /// # let my_compute_pipeline = ComputePipeline::create(&device, info, shader)?; /// # let mut my_graph = Graph::default(); - /// # let my_buf_node = my_graph.bind_node(my_buf); + /// # let my_buf_node = my_graph.bind_resource(my_buf); /// const CMD_SIZE: usize = size_of::(); /// /// let cmd = vk::DispatchIndirectCommand { @@ -186,9 +189,9 @@ impl ComputePipelineRef<'_> { /// /// let args_buf_flags = vk::BufferUsageFlags::STORAGE_BUFFER; /// let args_buf = Buffer::create_from_slice(&device, args_buf_flags, cmd_data)?; - /// let args_buf_node = my_graph.bind_node(args_buf); + /// let args_buf_node = my_graph.bind_resource(args_buf); /// - /// my_graph.begin_cmd().with_name("fill my_buf_node with data") + /// my_graph.begin_cmd().debug_name("fill my_buf_node with data") /// .bind_pipeline(&my_compute_pipeline) /// .read_node(args_buf_node) /// .write_descriptor(0, my_buf_node) @@ -209,8 +212,8 @@ impl ComputePipelineRef<'_> { let args_buf = args_buf.into(); unsafe { - self.device.cmd_dispatch_indirect( - self.cmd_buf, + self.cmd_buf.device.cmd_dispatch_indirect( + self.cmd_buf.handle, self.nodes[args_buf].handle, args_offset, ); @@ -269,7 +272,7 @@ impl ComputePipelineRef<'_> { /// # let shader = Shader::new_compute([0u8; 1].as_slice()); /// # let my_compute_pipeline = ComputePipeline::create(&device, info, shader)?; /// # let mut my_graph = Graph::default(); - /// my_graph.begin_cmd().with_name("compute the ultimate question") + /// my_graph.begin_cmd().debug_name("compute the ultimate question") /// .bind_pipeline(&my_compute_pipeline) /// .record_pipeline(move |compute, nodes| { /// compute.push_constants(0, &[42]) @@ -295,8 +298,8 @@ impl ComputePipelineRef<'_> { ); unsafe { - self.device.cmd_push_constants( - self.cmd_buf, + self.cmd_buf.device.cmd_push_constants( + self.cmd_buf.handle, self.pipeline.inner.layout, vk::ShaderStageFlags::COMPUTE, push_const.offset, @@ -319,7 +322,7 @@ impl PipelineRef<'_, ComputePipeline> { ) -> Self { let pipeline = self .cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -329,12 +332,11 @@ impl PipelineRef<'_, ComputePipeline> { .unwrap_compute() .clone(); - self.cmd.push_execute(move |device, cmd_buf, nodes| { + self.cmd.push_execute(move |cmd_buf, nodes| { func( ComputePipelineRef { nodes, cmd_buf, - device, pipeline, }, nodes, @@ -344,3 +346,84 @@ impl PipelineRef<'_, ComputePipeline> { self } } + +mod deprecated { + use { + crate::{ + cmd_ref::{Descriptor, PipelineRef, SubresourceRange, View, ViewInfo}, + driver::compute::ComputePipeline, + node::Node, + }, + vk_sync::AccessType, + }; + + impl PipelineRef<'_, ComputePipeline> { + #[deprecated = "use shader_resource_access function with AccessType::ComputeShaderReadOther"] + #[doc(hidden)] + pub fn read_descriptor(self, descriptor: impl Into, node: N) -> Self + where + N: Node + View, + N::Info: Copy, + SubresourceRange: From, + ViewInfo: From, + { + self.shader_resource_access(descriptor, node, AccessType::ComputeShaderReadOther) + } + + #[deprecated = "use shader_subresource_access function with AccessType::ComputeShaderReadOther"] + #[doc(hidden)] + pub fn read_descriptor_as( + self, + descriptor: impl Into, + node: N, + node_view: impl Into, + ) -> Self + where + N: Node + View, + N::Info: Copy, + SubresourceRange: From, + ViewInfo: From, + { + self.shader_subresource_access( + descriptor, + node, + node_view, + AccessType::ComputeShaderReadOther, + ) + } + + #[deprecated = "use shader_resource_access function with AccessType::ComputeShaderWrite"] + #[doc(hidden)] + pub fn write_descriptor(self, descriptor: impl Into, node: N) -> Self + where + N: Node + View, + N::Info: Copy, + SubresourceRange: From, + ViewInfo: From, + { + self.shader_resource_access(descriptor, node, AccessType::ComputeShaderWrite) + } + + #[deprecated = "use shader_subresource_access function with AccessType::ComputeShaderWrite"] + #[doc(hidden)] + pub fn write_descriptor_as( + self, + descriptor: impl Into, + node: N, + node_view: impl Into, + ) -> Self + where + N: Node + View, + N::Info: Copy, + SubresourceRange: From, + ViewInfo: From, + { + self.shader_subresource_access( + descriptor, + node, + node_view, + AccessType::ComputeShaderWrite, + ) + } + } +} diff --git a/src/cmd_ref/graphic.rs b/src/cmd_ref/graphic.rs index fb7ceeb2..b02a2839 100644 --- a/src/cmd_ref/graphic.rs +++ b/src/cmd_ref/graphic.rs @@ -1,13 +1,13 @@ use { - super::{AttachmentIndex, Info, Nodes, PipelineRef, Subresource, SubresourceAccess}, + super::{AttachmentIndex, Nodes, PipelineRef, SubresourceAccess, SubresourceRange}, crate::{ - AnyBufferNode, AnyImageNode, Area, Attachment, ClearColorValue, Node, NodeIndex, - SampleCount, + AnyBufferNode, AnyImageNode, Area, Attachment, ClearColorValue, Node, driver::{ - device::Device, + CommandBuffer, graphic::{DepthStencilMode, GraphicPipeline}, image::{ - ImageViewInfo, image_subresource_range_contains, image_subresource_range_intersects, + ImageInfo, ImageViewInfo, image_subresource_range_contains, + image_subresource_range_intersects, }, render_pass::ResolveMode, }, @@ -47,8 +47,8 @@ use { /// # let my_graphic_pipeline = GraphicPipeline::create(&device, info, [vert, frag])?; /// # let mut my_graph = Graph::default(); /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); -/// # let swapchain_image = my_graph.bind_node(Image::create(&device, info)?); -/// my_graph.begin_cmd().with_name("my draw pass") +/// # let swapchain_image = my_graph.bind_resource(Image::create(&device, info)?); +/// my_graph.begin_cmd().debug_name("my draw pass") /// .bind_pipeline(&my_graphic_pipeline) /// .store_color(0, swapchain_image) /// .record_pipeline(move |graphic, nodes| { @@ -57,8 +57,7 @@ use { /// # Ok(()) } /// ``` pub struct GraphicPipelineRef<'a> { - pub(super) cmd_buf: vk::CommandBuffer, - pub(super) device: &'a Device, + pub(super) cmd_buf: &'a CommandBuffer, pub(super) nodes: Nodes<'a>, pub(super) pipeline: GraphicPipeline, } @@ -92,14 +91,14 @@ impl GraphicPipelineRef<'_> { /// # let my_graphic_pipeline = GraphicPipeline::create(&device, info, [vert, frag])?; /// # let mut my_graph = Graph::default(); /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); - /// # let swapchain_image = my_graph.bind_node(Image::create(&device, info)?); + /// # let swapchain_image = my_graph.bind_resource(Image::create(&device, info)?); /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::INDEX_BUFFER); /// # let my_idx_buf = Buffer::create(&device, buf_info)?; /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::VERTEX_BUFFER); /// # let my_vtx_buf = Buffer::create(&device, buf_info)?; - /// # let my_idx_buf = my_graph.bind_node(my_idx_buf); - /// # let my_vtx_buf = my_graph.bind_node(my_vtx_buf); - /// my_graph.begin_cmd().with_name("my indexed geometry draw pass") + /// # let my_idx_buf = my_graph.bind_resource(my_idx_buf); + /// # let my_vtx_buf = my_graph.bind_resource(my_vtx_buf); + /// my_graph.begin_cmd().debug_name("my indexed geometry draw pass") /// .bind_pipeline(&my_graphic_pipeline) /// .store_color(0, swapchain_image) /// .read_node(my_idx_buf) @@ -121,8 +120,8 @@ impl GraphicPipelineRef<'_> { let buffer = buffer.into(); unsafe { - self.device.cmd_bind_index_buffer( - self.cmd_buf, + self.cmd_buf.device.cmd_bind_index_buffer( + self.cmd_buf.handle, self.nodes[buffer].handle, offset, index_ty, @@ -161,9 +160,9 @@ impl GraphicPipelineRef<'_> { /// # let my_graphic_pipeline = GraphicPipeline::create(&device, info, [vert, frag])?; /// # let mut my_graph = Graph::default(); /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); - /// # let swapchain_image = my_graph.bind_node(Image::create(&device, info)?); - /// # let my_vtx_buf = my_graph.bind_node(my_vtx_buf); - /// my_graph.begin_cmd().with_name("my unindexed geometry draw pass") + /// # let swapchain_image = my_graph.bind_resource(Image::create(&device, info)?); + /// # let my_vtx_buf = my_graph.bind_resource(my_vtx_buf); + /// my_graph.begin_cmd().debug_name("my unindexed geometry draw pass") /// .bind_pipeline(&my_graphic_pipeline) /// .store_color(0, swapchain_image) /// .read_node(my_vtx_buf) @@ -183,8 +182,8 @@ impl GraphicPipelineRef<'_> { let buffer = buffer.into(); unsafe { - self.device.cmd_bind_vertex_buffers( - self.cmd_buf, + self.cmd_buf.device.cmd_bind_vertex_buffers( + self.cmd_buf.handle, binding, slice::from_ref(&self.nodes[buffer].handle), slice::from_ref(&offset), @@ -226,8 +225,8 @@ impl GraphicPipelineRef<'_> { } unsafe { - self.device.cmd_bind_vertex_buffers( - self.cmd_buf, + self.cmd_buf.device.cmd_bind_vertex_buffers( + self.cmd_buf.handle, first_binding, buffers.as_slice(), offsets.as_slice(), @@ -253,8 +252,8 @@ impl GraphicPipelineRef<'_> { first_instance: u32, ) -> &Self { unsafe { - self.device.cmd_draw( - self.cmd_buf, + self.cmd_buf.device.cmd_draw( + self.cmd_buf.handle, vertex_count, instance_count, first_vertex, @@ -281,8 +280,8 @@ impl GraphicPipelineRef<'_> { first_instance: u32, ) -> &Self { unsafe { - self.device.cmd_draw_indexed( - self.cmd_buf, + self.cmd_buf.device.cmd_draw_indexed( + self.cmd_buf.handle, index_count, instance_count, first_index, @@ -331,10 +330,10 @@ impl GraphicPipelineRef<'_> { /// # let my_idx_buf = Buffer::create(&device, buf_info)?; /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::VERTEX_BUFFER); /// # let my_vtx_buf = Buffer::create(&device, buf_info)?; - /// # let my_idx_buf = my_graph.bind_node(my_idx_buf); - /// # let my_vtx_buf = my_graph.bind_node(my_vtx_buf); + /// # let my_idx_buf = my_graph.bind_resource(my_idx_buf); + /// # let my_vtx_buf = my_graph.bind_resource(my_vtx_buf); /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); - /// # let swapchain_image = my_graph.bind_node(Image::create(&device, info)?); + /// # let swapchain_image = my_graph.bind_resource(Image::create(&device, info)?); /// const CMD_SIZE: usize = size_of::(); /// /// let cmd = vk::DrawIndexedIndirectCommand { @@ -350,9 +349,9 @@ impl GraphicPipelineRef<'_> { /// /// let buf_flags = vk::BufferUsageFlags::STORAGE_BUFFER; /// let buf = Buffer::create_from_slice(&device, buf_flags, cmd_data)?; - /// let buf_node = my_graph.bind_node(buf); + /// let buf_node = my_graph.bind_resource(buf); /// - /// my_graph.begin_cmd().with_name("draw a single triangle") + /// my_graph.begin_cmd().debug_name("draw a single triangle") /// .bind_pipeline(&my_graphic_pipeline) /// .store_color(0, swapchain_image) /// .read_node(my_idx_buf) @@ -376,8 +375,8 @@ impl GraphicPipelineRef<'_> { let buffer = buffer.into(); unsafe { - self.device.cmd_draw_indexed_indirect( - self.cmd_buf, + self.cmd_buf.device.cmd_draw_indexed_indirect( + self.cmd_buf.handle, self.nodes[buffer].handle, offset, draw_count, @@ -414,8 +413,8 @@ impl GraphicPipelineRef<'_> { let count_buf = count_buf.into(); unsafe { - self.device.cmd_draw_indexed_indirect_count( - self.cmd_buf, + self.cmd_buf.device.cmd_draw_indexed_indirect_count( + self.cmd_buf.handle, self.nodes[buffer].handle, offset, self.nodes[count_buf].handle, @@ -442,8 +441,8 @@ impl GraphicPipelineRef<'_> { let buffer = buffer.into(); unsafe { - self.device.cmd_draw_indirect( - self.cmd_buf, + self.cmd_buf.device.cmd_draw_indirect( + self.cmd_buf.handle, self.nodes[buffer].handle, offset, draw_count, @@ -471,8 +470,8 @@ impl GraphicPipelineRef<'_> { let count_buf = count_buf.into(); unsafe { - self.device.cmd_draw_indirect_count( - self.cmd_buf, + self.cmd_buf.device.cmd_draw_indirect_count( + self.cmd_buf.handle, self.nodes[buffer].handle, offset, self.nodes[count_buf].handle, @@ -539,8 +538,8 @@ impl GraphicPipelineRef<'_> { /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); /// # let swapchain_image = Image::create(&device, info)?; /// # let mut my_graph = Graph::default(); - /// # let swapchain_image = my_graph.bind_node(swapchain_image); - /// my_graph.begin_cmd().with_name("draw a quad") + /// # let swapchain_image = my_graph.bind_resource(swapchain_image); + /// my_graph.begin_cmd().debug_name("draw a quad") /// .bind_pipeline(&my_graphic_pipeline) /// .store_color(0, swapchain_image) /// .record_pipeline(move |pipeline, nodes| { @@ -567,8 +566,8 @@ impl GraphicPipelineRef<'_> { ); unsafe { - self.device.cmd_push_constants( - self.cmd_buf, + self.cmd_buf.device.cmd_push_constants( + self.cmd_buf.handle, self.pipeline.inner.layout, push_const.stage_flags, start, @@ -585,8 +584,9 @@ impl GraphicPipelineRef<'_> { #[profiling::function] pub fn set_scissor(&self, first_scissor: u32, scissors: &[vk::Rect2D]) -> &Self { unsafe { - self.device - .cmd_set_scissor(self.cmd_buf, first_scissor, scissors); + self.cmd_buf + .device + .cmd_set_scissor(self.cmd_buf.handle, first_scissor, scissors); } self @@ -596,8 +596,9 @@ impl GraphicPipelineRef<'_> { #[profiling::function] pub fn set_viewport(&self, first_viewport: u32, viewports: &[vk::Viewport]) -> &Self { unsafe { - self.device - .cmd_set_viewport(self.cmd_buf, first_viewport, viewports); + self.cmd_buf + .device + .cmd_set_viewport(self.cmd_buf.handle, first_viewport, viewports); } self @@ -613,8 +614,8 @@ impl PipelineRef<'_, GraphicPipeline> { attachment_idx: AttachmentIndex, image: impl Into, ) -> Self { - let image: AnyImageNode = image.into(); - let image_info = image.info(&self.cmd.graph.bindings); + let image = image.into(); + let image_info = self.resource(image).info; let image_view_info: ImageViewInfo = image_info.into(); self.attach_color_as(attachment_idx, image, image_view_info) @@ -631,12 +632,12 @@ impl PipelineRef<'_, GraphicPipeline> { let image = image.into(); let image_view_info = image_view_info.into(); let node_idx = image.index(); - let (_, sample_count) = self.image_info(node_idx); + let ImageInfo { sample_count, .. } = self.resource(image).info; debug_assert!( !self .cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -647,7 +648,7 @@ impl PipelineRef<'_, GraphicPipeline> { debug_assert!( !self .cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -657,7 +658,7 @@ impl PipelineRef<'_, GraphicPipeline> { ); self.cmd - .as_mut() + .cmd_mut() .execs .last_mut() .unwrap() @@ -670,7 +671,7 @@ impl PipelineRef<'_, GraphicPipeline> { debug_assert!( Attachment::are_compatible( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -678,7 +679,7 @@ impl PipelineRef<'_, GraphicPipeline> { .get(&attachment_idx) .map(|(attachment, _)| *attachment), self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -691,7 +692,7 @@ impl PipelineRef<'_, GraphicPipeline> { debug_assert!( Attachment::are_compatible( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -699,7 +700,7 @@ impl PipelineRef<'_, GraphicPipeline> { .get(&attachment_idx) .copied(), self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -713,7 +714,7 @@ impl PipelineRef<'_, GraphicPipeline> { self.cmd.push_node_access( image, AccessType::ColorAttachmentWrite, - Subresource::Image(image_view_info.into()), + SubresourceRange::Image(image_view_info.into()), ); self @@ -722,9 +723,8 @@ impl PipelineRef<'_, GraphicPipeline> { /// Specifies `VK_ATTACHMENT_LOAD_OP_DONT_CARE` for the render pass attachment, and loads an /// image into the framebuffer. pub fn attach_depth_stencil(self, image: impl Into) -> Self { - let image: AnyImageNode = image.into(); - let image_info = image.info(&self.cmd.graph.bindings); - let image_view_info: ImageViewInfo = image_info.into(); + let image = image.into(); + let image_view_info = self.resource(image).info; self.attach_depth_stencil_as(image, image_view_info) } @@ -739,11 +739,11 @@ impl PipelineRef<'_, GraphicPipeline> { let image = image.into(); let image_view_info = image_view_info.into(); let node_idx = image.index(); - let (_, sample_count) = self.image_info(node_idx); + let ImageInfo { sample_count, .. } = self.resource(image).info; debug_assert!( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -753,7 +753,7 @@ impl PipelineRef<'_, GraphicPipeline> { ); debug_assert!( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -763,7 +763,7 @@ impl PipelineRef<'_, GraphicPipeline> { ); self.cmd - .as_mut() + .cmd_mut() .execs .last_mut() .unwrap() @@ -773,14 +773,14 @@ impl PipelineRef<'_, GraphicPipeline> { debug_assert!( Attachment::are_compatible( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() .depth_stencil_resolve .map(|(attachment, ..)| attachment), self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -790,9 +790,9 @@ impl PipelineRef<'_, GraphicPipeline> { ); debug_assert!( Attachment::are_compatible( - self.cmd.as_ref().execs.last().unwrap().depth_stencil_store, + self.cmd.cmd().execs.last().unwrap().depth_stencil_store, self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -816,7 +816,7 @@ impl PipelineRef<'_, GraphicPipeline> { } else { AccessType::StencilAttachmentWriteDepthReadOnly }, - Subresource::Image(image_view_info.into()), + SubresourceRange::Image(image_view_info.into()), ); self @@ -838,8 +838,8 @@ impl PipelineRef<'_, GraphicPipeline> { image: impl Into, color: impl Into, ) -> Self { - let image: AnyImageNode = image.into(); - let image_info = image.info(&self.cmd.graph.bindings); + let image = image.into(); + let image_info = self.resource(image).info; let image_view_info: ImageViewInfo = image_info.into(); self.clear_color_value_as(attachment_idx, image, color, image_view_info) @@ -856,14 +856,14 @@ impl PipelineRef<'_, GraphicPipeline> { let image = image.into(); let image_view_info = image_view_info.into(); let node_idx = image.index(); - let (_, sample_count) = self.image_info(node_idx); + let ImageInfo { sample_count, .. } = self.resource(image).info; let color = color.into(); debug_assert!( !self .cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -874,7 +874,7 @@ impl PipelineRef<'_, GraphicPipeline> { debug_assert!( !self .cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -884,7 +884,7 @@ impl PipelineRef<'_, GraphicPipeline> { ); self.cmd - .as_mut() + .cmd_mut() .execs .last_mut() .unwrap() @@ -900,7 +900,7 @@ impl PipelineRef<'_, GraphicPipeline> { debug_assert!( Attachment::are_compatible( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -908,7 +908,7 @@ impl PipelineRef<'_, GraphicPipeline> { .get(&attachment_idx) .map(|(attachment, _)| *attachment), self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -921,7 +921,7 @@ impl PipelineRef<'_, GraphicPipeline> { debug_assert!( Attachment::are_compatible( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -929,7 +929,7 @@ impl PipelineRef<'_, GraphicPipeline> { .get(&attachment_idx) .copied(), self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -946,7 +946,7 @@ impl PipelineRef<'_, GraphicPipeline> { // Upgrade existing read access to read-write if let Some(accesses) = self .cmd - .as_mut() + .cmd_mut() .execs .last_mut() .unwrap() @@ -982,7 +982,7 @@ impl PipelineRef<'_, GraphicPipeline> { } self.cmd - .push_node_access(image, image_access, Subresource::Image(image_range)); + .push_node_access(image, image_access, SubresourceRange::Image(image_range)); self } @@ -999,8 +999,8 @@ impl PipelineRef<'_, GraphicPipeline> { depth: f32, stencil: u32, ) -> Self { - let image: AnyImageNode = image.into(); - let image_info = image.info(&self.cmd.graph.bindings); + let image = image.into(); + let image_info = self.resource(image).info; let image_view_info: ImageViewInfo = image_info.into(); self.clear_depth_stencil_value_as(image, depth, stencil, image_view_info) @@ -1017,11 +1017,11 @@ impl PipelineRef<'_, GraphicPipeline> { let image = image.into(); let image_view_info = image_view_info.into(); let node_idx = image.index(); - let (_, sample_count) = self.image_info(node_idx); + let ImageInfo { sample_count, .. } = self.resource(image).info; debug_assert!( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1031,7 +1031,7 @@ impl PipelineRef<'_, GraphicPipeline> { ); debug_assert!( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1041,7 +1041,7 @@ impl PipelineRef<'_, GraphicPipeline> { ); self.cmd - .as_mut() + .cmd_mut() .execs .last_mut() .unwrap() @@ -1053,14 +1053,14 @@ impl PipelineRef<'_, GraphicPipeline> { debug_assert!( Attachment::are_compatible( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() .depth_stencil_resolve .map(|(attachment, ..)| attachment), self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1071,9 +1071,9 @@ impl PipelineRef<'_, GraphicPipeline> { ); debug_assert!( Attachment::are_compatible( - self.cmd.as_ref().execs.last().unwrap().depth_stencil_store, + self.cmd.cmd().execs.last().unwrap().depth_stencil_store, self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1107,7 +1107,7 @@ impl PipelineRef<'_, GraphicPipeline> { // Upgrade existing read access to read-write if let Some(accesses) = self .cmd - .as_mut() + .cmd_mut() .execs .last_mut() .unwrap() @@ -1172,20 +1172,11 @@ impl PipelineRef<'_, GraphicPipeline> { } self.cmd - .push_node_access(image, image_access, Subresource::Image(image_range)); + .push_node_access(image, image_access, SubresourceRange::Image(image_range)); self } - fn image_info(&self, node_idx: NodeIndex) -> (vk::Format, SampleCount) { - let image_info = self.cmd.graph.bindings[node_idx] - .as_driver_image() - .unwrap() - .info; - - (image_info.fmt, image_info.sample_count) - } - /// Specifies `VK_ATTACHMENT_LOAD_OP_LOAD` for the render pass attachment, and loads an image /// into the framebuffer. pub fn load_color( @@ -1193,8 +1184,8 @@ impl PipelineRef<'_, GraphicPipeline> { attachment_idx: AttachmentIndex, image: impl Into, ) -> Self { - let image: AnyImageNode = image.into(); - let image_info = self.node_info(image); + let image = image.into(); + let image_info = self.resource(image).info; // Use the plain node information as the whole view of the node let image_view_info = image_info; @@ -1213,12 +1204,12 @@ impl PipelineRef<'_, GraphicPipeline> { let image = image.into(); let image_view_info = image_view_info.into(); let node_idx = image.index(); - let (_, sample_count) = self.image_info(node_idx); + let ImageInfo { sample_count, .. } = self.resource(image).info; debug_assert!( !self .cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1229,7 +1220,7 @@ impl PipelineRef<'_, GraphicPipeline> { debug_assert!( !self .cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1239,7 +1230,7 @@ impl PipelineRef<'_, GraphicPipeline> { ); self.cmd - .as_mut() + .cmd_mut() .execs .last_mut() .unwrap() @@ -1252,7 +1243,7 @@ impl PipelineRef<'_, GraphicPipeline> { debug_assert!( Attachment::are_compatible( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1260,7 +1251,7 @@ impl PipelineRef<'_, GraphicPipeline> { .get(&attachment_idx) .map(|(attachment, _)| *attachment), self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1273,7 +1264,7 @@ impl PipelineRef<'_, GraphicPipeline> { debug_assert!( Attachment::are_compatible( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1281,7 +1272,7 @@ impl PipelineRef<'_, GraphicPipeline> { .get(&attachment_idx) .copied(), self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1298,7 +1289,7 @@ impl PipelineRef<'_, GraphicPipeline> { // Upgrade existing write access to read-write if let Some(accesses) = self .cmd - .as_mut() + .cmd_mut() .execs .last_mut() .unwrap() @@ -1334,7 +1325,7 @@ impl PipelineRef<'_, GraphicPipeline> { } self.cmd - .push_node_access(image, image_access, Subresource::Image(image_range)); + .push_node_access(image, image_access, SubresourceRange::Image(image_range)); self } @@ -1342,9 +1333,8 @@ impl PipelineRef<'_, GraphicPipeline> { /// Specifies `VK_ATTACHMENT_LOAD_OP_LOAD` for the render pass attachment, and loads an image /// into the framebuffer. pub fn load_depth_stencil(self, image: impl Into) -> Self { - let image: AnyImageNode = image.into(); - let image_info = self.node_info(image); - let image_view_info: ImageViewInfo = image_info.into(); + let image = image.into(); + let image_view_info = self.resource(image).info; self.load_depth_stencil_as(image, image_view_info) } @@ -1359,11 +1349,11 @@ impl PipelineRef<'_, GraphicPipeline> { let image = image.into(); let image_view_info = image_view_info.into(); let node_idx = image.index(); - let (_, sample_count) = self.image_info(node_idx); + let ImageInfo { sample_count, .. } = self.resource(image).info; debug_assert!( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1373,7 +1363,7 @@ impl PipelineRef<'_, GraphicPipeline> { ); debug_assert!( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1383,7 +1373,7 @@ impl PipelineRef<'_, GraphicPipeline> { ); self.cmd - .as_mut() + .cmd_mut() .execs .last_mut() .unwrap() @@ -1392,20 +1382,20 @@ impl PipelineRef<'_, GraphicPipeline> { debug_assert!( Attachment::are_compatible( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() .depth_stencil_resolve .map(|(attachment, ..)| attachment), - self.cmd.as_ref().execs.last().unwrap().depth_stencil_load + self.cmd.cmd().execs.last().unwrap().depth_stencil_load ), "depth/stencil attachment load incompatible with existing resolve" ); debug_assert!( Attachment::are_compatible( - self.cmd.as_ref().execs.last().unwrap().depth_stencil_store, - self.cmd.as_ref().execs.last().unwrap().depth_stencil_load + self.cmd.cmd().execs.last().unwrap().depth_stencil_store, + self.cmd.cmd().execs.last().unwrap().depth_stencil_load ), "depth/stencil attachment load incompatible with existing store" ); @@ -1416,7 +1406,7 @@ impl PipelineRef<'_, GraphicPipeline> { // Upgrade existing write access to read-write if let Some(accesses) = self .cmd - .as_mut() + .cmd_mut() .execs .last_mut() .unwrap() @@ -1460,7 +1450,7 @@ impl PipelineRef<'_, GraphicPipeline> { } self.cmd - .push_node_access(image, image_access, Subresource::Image(image_range)); + .push_node_access(image, image_access, SubresourceRange::Image(image_range)); self } @@ -1472,7 +1462,7 @@ impl PipelineRef<'_, GraphicPipeline> { ) -> Self { let pipeline = self .cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1482,12 +1472,11 @@ impl PipelineRef<'_, GraphicPipeline> { .unwrap_graphic() .clone(); - self.cmd.push_execute(move |device, cmd_buf, nodes| { + self.cmd.push_execute(move |cmd_buf, nodes| { func( GraphicPipelineRef { nodes, cmd_buf, - device, pipeline, }, nodes, @@ -1505,11 +1494,8 @@ impl PipelineRef<'_, GraphicPipeline> { dst_attachment_idx: AttachmentIndex, image: impl Into, ) -> Self { - let image: AnyImageNode = image.into(); - let image_info = self.node_info(image); - - // Use the plain node information as the whole view of the node - let image_view_info = image_info; + let image = image.into(); + let image_view_info = self.resource(image).info; self.resolve_color_as( src_attachment_idx, @@ -1531,10 +1517,10 @@ impl PipelineRef<'_, GraphicPipeline> { let image = image.into(); let image_view_info = image_view_info.into(); let node_idx = image.index(); - let (_, sample_count) = self.image_info(node_idx); + let ImageInfo { sample_count, .. } = self.resource(image).info; self.cmd - .as_mut() + .cmd_mut() .execs .last_mut() .unwrap() @@ -1550,7 +1536,7 @@ impl PipelineRef<'_, GraphicPipeline> { debug_assert!( Attachment::are_compatible( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1558,7 +1544,7 @@ impl PipelineRef<'_, GraphicPipeline> { .get(&dst_attachment_idx) .copied(), self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1571,7 +1557,7 @@ impl PipelineRef<'_, GraphicPipeline> { debug_assert!( Attachment::are_compatible( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1579,7 +1565,7 @@ impl PipelineRef<'_, GraphicPipeline> { .get(&dst_attachment_idx) .map(|(attachment, _)| *attachment), self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1592,7 +1578,7 @@ impl PipelineRef<'_, GraphicPipeline> { debug_assert!( Attachment::are_compatible( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1600,7 +1586,7 @@ impl PipelineRef<'_, GraphicPipeline> { .get(&dst_attachment_idx) .copied(), self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1617,7 +1603,7 @@ impl PipelineRef<'_, GraphicPipeline> { // Upgrade existing read access to read-write if let Some(accesses) = self .cmd - .as_mut() + .cmd_mut() .execs .last_mut() .unwrap() @@ -1653,7 +1639,7 @@ impl PipelineRef<'_, GraphicPipeline> { } self.cmd - .push_node_access(image, image_access, Subresource::Image(image_range)); + .push_node_access(image, image_access, SubresourceRange::Image(image_range)); self } @@ -1667,11 +1653,8 @@ impl PipelineRef<'_, GraphicPipeline> { depth_mode: Option, stencil_mode: Option, ) -> Self { - let image: AnyImageNode = image.into(); - let image_info = self.node_info(image); - - // Use the plain node information as the whole view of the node - let image_view_info = image_info; + let image = image.into(); + let image_view_info = self.resource(image).info; self.resolve_depth_stencil_as( dst_attachment_idx, @@ -1695,10 +1678,10 @@ impl PipelineRef<'_, GraphicPipeline> { let image = image.into(); let image_view_info = image_view_info.into(); let node_idx = image.index(); - let (_, sample_count) = self.image_info(node_idx); + let ImageInfo { sample_count, .. } = self.resource(image).info; self.cmd - .as_mut() + .cmd_mut() .execs .last_mut() .unwrap() @@ -1733,7 +1716,7 @@ impl PipelineRef<'_, GraphicPipeline> { // Upgrade existing read access to read-write if let Some(accesses) = self .cmd - .as_mut() + .cmd_mut() .execs .last_mut() .unwrap() @@ -1798,14 +1781,14 @@ impl PipelineRef<'_, GraphicPipeline> { } self.cmd - .push_node_access(image, image_access, Subresource::Image(image_range)); + .push_node_access(image, image_access, SubresourceRange::Image(image_range)); self } /// Sets a particular depth/stencil mode. pub fn set_depth_stencil(mut self, depth_stencil: DepthStencilMode) -> Self { - let pass = self.cmd.as_mut(); + let pass = self.cmd.cmd_mut(); let exec = pass.execs.last_mut().unwrap(); assert!(exec.depth_stencil.is_none()); @@ -1819,7 +1802,7 @@ impl PipelineRef<'_, GraphicPipeline> { /// /// See [`VkRenderPassMultiviewCreateInfo`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkRenderPassMultiviewCreateInfo.html#_description). pub fn set_multiview(mut self, view_mask: u32, correlated_view_mask: u32) -> Self { - let pass = self.cmd.as_mut(); + let pass = self.cmd.cmd_mut(); let exec = pass.execs.last_mut().unwrap(); exec.correlated_view_mask = correlated_view_mask; @@ -1838,7 +1821,7 @@ impl PipelineRef<'_, GraphicPipeline> { /// sets the viewport and scissor to the same values, with a `0..1` depth if not specified by /// `set_depth_stencil`. pub fn set_render_area(mut self, x: i32, y: i32, width: u32, height: u32) -> Self { - self.cmd.as_mut().execs.last_mut().unwrap().render_area = Some(Area { + self.cmd.cmd_mut().execs.last_mut().unwrap().render_area = Some(Area { height, width, x, @@ -1855,11 +1838,8 @@ impl PipelineRef<'_, GraphicPipeline> { attachment_idx: AttachmentIndex, image: impl Into, ) -> Self { - let image: AnyImageNode = image.into(); - let image_info = self.node_info(image); - - // Use the plain node information as the whole view of the node - let image_view_info = image_info; + let image = image.into(); + let image_view_info = self.resource(image).info; self.store_color_as(attachment_idx, image, image_view_info) } @@ -1875,10 +1855,10 @@ impl PipelineRef<'_, GraphicPipeline> { let image = image.into(); let image_view_info = image_view_info.into(); let node_idx = image.index(); - let (_, sample_count) = self.image_info(node_idx); + let ImageInfo { sample_count, .. } = self.resource(image).info; self.cmd - .as_mut() + .cmd_mut() .execs .last_mut() .unwrap() @@ -1891,7 +1871,7 @@ impl PipelineRef<'_, GraphicPipeline> { debug_assert!( Attachment::are_compatible( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1899,7 +1879,7 @@ impl PipelineRef<'_, GraphicPipeline> { .get(&attachment_idx) .copied(), self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1912,7 +1892,7 @@ impl PipelineRef<'_, GraphicPipeline> { debug_assert!( Attachment::are_compatible( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1920,7 +1900,7 @@ impl PipelineRef<'_, GraphicPipeline> { .get(&attachment_idx) .map(|(attachment, _)| *attachment), self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1933,7 +1913,7 @@ impl PipelineRef<'_, GraphicPipeline> { debug_assert!( Attachment::are_compatible( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1941,7 +1921,7 @@ impl PipelineRef<'_, GraphicPipeline> { .get(&attachment_idx) .copied(), self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -1958,7 +1938,7 @@ impl PipelineRef<'_, GraphicPipeline> { // Upgrade existing read access to read-write if let Some(accesses) = self .cmd - .as_mut() + .cmd_mut() .execs .last_mut() .unwrap() @@ -1994,7 +1974,7 @@ impl PipelineRef<'_, GraphicPipeline> { } self.cmd - .push_node_access(image, image_access, Subresource::Image(image_range)); + .push_node_access(image, image_access, SubresourceRange::Image(image_range)); self } @@ -2002,11 +1982,8 @@ impl PipelineRef<'_, GraphicPipeline> { /// Specifies `VK_ATTACHMENT_STORE_OP_STORE` for the render pass attachment, and stores the /// rendered pixels into an image. pub fn store_depth_stencil(self, image: impl Into) -> Self { - let image: AnyImageNode = image.into(); - let image_info = self.node_info(image); - - // Use the plain node information as the whole view of the node - let image_view_info = image_info; + let image = image.into(); + let image_view_info = self.resource(image).info; self.store_depth_stencil_as(image, image_view_info) } @@ -2023,10 +2000,10 @@ impl PipelineRef<'_, GraphicPipeline> { let image = image.into(); let image_view_info = image_view_info.into(); let node_idx = image.index(); - let (_, sample_count) = self.image_info(node_idx); + let ImageInfo { sample_count, .. } = self.resource(image).info; self.cmd - .as_mut() + .cmd_mut() .execs .last_mut() .unwrap() @@ -2035,32 +2012,32 @@ impl PipelineRef<'_, GraphicPipeline> { debug_assert!( Attachment::are_compatible( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() .depth_stencil_attachment, - self.cmd.as_ref().execs.last().unwrap().depth_stencil_store + self.cmd.cmd().execs.last().unwrap().depth_stencil_store ), "depth/stencil attachment store incompatible with existing attachment" ); debug_assert!( Attachment::are_compatible( self.cmd - .as_ref() + .cmd() .execs .last() .unwrap() .depth_stencil_clear .map(|(attachment, _)| attachment), - self.cmd.as_ref().execs.last().unwrap().depth_stencil_store + self.cmd.cmd().execs.last().unwrap().depth_stencil_store ), "depth/stencil attachment store incompatible with existing clear" ); debug_assert!( Attachment::are_compatible( - self.cmd.as_ref().execs.last().unwrap().depth_stencil_load, - self.cmd.as_ref().execs.last().unwrap().depth_stencil_store + self.cmd.cmd().execs.last().unwrap().depth_stencil_load, + self.cmd.cmd().execs.last().unwrap().depth_stencil_store ), "depth/stencil attachment store incompatible with existing load" ); @@ -2089,7 +2066,7 @@ impl PipelineRef<'_, GraphicPipeline> { // Upgrade existing read access to read-write if let Some(accesses) = self .cmd - .as_mut() + .cmd_mut() .execs .last_mut() .unwrap() @@ -2154,8 +2131,88 @@ impl PipelineRef<'_, GraphicPipeline> { } self.cmd - .push_node_access(image, image_access, Subresource::Image(image_range)); + .push_node_access(image, image_access, SubresourceRange::Image(image_range)); self } } + +mod deprecated { + use { + crate::{ + cmd_ref::{Descriptor, PipelineRef, SubresourceRange, View, ViewInfo}, + driver::graphic::GraphicPipeline, + node::Node, + }, + vk_sync::AccessType, + }; + + impl PipelineRef<'_, GraphicPipeline> { + #[deprecated = "use shader_resource_access function with AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer"] + #[doc(hidden)] + pub fn read_descriptor(self, descriptor: impl Into, node: N) -> Self + where + N: Node + View, + N::Info: Copy, + SubresourceRange: From, + ViewInfo: From, + { + self.shader_resource_access( + descriptor, + node, + AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, + ) + } + + #[deprecated = "use shader_subresource_access function with AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer"] + #[doc(hidden)] + pub fn read_descriptor_as( + self, + descriptor: impl Into, + node: N, + node_view: impl Into, + ) -> Self + where + N: Node + View, + N::Info: Copy, + SubresourceRange: From, + ViewInfo: From, + { + self.shader_subresource_access( + descriptor, + node, + node_view, + AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, + ) + } + + #[deprecated = "use shader_resource_access function with AccessType::AnyShaderWrite"] + #[doc(hidden)] + pub fn write_descriptor(self, descriptor: impl Into, node: N) -> Self + where + N: Node + View, + N::Info: Copy, + SubresourceRange: From, + ViewInfo: From, + { + self.shader_resource_access(descriptor, node, AccessType::AnyShaderWrite) + } + + #[deprecated = "use shader_subresource_access function with AccessType::AnyShaderWrite"] + #[doc(hidden)] + pub fn write_descriptor_as( + self, + descriptor: impl Into, + node: N, + node_view: impl Into, + ) -> Self + where + N: Node + View, + N::Info: Copy, + SubresourceRange: From, + ViewInfo: From, + { + self.shader_subresource_access(descriptor, node, node_view, AccessType::AnyShaderWrite) + } + } +} diff --git a/src/cmd_ref/mod.rs b/src/cmd_ref/mod.rs index e87c7174..04c4bd0e 100644 --- a/src/cmd_ref/mod.rs +++ b/src/cmd_ref/mod.rs @@ -1,11 +1,11 @@ //! Strongly-typed rendering commands. mod accel_struct; +mod bind; mod compute; mod graphic; mod pipeline; mod ray_trace; -mod view; pub use self::{ accel_struct::{ @@ -14,27 +14,24 @@ pub use self::{ UpdateAccelerationStructureInfo, }, pipeline::PipelineRef, - view::{View, ViewType}, }; use { + self::bind::BindCommand, super::{ AccelerationStructureLeaseNode, AccelerationStructureNode, AnyAccelerationStructureNode, - AnyBufferNode, AnyImageNode, Bind, Binding, BufferLeaseNode, BufferNode, Command, Edge, - Execution, ExecutionFunction, ExecutionPipeline, Graph, ImageLeaseNode, ImageNode, Info, - Node, SwapchainImageNode, + AnyBufferNode, AnyImageNode, BindGraph, Bound, BufferLeaseNode, BufferNode, Command, + Execution, ExecutionFunction, Graph, ImageLeaseNode, ImageNode, Node, Resource, + SwapchainImageNode, }, crate::driver::{ - accel_struct::AccelerationStructure, + CommandBuffer, + accel_struct::{AccelerationStructure, AccelerationStructureRange}, buffer::{Buffer, BufferSubresourceRange}, - compute::ComputePipeline, - device::Device, - graphic::GraphicPipeline, image::{Image, ImageViewInfo}, - ray_trace::RayTracePipeline, }, ash::vk, - std::{marker::PhantomData, ops::Index}, + std::ops::{Index, Range}, vk_sync::AccessType, }; @@ -50,94 +47,6 @@ pub type BindingOffset = u32; /// Alias for the descriptor set index of a shader descriptor. pub type DescriptorSetIndex = u32; -/// Associated type trait which enables default values for read and write methods. -pub trait Access { - /// The default `AccessType` for read operations, if not specified explicitly. - const DEFAULT_READ: AccessType; - - /// The default `AccessType` for write operations, if not specified explicitly. - const DEFAULT_WRITE: AccessType; -} - -impl Access for ComputePipeline { - const DEFAULT_READ: AccessType = AccessType::ComputeShaderReadOther; - const DEFAULT_WRITE: AccessType = AccessType::ComputeShaderWrite; -} - -impl Access for GraphicPipeline { - const DEFAULT_READ: AccessType = AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer; - const DEFAULT_WRITE: AccessType = AccessType::AnyShaderWrite; -} - -impl Access for RayTracePipeline { - const DEFAULT_READ: AccessType = - AccessType::RayTracingShaderReadSampledImageOrUniformTexelBuffer; - const DEFAULT_WRITE: AccessType = AccessType::AnyShaderWrite; -} - -macro_rules! bind { - ($name:ident) => { - paste::paste! { - impl<'a> Bind, PipelineRef<'a, [<$name Pipeline>]>> for &'a [<$name Pipeline>] { - // TODO: Allow binding as explicit secondary command buffers? like with compute/raytrace stuff - fn bind(self, mut cmd: CommandRef<'a>) -> PipelineRef<'a, [<$name Pipeline>]> { - let cmd_ref = cmd.as_mut(); - if cmd_ref.execs.last().unwrap().pipeline.is_some() { - // Binding from PipelinePass -> PipelinePass (changing shaders) - cmd_ref.execs.push(Default::default()); - } - - cmd_ref.execs.last_mut().unwrap().pipeline = Some(ExecutionPipeline::$name(self.clone())); - - PipelineRef { - __: PhantomData, - cmd, - } - } - } - - impl<'a> Bind, PipelineRef<'a, [<$name Pipeline>]>> for [<$name Pipeline>] { - // TODO: Allow binding as explicit secondary command buffers? like with compute/raytrace stuff - fn bind(self, mut cmd: CommandRef<'a>) -> PipelineRef<'a, [<$name Pipeline>]> { - let cmd_ref = cmd.as_mut(); - if cmd_ref.execs.last().unwrap().pipeline.is_some() { - // Binding from PipelinePass -> PipelinePass (changing shaders) - cmd_ref.execs.push(Default::default()); - } - - cmd_ref.execs.last_mut().unwrap().pipeline = Some(ExecutionPipeline::$name(self)); - - PipelineRef { - __: PhantomData, - cmd, - } - } - } - - impl ExecutionPipeline { - #[allow(unused)] - pub(super) fn [](&self) -> bool { - matches!(self, Self::$name(_)) - } - - #[allow(unused)] - pub(super) fn [](&self) -> &[<$name Pipeline>] { - if let Self::$name(binding) = self { - &binding - } else { - panic!(); - } - } - } - } - }; -} - -// Pipelines you can bind to a pass -bind!(Compute); -bind!(Graphic); -bind!(RayTrace); - /// A general render pass which may contain acceleration structure commands, general commands, or /// have pipeline bound to then record commands specific to those pipeline types. pub struct CommandRef<'a> { @@ -161,109 +70,38 @@ impl<'a> CommandRef<'a> { } } - /// Informs the pass that the next recorded command buffer will read or write the given `node` - /// using `access`. - /// - /// This function must be called for `node` before it is read or written within a `record` - /// function. For general purpose access, see [`PassRef::read_node`] or [`PassRef::write_node`]. - pub fn access_node(mut self, node: impl Node + Info, access: AccessType) -> Self { - self.access_node_mut(node, access); - - self - } - - /// Informs the pass that the next recorded command buffer will read or write the given `node` - /// using `access`. - /// - /// This function must be called for `node` before it is read or written within a `record` - /// function. For general purpose access, see [`PassRef::read_node_mut`] or - /// [`PassRef::write_node_mut`]. - pub fn access_node_mut(&mut self, node: impl Node + Info, access: AccessType) { - self.assert_bound_graph_node(node); - - let idx = node.index(); - let binding = &self.graph.bindings[idx]; - - let node_access_range = if let Some(buf) = binding.as_driver_buffer() { - Subresource::Buffer((0..buf.info.size).into()) - } else if let Some(image) = binding.as_driver_image() { - Subresource::Image(image.info.default_view_info().into()) - } else { - Subresource::AccelerationStructure - }; - - self.push_node_access(node, access, node_access_range); - } - - /// Informs the pass that the next recorded command buffer will read or write the `subresource` - /// of `node` using `access`. - /// - /// This function must be called for `node` before it is read or written within a `record` - /// function. For general purpose access, see [`PassRef::read_node`] or [`PassRef::write_node`]. - pub fn access_node_subrange( - mut self, - node: N, - access: AccessType, - subresource: impl Into, - ) -> Self - where - N: View, - { - self.access_node_subrange_mut(node, access, subresource); - - self - } - - /// Informs the pass that the next recorded command buffer will read or write the `subresource` - /// of `node` using `access`. - /// - /// This function must be called for `node` before it is read or written within a `record` - /// function. For general purpose access, see [`PassRef::read_node`] or [`PassRef::write_node`]. - pub fn access_node_subrange_mut( - &mut self, - node: N, - access: AccessType, - subresource: impl Into, - ) where - N: View, - { - self.push_node_access(node, access, subresource.into().into()); - } - - fn as_mut(&mut self) -> &mut Command { - &mut self.graph.cmds[self.cmd_idx] - } - - fn as_ref(&self) -> &Command { + fn cmd(&self) -> &Command { &self.graph.cmds[self.cmd_idx] } - fn assert_bound_graph_node(&self, node: impl Node) { - let idx = node.index(); - - assert!(idx < self.graph.bindings.len()); + fn cmd_mut(&mut self) -> &mut Command { + &mut self.graph.cmds[self.cmd_idx] } - /// Binds a Vulkan acceleration structure, buffer, or image to the graph associated with this - /// pass. + /// Binds a Vulkan buffer, image, or acceleration structure resource to the graph associated + /// with this command. /// /// Bound nodes may be used in passes for pipeline and shader operations. - pub fn bind_node<'b, B>(&'b mut self, binding: B) -> >::Result + pub fn bind_resource(&mut self, resource: R) -> R::Node where - B: Edge, - B: Bind<&'b mut Graph, >::Result>, + R: BindGraph, { - self.graph.bind_node(binding) + self.graph.bind_resource(resource) } /// Binds a [`ComputePipeline`], [`GraphicPipeline`], or [`RayTracePipeline`] to the current /// pass, allowing for strongly typed access to the related functions. - pub fn bind_pipeline(self, binding: B) -> >::Result + pub fn bind_pipeline

(self, pipeline: P) -> P::Ref where - B: Edge, - B: Bind>::Result>, + P: BindCommand<'a>, { - binding.bind(self) + pipeline.bind_cmd(self) + } + + /// Sets a debugging name, but only in debug builds + pub fn debug_name(mut self, name: impl Into) -> Self { + self.set_debug_name(name); + self } /// Finalize the recording of this command and return to the `Graph` where you may record @@ -277,21 +115,10 @@ impl<'a> CommandRef<'a> { self.graph } - /// Returns Info used to crate a node. - pub fn node_info(&self, node: N) -> ::Info - where - N: Info, - { - node.info(&self.graph.bindings) - } - - fn push_execute( - &mut self, - func: impl FnOnce(&Device, vk::CommandBuffer, Nodes<'_>) + Send + 'static, - ) { - let pass = self.as_mut(); + fn push_execute(&mut self, func: impl FnOnce(&CommandBuffer, Nodes<'_>) + Send + 'static) { + let cmd = self.cmd_mut(); let exec = { - let last_exec = pass.execs.last_mut().unwrap(); + let last_exec = cmd.execs.last_mut().unwrap(); last_exec.func = Some(ExecutionFunction(Box::new(func))); Execution { @@ -300,19 +127,25 @@ impl<'a> CommandRef<'a> { } }; - pass.execs.push(exec); + cmd.execs.push(exec); self.exec_idx += 1; } - fn push_node_access(&mut self, node: impl Node, access: AccessType, subresource: Subresource) { + fn push_node_access( + &mut self, + node: impl Node, + access: AccessType, + subresource: SubresourceRange, + ) { let node_idx = node.index(); - self.assert_bound_graph_node(node); + + assert!(self.graph.resources.get(node_idx).is_some()); let access = SubresourceAccess { access, subresource, }; - self.as_mut() + self.cmd_mut() .execs .last_mut() .unwrap() @@ -322,29 +155,6 @@ impl<'a> CommandRef<'a> { .or_insert(vec![access]); } - /// Informs the pass that the next recorded command buffer will read the given `node` using - /// [`AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer`]. - /// - /// This function must be called for `node` before it is read within a `record` function. For - /// more specific access, see [`PassRef::access_node`]. - pub fn read_node(mut self, node: impl Node + Info) -> Self { - self.read_node_mut(node); - - self - } - - /// Informs the pass that the next recorded command buffer will read the given `node` using - /// [`AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer`]. - /// - /// This function must be called for `node` before it is read within a `record` function. For - /// more specific access, see [`PassRef::access_node`]. - pub fn read_node_mut(&mut self, node: impl Node + Info) { - self.access_node_mut( - node, - AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, - ); - } - /// Begin recording an acceleration structure command buffer. /// /// This is the entry point for building and updating an [`AccelerationStructure`] instance. @@ -352,15 +162,8 @@ impl<'a> CommandRef<'a> { mut self, func: impl FnOnce(AccelerationStructureRef<'_>, Nodes<'_>) + Send + 'static, ) -> Self { - self.push_execute(move |device, cmd_buf, nodes| { - func( - AccelerationStructureRef { - nodes, - cmd_buf, - device, - }, - nodes, - ); + self.push_execute(move |cmd_buf, nodes| { + func(AccelerationStructureRef { nodes, cmd_buf }, nodes); }); self @@ -372,41 +175,91 @@ impl<'a> CommandRef<'a> { /// code and interfaces. pub fn record_cmd_buf( mut self, - func: impl FnOnce(&Device, vk::CommandBuffer, Nodes<'_>) + Send + 'static, + func: impl FnOnce(&CommandBuffer, Nodes<'_>) + Send + 'static, ) -> Self { self.push_execute(func); self } + /// Returns a borrow of the original Vulkan resource (buffer, image or acceleration structure) + /// which the given node represents. + pub fn resource(&self, node: N) -> &::Resource + where + N: Bound, + { + self.graph.resource(node) + } + + /// Informs the command that the next recorded command buffer will read or write `node` using + /// `access`. + /// + /// This function must be called for `node` before it is used within a `record_`-function. + pub fn resource_access(mut self, node: N, access: AccessType) -> Self + where + N: Node + View, + SubresourceRange: From<::Range>, + { + self.set_resource_access(node, access); + self + } + /// Sets a debugging name, but only in debug builds - pub fn with_name(mut self, name: impl Into) -> Self { + pub fn set_debug_name(&mut self, name: impl Into) -> &mut Self { #[cfg(debug_assertions)] { - self.as_mut().name = Some(name.into()); + self.cmd_mut().name = Some(name.into()); } self } - /// Informs the pass that the next recorded command buffer will write the given `node` using - /// [`AccessType::AnyShaderWrite`]. + /// Informs the command that the next recorded command buffer will read or write `node` using + /// `access`. /// - /// This function must be called for `node` before it is written within a `record` function. For - /// more specific access, see [`PassRef::access_node`]. - pub fn write_node(mut self, node: impl Node + Info) -> Self { - self.write_node_mut(node); + /// This function must be called for `node` before it is used within a `record_`-function. + pub fn set_resource_access(&mut self, node: N, access: AccessType) + where + N: Node + View, + SubresourceRange: From<::Range>, + { + let subresource = node.default_range(&self.graph.resources).into(); + self.push_node_access(node, access, subresource); + } - self + /// Informs the command that the next recorded command buffer will read or write the + /// `subresource` of `node` using `access`. + /// + /// This function must be called for `node` before it is used within a `record_`-function. + pub fn set_subresource_access( + &mut self, + node: N, + subresource: impl Into, + access: AccessType, + ) where + N: Node + View, + SubresourceRange: From<::Range>, + { + let subresource = subresource.into().into(); + self.push_node_access(node, access, subresource); } - /// Informs the pass that the next recorded command buffer will write the given `node` using - /// [`AccessType::AnyShaderWrite`]. + /// Informs the command that the next recorded command buffer will read or write the + /// `subresource` of `node` using `access`. /// - /// This function must be called for `node` before it is written within a `record` function. For - /// more specific access, see [`PassRef::access_node`]. - pub fn write_node_mut(&mut self, node: impl Node + Info) { - self.access_node_mut(node, AccessType::AnyShaderWrite); + /// This function must be called for `node` before it is used within a `record_`-function. + pub fn subresource_access( + mut self, + node: N, + subresource: impl Into, + access: AccessType, + ) -> Self + where + N: Node + View, + SubresourceRange: From<::Range>, + { + self.set_subresource_access(node, subresource, access); + self } } @@ -503,8 +356,8 @@ impl From<(DescriptorSetIndex, BindingIndex, [BindingOffset; 1])> for Descriptor /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); /// # let image = Image::create(&device, info)?; /// # let mut my_graph = Graph::default(); -/// # let my_image_node = my_graph.bind_node(image); -/// my_graph.begin_cmd().with_name("custom vulkan commands") +/// # let my_image_node = my_graph.bind_resource(image); +/// my_graph.begin_cmd().debug_name("custom vulkan commands") /// .record_cmd_buf(move |device, cmd_buf, nodes| { /// let my_image: &Image = &nodes[my_image_node]; /// @@ -515,25 +368,26 @@ impl From<(DescriptorSetIndex, BindingIndex, [BindingOffset; 1])> for Descriptor /// ``` #[derive(Clone, Copy, Debug)] pub struct Nodes<'a> { - bindings: &'a [Binding], - #[cfg(debug_assertions)] exec: &'a Execution, + + resources: &'a [Resource], } impl<'a> Nodes<'a> { pub(super) fn new( - bindings: &'a [Binding], + resources: &'a [Resource], #[cfg(debug_assertions)] exec: &'a Execution, ) -> Self { Self { - bindings, + resources, #[cfg(debug_assertions)] exec, } } - fn binding(&self, node_idx: usize) -> &Binding { + // TODO... + fn resource(&self, node_idx: usize) -> &Resource { // You must have called read or write for this node on this execution before indexing // into the bindings data! // @@ -545,7 +399,7 @@ impl<'a> Nodes<'a> { "unexpected node access: call access, read, or write first" ); - &self.bindings[node_idx] + &self.resources[node_idx] } } @@ -557,7 +411,7 @@ macro_rules! index { type Output = $handle; fn index(&self, node: [<$name Node>]) -> &Self::Output { - &*self.binding(node.idx).[]().unwrap() + &*self.resource(node.idx).[]().unwrap() } } } @@ -578,20 +432,10 @@ impl Index for Nodes<'_> { type Output = AccelerationStructure; fn index(&self, node: AnyAccelerationStructureNode) -> &Self::Output { - let node_idx = match node { - AnyAccelerationStructureNode::AccelerationStructure(node) => node.idx, - AnyAccelerationStructureNode::AccelerationStructureLease(node) => node.idx, - }; - let binding = self.binding(node_idx); + let node_idx = node.index(); + let resource = self.resource(node_idx); - match node { - AnyAccelerationStructureNode::AccelerationStructure(_) => { - binding.as_acceleration_structure().unwrap() - } - AnyAccelerationStructureNode::AccelerationStructureLease(_) => { - binding.as_acceleration_structure_lease().unwrap() - } - } + resource.as_driver_accel_struct().unwrap() } } @@ -599,16 +443,10 @@ impl Index for Nodes<'_> { type Output = Buffer; fn index(&self, node: AnyBufferNode) -> &Self::Output { - let node_idx = match node { - AnyBufferNode::Buffer(node) => node.idx, - AnyBufferNode::BufferLease(node) => node.idx, - }; - let binding = self.binding(node_idx); + let node_idx = node.index(); + let resource = self.resource(node_idx); - match node { - AnyBufferNode::Buffer(_) => binding.as_buffer().unwrap(), - AnyBufferNode::BufferLease(_) => binding.as_buffer_lease().unwrap(), - } + resource.as_driver_buffer().unwrap() } } @@ -616,26 +454,18 @@ impl Index for Nodes<'_> { type Output = Image; fn index(&self, node: AnyImageNode) -> &Self::Output { - let node_idx = match node { - AnyImageNode::Image(node) => node.idx, - AnyImageNode::ImageLease(node) => node.idx, - AnyImageNode::SwapchainImage(node) => node.idx, - }; - let binding = self.binding(node_idx); + let node_idx = node.index(); + let resource = self.resource(node_idx); - match node { - AnyImageNode::Image(_) => binding.as_image().unwrap(), - AnyImageNode::ImageLease(_) => binding.as_image_lease().unwrap(), - AnyImageNode::SwapchainImage(_) => binding.as_swapchain_image().unwrap(), - } + resource.as_driver_image().unwrap() } } -/// Describes a portion of a resource which is bound. #[derive(Clone, Copy, Debug)] -pub enum Subresource { +#[doc(hidden)] +pub enum SubresourceRange { /// Acceleration structures are bound whole. - AccelerationStructure, + AccelerationStructure(AccelerationStructureRange), /// Images may be partially bound. Image(vk::ImageSubresourceRange), @@ -644,7 +474,7 @@ pub enum Subresource { Buffer(BufferSubresourceRange), } -impl Subresource { +impl SubresourceRange { pub(super) fn as_image(&self) -> Option<&vk::ImageSubresourceRange> { if let Self::Image(subresource) = self { Some(subresource) @@ -654,26 +484,237 @@ impl Subresource { } } -impl From<()> for Subresource { - fn from(_: ()) -> Self { - Self::AccelerationStructure +impl From for SubresourceRange { + fn from(subresource: AccelerationStructureRange) -> Self { + Self::AccelerationStructure(subresource) } } -impl From for Subresource { - fn from(subresource: vk::ImageSubresourceRange) -> Self { - Self::Image(subresource) +impl From for SubresourceRange { + fn from(subresource: BufferSubresourceRange) -> Self { + Self::Buffer(subresource) } } -impl From for Subresource { - fn from(subresource: BufferSubresourceRange) -> Self { - Self::Buffer(subresource) +impl From for SubresourceRange { + fn from(subresource: ImageViewInfo) -> Self { + Self::Image(subresource.into()) + } +} +impl From for SubresourceRange { + fn from(subresource: vk::ImageSubresourceRange) -> Self { + Self::Image(subresource) } } #[derive(Clone, Copy, Debug)] pub(super) struct SubresourceAccess { pub access: AccessType, - pub subresource: Subresource, + pub subresource: SubresourceRange, +} + +/// Allows for a resource to be reinterpreted as differently formatted data. +#[doc(hidden)] +pub trait View { + /// The information about the resource when bound directly to shader descriptors. + type Info; + + /// The information about the resource when used indirectly by any part of a graph. + type Range; + + fn default_range(&self, _: &[Resource]) -> Self::Range + where + Self: Node; + + fn default_info(&self, _: &[Resource]) -> Self::Info + where + Self: Node; +} + +macro_rules! view_accel_struct { + ($name:ident) => { + impl View for $name { + type Info = Self::Range; + type Range = AccelerationStructureRange; + + fn default_range(&self, _: &[Resource]) -> Self::Range + where + Self: Node, + { + Self::Range::default() + } + + fn default_info(&self, resources: &[Resource]) -> Self::Info + where + Self: Node, + { + self.default_range(resources) + } + } + }; +} + +view_accel_struct!(AnyAccelerationStructureNode); +view_accel_struct!(AccelerationStructureLeaseNode); +view_accel_struct!(AccelerationStructureNode); + +macro_rules! view_buffer { + ($name:ident) => { + impl View for $name { + type Info = Self::Range; + type Range = BufferSubresourceRange; + + fn default_range(&self, resources: &[Resource]) -> Self::Range + where + Self: Node, + { + let idx = self.index(); + + resources[idx].as_buffer().unwrap().info.into() + } + + fn default_info(&self, resources: &[Resource]) -> Self::Info + where + Self: Node, + { + self.default_range(resources) + } + } + }; +} + +view_buffer!(AnyBufferNode); +view_buffer!(BufferLeaseNode); +view_buffer!(BufferNode); + +macro_rules! view_image { + ($name:ident) => { + impl View for $name { + type Info = ImageViewInfo; + type Range = vk::ImageSubresourceRange; + + fn default_range(&self, resources: &[Resource]) -> Self::Range + where + Self: Node, + { + self.default_info(resources).into() + } + + fn default_info(&self, resources: &[Resource]) -> Self::Info + where + Self: Node, + { + let idx = self.index(); + + resources[idx].as_image().unwrap().info.into() + } + } + }; +} + +view_image!(AnyImageNode); +view_image!(ImageLeaseNode); +view_image!(ImageNode); +view_image!(SwapchainImageNode); + +/// Describes the interpretation of a resource. +#[derive(Debug)] +#[doc(hidden)] +pub enum ViewInfo { + /// Acceleration structures are always whole resources. + AccelerationStructure(AccelerationStructureRange), + + /// Images may be interpreted as differently formatted images. + Image(ImageViewInfo), + + /// Buffers may be interpreted as subregions of the same buffer. + Buffer(BufferSubresourceRange), +} + +impl ViewInfo { + pub(crate) fn as_buffer(&self) -> Option<&BufferSubresourceRange> { + match self { + Self::Buffer(info) => Some(info), + _ => None, + } + } + + pub(crate) fn as_image(&self) -> Option<&ImageViewInfo> { + match self { + Self::Image(info) => Some(info), + _ => None, + } + } +} + +impl From for ViewInfo { + fn from(info: AccelerationStructureRange) -> Self { + Self::AccelerationStructure(info) + } +} + +impl From for ViewInfo { + fn from(info: BufferSubresourceRange) -> Self { + Self::Buffer(info) + } +} + +impl From for ViewInfo { + fn from(info: ImageViewInfo) -> Self { + Self::Image(info) + } +} + +impl From> for ViewInfo { + fn from(range: Range) -> Self { + Self::Buffer(BufferSubresourceRange { + start: range.start, + end: range.end, + }) + } +} + +mod deprecated { + use { + crate::{ + cmd_ref::{CommandRef, SubresourceRange, View}, + deprecated::Info, + node::Node, + }, + ash::vk, + vk_sync::AccessType, + }; + + impl<'a> CommandRef<'a> { + #[deprecated = "use set_resource_access function"] + #[doc(hidden)] + pub fn access_node_mut(&mut self, node: N, access: AccessType) -> &mut Self + where + N: Node + View, + SubresourceRange: From<::Range>, + { + self.set_resource_access(node, access); + self + } + + #[deprecated = "use device_address function of resource function result"] + #[doc(hidden)] + pub fn node_device_address(&self, node: impl Node) -> vk::DeviceAddress { + let idx = node.index(); + + self.graph.resources[idx] + .as_driver_buffer() + .unwrap() + .device_address() + } + + #[deprecated = "dereference info field of resource function result"] + #[doc(hidden)] + pub fn node_info(&self, node: N) -> N::Type + where + N: Node + Info, + { + node.info(&self.graph.resources) + } + } } diff --git a/src/cmd_ref/pipeline.rs b/src/cmd_ref/pipeline.rs index 96fc048f..1f4a759a 100644 --- a/src/cmd_ref/pipeline.rs +++ b/src/cmd_ref/pipeline.rs @@ -1,7 +1,7 @@ use { super::{ - Access, AccessType, Bind, CommandRef, Descriptor, Edge, Graph, Info, Node, Subresource, - View, ViewType, + AccessType, BindGraph, Bound, CommandRef, Descriptor, Graph, Node, SubresourceRange, View, + ViewInfo, }, std::marker::PhantomData, }; @@ -13,451 +13,223 @@ pub struct PipelineRef<'a, T> { } // NOTE: There are specific implementations of T in the compute, graphic, and ray trace modules -impl<'a, T> PipelineRef<'a, T> -where - T: Access, -{ - /// Informs the pass that the next recorded command buffer will read or write the given `node` - /// at the specified shader descriptor using `access`. +impl<'a, T> PipelineRef<'a, T> { + /// Binds a Vulkan buffer, image, or acceleration structure resource to the graph associated + /// with this command. /// - /// This function must be called for `node` before it is read or written within a `record` - /// function. For general purpose access, see [`PipelineCommandRef::read_descriptor`] or - /// [`PipelineCommandRef::write_descriptor`]. - pub fn access_descriptor( - self, - descriptor: impl Into, - node: N, - access: AccessType, - ) -> Self + /// Bound nodes may be used in passes for pipeline and shader operations. + pub fn bind_resource(&mut self, resource: R) -> R::Node where - N: Info, - N: View, - ViewType: From<::Info>, - ::Info: From<::Info>, - ::Subresource: From<::Info>, + R: BindGraph, { - let node_info = self.node_info(node); - - // Use the plain node information as the whole view of the node - let view_info = node_info; + self.cmd.bind_resource(resource) + } - self.access_descriptor_as(descriptor, node, access, view_info) + /// Finalizes a command and returns the graph so that additional commands may be added. + pub fn end_cmd(self) -> &'a mut Graph { + self.cmd.end_cmd() } - /// Informs the pass that the next recorded command buffer will read or write the given `node` - /// at the specified shader descriptor using `access`. The node will be interpreted using - /// `view_info`. - /// - /// This function must be called for `node` before it is read or written within a `record` - /// function. For general purpose access, see [`PipelineCommandRef::read_descriptor_as`] or - /// [`PipelineCommandRef::write_descriptor_as`]. - pub fn access_descriptor_as( - self, - descriptor: impl Into, - node: N, - access: AccessType, - view_info: impl Into, - ) -> Self + /// Returns a borrow of the original Vulkan resource (buffer, image or acceleration structure) + /// which the given node represents. + pub fn resource(&self, node: N) -> &::Resource where - N: View, - ::Info: Into, - ::Subresource: From<::Info>, + N: Bound, { - let view_info = view_info.into(); - let subresource = ::Subresource::from(view_info); - - self.access_descriptor_subrange(descriptor, node, access, view_info, subresource) + self.cmd.resource(node) } - /// Informs the pass that the next recorded command buffer will read or write the `subresource` - /// of `node` at the specified shader descriptor using `access`. The node will be interpreted - /// using `view_info`. + /// Informs the command that the next recorded command buffer will read or write `node` using + /// `access`. /// - /// This function must be called for `node` before it is read or written within a `record` - /// function. For general purpose access, see [`PipelineCommandRef::read_descriptor_subrange`] or - /// [`PipelineCommandRef::write_descriptor_subrange`]. - pub fn access_descriptor_subrange( - mut self, - descriptor: impl Into, - node: N, - access: AccessType, - view_info: impl Into, - subresource: impl Into, - ) -> Self + /// This function must be called for `node` before it is used within a `record_`-function. + pub fn resource_access(mut self, node: N, access: AccessType) -> Self where - N: View, - ::Info: Into, + N: Node + View, + SubresourceRange: From<::Range>, { - self.cmd - .push_node_access(node, access, subresource.into().into()); - self.push_node_view_bind(node, view_info.into(), descriptor.into()); - + self.cmd.set_resource_access(node, access); self } - /// Informs the pass that the next recorded command buffer will read or write the given `node` - /// using `access`. + /// Informs the command that the next recorded command buffer will read or write `node` using + /// `access`. /// - /// This function must be called for `node` before it is read or written within a `record` - /// function. For general purpose access, see [`PipelineCommandRef::read_node`] or - /// [`PipelineCommandRef::write_node`]. - pub fn access_node(mut self, node: impl Node + Info, access: AccessType) -> Self { - self.access_node_mut(node, access); - + /// This function must be called for `node` before it is used within a `record_`-function. + pub fn set_resource_access(&mut self, node: N, access: AccessType) -> &mut Self + where + N: Node + View, + SubresourceRange: From<::Range>, + { + self.cmd.set_resource_access(node, access); self } - /// Informs the pass that the next recorded command buffer will read or write the given `node` - /// using `access`. + /// Informs the command that the next recorded command buffer will read or write the `node` at + /// the specified shader `descriptor` using `access`. /// - /// This function must be called for `node` before it is read or written within a `record` - /// function. For general purpose access, see [`PipelineCommandRef::read_node_mut`] or - /// [`PipelineCommandRef::write_node_mut`]. - pub fn access_node_mut(&mut self, node: impl Node + Info, access: AccessType) { - self.cmd.assert_bound_graph_node(node); - - let idx = node.index(); - let binding = &self.cmd.graph.bindings[idx]; - - let node_access_range = if let Some(buf) = binding.as_driver_buffer() { - Subresource::Buffer((0..buf.info.size).into()) - } else if let Some(image) = binding.as_driver_image() { - Subresource::Image(image.info.default_view_info().into()) - } else { - Subresource::AccelerationStructure - }; - - self.cmd.push_node_access(node, access, node_access_range); - } - - /// Informs the pass that the next recorded command buffer will read or write the `subresource` - /// of `node` using `access`. - /// - /// This function must be called for `node` before it is read or written within a `record` - /// function. For general purpose access, see [`PipelineCommandRef::read_node_subrange`] or - /// [`PipelineCommandRef::write_node_subrange`]. - pub fn access_node_subrange( - mut self, + /// This function must be called for `node` before it is used within a `record_`-function. + pub fn set_shader_resource_access( + &mut self, + descriptor: impl Into, node: N, access: AccessType, - subresource: impl Into, - ) -> Self + ) -> &mut Self where - N: View, + N: Node + View, + N::Info: Copy, + SubresourceRange: From, + ViewInfo: From, { - self.access_node_subrange_mut(node, access, subresource); + let node_view = node.default_info(&self.cmd.graph.resources); - self + self.set_shader_subresource_access(descriptor, node, node_view, access) } - /// Informs the pass that the next recorded command buffer will read or write the `subresource` - /// of `node` using `access`. + /// Informs the command that the next recorded command buffer will read or write the `node` at + /// the specified shader `descriptor` using `access`. The node will be interpreted + /// using `view_info`. /// - /// This function must be called for `node` before it is read or written within a `record` - /// function. For general purpose access, see [`PipelineCommandRef::read_node_subrange_mut`] or - /// [`PipelineCommandRef::write_node_subrange_mut`]. - pub fn access_node_subrange_mut( + /// This function must be called for `node` before it is used within a `record_`-function. + pub fn set_shader_subresource_access( &mut self, + descriptor: impl Into, node: N, + node_view: impl Into, access: AccessType, - subresource: impl Into, - ) where - N: View, - { - self.cmd - .push_node_access(node, access, subresource.into().into()); - } - - /// Binds a Vulkan acceleration structure, buffer, or image to the graph associated with this - /// pass. - /// - /// Bound nodes may be used in passes for pipeline and shader operations. - pub fn bind_node<'b, B>(&'b mut self, binding: B) -> >::Result - where - B: Edge, - B: Bind<&'b mut Graph, >::Result>, - { - self.cmd.graph.bind_node(binding) - } - - /// Finalizes a command and returns the render graph so that additional commands may be added. - pub fn end_cmd(self) -> &'a mut Graph { - self.cmd.end_cmd() - } - - /// Returns Info used to crate a node. - pub fn node_info(&self, node: N) -> ::Info + ) -> &mut Self where - N: Info, + N: Node + View, + N::Info: Copy, + SubresourceRange: From, + ViewInfo: From, { - node.info(&self.cmd.graph.bindings) - } - - fn push_node_view_bind( - &mut self, - node: impl Node, - view_info: impl Into, - binding: Descriptor, - ) { + let descriptor = descriptor.into(); + let node_view = node_view.into(); + let subresource = node_view.into(); let node_idx = node.index(); - self.cmd.assert_bound_graph_node(node); + + self.cmd.push_node_access(node, access, subresource); assert!( self.cmd - .as_mut() + .cmd_mut() .execs .last_mut() .unwrap() .bindings - .insert(binding, (node_idx, Some(view_info.into()))) + .insert(descriptor, (node_idx, node_view.into())) .is_none(), - "descriptor {binding:?} has already been bound" + "descriptor {descriptor:?} has already been bound" ); - } - /// Informs the pass that the next recorded command buffer will read the given `node` at the - /// specified shader descriptor. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is read within a `record` function. For - /// more specific access, see [`PipelineCommandRef::access_descriptor`]. - pub fn read_descriptor(self, descriptor: impl Into, node: N) -> Self - where - N: Info, - N: View, - ViewType: From<::Info>, - ::Info: From<::Info>, - ::Subresource: From<::Info>, - { - let node_info = self.node_info(node); - - // Use the plain node information as the whole view of the node - let view_info = node_info; - - self.read_descriptor_as(descriptor, node, view_info) + self } - /// Informs the pass that the next recorded command buffer will read the given `node` at the - /// specified shader descriptor. The node will be interpreted using `view_info`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. + /// Informs the command that the next recorded command buffer will read or write the + /// `subresource` of `node` using `access`. /// - /// This function must be called for `node` before it is read within a `record` function. For - /// more specific access, see [`PipelineCommandRef::access_descriptor_as`]. - pub fn read_descriptor_as( - self, - descriptor: impl Into, + /// This function must be called for `node` before it is used within a `record_`-function. + pub fn set_subresource_access( + &mut self, node: N, - view_info: impl Into, - ) -> Self + subresource: impl Into, + access: AccessType, + ) -> &mut Self where - N: View, - ::Info: Into, - ::Subresource: From<::Info>, + N: Node + View, + SubresourceRange: From<::Range>, { - let view_info = view_info.into(); - let subresource = ::Subresource::from(view_info); - - self.read_descriptor_subrange(descriptor, node, view_info, subresource) + self.cmd.set_subresource_access(node, subresource, access); + self } - /// Informs the pass that the next recorded command buffer will read the `subresource` of `node` - /// at the specified shader descriptor. The node will be interpreted using `view_info`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. + /// Informs the command that the next recorded command buffer will read or write the `node` at + /// the specified shader `descriptor` using `access`. /// - /// This function must be called for `node` before it is read within a `record` function. For - /// more specific access, see [`PipelineCommandRef::access_descriptor_subrange`]. - pub fn read_descriptor_subrange( - self, + /// This function must be called for `node` before it is used within a `record_`-function. + pub fn shader_resource_access( + mut self, descriptor: impl Into, node: N, - view_info: impl Into, - subresource: impl Into, + access: AccessType, ) -> Self where - N: View, - ::Info: Into, - { - let access = ::DEFAULT_READ; - self.access_descriptor_subrange(descriptor, node, access, view_info, subresource) - } - - /// Informs the pass that the next recorded command buffer will read the given `node`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is read within a `record` function. For - /// more specific access, see [`PipelineCommandRef::access_node`]. - pub fn read_node(mut self, node: impl Node + Info) -> Self { - self.read_node_mut(node); - - self - } - - /// Informs the pass that the next recorded command buffer will read the given `node`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is read within a `record` function. For - /// more specific access, see [`PipelineCommandRef::access_node_mut`]. - pub fn read_node_mut(&mut self, node: impl Node + Info) { - let access = ::DEFAULT_READ; - self.access_node_mut(node, access); - } - - /// Informs the pass that the next recorded command buffer will read the `subresource` of - /// `node`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is read within a `record` function. For - /// more specific access, see [`PipelineCommandRef::access_node_subrange`]. - pub fn read_node_subrange(mut self, node: N, subresource: impl Into) -> Self - where - N: View, + N: Node + View, + N::Info: Copy, + SubresourceRange: From, + ViewInfo: From, { - self.read_node_subrange_mut(node, subresource); - + self.set_shader_resource_access(descriptor, node, access); self } - /// Informs the pass that the next recorded command buffer will read the `subresource` of - /// `node`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is read within a `record` function. For - /// more specific access, see [`PipelineCommandRef::access_node_subrange_mut`]. - pub fn read_node_subrange_mut(&mut self, node: N, subresource: impl Into) - where - N: View, - { - let access = ::DEFAULT_READ; - self.access_node_subrange_mut(node, access, subresource); - } - - /// Informs the pass that the next recorded command buffer will write the given `node` at the - /// specified shader descriptor. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is written within a `record` function. For - /// more specific access, see [`PipelineCommandRef::access_descriptor`]. - pub fn write_descriptor(self, descriptor: impl Into, node: N) -> Self - where - N: Info, - N: View, - ::Info: Into, - ::Info: From<::Info>, - ::Subresource: From<::Info>, - { - let node_info = self.node_info(node); - - // Use the plain node information as the whole view of the node - let view_info = node_info; - - self.write_descriptor_as(descriptor, node, view_info) - } - - /// Informs the pass that the next recorded command buffer will write the given `node` at the - /// specified shader descriptor. The node will be interpreted using `view_info`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. + /// Informs the command that the next recorded command buffer will read or write the `node` at + /// the specified shader `descriptor` using `access`. The node will be interpreted + /// using `view_info`. /// - /// This function must be called for `node` before it is written within a `record` function. For - /// more specific access, see [`PipelineCommandRef::access_descriptor_as`]. - pub fn write_descriptor_as( - self, + /// This function must be called for `node` before it is used within a `record_`-function. + pub fn shader_subresource_access( + mut self, descriptor: impl Into, node: N, - view_info: impl Into, + node_view: impl Into, + access: AccessType, ) -> Self where - N: View, - ::Info: Into, - ::Subresource: From<::Info>, + N: Node + View, + N::Info: Copy, + SubresourceRange: From, + ViewInfo: From, { - let view_info = view_info.into(); - let subresource = ::Subresource::from(view_info); - - self.write_descriptor_subrange(descriptor, node, view_info, subresource) + self.set_shader_subresource_access(descriptor, node, node_view, access); + self } - /// Informs the pass that the next recorded command buffer will write the `subresource` of - /// `node` at the specified shader descriptor. The node will be interpreted using `view_info`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. + /// Informs the command that the next recorded command buffer will read or write the + /// `subresource` of `node` using `access`. /// - /// This function must be called for `node` before it is written within a `record` function. For - /// more specific access, see [`PipelineCommandRef::access_descriptor_subrange`]. - pub fn write_descriptor_subrange( - self, - descriptor: impl Into, + /// This function must be called for `node` before it is used within a `record_`-function. + pub fn subresource_access( + mut self, node: N, - view_info: impl Into, - subresource: impl Into, + subresource: impl Into, + access: AccessType, ) -> Self where - N: View, - ::Info: Into, + N: Node + View, + SubresourceRange: From<::Range>, { - let access = ::DEFAULT_WRITE; - self.access_descriptor_subrange(descriptor, node, access, view_info, subresource) - } - - /// Informs the pass that the next recorded command buffer will write the given `node`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is written within a `record` function. For - /// more specific access, see [`PipelineCommandRef::access_node`]. - pub fn write_node(mut self, node: impl Node + Info) -> Self { - self.write_node_mut(node); - + self.cmd.set_subresource_access(node, subresource, access); self } +} - /// Informs the pass that the next recorded command buffer will write the given `node`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is written within a `record` function. For - /// more specific access, see [`PipelineCommandRef::access_node_mut`]. - pub fn write_node_mut(&mut self, node: impl Node + Info) { - let access = ::DEFAULT_WRITE; - self.access_node_mut(node, access); - } - - /// Informs the pass that the next recorded command buffer will write the `subresource` of - /// `node`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is written within a `record` function. For - /// more specific access, see [`PipelineCommandRef::access_node_subrange`]. - pub fn write_node_subrange(mut self, node: N, subresource: impl Into) -> Self - where - N: View, - { - self.write_node_subrange_mut(node, subresource); +mod deprecated { + use { + crate::{cmd_ref::PipelineRef, deprecated::Info, node::Node}, + ash::vk, + }; - self - } + impl<'a, T> PipelineRef<'a, T> { + #[deprecated = "use device_address function of resource function result"] + #[doc(hidden)] + pub fn node_device_address(&self, node: impl Node) -> vk::DeviceAddress { + let idx = node.index(); - /// Informs the pass that the next recorded command buffer will write the `subresource` of - /// `node`. - /// - /// The [`AccessType`] is inferred by the currently bound pipeline. See [`Access`] for details. - /// - /// This function must be called for `node` before it is written within a `record` function. For - /// more specific access, see [`PipelineCommandRef::access_node_subrange_mut`]. - pub fn write_node_subrange_mut(&mut self, node: N, subresource: impl Into) - where - N: View, - { - let access = ::DEFAULT_WRITE; - self.access_node_subrange_mut(node, access, subresource); + self.cmd.graph.resources[idx] + .as_driver_buffer() + .unwrap() + .device_address() + } + + #[deprecated = "dereference info field of resource function result"] + #[doc(hidden)] + pub fn node_info(&self, node: N) -> N::Type + where + N: Node + Info, + { + node.info(&self.cmd.graph.resources) + } } } diff --git a/src/cmd_ref/ray_trace.rs b/src/cmd_ref/ray_trace.rs index bd191bcb..6501f68d 100644 --- a/src/cmd_ref/ray_trace.rs +++ b/src/cmd_ref/ray_trace.rs @@ -1,6 +1,6 @@ use { super::{Nodes, PipelineRef}, - crate::driver::{device::Device, ray_trace::RayTracePipeline}, + crate::driver::{CommandBuffer, device::Device, ray_trace::RayTracePipeline}, ash::vk, log::trace, }; @@ -14,7 +14,7 @@ impl PipelineRef<'_, RayTracePipeline> { ) -> Self { let pipeline = self .cmd - .as_ref() + .cmd() .execs .last() .unwrap() @@ -27,11 +27,10 @@ impl PipelineRef<'_, RayTracePipeline> { #[cfg(debug_assertions)] let dynamic_stack_size = pipeline.inner.info.dynamic_stack_size; - self.cmd.push_execute(move |device, cmd_buf, nodes| { + self.cmd.push_execute(move |cmd_buf, nodes| { func( RayTracePipelineRef { cmd_buf, - device, #[cfg(debug_assertions)] dynamic_stack_size, @@ -73,7 +72,7 @@ impl PipelineRef<'_, RayTracePipeline> { /// [RayTraceShaderGroup::new_general(0)], /// )?; /// # let mut my_graph = Graph::default(); -/// my_graph.begin_cmd().with_name("my ray trace pass") +/// my_graph.begin_cmd().debug_name("my ray trace pass") /// .bind_pipeline(&my_ray_trace_pipeline) /// .record_pipeline(move |pipeline, nodes| { /// // During this closure we have access to the ray trace methods! @@ -81,8 +80,7 @@ impl PipelineRef<'_, RayTracePipeline> { /// # Ok(()) } /// ``` pub struct RayTracePipelineRef<'a> { - cmd_buf: vk::CommandBuffer, - device: &'a Device, + cmd_buf: &'a CommandBuffer, #[cfg(debug_assertions)] dynamic_stack_size: bool, @@ -148,7 +146,7 @@ impl RayTracePipelineRef<'_> { /// # let miss_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; /// # let call_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; /// # let mut my_graph = Graph::default(); - /// my_graph.begin_cmd().with_name("draw a cornell box") + /// my_graph.begin_cmd().debug_name("draw a cornell box") /// .bind_pipeline(&my_ray_trace_pipeline) /// .record_pipeline(move |pipeline, nodes| { /// pipeline.push_constants(0, &[0xcb]) @@ -173,8 +171,8 @@ impl RayTracePipelineRef<'_> { ); unsafe { - self.device.cmd_push_constants( - self.cmd_buf, + self.cmd_buf.device.cmd_push_constants( + self.cmd_buf.handle, self.pipeline.inner.layout, push_const.stage_flags, start, @@ -197,11 +195,11 @@ impl RayTracePipelineRef<'_> { #[cfg(debug_assertions)] assert!(self.dynamic_stack_size); - let ray_trace_ext = Device::expect_ray_trace_ext(self.device); + let ray_trace_ext = Device::expect_ray_trace_ext(&self.cmd_buf.device); unsafe { ray_trace_ext - .cmd_set_ray_tracing_pipeline_stack_size(self.cmd_buf, pipeline_stack_size); + .cmd_set_ray_tracing_pipeline_stack_size(self.cmd_buf.handle, pipeline_stack_size); } self @@ -240,7 +238,7 @@ impl RayTracePipelineRef<'_> { /// # let miss_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; /// # let call_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; /// # let mut my_graph = Graph::default(); - /// my_graph.begin_cmd().with_name("draw a cornell box") + /// my_graph.begin_cmd().debug_name("draw a cornell box") /// .bind_pipeline(&my_ray_trace_pipeline) /// .record_pipeline(move |pipeline, nodes| { /// pipeline.trace_rays(&rgen_sbt, &hit_sbt, &miss_sbt, &call_sbt, 320, 200, 1); @@ -261,11 +259,11 @@ impl RayTracePipelineRef<'_> { height: u32, depth: u32, ) -> &Self { - let ray_trace_ext = Device::expect_ray_trace_ext(self.device); + let ray_trace_ext = Device::expect_ray_trace_ext(&self.cmd_buf.device); unsafe { ray_trace_ext.cmd_trace_rays( - self.cmd_buf, + self.cmd_buf.handle, raygen_shader_binding_table, miss_shader_binding_table, hit_shader_binding_table, @@ -297,11 +295,11 @@ impl RayTracePipelineRef<'_> { callable_shader_binding_table: &vk::StridedDeviceAddressRegionKHR, indirect_device_address: vk::DeviceAddress, ) -> &Self { - let ray_trace_ext = Device::expect_ray_trace_ext(self.device); + let ray_trace_ext = Device::expect_ray_trace_ext(&self.cmd_buf.device); unsafe { ray_trace_ext.cmd_trace_rays_indirect( - self.cmd_buf, + self.cmd_buf.handle, raygen_shader_binding_table, miss_shader_binding_table, hit_shader_binding_table, @@ -313,3 +311,83 @@ impl RayTracePipelineRef<'_> { self } } + +mod deprecated { + use { + crate::{ + cmd_ref::{Descriptor, PipelineRef, SubresourceRange, View, ViewInfo}, + driver::ray_trace::RayTracePipeline, + node::Node, + }, + vk_sync::AccessType, + }; + + impl PipelineRef<'_, RayTracePipeline> { + #[deprecated = "use shader_resource_access function with AccessType::RayTracingShaderReadSampledImageOrUniformTexelBuffer"] + #[doc(hidden)] + pub fn read_descriptor(self, descriptor: impl Into, node: N) -> Self + where + N: Node + View, + N::Info: Copy, + SubresourceRange: From, + ViewInfo: From, + { + self.shader_resource_access( + descriptor, + node, + AccessType::RayTracingShaderReadSampledImageOrUniformTexelBuffer, + ) + } + + #[deprecated = "use shader_subresource_access function with AccessType::RayTracingShaderReadSampledImageOrUniformTexelBuffer"] + #[doc(hidden)] + pub fn read_descriptor_as( + self, + descriptor: impl Into, + node: N, + node_view: impl Into, + ) -> Self + where + N: Node + View, + N::Info: Copy, + SubresourceRange: From, + ViewInfo: From, + { + self.shader_subresource_access( + descriptor, + node, + node_view, + AccessType::RayTracingShaderReadSampledImageOrUniformTexelBuffer, + ) + } + + #[deprecated = "use shader_resource_access function with AccessType::AnyShaderWrite"] + #[doc(hidden)] + pub fn write_descriptor(self, descriptor: impl Into, node: N) -> Self + where + N: Node + View, + N::Info: Copy, + SubresourceRange: From, + ViewInfo: From, + { + self.shader_resource_access(descriptor, node, AccessType::AnyShaderWrite) + } + + #[deprecated = "use shader_subresource_access function with AccessType::AnyShaderWrite"] + #[doc(hidden)] + pub fn write_descriptor_as( + self, + descriptor: impl Into, + node: N, + node_view: impl Into, + ) -> Self + where + N: Node + View, + N::Info: Copy, + SubresourceRange: From, + ViewInfo: From, + { + self.shader_subresource_access(descriptor, node, node_view, AccessType::AnyShaderWrite) + } + } +} diff --git a/src/cmd_ref/view.rs b/src/cmd_ref/view.rs deleted file mode 100644 index a8ac9440..00000000 --- a/src/cmd_ref/view.rs +++ /dev/null @@ -1,124 +0,0 @@ -use { - super::{ - AccelerationStructureLeaseNode, AccelerationStructureNode, AnyAccelerationStructureNode, - AnyBufferNode, AnyImageNode, BufferLeaseNode, BufferNode, BufferSubresourceRange, - ImageLeaseNode, ImageNode, ImageViewInfo, Node, Subresource, SwapchainImageNode, vk, - }, - std::ops::Range, -}; - -/// Allows for a resource to be reinterpreted as differently formatted data. -pub trait View: Node -where - Self::Info: Copy, - Self::Subresource: Into, -{ - /// The Info about the resource interpretation. - type Info; - - /// The portion of the resource which is bound. - type Subresource; -} - -impl View for AccelerationStructureNode { - type Info = (); - type Subresource = (); -} - -impl View for AccelerationStructureLeaseNode { - type Info = (); - type Subresource = (); -} - -impl View for AnyAccelerationStructureNode { - type Info = (); - type Subresource = (); -} - -impl View for AnyBufferNode { - type Info = BufferSubresourceRange; - type Subresource = BufferSubresourceRange; -} - -impl View for AnyImageNode { - type Info = ImageViewInfo; - type Subresource = vk::ImageSubresourceRange; -} - -impl View for BufferLeaseNode { - type Info = BufferSubresourceRange; - type Subresource = BufferSubresourceRange; -} - -impl View for BufferNode { - type Info = BufferSubresourceRange; - type Subresource = BufferSubresourceRange; -} - -impl View for ImageLeaseNode { - type Info = ImageViewInfo; - type Subresource = vk::ImageSubresourceRange; -} - -impl View for ImageNode { - type Info = ImageViewInfo; - type Subresource = vk::ImageSubresourceRange; -} - -impl View for SwapchainImageNode { - type Info = ImageViewInfo; - type Subresource = vk::ImageSubresourceRange; -} - -/// Describes the interpretation of a resource. -#[derive(Debug)] -pub enum ViewType { - /// Acceleration structures are not reinterpreted. - AccelerationStructure, - - /// Images may be interpreted as differently formatted images. - Image(ImageViewInfo), - - /// Buffers may be interpreted as subregions of the same buffer. - Buffer(Range), -} - -impl ViewType { - pub(crate) fn as_buffer(&self) -> Option<&Range> { - match self { - Self::Buffer(view_info) => Some(view_info), - _ => None, - } - } - - pub(crate) fn as_image(&self) -> Option<&ImageViewInfo> { - match self { - Self::Image(view_info) => Some(view_info), - _ => None, - } - } -} - -impl From<()> for ViewType { - fn from(_: ()) -> Self { - Self::AccelerationStructure - } -} - -impl From for ViewType { - fn from(subresource: BufferSubresourceRange) -> Self { - Self::Buffer(subresource.start..subresource.end) - } -} - -impl From for ViewType { - fn from(info: ImageViewInfo) -> Self { - Self::Image(info) - } -} - -impl From> for ViewType { - fn from(range: Range) -> Self { - Self::Buffer(range) - } -} diff --git a/src/driver/accel_struct.rs b/src/driver/accel_struct.rs index 79f4d1b4..1f391920 100644 --- a/src/driver/accel_struct.rs +++ b/src/driver/accel_struct.rs @@ -339,7 +339,7 @@ impl AccelerationStructure { } /// Sets the debugging name assigned to this acceleration structure. - pub fn with_name(mut self, name: impl Into) -> Self { + pub fn debug_name(mut self, name: impl Into) -> Self { self.name = Some(name.into()); self @@ -745,6 +745,10 @@ impl From for AccelerationStructureInfoBuilderError { } } +/// Specifies a range of acceleration structure data. +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct AccelerationStructureRange; + /// Holds the results of the [`AccelerationStructure::size_of`] function. #[derive(Clone, Copy, Debug)] pub struct AccelerationStructureSize { diff --git a/src/driver/buffer.rs b/src/driver/buffer.rs index dbc380c0..d88843cd 100644 --- a/src/driver/buffer.rs +++ b/src/driver/buffer.rs @@ -452,7 +452,7 @@ impl Buffer { } /// Sets the debugging name assigned to this buffer. - pub fn with_name(mut self, name: impl Into) -> Self { + pub fn debug_name(mut self, name: impl Into) -> Self { self.name = Some(name.into()); self @@ -893,12 +893,6 @@ impl From> for BufferSubresourceRange { } } -impl From>> for BufferSubresourceRange { - fn from(range: Option>) -> Self { - range.unwrap_or(0..vk::WHOLE_SIZE).into() - } -} - impl From for Range { fn from(subresource: BufferSubresourceRange) -> Self { subresource.start..subresource.end diff --git a/src/driver/compute.rs b/src/driver/compute.rs index 746ce7bf..c18f8b50 100644 --- a/src/driver/compute.rs +++ b/src/driver/compute.rs @@ -103,11 +103,7 @@ impl ComputePipeline { .module(shader_module) .stage(shader.stage) .name(&entry_name); - let specialization_info = shader.specialization_info.as_ref().map(|info| { - vk::SpecializationInfo::default() - .map_entries(&info.map_entries) - .data(&info.data) - }); + let specialization_info = shader.specialization.as_ref().map(Into::into); if let Some(specialization_info) = &specialization_info { stage_create_info = stage_create_info.specialization_info(specialization_info); @@ -201,7 +197,7 @@ impl ComputePipeline { /// /// _Note:_ The pipeline name may only be assigned once. Subsequent calls will not update the /// previously set name value. - pub fn with_name(mut self, name: impl Into) -> Self { + pub fn debug_name(mut self, name: impl Into) -> Self { self.set_name(name); self diff --git a/src/driver/graphic.rs b/src/driver/graphic.rs index 4984431f..d94c1502 100644 --- a/src/driver/graphic.rs +++ b/src/driver/graphic.rs @@ -6,7 +6,7 @@ use { device::Device, image::SampleCount, merge_push_constant_ranges, - shader::{DescriptorBindingMap, PipelineDescriptorInfo, Shader, SpecializationInfo}, + shader::{DescriptorBindingMap, PipelineDescriptorInfo, Shader, SpecializationMap}, }, ash::vk, derive_builder::{Builder, UninitializedFieldError}, @@ -515,7 +515,7 @@ impl GraphicPipeline { flags: shader.stage, module: shader_module, name: CString::new(shader.entry_name.as_str()).unwrap(), - specialization_info: shader.specialization_info, + specialization: shader.specialization, }; Result::<_, DriverError>::Ok(shader_stage) @@ -600,7 +600,7 @@ impl GraphicPipeline { /// /// _Note:_ The pipeline name may only be assigned once. Subsequent calls will not update the /// previously set name value. - pub fn with_name(mut self, name: impl Into) -> Self { + pub fn debug_name(mut self, name: impl Into) -> Self { self.set_name(name); self @@ -809,7 +809,7 @@ pub(crate) struct ShaderStage { pub flags: vk::ShaderStageFlags, pub module: vk::ShaderModule, pub name: CString, // TODO - pub specialization_info: Option, + pub specialization: Option, } /// Specifies stencil mode during rasterization. diff --git a/src/driver/image.rs b/src/driver/image.rs index 0ee7a378..3b072ec5 100644 --- a/src/driver/image.rs +++ b/src/driver/image.rs @@ -420,7 +420,7 @@ impl Image { } /// Sets the debugging name assigned to this image. - pub fn with_name(mut self, name: impl Into) -> Self { + pub fn debug_name(mut self, name: impl Into) -> Self { self.name = Some(name.into()); self @@ -853,7 +853,7 @@ impl ImageInfo { } /// Provides an `ImageViewInfo` for this format, type, aspect, array elements, and mip levels. - pub fn default_view_info(self) -> ImageViewInfo { + pub fn into_image_view(self) -> ImageViewInfo { self.into() } @@ -922,6 +922,14 @@ impl From for ImageInfo { } } +impl From for vk::ImageSubresourceRange { + fn from(info: ImageInfo) -> Self { + let image_view_info: ImageViewInfo = info.into(); + + image_view_info.into() + } +} + impl ImageInfoBuilder { /// Builds a new `ImageInfo`. /// @@ -941,6 +949,11 @@ impl ImageInfoBuilder { Ok(info) => info, } } + + /// Provides an `ImageViewInfo` for this format, type, aspect, array elements, and mip levels. + pub fn into_image_view(self) -> ImageViewInfoBuilder { + self.build().into_image_view().to_builder() + } } #[derive(Debug)] @@ -952,18 +965,6 @@ impl From for ImageInfoBuilderError { } } -impl From for vk::ImageSubresourceRange { - fn from(info: ImageViewInfo) -> Self { - Self { - aspect_mask: info.aspect_mask, - base_mip_level: info.base_mip_level, - base_array_layer: info.base_array_layer, - layer_count: info.array_layer_count, - level_count: info.mip_level_count, - } - } -} - struct ImageView { device: Device, image_view: vk::ImageView, @@ -1137,6 +1138,18 @@ impl From for ImageViewInfo { } } +impl From for vk::ImageSubresourceRange { + fn from(info: ImageViewInfo) -> Self { + Self { + aspect_mask: info.aspect_mask, + base_mip_level: info.base_mip_level, + base_array_layer: info.base_array_layer, + layer_count: info.array_layer_count, + level_count: info.mip_level_count, + } + } +} + impl ImageViewInfoBuilder { /// Builds a new 'ImageViewInfo'. /// @@ -1222,6 +1235,18 @@ impl From for vk::SampleCountFlags { } } +mod deprecated { + use crate::driver::image::{ImageInfo, ImageViewInfo}; + + impl ImageInfo { + #[deprecated = "use into_image_view function"] + #[doc(hidden)] + pub fn default_view_info(self) -> ImageViewInfo { + self.into_image_view() + } + } +} + #[cfg(test)] mod tests { use {super::*, std::ops::Range}; diff --git a/src/driver/ray_trace.rs b/src/driver/ray_trace.rs index b36e32d6..1098b6af 100644 --- a/src/driver/ray_trace.rs +++ b/src/driver/ray_trace.rs @@ -163,13 +163,7 @@ impl RayTracePipeline { })?; let specialization_infos: Box<[Option]> = shaders .iter() - .map(|shader| { - shader.specialization_info.as_ref().map(|info| { - vk::SpecializationInfo::default() - .data(&info.data) - .map_entries(&info.map_entries) - }) - }) + .map(|shader| shader.specialization.as_ref().map(Into::into)) .collect(); let mut shader_stages: Vec = Vec::with_capacity(shaders.len()); @@ -379,7 +373,7 @@ impl RayTracePipeline { /// /// _Note:_ The pipeline name may only be assigned once. Subsequent calls will not update the /// previously set name value. - pub fn with_name(mut self, name: impl Into) -> Self { + pub fn debug_name(mut self, name: impl Into) -> Self { self.set_name(name); self diff --git a/src/driver/render_pass.rs b/src/driver/render_pass.rs index deff6cae..109a40d5 100644 --- a/src/driver/render_pass.rs +++ b/src/driver/render_pass.rs @@ -360,7 +360,7 @@ impl RenderPass { .inner .shader_stages .iter() - .map(|stage| stage.specialization_info.as_ref().map(Into::into)) + .map(|stage| stage.specialization.as_ref().map(Into::into)) .collect::>(); let stages = pipeline .inner diff --git a/src/driver/shader.rs b/src/driver/shader.rs index 28eaaf94..db6b8164 100644 --- a/src/driver/shader.rs +++ b/src/driver/shader.rs @@ -700,13 +700,13 @@ pub struct Shader { /// # use ash::vk; /// # use vk_graph::driver::DriverError; /// # use vk_graph::driver::device::{Device, DeviceInfo}; - /// # use vk_graph::driver::shader::{Shader, SpecializationInfo}; + /// # use vk_graph::driver::shader::{Shader, SpecializationMap}; /// # fn main() -> Result<(), DriverError> { /// # let device = Device::new(DeviceInfo::default())?; /// # let my_shader_code = [0u8; 1]; /// // We instead specify 42 for MY_COUNT: /// let shader = Shader::new_fragment(my_shader_code.as_slice()) - /// .specialization_info(SpecializationInfo::new( + /// .specialization_info(SpecializationMap::new( /// [vk::SpecializationMapEntry { /// constant_id: 0, /// offset: 0, @@ -717,7 +717,7 @@ pub struct Shader { /// # Ok(()) } /// ``` #[builder(default, setter(strip_option))] - pub specialization_info: Option, + pub specialization: Option, /// Shader code. /// @@ -1146,16 +1146,21 @@ impl Shader { fn reflect_entry_point( entry_name: &str, spirv: impl Into, - specialization_info: Option<&SpecializationInfo>, + specialization: Option<&SpecializationMap>, ) -> Result { let mut config = ReflectConfig::new(); config.ref_all_rscs(true).spv(spirv); - if let Some(spec_info) = specialization_info { - for spec in &spec_info.map_entries { + if let Some(specialization) = specialization { + for &vk::SpecializationMapEntry { + constant_id, + offset, + size, + } in &specialization.entries + { config.specialize( - spec.constant_id, - spec_info.data[spec.offset as usize..spec.offset as usize + spec.size].into(), + constant_id, + specialization.data[offset as usize..offset as usize + size].into(), ); } } @@ -1410,7 +1415,7 @@ impl ShaderBuilder { .as_ref() .map(|spirv| spirv.words()) .expect("spirv code must be set at initialization"), - self.specialization_info + self.specialization .as_ref() .map(|opt| opt.as_ref()) .unwrap_or_default(), @@ -1497,32 +1502,44 @@ impl From for ShaderBuilderError { } /// Describes specialized constant values. -#[derive(Clone, Debug)] -pub struct SpecializationInfo { +#[derive(Clone, Debug, Default)] +pub struct SpecializationMap { /// A buffer of data which holds the constant values. pub data: Vec, /// Mapping of locations within the constant value data which describe each individual constant. - pub map_entries: Vec, + pub entries: Vec, } -impl SpecializationInfo { - /// Constructs a new `SpecializationInfo`. - pub fn new( - map_entries: impl Into>, - data: impl Into>, - ) -> Self { +impl SpecializationMap { + /// Constructs a new `SpecializationMap`. + pub fn new(data: impl Into>) -> Self { Self { data: data.into(), - map_entries: map_entries.into(), + entries: Default::default(), } } + + /// Adds a single constant offset and size to the map and returns the map for further building. + pub fn constant(mut self, constant_id: u32, offset: u32, size: usize) -> Self { + self.set_constant(constant_id, offset, size); + self + } + + /// Adds a single constant offset and size to the map and returns the map for further building. + pub fn set_constant(&mut self, constant_id: u32, offset: u32, size: usize) { + self.entries.push(vk::SpecializationMapEntry { + constant_id, + offset, + size, + }); + } } -impl<'a> From<&'a SpecializationInfo> for vk::SpecializationInfo<'a> { - fn from(value: &'a SpecializationInfo) -> Self { +impl<'a> From<&'a SpecializationMap> for vk::SpecializationInfo<'a> { + fn from(value: &'a SpecializationMap) -> Self { vk::SpecializationInfo::default() - .map_entries(&value.map_entries) + .map_entries(&value.entries) .data(&value.data) } } diff --git a/src/edge.rs b/src/edge.rs index 2598f569..8bec1adb 100644 --- a/src/edge.rs +++ b/src/edge.rs @@ -9,8 +9,7 @@ use { accel_struct::AccelerationStructure, buffer::Buffer, compute::ComputePipeline, graphic::GraphicPipeline, image::Image, ray_trace::RayTracePipeline, swapchain::SwapchainImage, - }, - pool::Lease, + }, pool::Lease }, std::sync::Arc, }; @@ -18,20 +17,20 @@ use { /// A marker trait that says some graph object can transition into a different /// graph object; it is a one-way transition unless the other direction has /// been implemented too. -pub trait Edge { +pub trait Edge { type Result; } macro_rules! node { ($src:ty => $dst:ty) => { - impl Edge for $src { + impl Edge for $src { type Result = $dst; } }; } -// Edges that can be bound as nodes to the render graph: -// Ex: Graph::bind_node(&mut self, binding: X) -> Y +// Edges that can be bound as nodes to the graph: +// Ex: Graph::bind_resource(&mut self, binding: X) -> Y node!(AccelerationStructure => AccelerationStructureNode); node!(Arc => AccelerationStructureNode); node!(Lease => AccelerationStructureLeaseNode); @@ -46,8 +45,8 @@ node!(Lease => ImageLeaseNode); node!(Arc> => ImageLeaseNode); node!(SwapchainImage => SwapchainImageNode); -// Edges that can be unbound from the render graph: -// Ex: Graph::unbind_node(&mut self, node: X) -> Y +// Edges that can be borrowed from the graph: +// Ex: Graph::node(&mut self, node: X) -> Y node!(AccelerationStructureNode => Arc); node!(AccelerationStructureLeaseNode => Arc>); node!(BufferNode => Arc); @@ -58,7 +57,7 @@ node!(SwapchainImageNode => SwapchainImage); macro_rules! node_ref { ($src:ty => $dst:ty) => { - impl<'a> Edge for &'a $src { + impl<'a> Edge for &'a $src { type Result = $dst; } }; diff --git a/src/info.rs b/src/info.rs deleted file mode 100644 index 8e9e6643..00000000 --- a/src/info.rs +++ /dev/null @@ -1,37 +0,0 @@ -use { - super::{ - AccelerationStructureLeaseNode, AccelerationStructureNode, Binding, BufferLeaseNode, - BufferNode, ImageLeaseNode, ImageNode, SwapchainImageNode, - }, - crate::driver::{ - accel_struct::AccelerationStructureInfo, buffer::BufferInfo, image::ImageInfo, - }, -}; - -pub trait Info { - type Info; - - fn info(self, bindings: &[Binding]) -> Self::Info; -} - -macro_rules! info { - ($name:ident: $src:ident -> $dst:ident) => { - paste::paste! { - impl Info for $src { - type Info = $dst; - - fn info(self, bindings: &[Binding]) -> $dst { - bindings[self.idx].[]().unwrap().info - } - } - } - }; -} - -info!(acceleration_structure: AccelerationStructureNode -> AccelerationStructureInfo); -info!(acceleration_structure_lease: AccelerationStructureLeaseNode -> AccelerationStructureInfo); -info!(buffer: BufferNode -> BufferInfo); -info!(buffer_lease: BufferLeaseNode -> BufferInfo); -info!(image: ImageNode -> ImageInfo); -info!(image_lease: ImageLeaseNode -> ImageInfo); -info!(swapchain_image: SwapchainImageNode -> ImageInfo); diff --git a/src/lib.rs b/src/lib.rs index 7a7538ae..3852392d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -188,8 +188,8 @@ println!("{:?}", buffer); // Buffer println!("{:?}", image); // Image // Bind our resources into opaque "usize" nodes -let buffer = graph.bind_node(buffer); -let image = graph.bind_node(image); +let buffer = graph.bind_resource(buffer); +let image = graph.bind_resource(image); // The results have unique types! println!("{:?}", buffer); // BufferNode @@ -232,10 +232,10 @@ Example: # let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::STORAGE); # let image = Image::create(&device, info)?; let mut graph = Graph::default(); -let buffer_node = graph.bind_node(buffer); -let image_node = graph.bind_node(image); +let buffer_node = graph.bind_resource(buffer); +let image_node = graph.bind_resource(image); graph - .begin_cmd().with_name("Do some raw Vulkan or interop with another Vulkan library") + .begin_cmd().debug_name("Do some raw Vulkan or interop with another Vulkan library") .record_cmd_buf(move |device, cmd_buf, nodes| unsafe { // I always run first! }) @@ -272,7 +272,7 @@ Pipeline instances may be bound to a [`PassRef`] in order to execute the associa # let my_compute_pipeline = ComputePipeline::create(&device, info, shader)?; # let mut graph = Graph::default(); graph - .begin_cmd().with_name("My compute pass") + .begin_cmd().debug_name("My compute pass") .bind_pipeline(&my_compute_pipeline) .record_pipeline(|compute, _| { compute.push_constants(0, &42u32.to_ne_bytes()) @@ -352,21 +352,16 @@ pub mod node; pub mod pool; mod bind; -mod edge; -mod info; mod resolver; pub use self::{ - bind::{Bind, Bound}, + bind::{BindGraph, Bound, Resource}, resolver::Resolver, }; use { self::{ - bind::Binding, - cmd_ref::{AttachmentIndex, CommandRef, Descriptor, Nodes, SubresourceAccess, ViewType}, - edge::Edge, - info::Info, + cmd_ref::{AttachmentIndex, CommandRef, Descriptor, Nodes, SubresourceAccess, ViewInfo}, node::Node, node::{ AccelerationStructureLeaseNode, AccelerationStructureNode, @@ -375,9 +370,8 @@ use { }, }, crate::driver::{ - DescriptorBindingMap, + CommandBuffer, DescriptorBindingMap, compute::ComputePipeline, - device::Device, format_aspect_mask, format_texel_block_extent, format_texel_block_size, graphic::{DepthStencilMode, GraphicPipeline}, image::{ImageInfo, ImageViewInfo, SampleCount}, @@ -396,7 +390,7 @@ use { vk_sync::AccessType, }; -type ExecFn = Box) + Send>; +type ExecFn = Box) + Send>; type NodeIndex = usize; #[derive(Clone, Copy, Debug)] @@ -460,9 +454,7 @@ impl Attachment { .array_layer_count(self.array_layer_count) .mip_level_count(self.mip_level_count) .fmt(self.format) - .build() - .default_view_info() - .to_builder() + .into_image_view() .aspect_mask(self.aspect_mask) .base_array_layer(self.base_array_layer) .base_mip_level(self.base_mip_level) @@ -507,7 +499,7 @@ impl From<[u8; 4]> for ClearColorValue { #[derive(Default)] struct Execution { accesses: HashMap>, - bindings: BTreeMap)>, + bindings: BTreeMap, correlated_view_mask: u32, depth_stencil: Option, @@ -647,8 +639,8 @@ impl Command { /// [`graph.cpp`](https://github.com/Themaister/Granite/blob/master/renderer/graph.cpp). #[derive(Debug, Default)] pub struct Graph { - bindings: Vec, cmds: Vec, + resources: Vec, } impl Graph { @@ -663,35 +655,35 @@ impl Graph { CommandRef::new(self) } - /// Binds a Vulkan acceleration structure, buffer, or image to this graph. + /// Binds a Vulkan buffer, image, or acceleration structure resource to this graph. /// - /// Bound nodes may be used in passes for pipeline and shader operations. - pub fn bind_node<'a, B>(&'a mut self, binding: B) -> >::Result + /// Bound resource nodes may be used in commands for shader pipeline operations and other + /// general functions. + pub fn bind_resource(&mut self, resource: R) -> R::Node where - B: Edge, - B: Bind<&'a mut Self, >::Result>, + R: BindGraph, { - binding.bind(self) + resource.bind_graph(self) } /// Copy an image, potentially performing format conversion. pub fn blit_image( &mut self, - src_node: impl Into, - dst_node: impl Into, + src: impl Into, + dst: impl Into, filter: vk::Filter, ) -> &mut Self { - let src_node = src_node.into(); - let dst_node = dst_node.into(); + let src = src.into(); + let src_info = self.resource(src).info; - let src_info = self.node_info(src_node); - let dst_info = self.node_info(dst_node); + let dst = dst.into(); + let dst_info = self.resource(dst).info; self.blit_image_region( - src_node, - dst_node, + src, + dst, filter, - vk::ImageBlit { + [vk::ImageBlit { src_subresource: vk::ImageSubresourceLayers { aspect_mask: format_aspect_mask(src_info.fmt), mip_level: 0, @@ -720,56 +712,39 @@ impl Graph { z: dst_info.depth as _, }, ], - }, + }], ) } - /// Copy a region of an image, potentially performing format conversion. - pub fn blit_image_region( - &mut self, - src_node: impl Into, - dst_node: impl Into, - filter: vk::Filter, - region: vk::ImageBlit, - ) -> &mut Self { - self.blit_image_regions(src_node, dst_node, filter, [region]) - } - /// Copy regions of an image, potentially performing format conversion. #[profiling::function] - pub fn blit_image_regions( + pub fn blit_image_region( &mut self, - src_node: impl Into, - dst_node: impl Into, + src: impl Into, + dst: impl Into, filter: vk::Filter, regions: impl AsRef<[vk::ImageBlit]> + 'static + Send, ) -> &mut Self { - let src_node = src_node.into(); - let dst_node = dst_node.into(); + let src = src.into(); + let dst = dst.into(); - let mut pass = self.begin_cmd().with_name("blit image"); + let mut cmd = self.begin_cmd().debug_name("blit image"); for region in regions.as_ref() { - pass = pass - .access_node_subrange( - src_node, - AccessType::TransferRead, - image_subresource_range_from_layers(region.src_subresource), - ) - .access_node_subrange( - dst_node, - AccessType::TransferWrite, - image_subresource_range_from_layers(region.dst_subresource), - ); + let src_region = image_subresource_range_from_layers(region.src_subresource); + cmd.set_subresource_access(src, src_region, AccessType::TransferRead); + + let dst_region = image_subresource_range_from_layers(region.dst_subresource); + cmd.set_subresource_access(dst, dst_region, AccessType::TransferWrite); } - pass.record_cmd_buf(move |device, cmd_buf, nodes| { - let src_image = nodes[src_node].handle; - let dst_image = nodes[dst_node].handle; + cmd.record_cmd_buf(move |cmd_buf, nodes| { + let src_image = nodes[src].handle; + let dst_image = nodes[dst].handle; unsafe { - device.cmd_blit_image( - cmd_buf, + cmd_buf.device.cmd_blit_image( + cmd_buf.handle, src_image, vk::ImageLayout::TRANSFER_SRC_OPTIMAL, dst_image, @@ -786,26 +761,25 @@ impl Graph { #[profiling::function] pub fn clear_color_image( &mut self, - image_node: impl Into, - color_value: impl Into, + image: impl Into, + color: impl Into, ) -> &mut Self { - let color_value = color_value.into(); - let image_node = image_node.into(); - let image_info = self.node_info(image_node); - let image_view_info = image_info.default_view_info(); + let color = vk::ClearColorValue { + float32: color.into().0, + }; + let image = image.into(); + let image_view = self.resource(image).info.into(); self.begin_cmd() - .with_name("clear color") - .access_node_subrange(image_node, AccessType::TransferWrite, image_view_info) - .record_cmd_buf(move |device, cmd_buf, nodes| unsafe { - device.cmd_clear_color_image( - cmd_buf, - nodes[image_node].handle, + .debug_name("clear color") + .subresource_access(image, image_view, AccessType::TransferWrite) + .record_cmd_buf(move |cmd_buf, nodes| unsafe { + cmd_buf.device.cmd_clear_color_image( + cmd_buf.handle, + nodes[image].handle, vk::ImageLayout::TRANSFER_DST_OPTIMAL, - &vk::ClearColorValue { - float32: color_value.0, - }, - &[image_view_info.into()], + &color, + &[image_view], ); }) .end_cmd() @@ -815,24 +789,23 @@ impl Graph { #[profiling::function] pub fn clear_depth_stencil_image( &mut self, - image_node: impl Into, + image: impl Into, depth: f32, stencil: u32, ) -> &mut Self { - let image_node = image_node.into(); - let image_info = self.node_info(image_node); - let image_view_info = image_info.default_view_info(); + let image = image.into(); + let image_view = self.resource(image).info.into(); self.begin_cmd() - .with_name("clear depth/stencil") - .access_node_subrange(image_node, AccessType::TransferWrite, image_view_info) - .record_cmd_buf(move |device, cmd_buf, nodes| unsafe { - device.cmd_clear_depth_stencil_image( - cmd_buf, - nodes[image_node].handle, + .debug_name("clear depth/stencil") + .subresource_access(image, image_view, AccessType::TransferWrite) + .record_cmd_buf(move |cmd_buf, nodes| unsafe { + cmd_buf.device.cmd_clear_depth_stencil_image( + cmd_buf.handle, + nodes[image].handle, vk::ImageLayout::TRANSFER_DST_OPTIMAL, &vk::ClearDepthStencilValue { depth, stencil }, - &[image_view_info.into()], + &[image_view], ); }) .end_cmd() @@ -841,50 +814,43 @@ impl Graph { /// Copy data between buffers pub fn copy_buffer( &mut self, - src_node: impl Into, - dst_node: impl Into, + src: impl Into, + dst: impl Into, ) -> &mut Self { - let src_node = src_node.into(); - let dst_node = dst_node.into(); - let src_info = self.node_info(src_node); - let dst_info = self.node_info(dst_node); + let src = src.into(); + let dst = dst.into(); + let src_info = self.resource(src).info; + let dst_info = self.resource(dst).info; self.copy_buffer_region( - src_node, - dst_node, - vk::BufferCopy { + src, + dst, + [vk::BufferCopy { src_offset: 0, dst_offset: 0, size: src_info.size.min(dst_info.size), - }, + }], ) } - /// Copy data between buffer regions. - pub fn copy_buffer_region( - &mut self, - src_node: impl Into, - dst_node: impl Into, - region: vk::BufferCopy, - ) -> &mut Self { - self.copy_buffer_regions(src_node, dst_node, [region]) - } - /// Copy data between buffer regions. #[profiling::function] - pub fn copy_buffer_regions( + pub fn copy_buffer_region( &mut self, - src_node: impl Into, - dst_node: impl Into, + src: impl Into, + dst: impl Into, regions: impl AsRef<[vk::BufferCopy]> + 'static + Send, ) -> &mut Self { - let src_node = src_node.into(); - let dst_node = dst_node.into(); + let src = src.into(); + let dst = dst.into(); + + #[cfg(debug_assertions)] + let src_size = self.resource(src).info.size; #[cfg(debug_assertions)] - let (src_size, dst_size) = (self.node_info(src_node).size, self.node_info(dst_node).size); + let dst_size = self.resource(dst).info.size; - let mut pass = self.begin_cmd().with_name("copy buffer"); + let mut cmd = self.begin_cmd().debug_name("copy buffer"); for region in regions.as_ref() { #[cfg(debug_assertions)] @@ -901,25 +867,26 @@ impl Graph { ); }; - pass = pass - .access_node_subrange( - src_node, - AccessType::TransferRead, - region.src_offset..region.src_offset + region.size, - ) - .access_node_subrange( - dst_node, - AccessType::TransferWrite, - region.dst_offset..region.dst_offset + region.size, - ); + cmd.set_subresource_access( + src, + region.src_offset..region.src_offset + region.size, + AccessType::TransferRead, + ); + cmd.set_subresource_access( + dst, + region.dst_offset..region.dst_offset + region.size, + AccessType::TransferWrite, + ); } - pass.record_cmd_buf(move |device, cmd_buf, nodes| { - let src_buf = nodes[src_node].handle; - let dst_buf = nodes[dst_node].handle; + cmd.record_cmd_buf(move |cmd_buf, nodes| { + let src = nodes[src].handle; + let dst = nodes[dst].handle; unsafe { - device.cmd_copy_buffer(cmd_buf, src_buf, dst_buf, regions.as_ref()); + cmd_buf + .device + .cmd_copy_buffer(cmd_buf.handle, src, dst, regions.as_ref()); } }) .end_cmd() @@ -928,16 +895,16 @@ impl Graph { /// Copy data from a buffer into an image. pub fn copy_buffer_to_image( &mut self, - src_node: impl Into, - dst_node: impl Into, + src: impl Into, + dst: impl Into, ) -> &mut Self { - let dst_node = dst_node.into(); - let dst_info = self.node_info(dst_node); + let dst = dst.into(); + let dst_info = self.resource(dst).info; self.copy_buffer_to_image_region( - src_node, - dst_node, - vk::BufferImageCopy { + src, + dst, + [vk::BufferImageCopy { buffer_offset: 0, buffer_row_length: dst_info.width, buffer_image_height: dst_info.height, @@ -953,33 +920,23 @@ impl Graph { height: dst_info.height, width: dst_info.width, }, - }, + }], ) } - /// Copy data from a buffer into an image. - pub fn copy_buffer_to_image_region( - &mut self, - src_node: impl Into, - dst_node: impl Into, - region: vk::BufferImageCopy, - ) -> &mut Self { - self.copy_buffer_to_image_regions(src_node, dst_node, [region]) - } - /// Copy data from a buffer into an image. #[profiling::function] - pub fn copy_buffer_to_image_regions( + pub fn copy_buffer_to_image_region( &mut self, - src_node: impl Into, - dst_node: impl Into, + src: impl Into, + dst: impl Into, regions: impl AsRef<[vk::BufferImageCopy]> + 'static + Send, ) -> &mut Self { - let src_node = src_node.into(); - let dst_node = dst_node.into(); - let dst_info = self.node_info(dst_node); + let src = src.into(); + let dst = dst.into(); + let dst_info = self.resource(dst).info; - let mut pass = self.begin_cmd().with_name("copy buffer to image"); + let mut cmd = self.begin_cmd().debug_name("copy buffer to image"); for region in regions.as_ref() { let block_bytes_size = format_texel_block_size(dst_info.fmt); @@ -988,28 +945,27 @@ impl Graph { * (region.buffer_row_length / block_width) * (region.buffer_image_height / block_height); - pass = pass - .access_node_subrange( - src_node, - AccessType::TransferRead, - region.buffer_offset..region.buffer_offset + data_size as vk::DeviceSize, - ) - .access_node_subrange( - dst_node, - AccessType::TransferWrite, - image_subresource_range_from_layers(region.image_subresource), - ); + cmd.set_subresource_access( + src, + region.buffer_offset..region.buffer_offset + data_size as vk::DeviceSize, + AccessType::TransferRead, + ); + cmd.set_subresource_access( + dst, + image_subresource_range_from_layers(region.image_subresource), + AccessType::TransferWrite, + ); } - pass.record_cmd_buf(move |device, cmd_buf, nodes| { - let src_buf = nodes[src_node].handle; - let dst_image = nodes[dst_node].handle; + cmd.record_cmd_buf(move |cmd_buf, nodes| { + let src = nodes[src].handle; + let dst = nodes[dst].handle; unsafe { - device.cmd_copy_buffer_to_image( - cmd_buf, - src_buf, - dst_image, + cmd_buf.device.cmd_copy_buffer_to_image( + cmd_buf.handle, + src, + dst, vk::ImageLayout::TRANSFER_DST_OPTIMAL, regions.as_ref(), ); @@ -1021,19 +977,19 @@ impl Graph { /// Copy all layers of a source image to a destination image. pub fn copy_image( &mut self, - src_node: impl Into, - dst_node: impl Into, + src: impl Into, + dst: impl Into, ) -> &mut Self { - let src_node = src_node.into(); - let src_info = self.node_info(src_node); + let src = src.into(); + let src_info = self.resource(src).info; - let dst_node = dst_node.into(); - let dst_info = self.node_info(dst_node); + let dst = dst.into(); + let dst_info = self.resource(dst).info; self.copy_image_region( - src_node, - dst_node, - vk::ImageCopy { + src, + dst, + [vk::ImageCopy { src_subresource: vk::ImageSubresourceLayers { aspect_mask: format_aspect_mask(src_info.fmt), mip_level: 0, @@ -1053,57 +1009,46 @@ impl Graph { height: src_info.height.clamp(1, dst_info.height), width: src_info.width.min(dst_info.width), }, - }, + }], ) } - /// Copy data between images. - pub fn copy_image_region( - &mut self, - src_node: impl Into, - dst_node: impl Into, - region: vk::ImageCopy, - ) -> &mut Self { - self.copy_image_regions(src_node, dst_node, [region]) - } - /// Copy data between images. #[profiling::function] - pub fn copy_image_regions( + pub fn copy_image_region( &mut self, - src_node: impl Into, - dst_node: impl Into, + src: impl Into, + dst: impl Into, regions: impl AsRef<[vk::ImageCopy]> + 'static + Send, ) -> &mut Self { - let src_node = src_node.into(); - let dst_node = dst_node.into(); + let src = src.into(); + let dst = dst.into(); - let mut pass = self.begin_cmd().with_name("copy image"); + let mut cmd = self.begin_cmd().debug_name("copy image"); for region in regions.as_ref() { - pass = pass - .access_node_subrange( - src_node, - AccessType::TransferRead, - image_subresource_range_from_layers(region.src_subresource), - ) - .access_node_subrange( - dst_node, - AccessType::TransferWrite, - image_subresource_range_from_layers(region.dst_subresource), - ); + cmd.set_subresource_access( + src, + image_subresource_range_from_layers(region.src_subresource), + AccessType::TransferRead, + ); + cmd.set_subresource_access( + dst, + image_subresource_range_from_layers(region.dst_subresource), + AccessType::TransferWrite, + ); } - pass.record_cmd_buf(move |device, cmd_buf, nodes| { - let src_image = nodes[src_node].handle; - let dst_image = nodes[dst_node].handle; + cmd.record_cmd_buf(move |cmd_buf, nodes| { + let src = nodes[src].handle; + let dst = nodes[dst].handle; unsafe { - device.cmd_copy_image( - cmd_buf, - src_image, + cmd_buf.device.cmd_copy_image( + cmd_buf.handle, + src, vk::ImageLayout::TRANSFER_SRC_OPTIMAL, - dst_image, + dst, vk::ImageLayout::TRANSFER_DST_OPTIMAL, regions.as_ref(), ); @@ -1115,17 +1060,17 @@ impl Graph { /// Copy image data into a buffer. pub fn copy_image_to_buffer( &mut self, - src_node: impl Into, - dst_node: impl Into, + src: impl Into, + dst: impl Into, ) -> &mut Self { - let src_node = src_node.into(); - let dst_node = dst_node.into(); + let src = src.into(); + let dst = dst.into(); - let src_info = self.node_info(src_node); + let src_info = self.resource(src).info; self.copy_image_to_buffer_region( - src_node, - dst_node, + src, + dst, vk::BufferImageCopy { buffer_offset: 0, buffer_row_length: src_info.width, @@ -1149,26 +1094,26 @@ impl Graph { /// Copy image data into a buffer. pub fn copy_image_to_buffer_region( &mut self, - src_node: impl Into, - dst_node: impl Into, + src: impl Into, + dst: impl Into, region: vk::BufferImageCopy, ) -> &mut Self { - self.copy_image_to_buffer_regions(src_node, dst_node, [region]) + self.copy_image_to_buffer_regions(src, dst, [region]) } /// Copy image data into a buffer. #[profiling::function] pub fn copy_image_to_buffer_regions( &mut self, - src_node: impl Into, - dst_node: impl Into, + src: impl Into, + dst: impl Into, regions: impl AsRef<[vk::BufferImageCopy]> + 'static + Send, ) -> &mut Self { - let src_node = src_node.into(); - let src_info = self.node_info(src_node); - let dst_node = dst_node.into(); + let src = src.into(); + let src_info = self.resource(src).info; + let dst = dst.into(); - let mut pass = self.begin_cmd().with_name("copy image to buffer"); + let mut cmd = self.begin_cmd().debug_name("copy image to buffer"); for region in regions.as_ref() { let block_bytes_size = format_texel_block_size(src_info.fmt); @@ -1177,29 +1122,28 @@ impl Graph { * (region.buffer_row_length / block_width) * (region.buffer_image_height / block_height); - pass = pass - .access_node_subrange( - src_node, - AccessType::TransferRead, - image_subresource_range_from_layers(region.image_subresource), - ) - .access_node_subrange( - dst_node, - AccessType::TransferWrite, - region.buffer_offset..region.buffer_offset + data_size as vk::DeviceSize, - ); + cmd.set_subresource_access( + src, + image_subresource_range_from_layers(region.image_subresource), + AccessType::TransferRead, + ); + cmd.set_subresource_access( + dst, + region.buffer_offset..region.buffer_offset + data_size as vk::DeviceSize, + AccessType::TransferWrite, + ); } - pass.record_cmd_buf(move |device, cmd_buf, nodes| { - let src_image = nodes[src_node].handle; - let dst_buf = nodes[dst_node].handle; + cmd.record_cmd_buf(move |cmd_buf, nodes| { + let src = nodes[src].handle; + let dst = nodes[dst].handle; unsafe { - device.cmd_copy_image_to_buffer( - cmd_buf, - src_image, + cmd_buf.device.cmd_copy_image_to_buffer( + cmd_buf.handle, + src, vk::ImageLayout::TRANSFER_SRC_OPTIMAL, - dst_buf, + dst, regions.as_ref(), ); } @@ -1208,33 +1152,23 @@ impl Graph { } /// Fill a region of a buffer with a fixed value. - pub fn fill_buffer(&mut self, buffer_node: impl Into, data: u32) -> &mut Self { - let buffer_node = buffer_node.into(); - - let buffer_info = self.node_info(buffer_node); - - self.fill_buffer_region(buffer_node, data, 0..buffer_info.size) - } - - /// Fill a region of a buffer with a fixed value. - #[profiling::function] - pub fn fill_buffer_region( + pub fn fill_buffer( &mut self, - buffer_node: impl Into, - data: u32, + buffer: impl Into, region: Range, + data: u32, ) -> &mut Self { - let buffer_node = buffer_node.into(); + let buffer = buffer.into(); self.begin_cmd() - .with_name("fill buffer") - .access_node_subrange(buffer_node, AccessType::TransferWrite, region.clone()) - .record_cmd_buf(move |device, cmd_buf, nodes| { - let buffer = nodes[buffer_node].handle; + .debug_name("fill buffer") + .subresource_access(buffer, region.clone(), AccessType::TransferWrite) + .record_cmd_buf(move |cmd_buf, nodes| { + let buffer = nodes[buffer].handle; unsafe { - device.cmd_fill_buffer( - cmd_buf, + cmd_buf.device.cmd_fill_buffer( + cmd_buf.handle, buffer, region.start, region.end - region.start, @@ -1263,37 +1197,13 @@ impl Graph { /// Returns a borrow of the original Vulkan resource (buffer, image or acceleration structure) /// which the given node represents. - pub fn node(&mut self, node: N) -> &>::Result + pub fn resource(&self, node: N) -> &::Resource where - N: Edge, - N: Bound>::Result>, + N: Bound, { node.borrow(self) } - /// Returns the device address of a buffer node. - /// - /// # Panics - /// - /// Panics if the buffer is not currently bound or was not created with the - /// `SHADER_DEVICE_ADDRESS` usage flag. - pub fn node_device_address(&self, node: impl Into) -> vk::DeviceAddress { - let node: AnyBufferNode = node.into(); - - self.bindings[node.index()] - .as_driver_buffer() - .unwrap() - .device_address() - } - - /// Returns information used to crate a node. - pub fn node_info(&self, node: N) -> ::Info - where - N: Info, - { - node.info(&self.bindings) - } - /// Finalizes the graph and provides an object with functions for submitting the resulting /// commands. #[profiling::function] @@ -1310,16 +1220,16 @@ impl Graph { #[profiling::function] pub fn update_buffer( &mut self, - buffer_node: impl Into, + buffer: impl Into, offset: vk::DeviceSize, data: impl AsRef<[u8]> + 'static + Send, ) -> &mut Self { - let buffer_node = buffer_node.into(); + let buffer = buffer.into(); let data_end = offset + data.as_ref().len() as vk::DeviceSize; #[cfg(debug_assertions)] { - let buffer_info = self.node_info(buffer_node); + let buffer_info = self.resource(buffer).info; assert!( data_end <= buffer_info.size, @@ -1329,15 +1239,107 @@ impl Graph { } self.begin_cmd() - .with_name("update buffer") - .access_node_subrange(buffer_node, AccessType::TransferWrite, offset..data_end) - .record_cmd_buf(move |device, cmd_buf, nodes| { - let buffer = nodes[buffer_node].handle; + .debug_name("update buffer") + .subresource_access(buffer, offset..data_end, AccessType::TransferWrite) + .record_cmd_buf(move |cmd_buf, nodes| { + let buffer = nodes[buffer].handle; unsafe { - device.cmd_update_buffer(cmd_buf, buffer, offset, data.as_ref()); + cmd_buf + .device + .cmd_update_buffer(cmd_buf.handle, buffer, offset, data.as_ref()); } }) .end_cmd() } } + +pub(crate) mod deprecated { + use { + crate::{ + Graph, + bind::Resource, + driver::{ + accel_struct::AccelerationStructureInfo, buffer::BufferInfo, image::ImageInfo, + }, + node::{ + AccelerationStructureLeaseNode, AccelerationStructureNode, BufferLeaseNode, + BufferNode, ImageLeaseNode, ImageNode, Node, SwapchainImageNode, + }, + }, + ash::vk, + }; + + impl Graph { + #[deprecated = "use device_address function of resource function result"] + #[doc(hidden)] + pub fn node_device_address(&self, node: impl Node) -> vk::DeviceAddress { + let idx = node.index(); + + self.resources[idx] + .as_driver_buffer() + .unwrap() + .device_address() + } + + #[deprecated = "dereference info field of resource function result"] + #[doc(hidden)] + pub fn node_info(&self, node: N) -> N::Type + where + N: Node + Info, + { + node.info(&self.resources) + } + } + + pub trait Info { + type Type; + + fn info(&self, _: &[Resource]) -> Self::Type + where + Self: Node; + } + + impl Info for SwapchainImageNode { + type Type = ImageInfo; + + fn info(&self, bindings: &[Resource]) -> Self::Type + where + Self: Node, + { + bindings[self.idx].as_swapchain_image().unwrap().info + } + } + + macro_rules! info { + ($name:ident) => { + paste::paste! { + impl Info for [<$name Node>] { + type Type = [<$name Info>]; + + fn info(&self, bindings: &[Resource]) -> Self::Type + where + Self: Node, + { + bindings[self.idx].[]().unwrap().info + } + } + + impl Info for [<$name LeaseNode>] { + type Type = [<$name Info>]; + + fn info(&self, bindings: &[Resource]) -> Self::Type + where + Self: Node, + { + bindings[self.idx].[]().unwrap().info + } + } + } + }; + } + + info!(AccelerationStructure); + info!(Buffer); + info!(Image); +} diff --git a/src/node.rs b/src/node.rs index a1d0bd3c..e9710089 100644 --- a/src/node.rs +++ b/src/node.rs @@ -1,21 +1,10 @@ //! Bindings for Vulkan smart-pointer resources. -use { - super::{Binding, Bound, Graph, Info, NodeIndex}, - crate::{ - driver::{ - accel_struct::{AccelerationStructure, AccelerationStructureInfo}, - buffer::{Buffer, BufferInfo}, - image::{Image, ImageInfo}, - }, - pool::Lease, - }, - std::sync::Arc, -}; +use crate::NodeIndex; /// Specifies either an owned acceleration structure or an acceleration structure leased from a /// pool. -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] pub enum AnyAccelerationStructureNode { /// An owned acceleration structure. AccelerationStructure(AccelerationStructureNode), @@ -24,25 +13,6 @@ pub enum AnyAccelerationStructureNode { AccelerationStructureLease(AccelerationStructureLeaseNode), } -impl Clone for AnyAccelerationStructureNode { - fn clone(&self) -> Self { - *self - } -} - -impl Copy for AnyAccelerationStructureNode {} - -impl Info for AnyAccelerationStructureNode { - type Info = AccelerationStructureInfo; - - fn info(self, bindings: &[Binding]) -> Self::Info { - match self { - Self::AccelerationStructure(node) => node.info(bindings), - Self::AccelerationStructureLease(node) => node.info(bindings), - } - } -} - impl From for AnyAccelerationStructureNode { fn from(node: AccelerationStructureNode) -> Self { Self::AccelerationStructure(node) @@ -56,7 +26,7 @@ impl From for AnyAccelerationStructureNode { } impl Node for AnyAccelerationStructureNode { - fn index(self) -> NodeIndex { + fn index(&self) -> NodeIndex { match self { Self::AccelerationStructure(node) => node.index(), Self::AccelerationStructureLease(node) => node.index(), @@ -65,7 +35,7 @@ impl Node for AnyAccelerationStructureNode { } /// Specifies either an owned buffer or a buffer leased from a pool. -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] pub enum AnyBufferNode { /// An owned buffer. Buffer(BufferNode), @@ -74,25 +44,6 @@ pub enum AnyBufferNode { BufferLease(BufferLeaseNode), } -impl Clone for AnyBufferNode { - fn clone(&self) -> Self { - *self - } -} - -impl Copy for AnyBufferNode {} - -impl Info for AnyBufferNode { - type Info = BufferInfo; - - fn info(self, bindings: &[Binding]) -> Self::Info { - match self { - Self::Buffer(node) => node.info(bindings), - Self::BufferLease(node) => node.info(bindings), - } - } -} - impl From for AnyBufferNode { fn from(node: BufferNode) -> Self { Self::Buffer(node) @@ -106,7 +57,7 @@ impl From for AnyBufferNode { } impl Node for AnyBufferNode { - fn index(self) -> NodeIndex { + fn index(&self) -> NodeIndex { match self { Self::Buffer(node) => node.index(), Self::BufferLease(node) => node.index(), @@ -117,7 +68,7 @@ impl Node for AnyBufferNode { /// Specifies either an owned image or an image leased from a pool. /// /// The image may also be a special swapchain type of image. -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] pub enum AnyImageNode { /// An owned image. Image(ImageNode), @@ -129,26 +80,6 @@ pub enum AnyImageNode { SwapchainImage(SwapchainImageNode), } -impl Clone for AnyImageNode { - fn clone(&self) -> Self { - *self - } -} - -impl Copy for AnyImageNode {} - -impl Info for AnyImageNode { - type Info = ImageInfo; - - fn info(self, bindings: &[Binding]) -> Self::Info { - match self { - Self::Image(node) => node.info(bindings), - Self::ImageLease(node) => node.info(bindings), - Self::SwapchainImage(node) => node.info(bindings), - } - } -} - impl From for AnyImageNode { fn from(node: ImageNode) -> Self { Self::Image(node) @@ -168,7 +99,7 @@ impl From for AnyImageNode { } impl Node for AnyImageNode { - fn index(self) -> NodeIndex { + fn index(&self) -> NodeIndex { match self { Self::Image(node) => node.index(), Self::ImageLease(node) => node.index(), @@ -178,38 +109,30 @@ impl Node for AnyImageNode { } /// A Vulkan resource which has been bound to a [`Graph`] using [`Graph::bind_node`]. -pub trait Node: Copy { +pub trait Node { /// The internal node index of this bound resource. - fn index(self) -> NodeIndex; + fn index(&self) -> NodeIndex; } macro_rules! node { ($name:ident) => { paste::paste! { /// Resource node. - #[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct [<$name Node>] { pub(super) idx: NodeIndex, } impl [<$name Node>] { - pub(super) fn new(idx: NodeIndex) -> Self { + pub(super) fn new(idx: usize) -> Self { Self { idx, } } } - impl Clone for [<$name Node>] { - fn clone(&self) -> Self { - *self - } - } - - impl Copy for [<$name Node>] {} - impl Node for [<$name Node>] { - fn index(self) -> NodeIndex { + fn index(&self) -> NodeIndex { self.idx } } @@ -224,41 +147,3 @@ node!(BufferLease); node!(Image); node!(ImageLease); node!(SwapchainImage); - -macro_rules! node_bound { - ($name:ident) => { - paste::paste! { - impl Bound> for [<$name Node>] { - fn borrow(self, graph: &Graph) -> &Arc<$name> { - graph - .bindings[self.idx] - .[]() - .unwrap() - } - } - } - }; -} - -node_bound!(AccelerationStructure); -node_bound!(Buffer); -node_bound!(Image); - -macro_rules! node_lease_bound { - ($name:ident) => { - paste::paste! { - impl Bound>> for [<$name LeaseNode>] { - fn borrow(self, graph: &Graph) -> &Arc> { - graph - .bindings[self.idx] - .[]() - .unwrap() - } - } - } - }; -} - -node_lease_bound!(AccelerationStructure); -node_lease_bound!(Buffer); -node_lease_bound!(Image); diff --git a/src/pool/mod.rs b/src/pool/mod.rs index b80eb59f..0a7332f9 100644 --- a/src/pool/mod.rs +++ b/src/pool/mod.rs @@ -149,7 +149,7 @@ pub struct Lease { impl Lease { /// Sets the debugging name assigned to this acceleration structure. - pub fn with_name(mut self, name: impl Into) -> Self { + pub fn debug_name(mut self, name: impl Into) -> Self { self.name = Some(name.into()); self @@ -158,7 +158,7 @@ impl Lease { impl Lease { /// Sets the debugging name assigned to this buffer. - pub fn with_name(mut self, name: impl Into) -> Self { + pub fn debug_name(mut self, name: impl Into) -> Self { self.name = Some(name.into()); self @@ -167,7 +167,7 @@ impl Lease { impl Lease { /// Sets the debugging name assigned to this image. - pub fn with_name(mut self, name: impl Into) -> Self { + pub fn debug_name(mut self, name: impl Into) -> Self { self.name = Some(name.into()); self diff --git a/src/resolver.rs b/src/resolver.rs index dfb716a6..46ea1eba 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1,7 +1,7 @@ use { super::{ - Area, Attachment, Binding, Command, ExecutionPipeline, Graph, Node, NodeIndex, - cmd_ref::{Nodes, Subresource, SubresourceAccess}, + Area, Attachment, Command, ExecutionPipeline, Graph, Node, NodeIndex, Resource, + cmd_ref::{Nodes, SubresourceAccess, SubresourceRange}, node::SwapchainImageNode, }, crate::{ @@ -91,7 +91,7 @@ impl AccessCache { } fn update(&mut self, graph: &Graph, end_pass_idx: usize) { - self.binding_count = graph.bindings.len(); + self.binding_count = graph.resources.len(); let cache_len = self.binding_count * end_pass_idx; @@ -391,7 +391,7 @@ impl Resolver { #[profiling::function] fn begin_render_pass( cmd_buf: &CommandBuffer, - bindings: &[Binding], + bindings: &[Resource], pass: &Command, physical_pass: &mut PhysicalPass, render_area: Area, @@ -1885,7 +1885,7 @@ impl Resolver { #[profiling::function] fn record_execution_barriers<'a>( cmd_buf: &CommandBuffer, - bindings: &mut [Binding], + bindings: &mut [Resource], accesses: impl Iterator)>, ) { // We store a Barriers in TLS to save an alloc; contents are POD @@ -1932,9 +1932,9 @@ impl Resolver { let binding = &bindings[*node_idx]; match binding { - Binding::AccelerationStructure(..) - | Binding::AccelerationStructureLease(..) => { - let Some(accel_struct) = binding.as_driver_acceleration_structure() else { + Resource::AccelerationStructure(..) + | Resource::AccelerationStructureLease(..) => { + let Some(accel_struct) = binding.as_driver_accel_struct() else { #[cfg(debug_assertions)] unreachable!(); @@ -1956,7 +1956,7 @@ impl Resolver { ); tls.prev_accesses.push(prev_access); } - Binding::Buffer(..) | Binding::BufferLease(..) => { + Resource::Buffer(..) | Resource::BufferLease(..) => { let Some(buffer) = binding.as_driver_buffer() else { #[cfg(debug_assertions)] unreachable!(); @@ -1972,7 +1972,7 @@ impl Resolver { subresource, } in accesses { - let Subresource::Buffer(range) = subresource else { + let SubresourceRange::Buffer(range) = subresource else { unreachable!() }; @@ -1989,7 +1989,9 @@ impl Resolver { } } } - Binding::Image(..) | Binding::ImageLease(..) | Binding::SwapchainImage(..) => { + Resource::Image(..) + | Resource::ImageLease(..) + | Resource::SwapchainImage(..) => { let Some(image) = binding.as_driver_image() else { #[cfg(debug_assertions)] unreachable!(); @@ -2005,7 +2007,7 @@ impl Resolver { subresource, } in accesses { - let Subresource::Image(range) = subresource else { + let SubresourceRange::Image(range) = subresource else { unreachable!() }; @@ -2133,7 +2135,7 @@ impl Resolver { #[profiling::function] fn record_image_layout_transitions( cmd_buf: &CommandBuffer, - bindings: &mut [Binding], + bindings: &mut [Resource], pass: &mut Command, ) { // We store a Barriers in TLS to save an alloc; contents are POD @@ -2167,14 +2169,14 @@ impl Resolver { debug_assert!(bindings.get(node_idx).is_some()); let binding = unsafe { - // PassRef enforces this using assert_bound_graph_node + // CommandRef enforces this during push_node_access bindings.get_unchecked(node_idx) }; match binding { - Binding::AccelerationStructure(..) - | Binding::AccelerationStructureLease(..) => { - let Some(accel_struct) = binding.as_driver_acceleration_structure() else { + Resource::AccelerationStructure(..) + | Resource::AccelerationStructureLease(..) => { + let Some(accel_struct) = binding.as_driver_accel_struct() else { #[cfg(debug_assertions)] unreachable!(); @@ -2186,7 +2188,7 @@ impl Resolver { AccelerationStructure::access(accel_struct, AccessType::Nothing); } - Binding::Buffer(..) | Binding::BufferLease(..) => { + Resource::Buffer(..) | Resource::BufferLease(..) => { let Some(buffer) = binding.as_driver_buffer() else { #[cfg(debug_assertions)] unreachable!(); @@ -2199,7 +2201,7 @@ impl Resolver { for subresource_access in accesses { let &SubresourceAccess { - subresource: Subresource::Buffer(access_range), + subresource: SubresourceRange::Buffer(access_range), .. } = subresource_access else { @@ -2217,7 +2219,9 @@ impl Resolver { for _ in Buffer::access(buffer, AccessType::Nothing, access_range) {} } } - Binding::Image(..) | Binding::ImageLease(..) | Binding::SwapchainImage(..) => { + Resource::Image(..) + | Resource::ImageLease(..) + | Resource::SwapchainImage(..) => { let Some(image) = binding.as_driver_image() else { #[cfg(debug_assertions)] unreachable!(); @@ -2236,7 +2240,7 @@ impl Resolver { for subresource_access in accesses { let &SubresourceAccess { access, - subresource: Subresource::Image(access_range), + subresource: SubresourceRange::Image(access_range), } = subresource_access else { #[cfg(debug_assertions)] @@ -2333,7 +2337,7 @@ impl Resolver { { let node_idx = node.index(); - debug_assert!(self.graph.bindings.get(node_idx).is_some()); + debug_assert!(self.graph.resources.get(node_idx).is_some()); // We record up to but not including the first pass which accesses the target node if let Some(end_pass_idx) = self.graph.first_node_access_pass_index(node) { @@ -2356,7 +2360,7 @@ impl Resolver { { let node_idx = node.index(); - debug_assert!(self.graph.bindings.get(node_idx).is_some()); + debug_assert!(self.graph.resources.get(node_idx).is_some()); if self.graph.cmds.is_empty() { return Ok(()); @@ -2432,17 +2436,17 @@ impl Resolver { trace!("recording pass [{}: {}]", pass_idx, pass.name()); if !physical_pass.exec_descriptor_sets.is_empty() { - Self::write_descriptor_sets(cmd_buf, &self.graph.bindings, pass, physical_pass)?; + Self::write_descriptor_sets(cmd_buf, &self.graph.resources, pass, physical_pass)?; } let render_area = if is_graphic { - Self::record_image_layout_transitions(cmd_buf, &mut self.graph.bindings, pass); + Self::record_image_layout_transitions(cmd_buf, &mut self.graph.resources, pass); - let render_area = Self::render_area(&self.graph.bindings, pass); + let render_area = Self::render_area(&self.graph.resources, pass); Self::begin_render_pass( cmd_buf, - &self.graph.bindings, + &self.graph.resources, pass, physical_pass, render_area, @@ -2508,7 +2512,7 @@ impl Resolver { if !is_graphic { Self::record_execution_barriers( cmd_buf, - &mut self.graph.bindings, + &mut self.graph.resources, exec.accesses.iter(), ); } @@ -2520,10 +2524,9 @@ impl Resolver { let exec_func = exec.func.take().unwrap().0; exec_func( - &cmd_buf.device, - cmd_buf.handle, + cmd_buf, Nodes::new( - &self.graph.bindings, + &self.graph.resources, #[cfg(debug_assertions)] exec, ), @@ -2605,7 +2608,7 @@ impl Resolver { } #[profiling::function] - fn render_area(bindings: &[Binding], pass: &Command) -> Area { + fn render_area(bindings: &[Resource], pass: &Command) -> Area { // set_render_area was not specified so we're going to guess using the minimum common // attachment extents let first_exec = pass.execs.first().unwrap(); @@ -2721,9 +2724,9 @@ impl Resolver { unscheduled.fill(true); unscheduled.resize(end_pass_idx, true); - unresolved.truncate(self.graph.bindings.len()); + unresolved.truncate(self.graph.resources.len()); unresolved.fill(true); - unresolved.resize(self.graph.bindings.len(), true); + unresolved.resize(self.graph.resources.len(), true); debug_assert!(unchecked.is_empty()); @@ -2954,7 +2957,7 @@ impl Resolver { } pub(crate) fn swapchain_image(&mut self, node: SwapchainImageNode) -> &SwapchainImage { - let Some(swapchain_image) = self.graph.bindings[node.idx].as_swapchain_image() else { + let Some(swapchain_image) = self.graph.resources[node.idx].as_swapchain_image() else { panic!("invalid swapchain image node"); }; @@ -2964,7 +2967,7 @@ impl Resolver { #[profiling::function] fn write_descriptor_sets( cmd_buf: &CommandBuffer, - bindings: &[Binding], + bindings: &[Resource], pass: &Command, physical_pass: &PhysicalPass, ) -> Result<(), DriverError> { @@ -3009,7 +3012,6 @@ impl Resolver { let descriptor_type = descriptor_info.descriptor_type(); let bound_node = &bindings[*node_idx]; if let Some(image) = bound_node.as_driver_image() { - let view_info = view_info.as_ref().unwrap(); let mut image_view_info = *view_info.as_image().unwrap(); // Handle default views which did not specify a particaular aspect @@ -3064,7 +3066,6 @@ impl Resolver { .image_view(image_view), ); } else if let Some(buffer) = bound_node.as_driver_buffer() { - let view_info = view_info.as_ref().unwrap(); let buffer_view_info = view_info.as_buffer().unwrap(); if binding_offset == 0 { @@ -3088,7 +3089,7 @@ impl Resolver { .offset(buffer_view_info.start) .range(buffer_view_info.end - buffer_view_info.start), ); - } else if let Some(accel_struct) = bound_node.as_driver_acceleration_structure() { + } else if let Some(accel_struct) = bound_node.as_driver_accel_struct() { if binding_offset == 0 { tls.accel_struct_writes.push(IndexWrite { idx: tls.accel_struct_infos.len(), From 3f514d40898da6b2a4ef383293621eebd13a231b Mon Sep 17 00:00:00 2001 From: John Wells Date: Wed, 25 Feb 2026 11:34:03 -0500 Subject: [PATCH 22/86] clean-up --- README.md | 4 +- contrib/vk-graph-egui/src/lib.rs | 6 +- contrib/vk-graph-fx/src/bitmap_font.rs | 6 +- contrib/vk-graph-fx/src/image_loader.rs | 4 +- contrib/vk-graph-fx/src/presenter.rs | 12 +- contrib/vk-graph-fx/src/transition.rs | 4 +- contrib/vk-graph-hot/examples/glsl.rs | 10 +- contrib/vk-graph-hot/examples/hlsl.rs | 4 +- contrib/vk-graph-hot/src/compute.rs | 35 +++++- contrib/vk-graph-hot/src/graphic.rs | 35 +++++- contrib/vk-graph-hot/src/lib.rs | 4 +- contrib/vk-graph-hot/src/ray_trace.rs | 35 +++++- contrib/vk-graph-hot/src/shader.rs | 16 +-- contrib/vk-graph-imgui/src/lib.rs | 6 +- examples/bindless.rs | 4 +- examples/debugger.rs | 4 +- examples/font_bmp.rs | 4 +- examples/fuzzer.rs | 104 ++++++++-------- examples/image_sampler.rs | 10 +- examples/min_max.rs | 12 +- examples/mip_compute.rs | 16 +-- examples/mip_graphic.rs | 17 +-- examples/msaa.rs | 6 +- examples/multipass.rs | 12 +- examples/ray_omni.rs | 14 +-- examples/ray_trace.rs | 12 +- examples/rt_triangle.rs | 20 ++-- examples/shader-toy/src/main.rs | 55 ++++++--- examples/skeletal-anim/src/main.rs | 10 +- examples/subgroup_ops.rs | 18 +-- examples/triangle.rs | 4 +- examples/vertex_layout.rs | 4 +- examples/vr/src/main.rs | 12 +- examples/vsm_omni.rs | 28 +++-- src/cmd_ref/accel_struct.rs | 72 +++++++---- src/cmd_ref/compute.rs | 120 +++++++++++-------- src/cmd_ref/graphic.rs | 151 ++++++++++++++---------- src/cmd_ref/mod.rs | 99 ++++++++++------ src/cmd_ref/pipeline.rs | 41 ++++++- src/cmd_ref/ray_trace.rs | 53 ++++++--- src/driver/image.rs | 1 + src/driver/shader.rs | 14 +-- src/lib.rs | 48 ++++---- src/resolver.rs | 4 +- 44 files changed, 713 insertions(+), 437 deletions(-) diff --git a/README.md b/README.md index f09bce52..45c02511 100644 --- a/README.md +++ b/README.md @@ -53,8 +53,8 @@ graph .shader_resource_access(3, fire_buffer, Access::FragmentShaderReadUniformBuffer) .clear_color(0, swapchain_image) .store_color(0, swapchain_image) - .record_pipeline(move |pipeline, _| { - pipeline + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf .push_constants(some_u8_slice) .draw(6, 1, 0, 0); }); diff --git a/contrib/vk-graph-egui/src/lib.rs b/contrib/vk-graph-egui/src/lib.rs index c83f25f4..d37060da 100644 --- a/contrib/vk-graph-egui/src/lib.rs +++ b/contrib/vk-graph-egui/src/lib.rs @@ -175,7 +175,7 @@ impl Egui { // Add user textures. for (id, node) in self.user_textures.drain() { - bound_tex.insert(id, AnyImageNode::from(node)); + bound_tex.insert(id, node); } bound_tex @@ -290,8 +290,8 @@ impl Egui { ) .load_color(0, target) .store_color(0, target) - .record_pipeline(move |pipeline, _| { - pipeline + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf .bind_index_buffer(idx_buf, 0, vk::IndexType::UINT32) .bind_vertex_buffer(0, vert_buf, 0) .push_constants(0, cast_slice(&[push_constants])) diff --git a/contrib/vk-graph-fx/src/bitmap_font.rs b/contrib/vk-graph-fx/src/bitmap_font.rs index c36d79ba..248c968b 100644 --- a/contrib/vk-graph-fx/src/bitmap_font.rs +++ b/contrib/vk-graph-fx/src/bitmap_font.rs @@ -215,9 +215,9 @@ impl BitmapFont { ); } - pass.record_pipeline(move |pipeline, _| { + pass.record_cmd_buf(move |cmd_buf, _| { if let Some((x, y, width, height)) = scissor { - pipeline.set_scissor( + cmd_buf.set_scissor( 0, &[vk::Rect2D { offset: vk::Offset2D { x, y }, @@ -226,7 +226,7 @@ impl BitmapFont { ); } - pipeline + cmd_buf .push_constants(0, cast_slice(&transform.to_cols_array())) .push_constants(64, &(1.0 / image_info.width as f32).to_ne_bytes()) .push_constants(68, &(1.0 / image_info.height as f32).to_ne_bytes()) diff --git a/contrib/vk-graph-fx/src/image_loader.rs b/contrib/vk-graph-fx/src/image_loader.rs index 459e849a..6659f619 100644 --- a/contrib/vk-graph-fx/src/image_loader.rs +++ b/contrib/vk-graph-fx/src/image_loader.rs @@ -208,8 +208,8 @@ impl ImageLoader { .bind_pipeline(&self.decode_rgb_rgba) .shader_resource_access(0, pixel_buf, AccessType::ComputeShaderReadOther) .shader_resource_access(1, temp_image, AccessType::ComputeShaderWrite) - .record_pipeline(move |pipeline, _| { - pipeline + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf .push_constants(0, &(pixel_buf_stride >> 2).to_ne_bytes()) .dispatch(dispatch_x, dispatch_y, 1); }) diff --git a/contrib/vk-graph-fx/src/presenter.rs b/contrib/vk-graph-fx/src/presenter.rs index d2b14ecd..77ef3be1 100644 --- a/contrib/vk-graph-fx/src/presenter.rs +++ b/contrib/vk-graph-fx/src/presenter.rs @@ -44,8 +44,8 @@ impl ComputePresenter { .bind_pipeline(&self.0[0]) .shader_resource_access(0, image, AccessType::ComputeShaderReadOther) .shader_resource_access(1, swapchain, AccessType::ComputeShaderWrite) - .record_pipeline(move |pipeline, _| { - pipeline.dispatch(swapchain_info.width, swapchain_info.height, 1); + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf.dispatch(swapchain_info.width, swapchain_info.height, 1); }); } @@ -72,8 +72,8 @@ impl ComputePresenter { .shader_resource_access((0, [0]), top_image, AccessType::ComputeShaderReadOther) .shader_resource_access((0, [1]), bottom_image, AccessType::ComputeShaderReadOther) .shader_resource_access(1, swapchain, AccessType::ComputeShaderWrite) - .record_pipeline(move |pipeline, _| { - pipeline.dispatch(swapchain_info.width, swapchain_info.height, 1); + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf.dispatch(swapchain_info.width, swapchain_info.height, 1); }); } } @@ -130,9 +130,9 @@ impl GraphicPresenter { AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, ) .store_color(0, swapchain) - .record_pipeline(move |pipeline, _| { + .record_cmd_buf(move |cmd_buf, _| { // Draw a quad with implicit vertices (no buffer) - pipeline + cmd_buf .push_constants(0, cast_slice(&transform.to_cols_array())) .draw(6, 1, 0, 0); }); diff --git a/contrib/vk-graph-fx/src/transition.rs b/contrib/vk-graph-fx/src/transition.rs index 702700d2..225455c3 100644 --- a/contrib/vk-graph-fx/src/transition.rs +++ b/contrib/vk-graph-fx/src/transition.rs @@ -463,8 +463,8 @@ impl TransitionPipeline { .shader_resource_access(0, a_image, AccessType::ComputeShaderReadOther) .shader_resource_access(1, b_image, AccessType::ComputeShaderReadOther) .shader_resource_access(2, dest_image, AccessType::ComputeShaderWrite) - .record_pipeline(move |pipeline, _| { - pipeline.push_constants(0, &push_consts).dispatch( + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf.push_constants(0, &push_consts).dispatch( dest_info.width, dest_info.height, 1, diff --git a/contrib/vk-graph-hot/examples/glsl.rs b/contrib/vk-graph-hot/examples/glsl.rs index 40c26882..ef5f5a5a 100644 --- a/contrib/vk-graph-hot/examples/glsl.rs +++ b/contrib/vk-graph-hot/examples/glsl.rs @@ -34,10 +34,12 @@ fn main() -> Result<(), WindowError> { .debug_name("make some noise") .bind_pipeline(pipeline.hot()) .write_descriptor(0, frame.swapchain_image) - .record_pipeline(move |pipeline, _| { - pipeline - .push_constants(&frame_index.to_ne_bytes()) - .dispatch(frame.width, frame.height, 1); + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf.push_constants(&frame_index.to_ne_bytes()).dispatch( + frame.width, + frame.height, + 1, + ); }); frame_index += 1; diff --git a/contrib/vk-graph-hot/examples/hlsl.rs b/contrib/vk-graph-hot/examples/hlsl.rs index 9f3abf63..1e57bf4f 100644 --- a/contrib/vk-graph-hot/examples/hlsl.rs +++ b/contrib/vk-graph-hot/examples/hlsl.rs @@ -39,8 +39,8 @@ fn main() -> Result<(), WindowError> { .bind_pipeline(pipeline.hot()) .clear_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) - .record_pipeline(move |pipeline, _| { - pipeline + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf .push_constants_offset(0, &frame_index.to_ne_bytes()) .push_constants_offset(4, &frame.width.to_ne_bytes()) .push_constants_offset(8, &frame.height.to_ne_bytes()) diff --git a/contrib/vk-graph-hot/src/compute.rs b/contrib/vk-graph-hot/src/compute.rs index 1f040909..57ed96b8 100644 --- a/contrib/vk-graph-hot/src/compute.rs +++ b/contrib/vk-graph-hot/src/compute.rs @@ -4,9 +4,12 @@ use { super::{compile_shader_and_watch, create_watcher, shader::HotShader}, log::info, notify::RecommendedWatcher, - std::sync::{ - atomic::{AtomicBool, Ordering}, - Arc, + std::{ + ops::Deref, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, }, vk_graph::driver::{ compute::{ComputePipeline, ComputePipelineInfo}, @@ -48,8 +51,10 @@ impl HotComputePipeline { /// Returns the most recent compilation without checking for changes or re-compiling the shader /// source code. + #[deprecated = "use Deref instead"] + #[doc(hidden)] pub fn cold(&self) -> &ComputePipeline { - &self.pipeline + self } /// Returns the most recent compilation after checking for changes, and if needed re-compiling @@ -74,12 +79,32 @@ impl HotComputePipeline { } } - self.cold() + self } } impl AsRef for HotComputePipeline { fn as_ref(&self) -> &ComputePipeline { + self + } +} + +impl Deref for HotComputePipeline { + type Target = ComputePipeline; + + fn deref(&self) -> &Self::Target { &self.pipeline } } + +#[allow(unused)] +mod deprecated { + use {crate::compute::HotComputePipeline, vk_graph::driver::compute::ComputePipeline}; + + impl HotComputePipeline { + #[deprecated = "use Deref instead"] + fn as_ref(&self) -> &ComputePipeline { + self + } + } +} diff --git a/contrib/vk-graph-hot/src/graphic.rs b/contrib/vk-graph-hot/src/graphic.rs index 3b964888..9e5c5b6f 100644 --- a/contrib/vk-graph-hot/src/graphic.rs +++ b/contrib/vk-graph-hot/src/graphic.rs @@ -4,9 +4,12 @@ use { super::{compile_shader_and_watch, create_watcher, shader::HotShader}, log::info, notify::RecommendedWatcher, - std::sync::{ - atomic::{AtomicBool, Ordering}, - Arc, + std::{ + ops::Deref, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, }, vk_graph::driver::{ device::Device, @@ -57,8 +60,10 @@ impl HotGraphicPipeline { /// Returns the most recent compilation without checking for changes or re-compiling the shader /// source code. + #[deprecated = "use Deref instead"] + #[doc(hidden)] pub fn cold(&self) -> &GraphicPipeline { - &self.pipeline + self } /// Returns the most recent compilation after checking for changes, and if needed re-compiling @@ -88,12 +93,32 @@ impl HotGraphicPipeline { } } - self.cold() + self } } impl AsRef for HotGraphicPipeline { fn as_ref(&self) -> &GraphicPipeline { + self + } +} + +impl Deref for HotGraphicPipeline { + type Target = GraphicPipeline; + + fn deref(&self) -> &Self::Target { &self.pipeline } } + +#[allow(unused)] +mod deprecated { + use {crate::graphic::HotGraphicPipeline, vk_graph::driver::graphic::GraphicPipeline}; + + impl HotGraphicPipeline { + #[deprecated = "use Deref instead"] + fn as_ref(&self) -> &GraphicPipeline { + self + } + } +} diff --git a/contrib/vk-graph-hot/src/lib.rs b/contrib/vk-graph-hot/src/lib.rs index 9e7df688..372c9802 100644 --- a/contrib/vk-graph-hot/src/lib.rs +++ b/contrib/vk-graph-hot/src/lib.rs @@ -141,8 +141,8 @@ fn compile_shader_and_watch( base_shader = base_shader.entry_name(shader.entry_name.clone()); - if let Some(specialization_info) = &shader.specialization_info { - base_shader = base_shader.specialization_info(specialization_info.clone()); + if let Some(specialization) = &shader.specialization { + base_shader = base_shader.specialization(specialization.clone()); } Ok(base_shader) diff --git a/contrib/vk-graph-hot/src/ray_trace.rs b/contrib/vk-graph-hot/src/ray_trace.rs index 0ba29d24..b82264ff 100644 --- a/contrib/vk-graph-hot/src/ray_trace.rs +++ b/contrib/vk-graph-hot/src/ray_trace.rs @@ -4,9 +4,12 @@ use { super::{compile_shader_and_watch, create_watcher, shader::HotShader}, log::info, notify::RecommendedWatcher, - std::sync::{ - atomic::{AtomicBool, Ordering}, - Arc, + std::{ + ops::Deref, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, }, vk_graph::driver::{ device::Device, @@ -66,8 +69,10 @@ impl HotRayTracePipeline { /// Returns the most recent compilation without checking for changes or re-compiling the shader /// source code. + #[deprecated = "use Deref instead"] + #[doc(hidden)] pub fn cold(&self) -> &RayTracePipeline { - &self.pipeline + self } /// Returns the most recent compilation after checking for changes, and if needed re-compiling @@ -98,12 +103,32 @@ impl HotRayTracePipeline { } } - self.cold() + self } } impl AsRef for HotRayTracePipeline { fn as_ref(&self) -> &RayTracePipeline { + self + } +} + +impl Deref for HotRayTracePipeline { + type Target = RayTracePipeline; + + fn deref(&self) -> &Self::Target { &self.pipeline } } + +#[allow(unused)] +mod deprecated { + use {crate::ray_trace::HotRayTracePipeline, vk_graph::driver::ray_trace::RayTracePipeline}; + + impl HotRayTracePipeline { + #[deprecated = "use Deref instead"] + fn as_ref(&self) -> &RayTracePipeline { + self + } + } +} diff --git a/contrib/vk-graph-hot/src/shader.rs b/contrib/vk-graph-hot/src/shader.rs index e222ed65..292ee6ba 100644 --- a/contrib/vk-graph-hot/src/shader.rs +++ b/contrib/vk-graph-hot/src/shader.rs @@ -9,7 +9,7 @@ use { notify::{RecommendedWatcher, RecursiveMode, Watcher}, shaderc::{CompileOptions, EnvVersion, ShaderKind, TargetEnv}, std::path::{Path, PathBuf}, - vk_graph::driver::{ash::vk, shader::SpecializationInfo, DriverError}, + vk_graph::driver::{ash::vk, shader::SpecializationMap, DriverError}, }; /// Describes a shader program which runs on some pipeline stage. @@ -79,18 +79,14 @@ pub struct HotShader { /// # let my_shader_code = [0u8; 1]; /// // We instead specify 42 for MY_COUNT: /// let shader = HotShader::new_fragment(my_shader_code.as_slice()) - /// .specialization_info(SpecializationInfo::new( - /// [vk::SpecializationMapEntry { - /// constant_id: 0, - /// offset: 0, - /// size: 4, - /// }], - /// 42u32.to_ne_bytes() - /// )); + /// .specialization( + /// SpecializationMap::new(42u32.to_ne_bytes()) + /// .constant(0, 0, 4) + /// ); /// # Ok(()) } /// ``` #[builder(default, setter(strip_option))] - pub specialization_info: Option, + pub specialization: Option, /// The shader stage this structure applies to. pub stage: vk::ShaderStageFlags, diff --git a/contrib/vk-graph-imgui/src/lib.rs b/contrib/vk-graph-imgui/src/lib.rs index f3a7cb2d..4be2ac10 100644 --- a/contrib/vk-graph-imgui/src/lib.rs +++ b/contrib/vk-graph-imgui/src/lib.rs @@ -195,8 +195,8 @@ impl ImGui { ) .clear_color(0, image) .store_color(0, image) - .record_pipeline(move |pipeline, _| { - pipeline + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf .push_constants(0, &window_width.to_ne_bytes()) .push_constants(4, &window_height.to_ne_bytes()) .bind_index_buffer(index_buf, 0, vk::IndexType::UINT16) @@ -213,7 +213,7 @@ impl ImGui { let y = clip_rect[1].floor() as i32; let width = (clip_rect[2] - clip_rect[0]).ceil() as u32; let height = (clip_rect[3] - clip_rect[1]).ceil() as u32; - pipeline + cmd_buf .set_scissor( 0, &[vk::Rect2D { diff --git a/examples/bindless.rs b/examples/bindless.rs index 94d2da1e..054761cf 100644 --- a/examples/bindless.rs +++ b/examples/bindless.rs @@ -44,8 +44,8 @@ fn main() -> Result<(), WindowError> { cmd.clear_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) - .record_pipeline(move |pipeline, _| { - pipeline.draw_indirect(draw_buf_node, 0, 64, 16); + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf.draw_indirect(draw_buf_node, 0, 64, 16); }); }) } diff --git a/examples/debugger.rs b/examples/debugger.rs index 385006d5..84813624 100644 --- a/examples/debugger.rs +++ b/examples/debugger.rs @@ -180,8 +180,8 @@ fn main() -> Result<(), vk_graph_window::WindowError> { .debug_name("This doesn't look good...") .bind_pipeline(&compute_pipeline) .shader_resource_access(42, image, AccessType::ComputeShaderWrite) - .record_pipeline(|pipeline, _| { - pipeline.dispatch(1024, 1024, 1); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.dispatch(1024, 1024, 1); }); // Growing tired of your advenutes, you signal that it is time to close the window and exit diff --git a/examples/font_bmp.rs b/examples/font_bmp.rs index 62532f2c..a45302fd 100644 --- a/examples/font_bmp.rs +++ b/examples/font_bmp.rs @@ -134,8 +134,8 @@ fn main() -> anyhow::Result<()> { .debug_name("smoke") .bind_pipeline(&smoke_pipeline) .shader_resource_access(0, image_node, AccessType::ComputeShaderWrite) - .record_pipeline(move |pipeline, _| { - pipeline + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf .push_constants(0, &elapsed_time.as_secs_f32().to_ne_bytes()) .dispatch(frame.width.div_ceil(subgroup_size), frame.height, 1); }); diff --git a/examples/fuzzer.rs b/examples/fuzzer.rs index 1c50fb33..1c37d6e4 100644 --- a/examples/fuzzer.rs +++ b/examples/fuzzer.rs @@ -286,10 +286,10 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { .map(|(blas_node, _, _)| *blas_node) .collect::>(); - let mut cmd = cmd.record_accel_struct(move |accel_struct, nodes| { + let mut cmd = cmd.record_cmd_buf(move |cmd_buf, nodes| { for (blas_node, scratch_buf, build_data) in blas_nodes { let scratch_addr = nodes[scratch_buf].device_address(); - accel_struct.build(&[BuildAccelerationStructureInfo::new( + cmd_buf.build_accel_struct(&[BuildAccelerationStructureInfo::new( blas_node, scratch_addr, build_data, @@ -307,11 +307,11 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { AccessType::AccelerationStructureBufferWrite, ) .resource_access(tlas_node, AccessType::AccelerationStructureBuildWrite) - .record_accel_struct(move |accel_struct, nodes| { - let scratch_data = Buffer::device_address(&nodes[tlas_scratch_buf]); - accel_struct.build(&[BuildAccelerationStructureInfo::new( + .record_cmd_buf(move |cmd_buf, nodes| { + let scratch_addr = nodes[tlas_scratch_buf].device_address(); + cmd_buf.build_accel_struct(&[BuildAccelerationStructureInfo::new( tlas_node, - scratch_data, + scratch_addr, tlas_geometry_info, )]); }); @@ -376,8 +376,8 @@ fn record_pipeline_array_bind(frame: &mut FrameContext, pool: &mut HashPool) { .shader_resource_access((0, [2]), images[2], AccessType::ComputeShaderReadOther) .shader_resource_access((0, [3]), images[3], AccessType::ComputeShaderReadOther) .shader_resource_access((0, [4]), images[4], AccessType::ComputeShaderReadOther) - .record_pipeline(|pipeline, _| { - pipeline + .record_cmd_buf(|cmd_buf, _| { + cmd_buf .push_constants(0, &0f32.to_ne_bytes()) .dispatch(64, 64, 1); }); @@ -442,8 +442,8 @@ fn record_pipeline_bindless(frame: &mut FrameContext, pool: &mut HashPool) { .shader_resource_access((0, [2]), images[2], AccessType::ComputeShaderWrite) .shader_resource_access((0, [3]), images[3], AccessType::ComputeShaderWrite) .shader_resource_access((0, [4]), images[4], AccessType::ComputeShaderWrite) - .record_pipeline(|pipeline, _| { - pipeline + .record_cmd_buf(|cmd_buf, _| { + cmd_buf .push_constants(0, &5u32.to_ne_bytes()) .dispatch(64, 64, 1); }); @@ -472,8 +472,8 @@ fn record_pipeline_no_op(frame: &mut FrameContext, _: &mut HashPool) { .begin_cmd() .debug_name("no-op") .bind_pipeline(&pipeline) - .record_pipeline(|pipeline, _| { - pipeline.dispatch(1, 1, 1); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.dispatch(1, 1, 1); }); } @@ -580,8 +580,8 @@ fn record_graphic_bindless(frame: &mut FrameContext, pool: &mut HashPool) { ) .clear_color(0, image) .store_color(0, image) - .record_pipeline(|pipeline, _| { - pipeline + .record_cmd_buf(|cmd_buf, _| { + cmd_buf .push_constants(0, &5u32.to_ne_bytes()) .draw(1, 1, 0, 0); }); @@ -623,8 +623,8 @@ fn record_graphic_load_store(frame: &mut FrameContext, _: &mut HashPool) { .bind_pipeline(&pipeline) .load_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) - .record_pipeline(|pipeline, _| { - pipeline.draw(1, 1, 0, 0); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.draw(1, 1, 0, 0); }); } @@ -803,8 +803,8 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo Some(depth_resolve_mode), Some(ResolveMode::SampleZero), ) - .record_pipeline(|pipeline, _| { - pipeline.draw(3, 1, 0, 0); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.draw(3, 1, 0, 0); }); } @@ -851,8 +851,8 @@ fn record_graphic_will_merge_common_color1(frame: &mut FrameContext, pool: &mut .as_slice(), )) .store_color(0, image) - .record_pipeline(|pipeline, _| { - pipeline.draw(1, 1, 0, 0); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.draw(1, 1, 0, 0); }); frame .graph @@ -886,8 +886,8 @@ fn record_graphic_will_merge_common_color1(frame: &mut FrameContext, pool: &mut )) .load_color(0, image) .store_color(0, image) - .record_pipeline(|pipeline, _| { - pipeline.draw(1, 1, 0, 0); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.draw(1, 1, 0, 0); }); } @@ -942,8 +942,8 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut .as_slice(), )) .store_color(0, image_0) - .record_pipeline(|pipeline, _| { - pipeline.draw(1, 1, 0, 0); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.draw(1, 1, 0, 0); }); frame .graph @@ -980,8 +980,8 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut .load_color(0, image_0) .store_color(0, image_0) .store_color(1, image_1) - .record_pipeline(|pipeline, _| { - pipeline.draw(1, 1, 0, 0); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.draw(1, 1, 0, 0); }); frame .graph @@ -1015,8 +1015,8 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut )) .clear_color(0, image_0) .store_color(0, image_0) - .record_pipeline(|pipeline, _| { - pipeline.draw(1, 1, 0, 0); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.draw(1, 1, 0, 0); }); } @@ -1073,8 +1073,8 @@ fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut )) .store_color(0, color_image) .store_depth_stencil(depth_image) - .record_pipeline(|pipeline, _| { - pipeline.draw(1, 1, 0, 0); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.draw(1, 1, 0, 0); }); frame .graph @@ -1106,8 +1106,8 @@ fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut )) .load_depth_stencil(depth_image) .store_depth_stencil(depth_image) - .record_pipeline(|pipeline, _| { - pipeline.draw(1, 1, 0, 0); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.draw(1, 1, 0, 0); }); } @@ -1161,8 +1161,8 @@ fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut .as_slice(), )) .store_depth_stencil(depth_image) - .record_pipeline(|pipeline, _| { - pipeline.draw(1, 1, 0, 0); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.draw(1, 1, 0, 0); }); frame .graph @@ -1197,8 +1197,8 @@ fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut .store_color(0, color_image) .load_depth_stencil(depth_image) .store_depth_stencil(depth_image) - .record_pipeline(|pipeline, _| { - pipeline.draw(1, 1, 0, 0); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.draw(1, 1, 0, 0); }); } @@ -1242,8 +1242,8 @@ fn record_graphic_will_merge_common_depth3(frame: &mut FrameContext, pool: &mut .as_slice(), )) .store_depth_stencil(depth_image) - .record_pipeline(|pipeline, _| { - pipeline.draw(1, 1, 0, 0); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.draw(1, 1, 0, 0); }); frame .graph @@ -1275,8 +1275,8 @@ fn record_graphic_will_merge_common_depth3(frame: &mut FrameContext, pool: &mut )) .load_depth_stencil(depth_image) .store_depth_stencil(depth_image) - .record_pipeline(|pipeline, _| { - pipeline.draw(1, 1, 0, 0); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.draw(1, 1, 0, 0); }); } @@ -1349,8 +1349,8 @@ fn record_graphic_will_merge_subpass_input(frame: &mut FrameContext, pool: &mut .bind_pipeline(&pipeline_a) .clear_color(0, image) .store_color(0, image) - .record_pipeline(|pipeline, _| { - pipeline.draw(1, 1, 0, 0); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.draw(1, 1, 0, 0); }); frame .graph @@ -1358,8 +1358,8 @@ fn record_graphic_will_merge_subpass_input(frame: &mut FrameContext, pool: &mut .debug_name("b") .bind_pipeline(&pipeline_b) .store_color(0, image) - .record_pipeline(|pipeline, _| { - pipeline.draw(1, 1, 0, 0); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.draw(1, 1, 0, 0); }); } @@ -1408,8 +1408,8 @@ fn record_graphic_wont_merge(frame: &mut FrameContext, pool: &mut HashPool) { .debug_name("c") .bind_pipeline(&pipeline) .store_color(0, image) - .record_pipeline(|pipeline, _| { - pipeline.draw(1, 1, 0, 0); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.draw(1, 1, 0, 0); }); frame .graph @@ -1417,8 +1417,8 @@ fn record_graphic_wont_merge(frame: &mut FrameContext, pool: &mut HashPool) { .debug_name("d") .bind_pipeline(&pipeline) .store_color(0, image) - .record_pipeline(|pipeline, _| { - pipeline.draw(1, 1, 0, 0); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.draw(1, 1, 0, 0); }); } @@ -1490,8 +1490,8 @@ fn record_transfer_graphic_multipass(frame: &mut FrameContext, pool: &mut HashPo images[0], AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, ) - .record_pipeline(|pipeline, _| { - pipeline.draw(1, 1, 0, 0); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.draw(1, 1, 0, 0); }); frame .graph @@ -1505,8 +1505,8 @@ fn record_transfer_graphic_multipass(frame: &mut FrameContext, pool: &mut HashPo images[1], AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, ) - .record_pipeline(|pipeline, _| { - pipeline.draw(1, 1, 0, 0); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.draw(1, 1, 0, 0); }); } diff --git a/examples/image_sampler.rs b/examples/image_sampler.rs index 92074140..9bc399e7 100644 --- a/examples/image_sampler.rs +++ b/examples/image_sampler.rs @@ -71,10 +71,14 @@ fn main() -> anyhow::Result<()> { .begin_cmd() .debug_name("Draw gulf image to swapchain") .bind_pipeline(&pipelines[pipeline_index]) - .shader_resource_access(0, gulf_image, AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer) + .shader_resource_access( + 0, + gulf_image, + AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, + ) .store_color(0, frame.swapchain_image) - .record_pipeline(|pipeline, _| { - pipeline.draw(3, 1, 0, 0); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.draw(3, 1, 0, 0); }); })?; diff --git a/examples/min_max.rs b/examples/min_max.rs index ee8262d6..c03d1f84 100644 --- a/examples/min_max.rs +++ b/examples/min_max.rs @@ -161,7 +161,7 @@ fn reduce_depth_image( depth_image: ImageNode, reduction_mode: vk::SamplerReductionMode, ) -> Result { - let depth_info = graph.node_info(depth_image); + let depth_info = graph.resource(depth_image).info; assert_eq!(depth_info.width, depth_info.height); @@ -204,10 +204,10 @@ fn reduce_depth_image( ) .image_sampler(0, SamplerInfo::LINEAR.reduction_mode(reduction_mode)), )?) - .read_descriptor(0, depth_image) - .write_descriptor(1, reduced_image) - .record_pipeline(move |compute, _| { - compute.dispatch(reduced_info.width, reduced_info.height, 1); + .shader_resource_access(0, depth_image, AccessType::ComputeShaderReadOther) + .shader_resource_access(1, reduced_image, AccessType::ComputeShaderWrite) + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf.dispatch(reduced_info.width, reduced_info.height, 1); }); Ok(reduced_image) @@ -218,7 +218,7 @@ fn copy_image_to_buffer( graph: &mut Graph, reduced_image: ImageNode, ) -> Result, DriverError> { - let reduced_info = graph.node_info(reduced_image); + let reduced_info = graph.resource(reduced_image).info; let result_len = (reduced_info.width * reduced_info.height) as vk::DeviceSize * size_of::() as vk::DeviceSize; let result_buf = graph.bind_resource(Buffer::create( diff --git a/examples/mip_compute.rs b/examples/mip_compute.rs index ac0a137c..001049d3 100644 --- a/examples/mip_compute.rs +++ b/examples/mip_compute.rs @@ -32,7 +32,7 @@ fn main() -> Result<(), DriverError> { .to_builder() .mip_level_count(3), )?); - let depth_info = graph.node_info(depth_pyramid); + let depth_info = graph.resource(depth_pyramid).info; // You would normally create this buffer by copying the depth attachment image #[allow(clippy::inconsistent_digit_grouping)] @@ -85,26 +85,28 @@ fn main() -> Result<(), DriverError> { for mip_level in 1..depth_info.mip_level_count { pass = pass - .read_descriptor_as( + .shader_subresource_access( 0, depth_pyramid, depth_info - .default_view_info() + .into_image_view() .to_builder() .base_mip_level(mip_level - 1) .mip_level_count(1), + AccessType::ComputeShaderReadOther, ) - .write_descriptor_as( + .shader_subresource_access( 1, depth_pyramid, depth_info - .default_view_info() + .into_image_view() .to_builder() .base_mip_level(mip_level) .mip_level_count(1), + AccessType::ComputeShaderWrite, ) - .record_pipeline(move |compute, _| { - compute.dispatch( + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf.dispatch( depth_info.width >> mip_level, depth_info.height >> mip_level, 1, diff --git a/examples/mip_graphic.rs b/examples/mip_graphic.rs index a71e9469..38194b1c 100644 --- a/examples/mip_graphic.rs +++ b/examples/mip_graphic.rs @@ -47,16 +47,17 @@ fn main() -> Result<(), WindowError> { assert!( frame .graph - .resource(frame.swapchain_image).info + .resource(frame.swapchain_image) + .info .usage .contains(vk::ImageUsageFlags::COLOR_ATTACHMENT) ); let image = frame.graph.bind_resource(&image); - let swapchain_info = frame.graph.node_info(frame.swapchain_image); + let swapchain_info = frame.graph.resource(frame.swapchain_image).info; let stripe_width = swapchain_info.width / mip_level_count; - let mut pass = frame + let mut cmd = frame .graph .begin_cmd() .debug_name("splat mips") @@ -64,7 +65,7 @@ fn main() -> Result<(), WindowError> { for mip_level in 0..mip_level_count { let stripe_x = mip_level * stripe_width; - pass = pass + cmd = cmd .shader_subresource_access( 0, image, @@ -78,8 +79,8 @@ fn main() -> Result<(), WindowError> { .load_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) .set_render_area(stripe_x as _, 0, stripe_width, swapchain_info.height) - .record_pipeline(|cmd, _| { - cmd.draw(6, 1, 0, 0); + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.draw(6, 1, 0, 0); }); } }) @@ -168,8 +169,8 @@ fn fill_mip_levels(device: &Device, image: &Arc) -> Result<(), DriverErro .base_mip_level(mip_level) .mip_level_count(1), ) - .record_pipeline(|pipeline, _| { - pipeline + .record_cmd_buf(|cmd_buf, _| { + cmd_buf .push_constants( 0, bytes_of(&PushConstants { diff --git a/examples/msaa.rs b/examples/msaa.rs index 0738d4b7..49e3bc5d 100644 --- a/examples/msaa.rs +++ b/examples/msaa.rs @@ -108,7 +108,7 @@ fn main() -> anyhow::Result<()> { ImageInfo::image_2d( frame.width, frame.height, - pass.node_info(frame.swapchain_image).fmt, + pass.resource(frame.swapchain_image).info.fmt, vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::TRANSIENT_ATTACHMENT, ) @@ -156,8 +156,8 @@ fn main() -> anyhow::Result<()> { .store_color(0, frame.swapchain_image); } - pass.record_pipeline(move |pipeline, _| { - pipeline + pass.record_cmd_buf(move |cmd_buf, _| { + cmd_buf .bind_vertex_buffer(0, cube_vertex_buf, 0) .push_constants(0, bytes_of(&world_transform)) .draw(cube_mesh.vertex_count, 1, 0, 0); diff --git a/examples/multipass.rs b/examples/multipass.rs index 1640c9b7..165a560a 100644 --- a/examples/multipass.rs +++ b/examples/multipass.rs @@ -101,8 +101,8 @@ fn main() -> anyhow::Result<()> { .resource_access(vertex_buf, AccessType::VertexBuffer) .clear_depth_stencil(depth_stencil) .store_depth_stencil(depth_stencil) - .record_pipeline(move |pipeline, _| { - pipeline + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf .bind_index_buffer(index_buf, 0, vk::IndexType::UINT16) .bind_vertex_buffer(0, vertex_buf, 0) .push_constants(0, bytes_of(&obj_pos)) @@ -142,8 +142,8 @@ fn main() -> anyhow::Result<()> { .load_depth_stencil(depth_stencil) .store_depth_stencil(depth_stencil) .store_color(0, frame.swapchain_image) - .record_pipeline(move |pipeline, _| { - pipeline + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf .bind_index_buffer(index_buf, 0, vk::IndexType::UINT16) .bind_vertex_buffer(0, vertex_buf, 0) .push_constants(0, &push_const_data) @@ -167,8 +167,8 @@ fn main() -> anyhow::Result<()> { .load_depth_stencil(depth_stencil) .load_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) - .record_pipeline(move |pipeline, _| { - pipeline.draw(6, 1, 0, 0); + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf.draw(6, 1, 0, 0); }); })?; diff --git a/examples/ray_omni.rs b/examples/ray_omni.rs index d5a0cb5d..c778ecf6 100644 --- a/examples/ray_omni.rs +++ b/examples/ray_omni.rs @@ -106,13 +106,13 @@ fn main() -> anyhow::Result<()> { .clear_depth_stencil(depth_image) .clear_color_value(0, frame.swapchain_image, [0xff, 0xff, 0xff, 0xff]) .store_color(0, frame.swapchain_image) - .record_pipeline(move |pipeline, _| { - pipeline + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf .bind_index_buffer(model_mesh_index_buf, 0, vk::IndexType::UINT32) .bind_vertex_buffer(0, model_mesh_vertex_buf, 0) .draw_indexed(model_mesh.index_count, 1, 0, 0, 0); - pipeline + cmd_buf .bind_index_buffer(ground_mesh_index_buf, 0, vk::IndexType::UINT32) .bind_vertex_buffer(0, ground_mesh_vertex_buf, 0) .draw_indexed(ground_mesh.index_count, 1, 0, 0, 0); @@ -212,8 +212,8 @@ fn create_blas( pass.resource_access(blas, AccessType::AccelerationStructureBuildWrite) .resource_access(scratch_buf, AccessType::AccelerationStructureBufferWrite) - .record_accel_struct(move |accel_struct, _| { - accel_struct.build(&[BuildAccelerationStructureInfo::new( + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf.build_accel_struct(&[BuildAccelerationStructureInfo::new( blas, scratch_addr, info, @@ -392,8 +392,8 @@ fn create_tlas( .resource_access(instance_buf, AccessType::AccelerationStructureBuildRead) .resource_access(scratch_buf, AccessType::AccelerationStructureBufferWrite) .resource_access(tlas, AccessType::AccelerationStructureBuildWrite) - .record_accel_struct(move |accel_struct, _| { - accel_struct.build(&[BuildAccelerationStructureInfo::new( + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf.build_accel_struct(&[BuildAccelerationStructureInfo::new( tlas, scratch_addr, info, diff --git a/examples/ray_trace.rs b/examples/ray_trace.rs index 88b33b10..5e06afa9 100644 --- a/examples/ray_trace.rs +++ b/examples/ray_trace.rs @@ -693,8 +693,8 @@ fn main() -> anyhow::Result<()> { .resource_access(vertex_node, AccessType::AccelerationStructureBuildRead) .resource_access(scratch_buf, AccessType::AccelerationStructureBufferWrite) .resource_access(blas_node, AccessType::AccelerationStructureBuildWrite) - .record_accel_struct(move |accel_struct, _| { - accel_struct.build(&[BuildAccelerationStructureInfo::new( + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf.build_accel_struct(&[BuildAccelerationStructureInfo::new( blas_node, scratch_data, blas_geometry_info, @@ -724,8 +724,8 @@ fn main() -> anyhow::Result<()> { .resource_access(instance_node, AccessType::AccelerationStructureBuildRead) .resource_access(scratch_buf, AccessType::AccelerationStructureBufferWrite) .resource_access(tlas_node, AccessType::AccelerationStructureBuildWrite) - .record_accel_struct(move |accel_struct, _| { - accel_struct.build(&[BuildAccelerationStructureInfo::new( + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf.build_accel_struct(&[BuildAccelerationStructureInfo::new( tlas_node, scratch_addr, tlas_geometry_info, @@ -881,8 +881,8 @@ fn main() -> anyhow::Result<()> { AccessType::RayTracingShaderReadOther, ) .shader_resource_access(6, material_buf_node, AccessType::RayTracingShaderReadOther) - .record_pipeline(move |pipeline, _| { - pipeline.trace_rays( + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf.trace_rays( &sbt_rgen, &sbt_miss, &sbt_hit, diff --git a/examples/rt_triangle.rs b/examples/rt_triangle.rs index 1ffdadfc..8645261a 100644 --- a/examples/rt_triangle.rs +++ b/examples/rt_triangle.rs @@ -338,7 +338,7 @@ fn main() -> anyhow::Result<()> { .to_builder() .alignment(accel_struct_scratch_offset_alignment), )?); - let scratch_data = graph.resource(scratch_buf).device_address(); + let scratch_addr = graph.resource(scratch_buf).device_address(); graph .begin_cmd() @@ -347,10 +347,10 @@ fn main() -> anyhow::Result<()> { .resource_access(vertex_node, AccessType::AccelerationStructureBuildRead) .resource_access(scratch_buf, AccessType::AccelerationStructureBufferWrite) .resource_access(blas_node, AccessType::AccelerationStructureBuildWrite) - .record_accel_struct(move |accel_struct, _| { - accel_struct.build(&[BuildAccelerationStructureInfo::new( + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf.build_accel_struct(&[BuildAccelerationStructureInfo::new( blas_node, - scratch_data, + scratch_addr, blas_geometry_info, )]); }); @@ -368,7 +368,7 @@ fn main() -> anyhow::Result<()> { .to_builder() .alignment(accel_struct_scratch_offset_alignment), )?); - let scratch_data = graph.resource(scratch_buf).device_address(); + let scratch_addr = graph.resource(scratch_buf).device_address(); let tlas_node = graph.bind_resource(&tlas); graph @@ -378,10 +378,10 @@ fn main() -> anyhow::Result<()> { .resource_access(instance_node, AccessType::AccelerationStructureBuildRead) .resource_access(scratch_buf, AccessType::AccelerationStructureBufferWrite) .resource_access(tlas_node, AccessType::AccelerationStructureBuildWrite) - .record_accel_struct(move |accel_struct, _| { - accel_struct.build(&[BuildAccelerationStructureInfo::new( + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf.build_accel_struct(&[BuildAccelerationStructureInfo::new( tlas_node, - scratch_data, + scratch_addr, tlas_geometry_info, )]); }); @@ -418,8 +418,8 @@ fn main() -> anyhow::Result<()> { AccessType::RayTracingShaderReadAccelerationStructure, ) .shader_resource_access(1, frame.swapchain_image, AccessType::AnyShaderWrite) - .record_pipeline(move |pipeline, _| { - pipeline.trace_rays( + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf.trace_rays( &sbt_rgen, &sbt_miss, &sbt_hit, diff --git a/examples/shader-toy/src/main.rs b/examples/shader-toy/src/main.rs index e4126ae5..18559cab 100644 --- a/examples/shader-toy/src/main.rs +++ b/examples/shader-toy/src/main.rs @@ -44,6 +44,7 @@ use { graphic::{GraphicPipeline, GraphicPipelineInfo}, image::ImageInfo, shader::Shader, + AccessType, }, pool::{lazy::LazyPool, Pool as _}, Graph, @@ -167,9 +168,9 @@ fn main() -> anyhow::Result<()> { .clear_color_image(blank_image, [0.0, 0.0, 0.0, 1.0]) .clear_color_image(temp_image, [0.0, 1.0, 0.0, 1.0]); - let mut framebuffer_image_binding = Some(graph.node(framebuffer_image).clone()); - let mut blank_image_binding = Some(graph.node(blank_image).clone()); - let mut temp_image_binding = Some(graph.node(temp_image).clone()); + let mut framebuffer_image_binding = Some(graph.resource(framebuffer_image).clone()); + let mut blank_image_binding = Some(graph.resource(blank_image).clone()); + let mut temp_image_binding = Some(graph.resource(temp_image).clone()); graph.resolve().submit(&mut cache, 0, 0)?; @@ -289,13 +290,29 @@ fn main() -> anyhow::Result<()> { .begin_cmd() .debug_name("Buffer A") .bind_pipeline(&buffer_pipeline) - .read_descriptor(0, input) - .read_descriptor(1, noise_image) - .read_descriptor(2, flowers_image) - .read_descriptor(3, blank_image) + .shader_resource_access( + 0, + input, + AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, + ) + .shader_resource_access( + 1, + noise_image, + AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, + ) + .shader_resource_access( + 2, + flowers_image, + AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, + ) + .shader_resource_access( + 3, + blank_image, + AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, + ) .store_color(0, output) - .record_pipeline(move |pipeline, _| { - pipeline + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf .push_constants(0, bytes_of(&push_consts)) .draw(6, 1, 0, 0); }); @@ -306,10 +323,14 @@ fn main() -> anyhow::Result<()> { .begin_cmd() .debug_name("Image") .bind_pipeline(&image_pipeline) - .read_descriptor(0, output) + .shader_resource_access( + 0, + output, + AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, + ) .store_color(0, input) - .record_pipeline(move |pipeline, _| { - pipeline + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf .push_constants(0, bytes_of(&push_consts)) .draw(6, 1, 0, 0); }); @@ -318,11 +339,11 @@ fn main() -> anyhow::Result<()> { display.present_image(frame.graph, input, frame.swapchain_image); // Unbind things from this graph (we want them back for the next frame!) - flowers_image_binding = Some(frame.graph.node(flowers_image).clone()); - noise_image_binding = Some(frame.graph.node(noise_image).clone()); - framebuffer_image_binding = Some(frame.graph.node(framebuffer_image).clone()); - blank_image_binding = Some(frame.graph.node(blank_image).clone()); - temp_image_binding = Some(frame.graph.node(temp_image).clone()); + flowers_image_binding = Some(frame.graph.resource(flowers_image).clone()); + noise_image_binding = Some(frame.graph.resource(noise_image).clone()); + framebuffer_image_binding = Some(frame.graph.resource(framebuffer_image).clone()); + blank_image_binding = Some(frame.graph.resource(blank_image).clone()); + temp_image_binding = Some(frame.graph.resource(temp_image).clone()); }) .context("Unable to run event loop")?; diff --git a/examples/skeletal-anim/src/main.rs b/examples/skeletal-anim/src/main.rs index 0687f985..26d6e322 100644 --- a/examples/skeletal-anim/src/main.rs +++ b/examples/skeletal-anim/src/main.rs @@ -133,12 +133,16 @@ fn main() -> Result<(), WindowError> { .resource_access(vertex_buf, AccessType::VertexBuffer) .shader_resource_access(0, camera_buf, AccessType::VertexShaderReadUniformBuffer) .shader_resource_access(1, animation_buf, AccessType::VertexShaderReadOther) - .read_descriptor(2, texture) + .shader_resource_access( + 2, + texture, + AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, + ) .clear_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) .clear_depth_stencil(depth_image) - .record_pipeline(move |pipeline, _| { - pipeline + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf .bind_index_buffer(index_buf, 0, vk::IndexType::UINT16) .bind_vertex_buffer(0, vertex_buf, 0) .push_constants(0, bytes_of(&Mat4::IDENTITY)) diff --git a/examples/subgroup_ops.rs b/examples/subgroup_ops.rs index 8aed6242..9511712a 100644 --- a/examples/subgroup_ops.rs +++ b/examples/subgroup_ops.rs @@ -98,10 +98,10 @@ fn exclusive_sum( .begin_cmd() .debug_name("exclusive sum reduce") .bind_pipeline(reduce_pipeline) - .read_descriptor(0, input_buf) - .write_descriptor(1, workgroup_buf) - .record_pipeline(move |compute, _| { - compute.dispatch(reduce_count, 1, 1); + .shader_resource_access(0, input_buf, AccessType::ComputeShaderReadOther) + .shader_resource_access(1, workgroup_buf, AccessType::ComputeShaderWrite) + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf.dispatch(reduce_count, 1, 1); }); } @@ -109,11 +109,11 @@ fn exclusive_sum( .begin_cmd() .debug_name("exclusive sum scan") .bind_pipeline(scan_pipeline) - .read_descriptor(0, workgroup_buf) - .read_descriptor(1, input_buf) - .write_descriptor(2, output_buf) - .record_pipeline(move |compute, _| { - compute.dispatch(workgroup_count, 1, 1); + .shader_resource_access(0, workgroup_buf, AccessType::ComputeShaderReadOther) + .shader_resource_access(1, input_buf, AccessType::ComputeShaderReadOther) + .shader_resource_access(2, output_buf, AccessType::ComputeShaderWrite) + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf.dispatch(workgroup_count, 1, 1); }); let output_buf = graph.resource(output_buf).clone(); diff --git a/examples/triangle.rs b/examples/triangle.rs index ea0c4204..b16656be 100644 --- a/examples/triangle.rs +++ b/examples/triangle.rs @@ -91,8 +91,8 @@ fn main() -> Result<(), WindowError> { .resource_access(vertex_node, AccessType::VertexBuffer) .clear_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) - .record_pipeline(move |pipeline, _| { - pipeline + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf .bind_index_buffer(index_node, 0, vk::IndexType::UINT16) .bind_vertex_buffer(0, vertex_node, 0) .draw_indexed(3, 1, 0, 0, 0); diff --git a/examples/vertex_layout.rs b/examples/vertex_layout.rs index b2390817..4a96bb99 100644 --- a/examples/vertex_layout.rs +++ b/examples/vertex_layout.rs @@ -103,8 +103,8 @@ fn draw_triangle(frame: &mut FrameContext, pipeline: &GraphicPipeline, vertex_bu .load_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) .resource_access(vertex_buf, AccessType::VertexBuffer) - .record_pipeline(move |pipeline, _| { - pipeline + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf .bind_vertex_buffer(0, vertex_buf, 0) .draw(3, 1, 0, 0); }); diff --git a/examples/vr/src/main.rs b/examples/vr/src/main.rs index 73a5fecc..103f3281 100644 --- a/examples/vr/src/main.rs +++ b/examples/vr/src/main.rs @@ -380,8 +380,8 @@ fn main() -> anyhow::Result<()> { occlusion_texture, AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, ) - .record_pipeline(move |pipeline, _| { - pipeline + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf .bind_index_buffer(index_buf, 0, vk::IndexType::UINT32) .bind_vertex_buffer(0, vertex_buf, 0) .push_constants(0, bytes_of(&push_consts)) @@ -425,8 +425,8 @@ fn main() -> anyhow::Result<()> { occlusion_texture, AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, ) - .record_pipeline(move |pipeline, _| { - pipeline + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf .bind_index_buffer(index_buf, 0, vk::IndexType::UINT32) .bind_vertex_buffer(0, vertex_buf, 0) .push_constants(0, bytes_of(&push_consts)) @@ -463,8 +463,8 @@ fn main() -> anyhow::Result<()> { occlusion_texture, AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, ) - .record_pipeline(move |pipeline, _| { - pipeline + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf .bind_index_buffer(index_buf, 0, vk::IndexType::UINT32) .bind_vertex_buffer(0, vertex_buf, 0) .push_constants(0, bytes_of(&push_consts)) diff --git a/examples/vsm_omni.rs b/examples/vsm_omni.rs index e6a18d97..351c9beb 100644 --- a/examples/vsm_omni.rs +++ b/examples/vsm_omni.rs @@ -263,8 +263,8 @@ fn main() -> anyhow::Result<()> { .store_color(0, frame.swapchain_image) .clear_depth_stencil(depth_image) .store_depth_stencil(depth_image) - .record_pipeline(move |pipeline, _| { - pipeline + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf .bind_index_buffer(model_mesh_index_buf, 0, vk::IndexType::UINT32) .bind_vertex_buffer(0, model_mesh_vertex_buf, 0) .push_constants(0, cast_slice(&model_transform)) @@ -295,8 +295,8 @@ fn main() -> anyhow::Result<()> { .clear_color_value(0, shadow_faces_node, [light.range, light.range, 0.0, 0.0]) .store_color(0, shadow_faces_node) .clear_depth_stencil(shadow_depth_image) - .record_pipeline(move |pipeline, _| { - pipeline + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf .bind_index_buffer(model_shadow_index_buf, 0, vk::IndexType::UINT32) .bind_vertex_buffer(0, model_shadow_vertex_buf, 0) .push_constants(0, cast_slice(&model_transform)) @@ -349,8 +349,8 @@ fn main() -> anyhow::Result<()> { ) .store_color_as(0, shadow_faces_node, shadow_faces_view_info) .clear_depth_stencil(shadow_depth_image) - .record_pipeline(move |pipeline, _| { - pipeline + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf .bind_index_buffer(model_shadow_index_buf, 0, vk::IndexType::UINT32) .bind_vertex_buffer(0, model_shadow_vertex_buf, 0) .push_constants(0, cast_slice(&model_transform)) @@ -378,8 +378,8 @@ fn main() -> anyhow::Result<()> { AccessType::ComputeShaderReadOther, ) .shader_resource_access(1, temp_image, AccessType::ComputeShaderWrite) - .record_pipeline(move |compute, _| { - compute.dispatch(1, CUBEMAP_SIZE, 6); + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf.dispatch(1, CUBEMAP_SIZE, 6); }) .end_cmd() .begin_cmd() @@ -391,8 +391,8 @@ fn main() -> anyhow::Result<()> { shadow_faces_node, AccessType::ComputeShaderWrite, ) - .record_pipeline(move |compute, _| { - compute.dispatch(CUBEMAP_SIZE, 1, 6); + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf.dispatch(CUBEMAP_SIZE, 1, 6); }); } } @@ -429,12 +429,14 @@ fn main() -> anyhow::Result<()> { .clear_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) .clear_depth_stencil(depth_image) - .record_pipeline(move |pipeline, _| { - pipeline + .record_cmd_buf(move |cmd_buf, _| { + cmd_buf .bind_index_buffer(model_mesh_index_buf, 0, vk::IndexType::UINT32) .bind_vertex_buffer(0, model_mesh_vertex_buf, 0) .push_constants(0, cast_slice(&model_transform)) - .draw_indexed(model_mesh.index_count, 1, 0, 0, 0) + .draw_indexed(model_mesh.index_count, 1, 0, 0, 0); + + cmd_buf .bind_index_buffer(cube_mesh_index_buf, 0, vk::IndexType::UINT32) .bind_vertex_buffer(0, cube_mesh_vertex_buf, 0) .push_constants(0, cast_slice(&cube_transform)) diff --git a/src/cmd_ref/accel_struct.rs b/src/cmd_ref/accel_struct.rs index 0979a684..d692b9f7 100644 --- a/src/cmd_ref/accel_struct.rs +++ b/src/cmd_ref/accel_struct.rs @@ -1,5 +1,5 @@ use { - super::Nodes, + super::Resources, crate::{ AnyAccelerationStructureNode, driver::{ @@ -12,7 +12,7 @@ use { }, }, ash::vk, - std::cell::RefCell, + std::{cell::RefCell, ops::Deref}, }; /// Recording interface for acceleration structure commands. @@ -45,7 +45,7 @@ use { /// ``` pub struct AccelerationStructureRef<'a> { pub(super) cmd_buf: &'a CommandBuffer, - pub(super) nodes: Nodes<'a>, + pub(super) resources: Resources<'a>, } impl AccelerationStructureRef<'_> { @@ -71,7 +71,7 @@ impl AccelerationStructureRef<'_> { /// ```no_run /// # use ash::vk; /// # use vk_graph::cmd_ref::BuildAccelerationStructureInfo; - /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::{AccessType, DriverError}; /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureGeometry, AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, AccelerationStructureInfo, DeviceOrHostAddress}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; @@ -90,27 +90,27 @@ impl AccelerationStructureRef<'_> { /// # let my_idx_buf = Buffer::create(&device, buf_info)?; /// # let buf_info = BufferInfo::device_mem(8, vk::BufferUsageFlags::VERTEX_BUFFER); /// # let my_vtx_buf = Buffer::create(&device, buf_info)?; - /// # let index_node = my_graph.bind_resource(my_idx_buf); - /// # let vertex_node = my_graph.bind_resource(my_vtx_buf); + /// # let index_buf = my_graph.bind_resource(my_idx_buf); + /// # let vertex_buf = my_graph.bind_resource(my_vtx_buf); /// my_graph.begin_cmd() - /// .read_node(index_node) - /// .read_node(vertex_node) - /// .write_node(blas_node) - /// .write_node(scratch_buf) - /// .record_accel_struct(move |accel_struct, nodes| { - /// let scratch_addr = nodes[scratch_buf].device_address(); + /// .resource_access(index_buf, AccessType::IndexBuffer) + /// .resource_access(vertex_buf, AccessType::VertexBuffer) + /// .resource_access(scratch_buf, AccessType::AccelerationStructureBufferWrite) + /// .resource_access(blas_node, AccessType::AccelerationStructureBuildWrite) + /// .record_cmd_buf(move |cmd_buf, resources| { + /// let scratch_addr = resources[scratch_buf].device_address(); /// let geom = AccelerationStructureGeometry { /// max_primitive_count: 64, /// flags: vk::GeometryFlagsKHR::OPAQUE, /// geometry: AccelerationStructureGeometryData::Triangles { /// index_addr: DeviceOrHostAddress::DeviceAddress( - /// nodes[index_node].device_address() + /// resources[index_buf].device_address() /// ), /// index_type: vk::IndexType::UINT32, /// max_vertex: 42, /// transform_addr: None, /// vertex_addr: DeviceOrHostAddress::DeviceAddress( - /// nodes[vertex_node].device_address(), + /// resources[vertex_buf].device_address(), /// ), /// vertex_format: vk::Format::R32G32B32_SFLOAT, /// vertex_stride: 12, @@ -124,13 +124,13 @@ impl AccelerationStructureRef<'_> { /// }; /// let info = AccelerationStructureGeometryInfo::blas([(geom, build_range)]); /// - /// accel_struct.build(&[ + /// cmd_buf.build_accel_struct(&[ /// BuildAccelerationStructureInfo::new(blas_node, scratch_addr, info) /// ]); /// }); /// # Ok(()) } /// ``` - pub fn build(&self, infos: &[BuildAccelerationStructureInfo]) -> &Self { + pub fn build_accel_struct(&self, infos: &[BuildAccelerationStructureInfo]) -> &Self { #[derive(Default)] struct Tls { geometries: Vec>, @@ -180,7 +180,7 @@ impl AccelerationStructureRef<'_> { .ty(info.build_data.ty) .flags(info.build_data.flags) .mode(vk::BuildAccelerationStructureModeKHR::BUILD) - .dst_acceleration_structure(self.nodes[info.accel_struct].handle) + .dst_acceleration_structure(self.resources[info.accel_struct].handle) .geometries(&tls.geometries[start..end]) .scratch_data(info.scratch_addr.into()), ); @@ -207,7 +207,10 @@ impl AccelerationStructureRef<'_> { /// `range` is a buffer device address which points to `info.geometry.len()` /// [vk::VkAccelerationStructureBuildRangeInfoKHR] structures defining dynamic offsets to the /// addresses where geometry data is stored, as defined by `info`. - pub fn build_indirect(&self, infos: &[BuildAccelerationStructureIndirectInfo]) -> &Self { + pub fn build_accel_struct_indirect( + &self, + infos: &[BuildAccelerationStructureIndirectInfo], + ) -> &Self { #[derive(Default)] struct Tls { geometries: Vec>, @@ -252,7 +255,7 @@ impl AccelerationStructureRef<'_> { .ty(info.build_data.ty) .flags(info.build_data.flags) .mode(vk::BuildAccelerationStructureModeKHR::BUILD) - .dst_acceleration_structure(self.nodes[info.accel_struct].handle) + .dst_acceleration_structure(self.resources[info.accel_struct].handle) .geometries(&tls.geometries[start..end]) .scratch_data(info.scratch_data.into()), ); @@ -293,7 +296,7 @@ impl AccelerationStructureRef<'_> { /// [`AccelerationStructure::size_of`] aligned to `min_accel_struct_scratch_offset_alignment` /// of /// [`PhysicalDevice::accel_struct_properties`](crate::driver::physical_device::PhysicalDevice::accel_struct_properties). - pub fn update(&self, infos: &[UpdateAccelerationStructureInfo]) -> &Self { + pub fn update_accel_struct(&self, infos: &[UpdateAccelerationStructureInfo]) -> &Self { #[derive(Default)] struct Tls { geometries: Vec>, @@ -343,8 +346,12 @@ impl AccelerationStructureRef<'_> { .ty(info.update_data.ty) .flags(info.update_data.flags) .mode(vk::BuildAccelerationStructureModeKHR::UPDATE) - .dst_acceleration_structure(self.nodes[info.dst_accel_struct].handle) - .src_acceleration_structure(self.nodes[info.src_accel_struct].handle) + .dst_acceleration_structure( + self.resources[info.dst_accel_struct].handle, + ) + .src_acceleration_structure( + self.resources[info.src_accel_struct].handle, + ) .geometries(&tls.geometries[start..end]) .scratch_data(info.scratch_addr.into()), ); @@ -371,7 +378,10 @@ impl AccelerationStructureRef<'_> { /// `range` is a buffer device address which points to `info.geometry.len()` /// [vk::VkAccelerationStructureBuildRangeInfoKHR] structures defining dynamic offsets to the /// addresses where geometry data is stored, as defined by `info`. - pub fn update_indirect(&self, infos: &[UpdateAccelerationStructureIndirectInfo]) -> &Self { + pub fn update_accel_struct_indirect( + &self, + infos: &[UpdateAccelerationStructureIndirectInfo], + ) -> &Self { #[derive(Default)] struct Tls { geometries: Vec>, @@ -416,8 +426,12 @@ impl AccelerationStructureRef<'_> { .ty(info.update_data.ty) .flags(info.update_data.flags) .mode(vk::BuildAccelerationStructureModeKHR::UPDATE) - .src_acceleration_structure(self.nodes[info.src_accel_struct].handle) - .dst_acceleration_structure(self.nodes[info.dst_accel_struct].handle) + .src_acceleration_structure( + self.resources[info.src_accel_struct].handle, + ) + .dst_acceleration_structure( + self.resources[info.dst_accel_struct].handle, + ) .geometries(&tls.geometries[start..end]) .scratch_data(info.scratch_addr.into()), ); @@ -447,6 +461,14 @@ impl AccelerationStructureRef<'_> { } } +impl<'a> Deref for AccelerationStructureRef<'a> { + type Target = CommandBuffer; + + fn deref(&self) -> &Self::Target { + self.cmd_buf + } +} + /// Specifies the information and data used to build an acceleration structure. /// /// See diff --git a/src/cmd_ref/compute.rs b/src/cmd_ref/compute.rs index 371640e0..e3dad44c 100644 --- a/src/cmd_ref/compute.rs +++ b/src/cmd_ref/compute.rs @@ -1,11 +1,12 @@ use { - super::{Nodes, pipeline::PipelineRef}, + super::{Resources, pipeline::PipelineRef}, crate::{ AnyBufferNode, driver::{CommandBuffer, compute::ComputePipeline}, }, ash::vk, log::trace, + std::ops::Deref, }; /// Recording interface for computing commands. @@ -32,17 +33,18 @@ use { /// # let shader = Shader::new_compute([0u8; 1].as_slice()); /// # let my_compute_pipeline = ComputePipeline::create(&device, info, shader)?; /// # let mut my_graph = Graph::default(); -/// my_graph.begin_cmd() -/// .bind_pipeline(&my_compute_pipeline) -/// .record_pipeline(move |pipeline, nodes| { -/// // During this closure we have access to the compute dispatch methods! -/// }); +/// my_graph +/// .begin_cmd() +/// .bind_pipeline(&my_compute_pipeline) +/// .record_cmd_buf(move |cmd_buf, resources| { +/// // During this closure we have access to the compute dispatch methods! +/// }); /// # Ok(()) } /// ``` pub struct ComputePipelineRef<'a> { pub(super) cmd_buf: &'a CommandBuffer, - pub(super) nodes: Nodes<'a>, pub(super) pipeline: ComputePipeline, + pub(super) resources: Resources<'a>, } impl ComputePipelineRef<'_> { @@ -72,7 +74,7 @@ impl ComputePipelineRef<'_> { /// /// ```no_run /// # use ash::vk; - /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::{AccessType, DriverError}; /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; @@ -87,12 +89,14 @@ impl ComputePipelineRef<'_> { /// # let my_compute_pipeline = ComputePipeline::create(&device, info, shader)?; /// # let mut my_graph = Graph::default(); /// # let my_buf_node = my_graph.bind_resource(my_buf); - /// my_graph.begin_cmd().debug_name("fill my_buf_node with data") - /// .bind_pipeline(&my_compute_pipeline) - /// .write_descriptor(0, my_buf_node) - /// .record_pipeline(move |compute, nodes| { - /// compute.dispatch(128, 64, 32); - /// }); + /// my_graph + /// .begin_cmd() + /// .debug_name("fill my_buf_node with data") + /// .bind_pipeline(&my_compute_pipeline) + /// .shader_resource_access(0, my_buf_node, AccessType::ComputeShaderWrite) + /// .record_cmd_buf(move |cmd_buf, resources| { + /// cmd_buf.dispatch(128, 64, 32); + /// }); /// # Ok(()) } /// ``` /// @@ -159,9 +163,9 @@ impl ComputePipelineRef<'_> { /// Basic usage: /// /// ```no_run - /// # use std::mem::size_of; /// # use ash::vk; - /// # use vk_graph::driver::DriverError; + /// # use bytemuck::{bytes_of, Pod, Zeroable}; + /// # use vk_graph::driver::{AccessType, DriverError}; /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; @@ -176,28 +180,28 @@ impl ComputePipelineRef<'_> { /// # let my_compute_pipeline = ComputePipeline::create(&device, info, shader)?; /// # let mut my_graph = Graph::default(); /// # let my_buf_node = my_graph.bind_resource(my_buf); - /// const CMD_SIZE: usize = size_of::(); - /// - /// let cmd = vk::DispatchIndirectCommand { + /// # #[repr(C)] + /// # #[derive(Clone, Copy, Pod, Zeroable)] + /// # struct DispatchIndirectCommand { x: u32, y: u32, z: u32, } + /// let args = DispatchIndirectCommand { /// x: 1, /// y: 2, /// z: 3, /// }; - /// let cmd_data = unsafe { - /// std::slice::from_raw_parts(&cmd as *const _ as *const _, CMD_SIZE) - /// }; + /// let data = bytes_of(&args); + /// let usage = vk::BufferUsageFlags::INDIRECT_BUFFER | vk::BufferUsageFlags::STORAGE_BUFFER; + /// let args_buf = Buffer::create_from_slice(&device, usage, data)?; + /// let args_buf = my_graph.bind_resource(args_buf); /// - /// let args_buf_flags = vk::BufferUsageFlags::STORAGE_BUFFER; - /// let args_buf = Buffer::create_from_slice(&device, args_buf_flags, cmd_data)?; - /// let args_buf_node = my_graph.bind_resource(args_buf); - /// - /// my_graph.begin_cmd().debug_name("fill my_buf_node with data") - /// .bind_pipeline(&my_compute_pipeline) - /// .read_node(args_buf_node) - /// .write_descriptor(0, my_buf_node) - /// .record_pipeline(move |compute, nodes| { - /// compute.dispatch_indirect(args_buf_node, 0); - /// }); + /// my_graph + /// .begin_cmd() + /// .debug_name("fill my_buf_node with data") + /// .bind_pipeline(&my_compute_pipeline) + /// .resource_access(args_buf, AccessType::IndirectBuffer) + /// .shader_resource_access(0, my_buf_node, AccessType::ComputeShaderWrite) + /// .record_cmd_buf(move |cmd_buf, resources| { + /// cmd_buf.dispatch_indirect(args_buf, 0); + /// }); /// # Ok(()) } /// ``` /// @@ -214,7 +218,7 @@ impl ComputePipelineRef<'_> { unsafe { self.cmd_buf.device.cmd_dispatch_indirect( self.cmd_buf.handle, - self.nodes[args_buf].handle, + self.resources[args_buf].handle, args_offset, ); } @@ -272,12 +276,15 @@ impl ComputePipelineRef<'_> { /// # let shader = Shader::new_compute([0u8; 1].as_slice()); /// # let my_compute_pipeline = ComputePipeline::create(&device, info, shader)?; /// # let mut my_graph = Graph::default(); - /// my_graph.begin_cmd().debug_name("compute the ultimate question") - /// .bind_pipeline(&my_compute_pipeline) - /// .record_pipeline(move |compute, nodes| { - /// compute.push_constants(0, &[42]) - /// .dispatch(1, 1, 1); - /// }); + /// my_graph + /// .begin_cmd() + /// .debug_name("compute the ultimate question") + /// .bind_pipeline(&my_compute_pipeline) + /// .record_cmd_buf(move |cmd_buf, resources| { + /// cmd_buf + /// .push_constants(0, &[42]) + /// .dispatch(1, 1, 1); + /// }); /// # Ok(()) } /// ``` /// @@ -313,12 +320,20 @@ impl ComputePipelineRef<'_> { } } +impl<'a> Deref for ComputePipelineRef<'a> { + type Target = CommandBuffer; + + fn deref(&self) -> &Self::Target { + self.cmd_buf + } +} + // NOTE: local implementation of type from super module impl PipelineRef<'_, ComputePipeline> { /// Begin recording a compute pipeline command buffer. - pub fn record_pipeline( + pub fn record_cmd_buf( mut self, - func: impl FnOnce(ComputePipelineRef<'_>, Nodes<'_>) + Send + 'static, + func: impl FnOnce(ComputePipelineRef<'_>, Resources<'_>) + Send + 'static, ) -> Self { let pipeline = self .cmd @@ -332,14 +347,14 @@ impl PipelineRef<'_, ComputePipeline> { .unwrap_compute() .clone(); - self.cmd.push_execute(move |cmd_buf, nodes| { + self.cmd.push_execute(move |cmd_buf, resources| { func( ComputePipelineRef { - nodes, cmd_buf, pipeline, + resources, }, - nodes, + resources, ); }); @@ -347,10 +362,14 @@ impl PipelineRef<'_, ComputePipeline> { } } +#[allow(unused)] mod deprecated { use { crate::{ - cmd_ref::{Descriptor, PipelineRef, SubresourceRange, View, ViewInfo}, + cmd_ref::{ + Descriptor, PipelineRef, Resources, SubresourceRange, View, ViewInfo, + compute::ComputePipelineRef, + }, driver::compute::ComputePipeline, node::Node, }, @@ -392,6 +411,15 @@ mod deprecated { ) } + #[deprecated = "use record_cmd_buf function"] + #[doc(hidden)] + pub fn record_compute( + self, + func: impl FnOnce(ComputePipelineRef<'_>, Resources<'_>) + Send + 'static, + ) -> Self { + self.record_cmd_buf(func) + } + #[deprecated = "use shader_resource_access function with AccessType::ComputeShaderWrite"] #[doc(hidden)] pub fn write_descriptor(self, descriptor: impl Into, node: N) -> Self diff --git a/src/cmd_ref/graphic.rs b/src/cmd_ref/graphic.rs index b02a2839..2787603a 100644 --- a/src/cmd_ref/graphic.rs +++ b/src/cmd_ref/graphic.rs @@ -1,5 +1,5 @@ use { - super::{AttachmentIndex, Nodes, PipelineRef, SubresourceAccess, SubresourceRange}, + super::{AttachmentIndex, PipelineRef, Resources, SubresourceAccess, SubresourceRange}, crate::{ AnyBufferNode, AnyImageNode, Area, Attachment, ClearColorValue, Node, driver::{ @@ -48,18 +48,20 @@ use { /// # let mut my_graph = Graph::default(); /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); /// # let swapchain_image = my_graph.bind_resource(Image::create(&device, info)?); -/// my_graph.begin_cmd().debug_name("my draw pass") -/// .bind_pipeline(&my_graphic_pipeline) -/// .store_color(0, swapchain_image) -/// .record_pipeline(move |graphic, nodes| { -/// // During this closure we have access to the draw methods! -/// }); +/// my_graph +/// .begin_cmd() +/// .debug_name("my draw pass") +/// .bind_pipeline(&my_graphic_pipeline) +/// .store_color(0, swapchain_image) +/// .record_cmd_buf(move |cmd_buf, resources| { +/// // During this closure we have access to the draw methods! +/// }); /// # Ok(()) } /// ``` pub struct GraphicPipelineRef<'a> { pub(super) cmd_buf: &'a CommandBuffer, - pub(super) nodes: Nodes<'a>, pub(super) pipeline: GraphicPipeline, + pub(super) resources: Resources<'a>, } impl GraphicPipelineRef<'_> { @@ -74,7 +76,7 @@ impl GraphicPipelineRef<'_> { /// /// ```no_run /// # use ash::vk; - /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::{AccessType, DriverError}; /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; @@ -98,16 +100,19 @@ impl GraphicPipelineRef<'_> { /// # let my_vtx_buf = Buffer::create(&device, buf_info)?; /// # let my_idx_buf = my_graph.bind_resource(my_idx_buf); /// # let my_vtx_buf = my_graph.bind_resource(my_vtx_buf); - /// my_graph.begin_cmd().debug_name("my indexed geometry draw pass") - /// .bind_pipeline(&my_graphic_pipeline) - /// .store_color(0, swapchain_image) - /// .read_node(my_idx_buf) - /// .read_node(my_vtx_buf) - /// .record_pipeline(move |pipeline, nodes| { - /// pipeline.bind_index_buffer(my_idx_buf, 0, vk::IndexType::UINT16) - /// .bind_vertex_buffer(0, my_vtx_buf, 0) - /// .draw_indexed(42, 1, 0, 0, 0); - /// }); + /// my_graph + /// .begin_cmd() + /// .debug_name("my indexed geometry draw pass") + /// .bind_pipeline(&my_graphic_pipeline) + /// .store_color(0, swapchain_image) + /// .resource_access(my_idx_buf, AccessType::IndexBuffer) + /// .resource_access(my_vtx_buf, AccessType::VertexBuffer) + /// .record_cmd_buf(move |cmd_buf, resources| { + /// cmd_buf + /// .bind_index_buffer(my_idx_buf, 0, vk::IndexType::UINT16) + /// .bind_vertex_buffer(0, my_vtx_buf, 0) + /// .draw_indexed(42, 1, 0, 0, 0); + /// }); /// # Ok(()) } /// ``` #[profiling::function] @@ -122,7 +127,7 @@ impl GraphicPipelineRef<'_> { unsafe { self.cmd_buf.device.cmd_bind_index_buffer( self.cmd_buf.handle, - self.nodes[buffer].handle, + self.resources[buffer].handle, offset, index_ty, ); @@ -141,7 +146,7 @@ impl GraphicPipelineRef<'_> { /// /// ```no_run /// # use ash::vk; - /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::{AccessType, DriverError}; /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; @@ -162,14 +167,17 @@ impl GraphicPipelineRef<'_> { /// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); /// # let swapchain_image = my_graph.bind_resource(Image::create(&device, info)?); /// # let my_vtx_buf = my_graph.bind_resource(my_vtx_buf); - /// my_graph.begin_cmd().debug_name("my unindexed geometry draw pass") - /// .bind_pipeline(&my_graphic_pipeline) - /// .store_color(0, swapchain_image) - /// .read_node(my_vtx_buf) - /// .record_pipeline(move |pipeline, nodes| { - /// pipeline.bind_vertex_buffer(0, my_vtx_buf, 0) - /// .draw(42, 1, 0, 0); - /// }); + /// my_graph + /// .begin_cmd() + /// .debug_name("my unindexed geometry draw pass") + /// .bind_pipeline(&my_graphic_pipeline) + /// .store_color(0, swapchain_image) + /// .resource_access(my_vtx_buf, AccessType::VertexBuffer) + /// .record_cmd_buf(move |cmd_buf, resources| { + /// cmd_buf + /// .bind_vertex_buffer(0, my_vtx_buf, 0) + /// .draw(42, 1, 0, 0); + /// }); /// # Ok(()) } /// ``` #[profiling::function] @@ -185,7 +193,7 @@ impl GraphicPipelineRef<'_> { self.cmd_buf.device.cmd_bind_vertex_buffers( self.cmd_buf.handle, binding, - slice::from_ref(&self.nodes[buffer].handle), + slice::from_ref(&self.resources[buffer].handle), slice::from_ref(&offset), ); } @@ -220,7 +228,7 @@ impl GraphicPipelineRef<'_> { for (buffer, offset) in buffer_offsets { let buffer = buffer.into(); - buffers.push(self.nodes[buffer].handle); + buffers.push(self.resources[buffer].handle); offsets.push(offset); } @@ -310,7 +318,7 @@ impl GraphicPipelineRef<'_> { /// ```no_run /// # use std::mem::size_of; /// # use ash::vk; - /// # use vk_graph::driver::DriverError; + /// # use vk_graph::driver::{AccessType, DriverError}; /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; @@ -351,17 +359,20 @@ impl GraphicPipelineRef<'_> { /// let buf = Buffer::create_from_slice(&device, buf_flags, cmd_data)?; /// let buf_node = my_graph.bind_resource(buf); /// - /// my_graph.begin_cmd().debug_name("draw a single triangle") - /// .bind_pipeline(&my_graphic_pipeline) - /// .store_color(0, swapchain_image) - /// .read_node(my_idx_buf) - /// .read_node(my_vtx_buf) - /// .read_node(buf_node) - /// .record_pipeline(move |pipeline, nodes| { - /// pipeline.bind_index_buffer(my_idx_buf, 0, vk::IndexType::UINT16) - /// .bind_vertex_buffer(0, my_vtx_buf, 0) - /// .draw_indexed_indirect(buf_node, 0, 1, 0); - /// }); + /// my_graph + /// .begin_cmd() + /// .debug_name("draw a single triangle") + /// .bind_pipeline(&my_graphic_pipeline) + /// .store_color(0, swapchain_image) + /// .resource_access(my_idx_buf, AccessType::IndexBuffer) + /// .resource_access(my_vtx_buf, AccessType::VertexBuffer) + /// .resource_access(buf_node, AccessType::IndirectBuffer) + /// .record_cmd_buf(move |cmd_buf, resources| { + /// cmd_buf + /// .bind_index_buffer(my_idx_buf, 0, vk::IndexType::UINT16) + /// .bind_vertex_buffer(0, my_vtx_buf, 0) + /// .draw_indexed_indirect(buf_node, 0, 1, 0); + /// }); /// # Ok(()) } /// ``` #[profiling::function] @@ -377,7 +388,7 @@ impl GraphicPipelineRef<'_> { unsafe { self.cmd_buf.device.cmd_draw_indexed_indirect( self.cmd_buf.handle, - self.nodes[buffer].handle, + self.resources[buffer].handle, offset, draw_count, stride, @@ -415,9 +426,9 @@ impl GraphicPipelineRef<'_> { unsafe { self.cmd_buf.device.cmd_draw_indexed_indirect_count( self.cmd_buf.handle, - self.nodes[buffer].handle, + self.resources[buffer].handle, offset, - self.nodes[count_buf].handle, + self.resources[count_buf].handle, count_buf_offset, max_draw_count, stride, @@ -443,7 +454,7 @@ impl GraphicPipelineRef<'_> { unsafe { self.cmd_buf.device.cmd_draw_indirect( self.cmd_buf.handle, - self.nodes[buffer].handle, + self.resources[buffer].handle, offset, draw_count, stride, @@ -472,9 +483,9 @@ impl GraphicPipelineRef<'_> { unsafe { self.cmd_buf.device.cmd_draw_indirect_count( self.cmd_buf.handle, - self.nodes[buffer].handle, + self.resources[buffer].handle, offset, - self.nodes[count_buf].handle, + self.resources[count_buf].handle, count_buf_offset, max_draw_count, stride, @@ -539,13 +550,16 @@ impl GraphicPipelineRef<'_> { /// # let swapchain_image = Image::create(&device, info)?; /// # let mut my_graph = Graph::default(); /// # let swapchain_image = my_graph.bind_resource(swapchain_image); - /// my_graph.begin_cmd().debug_name("draw a quad") - /// .bind_pipeline(&my_graphic_pipeline) - /// .store_color(0, swapchain_image) - /// .record_pipeline(move |pipeline, nodes| { - /// pipeline.push_constants(0, &[42]) - /// .draw(6, 1, 0, 0); - /// }); + /// my_graph + /// .begin_cmd() + /// .debug_name("draw a quad") + /// .bind_pipeline(&my_graphic_pipeline) + /// .store_color(0, swapchain_image) + /// .record_cmd_buf(move |cmd_buf, resources| { + /// cmd_buf + /// .push_constants(0, &[42]) + /// .draw(6, 1, 0, 0); + /// }); /// # Ok(()) } /// ``` /// @@ -1456,9 +1470,9 @@ impl PipelineRef<'_, GraphicPipeline> { } /// Begin recording a graphics pipeline command buffer. - pub fn record_pipeline( + pub fn record_cmd_buf( mut self, - func: impl FnOnce(GraphicPipelineRef<'_>, Nodes<'_>) + Send + 'static, + func: impl FnOnce(GraphicPipelineRef<'_>, Resources<'_>) + Send + 'static, ) -> Self { let pipeline = self .cmd @@ -1472,14 +1486,14 @@ impl PipelineRef<'_, GraphicPipeline> { .unwrap_graphic() .clone(); - self.cmd.push_execute(move |cmd_buf, nodes| { + self.cmd.push_execute(move |cmd_buf, resources| { func( GraphicPipelineRef { - nodes, cmd_buf, pipeline, + resources, }, - nodes, + resources, ); }); @@ -2137,10 +2151,14 @@ impl PipelineRef<'_, GraphicPipeline> { } } +#[allow(unused)] mod deprecated { use { crate::{ - cmd_ref::{Descriptor, PipelineRef, SubresourceRange, View, ViewInfo}, + cmd_ref::{ + Descriptor, PipelineRef, Resources, SubresourceRange, View, ViewInfo, + graphic::GraphicPipelineRef, + }, driver::graphic::GraphicPipeline, node::Node, }, @@ -2186,6 +2204,15 @@ mod deprecated { ) } + #[deprecated = "use record_cmd_buf function"] + #[doc(hidden)] + pub fn record_subpass( + self, + func: impl FnOnce(GraphicPipelineRef<'_>, Resources<'_>) + Send + 'static, + ) -> Self { + self.record_cmd_buf(func) + } + #[deprecated = "use shader_resource_access function with AccessType::AnyShaderWrite"] #[doc(hidden)] pub fn write_descriptor(self, descriptor: impl Into, node: N) -> Self diff --git a/src/cmd_ref/mod.rs b/src/cmd_ref/mod.rs index 04c4bd0e..350f8695 100644 --- a/src/cmd_ref/mod.rs +++ b/src/cmd_ref/mod.rs @@ -115,7 +115,7 @@ impl<'a> CommandRef<'a> { self.graph } - fn push_execute(&mut self, func: impl FnOnce(&CommandBuffer, Nodes<'_>) + Send + 'static) { + fn push_execute(&mut self, func: impl FnOnce(&CommandBuffer, Resources<'_>) + Send + 'static) { let cmd = self.cmd_mut(); let exec = { let last_exec = cmd.execs.last_mut().unwrap(); @@ -158,26 +158,16 @@ impl<'a> CommandRef<'a> { /// Begin recording an acceleration structure command buffer. /// /// This is the entry point for building and updating an [`AccelerationStructure`] instance. - pub fn record_accel_struct( - mut self, - func: impl FnOnce(AccelerationStructureRef<'_>, Nodes<'_>) + Send + 'static, - ) -> Self { - self.push_execute(move |cmd_buf, nodes| { - func(AccelerationStructureRef { nodes, cmd_buf }, nodes); - }); - - self - } - - /// Begin recording a general command buffer. /// /// The provided closure allows you to run any Vulkan code, or interoperate with other Vulkan /// code and interfaces. pub fn record_cmd_buf( mut self, - func: impl FnOnce(&CommandBuffer, Nodes<'_>) + Send + 'static, + func: impl FnOnce(AccelerationStructureRef<'_>, Resources<'_>) + Send + 'static, ) -> Self { - self.push_execute(func); + self.push_execute(move |cmd_buf, resources| { + func(AccelerationStructureRef { cmd_buf, resources }, resources); + }); self } @@ -198,7 +188,7 @@ impl<'a> CommandRef<'a> { pub fn resource_access(mut self, node: N, access: AccessType) -> Self where N: Node + View, - SubresourceRange: From<::Range>, + SubresourceRange: From, { self.set_resource_access(node, access); self @@ -221,7 +211,7 @@ impl<'a> CommandRef<'a> { pub fn set_resource_access(&mut self, node: N, access: AccessType) where N: Node + View, - SubresourceRange: From<::Range>, + SubresourceRange: From, { let subresource = node.default_range(&self.graph.resources).into(); self.push_node_access(node, access, subresource); @@ -238,7 +228,7 @@ impl<'a> CommandRef<'a> { access: AccessType, ) where N: Node + View, - SubresourceRange: From<::Range>, + SubresourceRange: From, { let subresource = subresource.into().into(); self.push_node_access(node, access, subresource); @@ -256,7 +246,7 @@ impl<'a> CommandRef<'a> { ) -> Self where N: Node + View, - SubresourceRange: From<::Range>, + SubresourceRange: From, { self.set_subresource_access(node, subresource, access); self @@ -357,32 +347,34 @@ impl From<(DescriptorSetIndex, BindingIndex, [BindingOffset; 1])> for Descriptor /// # let image = Image::create(&device, info)?; /// # let mut my_graph = Graph::default(); /// # let my_image_node = my_graph.bind_resource(image); -/// my_graph.begin_cmd().debug_name("custom vulkan commands") -/// .record_cmd_buf(move |device, cmd_buf, nodes| { -/// let my_image: &Image = &nodes[my_image_node]; +/// my_graph +/// .begin_cmd() +/// .debug_name("custom vulkan commands") +/// .record_cmd_buf(move |cmd_buf, resources| { +/// let my_image: &Image = &resources[my_image_node]; /// -/// assert_ne!(my_image.handle, vk::Image::null()); -/// assert_eq!(my_image.info.width, 32); -/// }); +/// assert_ne!(my_image.handle, vk::Image::null()); +/// assert_eq!(my_image.info.width, 32); +/// }); /// # Ok(()) } /// ``` #[derive(Clone, Copy, Debug)] -pub struct Nodes<'a> { +pub struct Resources<'a> { #[cfg(debug_assertions)] exec: &'a Execution, resources: &'a [Resource], } -impl<'a> Nodes<'a> { +impl<'a> Resources<'a> { pub(super) fn new( resources: &'a [Resource], #[cfg(debug_assertions)] exec: &'a Execution, ) -> Self { Self { - resources, #[cfg(debug_assertions)] exec, + resources, } } @@ -406,7 +398,7 @@ impl<'a> Nodes<'a> { macro_rules! index { ($name:ident, $handle:ident) => { paste::paste! { - impl<'a> Index<[<$name Node>]> for Nodes<'a> + impl<'a> Index<[<$name Node>]> for Resources<'a> { type Output = $handle; @@ -428,7 +420,7 @@ index!(Image, Image); index!(ImageLease, Image); index!(SwapchainImage, Image); -impl Index for Nodes<'_> { +impl Index for Resources<'_> { type Output = AccelerationStructure; fn index(&self, node: AnyAccelerationStructureNode) -> &Self::Output { @@ -439,7 +431,7 @@ impl Index for Nodes<'_> { } } -impl Index for Nodes<'_> { +impl Index for Resources<'_> { type Output = Buffer; fn index(&self, node: AnyBufferNode) -> &Self::Output { @@ -450,7 +442,7 @@ impl Index for Nodes<'_> { } } -impl Index for Nodes<'_> { +impl Index for Resources<'_> { type Output = Image; fn index(&self, node: AnyImageNode) -> &Self::Output { @@ -674,10 +666,11 @@ impl From> for ViewInfo { } } +#[allow(unused)] mod deprecated { use { crate::{ - cmd_ref::{CommandRef, SubresourceRange, View}, + cmd_ref::{AccelerationStructureRef, CommandRef, Resources, SubresourceRange, View}, deprecated::Info, node::Node, }, @@ -691,12 +684,37 @@ mod deprecated { pub fn access_node_mut(&mut self, node: N, access: AccessType) -> &mut Self where N: Node + View, - SubresourceRange: From<::Range>, + SubresourceRange: From, { self.set_resource_access(node, access); self } + #[deprecated = "use resource_access function"] + #[doc(hidden)] + pub fn access_resource(mut self, node: N, access: AccessType) -> Self + where + N: Node + View, + SubresourceRange: From, + { + self.resource_access(node, access) + } + + #[deprecated = "use subresource_access function"] + #[doc(hidden)] + pub fn access_subresource( + mut self, + node: N, + subresource: impl Into, + access: AccessType, + ) -> Self + where + N: Node + View, + SubresourceRange: From, + { + self.subresource_access(node, subresource, access) + } + #[deprecated = "use device_address function of resource function result"] #[doc(hidden)] pub fn node_device_address(&self, node: impl Node) -> vk::DeviceAddress { @@ -716,5 +734,18 @@ mod deprecated { { node.info(&self.graph.resources) } + + #[deprecated = "use record_cmd_buf function"] + #[doc(hidden)] + pub fn record_accel_struct( + mut self, + func: impl FnOnce(AccelerationStructureRef<'_>, Resources<'_>) + Send + 'static, + ) -> Self { + self.push_execute(move |cmd_buf, resources| { + func(AccelerationStructureRef { cmd_buf, resources }, resources); + }); + + self + } } } diff --git a/src/cmd_ref/pipeline.rs b/src/cmd_ref/pipeline.rs index 1f4a759a..9c5eb664 100644 --- a/src/cmd_ref/pipeline.rs +++ b/src/cmd_ref/pipeline.rs @@ -46,7 +46,7 @@ impl<'a, T> PipelineRef<'a, T> { pub fn resource_access(mut self, node: N, access: AccessType) -> Self where N: Node + View, - SubresourceRange: From<::Range>, + SubresourceRange: From, { self.cmd.set_resource_access(node, access); self @@ -59,7 +59,7 @@ impl<'a, T> PipelineRef<'a, T> { pub fn set_resource_access(&mut self, node: N, access: AccessType) -> &mut Self where N: Node + View, - SubresourceRange: From<::Range>, + SubresourceRange: From, { self.cmd.set_resource_access(node, access); self @@ -138,7 +138,7 @@ impl<'a, T> PipelineRef<'a, T> { ) -> &mut Self where N: Node + View, - SubresourceRange: From<::Range>, + SubresourceRange: From, { self.cmd.set_subresource_access(node, subresource, access); self @@ -198,20 +198,51 @@ impl<'a, T> PipelineRef<'a, T> { ) -> Self where N: Node + View, - SubresourceRange: From<::Range>, + SubresourceRange: From, { self.cmd.set_subresource_access(node, subresource, access); self } } +#[allow(unused)] mod deprecated { use { - crate::{cmd_ref::PipelineRef, deprecated::Info, node::Node}, + crate::{ + cmd_ref::{PipelineRef, SubresourceRange, View}, + deprecated::Info, + node::Node, + }, ash::vk, + vk_sync::AccessType, }; impl<'a, T> PipelineRef<'a, T> { + #[deprecated = "use resource_access function"] + #[doc(hidden)] + pub fn access_resource(mut self, node: N, access: AccessType) -> Self + where + N: Node + View, + SubresourceRange: From, + { + self.resource_access(node, access) + } + + #[deprecated = "use subresource_access function"] + #[doc(hidden)] + pub fn access_subresource( + mut self, + node: N, + subresource: impl Into, + access: AccessType, + ) -> Self + where + N: Node + View, + SubresourceRange: From, + { + self.subresource_access(node, subresource, access) + } + #[deprecated = "use device_address function of resource function result"] #[doc(hidden)] pub fn node_device_address(&self, node: impl Node) -> vk::DeviceAddress { diff --git a/src/cmd_ref/ray_trace.rs b/src/cmd_ref/ray_trace.rs index 6501f68d..a434522c 100644 --- a/src/cmd_ref/ray_trace.rs +++ b/src/cmd_ref/ray_trace.rs @@ -1,16 +1,17 @@ use { - super::{Nodes, PipelineRef}, + super::{PipelineRef, Resources}, crate::driver::{CommandBuffer, device::Device, ray_trace::RayTracePipeline}, ash::vk, log::trace, + std::ops::Deref, }; // NOTE: local implementation of type from super module impl PipelineRef<'_, RayTracePipeline> { /// Begin recording a ray trace pipeline command buffer. - pub fn record_pipeline( + pub fn record_cmd_buf( mut self, - func: impl FnOnce(RayTracePipelineRef<'_>, Nodes<'_>) + Send + 'static, + func: impl FnOnce(RayTracePipelineRef<'_>, Resources<'_>) + Send + 'static, ) -> Self { let pipeline = self .cmd @@ -27,7 +28,7 @@ impl PipelineRef<'_, RayTracePipeline> { #[cfg(debug_assertions)] let dynamic_stack_size = pipeline.inner.info.dynamic_stack_size; - self.cmd.push_execute(move |cmd_buf, nodes| { + self.cmd.push_execute(move |cmd_buf, resources| { func( RayTracePipelineRef { cmd_buf, @@ -37,7 +38,7 @@ impl PipelineRef<'_, RayTracePipeline> { pipeline, }, - nodes, + resources, ); }); @@ -72,9 +73,10 @@ impl PipelineRef<'_, RayTracePipeline> { /// [RayTraceShaderGroup::new_general(0)], /// )?; /// # let mut my_graph = Graph::default(); -/// my_graph.begin_cmd().debug_name("my ray trace pass") +/// my_graph.begin_cmd() +/// .debug_name("my ray trace pass") /// .bind_pipeline(&my_ray_trace_pipeline) -/// .record_pipeline(move |pipeline, nodes| { +/// .record_cmd_buf(move |cmd_buf, nodes| { /// // During this closure we have access to the ray trace methods! /// }); /// # Ok(()) } @@ -146,10 +148,11 @@ impl RayTracePipelineRef<'_> { /// # let miss_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; /// # let call_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; /// # let mut my_graph = Graph::default(); - /// my_graph.begin_cmd().debug_name("draw a cornell box") + /// my_graph.begin_cmd() + /// .debug_name("draw a cornell box") /// .bind_pipeline(&my_ray_trace_pipeline) - /// .record_pipeline(move |pipeline, nodes| { - /// pipeline.push_constants(0, &[0xcb]) + /// .record_cmd_buf(move |cmd_buf, nodes| { + /// cmd_buf.push_constants(0, &[0xcb]) /// .trace_rays(&rgen_sbt, &hit_sbt, &miss_sbt, &call_sbt, 320, 200, 1); /// }); /// # Ok(()) } @@ -238,10 +241,11 @@ impl RayTracePipelineRef<'_> { /// # let miss_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; /// # let call_sbt = vk::StridedDeviceAddressRegionKHR { device_address: 0, stride: 0, size: 0 }; /// # let mut my_graph = Graph::default(); - /// my_graph.begin_cmd().debug_name("draw a cornell box") + /// my_graph.begin_cmd() + /// .debug_name("draw a cornell box") /// .bind_pipeline(&my_ray_trace_pipeline) - /// .record_pipeline(move |pipeline, nodes| { - /// pipeline.trace_rays(&rgen_sbt, &hit_sbt, &miss_sbt, &call_sbt, 320, 200, 1); + /// .record_cmd_buf(move |cmd_buf, nodes| { + /// cmd_buf.trace_rays(&rgen_sbt, &hit_sbt, &miss_sbt, &call_sbt, 320, 200, 1); /// }); /// # Ok(()) } /// ``` @@ -312,10 +316,22 @@ impl RayTracePipelineRef<'_> { } } +impl<'a> Deref for RayTracePipelineRef<'a> { + type Target = CommandBuffer; + + fn deref(&self) -> &Self::Target { + self.cmd_buf + } +} + +#[allow(unused)] mod deprecated { use { crate::{ - cmd_ref::{Descriptor, PipelineRef, SubresourceRange, View, ViewInfo}, + cmd_ref::{ + Descriptor, PipelineRef, Resources, SubresourceRange, View, ViewInfo, + ray_trace::RayTracePipelineRef, + }, driver::ray_trace::RayTracePipeline, node::Node, }, @@ -361,6 +377,15 @@ mod deprecated { ) } + #[deprecated = "use record_cmd_buf function"] + #[doc(hidden)] + pub fn record_ray_trace( + self, + func: impl FnOnce(RayTracePipelineRef<'_>, Resources<'_>) + Send + 'static, + ) -> Self { + self.record_cmd_buf(func) + } + #[deprecated = "use shader_resource_access function with AccessType::AnyShaderWrite"] #[doc(hidden)] pub fn write_descriptor(self, descriptor: impl Into, node: N) -> Self diff --git a/src/driver/image.rs b/src/driver/image.rs index 3b072ec5..2a8ed05d 100644 --- a/src/driver/image.rs +++ b/src/driver/image.rs @@ -1235,6 +1235,7 @@ impl From for vk::SampleCountFlags { } } +#[allow(unused)] mod deprecated { use crate::driver::image::{ImageInfo, ImageViewInfo}; diff --git a/src/driver/shader.rs b/src/driver/shader.rs index db6b8164..f784bf60 100644 --- a/src/driver/shader.rs +++ b/src/driver/shader.rs @@ -683,7 +683,7 @@ pub struct Shader { /// #version 460 core /// #pragma shader_stage(compute) /// - /// // Defaults to 6 if not set using Shader specialization_info! + /// // Defaults to 6 if not set using Shader specialization! /// layout(constant_id = 0) const uint MY_COUNT = 6; /// /// layout(set = 0, binding = 0) uniform sampler2D my_samplers[MY_COUNT]; @@ -706,14 +706,10 @@ pub struct Shader { /// # let my_shader_code = [0u8; 1]; /// // We instead specify 42 for MY_COUNT: /// let shader = Shader::new_fragment(my_shader_code.as_slice()) - /// .specialization_info(SpecializationMap::new( - /// [vk::SpecializationMapEntry { - /// constant_id: 0, - /// offset: 0, - /// size: 4, - /// }], - /// 42u32.to_ne_bytes() - /// )); + /// .specialization( + /// SpecializationMap::new(42u32.to_ne_bytes()) + /// .constant(0, 0, 4) + /// ); /// # Ok(()) } /// ``` #[builder(default, setter(strip_option))] diff --git a/src/lib.rs b/src/lib.rs index 3852392d..7cd5212f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -196,8 +196,8 @@ println!("{:?}", buffer); // BufferNode println!("{:?}", image); // ImageNode // Borrow resources using nodes (Optional!) -println!("{:?}", graph.node(buffer)); // &Arc -println!("{:?}", graph.node(image)); // &Arc +println!("{:?}", graph.resource(buffer)); // &Arc +println!("{:?}", graph.resource(image)); // &Arc # Ok(()) } ``` @@ -218,11 +218,12 @@ Example: ```no_run # use std::sync::Arc; # use ash::vk; -# use vk_graph::driver::DriverError; +# use vk_graph::driver::{AccessType, DriverError}; # use vk_graph::driver::device::{Device, DeviceInfo}; # use vk_graph::driver::buffer::{Buffer, BufferInfo}; # use vk_graph::driver::image::{Image, ImageInfo}; # use vk_graph::Graph; +# use vk_graph::node::{BufferNode, ImageNode}; # use vk_graph::pool::{Pool}; # use vk_graph::pool::lazy::{LazyPool}; # fn main() -> Result<(), DriverError> { @@ -232,21 +233,24 @@ Example: # let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::STORAGE); # let image = Image::create(&device, info)?; let mut graph = Graph::default(); -let buffer_node = graph.bind_resource(buffer); -let image_node = graph.bind_resource(image); +let buffer: BufferNode = graph.bind_resource(buffer); +let image: ImageNode = graph.bind_resource(image); graph - .begin_cmd().debug_name("Do some raw Vulkan or interop with another Vulkan library") - .record_cmd_buf(move |device, cmd_buf, nodes| unsafe { + .begin_cmd() + .debug_name("Do some raw Vulkan or interop with another Vulkan library") + .record_cmd_buf(|cmd_buf, nodes| { // I always run first! }) - .read_node(buffer_node) // <-- These two functions, read_node/write_node, completely - .write_node(image_node) // handle vulkan synchronization. - .record_cmd_buf(move |device, cmd_buf, nodes| unsafe { - // device is &ash::Device - // cmd_buf is vk::CommandBuffer - // bindings is a magical object you can retrieve the Vulkan resource from - let vk_buffer: vk::Buffer = nodes[buffer_node].handle; - let vk_image: vk::Image = nodes[image_node].handle; + .resource_access(buffer, AccessType::HostRead) + .resource_access(image, AccessType::HostWrite) + .record_cmd_buf(move |cmd_buf, nodes| { + // Raw ash types are available + let device: &ash::Device = &cmd_buf.device; + let cmd_buf: vk::CommandBuffer = cmd_buf.handle; + + // nodes is a magical object you can retrieve the Vulkan resource from + let buffer: vk::Buffer = nodes[buffer].handle; + let image: vk::Image = nodes[image].handle; // You are free to READ vk_buffer and WRITE vk_image! }); @@ -272,10 +276,11 @@ Pipeline instances may be bound to a [`PassRef`] in order to execute the associa # let my_compute_pipeline = ComputePipeline::create(&device, info, shader)?; # let mut graph = Graph::default(); graph - .begin_cmd().debug_name("My compute pass") + .begin_cmd() + .debug_name("My compute pass") .bind_pipeline(&my_compute_pipeline) - .record_pipeline(|compute, _| { - compute.push_constants(0, &42u32.to_ne_bytes()) + .record_cmd_buf(|cmd_buf, _| { + cmd_buf.push_constants(0, &42u32.to_ne_bytes()) .dispatch(128, 1, 1); }); # Ok(()) } @@ -361,7 +366,9 @@ pub use self::{ use { self::{ - cmd_ref::{AttachmentIndex, CommandRef, Descriptor, Nodes, SubresourceAccess, ViewInfo}, + cmd_ref::{ + AttachmentIndex, CommandRef, Descriptor, Resources, SubresourceAccess, ViewInfo, + }, node::Node, node::{ AccelerationStructureLeaseNode, AccelerationStructureNode, @@ -390,7 +397,7 @@ use { vk_sync::AccessType, }; -type ExecFn = Box) + Send>; +type ExecFn = Box) + Send>; type NodeIndex = usize; #[derive(Clone, Copy, Debug)] @@ -1254,6 +1261,7 @@ impl Graph { } } +#[allow(unused)] pub(crate) mod deprecated { use { crate::{ diff --git a/src/resolver.rs b/src/resolver.rs index 46ea1eba..31620820 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1,7 +1,7 @@ use { super::{ Area, Attachment, Command, ExecutionPipeline, Graph, Node, NodeIndex, Resource, - cmd_ref::{Nodes, SubresourceAccess, SubresourceRange}, + cmd_ref::{Resources, SubresourceAccess, SubresourceRange}, node::SwapchainImageNode, }, crate::{ @@ -2525,7 +2525,7 @@ impl Resolver { let exec_func = exec.func.take().unwrap().0; exec_func( cmd_buf, - Nodes::new( + Resources::new( &self.graph.resources, #[cfg(debug_assertions)] exec, From 33a3808b7ddcf97b799637b082d4ac037011eea0 Mon Sep 17 00:00:00 2001 From: John Wells Date: Wed, 25 Feb 2026 12:31:16 -0500 Subject: [PATCH 23/86] Resolver -> Queue --- contrib/vk-graph-fx/src/image_loader.rs | 2 +- examples/bindless.rs | 2 +- examples/cpu_readback.rs | 2 +- examples/image_sampler.rs | 2 +- examples/min_max.rs | 2 +- examples/mip_compute.rs | 2 +- examples/mip_graphic.rs | 2 +- examples/multipass.rs | 2 +- examples/multithread.rs | 4 +- examples/ray_omni.rs | 2 +- examples/ray_trace.rs | 2 +- examples/rt_triangle.rs | 2 +- examples/shader-toy/src/main.rs | 2 +- examples/skeletal-anim/src/main.rs | 4 +- examples/subgroup_ops.rs | 2 +- examples/vr/src/main.rs | 4 +- src/cmd_ref/{accel_struct.rs => cmd_buf.rs} | 6 +- src/cmd_ref/mod.rs | 21 +- src/cmd_ref/pipeline.rs | 2 +- src/display.rs | 86 +++--- src/lib.rs | 22 +- src/{resolver.rs => queue.rs} | 288 ++++++++++++-------- 22 files changed, 257 insertions(+), 206 deletions(-) rename src/cmd_ref/{accel_struct.rs => cmd_buf.rs} (99%) rename src/{resolver.rs => queue.rs} (98%) diff --git a/contrib/vk-graph-fx/src/image_loader.rs b/contrib/vk-graph-fx/src/image_loader.rs index 6659f619..68256df2 100644 --- a/contrib/vk-graph-fx/src/image_loader.rs +++ b/contrib/vk-graph-fx/src/image_loader.rs @@ -237,7 +237,7 @@ impl ImageLoader { let image = graph.resource(image).clone(); graph - .resolve() + .queue() .submit(&mut self.pool, queue_family_index, queue_index)?; Ok(image) diff --git a/examples/bindless.rs b/examples/bindless.rs index 054761cf..aae9e40e 100644 --- a/examples/bindless.rs +++ b/examples/bindless.rs @@ -75,7 +75,7 @@ fn create_images(device: &Device) -> Result>, DriverError> { let mut pool = LazyPool::new(device); - graph.resolve().submit(&mut pool, 0, 0)?; + graph.queue().submit(&mut pool, 0, 0)?; Ok(textures) } diff --git a/examples/cpu_readback.rs b/examples/cpu_readback.rs index f0f95bf6..8051ff75 100644 --- a/examples/cpu_readback.rs +++ b/examples/cpu_readback.rs @@ -35,7 +35,7 @@ fn main() -> Result<(), DriverError> { // Resolve and wait (or you can check has_executed without blocking) - alternatively you might // use device.queue_wait_idle(0) or device.device_wait_idle() - but those block on larger scopes - let mut cmd_buf = graph.resolve().submit(&mut HashPool::new(&device), 0, 0)?; + let mut cmd_buf = graph.queue().submit(&mut HashPool::new(&device), 0, 0)?; println!("Has executed? {}", cmd_buf.has_executed()?); let started = Instant::now(); diff --git a/examples/image_sampler.rs b/examples/image_sampler.rs index 9bc399e7..015d0508 100644 --- a/examples/image_sampler.rs +++ b/examples/image_sampler.rs @@ -254,7 +254,7 @@ fn read_image(device: &Device, path: impl AsRef) -> anyhow::Result Result<(), DriverError> { let max_result_buf = copy_image_to_buffer(&device, &mut graph, max_reduced_image)?; graph - .resolve() + .queue() .submit(&mut HashPool::new(&device), 0, 0)? .wait_until_executed()?; diff --git a/examples/mip_compute.rs b/examples/mip_compute.rs index 001049d3..77c303f3 100644 --- a/examples/mip_compute.rs +++ b/examples/mip_compute.rs @@ -143,7 +143,7 @@ fn main() -> Result<(), DriverError> { let depth_pixel = graph.resource(depth_pixel).clone(); graph - .resolve() + .queue() .submit(&mut HashPool::new(&device), 0, 0)? .wait_until_executed()?; diff --git a/examples/mip_graphic.rs b/examples/mip_graphic.rs index 38194b1c..2b77423b 100644 --- a/examples/mip_graphic.rs +++ b/examples/mip_graphic.rs @@ -198,7 +198,7 @@ fn fill_mip_levels(device: &Device, image: &Arc) -> Result<(), DriverErro // Submits to the GPU but does not wait for anything to be finished graph - .resolve() + .queue() .submit(&mut LazyPool::new(device), queue_family_index, 0) .map(|_| ()) } diff --git a/examples/multipass.rs b/examples/multipass.rs index 165a560a..b6d43f65 100644 --- a/examples/multipass.rs +++ b/examples/multipass.rs @@ -305,7 +305,7 @@ fn create_funky_shape(device: &Device, pool: &mut LazyPool) -> Result anyhow::Result<()> { // Submit on a queue we are reserving for only this thread to use graph - .resolve() + .queue() .submit(&mut pool, secondary_queue_family_index, queue_index) .unwrap(); @@ -257,7 +257,7 @@ fn load_font(device: &Device) -> anyhow::Result { let page_0 = graph.resource(page_0).clone(); // This copy happens in queue index 0! - graph.resolve().submit(&mut HashPool::new(device), 0, 0)?; + graph.queue().submit(&mut HashPool::new(device), 0, 0)?; BitmapFont::new(device, font, [page_0]) } diff --git a/examples/ray_omni.rs b/examples/ray_omni.rs index c778ecf6..f47f6697 100644 --- a/examples/ray_omni.rs +++ b/examples/ray_omni.rs @@ -222,7 +222,7 @@ fn create_blas( let blas = graph.resource(blas).clone(); - graph.resolve().submit(&mut LazyPool::new(device), 0, 0)?; + graph.queue().submit(&mut LazyPool::new(device), 0, 0)?; Ok(blas) } diff --git a/examples/ray_trace.rs b/examples/ray_trace.rs index 5e06afa9..a74d3f01 100644 --- a/examples/ray_trace.rs +++ b/examples/ray_trace.rs @@ -733,7 +733,7 @@ fn main() -> anyhow::Result<()> { }); } - graph.resolve().submit(&mut cache, 0, 0)?; + graph.queue().submit(&mut cache, 0, 0)?; } // ------------------------------------------------------------------------------------------ // diff --git a/examples/rt_triangle.rs b/examples/rt_triangle.rs index 8645261a..9fb1302b 100644 --- a/examples/rt_triangle.rs +++ b/examples/rt_triangle.rs @@ -387,7 +387,7 @@ fn main() -> anyhow::Result<()> { }); } - graph.resolve().submit(&mut pool, 0, 0)?; + graph.queue().submit(&mut pool, 0, 0)?; } // ------------------------------------------------------------------------------------------ // diff --git a/examples/shader-toy/src/main.rs b/examples/shader-toy/src/main.rs index 18559cab..83ffc17d 100644 --- a/examples/shader-toy/src/main.rs +++ b/examples/shader-toy/src/main.rs @@ -172,7 +172,7 @@ fn main() -> anyhow::Result<()> { let mut blank_image_binding = Some(graph.resource(blank_image).clone()); let mut temp_image_binding = Some(graph.resource(temp_image).clone()); - graph.resolve().submit(&mut cache, 0, 0)?; + graph.queue().submit(&mut cache, 0, 0)?; let started_at = Instant::now(); let mut count = 0i32; diff --git a/examples/skeletal-anim/src/main.rs b/examples/skeletal-anim/src/main.rs index 26d6e322..d5fc9f6d 100644 --- a/examples/skeletal-anim/src/main.rs +++ b/examples/skeletal-anim/src/main.rs @@ -196,7 +196,7 @@ fn load_texture(device: &Device, pak: &mut PakBuf, key: &str) -> Result anyhow::Result<()> { // in-flight resources) so that nothing is dropped until that image is actually done. swapchain.wait_image(xr::Duration::INFINITE).unwrap(); let cmd_buf = graph - .resolve() + .queue() .submit(&mut pool, queue_family_index as _, 0) .unwrap(); swapchain.release_image().unwrap(); @@ -771,7 +771,7 @@ fn load_texture( let queue_family_index = device_queue_family_index(device, vk::QueueFlags::TRANSFER).unwrap(); graph - .resolve() + .queue() .submit(&mut LazyPool::new(device), queue_family_index as _, 0)? .wait_until_executed()?; diff --git a/src/cmd_ref/accel_struct.rs b/src/cmd_ref/cmd_buf.rs similarity index 99% rename from src/cmd_ref/accel_struct.rs rename to src/cmd_ref/cmd_buf.rs index d692b9f7..2f52fe6f 100644 --- a/src/cmd_ref/accel_struct.rs +++ b/src/cmd_ref/cmd_buf.rs @@ -43,12 +43,12 @@ use { /// }); /// # Ok(()) } /// ``` -pub struct AccelerationStructureRef<'a> { +pub struct CommandBufferRef<'a> { pub(super) cmd_buf: &'a CommandBuffer, pub(super) resources: Resources<'a>, } -impl AccelerationStructureRef<'_> { +impl CommandBufferRef<'_> { /// Build acceleration structures. /// /// There is no ordering or synchronization implied between any of the individual acceleration @@ -461,7 +461,7 @@ impl AccelerationStructureRef<'_> { } } -impl<'a> Deref for AccelerationStructureRef<'a> { +impl<'a> Deref for CommandBufferRef<'a> { type Target = CommandBuffer; fn deref(&self) -> &Self::Target { diff --git a/src/cmd_ref/mod.rs b/src/cmd_ref/mod.rs index 350f8695..7a95e466 100644 --- a/src/cmd_ref/mod.rs +++ b/src/cmd_ref/mod.rs @@ -1,17 +1,16 @@ //! Strongly-typed rendering commands. -mod accel_struct; mod bind; +mod cmd_buf; mod compute; mod graphic; mod pipeline; mod ray_trace; pub use self::{ - accel_struct::{ - AccelerationStructureRef, BuildAccelerationStructureIndirectInfo, - BuildAccelerationStructureInfo, UpdateAccelerationStructureIndirectInfo, - UpdateAccelerationStructureInfo, + cmd_buf::{ + BuildAccelerationStructureIndirectInfo, BuildAccelerationStructureInfo, CommandBufferRef, + UpdateAccelerationStructureIndirectInfo, UpdateAccelerationStructureInfo, }, pipeline::PipelineRef, }; @@ -163,10 +162,10 @@ impl<'a> CommandRef<'a> { /// code and interfaces. pub fn record_cmd_buf( mut self, - func: impl FnOnce(AccelerationStructureRef<'_>, Resources<'_>) + Send + 'static, + func: impl FnOnce(CommandBufferRef<'_>, Resources<'_>) + Send + 'static, ) -> Self { self.push_execute(move |cmd_buf, resources| { - func(AccelerationStructureRef { cmd_buf, resources }, resources); + func(CommandBufferRef { cmd_buf, resources }, resources); }); self @@ -174,7 +173,7 @@ impl<'a> CommandRef<'a> { /// Returns a borrow of the original Vulkan resource (buffer, image or acceleration structure) /// which the given node represents. - pub fn resource(&self, node: N) -> &::Resource + pub fn resource(&self, node: N) -> &N::Resource where N: Bound, { @@ -670,7 +669,7 @@ impl From> for ViewInfo { mod deprecated { use { crate::{ - cmd_ref::{AccelerationStructureRef, CommandRef, Resources, SubresourceRange, View}, + cmd_ref::{CommandBufferRef, CommandRef, Resources, SubresourceRange, View}, deprecated::Info, node::Node, }, @@ -739,10 +738,10 @@ mod deprecated { #[doc(hidden)] pub fn record_accel_struct( mut self, - func: impl FnOnce(AccelerationStructureRef<'_>, Resources<'_>) + Send + 'static, + func: impl FnOnce(CommandBufferRef<'_>, Resources<'_>) + Send + 'static, ) -> Self { self.push_execute(move |cmd_buf, resources| { - func(AccelerationStructureRef { cmd_buf, resources }, resources); + func(CommandBufferRef { cmd_buf, resources }, resources); }); self diff --git a/src/cmd_ref/pipeline.rs b/src/cmd_ref/pipeline.rs index 9c5eb664..d20d865b 100644 --- a/src/cmd_ref/pipeline.rs +++ b/src/cmd_ref/pipeline.rs @@ -32,7 +32,7 @@ impl<'a, T> PipelineRef<'a, T> { /// Returns a borrow of the original Vulkan resource (buffer, image or acceleration structure) /// which the given node represents. - pub fn resource(&self, node: N) -> &::Resource + pub fn resource(&self, node: N) -> &N::Resource where N: Bound, { diff --git a/src/display.rs b/src/display.rs index 15b79da0..34e98014 100644 --- a/src/display.rs +++ b/src/display.rs @@ -153,8 +153,8 @@ impl Display { ) -> Result<(), DisplayError> { trace!("present_image"); - let mut resolver = graph.resolve(); - let wait_dst_stage_mask = resolver.node_pipeline_stages(swapchain_image); + let mut queue = graph.queue(); + let wait_dst_stage_mask = queue.resource_stages(swapchain_image); // The swapchain should have been written to, otherwise it would be noise and that's a panic assert!( @@ -162,7 +162,7 @@ impl Display { "uninitialized swapchain image: write something each frame!", ); - let exec_idx = resolver.swapchain_image(swapchain_image).exec_idx; + let exec_idx = queue.swapchain_image(swapchain_image).exec_idx; let exec = &mut self.execs[exec_idx]; debug_assert!(exec.queue.is_none()); @@ -180,11 +180,11 @@ impl Display { .map_err(|_| ())?; } - // resolver.record_node_dependencies(&mut *self.pool, cmd_buf, swapchain_image)?; - resolver.record_node(pool, &mut exec.cmd_buf, swapchain_image)?; + // queue.record_node_dependencies(&mut *self.pool, cmd_buf, swapchain_image)?; + queue.submit_resource(swapchain_image, pool, &mut exec.cmd_buf)?; { - let swapchain_image = resolver.swapchain_image(swapchain_image); + let swapchain_image = queue.swapchain_image(swapchain_image); for (access, range) in Image::access( swapchain_image, AccessType::Present, @@ -228,50 +228,52 @@ impl Display { // before present which use nodes that are unused in the remainder of the graph. // These operations are still important, but they don't need to wait for any of the above // things so we do them last - resolver.record_unscheduled_passes(pool, &mut exec.cmd_buf)?; + queue.submit_cmd_buf(pool, &mut exec.cmd_buf)?; - let queue = Device::queue( - &exec.cmd_buf.device, - self.info.queue_family_index, - queue_index, - ); + { + let queue = Device::queue( + &exec.cmd_buf.device, + self.info.queue_family_index, + queue_index, + ); - unsafe { - exec.cmd_buf - .device - .end_command_buffer(exec.cmd_buf.handle) - .map_err(|err| { - warn!("unable to end display command buffer: {err}"); + unsafe { + exec.cmd_buf + .device + .end_command_buffer(exec.cmd_buf.handle) + .map_err(|err| { + warn!("unable to end display command buffer: {err}"); - DriverError::InvalidData - })?; - exec.cmd_buf - .device - .queue_submit( - queue, - slice::from_ref( - &vk::SubmitInfo::default() - .command_buffers(slice::from_ref(&exec.cmd_buf.handle)) - .wait_semaphores(slice::from_ref(&exec.swapchain_acquired)) - .wait_dst_stage_mask(slice::from_ref(&wait_dst_stage_mask)) - .signal_semaphores(slice::from_ref(&exec.swapchain_rendered)), - ), - exec.cmd_buf.fence, - ) - .map_err(|err| { - warn!("unable to submit display command buffer: {err}"); + DriverError::InvalidData + })?; + exec.cmd_buf + .device + .queue_submit( + queue, + slice::from_ref( + &vk::SubmitInfo::default() + .command_buffers(slice::from_ref(&exec.cmd_buf.handle)) + .wait_semaphores(slice::from_ref(&exec.swapchain_acquired)) + .wait_dst_stage_mask(slice::from_ref(&wait_dst_stage_mask)) + .signal_semaphores(slice::from_ref(&exec.swapchain_rendered)), + ), + exec.cmd_buf.fence, + ) + .map_err(|err| { + warn!("unable to submit display command buffer: {err}"); + + DriverError::InvalidData + })? + } - DriverError::InvalidData - })? + exec.cmd_buf.waiting = true; + exec.queue = Some(queue); } - exec.cmd_buf.waiting = true; - exec.queue = Some(queue); - let elapsed = Instant::now() - started; trace!("🔜🔜🔜 vkQueueSubmit took {} μs", elapsed.as_micros(),); - let swapchain_image = resolver.swapchain_image(swapchain_image).clone(); + let swapchain_image = queue.swapchain_image(swapchain_image).clone(); self.swapchain.present_image( swapchain_image, @@ -282,7 +284,7 @@ impl Display { // Store the resolved graph because it contains bindings, leases, and other shared resources // that need to be kept alive until the fence is waited upon. - exec.cmd_buf.push_fenced_drop(resolver); + exec.cmd_buf.push_fenced_drop(queue); Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 7cd5212f..870f89fa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -357,11 +357,11 @@ pub mod node; pub mod pool; mod bind; -mod resolver; +mod queue; pub use self::{ bind::{BindGraph, Bound, Resource}, - resolver::Resolver, + queue::Queue, }; use { @@ -651,12 +651,6 @@ pub struct Graph { } impl Graph { - /// Constructs a new `Graph`. - #[deprecated = "use default function instead"] - pub fn new() -> Self { - Default::default() - } - /// Allocates and begins writing a new command. pub fn begin_cmd(&mut self) -> CommandRef<'_> { CommandRef::new(self) @@ -1204,7 +1198,7 @@ impl Graph { /// Returns a borrow of the original Vulkan resource (buffer, image or acceleration structure) /// which the given node represents. - pub fn resource(&self, node: N) -> &::Resource + pub fn resource(&self, node: N) -> &N::Resource where N: Bound, { @@ -1214,13 +1208,13 @@ impl Graph { /// Finalizes the graph and provides an object with functions for submitting the resulting /// commands. #[profiling::function] - pub fn resolve(mut self) -> Resolver { + pub fn queue(mut self) -> Queue { // The final execution of each pass has no function for pass in &mut self.cmds { pass.execs.pop(); } - Resolver::new(self) + Queue::new(self) } /// Note: `data` must not exceed 65536 bytes. @@ -1279,6 +1273,12 @@ pub(crate) mod deprecated { }; impl Graph { + #[deprecated = "use default function instead"] + #[doc(hidden)] + pub fn new() -> Self { + Default::default() + } + #[deprecated = "use device_address function of resource function result"] #[doc(hidden)] pub fn node_device_address(&self, node: impl Node) -> vk::DeviceAddress { diff --git a/src/resolver.rs b/src/queue.rs similarity index 98% rename from src/resolver.rs rename to src/queue.rs index 31620820..48e6d89a 100644 --- a/src/resolver.rs +++ b/src/queue.rs @@ -176,12 +176,12 @@ impl Drop for PhysicalPass { /// /// #[derive(Debug)] -pub struct Resolver { +pub struct Queue { pub(super) graph: Graph, physical_passes: Vec, } -impl Resolver { +impl Queue { pub(super) fn new(graph: Graph) -> Self { let physical_passes = Vec::with_capacity(graph.cmds.len()); @@ -688,7 +688,7 @@ impl Resolver { /// A fully-resolved graph contains no additional work and may be discarded, although doing so /// will stall the GPU while the fences are waited on. It is preferrable to wait a few frame so /// that the fences will have already been signalled. - pub fn is_resolved(&self) -> bool { + pub fn is_submitted(&self) -> bool { self.graph.cmds.is_empty() } @@ -1847,41 +1847,6 @@ impl Resolver { } } - /// Returns the stages that process the given node. - /// - /// Note that this value must be retrieved before resolving a node as there will be no - /// data left to inspect afterwards! - #[profiling::function] - pub fn node_pipeline_stages(&self, node: impl Node) -> vk::PipelineStageFlags { - let node_idx = node.index(); - let mut res = Default::default(); - - 'pass: for pass in self.graph.cmds.iter() { - for exec in pass.execs.iter() { - if exec.accesses.contains_key(&node_idx) { - res |= pass - .execs - .iter() - .filter_map(|exec| exec.pipeline.as_ref()) - .map(|pipeline| pipeline.stage()) - .reduce(|j, k| j | k) - .unwrap_or(vk::PipelineStageFlags::TRANSFER); - - // The execution pipelines of a pass are always the same type - continue 'pass; - } - } - } - - debug_assert_ne!( - res, - Default::default(), - "The given node was not accessed in this graph" - ); - - res - } - #[profiling::function] fn record_execution_barriers<'a>( cmd_buf: &CommandBuffer, @@ -2319,57 +2284,6 @@ impl Resolver { }); } - /// Records any pending render graph passes that are required by the given node, but does not - /// record any passes that actually contain the given node. - /// - /// As a side effect, the graph is optimized for the given node. Future calls may further optimize - /// the graph, but only on top of the existing optimizations. This only matters if you are pulling - /// multiple images out and you care - in that case pull the "most important" image first. - #[profiling::function] - pub fn record_node_dependencies

( - &mut self, - pool: &mut P, - cmd_buf: &mut CommandBuffer, - node: impl Node, - ) -> Result<(), DriverError> - where - P: Pool + Pool, - { - let node_idx = node.index(); - - debug_assert!(self.graph.resources.get(node_idx).is_some()); - - // We record up to but not including the first pass which accesses the target node - if let Some(end_pass_idx) = self.graph.first_node_access_pass_index(node) { - self.record_node_passes(pool, cmd_buf, node_idx, end_pass_idx)?; - } - - Ok(()) - } - - /// Records any pending render graph passes that the given node requires. - #[profiling::function] - pub fn record_node

( - &mut self, - pool: &mut P, - cmd_buf: &mut CommandBuffer, - node: impl Node, - ) -> Result<(), DriverError> - where - P: Pool + Pool, - { - let node_idx = node.index(); - - debug_assert!(self.graph.resources.get(node_idx).is_some()); - - if self.graph.cmds.is_empty() { - return Ok(()); - } - - let end_pass_idx = self.graph.cmds.len(); - self.record_node_passes(pool, cmd_buf, node_idx, end_pass_idx) - } - #[profiling::function] fn record_node_passes

( &mut self, @@ -2578,35 +2492,6 @@ impl Resolver { Ok(()) } - /// Records any pending render graph passes that have not been previously scheduled. - #[profiling::function] - pub fn record_unscheduled_passes

( - &mut self, - pool: &mut P, - cmd_buf: &mut CommandBuffer, - ) -> Result<(), DriverError> - where - P: Pool + Pool, - { - if self.graph.cmds.is_empty() { - return Ok(()); - } - - thread_local! { - static SCHEDULE: RefCell = Default::default(); - } - - SCHEDULE.with_borrow_mut(|schedule| { - schedule - .access_cache - .update(&self.graph, self.graph.cmds.len()); - schedule.passes.clear(); - schedule.passes.extend(0..self.graph.cmds.len()); - - self.record_scheduled_passes(pool, cmd_buf, schedule, self.graph.cmds.len()) - }) - } - #[profiling::function] fn render_area(bindings: &[Resource], pass: &Command) -> Area { // set_render_area was not specified so we're going to guess using the minimum common @@ -2709,6 +2594,41 @@ impl Resolver { }); } + /// Returns the stages that process the given node. + /// + /// Note that this value must be retrieved before resolving a node as there will be no + /// data left to inspect afterwards! + #[profiling::function] + pub fn resource_stages(&self, node: impl Node) -> vk::PipelineStageFlags { + let node_idx = node.index(); + let mut res = Default::default(); + + 'pass: for pass in self.graph.cmds.iter() { + for exec in pass.execs.iter() { + if exec.accesses.contains_key(&node_idx) { + res |= pass + .execs + .iter() + .filter_map(|exec| exec.pipeline.as_ref()) + .map(|pipeline| pipeline.stage()) + .reduce(|j, k| j | k) + .unwrap_or(vk::PipelineStageFlags::TRANSFER); + + // The execution pipelines of a pass are always the same type + continue 'pass; + } + } + } + + debug_assert_ne!( + res, + Default::default(), + "The given node was not accessed in this graph" + ); + + res + } + /// Returns a vec of pass indexes that are required to be executed, in order, for the given /// node. #[profiling::function] @@ -2918,7 +2838,7 @@ impl Resolver { .map_err(|_| DriverError::OutOfMemory)?; } - self.record_unscheduled_passes(pool, &mut cmd_buf)?; + self.submit_cmd_buf(pool, &mut cmd_buf)?; let queue = Device::queue(&cmd_buf.device, queue_family_index, queue_index); @@ -2956,6 +2876,86 @@ impl Resolver { Ok(cmd_buf) } + /// Records any pending render graph passes that have not been previously scheduled. + #[profiling::function] + pub fn submit_cmd_buf

( + &mut self, + pool: &mut P, + cmd_buf: &mut CommandBuffer, + ) -> Result<(), DriverError> + where + P: Pool + Pool, + { + if self.graph.cmds.is_empty() { + return Ok(()); + } + + thread_local! { + static SCHEDULE: RefCell = Default::default(); + } + + SCHEDULE.with_borrow_mut(|schedule| { + schedule + .access_cache + .update(&self.graph, self.graph.cmds.len()); + schedule.passes.clear(); + schedule.passes.extend(0..self.graph.cmds.len()); + + self.record_scheduled_passes(pool, cmd_buf, schedule, self.graph.cmds.len()) + }) + } + + /// Records any pending render graph passes that the given node requires. + #[profiling::function] + pub fn submit_resource

( + &mut self, + node: impl Node, + pool: &mut P, + cmd_buf: &mut CommandBuffer, + ) -> Result<(), DriverError> + where + P: Pool + Pool, + { + let node_idx = node.index(); + + debug_assert!(self.graph.resources.get(node_idx).is_some()); + + if self.graph.cmds.is_empty() { + return Ok(()); + } + + let end_pass_idx = self.graph.cmds.len(); + self.record_node_passes(pool, cmd_buf, node_idx, end_pass_idx) + } + + /// Records any pending render graph passes that are required by the given node, but does not + /// record any passes that actually contain the given node. + /// + /// As a side effect, the graph is optimized for the given node. Future calls may further optimize + /// the graph, but only on top of the existing optimizations. This only matters if you are pulling + /// multiple images out and you care - in that case pull the "most important" image first. + #[profiling::function] + pub fn submit_resource_dependencies

( + &mut self, + node: impl Node, + pool: &mut P, + cmd_buf: &mut CommandBuffer, + ) -> Result<(), DriverError> + where + P: Pool + Pool, + { + let node_idx = node.index(); + + debug_assert!(self.graph.resources.get(node_idx).is_some()); + + // We record up to but not including the first pass which accesses the target node + if let Some(end_pass_idx) = self.graph.first_node_access_pass_index(node) { + self.record_node_passes(pool, cmd_buf, node_idx, end_pass_idx)?; + } + + Ok(()) + } + pub(crate) fn swapchain_image(&mut self, node: SwapchainImageNode) -> &SwapchainImage { let Some(swapchain_image) = self.graph.resources[node.idx].as_swapchain_image() else { panic!("invalid swapchain image node"); @@ -3234,3 +3234,53 @@ struct Schedule { access_cache: AccessCache, passes: Vec, } + +#[allow(private_bounds)] +#[allow(unused)] +mod derecated { + use crate::{ + Queue, + driver::{ + CommandBuffer, DescriptorPool, DescriptorPoolInfo, DriverError, RenderPass, + RenderPassInfo, + }, + node::Node, + pool::Pool, + }; + + impl Queue { + #[deprecated = "use is_submitted function"] + #[doc(hidden)] + pub fn is_resolved(&self) -> bool { + self.is_submitted() + } + + #[deprecated = "use submit_resource function"] + #[doc(hidden)] + pub fn record_node

( + &mut self, + pool: &mut P, + cmd_buf: &mut CommandBuffer, + node: impl Node, + ) -> Result<(), DriverError> + where + P: Pool + Pool, + { + self.submit_resource(node, pool, cmd_buf) + } + + #[deprecated = "use submit_resource function"] + #[doc(hidden)] + pub fn record_node_dependencies

( + &mut self, + pool: &mut P, + cmd_buf: &mut CommandBuffer, + node: impl Node, + ) -> Result<(), DriverError> + where + P: Pool + Pool, + { + self.submit_resource_dependencies(node, pool, cmd_buf) + } + } +} From a6ca7d4784b949173c2d5d291a153df0bb990761 Mon Sep 17 00:00:00 2001 From: John Wells Date: Wed, 25 Feb 2026 15:54:21 -0500 Subject: [PATCH 24/86] Move Display to window crate and rename to Swapchain --- contrib/vk-graph-prelude/src/lib.rs | 5 +- contrib/vk-graph-window/Cargo.toml | 1 + contrib/vk-graph-window/src/lib.rs | 60 ++- .../vk-graph-window/src/swapchain.rs | 397 ++++++++++++------ examples/app.rs | 27 +- examples/shader-toy/src/main.rs | 2 +- examples/skeletal-anim/src/main.rs | 3 +- examples/vr/src/main.rs | 2 +- src/bind.rs | 2 +- src/cmd_ref/cmd_buf.rs | 2 +- src/cmd_ref/compute.rs | 2 +- src/cmd_ref/graphic.rs | 2 +- src/cmd_ref/mod.rs | 2 +- src/cmd_ref/ray_trace.rs | 2 +- src/driver/cmd_buf.rs | 36 +- src/driver/descriptor_set.rs | 40 +- src/driver/device.rs | 17 +- src/driver/instance.rs | 6 +- src/driver/mod.rs | 46 +- src/driver/render_pass.rs | 27 +- src/driver/swapchain.rs | 154 ++++--- src/edge.rs | 103 ----- src/lib.rs | 4 +- src/pool/fifo.rs | 10 +- src/pool/hash.rs | 10 +- src/pool/lazy.rs | 10 +- src/pool/mod.rs | 3 +- src/queue.rs | 55 ++- 28 files changed, 565 insertions(+), 465 deletions(-) rename src/display.rs => contrib/vk-graph-window/src/swapchain.rs (52%) delete mode 100644 src/edge.rs diff --git a/contrib/vk-graph-prelude/src/lib.rs b/contrib/vk-graph-prelude/src/lib.rs index 67401147..2244e570 100644 --- a/contrib/vk-graph-prelude/src/lib.rs +++ b/contrib/vk-graph-prelude/src/lib.rs @@ -8,9 +8,8 @@ pub use vk_graph::{ BuildAccelerationStructureIndirectInfo, BuildAccelerationStructureInfo, CommandRef, PipelineRef, UpdateAccelerationStructureIndirectInfo, UpdateAccelerationStructureInfo, }, - display::{Display, DisplayError, DisplayInfo, DisplayInfoBuilder, ResolverPool}, driver::{ - AccessType, CommandBuffer, DriverError, + DriverError, accel_struct::{ AccelerationStructure, AccelerationStructureGeometry, AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, @@ -19,6 +18,7 @@ pub use vk_graph::{ }, ash::vk, buffer::{Buffer, BufferInfo, BufferInfoBuilder, BufferSubresourceRange}, + cmd_buf::CommandBuffer, compute::{ComputePipeline, ComputePipelineInfo, ComputePipelineInfoBuilder}, device::{Device, DeviceInfo, DeviceInfoBuilder}, graphic::{ @@ -44,6 +44,7 @@ pub use vk_graph::{ swapchain::{ Swapchain, SwapchainError, SwapchainImage, SwapchainInfo, SwapchainInfoBuilder, }, + sync::AccessType, }, node::{ AccelerationStructureLeaseNode, AccelerationStructureNode, AnyAccelerationStructureNode, diff --git a/contrib/vk-graph-window/Cargo.toml b/contrib/vk-graph-window/Cargo.toml index dad1a98a..e9e4a2c1 100644 --- a/contrib/vk-graph-window/Cargo.toml +++ b/contrib/vk-graph-window/Cargo.toml @@ -7,6 +7,7 @@ license = "MIT OR Apache-2.0" readme = "README.md" [dependencies] +derive_builder = "0.20" log = "0.4" profiling = "1.0" vk-graph = { path = "../..", default-features = false } diff --git a/contrib/vk-graph-window/src/lib.rs b/contrib/vk-graph-window/src/lib.rs index 00b6cf38..8ce56cf5 100644 --- a/contrib/vk-graph-window/src/lib.rs +++ b/contrib/vk-graph-window/src/lib.rs @@ -3,19 +3,19 @@ #![warn(missing_docs)] mod frame; +pub mod swapchain; pub use self::frame::FrameContext; use { + self::swapchain::{Swapchain, SwapchainError, SwapchainInfo}, log::{error, info, trace, warn}, std::{error, fmt}, vk_graph::{ - display::{Display, DisplayError, DisplayInfoBuilder}, driver::{ ash::vk, device::{Device, DeviceInfo}, surface::Surface, - swapchain::{Swapchain, SwapchainInfo}, DriverError, }, pool::hash::HashPool, @@ -78,10 +78,10 @@ impl Window { } impl Application { - fn create_display( + fn create_swapchain( &mut self, window: &winit::window::Window, - ) -> Result { + ) -> Result { let surface = Surface::create(&self.device, window, window)?; let surface_formats = Surface::formats(&surface)?; let surface_format = self @@ -94,7 +94,8 @@ impl Window { let mut swapchain_info = SwapchainInfo::new(window_size.width, window_size.height, surface_format) - .to_builder(); + .to_builder() + .command_buffer_count(self.data.cmd_buf_count); if let Some(min_image_count) = self.data.min_image_count { swapchain_info = swapchain_info.min_image_count(min_image_count); @@ -128,16 +129,11 @@ impl Window { } } - let swapchain = Swapchain::new(&self.device, surface, swapchain_info)?; - let display = Display::new( - &self.device, - swapchain, - DisplayInfoBuilder::default().command_buffer_count(self.data.cmd_buf_count), - )?; + let swapchain = Swapchain::new(surface, swapchain_info)?; - trace!("created display"); + trace!("created swapchain"); - Ok(display) + Ok(swapchain) } fn window_mode_attributes( @@ -248,7 +244,7 @@ impl Window { } Ok(res) => res, }; - let display = match self.create_display(&window) { + let swapchain = match self.create_swapchain(&window) { Err(err) => { warn!("Unable to create swapchain: {err}"); @@ -259,12 +255,12 @@ impl Window { } Ok(res) => res, }; - let display_pool = HashPool::new(&self.device); + let swapchain_pool = HashPool::new(&self.device); self.active_window = Some(ActiveWindow { - display, - display_pool, - display_resize: None, + swapchain, + swapchain_pool, + swapchain_resize: None, events: vec![], window, }); @@ -299,7 +295,7 @@ impl Window { } } WindowEvent::Resized(size) => { - active_window.display_resize = Some((size.width, size.height)); + active_window.swapchain_resize = Some((size.width, size.height)); } _ => (), } @@ -312,9 +308,9 @@ impl Window { } struct ActiveWindow { - display: Display, - display_pool: HashPool, - display_resize: Option<(u32, u32)>, + swapchain: Swapchain, + swapchain_pool: HashPool, + swapchain_resize: Option<(u32, u32)>, events: Vec>, window: winit::window::Window, } @@ -324,18 +320,18 @@ impl Window { &mut self, device: &Device, mut f: impl FnMut(FrameContext), - ) -> Result { - if let Some((width, height)) = self.display_resize.take() { - let mut swapchain_info = self.display.swapchain.info; + ) -> Result { + if let Some((width, height)) = self.swapchain_resize.take() { + let mut swapchain_info = self.swapchain.info; swapchain_info.width = width; swapchain_info.height = height; - self.display.update_swapchain(swapchain_info); + self.swapchain.set_info(swapchain_info); } - if let Some(swapchain_image) = self.display.acquire_next_image()? { + if let Some(swapchain_image) = self.swapchain.acquire_next_image()? { let mut graph = Graph::default(); let swapchain_image = graph.bind_resource(swapchain_image); - let swapchain_info = self.display.swapchain.info; + let swapchain_info = self.swapchain.info; let mut will_exit = false; @@ -361,8 +357,8 @@ impl Window { } self.window.pre_present_notify(); - self.display - .present_image(&mut self.display_pool, graph, swapchain_image, 0) + self.swapchain + .present_image(&mut self.swapchain_pool, graph, swapchain_image, 0) .inspect_err(|err| { warn!("unable to present swapchain image: {err}"); })?; @@ -388,10 +384,10 @@ impl Window { self.event_loop.run_app(&mut app)?; if let Some(ActiveWindow { - display, window, .. + swapchain, window, .. }) = app.active_window.take() { - drop(display); + drop(swapchain); drop(window); } diff --git a/src/display.rs b/contrib/vk-graph-window/src/swapchain.rs similarity index 52% rename from src/display.rs rename to contrib/vk-graph-window/src/swapchain.rs index 34e98014..270a626a 100644 --- a/src/display.rs +++ b/contrib/vk-graph-window/src/swapchain.rs @@ -1,20 +1,6 @@ //! Resource leasing and pooling types. use { - super::{ - Graph, - driver::{ - CommandBuffer, CommandBufferInfo, DescriptorPool, DescriptorPoolInfo, DriverError, - RenderPass, RenderPassInfo, - device::Device, - image::Image, - image_access_layout, - swapchain::{Swapchain, SwapchainError, SwapchainImage, SwapchainInfo}, - }, - node::SwapchainImageNode, - pool::Pool, - }, - ash::vk, derive_builder::{Builder, UninitializedFieldError}, log::{trace, warn}, std::{ @@ -25,60 +11,105 @@ use { thread::panicking, time::Instant, }, - vk_sync::{AccessType, ImageBarrier, cmd::pipeline_barrier}, + vk_graph::{ + driver::{ + ash::{self, vk}, + cmd_buf::{CommandBuffer, CommandBufferInfo}, + descriptor_set::{DescriptorPool, DescriptorPoolInfo}, + device::Device, + image::Image, + render_pass::{RenderPass, RenderPassInfo}, + surface::Surface, + swapchain::{self, SwapchainImage}, + sync::{cmd::pipeline_barrier, AccessType, ImageBarrier, ImageLayout}, + DriverError, + }, + node::SwapchainImageNode, + pool::Pool, + Graph, + }, }; +fn create_semaphore(device: &ash::Device) -> Result { + let create_info = vk::SemaphoreCreateInfo::default(); + let allocation_callbacks = None; + + unsafe { device.create_semaphore(&create_info, allocation_callbacks) }.map_err(|err| { + warn!("{err}"); + + DriverError::OutOfMemory + }) +} + +const fn image_access_layout(access: AccessType) -> ImageLayout { + if matches!(access, AccessType::Present | AccessType::ComputeShaderWrite) { + ImageLayout::General + } else { + ImageLayout::Optimal + } +} + +#[doc(hidden)] +#[repr(C)] +pub struct ReadOnlySwapchain { + exec_idx: usize, + execs: Box<[Execution]>, + pub info: SwapchainInfo, + pub swapchain: swapchain::Swapchain, +} + +impl Deref for ReadOnlySwapchain { + type Target = swapchain::Swapchain; + + fn deref(&self) -> &Self::Target { + &self.swapchain + } +} + /// A physical display interface. #[repr(C)] -pub struct Display { +pub struct Swapchain { exec_idx: usize, execs: Box<[Execution]>, + image_execs: Vec, /// Information used to create this resource. /// /// _Note:_ This field is read-only. #[cfg(doc)] - pub info: DisplayInfo, + pub info: SwapchainInfo, #[cfg(not(doc))] - info: DisplayInfo, + info: SwapchainInfo, /// The swapchain which supports this display. /// /// _Note:_ This field is read-only. #[cfg(doc)] - pub swapchain: Swapchain, + pub swapchain: swapchain::Swapchain, #[cfg(not(doc))] - swapchain: Swapchain, + swapchain: swapchain::Swapchain, } -#[doc(hidden)] -#[repr(C)] -pub struct DisplayRef { - exec_idx: usize, - execs: Box<[Execution]>, - pub info: DisplayInfo, - pub swapchain: Swapchain, -} - -impl Display { - /// Constructs a new `Display` object. - pub fn new( - device: &Device, - swapchain: Swapchain, - info: impl Into, - ) -> Result { - let info: DisplayInfo = info.into(); +impl Swapchain { + /// Constructs a new `Swapchain` object. + pub fn new(surface: Surface, info: impl Into) -> Result { + let info: SwapchainInfo = info.into(); assert_ne!(info.command_buffer_count, 0); + let swapchain_info: swapchain::SwapchainInfo = info.into(); + let swapchain = swapchain::Swapchain::new(surface, swapchain_info)?; + let mut execs = Vec::with_capacity(info.command_buffer_count as _); for _ in 0..info.command_buffer_count { - let cmd_buf = - CommandBuffer::create(device, CommandBufferInfo::new(info.queue_family_index))?; - let swapchain_acquired = Device::create_semaphore(device)?; - let swapchain_rendered = Device::create_semaphore(device)?; + let cmd_buf = CommandBuffer::create( + &swapchain.surface.device, + CommandBufferInfo::new(info.queue_family_index), + )?; + let swapchain_acquired = create_semaphore(&swapchain.surface.device)?; + let swapchain_rendered = create_semaphore(&swapchain.surface.device)?; execs.push(Execution { cmd_buf, @@ -92,6 +123,7 @@ impl Display { Ok(Self { exec_idx: info.command_buffer_count, execs, + image_execs: Default::default(), info, swapchain, }) @@ -99,27 +131,25 @@ impl Display { /// Gets the next available swapchain image which should be rendered to and then presented using /// [`present_image`][Self::present_image]. - pub fn acquire_next_image(&mut self) -> Result, DisplayError> { + pub fn acquire_next_image(&mut self) -> Result, SwapchainError> { self.exec_idx += 1; self.exec_idx %= self.execs.len(); let exec = &mut self.execs[self.exec_idx]; if exec.queue.is_some() { exec.cmd_buf.wait_until_executed().inspect_err(|err| { - warn!("unable to wait for display fence: {err}"); + warn!("unable to wait for swapchain fence: {err}"); })?; exec.queue = None; } - exec.cmd_buf.drop_fenced(); - unsafe { exec.cmd_buf .device .reset_fences(slice::from_ref(&exec.cmd_buf.fence)) .map_err(|err| { - warn!("unable to reset display fence: {err}"); + warn!("unable to reset swapchain fence: {err}"); DriverError::InvalidData })?; @@ -131,26 +161,36 @@ impl Display { warn!("unable to acquire next swapchain image: {err:?}"); } - let mut swapchain_image = match acquire_next_image { - Err(SwapchainError::DeviceLost) => Err(DisplayError::DeviceLost), - Err(SwapchainError::Suboptimal) => return Ok(None), - Err(SwapchainError::SurfaceLost) => Err(DisplayError::Driver(DriverError::InvalidData)), + let swapchain_image = match acquire_next_image { + Err(swapchain::SwapchainError::DeviceLost) => Err(SwapchainError::DeviceLost), + Err(swapchain::SwapchainError::Suboptimal) => return Ok(None), + Err(swapchain::SwapchainError::SurfaceLost) => { + Err(SwapchainError::Driver(DriverError::InvalidData)) + } Ok(swapchain_image) => Ok(swapchain_image), }?; - swapchain_image.exec_idx = self.exec_idx; + + while swapchain_image.idx < self.image_execs.len() as u32 { + self.image_execs.push(0); + } + + self.image_execs[swapchain_image.idx as usize] = self.exec_idx; Ok(Some(swapchain_image)) } /// Displays the given swapchain image using passes specified in `graph`, if possible. #[profiling::function] - pub fn present_image( + pub fn present_image

( &mut self, - pool: &mut impl ResolverPool, + pool: &mut P, graph: Graph, swapchain_image: SwapchainImageNode, queue_index: u32, - ) -> Result<(), DisplayError> { + ) -> Result<(), SwapchainError> + where + P: Pool + Pool, + { trace!("present_image"); let mut queue = graph.queue(); @@ -162,7 +202,8 @@ impl Display { "uninitialized swapchain image: write something each frame!", ); - let exec_idx = queue.swapchain_image(swapchain_image).exec_idx; + let image_idx = queue.resource(swapchain_image).idx; + let exec_idx = self.image_execs[image_idx as usize]; let exec = &mut self.execs[exec_idx]; debug_assert!(exec.queue.is_none()); @@ -184,7 +225,7 @@ impl Display { queue.submit_resource(swapchain_image, pool, &mut exec.cmd_buf)?; { - let swapchain_image = queue.swapchain_image(swapchain_image); + let swapchain_image = queue.resource(swapchain_image); for (access, range) in Image::access( swapchain_image, AccessType::Present, @@ -198,7 +239,7 @@ impl Display { ) { trace!( "image {:?} {:?}->{:?}", - **swapchain_image, + swapchain_image, access, AccessType::Present, ); @@ -266,14 +307,13 @@ impl Display { })? } - exec.cmd_buf.waiting = true; exec.queue = Some(queue); } let elapsed = Instant::now() - started; trace!("🔜🔜🔜 vkQueueSubmit took {} μs", elapsed.as_micros(),); - let swapchain_image = queue.swapchain_image(swapchain_image).clone(); + let swapchain_image = queue.resource(swapchain_image).clone(); self.swapchain.present_image( swapchain_image, @@ -284,7 +324,7 @@ impl Display { // Store the resolved graph because it contains bindings, leases, and other shared resources // that need to be kept alive until the fence is waited upon. - exec.cmd_buf.push_fenced_drop(queue); + exec.cmd_buf.drop_after_executed(queue); Ok(()) } @@ -292,27 +332,27 @@ impl Display { /// Updates the information which controls the swapchain. /// /// Previously acquired swapchain images should be discarded after calling this function. - pub fn update_swapchain(&mut self, info: impl Into) { - self.swapchain.update(info); + pub fn set_info(&mut self, info: impl Into) { + self.swapchain.set_info(info); } } -impl Debug for Display { +impl Debug for Swapchain { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str("Display") + f.write_str("Swapchain") } } #[doc(hidden)] -impl Deref for Display { - type Target = DisplayRef; +impl Deref for Swapchain { + type Target = ReadOnlySwapchain; fn deref(&self) -> &Self::Target { unsafe { &*(self as *const Self as *const Self::Target) } } } -impl Drop for Display { +impl Drop for Swapchain { fn drop(&mut self) { if panicking() { return; @@ -352,7 +392,7 @@ impl Drop for Display { /// Describes error conditions relating to physical displays. #[derive(Debug)] -pub enum DisplayError { +pub enum SwapchainError { /// Unrecoverable device error; must destroy this device and display and start a new one DeviceLost, @@ -360,84 +400,169 @@ pub enum DisplayError { Driver(DriverError), } -impl Error for DisplayError {} +impl Error for SwapchainError {} -impl From<()> for DisplayError { +impl From<()> for SwapchainError { fn from(_: ()) -> Self { Self::DeviceLost } } -impl From for DisplayError { +impl From for SwapchainError { fn from(err: DriverError) -> Self { Self::Driver(err) } } -impl std::fmt::Display for DisplayError { +impl std::fmt::Display for SwapchainError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self) } } -/// Information used to create a [`Display`] instance. +/// Information used to create a [`Swapchain`] instance. #[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)] #[builder( - build_fn(private, name = "fallible_build", error = "DisplayInfoBuilderError"), + build_fn(private, name = "fallible_build", error = "SwapchainInfoBuilderError"), derive(Clone, Copy, Debug), pattern = "owned" )] #[non_exhaustive] -pub struct DisplayInfo { +pub struct SwapchainInfo { /// The number of command buffers to use for image submissions. /// /// Generally one more than the swapchain image count is best. #[builder(default = "4")] command_buffer_count: usize, + /// The initial height of the surface. + pub height: u32, + + /// The minimum number of presentable images that the application needs. The implementation will + /// either create the swapchain with at least that many images, or it will fail to create the + /// swapchain. + /// + /// More images introduce more display lag, but smoother animation. + #[builder(default = "2")] + pub min_image_count: u32, + + /// `vk::PresentModeKHR` Determines timing and queueing with which frames are actually displayed to the user. + /// `present_modes` is a set of these modes ordered by preference. If the first mode is not available it will fall + /// back to the next, etc... + /// + /// `vk::PresentModeKHR::FIFO` - Presentation frames are kept in a First-In-First-Out queue approximately 3 frames + /// long. Every vertical blanking period, the presentation engine will pop a frame off the queue to display. If + /// there is no frame to display, it will present the same frame again until the next vblank. + /// + /// When a present command is executed on the GPU, the presented image is added on the queue. + /// + /// * **Tearing:** No tearing will be observed. + /// * **Also known as**: "Vsync On" + /// + /// `vk::PresentModeKHR::FIFO_RELAXED` - Presentation frames are kept in a First-In-First-Out queue approximately 3 + /// frames long. Every vertical blanking period, the presentation engine will pop a frame off the queue to display. + /// If there is no frame to display, it will present the same frame until there is a frame in the queue. The moment + /// there is a frame in the queue, it will immediately pop the frame off the queue. + /// + /// When a present command is executed on the GPU, the presented image is added on the queue. + /// + /// * **Tearing**: + /// Tearing will be observed if frames last more than one vblank as the front buffer. + /// * **Also known as**: "Adaptive Vsync" + /// + /// `vk::PresentModeKHR::IMMEDIATE` - Presentation frames are not queued at all. The moment a present command is + /// executed on the GPU, the presented image is swapped onto the front buffer immediately. + /// + /// * **Tearing**: Tearing can be observed. + /// * **Also known as**: "Vsync Off" + /// + /// `vk::PresentModeKHR::MAILBOX` - Presentation frames are kept in a single-frame queue. Every vertical blanking + /// period, the presentation engine will pop a frame from the queue. If there is no frame to display, it will + /// present the same frame again until the next vblank. + /// + /// When a present command is executed on the GPU, the frame will be put into the queue. + /// If there was already a frame in the queue, the new frame will _replace_ the old frame + /// on the queue. + /// + /// * **Tearing**: No tearing will be observed. + /// * **Also known as**: "Fast Vsync" + #[builder(default = vk::PresentModeKHR::MAILBOX)] + pub present_mode: vk::PresentModeKHR, + /// The device queue family which will be used to submit and present images. #[builder(default = "0")] queue_family_index: u32, + + /// The format and color space of the surface. + pub surface: vk::SurfaceFormatKHR, + + /// The initial width of the surface. + pub width: u32, } -impl DisplayInfo { - /// Converts a `DisplayInfo` into a `DisplayInfoBuilder`. +impl SwapchainInfo { + /// Specifies a default swapchain with the given `width`, `height` and `format` values. #[inline(always)] - pub fn to_builder(self) -> DisplayInfoBuilder { - DisplayInfoBuilder { + pub fn new(width: u32, height: u32, surface: vk::SurfaceFormatKHR) -> SwapchainInfo { + Self { + command_buffer_count: 4, + height, + min_image_count: 2, + present_mode: vk::PresentModeKHR::MAILBOX, + queue_family_index: 0, + surface, + width, + } + } + + /// Converts a `SwapchainInfo` into a `SwapchainInfoBuilder`. + #[inline(always)] + pub fn to_builder(self) -> SwapchainInfoBuilder { + SwapchainInfoBuilder { command_buffer_count: Some(self.command_buffer_count), + height: Some(self.height), + min_image_count: Some(self.min_image_count), + present_mode: Some(self.present_mode), queue_family_index: Some(self.queue_family_index), + surface: Some(self.surface), + width: Some(self.width), } } } -impl Default for DisplayInfo { - fn default() -> Self { - Self { - command_buffer_count: 4, - queue_family_index: 0, - } +impl From for SwapchainInfo { + fn from(info: SwapchainInfoBuilder) -> Self { + info.build() } } -impl From for DisplayInfo { - fn from(info: DisplayInfoBuilder) -> Self { - info.build() +impl From for swapchain::SwapchainInfo { + fn from(val: SwapchainInfo) -> Self { + swapchain::SwapchainInfoBuilder::default() + .height(val.height) + .min_image_count(val.min_image_count) + .present_mode(val.present_mode) + .surface(val.surface) + .width(val.width) + .build() } } -impl DisplayInfoBuilder { - /// Builds a new `DisplayInfo`. +impl SwapchainInfoBuilder { + /// Builds a new `SwapchainInfo`. /// /// # Panics /// /// If any of the following values have not been set this function will panic: /// /// * `command_buffer_count` + /// * `width` + /// * `height` + /// * `surface` #[inline(always)] - pub fn build(self) -> DisplayInfo { + pub fn build(self) -> SwapchainInfo { let info = match self.fallible_build() { - Err(DisplayInfoBuilderError(err)) => panic!("{err}"), + Err(SwapchainInfoBuilderError(err)) => panic!("{err}"), Ok(info) => info, }; @@ -451,9 +576,9 @@ impl DisplayInfoBuilder { } #[derive(Debug)] -struct DisplayInfoBuilderError(UninitializedFieldError); +struct SwapchainInfoBuilderError(UninitializedFieldError); -impl From for DisplayInfoBuilderError { +impl From for SwapchainInfoBuilderError { fn from(err: UninitializedFieldError) -> Self { Self(err) } @@ -466,39 +591,28 @@ struct Execution { swapchain_rendered: vk::Semaphore, } -/// Combination trait which groups together all [`Pool`] traits required for a [`Resolver`] -/// instance. -/// -/// [`Resolver`]: crate::graph::Resolver -#[allow(private_bounds)] -pub trait ResolverPool: - Pool - + Pool - + Pool - + Send -{ -} - -impl ResolverPool for T where - T: Pool - + Pool - + Pool - + Send -{ -} - #[cfg(test)] mod tests { - use super::*; + use { + super::*, + std::mem::{offset_of, size_of}, + }; - type Info = DisplayInfo; - type Builder = DisplayInfoBuilder; + type Info = SwapchainInfo; + type Builder = SwapchainInfoBuilder; #[test] - pub fn display_info() { + pub fn swapchain_info() { let info = Info { command_buffer_count: 42, + height: 123, + min_image_count: 99, + present_mode: vk::PresentModeKHR::IMMEDIATE, queue_family_index: 16, + surface: vk::SurfaceFormatKHR::default() + .format(vk::Format::R8G8B8A8_UNORM) + .color_space(vk::ColorSpaceKHR::PASS_THROUGH_EXT), + width: 88, }; let builder = info.to_builder().build(); @@ -506,21 +620,37 @@ mod tests { } #[test] - pub fn display_info_builder() { + pub fn swapchain_info_builder() { let info = Info { command_buffer_count: 42, + height: 123, + min_image_count: 99, + present_mode: vk::PresentModeKHR::IMMEDIATE, queue_family_index: 16, + surface: vk::SurfaceFormatKHR::default() + .format(vk::Format::R8G8B8A8_UNORM) + .color_space(vk::ColorSpaceKHR::PASS_THROUGH_EXT), + width: 88, }; let builder = Builder::default() .command_buffer_count(42) + .height(42) + .min_image_count(99) + .present_mode(vk::PresentModeKHR::IMMEDIATE) .queue_family_index(16) + .surface( + vk::SurfaceFormatKHR::default() + .format(vk::Format::R8G8B8A8_UNORM) + .color_space(vk::ColorSpaceKHR::PASS_THROUGH_EXT), + ) + .width(88) .build(); assert_eq!(info, builder); } #[test] - pub fn display_info_default() { + pub fn swapchain_info_default() { let info = Info::default(); let builder = Builder::default().build(); @@ -529,7 +659,30 @@ mod tests { #[test] #[should_panic(expected = "Field value invalid: command_buffer_count")] - pub fn display_info_builder_uninit_command_buffer_count() { + pub fn swapchain_info_builder_uninit_command_buffer_count() { Builder::default().command_buffer_count(0).build(); } + + #[test] + pub fn swapchain_repr_c() { + // HACK: The readonly crate uses a private implementation and so we can't further deref it + // into the native object type. Because of this the ReadOnly part is manually implemented. + assert_eq!(size_of::(), size_of::()); + assert_eq!( + offset_of!(Swapchain, exec_idx), + offset_of!(ReadOnlySwapchain, exec_idx), + ); + assert_eq!( + offset_of!(Swapchain, execs), + offset_of!(ReadOnlySwapchain, execs), + ); + assert_eq!( + offset_of!(Swapchain, info), + offset_of!(ReadOnlySwapchain, info), + ); + assert_eq!( + offset_of!(Swapchain, swapchain), + offset_of!(ReadOnlySwapchain, swapchain), + ); + } } diff --git a/examples/app.rs b/examples/app.rs index 8ef28005..551bf6cf 100644 --- a/examples/app.rs +++ b/examples/app.rs @@ -5,14 +5,13 @@ use { log::error, vk_graph::{ Graph, - display::{Display, DisplayError, DisplayInfo}, driver::{ device::{Device, DeviceInfoBuilder}, surface::Surface, - swapchain::{Swapchain, SwapchainInfo}, }, pool::hash::HashPool, }, + vk_graph_window::swapchain::{Swapchain, SwapchainError, SwapchainInfo}, winit::{ application::ApplicationHandler, error::EventLoopError, @@ -50,18 +49,16 @@ impl ApplicationHandler for Application { let surface_format = Surface::linear_or_default(&surface_formats); let window_size = window.inner_size(); let swapchain = Swapchain::new( - &device, surface, SwapchainInfo::new(window_size.width, window_size.height, surface_format), ) .unwrap(); - let display_pool = HashPool::new(&device); - let display = Display::new(&device, swapchain, DisplayInfo::default()).unwrap(); + let swapchain_pool = HashPool::new(&device); self.0 = Some(Context { - display, - display_pool, + swapchain, + swapchain_pool, window, }); } @@ -77,10 +74,10 @@ impl ApplicationHandler for Application { match event { WindowEvent::CloseRequested => event_loop.exit(), WindowEvent::Resized(size) => { - let mut swapchain_info = context.display.swapchain.info; + let mut swapchain_info = context.swapchain.info; swapchain_info.width = size.width; swapchain_info.height = size.height; - context.display.update_swapchain(swapchain_info); + context.swapchain.set_info(swapchain_info); } WindowEvent::RedrawRequested => { if let Err(err) = context.draw() { @@ -105,14 +102,14 @@ struct Args { } struct Context { - display: Display, - display_pool: HashPool, + swapchain: Swapchain, + swapchain_pool: HashPool, window: Window, } impl Context { - fn draw(&mut self) -> Result<(), DisplayError> { - if let Some(swapchain_image) = self.display.acquire_next_image()? { + fn draw(&mut self) -> Result<(), SwapchainError> { + if let Some(swapchain_image) = self.swapchain.acquire_next_image()? { let mut graph = Graph::default(); let swapchain_image = graph.bind_resource(swapchain_image); @@ -120,8 +117,8 @@ impl Context { graph.clear_color_image(swapchain_image, [1.0, 0.0, 1.0]); self.window.pre_present_notify(); - self.display - .present_image(&mut self.display_pool, graph, swapchain_image, 0)?; + self.swapchain + .present_image(&mut self.swapchain_pool, graph, swapchain_image, 0)?; } Ok(()) diff --git a/examples/shader-toy/src/main.rs b/examples/shader-toy/src/main.rs index 83ffc17d..0f76972f 100644 --- a/examples/shader-toy/src/main.rs +++ b/examples/shader-toy/src/main.rs @@ -44,7 +44,7 @@ use { graphic::{GraphicPipeline, GraphicPipelineInfo}, image::ImageInfo, shader::Shader, - AccessType, + sync::AccessType, }, pool::{lazy::LazyPool, Pool as _}, Graph, diff --git a/examples/skeletal-anim/src/main.rs b/examples/skeletal-anim/src/main.rs index d5fc9f6d..ae43e398 100644 --- a/examples/skeletal-anim/src/main.rs +++ b/examples/skeletal-anim/src/main.rs @@ -24,7 +24,8 @@ use { graphic::{DepthStencilMode, GraphicPipeline, GraphicPipelineInfoBuilder}, image::{Image, ImageInfo}, shader::Shader, - AccessType, DriverError, + sync::AccessType, + DriverError, }, pool::{hash::HashPool, lazy::LazyPool, Pool as _}, Graph, diff --git a/examples/vr/src/main.rs b/examples/vr/src/main.rs index 293641d3..684d6222 100644 --- a/examples/vr/src/main.rs +++ b/examples/vr/src/main.rs @@ -27,7 +27,7 @@ use { device::Device, graphic::{DepthStencilMode, GraphicPipelineInfo}, image::{Image, ImageInfo}, - AccessType, + sync::AccessType, }, pool::{lazy::LazyPool, Pool as _}, Graph, diff --git a/src/bind.rs b/src/bind.rs index fc4e2687..b6a5c202 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -316,7 +316,7 @@ impl Bound for AnyImageNode { } impl Bound for SwapchainImageNode { - type Resource = Image; + type Resource = SwapchainImage; fn borrow(self, graph: &Graph) -> &Self::Resource { graph.resources[self.idx].as_swapchain_image().unwrap() diff --git a/src/cmd_ref/cmd_buf.rs b/src/cmd_ref/cmd_buf.rs index 2f52fe6f..512aa03e 100644 --- a/src/cmd_ref/cmd_buf.rs +++ b/src/cmd_ref/cmd_buf.rs @@ -3,11 +3,11 @@ use { crate::{ AnyAccelerationStructureNode, driver::{ - CommandBuffer, accel_struct::{ AccelerationStructureGeometry, AccelerationStructureGeometryInfo, DeviceOrHostAddress, }, + cmd_buf::CommandBuffer, device::Device, }, }, diff --git a/src/cmd_ref/compute.rs b/src/cmd_ref/compute.rs index e3dad44c..8bd06c3b 100644 --- a/src/cmd_ref/compute.rs +++ b/src/cmd_ref/compute.rs @@ -2,7 +2,7 @@ use { super::{Resources, pipeline::PipelineRef}, crate::{ AnyBufferNode, - driver::{CommandBuffer, compute::ComputePipeline}, + driver::{cmd_buf::CommandBuffer, compute::ComputePipeline}, }, ash::vk, log::trace, diff --git a/src/cmd_ref/graphic.rs b/src/cmd_ref/graphic.rs index 2787603a..ac2d92a2 100644 --- a/src/cmd_ref/graphic.rs +++ b/src/cmd_ref/graphic.rs @@ -3,7 +3,7 @@ use { crate::{ AnyBufferNode, AnyImageNode, Area, Attachment, ClearColorValue, Node, driver::{ - CommandBuffer, + cmd_buf::CommandBuffer, graphic::{DepthStencilMode, GraphicPipeline}, image::{ ImageInfo, ImageViewInfo, image_subresource_range_contains, diff --git a/src/cmd_ref/mod.rs b/src/cmd_ref/mod.rs index 7a95e466..ccb5a29a 100644 --- a/src/cmd_ref/mod.rs +++ b/src/cmd_ref/mod.rs @@ -24,9 +24,9 @@ use { SwapchainImageNode, }, crate::driver::{ - CommandBuffer, accel_struct::{AccelerationStructure, AccelerationStructureRange}, buffer::{Buffer, BufferSubresourceRange}, + cmd_buf::CommandBuffer, image::{Image, ImageViewInfo}, }, ash::vk, diff --git a/src/cmd_ref/ray_trace.rs b/src/cmd_ref/ray_trace.rs index a434522c..6d628564 100644 --- a/src/cmd_ref/ray_trace.rs +++ b/src/cmd_ref/ray_trace.rs @@ -1,6 +1,6 @@ use { super::{PipelineRef, Resources}, - crate::driver::{CommandBuffer, device::Device, ray_trace::RayTracePipeline}, + crate::driver::{cmd_buf::CommandBuffer, device::Device, ray_trace::RayTracePipeline}, ash::vk, log::trace, std::ops::Deref, diff --git a/src/driver/cmd_buf.rs b/src/driver/cmd_buf.rs index 665de5e5..36bfb9df 100644 --- a/src/driver/cmd_buf.rs +++ b/src/driver/cmd_buf.rs @@ -1,3 +1,5 @@ +//! TODO + use { super::{DriverError, device::Device}, ash::vk, @@ -20,7 +22,12 @@ pub struct CommandBuffer { pub device: Device, droppables: Vec>, - pub(crate) fence: vk::Fence, // Keeps state because everyone wants this + + /// The native Vulkan fence handle of this command buffer. + /// + /// _Note:_ This field is read-only. + #[readonly] + pub fence: vk::Fence, /// The native Vulkan resource handle of this command buffer. /// @@ -33,12 +40,12 @@ pub struct CommandBuffer { pub info: CommandBufferInfo, pub(crate) pool: vk::CommandPool, - pub(crate) waiting: bool, } impl CommandBuffer { + /// TODO #[profiling::function] - pub(crate) fn create(device: &Device, info: CommandBufferInfo) -> Result { + pub fn create(device: &Device, info: CommandBufferInfo) -> Result { let device = device.clone(); let pool = unsafe { @@ -81,13 +88,17 @@ impl CommandBuffer { handle, info, pool, - waiting: false, }) } + /// Drops an item after execution has been completed. + pub fn drop_after_executed(&mut self, x: impl Debug + Send + 'static) { + self.droppables.push(Box::new(x)); + } + /// Signals that execution has completed and it is time to drop anything we collected. #[profiling::function] - pub(crate) fn drop_fenced(&mut self) { + fn drop_fenced(&mut self) { if !self.droppables.is_empty() { trace!("dropping {} shared references", self.droppables.len()); } @@ -119,23 +130,19 @@ impl CommandBuffer { } } - /// Drops an item after execution has been completed - pub(crate) fn push_fenced_drop(&mut self, thing_to_drop: impl Debug + Send + 'static) { - self.droppables.push(Box::new(thing_to_drop)); - } - /// Stalls by blocking the current thread until the GPU has executed the previous submission to /// this command buffer. /// /// See [`Self::has_executed`] to check without blocking. #[profiling::function] pub fn wait_until_executed(&mut self) -> Result<(), DriverError> { - if !self.waiting { + if self.droppables.is_empty() { return Ok(()); } Device::wait_for_fence(&self.device, &self.fence)?; - self.waiting = false; + + self.drop_fenced(); Ok(()) } @@ -148,12 +155,10 @@ impl Drop for CommandBuffer { return; } - if self.waiting && Device::wait_for_fence(&self.device, &self.fence).is_err() { + if self.wait_until_executed().is_err() { return; } - self.drop_fenced(); - unsafe { self.device .free_command_buffers(self.pool, slice::from_ref(&self.handle)); @@ -180,6 +185,7 @@ pub struct CommandBufferInfo { } impl CommandBufferInfo { + /// TODO pub fn new(queue_family_index: u32) -> Self { Self { queue_family_index } } diff --git a/src/driver/descriptor_set.rs b/src/driver/descriptor_set.rs index 33d04cb3..b56fd18e 100644 --- a/src/driver/descriptor_set.rs +++ b/src/driver/descriptor_set.rs @@ -1,3 +1,5 @@ +//! TODO + use { super::{DescriptorSetLayout, DriverError, device::Device}, ash::vk, @@ -5,6 +7,7 @@ use { std::{ops::Deref, slice, thread::panicking}, }; +/// TODO #[derive(Debug)] #[readonly::make] pub struct DescriptorPool { @@ -29,7 +32,7 @@ pub struct DescriptorPool { impl DescriptorPool { #[profiling::function] - pub fn create( + pub(crate) fn create( device: &Device, info: impl Into, ) -> Result { @@ -160,7 +163,7 @@ impl DescriptorPool { }) } - pub fn allocate_descriptor_set( + pub(crate) fn allocate_descriptor_set( this: &Self, layout: &DescriptorSetLayout, ) -> Result { @@ -170,7 +173,7 @@ impl DescriptorPool { } #[profiling::function] - pub fn allocate_descriptor_sets<'a>( + pub(crate) fn allocate_descriptor_sets<'a>( &'a self, layout: &DescriptorSetLayout, count: u32, @@ -219,25 +222,26 @@ impl Drop for DescriptorPool { } } +/// TODO #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] pub struct DescriptorPoolInfo { - pub acceleration_structure_count: u32, - pub combined_image_sampler_count: u32, - pub input_attachment_count: u32, - pub max_sets: u32, - pub sampled_image_count: u32, - pub sampler_count: u32, - pub storage_buffer_count: u32, - pub storage_buffer_dynamic_count: u32, - pub storage_image_count: u32, - pub storage_texel_buffer_count: u32, - pub uniform_buffer_count: u32, - pub uniform_buffer_dynamic_count: u32, - pub uniform_texel_buffer_count: u32, + pub(crate) acceleration_structure_count: u32, + pub(crate) combined_image_sampler_count: u32, + pub(crate) input_attachment_count: u32, + pub(crate) max_sets: u32, + pub(crate) sampled_image_count: u32, + pub(crate) sampler_count: u32, + pub(crate) storage_buffer_count: u32, + pub(crate) storage_buffer_dynamic_count: u32, + pub(crate) storage_image_count: u32, + pub(crate) storage_texel_buffer_count: u32, + pub(crate) uniform_buffer_count: u32, + pub(crate) uniform_buffer_dynamic_count: u32, + pub(crate) uniform_texel_buffer_count: u32, } impl DescriptorPoolInfo { - pub fn is_empty(&self) -> bool { + pub(crate) fn is_empty(&self) -> bool { self.acceleration_structure_count + self.combined_image_sampler_count + self.input_attachment_count @@ -255,7 +259,7 @@ impl DescriptorPoolInfo { } #[derive(Debug)] -pub struct DescriptorSet { +pub(crate) struct DescriptorSet { descriptor_pool: vk::DescriptorPool, descriptor_set: vk::DescriptorSet, device: Device, diff --git a/src/driver/device.rs b/src/driver/device.rs index 4fe6274c..2fd5223f 100644 --- a/src/driver/device.rs +++ b/src/driver/device.rs @@ -141,17 +141,6 @@ impl Device { }) } - pub(crate) fn create_semaphore(this: &Self) -> Result { - let create_info = vk::SemaphoreCreateInfo::default(); - let allocation_callbacks = None; - - unsafe { this.create_semaphore(&create_info, allocation_callbacks) }.map_err(|err| { - warn!("{err}"); - - DriverError::OutOfMemory - }) - } - /// Helper for times when you already know that the device supports the acceleration /// structure extension. /// @@ -575,7 +564,10 @@ impl Deref for ReadOnlyDevice { #[cfg(test)] mod tests { - use {super::*, std::mem::offset_of}; + use { + super::*, + std::mem::{offset_of, size_of}, + }; type Info = DeviceInfo; type Builder = DeviceInfoBuilder; @@ -584,6 +576,7 @@ mod tests { pub fn device_repr_c() { // HACK: The readonly crate uses a private implementation and so we can't further deref it // into the native object type. Because of this the ReadOnly part is manually implemented. + assert_eq!(size_of::(), size_of::()); assert_eq!(offset_of!(Device, inner), offset_of!(ReadOnlyDevice, inner),); assert_eq!( offset_of!(Device, physical_device), diff --git a/src/driver/instance.rs b/src/driver/instance.rs index 48046478..f140cae7 100644 --- a/src/driver/instance.rs +++ b/src/driver/instance.rs @@ -554,12 +554,16 @@ impl Deref for ReadOnlyInstance { #[cfg(test)] mod tests { - use {super::*, std::mem::offset_of}; + use { + super::*, + std::mem::{offset_of, size_of}, + }; #[test] pub fn instance_repr_c() { // HACK: The readonly crate uses a private implementation and so we can't further deref it // into the native object type. Because of this the ReadOnly part is manually implemented. + assert_eq!(size_of::(), size_of::()); assert_eq!( offset_of!(Instance, entry), offset_of!(ReadOnlyInstance, entry), diff --git a/src/driver/mod.rs b/src/driver/mod.rs index 554d0bac..08a96641 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -28,7 +28,9 @@ pub mod accel_struct; pub mod buffer; +pub mod cmd_buf; pub mod compute; +pub mod descriptor_set; pub mod device; pub mod graphic; pub mod image; @@ -40,14 +42,11 @@ pub mod shader; pub mod surface; pub mod swapchain; -mod cmd_buf; -mod descriptor_set; mod descriptor_set_layout; pub use { - self::cmd_buf::CommandBuffer, ash::{self}, - vk_sync::AccessType, + vk_sync::{self as sync}, }; /// Specifying depth and stencil resolve modes. @@ -55,12 +54,11 @@ pub use { pub type ResolveMode = self::render_pass::ResolveMode; pub(crate) use self::{ - cmd_buf::CommandBufferInfo, - descriptor_set::{DescriptorPool, DescriptorPoolInfo, DescriptorSet}, + descriptor_set::DescriptorSet, descriptor_set_layout::DescriptorSetLayout, render_pass::{ - AttachmentInfo, AttachmentRef, FramebufferAttachmentImageInfo, FramebufferInfo, RenderPass, - RenderPassInfo, SubpassDependency, SubpassInfo, + AttachmentInfo, AttachmentRef, FramebufferAttachmentImageInfo, FramebufferInfo, + SubpassDependency, SubpassInfo, }, shader::{Descriptor, DescriptorBindingMap, DescriptorInfo}, surface::Surface, @@ -79,10 +77,12 @@ use { error::Error, fmt::{Display, Formatter}, }, - vk_sync::ImageLayout, }; -//pub type Result = Result; +// When removing, fix all the extra-overly-qualiified references in this file +#[deprecated = "use from sync module"] +#[doc(hidden)] +pub type AccessType = self::sync::AccessType; pub(super) const fn format_aspect_mask(fmt: vk::Format) -> vk::ImageAspectFlags { match fmt { @@ -635,24 +635,18 @@ pub(super) const fn image_subresource_range_from_layers( } } -pub(super) const fn image_access_layout(access: AccessType) -> ImageLayout { - if matches!(access, AccessType::Present | AccessType::ComputeShaderWrite) { - ImageLayout::General - } else { - ImageLayout::Optimal - } -} - -pub(super) const fn initial_image_layout_access(ty: AccessType) -> AccessType { - use AccessType::*; +pub(super) const fn initial_image_layout_access( + ty: self::sync::AccessType, +) -> self::sync::AccessType { + use self::sync::AccessType::*; match ty { DepthStencilAttachmentReadWrite => DepthStencilAttachmentRead, _ => ty, } } -pub(super) const fn is_read_access(ty: AccessType) -> bool { - use AccessType::*; +pub(super) const fn is_read_access(ty: self::sync::AccessType) -> bool { + use self::sync::AccessType::*; match ty { Nothing | CommandBufferWriteNVX @@ -725,8 +719,8 @@ pub(super) const fn is_read_access(ty: AccessType) -> bool { } } -pub(super) const fn is_write_access(ty: AccessType) -> bool { - use AccessType::*; +pub(super) const fn is_write_access(ty: self::sync::AccessType) -> bool { + use self::sync::AccessType::*; match ty { Nothing | CommandBufferReadNVX @@ -901,11 +895,11 @@ fn merge_push_constant_ranges(pcr: &[vk::PushConstantRange]) -> Vec (vk::PipelineStageFlags, vk::AccessFlags) { use { - AccessType as ty, vk::{AccessFlags as access, PipelineStageFlags as stage}, + vk_sync::AccessType as ty, }; match access_type { diff --git a/src/driver/render_pass.rs b/src/driver/render_pass.rs index 109a40d5..61a9026e 100644 --- a/src/driver/render_pass.rs +++ b/src/driver/render_pass.rs @@ -93,16 +93,10 @@ struct GraphicPipelineKey { subpass_idx: u32, } -#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] -pub(crate) struct RenderPassInfo { - pub attachments: Vec, - pub subpasses: Vec, - pub dependencies: Vec, -} - +/// TODO #[derive(Debug)] #[readonly::make] -pub(crate) struct RenderPass { +pub struct RenderPass { /// The device which owns this render pass resource. /// /// _Note:_ This field is read-only. @@ -127,7 +121,7 @@ pub(crate) struct RenderPass { impl RenderPass { #[profiling::function] - pub fn create(device: &Device, info: RenderPassInfo) -> Result { + pub(crate) fn create(device: &Device, info: RenderPassInfo) -> Result { //trace!("create: \n{:#?}", &info); trace!("create"); @@ -262,7 +256,10 @@ impl RenderPass { } #[profiling::function] - pub fn framebuffer(&mut self, info: FramebufferInfo) -> Result { + pub(crate) fn framebuffer( + &mut self, + info: FramebufferInfo, + ) -> Result { debug_assert!(!info.attachments.is_empty()); let entry = self.framebuffers.entry(info); @@ -319,7 +316,7 @@ impl RenderPass { } #[profiling::function] - pub fn pipeline_handle( + pub(crate) fn pipeline_handle( &mut self, pipeline: &GraphicPipeline, depth_stencil: Option, @@ -458,6 +455,14 @@ impl Drop for RenderPass { } } +/// TODO +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] +pub struct RenderPassInfo { + pub(crate) attachments: Vec, + pub(crate) subpasses: Vec, + pub(crate) dependencies: Vec, +} + /// Specifying depth and stencil resolve modes. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum ResolveMode { diff --git a/src/driver/swapchain.rs b/src/driver/swapchain.rs index 88e418e0..a0551622 100644 --- a/src/driver/swapchain.rs +++ b/src/driver/swapchain.rs @@ -14,15 +14,26 @@ use { // TODO: This needs to track completed command buffers and not constantly create semaphores +#[doc(hidden)] +#[repr(C)] +pub struct ReadOnlySwapchainImage { + pub idx: u32, + image: Image, +} + +#[doc(hidden)] +impl Deref for ReadOnlySwapchainImage { + type Target = Image; + + fn deref(&self) -> &Self::Target { + &self.image + } +} + /// Provides the ability to present rendering results to a [`Surface`]. #[derive(Debug)] #[readonly::make] pub struct Swapchain { - /// The device which owns this buffer resource. - /// - /// _Note:_ This field is read-only. - pub device: Device, - /// The native Vulkan resource handle of this swapchain. /// /// _Note:_ This field is read-only. @@ -48,16 +59,10 @@ impl Swapchain { /// Prepares a [`vk::SwapchainKHR`] object which is lazily created after calling /// [`acquire_next_image`][Self::acquire_next_image]. #[profiling::function] - pub fn new( - device: &Device, - surface: Surface, - info: impl Into, - ) -> Result { - let device = device.clone(); + pub fn new(surface: Surface, info: impl Into) -> Result { let info = info.into(); Ok(Swapchain { - device, images: Default::default(), handle: vk::SwapchainKHR::null(), handle_prev: vk::SwapchainKHR::null(), @@ -85,7 +90,7 @@ impl Swapchain { })?; } - let swapchain_ext = Device::expect_swapchain_ext(&self.device); + let swapchain_ext = Device::expect_swapchain_ext(&self.surface.device); let image_idx = unsafe { swapchain_ext.acquire_next_image(self.handle, u64::MAX, acquired, vk::Fence::null()) @@ -201,17 +206,17 @@ impl Swapchain { let present_info = vk::PresentInfoKHR::default() .wait_semaphores(wait_semaphores) .swapchains(slice::from_ref(&self.handle)) - .image_indices(slice::from_ref(&image.image_idx)); + .image_indices(slice::from_ref(&image.idx)); - let swapchain_ext = Device::expect_swapchain_ext(&self.device); + let swapchain_ext = Device::expect_swapchain_ext(&self.surface.device); unsafe { match swapchain_ext.queue_present( - Device::queue(&self.device, queue_family_index, queue_index), + Device::queue(&self.surface.device, queue_family_index, queue_index), &present_info, ) { Ok(_) => { - Self::destroy_swapchain(&self.device, &mut self.handle_prev); + Self::destroy_swapchain(&self.surface.device, &mut self.handle_prev); } Err(err) if err == vk::Result::ERROR_DEVICE_LOST @@ -232,13 +237,13 @@ impl Swapchain { } } - let image_idx = image.image_idx as usize; + let image_idx = image.idx as usize; self.images[image_idx] = image; } #[profiling::function] fn recreate_swapchain(&mut self) -> Result<(), DriverError> { - Self::destroy_swapchain(&self.device, &mut self.handle_prev); + Self::destroy_swapchain(&self.surface.device, &mut self.handle_prev); let surface_caps = Surface::capabilities(&self.surface)?; @@ -277,7 +282,7 @@ impl Swapchain { surface_caps.current_transform }; - let swapchain_ext = Device::expect_swapchain_ext(&self.device); + let swapchain_ext = Device::expect_swapchain_ext(&self.surface.device); let swapchain_create_info = vk::SwapchainCreateInfoKHR::default() .surface(self.surface.handle) .min_image_count(min_image_count) @@ -315,7 +320,7 @@ impl Swapchain { .enumerate() .map(|(image_idx, image)| { let mut image = Image::from_raw( - &self.device, + &self.surface.device, image, ImageInfo::image_2d( surface_width, @@ -329,9 +334,8 @@ impl Swapchain { image.name = Some(format!("swapchain{image_idx}")); Ok(SwapchainImage { - exec_idx: 0, + idx: image_idx, image, - image_idx, }) }) .collect::, _>>()?; @@ -355,6 +359,27 @@ impl Swapchain { Ok(()) } + /// Updates the information which controls this swapchain. + /// + /// Previously acquired swapchain images should be discarded after calling this function. + pub fn set_info(&mut self, info: impl Into) { + let info: SwapchainInfo = info.into(); + + if self.info != info { + // attempt to reducing flickering when resizing windows on mac + #[cfg(target_os = "macos")] + if let Err(err) = unsafe { self.device.device_wait_idle() } { + warn!("device_wait_idle() failed: {err}"); + } + + self.info = info; + + trace!("info: {:?}", self.info); + + self.suboptimal = true; + } + } + fn supported_surface_usage( &mut self, surface_capabilities: vk::ImageUsageFlags, @@ -368,6 +393,7 @@ impl Swapchain { } if self + .surface .device .physical_device .image_format_properties( @@ -397,27 +423,6 @@ impl Swapchain { Ok(res) } - - /// Updates the information which controls this swapchain. - /// - /// Previously acquired swapchain images should be discarded after calling this function. - pub fn update(&mut self, info: impl Into) { - let info: SwapchainInfo = info.into(); - - if self.info != info { - // attempt to reducing flickering when resizing windows on mac - #[cfg(target_os = "macos")] - if let Err(err) = unsafe { self.device.device_wait_idle() } { - warn!("device_wait_idle() failed: {err}"); - } - - self.info = info; - - trace!("info: {:?}", self.info); - - self.suboptimal = true; - } - } } impl Drop for Swapchain { @@ -427,8 +432,8 @@ impl Drop for Swapchain { return; } - Self::destroy_swapchain(&self.device, &mut self.handle_prev); - Self::destroy_swapchain(&self.device, &mut self.handle); + Self::destroy_swapchain(&self.surface.device, &mut self.handle_prev); + Self::destroy_swapchain(&self.surface.device, &mut self.handle); } } @@ -447,33 +452,37 @@ pub enum SwapchainError { /// An opaque type representing a swapchain image. #[derive(Debug)] +#[repr(C)] pub struct SwapchainImage { - pub(crate) exec_idx: usize, + /// The index of this swapchain among the other swapchain images. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub idx: u32, + + #[cfg(not(doc))] + idx: u32, + image: Image, - image_idx: u32, } impl Clone for SwapchainImage { fn clone(&self) -> Self { - let Self { - exec_idx, - image, - image_idx, - } = self; + let Self { idx, image } = self; Self { - exec_idx: *exec_idx, - image: Image::clone_swapchain(image), - image_idx: *image_idx, + idx: *idx, + image: image.clone_swapchain(), } } } +#[doc(hidden)] impl Deref for SwapchainImage { - type Target = Image; + type Target = ReadOnlySwapchainImage; fn deref(&self) -> &Self::Target { - &self.image + unsafe { &*(self as *const Self as *const Self::Target) } } } @@ -497,9 +506,6 @@ pub struct SwapchainInfo { #[builder(default = "2")] pub min_image_count: u32, - /// The format and color space of the surface. - pub surface: vk::SurfaceFormatKHR, - /// `vk::PresentModeKHR` Determines timing and queueing with which frames are actually displayed to the user. /// `present_modes` is a set of these modes ordered by preference. If the first mode is not available it will fall /// back to the next, etc... @@ -543,6 +549,9 @@ pub struct SwapchainInfo { #[builder(default = vk::PresentModeKHR::MAILBOX)] pub present_mode: vk::PresentModeKHR, + /// The format and color space of the surface. + pub surface: vk::SurfaceFormatKHR, + /// The initial width of the surface. pub width: u32, } @@ -609,11 +618,32 @@ impl From for SwapchainInfoBuilderError { #[cfg(test)] mod tests { - use super::*; + use { + super::*, + std::mem::{offset_of, size_of}, + }; type Info = SwapchainInfo; type Builder = SwapchainInfoBuilder; + #[test] + pub fn swapchain_image_repr_c() { + // HACK: The readonly crate uses a private implementation and so we can't further deref it + // into the native object type. Because of this the ReadOnly part is manually implemented. + assert_eq!( + size_of::(), + size_of::() + ); + assert_eq!( + offset_of!(SwapchainImage, idx), + offset_of!(ReadOnlySwapchainImage, idx), + ); + assert_eq!( + offset_of!(SwapchainImage, image), + offset_of!(ReadOnlySwapchainImage, image), + ); + } + #[test] pub fn swapchain_info() { let info = Info::new(20, 24, vk::SurfaceFormatKHR::default()); diff --git a/src/edge.rs b/src/edge.rs deleted file mode 100644 index 8bec1adb..00000000 --- a/src/edge.rs +++ /dev/null @@ -1,103 +0,0 @@ -use { - super::{ - AccelerationStructureLeaseNode, AccelerationStructureNode, BufferLeaseNode, BufferNode, - Graph, ImageLeaseNode, ImageNode, Resolver, SwapchainImageNode, - cmd_ref::{CommandRef, PipelineRef}, - }, - crate::{ - driver::{ - accel_struct::AccelerationStructure, buffer::Buffer, compute::ComputePipeline, - graphic::GraphicPipeline, image::Image, ray_trace::RayTracePipeline, - swapchain::SwapchainImage, - }, pool::Lease - }, - std::sync::Arc, -}; - -/// A marker trait that says some graph object can transition into a different -/// graph object; it is a one-way transition unless the other direction has -/// been implemented too. -pub trait Edge { - type Result; -} - -macro_rules! node { - ($src:ty => $dst:ty) => { - impl Edge for $src { - type Result = $dst; - } - }; -} - -// Edges that can be bound as nodes to the graph: -// Ex: Graph::bind_resource(&mut self, binding: X) -> Y -node!(AccelerationStructure => AccelerationStructureNode); -node!(Arc => AccelerationStructureNode); -node!(Lease => AccelerationStructureLeaseNode); -node!(Arc> => AccelerationStructureLeaseNode); -node!(Buffer => BufferNode); -node!(Arc => BufferNode); -node!(Lease => BufferLeaseNode); -node!(Arc> => BufferLeaseNode); -node!(Image => ImageNode); -node!(Arc => ImageNode); -node!(Lease => ImageLeaseNode); -node!(Arc> => ImageLeaseNode); -node!(SwapchainImage => SwapchainImageNode); - -// Edges that can be borrowed from the graph: -// Ex: Graph::node(&mut self, node: X) -> Y -node!(AccelerationStructureNode => Arc); -node!(AccelerationStructureLeaseNode => Arc>); -node!(BufferNode => Arc); -node!(BufferLeaseNode => Arc>); -node!(ImageNode => Arc); -node!(ImageLeaseNode => Arc>); -node!(SwapchainImageNode => SwapchainImage); - -macro_rules! node_ref { - ($src:ty => $dst:ty) => { - impl<'a> Edge for &'a $src { - type Result = $dst; - } - }; -} - -node_ref!(Arc => AccelerationStructureNode); -node_ref!(Arc> => AccelerationStructureLeaseNode); -node_ref!(Arc => BufferNode); -node_ref!(Arc> => BufferLeaseNode); -node_ref!(Arc => ImageNode); -node_ref!(Arc> => ImageLeaseNode); - -// Specialized edges for pipelines added to a pass: -// Ex: PassRef::bind_pipeline(&mut self, pipeline: X) -> PipelineRef -macro_rules! pipeline { - ($name:ident) => { - paste::paste! { - impl<'a> Edge> for &'a [<$name Pipeline>] { - type Result = PipelineRef<'a, [<$name Pipeline>]>; - } - - impl<'a> Edge> for [<$name Pipeline>] { - type Result = PipelineRef<'a, [<$name Pipeline>]>; - } - } - }; -} - -pipeline!(Compute); -pipeline!(Graphic); -pipeline!(RayTrace); - -macro_rules! resolve { - ($src:ident -> $dst:ident) => { - impl Edge for $src { - type Result = $dst; - } - }; -} - -// Edges that can be unbound from a resolved graph: -// (You get the full real actual swapchain image woo hoo!) -resolve!(SwapchainImageNode -> SwapchainImage); diff --git a/src/lib.rs b/src/lib.rs index 870f89fa..18662182 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -351,7 +351,6 @@ layout. #![warn(missing_docs)] pub mod cmd_ref; -pub mod display; pub mod driver; pub mod node; pub mod pool; @@ -377,7 +376,8 @@ use { }, }, crate::driver::{ - CommandBuffer, DescriptorBindingMap, + DescriptorBindingMap, + cmd_buf::CommandBuffer, compute::ComputePipeline, format_aspect_mask, format_texel_block_extent, format_texel_block_size, graphic::{DepthStencilMode, GraphicPipeline}, diff --git a/src/pool/fifo.rs b/src/pool/fifo.rs index 10c194e1..cfd03982 100644 --- a/src/pool/fifo.rs +++ b/src/pool/fifo.rs @@ -3,12 +3,14 @@ use { super::{Cache, Lease, Pool, PoolInfo, lease_command_buffer}, crate::driver::{ - CommandBuffer, CommandBufferInfo, DescriptorPool, DescriptorPoolInfo, DriverError, - RenderPass, RenderPassInfo, + DriverError, accel_struct::{AccelerationStructure, AccelerationStructureInfo}, buffer::{Buffer, BufferInfo}, + cmd_buf::{CommandBuffer, CommandBufferInfo}, + descriptor_set::{DescriptorPool, DescriptorPoolInfo}, device::Device, image::{Image, ImageInfo}, + render_pass::{RenderPass, RenderPassInfo}, }, log::debug, std::{collections::HashMap, sync::Arc}, @@ -182,7 +184,7 @@ impl Pool for FifoPool { .entry(info.queue_family_index) .or_insert_with(PoolInfo::default_cache); - let mut item = { + let item = { #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))] let mut cache = cache_ref.lock(); @@ -199,7 +201,7 @@ impl Pool for FifoPool { })?; // Drop anything we were holding from the last submission - item.drop_fenced(); + //item.wait_until_executed()?; Ok(Lease::new(Arc::downgrade(cache_ref), item)) } diff --git a/src/pool/hash.rs b/src/pool/hash.rs index f9279c4a..553ce0ad 100644 --- a/src/pool/hash.rs +++ b/src/pool/hash.rs @@ -3,12 +3,14 @@ use { super::{Cache, Lease, Pool, PoolInfo, lease_command_buffer}, crate::driver::{ - CommandBuffer, CommandBufferInfo, DescriptorPool, DescriptorPoolInfo, DriverError, - RenderPass, RenderPassInfo, + DriverError, accel_struct::{AccelerationStructure, AccelerationStructureInfo}, buffer::{Buffer, BufferInfo}, + cmd_buf::{CommandBuffer, CommandBufferInfo}, + descriptor_set::{DescriptorPool, DescriptorPoolInfo}, device::Device, image::{Image, ImageInfo}, + render_pass::{RenderPass, RenderPassInfo}, }, log::debug, paste::paste, @@ -133,7 +135,7 @@ impl Pool for HashPool { .command_buffer_cache .entry(info.queue_family_index) .or_insert_with(PoolInfo::default_cache); - let mut item = { + let item = { #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))] let mut cache = cache_ref.lock(); @@ -150,7 +152,7 @@ impl Pool for HashPool { })?; // Drop anything we were holding from the last submission - item.drop_fenced(); + //item.wait_until_executed()?; Ok(Lease::new(Arc::downgrade(cache_ref), item)) } diff --git a/src/pool/lazy.rs b/src/pool/lazy.rs index a1d9c428..853e5c23 100644 --- a/src/pool/lazy.rs +++ b/src/pool/lazy.rs @@ -3,12 +3,14 @@ use { super::{Cache, Lease, Pool, PoolInfo, lease_command_buffer}, crate::driver::{ - CommandBuffer, CommandBufferInfo, DescriptorPool, DescriptorPoolInfo, DriverError, - RenderPass, RenderPassInfo, + DriverError, accel_struct::{AccelerationStructure, AccelerationStructureInfo}, buffer::{Buffer, BufferInfo}, + cmd_buf::{CommandBuffer, CommandBufferInfo}, + descriptor_set::{DescriptorPool, DescriptorPoolInfo}, device::Device, image::{Image, ImageInfo, SampleCount}, + render_pass::{RenderPass, RenderPassInfo}, }, ash::vk, log::debug, @@ -246,7 +248,7 @@ impl Pool for LazyPool { .command_buffer_cache .entry(info.queue_family_index) .or_insert_with(PoolInfo::default_cache); - let mut item = { + let item = { #[cfg_attr(not(feature = "parking_lot"), allow(unused_mut))] let mut cache = cache_ref.lock(); @@ -263,7 +265,7 @@ impl Pool for LazyPool { })?; // Drop anything we were holding from the last submission - item.drop_fenced(); + //item.wait_until_executed()?; Ok(Lease::new(Arc::downgrade(cache_ref), item)) } diff --git a/src/pool/mod.rs b/src/pool/mod.rs index 0a7332f9..01935b19 100644 --- a/src/pool/mod.rs +++ b/src/pool/mod.rs @@ -91,11 +91,12 @@ pub mod lazy; use { crate::driver::{ - CommandBuffer, DriverError, + DriverError, accel_struct::{ AccelerationStructure, AccelerationStructureInfo, AccelerationStructureInfoBuilder, }, buffer::{Buffer, BufferInfo, BufferInfoBuilder}, + cmd_buf::CommandBuffer, image::{Image, ImageInfo, ImageInfoBuilder}, }, derive_builder::{Builder, UninitializedFieldError}, diff --git a/src/queue.rs b/src/queue.rs index 48e6d89a..aa213b46 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -2,23 +2,23 @@ use { super::{ Area, Attachment, Command, ExecutionPipeline, Graph, Node, NodeIndex, Resource, cmd_ref::{Resources, SubresourceAccess, SubresourceRange}, - node::SwapchainImageNode, }, crate::{ + Bound, driver::{ - AttachmentInfo, AttachmentRef, CommandBuffer, CommandBufferInfo, Descriptor, - DescriptorInfo, DescriptorPool, DescriptorPoolInfo, DescriptorSet, DriverError, - FramebufferAttachmentImageInfo, FramebufferInfo, RenderPass, RenderPassInfo, - SubpassDependency, SubpassInfo, + AttachmentInfo, AttachmentRef, Descriptor, DescriptorInfo, DescriptorSet, DriverError, + FramebufferAttachmentImageInfo, FramebufferInfo, SubpassDependency, SubpassInfo, accel_struct::AccelerationStructure, buffer::Buffer, + cmd_buf::{CommandBuffer, CommandBufferInfo}, + descriptor_set::{DescriptorPool, DescriptorPoolInfo}, device::Device, format_aspect_mask, graphic::{DepthStencilMode, GraphicPipeline}, image::{Image, ImageAccess}, - image_access_layout, initial_image_layout_access, is_read_access, is_write_access, + initial_image_layout_access, is_read_access, is_write_access, pipeline_stage_access_flags, - swapchain::SwapchainImage, + render_pass::{RenderPass, RenderPassInfo}, }, pool::{Lease, Pool}, }, @@ -34,12 +34,22 @@ use { ops::Range, slice, }, - vk_sync::{AccessType, BufferBarrier, GlobalBarrier, ImageBarrier, cmd::pipeline_barrier}, + vk_sync::{ + AccessType, BufferBarrier, GlobalBarrier, ImageBarrier, ImageLayout, cmd::pipeline_barrier, + }, }; #[cfg(not(debug_assertions))] use std::hint::unreachable_unchecked; +const fn image_access_layout(access: AccessType) -> ImageLayout { + if matches!(access, AccessType::Present | AccessType::ComputeShaderWrite) { + ImageLayout::General + } else { + ImageLayout::Optimal + } +} + #[derive(Default)] struct AccessCache { accesses: Vec, @@ -2471,7 +2481,7 @@ impl Queue { if pass_idx == schedule_idx { // This was a scheduled pass - store it! - cmd_buf.push_fenced_drop((pass, self.physical_passes.pop().unwrap())); + cmd_buf.drop_after_executed((pass, self.physical_passes.pop().unwrap())); break; } else { debug_assert!(pass_idx > schedule_idx); @@ -2594,6 +2604,15 @@ impl Queue { }); } + /// Returns a borrow of the original Vulkan resource (buffer, image or acceleration structure) + /// which the given node represents. + pub fn resource(&self, node: N) -> &N::Resource + where + N: Bound, + { + self.graph.resource(node) + } + /// Returns the stages that process the given node. /// /// Note that this value must be retrieved before resolving a node as there will be no @@ -2864,14 +2883,12 @@ impl Queue { .map_err(|_| DriverError::OutOfMemory)?; } - cmd_buf.waiting = true; - // This graph contains references to buffers, images, and other resources which must be kept // alive until this graph execution completes on the GPU. Once those references are dropped // they will return to the pool for other things to use. The drop will happen the next time // someone tries to lease a command buffer and we notice this one has returned and the fence // has been signalled. - cmd_buf.push_fenced_drop(self); + cmd_buf.drop_after_executed(self); Ok(cmd_buf) } @@ -2956,14 +2973,6 @@ impl Queue { Ok(()) } - pub(crate) fn swapchain_image(&mut self, node: SwapchainImageNode) -> &SwapchainImage { - let Some(swapchain_image) = self.graph.resources[node.idx].as_swapchain_image() else { - panic!("invalid swapchain image node"); - }; - - swapchain_image - } - #[profiling::function] fn write_descriptor_sets( cmd_buf: &CommandBuffer, @@ -3241,8 +3250,10 @@ mod derecated { use crate::{ Queue, driver::{ - CommandBuffer, DescriptorPool, DescriptorPoolInfo, DriverError, RenderPass, - RenderPassInfo, + DriverError, + cmd_buf::CommandBuffer, + descriptor_set::{DescriptorPool, DescriptorPoolInfo}, + render_pass::{RenderPass, RenderPassInfo}, }, node::Node, pool::Pool, From 8cd18df79b983f323dba77d8a55b9b1e8c832635 Mon Sep 17 00:00:00 2001 From: John Wells Date: Wed, 25 Feb 2026 22:20:32 -0500 Subject: [PATCH 25/86] clean-up --- contrib/vk-graph-hot/src/shader.rs | 11 +-- contrib/vk-graph-imgui/src/lib.rs | 2 +- contrib/vk-graph-window/src/swapchain.rs | 4 +- src/cmd_ref/compute.rs | 19 ++--- src/cmd_ref/graphic.rs | 25 ++++-- src/cmd_ref/ray_trace.rs | 12 +-- src/driver/accel_struct.rs | 3 +- src/driver/buffer.rs | 2 +- src/driver/compute.rs | 2 +- src/driver/device.rs | 2 +- src/driver/graphic.rs | 3 +- src/driver/image.rs | 2 +- src/driver/instance.rs | 2 +- src/driver/mod.rs | 2 +- src/driver/ray_trace.rs | 3 +- src/driver/shader.rs | 2 +- src/driver/swapchain.rs | 2 +- src/pool/mod.rs | 102 ++++++++++++++++++----- src/queue.rs | 74 ++++++++-------- 19 files changed, 164 insertions(+), 110 deletions(-) diff --git a/contrib/vk-graph-hot/src/shader.rs b/contrib/vk-graph-hot/src/shader.rs index 292ee6ba..e6ae67d8 100644 --- a/contrib/vk-graph-hot/src/shader.rs +++ b/contrib/vk-graph-hot/src/shader.rs @@ -20,7 +20,7 @@ use { #[allow(missing_docs)] #[derive(Builder, Clone, Debug)] #[builder( - build_fn(private, name = "fallible_build", error = "HotShaderBuilderError"), + build_fn(private, name = "fallible_build", error = "UninitializedFieldError"), derive(Clone, Debug), pattern = "owned" )] @@ -362,12 +362,3 @@ impl HotShaderBuilder { self } } - -#[derive(Debug)] -struct HotShaderBuilderError; - -impl From for HotShaderBuilderError { - fn from(_: UninitializedFieldError) -> Self { - Self - } -} diff --git a/contrib/vk-graph-imgui/src/lib.rs b/contrib/vk-graph-imgui/src/lib.rs index 4be2ac10..12ed43e5 100644 --- a/contrib/vk-graph-imgui/src/lib.rs +++ b/contrib/vk-graph-imgui/src/lib.rs @@ -110,7 +110,7 @@ impl ImGui { | vk::ImageUsageFlags::TRANSFER_SRC, // TODO: Make TRANSFER_SRC an "extra flags" )) .unwrap(); - image.as_mut().name = Some("ImGui Output".to_string()); + image.name = Some("ImGui Output".to_string()); image }); diff --git a/contrib/vk-graph-window/src/swapchain.rs b/contrib/vk-graph-window/src/swapchain.rs index 270a626a..ea4ca8b4 100644 --- a/contrib/vk-graph-window/src/swapchain.rs +++ b/contrib/vk-graph-window/src/swapchain.rs @@ -194,7 +194,7 @@ impl Swapchain { trace!("present_image"); let mut queue = graph.queue(); - let wait_dst_stage_mask = queue.resource_stages(swapchain_image); + let wait_dst_stage_mask = queue.node_stages(swapchain_image); // The swapchain should have been written to, otherwise it would be noise and that's a panic assert!( @@ -592,7 +592,7 @@ struct Execution { } #[cfg(test)] -mod tests { +mod test { use { super::*, std::mem::{offset_of, size_of}, diff --git a/src/cmd_ref/compute.rs b/src/cmd_ref/compute.rs index 8bd06c3b..4e7afa3c 100644 --- a/src/cmd_ref/compute.rs +++ b/src/cmd_ref/compute.rs @@ -1,9 +1,6 @@ use { - super::{Resources, pipeline::PipelineRef}, - crate::{ - AnyBufferNode, - driver::{cmd_buf::CommandBuffer, compute::ComputePipeline}, - }, + super::{Resources, cmd_buf::CommandBufferRef, pipeline::PipelineRef}, + crate::{driver::compute::ComputePipeline, node::AnyBufferNode}, ash::vk, log::trace, std::ops::Deref, @@ -42,9 +39,8 @@ use { /// # Ok(()) } /// ``` pub struct ComputePipelineRef<'a> { - pub(super) cmd_buf: &'a CommandBuffer, + pub(super) cmd_buf: CommandBufferRef<'a>, pub(super) pipeline: ComputePipeline, - pub(super) resources: Resources<'a>, } impl ComputePipelineRef<'_> { @@ -218,7 +214,7 @@ impl ComputePipelineRef<'_> { unsafe { self.cmd_buf.device.cmd_dispatch_indirect( self.cmd_buf.handle, - self.resources[args_buf].handle, + self.cmd_buf.resources[args_buf].handle, args_offset, ); } @@ -321,10 +317,10 @@ impl ComputePipelineRef<'_> { } impl<'a> Deref for ComputePipelineRef<'a> { - type Target = CommandBuffer; + type Target = CommandBufferRef<'a>; fn deref(&self) -> &Self::Target { - self.cmd_buf + &self.cmd_buf } } @@ -350,9 +346,8 @@ impl PipelineRef<'_, ComputePipeline> { self.cmd.push_execute(move |cmd_buf, resources| { func( ComputePipelineRef { - cmd_buf, + cmd_buf: CommandBufferRef { cmd_buf, resources }, pipeline, - resources, }, resources, ); diff --git a/src/cmd_ref/graphic.rs b/src/cmd_ref/graphic.rs index ac2d92a2..d1482a85 100644 --- a/src/cmd_ref/graphic.rs +++ b/src/cmd_ref/graphic.rs @@ -1,9 +1,11 @@ use { - super::{AttachmentIndex, PipelineRef, Resources, SubresourceAccess, SubresourceRange}, + super::{ + AttachmentIndex, Resources, SubresourceAccess, SubresourceRange, cmd_buf::CommandBufferRef, + pipeline::PipelineRef, + }, crate::{ - AnyBufferNode, AnyImageNode, Area, Attachment, ClearColorValue, Node, + Area, Attachment, ClearColorValue, driver::{ - cmd_buf::CommandBuffer, graphic::{DepthStencilMode, GraphicPipeline}, image::{ ImageInfo, ImageViewInfo, image_subresource_range_contains, @@ -11,10 +13,11 @@ use { }, render_pass::ResolveMode, }, + node::{AnyBufferNode, AnyImageNode, Node}, }, ash::vk, log::trace, - std::{cell::RefCell, slice}, + std::{cell::RefCell, ops::Deref, slice}, vk_sync::AccessType, }; @@ -59,9 +62,8 @@ use { /// # Ok(()) } /// ``` pub struct GraphicPipelineRef<'a> { - pub(super) cmd_buf: &'a CommandBuffer, + pub(super) cmd_buf: CommandBufferRef<'a>, pub(super) pipeline: GraphicPipeline, - pub(super) resources: Resources<'a>, } impl GraphicPipelineRef<'_> { @@ -619,6 +621,14 @@ impl GraphicPipelineRef<'_> { } } +impl<'a> Deref for GraphicPipelineRef<'a> { + type Target = CommandBufferRef<'a>; + + fn deref(&self) -> &Self::Target { + &self.cmd_buf + } +} + // NOTE: local implementation of type from super module impl PipelineRef<'_, GraphicPipeline> { /// Specifies `VK_ATTACHMENT_LOAD_OP_DONT_CARE` for the render pass attachment, and loads an @@ -1489,9 +1499,8 @@ impl PipelineRef<'_, GraphicPipeline> { self.cmd.push_execute(move |cmd_buf, resources| { func( GraphicPipelineRef { - cmd_buf, + cmd_buf: CommandBufferRef { cmd_buf, resources }, pipeline, - resources, }, resources, ); diff --git a/src/cmd_ref/ray_trace.rs b/src/cmd_ref/ray_trace.rs index 6d628564..642979e2 100644 --- a/src/cmd_ref/ray_trace.rs +++ b/src/cmd_ref/ray_trace.rs @@ -1,6 +1,6 @@ use { - super::{PipelineRef, Resources}, - crate::driver::{cmd_buf::CommandBuffer, device::Device, ray_trace::RayTracePipeline}, + super::{PipelineRef, Resources, cmd_buf::CommandBufferRef}, + crate::driver::{device::Device, ray_trace::RayTracePipeline}, ash::vk, log::trace, std::ops::Deref, @@ -31,7 +31,7 @@ impl PipelineRef<'_, RayTracePipeline> { self.cmd.push_execute(move |cmd_buf, resources| { func( RayTracePipelineRef { - cmd_buf, + cmd_buf: CommandBufferRef { cmd_buf, resources }, #[cfg(debug_assertions)] dynamic_stack_size, @@ -82,7 +82,7 @@ impl PipelineRef<'_, RayTracePipeline> { /// # Ok(()) } /// ``` pub struct RayTracePipelineRef<'a> { - cmd_buf: &'a CommandBuffer, + cmd_buf: CommandBufferRef<'a>, #[cfg(debug_assertions)] dynamic_stack_size: bool, @@ -317,10 +317,10 @@ impl RayTracePipelineRef<'_> { } impl<'a> Deref for RayTracePipelineRef<'a> { - type Target = CommandBuffer; + type Target = CommandBufferRef<'a>; fn deref(&self) -> &Self::Target { - self.cmd_buf + &self.cmd_buf } } diff --git a/src/driver/accel_struct.rs b/src/driver/accel_struct.rs index 1f391920..905e5500 100644 --- a/src/driver/accel_struct.rs +++ b/src/driver/accel_struct.rs @@ -684,7 +684,6 @@ impl AccelerationStructureInfo { } /// Creates a default `AccelerationStructureInfoBuilder`. - #[allow(clippy::new_ret_no_self)] pub fn builder() -> AccelerationStructureInfoBuilder { Default::default() } @@ -816,7 +815,7 @@ impl From for vk::DeviceOrHostAddressKHR { } #[cfg(test)] -mod tests { +mod test { use super::*; type Info = AccelerationStructureInfo; diff --git a/src/driver/buffer.rs b/src/driver/buffer.rs index d88843cd..88e5efba 100644 --- a/src/driver/buffer.rs +++ b/src/driver/buffer.rs @@ -900,7 +900,7 @@ impl From for Range { } #[cfg(test)] -mod tests { +mod test { use { super::*, rand::{Rng, SeedableRng, rngs::SmallRng}, diff --git a/src/driver/compute.rs b/src/driver/compute.rs index c18f8b50..ed14826f 100644 --- a/src/driver/compute.rs +++ b/src/driver/compute.rs @@ -307,7 +307,7 @@ impl Drop for ComputePipelineInner { } #[cfg(test)] -mod tests { +mod test { use super::*; type Info = ComputePipelineInfo; diff --git a/src/driver/device.rs b/src/driver/device.rs index 2fd5223f..e282f4d3 100644 --- a/src/driver/device.rs +++ b/src/driver/device.rs @@ -563,7 +563,7 @@ impl Deref for ReadOnlyDevice { } #[cfg(test)] -mod tests { +mod test { use { super::*, std::mem::{offset_of, size_of}, diff --git a/src/driver/graphic.rs b/src/driver/graphic.rs index d94c1502..bfcfbfa8 100644 --- a/src/driver/graphic.rs +++ b/src/driver/graphic.rs @@ -697,7 +697,6 @@ pub struct GraphicPipelineInfo { impl GraphicPipelineInfo { /// Creates a default `GraphicPipelineInfoBuilder`. - #[allow(clippy::new_ret_no_self)] pub fn builder() -> GraphicPipelineInfoBuilder { Default::default() } @@ -881,7 +880,7 @@ pub(crate) struct VertexInputState { } #[cfg(test)] -mod tests { +mod test { use super::*; type Info = GraphicPipelineInfo; diff --git a/src/driver/image.rs b/src/driver/image.rs index 2a8ed05d..25001e6b 100644 --- a/src/driver/image.rs +++ b/src/driver/image.rs @@ -1249,7 +1249,7 @@ mod deprecated { } #[cfg(test)] -mod tests { +mod test { use {super::*, std::ops::Range}; // ImageSubresourceRange does not implement PartialEq diff --git a/src/driver/instance.rs b/src/driver/instance.rs index f140cae7..1bae80d2 100644 --- a/src/driver/instance.rs +++ b/src/driver/instance.rs @@ -553,7 +553,7 @@ impl Deref for ReadOnlyInstance { } #[cfg(test)] -mod tests { +mod test { use { super::*, std::mem::{offset_of, size_of}, diff --git a/src/driver/mod.rs b/src/driver/mod.rs index 08a96641..bdf0d980 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -1142,7 +1142,7 @@ impl Display for DriverError { impl Error for DriverError {} #[cfg(test)] -mod tests { +mod test { use {super::merge_push_constant_ranges, ash::vk}; macro_rules! assert_pcr_eq { diff --git a/src/driver/ray_trace.rs b/src/driver/ray_trace.rs index 1098b6af..91163169 100644 --- a/src/driver/ray_trace.rs +++ b/src/driver/ray_trace.rs @@ -439,7 +439,6 @@ impl RayTracePipelineInfo { } /// Converts a `RayTracePipelineInfo` into a `RayTracePipelineInfoBuilder`. - #[inline(always)] pub fn to_builder(self) -> RayTracePipelineInfoBuilder { RayTracePipelineInfoBuilder { bindless_descriptor_count: Some(self.bindless_descriptor_count), @@ -639,7 +638,7 @@ impl From for vk::RayTracingShaderGroupTypeKHR { } #[cfg(test)] -mod tests { +mod test { use super::*; type Info = RayTracePipelineInfo; diff --git a/src/driver/shader.rs b/src/driver/shader.rs index f784bf60..d5dfefb5 100644 --- a/src/driver/shader.rs +++ b/src/driver/shader.rs @@ -1541,7 +1541,7 @@ impl<'a> From<&'a SpecializationMap> for vk::SpecializationInfo<'a> { } #[cfg(test)] -mod tests { +mod test { use super::*; type Info = SamplerInfo; diff --git a/src/driver/swapchain.rs b/src/driver/swapchain.rs index a0551622..dd6f90c7 100644 --- a/src/driver/swapchain.rs +++ b/src/driver/swapchain.rs @@ -617,7 +617,7 @@ impl From for SwapchainInfoBuilderError { } #[cfg(test)] -mod tests { +mod test { use { super::*, std::mem::{offset_of, size_of}, diff --git a/src/pool/mod.rs b/src/pool/mod.rs index 01935b19..4e0598aa 100644 --- a/src/pool/mod.rs +++ b/src/pool/mod.rs @@ -148,6 +148,9 @@ pub struct Lease { item: ManuallyDrop, } +// The following debug_name functions take a self of Lease and return Self. +// This allows leased resources to have the same `.debug_name("bugs")` chaining + impl Lease { /// Sets the debugging name assigned to this acceleration structure. pub fn debug_name(mut self, name: impl Into) -> Self { @@ -176,7 +179,6 @@ impl Lease { } impl Lease { - #[inline(always)] fn new(cache_ref: CacheRef, item: T) -> Self { Self { cache_ref, @@ -185,18 +187,6 @@ impl Lease { } } -impl AsRef for Lease { - fn as_ref(&self) -> &T { - &self.item - } -} - -impl AsMut for Lease { - fn as_mut(&mut self) -> &mut T { - &mut self.item - } -} - impl Deref for Lease { type Target = T; @@ -267,9 +257,9 @@ lease_builder!(ImageInfo => Image); /// Information used to create a [`FifoPool`](self::fifo::FifoPool), /// [`HashPool`](self::hash::HashPool) or [`LazyPool`](self::lazy::LazyPool) instance. -#[derive(Builder, Clone, Copy, Debug)] +#[derive(Builder, Clone, Copy, Debug, Eq, PartialEq)] #[builder( - build_fn(private, name = "fallible_build", error = "PoolInfoBuilderError"), + build_fn(private, name = "fallible_build", error = "UninitializedFieldError"), derive(Clone, Copy, Debug), pattern = "owned" )] @@ -313,6 +303,11 @@ impl PoolInfo { /// The maximum size of a single bucket of resource instances. pub const DEFAULT_RESOURCE_CAPACITY: usize = 16; + /// Creates a default `PoolInfoBuilder`. + pub fn builder() -> PoolInfoBuilder { + Default::default() + } + /// Constructs a new `PoolInfo` with the given acceleration structure, buffer and image resource /// capacity for any single bucket. pub const fn with_capacity(resource_capacity: usize) -> Self { @@ -323,6 +318,15 @@ impl PoolInfo { } } + /// Converts a `PoolInfo` into a `PoolInfoBuilder`. + pub fn to_builder(self) -> PoolInfoBuilder { + PoolInfoBuilder { + accel_struct_capacity: Some(self.accel_struct_capacity), + buffer_capacity: Some(self.buffer_capacity), + image_capacity: Some(self.image_capacity), + } + } + fn default_cache() -> Cache { Cache::new(Mutex::new(Vec::with_capacity( Self::DEFAULT_RESOURCE_CAPACITY, @@ -365,11 +369,69 @@ impl PoolInfoBuilder { } } -#[derive(Debug)] -struct PoolInfoBuilderError; +mod deprecated { + use { + crate::pool::Lease, + std::convert::{AsMut, AsRef}, + }; + + impl Lease { + #[allow(clippy::should_implement_trait)] + #[deprecated = "use Deref impl"] + #[doc(hidden)] + pub fn as_ref(&self) -> &T { + &self.item + } + + #[allow(clippy::should_implement_trait)] + #[deprecated = "use DerefMut impl"] + #[doc(hidden)] + pub fn as_mut(&mut self) -> &mut T { + &mut self.item + } + } + + impl AsRef for Lease { + fn as_ref(&self) -> &T { + &self.item + } + } + + impl AsMut for Lease { + fn as_mut(&mut self) -> &mut T { + &mut self.item + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + type Info = PoolInfo; + type Builder = PoolInfoBuilder; + + #[test] + pub fn pool_info() { + let info = Info::default(); + let builder = info.to_builder().build(); + + assert_eq!(info, builder); + } -impl From for PoolInfoBuilderError { - fn from(_: UninitializedFieldError) -> Self { - Self + #[test] + pub fn pool_info_builder() { + let info = Info { + accel_struct_capacity: 1, + buffer_capacity: 2, + image_capacity: 3, + }; + let builder = Builder::default() + .accel_struct_capacity(1) + .buffer_capacity(2) + .image_capacity(3) + .build(); + + assert_eq!(info, builder); } } diff --git a/src/queue.rs b/src/queue.rs index aa213b46..9b81e5ea 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -181,13 +181,13 @@ impl Drop for PhysicalPass { } } -/// A structure which can read and execute render graphs. This pattern was derived from: +/// A structure which can optimize and submit graphs. This pattern was derived from: /// /// /// #[derive(Debug)] pub struct Queue { - pub(super) graph: Graph, + graph: Graph, physical_passes: Vec, } @@ -1857,6 +1857,41 @@ impl Queue { } } + /// Returns the stages that process the given node. + /// + /// Note that this value must be retrieved before resolving a node as there will be no + /// data left to inspect afterwards! + #[profiling::function] + pub fn node_stages(&self, node: impl Node) -> vk::PipelineStageFlags { + let node_idx = node.index(); + let mut res = Default::default(); + + 'pass: for pass in self.graph.cmds.iter() { + for exec in pass.execs.iter() { + if exec.accesses.contains_key(&node_idx) { + res |= pass + .execs + .iter() + .filter_map(|exec| exec.pipeline.as_ref()) + .map(|pipeline| pipeline.stage()) + .reduce(|j, k| j | k) + .unwrap_or(vk::PipelineStageFlags::TRANSFER); + + // The execution pipelines of a pass are always the same type + continue 'pass; + } + } + } + + debug_assert_ne!( + res, + Default::default(), + "The given node was not accessed in this graph" + ); + + res + } + #[profiling::function] fn record_execution_barriers<'a>( cmd_buf: &CommandBuffer, @@ -2613,41 +2648,6 @@ impl Queue { self.graph.resource(node) } - /// Returns the stages that process the given node. - /// - /// Note that this value must be retrieved before resolving a node as there will be no - /// data left to inspect afterwards! - #[profiling::function] - pub fn resource_stages(&self, node: impl Node) -> vk::PipelineStageFlags { - let node_idx = node.index(); - let mut res = Default::default(); - - 'pass: for pass in self.graph.cmds.iter() { - for exec in pass.execs.iter() { - if exec.accesses.contains_key(&node_idx) { - res |= pass - .execs - .iter() - .filter_map(|exec| exec.pipeline.as_ref()) - .map(|pipeline| pipeline.stage()) - .reduce(|j, k| j | k) - .unwrap_or(vk::PipelineStageFlags::TRANSFER); - - // The execution pipelines of a pass are always the same type - continue 'pass; - } - } - } - - debug_assert_ne!( - res, - Default::default(), - "The given node was not accessed in this graph" - ); - - res - } - /// Returns a vec of pass indexes that are required to be executed, in order, for the given /// node. #[profiling::function] From 1a675b730c3eb804de0ed6162efe475c5469351b Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 26 Feb 2026 11:52:09 -0500 Subject: [PATCH 26/86] prep for refactor graphic pipeline command buffer attachment api --- contrib/vk-graph-prelude/src/lib.rs | 8 +- contrib/vk-graph-window/src/swapchain.rs | 14 +- examples/fuzzer.rs | 4 +- examples/mip_graphic.rs | 11 +- examples/msaa.rs | 2 +- examples/multipass.rs | 8 +- examples/ray_omni.rs | 2 +- examples/skeletal-anim/src/main.rs | 2 +- examples/vr/src/main.rs | 12 +- examples/vsm_omni.rs | 8 +- src/cmd_ref/bind.rs | 6 +- src/cmd_ref/compute.rs | 22 +- src/cmd_ref/graphic.rs | 2766 +++++++++++----------- src/cmd_ref/mod.rs | 8 +- src/cmd_ref/pipeline.rs | 8 +- src/cmd_ref/ray_trace.rs | 24 +- src/driver/accel_struct.rs | 10 +- src/driver/buffer.rs | 10 +- src/driver/cmd_buf.rs | 10 +- src/driver/compute.rs | 10 +- src/driver/device.rs | 10 +- src/driver/graphic.rs | 252 +- src/driver/image.rs | 20 +- src/driver/instance.rs | 10 +- src/driver/mod.rs | 3 +- src/driver/ray_trace.rs | 9 +- src/driver/render_pass.rs | 11 +- src/driver/shader.rs | 10 +- src/driver/swapchain.rs | 10 +- src/lib.rs | 14 +- src/pool/mod.rs | 37 +- src/queue.rs | 49 +- 32 files changed, 1758 insertions(+), 1622 deletions(-) diff --git a/contrib/vk-graph-prelude/src/lib.rs b/contrib/vk-graph-prelude/src/lib.rs index 2244e570..3d505ad9 100644 --- a/contrib/vk-graph-prelude/src/lib.rs +++ b/contrib/vk-graph-prelude/src/lib.rs @@ -6,7 +6,8 @@ pub use vk_graph::{ BindGraph, Bound, ClearColorValue, Graph, cmd_ref::{ BuildAccelerationStructureIndirectInfo, BuildAccelerationStructureInfo, CommandRef, - PipelineRef, UpdateAccelerationStructureIndirectInfo, UpdateAccelerationStructureInfo, + PipelineCommandRef, UpdateAccelerationStructureIndirectInfo, + UpdateAccelerationStructureInfo, }, driver::{ DriverError, @@ -22,7 +23,7 @@ pub use vk_graph::{ compute::{ComputePipeline, ComputePipelineInfo, ComputePipelineInfoBuilder}, device::{Device, DeviceInfo, DeviceInfoBuilder}, graphic::{ - BlendMode, BlendModeBuilder, DepthStencilMode, DepthStencilModeBuilder, + BlendInfo, BlendInfoBuilder, DepthStencilInfo, DepthStencilInfoBuilder, GraphicPipeline, GraphicPipelineInfo, GraphicPipelineInfoBuilder, StencilMode, }, image::{ @@ -59,3 +60,6 @@ pub use vk_graph::{ lazy::LazyPool, }, }; + +#[allow(deprecated)] +pub use vk_graph::driver::graphic::{BlendMode, DepthStencilMode}; diff --git a/contrib/vk-graph-window/src/swapchain.rs b/contrib/vk-graph-window/src/swapchain.rs index ea4ca8b4..185cdb72 100644 --- a/contrib/vk-graph-window/src/swapchain.rs +++ b/contrib/vk-graph-window/src/swapchain.rs @@ -427,13 +427,12 @@ impl std::fmt::Display for SwapchainError { derive(Clone, Copy, Debug), pattern = "owned" )] -#[non_exhaustive] pub struct SwapchainInfo { /// The number of command buffers to use for image submissions. /// /// Generally one more than the swapchain image count is best. #[builder(default = "4")] - command_buffer_count: usize, + pub command_buffer_count: usize, /// The initial height of the surface. pub height: u32, @@ -491,7 +490,7 @@ pub struct SwapchainInfo { /// The device queue family which will be used to submit and present images. #[builder(default = "0")] - queue_family_index: u32, + pub queue_family_index: u32, /// The format and color space of the surface. pub surface: vk::SurfaceFormatKHR, @@ -516,8 +515,7 @@ impl SwapchainInfo { } /// Converts a `SwapchainInfo` into a `SwapchainInfoBuilder`. - #[inline(always)] - pub fn to_builder(self) -> SwapchainInfoBuilder { + pub fn into_builder(self) -> SwapchainInfoBuilder { SwapchainInfoBuilder { command_buffer_count: Some(self.command_buffer_count), height: Some(self.height), @@ -528,6 +526,12 @@ impl SwapchainInfo { width: Some(self.width), } } + + #[deprecated = "use into_builder function"] + #[doc(hidden)] + pub fn to_builder(self) -> SwapchainInfoBuilder { + self.into_builder() + } } impl From for SwapchainInfo { diff --git a/examples/fuzzer.rs b/examples/fuzzer.rs index 1c37d6e4..0e72923f 100644 --- a/examples/fuzzer.rs +++ b/examples/fuzzer.rs @@ -768,7 +768,7 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo .unwrap(), ); - let depth_stencil_mode = DepthStencilMode { + let depth_stencil_mode = DepthStencilInfo { back: StencilMode::IGNORE, bounds_test: true, compare_op: vk::CompareOp::LESS_OR_EQUAL, @@ -793,7 +793,7 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo .begin_cmd() .debug_name("msaa-depth-stencil") .bind_pipeline(&pipeline) - .set_depth_stencil(depth_stencil_mode) + .depth_stencil(depth_stencil_mode) .clear_color(0, msaa_color_image) .clear_depth_stencil(msaa_depth_stencil_image) .resolve_color(0, 1, frame.swapchain_image) diff --git a/examples/mip_graphic.rs b/examples/mip_graphic.rs index 2b77423b..f094fcff 100644 --- a/examples/mip_graphic.rs +++ b/examples/mip_graphic.rs @@ -78,7 +78,16 @@ fn main() -> Result<(), WindowError> { ) .load_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) - .set_render_area(stripe_x as _, 0, stripe_width, swapchain_info.height) + .render_area(vk::Rect2D { + offset: vk::Offset2D { + x: stripe_x as _, + y: 0, + }, + extent: vk::Extent2D { + width: stripe_width, + height: swapchain_info.height, + }, + }) .record_cmd_buf(|cmd_buf, _| { cmd_buf.draw(6, 1, 0, 0); }); diff --git a/examples/msaa.rs b/examples/msaa.rs index 49e3bc5d..f5bb01ea 100644 --- a/examples/msaa.rs +++ b/examples/msaa.rs @@ -98,7 +98,7 @@ fn main() -> anyhow::Result<()> { } else { &mesh_noaa_pipeline }) - .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) + .depth_stencil(DepthStencilInfo::DEPTH_WRITE) .resource_access(cube_vertex_buf, AccessType::VertexBuffer) .shader_resource_access(0, scene_uniform_buf, AccessType::AnyShaderReadUniformBuffer); diff --git a/examples/multipass.rs b/examples/multipass.rs index b6d43f65..a42d3c9e 100644 --- a/examples/multipass.rs +++ b/examples/multipass.rs @@ -83,7 +83,7 @@ fn main() -> anyhow::Result<()> { let light_buf = bind_light_buf(frame.graph, &mut pool); let push_const_data = write_push_consts(obj_pos, material); - let mut write = DepthStencilMode::DEPTH_WRITE; + let mut write = DepthStencilInfo::DEPTH_WRITE; // Depth Prepass frame @@ -91,7 +91,7 @@ fn main() -> anyhow::Result<()> { .begin_cmd() .debug_name("Depth Prepass") .bind_pipeline(&prepass) - .set_depth_stencil(write) + .depth_stencil(write) .shader_resource_access( 0, camera_buf, @@ -126,7 +126,7 @@ fn main() -> anyhow::Result<()> { .begin_cmd() .debug_name("funky shape PBR") .bind_pipeline(&pbr) - .set_depth_stencil(write) + .depth_stencil(write) .shader_resource_access( 0, camera_buf, @@ -163,7 +163,7 @@ fn main() -> anyhow::Result<()> { .begin_cmd() .debug_name("fill background") .bind_pipeline(&fill_background) - .set_depth_stencil(read) + .depth_stencil(read) .load_depth_stencil(depth_stencil) .load_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) diff --git a/examples/ray_omni.rs b/examples/ray_omni.rs index f47f6697..9adb8bb5 100644 --- a/examples/ray_omni.rs +++ b/examples/ray_omni.rs @@ -102,7 +102,7 @@ fn main() -> anyhow::Result<()> { scene_tlas, AccessType::RayTracingShaderReadAccelerationStructure, ) - .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) + .depth_stencil(DepthStencilInfo::DEPTH_WRITE) .clear_depth_stencil(depth_image) .clear_color_value(0, frame.swapchain_image, [0xff, 0xff, 0xff, 0xff]) .store_color(0, frame.swapchain_image) diff --git a/examples/skeletal-anim/src/main.rs b/examples/skeletal-anim/src/main.rs index ae43e398..b3bd0b6b 100644 --- a/examples/skeletal-anim/src/main.rs +++ b/examples/skeletal-anim/src/main.rs @@ -129,7 +129,7 @@ fn main() -> Result<(), WindowError> { .begin_cmd() .debug_name("🦴") .bind_pipeline(&pipeline) - .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) + .depth_stencil(DepthStencilMode::DEPTH_WRITE) .resource_access(index_buf, AccessType::IndexBuffer) .resource_access(vertex_buf, AccessType::VertexBuffer) .shader_resource_access(0, camera_buf, AccessType::VertexShaderReadUniformBuffer) diff --git a/examples/vr/src/main.rs b/examples/vr/src/main.rs index 684d6222..52b21ecd 100644 --- a/examples/vr/src/main.rs +++ b/examples/vr/src/main.rs @@ -357,8 +357,8 @@ fn main() -> anyhow::Result<()> { .begin_cmd() .debug_name("Left hand") .bind_pipeline(hands_pipeline.hot()) - .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) - .set_multiview(VIEW_MASK, VIEW_MASK) + .depth_stencil(DepthStencilMode::DEPTH_WRITE) + .multiview(VIEW_MASK, VIEW_MASK) .store_color(0, swapchain_image) .clear_depth_stencil(depth_image) .resource_access(index_buf, AccessType::IndexBuffer) @@ -402,8 +402,8 @@ fn main() -> anyhow::Result<()> { .begin_cmd() .debug_name("Right hand") .bind_pipeline(hands_pipeline.hot()) - .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) - .set_multiview(VIEW_MASK, VIEW_MASK) + .depth_stencil(DepthStencilMode::DEPTH_WRITE) + .multiview(VIEW_MASK, VIEW_MASK) .store_color(0, swapchain_image) .clear_depth_stencil(depth_image) .resource_access(index_buf, AccessType::IndexBuffer) @@ -445,8 +445,8 @@ fn main() -> anyhow::Result<()> { .begin_cmd() .debug_name("Woolly Mammoth") .bind_pipeline(mammoth_pipeline.hot()) - .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) - .set_multiview(VIEW_MASK, VIEW_MASK) + .depth_stencil(DepthStencilMode::DEPTH_WRITE) + .multiview(VIEW_MASK, VIEW_MASK) .store_color(0, swapchain_image) .clear_depth_stencil(depth_image) .resource_access(index_buf, AccessType::IndexBuffer) diff --git a/examples/vsm_omni.rs b/examples/vsm_omni.rs index 351c9beb..b97d5929 100644 --- a/examples/vsm_omni.rs +++ b/examples/vsm_omni.rs @@ -244,7 +244,7 @@ fn main() -> anyhow::Result<()> { .begin_cmd() .debug_name("DEBUG") .bind_pipeline(&debug_pipeline) - .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) + .depth_stencil(DepthStencilInfo::DEPTH_WRITE) .shader_resource_access( 0, camera_uniform_buf, @@ -282,7 +282,7 @@ fn main() -> anyhow::Result<()> { .begin_cmd() .debug_name("Shadow (Using geometry shader)") .bind_pipeline(&shadow_pipeline) - .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) + .depth_stencil(DepthStencilInfo::DEPTH_WRITE) .shader_resource_access( 0, light_uniform_buf, @@ -331,7 +331,7 @@ fn main() -> anyhow::Result<()> { .begin_cmd() .debug_name("Shadow") .bind_pipeline(&shadow_pipeline) - .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) + .depth_stencil(DepthStencilInfo::DEPTH_WRITE) .shader_resource_access( 0, light_uniform_buf, @@ -403,7 +403,7 @@ fn main() -> anyhow::Result<()> { .begin_cmd() .debug_name("Mesh objects") .bind_pipeline(&mesh_pipeline) - .set_depth_stencil(DepthStencilMode::DEPTH_WRITE) + .depth_stencil(DepthStencilInfo::DEPTH_WRITE) .shader_resource_access( 0, camera_uniform_buf, diff --git a/src/cmd_ref/bind.rs b/src/cmd_ref/bind.rs index 1653b595..cf8047e2 100644 --- a/src/cmd_ref/bind.rs +++ b/src/cmd_ref/bind.rs @@ -1,5 +1,5 @@ use { - super::{CommandRef, pipeline::PipelineRef}, + super::{CommandRef, pipeline::PipelineCommandRef}, crate::{ ExecutionPipeline, driver::{compute::ComputePipeline, graphic::GraphicPipeline, ray_trace::RayTracePipeline}, @@ -24,7 +24,7 @@ macro_rules! bind_cmd_pipeline { ($name:ident) => { paste::paste! { impl<'a> BindCommand<'a> for &'a [<$name Pipeline>] { - type Ref = PipelineRef<'a, [<$name Pipeline>]>; + type Ref = PipelineCommandRef<'a, [<$name Pipeline>]>; // TODO: Allow binding as explicit secondary command buffers? like with compute/raytrace stuff fn bind_cmd(self, mut cmd: CommandRef<'a>) -> Self::Ref { @@ -44,7 +44,7 @@ macro_rules! bind_cmd_pipeline { } impl<'a> BindCommand<'a> for [<$name Pipeline>] { - type Ref = PipelineRef<'a, [<$name Pipeline>]>; + type Ref = PipelineCommandRef<'a, [<$name Pipeline>]>; fn bind_cmd(self, mut cmd: CommandRef<'a>) -> Self::Ref { let cmd_ref = cmd.cmd_mut(); diff --git a/src/cmd_ref/compute.rs b/src/cmd_ref/compute.rs index 4e7afa3c..1f8faef8 100644 --- a/src/cmd_ref/compute.rs +++ b/src/cmd_ref/compute.rs @@ -1,5 +1,5 @@ use { - super::{Resources, cmd_buf::CommandBufferRef, pipeline::PipelineRef}, + super::{Resources, cmd_buf::CommandBufferRef, pipeline::PipelineCommandRef}, crate::{driver::compute::ComputePipeline, node::AnyBufferNode}, ash::vk, log::trace, @@ -38,12 +38,12 @@ use { /// }); /// # Ok(()) } /// ``` -pub struct ComputePipelineRef<'a> { +pub struct ComputeCommandBufferRef<'a> { pub(super) cmd_buf: CommandBufferRef<'a>, pub(super) pipeline: ComputePipeline, } -impl ComputePipelineRef<'_> { +impl ComputeCommandBufferRef<'_> { /// [Dispatch] compute work items. /// /// When the command is executed, a global workgroup consisting of @@ -316,7 +316,7 @@ impl ComputePipelineRef<'_> { } } -impl<'a> Deref for ComputePipelineRef<'a> { +impl<'a> Deref for ComputeCommandBufferRef<'a> { type Target = CommandBufferRef<'a>; fn deref(&self) -> &Self::Target { @@ -325,11 +325,11 @@ impl<'a> Deref for ComputePipelineRef<'a> { } // NOTE: local implementation of type from super module -impl PipelineRef<'_, ComputePipeline> { +impl PipelineCommandRef<'_, ComputePipeline> { /// Begin recording a compute pipeline command buffer. pub fn record_cmd_buf( mut self, - func: impl FnOnce(ComputePipelineRef<'_>, Resources<'_>) + Send + 'static, + func: impl FnOnce(ComputeCommandBufferRef<'_>, Resources<'_>) + Send + 'static, ) -> Self { let pipeline = self .cmd @@ -345,7 +345,7 @@ impl PipelineRef<'_, ComputePipeline> { self.cmd.push_execute(move |cmd_buf, resources| { func( - ComputePipelineRef { + ComputeCommandBufferRef { cmd_buf: CommandBufferRef { cmd_buf, resources }, pipeline, }, @@ -362,8 +362,8 @@ mod deprecated { use { crate::{ cmd_ref::{ - Descriptor, PipelineRef, Resources, SubresourceRange, View, ViewInfo, - compute::ComputePipelineRef, + Descriptor, PipelineCommandRef, Resources, SubresourceRange, View, ViewInfo, + compute::ComputeCommandBufferRef, }, driver::compute::ComputePipeline, node::Node, @@ -371,7 +371,7 @@ mod deprecated { vk_sync::AccessType, }; - impl PipelineRef<'_, ComputePipeline> { + impl PipelineCommandRef<'_, ComputePipeline> { #[deprecated = "use shader_resource_access function with AccessType::ComputeShaderReadOther"] #[doc(hidden)] pub fn read_descriptor(self, descriptor: impl Into, node: N) -> Self @@ -410,7 +410,7 @@ mod deprecated { #[doc(hidden)] pub fn record_compute( self, - func: impl FnOnce(ComputePipelineRef<'_>, Resources<'_>) + Send + 'static, + func: impl FnOnce(ComputeCommandBufferRef<'_>, Resources<'_>) + Send + 'static, ) -> Self { self.record_cmd_buf(func) } diff --git a/src/cmd_ref/graphic.rs b/src/cmd_ref/graphic.rs index d1482a85..5adbdd31 100644 --- a/src/cmd_ref/graphic.rs +++ b/src/cmd_ref/graphic.rs @@ -1,12 +1,12 @@ use { super::{ AttachmentIndex, Resources, SubresourceAccess, SubresourceRange, cmd_buf::CommandBufferRef, - pipeline::PipelineRef, + pipeline::PipelineCommandRef, }, crate::{ - Area, Attachment, ClearColorValue, + Attachment, ClearColorValue, driver::{ - graphic::{DepthStencilMode, GraphicPipeline}, + graphic::{DepthStencilInfo, GraphicPipeline}, image::{ ImageInfo, ImageViewInfo, image_subresource_range_contains, image_subresource_range_intersects, @@ -25,7 +25,7 @@ use { /// /// This structure provides a strongly-typed set of methods which allow rasterization shader code to /// be executed. An instance of `Draw` is provided to the closure parameter of -/// [`PipelineRef::record_pipeline`] which may be accessed by binding a [`GraphicPipeline`] to a +/// [`PipelineCommandRef::record_pipeline`] which may be accessed by binding a [`GraphicPipeline`] to a /// render pass. /// /// # Examples @@ -61,12 +61,12 @@ use { /// }); /// # Ok(()) } /// ``` -pub struct GraphicPipelineRef<'a> { +pub struct GraphicCommandBufferRef<'a> { pub(super) cmd_buf: CommandBufferRef<'a>, pub(super) pipeline: GraphicPipeline, } -impl GraphicPipelineRef<'_> { +impl GraphicCommandBufferRef<'_> { /// Bind an index buffer to the current pass. /// /// `offset` is the starting offset in bytes within `buffer` used in index buffer address @@ -211,13 +211,13 @@ impl GraphicPipelineRef<'_> { /// The vertex input attributes that use each of these bindings will use these updated addresses /// in their address calculations for subsequent drawing commands. #[profiling::function] - pub fn bind_vertex_buffers( + pub fn bind_vertex_buffers( &self, first_binding: u32, - buffer_offsets: impl IntoIterator, + buffer_offsets: impl IntoIterator, ) -> &Self where - B: Into, + N: Into, { thread_local! { static BUFFERS_OFFSETS: RefCell<(Vec, Vec)> = Default::default(); @@ -621,7 +621,7 @@ impl GraphicPipelineRef<'_> { } } -impl<'a> Deref for GraphicPipelineRef<'a> { +impl<'a> Deref for GraphicCommandBufferRef<'a> { type Target = CommandBufferRef<'a>; fn deref(&self) -> &Self::Target { @@ -630,204 +630,614 @@ impl<'a> Deref for GraphicPipelineRef<'a> { } // NOTE: local implementation of type from super module -impl PipelineRef<'_, GraphicPipeline> { - /// Specifies `VK_ATTACHMENT_LOAD_OP_DONT_CARE` for the render pass attachment, and loads an - /// image into the framebuffer. - pub fn attach_color( - self, - attachment_idx: AttachmentIndex, - image: impl Into, - ) -> Self { - let image = image.into(); - let image_info = self.resource(image).info; - let image_view_info: ImageViewInfo = image_info.into(); +impl PipelineCommandRef<'_, GraphicPipeline> { + /// Sets the combined depth and stencil state used by any subsequent command buffer recordings + /// of the current graph command. + pub fn depth_stencil(mut self, depth_stencil: impl Into) -> Self { + self.set_depth_stencil(depth_stencil); + self + } - self.attach_color_as(attachment_idx, image, image_view_info) + /// Sets multiview view and correlation masks used by any subsequent command buffer recordings + /// of the current graph command. + /// + /// See [`VkRenderPassMultiviewCreateInfo`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkRenderPassMultiviewCreateInfo.html#_description). + pub fn multiview(mut self, view_mask: u32, correlated_view_mask: u32) -> Self { + self.set_multiview(view_mask, correlated_view_mask); + self } - /// Specifies `VK_ATTACHMENT_LOAD_OP_DONT_CARE` for the render pass attachment, and loads an - /// image into the framebuffer. - pub fn attach_color_as( + /// Begin recording a graphics pipeline command buffer. + pub fn record_cmd_buf( mut self, - attachment_idx: AttachmentIndex, - image: impl Into, - image_view_info: impl Into, + func: impl FnOnce(GraphicCommandBufferRef<'_>, Resources<'_>) + Send + 'static, ) -> Self { - let image = image.into(); - let image_view_info = image_view_info.into(); - let node_idx = image.index(); - let ImageInfo { sample_count, .. } = self.resource(image).info; - - debug_assert!( - !self - .cmd - .cmd() - .execs - .last() - .unwrap() - .color_clears - .contains_key(&attachment_idx), - "color attachment {attachment_idx} already attached via clear" - ); - debug_assert!( - !self - .cmd - .cmd() - .execs - .last() - .unwrap() - .color_loads - .contains_key(&attachment_idx), - "color attachment {attachment_idx} already attached via load" - ); - - self.cmd - .cmd_mut() + let pipeline = self + .cmd + .cmd() .execs - .last_mut() + .last() .unwrap() - .color_attachments - .insert( - attachment_idx, - Attachment::new(image_view_info, sample_count, node_idx), + .pipeline + .as_ref() + .unwrap() + .unwrap_graphic() + .clone(); + + self.cmd.push_execute(move |cmd_buf, resources| { + func( + GraphicCommandBufferRef { + cmd_buf: CommandBufferRef { cmd_buf, resources }, + pipeline, + }, + resources, ); + }); - debug_assert!( - Attachment::are_compatible( - self.cmd + self + } + + /// Sets the [`renderArea`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkRenderPassBeginInfo.html#_c_specification) + /// field when beginning a render pass used by any subsequent command buffer recordings + /// of the current graph command. + /// + /// _NOTE:_ Setting this value will cause the viewport and scissor to be unset, which is not the + /// default behavior. When this value is set you should call `set_viewport` and `set_scissor` on + /// the command buffer. + /// + /// If not set, this value defaults to the first loaded, resolved, or stored attachment + /// dimensions and sets the viewport and scissor to the same values, with a `0..1` depth if not + /// specified by `depth_stencil`. + pub fn render_area(mut self, area: vk::Rect2D) -> Self { + self.set_render_area(area); + self + } + + /// See [depth_stencil] + pub fn set_depth_stencil(&mut self, depth_stencil: impl Into) -> &mut Self { + let depth_stencil = depth_stencil.into(); + let cmd = self.cmd.cmd_mut(); + let exec = cmd.execs.last_mut().unwrap(); + + assert!(exec.depth_stencil.is_none()); + + exec.depth_stencil = Some(depth_stencil); + + self + } + + /// See [multiview] + pub fn set_multiview(&mut self, view_mask: u32, correlated_view_mask: u32) -> &mut Self { + let cmd = self.cmd.cmd_mut(); + let exec = cmd.execs.last_mut().unwrap(); + + exec.correlated_view_mask = correlated_view_mask; + exec.view_mask = view_mask; + + self + } + + /// See [render_area] + pub fn set_render_area(&mut self, area: vk::Rect2D) -> &mut Self { + self.cmd.cmd_mut().execs.last_mut().unwrap().render_area = Some(area); + self + } +} + +#[allow(unused)] +mod deprecated { + use { + crate::{ + Attachment, ClearColorValue, SubresourceAccess, + cmd_ref::{ + AttachmentIndex, Descriptor, PipelineCommandRef, Resources, SubresourceRange, View, + ViewInfo, graphic::GraphicCommandBufferRef, + }, + driver::{ + graphic::GraphicPipeline, + image::{ + ImageInfo, ImageViewInfo, image_subresource_range_contains, + image_subresource_range_intersects, + }, + render_pass::ResolveMode, + }, + node::{AnyImageNode, Node}, + }, + ash::vk, + vk_sync::AccessType, + }; + + // Attachment functions + impl PipelineCommandRef<'_, GraphicPipeline> { + #[deprecated] + #[doc(hidden)] + pub fn attach_color( + self, + attachment_idx: AttachmentIndex, + image: impl Into, + ) -> Self { + let image = image.into(); + let image_info = self.resource(image).info; + let image_view_info: ImageViewInfo = image_info.into(); + + #[allow(deprecated)] + self.attach_color_as(attachment_idx, image, image_view_info) + } + + #[deprecated] + #[doc(hidden)] + pub fn attach_color_as( + mut self, + attachment_idx: AttachmentIndex, + image: impl Into, + image_view_info: impl Into, + ) -> Self { + let image = image.into(); + let image_view_info = image_view_info.into(); + let node_idx = image.index(); + let ImageInfo { sample_count, .. } = self.resource(image).info; + + debug_assert!( + !self + .cmd .cmd() .execs .last() .unwrap() - .color_resolves - .get(&attachment_idx) - .map(|(attachment, _)| *attachment), - self.cmd + .color_clears + .contains_key(&attachment_idx), + "color attachment {attachment_idx} already attached via clear" + ); + debug_assert!( + !self + .cmd .cmd() .execs .last() .unwrap() - .color_attachments - .get(&attachment_idx) - .copied() - ), - "color attachment {attachment_idx} incompatible with existing resolve" - ); - debug_assert!( - Attachment::are_compatible( + .color_loads + .contains_key(&attachment_idx), + "color attachment {attachment_idx} already attached via load" + ); + + self.cmd + .cmd_mut() + .execs + .last_mut() + .unwrap() + .color_attachments + .insert( + attachment_idx, + Attachment::new(image_view_info, sample_count, node_idx), + ); + + debug_assert!( + Attachment::are_compatible( + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_resolves + .get(&attachment_idx) + .map(|(attachment, _)| *attachment), + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_attachments + .get(&attachment_idx) + .copied() + ), + "color attachment {attachment_idx} incompatible with existing resolve" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_stores + .get(&attachment_idx) + .copied(), + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_attachments + .get(&attachment_idx) + .copied() + ), + "color attachment {attachment_idx} incompatible with existing store" + ); + + self.cmd.push_node_access( + image, + AccessType::ColorAttachmentWrite, + SubresourceRange::Image(image_view_info.into()), + ); + + self + } + + #[deprecated] + #[doc(hidden)] + pub fn attach_depth_stencil(self, image: impl Into) -> Self { + let image = image.into(); + let image_view_info = self.resource(image).info; + + #[allow(deprecated)] + self.attach_depth_stencil_as(image, image_view_info) + } + + #[deprecated] + #[doc(hidden)] + pub fn attach_depth_stencil_as( + mut self, + image: impl Into, + image_view_info: impl Into, + ) -> Self { + let image = image.into(); + let image_view_info = image_view_info.into(); + let node_idx = image.index(); + let ImageInfo { sample_count, .. } = self.resource(image).info; + + debug_assert!( self.cmd .cmd() .execs .last() .unwrap() - .color_stores - .get(&attachment_idx) - .copied(), + .depth_stencil_clear + .is_none(), + "depth/stencil attachment already attached via clear" + ); + debug_assert!( self.cmd .cmd() .execs .last() .unwrap() - .color_attachments - .get(&attachment_idx) - .copied() - ), - "color attachment {attachment_idx} incompatible with existing store" - ); - - self.cmd.push_node_access( - image, - AccessType::ColorAttachmentWrite, - SubresourceRange::Image(image_view_info.into()), - ); + .depth_stencil_load + .is_none(), + "depth/stencil attachment already attached via load" + ); - self - } + self.cmd + .cmd_mut() + .execs + .last_mut() + .unwrap() + .depth_stencil_attachment = + Some(Attachment::new(image_view_info, sample_count, node_idx)); - /// Specifies `VK_ATTACHMENT_LOAD_OP_DONT_CARE` for the render pass attachment, and loads an - /// image into the framebuffer. - pub fn attach_depth_stencil(self, image: impl Into) -> Self { - let image = image.into(); - let image_view_info = self.resource(image).info; + debug_assert!( + Attachment::are_compatible( + self.cmd + .cmd() + .execs + .last() + .unwrap() + .depth_stencil_resolve + .map(|(attachment, ..)| attachment), + self.cmd + .cmd() + .execs + .last() + .unwrap() + .depth_stencil_attachment + ), + "depth/stencil attachment incompatible with existing resolve" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd.cmd().execs.last().unwrap().depth_stencil_store, + self.cmd + .cmd() + .execs + .last() + .unwrap() + .depth_stencil_attachment + ), + "depth/stencil attachment incompatible with existing store" + ); - self.attach_depth_stencil_as(image, image_view_info) - } + self.cmd.push_node_access( + image, + if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL) + { + AccessType::DepthStencilAttachmentWrite + } else if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH) + { + AccessType::DepthAttachmentWriteStencilReadOnly + } else { + AccessType::StencilAttachmentWriteDepthReadOnly + }, + SubresourceRange::Image(image_view_info.into()), + ); - /// Specifies `VK_ATTACHMENT_LOAD_OP_DONT_CARE` for the render pass attachment, and loads an - /// image into the framebuffer. - pub fn attach_depth_stencil_as( - mut self, - image: impl Into, - image_view_info: impl Into, - ) -> Self { - let image = image.into(); - let image_view_info = image_view_info.into(); - let node_idx = image.index(); - let ImageInfo { sample_count, .. } = self.resource(image).info; + self + } + + #[deprecated] + #[doc(hidden)] + pub fn clear_color( + self, + attachment_idx: AttachmentIndex, + image: impl Into, + ) -> Self { + #[allow(deprecated)] + self.clear_color_value(attachment_idx, image, [0.0, 0.0, 0.0, 0.0]) + } + + #[deprecated] + #[doc(hidden)] + pub fn clear_color_value( + self, + attachment_idx: AttachmentIndex, + image: impl Into, + color: impl Into, + ) -> Self { + let image = image.into(); + let image_info = self.resource(image).info; + let image_view_info: ImageViewInfo = image_info.into(); + + #[allow(deprecated)] + self.clear_color_value_as(attachment_idx, image, color, image_view_info) + } + + #[deprecated] + #[doc(hidden)] + pub fn clear_color_value_as( + mut self, + attachment_idx: AttachmentIndex, + image: impl Into, + color: impl Into, + image_view_info: impl Into, + ) -> Self { + let image = image.into(); + let image_view_info = image_view_info.into(); + let node_idx = image.index(); + let ImageInfo { sample_count, .. } = self.resource(image).info; + + let color = color.into(); + + debug_assert!( + !self + .cmd + .cmd() + .execs + .last() + .unwrap() + .color_attachments + .contains_key(&attachment_idx), + "color attachment {attachment_idx} already attached" + ); + debug_assert!( + !self + .cmd + .cmd() + .execs + .last() + .unwrap() + .color_loads + .contains_key(&attachment_idx), + "color attachment {attachment_idx} already attached via load" + ); - debug_assert!( self.cmd - .cmd() + .cmd_mut() .execs - .last() + .last_mut() .unwrap() - .depth_stencil_clear - .is_none(), - "depth/stencil attachment already attached via clear" - ); - debug_assert!( - self.cmd - .cmd() + .color_clears + .insert( + attachment_idx, + ( + Attachment::new(image_view_info, sample_count, node_idx), + color, + ), + ); + + debug_assert!( + Attachment::are_compatible( + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_resolves + .get(&attachment_idx) + .map(|(attachment, _)| *attachment), + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_clears + .get(&attachment_idx) + .map(|(attachment, _)| *attachment) + ), + "color attachment {attachment_idx} clear incompatible with existing resolve" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_stores + .get(&attachment_idx) + .copied(), + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_clears + .get(&attachment_idx) + .map(|(attachment, _)| *attachment) + ), + "color attachment {attachment_idx} clear incompatible with existing store" + ); + + let mut image_access = AccessType::ColorAttachmentWrite; + let image_range = image_view_info.into(); + + // Upgrade existing read access to read-write + if let Some(accesses) = self + .cmd + .cmd_mut() .execs - .last() + .last_mut() .unwrap() - .depth_stencil_load - .is_none(), - "depth/stencil attachment already attached via load" - ); + .accesses + .get_mut(&node_idx) + { + for SubresourceAccess { + access, + subresource, + } in accesses + { + let access_image_range = *subresource.as_image().unwrap(); + if !image_subresource_range_intersects(access_image_range, image_range) { + continue; + } - self.cmd - .cmd_mut() - .execs - .last_mut() - .unwrap() - .depth_stencil_attachment = - Some(Attachment::new(image_view_info, sample_count, node_idx)); + image_access = match *access { + AccessType::ColorAttachmentRead | AccessType::ColorAttachmentReadWrite => { + AccessType::ColorAttachmentReadWrite + } + AccessType::ColorAttachmentWrite => AccessType::ColorAttachmentWrite, + _ => continue, + }; - debug_assert!( - Attachment::are_compatible( - self.cmd - .cmd() - .execs - .last() - .unwrap() - .depth_stencil_resolve - .map(|(attachment, ..)| attachment), + *access = image_access; + + // If the clear access is a subset of the existing access range there is no need + // to push a new access + if image_subresource_range_contains(access_image_range, image_range) { + return self; + } + } + } + + self.cmd + .push_node_access(image, image_access, SubresourceRange::Image(image_range)); + + self + } + + #[deprecated] + #[doc(hidden)] + pub fn clear_depth_stencil(self, image: impl Into) -> Self { + #[allow(deprecated)] + self.clear_depth_stencil_value(image, 1.0, 0) + } + + #[deprecated] + #[doc(hidden)] + pub fn clear_depth_stencil_value( + self, + image: impl Into, + depth: f32, + stencil: u32, + ) -> Self { + let image = image.into(); + let image_info = self.resource(image).info; + let image_view_info: ImageViewInfo = image_info.into(); + + #[allow(deprecated)] + self.clear_depth_stencil_value_as(image, depth, stencil, image_view_info) + } + + #[deprecated] + #[doc(hidden)] + pub fn clear_depth_stencil_value_as( + mut self, + image: impl Into, + depth: f32, + stencil: u32, + image_view_info: impl Into, + ) -> Self { + let image = image.into(); + let image_view_info = image_view_info.into(); + let node_idx = image.index(); + let ImageInfo { sample_count, .. } = self.resource(image).info; + + debug_assert!( self.cmd .cmd() .execs .last() .unwrap() .depth_stencil_attachment - ), - "depth/stencil attachment incompatible with existing resolve" - ); - debug_assert!( - Attachment::are_compatible( - self.cmd.cmd().execs.last().unwrap().depth_stencil_store, + .is_none(), + "depth/stencil attachment already attached" + ); + debug_assert!( self.cmd .cmd() .execs .last() .unwrap() - .depth_stencil_attachment - ), - "depth/stencil attachment incompatible with existing store" - ); + .depth_stencil_load + .is_none(), + "depth/stencil attachment already attached via load" + ); - self.cmd.push_node_access( - image, - if image_view_info + self.cmd + .cmd_mut() + .execs + .last_mut() + .unwrap() + .depth_stencil_clear = Some(( + Attachment::new(image_view_info, sample_count, node_idx), + vk::ClearDepthStencilValue { depth, stencil }, + )); + + debug_assert!( + Attachment::are_compatible( + self.cmd + .cmd() + .execs + .last() + .unwrap() + .depth_stencil_resolve + .map(|(attachment, ..)| attachment), + self.cmd + .cmd() + .execs + .last() + .unwrap() + .depth_stencil_clear + .map(|(attachment, _)| attachment) + ), + "depth/stencil attachment clear incompatible with existing resolve" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd.cmd().execs.last().unwrap().depth_stencil_store, + self.cmd + .cmd() + .execs + .last() + .unwrap() + .depth_stencil_clear + .map(|(attachment, _)| attachment) + ), + "depth/stencil attachment clear incompatible with existing store" + ); + + let mut image_access = if image_view_info .aspect_mask .contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL) { @@ -838,1343 +1248,981 @@ impl PipelineRef<'_, GraphicPipeline> { { AccessType::DepthAttachmentWriteStencilReadOnly } else { - AccessType::StencilAttachmentWriteDepthReadOnly - }, - SubresourceRange::Image(image_view_info.into()), - ); - - self - } - - /// Clears the render pass attachment of any existing data. - pub fn clear_color( - self, - attachment_idx: AttachmentIndex, - image: impl Into, - ) -> Self { - self.clear_color_value(attachment_idx, image, [0.0, 0.0, 0.0, 0.0]) - } - - /// Clears the render pass attachment of any existing data. - pub fn clear_color_value( - self, - attachment_idx: AttachmentIndex, - image: impl Into, - color: impl Into, - ) -> Self { - let image = image.into(); - let image_info = self.resource(image).info; - let image_view_info: ImageViewInfo = image_info.into(); - - self.clear_color_value_as(attachment_idx, image, color, image_view_info) - } - - /// Clears the render pass attachment of any existing data. - pub fn clear_color_value_as( - mut self, - attachment_idx: AttachmentIndex, - image: impl Into, - color: impl Into, - image_view_info: impl Into, - ) -> Self { - let image = image.into(); - let image_view_info = image_view_info.into(); - let node_idx = image.index(); - let ImageInfo { sample_count, .. } = self.resource(image).info; + debug_assert!( + image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::STENCIL) + ); - let color = color.into(); + AccessType::StencilAttachmentWriteDepthReadOnly + }; + let image_range = image_view_info.into(); - debug_assert!( - !self + // Upgrade existing read access to read-write + if let Some(accesses) = self .cmd - .cmd() + .cmd_mut() .execs - .last() + .last_mut() .unwrap() - .color_attachments - .contains_key(&attachment_idx), - "color attachment {attachment_idx} already attached" - ); - debug_assert!( - !self - .cmd - .cmd() - .execs - .last() - .unwrap() - .color_loads - .contains_key(&attachment_idx), - "color attachment {attachment_idx} already attached via load" - ); + .accesses + .get_mut(&node_idx) + { + for SubresourceAccess { + access, + subresource, + } in accesses + { + let access_image_range = *subresource.as_image().unwrap(); + if !image_subresource_range_intersects(access_image_range, image_range) { + continue; + } - self.cmd - .cmd_mut() - .execs - .last_mut() - .unwrap() - .color_clears - .insert( - attachment_idx, - ( - Attachment::new(image_view_info, sample_count, node_idx), - color, - ), - ); + image_access = match *access { + AccessType::DepthAttachmentWriteStencilReadOnly => { + if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::STENCIL) + { + AccessType::DepthStencilAttachmentReadWrite + } else { + AccessType::DepthAttachmentWriteStencilReadOnly + } + } + AccessType::DepthStencilAttachmentRead => { + if !image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH) + { + AccessType::StencilAttachmentWriteDepthReadOnly + } else { + AccessType::DepthAttachmentWriteStencilReadOnly + } + } + AccessType::DepthStencilAttachmentWrite => { + AccessType::DepthStencilAttachmentWrite + } + AccessType::StencilAttachmentWriteDepthReadOnly => { + if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH) + { + AccessType::DepthStencilAttachmentReadWrite + } else { + AccessType::StencilAttachmentWriteDepthReadOnly + } + } + _ => continue, + }; - debug_assert!( - Attachment::are_compatible( - self.cmd - .cmd() - .execs - .last() - .unwrap() - .color_resolves - .get(&attachment_idx) - .map(|(attachment, _)| *attachment), - self.cmd - .cmd() - .execs - .last() - .unwrap() - .color_clears - .get(&attachment_idx) - .map(|(attachment, _)| *attachment) - ), - "color attachment {attachment_idx} clear incompatible with existing resolve" - ); - debug_assert!( - Attachment::are_compatible( - self.cmd + *access = image_access; + + // If the clear access is a subset of the existing access range there is no need + // to push a new access + if image_subresource_range_contains(access_image_range, image_range) { + return self; + } + } + } + + self.cmd + .push_node_access(image, image_access, SubresourceRange::Image(image_range)); + + self + } + + #[deprecated] + #[doc(hidden)] + pub fn load_color( + self, + attachment_idx: AttachmentIndex, + image: impl Into, + ) -> Self { + let image = image.into(); + let image_info = self.resource(image).info; + + // Use the plain node information as the whole view of the node + let image_view_info = image_info; + + #[allow(deprecated)] + self.load_color_as(attachment_idx, image, image_view_info) + } + + #[deprecated] + #[doc(hidden)] + pub fn load_color_as( + mut self, + attachment_idx: AttachmentIndex, + image: impl Into, + image_view_info: impl Into, + ) -> Self { + let image = image.into(); + let image_view_info = image_view_info.into(); + let node_idx = image.index(); + let ImageInfo { sample_count, .. } = self.resource(image).info; + + debug_assert!( + !self + .cmd .cmd() .execs .last() .unwrap() - .color_stores - .get(&attachment_idx) - .copied(), - self.cmd + .color_attachments + .contains_key(&attachment_idx), + "color attachment {attachment_idx} already attached" + ); + debug_assert!( + !self + .cmd .cmd() .execs .last() .unwrap() .color_clears - .get(&attachment_idx) - .map(|(attachment, _)| *attachment) - ), - "color attachment {attachment_idx} clear incompatible with existing store" - ); + .contains_key(&attachment_idx), + "color attachment {attachment_idx} already attached via clear" + ); - let mut image_access = AccessType::ColorAttachmentWrite; - let image_range = image_view_info.into(); + self.cmd + .cmd_mut() + .execs + .last_mut() + .unwrap() + .color_loads + .insert( + attachment_idx, + Attachment::new(image_view_info, sample_count, node_idx), + ); - // Upgrade existing read access to read-write - if let Some(accesses) = self - .cmd - .cmd_mut() - .execs - .last_mut() - .unwrap() - .accesses - .get_mut(&node_idx) - { - for SubresourceAccess { - access, - subresource, - } in accesses - { - let access_image_range = *subresource.as_image().unwrap(); - if !image_subresource_range_intersects(access_image_range, image_range) { - continue; - } + debug_assert!( + Attachment::are_compatible( + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_resolves + .get(&attachment_idx) + .map(|(attachment, _)| *attachment), + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_loads + .get(&attachment_idx) + .copied() + ), + "color attachment {attachment_idx} load incompatible with existing resolve" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_stores + .get(&attachment_idx) + .copied(), + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_loads + .get(&attachment_idx) + .copied() + ), + "color attachment {attachment_idx} load incompatible with existing store" + ); - image_access = match *access { - AccessType::ColorAttachmentRead | AccessType::ColorAttachmentReadWrite => { - AccessType::ColorAttachmentReadWrite + let mut image_access = AccessType::ColorAttachmentRead; + let image_range = image_view_info.into(); + + // Upgrade existing write access to read-write + if let Some(accesses) = self + .cmd + .cmd_mut() + .execs + .last_mut() + .unwrap() + .accesses + .get_mut(&node_idx) + { + for SubresourceAccess { + access, + subresource, + } in accesses + { + let access_image_range = *subresource.as_image().unwrap(); + if !image_subresource_range_intersects(access_image_range, image_range) { + continue; } - AccessType::ColorAttachmentWrite => AccessType::ColorAttachmentWrite, - _ => continue, - }; - *access = image_access; + image_access = match *access { + AccessType::ColorAttachmentRead => AccessType::ColorAttachmentRead, + AccessType::ColorAttachmentReadWrite | AccessType::ColorAttachmentWrite => { + AccessType::ColorAttachmentReadWrite + } + _ => continue, + }; + + *access = image_access; - // If the clear access is a subset of the existing access range there is no need - // to push a new access - if image_subresource_range_contains(access_image_range, image_range) { - return self; + // If the load access is a subset of the existing access range there is no need + // to push a new access + if image_subresource_range_contains(access_image_range, image_range) { + return self; + } } } - } - - self.cmd - .push_node_access(image, image_access, SubresourceRange::Image(image_range)); - - self - } - - /// Clears the render pass attachment of any existing data. - pub fn clear_depth_stencil(self, image: impl Into) -> Self { - self.clear_depth_stencil_value(image, 1.0, 0) - } - /// Clears the render pass attachment of any existing data. - pub fn clear_depth_stencil_value( - self, - image: impl Into, - depth: f32, - stencil: u32, - ) -> Self { - let image = image.into(); - let image_info = self.resource(image).info; - let image_view_info: ImageViewInfo = image_info.into(); + self.cmd + .push_node_access(image, image_access, SubresourceRange::Image(image_range)); - self.clear_depth_stencil_value_as(image, depth, stencil, image_view_info) - } + self + } - /// Clears the render pass attachment of any existing data. - pub fn clear_depth_stencil_value_as( - mut self, - image: impl Into, - depth: f32, - stencil: u32, - image_view_info: impl Into, - ) -> Self { - let image = image.into(); - let image_view_info = image_view_info.into(); - let node_idx = image.index(); - let ImageInfo { sample_count, .. } = self.resource(image).info; + #[deprecated] + #[doc(hidden)] + pub fn load_depth_stencil(self, image: impl Into) -> Self { + let image = image.into(); + let image_view_info = self.resource(image).info; - debug_assert!( - self.cmd - .cmd() - .execs - .last() - .unwrap() - .depth_stencil_attachment - .is_none(), - "depth/stencil attachment already attached" - ); - debug_assert!( - self.cmd - .cmd() - .execs - .last() - .unwrap() - .depth_stencil_load - .is_none(), - "depth/stencil attachment already attached via load" - ); + #[allow(deprecated)] + self.load_depth_stencil_as(image, image_view_info) + } - self.cmd - .cmd_mut() - .execs - .last_mut() - .unwrap() - .depth_stencil_clear = Some(( - Attachment::new(image_view_info, sample_count, node_idx), - vk::ClearDepthStencilValue { depth, stencil }, - )); + #[deprecated] + #[doc(hidden)] + pub fn load_depth_stencil_as( + mut self, + image: impl Into, + image_view_info: impl Into, + ) -> Self { + let image = image.into(); + let image_view_info = image_view_info.into(); + let node_idx = image.index(); + let ImageInfo { sample_count, .. } = self.resource(image).info; - debug_assert!( - Attachment::are_compatible( - self.cmd - .cmd() - .execs - .last() - .unwrap() - .depth_stencil_resolve - .map(|(attachment, ..)| attachment), + debug_assert!( self.cmd .cmd() .execs .last() .unwrap() - .depth_stencil_clear - .map(|(attachment, _)| attachment) - ), - "depth/stencil attachment clear incompatible with existing resolve" - ); - debug_assert!( - Attachment::are_compatible( - self.cmd.cmd().execs.last().unwrap().depth_stencil_store, + .depth_stencil_attachment + .is_none(), + "depth/stencil attachment already attached" + ); + debug_assert!( self.cmd .cmd() .execs .last() .unwrap() .depth_stencil_clear - .map(|(attachment, _)| attachment) - ), - "depth/stencil attachment clear incompatible with existing store" - ); - - let mut image_access = if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL) - { - AccessType::DepthStencilAttachmentWrite - } else if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH) - { - AccessType::DepthAttachmentWriteStencilReadOnly - } else { + .is_none(), + "depth/stencil attachment already attached via clear" + ); + + self.cmd + .cmd_mut() + .execs + .last_mut() + .unwrap() + .depth_stencil_load = + Some(Attachment::new(image_view_info, sample_count, node_idx)); + debug_assert!( - image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::STENCIL) + Attachment::are_compatible( + self.cmd + .cmd() + .execs + .last() + .unwrap() + .depth_stencil_resolve + .map(|(attachment, ..)| attachment), + self.cmd.cmd().execs.last().unwrap().depth_stencil_load + ), + "depth/stencil attachment load incompatible with existing resolve" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd.cmd().execs.last().unwrap().depth_stencil_store, + self.cmd.cmd().execs.last().unwrap().depth_stencil_load + ), + "depth/stencil attachment load incompatible with existing store" ); - AccessType::StencilAttachmentWriteDepthReadOnly - }; - let image_range = image_view_info.into(); + let mut image_access = AccessType::DepthStencilAttachmentRead; + let image_range = image_view_info.into(); - // Upgrade existing read access to read-write - if let Some(accesses) = self - .cmd - .cmd_mut() - .execs - .last_mut() - .unwrap() - .accesses - .get_mut(&node_idx) - { - for SubresourceAccess { - access, - subresource, - } in accesses + // Upgrade existing write access to read-write + if let Some(accesses) = self + .cmd + .cmd_mut() + .execs + .last_mut() + .unwrap() + .accesses + .get_mut(&node_idx) { - let access_image_range = *subresource.as_image().unwrap(); - if !image_subresource_range_intersects(access_image_range, image_range) { - continue; - } + for SubresourceAccess { + access, + subresource, + } in accesses + { + let access_image_range = *subresource.as_image().unwrap(); + if !image_subresource_range_intersects(access_image_range, image_range) { + continue; + } - image_access = match *access { - AccessType::DepthAttachmentWriteStencilReadOnly => { - if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::STENCIL) - { - AccessType::DepthStencilAttachmentReadWrite - } else { + image_access = match *access { + AccessType::DepthAttachmentWriteStencilReadOnly => { AccessType::DepthAttachmentWriteStencilReadOnly } - } - AccessType::DepthStencilAttachmentRead => { - if !image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH) - { - AccessType::StencilAttachmentWriteDepthReadOnly - } else { - AccessType::DepthAttachmentWriteStencilReadOnly + AccessType::DepthStencilAttachmentRead => { + AccessType::DepthStencilAttachmentRead } - } - AccessType::DepthStencilAttachmentWrite => { - AccessType::DepthStencilAttachmentWrite - } - AccessType::StencilAttachmentWriteDepthReadOnly => { - if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH) - { + AccessType::DepthStencilAttachmentWrite => { AccessType::DepthStencilAttachmentReadWrite - } else { + } + AccessType::StencilAttachmentWriteDepthReadOnly => { AccessType::StencilAttachmentWriteDepthReadOnly } - } - _ => continue, - }; + _ => continue, + }; - *access = image_access; + *access = image_access; - // If the clear access is a subset of the existing access range there is no need - // to push a new access - if image_subresource_range_contains(access_image_range, image_range) { - return self; + // If the load access is a subset of the existing access range there is no need + // to push a new access + if image_subresource_range_contains(access_image_range, image_range) { + return self; + } } } - } - - self.cmd - .push_node_access(image, image_access, SubresourceRange::Image(image_range)); - self - } + self.cmd + .push_node_access(image, image_access, SubresourceRange::Image(image_range)); - /// Specifies `VK_ATTACHMENT_LOAD_OP_LOAD` for the render pass attachment, and loads an image - /// into the framebuffer. - pub fn load_color( - self, - attachment_idx: AttachmentIndex, - image: impl Into, - ) -> Self { - let image = image.into(); - let image_info = self.resource(image).info; + self + } - // Use the plain node information as the whole view of the node - let image_view_info = image_info; + #[deprecated] + #[doc(hidden)] + pub fn store_color( + self, + attachment_idx: AttachmentIndex, + image: impl Into, + ) -> Self { + let image = image.into(); + let image_view_info = self.resource(image).info; - self.load_color_as(attachment_idx, image, image_view_info) - } + #[allow(deprecated)] + self.store_color_as(attachment_idx, image, image_view_info) + } - /// Specifies `VK_ATTACHMENT_LOAD_OP_LOAD` for the render pass attachment, and loads an image - /// into the framebuffer. - pub fn load_color_as( - mut self, - attachment_idx: AttachmentIndex, - image: impl Into, - image_view_info: impl Into, - ) -> Self { - let image = image.into(); - let image_view_info = image_view_info.into(); - let node_idx = image.index(); - let ImageInfo { sample_count, .. } = self.resource(image).info; + #[deprecated] + #[doc(hidden)] + pub fn store_color_as( + mut self, + attachment_idx: AttachmentIndex, + image: impl Into, + image_view_info: impl Into, + ) -> Self { + let image = image.into(); + let image_view_info = image_view_info.into(); + let node_idx = image.index(); + let ImageInfo { sample_count, .. } = self.resource(image).info; - debug_assert!( - !self - .cmd - .cmd() - .execs - .last() - .unwrap() - .color_attachments - .contains_key(&attachment_idx), - "color attachment {attachment_idx} already attached" - ); - debug_assert!( - !self - .cmd - .cmd() + self.cmd + .cmd_mut() .execs - .last() + .last_mut() .unwrap() - .color_clears - .contains_key(&attachment_idx), - "color attachment {attachment_idx} already attached via clear" - ); + .color_stores + .insert( + attachment_idx, + Attachment::new(image_view_info, sample_count, node_idx), + ); - self.cmd - .cmd_mut() - .execs - .last_mut() - .unwrap() - .color_loads - .insert( - attachment_idx, - Attachment::new(image_view_info, sample_count, node_idx), + debug_assert!( + Attachment::are_compatible( + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_attachments + .get(&attachment_idx) + .copied(), + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_stores + .get(&attachment_idx) + .copied() + ), + "color attachment {attachment_idx} store incompatible with existing attachment" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_clears + .get(&attachment_idx) + .map(|(attachment, _)| *attachment), + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_stores + .get(&attachment_idx) + .copied() + ), + "color attachment {attachment_idx} store incompatible with existing clear" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_loads + .get(&attachment_idx) + .copied(), + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_stores + .get(&attachment_idx) + .copied() + ), + "color attachment {attachment_idx} store incompatible with existing load" ); - debug_assert!( - Attachment::are_compatible( - self.cmd - .cmd() - .execs - .last() - .unwrap() - .color_resolves - .get(&attachment_idx) - .map(|(attachment, _)| *attachment), - self.cmd - .cmd() - .execs - .last() - .unwrap() - .color_loads - .get(&attachment_idx) - .copied() - ), - "color attachment {attachment_idx} load incompatible with existing resolve" - ); - debug_assert!( - Attachment::are_compatible( - self.cmd - .cmd() - .execs - .last() - .unwrap() - .color_stores - .get(&attachment_idx) - .copied(), - self.cmd - .cmd() - .execs - .last() - .unwrap() - .color_loads - .get(&attachment_idx) - .copied() - ), - "color attachment {attachment_idx} load incompatible with existing store" - ); - - let mut image_access = AccessType::ColorAttachmentRead; - let image_range = image_view_info.into(); + let mut image_access = AccessType::ColorAttachmentWrite; + let image_range = image_view_info.into(); - // Upgrade existing write access to read-write - if let Some(accesses) = self - .cmd - .cmd_mut() - .execs - .last_mut() - .unwrap() - .accesses - .get_mut(&node_idx) - { - for SubresourceAccess { - access, - subresource, - } in accesses + // Upgrade existing read access to read-write + if let Some(accesses) = self + .cmd + .cmd_mut() + .execs + .last_mut() + .unwrap() + .accesses + .get_mut(&node_idx) { - let access_image_range = *subresource.as_image().unwrap(); - if !image_subresource_range_intersects(access_image_range, image_range) { - continue; - } - - image_access = match *access { - AccessType::ColorAttachmentRead => AccessType::ColorAttachmentRead, - AccessType::ColorAttachmentReadWrite | AccessType::ColorAttachmentWrite => { - AccessType::ColorAttachmentReadWrite + for SubresourceAccess { + access, + subresource, + } in accesses + { + let access_image_range = *subresource.as_image().unwrap(); + if !image_subresource_range_intersects(access_image_range, image_range) { + continue; } - _ => continue, - }; - *access = image_access; + image_access = match *access { + AccessType::ColorAttachmentRead | AccessType::ColorAttachmentReadWrite => { + AccessType::ColorAttachmentReadWrite + } + AccessType::ColorAttachmentWrite => AccessType::ColorAttachmentWrite, + _ => continue, + }; + + *access = image_access; - // If the load access is a subset of the existing access range there is no need - // to push a new access - if image_subresource_range_contains(access_image_range, image_range) { - return self; + // If the store access is a subset of the existing access range there is no need + // to push a new access + if image_subresource_range_contains(access_image_range, image_range) { + return self; + } } } - } - self.cmd - .push_node_access(image, image_access, SubresourceRange::Image(image_range)); + self.cmd + .push_node_access(image, image_access, SubresourceRange::Image(image_range)); - self - } + self + } - /// Specifies `VK_ATTACHMENT_LOAD_OP_LOAD` for the render pass attachment, and loads an image - /// into the framebuffer. - pub fn load_depth_stencil(self, image: impl Into) -> Self { - let image = image.into(); - let image_view_info = self.resource(image).info; + #[deprecated] + #[doc(hidden)] + pub fn store_depth_stencil(self, image: impl Into) -> Self { + let image = image.into(); + let image_view_info = self.resource(image).info; - self.load_depth_stencil_as(image, image_view_info) - } + #[allow(deprecated)] + self.store_depth_stencil_as(image, image_view_info) + } - /// Specifies `VK_ATTACHMENT_LOAD_OP_LOAD` for the render pass attachment, and loads an image - /// into the framebuffer. - pub fn load_depth_stencil_as( - mut self, - image: impl Into, - image_view_info: impl Into, - ) -> Self { - let image = image.into(); - let image_view_info = image_view_info.into(); - let node_idx = image.index(); - let ImageInfo { sample_count, .. } = self.resource(image).info; + #[deprecated] + #[doc(hidden)] + pub fn store_depth_stencil_as( + mut self, + image: impl Into, + image_view_info: impl Into, + ) -> Self { + let image = image.into(); + let image_view_info = image_view_info.into(); + let node_idx = image.index(); + let ImageInfo { sample_count, .. } = self.resource(image).info; - debug_assert!( - self.cmd - .cmd() - .execs - .last() - .unwrap() - .depth_stencil_attachment - .is_none(), - "depth/stencil attachment already attached" - ); - debug_assert!( self.cmd - .cmd() + .cmd_mut() .execs - .last() + .last_mut() .unwrap() - .depth_stencil_clear - .is_none(), - "depth/stencil attachment already attached via clear" - ); + .depth_stencil_store = + Some(Attachment::new(image_view_info, sample_count, node_idx)); - self.cmd - .cmd_mut() - .execs - .last_mut() - .unwrap() - .depth_stencil_load = Some(Attachment::new(image_view_info, sample_count, node_idx)); + debug_assert!( + Attachment::are_compatible( + self.cmd + .cmd() + .execs + .last() + .unwrap() + .depth_stencil_attachment, + self.cmd.cmd().execs.last().unwrap().depth_stencil_store + ), + "depth/stencil attachment store incompatible with existing attachment" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd + .cmd() + .execs + .last() + .unwrap() + .depth_stencil_clear + .map(|(attachment, _)| attachment), + self.cmd.cmd().execs.last().unwrap().depth_stencil_store + ), + "depth/stencil attachment store incompatible with existing clear" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd.cmd().execs.last().unwrap().depth_stencil_load, + self.cmd.cmd().execs.last().unwrap().depth_stencil_store + ), + "depth/stencil attachment store incompatible with existing load" + ); - debug_assert!( - Attachment::are_compatible( - self.cmd - .cmd() - .execs - .last() - .unwrap() - .depth_stencil_resolve - .map(|(attachment, ..)| attachment), - self.cmd.cmd().execs.last().unwrap().depth_stencil_load - ), - "depth/stencil attachment load incompatible with existing resolve" - ); - debug_assert!( - Attachment::are_compatible( - self.cmd.cmd().execs.last().unwrap().depth_stencil_store, - self.cmd.cmd().execs.last().unwrap().depth_stencil_load - ), - "depth/stencil attachment load incompatible with existing store" - ); - - let mut image_access = AccessType::DepthStencilAttachmentRead; - let image_range = image_view_info.into(); - - // Upgrade existing write access to read-write - if let Some(accesses) = self - .cmd - .cmd_mut() - .execs - .last_mut() - .unwrap() - .accesses - .get_mut(&node_idx) - { - for SubresourceAccess { - access, - subresource, - } in accesses + let mut image_access = if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL) { - let access_image_range = *subresource.as_image().unwrap(); - if !image_subresource_range_intersects(access_image_range, image_range) { - continue; - } - - image_access = match *access { - AccessType::DepthAttachmentWriteStencilReadOnly => { - AccessType::DepthAttachmentWriteStencilReadOnly - } - AccessType::DepthStencilAttachmentRead => { - AccessType::DepthStencilAttachmentRead - } - AccessType::DepthStencilAttachmentWrite => { - AccessType::DepthStencilAttachmentReadWrite - } - AccessType::StencilAttachmentWriteDepthReadOnly => { - AccessType::StencilAttachmentWriteDepthReadOnly - } - _ => continue, - }; - - *access = image_access; - - // If the load access is a subset of the existing access range there is no need - // to push a new access - if image_subresource_range_contains(access_image_range, image_range) { - return self; - } - } - } - - self.cmd - .push_node_access(image, image_access, SubresourceRange::Image(image_range)); - - self - } - - /// Begin recording a graphics pipeline command buffer. - pub fn record_cmd_buf( - mut self, - func: impl FnOnce(GraphicPipelineRef<'_>, Resources<'_>) + Send + 'static, - ) -> Self { - let pipeline = self - .cmd - .cmd() - .execs - .last() - .unwrap() - .pipeline - .as_ref() - .unwrap() - .unwrap_graphic() - .clone(); - - self.cmd.push_execute(move |cmd_buf, resources| { - func( - GraphicPipelineRef { - cmd_buf: CommandBufferRef { cmd_buf, resources }, - pipeline, - }, - resources, - ); - }); - - self - } - - /// Resolves a multisample framebuffer to a non-multisample image for the render pass - /// attachment. - pub fn resolve_color( - self, - src_attachment_idx: AttachmentIndex, - dst_attachment_idx: AttachmentIndex, - image: impl Into, - ) -> Self { - let image = image.into(); - let image_view_info = self.resource(image).info; - - self.resolve_color_as( - src_attachment_idx, - dst_attachment_idx, - image, - image_view_info, - ) - } - - /// Resolves a multisample framebuffer to a non-multisample image for the render pass - /// attachment. - pub fn resolve_color_as( - mut self, - src_attachment_idx: AttachmentIndex, - dst_attachment_idx: AttachmentIndex, - image: impl Into, - image_view_info: impl Into, - ) -> Self { - let image = image.into(); - let image_view_info = image_view_info.into(); - let node_idx = image.index(); - let ImageInfo { sample_count, .. } = self.resource(image).info; - - self.cmd - .cmd_mut() - .execs - .last_mut() - .unwrap() - .color_resolves - .insert( - dst_attachment_idx, - ( - Attachment::new(image_view_info, sample_count, node_idx), - src_attachment_idx, - ), - ); - - debug_assert!( - Attachment::are_compatible( - self.cmd - .cmd() - .execs - .last() - .unwrap() - .color_attachments - .get(&dst_attachment_idx) - .copied(), - self.cmd - .cmd() - .execs - .last() - .unwrap() - .color_resolves - .get(&dst_attachment_idx) - .map(|(attachment, _)| *attachment) - ), - "color attachment {dst_attachment_idx} resolve incompatible with existing attachment" - ); - debug_assert!( - Attachment::are_compatible( - self.cmd - .cmd() - .execs - .last() - .unwrap() - .color_clears - .get(&dst_attachment_idx) - .map(|(attachment, _)| *attachment), - self.cmd - .cmd() - .execs - .last() - .unwrap() - .color_resolves - .get(&dst_attachment_idx) - .map(|(attachment, _)| *attachment) - ), - "color attachment {dst_attachment_idx} resolve incompatible with existing clear" - ); - debug_assert!( - Attachment::are_compatible( - self.cmd - .cmd() - .execs - .last() - .unwrap() - .color_loads - .get(&dst_attachment_idx) - .copied(), - self.cmd - .cmd() - .execs - .last() - .unwrap() - .color_resolves - .get(&dst_attachment_idx) - .map(|(attachment, _)| *attachment) - ), - "color attachment {dst_attachment_idx} resolve incompatible with existing load" - ); - - let mut image_access = AccessType::ColorAttachmentWrite; - let image_range = image_view_info.into(); - - // Upgrade existing read access to read-write - if let Some(accesses) = self - .cmd - .cmd_mut() - .execs - .last_mut() - .unwrap() - .accesses - .get_mut(&node_idx) - { - for SubresourceAccess { - access, - subresource, - } in accesses + AccessType::DepthStencilAttachmentWrite + } else if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH) { - let access_image_range = *subresource.as_image().unwrap(); - if !image_subresource_range_intersects(access_image_range, image_range) { - continue; - } - - image_access = match *access { - AccessType::ColorAttachmentRead | AccessType::ColorAttachmentReadWrite => { - AccessType::ColorAttachmentReadWrite - } - AccessType::ColorAttachmentWrite => AccessType::ColorAttachmentWrite, - _ => continue, - }; - - *access = image_access; - - // If the resolve access is a subset of the existing access range there is no need - // to push a new access - if image_subresource_range_contains(access_image_range, image_range) { - return self; - } - } - } - - self.cmd - .push_node_access(image, image_access, SubresourceRange::Image(image_range)); - - self - } - - /// Resolves a multisample framebuffer to a non-multisample image for the render pass - /// attachment. - pub fn resolve_depth_stencil( - self, - dst_attachment_idx: AttachmentIndex, - image: impl Into, - depth_mode: Option, - stencil_mode: Option, - ) -> Self { - let image = image.into(); - let image_view_info = self.resource(image).info; - - self.resolve_depth_stencil_as( - dst_attachment_idx, - image, - image_view_info, - depth_mode, - stencil_mode, - ) - } - - /// Resolves a multisample framebuffer to a non-multisample image for the render pass - /// attachment. - pub fn resolve_depth_stencil_as( - mut self, - dst_attachment_idx: AttachmentIndex, - image: impl Into, - image_view_info: impl Into, - depth_mode: Option, - stencil_mode: Option, - ) -> Self { - let image = image.into(); - let image_view_info = image_view_info.into(); - let node_idx = image.index(); - let ImageInfo { sample_count, .. } = self.resource(image).info; - - self.cmd - .cmd_mut() - .execs - .last_mut() - .unwrap() - .depth_stencil_resolve = Some(( - Attachment::new(image_view_info, sample_count, node_idx), - dst_attachment_idx, - depth_mode, - stencil_mode, - )); - - let mut image_access = if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL) - { - AccessType::DepthStencilAttachmentWrite - } else if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH) - { - AccessType::DepthAttachmentWriteStencilReadOnly - } else { - debug_assert!( - image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::STENCIL) - ); + AccessType::DepthAttachmentWriteStencilReadOnly + } else { + debug_assert!( + image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::STENCIL) + ); - AccessType::StencilAttachmentWriteDepthReadOnly - }; - let image_range = image_view_info.into(); + AccessType::StencilAttachmentWriteDepthReadOnly + }; + let image_range = image_view_info.into(); - // Upgrade existing read access to read-write - if let Some(accesses) = self - .cmd - .cmd_mut() - .execs - .last_mut() - .unwrap() - .accesses - .get_mut(&node_idx) - { - for SubresourceAccess { - access, - subresource, - } in accesses + // Upgrade existing read access to read-write + if let Some(accesses) = self + .cmd + .cmd_mut() + .execs + .last_mut() + .unwrap() + .accesses + .get_mut(&node_idx) { - let access_image_range = *subresource.as_image().unwrap(); - if !image_subresource_range_intersects(access_image_range, image_range) { - continue; - } + for SubresourceAccess { + access, + subresource, + } in accesses + { + let access_image_range = *subresource.as_image().unwrap(); + if !image_subresource_range_intersects(access_image_range, image_range) { + continue; + } - image_access = match *access { - AccessType::DepthAttachmentWriteStencilReadOnly => { - if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::STENCIL) - { - AccessType::DepthStencilAttachmentReadWrite - } else { - AccessType::DepthAttachmentWriteStencilReadOnly + image_access = match *access { + AccessType::DepthAttachmentWriteStencilReadOnly => { + if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::STENCIL) + { + AccessType::DepthStencilAttachmentReadWrite + } else { + AccessType::DepthAttachmentWriteStencilReadOnly + } } - } - AccessType::DepthStencilAttachmentRead => { - if !image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH) - { - AccessType::StencilAttachmentWriteDepthReadOnly - } else { - AccessType::DepthStencilAttachmentReadWrite + AccessType::DepthStencilAttachmentRead => { + if !image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH) + { + AccessType::StencilAttachmentWriteDepthReadOnly + } else { + AccessType::DepthStencilAttachmentReadWrite + } } - } - AccessType::DepthStencilAttachmentWrite => { - AccessType::DepthStencilAttachmentWrite - } - AccessType::StencilAttachmentWriteDepthReadOnly => { - if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH) - { - AccessType::DepthStencilAttachmentReadWrite - } else { - AccessType::StencilAttachmentWriteDepthReadOnly + AccessType::DepthStencilAttachmentWrite => { + AccessType::DepthStencilAttachmentWrite } - } - _ => continue, - }; + AccessType::StencilAttachmentWriteDepthReadOnly => { + if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH) + { + AccessType::DepthStencilAttachmentReadWrite + } else { + AccessType::StencilAttachmentWriteDepthReadOnly + } + } + _ => continue, + }; - *access = image_access; + *access = image_access; - // If the resolve access is a subset of the existing access range there is no need - // to push a new access - if image_subresource_range_contains(access_image_range, image_range) { - return self; + // If the store access is a subset of the existing access range there is no need + // to push a new access + if image_subresource_range_contains(access_image_range, image_range) { + return self; + } } } - } - - self.cmd - .push_node_access(image, image_access, SubresourceRange::Image(image_range)); - self - } - - /// Sets a particular depth/stencil mode. - pub fn set_depth_stencil(mut self, depth_stencil: DepthStencilMode) -> Self { - let pass = self.cmd.cmd_mut(); - let exec = pass.execs.last_mut().unwrap(); - - assert!(exec.depth_stencil.is_none()); - - exec.depth_stencil = Some(depth_stencil); - - self - } - - /// Sets multiview view and correlation masks. - /// - /// See [`VkRenderPassMultiviewCreateInfo`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkRenderPassMultiviewCreateInfo.html#_description). - pub fn set_multiview(mut self, view_mask: u32, correlated_view_mask: u32) -> Self { - let pass = self.cmd.cmd_mut(); - let exec = pass.execs.last_mut().unwrap(); - - exec.correlated_view_mask = correlated_view_mask; - exec.view_mask = view_mask; - - self - } + self.cmd + .push_node_access(image, image_access, SubresourceRange::Image(image_range)); - /// Sets the [`renderArea`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkRenderPassBeginInfo.html#_c_specification) - /// field when beginning a render pass. - /// - /// NOTE: Setting this value will cause the viewport and scissor to be unset, which is not the default - /// behavior. When this value is set you should call `set_viewport` and `set_scissor` on the subpass. - /// - /// If not set, this value defaults to the first loaded, resolved, or stored attachment dimensions and - /// sets the viewport and scissor to the same values, with a `0..1` depth if not specified by - /// `set_depth_stencil`. - pub fn set_render_area(mut self, x: i32, y: i32, width: u32, height: u32) -> Self { - self.cmd.cmd_mut().execs.last_mut().unwrap().render_area = Some(Area { - height, - width, - x, - y, - }); + self + } - self - } + #[deprecated] + #[doc(hidden)] + pub fn resolve_color( + self, + src_attachment_idx: AttachmentIndex, + dst_attachment_idx: AttachmentIndex, + image: impl Into, + ) -> Self { + let image = image.into(); + let image_view_info = self.resource(image).info; - /// Specifies `VK_ATTACHMENT_STORE_OP_STORE` for the render pass attachment, and stores the - /// rendered pixels into an image. - pub fn store_color( - self, - attachment_idx: AttachmentIndex, - image: impl Into, - ) -> Self { - let image = image.into(); - let image_view_info = self.resource(image).info; + #[allow(deprecated)] + self.resolve_color_as( + src_attachment_idx, + dst_attachment_idx, + image, + image_view_info, + ) + } - self.store_color_as(attachment_idx, image, image_view_info) - } + #[deprecated] + #[doc(hidden)] + pub fn resolve_color_as( + mut self, + src_attachment_idx: AttachmentIndex, + dst_attachment_idx: AttachmentIndex, + image: impl Into, + image_view_info: impl Into, + ) -> Self { + let image = image.into(); + let image_view_info = image_view_info.into(); + let node_idx = image.index(); + let ImageInfo { sample_count, .. } = self.resource(image).info; - /// Specifies `VK_ATTACHMENT_STORE_OP_STORE` for the render pass attachment, and stores the - /// rendered pixels into an image. - pub fn store_color_as( - mut self, - attachment_idx: AttachmentIndex, - image: impl Into, - image_view_info: impl Into, - ) -> Self { - let image = image.into(); - let image_view_info = image_view_info.into(); - let node_idx = image.index(); - let ImageInfo { sample_count, .. } = self.resource(image).info; + self.cmd + .cmd_mut() + .execs + .last_mut() + .unwrap() + .color_resolves + .insert( + dst_attachment_idx, + ( + Attachment::new(image_view_info, sample_count, node_idx), + src_attachment_idx, + ), + ); - self.cmd - .cmd_mut() - .execs - .last_mut() - .unwrap() - .color_stores - .insert( - attachment_idx, - Attachment::new(image_view_info, sample_count, node_idx), + debug_assert!( + Attachment::are_compatible( + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_attachments + .get(&dst_attachment_idx) + .copied(), + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_resolves + .get(&dst_attachment_idx) + .map(|(attachment, _)| *attachment) + ), + "color attachment {dst_attachment_idx} resolve incompatible with existing attachment" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_clears + .get(&dst_attachment_idx) + .map(|(attachment, _)| *attachment), + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_resolves + .get(&dst_attachment_idx) + .map(|(attachment, _)| *attachment) + ), + "color attachment {dst_attachment_idx} resolve incompatible with existing clear" + ); + debug_assert!( + Attachment::are_compatible( + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_loads + .get(&dst_attachment_idx) + .copied(), + self.cmd + .cmd() + .execs + .last() + .unwrap() + .color_resolves + .get(&dst_attachment_idx) + .map(|(attachment, _)| *attachment) + ), + "color attachment {dst_attachment_idx} resolve incompatible with existing load" ); - debug_assert!( - Attachment::are_compatible( - self.cmd - .cmd() - .execs - .last() - .unwrap() - .color_attachments - .get(&attachment_idx) - .copied(), - self.cmd - .cmd() - .execs - .last() - .unwrap() - .color_stores - .get(&attachment_idx) - .copied() - ), - "color attachment {attachment_idx} store incompatible with existing attachment" - ); - debug_assert!( - Attachment::are_compatible( - self.cmd - .cmd() - .execs - .last() - .unwrap() - .color_clears - .get(&attachment_idx) - .map(|(attachment, _)| *attachment), - self.cmd - .cmd() - .execs - .last() - .unwrap() - .color_stores - .get(&attachment_idx) - .copied() - ), - "color attachment {attachment_idx} store incompatible with existing clear" - ); - debug_assert!( - Attachment::are_compatible( - self.cmd - .cmd() - .execs - .last() - .unwrap() - .color_loads - .get(&attachment_idx) - .copied(), - self.cmd - .cmd() - .execs - .last() - .unwrap() - .color_stores - .get(&attachment_idx) - .copied() - ), - "color attachment {attachment_idx} store incompatible with existing load" - ); - - let mut image_access = AccessType::ColorAttachmentWrite; - let image_range = image_view_info.into(); - - // Upgrade existing read access to read-write - if let Some(accesses) = self - .cmd - .cmd_mut() - .execs - .last_mut() - .unwrap() - .accesses - .get_mut(&node_idx) - { - for SubresourceAccess { - access, - subresource, - } in accesses - { - let access_image_range = *subresource.as_image().unwrap(); - if !image_subresource_range_intersects(access_image_range, image_range) { - continue; - } + let mut image_access = AccessType::ColorAttachmentWrite; + let image_range = image_view_info.into(); - image_access = match *access { - AccessType::ColorAttachmentRead | AccessType::ColorAttachmentReadWrite => { - AccessType::ColorAttachmentReadWrite + // Upgrade existing read access to read-write + if let Some(accesses) = self + .cmd + .cmd_mut() + .execs + .last_mut() + .unwrap() + .accesses + .get_mut(&node_idx) + { + for SubresourceAccess { + access, + subresource, + } in accesses + { + let access_image_range = *subresource.as_image().unwrap(); + if !image_subresource_range_intersects(access_image_range, image_range) { + continue; } - AccessType::ColorAttachmentWrite => AccessType::ColorAttachmentWrite, - _ => continue, - }; - *access = image_access; + image_access = match *access { + AccessType::ColorAttachmentRead | AccessType::ColorAttachmentReadWrite => { + AccessType::ColorAttachmentReadWrite + } + AccessType::ColorAttachmentWrite => AccessType::ColorAttachmentWrite, + _ => continue, + }; - // If the store access is a subset of the existing access range there is no need - // to push a new access - if image_subresource_range_contains(access_image_range, image_range) { - return self; + *access = image_access; + + // If the resolve access is a subset of the existing access range there is no need + // to push a new access + if image_subresource_range_contains(access_image_range, image_range) { + return self; + } } } - } - self.cmd - .push_node_access(image, image_access, SubresourceRange::Image(image_range)); + self.cmd + .push_node_access(image, image_access, SubresourceRange::Image(image_range)); - self - } + self + } - /// Specifies `VK_ATTACHMENT_STORE_OP_STORE` for the render pass attachment, and stores the - /// rendered pixels into an image. - pub fn store_depth_stencil(self, image: impl Into) -> Self { - let image = image.into(); - let image_view_info = self.resource(image).info; + #[deprecated] + #[doc(hidden)] + pub fn resolve_depth_stencil( + self, + dst_attachment_idx: AttachmentIndex, + image: impl Into, + depth_mode: Option, + stencil_mode: Option, + ) -> Self { + let image = image.into(); + let image_view_info = self.resource(image).info; - self.store_depth_stencil_as(image, image_view_info) - } + #[allow(deprecated)] + self.resolve_depth_stencil_as( + dst_attachment_idx, + image, + image_view_info, + depth_mode, + stencil_mode, + ) + } - /// Specifies `VK_ATTACHMENT_STORE_OP_STORE` for the render pass attachment, and stores the - /// rendered pixels into an image. - /// - /// _NOTE:_ Order matters, call store after clear or load. - pub fn store_depth_stencil_as( - mut self, - image: impl Into, - image_view_info: impl Into, - ) -> Self { - let image = image.into(); - let image_view_info = image_view_info.into(); - let node_idx = image.index(); - let ImageInfo { sample_count, .. } = self.resource(image).info; + #[deprecated] + #[doc(hidden)] + pub fn resolve_depth_stencil_as( + mut self, + dst_attachment_idx: AttachmentIndex, + image: impl Into, + image_view_info: impl Into, + depth_mode: Option, + stencil_mode: Option, + ) -> Self { + let image = image.into(); + let image_view_info = image_view_info.into(); + let node_idx = image.index(); + let ImageInfo { sample_count, .. } = self.resource(image).info; - self.cmd - .cmd_mut() - .execs - .last_mut() - .unwrap() - .depth_stencil_store = Some(Attachment::new(image_view_info, sample_count, node_idx)); + self.cmd + .cmd_mut() + .execs + .last_mut() + .unwrap() + .depth_stencil_resolve = Some(( + Attachment::new(image_view_info, sample_count, node_idx), + dst_attachment_idx, + depth_mode, + stencil_mode, + )); - debug_assert!( - Attachment::are_compatible( - self.cmd - .cmd() - .execs - .last() - .unwrap() - .depth_stencil_attachment, - self.cmd.cmd().execs.last().unwrap().depth_stencil_store - ), - "depth/stencil attachment store incompatible with existing attachment" - ); - debug_assert!( - Attachment::are_compatible( - self.cmd - .cmd() - .execs - .last() - .unwrap() - .depth_stencil_clear - .map(|(attachment, _)| attachment), - self.cmd.cmd().execs.last().unwrap().depth_stencil_store - ), - "depth/stencil attachment store incompatible with existing clear" - ); - debug_assert!( - Attachment::are_compatible( - self.cmd.cmd().execs.last().unwrap().depth_stencil_load, - self.cmd.cmd().execs.last().unwrap().depth_stencil_store - ), - "depth/stencil attachment store incompatible with existing load" - ); - - let mut image_access = if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL) - { - AccessType::DepthStencilAttachmentWrite - } else if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH) - { - AccessType::DepthAttachmentWriteStencilReadOnly - } else { - debug_assert!( - image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::STENCIL) - ); + let mut image_access = if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL) + { + AccessType::DepthStencilAttachmentWrite + } else if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH) + { + AccessType::DepthAttachmentWriteStencilReadOnly + } else { + debug_assert!( + image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::STENCIL) + ); - AccessType::StencilAttachmentWriteDepthReadOnly - }; - let image_range = image_view_info.into(); + AccessType::StencilAttachmentWriteDepthReadOnly + }; + let image_range = image_view_info.into(); - // Upgrade existing read access to read-write - if let Some(accesses) = self - .cmd - .cmd_mut() - .execs - .last_mut() - .unwrap() - .accesses - .get_mut(&node_idx) - { - for SubresourceAccess { - access, - subresource, - } in accesses + // Upgrade existing read access to read-write + if let Some(accesses) = self + .cmd + .cmd_mut() + .execs + .last_mut() + .unwrap() + .accesses + .get_mut(&node_idx) { - let access_image_range = *subresource.as_image().unwrap(); - if !image_subresource_range_intersects(access_image_range, image_range) { - continue; - } + for SubresourceAccess { + access, + subresource, + } in accesses + { + let access_image_range = *subresource.as_image().unwrap(); + if !image_subresource_range_intersects(access_image_range, image_range) { + continue; + } - image_access = match *access { - AccessType::DepthAttachmentWriteStencilReadOnly => { - if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::STENCIL) - { - AccessType::DepthStencilAttachmentReadWrite - } else { - AccessType::DepthAttachmentWriteStencilReadOnly + image_access = match *access { + AccessType::DepthAttachmentWriteStencilReadOnly => { + if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::STENCIL) + { + AccessType::DepthStencilAttachmentReadWrite + } else { + AccessType::DepthAttachmentWriteStencilReadOnly + } } - } - AccessType::DepthStencilAttachmentRead => { - if !image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH) - { - AccessType::StencilAttachmentWriteDepthReadOnly - } else { - AccessType::DepthStencilAttachmentReadWrite + AccessType::DepthStencilAttachmentRead => { + if !image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH) + { + AccessType::StencilAttachmentWriteDepthReadOnly + } else { + AccessType::DepthStencilAttachmentReadWrite + } } - } - AccessType::DepthStencilAttachmentWrite => { - AccessType::DepthStencilAttachmentWrite - } - AccessType::StencilAttachmentWriteDepthReadOnly => { - if image_view_info - .aspect_mask - .contains(vk::ImageAspectFlags::DEPTH) - { - AccessType::DepthStencilAttachmentReadWrite - } else { - AccessType::StencilAttachmentWriteDepthReadOnly + AccessType::DepthStencilAttachmentWrite => { + AccessType::DepthStencilAttachmentWrite } - } - _ => continue, - }; + AccessType::StencilAttachmentWriteDepthReadOnly => { + if image_view_info + .aspect_mask + .contains(vk::ImageAspectFlags::DEPTH) + { + AccessType::DepthStencilAttachmentReadWrite + } else { + AccessType::StencilAttachmentWriteDepthReadOnly + } + } + _ => continue, + }; - *access = image_access; + *access = image_access; - // If the store access is a subset of the existing access range there is no need - // to push a new access - if image_subresource_range_contains(access_image_range, image_range) { - return self; + // If the resolve access is a subset of the existing access range there is no need + // to push a new access + if image_subresource_range_contains(access_image_range, image_range) { + return self; + } } } - } - self.cmd - .push_node_access(image, image_access, SubresourceRange::Image(image_range)); + self.cmd + .push_node_access(image, image_access, SubresourceRange::Image(image_range)); - self + self + } } -} - -#[allow(unused)] -mod deprecated { - use { - crate::{ - cmd_ref::{ - Descriptor, PipelineRef, Resources, SubresourceRange, View, ViewInfo, - graphic::GraphicPipelineRef, - }, - driver::graphic::GraphicPipeline, - node::Node, - }, - vk_sync::AccessType, - }; - impl PipelineRef<'_, GraphicPipeline> { + // Resource functions + impl PipelineCommandRef<'_, GraphicPipeline> { #[deprecated = "use shader_resource_access function with AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer"] #[doc(hidden)] pub fn read_descriptor(self, descriptor: impl Into, node: N) -> Self @@ -2217,7 +2265,7 @@ mod deprecated { #[doc(hidden)] pub fn record_subpass( self, - func: impl FnOnce(GraphicPipelineRef<'_>, Resources<'_>) + Send + 'static, + func: impl FnOnce(GraphicCommandBufferRef<'_>, Resources<'_>) + Send + 'static, ) -> Self { self.record_cmd_buf(func) } diff --git a/src/cmd_ref/mod.rs b/src/cmd_ref/mod.rs index ccb5a29a..055b0f8e 100644 --- a/src/cmd_ref/mod.rs +++ b/src/cmd_ref/mod.rs @@ -12,7 +12,7 @@ pub use self::{ BuildAccelerationStructureIndirectInfo, BuildAccelerationStructureInfo, CommandBufferRef, UpdateAccelerationStructureIndirectInfo, UpdateAccelerationStructureInfo, }, - pipeline::PipelineRef, + pipeline::PipelineCommandRef, }; use { @@ -324,9 +324,9 @@ impl From<(DescriptorSetIndex, BindingIndex, [BindingOffset; 1])> for Descriptor /// /// - [`PassRef::record_accel_struct`] for building and updating acceleration structures /// - [`PassRef::record_cmd_buf`] for general command streams -/// - [`PipelineRef::record_pipeline`] for dispatched compute operations -/// - [`PipelineRef::record_pipeline`] for raster drawing operations, such as triangle streams -/// - [`PipelineRef::record_pipeline`] for ray-traced operations +/// - [`PipelineCommandRef::record_pipeline`] for dispatched compute operations +/// - [`PipelineCommandRef::record_pipeline`] for raster drawing operations, such as triangle streams +/// - [`PipelineCommandRef::record_pipeline`] for ray-traced operations /// /// # Examples /// diff --git a/src/cmd_ref/pipeline.rs b/src/cmd_ref/pipeline.rs index d20d865b..95615399 100644 --- a/src/cmd_ref/pipeline.rs +++ b/src/cmd_ref/pipeline.rs @@ -7,13 +7,13 @@ use { }; /// A render pass which has been bound to a particular compute, graphic, or ray-trace pipeline. -pub struct PipelineRef<'a, T> { +pub struct PipelineCommandRef<'a, T> { pub(super) __: PhantomData, pub(super) cmd: CommandRef<'a>, } // NOTE: There are specific implementations of T in the compute, graphic, and ray trace modules -impl<'a, T> PipelineRef<'a, T> { +impl<'a, T> PipelineCommandRef<'a, T> { /// Binds a Vulkan buffer, image, or acceleration structure resource to the graph associated /// with this command. /// @@ -209,7 +209,7 @@ impl<'a, T> PipelineRef<'a, T> { mod deprecated { use { crate::{ - cmd_ref::{PipelineRef, SubresourceRange, View}, + cmd_ref::{PipelineCommandRef, SubresourceRange, View}, deprecated::Info, node::Node, }, @@ -217,7 +217,7 @@ mod deprecated { vk_sync::AccessType, }; - impl<'a, T> PipelineRef<'a, T> { + impl<'a, T> PipelineCommandRef<'a, T> { #[deprecated = "use resource_access function"] #[doc(hidden)] pub fn access_resource(mut self, node: N, access: AccessType) -> Self diff --git a/src/cmd_ref/ray_trace.rs b/src/cmd_ref/ray_trace.rs index 642979e2..95019978 100644 --- a/src/cmd_ref/ray_trace.rs +++ b/src/cmd_ref/ray_trace.rs @@ -1,5 +1,5 @@ use { - super::{PipelineRef, Resources, cmd_buf::CommandBufferRef}, + super::{PipelineCommandRef, Resources, cmd_buf::CommandBufferRef}, crate::driver::{device::Device, ray_trace::RayTracePipeline}, ash::vk, log::trace, @@ -7,11 +7,11 @@ use { }; // NOTE: local implementation of type from super module -impl PipelineRef<'_, RayTracePipeline> { +impl PipelineCommandRef<'_, RayTracePipeline> { /// Begin recording a ray trace pipeline command buffer. pub fn record_cmd_buf( mut self, - func: impl FnOnce(RayTracePipelineRef<'_>, Resources<'_>) + Send + 'static, + func: impl FnOnce(RayTraceCommandBufferRef<'_>, Resources<'_>) + Send + 'static, ) -> Self { let pipeline = self .cmd @@ -30,7 +30,7 @@ impl PipelineRef<'_, RayTracePipeline> { self.cmd.push_execute(move |cmd_buf, resources| { func( - RayTracePipelineRef { + RayTraceCommandBufferRef { cmd_buf: CommandBufferRef { cmd_buf, resources }, #[cfg(debug_assertions)] @@ -50,7 +50,7 @@ impl PipelineRef<'_, RayTracePipeline> { /// /// This structure provides a strongly-typed set of methods which allow ray trace shader code to be /// executed. An instance of `RayTrace` is provided to the closure parameter of -/// [`PipelineRef::record_pipeline`] which may be accessed by binding a [`RayTracePipeline`] to +/// [`PipelineCommandRef::record_pipeline`] which may be accessed by binding a [`RayTracePipeline`] to /// a render pass. /// /// # Examples @@ -81,7 +81,7 @@ impl PipelineRef<'_, RayTracePipeline> { /// }); /// # Ok(()) } /// ``` -pub struct RayTracePipelineRef<'a> { +pub struct RayTraceCommandBufferRef<'a> { cmd_buf: CommandBufferRef<'a>, #[cfg(debug_assertions)] @@ -90,7 +90,7 @@ pub struct RayTracePipelineRef<'a> { pipeline: RayTracePipeline, } -impl RayTracePipelineRef<'_> { +impl RayTraceCommandBufferRef<'_> { /// Updates push constants. /// /// Push constants represent a high speed path to modify constant data in pipelines that is @@ -316,7 +316,7 @@ impl RayTracePipelineRef<'_> { } } -impl<'a> Deref for RayTracePipelineRef<'a> { +impl<'a> Deref for RayTraceCommandBufferRef<'a> { type Target = CommandBufferRef<'a>; fn deref(&self) -> &Self::Target { @@ -329,8 +329,8 @@ mod deprecated { use { crate::{ cmd_ref::{ - Descriptor, PipelineRef, Resources, SubresourceRange, View, ViewInfo, - ray_trace::RayTracePipelineRef, + Descriptor, PipelineCommandRef, Resources, SubresourceRange, View, ViewInfo, + ray_trace::RayTraceCommandBufferRef, }, driver::ray_trace::RayTracePipeline, node::Node, @@ -338,7 +338,7 @@ mod deprecated { vk_sync::AccessType, }; - impl PipelineRef<'_, RayTracePipeline> { + impl PipelineCommandRef<'_, RayTracePipeline> { #[deprecated = "use shader_resource_access function with AccessType::RayTracingShaderReadSampledImageOrUniformTexelBuffer"] #[doc(hidden)] pub fn read_descriptor(self, descriptor: impl Into, node: N) -> Self @@ -381,7 +381,7 @@ mod deprecated { #[doc(hidden)] pub fn record_ray_trace( self, - func: impl FnOnce(RayTracePipelineRef<'_>, Resources<'_>) + Send + 'static, + func: impl FnOnce(RayTraceCommandBufferRef<'_>, Resources<'_>) + Send + 'static, ) -> Self { self.record_cmd_buf(func) } diff --git a/src/driver/accel_struct.rs b/src/driver/accel_struct.rs index 905e5500..43469dad 100644 --- a/src/driver/accel_struct.rs +++ b/src/driver/accel_struct.rs @@ -660,7 +660,6 @@ impl AccelerationStructureGeometryInfo { derive(Clone, Copy, Debug), pattern = "owned" )] -#[non_exhaustive] pub struct AccelerationStructureInfo { /// Type of acceleration structure. #[builder(default = "vk::AccelerationStructureTypeKHR::GENERIC")] @@ -699,13 +698,18 @@ impl AccelerationStructureInfo { } /// Converts an `AccelerationStructureInfo` into an `AccelerationStructureInfoBuilder`. - #[inline(always)] - pub fn to_builder(self) -> AccelerationStructureInfoBuilder { + pub fn into_builder(self) -> AccelerationStructureInfoBuilder { AccelerationStructureInfoBuilder { ty: Some(self.ty), size: Some(self.size), } } + + #[deprecated = "use into_builder function"] + #[doc(hidden)] + pub fn to_builder(self) -> AccelerationStructureInfoBuilder { + self.into_builder() + } } impl From for AccelerationStructureInfo { diff --git a/src/driver/buffer.rs b/src/driver/buffer.rs index 88e5efba..08fed737 100644 --- a/src/driver/buffer.rs +++ b/src/driver/buffer.rs @@ -720,7 +720,6 @@ where derive(Clone, Copy, Debug), pattern = "owned" )] -#[non_exhaustive] pub struct BufferInfo { /// Byte alignment of the base device address of the buffer. /// @@ -804,8 +803,7 @@ impl BufferInfo { } /// Converts a `BufferInfo` into a `BufferInfoBuilder`. - #[inline(always)] - pub fn to_builder(self) -> BufferInfoBuilder { + pub fn into_builder(self) -> BufferInfoBuilder { BufferInfoBuilder { alignment: Some(self.alignment), dedicated: Some(self.dedicated), @@ -815,6 +813,12 @@ impl BufferInfo { usage: Some(self.usage), } } + + #[deprecated = "use into_builder function"] + #[doc(hidden)] + pub fn to_builder(self) -> BufferInfoBuilder { + self.into_builder() + } } impl From for BufferInfo { diff --git a/src/driver/cmd_buf.rs b/src/driver/cmd_buf.rs index 36bfb9df..2c483f0a 100644 --- a/src/driver/cmd_buf.rs +++ b/src/driver/cmd_buf.rs @@ -175,7 +175,6 @@ impl Drop for CommandBuffer { derive(Clone, Copy, Debug), pattern = "owned" )] -#[non_exhaustive] pub struct CommandBufferInfo { /// Designates a queue family as described in section /// [Queue Family Properties](https://docs.vulkan.org/spec/latest/chapters/devsandqueues.html#devsandqueues-queueprops). @@ -193,12 +192,17 @@ impl CommandBufferInfo { impl CommandBufferInfo { /// Converts a `CommandBufferInfo` into a `CommandBufferInfoBuilder`. - #[inline(always)] - pub fn to_builder(self) -> CommandBufferInfoBuilder { + pub fn into_builder(self) -> CommandBufferInfoBuilder { CommandBufferInfoBuilder { queue_family_index: Some(self.queue_family_index), } } + + #[deprecated = "use into_builder function"] + #[doc(hidden)] + pub fn to_builder(self) -> CommandBufferInfoBuilder { + self.into_builder() + } } impl From for CommandBufferInfo { diff --git a/src/driver/compute.rs b/src/driver/compute.rs index ed14826f..b622814c 100644 --- a/src/driver/compute.rs +++ b/src/driver/compute.rs @@ -211,7 +211,6 @@ impl ComputePipeline { derive(Clone, Copy, Debug), pattern = "owned" )] -#[non_exhaustive] pub struct ComputePipelineInfo { /// The number of descriptors to allocate for a given binding when using bindless (unbounded) /// syntax. @@ -242,12 +241,17 @@ pub struct ComputePipelineInfo { impl ComputePipelineInfo { /// Converts a `ComputePipelineInfo` into a `ComputePipelineInfoBuilder`. - #[inline(always)] - pub fn to_builder(self) -> ComputePipelineInfoBuilder { + pub fn into_builder(self) -> ComputePipelineInfoBuilder { ComputePipelineInfoBuilder { bindless_descriptor_count: Some(self.bindless_descriptor_count), } } + + #[deprecated = "use into_builder function"] + #[doc(hidden)] + pub fn to_builder(self) -> ComputePipelineInfoBuilder { + self.into_builder() + } } impl Default for ComputePipelineInfo { diff --git a/src/driver/device.rs b/src/driver/device.rs index e282f4d3..c053e0b8 100644 --- a/src/driver/device.rs +++ b/src/driver/device.rs @@ -403,7 +403,6 @@ impl Deref for Device { derive(Clone, Copy, Debug), pattern = "owned" )] -#[non_exhaustive] pub struct DeviceInfo { /// Enables Vulkan validation layers. /// @@ -474,13 +473,18 @@ impl DeviceInfo { } /// Converts a `DeviceInfo` into a `DeviceInfoBuilder`. - #[inline(always)] - pub fn to_builder(self) -> DeviceInfoBuilder { + pub fn into_builder(self) -> DeviceInfoBuilder { DeviceInfoBuilder { debug: Some(self.debug), physical_device_index: Some(self.physical_device_index), } } + + #[deprecated = "use into_builder function"] + #[doc(hidden)] + pub fn to_builder(self) -> DeviceInfoBuilder { + self.into_builder() + } } impl From for DeviceInfo { diff --git a/src/driver/graphic.rs b/src/driver/graphic.rs index bfcfbfa8..2d0c5cf0 100644 --- a/src/driver/graphic.rs +++ b/src/driver/graphic.rs @@ -20,6 +20,14 @@ use { }, }; +#[deprecated = "use BlendInfo instead"] +#[doc(hidden)] +pub type BlendMode = BlendInfo; + +#[deprecated = "use DepthStencilInfo instead"] +#[doc(hidden)] +pub type DepthStencilMode = DepthStencilInfo; + const RGBA_COLOR_COMPONENTS: vk::ColorComponentFlags = vk::ColorComponentFlags::from_raw( vk::ColorComponentFlags::R.as_raw() | vk::ColorComponentFlags::G.as_raw() @@ -34,11 +42,11 @@ const RGBA_COLOR_COMPONENTS: vk::ColorComponentFlags = vk::ColorComponentFlags:: /// [VkPipelineColorBlendAttachmentState](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineColorBlendAttachmentState.html). #[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)] #[builder( - build_fn(private, name = "fallible_build", error = "BlendModeBuilderError"), + build_fn(private, name = "fallible_build", error = "UninitializedFieldError"), derive(Clone, Copy, Debug), pattern = "owned" )] -pub struct BlendMode { +pub struct BlendInfo { /// Controls whether blending is enabled for the corresponding color attachment. /// /// If blending is not enabled, the source fragment’s color for that attachment is passed @@ -79,7 +87,7 @@ pub struct BlendMode { pub color_write_mask: vk::ColorComponentFlags, } -impl BlendMode { +impl BlendInfo { /// A commonly used blend mode for replacing color attachment values with new ones. pub const REPLACE: Self = Self { blend_enable: false, @@ -118,21 +126,34 @@ impl BlendMode { }; /// Specifies a default blend mode which is not enabled. - #[allow(clippy::new_ret_no_self)] - pub fn new() -> BlendModeBuilder { - BlendModeBuilder::default() + pub fn builder() -> BlendInfoBuilder { + BlendInfoBuilder::default() + } + + /// Converts a `BlendInfo` into a `BlendInfoBuilder`. + pub fn into_builder(self) -> BlendInfoBuilder { + BlendInfoBuilder { + blend_enable: Some(self.blend_enable), + src_color_blend_factor: Some(self.src_color_blend_factor), + dst_color_blend_factor: Some(self.dst_color_blend_factor), + color_blend_op: Some(self.color_blend_op), + src_alpha_blend_factor: Some(self.src_alpha_blend_factor), + dst_alpha_blend_factor: Some(self.dst_alpha_blend_factor), + alpha_blend_op: Some(self.alpha_blend_op), + color_write_mask: Some(self.color_write_mask), + } } } // the Builder derive Macro wants Default to be implemented for BlendMode -impl Default for BlendMode { +impl Default for BlendInfo { fn default() -> Self { Self::REPLACE } } -impl From for vk::PipelineColorBlendAttachmentState { - fn from(mode: BlendMode) -> Self { +impl From for vk::PipelineColorBlendAttachmentState { + fn from(mode: BlendInfo) -> Self { Self { blend_enable: mode.blend_enable as _, src_color_blend_factor: mode.src_color_blend_factor, @@ -146,45 +167,33 @@ impl From for vk::PipelineColorBlendAttachmentState { } } -// HACK: https://github.com/colin-kiegel/rust-derive-builder/issues/56 -impl BlendModeBuilder { +impl BlendInfoBuilder { /// Builds a new `BlendMode`. - pub fn build(self) -> BlendMode { + pub fn build(self) -> BlendInfo { self.fallible_build().unwrap() } } -#[derive(Debug)] -struct BlendModeBuilderError; - -impl From for BlendModeBuilderError { - fn from(_: UninitializedFieldError) -> Self { - Self - } -} - /// Specifies the [depth bounds tests], [stencil test], and [depth test] pipeline state. /// /// [depth bounds tests]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fragops-dbt /// [stencil test]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fragops-stencil /// [depth test]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fragops-depth -#[derive(Builder, Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[derive(Builder, Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] #[builder( - build_fn( - private, - name = "fallible_build", - error = "DepthStencilModeBuilderError" - ), + build_fn(private, name = "fallible_build", error = "UninitializedFieldError"), derive(Clone, Copy, Debug), pattern = "owned" )] -pub struct DepthStencilMode { +pub struct DepthStencilInfo { /// Control parameters of the stencil test. + #[builder(default)] pub back: StencilMode, /// Controls whether [depth bounds testing] is enabled. /// /// [depth bounds testing]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fragops-dbt + #[builder(default)] pub bounds_test: bool, /// A value specifying the comparison operator to use in the [depth comparison] step of the @@ -192,11 +201,13 @@ pub struct DepthStencilMode { /// /// [depth comparison]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fragops-depth-comparison /// [depth test]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fragops-depth + #[builder(default)] pub compare_op: vk::CompareOp, /// Controls whether [depth testing] is enabled. /// /// [depth testing]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fragops-depth + #[builder(default)] pub depth_test: bool, /// Controls whether [depth writes] are enabled when `depth_test` is `true`. @@ -204,32 +215,35 @@ pub struct DepthStencilMode { /// Depth writes are always disabled when `depth_test` is `false`. /// /// [depth writes]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fragops-depth-write + #[builder(default)] pub depth_write: bool, /// Control parameters of the stencil test. + #[builder(default)] pub front: StencilMode, // Note: Using setter(into) so caller does not need our version of OrderedFloat /// Minimum depth bound used in the [depth bounds test]. /// /// [depth bounds test]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fragops-dbt - #[builder(setter(into))] + #[builder(default, setter(into))] pub min: OrderedFloat, // Note: Using setter(into) so caller does not need our version of OrderedFloat /// Maximum depth bound used in the [depth bounds test]. /// /// [depth bounds test]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fragops-dbt - #[builder(setter(into))] + #[builder(default, setter(into))] pub max: OrderedFloat, /// Controls whether [stencil testing] is enabled. /// /// [stencil testing]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fragops-stencil + #[builder(default)] pub stencil_test: bool, } -impl DepthStencilMode { +impl DepthStencilInfo { /// A commonly used depth/stencil mode pub const DEPTH_READ: Self = Self { back: StencilMode::IGNORE, @@ -269,79 +283,46 @@ impl DepthStencilMode { stencil_test: false, }; - /// Specifies a default depth/stencil mode which is equal to [`DepthStencilMode::IGNORE`]. - #[allow(clippy::new_ret_no_self)] - pub fn new() -> DepthStencilModeBuilder { - DepthStencilModeBuilder::default() + /// TODO + pub fn build() -> DepthStencilInfoBuilder { + Default::default() } -} -impl From for vk::PipelineDepthStencilStateCreateInfo<'_> { - fn from(mode: DepthStencilMode) -> Self { - Self::default() - .back(mode.back.into()) - .depth_bounds_test_enable(mode.bounds_test as _) - .depth_compare_op(mode.compare_op) - .depth_test_enable(mode.depth_test as _) - .depth_write_enable(mode.depth_write as _) - .front(mode.front.into()) - .max_depth_bounds(mode.max.into_inner()) - .min_depth_bounds(mode.min.into_inner()) - .stencil_test_enable(mode.stencil_test as _) + /// Converts a `DepthStencilInfo` into a `DepthStencilInfoBuilder`. + pub fn into_builder(self) -> DepthStencilInfoBuilder { + DepthStencilInfoBuilder { + back: Some(self.back), + bounds_test: Some(self.bounds_test), + compare_op: Some(self.compare_op), + depth_test: Some(self.depth_test), + depth_write: Some(self.depth_write), + front: Some(self.front), + max: Some(self.max), + min: Some(self.min), + stencil_test: Some(self.stencil_test), + } } } -// HACK: https://github.com/colin-kiegel/rust-derive-builder/issues/56 -impl DepthStencilModeBuilder { - /// Builds a new `DepthStencilMode`. - pub fn build(mut self) -> DepthStencilMode { - if self.back.is_none() { - self.back = Some(Default::default()); - } - - if self.bounds_test.is_none() { - self.bounds_test = Some(Default::default()); - } - - if self.compare_op.is_none() { - self.compare_op = Some(Default::default()); - } - - if self.depth_test.is_none() { - self.depth_test = Some(Default::default()); - } - - if self.depth_write.is_none() { - self.depth_write = Some(Default::default()); - } - - if self.front.is_none() { - self.front = Some(Default::default()); - } - - if self.min.is_none() { - self.min = Some(Default::default()); - } - - if self.max.is_none() { - self.max = Some(Default::default()); - } - - if self.stencil_test.is_none() { - self.stencil_test = Some(Default::default()); - } - - self.fallible_build() - .expect("All required fields set at initialization") +impl From for vk::PipelineDepthStencilStateCreateInfo<'_> { + fn from(info: DepthStencilInfo) -> Self { + Self::default() + .back(info.back.into()) + .depth_bounds_test_enable(info.bounds_test as _) + .depth_compare_op(info.compare_op) + .depth_test_enable(info.depth_test as _) + .depth_write_enable(info.depth_write as _) + .front(info.front.into()) + .max_depth_bounds(info.max.into_inner()) + .min_depth_bounds(info.min.into_inner()) + .stencil_test_enable(info.stencil_test as _) } } -#[derive(Debug)] -struct DepthStencilModeBuilderError; - -impl From for DepthStencilModeBuilderError { - fn from(_: UninitializedFieldError) -> Self { - Self +impl DepthStencilInfoBuilder { + /// Builds a new `DepthStencilInfo`. + pub fn build(self) -> DepthStencilInfo { + self.fallible_build().unwrap() } } @@ -614,7 +595,6 @@ impl GraphicPipeline { derive(Clone, Copy, Debug), pattern = "owned" )] -#[non_exhaustive] pub struct GraphicPipelineInfo { /// Controls whether a temporary coverage value is generated based on the alpha component of the /// fragment’s first color output. @@ -654,9 +634,9 @@ pub struct GraphicPipelineInfo { /// Specifies color blend state used when rasterization is enabled for any color attachments /// accessed during rendering. /// - /// The default value is [`BlendMode::REPLACE`]. + /// The default value is [`BlendInfo::REPLACE`]. #[builder(default)] - pub blend: BlendMode, + pub blend: BlendInfo, /// Bitmask controlling triangle culling. /// @@ -702,8 +682,7 @@ impl GraphicPipelineInfo { } /// Converts a `GraphicPipelineInfo` into a `GraphicPipelineInfoBuilder`. - #[inline(always)] - pub fn to_builder(self) -> GraphicPipelineInfoBuilder { + pub fn into_builder(self) -> GraphicPipelineInfoBuilder { GraphicPipelineInfoBuilder { alpha_to_coverage: Some(self.alpha_to_coverage), alpha_to_one: Some(self.alpha_to_one), @@ -717,6 +696,12 @@ impl GraphicPipelineInfo { samples: Some(self.samples), } } + + #[deprecated = "use into_builder function"] + #[doc(hidden)] + pub fn to_builder(self) -> GraphicPipelineInfoBuilder { + self.into_builder() + } } impl Default for GraphicPipelineInfo { @@ -725,7 +710,7 @@ impl Default for GraphicPipelineInfo { alpha_to_coverage: false, alpha_to_one: false, bindless_descriptor_count: 8192, - blend: BlendMode::REPLACE, + blend: BlendInfo::REPLACE, cull_mode: vk::CullModeFlags::BACK, front_face: vk::FrontFace::COUNTER_CLOCKWISE, min_sample_shading: None, @@ -879,25 +864,76 @@ pub(crate) struct VertexInputState { pub vertex_attribute_descriptions: Vec, } +mod deprecated { + use crate::driver::graphic::{BlendInfo, BlendInfoBuilder, DepthStencilInfo}; + + impl BlendInfo { + #[allow(clippy::new_ret_no_self)] + #[deprecated = "use builder function or BlendInfoBuilder"] + #[doc(hidden)] + pub fn new() -> BlendInfoBuilder { + Default::default() + } + } + + impl DepthStencilInfo { + #[allow(clippy::new_ret_no_self)] + #[deprecated = "use builder function or DepthStencilInfoBuilder"] + #[doc(hidden)] + pub fn new() -> BlendInfoBuilder { + Default::default() + } + } +} + #[cfg(test)] mod test { use super::*; - type Info = GraphicPipelineInfo; - type Builder = GraphicPipelineInfoBuilder; + #[test] + pub fn blend_info() { + let info = BlendInfo::default(); + let builder = info.into_builder().build(); + + assert_eq!(info, builder); + } + + #[test] + pub fn blend_info_builder() { + let info = BlendInfo::default(); + let builder = BlendInfoBuilder::default().build(); + + assert_eq!(info, builder); + } + + #[test] + pub fn depth_stencil_info() { + let info = DepthStencilInfo::default(); + let builder = info.into_builder().build(); + + assert_eq!(info, builder); + } + + #[test] + pub fn depth_stencil_info_builder() { + let info = DepthStencilInfo::default(); + let builder = DepthStencilInfoBuilder::default().build(); + + assert_eq!(info, builder); + } #[test] pub fn graphic_pipeline_info() { - let info = Info::default(); - let builder = info.to_builder().build(); + let info = GraphicPipelineInfo::default(); + let builder = info.into_builder().build(); assert_eq!(info, builder); } #[test] pub fn graphic_pipeline_info_builder() { - let info = Info::default(); - let builder = Builder::default().build(); + let info = GraphicPipelineInfo::default(); + let builder = GraphicPipelineInfoBuilder::default().build(); assert_eq!(info, builder); } diff --git a/src/driver/image.rs b/src/driver/image.rs index 25001e6b..e86e06c8 100644 --- a/src/driver/image.rs +++ b/src/driver/image.rs @@ -713,7 +713,6 @@ struct ImageAccessRange { derive(Copy, Clone, Debug), pattern = "owned" )] -#[non_exhaustive] pub struct ImageInfo { /// The number of layers in the image. #[builder(default = "1", setter(strip_option))] @@ -877,8 +876,7 @@ impl ImageInfo { } /// Converts an `ImageInfo` into an `ImageInfoBuilder`. - #[inline(always)] - pub fn to_builder(self) -> ImageInfoBuilder { + pub fn into_builder(self) -> ImageInfoBuilder { ImageInfoBuilder { array_layer_count: Some(self.array_layer_count), depth: Some(self.depth), @@ -893,6 +891,12 @@ impl ImageInfo { width: Some(self.width), } } + + #[deprecated = "use into_builder function"] + #[doc(hidden)] + pub fn to_builder(self) -> ImageInfoBuilder { + self.into_builder() + } } impl From for vk::ImageCreateInfo<'_> { @@ -1028,7 +1032,6 @@ impl Drop for ImageView { derive(Clone, Copy, Debug), pattern = "owned" )] -#[non_exhaustive] pub struct ImageViewInfo { /// The number of layers that will be contained in the view. /// @@ -1080,8 +1083,7 @@ impl ImageViewInfo { } /// Converts a `ImageViewInfo` into a `ImageViewInfoBuilder`. - #[inline(always)] - pub fn to_builder(self) -> ImageViewInfoBuilder { + pub fn into_builder(self) -> ImageViewInfoBuilder { ImageViewInfoBuilder { array_layer_count: Some(self.array_layer_count), aspect_mask: Some(self.aspect_mask), @@ -1093,6 +1095,12 @@ impl ImageViewInfo { } } + #[deprecated = "use into_builder function"] + #[doc(hidden)] + pub fn to_builder(self) -> ImageViewInfoBuilder { + self.into_builder() + } + /// Takes this instance and returns it with a newly specified `ImageViewType`. pub fn with_type(mut self, ty: vk::ImageViewType) -> Self { self.ty = ty; diff --git a/src/driver/instance.rs b/src/driver/instance.rs index 1bae80d2..00b1fb4a 100644 --- a/src/driver/instance.rs +++ b/src/driver/instance.rs @@ -451,7 +451,6 @@ impl Deref for Instance { derive(Clone, Debug), pattern = "owned" )] -#[non_exhaustive] pub struct InstanceInfo { /// The Vulkan API version to target #[builder(default = "ApiVersion::Vulkan13")] @@ -480,14 +479,19 @@ pub struct InstanceInfo { impl InstanceInfo { /// Converts a `InstanceInfo` into a `InstanceInfoBuilder`. - #[inline(always)] - pub fn to_builder(self) -> InstanceInfoBuilder { + pub fn into_builder(self) -> InstanceInfoBuilder { InstanceInfoBuilder { api_version: Some(self.api_version), debug: Some(self.debug), extension_names: Some(self.extension_names), } } + + #[deprecated = "use into_builder function"] + #[doc(hidden)] + pub fn to_builder(self) -> InstanceInfoBuilder { + self.into_builder() + } } impl InstanceInfoBuilder { diff --git a/src/driver/mod.rs b/src/driver/mod.rs index bdf0d980..59469d3e 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -67,8 +67,7 @@ pub(crate) use self::{ use { self::{ buffer::{Buffer, BufferInfo}, - graphic::{DepthStencilMode, GraphicPipeline, VertexInputState}, - image::SampleCount, + graphic::VertexInputState, }, ash::vk, gpu_allocator::AllocationError, diff --git a/src/driver/ray_trace.rs b/src/driver/ray_trace.rs index 91163169..dc90fb92 100644 --- a/src/driver/ray_trace.rs +++ b/src/driver/ray_trace.rs @@ -387,7 +387,6 @@ impl RayTracePipeline { derive(Clone, Copy, Debug), pattern = "owned" )] -#[non_exhaustive] pub struct RayTracePipelineInfo { /// The number of descriptors to allocate for a given binding when using bindless (unbounded) /// syntax. @@ -439,13 +438,19 @@ impl RayTracePipelineInfo { } /// Converts a `RayTracePipelineInfo` into a `RayTracePipelineInfoBuilder`. - pub fn to_builder(self) -> RayTracePipelineInfoBuilder { + pub fn into_builder(self) -> RayTracePipelineInfoBuilder { RayTracePipelineInfoBuilder { bindless_descriptor_count: Some(self.bindless_descriptor_count), dynamic_stack_size: Some(self.dynamic_stack_size), max_ray_recursion_depth: Some(self.max_ray_recursion_depth), } } + + #[deprecated = "use into_builder function"] + #[doc(hidden)] + pub fn to_builder(self) -> RayTracePipelineInfoBuilder { + self.into_builder() + } } impl Default for RayTracePipelineInfo { diff --git a/src/driver/render_pass.rs b/src/driver/render_pass.rs index 61a9026e..e7f5c458 100644 --- a/src/driver/render_pass.rs +++ b/src/driver/render_pass.rs @@ -1,7 +1,12 @@ //! Render pass related types. use { - super::{DepthStencilMode, DriverError, GraphicPipeline, SampleCount, device::Device}, + super::{ + DriverError, + device::Device, + graphic::{DepthStencilInfo, GraphicPipeline}, + image::SampleCount, + }, ash::vk, log::{trace, warn}, std::{ @@ -88,7 +93,7 @@ pub(crate) struct FramebufferInfo { #[derive(Debug, Eq, Hash, PartialEq)] struct GraphicPipelineKey { - depth_stencil: Option, + depth_stencil: Option, layout: vk::PipelineLayout, subpass_idx: u32, } @@ -319,7 +324,7 @@ impl RenderPass { pub(crate) fn pipeline_handle( &mut self, pipeline: &GraphicPipeline, - depth_stencil: Option, + depth_stencil: Option, subpass_idx: u32, ) -> Result { let entry = self.graphic_pipelines.entry(GraphicPipelineKey { diff --git a/src/driver/shader.rs b/src/driver/shader.rs index d5dfefb5..4bb02a3e 100644 --- a/src/driver/shader.rs +++ b/src/driver/shader.rs @@ -409,7 +409,6 @@ impl Drop for Sampler { derive(Clone, Copy, Debug), pattern = "owned" )] -#[non_exhaustive] pub struct SamplerInfo { /// Bitmask specifying additional parameters of a sampler. #[builder(default)] @@ -578,8 +577,7 @@ impl SamplerInfo { } /// Converts a `SamplerInfo` into a `SamplerInfoBuilder`. - #[inline(always)] - pub fn to_builder(self) -> SamplerInfoBuilder { + pub fn into_builder(self) -> SamplerInfoBuilder { SamplerInfoBuilder { flags: Some(self.flags), mag_filter: Some(self.mag_filter), @@ -600,6 +598,12 @@ impl SamplerInfo { reduction_mode: Some(self.reduction_mode), } } + + #[deprecated = "use into_builder function"] + #[doc(hidden)] + pub fn to_builder(self) -> SamplerInfoBuilder { + self.into_builder() + } } impl Default for SamplerInfo { diff --git a/src/driver/swapchain.rs b/src/driver/swapchain.rs index dd6f90c7..60c41b50 100644 --- a/src/driver/swapchain.rs +++ b/src/driver/swapchain.rs @@ -493,7 +493,6 @@ impl Deref for SwapchainImage { derive(Clone, Copy, Debug), pattern = "owned" )] -#[non_exhaustive] pub struct SwapchainInfo { /// The initial height of the surface. pub height: u32, @@ -570,8 +569,7 @@ impl SwapchainInfo { } /// Converts a `SwapchainInfo` into a `SwapchainInfoBuilder`. - #[inline(always)] - pub fn to_builder(self) -> SwapchainInfoBuilder { + pub fn into_builder(self) -> SwapchainInfoBuilder { SwapchainInfoBuilder { height: Some(self.height), min_image_count: Some(self.min_image_count), @@ -580,6 +578,12 @@ impl SwapchainInfo { width: Some(self.width), } } + + #[deprecated = "use into_builder function"] + #[doc(hidden)] + pub fn to_builder(self) -> SwapchainInfoBuilder { + self.into_builder() + } } impl From for SwapchainInfo { diff --git a/src/lib.rs b/src/lib.rs index 18662182..f2de61f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -380,7 +380,7 @@ use { cmd_buf::CommandBuffer, compute::ComputePipeline, format_aspect_mask, format_texel_block_extent, format_texel_block_size, - graphic::{DepthStencilMode, GraphicPipeline}, + graphic::{DepthStencilInfo, GraphicPipeline}, image::{ImageInfo, ImageViewInfo, SampleCount}, image_subresource_range_from_layers, ray_trace::RayTracePipeline, @@ -400,14 +400,6 @@ use { type ExecFn = Box) + Send>; type NodeIndex = usize; -#[derive(Clone, Copy, Debug)] -struct Area { - height: u32, - width: u32, - x: i32, - y: i32, -} - #[derive(Clone, Copy, Debug)] struct Attachment { array_layer_count: u32, @@ -509,8 +501,8 @@ struct Execution { bindings: BTreeMap, correlated_view_mask: u32, - depth_stencil: Option, - render_area: Option, + depth_stencil: Option, + render_area: Option, view_mask: u32, color_attachments: HashMap, diff --git a/src/pool/mod.rs b/src/pool/mod.rs index 4e0598aa..80f919e7 100644 --- a/src/pool/mod.rs +++ b/src/pool/mod.rs @@ -263,7 +263,6 @@ lease_builder!(ImageInfo => Image); derive(Clone, Copy, Debug), pattern = "owned" )] -#[non_exhaustive] pub struct PoolInfo { /// The maximum size of a single bucket of acceleration structure resource instances. The /// default value is [`PoolInfo::DEFAULT_RESOURCE_CAPACITY`]. @@ -308,18 +307,18 @@ impl PoolInfo { Default::default() } - /// Constructs a new `PoolInfo` with the given acceleration structure, buffer and image resource - /// capacity for any single bucket. - pub const fn with_capacity(resource_capacity: usize) -> Self { - Self { - accel_struct_capacity: resource_capacity, - buffer_capacity: resource_capacity, - image_capacity: resource_capacity, - } + fn default_cache() -> Cache { + Cache::new(Mutex::new(Vec::with_capacity( + Self::DEFAULT_RESOURCE_CAPACITY, + ))) + } + + fn explicit_cache(capacity: usize) -> Cache { + Cache::new(Mutex::new(Vec::with_capacity(capacity))) } /// Converts a `PoolInfo` into a `PoolInfoBuilder`. - pub fn to_builder(self) -> PoolInfoBuilder { + pub fn into_builder(self) -> PoolInfoBuilder { PoolInfoBuilder { accel_struct_capacity: Some(self.accel_struct_capacity), buffer_capacity: Some(self.buffer_capacity), @@ -327,14 +326,20 @@ impl PoolInfo { } } - fn default_cache() -> Cache { - Cache::new(Mutex::new(Vec::with_capacity( - Self::DEFAULT_RESOURCE_CAPACITY, - ))) + #[deprecated = "use into_builder function"] + #[doc(hidden)] + pub fn to_builder(self) -> PoolInfoBuilder { + self.into_builder() } - fn explicit_cache(capacity: usize) -> Cache { - Cache::new(Mutex::new(Vec::with_capacity(capacity))) + /// Constructs a new `PoolInfo` with the given acceleration structure, buffer and image resource + /// capacity for any single bucket. + pub const fn with_capacity(resource_capacity: usize) -> Self { + Self { + accel_struct_capacity: resource_capacity, + buffer_capacity: resource_capacity, + image_capacity: resource_capacity, + } } } diff --git a/src/queue.rs b/src/queue.rs index 9b81e5ea..c1246e49 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -1,6 +1,6 @@ use { super::{ - Area, Attachment, Command, ExecutionPipeline, Graph, Node, NodeIndex, Resource, + Attachment, Command, ExecutionPipeline, Graph, Node, NodeIndex, Resource, cmd_ref::{Resources, SubresourceAccess, SubresourceRange}, }, crate::{ @@ -14,7 +14,7 @@ use { descriptor_set::{DescriptorPool, DescriptorPoolInfo}, device::Device, format_aspect_mask, - graphic::{DepthStencilMode, GraphicPipeline}, + graphic::{DepthStencilInfo, GraphicPipeline}, image::{Image, ImageAccess}, initial_image_layout_access, is_read_access, is_write_access, pipeline_stage_access_flags, @@ -404,7 +404,7 @@ impl Queue { bindings: &[Resource], pass: &Command, physical_pass: &mut PhysicalPass, - render_area: Area, + render_area: vk::Rect2D, ) -> Result<(), DriverError> { trace!(" begin render pass"); @@ -573,16 +573,7 @@ impl Queue { &vk::RenderPassBeginInfo::default() .render_pass(render_pass.handle) .framebuffer(framebuffer) - .render_area(vk::Rect2D { - offset: vk::Offset2D { - x: render_area.x, - y: render_area.y, - }, - extent: vk::Extent2D { - width: render_area.width, - height: render_area.height, - }, - }) + .render_area(render_area) .clear_values(clear_values) .push_next( &mut vk::RenderPassAttachmentBeginInfoKHR::default() @@ -642,7 +633,7 @@ impl Queue { physical_pass: &mut PhysicalPass, exec_idx: usize, pipeline: &mut ExecutionPipeline, - depth_stencil: Option, + depth_stencil: Option, ) -> Result<(), DriverError> { if log_enabled!(Trace) { let (ty, name, vk_pipeline) = match pipeline { @@ -2401,7 +2392,10 @@ impl Queue { let render_area = if is_graphic { Self::record_image_layout_transitions(cmd_buf, &mut self.graph.resources, pass); - let render_area = Self::render_area(&self.graph.resources, pass); + let render_area = vk::Rect2D { + offset: vk::Offset2D { x: 0, y: 0 }, + extent: Self::render_extent(&self.graph.resources, pass), + }; Self::begin_render_pass( cmd_buf, @@ -2444,10 +2438,10 @@ impl Queue { // In this case we set the viewport and scissor for the user Self::set_viewport( cmd_buf, - render_area.x as _, - render_area.y as _, - render_area.width as _, - render_area.height as _, + render_area.offset.x as _, + render_area.offset.y as _, + render_area.extent.width as _, + render_area.extent.height as _, exec.depth_stencil .map(|depth_stencil| { let min = depth_stencil.min.0; @@ -2458,10 +2452,10 @@ impl Queue { ); Self::set_scissor( cmd_buf, - render_area.x, - render_area.y, - render_area.width, - render_area.height, + render_area.offset.x, + render_area.offset.y, + render_area.extent.width, + render_area.extent.height, ); } @@ -2538,7 +2532,7 @@ impl Queue { } #[profiling::function] - fn render_area(bindings: &[Resource], pass: &Command) -> Area { + fn render_extent(bindings: &[Resource], pass: &Command) -> vk::Extent2D { // set_render_area was not specified so we're going to guess using the minimum common // attachment extents let first_exec = pass.execs.first().unwrap(); @@ -2573,12 +2567,7 @@ impl Queue { height = height.min(attachment_height); } - Area { - height, - width, - x: 0, - y: 0, - } + vk::Extent2D { height, width } } #[profiling::function] From 2c4497128ef9eb9cdfab0928345806249032c49b Mon Sep 17 00:00:00 2001 From: John Wells Date: Fri, 27 Feb 2026 12:16:48 -0500 Subject: [PATCH 27/86] Refactor graphic pipeline command buffer attachment api --- README.md | 6 +- contrib/vk-graph-egui/src/lib.rs | 13 +- contrib/vk-graph-fx/src/bitmap_font.rs | 9 +- contrib/vk-graph-fx/src/image_loader.rs | 2 +- contrib/vk-graph-fx/src/presenter.rs | 8 +- contrib/vk-graph-fx/src/transition.rs | 2 +- contrib/vk-graph-hot/examples/glsl.rs | 2 +- contrib/vk-graph-hot/examples/hlsl.rs | 2 +- contrib/vk-graph-imgui/src/lib.rs | 7 +- contrib/vk-graph-prelude/src/lib.rs | 4 +- contrib/vk-graph-window/src/lib.rs | 2 +- examples/bindless.rs | 15 +- examples/debugger.rs | 2 +- examples/font_bmp.rs | 2 +- examples/fuzzer.rs | 149 ++-- examples/image_sampler.rs | 4 +- examples/min_max.rs | 2 +- examples/mip_compute.rs | 8 +- examples/mip_graphic.rs | 17 +- examples/msaa.rs | 52 +- examples/multipass.rs | 26 +- examples/ray_omni.rs | 27 +- examples/ray_trace.rs | 12 +- examples/rt_triangle.rs | 12 +- examples/shader-toy/src/main.rs | 9 +- examples/skeletal-anim/src/main.rs | 21 +- examples/subgroup_ops.rs | 4 +- examples/triangle.rs | 11 +- examples/vertex_layout.rs | 5 +- examples/vr/src/main.rs | 39 +- examples/vsm_omni.rs | 78 +- src/bind.rs | 33 +- src/cmd_ref/cmd_buf.rs | 80 +- src/cmd_ref/compute.rs | 45 +- src/cmd_ref/graphic.rs | 987 +++++++++++++++++++++--- src/cmd_ref/mod.rs | 161 +--- src/cmd_ref/ray_trace.rs | 35 +- src/driver/accel_struct.rs | 2 +- src/driver/buffer.rs | 2 +- src/driver/compute.rs | 2 +- src/driver/device.rs | 2 +- src/driver/graphic.rs | 52 +- src/driver/image.rs | 16 +- src/driver/ray_trace.rs | 2 +- src/driver/shader.rs | 4 +- src/driver/swapchain.rs | 2 +- src/lib.rs | 227 +++--- src/pool/mod.rs | 2 +- src/queue.rs | 19 +- 49 files changed, 1503 insertions(+), 722 deletions(-) diff --git a/README.md b/README.md index 45c02511..7a086771 100644 --- a/README.md +++ b/README.md @@ -51,9 +51,9 @@ graph .shader_resource_access(0, prev_image, AccessType::FragmentShaderReadColorInputAttachment) .shader_resource_access(1, some_image, AccessType::FragmentShaderReadOther) .shader_resource_access(3, fire_buffer, Access::FragmentShaderReadUniformBuffer) - .clear_color(0, swapchain_image) - .store_color(0, swapchain_image) - .record_cmd_buf(move |cmd_buf, _| { + .color_attachment_image(0, swapchain_image, LoadOp::CLEAR_BLACK_ALPHA_ZERO, StoreOp::Store) + .depth_stencil_attachment_image(depth_image, LoadOp::Load, StoreOp::DontCare) + .record_cmd_buf(move |cmd_buf| { cmd_buf .push_constants(some_u8_slice) .draw(6, 1, 0, 0); diff --git a/contrib/vk-graph-egui/src/lib.rs b/contrib/vk-graph-egui/src/lib.rs index d37060da..2a537a10 100644 --- a/contrib/vk-graph-egui/src/lib.rs +++ b/contrib/vk-graph-egui/src/lib.rs @@ -37,7 +37,7 @@ impl Egui { let ppl = GraphicPipeline::create( device, GraphicPipelineInfoBuilder::default() - .blend(BlendMode { + .blend(BlendInfo { blend_enable: true, src_color_blend_factor: vk::BlendFactor::ONE, dst_color_blend_factor: vk::BlendFactor::ONE_MINUS_SRC_ALPHA, @@ -283,14 +283,9 @@ impl Egui { .bind_pipeline(&self.ppl) .resource_access(idx_buf, AccessType::IndexBuffer) .resource_access(vert_buf, AccessType::VertexBuffer) - .shader_resource_access( - (0, 0), - *texture, - AccessType::FragmentShaderReadOther, - ) - .load_color(0, target) - .store_color(0, target) - .record_cmd_buf(move |cmd_buf, _| { + .shader_resource_access(0, *texture, AccessType::FragmentShaderReadOther) + .color_attachment_image(0, target, LoadOp::Load, StoreOp::Store) + .record_cmd_buf(move |cmd_buf| { cmd_buf .bind_index_buffer(idx_buf, 0, vk::IndexType::UINT32) .bind_vertex_buffer(0, vert_buf, 0) diff --git a/contrib/vk-graph-fx/src/bitmap_font.rs b/contrib/vk-graph-fx/src/bitmap_font.rs index 248c968b..3c4d64fc 100644 --- a/contrib/vk-graph-fx/src/bitmap_font.rs +++ b/contrib/vk-graph-fx/src/bitmap_font.rs @@ -40,7 +40,7 @@ impl BitmapFont { let num_pages = pages.len() as u32; let pipeline = GraphicPipeline::create( device, - GraphicPipelineInfoBuilder::default().blend(BlendMode::ALPHA), + GraphicPipelineInfoBuilder::default().blend(BlendInfo::ALPHA), [ Shader::new_vertex(include_glsl!("res/shader/graphic/font.vert").as_slice()), Shader::new_fragment(include_glsl!("res/shader/graphic/font.frag").as_slice()) @@ -202,9 +202,8 @@ impl BitmapFont { .begin_cmd() .debug_name("text") .bind_pipeline(&self.pipeline) - .resource_access(vertex_buf, AccessType::IndexBuffer) - .load_color(0, image) - .store_color(0, image); + .resource_access(vertex_buf, AccessType::VertexBuffer) + .color_attachment_image(0, image, LoadOp::Load, StoreOp::Store); for (idx, page_node) in page_nodes.iter().copied().enumerate() { let descriptor = (0, [idx as _]); @@ -215,7 +214,7 @@ impl BitmapFont { ); } - pass.record_cmd_buf(move |cmd_buf, _| { + pass.record_cmd_buf(move |cmd_buf| { if let Some((x, y, width, height)) = scissor { cmd_buf.set_scissor( 0, diff --git a/contrib/vk-graph-fx/src/image_loader.rs b/contrib/vk-graph-fx/src/image_loader.rs index 68256df2..6e9098b1 100644 --- a/contrib/vk-graph-fx/src/image_loader.rs +++ b/contrib/vk-graph-fx/src/image_loader.rs @@ -208,7 +208,7 @@ impl ImageLoader { .bind_pipeline(&self.decode_rgb_rgba) .shader_resource_access(0, pixel_buf, AccessType::ComputeShaderReadOther) .shader_resource_access(1, temp_image, AccessType::ComputeShaderWrite) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf .push_constants(0, &(pixel_buf_stride >> 2).to_ne_bytes()) .dispatch(dispatch_x, dispatch_y, 1); diff --git a/contrib/vk-graph-fx/src/presenter.rs b/contrib/vk-graph-fx/src/presenter.rs index 77ef3be1..fa8f378b 100644 --- a/contrib/vk-graph-fx/src/presenter.rs +++ b/contrib/vk-graph-fx/src/presenter.rs @@ -44,7 +44,7 @@ impl ComputePresenter { .bind_pipeline(&self.0[0]) .shader_resource_access(0, image, AccessType::ComputeShaderReadOther) .shader_resource_access(1, swapchain, AccessType::ComputeShaderWrite) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf.dispatch(swapchain_info.width, swapchain_info.height, 1); }); } @@ -72,7 +72,7 @@ impl ComputePresenter { .shader_resource_access((0, [0]), top_image, AccessType::ComputeShaderReadOther) .shader_resource_access((0, [1]), bottom_image, AccessType::ComputeShaderReadOther) .shader_resource_access(1, swapchain, AccessType::ComputeShaderWrite) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf.dispatch(swapchain_info.width, swapchain_info.height, 1); }); } @@ -129,8 +129,8 @@ impl GraphicPresenter { image, AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, ) - .store_color(0, swapchain) - .record_cmd_buf(move |cmd_buf, _| { + .color_attachment_image(0, swapchain, LoadOp::DontCare, StoreOp::Store) + .record_cmd_buf(move |cmd_buf| { // Draw a quad with implicit vertices (no buffer) cmd_buf .push_constants(0, cast_slice(&transform.to_cols_array())) diff --git a/contrib/vk-graph-fx/src/transition.rs b/contrib/vk-graph-fx/src/transition.rs index 225455c3..6e3724b4 100644 --- a/contrib/vk-graph-fx/src/transition.rs +++ b/contrib/vk-graph-fx/src/transition.rs @@ -463,7 +463,7 @@ impl TransitionPipeline { .shader_resource_access(0, a_image, AccessType::ComputeShaderReadOther) .shader_resource_access(1, b_image, AccessType::ComputeShaderReadOther) .shader_resource_access(2, dest_image, AccessType::ComputeShaderWrite) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf.push_constants(0, &push_consts).dispatch( dest_info.width, dest_info.height, diff --git a/contrib/vk-graph-hot/examples/glsl.rs b/contrib/vk-graph-hot/examples/glsl.rs index ef5f5a5a..91acee50 100644 --- a/contrib/vk-graph-hot/examples/glsl.rs +++ b/contrib/vk-graph-hot/examples/glsl.rs @@ -34,7 +34,7 @@ fn main() -> Result<(), WindowError> { .debug_name("make some noise") .bind_pipeline(pipeline.hot()) .write_descriptor(0, frame.swapchain_image) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf.push_constants(&frame_index.to_ne_bytes()).dispatch( frame.width, frame.height, diff --git a/contrib/vk-graph-hot/examples/hlsl.rs b/contrib/vk-graph-hot/examples/hlsl.rs index 1e57bf4f..5961a7bd 100644 --- a/contrib/vk-graph-hot/examples/hlsl.rs +++ b/contrib/vk-graph-hot/examples/hlsl.rs @@ -39,7 +39,7 @@ fn main() -> Result<(), WindowError> { .bind_pipeline(pipeline.hot()) .clear_color(0, frame.swapchain_image) .store_color(0, frame.swapchain_image) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf .push_constants_offset(0, &frame_index.to_ne_bytes()) .push_constants_offset(4, &frame.width.to_ne_bytes()) diff --git a/contrib/vk-graph-imgui/src/lib.rs b/contrib/vk-graph-imgui/src/lib.rs index 12ed43e5..a9685244 100644 --- a/contrib/vk-graph-imgui/src/lib.rs +++ b/contrib/vk-graph-imgui/src/lib.rs @@ -38,7 +38,7 @@ impl ImGui { let pipeline = GraphicPipeline::create( device, GraphicPipelineInfoBuilder::default() - .blend(BlendMode::PRE_MULTIPLIED_ALPHA) + .blend(BlendInfo::PRE_MULTIPLIED_ALPHA) .cull_mode(vk::CullModeFlags::NONE), [ Shader::new_vertex(include_glsl!("res/shader/imgui.vert").as_slice()), @@ -193,9 +193,8 @@ impl ImGui { font_atlas_image, AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, ) - .clear_color(0, image) - .store_color(0, image) - .record_cmd_buf(move |cmd_buf, _| { + .color_attachment_image(0, image, LoadOp::Load, StoreOp::Store) + .record_cmd_buf(move |cmd_buf| { cmd_buf .push_constants(0, &window_width.to_ne_bytes()) .push_constants(4, &window_height.to_ne_bytes()) diff --git a/contrib/vk-graph-prelude/src/lib.rs b/contrib/vk-graph-prelude/src/lib.rs index 3d505ad9..e146c09c 100644 --- a/contrib/vk-graph-prelude/src/lib.rs +++ b/contrib/vk-graph-prelude/src/lib.rs @@ -5,8 +5,8 @@ pub use vk_graph::{ BindGraph, Bound, ClearColorValue, Graph, cmd_ref::{ - BuildAccelerationStructureIndirectInfo, BuildAccelerationStructureInfo, CommandRef, - PipelineCommandRef, UpdateAccelerationStructureIndirectInfo, + BuildAccelerationStructureIndirectInfo, BuildAccelerationStructureInfo, CommandRef, LoadOp, + PipelineCommandRef, StoreOp, UpdateAccelerationStructureIndirectInfo, UpdateAccelerationStructureInfo, }, driver::{ diff --git a/contrib/vk-graph-window/src/lib.rs b/contrib/vk-graph-window/src/lib.rs index 8ce56cf5..cf2360c2 100644 --- a/contrib/vk-graph-window/src/lib.rs +++ b/contrib/vk-graph-window/src/lib.rs @@ -94,7 +94,7 @@ impl Window { let mut swapchain_info = SwapchainInfo::new(window_size.width, window_size.height, surface_format) - .to_builder() + .into_builder() .command_buffer_count(self.data.cmd_buf_count); if let Some(min_image_count) = self.data.min_image_count { diff --git a/examples/bindless.rs b/examples/bindless.rs index aae9e40e..126c951e 100644 --- a/examples/bindless.rs +++ b/examples/bindless.rs @@ -4,6 +4,7 @@ use { bytemuck::{Pod, Zeroable, cast_slice}, clap::Parser, std::sync::Arc, + vk_graph::cmd_ref::LoadOp, vk_graph_prelude::*, vk_graph_window::{WindowBuilder, WindowError}, vk_shader_macros::glsl, @@ -42,11 +43,15 @@ fn main() -> Result<(), WindowError> { ); } - cmd.clear_color(0, frame.swapchain_image) - .store_color(0, frame.swapchain_image) - .record_cmd_buf(move |cmd_buf, _| { - cmd_buf.draw_indirect(draw_buf_node, 0, 64, 16); - }); + cmd.color_attachment_image( + 0, + frame.swapchain_image, + LoadOp::CLEAR_BLACK_ALPHA_ZERO, + StoreOp::Store, + ) + .record_cmd_buf(move |cmd_buf| { + cmd_buf.draw_indirect(draw_buf_node, 0, 64, 16); + }); }) } diff --git a/examples/debugger.rs b/examples/debugger.rs index 84813624..82bb98f5 100644 --- a/examples/debugger.rs +++ b/examples/debugger.rs @@ -180,7 +180,7 @@ fn main() -> Result<(), vk_graph_window::WindowError> { .debug_name("This doesn't look good...") .bind_pipeline(&compute_pipeline) .shader_resource_access(42, image, AccessType::ComputeShaderWrite) - .record_cmd_buf(|cmd_buf, _| { + .record_cmd_buf(|cmd_buf| { cmd_buf.dispatch(1024, 1024, 1); }); diff --git a/examples/font_bmp.rs b/examples/font_bmp.rs index a45302fd..d3061eb0 100644 --- a/examples/font_bmp.rs +++ b/examples/font_bmp.rs @@ -134,7 +134,7 @@ fn main() -> anyhow::Result<()> { .debug_name("smoke") .bind_pipeline(&smoke_pipeline) .shader_resource_access(0, image_node, AccessType::ComputeShaderWrite) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf .push_constants(0, &elapsed_time.as_secs_f32().to_ne_bytes()) .dispatch(frame.width.div_ceil(subgroup_size), frame.height, 1); diff --git a/examples/fuzzer.rs b/examples/fuzzer.rs index 0e72923f..efb74a09 100644 --- a/examples/fuzzer.rs +++ b/examples/fuzzer.rs @@ -26,7 +26,7 @@ use { log::debug, rand::{Rng, rng, seq::IndexedRandom}, std::mem::size_of, - vk_graph::cmd_ref::BuildAccelerationStructureInfo, + vk_graph::cmd_ref::{BuildAccelerationStructureInfo, LoadOp}, vk_graph_prelude::*, vk_graph_window::{FrameContext, WindowBuilder, WindowError}, vk_shader_macros::glsl, @@ -225,7 +225,7 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS | vk::BufferUsageFlags::STORAGE_BUFFER, ) - .to_builder() + .into_builder() .alignment(accel_struct_scratch_offset_alignment), ) .unwrap(), @@ -258,7 +258,7 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { tlas_size.build_size, vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS | vk::BufferUsageFlags::STORAGE_BUFFER, ) - .to_builder() + .into_builder() .alignment(accel_struct_scratch_offset_alignment), ) .unwrap(), @@ -286,9 +286,11 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { .map(|(blas_node, _, _)| *blas_node) .collect::>(); - let mut cmd = cmd.record_cmd_buf(move |cmd_buf, nodes| { + let mut cmd = cmd.record_cmd_buf(move |cmd_buf| { for (blas_node, scratch_buf, build_data) in blas_nodes { - let scratch_addr = nodes[scratch_buf].device_address(); + let scratch_buf = cmd_buf.resource(scratch_buf); + let scratch_addr = scratch_buf.device_address(); + cmd_buf.build_accel_struct(&[BuildAccelerationStructureInfo::new( blas_node, scratch_addr, @@ -307,8 +309,10 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { AccessType::AccelerationStructureBufferWrite, ) .resource_access(tlas_node, AccessType::AccelerationStructureBuildWrite) - .record_cmd_buf(move |cmd_buf, nodes| { - let scratch_addr = nodes[tlas_scratch_buf].device_address(); + .record_cmd_buf(move |cmd_buf| { + let scratch_buf = cmd_buf.resource(tlas_scratch_buf); + let scratch_addr = scratch_buf.device_address(); + cmd_buf.build_accel_struct(&[BuildAccelerationStructureInfo::new( tlas_node, scratch_addr, @@ -376,7 +380,7 @@ fn record_pipeline_array_bind(frame: &mut FrameContext, pool: &mut HashPool) { .shader_resource_access((0, [2]), images[2], AccessType::ComputeShaderReadOther) .shader_resource_access((0, [3]), images[3], AccessType::ComputeShaderReadOther) .shader_resource_access((0, [4]), images[4], AccessType::ComputeShaderReadOther) - .record_cmd_buf(|cmd_buf, _| { + .record_cmd_buf(|cmd_buf| { cmd_buf .push_constants(0, &0f32.to_ne_bytes()) .dispatch(64, 64, 1); @@ -442,7 +446,7 @@ fn record_pipeline_bindless(frame: &mut FrameContext, pool: &mut HashPool) { .shader_resource_access((0, [2]), images[2], AccessType::ComputeShaderWrite) .shader_resource_access((0, [3]), images[3], AccessType::ComputeShaderWrite) .shader_resource_access((0, [4]), images[4], AccessType::ComputeShaderWrite) - .record_cmd_buf(|cmd_buf, _| { + .record_cmd_buf(|cmd_buf| { cmd_buf .push_constants(0, &5u32.to_ne_bytes()) .dispatch(64, 64, 1); @@ -472,7 +476,7 @@ fn record_pipeline_no_op(frame: &mut FrameContext, _: &mut HashPool) { .begin_cmd() .debug_name("no-op") .bind_pipeline(&pipeline) - .record_cmd_buf(|cmd_buf, _| { + .record_cmd_buf(|cmd_buf| { cmd_buf.dispatch(1, 1, 1); }); } @@ -578,9 +582,8 @@ fn record_graphic_bindless(frame: &mut FrameContext, pool: &mut HashPool) { images[4], AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, ) - .clear_color(0, image) - .store_color(0, image) - .record_cmd_buf(|cmd_buf, _| { + .color_attachment_image(0, image, LoadOp::CLEAR_BLACK_ALPHA_ZERO, StoreOp::Store) + .record_cmd_buf(|cmd_buf| { cmd_buf .push_constants(0, &5u32.to_ne_bytes()) .draw(1, 1, 0, 0); @@ -621,9 +624,8 @@ fn record_graphic_load_store(frame: &mut FrameContext, _: &mut HashPool) { .begin_cmd() .debug_name("load-store") .bind_pipeline(&pipeline) - .load_color(0, frame.swapchain_image) - .store_color(0, frame.swapchain_image) - .record_cmd_buf(|cmd_buf, _| { + .color_attachment_image(0, frame.swapchain_image, LoadOp::Load, StoreOp::Store) + .record_cmd_buf(|cmd_buf| { cmd_buf.draw(1, 1, 0, 0); }); } @@ -739,7 +741,7 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo swapchain_format, vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::TRANSIENT_ATTACHMENT, ) - .to_builder() + .into_builder() .sample_count(sample_count), ) .unwrap(), @@ -753,7 +755,7 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT | vk::ImageUsageFlags::TRANSIENT_ATTACHMENT, ) - .to_builder() + .into_builder() .sample_count(sample_count), ) .unwrap(), @@ -768,7 +770,7 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo .unwrap(), ); - let depth_stencil_mode = DepthStencilInfo { + let depth_stencil_info = DepthStencilInfo { back: StencilMode::IGNORE, bounds_test: true, compare_op: vk::CompareOp::LESS_OR_EQUAL, @@ -793,17 +795,26 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo .begin_cmd() .debug_name("msaa-depth-stencil") .bind_pipeline(&pipeline) - .depth_stencil(depth_stencil_mode) - .clear_color(0, msaa_color_image) - .clear_depth_stencil(msaa_depth_stencil_image) - .resolve_color(0, 1, frame.swapchain_image) - .resolve_depth_stencil( + .depth_stencil(depth_stencil_info) + .color_attachment_image( + 0, + msaa_color_image, + LoadOp::CLEAR_BLACK_ALPHA_ZERO, + StoreOp::DontCare, + ) + .color_attachment_image_resolve(1, frame.swapchain_image, 0) + .depth_stencil_attachment_image( + msaa_depth_stencil_image, + LoadOp::CLEAR_ZERO_STENCIL_ZERO, + StoreOp::DontCare, + ) + .depth_stencil_attachment_image_resolve( 2, depth_stencil_image, Some(depth_resolve_mode), Some(ResolveMode::SampleZero), ) - .record_cmd_buf(|cmd_buf, _| { + .record_cmd_buf(|cmd_buf| { cmd_buf.draw(3, 1, 0, 0); }); } @@ -850,8 +861,8 @@ fn record_graphic_will_merge_common_color1(frame: &mut FrameContext, pool: &mut ) .as_slice(), )) - .store_color(0, image) - .record_cmd_buf(|cmd_buf, _| { + .color_attachment_image(0, image, LoadOp::DontCare, StoreOp::Store) + .record_cmd_buf(|cmd_buf| { cmd_buf.draw(1, 1, 0, 0); }); frame @@ -884,9 +895,8 @@ fn record_graphic_will_merge_common_color1(frame: &mut FrameContext, pool: &mut ) .as_slice(), )) - .load_color(0, image) - .store_color(0, image) - .record_cmd_buf(|cmd_buf, _| { + .color_attachment_image(0, image, LoadOp::Load, StoreOp::Store) + .record_cmd_buf(|cmd_buf| { cmd_buf.draw(1, 1, 0, 0); }); } @@ -941,8 +951,8 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut ) .as_slice(), )) - .store_color(0, image_0) - .record_cmd_buf(|cmd_buf, _| { + .color_attachment_image(0, image_0, LoadOp::DontCare, StoreOp::Store) + .record_cmd_buf(|cmd_buf| { cmd_buf.draw(1, 1, 0, 0); }); frame @@ -977,10 +987,9 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut ) .as_slice(), )) - .load_color(0, image_0) - .store_color(0, image_0) - .store_color(1, image_1) - .record_cmd_buf(|cmd_buf, _| { + .color_attachment_image(0, image_0, LoadOp::Load, StoreOp::Store) + .color_attachment_image(1, image_1, LoadOp::DontCare, StoreOp::Store) + .record_cmd_buf(|cmd_buf| { cmd_buf.draw(1, 1, 0, 0); }); frame @@ -1013,9 +1022,8 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut ) .as_slice(), )) - .clear_color(0, image_0) - .store_color(0, image_0) - .record_cmd_buf(|cmd_buf, _| { + .color_attachment_image(0, image_0, LoadOp::CLEAR_BLACK_ALPHA_ZERO, StoreOp::Store) + .record_cmd_buf(|cmd_buf| { cmd_buf.draw(1, 1, 0, 0); }); } @@ -1071,9 +1079,9 @@ fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut ) .as_slice(), )) - .store_color(0, color_image) - .store_depth_stencil(depth_image) - .record_cmd_buf(|cmd_buf, _| { + .color_attachment_image(0, color_image, LoadOp::DontCare, StoreOp::Store) + .depth_stencil_attachment_image(depth_image, LoadOp::DontCare, StoreOp::Store) + .record_cmd_buf(|cmd_buf| { cmd_buf.draw(1, 1, 0, 0); }); frame @@ -1104,9 +1112,8 @@ fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut ) .as_slice(), )) - .load_depth_stencil(depth_image) - .store_depth_stencil(depth_image) - .record_cmd_buf(|cmd_buf, _| { + .depth_stencil_attachment_image(depth_image, LoadOp::Load, StoreOp::Store) + .record_cmd_buf(|cmd_buf| { cmd_buf.draw(1, 1, 0, 0); }); } @@ -1160,8 +1167,8 @@ fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut ) .as_slice(), )) - .store_depth_stencil(depth_image) - .record_cmd_buf(|cmd_buf, _| { + .depth_stencil_attachment_image(depth_image, LoadOp::DontCare, StoreOp::Store) + .record_cmd_buf(|cmd_buf| { cmd_buf.draw(1, 1, 0, 0); }); frame @@ -1194,10 +1201,9 @@ fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut ) .as_slice(), )) - .store_color(0, color_image) - .load_depth_stencil(depth_image) - .store_depth_stencil(depth_image) - .record_cmd_buf(|cmd_buf, _| { + .color_attachment_image(0, color_image, LoadOp::DontCare, StoreOp::Store) + .depth_stencil_attachment_image(depth_image, LoadOp::Load, StoreOp::Store) + .record_cmd_buf(|cmd_buf| { cmd_buf.draw(1, 1, 0, 0); }); } @@ -1241,8 +1247,8 @@ fn record_graphic_will_merge_common_depth3(frame: &mut FrameContext, pool: &mut ) .as_slice(), )) - .store_depth_stencil(depth_image) - .record_cmd_buf(|cmd_buf, _| { + .depth_stencil_attachment_image(depth_image, LoadOp::DontCare, StoreOp::Store) + .record_cmd_buf(|cmd_buf| { cmd_buf.draw(1, 1, 0, 0); }); frame @@ -1273,9 +1279,8 @@ fn record_graphic_will_merge_common_depth3(frame: &mut FrameContext, pool: &mut ) .as_slice(), )) - .load_depth_stencil(depth_image) - .store_depth_stencil(depth_image) - .record_cmd_buf(|cmd_buf, _| { + .depth_stencil_attachment_image(depth_image, LoadOp::Load, StoreOp::Store) + .record_cmd_buf(|cmd_buf| { cmd_buf.draw(1, 1, 0, 0); }); } @@ -1347,9 +1352,8 @@ fn record_graphic_will_merge_subpass_input(frame: &mut FrameContext, pool: &mut .begin_cmd() .debug_name("a") .bind_pipeline(&pipeline_a) - .clear_color(0, image) - .store_color(0, image) - .record_cmd_buf(|cmd_buf, _| { + .color_attachment_image(0, image, LoadOp::CLEAR_BLACK_ALPHA_ZERO, StoreOp::Store) + .record_cmd_buf(|cmd_buf| { cmd_buf.draw(1, 1, 0, 0); }); frame @@ -1357,8 +1361,8 @@ fn record_graphic_will_merge_subpass_input(frame: &mut FrameContext, pool: &mut .begin_cmd() .debug_name("b") .bind_pipeline(&pipeline_b) - .store_color(0, image) - .record_cmd_buf(|cmd_buf, _| { + .color_attachment_image(0, image, LoadOp::DontCare, StoreOp::Store) + .record_cmd_buf(|cmd_buf| { cmd_buf.draw(1, 1, 0, 0); }); } @@ -1407,8 +1411,8 @@ fn record_graphic_wont_merge(frame: &mut FrameContext, pool: &mut HashPool) { .begin_cmd() .debug_name("c") .bind_pipeline(&pipeline) - .store_color(0, image) - .record_cmd_buf(|cmd_buf, _| { + .color_attachment_image(0, image, LoadOp::DontCare, StoreOp::Store) + .record_cmd_buf(|cmd_buf| { cmd_buf.draw(1, 1, 0, 0); }); frame @@ -1416,8 +1420,8 @@ fn record_graphic_wont_merge(frame: &mut FrameContext, pool: &mut HashPool) { .begin_cmd() .debug_name("d") .bind_pipeline(&pipeline) - .store_color(0, image) - .record_cmd_buf(|cmd_buf, _| { + .color_attachment_image(0, image, LoadOp::DontCare, StoreOp::Store) + .record_cmd_buf(|cmd_buf| { cmd_buf.draw(1, 1, 0, 0); }); } @@ -1483,14 +1487,18 @@ fn record_transfer_graphic_multipass(frame: &mut FrameContext, pool: &mut HashPo .begin_cmd() .debug_name("a") .bind_pipeline(&pipeline) - .clear_color(0, frame.swapchain_image) - .store_color(0, frame.swapchain_image) + .color_attachment_image( + 0, + frame.swapchain_image, + LoadOp::CLEAR_BLACK_ALPHA_ZERO, + StoreOp::Store, + ) .shader_resource_access( 0, images[0], AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, ) - .record_cmd_buf(|cmd_buf, _| { + .record_cmd_buf(|cmd_buf| { cmd_buf.draw(1, 1, 0, 0); }); frame @@ -1498,14 +1506,13 @@ fn record_transfer_graphic_multipass(frame: &mut FrameContext, pool: &mut HashPo .begin_cmd() .debug_name("b") .bind_pipeline(&pipeline) - .load_color(0, frame.swapchain_image) - .store_color(0, frame.swapchain_image) + .color_attachment_image(0, frame.swapchain_image, LoadOp::Load, StoreOp::Store) .shader_resource_access( 0, images[1], AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, ) - .record_cmd_buf(|cmd_buf, _| { + .record_cmd_buf(|cmd_buf| { cmd_buf.draw(1, 1, 0, 0); }); } diff --git a/examples/image_sampler.rs b/examples/image_sampler.rs index 015d0508..21aa987c 100644 --- a/examples/image_sampler.rs +++ b/examples/image_sampler.rs @@ -76,8 +76,8 @@ fn main() -> anyhow::Result<()> { gulf_image, AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, ) - .store_color(0, frame.swapchain_image) - .record_cmd_buf(|cmd_buf, _| { + .color_attachment_image(0, frame.swapchain_image, LoadOp::DontCare, StoreOp::Store) + .record_cmd_buf(|cmd_buf| { cmd_buf.draw(3, 1, 0, 0); }); })?; diff --git a/examples/min_max.rs b/examples/min_max.rs index 0021a024..7a435a61 100644 --- a/examples/min_max.rs +++ b/examples/min_max.rs @@ -206,7 +206,7 @@ fn reduce_depth_image( )?) .shader_resource_access(0, depth_image, AccessType::ComputeShaderReadOther) .shader_resource_access(1, reduced_image, AccessType::ComputeShaderWrite) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf.dispatch(reduced_info.width, reduced_info.height, 1); }); diff --git a/examples/mip_compute.rs b/examples/mip_compute.rs index 77c303f3..b6107520 100644 --- a/examples/mip_compute.rs +++ b/examples/mip_compute.rs @@ -29,7 +29,7 @@ fn main() -> Result<(), DriverError> { | vk::ImageUsageFlags::TRANSFER_SRC | vk::ImageUsageFlags::TRANSFER_DST, ) - .to_builder() + .into_builder() .mip_level_count(3), )?); let depth_info = graph.resource(depth_pyramid).info; @@ -90,7 +90,7 @@ fn main() -> Result<(), DriverError> { depth_pyramid, depth_info .into_image_view() - .to_builder() + .into_builder() .base_mip_level(mip_level - 1) .mip_level_count(1), AccessType::ComputeShaderReadOther, @@ -100,12 +100,12 @@ fn main() -> Result<(), DriverError> { depth_pyramid, depth_info .into_image_view() - .to_builder() + .into_builder() .base_mip_level(mip_level) .mip_level_count(1), AccessType::ComputeShaderWrite, ) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf.dispatch( depth_info.width >> mip_level, depth_info.height >> mip_level, diff --git a/examples/mip_graphic.rs b/examples/mip_graphic.rs index f094fcff..93bdbb39 100644 --- a/examples/mip_graphic.rs +++ b/examples/mip_graphic.rs @@ -31,7 +31,7 @@ fn main() -> Result<(), WindowError> { vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::SAMPLED, ) - .to_builder() + .into_builder() .mip_level_count(mip_level_count) .build(); let image = Arc::new(Image::create(&window.device, image_info)?); @@ -71,13 +71,12 @@ fn main() -> Result<(), WindowError> { image, image_info .into_image_view() - .to_builder() + .into_builder() .base_mip_level(mip_level) .mip_level_count(1), AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, ) - .load_color(0, frame.swapchain_image) - .store_color(0, frame.swapchain_image) + .color_attachment_image(0, frame.swapchain_image, LoadOp::Load, StoreOp::Store) .render_area(vk::Rect2D { offset: vk::Offset2D { x: stripe_x as _, @@ -88,7 +87,7 @@ fn main() -> Result<(), WindowError> { height: swapchain_info.height, }, }) - .record_cmd_buf(|cmd_buf, _| { + .record_cmd_buf(|cmd_buf| { cmd_buf.draw(6, 1, 0, 0); }); } @@ -169,16 +168,18 @@ fn fill_mip_levels(device: &Device, image: &Arc) -> Result<(), DriverErro .begin_cmd() .debug_name("fill mip levels") .bind_pipeline(&vertical_gradient) - .store_color_as( + .color_attachment_image_view( 0, image, image_info .into_image_view() - .to_builder() + .into_builder() .base_mip_level(mip_level) .mip_level_count(1), + LoadOp::DontCare, + StoreOp::Store, ) - .record_cmd_buf(|cmd_buf, _| { + .record_cmd_buf(|cmd_buf| { cmd_buf .push_constants( 0, diff --git a/examples/msaa.rs b/examples/msaa.rs index f5bb01ea..e1fe526a 100644 --- a/examples/msaa.rs +++ b/examples/msaa.rs @@ -6,6 +6,7 @@ use { glam::{Mat4, Vec3}, log::warn, std::{mem::size_of, sync::Arc}, + vk_graph::cmd_ref::LoadOp, vk_graph_prelude::*, vk_graph_window::WindowBuilder, vk_shader_macros::glsl, @@ -15,8 +16,6 @@ use { type CubeVertex = [[f32; 3]; 3]; -const WHITE: ClearColorValue = ClearColorValue([1.0, 1.0, 1.0, 1.0]); - /// Draws a spinning cube with high-contrast edges; hold any key to display the cube in non-MSAA /// mode. /// @@ -89,7 +88,7 @@ fn main() -> anyhow::Result<()> { let cube_vertex_buf = frame.graph.bind_resource(&cube_mesh.vertex_buf); let scene_uniform_buf = frame.graph.bind_resource(scene_uniform_buf); - let mut pass = frame + let mut cmd = frame .graph .begin_cmd() .debug_name("cube") @@ -98,26 +97,26 @@ fn main() -> anyhow::Result<()> { } else { &mesh_noaa_pipeline }) - .depth_stencil(DepthStencilInfo::DEPTH_WRITE) + .depth_stencil(DepthStencilInfo::DEPTH_WRITE_LESS_IGNORE_STENCIL) .resource_access(cube_vertex_buf, AccessType::VertexBuffer) .shader_resource_access(0, scene_uniform_buf, AccessType::AnyShaderReadUniformBuffer); if will_render_msaa { - let msaa_color_image = pass.bind_resource( + let msaa_color_image = cmd.bind_resource( pool.lease( ImageInfo::image_2d( frame.width, frame.height, - pass.resource(frame.swapchain_image).info.fmt, + cmd.resource(frame.swapchain_image).info.fmt, vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::TRANSIENT_ATTACHMENT, ) - .to_builder() + .into_builder() .sample_count(sample_count), ) .unwrap(), ); - let msaa_depth_image = pass.bind_resource( + let msaa_depth_image = cmd.bind_resource( pool.lease( ImageInfo::image_2d( frame.width, @@ -126,19 +125,27 @@ fn main() -> anyhow::Result<()> { vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT | vk::ImageUsageFlags::TRANSIENT_ATTACHMENT, ) - .to_builder() + .into_builder() .sample_count(sample_count), ) .unwrap(), ); // Attachments for multisample mode - pass = pass - .clear_color_value(0, msaa_color_image, WHITE) - .clear_depth_stencil(msaa_depth_image) - .resolve_color(0, 1, frame.swapchain_image); + cmd.set_color_attachment_image( + 0, + msaa_color_image, + LoadOp::CLEAR_WHITE_ALPHA_ONE, + StoreOp::DontCare, + ) + .set_color_attachment_image_resolve(1, frame.swapchain_image, 0) + .set_depth_stencil_attachment_image( + msaa_depth_image, + LoadOp::CLEAR_ZERO_STENCIL_ZERO, + StoreOp::DontCare, + ); } else { - let noaa_depth_image = pass.bind_resource( + let noaa_depth_image = cmd.bind_resource( pool.lease(ImageInfo::image_2d( frame.width, frame.height, @@ -150,13 +157,20 @@ fn main() -> anyhow::Result<()> { ); // Attachments for non-multisample mode - pass = pass - .clear_color_value(0, frame.swapchain_image, WHITE) - .clear_depth_stencil(noaa_depth_image) - .store_color(0, frame.swapchain_image); + cmd.set_color_attachment_image( + 0, + frame.swapchain_image, + LoadOp::CLEAR_WHITE_ALPHA_ONE, + StoreOp::Store, + ) + .set_depth_stencil_attachment_image( + noaa_depth_image, + LoadOp::CLEAR_ZERO_STENCIL_ZERO, + StoreOp::DontCare, + ); } - pass.record_cmd_buf(move |cmd_buf, _| { + cmd.record_cmd_buf(move |cmd_buf| { cmd_buf .bind_vertex_buffer(0, cube_vertex_buf, 0) .push_constants(0, bytes_of(&world_transform)) diff --git a/examples/multipass.rs b/examples/multipass.rs index a42d3c9e..54afe500 100644 --- a/examples/multipass.rs +++ b/examples/multipass.rs @@ -5,6 +5,7 @@ use { clap::Parser, glam::{Mat4, Vec3, Vec4, vec3}, std::sync::Arc, + vk_graph::{cmd_ref::LoadOp, driver::graphic::DepthStencilInfo}, vk_graph_prelude::*, vk_graph_window::WindowBuilder, vk_shader_macros::glsl, @@ -83,7 +84,7 @@ fn main() -> anyhow::Result<()> { let light_buf = bind_light_buf(frame.graph, &mut pool); let push_const_data = write_push_consts(obj_pos, material); - let mut write = DepthStencilInfo::DEPTH_WRITE; + let mut write = DepthStencilInfo::DEPTH_WRITE_LESS_IGNORE_STENCIL; // Depth Prepass frame @@ -99,9 +100,12 @@ fn main() -> anyhow::Result<()> { ) .resource_access(index_buf, AccessType::IndexBuffer) .resource_access(vertex_buf, AccessType::VertexBuffer) - .clear_depth_stencil(depth_stencil) - .store_depth_stencil(depth_stencil) - .record_cmd_buf(move |cmd_buf, _| { + .depth_stencil_attachment_image( + depth_stencil, + LoadOp::CLEAR_ZERO_STENCIL_ZERO, + StoreOp::Store, + ) + .record_cmd_buf(move |cmd_buf| { cmd_buf .bind_index_buffer(index_buf, 0, vk::IndexType::UINT16) .bind_vertex_buffer(0, vertex_buf, 0) @@ -139,10 +143,9 @@ fn main() -> anyhow::Result<()> { ) .resource_access(index_buf, AccessType::IndexBuffer) .resource_access(vertex_buf, AccessType::VertexBuffer) - .load_depth_stencil(depth_stencil) - .store_depth_stencil(depth_stencil) - .store_color(0, frame.swapchain_image) - .record_cmd_buf(move |cmd_buf, _| { + .depth_stencil_attachment_image(depth_stencil, LoadOp::Load, StoreOp::Store) + .color_attachment_image(0, frame.swapchain_image, LoadOp::DontCare, StoreOp::Store) + .record_cmd_buf(move |cmd_buf| { cmd_buf .bind_index_buffer(index_buf, 0, vk::IndexType::UINT16) .bind_vertex_buffer(0, vertex_buf, 0) @@ -164,10 +167,9 @@ fn main() -> anyhow::Result<()> { .debug_name("fill background") .bind_pipeline(&fill_background) .depth_stencil(read) - .load_depth_stencil(depth_stencil) - .load_color(0, frame.swapchain_image) - .store_color(0, frame.swapchain_image) - .record_cmd_buf(move |cmd_buf, _| { + .depth_stencil_attachment_image(depth_stencil, LoadOp::Load, StoreOp::DontCare) + .color_attachment_image(0, frame.swapchain_image, LoadOp::Load, StoreOp::Store) + .record_cmd_buf(move |cmd_buf| { cmd_buf.draw(6, 1, 0, 0); }); })?; diff --git a/examples/ray_omni.rs b/examples/ray_omni.rs index 9adb8bb5..6410e061 100644 --- a/examples/ray_omni.rs +++ b/examples/ray_omni.rs @@ -14,6 +14,7 @@ use { sync::Arc, }, tobj::{GPU_LOAD_OPTIONS, load_obj}, + vk_graph::cmd_ref::LoadOp, vk_graph_prelude::*, vk_graph_window::WindowBuilder, vk_shader_macros::glsl, @@ -102,11 +103,19 @@ fn main() -> anyhow::Result<()> { scene_tlas, AccessType::RayTracingShaderReadAccelerationStructure, ) - .depth_stencil(DepthStencilInfo::DEPTH_WRITE) - .clear_depth_stencil(depth_image) - .clear_color_value(0, frame.swapchain_image, [0xff, 0xff, 0xff, 0xff]) - .store_color(0, frame.swapchain_image) - .record_cmd_buf(move |cmd_buf, _| { + .depth_stencil(DepthStencilInfo::DEPTH_WRITE_LESS_IGNORE_STENCIL) + .depth_stencil_attachment_image( + depth_image, + LoadOp::CLEAR_ZERO_STENCIL_ZERO, + StoreOp::DontCare, + ) + .color_attachment_image( + 0, + frame.swapchain_image, + LoadOp::CLEAR_WHITE_ALPHA_ONE, + StoreOp::Store, + ) + .record_cmd_buf(move |cmd_buf| { cmd_buf .bind_index_buffer(model_mesh_index_buf, 0, vk::IndexType::UINT32) .bind_vertex_buffer(0, model_mesh_vertex_buf, 0) @@ -195,7 +204,7 @@ fn create_blas( size.build_size, vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS | vk::BufferUsageFlags::STORAGE_BUFFER, ) - .to_builder() + .into_builder() .alignment(accel_struct_scratch_offset_alignment), )?); let scratch_addr = graph.resource(scratch_buf).device_address(); @@ -212,7 +221,7 @@ fn create_blas( pass.resource_access(blas, AccessType::AccelerationStructureBuildWrite) .resource_access(scratch_buf, AccessType::AccelerationStructureBufferWrite) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf.build_accel_struct(&[BuildAccelerationStructureInfo::new( blas, scratch_addr, @@ -377,7 +386,7 @@ fn create_tlas( size.build_size, vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS | vk::BufferUsageFlags::STORAGE_BUFFER, ) - .to_builder() + .into_builder() .alignment(accel_struct_scratch_offset_alignment), )?, ); @@ -392,7 +401,7 @@ fn create_tlas( .resource_access(instance_buf, AccessType::AccelerationStructureBuildRead) .resource_access(scratch_buf, AccessType::AccelerationStructureBufferWrite) .resource_access(tlas, AccessType::AccelerationStructureBuildWrite) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf.build_accel_struct(&[BuildAccelerationStructureInfo::new( tlas, scratch_addr, diff --git a/examples/ray_trace.rs b/examples/ray_trace.rs index a74d3f01..bb01c412 100644 --- a/examples/ray_trace.rs +++ b/examples/ray_trace.rs @@ -527,7 +527,7 @@ fn main() -> anyhow::Result<()> { vk::BufferUsageFlags::SHADER_BINDING_TABLE_KHR | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS, ) - .to_builder() + .into_builder() .alignment(shader_group_base_alignment as _), ) .unwrap(); @@ -681,7 +681,7 @@ fn main() -> anyhow::Result<()> { vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS | vk::BufferUsageFlags::STORAGE_BUFFER, ) - .to_builder() + .into_builder() .alignment(accel_struct_scratch_offset_alignment), )?); let scratch_data = graph.resource(scratch_buf).device_address(); @@ -693,7 +693,7 @@ fn main() -> anyhow::Result<()> { .resource_access(vertex_node, AccessType::AccelerationStructureBuildRead) .resource_access(scratch_buf, AccessType::AccelerationStructureBufferWrite) .resource_access(blas_node, AccessType::AccelerationStructureBuildWrite) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf.build_accel_struct(&[BuildAccelerationStructureInfo::new( blas_node, scratch_data, @@ -710,7 +710,7 @@ fn main() -> anyhow::Result<()> { vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS | vk::BufferUsageFlags::STORAGE_BUFFER, ) - .to_builder() + .into_builder() .alignment(accel_struct_scratch_offset_alignment), )?); let scratch_addr = graph.resource(scratch_buf).device_address(); @@ -724,7 +724,7 @@ fn main() -> anyhow::Result<()> { .resource_access(instance_node, AccessType::AccelerationStructureBuildRead) .resource_access(scratch_buf, AccessType::AccelerationStructureBufferWrite) .resource_access(tlas_node, AccessType::AccelerationStructureBuildWrite) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf.build_accel_struct(&[BuildAccelerationStructureInfo::new( tlas_node, scratch_addr, @@ -881,7 +881,7 @@ fn main() -> anyhow::Result<()> { AccessType::RayTracingShaderReadOther, ) .shader_resource_access(6, material_buf_node, AccessType::RayTracingShaderReadOther) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf.trace_rays( &sbt_rgen, &sbt_miss, diff --git a/examples/rt_triangle.rs b/examples/rt_triangle.rs index 9fb1302b..230e1d48 100644 --- a/examples/rt_triangle.rs +++ b/examples/rt_triangle.rs @@ -136,7 +136,7 @@ fn main() -> anyhow::Result<()> { vk::BufferUsageFlags::SHADER_BINDING_TABLE_KHR | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS, ) - .to_builder() + .into_builder() .alignment(shader_group_base_alignment as _), ) .unwrap(); @@ -335,7 +335,7 @@ fn main() -> anyhow::Result<()> { vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS | vk::BufferUsageFlags::STORAGE_BUFFER, ) - .to_builder() + .into_builder() .alignment(accel_struct_scratch_offset_alignment), )?); let scratch_addr = graph.resource(scratch_buf).device_address(); @@ -347,7 +347,7 @@ fn main() -> anyhow::Result<()> { .resource_access(vertex_node, AccessType::AccelerationStructureBuildRead) .resource_access(scratch_buf, AccessType::AccelerationStructureBufferWrite) .resource_access(blas_node, AccessType::AccelerationStructureBuildWrite) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf.build_accel_struct(&[BuildAccelerationStructureInfo::new( blas_node, scratch_addr, @@ -365,7 +365,7 @@ fn main() -> anyhow::Result<()> { vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS | vk::BufferUsageFlags::STORAGE_BUFFER, ) - .to_builder() + .into_builder() .alignment(accel_struct_scratch_offset_alignment), )?); let scratch_addr = graph.resource(scratch_buf).device_address(); @@ -378,7 +378,7 @@ fn main() -> anyhow::Result<()> { .resource_access(instance_node, AccessType::AccelerationStructureBuildRead) .resource_access(scratch_buf, AccessType::AccelerationStructureBufferWrite) .resource_access(tlas_node, AccessType::AccelerationStructureBuildWrite) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf.build_accel_struct(&[BuildAccelerationStructureInfo::new( tlas_node, scratch_addr, @@ -418,7 +418,7 @@ fn main() -> anyhow::Result<()> { AccessType::RayTracingShaderReadAccelerationStructure, ) .shader_resource_access(1, frame.swapchain_image, AccessType::AnyShaderWrite) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf.trace_rays( &sbt_rgen, &sbt_miss, diff --git a/examples/shader-toy/src/main.rs b/examples/shader-toy/src/main.rs index 0f76972f..e16eea5c 100644 --- a/examples/shader-toy/src/main.rs +++ b/examples/shader-toy/src/main.rs @@ -39,6 +39,7 @@ use { pak::{Pak, PakBuf}, std::time::Instant, vk_graph::{ + cmd_ref::{LoadOp, StoreOp}, driver::{ ash::vk, graphic::{GraphicPipeline, GraphicPipelineInfo}, @@ -310,8 +311,8 @@ fn main() -> anyhow::Result<()> { blank_image, AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, ) - .store_color(0, output) - .record_cmd_buf(move |cmd_buf, _| { + .color_attachment_image(0, output, LoadOp::DontCare, StoreOp::Store) + .record_cmd_buf(move |cmd_buf| { cmd_buf .push_constants(0, bytes_of(&push_consts)) .draw(6, 1, 0, 0); @@ -328,8 +329,8 @@ fn main() -> anyhow::Result<()> { output, AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, ) - .store_color(0, input) - .record_cmd_buf(move |cmd_buf, _| { + .color_attachment_image(0, input, LoadOp::DontCare, StoreOp::Store) + .record_cmd_buf(move |cmd_buf| { cmd_buf .push_constants(0, bytes_of(&push_consts)) .draw(6, 1, 0, 0); diff --git a/examples/skeletal-anim/src/main.rs b/examples/skeletal-anim/src/main.rs index b3bd0b6b..6d211bbb 100644 --- a/examples/skeletal-anim/src/main.rs +++ b/examples/skeletal-anim/src/main.rs @@ -17,11 +17,12 @@ use { time::{Duration, Instant}, }, vk_graph::{ + cmd_ref::{LoadOp, StoreOp}, driver::{ ash::vk, buffer::{Buffer, BufferInfo}, device::Device, - graphic::{DepthStencilMode, GraphicPipeline, GraphicPipelineInfoBuilder}, + graphic::{DepthStencilInfo, GraphicPipeline, GraphicPipelineInfoBuilder}, image::{Image, ImageInfo}, shader::Shader, sync::AccessType, @@ -129,7 +130,7 @@ fn main() -> Result<(), WindowError> { .begin_cmd() .debug_name("🦴") .bind_pipeline(&pipeline) - .depth_stencil(DepthStencilMode::DEPTH_WRITE) + .depth_stencil(DepthStencilInfo::DEPTH_WRITE_LESS_IGNORE_STENCIL) .resource_access(index_buf, AccessType::IndexBuffer) .resource_access(vertex_buf, AccessType::VertexBuffer) .shader_resource_access(0, camera_buf, AccessType::VertexShaderReadUniformBuffer) @@ -139,10 +140,18 @@ fn main() -> Result<(), WindowError> { texture, AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer, ) - .clear_color(0, frame.swapchain_image) - .store_color(0, frame.swapchain_image) - .clear_depth_stencil(depth_image) - .record_cmd_buf(move |cmd_buf, _| { + .color_attachment_image( + 0, + frame.swapchain_image, + LoadOp::CLEAR_BLACK_ALPHA_ZERO, + StoreOp::Store, + ) + .depth_stencil_attachment_image( + depth_image, + LoadOp::CLEAR_ZERO_STENCIL_ZERO, + StoreOp::DontCare, + ) + .record_cmd_buf(move |cmd_buf| { cmd_buf .bind_index_buffer(index_buf, 0, vk::IndexType::UINT16) .bind_vertex_buffer(0, vertex_buf, 0) diff --git a/examples/subgroup_ops.rs b/examples/subgroup_ops.rs index b6b6b02c..e71674d4 100644 --- a/examples/subgroup_ops.rs +++ b/examples/subgroup_ops.rs @@ -100,7 +100,7 @@ fn exclusive_sum( .bind_pipeline(reduce_pipeline) .shader_resource_access(0, input_buf, AccessType::ComputeShaderReadOther) .shader_resource_access(1, workgroup_buf, AccessType::ComputeShaderWrite) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf.dispatch(reduce_count, 1, 1); }); } @@ -112,7 +112,7 @@ fn exclusive_sum( .shader_resource_access(0, workgroup_buf, AccessType::ComputeShaderReadOther) .shader_resource_access(1, input_buf, AccessType::ComputeShaderReadOther) .shader_resource_access(2, output_buf, AccessType::ComputeShaderWrite) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf.dispatch(workgroup_count, 1, 1); }); diff --git a/examples/triangle.rs b/examples/triangle.rs index b16656be..91d0e6f4 100644 --- a/examples/triangle.rs +++ b/examples/triangle.rs @@ -4,6 +4,7 @@ use { bytemuck::cast_slice, clap::Parser, std::sync::Arc, + vk_graph::cmd_ref::LoadOp, vk_graph_prelude::*, vk_graph_window::{WindowBuilder, WindowError}, vk_shader_macros::glsl, @@ -89,9 +90,13 @@ fn main() -> Result<(), WindowError> { .bind_pipeline(&triangle_pipeline) .resource_access(index_node, AccessType::IndexBuffer) .resource_access(vertex_node, AccessType::VertexBuffer) - .clear_color(0, frame.swapchain_image) - .store_color(0, frame.swapchain_image) - .record_cmd_buf(move |cmd_buf, _| { + .color_attachment_image( + 0, + frame.swapchain_image, + LoadOp::CLEAR_BLACK_ALPHA_ZERO, + StoreOp::Store, + ) + .record_cmd_buf(move |cmd_buf| { cmd_buf .bind_index_buffer(index_node, 0, vk::IndexType::UINT16) .bind_vertex_buffer(0, vertex_node, 0) diff --git a/examples/vertex_layout.rs b/examples/vertex_layout.rs index 4a96bb99..9fb48255 100644 --- a/examples/vertex_layout.rs +++ b/examples/vertex_layout.rs @@ -100,10 +100,9 @@ fn draw_triangle(frame: &mut FrameContext, pipeline: &GraphicPipeline, vertex_bu .begin_cmd() .debug_name("Triangle") .bind_pipeline(pipeline) - .load_color(0, frame.swapchain_image) - .store_color(0, frame.swapchain_image) + .color_attachment_image(0, frame.swapchain_image, LoadOp::Load, StoreOp::Store) .resource_access(vertex_buf, AccessType::VertexBuffer) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf .bind_vertex_buffer(0, vertex_buf, 0) .draw(3, 1, 0, 0); diff --git a/examples/vr/src/main.rs b/examples/vr/src/main.rs index 52b21ecd..06da0ace 100644 --- a/examples/vr/src/main.rs +++ b/examples/vr/src/main.rs @@ -21,11 +21,12 @@ use { }, tobj::{load_obj, GPU_LOAD_OPTIONS}, vk_graph::{ + cmd_ref::{LoadOp, StoreOp}, driver::{ ash::vk::{self}, buffer::{Buffer, BufferInfo}, device::Device, - graphic::{DepthStencilMode, GraphicPipelineInfo}, + graphic::{DepthStencilInfo, GraphicPipelineInfo}, image::{Image, ImageInfo}, sync::AccessType, }, @@ -357,10 +358,8 @@ fn main() -> anyhow::Result<()> { .begin_cmd() .debug_name("Left hand") .bind_pipeline(hands_pipeline.hot()) - .depth_stencil(DepthStencilMode::DEPTH_WRITE) + .depth_stencil(DepthStencilInfo::DEPTH_WRITE_LESS_IGNORE_STENCIL) .multiview(VIEW_MASK, VIEW_MASK) - .store_color(0, swapchain_image) - .clear_depth_stencil(depth_image) .resource_access(index_buf, AccessType::IndexBuffer) .resource_access(vertex_buf, AccessType::VertexBuffer) .shader_resource_access(0, camera_buf, AccessType::VertexShaderReadUniformBuffer) @@ -380,7 +379,13 @@ fn main() -> anyhow::Result<()> { occlusion_texture, AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, ) - .record_cmd_buf(move |cmd_buf, _| { + .depth_stencil_attachment_image( + depth_image, + LoadOp::CLEAR_ZERO_STENCIL_ZERO, + StoreOp::DontCare, + ) + .color_attachment_image(0, swapchain_image, LoadOp::DontCare, StoreOp::Store) + .record_cmd_buf(move |cmd_buf| { cmd_buf .bind_index_buffer(index_buf, 0, vk::IndexType::UINT32) .bind_vertex_buffer(0, vertex_buf, 0) @@ -402,10 +407,8 @@ fn main() -> anyhow::Result<()> { .begin_cmd() .debug_name("Right hand") .bind_pipeline(hands_pipeline.hot()) - .depth_stencil(DepthStencilMode::DEPTH_WRITE) + .depth_stencil(DepthStencilInfo::DEPTH_WRITE_LESS_IGNORE_STENCIL) .multiview(VIEW_MASK, VIEW_MASK) - .store_color(0, swapchain_image) - .clear_depth_stencil(depth_image) .resource_access(index_buf, AccessType::IndexBuffer) .resource_access(vertex_buf, AccessType::VertexBuffer) .shader_resource_access(0, camera_buf, AccessType::VertexShaderReadUniformBuffer) @@ -425,7 +428,13 @@ fn main() -> anyhow::Result<()> { occlusion_texture, AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, ) - .record_cmd_buf(move |cmd_buf, _| { + .depth_stencil_attachment_image( + depth_image, + LoadOp::CLEAR_ZERO_STENCIL_ZERO, + StoreOp::DontCare, + ) + .color_attachment_image(0, swapchain_image, LoadOp::DontCare, StoreOp::Store) + .record_cmd_buf(move |cmd_buf| { cmd_buf .bind_index_buffer(index_buf, 0, vk::IndexType::UINT32) .bind_vertex_buffer(0, vertex_buf, 0) @@ -445,10 +454,8 @@ fn main() -> anyhow::Result<()> { .begin_cmd() .debug_name("Woolly Mammoth") .bind_pipeline(mammoth_pipeline.hot()) - .depth_stencil(DepthStencilMode::DEPTH_WRITE) + .depth_stencil(DepthStencilInfo::DEPTH_WRITE_LESS_IGNORE_STENCIL) .multiview(VIEW_MASK, VIEW_MASK) - .store_color(0, swapchain_image) - .clear_depth_stencil(depth_image) .resource_access(index_buf, AccessType::IndexBuffer) .resource_access(vertex_buf, AccessType::VertexBuffer) .shader_resource_access(0, camera_buf, AccessType::VertexShaderReadUniformBuffer) @@ -463,7 +470,13 @@ fn main() -> anyhow::Result<()> { occlusion_texture, AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, ) - .record_cmd_buf(move |cmd_buf, _| { + .depth_stencil_attachment_image( + depth_image, + LoadOp::CLEAR_ZERO_STENCIL_ZERO, + StoreOp::DontCare, + ) + .color_attachment_image(0, swapchain_image, LoadOp::DontCare, StoreOp::Store) + .record_cmd_buf(move |cmd_buf| { cmd_buf .bind_index_buffer(index_buf, 0, vk::IndexType::UINT32) .bind_vertex_buffer(0, vertex_buf, 0) diff --git a/examples/vsm_omni.rs b/examples/vsm_omni.rs index b97d5929..cf7e9099 100644 --- a/examples/vsm_omni.rs +++ b/examples/vsm_omni.rs @@ -13,6 +13,7 @@ use { sync::Arc, }, tobj::{GPU_LOAD_OPTIONS, load_obj}, + vk_graph::cmd_ref::LoadOp, vk_graph_prelude::*, vk_graph_window::WindowBuilder, vk_shader_macros::glsl, @@ -204,7 +205,7 @@ fn main() -> anyhow::Result<()> { | vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::STORAGE, ) - .to_builder() + .into_builder() .flags(vk::ImageCreateFlags::CUBE_COMPATIBLE), ) .unwrap(); @@ -244,7 +245,7 @@ fn main() -> anyhow::Result<()> { .begin_cmd() .debug_name("DEBUG") .bind_pipeline(&debug_pipeline) - .depth_stencil(DepthStencilInfo::DEPTH_WRITE) + .depth_stencil(DepthStencilInfo::DEPTH_WRITE_LESS_IGNORE_STENCIL) .shader_resource_access( 0, camera_uniform_buf, @@ -259,11 +260,18 @@ fn main() -> anyhow::Result<()> { .resource_access(model_mesh_vertex_buf, AccessType::VertexBuffer) .resource_access(cube_mesh_index_buf, AccessType::IndexBuffer) .resource_access(cube_mesh_vertex_buf, AccessType::VertexBuffer) - .clear_color(0, frame.swapchain_image) - .store_color(0, frame.swapchain_image) - .clear_depth_stencil(depth_image) - .store_depth_stencil(depth_image) - .record_cmd_buf(move |cmd_buf, _| { + .depth_stencil_attachment_image( + depth_image, + LoadOp::CLEAR_ZERO_STENCIL_ZERO, + StoreOp::Store, + ) + .color_attachment_image( + 0, + frame.swapchain_image, + LoadOp::CLEAR_BLACK_ALPHA_ZERO, + StoreOp::Store, + ) + .record_cmd_buf(move |cmd_buf| { cmd_buf .bind_index_buffer(model_mesh_index_buf, 0, vk::IndexType::UINT32) .bind_vertex_buffer(0, model_mesh_vertex_buf, 0) @@ -282,7 +290,7 @@ fn main() -> anyhow::Result<()> { .begin_cmd() .debug_name("Shadow (Using geometry shader)") .bind_pipeline(&shadow_pipeline) - .depth_stencil(DepthStencilInfo::DEPTH_WRITE) + .depth_stencil(DepthStencilInfo::DEPTH_WRITE_LESS_IGNORE_STENCIL) .shader_resource_access( 0, light_uniform_buf, @@ -292,10 +300,18 @@ fn main() -> anyhow::Result<()> { .resource_access(model_shadow_vertex_buf, AccessType::VertexBuffer) .resource_access(cube_shadow_index_buf, AccessType::IndexBuffer) .resource_access(cube_shadow_vertex_buf, AccessType::VertexBuffer) - .clear_color_value(0, shadow_faces_node, [light.range, light.range, 0.0, 0.0]) - .store_color(0, shadow_faces_node) - .clear_depth_stencil(shadow_depth_image) - .record_cmd_buf(move |cmd_buf, _| { + .depth_stencil_attachment_image( + shadow_depth_image, + LoadOp::CLEAR_ZERO_STENCIL_ZERO, + StoreOp::DontCare, + ) + .color_attachment_image( + 0, + shadow_faces_node, + LoadOp::clear_rgba(light.range, light.range, 0.0, 0.0), + StoreOp::Store, + ) + .record_cmd_buf(move |cmd_buf| { cmd_buf .bind_index_buffer(model_shadow_index_buf, 0, vk::IndexType::UINT32) .bind_vertex_buffer(0, model_shadow_vertex_buf, 0) @@ -331,7 +347,7 @@ fn main() -> anyhow::Result<()> { .begin_cmd() .debug_name("Shadow") .bind_pipeline(&shadow_pipeline) - .depth_stencil(DepthStencilInfo::DEPTH_WRITE) + .depth_stencil(DepthStencilInfo::DEPTH_WRITE_LESS_IGNORE_STENCIL) .shader_resource_access( 0, light_uniform_buf, @@ -341,15 +357,19 @@ fn main() -> anyhow::Result<()> { .resource_access(model_shadow_vertex_buf, AccessType::VertexBuffer) .resource_access(cube_shadow_index_buf, AccessType::IndexBuffer) .resource_access(cube_shadow_vertex_buf, AccessType::VertexBuffer) - .clear_color_value_as( + .depth_stencil_attachment_image( + shadow_depth_image, + LoadOp::CLEAR_ZERO_STENCIL_ZERO, + StoreOp::DontCare, + ) + .color_attachment_image_view( 0, shadow_faces_node, - [light.range, light.range, 0.0, 0.0], shadow_faces_view_info, + LoadOp::clear_rgba(light.range, light.range, 0.0, 0.0), + StoreOp::Store, ) - .store_color_as(0, shadow_faces_node, shadow_faces_view_info) - .clear_depth_stencil(shadow_depth_image) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf .bind_index_buffer(model_shadow_index_buf, 0, vk::IndexType::UINT32) .bind_vertex_buffer(0, model_shadow_vertex_buf, 0) @@ -378,7 +398,7 @@ fn main() -> anyhow::Result<()> { AccessType::ComputeShaderReadOther, ) .shader_resource_access(1, temp_image, AccessType::ComputeShaderWrite) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf.dispatch(1, CUBEMAP_SIZE, 6); }) .end_cmd() @@ -391,7 +411,7 @@ fn main() -> anyhow::Result<()> { shadow_faces_node, AccessType::ComputeShaderWrite, ) - .record_cmd_buf(move |cmd_buf, _| { + .record_cmd_buf(move |cmd_buf| { cmd_buf.dispatch(CUBEMAP_SIZE, 1, 6); }); } @@ -403,7 +423,7 @@ fn main() -> anyhow::Result<()> { .begin_cmd() .debug_name("Mesh objects") .bind_pipeline(&mesh_pipeline) - .depth_stencil(DepthStencilInfo::DEPTH_WRITE) + .depth_stencil(DepthStencilInfo::DEPTH_WRITE_LESS_IGNORE_STENCIL) .shader_resource_access( 0, camera_uniform_buf, @@ -426,10 +446,18 @@ fn main() -> anyhow::Result<()> { .resource_access(model_mesh_vertex_buf, AccessType::VertexBuffer) .resource_access(cube_mesh_index_buf, AccessType::IndexBuffer) .resource_access(cube_mesh_vertex_buf, AccessType::VertexBuffer) - .clear_color(0, frame.swapchain_image) - .store_color(0, frame.swapchain_image) - .clear_depth_stencil(depth_image) - .record_cmd_buf(move |cmd_buf, _| { + .depth_stencil_attachment_image( + depth_image, + LoadOp::CLEAR_ZERO_STENCIL_ZERO, + StoreOp::DontCare, + ) + .color_attachment_image( + 0, + frame.swapchain_image, + LoadOp::CLEAR_BLACK_ALPHA_ZERO, + StoreOp::Store, + ) + .record_cmd_buf(move |cmd_buf| { cmd_buf .bind_index_buffer(model_mesh_index_buf, 0, vk::IndexType::UINT32) .bind_vertex_buffer(0, model_mesh_vertex_buf, 0) diff --git a/src/bind.rs b/src/bind.rs index b6a5c202..c0980f91 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -281,45 +281,43 @@ bind_graph_resource!(Buffer); /// A trait for resources which may be borrowed from a `Graph`. /// /// See [`Graph::node`] for details. -pub trait Bound { +pub trait Bound: Node { /// The Vulkan buffer, image, or acceleration struction type. type Resource; /// Borrows the resource from a graph. - fn borrow(self, graph: &Graph) -> &Self::Resource; + fn borrow(self, resources: &[Resource]) -> &Self::Resource; } impl Bound for AnyAccelerationStructureNode { type Resource = AccelerationStructure; - fn borrow(self, graph: &Graph) -> &Self::Resource { - graph.resources[self.index()] - .as_driver_accel_struct() - .unwrap() + fn borrow(self, resources: &[Resource]) -> &Self::Resource { + resources[self.index()].as_driver_accel_struct().unwrap() } } impl Bound for AnyBufferNode { type Resource = Buffer; - fn borrow(self, graph: &Graph) -> &Self::Resource { - graph.resources[self.index()].as_driver_buffer().unwrap() + fn borrow(self, resources: &[Resource]) -> &Self::Resource { + resources[self.index()].as_driver_buffer().unwrap() } } impl Bound for AnyImageNode { type Resource = Image; - fn borrow(self, graph: &Graph) -> &Self::Resource { - graph.resources[self.index()].as_driver_image().unwrap() + fn borrow(self, resources: &[Resource]) -> &Self::Resource { + resources[self.index()].as_driver_image().unwrap() } } impl Bound for SwapchainImageNode { type Resource = SwapchainImage; - fn borrow(self, graph: &Graph) -> &Self::Resource { - graph.resources[self.idx].as_swapchain_image().unwrap() + fn borrow(self, resources: &[Resource]) -> &Self::Resource { + resources[self.idx].as_swapchain_image().unwrap() } } @@ -329,9 +327,9 @@ macro_rules! bound { impl Bound for [<$name Node>] { type Resource = Arc<$name>; - fn borrow(self, graph: &Graph) -> &Self::Resource { - graph - .resources[self.idx] + fn borrow(self, resources: &[Resource]) -> &Self::Resource { + + resources[self.idx] .[]() .unwrap() } @@ -340,9 +338,8 @@ macro_rules! bound { impl Bound for [<$name LeaseNode>] { type Resource = Arc>; - fn borrow(self, graph: &Graph) -> &Self::Resource { - graph - .resources[self.idx] + fn borrow(self, resources: &[Resource]) -> &Self::Resource { + resources[self.idx] .[]() .unwrap() } diff --git a/src/cmd_ref/cmd_buf.rs b/src/cmd_ref/cmd_buf.rs index 512aa03e..e9df8a86 100644 --- a/src/cmd_ref/cmd_buf.rs +++ b/src/cmd_ref/cmd_buf.rs @@ -1,7 +1,6 @@ use { - super::Resources, crate::{ - AnyAccelerationStructureNode, + AnyAccelerationStructureNode, Bound, Resource, driver::{ accel_struct::{ AccelerationStructureGeometry, AccelerationStructureGeometryInfo, @@ -15,6 +14,9 @@ use { std::{cell::RefCell, ops::Deref}, }; +#[cfg(debug_assertions)] +use crate::Execution; + /// Recording interface for acceleration structure commands. /// /// This structure provides a strongly-typed set of methods which allow acceleration structures to @@ -43,12 +45,30 @@ use { /// }); /// # Ok(()) } /// ``` +#[derive(Clone, Copy)] pub struct CommandBufferRef<'a> { - pub(super) cmd_buf: &'a CommandBuffer, - pub(super) resources: Resources<'a>, + cmd_buf: &'a CommandBuffer, + + #[cfg(debug_assertions)] + exec: &'a Execution, + + resources: &'a [Resource], } -impl CommandBufferRef<'_> { +impl<'a> CommandBufferRef<'a> { + pub(crate) fn new( + cmd_buf: &'a CommandBuffer, + resources: &'a [Resource], + #[cfg(debug_assertions)] exec: &'a Execution, + ) -> Self { + Self { + cmd_buf, + #[cfg(debug_assertions)] + exec, + resources, + } + } + /// Build acceleration structures. /// /// There is no ordering or synchronization implied between any of the individual acceleration @@ -71,7 +91,7 @@ impl CommandBufferRef<'_> { /// ```no_run /// # use ash::vk; /// # use vk_graph::cmd_ref::BuildAccelerationStructureInfo; - /// # use vk_graph::driver::{AccessType, DriverError}; + /// # use vk_graph::driver::{sync::AccessType, DriverError}; /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureGeometry, AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, AccelerationStructureInfo, DeviceOrHostAddress}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; @@ -97,20 +117,20 @@ impl CommandBufferRef<'_> { /// .resource_access(vertex_buf, AccessType::VertexBuffer) /// .resource_access(scratch_buf, AccessType::AccelerationStructureBufferWrite) /// .resource_access(blas_node, AccessType::AccelerationStructureBuildWrite) - /// .record_cmd_buf(move |cmd_buf, resources| { - /// let scratch_addr = resources[scratch_buf].device_address(); + /// .record_cmd_buf(move |cmd_buf| { + /// let scratch_addr = cmd_buf.resource(scratch_buf).device_address(); /// let geom = AccelerationStructureGeometry { /// max_primitive_count: 64, /// flags: vk::GeometryFlagsKHR::OPAQUE, /// geometry: AccelerationStructureGeometryData::Triangles { /// index_addr: DeviceOrHostAddress::DeviceAddress( - /// resources[index_buf].device_address() + /// cmd_buf.resource(index_buf).device_address() /// ), /// index_type: vk::IndexType::UINT32, /// max_vertex: 42, /// transform_addr: None, /// vertex_addr: DeviceOrHostAddress::DeviceAddress( - /// resources[vertex_buf].device_address(), + /// cmd_buf.resource(vertex_buf).device_address(), /// ), /// vertex_format: vk::Format::R32G32B32_SFLOAT, /// vertex_stride: 12, @@ -180,7 +200,7 @@ impl CommandBufferRef<'_> { .ty(info.build_data.ty) .flags(info.build_data.flags) .mode(vk::BuildAccelerationStructureModeKHR::BUILD) - .dst_acceleration_structure(self.resources[info.accel_struct].handle) + .dst_acceleration_structure(self.resource(info.accel_struct).handle) .geometries(&tls.geometries[start..end]) .scratch_data(info.scratch_addr.into()), ); @@ -255,7 +275,7 @@ impl CommandBufferRef<'_> { .ty(info.build_data.ty) .flags(info.build_data.flags) .mode(vk::BuildAccelerationStructureModeKHR::BUILD) - .dst_acceleration_structure(self.resources[info.accel_struct].handle) + .dst_acceleration_structure(self.resource(info.accel_struct).handle) .geometries(&tls.geometries[start..end]) .scratch_data(info.scratch_data.into()), ); @@ -346,12 +366,8 @@ impl CommandBufferRef<'_> { .ty(info.update_data.ty) .flags(info.update_data.flags) .mode(vk::BuildAccelerationStructureModeKHR::UPDATE) - .dst_acceleration_structure( - self.resources[info.dst_accel_struct].handle, - ) - .src_acceleration_structure( - self.resources[info.src_accel_struct].handle, - ) + .dst_acceleration_structure(self.resource(info.dst_accel_struct).handle) + .src_acceleration_structure(self.resource(info.src_accel_struct).handle) .geometries(&tls.geometries[start..end]) .scratch_data(info.scratch_addr.into()), ); @@ -426,12 +442,8 @@ impl CommandBufferRef<'_> { .ty(info.update_data.ty) .flags(info.update_data.flags) .mode(vk::BuildAccelerationStructureModeKHR::UPDATE) - .src_acceleration_structure( - self.resources[info.src_accel_struct].handle, - ) - .dst_acceleration_structure( - self.resources[info.dst_accel_struct].handle, - ) + .src_acceleration_structure(self.resource(info.src_accel_struct).handle) + .dst_acceleration_structure(self.resource(info.dst_accel_struct).handle) .geometries(&tls.geometries[start..end]) .scratch_data(info.scratch_addr.into()), ); @@ -459,6 +471,26 @@ impl CommandBufferRef<'_> { self } + + /// Returns a borrow of the original Vulkan resource (buffer, image or acceleration structure) + /// which the given node represents. + pub fn resource(&self, node: N) -> &N::Resource + where + N: Bound, + { + // You must have called an access function for this node on this execution before indexing + // into the bindings data! + // + // Why: Code that attempts to access this function is attempting to get access to the Vulkan + // resource (buffer, image, or acceleration structure). In order to access any resources the + // access type must first be specified so the correct barriers may be added. + debug_assert!( + self.exec.accesses.contains_key(&node.index()), + "unexpected node access: call access, read, or write first" + ); + + node.borrow(self.resources) + } } impl<'a> Deref for CommandBufferRef<'a> { diff --git a/src/cmd_ref/compute.rs b/src/cmd_ref/compute.rs index 1f8faef8..aa8940b7 100644 --- a/src/cmd_ref/compute.rs +++ b/src/cmd_ref/compute.rs @@ -1,5 +1,5 @@ use { - super::{Resources, cmd_buf::CommandBufferRef, pipeline::PipelineCommandRef}, + super::{cmd_buf::CommandBufferRef, pipeline::PipelineCommandRef}, crate::{driver::compute::ComputePipeline, node::AnyBufferNode}, ash::vk, log::trace, @@ -33,16 +33,25 @@ use { /// my_graph /// .begin_cmd() /// .bind_pipeline(&my_compute_pipeline) -/// .record_cmd_buf(move |cmd_buf, resources| { +/// .record_cmd_buf(move |cmd_buf| { /// // During this closure we have access to the compute dispatch methods! /// }); /// # Ok(()) } /// ``` pub struct ComputeCommandBufferRef<'a> { - pub(super) cmd_buf: CommandBufferRef<'a>, - pub(super) pipeline: ComputePipeline, + cmd_buf: CommandBufferRef<'a>, + pipeline: ComputePipeline, } +// impl<'a> ComputeCommandBufferRef<'a> { +// pub(super) fn new(cmd_buf: CommandBufferRef<'a>, +// pipeline: ComputePipeline,) -> Self { +// Self { +// cmd_buf,pipeline +// } +// } +// } + impl ComputeCommandBufferRef<'_> { /// [Dispatch] compute work items. /// @@ -90,7 +99,7 @@ impl ComputeCommandBufferRef<'_> { /// .debug_name("fill my_buf_node with data") /// .bind_pipeline(&my_compute_pipeline) /// .shader_resource_access(0, my_buf_node, AccessType::ComputeShaderWrite) - /// .record_cmd_buf(move |cmd_buf, resources| { + /// .record_cmd_buf(move |cmd_buf| { /// cmd_buf.dispatch(128, 64, 32); /// }); /// # Ok(()) } @@ -195,7 +204,7 @@ impl ComputeCommandBufferRef<'_> { /// .bind_pipeline(&my_compute_pipeline) /// .resource_access(args_buf, AccessType::IndirectBuffer) /// .shader_resource_access(0, my_buf_node, AccessType::ComputeShaderWrite) - /// .record_cmd_buf(move |cmd_buf, resources| { + /// .record_cmd_buf(move |cmd_buf| { /// cmd_buf.dispatch_indirect(args_buf, 0); /// }); /// # Ok(()) } @@ -210,11 +219,12 @@ impl ComputeCommandBufferRef<'_> { args_offset: vk::DeviceSize, ) -> &Self { let args_buf = args_buf.into(); + let args_buf = self.resource(args_buf); unsafe { self.cmd_buf.device.cmd_dispatch_indirect( self.cmd_buf.handle, - self.cmd_buf.resources[args_buf].handle, + args_buf.handle, args_offset, ); } @@ -276,7 +286,7 @@ impl ComputeCommandBufferRef<'_> { /// .begin_cmd() /// .debug_name("compute the ultimate question") /// .bind_pipeline(&my_compute_pipeline) - /// .record_cmd_buf(move |cmd_buf, resources| { + /// .record_cmd_buf(move |cmd_buf| { /// cmd_buf /// .push_constants(0, &[42]) /// .dispatch(1, 1, 1); @@ -329,7 +339,7 @@ impl PipelineCommandRef<'_, ComputePipeline> { /// Begin recording a compute pipeline command buffer. pub fn record_cmd_buf( mut self, - func: impl FnOnce(ComputeCommandBufferRef<'_>, Resources<'_>) + Send + 'static, + func: impl FnOnce(ComputeCommandBufferRef<'_>) + Send + 'static, ) -> Self { let pipeline = self .cmd @@ -343,14 +353,8 @@ impl PipelineCommandRef<'_, ComputePipeline> { .unwrap_compute() .clone(); - self.cmd.push_execute(move |cmd_buf, resources| { - func( - ComputeCommandBufferRef { - cmd_buf: CommandBufferRef { cmd_buf, resources }, - pipeline, - }, - resources, - ); + self.cmd.push_execute(move |cmd_buf| { + func(ComputeCommandBufferRef { cmd_buf, pipeline }); }); self @@ -362,12 +366,13 @@ mod deprecated { use { crate::{ cmd_ref::{ - Descriptor, PipelineCommandRef, Resources, SubresourceRange, View, ViewInfo, + Descriptor, PipelineCommandRef, SubresourceRange, View, ViewInfo, compute::ComputeCommandBufferRef, }, driver::compute::ComputePipeline, node::Node, }, + std::any::Any, vk_sync::AccessType, }; @@ -410,9 +415,9 @@ mod deprecated { #[doc(hidden)] pub fn record_compute( self, - func: impl FnOnce(ComputeCommandBufferRef<'_>, Resources<'_>) + Send + 'static, + func: impl FnOnce(ComputeCommandBufferRef<'_>, ()) + Send + 'static, ) -> Self { - self.record_cmd_buf(func) + self.record_cmd_buf(|cmd_buf| func(cmd_buf, ())) } #[deprecated = "use shader_resource_access function with AccessType::ComputeShaderWrite"] diff --git a/src/cmd_ref/graphic.rs b/src/cmd_ref/graphic.rs index 5adbdd31..23ffdb67 100644 --- a/src/cmd_ref/graphic.rs +++ b/src/cmd_ref/graphic.rs @@ -1,26 +1,85 @@ use { - super::{ - AttachmentIndex, Resources, SubresourceAccess, SubresourceRange, cmd_buf::CommandBufferRef, - pipeline::PipelineCommandRef, - }, + super::{AttachmentIndex, cmd_buf::CommandBufferRef, pipeline::PipelineCommandRef}, crate::{ - Attachment, ClearColorValue, + ClearColorValue, driver::{ graphic::{DepthStencilInfo, GraphicPipeline}, - image::{ - ImageInfo, ImageViewInfo, image_subresource_range_contains, - image_subresource_range_intersects, - }, + image::ImageViewInfo, render_pass::ResolveMode, }, - node::{AnyBufferNode, AnyImageNode, Node}, + node::{AnyBufferNode, AnyImageNode}, }, ash::vk, log::trace, std::{cell::RefCell, ops::Deref, slice}, - vk_sync::AccessType, }; +/// TODO +#[derive(Clone, Copy, Debug)] +pub enum LoadOp { + /// TODO + Clear(T), + + /// TODO + DontCare, + + /// TODO + Load, +} + +impl LoadOp { + /// A load operation which results in a color attachment filled with rgb zeros and alpha ones. + pub const CLEAR_BLACK_ALPHA_ONE: Self = + Self::Clear(ClearColorValue::Float32([0.0, 0.0, 0.0, 1.0])); + + /// A load operation which results in a color attachment filled with zeros. + pub const CLEAR_BLACK_ALPHA_ZERO: Self = + Self::Clear(ClearColorValue::Float32([0.0, 0.0, 0.0, 0.0])); + + /// A load operation which results in a color attachment filled with rgb zeros and alpha ones. + pub const CLEAR_WHITE_ALPHA_ONE: Self = + Self::Clear(ClearColorValue::Float32([1.0, 1.0, 1.0, 1.0])); + + /// A load operation which results in a color attachment filled with rgba ones and alpha zeros. + pub const CLEAR_WHITE_ALPHA_ZERO: Self = + Self::Clear(ClearColorValue::Float32([1.0, 1.0, 1.0, 0.0])); + + /// Convenience constructor for clear color values. + pub fn clear_rgba(r: f32, g: f32, b: f32, a: f32) -> Self { + Self::Clear(ClearColorValue::Float32([r, g, b, a])) + } +} + +impl LoadOp { + /// A load operation which results in a depth attachment filled with ones and stencil filled + /// with zeros. + pub const CLEAR_ONE_STENCIL_ZERO: Self = Self::Clear(vk::ClearDepthStencilValue { + depth: 1.0, + stencil: 0, + }); + + /// A load operation which results in a depth and stencil attachment filled with zeros. + pub const CLEAR_ZERO_STENCIL_ZERO: Self = Self::Clear(vk::ClearDepthStencilValue { + depth: 0.0, + stencil: 0, + }); + + /// Convenience constructor for clear depth and stencil values. + pub fn clear_depth_stencil(depth: f32, stencil: u32) -> Self { + Self::Clear(vk::ClearDepthStencilValue { depth, stencil }) + } +} + +/// TODO +#[derive(Clone, Copy, Debug)] +pub enum StoreOp { + /// TODO + DontCare, + + /// TODO + Store, +} + /// Recording interface for drawing commands. /// /// This structure provides a strongly-typed set of methods which allow rasterization shader code to @@ -56,16 +115,25 @@ use { /// .debug_name("my draw pass") /// .bind_pipeline(&my_graphic_pipeline) /// .store_color(0, swapchain_image) -/// .record_cmd_buf(move |cmd_buf, resources| { +/// .record_cmd_buf(move |cmd_buf| { /// // During this closure we have access to the draw methods! /// }); /// # Ok(()) } /// ``` pub struct GraphicCommandBufferRef<'a> { - pub(super) cmd_buf: CommandBufferRef<'a>, - pub(super) pipeline: GraphicPipeline, + cmd_buf: CommandBufferRef<'a>, + pipeline: GraphicPipeline, } +// impl<'a> GraphicCommandBufferRef<'a> { +// pub(super) fn new(cmd_buf: CommandBufferRef<'a>, +// pipeline: GraphicPipeline,) -> Self { +// Self { +// cmd_buf,pipeline +// } +// } +// } + impl GraphicCommandBufferRef<'_> { /// Bind an index buffer to the current pass. /// @@ -109,7 +177,7 @@ impl GraphicCommandBufferRef<'_> { /// .store_color(0, swapchain_image) /// .resource_access(my_idx_buf, AccessType::IndexBuffer) /// .resource_access(my_vtx_buf, AccessType::VertexBuffer) - /// .record_cmd_buf(move |cmd_buf, resources| { + /// .record_cmd_buf(move |cmd_buf| { /// cmd_buf /// .bind_index_buffer(my_idx_buf, 0, vk::IndexType::UINT16) /// .bind_vertex_buffer(0, my_vtx_buf, 0) @@ -125,11 +193,12 @@ impl GraphicCommandBufferRef<'_> { index_ty: vk::IndexType, ) -> &Self { let buffer = buffer.into(); + let buffer = self.resource(buffer); unsafe { self.cmd_buf.device.cmd_bind_index_buffer( self.cmd_buf.handle, - self.resources[buffer].handle, + buffer.handle, offset, index_ty, ); @@ -148,7 +217,7 @@ impl GraphicCommandBufferRef<'_> { /// /// ```no_run /// # use ash::vk; - /// # use vk_graph::driver::{AccessType, DriverError}; + /// # use vk_graph::driver::{sync::AccessType, DriverError}; /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; @@ -175,7 +244,7 @@ impl GraphicCommandBufferRef<'_> { /// .bind_pipeline(&my_graphic_pipeline) /// .store_color(0, swapchain_image) /// .resource_access(my_vtx_buf, AccessType::VertexBuffer) - /// .record_cmd_buf(move |cmd_buf, resources| { + /// .record_cmd_buf(move |cmd_buf| { /// cmd_buf /// .bind_vertex_buffer(0, my_vtx_buf, 0) /// .draw(42, 1, 0, 0); @@ -190,12 +259,13 @@ impl GraphicCommandBufferRef<'_> { offset: vk::DeviceSize, ) -> &Self { let buffer = buffer.into(); + let buffer = self.resource(buffer); unsafe { self.cmd_buf.device.cmd_bind_vertex_buffers( self.cmd_buf.handle, binding, - slice::from_ref(&self.resources[buffer].handle), + slice::from_ref(&buffer.handle), slice::from_ref(&offset), ); } @@ -229,8 +299,9 @@ impl GraphicCommandBufferRef<'_> { for (buffer, offset) in buffer_offsets { let buffer = buffer.into(); + let buffer = self.resource(buffer); - buffers.push(self.resources[buffer].handle); + buffers.push(buffer.handle); offsets.push(offset); } @@ -369,7 +440,7 @@ impl GraphicCommandBufferRef<'_> { /// .resource_access(my_idx_buf, AccessType::IndexBuffer) /// .resource_access(my_vtx_buf, AccessType::VertexBuffer) /// .resource_access(buf_node, AccessType::IndirectBuffer) - /// .record_cmd_buf(move |cmd_buf, resources| { + /// .record_cmd_buf(move |cmd_buf| { /// cmd_buf /// .bind_index_buffer(my_idx_buf, 0, vk::IndexType::UINT16) /// .bind_vertex_buffer(0, my_vtx_buf, 0) @@ -386,11 +457,12 @@ impl GraphicCommandBufferRef<'_> { stride: u32, ) -> &Self { let buffer = buffer.into(); + let buffer = self.resource(buffer); unsafe { self.cmd_buf.device.cmd_draw_indexed_indirect( self.cmd_buf.handle, - self.resources[buffer].handle, + buffer.handle, offset, draw_count, stride, @@ -423,14 +495,17 @@ impl GraphicCommandBufferRef<'_> { stride: u32, ) -> &Self { let buffer = buffer.into(); + let buffer = self.resource(buffer); + let count_buf = count_buf.into(); + let count_buf = self.resource(count_buf); unsafe { self.cmd_buf.device.cmd_draw_indexed_indirect_count( self.cmd_buf.handle, - self.resources[buffer].handle, + buffer.handle, offset, - self.resources[count_buf].handle, + count_buf.handle, count_buf_offset, max_draw_count, stride, @@ -452,11 +527,12 @@ impl GraphicCommandBufferRef<'_> { stride: u32, ) -> &Self { let buffer = buffer.into(); + let buffer = self.resource(buffer); unsafe { self.cmd_buf.device.cmd_draw_indirect( self.cmd_buf.handle, - self.resources[buffer].handle, + buffer.handle, offset, draw_count, stride, @@ -480,14 +556,17 @@ impl GraphicCommandBufferRef<'_> { stride: u32, ) -> &Self { let buffer = buffer.into(); + let buffer = self.resource(buffer); + let count_buf = count_buf.into(); + let count_buf = self.resource(count_buf); unsafe { self.cmd_buf.device.cmd_draw_indirect_count( self.cmd_buf.handle, - self.resources[buffer].handle, + buffer.handle, offset, - self.resources[count_buf].handle, + count_buf.handle, count_buf_offset, max_draw_count, stride, @@ -557,7 +636,7 @@ impl GraphicCommandBufferRef<'_> { /// .debug_name("draw a quad") /// .bind_pipeline(&my_graphic_pipeline) /// .store_color(0, swapchain_image) - /// .record_cmd_buf(move |cmd_buf, resources| { + /// .record_cmd_buf(move |cmd_buf| { /// cmd_buf /// .push_constants(0, &[42]) /// .draw(6, 1, 0, 0); @@ -631,6 +710,69 @@ impl<'a> Deref for GraphicCommandBufferRef<'a> { // NOTE: local implementation of type from super module impl PipelineCommandRef<'_, GraphicPipeline> { + /// TODO + pub fn color_attachment_image( + mut self, + color_attachment_idx: AttachmentIndex, + color_image: impl Into, + load: LoadOp, + store: StoreOp, + ) -> Self { + self.set_color_attachment_image(color_attachment_idx, color_image, load, store); + self + } + + /// TODO + pub fn color_attachment_image_resolve( + mut self, + color_attachment_idx: AttachmentIndex, + color_image: impl Into, + resolve_attachment_idx: AttachmentIndex, + ) -> Self { + self.set_color_attachment_image_resolve( + color_attachment_idx, + color_image, + resolve_attachment_idx, + ); + self + } + + /// TODO + pub fn color_attachment_image_view( + mut self, + color_attachment_idx: AttachmentIndex, + color_image: impl Into, + color_image_view: impl Into, + load: LoadOp, + store: StoreOp, + ) -> Self { + self.set_color_attachment_image_view( + color_attachment_idx, + color_image, + color_image_view, + load, + store, + ); + self + } + + /// TODO + pub fn color_attachment_image_view_resolve( + mut self, + color_attachment_idx: AttachmentIndex, + color_image: impl Into, + color_image_view: impl Into, + resolve_attachment_idx: AttachmentIndex, + ) -> Self { + self.set_color_attachment_image_view_resolve( + color_attachment_idx, + color_image, + color_image_view, + resolve_attachment_idx, + ); + self + } + /// Sets the combined depth and stencil state used by any subsequent command buffer recordings /// of the current graph command. pub fn depth_stencil(mut self, depth_stencil: impl Into) -> Self { @@ -638,6 +780,70 @@ impl PipelineCommandRef<'_, GraphicPipeline> { self } + /// TODO + pub fn depth_stencil_attachment_image( + mut self, + depth_stencil_image: impl Into, + load: LoadOp, + store: StoreOp, + ) -> Self { + self.set_depth_stencil_attachment_image(depth_stencil_image, load, store); + self + } + + /// TODO + pub fn depth_stencil_attachment_image_resolve( + mut self, + attachment_idx: AttachmentIndex, + depth_stencil_image: impl Into, + depth_mode: Option, + stencil_mode: Option, + ) -> Self { + self.set_depth_stencil_attachment_image_resolve( + attachment_idx, + depth_stencil_image, + depth_mode, + stencil_mode, + ); + self + } + + /// TODO + pub fn depth_stencil_attachment_image_view( + mut self, + depth_stencil_image: impl Into, + depth_stencil_image_view: impl Into, + load: LoadOp, + store: StoreOp, + ) -> Self { + self.set_depth_stencil_attachment_image_view( + depth_stencil_image, + depth_stencil_image_view, + load, + store, + ); + self + } + + /// TODO + pub fn depth_stencil_attachment_image_view_resolve( + mut self, + attachment_idx: AttachmentIndex, + depth_stencil_image: impl Into, + depth_stencil_image_view: impl Into, + depth_mode: Option, + stencil_mode: Option, + ) -> Self { + self.set_depth_stencil_attachment_image_view_resolve( + attachment_idx, + depth_stencil_image, + depth_stencil_image_view, + depth_mode, + stencil_mode, + ); + self + } + /// Sets multiview view and correlation masks used by any subsequent command buffer recordings /// of the current graph command. /// @@ -650,7 +856,7 @@ impl PipelineCommandRef<'_, GraphicPipeline> { /// Begin recording a graphics pipeline command buffer. pub fn record_cmd_buf( mut self, - func: impl FnOnce(GraphicCommandBufferRef<'_>, Resources<'_>) + Send + 'static, + func: impl FnOnce(GraphicCommandBufferRef<'_>) + Send + 'static, ) -> Self { let pipeline = self .cmd @@ -664,14 +870,8 @@ impl PipelineCommandRef<'_, GraphicPipeline> { .unwrap_graphic() .clone(); - self.cmd.push_execute(move |cmd_buf, resources| { - func( - GraphicCommandBufferRef { - cmd_buf: CommandBufferRef { cmd_buf, resources }, - pipeline, - }, - resources, - ); + self.cmd.push_execute(move |cmd_buf| { + func(GraphicCommandBufferRef { cmd_buf, pipeline }); }); self @@ -693,6 +893,108 @@ impl PipelineCommandRef<'_, GraphicPipeline> { self } + /// See [color_attachment_image] + pub fn set_color_attachment_image( + &mut self, + color_attachment_idx: AttachmentIndex, + color_image: impl Into, + load: LoadOp, + store: StoreOp, + ) -> &mut Self { + let color_image = color_image.into(); + let color_image_view = self.resource(color_image).info; + + self.set_color_attachment_image_view( + color_attachment_idx, + color_image, + color_image_view, + load, + store, + ); + + self + } + + /// See [color_attachment_image_resolve] + pub fn set_color_attachment_image_resolve( + &mut self, + color_attachment_idx: AttachmentIndex, + color_image: impl Into, + resolve_attachment_idx: AttachmentIndex, + ) -> &mut Self { + let color_image = color_image.into(); + let color_image_view = self.resource(color_image).info; + + self.set_color_attachment_image_view_resolve( + color_attachment_idx, + color_image, + color_image_view, + resolve_attachment_idx, + ); + + self + } + + /// See [color_attachment_image_view] + pub fn set_color_attachment_image_view( + &mut self, + color_attachment_idx: AttachmentIndex, + color_image: impl Into, + color_image_view: impl Into, + load: LoadOp, + store: StoreOp, + ) -> &mut Self { + let color_image = color_image.into(); + let color_image_view = color_image_view.into(); + + #[allow(deprecated)] + { + match load { + LoadOp::Clear(color) => self.set_clear_color_value_as( + color_attachment_idx, + color_image, + color, + color_image_view, + ), + LoadOp::DontCare => { + self.set_attach_color_as(color_attachment_idx, color_image, color_image_view) + } + LoadOp::Load => { + self.set_load_color_as(color_attachment_idx, color_image, color_image_view) + } + }; + + if let StoreOp::Store = store { + self.set_store_color_as(color_attachment_idx, color_image, color_image_view); + } + } + + self + } + + /// See [color_attachment_image_view_resolve] + pub fn set_color_attachment_image_view_resolve( + &mut self, + color_attachment_idx: AttachmentIndex, + color_image: impl Into, + color_image_view: impl Into, + resolve_attachment_idx: AttachmentIndex, + ) -> &mut Self { + let color_image = color_image.into(); + let color_image_view = color_image_view.into(); + + #[allow(deprecated)] + self.set_attach_color_as(color_attachment_idx, color_image, color_image_view) + .set_resolve_color_as( + resolve_attachment_idx, + color_attachment_idx, + color_image, + color_image_view, + ); + + self + } + /// See [depth_stencil] pub fn set_depth_stencil(&mut self, depth_stencil: impl Into) -> &mut Self { let depth_stencil = depth_stencil.into(); @@ -706,6 +1008,109 @@ impl PipelineCommandRef<'_, GraphicPipeline> { self } + /// See [depth_stencil_attachment_image] + pub fn set_depth_stencil_attachment_image( + &mut self, + depth_stencil_image: impl Into, + load: LoadOp, + store: StoreOp, + ) -> &mut Self { + let depth_stencil_image = depth_stencil_image.into(); + let depth_stencil_image_view = self.resource(depth_stencil_image).info; + + self.set_depth_stencil_attachment_image_view( + depth_stencil_image, + depth_stencil_image_view, + load, + store, + ); + + self + } + + /// See [depth_stencil_attachment_image_resolve] + pub fn set_depth_stencil_attachment_image_resolve( + &mut self, + attachment_idx: AttachmentIndex, + depth_stencil_image: impl Into, + depth_mode: Option, + stencil_mode: Option, + ) -> &mut Self { + let depth_stencil_image = depth_stencil_image.into(); + let depth_stencil_image_view = self.resource(depth_stencil_image).info; + + self.set_depth_stencil_attachment_image_view_resolve( + attachment_idx, + depth_stencil_image, + depth_stencil_image_view, + depth_mode, + stencil_mode, + ); + + self + } + + /// See [depth_stencil_attachment_image_view] + pub fn set_depth_stencil_attachment_image_view( + &mut self, + depth_stencil_image: impl Into, + depth_stencil_image_view: impl Into, + load: LoadOp, + store: StoreOp, + ) -> &mut Self { + let depth_stencil_image = depth_stencil_image.into(); + let depth_stencil_image_view = depth_stencil_image_view.into(); + + #[allow(deprecated)] + { + match load { + LoadOp::Clear(color) => self.set_clear_depth_stencil_value_as( + depth_stencil_image, + color.depth, + color.stencil, + depth_stencil_image_view, + ), + LoadOp::DontCare => { + self.set_attach_depth_stencil_as(depth_stencil_image, depth_stencil_image_view) + } + LoadOp::Load => { + self.set_load_depth_stencil_as(depth_stencil_image, depth_stencil_image_view) + } + }; + + if let StoreOp::Store = store { + self.set_store_depth_stencil_as(depth_stencil_image, depth_stencil_image_view); + } + } + + self + } + + /// See [depth_stencil_attachment_image_view_resolve] + pub fn set_depth_stencil_attachment_image_view_resolve( + &mut self, + attachment_idx: AttachmentIndex, + depth_stencil_image: impl Into, + depth_stencil_image_view: impl Into, + depth_mode: Option, + stencil_mode: Option, + ) -> &mut Self { + let depth_stencil_image = depth_stencil_image.into(); + let depth_stencil_image_view = depth_stencil_image_view.into(); + + #[allow(deprecated)] + self.set_attach_depth_stencil_as(depth_stencil_image, depth_stencil_image_view) + .set_resolve_depth_stencil_as( + attachment_idx, + depth_stencil_image, + depth_stencil_image_view, + depth_mode, + stencil_mode, + ); + + self + } + /// See [multiview] pub fn set_multiview(&mut self, view_mask: u32, correlated_view_mask: u32) -> &mut Self { let cmd = self.cmd.cmd_mut(); @@ -730,8 +1135,8 @@ mod deprecated { crate::{ Attachment, ClearColorValue, SubresourceAccess, cmd_ref::{ - AttachmentIndex, Descriptor, PipelineCommandRef, Resources, SubresourceRange, View, - ViewInfo, graphic::GraphicCommandBufferRef, + AttachmentIndex, Descriptor, PipelineCommandRef, SubresourceRange, View, ViewInfo, + graphic::GraphicCommandBufferRef, }, driver::{ graphic::GraphicPipeline, @@ -747,9 +1152,9 @@ mod deprecated { vk_sync::AccessType, }; - // Attachment functions + // Attachment functions from previous version impl PipelineCommandRef<'_, GraphicPipeline> { - #[deprecated] + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] pub fn attach_color( self, @@ -764,7 +1169,7 @@ mod deprecated { self.attach_color_as(attachment_idx, image, image_view_info) } - #[deprecated] + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] pub fn attach_color_as( mut self, @@ -772,6 +1177,330 @@ mod deprecated { image: impl Into, image_view_info: impl Into, ) -> Self { + #[allow(deprecated)] + self.set_attach_color_as(attachment_idx, image, image_view_info); + + self + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn attach_depth_stencil(self, image: impl Into) -> Self { + let image = image.into(); + let image_view_info = self.resource(image).info; + + #[allow(deprecated)] + self.attach_depth_stencil_as(image, image_view_info) + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn attach_depth_stencil_as( + mut self, + image: impl Into, + image_view_info: impl Into, + ) -> Self { + #[allow(deprecated)] + self.set_attach_depth_stencil_as(image, image_view_info); + + self + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn clear_color( + self, + attachment_idx: AttachmentIndex, + image: impl Into, + ) -> Self { + #[allow(deprecated)] + self.clear_color_value(attachment_idx, image, [0.0, 0.0, 0.0, 0.0]) + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn clear_color_value( + self, + attachment_idx: AttachmentIndex, + image: impl Into, + color: impl Into, + ) -> Self { + let image = image.into(); + let image_info = self.resource(image).info; + let image_view_info: ImageViewInfo = image_info.into(); + + #[allow(deprecated)] + self.clear_color_value_as(attachment_idx, image, color, image_view_info) + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn clear_color_value_as( + mut self, + attachment_idx: AttachmentIndex, + image: impl Into, + color: impl Into, + image_view_info: impl Into, + ) -> Self { + #[allow(deprecated)] + self.set_clear_color_value_as(attachment_idx, image, color, image_view_info); + + self + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn clear_depth_stencil(self, image: impl Into) -> Self { + #[allow(deprecated)] + self.clear_depth_stencil_value(image, 1.0, 0) + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn clear_depth_stencil_value( + self, + image: impl Into, + depth: f32, + stencil: u32, + ) -> Self { + let image = image.into(); + let image_info = self.resource(image).info; + let image_view_info: ImageViewInfo = image_info.into(); + + #[allow(deprecated)] + self.clear_depth_stencil_value_as(image, depth, stencil, image_view_info) + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn clear_depth_stencil_value_as( + mut self, + image: impl Into, + depth: f32, + stencil: u32, + image_view_info: impl Into, + ) -> Self { + #[allow(deprecated)] + self.set_clear_depth_stencil_value_as(image, depth, stencil, image_view_info); + + self + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn load_color( + self, + attachment_idx: AttachmentIndex, + image: impl Into, + ) -> Self { + let image = image.into(); + let image_info = self.resource(image).info; + + // Use the plain node information as the whole view of the node + let image_view_info = image_info; + + #[allow(deprecated)] + self.load_color_as(attachment_idx, image, image_view_info) + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn load_color_as( + mut self, + attachment_idx: AttachmentIndex, + image: impl Into, + image_view_info: impl Into, + ) -> Self { + #[allow(deprecated)] + self.set_load_color_as(attachment_idx, image, image_view_info); + + self + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn load_depth_stencil(self, image: impl Into) -> Self { + let image = image.into(); + let image_view_info = self.resource(image).info; + + #[allow(deprecated)] + self.load_depth_stencil_as(image, image_view_info) + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn load_depth_stencil_as( + mut self, + image: impl Into, + image_view_info: impl Into, + ) -> Self { + #[allow(deprecated)] + self.set_load_depth_stencil_as(image, image_view_info); + + self + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn store_color( + self, + attachment_idx: AttachmentIndex, + image: impl Into, + ) -> Self { + let image = image.into(); + let image_view_info = self.resource(image).info; + + #[allow(deprecated)] + self.store_color_as(attachment_idx, image, image_view_info) + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn store_color_as( + mut self, + attachment_idx: AttachmentIndex, + image: impl Into, + image_view_info: impl Into, + ) -> Self { + #[allow(deprecated)] + self.set_store_color_as(attachment_idx, image, image_view_info); + + self + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn store_depth_stencil(self, image: impl Into) -> Self { + let image = image.into(); + let image_view_info = self.resource(image).info; + + #[allow(deprecated)] + self.store_depth_stencil_as(image, image_view_info) + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn store_depth_stencil_as( + mut self, + image: impl Into, + image_view_info: impl Into, + ) -> Self { + #[allow(deprecated)] + self.set_store_depth_stencil_as(image, image_view_info); + + self + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn resolve_color( + self, + src_attachment_idx: AttachmentIndex, + dst_attachment_idx: AttachmentIndex, + image: impl Into, + ) -> Self { + let image = image.into(); + let image_view_info = self.resource(image).info; + + #[allow(deprecated)] + self.resolve_color_as( + src_attachment_idx, + dst_attachment_idx, + image, + image_view_info, + ) + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn resolve_color_as( + mut self, + src_attachment_idx: AttachmentIndex, + dst_attachment_idx: AttachmentIndex, + image: impl Into, + image_view_info: impl Into, + ) -> Self { + #[allow(deprecated)] + self.set_resolve_color_as( + src_attachment_idx, + dst_attachment_idx, + image, + image_view_info, + ); + + self + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn resolve_depth_stencil( + self, + dst_attachment_idx: AttachmentIndex, + image: impl Into, + depth_mode: Option, + stencil_mode: Option, + ) -> Self { + let image = image.into(); + let image_view_info = self.resource(image).info; + + #[allow(deprecated)] + self.resolve_depth_stencil_as( + dst_attachment_idx, + image, + image_view_info, + depth_mode, + stencil_mode, + ) + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn resolve_depth_stencil_as( + mut self, + dst_attachment_idx: AttachmentIndex, + image: impl Into, + image_view_info: impl Into, + depth_mode: Option, + stencil_mode: Option, + ) -> Self { + #[allow(deprecated)] + self.set_resolve_depth_stencil_as( + dst_attachment_idx, + image, + image_view_info, + depth_mode, + stencil_mode, + ); + + self + } + } + + // Attachment functions as setters + impl PipelineCommandRef<'_, GraphicPipeline> { + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn set_attach_color( + &mut self, + attachment_idx: AttachmentIndex, + image: impl Into, + ) -> &mut Self { + let image = image.into(); + let image_info = self.resource(image).info; + let image_view_info: ImageViewInfo = image_info.into(); + + #[allow(deprecated)] + self.set_attach_color_as(attachment_idx, image, image_view_info) + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn set_attach_color_as( + &mut self, + attachment_idx: AttachmentIndex, + image: impl Into, + image_view_info: impl Into, + ) -> &mut Self { let image = image.into(); let image_view_info = image_view_info.into(); let node_idx = image.index(); @@ -863,23 +1592,23 @@ mod deprecated { self } - #[deprecated] + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] - pub fn attach_depth_stencil(self, image: impl Into) -> Self { + pub fn set_attach_depth_stencil(&mut self, image: impl Into) -> &mut Self { let image = image.into(); let image_view_info = self.resource(image).info; #[allow(deprecated)] - self.attach_depth_stencil_as(image, image_view_info) + self.set_attach_depth_stencil_as(image, image_view_info) } - #[deprecated] + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] - pub fn attach_depth_stencil_as( - mut self, + pub fn set_attach_depth_stencil_as( + &mut self, image: impl Into, image_view_info: impl Into, - ) -> Self { + ) -> &mut Self { let image = image.into(); let image_view_info = image_view_info.into(); let node_idx = image.index(); @@ -966,48 +1695,50 @@ mod deprecated { self } - #[deprecated] + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] - pub fn clear_color( - self, + pub fn set_clear_color( + &mut self, attachment_idx: AttachmentIndex, image: impl Into, - ) -> Self { + ) -> &mut Self { #[allow(deprecated)] - self.clear_color_value(attachment_idx, image, [0.0, 0.0, 0.0, 0.0]) + self.set_clear_color_value(attachment_idx, image, [0.0, 0.0, 0.0, 0.0]) } - #[deprecated] + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] - pub fn clear_color_value( - self, + pub fn set_clear_color_value( + &mut self, attachment_idx: AttachmentIndex, image: impl Into, color: impl Into, - ) -> Self { + ) -> &mut Self { let image = image.into(); let image_info = self.resource(image).info; let image_view_info: ImageViewInfo = image_info.into(); #[allow(deprecated)] - self.clear_color_value_as(attachment_idx, image, color, image_view_info) + self.set_clear_color_value_as(attachment_idx, image, color, image_view_info) } - #[deprecated] + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] - pub fn clear_color_value_as( - mut self, + pub fn set_clear_color_value_as( + &mut self, attachment_idx: AttachmentIndex, image: impl Into, color: impl Into, image_view_info: impl Into, - ) -> Self { + ) -> &mut Self { let image = image.into(); let image_view_info = image_view_info.into(); let node_idx = image.index(); let ImageInfo { sample_count, .. } = self.resource(image).info; let color = color.into(); + let color: vk::ClearColorValue = color.into(); + let color = unsafe { color.float32 }; debug_assert!( !self @@ -1136,38 +1867,38 @@ mod deprecated { self } - #[deprecated] + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] - pub fn clear_depth_stencil(self, image: impl Into) -> Self { + pub fn set_clear_depth_stencil(&mut self, image: impl Into) -> &mut Self { #[allow(deprecated)] - self.clear_depth_stencil_value(image, 1.0, 0) + self.set_clear_depth_stencil_value(image, 1.0, 0) } - #[deprecated] + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] - pub fn clear_depth_stencil_value( - self, + pub fn set_clear_depth_stencil_value( + &mut self, image: impl Into, depth: f32, stencil: u32, - ) -> Self { + ) -> &mut Self { let image = image.into(); let image_info = self.resource(image).info; let image_view_info: ImageViewInfo = image_info.into(); #[allow(deprecated)] - self.clear_depth_stencil_value_as(image, depth, stencil, image_view_info) + self.set_clear_depth_stencil_value_as(image, depth, stencil, image_view_info) } - #[deprecated] + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] - pub fn clear_depth_stencil_value_as( - mut self, + pub fn set_clear_depth_stencil_value_as( + &mut self, image: impl Into, depth: f32, stencil: u32, image_view_info: impl Into, - ) -> Self { + ) -> &mut Self { let image = image.into(); let image_view_info = image_view_info.into(); let node_idx = image.index(); @@ -1331,13 +2062,13 @@ mod deprecated { self } - #[deprecated] + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] - pub fn load_color( - self, + pub fn set_load_color( + &mut self, attachment_idx: AttachmentIndex, image: impl Into, - ) -> Self { + ) -> &mut Self { let image = image.into(); let image_info = self.resource(image).info; @@ -1345,17 +2076,17 @@ mod deprecated { let image_view_info = image_info; #[allow(deprecated)] - self.load_color_as(attachment_idx, image, image_view_info) + self.set_load_color_as(attachment_idx, image, image_view_info) } - #[deprecated] + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] - pub fn load_color_as( - mut self, + pub fn set_load_color_as( + &mut self, attachment_idx: AttachmentIndex, image: impl Into, image_view_info: impl Into, - ) -> Self { + ) -> &mut Self { let image = image.into(); let image_view_info = image_view_info.into(); let node_idx = image.index(); @@ -1485,23 +2216,23 @@ mod deprecated { self } - #[deprecated] + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] - pub fn load_depth_stencil(self, image: impl Into) -> Self { + pub fn set_load_depth_stencil(&mut self, image: impl Into) -> &mut Self { let image = image.into(); let image_view_info = self.resource(image).info; #[allow(deprecated)] - self.load_depth_stencil_as(image, image_view_info) + self.set_load_depth_stencil_as(image, image_view_info) } - #[deprecated] + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] - pub fn load_depth_stencil_as( - mut self, + pub fn set_load_depth_stencil_as( + &mut self, image: impl Into, image_view_info: impl Into, - ) -> Self { + ) -> &mut Self { let image = image.into(); let image_view_info = image_view_info.into(); let node_idx = image.index(); @@ -1612,28 +2343,28 @@ mod deprecated { self } - #[deprecated] + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] - pub fn store_color( - self, + pub fn set_store_color( + &mut self, attachment_idx: AttachmentIndex, image: impl Into, - ) -> Self { + ) -> &mut Self { let image = image.into(); let image_view_info = self.resource(image).info; #[allow(deprecated)] - self.store_color_as(attachment_idx, image, image_view_info) + self.set_store_color_as(attachment_idx, image, image_view_info) } - #[deprecated] + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] - pub fn store_color_as( - mut self, + pub fn set_store_color_as( + &mut self, attachment_idx: AttachmentIndex, image: impl Into, image_view_info: impl Into, - ) -> Self { + ) -> &mut Self { let image = image.into(); let image_view_info = image_view_info.into(); let node_idx = image.index(); @@ -1761,23 +2492,23 @@ mod deprecated { self } - #[deprecated] + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] - pub fn store_depth_stencil(self, image: impl Into) -> Self { + pub fn set_store_depth_stencil(&mut self, image: impl Into) -> &mut Self { let image = image.into(); let image_view_info = self.resource(image).info; #[allow(deprecated)] - self.store_depth_stencil_as(image, image_view_info) + self.set_store_depth_stencil_as(image, image_view_info) } - #[deprecated] + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] - pub fn store_depth_stencil_as( - mut self, + pub fn set_store_depth_stencil_as( + &mut self, image: impl Into, image_view_info: impl Into, - ) -> Self { + ) -> &mut Self { let image = image.into(); let image_view_info = image_view_info.into(); let node_idx = image.index(); @@ -1918,19 +2649,19 @@ mod deprecated { self } - #[deprecated] + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] - pub fn resolve_color( - self, + pub fn set_resolve_color( + &mut self, src_attachment_idx: AttachmentIndex, dst_attachment_idx: AttachmentIndex, image: impl Into, - ) -> Self { + ) -> &mut Self { let image = image.into(); let image_view_info = self.resource(image).info; #[allow(deprecated)] - self.resolve_color_as( + self.set_resolve_color_as( src_attachment_idx, dst_attachment_idx, image, @@ -1938,15 +2669,15 @@ mod deprecated { ) } - #[deprecated] + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] - pub fn resolve_color_as( - mut self, + pub fn set_resolve_color_as( + &mut self, src_attachment_idx: AttachmentIndex, dst_attachment_idx: AttachmentIndex, image: impl Into, image_view_info: impl Into, - ) -> Self { + ) -> &mut Self { let image = image.into(); let image_view_info = image_view_info.into(); let node_idx = image.index(); @@ -2077,20 +2808,20 @@ mod deprecated { self } - #[deprecated] + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] - pub fn resolve_depth_stencil( - self, + pub fn set_resolve_depth_stencil( + &mut self, dst_attachment_idx: AttachmentIndex, image: impl Into, depth_mode: Option, stencil_mode: Option, - ) -> Self { + ) -> &mut Self { let image = image.into(); let image_view_info = self.resource(image).info; #[allow(deprecated)] - self.resolve_depth_stencil_as( + self.set_resolve_depth_stencil_as( dst_attachment_idx, image, image_view_info, @@ -2099,16 +2830,16 @@ mod deprecated { ) } - #[deprecated] + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] - pub fn resolve_depth_stencil_as( - mut self, + pub fn set_resolve_depth_stencil_as( + &mut self, dst_attachment_idx: AttachmentIndex, image: impl Into, image_view_info: impl Into, depth_mode: Option, stencil_mode: Option, - ) -> Self { + ) -> &mut Self { let image = image.into(); let image_view_info = image_view_info.into(); let node_idx = image.index(); @@ -2265,9 +2996,11 @@ mod deprecated { #[doc(hidden)] pub fn record_subpass( self, - func: impl FnOnce(GraphicCommandBufferRef<'_>, Resources<'_>) + Send + 'static, + func: impl FnOnce(GraphicCommandBufferRef<'_>, ()) + Send + 'static, ) -> Self { - self.record_cmd_buf(func) + self.record_cmd_buf(|cmd_buf| { + func(cmd_buf, ()); + }) } #[deprecated = "use shader_resource_access function with AccessType::AnyShaderWrite"] diff --git a/src/cmd_ref/mod.rs b/src/cmd_ref/mod.rs index 055b0f8e..e7651429 100644 --- a/src/cmd_ref/mod.rs +++ b/src/cmd_ref/mod.rs @@ -12,7 +12,10 @@ pub use self::{ BuildAccelerationStructureIndirectInfo, BuildAccelerationStructureInfo, CommandBufferRef, UpdateAccelerationStructureIndirectInfo, UpdateAccelerationStructureInfo, }, + compute::ComputeCommandBufferRef, + graphic::{GraphicCommandBufferRef, LoadOp, StoreOp}, pipeline::PipelineCommandRef, + ray_trace::RayTraceCommandBufferRef, }; use { @@ -24,13 +27,11 @@ use { SwapchainImageNode, }, crate::driver::{ - accel_struct::{AccelerationStructure, AccelerationStructureRange}, - buffer::{Buffer, BufferSubresourceRange}, - cmd_buf::CommandBuffer, - image::{Image, ImageViewInfo}, + accel_struct::AccelerationStructureRange, buffer::BufferSubresourceRange, + image::ImageViewInfo, }, ash::vk, - std::ops::{Index, Range}, + std::ops::Range, vk_sync::AccessType, }; @@ -114,7 +115,7 @@ impl<'a> CommandRef<'a> { self.graph } - fn push_execute(&mut self, func: impl FnOnce(&CommandBuffer, Resources<'_>) + Send + 'static) { + fn push_execute(&mut self, func: impl FnOnce(CommandBufferRef) + Send + 'static) { let cmd = self.cmd_mut(); let exec = { let last_exec = cmd.execs.last_mut().unwrap(); @@ -162,10 +163,10 @@ impl<'a> CommandRef<'a> { /// code and interfaces. pub fn record_cmd_buf( mut self, - func: impl FnOnce(CommandBufferRef<'_>, Resources<'_>) + Send + 'static, + func: impl FnOnce(CommandBufferRef<'_>) + Send + 'static, ) -> Self { - self.push_execute(move |cmd_buf, resources| { - func(CommandBufferRef { cmd_buf, resources }, resources); + self.push_execute(move |cmd_buf| { + func(cmd_buf); }); self @@ -318,140 +319,6 @@ impl From<(DescriptorSetIndex, BindingIndex, [BindingOffset; 1])> for Descriptor } } -/// An indexable structure will provides access to Vulkan resources inside a command closure. -/// -/// This type is available while recording commands in the following closures: -/// -/// - [`PassRef::record_accel_struct`] for building and updating acceleration structures -/// - [`PassRef::record_cmd_buf`] for general command streams -/// - [`PipelineCommandRef::record_pipeline`] for dispatched compute operations -/// - [`PipelineCommandRef::record_pipeline`] for raster drawing operations, such as triangle streams -/// - [`PipelineCommandRef::record_pipeline`] for ray-traced operations -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```no_run -/// # use std::sync::Arc; -/// # use ash::vk; -/// # use vk_graph::driver::DriverError; -/// # use vk_graph::driver::device::{Device, DeviceInfo}; -/// # use vk_graph::driver::image::{Image, ImageInfo}; -/// # use vk_graph::Graph; -/// # use vk_graph::node::ImageNode; -/// # fn main() -> Result<(), DriverError> { -/// # let device = Device::new(DeviceInfo::default())?; -/// # let info = ImageInfo::image_2d(32, 32, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::SAMPLED); -/// # let image = Image::create(&device, info)?; -/// # let mut my_graph = Graph::default(); -/// # let my_image_node = my_graph.bind_resource(image); -/// my_graph -/// .begin_cmd() -/// .debug_name("custom vulkan commands") -/// .record_cmd_buf(move |cmd_buf, resources| { -/// let my_image: &Image = &resources[my_image_node]; -/// -/// assert_ne!(my_image.handle, vk::Image::null()); -/// assert_eq!(my_image.info.width, 32); -/// }); -/// # Ok(()) } -/// ``` -#[derive(Clone, Copy, Debug)] -pub struct Resources<'a> { - #[cfg(debug_assertions)] - exec: &'a Execution, - - resources: &'a [Resource], -} - -impl<'a> Resources<'a> { - pub(super) fn new( - resources: &'a [Resource], - #[cfg(debug_assertions)] exec: &'a Execution, - ) -> Self { - Self { - #[cfg(debug_assertions)] - exec, - resources, - } - } - - // TODO... - fn resource(&self, node_idx: usize) -> &Resource { - // You must have called read or write for this node on this execution before indexing - // into the bindings data! - // - // Why: Code that attempts to access this function is attempting to get access to the Vulkan - // resource (buffer, image, or acceleration structure). In order to access any resources the - // access type must first be specified so the correct barriers may be added. - debug_assert!( - self.exec.accesses.contains_key(&node_idx), - "unexpected node access: call access, read, or write first" - ); - - &self.resources[node_idx] - } -} - -macro_rules! index { - ($name:ident, $handle:ident) => { - paste::paste! { - impl<'a> Index<[<$name Node>]> for Resources<'a> - { - type Output = $handle; - - fn index(&self, node: [<$name Node>]) -> &Self::Output { - &*self.resource(node.idx).[]().unwrap() - } - } - } - }; -} - -// Allow indexing the Nodes data during command execution: -// (This gets you access to the driver images or other resources) -index!(AccelerationStructure, AccelerationStructure); -index!(AccelerationStructureLease, AccelerationStructure); -index!(Buffer, Buffer); -index!(BufferLease, Buffer); -index!(Image, Image); -index!(ImageLease, Image); -index!(SwapchainImage, Image); - -impl Index for Resources<'_> { - type Output = AccelerationStructure; - - fn index(&self, node: AnyAccelerationStructureNode) -> &Self::Output { - let node_idx = node.index(); - let resource = self.resource(node_idx); - - resource.as_driver_accel_struct().unwrap() - } -} - -impl Index for Resources<'_> { - type Output = Buffer; - - fn index(&self, node: AnyBufferNode) -> &Self::Output { - let node_idx = node.index(); - let resource = self.resource(node_idx); - - resource.as_driver_buffer().unwrap() - } -} - -impl Index for Resources<'_> { - type Output = Image; - - fn index(&self, node: AnyImageNode) -> &Self::Output { - let node_idx = node.index(); - let resource = self.resource(node_idx); - - resource.as_driver_image().unwrap() - } -} - #[derive(Clone, Copy, Debug)] #[doc(hidden)] pub enum SubresourceRange { @@ -669,7 +536,7 @@ impl From> for ViewInfo { mod deprecated { use { crate::{ - cmd_ref::{CommandBufferRef, CommandRef, Resources, SubresourceRange, View}, + cmd_ref::{CommandBufferRef, CommandRef, SubresourceRange, View}, deprecated::Info, node::Node, }, @@ -738,10 +605,10 @@ mod deprecated { #[doc(hidden)] pub fn record_accel_struct( mut self, - func: impl FnOnce(CommandBufferRef<'_>, Resources<'_>) + Send + 'static, + func: impl FnOnce(CommandBufferRef<'_>, ()) + Send + 'static, ) -> Self { - self.push_execute(move |cmd_buf, resources| { - func(CommandBufferRef { cmd_buf, resources }, resources); + self.push_execute(|cmd_buf| { + func(cmd_buf, ()); }); self diff --git a/src/cmd_ref/ray_trace.rs b/src/cmd_ref/ray_trace.rs index 95019978..6bcee8b9 100644 --- a/src/cmd_ref/ray_trace.rs +++ b/src/cmd_ref/ray_trace.rs @@ -1,5 +1,5 @@ use { - super::{PipelineCommandRef, Resources, cmd_buf::CommandBufferRef}, + super::{PipelineCommandRef, cmd_buf::CommandBufferRef}, crate::driver::{device::Device, ray_trace::RayTracePipeline}, ash::vk, log::trace, @@ -11,7 +11,7 @@ impl PipelineCommandRef<'_, RayTracePipeline> { /// Begin recording a ray trace pipeline command buffer. pub fn record_cmd_buf( mut self, - func: impl FnOnce(RayTraceCommandBufferRef<'_>, Resources<'_>) + Send + 'static, + func: impl FnOnce(RayTraceCommandBufferRef<'_>) + Send + 'static, ) -> Self { let pipeline = self .cmd @@ -28,18 +28,15 @@ impl PipelineCommandRef<'_, RayTracePipeline> { #[cfg(debug_assertions)] let dynamic_stack_size = pipeline.inner.info.dynamic_stack_size; - self.cmd.push_execute(move |cmd_buf, resources| { - func( - RayTraceCommandBufferRef { - cmd_buf: CommandBufferRef { cmd_buf, resources }, + self.cmd.push_execute(move |cmd_buf| { + func(RayTraceCommandBufferRef { + cmd_buf, - #[cfg(debug_assertions)] - dynamic_stack_size, + #[cfg(debug_assertions)] + dynamic_stack_size, - pipeline, - }, - resources, - ); + pipeline, + }); }); self @@ -76,7 +73,7 @@ impl PipelineCommandRef<'_, RayTracePipeline> { /// my_graph.begin_cmd() /// .debug_name("my ray trace pass") /// .bind_pipeline(&my_ray_trace_pipeline) -/// .record_cmd_buf(move |cmd_buf, nodes| { +/// .record_cmd_buf(move |cmd_buf| { /// // During this closure we have access to the ray trace methods! /// }); /// # Ok(()) } @@ -151,7 +148,7 @@ impl RayTraceCommandBufferRef<'_> { /// my_graph.begin_cmd() /// .debug_name("draw a cornell box") /// .bind_pipeline(&my_ray_trace_pipeline) - /// .record_cmd_buf(move |cmd_buf, nodes| { + /// .record_cmd_buf(move |cmd_buf| { /// cmd_buf.push_constants(0, &[0xcb]) /// .trace_rays(&rgen_sbt, &hit_sbt, &miss_sbt, &call_sbt, 320, 200, 1); /// }); @@ -244,7 +241,7 @@ impl RayTraceCommandBufferRef<'_> { /// my_graph.begin_cmd() /// .debug_name("draw a cornell box") /// .bind_pipeline(&my_ray_trace_pipeline) - /// .record_cmd_buf(move |cmd_buf, nodes| { + /// .record_cmd_buf(move |cmd_buf| { /// cmd_buf.trace_rays(&rgen_sbt, &hit_sbt, &miss_sbt, &call_sbt, 320, 200, 1); /// }); /// # Ok(()) } @@ -329,7 +326,7 @@ mod deprecated { use { crate::{ cmd_ref::{ - Descriptor, PipelineCommandRef, Resources, SubresourceRange, View, ViewInfo, + Descriptor, PipelineCommandRef, SubresourceRange, View, ViewInfo, ray_trace::RayTraceCommandBufferRef, }, driver::ray_trace::RayTracePipeline, @@ -381,9 +378,11 @@ mod deprecated { #[doc(hidden)] pub fn record_ray_trace( self, - func: impl FnOnce(RayTraceCommandBufferRef<'_>, Resources<'_>) + Send + 'static, + func: impl FnOnce(RayTraceCommandBufferRef<'_>, ()) + Send + 'static, ) -> Self { - self.record_cmd_buf(func) + self.record_cmd_buf(|cmd_buf| { + func(cmd_buf, ()); + }) } #[deprecated = "use shader_resource_access function with AccessType::AnyShaderWrite"] diff --git a/src/driver/accel_struct.rs b/src/driver/accel_struct.rs index 43469dad..8f1a34ef 100644 --- a/src/driver/accel_struct.rs +++ b/src/driver/accel_struct.rs @@ -828,7 +828,7 @@ mod test { #[test] pub fn accel_struct_info() { let info = Info::blas(32); - let builder = info.to_builder().build(); + let builder = info.into_builder().build(); assert_eq!(info, builder); } diff --git a/src/driver/buffer.rs b/src/driver/buffer.rs index 08fed737..8da4a87f 100644 --- a/src/driver/buffer.rs +++ b/src/driver/buffer.rs @@ -1307,7 +1307,7 @@ mod test { #[test] pub fn buffer_info() { let info = Info::device_mem(0, vk::BufferUsageFlags::empty()); - let builder = info.to_builder().build(); + let builder = info.into_builder().build(); assert_eq!(info, builder); } diff --git a/src/driver/compute.rs b/src/driver/compute.rs index b622814c..d75758f8 100644 --- a/src/driver/compute.rs +++ b/src/driver/compute.rs @@ -320,7 +320,7 @@ mod test { #[test] pub fn compute_pipeline_info() { let info = Info::default(); - let builder = info.to_builder().build(); + let builder = info.into_builder().build(); assert_eq!(info, builder); } diff --git a/src/driver/device.rs b/src/driver/device.rs index c053e0b8..f85f5cfa 100644 --- a/src/driver/device.rs +++ b/src/driver/device.rs @@ -590,7 +590,7 @@ mod test { #[test] pub fn device_info() { - Info::default().to_builder().build(); + Info::default().into_builder().build(); } #[test] diff --git a/src/driver/graphic.rs b/src/driver/graphic.rs index 2d0c5cf0..0011bacd 100644 --- a/src/driver/graphic.rs +++ b/src/driver/graphic.rs @@ -174,6 +174,7 @@ impl BlendInfoBuilder { } } +// TODO: This could be simplified (bounds_test controsl min/max etc) /// Specifies the [depth bounds tests], [stencil test], and [depth test] pipeline state. /// /// [depth bounds tests]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fragops-dbt @@ -245,20 +246,7 @@ pub struct DepthStencilInfo { impl DepthStencilInfo { /// A commonly used depth/stencil mode - pub const DEPTH_READ: Self = Self { - back: StencilMode::IGNORE, - bounds_test: true, - compare_op: vk::CompareOp::LESS, - depth_test: true, - depth_write: false, - front: StencilMode::IGNORE, - min: OrderedFloat(0.0), - max: OrderedFloat(1.0), - stencil_test: false, - }; - - /// A commonly used depth/stencil mode - pub const DEPTH_WRITE: Self = Self { + pub const DEPTH_WRITE_LESS_IGNORE_STENCIL: Self = Self { back: StencilMode::IGNORE, bounds_test: true, compare_op: vk::CompareOp::LESS, @@ -271,6 +259,8 @@ impl DepthStencilInfo { }; /// Specifies a no-depth/no-stencil mode. + /// + /// This is the default state. pub const IGNORE: Self = Self { back: StencilMode::IGNORE, bounds_test: false, @@ -865,7 +855,11 @@ pub(crate) struct VertexInputState { } mod deprecated { - use crate::driver::graphic::{BlendInfo, BlendInfoBuilder, DepthStencilInfo}; + use { + crate::driver::graphic::{BlendInfo, BlendInfoBuilder, DepthStencilInfo, StencilMode}, + ash::vk, + ordered_float::OrderedFloat, + }; impl BlendInfo { #[allow(clippy::new_ret_no_self)] @@ -877,6 +871,34 @@ mod deprecated { } impl DepthStencilInfo { + /// A commonly used depth/stencil mode + #[deprecated = "use constructor or builder function"] + pub const DEPTH_READ: Self = Self { + back: StencilMode::IGNORE, + bounds_test: true, + compare_op: vk::CompareOp::LESS, + depth_test: true, + depth_write: false, + front: StencilMode::IGNORE, + min: OrderedFloat(0.0), + max: OrderedFloat(1.0), + stencil_test: false, + }; + + /// A commonly used depth/stencil mode + #[deprecated = "use DEPTH_WRITE_LESS_IGNORE_STENCIL"] + pub const DEPTH_WRITE: Self = Self { + back: StencilMode::IGNORE, + bounds_test: true, + compare_op: vk::CompareOp::LESS, + depth_test: true, + depth_write: true, + front: StencilMode::IGNORE, + min: OrderedFloat(0.0), + max: OrderedFloat(1.0), + stencil_test: false, + }; + #[allow(clippy::new_ret_no_self)] #[deprecated = "use builder function or DepthStencilInfoBuilder"] #[doc(hidden)] diff --git a/src/driver/image.rs b/src/driver/image.rs index e86e06c8..15143756 100644 --- a/src/driver/image.rs +++ b/src/driver/image.rs @@ -956,7 +956,7 @@ impl ImageInfoBuilder { /// Provides an `ImageViewInfo` for this format, type, aspect, array elements, and mip levels. pub fn into_image_view(self) -> ImageViewInfoBuilder { - self.build().into_image_view().to_builder() + self.build().into_image_view().into_builder() } } @@ -2082,7 +2082,7 @@ mod test { #[test] pub fn image_info_cube() { let info = ImageInfo::cube(42, vk::Format::R32_SFLOAT, vk::ImageUsageFlags::empty()); - let builder = info.to_builder().build(); + let builder = info.into_builder().build(); assert_eq!(info, builder); } @@ -2106,7 +2106,7 @@ mod test { #[test] pub fn image_info_image_1d() { let info = ImageInfo::image_1d(42, vk::Format::R32_SFLOAT, vk::ImageUsageFlags::empty()); - let builder = info.to_builder().build(); + let builder = info.into_builder().build(); assert_eq!(info, builder); } @@ -2129,7 +2129,7 @@ mod test { pub fn image_info_image_2d() { let info = ImageInfo::image_2d(42, 84, vk::Format::R32_SFLOAT, vk::ImageUsageFlags::empty()); - let builder = info.to_builder().build(); + let builder = info.into_builder().build(); assert_eq!(info, builder); } @@ -2158,7 +2158,7 @@ mod test { vk::Format::default(), vk::ImageUsageFlags::empty(), ); - let builder = info.to_builder().build(); + let builder = info.into_builder().build(); assert_eq!(info, builder); } @@ -2193,7 +2193,7 @@ mod test { vk::Format::R32_SFLOAT, vk::ImageUsageFlags::empty(), ); - let builder = info.to_builder().build(); + let builder = info.into_builder().build(); assert_eq!(info, builder); } @@ -2266,7 +2266,7 @@ mod test { mip_level_count: u32, ) -> ImageInfo { ImageInfo::image_2d(1, 1, fmt, vk::ImageUsageFlags::empty()) - .to_builder() + .into_builder() .array_layer_count(array_layer_count) .mip_level_count(mip_level_count) .build() @@ -2340,7 +2340,7 @@ mod test { #[test] pub fn image_view_info() { let info = ImageViewInfo::new(vk::Format::default(), vk::ImageViewType::TYPE_1D); - let builder = info.to_builder().build(); + let builder = info.into_builder().build(); assert_eq!(info, builder); } diff --git a/src/driver/ray_trace.rs b/src/driver/ray_trace.rs index dc90fb92..9c038817 100644 --- a/src/driver/ray_trace.rs +++ b/src/driver/ray_trace.rs @@ -652,7 +652,7 @@ mod test { #[test] pub fn ray_trace_pipeline_info() { let info = Info::default(); - let builder = info.to_builder().build(); + let builder = info.into_builder().build(); assert_eq!(info, builder); } diff --git a/src/driver/shader.rs b/src/driver/shader.rs index 4bb02a3e..0502461f 100644 --- a/src/driver/shader.rs +++ b/src/driver/shader.rs @@ -573,7 +573,7 @@ impl SamplerInfo { #[deprecated = "Use SamplerInfo::default()"] #[doc(hidden)] pub fn new() -> SamplerInfoBuilder { - Self::default().to_builder() + Self::default().into_builder() } /// Converts a `SamplerInfo` into a `SamplerInfoBuilder`. @@ -1554,7 +1554,7 @@ mod test { #[test] pub fn sampler_info() { let info = Info::default(); - let builder = info.to_builder().build(); + let builder = info.into_builder().build(); assert_eq!(info, builder); } diff --git a/src/driver/swapchain.rs b/src/driver/swapchain.rs index 60c41b50..b4c59da3 100644 --- a/src/driver/swapchain.rs +++ b/src/driver/swapchain.rs @@ -651,7 +651,7 @@ mod test { #[test] pub fn swapchain_info() { let info = Info::new(20, 24, vk::SurfaceFormatKHR::default()); - let builder = info.to_builder().build(); + let builder = info.into_builder().build(); assert_eq!(info, builder); } diff --git a/src/lib.rs b/src/lib.rs index f2de61f4..00b5ebc1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -218,7 +218,8 @@ Example: ```no_run # use std::sync::Arc; # use ash::vk; -# use vk_graph::driver::{AccessType, DriverError}; +# use vk_graph::driver::DriverError; +# use vk_graph::driver::sync::AccessType; # use vk_graph::driver::device::{Device, DeviceInfo}; # use vk_graph::driver::buffer::{Buffer, BufferInfo}; # use vk_graph::driver::image::{Image, ImageInfo}; @@ -238,21 +239,21 @@ let image: ImageNode = graph.bind_resource(image); graph .begin_cmd() .debug_name("Do some raw Vulkan or interop with another Vulkan library") - .record_cmd_buf(|cmd_buf, nodes| { + .record_cmd_buf(|cmd_buf| { // I always run first! }) .resource_access(buffer, AccessType::HostRead) .resource_access(image, AccessType::HostWrite) - .record_cmd_buf(move |cmd_buf, nodes| { - // Raw ash types are available + .record_cmd_buf(move |cmd_buf| { + // cmd_buf allows you to borrow the Vulkan resources + let buffer: vk::Buffer = cmd_buf.resource(buffer).handle; + let image: vk::Image = cmd_buf.resource(image).handle; + + // Raw ash types are also available let device: &ash::Device = &cmd_buf.device; let cmd_buf: vk::CommandBuffer = cmd_buf.handle; - // nodes is a magical object you can retrieve the Vulkan resource from - let buffer: vk::Buffer = nodes[buffer].handle; - let image: vk::Image = nodes[image].handle; - - // You are free to READ vk_buffer and WRITE vk_image! + // You are free to READ buffer and WRITE image! }); # Ok(()) } ``` @@ -279,7 +280,7 @@ graph .begin_cmd() .debug_name("My compute pass") .bind_pipeline(&my_compute_pipeline) - .record_cmd_buf(|cmd_buf, _| { + .record_cmd_buf(|cmd_buf| { cmd_buf.push_constants(0, &42u32.to_ne_bytes()) .dispatch(128, 1, 1); }); @@ -358,6 +359,8 @@ pub mod pool; mod bind; mod queue; +use crate::cmd_ref::CommandBufferRef; + pub use self::{ bind::{BindGraph, Bound, Resource}, queue::Queue, @@ -365,9 +368,7 @@ pub use self::{ use { self::{ - cmd_ref::{ - AttachmentIndex, CommandRef, Descriptor, Resources, SubresourceAccess, ViewInfo, - }, + cmd_ref::{AttachmentIndex, CommandRef, Descriptor, SubresourceAccess, ViewInfo}, node::Node, node::{ AccelerationStructureLeaseNode, AccelerationStructureNode, @@ -377,7 +378,6 @@ use { }, crate::driver::{ DescriptorBindingMap, - cmd_buf::CommandBuffer, compute::ComputePipeline, format_aspect_mask, format_texel_block_extent, format_texel_block_size, graphic::{DepthStencilInfo, GraphicPipeline}, @@ -397,7 +397,7 @@ use { vk_sync::AccessType, }; -type ExecFn = Box) + Send>; +type ExecFn = Box; type NodeIndex = usize; #[derive(Clone, Copy, Debug)] @@ -449,7 +449,7 @@ impl Attachment { fn image_view_info(self, image_info: ImageInfo) -> ImageViewInfo { image_info - .to_builder() + .into_builder() .array_layer_count(self.array_layer_count) .mip_level_count(self.mip_level_count) .fmt(self.format) @@ -461,37 +461,67 @@ impl Attachment { } } -/// Specifies a color attachment clear value which can be used to initliaze an image. +/// TODO #[derive(Clone, Copy, Debug)] -pub struct ClearColorValue(pub [f32; 4]); +pub enum ClearColorValue { + /// Value as [f32]. + Float32([f32; 4]), + + /// Value as [i32]. + Int32([i32; 4]), + + /// Value as [u32]. + Uint32([u32; 4]), +} + +impl From<[f32; 4]> for ClearColorValue { + fn from(float32: [f32; 4]) -> Self { + Self::Float32(float32) + } +} impl From<[f32; 3]> for ClearColorValue { - fn from(color: [f32; 3]) -> Self { - [color[0], color[1], color[2], 1.0].into() + fn from(float32: [f32; 3]) -> Self { + Self::from([float32[0], float32[1], float32[2], 1.0]) } } -impl From<[f32; 4]> for ClearColorValue { - fn from(color: [f32; 4]) -> Self { - Self(color) +impl From<[i32; 4]> for ClearColorValue { + fn from(int32: [i32; 4]) -> Self { + Self::Int32(int32) + } +} + +impl From<[u8; 4]> for ClearColorValue { + fn from(uint8: [u8; 4]) -> Self { + Self::from([ + uint8[0] as f32 / u8::MAX as f32, + uint8[1] as f32 / u8::MAX as f32, + uint8[2] as f32 / u8::MAX as f32, + uint8[3] as f32 / u8::MAX as f32, + ]) } } impl From<[u8; 3]> for ClearColorValue { - fn from(color: [u8; 3]) -> Self { - [color[0], color[1], color[2], u8::MAX].into() + fn from(uint8: [u8; 3]) -> Self { + Self::from([uint8[0], uint8[1], uint8[2], u8::MAX]) } } -impl From<[u8; 4]> for ClearColorValue { - fn from(color: [u8; 4]) -> Self { - [ - color[0] as f32 / u8::MAX as f32, - color[1] as f32 / u8::MAX as f32, - color[2] as f32 / u8::MAX as f32, - color[3] as f32 / u8::MAX as f32, - ] - .into() +impl From<[u32; 4]> for ClearColorValue { + fn from(uint32: [u32; 4]) -> Self { + Self::Uint32(uint32) + } +} + +impl From for vk::ClearColorValue { + fn from(value: ClearColorValue) -> Self { + match value { + ClearColorValue::Float32(float32) => Self { float32 }, + ClearColorValue::Int32(int32) => Self { int32 }, + ClearColorValue::Uint32(uint32) => Self { uint32 }, + } } } @@ -506,7 +536,7 @@ struct Execution { view_mask: u32, color_attachments: HashMap, - color_clears: HashMap, + color_clears: HashMap, color_loads: HashMap, color_resolves: HashMap, color_stores: HashMap, @@ -643,6 +673,11 @@ pub struct Graph { } impl Graph { + /// Constructs a default `Graph`. + pub fn new() -> Self { + Self::default() + } + /// Allocates and begins writing a new command. pub fn begin_cmd(&mut self) -> CommandRef<'_> { CommandRef::new(self) @@ -731,9 +766,9 @@ impl Graph { cmd.set_subresource_access(dst, dst_region, AccessType::TransferWrite); } - cmd.record_cmd_buf(move |cmd_buf, nodes| { - let src_image = nodes[src].handle; - let dst_image = nodes[dst].handle; + cmd.record_cmd_buf(move |cmd_buf| { + let src_image = cmd_buf.resource(src).handle; + let dst_image = cmd_buf.resource(dst).handle; unsafe { cmd_buf.device.cmd_blit_image( @@ -757,23 +792,25 @@ impl Graph { image: impl Into, color: impl Into, ) -> &mut Self { - let color = vk::ClearColorValue { - float32: color.into().0, - }; + let color = color.into().into(); let image = image.into(); let image_view = self.resource(image).info.into(); self.begin_cmd() .debug_name("clear color") .subresource_access(image, image_view, AccessType::TransferWrite) - .record_cmd_buf(move |cmd_buf, nodes| unsafe { - cmd_buf.device.cmd_clear_color_image( - cmd_buf.handle, - nodes[image].handle, - vk::ImageLayout::TRANSFER_DST_OPTIMAL, - &color, - &[image_view], - ); + .record_cmd_buf(move |cmd_buf| { + let image = cmd_buf.resource(image); + + unsafe { + cmd_buf.device.cmd_clear_color_image( + cmd_buf.handle, + image.handle, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + &color, + &[image_view], + ); + } }) .end_cmd() } @@ -792,14 +829,18 @@ impl Graph { self.begin_cmd() .debug_name("clear depth/stencil") .subresource_access(image, image_view, AccessType::TransferWrite) - .record_cmd_buf(move |cmd_buf, nodes| unsafe { - cmd_buf.device.cmd_clear_depth_stencil_image( - cmd_buf.handle, - nodes[image].handle, - vk::ImageLayout::TRANSFER_DST_OPTIMAL, - &vk::ClearDepthStencilValue { depth, stencil }, - &[image_view], - ); + .record_cmd_buf(move |cmd_buf| { + let image = cmd_buf.resource(image); + + unsafe { + cmd_buf.device.cmd_clear_depth_stencil_image( + cmd_buf.handle, + image.handle, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + &vk::ClearDepthStencilValue { depth, stencil }, + &[image_view], + ); + } }) .end_cmd() } @@ -872,14 +913,17 @@ impl Graph { ); } - cmd.record_cmd_buf(move |cmd_buf, nodes| { - let src = nodes[src].handle; - let dst = nodes[dst].handle; + cmd.record_cmd_buf(move |cmd_buf| { + let src = cmd_buf.resource(src); + let dst = cmd_buf.resource(dst); unsafe { - cmd_buf - .device - .cmd_copy_buffer(cmd_buf.handle, src, dst, regions.as_ref()); + cmd_buf.device.cmd_copy_buffer( + cmd_buf.handle, + src.handle, + dst.handle, + regions.as_ref(), + ); } }) .end_cmd() @@ -950,15 +994,15 @@ impl Graph { ); } - cmd.record_cmd_buf(move |cmd_buf, nodes| { - let src = nodes[src].handle; - let dst = nodes[dst].handle; + cmd.record_cmd_buf(move |cmd_buf| { + let src = cmd_buf.resource(src); + let dst = cmd_buf.resource(dst); unsafe { cmd_buf.device.cmd_copy_buffer_to_image( cmd_buf.handle, - src, - dst, + src.handle, + dst.handle, vk::ImageLayout::TRANSFER_DST_OPTIMAL, regions.as_ref(), ); @@ -1032,16 +1076,16 @@ impl Graph { ); } - cmd.record_cmd_buf(move |cmd_buf, nodes| { - let src = nodes[src].handle; - let dst = nodes[dst].handle; + cmd.record_cmd_buf(move |cmd_buf| { + let src = cmd_buf.resource(src); + let dst = cmd_buf.resource(dst); unsafe { cmd_buf.device.cmd_copy_image( cmd_buf.handle, - src, + src.handle, vk::ImageLayout::TRANSFER_SRC_OPTIMAL, - dst, + dst.handle, vk::ImageLayout::TRANSFER_DST_OPTIMAL, regions.as_ref(), ); @@ -1127,16 +1171,16 @@ impl Graph { ); } - cmd.record_cmd_buf(move |cmd_buf, nodes| { - let src = nodes[src].handle; - let dst = nodes[dst].handle; + cmd.record_cmd_buf(move |cmd_buf| { + let src = cmd_buf.resource(src); + let dst = cmd_buf.resource(dst); unsafe { cmd_buf.device.cmd_copy_image_to_buffer( cmd_buf.handle, - src, + src.handle, vk::ImageLayout::TRANSFER_SRC_OPTIMAL, - dst, + dst.handle, regions.as_ref(), ); } @@ -1156,13 +1200,13 @@ impl Graph { self.begin_cmd() .debug_name("fill buffer") .subresource_access(buffer, region.clone(), AccessType::TransferWrite) - .record_cmd_buf(move |cmd_buf, nodes| { - let buffer = nodes[buffer].handle; + .record_cmd_buf(move |cmd_buf| { + let buffer = cmd_buf.resource(buffer); unsafe { cmd_buf.device.cmd_fill_buffer( cmd_buf.handle, - buffer, + buffer.handle, region.start, region.end - region.start, data, @@ -1194,7 +1238,7 @@ impl Graph { where N: Bound, { - node.borrow(self) + node.borrow(&self.resources) } /// Finalizes the graph and provides an object with functions for submitting the resulting @@ -1234,13 +1278,16 @@ impl Graph { self.begin_cmd() .debug_name("update buffer") .subresource_access(buffer, offset..data_end, AccessType::TransferWrite) - .record_cmd_buf(move |cmd_buf, nodes| { - let buffer = nodes[buffer].handle; + .record_cmd_buf(move |cmd_buf| { + let buffer = cmd_buf.resource(buffer); unsafe { - cmd_buf - .device - .cmd_update_buffer(cmd_buf.handle, buffer, offset, data.as_ref()); + cmd_buf.device.cmd_update_buffer( + cmd_buf.handle, + buffer.handle, + offset, + data.as_ref(), + ); } }) .end_cmd() @@ -1265,12 +1312,6 @@ pub(crate) mod deprecated { }; impl Graph { - #[deprecated = "use default function instead"] - #[doc(hidden)] - pub fn new() -> Self { - Default::default() - } - #[deprecated = "use device_address function of resource function result"] #[doc(hidden)] pub fn node_device_address(&self, node: impl Node) -> vk::DeviceAddress { diff --git a/src/pool/mod.rs b/src/pool/mod.rs index 80f919e7..f2137837 100644 --- a/src/pool/mod.rs +++ b/src/pool/mod.rs @@ -419,7 +419,7 @@ mod test { #[test] pub fn pool_info() { let info = Info::default(); - let builder = info.to_builder().build(); + let builder = info.into_builder().build(); assert_eq!(info, builder); } diff --git a/src/queue.rs b/src/queue.rs index c1246e49..5112a965 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -1,10 +1,11 @@ use { super::{ Attachment, Command, ExecutionPipeline, Graph, Node, NodeIndex, Resource, - cmd_ref::{Resources, SubresourceAccess, SubresourceRange}, + cmd_ref::{SubresourceAccess, SubresourceRange}, }, crate::{ Bound, + cmd_ref::CommandBufferRef, driver::{ AttachmentInfo, AttachmentRef, Descriptor, DescriptorInfo, DescriptorSet, DriverError, FramebufferAttachmentImageInfo, FramebufferInfo, SubpassDependency, SubpassInfo, @@ -441,7 +442,7 @@ impl Queue { { clear_values[*attachment_idx as usize] = vk::ClearValue { color: vk::ClearColorValue { - float32: clear_value.0, + float32: *clear_value, }, }; @@ -2476,14 +2477,12 @@ impl Queue { profiling::scope!("Execute callback"); let exec_func = exec.func.take().unwrap().0; - exec_func( + exec_func(CommandBufferRef::new( cmd_buf, - Resources::new( - &self.graph.resources, - #[cfg(debug_assertions)] - exec, - ), - ); + &self.graph.resources, + #[cfg(debug_assertions)] + exec, + )); } } @@ -3152,7 +3151,7 @@ impl Queue { let image = image_binding.as_driver_image().unwrap(); let image_view_info = attachment .image_view_info(image.info) - .to_builder() + .into_builder() .array_layer_count(image_range.layer_count) .base_array_layer(image_range.base_array_layer) .base_mip_level(image_range.base_mip_level) From cd35779f0403e73a6def48abcb6b6ec62821cfd5 Mon Sep 17 00:00:00 2001 From: John Wells Date: Sat, 28 Feb 2026 08:54:12 -0500 Subject: [PATCH 28/86] clean-up --- contrib/vk-graph-egui/src/lib.rs | 8 +- contrib/vk-graph-fx/src/bitmap_font.rs | 10 +- contrib/vk-graph-fx/src/image_loader.rs | 4 +- contrib/vk-graph-fx/src/transition.rs | 2 +- contrib/vk-graph-imgui/src/lib.rs | 10 +- contrib/vk-graph-prelude/src/lib.rs | 7 +- examples/aliasing.rs | 10 +- examples/cpu_readback.rs | 4 +- examples/egui.rs | 2 +- examples/font_bmp.rs | 2 +- examples/fuzzer.rs | 132 +++++++---- examples/image_sampler.rs | 2 +- examples/imgui.rs | 2 +- examples/msaa.rs | 10 +- examples/multipass.rs | 6 +- examples/multithread.rs | 2 +- examples/ray_omni.rs | 9 +- examples/ray_trace.rs | 4 +- examples/shader-toy/src/main.rs | 6 +- examples/skeletal-anim/src/main.rs | 6 +- examples/vr/src/main.rs | 6 +- examples/vsm_omni.rs | 10 +- src/cmd_ref/bind.rs | 5 +- src/cmd_ref/cmd_buf.rs | 8 +- src/cmd_ref/compute.rs | 11 +- src/cmd_ref/graphic.rs | 297 +++++++++++++----------- src/cmd_ref/mod.rs | 88 +++---- src/cmd_ref/pipeline.rs | 58 ++--- src/cmd_ref/ray_trace.rs | 3 +- src/driver/buffer.rs | 15 +- src/driver/instance.rs | 162 ++++++++++--- src/lib.rs | 8 +- src/pool/alias.rs | 57 +++-- src/pool/fifo.rs | 18 +- src/pool/hash.rs | 14 +- src/pool/lazy.rs | 18 +- src/pool/mod.rs | 14 +- src/queue.rs | 8 +- 38 files changed, 608 insertions(+), 430 deletions(-) diff --git a/contrib/vk-graph-egui/src/lib.rs b/contrib/vk-graph-egui/src/lib.rs index 2a537a10..fcd7fa0e 100644 --- a/contrib/vk-graph-egui/src/lib.rs +++ b/contrib/vk-graph-egui/src/lib.rs @@ -108,7 +108,7 @@ impl Egui { let tmp_buf = { let mut buf = self .cache - .lease(BufferInfo::host_mem( + .lease_resource(BufferInfo::host_mem( (pixels.len() * delta.image.bytes_per_pixel()) as u64, vk::BufferUsageFlags::TRANSFER_SRC, )) @@ -153,7 +153,7 @@ impl Egui { } else { let image = graph.bind_resource( self.cache - .lease(ImageInfo::image_2d( + .lease_resource(ImageInfo::image_2d( delta.image.width() as u32, delta.image.height() as u32, vk::Format::R8G8B8A8_UNORM, @@ -230,7 +230,7 @@ impl Egui { let idx_buf = { let mut buf = self .cache - .lease(BufferInfo::host_mem( + .lease_resource(BufferInfo::host_mem( (mesh.indices.len() * 4) as u64, vk::BufferUsageFlags::INDEX_BUFFER, )) @@ -243,7 +243,7 @@ impl Egui { let vert_buf = { let mut buf = self .cache - .lease(BufferInfo::host_mem( + .lease_resource(BufferInfo::host_mem( (mesh.vertices.len() * std::mem::size_of::()) as u64, vk::BufferUsageFlags::VERTEX_BUFFER, diff --git a/contrib/vk-graph-fx/src/bitmap_font.rs b/contrib/vk-graph-fx/src/bitmap_font.rs index 3c4d64fc..15becde7 100644 --- a/contrib/vk-graph-fx/src/bitmap_font.rs +++ b/contrib/vk-graph-fx/src/bitmap_font.rs @@ -22,10 +22,10 @@ fn color_to_unorm(color: Color) -> [u8; 16] { /// Holds a decoded bitmap Font. #[derive(Debug)] pub struct BitmapFont { - cache: HashPool, font: BMFont, pages: Vec>, pipeline: GraphicPipeline, + pool: LazyPool, } impl BitmapFont { @@ -35,7 +35,7 @@ impl BitmapFont { font: BMFont, pages: impl Into>>, ) -> anyhow::Result { - let cache = HashPool::new(device); + let pool = LazyPool::new(device); let pages = pages.into(); let num_pages = pages.len() as u32; let pipeline = GraphicPipeline::create( @@ -52,10 +52,10 @@ impl BitmapFont { .context("Unable to create bitmap font pipeline")?; Ok(Self { - cache, font, pages, pipeline, + pool, }) } @@ -155,8 +155,8 @@ impl BitmapFont { let vertex_buf_len = 120 * text.chars().count() as vk::DeviceSize; let mut vertex_buf = self - .cache - .lease(BufferInfo::host_mem( + .pool + .lease_resource(BufferInfo::host_mem( vertex_buf_len, vk::BufferUsageFlags::VERTEX_BUFFER, )) diff --git a/contrib/vk-graph-fx/src/image_loader.rs b/contrib/vk-graph-fx/src/image_loader.rs index 6e9098b1..af340b2f 100644 --- a/contrib/vk-graph-fx/src/image_loader.rs +++ b/contrib/vk-graph-fx/src/image_loader.rs @@ -168,7 +168,7 @@ impl ImageLoader { //trace!("pixel_buf_len={pixel_buf_len} pixel_buf_stride={pixel_buf_stride}"); // Lease a temporary buffer from the cache pool - let mut pixel_buf = self.pool.lease(BufferInfo::host_mem( + let mut pixel_buf = self.pool.lease_resource(BufferInfo::host_mem( pixel_buf_len, vk::BufferUsageFlags::STORAGE_BUFFER, ))?; @@ -218,7 +218,7 @@ impl ImageLoader { } ImageFormat::R8G8 | ImageFormat::R8G8B8A8 => { // Lease a temporary buffer from the pool - let mut pixel_buf = self.pool.lease(BufferInfo::host_mem( + let mut pixel_buf = self.pool.lease_resource(BufferInfo::host_mem( pixels.len() as _, vk::BufferUsageFlags::TRANSFER_SRC, ))?; diff --git a/contrib/vk-graph-fx/src/transition.rs b/contrib/vk-graph-fx/src/transition.rs index 6e3724b4..b4c46179 100644 --- a/contrib/vk-graph-fx/src/transition.rs +++ b/contrib/vk-graph-fx/src/transition.rs @@ -422,7 +422,7 @@ impl TransitionPipeline { | vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::TRANSFER_SRC, ); - let dest_image = graph.bind_resource(self.cache.lease(dest_info).unwrap()); + let dest_image = graph.bind_resource(self.cache.lease_resource(dest_info).unwrap()); self.apply_to(graph, a_image, b_image, dest_image, transition, progress); diff --git a/contrib/vk-graph-imgui/src/lib.rs b/contrib/vk-graph-imgui/src/lib.rs index a9685244..00c5ef85 100644 --- a/contrib/vk-graph-imgui/src/lib.rs +++ b/contrib/vk-graph-imgui/src/lib.rs @@ -99,7 +99,7 @@ impl ImGui { let image = graph.bind_resource({ let mut image = pool - .lease(ImageInfo::image_2d( + .lease_resource(ImageInfo::image_2d( window.inner_size().width, window.inner_size().height, vk::Format::R8G8B8A8_UNORM, @@ -127,7 +127,7 @@ impl ImGui { for draw_list in draw_data.draw_lists() { let indices = cast_slice(draw_list.idx_buffer()); let mut index_buf = pool - .lease(BufferInfo::host_mem( + .lease_resource(BufferInfo::host_mem( indices.len() as _, vk::BufferUsageFlags::INDEX_BUFFER, )) @@ -142,7 +142,7 @@ impl ImGui { let vertices = draw_list.vtx_buffer(); let vertex_buf_len = vertices.len() * 20; let mut vertex_buf = pool - .lease(BufferInfo::host_mem( + .lease_resource(BufferInfo::host_mem( vertex_buf_len as _, vk::BufferUsageFlags::VERTEX_BUFFER, )) @@ -272,7 +272,7 @@ impl ImGui { let texture = fonts.build_rgba32_texture(); // TODO: Fix fb channel writes and use alpha8! let temp_buf_len = texture.data.len(); let mut temp_buf = pool - .lease(BufferInfo::host_mem( + .lease_resource(BufferInfo::host_mem( temp_buf_len as _, vk::BufferUsageFlags::TRANSFER_SRC, )) @@ -285,7 +285,7 @@ impl ImGui { let temp_buf = graph.bind_resource(temp_buf); let image = graph.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( texture.width, texture.height, vk::Format::R8G8B8A8_UNORM, diff --git a/contrib/vk-graph-prelude/src/lib.rs b/contrib/vk-graph-prelude/src/lib.rs index e146c09c..5e1f679b 100644 --- a/contrib/vk-graph-prelude/src/lib.rs +++ b/contrib/vk-graph-prelude/src/lib.rs @@ -54,7 +54,7 @@ pub use vk_graph::{ }, pool::{ Lease, Pool, PoolInfo, PoolInfoBuilder, - alias::{Alias, AliasPool}, + alias::{Alias, AliasWrapper}, fifo::FifoPool, hash::HashPool, lazy::LazyPool, @@ -62,4 +62,7 @@ pub use vk_graph::{ }; #[allow(deprecated)] -pub use vk_graph::driver::graphic::{BlendMode, DepthStencilMode}; +pub use vk_graph::{ + driver::graphic::{BlendMode, DepthStencilMode}, + pool::alias::AliasPool, +}; diff --git a/examples/aliasing.rs b/examples/aliasing.rs index 2ecadc84..cd11db3d 100644 --- a/examples/aliasing.rs +++ b/examples/aliasing.rs @@ -21,7 +21,7 @@ fn main() -> Result<(), DriverError> { let device = Device::new(device_info)?; // We wrap HashPool in an AliasPool container to enable resource aliasing - let mut pool = AliasPool::new(HashPool::new(&device)); + let mut pool = AliasWrapper::new(HashPool::new(&device)); // This is the information we will use to alias image1 and image2 let image_info = ImageInfo::image_2d( @@ -32,8 +32,8 @@ fn main() -> Result<(), DriverError> { ); // Any two compatible images aliased from the same pool will be the same physical image - let image1 = pool.alias(image_info)?; - let image2 = pool.alias(image_info)?; + let image1 = pool.alias_resource(image_info)?; + let image2 = pool.alias_resource(image_info)?; assert!(Arc::ptr_eq(&image1, &image2)); let mut graph = Graph::default(); @@ -61,11 +61,11 @@ fn main() -> Result<(), DriverError> { ); // We alias the compatible information and still produce the same physical image and node - let image3_node = graph.bind_resource(pool.alias(image_info)?); + let image3_node = graph.bind_resource(pool.alias_resource(image_info)?); assert_eq!(image1_node, image3_node); // Using the same information for a new LEASE will generate an entirely different image!! - let image4_node = graph.bind_resource(pool.lease(image_info)?); + let image4_node = graph.bind_resource(pool.lease_resource(image_info)?); assert_ne!(image1_node, image4_node); Ok(()) diff --git a/examples/cpu_readback.rs b/examples/cpu_readback.rs index 8051ff75..1d567e39 100644 --- a/examples/cpu_readback.rs +++ b/examples/cpu_readback.rs @@ -15,12 +15,12 @@ fn main() -> Result<(), DriverError> { let src_buf = graph.bind_resource(Buffer::create_from_slice( &device, vk::BufferUsageFlags::TRANSFER_SRC, - [1, 2, 3, 4], + &[1, 2, 3, 4], )?); let dst_buf = graph.bind_resource(Buffer::create_from_slice( &device, vk::BufferUsageFlags::TRANSFER_DST, - [0, 0, 0, 0], + &[0, 0, 0, 0], )?); // We are using the GPU to copy data, but the same thing works if you're executing a pipeline diff --git a/examples/egui.rs b/examples/egui.rs index a79aa62d..622397c8 100644 --- a/examples/egui.rs +++ b/examples/egui.rs @@ -22,7 +22,7 @@ fn main() -> anyhow::Result<()> { window.run(|frame| { let img = frame.graph.bind_resource( cache - .lease(ImageInfo::image_2d( + .lease_resource(ImageInfo::image_2d( 100, 100, vk::Format::R8G8B8A8_UNORM, diff --git a/examples/font_bmp.rs b/examples/font_bmp.rs index d3061eb0..a9e10113 100644 --- a/examples/font_bmp.rs +++ b/examples/font_bmp.rs @@ -114,7 +114,7 @@ fn main() -> anyhow::Result<()> { window.run(|frame| { let image_node = frame.graph.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( 320, 200, vk::Format::R8G8B8A8_UNORM, diff --git a/examples/fuzzer.rs b/examples/fuzzer.rs index efb74a09..5288eb46 100644 --- a/examples/fuzzer.rs +++ b/examples/fuzzer.rs @@ -105,7 +105,7 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { // Vertex buffer for a triangle let vertex_buf = { let mut buf = pool - .lease(BufferInfo::host_mem( + .lease_resource(BufferInfo::host_mem( 36, vk::BufferUsageFlags::ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_KHR | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS @@ -114,19 +114,19 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { .unwrap(); // Vertex 1 - Buffer::copy_from_slice(&mut buf, 0, 0f32.to_ne_bytes()); - Buffer::copy_from_slice(&mut buf, 4, 0f32.to_ne_bytes()); - Buffer::copy_from_slice(&mut buf, 8, 0f32.to_ne_bytes()); + Buffer::copy_from_slice(&mut buf, 0, 0f32.to_ne_bytes().as_slice()); + Buffer::copy_from_slice(&mut buf, 4, 0f32.to_ne_bytes().as_slice()); + Buffer::copy_from_slice(&mut buf, 8, 0f32.to_ne_bytes().as_slice()); // Vertex 2 - Buffer::copy_from_slice(&mut buf, 12, 1f32.to_ne_bytes()); - Buffer::copy_from_slice(&mut buf, 16, 1f32.to_ne_bytes()); - Buffer::copy_from_slice(&mut buf, 20, 0f32.to_ne_bytes()); + Buffer::copy_from_slice(&mut buf, 12, 1f32.to_ne_bytes().as_slice()); + Buffer::copy_from_slice(&mut buf, 16, 1f32.to_ne_bytes().as_slice()); + Buffer::copy_from_slice(&mut buf, 20, 0f32.to_ne_bytes().as_slice()); // Vertex 3 - Buffer::copy_from_slice(&mut buf, 24, 2f32.to_ne_bytes()); - Buffer::copy_from_slice(&mut buf, 28, 0f32.to_ne_bytes()); - Buffer::copy_from_slice(&mut buf, 32, 0f32.to_ne_bytes()); + Buffer::copy_from_slice(&mut buf, 24, 2f32.to_ne_bytes().as_slice()); + Buffer::copy_from_slice(&mut buf, 28, 0f32.to_ne_bytes().as_slice()); + Buffer::copy_from_slice(&mut buf, 32, 0f32.to_ne_bytes().as_slice()); buf }; @@ -134,7 +134,7 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { // Index buffer for a single triangle let index_buf = { let mut buf = pool - .lease(BufferInfo::host_mem( + .lease_resource(BufferInfo::host_mem( 6, vk::BufferUsageFlags::ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_KHR | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS @@ -142,9 +142,9 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { )) .unwrap(); - Buffer::copy_from_slice(&mut buf, 0, 0u16.to_ne_bytes()); - Buffer::copy_from_slice(&mut buf, 2, 1u16.to_ne_bytes()); - Buffer::copy_from_slice(&mut buf, 4, 2u16.to_ne_bytes()); + Buffer::copy_from_slice(&mut buf, 0, 0u16.to_ne_bytes().as_slice()); + Buffer::copy_from_slice(&mut buf, 2, 1u16.to_ne_bytes().as_slice()); + Buffer::copy_from_slice(&mut buf, 4, 2u16.to_ne_bytes().as_slice()); buf }; @@ -193,7 +193,7 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { // Lease and bind a bunch of bottom-level acceleration structures and add to instance buffer let mut blas_nodes = Vec::with_capacity(BLAS_COUNT as _); for idx in 0..BLAS_COUNT { - let blas = pool.lease(blas_info).unwrap(); + let blas = pool.lease_resource(blas_info).unwrap(); Buffer::copy_from_slice( &mut instance_buf, @@ -219,7 +219,7 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { let blas_node = frame.graph.bind_resource(blas); let scratch_buf = frame.graph.bind_resource( - pool.lease( + pool.lease_resource( BufferInfo::device_mem( blas_size.build_size, vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS @@ -249,11 +249,11 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { let instance_buf = frame.graph.bind_resource(instance_buf); let tlas_size = AccelerationStructure::size_of(frame.device, &tlas_geometry_info); let tlas = pool - .lease(AccelerationStructureInfo::tlas(tlas_size.create_size)) + .lease_resource(AccelerationStructureInfo::tlas(tlas_size.create_size)) .unwrap(); let tlas_node = frame.graph.bind_resource(tlas); let tlas_scratch_buf = frame.graph.bind_resource( - pool.lease( + pool.lease_resource( BufferInfo::device_mem( tlas_size.build_size, vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS | vk::BufferUsageFlags::STORAGE_BUFFER, @@ -358,11 +358,21 @@ fn record_pipeline_array_bind(frame: &mut FrameContext, pool: &mut HashPool) { vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST, ); let images = [ - frame.graph.bind_resource(pool.lease(image_info).unwrap()), - frame.graph.bind_resource(pool.lease(image_info).unwrap()), - frame.graph.bind_resource(pool.lease(image_info).unwrap()), - frame.graph.bind_resource(pool.lease(image_info).unwrap()), - frame.graph.bind_resource(pool.lease(image_info).unwrap()), + frame + .graph + .bind_resource(pool.lease_resource(image_info).unwrap()), + frame + .graph + .bind_resource(pool.lease_resource(image_info).unwrap()), + frame + .graph + .bind_resource(pool.lease_resource(image_info).unwrap()), + frame + .graph + .bind_resource(pool.lease_resource(image_info).unwrap()), + frame + .graph + .bind_resource(pool.lease_resource(image_info).unwrap()), ]; frame @@ -429,11 +439,21 @@ fn record_pipeline_bindless(frame: &mut FrameContext, pool: &mut HashPool) { vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::STORAGE, ); let images = [ - frame.graph.bind_resource(pool.lease(image_info).unwrap()), - frame.graph.bind_resource(pool.lease(image_info).unwrap()), - frame.graph.bind_resource(pool.lease(image_info).unwrap()), - frame.graph.bind_resource(pool.lease(image_info).unwrap()), - frame.graph.bind_resource(pool.lease(image_info).unwrap()), + frame + .graph + .bind_resource(pool.lease_resource(image_info).unwrap()), + frame + .graph + .bind_resource(pool.lease_resource(image_info).unwrap()), + frame + .graph + .bind_resource(pool.lease_resource(image_info).unwrap()), + frame + .graph + .bind_resource(pool.lease_resource(image_info).unwrap()), + frame + .graph + .bind_resource(pool.lease_resource(image_info).unwrap()), ]; frame @@ -523,7 +543,7 @@ fn record_graphic_bindless(frame: &mut FrameContext, pool: &mut HashPool) { ); let image = frame.graph.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( 256, 256, vk::Format::R8G8B8A8_UNORM, @@ -540,11 +560,21 @@ fn record_graphic_bindless(frame: &mut FrameContext, pool: &mut HashPool) { | vk::ImageUsageFlags::TRANSFER_DST, ); let images = [ - frame.graph.bind_resource(pool.lease(image_info).unwrap()), - frame.graph.bind_resource(pool.lease(image_info).unwrap()), - frame.graph.bind_resource(pool.lease(image_info).unwrap()), - frame.graph.bind_resource(pool.lease(image_info).unwrap()), - frame.graph.bind_resource(pool.lease(image_info).unwrap()), + frame + .graph + .bind_resource(pool.lease_resource(image_info).unwrap()), + frame + .graph + .bind_resource(pool.lease_resource(image_info).unwrap()), + frame + .graph + .bind_resource(pool.lease_resource(image_info).unwrap()), + frame + .graph + .bind_resource(pool.lease_resource(image_info).unwrap()), + frame + .graph + .bind_resource(pool.lease_resource(image_info).unwrap()), ]; frame @@ -734,7 +764,7 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo let swapchain_format = frame.graph.resource(frame.swapchain_image).info.fmt; let msaa_color_image = frame.graph.bind_resource( - pool.lease( + pool.lease_resource( ImageInfo::image_2d( frame.width, frame.height, @@ -747,7 +777,7 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo .unwrap(), ); let msaa_depth_stencil_image = frame.graph.bind_resource( - pool.lease( + pool.lease_resource( ImageInfo::image_2d( frame.width, frame.height, @@ -761,7 +791,7 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo .unwrap(), ); let depth_stencil_image = frame.graph.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( frame.width, frame.height, depth_stencil_format, @@ -802,13 +832,13 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo LoadOp::CLEAR_BLACK_ALPHA_ZERO, StoreOp::DontCare, ) - .color_attachment_image_resolve(1, frame.swapchain_image, 0) + .color_attachment_resolve_image(1, frame.swapchain_image, 0) .depth_stencil_attachment_image( msaa_depth_stencil_image, LoadOp::CLEAR_ZERO_STENCIL_ZERO, StoreOp::DontCare, ) - .depth_stencil_attachment_image_resolve( + .depth_stencil_attachment_resolve_image( 2, depth_stencil_image, Some(depth_resolve_mode), @@ -821,7 +851,7 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo fn record_graphic_will_merge_common_color1(frame: &mut FrameContext, pool: &mut HashPool) { let image = frame.graph.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( 256, 256, vk::Format::R8G8B8A8_UNORM, @@ -903,7 +933,7 @@ fn record_graphic_will_merge_common_color1(frame: &mut FrameContext, pool: &mut fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut HashPool) { let image_0 = frame.graph.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( 256, 256, vk::Format::R8G8B8A8_UNORM, @@ -912,7 +942,7 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut .unwrap(), ); let image_1 = frame.graph.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( 256, 256, vk::Format::R8G8B8A8_UNORM, @@ -1030,7 +1060,7 @@ fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut HashPool) { let color_image = frame.graph.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( 256, 256, vk::Format::R8G8B8A8_UNORM, @@ -1039,7 +1069,7 @@ fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut .unwrap(), ); let depth_image = frame.graph.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( 256, 256, vk::Format::D32_SFLOAT, @@ -1120,7 +1150,7 @@ fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut HashPool) { let color_image = frame.graph.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( 256, 256, vk::Format::R8G8B8A8_UNORM, @@ -1129,7 +1159,7 @@ fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut .unwrap(), ); let depth_image = frame.graph.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( 256, 256, vk::Format::D32_SFLOAT, @@ -1210,7 +1240,7 @@ fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut fn record_graphic_will_merge_common_depth3(frame: &mut FrameContext, pool: &mut HashPool) { let depth_image = frame.graph.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( 256, 256, vk::Format::D32_SFLOAT, @@ -1335,7 +1365,7 @@ fn record_graphic_will_merge_subpass_input(frame: &mut FrameContext, pool: &mut .as_slice(), ); let image = frame.graph.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( 256, 256, vk::Format::R8G8B8A8_UNORM, @@ -1396,7 +1426,7 @@ fn record_graphic_wont_merge(frame: &mut FrameContext, pool: &mut HashPool) { ); let image = frame.graph.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( 256, 256, vk::Format::R8G8B8A8_UNORM, @@ -1458,7 +1488,7 @@ fn record_transfer_graphic_multipass(frame: &mut FrameContext, pool: &mut HashPo ); let images = [ frame.graph.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( 256, 256, vk::Format::R8G8B8A8_UNORM, @@ -1467,7 +1497,7 @@ fn record_transfer_graphic_multipass(frame: &mut FrameContext, pool: &mut HashPo .unwrap(), ), frame.graph.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( 256, 256, vk::Format::R8G8B8A8_UNORM, diff --git a/examples/image_sampler.rs b/examples/image_sampler.rs index 21aa987c..4067fd07 100644 --- a/examples/image_sampler.rs +++ b/examples/image_sampler.rs @@ -251,7 +251,7 @@ fn read_image(device: &Device, path: impl AsRef) -> anyhow::Result Result<(), WindowError> { window.run(|frame| { // Lease and clear an image as a stand-in for some real game or program output let app_image = frame.graph.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( frame.width, frame.height, vk::Format::R8G8B8A8_UNORM, diff --git a/examples/msaa.rs b/examples/msaa.rs index e1fe526a..20b040b2 100644 --- a/examples/msaa.rs +++ b/examples/msaa.rs @@ -64,7 +64,7 @@ fn main() -> anyhow::Result<()> { * Mat4::from_rotation_z(angle * 0.22); let mut scene_uniform_buf = pool - .lease(BufferInfo::host_mem( + .lease_resource(BufferInfo::host_mem( size_of::() as _, vk::BufferUsageFlags::UNIFORM_BUFFER, )) @@ -103,7 +103,7 @@ fn main() -> anyhow::Result<()> { if will_render_msaa { let msaa_color_image = cmd.bind_resource( - pool.lease( + pool.lease_resource( ImageInfo::image_2d( frame.width, frame.height, @@ -117,7 +117,7 @@ fn main() -> anyhow::Result<()> { .unwrap(), ); let msaa_depth_image = cmd.bind_resource( - pool.lease( + pool.lease_resource( ImageInfo::image_2d( frame.width, frame.height, @@ -138,7 +138,7 @@ fn main() -> anyhow::Result<()> { LoadOp::CLEAR_WHITE_ALPHA_ONE, StoreOp::DontCare, ) - .set_color_attachment_image_resolve(1, frame.swapchain_image, 0) + .set_color_attachment_resolve_image(1, frame.swapchain_image, 0) .set_depth_stencil_attachment_image( msaa_depth_image, LoadOp::CLEAR_ZERO_STENCIL_ZERO, @@ -146,7 +146,7 @@ fn main() -> anyhow::Result<()> { ); } else { let noaa_depth_image = cmd.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( frame.width, frame.height, depth_format, diff --git a/examples/multipass.rs b/examples/multipass.rs index 54afe500..bcf89617 100644 --- a/examples/multipass.rs +++ b/examples/multipass.rs @@ -65,7 +65,7 @@ fn main() -> anyhow::Result<()> { let vertex_buf = frame.graph.bind_resource(&funky_shape.vertex_buf); let depth_stencil = frame.graph.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( frame.width, frame.height, depth_stencil_format, @@ -207,7 +207,7 @@ fn bind_camera_buf( model: Mat4, ) -> BufferLeaseNode { let mut buf = pool - .lease(BufferInfo::host_mem( + .lease_resource(BufferInfo::host_mem( 204, vk::BufferUsageFlags::UNIFORM_BUFFER, )) @@ -219,7 +219,7 @@ fn bind_camera_buf( fn bind_light_buf(graph: &mut Graph, pool: &mut LazyPool) -> BufferLeaseNode { let mut buf = pool - .lease(BufferInfo::host_mem( + .lease_resource(BufferInfo::host_mem( 64, vk::BufferUsageFlags::UNIFORM_BUFFER, )) diff --git a/examples/multithread.rs b/examples/multithread.rs index b1288ca5..b5d8f222 100644 --- a/examples/multithread.rs +++ b/examples/multithread.rs @@ -103,7 +103,7 @@ fn main() -> anyhow::Result<()> { // Clear a new image to a cycling color let mut graph = Graph::default(); let image = graph.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( 10, 10, vk::Format::R8G8B8A8_UNORM, diff --git a/examples/ray_omni.rs b/examples/ray_omni.rs index 6410e061..4d198b94 100644 --- a/examples/ray_omni.rs +++ b/examples/ray_omni.rs @@ -54,7 +54,7 @@ fn main() -> anyhow::Result<()> { let model_mesh_vertex_buf = frame.graph.bind_resource(&model_mesh.vertex_buf); let depth_image = frame.graph.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( frame.width, frame.height, depth_fmt, @@ -64,7 +64,7 @@ fn main() -> anyhow::Result<()> { ); let camera_buf = frame.graph.bind_resource({ let mut buf = pool - .lease(BufferInfo::host_mem( + .lease_resource(BufferInfo::host_mem( size_of::() as _, vk::BufferUsageFlags::UNIFORM_BUFFER, )) @@ -371,7 +371,8 @@ fn create_tlas( )]) .flags(vk::BuildAccelerationStructureFlagsKHR::PREFER_FAST_TRACE); let size = AccelerationStructure::size_of(device, &info); - let tlas = graph.bind_resource(pool.lease(AccelerationStructureInfo::tlas(size.create_size))?); + let tlas = graph + .bind_resource(pool.lease_resource(AccelerationStructureInfo::tlas(size.create_size))?); let accel_struct_scratch_offset_alignment = device .physical_device @@ -381,7 +382,7 @@ fn create_tlas( .min_accel_struct_scratch_offset_alignment as vk::DeviceSize; let scratch_buf = graph.bind_resource( - pool.lease( + pool.lease_resource( BufferInfo::device_mem( size.build_size, vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS | vk::BufferUsageFlags::STORAGE_BUFFER, diff --git a/examples/ray_trace.rs b/examples/ray_trace.rs index bb01c412..15a1b1c7 100644 --- a/examples/ray_trace.rs +++ b/examples/ray_trace.rs @@ -758,7 +758,7 @@ fn main() -> anyhow::Result<()> { if image.is_none() { image = Some(Arc::new( cache - .lease(ImageInfo::image_2d( + .lease_resource(ImageInfo::image_2d( frame.width, frame.height, frame.graph.resource(frame.swapchain_image).info.fmt, @@ -828,7 +828,7 @@ fn main() -> anyhow::Result<()> { } let mut buf = cache - .lease(BufferInfo::host_mem( + .lease_resource(BufferInfo::host_mem( size_of::() as _, vk::BufferUsageFlags::UNIFORM_BUFFER, )) diff --git a/examples/shader-toy/src/main.rs b/examples/shader-toy/src/main.rs index e16eea5c..9e9a4067 100644 --- a/examples/shader-toy/src/main.rs +++ b/examples/shader-toy/src/main.rs @@ -127,7 +127,7 @@ fn main() -> anyhow::Result<()> { let mut graph = Graph::default(); let blank_image = graph.bind_resource( cache - .lease(ImageInfo::image_2d( + .lease_resource(ImageInfo::image_2d( 8, 8, vk::Format::R8G8B8A8_SRGB, @@ -139,7 +139,7 @@ fn main() -> anyhow::Result<()> { let (width, height) = (1280, 720); let framebuffer_image = graph.bind_resource( cache - .lease(ImageInfo::image_2d( + .lease_resource(ImageInfo::image_2d( width, height, vk::Format::R8G8B8A8_SRGB, @@ -152,7 +152,7 @@ fn main() -> anyhow::Result<()> { ); let temp_image = graph.bind_resource( cache - .lease(ImageInfo::image_2d( + .lease_resource(ImageInfo::image_2d( width, height, vk::Format::R8G8B8A8_SRGB, diff --git a/examples/skeletal-anim/src/main.rs b/examples/skeletal-anim/src/main.rs index 6d211bbb..dd2c0cc9 100644 --- a/examples/skeletal-anim/src/main.rs +++ b/examples/skeletal-anim/src/main.rs @@ -64,7 +64,7 @@ fn main() -> Result<(), WindowError> { let index_buf = frame.graph.bind_resource(&character.index_buf); let vertex_buf = frame.graph.bind_resource(&character.vertex_buf); let depth_image = frame.graph.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( frame.width, frame.height, vk::Format::D32_SFLOAT, @@ -88,7 +88,7 @@ fn main() -> Result<(), WindowError> { let projection = Mat4::perspective_rh(45.0, aspect_ratio, 0.1, 100.0); let view = Mat4::look_at_rh(position, Vec3::Y * 2.0, -Vec3::Y); let mut buf = pool - .lease(BufferInfo::host_mem( + .lease_resource(BufferInfo::host_mem( size_of::() as _, vk::BufferUsageFlags::UNIFORM_BUFFER, )) @@ -114,7 +114,7 @@ fn main() -> Result<(), WindowError> { }; let joints = animation.update(0.016); let mut buf = pool - .lease(BufferInfo::host_mem( + .lease_resource(BufferInfo::host_mem( size_of_val(joints) as _, vk::BufferUsageFlags::STORAGE_BUFFER, )) diff --git a/examples/vr/src/main.rs b/examples/vr/src/main.rs index 06da0ace..0ad74f5f 100644 --- a/examples/vr/src/main.rs +++ b/examples/vr/src/main.rs @@ -273,7 +273,7 @@ fn main() -> anyhow::Result<()> { let mut graph = Graph::default(); let depth_image = graph.bind_resource( - pool.lease(ImageInfo::image_2d_array( + pool.lease_resource(ImageInfo::image_2d_array( resolution.width, resolution.height, 2, @@ -296,7 +296,7 @@ fn main() -> anyhow::Result<()> { let camera_buf = { let cameras = [CameraBuffer::new(views[0]), CameraBuffer::new(views[1])]; let data = cast_slice(&cameras); - let mut buf = pool.lease(BufferInfo::host_mem( + let mut buf = pool.lease_resource(BufferInfo::host_mem( data.len() as _, vk::BufferUsageFlags::UNIFORM_BUFFER, ))?; @@ -335,7 +335,7 @@ fn main() -> anyhow::Result<()> { let light_buf = { let light = LightBuffer::new(light_position); let data = bytes_of(&light); - let mut buf = pool.lease(BufferInfo::host_mem( + let mut buf = pool.lease_resource(BufferInfo::host_mem( data.len() as _, vk::BufferUsageFlags::UNIFORM_BUFFER, ))?; diff --git a/examples/vsm_omni.rs b/examples/vsm_omni.rs index cf7e9099..92f6bf85 100644 --- a/examples/vsm_omni.rs +++ b/examples/vsm_omni.rs @@ -195,7 +195,7 @@ fn main() -> anyhow::Result<()> { // Lease and bind a cube-compatible shadow 2D image array to the graph of the current frame let shadow_faces_image = pool - .lease( + .lease_resource( ImageInfo::image_2d_array( CUBEMAP_SIZE, CUBEMAP_SIZE, @@ -215,11 +215,11 @@ fn main() -> anyhow::Result<()> { // Lease and bind a temporary image we'll use during blur passes let temp_image = frame .graph - .bind_resource(pool.lease(shadow_faces_info).unwrap()); + .bind_resource(pool.lease_resource(shadow_faces_info).unwrap()); // Lastly we lease and bind depth images needed for rendering let shadow_depth_image = frame.graph.bind_resource( - pool.lease(ImageInfo::image_2d_array( + pool.lease_resource(ImageInfo::image_2d_array( frame.width, frame.height, if use_geometry_shader { 6 } else { 1 }, @@ -229,7 +229,7 @@ fn main() -> anyhow::Result<()> { .unwrap(), ); let depth_image = frame.graph.bind_resource( - pool.lease(ImageInfo::image_2d( + pool.lease_resource(ImageInfo::image_2d( frame.width, frame.height, depth_format, @@ -1191,7 +1191,7 @@ fn lease_uniform_buffer( data: &impl NoUninit, ) -> Result, DriverError> { let data = bytes_of(data); - let mut buf = pool.lease(BufferInfo::host_mem( + let mut buf = pool.lease_resource(BufferInfo::host_mem( data.len() as _, vk::BufferUsageFlags::UNIFORM_BUFFER, ))?; diff --git a/src/cmd_ref/bind.rs b/src/cmd_ref/bind.rs index cf8047e2..a36c86bc 100644 --- a/src/cmd_ref/bind.rs +++ b/src/cmd_ref/bind.rs @@ -26,11 +26,10 @@ macro_rules! bind_cmd_pipeline { impl<'a> BindCommand<'a> for &'a [<$name Pipeline>] { type Ref = PipelineCommandRef<'a, [<$name Pipeline>]>; - // TODO: Allow binding as explicit secondary command buffers? like with compute/raytrace stuff fn bind_cmd(self, mut cmd: CommandRef<'a>) -> Self::Ref { let cmd_ref = cmd.cmd_mut(); if cmd_ref.execs.last().unwrap().pipeline.is_some() { - // Binding from PipelinePass -> PipelinePass (changing shaders) + // Binding from PipelineCommandRef -> PipelineCommandRef (changing shaders) cmd_ref.execs.push(Default::default()); } @@ -49,7 +48,7 @@ macro_rules! bind_cmd_pipeline { fn bind_cmd(self, mut cmd: CommandRef<'a>) -> Self::Ref { let cmd_ref = cmd.cmd_mut(); if cmd_ref.execs.last().unwrap().pipeline.is_some() { - // Binding from PipelinePass -> PipelinePass (changing shaders) + // Binding from PipelineCommandRef -> PipelineCommandRef (changing shaders) cmd_ref.execs.push(Default::default()); } diff --git a/src/cmd_ref/cmd_buf.rs b/src/cmd_ref/cmd_buf.rs index e9df8a86..5b2ecf64 100644 --- a/src/cmd_ref/cmd_buf.rs +++ b/src/cmd_ref/cmd_buf.rs @@ -473,8 +473,8 @@ impl<'a> CommandBufferRef<'a> { } /// Returns a borrow of the original Vulkan resource (buffer, image or acceleration structure) - /// which the given node represents. - pub fn resource(&self, node: N) -> &N::Resource + /// which the given bound resource represents. + pub fn resource(&self, resource: N) -> &N::Resource where N: Bound, { @@ -485,11 +485,11 @@ impl<'a> CommandBufferRef<'a> { // resource (buffer, image, or acceleration structure). In order to access any resources the // access type must first be specified so the correct barriers may be added. debug_assert!( - self.exec.accesses.contains_key(&node.index()), + self.exec.accesses.contains_key(&resource.index()), "unexpected node access: call access, read, or write first" ); - node.borrow(self.resources) + resource.borrow(self.resources) } } diff --git a/src/cmd_ref/compute.rs b/src/cmd_ref/compute.rs index aa8940b7..fdbb3e3c 100644 --- a/src/cmd_ref/compute.rs +++ b/src/cmd_ref/compute.rs @@ -43,15 +43,6 @@ pub struct ComputeCommandBufferRef<'a> { pipeline: ComputePipeline, } -// impl<'a> ComputeCommandBufferRef<'a> { -// pub(super) fn new(cmd_buf: CommandBufferRef<'a>, -// pipeline: ComputePipeline,) -> Self { -// Self { -// cmd_buf,pipeline -// } -// } -// } - impl ComputeCommandBufferRef<'_> { /// [Dispatch] compute work items. /// @@ -353,7 +344,7 @@ impl PipelineCommandRef<'_, ComputePipeline> { .unwrap_compute() .clone(); - self.cmd.push_execute(move |cmd_buf| { + self.cmd.push_exec(move |cmd_buf| { func(ComputeCommandBufferRef { cmd_buf, pipeline }); }); diff --git a/src/cmd_ref/graphic.rs b/src/cmd_ref/graphic.rs index 23ffdb67..fee6f06d 100644 --- a/src/cmd_ref/graphic.rs +++ b/src/cmd_ref/graphic.rs @@ -125,15 +125,6 @@ pub struct GraphicCommandBufferRef<'a> { pipeline: GraphicPipeline, } -// impl<'a> GraphicCommandBufferRef<'a> { -// pub(super) fn new(cmd_buf: CommandBufferRef<'a>, -// pipeline: GraphicPipeline,) -> Self { -// Self { -// cmd_buf,pipeline -// } -// } -// } - impl GraphicCommandBufferRef<'_> { /// Bind an index buffer to the current pass. /// @@ -723,51 +714,51 @@ impl PipelineCommandRef<'_, GraphicPipeline> { } /// TODO - pub fn color_attachment_image_resolve( + pub fn color_attachment_image_view( mut self, color_attachment_idx: AttachmentIndex, color_image: impl Into, - resolve_attachment_idx: AttachmentIndex, + color_image_view_info: impl Into, + load: LoadOp, + store: StoreOp, ) -> Self { - self.set_color_attachment_image_resolve( + self.set_color_attachment_image_view( color_attachment_idx, color_image, - resolve_attachment_idx, + color_image_view_info, + load, + store, ); self } /// TODO - pub fn color_attachment_image_view( + pub fn color_attachment_resolve_image( mut self, color_attachment_idx: AttachmentIndex, color_image: impl Into, - color_image_view: impl Into, - load: LoadOp, - store: StoreOp, + resolve_attachment_idx: AttachmentIndex, ) -> Self { - self.set_color_attachment_image_view( + self.set_color_attachment_resolve_image( color_attachment_idx, color_image, - color_image_view, - load, - store, + resolve_attachment_idx, ); self } /// TODO - pub fn color_attachment_image_view_resolve( + pub fn color_attachment_resolve_image_view( mut self, color_attachment_idx: AttachmentIndex, color_image: impl Into, - color_image_view: impl Into, + color_image_view_info: impl Into, resolve_attachment_idx: AttachmentIndex, ) -> Self { - self.set_color_attachment_image_view_resolve( + self.set_color_attachment_resolve_image_view( color_attachment_idx, color_image, - color_image_view, + color_image_view_info, resolve_attachment_idx, ); self @@ -792,52 +783,52 @@ impl PipelineCommandRef<'_, GraphicPipeline> { } /// TODO - pub fn depth_stencil_attachment_image_resolve( + pub fn depth_stencil_attachment_image_view( mut self, - attachment_idx: AttachmentIndex, depth_stencil_image: impl Into, - depth_mode: Option, - stencil_mode: Option, + depth_stencil_image_view_info: impl Into, + load: LoadOp, + store: StoreOp, ) -> Self { - self.set_depth_stencil_attachment_image_resolve( - attachment_idx, + self.set_depth_stencil_attachment_image_view( depth_stencil_image, - depth_mode, - stencil_mode, + depth_stencil_image_view_info, + load, + store, ); self } /// TODO - pub fn depth_stencil_attachment_image_view( + pub fn depth_stencil_attachment_resolve_image( mut self, + attachment_idx: AttachmentIndex, depth_stencil_image: impl Into, - depth_stencil_image_view: impl Into, - load: LoadOp, - store: StoreOp, + depth_mode: Option, + stencil_mode: Option, ) -> Self { - self.set_depth_stencil_attachment_image_view( + self.set_depth_stencil_attachment_resolve_image( + attachment_idx, depth_stencil_image, - depth_stencil_image_view, - load, - store, + depth_mode, + stencil_mode, ); self } /// TODO - pub fn depth_stencil_attachment_image_view_resolve( + pub fn depth_stencil_attachment_resolve_image_view( mut self, attachment_idx: AttachmentIndex, depth_stencil_image: impl Into, - depth_stencil_image_view: impl Into, + depth_stencil_image_view_info: impl Into, depth_mode: Option, stencil_mode: Option, ) -> Self { - self.set_depth_stencil_attachment_image_view_resolve( + self.set_depth_stencil_attachment_resolve_image_view( attachment_idx, depth_stencil_image, - depth_stencil_image_view, + depth_stencil_image_view_info, depth_mode, stencil_mode, ); @@ -870,7 +861,7 @@ impl PipelineCommandRef<'_, GraphicPipeline> { .unwrap_graphic() .clone(); - self.cmd.push_execute(move |cmd_buf| { + self.cmd.push_exec(move |cmd_buf| { func(GraphicCommandBufferRef { cmd_buf, pipeline }); }); @@ -915,37 +906,17 @@ impl PipelineCommandRef<'_, GraphicPipeline> { self } - /// See [color_attachment_image_resolve] - pub fn set_color_attachment_image_resolve( - &mut self, - color_attachment_idx: AttachmentIndex, - color_image: impl Into, - resolve_attachment_idx: AttachmentIndex, - ) -> &mut Self { - let color_image = color_image.into(); - let color_image_view = self.resource(color_image).info; - - self.set_color_attachment_image_view_resolve( - color_attachment_idx, - color_image, - color_image_view, - resolve_attachment_idx, - ); - - self - } - /// See [color_attachment_image_view] pub fn set_color_attachment_image_view( &mut self, color_attachment_idx: AttachmentIndex, color_image: impl Into, - color_image_view: impl Into, + color_image_view_info: impl Into, load: LoadOp, store: StoreOp, ) -> &mut Self { let color_image = color_image.into(); - let color_image_view = color_image_view.into(); + let color_image_view_info = color_image_view_info.into(); #[allow(deprecated)] { @@ -954,42 +925,64 @@ impl PipelineCommandRef<'_, GraphicPipeline> { color_attachment_idx, color_image, color, - color_image_view, + color_image_view_info, + ), + LoadOp::DontCare => self.set_attach_color_as( + color_attachment_idx, + color_image, + color_image_view_info, ), - LoadOp::DontCare => { - self.set_attach_color_as(color_attachment_idx, color_image, color_image_view) - } LoadOp::Load => { - self.set_load_color_as(color_attachment_idx, color_image, color_image_view) + self.set_load_color_as(color_attachment_idx, color_image, color_image_view_info) } }; if let StoreOp::Store = store { - self.set_store_color_as(color_attachment_idx, color_image, color_image_view); + self.set_store_color_as(color_attachment_idx, color_image, color_image_view_info); } } self } - /// See [color_attachment_image_view_resolve] - pub fn set_color_attachment_image_view_resolve( + /// See [color_attachment_resolve_image] + pub fn set_color_attachment_resolve_image( + &mut self, + color_attachment_idx: AttachmentIndex, + color_image: impl Into, + resolve_attachment_idx: AttachmentIndex, + ) -> &mut Self { + let color_image = color_image.into(); + let color_image_view = self.resource(color_image).info; + + self.set_color_attachment_resolve_image_view( + color_attachment_idx, + color_image, + color_image_view, + resolve_attachment_idx, + ); + + self + } + + /// See [color_attachment_resolve_image_view] + pub fn set_color_attachment_resolve_image_view( &mut self, color_attachment_idx: AttachmentIndex, color_image: impl Into, - color_image_view: impl Into, + color_image_view_info: impl Into, resolve_attachment_idx: AttachmentIndex, ) -> &mut Self { let color_image = color_image.into(); - let color_image_view = color_image_view.into(); + let color_image_view_info = color_image_view_info.into(); #[allow(deprecated)] - self.set_attach_color_as(color_attachment_idx, color_image, color_image_view) + self.set_attach_color_as(color_attachment_idx, color_image, color_image_view_info) .set_resolve_color_as( resolve_attachment_idx, color_attachment_idx, color_image, - color_image_view, + color_image_view_info, ); self @@ -1016,11 +1009,11 @@ impl PipelineCommandRef<'_, GraphicPipeline> { store: StoreOp, ) -> &mut Self { let depth_stencil_image = depth_stencil_image.into(); - let depth_stencil_image_view = self.resource(depth_stencil_image).info; + let depth_stencil_image_view_info = self.resource(depth_stencil_image).info; self.set_depth_stencil_attachment_image_view( depth_stencil_image, - depth_stencil_image_view, + depth_stencil_image_view_info, load, store, ); @@ -1028,38 +1021,16 @@ impl PipelineCommandRef<'_, GraphicPipeline> { self } - /// See [depth_stencil_attachment_image_resolve] - pub fn set_depth_stencil_attachment_image_resolve( - &mut self, - attachment_idx: AttachmentIndex, - depth_stencil_image: impl Into, - depth_mode: Option, - stencil_mode: Option, - ) -> &mut Self { - let depth_stencil_image = depth_stencil_image.into(); - let depth_stencil_image_view = self.resource(depth_stencil_image).info; - - self.set_depth_stencil_attachment_image_view_resolve( - attachment_idx, - depth_stencil_image, - depth_stencil_image_view, - depth_mode, - stencil_mode, - ); - - self - } - /// See [depth_stencil_attachment_image_view] pub fn set_depth_stencil_attachment_image_view( &mut self, depth_stencil_image: impl Into, - depth_stencil_image_view: impl Into, + depth_stencil_image_view_info: impl Into, load: LoadOp, store: StoreOp, ) -> &mut Self { let depth_stencil_image = depth_stencil_image.into(); - let depth_stencil_image_view = depth_stencil_image_view.into(); + let depth_stencil_image_view_info = depth_stencil_image_view_info.into(); #[allow(deprecated)] { @@ -1068,42 +1039,64 @@ impl PipelineCommandRef<'_, GraphicPipeline> { depth_stencil_image, color.depth, color.stencil, - depth_stencil_image_view, + depth_stencil_image_view_info, ), - LoadOp::DontCare => { - self.set_attach_depth_stencil_as(depth_stencil_image, depth_stencil_image_view) - } - LoadOp::Load => { - self.set_load_depth_stencil_as(depth_stencil_image, depth_stencil_image_view) - } + LoadOp::DontCare => self.set_attach_depth_stencil_as( + depth_stencil_image, + depth_stencil_image_view_info, + ), + LoadOp::Load => self + .set_load_depth_stencil_as(depth_stencil_image, depth_stencil_image_view_info), }; if let StoreOp::Store = store { - self.set_store_depth_stencil_as(depth_stencil_image, depth_stencil_image_view); + self.set_store_depth_stencil_as(depth_stencil_image, depth_stencil_image_view_info); } } self } - /// See [depth_stencil_attachment_image_view_resolve] - pub fn set_depth_stencil_attachment_image_view_resolve( + /// See [depth_stencil_attachment_resolve_image] + pub fn set_depth_stencil_attachment_resolve_image( + &mut self, + attachment_idx: AttachmentIndex, + depth_stencil_image: impl Into, + depth_mode: Option, + stencil_mode: Option, + ) -> &mut Self { + let depth_stencil_image = depth_stencil_image.into(); + let depth_stencil_image_view = self.resource(depth_stencil_image).info; + + self.set_depth_stencil_attachment_resolve_image_view( + attachment_idx, + depth_stencil_image, + depth_stencil_image_view, + depth_mode, + stencil_mode, + ); + + self + } + + /// See [depth_stencil_attachment_resolve_image_view] + pub fn set_depth_stencil_attachment_resolve_image_view( &mut self, attachment_idx: AttachmentIndex, depth_stencil_image: impl Into, - depth_stencil_image_view: impl Into, + depth_stencil_image_view_info: impl Into, depth_mode: Option, stencil_mode: Option, ) -> &mut Self { let depth_stencil_image = depth_stencil_image.into(); - let depth_stencil_image_view = depth_stencil_image_view.into(); + let depth_stencil_image_view_info = depth_stencil_image_view_info.into(); #[allow(deprecated)] - self.set_attach_depth_stencil_as(depth_stencil_image, depth_stencil_image_view) + self.set_attach_depth_stencil_as(depth_stencil_image, depth_stencil_image_view_info) .set_resolve_depth_stencil_as( attachment_idx, depth_stencil_image, - depth_stencil_image_view, + depth_stencil_image_view_info, depth_mode, stencil_mode, ); @@ -1583,10 +1576,10 @@ mod deprecated { "color attachment {attachment_idx} incompatible with existing store" ); - self.cmd.push_node_access( + self.cmd.push_subresource_access( image, - AccessType::ColorAttachmentWrite, SubresourceRange::Image(image_view_info.into()), + AccessType::ColorAttachmentWrite, ); self @@ -1674,8 +1667,9 @@ mod deprecated { "depth/stencil attachment incompatible with existing store" ); - self.cmd.push_node_access( + self.cmd.push_subresource_access( image, + SubresourceRange::Image(image_view_info.into()), if image_view_info .aspect_mask .contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL) @@ -1689,7 +1683,6 @@ mod deprecated { } else { AccessType::StencilAttachmentWriteDepthReadOnly }, - SubresourceRange::Image(image_view_info.into()), ); self @@ -1861,8 +1854,11 @@ mod deprecated { } } - self.cmd - .push_node_access(image, image_access, SubresourceRange::Image(image_range)); + self.cmd.push_subresource_access( + image, + SubresourceRange::Image(image_range), + image_access, + ); self } @@ -2056,8 +2052,11 @@ mod deprecated { } } - self.cmd - .push_node_access(image, image_access, SubresourceRange::Image(image_range)); + self.cmd.push_subresource_access( + image, + SubresourceRange::Image(image_range), + image_access, + ); self } @@ -2210,8 +2209,11 @@ mod deprecated { } } - self.cmd - .push_node_access(image, image_access, SubresourceRange::Image(image_range)); + self.cmd.push_subresource_access( + image, + SubresourceRange::Image(image_range), + image_access, + ); self } @@ -2337,8 +2339,11 @@ mod deprecated { } } - self.cmd - .push_node_access(image, image_access, SubresourceRange::Image(image_range)); + self.cmd.push_subresource_access( + image, + SubresourceRange::Image(image_range), + image_access, + ); self } @@ -2486,8 +2491,11 @@ mod deprecated { } } - self.cmd - .push_node_access(image, image_access, SubresourceRange::Image(image_range)); + self.cmd.push_subresource_access( + image, + SubresourceRange::Image(image_range), + image_access, + ); self } @@ -2643,8 +2651,11 @@ mod deprecated { } } - self.cmd - .push_node_access(image, image_access, SubresourceRange::Image(image_range)); + self.cmd.push_subresource_access( + image, + SubresourceRange::Image(image_range), + image_access, + ); self } @@ -2802,8 +2813,11 @@ mod deprecated { } } - self.cmd - .push_node_access(image, image_access, SubresourceRange::Image(image_range)); + self.cmd.push_subresource_access( + image, + SubresourceRange::Image(image_range), + image_access, + ); self } @@ -2945,8 +2959,11 @@ mod deprecated { } } - self.cmd - .push_node_access(image, image_access, SubresourceRange::Image(image_range)); + self.cmd.push_subresource_access( + image, + SubresourceRange::Image(image_range), + image_access, + ); self } diff --git a/src/cmd_ref/mod.rs b/src/cmd_ref/mod.rs index e7651429..086aa0a7 100644 --- a/src/cmd_ref/mod.rs +++ b/src/cmd_ref/mod.rs @@ -115,7 +115,7 @@ impl<'a> CommandRef<'a> { self.graph } - fn push_execute(&mut self, func: impl FnOnce(CommandBufferRef) + Send + 'static) { + fn push_exec(&mut self, func: impl FnOnce(CommandBufferRef) + Send + 'static) { let cmd = self.cmd_mut(); let exec = { let last_exec = cmd.execs.last_mut().unwrap(); @@ -131,15 +131,15 @@ impl<'a> CommandRef<'a> { self.exec_idx += 1; } - fn push_node_access( + fn push_subresource_access( &mut self, - node: impl Node, - access: AccessType, + resource: impl Node, subresource: SubresourceRange, + access: AccessType, ) { - let node_idx = node.index(); + let node_idx = resource.index(); - assert!(self.graph.resources.get(node_idx).is_some()); + debug_assert!(self.graph.resources.get(node_idx).is_some()); let access = SubresourceAccess { access, @@ -165,7 +165,7 @@ impl<'a> CommandRef<'a> { mut self, func: impl FnOnce(CommandBufferRef<'_>) + Send + 'static, ) -> Self { - self.push_execute(move |cmd_buf| { + self.push_exec(move |cmd_buf| { func(cmd_buf); }); @@ -173,28 +173,28 @@ impl<'a> CommandRef<'a> { } /// Returns a borrow of the original Vulkan resource (buffer, image or acceleration structure) - /// which the given node represents. - pub fn resource(&self, node: N) -> &N::Resource + /// which the given bound resource represents. + pub fn resource(&self, resource: N) -> &N::Resource where N: Bound, { - self.graph.resource(node) + self.graph.resource(resource) } /// Informs the command that the next recorded command buffer will read or write `node` using /// `access`. /// /// This function must be called for `node` before it is used within a `record_`-function. - pub fn resource_access(mut self, node: N, access: AccessType) -> Self + pub fn resource_access(mut self, resource: N, access: AccessType) -> Self where N: Node + View, SubresourceRange: From, { - self.set_resource_access(node, access); + self.set_resource_access(resource, access); self } - /// Sets a debugging name, but only in debug builds + /// Sets a debugging name, but only in debug builds. pub fn set_debug_name(&mut self, name: impl Into) -> &mut Self { #[cfg(debug_assertions)] { @@ -208,13 +208,15 @@ impl<'a> CommandRef<'a> { /// `access`. /// /// This function must be called for `node` before it is used within a `record_`-function. - pub fn set_resource_access(&mut self, node: N, access: AccessType) + pub fn set_resource_access(&mut self, resource: N, access: AccessType) where N: Node + View, SubresourceRange: From, { - let subresource = node.default_range(&self.graph.resources).into(); - self.push_node_access(node, access, subresource); + let whole_resource = resource.range(&self.graph.resources); + let subresource = SubresourceRange::from(whole_resource); + + self.push_subresource_access(resource, subresource, access); } /// Informs the command that the next recorded command buffer will read or write the @@ -223,15 +225,17 @@ impl<'a> CommandRef<'a> { /// This function must be called for `node` before it is used within a `record_`-function. pub fn set_subresource_access( &mut self, - node: N, + resource: N, subresource: impl Into, access: AccessType, ) where N: Node + View, SubresourceRange: From, { - let subresource = subresource.into().into(); - self.push_node_access(node, access, subresource); + let subresource = subresource.into(); + let subresource = SubresourceRange::from(subresource); + + self.push_subresource_access(resource, subresource, access); } /// Informs the command that the next recorded command buffer will read or write the @@ -240,7 +244,7 @@ impl<'a> CommandRef<'a> { /// This function must be called for `node` before it is used within a `record_`-function. pub fn subresource_access( mut self, - node: N, + resource: N, subresource: impl Into, access: AccessType, ) -> Self @@ -248,7 +252,7 @@ impl<'a> CommandRef<'a> { N: Node + View, SubresourceRange: From, { - self.set_subresource_access(node, subresource, access); + self.set_subresource_access(resource, subresource, access); self } } @@ -343,8 +347,8 @@ impl SubresourceRange { } impl From for SubresourceRange { - fn from(subresource: AccelerationStructureRange) -> Self { - Self::AccelerationStructure(subresource) + fn from(_: AccelerationStructureRange) -> Self { + Self::AccelerationStructure(AccelerationStructureRange) } } @@ -380,11 +384,11 @@ pub trait View { /// The information about the resource when used indirectly by any part of a graph. type Range; - fn default_range(&self, _: &[Resource]) -> Self::Range + fn info(&self, _: &[Resource]) -> Self::Info where Self: Node; - fn default_info(&self, _: &[Resource]) -> Self::Info + fn range(&self, _: &[Resource]) -> Self::Range where Self: Node; } @@ -395,18 +399,18 @@ macro_rules! view_accel_struct { type Info = Self::Range; type Range = AccelerationStructureRange; - fn default_range(&self, _: &[Resource]) -> Self::Range + fn info(&self, resources: &[Resource]) -> Self::Info where Self: Node, { - Self::Range::default() + self.range(resources) } - fn default_info(&self, resources: &[Resource]) -> Self::Info + fn range(&self, _: &[Resource]) -> Self::Range where Self: Node, { - self.default_range(resources) + Self::Range::default() } } }; @@ -422,20 +426,20 @@ macro_rules! view_buffer { type Info = Self::Range; type Range = BufferSubresourceRange; - fn default_range(&self, resources: &[Resource]) -> Self::Range + fn info(&self, resources: &[Resource]) -> Self::Info where Self: Node, { - let idx = self.index(); - - resources[idx].as_buffer().unwrap().info.into() + self.range(resources) } - fn default_info(&self, resources: &[Resource]) -> Self::Info + fn range(&self, resources: &[Resource]) -> Self::Range where Self: Node, { - self.default_range(resources) + let idx = self.index(); + + resources[idx].as_buffer().unwrap().info.into() } } }; @@ -451,20 +455,20 @@ macro_rules! view_image { type Info = ImageViewInfo; type Range = vk::ImageSubresourceRange; - fn default_range(&self, resources: &[Resource]) -> Self::Range + fn info(&self, resources: &[Resource]) -> Self::Info where Self: Node, { - self.default_info(resources).into() + let idx = self.index(); + + resources[idx].as_image().unwrap().info.into() } - fn default_info(&self, resources: &[Resource]) -> Self::Info + fn range(&self, resources: &[Resource]) -> Self::Range where Self: Node, { - let idx = self.index(); - - resources[idx].as_image().unwrap().info.into() + self.info(resources).into() } } }; @@ -607,7 +611,7 @@ mod deprecated { mut self, func: impl FnOnce(CommandBufferRef<'_>, ()) + Send + 'static, ) -> Self { - self.push_execute(|cmd_buf| { + self.push_exec(|cmd_buf| { func(cmd_buf, ()); }); diff --git a/src/cmd_ref/pipeline.rs b/src/cmd_ref/pipeline.rs index 95615399..31fbb871 100644 --- a/src/cmd_ref/pipeline.rs +++ b/src/cmd_ref/pipeline.rs @@ -32,23 +32,23 @@ impl<'a, T> PipelineCommandRef<'a, T> { /// Returns a borrow of the original Vulkan resource (buffer, image or acceleration structure) /// which the given node represents. - pub fn resource(&self, node: N) -> &N::Resource + pub fn resource(&self, resource: N) -> &N::Resource where N: Bound, { - self.cmd.resource(node) + self.cmd.resource(resource) } /// Informs the command that the next recorded command buffer will read or write `node` using /// `access`. /// /// This function must be called for `node` before it is used within a `record_`-function. - pub fn resource_access(mut self, node: N, access: AccessType) -> Self + pub fn resource_access(mut self, resource: N, access: AccessType) -> Self where N: Node + View, SubresourceRange: From, { - self.cmd.set_resource_access(node, access); + self.cmd.set_resource_access(resource, access); self } @@ -56,23 +56,23 @@ impl<'a, T> PipelineCommandRef<'a, T> { /// `access`. /// /// This function must be called for `node` before it is used within a `record_`-function. - pub fn set_resource_access(&mut self, node: N, access: AccessType) -> &mut Self + pub fn set_resource_access(&mut self, resource: N, access: AccessType) -> &mut Self where N: Node + View, SubresourceRange: From, { - self.cmd.set_resource_access(node, access); + self.cmd.set_resource_access(resource, access); self } - /// Informs the command that the next recorded command buffer will read or write the `node` at - /// the specified shader `descriptor` using `access`. + /// Informs the command that the next recorded command buffer will read or write the `resource` + /// at the specified shader `descriptor` using `access`. /// - /// This function must be called for `node` before it is used within a `record_`-function. + /// This function must be called for `resource` before it is used within a `record_`-function. pub fn set_shader_resource_access( &mut self, descriptor: impl Into, - node: N, + resource: N, access: AccessType, ) -> &mut Self where @@ -81,9 +81,9 @@ impl<'a, T> PipelineCommandRef<'a, T> { SubresourceRange: From, ViewInfo: From, { - let node_view = node.default_info(&self.cmd.graph.resources); + let subresource = resource.info(&self.cmd.graph.resources); - self.set_shader_subresource_access(descriptor, node, node_view, access) + self.set_shader_subresource_access(descriptor, resource, subresource, access) } /// Informs the command that the next recorded command buffer will read or write the `node` at @@ -94,8 +94,8 @@ impl<'a, T> PipelineCommandRef<'a, T> { pub fn set_shader_subresource_access( &mut self, descriptor: impl Into, - node: N, - node_view: impl Into, + resource: N, + subresource: impl Into, access: AccessType, ) -> &mut Self where @@ -105,11 +105,11 @@ impl<'a, T> PipelineCommandRef<'a, T> { ViewInfo: From, { let descriptor = descriptor.into(); - let node_view = node_view.into(); - let subresource = node_view.into(); - let node_idx = node.index(); + let subresource = subresource.into(); + let node_idx = resource.index(); - self.cmd.push_node_access(node, access, subresource); + self.cmd + .push_subresource_access(resource, SubresourceRange::from(subresource), access); assert!( self.cmd @@ -118,7 +118,7 @@ impl<'a, T> PipelineCommandRef<'a, T> { .last_mut() .unwrap() .bindings - .insert(descriptor, (node_idx, node_view.into())) + .insert(descriptor, (node_idx, subresource.into())) .is_none(), "descriptor {descriptor:?} has already been bound" ); @@ -132,7 +132,7 @@ impl<'a, T> PipelineCommandRef<'a, T> { /// This function must be called for `node` before it is used within a `record_`-function. pub fn set_subresource_access( &mut self, - node: N, + resource: N, subresource: impl Into, access: AccessType, ) -> &mut Self @@ -140,7 +140,8 @@ impl<'a, T> PipelineCommandRef<'a, T> { N: Node + View, SubresourceRange: From, { - self.cmd.set_subresource_access(node, subresource, access); + self.cmd + .set_subresource_access(resource, subresource, access); self } @@ -151,7 +152,7 @@ impl<'a, T> PipelineCommandRef<'a, T> { pub fn shader_resource_access( mut self, descriptor: impl Into, - node: N, + resource: N, access: AccessType, ) -> Self where @@ -160,7 +161,7 @@ impl<'a, T> PipelineCommandRef<'a, T> { SubresourceRange: From, ViewInfo: From, { - self.set_shader_resource_access(descriptor, node, access); + self.set_shader_resource_access(descriptor, resource, access); self } @@ -172,8 +173,8 @@ impl<'a, T> PipelineCommandRef<'a, T> { pub fn shader_subresource_access( mut self, descriptor: impl Into, - node: N, - node_view: impl Into, + resource: N, + subresource: impl Into, access: AccessType, ) -> Self where @@ -182,7 +183,7 @@ impl<'a, T> PipelineCommandRef<'a, T> { SubresourceRange: From, ViewInfo: From, { - self.set_shader_subresource_access(descriptor, node, node_view, access); + self.set_shader_subresource_access(descriptor, resource, subresource, access); self } @@ -192,7 +193,7 @@ impl<'a, T> PipelineCommandRef<'a, T> { /// This function must be called for `node` before it is used within a `record_`-function. pub fn subresource_access( mut self, - node: N, + resource: N, subresource: impl Into, access: AccessType, ) -> Self @@ -200,7 +201,8 @@ impl<'a, T> PipelineCommandRef<'a, T> { N: Node + View, SubresourceRange: From, { - self.cmd.set_subresource_access(node, subresource, access); + self.cmd + .set_subresource_access(resource, subresource, access); self } } diff --git a/src/cmd_ref/ray_trace.rs b/src/cmd_ref/ray_trace.rs index 6bcee8b9..ae2f27f8 100644 --- a/src/cmd_ref/ray_trace.rs +++ b/src/cmd_ref/ray_trace.rs @@ -28,7 +28,7 @@ impl PipelineCommandRef<'_, RayTracePipeline> { #[cfg(debug_assertions)] let dynamic_stack_size = pipeline.inner.info.dynamic_stack_size; - self.cmd.push_execute(move |cmd_buf| { + self.cmd.push_exec(move |cmd_buf| { func(RayTraceCommandBufferRef { cmd_buf, @@ -181,6 +181,7 @@ impl RayTraceCommandBufferRef<'_> { } } } + self } diff --git a/src/driver/buffer.rs b/src/driver/buffer.rs index 8da4a87f..579c976c 100644 --- a/src/driver/buffer.rs +++ b/src/driver/buffer.rs @@ -224,13 +224,12 @@ impl Buffer { pub fn create_from_slice( device: &Device, usage: vk::BufferUsageFlags, - slice: impl AsRef<[u8]>, + data: &[u8], ) -> Result { - let slice = slice.as_ref(); - let info = BufferInfo::host_mem(slice.len() as _, usage); + let info = BufferInfo::host_mem(data.len() as _, usage); let mut buffer = Self::create(device, info)?; - Self::copy_from_slice(&mut buffer, 0, slice); + Self::copy_from_slice(&mut buffer, 0, data); Ok(buffer) } @@ -331,9 +330,11 @@ impl Buffer { /// # Ok(()) } /// ``` #[profiling::function] - pub fn copy_from_slice(&mut self, offset: vk::DeviceSize, slice: impl AsRef<[u8]>) { - let slice = slice.as_ref(); - self.mapped_slice_mut()[offset as _..offset as usize + slice.len()].copy_from_slice(slice); + pub fn copy_from_slice(&mut self, offset: vk::DeviceSize, data: &[u8]) { + let range = offset as _..offset as usize + data.len(); + let mapped_data = self.mapped_slice_mut(); + + mapped_data[range].copy_from_slice(data); } /// Returns the device address of this object. diff --git a/src/driver/instance.rs b/src/driver/instance.rs index 00b1fb4a..781efbf0 100644 --- a/src/driver/instance.rs +++ b/src/driver/instance.rs @@ -6,8 +6,9 @@ use { derive_builder::{Builder, UninitializedFieldError}, log::{debug, error, trace, warn}, std::{ + error::Error, ffi::CStr, - fmt::{Debug, Formatter}, + fmt::{Debug, Display, Formatter}, ops::Deref, os::raw::c_char, sync::Arc, @@ -126,7 +127,77 @@ pub enum ApiVersion { impl ApiVersion { /// The most recent supported version of Vulkan - pub const MAX: Self = Self::Vulkan13; + pub const MAX_SUPPORTED: Self = Self::Vulkan13; + + /// Returns a version parsed from a native Vulkan value. + pub fn try_parse_vk_api_version(version: u32) -> Result { + Self::try_from(version) + } + + /// Vulkan API major version number component. Ex: vX.0.0-0 + /// + /// Always one. + pub fn major(self) -> u32 { + 1 + } + + /// Vulkan API minor version number component. Ex: v0.X.0-0 + pub fn minor(self) -> u32 { + match self { + Self::Vulkan12 => 2, + Self::Vulkan13 => 3, + } + } + + /// Vulkan API minor version number component. Ex: v0.0.X-0 + /// + /// Always zero. + pub fn patch(self) -> u32 { + 0 + } + + /// Returns a native Vulkan value. + pub fn to_vk_api_version(self) -> u32 { + self.into() + } + + /// Vulkan API variant version number component. Ex: v0.0.0-X + /// + /// Always zero. + pub fn variant(self) -> u32 { + 0 + } +} + +impl From for u32 { + fn from(val: ApiVersion) -> Self { + vk::make_api_version(val.variant(), val.major(), val.minor(), val.patch()) + } +} + +impl TryFrom for ApiVersion { + type Error = ParseApiVersionError; + + fn try_from(val: u32) -> Result { + let major = vk::api_version_major(val); + let minor = vk::api_version_minor(val); + let patch = vk::api_version_patch(val); + let variant = vk::api_version_variant(val); + + if variant != 0 || major != 1 || minor < 2 { + return Err(ParseApiVersionError { + major, + minor, + patch, + variant, + }); + } + + Ok(match minor { + 2 => ApiVersion::Vulkan12, + _ => ApiVersion::Vulkan13, + }) + } } /// There is no global state in Vulkan and all per-application state is stored in a VkInstance @@ -291,27 +362,21 @@ impl Instance { vk::Result::ERROR_OUT_OF_HOST_MEMORY => DriverError::OutOfMemory, vk::Result::ERROR_VALIDATION_FAILED_EXT => DriverError::InvalidData, err => { - warn!("unable to enumerate instance version: {err}"); + error!("unable to enumerate instance version: {err}"); DriverError::Unsupported } })? - .map(|version| { - match ( - vk::api_version_major(version), - vk::api_version_minor(version), - ) { - (1, x) if x >= 3 => Some(ApiVersion::Vulkan13), - (1, 2) => Some(ApiVersion::Vulkan12), - (major, minor) => { - warn!("unsupported Vulkan version: {major}.{minor}"); - - None - } - } + .unwrap_or_else(|| { + // The implementation *should* provide a version. If it does not we just send it. + ApiVersion::MAX_SUPPORTED.to_vk_api_version() }) - .ok_or(DriverError::Unsupported)? - .unwrap_or(ApiVersion::MAX); + .try_into() + .map_err(|err| { + warn!("unsupported instance: {err}"); + + DriverError::Unsupported + })?; let instance = unsafe { ash::Instance::load(entry.static_fn(), instance) }; @@ -367,13 +432,11 @@ impl Instance { physical_device: vk::PhysicalDevice, ) -> Result { let physical_device = PhysicalDevice::new(this.clone(), physical_device)?; - let major = vk::api_version_major(physical_device.properties_v1_0.api_version); - let minor = vk::api_version_minor(physical_device.properties_v1_0.api_version); - let supports_vulkan_1_2 = major == 1 && minor >= 2; - - if !supports_vulkan_1_2 { + if let Err(err) = + ApiVersion::try_parse_vk_api_version(physical_device.properties_v1_0.api_version) + { warn!( - "physical device `{}` does not support Vulkan v1.2", + "unsupported physical device `{}`: {err}", physical_device.properties_v1_0.device_name ); @@ -408,22 +471,20 @@ impl Instance { let res = PhysicalDevice::new(this.clone(), physical_device); if let Err(err) = &res { - warn!("unable to create physical device at index {idx}: {err}"); + warn!("unsupported physical device #{idx}: {err}"); } res.ok().filter(|physical_device| { - let major = vk::api_version_major(physical_device.properties_v1_0.api_version); - let minor = vk::api_version_minor(physical_device.properties_v1_0.api_version); - let supports_vulkan_1_2 = major == 1 && minor >= 2; - - if !supports_vulkan_1_2 { - warn!( - "physical device `{}` does not support Vulkan v1.2", + ApiVersion::try_parse_vk_api_version( + physical_device.properties_v1_0.api_version, + ) + .inspect_err(|err| { + debug!( + "unsupported physical device `{}`: {err}", physical_device.properties_v1_0.device_name ); - } - - supports_vulkan_1_2 + }) + .is_ok() }) })) } @@ -539,6 +600,37 @@ impl Drop for InstanceInner { } } +/// Data returned when attempting to parse a Vulkan API version number. +#[derive(Clone, Copy, Debug)] +pub struct ParseApiVersionError { + /// The _major_ version indicates a significant change in the API, which will encompass a wholly + /// new version of the specification. + pub major: u32, + + /// The _minor_ version indicates the incorporation of new functionality into the core + /// specification. + pub minor: u32, + + /// The _patch_ version indicates bug fixes, clarifications, and language improvements have been + /// incorporated into the specification. + pub patch: u32, + + /// The _variant_ indicates the variant of the Vulkan API supported by the implementation. This + /// is always 0 for the Vulkan API. + pub variant: u32, +} + +impl Display for ParseApiVersionError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!( + "v{}.{}.{}-{}", + self.major, self.minor, self.patch, self.variant + )) + } +} + +impl Error for ParseApiVersionError {} + #[doc(hidden)] #[repr(C)] pub struct ReadOnlyInstance { diff --git a/src/lib.rs b/src/lib.rs index 00b5ebc1..745eff06 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -142,7 +142,7 @@ For example, leasing an image: let mut pool = LazyPool::new(&device); let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::STORAGE); -let my_image = pool.lease(info)?; +let my_image = pool.lease_resource(info)?; # Ok(()) } ``` @@ -654,7 +654,7 @@ impl Command { } fn name(&self) -> &str { - self.name.as_deref().unwrap_or("pass") + self.name.as_deref().unwrap_or("command") } } @@ -1246,8 +1246,8 @@ impl Graph { #[profiling::function] pub fn queue(mut self) -> Queue { // The final execution of each pass has no function - for pass in &mut self.cmds { - pass.execs.pop(); + for cmd in &mut self.cmds { + cmd.execs.pop(); } Queue::new(self) diff --git a/src/pool/alias.rs b/src/pool/alias.rs index 789a68d7..bbd1f28a 100644 --- a/src/pool/alias.rs +++ b/src/pool/alias.rs @@ -17,10 +17,23 @@ use { }, }; +#[deprecated = "use AliasWrapper type"] +#[doc(hidden)] +pub type AliasPool = self::AliasWrapper; + /// Allows aliasing of resources using driver information structures. +/// +/// Aliasing is just like leasing except you don't get an instance and somebody else might have +/// already recorded commands against the resource which have not yet completed. pub trait Alias { - /// Aliases a resource. - fn alias(&mut self, info: I) -> Result>, DriverError>; + #[deprecated = "use alias_resource function"] + #[doc(hidden)] + fn alias(&mut self, info: I) -> Result>, DriverError> { + self.alias_resource(info) + } + + /// Returns an alias of a resource. + fn alias_resource(&mut self, info: I) -> Result>, DriverError>; } // Enable aliasing items using their info builder type for convenience @@ -28,10 +41,10 @@ macro_rules! alias_builder { ($info:ident => $item:ident) => { paste::paste! { impl Alias<[<$info Builder>], $item> for T where T: Alias<$info, $item> { - fn alias(&mut self, builder: [<$info Builder>]) -> Result>, DriverError> { + fn alias_resource(&mut self, builder: [<$info Builder>]) -> Result>, DriverError> { let info = builder.build(); - self.alias(info) + self.alias_resource(info) } } } @@ -51,8 +64,8 @@ alias_builder!(ImageInfo => Image); /// All regular leasing and other functionality of the wrapped pool is available through `Deref` and /// `DerefMut`. /// -/// **_NOTE:_** You must call `alias(..)` to use resource aliasing as regular `lease(..)` calls will -/// not inspect or return aliased resources. +/// **_NOTE:_** You must call `alias_resource(..)` to use resource aliasing as regular +/// `lease_resource(..)` calls will not inspect or return aliased resources. /// /// # Details /// @@ -63,7 +76,7 @@ alias_builder!(ImageInfo => Image); /// # Examples /// /// See [`aliasing.rs`](https://github.com/attackgoat/vk-graph/blob/master/examples/aliasing.rs) -pub struct AliasPool { +pub struct AliasWrapper { accel_structs: Vec<( AccelerationStructureInfo, Weak>, @@ -73,7 +86,7 @@ pub struct AliasPool { pool: T, } -impl AliasPool { +impl AliasWrapper { /// Creates a new aliasable wrapper over the given pool. pub fn new(pool: T) -> Self { Self { @@ -89,9 +102,9 @@ impl AliasPool { macro_rules! lease_pass_through { ($info:ident => $item:ident) => { paste::paste! { - impl Pool<$info, $item> for AliasPool where T: Pool<$info, $item> { - fn lease(&mut self, info: $info) -> Result, DriverError> { - self.pool.lease(info) + impl Pool<$info, $item> for AliasWrapper where T: Pool<$info, $item> { + fn lease_resource(&mut self, info: $info) -> Result, DriverError> { + self.pool.lease_resource(info) } } } @@ -102,11 +115,11 @@ lease_pass_through!(AccelerationStructureInfo => AccelerationStructure); lease_pass_through!(BufferInfo => Buffer); lease_pass_through!(ImageInfo => Image); -impl Alias for AliasPool +impl Alias for AliasWrapper where T: Pool, { - fn alias( + fn alias_resource( &mut self, info: AccelerationStructureInfo, ) -> Result>, DriverError> { @@ -129,18 +142,18 @@ where debug!("Leasing new {}", stringify!(AccelerationStructure)); - let item = Arc::new(self.pool.lease(info)?); + let item = Arc::new(self.pool.lease_resource(info)?); self.accel_structs.push((info, Arc::downgrade(&item))); Ok(item) } } -impl Alias for AliasPool +impl Alias for AliasWrapper where T: Pool, { - fn alias(&mut self, info: BufferInfo) -> Result>, DriverError> { + fn alias_resource(&mut self, info: BufferInfo) -> Result>, DriverError> { self.buffers.retain(|(_, item)| item.strong_count() > 0); { @@ -165,18 +178,18 @@ where debug!("Leasing new {}", stringify!(Buffer)); - let item = Arc::new(self.pool.lease(info)?); + let item = Arc::new(self.pool.lease_resource(info)?); self.buffers.push((info, Arc::downgrade(&item))); Ok(item) } } -impl Alias for AliasPool +impl Alias for AliasWrapper where T: Pool, { - fn alias(&mut self, info: ImageInfo) -> Result>, DriverError> { + fn alias_resource(&mut self, info: ImageInfo) -> Result>, DriverError> { self.images.retain(|(_, item)| item.strong_count() > 0); { @@ -206,14 +219,14 @@ where debug!("Leasing new {}", stringify!(Image)); - let item = Arc::new(self.pool.lease(info)?); + let item = Arc::new(self.pool.lease_resource(info)?); self.images.push((info, Arc::downgrade(&item))); Ok(item) } } -impl Deref for AliasPool { +impl Deref for AliasWrapper { type Target = T; fn deref(&self) -> &Self::Target { @@ -221,7 +234,7 @@ impl Deref for AliasPool { } } -impl DerefMut for AliasPool { +impl DerefMut for AliasWrapper { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.pool } diff --git a/src/pool/fifo.rs b/src/pool/fifo.rs index cfd03982..9969c3c2 100644 --- a/src/pool/fifo.rs +++ b/src/pool/fifo.rs @@ -102,7 +102,7 @@ impl FifoPool { impl Pool for FifoPool { #[profiling::function] - fn lease( + fn lease_resource( &mut self, info: AccelerationStructureInfo, ) -> Result, DriverError> { @@ -138,7 +138,7 @@ impl Pool for FifoPool { impl Pool for FifoPool { #[profiling::function] - fn lease(&mut self, info: BufferInfo) -> Result, DriverError> { + fn lease_resource(&mut self, info: BufferInfo) -> Result, DriverError> { let cache_ref = Arc::downgrade(&self.buffer_cache); { @@ -178,7 +178,10 @@ impl Pool for FifoPool { impl Pool for FifoPool { #[profiling::function] - fn lease(&mut self, info: CommandBufferInfo) -> Result, DriverError> { + fn lease_resource( + &mut self, + info: CommandBufferInfo, + ) -> Result, DriverError> { let cache_ref = self .command_buffer_cache .entry(info.queue_family_index) @@ -209,7 +212,10 @@ impl Pool for FifoPool { impl Pool for FifoPool { #[profiling::function] - fn lease(&mut self, info: DescriptorPoolInfo) -> Result, DriverError> { + fn lease_resource( + &mut self, + info: DescriptorPoolInfo, + ) -> Result, DriverError> { let cache_ref = Arc::downgrade(&self.descriptor_pool_cache); { @@ -255,7 +261,7 @@ impl Pool for FifoPool { impl Pool for FifoPool { #[profiling::function] - fn lease(&mut self, info: ImageInfo) -> Result, DriverError> { + fn lease_resource(&mut self, info: ImageInfo) -> Result, DriverError> { let cache_ref = Arc::downgrade(&self.image_cache); { @@ -300,7 +306,7 @@ impl Pool for FifoPool { impl Pool for FifoPool { #[profiling::function] - fn lease(&mut self, info: RenderPassInfo) -> Result, DriverError> { + fn lease_resource(&mut self, info: RenderPassInfo) -> Result, DriverError> { let cache_ref = if let Some(cache) = self.render_pass_cache.get(&info) { cache } else { diff --git a/src/pool/hash.rs b/src/pool/hash.rs index 553ce0ad..9aff5f09 100644 --- a/src/pool/hash.rs +++ b/src/pool/hash.rs @@ -130,7 +130,10 @@ resource_mgmt_fns!("images", "image", ImageInfo, image_cache); impl Pool for HashPool { #[profiling::function] - fn lease(&mut self, info: CommandBufferInfo) -> Result, DriverError> { + fn lease_resource( + &mut self, + info: CommandBufferInfo, + ) -> Result, DriverError> { let cache_ref = self .command_buffer_cache .entry(info.queue_family_index) @@ -160,7 +163,10 @@ impl Pool for HashPool { impl Pool for HashPool { #[profiling::function] - fn lease(&mut self, info: DescriptorPoolInfo) -> Result, DriverError> { + fn lease_resource( + &mut self, + info: DescriptorPoolInfo, + ) -> Result, DriverError> { let cache_ref = self .descriptor_pool_cache .entry(info.clone()) @@ -187,7 +193,7 @@ impl Pool for HashPool { impl Pool for HashPool { #[profiling::function] - fn lease(&mut self, info: RenderPassInfo) -> Result, DriverError> { + fn lease_resource(&mut self, info: RenderPassInfo) -> Result, DriverError> { let cache_ref = if let Some(cache) = self.render_pass_cache.get(&info) { cache } else { @@ -222,7 +228,7 @@ macro_rules! lease { paste::paste! { impl Pool<$info, $item> for HashPool { #[profiling::function] - fn lease(&mut self, info: $info) -> Result, DriverError> { + fn lease_resource(&mut self, info: $info) -> Result, DriverError> { let cache_ref = self.[<$item:snake _cache>].entry(info) .or_insert_with(|| { Cache::new(Mutex::new(Vec::with_capacity(self.info.$capacity))) diff --git a/src/pool/lazy.rs b/src/pool/lazy.rs index 853e5c23..b25925cc 100644 --- a/src/pool/lazy.rs +++ b/src/pool/lazy.rs @@ -160,7 +160,7 @@ impl LazyPool { impl Pool for LazyPool { #[profiling::function] - fn lease( + fn lease_resource( &mut self, info: AccelerationStructureInfo, ) -> Result, DriverError> { @@ -200,7 +200,7 @@ impl Pool for LazyPool { impl Pool for LazyPool { #[profiling::function] - fn lease(&mut self, info: BufferInfo) -> Result, DriverError> { + fn lease_resource(&mut self, info: BufferInfo) -> Result, DriverError> { let cache = self .buffer_cache .entry((info.host_read | info.host_write, info.alignment)) @@ -243,7 +243,10 @@ impl Pool for LazyPool { impl Pool for LazyPool { #[profiling::function] - fn lease(&mut self, info: CommandBufferInfo) -> Result, DriverError> { + fn lease_resource( + &mut self, + info: CommandBufferInfo, + ) -> Result, DriverError> { let cache_ref = self .command_buffer_cache .entry(info.queue_family_index) @@ -273,7 +276,10 @@ impl Pool for LazyPool { impl Pool for LazyPool { #[profiling::function] - fn lease(&mut self, info: DescriptorPoolInfo) -> Result, DriverError> { + fn lease_resource( + &mut self, + info: DescriptorPoolInfo, + ) -> Result, DriverError> { let cache_ref = Arc::downgrade(&self.descriptor_pool_cache); { @@ -319,7 +325,7 @@ impl Pool for LazyPool { impl Pool for LazyPool { #[profiling::function] - fn lease(&mut self, info: ImageInfo) -> Result, DriverError> { + fn lease_resource(&mut self, info: ImageInfo) -> Result, DriverError> { let cache = self .image_cache .entry(info.into()) @@ -356,7 +362,7 @@ impl Pool for LazyPool { impl Pool for LazyPool { #[profiling::function] - fn lease(&mut self, info: RenderPassInfo) -> Result, DriverError> { + fn lease_resource(&mut self, info: RenderPassInfo) -> Result, DriverError> { let cache_ref = if let Some(cache) = self.render_pass_cache.get(&info) { cache } else { diff --git a/src/pool/mod.rs b/src/pool/mod.rs index f2137837..da9a12a1 100644 --- a/src/pool/mod.rs +++ b/src/pool/mod.rs @@ -35,7 +35,7 @@ //! let mut pool = LazyPool::new(&device); //! //! let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::STORAGE); -//! let my_image = pool.lease(info)?; +//! let my_image = pool.lease_resource(info)?; //! //! assert!(my_image.info.usage.contains(vk::ImageUsageFlags::STORAGE)); //! # Ok(()) } @@ -232,8 +232,14 @@ impl Drop for Lease { /// Allows leasing of resources using driver information structures. pub trait Pool { + #[deprecated = "use lease_resource function"] + #[doc(hidden)] + fn lease(&mut self, info: I) -> Result, DriverError> { + self.lease_resource(info) + } + /// Lease a resource. - fn lease(&mut self, info: I) -> Result, DriverError>; + fn lease_resource(&mut self, info: I) -> Result, DriverError>; } // Enable leasing items using their info builder type for convenience @@ -241,10 +247,10 @@ macro_rules! lease_builder { ($info:ident => $item:ident) => { paste::paste! { impl Pool<[<$info Builder>], $item> for T where T: Pool<$info, $item> { - fn lease(&mut self, builder: [<$info Builder>]) -> Result, DriverError> { + fn lease_resource(&mut self, builder: [<$info Builder>]) -> Result, DriverError> { let info = builder.build(); - self.lease(info) + self.lease_resource(info) } } } diff --git a/src/queue.rs b/src/queue.rs index 5112a965..fcf15b97 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -793,7 +793,7 @@ impl Queue { // debug!("{:#?}", info); - Ok(Some(pool.lease(info)?)) + Ok(Some(pool.lease_resource(info)?)) } #[profiling::function] @@ -1653,7 +1653,7 @@ impl Queue { // trace!("{:#?}", info); - pool.lease(RenderPassInfo { + pool.lease_resource(RenderPassInfo { attachments, dependencies, subpasses, @@ -2171,7 +2171,7 @@ impl Queue { debug_assert!(bindings.get(node_idx).is_some()); let binding = unsafe { - // CommandRef enforces this during push_node_access + // CommandRef enforces this during push_resource_access bindings.get_unchecked(node_idx) }; @@ -2830,7 +2830,7 @@ impl Queue { { trace!("submit"); - let mut cmd_buf = pool.lease(CommandBufferInfo::new(queue_family_index as _))?; + let mut cmd_buf = pool.lease_resource(CommandBufferInfo::new(queue_family_index as _))?; cmd_buf.wait_until_executed()?; From 20f0c1e95b979463279b4367a52be4b6f78b6b77 Mon Sep 17 00:00:00 2001 From: John Wells Date: Sat, 28 Feb 2026 09:26:01 -0500 Subject: [PATCH 29/86] Hide resource type --- src/bind.rs | 130 ++++++++++++++++++++++++++++----------------------- src/lib.rs | 12 ++--- src/queue.rs | 29 ++++++------ 3 files changed, 92 insertions(+), 79 deletions(-) diff --git a/src/bind.rs b/src/bind.rs index c0980f91..c259f775 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -33,53 +33,6 @@ pub trait BindGraph { fn bind(self, graph: &mut Graph) -> Self::Node; } -/// TODO -#[allow(missing_docs)] -#[derive(Debug)] -pub enum Resource { - AccelerationStructure(Arc), - AccelerationStructureLease(Arc>), - Buffer(Arc), - BufferLease(Arc>), - Image(Arc), - ImageLease(Arc>), - SwapchainImage(Box), -} - -impl Resource { - pub(super) fn as_driver_accel_struct(&self) -> Option<&AccelerationStructure> { - Some(match self { - Self::AccelerationStructure(resource) => resource, - Self::AccelerationStructureLease(resource) => resource, - _ => return None, - }) - } - - pub(super) fn as_driver_buffer(&self) -> Option<&Buffer> { - Some(match self { - Self::Buffer(resource) => resource, - Self::BufferLease(resource) => resource, - _ => return None, - }) - } - - pub(super) fn as_driver_image(&self) -> Option<&Image> { - Some(match self { - Self::Image(resource) => resource, - Self::ImageLease(resource) => resource, - Self::SwapchainImage(resource) => resource, - _ => return None, - }) - } - - pub(super) fn as_swapchain_image(&self) -> Option<&SwapchainImage> { - Some(match self { - Self::SwapchainImage(resource) => resource, - _ => return None, - }) - } -} - impl BindGraph for SwapchainImage { type Node = SwapchainImageNode; @@ -89,9 +42,9 @@ impl BindGraph for SwapchainImage { //trace!("Node {}: {:?}", res.idx, &self); - graph - .resources - .push(Resource::SwapchainImage(Box::new(self))); + graph.resources.push(Resource { + inner: ResourceInner::SwapchainImage(Box::new(self)), + }); res } @@ -113,7 +66,9 @@ macro_rules! bind_graph_resource { // We will return a new node let res = Self::Node::new(graph.resources.len()); - let resource = Resource::$name(Arc::new(self)); + let resource = Resource { + inner: ResourceInner::$name(Arc::new(self)), + }; graph.resources.push(resource); res @@ -144,7 +99,9 @@ macro_rules! bind_graph_resource { // Return a new node let res = Self::Node::new(graph.resources.len()); - let resource = Resource::$name(self); + let resource = Resource { + inner: ResourceInner::$name(self), + }; graph.resources.push(resource); res @@ -180,7 +137,9 @@ macro_rules! bind_graph_resource { // We will return a new node let res = Self::Node::new(graph.resources.len()); - let resource = Resource::[<$name Lease>](Arc::new(self)); + let resource = Resource { + inner: ResourceInner::[<$name Lease>](Arc::new(self)), + }; graph.resources.push(resource); res @@ -211,7 +170,9 @@ macro_rules! bind_graph_resource { // We will return a new node let res = Self::Node::new(graph.resources.len()); - let resource = Resource::[<$name Lease>](self); + let resource = Resource { + inner: ResourceInner::[<$name Lease>](self), + }; graph.resources.push(resource); res @@ -239,7 +200,7 @@ macro_rules! bind_graph_resource { impl Resource { pub(super) fn [](&self) -> Option<&Arc<$name>> { - let Self::$name(resource) = self else { + let ResourceInner::$name(resource) = &self.inner else { return None; }; @@ -247,7 +208,7 @@ macro_rules! bind_graph_resource { } pub(super) fn [](&mut self) -> Option<&mut Arc<$name>> { - let Self::$name(resource) = self else { + let ResourceInner::$name(resource) = &mut self.inner else { return None; }; @@ -255,15 +216,15 @@ macro_rules! bind_graph_resource { } pub(super) fn [](&self) -> Option<&Arc>> { - let Self::[<$name Lease>](resource) = self else { + let ResourceInner::[<$name Lease>](resource) = &self.inner else { return None }; Some(resource) } - pub(super) fn [](&mut self) -> Option<&Arc>> { - let Self::[<$name Lease>](resource) = self else { + pub(super) fn [](&mut self) -> Option<&mut Arc>> { + let ResourceInner::[<$name Lease>](resource) = &mut self.inner else { return None; }; @@ -351,3 +312,54 @@ macro_rules! bound { bound!(AccelerationStructure); bound!(Buffer); bound!(Image); + +/// TODO +#[derive(Debug)] +pub struct Resource { + pub(crate) inner: ResourceInner, +} + +impl Resource { + pub(super) fn as_driver_accel_struct(&self) -> Option<&AccelerationStructure> { + Some(match &self.inner { + ResourceInner::AccelerationStructure(resource) => resource, + ResourceInner::AccelerationStructureLease(resource) => resource, + _ => return None, + }) + } + + pub(super) fn as_driver_buffer(&self) -> Option<&Buffer> { + Some(match &self.inner { + ResourceInner::Buffer(resource) => resource, + ResourceInner::BufferLease(resource) => resource, + _ => return None, + }) + } + + pub(super) fn as_driver_image(&self) -> Option<&Image> { + Some(match &self.inner { + ResourceInner::Image(resource) => resource, + ResourceInner::ImageLease(resource) => resource, + ResourceInner::SwapchainImage(resource) => resource, + _ => return None, + }) + } + + pub(super) fn as_swapchain_image(&self) -> Option<&SwapchainImage> { + Some(match &self.inner { + ResourceInner::SwapchainImage(resource) => resource, + _ => return None, + }) + } +} + +#[derive(Debug)] +pub(crate) enum ResourceInner { + AccelerationStructure(Arc), + AccelerationStructureLease(Arc>), + Buffer(Arc), + BufferLease(Arc>), + Image(Arc), + ImageLease(Arc>), + SwapchainImage(Box), +} diff --git a/src/lib.rs b/src/lib.rs index 745eff06..22c733a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1344,11 +1344,11 @@ pub(crate) mod deprecated { impl Info for SwapchainImageNode { type Type = ImageInfo; - fn info(&self, bindings: &[Resource]) -> Self::Type + fn info(&self, resources: &[Resource]) -> Self::Type where Self: Node, { - bindings[self.idx].as_swapchain_image().unwrap().info + resources[self.idx].as_swapchain_image().unwrap().info } } @@ -1358,22 +1358,22 @@ pub(crate) mod deprecated { impl Info for [<$name Node>] { type Type = [<$name Info>]; - fn info(&self, bindings: &[Resource]) -> Self::Type + fn info(&self, resources: &[Resource]) -> Self::Type where Self: Node, { - bindings[self.idx].[]().unwrap().info + resources[self.idx].[]().unwrap().info } } impl Info for [<$name LeaseNode>] { type Type = [<$name Info>]; - fn info(&self, bindings: &[Resource]) -> Self::Type + fn info(&self, resources: &[Resource]) -> Self::Type where Self: Node, { - bindings[self.idx].[]().unwrap().info + resources[self.idx].[]().unwrap().info } } } diff --git a/src/queue.rs b/src/queue.rs index fcf15b97..5b7b0772 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -1,6 +1,7 @@ use { super::{ Attachment, Command, ExecutionPipeline, Graph, Node, NodeIndex, Resource, + bind::ResourceInner, cmd_ref::{SubresourceAccess, SubresourceRange}, }, crate::{ @@ -1933,9 +1934,9 @@ impl Queue { for (node_idx, accesses) in accesses { let binding = &bindings[*node_idx]; - match binding { - Resource::AccelerationStructure(..) - | Resource::AccelerationStructureLease(..) => { + match &binding.inner { + ResourceInner::AccelerationStructure(..) + | ResourceInner::AccelerationStructureLease(..) => { let Some(accel_struct) = binding.as_driver_accel_struct() else { #[cfg(debug_assertions)] unreachable!(); @@ -1958,7 +1959,7 @@ impl Queue { ); tls.prev_accesses.push(prev_access); } - Resource::Buffer(..) | Resource::BufferLease(..) => { + ResourceInner::Buffer(..) | ResourceInner::BufferLease(..) => { let Some(buffer) = binding.as_driver_buffer() else { #[cfg(debug_assertions)] unreachable!(); @@ -1991,9 +1992,9 @@ impl Queue { } } } - Resource::Image(..) - | Resource::ImageLease(..) - | Resource::SwapchainImage(..) => { + ResourceInner::Image(..) + | ResourceInner::ImageLease(..) + | ResourceInner::SwapchainImage(..) => { let Some(image) = binding.as_driver_image() else { #[cfg(debug_assertions)] unreachable!(); @@ -2175,9 +2176,9 @@ impl Queue { bindings.get_unchecked(node_idx) }; - match binding { - Resource::AccelerationStructure(..) - | Resource::AccelerationStructureLease(..) => { + match &binding.inner { + ResourceInner::AccelerationStructure(..) + | ResourceInner::AccelerationStructureLease(..) => { let Some(accel_struct) = binding.as_driver_accel_struct() else { #[cfg(debug_assertions)] unreachable!(); @@ -2190,7 +2191,7 @@ impl Queue { AccelerationStructure::access(accel_struct, AccessType::Nothing); } - Resource::Buffer(..) | Resource::BufferLease(..) => { + ResourceInner::Buffer(..) | ResourceInner::BufferLease(..) => { let Some(buffer) = binding.as_driver_buffer() else { #[cfg(debug_assertions)] unreachable!(); @@ -2221,9 +2222,9 @@ impl Queue { for _ in Buffer::access(buffer, AccessType::Nothing, access_range) {} } } - Resource::Image(..) - | Resource::ImageLease(..) - | Resource::SwapchainImage(..) => { + ResourceInner::Image(..) + | ResourceInner::ImageLease(..) + | ResourceInner::SwapchainImage(..) => { let Some(image) = binding.as_driver_image() else { #[cfg(debug_assertions)] unreachable!(); From 6adbd464423d15940eef588b996ed670af963ee2 Mon Sep 17 00:00:00 2001 From: John Wells Date: Mon, 2 Mar 2026 21:03:41 -0500 Subject: [PATCH 30/86] Clean-up --- contrib/vk-graph-prelude/src/lib.rs | 2 +- examples/bindless.rs | 2 +- examples/fuzzer.rs | 2 +- examples/mip_compute.rs | 4 +- examples/msaa.rs | 2 +- examples/multipass.rs | 2 +- examples/ray_omni.rs | 2 +- examples/shader-toy/src/main.rs | 2 +- examples/skeletal-anim/src/main.rs | 2 +- examples/triangle.rs | 2 +- examples/vr/src/main.rs | 2 +- examples/vsm_omni.rs | 2 +- src/bind.rs | 2 +- src/{cmd_ref => cmd}/bind.rs | 31 ++- src/{cmd_ref => cmd}/cmd_buf.rs | 170 +++++++++++- src/{cmd_ref => cmd}/compute.rs | 10 +- src/{cmd_ref => cmd}/graphic.rs | 48 ++-- src/{cmd_ref => cmd}/mod.rs | 81 +++++- src/{cmd_ref => cmd}/pipeline.rs | 100 ++++++- src/{cmd_ref => cmd}/ray_trace.rs | 10 +- src/driver/accel_struct.rs | 8 +- src/driver/buffer.rs | 5 + src/driver/cmd_buf.rs | 5 + src/driver/compute.rs | 48 +++- src/driver/device.rs | 118 +++++--- src/driver/graphic.rs | 45 ++- src/driver/image.rs | 5 + src/driver/instance.rs | 7 +- src/driver/physical_device.rs | 25 +- src/driver/ray_trace.rs | 40 ++- src/driver/shader.rs | 53 ++++ src/driver/swapchain.rs | 30 ++ src/lib.rs | 407 ++++++++++++++++++++++++++-- src/pool/fifo.rs | 17 +- src/pool/hash.rs | 17 +- src/pool/lazy.rs | 17 +- src/queue.rs | 10 +- 37 files changed, 1160 insertions(+), 175 deletions(-) rename src/{cmd_ref => cmd}/bind.rs (70%) rename src/{cmd_ref => cmd}/cmd_buf.rs (81%) rename src/{cmd_ref => cmd}/compute.rs (98%) rename src/{cmd_ref => cmd}/graphic.rs (98%) rename src/{cmd_ref => cmd}/mod.rs (87%) rename src/{cmd_ref => cmd}/pipeline.rs (72%) rename src/{cmd_ref => cmd}/ray_trace.rs (98%) diff --git a/contrib/vk-graph-prelude/src/lib.rs b/contrib/vk-graph-prelude/src/lib.rs index 5e1f679b..7cad28ed 100644 --- a/contrib/vk-graph-prelude/src/lib.rs +++ b/contrib/vk-graph-prelude/src/lib.rs @@ -4,7 +4,7 @@ pub use vk_graph::{ BindGraph, Bound, ClearColorValue, Graph, - cmd_ref::{ + cmd::{ BuildAccelerationStructureIndirectInfo, BuildAccelerationStructureInfo, CommandRef, LoadOp, PipelineCommandRef, StoreOp, UpdateAccelerationStructureIndirectInfo, UpdateAccelerationStructureInfo, diff --git a/examples/bindless.rs b/examples/bindless.rs index 126c951e..370d856f 100644 --- a/examples/bindless.rs +++ b/examples/bindless.rs @@ -4,7 +4,7 @@ use { bytemuck::{Pod, Zeroable, cast_slice}, clap::Parser, std::sync::Arc, - vk_graph::cmd_ref::LoadOp, + vk_graph::cmd::LoadOp, vk_graph_prelude::*, vk_graph_window::{WindowBuilder, WindowError}, vk_shader_macros::glsl, diff --git a/examples/fuzzer.rs b/examples/fuzzer.rs index 5288eb46..e4a10842 100644 --- a/examples/fuzzer.rs +++ b/examples/fuzzer.rs @@ -26,7 +26,7 @@ use { log::debug, rand::{Rng, rng, seq::IndexedRandom}, std::mem::size_of, - vk_graph::cmd_ref::{BuildAccelerationStructureInfo, LoadOp}, + vk_graph::cmd::{BuildAccelerationStructureInfo, LoadOp}, vk_graph_prelude::*, vk_graph_window::{FrameContext, WindowBuilder, WindowError}, vk_shader_macros::glsl, diff --git a/examples/mip_compute.rs b/examples/mip_compute.rs index b6107520..223401d8 100644 --- a/examples/mip_compute.rs +++ b/examples/mip_compute.rs @@ -121,7 +121,7 @@ fn main() -> Result<(), DriverError> { graph.copy_image_to_buffer_region( depth_pyramid, depth_pixel, - vk::BufferImageCopy { + [vk::BufferImageCopy { buffer_offset: 0, buffer_row_length: 1, buffer_image_height: 1, @@ -137,7 +137,7 @@ fn main() -> Result<(), DriverError> { height: 1, depth: 1, }, - }, + }], ); let depth_pixel = graph.resource(depth_pixel).clone(); diff --git a/examples/msaa.rs b/examples/msaa.rs index 20b040b2..3e1335d6 100644 --- a/examples/msaa.rs +++ b/examples/msaa.rs @@ -6,7 +6,7 @@ use { glam::{Mat4, Vec3}, log::warn, std::{mem::size_of, sync::Arc}, - vk_graph::cmd_ref::LoadOp, + vk_graph::cmd::LoadOp, vk_graph_prelude::*, vk_graph_window::WindowBuilder, vk_shader_macros::glsl, diff --git a/examples/multipass.rs b/examples/multipass.rs index bcf89617..37112d6a 100644 --- a/examples/multipass.rs +++ b/examples/multipass.rs @@ -5,7 +5,7 @@ use { clap::Parser, glam::{Mat4, Vec3, Vec4, vec3}, std::sync::Arc, - vk_graph::{cmd_ref::LoadOp, driver::graphic::DepthStencilInfo}, + vk_graph::{cmd::LoadOp, driver::graphic::DepthStencilInfo}, vk_graph_prelude::*, vk_graph_window::WindowBuilder, vk_shader_macros::glsl, diff --git a/examples/ray_omni.rs b/examples/ray_omni.rs index 4d198b94..44769dbd 100644 --- a/examples/ray_omni.rs +++ b/examples/ray_omni.rs @@ -14,7 +14,7 @@ use { sync::Arc, }, tobj::{GPU_LOAD_OPTIONS, load_obj}, - vk_graph::cmd_ref::LoadOp, + vk_graph::cmd::LoadOp, vk_graph_prelude::*, vk_graph_window::WindowBuilder, vk_shader_macros::glsl, diff --git a/examples/shader-toy/src/main.rs b/examples/shader-toy/src/main.rs index 9e9a4067..d3c13776 100644 --- a/examples/shader-toy/src/main.rs +++ b/examples/shader-toy/src/main.rs @@ -39,7 +39,7 @@ use { pak::{Pak, PakBuf}, std::time::Instant, vk_graph::{ - cmd_ref::{LoadOp, StoreOp}, + cmd::{LoadOp, StoreOp}, driver::{ ash::vk, graphic::{GraphicPipeline, GraphicPipelineInfo}, diff --git a/examples/skeletal-anim/src/main.rs b/examples/skeletal-anim/src/main.rs index dd2c0cc9..3961e300 100644 --- a/examples/skeletal-anim/src/main.rs +++ b/examples/skeletal-anim/src/main.rs @@ -17,7 +17,7 @@ use { time::{Duration, Instant}, }, vk_graph::{ - cmd_ref::{LoadOp, StoreOp}, + cmd::{LoadOp, StoreOp}, driver::{ ash::vk, buffer::{Buffer, BufferInfo}, diff --git a/examples/triangle.rs b/examples/triangle.rs index 91d0e6f4..f5246c2d 100644 --- a/examples/triangle.rs +++ b/examples/triangle.rs @@ -4,7 +4,7 @@ use { bytemuck::cast_slice, clap::Parser, std::sync::Arc, - vk_graph::cmd_ref::LoadOp, + vk_graph::cmd::LoadOp, vk_graph_prelude::*, vk_graph_window::{WindowBuilder, WindowError}, vk_shader_macros::glsl, diff --git a/examples/vr/src/main.rs b/examples/vr/src/main.rs index 0ad74f5f..d047ce7b 100644 --- a/examples/vr/src/main.rs +++ b/examples/vr/src/main.rs @@ -21,7 +21,7 @@ use { }, tobj::{load_obj, GPU_LOAD_OPTIONS}, vk_graph::{ - cmd_ref::{LoadOp, StoreOp}, + cmd::{LoadOp, StoreOp}, driver::{ ash::vk::{self}, buffer::{Buffer, BufferInfo}, diff --git a/examples/vsm_omni.rs b/examples/vsm_omni.rs index 92f6bf85..df98a4b6 100644 --- a/examples/vsm_omni.rs +++ b/examples/vsm_omni.rs @@ -13,7 +13,7 @@ use { sync::Arc, }, tobj::{GPU_LOAD_OPTIONS, load_obj}, - vk_graph::cmd_ref::LoadOp, + vk_graph::cmd::LoadOp, vk_graph_prelude::*, vk_graph_window::WindowBuilder, vk_shader_macros::glsl, diff --git a/src/bind.rs b/src/bind.rs index c259f775..e996a989 100644 --- a/src/bind.rs +++ b/src/bind.rs @@ -18,7 +18,7 @@ use { /// A trait for resources which may be bound to a `Graph`. /// /// See [`Graph::bind_resource`] and -/// [`CommandRef::bind_resource`](super::cmd_ref::CommandRef::bind_resource) for details. +/// [`CommandRef::bind_resource`](super::cmd::CommandRef::bind_resource) for details. pub trait BindGraph { /// The resource handle type. type Node; diff --git a/src/cmd_ref/bind.rs b/src/cmd/bind.rs similarity index 70% rename from src/cmd_ref/bind.rs rename to src/cmd/bind.rs index a36c86bc..7d57037d 100644 --- a/src/cmd_ref/bind.rs +++ b/src/cmd/bind.rs @@ -9,7 +9,7 @@ use { /// A trait for pipelines which may be bound to a `CommandRef`. /// -/// See [`CommandRef::bind_pipeline`](super::cmd_ref::CommandRef::bind_pipeline) for details. +/// See [`CommandRef::bind_pipeline`](super::cmd::CommandRef::bind_pipeline) for details. pub trait BindCommand<'a> { /// The resource reference type. type Ref; @@ -27,32 +27,37 @@ macro_rules! bind_cmd_pipeline { type Ref = PipelineCommandRef<'a, [<$name Pipeline>]>; fn bind_cmd(self, mut cmd: CommandRef<'a>) -> Self::Ref { - let cmd_ref = cmd.cmd_mut(); - if cmd_ref.execs.last().unwrap().pipeline.is_some() { - // Binding from PipelineCommandRef -> PipelineCommandRef (changing shaders) - cmd_ref.execs.push(Default::default()); - } + { + let cmd = cmd.cmd_mut(); + if cmd.execs.last().unwrap().pipeline.is_some() { + cmd.execs.push(Default::default()); + } - cmd_ref.execs.last_mut().unwrap().pipeline = Some(ExecutionPipeline::$name(self.clone())); + cmd.execs.last_mut().unwrap().pipeline + = Some(ExecutionPipeline::$name(self.clone())); + } Self::Ref { __: PhantomData, cmd, } } + } impl<'a> BindCommand<'a> for [<$name Pipeline>] { type Ref = PipelineCommandRef<'a, [<$name Pipeline>]>; fn bind_cmd(self, mut cmd: CommandRef<'a>) -> Self::Ref { - let cmd_ref = cmd.cmd_mut(); - if cmd_ref.execs.last().unwrap().pipeline.is_some() { - // Binding from PipelineCommandRef -> PipelineCommandRef (changing shaders) - cmd_ref.execs.push(Default::default()); - } + { + let cmd = cmd.cmd_mut(); + if cmd.execs.last().unwrap().pipeline.is_some() { + cmd.execs.push(Default::default()); + } - cmd_ref.execs.last_mut().unwrap().pipeline = Some(ExecutionPipeline::$name(self)); + cmd.execs.last_mut().unwrap().pipeline + = Some(ExecutionPipeline::$name(self)); + } Self::Ref { __: PhantomData, diff --git a/src/cmd_ref/cmd_buf.rs b/src/cmd/cmd_buf.rs similarity index 81% rename from src/cmd_ref/cmd_buf.rs rename to src/cmd/cmd_buf.rs index 5b2ecf64..f0421405 100644 --- a/src/cmd_ref/cmd_buf.rs +++ b/src/cmd/cmd_buf.rs @@ -40,7 +40,7 @@ use crate::Execution; /// # let mut my_graph = Graph::default(); /// # let info = AccelerationStructureInfo::blas(1); /// my_graph.begin_cmd() -/// .record_accel_struct(move |accel_struct, nodes| { +/// .record_cmd_buf(move |cmd_buf| { /// // During this closure we have access to the build and update methods /// }); /// # Ok(()) } @@ -90,7 +90,7 @@ impl<'a> CommandBufferRef<'a> { /// /// ```no_run /// # use ash::vk; - /// # use vk_graph::cmd_ref::BuildAccelerationStructureInfo; + /// # use vk_graph::cmd::BuildAccelerationStructureInfo; /// # use vk_graph::driver::{sync::AccessType, DriverError}; /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureGeometry, AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, AccelerationStructureInfo, DeviceOrHostAddress}; @@ -694,3 +694,169 @@ impl UpdateAccelerationStructureInfo { } } } + +#[allow(missing_docs)] +#[allow(deprecated)] +mod deprecated { + use ash::vk; + + use crate::{ + cmd::{ + BuildAccelerationStructureIndirectInfo, BuildAccelerationStructureInfo, + CommandBufferRef, UpdateAccelerationStructureIndirectInfo, + UpdateAccelerationStructureInfo, + }, + driver::accel_struct::{ + AccelerationStructureGeometry, AccelerationStructureGeometryInfo, DeviceOrHostAddress, + }, + graph::pass_ref::{ + AccelerationStructureBuildInfo, AccelerationStructureIndirectBuildInfo, + AccelerationStructureIndirectUpdateInfo, AccelerationStructureUpdateInfo, + }, + node::AnyAccelerationStructureNode, + }; + + impl<'a> CommandBufferRef<'a> { + #[deprecated = "use build_accel_struct function"] + #[doc(hidden)] + pub fn build_structure( + &self, + info: &AccelerationStructureGeometryInfo<( + AccelerationStructureGeometry, + vk::AccelerationStructureBuildRangeInfoKHR, + )>, + accel_struct: impl Into, + scratch_addr: impl Into, + ) -> &Self { + self.build_accel_struct(&[BuildAccelerationStructureInfo { + accel_struct: accel_struct.into(), + build_data: info.clone(), + scratch_addr: scratch_addr.into(), + }]) + } + + #[deprecated = "use build_accel_struct_indirect function"] + #[doc(hidden)] + pub fn build_structure_indirect( + &self, + info: &AccelerationStructureGeometryInfo, + accel_struct: impl Into, + scratch_addr: impl Into, + range_base: vk::DeviceAddress, + range_stride: u32, + ) -> &Self { + self.build_accel_struct_indirect(&[BuildAccelerationStructureIndirectInfo { + accel_struct: accel_struct.into(), + build_data: info.clone(), + range_base, + range_stride, + scratch_data: scratch_addr.into(), + }]) + } + + #[deprecated = "use build_accel_struct function"] + #[doc(hidden)] + pub fn build_structures(&self, infos: &[AccelerationStructureBuildInfo]) -> &Self { + for info in infos { + self.build_structure(&info.build_data, info.accel_struct, info.scratch_addr); + } + + self + } + + #[deprecated = "use build_accel_struct_indirect function"] + #[doc(hidden)] + pub fn build_structures_indirect( + &self, + infos: &[AccelerationStructureIndirectBuildInfo], + ) -> &Self { + for info in infos { + self.build_structure_indirect( + &info.build_data, + info.accel_struct, + info.scratch_data, + info.range_base, + info.range_stride, + ); + } + + self + } + + #[deprecated = "use update_accel_struct function"] + #[doc(hidden)] + pub fn update_structure( + &self, + info: &AccelerationStructureGeometryInfo<( + AccelerationStructureGeometry, + vk::AccelerationStructureBuildRangeInfoKHR, + )>, + src_accel_struct: impl Into, + dst_accel_struct: impl Into, + scratch_addr: impl Into, + ) -> &Self { + self.update_accel_struct(&[UpdateAccelerationStructureInfo { + src_accel_struct: src_accel_struct.into(), + dst_accel_struct: dst_accel_struct.into(), + update_data: info.clone(), + scratch_addr: scratch_addr.into(), + }]) + } + + #[deprecated = "use update_accel_struct_indirect function"] + #[doc(hidden)] + pub fn update_structure_indirect( + &self, + info: &AccelerationStructureGeometryInfo, + src_accel_struct: impl Into, + dst_accel_struct: impl Into, + scratch_addr: impl Into, + range_base: vk::DeviceAddress, + range_stride: u32, + ) -> &Self { + self.update_accel_struct_indirect(&[UpdateAccelerationStructureIndirectInfo { + src_accel_struct: src_accel_struct.into(), + dst_accel_struct: dst_accel_struct.into(), + update_data: info.clone(), + range_base, + range_stride, + scratch_addr: scratch_addr.into(), + }]) + } + + #[deprecated = "use update_accel_struct function"] + #[doc(hidden)] + pub fn update_structures(&self, infos: &[AccelerationStructureUpdateInfo]) -> &Self { + for info in infos { + self.update_structure( + &info.update_data, + info.src_accel_struct, + info.dst_accel_struct, + info.scratch_addr, + ); + } + + self + } + + #[deprecated = "use update_accel_struct_indirect function"] + #[doc(hidden)] + pub fn update_structures_indirect( + &self, + infos: &[AccelerationStructureIndirectUpdateInfo], + ) -> &Self { + for info in infos { + self.update_structure_indirect( + &info.update_data, + info.src_accel_struct, + info.dst_accel_struct, + info.scratch_addr, + info.range_base, + info.range_stride, + ); + } + + self + } + } +} diff --git a/src/cmd_ref/compute.rs b/src/cmd/compute.rs similarity index 98% rename from src/cmd_ref/compute.rs rename to src/cmd/compute.rs index fdbb3e3c..6e567cbf 100644 --- a/src/cmd_ref/compute.rs +++ b/src/cmd/compute.rs @@ -356,7 +356,7 @@ impl PipelineCommandRef<'_, ComputePipeline> { mod deprecated { use { crate::{ - cmd_ref::{ + cmd::{ Descriptor, PipelineCommandRef, SubresourceRange, View, ViewInfo, compute::ComputeCommandBufferRef, }, @@ -367,6 +367,14 @@ mod deprecated { vk_sync::AccessType, }; + impl ComputeCommandBufferRef<'_> { + #[deprecated = "use push_constants function"] + #[doc(hidden)] + pub fn push_constants_offset(&self, offset: u32, data: &[u8]) -> &Self { + self.push_constants(offset, data) + } + } + impl PipelineCommandRef<'_, ComputePipeline> { #[deprecated = "use shader_resource_access function with AccessType::ComputeShaderReadOther"] #[doc(hidden)] diff --git a/src/cmd_ref/graphic.rs b/src/cmd/graphic.rs similarity index 98% rename from src/cmd_ref/graphic.rs rename to src/cmd/graphic.rs index fee6f06d..dabc608a 100644 --- a/src/cmd_ref/graphic.rs +++ b/src/cmd/graphic.rs @@ -29,24 +29,20 @@ pub enum LoadOp { impl LoadOp { /// A load operation which results in a color attachment filled with rgb zeros and alpha ones. - pub const CLEAR_BLACK_ALPHA_ONE: Self = - Self::Clear(ClearColorValue::Float32([0.0, 0.0, 0.0, 1.0])); + pub const CLEAR_BLACK_ALPHA_ONE: Self = Self::Clear(ClearColorValue::BLACK_ALPHA_ONE); /// A load operation which results in a color attachment filled with zeros. - pub const CLEAR_BLACK_ALPHA_ZERO: Self = - Self::Clear(ClearColorValue::Float32([0.0, 0.0, 0.0, 0.0])); + pub const CLEAR_BLACK_ALPHA_ZERO: Self = Self::Clear(ClearColorValue::BLACK_ALPHA_ZERO); /// A load operation which results in a color attachment filled with rgb zeros and alpha ones. - pub const CLEAR_WHITE_ALPHA_ONE: Self = - Self::Clear(ClearColorValue::Float32([1.0, 1.0, 1.0, 1.0])); + pub const CLEAR_WHITE_ALPHA_ONE: Self = Self::Clear(ClearColorValue::WHITE_ALPHA_ONE); - /// A load operation which results in a color attachment filled with rgba ones and alpha zeros. - pub const CLEAR_WHITE_ALPHA_ZERO: Self = - Self::Clear(ClearColorValue::Float32([1.0, 1.0, 1.0, 0.0])); + /// A load operation which results in a color attachment filled with rgb ones and alpha zeros. + pub const CLEAR_WHITE_ALPHA_ZERO: Self = Self::Clear(ClearColorValue::WHITE_ALPHA_ZERO); /// Convenience constructor for clear color values. pub fn clear_rgba(r: f32, g: f32, b: f32, a: f32) -> Self { - Self::Clear(ClearColorValue::Float32([r, g, b, a])) + Self::Clear(ClearColorValue::rgba(r, g, b, a)) } } @@ -280,28 +276,34 @@ impl GraphicCommandBufferRef<'_> { where N: Into, { + #[derive(Default)] + struct Tls { + buffers: Vec, + offsets: Vec, + } + thread_local! { - static BUFFERS_OFFSETS: RefCell<(Vec, Vec)> = Default::default(); + static TLS: RefCell = Default::default(); } - BUFFERS_OFFSETS.with_borrow_mut(|(buffers, offsets)| { - buffers.clear(); - offsets.clear(); + TLS.with_borrow_mut(|tls| { + tls.buffers.clear(); + tls.offsets.clear(); for (buffer, offset) in buffer_offsets { let buffer = buffer.into(); let buffer = self.resource(buffer); - buffers.push(buffer.handle); - offsets.push(offset); + tls.buffers.push(buffer.handle); + tls.offsets.push(offset); } unsafe { self.cmd_buf.device.cmd_bind_vertex_buffers( self.cmd_buf.handle, first_binding, - buffers.as_slice(), - offsets.as_slice(), + tls.buffers.as_slice(), + tls.offsets.as_slice(), ); } }); @@ -1127,7 +1129,7 @@ mod deprecated { use { crate::{ Attachment, ClearColorValue, SubresourceAccess, - cmd_ref::{ + cmd::{ AttachmentIndex, Descriptor, PipelineCommandRef, SubresourceRange, View, ViewInfo, graphic::GraphicCommandBufferRef, }, @@ -1145,6 +1147,14 @@ mod deprecated { vk_sync::AccessType, }; + impl GraphicCommandBufferRef<'_> { + #[deprecated = "use push_constants function"] + #[doc(hidden)] + pub fn push_constants_offset(&self, offset: u32, data: &[u8]) -> &Self { + self.push_constants(offset, data) + } + } + // Attachment functions from previous version impl PipelineCommandRef<'_, GraphicPipeline> { #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] diff --git a/src/cmd_ref/mod.rs b/src/cmd/mod.rs similarity index 87% rename from src/cmd_ref/mod.rs rename to src/cmd/mod.rs index 086aa0a7..8d867e2a 100644 --- a/src/cmd_ref/mod.rs +++ b/src/cmd/mod.rs @@ -27,7 +27,7 @@ use { SwapchainImageNode, }, crate::driver::{ - accel_struct::AccelerationStructureRange, buffer::BufferSubresourceRange, + accel_struct::AccelerationStructureSubresourceRange, buffer::BufferSubresourceRange, image::ImageViewInfo, }, ash::vk, @@ -327,7 +327,7 @@ impl From<(DescriptorSetIndex, BindingIndex, [BindingOffset; 1])> for Descriptor #[doc(hidden)] pub enum SubresourceRange { /// Acceleration structures are bound whole. - AccelerationStructure(AccelerationStructureRange), + AccelerationStructure(AccelerationStructureSubresourceRange), /// Images may be partially bound. Image(vk::ImageSubresourceRange), @@ -346,9 +346,9 @@ impl SubresourceRange { } } -impl From for SubresourceRange { - fn from(_: AccelerationStructureRange) -> Self { - Self::AccelerationStructure(AccelerationStructureRange) +impl From for SubresourceRange { + fn from(_: AccelerationStructureSubresourceRange) -> Self { + Self::AccelerationStructure(AccelerationStructureSubresourceRange) } } @@ -397,7 +397,7 @@ macro_rules! view_accel_struct { ($name:ident) => { impl View for $name { type Info = Self::Range; - type Range = AccelerationStructureRange; + type Range = AccelerationStructureSubresourceRange; fn info(&self, resources: &[Resource]) -> Self::Info where @@ -484,7 +484,7 @@ view_image!(SwapchainImageNode); #[doc(hidden)] pub enum ViewInfo { /// Acceleration structures are always whole resources. - AccelerationStructure(AccelerationStructureRange), + AccelerationStructure(AccelerationStructureSubresourceRange), /// Images may be interpreted as differently formatted images. Image(ImageViewInfo), @@ -509,8 +509,8 @@ impl ViewInfo { } } -impl From for ViewInfo { - fn from(info: AccelerationStructureRange) -> Self { +impl From for ViewInfo { + fn from(info: AccelerationStructureSubresourceRange) -> Self { Self::AccelerationStructure(info) } } @@ -536,11 +536,13 @@ impl From> for ViewInfo { } } +#[allow(deprecated)] #[allow(unused)] mod deprecated { use { crate::{ - cmd_ref::{CommandBufferRef, CommandRef, SubresourceRange, View}, + BindGraph, Graph, + cmd::{CommandBufferRef, CommandRef, SubresourceRange, View}, deprecated::Info, node::Node, }, @@ -549,6 +551,16 @@ mod deprecated { }; impl<'a> CommandRef<'a> { + #[deprecated = "use resource_access function"] + #[doc(hidden)] + pub fn access_node(mut self, node: N, access: AccessType) -> Self + where + N: Node + View, + SubresourceRange: From, + { + self.resource_access(node, access) + } + #[deprecated = "use set_resource_access function"] #[doc(hidden)] pub fn access_node_mut(&mut self, node: N, access: AccessType) -> &mut Self @@ -560,6 +572,38 @@ mod deprecated { self } + #[deprecated = "use subresource_access function"] + #[doc(hidden)] + pub fn access_node_subrange( + mut self, + node: N, + access: AccessType, + subresource: impl Into, + ) -> Self + where + N: Node + View, + SubresourceRange: From, + { + self.access_node_subrange_mut(node, access, subresource); + self + } + + #[deprecated = "use set_subresource_access function"] + #[doc(hidden)] + pub fn access_node_subrange_mut( + &mut self, + node: N, + access: AccessType, + subresource: impl Into, + ) -> &mut Self + where + N: Node + View, + SubresourceRange: From, + { + self.set_subresource_access(node, subresource, access); + self + } + #[deprecated = "use resource_access function"] #[doc(hidden)] pub fn access_resource(mut self, node: N, access: AccessType) -> Self @@ -585,6 +629,15 @@ mod deprecated { self.subresource_access(node, subresource, access) } + #[deprecated = "use bind_resource function"] + #[doc(hidden)] + pub fn bind_node(&mut self, resource: R) -> R::Node + where + R: BindGraph, + { + self.bind_resource(resource) + } + #[deprecated = "use device_address function of resource function result"] #[doc(hidden)] pub fn node_device_address(&self, node: impl Node) -> vk::DeviceAddress { @@ -607,7 +660,7 @@ mod deprecated { #[deprecated = "use record_cmd_buf function"] #[doc(hidden)] - pub fn record_accel_struct( + pub fn record_acceleration( mut self, func: impl FnOnce(CommandBufferRef<'_>, ()) + Send + 'static, ) -> Self { @@ -617,5 +670,11 @@ mod deprecated { self } + + #[deprecated = "use end_cmd function"] + #[doc(hidden)] + pub fn submit_pass(self) -> &'a mut Graph { + self.end_cmd() + } } } diff --git a/src/cmd_ref/pipeline.rs b/src/cmd/pipeline.rs similarity index 72% rename from src/cmd_ref/pipeline.rs rename to src/cmd/pipeline.rs index 31fbb871..530c6a15 100644 --- a/src/cmd_ref/pipeline.rs +++ b/src/cmd/pipeline.rs @@ -207,12 +207,15 @@ impl<'a, T> PipelineCommandRef<'a, T> { } } +#[allow(deprecated)] #[allow(unused)] mod deprecated { use { crate::{ - cmd_ref::{PipelineCommandRef, SubresourceRange, View}, + BindGraph, Bound, Graph, + cmd::{Descriptor, PipelineCommandRef, SubresourceRange, View}, deprecated::Info, + graph::pass_ref::ViewType, node::Node, }, ash::vk, @@ -220,9 +223,65 @@ mod deprecated { }; impl<'a, T> PipelineCommandRef<'a, T> { + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn access_descriptor( + self, + descriptor: impl Into, + node: N, + access: AccessType, + ) -> Self + where + N: Node + Info + View, + ViewType: From<::Info>, + ::Info: Copy + From<::Type>, + ::Range: From<::Info>, + { + let view_info = View::info(&node, &self.cmd.graph.resources); + + self.access_descriptor_as(descriptor, node, access, view_info) + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn access_descriptor_as( + self, + descriptor: impl Into, + node: N, + access: AccessType, + view_info: impl Into, + ) -> Self + where + N: View, + ::Info: Copy + Into, + ::Range: From<::Info>, + { + let view_info = view_info.into(); + let subresource = ::Range::from(view_info); + + self.access_descriptor_subrange(descriptor, node, access, view_info, subresource) + } + + #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[doc(hidden)] + pub fn access_descriptor_subrange( + self, + descriptor: impl Into, + node: N, + access: AccessType, + view_info: impl Into, + subresource: impl Into, + ) -> Self + where + N: View, + ::Info: Into, + { + unimplemented!() + } + #[deprecated = "use resource_access function"] #[doc(hidden)] - pub fn access_resource(mut self, node: N, access: AccessType) -> Self + pub fn access_node(mut self, node: N, access: AccessType) -> Self where N: Node + View, SubresourceRange: From, @@ -232,17 +291,42 @@ mod deprecated { #[deprecated = "use subresource_access function"] #[doc(hidden)] - pub fn access_subresource( + pub fn access_node_subrange( mut self, node: N, - subresource: impl Into, access: AccessType, + subresource: impl Into, ) -> Self where N: Node + View, SubresourceRange: From, { - self.subresource_access(node, subresource, access) + self.access_node_subrange_mut(node, access, subresource); + self + } + + #[deprecated = "use set_subresource_access function"] + #[doc(hidden)] + pub fn access_node_subrange_mut( + &mut self, + node: N, + access: AccessType, + subresource: impl Into, + ) -> &mut Self + where + N: Node + View, + SubresourceRange: From, + { + self.set_subresource_access(node, subresource, access) + } + + #[deprecated = "use bind_resource function"] + #[doc(hidden)] + pub fn bind_node(&mut self, resource: R) -> R::Node + where + R: BindGraph, + { + self.bind_resource(resource) } #[deprecated = "use device_address function of resource function result"] @@ -264,5 +348,11 @@ mod deprecated { { node.info(&self.cmd.graph.resources) } + + #[deprecated = "use end_cmd function"] + #[doc(hidden)] + pub fn submit_pass(self) -> &'a mut Graph { + self.end_cmd() + } } } diff --git a/src/cmd_ref/ray_trace.rs b/src/cmd/ray_trace.rs similarity index 98% rename from src/cmd_ref/ray_trace.rs rename to src/cmd/ray_trace.rs index ae2f27f8..504faef5 100644 --- a/src/cmd_ref/ray_trace.rs +++ b/src/cmd/ray_trace.rs @@ -326,7 +326,7 @@ impl<'a> Deref for RayTraceCommandBufferRef<'a> { mod deprecated { use { crate::{ - cmd_ref::{ + cmd::{ Descriptor, PipelineCommandRef, SubresourceRange, View, ViewInfo, ray_trace::RayTraceCommandBufferRef, }, @@ -336,6 +336,14 @@ mod deprecated { vk_sync::AccessType, }; + impl RayTraceCommandBufferRef<'_> { + #[deprecated = "use push_constants function"] + #[doc(hidden)] + pub fn push_constants_offset(&self, offset: u32, data: &[u8]) -> &Self { + self.push_constants(offset, data) + } + } + impl PipelineCommandRef<'_, RayTracePipeline> { #[deprecated = "use shader_resource_access function with AccessType::RayTracingShaderReadSampledImageOrUniformTexelBuffer"] #[doc(hidden)] diff --git a/src/driver/accel_struct.rs b/src/driver/accel_struct.rs index 8f1a34ef..300be954 100644 --- a/src/driver/accel_struct.rs +++ b/src/driver/accel_struct.rs @@ -750,7 +750,13 @@ impl From for AccelerationStructureInfoBuilderError { /// Specifies a range of acceleration structure data. #[derive(Clone, Copy, Debug, Default, PartialEq)] -pub struct AccelerationStructureRange; +pub struct AccelerationStructureSubresourceRange; + +impl From for AccelerationStructureSubresourceRange { + fn from(_: AccelerationStructureInfo) -> Self { + Self + } +} /// Holds the results of the [`AccelerationStructure::size_of`] function. #[derive(Clone, Copy, Debug)] diff --git a/src/driver/buffer.rs b/src/driver/buffer.rs index 579c976c..30b968ed 100644 --- a/src/driver/buffer.rs +++ b/src/driver/buffer.rs @@ -798,6 +798,11 @@ impl BufferInfo { } } + /// Creates a default `BufferInfoBuilder`. + pub fn builder() -> BufferInfoBuilder { + Default::default() + } + /// Returns `true` if this information specifies host-accessible memory. pub fn is_host_mem(&self) -> bool { self.host_read | self.host_write diff --git a/src/driver/cmd_buf.rs b/src/driver/cmd_buf.rs index 2c483f0a..d1ea9a2d 100644 --- a/src/driver/cmd_buf.rs +++ b/src/driver/cmd_buf.rs @@ -191,6 +191,11 @@ impl CommandBufferInfo { } impl CommandBufferInfo { + /// Creates a default `CommandBufferInfoBuilder`. + pub fn builder() -> CommandBufferInfoBuilder { + Default::default() + } + /// Converts a `CommandBufferInfo` into a `CommandBufferInfoBuilder`. pub fn into_builder(self) -> CommandBufferInfoBuilder { CommandBufferInfoBuilder { diff --git a/src/driver/compute.rs b/src/driver/compute.rs index d75758f8..298233e5 100644 --- a/src/driver/compute.rs +++ b/src/driver/compute.rs @@ -11,6 +11,7 @@ use { log::{trace, warn}, std::{ ffi::CString, + hash::{Hash, Hasher}, slice, sync::{Arc, OnceLock}, thread::panicking, @@ -160,6 +161,11 @@ impl ComputePipeline { } } + /// Gets the debugging name assigned to this pipeline, if one has been set. + pub fn debug_name(&self) -> Option<&str> { + self.inner.name.get().map(String::as_str) + } + /// The device which owns this compute pipeline. pub fn device(&self) -> &Device { &self.inner.device @@ -175,16 +181,11 @@ impl ComputePipeline { self.inner.info } - /// Gets the debugging name assigned to this pipeline, if one has been set. - pub fn name(&self) -> Option<&str> { - self.inner.name.get().map(String::as_str) - } - /// Sets the debugging name assigned to this pipeline. /// /// _Note:_ The pipeline name may only be assigned once. Subsequent calls will not update the /// previously set name value. - pub fn set_name(&mut self, name: impl Into) { + pub fn set_debug_name(&mut self, name: impl Into) { if !self.inner.device.physical_device.instance.info.debug { return; } @@ -197,13 +198,27 @@ impl ComputePipeline { /// /// _Note:_ The pipeline name may only be assigned once. Subsequent calls will not update the /// previously set name value. - pub fn debug_name(mut self, name: impl Into) -> Self { - self.set_name(name); + pub fn with_debug_name(mut self, name: impl Into) -> Self { + self.set_debug_name(name); self } } +impl Eq for ComputePipeline {} + +impl Hash for ComputePipeline { + fn hash(&self, state: &mut H) { + Arc::as_ptr(&self.inner).hash(state); + } +} + +impl PartialEq for ComputePipeline { + fn eq(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.inner, &other.inner) + } +} + /// Information used to create a [`ComputePipeline`] instance. #[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)] #[builder( @@ -240,6 +255,11 @@ pub struct ComputePipelineInfo { } impl ComputePipelineInfo { + /// Creates a default `ComputePipelineInfoBuilder`. + pub fn builder() -> ComputePipelineInfoBuilder { + Default::default() + } + /// Converts a `ComputePipelineInfo` into a `ComputePipelineInfoBuilder`. pub fn into_builder(self) -> ComputePipelineInfoBuilder { ComputePipelineInfoBuilder { @@ -310,6 +330,18 @@ impl Drop for ComputePipelineInner { } } +mod deprecated { + use crate::driver::compute::ComputePipeline; + + impl ComputePipeline { + #[deprecated = "use with_debug_name function"] + #[doc(hidden)] + pub fn with_name(this: Self, name: impl Into) -> Self { + this.with_debug_name(name) + } + } +} + #[cfg(test)] mod test { use super::*; diff --git a/src/driver/device.rs b/src/driver/device.rs index f85f5cfa..d1dd879d 100644 --- a/src/driver/device.rs +++ b/src/driver/device.rs @@ -224,11 +224,11 @@ impl Device { queues.push(queue_family.into_boxed_slice()); } - let surface_ext = physical_device.display.then(|| { + let surface_ext = physical_device.surface_ext.then(|| { khr::surface::Instance::new(&physical_device.instance.entry, &physical_device.instance) }); let swapchain_ext = physical_device - .display + .swapchain_ext .then(|| khr::swapchain::Device::new(&physical_device.instance, &device)); let accel_struct_ext = physical_device .accel_struct_properties @@ -427,49 +427,9 @@ pub struct DeviceInfo { } impl DeviceInfo { - /// A helper function which prioritizes selection of lower-power integrated GPU devices. - #[profiling::function] - pub fn integrated_gpu_index(physical_devices: &[PhysicalDevice]) -> usize { - Self::pick_best_gpu( - physical_devices, - &[ - vk::PhysicalDeviceType::INTEGRATED_GPU, - vk::PhysicalDeviceType::DISCRETE_GPU, - vk::PhysicalDeviceType::VIRTUAL_GPU, - vk::PhysicalDeviceType::CPU, - vk::PhysicalDeviceType::OTHER, - ], - ) - } - - /// A helper function which prioritizes selection of higher-performance discrete GPU devices. - #[profiling::function] - pub fn discrete_gpu_index(physical_devices: &[PhysicalDevice]) -> usize { - Self::pick_best_gpu( - physical_devices, - &[ - vk::PhysicalDeviceType::DISCRETE_GPU, - vk::PhysicalDeviceType::VIRTUAL_GPU, - vk::PhysicalDeviceType::INTEGRATED_GPU, - vk::PhysicalDeviceType::CPU, - vk::PhysicalDeviceType::OTHER, - ], - ) - } - - fn pick_best_gpu( - physical_devices: &[PhysicalDevice], - best: &[vk::PhysicalDeviceType], - ) -> usize { - for best in best.iter().copied() { - for (idx, physical_device) in physical_devices.iter().enumerate() { - if physical_device.properties_v1_0.device_type == best { - return idx; - } - } - } - - 0 + /// Creates a default `DeviceInfoBuilder`. + pub fn builder() -> DeviceInfoBuilder { + Default::default() } /// Converts a `DeviceInfo` into a `DeviceInfoBuilder`. @@ -566,6 +526,74 @@ impl Deref for ReadOnlyDevice { } } +#[allow(deprecated)] +#[allow(unused)] +pub(crate) mod deprecated { + use { + crate::driver::{ + DriverError, + device::{Device, DeviceInfo, DeviceInfoBuilder}, + }, + ash::vk, + raw_window_handle::HasDisplayHandle, + std::any::Any, + }; + + impl Device { + #[deprecated = "use from_display function"] + #[doc(hidden)] + pub fn create_display( + info: impl Into, + display_handle: &impl HasDisplayHandle, + ) -> Result { + Self::from_display(display_handle, info) + } + + #[deprecated = "use new function"] + #[doc(hidden)] + pub fn create_headless(info: impl Into) -> Result { + Self::new(info) + } + #[deprecated = "use format_properties function of physical_device field"] + #[doc(hidden)] + pub fn format_properties(this: &Self, format: vk::Format) -> vk::FormatProperties { + this.physical_device.format_properties(format) + } + + #[deprecated = "use image_format_properties function of physical_device field"] + #[doc(hidden)] + pub fn image_format_properties( + this: &Self, + format: vk::Format, + ty: vk::ImageType, + tiling: vk::ImageTiling, + usage: vk::ImageUsageFlags, + flags: vk::ImageCreateFlags, + ) -> Result, DriverError> { + this.physical_device + .image_format_properties(format, ty, tiling, usage, flags) + } + } + + impl DeviceInfo { + #[deprecated = "use vk_graph::driver::instance::Instance::physical_devices function"] + #[doc(hidden)] + pub fn integrated_gpu() {} + + #[deprecated = "use vk_graph::driver::instance::Instance::physical_devices function"] + #[doc(hidden)] + pub fn discrete_gpu() {} + } + + impl DeviceInfoBuilder { + #[deprecated = "use vk_graph::driver::instance::Instance::physical_devices function"] + #[doc(hidden)] + pub fn select_physical_device(self, _: Box) -> Self { + self + } + } +} + #[cfg(test)] mod test { use { diff --git a/src/driver/graphic.rs b/src/driver/graphic.rs index 0011bacd..d6e2f1d8 100644 --- a/src/driver/graphic.rs +++ b/src/driver/graphic.rs @@ -15,6 +15,7 @@ use { std::{ collections::HashSet, ffi::CString, + hash::{Hash, Hasher}, sync::{Arc, OnceLock}, thread::panicking, }, @@ -539,7 +540,12 @@ impl GraphicPipeline { } } - /// The device which owns this compute pipeline. + /// Gets the debugging name assigned to this pipeline, if one has been set. + pub fn debug_name(&self) -> Option<&str> { + self.inner.name.get().map(String::as_str) + } + + /// The device which owns this graphic pipeline. pub fn device(&self) -> &Device { &self.inner.device } @@ -549,16 +555,11 @@ impl GraphicPipeline { self.inner.info } - /// Gets the debugging name assigned to this pipeline, if one has been set. - pub fn name(&self) -> Option<&str> { - self.inner.name.get().map(String::as_str) - } - /// Sets the debugging name assigned to this pipeline. /// /// _Note:_ The pipeline name may only be assigned once. Subsequent calls will not update the /// previously set name value. - pub fn set_name(&mut self, name: impl Into) { + pub fn set_debug_name(&mut self, name: impl Into) { if !self.inner.device.physical_device.instance.info.debug { return; } @@ -571,13 +572,27 @@ impl GraphicPipeline { /// /// _Note:_ The pipeline name may only be assigned once. Subsequent calls will not update the /// previously set name value. - pub fn debug_name(mut self, name: impl Into) -> Self { - self.set_name(name); + pub fn with_debug_name(mut self, name: impl Into) -> Self { + self.set_debug_name(name); self } } +impl Eq for GraphicPipeline {} + +impl Hash for GraphicPipeline { + fn hash(&self, state: &mut H) { + Arc::as_ptr(&self.inner).hash(state); + } +} + +impl PartialEq for GraphicPipeline { + fn eq(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.inner, &other.inner) + } +} + /// Information used to create a [`GraphicPipeline`] instance. #[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)] #[builder( @@ -856,7 +871,9 @@ pub(crate) struct VertexInputState { mod deprecated { use { - crate::driver::graphic::{BlendInfo, BlendInfoBuilder, DepthStencilInfo, StencilMode}, + crate::driver::graphic::{ + BlendInfo, BlendInfoBuilder, DepthStencilInfo, GraphicPipeline, StencilMode, + }, ash::vk, ordered_float::OrderedFloat, }; @@ -906,6 +923,14 @@ mod deprecated { Default::default() } } + + impl GraphicPipeline { + #[deprecated = "use with_debug_name function"] + #[doc(hidden)] + pub fn with_name(this: Self, name: impl Into) -> Self { + this.with_debug_name(name) + } + } } #[cfg(test)] diff --git a/src/driver/image.rs b/src/driver/image.rs index 15143756..ae2b432c 100644 --- a/src/driver/image.rs +++ b/src/driver/image.rs @@ -851,6 +851,11 @@ impl ImageInfo { } } + /// Creates a default `ImageInfoBuilder`. + pub fn builder() -> ImageInfoBuilder { + Default::default() + } + /// Provides an `ImageViewInfo` for this format, type, aspect, array elements, and mip levels. pub fn into_image_view(self) -> ImageViewInfo { self.into() diff --git a/src/driver/instance.rs b/src/driver/instance.rs index 781efbf0..b2e8ede7 100644 --- a/src/driver/instance.rs +++ b/src/driver/instance.rs @@ -509,7 +509,7 @@ impl Deref for Instance { #[derive(Builder, Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] #[builder( build_fn(private, name = "fallible_build", error = "UninitializedFieldError"), - derive(Clone, Debug), + derive(Clone, Copy, Debug), pattern = "owned" )] pub struct InstanceInfo { @@ -539,6 +539,11 @@ pub struct InstanceInfo { } impl InstanceInfo { + /// Creates a default `InstanceInfoBuilder`. + pub fn builder() -> InstanceInfoBuilder { + Default::default() + } + /// Converts a `InstanceInfo` into a `InstanceInfoBuilder`. pub fn into_builder(self) -> InstanceInfoBuilder { InstanceInfoBuilder { diff --git a/src/driver/physical_device.rs b/src/driver/physical_device.rs index 56e72500..675fb253 100644 --- a/src/driver/physical_device.rs +++ b/src/driver/physical_device.rs @@ -153,11 +153,6 @@ pub struct PhysicalDevice { /// _Note:_ This field is read-only. pub depth_stencil_resolve_properties: DepthStencilResolveProperties, - /// True if the device may be used for windowed or full-screen display. - /// - /// _Note:_ This field is read-only. - pub display: bool, - /// Describes the features of the physical device which are part of the Vulkan 1.0 base feature set. /// /// _Note:_ This field is read-only. @@ -236,6 +231,16 @@ pub struct PhysicalDevice { /// /// _Note:_ This field is read-only. pub sampler_filter_minmax_properties: SamplerFilterMinmaxProperties, + + /// True if the device may be used for windowed or full-screen display. + /// + /// _Note:_ This field is read-only. + pub surface_ext: bool, + + /// True if the device may be used for windowed or full-screen display. + /// + /// _Note:_ This field is read-only. + pub swapchain_ext: bool, } impl PhysicalDevice { @@ -351,8 +356,8 @@ impl PhysicalDevice { let supports_index_type_uint8 = extensions.contains(ext::index_type_uint8::NAME); let supports_ray_query = extensions.contains(khr::ray_query::NAME); let supports_ray_trace = extensions.contains(khr::ray_tracing_pipeline::NAME); - let supports_surface = extensions.contains(khr::surface::NAME); - let supports_swapchain = extensions.contains(khr::swapchain::NAME); + let surface_ext = extensions.contains(khr::surface::NAME); + let swapchain_ext = extensions.contains(khr::swapchain::NAME); // Gather optional features and properties of the physical device let index_type_uint8_features = if supports_index_type_uint8 { @@ -372,12 +377,10 @@ impl PhysicalDevice { }; let accel_struct_properties = supports_accel_struct.then(|| accel_struct_properties.into()); let ray_trace_properties = supports_ray_trace.then(|| ray_trace_properties.into()); - let display = supports_surface && supports_swapchain; Ok(Self { accel_struct_properties, depth_stencil_resolve_properties, - display, features_v1_0, features_v1_1, features_v1_2, @@ -394,6 +397,8 @@ impl PhysicalDevice { ray_trace_features, ray_trace_properties, sampler_filter_minmax_properties, + surface_ext, + swapchain_ext, }) } @@ -415,7 +420,7 @@ impl PhysicalDevice { { let mut enabled_ext_names = Vec::with_capacity(6); - if self.display { + if self.swapchain_ext { enabled_ext_names.push(khr::swapchain::NAME.as_ptr()); } diff --git a/src/driver/ray_trace.rs b/src/driver/ray_trace.rs index 9c038817..58030bef 100644 --- a/src/driver/ray_trace.rs +++ b/src/driver/ray_trace.rs @@ -13,6 +13,7 @@ use { log::warn, std::{ ffi::CString, + hash::{Hash, Hasher}, sync::{Arc, OnceLock}, thread::panicking, }, @@ -294,7 +295,12 @@ impl RayTracePipeline { } } - /// The device which owns this compute pipeline. + /// Gets the debugging name assigned to this pipeline, if one has been set. + pub fn debug_name(&self) -> Option<&str> { + self.inner.name.get().map(String::as_str) + } + + /// The device which owns this ray trace pipeline. pub fn device(&self) -> &Device { &self.inner.device } @@ -360,7 +366,7 @@ impl RayTracePipeline { /// /// _Note:_ The pipeline name may only be assigned once. Subsequent calls will not update the /// previously set name value. - pub fn set_name(&mut self, name: impl Into) { + pub fn set_debug_name(&mut self, name: impl Into) { if !self.inner.device.physical_device.instance.info.debug { return; } @@ -373,13 +379,27 @@ impl RayTracePipeline { /// /// _Note:_ The pipeline name may only be assigned once. Subsequent calls will not update the /// previously set name value. - pub fn debug_name(mut self, name: impl Into) -> Self { - self.set_name(name); + pub fn with_debug_name(mut self, name: impl Into) -> Self { + self.set_debug_name(name); self } } +impl Eq for RayTracePipeline {} + +impl Hash for RayTracePipeline { + fn hash(&self, state: &mut H) { + Arc::as_ptr(&self.inner).hash(state); + } +} + +impl PartialEq for RayTracePipeline { + fn eq(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.inner, &other.inner) + } +} + /// Information used to create a [`RayTracePipeline`] instance. #[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)] #[builder( @@ -642,6 +662,18 @@ impl From for vk::RayTracingShaderGroupTypeKHR { } } +mod deprecated { + use crate::driver::ray_trace::RayTracePipeline; + + impl RayTracePipeline { + #[deprecated = "use with_debug_name function"] + #[doc(hidden)] + pub fn with_name(this: Self, name: impl Into) -> Self { + this.with_debug_name(name) + } + } +} + #[cfg(test)] mod test { use super::*; diff --git a/src/driver/shader.rs b/src/driver/shader.rs index 0502461f..fed71ca5 100644 --- a/src/driver/shader.rs +++ b/src/driver/shader.rs @@ -22,6 +22,10 @@ use { }, }; +#[allow(deprecated)] +#[doc(hidden)] +pub type SpecializationInfo = self::deprecated::SpecializationInfo; + pub(crate) type DescriptorBindingMap = HashMap; #[profiling::function] @@ -576,6 +580,11 @@ impl SamplerInfo { Self::default().into_builder() } + /// Creates a default `SamplerInfoBuilder`. + pub fn builder() -> SamplerInfoBuilder { + Default::default() + } + /// Converts a `SamplerInfo` into a `SamplerInfoBuilder`. pub fn into_builder(self) -> SamplerInfoBuilder { SamplerInfoBuilder { @@ -896,6 +905,11 @@ impl Shader { ) } + /// Creates a default `ShaderBuilder`. + pub fn builder() -> ShaderBuilder { + Default::default() + } + #[profiling::function] pub(super) fn descriptor_bindings(&self) -> DescriptorBindingMap { let mut res = DescriptorBindingMap::default(); @@ -1544,6 +1558,45 @@ impl<'a> From<&'a SpecializationMap> for vk::SpecializationInfo<'a> { } } +mod deprecated { + use { + crate::driver::shader::{ShaderBuilder, SpecializationMap}, + ash::vk, + }; + + #[derive(Clone, Debug)] + pub struct SpecializationInfo { + pub data: Vec, + pub map_entries: Vec, + } + + impl SpecializationInfo { + pub fn new( + map_entries: impl Into>, + data: impl Into>, + ) -> Self { + Self { + data: data.into(), + map_entries: map_entries.into(), + } + } + } + + impl ShaderBuilder { + #[deprecated = "use specialization function"] + #[doc(hidden)] + pub fn specialization_info(self, info: SpecializationInfo) -> Self { + let mut specialization = SpecializationMap::new(info.data); + + for entry in &info.map_entries { + specialization.set_constant(entry.constant_id, entry.offset, entry.size); + } + + self.specialization(specialization) + } + } +} + #[cfg(test)] mod test { use super::*; diff --git a/src/driver/swapchain.rs b/src/driver/swapchain.rs index b4c59da3..a47029a3 100644 --- a/src/driver/swapchain.rs +++ b/src/driver/swapchain.rs @@ -553,6 +553,13 @@ pub struct SwapchainInfo { /// The initial width of the surface. pub width: u32, + + /// NOTE: This field does not do anything, use the new one + #[builder(default)] + #[builder_field_attr(deprecated = "use min_image_count field")] + #[builder_setter_attr(deprecated = "use min_image_count field")] + #[deprecated = "use min_image_count field"] + pub desired_image_count: u32, } impl SwapchainInfo { @@ -560,6 +567,8 @@ impl SwapchainInfo { #[inline(always)] pub fn new(width: u32, height: u32, surface: vk::SurfaceFormatKHR) -> SwapchainInfo { Self { + #[allow(deprecated)] + desired_image_count: 0, height, min_image_count: 2, present_mode: vk::PresentModeKHR::MAILBOX, @@ -568,9 +577,16 @@ impl SwapchainInfo { } } + /// Creates a default `SwapchainInfoBuilder`. + pub fn builder() -> SwapchainInfoBuilder { + Default::default() + } + /// Converts a `SwapchainInfo` into a `SwapchainInfoBuilder`. pub fn into_builder(self) -> SwapchainInfoBuilder { SwapchainInfoBuilder { + #[allow(deprecated)] + desired_image_count: Some(self.desired_image_count), height: Some(self.height), min_image_count: Some(self.min_image_count), present_mode: Some(self.present_mode), @@ -620,6 +636,20 @@ impl From for SwapchainInfoBuilderError { } } +mod deprecated { + use ash::vk; + + use crate::driver::swapchain::SwapchainInfoBuilder; + + impl SwapchainInfoBuilder { + #[deprecated = "use present_mode function"] + #[doc(hidden)] + pub fn present_modes(self, modes: impl Into>) -> Self { + self.present_mode(modes.into()[0]) + } + } +} + #[cfg(test)] mod test { use { diff --git a/src/lib.rs b/src/lib.rs index 22c733a4..3bb4b7a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -351,7 +351,7 @@ layout. #![warn(missing_docs)] -pub mod cmd_ref; +pub mod cmd; pub mod driver; pub mod node; pub mod pool; @@ -359,16 +359,19 @@ pub mod pool; mod bind; mod queue; -use crate::cmd_ref::CommandBufferRef; +use crate::cmd::CommandBufferRef; pub use self::{ bind::{BindGraph, Bound, Resource}, queue::Queue, }; +#[allow(deprecated)] +pub use self::deprecated::{Display, DisplayInfo, DisplayInfoBuilder}; + use { self::{ - cmd_ref::{AttachmentIndex, CommandRef, Descriptor, SubresourceAccess, ViewInfo}, + cmd::{AttachmentIndex, CommandRef, Descriptor, SubresourceAccess, ViewInfo}, node::Node, node::{ AccelerationStructureLeaseNode, AccelerationStructureNode, @@ -474,6 +477,25 @@ pub enum ClearColorValue { Uint32([u32; 4]), } +impl ClearColorValue { + /// rgb zeros and alpha ones. + pub const BLACK_ALPHA_ONE: Self = Self::Float32([0.0, 0.0, 0.0, 1.0]); + + /// zeros. + pub const BLACK_ALPHA_ZERO: Self = Self::Float32([0.0, 0.0, 0.0, 0.0]); + + /// rgb zeros and alpha ones. + pub const WHITE_ALPHA_ONE: Self = Self::Float32([1.0, 1.0, 1.0, 1.0]); + + /// rgb ones and alpha zeros. + pub const WHITE_ALPHA_ZERO: Self = Self::Float32([1.0, 1.0, 1.0, 0.0]); + + /// Convenience constructor for clear color values. + pub fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self { + Self::Float32([r, g, b, a]) + } +} + impl From<[f32; 4]> for ClearColorValue { fn from(float32: [f32; 4]) -> Self { Self::Float32(float32) @@ -1108,7 +1130,7 @@ impl Graph { self.copy_image_to_buffer_region( src, dst, - vk::BufferImageCopy { + [vk::BufferImageCopy { buffer_offset: 0, buffer_row_length: src_info.width, buffer_image_height: src_info.height, @@ -1124,23 +1146,13 @@ impl Graph { height: src_info.height, width: src_info.width, }, - }, + }], ) } - /// Copy image data into a buffer. - pub fn copy_image_to_buffer_region( - &mut self, - src: impl Into, - dst: impl Into, - region: vk::BufferImageCopy, - ) -> &mut Self { - self.copy_image_to_buffer_regions(src, dst, [region]) - } - /// Copy image data into a buffer. #[profiling::function] - pub fn copy_image_to_buffer_regions( + pub fn copy_image_to_buffer_region( &mut self, src: impl Into, dst: impl Into, @@ -1294,24 +1306,313 @@ impl Graph { } } +#[deprecated] +#[doc(hidden)] +pub mod graph { + #[deprecated = "use vk_graph::node module"] + pub mod node { + #[deprecated = "use vk_graph::node::AccelerationStructureLeaseNode"] + pub type AccelerationStructureLeaseNode = crate::node::AccelerationStructureLeaseNode; + + #[deprecated = "use vk_graph::node::AccelerationStructureNode"] + pub type AccelerationStructureNode = crate::node::AccelerationStructureNode; + + #[deprecated = "use vk_graph::node::AnyAccelerationStructureNode"] + pub type AnyAccelerationStructureNode = crate::node::AnyAccelerationStructureNode; + + #[deprecated = "use vk_graph::node::AnyBufferNode"] + pub type AnyBufferNode = crate::node::AnyBufferNode; + + #[deprecated = "use vk_graph::node::AnyImageNode"] + pub type AnyImageNode = crate::node::AnyImageNode; + + #[deprecated = "use vk_graph::node::BufferLeaseNode"] + pub type BufferLeaseNode = crate::node::BufferLeaseNode; + + #[deprecated = "use vk_graph::node::BufferNode"] + pub type BufferNode = crate::node::BufferNode; + + #[deprecated = "use vk_graph::node::ImageLeaseNode"] + pub type ImageLeaseNode = crate::node::ImageLeaseNode; + + #[deprecated = "use vk_graph::node::ImageNode"] + pub type ImageNode = crate::node::ImageNode; + + #[deprecated = "use vk_graph::node::Node"] + pub type Node = dyn crate::node::Node; + + #[deprecated = "use vk_graph::node::SwapchainImageNode"] + pub type SwapchainImageNode = crate::node::SwapchainImageNode; + } + + #[deprecated] + #[doc(hidden)] + pub mod pass_ref { + #[deprecated = "use vk_graph::cmd::CommandBufferRef"] + pub type Acceleration<'a> = crate::cmd::CommandBufferRef<'a>; + + #[deprecated = "use vk_graph::cmd::CommandBufferRef"] + pub type AccelerationStructureBuildInfo = crate::cmd::BuildAccelerationStructureInfo; + + #[deprecated = "use vk_graph::cmd::CommandBufferRef"] + pub type AccelerationStructureIndirectBuildInfo = + crate::cmd::BuildAccelerationStructureIndirectInfo; + + #[deprecated = "use vk_graph::cmd::CommandBufferRef"] + pub type AccelerationStructureIndirectUpdateInfo = + crate::cmd::UpdateAccelerationStructureIndirectInfo; + + #[deprecated = "use vk_graph::cmd::CommandBufferRef"] + pub type AccelerationStructureUpdateInfo = crate::cmd::UpdateAccelerationStructureInfo; + + #[deprecated = "use vk_graph::Descriptor"] + pub type Descriptor = crate::Descriptor; + + #[deprecated = "use vk_graph::cmd::GraphicCommandBufferRef"] + pub type Draw<'a> = crate::cmd::GraphicCommandBufferRef<'a>; + + #[deprecated = "use vk_graph::cmd::CommandRef"] + pub type PassRef<'a> = crate::cmd::CommandRef<'a>; + + #[deprecated = "use vk_graph::cmd::PipelineCommandRef"] + pub type PipelinePassRef<'a, T> = crate::cmd::PipelineCommandRef<'a, T>; + + #[deprecated = "use vk_graph::cmd::RayTraceCommandBufferRef"] + pub type RayTrace<'a> = crate::cmd::RayTraceCommandBufferRef<'a>; + + #[deprecated = "use vk_graph::ViewInfo"] + pub type ViewType = crate::cmd::ViewInfo; + + #[deprecated = "remove"] + pub trait View { + type Information; + } + } + + #[deprecated = "use vk_graph::Graph"] + pub type RenderGraph = crate::Graph; + + #[deprecated = "use vk_graph::Queue"] + pub type Resolver = crate::Queue; +} + +#[allow(deprecated)] #[allow(unused)] +#[doc(hidden)] pub(crate) mod deprecated { use { crate::{ - Graph, + BindGraph, Graph, bind::Resource, driver::{ - accel_struct::AccelerationStructureInfo, buffer::BufferInfo, image::ImageInfo, + DriverError, + accel_struct::{AccelerationStructure, AccelerationStructureInfo}, + buffer::{Buffer, BufferInfo}, + cmd_buf::{CommandBuffer, CommandBufferInfo}, + descriptor_set::{DescriptorPool, DescriptorPoolInfo}, + device::Device, + image::{Image, ImageInfo}, + render_pass::{RenderPass, RenderPassInfo}, + swapchain::{Swapchain, SwapchainImage, SwapchainInfo}, }, node::{ - AccelerationStructureLeaseNode, AccelerationStructureNode, BufferLeaseNode, + AccelerationStructureLeaseNode, AccelerationStructureNode, + AnyAccelerationStructureNode, AnyBufferNode, AnyImageNode, BufferLeaseNode, BufferNode, ImageLeaseNode, ImageNode, Node, SwapchainImageNode, }, + pool::{Lease, Pool}, }, ash::vk, + std::{ops::Range, sync::Arc}, }; + /// Specifies a color attachment clear value which can be used to initliaze an image. + #[derive(Clone, Copy, Debug)] + pub struct ClearColorValue(pub [f32; 4]); + + impl From<[f32; 3]> for ClearColorValue { + fn from(color: [f32; 3]) -> Self { + [color[0], color[1], color[2], 1.0].into() + } + } + + impl From<[f32; 4]> for ClearColorValue { + fn from(color: [f32; 4]) -> Self { + Self(color) + } + } + + impl From<[u8; 3]> for ClearColorValue { + fn from(color: [u8; 3]) -> Self { + [color[0], color[1], color[2], u8::MAX].into() + } + } + + impl From<[u8; 4]> for ClearColorValue { + fn from(color: [u8; 4]) -> Self { + [ + color[0] as f32 / u8::MAX as f32, + color[1] as f32 / u8::MAX as f32, + color[2] as f32 / u8::MAX as f32, + color[3] as f32 / u8::MAX as f32, + ] + .into() + } + } + + #[deprecated = "use Swapchain from vk_graph_window crate"] + #[derive(Debug)] + pub struct Display; + + impl Display { + pub fn new( + device: &Arc, + swapchain: Swapchain, + info: impl Into, + ) -> Result { + todo!() + } + + pub fn acquire_next_image(&mut self) -> Result, DisplayError> { + todo!() + } + + pub fn present_image( + &mut self, + pool: &mut impl ResolverPool, + render_graph: crate::graph::RenderGraph, + swapchain_image: SwapchainImageNode, + queue_index: u32, + ) -> Result<(), DisplayError> { + todo!() + } + + pub fn set_swapchain_info(&mut self, info: impl Into) { + todo!() + } + + pub fn swapchain_info(&self) -> SwapchainInfo { + todo!() + } + } + + #[deprecated = "use vk_graph_window::SwapchainError"] + #[derive(Clone, Copy, Debug, Default)] + pub struct DisplayError; + + #[deprecated = "use vk_graph_window::SwapchainInfo"] + #[derive(Clone, Copy, Debug, Default)] + pub struct DisplayInfo; + + #[deprecated = "use vk_graph_window::SwapchainInfoBuilder"] + #[derive(Clone, Copy, Debug, Default)] + pub struct DisplayInfoBuilder; + + // General stuff impl Graph { + #[deprecated = "use begin_cmd function"] + #[doc(hidden)] + pub fn begin_pass(&mut self, name: impl AsRef) -> crate::graph::pass_ref::PassRef<'_> { + self.begin_cmd().debug_name(name.as_ref().to_owned()) + } + + #[deprecated = "use bind_resource function"] + #[doc(hidden)] + pub fn bind_node(&mut self, resource: R) -> R::Node + where + R: BindGraph, + { + self.bind_resource(resource) + } + + #[deprecated = "use blit_image_region function"] + #[doc(hidden)] + pub fn blit_image_regions( + &mut self, + src_node: impl Into, + dst_node: impl Into, + filter: vk::Filter, + regions: impl AsRef<[vk::ImageBlit]> + 'static + Send, + ) -> &mut Self { + self.blit_image_region(src_node, dst_node, filter, regions) + } + + #[deprecated = "use clear_color_image function"] + #[doc(hidden)] + pub fn clear_color_image_value( + &mut self, + image_node: impl Into, + color_value: impl Into, + ) -> &mut Self { + self.clear_color_image(image_node, color_value.into().0) + } + + #[deprecated = "use clear_depth_stencil_image function"] + #[doc(hidden)] + pub fn clear_depth_stencil_image_value( + &mut self, + image_node: impl Into, + depth: f32, + stencil: u32, + ) -> &mut Self { + self.clear_depth_stencil_image(image_node, depth, stencil) + } + + #[deprecated = "use copy_buffer_region function"] + #[doc(hidden)] + pub fn copy_buffer_regions( + &mut self, + src_node: impl Into, + dst_node: impl Into, + regions: impl AsRef<[vk::BufferCopy]> + 'static + Send, + ) -> &mut Self { + self.copy_buffer_region(src_node, dst_node, regions) + } + + #[deprecated = "use copy_buffer_to_image_region function"] + #[doc(hidden)] + pub fn copy_buffer_to_image_regions( + &mut self, + src_node: impl Into, + dst_node: impl Into, + regions: impl AsRef<[vk::BufferImageCopy]> + 'static + Send, + ) -> &mut Self { + self.copy_buffer_to_image_region(src_node, dst_node, regions) + } + + #[deprecated = "use copy_image_region function"] + #[doc(hidden)] + pub fn copy_image_regions( + &mut self, + src_node: impl Into, + dst_node: impl Into, + regions: impl AsRef<[vk::ImageCopy]> + 'static + Send, + ) -> &mut Self { + self.copy_image_region(src_node, dst_node, regions) + } + + #[deprecated = "use copy_image_to_buffer_region function"] + #[doc(hidden)] + pub fn copy_image_to_buffer_regions( + &mut self, + src_node: impl Into, + dst_node: impl Into, + regions: impl AsRef<[vk::BufferImageCopy]> + 'static + Send, + ) -> &mut Self { + self.copy_image_to_buffer_region(src_node, dst_node, regions) + } + + #[deprecated = "use fill_buffer function"] + #[doc(hidden)] + pub fn fill_buffer_region( + &mut self, + buffer_node: impl Into, + data: u32, + region: Range, + ) -> &mut Self { + self.fill_buffer(buffer_node, region, data) + } + #[deprecated = "use device_address function of resource function result"] #[doc(hidden)] pub fn node_device_address(&self, node: impl Node) -> vk::DeviceAddress { @@ -1331,6 +1632,32 @@ pub(crate) mod deprecated { { node.info(&self.resources) } + + #[deprecated = "use queue function"] + #[doc(hidden)] + pub fn resolve(self) -> crate::graph::Resolver { + self.queue() + } + + #[deprecated = "use resource and clone functions"] + #[doc(hidden)] + pub fn unbind_node(&mut self, node: N) -> N::Result + where + N: Unbind, + { + node.unbind(&self.resources) + } + + #[deprecated = "use update_buffer function"] + #[doc(hidden)] + pub fn update_buffer_offset( + &mut self, + buffer_node: impl Into, + offset: vk::DeviceSize, + data: impl AsRef<[u8]> + 'static + Send, + ) -> &mut Self { + self.update_buffer(buffer_node, offset, data) + } } pub trait Info { @@ -1366,6 +1693,17 @@ pub(crate) mod deprecated { } } + impl Info for [] { + type Type = [<$name Info>]; + + fn info(&self, resources: &[Resource]) -> Self::Type + where + Self: Node, + { + resources[self.index()].[]().unwrap().info + } + } + impl Info for [<$name LeaseNode>] { type Type = [<$name Info>]; @@ -1376,6 +1714,22 @@ pub(crate) mod deprecated { resources[self.idx].[]().unwrap().info } } + + impl Unbind for [<$name Node>] { + type Result = Arc<$name>; + + fn unbind(&self, resources: &[Resource]) -> Self::Result { + resources[self.index()].[]().unwrap().clone() + } + } + + impl Unbind for [<$name LeaseNode>] { + type Result = Arc>; + + fn unbind(&self, resources: &[Resource]) -> Self::Result { + resources[self.index()].[]().unwrap().clone() + } + } } }; } @@ -1383,4 +1737,19 @@ pub(crate) mod deprecated { info!(AccelerationStructure); info!(Buffer); info!(Image); + + #[deprecated = "remove"] + pub trait ResolverPool: + Pool + + Pool + + Pool + + Send + { + } + + pub trait Unbind: Node { + type Result; + + fn unbind(&self, _: &[Resource]) -> Self::Result; + } } diff --git a/src/pool/fifo.rs b/src/pool/fifo.rs index 9969c3c2..9357f584 100644 --- a/src/pool/fifo.rs +++ b/src/pool/fifo.rs @@ -43,14 +43,27 @@ use { /// of stored resources, however you may call [`FifoPool::clear`] or the other memory management /// functions at any time to discard stored resources. #[derive(Debug)] +#[readonly::make] pub struct FifoPool { accel_struct_cache: Cache, buffer_cache: Cache, command_buffer_cache: HashMap>, descriptor_pool_cache: Cache, - device: Device, + + /// The device which owns this pool. + /// + /// _Note:_ This field is read-only. + #[readonly] + pub device: Device, + image_cache: Cache, - info: PoolInfo, + + /// Information used to create this pool. + /// + /// _Note:_ This field is read-only. + #[readonly] + pub info: PoolInfo, + render_pass_cache: HashMap>, } diff --git a/src/pool/hash.rs b/src/pool/hash.rs index 9aff5f09..a2eba29b 100644 --- a/src/pool/hash.rs +++ b/src/pool/hash.rs @@ -39,14 +39,27 @@ use std::sync::Mutex; /// If requests for varying resources is common [`HashPool::clear_images_by_info`] and other memory /// management functions are nessecery in order to avoid using all available device memory. #[derive(Debug)] +#[readonly::make] pub struct HashPool { acceleration_structure_cache: HashMap>, buffer_cache: HashMap>, command_buffer_cache: HashMap>, descriptor_pool_cache: HashMap>, - device: Device, + + /// The device which owns this pool. + /// + /// _Note:_ This field is read-only. + #[readonly] + pub device: Device, + image_cache: HashMap>, - info: PoolInfo, + + /// Information used to create this pool. + /// + /// _Note:_ This field is read-only. + #[readonly] + pub info: PoolInfo, + render_pass_cache: HashMap>, } diff --git a/src/pool/lazy.rs b/src/pool/lazy.rs index b25925cc..99d40c3c 100644 --- a/src/pool/lazy.rs +++ b/src/pool/lazy.rs @@ -73,14 +73,27 @@ impl From for ImageKey { /// If requests for varying resources is common [`LazyPool::clear_images_by_info`] and other memory /// management functions are nessecery in order to avoid using all available device memory. #[derive(Debug)] +#[readonly::make] pub struct LazyPool { accel_struct_cache: HashMap>, buffer_cache: HashMap<(bool, vk::DeviceSize), Cache>, command_buffer_cache: HashMap>, descriptor_pool_cache: Cache, - device: Device, + + /// The device which owns this pool. + /// + /// _Note:_ This field is read-only. + #[readonly] + pub device: Device, + image_cache: HashMap>, - info: PoolInfo, + + /// Information used to create this pool. + /// + /// _Note:_ This field is read-only. + #[readonly] + pub info: PoolInfo, + render_pass_cache: HashMap>, } diff --git a/src/queue.rs b/src/queue.rs index 5b7b0772..ed9a9a7e 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -2,11 +2,11 @@ use { super::{ Attachment, Command, ExecutionPipeline, Graph, Node, NodeIndex, Resource, bind::ResourceInner, - cmd_ref::{SubresourceAccess, SubresourceRange}, + cmd::{SubresourceAccess, SubresourceRange}, }, crate::{ Bound, - cmd_ref::CommandBufferRef, + cmd::CommandBufferRef, driver::{ AttachmentInfo, AttachmentRef, Descriptor, DescriptorInfo, DescriptorSet, DriverError, FramebufferAttachmentImageInfo, FramebufferInfo, SubpassDependency, SubpassInfo, @@ -640,13 +640,13 @@ impl Queue { if log_enabled!(Trace) { let (ty, name, vk_pipeline) = match pipeline { ExecutionPipeline::Compute(pipeline) => { - ("compute", pipeline.name(), pipeline.handle()) + ("compute", pipeline.debug_name(), pipeline.handle()) } ExecutionPipeline::Graphic(pipeline) => { - ("graphic", pipeline.name(), vk::Pipeline::null()) + ("graphic", pipeline.debug_name(), vk::Pipeline::null()) } ExecutionPipeline::RayTrace(pipeline) => { - ("ray trace", pipeline.name(), pipeline.handle()) + ("ray trace", pipeline.debug_name(), pipeline.handle()) } }; if let Some(name) = name { From 4506a7abcf89ec6776b45ab0f38d9965cb175413 Mon Sep 17 00:00:00 2001 From: John Wells Date: Tue, 3 Mar 2026 12:36:36 -0500 Subject: [PATCH 31/86] Clean-up --- contrib/vk-graph-egui/src/lib.rs | 6 +- contrib/vk-graph-fx/src/image_loader.rs | 2 +- contrib/vk-graph-window/src/swapchain.rs | 2 +- examples/aliasing.rs | 2 +- examples/app.rs | 2 +- examples/bindless.rs | 2 +- examples/cpu_readback.rs | 4 +- examples/fuzzer.rs | 37 ++++++------- examples/image_sampler.rs | 4 +- examples/min_max.rs | 2 +- examples/mip_compute.rs | 2 +- examples/mip_graphic.rs | 2 +- examples/msaa.rs | 5 +- examples/multipass.rs | 2 +- examples/multithread.rs | 6 +- examples/ray_omni.rs | 15 ++--- examples/ray_trace.rs | 22 ++++---- examples/rt_triangle.rs | 16 +++--- examples/shader-toy/src/main.rs | 2 +- examples/skeletal-anim/src/main.rs | 52 +++++++++--------- examples/subgroup_ops.rs | 4 +- examples/vr/src/main.rs | 27 ++++----- examples/vsm_omni.rs | 2 +- src/cmd/compute.rs | 11 +++- src/cmd/graphic.rs | 27 +++++---- src/cmd/mod.rs | 13 ++++- src/cmd/pipeline.rs | 8 +-- src/cmd/ray_trace.rs | 11 +++- src/driver/buffer.rs | 4 +- src/driver/shader.rs | 1 + src/lib.rs | 70 +++++++++++++----------- src/queue.rs | 6 ++ 32 files changed, 210 insertions(+), 161 deletions(-) diff --git a/contrib/vk-graph-egui/src/lib.rs b/contrib/vk-graph-egui/src/lib.rs index fcd7fa0e..aff3a03e 100644 --- a/contrib/vk-graph-egui/src/lib.rs +++ b/contrib/vk-graph-egui/src/lib.rs @@ -113,7 +113,7 @@ impl Egui { vk::BufferUsageFlags::TRANSFER_SRC, )) .unwrap(); - Buffer::copy_from_slice(&mut buf, 0, cast_slice(&pixels)); + buf.copy_from_slice(0, cast_slice(&pixels)); graph.bind_resource(buf) }; @@ -235,7 +235,7 @@ impl Egui { vk::BufferUsageFlags::INDEX_BUFFER, )) .unwrap(); - Buffer::copy_from_slice(&mut buf, 0, cast_slice(&mesh.indices)); + buf.copy_from_slice(0, cast_slice(&mesh.indices)); buf }; let idx_buf = graph.bind_resource(idx_buf); @@ -249,7 +249,7 @@ impl Egui { vk::BufferUsageFlags::VERTEX_BUFFER, )) .unwrap(); - Buffer::copy_from_slice(&mut buf, 0, cast_slice(&mesh.vertices)); + buf.copy_from_slice(0, cast_slice(&mesh.vertices)); buf }; let vert_buf = graph.bind_resource(vert_buf); diff --git a/contrib/vk-graph-fx/src/image_loader.rs b/contrib/vk-graph-fx/src/image_loader.rs index af340b2f..01156c15 100644 --- a/contrib/vk-graph-fx/src/image_loader.rs +++ b/contrib/vk-graph-fx/src/image_loader.rs @@ -237,7 +237,7 @@ impl ImageLoader { let image = graph.resource(image).clone(); graph - .queue() + .into_queue() .submit(&mut self.pool, queue_family_index, queue_index)?; Ok(image) diff --git a/contrib/vk-graph-window/src/swapchain.rs b/contrib/vk-graph-window/src/swapchain.rs index 185cdb72..b5f99cc5 100644 --- a/contrib/vk-graph-window/src/swapchain.rs +++ b/contrib/vk-graph-window/src/swapchain.rs @@ -193,7 +193,7 @@ impl Swapchain { { trace!("present_image"); - let mut queue = graph.queue(); + let mut queue = graph.into_queue(); let wait_dst_stage_mask = queue.node_stages(swapchain_image); // The swapchain should have been written to, otherwise it would be noise and that's a panic diff --git a/examples/aliasing.rs b/examples/aliasing.rs index cd11db3d..2d5a3192 100644 --- a/examples/aliasing.rs +++ b/examples/aliasing.rs @@ -10,7 +10,7 @@ use {clap::Parser, std::sync::Arc, vk_graph_prelude::*}; /// Acceleration structures, buffers and images may be "aliased" by different parts of any one or /// more graphs. The process involves wrapping any pool type (FifoPool, LazyPool, HashPool) /// in an AliasPool container. AliasPool offers an alias(..) function which operates exactly the -/// same as a regular pool lease(..) except that the result is wrapped in an Arc<>. +/// same as a regular pool lease_resource(..) except that the result is wrapped in an Arc<>. /// /// AliasPool derefs to the base pool type and so leasing may be used normally too. fn main() -> Result<(), DriverError> { diff --git a/examples/app.rs b/examples/app.rs index 551bf6cf..fedf104e 100644 --- a/examples/app.rs +++ b/examples/app.rs @@ -114,7 +114,7 @@ impl Context { let swapchain_image = graph.bind_resource(swapchain_image); // Rendering goes here! - graph.clear_color_image(swapchain_image, [1.0, 0.0, 1.0]); + graph.clear_color_image(swapchain_image, [1.0, 0.0, 1.0, 1.0]); self.window.pre_present_notify(); self.swapchain diff --git a/examples/bindless.rs b/examples/bindless.rs index 370d856f..6ae225c3 100644 --- a/examples/bindless.rs +++ b/examples/bindless.rs @@ -80,7 +80,7 @@ fn create_images(device: &Device) -> Result>, DriverError> { let mut pool = LazyPool::new(device); - graph.queue().submit(&mut pool, 0, 0)?; + graph.into_queue().submit(&mut pool, 0, 0)?; Ok(textures) } diff --git a/examples/cpu_readback.rs b/examples/cpu_readback.rs index 1d567e39..fe6b8b6b 100644 --- a/examples/cpu_readback.rs +++ b/examples/cpu_readback.rs @@ -35,7 +35,9 @@ fn main() -> Result<(), DriverError> { // Resolve and wait (or you can check has_executed without blocking) - alternatively you might // use device.queue_wait_idle(0) or device.device_wait_idle() - but those block on larger scopes - let mut cmd_buf = graph.queue().submit(&mut HashPool::new(&device), 0, 0)?; + let mut cmd_buf = graph + .into_queue() + .submit(&mut HashPool::new(&device), 0, 0)?; println!("Has executed? {}", cmd_buf.has_executed()?); let started = Instant::now(); diff --git a/examples/fuzzer.rs b/examples/fuzzer.rs index e4a10842..d93cfeea 100644 --- a/examples/fuzzer.rs +++ b/examples/fuzzer.rs @@ -114,19 +114,19 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { .unwrap(); // Vertex 1 - Buffer::copy_from_slice(&mut buf, 0, 0f32.to_ne_bytes().as_slice()); - Buffer::copy_from_slice(&mut buf, 4, 0f32.to_ne_bytes().as_slice()); - Buffer::copy_from_slice(&mut buf, 8, 0f32.to_ne_bytes().as_slice()); + buf.copy_from_slice(0, 0f32.to_ne_bytes().as_slice()); + buf.copy_from_slice(4, 0f32.to_ne_bytes().as_slice()); + buf.copy_from_slice(8, 0f32.to_ne_bytes().as_slice()); // Vertex 2 - Buffer::copy_from_slice(&mut buf, 12, 1f32.to_ne_bytes().as_slice()); - Buffer::copy_from_slice(&mut buf, 16, 1f32.to_ne_bytes().as_slice()); - Buffer::copy_from_slice(&mut buf, 20, 0f32.to_ne_bytes().as_slice()); + buf.copy_from_slice(12, 1f32.to_ne_bytes().as_slice()); + buf.copy_from_slice(16, 1f32.to_ne_bytes().as_slice()); + buf.copy_from_slice(20, 0f32.to_ne_bytes().as_slice()); // Vertex 3 - Buffer::copy_from_slice(&mut buf, 24, 2f32.to_ne_bytes().as_slice()); - Buffer::copy_from_slice(&mut buf, 28, 0f32.to_ne_bytes().as_slice()); - Buffer::copy_from_slice(&mut buf, 32, 0f32.to_ne_bytes().as_slice()); + buf.copy_from_slice(24, 2f32.to_ne_bytes().as_slice()); + buf.copy_from_slice(28, 0f32.to_ne_bytes().as_slice()); + buf.copy_from_slice(32, 0f32.to_ne_bytes().as_slice()); buf }; @@ -142,9 +142,9 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { )) .unwrap(); - Buffer::copy_from_slice(&mut buf, 0, 0u16.to_ne_bytes().as_slice()); - Buffer::copy_from_slice(&mut buf, 2, 1u16.to_ne_bytes().as_slice()); - Buffer::copy_from_slice(&mut buf, 4, 2u16.to_ne_bytes().as_slice()); + buf.copy_from_slice(0, 0u16.to_ne_bytes().as_slice()); + buf.copy_from_slice(2, 1u16.to_ne_bytes().as_slice()); + buf.copy_from_slice(4, 2u16.to_ne_bytes().as_slice()); buf }; @@ -154,13 +154,11 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { max_primitive_count: 1, flags: vk::GeometryFlagsKHR::OPAQUE, geometry: AccelerationStructureGeometryData::Triangles { - index_addr: DeviceOrHostAddress::DeviceAddress(Buffer::device_address(&index_buf)), + index_addr: DeviceOrHostAddress::DeviceAddress(index_buf.device_address()), index_type: vk::IndexType::UINT16, max_vertex: 3, transform_addr: None, - vertex_addr: DeviceOrHostAddress::DeviceAddress(Buffer::device_address( - &vertex_buf, - )), + vertex_addr: DeviceOrHostAddress::DeviceAddress(vertex_buf.device_address()), vertex_format: vk::Format::R32G32B32_SFLOAT, vertex_stride: 12, }, @@ -195,8 +193,7 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { for idx in 0..BLAS_COUNT { let blas = pool.lease_resource(blas_info).unwrap(); - Buffer::copy_from_slice( - &mut instance_buf, + instance_buf.copy_from_slice( idx * instance_len, AccelerationStructure::instance_slice(&[vk::AccelerationStructureInstanceKHR { transform: vk::TransformMatrixKHR { @@ -241,7 +238,7 @@ fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) { flags: vk::GeometryFlagsKHR::OPAQUE, geometry: AccelerationStructureGeometryData::Instances { array_of_pointers: false, - addr: DeviceOrHostAddress::DeviceAddress(Buffer::device_address(&instance_buf)), + addr: DeviceOrHostAddress::DeviceAddress(instance_buf.device_address()), }, }, vk::AccelerationStructureBuildRangeInfoKHR::default().primitive_count(1), @@ -832,7 +829,7 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo LoadOp::CLEAR_BLACK_ALPHA_ZERO, StoreOp::DontCare, ) - .color_attachment_resolve_image(1, frame.swapchain_image, 0) + .color_attachment_resolve_image(0, 1, frame.swapchain_image) .depth_stencil_attachment_image( msaa_depth_stencil_image, LoadOp::CLEAR_ZERO_STENCIL_ZERO, diff --git a/examples/image_sampler.rs b/examples/image_sampler.rs index 4067fd07..96109ef8 100644 --- a/examples/image_sampler.rs +++ b/examples/image_sampler.rs @@ -254,7 +254,9 @@ fn read_image(device: &Device, path: impl AsRef) -> anyhow::Result Result<(), DriverError> { let max_result_buf = copy_image_to_buffer(&device, &mut graph, max_reduced_image)?; graph - .queue() + .into_queue() .submit(&mut HashPool::new(&device), 0, 0)? .wait_until_executed()?; diff --git a/examples/mip_compute.rs b/examples/mip_compute.rs index 223401d8..44af0b61 100644 --- a/examples/mip_compute.rs +++ b/examples/mip_compute.rs @@ -143,7 +143,7 @@ fn main() -> Result<(), DriverError> { let depth_pixel = graph.resource(depth_pixel).clone(); graph - .queue() + .into_queue() .submit(&mut HashPool::new(&device), 0, 0)? .wait_until_executed()?; diff --git a/examples/mip_graphic.rs b/examples/mip_graphic.rs index 93bdbb39..3461c216 100644 --- a/examples/mip_graphic.rs +++ b/examples/mip_graphic.rs @@ -208,7 +208,7 @@ fn fill_mip_levels(device: &Device, image: &Arc) -> Result<(), DriverErro // Submits to the GPU but does not wait for anything to be finished graph - .queue() + .into_queue() .submit(&mut LazyPool::new(device), queue_family_index, 0) .map(|_| ()) } diff --git a/examples/msaa.rs b/examples/msaa.rs index 3e1335d6..c85993f8 100644 --- a/examples/msaa.rs +++ b/examples/msaa.rs @@ -69,8 +69,7 @@ fn main() -> anyhow::Result<()> { vk::BufferUsageFlags::UNIFORM_BUFFER, )) .unwrap(); - Buffer::copy_from_slice( - &mut scene_uniform_buf, + scene_uniform_buf.copy_from_slice( 0, bytes_of(&SceneUniformBuffer { view: Mat4::look_at_lh(Vec3::Z * 4.0, Vec3::ZERO, Vec3::NEG_Y), @@ -138,7 +137,7 @@ fn main() -> anyhow::Result<()> { LoadOp::CLEAR_WHITE_ALPHA_ONE, StoreOp::DontCare, ) - .set_color_attachment_resolve_image(1, frame.swapchain_image, 0) + .set_color_attachment_resolve_image(0, 1, frame.swapchain_image) .set_depth_stencil_attachment_image( msaa_depth_image, LoadOp::CLEAR_ZERO_STENCIL_ZERO, diff --git a/examples/multipass.rs b/examples/multipass.rs index 37112d6a..400fa88b 100644 --- a/examples/multipass.rs +++ b/examples/multipass.rs @@ -307,7 +307,7 @@ fn create_funky_shape(device: &Device, pool: &mut LazyPool) -> Result anyhow::Result<()> { // Submit on a queue we are reserving for only this thread to use graph - .queue() + .into_queue() .submit(&mut pool, secondary_queue_family_index, queue_index) .unwrap(); @@ -257,7 +257,9 @@ fn load_font(device: &Device) -> anyhow::Result { let page_0 = graph.resource(page_0).clone(); // This copy happens in queue index 0! - graph.queue().submit(&mut HashPool::new(device), 0, 0)?; + graph + .into_queue() + .submit(&mut HashPool::new(device), 0, 0)?; BitmapFont::new(device, font, [page_0]) } diff --git a/examples/ray_omni.rs b/examples/ray_omni.rs index 44769dbd..c09d9502 100644 --- a/examples/ray_omni.rs +++ b/examples/ray_omni.rs @@ -69,8 +69,7 @@ fn main() -> anyhow::Result<()> { vk::BufferUsageFlags::UNIFORM_BUFFER, )) .unwrap(); - Buffer::copy_from_slice( - &mut buf, + buf.copy_from_slice( 0, bytes_of(&Camera { projection: Mat4::perspective_rh( @@ -167,11 +166,11 @@ fn create_blas( max_primitive_count: model.index_count / 3, flags: vk::GeometryFlagsKHR::OPAQUE, geometry: AccelerationStructureGeometryData::triangles( - Buffer::device_address(&model.index_buf), + model.index_buf.device_address(), vk::IndexType::UINT32, model.vertex_count, None, - Buffer::device_address(&model.vertex_buf), + model.vertex_buf.device_address(), vk::Format::R32G32B32_SFLOAT, 24, ), @@ -231,7 +230,9 @@ fn create_blas( let blas = graph.resource(blas).clone(); - graph.queue().submit(&mut LazyPool::new(device), 0, 0)?; + graph + .into_queue() + .submit(&mut LazyPool::new(device), 0, 0)?; Ok(blas) } @@ -357,7 +358,7 @@ fn create_tlas( | vk::BufferUsageFlags::STORAGE_BUFFER, ), )?; - Buffer::copy_from_slice(&mut buffer, 0, instance_data); + buffer.copy_from_slice(0, instance_data); buffer }); @@ -365,7 +366,7 @@ fn create_tlas( let info = AccelerationStructureGeometryInfo::tlas([( AccelerationStructureGeometry::opaque( 2, - AccelerationStructureGeometryData::instances(Buffer::device_address(&instance_buf)), + AccelerationStructureGeometryData::instances(instance_buf.device_address()), ), vk::AccelerationStructureBuildRangeInfoKHR::default().primitive_count(1), )]) diff --git a/examples/ray_trace.rs b/examples/ray_trace.rs index 15a1b1c7..873e8687 100644 --- a/examples/ray_trace.rs +++ b/examples/ray_trace.rs @@ -398,7 +398,7 @@ fn load_scene_buffers( | vk::BufferUsageFlags::STORAGE_BUFFER, ), )?; - Buffer::copy_from_slice(&mut buf, 0, data); + buf.copy_from_slice(0, data); buf }; @@ -413,7 +413,7 @@ fn load_scene_buffers( | vk::BufferUsageFlags::STORAGE_BUFFER, ), )?; - Buffer::copy_from_slice(&mut buf, 0, data); + buf.copy_from_slice(0, data); buf }; @@ -429,7 +429,7 @@ fn load_scene_buffers( device, BufferInfo::host_mem(data.len() as _, vk::BufferUsageFlags::STORAGE_BUFFER), )?; - Buffer::copy_from_slice(&mut buf, 0, data); + buf.copy_from_slice(0, data); buf }; @@ -466,7 +466,7 @@ fn load_scene_buffers( device, BufferInfo::host_mem(buf_len as _, vk::BufferUsageFlags::STORAGE_BUFFER), )?; - Buffer::copy_from_slice(&mut buf, 0, unsafe { + buf.copy_from_slice(0, unsafe { from_raw_parts(materials.as_ptr() as *const _, buf_len) }); buf @@ -551,7 +551,7 @@ fn main() -> anyhow::Result<()> { buf }); - let sbt_address = Buffer::device_address(&sbt_buf); + let sbt_address = sbt_buf.device_address(); let sbt_rgen = vk::StridedDeviceAddressRegionKHR { device_address: sbt_address, stride: shader_group_handle_size as _, @@ -584,11 +584,11 @@ fn main() -> anyhow::Result<()> { AccelerationStructureGeometry::opaque( triangle_count, AccelerationStructureGeometryData::triangles( - Buffer::device_address(&index_buf), + index_buf.device_address(), vk::IndexType::UINT32, vertex_count, None, - Buffer::device_address(&vertex_buf), + vertex_buf.device_address(), vk::Format::R32G32B32_SFLOAT, 12, ), @@ -633,7 +633,7 @@ fn main() -> anyhow::Result<()> { | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS, ), )?; - Buffer::copy_from_slice(&mut buffer, 0, instance_data); + buffer.copy_from_slice(0, instance_data); buffer }); @@ -645,7 +645,7 @@ fn main() -> anyhow::Result<()> { let tlas_geometry_info = AccelerationStructureGeometryInfo::tlas([( AccelerationStructureGeometry::opaque( 1, - AccelerationStructureGeometryData::instances(Buffer::device_address(&instance_buf)), + AccelerationStructureGeometryData::instances(instance_buf.device_address()), ), vk::AccelerationStructureBuildRangeInfoKHR::default().primitive_count(1), )]); @@ -733,7 +733,7 @@ fn main() -> anyhow::Result<()> { }); } - graph.queue().submit(&mut cache, 0, 0)?; + graph.into_queue().submit(&mut cache, 0, 0)?; } // ------------------------------------------------------------------------------------------ // @@ -833,7 +833,7 @@ fn main() -> anyhow::Result<()> { vk::BufferUsageFlags::UNIFORM_BUFFER, )) .unwrap(); - Buffer::copy_from_slice(&mut buf, 0, unsafe { + buf.copy_from_slice(0, unsafe { std::slice::from_raw_parts( &Camera { position, diff --git a/examples/rt_triangle.rs b/examples/rt_triangle.rs index 230e1d48..845d5a4b 100644 --- a/examples/rt_triangle.rs +++ b/examples/rt_triangle.rs @@ -156,7 +156,7 @@ fn main() -> anyhow::Result<()> { buf }); - let sbt_address = Buffer::device_address(&sbt_buf); + let sbt_address = sbt_buf.device_address(); let sbt_rgen = vk::StridedDeviceAddressRegionKHR { device_address: sbt_address, stride: shader_group_handle_size as _, @@ -212,7 +212,7 @@ fn main() -> anyhow::Result<()> { | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS, ), )?; - Buffer::copy_from_slice(&mut buf, 0, data); + buf.copy_from_slice(0, data); Arc::new(buf) }; @@ -226,7 +226,7 @@ fn main() -> anyhow::Result<()> { | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS, ), )?; - Buffer::copy_from_slice(&mut buf, 0, data); + buf.copy_from_slice(0, data); Arc::new(buf) }; @@ -238,11 +238,11 @@ fn main() -> anyhow::Result<()> { AccelerationStructureGeometry::opaque( triangle_count, AccelerationStructureGeometryData::triangles( - Buffer::device_address(&index_buf), + index_buf.device_address(), vk::IndexType::UINT32, vertex_count, None, - Buffer::device_address(&vertex_buf), + vertex_buf.device_address(), vk::Format::R32G32B32_SFLOAT, 12, ), @@ -287,7 +287,7 @@ fn main() -> anyhow::Result<()> { | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS, ), )?; - Buffer::copy_from_slice(&mut buffer, 0, instance_data); + buffer.copy_from_slice(0, instance_data); buffer }); @@ -299,7 +299,7 @@ fn main() -> anyhow::Result<()> { let tlas_geometry_info = AccelerationStructureGeometryInfo::tlas([( AccelerationStructureGeometry::opaque( 1, - AccelerationStructureGeometryData::instances(Buffer::device_address(&instance_buf)), + AccelerationStructureGeometryData::instances(instance_buf.device_address()), ), vk::AccelerationStructureBuildRangeInfoKHR::default().primitive_count(1), )]); @@ -387,7 +387,7 @@ fn main() -> anyhow::Result<()> { }); } - graph.queue().submit(&mut pool, 0, 0)?; + graph.into_queue().submit(&mut pool, 0, 0)?; } // ------------------------------------------------------------------------------------------ // diff --git a/examples/shader-toy/src/main.rs b/examples/shader-toy/src/main.rs index d3c13776..d0456ba3 100644 --- a/examples/shader-toy/src/main.rs +++ b/examples/shader-toy/src/main.rs @@ -173,7 +173,7 @@ fn main() -> anyhow::Result<()> { let mut blank_image_binding = Some(graph.resource(blank_image).clone()); let mut temp_image_binding = Some(graph.resource(temp_image).clone()); - graph.queue().submit(&mut cache, 0, 0)?; + graph.into_queue().submit(&mut cache, 0, 0)?; let started_at = Instant::now(); let mut count = 0i32; diff --git a/examples/skeletal-anim/src/main.rs b/examples/skeletal-anim/src/main.rs index 3961e300..c1b316ea 100644 --- a/examples/skeletal-anim/src/main.rs +++ b/examples/skeletal-anim/src/main.rs @@ -94,8 +94,7 @@ fn main() -> Result<(), WindowError> { )) .unwrap(); - Buffer::copy_from_slice( - &mut buf, + buf.copy_from_slice( 0, bytes_of(&CameraUniform { projection, @@ -120,7 +119,7 @@ fn main() -> Result<(), WindowError> { )) .unwrap(); - Buffer::copy_from_slice(&mut buf, 0, cast_slice(joints)); + buf.copy_from_slice(0, cast_slice(joints)); buf }); @@ -202,11 +201,15 @@ fn load_texture(device: &Device, pak: &mut PakBuf, key: &str) -> Result anyhow::Result<()> { }; let mut pool = LazyPool::new(device); - let mut graphs = Vec::with_capacity(Swapchain::images(&swapchain).len()); - for _ in 0..graphs.capacity() { - graphs.push(None); - } + let swapchain_image_count = Swapchain::images(&swapchain).len(); + let mut swapchain_queues = repeat_with(|| None) + .take(swapchain_image_count) + .collect::>(); let res_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("res"); @@ -300,7 +301,7 @@ fn main() -> anyhow::Result<()> { data.len() as _, vk::BufferUsageFlags::UNIFORM_BUFFER, ))?; - Buffer::copy_from_slice(&mut buf, 0, data); + buf.copy_from_slice(0, data); graph.bind_resource(buf) }; @@ -339,7 +340,7 @@ fn main() -> anyhow::Result<()> { data.len() as _, vk::BufferUsageFlags::UNIFORM_BUFFER, ))?; - Buffer::copy_from_slice(&mut buf, 0, data); + buf.copy_from_slice(0, data); graph.bind_resource(buf) }; @@ -454,8 +455,6 @@ fn main() -> anyhow::Result<()> { .begin_cmd() .debug_name("Woolly Mammoth") .bind_pipeline(mammoth_pipeline.hot()) - .depth_stencil(DepthStencilInfo::DEPTH_WRITE_LESS_IGNORE_STENCIL) - .multiview(VIEW_MASK, VIEW_MASK) .resource_access(index_buf, AccessType::IndexBuffer) .resource_access(vertex_buf, AccessType::VertexBuffer) .shader_resource_access(0, camera_buf, AccessType::VertexShaderReadUniformBuffer) @@ -470,12 +469,14 @@ fn main() -> anyhow::Result<()> { occlusion_texture, AccessType::FragmentShaderReadSampledImageOrUniformTexelBuffer, ) + .multiview(VIEW_MASK, VIEW_MASK) + .color_attachment_image(0, swapchain_image, LoadOp::DontCare, StoreOp::Store) + .depth_stencil(DepthStencilInfo::DEPTH_WRITE_LESS_IGNORE_STENCIL) .depth_stencil_attachment_image( depth_image, LoadOp::CLEAR_ZERO_STENCIL_ZERO, StoreOp::DontCare, ) - .color_attachment_image(0, swapchain_image, LoadOp::DontCare, StoreOp::Store) .record_cmd_buf(move |cmd_buf| { cmd_buf .bind_index_buffer(index_buf, 0, vk::IndexType::UINT32) @@ -489,12 +490,12 @@ fn main() -> anyhow::Result<()> { // the image - afterwards we keep the submitted command buffer around (including all // in-flight resources) so that nothing is dropped until that image is actually done. swapchain.wait_image(xr::Duration::INFINITE).unwrap(); - let cmd_buf = graph - .queue() + let swapchain_queue = graph + .into_queue() .submit(&mut pool, queue_family_index as _, 0) .unwrap(); swapchain.release_image().unwrap(); - graphs[swapchain_image_index as usize] = Some(cmd_buf); + swapchain_queues[swapchain_image_index as usize] = Some(swapchain_queue); frame_stream.end( xr_frame_state.predicted_display_time, @@ -784,7 +785,7 @@ fn load_texture( let queue_family_index = device_queue_family_index(device, vk::QueueFlags::TRANSFER).unwrap(); graph - .queue() + .into_queue() .submit(&mut LazyPool::new(device), queue_family_index as _, 0)? .wait_until_executed()?; diff --git a/examples/vsm_omni.rs b/examples/vsm_omni.rs index df98a4b6..ac812038 100644 --- a/examples/vsm_omni.rs +++ b/examples/vsm_omni.rs @@ -1195,7 +1195,7 @@ fn lease_uniform_buffer( data.len() as _, vk::BufferUsageFlags::UNIFORM_BUFFER, ))?; - Buffer::copy_from_slice(&mut buf, 0, data); + buf.copy_from_slice(0, data); Ok(buf) } diff --git a/src/cmd/compute.rs b/src/cmd/compute.rs index 6e567cbf..85c67ac7 100644 --- a/src/cmd/compute.rs +++ b/src/cmd/compute.rs @@ -332,6 +332,15 @@ impl PipelineCommandRef<'_, ComputePipeline> { mut self, func: impl FnOnce(ComputeCommandBufferRef<'_>) + Send + 'static, ) -> Self { + self.record_cmd_buf_mut(func); + self + } + + /// Begin recording a compute pipeline command buffer. + pub fn record_cmd_buf_mut( + &mut self, + func: impl FnOnce(ComputeCommandBufferRef<'_>) + Send + 'static, + ) { let pipeline = self .cmd .cmd() @@ -347,8 +356,6 @@ impl PipelineCommandRef<'_, ComputePipeline> { self.cmd.push_exec(move |cmd_buf| { func(ComputeCommandBufferRef { cmd_buf, pipeline }); }); - - self } } diff --git a/src/cmd/graphic.rs b/src/cmd/graphic.rs index dabc608a..d663bfa0 100644 --- a/src/cmd/graphic.rs +++ b/src/cmd/graphic.rs @@ -737,14 +737,14 @@ impl PipelineCommandRef<'_, GraphicPipeline> { /// TODO pub fn color_attachment_resolve_image( mut self, + msaa_attachment_idx: AttachmentIndex, color_attachment_idx: AttachmentIndex, color_image: impl Into, - resolve_attachment_idx: AttachmentIndex, ) -> Self { self.set_color_attachment_resolve_image( + msaa_attachment_idx, color_attachment_idx, color_image, - resolve_attachment_idx, ); self } @@ -752,16 +752,16 @@ impl PipelineCommandRef<'_, GraphicPipeline> { /// TODO pub fn color_attachment_resolve_image_view( mut self, + msaa_attachment_idx: AttachmentIndex, color_attachment_idx: AttachmentIndex, color_image: impl Into, color_image_view_info: impl Into, - resolve_attachment_idx: AttachmentIndex, ) -> Self { self.set_color_attachment_resolve_image_view( + msaa_attachment_idx, color_attachment_idx, color_image, color_image_view_info, - resolve_attachment_idx, ); self } @@ -851,6 +851,15 @@ impl PipelineCommandRef<'_, GraphicPipeline> { mut self, func: impl FnOnce(GraphicCommandBufferRef<'_>) + Send + 'static, ) -> Self { + self.record_cmd_buf_mut(func); + self + } + + /// Begin recording a graphics pipeline command buffer. + pub fn record_cmd_buf_mut( + &mut self, + func: impl FnOnce(GraphicCommandBufferRef<'_>) + Send + 'static, + ) { let pipeline = self .cmd .cmd() @@ -866,8 +875,6 @@ impl PipelineCommandRef<'_, GraphicPipeline> { self.cmd.push_exec(move |cmd_buf| { func(GraphicCommandBufferRef { cmd_buf, pipeline }); }); - - self } /// Sets the [`renderArea`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkRenderPassBeginInfo.html#_c_specification) @@ -950,18 +957,18 @@ impl PipelineCommandRef<'_, GraphicPipeline> { /// See [color_attachment_resolve_image] pub fn set_color_attachment_resolve_image( &mut self, + msaa_attachment_idx: AttachmentIndex, color_attachment_idx: AttachmentIndex, color_image: impl Into, - resolve_attachment_idx: AttachmentIndex, ) -> &mut Self { let color_image = color_image.into(); let color_image_view = self.resource(color_image).info; self.set_color_attachment_resolve_image_view( + msaa_attachment_idx, color_attachment_idx, color_image, color_image_view, - resolve_attachment_idx, ); self @@ -970,10 +977,10 @@ impl PipelineCommandRef<'_, GraphicPipeline> { /// See [color_attachment_resolve_image_view] pub fn set_color_attachment_resolve_image_view( &mut self, + msaa_attachment_idx: AttachmentIndex, color_attachment_idx: AttachmentIndex, color_image: impl Into, color_image_view_info: impl Into, - resolve_attachment_idx: AttachmentIndex, ) -> &mut Self { let color_image = color_image.into(); let color_image_view_info = color_image_view_info.into(); @@ -981,7 +988,7 @@ impl PipelineCommandRef<'_, GraphicPipeline> { #[allow(deprecated)] self.set_attach_color_as(color_attachment_idx, color_image, color_image_view_info) .set_resolve_color_as( - resolve_attachment_idx, + msaa_attachment_idx, color_attachment_idx, color_image, color_image_view_info, diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index 8d867e2a..30ade478 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -165,11 +165,20 @@ impl<'a> CommandRef<'a> { mut self, func: impl FnOnce(CommandBufferRef<'_>) + Send + 'static, ) -> Self { + self.record_cmd_buf_mut(func); + self + } + + /// Begin recording an acceleration structure command buffer. + /// + /// This is the entry point for building and updating an [`AccelerationStructure`] instance. + /// + /// The provided closure allows you to run any Vulkan code, or interoperate with other Vulkan + /// code and interfaces. + pub fn record_cmd_buf_mut(&mut self, func: impl FnOnce(CommandBufferRef<'_>) + Send + 'static) { self.push_exec(move |cmd_buf| { func(cmd_buf); }); - - self } /// Returns a borrow of the original Vulkan resource (buffer, image or acceleration structure) diff --git a/src/cmd/pipeline.rs b/src/cmd/pipeline.rs index 530c6a15..ed7746e8 100644 --- a/src/cmd/pipeline.rs +++ b/src/cmd/pipeline.rs @@ -213,7 +213,7 @@ mod deprecated { use { crate::{ BindGraph, Bound, Graph, - cmd::{Descriptor, PipelineCommandRef, SubresourceRange, View}, + cmd::{Descriptor, PipelineCommandRef, SubresourceRange, View, ViewInfo}, deprecated::Info, graph::pass_ref::ViewType, node::Node, @@ -223,7 +223,7 @@ mod deprecated { }; impl<'a, T> PipelineCommandRef<'a, T> { - #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[deprecated = "use shader_resource_access function"] #[doc(hidden)] pub fn access_descriptor( self, @@ -242,7 +242,7 @@ mod deprecated { self.access_descriptor_as(descriptor, node, access, view_info) } - #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[deprecated = "use shader_subresource_access function"] #[doc(hidden)] pub fn access_descriptor_as( self, @@ -262,7 +262,7 @@ mod deprecated { self.access_descriptor_subrange(descriptor, node, access, view_info, subresource) } - #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] + #[deprecated = "use shader_subresource_access function"] #[doc(hidden)] pub fn access_descriptor_subrange( self, diff --git a/src/cmd/ray_trace.rs b/src/cmd/ray_trace.rs index 504faef5..815bda1c 100644 --- a/src/cmd/ray_trace.rs +++ b/src/cmd/ray_trace.rs @@ -13,6 +13,15 @@ impl PipelineCommandRef<'_, RayTracePipeline> { mut self, func: impl FnOnce(RayTraceCommandBufferRef<'_>) + Send + 'static, ) -> Self { + self.record_cmd_buf_mut(func); + self + } + + /// Begin recording a ray trace pipeline command buffer. + pub fn record_cmd_buf_mut( + &mut self, + func: impl FnOnce(RayTraceCommandBufferRef<'_>) + Send + 'static, + ) { let pipeline = self .cmd .cmd() @@ -38,8 +47,6 @@ impl PipelineCommandRef<'_, RayTracePipeline> { pipeline, }); }); - - self } } diff --git a/src/driver/buffer.rs b/src/driver/buffer.rs index 30b968ed..54158708 100644 --- a/src/driver/buffer.rs +++ b/src/driver/buffer.rs @@ -46,7 +46,7 @@ use std::sync::Mutex; /// # let device = Device::new(DeviceInfo::default())?; /// # let info = BufferInfo::device_mem(8, vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS); /// # let my_buf = Buffer::create(&device, info)?; -/// let addr = Buffer::device_address(&my_buf); +/// let addr = my_buf.device_address(); /// # Ok(()) } /// ``` /// @@ -357,7 +357,7 @@ impl Buffer { /// # let device = Device::new(DeviceInfo::default())?; /// # let info = BufferInfo::host_mem(4, vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS); /// # let my_buf = Buffer::create(&device, info)?; - /// let addr = Buffer::device_address(&my_buf); + /// let addr = my_buf.device_address(); /// /// assert_ne!(addr, 0); /// # Ok(()) } diff --git a/src/driver/shader.rs b/src/driver/shader.rs index fed71ca5..67b910e5 100644 --- a/src/driver/shader.rs +++ b/src/driver/shader.rs @@ -23,6 +23,7 @@ use { }; #[allow(deprecated)] +#[deprecated = "use SpecializationMap struct"] #[doc(hidden)] pub type SpecializationInfo = self::deprecated::SpecializationInfo; diff --git a/src/lib.rs b/src/lib.rs index 3bb4b7a1..5545c434 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -491,20 +491,35 @@ impl ClearColorValue { pub const WHITE_ALPHA_ZERO: Self = Self::Float32([1.0, 1.0, 1.0, 0.0]); /// Convenience constructor for clear color values. - pub fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self { + pub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self { Self::Float32([r, g, b, a]) } + + /// Convert RGB+A values into a ClearColorValue. + pub const fn from_f32(r: f32, g: f32, b: f32, a: f32) -> Self { + Self::rgba(r, g, b, a) + } + + /// Convert RGB+A values into a ClearColorValue. + pub const fn from_u8(r: u8, g: u8, b: u8, a: u8) -> Self { + Self::from_f32( + r as f32 / u8::MAX as f32, + g as f32 / u8::MAX as f32, + b as f32 / u8::MAX as f32, + a as f32 / u8::MAX as f32, + ) + } } -impl From<[f32; 4]> for ClearColorValue { - fn from(float32: [f32; 4]) -> Self { - Self::Float32(float32) +impl Default for ClearColorValue { + fn default() -> Self { + Self::from_f32(0.0, 0.0, 0.0, 0.0) } } -impl From<[f32; 3]> for ClearColorValue { - fn from(float32: [f32; 3]) -> Self { - Self::from([float32[0], float32[1], float32[2], 1.0]) +impl From<[f32; 4]> for ClearColorValue { + fn from(float32: [f32; 4]) -> Self { + Self::Float32(float32) } } @@ -516,18 +531,7 @@ impl From<[i32; 4]> for ClearColorValue { impl From<[u8; 4]> for ClearColorValue { fn from(uint8: [u8; 4]) -> Self { - Self::from([ - uint8[0] as f32 / u8::MAX as f32, - uint8[1] as f32 / u8::MAX as f32, - uint8[2] as f32 / u8::MAX as f32, - uint8[3] as f32 / u8::MAX as f32, - ]) - } -} - -impl From<[u8; 3]> for ClearColorValue { - fn from(uint8: [u8; 3]) -> Self { - Self::from([uint8[0], uint8[1], uint8[2], u8::MAX]) + Self::from_u8(uint8[0], uint8[1], uint8[2], uint8[3]) } } @@ -1244,19 +1248,10 @@ impl Graph { None } - /// Returns a borrow of the original Vulkan resource (buffer, image or acceleration structure) - /// which the given node represents. - pub fn resource(&self, node: N) -> &N::Resource - where - N: Bound, - { - node.borrow(&self.resources) - } - /// Finalizes the graph and provides an object with functions for submitting the resulting /// commands. #[profiling::function] - pub fn queue(mut self) -> Queue { + pub fn into_queue(mut self) -> Queue { // The final execution of each pass has no function for cmd in &mut self.cmds { cmd.execs.pop(); @@ -1265,6 +1260,15 @@ impl Graph { Queue::new(self) } + /// Returns a borrow of the original Vulkan resource (buffer, image or acceleration structure) + /// which the given node represents. + pub fn resource(&self, node: N) -> &N::Resource + where + N: Bound, + { + node.borrow(&self.resources) + } + /// Note: `data` must not exceed 65536 bytes. #[profiling::function] pub fn update_buffer( @@ -1365,8 +1369,8 @@ pub mod graph { #[deprecated = "use vk_graph::cmd::CommandBufferRef"] pub type AccelerationStructureUpdateInfo = crate::cmd::UpdateAccelerationStructureInfo; - #[deprecated = "use vk_graph::Descriptor"] - pub type Descriptor = crate::Descriptor; + #[deprecated = "use vk_graph::cmd::Descriptor"] + pub type Descriptor = crate::cmd::Descriptor; #[deprecated = "use vk_graph::cmd::GraphicCommandBufferRef"] pub type Draw<'a> = crate::cmd::GraphicCommandBufferRef<'a>; @@ -1633,10 +1637,10 @@ pub(crate) mod deprecated { node.info(&self.resources) } - #[deprecated = "use queue function"] + #[deprecated = "use into_queue function"] #[doc(hidden)] pub fn resolve(self) -> crate::graph::Resolver { - self.queue() + self.into_queue() } #[deprecated = "use resource and clone functions"] diff --git a/src/queue.rs b/src/queue.rs index ed9a9a7e..afb5cec2 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -3227,6 +3227,12 @@ impl Queue { } } +impl From for Queue { + fn from(val: Graph) -> Self { + val.into_queue() + } +} + #[derive(Default)] struct Schedule { access_cache: AccessCache, From bae481d7d0803fee6ea3410b9336718a298291d4 Mon Sep 17 00:00:00 2001 From: John Wells Date: Wed, 4 Mar 2026 05:07:44 -0500 Subject: [PATCH 32/86] clean up bind traits --- contrib/vk-graph-prelude/src/lib.rs | 2 +- src/cmd/bind.rs | 91 ------------ src/cmd/cmd_buf.rs | 11 +- src/cmd/compute.rs | 2 +- src/cmd/mod.rs | 34 ++--- src/cmd/pipeline.rs | 111 ++++++++++++-- src/lib.rs | 18 ++- src/node.rs | 2 +- src/queue.rs | 8 +- src/{bind.rs => resource.rs} | 215 ++++++++++++---------------- 10 files changed, 236 insertions(+), 258 deletions(-) delete mode 100644 src/cmd/bind.rs rename src/{bind.rs => resource.rs} (84%) diff --git a/contrib/vk-graph-prelude/src/lib.rs b/contrib/vk-graph-prelude/src/lib.rs index 7cad28ed..934aa4dc 100644 --- a/contrib/vk-graph-prelude/src/lib.rs +++ b/contrib/vk-graph-prelude/src/lib.rs @@ -3,7 +3,7 @@ #![warn(missing_docs)] pub use vk_graph::{ - BindGraph, Bound, ClearColorValue, Graph, + ClearColorValue, Graph, GraphNode, GraphResource, cmd::{ BuildAccelerationStructureIndirectInfo, BuildAccelerationStructureInfo, CommandRef, LoadOp, PipelineCommandRef, StoreOp, UpdateAccelerationStructureIndirectInfo, diff --git a/src/cmd/bind.rs b/src/cmd/bind.rs deleted file mode 100644 index 7d57037d..00000000 --- a/src/cmd/bind.rs +++ /dev/null @@ -1,91 +0,0 @@ -use { - super::{CommandRef, pipeline::PipelineCommandRef}, - crate::{ - ExecutionPipeline, - driver::{compute::ComputePipeline, graphic::GraphicPipeline, ray_trace::RayTracePipeline}, - }, - std::marker::PhantomData, -}; - -/// A trait for pipelines which may be bound to a `CommandRef`. -/// -/// See [`CommandRef::bind_pipeline`](super::cmd::CommandRef::bind_pipeline) for details. -pub trait BindCommand<'a> { - /// The resource reference type. - type Ref; - - /// Binds the resource to a command. - /// - /// Returns a reference type. - fn bind_cmd(self, _: CommandRef<'a>) -> Self::Ref; -} - -macro_rules! bind_cmd_pipeline { - ($name:ident) => { - paste::paste! { - impl<'a> BindCommand<'a> for &'a [<$name Pipeline>] { - type Ref = PipelineCommandRef<'a, [<$name Pipeline>]>; - - fn bind_cmd(self, mut cmd: CommandRef<'a>) -> Self::Ref { - { - let cmd = cmd.cmd_mut(); - if cmd.execs.last().unwrap().pipeline.is_some() { - cmd.execs.push(Default::default()); - } - - cmd.execs.last_mut().unwrap().pipeline - = Some(ExecutionPipeline::$name(self.clone())); - } - - Self::Ref { - __: PhantomData, - cmd, - } - } - - } - - impl<'a> BindCommand<'a> for [<$name Pipeline>] { - type Ref = PipelineCommandRef<'a, [<$name Pipeline>]>; - - fn bind_cmd(self, mut cmd: CommandRef<'a>) -> Self::Ref { - { - let cmd = cmd.cmd_mut(); - if cmd.execs.last().unwrap().pipeline.is_some() { - cmd.execs.push(Default::default()); - } - - cmd.execs.last_mut().unwrap().pipeline - = Some(ExecutionPipeline::$name(self)); - } - - Self::Ref { - __: PhantomData, - cmd, - } - } - } - - impl ExecutionPipeline { - #[allow(unused)] - pub(crate) fn [](&self) -> bool { - matches!(self, Self::$name(_)) - } - - #[allow(unused)] - pub(crate) fn [](&self) -> &[<$name Pipeline>] { - if let Self::$name(binding) = self { - &binding - } else { - panic!(); - } - } - } - } - }; -} - -// Pipelines you can bind to a pass -bind_cmd_pipeline!(Compute); -bind_cmd_pipeline!(Graphic); -bind_cmd_pipeline!(RayTrace); diff --git a/src/cmd/cmd_buf.rs b/src/cmd/cmd_buf.rs index f0421405..a37fe2ec 100644 --- a/src/cmd/cmd_buf.rs +++ b/src/cmd/cmd_buf.rs @@ -1,6 +1,6 @@ use { crate::{ - AnyAccelerationStructureNode, Bound, Resource, + AnyAccelerationStructureNode, GraphNode, Resource, driver::{ accel_struct::{ AccelerationStructureGeometry, AccelerationStructureGeometryInfo, @@ -9,6 +9,7 @@ use { cmd_buf::CommandBuffer, device::Device, }, + node::Node, }, ash::vk, std::{cell::RefCell, ops::Deref}, @@ -474,9 +475,9 @@ impl<'a> CommandBufferRef<'a> { /// Returns a borrow of the original Vulkan resource (buffer, image or acceleration structure) /// which the given bound resource represents. - pub fn resource(&self, resource: N) -> &N::Resource + pub fn resource(&self, node: N) -> &N::Resource where - N: Bound, + N: GraphNode + Node, { // You must have called an access function for this node on this execution before indexing // into the bindings data! @@ -485,11 +486,11 @@ impl<'a> CommandBufferRef<'a> { // resource (buffer, image, or acceleration structure). In order to access any resources the // access type must first be specified so the correct barriers may be added. debug_assert!( - self.exec.accesses.contains_key(&resource.index()), + self.exec.accesses.contains_key(&node.index()), "unexpected node access: call access, read, or write first" ); - resource.borrow(self.resources) + node.borrow(self.resources) } } diff --git a/src/cmd/compute.rs b/src/cmd/compute.rs index 85c67ac7..bcb5cba0 100644 --- a/src/cmd/compute.rs +++ b/src/cmd/compute.rs @@ -428,7 +428,7 @@ mod deprecated { #[deprecated = "use shader_resource_access function with AccessType::ComputeShaderWrite"] #[doc(hidden)] - pub fn write_descriptor(self, descriptor: impl Into, node: N) -> Self + pub fn shader_resource_accesswrite_descriptor(self, descriptor: impl Into, node: N) -> Self where N: Node + View, N::Info: Copy, diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index 30ade478..fd714e79 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -1,6 +1,5 @@ //! Strongly-typed rendering commands. -mod bind; mod cmd_buf; mod compute; mod graphic; @@ -14,21 +13,23 @@ pub use self::{ }, compute::ComputeCommandBufferRef, graphic::{GraphicCommandBufferRef, LoadOp, StoreOp}, - pipeline::PipelineCommandRef, + pipeline::{CommandPipeline, PipelineCommandRef}, ray_trace::RayTraceCommandBufferRef, }; use { - self::bind::BindCommand, super::{ AccelerationStructureLeaseNode, AccelerationStructureNode, AnyAccelerationStructureNode, - AnyBufferNode, AnyImageNode, BindGraph, Bound, BufferLeaseNode, BufferNode, Command, - Execution, ExecutionFunction, Graph, ImageLeaseNode, ImageNode, Node, Resource, + AnyBufferNode, AnyImageNode, BufferLeaseNode, BufferNode, Command, Execution, + ExecutionFunction, Graph, GraphResource, ImageLeaseNode, ImageNode, Node, Resource, SwapchainImageNode, }, - crate::driver::{ - accel_struct::AccelerationStructureSubresourceRange, buffer::BufferSubresourceRange, - image::ImageViewInfo, + crate::{ + driver::{ + accel_struct::AccelerationStructureSubresourceRange, buffer::BufferSubresourceRange, + image::ImageViewInfo, + }, + resource::GraphNode, }, ash::vk, std::ops::Range, @@ -84,7 +85,7 @@ impl<'a> CommandRef<'a> { /// Bound nodes may be used in passes for pipeline and shader operations. pub fn bind_resource(&mut self, resource: R) -> R::Node where - R: BindGraph, + R: GraphResource, { self.graph.bind_resource(resource) } @@ -93,7 +94,7 @@ impl<'a> CommandRef<'a> { /// pass, allowing for strongly typed access to the related functions. pub fn bind_pipeline

(self, pipeline: P) -> P::Ref where - P: BindCommand<'a>, + P: CommandPipeline<'a>, { pipeline.bind_cmd(self) } @@ -183,11 +184,11 @@ impl<'a> CommandRef<'a> { /// Returns a borrow of the original Vulkan resource (buffer, image or acceleration structure) /// which the given bound resource represents. - pub fn resource(&self, resource: N) -> &N::Resource + pub fn resource(&self, node: N) -> &N::Resource where - N: Bound, + N: GraphNode, { - self.graph.resource(resource) + self.graph.resource(node) } /// Informs the command that the next recorded command buffer will read or write `node` using @@ -385,7 +386,6 @@ pub(super) struct SubresourceAccess { } /// Allows for a resource to be reinterpreted as differently formatted data. -#[doc(hidden)] pub trait View { /// The information about the resource when bound directly to shader descriptors. type Info; @@ -393,10 +393,12 @@ pub trait View { /// The information about the resource when used indirectly by any part of a graph. type Range; + #[doc(hidden)] fn info(&self, _: &[Resource]) -> Self::Info where Self: Node; + #[doc(hidden)] fn range(&self, _: &[Resource]) -> Self::Range where Self: Node; @@ -550,7 +552,7 @@ impl From> for ViewInfo { mod deprecated { use { crate::{ - BindGraph, Graph, + Graph, GraphResource, cmd::{CommandBufferRef, CommandRef, SubresourceRange, View}, deprecated::Info, node::Node, @@ -642,7 +644,7 @@ mod deprecated { #[doc(hidden)] pub fn bind_node(&mut self, resource: R) -> R::Node where - R: BindGraph, + R: GraphResource, { self.bind_resource(resource) } diff --git a/src/cmd/pipeline.rs b/src/cmd/pipeline.rs index ed7746e8..fbad14e6 100644 --- a/src/cmd/pipeline.rs +++ b/src/cmd/pipeline.rs @@ -1,42 +1,129 @@ use { super::{ - AccessType, BindGraph, Bound, CommandRef, Descriptor, Graph, Node, SubresourceRange, View, - ViewInfo, + AccessType, CommandRef, Descriptor, Graph, GraphNode, GraphResource, Node, + SubresourceRange, View, ViewInfo, + }, + crate::{ + ExecutionPipeline, + driver::{compute::ComputePipeline, graphic::GraphicPipeline, ray_trace::RayTracePipeline}, }, std::marker::PhantomData, }; +/// A trait for pipelines which may be bound to a `CommandRef`. +/// +/// See [`CommandRef::bind_pipeline`](super::cmd::CommandRef::bind_pipeline) for details. +pub trait CommandPipeline<'a> { + /// The resource reference type. + type Ref; + + /// Binds the resource to a command. + /// + /// Returns a reference type. + fn bind_cmd(self, _: CommandRef<'a>) -> Self::Ref; +} + +macro_rules! pipeline { + ($name:ident) => { + paste::paste! { + impl<'a> CommandPipeline<'a> for [<$name Pipeline>] { + type Ref = PipelineCommandRef<'a, [<$name Pipeline>]>; + + fn bind_cmd(self, mut cmd: CommandRef<'a>) -> Self::Ref { + { + let cmd = cmd.cmd_mut(); + if cmd.execs.last().unwrap().pipeline.is_some() { + cmd.execs.push(Default::default()); + } + + cmd.execs.last_mut().unwrap().pipeline + = Some(ExecutionPipeline::$name(self)); + } + + Self::Ref { + __: PhantomData, + cmd, + } + } + } + + impl<'a> CommandPipeline<'a> for &'a [<$name Pipeline>] { + type Ref = PipelineCommandRef<'a, [<$name Pipeline>]>; + + fn bind_cmd(self, mut cmd: CommandRef<'a>) -> Self::Ref { + { + let cmd = cmd.cmd_mut(); + if cmd.execs.last().unwrap().pipeline.is_some() { + cmd.execs.push(Default::default()); + } + + cmd.execs.last_mut().unwrap().pipeline + = Some(ExecutionPipeline::$name(self.clone())); + } + + Self::Ref { + __: PhantomData, + cmd, + } + } + + } + + impl ExecutionPipeline { + #[allow(unused)] + pub(crate) fn [](&self) -> bool { + matches!(self, Self::$name(_)) + } + + #[allow(unused)] + pub(crate) fn [](&self) -> &[<$name Pipeline>] { + if let Self::$name(binding) = self { + &binding + } else { + panic!(); + } + } + } + } + }; +} + +// Pipelines you can bind to a command ref +pipeline!(Compute); +pipeline!(Graphic); +pipeline!(RayTrace); + /// A render pass which has been bound to a particular compute, graphic, or ray-trace pipeline. -pub struct PipelineCommandRef<'a, T> { +pub struct PipelineCommandRef<'c, T> { pub(super) __: PhantomData, - pub(super) cmd: CommandRef<'a>, + pub(super) cmd: CommandRef<'c>, } // NOTE: There are specific implementations of T in the compute, graphic, and ray trace modules -impl<'a, T> PipelineCommandRef<'a, T> { +impl<'c, T> PipelineCommandRef<'c, T> { /// Binds a Vulkan buffer, image, or acceleration structure resource to the graph associated /// with this command. /// /// Bound nodes may be used in passes for pipeline and shader operations. pub fn bind_resource(&mut self, resource: R) -> R::Node where - R: BindGraph, + R: GraphResource, { self.cmd.bind_resource(resource) } /// Finalizes a command and returns the graph so that additional commands may be added. - pub fn end_cmd(self) -> &'a mut Graph { + pub fn end_cmd(self) -> &'c mut Graph { self.cmd.end_cmd() } /// Returns a borrow of the original Vulkan resource (buffer, image or acceleration structure) /// which the given node represents. - pub fn resource(&self, resource: N) -> &N::Resource + pub fn resource(&self, node: N) -> &N::Resource where - N: Bound, + N: GraphNode, { - self.cmd.resource(resource) + self.cmd.resource(node) } /// Informs the command that the next recorded command buffer will read or write `node` using @@ -212,7 +299,7 @@ impl<'a, T> PipelineCommandRef<'a, T> { mod deprecated { use { crate::{ - BindGraph, Bound, Graph, + Graph, GraphResource, cmd::{Descriptor, PipelineCommandRef, SubresourceRange, View, ViewInfo}, deprecated::Info, graph::pass_ref::ViewType, @@ -324,7 +411,7 @@ mod deprecated { #[doc(hidden)] pub fn bind_node(&mut self, resource: R) -> R::Node where - R: BindGraph, + R: GraphResource, { self.bind_resource(resource) } diff --git a/src/lib.rs b/src/lib.rs index 5545c434..2eb92cde 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -356,14 +356,14 @@ pub mod driver; pub mod node; pub mod pool; -mod bind; mod queue; +mod resource; use crate::cmd::CommandBufferRef; pub use self::{ - bind::{BindGraph, Bound, Resource}, queue::Queue, + resource::{GraphNode, GraphResource, Resource}, }; #[allow(deprecated)] @@ -715,7 +715,7 @@ impl Graph { /// general functions. pub fn bind_resource(&mut self, resource: R) -> R::Node where - R: BindGraph, + R: GraphResource, { resource.bind_graph(self) } @@ -1264,7 +1264,7 @@ impl Graph { /// which the given node represents. pub fn resource(&self, node: N) -> &N::Resource where - N: Bound, + N: GraphNode, { node.borrow(&self.resources) } @@ -1406,8 +1406,7 @@ pub mod graph { pub(crate) mod deprecated { use { crate::{ - BindGraph, Graph, - bind::Resource, + Graph, GraphResource, driver::{ DriverError, accel_struct::{AccelerationStructure, AccelerationStructureInfo}, @@ -1425,6 +1424,7 @@ pub(crate) mod deprecated { BufferNode, ImageLeaseNode, ImageNode, Node, SwapchainImageNode, }, pool::{Lease, Pool}, + resource::Resource, }, ash::vk, std::{ops::Range, sync::Arc}, @@ -1466,6 +1466,7 @@ pub(crate) mod deprecated { #[deprecated = "use Swapchain from vk_graph_window crate"] #[derive(Debug)] + #[doc(hidden)] pub struct Display; impl Display { @@ -1502,14 +1503,17 @@ pub(crate) mod deprecated { #[deprecated = "use vk_graph_window::SwapchainError"] #[derive(Clone, Copy, Debug, Default)] + #[doc(hidden)] pub struct DisplayError; #[deprecated = "use vk_graph_window::SwapchainInfo"] #[derive(Clone, Copy, Debug, Default)] + #[doc(hidden)] pub struct DisplayInfo; #[deprecated = "use vk_graph_window::SwapchainInfoBuilder"] #[derive(Clone, Copy, Debug, Default)] + #[doc(hidden)] pub struct DisplayInfoBuilder; // General stuff @@ -1524,7 +1528,7 @@ pub(crate) mod deprecated { #[doc(hidden)] pub fn bind_node(&mut self, resource: R) -> R::Node where - R: BindGraph, + R: GraphResource, { self.bind_resource(resource) } diff --git a/src/node.rs b/src/node.rs index e9710089..29da4143 100644 --- a/src/node.rs +++ b/src/node.rs @@ -110,7 +110,7 @@ impl Node for AnyImageNode { /// A Vulkan resource which has been bound to a [`Graph`] using [`Graph::bind_node`]. pub trait Node { - /// The internal node index of this bound resource. + #[doc(hidden)] fn index(&self) -> NodeIndex; } diff --git a/src/queue.rs b/src/queue.rs index afb5cec2..957c1ab3 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -1,11 +1,11 @@ use { super::{ Attachment, Command, ExecutionPipeline, Graph, Node, NodeIndex, Resource, - bind::ResourceInner, cmd::{SubresourceAccess, SubresourceRange}, + resource::ResourceInner, }, crate::{ - Bound, + GraphNode, cmd::CommandBufferRef, driver::{ AttachmentInfo, AttachmentRef, Descriptor, DescriptorInfo, DescriptorSet, DriverError, @@ -2380,7 +2380,7 @@ impl Queue { for pass_idx in schedule.passes.iter().copied() { let pass = &mut self.graph.cmds[pass_idx]; - profiling::scope!("Pass", &pass.name); + profiling::scope!("Pass", pass.name()); let physical_pass = &mut self.physical_passes[pass_idx]; let is_graphic = physical_pass.render_pass.is_some(); @@ -2632,7 +2632,7 @@ impl Queue { /// which the given node represents. pub fn resource(&self, node: N) -> &N::Resource where - N: Bound, + N: GraphNode, { self.graph.resource(node) } diff --git a/src/bind.rs b/src/resource.rs similarity index 84% rename from src/bind.rs rename to src/resource.rs index e996a989..d1683c33 100644 --- a/src/bind.rs +++ b/src/resource.rs @@ -15,25 +15,102 @@ use { std::{fmt::Debug, sync::Arc}, }; +/// A trait for resources which may be borrowed from a `Graph`. +/// +/// See [`Graph::node`] for details. +pub trait GraphNode { + /// The Vulkan buffer, image, or acceleration struction type. + type Resource; + + #[doc(hidden)] + fn borrow(self, resources: &[Resource]) -> &Self::Resource; +} + +impl GraphNode for AnyAccelerationStructureNode { + type Resource = AccelerationStructure; + + fn borrow(self, resources: &[Resource]) -> &Self::Resource { + resources[self.index()].as_driver_accel_struct().unwrap() + } +} + +impl GraphNode for AnyBufferNode { + type Resource = Buffer; + + fn borrow(self, resources: &[Resource]) -> &Self::Resource { + resources[self.index()].as_driver_buffer().unwrap() + } +} + +impl GraphNode for AnyImageNode { + type Resource = Image; + + fn borrow(self, resources: &[Resource]) -> &Self::Resource { + resources[self.index()].as_driver_image().unwrap() + } +} + +impl GraphNode for SwapchainImageNode { + type Resource = SwapchainImage; + + fn borrow(self, resources: &[Resource]) -> &Self::Resource { + resources[self.idx].as_swapchain_image().unwrap() + } +} + +macro_rules! graph_node { + ($name:ident) => { + paste::paste! { + impl GraphNode for [<$name Node>] { + type Resource = Arc<$name>; + + fn borrow(self, resources: &[Resource]) -> &Self::Resource { + + resources[self.idx] + .[]() + .unwrap() + } + } + + impl GraphNode for [<$name LeaseNode>] { + type Resource = Arc>; + + fn borrow(self, resources: &[Resource]) -> &Self::Resource { + resources[self.idx] + .[]() + .unwrap() + } + } + } + }; +} + +graph_node!(AccelerationStructure); +graph_node!(Buffer); +graph_node!(Image); + /// A trait for resources which may be bound to a `Graph`. /// /// See [`Graph::bind_resource`] and /// [`CommandRef::bind_resource`](super::cmd::CommandRef::bind_resource) for details. -pub trait BindGraph { +pub trait GraphResource { /// The resource handle type. type Node; - /// Binds the resource to a graph. - /// - /// Returns a resource node handle. - fn bind_graph(self, graph: &mut Graph) -> Self::Node; + #[doc(hidden)] + fn bind_graph(self, _: &mut Graph) -> Self::Node; #[deprecated = "use bind_graph function"] #[doc(hidden)] - fn bind(self, graph: &mut Graph) -> Self::Node; + fn bind(self, graph: &mut Graph) -> Self::Node + where + Self: Sized, + { + self.bind_graph(graph) + } } -impl BindGraph for SwapchainImage { +impl GraphResource for SwapchainImage { type Node = SwapchainImageNode; fn bind_graph(self, graph: &mut Graph) -> Self::Node { @@ -48,16 +125,12 @@ impl BindGraph for SwapchainImage { res } - - fn bind(self, graph: &mut Graph) -> Self::Node { - self.bind_graph(graph) - } } -macro_rules! bind_graph_resource { +macro_rules! graph_resource { ($name:ident) => { paste::paste! { - impl BindGraph for $name { + impl GraphResource for $name { type Node = [<$name Node>]; #[profiling::function] @@ -73,13 +146,9 @@ macro_rules! bind_graph_resource { res } - - fn bind(self, graph: &mut Graph) -> Self::Node { - self.bind_graph(graph) - } } - impl BindGraph for Arc<$name> { + impl GraphResource for Arc<$name> { type Node = [<$name Node>]; #[profiling::function] @@ -106,13 +175,9 @@ macro_rules! bind_graph_resource { res } - - fn bind(self, graph: &mut Graph) -> Self::Node { - self.bind_graph(graph) - } } - impl<'a> BindGraph for &'a Arc<$name> { + impl<'a> GraphResource for &'a Arc<$name> { type Node = [<$name Node>]; fn bind_graph(self, graph: &mut Graph) -> Self::Node { @@ -121,13 +186,9 @@ macro_rules! bind_graph_resource { Arc::clone(self).bind_graph(graph) } - - fn bind(self, graph: &mut Graph) -> Self::Node { - self.bind_graph(graph) - } } - impl BindGraph for Lease<$name> { + impl GraphResource for Lease<$name> { type Node = [<$name LeaseNode>]; #[profiling::function] @@ -144,13 +205,9 @@ macro_rules! bind_graph_resource { res } - - fn bind(self, graph: &mut Graph) -> Self::Node { - self.bind_graph(graph) - } } - impl BindGraph for Arc> { + impl GraphResource for Arc> { type Node = [<$name LeaseNode>]; #[profiling::function] @@ -177,13 +234,9 @@ macro_rules! bind_graph_resource { res } - - fn bind(self, graph: &mut Graph) -> Self::Node { - self.bind_graph(graph) - } } - impl<'a> BindGraph for &'a Arc> { + impl<'a> GraphResource for &'a Arc> { type Node = [<$name LeaseNode>]; fn bind_graph(self, graph: &mut Graph) -> Self::Node { @@ -192,10 +245,6 @@ macro_rules! bind_graph_resource { Arc::clone(self).bind_graph(graph) } - - fn bind(self, graph: &mut Graph) -> Self::Node { - self.bind_graph(graph) - } } impl Resource { @@ -235,85 +284,11 @@ macro_rules! bind_graph_resource { }; } -bind_graph_resource!(AccelerationStructure); -bind_graph_resource!(Image); -bind_graph_resource!(Buffer); - -/// A trait for resources which may be borrowed from a `Graph`. -/// -/// See [`Graph::node`] for details. -pub trait Bound: Node { - /// The Vulkan buffer, image, or acceleration struction type. - type Resource; - - /// Borrows the resource from a graph. - fn borrow(self, resources: &[Resource]) -> &Self::Resource; -} - -impl Bound for AnyAccelerationStructureNode { - type Resource = AccelerationStructure; - - fn borrow(self, resources: &[Resource]) -> &Self::Resource { - resources[self.index()].as_driver_accel_struct().unwrap() - } -} - -impl Bound for AnyBufferNode { - type Resource = Buffer; - - fn borrow(self, resources: &[Resource]) -> &Self::Resource { - resources[self.index()].as_driver_buffer().unwrap() - } -} - -impl Bound for AnyImageNode { - type Resource = Image; - - fn borrow(self, resources: &[Resource]) -> &Self::Resource { - resources[self.index()].as_driver_image().unwrap() - } -} - -impl Bound for SwapchainImageNode { - type Resource = SwapchainImage; - - fn borrow(self, resources: &[Resource]) -> &Self::Resource { - resources[self.idx].as_swapchain_image().unwrap() - } -} - -macro_rules! bound { - ($name:ident) => { - paste::paste! { - impl Bound for [<$name Node>] { - type Resource = Arc<$name>; - - fn borrow(self, resources: &[Resource]) -> &Self::Resource { - - resources[self.idx] - .[]() - .unwrap() - } - } - - impl Bound for [<$name LeaseNode>] { - type Resource = Arc>; - - fn borrow(self, resources: &[Resource]) -> &Self::Resource { - resources[self.idx] - .[]() - .unwrap() - } - } - } - }; -} - -bound!(AccelerationStructure); -bound!(Buffer); -bound!(Image); +graph_resource!(AccelerationStructure); +graph_resource!(Image); +graph_resource!(Buffer); -/// TODO +/// Opaque wrapper for Vulkan resource types. #[derive(Debug)] pub struct Resource { pub(crate) inner: ResourceInner, From c920ec04fbe42817ce159a34ce8c3dceec036ac9 Mon Sep 17 00:00:00 2001 From: John Wells Date: Wed, 4 Mar 2026 06:04:13 -0500 Subject: [PATCH 33/86] Add book template --- .github/workflows/mdbook.yml | 35 +++++++++++++++++++++++++++ .github/workflows/rust-clippy.yml | 4 +-- .github/workflows/rust.yml | 4 +-- book/.gitignore | 1 + book/book.toml | 4 +++ book/src/README.md | 1 + book/src/SUMMARY.md | 24 ++++++++++++++++++ book/src/chapter_1.md | 1 + book/src/cmd.md | 1 + book/src/pipeline.md | 1 + book/src/pipeline_compute.md | 1 + book/src/pipeline_compute_dispatch.md | 1 + book/src/pipeline_graphic.md | 1 + book/src/pipeline_graphic_draw.md | 1 + book/src/pipeline_graphic_vertex.md | 1 + book/src/pipeline_push_const.md | 1 + book/src/pipeline_ray_trace.md | 1 + book/src/pipeline_spec.md | 1 + book/src/resource.md | 1 + book/src/resource_accel_struct.md | 1 + book/src/resource_buffer.md | 1 + book/src/resource_image.md | 1 + 22 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/mdbook.yml create mode 100644 book/.gitignore create mode 100644 book/book.toml create mode 100644 book/src/README.md create mode 100644 book/src/SUMMARY.md create mode 100644 book/src/chapter_1.md create mode 100644 book/src/cmd.md create mode 100644 book/src/pipeline.md create mode 100644 book/src/pipeline_compute.md create mode 100644 book/src/pipeline_compute_dispatch.md create mode 100644 book/src/pipeline_graphic.md create mode 100644 book/src/pipeline_graphic_draw.md create mode 100644 book/src/pipeline_graphic_vertex.md create mode 100644 book/src/pipeline_push_const.md create mode 100644 book/src/pipeline_ray_trace.md create mode 100644 book/src/pipeline_spec.md create mode 100644 book/src/resource.md create mode 100644 book/src/resource_accel_struct.md create mode 100644 book/src/resource_buffer.md create mode 100644 book/src/resource_image.md diff --git a/.github/workflows/mdbook.yml b/.github/workflows/mdbook.yml new file mode 100644 index 00000000..90890051 --- /dev/null +++ b/.github/workflows/mdbook.yml @@ -0,0 +1,35 @@ +name: GH Pages Deploy + +on: + push: + branches: [main, vk-graph] + paths-ignore: + - "contrib/**" + - "examples/**" + + pull_request: + branches: [main, vk-graph] + paths-ignore: + - "contrib/**" + - "examples/**" + +jobs: + build-deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout 🛎 + uses: actions/checkout@v2 + + - name: Install dependencies 🔧 + run: cargo install mdbook + + - name: Build 🏗️ + run: cd book && mdbook build + + - name: Deploy to GitHub Pages 🚀 + if: ${{ github.event_name != 'pull_request' }} + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: book/book diff --git a/.github/workflows/rust-clippy.yml b/.github/workflows/rust-clippy.yml index 92c1f751..bddc9c59 100644 --- a/.github/workflows/rust-clippy.yml +++ b/.github/workflows/rust-clippy.yml @@ -11,10 +11,10 @@ name: rust-clippy analyze on: push: - branches: [ master ] + branches: [ main ] pull_request: # The branches below must be a subset of the branches above - branches: [ master ] + branches: [ main ] schedule: - cron: '24 22 * * 3' diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 20cb8323..848e7967 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -2,9 +2,9 @@ name: Rust on: push: - branches: [ master ] + branches: [ main ] pull_request: - branches: [ master ] + branches: [ main ] env: CARGO_TERM_COLOR: always diff --git a/book/.gitignore b/book/.gitignore new file mode 100644 index 00000000..e9c07289 --- /dev/null +++ b/book/.gitignore @@ -0,0 +1 @@ +book \ No newline at end of file diff --git a/book/book.toml b/book/book.toml new file mode 100644 index 00000000..e3c5e311 --- /dev/null +++ b/book/book.toml @@ -0,0 +1,4 @@ +[book] +title = "vk-graph" +authors = ["John Wells"] +language = "en" diff --git a/book/src/README.md b/book/src/README.md new file mode 100644 index 00000000..e10b99d0 --- /dev/null +++ b/book/src/README.md @@ -0,0 +1 @@ +# Introduction diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md new file mode 100644 index 00000000..adf03791 --- /dev/null +++ b/book/src/SUMMARY.md @@ -0,0 +1,24 @@ +# Summary + +[Introduction](README.md) + +# User Guide + +- [Chapter 1](./chapter_1.md) + +# Reference Guide + +- [Resources](./resource.md) + - [Acceleration Structure](./resource_accel_struct.md) + - [Image](./resource_image.md) + - [Buffer](./resource_buffer.md) +- [Pipelines](./pipeline.md) + - [Push Constants](./pipeline_push_const.md) + - [Specialization](./pipeline_spec.md) + - [Compute](./pipeline_compute.md) + - [Dispatch Commands](./pipeline_compute_dispatch.md) + - [Graphic](./pipeline_graphic.md) + - [Draw Commands](./pipeline_graphic_draw.md) + - [Vertex Commands](./pipeline_graphic_vertex.md) + - [Ray Trace](./pipeline_ray_trace.md) +- [Commands](./cmd.md) diff --git a/book/src/chapter_1.md b/book/src/chapter_1.md new file mode 100644 index 00000000..b743fda3 --- /dev/null +++ b/book/src/chapter_1.md @@ -0,0 +1 @@ +# Chapter 1 diff --git a/book/src/cmd.md b/book/src/cmd.md new file mode 100644 index 00000000..61c515e7 --- /dev/null +++ b/book/src/cmd.md @@ -0,0 +1 @@ +# Commands diff --git a/book/src/pipeline.md b/book/src/pipeline.md new file mode 100644 index 00000000..920ca953 --- /dev/null +++ b/book/src/pipeline.md @@ -0,0 +1 @@ +# Pipelines diff --git a/book/src/pipeline_compute.md b/book/src/pipeline_compute.md new file mode 100644 index 00000000..3768cf01 --- /dev/null +++ b/book/src/pipeline_compute.md @@ -0,0 +1 @@ +# Compute diff --git a/book/src/pipeline_compute_dispatch.md b/book/src/pipeline_compute_dispatch.md new file mode 100644 index 00000000..5f10c322 --- /dev/null +++ b/book/src/pipeline_compute_dispatch.md @@ -0,0 +1 @@ +# Dispatch Commands diff --git a/book/src/pipeline_graphic.md b/book/src/pipeline_graphic.md new file mode 100644 index 00000000..93c96e0d --- /dev/null +++ b/book/src/pipeline_graphic.md @@ -0,0 +1 @@ +# Graphic diff --git a/book/src/pipeline_graphic_draw.md b/book/src/pipeline_graphic_draw.md new file mode 100644 index 00000000..047d7ae5 --- /dev/null +++ b/book/src/pipeline_graphic_draw.md @@ -0,0 +1 @@ +# Draw Commands diff --git a/book/src/pipeline_graphic_vertex.md b/book/src/pipeline_graphic_vertex.md new file mode 100644 index 00000000..a884507b --- /dev/null +++ b/book/src/pipeline_graphic_vertex.md @@ -0,0 +1 @@ +# Vertex Commands diff --git a/book/src/pipeline_push_const.md b/book/src/pipeline_push_const.md new file mode 100644 index 00000000..d4a7de2e --- /dev/null +++ b/book/src/pipeline_push_const.md @@ -0,0 +1 @@ +# Push Constants diff --git a/book/src/pipeline_ray_trace.md b/book/src/pipeline_ray_trace.md new file mode 100644 index 00000000..133af86e --- /dev/null +++ b/book/src/pipeline_ray_trace.md @@ -0,0 +1 @@ +# Ray Trace diff --git a/book/src/pipeline_spec.md b/book/src/pipeline_spec.md new file mode 100644 index 00000000..ae4b1832 --- /dev/null +++ b/book/src/pipeline_spec.md @@ -0,0 +1 @@ +# Specialization diff --git a/book/src/resource.md b/book/src/resource.md new file mode 100644 index 00000000..3c1229ee --- /dev/null +++ b/book/src/resource.md @@ -0,0 +1 @@ +# Resources diff --git a/book/src/resource_accel_struct.md b/book/src/resource_accel_struct.md new file mode 100644 index 00000000..40d98da6 --- /dev/null +++ b/book/src/resource_accel_struct.md @@ -0,0 +1 @@ +# Acceleration Structure diff --git a/book/src/resource_buffer.md b/book/src/resource_buffer.md new file mode 100644 index 00000000..80b58c75 --- /dev/null +++ b/book/src/resource_buffer.md @@ -0,0 +1 @@ +# Buffer diff --git a/book/src/resource_image.md b/book/src/resource_image.md new file mode 100644 index 00000000..77cfc546 --- /dev/null +++ b/book/src/resource_image.md @@ -0,0 +1 @@ +# Image From 1132e7d1604a64a515e67045945ca8cfdd8b219e Mon Sep 17 00:00:00 2001 From: John Wells Date: Wed, 4 Mar 2026 06:38:13 -0500 Subject: [PATCH 34/86] test book --- .github/workflows/mdbook.yml | 56 ++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/.github/workflows/mdbook.yml b/.github/workflows/mdbook.yml index 90890051..5aa0c9ff 100644 --- a/.github/workflows/mdbook.yml +++ b/.github/workflows/mdbook.yml @@ -1,35 +1,41 @@ -name: GH Pages Deploy - +name: Deploy Book on: push: - branches: [main, vk-graph] - paths-ignore: - - "contrib/**" - - "examples/**" - - pull_request: - branches: [main, vk-graph] + branches: + - main + - vk-graph paths-ignore: - "contrib/**" - "examples/**" jobs: - build-deploy: + deploy: runs-on: ubuntu-latest - + permissions: + contents: write # To push a branch + pages: write # To push to a GitHub Pages site + id-token: write # To update the deployment status steps: - - name: Checkout 🛎 - uses: actions/checkout@v2 - - - name: Install dependencies 🔧 - run: cargo install mdbook - - - name: Build 🏗️ - run: cd book && mdbook build - - - name: Deploy to GitHub Pages 🚀 - if: ${{ github.event_name != 'pull_request' }} - uses: peaceiris/actions-gh-pages@v3 + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Install latest mdbook + run: | + tag=$(curl 'https://api.github.com/repos/rust-lang/mdbook/releases/latest' | jq -r '.tag_name') + url="https://github.com/rust-lang/mdbook/releases/download/${tag}/mdbook-${tag}-x86_64-unknown-linux-gnu.tar.gz" + mkdir mdbook + curl -sSL $url | tar -xz --directory=./mdbook + echo `pwd`/mdbook >> $GITHUB_PATH + - name: Build Book + run: | + cd book + mdbook build + - name: Setup Pages + uses: actions/configure-pages@v4 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: book/book + path: 'book/book' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 From b525091c5aef3c5a53fba05286ba8b454057b2b1 Mon Sep 17 00:00:00 2001 From: John Wells Date: Wed, 4 Mar 2026 06:54:02 -0500 Subject: [PATCH 35/86] test book --- .github/workflows/mdbook.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/mdbook.yml b/.github/workflows/mdbook.yml index 5aa0c9ff..8baf68c0 100644 --- a/.github/workflows/mdbook.yml +++ b/.github/workflows/mdbook.yml @@ -8,6 +8,7 @@ on: - "contrib/**" - "examples/**" + jobs: deploy: runs-on: ubuntu-latest From c8f574c8d671303d8ee2efc8eeb0752f35b0e3a8 Mon Sep 17 00:00:00 2001 From: John Wells Date: Wed, 4 Mar 2026 08:32:20 -0500 Subject: [PATCH 36/86] contrib/ -> crates/ --- .github/workflows/mdbook.yml | 14 +++-- .gitignore | 3 +- CONTRIBUTING.md | 4 +- Cargo.toml | 10 ++-- README.md | 6 +-- {contrib/rel-mgmt => bin}/check | 48 +++++++++--------- {contrib/rel-mgmt => bin}/run-all-examples | 16 +++--- contrib/.vscode/launch.json | 45 ---------------- contrib/.vscode/tasks.json | 25 --------- {contrib => crates}/README.md | 0 {contrib => crates}/vk-graph-egui/Cargo.toml | 0 {contrib => crates}/vk-graph-egui/README.md | 0 .../vk-graph-egui/shaders/egui.frag | 0 .../vk-graph-egui/shaders/egui.vert | 0 {contrib => crates}/vk-graph-egui/src/lib.rs | 0 {contrib => crates}/vk-graph-fx/Cargo.toml | 2 +- {contrib => crates}/vk-graph-fx/README.md | 0 .../shader/compute/decode_bitmap_r_rg.comp | 0 .../compute/decode_bitmap_rgb_rgba.comp | 0 .../res/shader/compute/present1.comp | 0 .../res/shader/compute/present2.comp | 0 .../vk-graph-fx/res/shader/graphic/font.frag | 0 .../vk-graph-fx/res/shader/graphic/font.vert | 0 .../res/shader/graphic/present.frag | 0 .../res/shader/graphic/present.vert | 0 .../res/shader/inc/catmull_rom.glsl | 0 .../res/shader/inc/color_space.glsl | 0 .../vk-graph-fx/res/shader/inc/content.glsl | 0 .../vk-graph-fx/res/shader/inc/quad.glsl | 0 .../res/shader/transition/_defs.glsl | 0 .../res/shader/transition/_main.glsl | 0 .../res/shader/transition/angular.comp | 0 .../res/shader/transition/bounce.comp | 0 .../shader/transition/bow_tie_horizontal.comp | 0 .../shader/transition/bow_tie_vertical.comp | 0 .../transition/bow_tie_with_parameter.comp | 0 .../res/shader/transition/burn.comp | 0 .../transition/butterfly_wave_scrawler.comp | 0 .../res/shader/transition/cannabis_leaf.comp | 0 .../res/shader/transition/circle.comp | 0 .../res/shader/transition/circle_crop.comp | 0 .../res/shader/transition/circle_open.comp | 0 .../res/shader/transition/color_distance.comp | 0 .../res/shader/transition/color_phase.comp | 0 .../res/shader/transition/coord_from_in.comp | 0 .../transition/crazy_parametric_fun.comp | 0 .../res/shader/transition/cross_warp.comp | 0 .../res/shader/transition/cross_zoom.comp | 0 .../res/shader/transition/crosshatch.comp | 0 .../res/shader/transition/cube.comp | 0 .../res/shader/transition/directional.comp | 0 .../shader/transition/directional_easing.comp | 0 .../shader/transition/directional_warp.comp | 0 .../shader/transition/directional_wipe.comp | 0 .../res/shader/transition/displacement.comp | 0 .../res/shader/transition/doom_screen.comp | 0 .../res/shader/transition/doorway.comp | 0 .../res/shader/transition/dreamy.comp | 0 .../res/shader/transition/dreamy_zoom.comp | 0 .../res/shader/transition/fade.comp | 0 .../res/shader/transition/fade_color.comp | 0 .../res/shader/transition/fade_grayscale.comp | 0 .../res/shader/transition/film_burn.comp | 0 .../res/shader/transition/flyeye.comp | 0 .../shader/transition/glitch_displace.comp | 0 .../shader/transition/glitch_memories.comp | 0 .../res/shader/transition/grid_flip.comp | 0 .../res/shader/transition/heart.comp | 0 .../res/shader/transition/hexagonalize.comp | 0 .../shader/transition/inverted_page_curl.comp | 0 .../res/shader/transition/kaleidoscope.comp | 0 .../res/shader/transition/left_right.comp | 0 .../res/shader/transition/linear_blur.comp | 0 .../res/shader/transition/luma.comp | 0 .../res/shader/transition/luminance_melt.comp | 0 .../res/shader/transition/morph.comp | 0 .../res/shader/transition/mosaic.comp | 0 .../res/shader/transition/multiply.comp | 0 .../res/shader/transition/overexposure.comp | 0 .../res/shader/transition/perlin.comp | 0 .../res/shader/transition/pinwheel.comp | 0 .../res/shader/transition/pixelize.comp | 0 .../res/shader/transition/polar_function.comp | 0 .../shader/transition/polka_dots_curtain.comp | 0 .../res/shader/transition/power_kaleido.comp | 0 .../res/shader/transition/radial.comp | 0 .../res/shader/transition/random_noisex.comp | 0 .../res/shader/transition/random_squares.comp | 0 .../res/shader/transition/ripple.comp | 0 .../res/shader/transition/rotate.comp | 0 .../res/shader/transition/rotate_scale.comp | 0 .../res/shader/transition/scale_in.comp | 0 .../res/shader/transition/simple_zoom.comp | 0 .../res/shader/transition/squares_wire.comp | 0 .../res/shader/transition/squeeze.comp | 0 .../res/shader/transition/stereo_viewer.comp | 0 .../res/shader/transition/swap.comp | 0 .../res/shader/transition/swirl.comp | 0 .../transition/tangent_motion_blur.comp | 0 .../res/shader/transition/top_bottom.comp | 0 .../res/shader/transition/tv_static.comp | 0 .../transition/undulating_burn_out.comp | 0 .../res/shader/transition/water_drop.comp | 0 .../res/shader/transition/wind.comp | 0 .../res/shader/transition/window_blinds.comp | 0 .../res/shader/transition/window_slice.comp | 0 .../res/shader/transition/wipe_down.comp | 0 .../res/shader/transition/wipe_left.comp | 0 .../res/shader/transition/wipe_right.comp | 0 .../res/shader/transition/wipe_up.comp | 0 .../shader/transition/zoom_in_circles.comp | 0 .../res/shader/transition/zoom_left_wipe.comp | 0 .../shader/transition/zoom_right_wipe.comp | 0 .../vk-graph-fx/src/bitmap_font.rs | 0 .../vk-graph-fx/src/image_loader.rs | 0 {contrib => crates}/vk-graph-fx/src/lib.rs | 0 .../vk-graph-fx/src/presenter.rs | 0 .../vk-graph-fx/src/transition.rs | 0 .../vk-graph-hot/.github/img/noise.png | Bin {contrib => crates}/vk-graph-hot/Cargo.toml | 2 +- {contrib => crates}/vk-graph-hot/README.md | 0 .../vk-graph-hot/examples/README.md | 0 .../vk-graph-hot/examples/glsl.rs | 0 .../vk-graph-hot/examples/hlsl.rs | 0 .../vk-graph-hot/examples/res/fill_image.comp | 0 .../vk-graph-hot/examples/res/fill_image.hlsl | 0 .../vk-graph-hot/examples/res/noise.glsl | 0 .../vk-graph-hot/examples/res/noise.hlsl | 0 .../vk-graph-hot/src/compute.rs | 0 .../vk-graph-hot/src/graphic.rs | 0 {contrib => crates}/vk-graph-hot/src/lib.rs | 0 .../vk-graph-hot/src/ray_trace.rs | 0 .../vk-graph-hot/src/shader.rs | 0 {contrib => crates}/vk-graph-imgui/Cargo.toml | 0 {contrib => crates}/vk-graph-imgui/README.md | 0 .../vk-graph-imgui/res/font/mplus-1p/LICENSE | 0 .../res/font/mplus-1p/mplus-1p-regular.ttf | Bin .../vk-graph-imgui/res/font/roboto/LICENSE | 0 .../res/font/roboto/roboto-regular.ttf | Bin .../vk-graph-imgui/res/shader/imgui.frag | 0 .../vk-graph-imgui/res/shader/imgui.vert | 0 {contrib => crates}/vk-graph-imgui/src/lib.rs | 0 .../vk-graph-prelude/Cargo.toml | 0 .../vk-graph-prelude/README.md | 0 .../vk-graph-prelude/src/lib.rs | 0 .../vk-graph-window/Cargo.toml | 0 {contrib => crates}/vk-graph-window/README.md | 0 .../vk-graph-window/examples/hello_world.rs | 0 .../vk-graph-window/src/frame.rs | 0 .../vk-graph-window/src/lib.rs | 0 .../vk-graph-window/src/swapchain.rs | 0 examples/README.md | 4 +- examples/shader-toy/Cargo.toml | 4 +- examples/skeletal-anim/Cargo.toml | 2 +- examples/vr/Cargo.toml | 2 +- {book => guide}/.gitignore | 0 {book => guide}/book.toml | 0 {book => guide}/src/README.md | 0 {book => guide}/src/SUMMARY.md | 0 {book => guide}/src/chapter_1.md | 0 {book => guide}/src/cmd.md | 0 {book => guide}/src/pipeline.md | 0 {book => guide}/src/pipeline_compute.md | 0 .../src/pipeline_compute_dispatch.md | 0 {book => guide}/src/pipeline_graphic.md | 0 {book => guide}/src/pipeline_graphic_draw.md | 0 .../src/pipeline_graphic_vertex.md | 0 {book => guide}/src/pipeline_push_const.md | 0 {book => guide}/src/pipeline_ray_trace.md | 0 {book => guide}/src/pipeline_spec.md | 0 {book => guide}/src/resource.md | 0 {book => guide}/src/resource_accel_struct.md | 0 {book => guide}/src/resource_buffer.md | 0 {book => guide}/src/resource_image.md | 0 src/cmd/compute.rs | 6 ++- 175 files changed, 65 insertions(+), 128 deletions(-) rename {contrib/rel-mgmt => bin}/check (50%) rename {contrib/rel-mgmt => bin}/run-all-examples (74%) delete mode 100644 contrib/.vscode/launch.json delete mode 100644 contrib/.vscode/tasks.json rename {contrib => crates}/README.md (100%) rename {contrib => crates}/vk-graph-egui/Cargo.toml (100%) rename {contrib => crates}/vk-graph-egui/README.md (100%) rename {contrib => crates}/vk-graph-egui/shaders/egui.frag (100%) rename {contrib => crates}/vk-graph-egui/shaders/egui.vert (100%) rename {contrib => crates}/vk-graph-egui/src/lib.rs (100%) rename {contrib => crates}/vk-graph-fx/Cargo.toml (91%) rename {contrib => crates}/vk-graph-fx/README.md (100%) rename {contrib => crates}/vk-graph-fx/res/shader/compute/decode_bitmap_r_rg.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/compute/decode_bitmap_rgb_rgba.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/compute/present1.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/compute/present2.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/graphic/font.frag (100%) rename {contrib => crates}/vk-graph-fx/res/shader/graphic/font.vert (100%) rename {contrib => crates}/vk-graph-fx/res/shader/graphic/present.frag (100%) rename {contrib => crates}/vk-graph-fx/res/shader/graphic/present.vert (100%) rename {contrib => crates}/vk-graph-fx/res/shader/inc/catmull_rom.glsl (100%) rename {contrib => crates}/vk-graph-fx/res/shader/inc/color_space.glsl (100%) rename {contrib => crates}/vk-graph-fx/res/shader/inc/content.glsl (100%) rename {contrib => crates}/vk-graph-fx/res/shader/inc/quad.glsl (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/_defs.glsl (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/_main.glsl (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/angular.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/bounce.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/bow_tie_horizontal.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/bow_tie_vertical.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/bow_tie_with_parameter.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/burn.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/butterfly_wave_scrawler.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/cannabis_leaf.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/circle.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/circle_crop.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/circle_open.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/color_distance.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/color_phase.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/coord_from_in.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/crazy_parametric_fun.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/cross_warp.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/cross_zoom.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/crosshatch.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/cube.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/directional.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/directional_easing.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/directional_warp.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/directional_wipe.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/displacement.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/doom_screen.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/doorway.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/dreamy.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/dreamy_zoom.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/fade.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/fade_color.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/fade_grayscale.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/film_burn.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/flyeye.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/glitch_displace.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/glitch_memories.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/grid_flip.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/heart.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/hexagonalize.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/inverted_page_curl.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/kaleidoscope.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/left_right.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/linear_blur.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/luma.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/luminance_melt.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/morph.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/mosaic.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/multiply.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/overexposure.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/perlin.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/pinwheel.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/pixelize.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/polar_function.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/polka_dots_curtain.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/power_kaleido.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/radial.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/random_noisex.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/random_squares.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/ripple.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/rotate.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/rotate_scale.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/scale_in.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/simple_zoom.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/squares_wire.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/squeeze.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/stereo_viewer.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/swap.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/swirl.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/tangent_motion_blur.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/top_bottom.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/tv_static.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/undulating_burn_out.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/water_drop.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/wind.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/window_blinds.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/window_slice.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/wipe_down.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/wipe_left.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/wipe_right.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/wipe_up.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/zoom_in_circles.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/zoom_left_wipe.comp (100%) rename {contrib => crates}/vk-graph-fx/res/shader/transition/zoom_right_wipe.comp (100%) rename {contrib => crates}/vk-graph-fx/src/bitmap_font.rs (100%) rename {contrib => crates}/vk-graph-fx/src/image_loader.rs (100%) rename {contrib => crates}/vk-graph-fx/src/lib.rs (100%) rename {contrib => crates}/vk-graph-fx/src/presenter.rs (100%) rename {contrib => crates}/vk-graph-fx/src/transition.rs (100%) rename {contrib => crates}/vk-graph-hot/.github/img/noise.png (100%) rename {contrib => crates}/vk-graph-hot/Cargo.toml (91%) rename {contrib => crates}/vk-graph-hot/README.md (100%) rename {contrib => crates}/vk-graph-hot/examples/README.md (100%) rename {contrib => crates}/vk-graph-hot/examples/glsl.rs (100%) rename {contrib => crates}/vk-graph-hot/examples/hlsl.rs (100%) rename {contrib => crates}/vk-graph-hot/examples/res/fill_image.comp (100%) rename {contrib => crates}/vk-graph-hot/examples/res/fill_image.hlsl (100%) rename {contrib => crates}/vk-graph-hot/examples/res/noise.glsl (100%) rename {contrib => crates}/vk-graph-hot/examples/res/noise.hlsl (100%) rename {contrib => crates}/vk-graph-hot/src/compute.rs (100%) rename {contrib => crates}/vk-graph-hot/src/graphic.rs (100%) rename {contrib => crates}/vk-graph-hot/src/lib.rs (100%) rename {contrib => crates}/vk-graph-hot/src/ray_trace.rs (100%) rename {contrib => crates}/vk-graph-hot/src/shader.rs (100%) rename {contrib => crates}/vk-graph-imgui/Cargo.toml (100%) rename {contrib => crates}/vk-graph-imgui/README.md (100%) rename {contrib => crates}/vk-graph-imgui/res/font/mplus-1p/LICENSE (100%) rename {contrib => crates}/vk-graph-imgui/res/font/mplus-1p/mplus-1p-regular.ttf (100%) rename {contrib => crates}/vk-graph-imgui/res/font/roboto/LICENSE (100%) rename {contrib => crates}/vk-graph-imgui/res/font/roboto/roboto-regular.ttf (100%) rename {contrib => crates}/vk-graph-imgui/res/shader/imgui.frag (100%) rename {contrib => crates}/vk-graph-imgui/res/shader/imgui.vert (100%) rename {contrib => crates}/vk-graph-imgui/src/lib.rs (100%) rename {contrib => crates}/vk-graph-prelude/Cargo.toml (100%) rename {contrib => crates}/vk-graph-prelude/README.md (100%) rename {contrib => crates}/vk-graph-prelude/src/lib.rs (100%) rename {contrib => crates}/vk-graph-window/Cargo.toml (100%) rename {contrib => crates}/vk-graph-window/README.md (100%) rename {contrib => crates}/vk-graph-window/examples/hello_world.rs (100%) rename {contrib => crates}/vk-graph-window/src/frame.rs (100%) rename {contrib => crates}/vk-graph-window/src/lib.rs (100%) rename {contrib => crates}/vk-graph-window/src/swapchain.rs (100%) rename {book => guide}/.gitignore (100%) rename {book => guide}/book.toml (100%) rename {book => guide}/src/README.md (100%) rename {book => guide}/src/SUMMARY.md (100%) rename {book => guide}/src/chapter_1.md (100%) rename {book => guide}/src/cmd.md (100%) rename {book => guide}/src/pipeline.md (100%) rename {book => guide}/src/pipeline_compute.md (100%) rename {book => guide}/src/pipeline_compute_dispatch.md (100%) rename {book => guide}/src/pipeline_graphic.md (100%) rename {book => guide}/src/pipeline_graphic_draw.md (100%) rename {book => guide}/src/pipeline_graphic_vertex.md (100%) rename {book => guide}/src/pipeline_push_const.md (100%) rename {book => guide}/src/pipeline_ray_trace.md (100%) rename {book => guide}/src/pipeline_spec.md (100%) rename {book => guide}/src/resource.md (100%) rename {book => guide}/src/resource_accel_struct.md (100%) rename {book => guide}/src/resource_buffer.md (100%) rename {book => guide}/src/resource_image.md (100%) diff --git a/.github/workflows/mdbook.yml b/.github/workflows/mdbook.yml index 8baf68c0..75590c33 100644 --- a/.github/workflows/mdbook.yml +++ b/.github/workflows/mdbook.yml @@ -3,12 +3,11 @@ on: push: branches: - main - - vk-graph + - vk-graph # TEMP paths-ignore: - - "contrib/**" + - "bin/**" - "examples/**" - jobs: deploy: runs-on: ubuntu-latest @@ -20,6 +19,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Install latest mdbook run: | tag=$(curl 'https://api.github.com/repos/rust-lang/mdbook/releases/latest' | jq -r '.tag_name') @@ -27,16 +27,20 @@ jobs: mkdir mdbook curl -sSL $url | tar -xz --directory=./mdbook echo `pwd`/mdbook >> $GITHUB_PATH + - name: Build Book run: | - cd book + cd guide mdbook build + - name: Setup Pages uses: actions/configure-pages@v4 + - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: - path: 'book/book' + path: 'guide/book' + - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index ec03cfa5..26303c39 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ Cargo.lock **/target/ **/.vscode/ -!contrib/.vscode/ imgui.ini **/*.pak -**/*.profraw \ No newline at end of file +**/*.profraw diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e6fe4cc5..7179f7c4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -54,8 +54,8 @@ needed. ## Release checklist - Run `cargo update` to ensure you have the latest dependencies -- Double-check `cargo clippy --release` and `cargo fmt` _(use `contrib/rel-mgmt/check`)_ -- Double-check all examples compile and run as intended _(use `contrib/rel-mgmt/run-all-examples`)_ +- Double-check `cargo clippy --release` and `cargo fmt` _(use `bin/check`)_ +- Double-check all examples compile and run as intended _(use `bin/run-all-examples`)_ - Double-check the above on all supported platforms - Change log: Add a section for the new version - Change log: Transfer unreleased details to the new version diff --git a/Cargo.toml b/Cargo.toml index 3fdc6a69..d0a225f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,11 +54,11 @@ puffin = "0.19" puffin_http = "0.16" rand = "0.9" reqwest = { version = "0.12", features = ["blocking"] } -vk-graph-fx = { path = "contrib/vk-graph-fx" } -vk-graph-imgui = { path = "contrib/vk-graph-imgui" } -vk-graph-egui = { path = "contrib/vk-graph-egui" } -vk-graph-prelude = { path = "contrib/vk-graph-prelude" } -vk-graph-window = { path = "contrib/vk-graph-window" } +vk-graph-fx = { path = "crates/vk-graph-fx" } +vk-graph-imgui = { path = "crates/vk-graph-imgui" } +vk-graph-egui = { path = "crates/vk-graph-egui" } +vk-graph-prelude = { path = "crates/vk-graph-prelude" } +vk-graph-window = { path = "crates/vk-graph-window" } vk-shader-macros = "0.2" tobj = "4.0" winit = "0.30" diff --git a/README.md b/README.md index 7a086771..a6347220 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Crates.io](https://img.shields.io/crates/v/vk-graph.svg)](https://crates.io/crates/vk-graph) [![Docs.rs](https://docs.rs/vk-graph/badge.svg)](https://docs.rs/vk-graph) -[![LoC](https://tokei.rs/b1/github/attackgoat/vk-graph?category=code)](https://github.com/attackgoat/vk-graph) +[![Guide Book](https://tokei.rs/b1/github/attackgoat/vk-graph?category=code)](http://attackgoat.github.io/vk-graph) _vk-graph_ is a high-performance Vulkan graphics driver with automatic resource management and execution. @@ -41,7 +41,7 @@ Features of the render graph: - Automatic Vulkan management (render passes, subpasses, descriptors, pools, _etc._) - Automatic render pass scheduling, re-ordering, merging, with resource aliasing - Interoperable with existing Vulkan code - - Optional [shader hot-reload](contrib/vk-graph-hot/README.md) from disk + - Optional [shader hot-reload](crates/vk-graph-hot/README.md) from disk ```rust graph @@ -102,7 +102,7 @@ cargo run --features profile-with-puffin --release --example vsm_omni Included are some examples you might find helpful: -- [`hello_world.rs`](contrib/vk-graph-window/examples/hello_world.rs) — Displays a window on the screen. Please start here. +- [`hello_world.rs`](crates/vk-graph-window/examples/hello_world.rs) — Displays a window on the screen. Please start here. - [`triangle.rs`](examples/triangle.rs) — Shaders and full setup of index/vertex buffers; < 100 LOC. - [`shader-toy/`](examples/shader-toy) — Recreation of a two-pass shader toy using the original shader code. diff --git a/contrib/rel-mgmt/check b/bin/check similarity index 50% rename from contrib/rel-mgmt/check rename to bin/check index 5b57cc41..7132c35c 100755 --- a/contrib/rel-mgmt/check +++ b/bin/check @@ -16,12 +16,12 @@ diff || fail "Uncommitted changes" # Unformatted rust code cargo fmt && diff || fail "Unformatted rust code" -cargo fmt --manifest-path contrib/vk-graph-egui/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-egui)" -cargo fmt --manifest-path contrib/vk-graph-fx/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-fx)" -cargo fmt --manifest-path contrib/vk-graph-hot/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-hot)" -cargo fmt --manifest-path contrib/vk-graph-imgui/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-imgui)" -cargo fmt --manifest-path contrib/vk-graph-prelude/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-prelude)" -cargo fmt --manifest-path contrib/vk-graph-window/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-window)" +cargo fmt --manifest-path crates/vk-graph-egui/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-egui)" +cargo fmt --manifest-path crates/vk-graph-fx/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-fx)" +cargo fmt --manifest-path crates/vk-graph-hot/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-hot)" +cargo fmt --manifest-path crates/vk-graph-imgui/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-imgui)" +cargo fmt --manifest-path crates/vk-graph-prelude/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-prelude)" +cargo fmt --manifest-path crates/vk-graph-window/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-window)" cargo fmt --manifest-path examples/shader-toy/Cargo.toml && diff || fail "Unformatted rust code (shader-toy)" cargo fmt --manifest-path examples/skeletal-anim/Cargo.toml && diff || fail "Unformatted rust code (skeletal-anim)" cargo fmt --manifest-path examples/vr/Cargo.toml && diff || fail "Unformatted rust code (vr)" @@ -33,29 +33,29 @@ echo "Checking vk-graph" \ && cargo check --all-targets --features parking_lot || fail \ && cargo clippy --all-targets --features parking_lot || fail -echo "Checking contrib/vk-graph-egui" \ - && cargo check --manifest-path contrib/vk-graph-egui/Cargo.toml || fail \ - && cargo clippy --manifest-path contrib/vk-graph-egui/Cargo.toml || fail +echo "Checking crates/vk-graph-egui" \ + && cargo check --manifest-path crates/vk-graph-egui/Cargo.toml || fail \ + && cargo clippy --manifest-path crates/vk-graph-egui/Cargo.toml || fail -echo "Checking contrib/vk-graph-fx" \ - && cargo check --manifest-path contrib/vk-graph-fx/Cargo.toml || fail \ - && cargo clippy --manifest-path contrib/vk-graph-fx/Cargo.toml || fail +echo "Checking crates/vk-graph-fx" \ + && cargo check --manifest-path crates/vk-graph-fx/Cargo.toml || fail \ + && cargo clippy --manifest-path crates/vk-graph-fx/Cargo.toml || fail -echo "Checking contrib/vk-graph-hot" \ - && cargo check --manifest-path contrib/vk-graph-hot/Cargo.toml || fail \ - && cargo clippy --manifest-path contrib/vk-graph-hot/Cargo.toml || fail +echo "Checking crates/vk-graph-hot" \ + && cargo check --manifest-path crates/vk-graph-hot/Cargo.toml || fail \ + && cargo clippy --manifest-path crates/vk-graph-hot/Cargo.toml || fail -# echo "Checking contrib/vk-graph-imgui" \ -# && cargo check --manifest-path contrib/vk-graph-imgui/Cargo.toml || fail \ -# && cargo clippy --manifest-path contrib/vk-graph-imgui/Cargo.toml || fail +# echo "Checking crates/vk-graph-imgui" \ +# && cargo check --manifest-path crates/vk-graph-imgui/Cargo.toml || fail \ +# && cargo clippy --manifest-path crates/vk-graph-imgui/Cargo.toml || fail -echo "Checking contrib/vk-graph-prelude" \ - && cargo check --manifest-path contrib/vk-graph-prelude/Cargo.toml || fail \ - && cargo clippy --manifest-path contrib/vk-graph-prelude/Cargo.toml || fail +echo "Checking crates/vk-graph-prelude" \ + && cargo check --manifest-path crates/vk-graph-prelude/Cargo.toml || fail \ + && cargo clippy --manifest-path crates/vk-graph-prelude/Cargo.toml || fail -echo "Checking contrib/vk-graph-window" \ - && cargo check --manifest-path contrib/vk-graph-window/Cargo.toml || fail \ - && cargo clippy --manifest-path contrib/vk-graph-window/Cargo.toml || fail +echo "Checking crates/vk-graph-window" \ + && cargo check --manifest-path crates/vk-graph-window/Cargo.toml || fail \ + && cargo clippy --manifest-path crates/vk-graph-window/Cargo.toml || fail echo "Checking examples/shader-toy" \ && cargo check --manifest-path examples/shader-toy/Cargo.toml || fail \ diff --git a/contrib/rel-mgmt/run-all-examples b/bin/run-all-examples similarity index 74% rename from contrib/rel-mgmt/run-all-examples rename to bin/run-all-examples index 4fa2d722..91bbf2cd 100755 --- a/contrib/rel-mgmt/run-all-examples +++ b/bin/run-all-examples @@ -4,11 +4,11 @@ set -e # Update everything cargo update -cargo update --manifest-path contrib/vk-graph-egui/Cargo.toml -cargo update --manifest-path contrib/vk-graph-fx/Cargo.toml -cargo update --manifest-path contrib/vk-graph-hot/Cargo.toml -cargo update --manifest-path contrib/vk-graph-imgui/Cargo.toml -cargo update --manifest-path contrib/vk-graph-window/Cargo.toml +cargo update --manifest-path crates/vk-graph-egui/Cargo.toml +cargo update --manifest-path crates/vk-graph-fx/Cargo.toml +cargo update --manifest-path crates/vk-graph-hot/Cargo.toml +cargo update --manifest-path crates/vk-graph-imgui/Cargo.toml +cargo update --manifest-path crates/vk-graph-window/Cargo.toml cargo update --manifest-path examples/skeletal-anim/Cargo.toml cargo update --manifest-path examples/shader-toy/Cargo.toml cargo update --manifest-path examples/vr/Cargo.toml @@ -20,7 +20,7 @@ cargo build --examples cargo run --example fuzzer # Run all regular examples, in debug mode, next -cargo run --manifest-path contrib/vk-graph-window/Cargo.toml --example hello_world -- --debug +cargo run --manifest-path crates/vk-graph-window/Cargo.toml --example hello_world -- --debug cargo run --example app -- --debug cargo run --example aliasing -- --debug cargo run --example cpu_readback -- --debug @@ -50,8 +50,8 @@ cargo run --example ray_omni -- --debug cargo run --manifest-path examples/skeletal-anim/Cargo.toml -- --debug # Hot-reload examples -cargo run --manifest-path contrib/vk-graph-hot/Cargo.toml --example glsl -- --debug -cargo run --manifest-path contrib/vk-graph-hot/Cargo.toml --example hlsl -- --debug +cargo run --manifest-path crates/vk-graph-hot/Cargo.toml --example glsl -- --debug +cargo run --manifest-path crates/vk-graph-hot/Cargo.toml --example hlsl -- --debug # Run this one in release mode cargo run --manifest-path examples/shader-toy/Cargo.toml --release -- --debug diff --git a/contrib/.vscode/launch.json b/contrib/.vscode/launch.json deleted file mode 100644 index d94b9e66..00000000 --- a/contrib/.vscode/launch.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in library 'vk-graph'", - "cargo": { - "args": [ - "test", - "--no-run", - "--lib", - "--package=vk-graph" - ], - "filter": { - "name": "vk-graph", - "kind": "lib" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug example 'hello_world'", - "cargo": { - "args": [ - "build", - "--example=hello_world", - "--package=vk-graph" - ], - "filter": { - "name": "hello_world", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - } - ] -} diff --git a/contrib/.vscode/tasks.json b/contrib/.vscode/tasks.json deleted file mode 100644 index ddea6c4c..00000000 --- a/contrib/.vscode/tasks.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "type": "cargo", - "command": "build", - "problemMatcher": [ - "$rustc" - ], - "group": { - "kind": "build", - "isDefault": true - }, - "presentation": { - "echo": false, - "revealProblems": "onProblem", - "focus": false, - "panel": "shared", - "showReuseMessage": false, - "clear": true - }, - "label": "Build vk-graph" - } - ] -} diff --git a/contrib/README.md b/crates/README.md similarity index 100% rename from contrib/README.md rename to crates/README.md diff --git a/contrib/vk-graph-egui/Cargo.toml b/crates/vk-graph-egui/Cargo.toml similarity index 100% rename from contrib/vk-graph-egui/Cargo.toml rename to crates/vk-graph-egui/Cargo.toml diff --git a/contrib/vk-graph-egui/README.md b/crates/vk-graph-egui/README.md similarity index 100% rename from contrib/vk-graph-egui/README.md rename to crates/vk-graph-egui/README.md diff --git a/contrib/vk-graph-egui/shaders/egui.frag b/crates/vk-graph-egui/shaders/egui.frag similarity index 100% rename from contrib/vk-graph-egui/shaders/egui.frag rename to crates/vk-graph-egui/shaders/egui.frag diff --git a/contrib/vk-graph-egui/shaders/egui.vert b/crates/vk-graph-egui/shaders/egui.vert similarity index 100% rename from contrib/vk-graph-egui/shaders/egui.vert rename to crates/vk-graph-egui/shaders/egui.vert diff --git a/contrib/vk-graph-egui/src/lib.rs b/crates/vk-graph-egui/src/lib.rs similarity index 100% rename from contrib/vk-graph-egui/src/lib.rs rename to crates/vk-graph-egui/src/lib.rs diff --git a/contrib/vk-graph-fx/Cargo.toml b/crates/vk-graph-fx/Cargo.toml similarity index 91% rename from contrib/vk-graph-fx/Cargo.toml rename to crates/vk-graph-fx/Cargo.toml index 41bb9402..060c1868 100644 --- a/contrib/vk-graph-fx/Cargo.toml +++ b/crates/vk-graph-fx/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" license = "MIT OR Apache-2.0" readme = "README.md" repository = "https://github.com/attackgoat/vk-graph" -homepage = "https://github.com/attackgoat/vk-graph/contrib/vk-graph-fx" +homepage = "https://github.com/attackgoat/vk-graph/crates/vk-graph-fx" documentation = "https://docs.rs/vk-graph" keywords = ["gamedev", "vulkan"] categories = ["game-development", "multimedia::images", "rendering::engine"] diff --git a/contrib/vk-graph-fx/README.md b/crates/vk-graph-fx/README.md similarity index 100% rename from contrib/vk-graph-fx/README.md rename to crates/vk-graph-fx/README.md diff --git a/contrib/vk-graph-fx/res/shader/compute/decode_bitmap_r_rg.comp b/crates/vk-graph-fx/res/shader/compute/decode_bitmap_r_rg.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/compute/decode_bitmap_r_rg.comp rename to crates/vk-graph-fx/res/shader/compute/decode_bitmap_r_rg.comp diff --git a/contrib/vk-graph-fx/res/shader/compute/decode_bitmap_rgb_rgba.comp b/crates/vk-graph-fx/res/shader/compute/decode_bitmap_rgb_rgba.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/compute/decode_bitmap_rgb_rgba.comp rename to crates/vk-graph-fx/res/shader/compute/decode_bitmap_rgb_rgba.comp diff --git a/contrib/vk-graph-fx/res/shader/compute/present1.comp b/crates/vk-graph-fx/res/shader/compute/present1.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/compute/present1.comp rename to crates/vk-graph-fx/res/shader/compute/present1.comp diff --git a/contrib/vk-graph-fx/res/shader/compute/present2.comp b/crates/vk-graph-fx/res/shader/compute/present2.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/compute/present2.comp rename to crates/vk-graph-fx/res/shader/compute/present2.comp diff --git a/contrib/vk-graph-fx/res/shader/graphic/font.frag b/crates/vk-graph-fx/res/shader/graphic/font.frag similarity index 100% rename from contrib/vk-graph-fx/res/shader/graphic/font.frag rename to crates/vk-graph-fx/res/shader/graphic/font.frag diff --git a/contrib/vk-graph-fx/res/shader/graphic/font.vert b/crates/vk-graph-fx/res/shader/graphic/font.vert similarity index 100% rename from contrib/vk-graph-fx/res/shader/graphic/font.vert rename to crates/vk-graph-fx/res/shader/graphic/font.vert diff --git a/contrib/vk-graph-fx/res/shader/graphic/present.frag b/crates/vk-graph-fx/res/shader/graphic/present.frag similarity index 100% rename from contrib/vk-graph-fx/res/shader/graphic/present.frag rename to crates/vk-graph-fx/res/shader/graphic/present.frag diff --git a/contrib/vk-graph-fx/res/shader/graphic/present.vert b/crates/vk-graph-fx/res/shader/graphic/present.vert similarity index 100% rename from contrib/vk-graph-fx/res/shader/graphic/present.vert rename to crates/vk-graph-fx/res/shader/graphic/present.vert diff --git a/contrib/vk-graph-fx/res/shader/inc/catmull_rom.glsl b/crates/vk-graph-fx/res/shader/inc/catmull_rom.glsl similarity index 100% rename from contrib/vk-graph-fx/res/shader/inc/catmull_rom.glsl rename to crates/vk-graph-fx/res/shader/inc/catmull_rom.glsl diff --git a/contrib/vk-graph-fx/res/shader/inc/color_space.glsl b/crates/vk-graph-fx/res/shader/inc/color_space.glsl similarity index 100% rename from contrib/vk-graph-fx/res/shader/inc/color_space.glsl rename to crates/vk-graph-fx/res/shader/inc/color_space.glsl diff --git a/contrib/vk-graph-fx/res/shader/inc/content.glsl b/crates/vk-graph-fx/res/shader/inc/content.glsl similarity index 100% rename from contrib/vk-graph-fx/res/shader/inc/content.glsl rename to crates/vk-graph-fx/res/shader/inc/content.glsl diff --git a/contrib/vk-graph-fx/res/shader/inc/quad.glsl b/crates/vk-graph-fx/res/shader/inc/quad.glsl similarity index 100% rename from contrib/vk-graph-fx/res/shader/inc/quad.glsl rename to crates/vk-graph-fx/res/shader/inc/quad.glsl diff --git a/contrib/vk-graph-fx/res/shader/transition/_defs.glsl b/crates/vk-graph-fx/res/shader/transition/_defs.glsl similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/_defs.glsl rename to crates/vk-graph-fx/res/shader/transition/_defs.glsl diff --git a/contrib/vk-graph-fx/res/shader/transition/_main.glsl b/crates/vk-graph-fx/res/shader/transition/_main.glsl similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/_main.glsl rename to crates/vk-graph-fx/res/shader/transition/_main.glsl diff --git a/contrib/vk-graph-fx/res/shader/transition/angular.comp b/crates/vk-graph-fx/res/shader/transition/angular.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/angular.comp rename to crates/vk-graph-fx/res/shader/transition/angular.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/bounce.comp b/crates/vk-graph-fx/res/shader/transition/bounce.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/bounce.comp rename to crates/vk-graph-fx/res/shader/transition/bounce.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/bow_tie_horizontal.comp b/crates/vk-graph-fx/res/shader/transition/bow_tie_horizontal.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/bow_tie_horizontal.comp rename to crates/vk-graph-fx/res/shader/transition/bow_tie_horizontal.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/bow_tie_vertical.comp b/crates/vk-graph-fx/res/shader/transition/bow_tie_vertical.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/bow_tie_vertical.comp rename to crates/vk-graph-fx/res/shader/transition/bow_tie_vertical.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/bow_tie_with_parameter.comp b/crates/vk-graph-fx/res/shader/transition/bow_tie_with_parameter.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/bow_tie_with_parameter.comp rename to crates/vk-graph-fx/res/shader/transition/bow_tie_with_parameter.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/burn.comp b/crates/vk-graph-fx/res/shader/transition/burn.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/burn.comp rename to crates/vk-graph-fx/res/shader/transition/burn.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/butterfly_wave_scrawler.comp b/crates/vk-graph-fx/res/shader/transition/butterfly_wave_scrawler.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/butterfly_wave_scrawler.comp rename to crates/vk-graph-fx/res/shader/transition/butterfly_wave_scrawler.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/cannabis_leaf.comp b/crates/vk-graph-fx/res/shader/transition/cannabis_leaf.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/cannabis_leaf.comp rename to crates/vk-graph-fx/res/shader/transition/cannabis_leaf.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/circle.comp b/crates/vk-graph-fx/res/shader/transition/circle.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/circle.comp rename to crates/vk-graph-fx/res/shader/transition/circle.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/circle_crop.comp b/crates/vk-graph-fx/res/shader/transition/circle_crop.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/circle_crop.comp rename to crates/vk-graph-fx/res/shader/transition/circle_crop.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/circle_open.comp b/crates/vk-graph-fx/res/shader/transition/circle_open.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/circle_open.comp rename to crates/vk-graph-fx/res/shader/transition/circle_open.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/color_distance.comp b/crates/vk-graph-fx/res/shader/transition/color_distance.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/color_distance.comp rename to crates/vk-graph-fx/res/shader/transition/color_distance.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/color_phase.comp b/crates/vk-graph-fx/res/shader/transition/color_phase.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/color_phase.comp rename to crates/vk-graph-fx/res/shader/transition/color_phase.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/coord_from_in.comp b/crates/vk-graph-fx/res/shader/transition/coord_from_in.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/coord_from_in.comp rename to crates/vk-graph-fx/res/shader/transition/coord_from_in.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/crazy_parametric_fun.comp b/crates/vk-graph-fx/res/shader/transition/crazy_parametric_fun.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/crazy_parametric_fun.comp rename to crates/vk-graph-fx/res/shader/transition/crazy_parametric_fun.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/cross_warp.comp b/crates/vk-graph-fx/res/shader/transition/cross_warp.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/cross_warp.comp rename to crates/vk-graph-fx/res/shader/transition/cross_warp.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/cross_zoom.comp b/crates/vk-graph-fx/res/shader/transition/cross_zoom.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/cross_zoom.comp rename to crates/vk-graph-fx/res/shader/transition/cross_zoom.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/crosshatch.comp b/crates/vk-graph-fx/res/shader/transition/crosshatch.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/crosshatch.comp rename to crates/vk-graph-fx/res/shader/transition/crosshatch.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/cube.comp b/crates/vk-graph-fx/res/shader/transition/cube.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/cube.comp rename to crates/vk-graph-fx/res/shader/transition/cube.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/directional.comp b/crates/vk-graph-fx/res/shader/transition/directional.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/directional.comp rename to crates/vk-graph-fx/res/shader/transition/directional.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/directional_easing.comp b/crates/vk-graph-fx/res/shader/transition/directional_easing.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/directional_easing.comp rename to crates/vk-graph-fx/res/shader/transition/directional_easing.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/directional_warp.comp b/crates/vk-graph-fx/res/shader/transition/directional_warp.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/directional_warp.comp rename to crates/vk-graph-fx/res/shader/transition/directional_warp.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/directional_wipe.comp b/crates/vk-graph-fx/res/shader/transition/directional_wipe.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/directional_wipe.comp rename to crates/vk-graph-fx/res/shader/transition/directional_wipe.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/displacement.comp b/crates/vk-graph-fx/res/shader/transition/displacement.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/displacement.comp rename to crates/vk-graph-fx/res/shader/transition/displacement.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/doom_screen.comp b/crates/vk-graph-fx/res/shader/transition/doom_screen.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/doom_screen.comp rename to crates/vk-graph-fx/res/shader/transition/doom_screen.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/doorway.comp b/crates/vk-graph-fx/res/shader/transition/doorway.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/doorway.comp rename to crates/vk-graph-fx/res/shader/transition/doorway.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/dreamy.comp b/crates/vk-graph-fx/res/shader/transition/dreamy.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/dreamy.comp rename to crates/vk-graph-fx/res/shader/transition/dreamy.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/dreamy_zoom.comp b/crates/vk-graph-fx/res/shader/transition/dreamy_zoom.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/dreamy_zoom.comp rename to crates/vk-graph-fx/res/shader/transition/dreamy_zoom.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/fade.comp b/crates/vk-graph-fx/res/shader/transition/fade.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/fade.comp rename to crates/vk-graph-fx/res/shader/transition/fade.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/fade_color.comp b/crates/vk-graph-fx/res/shader/transition/fade_color.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/fade_color.comp rename to crates/vk-graph-fx/res/shader/transition/fade_color.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/fade_grayscale.comp b/crates/vk-graph-fx/res/shader/transition/fade_grayscale.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/fade_grayscale.comp rename to crates/vk-graph-fx/res/shader/transition/fade_grayscale.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/film_burn.comp b/crates/vk-graph-fx/res/shader/transition/film_burn.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/film_burn.comp rename to crates/vk-graph-fx/res/shader/transition/film_burn.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/flyeye.comp b/crates/vk-graph-fx/res/shader/transition/flyeye.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/flyeye.comp rename to crates/vk-graph-fx/res/shader/transition/flyeye.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/glitch_displace.comp b/crates/vk-graph-fx/res/shader/transition/glitch_displace.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/glitch_displace.comp rename to crates/vk-graph-fx/res/shader/transition/glitch_displace.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/glitch_memories.comp b/crates/vk-graph-fx/res/shader/transition/glitch_memories.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/glitch_memories.comp rename to crates/vk-graph-fx/res/shader/transition/glitch_memories.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/grid_flip.comp b/crates/vk-graph-fx/res/shader/transition/grid_flip.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/grid_flip.comp rename to crates/vk-graph-fx/res/shader/transition/grid_flip.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/heart.comp b/crates/vk-graph-fx/res/shader/transition/heart.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/heart.comp rename to crates/vk-graph-fx/res/shader/transition/heart.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/hexagonalize.comp b/crates/vk-graph-fx/res/shader/transition/hexagonalize.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/hexagonalize.comp rename to crates/vk-graph-fx/res/shader/transition/hexagonalize.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/inverted_page_curl.comp b/crates/vk-graph-fx/res/shader/transition/inverted_page_curl.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/inverted_page_curl.comp rename to crates/vk-graph-fx/res/shader/transition/inverted_page_curl.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/kaleidoscope.comp b/crates/vk-graph-fx/res/shader/transition/kaleidoscope.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/kaleidoscope.comp rename to crates/vk-graph-fx/res/shader/transition/kaleidoscope.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/left_right.comp b/crates/vk-graph-fx/res/shader/transition/left_right.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/left_right.comp rename to crates/vk-graph-fx/res/shader/transition/left_right.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/linear_blur.comp b/crates/vk-graph-fx/res/shader/transition/linear_blur.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/linear_blur.comp rename to crates/vk-graph-fx/res/shader/transition/linear_blur.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/luma.comp b/crates/vk-graph-fx/res/shader/transition/luma.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/luma.comp rename to crates/vk-graph-fx/res/shader/transition/luma.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/luminance_melt.comp b/crates/vk-graph-fx/res/shader/transition/luminance_melt.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/luminance_melt.comp rename to crates/vk-graph-fx/res/shader/transition/luminance_melt.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/morph.comp b/crates/vk-graph-fx/res/shader/transition/morph.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/morph.comp rename to crates/vk-graph-fx/res/shader/transition/morph.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/mosaic.comp b/crates/vk-graph-fx/res/shader/transition/mosaic.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/mosaic.comp rename to crates/vk-graph-fx/res/shader/transition/mosaic.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/multiply.comp b/crates/vk-graph-fx/res/shader/transition/multiply.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/multiply.comp rename to crates/vk-graph-fx/res/shader/transition/multiply.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/overexposure.comp b/crates/vk-graph-fx/res/shader/transition/overexposure.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/overexposure.comp rename to crates/vk-graph-fx/res/shader/transition/overexposure.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/perlin.comp b/crates/vk-graph-fx/res/shader/transition/perlin.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/perlin.comp rename to crates/vk-graph-fx/res/shader/transition/perlin.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/pinwheel.comp b/crates/vk-graph-fx/res/shader/transition/pinwheel.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/pinwheel.comp rename to crates/vk-graph-fx/res/shader/transition/pinwheel.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/pixelize.comp b/crates/vk-graph-fx/res/shader/transition/pixelize.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/pixelize.comp rename to crates/vk-graph-fx/res/shader/transition/pixelize.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/polar_function.comp b/crates/vk-graph-fx/res/shader/transition/polar_function.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/polar_function.comp rename to crates/vk-graph-fx/res/shader/transition/polar_function.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/polka_dots_curtain.comp b/crates/vk-graph-fx/res/shader/transition/polka_dots_curtain.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/polka_dots_curtain.comp rename to crates/vk-graph-fx/res/shader/transition/polka_dots_curtain.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/power_kaleido.comp b/crates/vk-graph-fx/res/shader/transition/power_kaleido.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/power_kaleido.comp rename to crates/vk-graph-fx/res/shader/transition/power_kaleido.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/radial.comp b/crates/vk-graph-fx/res/shader/transition/radial.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/radial.comp rename to crates/vk-graph-fx/res/shader/transition/radial.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/random_noisex.comp b/crates/vk-graph-fx/res/shader/transition/random_noisex.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/random_noisex.comp rename to crates/vk-graph-fx/res/shader/transition/random_noisex.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/random_squares.comp b/crates/vk-graph-fx/res/shader/transition/random_squares.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/random_squares.comp rename to crates/vk-graph-fx/res/shader/transition/random_squares.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/ripple.comp b/crates/vk-graph-fx/res/shader/transition/ripple.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/ripple.comp rename to crates/vk-graph-fx/res/shader/transition/ripple.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/rotate.comp b/crates/vk-graph-fx/res/shader/transition/rotate.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/rotate.comp rename to crates/vk-graph-fx/res/shader/transition/rotate.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/rotate_scale.comp b/crates/vk-graph-fx/res/shader/transition/rotate_scale.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/rotate_scale.comp rename to crates/vk-graph-fx/res/shader/transition/rotate_scale.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/scale_in.comp b/crates/vk-graph-fx/res/shader/transition/scale_in.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/scale_in.comp rename to crates/vk-graph-fx/res/shader/transition/scale_in.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/simple_zoom.comp b/crates/vk-graph-fx/res/shader/transition/simple_zoom.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/simple_zoom.comp rename to crates/vk-graph-fx/res/shader/transition/simple_zoom.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/squares_wire.comp b/crates/vk-graph-fx/res/shader/transition/squares_wire.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/squares_wire.comp rename to crates/vk-graph-fx/res/shader/transition/squares_wire.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/squeeze.comp b/crates/vk-graph-fx/res/shader/transition/squeeze.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/squeeze.comp rename to crates/vk-graph-fx/res/shader/transition/squeeze.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/stereo_viewer.comp b/crates/vk-graph-fx/res/shader/transition/stereo_viewer.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/stereo_viewer.comp rename to crates/vk-graph-fx/res/shader/transition/stereo_viewer.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/swap.comp b/crates/vk-graph-fx/res/shader/transition/swap.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/swap.comp rename to crates/vk-graph-fx/res/shader/transition/swap.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/swirl.comp b/crates/vk-graph-fx/res/shader/transition/swirl.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/swirl.comp rename to crates/vk-graph-fx/res/shader/transition/swirl.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/tangent_motion_blur.comp b/crates/vk-graph-fx/res/shader/transition/tangent_motion_blur.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/tangent_motion_blur.comp rename to crates/vk-graph-fx/res/shader/transition/tangent_motion_blur.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/top_bottom.comp b/crates/vk-graph-fx/res/shader/transition/top_bottom.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/top_bottom.comp rename to crates/vk-graph-fx/res/shader/transition/top_bottom.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/tv_static.comp b/crates/vk-graph-fx/res/shader/transition/tv_static.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/tv_static.comp rename to crates/vk-graph-fx/res/shader/transition/tv_static.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/undulating_burn_out.comp b/crates/vk-graph-fx/res/shader/transition/undulating_burn_out.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/undulating_burn_out.comp rename to crates/vk-graph-fx/res/shader/transition/undulating_burn_out.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/water_drop.comp b/crates/vk-graph-fx/res/shader/transition/water_drop.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/water_drop.comp rename to crates/vk-graph-fx/res/shader/transition/water_drop.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/wind.comp b/crates/vk-graph-fx/res/shader/transition/wind.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/wind.comp rename to crates/vk-graph-fx/res/shader/transition/wind.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/window_blinds.comp b/crates/vk-graph-fx/res/shader/transition/window_blinds.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/window_blinds.comp rename to crates/vk-graph-fx/res/shader/transition/window_blinds.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/window_slice.comp b/crates/vk-graph-fx/res/shader/transition/window_slice.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/window_slice.comp rename to crates/vk-graph-fx/res/shader/transition/window_slice.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/wipe_down.comp b/crates/vk-graph-fx/res/shader/transition/wipe_down.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/wipe_down.comp rename to crates/vk-graph-fx/res/shader/transition/wipe_down.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/wipe_left.comp b/crates/vk-graph-fx/res/shader/transition/wipe_left.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/wipe_left.comp rename to crates/vk-graph-fx/res/shader/transition/wipe_left.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/wipe_right.comp b/crates/vk-graph-fx/res/shader/transition/wipe_right.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/wipe_right.comp rename to crates/vk-graph-fx/res/shader/transition/wipe_right.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/wipe_up.comp b/crates/vk-graph-fx/res/shader/transition/wipe_up.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/wipe_up.comp rename to crates/vk-graph-fx/res/shader/transition/wipe_up.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/zoom_in_circles.comp b/crates/vk-graph-fx/res/shader/transition/zoom_in_circles.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/zoom_in_circles.comp rename to crates/vk-graph-fx/res/shader/transition/zoom_in_circles.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/zoom_left_wipe.comp b/crates/vk-graph-fx/res/shader/transition/zoom_left_wipe.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/zoom_left_wipe.comp rename to crates/vk-graph-fx/res/shader/transition/zoom_left_wipe.comp diff --git a/contrib/vk-graph-fx/res/shader/transition/zoom_right_wipe.comp b/crates/vk-graph-fx/res/shader/transition/zoom_right_wipe.comp similarity index 100% rename from contrib/vk-graph-fx/res/shader/transition/zoom_right_wipe.comp rename to crates/vk-graph-fx/res/shader/transition/zoom_right_wipe.comp diff --git a/contrib/vk-graph-fx/src/bitmap_font.rs b/crates/vk-graph-fx/src/bitmap_font.rs similarity index 100% rename from contrib/vk-graph-fx/src/bitmap_font.rs rename to crates/vk-graph-fx/src/bitmap_font.rs diff --git a/contrib/vk-graph-fx/src/image_loader.rs b/crates/vk-graph-fx/src/image_loader.rs similarity index 100% rename from contrib/vk-graph-fx/src/image_loader.rs rename to crates/vk-graph-fx/src/image_loader.rs diff --git a/contrib/vk-graph-fx/src/lib.rs b/crates/vk-graph-fx/src/lib.rs similarity index 100% rename from contrib/vk-graph-fx/src/lib.rs rename to crates/vk-graph-fx/src/lib.rs diff --git a/contrib/vk-graph-fx/src/presenter.rs b/crates/vk-graph-fx/src/presenter.rs similarity index 100% rename from contrib/vk-graph-fx/src/presenter.rs rename to crates/vk-graph-fx/src/presenter.rs diff --git a/contrib/vk-graph-fx/src/transition.rs b/crates/vk-graph-fx/src/transition.rs similarity index 100% rename from contrib/vk-graph-fx/src/transition.rs rename to crates/vk-graph-fx/src/transition.rs diff --git a/contrib/vk-graph-hot/.github/img/noise.png b/crates/vk-graph-hot/.github/img/noise.png similarity index 100% rename from contrib/vk-graph-hot/.github/img/noise.png rename to crates/vk-graph-hot/.github/img/noise.png diff --git a/contrib/vk-graph-hot/Cargo.toml b/crates/vk-graph-hot/Cargo.toml similarity index 91% rename from contrib/vk-graph-hot/Cargo.toml rename to crates/vk-graph-hot/Cargo.toml index 64e69e9e..52664a07 100644 --- a/contrib/vk-graph-hot/Cargo.toml +++ b/crates/vk-graph-hot/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" license = "MIT OR Apache-2.0" readme = "README.md" repository = "https://github.com/attackgoat/vk-graph" -homepage = "https://github.com/attackgoat/vk-graph/contrib/vk-graph-hot" +homepage = "https://github.com/attackgoat/vk-graph/crates/vk-graph-hot" keywords = ["gamedev", "vulkan"] categories = ["game-development", "multimedia::images", "rendering::engine"] description = "Hot-reloading shader pipelines for vk-graph" diff --git a/contrib/vk-graph-hot/README.md b/crates/vk-graph-hot/README.md similarity index 100% rename from contrib/vk-graph-hot/README.md rename to crates/vk-graph-hot/README.md diff --git a/contrib/vk-graph-hot/examples/README.md b/crates/vk-graph-hot/examples/README.md similarity index 100% rename from contrib/vk-graph-hot/examples/README.md rename to crates/vk-graph-hot/examples/README.md diff --git a/contrib/vk-graph-hot/examples/glsl.rs b/crates/vk-graph-hot/examples/glsl.rs similarity index 100% rename from contrib/vk-graph-hot/examples/glsl.rs rename to crates/vk-graph-hot/examples/glsl.rs diff --git a/contrib/vk-graph-hot/examples/hlsl.rs b/crates/vk-graph-hot/examples/hlsl.rs similarity index 100% rename from contrib/vk-graph-hot/examples/hlsl.rs rename to crates/vk-graph-hot/examples/hlsl.rs diff --git a/contrib/vk-graph-hot/examples/res/fill_image.comp b/crates/vk-graph-hot/examples/res/fill_image.comp similarity index 100% rename from contrib/vk-graph-hot/examples/res/fill_image.comp rename to crates/vk-graph-hot/examples/res/fill_image.comp diff --git a/contrib/vk-graph-hot/examples/res/fill_image.hlsl b/crates/vk-graph-hot/examples/res/fill_image.hlsl similarity index 100% rename from contrib/vk-graph-hot/examples/res/fill_image.hlsl rename to crates/vk-graph-hot/examples/res/fill_image.hlsl diff --git a/contrib/vk-graph-hot/examples/res/noise.glsl b/crates/vk-graph-hot/examples/res/noise.glsl similarity index 100% rename from contrib/vk-graph-hot/examples/res/noise.glsl rename to crates/vk-graph-hot/examples/res/noise.glsl diff --git a/contrib/vk-graph-hot/examples/res/noise.hlsl b/crates/vk-graph-hot/examples/res/noise.hlsl similarity index 100% rename from contrib/vk-graph-hot/examples/res/noise.hlsl rename to crates/vk-graph-hot/examples/res/noise.hlsl diff --git a/contrib/vk-graph-hot/src/compute.rs b/crates/vk-graph-hot/src/compute.rs similarity index 100% rename from contrib/vk-graph-hot/src/compute.rs rename to crates/vk-graph-hot/src/compute.rs diff --git a/contrib/vk-graph-hot/src/graphic.rs b/crates/vk-graph-hot/src/graphic.rs similarity index 100% rename from contrib/vk-graph-hot/src/graphic.rs rename to crates/vk-graph-hot/src/graphic.rs diff --git a/contrib/vk-graph-hot/src/lib.rs b/crates/vk-graph-hot/src/lib.rs similarity index 100% rename from contrib/vk-graph-hot/src/lib.rs rename to crates/vk-graph-hot/src/lib.rs diff --git a/contrib/vk-graph-hot/src/ray_trace.rs b/crates/vk-graph-hot/src/ray_trace.rs similarity index 100% rename from contrib/vk-graph-hot/src/ray_trace.rs rename to crates/vk-graph-hot/src/ray_trace.rs diff --git a/contrib/vk-graph-hot/src/shader.rs b/crates/vk-graph-hot/src/shader.rs similarity index 100% rename from contrib/vk-graph-hot/src/shader.rs rename to crates/vk-graph-hot/src/shader.rs diff --git a/contrib/vk-graph-imgui/Cargo.toml b/crates/vk-graph-imgui/Cargo.toml similarity index 100% rename from contrib/vk-graph-imgui/Cargo.toml rename to crates/vk-graph-imgui/Cargo.toml diff --git a/contrib/vk-graph-imgui/README.md b/crates/vk-graph-imgui/README.md similarity index 100% rename from contrib/vk-graph-imgui/README.md rename to crates/vk-graph-imgui/README.md diff --git a/contrib/vk-graph-imgui/res/font/mplus-1p/LICENSE b/crates/vk-graph-imgui/res/font/mplus-1p/LICENSE similarity index 100% rename from contrib/vk-graph-imgui/res/font/mplus-1p/LICENSE rename to crates/vk-graph-imgui/res/font/mplus-1p/LICENSE diff --git a/contrib/vk-graph-imgui/res/font/mplus-1p/mplus-1p-regular.ttf b/crates/vk-graph-imgui/res/font/mplus-1p/mplus-1p-regular.ttf similarity index 100% rename from contrib/vk-graph-imgui/res/font/mplus-1p/mplus-1p-regular.ttf rename to crates/vk-graph-imgui/res/font/mplus-1p/mplus-1p-regular.ttf diff --git a/contrib/vk-graph-imgui/res/font/roboto/LICENSE b/crates/vk-graph-imgui/res/font/roboto/LICENSE similarity index 100% rename from contrib/vk-graph-imgui/res/font/roboto/LICENSE rename to crates/vk-graph-imgui/res/font/roboto/LICENSE diff --git a/contrib/vk-graph-imgui/res/font/roboto/roboto-regular.ttf b/crates/vk-graph-imgui/res/font/roboto/roboto-regular.ttf similarity index 100% rename from contrib/vk-graph-imgui/res/font/roboto/roboto-regular.ttf rename to crates/vk-graph-imgui/res/font/roboto/roboto-regular.ttf diff --git a/contrib/vk-graph-imgui/res/shader/imgui.frag b/crates/vk-graph-imgui/res/shader/imgui.frag similarity index 100% rename from contrib/vk-graph-imgui/res/shader/imgui.frag rename to crates/vk-graph-imgui/res/shader/imgui.frag diff --git a/contrib/vk-graph-imgui/res/shader/imgui.vert b/crates/vk-graph-imgui/res/shader/imgui.vert similarity index 100% rename from contrib/vk-graph-imgui/res/shader/imgui.vert rename to crates/vk-graph-imgui/res/shader/imgui.vert diff --git a/contrib/vk-graph-imgui/src/lib.rs b/crates/vk-graph-imgui/src/lib.rs similarity index 100% rename from contrib/vk-graph-imgui/src/lib.rs rename to crates/vk-graph-imgui/src/lib.rs diff --git a/contrib/vk-graph-prelude/Cargo.toml b/crates/vk-graph-prelude/Cargo.toml similarity index 100% rename from contrib/vk-graph-prelude/Cargo.toml rename to crates/vk-graph-prelude/Cargo.toml diff --git a/contrib/vk-graph-prelude/README.md b/crates/vk-graph-prelude/README.md similarity index 100% rename from contrib/vk-graph-prelude/README.md rename to crates/vk-graph-prelude/README.md diff --git a/contrib/vk-graph-prelude/src/lib.rs b/crates/vk-graph-prelude/src/lib.rs similarity index 100% rename from contrib/vk-graph-prelude/src/lib.rs rename to crates/vk-graph-prelude/src/lib.rs diff --git a/contrib/vk-graph-window/Cargo.toml b/crates/vk-graph-window/Cargo.toml similarity index 100% rename from contrib/vk-graph-window/Cargo.toml rename to crates/vk-graph-window/Cargo.toml diff --git a/contrib/vk-graph-window/README.md b/crates/vk-graph-window/README.md similarity index 100% rename from contrib/vk-graph-window/README.md rename to crates/vk-graph-window/README.md diff --git a/contrib/vk-graph-window/examples/hello_world.rs b/crates/vk-graph-window/examples/hello_world.rs similarity index 100% rename from contrib/vk-graph-window/examples/hello_world.rs rename to crates/vk-graph-window/examples/hello_world.rs diff --git a/contrib/vk-graph-window/src/frame.rs b/crates/vk-graph-window/src/frame.rs similarity index 100% rename from contrib/vk-graph-window/src/frame.rs rename to crates/vk-graph-window/src/frame.rs diff --git a/contrib/vk-graph-window/src/lib.rs b/crates/vk-graph-window/src/lib.rs similarity index 100% rename from contrib/vk-graph-window/src/lib.rs rename to crates/vk-graph-window/src/lib.rs diff --git a/contrib/vk-graph-window/src/swapchain.rs b/crates/vk-graph-window/src/swapchain.rs similarity index 100% rename from contrib/vk-graph-window/src/swapchain.rs rename to crates/vk-graph-window/src/swapchain.rs diff --git a/examples/README.md b/examples/README.md index be964ba3..6cf15c22 100644 --- a/examples/README.md +++ b/examples/README.md @@ -17,7 +17,7 @@ Example | Instructions | Preview [min_max.rs](min_max.rs) |

cargo run --example min_max
| _See console output_ [mip_compute.rs](mip_compute.rs) |
cargo run --example mip_compute
| _See console output_ [subgroup_ops.rs](subgroup_ops.rs) |
cargo run --example subgroup_ops
| _See console output_ -[hello_world.rs](../contrib/vk-graph-window/examples/hello_world.rs) | _See [vk-graph-window](../contrib/vk-graph-window/README.md)_ | hello_world.rs +[hello_world.rs](../crates/vk-graph-window/examples/hello_world.rs) | _See [vk-graph-window](../crates/vk-graph-window/README.md)_ | hello_world.rs [app.rs](app.rs) |
cargo run --example app
| app.rs [triangle.rs](triangle.rs) |
cargo run --example triangle
| triangle.rs [vertex_layout.rs](vertex_layout.rs) |
cargo run --example vertex_layout
| vertex_layout.rs @@ -43,7 +43,7 @@ Example | Instructions | Preview The following packages offer examples for specific cases not listed here: -- [contrib/vk-graph-hot](../contrib/vk-graph-hot/examples/README.md): Shader pipeline hot-reload +- [crates/vk-graph-hot](../crates/vk-graph-hot/examples/README.md): Shader pipeline hot-reload - [attackgoat/mood](https://github.com/attackgoat/mood): FPS game prototype with level loading and multiple rendering backends - [attackgoat/jw-basic](https://github.com/attackgoat/jw-basic): BASIC interpreter with graphics diff --git a/examples/shader-toy/Cargo.toml b/examples/shader-toy/Cargo.toml index 2c72ba7f..5c644076 100644 --- a/examples/shader-toy/Cargo.toml +++ b/examples/shader-toy/Cargo.toml @@ -18,8 +18,8 @@ clap = { version = "4.5", features = ["derive"] } pak = "0.5" pretty_env_logger = "0.5" vk-graph = { path = "../.." } -vk-graph-fx = { path = "../../contrib/vk-graph-fx" } -vk-graph-window = { path = "../../contrib/vk-graph-window" } +vk-graph-fx = { path = "../../crates/vk-graph-fx" } +vk-graph-window = { path = "../../crates/vk-graph-window" } winit = "0.30" [build-dependencies] diff --git a/examples/skeletal-anim/Cargo.toml b/examples/skeletal-anim/Cargo.toml index 84c60f3b..cf1af3d8 100644 --- a/examples/skeletal-anim/Cargo.toml +++ b/examples/skeletal-anim/Cargo.toml @@ -13,7 +13,7 @@ glam = { version = "0.27", features = ["bytemuck"] } pak = "=0.5.0" pretty_env_logger = "0.5" vk-graph = { path = "../.." } -vk-graph-window = { path = "../../contrib/vk-graph-window" } +vk-graph-window = { path = "../../crates/vk-graph-window" } [build-dependencies] anyhow = "1.0" diff --git a/examples/vr/Cargo.toml b/examples/vr/Cargo.toml index b68eff4f..bdef166f 100644 --- a/examples/vr/Cargo.toml +++ b/examples/vr/Cargo.toml @@ -21,5 +21,5 @@ mint = "0.5" openxr = { version = "0.18", features = ["mint", "static"] } pretty_env_logger = "0.5" vk-graph = { path = "../.." } -vk-graph-hot = { path = "../../contrib/vk-graph-hot" } +vk-graph-hot = { path = "../../crates/vk-graph-hot" } tobj = "4.0" diff --git a/book/.gitignore b/guide/.gitignore similarity index 100% rename from book/.gitignore rename to guide/.gitignore diff --git a/book/book.toml b/guide/book.toml similarity index 100% rename from book/book.toml rename to guide/book.toml diff --git a/book/src/README.md b/guide/src/README.md similarity index 100% rename from book/src/README.md rename to guide/src/README.md diff --git a/book/src/SUMMARY.md b/guide/src/SUMMARY.md similarity index 100% rename from book/src/SUMMARY.md rename to guide/src/SUMMARY.md diff --git a/book/src/chapter_1.md b/guide/src/chapter_1.md similarity index 100% rename from book/src/chapter_1.md rename to guide/src/chapter_1.md diff --git a/book/src/cmd.md b/guide/src/cmd.md similarity index 100% rename from book/src/cmd.md rename to guide/src/cmd.md diff --git a/book/src/pipeline.md b/guide/src/pipeline.md similarity index 100% rename from book/src/pipeline.md rename to guide/src/pipeline.md diff --git a/book/src/pipeline_compute.md b/guide/src/pipeline_compute.md similarity index 100% rename from book/src/pipeline_compute.md rename to guide/src/pipeline_compute.md diff --git a/book/src/pipeline_compute_dispatch.md b/guide/src/pipeline_compute_dispatch.md similarity index 100% rename from book/src/pipeline_compute_dispatch.md rename to guide/src/pipeline_compute_dispatch.md diff --git a/book/src/pipeline_graphic.md b/guide/src/pipeline_graphic.md similarity index 100% rename from book/src/pipeline_graphic.md rename to guide/src/pipeline_graphic.md diff --git a/book/src/pipeline_graphic_draw.md b/guide/src/pipeline_graphic_draw.md similarity index 100% rename from book/src/pipeline_graphic_draw.md rename to guide/src/pipeline_graphic_draw.md diff --git a/book/src/pipeline_graphic_vertex.md b/guide/src/pipeline_graphic_vertex.md similarity index 100% rename from book/src/pipeline_graphic_vertex.md rename to guide/src/pipeline_graphic_vertex.md diff --git a/book/src/pipeline_push_const.md b/guide/src/pipeline_push_const.md similarity index 100% rename from book/src/pipeline_push_const.md rename to guide/src/pipeline_push_const.md diff --git a/book/src/pipeline_ray_trace.md b/guide/src/pipeline_ray_trace.md similarity index 100% rename from book/src/pipeline_ray_trace.md rename to guide/src/pipeline_ray_trace.md diff --git a/book/src/pipeline_spec.md b/guide/src/pipeline_spec.md similarity index 100% rename from book/src/pipeline_spec.md rename to guide/src/pipeline_spec.md diff --git a/book/src/resource.md b/guide/src/resource.md similarity index 100% rename from book/src/resource.md rename to guide/src/resource.md diff --git a/book/src/resource_accel_struct.md b/guide/src/resource_accel_struct.md similarity index 100% rename from book/src/resource_accel_struct.md rename to guide/src/resource_accel_struct.md diff --git a/book/src/resource_buffer.md b/guide/src/resource_buffer.md similarity index 100% rename from book/src/resource_buffer.md rename to guide/src/resource_buffer.md diff --git a/book/src/resource_image.md b/guide/src/resource_image.md similarity index 100% rename from book/src/resource_image.md rename to guide/src/resource_image.md diff --git a/src/cmd/compute.rs b/src/cmd/compute.rs index bcb5cba0..a045c368 100644 --- a/src/cmd/compute.rs +++ b/src/cmd/compute.rs @@ -428,7 +428,11 @@ mod deprecated { #[deprecated = "use shader_resource_access function with AccessType::ComputeShaderWrite"] #[doc(hidden)] - pub fn shader_resource_accesswrite_descriptor(self, descriptor: impl Into, node: N) -> Self + pub fn shader_resource_accesswrite_descriptor( + self, + descriptor: impl Into, + node: N, + ) -> Self where N: Node + View, N::Info: Copy, From 19e83cabbacd404c08f769addabfc77cef33a43f Mon Sep 17 00:00:00 2001 From: John Wells Date: Fri, 6 Mar 2026 12:19:21 -0500 Subject: [PATCH 37/86] book structure --- README.md | 3 +- bin/check | 3 + crates/vk-graph-window/src/lib.rs | 2 +- examples/app.rs | 2 +- examples/triangle.rs | 20 ++-- examples/vr/src/driver/instance.rs | 9 +- guide/README.md | 1 + guide/book.toml | 6 + guide/guide-helper/Cargo.toml | 13 +++ guide/guide-helper/README.md | 0 guide/guide-helper/src/lib.rs | 61 ++++++++++ guide/guide-helper/src/main.rs | 21 ++++ guide/src/README.md | 50 +++++++++ guide/src/SUMMARY.md | 36 +++--- guide/src/chapter_1.md | 1 - guide/src/cmd.md | 10 ++ guide/src/cmd_compute.md | 5 + guide/src/cmd_graphic.md | 12 ++ guide/src/cmd_ray_trace.md | 9 ++ guide/src/install.md | 14 +++ guide/src/pipeline.md | 1 - guide/src/pipeline_compute.md | 1 - guide/src/pipeline_compute_dispatch.md | 1 - guide/src/pipeline_graphic.md | 1 - guide/src/pipeline_graphic_draw.md | 1 - guide/src/pipeline_graphic_vertex.md | 1 - guide/src/pipeline_hot_reload.md | 1 + guide/src/pipeline_ray_trace.md | 1 - guide/src/pipeline_sync.md | 1 + guide/src/pipeline_sync_shader.md | 1 + guide/src/pipeline_sync_subresource.md | 1 + guide/src/resource.md | 1 - guide/src/resource_accel_struct.md | 1 - guide/src/resource_buffer.md | 1 - guide/src/resource_image.md | 1 - guide/src/scope.md | 1 + guide/src/usage.md | 147 +++++++++++++++++++++++++ guide/src/usage_debugging.md | 1 + guide/src/usage_device.md | 110 ++++++++++++++++++ guide/src/usage_moltenvk.md | 3 + guide/src/usage_shader.md | 39 +++++++ guide/src/usage_thread.md | 54 +++++++++ guide/src/usage_window.md | 24 ++++ src/driver/device.rs | 56 +++++----- src/driver/physical_device.rs | 6 + src/driver/shader.rs | 69 +++++++++--- 46 files changed, 714 insertions(+), 89 deletions(-) create mode 100644 guide/README.md create mode 100644 guide/guide-helper/Cargo.toml create mode 100644 guide/guide-helper/README.md create mode 100644 guide/guide-helper/src/lib.rs create mode 100644 guide/guide-helper/src/main.rs delete mode 100644 guide/src/chapter_1.md create mode 100644 guide/src/cmd_compute.md create mode 100644 guide/src/cmd_graphic.md create mode 100644 guide/src/cmd_ray_trace.md create mode 100644 guide/src/install.md delete mode 100644 guide/src/pipeline.md delete mode 100644 guide/src/pipeline_compute.md delete mode 100644 guide/src/pipeline_compute_dispatch.md delete mode 100644 guide/src/pipeline_graphic.md delete mode 100644 guide/src/pipeline_graphic_draw.md delete mode 100644 guide/src/pipeline_graphic_vertex.md create mode 100644 guide/src/pipeline_hot_reload.md delete mode 100644 guide/src/pipeline_ray_trace.md create mode 100644 guide/src/pipeline_sync.md create mode 100644 guide/src/pipeline_sync_shader.md create mode 100644 guide/src/pipeline_sync_subresource.md delete mode 100644 guide/src/resource.md delete mode 100644 guide/src/resource_accel_struct.md delete mode 100644 guide/src/resource_buffer.md delete mode 100644 guide/src/resource_image.md create mode 100644 guide/src/scope.md create mode 100644 guide/src/usage.md create mode 100644 guide/src/usage_debugging.md create mode 100644 guide/src/usage_device.md create mode 100644 guide/src/usage_moltenvk.md create mode 100644 guide/src/usage_shader.md create mode 100644 guide/src/usage_thread.md create mode 100644 guide/src/usage_window.md diff --git a/README.md b/README.md index a6347220..fef6259c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,8 @@ [![Crates.io](https://img.shields.io/crates/v/vk-graph.svg)](https://crates.io/crates/vk-graph) [![Docs.rs](https://docs.rs/vk-graph/badge.svg)](https://docs.rs/vk-graph) -[![Guide Book](https://tokei.rs/b1/github/attackgoat/vk-graph?category=code)](http://attackgoat.github.io/vk-graph) +![Guide Book](https://img.shields.io/badge/vk--graph-Guide--Book-green?link=https%3A%2F%2Fattackgoat.github.io%2Fvk-graph%2F) + _vk-graph_ is a high-performance Vulkan graphics driver with automatic resource management and execution. diff --git a/bin/check b/bin/check index 7132c35c..52fb4835 100755 --- a/bin/check +++ b/bin/check @@ -72,6 +72,9 @@ echo "Checking examples/vr" \ # Rust code tests cargo test || fail +# Build guide book +mdbook build guide/ || fail "Unable to build guide book" + # Check for semver breaking changes: if this fails you must update the crate version or fix the code cargo semver-checks check-release --default-features diff --git a/crates/vk-graph-window/src/lib.rs b/crates/vk-graph-window/src/lib.rs index cf2360c2..a7092d7c 100644 --- a/crates/vk-graph-window/src/lib.rs +++ b/crates/vk-graph-window/src/lib.rs @@ -422,7 +422,7 @@ impl WindowBuilder { /// TODO pub fn build(self) -> Result { let event_loop = EventLoop::new()?; - let device = Device::from_display(&event_loop, self.device_info)?; + let device = Device::try_from_display(&event_loop, self.device_info)?; Ok(Window { data: WindowData { diff --git a/examples/app.rs b/examples/app.rs index fedf104e..cf696585 100644 --- a/examples/app.rs +++ b/examples/app.rs @@ -42,7 +42,7 @@ impl ApplicationHandler for Application { let args = Args::parse(); let device_info = DeviceInfoBuilder::default().debug(args.debug); - let device = Device::from_display(&window, device_info).unwrap(); + let device = Device::try_from_display(&window, device_info).unwrap(); let surface = Surface::create(&device, &window, &window).unwrap(); let surface_formats = Surface::formats(&surface).unwrap(); diff --git a/examples/triangle.rs b/examples/triangle.rs index f5246c2d..f047020a 100644 --- a/examples/triangle.rs +++ b/examples/triangle.rs @@ -21,9 +21,8 @@ fn main() -> Result<(), WindowError> { &window.device, GraphicPipelineInfo::default(), [ - Shader::new_vertex( - glsl!( - r#" + glsl!( + r#" #version 460 core #pragma shader_stage(vertex) @@ -37,12 +36,10 @@ fn main() -> Result<(), WindowError> { vk_Color = color; } "# - ) - .as_slice(), - ), - Shader::new_fragment( - glsl!( - r#" + ) + .as_slice(), + glsl!( + r#" #version 460 core #pragma shader_stage(fragment) @@ -54,9 +51,8 @@ fn main() -> Result<(), WindowError> { vk_Color = vec4(color, 1); } "# - ) - .as_slice(), - ), + ) + .as_slice(), ], )?; diff --git a/examples/vr/src/driver/instance.rs b/examples/vr/src/driver/instance.rs index 1e6106b7..80e3316e 100644 --- a/examples/vr/src/driver/instance.rs +++ b/examples/vr/src/driver/instance.rs @@ -201,11 +201,12 @@ impl XrInstance { InstanceCreateError::VulkanUnsupported })?; - let device = Device::from_ash_device(ash_device, physical_device).map_err(|err| { - error!("Vulkan device: {err}"); + let device = + Device::try_from_ash_device(ash_device, physical_device).map_err(|err| { + error!("Vulkan device: {err}"); - InstanceCreateError::VulkanUnsupported - })?; + InstanceCreateError::VulkanUnsupported + })?; let event_buf = xr::EventDataBuffer::new(); Ok(Self { diff --git a/guide/README.md b/guide/README.md new file mode 100644 index 00000000..8c0d02fa --- /dev/null +++ b/guide/README.md @@ -0,0 +1 @@ +# Guide diff --git a/guide/book.toml b/guide/book.toml index e3c5e311..76517bb3 100644 --- a/guide/book.toml +++ b/guide/book.toml @@ -2,3 +2,9 @@ title = "vk-graph" authors = ["John Wells"] language = "en" + +[preprocessor.guide-helper] +command = "cargo run --quiet --manifest-path guide-helper/Cargo.toml" + +[build] +extra-watch-dirs = ["guide-helper/src"] diff --git a/guide/guide-helper/Cargo.toml b/guide/guide-helper/Cargo.toml new file mode 100644 index 00000000..65af5a57 --- /dev/null +++ b/guide/guide-helper/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "guide-helper" +version = "0.1.0" +publish = false +edition = "2024" +license = "MIT OR Apache-2.0" +readme = "README.md" + +[dependencies] +mdbook-preprocessor = { version = "0.5.2" } +semver = "1.0.27" +serde_json = "1.0.149" +toml = "1.0.3" diff --git a/guide/guide-helper/README.md b/guide/guide-helper/README.md new file mode 100644 index 00000000..e69de29b diff --git a/guide/guide-helper/src/lib.rs b/guide/guide-helper/src/lib.rs new file mode 100644 index 00000000..c812e74f --- /dev/null +++ b/guide/guide-helper/src/lib.rs @@ -0,0 +1,61 @@ +//! Preprocessor for the mdBook guide. + +use mdbook_preprocessor::book::Book; +use mdbook_preprocessor::errors::Result; +use mdbook_preprocessor::{Preprocessor, PreprocessorContext}; +use semver::{Version, VersionReq}; +use std::io; + +/// Preprocessing entry point. +pub fn handle_preprocessing() -> Result<()> { + let pre = GuideHelper; + let (ctx, book) = mdbook_preprocessor::parse_input(io::stdin())?; + + let book_version = Version::parse(&ctx.mdbook_version)?; + let version_req = VersionReq::parse(mdbook_preprocessor::MDBOOK_VERSION)?; + + if !version_req.matches(&book_version) { + eprintln!( + "warning: The {} plugin was built against version {} of mdbook, \ + but we're being called from version {}", + pre.name(), + mdbook_preprocessor::MDBOOK_VERSION, + ctx.mdbook_version + ); + } + + let processed_book = pre.run(&ctx, book)?; + serde_json::to_writer(io::stdout(), &processed_book)?; + + Ok(()) +} + +struct GuideHelper; + +impl Preprocessor for GuideHelper { + fn name(&self) -> &str { + "guide-helper" + } + + fn run(&self, _ctx: &PreprocessorContext, mut book: Book) -> Result { + insert_version(&mut book); + Ok(book) + } +} + +fn insert_version(book: &mut Book) { + let path = std::env::current_dir() + .unwrap() + .parent() + .unwrap() + .join("Cargo.toml"); + let manifest_contents = std::fs::read_to_string(&path).unwrap(); + let manifest: toml::Value = toml::from_str(&manifest_contents).unwrap(); + let version = manifest["package"]["version"].as_str().unwrap(); + const MARKER: &str = "{{ vk-graph.version }}"; + book.for_each_chapter_mut(|ch| { + if ch.content.contains(MARKER) { + ch.content = ch.content.replace(MARKER, version); + } + }); +} diff --git a/guide/guide-helper/src/main.rs b/guide/guide-helper/src/main.rs new file mode 100644 index 00000000..52d02074 --- /dev/null +++ b/guide/guide-helper/src/main.rs @@ -0,0 +1,21 @@ +//! Preprocessor for the mdBook guide. + +fn main() { + let mut args = std::env::args().skip(1); + match args.next().as_deref() { + Some("supports") => { + // Supports all renderers. + return; + } + Some(arg) => { + eprintln!("unknown argument: {arg}"); + std::process::exit(1); + } + None => {} + } + + if let Err(e) = guide_helper::handle_preprocessing() { + eprintln!("{e:?}"); + std::process::exit(1); + } +} diff --git a/guide/src/README.md b/guide/src/README.md index e10b99d0..7d853294 100644 --- a/guide/src/README.md +++ b/guide/src/README.md @@ -1 +1,51 @@ # Introduction + +`vk-graph` is a high-performance Vulkan driver for the Rust programming language featuring automated resource management and execution. It is _blazingly_-fast, built for real-world use, and executes all modern Vulkan commands[^modern]. + +This guide book will walk you through the mental model of this crate and help explain how it maps to Vulkan API usage. + +> [!IMPORTANT] +> Users should be familiar with the [Vulkan specification](https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html). + +## Design + +This guide provides a tour of the main public types: + +Resources + : [Buffer](), [Image](), [Shader](), _etc.._ + +[Graph]() + : _Builder-pattern for Vulkan commands_ + +[Queue]() + : _Automated graph submission_ + +A `Graph` is data built dynamically by your program every frame. Once complete, the graph is optimized into a `Queue` which may be used to submit commands to the Vulkan implementation. + +The wall-time overhead of this crate is intended to be in the 250 μs/frame range. + +## Philosophy + +Vulkan is hard. Synchronization is _extremely_ hard. `vk-graph` makes Vulkan *less painful* to write and *a joy* to maintain. + +The driver is based off the popular `ash` crate and `vk-sync`; reasoned as follows: +- _Everything_ is constructed from "`Info`" structs; all info is `Copy` +- Match the naming described in the specification +- Support all modern Vulkan usage[^modern] except video[^video] +- Don't use macro-magic or anything that needs to be learned +- Don't rely on "helper" functions unless absolutely required + +[^modern]: Modern Vulkan usage means no pixel queries. Anything else unsupported is due to there being better options, no current need, or no interest. Please open an issue. +[^video]: Video encode/decode is interesting but unsupported. As an alternative consider `ffmpeg`, `libavcodec`, or one of the experimental Rust bindings to the Vulkan video API. + +## History + + + + +- 2026 --- v0.15 released and renamed `vk-graph` +- 2022 --- v0.2 released with `RenderGraph` type based on +[`Kajiya`](https://github.com/EmbarkStudios/kajiya) +- 2020 --- Project migrated to Github and named `Screen-13` +- 2018 --- Project started privately as a game engine using +[`Corange`](https://github.com/orangeduck/Corange) diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index adf03791..37cb9026 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -2,23 +2,29 @@ [Introduction](README.md) +--- + # User Guide -- [Chapter 1](./chapter_1.md) +1. [Installation](./install.md) +1. [Usage](./usage.md) + 1. [Device Creation](./usage_device.md) + 1. [Shader Compilation](./usage_shader.md) + 1. [Threading Behavior](./usage_thread.md) + 1. [Window Handling](./usage_window.md) + 1. [MoltenVK](./usage_moltenvk.md) + 1. [Debugging](./usage_debugging.md) # Reference Guide -- [Resources](./resource.md) - - [Acceleration Structure](./resource_accel_struct.md) - - [Image](./resource_image.md) - - [Buffer](./resource_buffer.md) -- [Pipelines](./pipeline.md) - - [Push Constants](./pipeline_push_const.md) - - [Specialization](./pipeline_spec.md) - - [Compute](./pipeline_compute.md) - - [Dispatch Commands](./pipeline_compute_dispatch.md) - - [Graphic](./pipeline_graphic.md) - - [Draw Commands](./pipeline_graphic_draw.md) - - [Vertex Commands](./pipeline_graphic_vertex.md) - - [Ray Trace](./pipeline_ray_trace.md) -- [Commands](./cmd.md) +1. [Pipelines]() + 1. [Hot Reload](./pipeline_hot_reload.md) + 1. [Push Constants](./pipeline_push_const.md) + 1. [Specialization](./pipeline_spec.md) + 1. [Synchronization](./pipeline_sync.md) + 1. [Shaders](./pipeline_sync_shader.md) + 1. [Subresources](./pipeline_sync_subresource.md) +1. [Commands](./cmd.md) + 1. [Computing](./cmd_compute.md) + 1. [Graphics](./cmd_graphic.md) + 1. [Ray Tracing](./cmd_ray_trace.md) diff --git a/guide/src/chapter_1.md b/guide/src/chapter_1.md deleted file mode 100644 index b743fda3..00000000 --- a/guide/src/chapter_1.md +++ /dev/null @@ -1 +0,0 @@ -# Chapter 1 diff --git a/guide/src/cmd.md b/guide/src/cmd.md index 61c515e7..4174b0d6 100644 --- a/guide/src/cmd.md +++ b/guide/src/cmd.md @@ -1 +1,11 @@ # Commands + +1. [`blit_image`]() +1. [`clear_color_image`]() +1. [`clear_depth_stencil_image`]() +1. [`copy_buffer`]() +1. [`copy_buffer_to_image`]() +1. [`copy_image`]() +1. [`copy_image_to_buffer`]() +1. [`fill_buffer`]() +1. [`update_buffer`]() diff --git a/guide/src/cmd_compute.md b/guide/src/cmd_compute.md new file mode 100644 index 00000000..565dbf99 --- /dev/null +++ b/guide/src/cmd_compute.md @@ -0,0 +1,5 @@ +# Computing + +1. [`dispatch`]() +1. [`dispatch_base`]() +1. [`dispatch_indirect`]() diff --git a/guide/src/cmd_graphic.md b/guide/src/cmd_graphic.md new file mode 100644 index 00000000..b5b6325d --- /dev/null +++ b/guide/src/cmd_graphic.md @@ -0,0 +1,12 @@ +# Graphics + +1. [`bind_index_buffer`]() +1. [`bind_vertex_buffers`]() +1. [`draw`]() +1. [`draw_indexed`]() +1. [`draw_indexed_indirect`]() +1. [`draw_indexed_indirect_count`]() +1. [`draw_indirect`]() +1. [`draw_indirect_count`]() +1. [`set_scissor`]() +1. [`set_viewport`]() diff --git a/guide/src/cmd_ray_trace.md b/guide/src/cmd_ray_trace.md new file mode 100644 index 00000000..ac20c197 --- /dev/null +++ b/guide/src/cmd_ray_trace.md @@ -0,0 +1,9 @@ +# Ray Tracing + +1. [`build_accel_struct`]() +1. [`build_accel_struct_indirect`]() +1. [`set_stack_size`]() +1. [`trace_rays`]() +1. [`trace_rays_indirect`]() +1. [`update_accel_struct`]() +1. [`update_accel_struct_indirect`]() diff --git a/guide/src/install.md b/guide/src/install.md new file mode 100644 index 00000000..37d03241 --- /dev/null +++ b/guide/src/install.md @@ -0,0 +1,14 @@ +# Installation + +To get started with `vk-graph`, add it as a project dependency to your `Cargo.toml`: + +```toml +# Cargo.toml + +[dependencies] +vk-graph = "{{ vk-graph.version }}" +``` + +## Features + +TODO diff --git a/guide/src/pipeline.md b/guide/src/pipeline.md deleted file mode 100644 index 920ca953..00000000 --- a/guide/src/pipeline.md +++ /dev/null @@ -1 +0,0 @@ -# Pipelines diff --git a/guide/src/pipeline_compute.md b/guide/src/pipeline_compute.md deleted file mode 100644 index 3768cf01..00000000 --- a/guide/src/pipeline_compute.md +++ /dev/null @@ -1 +0,0 @@ -# Compute diff --git a/guide/src/pipeline_compute_dispatch.md b/guide/src/pipeline_compute_dispatch.md deleted file mode 100644 index 5f10c322..00000000 --- a/guide/src/pipeline_compute_dispatch.md +++ /dev/null @@ -1 +0,0 @@ -# Dispatch Commands diff --git a/guide/src/pipeline_graphic.md b/guide/src/pipeline_graphic.md deleted file mode 100644 index 93c96e0d..00000000 --- a/guide/src/pipeline_graphic.md +++ /dev/null @@ -1 +0,0 @@ -# Graphic diff --git a/guide/src/pipeline_graphic_draw.md b/guide/src/pipeline_graphic_draw.md deleted file mode 100644 index 047d7ae5..00000000 --- a/guide/src/pipeline_graphic_draw.md +++ /dev/null @@ -1 +0,0 @@ -# Draw Commands diff --git a/guide/src/pipeline_graphic_vertex.md b/guide/src/pipeline_graphic_vertex.md deleted file mode 100644 index a884507b..00000000 --- a/guide/src/pipeline_graphic_vertex.md +++ /dev/null @@ -1 +0,0 @@ -# Vertex Commands diff --git a/guide/src/pipeline_hot_reload.md b/guide/src/pipeline_hot_reload.md new file mode 100644 index 00000000..7d21fcd3 --- /dev/null +++ b/guide/src/pipeline_hot_reload.md @@ -0,0 +1 @@ +# Hot Reload diff --git a/guide/src/pipeline_ray_trace.md b/guide/src/pipeline_ray_trace.md deleted file mode 100644 index 133af86e..00000000 --- a/guide/src/pipeline_ray_trace.md +++ /dev/null @@ -1 +0,0 @@ -# Ray Trace diff --git a/guide/src/pipeline_sync.md b/guide/src/pipeline_sync.md new file mode 100644 index 00000000..ef0d0ee7 --- /dev/null +++ b/guide/src/pipeline_sync.md @@ -0,0 +1 @@ +# Synchronization diff --git a/guide/src/pipeline_sync_shader.md b/guide/src/pipeline_sync_shader.md new file mode 100644 index 00000000..e3eb2a18 --- /dev/null +++ b/guide/src/pipeline_sync_shader.md @@ -0,0 +1 @@ +# Shaders diff --git a/guide/src/pipeline_sync_subresource.md b/guide/src/pipeline_sync_subresource.md new file mode 100644 index 00000000..c9665053 --- /dev/null +++ b/guide/src/pipeline_sync_subresource.md @@ -0,0 +1 @@ +# Subresources diff --git a/guide/src/resource.md b/guide/src/resource.md deleted file mode 100644 index 3c1229ee..00000000 --- a/guide/src/resource.md +++ /dev/null @@ -1 +0,0 @@ -# Resources diff --git a/guide/src/resource_accel_struct.md b/guide/src/resource_accel_struct.md deleted file mode 100644 index 40d98da6..00000000 --- a/guide/src/resource_accel_struct.md +++ /dev/null @@ -1 +0,0 @@ -# Acceleration Structure diff --git a/guide/src/resource_buffer.md b/guide/src/resource_buffer.md deleted file mode 100644 index 80b58c75..00000000 --- a/guide/src/resource_buffer.md +++ /dev/null @@ -1 +0,0 @@ -# Buffer diff --git a/guide/src/resource_image.md b/guide/src/resource_image.md deleted file mode 100644 index 77cfc546..00000000 --- a/guide/src/resource_image.md +++ /dev/null @@ -1 +0,0 @@ -# Image diff --git a/guide/src/scope.md b/guide/src/scope.md new file mode 100644 index 00000000..dfcb4e1d --- /dev/null +++ b/guide/src/scope.md @@ -0,0 +1 @@ +# Project Scope diff --git a/guide/src/usage.md b/guide/src/usage.md new file mode 100644 index 00000000..77160938 --- /dev/null +++ b/guide/src/usage.md @@ -0,0 +1,147 @@ +# Usage + +`vk-graph` acts as a safe builder-pattern for Vulkan functions (_Example: [vkCmdDrawIndexed](https://docs.vulkan.org/refpages/latest/refpages/source/vkCmdDrawIndexed.html)_). + +Typical usage contains: + +```rust +// A borrow of Device is an argument of many vk-graph functions +let device: &Device = &self.device; +``` + +## Resources + +Resources, such as buffers and images, may be created from "`Info`" structs: + +```rust +let usage = vk::BufferUsageFlags::TRANSFER_SRC; +let info = BufferInfo::device_mem(320 * 200 * 4, usage); +let buffer: Buffer = Buffer::create(device, info)?; + +let usage = vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST; +let info = ImageInfo::image_2d(320, 200, vk::Format::R8G8B8A8_UNORM, usage)?; +let image: Image = Image::create(device, info)?; +``` + +Resources may be bound to a graph as `usize` handles referred to as _"nodes"_: + +```rust +let mut graph = Graph::default(); +let buffer: BufferNode = graph.bind_resource(buffer); +let image: ImageNode = graph.bind_resource(image); +``` + +Bound resources may be borrowed from graphs, commands, pipeline commands, or active command buffers using their node handle: + +```rust +let shared_image: &Arc = graph.resource(image); + +assert_eq!(shared_image.info.width, 320); +``` + +## Commands + +Nodes may be used with built-in graph commands: + +```rust +graph.clear_color_image(image, ClearColorValue::BLACK_ALPHA_ZERO); +``` + +Graphs may contain many commands: + +``` +graph + .fill_buffer(buffer, 0..320 * 200, 0) + .copy_buffer_to_image(buffer, image); +``` + +Commands enable custom Vulkan behavior: + +```rust +graph + .begin_cmd() + .access_resource(image, AccessType::TransferRead) + .access_resource(buffer, AccessType::TransferWrite) + .record_cmd_buf(move |cmd_buf| { + // Borrow resources from nodes we move into the closure + let buffer = cmd_buf.resource(buffer); + let image = cmd_buf.resource(image); + + // Run *any* Vulkan code using ash::Device + unsafe { + // Note: for example only, use safe versions! + cmd_buf.device.cmd_copy_image_to_buffer2( + cmd_buf.handle, + vk::CopyImageToBufferInfo2::default() + .src_image(image.handle) + .dst_buffer(buffer.handle), + ); + } + }) + .end_cmd(); +``` + +## Pipelines + +Pipelines allow shader code to execute as a graph command. A borrow of a pipeline may be bound to +record shader-stage specific commands: + +```glsl +// compute.glsl +#version 460 core +#pragma shader_stage(compute) + +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, rgba8) writeonly uniform image2D dstImage; + +void main() { + imageStore( + dstImage, + ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y), + vec4(0.0) + ); +} +``` + +```bash +# See: "Shader Compilation" +glslc compute.glsl -o compute.spv +``` + +```rust +let pipeline = ComputePipeline::create( + device, + ComputePipelineInfo::default(), + include!("compute.spv"), +)?; + +graph + .begin_cmd() + .bind_pipeline(&pipeline) + .shader_resource_access(0, image, AccessType::ComputeShaderWrite) + .record_cmd_buf(|cmd_buf| { + cmd_buf.dispatch(320, 200, 1); + }); +``` + +## Queue Submission + +Completed graphs are submitted to a Vulkan implementation queue for execution. + +> [!NOTE] +> While executing, resources used in a graph may be bound and used by other graphs. Graph commands +> access resources in the logical state defined by all prior commands and previously submitted +> graphs. + +Typical programs rely on a single `Graph` per frame and let their window implementation submit the +graph, but they may do so manually: + +```rust +// NOTE: This blocks, but may run on backgound threads or checked periodically +// NOTE: Dropping the submitted queue blocks, so we must choose wait-or-async +graph + .into_queue() + .submit(LazyPool::new(device), 0, 0)? + .wait_until_executed()?; +``` diff --git a/guide/src/usage_debugging.md b/guide/src/usage_debugging.md new file mode 100644 index 00000000..1e584c95 --- /dev/null +++ b/guide/src/usage_debugging.md @@ -0,0 +1 @@ +# Debugging diff --git a/guide/src/usage_device.md b/guide/src/usage_device.md new file mode 100644 index 00000000..126c740a --- /dev/null +++ b/guide/src/usage_device.md @@ -0,0 +1,110 @@ +# Device Creation + +Most Vulkan operations occur within the context of a logical device, provided by +`Device` (_a smart pointer for `ash::Device`_). + +> [!WARNING] +> Vulkan has no global state and does not share resources between devices by default. +> +> Do not combine resources from multiple devices! The steps required to share resources accross +> devices is not currently documented but could be inferred from the Vulkan specification. + +## Headless Operation + +For any sort of server-based rendering or similar Vulkan usage without a display, the following is +production-ready code used to create a device: + +```rust +// The default information is: +// DeviceInfo { +// debug: false, +// phyiscal_device_index: 0, +// } +let device = Device::new(DeviceInfo::default())?; +``` + +## Windowed Operation + +Prototype and demo code might use the built-in window handler, which creates a `Device` during +window creation: + +```toml +# Cargo.toml + +[dependencies] +vk-graph-window = "{{ vk-graph.version }}" +``` + +```rust +use vk_graph_window::WindowBuilder; + +let window = WindowBuilder::default().build()?; + +// Before run +let _: &Device = &window.device; + +window.run(|frame| { + // During any frame + let _: &Device = frame.device; +})?; +``` + +## Advanced + +There are several scenarios that require advanced `Device` creation techniques: + +- Allowing user-selection of device +- Custom Window(s) handling +- FFI with OpenXR (_or similar_) +- Unsupported drivers/platforms + +### Device Selection + +The entrypoint is an `Instance` from which the available hardware is enumerated and inspected: + +```rust +let instance = Instance::new(InstanceInfo::default())?; +let physical_devices = Instance::physical_devices(&instance)?; + +for physical_device in physical_devices { + // We are looking for a device with support for these features + if !physical_device.swapchain_ext + || !physical_device.ray_trace_features.ray_tracing_pipeline { + continue; + } + + let _: Device = physical_device.try_into_device()?; +} +``` + +### Native Device Usage + +Some scenarios require the Vulkan instance and/or device be created by other code and accepted for +use by `vk-graph`: + +```rust +// Native ash types from somewhere else +let entry: ash::Entry = todo!(); +let instance: vk::Instance = todo!(); +let physical_device: vk::PhysicalDevice = todo!(); + +// vk-graph types +let instance = Instance::from_entry(entry, instance)?; +let physical_device = Instance::phsyical_device(&instance, physical_device)?; + +// Use our PhysicalDevice to create a native ash::Device (OpenXR requires this pattern) +let device: ash::Device = physical_device + .create_ash_device(|create_info| { + // Somewhere else also provides the logical device! + let device: vk::Device = todo!(); + + Ok(ash::Device::load(instance.fp_v1_0(), device)) + })?; + +// Create a Device from their native stuff +let device = Device::try_from_ash_device(device, physical_device)?; +``` + +> [!TIP] +> See [`examples/vr`](https://github.com/attackgoat/vk-graph/tree/main/examples/vr) for an in-depth +> example of native device usage. diff --git a/guide/src/usage_moltenvk.md b/guide/src/usage_moltenvk.md new file mode 100644 index 00000000..5f12ea30 --- /dev/null +++ b/guide/src/usage_moltenvk.md @@ -0,0 +1,3 @@ +# MoltenVK + +TODO diff --git a/guide/src/usage_shader.md b/guide/src/usage_shader.md new file mode 100644 index 00000000..8f700117 --- /dev/null +++ b/guide/src/usage_shader.md @@ -0,0 +1,39 @@ +# Shader Compilation + +`vk-graph` does not provide any shader compiler or require any specific shading language. Users must +provide SPIR-V binary-format shaders. + +> [!TIP] +> See [Hot Reload](./pipeline_hot_reload.md) for details on a shader compiler provided as a separate +> crate. + +Examples using multiple shading languages and compilers are provided in the [`examples/`](https://github.com/attackgoat/vk-graph/tree/main/examples) directory. + +## Shader-stage `#pragma` + +This applies to GLSL and Shaderc generally but you might find similar functionality with other +languages and compilers. + +```glsl +// shader.glsl +#version 460 core +#pragma shader_stage(compute) + +void main() { + // Some code here +} +``` + +```rust +let spirv = include_glsl!("shader.glsl"); + +// #pragma allows for from_spirv syntax: +let vertex_shader = Shader::from_spirv( + spirv.as_slice(), +); + +// Without this #pragma we must specify stage: +let vertex_shader = Shader::new_compute( + spirv.as_slice(), +); +``` diff --git a/guide/src/usage_thread.md b/guide/src/usage_thread.md new file mode 100644 index 00000000..d0ffdee8 --- /dev/null +++ b/guide/src/usage_thread.md @@ -0,0 +1,54 @@ +# Threading Behavior + +`vk-graph` is intended to provide scalable performance when used on multiple host threads. All +commands support being called concurrently from multiple threads, but resources are defined to be +externally synchronized. This means that the caller must guarantee that no more than one thread is +submitting a resource at a given time. + +More precisely, `vk-graph` stores the most recent access of each subresource of a resource. As +commands are submitted to the Vulkan implementation queue, the internal state of these resources is +updated. + +Resource state is updated during the following function calls: + +- `Queue::submit` +- `Queue::submit_resource` +- `Queue::submit_resource_dependencies` + +> [!CAUTION] +> Do not call any `Queue` submission function accessing buffers, images, or acceleration structures +> currently being submitted on other threads. + +## Safe Patterns + +Resources (buffers, images, or acceleration structures) are the only mutable types which require any +thread safety notes. All other types provided by `vk-graph` are immutable data structures or Vulkan +handle smart pointers. + +For example, there is no race condition or thread contention caused by using the same pipeline on +two threads.[^threads] In fact, there is no runtime overhead at all from this. + +Additionally, it is safe to build `Graph` instances, bind resources, record command buffers, and +call `Graph::into_queue` at *any* time on *any* thread. + +These patterns are safe: +- Build `Graph` and `Send` to another thread for submission +- Build `Graph` and `Drop` it without submission +- `Send` resources to other threads _or_ share as `Arc` +- `Clone` device or pipelines and `Send` to other threads + +## Risky Patterns + +Host-mappable buffers require extra understanding to use properly. + +The contents of a buffer are undefined from the time of submission until that `Queue` has been +fully submitted, as indicated by `Queue::is_submitted`. This means that you should not call +`Buffer::mapped_slice` during any execution accessing that memory. + +_See: +[`examples/cpu_readback.rs`](https://github.com/attackgoat/vk-graph/blob/main/examples/cpu_readback.rs)_ + + +[^threads]: The internal implementation of `GraphicPipeline` does do a bit of caching in order to +improve performance, however this behavior should never generate issues with any fathomable +workload. diff --git a/guide/src/usage_window.md b/guide/src/usage_window.md new file mode 100644 index 00000000..87906f11 --- /dev/null +++ b/guide/src/usage_window.md @@ -0,0 +1,24 @@ +# Window Handling + +`vk-graph` does not directly provide any window implementation. Instead an accessory crate, +`vk-graph-window` is provided, based on `winit`. + +> [!TIP] +> [`vk-graph-window`](https://github.com/attackgoat/vk-graph/tree/main/crates/vk-graph-window) +> provides additional documentation and examples. + +## Swapchain + +The bifurcation of `vk-graph` along the window abstraction results in two `Swapchain` types, one in +each crate. + +Type | Usage +-- | -- +`vk_graph::driver::swapchain::Swapchain` | Vulkan swapchain smart pointer, contains "raw" functions +`vk_graph_window::swapchain::Swapchain` | High-level display interface for building window handlers + +### OpenXR + +Virtual reality support via OpenXR is provided as +[an example](https://github.com/attackgoat/vk-graph/tree/main/examples/vr) which also implements a +swapchain. diff --git a/src/driver/device.rs b/src/driver/device.rs index d1dd879d..41ba0e4f 100644 --- a/src/driver/device.rs +++ b/src/driver/device.rs @@ -117,7 +117,7 @@ impl Device { let instance = Instance::new(instance_info)?; let physical_device = select_physical_device(&instance, physical_device_index)?; - Self::from_physical_device(physical_device) + Self::try_from_physical_device(physical_device) } pub(crate) fn allocator(this: &Self) -> &Mutex { @@ -185,9 +185,30 @@ impl Device { this.inner.swapchain_ext.as_ref().expect("VK_KHR_swapchain") } + pub(crate) fn pipeline_cache(this: &Self) -> vk::PipelineCache { + this.inner.pipeline_cache + } + + /// TODO + /// + /// Panics if the indicies are invalid. + pub fn queue(this: &Self, queue_family_index: u32, queue_index: u32) -> vk::Queue { + debug_assert!( + (queue_family_index as usize) < this.physical_device.queue_families.len(), + "Queue family index must be within the range of the available queues created by the device." + ); + debug_assert!( + queue_index + < this.physical_device.queue_families[queue_family_index as usize].queue_count, + "Queue index must be within the range of the available queues created by the device." + ); + + this.inner.queues[queue_family_index as usize][queue_index as usize] + } + /// Loads and existing `ash` Vulkan device that may have been created by other means. #[profiling::function] - pub fn from_ash_device( + pub fn try_from_ash_device( device: ash::Device, physical_device: PhysicalDevice, ) -> Result { @@ -264,7 +285,7 @@ impl Device { /// Constructs a new device using the given configuration. #[profiling::function] - pub fn from_display( + pub fn try_from_display( display_handle: impl HasDisplayHandle, info: impl Into, ) -> Result { @@ -289,12 +310,12 @@ impl Device { let instance = Instance::new(instance_info)?; let physical_device = select_physical_device(&instance, physical_device_index)?; - Self::from_physical_device(physical_device) + Self::try_from_physical_device(physical_device) } /// Constructs a new device using the given physical device. #[profiling::function] - pub fn from_physical_device(physical_device: PhysicalDevice) -> Result { + pub fn try_from_physical_device(physical_device: PhysicalDevice) -> Result { let device = unsafe { physical_device.create_ash_device(|device_create_info| { physical_device.instance.create_device( @@ -312,28 +333,7 @@ impl Device { info!("created {}", physical_device.properties_v1_0.device_name); - Self::from_ash_device(device, physical_device) - } - - pub(crate) fn pipeline_cache(this: &Self) -> vk::PipelineCache { - this.inner.pipeline_cache - } - - /// TODO - /// - /// Panics if the indicies are invalid. - pub fn queue(this: &Self, queue_family_index: u32, queue_index: u32) -> vk::Queue { - debug_assert!( - (queue_family_index as usize) < this.physical_device.queue_families.len(), - "Queue family index must be within the range of the available queues created by the device." - ); - debug_assert!( - queue_index - < this.physical_device.queue_families[queue_family_index as usize].queue_count, - "Queue index must be within the range of the available queues created by the device." - ); - - this.inner.queues[queue_family_index as usize][queue_index as usize] + Self::try_from_ash_device(device, physical_device) } #[profiling::function] @@ -546,7 +546,7 @@ pub(crate) mod deprecated { info: impl Into, display_handle: &impl HasDisplayHandle, ) -> Result { - Self::from_display(display_handle, info) + Self::try_from_display(display_handle, info) } #[deprecated = "use new function"] diff --git a/src/driver/physical_device.rs b/src/driver/physical_device.rs index 675fb253..b2ec1935 100644 --- a/src/driver/physical_device.rs +++ b/src/driver/physical_device.rs @@ -2,6 +2,7 @@ use { super::{DriverError, instance::Instance}, + crate::driver::device::Device, ash::{ext, khr, vk}, log::{debug, error}, std::{ @@ -548,6 +549,11 @@ impl PhysicalDevice { } } } + + /// TODO + pub fn try_into_device(self) -> Result { + Device::try_from_physical_device(self) + } } impl Debug for PhysicalDevice { diff --git a/src/driver/shader.rs b/src/driver/shader.rs index 67b910e5..e361a162 100644 --- a/src/driver/shader.rs +++ b/src/driver/shader.rs @@ -10,6 +10,7 @@ use { ReflectConfig, entry_point::EntryPoint, parse::SpirvBinary, + spirv::ExecutionModel, ty::{DescriptorType, ScalarType, Type, VectorType}, var::Variable, }, @@ -751,7 +752,7 @@ pub struct Shader { } impl Shader { - /// Specifies a shader with the given `stage` and shader code values. + /// Specifies a shader with the given `stage` and shader code. #[allow(clippy::new_ret_no_self)] pub fn new(stage: vk::ShaderStageFlags, spirv: impl Into) -> ShaderBuilder { ShaderBuilder::default().spirv(spirv).stage(stage) @@ -988,6 +989,11 @@ impl Shader { res } + /// Specifies a shader with the given shader code. + pub fn from_spirv(spirv: impl Into) -> ShaderBuilder { + ShaderBuilder::default().spirv(spirv) + } + fn image_sampler(&self, descriptor: Descriptor, name: &str) -> (SamplerInfo, bool) { self.image_samplers .get(&descriptor) @@ -1413,6 +1419,15 @@ impl From for Shader { } } +impl From for Shader +where + T: Into, +{ + fn from(spirv: T) -> Self { + Shader::from_spirv(spirv).build() + } +} + // HACK: https://github.com/colin-kiegel/rust-derive-builder/issues/56 impl ShaderBuilder { /// Specifies a shader with the given `stage` and shader code values. @@ -1423,20 +1438,44 @@ impl ShaderBuilder { /// Builds a new `Shader`. pub fn build(mut self) -> Shader { let entry_name = self.entry_name.as_deref().unwrap_or("main"); - self.entry_point = Some( - Shader::reflect_entry_point( - entry_name, - self.spirv - .as_ref() - .map(|spirv| spirv.words()) - .expect("spirv code must be set at initialization"), - self.specialization - .as_ref() - .map(|opt| opt.as_ref()) - .unwrap_or_default(), - ) - .unwrap_or_else(|_| panic!("invalid shader code for entry name \'{entry_name}\'")), - ); + let entry_point = Shader::reflect_entry_point( + entry_name, + self.spirv + .as_ref() + .map(|spirv| spirv.words()) + .expect("spirv code must be set at initialization"), + self.specialization + .as_ref() + .map(|opt| opt.as_ref()) + .unwrap_or_default(), + ) + .unwrap_or_else(|_| panic!("invalid shader code for entry name \'{entry_name}\'")); + + if self.stage.unwrap_or_default().is_empty() { + self.stage = Some(match entry_point.exec_model { + ExecutionModel::Vertex => vk::ShaderStageFlags::VERTEX, + ExecutionModel::TessellationControl => vk::ShaderStageFlags::TESSELLATION_CONTROL, + ExecutionModel::TessellationEvaluation => { + vk::ShaderStageFlags::TESSELLATION_EVALUATION + } + ExecutionModel::Geometry => vk::ShaderStageFlags::GEOMETRY, + ExecutionModel::Fragment => vk::ShaderStageFlags::FRAGMENT, + ExecutionModel::GLCompute => vk::ShaderStageFlags::COMPUTE, + ExecutionModel::Kernel => unimplemented!("unknown stage"), + ExecutionModel::TaskNV => vk::ShaderStageFlags::TASK_EXT, + ExecutionModel::MeshNV => vk::ShaderStageFlags::MESH_EXT, + ExecutionModel::RayGenerationNV => vk::ShaderStageFlags::RAYGEN_KHR, + ExecutionModel::IntersectionNV => vk::ShaderStageFlags::INTERSECTION_KHR, + ExecutionModel::AnyHitNV => vk::ShaderStageFlags::ANY_HIT_KHR, + ExecutionModel::ClosestHitNV => vk::ShaderStageFlags::CLOSEST_HIT_KHR, + ExecutionModel::MissNV => vk::ShaderStageFlags::MISS_KHR, + ExecutionModel::CallableNV => vk::ShaderStageFlags::CALLABLE_KHR, + ExecutionModel::TaskEXT => vk::ShaderStageFlags::TASK_EXT, + ExecutionModel::MeshEXT => vk::ShaderStageFlags::MESH_EXT, + }) + } + + self.entry_point = Some(entry_point); self.fallible_build() .expect("All required fields set at initialization") From 7dc2052c034b67ffd3163d451eb1f3c3a25120c5 Mon Sep 17 00:00:00 2001 From: John Wells Date: Fri, 6 Mar 2026 13:59:41 -0500 Subject: [PATCH 38/86] clean-up --- README.md | 2 +- guide/src/usage_thread.md | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fef6259c..3802b60f 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Crates.io](https://img.shields.io/crates/v/vk-graph.svg)](https://crates.io/crates/vk-graph) [![Docs.rs](https://docs.rs/vk-graph/badge.svg)](https://docs.rs/vk-graph) -![Guide Book](https://img.shields.io/badge/vk--graph-Guide--Book-green?link=https%3A%2F%2Fattackgoat.github.io%2Fvk-graph%2F) +[![Guide Book](https://img.shields.io/badge/vk--graph-Guide--Book-green?link=https%3A%2F%2Fattackgoat.github.io%2Fvk-graph%2F)](https://attackgoat.github.io/vk-graph) _vk-graph_ is a high-performance Vulkan graphics driver with automatic resource management and diff --git a/guide/src/usage_thread.md b/guide/src/usage_thread.md index d0ffdee8..affbc44c 100644 --- a/guide/src/usage_thread.md +++ b/guide/src/usage_thread.md @@ -19,6 +19,14 @@ Resource state is updated during the following function calls: > Do not call any `Queue` submission function accessing buffers, images, or acceleration structures > currently being submitted on other threads. +## Execution + +The provided `Queue` submission functions are designed to this workflow: +- Submit all commands the swapchain depends on +- Acquire swapchain +- Submit swapchain commands +- Submit any final unrelated commands + ## Safe Patterns Resources (buffers, images, or acceleration structures) are the only mutable types which require any @@ -42,8 +50,8 @@ These patterns are safe: Host-mappable buffers require extra understanding to use properly. The contents of a buffer are undefined from the time of submission until that `Queue` has been -fully submitted, as indicated by `Queue::is_submitted`. This means that you should not call -`Buffer::mapped_slice` during any execution accessing that memory. +fully executed, as indicated by `CommandBuffer::has_executed`. This means that you should not call +`Buffer::mapped_slice` during any submission or execution accessing that memory. _See: [`examples/cpu_readback.rs`](https://github.com/attackgoat/vk-graph/blob/main/examples/cpu_readback.rs)_ From 2a12e7fc04708b0f4c29aa3b7b4398dd36b218a1 Mon Sep 17 00:00:00 2001 From: John Wells Date: Sun, 8 Mar 2026 10:15:06 -0400 Subject: [PATCH 39/86] Add more documentation --- Cargo.toml | 2 +- README.md | 2 +- examples/README.md | 2 +- examples/getting-started.md | 54 ---------------------- guide/book.toml | 9 ++-- guide/guide-helper/Cargo.toml | 2 +- guide/guide-helper/src/lib.rs | 56 +++++++++++++++-------- guide/src/install.md | 31 ++++++++++++- {.github/img => guide/src}/profile.png | Bin guide/src/usage_debugging.md | 59 +++++++++++++++++++++++++ guide/src/usage_device.md | 2 +- 11 files changed, 137 insertions(+), 82 deletions(-) delete mode 100644 examples/getting-started.md rename {.github/img => guide/src}/profile.png (100%) diff --git a/Cargo.toml b/Cargo.toml index d0a225f9..30476153 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ profile-with-superluminal = ["profiling/profile-with-superluminal"] profile-with-tracy = ["profiling/profile-with-tracy"] [dependencies] -ash = "0.38" +ash = "0.38.0+1.3.281" ash-window = "0.13" derive_builder = "0.20" gpu-allocator = "0.28" diff --git a/README.md b/README.md index 3802b60f..31cd9df5 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ _Example code uses [puffin](https://crates.io/crates/puffin):_ cargo run --features profile-with-puffin --release --example vsm_omni ``` -Flamegraph of performance data +Flamegraph of performance data ## Quick Start diff --git a/examples/README.md b/examples/README.md index 6cf15c22..1f465a82 100644 --- a/examples/README.md +++ b/examples/README.md @@ -2,7 +2,7 @@ ## Getting Started -A helpful [getting started](getting-started.md) guide is available which describes basic _vk-graph_ +A helpful [guide](https://attackgoat.github.io/vk-graph) is available which describes _vk-graph_ types and functions. See the [README](../README.md) for more information. diff --git a/examples/getting-started.md b/examples/getting-started.md deleted file mode 100644 index c7602147..00000000 --- a/examples/getting-started.md +++ /dev/null @@ -1,54 +0,0 @@ -# Getting Started with _vk-graph_ - -This guide is intended for developers who are new to _vk-graph_ and want a step-by-step introduction. For further details on these -topics refer to the online [documentation](https://docs.rs/vk-graph/latest/vk_graph/). - -## Required Packages - -_Linux (Debian-like)_: -- `sudo apt install cmake uuid-dev libfontconfig-dev libssl-dev` - -_Mac OS (10.15 or later)_: -- Xcode 12 -- Python 2.7 -- `brew install cmake ossp-uuid` - -_Windows_: -- TODO (works but I haven't gathered the requirements) - -## Documentation - -Read the generated [documentation](https://docs.rs/vk-graph/latest/vk_graph/) online, or run the -following command locally: - -``` -cargo doc --open -``` - -## Changes - -Stay informed of recent changes to _vk-graph_ using the -[change log](https://github.com/attackgoat/vk-graph/blob/master/CHANGELOG.md) file. - -## Performance Profiling - -Most of the example code (_the ones which use an `EventLoop`_) support profiling using `puffin`. To -use it, first install and run `puffin_viewer` and then run the example with the -`--features profile-with-puffin` and `--release` flags. - -You may need to disable CPU thermal throttling in order to get consistent results on some platforms. -The inconsistent results are certainly valid, but they do not help in accurately measuring potential -changes. This may be done on Intel Linux machines by modifying the Intel P-State driver: - -```bash -echo 100 | sudo tee /sys/devices/system/cpu/intel_pstate/min_perf_pct -``` - -(_[Source](https://www.kernel.org/doc/Documentation/cpu-freq/intel-pstate.txt)_) - -## Helpful tools - -- [VulkanSDK](https://vulkan.lunarg.com/sdk/home) _(Required when calling `EventLoop::debug(true)`)_ -- NVIDIA: [nvidia-smi](https://developer.nvidia.com/nvidia-system-management-interface) -- AMD: [RadeonTop](https://github.com/clbr/radeontop) -- [RenderDoc](https://renderdoc.org/) diff --git a/guide/book.toml b/guide/book.toml index 76517bb3..8cc33366 100644 --- a/guide/book.toml +++ b/guide/book.toml @@ -3,8 +3,11 @@ title = "vk-graph" authors = ["John Wells"] language = "en" -[preprocessor.guide-helper] -command = "cargo run --quiet --manifest-path guide-helper/Cargo.toml" - [build] extra-watch-dirs = ["guide-helper/src"] + +[output.html] +smart-punctuation = true + +[preprocessor.guide-helper] +command = "cargo run --quiet --manifest-path guide-helper/Cargo.toml" diff --git a/guide/guide-helper/Cargo.toml b/guide/guide-helper/Cargo.toml index 65af5a57..96f60d20 100644 --- a/guide/guide-helper/Cargo.toml +++ b/guide/guide-helper/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" readme = "README.md" [dependencies] +cargo_toml = "0.22" mdbook-preprocessor = { version = "0.5.2" } semver = "1.0.27" serde_json = "1.0.149" -toml = "1.0.3" diff --git a/guide/guide-helper/src/lib.rs b/guide/guide-helper/src/lib.rs index c812e74f..e10c58cb 100644 --- a/guide/guide-helper/src/lib.rs +++ b/guide/guide-helper/src/lib.rs @@ -1,10 +1,11 @@ -//! Preprocessor for the mdBook guide. +//! Preprocessor for the vk-graph guide. -use mdbook_preprocessor::book::Book; -use mdbook_preprocessor::errors::Result; -use mdbook_preprocessor::{Preprocessor, PreprocessorContext}; -use semver::{Version, VersionReq}; -use std::io; +use { + cargo_toml::Manifest, + mdbook_preprocessor::{Preprocessor, PreprocessorContext, book::Book, errors::Result}, + semver::{Version, VersionReq}, + std::{env::current_dir, io}, +}; /// Preprocessing entry point. pub fn handle_preprocessing() -> Result<()> { @@ -38,24 +39,43 @@ impl Preprocessor for GuideHelper { } fn run(&self, _ctx: &PreprocessorContext, mut book: Book) -> Result { - insert_version(&mut book); + insert_crate_version(&mut book); + insert_vulkan_sdk_version(&mut book); + Ok(book) } } -fn insert_version(book: &mut Book) { - let path = std::env::current_dir() - .unwrap() - .parent() - .unwrap() - .join("Cargo.toml"); - let manifest_contents = std::fs::read_to_string(&path).unwrap(); - let manifest: toml::Value = toml::from_str(&manifest_contents).unwrap(); - let version = manifest["package"]["version"].as_str().unwrap(); - const MARKER: &str = "{{ vk-graph.version }}"; +fn manifest() -> Manifest { + let path = current_dir().unwrap().parent().unwrap().join("Cargo.toml"); + + Manifest::from_path(path).unwrap() +} + +fn insert_crate_version(book: &mut Book) { + let Version { major, minor, .. } = + Version::parse(manifest().package.unwrap().version()).unwrap(); + let version = format!("{major}.{minor}"); + + const MARKER: &str = "{{ crate.version }}"; + + book.for_each_chapter_mut(|ch| { + if ch.content.contains(MARKER) { + ch.content = ch.content.replace(MARKER, &version); + } + }); +} + +fn insert_vulkan_sdk_version(book: &mut Book) { + // Technically this is a VersionReq but we're not using it that way and want the build metadata + let Version { build, .. } = + Version::parse(manifest().dependencies.get("ash").unwrap().req()).unwrap(); + + const MARKER: &str = "{{ vulkan_sdk.version }}"; + book.for_each_chapter_mut(|ch| { if ch.content.contains(MARKER) { - ch.content = ch.content.replace(MARKER, version); + ch.content = ch.content.replace(MARKER, build.as_str()); } }); } diff --git a/guide/src/install.md b/guide/src/install.md index 37d03241..560320c9 100644 --- a/guide/src/install.md +++ b/guide/src/install.md @@ -6,9 +6,36 @@ To get started with `vk-graph`, add it as a project dependency to your `Cargo.to # Cargo.toml [dependencies] -vk-graph = "{{ vk-graph.version }}" +vk-graph = "{{ crate.version }}" ``` ## Features -TODO +- `loaded` (_default_): Support searching for the Vulkan loader manually at runtime. +- `linked`: Link the Vulkan loader at compile time. +- `profile_with_`: Use the specified profiling backend + - ...`puffin` + - ...`optick` + - ...`superluminal` + - ...`tracy` + +## Vulkan SDK + +Debug mode (setting the `debug` field of `DeviceInfo` or `InstanceInfo` to `true`) is only supported +when a compatible [Vulkan SDK](https://vulkan.lunarg.com/sdk/home) is installed. + +> [!IMPORTANT] +> The installed Vulkan SDK version must be at least v{{ vulkan_sdk.version }}. + +## Required Packages + +_Linux (Debian-like)_: +- `sudo apt install cmake uuid-dev libfontconfig-dev libssl-dev` + +_Mac OS (10.15 or later)_: +- Xcode 12 +- Python 2.7 +- `brew install cmake ossp-uuid` + +_Windows_: +- TODO diff --git a/.github/img/profile.png b/guide/src/profile.png similarity index 100% rename from .github/img/profile.png rename to guide/src/profile.png diff --git a/guide/src/usage_debugging.md b/guide/src/usage_debugging.md index 1e584c95..46c2feab 100644 --- a/guide/src/usage_debugging.md +++ b/guide/src/usage_debugging.md @@ -1 +1,60 @@ # Debugging + +`vk-graph` uses [`log`](https://crates.io/crates/log) for low-overhead logging. + +To enable logging, set the `RUST_LOG` environment variable to `trace`, `debug`, `info`, `warn` or +`error` and initialize the logging provider of your choice. Examples use +[`pretty_env_logger`](https://docs.rs/pretty_env_logger/latest/pretty_env_logger/). + +_You may also filter messages, for example:_ + +```bash +RUST_LOG=vk_graph::driver=trace,vk_graph=warn cargo run --example ray_trace +``` + +``` +TRACE vk_graph::driver::instance > created a Vulkan instance +DEBUG vk_graph::driver::physical_device > physical device: NVIDIA GeForce RTX 3090 +DEBUG vk_graph::driver::physical_device > extension "VK_KHR_16bit_storage" v1 +DEBUG vk_graph::driver::physical_device > extension "VK_KHR_8bit_storage" v1 +DEBUG vk_graph::driver::physical_device > extension "VK_KHR_acceleration_structure" v13 +... +``` + +## Performance Profiling + +`vk-graph` uses [`profiling`](https://crates.io/crates/profiling) and supports multiple profiling +providers. When not in use profiling has zero cost. + +To enable profiling, compile with one of the `profile-with-*` features enabled and initialize the +profiling provider of your choice. + +_Example code uses [puffin](https://crates.io/crates/puffin):_ + +```bash +cargo run --features profile-with-puffin --release --example vsm_omni +``` + +Flamegraph of performance data + + +### Comparing Results + +Always profile code using a release-mode build. + +You may need to disable CPU thermal throttling in order to get consistent results on some platforms. +The inconsistent results are certainly valid, but they do not help in accurately measuring potential +changes. This may be done on Intel Linux machines by modifying the Intel P-State driver: + +```bash +echo 100 | sudo tee /sys/devices/system/cpu/intel_pstate/min_perf_pct +``` + +(_[Source](https://www.kernel.org/doc/Documentation/cpu-freq/intel-pstate.txt)_) + +## Helpful tools + +- [VulkanSDK](https://vulkan.lunarg.com/sdk/home) _(Required when setting `debug` to `true`)_ +- NVIDIA: [nvidia-smi](https://developer.nvidia.com/nvidia-system-management-interface) +- AMD: [RadeonTop](https://github.com/clbr/radeontop) +- [RenderDoc](https://renderdoc.org/) diff --git a/guide/src/usage_device.md b/guide/src/usage_device.md index 126c740a..1f355259 100644 --- a/guide/src/usage_device.md +++ b/guide/src/usage_device.md @@ -32,7 +32,7 @@ window creation: # Cargo.toml [dependencies] -vk-graph-window = "{{ vk-graph.version }}" +vk-graph-window = "{{ crate.version }}" ``` ```rust From a6c378654b759f4e8547ef0aebc317148acf24d2 Mon Sep 17 00:00:00 2001 From: John Wells Date: Sun, 8 Mar 2026 21:29:21 -0400 Subject: [PATCH 40/86] dedicated image allocations --- README.md | 2 +- guide/src/README.md | 11 ++++------- guide/src/SUMMARY.md | 4 +++- guide/src/install.md | 2 +- guide/src/pi | 1 + guide/src/pipeline.md | 1 + guide/src/queue.md | 1 + guide/src/resource.md | 12 ++++++++++++ guide/src/usage_debugging.md | 12 ++++++++++++ guide/src/usage_device.md | 4 ++-- src/driver/image.rs | 16 +++++++++++++++- src/pool/alias.rs | 1 + src/pool/fifo.rs | 1 + 13 files changed, 55 insertions(+), 13 deletions(-) create mode 100644 guide/src/pi create mode 100644 guide/src/pipeline.md create mode 100644 guide/src/queue.md create mode 100644 guide/src/resource.md diff --git a/README.md b/README.md index 31cd9df5..8a036a3c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Crates.io](https://img.shields.io/crates/v/vk-graph.svg)](https://crates.io/crates/vk-graph) [![Docs.rs](https://docs.rs/vk-graph/badge.svg)](https://docs.rs/vk-graph) -[![Guide Book](https://img.shields.io/badge/vk--graph-Guide--Book-green?link=https%3A%2F%2Fattackgoat.github.io%2Fvk-graph%2F)](https://attackgoat.github.io/vk-graph) +[![Guide Book](https://img.shields.io/badge/vk--graph-Guide--Book-blue?link=https%3A%2F%2Fattackgoat.github.io%2Fvk-graph%2F)](https://attackgoat.github.io/vk-graph) _vk-graph_ is a high-performance Vulkan graphics driver with automatic resource management and diff --git a/guide/src/README.md b/guide/src/README.md index 7d853294..8f31cbe5 100644 --- a/guide/src/README.md +++ b/guide/src/README.md @@ -40,12 +40,9 @@ The driver is based off the popular `ash` crate and `vk-sync`; reasoned as follo ## History - - - -- 2026 --- v0.15 released and renamed `vk-graph` -- 2022 --- v0.2 released with `RenderGraph` type based on -[`Kajiya`](https://github.com/EmbarkStudios/kajiya) -- 2020 --- Project migrated to Github and named `Screen-13` - 2018 --- Project started privately as a game engine using [`Corange`](https://github.com/orangeduck/Corange) +- 2020 --- Project migrated to Github and named `screen-13` +- 2022 --- v0.2 released with `RenderGraph` type based on +[`Kajiya`](https://github.com/EmbarkStudios/kajiya) +- 2026 --- Project renamed `vk-graph` (v0.14) diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index 37cb9026..9af4d784 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -17,7 +17,8 @@ # Reference Guide -1. [Pipelines]() +1. [Resources](./resource.md) +1. [Pipelines](./pipeline.md) 1. [Hot Reload](./pipeline_hot_reload.md) 1. [Push Constants](./pipeline_push_const.md) 1. [Specialization](./pipeline_spec.md) @@ -28,3 +29,4 @@ 1. [Computing](./cmd_compute.md) 1. [Graphics](./cmd_graphic.md) 1. [Ray Tracing](./cmd_ray_trace.md) +1. [Queue](./queue.md) diff --git a/guide/src/install.md b/guide/src/install.md index 560320c9..3f32f43d 100644 --- a/guide/src/install.md +++ b/guide/src/install.md @@ -21,7 +21,7 @@ vk-graph = "{{ crate.version }}" ## Vulkan SDK -Debug mode (setting the `debug` field of `DeviceInfo` or `InstanceInfo` to `true`) is only supported +Debug mode (setting the `debug` field of `DeviceInfo` or `InstanceInfo` to `true`) is supported only when a compatible [Vulkan SDK](https://vulkan.lunarg.com/sdk/home) is installed. > [!IMPORTANT] diff --git a/guide/src/pi b/guide/src/pi new file mode 100644 index 00000000..920ca953 --- /dev/null +++ b/guide/src/pi @@ -0,0 +1 @@ +# Pipelines diff --git a/guide/src/pipeline.md b/guide/src/pipeline.md new file mode 100644 index 00000000..920ca953 --- /dev/null +++ b/guide/src/pipeline.md @@ -0,0 +1 @@ +# Pipelines diff --git a/guide/src/queue.md b/guide/src/queue.md new file mode 100644 index 00000000..d095a2b0 --- /dev/null +++ b/guide/src/queue.md @@ -0,0 +1 @@ +# Queue diff --git a/guide/src/resource.md b/guide/src/resource.md new file mode 100644 index 00000000..9557438a --- /dev/null +++ b/guide/src/resource.md @@ -0,0 +1,12 @@ +# Resources + +Buffers, images, and acceleration structures are the user-definable + +## Memory Allocation + +`vk-graph` uses a single memory allocator (currently `gpu-allocator`). + +The memory allocation strategy provides a large section of memory which is then sub-allocated for +the resources which use it. This may lead to fragmentation and memory exhaustion in some scenarios. + +Individual buffers or images may use dedicated memory allocations diff --git a/guide/src/usage_debugging.md b/guide/src/usage_debugging.md index 46c2feab..17edf0c6 100644 --- a/guide/src/usage_debugging.md +++ b/guide/src/usage_debugging.md @@ -1,5 +1,17 @@ # Debugging +Debug mode (setting the `debug` field of `DeviceInfo` or `InstanceInfo` to `true`) is supported only +when a compatible [Vulkan SDK](https://vulkan.lunarg.com/sdk/home) is installed. + +> [!IMPORTANT] +> The installed Vulkan SDK version must be at least v{{ vulkan_sdk.version }}. + +While in debug mode `vk-graph` watches for errors, warnings, and certain performance warnings +emitted from any currently enabled Vulkan debug application layers. Emitted events will cause the +active thread to be parked and log a message indicating how to attach a debugger. + +## Logging + `vk-graph` uses [`log`](https://crates.io/crates/log) for low-overhead logging. To enable logging, set the `RUST_LOG` environment variable to `trace`, `debug`, `info`, `warn` or diff --git a/guide/src/usage_device.md b/guide/src/usage_device.md index 1f355259..ff2c7857 100644 --- a/guide/src/usage_device.md +++ b/guide/src/usage_device.md @@ -6,8 +6,8 @@ Most Vulkan operations occur within the context of a logical device, provided by > [!WARNING] > Vulkan has no global state and does not share resources between devices by default. > -> Do not combine resources from multiple devices! The steps required to share resources accross -> devices is not currently documented but could be inferred from the Vulkan specification. +> Do not combine resources from multiple devices! The steps required to share resources across +> devices are not currently documented. ## Headless Operation diff --git a/src/driver/image.rs b/src/driver/image.rs index ae2b432c..4642b146 100644 --- a/src/driver/image.rs +++ b/src/driver/image.rs @@ -171,6 +171,11 @@ impl Image { })? }; let requirements = unsafe { device.get_image_memory_requirements(handle) }; + let allocation_scheme = if info.dedicated { + AllocationScheme::DedicatedImage(handle) + } else { + AllocationScheme::GpuAllocatorManaged + }; let allocation = { profiling::scope!("allocate"); @@ -186,7 +191,7 @@ impl Image { requirements, location: MemoryLocation::GpuOnly, linear: false, - allocation_scheme: AllocationScheme::GpuAllocatorManaged, + allocation_scheme, }) .map_err(|err| { warn!("unable to allocate image memory: {err}"); @@ -718,6 +723,13 @@ pub struct ImageInfo { #[builder(default = "1", setter(strip_option))] pub array_layer_count: u32, + /// Specifies a dedicated memory allocation managed by the Vulkan driver and not by the internal + /// memory allocation pool transient resources share. + /// + /// The driver may optimize access to dedicated buffers. + #[builder(default)] + pub dedicated: bool, + /// Image extent of the Z axis, when describing a three dimensional image. #[builder(setter(strip_option))] pub depth: u32, @@ -837,6 +849,7 @@ impl ImageInfo { usage: vk::ImageUsageFlags, ) -> Self { Self { + dedicated: false, ty, width, height, @@ -884,6 +897,7 @@ impl ImageInfo { pub fn into_builder(self) -> ImageInfoBuilder { ImageInfoBuilder { array_layer_count: Some(self.array_layer_count), + dedicated: Some(self.dedicated), depth: Some(self.depth), flags: Some(self.flags), fmt: Some(self.fmt), diff --git a/src/pool/alias.rs b/src/pool/alias.rs index bbd1f28a..4e8ce8a8 100644 --- a/src/pool/alias.rs +++ b/src/pool/alias.rs @@ -197,6 +197,7 @@ where for (item_info, item) in &self.images { if item_info.array_layer_count == info.array_layer_count + && item_info.dedicated == info.dedicated && item_info.depth == info.depth && item_info.fmt == info.fmt && item_info.height == info.height diff --git a/src/pool/fifo.rs b/src/pool/fifo.rs index 9357f584..ebedd414 100644 --- a/src/pool/fifo.rs +++ b/src/pool/fifo.rs @@ -291,6 +291,7 @@ impl Pool for FifoPool { for idx in 0..cache.len() { let item = unsafe { cache.get_unchecked(idx) }; if item.info.array_layer_count == info.array_layer_count + && item.info.dedicated == info.dedicated && item.info.depth == info.depth && item.info.fmt == info.fmt && item.info.height == info.height From 6c01e5518eefd7aba7684405c54caa8547e3ccbb Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 12 Mar 2026 13:55:04 -0400 Subject: [PATCH 41/86] update docs --- guide/src/README.md | 4 +- guide/src/SUMMARY.md | 5 +- guide/src/pipeline.md | 52 +++++++++ guide/src/pipeline_push_const.md | 27 +++++ guide/src/pipeline_spec.md | 34 ++++++ guide/src/pipeline_sync.md | 151 +++++++++++++++++++++++++ guide/src/pipeline_sync_shader.md | 1 - guide/src/pipeline_sync_subresource.md | 1 - guide/src/resource.md | 54 ++++++++- guide/src/resource_accel_struct.md | 73 ++++++++++++ guide/src/resource_buffer.md | 53 +++++++++ guide/src/resource_image.md | 83 ++++++++++++++ guide/src/scope.md | 1 - guide/src/usage.md | 43 ++++++- 14 files changed, 565 insertions(+), 17 deletions(-) delete mode 100644 guide/src/pipeline_sync_shader.md delete mode 100644 guide/src/pipeline_sync_subresource.md create mode 100644 guide/src/resource_accel_struct.md create mode 100644 guide/src/resource_buffer.md create mode 100644 guide/src/resource_image.md delete mode 100644 guide/src/scope.md diff --git a/guide/src/README.md b/guide/src/README.md index 8f31cbe5..4934d07d 100644 --- a/guide/src/README.md +++ b/guide/src/README.md @@ -11,14 +11,14 @@ This guide book will walk you through the mental model of this crate and help ex This guide provides a tour of the main public types: -Resources +[Driver]() : [Buffer](), [Image](), [Shader](), _etc.._ [Graph]() : _Builder-pattern for Vulkan commands_ [Queue]() - : _Automated graph submission_ + : _Automated graph execution_ A `Graph` is data built dynamically by your program every frame. Once complete, the graph is optimized into a `Queue` which may be used to submit commands to the Vulkan implementation. diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index 9af4d784..8db8f26d 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -18,13 +18,14 @@ # Reference Guide 1. [Resources](./resource.md) + 1. [Buffers](./resource_buffer.md) + 1. [Images](./resource_image.md) + 1. [Acceleration Structures](./resource_accel_struct.md) 1. [Pipelines](./pipeline.md) 1. [Hot Reload](./pipeline_hot_reload.md) 1. [Push Constants](./pipeline_push_const.md) 1. [Specialization](./pipeline_spec.md) 1. [Synchronization](./pipeline_sync.md) - 1. [Shaders](./pipeline_sync_shader.md) - 1. [Subresources](./pipeline_sync_subresource.md) 1. [Commands](./cmd.md) 1. [Computing](./cmd_compute.md) 1. [Graphics](./cmd_graphic.md) diff --git a/guide/src/pipeline.md b/guide/src/pipeline.md index 920ca953..6b410539 100644 --- a/guide/src/pipeline.md +++ b/guide/src/pipeline.md @@ -1 +1,53 @@ # Pipelines + +> [!CAUTION] +> All pipelines and resources (_buffers, images, and acceleration structures_) "bound" to any +> `Graph` must have been created by the same `Device`. + +Pipelines are created from `Device` references. They may be bound to graph commands. + +```rust +let mut graph = Graph::default() + .begin_cmd() + .bind_pipeline(ComputePipeline::create( + device, + include_bytes!("shader.spv"), + )?) + .record_cmd_buf(|cmd_buf| { + todo!("add resource access") + }) + .end_cmd(); +``` + +Pipelines are cheap to `Clone` and should be cached in between use. The recommendation is to bind a +borrow of a pipeline to when beginning a command. + +## Commands + +A graph command is the smallest unit which the `Queue` type will schedule for execution. + +Calls to `Graph::begin_cmd` (and, optionally `Graph::end_cmd`) define a single graph command which +will execute in physical order as recorded. During graph command recording you may change pipelines, +modify shader descriptor bindings, or otherwise modify the state of the command buffer. + +Example: + +```rust +let fire_pipeline = ComputePipeline::create(device, include_bytes!("fire.spv"))?; +let water_pipeline = ComputePipeline::create(device, include_bytes!("water.spv"))?; + +let mut graph = Graph::default(); +graph + .begin_cmd() + .bind_pipeline(&fire_pipeline) + .record_cmd_buf(|cmd_buf| todo!("1st")) + .bind_pipeline(&water_pipeline) + .record_cmd_buf(|cmd_buf| todo!("2nd")) + .bind_pipeline(&fire_pipeline) + .record_cmd_buf(|cmd_buf| todo!("3rd")); +``` + +A call to `Graph::end_cmd` is never requried and the command is automatically ended. The call is +useful for builder-pattern code which is building a very large series of commands. + +## Shaders diff --git a/guide/src/pipeline_push_const.md b/guide/src/pipeline_push_const.md index d4a7de2e..a97d027e 100644 --- a/guide/src/pipeline_push_const.md +++ b/guide/src/pipeline_push_const.md @@ -1 +1,28 @@ # Push Constants + +Command buffers may update a very small data cache which shaders may read during execution using +push constants. It is recommended to target 128 bytes as the maximum push constant data size. + +```glsl +// render_mesh.glsl +#version 460 core + +layout(push_constant) uniform PushConstants { + layout(offset = 0) uint mesh_index; +}; + +... +``` + +```rust +let shader = Shader::new_compute(include_bytes!("render_mesh.spv").as_slice()); +let pipeline = ComputePipeline::create(device, ComputePipelineInfo::default(), shader)?; + +Graph::default() + .bind_pipeline(&pipeline) + .record_cmd_buf(|cmd_buf| { + cmd_buf + .push_constants(0, 42u32.to_ne_bytes()) + .dispatch(8, 8, 1) + }); +``` diff --git a/guide/src/pipeline_spec.md b/guide/src/pipeline_spec.md index ae4b1832..d8b68cef 100644 --- a/guide/src/pipeline_spec.md +++ b/guide/src/pipeline_spec.md @@ -1 +1,35 @@ # Specialization + +Pipeline specialization allows pre-compiled SPIR-V binary shaders to be specialized with constant +values specified at run-time. + +The Vulkan implementation may use these constant values to generate optimized shader code. + +`vk-graph` provides `SpecializationMap` as an easy-to-use way of storing the data and lookup entries +required to use this feature. + +```glsl +// kaboom.glsl +#version 460 core + +layout(constant_id = 0) const float INFERNO_EPSILON = 0.999; +layout(constant_id = 1) const float COEFF_OF_BOOM = 1.4; + +... +``` + +```rust +// Use this shader for the glsl-specified values: +let shader = Shader::new_compute(include_bytes!("kaboom.spv").as_slice()); + +let specialization = SpecializationMap::new(bytemuck::bytes_of([ + 0.99999f32, + 1.0, + ])) + .constant(0, 0, 4) + .constant(1, 4, 8); + +// Use this shader for the updated run-time values: +let spec_shader = shader.specialization(specialization); +.... +``` diff --git a/guide/src/pipeline_sync.md b/guide/src/pipeline_sync.md index ef0d0ee7..c782c089 100644 --- a/guide/src/pipeline_sync.md +++ b/guide/src/pipeline_sync.md @@ -1 +1,152 @@ # Synchronization + +`vk-graph` provides a high-performance abstraction over Vulkan synchronization which retains the +low driver overhead of correctly synchronized command buffers. + +## Pipeline Barriers + +Vulkan specifies that resources and pipelines will have synchronized access when barriers are +inserted into the command stream. Unsynchronized access results in undefined behavior. + +> [!TIP] +> Unsynchronized access *may* be detected through debug assertions or Vulkan SDK debugging layers. + +## Access Type Abstraction + +`vk-graph` uses an enumeration of possible states to define all supported pipeline barriers in an +easy-to-use way. + +An `AccessType` is composed of a name and three private fields: + +Field|Type +-|- +`stage_mask`|`vk::PipelineStageFlags` +`access_mask`|`vk::AccessFlags` +`image_layout`|`vk::ImageLayout` + +Sample access types: + +Type|Usage +-|- +`AccessType::General`|Covers any access - useful for debug, generally avoid for performance reasons +`AccessType::ColorAttachmentWrite`|Written as a color attachment during rendering +`AccessType::ComputeShaderReadUniformBuffer`|Read as a uniform buffer in a compute shader + +## Resource Access + +The required access varies depending on the function being called and what the Vulkan specification +requires for a given command. + +Generally, access must be specified before each command uses a resource. It appears as an "access" +function call: + +```rust +graph + .begin_cmd() + .resource_access(some_buffer, AccessType::TransferRead) + .resource_access(some_image, AccessType::TransferWrite) + .record_cmd_buf(|cmd_buf| { + // we are synchronized! + // You may: + // - Read some_buffer + // - Write some_image + }); +``` + +Resource access is specified for and consumed by the following command buffer recording. For +multiple accesses, use multiple "access" and "record" function calls: + +```rust +graph + .begin_cmd() + .resource_access(buffer, AccessType::TransferRead) + .resource_access(image, AccessType::TransferWrite) + .record_cmd_buf(|cmd_buf| { + // Safe to copy buffer to image + }) + .resource_access(image, AccessType::TransferRead) + .resource_access(buffer, AccessType::TransferWrite) + .record_cmd_buf(|cmd_buf| { + // Safe to copy image to buffer + }); +``` + +## Shader Resource Access + +When a resource (buffer, image, or acceleration structure) is accessed from a shader the +`shader_resource_access` function is used: + +```glsl +// clear_image.glsl +#version 460 core +#pragma shader_stage(compute) + +layout(binding = 42, rgba8) writeonly uniform image2D dstImage; + +void main() { + imageStore( + dstImage, + ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y), + vec4(0) + ); +} +``` + +```rust +let mut graph = Graph::default(); + +let fmt = vk::Format::R8G8B8A8_UNORM; +let usage = vk::ImageUsageFlags::STORAGE; +let info = ImageInfo::image_2d(32, 32, fmt, usage); +let image = graph.bind_resource(Image::create( + device, + info, +)?); + +graph + .begin_cmd() + .bind_pipeline(ComputePipeline::create( + device, + include_glsl!("clear_image.glsl").as_slice(), + )) + .shader_resource_access(42, image, AccessType::ComputeShaderWrite) + .record_cmd_buf(|cmd_buf| { + cmd_buf.dispatch(32, 32, 1); + }); +``` + +## Subresource Access + +Buffer ranges and image views are referred to as subresource ranges and bound using "subresource" +function variants: + +```rust +let mut graph = Graph::default(); + +let fmt = vk::Format::R8G8B8A8_UNORM; +let usage = vk::ImageUsageFlags::STORAGE; +let info = ImageInfo::image_2d(32, 32, fmt, usage); +let image = graph.bind_resource(Image::create( + device, + info, +)?); + +graph + .begin_cmd() + .bind_pipeline(ComputePipeline::create( + device, + include_glsl!("clear_image.glsl").as_slice(), + )) + .shader_subresource_access(42, image, info, AccessType::ComputeShaderWrite) + .record_cmd_buf(|cmd_buf| { + cmd_buf.dispatch(32, 32, 1); + }); +``` + +## Built-In Commands + +The commands directly attached to a `Graph`, such as `Graph::copy_buffer_to_image`, do not require +any access function calls. + +The source code for these built-in commands uses public graph functions and provides good examples +of typical usage. diff --git a/guide/src/pipeline_sync_shader.md b/guide/src/pipeline_sync_shader.md deleted file mode 100644 index e3eb2a18..00000000 --- a/guide/src/pipeline_sync_shader.md +++ /dev/null @@ -1 +0,0 @@ -# Shaders diff --git a/guide/src/pipeline_sync_subresource.md b/guide/src/pipeline_sync_subresource.md deleted file mode 100644 index c9665053..00000000 --- a/guide/src/pipeline_sync_subresource.md +++ /dev/null @@ -1 +0,0 @@ -# Subresources diff --git a/guide/src/resource.md b/guide/src/resource.md index 9557438a..c5308660 100644 --- a/guide/src/resource.md +++ b/guide/src/resource.md @@ -1,12 +1,54 @@ # Resources -Buffers, images, and acceleration structures are the user-definable +> [!CAUTION] +> All pipelines and resources (_buffers, images, and acceleration structures_) "bound" to any +> `Graph` must have been created by the same `Device`. -## Memory Allocation +Owned resources are created from `Device` references. They may be bound directly to graphs. -`vk-graph` uses a single memory allocator (currently `gpu-allocator`). +A borrow of `Arc` of any resource may be bound to a graph if the resource needs to be referenced +in future graphs. -The memory allocation strategy provides a large section of memory which is then sub-allocated for -the resources which use it. This may lead to fragmentation and memory exhaustion in some scenarios. +Binding resources to a graph produces a "Node" handle which may be used in commands and shader +pipelines. `Graph::bind_resource(resource: T) -> N`: -Individual buffers or images may use dedicated memory allocations +`T`|`N` +-|- +`Buffer`|`BufferNode` +`Arc`|`BufferNode` +`Lease`|`BufferLeaseNode` +`Arc>`|`BufferLeaseNode` + +_(etc...)_ + +Resources may be borrowed from a graph. `Graph::resource(node: N) -> &T`: + +`N`|`T` +-|- +`BufferNode`|`Arc` +`BufferLeaseNode`|`Arc>` + +## Bound Resource Nodes + +The concept of binding resources to graphs as node handles exists to support the callback-style +command buffer recording provided by `vk-graph`. + +Commands are recorded in logical order, but the execution is re-ordered for performance and so a +closure argument is provided to call Vulkan command buffer functions. The use of a small and `Copy` +node handle allows resource handles to be moved into command buffer closures without `Arc::clone`. + +Additionally, node handles support internal optimizations by providing direct indexed access to +graph data structures. + +## Pooling Resources + +Pooled resources are leased from `Pool` implementations. Dropped leases return to the pool. + +The `Lease` type otherwise acts identically to an owned resource. + +## Aliased Resources + +Resource aliasing is available using the `AliasWrapper` and any `Pool`. + +Aliased resources allow extremely optimized programs to ensure minimal resources during complex +graphs. diff --git a/guide/src/resource_accel_struct.md b/guide/src/resource_accel_struct.md new file mode 100644 index 00000000..0a102e26 --- /dev/null +++ b/guide/src/resource_accel_struct.md @@ -0,0 +1,73 @@ +# Acceleration Structures + +```rust +// Some buffer holding geometry data +let buffer: Buffer = todo!(); + +// Some sample geometry to put into a BLAS: +let geometry = AccelerationStructureGeometryData::Triangles { + index_addr: buffer.device_address(), + index_type: vk::IndexType::UINT16, + max_vertex: 100, + transform_addr: None, + vertex_addr: buffer.device_address() + 2_048, + vertex_format: vk::Format::R32G32B32_SFLOAT, + vertex_stride: 12, +}; +let geom = AccelerationStructureGeometry { + max_primitive_count: 120, + flags: vk::GeometryFlagsKHR::OPAQUE, + geometry, +}; +let build_range = vk::AccelerationStructureBuildRangeInfoKHR { + primitive_count: 120, + primitive_offset: 0, + first_vertex: 0, + transform_offset: 0, +}; +let ty = vk::AccelerationStructureTypeKHR::BOTTOM_LEVEL; +let geom_info = AccelerationStructureGeometryInfo { + ty, + flags: vk::BuildAccelerationStructureFlagsKHR::ALLOW_UPDATE, + geometries: vec![ + (geom, build_range), + ].into_boxed_slice(), +}; + +// Use helper function to find size +let size = AccelerationStructure::size_of(device, &geom_info); + +// Create acceleration structure info multiple ways: +let info = AccelerationStructureInfo { + ty, + size, +}; +let other_info = AccelerationStructureInfo::blas(size); + +assert_eq!(info, other_info); + +// Builder pattern +let same_info = AccelerationStructureInfoBuilder::default() + .ty(ty) + .size(size); + +// Create directly from info +let blas = AccelerationStructure::create(device, info)?; + +// Info built from other info +// Note: Never calculate size/always get from function +let more_info = blas + .info + .into_builder() + .size(size * 2) + .build(); + +// The provided fields are helpful: +assert_eq!(blas.device, *device); +assert_eq!(blas.info, info); +assert_ne!(blas.buffer, vk::Buffer::null()); +assert_ne!(blas.handle, vk::AccelerationStrucuture::null()); + +// Acceleration structures have no "subresources" and are bound whole +let my_subresource = AccelerationStructureSubresourceRange; +``` diff --git a/guide/src/resource_buffer.md b/guide/src/resource_buffer.md new file mode 100644 index 00000000..7dc12fe2 --- /dev/null +++ b/guide/src/resource_buffer.md @@ -0,0 +1,53 @@ +# Buffers + +```rust +let size = 1_024; +let usage = vk::BufferUsageFlags::STORAGE; + +// Create buffer info multiple ways: +let info = BufferInfo { + alignment: 1, + dedicated: false, + host_read: false, + host_write: false, + size, + usage, +}; +let device_mem = BufferInfo::device_mem(size, usage); +let host_mem = BufferInfo::host_mem(size, usage); + +assert_eq!(info, device_mem); +assert_ne!(info, host_mem); + +// Builder pattern +let same_info = BufferInfoBuilder::default() + .size(size) + .usage(usage); + +// Info built from other info +let more_info = host_mem + .info + .into_builder() + .usage(usage | vk::BufferUsageFlags::INDIRECT_BUFFER) + .build(); + +// There is a helper function for creating buffers from a slice +let data = [1u8, 2, 3, 4]; +let buffer = Buffer::create_from_slice(device, usage, &data)?; + +// This is equivalent to: +let mut buffer = Buffer::create(device, host_mem)?; +buffer.mapped_slice_mut().copy_from_slice(&data); + +// Or: +let mut buffer = Buffer::create(device, host_mem)?; +buffer.copy_from_slice(&data); + +// The provided fields are helpful: +assert_eq!(buffer.device, *device); +assert_eq!(buffer.info, host_mem); +assert_ne!(buffer.handle, vk::Buffer::null()); + +// Buffer "subresources" are just ranges of that buffer +let my_subresource = 0..size; +``` diff --git a/guide/src/resource_image.md b/guide/src/resource_image.md new file mode 100644 index 00000000..1dc42451 --- /dev/null +++ b/guide/src/resource_image.md @@ -0,0 +1,83 @@ +# Images + +```rust +let (width, height) = (320, 200); +let usage = vk::ImageUsageFlags::SAMPLED; +let fmt = vk::Format::R8G8B8A8_UNORM; + +// Create image info multiple ways +let info = ImageInfo { + array_layer_count: 1, + dedicated: false, + depth: 1, + flags: vk::ImageCreateFlags::empty(), + fmt, + height, + mip_level_count: 1, + sample_count: SampleCount::Type1, + tiling: vk::ImageTiling::OPTIMAL, + ty: vk::ImageType::TYPE_2D, + usage, + width, +}; +let other_info = ImageInfo::image_2d(width, height, fmt, usage); +let cube_info = ImageInfo::cube(width, fmt, usage); + +assert_eq!(info, other_info); +assert_ne!(info, cube_info); + +// Builder pattern +let same_info = ImageInfoBuilder::default() + .width(width) + .height(height) + .depth(1) + .fmt(fmt) + .usage(usage) + .ty(vk::ImageType::TYPE_2D); + +// Info built from other info +let cube_info = cube_info + .info + .into_builder() + .flags(vk::ImageCreateFlags::CUBE_COMPATIBLE) + .build(); + +// Images are created simply +let image = Image::create(device, info)?; + +// For interop this may be handy: +let image = Image::from_raw(device, vk::Image::null(), info); + +// The provided fields are helpful: +assert_eq!(image.device, *device); +assert_eq!(image.info, info); +assert_ne!(image.handle, vk::Buffer::null()); + +// Image "subresources" are the native type: +let my_subresource = vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectMaskFlags::COLOR, + base_mip_level: 0, + level_count: 1, + base_array_layer: 0, + layer_count: 1, +}; + +// Image views are also subresources: +let image_view = ImageViewInfo { + array_layer_count: 1, + aspect_mask: vk::ImageAspectFlags::COLOR, + base_array_layer: 0, + base_mip_level: 0, + fmt, + mip_level_count: 1, + ty: vk::ImageViewType::TYPE_2D, +}; + +// Image views have the same builder functionality: +let other_view = ImageViewInfoBuilder::default(); + +// Image views can be inferred from the whole image info: +let addl_view = info.into_image_view() + +assert_eq!(image_view, addl_view); +``` diff --git a/guide/src/scope.md b/guide/src/scope.md deleted file mode 100644 index dfcb4e1d..00000000 --- a/guide/src/scope.md +++ /dev/null @@ -1 +0,0 @@ -# Project Scope diff --git a/guide/src/usage.md b/guide/src/usage.md index 77160938..64ccd71a 100644 --- a/guide/src/usage.md +++ b/guide/src/usage.md @@ -9,18 +9,48 @@ Typical usage contains: let device: &Device = &self.device; ``` + + + + ## Resources Resources, such as buffers and images, may be created from "`Info`" structs: ```rust let usage = vk::BufferUsageFlags::TRANSFER_SRC; -let info = BufferInfo::device_mem(320 * 200 * 4, usage); -let buffer: Buffer = Buffer::create(device, info)?; +let buffer_info = BufferInfo::device_mem(320 * 200 * 4, usage); +let buffer: Buffer = Buffer::create(device, buffer_info)?; let usage = vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST; -let info = ImageInfo::image_2d(320, 200, vk::Format::R8G8B8A8_UNORM, usage)?; -let image: Image = Image::create(device, info)?; +let image_info = ImageInfo::image_2d(320, 200, vk::Format::R8G8B8A8_UNORM, usage)?; +let image: Image = Image::create(device, image_info)?; +``` + +### Memory Allocation + +`vk-graph` uses a single memory allocator instance (currently `gpu-allocator`) for all allocations. + +The memory allocation strategy provides a large section of memory which is then sub-allocated for +the resources which use it. This may lead to fragmentation and memory exhaustion in some scenarios. + +Individual buffers or images may use dedicated memory allocations by setting their `dedicated` +field: + +```rust +// The info fields may be used or set directly +let uber_mesh_buf = Buffer::create( + device, + BufferInfo { + dedicated: true, + ..buffer_info + } +)?; + +// Builder functions are also availble +// (builder and the info types are interchangable) +let dedicated_info = image_info.into_builder().dedicated(true); +let important_image = Image::create(device, dedicated_info)?; ``` Resources may be bound to a graph as `usize` handles referred to as _"nodes"_: @@ -145,3 +175,8 @@ graph .submit(LazyPool::new(device), 0, 0)? .wait_until_executed()?; ``` + +### Device Usage + +Buffers, images, and acceleration structures resources are created and used by a single `Device`. +All commands which use a resource must execute on the same `Device` which created the resource. From 8e8ca2a446b576da0c6f4ae564785109d2fbce25 Mon Sep 17 00:00:00 2001 From: John Wells Date: Fri, 13 Mar 2026 22:46:35 -0400 Subject: [PATCH 42/86] hot shader updates --- bin/check | 3 + crates/vk-graph-egui/Cargo.toml | 2 +- crates/vk-graph-egui/src/lib.rs | 10 +- crates/vk-graph-fx/Cargo.toml | 2 +- crates/vk-graph-fx/src/bitmap_font.rs | 2 +- crates/vk-graph-fx/src/presenter.rs | 2 +- crates/vk-graph-hot/Cargo.toml | 9 +- crates/vk-graph-hot/README.md | 7 +- crates/vk-graph-hot/examples/glsl.rs | 18 +- crates/vk-graph-hot/examples/hlsl.rs | 24 +-- crates/vk-graph-hot/src/compute.rs | 104 ++++------- crates/vk-graph-hot/src/graphic.rs | 119 ++++--------- crates/vk-graph-hot/src/lib.rs | 167 +++++++++++++++--- crates/vk-graph-hot/src/ray_trace.rs | 122 +++++-------- crates/vk-graph-hot/src/shader.rs | 24 ++- crates/vk-graph-imgui/Cargo.toml | 2 +- crates/vk-graph-imgui/src/lib.rs | 2 +- crates/vk-graph-window/Cargo.toml | 2 +- .../vk-graph-window/examples/hello_world.rs | 2 +- crates/vk-graph-window/src/frame.rs | 2 +- crates/vk-graph-window/src/lib.rs | 4 +- crates/vk-graph-window/src/swapchain.rs | 6 +- examples/shader-toy/Cargo.toml | 2 +- examples/shader-toy/build.rs | 4 +- examples/shader-toy/src/main.rs | 6 +- examples/skeletal-anim/Cargo.toml | 2 +- examples/skeletal-anim/build.rs | 2 +- examples/skeletal-anim/src/main.rs | 10 +- examples/vr/Cargo.toml | 2 +- examples/vr/src/main.rs | 26 +-- src/cmd/compute.rs | 6 +- 31 files changed, 351 insertions(+), 344 deletions(-) diff --git a/bin/check b/bin/check index 52fb4835..cd8968fb 100755 --- a/bin/check +++ b/bin/check @@ -28,6 +28,7 @@ cargo fmt --manifest-path examples/vr/Cargo.toml && diff || fail "Unformatted ru echo "Checking vk-graph" \ && cargo check --all-targets || fail \ + && cargo check --all-targets --examples || fail \ && cargo clippy --all-targets || fail \ && echo "Checking vk-graph (w/ parking_lot)" \ && cargo check --all-targets --features parking_lot || fail \ @@ -43,6 +44,7 @@ echo "Checking crates/vk-graph-fx" \ echo "Checking crates/vk-graph-hot" \ && cargo check --manifest-path crates/vk-graph-hot/Cargo.toml || fail \ + && cargo check --manifest-path crates/vk-graph-hot/Cargo.toml --examples || fail \ && cargo clippy --manifest-path crates/vk-graph-hot/Cargo.toml || fail # echo "Checking crates/vk-graph-imgui" \ @@ -55,6 +57,7 @@ echo "Checking crates/vk-graph-prelude" \ echo "Checking crates/vk-graph-window" \ && cargo check --manifest-path crates/vk-graph-window/Cargo.toml || fail \ + && cargo check --manifest-path crates/vk-graph-window/Cargo.toml --examples || fail \ && cargo clippy --manifest-path crates/vk-graph-window/Cargo.toml || fail echo "Checking examples/shader-toy" \ diff --git a/crates/vk-graph-egui/Cargo.toml b/crates/vk-graph-egui/Cargo.toml index f4f1c391..8d2aea70 100644 --- a/crates/vk-graph-egui/Cargo.toml +++ b/crates/vk-graph-egui/Cargo.toml @@ -2,7 +2,7 @@ name = "vk-graph-egui" version = "0.1.0" authors = ["Christian Döring "] -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" readme = "README.md" diff --git a/crates/vk-graph-egui/src/lib.rs b/crates/vk-graph-egui/src/lib.rs index aff3a03e..0c2f60a0 100644 --- a/crates/vk-graph-egui/src/lib.rs +++ b/crates/vk-graph-egui/src/lib.rs @@ -4,7 +4,7 @@ /// TODO pub mod prelude { - pub use super::{egui, Egui}; + pub use super::{Egui, egui}; } pub use egui; @@ -189,10 +189,10 @@ impl Egui { ) { // Unbind textures for (id, tex) in bound_tex.iter() { - if let AnyImageNode::ImageLease(tex) = tex { - if let egui::TextureId::Managed(_) = *id { - self.textures.insert(*id, graph.resource(*tex).clone()); - } + if let AnyImageNode::ImageLease(tex) = tex + && let egui::TextureId::Managed(_) = *id + { + self.textures.insert(*id, graph.resource(*tex).clone()); } } diff --git a/crates/vk-graph-fx/Cargo.toml b/crates/vk-graph-fx/Cargo.toml index 060c1868..8f9c89e7 100644 --- a/crates/vk-graph-fx/Cargo.toml +++ b/crates/vk-graph-fx/Cargo.toml @@ -2,7 +2,7 @@ name = "vk-graph-fx" version = "0.1.0" authors = ["John Wells "] -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" readme = "README.md" repository = "https://github.com/attackgoat/vk-graph" diff --git a/crates/vk-graph-fx/src/bitmap_font.rs b/crates/vk-graph-fx/src/bitmap_font.rs index 15becde7..34fd3ecf 100644 --- a/crates/vk-graph-fx/src/bitmap_font.rs +++ b/crates/vk-graph-fx/src/bitmap_font.rs @@ -2,7 +2,7 @@ use { anyhow::Context, bmfont::BMFont, bytemuck::{cast, cast_slice}, - glam::{vec3, Mat4}, + glam::{Mat4, vec3}, std::sync::Arc, vk_graph_prelude::*, vk_shader_macros::include_glsl, diff --git a/crates/vk-graph-fx/src/presenter.rs b/crates/vk-graph-fx/src/presenter.rs index fa8f378b..088532a7 100644 --- a/crates/vk-graph-fx/src/presenter.rs +++ b/crates/vk-graph-fx/src/presenter.rs @@ -1,6 +1,6 @@ use { bytemuck::cast_slice, - glam::{vec3, Mat4}, + glam::{Mat4, vec3}, vk_graph_prelude::*, vk_shader_macros::include_glsl, }; diff --git a/crates/vk-graph-hot/Cargo.toml b/crates/vk-graph-hot/Cargo.toml index 52664a07..fbf5e4f4 100644 --- a/crates/vk-graph-hot/Cargo.toml +++ b/crates/vk-graph-hot/Cargo.toml @@ -2,7 +2,7 @@ name = "vk-graph-hot" version = "0.1.0" authors = ["John Wells "] -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" readme = "README.md" repository = "https://github.com/attackgoat/vk-graph" @@ -13,15 +13,16 @@ description = "Hot-reloading shader pipelines for vk-graph" [dependencies] anyhow = "1.0" -derive_builder = "0.13" +derive_builder = "0.20" log = "0.4" -notify = "6.1" +notify = "8.2" +paste = "1.0" vk-graph = { path = "../..", default-features = false } vk-graph-window = { path = "../vk-graph-window" } shader-prepper = "0.3.0-pre.3" shaderc = "0.10" [dev-dependencies] -clap = { version = "4.5", features = ["derive"] } +clap = { version = "4.6", features = ["derive"] } pretty_env_logger = "0.5" vk-graph-prelude = { path = "../vk-graph-prelude" } diff --git a/crates/vk-graph-hot/README.md b/crates/vk-graph-hot/README.md index abdfac9c..34e5f967 100644 --- a/crates/vk-graph-hot/README.md +++ b/crates/vk-graph-hot/README.md @@ -14,11 +14,8 @@ See the [example code](examples/README.md), See the [GLSL](examples/glsl.rs) and [HLSL](examples/hlsl.rs) examples for usage - the hot pipelines are drop-in replacements for the regular shader pipelines offered by _vk-graph_. -After creating a pipeline two functions are available, `hot` or `cold`. The result of each may be -bound to a render graph for any sort of regular use. - -- `hot()`: Returns the pipeline instance which includes any changes found on disk. -- `cold()`: Returns the most recent successful compilation without watching for changes. +Use `HotShader` with a file path and it will automatically update the created pipeline whenever the +files included in the source code change. ## Advanced usage diff --git a/crates/vk-graph-hot/examples/glsl.rs b/crates/vk-graph-hot/examples/glsl.rs index 91acee50..3762fb08 100644 --- a/crates/vk-graph-hot/examples/glsl.rs +++ b/crates/vk-graph-hot/examples/glsl.rs @@ -1,7 +1,7 @@ use { clap::Parser, std::path::PathBuf, - vk_graph_hot::prelude::*, + vk_graph_hot::{HotComputePipeline, HotShader}, vk_graph_prelude::*, vk_graph_window::{Window, WindowError}, }; @@ -19,10 +19,10 @@ fn main() -> Result<(), WindowError> { // Create a compute pipeline - the same as normal except for "Hot" prefixes and we provide the // shader source code path instead of the shader source code bytes let cargo_manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let mut pipeline = HotComputePipeline::create( + let pipeline = HotComputePipeline::create( &window.device, ComputePipelineInfo::default(), - HotShader::new_compute(cargo_manifest_dir.join("examples/res/fill_image.comp")), + HotShader::from_path(cargo_manifest_dir.join("examples/res/fill_image.comp")), )?; let mut frame_index: u32 = 0; @@ -32,14 +32,12 @@ fn main() -> Result<(), WindowError> { .graph .begin_cmd() .debug_name("make some noise") - .bind_pipeline(pipeline.hot()) - .write_descriptor(0, frame.swapchain_image) + .bind_pipeline(&pipeline) + .shader_resource_access(0, frame.swapchain_image, AccessType::ComputeShaderWrite) .record_cmd_buf(move |cmd_buf| { - cmd_buf.push_constants(&frame_index.to_ne_bytes()).dispatch( - frame.width, - frame.height, - 1, - ); + cmd_buf + .push_constants(0, &frame_index.to_ne_bytes()) + .dispatch(frame.width, frame.height, 1); }); frame_index += 1; diff --git a/crates/vk-graph-hot/examples/hlsl.rs b/crates/vk-graph-hot/examples/hlsl.rs index 5961a7bd..5b415dfd 100644 --- a/crates/vk-graph-hot/examples/hlsl.rs +++ b/crates/vk-graph-hot/examples/hlsl.rs @@ -1,7 +1,7 @@ use { clap::Parser, std::path::PathBuf, - vk_graph_hot::prelude::*, + vk_graph_hot::{HotGraphicPipeline, HotShader}, vk_graph_prelude::*, vk_graph_window::{Window, WindowError}, }; @@ -20,12 +20,12 @@ fn main() -> Result<(), WindowError> { // shader source code path instead of the shader source code bytes let cargo_manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let fill_image_path = cargo_manifest_dir.join("examples/res/fill_image.hlsl"); - let mut pipeline = HotGraphicPipeline::create( + let pipeline = HotGraphicPipeline::create( &window.device, GraphicPipelineInfo::default(), [ - HotShader::new_vertex(&fill_image_path).entry_name("vertex_main".to_string()), - HotShader::new_fragment(&fill_image_path).entry_name("fragment_main".to_string()), + HotShader::new_vertex(&fill_image_path).entry_name("vertex_main"), + HotShader::new_fragment(&fill_image_path).entry_name("fragment_main"), ], )?; @@ -36,14 +36,18 @@ fn main() -> Result<(), WindowError> { .graph .begin_cmd() .debug_name("make some noise") - .bind_pipeline(pipeline.hot()) - .clear_color(0, frame.swapchain_image) - .store_color(0, frame.swapchain_image) + .bind_pipeline(&pipeline) + .color_attachment_image( + 0, + frame.swapchain_image, + LoadOp::CLEAR_BLACK_ALPHA_ZERO, + StoreOp::Store, + ) .record_cmd_buf(move |cmd_buf| { cmd_buf - .push_constants_offset(0, &frame_index.to_ne_bytes()) - .push_constants_offset(4, &frame.width.to_ne_bytes()) - .push_constants_offset(8, &frame.height.to_ne_bytes()) + .push_constants(0, &frame_index.to_ne_bytes()) + .push_constants(4, &frame.width.to_ne_bytes()) + .push_constants(8, &frame.height.to_ne_bytes()) .draw(3, 1, 0, 0); }); diff --git a/crates/vk-graph-hot/src/compute.rs b/crates/vk-graph-hot/src/compute.rs index 57ed96b8..328c2482 100644 --- a/crates/vk-graph-hot/src/compute.rs +++ b/crates/vk-graph-hot/src/compute.rs @@ -1,30 +1,32 @@ //! TODO use { - super::{compile_shader_and_watch, create_watcher, shader::HotShader}, + super::{ + HotPipeline, compile_shader_and_watch, create_watcher, pipeline, pipeline_handle, + shader::HotShader, + }, log::info, - notify::RecommendedWatcher, - std::{ - ops::Deref, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, + std::sync::{ + Arc, RwLock, + atomic::{AtomicBool, Ordering}, }, - vk_graph::driver::{ - compute::{ComputePipeline, ComputePipelineInfo}, - device::Device, - DriverError, + vk_graph::{ + cmd::{CommandPipeline, CommandRef}, + driver::{ + DriverError, + compute::{ComputePipeline, ComputePipelineInfo}, + device::Device, + }, }, }; /// TODO #[derive(Debug)] pub struct HotComputePipeline { + cache: RwLock>, + device: Device, has_changes: Arc, - pipeline: ComputePipeline, shader: HotShader, - watcher: RecommendedWatcher, } impl HotComputePipeline { @@ -36,75 +38,41 @@ impl HotComputePipeline { ) -> Result { let shader = shader.into(); - let (mut watcher, has_changes) = create_watcher(); + let has_changes = Default::default(); + let mut watcher = create_watcher(&has_changes); + let compiled_shader = compile_shader_and_watch(&shader, &mut watcher)?; let pipeline = ComputePipeline::create(device, info, compiled_shader)?; Ok(Self { + cache: RwLock::new(HotPipeline { pipeline, watcher }), + device: device.clone(), has_changes, - pipeline, shader, - watcher, }) } - /// Returns the most recent compilation without checking for changes or re-compiling the shader - /// source code. - #[deprecated = "use Deref instead"] - #[doc(hidden)] - pub fn cold(&self) -> &ComputePipeline { - self - } - - /// Returns the most recent compilation after checking for changes, and if needed re-compiling - /// the shader source code. - pub fn hot(&mut self) -> &ComputePipeline { - let has_changes = self.has_changes.swap(false, Ordering::Relaxed); - - if has_changes { + fn compile_shader_and_bind_cmd<'a>( + &self, + cmd: CommandRef<'a>, + ) -> >::Ref { + if self.has_changes.swap(false, Ordering::Relaxed) { info!("Shader change detected"); - let (mut watcher, has_changes) = create_watcher(); - if let Ok(compiled_shader) = compile_shader_and_watch(&self.shader, &mut watcher) { - if let Ok(pipeline) = ComputePipeline::create( - self.pipeline.device(), - self.pipeline.info(), - compiled_shader, - ) { - self.pipeline = pipeline; - self.has_changes = has_changes; - self.watcher = watcher; - } + let mut cache = self.cache_mut(); + + if let Ok(shader) = compile_shader_and_watch(&self.shader, &mut cache.watcher) + && let Ok(pipeline) = + ComputePipeline::create(&self.device, cache.pipeline.info(), shader) + { + cache.pipeline = pipeline; } } - self + self.cache().pipeline.clone().bind_cmd(cmd) } } -impl AsRef for HotComputePipeline { - fn as_ref(&self) -> &ComputePipeline { - self - } -} - -impl Deref for HotComputePipeline { - type Target = ComputePipeline; - - fn deref(&self) -> &Self::Target { - &self.pipeline - } -} - -#[allow(unused)] -mod deprecated { - use {crate::compute::HotComputePipeline, vk_graph::driver::compute::ComputePipeline}; - - impl HotComputePipeline { - #[deprecated = "use Deref instead"] - fn as_ref(&self) -> &ComputePipeline { - self - } - } -} +pipeline!(Compute); +pipeline_handle!(Compute); diff --git a/crates/vk-graph-hot/src/graphic.rs b/crates/vk-graph-hot/src/graphic.rs index 9e5c5b6f..5972a68d 100644 --- a/crates/vk-graph-hot/src/graphic.rs +++ b/crates/vk-graph-hot/src/graphic.rs @@ -1,30 +1,29 @@ //! TODO use { - super::{compile_shader_and_watch, create_watcher, shader::HotShader}, + super::{HotPipeline, compile_shaders_and_watch, create_watcher, pipeline, shader::HotShader}, log::info, - notify::RecommendedWatcher, - std::{ - ops::Deref, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, + std::sync::{ + Arc, RwLock, + atomic::{AtomicBool, Ordering}, }, - vk_graph::driver::{ - device::Device, - graphic::{GraphicPipeline, GraphicPipelineInfo}, - DriverError, + vk_graph::{ + cmd::{CommandPipeline, CommandRef}, + driver::{ + DriverError, + device::Device, + graphic::{GraphicPipeline, GraphicPipelineInfo}, + }, }, }; /// TODO #[derive(Debug)] pub struct HotGraphicPipeline { + cache: RwLock>, + device: Device, has_changes: Arc, - pipeline: GraphicPipeline, shaders: Box<[HotShader]>, - watcher: RecommendedWatcher, } impl HotGraphicPipeline { @@ -37,88 +36,46 @@ impl HotGraphicPipeline { where S: Into, { - let shaders = shaders - .into_iter() - .map(|shader| shader.into()) - .collect::>(); + let shaders = shaders.into_iter().map(Into::into).collect::>(); - let (mut watcher, has_changes) = create_watcher(); - let compiled_shaders = shaders - .iter() - .map(|shader| compile_shader_and_watch(shader, &mut watcher)) - .collect::, _>>()?; + let has_changes = Default::default(); + let mut watcher = create_watcher(&has_changes); - let pipeline = GraphicPipeline::create(device, info, compiled_shaders)?; + let pipeline = { + GraphicPipeline::create( + device, + info, + compile_shaders_and_watch(&shaders, &mut watcher)?, + ) + }?; Ok(Self { + cache: RwLock::new(HotPipeline { pipeline, watcher }), + device: device.clone(), has_changes, - pipeline, shaders, - watcher, }) } - /// Returns the most recent compilation without checking for changes or re-compiling the shader - /// source code. - #[deprecated = "use Deref instead"] - #[doc(hidden)] - pub fn cold(&self) -> &GraphicPipeline { - self - } - - /// Returns the most recent compilation after checking for changes, and if needed re-compiling - /// the shader source code. - pub fn hot(&mut self) -> &GraphicPipeline { - let has_changes = self.has_changes.swap(false, Ordering::Relaxed); - - if has_changes { + fn compile_shader_and_bind_cmd<'a>( + &self, + cmd: CommandRef<'a>, + ) -> >::Ref { + if self.has_changes.swap(false, Ordering::Relaxed) { info!("Shader change detected"); - let (mut watcher, has_changes) = create_watcher(); - if let Ok(compiled_shaders) = self - .shaders - .iter() - .map(|shader| compile_shader_and_watch(shader, &mut watcher)) - .collect::, DriverError>>() + let mut cache = self.cache_mut(); + + if let Ok(shaders) = compile_shaders_and_watch(&self.shaders, &mut cache.watcher) + && let Ok(pipeline) = + GraphicPipeline::create(&self.device, cache.pipeline.info(), shaders) { - if let Ok(pipeline) = GraphicPipeline::create( - self.pipeline.device(), - self.pipeline.info(), - compiled_shaders, - ) { - self.pipeline = pipeline; - self.has_changes = has_changes; - self.watcher = watcher; - } + cache.pipeline = pipeline; } } - self - } -} - -impl AsRef for HotGraphicPipeline { - fn as_ref(&self) -> &GraphicPipeline { - self + self.cache().pipeline.clone().bind_cmd(cmd) } } -impl Deref for HotGraphicPipeline { - type Target = GraphicPipeline; - - fn deref(&self) -> &Self::Target { - &self.pipeline - } -} - -#[allow(unused)] -mod deprecated { - use {crate::graphic::HotGraphicPipeline, vk_graph::driver::graphic::GraphicPipeline}; - - impl HotGraphicPipeline { - #[deprecated = "use Deref instead"] - fn as_ref(&self) -> &GraphicPipeline { - self - } - } -} +pipeline!(Graphic); diff --git a/crates/vk-graph-hot/src/lib.rs b/crates/vk-graph-hot/src/lib.rs index 372c9802..391f9ffa 100644 --- a/crates/vk-graph-hot/src/lib.rs +++ b/crates/vk-graph-hot/src/lib.rs @@ -2,28 +2,24 @@ #![warn(missing_docs)] -pub mod compute; -pub mod graphic; -pub mod ray_trace; -pub mod shader; - -/// TODO -pub mod prelude { - pub use super::{ - compute::HotComputePipeline, - graphic::HotGraphicPipeline, - ray_trace::HotRayTracePipeline, - shader::{HotShader, HotShaderBuilder, OptimizationLevel, SourceLanguage, SpirvVersion}, - }; -} +mod compute; +mod graphic; +mod ray_trace; +mod shader; + +pub use self::{ + compute::HotComputePipeline, + graphic::HotGraphicPipeline, + ray_trace::HotRayTracePipeline, + shader::{HotShader, HotShaderBuilder}, +}; use { - self::shader::HotShader, log::{error, info}, - notify::{recommended_watcher, Event, EventKind, RecommendedWatcher}, + notify::{Event, EventKind, RecommendedWatcher, recommended_watcher}, shader_prepper::{ - process_file, BoxedIncludeProviderError, IncludeProvider, ResolvedInclude, - ResolvedIncludePath, + BoxedIncludeProviderError, IncludeProvider, ResolvedInclude, ResolvedIncludePath, + process_file, }, shaderc::{CompileOptions, Compiler, ShaderKind, SourceLanguage}, std::{ @@ -32,13 +28,13 @@ use { io::{Error, ErrorKind}, path::{Path, PathBuf}, sync::{ - atomic::{AtomicBool, Ordering}, Arc, OnceLock, + atomic::{AtomicBool, Ordering}, }, }, vk_graph::driver::{ - shader::{Shader, ShaderBuilder}, DriverError, + shader::{Shader, ShaderBuilder}, }, }; @@ -148,21 +144,29 @@ fn compile_shader_and_watch( Ok(base_shader) } -fn create_watcher() -> (RecommendedWatcher, Arc) { - let has_changes = Arc::new(AtomicBool::new(false)); - let has_changes_clone = Arc::clone(&has_changes); - let watcher = recommended_watcher(move |event: notify::Result| { +fn compile_shaders_and_watch( + shaders: &[HotShader], + watcher: &mut RecommendedWatcher, +) -> Result, DriverError> { + shaders + .iter() + .map(|shader| compile_shader_and_watch(shader, watcher)) + .collect() +} + +fn create_watcher(has_changes: &Arc) -> RecommendedWatcher { + let has_changes = Arc::clone(has_changes); + + recommended_watcher(move |event: notify::Result| { let event = event.unwrap_or_else(|_| Event::new(EventKind::Any)); if matches!( event.kind, EventKind::Any | EventKind::Modify(_) | EventKind::Other ) { - has_changes_clone.store(true, Ordering::Relaxed); + has_changes.store(true, Ordering::Relaxed); } }) - .unwrap(); - - (watcher, has_changes) + .unwrap() } fn guess_shader_kind(path: impl AsRef) -> ShaderKind { @@ -205,3 +209,110 @@ fn guess_shader_source_language(path: impl AsRef) -> Option None, } } + +macro_rules! pipeline { + ($name:ident) => { + ::paste::paste! { + impl [] { + fn cache(&self) -> ::std::sync::RwLockReadGuard<'_, HotPipeline<[<$name Pipeline>]>> { + self.cache + .read() + .unwrap() + } + + fn cache_mut(&self) -> ::std::sync::RwLockWriteGuard<'_, HotPipeline<[<$name Pipeline>]>> { + self.cache + .write() + .unwrap() + } + } + + impl [] { + /// Gets the debugging name assigned to this pipeline, if one has been set. + pub fn debug_name(&self) -> Option { + self.cache() + .pipeline + .debug_name() + .map(ToOwned::to_owned) + } + + /// The device which owns this pipeline. + pub fn device(&self) -> &Device { + &self.device + } + + /// Gets the information used to create this object. + pub fn info(&self) -> [<$name PipelineInfo>] { + self.cache() + .pipeline + .info() + } + + /// Sets the debugging name assigned to this pipeline. + /// + /// _Note:_ The pipeline name may only be assigned once. Subsequent calls will not update the + /// previously set name value. + pub fn set_debug_name(&mut self, name: impl Into) { + self.cache_mut() + .pipeline + .set_debug_name(name); + } + + /// Sets the debugging name assigned to this pipeline. + /// + /// _Note:_ The pipeline name may only be assigned once. Subsequent calls will not update the + /// previously set name value. + pub fn with_debug_name(mut self, name: impl Into) -> Self { + self.set_debug_name(name); + + self + } + } + + impl<'a> CommandPipeline<'a> for [] { + type Ref = <[<$name Pipeline>] as CommandPipeline<'a>>::Ref; + + fn bind_cmd(self, cmd: CommandRef<'a>) -> Self::Ref { + self.compile_shader_and_bind_cmd(cmd) + } + } + + impl<'a> CommandPipeline<'a> for &'a [] { + type Ref = <[<$name Pipeline>] as CommandPipeline<'a>>::Ref; + + fn bind_cmd(self, cmd: CommandRef<'a>) -> Self::Ref { + self.compile_shader_and_bind_cmd(cmd) + } + } + + } + }; +} + +use pipeline; + +// pipeline!(Graphic); +// pipeline!(RayTrace); + +macro_rules! pipeline_handle { + ($name:ident) => { + ::paste::paste! { + impl [] { + /// The native Vulkan pipeline handle of this pipeline. + pub fn handle(&self) -> ::vk_graph::driver::ash::vk::Pipeline { + self.cache() + .pipeline + .handle() + } + } + } + }; +} + +use pipeline_handle; + +#[derive(Debug)] +struct HotPipeline { + pipeline: T, + watcher: RecommendedWatcher, +} diff --git a/crates/vk-graph-hot/src/ray_trace.rs b/crates/vk-graph-hot/src/ray_trace.rs index b82264ff..7a4abad6 100644 --- a/crates/vk-graph-hot/src/ray_trace.rs +++ b/crates/vk-graph-hot/src/ray_trace.rs @@ -1,31 +1,33 @@ //! TODO use { - super::{compile_shader_and_watch, create_watcher, shader::HotShader}, + super::{ + HotPipeline, compile_shaders_and_watch, create_watcher, pipeline, pipeline_handle, + shader::HotShader, + }, log::info, - notify::RecommendedWatcher, - std::{ - ops::Deref, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, + std::sync::{ + Arc, RwLock, + atomic::{AtomicBool, Ordering}, }, - vk_graph::driver::{ - device::Device, - ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}, - DriverError, + vk_graph::{ + cmd::{CommandPipeline, CommandRef}, + driver::{ + DriverError, + device::Device, + ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}, + }, }, }; /// TODO #[derive(Debug)] pub struct HotRayTracePipeline { + cache: RwLock>, + device: Device, has_changes: Arc, - pipeline: RayTracePipeline, shader_groups: Box<[RayTraceShaderGroup]>, shaders: Box<[HotShader]>, - watcher: RecommendedWatcher, } impl HotRayTracePipeline { @@ -39,96 +41,52 @@ impl HotRayTracePipeline { where S: Into, { + let shaders = shaders.into_iter().map(Into::into).collect::>(); let shader_groups = shader_groups.into_iter().collect::>(); - let shaders = shaders - .into_iter() - .map(|shader| shader.into()) - .collect::>(); - let (mut watcher, has_changes) = create_watcher(); - let compiled_shaders = shaders - .iter() - .map(|shader| compile_shader_and_watch(shader, &mut watcher)) - .collect::, _>>()?; + let has_changes = Default::default(); + let mut watcher = create_watcher(&has_changes); let pipeline = RayTracePipeline::create( device, info, - compiled_shaders, + compile_shaders_and_watch(&shaders, &mut watcher)?, shader_groups.iter().copied(), )?; Ok(Self { + cache: RwLock::new(HotPipeline { pipeline, watcher }), + device: device.clone(), has_changes, - pipeline, shader_groups, shaders, - watcher, }) } - /// Returns the most recent compilation without checking for changes or re-compiling the shader - /// source code. - #[deprecated = "use Deref instead"] - #[doc(hidden)] - pub fn cold(&self) -> &RayTracePipeline { - self - } - - /// Returns the most recent compilation after checking for changes, and if needed re-compiling - /// the shader source code. - pub fn hot(&mut self) -> &RayTracePipeline { - let has_changes = self.has_changes.swap(false, Ordering::Relaxed); - - if has_changes { + fn compile_shader_and_bind_cmd<'a>( + &self, + cmd: CommandRef<'a>, + ) -> >::Ref { + if self.has_changes.swap(false, Ordering::Relaxed) { info!("Shader change detected"); - let (mut watcher, has_changes) = create_watcher(); - if let Ok(compiled_shaders) = self - .shaders - .iter() - .map(|shader| compile_shader_and_watch(shader, &mut watcher)) - .collect::, DriverError>>() - { - if let Ok(pipeline) = RayTracePipeline::create( - self.pipeline.device(), - self.pipeline.info(), - compiled_shaders, + let mut cache = self.cache_mut(); + + if let Ok(shaders) = compile_shaders_and_watch(&self.shaders, &mut cache.watcher) + && let Ok(pipeline) = RayTracePipeline::create( + &self.device, + cache.pipeline.info(), + shaders, self.shader_groups.iter().copied(), - ) { - self.pipeline = pipeline; - self.has_changes = has_changes; - self.watcher = watcher; - } + ) + { + cache.pipeline = pipeline; } } - self - } -} - -impl AsRef for HotRayTracePipeline { - fn as_ref(&self) -> &RayTracePipeline { - self - } -} - -impl Deref for HotRayTracePipeline { - type Target = RayTracePipeline; - - fn deref(&self) -> &Self::Target { - &self.pipeline + self.cache().pipeline.clone().bind_cmd(cmd) } } -#[allow(unused)] -mod deprecated { - use {crate::ray_trace::HotRayTracePipeline, vk_graph::driver::ray_trace::RayTracePipeline}; - - impl HotRayTracePipeline { - #[deprecated = "use Deref instead"] - fn as_ref(&self) -> &RayTracePipeline { - self - } - } -} +pipeline!(RayTrace); +pipeline_handle!(RayTrace); diff --git a/crates/vk-graph-hot/src/shader.rs b/crates/vk-graph-hot/src/shader.rs index e6ae67d8..a3412560 100644 --- a/crates/vk-graph-hot/src/shader.rs +++ b/crates/vk-graph-hot/src/shader.rs @@ -9,7 +9,7 @@ use { notify::{RecommendedWatcher, RecursiveMode, Watcher}, shaderc::{CompileOptions, EnvVersion, ShaderKind, TargetEnv}, std::path::{Path, PathBuf}, - vk_graph::driver::{ash::vk, shader::SpecializationMap, DriverError}, + vk_graph::driver::{DriverError, ash::vk, shader::SpecializationMap}, }; /// Describes a shader program which runs on some pipeline stage. @@ -28,7 +28,7 @@ pub struct HotShader { /// The name of the entry point which will be executed by this shader. /// /// The default value is `main`. - #[builder(default = "\"main\".to_owned()")] + #[builder(default = "\"main\".to_owned()", setter(into))] pub entry_name: String, /// Macro definitions. @@ -40,6 +40,7 @@ pub struct HotShader { pub optimization_level: Option, /// Shader source code path. + #[builder(setter(custom))] pub path: PathBuf, /// Sets the source language. @@ -314,6 +315,15 @@ impl HotShader { Ok(res.spirv_code) } + + /// Creates a shader using a shader kind inferred from the source code. + /// + /// # Panics + /// + /// If the shader code is invalid. + pub fn from_path(path: impl AsRef) -> HotShaderBuilder { + HotShaderBuilder::default().path(path) + } } impl From for HotShader { @@ -326,9 +336,7 @@ impl From for HotShader { impl HotShaderBuilder { /// Specifies a shader with the given `stage` and shader path values. pub fn new(stage: vk::ShaderStageFlags, path: impl AsRef) -> Self { - Self::default() - .stage(stage) - .path(path.as_ref().to_path_buf()) + Self::default().stage(stage).path(path) } /// Builds a new `HotShader`. @@ -361,4 +369,10 @@ impl HotShaderBuilder { self } + + /// Shader source code path. + pub fn path(mut self, path: impl AsRef) -> Self { + self.path = Some(path.as_ref().to_owned()); + self + } } diff --git a/crates/vk-graph-imgui/Cargo.toml b/crates/vk-graph-imgui/Cargo.toml index 1c578f04..12b23556 100644 --- a/crates/vk-graph-imgui/Cargo.toml +++ b/crates/vk-graph-imgui/Cargo.toml @@ -2,7 +2,7 @@ name = "vk-graph-imgui" version = "0.1.0" authors = ["John Wells "] -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" readme = "README.md" diff --git a/crates/vk-graph-imgui/src/lib.rs b/crates/vk-graph-imgui/src/lib.rs index 00c5ef85..d8032069 100644 --- a/crates/vk-graph-imgui/src/lib.rs +++ b/crates/vk-graph-imgui/src/lib.rs @@ -4,7 +4,7 @@ /// TODO pub mod prelude { - pub use super::{imgui, Condition, ImGui, Ui}; + pub use super::{Condition, ImGui, Ui, imgui}; } pub use imgui::{self, Condition, Ui}; diff --git a/crates/vk-graph-window/Cargo.toml b/crates/vk-graph-window/Cargo.toml index e9e4a2c1..0a1ee553 100644 --- a/crates/vk-graph-window/Cargo.toml +++ b/crates/vk-graph-window/Cargo.toml @@ -2,7 +2,7 @@ name = "vk-graph-window" version = "0.1.0" authors = ["John Wells "] -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" readme = "README.md" diff --git a/crates/vk-graph-window/examples/hello_world.rs b/crates/vk-graph-window/examples/hello_world.rs index 56604c2f..8950a147 100644 --- a/crates/vk-graph-window/examples/hello_world.rs +++ b/crates/vk-graph-window/examples/hello_world.rs @@ -7,6 +7,6 @@ fn main() -> Result<(), WindowError> { Window::new()?.run(|frame| { frame .graph - .clear_color_image(frame.swapchain_image, [100u8, 149, 237]); + .clear_color_image(frame.swapchain_image, [100u8, 149, 237, 255]); }) } diff --git a/crates/vk-graph-window/src/frame.rs b/crates/vk-graph-window/src/frame.rs index e56fa62a..28b53f37 100644 --- a/crates/vk-graph-window/src/frame.rs +++ b/crates/vk-graph-window/src/frame.rs @@ -1,5 +1,5 @@ use { - vk_graph::{driver::device::Device, node::SwapchainImageNode, Graph}, + vk_graph::{Graph, driver::device::Device, node::SwapchainImageNode}, winit::{dpi::PhysicalPosition, event::Event, window::Window}, }; diff --git a/crates/vk-graph-window/src/lib.rs b/crates/vk-graph-window/src/lib.rs index a7092d7c..c7511517 100644 --- a/crates/vk-graph-window/src/lib.rs +++ b/crates/vk-graph-window/src/lib.rs @@ -12,14 +12,14 @@ use { log::{error, info, trace, warn}, std::{error, fmt}, vk_graph::{ + Graph, driver::{ + DriverError, ash::vk, device::{Device, DeviceInfo}, surface::Surface, - DriverError, }, pool::hash::HashPool, - Graph, }, winit::{ application::ApplicationHandler, diff --git a/crates/vk-graph-window/src/swapchain.rs b/crates/vk-graph-window/src/swapchain.rs index b5f99cc5..be756a56 100644 --- a/crates/vk-graph-window/src/swapchain.rs +++ b/crates/vk-graph-window/src/swapchain.rs @@ -12,7 +12,9 @@ use { time::Instant, }, vk_graph::{ + Graph, driver::{ + DriverError, ash::{self, vk}, cmd_buf::{CommandBuffer, CommandBufferInfo}, descriptor_set::{DescriptorPool, DescriptorPoolInfo}, @@ -21,12 +23,10 @@ use { render_pass::{RenderPass, RenderPassInfo}, surface::Surface, swapchain::{self, SwapchainImage}, - sync::{cmd::pipeline_barrier, AccessType, ImageBarrier, ImageLayout}, - DriverError, + sync::{AccessType, ImageBarrier, ImageLayout, cmd::pipeline_barrier}, }, node::SwapchainImageNode, pool::Pool, - Graph, }, }; diff --git a/examples/shader-toy/Cargo.toml b/examples/shader-toy/Cargo.toml index 5c644076..6fac6ec1 100644 --- a/examples/shader-toy/Cargo.toml +++ b/examples/shader-toy/Cargo.toml @@ -2,7 +2,7 @@ name = "shader-toy" version = "0.1.0" authors = ["John Wells "] -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" readme = "README.md" description = "Example api usage" diff --git a/examples/shader-toy/build.rs b/examples/shader-toy/build.rs index 91813984..80a1c620 100644 --- a/examples/shader-toy/build.rs +++ b/examples/shader-toy/build.rs @@ -169,8 +169,8 @@ fn remove_common_path( fn read_shader_source(path: impl AsRef) -> String { use { shader_prepper::{ - process_file, BoxedIncludeProviderError, IncludeProvider, ResolvedInclude, - ResolvedIncludePath, + BoxedIncludeProviderError, IncludeProvider, ResolvedInclude, ResolvedIncludePath, + process_file, }, std::fs::read_to_string, }; diff --git a/examples/shader-toy/src/main.rs b/examples/shader-toy/src/main.rs index d0456ba3..b0efc3e0 100644 --- a/examples/shader-toy/src/main.rs +++ b/examples/shader-toy/src/main.rs @@ -34,11 +34,12 @@ mod res { use { anyhow::Context, - bytemuck::{bytes_of, Pod, Zeroable}, + bytemuck::{Pod, Zeroable, bytes_of}, clap::Parser, pak::{Pak, PakBuf}, std::time::Instant, vk_graph::{ + Graph, cmd::{LoadOp, StoreOp}, driver::{ ash::vk, @@ -47,8 +48,7 @@ use { shader::Shader, sync::AccessType, }, - pool::{lazy::LazyPool, Pool as _}, - Graph, + pool::{Pool as _, lazy::LazyPool}, }, vk_graph_fx::*, vk_graph_window::WindowBuilder, diff --git a/examples/skeletal-anim/Cargo.toml b/examples/skeletal-anim/Cargo.toml index cf1af3d8..9929d3c7 100644 --- a/examples/skeletal-anim/Cargo.toml +++ b/examples/skeletal-anim/Cargo.toml @@ -2,7 +2,7 @@ name = "animation" version = "0.1.0" authors = ["John Wells "] -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" readme = "README.md" diff --git a/examples/skeletal-anim/build.rs b/examples/skeletal-anim/build.rs index dae28c07..30f49e76 100644 --- a/examples/skeletal-anim/build.rs +++ b/examples/skeletal-anim/build.rs @@ -5,7 +5,7 @@ use { simplelog::{CombinedLogger, ConfigBuilder, LevelFilter, WriteLogger}, std::{ env::var, - fs::{read_to_string, write, File}, + fs::{File, read_to_string, write}, path::{Path, PathBuf}, }, }; diff --git a/examples/skeletal-anim/src/main.rs b/examples/skeletal-anim/src/main.rs index c1b316ea..a74475d5 100644 --- a/examples/skeletal-anim/src/main.rs +++ b/examples/skeletal-anim/src/main.rs @@ -1,12 +1,12 @@ use { - bytemuck::{bytes_of, cast_slice, NoUninit}, + bytemuck::{NoUninit, bytes_of, cast_slice}, clap::Parser, glam::{Mat4, Quat, Vec3}, pak::{ + Pak, PakBuf, anim::{Channel, Interpolation, Outputs}, bitmap::BitmapFormat, model::{Joint, Vertex}, - Pak, PakBuf, }, std::{ cmp::Ordering, @@ -17,8 +17,10 @@ use { time::{Duration, Instant}, }, vk_graph::{ + Graph, cmd::{LoadOp, StoreOp}, driver::{ + DriverError, ash::vk, buffer::{Buffer, BufferInfo}, device::Device, @@ -26,10 +28,8 @@ use { image::{Image, ImageInfo}, shader::Shader, sync::AccessType, - DriverError, }, - pool::{hash::HashPool, lazy::LazyPool, Pool as _}, - Graph, + pool::{Pool as _, hash::HashPool, lazy::LazyPool}, }, vk_graph_window::{WindowBuilder, WindowError}, }; diff --git a/examples/vr/Cargo.toml b/examples/vr/Cargo.toml index bdef166f..0e32fc16 100644 --- a/examples/vr/Cargo.toml +++ b/examples/vr/Cargo.toml @@ -2,7 +2,7 @@ name = "vr" version = "0.1.0" authors = ["John Wells "] -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" readme = "README.md" diff --git a/examples/vr/src/main.rs b/examples/vr/src/main.rs index 5cd4d195..9849df0f 100644 --- a/examples/vr/src/main.rs +++ b/examples/vr/src/main.rs @@ -2,26 +2,27 @@ mod driver; use { self::driver::{Swapchain, XrInstance}, - bytemuck::{bytes_of, cast_slice, Pod, Zeroable}, - glam::{vec3, vec4, Mat3, Mat4, Quat, Vec2, Vec3}, + bytemuck::{Pod, Zeroable, bytes_of, cast_slice}, + glam::{Mat3, Mat4, Quat, Vec2, Vec3, vec3, vec4}, log::{debug, error, trace}, meshopt::{generate_vertex_remap, remap_index_buffer, remap_vertex_buffer}, openxr::{self as xr, EnvironmentBlendMode, ViewConfigurationType}, std::{ - fs::{metadata, File}, + fs::{File, metadata}, io::BufReader, iter::repeat_with, path::{Path, PathBuf}, ptr::copy_nonoverlapping, sync::{ - atomic::{AtomicBool, Ordering}, Arc, + atomic::{AtomicBool, Ordering}, }, thread::sleep, time::Duration, }, - tobj::{load_obj, GPU_LOAD_OPTIONS}, + tobj::{GPU_LOAD_OPTIONS, load_obj}, vk_graph::{ + Graph, cmd::{LoadOp, StoreOp}, driver::{ ash::vk::{self}, @@ -31,10 +32,9 @@ use { image::{Image, ImageInfo}, sync::AccessType, }, - pool::{lazy::LazyPool, Pool as _}, - Graph, + pool::{Pool as _, lazy::LazyPool}, }, - vk_graph_hot::{graphic::HotGraphicPipeline, shader::HotShader}, + vk_graph_hot::{HotGraphicPipeline, HotShader}, }; // Sets bits with index 0 and 1 for stereoscopic rendering @@ -124,7 +124,7 @@ fn main() -> anyhow::Result<()> { let res_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("res"); - let mut hands_pipeline = HotGraphicPipeline::create( + let hands_pipeline = HotGraphicPipeline::create( device, GraphicPipelineInfo::default(), [ @@ -132,7 +132,7 @@ fn main() -> anyhow::Result<()> { HotShader::new_fragment(res_dir.join("hands.frag")), ], )?; - let mut mammoth_pipeline = HotGraphicPipeline::create( + let mammoth_pipeline = HotGraphicPipeline::create( device, GraphicPipelineInfo::default(), [ @@ -358,7 +358,7 @@ fn main() -> anyhow::Result<()> { graph .begin_cmd() .debug_name("Left hand") - .bind_pipeline(hands_pipeline.hot()) + .bind_pipeline(&hands_pipeline) .depth_stencil(DepthStencilInfo::DEPTH_WRITE_LESS_IGNORE_STENCIL) .multiview(VIEW_MASK, VIEW_MASK) .resource_access(index_buf, AccessType::IndexBuffer) @@ -407,7 +407,7 @@ fn main() -> anyhow::Result<()> { graph .begin_cmd() .debug_name("Right hand") - .bind_pipeline(hands_pipeline.hot()) + .bind_pipeline(&hands_pipeline) .depth_stencil(DepthStencilInfo::DEPTH_WRITE_LESS_IGNORE_STENCIL) .multiview(VIEW_MASK, VIEW_MASK) .resource_access(index_buf, AccessType::IndexBuffer) @@ -454,7 +454,7 @@ fn main() -> anyhow::Result<()> { graph .begin_cmd() .debug_name("Woolly Mammoth") - .bind_pipeline(mammoth_pipeline.hot()) + .bind_pipeline(&mammoth_pipeline) .resource_access(index_buf, AccessType::IndexBuffer) .resource_access(vertex_buf, AccessType::VertexBuffer) .shader_resource_access(0, camera_buf, AccessType::VertexShaderReadUniformBuffer) diff --git a/src/cmd/compute.rs b/src/cmd/compute.rs index a045c368..85c67ac7 100644 --- a/src/cmd/compute.rs +++ b/src/cmd/compute.rs @@ -428,11 +428,7 @@ mod deprecated { #[deprecated = "use shader_resource_access function with AccessType::ComputeShaderWrite"] #[doc(hidden)] - pub fn shader_resource_accesswrite_descriptor( - self, - descriptor: impl Into, - node: N, - ) -> Self + pub fn write_descriptor(self, descriptor: impl Into, node: N) -> Self where N: Node + View, N::Info: Copy, From 0b10a0a13c3ae3051ff8dcca44cf77a53d2583cb Mon Sep 17 00:00:00 2001 From: John Wells Date: Sat, 14 Mar 2026 14:29:38 -0400 Subject: [PATCH 43/86] clean-up --- crates/vk-graph-window/src/lib.rs | 64 ++++++++++++++++++++++--- crates/vk-graph-window/src/swapchain.rs | 16 ++++++- examples/egui.rs | 2 +- src/driver/device.rs | 2 +- src/driver/physical_device.rs | 13 ++--- src/driver/surface.rs | 23 ++++----- src/driver/swapchain.rs | 14 +++--- 7 files changed, 96 insertions(+), 38 deletions(-) diff --git a/crates/vk-graph-window/src/lib.rs b/crates/vk-graph-window/src/lib.rs index c7511517..17adc7d2 100644 --- a/crates/vk-graph-window/src/lib.rs +++ b/crates/vk-graph-window/src/lib.rs @@ -10,7 +10,7 @@ pub use self::frame::FrameContext; use { self::swapchain::{Swapchain, SwapchainError, SwapchainInfo}, log::{error, info, trace, warn}, - std::{error, fmt}, + std::{error, fmt, ops::Deref}, vk_graph::{ Graph, driver::{ @@ -21,6 +21,7 @@ use { }, pool::hash::HashPool, }, + winit::raw_window_handle::{DisplayHandle, HandleError, HasDisplayHandle}, winit::{ application::ApplicationHandler, error::EventLoopError, @@ -41,12 +42,30 @@ pub enum FullscreenMode { Exclusive, } +#[doc(hidden)] +#[repr(C)] +pub struct ReadOnlyWindow { + data: WindowData, + pub device: Device, + event_loop: EventLoop<()>, +} + +impl Deref for ReadOnlyWindow { + type Target = EventLoop<()>; + + fn deref(&self) -> &Self::Target { + &self.event_loop + } +} + /// TODO -// #[derive(Debug)] +#[repr(C)] pub struct Window { data: WindowData, - /// TODO + /// A device which is compatible with this window. + /// + /// _Note:_ This field is read-only. pub device: Device, event_loop: EventLoop<()>, @@ -401,9 +420,18 @@ impl Window { } } -impl AsRef> for Window { - fn as_ref(&self) -> &EventLoop<()> { - &self.event_loop +#[doc(hidden)] +impl Deref for Window { + type Target = ReadOnlyWindow; + + fn deref(&self) -> &Self::Target { + unsafe { &*(self as *const Self as *const Self::Target) } + } +} + +impl HasDisplayHandle for Window { + fn display_handle(&self) -> Result, HandleError> { + self.event_loop.display_handle() } } @@ -608,3 +636,27 @@ impl From for WindowError { Self::EventLoop(err) } } + +#[cfg(test)] +mod test { + use { + super::*, + std::mem::{offset_of, size_of}, + }; + + #[test] + pub fn window_repr_c() { + // HACK: The readonly crate uses a private implementation and so we can't further deref it + // into the native object type. Because of this the ReadOnly part is manually implemented. + assert_eq!(size_of::(), size_of::()); + assert_eq!(offset_of!(Window, data), offset_of!(ReadOnlyWindow, data)); + assert_eq!( + offset_of!(Window, device), + offset_of!(ReadOnlyWindow, device) + ); + assert_eq!( + offset_of!(Window, event_loop), + offset_of!(ReadOnlyWindow, event_loop) + ); + } +} diff --git a/crates/vk-graph-window/src/swapchain.rs b/crates/vk-graph-window/src/swapchain.rs index be756a56..d2bed1a8 100644 --- a/crates/vk-graph-window/src/swapchain.rs +++ b/crates/vk-graph-window/src/swapchain.rs @@ -54,6 +54,7 @@ const fn image_access_layout(access: AccessType) -> ImageLayout { pub struct ReadOnlySwapchain { exec_idx: usize, execs: Box<[Execution]>, + image_execs: Vec, pub info: SwapchainInfo, pub swapchain: swapchain::Swapchain, } @@ -95,7 +96,7 @@ pub struct Swapchain { impl Swapchain { /// Constructs a new `Swapchain` object. pub fn new(surface: Surface, info: impl Into) -> Result { - let info: SwapchainInfo = info.into(); + let info = info.into(); assert_ne!(info.command_buffer_count, 0); @@ -170,7 +171,7 @@ impl Swapchain { Ok(swapchain_image) => Ok(swapchain_image), }?; - while swapchain_image.idx < self.image_execs.len() as u32 { + while self.image_execs.len() >= swapchain_image.idx as _ { self.image_execs.push(0); } @@ -333,7 +334,14 @@ impl Swapchain { /// /// Previously acquired swapchain images should be discarded after calling this function. pub fn set_info(&mut self, info: impl Into) { + let info = info.into(); + self.swapchain.set_info(info); + self.info.height = info.height; + self.info.min_image_count = info.min_image_count; + self.info.present_mode = info.present_mode; + self.info.surface = info.surface; + self.info.width = info.width; } } @@ -680,6 +688,10 @@ mod test { offset_of!(Swapchain, execs), offset_of!(ReadOnlySwapchain, execs), ); + assert_eq!( + offset_of!(Swapchain, image_execs), + offset_of!(ReadOnlySwapchain, image_execs), + ); assert_eq!( offset_of!(Swapchain, info), offset_of!(ReadOnlySwapchain, info), diff --git a/examples/egui.rs b/examples/egui.rs index 622397c8..e8c7c2db 100644 --- a/examples/egui.rs +++ b/examples/egui.rs @@ -15,7 +15,7 @@ fn main() -> anyhow::Result<()> { .v_sync(false) .window(|window| window.with_inner_size(LogicalSize::new(1024, 768))) .build()?; - let mut egui = Egui::new(&window.device, window.as_ref()); + let mut egui = Egui::new(&window.device, &window); let mut cache = LazyPool::new(&window.device); diff --git a/src/driver/device.rs b/src/driver/device.rs index 41ba0e4f..f0e4e79d 100644 --- a/src/driver/device.rs +++ b/src/driver/device.rs @@ -245,7 +245,7 @@ impl Device { queues.push(queue_family.into_boxed_slice()); } - let surface_ext = physical_device.surface_ext.then(|| { + let surface_ext = physical_device.swapchain_ext.then(|| { khr::surface::Instance::new(&physical_device.instance.entry, &physical_device.instance) }); let swapchain_ext = physical_device diff --git a/src/driver/physical_device.rs b/src/driver/physical_device.rs index b2ec1935..77c335b6 100644 --- a/src/driver/physical_device.rs +++ b/src/driver/physical_device.rs @@ -233,12 +233,7 @@ pub struct PhysicalDevice { /// _Note:_ This field is read-only. pub sampler_filter_minmax_properties: SamplerFilterMinmaxProperties, - /// True if the device may be used for windowed or full-screen display. - /// - /// _Note:_ This field is read-only. - pub surface_ext: bool, - - /// True if the device may be used for windowed or full-screen display. + /// True if the device supports swapchain use. /// /// _Note:_ This field is read-only. pub swapchain_ext: bool, @@ -348,8 +343,8 @@ impl PhysicalDevice { // Check for supported extensions let extensions = extensions .iter() - .map(|property: &vk::ExtensionProperties| property.extension_name.as_ptr()) - .filter(|&extension_name| !extension_name.is_null()) + .map(|property| property.extension_name.as_ptr()) + .filter(|extension_name| !extension_name.is_null()) .map(|extension_name| unsafe { CStr::from_ptr(extension_name) }) .collect::>(); let supports_accel_struct = extensions.contains(khr::acceleration_structure::NAME) @@ -357,7 +352,6 @@ impl PhysicalDevice { let supports_index_type_uint8 = extensions.contains(ext::index_type_uint8::NAME); let supports_ray_query = extensions.contains(khr::ray_query::NAME); let supports_ray_trace = extensions.contains(khr::ray_tracing_pipeline::NAME); - let surface_ext = extensions.contains(khr::surface::NAME); let swapchain_ext = extensions.contains(khr::swapchain::NAME); // Gather optional features and properties of the physical device @@ -398,7 +392,6 @@ impl PhysicalDevice { ray_trace_features, ray_trace_properties, sampler_filter_minmax_properties, - surface_ext, swapchain_ext, }) } diff --git a/src/driver/surface.rs b/src/driver/surface.rs index 6a1e82b7..c33a1256 100644 --- a/src/driver/surface.rs +++ b/src/driver/surface.rs @@ -74,7 +74,7 @@ impl Surface { ) } .map_err(|err| { - warn!("Unable to create surface: {err}"); + warn!("unable to create surface: {err}"); DriverError::Unsupported })?; @@ -85,18 +85,19 @@ impl Surface { /// Lists the supported surface formats. #[profiling::function] pub fn formats(&self) -> Result, DriverError> { - unsafe { - Device::expect_surface_ext(&self.device) - .get_physical_device_surface_formats( - self.device.physical_device.handle, - self.handle, - ) - .map_err(|err| { - warn!("Unable to get surface formats: {err}"); + let surface_ext = Device::expect_surface_ext(&self.device); - DriverError::Unsupported - }) + unsafe { + surface_ext.get_physical_device_surface_formats( + self.device.physical_device.handle, + self.handle, + ) } + .map_err(|err| { + warn!("unable to get surface formats: {err}"); + + DriverError::Unsupported + }) } /// Helper function to automatically select the best UNORM format, if one is available. diff --git a/src/driver/swapchain.rs b/src/driver/swapchain.rs index a47029a3..cf59bbda 100644 --- a/src/driver/swapchain.rs +++ b/src/driver/swapchain.rs @@ -81,7 +81,7 @@ impl Swapchain { ) -> Result { for _ in 0..2 { if self.suboptimal { - self.recreate_swapchain().map_err(|err| { + self.recreate().map_err(|err| { if matches!(err, DriverError::Unsupported) { SwapchainError::Suboptimal } else { @@ -174,7 +174,7 @@ impl Swapchain { } #[profiling::function] - fn destroy_swapchain(device: &Device, swapchain: &mut vk::SwapchainKHR) { + fn destroy(device: &Device, swapchain: &mut vk::SwapchainKHR) { if *swapchain != vk::SwapchainKHR::null() { // wait for device to be finished with swapchain before destroying it. // This avoid crashes when resizing windows @@ -216,7 +216,7 @@ impl Swapchain { &present_info, ) { Ok(_) => { - Self::destroy_swapchain(&self.surface.device, &mut self.handle_prev); + Self::destroy(&self.surface.device, &mut self.handle_prev); } Err(err) if err == vk::Result::ERROR_DEVICE_LOST @@ -242,8 +242,8 @@ impl Swapchain { } #[profiling::function] - fn recreate_swapchain(&mut self) -> Result<(), DriverError> { - Self::destroy_swapchain(&self.surface.device, &mut self.handle_prev); + fn recreate(&mut self) -> Result<(), DriverError> { + Self::destroy(&self.surface.device, &mut self.handle_prev); let surface_caps = Surface::capabilities(&self.surface)?; @@ -432,8 +432,8 @@ impl Drop for Swapchain { return; } - Self::destroy_swapchain(&self.surface.device, &mut self.handle_prev); - Self::destroy_swapchain(&self.surface.device, &mut self.handle); + Self::destroy(&self.surface.device, &mut self.handle_prev); + Self::destroy(&self.surface.device, &mut self.handle); } } From a3aac1aee6ffc210f45cec4328e60493d8d6ff30 Mon Sep 17 00:00:00 2001 From: John Wells Date: Wed, 18 Mar 2026 05:56:38 -0400 Subject: [PATCH 44/86] make repo into workspace --- Cargo.toml | 57 +++++++++--- README.md | 2 +- bin/check | 5 - crates/vk-graph-egui/Cargo.toml | 11 ++- crates/vk-graph-egui/src/lib.rs | 26 ++++-- crates/vk-graph-fx/Cargo.toml | 16 ++-- crates/vk-graph-fx/src/bitmap_font.rs | 18 +++- crates/vk-graph-fx/src/image_loader.rs | 22 ++++- crates/vk-graph-fx/src/lib.rs | 8 -- crates/vk-graph-fx/src/presenter.rs | 14 ++- crates/vk-graph-fx/src/transition.rs | 21 ++++- crates/vk-graph-hot/Cargo.toml | 16 ++-- crates/vk-graph-hot/examples/glsl.rs | 2 +- crates/vk-graph-hot/examples/hlsl.rs | 5 +- crates/vk-graph-imgui/Cargo.toml | 9 +- crates/vk-graph-imgui/src/lib.rs | 18 +++- crates/vk-graph-prelude/Cargo.toml | 10 -- crates/vk-graph-prelude/README.md | 3 - crates/vk-graph-prelude/src/lib.rs | 68 -------------- crates/vk-graph-window/Cargo.toml | 15 +-- crates/vk-graph-window/src/lib.rs | 8 +- crates/vk-graph-window/src/swapchain.rs | 10 +- examples/aliasing.rs | 21 ++++- examples/bindless.rs | 118 +++++++++++++----------- examples/cpu_readback.rs | 17 +++- examples/debugger.rs | 10 +- examples/egui.rs | 11 ++- examples/font_bmp.rs | 16 +++- examples/fuzzer.rs | 31 +++++-- examples/image_sampler.rs | 25 +++-- examples/imgui.rs | 10 +- examples/min_max.rs | 18 +++- examples/mip_compute.rs | 24 ++++- examples/mip_graphic.rs | 21 ++++- examples/msaa.rs | 35 ++++--- examples/multipass.rs | 22 ++++- examples/multithread.rs | 18 ++-- examples/ray_omni.rs | 27 +++++- examples/ray_trace.rs | 53 +++++++---- examples/rt_triangle.rs | 31 +++++-- examples/shader-toy/Cargo.toml | 21 +++-- examples/shader-toy/src/main.rs | 25 +---- examples/skeletal-anim/Cargo.toml | 19 ++-- examples/skeletal-anim/src/main.rs | 12 +-- examples/subgroup_ops.rs | 19 +++- examples/transitions.rs | 4 +- examples/triangle.rs | 15 ++- examples/vertex_layout.rs | 19 +++- examples/vr/Cargo.toml | 19 ++-- examples/vsm_omni.rs | 26 ++++-- 50 files changed, 663 insertions(+), 388 deletions(-) delete mode 100644 crates/vk-graph-prelude/Cargo.toml delete mode 100644 crates/vk-graph-prelude/README.md delete mode 100644 crates/vk-graph-prelude/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 30476153..4f228b57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,43 @@ +[workspace] +resolver = "3" +members = [ + "crates/vk-graph-egui", + "crates/vk-graph-fx", + "crates/vk-graph-hot", + "crates/vk-graph-imgui", + "crates/vk-graph-window", + "examples/shader-toy", + "examples/skeletal-anim", + "examples/vr", + "guide/guide-helper" +] + +[workspace.package] +rust-version = "1.94" +edition = "2024" +license = "MIT OR Apache-2.0" +repository = "https://github.com/attackgoat/vk-graph" +readme = "README.md" + +[workspace.dependencies] +log = "0.4" +profiling = "1.0" +vk-graph.path = "" +vk-graph-egui.path = "crates/vk-graph-egui" +vk-graph-fx.path = "crates/vk-graph-fx" +vk-graph-hot.path = "crates/vk-graph-hot" +vk-graph-imgui.path = "crates/vk-graph-imgui" +vk-graph-window.path = "crates/vk-graph-window" +winit = "0.30" + [package] name = "vk-graph" version = "0.14.0+alpha" authors = ["John Wells "] -edition = "2024" -license = "MIT OR Apache-2.0" +edition.workspace = true +license.workspace = true +repository.workspace = true readme = "README.md" -repository = "https://github.com/attackgoat/vk-graph" homepage = "https://github.com/attackgoat/vk-graph" documentation = "https://docs.rs/vk-graph" keywords = ["gamedev", "vulkan"] @@ -24,11 +56,11 @@ ash = "0.38.0+1.3.281" ash-window = "0.13" derive_builder = "0.20" gpu-allocator = "0.28" -log = "0.4" +log.workspace = true ordered-float = "5.1" parking_lot = { version = "0.12", optional = true } paste = "1.0" -profiling = "1.0" +profiling.workspace = true raw-window-handle = "0.6" readonly = "0.2" spirq = "1.2" @@ -40,9 +72,9 @@ ash-molten = "0.20" [dev-dependencies] anyhow = "1.0" bmfont = { version = "0.3", default-features = false } -bytemuck = "1.22" +bytemuck = "1.25" clap = { version = "4.5", features = ["derive"] } -glam = { version = "0.30", features = ["bytemuck"] } +glam = { version = "0.32", features = ["bytemuck"] } half = { version = "2.4", features = ["bytemuck"] } hassle-rs = "0.11" image = "0.25" @@ -54,12 +86,11 @@ puffin = "0.19" puffin_http = "0.16" rand = "0.9" reqwest = { version = "0.12", features = ["blocking"] } -vk-graph-fx = { path = "crates/vk-graph-fx" } -vk-graph-imgui = { path = "crates/vk-graph-imgui" } -vk-graph-egui = { path = "crates/vk-graph-egui" } -vk-graph-prelude = { path = "crates/vk-graph-prelude" } -vk-graph-window = { path = "crates/vk-graph-window" } +vk-graph-fx.workspace = true +vk-graph-imgui.workspace = true +vk-graph-egui.workspace = true +vk-graph-window.workspace = true vk-shader-macros = "0.2" tobj = "4.0" -winit = "0.30" +winit.workspace = true winit_input_helper = { git = "https://github.com/stefnotch/winit_input_helper.git", rev = "6e76a79d01ce836c01b9cdeaa98846a6f0955dc4" } #"0.16" diff --git a/README.md b/README.md index 8a036a3c..32134d2c 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ typed access to all the resources used while rendering. The `Graph` structure al smart pointer resources to be bound as "nodes" which may be used anywhere in a graph. The graph itself is not tied to swapchain access and may be used to execute general command streams. -Features of the render graph: +Features of the graph: - Compute, graphic, and ray-trace pipelines - Automatic Vulkan management (render passes, subpasses, descriptors, pools, _etc._) diff --git a/bin/check b/bin/check index cd8968fb..deceb22d 100755 --- a/bin/check +++ b/bin/check @@ -20,7 +20,6 @@ cargo fmt --manifest-path crates/vk-graph-egui/Cargo.toml && diff || fail "Unfor cargo fmt --manifest-path crates/vk-graph-fx/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-fx)" cargo fmt --manifest-path crates/vk-graph-hot/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-hot)" cargo fmt --manifest-path crates/vk-graph-imgui/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-imgui)" -cargo fmt --manifest-path crates/vk-graph-prelude/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-prelude)" cargo fmt --manifest-path crates/vk-graph-window/Cargo.toml && diff || fail "Unformatted rust code (vk-graph-window)" cargo fmt --manifest-path examples/shader-toy/Cargo.toml && diff || fail "Unformatted rust code (shader-toy)" cargo fmt --manifest-path examples/skeletal-anim/Cargo.toml && diff || fail "Unformatted rust code (skeletal-anim)" @@ -51,10 +50,6 @@ echo "Checking crates/vk-graph-hot" \ # && cargo check --manifest-path crates/vk-graph-imgui/Cargo.toml || fail \ # && cargo clippy --manifest-path crates/vk-graph-imgui/Cargo.toml || fail -echo "Checking crates/vk-graph-prelude" \ - && cargo check --manifest-path crates/vk-graph-prelude/Cargo.toml || fail \ - && cargo clippy --manifest-path crates/vk-graph-prelude/Cargo.toml || fail - echo "Checking crates/vk-graph-window" \ && cargo check --manifest-path crates/vk-graph-window/Cargo.toml || fail \ && cargo check --manifest-path crates/vk-graph-window/Cargo.toml --examples || fail \ diff --git a/crates/vk-graph-egui/Cargo.toml b/crates/vk-graph-egui/Cargo.toml index 8d2aea70..ed9ca0dd 100644 --- a/crates/vk-graph-egui/Cargo.toml +++ b/crates/vk-graph-egui/Cargo.toml @@ -2,15 +2,16 @@ name = "vk-graph-egui" version = "0.1.0" authors = ["Christian Döring "] -edition = "2024" -license = "MIT OR Apache-2.0" -readme = "README.md" +edition.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true [dependencies] bytemuck = "1.25" # TODO: Waiting for egui to update winit version egui = { git = "https://github.com/emilk/egui.git", rev = "3777b8d2741f298eaa1409dc08062902f7541990" } #{ version = "0.28", features = ["bytemuck"] } egui-winit = { git = "https://github.com/emilk/egui.git", rev = "3777b8d2741f298eaa1409dc08062902f7541990" } #"0.28" -vk-graph-prelude = { path = "../vk-graph-prelude" } -vk-graph-fx = { path = "../vk-graph-fx" } +vk-graph.workspace = true +vk-graph-fx.workspace = true vk-shader-macros = "0.2" diff --git a/crates/vk-graph-egui/src/lib.rs b/crates/vk-graph-egui/src/lib.rs index 0c2f60a0..2bce594b 100644 --- a/crates/vk-graph-egui/src/lib.rs +++ b/crates/vk-graph-egui/src/lib.rs @@ -2,19 +2,27 @@ #![warn(missing_docs)] -/// TODO -pub mod prelude { - pub use super::{Egui, egui}; -} - pub use egui; -use egui_winit::winit::raw_window_handle::HasDisplayHandle; +use vk_graph::{ + cmd::{LoadOp, StoreOp}, + driver::{ + ash::vk, + buffer::BufferInfo, + device::Device, + graphic::{BlendInfo, GraphicPipeline, GraphicPipelineInfo}, + image::{Image, ImageInfo}, + shader::Shader, + sync::AccessType, + }, + node::AnyImageNode, + pool::{Lease, Pool as _, hash::HashPool}, +}; use { bytemuck::cast_slice, - egui_winit::winit::{event::Event, window::Window}, + egui_winit::winit::{event::Event, raw_window_handle::HasDisplayHandle, window::Window}, std::{borrow::Cow, collections::HashMap, sync::Arc}, - vk_graph_prelude::*, + vk_graph::Graph, vk_shader_macros::include_glsl, }; @@ -36,7 +44,7 @@ impl Egui { pub fn new(device: &Device, display_target: &dyn HasDisplayHandle) -> Self { let ppl = GraphicPipeline::create( device, - GraphicPipelineInfoBuilder::default() + GraphicPipelineInfo::builder() .blend(BlendInfo { blend_enable: true, src_color_blend_factor: vk::BlendFactor::ONE, diff --git a/crates/vk-graph-fx/Cargo.toml b/crates/vk-graph-fx/Cargo.toml index 8f9c89e7..47376080 100644 --- a/crates/vk-graph-fx/Cargo.toml +++ b/crates/vk-graph-fx/Cargo.toml @@ -2,15 +2,15 @@ name = "vk-graph-fx" version = "0.1.0" authors = ["John Wells "] -edition = "2024" -license = "MIT OR Apache-2.0" -readme = "README.md" -repository = "https://github.com/attackgoat/vk-graph" -homepage = "https://github.com/attackgoat/vk-graph/crates/vk-graph-fx" documentation = "https://docs.rs/vk-graph" +homepage = "https://github.com/attackgoat/vk-graph/crates/vk-graph-fx" keywords = ["gamedev", "vulkan"] categories = ["game-development", "multimedia::images", "rendering::engine"] description = "A bunch of pre-built utilities that are helpful when using vk-graph" +edition.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true [features] default = [ @@ -24,10 +24,10 @@ matte-modes = [] [dependencies] bmfont = { version = "0.3", default-features = false } -bytemuck = "1.14" +bytemuck = "1.25" parking_lot = "0.12" -log = "0.4" +log.workspace = true anyhow = "1.0" glam = "0.27" -vk-graph-prelude = { path = "../vk-graph-prelude" } +vk-graph.workspace = true vk-shader-macros = "0.2" diff --git a/crates/vk-graph-fx/src/bitmap_font.rs b/crates/vk-graph-fx/src/bitmap_font.rs index 34fd3ecf..cfe139ef 100644 --- a/crates/vk-graph-fx/src/bitmap_font.rs +++ b/crates/vk-graph-fx/src/bitmap_font.rs @@ -4,7 +4,21 @@ use { bytemuck::{cast, cast_slice}, glam::{Mat4, vec3}, std::sync::Arc, - vk_graph_prelude::*, + vk_graph::{ + Graph, + cmd::{LoadOp, StoreOp}, + driver::{ + ash::vk, + buffer::{Buffer, BufferInfo}, + device::Device, + graphic::{BlendInfo, GraphicPipeline, GraphicPipelineInfo}, + image::Image, + shader::{Shader, SpecializationMap}, + sync::AccessType, + }, + node::{AnyImageNode, ImageNode}, + pool::{Pool as _, lazy::LazyPool}, + }, vk_shader_macros::include_glsl, }; @@ -40,7 +54,7 @@ impl BitmapFont { let num_pages = pages.len() as u32; let pipeline = GraphicPipeline::create( device, - GraphicPipelineInfoBuilder::default().blend(BlendInfo::ALPHA), + GraphicPipelineInfo::builder().blend(BlendInfo::ALPHA), [ Shader::new_vertex(include_glsl!("res/shader/graphic/font.vert").as_slice()), Shader::new_fragment(include_glsl!("res/shader/graphic/font.frag").as_slice()) diff --git a/crates/vk-graph-fx/src/image_loader.rs b/crates/vk-graph-fx/src/image_loader.rs index 01156c15..07f657c9 100644 --- a/crates/vk-graph-fx/src/image_loader.rs +++ b/crates/vk-graph-fx/src/image_loader.rs @@ -1,6 +1,24 @@ use { - super::BitmapFont, anyhow::Context, bmfont::BMFont, log::info, std::sync::Arc, - vk_graph_prelude::*, vk_shader_macros::include_glsl, + super::BitmapFont, + anyhow::Context, + bmfont::BMFont, + log::info, + std::sync::Arc, + vk_graph::{ + Graph, + driver::{ + DriverError, + ash::vk, + buffer::{Buffer, BufferInfo}, + compute::{ComputePipeline, ComputePipelineInfo}, + device::Device, + image::{Image, ImageInfo}, + shader::Shader, + sync::AccessType, + }, + pool::{Pool as _, hash::HashPool}, + }, + vk_shader_macros::include_glsl, }; #[cfg(debug_assertions)] diff --git a/crates/vk-graph-fx/src/lib.rs b/crates/vk-graph-fx/src/lib.rs index 1af35656..3b3be9e8 100644 --- a/crates/vk-graph-fx/src/lib.rs +++ b/crates/vk-graph-fx/src/lib.rs @@ -2,14 +2,6 @@ #![warn(missing_docs)] -/// TODO -pub mod prelude { - pub use super::{ - BitmapFont, BitmapGlyphColor, ComputePresenter, GraphicPresenter, ImageFormat, ImageLoader, - Transition, TransitionPipeline, - }; -} - mod bitmap_font; mod image_loader; mod presenter; diff --git a/crates/vk-graph-fx/src/presenter.rs b/crates/vk-graph-fx/src/presenter.rs index 088532a7..4925fc7b 100644 --- a/crates/vk-graph-fx/src/presenter.rs +++ b/crates/vk-graph-fx/src/presenter.rs @@ -1,7 +1,19 @@ use { bytemuck::cast_slice, glam::{Mat4, vec3}, - vk_graph_prelude::*, + vk_graph::{ + Graph, + cmd::{LoadOp, StoreOp}, + driver::{ + DriverError, + compute::{ComputePipeline, ComputePipelineInfo}, + device::Device, + graphic::{GraphicPipeline, GraphicPipelineInfo}, + shader::Shader, + sync::AccessType, + }, + node::{AnyImageNode, SwapchainImageNode}, + }, vk_shader_macros::include_glsl, }; diff --git a/crates/vk-graph-fx/src/transition.rs b/crates/vk-graph-fx/src/transition.rs index b4c46179..e7174398 100644 --- a/crates/vk-graph-fx/src/transition.rs +++ b/crates/vk-graph-fx/src/transition.rs @@ -2,7 +2,24 @@ // NOTE: Some are rough or broken and need a bit of care - others should be optimized for production // use. -use {log::trace, std::collections::HashMap, vk_graph_prelude::*, vk_shader_macros::include_glsl}; +use { + log::trace, + std::collections::HashMap, + vk_graph::{ + Graph, + driver::{ + ash::vk, + compute::{ComputePipeline, ComputePipelineInfo}, + device::Device, + image::ImageInfo, + shader::Shader, + sync::AccessType, + }, + node::{AnyImageNode, ImageLeaseNode}, + pool::{Pool as _, hash::HashPool}, + }, + vk_shader_macros::include_glsl, +}; #[allow(missing_docs)] #[derive(Clone, Copy, Debug)] @@ -380,7 +397,7 @@ impl Transition { /// TODO pub struct TransitionPipeline { cache: HashPool, - device: Device, + device: Device, // TODO REMOVE pipelines: HashMap, } diff --git a/crates/vk-graph-hot/Cargo.toml b/crates/vk-graph-hot/Cargo.toml index fbf5e4f4..1c188cae 100644 --- a/crates/vk-graph-hot/Cargo.toml +++ b/crates/vk-graph-hot/Cargo.toml @@ -2,27 +2,27 @@ name = "vk-graph-hot" version = "0.1.0" authors = ["John Wells "] -edition = "2024" -license = "MIT OR Apache-2.0" -readme = "README.md" -repository = "https://github.com/attackgoat/vk-graph" homepage = "https://github.com/attackgoat/vk-graph/crates/vk-graph-hot" keywords = ["gamedev", "vulkan"] categories = ["game-development", "multimedia::images", "rendering::engine"] description = "Hot-reloading shader pipelines for vk-graph" +edition.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true [dependencies] anyhow = "1.0" derive_builder = "0.20" -log = "0.4" +log.workspace = true notify = "8.2" paste = "1.0" -vk-graph = { path = "../..", default-features = false } -vk-graph-window = { path = "../vk-graph-window" } +vk-graph.workspace = true +vk-graph-window.workspace = true shader-prepper = "0.3.0-pre.3" shaderc = "0.10" [dev-dependencies] clap = { version = "4.6", features = ["derive"] } pretty_env_logger = "0.5" -vk-graph-prelude = { path = "../vk-graph-prelude" } +vk-graph.workspace = true diff --git a/crates/vk-graph-hot/examples/glsl.rs b/crates/vk-graph-hot/examples/glsl.rs index 3762fb08..d57e225d 100644 --- a/crates/vk-graph-hot/examples/glsl.rs +++ b/crates/vk-graph-hot/examples/glsl.rs @@ -1,8 +1,8 @@ use { clap::Parser, std::path::PathBuf, + vk_graph::driver::{compute::ComputePipelineInfo, sync::AccessType}, vk_graph_hot::{HotComputePipeline, HotShader}, - vk_graph_prelude::*, vk_graph_window::{Window, WindowError}, }; diff --git a/crates/vk-graph-hot/examples/hlsl.rs b/crates/vk-graph-hot/examples/hlsl.rs index 5b415dfd..26c81322 100644 --- a/crates/vk-graph-hot/examples/hlsl.rs +++ b/crates/vk-graph-hot/examples/hlsl.rs @@ -1,8 +1,11 @@ use { clap::Parser, std::path::PathBuf, + vk_graph::{ + cmd::{LoadOp, StoreOp}, + driver::graphic::GraphicPipelineInfo, + }, vk_graph_hot::{HotGraphicPipeline, HotShader}, - vk_graph_prelude::*, vk_graph_window::{Window, WindowError}, }; diff --git a/crates/vk-graph-imgui/Cargo.toml b/crates/vk-graph-imgui/Cargo.toml index 12b23556..353b26c5 100644 --- a/crates/vk-graph-imgui/Cargo.toml +++ b/crates/vk-graph-imgui/Cargo.toml @@ -2,13 +2,14 @@ name = "vk-graph-imgui" version = "0.1.0" authors = ["John Wells "] -edition = "2024" -license = "MIT OR Apache-2.0" -readme = "README.md" +edition.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true [dependencies] bytemuck = "1.25" imgui = "0.12" imgui-winit-support = "0.13" -vk-graph-prelude = { path = "../vk-graph-prelude" } +vk-graph.workspace = true vk-shader-macros = "0.2" diff --git a/crates/vk-graph-imgui/src/lib.rs b/crates/vk-graph-imgui/src/lib.rs index d8032069..a5088bd1 100644 --- a/crates/vk-graph-imgui/src/lib.rs +++ b/crates/vk-graph-imgui/src/lib.rs @@ -17,7 +17,21 @@ use { {HiDpiMode, WinitPlatform}, }, std::{sync::Arc, time::Duration}, - vk_graph_prelude::*, + vk_graph::{ + Graph, + cmd::{LoadOp, StoreOp}, + driver::{ + ash::vk, + buffer::{Buffer, BufferInfo}, + device::Device, + graphic::{BlendInfo, GraphicPipeline, GraphicPipelineInfo}, + image::{Image, ImageInfo}, + shader::Shader, + sync::AccessType, + }, + node::ImageLeaseNode, + pool::{Lease, Pool}, + }, vk_shader_macros::include_glsl, }; @@ -37,7 +51,7 @@ impl ImGui { let platform = WinitPlatform::new(&mut context); let pipeline = GraphicPipeline::create( device, - GraphicPipelineInfoBuilder::default() + GraphicPipelineInfo::builder() .blend(BlendInfo::PRE_MULTIPLIED_ALPHA) .cull_mode(vk::CullModeFlags::NONE), [ diff --git a/crates/vk-graph-prelude/Cargo.toml b/crates/vk-graph-prelude/Cargo.toml deleted file mode 100644 index 0bd1f4dc..00000000 --- a/crates/vk-graph-prelude/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "vk-graph-prelude" -version = "0.1.0" -authors = ["John Wells "] -edition = "2024" -license = "MIT OR Apache-2.0" -readme = "README.md" - -[dependencies] -vk-graph = { path = "../..", default-features = false } diff --git a/crates/vk-graph-prelude/README.md b/crates/vk-graph-prelude/README.md deleted file mode 100644 index 482506c0..00000000 --- a/crates/vk-graph-prelude/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# vk-graph-prelude - -Things which are used in almost every single _vk-graph_ program. diff --git a/crates/vk-graph-prelude/src/lib.rs b/crates/vk-graph-prelude/src/lib.rs deleted file mode 100644 index 934aa4dc..00000000 --- a/crates/vk-graph-prelude/src/lib.rs +++ /dev/null @@ -1,68 +0,0 @@ -//! TODO - -#![warn(missing_docs)] - -pub use vk_graph::{ - ClearColorValue, Graph, GraphNode, GraphResource, - cmd::{ - BuildAccelerationStructureIndirectInfo, BuildAccelerationStructureInfo, CommandRef, LoadOp, - PipelineCommandRef, StoreOp, UpdateAccelerationStructureIndirectInfo, - UpdateAccelerationStructureInfo, - }, - driver::{ - DriverError, - accel_struct::{ - AccelerationStructure, AccelerationStructureGeometry, - AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, - AccelerationStructureInfo, AccelerationStructureInfoBuilder, AccelerationStructureSize, - DeviceOrHostAddress, - }, - ash::vk, - buffer::{Buffer, BufferInfo, BufferInfoBuilder, BufferSubresourceRange}, - cmd_buf::CommandBuffer, - compute::{ComputePipeline, ComputePipelineInfo, ComputePipelineInfoBuilder}, - device::{Device, DeviceInfo, DeviceInfoBuilder}, - graphic::{ - BlendInfo, BlendInfoBuilder, DepthStencilInfo, DepthStencilInfoBuilder, - GraphicPipeline, GraphicPipelineInfo, GraphicPipelineInfoBuilder, StencilMode, - }, - image::{ - Image, ImageInfo, ImageInfoBuilder, ImageViewInfo, ImageViewInfoBuilder, SampleCount, - }, - instance::{Instance, InstanceInfo, InstanceInfoBuilder}, - physical_device::{ - AccelerationStructureProperties, PhysicalDevice, RayQueryFeatures, RayTraceFeatures, - RayTraceProperties, Vulkan10Features, Vulkan10Limits, Vulkan10Properties, - Vulkan11Features, Vulkan11Properties, Vulkan12Features, Vulkan12Properties, - }, - ray_trace::{ - RayTracePipeline, RayTracePipelineInfo, RayTracePipelineInfoBuilder, - RayTraceShaderGroup, RayTraceShaderGroupType, - }, - render_pass::ResolveMode, - shader::{SamplerInfo, SamplerInfoBuilder, Shader, ShaderBuilder, SpecializationMap}, - surface::Surface, - swapchain::{ - Swapchain, SwapchainError, SwapchainImage, SwapchainInfo, SwapchainInfoBuilder, - }, - sync::AccessType, - }, - node::{ - AccelerationStructureLeaseNode, AccelerationStructureNode, AnyAccelerationStructureNode, - AnyBufferNode, AnyImageNode, BufferLeaseNode, BufferNode, ImageLeaseNode, ImageNode, - SwapchainImageNode, - }, - pool::{ - Lease, Pool, PoolInfo, PoolInfoBuilder, - alias::{Alias, AliasWrapper}, - fifo::FifoPool, - hash::HashPool, - lazy::LazyPool, - }, -}; - -#[allow(deprecated)] -pub use vk_graph::{ - driver::graphic::{BlendMode, DepthStencilMode}, - pool::alias::AliasPool, -}; diff --git a/crates/vk-graph-window/Cargo.toml b/crates/vk-graph-window/Cargo.toml index 0a1ee553..9ab4d4d9 100644 --- a/crates/vk-graph-window/Cargo.toml +++ b/crates/vk-graph-window/Cargo.toml @@ -2,16 +2,17 @@ name = "vk-graph-window" version = "0.1.0" authors = ["John Wells "] -edition = "2024" -license = "MIT OR Apache-2.0" -readme = "README.md" +edition.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true [dependencies] derive_builder = "0.20" -log = "0.4" -profiling = "1.0" -vk-graph = { path = "../..", default-features = false } -winit = "0.30" +log.workspace = true +profiling.workspace = true +vk-graph.workspace = true +winit.workspace = true [dev-dependencies] pretty_env_logger = "0.5" diff --git a/crates/vk-graph-window/src/lib.rs b/crates/vk-graph-window/src/lib.rs index 17adc7d2..ed500b4f 100644 --- a/crates/vk-graph-window/src/lib.rs +++ b/crates/vk-graph-window/src/lib.rs @@ -5,7 +5,7 @@ mod frame; pub mod swapchain; -pub use self::frame::FrameContext; +pub use {self::frame::FrameContext, winit}; use { self::swapchain::{Swapchain, SwapchainError, SwapchainInfo}, @@ -66,8 +66,12 @@ pub struct Window { /// A device which is compatible with this window. /// /// _Note:_ This field is read-only. + #[cfg(doc)] pub device: Device, + #[cfg(not(doc))] + device: Device, + event_loop: EventLoop<()>, } @@ -79,7 +83,7 @@ impl Window { /// TODO pub fn builder() -> WindowBuilder { - WindowBuilder::default() + Default::default() } /// TODO diff --git a/crates/vk-graph-window/src/swapchain.rs b/crates/vk-graph-window/src/swapchain.rs index d2bed1a8..be52517e 100644 --- a/crates/vk-graph-window/src/swapchain.rs +++ b/crates/vk-graph-window/src/swapchain.rs @@ -626,7 +626,7 @@ mod test { .color_space(vk::ColorSpaceKHR::PASS_THROUGH_EXT), width: 88, }; - let builder = info.to_builder().build(); + let builder = info.into_builder().build(); assert_eq!(info, builder); } @@ -661,14 +661,6 @@ mod test { assert_eq!(info, builder); } - #[test] - pub fn swapchain_info_default() { - let info = Info::default(); - let builder = Builder::default().build(); - - assert_eq!(info, builder); - } - #[test] #[should_panic(expected = "Field value invalid: command_buffer_count")] pub fn swapchain_info_builder_uninit_command_buffer_count() { diff --git a/examples/aliasing.rs b/examples/aliasing.rs index 2d5a3192..413be3ac 100644 --- a/examples/aliasing.rs +++ b/examples/aliasing.rs @@ -1,4 +1,21 @@ -use {clap::Parser, std::sync::Arc, vk_graph_prelude::*}; +use { + ash::vk, + clap::Parser, + std::sync::Arc, + vk_graph::{ + Graph, + driver::{ + DriverError, + device::{Device, DeviceInfo}, + image::ImageInfo, + }, + pool::{ + Pool as _, + alias::{Alias as _, AliasWrapper}, + hash::HashPool, + }, + }, +}; /// This example demonstrates resource aliasing. Aliasing is a memory-efficiency optimization that /// may be used anywhere resources are leased and used in a graph. Aliasing allows complex @@ -17,7 +34,7 @@ fn main() -> Result<(), DriverError> { pretty_env_logger::init(); let args = Args::parse(); - let device_info = DeviceInfoBuilder::default().debug(args.debug); + let device_info = DeviceInfo::builder().debug(args.debug); let device = Device::new(device_info)?; // We wrap HashPool in an AliasPool container to enable resource aliasing diff --git a/examples/bindless.rs b/examples/bindless.rs index 6ae225c3..478ed625 100644 --- a/examples/bindless.rs +++ b/examples/bindless.rs @@ -1,13 +1,25 @@ mod profile_with_puffin; use { + ash::vk, bytemuck::{Pod, Zeroable, cast_slice}, clap::Parser, std::sync::Arc, - vk_graph::cmd::LoadOp, - vk_graph_prelude::*, - vk_graph_window::{WindowBuilder, WindowError}, + vk_graph::{ + Graph, + cmd::{LoadOp, StoreOp}, + driver::{ + DriverError, + buffer::Buffer, + device::Device, + graphic::{GraphicPipeline, GraphicPipelineInfo}, + image::{Image, ImageInfo}, + }, + pool::lazy::LazyPool, + }, + vk_graph_window::{Window, WindowError}, vk_shader_macros::glsl, + vk_sync::AccessType, winit::dpi::LogicalSize, }; @@ -16,7 +28,7 @@ fn main() -> Result<(), WindowError> { profile_with_puffin::init(); let args = Args::parse(); - let window = WindowBuilder::default() + let window = Window::builder() .debug(args.debug) .window(|window| window.with_inner_size(LogicalSize::new(512, 512))) .build()?; @@ -108,57 +120,53 @@ fn create_graphic_pipeline(device: &Device) -> Result Result<(), DriverError> { // For this example we create a headless device, but the same thing works using a window let args = Args::parse(); - let device_info = DeviceInfoBuilder::default().debug(args.debug); + let device_info = DeviceInfo::builder().debug(args.debug); let device = Device::new(device_info)?; let mut graph = Graph::default(); diff --git a/examples/debugger.rs b/examples/debugger.rs index 82bb98f5..f67c09dc 100644 --- a/examples/debugger.rs +++ b/examples/debugger.rs @@ -1,3 +1,11 @@ +use ash::vk; +use vk_graph::driver::{ + compute::{ComputePipeline, ComputePipelineInfo}, + image::{Image, ImageInfo}, + shader::Shader, +}; +use vk_sync::AccessType; + /* This example details some common debugging techniques you might find helpful when something goes wrong. @@ -24,7 +32,7 @@ To continue, uncomment line 30. */ fn main() -> Result<(), vk_graph_window::WindowError> { - use {log::debug, vk_graph_prelude::*, vk_graph_window::Window}; + use {log::debug, vk_graph_window::Window}; // 👋, 🌎! //pretty_env_logger::init(); diff --git a/examples/egui.rs b/examples/egui.rs index e8c7c2db..db13e9fd 100644 --- a/examples/egui.rs +++ b/examples/egui.rs @@ -1,7 +1,14 @@ mod profile_with_puffin; use { - clap::Parser, vk_graph_egui::prelude::*, vk_graph_prelude::*, vk_graph_window::WindowBuilder, + ash::vk, + clap::Parser, + vk_graph::{ + driver::image::ImageInfo, + pool::{Pool as _, lazy::LazyPool}, + }, + vk_graph_egui::{Egui, egui}, + vk_graph_window::Window, winit::dpi::LogicalSize, }; @@ -10,7 +17,7 @@ fn main() -> anyhow::Result<()> { profile_with_puffin::init(); let args = Args::parse(); - let window = WindowBuilder::default() + let window = Window::builder() .debug(args.debug) .v_sync(false) .window(|window| window.with_inner_size(LogicalSize::new(1024, 768))) diff --git a/examples/font_bmp.rs b/examples/font_bmp.rs index a9e10113..21af064a 100644 --- a/examples/font_bmp.rs +++ b/examples/font_bmp.rs @@ -1,14 +1,24 @@ mod profile_with_puffin; use { + ash::vk, bmfont::{BMFont, OrdinateOrientation}, clap::Parser, image::ImageReader, std::{io::Cursor, time::Instant}, + vk_graph::{ + driver::{ + compute::{ComputePipeline, ComputePipelineInfo}, + image::ImageInfo, + physical_device::Vulkan11Properties, + shader::{Shader, SpecializationMap}, + }, + pool::{Pool as _, hash::HashPool}, + }, vk_graph_fx::*, - vk_graph_prelude::*, - vk_graph_window::WindowBuilder, + vk_graph_window::Window, vk_shader_macros::glsl, + vk_sync::AccessType, }; fn main() -> anyhow::Result<()> { @@ -17,7 +27,7 @@ fn main() -> anyhow::Result<()> { // Standard vk-graph stuff let args = Args::parse(); - let window = WindowBuilder::default().debug(args.debug).build()?; + let window = Window::builder().debug(args.debug).build()?; let display = GraphicPresenter::new(&window.device)?; let mut image_loader = ImageLoader::new(&window.device)?; let mut pool = HashPool::new(&window.device); diff --git a/examples/fuzzer.rs b/examples/fuzzer.rs index d93cfeea..bbc3f260 100644 --- a/examples/fuzzer.rs +++ b/examples/fuzzer.rs @@ -22,14 +22,33 @@ Also helpful to run with valgrind: */ use { + ash::vk, clap::Parser, log::debug, rand::{Rng, rng, seq::IndexedRandom}, std::mem::size_of, - vk_graph::cmd::{BuildAccelerationStructureInfo, LoadOp}, - vk_graph_prelude::*, - vk_graph_window::{FrameContext, WindowBuilder, WindowError}, + vk_graph::{ + cmd::{BuildAccelerationStructureInfo, LoadOp, StoreOp}, + driver::{ + accel_struct::{ + AccelerationStructure, AccelerationStructureGeometry, + AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, + AccelerationStructureInfo, DeviceOrHostAddress, + }, + buffer::{Buffer, BufferInfo}, + compute::{ComputePipeline, ComputePipelineInfo}, + device::Device, + graphic::{DepthStencilInfo, GraphicPipeline, GraphicPipelineInfo, StencilMode}, + image::{ImageInfo, SampleCount}, + physical_device::Vulkan10Limits, + render_pass::ResolveMode, + shader::{Shader, SpecializationMap}, + }, + pool::{Pool as _, hash::HashPool}, + }, + vk_graph_window::{FrameContext, Window, WindowError}, vk_shader_macros::glsl, + vk_sync::AccessType, }; type Operation = fn(&mut FrameContext, &mut HashPool); @@ -58,7 +77,7 @@ fn main() -> Result<(), WindowError> { let mut rng = rng(); - let vk_graph = WindowBuilder::default().debug(true).build()?; + let vk_graph = Window::builder().debug(true).build()?; let mut pool = HashPool::new(&vk_graph.device); let mut frame_count = 0; @@ -323,7 +342,7 @@ fn record_pipeline_array_bind(frame: &mut FrameContext, pool: &mut HashPool) { "array_bind", frame.device, ComputePipelineInfo::default(), - Shader::new_compute( + Shader::from_spirv( glsl!( r#" #version 460 core @@ -726,7 +745,7 @@ fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPo let pipeline = graphic_vert_frag_pipeline( frame.device, - GraphicPipelineInfoBuilder::default().samples(sample_count), + GraphicPipelineInfo::builder().samples(sample_count), glsl!( r#" #version 460 core diff --git a/examples/image_sampler.rs b/examples/image_sampler.rs index 96109ef8..1031aeaa 100644 --- a/examples/image_sampler.rs +++ b/examples/image_sampler.rs @@ -1,15 +1,28 @@ mod profile_with_puffin; use { + ash::vk, clap::Parser, hassle_rs::compile_hlsl, std::{ path::{Path, PathBuf}, sync::Arc, }, - vk_graph_prelude::*, - vk_graph_window::WindowBuilder, + vk_graph::{ + Graph, + cmd::{LoadOp, StoreOp}, + driver::{ + buffer::Buffer, + device::Device, + graphic::{GraphicPipeline, GraphicPipelineInfo}, + image::{Image, ImageInfo}, + shader::{SamplerInfo, Shader}, + }, + pool::hash::HashPool, + }, + vk_graph_window::Window, vk_shader_macros::glsl, + vk_sync::AccessType, }; /// Displays a sequence of image samplers. @@ -31,18 +44,18 @@ fn main() -> anyhow::Result<()> { profile_with_puffin::init(); let args = Args::parse(); - let window = WindowBuilder::default().debug(args.debug).build()?; + let window = Window::builder().debug(args.debug).build()?; let gulf_image = read_image(&window.device, "examples/res/image/gulf.jpg")?; // Sampler info contains the full definition of Vulkan sampler settings using a builder struct - let edge_edge = SamplerInfoBuilder::default() + let edge_edge = SamplerInfo::builder() .address_mode_u(vk::SamplerAddressMode::CLAMP_TO_EDGE) .address_mode_v(vk::SamplerAddressMode::CLAMP_TO_EDGE); - let border_edge_black = SamplerInfoBuilder::default() + let border_edge_black = SamplerInfo::builder() .address_mode_u(vk::SamplerAddressMode::CLAMP_TO_BORDER) .address_mode_v(vk::SamplerAddressMode::CLAMP_TO_EDGE) .border_color(vk::BorderColor::FLOAT_OPAQUE_BLACK); - let edge_border_white = SamplerInfoBuilder::default() + let edge_border_white = SamplerInfo::builder() .address_mode_u(vk::SamplerAddressMode::CLAMP_TO_EDGE) .address_mode_v(vk::SamplerAddressMode::CLAMP_TO_BORDER) .border_color(vk::BorderColor::FLOAT_OPAQUE_WHITE); diff --git a/examples/imgui.rs b/examples/imgui.rs index 949f4e1f..d3c6ecf6 100644 --- a/examples/imgui.rs +++ b/examples/imgui.rs @@ -1,11 +1,15 @@ mod profile_with_puffin; use { + ash::vk, clap::Parser, + vk_graph::{ + driver::image::ImageInfo, + pool::{Pool as _, lazy::LazyPool}, + }, vk_graph_fx::*, vk_graph_imgui::{Condition, ImGui}, - vk_graph_prelude::*, - vk_graph_window::{WindowBuilder, WindowError}, + vk_graph_window::{Window, WindowError}, winit::dpi::LogicalSize, }; @@ -15,7 +19,7 @@ fn main() -> Result<(), WindowError> { // vk-graph things we need for this demo let args = Args::parse(); - let window = WindowBuilder::default() + let window = Window::builder() .debug(args.debug) .v_sync(false) .window(|window| window.with_inner_size(LogicalSize::new(1024, 768))) diff --git a/examples/min_max.rs b/examples/min_max.rs index 056e2f11..a862e819 100644 --- a/examples/min_max.rs +++ b/examples/min_max.rs @@ -1,10 +1,24 @@ use { + ash::vk, bytemuck::cast_slice, clap::Parser, log::warn, std::{mem::size_of, sync::Arc}, - vk_graph_prelude::*, + vk_graph::{ + Graph, + driver::{ + DriverError, + buffer::{Buffer, BufferInfo}, + compute::{ComputePipeline, ComputePipelineInfo}, + device::{Device, DeviceInfo}, + image::{Image, ImageInfo}, + shader::{SamplerInfo, Shader}, + }, + node::ImageNode, + pool::hash::HashPool, + }, vk_shader_macros::glsl, + vk_sync::AccessType, }; // Min/max sampler reduction is commonly used to create depth buffer mip-maps for use with gpu-based @@ -21,7 +35,7 @@ fn main() -> Result<(), DriverError> { let mut graph = Graph::default(); let args = Args::parse(); - let device_info = DeviceInfoBuilder::default().debug(args.debug); + let device_info = DeviceInfo::builder().debug(args.debug); let device = Device::new(device_info)?; let size = 4; diff --git a/examples/mip_compute.rs b/examples/mip_compute.rs index 44af0b61..ec22a9b3 100644 --- a/examples/mip_compute.rs +++ b/examples/mip_compute.rs @@ -1,6 +1,24 @@ mod profile_with_puffin; -use {bytemuck::cast_slice, clap::Parser, vk_graph_prelude::*, vk_shader_macros::glsl}; +use { + ash::vk, + bytemuck::cast_slice, + clap::Parser, + vk_graph::{ + Graph, + driver::{ + DriverError, + buffer::{Buffer, BufferInfo}, + compute::{ComputePipeline, ComputePipelineInfo}, + device::{Device, DeviceInfo}, + image::{Image, ImageInfo}, + shader::{SamplerInfo, Shader}, + }, + pool::hash::HashPool, + }, + vk_shader_macros::glsl, + vk_sync::AccessType, +}; /// This program demonstrates a single render pass which uses multiple executions to record a chain /// of image copies which reduce an input image from 4x4 into 2x2 and finally 1x1. This is useful @@ -13,7 +31,7 @@ fn main() -> Result<(), DriverError> { profile_with_puffin::init(); let args = Args::parse(); - let device_info = DeviceInfoBuilder::default().debug(args.debug); + let device_info = DeviceInfo::builder().debug(args.debug); let device = Device::new(device_info)?; let mut graph = Graph::default(); @@ -73,7 +91,7 @@ fn main() -> Result<(), DriverError> { ) .image_sampler( 0, - SamplerInfoBuilder::default() + SamplerInfo::builder() .address_mode_u(vk::SamplerAddressMode::CLAMP_TO_EDGE) .address_mode_v(vk::SamplerAddressMode::CLAMP_TO_EDGE) .mag_filter(vk::Filter::LINEAR) diff --git a/examples/mip_graphic.rs b/examples/mip_graphic.rs index 3461c216..1b64ee01 100644 --- a/examples/mip_graphic.rs +++ b/examples/mip_graphic.rs @@ -1,14 +1,27 @@ mod profile_with_puffin; use { + ash::vk, bytemuck::{Pod, Zeroable, bytes_of}, clap::Parser, core::f32, glam::{Vec4, vec3}, std::sync::Arc, - vk_graph_prelude::*, - vk_graph_window::{WindowBuilder, WindowError}, + vk_graph::{ + Graph, + cmd::{LoadOp, StoreOp}, + driver::{ + DriverError, + device::Device, + graphic::{GraphicPipeline, GraphicPipelineInfo}, + image::{Image, ImageInfo}, + shader::{SamplerInfoBuilder, Shader}, + }, + pool::lazy::LazyPool, + }, + vk_graph_window::{Window, WindowError}, vk_shader_macros::glsl, + vk_sync::AccessType, }; // TODO: Add texelFetch option @@ -18,7 +31,7 @@ fn main() -> Result<(), WindowError> { profile_with_puffin::init(); let args = Args::parse(); - let window = WindowBuilder::default().debug(args.debug).build()?; + let window = Window::builder().debug(args.debug).build()?; let size = 237u32; let mip_level_count = size.ilog2(); @@ -95,8 +108,8 @@ fn main() -> Result<(), WindowError> { } fn fill_mip_levels(device: &Device, image: &Arc) -> Result<(), DriverError> { - #[derive(Clone, Copy, Pod, Zeroable)] #[repr(C)] + #[derive(Clone, Copy, Pod, Zeroable)] struct PushConstants { a: Vec4, b: Vec4, diff --git a/examples/msaa.rs b/examples/msaa.rs index c85993f8..282cc333 100644 --- a/examples/msaa.rs +++ b/examples/msaa.rs @@ -1,15 +1,27 @@ mod profile_with_puffin; use { - bytemuck::{NoUninit, bytes_of, cast_slice}, + ash::vk, + bytemuck::{Pod, Zeroable, bytes_of, cast_slice}, clap::Parser, glam::{Mat4, Vec3}, log::warn, std::{mem::size_of, sync::Arc}, - vk_graph::cmd::LoadOp, - vk_graph_prelude::*, - vk_graph_window::WindowBuilder, + vk_graph::{ + cmd::{LoadOp, StoreOp}, + driver::{ + DriverError, + buffer::{Buffer, BufferInfo}, + device::Device, + graphic::{DepthStencilInfo, GraphicPipeline, GraphicPipelineInfo}, + image::{ImageInfo, SampleCount}, + physical_device::Vulkan10Limits, + }, + pool::{Pool as _, fifo::FifoPool}, + }, + vk_graph_window::Window, vk_shader_macros::glsl, + vk_sync::AccessType, winit::{event::Event, keyboard::KeyCode}, winit_input_helper::WinitInputHelper, }; @@ -26,7 +38,7 @@ fn main() -> anyhow::Result<()> { let mut input = WinitInputHelper::default(); let args = Args::parse(); - let window = WindowBuilder::default().debug(args.debug).build()?; + let window = Window::builder().debug(args.debug).build()?; let depth_format = best_depth_format(&window.device); let sample_count = max_supported_sample_count(&window.device); let mesh_msaa_pipeline = create_mesh_pipeline(&window.device, sample_count)?; @@ -385,16 +397,9 @@ fn create_mesh_pipeline( "# ); - let info = GraphicPipelineInfoBuilder::default().samples(sample_count); + let info = GraphicPipelineInfo::builder().samples(sample_count); - GraphicPipeline::create( - device, - info, - [ - Shader::new_vertex(vert.as_slice()), - Shader::new_fragment(frag.as_slice()), - ], - ) + GraphicPipeline::create(device, info, [vert.as_slice(), frag.as_slice()]) } #[derive(Parser)] @@ -410,7 +415,7 @@ struct Model { } #[repr(C)] -#[derive(Clone, Copy, NoUninit)] +#[derive(Clone, Copy, Pod, Zeroable)] struct SceneUniformBuffer { view: Mat4, projection: Mat4, diff --git a/examples/multipass.rs b/examples/multipass.rs index 400fa88b..1c19ca99 100644 --- a/examples/multipass.rs +++ b/examples/multipass.rs @@ -1,14 +1,28 @@ mod profile_with_puffin; use { + ash::vk, bytemuck::{bytes_of, cast_slice}, clap::Parser, glam::{Mat4, Vec3, Vec4, vec3}, std::sync::Arc, - vk_graph::{cmd::LoadOp, driver::graphic::DepthStencilInfo}, - vk_graph_prelude::*, - vk_graph_window::WindowBuilder, + vk_graph::{ + Graph, + cmd::{LoadOp, StoreOp}, + driver::{ + DriverError, + buffer::{Buffer, BufferInfo}, + device::Device, + graphic::{DepthStencilInfo, GraphicPipeline, GraphicPipelineInfo}, + image::ImageInfo, + shader::Shader, + }, + node::BufferLeaseNode, + pool::{Lease, Pool as _, lazy::LazyPool}, + }, + vk_graph_window::Window, vk_shader_macros::glsl, + vk_sync::AccessType, }; #[derive(Clone, Copy)] @@ -49,7 +63,7 @@ fn main() -> anyhow::Result<()> { profile_with_puffin::init(); let args = Args::parse(); - let window = WindowBuilder::default().debug(args.debug).build()?; + let window = Window::builder().debug(args.debug).build()?; let depth_stencil_format = best_depth_stencil_format(&window.device); let mut pool = LazyPool::new(&window.device); let fill_background = create_fill_background_pipeline(&window.device); diff --git a/examples/multithread.rs b/examples/multithread.rs index 178909f0..8513d1e9 100644 --- a/examples/multithread.rs +++ b/examples/multithread.rs @@ -1,6 +1,7 @@ mod profile_with_puffin; use { + ash::vk, bmfont::{BMFont, OrdinateOrientation}, clap::Parser, image::ImageReader, @@ -16,9 +17,17 @@ use { thread::{available_parallelism, sleep, spawn}, time::{Duration, Instant}, }, + vk_graph::{ + Graph, + driver::{ + buffer::Buffer, + device::Device, + image::{Image, ImageInfo}, + }, + pool::{Pool as _, hash::HashPool}, + }, vk_graph_fx::BitmapFont, - vk_graph_prelude::*, - vk_graph_window::WindowBuilder, + vk_graph_window::Window, }; const COLOR_SUBRESOURCE_LAYER: vk::ImageSubresourceLayers = vk::ImageSubresourceLayers { @@ -38,10 +47,7 @@ fn main() -> anyhow::Result<()> { // For this example we don't use V-Sync so that we are able to submit work as often as possible let args = Args::parse(); - let window = WindowBuilder::default() - .debug(args.debug) - .v_sync(false) - .build()?; + let window = Window::builder().debug(args.debug).v_sync(false).build()?; // We want to create one hardware queue for each CPU, or at least two let desired_queue_count = available_parallelism() diff --git a/examples/ray_omni.rs b/examples/ray_omni.rs index c09d9502..f9863c99 100644 --- a/examples/ray_omni.rs +++ b/examples/ray_omni.rs @@ -1,6 +1,7 @@ mod profile_with_puffin; use { + ash::vk, bytemuck::{Pod, Zeroable, bytes_of, cast_slice}, clap::Parser, glam::{Mat4, Vec3, Vec4, vec3, vec4}, @@ -14,10 +15,28 @@ use { sync::Arc, }, tobj::{GPU_LOAD_OPTIONS, load_obj}, - vk_graph::cmd::LoadOp, - vk_graph_prelude::*, - vk_graph_window::WindowBuilder, + vk_graph::{ + Graph, + cmd::{BuildAccelerationStructureInfo, LoadOp, StoreOp}, + driver::{ + DriverError, + accel_struct::{ + AccelerationStructure, AccelerationStructureGeometry, + AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, + AccelerationStructureInfo, + }, + buffer::{Buffer, BufferInfo}, + device::Device, + graphic::{DepthStencilInfo, GraphicPipeline, GraphicPipelineInfo}, + image::ImageInfo, + shader::Shader, + }, + node::AccelerationStructureLeaseNode, + pool::{Pool as _, lazy::LazyPool}, + }, + vk_graph_window::Window, vk_shader_macros::glsl, + vk_sync::AccessType, }; fn main() -> anyhow::Result<()> { @@ -25,7 +44,7 @@ fn main() -> anyhow::Result<()> { profile_with_puffin::init(); let args = Args::parse(); - let window = WindowBuilder::default().debug(args.debug).build()?; + let window = Window::builder().debug(args.debug).build()?; let mut pool = LazyPool::new(&window.device); let depth_fmt = best_2d_optimal_format( diff --git a/examples/ray_trace.rs b/examples/ray_trace.rs index 873e8687..a04b762b 100644 --- a/examples/ray_trace.rs +++ b/examples/ray_trace.rs @@ -1,14 +1,34 @@ mod profile_with_puffin; use { - bytemuck::cast_slice, + ash::vk, + bytemuck::{Pod, Zeroable, bytes_of, cast_slice}, clap::Parser, log::warn, std::{io::BufReader, mem::size_of, sync::Arc}, tobj::{GPU_LOAD_OPTIONS, load_mtl_buf, load_obj_buf}, - vk_graph_prelude::*, - vk_graph_window::WindowBuilder, + vk_graph::{ + Graph, + cmd::BuildAccelerationStructureInfo, + driver::{ + DriverError, + accel_struct::{ + AccelerationStructure, AccelerationStructureGeometry, + AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, + AccelerationStructureInfo, + }, + buffer::{Buffer, BufferInfo}, + device::Device, + image::ImageInfo, + physical_device::RayTraceProperties, + ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}, + shader::Shader, + }, + pool::{Pool as _, hash::HashPool}, + }, + vk_graph_window::Window, vk_shader_macros::glsl, + vk_sync::AccessType, winit::{event::Event, keyboard::KeyCode}, winit_input_helper::WinitInputHelper, }; @@ -332,7 +352,7 @@ static SHADER_SHADOW_MISS: &[u32] = glsl!( fn create_ray_trace_pipeline(device: &Device) -> Result { RayTracePipeline::create( device, - RayTracePipelineInfoBuilder::default().max_ray_recursion_depth(1), + RayTracePipelineInfo::builder().max_ray_recursion_depth(1), [ Shader::new_ray_gen(SHADER_RAY_GEN), Shader::new_closest_hit(SHADER_CLOSEST_HIT), @@ -488,7 +508,7 @@ fn main() -> anyhow::Result<()> { profile_with_puffin::init(); let args = Args::parse(); - let window = WindowBuilder::default().debug(args.debug).build()?; + let window = Window::builder().debug(args.debug).build()?; let mut cache = HashPool::new(&window.device); // ------------------------------------------------------------------------------------------ // @@ -819,6 +839,7 @@ fn main() -> anyhow::Result<()> { let camera_buf = frame.graph.bind_resource({ #[repr(C)] + #[derive(Clone, Copy, Pod, Zeroable)] struct Camera { position: [f32; 4], right: [f32; 4], @@ -833,18 +854,16 @@ fn main() -> anyhow::Result<()> { vk::BufferUsageFlags::UNIFORM_BUFFER, )) .unwrap(); - buf.copy_from_slice(0, unsafe { - std::slice::from_raw_parts( - &Camera { - position, - right, - up, - forward, - frame_count, - } as *const _ as *const _, - size_of::(), - ) - }); + buf.copy_from_slice( + 0, + bytes_of(&Camera { + position, + right, + up, + forward, + frame_count, + }), + ); buf }); diff --git a/examples/rt_triangle.rs b/examples/rt_triangle.rs index 845d5a4b..f449d1ae 100644 --- a/examples/rt_triangle.rs +++ b/examples/rt_triangle.rs @@ -1,12 +1,31 @@ mod profile_with_puffin; use { - bytemuck::{NoUninit, cast_slice}, + ash::vk, + bytemuck::{Pod, Zeroable, cast_slice}, clap::Parser, std::sync::Arc, - vk_graph_prelude::*, - vk_graph_window::WindowBuilder, + vk_graph::{ + Graph, + cmd::BuildAccelerationStructureInfo, + driver::{ + DriverError, + accel_struct::{ + AccelerationStructure, AccelerationStructureGeometry, + AccelerationStructureGeometryData, AccelerationStructureGeometryInfo, + AccelerationStructureInfo, + }, + buffer::{Buffer, BufferInfo}, + device::Device, + physical_device::RayTraceProperties, + ray_trace::{RayTracePipeline, RayTracePipelineInfo, RayTraceShaderGroup}, + shader::Shader, + }, + pool::hash::HashPool, + }, + vk_graph_window::Window, vk_shader_macros::glsl, + vk_sync::AccessType, }; static SHADER_RAY_GEN: &[u32] = glsl!( @@ -79,7 +98,7 @@ static SHADER_MISS: &[u32] = glsl!( fn create_ray_trace_pipeline(device: &Device) -> Result { RayTracePipeline::create( device, - RayTracePipelineInfoBuilder::default().max_ray_recursion_depth(1), + RayTracePipelineInfo::builder().max_ray_recursion_depth(1), [ Shader::new_ray_gen(SHADER_RAY_GEN), Shader::new_closest_hit(SHADER_CLOSEST_HIT), @@ -99,7 +118,7 @@ fn main() -> anyhow::Result<()> { profile_with_puffin::init(); let args = Args::parse(); - let window = WindowBuilder::default().debug(args.debug).build()?; + let window = Window::builder().debug(args.debug).build()?; let mut pool = HashPool::new(&window.device); // ------------------------------------------------------------------------------------------ // @@ -182,7 +201,7 @@ fn main() -> anyhow::Result<()> { let vertex_count = triangle_count * 3; #[repr(C)] - #[derive(Debug, Clone, Copy, NoUninit)] + #[derive(Debug, Clone, Copy, Pod, Zeroable)] #[allow(dead_code)] struct Vertex { pos: [f32; 3], diff --git a/examples/shader-toy/Cargo.toml b/examples/shader-toy/Cargo.toml index 6fac6ec1..d45d9860 100644 --- a/examples/shader-toy/Cargo.toml +++ b/examples/shader-toy/Cargo.toml @@ -1,11 +1,12 @@ [package] name = "shader-toy" -version = "0.1.0" authors = ["John Wells "] -edition = "2024" -license = "MIT OR Apache-2.0" -readme = "README.md" description = "Example api usage" +edition.workspace = true +license.workspace = true +publish = false +readme.workspace = true +repository.workspace = true [features] default = ["include-pak"] @@ -13,18 +14,18 @@ include-pak = [] [dependencies] anyhow = "1.0" -bytemuck = "1.14" +bytemuck = { version = "1.25", features = ["derive"] } clap = { version = "4.5", features = ["derive"] } pak = "0.5" pretty_env_logger = "0.5" -vk-graph = { path = "../.." } -vk-graph-fx = { path = "../../crates/vk-graph-fx" } -vk-graph-window = { path = "../../crates/vk-graph-window" } -winit = "0.30" +vk-graph.workspace = true +vk-graph-fx.workspace = true +vk-graph-window.workspace = true +winit.workspace = true [build-dependencies] anyhow = "1.0" pak = { version = "0.5", features = ["bake"] } paste = "1.0" shader-prepper = "0.3.0-pre.3" -shaderc = "0.8" +shaderc = "0.10" diff --git a/examples/shader-toy/src/main.rs b/examples/shader-toy/src/main.rs index b0efc3e0..a2fecf83 100644 --- a/examples/shader-toy/src/main.rs +++ b/examples/shader-toy/src/main.rs @@ -51,7 +51,7 @@ use { pool::{Pool as _, lazy::LazyPool}, }, vk_graph_fx::*, - vk_graph_window::WindowBuilder, + vk_graph_window::Window, winit::dpi::PhysicalSize, }; @@ -59,7 +59,7 @@ fn main() -> anyhow::Result<()> { pretty_env_logger::init(); let args = Args::parse(); - let window = WindowBuilder::default() + let window = Window::builder() .debug(args.debug) .min_image_count(3) .window(|builder| builder.with_inner_size(PhysicalSize::new(1280.0f64, 720.0f64))) @@ -210,7 +210,7 @@ fn main() -> anyhow::Result<()> { // type will do but we are getting fancy here by defining a struct to be super precise // about what we're doing - but you may want to just send a bunch of f32's #[repr(C)] - #[derive(Clone, Copy)] + #[derive(Clone, Copy, Pod, Zeroable)] struct PushConstants { resolution: [f32; 3], _pad_1: u32, @@ -224,25 +224,6 @@ fn main() -> anyhow::Result<()> { channel_resolution: [f32; 16], } - unsafe impl Pod for PushConstants {} - - unsafe impl Zeroable for PushConstants { - fn zeroed() -> Self { - Self { - resolution: [0f32; 3], - _pad_1: 0u32, - date: [0f32; 4], - mouse: [0f32; 4], - time: 0f32, - time_delta: 0f32, - frame: 0i32, - sample_rate: 0f32, - channel_time: [0f32; 4], - channel_resolution: [0f32; 16], - } - } - } - // Each pipeline gets the same constant data let push_consts = PushConstants { resolution: [frame.width as f32, frame.height as _, 1.0], diff --git a/examples/skeletal-anim/Cargo.toml b/examples/skeletal-anim/Cargo.toml index 9929d3c7..0cc76a0d 100644 --- a/examples/skeletal-anim/Cargo.toml +++ b/examples/skeletal-anim/Cargo.toml @@ -1,23 +1,24 @@ [package] name = "animation" -version = "0.1.0" authors = ["John Wells "] -edition = "2024" -license = "MIT OR Apache-2.0" -readme = "README.md" +edition.workspace = true +license.workspace = true +publish = false +readme.workspace = true +repository.workspace = true [dependencies] -bytemuck = "1.14" +bytemuck = { version = "1.25", features = ["derive"] } clap = { version = "4.5", features = ["derive"] } glam = { version = "0.27", features = ["bytemuck"] } pak = "=0.5.0" pretty_env_logger = "0.5" -vk-graph = { path = "../.." } -vk-graph-window = { path = "../../crates/vk-graph-window" } +vk-graph.workspace = true +vk-graph-window.workspace = true [build-dependencies] anyhow = "1.0" -log = "0.4" +log.workspace = true pak = { version = "=0.5.0", features = ["bake"] } -shaderc = "0.8" +shaderc = "0.10" simplelog = "0.12" diff --git a/examples/skeletal-anim/src/main.rs b/examples/skeletal-anim/src/main.rs index a74475d5..4e73c11c 100644 --- a/examples/skeletal-anim/src/main.rs +++ b/examples/skeletal-anim/src/main.rs @@ -1,5 +1,5 @@ use { - bytemuck::{NoUninit, bytes_of, cast_slice}, + bytemuck::{Pod, Zeroable, bytes_of, cast_slice}, clap::Parser, glam::{Mat4, Quat, Vec3}, pak::{ @@ -31,7 +31,7 @@ use { }, pool::{Pool as _, hash::HashPool, lazy::LazyPool}, }, - vk_graph_window::{WindowBuilder, WindowError}, + vk_graph_window::{Window, WindowError}, }; // This blog has a really good overview of what is happening here: @@ -43,7 +43,7 @@ fn main() -> Result<(), WindowError> { let mut pak = PakBuf::open(pak_path).unwrap(); let args = Args::parse(); - let window = WindowBuilder::default().debug(args.debug).build()?; + let window = Window::builder().debug(args.debug).build()?; let device = &window.device; let pipeline = create_pipeline(device, &mut pak)?; @@ -100,6 +100,7 @@ fn main() -> Result<(), WindowError> { projection, view, position, + ..Default::default() }), ); @@ -377,15 +378,14 @@ struct Args { } #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default, Pod, Zeroable)] struct CameraUniform { projection: Mat4, view: Mat4, position: Vec3, + __: u32, } -unsafe impl NoUninit for CameraUniform {} - struct Model { index_buf: Arc, index_count: u32, diff --git a/examples/subgroup_ops.rs b/examples/subgroup_ops.rs index 53e22f77..35c00854 100644 --- a/examples/subgroup_ops.rs +++ b/examples/subgroup_ops.rs @@ -1,9 +1,22 @@ use { + ash::vk, bytemuck::cast_slice, clap::Parser, std::{mem::size_of, sync::Arc, time::Instant}, - vk_graph_prelude::*, + vk_graph::{ + Graph, + driver::{ + DriverError, + buffer::{Buffer, BufferInfo}, + compute::{ComputePipeline, ComputePipelineInfo}, + device::{Device, DeviceInfo}, + physical_device::Vulkan11Properties, + shader::{Shader, SpecializationMap}, + }, + pool::hash::HashPool, + }, vk_shader_macros::glsl, + vk_sync::AccessType, }; /// Advanced example demonstrating subgroup operations (arithmetic and ballot). @@ -30,7 +43,7 @@ fn main() -> Result<(), DriverError> { pretty_env_logger::init(); let args = Args::parse(); - let device_info = DeviceInfoBuilder::default().debug(args.debug); + let device_info = DeviceInfo::builder().debug(args.debug); let device = Device::new(device_info)?; let Vulkan11Properties { subgroup_size, @@ -156,7 +169,7 @@ fn create_reduce_pipeline(device: &Device) -> Result anyhow::Result<()> { // Create vk-graph things any similar program might need let args = Args::parse(); - let window = WindowBuilder::default() + let window = Window::builder() .debug(args.debug) .window(|builder| builder.with_inner_size(LogicalSize::new(1024.0f64, 768.0f64))) .build()?; diff --git a/examples/triangle.rs b/examples/triangle.rs index f047020a..6aec2b83 100644 --- a/examples/triangle.rs +++ b/examples/triangle.rs @@ -1,13 +1,20 @@ mod profile_with_puffin; use { + ash::vk, bytemuck::cast_slice, clap::Parser, std::sync::Arc, - vk_graph::cmd::LoadOp, - vk_graph_prelude::*, - vk_graph_window::{WindowBuilder, WindowError}, + vk_graph::{ + cmd::{LoadOp, StoreOp}, + driver::{ + buffer::Buffer, + graphic::{GraphicPipeline, GraphicPipelineInfo}, + }, + }, + vk_graph_window::{Window, WindowError}, vk_shader_macros::glsl, + vk_sync::AccessType, }; // A Vulkan triangle using a graphic pipeline, vertex/fragment shaders, and index/vertex buffers. @@ -16,7 +23,7 @@ fn main() -> Result<(), WindowError> { profile_with_puffin::init(); let args = Args::parse(); - let window = WindowBuilder::default().debug(args.debug).build()?; + let window = Window::builder().debug(args.debug).build()?; let triangle_pipeline = GraphicPipeline::create( &window.device, GraphicPipelineInfo::default(), diff --git a/examples/vertex_layout.rs b/examples/vertex_layout.rs index 9fb48255..5febcf5d 100644 --- a/examples/vertex_layout.rs +++ b/examples/vertex_layout.rs @@ -1,13 +1,24 @@ mod profile_with_puffin; use { + ash::vk, bytemuck::{Pod, Zeroable, cast_slice}, clap::Parser, half::f16, std::{mem::size_of, sync::Arc}, - vk_graph_prelude::*, - vk_graph_window::{FrameContext, WindowBuilder}, + vk_graph::{ + cmd::{LoadOp, StoreOp}, + driver::{ + DriverError, + buffer::Buffer, + device::Device, + graphic::{GraphicPipeline, GraphicPipelineInfo}, + shader::{Shader, ShaderBuilder}, + }, + }, + vk_graph_window::{FrameContext, Window}, vk_shader_macros::glsl, + vk_sync::AccessType, }; /// This example draws two triangles using two different vertex formats. @@ -24,7 +35,7 @@ fn main() -> anyhow::Result<()> { // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fxvertex-attrib let args = Args::parse(); - let window = WindowBuilder::default().debug(args.debug).build()?; + let window = Window::builder().debug(args.debug).build()?; let f16_pipeline = create_f16_pipeline(&window.device).ok(); let f16_vertex_buf = { @@ -231,7 +242,7 @@ fn create_pipeline(device: &Device, vertex: ShaderBuilder) -> Result"] -edition = "2024" -license = "MIT OR Apache-2.0" -readme = "README.md" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +edition.workspace = true +license.workspace = true +publish = false +readme.workspace = true +repository.workspace = true [dependencies] anyhow = "1.0" -bytemuck = { version = "1.14", features = ["derive"] } +bytemuck = { version = "1.25", features = ["derive"] } ctrlc = "3.4" glam = { version = "0.27", features = ["bytemuck", "mint"] } image = "0.25" -log = "0.4" +log.workspace = true meshopt = "0.2" mikktspace = "0.3" mint = "0.5" openxr = { version = "0.18", features = ["mint", "static"] } pretty_env_logger = "0.5" -vk-graph = { path = "../.." } -vk-graph-hot = { path = "../../crates/vk-graph-hot" } +vk-graph.workspace = true +vk-graph-hot.workspace = true tobj = "4.0" diff --git a/examples/vsm_omni.rs b/examples/vsm_omni.rs index ac812038..ab910d27 100644 --- a/examples/vsm_omni.rs +++ b/examples/vsm_omni.rs @@ -1,6 +1,7 @@ mod profile_with_puffin; use { + ash::vk, bytemuck::{NoUninit, Pod, Zeroable, bytes_of, cast_slice}, clap::Parser, glam::{Mat4, Quat, Vec3, vec3}, @@ -13,10 +14,23 @@ use { sync::Arc, }, tobj::{GPU_LOAD_OPTIONS, load_obj}, - vk_graph::cmd::LoadOp, - vk_graph_prelude::*, - vk_graph_window::WindowBuilder, + vk_graph::{ + cmd::{LoadOp, StoreOp}, + driver::{ + DriverError, + buffer::{Buffer, BufferInfo}, + compute::{ComputePipeline, ComputePipelineInfo}, + device::Device, + graphic::{DepthStencilInfo, GraphicPipeline, GraphicPipelineInfo}, + image::ImageInfo, + physical_device::Vulkan10Features, + shader::{Shader, SpecializationMap}, + }, + pool::{Lease, Pool, fifo::FifoPool}, + }, + vk_graph_window::Window, vk_shader_macros::glsl, + vk_sync::AccessType, winit::{dpi::LogicalSize, event::Event, keyboard::KeyCode, window::Fullscreen}, winit_input_helper::WinitInputHelper, }; @@ -49,7 +63,7 @@ fn main() -> anyhow::Result<()> { let mut input = WinitInputHelper::default(); let args = Args::parse(); - let window = WindowBuilder::default() + let window = Window::builder() .debug(args.debug) .window(|window| window.with_inner_size(LogicalSize::new(800, 600))) .build()?; @@ -1407,7 +1421,7 @@ where /// Loads an .obj model as indexed position and normal vertices fn load_model_mesh(device: &Device, path: impl AsRef) -> anyhow::Result { #[repr(C)] - #[derive(Clone, Copy, Default, NoUninit)] + #[derive(Clone, Copy, Default, Pod, Zeroable)] struct Vertex { position: Vec3, normal: Vec3, @@ -1444,7 +1458,7 @@ fn load_model_mesh(device: &Device, path: impl AsRef) -> anyhow::Result) -> anyhow::Result { #[repr(C)] - #[derive(Clone, Copy, Default, NoUninit)] + #[derive(Clone, Copy, Default, Pod, Zeroable)] struct Vertex { position: Vec3, } From 5db3cf18377a4d73f80becdfd6592938d05887da Mon Sep 17 00:00:00 2001 From: John Wells Date: Wed, 18 Mar 2026 13:07:26 -0400 Subject: [PATCH 45/86] docs --- crates/vk-graph-window/src/swapchain.rs | 6 +- guide/guide-helper/src/lib.rs | 31 +++++++++- guide/src/README.md | 41 ++++++++----- guide/src/SUMMARY.md | 1 - guide/src/install.md | 3 +- guide/src/pipeline.md | 40 +++++++++--- guide/src/pipeline_hot_reload.md | 52 ++++++++++++++++ guide/src/pipeline_push_const.md | 16 +++-- guide/src/pipeline_spec.md | 14 ++--- guide/src/pipeline_sync.md | 10 +-- guide/src/queue.md | 1 - guide/src/resource.md | 24 +++++--- guide/src/resource_buffer.md | 8 +-- guide/src/resource_image.md | 5 +- guide/src/usage.md | 12 ++-- guide/src/usage_debugging.md | 29 +++++---- guide/src/usage_device.md | 18 +++--- guide/src/usage_moltenvk.md | 14 ++++- guide/src/usage_shader.md | 4 +- guide/src/usage_thread.md | 20 +++--- guide/src/usage_window.md | 8 ++- src/cmd/cmd_buf.rs | 4 +- src/cmd/mod.rs | 11 ++-- src/cmd/pipeline.rs | 6 +- src/driver/swapchain.rs | 24 ++++---- src/lib.rs | 18 +++--- src/node.rs | 70 ++++++++++++++++++--- src/queue.rs | 3 +- src/resource.rs | 82 +------------------------ 29 files changed, 343 insertions(+), 232 deletions(-) delete mode 100644 guide/src/queue.md diff --git a/crates/vk-graph-window/src/swapchain.rs b/crates/vk-graph-window/src/swapchain.rs index be52517e..ad28a069 100644 --- a/crates/vk-graph-window/src/swapchain.rs +++ b/crates/vk-graph-window/src/swapchain.rs @@ -171,11 +171,11 @@ impl Swapchain { Ok(swapchain_image) => Ok(swapchain_image), }?; - while self.image_execs.len() >= swapchain_image.idx as _ { + while self.image_execs.len() >= swapchain_image.index as _ { self.image_execs.push(0); } - self.image_execs[swapchain_image.idx as usize] = self.exec_idx; + self.image_execs[swapchain_image.index as usize] = self.exec_idx; Ok(Some(swapchain_image)) } @@ -203,7 +203,7 @@ impl Swapchain { "uninitialized swapchain image: write something each frame!", ); - let image_idx = queue.resource(swapchain_image).idx; + let image_idx = queue.resource(swapchain_image).index; let exec_idx = self.image_execs[image_idx as usize]; let exec = &mut self.execs[exec_idx]; diff --git a/guide/guide-helper/src/lib.rs b/guide/guide-helper/src/lib.rs index e10c58cb..d477ea3c 100644 --- a/guide/guide-helper/src/lib.rs +++ b/guide/guide-helper/src/lib.rs @@ -2,7 +2,11 @@ use { cargo_toml::Manifest, - mdbook_preprocessor::{Preprocessor, PreprocessorContext, book::Book, errors::Result}, + mdbook_preprocessor::{ + Preprocessor, PreprocessorContext, + book::Book, + errors::{Error, Result}, + }, semver::{Version, VersionReq}, std::{env::current_dir, io}, }; @@ -40,9 +44,21 @@ impl Preprocessor for GuideHelper { fn run(&self, _ctx: &PreprocessorContext, mut book: Book) -> Result { insert_crate_version(&mut book); + insert_dependency_req(&mut book, "log"); + insert_dependency_req(&mut book, "profiling"); insert_vulkan_sdk_version(&mut book); - Ok(book) + let mut ok = true; + book.for_each_chapter_mut(|ch| { + ok &= !ch.content.contains("{{"); + ok &= !ch.content.contains("}}"); + }); + + if ok { + Ok(book) + } else { + Err(Error::msg("unredacted formatting marks")) + } } } @@ -66,6 +82,17 @@ fn insert_crate_version(book: &mut Book) { }); } +fn insert_dependency_req(book: &mut Book, dep: &str) { + let req = manifest().dependencies.get(dep).unwrap().req().to_owned(); + let marker = format!("{{{{ {dep}.version }}}}"); + + book.for_each_chapter_mut(|ch| { + if ch.content.contains(&marker) { + ch.content = ch.content.replace(&marker, &req); + } + }); +} + fn insert_vulkan_sdk_version(book: &mut Book) { // Technically this is a VersionReq but we're not using it that way and want the build metadata let Version { build, .. } = diff --git a/guide/src/README.md b/guide/src/README.md index 4934d07d..6ad70784 100644 --- a/guide/src/README.md +++ b/guide/src/README.md @@ -1,32 +1,39 @@ # Introduction -`vk-graph` is a high-performance Vulkan driver for the Rust programming language featuring automated resource management and execution. It is _blazingly_-fast, built for real-world use, and executes all modern Vulkan commands[^modern]. +`vk-graph` is a high-performance Vulkan driver for the Rust programming language featuring automated +resource management and execution. It is _blazingly_-fast, built for real-world use, and supports +modern Vulkan commands[^modern]. -This guide book will walk you through the mental model of this crate and help explain how it maps to Vulkan API usage. +This guide book will walk you through the mental model of this crate and help explain how it maps to +Vulkan API usage. > [!IMPORTANT] -> Users should be familiar with the [Vulkan specification](https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html). +> Users should be familiar with the Vulkan +[_specification_](https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html) +. ## Design This guide provides a tour of the main public types: -[Driver]() - : [Buffer](), [Image](), [Shader](), _etc.._ +Driver + : _Buffer, Image, Shader, etc.._ -[Graph]() +Graph : _Builder-pattern for Vulkan commands_ -[Queue]() +Queue : _Automated graph execution_ -A `Graph` is data built dynamically by your program every frame. Once complete, the graph is optimized into a `Queue` which may be used to submit commands to the Vulkan implementation. +A `Graph` is data built dynamically by your program every frame. Once complete, the graph is +optimized into a `Queue` which may be used to submit commands to the Vulkan implementation. -The wall-time overhead of this crate is intended to be in the 250 μs/frame range. +The overhead of building and submitting each graph is typically a few hundred microseconds. ## Philosophy -Vulkan is hard. Synchronization is _extremely_ hard. `vk-graph` makes Vulkan *less painful* to write and *a joy* to maintain. +Vulkan is hard. Synchronization is _extremely_ hard. `vk-graph` makes Vulkan *less painful* to write +and *a joy* to maintain. The driver is based off the popular `ash` crate and `vk-sync`; reasoned as follows: - _Everything_ is constructed from "`Info`" structs; all info is `Copy` @@ -35,14 +42,18 @@ The driver is based off the popular `ash` crate and `vk-sync`; reasoned as follo - Don't use macro-magic or anything that needs to be learned - Don't rely on "helper" functions unless absolutely required -[^modern]: Modern Vulkan usage means no pixel queries. Anything else unsupported is due to there being better options, no current need, or no interest. Please open an issue. -[^video]: Video encode/decode is interesting but unsupported. As an alternative consider `ffmpeg`, `libavcodec`, or one of the experimental Rust bindings to the Vulkan video API. - ## History - 2018 --- Project started privately as a game engine using -[`Corange`](https://github.com/orangeduck/Corange) +[_`Corange`_](https://github.com/orangeduck/Corange) + - 2020 --- Project migrated to Github and named `screen-13` - 2022 --- v0.2 released with `RenderGraph` type based on -[`Kajiya`](https://github.com/EmbarkStudios/kajiya) +[_`Kajiya`_](https://github.com/EmbarkStudios/kajiya) + - 2026 --- Project renamed `vk-graph` (v0.14) + +[^modern]: Modern Vulkan usage means no pixel queries. Anything else unsupported is due to there +being better options, no current need, or no interest. Please open an issue. +[^video]: Video encode/decode is interesting but unsupported. As an alternative consider `ffmpeg`, +`libavcodec`, or one of the experimental Rust bindings to the Vulkan video API. diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index 8db8f26d..a069a165 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -30,4 +30,3 @@ 1. [Computing](./cmd_compute.md) 1. [Graphics](./cmd_graphic.md) 1. [Ray Tracing](./cmd_ray_trace.md) -1. [Queue](./queue.md) diff --git a/guide/src/install.md b/guide/src/install.md index 3f32f43d..c9a686d0 100644 --- a/guide/src/install.md +++ b/guide/src/install.md @@ -22,7 +22,8 @@ vk-graph = "{{ crate.version }}" ## Vulkan SDK Debug mode (setting the `debug` field of `DeviceInfo` or `InstanceInfo` to `true`) is supported only -when a compatible [Vulkan SDK](https://vulkan.lunarg.com/sdk/home) is installed. +when a compatible [_Vulkan SDK_](https://vulkan.lunarg.com/sdk/home) + is installed. > [!IMPORTANT] > The installed Vulkan SDK version must be at least v{{ vulkan_sdk.version }}. diff --git a/guide/src/pipeline.md b/guide/src/pipeline.md index 6b410539..4fe9f935 100644 --- a/guide/src/pipeline.md +++ b/guide/src/pipeline.md @@ -1,20 +1,21 @@ # Pipelines > [!CAUTION] -> All pipelines and resources (_buffers, images, and acceleration structures_) "bound" to any -> `Graph` must have been created by the same `Device`. +> All pipelines and resources (_buffers, images, and acceleration structures_) used in a `Graph` +> must have been created using the same `Device`. Pipelines are created from `Device` references. They may be bound to graph commands. ```rust +let info = ComputePipelineInfo::default(); +let shader = include_bytes!("shader.spv"); +let pipeline = ComputePipeline::create(device, info, shader)?; + let mut graph = Graph::default() .begin_cmd() - .bind_pipeline(ComputePipeline::create( - device, - include_bytes!("shader.spv"), - )?) + .bind_pipeline(&pipeline) .record_cmd_buf(|cmd_buf| { - todo!("add resource access") + // Record vulkan commands here }) .end_cmd(); ``` @@ -47,7 +48,30 @@ graph .record_cmd_buf(|cmd_buf| todo!("3rd")); ``` -A call to `Graph::end_cmd` is never requried and the command is automatically ended. The call is +A call to `Graph::end_cmd` is never requried and the command is automatically ended. The call may be useful for builder-pattern code which is building a very large series of commands. ## Shaders + +Compute, graphic, and ray trace pipelines require one or more shaders: + +Pipeline Type|Shaders +--|-- +`ComputePipeline`|Single: must be compute stage +`GraphicPipeline`|Multiple: must be a raster stage +`RayTracePipeline`|Multiple: must be a ray tracing stage + +> [!CAUTION] +> All `Shader` constructors panic when provided with invalid SPIR-V shader code. + +The `Shader` type uses a builder pattern: + +```rust +// Pipelines may be created using "code", "shader", or "custom": +let code = include_bytes!("raygen.spv"); +let shader = Shader::from_spirv(code.as_slice()); +let custom = shader + .entry_name("main_but_faster") + .image_sampler(0, SamplerInfo::default()) + .image_sampler(1, SamplerInfo::LINEAR); +``` diff --git a/guide/src/pipeline_hot_reload.md b/guide/src/pipeline_hot_reload.md index 7d21fcd3..b5e2ec10 100644 --- a/guide/src/pipeline_hot_reload.md +++ b/guide/src/pipeline_hot_reload.md @@ -1 +1,53 @@ # Hot Reload + +An accessory crate is provided to support automatic reloading of changed shader pipelines. + +`vk-graph-hot` uses a file watcher and `Shaderc`. It may be used directly or may be swapped out +using a build feature: + +```toml +# Cargo.toml + +[features] +default = [] +hot = ["dep:vk-graph-hot"] + +[dependencies] +vk-graph = "{{ crate.version }}" +vk-graph-hot = { version = "{{ crate.version }}", optional = true } +``` + +```rust +use vk_graph::driver::{DriverError, compute::ComputePipelineInfo, device::Device}; + +#[cfg(feature = "hot")] +use vk_graph_hot::{ + HotComputePipeline as ComputePipeline, + HotShader, +}; + +#[cfg(not(feature = "hot"))] +use vk_graph::driver::{ + compute::ComputePipeline, + shader::Shader, +}; + +pub fn create_explosion_pipeline( + device: &Device, +) -> Result { + let info = ComputePipelineInfo::default(); + let shader = { + #[cfg(feature = "hot")] + HotShader::from_path("shaders/explosion.glsl") + + #[cfg(not(feature = "hot"))] + Shader::from_spirv(include_bytes!("shaders/explosion.spv")) + }; + + ComputePipeline::create(device, info, shader) +} +``` + +> [!NOTE] +> The hot versions of each type support all features, options, and usage provided by the normal +> types. This include public fields, available information, and graph binding features. diff --git a/guide/src/pipeline_push_const.md b/guide/src/pipeline_push_const.md index a97d027e..2c94a4d2 100644 --- a/guide/src/pipeline_push_const.md +++ b/guide/src/pipeline_push_const.md @@ -15,14 +15,22 @@ layout(push_constant) uniform PushConstants { ``` ```rust -let shader = Shader::new_compute(include_bytes!("render_mesh.spv").as_slice()); -let pipeline = ComputePipeline::create(device, ComputePipelineInfo::default(), shader)?; +let info = ComputePipelineInfo::default(); +let code = include_bytes!("render_mesh.spv"); +let shader = Shader::new_compute(code.as_slice()); +let pipeline = ComputePipeline::create(device, info, shader)?; -Graph::default() +let mut graph = Graph::default(); +graph + .begin_cmd() .bind_pipeline(&pipeline) .record_cmd_buf(|cmd_buf| { cmd_buf .push_constants(0, 42u32.to_ne_bytes()) - .dispatch(8, 8, 1) + .dispatch(1, 1, 1); }); ``` + +> [!TIP] +> A crate such as `bytemuck` is helpful for converting Rust structures to bytes suitable for push +> constant usage. See the example code for more. diff --git a/guide/src/pipeline_spec.md b/guide/src/pipeline_spec.md index d8b68cef..05acd09b 100644 --- a/guide/src/pipeline_spec.md +++ b/guide/src/pipeline_spec.md @@ -14,22 +14,20 @@ required to use this feature. layout(constant_id = 0) const float INFERNO_EPSILON = 0.999; layout(constant_id = 1) const float COEFF_OF_BOOM = 1.4; - -... ``` ```rust // Use this shader for the glsl-specified values: let shader = Shader::new_compute(include_bytes!("kaboom.spv").as_slice()); -let specialization = SpecializationMap::new(bytemuck::bytes_of([ - 0.99999f32, - 1.0, - ])) +let better_consts = [ + 0.99999f32, + 1.0, +]; +let spec = SpecializationMap::new(bytemuck::bytes_of(better_consts)) .constant(0, 0, 4) .constant(1, 4, 8); // Use this shader for the updated run-time values: -let spec_shader = shader.specialization(specialization); -.... +let spec_shader = shader.specialization(spec); ``` diff --git a/guide/src/pipeline_sync.md b/guide/src/pipeline_sync.md index c782c089..d3dfd825 100644 --- a/guide/src/pipeline_sync.md +++ b/guide/src/pipeline_sync.md @@ -16,14 +16,6 @@ inserted into the command stream. Unsynchronized access results in undefined beh `vk-graph` uses an enumeration of possible states to define all supported pipeline barriers in an easy-to-use way. -An `AccessType` is composed of a name and three private fields: - -Field|Type --|- -`stage_mask`|`vk::PipelineStageFlags` -`access_mask`|`vk::AccessFlags` -`image_layout`|`vk::ImageLayout` - Sample access types: Type|Usage @@ -117,7 +109,7 @@ graph ## Subresource Access -Buffer ranges and image views are referred to as subresource ranges and bound using "subresource" +Buffer ranges and image views are referred to as subresource ranges and accessed using "subresource" function variants: ```rust diff --git a/guide/src/queue.md b/guide/src/queue.md deleted file mode 100644 index d095a2b0..00000000 --- a/guide/src/queue.md +++ /dev/null @@ -1 +0,0 @@ -# Queue diff --git a/guide/src/resource.md b/guide/src/resource.md index c5308660..924e4e21 100644 --- a/guide/src/resource.md +++ b/guide/src/resource.md @@ -1,29 +1,35 @@ # Resources > [!CAUTION] -> All pipelines and resources (_buffers, images, and acceleration structures_) "bound" to any -> `Graph` must have been created by the same `Device`. +> All pipelines and resources (_buffers, images, and acceleration structures_) used in a `Graph` +> must have been created using the same `Device`. Owned resources are created from `Device` references. They may be bound directly to graphs. -A borrow of `Arc` of any resource may be bound to a graph if the resource needs to be referenced -in future graphs. +An `Arc` or `&Arc` of any resource may be bound to a graph if the resource needs to be +referenced in future graphs. + +## Binding Binding resources to a graph produces a "Node" handle which may be used in commands and shader -pipelines. `Graph::bind_resource(resource: T) -> N`: +pipelines. + +Example for buffers using `Graph::bind_resource(&mut self, resource: R) -> N`: -`T`|`N` +`R`|`N` -|- `Buffer`|`BufferNode` `Arc`|`BufferNode` `Lease`|`BufferLeaseNode` `Arc>`|`BufferLeaseNode` -_(etc...)_ +## Borrowing + +Resources may be borrowed from a graph. -Resources may be borrowed from a graph. `Graph::resource(node: N) -> &T`: +Example for buffers using `Graph::resource(&self, node: N) -> &R`: -`N`|`T` +`N`|`R` -|- `BufferNode`|`Arc` `BufferLeaseNode`|`Arc>` diff --git a/guide/src/resource_buffer.md b/guide/src/resource_buffer.md index 7dc12fe2..e0ffa56a 100644 --- a/guide/src/resource_buffer.md +++ b/guide/src/resource_buffer.md @@ -35,13 +35,13 @@ let more_info = host_mem let data = [1u8, 2, 3, 4]; let buffer = Buffer::create_from_slice(device, usage, &data)?; -// This is equivalent to: +// This is equivalent to: let mut buffer = Buffer::create(device, host_mem)?; -buffer.mapped_slice_mut().copy_from_slice(&data); +buffer.copy_from_slice(&data); -// Or: +// Or use the std copy_from_slice (it panics if size != range) let mut buffer = Buffer::create(device, host_mem)?; -buffer.copy_from_slice(&data); +buffer.mapped_slice_mut().copy_from_slice(&data); // The provided fields are helpful: assert_eq!(buffer.device, *device); diff --git a/guide/src/resource_image.md b/guide/src/resource_image.md index 1dc42451..e22026f5 100644 --- a/guide/src/resource_image.md +++ b/guide/src/resource_image.md @@ -36,10 +36,9 @@ let same_info = ImageInfoBuilder::default() .ty(vk::ImageType::TYPE_2D); // Info built from other info -let cube_info = cube_info - .info +let array_info = cube_info .into_builder() - .flags(vk::ImageCreateFlags::CUBE_COMPATIBLE) + .flags(vk::ImageCreateFlags::TYPE_2D_ARRAY_COMPATIBLE) .build(); // Images are created simply diff --git a/guide/src/usage.md b/guide/src/usage.md index 64ccd71a..82289112 100644 --- a/guide/src/usage.md +++ b/guide/src/usage.md @@ -1,6 +1,6 @@ # Usage -`vk-graph` acts as a safe builder-pattern for Vulkan functions (_Example: [vkCmdDrawIndexed](https://docs.vulkan.org/refpages/latest/refpages/source/vkCmdDrawIndexed.html)_). +`vk-graph` acts as a safe builder-pattern for the Vulkan API. Typical usage contains: @@ -9,10 +9,6 @@ Typical usage contains: let device: &Device = &self.device; ``` - - - - ## Resources Resources, such as buffers and images, may be created from "`Info`" structs: @@ -20,11 +16,11 @@ Resources, such as buffers and images, may be created from "`Info`" structs: ```rust let usage = vk::BufferUsageFlags::TRANSFER_SRC; let buffer_info = BufferInfo::device_mem(320 * 200 * 4, usage); -let buffer: Buffer = Buffer::create(device, buffer_info)?; +let buffer = Buffer::create(device, buffer_info)?; let usage = vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST; let image_info = ImageInfo::image_2d(320, 200, vk::Format::R8G8B8A8_UNORM, usage)?; -let image: Image = Image::create(device, image_info)?; +let image = Image::create(device, image_info)?; ``` ### Memory Allocation @@ -85,7 +81,7 @@ graph .copy_buffer_to_image(buffer, image); ``` -Commands enable custom Vulkan behavior: +Custom commands enable advanced Vulkan behavior: ```rust graph diff --git a/guide/src/usage_debugging.md b/guide/src/usage_debugging.md index 17edf0c6..670b4b76 100644 --- a/guide/src/usage_debugging.md +++ b/guide/src/usage_debugging.md @@ -1,7 +1,8 @@ # Debugging Debug mode (setting the `debug` field of `DeviceInfo` or `InstanceInfo` to `true`) is supported only -when a compatible [Vulkan SDK](https://vulkan.lunarg.com/sdk/home) is installed. +when a compatible [_Vulkan SDK_](https://vulkan.lunarg.com/sdk/home) + is installed. > [!IMPORTANT] > The installed Vulkan SDK version must be at least v{{ vulkan_sdk.version }}. @@ -12,11 +13,10 @@ active thread to be parked and log a message indicating how to attach a debugger ## Logging -`vk-graph` uses [`log`](https://crates.io/crates/log) for low-overhead logging. +`vk-graph` uses `log` v{{ log.version }} for low-overhead logging. To enable logging, set the `RUST_LOG` environment variable to `trace`, `debug`, `info`, `warn` or -`error` and initialize the logging provider of your choice. Examples use -[`pretty_env_logger`](https://docs.rs/pretty_env_logger/latest/pretty_env_logger/). +`error` and initialize the logging provider of your choice. Examples use `pretty_env_logger`. _You may also filter messages, for example:_ @@ -35,13 +35,13 @@ DEBUG vk_graph::driver::physical_device > extension "VK_KHR_acceleration_structu ## Performance Profiling -`vk-graph` uses [`profiling`](https://crates.io/crates/profiling) and supports multiple profiling -providers. When not in use profiling has zero cost. +`vk-graph` uses `profiling` v{{ profiling.version }} and supports multiple profiling providers. When not in use profiling has +zero cost. To enable profiling, compile with one of the `profile-with-*` features enabled and initialize the profiling provider of your choice. -_Example code uses [puffin](https://crates.io/crates/puffin):_ +_Example using `puffin`:_ ```bash cargo run --features profile-with-puffin --release --example vsm_omni @@ -62,11 +62,16 @@ changes. This may be done on Intel Linux machines by modifying the Intel P-State echo 100 | sudo tee /sys/devices/system/cpu/intel_pstate/min_perf_pct ``` -(_[Source](https://www.kernel.org/doc/Documentation/cpu-freq/intel-pstate.txt)_) +([_Source_](https://www.kernel.org/doc/Documentation/cpu-freq/intel-pstate.txt) +) ## Helpful tools -- [VulkanSDK](https://vulkan.lunarg.com/sdk/home) _(Required when setting `debug` to `true`)_ -- NVIDIA: [nvidia-smi](https://developer.nvidia.com/nvidia-system-management-interface) -- AMD: [RadeonTop](https://github.com/clbr/radeontop) -- [RenderDoc](https://renderdoc.org/) +- [_VulkanSDK_](https://vulkan.lunarg.com/sdk/home) + +_(Required when setting `debug` to `true`)_ +- NVIDIA: [_nvidia-smi_](https://developer.nvidia.com/nvidia-system-management-interface) + +- AMD: [_RadeonTop_](https://github.com/clbr/radeontop) + +- [_RenderDoc_](https://renderdoc.org/) diff --git a/guide/src/usage_device.md b/guide/src/usage_device.md index ff2c7857..892e5917 100644 --- a/guide/src/usage_device.md +++ b/guide/src/usage_device.md @@ -15,12 +15,13 @@ For any sort of server-based rendering or similar Vulkan usage without a display production-ready code used to create a device: ```rust -// The default information is: -// DeviceInfo { -// debug: false, -// phyiscal_device_index: 0, -// } -let device = Device::new(DeviceInfo::default())?; +let info = DeviceInfo::default(); +let device = Device::new(info)?; + +assert_eq!(device.info, DeviceInfo { + debug: false, + physical_device_index: 0, +}); ``` ## Windowed Operation @@ -106,5 +107,6 @@ let device = Device::try_from_ash_device(device, physical_device)?; ``` > [!TIP] -> See [`examples/vr`](https://github.com/attackgoat/vk-graph/tree/main/examples/vr) for an in-depth -> example of native device usage. +> See [_`examples/vr`_](https://github.com/attackgoat/vk-graph/tree/main/examples/vr) +> for an in-depth example of native device +> usage. diff --git a/guide/src/usage_moltenvk.md b/guide/src/usage_moltenvk.md index 5f12ea30..0806b0fa 100644 --- a/guide/src/usage_moltenvk.md +++ b/guide/src/usage_moltenvk.md @@ -1,3 +1,15 @@ # MoltenVK -TODO +Vulkan is emulated on Apple platforms using MoltenVK. + +> [!WARNING] +> MoltenVK does not support all Vulkan features and has limited extension and format support. Pay +> particular attention to these areas: +> - Bindless descriptor count limit +> - Hardware queues provided for execution +> - Indirect drawing command support +> - Image format support + +Support for MoltenVK is best-effort and may not always be up to date. In the event that any +`vk-graph` workflow does not work using MoltenVK please +[_open an issue_](https://github.com/attackgoat/vk-graph/issues) . diff --git a/guide/src/usage_shader.md b/guide/src/usage_shader.md index 8f700117..b29a7bdc 100644 --- a/guide/src/usage_shader.md +++ b/guide/src/usage_shader.md @@ -7,7 +7,9 @@ provide SPIR-V binary-format shaders. > See [Hot Reload](./pipeline_hot_reload.md) for details on a shader compiler provided as a separate > crate. -Examples using multiple shading languages and compilers are provided in the [`examples/`](https://github.com/attackgoat/vk-graph/tree/main/examples) directory. +Examples using multiple shading languages and compilers are provided in the +[_`examples/`_](https://github.com/attackgoat/vk-graph/tree/main/examples) + directory. ## Shader-stage `#pragma` diff --git a/guide/src/usage_thread.md b/guide/src/usage_thread.md index affbc44c..48dfe033 100644 --- a/guide/src/usage_thread.md +++ b/guide/src/usage_thread.md @@ -21,11 +21,13 @@ Resource state is updated during the following function calls: ## Execution -The provided `Queue` submission functions are designed to this workflow: -- Submit all commands the swapchain depends on -- Acquire swapchain -- Submit swapchain commands -- Submit any final unrelated commands +The provided `Queue` submission functions are designed to support a typical swapchain-based +workflow: +1. Submit all commands the swapchain depends on +1. Acquire swapchain +1. Submit swapchain commands +1. Present swapchain +1. Submit any final unrelated commands ## Safe Patterns @@ -53,10 +55,10 @@ The contents of a buffer are undefined from the time of submission until that `Q fully executed, as indicated by `CommandBuffer::has_executed`. This means that you should not call `Buffer::mapped_slice` during any submission or execution accessing that memory. -_See: -[`examples/cpu_readback.rs`](https://github.com/attackgoat/vk-graph/blob/main/examples/cpu_readback.rs)_ +See: +[_`examples/cpu_readback.rs`_](https://github.com/attackgoat/vk-graph/blob/main/examples/cpu_readback.rs) + [^threads]: The internal implementation of `GraphicPipeline` does do a bit of caching in order to -improve performance, however this behavior should never generate issues with any fathomable -workload. +improve performance, however this behavior should not generate issues with any reasonable workload. diff --git a/guide/src/usage_window.md b/guide/src/usage_window.md index 87906f11..02185b2b 100644 --- a/guide/src/usage_window.md +++ b/guide/src/usage_window.md @@ -4,8 +4,9 @@ `vk-graph-window` is provided, based on `winit`. > [!TIP] -> [`vk-graph-window`](https://github.com/attackgoat/vk-graph/tree/main/crates/vk-graph-window) -> provides additional documentation and examples. +> [_`vk-graph-window`_ ](https://github.com/attackgoat/vk-graph/tree/main/crates/vk-graph-window) +> provides additional documentation and +> examples. ## Swapchain @@ -20,5 +21,6 @@ Type | Usage ### OpenXR Virtual reality support via OpenXR is provided as -[an example](https://github.com/attackgoat/vk-graph/tree/main/examples/vr) which also implements a +[_an example_](https://github.com/attackgoat/vk-graph/tree/main/examples/vr) + which also implements a swapchain. diff --git a/src/cmd/cmd_buf.rs b/src/cmd/cmd_buf.rs index a37fe2ec..9e0534f0 100644 --- a/src/cmd/cmd_buf.rs +++ b/src/cmd/cmd_buf.rs @@ -1,6 +1,6 @@ use { crate::{ - AnyAccelerationStructureNode, GraphNode, Resource, + AnyAccelerationStructureNode, Resource, driver::{ accel_struct::{ AccelerationStructureGeometry, AccelerationStructureGeometryInfo, @@ -477,7 +477,7 @@ impl<'a> CommandBufferRef<'a> { /// which the given bound resource represents. pub fn resource(&self, node: N) -> &N::Resource where - N: GraphNode + Node, + N: Node, { // You must have called an access function for this node on this execution before indexing // into the bindings data! diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index fd714e79..71bad1bf 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -24,12 +24,9 @@ use { ExecutionFunction, Graph, GraphResource, ImageLeaseNode, ImageNode, Node, Resource, SwapchainImageNode, }, - crate::{ - driver::{ - accel_struct::AccelerationStructureSubresourceRange, buffer::BufferSubresourceRange, - image::ImageViewInfo, - }, - resource::GraphNode, + crate::driver::{ + accel_struct::AccelerationStructureSubresourceRange, buffer::BufferSubresourceRange, + image::ImageViewInfo, }, ash::vk, std::ops::Range, @@ -186,7 +183,7 @@ impl<'a> CommandRef<'a> { /// which the given bound resource represents. pub fn resource(&self, node: N) -> &N::Resource where - N: GraphNode, + N: Node, { self.graph.resource(node) } diff --git a/src/cmd/pipeline.rs b/src/cmd/pipeline.rs index fbad14e6..98f0c4e1 100644 --- a/src/cmd/pipeline.rs +++ b/src/cmd/pipeline.rs @@ -1,7 +1,7 @@ use { super::{ - AccessType, CommandRef, Descriptor, Graph, GraphNode, GraphResource, Node, - SubresourceRange, View, ViewInfo, + AccessType, CommandRef, Descriptor, Graph, GraphResource, Node, SubresourceRange, View, + ViewInfo, }, crate::{ ExecutionPipeline, @@ -121,7 +121,7 @@ impl<'c, T> PipelineCommandRef<'c, T> { /// which the given node represents. pub fn resource(&self, node: N) -> &N::Resource where - N: GraphNode, + N: Node, { self.cmd.resource(node) } diff --git a/src/driver/swapchain.rs b/src/driver/swapchain.rs index cf59bbda..0144de58 100644 --- a/src/driver/swapchain.rs +++ b/src/driver/swapchain.rs @@ -17,8 +17,8 @@ use { #[doc(hidden)] #[repr(C)] pub struct ReadOnlySwapchainImage { - pub idx: u32, image: Image, + pub index: u32, } #[doc(hidden)] @@ -206,7 +206,7 @@ impl Swapchain { let present_info = vk::PresentInfoKHR::default() .wait_semaphores(wait_semaphores) .swapchains(slice::from_ref(&self.handle)) - .image_indices(slice::from_ref(&image.idx)); + .image_indices(slice::from_ref(&image.index)); let swapchain_ext = Device::expect_swapchain_ext(&self.surface.device); @@ -237,7 +237,7 @@ impl Swapchain { } } - let image_idx = image.idx as usize; + let image_idx = image.index as usize; self.images[image_idx] = image; } @@ -334,8 +334,8 @@ impl Swapchain { image.name = Some(format!("swapchain{image_idx}")); Ok(SwapchainImage { - idx: image_idx, image, + index: image_idx, }) }) .collect::, _>>()?; @@ -454,25 +454,25 @@ pub enum SwapchainError { #[derive(Debug)] #[repr(C)] pub struct SwapchainImage { + image: Image, + /// The index of this swapchain among the other swapchain images. /// /// _Note:_ This field is read-only. #[cfg(doc)] - pub idx: u32, + pub index: u32, #[cfg(not(doc))] - idx: u32, - - image: Image, + index: u32, } impl Clone for SwapchainImage { fn clone(&self) -> Self { - let Self { idx, image } = self; + let Self { image, index } = self; Self { - idx: *idx, image: image.clone_swapchain(), + index: *index, } } } @@ -669,8 +669,8 @@ mod test { size_of::() ); assert_eq!( - offset_of!(SwapchainImage, idx), - offset_of!(ReadOnlySwapchainImage, idx), + offset_of!(SwapchainImage, index), + offset_of!(ReadOnlySwapchainImage, index), ); assert_eq!( offset_of!(SwapchainImage, image), diff --git a/src/lib.rs b/src/lib.rs index 2eb92cde..e9c63e4d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,10 @@ /*! -This crate provides a high performance [Vulkan](https://www.vulkan.org/) graphics driver with -automatic resource management and execution. +This crate provides a high-performance [Vulkan](https://www.vulkan.org/) driver featuring automated +resource management and execution. -The provided graph structure may be used to compose any type of graphics algorithm using driver -resources (_buffers, images, and acceleration structures_) and shader pipelines. Some -implementations of common graphics patterns are provided in the `contrib` directory. +For a general overview, including installation and typical usage, see the +[Guide Book](https://attackgoat.github.io/vk-graph). # Getting Sarted @@ -363,7 +362,7 @@ use crate::cmd::CommandBufferRef; pub use self::{ queue::Queue, - resource::{GraphNode, GraphResource, Resource}, + resource::{GraphResource, Resource}, }; #[allow(deprecated)] @@ -1254,6 +1253,9 @@ impl Graph { pub fn into_queue(mut self) -> Queue { // The final execution of each pass has no function for cmd in &mut self.cmds { + debug_assert!(!cmd.execs.is_empty()); + debug_assert!(cmd.execs.last().unwrap().func.is_none()); + cmd.execs.pop(); } @@ -1264,7 +1266,7 @@ impl Graph { /// which the given node represents. pub fn resource(&self, node: N) -> &N::Resource where - N: GraphNode, + N: Node, { node.borrow(&self.resources) } @@ -1343,7 +1345,7 @@ pub mod graph { pub type ImageNode = crate::node::ImageNode; #[deprecated = "use vk_graph::node::Node"] - pub type Node = dyn crate::node::Node; + pub type Node = dyn crate::node::Node; #[deprecated = "use vk_graph::node::SwapchainImageNode"] pub type SwapchainImageNode = crate::node::SwapchainImageNode; diff --git a/src/node.rs b/src/node.rs index 29da4143..4d16a2fb 100644 --- a/src/node.rs +++ b/src/node.rs @@ -1,6 +1,16 @@ //! Bindings for Vulkan smart-pointer resources. -use crate::NodeIndex; +use std::sync::Arc; + +use crate::{ + driver::{ + accel_struct::AccelerationStructure, buffer::Buffer, image::Image, + swapchain::SwapchainImage, + }, + pool::Lease, +}; + +use super::{NodeIndex, Resource, resource::ResourceInner}; /// Specifies either an owned acceleration structure or an acceleration structure leased from a /// pool. @@ -26,6 +36,12 @@ impl From for AnyAccelerationStructureNode { } impl Node for AnyAccelerationStructureNode { + type Resource = AccelerationStructure; + + fn borrow(self, resources: &[Resource]) -> &Self::Resource { + resources[self.index()].as_driver_accel_struct().unwrap() + } + fn index(&self) -> NodeIndex { match self { Self::AccelerationStructure(node) => node.index(), @@ -57,6 +73,12 @@ impl From for AnyBufferNode { } impl Node for AnyBufferNode { + type Resource = Buffer; + + fn borrow(self, resources: &[Resource]) -> &Self::Resource { + resources[self.index()].as_driver_buffer().unwrap() + } + fn index(&self) -> NodeIndex { match self { Self::Buffer(node) => node.index(), @@ -99,6 +121,12 @@ impl From for AnyImageNode { } impl Node for AnyImageNode { + type Resource = Image; + + fn borrow(self, resources: &[Resource]) -> &Self::Resource { + resources[self.index()].as_driver_image().unwrap() + } + fn index(&self) -> NodeIndex { match self { Self::Image(node) => node.index(), @@ -110,12 +138,18 @@ impl Node for AnyImageNode { /// A Vulkan resource which has been bound to a [`Graph`] using [`Graph::bind_node`]. pub trait Node { + /// The Vulkan buffer, image, or acceleration struction type. + type Resource; + + #[doc(hidden)] + fn borrow(self, resources: &[Resource]) -> &Self::Resource; + #[doc(hidden)] fn index(&self) -> NodeIndex; } macro_rules! node { - ($name:ident) => { + ($name:ident, $resource:ty, $fn_name:ident) => { paste::paste! { /// Resource node. #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -132,6 +166,16 @@ macro_rules! node { } impl Node for [<$name Node>] { + type Resource = $resource; + + fn borrow(self, resources: &[Resource]) -> &Self::Resource { + let Resource { inner: ResourceInner::$name(res), } = &resources[self.idx] else { + panic!("invalid resource node handle"); + }; + + res + } + fn index(&self) -> NodeIndex { self.idx } @@ -140,10 +184,18 @@ macro_rules! node { }; } -node!(AccelerationStructure); -node!(AccelerationStructureLease); -node!(Buffer); -node!(BufferLease); -node!(Image); -node!(ImageLease); -node!(SwapchainImage); +node!( + AccelerationStructure, + Arc, + as_driver_accel_struct +); +node!( + AccelerationStructureLease, + Arc>, + as_driver_accel_struct +); +node!(Buffer, Arc, as_driver_buffer); +node!(BufferLease, Arc>, as_driver_buffer); +node!(Image, Arc, as_driver_image); +node!(ImageLease, Arc>, as_driver_image); +node!(SwapchainImage, SwapchainImage, as_swapchain_image); diff --git a/src/queue.rs b/src/queue.rs index 957c1ab3..22fb8129 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -5,7 +5,6 @@ use { resource::ResourceInner, }, crate::{ - GraphNode, cmd::CommandBufferRef, driver::{ AttachmentInfo, AttachmentRef, Descriptor, DescriptorInfo, DescriptorSet, DriverError, @@ -2632,7 +2631,7 @@ impl Queue { /// which the given node represents. pub fn resource(&self, node: N) -> &N::Resource where - N: GraphNode, + N: Node, { self.graph.resource(node) } diff --git a/src/resource.rs b/src/resource.rs index d1683c33..a274cb2d 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -1,94 +1,18 @@ use { super::{ - AccelerationStructureLeaseNode, AccelerationStructureNode, AnyAccelerationStructureNode, - AnyBufferNode, AnyImageNode, BufferLeaseNode, BufferNode, Graph, ImageLeaseNode, ImageNode, - SwapchainImageNode, + AccelerationStructureLeaseNode, AccelerationStructureNode, BufferLeaseNode, BufferNode, + Graph, ImageLeaseNode, ImageNode, SwapchainImageNode, }, crate::{ driver::{ accel_struct::AccelerationStructure, buffer::Buffer, image::Image, swapchain::SwapchainImage, }, - node::Node, pool::Lease, }, std::{fmt::Debug, sync::Arc}, }; -/// A trait for resources which may be borrowed from a `Graph`. -/// -/// See [`Graph::node`] for details. -pub trait GraphNode { - /// The Vulkan buffer, image, or acceleration struction type. - type Resource; - - #[doc(hidden)] - fn borrow(self, resources: &[Resource]) -> &Self::Resource; -} - -impl GraphNode for AnyAccelerationStructureNode { - type Resource = AccelerationStructure; - - fn borrow(self, resources: &[Resource]) -> &Self::Resource { - resources[self.index()].as_driver_accel_struct().unwrap() - } -} - -impl GraphNode for AnyBufferNode { - type Resource = Buffer; - - fn borrow(self, resources: &[Resource]) -> &Self::Resource { - resources[self.index()].as_driver_buffer().unwrap() - } -} - -impl GraphNode for AnyImageNode { - type Resource = Image; - - fn borrow(self, resources: &[Resource]) -> &Self::Resource { - resources[self.index()].as_driver_image().unwrap() - } -} - -impl GraphNode for SwapchainImageNode { - type Resource = SwapchainImage; - - fn borrow(self, resources: &[Resource]) -> &Self::Resource { - resources[self.idx].as_swapchain_image().unwrap() - } -} - -macro_rules! graph_node { - ($name:ident) => { - paste::paste! { - impl GraphNode for [<$name Node>] { - type Resource = Arc<$name>; - - fn borrow(self, resources: &[Resource]) -> &Self::Resource { - - resources[self.idx] - .[]() - .unwrap() - } - } - - impl GraphNode for [<$name LeaseNode>] { - type Resource = Arc>; - - fn borrow(self, resources: &[Resource]) -> &Self::Resource { - resources[self.idx] - .[]() - .unwrap() - } - } - } - }; -} - -graph_node!(AccelerationStructure); -graph_node!(Buffer); -graph_node!(Image); - /// A trait for resources which may be bound to a `Graph`. /// /// See [`Graph::bind_resource`] and @@ -288,8 +212,8 @@ graph_resource!(AccelerationStructure); graph_resource!(Image); graph_resource!(Buffer); -/// Opaque wrapper for Vulkan resource types. #[derive(Debug)] +#[doc(hidden)] pub struct Resource { pub(crate) inner: ResourceInner, } From ca58d8ceb05fc35c8b16e2d2636136427d25ea79 Mon Sep 17 00:00:00 2001 From: John Wells Date: Wed, 18 Mar 2026 21:19:01 -0400 Subject: [PATCH 46/86] Move Node trait --- src/cmd/cmd_buf.rs | 7 +- src/cmd/compute.rs | 2 +- src/cmd/graphic.rs | 96 ++++++++++- src/cmd/mod.rs | 31 ++-- src/cmd/pipeline.rs | 10 +- src/cmd/ray_trace.rs | 2 +- src/lib.rs | 400 +++++++++++++++++++++++++++++++------------ src/node.rs | 33 ++-- src/queue.rs | 16 +- src/resource.rs | 264 ---------------------------- 10 files changed, 422 insertions(+), 439 deletions(-) delete mode 100644 src/resource.rs diff --git a/src/cmd/cmd_buf.rs b/src/cmd/cmd_buf.rs index 9e0534f0..864dda34 100644 --- a/src/cmd/cmd_buf.rs +++ b/src/cmd/cmd_buf.rs @@ -1,6 +1,6 @@ use { crate::{ - AnyAccelerationStructureNode, Resource, + AnyAccelerationStructureNode, AnyResource, Node, driver::{ accel_struct::{ AccelerationStructureGeometry, AccelerationStructureGeometryInfo, @@ -9,7 +9,6 @@ use { cmd_buf::CommandBuffer, device::Device, }, - node::Node, }, ash::vk, std::{cell::RefCell, ops::Deref}, @@ -53,13 +52,13 @@ pub struct CommandBufferRef<'a> { #[cfg(debug_assertions)] exec: &'a Execution, - resources: &'a [Resource], + resources: &'a [AnyResource], } impl<'a> CommandBufferRef<'a> { pub(crate) fn new( cmd_buf: &'a CommandBuffer, - resources: &'a [Resource], + resources: &'a [AnyResource], #[cfg(debug_assertions)] exec: &'a Execution, ) -> Self { Self { diff --git a/src/cmd/compute.rs b/src/cmd/compute.rs index 85c67ac7..7eac8aa1 100644 --- a/src/cmd/compute.rs +++ b/src/cmd/compute.rs @@ -363,12 +363,12 @@ impl PipelineCommandRef<'_, ComputePipeline> { mod deprecated { use { crate::{ + Node, cmd::{ Descriptor, PipelineCommandRef, SubresourceRange, View, ViewInfo, compute::ComputeCommandBufferRef, }, driver::compute::ComputePipeline, - node::Node, }, std::any::Any, vk_sync::AccessType, diff --git a/src/cmd/graphic.rs b/src/cmd/graphic.rs index d663bfa0..7bd7870e 100644 --- a/src/cmd/graphic.rs +++ b/src/cmd/graphic.rs @@ -1,7 +1,6 @@ use { super::{AttachmentIndex, cmd_buf::CommandBufferRef, pipeline::PipelineCommandRef}, crate::{ - ClearColorValue, driver::{ graphic::{DepthStencilInfo, GraphicPipeline}, image::ImageViewInfo, @@ -14,6 +13,93 @@ use { std::{cell::RefCell, ops::Deref, slice}, }; +/// TODO +#[derive(Clone, Copy, Debug)] +pub enum ClearColorValue { + /// Value as [f32]. + Float32([f32; 4]), + + /// Value as [i32]. + Int32([i32; 4]), + + /// Value as [u32]. + Uint32([u32; 4]), +} + +impl ClearColorValue { + /// rgb zeros and alpha ones. + pub const BLACK_ALPHA_ONE: Self = Self::Float32([0.0, 0.0, 0.0, 1.0]); + + /// zeros. + pub const BLACK_ALPHA_ZERO: Self = Self::Float32([0.0, 0.0, 0.0, 0.0]); + + /// rgb zeros and alpha ones. + pub const WHITE_ALPHA_ONE: Self = Self::Float32([1.0, 1.0, 1.0, 1.0]); + + /// rgb ones and alpha zeros. + pub const WHITE_ALPHA_ZERO: Self = Self::Float32([1.0, 1.0, 1.0, 0.0]); + + /// Convenience constructor for clear color values. + pub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self { + Self::Float32([r, g, b, a]) + } + + /// Convert RGB+A values into a ClearColorValue. + pub const fn from_f32(r: f32, g: f32, b: f32, a: f32) -> Self { + Self::rgba(r, g, b, a) + } + + /// Convert RGB+A values into a ClearColorValue. + pub const fn from_u8(r: u8, g: u8, b: u8, a: u8) -> Self { + Self::from_f32( + r as f32 / u8::MAX as f32, + g as f32 / u8::MAX as f32, + b as f32 / u8::MAX as f32, + a as f32 / u8::MAX as f32, + ) + } +} + +impl Default for ClearColorValue { + fn default() -> Self { + Self::from_f32(0.0, 0.0, 0.0, 0.0) + } +} + +impl From<[f32; 4]> for ClearColorValue { + fn from(float32: [f32; 4]) -> Self { + Self::Float32(float32) + } +} + +impl From<[i32; 4]> for ClearColorValue { + fn from(int32: [i32; 4]) -> Self { + Self::Int32(int32) + } +} + +impl From<[u8; 4]> for ClearColorValue { + fn from(uint8: [u8; 4]) -> Self { + Self::from_u8(uint8[0], uint8[1], uint8[2], uint8[3]) + } +} + +impl From<[u32; 4]> for ClearColorValue { + fn from(uint32: [u32; 4]) -> Self { + Self::Uint32(uint32) + } +} + +impl From for vk::ClearColorValue { + fn from(value: ClearColorValue) -> Self { + match value { + ClearColorValue::Float32(float32) => Self { float32 }, + ClearColorValue::Int32(int32) => Self { int32 }, + ClearColorValue::Uint32(uint32) => Self { uint32 }, + } + } +} + /// TODO #[derive(Clone, Copy, Debug)] pub enum LoadOp { @@ -1135,10 +1221,10 @@ impl PipelineCommandRef<'_, GraphicPipeline> { mod deprecated { use { crate::{ - Attachment, ClearColorValue, SubresourceAccess, + Attachment, Node, SubresourceAccess, cmd::{ - AttachmentIndex, Descriptor, PipelineCommandRef, SubresourceRange, View, ViewInfo, - graphic::GraphicCommandBufferRef, + AttachmentIndex, ClearColorValue, Descriptor, PipelineCommandRef, SubresourceRange, + View, ViewInfo, graphic::GraphicCommandBufferRef, }, driver::{ graphic::GraphicPipeline, @@ -1148,7 +1234,7 @@ mod deprecated { }, render_pass::ResolveMode, }, - node::{AnyImageNode, Node}, + node::AnyImageNode, }, ash::vk, vk_sync::AccessType, diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index 71bad1bf..1476749c 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -12,7 +12,7 @@ pub use self::{ UpdateAccelerationStructureIndirectInfo, UpdateAccelerationStructureInfo, }, compute::ComputeCommandBufferRef, - graphic::{GraphicCommandBufferRef, LoadOp, StoreOp}, + graphic::{ClearColorValue, GraphicCommandBufferRef, LoadOp, StoreOp}, pipeline::{CommandPipeline, PipelineCommandRef}, ray_trace::RayTraceCommandBufferRef, }; @@ -20,9 +20,8 @@ pub use self::{ use { super::{ AccelerationStructureLeaseNode, AccelerationStructureNode, AnyAccelerationStructureNode, - AnyBufferNode, AnyImageNode, BufferLeaseNode, BufferNode, Command, Execution, - ExecutionFunction, Graph, GraphResource, ImageLeaseNode, ImageNode, Node, Resource, - SwapchainImageNode, + AnyBufferNode, AnyImageNode, AnyResource, BufferLeaseNode, BufferNode, Command, Execution, + ExecutionFunction, Graph, ImageLeaseNode, ImageNode, Node, Resource, SwapchainImageNode, }, crate::driver::{ accel_struct::AccelerationStructureSubresourceRange, buffer::BufferSubresourceRange, @@ -82,7 +81,7 @@ impl<'a> CommandRef<'a> { /// Bound nodes may be used in passes for pipeline and shader operations. pub fn bind_resource(&mut self, resource: R) -> R::Node where - R: GraphResource, + R: Resource, { self.graph.bind_resource(resource) } @@ -370,6 +369,7 @@ impl From for SubresourceRange { Self::Image(subresource.into()) } } + impl From for SubresourceRange { fn from(subresource: vk::ImageSubresourceRange) -> Self { Self::Image(subresource) @@ -391,12 +391,12 @@ pub trait View { type Range; #[doc(hidden)] - fn info(&self, _: &[Resource]) -> Self::Info + fn info(&self, _: &[AnyResource]) -> Self::Info where Self: Node; #[doc(hidden)] - fn range(&self, _: &[Resource]) -> Self::Range + fn range(&self, _: &[AnyResource]) -> Self::Range where Self: Node; } @@ -407,14 +407,14 @@ macro_rules! view_accel_struct { type Info = Self::Range; type Range = AccelerationStructureSubresourceRange; - fn info(&self, resources: &[Resource]) -> Self::Info + fn info(&self, resources: &[AnyResource]) -> Self::Info where Self: Node, { self.range(resources) } - fn range(&self, _: &[Resource]) -> Self::Range + fn range(&self, _: &[AnyResource]) -> Self::Range where Self: Node, { @@ -434,14 +434,14 @@ macro_rules! view_buffer { type Info = Self::Range; type Range = BufferSubresourceRange; - fn info(&self, resources: &[Resource]) -> Self::Info + fn info(&self, resources: &[AnyResource]) -> Self::Info where Self: Node, { self.range(resources) } - fn range(&self, resources: &[Resource]) -> Self::Range + fn range(&self, resources: &[AnyResource]) -> Self::Range where Self: Node, { @@ -463,7 +463,7 @@ macro_rules! view_image { type Info = ImageViewInfo; type Range = vk::ImageSubresourceRange; - fn info(&self, resources: &[Resource]) -> Self::Info + fn info(&self, resources: &[AnyResource]) -> Self::Info where Self: Node, { @@ -472,7 +472,7 @@ macro_rules! view_image { resources[idx].as_image().unwrap().info.into() } - fn range(&self, resources: &[Resource]) -> Self::Range + fn range(&self, resources: &[AnyResource]) -> Self::Range where Self: Node, { @@ -549,10 +549,9 @@ impl From> for ViewInfo { mod deprecated { use { crate::{ - Graph, GraphResource, + Graph, Node, Resource, cmd::{CommandBufferRef, CommandRef, SubresourceRange, View}, deprecated::Info, - node::Node, }, ash::vk, vk_sync::AccessType, @@ -641,7 +640,7 @@ mod deprecated { #[doc(hidden)] pub fn bind_node(&mut self, resource: R) -> R::Node where - R: GraphResource, + R: Resource, { self.bind_resource(resource) } diff --git a/src/cmd/pipeline.rs b/src/cmd/pipeline.rs index 98f0c4e1..0b9067a1 100644 --- a/src/cmd/pipeline.rs +++ b/src/cmd/pipeline.rs @@ -1,7 +1,6 @@ use { super::{ - AccessType, CommandRef, Descriptor, Graph, GraphResource, Node, SubresourceRange, View, - ViewInfo, + AccessType, CommandRef, Descriptor, Graph, Node, Resource, SubresourceRange, View, ViewInfo, }, crate::{ ExecutionPipeline, @@ -107,7 +106,7 @@ impl<'c, T> PipelineCommandRef<'c, T> { /// Bound nodes may be used in passes for pipeline and shader operations. pub fn bind_resource(&mut self, resource: R) -> R::Node where - R: GraphResource, + R: Resource, { self.cmd.bind_resource(resource) } @@ -299,11 +298,10 @@ impl<'c, T> PipelineCommandRef<'c, T> { mod deprecated { use { crate::{ - Graph, GraphResource, + Graph, Node, Resource, cmd::{Descriptor, PipelineCommandRef, SubresourceRange, View, ViewInfo}, deprecated::Info, graph::pass_ref::ViewType, - node::Node, }, ash::vk, vk_sync::AccessType, @@ -411,7 +409,7 @@ mod deprecated { #[doc(hidden)] pub fn bind_node(&mut self, resource: R) -> R::Node where - R: GraphResource, + R: Resource, { self.bind_resource(resource) } diff --git a/src/cmd/ray_trace.rs b/src/cmd/ray_trace.rs index 815bda1c..294269fa 100644 --- a/src/cmd/ray_trace.rs +++ b/src/cmd/ray_trace.rs @@ -333,12 +333,12 @@ impl<'a> Deref for RayTraceCommandBufferRef<'a> { mod deprecated { use { crate::{ + Node, cmd::{ Descriptor, PipelineCommandRef, SubresourceRange, View, ViewInfo, ray_trace::RayTraceCommandBufferRef, }, driver::ray_trace::RayTracePipeline, - node::Node, }, vk_sync::AccessType, }; diff --git a/src/lib.rs b/src/lib.rs index e9c63e4d..4a239b79 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -356,22 +356,26 @@ pub mod node; pub mod pool; mod queue; -mod resource; -use crate::cmd::CommandBufferRef; +use std::sync::Arc; -pub use self::{ - queue::Queue, - resource::{GraphResource, Resource}, +use crate::{ + cmd::{ClearColorValue, CommandBufferRef}, + driver::{ + accel_struct::AccelerationStructure, buffer::Buffer, image::Image, + swapchain::SwapchainImage, + }, + pool::Lease, }; +pub use self::queue::Queue; + #[allow(deprecated)] pub use self::deprecated::{Display, DisplayInfo, DisplayInfoBuilder}; use { self::{ cmd::{AttachmentIndex, CommandRef, Descriptor, SubresourceAccess, ViewInfo}, - node::Node, node::{ AccelerationStructureLeaseNode, AccelerationStructureNode, AnyAccelerationStructureNode, AnyBufferNode, AnyImageNode, BufferLeaseNode, BufferNode, @@ -402,6 +406,46 @@ use { type ExecFn = Box; type NodeIndex = usize; +#[derive(Debug)] +#[doc(hidden)] +pub struct AnyResource { + pub(crate) inner: ResourceInner, +} + +impl AnyResource { + fn as_driver_accel_struct(&self) -> Option<&AccelerationStructure> { + Some(match &self.inner { + ResourceInner::AccelerationStructure(resource) => resource, + ResourceInner::AccelerationStructureLease(resource) => resource, + _ => return None, + }) + } + + fn as_driver_buffer(&self) -> Option<&Buffer> { + Some(match &self.inner { + ResourceInner::Buffer(resource) => resource, + ResourceInner::BufferLease(resource) => resource, + _ => return None, + }) + } + + fn as_driver_image(&self) -> Option<&Image> { + Some(match &self.inner { + ResourceInner::Image(resource) => resource, + ResourceInner::ImageLease(resource) => resource, + ResourceInner::SwapchainImage(resource) => resource, + _ => return None, + }) + } + + fn as_swapchain_image(&self) -> Option<&SwapchainImage> { + Some(match &self.inner { + ResourceInner::SwapchainImage(resource) => resource, + _ => return None, + }) + } +} + #[derive(Clone, Copy, Debug)] struct Attachment { array_layer_count: u32, @@ -463,93 +507,6 @@ impl Attachment { } } -/// TODO -#[derive(Clone, Copy, Debug)] -pub enum ClearColorValue { - /// Value as [f32]. - Float32([f32; 4]), - - /// Value as [i32]. - Int32([i32; 4]), - - /// Value as [u32]. - Uint32([u32; 4]), -} - -impl ClearColorValue { - /// rgb zeros and alpha ones. - pub const BLACK_ALPHA_ONE: Self = Self::Float32([0.0, 0.0, 0.0, 1.0]); - - /// zeros. - pub const BLACK_ALPHA_ZERO: Self = Self::Float32([0.0, 0.0, 0.0, 0.0]); - - /// rgb zeros and alpha ones. - pub const WHITE_ALPHA_ONE: Self = Self::Float32([1.0, 1.0, 1.0, 1.0]); - - /// rgb ones and alpha zeros. - pub const WHITE_ALPHA_ZERO: Self = Self::Float32([1.0, 1.0, 1.0, 0.0]); - - /// Convenience constructor for clear color values. - pub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self { - Self::Float32([r, g, b, a]) - } - - /// Convert RGB+A values into a ClearColorValue. - pub const fn from_f32(r: f32, g: f32, b: f32, a: f32) -> Self { - Self::rgba(r, g, b, a) - } - - /// Convert RGB+A values into a ClearColorValue. - pub const fn from_u8(r: u8, g: u8, b: u8, a: u8) -> Self { - Self::from_f32( - r as f32 / u8::MAX as f32, - g as f32 / u8::MAX as f32, - b as f32 / u8::MAX as f32, - a as f32 / u8::MAX as f32, - ) - } -} - -impl Default for ClearColorValue { - fn default() -> Self { - Self::from_f32(0.0, 0.0, 0.0, 0.0) - } -} - -impl From<[f32; 4]> for ClearColorValue { - fn from(float32: [f32; 4]) -> Self { - Self::Float32(float32) - } -} - -impl From<[i32; 4]> for ClearColorValue { - fn from(int32: [i32; 4]) -> Self { - Self::Int32(int32) - } -} - -impl From<[u8; 4]> for ClearColorValue { - fn from(uint8: [u8; 4]) -> Self { - Self::from_u8(uint8[0], uint8[1], uint8[2], uint8[3]) - } -} - -impl From<[u32; 4]> for ClearColorValue { - fn from(uint32: [u32; 4]) -> Self { - Self::Uint32(uint32) - } -} - -impl From for vk::ClearColorValue { - fn from(value: ClearColorValue) -> Self { - match value { - ClearColorValue::Float32(float32) => Self { float32 }, - ClearColorValue::Int32(int32) => Self { int32 }, - ClearColorValue::Uint32(uint32) => Self { uint32 }, - } - } -} - #[derive(Default)] struct Execution { accesses: HashMap>, @@ -694,7 +651,7 @@ impl Command { #[derive(Debug, Default)] pub struct Graph { cmds: Vec, - resources: Vec, + resources: Vec, } impl Graph { @@ -714,7 +671,7 @@ impl Graph { /// general functions. pub fn bind_resource(&mut self, resource: R) -> R::Node where - R: GraphResource, + R: Resource, { resource.bind_graph(self) } @@ -1312,6 +1269,228 @@ impl Graph { } } +/// A Vulkan resource which has been bound to a [`Graph`] using [`Graph::bind_node`]. +pub trait Node { + /// The Vulkan buffer, image, or acceleration struction type. + type Resource; + + #[doc(hidden)] + fn borrow(self, resources: &[AnyResource]) -> &Self::Resource; + + #[doc(hidden)] + fn index(&self) -> NodeIndex; +} + +/// A trait for resources which may be bound to a `Graph`. +/// +/// See [`Graph::bind_resource`] and +/// [`CommandRef::bind_resource`](super::cmd::CommandRef::bind_resource) for details. +pub trait Resource { + /// The resource handle type. + type Node; + + #[doc(hidden)] + fn bind_graph(self, _: &mut Graph) -> Self::Node; + + #[deprecated = "use bind_graph function"] + #[doc(hidden)] + fn bind(self, graph: &mut Graph) -> Self::Node + where + Self: Sized, + { + self.bind_graph(graph) + } +} + +impl Resource for SwapchainImage { + type Node = SwapchainImageNode; + + fn bind_graph(self, graph: &mut Graph) -> Self::Node { + // We will return a new node + let res = Self::Node::new(graph.resources.len()); + + //trace!("Node {}: {:?}", res.idx, &self); + + graph.resources.push(AnyResource { + inner: ResourceInner::SwapchainImage(Box::new(self)), + }); + + res + } +} + +macro_rules! graph_resource { + ($name:ident) => { + paste::paste! { + impl Resource for $name { + type Node = [<$name Node>]; + + #[profiling::function] + fn bind_graph(self, graph: &mut Graph) -> Self::Node { + // In this function we are resource a new item (Image or Buffer or etc) + + // We will return a new node + let res = Self::Node::new(graph.resources.len()); + let resource = AnyResource { + inner: ResourceInner::$name(Arc::new(self)), + }; + graph.resources.push(resource); + + res + } + } + + impl Resource for Arc<$name> { + type Node = [<$name Node>]; + + #[profiling::function] + fn bind_graph(self, graph: &mut Graph) -> Self::Node { + // In this function we are resource an existing resource (Arc or + // Arc or etc) + + // We will return an existing node, if possible + // TODO: Could store a sorted list of these shared pointers to avoid the O(N) + for (idx, existing_resource) in graph.resources.iter_mut().enumerate() { + if let Some(existing_resource) = existing_resource.[]() { + if Arc::ptr_eq(existing_resource, &self) { + return Self::Node::new(idx); + } + } + } + + // Return a new node + let res = Self::Node::new(graph.resources.len()); + let resource = AnyResource { + inner: ResourceInner::$name(self), + }; + graph.resources.push(resource); + + res + } + } + + impl<'a> Resource for &'a Arc<$name> { + type Node = [<$name Node>]; + + fn bind_graph(self, graph: &mut Graph) -> Self::Node { + // In this function we are resource a borrowed resource (&Arc or + // &Arc or etc) + + Arc::clone(self).bind_graph(graph) + } + } + + impl Resource for Lease<$name> { + type Node = [<$name LeaseNode>]; + + #[profiling::function] + fn bind_graph(self, graph: &mut Graph) -> Self::Node { + // In this function we are resource a new lease (Lease or Lease or + // etc) + + // We will return a new node + let res = Self::Node::new(graph.resources.len()); + let resource = AnyResource { + inner: ResourceInner::[<$name Lease>](Arc::new(self)), + }; + graph.resources.push(resource); + + res + } + } + + impl Resource for Arc> { + type Node = [<$name LeaseNode>]; + + #[profiling::function] + fn bind_graph(self, graph: &mut Graph) -> Self::Node { + // In this function we are resource an existing lease resource + // (Arc> or Arc> or etc) + + // We will return an existing node, if possible + // TODO: Could store a sorted list of these shared pointers to avoid the O(N) + for (idx, existing_resource) in graph.resources.iter_mut().enumerate() { + if let Some(existing_resource) = existing_resource.[]() { + if Arc::ptr_eq(existing_resource, &self) { + return Self::Node::new(idx); + } + } + } + + // We will return a new node + let res = Self::Node::new(graph.resources.len()); + let resource = AnyResource { + inner: ResourceInner::[<$name Lease>](self), + }; + graph.resources.push(resource); + + res + } + } + + impl<'a> Resource for &'a Arc> { + type Node = [<$name LeaseNode>]; + + fn bind_graph(self, graph: &mut Graph) -> Self::Node { + // In this function we are resource a borrowed resource (&Arc> or + // &Arc> or etc) + + Arc::clone(self).bind_graph(graph) + } + } + + impl AnyResource { + fn [](&self) -> Option<&Arc<$name>> { + let ResourceInner::$name(resource) = &self.inner else { + return None; + }; + + Some(resource) + } + + fn [](&mut self) -> Option<&mut Arc<$name>> { + let ResourceInner::$name(resource) = &mut self.inner else { + return None; + }; + + Some(resource) + } + + fn [](&self) -> Option<&Arc>> { + let ResourceInner::[<$name Lease>](resource) = &self.inner else { + return None + }; + + Some(resource) + } + + fn [](&mut self) -> Option<&mut Arc>> { + let ResourceInner::[<$name Lease>](resource) = &mut self.inner else { + return None; + }; + + Some(resource) + } + } + } + }; +} + +graph_resource!(AccelerationStructure); +graph_resource!(Image); +graph_resource!(Buffer); + +#[derive(Debug)] +pub(crate) enum ResourceInner { + AccelerationStructure(Arc), + AccelerationStructureLease(Arc>), + Buffer(Arc), + BufferLease(Arc>), + Image(Arc), + ImageLease(Arc>), + SwapchainImage(Box), +} + #[deprecated] #[doc(hidden)] pub mod graph { @@ -1345,7 +1524,7 @@ pub mod graph { pub type ImageNode = crate::node::ImageNode; #[deprecated = "use vk_graph::node::Node"] - pub type Node = dyn crate::node::Node; + pub type Node = dyn crate::Node; #[deprecated = "use vk_graph::node::SwapchainImageNode"] pub type SwapchainImageNode = crate::node::SwapchainImageNode; @@ -1408,7 +1587,7 @@ pub mod graph { pub(crate) mod deprecated { use { crate::{ - Graph, GraphResource, + AnyResource, Graph, Node, Resource, driver::{ DriverError, accel_struct::{AccelerationStructure, AccelerationStructureInfo}, @@ -1423,10 +1602,9 @@ pub(crate) mod deprecated { node::{ AccelerationStructureLeaseNode, AccelerationStructureNode, AnyAccelerationStructureNode, AnyBufferNode, AnyImageNode, BufferLeaseNode, - BufferNode, ImageLeaseNode, ImageNode, Node, SwapchainImageNode, + BufferNode, ImageLeaseNode, ImageNode, SwapchainImageNode, }, pool::{Lease, Pool}, - resource::Resource, }, ash::vk, std::{ops::Range, sync::Arc}, @@ -1530,7 +1708,7 @@ pub(crate) mod deprecated { #[doc(hidden)] pub fn bind_node(&mut self, resource: R) -> R::Node where - R: GraphResource, + R: Resource, { self.bind_resource(resource) } @@ -1673,7 +1851,7 @@ pub(crate) mod deprecated { pub trait Info { type Type; - fn info(&self, _: &[Resource]) -> Self::Type + fn info(&self, _: &[AnyResource]) -> Self::Type where Self: Node; } @@ -1681,11 +1859,11 @@ pub(crate) mod deprecated { impl Info for SwapchainImageNode { type Type = ImageInfo; - fn info(&self, resources: &[Resource]) -> Self::Type + fn info(&self, resources: &[AnyResource]) -> Self::Type where Self: Node, { - resources[self.idx].as_swapchain_image().unwrap().info + resources[self.index()].as_swapchain_image().unwrap().info } } @@ -1695,18 +1873,18 @@ pub(crate) mod deprecated { impl Info for [<$name Node>] { type Type = [<$name Info>]; - fn info(&self, resources: &[Resource]) -> Self::Type + fn info(&self, resources: &[AnyResource]) -> Self::Type where Self: Node, { - resources[self.idx].[]().unwrap().info + resources[self.index()].[]().unwrap().info } } impl Info for [] { type Type = [<$name Info>]; - fn info(&self, resources: &[Resource]) -> Self::Type + fn info(&self, resources: &[AnyResource]) -> Self::Type where Self: Node, { @@ -1717,18 +1895,18 @@ pub(crate) mod deprecated { impl Info for [<$name LeaseNode>] { type Type = [<$name Info>]; - fn info(&self, resources: &[Resource]) -> Self::Type + fn info(&self, resources: &[AnyResource]) -> Self::Type where Self: Node, { - resources[self.idx].[]().unwrap().info + resources[self.index()].[]().unwrap().info } } impl Unbind for [<$name Node>] { type Result = Arc<$name>; - fn unbind(&self, resources: &[Resource]) -> Self::Result { + fn unbind(&self, resources: &[AnyResource]) -> Self::Result { resources[self.index()].[]().unwrap().clone() } } @@ -1736,7 +1914,7 @@ pub(crate) mod deprecated { impl Unbind for [<$name LeaseNode>] { type Result = Arc>; - fn unbind(&self, resources: &[Resource]) -> Self::Result { + fn unbind(&self, resources: &[AnyResource]) -> Self::Result { resources[self.index()].[]().unwrap().clone() } } @@ -1760,6 +1938,6 @@ pub(crate) mod deprecated { pub trait Unbind: Node { type Result; - fn unbind(&self, _: &[Resource]) -> Self::Result; + fn unbind(&self, _: &[AnyResource]) -> Self::Result; } } diff --git a/src/node.rs b/src/node.rs index 4d16a2fb..41d3bdff 100644 --- a/src/node.rs +++ b/src/node.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use crate::{ + Node, ResourceInner, driver::{ accel_struct::AccelerationStructure, buffer::Buffer, image::Image, swapchain::SwapchainImage, @@ -10,7 +11,7 @@ use crate::{ pool::Lease, }; -use super::{NodeIndex, Resource, resource::ResourceInner}; +use super::{AnyResource, NodeIndex}; /// Specifies either an owned acceleration structure or an acceleration structure leased from a /// pool. @@ -38,7 +39,7 @@ impl From for AnyAccelerationStructureNode { impl Node for AnyAccelerationStructureNode { type Resource = AccelerationStructure; - fn borrow(self, resources: &[Resource]) -> &Self::Resource { + fn borrow(self, resources: &[AnyResource]) -> &Self::Resource { resources[self.index()].as_driver_accel_struct().unwrap() } @@ -75,7 +76,7 @@ impl From for AnyBufferNode { impl Node for AnyBufferNode { type Resource = Buffer; - fn borrow(self, resources: &[Resource]) -> &Self::Resource { + fn borrow(self, resources: &[AnyResource]) -> &Self::Resource { resources[self.index()].as_driver_buffer().unwrap() } @@ -123,7 +124,7 @@ impl From for AnyImageNode { impl Node for AnyImageNode { type Resource = Image; - fn borrow(self, resources: &[Resource]) -> &Self::Resource { + fn borrow(self, resources: &[AnyResource]) -> &Self::Resource { resources[self.index()].as_driver_image().unwrap() } @@ -136,31 +137,19 @@ impl Node for AnyImageNode { } } -/// A Vulkan resource which has been bound to a [`Graph`] using [`Graph::bind_node`]. -pub trait Node { - /// The Vulkan buffer, image, or acceleration struction type. - type Resource; - - #[doc(hidden)] - fn borrow(self, resources: &[Resource]) -> &Self::Resource; - - #[doc(hidden)] - fn index(&self) -> NodeIndex; -} - macro_rules! node { ($name:ident, $resource:ty, $fn_name:ident) => { paste::paste! { /// Resource node. #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct [<$name Node>] { - pub(super) idx: NodeIndex, + index: NodeIndex, } impl [<$name Node>] { - pub(super) fn new(idx: usize) -> Self { + pub(crate) fn new(index: usize) -> Self { Self { - idx, + index, } } } @@ -168,8 +157,8 @@ macro_rules! node { impl Node for [<$name Node>] { type Resource = $resource; - fn borrow(self, resources: &[Resource]) -> &Self::Resource { - let Resource { inner: ResourceInner::$name(res), } = &resources[self.idx] else { + fn borrow(self, resources: &[AnyResource]) -> &Self::Resource { + let AnyResource { inner: ResourceInner::$name(res), } = &resources[self.index] else { panic!("invalid resource node handle"); }; @@ -177,7 +166,7 @@ macro_rules! node { } fn index(&self) -> NodeIndex { - self.idx + self.index } } } diff --git a/src/queue.rs b/src/queue.rs index 22fb8129..1c56ef3e 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -1,8 +1,7 @@ use { super::{ - Attachment, Command, ExecutionPipeline, Graph, Node, NodeIndex, Resource, + AnyResource, Attachment, Command, ExecutionPipeline, Graph, Node, NodeIndex, ResourceInner, cmd::{SubresourceAccess, SubresourceRange}, - resource::ResourceInner, }, crate::{ cmd::CommandBufferRef, @@ -402,7 +401,7 @@ impl Queue { #[profiling::function] fn begin_render_pass( cmd_buf: &CommandBuffer, - bindings: &[Resource], + bindings: &[AnyResource], pass: &Command, physical_pass: &mut PhysicalPass, render_area: vk::Rect2D, @@ -1887,7 +1886,7 @@ impl Queue { #[profiling::function] fn record_execution_barriers<'a>( cmd_buf: &CommandBuffer, - bindings: &mut [Resource], + bindings: &mut [AnyResource], accesses: impl Iterator)>, ) { // We store a Barriers in TLS to save an alloc; contents are POD @@ -2137,7 +2136,7 @@ impl Queue { #[profiling::function] fn record_image_layout_transitions( cmd_buf: &CommandBuffer, - bindings: &mut [Resource], + bindings: &mut [AnyResource], pass: &mut Command, ) { // We store a Barriers in TLS to save an alloc; contents are POD @@ -2531,7 +2530,7 @@ impl Queue { } #[profiling::function] - fn render_extent(bindings: &[Resource], pass: &Command) -> vk::Extent2D { + fn render_extent(bindings: &[AnyResource], pass: &Command) -> vk::Extent2D { // set_render_area was not specified so we're going to guess using the minimum common // attachment extents let first_exec = pass.execs.first().unwrap(); @@ -2964,7 +2963,7 @@ impl Queue { #[profiling::function] fn write_descriptor_sets( cmd_buf: &CommandBuffer, - bindings: &[Resource], + bindings: &[AnyResource], pass: &Command, physical_pass: &PhysicalPass, ) -> Result<(), DriverError> { @@ -3242,14 +3241,13 @@ struct Schedule { #[allow(unused)] mod derecated { use crate::{ - Queue, + Node, Queue, driver::{ DriverError, cmd_buf::CommandBuffer, descriptor_set::{DescriptorPool, DescriptorPoolInfo}, render_pass::{RenderPass, RenderPassInfo}, }, - node::Node, pool::Pool, }; diff --git a/src/resource.rs b/src/resource.rs deleted file mode 100644 index a274cb2d..00000000 --- a/src/resource.rs +++ /dev/null @@ -1,264 +0,0 @@ -use { - super::{ - AccelerationStructureLeaseNode, AccelerationStructureNode, BufferLeaseNode, BufferNode, - Graph, ImageLeaseNode, ImageNode, SwapchainImageNode, - }, - crate::{ - driver::{ - accel_struct::AccelerationStructure, buffer::Buffer, image::Image, - swapchain::SwapchainImage, - }, - pool::Lease, - }, - std::{fmt::Debug, sync::Arc}, -}; - -/// A trait for resources which may be bound to a `Graph`. -/// -/// See [`Graph::bind_resource`] and -/// [`CommandRef::bind_resource`](super::cmd::CommandRef::bind_resource) for details. -pub trait GraphResource { - /// The resource handle type. - type Node; - - #[doc(hidden)] - fn bind_graph(self, _: &mut Graph) -> Self::Node; - - #[deprecated = "use bind_graph function"] - #[doc(hidden)] - fn bind(self, graph: &mut Graph) -> Self::Node - where - Self: Sized, - { - self.bind_graph(graph) - } -} - -impl GraphResource for SwapchainImage { - type Node = SwapchainImageNode; - - fn bind_graph(self, graph: &mut Graph) -> Self::Node { - // We will return a new node - let res = Self::Node::new(graph.resources.len()); - - //trace!("Node {}: {:?}", res.idx, &self); - - graph.resources.push(Resource { - inner: ResourceInner::SwapchainImage(Box::new(self)), - }); - - res - } -} - -macro_rules! graph_resource { - ($name:ident) => { - paste::paste! { - impl GraphResource for $name { - type Node = [<$name Node>]; - - #[profiling::function] - fn bind_graph(self, graph: &mut Graph) -> Self::Node { - // In this function we are resource a new item (Image or Buffer or etc) - - // We will return a new node - let res = Self::Node::new(graph.resources.len()); - let resource = Resource { - inner: ResourceInner::$name(Arc::new(self)), - }; - graph.resources.push(resource); - - res - } - } - - impl GraphResource for Arc<$name> { - type Node = [<$name Node>]; - - #[profiling::function] - fn bind_graph(self, graph: &mut Graph) -> Self::Node { - // In this function we are resource an existing resource (Arc or - // Arc or etc) - - // We will return an existing node, if possible - // TODO: Could store a sorted list of these shared pointers to avoid the O(N) - for (idx, existing_resource) in graph.resources.iter_mut().enumerate() { - if let Some(existing_resource) = existing_resource.[]() { - if Arc::ptr_eq(existing_resource, &self) { - return Self::Node::new(idx); - } - } - } - - // Return a new node - let res = Self::Node::new(graph.resources.len()); - let resource = Resource { - inner: ResourceInner::$name(self), - }; - graph.resources.push(resource); - - res - } - } - - impl<'a> GraphResource for &'a Arc<$name> { - type Node = [<$name Node>]; - - fn bind_graph(self, graph: &mut Graph) -> Self::Node { - // In this function we are resource a borrowed resource (&Arc or - // &Arc or etc) - - Arc::clone(self).bind_graph(graph) - } - } - - impl GraphResource for Lease<$name> { - type Node = [<$name LeaseNode>]; - - #[profiling::function] - fn bind_graph(self, graph: &mut Graph) -> Self::Node { - // In this function we are resource a new lease (Lease or Lease or - // etc) - - // We will return a new node - let res = Self::Node::new(graph.resources.len()); - let resource = Resource { - inner: ResourceInner::[<$name Lease>](Arc::new(self)), - }; - graph.resources.push(resource); - - res - } - } - - impl GraphResource for Arc> { - type Node = [<$name LeaseNode>]; - - #[profiling::function] - fn bind_graph(self, graph: &mut Graph) -> Self::Node { - // In this function we are resource an existing lease resource - // (Arc> or Arc> or etc) - - // We will return an existing node, if possible - // TODO: Could store a sorted list of these shared pointers to avoid the O(N) - for (idx, existing_resource) in graph.resources.iter_mut().enumerate() { - if let Some(existing_resource) = existing_resource.[]() { - if Arc::ptr_eq(existing_resource, &self) { - return Self::Node::new(idx); - } - } - } - - // We will return a new node - let res = Self::Node::new(graph.resources.len()); - let resource = Resource { - inner: ResourceInner::[<$name Lease>](self), - }; - graph.resources.push(resource); - - res - } - } - - impl<'a> GraphResource for &'a Arc> { - type Node = [<$name LeaseNode>]; - - fn bind_graph(self, graph: &mut Graph) -> Self::Node { - // In this function we are resource a borrowed resource (&Arc> or - // &Arc> or etc) - - Arc::clone(self).bind_graph(graph) - } - } - - impl Resource { - pub(super) fn [](&self) -> Option<&Arc<$name>> { - let ResourceInner::$name(resource) = &self.inner else { - return None; - }; - - Some(resource) - } - - pub(super) fn [](&mut self) -> Option<&mut Arc<$name>> { - let ResourceInner::$name(resource) = &mut self.inner else { - return None; - }; - - Some(resource) - } - - pub(super) fn [](&self) -> Option<&Arc>> { - let ResourceInner::[<$name Lease>](resource) = &self.inner else { - return None - }; - - Some(resource) - } - - pub(super) fn [](&mut self) -> Option<&mut Arc>> { - let ResourceInner::[<$name Lease>](resource) = &mut self.inner else { - return None; - }; - - Some(resource) - } - } - } - }; -} - -graph_resource!(AccelerationStructure); -graph_resource!(Image); -graph_resource!(Buffer); - -#[derive(Debug)] -#[doc(hidden)] -pub struct Resource { - pub(crate) inner: ResourceInner, -} - -impl Resource { - pub(super) fn as_driver_accel_struct(&self) -> Option<&AccelerationStructure> { - Some(match &self.inner { - ResourceInner::AccelerationStructure(resource) => resource, - ResourceInner::AccelerationStructureLease(resource) => resource, - _ => return None, - }) - } - - pub(super) fn as_driver_buffer(&self) -> Option<&Buffer> { - Some(match &self.inner { - ResourceInner::Buffer(resource) => resource, - ResourceInner::BufferLease(resource) => resource, - _ => return None, - }) - } - - pub(super) fn as_driver_image(&self) -> Option<&Image> { - Some(match &self.inner { - ResourceInner::Image(resource) => resource, - ResourceInner::ImageLease(resource) => resource, - ResourceInner::SwapchainImage(resource) => resource, - _ => return None, - }) - } - - pub(super) fn as_swapchain_image(&self) -> Option<&SwapchainImage> { - Some(match &self.inner { - ResourceInner::SwapchainImage(resource) => resource, - _ => return None, - }) - } -} - -#[derive(Debug)] -pub(crate) enum ResourceInner { - AccelerationStructure(Arc), - AccelerationStructureLease(Arc>), - Buffer(Arc), - BufferLease(Arc>), - Image(Arc), - ImageLease(Arc>), - SwapchainImage(Box), -} From 8af9cffe9435508f5b979d93be4a2ae27fb72d98 Mon Sep 17 00:00:00 2001 From: John Wells Date: Wed, 18 Mar 2026 21:30:52 -0400 Subject: [PATCH 47/86] Remove InnerResource type --- src/lib.rs | 97 ++++++++++++++++++++++------------------------------ src/node.rs | 4 +-- src/queue.rs | 54 ++++++++++++++--------------- 3 files changed, 70 insertions(+), 85 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4a239b79..72c07871 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -408,39 +408,45 @@ type NodeIndex = usize; #[derive(Debug)] #[doc(hidden)] -pub struct AnyResource { - pub(crate) inner: ResourceInner, +pub enum AnyResource { + AccelerationStructure(Arc), + AccelerationStructureLease(Arc>), + Buffer(Arc), + BufferLease(Arc>), + Image(Arc), + ImageLease(Arc>), + SwapchainImage(Box), } impl AnyResource { fn as_driver_accel_struct(&self) -> Option<&AccelerationStructure> { - Some(match &self.inner { - ResourceInner::AccelerationStructure(resource) => resource, - ResourceInner::AccelerationStructureLease(resource) => resource, + Some(match self { + Self::AccelerationStructure(resource) => resource, + Self::AccelerationStructureLease(resource) => resource, _ => return None, }) } fn as_driver_buffer(&self) -> Option<&Buffer> { - Some(match &self.inner { - ResourceInner::Buffer(resource) => resource, - ResourceInner::BufferLease(resource) => resource, + Some(match self { + Self::Buffer(resource) => resource, + Self::BufferLease(resource) => resource, _ => return None, }) } fn as_driver_image(&self) -> Option<&Image> { - Some(match &self.inner { - ResourceInner::Image(resource) => resource, - ResourceInner::ImageLease(resource) => resource, - ResourceInner::SwapchainImage(resource) => resource, + Some(match self { + Self::Image(resource) => resource, + Self::ImageLease(resource) => resource, + Self::SwapchainImage(resource) => resource, _ => return None, }) } fn as_swapchain_image(&self) -> Option<&SwapchainImage> { - Some(match &self.inner { - ResourceInner::SwapchainImage(resource) => resource, + Some(match self { + Self::SwapchainImage(resource) => resource, _ => return None, }) } @@ -1307,15 +1313,14 @@ impl Resource for SwapchainImage { fn bind_graph(self, graph: &mut Graph) -> Self::Node { // We will return a new node - let res = Self::Node::new(graph.resources.len()); + let node = Self::Node::new(graph.resources.len()); //trace!("Node {}: {:?}", res.idx, &self); - graph.resources.push(AnyResource { - inner: ResourceInner::SwapchainImage(Box::new(self)), - }); + let resource = AnyResource::SwapchainImage(Box::new(self)); + graph.resources.push(resource); - res + node } } @@ -1328,15 +1333,13 @@ macro_rules! graph_resource { #[profiling::function] fn bind_graph(self, graph: &mut Graph) -> Self::Node { // In this function we are resource a new item (Image or Buffer or etc) - // We will return a new node - let res = Self::Node::new(graph.resources.len()); - let resource = AnyResource { - inner: ResourceInner::$name(Arc::new(self)), - }; + let node = Self::Node::new(graph.resources.len()); + + let resource = AnyResource::$name(Arc::new(self)); graph.resources.push(resource); - res + node } } @@ -1347,7 +1350,6 @@ macro_rules! graph_resource { fn bind_graph(self, graph: &mut Graph) -> Self::Node { // In this function we are resource an existing resource (Arc or // Arc or etc) - // We will return an existing node, if possible // TODO: Could store a sorted list of these shared pointers to avoid the O(N) for (idx, existing_resource) in graph.resources.iter_mut().enumerate() { @@ -1359,13 +1361,11 @@ macro_rules! graph_resource { } // Return a new node - let res = Self::Node::new(graph.resources.len()); - let resource = AnyResource { - inner: ResourceInner::$name(self), - }; + let node = Self::Node::new(graph.resources.len()); + let resource = AnyResource::$name(self); graph.resources.push(resource); - res + node } } @@ -1389,13 +1389,11 @@ macro_rules! graph_resource { // etc) // We will return a new node - let res = Self::Node::new(graph.resources.len()); - let resource = AnyResource { - inner: ResourceInner::[<$name Lease>](Arc::new(self)), - }; + let node = Self::Node::new(graph.resources.len()); + let resource = AnyResource::[<$name Lease>](Arc::new(self)); graph.resources.push(resource); - res + node } } @@ -1418,13 +1416,11 @@ macro_rules! graph_resource { } // We will return a new node - let res = Self::Node::new(graph.resources.len()); - let resource = AnyResource { - inner: ResourceInner::[<$name Lease>](self), - }; + let node = Self::Node::new(graph.resources.len()); + let resource = AnyResource::[<$name Lease>](self); graph.resources.push(resource); - res + node } } @@ -1441,7 +1437,7 @@ macro_rules! graph_resource { impl AnyResource { fn [](&self) -> Option<&Arc<$name>> { - let ResourceInner::$name(resource) = &self.inner else { + let Self::$name(resource) = self else { return None; }; @@ -1449,7 +1445,7 @@ macro_rules! graph_resource { } fn [](&mut self) -> Option<&mut Arc<$name>> { - let ResourceInner::$name(resource) = &mut self.inner else { + let Self::$name(resource) = self else { return None; }; @@ -1457,7 +1453,7 @@ macro_rules! graph_resource { } fn [](&self) -> Option<&Arc>> { - let ResourceInner::[<$name Lease>](resource) = &self.inner else { + let Self::[<$name Lease>](resource) = self else { return None }; @@ -1465,7 +1461,7 @@ macro_rules! graph_resource { } fn [](&mut self) -> Option<&mut Arc>> { - let ResourceInner::[<$name Lease>](resource) = &mut self.inner else { + let Self::[<$name Lease>](resource) = self else { return None; }; @@ -1480,17 +1476,6 @@ graph_resource!(AccelerationStructure); graph_resource!(Image); graph_resource!(Buffer); -#[derive(Debug)] -pub(crate) enum ResourceInner { - AccelerationStructure(Arc), - AccelerationStructureLease(Arc>), - Buffer(Arc), - BufferLease(Arc>), - Image(Arc), - ImageLease(Arc>), - SwapchainImage(Box), -} - #[deprecated] #[doc(hidden)] pub mod graph { diff --git a/src/node.rs b/src/node.rs index 41d3bdff..b04ebb38 100644 --- a/src/node.rs +++ b/src/node.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use crate::{ - Node, ResourceInner, + Node, driver::{ accel_struct::AccelerationStructure, buffer::Buffer, image::Image, swapchain::SwapchainImage, @@ -158,7 +158,7 @@ macro_rules! node { type Resource = $resource; fn borrow(self, resources: &[AnyResource]) -> &Self::Resource { - let AnyResource { inner: ResourceInner::$name(res), } = &resources[self.index] else { + let AnyResource::$name(res) = &resources[self.index] else { panic!("invalid resource node handle"); }; diff --git a/src/queue.rs b/src/queue.rs index 1c56ef3e..2901b612 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -1,6 +1,6 @@ use { super::{ - AnyResource, Attachment, Command, ExecutionPipeline, Graph, Node, NodeIndex, ResourceInner, + AnyResource, Attachment, Command, ExecutionPipeline, Graph, Node, NodeIndex, cmd::{SubresourceAccess, SubresourceRange}, }, crate::{ @@ -1886,7 +1886,7 @@ impl Queue { #[profiling::function] fn record_execution_barriers<'a>( cmd_buf: &CommandBuffer, - bindings: &mut [AnyResource], + resources: &mut [AnyResource], accesses: impl Iterator)>, ) { // We store a Barriers in TLS to save an alloc; contents are POD @@ -1930,12 +1930,12 @@ impl Queue { // render pass leasing function) for (node_idx, accesses) in accesses { - let binding = &bindings[*node_idx]; + let resource = &resources[*node_idx]; - match &binding.inner { - ResourceInner::AccelerationStructure(..) - | ResourceInner::AccelerationStructureLease(..) => { - let Some(accel_struct) = binding.as_driver_accel_struct() else { + match resource { + AnyResource::AccelerationStructure(..) + | AnyResource::AccelerationStructureLease(..) => { + let Some(accel_struct) = resource.as_driver_accel_struct() else { #[cfg(debug_assertions)] unreachable!(); @@ -1957,8 +1957,8 @@ impl Queue { ); tls.prev_accesses.push(prev_access); } - ResourceInner::Buffer(..) | ResourceInner::BufferLease(..) => { - let Some(buffer) = binding.as_driver_buffer() else { + AnyResource::Buffer(..) | AnyResource::BufferLease(..) => { + let Some(buffer) = resource.as_driver_buffer() else { #[cfg(debug_assertions)] unreachable!(); @@ -1990,10 +1990,10 @@ impl Queue { } } } - ResourceInner::Image(..) - | ResourceInner::ImageLease(..) - | ResourceInner::SwapchainImage(..) => { - let Some(image) = binding.as_driver_image() else { + AnyResource::Image(..) + | AnyResource::ImageLease(..) + | AnyResource::SwapchainImage(..) => { + let Some(image) = resource.as_driver_image() else { #[cfg(debug_assertions)] unreachable!(); @@ -2136,7 +2136,7 @@ impl Queue { #[profiling::function] fn record_image_layout_transitions( cmd_buf: &CommandBuffer, - bindings: &mut [AnyResource], + resources: &mut [AnyResource], pass: &mut Command, ) { // We store a Barriers in TLS to save an alloc; contents are POD @@ -2167,17 +2167,17 @@ impl Queue { .flat_map(|exec| exec.accesses.iter()) .map(|(node_idx, accesses)| (*node_idx, accesses)) { - debug_assert!(bindings.get(node_idx).is_some()); + debug_assert!(resources.get(node_idx).is_some()); - let binding = unsafe { + let resource = unsafe { // CommandRef enforces this during push_resource_access - bindings.get_unchecked(node_idx) + resources.get_unchecked(node_idx) }; - match &binding.inner { - ResourceInner::AccelerationStructure(..) - | ResourceInner::AccelerationStructureLease(..) => { - let Some(accel_struct) = binding.as_driver_accel_struct() else { + match resource { + AnyResource::AccelerationStructure(..) + | AnyResource::AccelerationStructureLease(..) => { + let Some(accel_struct) = resource.as_driver_accel_struct() else { #[cfg(debug_assertions)] unreachable!(); @@ -2189,8 +2189,8 @@ impl Queue { AccelerationStructure::access(accel_struct, AccessType::Nothing); } - ResourceInner::Buffer(..) | ResourceInner::BufferLease(..) => { - let Some(buffer) = binding.as_driver_buffer() else { + AnyResource::Buffer(..) | AnyResource::BufferLease(..) => { + let Some(buffer) = resource.as_driver_buffer() else { #[cfg(debug_assertions)] unreachable!(); @@ -2220,10 +2220,10 @@ impl Queue { for _ in Buffer::access(buffer, AccessType::Nothing, access_range) {} } } - ResourceInner::Image(..) - | ResourceInner::ImageLease(..) - | ResourceInner::SwapchainImage(..) => { - let Some(image) = binding.as_driver_image() else { + AnyResource::Image(..) + | AnyResource::ImageLease(..) + | AnyResource::SwapchainImage(..) => { + let Some(image) = resource.as_driver_image() else { #[cfg(debug_assertions)] unreachable!(); From 1effe9802b973869a77a12d1c1845a86476bc24e Mon Sep 17 00:00:00 2001 From: John Wells Date: Wed, 18 Mar 2026 21:56:26 -0400 Subject: [PATCH 48/86] Rename things --- crates/vk-graph-hot/src/compute.rs | 6 +- crates/vk-graph-hot/src/graphic.rs | 6 +- crates/vk-graph-hot/src/lib.rs | 12 ++-- crates/vk-graph-hot/src/ray_trace.rs | 6 +- src/cmd/cmd_buf.rs | 20 +++---- src/cmd/compute.rs | 32 +++++----- src/cmd/graphic.rs | 88 ++++++++++++++-------------- src/cmd/mod.rs | 44 +++++++------- src/cmd/pipeline.rs | 38 ++++++------ src/cmd/ray_trace.rs | 32 +++++----- src/lib.rs | 30 +++++----- src/queue.rs | 23 ++++---- 12 files changed, 166 insertions(+), 171 deletions(-) diff --git a/crates/vk-graph-hot/src/compute.rs b/crates/vk-graph-hot/src/compute.rs index 328c2482..66cfa855 100644 --- a/crates/vk-graph-hot/src/compute.rs +++ b/crates/vk-graph-hot/src/compute.rs @@ -11,7 +11,7 @@ use { atomic::{AtomicBool, Ordering}, }, vk_graph::{ - cmd::{CommandPipeline, CommandRef}, + cmd::{Command, Pipeline}, driver::{ DriverError, compute::{ComputePipeline, ComputePipelineInfo}, @@ -55,8 +55,8 @@ impl HotComputePipeline { fn compile_shader_and_bind_cmd<'a>( &self, - cmd: CommandRef<'a>, - ) -> >::Ref { + cmd: Command<'a>, + ) -> >::Command { if self.has_changes.swap(false, Ordering::Relaxed) { info!("Shader change detected"); diff --git a/crates/vk-graph-hot/src/graphic.rs b/crates/vk-graph-hot/src/graphic.rs index 5972a68d..9d85a61a 100644 --- a/crates/vk-graph-hot/src/graphic.rs +++ b/crates/vk-graph-hot/src/graphic.rs @@ -8,7 +8,7 @@ use { atomic::{AtomicBool, Ordering}, }, vk_graph::{ - cmd::{CommandPipeline, CommandRef}, + cmd::{Command, Pipeline}, driver::{ DriverError, device::Device, @@ -59,8 +59,8 @@ impl HotGraphicPipeline { fn compile_shader_and_bind_cmd<'a>( &self, - cmd: CommandRef<'a>, - ) -> >::Ref { + cmd: Command<'a>, + ) -> >::Command { if self.has_changes.swap(false, Ordering::Relaxed) { info!("Shader change detected"); diff --git a/crates/vk-graph-hot/src/lib.rs b/crates/vk-graph-hot/src/lib.rs index 391f9ffa..58e34f34 100644 --- a/crates/vk-graph-hot/src/lib.rs +++ b/crates/vk-graph-hot/src/lib.rs @@ -269,18 +269,18 @@ macro_rules! pipeline { } } - impl<'a> CommandPipeline<'a> for [] { - type Ref = <[<$name Pipeline>] as CommandPipeline<'a>>::Ref; + impl<'a> Pipeline<'a> for [] { + type Command = <[<$name Pipeline>] as Pipeline<'a>>::Command; - fn bind_cmd(self, cmd: CommandRef<'a>) -> Self::Ref { + fn bind_cmd(self, cmd: Command<'a>) -> Self::Command { self.compile_shader_and_bind_cmd(cmd) } } - impl<'a> CommandPipeline<'a> for &'a [] { - type Ref = <[<$name Pipeline>] as CommandPipeline<'a>>::Ref; + impl<'a> Pipeline<'a> for &'a [] { + type Command = <[<$name Pipeline>] as Pipeline<'a>>::Command; - fn bind_cmd(self, cmd: CommandRef<'a>) -> Self::Ref { + fn bind_cmd(self, cmd: Command<'a>) -> Self::Command { self.compile_shader_and_bind_cmd(cmd) } } diff --git a/crates/vk-graph-hot/src/ray_trace.rs b/crates/vk-graph-hot/src/ray_trace.rs index 7a4abad6..2364ae64 100644 --- a/crates/vk-graph-hot/src/ray_trace.rs +++ b/crates/vk-graph-hot/src/ray_trace.rs @@ -11,7 +11,7 @@ use { atomic::{AtomicBool, Ordering}, }, vk_graph::{ - cmd::{CommandPipeline, CommandRef}, + cmd::{Command, Pipeline}, driver::{ DriverError, device::Device, @@ -65,8 +65,8 @@ impl HotRayTracePipeline { fn compile_shader_and_bind_cmd<'a>( &self, - cmd: CommandRef<'a>, - ) -> >::Ref { + cmd: Command<'a>, + ) -> >::Command { if self.has_changes.swap(false, Ordering::Relaxed) { info!("Shader change detected"); diff --git a/src/cmd/cmd_buf.rs b/src/cmd/cmd_buf.rs index 864dda34..0bc1794f 100644 --- a/src/cmd/cmd_buf.rs +++ b/src/cmd/cmd_buf.rs @@ -6,7 +6,6 @@ use { AccelerationStructureGeometry, AccelerationStructureGeometryInfo, DeviceOrHostAddress, }, - cmd_buf::CommandBuffer, device::Device, }, }, @@ -46,8 +45,8 @@ use crate::Execution; /// # Ok(()) } /// ``` #[derive(Clone, Copy)] -pub struct CommandBufferRef<'a> { - cmd_buf: &'a CommandBuffer, +pub struct CommandBuffer<'a> { + cmd_buf: &'a crate::driver::cmd_buf::CommandBuffer, #[cfg(debug_assertions)] exec: &'a Execution, @@ -55,9 +54,9 @@ pub struct CommandBufferRef<'a> { resources: &'a [AnyResource], } -impl<'a> CommandBufferRef<'a> { +impl<'a> CommandBuffer<'a> { pub(crate) fn new( - cmd_buf: &'a CommandBuffer, + cmd_buf: &'a crate::driver::cmd_buf::CommandBuffer, resources: &'a [AnyResource], #[cfg(debug_assertions)] exec: &'a Execution, ) -> Self { @@ -493,8 +492,8 @@ impl<'a> CommandBufferRef<'a> { } } -impl<'a> Deref for CommandBufferRef<'a> { - type Target = CommandBuffer; +impl<'a> Deref for CommandBuffer<'a> { + type Target = crate::driver::cmd_buf::CommandBuffer; fn deref(&self) -> &Self::Target { self.cmd_buf @@ -702,9 +701,8 @@ mod deprecated { use crate::{ cmd::{ - BuildAccelerationStructureIndirectInfo, BuildAccelerationStructureInfo, - CommandBufferRef, UpdateAccelerationStructureIndirectInfo, - UpdateAccelerationStructureInfo, + BuildAccelerationStructureIndirectInfo, BuildAccelerationStructureInfo, CommandBuffer, + UpdateAccelerationStructureIndirectInfo, UpdateAccelerationStructureInfo, }, driver::accel_struct::{ AccelerationStructureGeometry, AccelerationStructureGeometryInfo, DeviceOrHostAddress, @@ -716,7 +714,7 @@ mod deprecated { node::AnyAccelerationStructureNode, }; - impl<'a> CommandBufferRef<'a> { + impl<'a> CommandBuffer<'a> { #[deprecated = "use build_accel_struct function"] #[doc(hidden)] pub fn build_structure( diff --git a/src/cmd/compute.rs b/src/cmd/compute.rs index 7eac8aa1..fb500cf8 100644 --- a/src/cmd/compute.rs +++ b/src/cmd/compute.rs @@ -1,5 +1,5 @@ use { - super::{cmd_buf::CommandBufferRef, pipeline::PipelineCommandRef}, + super::{cmd_buf::CommandBuffer, pipeline::PipelineCommand}, crate::{driver::compute::ComputePipeline, node::AnyBufferNode}, ash::vk, log::trace, @@ -10,7 +10,7 @@ use { /// /// This structure provides a strongly-typed set of methods which allow compute shader code to be /// executed. An instance of `Compute` is provided to the closure parameter of -/// [`PipelineCommandRef::record_pipeline`] which may be accessed by binding a [`ComputePipeline`] to a +/// [`PipelineCommand::record_pipeline`] which may be accessed by binding a [`ComputePipeline`] to a /// render pass. /// /// # Examples @@ -38,12 +38,12 @@ use { /// }); /// # Ok(()) } /// ``` -pub struct ComputeCommandBufferRef<'a> { - cmd_buf: CommandBufferRef<'a>, +pub struct ComputeCommandBuffer<'a> { + cmd_buf: CommandBuffer<'a>, pipeline: ComputePipeline, } -impl ComputeCommandBufferRef<'_> { +impl ComputeCommandBuffer<'_> { /// [Dispatch] compute work items. /// /// When the command is executed, a global workgroup consisting of @@ -317,8 +317,8 @@ impl ComputeCommandBufferRef<'_> { } } -impl<'a> Deref for ComputeCommandBufferRef<'a> { - type Target = CommandBufferRef<'a>; +impl<'a> Deref for ComputeCommandBuffer<'a> { + type Target = CommandBuffer<'a>; fn deref(&self) -> &Self::Target { &self.cmd_buf @@ -326,11 +326,11 @@ impl<'a> Deref for ComputeCommandBufferRef<'a> { } // NOTE: local implementation of type from super module -impl PipelineCommandRef<'_, ComputePipeline> { +impl PipelineCommand<'_, ComputePipeline> { /// Begin recording a compute pipeline command buffer. pub fn record_cmd_buf( mut self, - func: impl FnOnce(ComputeCommandBufferRef<'_>) + Send + 'static, + func: impl FnOnce(ComputeCommandBuffer<'_>) + Send + 'static, ) -> Self { self.record_cmd_buf_mut(func); self @@ -339,7 +339,7 @@ impl PipelineCommandRef<'_, ComputePipeline> { /// Begin recording a compute pipeline command buffer. pub fn record_cmd_buf_mut( &mut self, - func: impl FnOnce(ComputeCommandBufferRef<'_>) + Send + 'static, + func: impl FnOnce(ComputeCommandBuffer<'_>) + Send + 'static, ) { let pipeline = self .cmd @@ -354,7 +354,7 @@ impl PipelineCommandRef<'_, ComputePipeline> { .clone(); self.cmd.push_exec(move |cmd_buf| { - func(ComputeCommandBufferRef { cmd_buf, pipeline }); + func(ComputeCommandBuffer { cmd_buf, pipeline }); }); } } @@ -365,8 +365,8 @@ mod deprecated { crate::{ Node, cmd::{ - Descriptor, PipelineCommandRef, SubresourceRange, View, ViewInfo, - compute::ComputeCommandBufferRef, + Descriptor, PipelineCommand, SubresourceRange, View, ViewInfo, + compute::ComputeCommandBuffer, }, driver::compute::ComputePipeline, }, @@ -374,7 +374,7 @@ mod deprecated { vk_sync::AccessType, }; - impl ComputeCommandBufferRef<'_> { + impl ComputeCommandBuffer<'_> { #[deprecated = "use push_constants function"] #[doc(hidden)] pub fn push_constants_offset(&self, offset: u32, data: &[u8]) -> &Self { @@ -382,7 +382,7 @@ mod deprecated { } } - impl PipelineCommandRef<'_, ComputePipeline> { + impl PipelineCommand<'_, ComputePipeline> { #[deprecated = "use shader_resource_access function with AccessType::ComputeShaderReadOther"] #[doc(hidden)] pub fn read_descriptor(self, descriptor: impl Into, node: N) -> Self @@ -421,7 +421,7 @@ mod deprecated { #[doc(hidden)] pub fn record_compute( self, - func: impl FnOnce(ComputeCommandBufferRef<'_>, ()) + Send + 'static, + func: impl FnOnce(ComputeCommandBuffer<'_>, ()) + Send + 'static, ) -> Self { self.record_cmd_buf(|cmd_buf| func(cmd_buf, ())) } diff --git a/src/cmd/graphic.rs b/src/cmd/graphic.rs index 7bd7870e..4b736007 100644 --- a/src/cmd/graphic.rs +++ b/src/cmd/graphic.rs @@ -1,5 +1,5 @@ use { - super::{AttachmentIndex, cmd_buf::CommandBufferRef, pipeline::PipelineCommandRef}, + super::{AttachmentIndex, cmd_buf::CommandBuffer, pipeline::PipelineCommand}, crate::{ driver::{ graphic::{DepthStencilInfo, GraphicPipeline}, @@ -15,7 +15,7 @@ use { /// TODO #[derive(Clone, Copy, Debug)] -pub enum ClearColorValue { +pub enum ClearColor { /// Value as [f32]. Float32([f32; 4]), @@ -26,7 +26,7 @@ pub enum ClearColorValue { Uint32([u32; 4]), } -impl ClearColorValue { +impl ClearColor { /// rgb zeros and alpha ones. pub const BLACK_ALPHA_ONE: Self = Self::Float32([0.0, 0.0, 0.0, 1.0]); @@ -60,42 +60,42 @@ impl ClearColorValue { } } -impl Default for ClearColorValue { +impl Default for ClearColor { fn default() -> Self { Self::from_f32(0.0, 0.0, 0.0, 0.0) } } -impl From<[f32; 4]> for ClearColorValue { +impl From<[f32; 4]> for ClearColor { fn from(float32: [f32; 4]) -> Self { Self::Float32(float32) } } -impl From<[i32; 4]> for ClearColorValue { +impl From<[i32; 4]> for ClearColor { fn from(int32: [i32; 4]) -> Self { Self::Int32(int32) } } -impl From<[u8; 4]> for ClearColorValue { +impl From<[u8; 4]> for ClearColor { fn from(uint8: [u8; 4]) -> Self { Self::from_u8(uint8[0], uint8[1], uint8[2], uint8[3]) } } -impl From<[u32; 4]> for ClearColorValue { +impl From<[u32; 4]> for ClearColor { fn from(uint32: [u32; 4]) -> Self { Self::Uint32(uint32) } } -impl From for vk::ClearColorValue { - fn from(value: ClearColorValue) -> Self { +impl From for vk::ClearColorValue { + fn from(value: ClearColor) -> Self { match value { - ClearColorValue::Float32(float32) => Self { float32 }, - ClearColorValue::Int32(int32) => Self { int32 }, - ClearColorValue::Uint32(uint32) => Self { uint32 }, + ClearColor::Float32(float32) => Self { float32 }, + ClearColor::Int32(int32) => Self { int32 }, + ClearColor::Uint32(uint32) => Self { uint32 }, } } } @@ -113,22 +113,22 @@ pub enum LoadOp { Load, } -impl LoadOp { +impl LoadOp { /// A load operation which results in a color attachment filled with rgb zeros and alpha ones. - pub const CLEAR_BLACK_ALPHA_ONE: Self = Self::Clear(ClearColorValue::BLACK_ALPHA_ONE); + pub const CLEAR_BLACK_ALPHA_ONE: Self = Self::Clear(ClearColor::BLACK_ALPHA_ONE); /// A load operation which results in a color attachment filled with zeros. - pub const CLEAR_BLACK_ALPHA_ZERO: Self = Self::Clear(ClearColorValue::BLACK_ALPHA_ZERO); + pub const CLEAR_BLACK_ALPHA_ZERO: Self = Self::Clear(ClearColor::BLACK_ALPHA_ZERO); /// A load operation which results in a color attachment filled with rgb zeros and alpha ones. - pub const CLEAR_WHITE_ALPHA_ONE: Self = Self::Clear(ClearColorValue::WHITE_ALPHA_ONE); + pub const CLEAR_WHITE_ALPHA_ONE: Self = Self::Clear(ClearColor::WHITE_ALPHA_ONE); /// A load operation which results in a color attachment filled with rgb ones and alpha zeros. - pub const CLEAR_WHITE_ALPHA_ZERO: Self = Self::Clear(ClearColorValue::WHITE_ALPHA_ZERO); + pub const CLEAR_WHITE_ALPHA_ZERO: Self = Self::Clear(ClearColor::WHITE_ALPHA_ZERO); /// Convenience constructor for clear color values. pub fn clear_rgba(r: f32, g: f32, b: f32, a: f32) -> Self { - Self::Clear(ClearColorValue::rgba(r, g, b, a)) + Self::Clear(ClearColor::rgba(r, g, b, a)) } } @@ -166,7 +166,7 @@ pub enum StoreOp { /// /// This structure provides a strongly-typed set of methods which allow rasterization shader code to /// be executed. An instance of `Draw` is provided to the closure parameter of -/// [`PipelineCommandRef::record_pipeline`] which may be accessed by binding a [`GraphicPipeline`] to a +/// [`PipelineCommand::record_pipeline`] which may be accessed by binding a [`GraphicPipeline`] to a /// render pass. /// /// # Examples @@ -202,12 +202,12 @@ pub enum StoreOp { /// }); /// # Ok(()) } /// ``` -pub struct GraphicCommandBufferRef<'a> { - cmd_buf: CommandBufferRef<'a>, +pub struct GraphicCommandBuffer<'a> { + cmd_buf: CommandBuffer<'a>, pipeline: GraphicPipeline, } -impl GraphicCommandBufferRef<'_> { +impl GraphicCommandBuffer<'_> { /// Bind an index buffer to the current pass. /// /// `offset` is the starting offset in bytes within `buffer` used in index buffer address @@ -779,8 +779,8 @@ impl GraphicCommandBufferRef<'_> { } } -impl<'a> Deref for GraphicCommandBufferRef<'a> { - type Target = CommandBufferRef<'a>; +impl<'a> Deref for GraphicCommandBuffer<'a> { + type Target = CommandBuffer<'a>; fn deref(&self) -> &Self::Target { &self.cmd_buf @@ -788,13 +788,13 @@ impl<'a> Deref for GraphicCommandBufferRef<'a> { } // NOTE: local implementation of type from super module -impl PipelineCommandRef<'_, GraphicPipeline> { +impl PipelineCommand<'_, GraphicPipeline> { /// TODO pub fn color_attachment_image( mut self, color_attachment_idx: AttachmentIndex, color_image: impl Into, - load: LoadOp, + load: LoadOp, store: StoreOp, ) -> Self { self.set_color_attachment_image(color_attachment_idx, color_image, load, store); @@ -807,7 +807,7 @@ impl PipelineCommandRef<'_, GraphicPipeline> { color_attachment_idx: AttachmentIndex, color_image: impl Into, color_image_view_info: impl Into, - load: LoadOp, + load: LoadOp, store: StoreOp, ) -> Self { self.set_color_attachment_image_view( @@ -935,7 +935,7 @@ impl PipelineCommandRef<'_, GraphicPipeline> { /// Begin recording a graphics pipeline command buffer. pub fn record_cmd_buf( mut self, - func: impl FnOnce(GraphicCommandBufferRef<'_>) + Send + 'static, + func: impl FnOnce(GraphicCommandBuffer<'_>) + Send + 'static, ) -> Self { self.record_cmd_buf_mut(func); self @@ -944,7 +944,7 @@ impl PipelineCommandRef<'_, GraphicPipeline> { /// Begin recording a graphics pipeline command buffer. pub fn record_cmd_buf_mut( &mut self, - func: impl FnOnce(GraphicCommandBufferRef<'_>) + Send + 'static, + func: impl FnOnce(GraphicCommandBuffer<'_>) + Send + 'static, ) { let pipeline = self .cmd @@ -959,7 +959,7 @@ impl PipelineCommandRef<'_, GraphicPipeline> { .clone(); self.cmd.push_exec(move |cmd_buf| { - func(GraphicCommandBufferRef { cmd_buf, pipeline }); + func(GraphicCommandBuffer { cmd_buf, pipeline }); }); } @@ -984,7 +984,7 @@ impl PipelineCommandRef<'_, GraphicPipeline> { &mut self, color_attachment_idx: AttachmentIndex, color_image: impl Into, - load: LoadOp, + load: LoadOp, store: StoreOp, ) -> &mut Self { let color_image = color_image.into(); @@ -1007,7 +1007,7 @@ impl PipelineCommandRef<'_, GraphicPipeline> { color_attachment_idx: AttachmentIndex, color_image: impl Into, color_image_view_info: impl Into, - load: LoadOp, + load: LoadOp, store: StoreOp, ) -> &mut Self { let color_image = color_image.into(); @@ -1223,8 +1223,8 @@ mod deprecated { crate::{ Attachment, Node, SubresourceAccess, cmd::{ - AttachmentIndex, ClearColorValue, Descriptor, PipelineCommandRef, SubresourceRange, - View, ViewInfo, graphic::GraphicCommandBufferRef, + AttachmentIndex, ClearColor, Descriptor, PipelineCommand, SubresourceRange, View, + ViewInfo, graphic::GraphicCommandBuffer, }, driver::{ graphic::GraphicPipeline, @@ -1240,7 +1240,7 @@ mod deprecated { vk_sync::AccessType, }; - impl GraphicCommandBufferRef<'_> { + impl GraphicCommandBuffer<'_> { #[deprecated = "use push_constants function"] #[doc(hidden)] pub fn push_constants_offset(&self, offset: u32, data: &[u8]) -> &Self { @@ -1249,7 +1249,7 @@ mod deprecated { } // Attachment functions from previous version - impl PipelineCommandRef<'_, GraphicPipeline> { + impl PipelineCommand<'_, GraphicPipeline> { #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] pub fn attach_color( @@ -1319,7 +1319,7 @@ mod deprecated { self, attachment_idx: AttachmentIndex, image: impl Into, - color: impl Into, + color: impl Into, ) -> Self { let image = image.into(); let image_info = self.resource(image).info; @@ -1335,7 +1335,7 @@ mod deprecated { mut self, attachment_idx: AttachmentIndex, image: impl Into, - color: impl Into, + color: impl Into, image_view_info: impl Into, ) -> Self { #[allow(deprecated)] @@ -1573,7 +1573,7 @@ mod deprecated { } // Attachment functions as setters - impl PipelineCommandRef<'_, GraphicPipeline> { + impl PipelineCommand<'_, GraphicPipeline> { #[deprecated = "upgrade guide: https://github.com/attackgoat/vk-graph/pull/107"] #[doc(hidden)] pub fn set_attach_color( @@ -1808,7 +1808,7 @@ mod deprecated { &mut self, attachment_idx: AttachmentIndex, image: impl Into, - color: impl Into, + color: impl Into, ) -> &mut Self { let image = image.into(); let image_info = self.resource(image).info; @@ -1824,7 +1824,7 @@ mod deprecated { &mut self, attachment_idx: AttachmentIndex, image: impl Into, - color: impl Into, + color: impl Into, image_view_info: impl Into, ) -> &mut Self { let image = image.into(); @@ -3073,7 +3073,7 @@ mod deprecated { } // Resource functions - impl PipelineCommandRef<'_, GraphicPipeline> { + impl PipelineCommand<'_, GraphicPipeline> { #[deprecated = "use shader_resource_access function with AccessType::AnyShaderReadSampledImageOrUniformTexelBuffer"] #[doc(hidden)] pub fn read_descriptor(self, descriptor: impl Into, node: N) -> Self @@ -3116,7 +3116,7 @@ mod deprecated { #[doc(hidden)] pub fn record_subpass( self, - func: impl FnOnce(GraphicCommandBufferRef<'_>, ()) + Send + 'static, + func: impl FnOnce(GraphicCommandBuffer<'_>, ()) + Send + 'static, ) -> Self { self.record_cmd_buf(|cmd_buf| { func(cmd_buf, ()); diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index 1476749c..8650fee0 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -8,20 +8,21 @@ mod ray_trace; pub use self::{ cmd_buf::{ - BuildAccelerationStructureIndirectInfo, BuildAccelerationStructureInfo, CommandBufferRef, + BuildAccelerationStructureIndirectInfo, BuildAccelerationStructureInfo, CommandBuffer, UpdateAccelerationStructureIndirectInfo, UpdateAccelerationStructureInfo, }, - compute::ComputeCommandBufferRef, - graphic::{ClearColorValue, GraphicCommandBufferRef, LoadOp, StoreOp}, - pipeline::{CommandPipeline, PipelineCommandRef}, - ray_trace::RayTraceCommandBufferRef, + compute::ComputeCommandBuffer, + graphic::{ClearColor, GraphicCommandBuffer, LoadOp, StoreOp}, + pipeline::{Pipeline, PipelineCommand}, + ray_trace::RayTraceCommandBuffer, }; use { super::{ AccelerationStructureLeaseNode, AccelerationStructureNode, AnyAccelerationStructureNode, - AnyBufferNode, AnyImageNode, AnyResource, BufferLeaseNode, BufferNode, Command, Execution, - ExecutionFunction, Graph, ImageLeaseNode, ImageNode, Node, Resource, SwapchainImageNode, + AnyBufferNode, AnyImageNode, AnyResource, BufferLeaseNode, BufferNode, CommandData, + Execution, ExecutionFunction, Graph, ImageLeaseNode, ImageNode, Node, Resource, + SwapchainImageNode, }, crate::driver::{ accel_struct::AccelerationStructureSubresourceRange, buffer::BufferSubresourceRange, @@ -46,16 +47,16 @@ pub type DescriptorSetIndex = u32; /// A general render pass which may contain acceleration structure commands, general commands, or /// have pipeline bound to then record commands specific to those pipeline types. -pub struct CommandRef<'a> { +pub struct Command<'a> { pub(super) cmd_idx: usize, pub(super) exec_idx: usize, pub(super) graph: &'a mut Graph, } -impl<'a> CommandRef<'a> { +impl<'a> Command<'a> { pub(super) fn new(graph: &'a mut Graph) -> Self { let cmd_idx = graph.cmds.len(); - graph.cmds.push(Command { + graph.cmds.push(CommandData { execs: vec![Default::default()], // We start off with a default execution! name: None, }); @@ -67,11 +68,11 @@ impl<'a> CommandRef<'a> { } } - fn cmd(&self) -> &Command { + fn cmd(&self) -> &CommandData { &self.graph.cmds[self.cmd_idx] } - fn cmd_mut(&mut self) -> &mut Command { + fn cmd_mut(&mut self) -> &mut CommandData { &mut self.graph.cmds[self.cmd_idx] } @@ -88,9 +89,9 @@ impl<'a> CommandRef<'a> { /// Binds a [`ComputePipeline`], [`GraphicPipeline`], or [`RayTracePipeline`] to the current /// pass, allowing for strongly typed access to the related functions. - pub fn bind_pipeline

(self, pipeline: P) -> P::Ref + pub fn bind_pipeline

(self, pipeline: P) -> P::Command where - P: CommandPipeline<'a>, + P: Pipeline<'a>, { pipeline.bind_cmd(self) } @@ -112,7 +113,7 @@ impl<'a> CommandRef<'a> { self.graph } - fn push_exec(&mut self, func: impl FnOnce(CommandBufferRef) + Send + 'static) { + fn push_exec(&mut self, func: impl FnOnce(CommandBuffer) + Send + 'static) { let cmd = self.cmd_mut(); let exec = { let last_exec = cmd.execs.last_mut().unwrap(); @@ -158,10 +159,7 @@ impl<'a> CommandRef<'a> { /// /// The provided closure allows you to run any Vulkan code, or interoperate with other Vulkan /// code and interfaces. - pub fn record_cmd_buf( - mut self, - func: impl FnOnce(CommandBufferRef<'_>) + Send + 'static, - ) -> Self { + pub fn record_cmd_buf(mut self, func: impl FnOnce(CommandBuffer<'_>) + Send + 'static) -> Self { self.record_cmd_buf_mut(func); self } @@ -172,7 +170,7 @@ impl<'a> CommandRef<'a> { /// /// The provided closure allows you to run any Vulkan code, or interoperate with other Vulkan /// code and interfaces. - pub fn record_cmd_buf_mut(&mut self, func: impl FnOnce(CommandBufferRef<'_>) + Send + 'static) { + pub fn record_cmd_buf_mut(&mut self, func: impl FnOnce(CommandBuffer<'_>) + Send + 'static) { self.push_exec(move |cmd_buf| { func(cmd_buf); }); @@ -550,14 +548,14 @@ mod deprecated { use { crate::{ Graph, Node, Resource, - cmd::{CommandBufferRef, CommandRef, SubresourceRange, View}, + cmd::{Command, CommandBuffer, SubresourceRange, View}, deprecated::Info, }, ash::vk, vk_sync::AccessType, }; - impl<'a> CommandRef<'a> { + impl<'a> Command<'a> { #[deprecated = "use resource_access function"] #[doc(hidden)] pub fn access_node(mut self, node: N, access: AccessType) -> Self @@ -669,7 +667,7 @@ mod deprecated { #[doc(hidden)] pub fn record_acceleration( mut self, - func: impl FnOnce(CommandBufferRef<'_>, ()) + Send + 'static, + func: impl FnOnce(CommandBuffer<'_>, ()) + Send + 'static, ) -> Self { self.push_exec(|cmd_buf| { func(cmd_buf, ()); diff --git a/src/cmd/pipeline.rs b/src/cmd/pipeline.rs index 0b9067a1..e62b58b6 100644 --- a/src/cmd/pipeline.rs +++ b/src/cmd/pipeline.rs @@ -1,6 +1,6 @@ use { super::{ - AccessType, CommandRef, Descriptor, Graph, Node, Resource, SubresourceRange, View, ViewInfo, + AccessType, Command, Descriptor, Graph, Node, Resource, SubresourceRange, View, ViewInfo, }, crate::{ ExecutionPipeline, @@ -9,26 +9,26 @@ use { std::marker::PhantomData, }; -/// A trait for pipelines which may be bound to a `CommandRef`. +/// A trait for pipelines which may be bound to a `Command`. /// -/// See [`CommandRef::bind_pipeline`](super::cmd::CommandRef::bind_pipeline) for details. -pub trait CommandPipeline<'a> { +/// See [`Command::bind_pipeline`](super::cmd::Command::bind_pipeline) for details. +pub trait Pipeline<'a> { /// The resource reference type. - type Ref; + type Command; /// Binds the resource to a command. /// /// Returns a reference type. - fn bind_cmd(self, _: CommandRef<'a>) -> Self::Ref; + fn bind_cmd(self, _: Command<'a>) -> Self::Command; } macro_rules! pipeline { ($name:ident) => { paste::paste! { - impl<'a> CommandPipeline<'a> for [<$name Pipeline>] { - type Ref = PipelineCommandRef<'a, [<$name Pipeline>]>; + impl<'a> Pipeline<'a> for [<$name Pipeline>] { + type Command = PipelineCommand<'a, [<$name Pipeline>]>; - fn bind_cmd(self, mut cmd: CommandRef<'a>) -> Self::Ref { + fn bind_cmd(self, mut cmd: Command<'a>) -> Self::Command { { let cmd = cmd.cmd_mut(); if cmd.execs.last().unwrap().pipeline.is_some() { @@ -39,17 +39,17 @@ macro_rules! pipeline { = Some(ExecutionPipeline::$name(self)); } - Self::Ref { + Self::Command { __: PhantomData, cmd, } } } - impl<'a> CommandPipeline<'a> for &'a [<$name Pipeline>] { - type Ref = PipelineCommandRef<'a, [<$name Pipeline>]>; + impl<'a> Pipeline<'a> for &'a [<$name Pipeline>] { + type Command = PipelineCommand<'a, [<$name Pipeline>]>; - fn bind_cmd(self, mut cmd: CommandRef<'a>) -> Self::Ref { + fn bind_cmd(self, mut cmd: Command<'a>) -> Self::Command { { let cmd = cmd.cmd_mut(); if cmd.execs.last().unwrap().pipeline.is_some() { @@ -60,7 +60,7 @@ macro_rules! pipeline { = Some(ExecutionPipeline::$name(self.clone())); } - Self::Ref { + Self::Command { __: PhantomData, cmd, } @@ -93,13 +93,13 @@ pipeline!(Graphic); pipeline!(RayTrace); /// A render pass which has been bound to a particular compute, graphic, or ray-trace pipeline. -pub struct PipelineCommandRef<'c, T> { +pub struct PipelineCommand<'c, T> { pub(super) __: PhantomData, - pub(super) cmd: CommandRef<'c>, + pub(super) cmd: Command<'c>, } // NOTE: There are specific implementations of T in the compute, graphic, and ray trace modules -impl<'c, T> PipelineCommandRef<'c, T> { +impl<'c, T> PipelineCommand<'c, T> { /// Binds a Vulkan buffer, image, or acceleration structure resource to the graph associated /// with this command. /// @@ -299,7 +299,7 @@ mod deprecated { use { crate::{ Graph, Node, Resource, - cmd::{Descriptor, PipelineCommandRef, SubresourceRange, View, ViewInfo}, + cmd::{Descriptor, PipelineCommand, SubresourceRange, View, ViewInfo}, deprecated::Info, graph::pass_ref::ViewType, }, @@ -307,7 +307,7 @@ mod deprecated { vk_sync::AccessType, }; - impl<'a, T> PipelineCommandRef<'a, T> { + impl<'a, T> PipelineCommand<'a, T> { #[deprecated = "use shader_resource_access function"] #[doc(hidden)] pub fn access_descriptor( diff --git a/src/cmd/ray_trace.rs b/src/cmd/ray_trace.rs index 294269fa..e3a6e1fd 100644 --- a/src/cmd/ray_trace.rs +++ b/src/cmd/ray_trace.rs @@ -1,5 +1,5 @@ use { - super::{PipelineCommandRef, cmd_buf::CommandBufferRef}, + super::{PipelineCommand, cmd_buf::CommandBuffer}, crate::driver::{device::Device, ray_trace::RayTracePipeline}, ash::vk, log::trace, @@ -7,11 +7,11 @@ use { }; // NOTE: local implementation of type from super module -impl PipelineCommandRef<'_, RayTracePipeline> { +impl PipelineCommand<'_, RayTracePipeline> { /// Begin recording a ray trace pipeline command buffer. pub fn record_cmd_buf( mut self, - func: impl FnOnce(RayTraceCommandBufferRef<'_>) + Send + 'static, + func: impl FnOnce(RayTraceCommandBuffer<'_>) + Send + 'static, ) -> Self { self.record_cmd_buf_mut(func); self @@ -20,7 +20,7 @@ impl PipelineCommandRef<'_, RayTracePipeline> { /// Begin recording a ray trace pipeline command buffer. pub fn record_cmd_buf_mut( &mut self, - func: impl FnOnce(RayTraceCommandBufferRef<'_>) + Send + 'static, + func: impl FnOnce(RayTraceCommandBuffer<'_>) + Send + 'static, ) { let pipeline = self .cmd @@ -38,7 +38,7 @@ impl PipelineCommandRef<'_, RayTracePipeline> { let dynamic_stack_size = pipeline.inner.info.dynamic_stack_size; self.cmd.push_exec(move |cmd_buf| { - func(RayTraceCommandBufferRef { + func(RayTraceCommandBuffer { cmd_buf, #[cfg(debug_assertions)] @@ -54,7 +54,7 @@ impl PipelineCommandRef<'_, RayTracePipeline> { /// /// This structure provides a strongly-typed set of methods which allow ray trace shader code to be /// executed. An instance of `RayTrace` is provided to the closure parameter of -/// [`PipelineCommandRef::record_pipeline`] which may be accessed by binding a [`RayTracePipeline`] to +/// [`PipelineCommand::record_pipeline`] which may be accessed by binding a [`RayTracePipeline`] to /// a render pass. /// /// # Examples @@ -85,8 +85,8 @@ impl PipelineCommandRef<'_, RayTracePipeline> { /// }); /// # Ok(()) } /// ``` -pub struct RayTraceCommandBufferRef<'a> { - cmd_buf: CommandBufferRef<'a>, +pub struct RayTraceCommandBuffer<'a> { + cmd_buf: CommandBuffer<'a>, #[cfg(debug_assertions)] dynamic_stack_size: bool, @@ -94,7 +94,7 @@ pub struct RayTraceCommandBufferRef<'a> { pipeline: RayTracePipeline, } -impl RayTraceCommandBufferRef<'_> { +impl RayTraceCommandBuffer<'_> { /// Updates push constants. /// /// Push constants represent a high speed path to modify constant data in pipelines that is @@ -321,8 +321,8 @@ impl RayTraceCommandBufferRef<'_> { } } -impl<'a> Deref for RayTraceCommandBufferRef<'a> { - type Target = CommandBufferRef<'a>; +impl<'a> Deref for RayTraceCommandBuffer<'a> { + type Target = CommandBuffer<'a>; fn deref(&self) -> &Self::Target { &self.cmd_buf @@ -335,15 +335,15 @@ mod deprecated { crate::{ Node, cmd::{ - Descriptor, PipelineCommandRef, SubresourceRange, View, ViewInfo, - ray_trace::RayTraceCommandBufferRef, + Descriptor, PipelineCommand, SubresourceRange, View, ViewInfo, + ray_trace::RayTraceCommandBuffer, }, driver::ray_trace::RayTracePipeline, }, vk_sync::AccessType, }; - impl RayTraceCommandBufferRef<'_> { + impl RayTraceCommandBuffer<'_> { #[deprecated = "use push_constants function"] #[doc(hidden)] pub fn push_constants_offset(&self, offset: u32, data: &[u8]) -> &Self { @@ -351,7 +351,7 @@ mod deprecated { } } - impl PipelineCommandRef<'_, RayTracePipeline> { + impl PipelineCommand<'_, RayTracePipeline> { #[deprecated = "use shader_resource_access function with AccessType::RayTracingShaderReadSampledImageOrUniformTexelBuffer"] #[doc(hidden)] pub fn read_descriptor(self, descriptor: impl Into, node: N) -> Self @@ -394,7 +394,7 @@ mod deprecated { #[doc(hidden)] pub fn record_ray_trace( self, - func: impl FnOnce(RayTraceCommandBufferRef<'_>, ()) + Send + 'static, + func: impl FnOnce(RayTraceCommandBuffer<'_>, ()) + Send + 'static, ) -> Self { self.record_cmd_buf(|cmd_buf| { func(cmd_buf, ()); diff --git a/src/lib.rs b/src/lib.rs index 72c07871..7ad680ee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -360,7 +360,7 @@ mod queue; use std::sync::Arc; use crate::{ - cmd::{ClearColorValue, CommandBufferRef}, + cmd::{ClearColor, CommandBuffer}, driver::{ accel_struct::AccelerationStructure, buffer::Buffer, image::Image, swapchain::SwapchainImage, @@ -375,7 +375,7 @@ pub use self::deprecated::{Display, DisplayInfo, DisplayInfoBuilder}; use { self::{ - cmd::{AttachmentIndex, CommandRef, Descriptor, SubresourceAccess, ViewInfo}, + cmd::{AttachmentIndex, Command, Descriptor, SubresourceAccess, ViewInfo}, node::{ AccelerationStructureLeaseNode, AccelerationStructureNode, AnyAccelerationStructureNode, AnyBufferNode, AnyImageNode, BufferLeaseNode, BufferNode, @@ -403,7 +403,7 @@ use { vk_sync::AccessType, }; -type ExecFn = Box; +type ExecFn = Box; type NodeIndex = usize; #[derive(Debug)] @@ -626,12 +626,12 @@ impl ExecutionPipeline { } #[derive(Debug)] -struct Command { +struct CommandData { execs: Vec, name: Option, } -impl Command { +impl CommandData { fn descriptor_pools_sizes( &self, ) -> impl Iterator>> { @@ -656,7 +656,7 @@ impl Command { /// [`graph.cpp`](https://github.com/Themaister/Granite/blob/master/renderer/graph.cpp). #[derive(Debug, Default)] pub struct Graph { - cmds: Vec, + cmds: Vec, resources: Vec, } @@ -667,8 +667,8 @@ impl Graph { } /// Allocates and begins writing a new command. - pub fn begin_cmd(&mut self) -> CommandRef<'_> { - CommandRef::new(self) + pub fn begin_cmd(&mut self) -> Command<'_> { + Command::new(self) } /// Binds a Vulkan buffer, image, or acceleration structure resource to this graph. @@ -778,7 +778,7 @@ impl Graph { pub fn clear_color_image( &mut self, image: impl Into, - color: impl Into, + color: impl Into, ) -> &mut Self { let color = color.into().into(); let image = image.into(); @@ -1519,7 +1519,7 @@ pub mod graph { #[doc(hidden)] pub mod pass_ref { #[deprecated = "use vk_graph::cmd::CommandBufferRef"] - pub type Acceleration<'a> = crate::cmd::CommandBufferRef<'a>; + pub type Acceleration<'a> = crate::cmd::CommandBuffer<'a>; #[deprecated = "use vk_graph::cmd::CommandBufferRef"] pub type AccelerationStructureBuildInfo = crate::cmd::BuildAccelerationStructureInfo; @@ -1539,16 +1539,16 @@ pub mod graph { pub type Descriptor = crate::cmd::Descriptor; #[deprecated = "use vk_graph::cmd::GraphicCommandBufferRef"] - pub type Draw<'a> = crate::cmd::GraphicCommandBufferRef<'a>; + pub type Draw<'a> = crate::cmd::GraphicCommandBuffer<'a>; #[deprecated = "use vk_graph::cmd::CommandRef"] - pub type PassRef<'a> = crate::cmd::CommandRef<'a>; + pub type PassRef<'a> = crate::cmd::Command<'a>; - #[deprecated = "use vk_graph::cmd::PipelineCommandRef"] - pub type PipelinePassRef<'a, T> = crate::cmd::PipelineCommandRef<'a, T>; + #[deprecated = "use vk_graph::cmd::PipelineCommand"] + pub type PipelinePassRef<'a, T> = crate::cmd::PipelineCommand<'a, T>; #[deprecated = "use vk_graph::cmd::RayTraceCommandBufferRef"] - pub type RayTrace<'a> = crate::cmd::RayTraceCommandBufferRef<'a>; + pub type RayTrace<'a> = crate::cmd::RayTraceCommandBuffer<'a>; #[deprecated = "use vk_graph::ViewInfo"] pub type ViewType = crate::cmd::ViewInfo; diff --git a/src/queue.rs b/src/queue.rs index 2901b612..4f265bd2 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -1,10 +1,9 @@ use { super::{ - AnyResource, Attachment, Command, ExecutionPipeline, Graph, Node, NodeIndex, + AnyResource, Attachment, CommandData, ExecutionPipeline, Graph, Node, NodeIndex, cmd::{SubresourceAccess, SubresourceRange}, }, crate::{ - cmd::CommandBufferRef, driver::{ AttachmentInfo, AttachmentRef, Descriptor, DescriptorInfo, DescriptorSet, DriverError, FramebufferAttachmentImageInfo, FramebufferInfo, SubpassDependency, SubpassInfo, @@ -202,8 +201,8 @@ impl Queue { } #[profiling::function] - fn allow_merge_passes(lhs: &Command, rhs: &Command) -> bool { - fn first_graphic_pipeline(pass: &Command) -> Option<&GraphicPipeline> { + fn allow_merge_passes(lhs: &CommandData, rhs: &CommandData) -> bool { + fn first_graphic_pipeline(pass: &CommandData) -> Option<&GraphicPipeline> { pass.execs .first() .and_then(|exec| exec.pipeline.as_ref().map(ExecutionPipeline::as_graphic)) @@ -402,7 +401,7 @@ impl Queue { fn begin_render_pass( cmd_buf: &CommandBuffer, bindings: &[AnyResource], - pass: &Command, + pass: &CommandData, physical_pass: &mut PhysicalPass, render_area: vk::Rect2D, ) -> Result<(), DriverError> { @@ -697,7 +696,7 @@ impl Queue { #[profiling::function] fn lease_descriptor_pool

( pool: &mut P, - pass: &Command, + pass: &CommandData, ) -> Result>, DriverError> where P: Pool, @@ -1753,7 +1752,7 @@ impl Queue { #[profiling::function] fn merge_scheduled_passes(&mut self, schedule: &mut Vec) { thread_local! { - static PASSES: RefCell>> = Default::default(); + static PASSES: RefCell>> = Default::default(); } PASSES.with_borrow_mut(|passes| { @@ -2137,7 +2136,7 @@ impl Queue { fn record_image_layout_transitions( cmd_buf: &CommandBuffer, resources: &mut [AnyResource], - pass: &mut Command, + pass: &mut CommandData, ) { // We store a Barriers in TLS to save an alloc; contents are POD thread_local! { @@ -2476,7 +2475,7 @@ impl Queue { profiling::scope!("Execute callback"); let exec_func = exec.func.take().unwrap().0; - exec_func(CommandBufferRef::new( + exec_func(crate::cmd::CommandBuffer::new( cmd_buf, &self.graph.resources, #[cfg(debug_assertions)] @@ -2491,7 +2490,7 @@ impl Queue { } thread_local! { - static PASSES: RefCell> = Default::default(); + static PASSES: RefCell> = Default::default(); } PASSES.with_borrow_mut(|passes| { @@ -2530,7 +2529,7 @@ impl Queue { } #[profiling::function] - fn render_extent(bindings: &[AnyResource], pass: &Command) -> vk::Extent2D { + fn render_extent(bindings: &[AnyResource], pass: &CommandData) -> vk::Extent2D { // set_render_area was not specified so we're going to guess using the minimum common // attachment extents let first_exec = pass.execs.first().unwrap(); @@ -2964,7 +2963,7 @@ impl Queue { fn write_descriptor_sets( cmd_buf: &CommandBuffer, bindings: &[AnyResource], - pass: &Command, + pass: &CommandData, physical_pass: &PhysicalPass, ) -> Result<(), DriverError> { struct IndexWrite<'a> { From 15984181179ee2aaa7e11a25ba18a7f45593adf2 Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 19 Mar 2026 06:07:29 -0400 Subject: [PATCH 49/86] make push const code common --- src/cmd/cmd_buf.rs | 33 ++ src/cmd/compute.rs | 98 ++-- src/cmd/graphic.rs | 1034 +++++++++++++++++++++--------------------- src/cmd/ray_trace.rs | 31 +- 4 files changed, 585 insertions(+), 611 deletions(-) diff --git a/src/cmd/cmd_buf.rs b/src/cmd/cmd_buf.rs index 0bc1794f..8fe9f6a5 100644 --- a/src/cmd/cmd_buf.rs +++ b/src/cmd/cmd_buf.rs @@ -10,6 +10,7 @@ use { }, }, ash::vk, + log::trace, std::{cell::RefCell, ops::Deref}, }; @@ -303,6 +304,38 @@ impl<'a> CommandBuffer<'a> { self } + pub(crate) fn cmd_push_constants( + &self, + layout: vk::PipelineLayout, + push_consts: &[vk::PushConstantRange], + offset: u32, + data: &[u8], + ) { + for push_const in push_consts { + let push_const_end = push_const.offset + push_const.size; + let data_end = offset + data.len() as u32; + let end = data_end.min(push_const_end); + let start = offset.max(push_const.offset); + + if end > start { + trace!( + " push constants {:?} {}..{}", + push_const.stage_flags, start, end + ); + + unsafe { + self.device.cmd_push_constants( + self.handle, + layout, + push_const.stage_flags, + start, + &data[(start - offset) as usize..(end - offset) as usize], + ); + } + } + } + } + /// Update acceleration structures. /// /// There is no ordering or synchronization implied between any of the individual acceleration diff --git a/src/cmd/compute.rs b/src/cmd/compute.rs index fb500cf8..8a6245a1 100644 --- a/src/cmd/compute.rs +++ b/src/cmd/compute.rs @@ -2,10 +2,42 @@ use { super::{cmd_buf::CommandBuffer, pipeline::PipelineCommand}, crate::{driver::compute::ComputePipeline, node::AnyBufferNode}, ash::vk, - log::trace, std::ops::Deref, }; +impl PipelineCommand<'_, ComputePipeline> { + /// Begin recording a compute pipeline command buffer. + pub fn record_cmd_buf( + mut self, + func: impl FnOnce(ComputeCommandBuffer<'_>) + Send + 'static, + ) -> Self { + self.record_cmd_buf_mut(func); + self + } + + /// Begin recording a compute pipeline command buffer. + pub fn record_cmd_buf_mut( + &mut self, + func: impl FnOnce(ComputeCommandBuffer<'_>) + Send + 'static, + ) { + let pipeline = self + .cmd + .cmd() + .execs + .last() + .unwrap() + .pipeline + .as_ref() + .unwrap() + .unwrap_compute() + .clone(); + + self.cmd.push_exec(move |cmd_buf| { + func(ComputeCommandBuffer { cmd_buf, pipeline }); + }); + } +} + /// Recording interface for computing commands. /// /// This structure provides a strongly-typed set of methods which allow compute shader code to be @@ -288,30 +320,12 @@ impl ComputeCommandBuffer<'_> { /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all #[profiling::function] pub fn push_constants(&self, offset: u32, data: &[u8]) -> &Self { - if let Some(push_const) = self.pipeline.inner.push_constants { - // Determine the range of the overall pipline push constants which overlap with `data` - let push_const_end = push_const.offset + push_const.size; - let data_end = offset + data.len() as u32; - let end = data_end.min(push_const_end); - let start = offset.max(push_const.offset); - - if end > start { - trace!( - " push constants {:?} {}..{}", - push_const.stage_flags, start, end - ); - - unsafe { - self.cmd_buf.device.cmd_push_constants( - self.cmd_buf.handle, - self.pipeline.inner.layout, - vk::ShaderStageFlags::COMPUTE, - push_const.offset, - &data[(start - offset) as usize..(end - offset) as usize], - ); - } - } - } + self.cmd_push_constants( + self.pipeline.inner.layout, + self.pipeline.inner.push_constants.as_slice(), + offset, + data, + ); self } @@ -325,40 +339,6 @@ impl<'a> Deref for ComputeCommandBuffer<'a> { } } -// NOTE: local implementation of type from super module -impl PipelineCommand<'_, ComputePipeline> { - /// Begin recording a compute pipeline command buffer. - pub fn record_cmd_buf( - mut self, - func: impl FnOnce(ComputeCommandBuffer<'_>) + Send + 'static, - ) -> Self { - self.record_cmd_buf_mut(func); - self - } - - /// Begin recording a compute pipeline command buffer. - pub fn record_cmd_buf_mut( - &mut self, - func: impl FnOnce(ComputeCommandBuffer<'_>) + Send + 'static, - ) { - let pipeline = self - .cmd - .cmd() - .execs - .last() - .unwrap() - .pipeline - .as_ref() - .unwrap() - .unwrap_compute() - .clone(); - - self.cmd.push_exec(move |cmd_buf| { - func(ComputeCommandBuffer { cmd_buf, pipeline }); - }); - } -} - #[allow(unused)] mod deprecated { use { diff --git a/src/cmd/graphic.rs b/src/cmd/graphic.rs index 4b736007..96e85533 100644 --- a/src/cmd/graphic.rs +++ b/src/cmd/graphic.rs @@ -9,10 +9,438 @@ use { node::{AnyBufferNode, AnyImageNode}, }, ash::vk, - log::trace, std::{cell::RefCell, ops::Deref, slice}, }; +impl PipelineCommand<'_, GraphicPipeline> { + /// TODO + pub fn color_attachment_image( + mut self, + color_attachment_idx: AttachmentIndex, + color_image: impl Into, + load: LoadOp, + store: StoreOp, + ) -> Self { + self.set_color_attachment_image(color_attachment_idx, color_image, load, store); + self + } + + /// TODO + pub fn color_attachment_image_view( + mut self, + color_attachment_idx: AttachmentIndex, + color_image: impl Into, + color_image_view_info: impl Into, + load: LoadOp, + store: StoreOp, + ) -> Self { + self.set_color_attachment_image_view( + color_attachment_idx, + color_image, + color_image_view_info, + load, + store, + ); + self + } + + /// TODO + pub fn color_attachment_resolve_image( + mut self, + msaa_attachment_idx: AttachmentIndex, + color_attachment_idx: AttachmentIndex, + color_image: impl Into, + ) -> Self { + self.set_color_attachment_resolve_image( + msaa_attachment_idx, + color_attachment_idx, + color_image, + ); + self + } + + /// TODO + pub fn color_attachment_resolve_image_view( + mut self, + msaa_attachment_idx: AttachmentIndex, + color_attachment_idx: AttachmentIndex, + color_image: impl Into, + color_image_view_info: impl Into, + ) -> Self { + self.set_color_attachment_resolve_image_view( + msaa_attachment_idx, + color_attachment_idx, + color_image, + color_image_view_info, + ); + self + } + + /// Sets the combined depth and stencil state used by any subsequent command buffer recordings + /// of the current graph command. + pub fn depth_stencil(mut self, depth_stencil: impl Into) -> Self { + self.set_depth_stencil(depth_stencil); + self + } + + /// TODO + pub fn depth_stencil_attachment_image( + mut self, + depth_stencil_image: impl Into, + load: LoadOp, + store: StoreOp, + ) -> Self { + self.set_depth_stencil_attachment_image(depth_stencil_image, load, store); + self + } + + /// TODO + pub fn depth_stencil_attachment_image_view( + mut self, + depth_stencil_image: impl Into, + depth_stencil_image_view_info: impl Into, + load: LoadOp, + store: StoreOp, + ) -> Self { + self.set_depth_stencil_attachment_image_view( + depth_stencil_image, + depth_stencil_image_view_info, + load, + store, + ); + self + } + + /// TODO + pub fn depth_stencil_attachment_resolve_image( + mut self, + attachment_idx: AttachmentIndex, + depth_stencil_image: impl Into, + depth_mode: Option, + stencil_mode: Option, + ) -> Self { + self.set_depth_stencil_attachment_resolve_image( + attachment_idx, + depth_stencil_image, + depth_mode, + stencil_mode, + ); + self + } + + /// TODO + pub fn depth_stencil_attachment_resolve_image_view( + mut self, + attachment_idx: AttachmentIndex, + depth_stencil_image: impl Into, + depth_stencil_image_view_info: impl Into, + depth_mode: Option, + stencil_mode: Option, + ) -> Self { + self.set_depth_stencil_attachment_resolve_image_view( + attachment_idx, + depth_stencil_image, + depth_stencil_image_view_info, + depth_mode, + stencil_mode, + ); + self + } + + /// Sets multiview view and correlation masks used by any subsequent command buffer recordings + /// of the current graph command. + /// + /// See [`VkRenderPassMultiviewCreateInfo`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkRenderPassMultiviewCreateInfo.html#_description). + pub fn multiview(mut self, view_mask: u32, correlated_view_mask: u32) -> Self { + self.set_multiview(view_mask, correlated_view_mask); + self + } + + /// Begin recording a graphics pipeline command buffer. + pub fn record_cmd_buf( + mut self, + func: impl FnOnce(GraphicCommandBuffer<'_>) + Send + 'static, + ) -> Self { + self.record_cmd_buf_mut(func); + self + } + + /// Begin recording a graphics pipeline command buffer. + pub fn record_cmd_buf_mut( + &mut self, + func: impl FnOnce(GraphicCommandBuffer<'_>) + Send + 'static, + ) { + let pipeline = self + .cmd + .cmd() + .execs + .last() + .unwrap() + .pipeline + .as_ref() + .unwrap() + .unwrap_graphic() + .clone(); + + self.cmd.push_exec(move |cmd_buf| { + func(GraphicCommandBuffer { cmd_buf, pipeline }); + }); + } + + /// Sets the [`renderArea`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkRenderPassBeginInfo.html#_c_specification) + /// field when beginning a render pass used by any subsequent command buffer recordings + /// of the current graph command. + /// + /// _NOTE:_ Setting this value will cause the viewport and scissor to be unset, which is not the + /// default behavior. When this value is set you should call `set_viewport` and `set_scissor` on + /// the command buffer. + /// + /// If not set, this value defaults to the first loaded, resolved, or stored attachment + /// dimensions and sets the viewport and scissor to the same values, with a `0..1` depth if not + /// specified by `depth_stencil`. + pub fn render_area(mut self, area: vk::Rect2D) -> Self { + self.set_render_area(area); + self + } + + /// See [color_attachment_image] + pub fn set_color_attachment_image( + &mut self, + color_attachment_idx: AttachmentIndex, + color_image: impl Into, + load: LoadOp, + store: StoreOp, + ) -> &mut Self { + let color_image = color_image.into(); + let color_image_view = self.resource(color_image).info; + + self.set_color_attachment_image_view( + color_attachment_idx, + color_image, + color_image_view, + load, + store, + ); + + self + } + + /// See [color_attachment_image_view] + pub fn set_color_attachment_image_view( + &mut self, + color_attachment_idx: AttachmentIndex, + color_image: impl Into, + color_image_view_info: impl Into, + load: LoadOp, + store: StoreOp, + ) -> &mut Self { + let color_image = color_image.into(); + let color_image_view_info = color_image_view_info.into(); + + #[allow(deprecated)] + { + match load { + LoadOp::Clear(color) => self.set_clear_color_value_as( + color_attachment_idx, + color_image, + color, + color_image_view_info, + ), + LoadOp::DontCare => self.set_attach_color_as( + color_attachment_idx, + color_image, + color_image_view_info, + ), + LoadOp::Load => { + self.set_load_color_as(color_attachment_idx, color_image, color_image_view_info) + } + }; + + if let StoreOp::Store = store { + self.set_store_color_as(color_attachment_idx, color_image, color_image_view_info); + } + } + + self + } + + /// See [color_attachment_resolve_image] + pub fn set_color_attachment_resolve_image( + &mut self, + msaa_attachment_idx: AttachmentIndex, + color_attachment_idx: AttachmentIndex, + color_image: impl Into, + ) -> &mut Self { + let color_image = color_image.into(); + let color_image_view = self.resource(color_image).info; + + self.set_color_attachment_resolve_image_view( + msaa_attachment_idx, + color_attachment_idx, + color_image, + color_image_view, + ); + + self + } + + /// See [color_attachment_resolve_image_view] + pub fn set_color_attachment_resolve_image_view( + &mut self, + msaa_attachment_idx: AttachmentIndex, + color_attachment_idx: AttachmentIndex, + color_image: impl Into, + color_image_view_info: impl Into, + ) -> &mut Self { + let color_image = color_image.into(); + let color_image_view_info = color_image_view_info.into(); + + #[allow(deprecated)] + self.set_attach_color_as(color_attachment_idx, color_image, color_image_view_info) + .set_resolve_color_as( + msaa_attachment_idx, + color_attachment_idx, + color_image, + color_image_view_info, + ); + + self + } + + /// See [depth_stencil] + pub fn set_depth_stencil(&mut self, depth_stencil: impl Into) -> &mut Self { + let depth_stencil = depth_stencil.into(); + let cmd = self.cmd.cmd_mut(); + let exec = cmd.execs.last_mut().unwrap(); + + assert!(exec.depth_stencil.is_none()); + + exec.depth_stencil = Some(depth_stencil); + + self + } + + /// See [depth_stencil_attachment_image] + pub fn set_depth_stencil_attachment_image( + &mut self, + depth_stencil_image: impl Into, + load: LoadOp, + store: StoreOp, + ) -> &mut Self { + let depth_stencil_image = depth_stencil_image.into(); + let depth_stencil_image_view_info = self.resource(depth_stencil_image).info; + + self.set_depth_stencil_attachment_image_view( + depth_stencil_image, + depth_stencil_image_view_info, + load, + store, + ); + + self + } + + /// See [depth_stencil_attachment_image_view] + pub fn set_depth_stencil_attachment_image_view( + &mut self, + depth_stencil_image: impl Into, + depth_stencil_image_view_info: impl Into, + load: LoadOp, + store: StoreOp, + ) -> &mut Self { + let depth_stencil_image = depth_stencil_image.into(); + let depth_stencil_image_view_info = depth_stencil_image_view_info.into(); + + #[allow(deprecated)] + { + match load { + LoadOp::Clear(color) => self.set_clear_depth_stencil_value_as( + depth_stencil_image, + color.depth, + color.stencil, + depth_stencil_image_view_info, + ), + LoadOp::DontCare => self.set_attach_depth_stencil_as( + depth_stencil_image, + depth_stencil_image_view_info, + ), + LoadOp::Load => self + .set_load_depth_stencil_as(depth_stencil_image, depth_stencil_image_view_info), + }; + + if let StoreOp::Store = store { + self.set_store_depth_stencil_as(depth_stencil_image, depth_stencil_image_view_info); + } + } + + self + } + + /// See [depth_stencil_attachment_resolve_image] + pub fn set_depth_stencil_attachment_resolve_image( + &mut self, + attachment_idx: AttachmentIndex, + depth_stencil_image: impl Into, + depth_mode: Option, + stencil_mode: Option, + ) -> &mut Self { + let depth_stencil_image = depth_stencil_image.into(); + let depth_stencil_image_view = self.resource(depth_stencil_image).info; + + self.set_depth_stencil_attachment_resolve_image_view( + attachment_idx, + depth_stencil_image, + depth_stencil_image_view, + depth_mode, + stencil_mode, + ); + + self + } + + /// See [depth_stencil_attachment_resolve_image_view] + pub fn set_depth_stencil_attachment_resolve_image_view( + &mut self, + attachment_idx: AttachmentIndex, + depth_stencil_image: impl Into, + depth_stencil_image_view_info: impl Into, + depth_mode: Option, + stencil_mode: Option, + ) -> &mut Self { + let depth_stencil_image = depth_stencil_image.into(); + let depth_stencil_image_view_info = depth_stencil_image_view_info.into(); + + #[allow(deprecated)] + self.set_attach_depth_stencil_as(depth_stencil_image, depth_stencil_image_view_info) + .set_resolve_depth_stencil_as( + attachment_idx, + depth_stencil_image, + depth_stencil_image_view_info, + depth_mode, + stencil_mode, + ); + + self + } + + /// See [multiview] + pub fn set_multiview(&mut self, view_mask: u32, correlated_view_mask: u32) -> &mut Self { + let cmd = self.cmd.cmd_mut(); + let exec = cmd.execs.last_mut().unwrap(); + + exec.correlated_view_mask = correlated_view_mask; + exec.view_mask = view_mask; + + self + } + + /// See [render_area] + pub fn set_render_area(&mut self, area: vk::Rect2D) -> &mut Self { + self.cmd.cmd_mut().execs.last_mut().unwrap().render_area = Some(area); + self + } +} + /// TODO #[derive(Clone, Copy, Debug)] pub enum ClearColor { @@ -93,73 +521,11 @@ impl From<[u32; 4]> for ClearColor { impl From for vk::ClearColorValue { fn from(value: ClearColor) -> Self { match value { - ClearColor::Float32(float32) => Self { float32 }, - ClearColor::Int32(int32) => Self { int32 }, - ClearColor::Uint32(uint32) => Self { uint32 }, - } - } -} - -/// TODO -#[derive(Clone, Copy, Debug)] -pub enum LoadOp { - /// TODO - Clear(T), - - /// TODO - DontCare, - - /// TODO - Load, -} - -impl LoadOp { - /// A load operation which results in a color attachment filled with rgb zeros and alpha ones. - pub const CLEAR_BLACK_ALPHA_ONE: Self = Self::Clear(ClearColor::BLACK_ALPHA_ONE); - - /// A load operation which results in a color attachment filled with zeros. - pub const CLEAR_BLACK_ALPHA_ZERO: Self = Self::Clear(ClearColor::BLACK_ALPHA_ZERO); - - /// A load operation which results in a color attachment filled with rgb zeros and alpha ones. - pub const CLEAR_WHITE_ALPHA_ONE: Self = Self::Clear(ClearColor::WHITE_ALPHA_ONE); - - /// A load operation which results in a color attachment filled with rgb ones and alpha zeros. - pub const CLEAR_WHITE_ALPHA_ZERO: Self = Self::Clear(ClearColor::WHITE_ALPHA_ZERO); - - /// Convenience constructor for clear color values. - pub fn clear_rgba(r: f32, g: f32, b: f32, a: f32) -> Self { - Self::Clear(ClearColor::rgba(r, g, b, a)) - } -} - -impl LoadOp { - /// A load operation which results in a depth attachment filled with ones and stencil filled - /// with zeros. - pub const CLEAR_ONE_STENCIL_ZERO: Self = Self::Clear(vk::ClearDepthStencilValue { - depth: 1.0, - stencil: 0, - }); - - /// A load operation which results in a depth and stencil attachment filled with zeros. - pub const CLEAR_ZERO_STENCIL_ZERO: Self = Self::Clear(vk::ClearDepthStencilValue { - depth: 0.0, - stencil: 0, - }); - - /// Convenience constructor for clear depth and stencil values. - pub fn clear_depth_stencil(depth: f32, stencil: u32) -> Self { - Self::Clear(vk::ClearDepthStencilValue { depth, stencil }) - } -} - -/// TODO -#[derive(Clone, Copy, Debug)] -pub enum StoreOp { - /// TODO - DontCare, - - /// TODO - Store, + ClearColor::Float32(float32) => Self { float32 }, + ClearColor::Int32(int32) => Self { int32 }, + ClearColor::Uint32(uint32) => Self { uint32 }, + } + } } /// Recording interface for drawing commands. @@ -725,496 +1091,110 @@ impl GraphicCommandBuffer<'_> { /// /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all #[profiling::function] - pub fn push_constants(&self, offset: u32, data: &[u8]) -> &Self { - for push_const in self.pipeline.inner.push_constants.iter() { - // Determine the range of the overall pipline push constants which overlap with `data` - let push_const_end = push_const.offset + push_const.size; - let data_end = offset + data.len() as u32; - let end = data_end.min(push_const_end); - let start = offset.max(push_const.offset); - - if end > start { - trace!( - " push constants {:?} {}..{}", - push_const.stage_flags, start, end - ); - - unsafe { - self.cmd_buf.device.cmd_push_constants( - self.cmd_buf.handle, - self.pipeline.inner.layout, - push_const.stage_flags, - start, - &data[(start - offset) as usize..(end - offset) as usize], - ); - } - } - } - - self - } - - /// Set scissor rectangle dynamically for the current command. - #[profiling::function] - pub fn set_scissor(&self, first_scissor: u32, scissors: &[vk::Rect2D]) -> &Self { - unsafe { - self.cmd_buf - .device - .cmd_set_scissor(self.cmd_buf.handle, first_scissor, scissors); - } - - self - } - - /// Set the viewport dynamically for the current command. - #[profiling::function] - pub fn set_viewport(&self, first_viewport: u32, viewports: &[vk::Viewport]) -> &Self { - unsafe { - self.cmd_buf - .device - .cmd_set_viewport(self.cmd_buf.handle, first_viewport, viewports); - } - - self - } -} - -impl<'a> Deref for GraphicCommandBuffer<'a> { - type Target = CommandBuffer<'a>; - - fn deref(&self) -> &Self::Target { - &self.cmd_buf - } -} - -// NOTE: local implementation of type from super module -impl PipelineCommand<'_, GraphicPipeline> { - /// TODO - pub fn color_attachment_image( - mut self, - color_attachment_idx: AttachmentIndex, - color_image: impl Into, - load: LoadOp, - store: StoreOp, - ) -> Self { - self.set_color_attachment_image(color_attachment_idx, color_image, load, store); - self - } - - /// TODO - pub fn color_attachment_image_view( - mut self, - color_attachment_idx: AttachmentIndex, - color_image: impl Into, - color_image_view_info: impl Into, - load: LoadOp, - store: StoreOp, - ) -> Self { - self.set_color_attachment_image_view( - color_attachment_idx, - color_image, - color_image_view_info, - load, - store, - ); - self - } - - /// TODO - pub fn color_attachment_resolve_image( - mut self, - msaa_attachment_idx: AttachmentIndex, - color_attachment_idx: AttachmentIndex, - color_image: impl Into, - ) -> Self { - self.set_color_attachment_resolve_image( - msaa_attachment_idx, - color_attachment_idx, - color_image, - ); - self - } - - /// TODO - pub fn color_attachment_resolve_image_view( - mut self, - msaa_attachment_idx: AttachmentIndex, - color_attachment_idx: AttachmentIndex, - color_image: impl Into, - color_image_view_info: impl Into, - ) -> Self { - self.set_color_attachment_resolve_image_view( - msaa_attachment_idx, - color_attachment_idx, - color_image, - color_image_view_info, - ); - self - } - - /// Sets the combined depth and stencil state used by any subsequent command buffer recordings - /// of the current graph command. - pub fn depth_stencil(mut self, depth_stencil: impl Into) -> Self { - self.set_depth_stencil(depth_stencil); - self - } - - /// TODO - pub fn depth_stencil_attachment_image( - mut self, - depth_stencil_image: impl Into, - load: LoadOp, - store: StoreOp, - ) -> Self { - self.set_depth_stencil_attachment_image(depth_stencil_image, load, store); - self - } - - /// TODO - pub fn depth_stencil_attachment_image_view( - mut self, - depth_stencil_image: impl Into, - depth_stencil_image_view_info: impl Into, - load: LoadOp, - store: StoreOp, - ) -> Self { - self.set_depth_stencil_attachment_image_view( - depth_stencil_image, - depth_stencil_image_view_info, - load, - store, - ); - self - } - - /// TODO - pub fn depth_stencil_attachment_resolve_image( - mut self, - attachment_idx: AttachmentIndex, - depth_stencil_image: impl Into, - depth_mode: Option, - stencil_mode: Option, - ) -> Self { - self.set_depth_stencil_attachment_resolve_image( - attachment_idx, - depth_stencil_image, - depth_mode, - stencil_mode, - ); - self - } - - /// TODO - pub fn depth_stencil_attachment_resolve_image_view( - mut self, - attachment_idx: AttachmentIndex, - depth_stencil_image: impl Into, - depth_stencil_image_view_info: impl Into, - depth_mode: Option, - stencil_mode: Option, - ) -> Self { - self.set_depth_stencil_attachment_resolve_image_view( - attachment_idx, - depth_stencil_image, - depth_stencil_image_view_info, - depth_mode, - stencil_mode, - ); - self - } - - /// Sets multiview view and correlation masks used by any subsequent command buffer recordings - /// of the current graph command. - /// - /// See [`VkRenderPassMultiviewCreateInfo`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkRenderPassMultiviewCreateInfo.html#_description). - pub fn multiview(mut self, view_mask: u32, correlated_view_mask: u32) -> Self { - self.set_multiview(view_mask, correlated_view_mask); - self - } - - /// Begin recording a graphics pipeline command buffer. - pub fn record_cmd_buf( - mut self, - func: impl FnOnce(GraphicCommandBuffer<'_>) + Send + 'static, - ) -> Self { - self.record_cmd_buf_mut(func); - self - } - - /// Begin recording a graphics pipeline command buffer. - pub fn record_cmd_buf_mut( - &mut self, - func: impl FnOnce(GraphicCommandBuffer<'_>) + Send + 'static, - ) { - let pipeline = self - .cmd - .cmd() - .execs - .last() - .unwrap() - .pipeline - .as_ref() - .unwrap() - .unwrap_graphic() - .clone(); - - self.cmd.push_exec(move |cmd_buf| { - func(GraphicCommandBuffer { cmd_buf, pipeline }); - }); - } - - /// Sets the [`renderArea`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkRenderPassBeginInfo.html#_c_specification) - /// field when beginning a render pass used by any subsequent command buffer recordings - /// of the current graph command. - /// - /// _NOTE:_ Setting this value will cause the viewport and scissor to be unset, which is not the - /// default behavior. When this value is set you should call `set_viewport` and `set_scissor` on - /// the command buffer. - /// - /// If not set, this value defaults to the first loaded, resolved, or stored attachment - /// dimensions and sets the viewport and scissor to the same values, with a `0..1` depth if not - /// specified by `depth_stencil`. - pub fn render_area(mut self, area: vk::Rect2D) -> Self { - self.set_render_area(area); - self - } - - /// See [color_attachment_image] - pub fn set_color_attachment_image( - &mut self, - color_attachment_idx: AttachmentIndex, - color_image: impl Into, - load: LoadOp, - store: StoreOp, - ) -> &mut Self { - let color_image = color_image.into(); - let color_image_view = self.resource(color_image).info; - - self.set_color_attachment_image_view( - color_attachment_idx, - color_image, - color_image_view, - load, - store, + pub fn push_constants(&self, offset: u32, data: &[u8]) -> &Self { + self.cmd_push_constants( + self.pipeline.inner.layout, + &self.pipeline.inner.push_constants, + offset, + data, ); self } - /// See [color_attachment_image_view] - pub fn set_color_attachment_image_view( - &mut self, - color_attachment_idx: AttachmentIndex, - color_image: impl Into, - color_image_view_info: impl Into, - load: LoadOp, - store: StoreOp, - ) -> &mut Self { - let color_image = color_image.into(); - let color_image_view_info = color_image_view_info.into(); - - #[allow(deprecated)] - { - match load { - LoadOp::Clear(color) => self.set_clear_color_value_as( - color_attachment_idx, - color_image, - color, - color_image_view_info, - ), - LoadOp::DontCare => self.set_attach_color_as( - color_attachment_idx, - color_image, - color_image_view_info, - ), - LoadOp::Load => { - self.set_load_color_as(color_attachment_idx, color_image, color_image_view_info) - } - }; - - if let StoreOp::Store = store { - self.set_store_color_as(color_attachment_idx, color_image, color_image_view_info); - } + /// Set scissor rectangle dynamically for the current command. + #[profiling::function] + pub fn set_scissor(&self, first_scissor: u32, scissors: &[vk::Rect2D]) -> &Self { + unsafe { + self.cmd_buf + .device + .cmd_set_scissor(self.cmd_buf.handle, first_scissor, scissors); } self } - /// See [color_attachment_resolve_image] - pub fn set_color_attachment_resolve_image( - &mut self, - msaa_attachment_idx: AttachmentIndex, - color_attachment_idx: AttachmentIndex, - color_image: impl Into, - ) -> &mut Self { - let color_image = color_image.into(); - let color_image_view = self.resource(color_image).info; - - self.set_color_attachment_resolve_image_view( - msaa_attachment_idx, - color_attachment_idx, - color_image, - color_image_view, - ); - - self - } - - /// See [color_attachment_resolve_image_view] - pub fn set_color_attachment_resolve_image_view( - &mut self, - msaa_attachment_idx: AttachmentIndex, - color_attachment_idx: AttachmentIndex, - color_image: impl Into, - color_image_view_info: impl Into, - ) -> &mut Self { - let color_image = color_image.into(); - let color_image_view_info = color_image_view_info.into(); - - #[allow(deprecated)] - self.set_attach_color_as(color_attachment_idx, color_image, color_image_view_info) - .set_resolve_color_as( - msaa_attachment_idx, - color_attachment_idx, - color_image, - color_image_view_info, - ); + /// Set the viewport dynamically for the current command. + #[profiling::function] + pub fn set_viewport(&self, first_viewport: u32, viewports: &[vk::Viewport]) -> &Self { + unsafe { + self.cmd_buf + .device + .cmd_set_viewport(self.cmd_buf.handle, first_viewport, viewports); + } self } +} - /// See [depth_stencil] - pub fn set_depth_stencil(&mut self, depth_stencil: impl Into) -> &mut Self { - let depth_stencil = depth_stencil.into(); - let cmd = self.cmd.cmd_mut(); - let exec = cmd.execs.last_mut().unwrap(); - - assert!(exec.depth_stencil.is_none()); - - exec.depth_stencil = Some(depth_stencil); +impl<'a> Deref for GraphicCommandBuffer<'a> { + type Target = CommandBuffer<'a>; - self + fn deref(&self) -> &Self::Target { + &self.cmd_buf } +} - /// See [depth_stencil_attachment_image] - pub fn set_depth_stencil_attachment_image( - &mut self, - depth_stencil_image: impl Into, - load: LoadOp, - store: StoreOp, - ) -> &mut Self { - let depth_stencil_image = depth_stencil_image.into(); - let depth_stencil_image_view_info = self.resource(depth_stencil_image).info; - - self.set_depth_stencil_attachment_image_view( - depth_stencil_image, - depth_stencil_image_view_info, - load, - store, - ); - - self - } +/// TODO +#[derive(Clone, Copy, Debug)] +pub enum LoadOp { + /// TODO + Clear(T), - /// See [depth_stencil_attachment_image_view] - pub fn set_depth_stencil_attachment_image_view( - &mut self, - depth_stencil_image: impl Into, - depth_stencil_image_view_info: impl Into, - load: LoadOp, - store: StoreOp, - ) -> &mut Self { - let depth_stencil_image = depth_stencil_image.into(); - let depth_stencil_image_view_info = depth_stencil_image_view_info.into(); + /// TODO + DontCare, - #[allow(deprecated)] - { - match load { - LoadOp::Clear(color) => self.set_clear_depth_stencil_value_as( - depth_stencil_image, - color.depth, - color.stencil, - depth_stencil_image_view_info, - ), - LoadOp::DontCare => self.set_attach_depth_stencil_as( - depth_stencil_image, - depth_stencil_image_view_info, - ), - LoadOp::Load => self - .set_load_depth_stencil_as(depth_stencil_image, depth_stencil_image_view_info), - }; + /// TODO + Load, +} - if let StoreOp::Store = store { - self.set_store_depth_stencil_as(depth_stencil_image, depth_stencil_image_view_info); - } - } +impl LoadOp { + /// A load operation which results in a color attachment filled with rgb zeros and alpha ones. + pub const CLEAR_BLACK_ALPHA_ONE: Self = Self::Clear(ClearColor::BLACK_ALPHA_ONE); - self - } + /// A load operation which results in a color attachment filled with zeros. + pub const CLEAR_BLACK_ALPHA_ZERO: Self = Self::Clear(ClearColor::BLACK_ALPHA_ZERO); - /// See [depth_stencil_attachment_resolve_image] - pub fn set_depth_stencil_attachment_resolve_image( - &mut self, - attachment_idx: AttachmentIndex, - depth_stencil_image: impl Into, - depth_mode: Option, - stencil_mode: Option, - ) -> &mut Self { - let depth_stencil_image = depth_stencil_image.into(); - let depth_stencil_image_view = self.resource(depth_stencil_image).info; + /// A load operation which results in a color attachment filled with rgb zeros and alpha ones. + pub const CLEAR_WHITE_ALPHA_ONE: Self = Self::Clear(ClearColor::WHITE_ALPHA_ONE); - self.set_depth_stencil_attachment_resolve_image_view( - attachment_idx, - depth_stencil_image, - depth_stencil_image_view, - depth_mode, - stencil_mode, - ); + /// A load operation which results in a color attachment filled with rgb ones and alpha zeros. + pub const CLEAR_WHITE_ALPHA_ZERO: Self = Self::Clear(ClearColor::WHITE_ALPHA_ZERO); - self + /// Convenience constructor for clear color values. + pub fn clear_rgba(r: f32, g: f32, b: f32, a: f32) -> Self { + Self::Clear(ClearColor::rgba(r, g, b, a)) } +} - /// See [depth_stencil_attachment_resolve_image_view] - pub fn set_depth_stencil_attachment_resolve_image_view( - &mut self, - attachment_idx: AttachmentIndex, - depth_stencil_image: impl Into, - depth_stencil_image_view_info: impl Into, - depth_mode: Option, - stencil_mode: Option, - ) -> &mut Self { - let depth_stencil_image = depth_stencil_image.into(); - let depth_stencil_image_view_info = depth_stencil_image_view_info.into(); +impl LoadOp { + /// A load operation which results in a depth attachment filled with ones and stencil filled + /// with zeros. + pub const CLEAR_ONE_STENCIL_ZERO: Self = Self::Clear(vk::ClearDepthStencilValue { + depth: 1.0, + stencil: 0, + }); - #[allow(deprecated)] - self.set_attach_depth_stencil_as(depth_stencil_image, depth_stencil_image_view_info) - .set_resolve_depth_stencil_as( - attachment_idx, - depth_stencil_image, - depth_stencil_image_view_info, - depth_mode, - stencil_mode, - ); + /// A load operation which results in a depth and stencil attachment filled with zeros. + pub const CLEAR_ZERO_STENCIL_ZERO: Self = Self::Clear(vk::ClearDepthStencilValue { + depth: 0.0, + stencil: 0, + }); - self + /// Convenience constructor for clear depth and stencil values. + pub fn clear_depth_stencil(depth: f32, stencil: u32) -> Self { + Self::Clear(vk::ClearDepthStencilValue { depth, stencil }) } +} - /// See [multiview] - pub fn set_multiview(&mut self, view_mask: u32, correlated_view_mask: u32) -> &mut Self { - let cmd = self.cmd.cmd_mut(); - let exec = cmd.execs.last_mut().unwrap(); - - exec.correlated_view_mask = correlated_view_mask; - exec.view_mask = view_mask; - - self - } +/// TODO +#[derive(Clone, Copy, Debug)] +pub enum StoreOp { + /// TODO + DontCare, - /// See [render_area] - pub fn set_render_area(&mut self, area: vk::Rect2D) -> &mut Self { - self.cmd.cmd_mut().execs.last_mut().unwrap().render_area = Some(area); - self - } + /// TODO + Store, } #[allow(unused)] diff --git a/src/cmd/ray_trace.rs b/src/cmd/ray_trace.rs index e3a6e1fd..bef2f68e 100644 --- a/src/cmd/ray_trace.rs +++ b/src/cmd/ray_trace.rs @@ -2,11 +2,9 @@ use { super::{PipelineCommand, cmd_buf::CommandBuffer}, crate::driver::{device::Device, ray_trace::RayTracePipeline}, ash::vk, - log::trace, std::ops::Deref, }; -// NOTE: local implementation of type from super module impl PipelineCommand<'_, RayTracePipeline> { /// Begin recording a ray trace pipeline command buffer. pub fn record_cmd_buf( @@ -165,29 +163,12 @@ impl RayTraceCommandBuffer<'_> { /// [gpuinfo.org]: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPushConstantsSize&platform=all #[profiling::function] pub fn push_constants(&self, offset: u32, data: &[u8]) -> &Self { - for push_const in self.pipeline.inner.push_constants.iter() { - let push_const_end = push_const.offset + push_const.size; - let data_end = offset + data.len() as u32; - let end = data_end.min(push_const_end); - let start = offset.max(push_const.offset); - - if end > start { - trace!( - " push constants {:?} {}..{}", - push_const.stage_flags, start, end - ); - - unsafe { - self.cmd_buf.device.cmd_push_constants( - self.cmd_buf.handle, - self.pipeline.inner.layout, - push_const.stage_flags, - start, - &data[(start - offset) as usize..(end - offset) as usize], - ); - } - } - } + self.cmd_push_constants( + self.pipeline.inner.layout, + &self.pipeline.inner.push_constants, + offset, + data, + ); self } From b565606c37f348161c893055144a9e0cd245cb44 Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 19 Mar 2026 06:39:28 -0400 Subject: [PATCH 50/86] docs --- src/cmd/graphic.rs | 104 ++++++++++++++++++++++++---------------- src/cmd/mod.rs | 2 +- src/driver/image.rs | 26 ---------- src/driver/mod.rs | 2 +- src/driver/swapchain.rs | 35 +++++++++++++- src/lib.rs | 4 +- 6 files changed, 102 insertions(+), 71 deletions(-) diff --git a/src/cmd/graphic.rs b/src/cmd/graphic.rs index 96e85533..fd911f2e 100644 --- a/src/cmd/graphic.rs +++ b/src/cmd/graphic.rs @@ -18,7 +18,7 @@ impl PipelineCommand<'_, GraphicPipeline> { mut self, color_attachment_idx: AttachmentIndex, color_image: impl Into, - load: LoadOp, + load: LoadOp, store: StoreOp, ) -> Self { self.set_color_attachment_image(color_attachment_idx, color_image, load, store); @@ -31,7 +31,7 @@ impl PipelineCommand<'_, GraphicPipeline> { color_attachment_idx: AttachmentIndex, color_image: impl Into, color_image_view_info: impl Into, - load: LoadOp, + load: LoadOp, store: StoreOp, ) -> Self { self.set_color_attachment_image_view( @@ -208,7 +208,7 @@ impl PipelineCommand<'_, GraphicPipeline> { &mut self, color_attachment_idx: AttachmentIndex, color_image: impl Into, - load: LoadOp, + load: LoadOp, store: StoreOp, ) -> &mut Self { let color_image = color_image.into(); @@ -231,7 +231,7 @@ impl PipelineCommand<'_, GraphicPipeline> { color_attachment_idx: AttachmentIndex, color_image: impl Into, color_image_view_info: impl Into, - load: LoadOp, + load: LoadOp, store: StoreOp, ) -> &mut Self { let color_image = color_image.into(); @@ -441,30 +441,45 @@ impl PipelineCommand<'_, GraphicPipeline> { } } -/// TODO +/// Structure specifying a clear color value. #[derive(Clone, Copy, Debug)] -pub enum ClearColor { +pub enum ClearColorValue { /// Value as [f32]. + /// + /// Use this member for color clear values when the format of the image or attachment is one of + /// the numeric formats with a numeric type that is floating-point. Floating-point values are + /// automatically converted to the format of the image, with the clear value being treated as + /// linear if the image is sRGB. Float32([f32; 4]), /// Value as [i32]. + /// + /// Use this member for color clear values when the format of the image or attachment has a + /// numeric type that is signed integer. Signed integer values are converted to the format of + /// the image by casting to the smaller type (with negative 32-bit values mapping to negative + /// values in the smaller type). If the integer clear value is not representable in the target + /// type (e.g. would overflow in conversion to that type), the clear value is undefined. Int32([i32; 4]), /// Value as [u32]. + /// + /// Use this member for color clear values when the format of the image or attachment has a + /// numeric type that is unsigned integer. Unsigned integer values are converted to the format + /// of the image by casting to the integer type with fewer bits. Uint32([u32; 4]), } -impl ClearColor { - /// rgb zeros and alpha ones. +impl ClearColorValue { + /// RGB zeros and alpha ones. pub const BLACK_ALPHA_ONE: Self = Self::Float32([0.0, 0.0, 0.0, 1.0]); - /// zeros. + /// All zeros. pub const BLACK_ALPHA_ZERO: Self = Self::Float32([0.0, 0.0, 0.0, 0.0]); - /// rgb zeros and alpha ones. + /// RGB zeros and alpha ones. pub const WHITE_ALPHA_ONE: Self = Self::Float32([1.0, 1.0, 1.0, 1.0]); - /// rgb ones and alpha zeros. + /// RGB ones and alpha zeros. pub const WHITE_ALPHA_ZERO: Self = Self::Float32([1.0, 1.0, 1.0, 0.0]); /// Convenience constructor for clear color values. @@ -488,42 +503,42 @@ impl ClearColor { } } -impl Default for ClearColor { +impl Default for ClearColorValue { fn default() -> Self { Self::from_f32(0.0, 0.0, 0.0, 0.0) } } -impl From<[f32; 4]> for ClearColor { +impl From<[f32; 4]> for ClearColorValue { fn from(float32: [f32; 4]) -> Self { Self::Float32(float32) } } -impl From<[i32; 4]> for ClearColor { +impl From<[i32; 4]> for ClearColorValue { fn from(int32: [i32; 4]) -> Self { Self::Int32(int32) } } -impl From<[u8; 4]> for ClearColor { +impl From<[u8; 4]> for ClearColorValue { fn from(uint8: [u8; 4]) -> Self { Self::from_u8(uint8[0], uint8[1], uint8[2], uint8[3]) } } -impl From<[u32; 4]> for ClearColor { +impl From<[u32; 4]> for ClearColorValue { fn from(uint32: [u32; 4]) -> Self { Self::Uint32(uint32) } } -impl From for vk::ClearColorValue { - fn from(value: ClearColor) -> Self { +impl From for vk::ClearColorValue { + fn from(value: ClearColorValue) -> Self { match value { - ClearColor::Float32(float32) => Self { float32 }, - ClearColor::Int32(int32) => Self { int32 }, - ClearColor::Uint32(uint32) => Self { uint32 }, + ClearColorValue::Float32(float32) => Self { float32 }, + ClearColorValue::Int32(int32) => Self { int32 }, + ClearColorValue::Uint32(uint32) => Self { uint32 }, } } } @@ -1135,35 +1150,41 @@ impl<'a> Deref for GraphicCommandBuffer<'a> { } } -/// TODO +/// Specifies the state of a color or depth stencil image attachment during graphic framebuffer +/// load operations. +/// +/// Use this to specify the desired contents of any image before use in a pipeline command buffer. #[derive(Clone, Copy, Debug)] pub enum LoadOp { - /// TODO + /// Clears the attachment. + /// + /// `T` will be [ClearColorValue] for color images or [vk::ClearDepthStencilValue] for + /// depth/stencil images. Clear(T), - /// TODO + /// The attachment will become undefined and reads will produce garbage data. DontCare, - /// TODO + /// The attachment will be preserved in memory. Load, } -impl LoadOp { +impl LoadOp { /// A load operation which results in a color attachment filled with rgb zeros and alpha ones. - pub const CLEAR_BLACK_ALPHA_ONE: Self = Self::Clear(ClearColor::BLACK_ALPHA_ONE); + pub const CLEAR_BLACK_ALPHA_ONE: Self = Self::Clear(ClearColorValue::BLACK_ALPHA_ONE); /// A load operation which results in a color attachment filled with zeros. - pub const CLEAR_BLACK_ALPHA_ZERO: Self = Self::Clear(ClearColor::BLACK_ALPHA_ZERO); + pub const CLEAR_BLACK_ALPHA_ZERO: Self = Self::Clear(ClearColorValue::BLACK_ALPHA_ZERO); /// A load operation which results in a color attachment filled with rgb zeros and alpha ones. - pub const CLEAR_WHITE_ALPHA_ONE: Self = Self::Clear(ClearColor::WHITE_ALPHA_ONE); + pub const CLEAR_WHITE_ALPHA_ONE: Self = Self::Clear(ClearColorValue::WHITE_ALPHA_ONE); /// A load operation which results in a color attachment filled with rgb ones and alpha zeros. - pub const CLEAR_WHITE_ALPHA_ZERO: Self = Self::Clear(ClearColor::WHITE_ALPHA_ZERO); + pub const CLEAR_WHITE_ALPHA_ZERO: Self = Self::Clear(ClearColorValue::WHITE_ALPHA_ZERO); /// Convenience constructor for clear color values. pub fn clear_rgba(r: f32, g: f32, b: f32, a: f32) -> Self { - Self::Clear(ClearColor::rgba(r, g, b, a)) + Self::Clear(ClearColorValue::rgba(r, g, b, a)) } } @@ -1187,13 +1208,16 @@ impl LoadOp { } } -/// TODO +/// Specifies the state of a color or depth stencil image attachment after graphic framebuffer +/// store operations. +/// +/// Use this to specify the desired contents of any image after use in a pipeline command buffer. #[derive(Clone, Copy, Debug)] pub enum StoreOp { - /// TODO + /// The attachment will become undefined and reads will produce garbage data. DontCare, - /// TODO + /// The attachment will be preserved in memory. Store, } @@ -1203,8 +1227,8 @@ mod deprecated { crate::{ Attachment, Node, SubresourceAccess, cmd::{ - AttachmentIndex, ClearColor, Descriptor, PipelineCommand, SubresourceRange, View, - ViewInfo, graphic::GraphicCommandBuffer, + AttachmentIndex, ClearColorValue, Descriptor, PipelineCommand, SubresourceRange, + View, ViewInfo, graphic::GraphicCommandBuffer, }, driver::{ graphic::GraphicPipeline, @@ -1299,7 +1323,7 @@ mod deprecated { self, attachment_idx: AttachmentIndex, image: impl Into, - color: impl Into, + color: impl Into, ) -> Self { let image = image.into(); let image_info = self.resource(image).info; @@ -1315,7 +1339,7 @@ mod deprecated { mut self, attachment_idx: AttachmentIndex, image: impl Into, - color: impl Into, + color: impl Into, image_view_info: impl Into, ) -> Self { #[allow(deprecated)] @@ -1788,7 +1812,7 @@ mod deprecated { &mut self, attachment_idx: AttachmentIndex, image: impl Into, - color: impl Into, + color: impl Into, ) -> &mut Self { let image = image.into(); let image_info = self.resource(image).info; @@ -1804,7 +1828,7 @@ mod deprecated { &mut self, attachment_idx: AttachmentIndex, image: impl Into, - color: impl Into, + color: impl Into, image_view_info: impl Into, ) -> &mut Self { let image = image.into(); diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index 8650fee0..c53ce230 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -12,7 +12,7 @@ pub use self::{ UpdateAccelerationStructureIndirectInfo, UpdateAccelerationStructureInfo, }, compute::ComputeCommandBuffer, - graphic::{ClearColor, GraphicCommandBuffer, LoadOp, StoreOp}, + graphic::{ClearColorValue, GraphicCommandBuffer, LoadOp, StoreOp}, pipeline::{Pipeline, PipelineCommand}, ray_trace::RayTraceCommandBuffer, }; diff --git a/src/driver/image.rs b/src/driver/image.rs index 4642b146..f2dfeaa2 100644 --- a/src/driver/image.rs +++ b/src/driver/image.rs @@ -65,32 +65,6 @@ pub(crate) fn image_subresource_range_intersects( /// Smart pointer handle to an [image] object. /// /// Also contains information about the object. -/// -/// ## `Deref` behavior -/// -/// `Image` automatically dereferences to [`vk::Image`] (via the [`Deref`] trait), so you can -/// call `vk::Image`'s methods on a value of type `Image`. To avoid name clashes with `vk::Image`'s -/// methods, the methods of `Image` itself are associated functions, called using -/// [fully qualified syntax]: -/// -/// ```no_run -/// # use std::sync::Arc; -/// # use ash::vk; -/// # use vk_graph::driver::{AccessType, DriverError}; -/// # use vk_graph::driver::device::{Device, DeviceInfo}; -/// # use vk_graph::driver::image::{Image, ImageInfo}; -/// # fn main() -> Result<(), DriverError> { -/// # let device = Device::new(DeviceInfo::default())?; -/// # let info = ImageInfo::image_1d(1, vk::Format::R8_UINT, vk::ImageUsageFlags::STORAGE); -/// # let my_image = Image::create(&device, info)?; -/// # let my_subresource_range = vk::ImageSubresourceRange::default(); -/// let prev = Image::access(&my_image, AccessType::AnyShaderWrite, my_subresource_range); -/// # Ok(()) } -/// ``` -/// -/// [image]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImage.html -/// [deref]: core::ops::Deref -/// [fully qualified syntax]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name #[readonly::make] pub struct Image { accesses: Mutex>, diff --git a/src/driver/mod.rs b/src/driver/mod.rs index 59469d3e..bfaefa8e 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -49,8 +49,8 @@ pub use { vk_sync::{self as sync}, }; -/// Specifying depth and stencil resolve modes. #[deprecated = "Use driver::render_pass::ResolveMode instead"] +#[doc(hidden)] pub type ResolveMode = self::render_pass::ResolveMode; pub(crate) use self::{ diff --git a/src/driver/swapchain.rs b/src/driver/swapchain.rs index 0144de58..4d77e59e 100644 --- a/src/driver/swapchain.rs +++ b/src/driver/swapchain.rs @@ -454,6 +454,18 @@ pub enum SwapchainError { #[derive(Debug)] #[repr(C)] pub struct SwapchainImage { + /// The device which owns this image resource. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub device: Device, + + /// The native Vulkan resource handle of this image. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub handle: vk::Image, + image: Image, /// The index of this swapchain among the other swapchain images. @@ -464,6 +476,18 @@ pub struct SwapchainImage { #[cfg(not(doc))] index: u32, + + /// Information used to create this resource. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub info: ImageInfo, + + /// A name for debugging purposes. + /// + /// _Note:_ This field is read-only. + #[cfg(doc)] + pub name: Option, } impl Clone for SwapchainImage { @@ -477,7 +501,7 @@ impl Clone for SwapchainImage { } } -#[doc(hidden)] +#[cfg(not(doc))] impl Deref for SwapchainImage { type Target = ReadOnlySwapchainImage; @@ -486,6 +510,15 @@ impl Deref for SwapchainImage { } } +#[cfg(doc)] +impl Deref for SwapchainImage { + type Target = Image; + + fn deref(&self) -> &Self::Target { + unreachable!() + } +} + /// Information used to create a [`Swapchain`] instance. #[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)] #[builder( diff --git a/src/lib.rs b/src/lib.rs index 7ad680ee..d5fbd93f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -360,7 +360,7 @@ mod queue; use std::sync::Arc; use crate::{ - cmd::{ClearColor, CommandBuffer}, + cmd::{ClearColorValue, CommandBuffer}, driver::{ accel_struct::AccelerationStructure, buffer::Buffer, image::Image, swapchain::SwapchainImage, @@ -778,7 +778,7 @@ impl Graph { pub fn clear_color_image( &mut self, image: impl Into, - color: impl Into, + color: impl Into, ) -> &mut Self { let color = color.into().into(); let image = image.into(); From 4dc79c945ec524aadc0c952b7870bc8214fe89d5 Mon Sep 17 00:00:00 2001 From: John Wells Date: Thu, 19 Mar 2026 12:54:41 -0400 Subject: [PATCH 51/86] docs --- src/cmd/cmd_buf.rs | 47 ++--- src/cmd/compute.rs | 8 +- src/cmd/graphic.rs | 28 +-- src/cmd/mod.rs | 20 +- src/cmd/pipeline.rs | 2 +- src/cmd/ray_trace.rs | 8 +- src/driver/accel_struct.rs | 23 +-- src/driver/buffer.rs | 20 +- src/driver/compute.rs | 10 +- src/driver/graphic.rs | 2 +- src/driver/image.rs | 19 ++ src/driver/mod.rs | 5 +- src/driver/physical_device.rs | 18 +- src/driver/ray_trace.rs | 13 +- src/driver/surface.rs | 3 +- src/lib.rs | 349 +--------------------------------- src/node.rs | 2 +- src/pool/mod.rs | 2 +- src/queue.rs | 4 +- 19 files changed, 115 insertions(+), 468 deletions(-) diff --git a/src/cmd/cmd_buf.rs b/src/cmd/cmd_buf.rs index 8fe9f6a5..cd493b03 100644 --- a/src/cmd/cmd_buf.rs +++ b/src/cmd/cmd_buf.rs @@ -17,33 +17,24 @@ use { #[cfg(debug_assertions)] use crate::Execution; -/// Recording interface for acceleration structure commands. +/// Recording interface for general Vulkan commands. /// /// This structure provides a strongly-typed set of methods which allow acceleration structures to -/// be built and updated. An instance of `Acceleration` is provided to the closure parameter of -/// [`PassRef::record_accel_struct`]. +/// be built and updated. /// /// # Examples /// /// Basic usage: /// /// ```no_run -/// # use std::sync::Arc; -/// # use ash::vk; -/// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; -/// # use vk_graph::driver::DriverError; -/// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::Graph; -/// # use vk_graph::driver::shader::Shader; -/// # fn main() -> Result<(), DriverError> { -/// # let device = Device::new(DeviceInfo::default())?; +/// # fn main() { /// # let mut my_graph = Graph::default(); -/// # let info = AccelerationStructureInfo::blas(1); /// my_graph.begin_cmd() /// .record_cmd_buf(move |cmd_buf| { -/// // During this closure we have access to the build and update methods +/// // Use provided command buffer functions or native calls /// }); -/// # Ok(()) } +/// # } /// ``` #[derive(Clone, Copy)] pub struct CommandBuffer<'a> { @@ -78,8 +69,8 @@ impl<'a> CommandBuffer<'a> { /// /// - Flags must include [`vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS`] /// - Size must be equal to or greater than the `build_size` value returned by - /// [`AccelerationStructure::size_of`] aligned to `min_accel_struct_scratch_offset_alignment` - /// of + /// [`AccelerationStructure::size_of`](crate::driver::accel_struct::AccelerationStructure::size_of) + /// aligned to `min_accel_struct_scratch_offset_alignment` of /// [`PhysicalDevice::accel_struct_properties`](crate::driver::physical_device::PhysicalDevice::accel_struct_properties). /// /// TODO: Link to somewhere else for a full example of the scratch buffer steps @@ -225,7 +216,7 @@ impl<'a> CommandBuffer<'a> { /// structure builds. /// /// `range` is a buffer device address which points to `info.geometry.len()` - /// [vk::VkAccelerationStructureBuildRangeInfoKHR] structures defining dynamic offsets to the + /// [vk::AccelerationStructureBuildRangeInfoKHR] structures defining dynamic offsets to the /// addresses where geometry data is stored, as defined by `info`. pub fn build_accel_struct_indirect( &self, @@ -345,8 +336,8 @@ impl<'a> CommandBuffer<'a> { /// /// - Flags must include [`vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS`] /// - Size must be equal to or greater than the `update_size` value returned by - /// [`AccelerationStructure::size_of`] aligned to `min_accel_struct_scratch_offset_alignment` - /// of + /// [`AccelerationStructure::size_of`](crate::driver::accel_struct::AccelerationStructure::size_of) + /// aligned to `min_accel_struct_scratch_offset_alignment` of /// [`PhysicalDevice::accel_struct_properties`](crate::driver::physical_device::PhysicalDevice::accel_struct_properties). pub fn update_accel_struct(&self, infos: &[UpdateAccelerationStructureInfo]) -> &Self { #[derive(Default)] @@ -424,7 +415,7 @@ impl<'a> CommandBuffer<'a> { /// structure updates. /// /// `range` is a buffer device address which points to `info.geometry.len()` - /// [vk::VkAccelerationStructureBuildRangeInfoKHR] structures defining dynamic offsets to the + /// [vk::AccelerationStructureBuildRangeInfoKHR] structures defining dynamic offsets to the /// addresses where geometry data is stored, as defined by `info`. pub fn update_accel_struct_indirect( &self, @@ -550,7 +541,7 @@ pub struct BuildAccelerationStructureInfo { )>, /// The temporary buffer or host address (with enough capacity per - /// [AccelerationStructure::size_of]). + /// [`AccelerationStructure::size_of`](crate::driver::accel_struct::AccelerationStructure::size_of)). pub scratch_addr: DeviceOrHostAddress, } @@ -590,15 +581,15 @@ pub struct BuildAccelerationStructureIndirectInfo { pub build_data: AccelerationStructureGeometryInfo, /// A buffer device addresses which points to `data.geometry.len()` - /// [vk::VkAccelerationStructureBuildRangeInfoKHR] structures defining dynamic offsets to the + /// [vk::AccelerationStructureBuildRangeInfoKHR] structures defining dynamic offsets to the /// addresses where geometry data is stored. pub range_base: vk::DeviceAddress, - /// Byte stride between elements of [range]. + /// Byte stride between elements of [`Self::range_base`]. pub range_stride: u32, /// The temporary buffer or host address (with enough capacity per - /// [AccelerationStructure::size_of]). + /// [`AccelerationStructure::size_of`](crate::driver::accel_struct::AccelerationStructure::size_of)). pub scratch_data: DeviceOrHostAddress, } @@ -636,15 +627,15 @@ pub struct UpdateAccelerationStructureIndirectInfo { pub dst_accel_struct: AnyAccelerationStructureNode, /// A buffer device addresses which points to `data.geometry.len()` - /// [vk::VkAccelerationStructureBuildRangeInfoKHR] structures defining dynamic offsets to the + /// [vk::AccelerationStructureBuildRangeInfoKHR] structures defining dynamic offsets to the /// addresses where geometry data is stored. pub range_base: vk::DeviceAddress, - /// Byte stride between elements of [range]. + /// Byte stride between elements of [`Self::range_base`]. pub range_stride: u32, /// The temporary buffer or host address (with enough capacity per - /// [AccelerationStructure::size_of]). + /// [`AccelerationStructure::size_of`](crate::driver::accel_struct::AccelerationStructure::size_of)). pub scratch_addr: DeviceOrHostAddress, /// The source acceleration structure to be read. @@ -690,7 +681,7 @@ pub struct UpdateAccelerationStructureInfo { pub dst_accel_struct: AnyAccelerationStructureNode, /// The temporary buffer or host address (with enough capacity per - /// [AccelerationStructure::size_of]). + /// [`AccelerationStructure::size_of`](crate::driver::accel_struct::AccelerationStructure::size_of)). pub scratch_addr: DeviceOrHostAddress, /// The source acceleration structure to be read. diff --git a/src/cmd/compute.rs b/src/cmd/compute.rs index 8a6245a1..b224bb91 100644 --- a/src/cmd/compute.rs +++ b/src/cmd/compute.rs @@ -41,8 +41,8 @@ impl PipelineCommand<'_, ComputePipeline> { /// Recording interface for computing commands. /// /// This structure provides a strongly-typed set of methods which allow compute shader code to be -/// executed. An instance of `Compute` is provided to the closure parameter of -/// [`PipelineCommand::record_pipeline`] which may be accessed by binding a [`ComputePipeline`] to a +/// executed. An instance is provided to the closure parameter of +/// [`PipelineCommand::record_cmd_buf`] which may be accessed by binding a [`ComputePipeline`] to a /// render pass. /// /// # Examples @@ -150,7 +150,7 @@ impl ComputeCommandBuffer<'_> { /// WorkgroupId values ranging from `[base_group*, base_group* + group_count*)` in each /// component. /// - /// [`Compute::dispatch`] is equivalent to + /// [`Self::dispatch`] is equivalent to /// `dispatch_base(0, 0, 0, group_count_x, group_count_y, group_count_z)`. /// /// [Dispatch]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCmdDispatchBase.html @@ -181,7 +181,7 @@ impl ComputeCommandBuffer<'_> { /// Dispatch compute work items with indirect parameters. /// - /// `dispatch_indirect` behaves similarly to [`Compute::dispatch`] except that the parameters + /// `dispatch_indirect` behaves similarly to [`Self::dispatch`] except that the parameters /// are read by the device from `args_buf` during execution. The parameters of the dispatch are /// encoded in a [`vk::DispatchIndirectCommand`] structure taken from `args_buf` starting at /// `args_offset`. diff --git a/src/cmd/graphic.rs b/src/cmd/graphic.rs index fd911f2e..aee4966d 100644 --- a/src/cmd/graphic.rs +++ b/src/cmd/graphic.rs @@ -203,7 +203,7 @@ impl PipelineCommand<'_, GraphicPipeline> { self } - /// See [color_attachment_image] + /// See [Self::color_attachment_image] pub fn set_color_attachment_image( &mut self, color_attachment_idx: AttachmentIndex, @@ -225,7 +225,7 @@ impl PipelineCommand<'_, GraphicPipeline> { self } - /// See [color_attachment_image_view] + /// See [Self::color_attachment_image_view] pub fn set_color_attachment_image_view( &mut self, color_attachment_idx: AttachmentIndex, @@ -264,7 +264,7 @@ impl PipelineCommand<'_, GraphicPipeline> { self } - /// See [color_attachment_resolve_image] + /// See [Self::color_attachment_resolve_image] pub fn set_color_attachment_resolve_image( &mut self, msaa_attachment_idx: AttachmentIndex, @@ -284,7 +284,7 @@ impl PipelineCommand<'_, GraphicPipeline> { self } - /// See [color_attachment_resolve_image_view] + /// See [Self::color_attachment_resolve_image_view] pub fn set_color_attachment_resolve_image_view( &mut self, msaa_attachment_idx: AttachmentIndex, @@ -307,7 +307,7 @@ impl PipelineCommand<'_, GraphicPipeline> { self } - /// See [depth_stencil] + /// See [Self::depth_stencil] pub fn set_depth_stencil(&mut self, depth_stencil: impl Into) -> &mut Self { let depth_stencil = depth_stencil.into(); let cmd = self.cmd.cmd_mut(); @@ -320,7 +320,7 @@ impl PipelineCommand<'_, GraphicPipeline> { self } - /// See [depth_stencil_attachment_image] + /// See [Self::depth_stencil_attachment_image] pub fn set_depth_stencil_attachment_image( &mut self, depth_stencil_image: impl Into, @@ -340,7 +340,7 @@ impl PipelineCommand<'_, GraphicPipeline> { self } - /// See [depth_stencil_attachment_image_view] + /// See [Self::depth_stencil_attachment_image_view] pub fn set_depth_stencil_attachment_image_view( &mut self, depth_stencil_image: impl Into, @@ -376,7 +376,7 @@ impl PipelineCommand<'_, GraphicPipeline> { self } - /// See [depth_stencil_attachment_resolve_image] + /// See [Self::depth_stencil_attachment_resolve_image] pub fn set_depth_stencil_attachment_resolve_image( &mut self, attachment_idx: AttachmentIndex, @@ -398,7 +398,7 @@ impl PipelineCommand<'_, GraphicPipeline> { self } - /// See [depth_stencil_attachment_resolve_image_view] + /// See [Self::depth_stencil_attachment_resolve_image_view] pub fn set_depth_stencil_attachment_resolve_image_view( &mut self, attachment_idx: AttachmentIndex, @@ -423,7 +423,7 @@ impl PipelineCommand<'_, GraphicPipeline> { self } - /// See [multiview] + /// See [Self::multiview] pub fn set_multiview(&mut self, view_mask: u32, correlated_view_mask: u32) -> &mut Self { let cmd = self.cmd.cmd_mut(); let exec = cmd.execs.last_mut().unwrap(); @@ -434,7 +434,7 @@ impl PipelineCommand<'_, GraphicPipeline> { self } - /// See [render_area] + /// See [Self::render_area] pub fn set_render_area(&mut self, area: vk::Rect2D) -> &mut Self { self.cmd.cmd_mut().execs.last_mut().unwrap().render_area = Some(area); self @@ -547,7 +547,7 @@ impl From for vk::ClearColorValue { /// /// This structure provides a strongly-typed set of methods which allow rasterization shader code to /// be executed. An instance of `Draw` is provided to the closure parameter of -/// [`PipelineCommand::record_pipeline`] which may be accessed by binding a [`GraphicPipeline`] to a +/// [`PipelineCommand::record_cmd_buf`] which may be accessed by binding a [`GraphicPipeline`] to a /// render pass. /// /// # Examples @@ -977,7 +977,7 @@ impl GraphicCommandBuffer<'_> { /// Draw primitives with indirect parameters and unindexed vertices. /// - /// Behaves otherwise similar to [`Draw::draw_indexed_indirect`]. + /// Behaves otherwise similar to [`Self::draw_indexed_indirect`]. #[profiling::function] pub fn draw_indirect( &self, @@ -1004,7 +1004,7 @@ impl GraphicCommandBuffer<'_> { /// Draw primitives with indirect parameters, unindexed vertices, and draw count. /// - /// Behaves otherwise similar to [`Draw::draw_indexed_indirect_count`]. + /// Behaves otherwise similar to [`Self::draw_indexed_indirect_count`]. #[profiling::function] pub fn draw_indirect_count( &self, diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index c53ce230..797448dc 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -1,4 +1,4 @@ -//! Strongly-typed rendering commands. +//! Strongly-typed [`Graph`] commands. mod cmd_buf; mod compute; @@ -79,7 +79,7 @@ impl<'a> Command<'a> { /// Binds a Vulkan buffer, image, or acceleration structure resource to the graph associated /// with this command. /// - /// Bound nodes may be used in passes for pipeline and shader operations. + /// Bound nodes may be used in commands for pipeline and shader operations. pub fn bind_resource(&mut self, resource: R) -> R::Node where R: Resource, @@ -87,8 +87,14 @@ impl<'a> Command<'a> { self.graph.bind_resource(resource) } - /// Binds a [`ComputePipeline`], [`GraphicPipeline`], or [`RayTracePipeline`] to the current - /// pass, allowing for strongly typed access to the related functions. + /// Binds a shader pipeline to the current command, allowing for strongly typed access to the + /// related functions. + /// + /// `P`|`P::Command` + /// -|- + /// [`ComputePipeline`](crate::driver::compute::ComputePipeline)|[`PipelineCommand<'_, ComputePipeline>`] + /// [`GraphicPipeline`](crate::driver::graphic::GraphicPipeline)|[`PipelineCommand<'_, GraphicPipeline>`] + /// [`RayTracePipeline`](crate::driver::ray_trace::RayTracePipeline)|[`PipelineCommand<'_, RayTracePipeline>`] pub fn bind_pipeline

(self, pipeline: P) -> P::Command where P: Pipeline<'a>, @@ -155,7 +161,8 @@ impl<'a> Command<'a> { /// Begin recording an acceleration structure command buffer. /// - /// This is the entry point for building and updating an [`AccelerationStructure`] instance. + /// This is the entry point for building and updating an + /// [`AccelerationStructure`](crate::driver::accel_struct::AccelerationStructure) instance. /// /// The provided closure allows you to run any Vulkan code, or interoperate with other Vulkan /// code and interfaces. @@ -166,7 +173,8 @@ impl<'a> Command<'a> { /// Begin recording an acceleration structure command buffer. /// - /// This is the entry point for building and updating an [`AccelerationStructure`] instance. + /// This is the entry point for building and updating an + /// [`AccelerationStructure`](crate::driver::accel_struct::AccelerationStructure) instance. /// /// The provided closure allows you to run any Vulkan code, or interoperate with other Vulkan /// code and interfaces. diff --git a/src/cmd/pipeline.rs b/src/cmd/pipeline.rs index e62b58b6..a8ca97af 100644 --- a/src/cmd/pipeline.rs +++ b/src/cmd/pipeline.rs @@ -11,7 +11,7 @@ use { /// A trait for pipelines which may be bound to a `Command`. /// -/// See [`Command::bind_pipeline`](super::cmd::Command::bind_pipeline) for details. +/// See [`Command::bind_pipeline`](crate::cmd::Command::bind_pipeline) for details. pub trait Pipeline<'a> { /// The resource reference type. type Command; diff --git a/src/cmd/ray_trace.rs b/src/cmd/ray_trace.rs index bef2f68e..a8dd06c1 100644 --- a/src/cmd/ray_trace.rs +++ b/src/cmd/ray_trace.rs @@ -51,9 +51,9 @@ impl PipelineCommand<'_, RayTracePipeline> { /// Recording interface for ray tracing commands. /// /// This structure provides a strongly-typed set of methods which allow ray trace shader code to be -/// executed. An instance of `RayTrace` is provided to the closure parameter of -/// [`PipelineCommand::record_pipeline`] which may be accessed by binding a [`RayTracePipeline`] to -/// a render pass. +/// executed. An instance of is provided to the closure parameter of +/// [`PipelineCommand::record_cmd_buf`] which may be accessed by binding a [`RayTracePipeline`] to +/// a command. /// /// # Examples /// @@ -275,7 +275,7 @@ impl RayTraceCommandBuffer<'_> { /// /// See [`vkCmdTraceRaysIndirectKHR`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCmdTraceRaysIndirectKHR.html). /// - /// [buffer device address]: Buffer::device_address + /// [buffer device address]: crate::driver::buffer::Buffer::device_address #[profiling::function] pub fn trace_rays_indirect( &self, diff --git a/src/driver/accel_struct.rs b/src/driver/accel_struct.rs index 300be954..1149eac7 100644 --- a/src/driver/accel_struct.rs +++ b/src/driver/accel_struct.rs @@ -23,32 +23,23 @@ use std::sync::Mutex; /// /// Also contains the backing buffer and information about the object. /// -/// ## `Deref` behavior -/// -/// `AccelerationStructure` automatically dereferences to [`vk::AccelerationStructureKHR`] (via the -/// [`Deref`] trait), so you can call `vk::AccelerationStructureKHR`'s methods on a value of -/// type `AccelerationStructure`. To avoid name clashes with `vk::AccelerationStructureKHR`'s -/// methods, the methods of `AccelerationStructure` itself are associated functions, called using -/// [fully qualified syntax]: -/// /// ```no_run -/// # use std::sync::Arc; /// # use ash::vk; -/// # use vk_graph::driver::{AccessType, DriverError}; +/// # use vk_graph::driver::DriverError; /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::accel_struct::{AccelerationStructure, AccelerationStructureInfo}; /// # fn main() -> Result<(), DriverError> { /// # let device = Device::new(DeviceInfo::default())?; -/// # const SIZE: vk::DeviceSize = 1024; -/// # let info = AccelerationStructureInfo::blas(SIZE); -/// # let my_accel_struct = AccelerationStructure::create(&device, info)?; -/// let addr = AccelerationStructure::device_address(&my_accel_struct); +/// let info = AccelerationStructureInfo::blas(0); +/// let accel_struct = AccelerationStructure::create(&device, info)?; +/// let addr = accel_struct.device_address(); +/// +/// assert_eq!(accel_struct.info, info); +/// assert_ne!(accel_struct.handle, vk::AccelerationStructureKHR::null()); /// # Ok(()) } /// ``` /// /// [acceleration structure]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkAccelerationStructureKHR.html -/// [deref]: core::ops::Deref -/// [fully qualified syntax]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name #[derive(Debug)] #[readonly::make] pub struct AccelerationStructure { diff --git a/src/driver/buffer.rs b/src/driver/buffer.rs index 54158708..a7741bfa 100644 --- a/src/driver/buffer.rs +++ b/src/driver/buffer.rs @@ -29,30 +29,22 @@ use std::sync::Mutex; /// /// Also contains information about the object. /// -/// ## `Deref` behavior -/// -/// `Buffer` automatically dereferences to [`vk::Buffer`] (via the [`Deref`] trait), so you -/// can call `vk::Buffer`'s methods on a value of type `Buffer`. To avoid name clashes with -/// `vk::Buffer`'s methods, the methods of `Buffer` itself are associated functions, called using -/// [fully qualified syntax]: -/// /// ```no_run -/// # use std::sync::Arc; /// # use ash::vk; -/// # use vk_graph::driver::{AccessType, DriverError}; +/// # use vk_graph::driver::DriverError; /// # use vk_graph::driver::device::{Device, DeviceInfo}; /// # use vk_graph::driver::buffer::{Buffer, BufferInfo}; /// # fn main() -> Result<(), DriverError> { /// # let device = Device::new(DeviceInfo::default())?; -/// # let info = BufferInfo::device_mem(8, vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS); -/// # let my_buf = Buffer::create(&device, info)?; -/// let addr = my_buf.device_address(); +/// let info = BufferInfo::device_mem(1_024, vk::BufferUsageFlags::STORAGE_BUFFER); +/// let my_buf = Buffer::create(&device, info)?; +/// +/// assert_eq!(my_buf.info, info); +/// assert_ne!(my_buf.handle, vk::Buffer::null()); /// # Ok(()) } /// ``` /// /// [buffer]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBuffer.html -/// [deref]: core::ops::Deref -/// [fully qualified syntax]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name #[readonly::make] pub struct Buffer { accesses: Mutex, diff --git a/src/driver/compute.rs b/src/driver/compute.rs index 298233e5..af712400 100644 --- a/src/driver/compute.rs +++ b/src/driver/compute.rs @@ -18,17 +18,9 @@ use { }, }; -/// Smart pointer handle to a [pipeline] object. +/// Smart pointer handle of a pipeline object. /// /// Also contains information about the object. -/// -/// ## `Deref` behavior -/// -/// `ComputePipeline` automatically dereferences to [`vk::Pipeline`] (via the [`Deref`] -/// trait), so you can call `vk::Pipeline`'s methods on a value of type `ComputePipeline`. -/// -/// [pipeline]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipeline.html -/// [deref]: core::ops::Deref #[derive(Clone, Debug)] pub struct ComputePipeline { pub(crate) inner: Arc, diff --git a/src/driver/graphic.rs b/src/driver/graphic.rs index d6e2f1d8..9a309af1 100644 --- a/src/driver/graphic.rs +++ b/src/driver/graphic.rs @@ -317,7 +317,7 @@ impl DepthStencilInfoBuilder { } } -/// Opaque representation of a [pipeline] object. +/// Opaque representation of a pipeline object. /// /// Also contains information about the object. /// diff --git a/src/driver/image.rs b/src/driver/image.rs index f2dfeaa2..fa466f31 100644 --- a/src/driver/image.rs +++ b/src/driver/image.rs @@ -65,6 +65,25 @@ pub(crate) fn image_subresource_range_intersects( /// Smart pointer handle to an [image] object. /// /// Also contains information about the object. +/// +/// ```no_run +/// # use ash::vk; +/// # use vk_graph::driver::{AccessType, DriverError}; +/// # use vk_graph::driver::device::{Device, DeviceInfo}; +/// # use vk_graph::driver::image::{Image, ImageInfo}; +/// # fn main() -> Result<(), DriverError> { +/// # let device = Device::new(DeviceInfo::default())?; +/// let fmt = vk::Format::R8G8B8A8_UNORM; +/// let usage = vk::ImageUsageFlags::SAMPLED; +/// let info = ImageInfo::image_2d(320, 200, fmt, usage); +/// let my_img = Image::create(&device, info)?; +/// +/// assert_eq!(my_img.info, info); +/// assert_ne!(my_img.handle, vk::Image::null()); +/// # Ok(()) } +/// ``` +/// +/// [image]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBuffer.html #[readonly::make] pub struct Image { accesses: Mutex>, diff --git a/src/driver/mod.rs b/src/driver/mod.rs index bfaefa8e..1c7459ba 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -1,5 +1,4 @@ -//! [Vulkan](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/index.html) interface -//! based on smart pointers. +//! Vulkan interface based on smart pointers. //! //! # Resources //! @@ -23,7 +22,7 @@ //! The following pipelines are available: //! //! - [`ComputePipeline`](compute::ComputePipeline) -//! - [`GraphicPipeline`] +//! - [`GraphicPipeline`](graphic::GraphicPipeline) //! - [`RayTracePipeline`](ray_trace::RayTracePipeline) pub mod accel_struct; diff --git a/src/driver/physical_device.rs b/src/driver/physical_device.rs index 77c335b6..ac2f3238 100644 --- a/src/driver/physical_device.rs +++ b/src/driver/physical_device.rs @@ -1297,43 +1297,43 @@ impl From for Vulkan10Features { #[derive(Clone, Copy, Debug)] pub struct Vulkan10Limits { /// The largest dimension (width) that is guaranteed to be supported for all images created with - /// an image type of [`ImageType::Texture1D`](super::image::ImageType). + /// an image type of [`vk::ImageType::TYPE_1D`]. /// /// Some combinations of image parameters (format, usage, etc.) may allow support for larger /// dimensions, which can be queried using - /// [`Device::image_format_properties`](super::device::Device::image_format_properties). + /// [`PhysicalDevice::image_format_properties`]. pub max_image_dimension1_d: u32, /// The largest dimension (width or height) that is guaranteed to be supported for all images - /// created with an image type of [`ImageType::Texture2D`](super::image::ImageType) and without + /// created with an image type of [`vk::ImageType::TYPE_2D`] and without /// [`vk::ImageCreateFlags::CUBE_COMPATIBLE`] set in /// [`ImageInfo::flags`](super::image::ImageInfo::flags). /// /// Some combinations of image parameters (format, usage, etc.) may allow support for larger /// dimensions, which can be queried using - /// [`Device::image_format_properties`](super::device::Device::image_format_properties). + /// [`PhysicalDevice::image_format_properties`]. pub max_image_dimension2_d: u32, /// The largest dimension (width, height, or depth) that is guaranteed to be supported for all - /// images created with an image type of [`ImageType::Texture3D`](super::image::ImageType). + /// images created with an image type of [`vk::ImageType::TYPE_3D`]. /// /// Some combinations of image parameters (format, usage, etc.) may allow support for larger /// dimensions, which can be queried using - /// [`Device::image_format_properties`](super::device::Device::image_format_properties). + /// [`PhysicalDevice::image_format_properties`]. pub max_image_dimension3_d: u32, /// The largest dimension (width or height) that is guaranteed to be supported for all images - /// created with an image type of [`ImageType::Texture2D`](super::image::ImageType) and with + /// created with an image type of [`vk::ImageType::TYPE_2D`] and with /// [`vk::ImageCreateFlags::CUBE_COMPATIBLE`] set in /// [`ImageInfo::flags`](super::image::ImageInfo::flags). /// /// Some combinations of image parameters (format, usage, etc.) may allow support for larger /// dimensions, which can be queried using - /// [`Device::image_format_properties`](super::device::Device::image_format_properties). + /// [`PhysicalDevice::image_format_properties`]. pub max_image_dimension_cube: u32, /// The maximum number of layers - /// ([`ImageInfo::array_elements`](super::image::ImageInfo::array_elements)) for an image. + /// ([`ImageInfo::array_layer_count`](super::image::ImageInfo::array_layer_count)) for an image. pub max_image_array_layers: u32, /// The maximum number of addressable texels for a buffer view created on a buffer which was diff --git a/src/driver/ray_trace.rs b/src/driver/ray_trace.rs index 58030bef..9b7e1362 100644 --- a/src/driver/ray_trace.rs +++ b/src/driver/ray_trace.rs @@ -19,20 +19,9 @@ use { }, }; -/// Smart pointer handle to a [pipeline] object. +/// Smart pointer handle of a pipeline object. /// /// Also contains information about the object. -/// -/// ## `Deref` behavior -/// -/// `RayTracePipeline` automatically dereferences to [`vk::Pipeline`] (via the [`Deref`] -/// trait), so you can call `vk::Pipeline`'s methods on a value of type `RayTracePipeline`. To avoid -/// name clashes with `vk::Pipeline`'s methods, the methods of `RayTracePipeline` itself are -/// associated functions, called using [fully qualified syntax]: -/// -/// [pipeline]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipeline.html -/// [deref]: core::ops::Deref -/// [fully qualified syntax]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name #[derive(Clone, Debug)] #[readonly::make] pub struct RayTracePipeline { diff --git a/src/driver/surface.rs b/src/driver/surface.rs index c33a1256..0e930d1a 100644 --- a/src/driver/surface.rs +++ b/src/driver/surface.rs @@ -43,8 +43,7 @@ impl Surface { /// Create a surface from a raw window display handle. /// - /// `device` must have been created with platform specific surface extensions enabled, acquired - /// through [`Device::create_display_window`]. + /// `device` must have been created with platform specific surface extensions enabled. #[profiling::function] pub fn create( device: &Device, diff --git a/src/lib.rs b/src/lib.rs index d5fbd93f..471b8dab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,348 +3,11 @@ This crate provides a high-performance [Vulkan](https://www.vulkan.org/) driver featuring automated resource management and execution. -For a general overview, including installation and typical usage, see the +For an overview, including installation and typical usage, see the [Guide Book](https://attackgoat.github.io/vk-graph). -# Getting Sarted -Typical usage begins by displaying a winit [`Window`()]. The provided example code displays a window -and creates Vulkan [`Device`] driver automatically: -```no_run -use vk_graph_window::{Window, WindowError}; - -fn main() -> Result<(), WindowError> { - let window = Window::new()?; - - // Use the device to create resources and pipelines before running - let device = &window.device; - - window.run(|frame| { - // You may also create resources and pipelines while running - let device = &frame.device; - }) -} -``` - -## _Optional_: Headless Rendering - -```no_run -use vk_graph::driver::{device::{Device, DeviceInfo}, DriverError}; - -fn main() -> Result<(), DriverError> { - let device = Device::new(DeviceInfo::default())?; - - // Do stuff... - # Ok(()) -} -``` - -# Resources and Pipelines - -All resources and pipelines, as well as the driver itself, use shared reference tracking to keep -pointers alive. _vk-graph_ uses `std::sync::Arc` to track references. - -## Information - -All [`driver`] types have associated information structures which describe their properties. -Each object provides a `create` function which uses the information to return an instance. - -| Resource | Create Using | -|-------------------------------|-----------------------------------------------------| -| [`AccelerationStructureInfo`] | [`AccelerationStructure::create`] | -| [`BufferInfo`] | [`Buffer::create`] or [`Buffer::create_from_slice`] | -| [`ImageInfo`] | [`Image::create`] | - -For example, a typical host-mappable buffer: - -```no_run -# use std::sync::Arc; -# use ash::vk; -# use vk_graph::driver::DriverError; -# use vk_graph::driver::device::{Device, DeviceInfo}; -# use vk_graph::driver::buffer::{Buffer, BufferInfo}; -# fn main() -> Result<(), DriverError> { -# let device = Device::new(DeviceInfo::default())?; -let info = BufferInfo::host_mem(1024, vk::BufferUsageFlags::STORAGE_BUFFER); -let my_buf = Buffer::create(&device, info)?; -# Ok(()) } -``` - -| Pipeline | Create Using | -|-------------------------------|-----------------------------------------------------| -| [`ComputePipelineInfo`] | [`ComputePipeline::create`] | -| [`GraphicPipelineInfo`] | [`GraphicPipeline::create`] | -| [`RayTracePipelineInfo`] | [`RayTracePipeline::create`] | - -For example, a graphics pipeline: - -```no_run -# use std::sync::Arc; -# use ash::vk; -# use vk_graph::driver::DriverError; -# use vk_graph::driver::device::{Device, DeviceInfo}; -# use vk_graph::driver::graphic::{GraphicPipeline, GraphicPipelineInfo}; -# use vk_graph::driver::shader::Shader; -# fn main() -> Result<(), DriverError> { -# let device = Device::new(DeviceInfo::default())?; -# let my_frag_code = [0u8; 1]; -# let my_vert_code = [0u8; 1]; -// shader code is SPIR-V in u32 format -let vert = Shader::new_vertex(my_vert_code.as_slice()); -let frag = Shader::new_fragment(my_frag_code.as_slice()); -let info = GraphicPipelineInfo::default(); -let my_pipeline = GraphicPipeline::create(&device, info, [vert, frag])?; -# Ok(()) } -``` - -_Note:_ dtolnay's read-only public field deref pattern -(_[link](https://github.com/dtolnay/case-studies/blob/master/readonly-fields/README.md)_) is used to -make the information of each resource easily available and immutable. - -```no_run -# use std::sync::Arc; -# use ash::vk; -# use vk_graph::driver::DriverError; -# use vk_graph::driver::device::{Device, DeviceInfo}; -# use vk_graph::driver::image::{Image, ImageInfo}; -# fn main() -> Result<(), DriverError> { -# let device = Device::new(DeviceInfo::default())?; -let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::empty()); -let my_image = Image::create(&device, info)?; - -// Note: info is a field provided through the Deref trait and is immutable! -assert_eq!(8, my_image.info.width); -# Ok(()) } -``` - -## Pooling - -Multiple [`pool`] types are available to reduce the impact of frequently creating and dropping -resources. Leased resources behave identically to owned resources and can be used in a render graph. - -Resource aliasing is also availble as an optional way to reduce the number of concurrent resources -that may be required. - -For example, leasing an image: - -```no_run -# use std::sync::Arc; -# use ash::vk; -# use vk_graph::driver::DriverError; -# use vk_graph::driver::device::{Device, DeviceInfo}; -# use vk_graph::driver::image::{ImageInfo}; -# use vk_graph::pool::{Pool}; -# use vk_graph::pool::lazy::{LazyPool}; -# fn main() -> Result<(), DriverError> { -# let device = Device::new(DeviceInfo::default())?; -let mut pool = LazyPool::new(&device); - -let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::STORAGE); -let my_image = pool.lease_resource(info)?; -# Ok(()) } -``` - -# Render Graph Operations - -All rendering in _vk-graph_ is performed using a [`Graph`] composed of user-specified passes, -which may include pipelines and read/write access to resources. Recorded passes are automatically -optimized before submission to the graphics hardware. - -Some notes about the awesome render pass optimization which was _totally stolen_ from [Granite]: - -- Scheduling: passes are submitted to the Vulkan API using batches designed for low-latency -- Re-ordering: passes are shuffled using a heuristic which gives the GPU more time to complete work -- Merging: compatible passes are merged into dynamic subpasses when it is more efficient (_on-tile - rendering_) -- Aliasing: resources and pipelines are optimized to emit minimal barriers per unit of work (_max - one, typically zero_) - -## Nodes - -Resources may be directly bound to a render graph. During the time a resource is bound we refer to -it as a node. Bound nodes may only be used with the graphs they were bound to. Nodes implement -`Copy` to make using them easier. - -```no_run -# use std::sync::Arc; -# use ash::vk; -# use vk_graph::driver::DriverError; -# use vk_graph::driver::device::{Device, DeviceInfo}; -# use vk_graph::driver::buffer::{Buffer, BufferInfo}; -# use vk_graph::driver::image::{Image, ImageInfo}; -# use vk_graph::Graph; -# use vk_graph::pool::{Pool}; -# use vk_graph::pool::lazy::{LazyPool}; -# fn main() -> Result<(), DriverError> { -# let device = Device::new(DeviceInfo::default())?; -# let info = BufferInfo::host_mem(1024, vk::BufferUsageFlags::STORAGE_BUFFER); -# let buffer = Buffer::create(&device, info)?; -# let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::STORAGE); -# let image = Image::create(&device, info)?; -# let mut graph = Graph::default(); -println!("{:?}", buffer); // Buffer -println!("{:?}", image); // Image - -// Bind our resources into opaque "usize" nodes -let buffer = graph.bind_resource(buffer); -let image = graph.bind_resource(image); - -// The results have unique types! -println!("{:?}", buffer); // BufferNode -println!("{:?}", image); // ImageNode - -// Borrow resources using nodes (Optional!) -println!("{:?}", graph.resource(buffer)); // &Arc -println!("{:?}", graph.resource(image)); // &Arc -# Ok(()) } -``` - -_Note:_ See [this code](https://github.com/attackgoat/vk-graph/blob/master/src/graph/edge.rs#L34) -for all the things that can be bound or unbound from a graph. - -_Note:_ Once unbound, the node is invalid and should be dropped. - -## Access and synchronization - -Render graphs and their passes contain a set of functions used to handle Vulkan synchronization with -prefixes of `access`, `read`, or `write`. For each resource used in a computing, graphics subpass, -ray tracing, or general command buffer you must call an access function. Generally choose a `read` -or `write` function unless you want to be most efficient. - -Example: - -```no_run -# use std::sync::Arc; -# use ash::vk; -# use vk_graph::driver::DriverError; -# use vk_graph::driver::sync::AccessType; -# use vk_graph::driver::device::{Device, DeviceInfo}; -# use vk_graph::driver::buffer::{Buffer, BufferInfo}; -# use vk_graph::driver::image::{Image, ImageInfo}; -# use vk_graph::Graph; -# use vk_graph::node::{BufferNode, ImageNode}; -# use vk_graph::pool::{Pool}; -# use vk_graph::pool::lazy::{LazyPool}; -# fn main() -> Result<(), DriverError> { -# let device = Device::new(DeviceInfo::default())?; -# let info = BufferInfo::host_mem(1024, vk::BufferUsageFlags::STORAGE_BUFFER); -# let buffer = Buffer::create(&device, info)?; -# let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::STORAGE); -# let image = Image::create(&device, info)?; -let mut graph = Graph::default(); -let buffer: BufferNode = graph.bind_resource(buffer); -let image: ImageNode = graph.bind_resource(image); -graph - .begin_cmd() - .debug_name("Do some raw Vulkan or interop with another Vulkan library") - .record_cmd_buf(|cmd_buf| { - // I always run first! - }) - .resource_access(buffer, AccessType::HostRead) - .resource_access(image, AccessType::HostWrite) - .record_cmd_buf(move |cmd_buf| { - // cmd_buf allows you to borrow the Vulkan resources - let buffer: vk::Buffer = cmd_buf.resource(buffer).handle; - let image: vk::Image = cmd_buf.resource(image).handle; - - // Raw ash types are also available - let device: &ash::Device = &cmd_buf.device; - let cmd_buf: vk::CommandBuffer = cmd_buf.handle; - - // You are free to READ buffer and WRITE image! - }); -# Ok(()) } -``` - -## Shader pipelines - -Pipeline instances may be bound to a [`PassRef`] in order to execute the associated shader code: - -```no_run -# use ash::vk; -# use vk_graph::driver::DriverError; -# use vk_graph::driver::device::{Device, DeviceInfo}; -# use vk_graph::driver::compute::{ComputePipeline, ComputePipelineInfo}; -# use vk_graph::driver::shader::{Shader}; -# use vk_graph::Graph; -# fn main() -> Result<(), DriverError> { -# let device = Device::new(DeviceInfo::default())?; -# let my_shader_code = [0u8; 1]; -# let info = ComputePipelineInfo::default(); -# let shader = Shader::new_compute(my_shader_code.as_slice()); -# let my_compute_pipeline = ComputePipeline::create(&device, info, shader)?; -# let mut graph = Graph::default(); -graph - .begin_cmd() - .debug_name("My compute pass") - .bind_pipeline(&my_compute_pipeline) - .record_cmd_buf(|cmd_buf| { - cmd_buf.push_constants(0, &42u32.to_ne_bytes()) - .dispatch(128, 1, 1); - }); -# Ok(()) } -``` - -## Image samplers - -By default, _vk-graph_ will use "linear repeat-mode" samplers unless a special suffix appears as -part of the name within GLSL or HLSL shader code. The `_sampler_123` suffix should be used where -`1`, `2`, and `3` are replaced with: - -1. `l` for `LINEAR` texel filtering (default) or `n` for `NEAREST` -1. `l` (default) or `n`, as above, but for mipmap filtering -1. Addressing mode where: - - `b` is `CLAMP_TO_BORDER` - - `e` is `CLAMP_TO_EDGE` - - `m` is `MIRRORED_REPEAT` - - `r` is `REPEAT` - -For example, the following sampler named `pages_sampler_nnr` specifies nearest texel/mipmap modes and repeat addressing: - -```glsl -layout(set = 0, binding = 0) uniform sampler2D pages_sampler_nnr[NUM_PAGES]; -``` - -For more complex image sampling, use [`ShaderBuilder::image_sampler`] to specify the exact image -sampling mode. - -## Vertex input - -Optional name suffixes are used in the same way with vertex input as with image samplers. The -additional attribution of your shader code is optional but may help in a few scenarios: - -- Per-instance vertex rate data -- Multiple vertex buffer binding indexes - -The data for vertex input is assumed to be per-vertex and bound to vertex buffer binding index zero. -Add `_ibindX` for per-instance data, or the matching `_vbindX` for per-vertex data where `X` is -replaced with the vertex buffer binding index in each case. - -For more complex vertex layouts, use the [`ShaderBuilder::vertex_input`] to specify the exact -layout. - -[`AccelerationStructureInfo`]: driver::accel_struct::AccelerationStructureInfo -[`AccelerationStructure::create`]: driver::accel_struct::AccelerationStructure::create -[`Buffer::create`]: driver::buffer::Buffer::create -[`Buffer::create_from_slice`]: driver::buffer::Buffer::create_from_slice -[`BufferInfo`]: driver::buffer::BufferInfo -[`ComputePipeline::create`]: driver::compute::ComputePipeline::create -[`ComputePipelineInfo`]: driver::compute::ComputePipelineInfo -[`Device`]: driver::device::Device -[`EventLoop`]: EventLoop -[`FrameContext`]: FrameContext -[Granite]: https://github.com/Themaister/Granite -[`GraphicPipeline::create`]: driver::graphic::GraphicPipeline::create -[`GraphicPipelineInfo`]: driver::graphic::GraphicPipelineInfo -[`Image::create`]: driver::image::Image::create -[`ImageInfo`]: driver::image::ImageInfo -[`PassRef`]: graph::pass_ref::PassRef -[`RayTracePipeline::create`]: driver::ray_trace::RayTracePipeline::create -[`RayTracePipelineInfo`]: driver::ray_trace::RayTracePipelineInfo -[`Graph`]: graph::Graph -[`ShaderBuilder::image_sampler`]: driver::shader::ShaderBuilder::image_sampler -[`ShaderBuilder::vertex_input`]: driver::shader::ShaderBuilder::vertex_input */ @@ -646,7 +309,7 @@ impl CommandData { } } -/// A composable graph of render pass operations. +/// A composable graph of Vulkan command buffer operations. /// /// `Graph` instances are are intended for one-time use. /// @@ -1275,7 +938,9 @@ impl Graph { } } -/// A Vulkan resource which has been bound to a [`Graph`] using [`Graph::bind_node`]. +/// A Vulkan resource which has been bound to a [`Graph`]. +/// +/// See [`Graph::bind_resource`]. pub trait Node { /// The Vulkan buffer, image, or acceleration struction type. type Resource; @@ -1287,10 +952,10 @@ pub trait Node { fn index(&self) -> NodeIndex; } -/// A trait for resources which may be bound to a `Graph`. +/// A Vulkan resource which may be bound to a [`Graph`]. /// /// See [`Graph::bind_resource`] and -/// [`CommandRef::bind_resource`](super::cmd::CommandRef::bind_resource) for details. +/// [`Command::bind_resource`](crate::cmd::Command::bind_resource). pub trait Resource { /// The resource handle type. type Node; diff --git a/src/node.rs b/src/node.rs index b04ebb38..2159c5db 100644 --- a/src/node.rs +++ b/src/node.rs @@ -1,4 +1,4 @@ -//! Bindings for Vulkan smart-pointer resources. +//! Handles for Vulkan smart-pointer resources. use std::sync::Arc; diff --git a/src/pool/mod.rs b/src/pool/mod.rs index da9a12a1..da853500 100644 --- a/src/pool/mod.rs +++ b/src/pool/mod.rs @@ -1,4 +1,4 @@ -//! Resource leasing and pooling types. +//! Resource pooling, leasing, and aliasing types. //! //! _vk-graph_ provides caching for acceleration structure, buffer and image resources which may be //! leased from configurable pools using their corresponding information structure. Most programs diff --git a/src/queue.rs b/src/queue.rs index 4f265bd2..07e76654 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -180,7 +180,9 @@ impl Drop for PhysicalPass { } } -/// A structure which can optimize and submit graphs. This pattern was derived from: +/// A structure which can optimize and submit [`Graph`] instances. +/// +/// This pattern was derived from: /// /// /// From f1a8f989eeb6bbe4526813bcf9b199b1311068c8 Mon Sep 17 00:00:00 2001 From: John Wells Date: Fri, 20 Mar 2026 06:05:52 -0400 Subject: [PATCH 52/86] Improve hot example --- .../vk-graph-hot/examples/res/fill_image.comp | 17 +++++-- crates/vk-graph-hot/examples/res/plasma.glsl | 22 +++++++++ crates/vk-graph-hot/src/shader.rs | 48 ++++++++++++------- 3 files changed, 66 insertions(+), 21 deletions(-) create mode 100644 crates/vk-graph-hot/examples/res/plasma.glsl diff --git a/crates/vk-graph-hot/examples/res/fill_image.comp b/crates/vk-graph-hot/examples/res/fill_image.comp index b4b02949..506a3078 100644 --- a/crates/vk-graph-hot/examples/res/fill_image.comp +++ b/crates/vk-graph-hot/examples/res/fill_image.comp @@ -1,6 +1,6 @@ #version 460 core -#include "noise.glsl" +#include "plasma.glsl" layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; @@ -11,8 +11,17 @@ layout(push_constant) uniform PushConstants { layout(set = 0, binding = 0, rgba32f) restrict writeonly uniform image2D image; void main() { - uvec3 data = uvec3(gl_GlobalInvocationID.xy, push_const.frame_index); - vec4 color = vec4(hash(data), 1.0); + vec2 extent = vec2(1.0, 1024 / 768); + vec2 offset = vec2(gl_GlobalInvocationID.xy) / extent; + vec4 color = plasma( + offset, + extent, + float(push_const.frame_index) / 1440.0, + 0.0015, + 1.0, + 0.02, + vec3(0.2, 0.8, 0.5) + ); imageStore(image, ivec2(gl_GlobalInvocationID.xy), color); -} \ No newline at end of file +} diff --git a/crates/vk-graph-hot/examples/res/plasma.glsl b/crates/vk-graph-hot/examples/res/plasma.glsl new file mode 100644 index 00000000..f69e0598 --- /dev/null +++ b/crates/vk-graph-hot/examples/res/plasma.glsl @@ -0,0 +1,22 @@ +const float PI = 3.14159265; + +vec4 plasma( + vec2 offset, + vec2 extent, + float time, + float radius, + float alpha, + float size, + vec3 shift +) { + float color1 = (sin(dot(offset, vec2(sin(time * 3.0), cos(time * 3.0))) * 0.02 + time * 3.0) + 1.0) / 2.0; + vec2 center = extent / 2.0 + vec2(extent.x / 2.0 * sin(-time * 3.0) * radius, extent.y / 2.0 * cos(-time * 3.0) * radius); + float color2 = (cos(length(offset - center) * size) + 1.0) / 2.0; + float color = (color1 + color2) / 2.0; + + float red = (sin(PI * color / shift.r + time * 3.0) + 1.0) / 2.0; + float green = (sin(PI * color / shift.g + time * 3.0) + 1.0) / 2.0; + float blue = (sin(PI * color / shift.b + time * 3.0) + 1.0) / 2.0; + + return vec4(red, green, blue, alpha); +} diff --git a/crates/vk-graph-hot/src/shader.rs b/crates/vk-graph-hot/src/shader.rs index a3412560..dd0d7746 100644 --- a/crates/vk-graph-hot/src/shader.rs +++ b/crates/vk-graph-hot/src/shader.rs @@ -5,7 +5,7 @@ pub use shaderc::{OptimizationLevel, SourceLanguage, SpirvVersion}; use { super::{compile_shader, guess_shader_source_language}, derive_builder::{Builder, UninitializedFieldError}, - log::{debug, error}, + log::{Level, debug, error, log_enabled}, notify::{RecommendedWatcher, RecursiveMode, Watcher}, shaderc::{CompileOptions, EnvVersion, ShaderKind, TargetEnv}, std::path::{Path, PathBuf}, @@ -238,21 +238,25 @@ impl HotShader { &self, watcher: &mut RecommendedWatcher, ) -> Result, DriverError> { - let shader_kind = match self.stage { - vk::ShaderStageFlags::ANY_HIT_KHR => ShaderKind::AnyHit, - vk::ShaderStageFlags::CALLABLE_KHR => ShaderKind::Callable, - vk::ShaderStageFlags::CLOSEST_HIT_KHR => ShaderKind::ClosestHit, - vk::ShaderStageFlags::COMPUTE => ShaderKind::Compute, - vk::ShaderStageFlags::FRAGMENT => ShaderKind::Fragment, - vk::ShaderStageFlags::GEOMETRY => ShaderKind::Geometry, - vk::ShaderStageFlags::INTERSECTION_KHR => ShaderKind::Intersection, - vk::ShaderStageFlags::MISS_KHR => ShaderKind::Miss, - vk::ShaderStageFlags::RAYGEN_KHR => ShaderKind::RayGeneration, - vk::ShaderStageFlags::TASK_EXT => ShaderKind::Task, - vk::ShaderStageFlags::TESSELLATION_CONTROL => ShaderKind::TessControl, - vk::ShaderStageFlags::TESSELLATION_EVALUATION => ShaderKind::TessEvaluation, - vk::ShaderStageFlags::VERTEX => ShaderKind::Vertex, - _ => unimplemented!("{:?}", self.stage), + let shader_kind = if self.stage == vk::ShaderStageFlags::empty() { + None + } else { + Some(match self.stage { + vk::ShaderStageFlags::ANY_HIT_KHR => ShaderKind::AnyHit, + vk::ShaderStageFlags::CALLABLE_KHR => ShaderKind::Callable, + vk::ShaderStageFlags::CLOSEST_HIT_KHR => ShaderKind::ClosestHit, + vk::ShaderStageFlags::COMPUTE => ShaderKind::Compute, + vk::ShaderStageFlags::FRAGMENT => ShaderKind::Fragment, + vk::ShaderStageFlags::GEOMETRY => ShaderKind::Geometry, + vk::ShaderStageFlags::INTERSECTION_KHR => ShaderKind::Intersection, + vk::ShaderStageFlags::MISS_KHR => ShaderKind::Miss, + vk::ShaderStageFlags::RAYGEN_KHR => ShaderKind::RayGeneration, + vk::ShaderStageFlags::TASK_EXT => ShaderKind::Task, + vk::ShaderStageFlags::TESSELLATION_CONTROL => ShaderKind::TessControl, + vk::ShaderStageFlags::TESSELLATION_EVALUATION => ShaderKind::TessEvaluation, + vk::ShaderStageFlags::VERTEX => ShaderKind::Vertex, + _ => unimplemented!("{:?}", self.stage), + }) }; let mut additional_opts = CompileOptions::new().map_err(|err| { @@ -294,10 +298,14 @@ impl HotShader { let res = compile_shader( &self.path, &self.entry_name, - Some(shader_kind), + shader_kind, Some(&additional_opts), ) .map_err(|err| { + if !log_enabled!(Level::Error) { + panic!("Unable to compile shader {}: {err}", self.path.display()); + } + error!("Unable to compile shader {}: {err}", self.path.display()); DriverError::InvalidData @@ -346,6 +354,12 @@ impl HotShaderBuilder { #[cfg(target_os = "macos")] let this = this.macro_definition("MOLTEN_VK", Some("1".to_string())); + let mut this = this; + + if this.stage.is_none() { + this.stage = Some(vk::ShaderStageFlags::empty()); + } + this.fallible_build() .expect("All required fields set at initialization") } From 75d1def9d5aa51356c4388943a7d0fecfc588861 Mon Sep 17 00:00:00 2001 From: John Wells Date: Mon, 23 Mar 2026 15:04:11 -0400 Subject: [PATCH 53/86] fixes --- crates/vk-graph-window/src/swapchain.rs | 6 +- src/cmd/mod.rs | 2 +- src/cmd/pipeline.rs | 2 +- src/driver/swapchain.rs | 3 +- src/lib.rs | 81 ++++++++++--------------- src/node.rs | 18 +++--- src/queue.rs | 32 +++++----- 7 files changed, 65 insertions(+), 79 deletions(-) diff --git a/crates/vk-graph-window/src/swapchain.rs b/crates/vk-graph-window/src/swapchain.rs index ad28a069..ad99956a 100644 --- a/crates/vk-graph-window/src/swapchain.rs +++ b/crates/vk-graph-window/src/swapchain.rs @@ -156,7 +156,9 @@ impl Swapchain { })?; } - let acquire_next_image = self.swapchain.acquire_next_image(exec.swapchain_acquired); + let acquire_next_image = self + .swapchain + .acquire_next_image(u64::MAX, exec.swapchain_acquired); if let Err(err) = acquire_next_image { warn!("unable to acquire next swapchain image: {err:?}"); @@ -171,7 +173,7 @@ impl Swapchain { Ok(swapchain_image) => Ok(swapchain_image), }?; - while self.image_execs.len() >= swapchain_image.index as _ { + while swapchain_image.index >= self.image_execs.len() as u32 { self.image_execs.push(0); } diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index 797448dc..f5064024 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -657,7 +657,7 @@ mod deprecated { let idx = node.index(); self.graph.resources[idx] - .as_driver_buffer() + .as_buffer() .unwrap() .device_address() } diff --git a/src/cmd/pipeline.rs b/src/cmd/pipeline.rs index a8ca97af..feac9f29 100644 --- a/src/cmd/pipeline.rs +++ b/src/cmd/pipeline.rs @@ -420,7 +420,7 @@ mod deprecated { let idx = node.index(); self.cmd.graph.resources[idx] - .as_driver_buffer() + .as_buffer() .unwrap() .device_address() } diff --git a/src/driver/swapchain.rs b/src/driver/swapchain.rs index 4d77e59e..12f3ebb4 100644 --- a/src/driver/swapchain.rs +++ b/src/driver/swapchain.rs @@ -77,6 +77,7 @@ impl Swapchain { #[profiling::function] pub fn acquire_next_image( &mut self, + timeout: u64, acquired: vk::Semaphore, ) -> Result { for _ in 0..2 { @@ -93,7 +94,7 @@ impl Swapchain { let swapchain_ext = Device::expect_swapchain_ext(&self.surface.device); let image_idx = unsafe { - swapchain_ext.acquire_next_image(self.handle, u64::MAX, acquired, vk::Fence::null()) + swapchain_ext.acquire_next_image(self.handle, timeout, acquired, vk::Fence::null()) } .map(|(idx, suboptimal)| { if suboptimal { diff --git a/src/lib.rs b/src/lib.rs index 471b8dab..5cef9d17 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -82,7 +82,7 @@ pub enum AnyResource { } impl AnyResource { - fn as_driver_accel_struct(&self) -> Option<&AccelerationStructure> { + fn as_accel_struct(&self) -> Option<&AccelerationStructure> { Some(match self { Self::AccelerationStructure(resource) => resource, Self::AccelerationStructureLease(resource) => resource, @@ -90,7 +90,7 @@ impl AnyResource { }) } - fn as_driver_buffer(&self) -> Option<&Buffer> { + fn as_buffer(&self) -> Option<&Buffer> { Some(match self { Self::Buffer(resource) => resource, Self::BufferLease(resource) => resource, @@ -98,7 +98,7 @@ impl AnyResource { }) } - fn as_driver_image(&self) -> Option<&Image> { + fn as_image(&self) -> Option<&Image> { Some(match self { Self::Image(resource) => resource, Self::ImageLease(resource) => resource, @@ -1018,7 +1018,7 @@ macro_rules! graph_resource { // We will return an existing node, if possible // TODO: Could store a sorted list of these shared pointers to avoid the O(N) for (idx, existing_resource) in graph.resources.iter_mut().enumerate() { - if let Some(existing_resource) = existing_resource.[]() { + if let AnyResource::$name(existing_resource) = existing_resource { if Arc::ptr_eq(existing_resource, &self) { return Self::Node::new(idx); } @@ -1072,8 +1072,8 @@ macro_rules! graph_resource { // We will return an existing node, if possible // TODO: Could store a sorted list of these shared pointers to avoid the O(N) - for (idx, existing_resource) in graph.resources.iter_mut().enumerate() { - if let Some(existing_resource) = existing_resource.[]() { + for (idx, existing_resource) in graph.resources.iter().enumerate() { + if let AnyResource::[<$name Lease>](existing_resource) = existing_resource { if Arc::ptr_eq(existing_resource, &self) { return Self::Node::new(idx); } @@ -1099,40 +1099,6 @@ macro_rules! graph_resource { Arc::clone(self).bind_graph(graph) } } - - impl AnyResource { - fn [](&self) -> Option<&Arc<$name>> { - let Self::$name(resource) = self else { - return None; - }; - - Some(resource) - } - - fn [](&mut self) -> Option<&mut Arc<$name>> { - let Self::$name(resource) = self else { - return None; - }; - - Some(resource) - } - - fn [](&self) -> Option<&Arc>> { - let Self::[<$name Lease>](resource) = self else { - return None - }; - - Some(resource) - } - - fn [](&mut self) -> Option<&mut Arc>> { - let Self::[<$name Lease>](resource) = self else { - return None; - }; - - Some(resource) - } - } } }; } @@ -1456,10 +1422,7 @@ pub(crate) mod deprecated { pub fn node_device_address(&self, node: impl Node) -> vk::DeviceAddress { let idx = node.index(); - self.resources[idx] - .as_driver_buffer() - .unwrap() - .device_address() + self.resources[idx].as_buffer().unwrap().device_address() } #[deprecated = "dereference info field of resource function result"] @@ -1527,7 +1490,11 @@ pub(crate) mod deprecated { where Self: Node, { - resources[self.index()].[]().unwrap().info + let AnyResource::$name(resource) = &resources[self.index()] else { + panic!("invalid node"); + }; + + resource.info } } @@ -1538,7 +1505,11 @@ pub(crate) mod deprecated { where Self: Node, { - resources[self.index()].[]().unwrap().info + let AnyResource::$name(resource) = &resources[self.index()] else { + panic!("invalid node"); + }; + + resource.info } } @@ -1549,7 +1520,11 @@ pub(crate) mod deprecated { where Self: Node, { - resources[self.index()].[]().unwrap().info + let AnyResource::[<$name Lease>](resource) = &resources[self.index()] else { + panic!("invalid node"); + }; + + resource.info } } @@ -1557,7 +1532,11 @@ pub(crate) mod deprecated { type Result = Arc<$name>; fn unbind(&self, resources: &[AnyResource]) -> Self::Result { - resources[self.index()].[]().unwrap().clone() + let AnyResource::$name(resource) = &resources[self.index()] else { + panic!("invalid node"); + }; + + resource.clone() } } @@ -1565,7 +1544,11 @@ pub(crate) mod deprecated { type Result = Arc>; fn unbind(&self, resources: &[AnyResource]) -> Self::Result { - resources[self.index()].[]().unwrap().clone() + let AnyResource::[<$name Lease>](resource) = &resources[self.index()] else { + panic!("invalid node"); + }; + + resource.clone() } } } diff --git a/src/node.rs b/src/node.rs index 2159c5db..5fd848b1 100644 --- a/src/node.rs +++ b/src/node.rs @@ -40,7 +40,7 @@ impl Node for AnyAccelerationStructureNode { type Resource = AccelerationStructure; fn borrow(self, resources: &[AnyResource]) -> &Self::Resource { - resources[self.index()].as_driver_accel_struct().unwrap() + resources[self.index()].as_accel_struct().unwrap() } fn index(&self) -> NodeIndex { @@ -77,7 +77,7 @@ impl Node for AnyBufferNode { type Resource = Buffer; fn borrow(self, resources: &[AnyResource]) -> &Self::Resource { - resources[self.index()].as_driver_buffer().unwrap() + resources[self.index()].as_buffer().unwrap() } fn index(&self) -> NodeIndex { @@ -125,7 +125,7 @@ impl Node for AnyImageNode { type Resource = Image; fn borrow(self, resources: &[AnyResource]) -> &Self::Resource { - resources[self.index()].as_driver_image().unwrap() + resources[self.index()].as_image().unwrap() } fn index(&self) -> NodeIndex { @@ -176,15 +176,15 @@ macro_rules! node { node!( AccelerationStructure, Arc, - as_driver_accel_struct + as_accel_struct ); node!( AccelerationStructureLease, Arc>, - as_driver_accel_struct + as_accel_struct ); -node!(Buffer, Arc, as_driver_buffer); -node!(BufferLease, Arc>, as_driver_buffer); -node!(Image, Arc, as_driver_image); -node!(ImageLease, Arc>, as_driver_image); +node!(Buffer, Arc, as_buffer); +node!(BufferLease, Arc>, as_buffer); +node!(Image, Arc, as_image); +node!(ImageLease, Arc>, as_image); node!(SwapchainImage, SwapchainImage, as_swapchain_image); diff --git a/src/queue.rs b/src/queue.rs index 07e76654..559b58b2 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -446,7 +446,7 @@ impl Queue { }, }; - let image = bindings[attachment.target].as_driver_image().unwrap(); + let image = bindings[attachment.target].as_image().unwrap(); attachment_image.flags = image.info.flags; attachment_image.usage = image.info.usage; @@ -474,7 +474,7 @@ impl Queue { .view_formats .binary_search(&attachment.format) { - let image = bindings[attachment.target].as_driver_image().unwrap(); + let image = bindings[attachment.target].as_image().unwrap(); attachment_image.flags = image.info.flags; attachment_image.usage = image.info.usage; @@ -500,7 +500,7 @@ impl Queue { depth_stencil: *clear_value, }; - let image = bindings[attachment.target].as_driver_image().unwrap(); + let image = bindings[attachment.target].as_image().unwrap(); attachment_image.flags = image.info.flags; attachment_image.usage = image.info.usage; @@ -526,7 +526,7 @@ impl Queue { .view_formats .binary_search(&attachment.format) { - let image = bindings[attachment.target].as_driver_image().unwrap(); + let image = bindings[attachment.target].as_image().unwrap(); attachment_image.flags = image.info.flags; attachment_image.usage = image.info.usage; @@ -550,7 +550,7 @@ impl Queue { .view_formats .binary_search(&attachment.format) { - let image = bindings[attachment.target].as_driver_image().unwrap(); + let image = bindings[attachment.target].as_image().unwrap(); attachment_image.flags = image.info.flags; attachment_image.usage = image.info.usage; @@ -1936,7 +1936,7 @@ impl Queue { match resource { AnyResource::AccelerationStructure(..) | AnyResource::AccelerationStructureLease(..) => { - let Some(accel_struct) = resource.as_driver_accel_struct() else { + let Some(accel_struct) = resource.as_accel_struct() else { #[cfg(debug_assertions)] unreachable!(); @@ -1959,7 +1959,7 @@ impl Queue { tls.prev_accesses.push(prev_access); } AnyResource::Buffer(..) | AnyResource::BufferLease(..) => { - let Some(buffer) = resource.as_driver_buffer() else { + let Some(buffer) = resource.as_buffer() else { #[cfg(debug_assertions)] unreachable!(); @@ -1994,7 +1994,7 @@ impl Queue { AnyResource::Image(..) | AnyResource::ImageLease(..) | AnyResource::SwapchainImage(..) => { - let Some(image) = resource.as_driver_image() else { + let Some(image) = resource.as_image() else { #[cfg(debug_assertions)] unreachable!(); @@ -2178,7 +2178,7 @@ impl Queue { match resource { AnyResource::AccelerationStructure(..) | AnyResource::AccelerationStructureLease(..) => { - let Some(accel_struct) = resource.as_driver_accel_struct() else { + let Some(accel_struct) = resource.as_accel_struct() else { #[cfg(debug_assertions)] unreachable!(); @@ -2191,7 +2191,7 @@ impl Queue { AccelerationStructure::access(accel_struct, AccessType::Nothing); } AnyResource::Buffer(..) | AnyResource::BufferLease(..) => { - let Some(buffer) = resource.as_driver_buffer() else { + let Some(buffer) = resource.as_buffer() else { #[cfg(debug_assertions)] unreachable!(); @@ -2224,7 +2224,7 @@ impl Queue { AnyResource::Image(..) | AnyResource::ImageLease(..) | AnyResource::SwapchainImage(..) => { - let Some(image) = resource.as_driver_image() else { + let Some(image) = resource.as_image() else { #[cfg(debug_assertions)] unreachable!(); @@ -2554,7 +2554,7 @@ impl Queue { .chain(first_exec.depth_stencil_load) .chain(first_exec.depth_stencil_store) .map(|attachment| { - let info = bindings[attachment.target].as_driver_image().unwrap().info; + let info = bindings[attachment.target].as_image().unwrap().info; ( info.width >> attachment.base_mip_level, @@ -3008,7 +3008,7 @@ impl Queue { .unwrap_or_else(|| panic!("descriptor {descriptor_set_idx}.{dst_binding}[{binding_offset}] specified in recorded execution of pass \"{}\" was not discovered through shader reflection", pass.name())); let descriptor_type = descriptor_info.descriptor_type(); let bound_node = &bindings[*node_idx]; - if let Some(image) = bound_node.as_driver_image() { + if let Some(image) = bound_node.as_image() { let mut image_view_info = *view_info.as_image().unwrap(); // Handle default views which did not specify a particaular aspect @@ -3062,7 +3062,7 @@ impl Queue { .image_layout(image_layout) .image_view(image_view), ); - } else if let Some(buffer) = bound_node.as_driver_buffer() { + } else if let Some(buffer) = bound_node.as_buffer() { let buffer_view_info = view_info.as_buffer().unwrap(); if binding_offset == 0 { @@ -3086,7 +3086,7 @@ impl Queue { .offset(buffer_view_info.start) .range(buffer_view_info.end - buffer_view_info.start), ); - } else if let Some(accel_struct) = bound_node.as_driver_accel_struct() { + } else if let Some(accel_struct) = bound_node.as_accel_struct() { if binding_offset == 0 { tls.accel_struct_writes.push(IndexWrite { idx: tls.accel_struct_infos.len(), @@ -3148,7 +3148,7 @@ impl Queue { let late = &write_exec.accesses[&attachment.target].last().unwrap(); let image_range = late.subresource.as_image().unwrap(); let image_binding = &bindings[attachment.target]; - let image = image_binding.as_driver_image().unwrap(); + let image = image_binding.as_image().unwrap(); let image_view_info = attachment .image_view_info(image.info) .into_builder() From 3935897c0f330ab7b8dfffd76576992281555906 Mon Sep 17 00:00:00 2001 From: John Wells Date: Mon, 23 Mar 2026 15:05:48 -0400 Subject: [PATCH 54/86] hot crate cleanup --- crates/vk-graph-hot/.github/img/noise.png | Bin 1672497 -> 0 bytes crates/vk-graph-hot/.github/img/plasma.png | Bin 0 -> 746970 bytes crates/vk-graph-hot/examples/README.md | 4 +-- crates/vk-graph-hot/examples/glsl.rs | 9 ++++--- crates/vk-graph-hot/examples/hlsl.rs | 6 ++--- .../vk-graph-hot/examples/res/fill_image.hlsl | 23 ++++++++++++------ crates/vk-graph-hot/examples/res/noise.glsl | 9 ------- crates/vk-graph-hot/examples/res/noise.hlsl | 9 ------- crates/vk-graph-hot/examples/res/plasma.hlsl | 22 +++++++++++++++++ 9 files changed, 47 insertions(+), 35 deletions(-) delete mode 100644 crates/vk-graph-hot/.github/img/noise.png create mode 100644 crates/vk-graph-hot/.github/img/plasma.png delete mode 100644 crates/vk-graph-hot/examples/res/noise.glsl delete mode 100644 crates/vk-graph-hot/examples/res/noise.hlsl create mode 100644 crates/vk-graph-hot/examples/res/plasma.hlsl diff --git a/crates/vk-graph-hot/.github/img/noise.png b/crates/vk-graph-hot/.github/img/noise.png deleted file mode 100644 index 4f096886bad2649851c2fd4c8e6f4b5e49227adb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1672497 zcmd42gLhv3mOaz3=SYJEC&YmO&kpDYyS_3&lzvZssS*tFMg&%Lcak* zLIgH;)<&ilhG1ai(Vo#f;ywI0ecJLOQz5>n!kh+$(rCgWICan>g@|VUO1P4=Z+}VQ z03o3{n`$jt4CYt74?U`cLXnsF#`bs(gb5qZ7^F< z0h^`o4X^6D3=*H6=G9}keV3t+aln{^SvtEUHQKU@slF03%QnS!VewHc zodLHJhqdkeu;P3xnYS)^K?Svanh`h@wH_3bFnM9NAEQOD$sC|6D*LT5(tGhvVpncZ z@hkDbSIS9(7OzXb$F;J!jMU2*LEFrmK3iVVKjoU4vi7ML`U9q^U|q(WK2mX^h0T@77K*44Cbj?vk;b>#v>H!vxXw5J*yvf|z*;&`wxqK~+d~r?B z`5+`X_6G!X&{yafrkmHQ=1^{k!WKT~xAyWAjnObNqjQdQdtUni*~^Zs$MGA_vsb|% zgno#ELDmINDfm5Ag6z^MiMkKG~?RuC{(*Kft#ULcF@e#j49eK6XJ-Ybua|7!yN)oKZ z22%F_J?)3MK}TT7JZQpr`KaDA^TADE$co{4YHRe)Lwunv81oNrKg177_F?hLCpr4( z<;~l*zta!Uh?r%Bat7OV)@6z@QU3|ThabhL29x_FDE=(>XV}DM80D;kP{)uI+>;W8 zg0a#=w4Ux?o{tCLW)1|Sx0!s%YkIt?rfG5>=QD3&=6j#M>7XI; zltONyyQ6#7;Ft3v!SIyO85UQGnfy=K4dj4FLGdAR`sY>iic$U`UGNX-XvZdsy6ZT6uxQkd)bg701v(&P(Js z@Pj>v5b*VXI!pNzyJVC5J`8p@Mo5v9MM%6w{-$q8`d3!eS56etHtK)&Lbglm7(!Ym^`9 zzu0K!fi*PZK#ls*`$d)?0}=Z1>%_D_E(`uW7ZBfT`d&ot`l=mZ`a)#bEF))RU7eh? z?~nl_V*1hUoeEd@BlZh5<0CK052*hmcC`ahKo@R@{$1nicpHq=QP1>ChZ(Py{cTge z%OxrcAPEZVQ>qMT(Ifm0Z!H{?ue+`tR=Bvh;Z;>tK1D@E!RhJgNm-wx{&U2~$Ajbl zE91Kq6fj4iOvzkMUS1v)6SLm#`NGP{%lm#;qFk{pFDDmkYIFzb^o?!CFZ-4d@RlYx zt^7UL8tRjkkZHyNcylX^C^-srU3hqSQCXQKtB{b;x23rM;A9P1;P*KWCNcusJ)Rgz zSy=8oRiMGReFBqy05@nz3>|tKMwyFV6bRh-j=oy@aQ>0g*4DPPwzf9c*9S>u{U%>W zj|b*G+sl>AnJ{B9V}+jr{&SxZa+DB0GD}O#Sl}-;H8t|M;Rjs+^3R_?pHG)+f_i&} zzkA=RbAnqy#LRrpiTs`)>HmutIkugG!1HKbbePVMxQsc{(no+_+Fvm1Q$-B+PEO-F zWo2bx;?mNj92}T+YBcWA|2dg0Fb|1`ho`o#PL>?yQw~8vLA=1v(E&q`@aH?fOS4Vx z_6!TqjF+eO5nU*T2ziU#lm1ylp0muM!t_g%Pc*-hr>AFlNs0N-f7DZ_CItjS0|0>P z!2d()g0= zTU{My9Rpq6tQ0!!b|g19H_oAVc1s zkAt6j!Of&bgl&E7Hsf`SoQ0hk7G@{<)^(`AMUFrZJ=suS&pHYslljU26CYm&2n;!e z`d8mUe0ll#F)=ZJBO)TYhlhu0KTYYVp^=J34)^$ljtSF`Ib(?P!x}dUN*w>h`L>GG zQO)#y`3v$N>iPM3(I&e+%Gl)O;gtq!!_M#j0fLrhr2_d0`X5JLP0@Yhr|5m zZREdxeb18%_V@R9^ziTy*3-+T^{)NRCDwm#o-6)vkx|O=tinGmel}GQa4C%+QTykf zpW@RlQlHQS7AjEs`w{<3NZ2ShKR;W6@)H(S;UOW=5>iqu2a&^xE%-^AH_-=}MIz5k zFrTg;nbExQ)1$RW^Est+9>T`ak>;y7`@aEC&?D|s)e4kxM9$}{{u^xy=!5r3+}BlT zHW^s@Ti=Ey+Z*WC$d#Z>zMcP>?&|8&(AM7I@BR;O+v=^JFKEQX#NXWA-I?A^s`keN z^$9&_OD>D}Z;SrqK8hm(KQxkPNj!_`Vz8ss#l*zUHaDXrAPoM+tDpzMr(PNx8+YfY zrl!Ek`G4|ixyQE8izjuB-h7xvO&3u0k!iiG^23FHNli_)_!LvHOlgFT;yqB)_jihE_Y3XiTdO!W@&1g#KXa%+Ai{+O`SoB zi9zAvQt+RS$>Kdr)MP+Vk&7UbaoZ$-^(eV4=RuUE2!?`&hW5z=m^}Y)Xd`TInVnVe z&&$g@YQd39r$d;H+TsNa%a9aBhF?cTW94dtm6n#K=H83{=Pu5lrvC?oPir&UOc)_3 z8G~H-y99d&)5satH3_^p9ANRBoSbH#z!xgw`OhG)nhuVRa2qT&HQ*NQh%xIg3h2ar z2}YYtP$%;GAqU^++1S`50f1<0$^V!%0Hmb)Mn^|4HPI*@gTt~PCDWe@Bg6F)qSbO_ z;3oh0`1n)-^J4zL^|D0@217A@k8ASi?d?gL`Jz?w9;Ezv8;v}flT%X+aTOB(6MtrU zn$a~uhZFR)C;vny^dJDt zsO3eAK>0(MIXOK&9a91PA4Pe}xaEuLpfC9KHWJ<-6_J32Cx80eDw|$1q(m;xt+-?sxS?Ac_byvy6oipjm_`}< zOufS4=2_v#sy_%Z;{kDY<{^idvP*j4K+8thJ7Ks#nzT{-8uXL+U4Bp3+&r|Ug{#H+qJLw9PiSO! zBq6dt9D8SPPsGLL@q>}T)80t-dT$D+z4i-~xZsayK=CBgY13t*%N_m%h{$@|^A>NT zg=!4FA!k-VS&VRx)DQ9NV-DKj+8Qo0GV*eRb>!zB63{K%;&evOz|gg}_U5e$x^4P$ zz?kY-%TyU{?fR+aZGGm7E56FGBV+1HdU3*MLu$aW_m$JT`6szY3d4HRLv{0XFX7Le z;B>hz6dso|v#d-i5FU52(U$n(V&miR)%9jSPqW3z#N0ebu4u8vnXahl0tD`FjzA`W=BL~w@ykw<43PZvOPRsm4dX2|%okJk!ewe}Y3w zxICLZ+fhYR?7ZS3S0O1pkRN$uPmyT}yA)_2J0@{6l}4eP`*^d>`}_)Rgg;*}v?v`* z@A1(bvgygB>PT@PnbC>ToXELHz^`V9cY4=Yxb- zB0V%O5)TU}nB|KS291WnXN3b;*??$t!tFTsDv20bInpGwq%i{m4^5tLob6jj8xtEE z>5zi5L*7n=y~cvmFJQCfnk@)lClRnB+DS)Dra$K1;j!bFi3xkXqe3b>A$t>O0PzoE z?3!Y!mUWFU;&%sI6P-&*e2|{<7(2Tb5sZ(083KZ)dto^O$wIlL5t*Iz3cIh}imvd* z&gW62KXP;Q`IXF#uhqFg9SWF#BT9{Xn#f2@b64B7n<(gq6@Bl^sG@l!mI-ouCr6`S z^KhBGu4ij<6n0o=Y0SdcAwTr|ilm$*Evw}lB}D}3P7OwM`SfN#6kMSRh4+PGK2x%( z?w|NWzqFAP*KzkcS~Itqu+apAK1yRJ`SD2ec%GBC4^2|{!3zC-L-mul_~TT#xUvc! zPi#y#H-ki$i7mHX_osP^&=<>Q^ss0u#o1%Z|LYTV|T z#}EIU+Y2(7CCb_Yb1Qy1SB$|&LQK9Vl7n$K9_X+F7S|HGv*sYc`*B7r*~|jnw~jBn-cVwM zzPTj6I$DdxVMr*rV92C%lW`Ji>a+P%h?K;>UJ^Z?)C)Qk(mkB7z34#0dcWAmrF8r0 zITnM2K6++SN%!D>MhdyUzW%bMz$_}C>28;UF*^XFcQX&Q28R$Zev+bzK#Tu{CExKo zzGMHRYgqor)y>t-%}b3;Onv=*28&nR--i6E1WN9B-5&8@Csm|<^ok;5Txsm4_8iATS8B^049^&B-T>Pbwz(} zUg-1b^MCJYb2wgrZyn>8ar+}~2R@7~2|vMM|Mr+*Bl1J!(-91Y77i;&j zZzr#lN`-K5x{<^AH`HC`6*B^id8n$_i`9^veo)?z+w?`qRIjPZnQfsPwC;(ATJ=S2 z(RTj`k_7-<`HJ#dBR_?pk^UK-1V0l#UK6E+`XU$p)t^m~l_l`H=GEqfZ)x-3-Q z39>epb$9f=d?|)BT8wbwz4XhZ{g97uJkrYP`0Jd%$QX?G2Ljz_M>Ht~!`h)au-36Q zZ?(N?H5LO4)59ivrnhZhDxb+8X{Qwb9&Wm~mFSr`Lj?CGM7bE@n>5-E^T>PWKOSZs zm=hVUlrZ%|^48gPSW~-Kf-V>|p>>3OejwxU%bXg;8^^rHXe6S}kdlLD8kLwq?WHo4}|L-o&zV{n+o|k`zH~Yq2vqH|32~*K+19F1h3;bSwp8x!U1tqev zd7m-*yU&RHE7e#k$4yE+K7*bg%C^kscJVUp8S_3Cs~z2#dl8MeU62jk66r0O4FAf? zL2()n`Qj}0dk#eKSMb{pw+#z`sZhC~)o%UENsJou&cItG{$;g7+0&(-Y8HQ%!t3r{ z{=VgEAe)6^@`_Fw=;^MvE#gCm2U=cYl-F)wFh;4KnG)RjDDl#jL-Jd2rIln-R{meHfX$jcw+kDN{Eeei^NjIeFZ~uzQ(QQ_Ux^xVXoKc{Q$+c>u7pRrR6!< zBnp#&lxI6HanqA5h)ztrR`J=DvZi5!yYgxG%Wsp3>IPeAE-Q*2U`FnsHWUBRD)d`0 z`C8E+&!mSmw$_~w%omN+)qJKkW(QL0ahC3U2RrNC^6 zThw$Zy%c1l9Pu%#b)8ftGt*BPmujNnHpqui1$xo*))A{zovYYtZW(t*Elirz&BLDy{px^;&4;wYI`uRee-IA zblKtIn{Y(iUk6KLJDQG*4v8%ZS36l()hGDG6sL}a5MgofT5}uyJGecGMtXdleMvof zFZ3-&&ehHgk__*c*W(jPGlu$-9*7qwi*$dS_x{Nvn_|P;i4R5a)uy1W;s@u)eR9qP zZd5KB;Kvny!S+zuif}$0QZgI(s&{JskQ^g|=QqGgHuRmn)tbk>5Suv=o@01kzpK|f z-#&bL6%9KrqhysC3V~y{DImvdD%WEqL@wRVEu0L}Q`^&vCmFoE+@eqag+`B=Z^MiH zGyDTdqXn@15Q%(eCp;T5m7>+8y~tA{tNo&` zu0>D;>1XdAo!fPDAPsw=>4o_`RLJJT_(AA1?)u2ZU$1G4Bp)1+l{tf)ngL{EwMf5F zs8uQpQA++Twzo5EF4o&pUZ)}pA){i9fKqHl>r%&&lF1LCTY>*-&md+{$Gz(@W(%O!oS7!f4$Bhg#Z5_7KER0|DdHSDH#@9 z{GTec0cQXEDccv&;w@UKJJJvFspeY%zB33E6Za5|m#?kGKgWE{Jr|D}V?m}g5(h->=8ynitNA#p@L1uA+khu$oy2k?w!F(6diZJlLH8eX= z>lpz>gf*c^cG$|ZFLH7Dc(U0}utZl`r&PKJ(taCgHL!@nBZIu%vJuBk7IEA;>_ei) zSPrhKbFy1%{_4Pz{a2OdYfj+xMB2G$HLcj=sD|cHri5(Kvi)^5u2hX zBd##TFwC3iv}I!F?uC4)WIRoO121W1D8*U|&PsPY%$_rnpXPgl%}zFa@=Fsro?2oJi!b{+PbxRaUB8w- z_^;)P4b6*z6PZkw_!D?J&6?zV7R6|?a-nz4;t5ettct^vvc0{>AnP2anHd?q?0a6^O zxDK2Qyy~pb2u*|6U0BIV-hC@PfH0P`u`CQa4g6GM^BrXe%(a9QbmJKh8SQ)r?VhUR znkuOwfVVkzx9^2s*1Z15I0{ze(E#+uYb@8QfA0YY=4Ivg_`GKO>TPo11z>CTabszQ zkZvzw!2p1Os}{@Cy;H4f;-QFsuz+4zJMSOGh_HCtp6q4q&PZtXl@O9rU-lL@S#`iB zBWGU#)tczxQC_+LZ%iGw|Na3NXK^y%OTa|Z?8sk;K6g+>F;4*%tMTtUm1=`Cu?I`y zt849^;THR$7E%}=RhC9eM?GK$B{$`AvL`B>YFiEycACeKlL~s88%LARmmIUwoZ@** zD{D;=4G(atS)Mr5e3sI4f){CZ&kr zVc%v5>XC)sMnKr>cl=h~(A0OX(Kn=Cf#Blz*KJ5h%IOtpqt$L>>QZ&ppt z5>x1}B#c#;w@@+S!lBFlfSs{_KGI;}76m&0E=hj<3ip(dO7PgL+d85uZvX(m^Wus9 z?rB9~p&lruw3l_h-Wwbd$ErA#SVBsp=dSc=XFWTbv7)L7aLj+~))O9mO0yfNYeKdI zp(B;4R5!KZ7A7SVxPTa+Ab15!sj|#8^lKoZimG&KHkuX~4PU=gCx|x7e;t z`!-g_+`_3#hHvHlpt6#tYcq?rSoQw}i9h8a58o^{pWmOCw#wHW;J);{J*l0t`P7pL zVq=TPJ)KhWt6a$as<_~^J@@o0wGJmzD6)q+wC(ftv{cc?8!M+h#I>mVH=$weyzb1k z#m1GHb6FufoMO>X)R&+OYmsehjzi_?uf`$#vhPESSN24=A=`Qu(dHO0RSMry3*<1CBa3LUL#q#lI zHWuC#&ehcvUbKxmjhlw|x~8O(>x5F-c&o-YEI9#`2l;Hf+eMil z#<)3uA&*hR>NiV(+9&@tOTY;w&Kzm2l3;H6EcY!LW_7(puY1J5SJBz%%>T}!&Mj2F ziYrxhJ7j_gAzzDApk9oZI_bA>=2KCxwrMv=ql}{380nqPg2G`i$uos*>VBVu5>`tl zoYeX8%%4i}1!prco@;8$x2T~XYc9;NR9ywjcZ78`+-!7KU@3{MhIl6Lz4!BA!M|q# zoOZXS!Q7K;YxcVDfV*v4fnRLh0cVyHJZlW>kR5v9w%i*AgBNFO;_F+YNy$noPV@&` zTN)^`3wowc$d6LNSv3XNTl;R{ry1kF-&A2g_Mm83x#WM{bMithw_3&eJ^~a~mG#gB z-s?E#b;;7QYTzjqHt1yjRuN%eG6al>Kv7A-lo{yyAp z0|?#4QsP|y$TGPH;HVz=9nDEBn%~B@DmqQN575(MIqUA;yZ6Q^+IG8xK~#8=u~age zFZk=(HRJwbW!^uGxm!)n$qKHgI#X({v_Dd6K^ab5*bRpi^AypkH--tR1|PLeh9IoIW2RJTw#O>pJdas#66pEZV)!}D68<@H5K zD1^F)=3Bmg)xL>6gfLyJ9Rzj~b2N(>N5O zh*YI^WR7d$90&Ty-ggdbU;q$$7nSDwdPDoz#aqdqw6rO+ufWMb_Y)O;0ZKrBj^$aljm}p#Tv}Ba7pqsbaiV&iTNWIhY&P&alAv^+Jc6=|fvFPO?3N zvDyR+l_`IBClL%(&v4S7dfSx(`mvBWlA}GR;)uw6*%Vsp#Z>W;;4yYfb+|~1Cf6$h zDrCB~EjbWFm<{LP^onijEsIfywX{`i(a`42jH$Oh^#V+q{*8_{nzW0kMaBVl3Uj#E z{Q~BV-M5f&NY`5j#2ZWPNj@EB2`&|#5-z`1TmMN1+P*pax+=Z1pa(h$e- z{i}oV5ott;FBW|A%5d9`A8w^{^`s63Xk5eT`tf`6uM!2*cM9;WtckLEieKuTn+By; z+{i6cY$VK4ofev>Ngt5Q(Hi#8Hp`gjth>)++GE*f_j`NVOPAdI``4civzgNFWsIU7 z$DNAdLUYvLz>jb13obgz|MJjAbK5bgtIh3=m&Ul1IT+X1P}Q_@UdchJQzNr)jDke; z@e%FI&<4)Yb8T~W9O#k;XU?e-U+J-FXF@BR_B3f>>G%uq&U>|kvtmw6Nkz^wOP|#H zZ?AevJfQIMHFmbnN;J~uV-#Bo>`Ts!I7!p`YLC8WYtiIy)~~NxE2KYj4zMp-(u-`v zFdg+drfaa0xcbpx&z>ahpmv|7m!DQ0ibQT)OtFxp10C_BKV${UFN(zT4oZf$v_U~> zbVaNTf0C^1k0!Sf)$#^rCL(VKPg zde!y~+N@2D5}b*gUva&-mV+AuaF=2W$iF9K>>od=%nsR4-s5#j54a>VriE(d`=z*4 z^O^Fh;(D6q1NGMHXf2YCBsBW+XG?Z_mWWbRY4Sd_n+GbF0L1{N?4Fa(>r~0Lp4KeBw&Ly zTF9Ie#MVQ2VRCvhu`;~tc5-0^^~37z2(e-PQrEw`MQkg-*g0gk#)HT3Jvn_l-6#<1 zak12bt z{}LRQ<bWW|l6Be7QzepgWc-Q>2Bxi0Mphg-P^s zW;~|8!ocAKu70DdpS?4>QX)I%9L*M)D7{^JLhHNgw@}a99$Yq@Rd)_uk$2kQ2AP+XKu`YRcU3_J&;uo6I zr;m{HMJnW?gVY2a|8%^jS-?{4%)O#GM`zhp$fU5g@7rGDN3f0@UaeXgp3M_7kuxLz z+&OHW*K`!(oMoSC|K?v=rMfoO}7UI1v>QPAK1_=J-U4Xwm57S1FU6#h2g!0&b8t7G1c%7=;bx+PVDWvNuX7^ zjTg6sqF(#l>tLSci`$Ad|XCNS^U;0p=r6Z0D&0IZDegdcle;qa?Q~nBS zspND(6C%~hf31~QDCSBklrXgAKpW}3I%j@J={a7kf;Hy}FGIDnHM-S}I4|wypKy6L z!UdaepQWKs#(LQ%R~3p3+;2%_r-rG5a|}LPiin_yGf}r<)F)NiUuq4xrF;u;g>#$A z^j6Gwa1*z0P=#IE_}Xv{{fx)j1?0m|p-kLjB0;z6P|CDABIKsV>@(;yF)Q_!bnXyz zfVGRWGC4t_O6ySXko)JqY3WW=xMvFuCf`*O>(Hv!z)OVq~g z^)EkxIz1X3ers1?#uz=UYf5T4gRY~#w;Sz^Rr?OAgrXG+Cyi%jKa(Jypdb%JK{0Cx zzSc~`Oe2fF9%{irY|Ri{z`3;86n#)z))<_|zL=UioyhQg;W#5-YGH%nSw?1=N|`Kw zQE9NIG}nmSL|@CmX-S{lZ#ZCgSOGe$uwzj#%G~rRo0yyQM}V3w(iw5y4oVu|Z=pD; z+lwi{&?35Bs0ivSqbcO8LtOK2Fi= zSgqn(+9xlXrwagzgq!FY2`eV(YD|`o1r(QH8fyoov{$gZIr1Hp&}{Anf7^{v>&&0} zMxQDRy&ytyiU4n9kfq{bt*I+JsG5(K(NhX$&#C1qFp(mK;YID@MF!4A3gyqjO8M-*qQ3y;>hPE>ON7r zNT}42#%SDAum)Sr0^jYM@1>K4+0`E*8n*4tgC4^i!0otWERpL0(#~FYbcnQzM&sb9 zEgmz6SHNB?!N~JeZ0%~`Kw^39G6-g&i%=3igit+U$`5ivS2Y~_l-^sSepZ9}bS7QH z*h-eoj*r4TbN}%xa|Y{|jAAtEYE$8Zv|lAWuo*l=YwFMu57u}>QLt7lZU-9%e{KP?NImJ* z$T7;iRfpvIf~$+`Hk7AU>44!XPuQ|Ba(gv6k64nNmN%y%H`#S@5gkwYc-9QSTur07 z&*sBY@(iu337)OUThTvdR(n~uUJ;%0Vs%n^d|GUHH_EI?tAy}GfN+Hq< z6-USua$Kg=v~@~j3D&^$o$3#~rV#14iyih+#0^UHCHW>A;|ub{%6$?kb23EN-($zw z0EdYaVQndna^~@&3N)_7Gp8g)JblcDLkLKVbo)$s=lTXH|K>=S>JCijXN4|RQ-O=j zXcR}+&gAjFq6Sq!yIG&APE%khgVyP~cIeNmrKK&GB&#*fNyfo=&nL>Af_2}y(b7>y z?JkK2|63ldgsi~Rs&l^7Mz_Ajd^MReHf46~q)`@qWm?4|v4^R-Ba^yN+_DrVvI$)y z1@$2{1Tl+}Voa_F=APn!EX@brilf`LZ!z1NG%-YDxvzt{cUIHVY)83CN?$|FJZaJ1 z@*bRS=Sy?%0`N+qN+RC-$6Fn+Cc@`>T=yHpcA;L5%GSfo_9-GzVEW9JVH3QWgim7G z5@h0tt7BuTIN+;|S`e(;psTF=O!GClfD0VU+|p~3yi!tfwDH~gp3?QRtogf{N+bn~ zh+aB0mJq`9n~8Lh2$Fl#@B62)+Wk$EezZiEInx`4jPJ)iUpiD4jKRrw)!5ccM&XC zsvmHEMA1W~oH(8N+=edP9TZnjXF(gv(VUJ0)0Ew*!%wpqs4An?Me#VL@}X`u*HLGZ zn){-VHTo53p&&9Qp}kPW8N*Y$%!zKAx5D{ZpDyF!sq!RLQBWO5!%8Z$vh~e@Ixc&DX}gJ{am5Y)K2Wxh~;Ps0Kv^1f?IO2H+wDHVJN^AvVykWW%4hC1U2aUq&kBYJRpKpVv zXn?BVERysl$#%`~De&|Br|XE_BKed)S0#NeYBH%db$w6`nqsTHotY5%(zUAX1{FSJ zr1NH_l67~nvcV!I_A1Sr%83o?K%SO^*+sCwKK;^x!sbFxSFc&!rrj%hYJ9?W_&Y27 zI5ZKvHwqhQRmHOXZ#W#M{N*N0KI^e$JKh_I+e+BrA9tKWo48CS!ykiL;p?+#rPQve zSH@Pl9v#2D817v37t@S}%rVXwq6JdYL3aU6va*)0s-EOK&`SG$O~?Z4@YJ^Iy%Deb zl8a8^j~oTImN$QY^1cnJP2!#CSr&nIfkluFg((ZM5fN6q86D^ocnwGGc5HV&wDPa# zMAaDRLVW71)$VwiwP!Yz6)S_(4;w)%jImUP{adTq*aOgg(*}z?$&pwR-ZJVIqde@~ zmDmRbXu3Yi_UDTmn{0s_izX_5rl`rkNvrt0R&FaRJta%79ZmMN@IJmva3~ZYx7=bO zS!w&ve4mgnT!j%t$Imwn)`wl%ndm*E!DzPR;yI8qdY+tO#)$@{oC~Qs7D3-7(Dlrk z<3`)BO!4#mVOY(vV#D{qa?v+cjSu*_9@g<2!n+RP2BOSZ;mWGj!~^_eL>1#L7yL=< zG`sM`KxqutxGjP8hEzd6%o_*rMk~v{6q{A%wxg#bdP2n2cx)sf*U?tXg&d^SGd){o z6H%?ik@V2-lah9<(>59!<#u=7yGy&i2J%g*st@mfdmZ7G1Vg%VKY@IQwM4=UdR z7~Jr_+F28&#d9AR?kICJuZ%V=%@o$Ghx)>q*%;Kam7f6ZSjl4s35%GA?1Ke*%;|T# zq_JET`c6zJv&0IRo^3cN`yaBbyS&(wW{sSG+YUR?ilWkp`uJ>JEHZ zq$%*Q)8^QUc9<@!dG~>{r+?b|P@@5&4!v!2e+)w}|DM!*svM`H78Ej0`uN5gQggs| zGt!4L|8C|lk#qw)&J#-01i5MF4NLgV_~)IwC#x_`rk5&@+XQ7_{gti8ifd#|$u-l7 z(>FAZyM8kfTr2hc-e!H~6KxD#)~<{xSF`MqgEUUBBJwn4}UZ+N0Mq=i)80EPZ$7#r^- zCu_JiM=gwtK&dJw<9#2oH=H#MY2_9e2KN?yIP%MwBZrq97JO5z;NWfK@V!A3;X1&M zm}u(PDvP7$+Q57Rx(;r5!{@<8$V+^H7q8_U{dR)*@|JAxWJT1GcfS{tgJ0mDU^E=A zT}8E;D0)%?a(HHRTwOq%Y=YB0OOQ3Q`>_;)kSP*(tZ=~=@XXPIsOZF1GT)?=NiCh_ zMjW(S7aka{N8ap6n(SgOYV0mfNJA{Q%~tskKv>VBrZ;Ix)a8ghO(6%_=RjviJ<*dY zy|VPO%Z;p!OSo;QFIrTc?Z%18<19~!QJO1oE`RH;`StU!O1q~SX(J3i)XlaaHp`yh z=onH{(=#S9jEOMCL$#-iaa0m9re#G!eQTeOpR{cu1=4X?c zd=KY$j@T9>db8dN8T7bmAV&+v!+wlbEi3?2E#_4HsZhie5?oPs9dKGFsPMYqx7fLoAK7>vc75K<+-%jD7L7ih=- zEV24A^9lx|BD~%|k&#-IHzmpLWhbsYz~jy{7tG{0JA_Q_9QGb7Sbo5y zv32yiM#6kwu;?7s|K&TDjAYkJ-NLE(>mZKaDlJ5JDn2@PmSZ3qd+(@jIig#NlA(P^ zz-@n}GuLr0sl}Bszl>`7E*&b+Z9QLCh3}aBf{}4VD7_in?np(Tzy31UCNexRo^xnP z(7CIW3<1Yj)E4}AN2{)1F6uh3)7^AKZ2`}`*&b|``tq)wU_6Q5x ztfPz59amaYvv*1eS}m`z(Of=!l?rRCaU?1e?R()Jb4+^Zi2SCKH4&?2U(pR9pcHQ+ zTy}&aO(ZlnScn1V7UsAK9n%4plKXem#WSsu)kIh5P2G>{<*r~{0d8pCzBmuK;oW?(tezl5&Cp6DUR&+F-W`WoCB&9zzbF^}}@+++TJKoNeCp|8w1)=pq#HeY` zett9!q56$&>5yb=NPw)<15tk&jpPi7YUu8TJ09G)%R8T`C3(THy_o_JZEn!s2?+*v zc%~){_e&&pOe-p2j`FN5>%okHNb#LTjN4fAbJKp!((ld(RR1d6yQNdT_6bW zeV^Jaw&hMbI%F)e2#z{)c1*uM5wI7xJhi(8aojXYTvX2RtEaAMLpX!&`z*UO?w<*- zl9WFXhhK7YljLTkik(5v#Gw7R+qIO%IQ-~At2B$S(waoP3O1XZV+gGx^GlL;@P(Ja zpQ!XfNWPJb^;)*8#~;$}4`sa{c7ODjUd7?fd$&#u$Pg0S3loU7T^m1n=QjOU%O+;fe8@GspsEJfUyT1$58eHMB6K)`(;>L9-Q$%pO%r4hD#r zOy(nuhhW9d%jisooVZ}8fJTh4SE%N7bmQ$F&7|}%L%CxYXnii!>;n!_wxqA({Lq{F zeIChM+3pRJM$Goj>auo&i*KtUbo%btgbS#59dxX@l{)$!brmUbAF_A!0|Gnw4ts3BBHP8Qc|P;f+Er>nDba>u5UIodL+ z42K8r|F)-Hr$e7KeG^=5R?I(Nh7f+CF}CU00w2HK->mO4Io}{&$=<4M#}Go@(@Jkz z%j-VYiFf7`HPMGLnq3PQx~FK>(hM0;=d9ioRTk95w{xB%E#HMFV6h|S@T*b0BF|;YGz|t|A?7w6>>-n>(B6iQ| zDSh@@B}_Yu&EX-+wdVJR)7)2v8qKmVUdtd-lmi!2FzfR61 zser9UkOm`tHbyJm5QBO08$2t&>KzG-+&PnH_Rr}11y{pUKwb?@l}wSJax|7CyS2oC z>l1_hC4pWX{4a<_1}-r;!JaLzp{Cl^)BcFuV1;Y0p8J6~RrwY&G92R@a%*z6j`_@y zQjS<^BWZ@UjUTBGTotJ~3|xo9*9?6&HCkb3tEt@Tt-dogEA-1fu|we-a!+;yB`LQC z=1B&$E|Fbh*%)g|%{vWyC|O@LIokIa#4^${Kbnpu*Ipsk4&oM#yaJQ3aVg{oLW+#z z>CJj*m-|hxoHoA3;|~w#3<6S3^oF8UHC3mWqJOyt*veHQ!b}hZI9_hcc2dZV7BI5U z`ct?^PEVkDT-@=qxv`?N;%=#`b5Ua9cRFn9gvu8IqG&Pkmatk-$fFBnOEqZ=mzeqs5`~4NZYKt z&PFDRT(el0HpgW<0)+x*lY4yo?_;^#)8rc32Jvwu^mD#Atd$E%j^w2_UdFh4j;UHU zrWB-{f1dSI&bBO5VJ|@O4Bs?O!d-9V%z7}skGSY@YeVblt zGy@OT53t`iVOFk9$vjdTn{s7MwOj04cT)bXmDCrpC{-)N^Cw0LZ)riW@f06mC4yGv z%(~TbNSC7^#qrt(lTaX%wSDt9kNB;!zS(ym8-}|v-Y0s7|M|=#Xe$kw9nZ_>z!w@v z$-Vl#OBl~T@KVPeqvKiq^5m5#8*AQonTJBlEEDtfVhr1U5nXnCZmR_HcErWNXqOXCu4+12AI^R%Fi zyf;@+DC6n-5TvUy5wtl{tY<}oG3>K<<}!7{k#;k2IckZKf{m+cu>gNj^A27TJ}|gP zY?%>Bpzj2WE!K80Pz<4VF-f%Lz>B6OKU6QR?`uX2v`yH0b?%2~-@ReAH=Ey%WIh_C8lp7c*erWG{T+kfQN>@tysNe1KGhi=`ztVej@k3pfdAXO{fF=U4wU*_KpHOow7W4& z*?DcYfiv6db}V^IgWp`2)8T39uDcH8{F?R1GMtPJ-bGip>5T9H&H}7pYA-gp!g*o8 zHMt%zH=qpYH|!y{1i&7o&oG;Pcy(I1dn>%O>8W-R&r(BQj=A~w3GuAMorh)x{yzXu zK(N07H7*LQ3Ju2jb1vC4%n0^sWSU+o3(igD_mMY*8pg2UNGn#nyw4iH`_z`$uzYtG ziyfoPbeGq>sxxnI}ge9;%%MbinrJd)l^+tblgo!hP($g=1{ z#Y1H-9DhLKgonH>zl6c>wG1k}#@beS?D?FA()L6&XH3MbeHB6P?_)lGJN8zeN$4|; zkil!&r}zw&!W!oEYsZn@f5?eHi>=2z5-YCJW4;a!hg`55-n;YW^+$Lt2ZiXq;|I*MECZo^gWhcAXgAWDr}wzN7GPQ~W#q zKRRxTNAq+I#f_oFM-|Z|wJT2jp7Z4Tdz#&8 zg3q=Kd~WBD>((1=TvI~rx(C>N+(7Wi=h&w1)bn`b2Eb z;y}yIJiZW4opu6Io!{|p=4O%~x2Ib`4Acv$kU;4$Y6?u$bd4t+bGuh*l%H2WHJdS-y>S+&-HTEYi@HTh0cSK|9R|c5c zW6=II`!X}Io~+8n#}nvOa){NT$1vQZ!+}2jI1QXhyuUBomkcA>G@ZMyuLv;dif6DR zf0_?Kt*;T=NBWSeA5F=l8I)~X$^G`l!b}&DlV?(O14q_h4IICWR4~a5C+S?u|OSr8UP* zy*Fzdx|3uO#p~h1lptjuhkfKr*K%5JwqRRL1H;DlpvjqAtT*0|qwgb<(}USna*CXV zDemSvi@xj=S+*vY#>MPEvc|6(f@@SpT<@um45z zGOwEb-hs@y){I`p&xyWRjqc)HN{>Dy?A$IGyYR%MoT~d1Fliov#&RcTWVP8(|id4c54x&}f5!Q88W2@Fvd<^w@ zzQ~^zfhV~Y+CWg+Oj_S+Lc)ojIFD$D*;fy`%~B$v=To9S_T%1~KoWxI5-MXc8~Tf_ zN5=48!yGqD9kQF>=Dpq`hC2DswfPw;8@%~&*qDCHRj@pFf^G9%d28Xuimoy2)4$Bg zFF&~#s?W~Sewst}NhN_x zs}JzlXFlp>W2s3ir{|XvxU7lOgKCl{EamBRV|FObVSe%;#>T$G*W)u~CL>7QzLK?@ zPqM)1GV4F2Vz4(BtuyPG+S81`8~(8IdKML%o}u}l7rSn~fz~F7tv6Pe74Z%DO88z4 zX75-)C_^C}?s9Z_-nhOliKF#2O;WRTIOvi8G z#O<}Etk#hOrD6ChJf?YgJh3mXQ!-*O8AiJZSk?=-{fAkz=`?5k)44xX2lqqzJZrO* zBMql;IkAuhTcf#n%Yn~p=F_FiW=6(Fq88SluNB_pDf<)i-~QB8(OUA zaPSd~8^iei$qvQ($9S$NWUAgb(k92TGSi4Qg9bBe$TCKjMRNPxZjODw%1ZZ992|R; z=W`cwnl`m*G4n=1|_<#L>?Z8 z&Zmw1cX%-GN;l$>uTAW;u`E)t#>%c4U(;Nu9y6FOYeupzxs-18n!F3@%$Cw}wqMgG z^UrGfhU;+XVlMYWf~hn1W^?&-S~_&*l1B}1f9&K*`KYU+b)bD%0Cp{IGGX{uHqG`&&qkGXDKY$guFld<_vk$$h<0_6+%%7*v1BTh zwR$XXp+s8Od|D3B=hvlj>W}Ag@X{`lI_5F)SsjUOmy^e+(-MhIe5ieV5@9PaO+h#>|Mv6xIDgB2UC8agrhokJSlC#O+8bzzaHgjnl-vR zvWPwKhugk=`B|qx)`0(*+q{x0eRb{+x{u03LuSnMqv3V|P9{BR?h(yf+E8yDcb`x$t-Q8{=7OW8}iv?@{+Q5_9WkfVBe=~=+Ho; znm*>qu{?a$4Q(r1_E_8DSZq(>jecBd8Nr6FV<|2ffmd=9YFj_2yXQY*s+?JB5{qVX z54QT4GCom}k8^ym4pC%9o)O)%uQBnZ3$2uOn0c}_8ycZg{%*SFSa7eWE9)Ko`O)hU zovgc||H_~K%d40kk;eV$zp(UCq;q~EbsD|-`o|K3w7p!)Z^sPVEfgNn<-@KPwCfN{ z{heCuk3Xik`E|lPPVuN_1N%a|p&{lcI z$ubvKCWi6xUpZE;^O+H%z~lW#2uSKci#Q8*ZCS-fw~knk8pTruRkR{ZC=o^6n0wwRcfo_OxR)Gx&Oe= z^#JLfXQ(a6+>#WL`}V9uE6UT18I|IIA;b z^FGpaX7l}eSL#C-VVJE-(+i0_OFl>7Hg$R?MYH-)IgYara<)!JR5l@eMr)o;*v+giOE5jNh38kMk&_j{+RT|4 zj=eyUdLFKR_h?#hfG5Z15EETYT=*$GTTH|J+&A6~N#*0P7WDl$lMRZ(t@UmAm0QBw z(g50@G{)x7XJQ{KvE`T(BQmye;$;`6F3&(yr5`Tkr*JLV!}Si9ygl-nFC{nG>gB@+ zr7&6sKH{*#cMMX}&@@jXqn|$tAKlR$qs=sZ!6D-ZO6_X+_vi&%ay^;Uqn`JhzM+sZ znTTG`aCPW}Mx-+>V@!Gfpb2km`*JYyE#|YIv+KZq4v)zq{rW^!Y|usd)@yzbO`yBw zT(-GH@%UE^rah0+rHL+somybL&IgUY!*H;8OrPS%?2a_%N=7G2rcw_k8&2#%511 z)ONnd)6J2_F0s6d8;04+j>K$r;>yi*Znr)~uf7Lpg)b(PJh30T5%u@i(L#+DsV%tL zq&0eORU9qsMAd>@jCyesi{5&8=gBeUCn&15B|E!e6O$ELqE*@XPlZXOzLX z@i($78OP`h3O;|sw`?3*N9Ho?*>N;KO=iIM37oj!lHbM(TzRvF(+W-SP`HS7j6F*y z#Iw%l2}72)roW{z+YRc-Ycre&7bmdzjV8HQh7ok)9};fPcnS4pOcAVnF^i% zx>El89Osi`NSUO{gil_~FYb*(Z2>oIN3i&uJx%PNa>?ozIXP?i>AZ}%r8V3vc}%BD zXBMw*PV>MbTuLmMQtr;l|7uCG&E?y&=A0haK>N%M46j^|a+@J^P)uaw?wwRF{=&?m znxxE~Lr6j=Jl>CGr4qF2a+%8QB{V5pPN?@q9HL2uAgf1$kdYs!)$)qQjsB;-he(GleXIhZAw=M4q7cptK4)^*j zru0)aUoPz9jhZms;s{CWAJNdNHIs}+v+<%5ajUiwea?D5nle?xV#0@2-^c>H<- zT^=N|#!iW)XF?cJT94NmIP$U&BOXm-`!)?Wq#XXgA8DwY=0RB4Y6@)P@%;%7Z7<+- z*N2_9zFarXLCf5Z87(VF%=DsV^9J58ZcS@5V^s5k7;NrB;f0CZ{P2d1NjFH^)P!|$ zqlnL%#6&}1YWD|_^s^<7E*s2QfA*s6#w||y?Lo)?8`B@; zGPPApV*h9}eC|)qom-8+{ScJa>vQSHc>H>s@vr3;TGn2`I`brBH|x_SHUoRl1ZFr# zu-08zGhzseP3qB4X+v^e6e_JUP~IXJ$d%tmv-$2vGLm) zY&t9_z49Whl*}X#wm~Ib3+K4dptQkK#7IMNrb($!r65I$A+rW%fOLhwx3Ql&=j26JO)u^^yusBB|7t}P>=oflQHP7iD5=0br-8p-hG>u zO7D40UC?2Jk{v0fi9OCb9HL@Ib zV0%23&Oz}^**cciKRVF%*DMZp^yJcugUo3;lCn=Zd|cthl;+_K=rfA}nMO>Tx|A8C zlJHKhVrj!B=J{1I;_w)(r&ywTs1dt4qu4Sgh!G8=S!FtqNz2yIYxZW2Ma45IG6FXB zX1?+i9AZN0Jkpb!Gy2fBsUymB=Q8f~cXoGsM|H(f_RrbIwAutbI^~fv?J6gdp0i}@ zaxzYwBWQCCV+*?W6yH5?Pg+M+~@R& z6wcIrBx&DlzBlHxVa9))D<~%-*o`Ol?a3ZA7v%*rIUKT#jk_PA=y8@l@17BJ@F*37 zpA(m>&EDt?-Y@j!*k(88Z;N8^fk8YD-N{;m_AFU3ox~>5);q8L1V4g;^MuzOz}dv~p-^zKtk zTWp|{jVktk3|UdzK=rYSm|bzlp^Y-b+6<%L?DiZU{Ffu@-N`?7kDGHfxU6c4!}^|I+4SoW8U|WC$(%yu4+VUNXJES}m~qS3^G|IhzCEv!IWCvS zM?47l{T{b>y%{{|2c3k3G`wpy5j^p}>Y;M<$rFL~S)#azS{c;G~ zEkEF9bdnzP*0AD+B}ywhQL^a^KkpRqtK~lW&7R7GgKkXT?n~kY9WH&+Vb(=e)XtSq z=>HDA0i%d+4Cc*_`z-o4hd^BWejxHcG&y7chOZgq;N79UQOrNO3_JM=> zHufVX`s29e*@@)e=eQm)pVsY%v8&OVr(U0EWp6-I-*hI{PDIho5)=C=G)#>puiZ-~ zYTjmy&Ni-V49893HXh;aY5QvjgR&dw7uAb4i%t0x+Kh(MMiLjQQmLvyaf=)pEAr^? zn2pP>8N6;E$JqKZnk`7@Sz{i3Gv08&X&Lvk-3S@oj8k)~S-Ux(lDK@jnVIvU*BT0+ zTq7ednE4moDOqaDeh)Wndap$5&Sms$SE5?0$d(*sBCKAcKRA>oHa=9pYQ?^vRh)d< zNRP1#IbNE}hgtbpIkjhYo&)p3stDa!g07AsI|ACW;Lag7H#Nro;{dGcJCR$Szy^aF zMw-0E|41Rtk9(5(&XWAjzwy*7x9R*Gwv@+R9MC^)!gX6`>~62%gGXz6-7+UAWI7#RR$$s}G2WG5;?ZN`13lNo&B=tQ&hpIYe_^L8;a$EIUH82qJHg83b$ON#qT=aPQFQp zBbMaeu;S*bS=wy?^M`h(!_*kAwY^Emo^?3w zTuq8&2YSbrQ(2Nj*|zTNP`gF0aZj%QawW0-HWHF;X%lvYJ;!orulSGrEs?ZN%cLyX zlWUn(?9H1^#Mo*&F8RQV;6^6y{6n*g{}EGB$|t4EeE+zGD<=}T*ts|QM>VLo=uES; znJ6_(=1A>%9Lp4$GBug9vwg8HspiD0GaQ?=fvADTY&5K8@4mxK+IkJWI$QSp-Joet zHKLee^Hz*e#+0V$=v$sfvUoGjxMp_!oGQ|RNctq z;3`Ts4y{kRvSv@fJdSjJLc+uYRQf1$QLEqnPcbTxjHm~Q_noudai+SdOA2P zA3^xYt{4w5WM^(JLH^@8eWsSHqoW9R_=NJFMSQpbT>`wZJCaA!O5SJvp|v2 z)`zboZ}cF{ZqC3`>l7}fH<&+a21+{&7^T@7)o?$axtr5}i3>(`W0+_2k;<>jnXj-P z*X8rb>G~Rl9+|9mQ$uA;6+KTh1sx+?TKrvAA0W1-`fVYIcgC z?G+d=|Br;$<}|2`M%`y1H@D|d{OC8QKfI*L;`Xe+&>i*q37i>|%B2y>yndF8*YT_L z^*D{n?-E)q9*c6g9@dT})J3VXZ}TTg-syAgpc6Z;yr%y+i~suzQm#n~O;<#4cb+!w zUEcB|ypD|8v3R8p$KR?8uZHa7VZR1ODHo&m_7euq_WZt@hhO4Cnynwox;X_rI(37B z;@6x_v?q1c6t0B8;&1mboNUOnJCUr_c*fBwYq{0z4T}=Iu}Qj2oMk3&-_BtF3pJt= zZV}L7KEpdTCAY_CzI<^eYuj`z-Hh?N7sPBGO`1#?f~&e46ZYhjJpLZ@wBE5qWg&jG ziR5>D#K?~G7~-2w#s?*~)m)&t!3`?z8T zPR09^jO#j#g|TJKi?w8K)JpO+N6@0+Ih#L+5I6N39p?Qec7(tQn)Ej8>NwwME5J}|gP8rEBFaSyE_K6@m6S{&wuOE@+?%z5u^&XBdu z_@^35Xw_WRD z2N$SN@nSuv@}F}tavrvOZHd-jM4^`xsjZ5!>7_)PWiHLS1hKM3CJISyDfMt*ZEgU0 z|B6X_nN7bhnM@ho45eHDxNtlZlfnM!6)rM)wQW^gI87z=l$?*EMH~!a|bu zKB1!9z)-gdWLg%{M zLvhal4t*ZWis%b`*6V`F^amKW>rGymP&ld4mY@q5kjn*(qNsNi<(OXkPE<7$t5 zVggqZoDo7udmXHGPtxX^Kj-8oP6cWxcCq4=lMa!`ZqTjrJHIE7!A`q9e}liFR&ADh>9w?qCXeM2Bt-Q9N9y`>{M|hq z^^#a3PtY7@OdaEyGwTccopz8@dI05U7dBtK!IbAq7!b3Yii(YdZ&Rl%WD5t<_j0JO z37)+^)B98j&r~eA*X;{)+e~2D>kc^If5q2}{-pdXr+J^{;nQ3i zw2n<3o!OzRhuhi(3~C)izpzAVl9qGm{XcvTSP+={lm^Er`n6xdiNJc^KWxG2A!&Rp zorc4v{pfUz;J~IJLOu*;`o0NF{o{#ggc+Kur34(g%gwa$tXx%0_|QN~)zm549mO+` z_c-m!r1O%QjG1$X4b4g^c6`rz&4oDKtE2qz0lczH>A0#lTaMhsdBZH0SUJ#8XUn{Q zsua$Bi(XwTZt0qk=Dwc9o#9MQETLSp6@voq z%imcOQhXSvM;ZLg@?n1H814?u<<{adEOo4Sw?voEE0lP;Xcl*W#W2UHl0%m@u+Kk= z)q=H{4iIdv55#ZrKL&rb$1uGGMH$m+3vIuLj@wNYd9V?kSq3$m~=Tt56^0}{VS+>aU0zQ%`hAKfY7odvlPdR|7~ z=GtsC1~qze{o_bR?De9@kFP}K45UN56w=T9pujW`Gd(L3UOgdct}mCo+B3Iu7A`L8 z{Oz>{s~_KR_;HY|14-;?wFI|}yL`4E$-DUByx6=Q?Ww~FG&)aQpH8eSTFTRJ3yHT{ z2qyKk%j?IoToZzf0#R}D;QZ?#dkvaGX`GOZYFND{DtPqA;YCxSkq3{|MYqV*mgk07*naR6c@iFUY@ML}BPSrj4G% z)BOFIOl9}#E(bL2eDt~ z#JY}2%_)_LRUB{Wki7UY0egBg|EUs5mgzK^QAf7% z9Gc(W!pZ7~)Gn~YA$}_cu?~!P@#XOM`6QKWVo!^Be3Cj+G=2%MCOdL% z5pf|GsAzSMROK29o`%!IQ=QUt7O4FA!uUB8uuTu6e&lUT2Tfx9q%Y)-%V11VUuIex z;@-9&d)ldUYe*E^8-_9{v4SPtAMv;CeL5WJO@8bX^qVEI#l{>*+a+A;p~diNM`#yk z&J(>?tT5AJP`6l~yVsJr$qH-Fwp<$cnbq3HRCa#=CHbtF`zN`B9jAFd?ttEboPxx6cH&zxRwursOQ$iu_fSjV7IKbDjS0kk!+Wz?*1|G%Za z75-n*1!X`5PzBTg4WJFs4$uMgfKEVHz!>NU34f;6XfO9OPs+eQ!)0|{fH-Z;6ScOF z<>$2jWX!U;QZ#;>eCrr2np)Ztuzr=CXgnu13$#Qd_PV4j-YV(8Eu~GLv-0n@wT#yK zEtmTql#SnFU zn?GItv)m+F@vkNR;UjVXwN3urjTW7JYpMCHCyH~f%BR=wQo+z|bGi&AL&p??xu(dR7S|~lP2Fu5eF4Dfk1=*$YK~neUNZAZkc{90MY^KzR z%DhW*f4QC9D_A4vKX(vovyO5vp5btIEW&h)8(sRmj zX&SOh9y%WupCz@@G$ly3j`$`SdS9iFm5F#;rOILJ4dO7dOct+H6+4FuvdpJQtemHb zZgQ|(SUydZ8ivU5&gD{WbWiN-n}|nBE2;51E2Bfyq<#BeVmqx){*AaLO?sF}_Jl6- zc-%r6vZ`8cDH}@qX#;7Z(m_rnN6Vj(pK>R*g^Z6cmUZI;r2oOC^7M2cvENxOl@IO3 zBBHCTzIRNnmK>MiWq-uAV7-i*>nb1SjF#Rlou$MsMbZOOByd239cC7+)~#SrO>z1a_l;J zw)BeJIiw?}?!A%|MQx-*agc=h?vTM#=1H?m9XaIuNRkiKOI}`>eEj-Lnwp)LWpnq) zq!FVfXr!TJO*|tV@BNg;b0XxQbAv2QI3uYRQDWTuhnOy#F0Mnr$xBCLF>AY62Gym> z!i$|m<>eyD+NUUMihs$n(jijQbD;dyRuhHpk7VBX&(iem9I=TGl(VkMGRxXscI)cN z$;r;rq4Q{&GVZH%np`h0JDbRvvsw~ZG)q3+vydM5oaK|<6zQ_9UM#jgl9b*2u#r^mf8Bz9J zKL0mcI%@eyPNlsp+W125HVhGij4as_Df0I~t*ER>lRLU)qTW$o?t6R{Grb;?+VZk& z3R4orZf^4C*$L@2$V1d$Xv?t+wNhIBTFT=_Nc_Z;GO3%l*uH2je*FvNd0dpF7(J0h zuLN;x=PUoccaVpl_R8y|NV#PCRt`EIl-~QwW#F%PnfTOP#ymVDgU4IR&|??HW%4IE zQ&u2V)7nVFj|f@rTp_tr!{xQk581hVw&Z2_%AC_f#AjBJeC*Icil$$c?`!Jh^x+tp zd1j@!9eyT>^Lt2D^Bc0?_oJws@|T&eN5%HwFi~GVMSiN8$j8o3vgMVhEOPrLdF!6b z&Ti+WjkAT^3=5OOVb^6*|9lztagC@MK9ua(qcZ(ku*~sHmNO2KqEnhL2HRX^$A5{U z(OFT77iG#sr=wCEtt-~SIx_n}j66%zlGz4>#j46ie4Y-LbCYAlOTVd1*V!N?A1_FD zzMYsl4wj&n+r-=Dsd!#V6Q51b<$IgyqWbQdw4HoZ?j7`$XKBBrVzRz;S*9h)+O={b z?~hC@`67zhiPAywpwzzfmdyrPVsahXyX%K++FUI<6<5U8poat;>LA0r$I6*ZcbRoQ zL?)b!(tNr|L;JC^sBX9%EgdNPGr!6Hj*7A=D^E`Ko+++@k0mm9 zv)npaES2YVrSWJlNsinj7vt7RNB3$O-r}rGJi1=uDt?HXTYI^Ad76yQ2@*fWBhvr3 zt@!!%71P%F5^%hYJX}0RI?f0Xg)0FP7aS&9sTMMRLV%2%oGVeiPf0`eJ-NBVP)u`A zONxi1e0mikSI)PV7RJVss9i0C)FMSI@Tsh-c_C_+5%O!n5NUh1vrGq-fB1AcGTBu69=|VP!+**j`6?HCZj@d%N#bF6NpuS5$cdv(L~IUmuPH+vINCMwyy+TQ~HzY>w*FLwF=B!Ac)N$lY!f0|8_KIa`JuArUFn58dv>sLu*u&x~5 zGfNboyb#~Kqhb)TUl!{fl~-rXL}8evESow>rrv!izbre7^NFU?XTOb%cyvaVJX$7B z6&Iy-NzSdSmM&dyN_K#aeA+xy##nBX+keZ%Y9WYXXj5^=y@y!%?o2{9DSmB&TfJW_N6-pY_f1?jxtrd*285r<1I;xx`tt`GVo zIc9}ouWTf_?b=B5UhCw!%^As#93%Gm$}%_ixM)|rk)C~Hq*C*V6t})C2UOj~DalfL zbxM&*+55z^V4$S6_m#I!=F+Y4g3MifQGWGNlPd}}^5E-hX?<;|+{=6;m)-YBn}P?D z7oI00TaJ@c*M5nzovS=dy)Cl>f@DHrXGwk@B;P*Fl*bd_iHXuJSz|a`{ykqSJC7mP z?CmA|d0+XelOa#N$BN&vVmZ;gyG*}oBU;7Pa{Sc}8P>!|Ub-a7J(Xb+)oZ&HOc)>v z!%f60e{Sjx}RUzMLVcjT(#S&8{kAa6g|$##uk(Xcx%E7QJXON-18fzvCw;holCksTj^plGQ zeo|0)T#~HY%EEaYMYa26nXZ{Chbw&~_I0&fbSoC%>HjP;@P9Tj{-0Gk{m(8s|FcY+ z|Jg?Ef7Vg?pM9GB&q4}7)Bn#ec2Dcn331lKuS;)wHR;ItCUry<1>k?(m!nVJxVz>) z#q+PSH}M%x2bSYvbDjx`ZkW4NVz=`dI|iq7+)0&2$zWy06fX1{M2qy7{O5ZNtIJFFYc_5D!% z+Jc;8I#k!xlRIi5?M9R{R-rF1M)qgiw z_h4{cIuu7R z;CK^E%M$qUY$~}^!_yLCOtk5=PuL?V->JZ0_fzx*+o#|KabA0$jd$puguL!7~!+wJaW&rPBzr{EphrI8FsLW!3c~_{R-nxw_kpIJaph2jYB5fBBNG#ZNKql+6qMcl2*Ov+CCZQ z%6x{ZoxwXZ6DzB=Y@OMTHAe%;Rn}vkY7x#`AF=G{A-p$)q4@a~VZB~*`DiXV`;{@g zHV7A^c6>j-kzI|p)YL>^-lh=^H+Nc$8AAW9S+E@&Ohu;mbo8ki)$DxO7sbE zAR|PD)GslsA~ce|+)rjpWx>J=hycSi*Dub7`b| z@m4+`F(AFl5QT$RSv^0O{~|Xr_}?uSc;Dpe{4a!+RWQ9zEj3|VC_1zS_24HIJXggm z^e#FrGpL(5hF&p0ShDjz9TQ&DROvH?ndfL*o=ETPEtFYC@pOhet8TQSN4GaT?vsM$ z%Z;?zr;e#>INx1{uqeAJDdQC|EuKTc-|swj*oSGTCUU+vnDu2)!(p0vl~Z&so`Kyh zNoi$7AJuxA8ovK7ogx3p=eh@n&HpcGm+r zTmP6$!-1kM#Y%mMfEnk>!#@@Rlfsf4su6(hnre(csswB+j-v&i2-uSuSF| zvp$~=r*5-!>LC0-zh>vIkvzAqChd3yyVi}T*hPbW<~a%Zwl?^V5IkjtK;5s#Q7lRy{>bAm-u{f z|FIFznG4z9{RA0vZZl)eAvR9CL;7NMN{-Iu@9=f3Y`qY>*cZG~Q)JqqFg8qyX1-%C z;h%ou)cFd-{C6>PUN00zoWbF$H7(u+bN9XnZ`LfJVD1;5etAdNz+tFVj$^lCAnX5Y z%A;B4_>N8I@kw)5j49;$sXEeh9--g(086i6{LOUP*rf@%yYi7X&#|BX9n;$y^xjlN z+|w`Ae_4Y~GZ%)A>`C?IZ;UfGBPKa}Md#SP<^ok4t0}B{igkHcwm*qQrpL19@eD?J$FbmhD8oWM2-B&@B=-VZrB2A} zJ=itzB<@asNbif6_7B2*-hlB&v?i}Z?cid5lr(TP5YqRmaO=Az&rMd)-Cz{u;hQ;= z-AMYfqpTYe#{FbPw!B`>NZZ4>{wv2u`wM3)HgoWPGB>QcaO>YvR8Ma}S{vT%EtSbZuaJ$_}B$1?2ku;zrR8lHU5<*g;BqYBK zQOJ}bBq3BP6-5d~q=}L!8buS5gpwgmNJ@k1+?=a(&b!Xd|6S|7cs|6l_S(B=18$?v zu+Zuq8 z)=TL+vXaBQUg8y}ir@YTR2Z2OCZx-rsuEVcRHy6x74FSCfra-cp5IR7=8)CQ*tiSd zA=?qVsmaXG$yn=eVxEK_9nqqU68y<(84qGgLfO!4i^O1(fv6N-VB3EsG(5b^H^D(1j{k#ovmZ%Q zHgS8LInAGU(f67zfnQfJBym1Ssr5#n2+KH_1LwA>b;{rP5kx5v?W))6hKbtE`9 zu(c(E_HEyZSZYX$|313B)-uK99x4_hNG`fSN2M)UuBrGt^f9Hf8^>Ea*zxEQE^3x^ zeGkI!pgFHpHsUonj;>r2uB5l)Gfj#iiV6HU8^Vrq9on0Q;?U=SjCLzxdG>tEEySbZ zAj?b6(fG)Pu`W`q$O>g-o&rl8vPj=nPuIh<6n9W2$f*)xf%r2V8{>VG*?mmKE-bj*c&JdJgiH3CuV+C?hb9hF1x*km; zFG(HqmxIrmh#g_iswthgb{f;~(nVrsCdMy2h+Q3q@yKQzmbx?)!cEI6$=iN#9m=o)0j2NyX{LLWhjD&+DR!X<8J*%G!Hc)JS+k0rdo;)M^)%;Z7}E8~iXZ#Jm^<8+jVEHzFf&IcLX1B1E++gcMKbp&yAIE1mW?7W zo`^Eo-;FQMOGqs=Vw`|8Dk&~Rj_>DwrwR7u@fe!7P-oahUQIl;VP<40e`U+-4g3>p zp<%)_jNRTKb9**s8LhbFEu|_fo@aVyg#4X{aDpC#*B>MGxjd={`7{@g#nx>QdB5J0 z)7ePOyO&(;`NEX=@rbUG;ccKW%U^3_wBafHU$$`gi6srQf8rVNoQ2cp@oh&n+56ow z`tXgv>9$l}-o?I+k*r>Il17zYQYH&r zUvs`p9Pu@t>=g5@%6f^8-nn@jP2A!QqwL zFvwP+=khy3w;siB_ZKuS?%>C553=42@Y(z;`Jr~$7!T${wm3hZS5Xui$@4!e(HK36 zwSoFf*%`}&(po-Q$)RXs!m~eP_(xry(@5ElDf3wZ4uYR#!$1sf$`HX;xMuZzm+2xy!sz4;Q}mK zR6u6PT#C+kpf#eMz!64Be7wV%fFE3v{>)23Ydpp!5tXAs&(FQA6w9=fPvD?^ zES{3*kcvLSaG4GkXq+eIoCH(XnezH`3KP_>VlgM5XC28rm0m>P+~Jrs3J^1CAwdxj z(EZuNsta$}BJz|1KQU~Ay~r6QLQH-Ft&$_T;%!Rno(js&88YaKF3lTeF)+7>D|_a$ zs5^%%FN|3hZbaLB^;MuiQHy;7MovS?2EIU zUSCPpdjqb0{DajgUj*e<8NSSpud{qvrMZLDMK75)d@zEGy?G?$j9=k#8Z?dg7Px_M zGYPJ@tMKcTH!4;F_}r>te9wCn^8{#{IgAC!Flxp^>Kg5c9?}Tq3lOxQ!pp8Hm>69` z`QIUy=2!AIa4|JoHqtbBEAM4S($y$N-x({f@u8?4i~&ZZ|XPf2R@PUXddxKdTd`egSmeeGRVpeVVzlwt$j+ZhBKL~ z?vXI(1})3?keR-a&k8CCguAf%j2F?HJDKxMABp;5{7En)OU#))_wFK9)4k6^TJN={+II zXrJ9g+v?*uuMxf0TuMVFxbUEX`?4k6_q1SMWi_Wd-cypgo2u6W2=Djieaa!`y58bS zx({=7!x`M*&zQW+c&zp$p|qX^kpSk1`tYc^nPI+5SepP)K(D_Iu2X*^lXV-X_|LRQ ztYoH$3#YtXF$x*Qwsbiv?rE^1;t9o1j5t`C#G`Iy4leU&-|sDm;h_PIBFV_o9xga)^Lzz)@+g#!D8dD~QY@kReh>^9%jP5cZ>hxm{zKmnc z%1|xPL88K#Ktv)v4@lg2!JfJ+9O*t5!nahn2g ziPoZ1;4q7ZDPX4k9y^U6Soc3cSpOxLo{mNTr5Qfj_s~>#XLsfyeyuEGO#3$Kzv|Jl zCXEND|07=QHU8!Au{ssSotzEGU+`kuwQ7ubuVU5#`4Ew z85s_TSrBGR-qJ2^hsm%^Oap@*^}OxV;6Q*GYx{Pvt?)1_kE=5+DVr1H1eo!nf$Ert z)D4}_AHzLto~ezlX()%}?J=J>itC!knCVzYhub!;`wwD>zW}?H7m@PgD5oBbMMd-{ zlZR=bqFqUBPzBl3$$mFFkB>=Tahmj_H!~DadQ5{6;#G_pXrnYX+k3$LTb5L0NGjFOOH!+&UVyvv;wJDL^*O z7dKA_eugMAKh+cE#{KLKH|OH>XpS~?kaTJ*19t<^iX1^~#VQ8$UXxfJOZnPOJQY-7 z{}L6xdn!>nVK){t_R?Zvj>ed0T#J+Ey+R4={(FH(=V6w07V`rthy!IAS;R!frb#rc$Hk)dvvRFg`<>t+p7<*Fvc@7sJB;q+_ z83soma&omiTk1BEV;9I|v3HDeyT<6Jp3Jp&!8ddV??-K5RQ+1kjCA4ZP+<;5sKW6k zVkazNPI4B#1{L^3AK}|LC)TOj6X+R|q{5>`?ab(}GE60~g%VP9E3n_};=t@1M? z7tO|R|7_%^q>*;LneToh@f!UAov=1yl}>TTp_x|qq3k`-ilc)d#cQj1^v9FO8>@L| zSwpGvUy^UW!eh!GI+wK&IxwGuHX^vKS7WZ;3r>DI#>uBQi98&}{?oV?c3ZTcR$At!W$ zuZc?$ytE(BJ_CyK_n|nhi$p(R#`bqXM;7i1=17d&#)BE#dExPald_jExN-sw>$g;CU3J<9wa&3yllJ4os?RM~V@lsF}A>`!K4W8cf)^6#Ww_C-6u zfNOVC_*S-qa}yePF)NA51E;BuRKWHAE6yyHMgC4YwnZlh?*7ZD)RQ=*PvdoYD#>e# zX*FDnQo%+P-js32HJ3Fd7qFyN#yK0h3uF<=1zRd@*p|t z-aWyzO`ZR~igM-eM0CdZvgTkS56e54^FobDhyU^6{yo~qWZ~u{L1CsMyK0YeH+((i zT1Oc9-%N%aal%5`4CVDbxE`%Rcb@|7kFL>ek;`gSt*zEe^0_GWs52dTYlQCsB8*3TBioXg~f*)j5ZGWjQ+O@K&0@_jpy zUU>tF);;(XMq&PO9nRxKDAo>U#TQ`?Cx68H)hc!u4x%{rJvI;jQ5JlJ!Iw*zwPzy3 z=KV&qBZQ2yPYiq?N%*<~`X6bsqtu?wu^j}*?cn9CyCe^aLOwnnmH*<<$q7N(QVvCt zFm%S&z={1dynBspRut%XaA8h5olO^zPSYfRXaTztuajHiPEv;-HS;!b@5=&ux=ZNa zn!|y%BD_++6Dc{4lII%SD|yBC6gy^Qi!wvp8i`Yf=-w^CI{jAMTHWvoisf9$YP#Qv z)3@pY!^84$akeA-)>#HT-eD`0!tm!edDGmDTD%D3u34~h#WZ?tDj`{pfGhr#IN31z zqy?EiJ9!f+%t!eQvKFmJu{H|FPs@2P;!KWr3;&vAh?)_>phG4+TJeUuk?qLLyTqH~ z+uWGBlwH#MX%)3Zefus#lAAHG_e0^BH_{!FOuf4qc?(tA|LLOtBoxmzA(Z4faraFa zt6R5W`{o>h7V?bDi^5VUoVaU4Xx!t8!_jymgbj(yn~zyRFlo`p5g74-DH2-|3u@>5 z#0fOH4kV;e^ilUZuo^OkrQ92;NRFVA*-Lr>>rlau6 zy~6aR(!}b0<7h)P1z{10$$#Ze&L;}Bud*WWGE4saz^?TJ1>uL-@~MEe6)i}v*+71v zGrw+LLI2AQ3bM@@|8yL4za|khXbq}%#}M^-#{A`a2%kPe=@ey5h8y`I4M(KU8c`izOQ+L$BBT6?+It$!cr`A*na-;PBe9UTN8Do{yOS4i z?a6tP{;c9_y)06jN08T4#qDPWDBS3w?wJqSy+ZWo8?wCa3`)lH&<^NA%3Tx9gLg31 z@nTcxTbeiBIL*I86u&*o$mL`5%ir>L8Cx}PYW9mdup^O+I*2(y2U zWNxWuQT0dC?8V4=lEIwrLK^lD#_LBNT2n(g99M&B#WRY|_n^C9iS_L!_~rYe&=!fi z-hKuO8@TPPik9OClFv6$-fzI;Et#yV9z@zaQF6k6FkA6C--Bg|y5_{a^ge8RPask( zhNs~|G;5|)JVJ@cu0fQS45s&JKSy)l^Hbp*xrer3JZvzfAC;K$Y7BS8U(k4C2Qy~; z!@ax_Z69k~lBUtOU_Q$Jdk9f0VxHJMLj9wVn3h46vkSkaO6jiYC-?XRMjk4rczFnS zKjm?6&|DPahNXe3{pn%vm4=?l{sfTKvAR?qlyl)Zgm=#Ver?> ziI!p$;c(%n8T*=5kP;VgGHUtshYc@BTpL_%#YEz@R^mDS9B-9t?ISH=vp&8Xc7 zpt|502MmLeyasL8deK&lCnQk=`HjPfnpn<{%Re!VPGb456V#{4@GNi#KS$~yGj%Cr zH^1TF#XGoMkw8nzklLPw7|16h6XcGgN*q-$H&LSVj3Fm4vdw-Dq+t$ zf&G)4=qY_A?v*T`M5-9FZw(mjBWPw4N!j%btR0Kj%#dQ5%pUM8%1$=lU~5Ejtm zgxGBypZ}s^i8o5O49O4pik$s6CjRL~HT4T)qK9JLd;*un%Lpx>%zJH9%C!q9JUa#> zjd7gr-@=oID@;A-%H@|%ob~vFy5=(0XLS(zBpe&_Z`_=cO_XCKTV_R)TF}YQ-A+7- zm`eW1$;`5d<51^MMh}EBdvg@U=YJEVs89cLS2ixSXTC!Nm&f=qT5cS*C%TEw_eP;Z zmODPqLVRbE>`}LnWtmer;bS51GCdm1eQz5&Y(hJ8a z+xZODpr6PI&PA$lKbQV)pj}Xm=pp+Vu)j%>QzmV_TGRwsBPFiKgydrkx?snj8x@?9 zdO_DQV}!^2V4375*83*m`pKH~4{Pyj6D0Jf5Jh%0TVN^a z$+7$iI6K<0aK|Xd@14)xM}NpSI6&2dca$#HXF+TZ4(h#ZYI%i0Vh&>j)41uhn+@l+ zIiWcnD@AFpos?j&c^&nUE}YeZOTtq~wuPuosYt!M#>8MD5;d9-51om9{e7I>j<9F& zdk*LIu=uwH$A-&eH7T0(aVPleCB_r6LRKG7V{!8awsaa$dbtCq5j(l)ahi4cFF7LD z&HRIr+Amj6w|I$a$*XJx)E3?+T|ZkAM;BEkq# z4&CPE{l|P-{{Y>S!NeStB|W>EqZ*N1*sg`i^MB||)*^1F${r^})~=KUGXF>ajNt!+ z9{sO2=>LZ-2?9$10i&fEGF!EsxMM$nltMTYZHss@hp>hA2{eHFJm z39zdVrThJSdmhfxozqDkTR>#=IpzynG0?0+S+yN!`ehNevcc&0S-SmSQng5yp_lxq zJYLOW>wH?WI*2g?$mgczF)ZF~=CsTr z21luKYkM$_-$rrNzKryE0ZfX$ak4pwURDhg8kZ3THNrgr{8!?1s zhkel(%IErXX~OOrBUWgKXH5^elU9-#bC2>ZXGFB?pG3%uRe`1aCUfe?G{+aA>)gWcda^y-*^LP7j@Y7iMzgGo6ot!ZCFL@3qluq8R2U_(6zdfwD2ILF_2l=||1@Ba?-9IQtZ4Rh!Ad}u zDgNRdeDi==qCUi4FTumBmIrCMM6A4z&N)$r%wE9JU<=k*g%N8vllocP7=25I5ch2? zTyU7pDV7)wTfqn6&(xliqq4e~Yt4H&Q8EJ8GY+_&?`Gq(CwTVR)10JBOIR^(pMwd0 zBF5`io0zZfh5x+$OcTgQNOKAcOZ!L}_=Mpl4bmoFqkm}wx_Q~0I2Ox8msli);&?rJ z3Bx8o#p?84PW)5h{X}P?A861z`vZxEXV|o88>tBbIK8^TgsEj1BpzW-$}3_y&*ixj zczCOut9K5P`Y4B{t&!*+7Nq)379HLx#AQ{{9HfWG_USYn7iN`rAdPBTI9Gh&*6U*W zmW$J`e39+9ZK(fvk08~ZL_Ub6?)hEB<@(wBLynz;or(Q2lnZ5BIA7a_K$sfhlU5Oa zuZ0Kc(>OoRfM)w%4i5=r@XtiPPYcJ+!;1O!&nX*Sgu`wl&i9I-oOzu7ySF$vWIV~M zZlWG9i>v!`An8Pccy0fHMGvp$9%;B%%e7Oa_~PUJpB(j<2ht~|A?@i zDvNg55qEnhDVDMZztnNcMy?Njn?zMXi9IzL-!t{x9xF??c;E*Gkprr zXm>k=`FT^^gg=pa(~}ntZ8T?fvg{xH78K`M<~IIb@y5T}i<9l^nUjz}p<@(^;;&ir z$b%yN2*j+cnRvtx+1SS{bxP#Z>Z3#l6(S$E6M+L`I32T+^w4gEvS;9>oXHlQ5q#YL z4YResS$TRHXSe6G|G5@duGNrwSc2Fh53JW0a$x>2oOGsgUAmaIw@Vl*YQk0JpExd< zhV@`~MqSn5P*^T9a)%Jput4XAII3;qSlh41nz`E<{AE2eXAPrUcpQR%?ol_dpI|Xl z29&pBnix%L$QaC2!};+=k%|?C>|Ys2wP!N%n~nG-a}B-CE9mW&Vs=9cC(eH$N%aN& z6Nd3x_!W~XM^TZz29t&@MEZ{*BF+g{1wWcLey1#12gykh#4>RW#{Pq~T1XkV@hxiRMcFp4ar5l-}HcHaU-G9-B>JDGp{U&UMG9$St~|gG1wp`X!8ncPjtf@^Sl~kF2jb z+ON0ZA2b%n&AH5f^_a$gdFXZuV__N2FGr^b2>``u++(Ba^xH z?gdwu-y_M!9s2`KJU^66tN9FiMie0AG7+0v59W-1M{UaiB4S1}&bktN_kN6peCa-N zi%@(iQ;WgO{}fj>>@oQH4P&8yEYEyOL|Q2R7VbnyPo`5rfsymJ;io+rizy9E4-Ftq z^9PbodpRia9|!WcA`xxE%hl0rDEUQ`WEtm~PWW<9GA?$|ts#b#<$d&@7_q{slozg_ z`Jncl(JTAtGIzpcY7lSQ1o5Bbgl%j#^54uz9Wj@MYd`WW^&G2TOQ11I9E0nZ@rcl5 z_OE-m2ib7mXD+5D!JL%LVDCFiuCLxfS-Lxp%;9!yJm;oYlVT%6no2LxT7$9ry@O4& zVW^B8*6+n=5WdN}qId>fYrs>(g4bR9FmTwy!B;g**e{CS&W{XVpoYx(H$-XK@K^pn zMuxO7D@d8_>yz0!W(8&%eTW3SB1Kt;($O(mBX*^E5Hs#Jg%3~CdSwC|TLUSc1TB5y*f>1l z)|U=esEnZ3u9^*XmMq#;&Cs?XSSn}JmJ^2RZ%3xM3vt+~j?*f?$ZbrfvVQ{iOxBWF zHi#70Jc_+4k>2WsgxVD{MAz~mH2{qRWwhJYQ@-OH!adbI9GS(Zdif3dl}J>3Rylljf9=gaU3vI*`_bCjFoP@;uCO%j1!`9&#i=4Yy^;{9XrRtP@5ao)E zBQg7*GWF|6%=_EWsPLy@^kotZ%W#qQ=kSQHgtzZyOAWZ}%_V5LF#-{(Bwo*BRihlb z<0tdsc`XyCS!3;WhWGg{)EC85r?HLboP`Xkxk1XE#k5SyV7YrYsUh7QKPAQf1R2@` zms9XnfjjnF__&~&8%KgMaB4-!M2vciF06F?F!DOf`2A^QR`s*a#fW;H@i=+xA^OB4 zer|9>HEJ_v%JC%GwQZZMV5QHHn}}@zl(4Mtq|%izG~0d37Ysp)EunIzi@wIXtYc72(WafO`$sYfeSO^$no1VsG*AANJ$RVvYiO&NE$#S)0S8+eT9t3m0X$R7(3wa{F zn@z!vJX+n3>B%!#PFF?8(1BAa`ApGmCw0&)rZtu^+W0p^Y;*ZO{1$QnWvuq|q``Fq zYodafI^_|QM{h#U={RaBR+L?{!_uV)=^6)O|DF2&e}9{_{{>NJFz7ae~o>@<- zdEq{cBZG4}l^(DCnYEq`}GmK9gX7l3Jbh$gim9Sl=ovKAh)WtQFt9 zcCyw-6RYh*Xm47=#ySCfJ}f3FB81u%^#m&ka8mC8zv}9c7+*lVlnb5~dYGsOVvt@+ zo@ya2#`n+{&}NCF0pp4e^7QBfRM$=CT(AngI_v2SvS;LY2}(bSFz$B>&i~nBcTtj> z-lJqUSfN_8} zg|^>M!Vl^bi7=6~ygZ?^z`AlgM8YY}7U9^ZAuHo)9E(xex}0Bl*(e#YJ;# z(iB_y)@DIdTpg){cHtZ!%&JahLbEPlxgdn~aYu10Si*tO)ug2tq5j}BYi+A}l=}w;Sxow1N^Mm({g>AwH1j#emXGNO=%6NA46omvT(JCr%iGmxUNffefIU|obFjYm z42>uIv07q;^CSgAs_IcpP-1m?1N(1kqy659H3Bnexwo4=ukCsFMS+j9r&wYe!LdUI zxX#bUOyN6EpY1_%q9RB0gfU9mMg18m!e4&n#@u5wyf+mYYY(8L4 zu;zBAzqX}+TP@c-Ueolr2vv_QtXp`SGN;vO2VKE&&>}uXSYdM0gl#$tv9I(eUM_Vv;id}bwi z)AA5(?BV11eMpKf;m?DWJYVZWt%NtV_PzUkD_X*o%PT!3w?8r$$d))`lq}U;y z`j5)OVBX!Ejd;Ogl0Upc{_cJJG=0!XK2NYl5~HoI;}#siX3ZP8C~m{ZWHRO5lE|!k zOy-1#SPGx!!A&DxZPVfAuTA(=twTv)mMfFrqowu>r6Yxucif_Fz8%gXW`wP1BU>n+ zrix{Vt`fyR(us(xv-nTM5rcpRyv|%?(y!M9cZi_UmW!$9JBs&SBD?K07&t`rzGCj^si2T9 z&AfjXxIF$c8xQ-kY(*kjHg}n_bt9qP-YjUf$9uB{69+6fog>c-jWUk+?7=Qv1oK4$ z*p(b1qtE=uS>gZyAOJ~3K~xUYW1_sXU5!AL36f5>WK<}l;(LQj$0c$5B8T*gu~^K` zW6-#6PWF68QCchsj0LFHrySGMT0J?$gQb}Esh6HLyu@w|I^nnoQh zN6?-QZN4qLE{ZLZQQ`nh;PL1-^y(tNAd#vXqI?P(^+>! z>j#nO`IXhZYY|U5PvynUyn5xv+_yJz6OhNMC7qi)b+GqcKwpe3vn61HntR_ovR}_W2efSe;$oQw)G>VVG**1_n7q)V^cq1Qz zKC{eq7c(lV$W7KH`_^!-%$~=zmRvTkt;WRAo#)wO+4UzA$=zb4I1A%5=q-cH{}H)P zfMcdZIl0~q!44(Vzwf4G@^*$i>BP_O2J`A);d^cvXJyMcsiOLnIL3(yeENMA zU*&g9oSTLDn0NTTSwh~>R3@*`r(^9&3Ksw5_M(TB{*L4Q&UWSv)k8EykHVx(#A}MU zJ!~JL`BtpiIG*;QHwZ2{$LQKnK4cZ5H~a+$o~`15MFolH`|uePf#vtTyizX)hPWCL^r20gd&SP~4%yR>?1%hzX?b`vTs27NQih86hzl z9vP^yL|%}&`kNSRXh=cfDC}mJawKdeU)zoFYuk!B8F*R8eSD%(95Hm_Dw> zZ^}1zyWD5ZA`@(~Ygo~}j3D)ANF9%6gG~%d&*~ZKw}rOxzMP$V6Tug1Bz}EBxfV1hOV@ z8Jfv@#4LJBS^ITXERy7@QzMU?6ZsT(QBtS~zd%A}nM)#u> z#?Lk&so74tuVYdKqu zqS>>)8>M&^#O9_l=baOgzeZ7hyos#A-w7_WAyNK3 zk0JO_o7+Iob8}X9%xBShdoFt3=E!mZYWp3@-YBphnm>wy8ZWJ0bBX=%o>lRTFznKev8&DRQ$>i2$)E(@`&EpkXUk)>(rUnQ7 zH8}Tl(DU99zt4`mk?v>6m=Y|_&Do_gil(&=G=0m*@SQlhWi5;s8HV~mC8OTWWz=93 zzTG*>^F`qV_%>qW6o`ZQWCXN@&^8i6a8>{T*)FUIJA;SKPZI8bVw=Yo3SS3f@Sg<% z#d^#>tjj6E3g+0%;ho|tas|rB-5}4MwwLHW-%8h6ecZac8InAU&Y~=Q9~w|w3vaJh zkT&EDBLX%cv;QsS-Z7l34nzIb7%CG3k?P36BtI5mc_X$t?jhsTg{ zzRRoB7+wYUp}ph+JyEy#T6CQ}v*-9)zhP3~3KkSPpw)hiwLy5@h>pb>TP9Pk1 zh#hM#^YFVQ;!{l#JUJerKmiVP3?}d2G4w@0AhI%uPLBiVy6bUp?pccY@#Wll?!4yyww)v{p1)X1O{+* z$rt`*KSk-~5_%Rlv2(@jIh+d!VXDSb?6M_@i@L$8^wa!X zasaC@lTg`}f{$$$1p%t4>yKmHSu377pC$XjXjG1wlMv~{;WP<+{Y;ouBS7f+V76%6 z5i5Qf?WjZa{&8f?$_OG33UEyz7;C3cPC8fNFgJy9LrYPtXk_&ACU%+pz*@tf)J@9Z`Rrs5!TU%;o3@iJ^hdvufkT%PlyIS;DO~%q7Alj&Ho~b;>m1CmBim@ zC#eFnS(`Kf!4lYsnsPg)9PgxWI8Hgv^A|Zxc;$@GM-iS*55hV#f>X8!d7CGQ$Jb*# zl^V~=>~{XHGNN1K7S=8Uc-{L(rE~@(u8+e*T8s%9%FJ1v%()dJC>IW)?)?jF@o+9r)hTj~>&`eLxSHGtzxN3NNjWsYVCn`MqOcabqHH;y~5t0<;zMY3@iyxH^F2m6izp9l?Bk0oMs8$n$uaKOrcp2>k*J1fe$4qu zgxY!(^Iq^rH-bNILvVe!o>0Y;#KfG&=wdRu(+qjvnMC?s2NZ{QQPHSETHPm%hE8Ob zogv?bM3KBA1Lq~}OpF`F>y>hty6H1Db|tS0t7$G0=XB9?f(=zDT3y7Y-`6m-Zf4BU zYQ&@#h{p9~D%sKF_F{B&IJ}#F>VT$Sqw>i?I}c<~eb?dk;}{!?~Py zgW!fE1Qq$Br*)II{17(Q+(h$lxxNinC5uguvfFh@E+r z#J?J3cv?}R;m5L56FIh9fJ*Nd6fbcjHN}Bq8!(^b$YL!6s;Z0`5#@wy`!zlv6~TON1x6))EO-z{&A2Wm)a}8&S%-++moR!e zLbVMX8MKJFg}Z1~XhKZsJ)$GD=)RiGdVMb%d?K0Q_zX7@L2On}phW*PL-$o8IdUoE zXC-1hOr7~=m&n=|gm!r;TTEIx=)Rjp&4)PGsgAIuCdXgyF{Qs~X8(n`kcRYq6!FBXOY_!(KV zHERiDmCX>V`+{M`WD;LaM$7#XhXTELIYykl*5hfNJf3e+?JU{2lO@yrc`#HGsi)To zFYRJz!XU~cEYVsvnvBs_?DY6U$W9@`w{By+xH9#t#SuMl3fbL{u-mLfll?gkrKB+_ zd>4i7xlD69O6B+?oLvw_x1<1zf`v#bEaTq&KDG{1W3BWtE;!9Z%fF7B4=YF)GC<>R zKekIQ@_pGTmLSRH!PW@GRv{qdhivN~zK_(!)5?@A$6gGCgt+}Ag61KiG@c#8Q?q8S z{fp%H@fM!+`y(c9jO8v9G%ensAZ1VXvMBZ&4{%6P6vcTb@oEl1Ov8bp4_08HA&c+9 z9&)^7C>^(ud2=nfJ1dZ2i(e#Hc%vec#)EcU&ITFcBv8er&vx7wok-(8V-AeY!reiT zUr};|XDV^#`ypCd&l9BbgVvXEM6`HuePs+*_df9CRy#pq%ZLlR%t*Q63=Hw2tU`|o z8(z{WaExh^zj^H0$L`a=xzuPu)2t3Qn?7XU!DhN=>}20$A%vD6;jo+;1)tWjV&Eh* zV?ww%TY_W5r{mHW!_<-g7c=MC4g|w6(1VDIv}`FU5lKd(LeVY>?UYm$WrQM?RftNZ zAq|n0%BYmAJ8gvuDI-O+2-%|EKk*|bud zS1UA-X#Y%=={z3$Hgdl8IbD-Jk?$tO!-gIT5^qo+vWnARIuP2hjT2%w@olXoTy-La zuH)%>+RM=6gYi*a$ONGQ#&{SqATbx+N+Slo*0ZeLn1v!<=-F6cruvBrabKR@s^sl2 zdu9(m#Z!wyt_^>KgvCr?AY4s9^;9P8OJz#B>H1{2iS!ulluhftHxEw%PQyM|5CXyyw&$Xi042>Seu+UtT z&s1P{{}95fhBIE)1?RTQ?45p{ch+K5m6Xx`$BmGUdl2h+;ZIcT>DrgdNu|v2@fn+zo2@S=x-Ry&Y$BzBA-! z9rJ9eI3aop`PKiiC-M`EP7Ps7Kp(fR9Hg^&5cBn|(75@WlOH}ZeMSe#{Q-#2GGXZM zMzWM-3DFPXTfH9Yg&}mR93^;oDHo?dV2z~~4kv~aX{*oPDRac{*m9F{f{m0pizzzHTb{bb4A za+Kqh*tgY-1K9(poL|BIi|25#Si_(vN7;1I0P9mv@y|ZNkgg1d+Dygff-|>dH=(}V zjA>!Vso#GSkEkTRPVyrBmp0k6KQZ@GA6IrSXXLB^KE5!(P~a28e6+am!;^UnMx!@y ziUaG;qrUJS^{EPky}!i|ua9_sRX|Ac2=eonQWG?pOUFGJB07lBQwPyrn@fV(QtHyj zQ8CSk;w!_bXs^ICse;oV3(?oJ$L0G*TI`^Y=3DR^!jj)I97i+AK9 zd!rwT1OdVgx1cmhgWy-$9Dea0x5d|acis!blxD1!>|(V1DAMMC!}Z`EcAsuWUQYyp zeO`Q&mP1u>9J@tFGOfHA5fxF^OC+<|@jXrHjsH`F^go|8On%XGk34k`tg{ zm^>#I+~R}s0C(=* zna)8pzTiHqHi?E$FbIT22!q7h*exc z$YC9NgKm?0U=F8;UVzI^bHi|bx0(g=+sJo` z!!oOc-gjYW_IU9w_y*s~@|pkcBigTD5z%pslt4Q|56Yr`Xb`mriqP&p#EtKv48Q#a z!A*WR+rhxIE7(mtM3MGVZhjCTd9)O9{fkk4F@{ZYt1$?$MkDSx9{z`DRqmi~_GdZ@ zKaw4Jm<^Zu$vHcb{U7?+{#u>=PnF0lu4krz9*LG>%yV7HoV1H9RZ}KN<|h&sejI%7 zOZ4y^7+ze&{jL%s<;K$^>4CTATV9`3;(?4F6<=*|(^kRpy8(sMHgiE^0|Hm);?Z{; z{U^$3NSwfH)lLRvhqFc27?~6~Mt*gF-1Y;1y-e75w))Mt}6z9%4vf}krY9~Z;e~B@VBs&?{KbrO>!)R<6hTrwmj1DNnZ<0HnyJa}m zgIs5Glh>;C>Ze+C1($mYu&N+%cD(yKG;sfVEbe;E0&Vw z`i^}ozR(nO7+3FT>;v=oR{#O!+Q>g?VtrsW!veMOk`d&uNG%o1Lil-k6`3oKvt>a7 zI>k|pT+zYvsSZfiydk-%hAg{MZiURDIX4vNCkDLRlFPkv0Ynl;GpKtrPAuiBU;_8M z>yVV#M`WA`vxhEVxK}N~V{Q4hvWN8L=U6K`1@C2B@zaPQ%_@OY@h{jq?F);)q+@jm zemQ88;{FZo@0SsA&8EFgj~bmHsHQ0*HsFA#h7Hz-cJQINj^?mE4BRlr=xPKuoindx4p+7CsY3^76ScntCFFBRT?)<7PszJ2Me^*4q=mcS zpS=kWXDNC-dJwgXpeZpM>50>5S#+J%3y$IkA$;gVatXy}D9AP1DDwtEf z)Q!{y2AEb#v$SO{n`Ad~{LMtlKVD|s)fZ$4O0j@)Y$6g#S24il(>~1Srl4~e+|S&n zxiJf=h8-y7zr@&IoSwIH`D&j}vbH^OH#Hb!G!K)A`-HUpN92fy6c=8>&r*-3m7UzN zJkIyrc67w*I1}m3@PCt;V6~sEInz0_OA>jdJpPXGCcj1+eU*=#^$WsuWJzPRSw2!F=xaD z7H5t^{){Bup6}^35=F@-j)swWlV=%=FF`dlez0tP1n2E=uLUVt-nQN zo-gL)AYBTdoaE)koftcQ!ed7gI`$dtlA6t+(YKkc`-LqdLQwa0LH6EN1iF8abX=J; z-x{g8B*~jM7QC6BKyAfBl^yb6J61C4Xr_K#7C}N zFJ%9@G?u@e!rK0L{3XPgb7UwI&BEQ#%SAxF@{q0U%h!~TTgO*dh32c+$c$rv6@lAbE}->swU z&Nqs)`#I(Pgs$~T)JQKztWb_yTc*&blt7hXD`xW)@Dv}z*_zoL{TM)4(-w@MS)ui` z1gCvVaN5|x+flLX7}SZ$j(tRm%aC|;90S%9@QRctMQ{yI{yf3y@@RgKH^gXt03R2> zAzU$qcjDy;MT}yn!V*+|!iJ)wG`o7BylE1@ty1`T{50dI*pY0tm)T3Zm|RuHAd^^f z$46tlGK!AHLn)H%Kw62iQJ-Mo0c8O63a}Jm(s-CWIii>kblPQ@I>z%3Nh>M%meMe)>5ct6!%| zC5OPJGugOgC(dSv@q6gSgLmJ#5q5*o$+sEdpvAB!gRwvT71`aN+0{CphkF!I{XLbD zQ(#;0MY1M7$2hu*O>K#ME~{Z~TrJu+Vlj9a#?2{mJa$<_W6ph4;3B!=`NA53f}h|(%=`ek2Wb9y4Oor5@d z?-=7{G;wUo;@vPS_OzbI(|s(T^^Z|&q0MoNezJ3qBJckl^T%7TO@B`DoU@G3--&MT zZf2#_vZ+Ll!_n>tiv1wz>m#Z{+o*Xli(*|3dPlT_+eDUZE#P_l5kUZ8T33DVVfX?5Bai7Y9*!3e*BBIpt|}tS|gfiPFv4hwYx;j zGyb1W8vf^ilzVQscHhlJ#?MGasPhxbp82#G1aR%B1^&mXxb!auk2A-3*4{+#pV#R8 zN$0%Zb$-Z-vSQso_VrvRTqcRQt7-hYrGa9861BV4xKJmCW$zgYnJ^3k1F`VMYBboKQnX!d) zftT=(KZxFESAITS$2q+xc-E~XJVJ-)o5!&4xQv_2C}b9kGclrp;X@l(F!=*FuPsLH zVKlEDhcd0nn?8>uP6sIvIJ1PrAYayo>|w3$Q)1LN@lufJ&_-9U0j0A2_+854`u4q$;kaX&G5E$46U%n(mRKkPG2saYoTn=911iF@Y2|h zX8tVlmxS^sHHXlapDeM_$3W#)kMQ&ivMgq@tfU@E zk!BwMSxrS>DGFmwkmBk?`?Y<M`EJ{AOAk()Qh1U^=;#d*~5q2jbvM%WM*(Yy1yg%;Bbw~Ny|BXE|u_|4a}H*ghwiQRJTr~U3(c3At7k} zFvqvUlzAIIam;xaDHD_!5k7$g+fp6|K4L)aFk9W`(J{J+=GUv3*ComKX=@m(J&C05 zg9J&|b93b$GCoIABma>8HOb5y7Q_U*K&rBw_-2(!V8tnp7M(`xX&93|(lFL>=J@G6 z9_$~%am!^`RA`g?+nR0CC0tL}KstC0!TQ^{-cZRbmki3c%X9401~h#GQPmO0Ax4_Y zxC){kLgs({G!#2B#9<9%tJbl0+bPbZ3sKoFO47hp z#VIrRMj!%`AIRgb3-htGcNGkQ3Myka%P3JA}Sh#PI)J>lXjspQV-ihZcNNsaku~g zAOJ~3K~%TegM*q56XXneTO&gCc3J)v3!^xoLbRkBcMpkTddRbl7Wz%)+V6Qf9vNBhK_MBs1V3k8dnx zlv5vB(|a*3UWr;|9@B+CkbcgY!Y(B&7FMI6^984FOQs0@$GPPfS>nEuXR=M$D!(G# z{1jR?+bIk&B%}8(5@EH(I~C&qQ$Vc0Nw z$qTmJ%i_YQ!<3E`B>#^9B-$3%rH$He&lBRo$ILSZcZO>tAoeGu9 zKae#e8G{A;dA0uy6@$<5azigYjt{v0@E5D=dkD9(<)+wo_EgT`VCR2m2ao2dwGr9F zUl1a&oQbnuvhrFWeuIPPYr4kuz$Ns4zmK$rB$BPS2@$nm>)&hKiHYZ;%NI0NZnMgK zE%gOj90?nO%(fI-jvhv(d=L`vI$0w(pJPWuXj>gYY0GP_s4V2@g9>tIe&XhVV>}mF zM!2ODQ_ugTe$#F|Ev0$nYQ-Vbr4+Z%WS`bUYNlGUc*X}luhrzLraq4j{NaOC01I@t zBIIq3o&QqY?{soq^a@cr3M9BKV%cD2-aGp7c}O6`?jC26`)8K?8A0I}1L|(h!P`*` zQ=3h+l&JE%ZUI_j|B-M15vj-T@EI|maR;oBme8YHWgkZ?G$q_w6_+zyBcH?(!;v#P z$FyU+xi|L^gZH{oZ4%8$&D)%}4rJq=$(Y`MNA*}y9yg|wsr8rF>)r6qk04by9?h~g zj{aGUkH&HgX7=)SzcJR51Nty z^W%2>o_O#ycM|2+owR1fQZqe`{~8~#a8Wzi$?I7pvWy+Q`|)=C#6f*=VoC>dR#KCZ zPXdsBSs+T`(6U<@yY=ka*?0~!K{VRmvRFRzv0oU(wc zFQa&tC4s<$!FZ|6;6kG_9-HUW{z-(6va=Msx6+_E2C?jaY_gkx!ypZAeVfbnv4I>X z2Z5F8l=an9Dw%~zi~>UzbrY<;fM-Iz-0D0+&0s|)?Q%f5{~MAKSD3Fq1-I1&4BmQ& z2|9Jm9`3}mH^B^3d_?@@-AG*po51G?nA9@Jy`DP*GgzE5pP0LG#9X}0k^`3HUjIvx zuQdi{k+g^u;rBTa*&!-)Zx&_1%Qxurg@AMXz@hQX^~_ zHRA|BCmv(k%Q!03FW^1n2#Ypy%U)yWT}F=m4Q3DPW7&l{+}%BzxQrPXzl=a9 zdI(yJcCc%35gQlnW?)?!Z-jcewbO_Jtz!14Z${~v4jI4Nxaqr%CbJFnE)$@^PM7n$ za)=%oK+bqeqz%6^^Jo+XLsrmvb|;UAc_VpH250w?TvF-5yYmtyIfg90f1c(Y=}5de zNY|W=|5HHvpEpQCL9?hFxJSi+0NxgDyw?q4_SP0+&Bw7%))b$d9+bE3C27@N9*bo1 zY{V)oFRvkX(rd~m--9S|hW`m;d#30=4V8LM8k$jV^@ZR2 ztk^DA#X$Wu#*bT%|2!u;QwrGjZ#NgVCo}V>3OC&zk+(q}Kj()W7BT0L@^T!;o@C8n z8_t_OVV8v<3d_E*qVzYx zXj#>c_{d8nT(RY=>kQ@<|7B$QU)05wh$vrzXY2@Edu>tLsfa|_GP12F;`Ut}>F-A5 znolMFjXYnylM!*aM$DX*3^W(9Rv?b~&zcbWF_o9=zoB7P!;S+V*kiw*GNYlizNsc; zXc%>sc`SIipUC0MIM9|zm##Ll3N2h7c+8yW3?kp}CgXDe)wW&a?O%?;C0!={vBG;z z54rNqbR{Nm&i)YBZdtPY?OJqKDI$7iB9;p!X~7szX$AT}HgQ2>Crx__XxsOU(QmDI zA8g1>lTpl!n#RFS3rF64D~;;1Ig+Ol+Jyk3LcxcvBv_N!^c#@!}KdLgH~@+hdSWVt~y+lx1Hd5a$f@1Nn6`IX179x&84pScDL zxRoM}#ihl32zR3GVjFMf?czr0Q5;qUu*2ssbLE8ad%cA}O$SlG=|+suYVMxDkM3l9 z9zV(CYe@!i%KOmD&A>mVhd)}AS$1zb6)qiA_%9=Bg)Nuc79-o=&zE;g+38%%_n+yw z9WKB+doZdcUwQoY7R~X-j6bm#yS;_vihkxkFB#wA!wjVztX?;jf4M`*?bo5kV=jaK z3#4gRGV>R&q}4VR?fOHwRVH#{U?WxuSNXlpjF_8cTzwRUZunL@CY<2%+wZhroW-NF zhiJ{RM|RI6)MTD=>F6ff&qd&UK8b-*tvq`0n_gpmif+xpc1#X)B;ND<=VmVSbQ9pZ ziUTVvkoh0K8){j11suI7lh9ADKgF))89`ciEi`qPf@iE((1 zeagmF$&63+X!ONL=?-*0A_ma>wlKjqB#7#5j&Dw0>E5 z&}0^epLg)-^+Cq(iKKD$WDW&QW$VJJm@kq?NY#N|(J^crs7KuJ8BIZ#$&W3gul+q& z_9-#0x0He7tt`GD%}Mtlp0-*OQLaG1?;;MFSn*B(7!{BVXHEEJaam6vu2^H z9Y(=WP4*aQaLm*M6^lT${yW0dfi#vXzUM>J1m3%DVUtP*J0A8RdVLOewyLn)_aBBu z-x-&?g1k}dFnaFEnVj{kxYR-Xs93CqPbM(cpY7+e*tet)|JB~4?y*JYg)T1XFWJ91 zjp>nUY_3m$f?}lgl!!Sog;5Tt7#Gxwe?vF1=5Bb#&0%ka5y2*Plua-|G*Jvwl?p^B zEyQDkEn`fDS@mWQu3yffSmVpYW8d)I_=yWG!wLFoO0<3sr$*jjO7l?kZ|=u#c|RIy z4n)b1=fCnB1PYyCozWVeCVpYLdNR)ctwY#;5A7-&*_*3Or>q4BLo7H~w3~j_W&H6P z#D*b;?Eik2h)uWHqpN`U`NNFYEFtdXH)?`r^6a)IVlArpeX(arjx?F0-4RQ%Wbpc6 z;!n6!oT|*s5s#R)LY%S)Efn>S&^e%v=*l#*%`zG1vXSY#wa^$WPin8c_Z?nWakKh_@Qe5cgnIyyx<;E}bhT8yFfV&G-CTu3db< z=5$rQc~ns(5sUU2ck~6$;iD=_OLhRu4(D)iTR-=k3Ta;=iOvK+$|{#|>7o)J&gn5x zX)b#W9%5{6Opl-gj|NL17od#K+r1o|Sje%Z<%oY+j(*lGT9pqnrD!r=<1;8UDnPj- zg#Q{g&}b=(j@TBimyN(Q^fvG3?I61D4%Nl#Y%N%YWlRfF{)&wC8_JmCE~>*9qGegm zUioTu6|B0b>icwy#yT5WPXb}?;pNm;h7yYt?D7ER2C8A<9KhI zj_`UNp8V89Y0f{Q(^8PB3_(p%mlM)PY`5Ics_hm87e2sRZ45KFU%;%l7t5OsWZhgt z-O6M2*P&1F+WnuLq7-1eMT@R zPL<?+mxD{Bfe0T z-@)~qk$lV6z$_$(j>U890MTOhSn!G;PKUX=@fwdtjl%T(LbPiG(WwmKdir6k#g8L@ z`#4+uHL&}ypJVseGg~N(MWZJmBs-dt1uq!C)tOK;L;NEL^I+FPq)&=4|5OOMcjD+j z*1=n?D|o(6q1nTdb?17}{-uq(Pc+S+@_5rQ9gk*B%HN2f?_kRX?IkSDbSCrQ0Etu9 zai`V;MY}W>?G+>`>>)NL=4@X3fjLbDxUYGKh)q22O3m0(zL^8z6A@ItgyYIUwm7OV zrr(+FZ*nO8PQ=$n6{(jOFqS{c<}^PF>faE)85&IwQrUH#(|V67j}xV{{~ww;!+3bW z5%u|oY@e_hb1zk-f4<<*KoA$|1~KY{8(#0uVzH-=$c1BQz44s1gBM77Z$!yjYZ{h$ zBK}}AuS7O**K!9Xt_igK*-XV=0|NB?i1>YuyWMS+3FNWrz#bO9P{hg4o+Dznuo<)e&)}P_6vpeuQ!}87+tUtgE)Qq-a$!1FA7OfdBJLVTX`MNqy?U2d3u*p!lMVl)@w$ek3xXTZ)sz-g0(y z0oCqhEOZXz`K9Toeh4G3F@oBcE~Iv@qUCtT|GYu^p99j8*V~Egn9HdPmw6*p&3_W1 zylVbSIu8giS%uAtd!#2{WP*4j>%&XXjI&~6WHfhIZ070k>&#l9&ZprAc$`{IL)AG% zQyU2PdyD7FHdcCTAaK$I%O5)!Q)G^@#Y8^)>0#9RlcWdHL}p8n{H~09t!k(mx8kv9 zHMvDWq;K29x;1ObA5u+gtsEhmQq@Fu3d986#8TGsE9H0Gjn<8yeFCORVhYuY6wE@AhFl3H*5b;ij#E-emID3+RlQeibCY(3(o^jnh zj&)Bq(DP*(8LJ!^al@JGjpg+8SD+I9hTcP69LU{DaEJw>#g{lO;lSxYAqp3W;Tc+i z@Yx$EZ(YY1d3jcg3bJ~#BjY>X6P;j2{YhmuYK!pY-YL$$Rz+N70UeuMi2Cfxg?*-& zUd^RGbPZWg+jy@TK}c#67ysR+y>&IB`PIDd)j+c>6VKWnh9^&^*zO#5d)|^Y%b$B{ z$;=vJft>9Pib9@XB+NedCKP3lMt54rZza1BesKir+f}|tt+^kZQ+_#B$sb% zVOY8Zer_PfOdUV#Itphvaav;^wl_66ob!qFksJ89couVfYzXQtB59~MkDp#AJ<^F$ z|K2h7?Ru_V8BN`R{j@%QO1$kdcILHUbm%cBfBwbsk`%drsxf-CjP~p4=sz0F*(;ie zxL(3(>oeRm!T!x@_DR@~on*g5x1YzUxlnNVOksF)vv3^*1+Hco14)O2e*H@;jE0Rel8f zbKhB%y_oWd32eMCkJ}7$I;JU*m9v^hQ7R9V z=d*2C3#q{F&{`CUhN;LuEK@>(24{jqvq&(C5_ zG| z83-R&;N$rr{8f>~Jf;pkwGLjneCKR)6~RYx5E$dna+OUK{WM2(h6j1?edw$%K%p@X z1D#r0qoR2`H;cQmS;*}!;pt%w-U*&yyUTcTVzPN#r%vXhkra52f56tnhZ;!8g@=ToCl)L{m63_Ek_MZpbuK4Wt(Z5xlUEXK5P9`x~-<_k7lkmf>E4 zGv8K5l2Ty8$|W)wtQbsmfCDk_Hj*DDz^x0Lx%hMpD;B5`|8WA+-}Z3A{R!8nh+tvc z&n6#puB{Q{e7g;*Mjc#>GNW?GdfYOnAme|E+sQUu6&itn=Uw7OwxXkM1ZBPW@0iK& zxO9ftIwNa4ffp;z(6&X261`MBKIPK&K!P}ZJ(T`l#Ij%q^@}d>&@_tYf8XNL)x&Xh zCqhNT@%|gZo{Z60t@US3Wg~X~c2X(%kL?kI`75}GNdrpk`eliBR3hpx)vziI!~9e% zX33ps3nh|$Yb>w44{*eA3ya6hq;8or-zKkSnc;dQd#BLbtIl+p=cGp-ASK5Od+Yyj zFz(};&@~c}Yv@Tjz;k^$D(;%l{W}J#%vgMU89Jp-%sz623$}$!3Qa?N`F1|6ap3menTV%& zaHs1cP6^VKpKaHvGzx zS^P~3yY>56>S@Ab$4MNBYU5^w4N(gFXl)(Ln8OvcZT-gm|GJU-{2NiBGURU6uxV-v z$LD?G@U12ESKYyUdld)f=JB7`6C%pj(SJJ*v(gv%HEiSGtT`Ml)#uH?O&a9Y*e&#w zOQDJE(K1BC;|Y&%PNBG>9EGNGZm23E*ZPVh?vpuaJeJApOo>cC%UMrZzE{4&#l8UJ zA_WvW{pjy~Nz%X+W~mi3Z*()i1+1}NFOFJzD_Jwl2opa{ZigGXBUR~C9!JRfEv(lF zK;q3QbmGF<<`qv}MGqZ$q7)nn<=QI+`lk6X>DPFU`L)pY(t1&ZtsnvZHx}fipLT4pCA=lf~-iK%dugfXnppD`=7FCag?Rpv=pa` zz3BWqi{a!%=4$FuF=8bWTeaAA+aeznMtzRaLg$~2Ywuv9&nd`Ts$?naXQp^q-(Cv0}ep&|P->dnej zeb?aGr9+I}rh?jbBYfL#@=B$dIYZ3YQzFQKV;Ku~r!hHW8buxV7<{XQLmjtxVR4>M zJBM&i*PNG%z1)0L#_gD3wk}pf*jJV3i8sj;dPi1j0WCA5csVncD??qmHmM(*1zlum z1R+p3i>#or6j}A)-z7ugzyMbapE7;lD|-BU(C_Ub;=LNLaM zVQ28#l}vN75BdLesUJU-)_;Ep)hI+$&J&rdPw2R`h70?BVzG4^(nZnn44A*|do)8t*n{Pv|eG>j5 z@>KokV&IGlZe?Bk>x{xLS(reTk%dCPxvAvEFv%)v1V=O9%@)!9V(6`V%#z>3k(7_*Y5p*r4mD!=b}d8WlW+-3 z;ec!cr_NckDMyUi+cr{o&>N@l1f;$jAw15G6EGl^L#!a9?>}RWydCy_`cxfVj_s{V7H&==tH+&JGX=R> zF_U3tgD4o9#4qsaqz&iV=g^SviM?kul6sILWb#R~aT3Ok9~HHIm=rSi7k6_{C$gP*% zh#sFuH=D_xQ`}SX)T`Nq-p|w~92y?1LfQ2JPQFK|$x%8 z8wnR~M!xq8*?HMaz8%Ek2kB^92h%P$lC3Y)2##{)ZN3bpCUHEhi>LhUJ~n)>!luKF zxu54@_@x`8BRYt0R|4rFxa3N6YQ%Y#gjew6_8Izfr_$-J!?B5Tu>0!8HMCJXu1Uov zcTx-0=#n0U!?kbZ$b=$iE{(pxerh)Ra>xD`8w*6aEc6VsaX(nHTmtb4p6r#FfNAdw zu9!_^+ZPwq9%f*lc8|T)dYHuDXTzL>JP2u^qGAFGr%$r!l^-i68L=Y4fGntDaN*dmNg z)g?k@hw&oIhrA1th_nUpc%}*)r6mxQxJ3F~2}+d2P>a8dTwxF5&-_?&TaF`jXV}!= ziv5xORQh<(zi1V!4)3HZQJE8#Upc*`3H!}m{JQMN<*9D$8axKu17ZCB?*wieCD|<; z#pgMBD5h>CZd?OG8y+yWdlnBpmt!sOO*|O{t(T%AL6K{fk2t#e36ZBf*(G?IPbzx2 z9%{s+)q~W(lhOK^%jASb49o0BSLr^_%0pN;rUCodS5$Og#5*B^bGMwaey5F>a|>E^ zx@4P`BM=+Ua#ab+6t7}r+M!=jc13R-fA6ij4QUMp#;Vd&imrY|!Ukr}p3 znSF(2A9tYt*@~dVH6&hb;^Mk5%)*WKczo2PUT`^7o03ZNKL_t(y z7NILH5UrI$^4nP`rU#JqXDi_mHyDxAkDOfyNwY*q&y=B1^E_!jfkeywA|iVlmcKV6 z8zjxnX?y8UAIrkgdl1S@N9@H}tj3)n#6p4wtq#{Ja*J$@%!tYT_$h3A~URMtr24)afw;AcbiUoD4vT`$5_4>ZDxaY24>UM>3MyUly5fdK4nPOv`Bv0P3Mi-aduxcW3Tv5p2nt9 z*=fy#XLh)y?ZA9r1vQ;l(eg_oFfN_($)04~UxCZQYF7T0LM!_+&Erqe(d~=q)h@hj z#<5c}lkAXcyj&)7zFddMLy=gIE8^1OF`OS`&dG-xd0lssH5umAyg$cCp+N+%8%MZR zCOsp+@yhoSlIq71mhND~mOx6zYf^6AN4KgEMp5xJW?D0W7feT13m$`Q@}96^qT7_`G6}p%h)e zW>Dbm$G$Ojd=+#?!f_Wp6-$x2?ZWe8FF9QL1sfA@;x>sg)oTZ3-@Y)%r-YhZEv$mS z5z{V-=9<~0jqqjluf=3;SwO{pIgIX=BJ=tj{g1!WvE~K_Qo2;ly}-9ifushOAX9Xj z>sbw~lKG1G6GPe(=OKS9359MgOgG*j-6@`@clwCWT7sUG0*yi5INp3q!Ob{IRa@A) zvzK+7Zu3akpOFOzk+hbf+v5xi&$Q#&bCuoIC8Xc@!N|(Lh*o~!$;AjtbUesg-G#2y zNXBdr#kgxCT1A_=G-MePQ_m6n;VABDzp?lngMnxxzQNh}I~4M2u^@@Hhp=!?pxoaS zn=KItPI*n>zQf2rTuoY!1P?xJ!eqdmVL1te&ilxdMhSwV<;i_N9o<_-TyGde#g`4V zIKC!ht^@mOUvXd1o|fgen0>^V&#qe-HM)&ztuk_t)>C|>iEEdPIhoLjNQO6par?== z70Q@rMQGoxCg_O(A|}?P`42E|QZuPa^&IPJz*jeinODBxc7F)A|HsUIwuRvMVH-b1 zQ$mpjLS~8(5)CONBQp&oNm)@@DJwI3WG7KbsgRw9St3P5MG?v>qDb_;pZxCQxL?5k z!TmBm$8}t1#5S5Xai{yPAW}2}NJuh5b7Oz{r6*&!B$$RJhFspZlSMoI(P}-EHpNTm z)Yz0|PANQ7h$Gq4f$UaoQ1!lmYg|)yJ2*2u@F$<*Z(=;anfz{%3{QHDljbEh9$AWc z$C2z)^x)^tmXr;<$6wXqOkA>(jTdt`RHcp6;u@Zhol3~#t{5KqfK}ZSqSg*UY1}^S z$J8^m*BNRfF7d7FUlyjgW3k>Hcb6|*n&Zm#2l~WxokMKHGwgle@gZ#lX3rzBPWI%@ z^rsAsvSvb?a#q@2$9C^}e&;t}Z~Bo_*2;vrCSn~mo6lXQp!KUQN2+o$sVt`AdNNAe z+!=c`h?|8^@EOyJcCW4Q4ckxQ$p>^*>PYpCcO*vzvcY@`8OKcd@-CR3KC@~6cO<*A zcan6ym=neBw0Jj+Kqq|?o{!|;_LdkeXvggbJIUUwg8%5TRQJl^rdJrN!Wk-h3!n4;!e3I`x8_bv zH)sZTH(%jjxfQ~>HzzFCKm;sM~obIKz z^2S|X+ztfEiio}P(7ay$rIbnbnKd$|?;1(^yih9N$BW+1w=!x|7m3lfkgtu@r0`{b zac((QUPOe*l#gy=l6+d)wYez?&TXZj!&sS9A1U>|$Hk!80I73!l(jZ$@^H{H*%bR& znzKs!Bvi}(*hiAL~*MT|;zNK&gEa^JRI26QQrZ4s4X{q~eZxc3%q z%kR?seyQvqVuU+(RtkdIM&%TuqrCP~u{7qEjR)_?4kpgRX>Ed}-{&=?#|Kw*fY|v~m)zy&Sp-tt7R;gtE zERaJ#Hj1&Gf{d;ICW9CKkcX*OvQ=}IOiI5cyCzmj%4$d1Y_&!%7=D%Neu1LDzFcajbhjm5rmHi)q{&S>8EX8Y#NVr6)^dSA(fcyc8)b=cvfUIYXteDot{9^kln1fP9>n zD-&J5OR?E9S&?{BHvQ@}gs8~sF+o!LGehLjBzd`Fy|jzjE18Bp#puOm zSu4F|$nY?!$G`(dnf z|F}X zDiFKmzfy3>Mb?-#lHQBVL^0J$%6~;l%ndy;ObXC+f(h)8Ay)+pF?%yqX*jz9j3W50|t+H&OE) zETN{6;_Wn0ru_&I3%#e(*6+QHeyt;!s!kHUc&6O{rYJhaz2v9;0EujVM>@0%mcd6X zWlx(=@^yTj_=LTb!^Z#QQq2J|OGuSO_gB)|B3M#xUzR0(ZpaCTc@mayBBxp>$efXV z#rBcEOfw9UESHsHf7VD=)nAc@KUZaG;%+gu7$J${v*cRTMQN{CCeuEtiD@$j=^o!t z^xJolngS0oNZlY`Ls!V?KTYJ-=uMIqYa+g;vt-`Pf1+=jD$+Jra=$l7N=&^>n{z?T zH5BE}^a*0)(m|RG_$1SN$4Kjh6QXkFsT@syAldsn$@HrOWZ?ehQhU)?HqO5%iaT1! zm^T)Z9x_DM)Ran+k&%pX93@E`{3Wlok^H+CA)D@P5nUT|8L;Ax^ip^$Kaa1FS8tok z{SV`$f74ZBji(IPz96?Ezsmd>b40nx74a!MESc@qq{PTlTsvQuGXpQmhdJ|P?S@D( zEgvWIqc6$nn@#1)k!I36@Ujf3{Vl$!YvoI;D9O6BLeBK4ouPl<6%ubkEANuKQ$(dx^Z5byZyZoRP$+RZ_EVj@($%QeutU$`^Sgo3$3pP_GYi zY(yuC|5GWAt~QdY-g%<0{7)iVf0HiF9*OG@Z5fujMRfm!N{hiCq|L-EnKRc{?C)Di z!0jR#p!!Qn!>5R6-Xxjm-c>>$<%mTv@(~V zsC81f{G-H|j1|?d2BPYGTqbUMB7P2GGRuFZ9BB7lib9K}$CNh&4P%R@pb)@l%u5!upg>*a>C66qMWV(Kt#6&wuNLyVgdb~rt7Zk~` z3H5TM^(iUd>>{UaF3G@F`{nn&Pg1J=RbC$KDzmz!%e?hgQqujo#4YPBw%@x-@ZVPw z^&>`p^hlO-KL$!ie-k-$&`utyHW9BWhs5WKro=CMBki?=W!$F+vigdGY+lzx)|Mzp zdAIvw-oKluYZ%L#ACJVdVx6qtG)#s)8Z7?n_sUVPzOvI}m8_hvAbKr*<<$JYrkO2~ zw@>#&`)y5+Q@|#^)fb~gG}@rBn4J?M5AGoEb8A!_J``p zrnd>vE|obCR%04l?8OVCivjr9`h?D2wJAN*lkI@-@L!6w9y6 z_HK=3snTpwSUNzWEiXyW#0OKrqzL5Y|ZhREIliV`> zC9y{(@dFj#w?zvUEf*B~EH~s>}V~ljPZ;68W5%A}NJkG_ znQ8b^t~{!itA|ENpF%H*E)0oS}u0&U&@{AQSz_TV~N%JDxCvPh`F7Y z^s-nkwNHCU(lR|+J?o5A7P!k@HwRhr`HbkyF_)EU@v^AtLb(^mWjm&=7FmnAIphBQz5BfaKam)A=3WLCDd z?A={1w(9-F#lUK?I_cxPnC5uUyI4HzcTYciYV0Rh}H5Q@^jKX zsXBX2{AVqYpBK-_&{vVNVChSl(`SdoHytQt?mJ}F&c_n;B}tC-2o*m+1391SC_v7C z7BTzJCPx2RrQ?5g(f!Xdn*Z6R*?-ni{m(v%|5>OppwQ%hJxL|h&%(GS5~b6g1Ye$n z*ZJkRDJfHUNfG529k_7oGdFCm(qG-53;y0LZg-WO$8oHkvx6n=f6+g-hFKjZv;W9C z0-6rz`9XJDRM#=IawUUmpE5pbH&dp2uyLXX&jW-D!x|Aar7cbe@@UjCkN$(VV-~rT z;7g;Jt{cj+FY5^%u@SS`T~JeM!bejN8uXWPqA-zy@n!^{)8ogU=cv3J&n4Gi=uLk@ z+m%20=#|E!fk7yaY|jYKZ&>cE=7ee-ztz5TKlKg!Erv26voUJFwxS+75!2xz^p0$c zuA>)s_I&4f;9SxN#qvJlD;85uVChjngZ))(j`!tl%XqE^m@;?f2?E!Ikx}lB*R^$Q z^3-S9@Ggvq%%aVe1bo!J@R$_H5nXkrJGN(pjUT7C7jvvE0QWh^STJ6JqP;3~3u}Rk zbt2n3{H9OMZr1L+gUeAb+KpRB<^EVg^+sWL6tXW@G4f*}0Sh(Iei}>0o$)-eJxr5k z35Wl_7M1cmG`1OWbgnZQ%{*|@?Lx`WEmVCUi@xU? zrtiMXOwFBSpJ|1Ea1LLt?Iz^qNc_jHq0jwB%(Q$&+muc0k5a=n(wrtE>baG+6|3~m zsHqMh?(BZrPIsfMX%43?THre~fem&(?6};5t+Sr9qsovy@oto84o4n6Wm#(c~W{;cI!!W=5rY@kP*Ram;bChhQN-hVpEQ@_3(TRb17-Z6~58Aps~H1SUwu`ef_ zT~!aUAMc6#!_KTzE5gGff+0p0L}&bF+S2zlE$f1|Und5%-N%D_^J&rj9qW$g^DE6A z<(t{qF6)d^_r9E4)`|-&;+dP#nU6yj;d{oE55+0C>RiEcpg*PKXJA+A#i2X4%wN6} zkG;8sx=rNhz-H8F=wkF|4$o?IQ0jFW&yUU66@C|k;g&pCsvvD}7S0)S(HK6N;T;S) zFfgJ`rb9JLjX$=Usz(-#NQJS3KU9R<$j^k9PX3w{quWT&GQ{kA;dhR-Ul zhud=1sTns@UHSIq9~XR_Sh(yon$0iZ@S>jme~$4wtClf8O1beenT0DS@Nel9>fZFg zFu0Bq)jIkgGbU(w6)hdUbJt-eK`L`m_gX-j<7sMC->~gp4kMJivHy)VAxk<@v9p4y zpWD;fv=cX)hp-^O8^ipv2uy5@)nFC=#%^QxuvYZc^Jh=L88qmtu;yzuul6OfT(3D= zT4ng%dWk`rFyCINPY9O45tCJ ziEZ5pV>;O32^)fO?L zLjj+5{HA)VE2%04%t#!F&mtYNGtZIm#g`>HYq&G%1=$zo(Jpu%k4>%FWPc3rUs3;(VbI;e{zI>NAib-=E;R;~uqvN7;6=Gl~CnxcfevjT^LS+hijiy};6p*pX4jz7z)gGJK#G(@m|pnf{b9w|8;g)q~5UENSOEoBVOT85BO5 zZ^vS|ljYAk^9fY#7>&!TzbG^>M(@WoTKvo>XhjYcTSuav9)iBZZD#cg#NE;r;{!KH zeL9OC+f2#6p^HcPB0ltPVDKRL- zdhoum1dAsTOug+!*QOIl*=)tZmodzkmCKoqW*j@Rg4H>9*cw#H?ZNHoub{}2Ir}L* zIfp?C!16 z;(2r)_onZMTBfh}@Y$S-Ty`J2Rjua5q8iManWMGYk>#D2 zqyO$ImnXI2pJyeVy|b|^?L^k#!(^%{v0lxYb#jVAl1YkhjK(u#IOM^pb;g|XjOBH4 z27z71Vl=gatP!Eq-Uuh6>tmX=UWLkpE%Z6Niz(x6nfKL-z<#^`bIp{`^uWegYg^f#ouxuY_g=f6#OGJOa=YQNI{$1Zkl9YLd8H;9yUR@LpndG=|xuAW5C zC{K2sY#?^oK~j#Mp#8KxS)8NK%)za7r-z~BXu z;o<1+(Bkd5%cQ2eFtAvGAm0T9I3H)^%3*8^*n-pSebkowVG-;{|1sqh&PijS@gP20 zW#RMSJh}fqvd4K4*Vh-Yas79U_dn#y#$*as9wPUI2A;DWNj%y>Qo%jm>7^2F(;S(5 zmUxwi^qFqR&4p>qOu0w0Z6i8YULe1A7_*DQ=|0_qg9WSUR`Q$5#BNOUJ(T8-NFQyAaVq{z^Rr-yEnr&+}OxQpmr{l~}+W4Q1C3x9QA zR43GMGOa7ae>Jc^YZ!NW-(qQHXI2$75NbOHMWqnjhUioMbS>jv++cK{JpKmy^WsVl zf1_qM!f_Pb+HtS>AsnXqaPN>UDTn574vaPc03ZNKL_t(Cr}+#{y&KK))OxB` z_MklTB2Sko@a@|;y4EiwI;ty+rd~li$&a!K6UK!$V!3{Enx?kllaep(H_YV9&mA;5 z{hI}MVwnFWh)+|LN$Ko_o6UE!>Sr+T(?t#}3TKI8IGcL!VdCM>C>?BpP2e#!Lbdta zaU)^wnHVVy$LnznYdkzzH^`6Fy47^*v74WJbMVSqN$>X)*kJGnqjmSN?pDFyBjbtn zGUuz6F=uDqWWt_%OgB!${PqR9EOX$pS_x`9yHOl{AKji0$&5Y4?)u-ne7^|=rC@d! zg`xIt9NV4%Za|U0j>FG23d5Y$?9#8I%%B9F!={9vyUws@FVKCkiB#8p>`OZ6d5e638F5TOpU&eHMSS%&=Gg5G*< zVpiwLn+j_#J{tNXw(JKd%!$8WaKcv z-&Z^u;;^2#i?k$nCjU^!Lnn+CZHCh8=U^_kZ%((4&q&zWgFkIlIp@@yD`)rfGWsin zeH&wP@&m~R{b_n+6;6$sF*eqeBl(>$SGt6%qXC8UW@7s|3+;>@L@%mEd8Ri9Uk}4c z&5?CM3wWh%%F3H(8R1%i{fB#OtZHDz!l4}N@dcydF>G%c%ZiYXylGX;M(nW|K8M^v z186s*hGB0tIXi7OZzff8$fpM7SM#X79U(@$?T23M=8csz&6MTI3zHvx^*2$?$j5vmK(5Y?1SxswY2h{ z!48vgjD9qpjjqjDsTPP<$6S{CSW+LQ$Av@T41PP3!&9d7W9&=DOChapcj3wK)%Zsm z@jG0Hv>!+K@%|)+irRepUCE!o5N25#V^X(<5hK*e8T}rQsh7Fy;KJ-IBepl3B<$P| zQXc+f*q}VjyAGv$YHP~W^7xr+#3GA-RQu-9v+ZY+lP!q&7l3n0CU0wYQX9L8e>Prp z9ykogyi$5ERcB7BKFiADNeEA7sbLK}@4E7D!8i`izsDcD!KAr<<5E~JY7WohyY*Eb zZfnjAvq(C=n$MQ|9r#lhNa_U-#>^ea^B0|&)b<$aZyzz&Gmzz9mC?I4iy7V*x#=BD z#r{Ov2E?JRmx+F{BB>7o8CA2K5zme?^2|wGG%Cq|;J^h%OMd)r&5{Tg&g8(b2YI|0 z)t~&lQsTTlQO!|cRG|}&!xMSgv4N*$Ni#D&~++6;d!pFAkxiW>@$1M1);fL9@FqCuxd7U$! z7;|H);-=HA;WtBPMlfmhOztHV^X}a&oVwRDV#H7~I-Wpn?^uQp|4CNiP^^~);-;#J zk=HhyE9xk|+M0H)n&Pl8g%KBWXmzbMiq)p1Ht6zbvlVO08dH3DHE*xEa^Ora3Knmt zQ$;C8o)5_UwHnj!dujUW3VPM!P!EV^-oDj5+!ai#OKV9^n8=pm7o2fu!;j0FB%d?m zmE(9WrQXK1Q38{bJkYdo=U-$?D%(c0y7E1L1`ftk$&;Zsa(UinJr@?=#BD<`i#@Ed zDA~+}Hsv%6AH*8NQ?xLh!P@TgX_&d49ts!H9lHvJ?eA%syBoy`7dSmfm6>fL+0@mE z=P@ZPmRz1ra-d=TItnZnF}HRrzkSc+)M`3c7T4o)<0@S?_hNT&{KqZ~X#xec5D?^*{e0{m(PskEWmLGcs%tDlMiF8PkU(6+iN(y=7wgcp6_QAY1z` zs!f*jKxIBFQZJDDs{;eN-r-J(7N2Q@irFV_bpK21VT0%xuFIlGBQE~BkG9@#LbDbV z8QF>9``^-X;7$De-lB0UhpwgXY24&IO@57G$K*UlfB42Y`z(@1Td?4l0&NTDk+0`Z zb?;K9HtWabU$*o(Wq{)oXY%?Lk=9m$Ik}ISFy0S^v?4mH{ozE%!vt*E&GO2z#C14| zYU947&Ob$F(g8L&R#I1H&dV!(Sh&oc{_Q4E+{Op>PoV^S?}W2=GSOG780@zI^?N~_ z8GQo9@$PJL8ODR{HYg~hFmhfB#_MD7uAffopEoqL*vHN1+j*#@jor10D0R3-{pUwC zbob_AbZeUK^2Rr@fnm1YDA~J->$eUN+;Jn$lb*8n$6&(!AE0OA#m6`c?hf3^dh-Z= zu6f7R~Gy>=Z<%r>=eTkvm$n^XhDffZ-%XiA^qJ_TAJIU z*X1c^)>#nUUxV(a$C2ulLEAl@sH*RU{`O?LWFEpY@+%e^ab)An&xhAAvb#ZLz#Y_o zmtvNGiRH^OQ5c)W_CrlkG(Ahp%p~+%nQ(Hc2jS1E`1ZClHp;?+ebc$p`8f6G6)C@V zp8nqL8B^emiS1~<* znBwG(lSySV)YDKZntEkLoE?cexfq#B6iEFSlqKa z?Nbjk(ETIfe{=~_y^6c~aIOr=;lbunWN+NWle#|`Dp#`3HkHu_ny{;MCt-7i+V5NV zY59tqZ(ef8F%J7LlNokz4ebl(Q$GJ7!fkZWOFn!AoO1N^5$NY4U-V+vd?KZxY(};n@EQqwer<-gfWF zLgy@o@43mY)%UpEP{NJAwH)uVh25>ZxOHOz!8ulFI=-N!sR~+cBhma~gZ=rfs1IDv zyXqFirW$hCF98+%ugo;=OV!3=3aU(3p(R zlu#Tr`w}$bCUb{pud81pQ^F7bs|KPFz8f(ZBc6$UFyj~?@~JU zd&LPGWi-+o@zz_LXGf3l!QlzkJKynls{)>TZs3-`nb7k)sPA-~H8H-JJ9lAVq&F^S zPoP#6Mel$bmKFWLyoCbm<6rPX{R5#|`7G9-E z_T8GSK2m|slA=T%P>o?BHQyOUW&Fn+hT@7>ml^}^BwONn+Yv6B+oR01xIVK zYWaiNzLo?g&8Ah;NhJ0ANta%s{95F~guQDpz2n0C+SatGTgp%$6TT_N;N0OVqe4^I zR~$=C>`=ze^z8rYgiikG-J4Il74KCNHlac8{b9)O+J&o|_>cY?-LwR#}4X0bb=ZM7_^42!T^{FTK9{Ql8 zVT0>lMP}p=<;apoj86~6piGw|2|-MBxzC>RHf-@2Obf%Y{QK&}<@9spZHr~En=LO3 zJjv+Vm{K1t_U-j$=O`_XPgzJ|qxt;lb%x%3+hLGz%virl))d~wFxs0^l_-YUOam=P zj2Cp}cGOMQ{AkJ#)qkwr7>~Nze2g}Krsz!vF2=0l$?bUFy|=)rp%Vu?r||ZA5WSRF z&}Qrty7)cgmx3}^tj)Q*rU$A6wb=1CjUB<;xj3ST^JjE9my(Z8<626UyzqA$M^RXR z0yU>&@v;HMgC3Z3nk={Z^lT7r+`hq)*)@bKnUVNlDd!uF;oIZ(G&vf~psiX|c)Fn& zuf`_TrF>tk%*P#3JUJDHsYeMpCtIL;^emBg4VkCBp8IKSF?NY#sDU%PZPj_%yd$0W z*OHb0gzBkFxn{PT;tXH%G{)d(Kb&??SL3XZPKB(a^RFMQm^Buitb@EMN@Q(XD*`tB zVep&Hkk5CvDL6d|K%*qxJb*3%1EoRbDGSOOZ$iaKVIAuG7 zYpXu;EqfUE2AH8RI+i!Nx>R`OvBBA#oO|}<#Ej$8ixqV0qsQIPckm0zrboRuv0_2q z_~#rumPXm+vz#@#g67&fz8sy*l6D>V@jZg*YisEI$(xaJ-6*#%rrFhRv^(vNo!<-I z&&?!r-bemE-@}X0bIhr3in>`#D!Z?xOL`kjHq4~|xjfEHH08eSKi+)0z@RQu`FNux z@huYg^;{X_fr*@~i=(!58Ho>OP~>LK_|#OARBU;B-jPL9&!aY@2@k*gCf~x2_&X8Q zJ@Lcwcnohl>vGB36yk|LXl0uW+ zwDvsDjKMBENa#V=oII|bO=0H8U3@&Ag{s0oT9{17!lp41b{bp{ea8|jZM0IO>8Cq@ z#?2n{VonUUrw8(5P#9WAKXLPgI+K3|p>?X5=~i86eYOkA-}__Z>cq{xZMdUv!5*8r zOk7fi^ZvaU9%#fEmm*wBy7F_%3098UOOWdpGOsp!1H=~I6d`<55LPE;y{->Gm|9PZAdt8*R^T14+RFz}hyf?E;wCQCT zOTOy-~DHkQC+vsv^P*1iiCy-s?L0uzF zv>!fYmA?-L0c)9fum|mXE@7$fDwJDxrf0_-tfo$2Usw``>DNj7JdTC)XR~NVJQ-2r zndqy|q(n3R^_fV~k@l?3)*!jsnN`MN)PLBE*5)88pNHd{qfX~vXDMk}hqI(HC0Bv0 zB2#RpHl>5YZ6bBQF{9%m-p}?y{j?K4(_#FD71(7cq2Bm1*>h*oLg6a+!=K|3e}WH- zGcX*qms{OiGe4w&=+*XfY1iK)g2idxvwrZT*A+}YJ^J1E@gm?!L8hea{5lsl%k%UvvQ%te#!^gkc{K#I&#!+jD^$)?IWIfN#1NdZb z!Y^xWhHQOE?)J{CI<^`A(p(yX{8?E4n(NS0VrKWxD>` zh~mA4i57#CsaT#4A8{?=PLEY7EjG1eMo$U;cxI_~1Xc;E^U*T~~$eWYLFBR1o9{S!Oe_+xK0m99D|tg_U>^WkrL=4Y_8=S5s+EM}@-E>1&z$vk}< zwG9hcHn}D5T9*^$AlMxJ!h=6M=xo>lmo9-MKHEV1%HdX@YVM|Sv~>*^BY#kqtH<`UX(&82LD{{8PxU!`o9oZ(o@Y>U>q{H^ zBWx(E)Qup2s(*s((x+tZl*NoGoy-)|Mp%Zf)gRHc`U_rRL)ce2o6OoC zB)`lY}{GO&Y>fy7@o`d z@je{X9?JMG@sxi)#?mJFglP}Oxuqi|`{tt+`H`7%8`<~kG8*A=+_>|UaZ5ufN*+Se z?t3h`zMJY}$_&l_hUu{=;*{6W^?@54Y(rtGK4+I36TQ@nDtkX#ebvSN{#5K#8l!V2 z7q97!NH#jf!R{j&ux~k9Nn6Rkkjw6fEi?|fK||Ra>SBBG;czW(gO#xStb+I9!vt&1 zWbJ|O>|W@F%a!p=+OiQh^-x;cpX8y^WcED^=2Koi1+Vv_H?fctntR#0%?S5V15h8E zOsC_=F}M}Rh8d4p6Bxl0r+z4}jb__cVSi>hHEA8lJ3AKmLt@%3vP^4fl<<%1Q3vQ8 z4r99x;dMb41s|6&EczBsv)iJ+ybcGwzHDrEizU956in*P7>B-S8ct-uNMjEE-9h92 zTj+DX9RrPaF{S-K?5E~nK70_@L)KH^G@9z)r-&T-p0-5?nN~ZIKE}yBIRA@0uk(ED z;KtT~K>S;bW88f`Ds`ICqn#^hH>>%!;SR>7Hsrn^%ZsySj3}8*vd>^t*OxLo=oWX+ zo@dw1FnX+c#hCt9JlME}WYs$K)Nf6hh9{bxg>9K+5qYw7fTm^^c#>$>TmcwjP9> zy~LWs3t4UOn1#b;P#l^_uS#W3ZV2ICA7#R~<}#*l0wlcCYt7w`9Vn^2%bpgIthb#^fcahC2lisP z?MN22YJqo`BH9~GM}K;6(!K5Ja(6Bdl*{oCh-diUp%@fAB+ek6ZnvE=?mCcDUcq?u zZ$@7173xkJa^-RocW2sg*zq~Xg4&=y)ts>785BH@r=H=p(!;s55MWhLzjZm={ki!mE7a`klu!|m2^D%}GMj~YDw zCb7iGm`4GsG*o0TV88@6-`**MtocTYQfuf{QRn2TCR}0^xu{*w zIeQD7MlNP)=zrw++B0pTHzm)$@okqb#v8{le?SBac3TF=Ll2YeqZ%ssV76s7*gF~outr@d6~y2Y7S)vQ!7rO)9DHP^5GPa_Tg zbA#l#ZoekCj47RRnY$i=yqS}Z!roczJgv+Rj+|caZ!5B?bS`$Uca=yfgAYYbQ zJY{x!4okr(c(E#y(J7k8Ykd10W&A%|?T zzo@e@qLiI0Mj}}InP@>%#`|yLkJ%-h-$vmdEyOFmd<4(+vORSdyW%D=cI^lZwR}-& zZza!mC_>j&m?LaK(HIYAgulbRTm!@08EB_p#F{0TiJ=$-wPrUZiZB&yuGxl&H$$9ySG$Nin$nS;#zxC%BrN;jyeWi-)u_^wBdkBwx}$F%8GP ztu!{j;i5z&^D?ceR7j?O#3b@VJsI5@fot_l;+>vxu??l}wPKF>?nBx%=%A7s?kfqsg3BUbuYwMD`1m6jr=(wG)@R7t+^3N!%$95zebyeJGr~2aAe13 z?%&?X#L>UWpE-z*oUaU|b+az#1QBVHY%F!5R@e(&*92}0%_4J|7ytUt@^E-DbK(n7 zeg2S78mjE}+smLoXL(+>kcElcP|fk7001BWNkl>SS4Klwenn&jc- z>@~WG(D(nCYxo<(TLygVQ{!|@J^pPkkuKiP2Hnf#C(mQV5FI`pkz{%E3q+?I;`Dki z5r1wn#Ki_}DP?khj^RP}8U}6Xz~R_UUQ6!5S3rxfY%9(Te#)}C683%m#US+?Xl~v~ z^1@0oHvHhZiYLE!N;B(uIj6ttGx^^uGG-Lewp5SP=jI@BN}K<-ogr#rB#BlBNVqMB z+QhL;duD;)yIKx6++n9=Dt~>4b2`(Ipu%@ljP9X3uaDkqVR(OV#4`62)uOe?L|AiO z(Gk?+8T{laFE>Xq>`E&~4VUA1^)GMC^AHJ}L-CiNxV&1-Og|?K$L&TUZYd$-Msrm8 zI|2H#Xq>&pX#E?+ZWrTIN&<&6%<$@R;YGCuXZ(ioHKUD&Ycib7%|uV?JH@3x_&CH8 z$3=VbIX{-vI03eK>+|`EAQtZ@Afh5hWo;Sv>VC25z8$p&|8c}ToI-sOww{)v=eGk! zmmE-X-^y6gx%5Y@W6HJ1Bpm(5UU3)d%rmJk`NYz7GqEig&Dy(a%vQDK`4tsxKP%GL zD#ncsoAE@47+D$4v?&mg7mLH%SpL4)Nc>PSHg^f|J9Rn52h}-oqL}zyb?m%;j|WQz zGw|yHU&fj-tV)nYXD%_``V;or5m=B&Kdi7 zQ?!~{@lA+pyhGvc6K1U3$wnm+$^=xIzodrIt3L8EG8EOUBCgoXr6uG&&VHYf@A`p% z!8?Q>97NT-go}w8bT6CA(cIf~bk%c981n!G#?2w%++#MbSHNz1KIU4R**#AQ%ce6NDOy9zl@QK~#9{5%!Rn`H zsQCDlv-P`}(P4>qfh8ZutfFsgH`O<$BQIjkC$DzG42&5#zny1^B}gqb z60@G&`=oKUXhUisnJFx1{*f$XzQm!sqK_THik!N&gC`qq;C3#7mP65q?Y&J)p(h44z|-F-}hmjn&3)OZ_HkGsokIMK}VLLbgHeqxi}aVj^Lp!r`XnWp(%X>mr( z_!C=1x;W<@g7eMkM7*&L9rPjLwK57) zvv3)hi{WQ?oWd^>KcI}Wo+Cl;oY|hgjBESK5m_>UVV^VUoHvfH7q{7b>oD<`tw>Rk zWZHN?{^WSF0w<(DJ!0qkb&O2@jNuJM#@!snq$OVT7)>GFcQb8^jn2C+e_rNR-E|6%_)%#53R7h(VodMw_D~;RTx^j2 z^qt#lu2A;I4CQ(A@jc#%uSYGbb@$>id<0(|*Hb%XIgKxDc`Gi#wDN_d`Oc+i%NRb7 z(j$EL8jK@;(!Mzo@v9?=9-csmMLrpyU(q#f1`g-SvAE;JWO;S&j+@WrGdtMjG=hRt z0rU^|!n$Puquo0(KO#%7$Q;Hrw{rSFeM)a95~?Y}rKP2q#pFVjJ_Bl(>AoUlSSjq5{=&&8iH$ZNaWSgk z@`}kw{AW*pLJ_UCN7>|H&248>Dyv6Pexa1&`p2B>bz@VFIBEwDpp{*RQJN7S9Vaov z^#`(!%5+M3vTfF9Dzc54>H~k@b+Yo9KVDB#ShQ{kBZFi}lpl+MXay2Zo5-9q1lM)> zq-gfB^o|3niYu@R9$=pQbYx8n>FklE&OeFZSJhOlsN|39Z?SpQrL zf5lO(@08}@e@9sIZWn8B3nLfj&FWzX7%V*>uaE6KcOT4}+jEI3wI;r+g>R=Dsm>Th zeBN1xA08mV!2=P418kmoiORqKvAD~Q+^>Sjzo^81kr3A|ePi*fWSo1n_>pYMHHiSu z9X95yeh5Q+|ImJR5(%yOOm0x2dB;WK(?4;1#ZJ6L&J(ak2CpxwBn3E7|7stCV%g+d zZ=?G9WImQZr=$r8q2)ZheVpwYUL=eY zW;|vr4IG5mxbuWYWpXbwmSUvpR> zsT|9rtO*-$&d9d?#?xJf@F_Al>;W2v;I0z8pPacT%4D5xpRs0~-|R9lZpF zk2^W|ZZ00m2e4^#L_Mqm*V_Wv#VgP;Js!W&j(E)2%In_?kk433wrD&pBkJ(HYKDl` z1bl`a;dE9YyBZ@1__!bO``Se4Y$R6DhP<@le6hYq#OJX*3?0Rt)uGI>(8p()GYRJu z@fDI}zE>yCY{7QfDo&*hP|;FH_x@Q330d-bnjDhd?@*M;Ajq_Yr$1IPU^thCmwjwn zk80Z`|OM8TNmQ|$euH8#sq)e#gFlsM2g$fFx-uO(+$}5E);7MJzh$baNFhy z;;W<3u0BP5=p~eW92s9LN~DVgXMcN;RIrky!6IC_p@NC*V+!OS@!_2q&wF>!mR5yZ zZwOnu;@LjoBA0I#aeU`EX}(G(V#$<^f&v zXCpdIkySTs7$tk0ZvRLWC$(Xe9?mb>M1HwGW^0@Rv7M99ShEQ&?Pm7bZ$k-c8U+ER5QJi75&@{h6+Ma=em z!S#E0v3eT7hTTHEaXdolsad33t8>&bhDRUE(3@kzkg8f_tio|gGUu@VS&|Oef#w4Y z-;P7W$&)vu=ArxVEB2jZm?B$-uGt@^_N`+7GG5fk z^3Cuz`z(I(EOIv!F8b2lIG1^Ax(PK`Waqsk4({t?u3ZfaYd2xF*oMEGy|@(bj>1

;YU?70x6&dZ;_sU?>WQ@hzd z?*ch?E4h8L5!145xKze6-0(ivyNXb8{)o$uBNPY^#cpID&Q0rx2tJF_)^gmgFG5zL zlAl$DYzzuzxWEexRP`9GG?%m&X$*gTo?BImxwobifvz!Bh8gli(G1s31{C`Evexf9 z5)EIOHd}|do+*sC-NCK7HwpUr9hH!FR%E+!@L(5*9|)l)bB%z{lL(Yl;Nb~h%A#y>Tt`$*02yY^?D6|T-Ks8z2;O0S z#xNqgN3k}ho<}36vS?KUKG*J2`lXxNxMe8zM$qTlPgl)z>YiWYgH$=f!*Ad_Lc3{-E3OrR0y1v#i~>#wwZjwKBWQYl8@+aDj{KDHWuoG zDP3W~y&+4vHR3&$NA)q!FX#1JM`~uDV!K5OtBOAJyYoA9!#Y?abAz&hMslk2Igo3N z|ElYZlX{8b`-vD|+fUM^y|nxpP0_jl#OG}yv3WXPGZPSu>jR@~;v_1^l>e(c!g$oYYJdogU z{6~@kH&dwpocHf9(PXGant22N9EzAS;x9(-Z#nYxB*)YoC@ZUG`y_XiC#&&#`5E%O z#-T5-gUSa3?xzmJ==Czf65jB6P%*y0y)fFlf!-(fIA<@Uwm*gQCoW^D{*0Z)dl8zY zPx|F`_|K4{LHsdKu1`j~(go)&5%~O?hTyzPevAm^p{NuGMP!IB>qYIQH;0DQppvouiKA@$5)~UMWJa7~ zZrwKKRGj6RvMP4NNAdLd2X+P4^T+HQ<-x0o-R?>4WKCoT%|c@A4tht&acwvZSNMvV zj33#LD$y@|$b?lj%u(^@)&7ITj+e*E=ot*|qW5VTjiv*v^B>HYzG9ZSi1A)w2;;-% z;NQ6gllD@KPA%e#N*lpb193lj5|ihtOey!^S*00cJb&;>sfDUPbxe7X#}F@Xu6)+S zKF*!Vc2&$$-iY1B>3o_vhmZ#eJp8N8IgL|H4e2ICRiA;upSbmC0YWZhR0ny%O$`;h3RImxhbKkflua8>R@YZ#t+F}0*_ok|29#~?!QjJ5&W)RZN6J>T_PJ8zH-g)`naq*V zrKslw-%k$W{<;&iW^Q9d%y+IIv1Q)%Kvs>HLTcq3Exbr?))!S8+uS9i=Lc6$~@ z9gTzx-AU8qeT1v|;#pqGi}ra`+a2Kjw(LbxP;aO zDN+i3*r+0gchCfKd_!4K(@yj>5l%%2VWptK(u-OYDIe$E`mfxyUrAB(1w=kBCd99b zVOm>IN;;3y@457?sAgSUCO`C~nUZr6r8{@|**}5y+J8i7cO#&BhsF1PVLtf>f>lQu z%Qh}luHo&u)fk^G!;)Yw^{}hn^=aGgRQ+@a-Daf4OIXJkNBl_?TA}d2Vacvc|#G?7V|2v|( z;+(P{g?p?cYk#F9>;94Phy{QEeUmf)ortrA7sB^{}7B~pgm zV9wlA{I-nZdgn(DrUkI{Ks;(ic}#frfk%leaXB4F`^^A^Qakbf^M&h~+I*EB#pM%i zObk1M@kcTC?GfW#K{TIJohe0zZmlm^FP+Ac6ZKrK)+ew-ijNsLnB8wr`RL=Ex_OAu z2Xb6@bi}mbH?upQ;gcPL{3B=FhZQk!p`PnGvfSBuoAzxQxHbP{weC3H2JOUj%|T4Z zpJ#)XEVoL&k&_fdRzffuPt4J_Eaqq0II4VB(=C69gLWe*3m%Wao)Ki{ZpCJ+HkbB~ zOKlC?Zi{dl+zhT zRL%6K;^kHZid|W2xCVvb0>Y3~zJ7Op7{t983EY>to_`Gq^&c##}hEN z(B$+p7t$4?(9E=D#~l&E0>ugS)?-}50?LL85BnbzTs4eG4&n@3aGTI(d#pp+h*a5! ziK8UJDQEL&V8%(+UTp#aW!f37;K~Rndi%b&G^WV)o z4FT56-sE!JGg`hRk&-1w;gP|lmyTw{*Y~Vd@jy#lfvgMzDl&KT{7xhmGR}CN8^MQN zH)&5)4{)hgR2T3|Nk=jEUh&}Pet z;mA4$c`jpD`Xv6`8p5aF)r1M%B7H>*ixV%Qd3Fzpx^6twF-20ln)21(DJk5J!TI}i zJAUR_$T%jJd?ECK6IsrY_}#L{<w zLm_r=BhCed<>%_Nj7e(Kf_>G$8LEiQ!6N>~ytxcSe%B2?>;b58{=DEidCrc$I9x zlP7N&)9Z@p(Fz>y#i3btg&mbI30`bPSCKM`yBe5N^Oeb3XDD0tg8$UDc`Oi(f!hS6 zum5DLS{u@Hf1#hL#?O!MDUy22+%KCj%6DgFvK^hTEC@FFj>Ertrq1bLPhk}v%MS8b z&WSEL4W5U%VRFHOaTm;)*R=}y94ChMY(Yp{j62ECuvFPe;kXo%g`Jo=shB@{Hq=S| zNA&TbxUKW$dTj-!M#?xQ%;(;M58OGPfz{s-{&fB$P)mesha&j&Y8L`FvB*DJg>rKS z5jm!Oj5TN1*(Y@G3&K|~mOAxzMrCb6+hr>i3I#|Fi{R`HF@z43bMUqhy_=^JrShDB zMegK|ddH}c6qfu8#2}`Yz`YKnȈ%5Y&*Q1iiCdZZEXqbFu=cJ zIsfjOu}1hh(*ElB8W_;Mb|^=u?PgHMUL>T)qI0T@>!S`}a2`J2mEv^n2i{D&fVNE? zt2OJWk}u*>)f?u&R_BCi1aeFFa=%Fw6V1!?UaG~X`V|*+GcoL|WR$No-(*g+(LIW; zneTCkeNEb;v7|OPF$H-ht(}j6!BOf3Q}}atG4{rBoPX8DiKSVT9+suL@&@KQO)R+^ zLDZZ!yqfl*{b4T;o6Hc4EJUT$i>l%}_Wc}SmHH~IkB!4}U^#;30{o})fik}%7zggA z;>2DQA8BH`REFK+FOlvaU|eP(lO(6$mUNJoFX5<6w7WRPejhB@ZoS5D_*amcFAjWeigFktq=7RRA>!$C(T9{ zol0MvcPwS2rZhD--yqxhj)o`SQ1w$J^4<)F4pu^NL;xn9QMBI|B{A2RS$<=QXDR(3 zwK@LlC;79^({}$g(e`jI$DP86nJAwvXLL#lsTGIW-|)o2wTFxbsJik1PIg!mX3W8M=I5UPGNgBFZZ= zSWwu>4ToDqHbv93WgUaYD4{pm4atV*%$^>KT&@WxH73)3B7tL(Ge~`XfY(bzF!JkT z>(o8Wdg#rs#87UUJi@Ru1*v}*5#0NR%XQZLyK0JsX9Po4Cb9jQFS<|j*mJ>)+1FH= z)c1{qpQgNdF^7MrhoBRBiNVeq6gPjRbX6@|{H8J7yMx{>c7$$q!~Ui>Q)7Zzprebq z(=_Vs9$+ut#tQQzc%J#oZ3A=u^z9*5uZp6lU1Z0Xac%Y;eyJ;C^(lqoaf29?-@rlB zCQAL+^3*WmKNDqTK!^7DDZOjIU$rmNtA2zhIr%Y4U%j zk^Hxn#JSbjW@qDQbsC`~4g>_wCU4duww5(~Hzb^NFk70>=eWNd zxK+u`H)hEGmLt&$J;6iGH$gHv%H!?qOE9bU&E~V-b5J7bF+8_|1#y6 zKf0YYM?l{uiIEwXsrA*TU{x-Wx_MNngmUHEDntZ7li_B;29VOJM8(Q8~x>WmwHdq5aq-b}`N(fb_Xiln*_~PW$^f zD};02cs|E({@~Q5lSt3>B;Dx~-(KHDIj9a}HEA?T>sb|RN%PoPhNK2?Sm_>#S`YBl zdPY*SGjcLh=#)IkOp`~1IqU?CFPL1)8w_$%^J_147D8&dOH*^>iT8APwdh-AONQ2X8AI;5Q_VOa! zmfF_=xVm+6Pxu`fF_J_HNU`I}Tdu1gqPp`b^~JMrvC&1XNP?hmlbOEYJ_+w5NSrYp z$4mh#bGk4ZDaHkbOf=sfW0KEsu2s4b6ez%?_e#9oycy%&mFQfY&brgz@yHKDS@=D7 zey!$qaxS*Ym$4B_;oq7{_6f?Plx#u3>mQiduBK$fJC@`QBJz_airbGe|5G3xzoS^( z9nG|>S1J2%EIF|SblZL<$6W}qmyQJ5EMvR%O3XvrxaRtXpwB+68Zs0U^$vtro#)KK z9v1!m&G)|Jtk`sn@&18C=^Z08;R7Fq4cIg;6syNmX|n!E_GV@F2#3?!*UIs3QQF6< z(f^`|t>zmj?pVRK=x5Z==|Do|F5?#I@@n{9ymcS4G+UF7KjIv)S#T7((7!>sZDc>%kJ?To#5pMz5k*t6k3a*w!h(Nln@g^g$&KZ)YtPLfNv^1y!- zC3^XIZuH<}&IDY`Z?N@q9J;6O87pP5s7S-f{S_vLda z-gA&$#*$>M%VChJENuxfESuTHtnkm646X$uGsqgurm?QbG&qp9Yb)37hx5SeI+{^y zuzvXxv(r&zEwthD;8lE{Y(~@j-~j6F+&yfL%q)(|;q0!6kX5N-<~ zG0%*z87GO06((Hc2lJ&1`L3!<=ZgyZzq)ZrItkClA9!f^)7v!-_rRNcIctc;4hItB zg@`mN<$cpbVog1mFujaMaSb}h*P!f~N2F&rhb=xc$ytQ^8>Zm(VLF;oX6%1i8?U*N>5+TeReVQ+-dy#IKLcu^BN@`t*wZ2CB_HOp2TXSQ{ZM3Zx^H(XKoEm3} z2fOk+{Vb~ehO8G`%PlDhR8~J`{GW9Q2FM`Tluet!WW4RYd0LW&ebQs5d|1r(x9Utf z-ba6uIK|;k=&jIaUFLc+0$WjuH(~%|K%Bp-qz(>wr6kmR#<)X^jRlKXtaE}bevyQV zZN^OdDFOZ4`B>J&_OIp?YF8p8AcV{Ezo>cqLU3R?kvXl%OUAKUa|`t!O=x*-gvfGv zR>v9BJNy-U6V1sIJB-`-i`)v-X8o%X43rI!t~HJ8=I>c6R=}6LDdZX@5-vN3U%B7# zDa^#;%utqzyAe^IOo&f6Uz|qqThbfVAqGrXB*2^u7w+Y);QRDFG@jXj#}F-&1D+$0 zqsNmQ`Q#nbL96O8*T4JX`dorZvF`j9IYj6CAinmAAY4$4@wPFj1-Vc@H4u@?Q^-u2 z!!N%*6s{k{cmI5b7^bpui5ItvKJ$HSDK4Y>*xlxgL7)~+Sz$bxI+(-nL=j)t$U=*J z^q-vK*~)12pL=uFP?%hK7gT&4I6G+*`bC(sIcd}(_Sr7Mh9-){2jq^iivVWo3Ndn}>*R~>8qEhV<_1-paGah_}g9!VUv`-%TU0oKN~p*>|T zJ$Hw2;j9r4w(UavMJ6rvO9;wzML!@4vk3(h>qzq^z#fP1TbQzJ53Lnq43GEW!0%pOB*r=uJZDpA?6$isG?X zfvQF`3Kh5DIbWF3u7{X=VG^^`ck{vWE2EBNV4r%Lf~q9;Pq|2++(v@Lt5JM$8Q-qS z+_~t1_Ir6ce>$LYzl=VeS&Zy+q2=Zo-17C1(4Iig>JE+^_F(2|FEszk^8If%65|gN z_%eoXJ$0C@Jw~n5dSYf2;=E}B=M@(tZzD*GPBRlX+2DPrmyo5FoZEhoAmcnNvqoYj zWkg{^D<|FW@a9K2sedX+GgL?7*$I-I^D)j{gQ9#P(&>{qx#I^D+twlQyMv4K3Q!W4 z<+tTnT9&QERc#5I9Ur6ZQ;7GPECkLR6+w zD?x)fqZiJ+osb`QnsJ{d@GVIIyX8NSyQs$K*eo3K+nE@y#AuJdI46BX=%x?*Eyq#0 z#1@y1SR7p?dGq=nA!TRr<{7^;UeMnIvzC{#`*a@_CC<2L>o8)h338YBAn-YkW05lv zeCEfLEpAlV>_$gr4V^|kAiKLY!M`L2GAzLYFF>^ga{aV}ofo;03`=i~cR+;nISKk-EF`|nkX+*-tWI6b z^wg8|1f4`+T{?{giwNql=lX4?o_LdlMF3=e7(cB3A984r}j zw{j$IIj$>~u+8}wgHBANYtJ)EJ;ITmGK96xUPR9m!%q1+5o*>v9k0b#i-$CzL&}ku&h&@z!1f zQ&o{jSp`f^lnt2;ZjH>6_2TQJX zD)RQ^2OPzl>DeQQi_{k!Wy=}!*o?{@Fe7;uzT(XU#4O}y$`=M!??&f<99DAMu=??k zGy1Rb-#VNih1HDzDM?e+7e>m8LFAblTyF(%V0{;> zEx`4GEN=b}i9ObfVtOBA)!PxQ7)C?-K1NjzCNDt-xtc%Z<*r2QX(l`0ZbD$xL9R^d zI*6iNQ8@3x6TSPV7rtTk0Uuh z>IofrDP%PWbMe|}9Br4fr_lwKTV+g8Go-fv8s`OVn6+yQ6;bib_5?N0X_SQihyBj+ zNNFx(SN452>|93WLtC2DIvG5&2Gz_G+W%8Wul*-3TG`YOa>ht_H>xw`7-`Xf-1)1F z`96Zp!Q-kzB{~$%}gtCRl_<34~q~?EQ>c*0i z`Ixxxsl2}>%=+XChP)ZUf5o0C9&9Bs--ofDdx$C5A-8Bfp2-vNd$5KYt;HOfcLwK; znwSb5<&WMM!4+kvX<7dV>9 z%-gaQy3c2rlq>r_#iO~X4YAw`o=G@jQ??N6o6#&PNJC-r8WL0AVUax;qoL(^23|&D zMGW1JO}y%#$?Mc$;wEmwV}=n`Cr0A?u>_S zV!S*Nqf__L?jKF@Dsjey=rL;Q1LCa&`TV_yru%*l0DJ zjxnM14V%D%rf+-;vt)KoJ1gb);gNrfgKJYcNi+*?EyU;65sFqlp}a-{)8}%O$VHHz z?!X8A7Lo?)@TkybrIR_AhDIj`J=#ylHvE@ML`&?=8S*_Z2q3&%@3& zl%A?xWXYvq=_ke#^Y6HH)M1&I#kB+h^m6lXwMfVxkZ^j(Azl4iR^Fb(*12o0-NMkly2-(@lP-2+*c zC&kALiCCqGb7OTFRc-5to-l-~>-0(dkcO##FPV1dn6_gLhc|C!g{BJMm#C6%qKQ?I zBgr}i9D98i%k(n-8`Vqbz!K6{tfD*V5vqd>unW-Q^<-(PTtd+F>V%F!CZ7Ms-?M_4 zE48EXV<*o}$RK6XNaxadSQ+{;L+A|h$7XZUNtuOLs*t(09p#xK%&$nNtgwfx?X^sg z31??eA(5?(6!@=Xn9(;*n{8mD*iD8+$5Rlk#2u}6w!VKu^Va25HLm1h&qm@Lrg3@s zTr3`3;o+red{M6Ad`}8CdjE)XNancg3bNm=WJa+fCr2hQ=Rh#qCXQjpfiryQjUy#o zh9~g~3`~vY@!JdJhE*W6--IoO^N4%cOK*!kVT=5ja?p%>!w#_if)s%YB{-dOVw?5| z5@y}y)bmYbY8a6l_72M{=LswR$ET@UI2w5KxM)F!}F(KW@=&gH* zko{i_UcBb3pA}1Lexn$79pkUV$O@4m`1D;AeD-swXcsA2OL#VOCc_<%(irWDdHh$k4<#aa(z6(<~PmmJJuH?|ia<#?lN0UI)jc@k^46LU3HcgO4RIv667W&TJ^p zuP3uPY96*@_OieDK9@@zQNMVeJL<0~sosTr?r>HFy0T;L6eJ`%aWF9Drfe39B^g96 zlIQfwowOQ9VO(++Q>#U!+|=XHpldkX--3U87h%aFbTnw7zWxRCyoGT23ZF(VfWbTI ze`?06?9Z6YGUw;OdKB~(5$qa=UHlS$&M#$pQxh$-7xKkpBXTF2G1vg}{WP$Y%wnv< zao*ehAS|t)xZp=n`fRAt z`}+&e8)uk)_ydhk_cL+hK3e6!Ql;LCSff7nv(l;aJj;e(PRza*#((9{sMbp3(3~`W zt?ePbZ7^=Sdr3(YA>ZGT{SD5%@{Gm!yfjuZ`Lr4y#Hs%mYigj+d?cNOFfh&m^9pB{ zhc;4qLXs&F3t2Uymvf>5RGidA;p10iw|rreMnCia*b?9;#IKfV%&khn?Cc-@6mCWG zj1oo)$^`GI=E}Kktcot7`gk?R26ZvDQ-K(DcYc`MrQ_Hea&}!|Yj-eGR+(&63P5OS z9Iw=eGU-qiceL%m%bb)g-MkZ4;FZi3((Kil;&7Q4KR*#;0z+)8Nhyq>yD^Pi)yb5{ z_Yn7AKD9IZ88`5Oid$WrtoPxmgD*pbwqsbFfNf+ic1L4bWT$|@%|MEsXVWWugF9-U z*>p^Tm9H0Yf724SCN4tkogk9;mN2a}leUCAeA!~ivkz`)jB93A{e8Api(tGjnep?S z={9KL<;o_aWR`F|q8_P;e|Q#U@Tt9tL!(TIKQF)zV|$hvzeHQ|un{w2ql#{(_G%niF zBwcvGwq;9MBOQ&b=q02BJ%}9j8lk_+EcHld$7mnq?hQd?#Urxz+-Bak`AoYxot~NJ z$Xld_&+xf)sW-7CsF|`$N%W>U)72z|*_lzOCJjeY*n}-5-F%u@%d(&`$nLO0PWKa* z^$S^3Skp3irWX67;?{Z;7wiKaxtC*56&B^8LNnNEq9U3cm^6>}19c!tw=wbFj5r+IH z&c>BJTvq&y%DP?r66(Qy!5enT1|zcIBjSrH7$^FbT`f%ny}rUF%^_S-uS4h5FfKid zL-f!}lDo7p68pe|lzL`*`LXFq2CBN7X!DiARVR<8$({ro4x=II6iS+7`PbTyqh&NE z2}VfTMPYF65`DH=bcOuG%5Vn7_n!0nrwkGTf(ZCK;$GB<&xv*P=G(ABsGY&@-ErT) z5M|+?+`sga3xg)(dZCEB^blv#%8=cH?2^kvXZIkQZ&{+Ktc---4aT+?^JQK*!?Q%$ zduIbX4%^XH7|*=Xg4C96XUHKnLf#&ue!(&nlB)S=SIw-T2*&uY=CqI(gD0Ki?*=c< z`>$hpf)gLg*Wx$2l2@gV`R*mo-7RbJ{x$F>M}L(JgLH-4r|m$#_f!)Elxz&n37Ho6Hg$S?u%oGO%zocLa2C%Kgc7xzVHvj-)ok zn=^-Q;_z`hW;d=dxb_Y%dKFl&RU*XKoZ-b;SUdSq?*Em$`?s(~$PUqvTIxI^_!U#e zr|}~x8sv}Q_gy5d4`f+f1j_n`oIEE((N+U`=7=(`u@}Qzw*0g{#0$B8Qp^e|`B#kX zOdBF6s&ZvSAPa<>nBzY{{sMD~MB8~|-^JE;F`SJ{>G2&q2 z&ORaj3r)nL`T*aQ+j%bH$k^OZ%xgAf!9gExjISa8z%k6nJM(AVdG@&!(hxA8g#Yxo zTAN9zktc`jKF}!CNtU83_0P8;P-sW;&te>()Zw6BN2K6H?g^K2D|I!=QA06%eTb9C z2BY=sDiK|xbR_Pfq2U3c7P=^@^kO#WFaa&IkOsbYb`(SGG7Q(UkO* z!LKSP{Ta?dVP%%(q~M%fMcILP3eMXhx$`2mi{9dKWh$E&oIrKxJUTrbxh7%H1Z6L7 zX6?YO=srpfDX49$$4)v4<8&h=S3IC_OgC@WMBpS+&9FX0)J`v=dXXH4%0WB~o6XY2 zADH>+8_W-8bLcSy>$OmQ(Z``4eFB7k!v^t4Q7cB2^07cvpIx8w5b zM7BSE##VWEJpc7^*Xkw>6Q}Wf_+93mi=gY}V9wuaB6vqTAq5{$m)*|Xo5qApspO8T zCcf<}aT}?}JDFkh?%YSU@j6Ov1espcjM42QtPY>Vz^Vx(y4SOI?0mF$S`pR0m6D>< zIM&Pa?sEw)!VY{gjAv|{G;zNqG4WNQ?$$Ouvx@ofGYHRYTds{b%_+4bOn!ZyKBs9M zb6>^TPnL}O9ZQ3C9TmI!2xxuC?~_?~j&UHU@UkTHf;)n8l zb<9tx5_3Qiu@~JGZ=H|AmWimI8H=*ZX1>}Np`jwi)9P#n-T%yXw>Boqit^;90A@o| z88xW>OQ$~=}3r{6MjYsg~?yJrZbxGlqo8(>1Qxyf6n1OyMPnoL4>yKpyTU#ery=PFIa`o{g?O>8^=B$ zSL`mtQS9M@rG^UH1DTwklg#ExyQv$|3Fn0P(sGES&Aa&{V8F|VCY-5$g~PAcG#BU7 zzrmlWLM7Y^yFu3cY>Mv&@iA2xD@zfwN2Ow)*iNqGMKod_F|bMy;cN?T47@;mkqh8`ct<@}7SCa6Hn+QzE#A zTDKj@B^#sD^&ip`>d>x^@#Ip8FgPdm-{ixuVq~P0b}`P9Cnu&g~Sf zDu-}!l_4lglmASLan4RhSmGWnj#5;smrzi=lZ;at{74OFpu>{!wSu%>oxsdEYs~7( zSa-dX%Zf@I_EM!cOd6vsBN{VwN%RduUi>iw|E)*$j4#CohP<{sh{fM2NFA5wm;Ph2 z+<$Q3pCu|~0*qF9%Y%*kuv#L{uNRv17ffML+X`g2-lu$=7@=C4EGb)qiFXZ3XAg4v z-g0DaUFYm`V;)>^LepFsskQ3#xSyrz!)sELhA}JBkz>Q!2=Mmh?COz-4|zwdkp!ti zclk8)6IM1BIi@JF{sM$#H&Jq@jF63F# zYV?f@8S<^2>rGJ{o_Us)zVV#6{2qfHt;p=F!DZhN=ASH}y>mNf4b(aA9fMF%Gx`0Z zY!i!Rh^zpKo;Ug0u8XpvHN&qc;?>}cU+PdsCcVV>WD%HK@Kt0PcW+)~(Y88lZ;v5Q zx||Qrv5a;sB)WVC-(4ig8oiy*4PRN@)qvm#R}?B2aPr(L);H@>GOfksi&6slRz0ROw?ZXo9qdf4Q%Z4MK>M1l_&%@^Ti2FTZ#jX83{aJ=WpdItnb&-8HlDJK~$^9Ee zxmGk)(Zg9Zbuu~Iy3kKDWADQ#hGrchaQQ}tFPEicRXZcAqDk5B&2O_jR1b-foT zi+yCZ4M%=Q1;*n(k+Sgy@7Gn4cX=)S{%V+dzNDr330f-03Du6m|GF!F3Ua(Di9_Ll zC_e@dL(#FA7sC=sJ+%Y9O{E;wc4nfVDle1bDDBYVM!z7#*VMA0dkm3bdr&UTBwl|3 zwemZOIM_wn(sF9{Tky&F4Ss)i(%EW{%H7A5o&U&m1u3>}`Gdrz2b^hX;qAM(bY8uK zyQ>%BNr&jaufnyXk6C>EI7JqsjQ&2H{FSUS+Hw3}TP+W`>%4!MZY-{p$_5 zW{^$vPhlo5TSP;~A-ZPRqLCDXu!StP^Tc>>_XJ0s(MbN+N99^wu5ZyoG)j`Kx%VmF zwv7I@{m4Y$Bx{W@ogG1RmgX|{=l}(dZ}=$n14q>YM2gK&aQ}eJr0-mEy2zA6he-GV zWeJn8U66&_x_BbGGsw}n%*2gHnUMVwqos0`w1Mnk725yJrdq9pc@YZ8x7PA|!C~xu zoOl;`o~Ymp3}4_!jAb>iugqf8w95#F=TkhsfcYuL96!>*`ko?2+!CPXX%EX@NOR#t z3Wh#cdH$^#lXX`Z@}D6E-s7;GX@uVCzX)E+LHpNoWNt+|D3uLZwq~d}Z>-RoFakl~Lts*q$#*vvhoJnttF$-!V zA*qnPL&Z?cyU4kR$(&u(z>%8SNEw~QA%6xMi*IuC_FhhY|HT`b?R=jt#;oo#g2QAG z&e@FYra%m1#^Re0N1SZ{X;U;AHMW2oM~;!=vzR@mzbHEsgsYVpcVEYGI^hv=VtTkF zL@~i^0zOOgNu23L>3}HT;||a!xCg-nkGU13iihu5L|?U#WfVz(@O*|$jN%toR33cG zIgL1sQ;o676C}gt5KT$F%*YP_`z#(6zh$#T5m!9NpgW+=wiTw-xa{ThiWk_d+=Jb^ zG5q+bht(f>#)^pX=JOTWKRF>6`zmn+Z5DfMj2C`MiW0d79= zJnRnTVT=wczQYi={DAkBJgj3%n9|e0-nn8V*+k&&_7C$24KhwFBtPgE&&Ss=)wTtz zhf|qrA&yy639oZYxSJ)x>)Vrw?K7eE+#9;>ma|sm0dB(IFj(+|rEUeh%a$beSQu>w z=Ebi?g8%>^07*naRN$a{0qHyyEcfS9lv~Hp&;JPWXhg1}ivE;EJa4gPWB+!XH(#Rq zf-x0?XHxp`6&fSAF>j;}GDCwA8ng%9uq{m6(!+w!-$>p%nVm!2=$Ws@mJd6rNEYGl z*ml%Lzr}y&HH?=0Vwl8klxCE3sn?Ts=anp3J%C1y4RxXNxQzY7OWAKMl#^wkY(B4E z++*F)>Aba4#BA_j?q8{7;>>>xYB!+Z!BjrxxN>XVWqvh@v*Y4pQe1B!9%#wwL$#=S z)uWcu&ZDd$j1^Sm#1?0^*&oKG;~*uM)-iATI&Nq7p%zv{X=SR#xYvp1L=pqu}ZfI5@#!|3 zRex~pZUrhG<~Y5tXPcrdXN9zJ49I5ZmnyC)?x4mpnDz;U7#1F)_-+>?+s`p*M|#k?jqlY@Fl4pP;{_FWQ8JZW>0PN0QUBh=+NBg!oQEquGHzkz^(vn90LlZ)Qeq z#i08{U;Gtw~xy=Jn39@3CZ08 zba_)Eroap`Zmi>C;KsGl5Ch zkD*($TWnb&Yn_ljN0LCAxPh&%(PY zA7seL5$o`4Gea_BKH8GG>=!R0!ge%`=LfS?FCT&b7PIQfZ=P%1r+MFYG)sQ*ecJ_; zLeC-6a2h$QpLm_E#caM7CJ(&mzO;zn`YC+Lx8S8zH_b2dIBlYg=IOI2wRCd)$9nX4 z3Xv@D#r93jxGTldy)=%NC*L_!JCW>(s$#+cdtrZw066}^;Yo7khgv2s*8bW#DHixxq=JHIaj#@)~Hmv_aftMSXUwAN9KMQ%sU$p8MaB*rQ z6`r%$;PjIEtS}7jt|lV2oQvyKv6_;CrTlB0l^h8F;7R`-y)DXhtQSBJ%nW6A=FTanbFr{M{k)LF6>bOVy zPfa!pUSjRqJ{pX3aG&E#U`i~BCzBB!8^*EluWZPfji=IJv^VV_!}1Q(aswEczl&IJ zYm%K-qN4sAmyhP?6=XAI&OvsX-{t9AdlD1gVcyY4sfIrFH|lWue2y)rAEISegl&rx z>qj*peOi@_>?f#J#u5?|Lru(Vj$N}w&gLQ^#!84EGoj|$Ee>s3gxsEs7+L%v=}auw zUYuphiBMYW)R>w0nFS$QXvg=_Gf{^D$zG1_*ut{=8(3j80;T`L823zyk1nB1?nq?Y z{&@D-Y(wct6%xO0Fr`U{(J5iXKJ!9Ku9fKK1Y}Jdh=114?659$mJ2aDB%H*B{Y3Vh zMng0m(e>{T9BRkSoUv4?EMb9&ETPkGaoxWJ?>VkGez?S$m3xWaQpLXP4Q%y2gi+@% z=H|R0Y@azrcK?{(eT{e4IY?TbBm9ayZ*zTVJ7b1`*kw{q_98X@24&(ch#s8B3Af*f zuhzott_cg}&(bkn4%gegj5$_AOwS4ir3q8+R7&)C6%0cjGyKX7l-2rac2{HdXeSml zeqvVVc>+gh=CfrJr+y}J zWZFIE9W>+hR~cdwgb}N4BYW5+45p>vq;eJQbw4<;FqJhnqmZtMCB*9$nhlzK@O9?n zt!_3<)FSR`7pskRNSLEagyJ$rUl*b3bTl14W>jWr^7&5;@vEni{^$tqZ{G9lLnhJQ=#Ui77XZ5x3r-Ki|99YZH#8$r5HAIl;qw z(mW|oq3r2FOgc_;(f9${I`(Y-IvdlefpmQEb|& zgPK7e5*6Cinlv)V=RJ@9C@|~70AJNNGpZ(#m-gp4;+Mt3d-Ji`q{)>@?|GA8#=57v zY+k6rFq1iqe4fgjW#QbhlVESjTZ%VWQ>gz9vEibW+)1BzU0%_V3g+Fpw+++ zxrB0lh^LSc=gy7^_FP|Hj!u>o_mVatKS_acwbm@Xb>RPY#V%;7{#4J9k?cx+#RbFb zB)#om$6_AKb0vwjD#vf`de+tKqMphfmyhl-$dPzWM0>aGQn*kwew?GJ-80ZJbM=EZDwDZ3nQlA zpnudWRy-L>XQ3&ENtyihS%GMz8#bpD(f9Ho=8hkK9ar(eu7QR3D#+Otgxc&LMt7Pa zzH=-9RD^zLq;i5;!7wHv&Y+ECfS7WaAF4Ge=ner)WdLtD+C3rQFmFE zyv85gme6GV{Y0ir@We{Elx%w+6y(;>dc2z-Z&vg9XbKAj7mzBkfyZsn_;THe4%rZ_ zo@BCGtdBQiMiN-Cmx5jHjA?qns|ssA?jMA`SQ%C|aRk^D^11W_z2eufUQ&+VD;tij z+sBl;AJp!Pp>nSQTkjud-RNXGhR#7E!J4IigmJiio9UUxC3BTygbNx#ks}Q98YY+lG@udD;!2W^F?02o@u9__FWB#L~*Pp~COZj#n4fVHy zyqy?<%z|3}8DyfBE06N6y@VNmhCGMYf;V`1K|IYP?Zcs@OJ;-ilQ zuA?85{qzT&0d0h}1>m-#fP1oCs4czDRB01}WA9+~;Sj4=?m@NhB|dr=@K849x5zmZ z1}9PWcnd?6uJCnC4%PWBXvkQix7L@273st*o=ar8IW^stTy5UU)YeHP9{fhP(lxez z4P$NZ9;90j67IN!__sYQFB(m3Q2|R!@;JD^k|KE<>P9ZZOYQ*&-EYyCH=He9(wJbv z&B3d9Fu{t~(>pM`b%m!P2e5B{#`nky`n(0uDAz$&z8J5hx1`q#QMXEu(DYwCR(D4D zu`t6Nq;bygC4YwwVl^S$9P^j;`N~Ld&>*Se2j@bzu+{M(-G|OlZQzWMwH^1Lxg)A^ zo96vf5LqF^w!|f@&%e)zKws*$0_n|jr!7R5mv>&0D4l_J^>T)qb|W@Vj{*g09>@&g z`}D;;)v(~o^*(%*7PD|xGM|=S;qU=r&iW>>`pkcn^gQ9s*>yacaF&>e0s6+(V*Fn$ zzn`t+O1B*ngUVPwuLX}hSGFp;vt>v=Cq|57XQ~ee_V3_u`W0$q4C#I)L9pi=EQ}5C zE)AkuY9`e=He6AA$Es^$)S0Yc(N$ZfXs6(K_&yH`W+60TB^oQ2lA~nBkI)x9>dC}4 zaTOjbyVzgmO7K5vJ`8GPjqVQW9Hj8llVtruWqzNs;rPj`sNLC#Zo_L_90l?A5aij# zZZ7}Z&ey*IMC@IISDFeJ7R@0b*axeJ%Smg$%rPF(e8L^wv`!q86-bI}rNXI$tuhj5 zEr?>o?R6C297#cR6dP+*xo=yI=RXW>ZfOv5RbQwY<%atr^5; z#i1mbN`RLqh$Wyd>%`xT7V-pFa4TPs!TIqV?Dj^yVmmcP57As1NQUYULcGH{A{0lW zuRg_}FY!&Wf(5UZvo*sAb#G0iZk5q>tB=HsmoYOLPn_&dBokKP-xMd5;Utb#)coOOvop|=r?+Kl~=cPyIP%gl^pgyvdvORO26quu=eqr(QV z1E{;qWoyDuhE_@9k*|yKz7-hlPA4p?gwlQQdD)%MN&DTr$=S-ZSw(22%kZXQI9@}% zSs#*s>8(L%IeO8n9Y#WEDkEGfY2KU2=FoDo_B|uqw2jW14;;MN&4Ynnif@eLzTaN1 zW`^*h7xE7*z|~+oX~V7}Qq#w|%pWwK{zCBZYXk=WCN;m0QC+ocQfi~@$OgQU;`uc) zj&CjIJU2SXO7C^txa7|ata$QwBHv^ekX~_y%7+Juos`G4+a2`Zo6O9@vy?oW!V0Zp zOig>n_`e5fzq%5Gc_F-xzRHu`2MGU@OT>3)?ksS?ZIs}R+=N!;Z< ze3<@{AwPt1$?xT1`Cg_y|3~BIUK9sk;dSG1V)ob~8E}LR554&BfEu%IAI9NdF-`Yv z*|@Qr&`))=Ouo#GzE#K#xk2dc50ngV!gB1g)N_HS^b#f zWwreGcp<*E?aaO{%g{B$`5ix+&=(JB3lu>jw2*)Q=} zH+cE1i_tqQXgj-v5flB$5{sZGe;ccmD=|;CW9x>&97u5EzxYIw-dQnqd@&zHQaI{h z!P)vU6y$aAmzKqJK?mE+ry$i5fQ-ssOk2ic7V($SF9oTdbDjF%|FOAxJ6`%>j2>0N z2&rUZWt|8!^G5lxHK~J|+4uA(4Yvefc?TVDC1?r|!T^v_00Oah5d!H95|_q#@pw(?$}20`&z_JYPdIhBDw=^EWfiKy?xoF z1*%iDpqSsfySe0$O#R+sUM890E$Tz=1QGiEFCf1_l=2Oe`6qt@A#)$5PF+oVYcfL< z{V^8N5Oc=89g48acxDZo zg=B>SnU8fzUv191f5)(UnT4M~8%=sb?2OdIWY;(RS9qg5$cX%ox7=_Npj`4b!G^DK z&*?zo{%yqLwRv$Tg*CRZJk=MZ+sK=kC?}kyc2lYnNL9W$CRH1Fx#tsM9?DF8s>7Q< zdgMNTOV+ta-c|oWseV7*`AHlx7>atvB3}6HmjhPHDxBOgkMYj>6joHS ze!2x(q1)N+u$AQCW4QQzL&98@B;!U}syAaE*Tpb_C@dOp5@#_B8Rafo(>$5>Pl!2| zV;MElgC~`Jbf_ECbS;8Mv8_1e48wE2Ip5a#V|8DKsHR^GY*<6OYdV2Xmy_Z32kFrX z2$Zfvp{E$>XLp$GGLmnnF5|BG5sA+?c`NXe!#M>^-XF-(S!!&mY{t&=8j}kR32Qjd z*4gK*)uurU3x|Ixk}!oDdSpDiSX1VTu(5i zTrrJ_pKg+!mPEU1F@rakF*GF$x%+EK#x>u%1wXnVl_71?QQV zG7Yt+F1&YHl9)Pz#-oY&f0H8PaUD;p28fB0=2(0V#R7p`d9jwo`|TlmEWXD}DGxWt zAmcBIlg^XW(ZalhKE`jI#^`rt%>Dcmr90`Y?>$bSlo&OC$~dc7NzC;u^8R%*!mQ0WUUx;U?w;3ydkAEhtonCeEO(KaGn;&@^8{zbPO3c zTYkN}7Yj$(GUH&E89vrpz4B1Qy(o49XO-%569bP$llqFk!Kn)l1mvaG6cb8 z(&%Y5Vk17A=Yif_P$|I3KLF49JGdcno$M(F6k1K^ons;%&WHGTbOwLNbh7H(4{B7V zbN~85x+(;jSEJ5xOAl6rE+qGtDZ8zbxp=M;i-;M>%+qAit?QIliZV&FgJ^44E)_3l zq_sWbj^WI>ZN)qpEn3H0GVJ_f?g<9tFn&Ej=4TMq&cNf{pTtb*(NkzA4}C|H3UTpX%ZF1zb}+{HxoY3p3l`% z4FYnqDE+F##^PJp%^i=-r1@-n?uXDGTLO}Pp#SGGGo%EmS}scI$w;<;pGmUJB}(7B zvv6Q2gJkcM;l7hQB74!W*pF_SA$l=pxD?w{{OT2(hnXQU-jCf$N`$t?py#v;r(ep< zNn1~wc`zN;n<-s-n`Pp%CLS=nLx!9!OD|3!+{l9~-M$PceJ8p29ZfE$ zSZ2`5!c`gEZ;oS2gee}jezdqWGOEuOy|LE`ySN&iv;zd}8BJJoM#}fhXU?Ma9PYOw>$D(;9t5+}Y%+&*zM-Bvo;TXhF^s#zZv8r(Yb?1p zP{NI8`&glQ46k}qbUT)D&}su}+mJX;v+Lhj-f{XBm?*T*|OY$G|ewam>nkEZ5eg3C-`zi2$ex844++t zZP+6I&fiL?r8w_KDw3+=$EQikf4wkC2{u$~9oH&brY!<9C3>PIa>D=31s zD^n3YnaYCU19S;@ur_lLB~hOIoi&dYV{fsr(1G43QY`(l7vH;!DOfiIv8n>X=U*UR z#|fcKWg2_ym|>`m;`B@OY?LH)tpj?JKKR+ha4#YUwFm#$sWg%x_v1`#sN(0D6yCjG z#=gl~q-lq7ai%90Yhtl`zJcaQ9j^X+LVcGodXZ;1dgvfohP&CCzJ_~OF0kb7PR@={ zrF{1c=F3h2?|kkQU1Rr*qeu*x5-0r)n>Dd$d~_po`#pwiIL6d_IcP7p=KZHPBya8J zeupnVb#CKi^opG;%BZ?w#*Bk`NDgU6$@MUgb$w}25u#A39(TDpCX4A#z{5yo1e{yxSlG$Yy;~SQZZn?VV|c1r2yK3h zc7H;MssQc2AMupe=FOiutX?V3tW6%Aw7JCClg`L}dBCR7@zh^(;igIjsp_HZc4*|J zYXCQ=c~b4u#4Z2XwCs+g%6c*F=O3c+q>Ikf4m|o_vu5yM;w;ZFZJ9M%{~CySW=`wm zVE*L{qpCHYFps?~5r2u@JrPnb+ zaPT~WoZc|1lUj-Gb!T^@HvL}O)PK;TLH;nAu9Cc0Ok=Lw2QtskgRemxh;~Hgxizn% z^Jrf1l?MGitXyl$$ctK}`j5t=c^BCSp0ii#23MrqC^{PQKd;#R&p${f?Qfx8c#Mc$ z(!BP5!?FoZWIcb5zFarKanQFT6Q%a2*iJly*r`^uqvmtiKaT4KD!6|cN$b8vJEscSc}ZG>Q*jL!cKuil4A~`k0B4?uSZlfZt;EI>8TU?(3MD^?*t?b<;^M?=>PyA07*na zR7Hyq&VQ#ebFUFkjbWT}^g9C|q+|H$iy2NiR;#9B^7v>%2pidj~ zOCO>c7mvZARu27<$L#NHteyq3#dH`u|13s9^C8LEa(s{IL8hvUkN%U1+>^`N6b}ZH zH{<>MA0eY`xS(9l$7)YbJA06Av=Xs}Ni=`-AoZ>q3CUw{?0JOLEnl|DrSLj`7e`B# znRxUoga734^6+nV@7+o4L4D5qe#M}onyqsl@$~phWHO>yVjqvs;1lR4jUZcZJw5qSqo7}}TwMmR9F{I099qExFqy;x1R3l7d zcO!*w)M;N@M@Z)}`Z|M1(|$ujm?PFbS9m3H1+UbbNUht(;z$K9tuLeFW)Ck$H&R(} zhni_mxjA*Tk%BEO*%TZ3)rJd31a zy*o;a_n=d8iM*9#I2HH_&)}ux%Xah9se-2$cVn#fgk$S&p{BnDVT=$kEJylN1}Eg< z`cO;cmlU!8$RuJ;h_U!r8OA0?7zoV5v+6tUnN7Htm=ZL985aM2z*YD#x}D2-^5G@1 z{)S8_8cF+IP2Px1p~!v`4}WWO^-UC~1loxma*jKHFCkno2^Wi0G!;V`aWWH=$3N)} zF{4xM1x@8Sm@=LHyX^_R?#7z=5zM$?Mo6(3nI*zxMt~{%^Nit47^KWHad8&?j9~n72ih3NflcEc&-@M|0A8EFYaF(AF25 zeIV^6LvP9pcD-Cj^L$s#EP6QZyPH!oGU%80V;gOTY~*vce9pt+fFr}lc(A);2>~4n zeEPGNX_FqIscOu0w}TkTXVRK6i<{~H;6yg&QNP$#6hq@3E1LE15Ek!3pg=#d%FnUd zlg_o(BT1f7i>1;Rq&z$jdUKEMf`c%zjzaq0Bfc6O<@w6BtY6v0Blk@-A1PwQr=_H% zpF?zWB~zD;V9t;`4DKsNw{|19`wg%^QH6%tLuw+fkgTwnuwQ!ovxvv&@E%TeZ(#r6 z0ZJt%v1fiaKk`mu`+gX!7eA$_>kTq1_u=#6H+O@Vk-c&_Tb=FE-S&$yF~#`UBy%?5 zGVO1F70S!3_D)Eis5U-V~&iFVo}iyW?mNM+xRh@RLLSg(V7g0WlW!wO<+a6;vc>+7%)sQ#77fC~XewIJw z&u3j;PMXiA*xMZ2b%=G_GibUQ&*5f{_~=nY(cuL#;irOw(eRx+%~8d&HTNeM}f~ z5o42?1bsYDwAlc@?Gf}|6=BK24V4 zwk?~$YSUiob$c+o?Tf#g02*t~Q?MbN6C$RV&k)5~#fp-D+sR5W#35ofJr#{eZmOjw z@&b~hmy!J^0kfhZMC$COYH}s6Uyh=5`8ab?&>U_)dVwc?*dF? z8VT2UfVt!(&RT54>!2%}#9Vo@VFxElPVlSb0FV4_S(JYQ;Tz3lef~>(bq4CoPogr* zlhEa@EQsr3NaSBW`&QsE_7uYQfqX1DOzX;Xm~KA6u`4>9{B6eu`BQuhh{jI)D1K`m za6C94hFO!J@SWbB3V7#BQa4G0P3K>;SMdeMQ>T&?@1EJMwYc7dB_iqrPr6pC3F#;^TV82P;r#x{A{1d7NK!g)wE-?78g2=F3ab zG@ZiuK}E#Li}7IYeF}3kx$1nMBiZX2A`;8oUI()0O7q-bku!4~S#vglz{=N58nO}V z`R7m^VMgcEJNzvTrPcZ@e(iDOuBjmNS`Xg)=W?WUEYkmDo)P?i>2?2K8}$F9-U$Lr z0D*Wbs$bWlC<3PquBNZ}btHqz9~+mPq5p4a0e2|BHViG2xW*Q%&*)8uTAIK!Ic znf_}Y8c!#ZANLUn^$3J(&LAayiJ+yYaFQ!#w6YElQob`==MZBvqREK-ikqM(2fw^# z#o`GZFIVMJ_gZ%REN9u=Fz#+L=d}HI+G4h&AyUAS$rF*^|AjMu6zTk6j>~XGhMZKT z<-}uZLjrM|p~tYJ3;6f&2cqWVunxb&g6d`XI}Ji~iaa+h_HbEX5I?6za&qoU3ilo6 z`@bX9T%N-V&wrGBzC_LjUsAI_f{`<03=`SY*MQi&e2ll=qDbo>?nCMreO!peUQP5n ztMhTc3{53}h?5=3dEqp~=3PU$yqPt7qWFK9d9$w&7zX@yD;k7K5h;qIBt$Y4ic+e# zQBoq5GNws{kfaDDQ$%Pa^N^${N+N|cph6{0G|)W$Zq7P4=bXFm;yi0Tf5cvEuRZru z9{N`#Fize|%w!YV&evfhB*MVtT2eJkcx9!Ay{RNW_lvWyCzoQ^?U-f#ruy@H))&4) z(ZQGM*?}zGa+SQ6VrI^?r=v*>IcXohIT<0G??GBl6wm!<6QS`JmCYAWxL!eclM**Z z%CJ^;4PTE=V&8@De5#0|Zhrx~qiV4?xzG16JD7QLCj*=QBhp%#aI-!No<1agS}eWh zpOL*&&sldxDxR&SZkwdIpa1Q;v#YD*uCQ-tJ z<7LaSU7*RV(M@zroK}zln&!^(^jR#Wd|+4jO$$z{?Mp8+TB;Wy6PP@2vJ|WA`*u_E^j%#Y-29><4rX6KB6`7@a;jjH-G~ZmtI_XVemG z{trd>S|-F*qOmf7jibNQZ#Ij3aSiVNo50VGN?Jx$(coswkTqusK3ITBR2ic?XE8H< z5aL@>nAtLs-=@bnJ@+e-mN$uZ8bEDa0Xq%%vE4g_730pLDSZ_A#*_TG6^Cwt8_JU- z3En1)?tFV{<;Ifc5rx=)E0OY7;kVWS{+zerx{(vFCyqup(T1(FcQO6A0&DUN$$2vb z!J%WZPm9Cod=Kjea#&ct1ogWQiSoJ3>&r9IpO8<WR~8g?W!kb z6H6KGEkgXuQRM5ClC!mwm2+d+F>@TY>r%-#4#%zG1>>CHcBeb*E}WtL=3`{XO(WH# zoQW&HAfJ7YfhA__tp1LL&N8l8ZA4`MASQjY<%rV_taLxI{nI&~X&YgjqKw$*DvXa= z@-{M;*|tCF$!KTDjF;FgNg;QR0ypwJIB;$~g?GkdE3b^crw#&x%UGdg%{$kR47*!} zZdDLE+H+YL|A#k?TVxe%=Ol{dFR! zd=~M06!@{gl6MMX$92bR`{ywDTei3xx?hq)b+?4LPR| z(ysN9tQi&Plt}WRbvducIx$Jqm2r(ecojCWGvh30Kfa!_Z%}hUx9&2~PRT zx3BpK{-;Cj34dk>PeoL;jVxUOo*vX<*R@+hlK;&dOd%-1T&6^dG~jGoc*4xsK-I6KwLIfq$|x&US-HFzRJr!6wQT zo{?5LnZYkBS+PEeIiic1{aJ;~&8Dn0*$rs%ucDvxW{0p;u=kcpj zhthy#-h>ZD`^OSy)Xl<9JcuLPvN^IUlE>qo@Mu;pmc^!2`zdnGH<`)B_O$)?oPTo# z*%seIqNo-V7H+0`hYJf6KXI-mh2%i_ZT;s)H3 zo^xq>I$c}NB32>9-Rb${<{so?#26lH-yk7(4O)J_VvSza8HdJ#1XPmNOcvC~uoWeo6u_ zE&OOT{lnmY8|j{4#SV!V{&Gg`C<1#%(bne0TT>ObKRHaRe<;>L?`aVD zO{eTVl!Xkr8nukl9Z48R%F-Bp3j?nv685R1^t6}{4%!4?R6uFx6HZvorOU+zgB@yU zYGg1ZItr<=8FZ{!LitT8+D85&_G$#yZ=SKmLYn@VH(dGdg#P)U zbUMMs@zjd!Wu(^tV#3|D+m`a@KYc7uxu9y8%CLg}Fbj}FX=4sIcK7prO9ETJn=nUm zExl%uD4!L;-ZYHo0`vK*a%Uc4#q;qfO~t-GP`~9W$P2GpPak9@gVwXw7(m zU0BUq$>k4M81urL{VkO&o}7Z;LLIEHgfC+m0sm5uwDJVy~*&pWJ%bEn4m z3uDghV#DbvsDA%RnZ+p5ruven^c91&Ye*YDMRm(+24(rum=edqQ&sf2tzq-{{aBtI z!jc^Z{FCishtqN%Uf#f$ys;QnrE|PzFH^3Tvoc$PvRN0HXdS}nTx*WnC6F($fj>W@ z@m&=`(PcR-4lQA&njy1)s8AwmK*4ASd|s7PEC_SDe9*QKAo66#2X1ILXfa}&!0*?XHtZO*utv(hRh4xE17hp8o~6(Ot)(wbmnrDYe%y>egr1Z{9!#`f~STcv8?R@Oz+s*rpNppS zMx;mivh{TUPC*k;Z#&AJ_482c=;xH>MTVA2u*ld1^AqtLY4=9@>KWof3Q!0U=6>sZ z%04{C|IjU-rLX6*iZAY4OA!oLBcr;O@YquJilGn1KxHMIJgGCU!Ul`%`OIQRe3coQYjCvb8oL8?nwcQluWQzMY=8b+|wVKVQib6@i=m9AOLwn{_iMJrYd zKJ&{fjs8+)x`*VVIc^#$1-?w}I>xBSMrf~J$OYSSOomy|{pb@TBb?cI!yN~!-x%Hx zCb0NBuUDs1y=f`(Mo-ZY{zkjkJ~jv_Q5$@bUIiy!gx7GrQ=E0D%W)_S=Ve|HisKjZ z*wYoC`);^tk0I=@3sp(G_%z}*im?`aaBfBTSUR@{ZDWYSS?lQvg9p={zDK55kh;45&I_DGGg`?3L_eMn6AJoky+?==wPlSN!Y!i zw8$@F#|A%a*UTi@Z4(`iHOyaJiR9p;G_F5POZ*|KE*&IVX*YWngqhG($yePmBn*AR zFWXj1jLj*{9Lac-t?U$h!p+gC*x8>$C{>l}2UaY7bdT)djU4pKA;HawQ&ZZAI60Ah zZkaqyF-B~-CjZTAW`ct~2U^yXaeguRYU9|DnaYL6SXBNTpzH5t0yI2XeO!q{iYmCT z5X8q*iPDo{$SoPo#j#ZkeGrOuTs!=?pY`zz@Cmv`z5NQLlqXYDEsx2*%M99jfa)i< zBz+Mk$NeeIq0LxY*6=`X2wUtLF)KRG3=3fn4wT^QTTJ-CYy_l|kU5b{n^76o&-;1r zSkK_AH7q|rjyL_n)c4S0UhT{;hJA~IB)gV8!{T2;Zm)~P5ZJ}2$px$%hhp46nc_o@Y!(jVP}43l1vQy;V?Rf}D{;eTBOfA9 zawsdFrAaCbeqYLntjpXmG@{ONB&+lC`SDgC*{W3P_y6V3vQI=LDe}gB3u_HMQ7clW zqQIIG;bG|c3*tWO6(hC;;d4g=#;)U{$8Gkm3PY{PA8MZ?9($J~&$nZiv6PW(L-Dk~ z$L#kL(eCRdchEO3CoaT)x)m2Bm6&Eb_3MM^{Fz63&m9CS8o9M8is7~SiBU+*lN6db9v%o$sJGm8fMell|q33Q~tT`z*T((6YG3Q`eVdoIU^3(Z)NZB^&BiUq~$~sF?F#p!HvBa zqq*HzM)neAhD4R2@m!eIRl2AOl(7BUUFIAcLGGN<%$>D^6&J#2?DHY{YCFe<&LE-j z9^q5fSh?#ydin~8ZIt2LH5XiR?HDrt5dGbW{E~l8*TWVJHKnm=I>){RQfOul$4uuW z=4%@M-x{r#}ZW3FbvikF9+DN8L_$~EoPh$`=fv5RSoP1JD_0!3eH{2#s zxR3{VD_PJc#E5+hN%Oyn&yjwTjk-AYE)f&+k!*SY8Lh8v2q?|KD67dUv#H$>ru{jnlm@$9G+W}*b+I0KQkJcou7_M_5$+zZ=%-{MUl!=3_JpPQ*weD znRBeR+`)@9bL!O0Z)4x-$4{2@VT?G2Z<(YhTo&WvEW)TPa4J zyT}diBqW61W83)--gHuHw~ecE_c3~wgj>yNemGY$NkWKXJ$r_Gc<|am7{e{ssE&EW z%%AD}5WI-kk^sKB&73twPWE9A3a~ zy}fu%tRb>Sms#Rdk?b0Sug*+_B7WhQDa>Kf9ejV|#=5k#^yn1vy(NaPWl0$CXhy+J z8|7nm9Mnms>y9^0KZbBo(1Q&neIXNu!_=nC2TK8$@f znW)T0R#=;GK5!7{Tn=)5S_f+4=h^beo7%houpPOXas^XP&(G&aLL>_!WuUK>@41JN zUN1-2p3_7sOOe|3n63||oGjhKfN%(NUvx0e>I+*(_%k%_C_-k(h~L$PuFMURhsM*A z-pr>8FGRy9bJOJozrEcFzIBp2F42^Y`GU09Jl1>&WcOlm%yKe#lf4lEcUi)FvS}E+ zhoaf5`Mo%h?uil@H^wsZhCaU6TiLVqHI=Fx2oI~nvrLOal4|s4o##tJ6W%hyv=y2$ zr0YKC$9VHM^a}Yh|ETKgWXKj7mIuBje6<&cYcCNw_A-TgR{zfxyZ<>LeGUsHRpACz zA70`mxf;WmR0<3&Q9s#5+09FQjrq+sonjV*T_L$bn3~Q6-rTyyoO{=>4r}3!`Ue{G zoC)8%8+HE`h%OFf+`2+UJK`{THjzHZ7<#X5;L`g~be6g?t*;nG*O7Q=o3dNx2fv!P zqnWpX620w|Hi{y*qnnLI9(XR;O7}Mt{Hw09>azzq!sbN$T#5W*W3CRq&B|#moIfGV z+p+KPKJt&PjSGl(zsucu_C&uG<=3%(=BTBx%g|l87n4|cw?q(G!A544 zZNYVJ7e^L6=J32G6hj8_eV7MlzJ(&1-@qkB3D$URBxPGUDZ}#-7F|xm9tDc;eIxvn zCEx#T<7n*+G;Pl zZ;28=!}r*+=-M1ceb7jL9T>nm?+@pG>_q<6Qu3ZvVKaBA;wAt9AOJ~3K~y}PpGleA zOZkT0`va^>4kG(cJ5zKO80f#s{`=o>w0=pB^(o>Cp71X2G^?#fu~agTYXd8=dZo@C zcMX!0TA3nV%D?q4Xq*;d^}bsqZi;2+zgx^tl)}w4hdCD};cw{=g_2}co+sJsHluwv zk>+uj_T<-mO5V%Zs)L+*;>hq@Uub+4g6+NubXnwaDrbQ8eTqyPC(75`o2U$Hpe41B z<6(w~+HYow;wWy*Dlo}7ldO`Ntex6}{m(YGrA5(@GYjjJi+LsTl-o*W?6oqHr_e0?0;}C#D7>#BZuT?cE#I)rRh8Mbfy}uvgkuX1Q?|kq5hD>kY|dt> zvjT$lhQyhELsn3o>s9A@pDlw*RSfYetN5^FKf2Yacx|cY#}o

`Wp0(R4x_iqR;# ziKXjGlrvV6CD2TwP%7>VpOE~)p0lE^C#h)m&e{BJU?0{JLEg@$Q=Q0iZW_g_5}Hi^<+U_gGoxj^akX2@nd2IW46@9`cC@av@rU&A-VDy zM0Ss&+Gzlv%$G!Di=yC`L{M}fL4A%a)@-9(@(4>4R};JM9O{Xy$gLA3GV?7RVOJ1* zV?c1XEIZDHF)Vj584?MkROGTzJchZcV>qdKjSL}g1}>KnYI~Jo&-T%$JPS$fUwDWe zz(6XUYhUFNITgV8>OahqIEriCI&yt)a#3s-ymQB0t$_=+kIC33gNJSne_a++@j(Fd z9aossSA^hQNoWh=j-L|Gv_?Tx6phlhh~Jt|^_<;^+`h&6Pv_9lOrtFN45H2BkZoMR zw<;NmEEUN=9>dsVA?{4|r0w`APT-u#J&jeII&Y`E~82=4vW14R>x$+sD6gWbW-Z3}UEeu8K8}#T84T{I<#+UYjDrMm+8K$VyDt;AyRy5-hJrCS@E>*+ z>nu~sRw*$g!~!Q_4-${H5k5wRsBIUxEWC%HybeU?*>LXbd0bXJV6b~K#@^Bh)R*JB zVKZ7qwTv_xf~1}hf9H#E-+BOp&}4S>$s=}Lo(VP;49?6%`s6YWxI81@ubf^lAF?381^RLH-razl0-=WUF=Qczrjs;0mI*y7^6Y-BW zMFVa*b(1KsMM01#CTdL^aW?)cXdANUO&dIR-67^ z-#Na21Q(4r5m+{uiHiB;n(t+ISqasZqv;vZk6nQ}z0Y-NDiq-93TxJgbn*AyFbcB+ zvHu}OxZWlv42r@u+=~0xtXcnR1KV#5$3em#4Lt>dZ_Dzx`~>MmOF1F@9V?Yo!~`0V zsy{>HhG-rM=8)j{5W~DD%#n@f{_rM#R>d&KF_@5B4Jhwik66DO7o^X!e$feX8=bH< z7)QQ^8P$pV&@t@9%r%*E$+bM6r_Ec_Jo>)E5p+Z zP+M`93bSXdla-?-JcJIHQk4BCqjk5F2z?h0J_*O-)KY5Agt5J)1#-1iOU~z+^DnHQ zB=dLYGB)0y&*vxe2tZ0u!}pK38Y=`nK$3P@Q}!uS42c04QP z;^_qR{!PRG`Fkb}`H!}iJyggR@NV@y?1S^rYhKU2fkYlA*%K`MmgGZSta$&1neFeH zd}JFH5;N`Z;E0suWE-#oy7#S=rP;s80*cW8(3vAH<}6t<31O=4Rzl ze9tW-cU>gMJ_qyPwA;vPmoRc^FsA#3xb&is3|nzb9KF$xlHqBT8asnFBC4x`cX>0T zXCzTI;V#2JTe0Qa7$oj>qrSI>=78sHs}aUE=L>_cMKL91C&KGWc&jy<>UDE4p7wyv zOKxGl_aHB3<)W*3mkZVNslKuZO|u(lTbgq7>qfL}KH{=&4A-T;VYS(uM1>qWH5Ou+ zE{f3EB4)m_!qn57*XLpxdbgZkGYZIz6G!Q?0$bNxPlZ=V)2!qXQO*-Q4Q!V?EQ_+&lpCv-hu6<+|Z{uRd1&)o}MCa_0 zNNtg#;ZZ*gr{A${x&d8lEtr)R#~?!+%9T&BEba$t#p4;cX~CL}b2)T%Jj0^X+2%cu z%H}K7h8{upxIX!d`>C-Xg7n$<#E89PfnF$1L$xT`<*CSA z#H!0P3af|@^g_d}l<*VrMCkrSv27R!Zj9lHuNi9c{z%p?#C${%8~O#< z|2BwOZ8tb<(~gS^Erp!H9RAw4+_H4ujd}7EIjyP5ex3V{`dS4)2v^xU~nz7RYnsOBm5fD_K4I0K2w~ z<#u%;!h1$hDdSE?l+6GC0qL4_Atw*(FyFkBkQpDiG4~F_LK_)xx0ea$-g8yZjZObG z(i$1SSH&U}0^j0YkxiNJTaG<)!tI|W0ue1pS-BCq-<1EP)?n4CO-+Xv3H`em9x)rk z=5TsM>nZ+m4$tG4QSmNg>pVN;(vJ{ns6w6GV`|5WP!mx@%l4t996m$$=|T+ln4?uz zgjT398s4oW*iYimd_&yI-gDVHj+Y_hDHTvdNi&S5S!GPjJ%dzW4JX?R@%Org<$uCF z?D@uuI3MzC-eW#J97XY~^mr$5^GyyS7p~E@;4SI0-i-bCi?F8}NDgY^&W2K6J-5Mi zayOTPceCc$Gm5Wm!?ABLHfdLgrIiB<8_>x8g^t&21~1Cx?##!WmOjm-r|o#Q_FsNKX9 ztvy`spM%N3IBef$vudjXZaUhuq{d;D?#X5Ehd2cG)21%XZJAQq4mmRF;Xyz-3Jj)sL6s+LMRc!g%KT$D%myZ10aGOWU`!PPVoww?> zEK^Wp#6o`>gvL-L=!kitGat0oSejD7UH3)k%Y|T|IfvYT?%0{{VeX$x2xx?{r>Pg0 zUpjO}k0VvO995f+8An22EZM5M$lF+w)N9Q{x2XtO|HdP}fWKc$ zIa1w*xAQkr%_m@YHkEhH>**R*kH_o+icc=ZD`_%m2cEDXteHj!Z3GpBDeiYBX^b-; zly^|xpv}FQiPVi-&oQekte-xi|BXDJ`y07g>c;3{#>kb-V5#3dio>@t;?-J|Moz<8 zRSKUiI@n%K!LKcahs92eoD_+Er5_iJz1d^c#KiQooPQoiTTCtu>sIll<_rDCdKmi# zW0G%1?W!Ma)>XkmsGj1>0TkGz^69S(4mYzYA09(v*)Eb#HZwy^melSD5-zR8i%Bg1 zyqVcLfxL{oPi4#s;`gi2{8gS;MpIaLDTKw#{*k^$1bv|&2xq%;r}YG#!>>|iGLw#; zCVoubL`st;SvHd}(vxMeQ8`X=oA}Q+1+TOT2p+h~&V)Xu?iR&SY6qiIr=a&nhjzz! z8up)MX?QXP!q!~Wwc^6WK8jAv#A!qqWhWl;Xka(Bj_Fjk!`ds+DLfx2%3d^iC^kP*pFjw3SgVbC~mOfbs54^pcZI>wp7>_iHJ?dI&SYXbwLb$Dh4X>+)K45O*h)Q4%~4&u}jaRdq4R8A2+gl--8~%r70Jj;8W7ZG zi<5p5jeoxrJZ~e8<-TNFh;iF*EO)Z%8MAjiXYa@{CnpW1tJy3|UqDljG}YEym>Pc% z=bZUGHd4X6$BaMo;_=M%Vfb(-!j0dd8mq&OAUn>Q`fx*OIaxQ(Fzfa^rj9J;)74Yx zMYgij-<_R{KQpgD2BAxzP;Q#etJW%p$Za91ryGf82Ykz>Fj7Q=rGBG`dHJ5N2j@_G z$dQ(^YDA9uQ&jtj(@SSk>>$dp!C!HmAH~tcLG09(W9O@%xT_^IC1MwkN)A&lB8Bt1 zE_6hN379dSX{~=5XB&p)f3|nAE z<@hW_mFMwK^aP@U{%E}Y!Q^{Gi0#!PM*KP_L%(BOJ(u+I1SF>2rNgI_@bmMjHq57_ z;|S9Ny15lSlmw-D;xBPo%?bX3_`#T;F%)~OipW?-j_`3Q6o!ivNQ1GL!(jCc{ zhJ>t^;)S&f9>y=I{8f+6P(xOA&SM~HCyP4V7=PeC%g*khd)+WX$`*3#h%~BIE9o&m z$JIP1w)Zsi%s7kT$=7hWE6tFx3+U>;fkeLRq|!Nd5R>ebShCxb z#B=f3X?&vN#syYybs^{FJ7(|H=Z~QT`jgzTNV~u*v#;DvE#mZc3)1fWAmGkZ79W_% ztBv{Wd^VI3cFwHGTu6UqJu6l$;rgljJU(Ek?;pqcanUp=)Wr`<0ZnABm2VW4DhflT}lwZd!(jX9h9(>yQ}Yhlpk;4(dTj=SZ<| z*)~q(2{LAaA6d2csSXk0>b*l8R=CGh#q(^`IYUQ91{KGiBatzhrsmUVSO3RSS!H@| zw=-nU0i3lWD9vr5Tz?v^UAJj1?4UzcpWh;@@T>MG`uu&I7s>GD(i&21zVTSYhW&qP z@NHK{`*|c^XIoJ0GM!M5Im~;qpRd{@F==ch)@CdjbLTRAw=B0bPIK%+7K+{1xKLP) zWYaASc5PwHTUQ(meE8S;mP-p#IGmG?r>+kOfA}Nn? zCnSii5+~!N7dyKr5wfy|&c*8}pE(Vm1v#jm-c4w78k1(2v0tK=)>+jY-E*83zLn(E zq%wJ5JXaK^BYn?_pnEa6>2~lhBNWFiPFz~5OK6`E4|;kTzFnR2Cs(Kl7h~wevG^V` zBxJP{Q}=gLbU=U+KN`6A?g9xd4P4q7$mgfB>kf(M+PORB5$XeV1blX5>D^$eM-BU*1JeII^R3eKQ$sYW zl((U|{Au2XtC1%YA527`HV#G2aoqVGL)?#*80t@_|K~mwZm%Zf%P1NYwYU%+h58h2 z@>Qz1H#U$DI?u?AA4FZQ3t>_d$Q>Tcguy20eriU`>lD3A+jw3CXF|Y}M`L&{sDkr&OV?8H-g1jlnI4ujL3V1`CNT6-15=&ZR3Q@O`_FOQIj~!=Oy~&O>E`St$K_z&(L$d82uU6 z%&n8dJ2e8m!Xj?Z(C4&a9}VL2=$I_S{&+o-H=KDp@Q&$goJjK4;M#;Kn4K4+x@8gt zjzZ-8^5RKq6EiIa5w^mDC%2%^JqrEDuc;FHN38rUTz!urpXNiRQ!NjrXR|fXjmF6z zd1VP^tN+8+po`?a0<5(OVA7#e2nx<)u!;~-XL}GzUdXhi>yZEC&&yYW1TJ)@{)8u= zB|b82#B>IqUXI=;F$zpwP)+{DIY~QS9MVE3$)2W{dh|r@Li_y(<`imBo@Yqjg9E%c zlEK$ge|S}r&y+_tq*`k*BU}W7D>C@nrEue-J;m{sv>chnlGZe2zf1rI5w)#|>*6|W z(teLqNINSn!YSAq#(-=QAtO9!_B%}Ot^1VE4dcVLI6OMOkh|;=LR~*G)Z5B|IdSCs zcF=Hi67dx>#C5J^vyD{Zsg`! zHUK{0mMV)(UAn=|hu=xP$>aeOZaqUE?_`;9G*am=pBu{N8Y;st}uDQb4Dyh(4Tg{Lg=U9;Xo8z4g zjN7*l!Rn2aEjfY0=XW@G3-Eqt6-v$i95GU3^0Libt_sKJ{6s!RX|b_C1+|tI>_5F{ zW5#->m|Jji=3D$q#&fXf5`Q1nv0Y*r#(9fq*jR_E*i~Q0g8Lky02g>%}PZI(io-;jB=}l*`ft-iYF$ zLMXot-mvIyAQ$eQN3psMk0<8|QlG_=6BvHe2JuwBau>E%)85_ z1b5A)Y3P5fyB>^f;9Ay%2{2`&1Y5Vq@%ltNkrRut82Jr@G2O&%8^*@>Q|bQwmF8Ks z82?Noa9rWwU9$XMxSioQl28><+rg##c@v!I^>dgGdhZkx7XtQbOZ~mx8s^pN|VS?KEyp>NNW}+ z_8T+k=1syyy1CUe0TB^+pzgq%)$!;JACI%oC#nRZxc+i8v((kFJvy0RUJG#us3G|0 zeX>@C6Pz;|=_jp>d;g2vE+s~r1yKF;0p9vADfPIFN?beUS2}ps(SVe}Py|!2lX0by z#{XU;XVOE^%nsHrZQ@^oHgB@cP+6d7$6Vh$;i_ zltj}pMFH1imGl>O^HOv!=a$&;NlpO!sBA)8k0T;C7LN}tEPC^p$MTN6QfTAXl`))t z^pWI%I4bO3@%sKKECwIrx>XGlis4M3Hjgjys)*gNCHVJg>U3SX_eYm~+urdYt&(pS zuVJ`+6EC*OA?e#o$MqP*T&9yUcQ((SY4Sy0i1*^*EYC9H>BFIn4jhTim|n&p%8vjO z#NXc`sKbPYRn8m?*2FM94oSa_4B3)QX-pNR29H?zF&@F+5xh10KxS13`;&cn_p^_~ zZ?%~-Hj26PzM*q$7|qs_jIV6rnvN@90h52P@Ohy? z&w`<(c|;?fVaipOe(|I-e?GU=<7jM%A)v&G-%FO`SaA^D zLIXC$hSR6_n$eRJiGHR>(x!D3==X7RwLA)r34HrwjlI1pS_cI==rM_n-?LbnW464jjF?9VJLf?cj9u)1^aKM*r>c98 zzz$A7vO`VkA%|AXCZcRGv!)3#{8ttutU~B{HI=&MYP8nfVUf`+Tr2WOQvHan@-e3F zdcvcy+k_fS;$ERIzKe&lrab^55jpzJMY$Zb9Oqri^qxvY#yWYmurwG@saG5d^!{j81=Uc?|5IDMl!tbxWdev{d}x{!Bm4i_?V9*JUW1B8=AAbz*4LS_3BTkncimUmJ?IqE2%;>Q-wJ%EYNvl z%=`2vKAjxH{K>-18#|j{ua9xDu$p0)WDrQ~!7enAbI}h8KPW|>^c}`IWs{h$g6PWt z{vEYqLgYafz6l{mb{r9}ZnGyzfveqWs8~EjaK=l_#HvwPV9excJ+%HB!pqessTx;K zxu-avB41MS;vt@~LUdUyW{rp^rqiwHJ>gBjio-lvAIqRrd19sKp*_j~$G&CMjjEy2 z#RLzlAA}y#?G=>i6pGzn@icPEAOJ~3K~!pW#4SD_^;_pr@!rUdZJROa zjG_GOF}^9RB*m?Z1O+XuUyNes^Yyr!wo$UYf}LCJ5U&@)fACHIDTs3O%{oNZcoJm( z1ZiVA{5Az5@%t;`<GLn_YKX-(Oly}xY7n1^930fbeq!DN$42f!zO7AF^iWlda(>5 zPX`nIf0(-ubs!i9fWoImW|CDBk*H{w5h)ECQW{E{C@rLg5K&T6QYxinL{vhj%Df?#T=iUwlKKgr1+QYdurdE`6j6Od zfwVb!Y_406a%~O$Q>G9o=0Nk4>s)fF#i}KdUxTIScpbs8i-xG$*3zFS${J@U4nB#X zYu`7Nqipa{J45b>7!0Bd7*pVYxc&_Wct@>k8-flCsT=3SRGU*&&TYndpoa}L6%=hx zVfIxI{NhsR*xQAQ*D4}%wAi~f4{HEVK(N2Bemb+Bpea;IolYZ1SNl;p%Lf%zdFC5= zAhc7AaQSZ>QN#@OpWynHU8zGNfcDk_=P`f>cDyaKLo=z6hf)=saMWP1$^qVc?qRH?C=ol~a!T_# zORu}(w_^*E@4xY3UL;jk9aQ?q5wTT^jxY}<=M@OEtBg+XlD#a|p1m!bX#3w3qYo0i6C0CfQQ_mSw zOZ*L|6Jx4GL+LoW>~e6ZYGU4^{U}>J;J9ui&N|-6PAJDP%Zp*X?hHRMnBKvn{C?TO z{Kn@@yPLoY(GWyWhm)VTlxCp^C?8r%UWf?8mo4CvXBqGA?dFzg66?oQ&|T!iyU0V7 z?>x)M#|qq-BTG!s8h$==Wy9@U`qwM3q`fjBu%}-%pGCq^d04&WFRMf@A09<6|bf1$c`M# zq>pmQ9#`a>^He?6JxNGF-0lUj5G5t zBc|Afk=lR!H~JTYo+t2Hw}kT%|FBuB&w`tlxVqHRTd<6iePyUhm5@J6oR+Luo*NtT z^zus%mgZyn=LaM6BWPEv<$Cuxs)oV@Rio^w!HwZvWa_@*p_vuyixN?j zmSmjV2+qat=e6NS+5*1v((ygEE&@ysRYA8Yk}XqBsB}=^U*!+RSXWYfX#^|p@233C zA1X$*G3uBlu>x5beOt$q;xz;$11@)V1g^JDW$JRBxtd}}geBOS;!GA1%y5uq(FN%(GxNmMLG zQ~z=H^f-1}3S)g-2+28*$*LNSX0RMzovbmtCd&!eAyB2u1r>Dy?YFS8J0BGpFUkdN z$^Y~U$JW=x#jC&;2kb&q;70>0!@ly_R|ds`&4kz`(PQ+Fn6@lVrbZ$AK8pM=1`I8^ zh>k%VKK^l7*Co=@a31Z9YRX-q3tYRbQqt$4cO{`W{0Z> zecAz7pZ<%AaxZ0_9lYz?gJ#-y_Wf-@;>BCm^=!xM3#?2L=J@_f{wP-?JYI#*uJ>sT zHbD867km7Y*tew>3A?G3O{m~fS|=5CZd8Qakf;7%5SuESxGwygFH)iOFAK+|&lXX;1pe(i#=+beBIiG# zXVqgYm-t~Vypl)C*Ep>A8}|O67o_Whnr0NdTZ!(_hXmyh<#ERaYTL(ByTXx~ncGlF zPoesbKEvIe(cSY4*D77a-7@iWPQ&1)F&7$FA+Vqw;fptsPy5GU?NUxW)Z=F3bZ%K? zbACe^-5mu8E*nl!wH-^d-AOQ*Mei^z#xGom<4!X|m9??Zh(cYqg;Mof=q`Lf>ZRRO zY?Ei{H%t6It(jtVn*Y2sSwA^lb^^WmPII8E&YS{UdkrC}y8_WN>!?+sy~DF2IzT zC++c&$>(sGBGIvP@qhY`^`Nkk3r+;CCwmA)x zPLVrnfI)4~(2i-sdhS79whrNGvjJ6GGFh2wjh6K~&i!&lW$03D)q^;sUQDldD@*m( zlGCJs=%W-gwf6F$V}Mx|2FPC(Lry&gzpJ~M61bHmu?IM`tb?$FLr@UCgY0HI#=RNN z%TggWj8nmVmOEp!{Rozr!{43a`2M@a&wHmiRo9H$vmQ#e*OIxpgZtrs5PS6rRXhgo@Yt66Yr+fk#%JYbxw=%W;Vs4fgB6ULGn!}Czk4< zY~H}frLjzmt>yPpcMd%$z+N+$t0Bc4z1F}wkysjTOu^S#kus+e)P5)+s+&Td{~gBu zYhmpTJzSQ4!AL=n-p?9LweiPgr~*O#;(YfF#=Wo-;pZi2E?mXv&&SC>xSEysHOU<< zjB33*DW+Q(F+Kz19zBXRx|waCM`Y+vR#!iv!8n&aMNL#q?7?>PPR`G@Lpt^qX3@iW z^WZuM157D=B#h1BN{%Qd6WbMv;;Ud*ow>_c6FGLDjiO=7ScFT2uwS6Tlgb^eRcJ;` zydNF2>-+lqg(R~`(k+-4v5e^<1g#1H!?iv5E~rsG1=9NP$yNymActA;TLB!&NE|4 z8Ig-;@iA)`JIi8GmQBP%Vl+z~#fg!9&9|Tx?B8*d4UhYH@Sp*u=U#Z$l%pA)!TXYZ z7&z5%f09271vYc;$}A?%zJicZCIiZ|38;U^-GCByf2-zOdl|8FJ`&fThj;!uG9*RV zv2`2OA$!Tv`9Z@wZ4Sl!$HpJqkV@G>URehEH~ukm;%>qRWEiYb0G-#(i$%4)25JD8LLAEti^COqU03crJRBfXw|r*5$C z@J_U@Zsoq#HwMa7Ihm1)(Me| zTE+IzwT$>Mg5oFtC}I;ucdsJhvzif^|8cE7mkW|7Ik6^Yyk(cl%H= z&5g?avoX832${BgWOr;L_w810oJ=IIDHYGB>xg{wgQ;g#8@sU=U?=XHv1 zWogdbab=fCJsyFea`_G88e(|t-p`P&cR6(!)V_hXRBXuxO^hZ_0h5H9y^Lfxi z_WiSAxUU|gGvv7OqMQPAX%@8oWbL8L2zlgEDy510B6}F;$Ls-DEKm0`UsMns+3$qR zjbW;)BugrXaQ%oNH$`PB9=L|@b4S)sv*C60QJU``BS5GK)vh(9k4fi}z$Gf}x|r;! zi*S%Cbzhap%5~uQlUL}S{zXS=EUiBu(tIS0ZzBaczkU}xcf4apPc3uw%GqUn6PxdG zOr9r8*?wgPDxafLv<0{0F3iXrOh>CMDY9im+}%L<0uN4}PDVXXfCUF;G3047x2B0R z`c*P#tWFa1t(f3nN;s8l5v|pj#bEP+eY5&S>y~aVDgL++>u|0>w+L^ z6pVOdR*Fzy3a)SBNo=bn_dqDA7l+azdl8rNNX{!3QYxH>qwaQ;Wc+y+d!6=ZVT3Lm zWm@t=(&la;LU1YNPyTZCtsY^ob`!dI0U5Vc_$4+NZ<8aW-F9b=%vXX9DlzTZ#^z0? zm>dj2=%XcGdtXrVNsle}{LmSkP27z`gs=ER`H&())34)x3D1lr%oqAOC-kVizmd zGtI_^>Z5;%^4f=idnt!C?^11jjl{*f&{JH)vS?TC^)?|ns*|&KijfO*WbKU_0<`>S zTqTKA*$C3sEkQNsHhB(muB zTWW^CLM3xBivq)VAN-Vm%|kh~Zw=;8^x62ihssCmka`%!9dlbo-;`$9i{U6XCormh zHM`UDDale}lSnR`cc;+g_?Fs?RALPuvh>{*HgJ;HTPCn!dH}j(+{rv1$C+u;>~~s9 zseT9M)z6ryu7GZUGTBSSu@RCXOzb90bvn84U&glqWt@~XIR7GvE$urQwceWA2aa4R z_GVzW6~ms&FmdWm0_10)e)c61Yt@i9-HuarDC$Q=sIwl;o@?*f>^2$w!Sk6}Acbp} z2t`*vuwKEBu61*X80O3SCEaxCxbPx3hSoC){5})K0hOV69ehTr-Uf`?_7MK^4b$h; za;xtile+JtpDaP|#x1PL_hezlO`fNpVf#=mhSqknJs};rzb1^l)=s$Q1jK5((48KG z@cff>8dsq9d_K`F;50@e9{>lFRkEO>u=88JB8rRaV&YckVWl>aNd0r z<6ZTfZP<*$gHgn8sOIlDC8BGC_@LrO@UoB8^*ZppE0l4OLkavPfav?pG^Xpbe)}F4 z1pj41$t9ZArDq~BN#3oPL7x}t7=7Qzfy#yT>$|Ko=m!HiIPJybC-Q(isNI>>@h)2wVro(!f^f6 zjl_pX^j1_5*m;xdgHt$~BFzQCU-TKMa{l==)<^$DI(-FVvqy0HxEHB!M&Pc0me)$L zJQi-n^rbD6#%1y(pr6d7I|y&d!_DR>ff*JUH^g#aaUR>vmf*X4KW*A0DO+7Y=ExJ| z{0Spdav|B)Bl&(gfe6EeOc&mc?`40k9&bT)=X%bpk)prrBnbjp3`{hmXVXb)e@rCq z@C~wNI^efmmPH}b_aL$cG^vXqiJ*zn~@*wxmRFNn36C*VNnux^9 z+z%P`RKNJb1l54^*q1*=aQYPtge0)an#0AL!)U)ENc{6? z1d1U2fEVIVmSMJDgwEI{INa(XC*vpLWBZ6GY@>5aA?Cl0k?I`762V0@S2T0YI-07f z{`}`I#relAIIQ}FdE-#FzPis{yJrZ_FQol%H5V5Lutaewv#-tLw^s-4(=$2YBSD)& z9=B}Y@gi_9mp?XPd;T!*1suqo{|P&fLZ(XAQEw^0EYEs|rhBukvHMt6v-p3^OsPj?#Xgjr-I@4hA|AnexHPhd zs8m5VNIGNSbe#=yp)?zWa!6|lGp1T#=F&)x{~RhL^vK@-fG=C`Vsh&cKi>r6AvBEg z!ctTh#7;1m_^0_t)^;`Z$_yqd8`8N8?{(itF}J z_0gPzUdOQ$E~D0N93tswD9)EcCBlm6Rm(XgJ&L~<5@?7RPxfgM^p*86w~%Fw#6%)@ zC}SwIg7`URv}o$E^6+kKt1oeNbRR~huUUI81u@5L+C?7mUd@5sqx{JnI-kbAb8J7l z9~pNac6s-+Ir|8^<_K}^PXU6zC-ZJeE~DaqqSG3MXO}r^6E!I|%H{X7{gjV!X7o2x z-rBC=LyYj50X}1IC_ZS*BV1Z@mz!=w(xl2BmBqv;~Zzjjcx^UQ-XQ^x|d3~ zMEX~@ayIo9+ao8FqASh>^DYdVoG3nTPHt-``%{c~Q9lPgvkpW<3ix9<1@Qt~_UPY5 zWZ5TzeeHRFPY=Ia0nC}SioE$r*hyIOdS?UoJ~g1YXBwx@UMJL8hGUm%knY=0&;7C3 z?pew7mb1v2^mBE3GKP{?+^*V$kXSX30xMaSeT?1l+Ejbw^U}DQj&K$7?n}}>)SUB4 zM`+T$jhI&fcRs%$)4Pd@7QgA(be$W6lh`cmNamAkyv%>V-)%!k$W!IUw3+;9IK_WM zB@ob9#fwT~n!66;sb#^6#beNG$)fp|J8QlfAZ+rU^8c1GV@EDUGmkK-;4c2_A7DG^ zA5IUQINs&SwRNg=3WU+u`h+*yM(k>dBWCtHG@=&q`t)8phh1UZK{JX%CR5{C!az|h zdXWYMymX?a^DE<$v~irVf*!{@vP{e{JYbF1l`)tVPULIaS!5!vu}RyTUpBVre`;au zuSKZ`T!~|yik*4|DC>+n5-QO z*cnG*>gR^ ze?;OmL!HZ3N{o}-%;D@pgo+*T2t1F(ZvomClygTQk#TNgsfbpi_uLnDTOQ};A`50k z{6lNrYZ`T8NKikH?pa7lOXi&RiU0pm?4;)(`L6$(LoRPIRe8?X+DK}?3`TyX8w>aD zr+C*0PF}FZ#^)F*f}Par9YNQ{k6itIBP?2spld9zj|rmpRi4#{VC}ru@46I4TK2HL?zjmLl?bRmv082G>|Qlqt|H! zQ$v@s_R|3u#H|RHJ#40`u#_#?2U#MKN_X@Ox{gM( z;DkLQ^DA+;G-B^eAC{+T6J(->m3kfb7JkQncq?$2@+%9d@wUKcaT<{kR|&g!9+}zEG>3K}7G}iL3~{njb}}p@ zmAd;ie7Ne5;#qx^n@y3nd9f4#03ZNKL_t(B2t>VLKMv!K`1tcXYdnk)XqZZKL;?yu zQOG{D;^ss{E~-3bdtxM?wBk5qp2g$$4{5xd&4X#5@p-h8(1c0?#$Tq->o7ltBoGqf z$M!&L?)o?~X}buOVwqTuilpOJE03ffp?JuKu@fyZIAKXrUmQMLud;DcI{7R5Y5c28 zK*%@_Tofny&3*ofh~XvYkLcV947shrR0sEA zj7*a+`yYpKKhc6?r(>Bg;T7RGv`LC=Vb?$p)vZTSzt&3PsP}Yz{76RFbxLH)AFqp{sTeL3jTU^!5oOclz_VsDPMHr38GBq_SiMjmqPw zH1J2ZCJ@EN-3a8YVBE7EyxHZ=yJT_l4r{W~ERcKO7t!Dn#Ps9ySmI`k;>Ib2c`czm zT9FSGbC8o*&GWoj+!d^#^^h{bevyPmcVl(Tmy}O(JhLn(?&^3NW;8NABAMCECm6J* zn{x$Xbgmm<#IyIvk5Wc3CX=V{#Q1soAcEdE(bzj2<)4zcufK{-Pa^38_8_c9=72KB zuPWiP8B663p*`m;gA)5O-hGz>n;!_u{h<4MG#V%OlVq>U4z&oBy9eWATgnY%73A*r z(=z%dnO*JV$rtl!h&ZY>R=C~nqSNy%?{|qXZtQ62jg+)3;c?ZBJZi(ew3TEo z5apQEOU@5V;cC$V7Ck@0cM)a0Z(c|Dj2F8DL%6R$k9(U#I6lsUJF@-E+q3}XIb->D z(wkprKhY+QY%{Kx3<({GMLzQuCUe3`I%G$$k~y-?ur^MbLE$fWFf)}YFE_IO zayB|!XR-NVFvcJ5vj5acN}k0~7p{RK&F5w&W8v6+!xZ!+>vY@93#(n1N`JWW8 zU&c|FUi|kiL^InAJ=q87ltP<(1pjS)%g~u{Kevowb;48`2hjg)BlXVn*M-Zt`S~#V65H6QM;N`j4)eP{e_!y;g z{i)7s;KrZ~4&=$x9=M!4mCl$AljGF37}lg*BE9?@&Wm(O^ROrFPB(uo_K}dM#GU7z zOu4a)+xenc{wPLkkOku>-{YIxDz4rAiqO_ZrhGD?{>5Xc*fklIYD-v7rF|1Q)|{p4FzG*iQ$Ge1e1 z+fBdGSbU2ft%KCP^+uy)KJK%+XpOhSSY;t9!H>~s$s%msR2&azTI?@7`ObQm&9*n&~3z34TqWyYs@IOhywS?Lrsh8eQ0$(rYzc2RmYjDquZ z41D^JmiuEU>Ud9K^D5M03P}ibXR2rkrs*S4vL40ubs|WIoknBH8-zCQWJ~Bhl4B-u z&h!WI9)-M&w&dTdEd(t4$jT}SLe$h*;y0bAwK<%A`HE4s>Sr#G_1Q9A*J#d9hbofgpB8* zQ3aBxhj6>ckNXnW%_sSB+<_{s>?+p*v zoZywlRpQOxk)(7O&9C`92$Nvh%UkHCU!>`?4=?WRL`S!p-)bIc$82Z2{V1lU_8@+H zFt!gfh}Dk9da^WqB2JvL_>I?y(+F&ru=Ag-a8 zhzOQos_bXZUUFx0X$9XGFQ!_19yj`Da7ybfBM;xeXS4+-(u;W*dyKz!9vB{(g0u4% z_J=&Be{3de;(ZVMrgdDL7cJN=;Iq6KN6r;#@2EtS%LS@JiUCF{1M+A4xq zj~@*R&-gpRf_SY29(n2T_}xz`1k9PV+=T!!bKFeBs6dwYs|S<5+>^KyD)gIF@~5zi zy3bLxN&TQHJ`3B-Lo9f;7|FSx+20X@e6S!9hia+I`oWpgDj3_eqkBb+F>81)t}|_!)q9m&emVT~nTmeDBB_yTeBW@5qYlgYw5W>+<6NEvyRqi( zcm5Nbgz?v_NGO%ldNYaPRnHNy8jVQaBMK8fu{PYu$_|M0Xih23}d z@kmUCsi%H%Emwk9g&P>|UCQFVIkd%E6X&K%n1>B@;%S7dUdKZ}mq!{lC_n$mnk)I- zKU_ze^dyGd-p$rI1?bMaNvh#tM0X!SYh4GW(e3QF+(7)FNldV^MBt(pB_{_+y|;}E z!@qJ$Xd*k){5X661NO#J^vGR9)fle*%4gfZD;Vw@L;bfibO)S3`&=hmB%aW$K8EBc z7ChN3iP8VNVs}vgHg;Dp^JCx(>W4q^J|G(9?wOpj9pL$zcZ6+jC;g`{!xWZLTfUy% z;}vM#wSncfSvV=wvAVT{!Z8+j*Jd+DUxRT*<(LS{uxi;uw)>ZmvNxO0scR{A8H$rSA+0Ba(y?l@2W%}%S{sPYjBbh&31&bf25PNKk zvr99(4wf=!MiOqTHj!&Fn$%lGG|YU7vX&QLZ^Qb2hy$HC z+}GHSu;6^8UkkFiZYd|brZYtUIOE!VksQ2%=kdm5DVS1v`#RoN90(RoVROSs4lYjR zP=X*PLzJkP=FU`%n7h;*_oa!PQ(n!N72A1o)E}8dX~ffeXmCtMc^2%E*P^&poOV+& zwm!Ltg2FLWUY5wg!?_=^mtFZs(0TBTx|0H|>Tah|VF(WsZAo?yz*k+2ikR0_ zU2R~HXcvc<9U(%~k?y#!REK@T{O=T&Jxsvh`)d9j38HuM9Nf*)xqID~X`Uv?3%cX; zZ7?^s>>jcSVud+%-5hDhtz=8i z$6j|c-yd(qD@TMc8V@P!^5t_{IEvQM+`XHQj^JUYB`ia_D2?iidq`fOg>r%%m-8(# zIu^{)6-AgGlclTRJNt*QYu{e6RsLo`y0Txq=Hi{ZN%Tw7b%HuedN@0s)F zq#DO1iVztliQ(v8wyjRWXYXQW)z4)9Bu_RhRO8#CDHzUhWcl(T80M(MyELXp&LMxp z0|v!K@WA5{tyAjQ_jo$tYSZwV(og2>8We|2!cJ71>4lbDn6#3l5IZimnIKo3#fSH! z_)8!@dqz@WbcX(wsa)L9!^KWDPDotG$4?LaqDHhPAEzV1j!y!Uu${4vpxw~~q^F~y z-a-8fJ&x5rBu71qDi26Bg211XNxl|J^@wc7-ijtzW+|H54~Y2UMa`cwth=k}^ADlb z#)g8@EXty;p)++5!lE0QvONn2UtzvAW?(Zj0rL$y6r6UVRK6D>U4Jy2UJ}V0rbyeO z7=0YuiJAljjK^Ml0fs~R@sILj{_Kf(1)3vzx`D=53D{~T;GUtb9lqx(F4S8cxJrqIu>5!jraf=z|5CVu}oJ-pi)%HDrW& zAntgbd3{&t8EnnG-ba+aY^Nb^Jc9>M?Zh8)W zRu-&om1LJ}JgfbGlQX`b0lSH;F$%``*vRUrcghY?g*O04cNo?X1e6!Sv1wlQI`M?N*1s*zKDdk zJP{Y?qwzb2_1ld2{bo5!wI@G5`H5Gz^6i3bmlMycP@HNWLDJg=Tyv)nz;A zOX?zJxi?2_9y7wMh{z6mymJItJ@qw~Q`D%l{Y0G1UCuoC&M5H@G-#Y-<^i`q2O==B_>-KRX-oCrla^rzChCB z3a1KdaGm~)wa?p88T^dKkWx;qYQSlmC3)KOP@8y+W4G3mW>m$>_BEu+#qq(mjT!N- z7u%?*&#v{vt5Gk74a5%T3CuYc6g>mouyvIPR>b8$RmO09zyW;^8w)5*bZz6$jjGHl5DLH>Xo zHw}vUc5DO&-A3%Nis5Y2TLktdu|O+`k&40W{Sk?BNDPTa9xR(L#82Nv2))hcN`M^l zBSV~K^FoP32|v1|hROnI-qh1`H}^b^F$+Tufs#ZSHs|HbH+v)D7;pLE1n)$tn3 z*S825QGj@`0MYl-xKmxo)4DvWI-YR*a4}yUvydAF|@K{weAQOzna67XGT~w zy75n4l7oMibEi~-@uCIjJ9Z(q>kLY=Be=T0jLMp8tWC}5rNeW?pGG3`p9oR|pV*q9 zhmE}ytGcH!Kj#YH(^u2pumzFbZ|Eu-g4?s3{Otja)H%G{@RcQIB3L+XBR97bhe7tt zzcmtz^S5x13gq0-uM8feiG+O;(<(pEo$1MDqjWBe_`}hQnG{~_Wa&pM>ZI#1U$~XS zLoYC2po)z@rFomPi)zajeoLA#pd`!rG;eZ6bTB{gfqlDuaU4FLv@6p%kbQy~_w0Cj z;WxcqZ+LoTBTEl^Q~x%JmO7{Z$^FDJhT+15{(*X7azs-s|iZY4djei zMT?dO(?+hrRkc=v23iiycsYm_zMWlXaA-sPW zKjaqCck3I=_9-&I_c6I|E7)bb3kg|6OtVu+xITf8VTrg%PQgL<8A8YXXv)}$U79Tu z>o((H?u+E%r|c2fLVLIcviF_N6S z^p$NI8?bs9z|-J$gpU72$Nc$pos>uB)*nLu74iJ`KStE9;P#TiRJg?PWAqk61XZ|h zu#Y`6{3%;sLxt64a;N;I(fu^5E^bBtvIY*1Wm#fb#i);BlHH|jdx_0cT^6oA#ev2G zT0*qB>1aZI-V2OdEQ#;lNZ*=bWO^qvF8@EQOoVxH;Wn{JZG_7Qa%AjbHs75=^v6!T z=0wrr^O5)ID+vx4=J|j&_ufuMvPkIvU$Jwq5t^}jMlaf->R2qZ!*R}Y1Z{`$RU(P; zjn`1Wb&NYx_Yf@df+gy$7|L$oXU`LQw;w>>Y7Iwr1(9^&9V%8W*_ejkxroa3Fum{q&Ru626Bb4G3SoX9DMIjc5H?>Du}poy*63H<-1CiV$2TD% zaTxIt-l$)2#Uy?fNBjio?Gq>CQ5w73e!#SG$e;YdAJ4yt|EEgv9Rnteeosn;3J$LN z(KKW%C16N?4C-X92vp{_3eZlOeOkX7a5kxnEvf1<$fkt^hdHHy^bdpVr<;f z&V_+w8r6=ny2TZlVego;zJV_(OYmPNhD5^(%tNe5Rghsxl?!t(ykg~yzkFLDLUD%& zoi+9-3V)-0c_q<(ifnr4hRnnu6a#lMa^Nn7GsmNPr;HwtTBOq|ajkYCa?UQsuTUej zGnwbbzv%w^fOER#eEJhXoM9{rcluz69wA)g%qJ9C+mm&I)7l_ag2huw-Ef_Cm8I^CABI=@)$u$13lcCuPzHS_i_ z=AG9ZB9cS+)A^OQ?F*T|#D;g_Px<0<49AU6_|T`w(y7Hv@HS;yPA8j&TxN*;AeJOr z;BnHH(pU4CX7h&qZX+1eX2tmOLp;xyC0$@9&IN9`1V1EUoi!TjzKn|KW3TQjq@1(~ zto-vo=I;9|1ji5m{*#J|M7xv*85!A1R!NcUj3Q)}5fNFHR0!E3BxGbYP!Xw6Axd_M z6rnd2sf@(y{^fI@bN>O~`<(CVzj&PMT-OV&RW?k!y_{p^37k3jgyCK1_LQ z?>I#F&po+!AdbbBfxP-Hlnoq2*~QJA>wga2%CjVO(?CJ(AfFwhSiAEus@=*-QV(L8 zi48Y~&cfr$8U~rFF|Y4R9LK0~vP);2&ws|`;8{Ld*kjweoVv!5*y*mMI%@=~xqq0j zs6EkL9#EhC5Bu#id8=;3lD^6mA05F}^DVSIV8P>6c@##J;C0F$*KMh2e&3JRlv7Mk z{7u%iFxo`>aDMZ0woG*8W?2PWpM`Msk1>6Ey~1mkJ@v)g*to%(;GzGao%f8xZ4Yx% zqnZ}_PjQO%rfbtlv^;!^tqw{QCw?T?(*@;%Oy>3-M7?Gds`nz1Un?0FeGTQIlSw!* ziQI)6d{drJZ2oo@z13k*qyleka`-#ZfsdQdVUcFTj-Y#tyP(4NAVVBNE@JAPhiT|5 zR`gB6Ddq?lf2VOeEQk?94x^T>emkZ5YOiFLTjcb%;#2Rm?pwk#o654Ef-SRqTH>4|l-ZNDDvnHkes%VEjgX zYVPIpI%Oeczb0XSWF(1WV|ba{n?Eado7}LmAziwY}XgLuz zj~-;(_>tdcAm%N$(Os)Qq2E%NKE5TbmEJP=VLGSQ1u?MQdumcPafB379B!dDdIu59 z9Z>7t5#_9CY~!+78EV6w`sMWQ+=U*_eR0p6fLc>cn%fNKf=?Frzxq;=^^j7fOzOQ` z@yBQs-SRDPP1YewHHQs`y;lNo6kKS!WsE6f=^qYapSBT zZ^K8UTmzqnj_2;ke_WrFjCw>Ot3N-)(Ke94g8hew6E!kMwALWr9n6KWN-%}cx zb>SAK|Gu%l=X-7$@17j0Ddq?JZQjvI%TY0n=+p^Y7+NuA6{!J7*?aJ`D1YaHpGTfsV)OafK^ux;mhRC3h*k9Lsw{{ZO!1y6H81!w`N z0qTGTpbh8)Mt~_`4p;z|Kwlsa$j?nOkPc<%Md!n8ncPq<5nt}el-eS(G6<4Pg)G@= z@m^NAb(R8~6Eb?=bQ#{zL>@OeFDfoSWODapNpDvsGb5_RO?j}y-LI3G9>Zks%ob8R zafB3pKQ5KOTx4HSko-utmHn}S(q+(H@$TMI^xyi)pH@CH!m*ds*9Ayz)oeMZmM6V- z{}B5jVe%%)Owx`bFQUhZ$Cn2Z8j>zOKRU?mmz9!~aZ*<9Q2gFc^IA0`g(!o**rkEp)+BmM0Mini7uIlrb> zDmHXTPN>QN03ZNKL_t)Nh{?m{NmzHOd-6`s=Wdo}>J>6Cx3x@ezC)^K-j+v=LnXYU zmE0<-5KE5>5@T;E;|8ph@-O<*FgZ#TTkMfJ=EtOBZH6>=50Q2kv}AnN21$##E|whv zBr{4uOxvZ)_$E)~@|(NzC(%R(mmQH+FV4x>=nZ0OwMLSb+sTr}KP9+COK#_Oko#?y z$*-B}QmOl1?#_0XS&gAGxm}}_X&sf))xSl#jj!zO@JMbM_ZEZDg)(E|J4tHiCOxOL zk#ygB`M&PH{H@t03Coab>sPXB=N8%UbB=5Z>Mq0A1xk5c6S>m;whTG?Nlqwt685)} zrPetCYSt^4CPe0ndcy? zGi+s7)<+2|Y$j(5pUU?nW!W4wS)3#OiCu>aQh4{dfzVq{UIf3VSGnA3}}!!qwh)G z$YNQm@>>8uK)}D+O-zwH>juaI7jsE2y(vEqrOP0t0m$ZOp4z=5zBR1Qh7s72EY0x{&tgO?alzXdgqgj&2A-oO9qSOi4gIc z{YZSsezyj?v<)=j-8uXXiB-|CYX>6S>aXQtTvS}G5fDrD!e zv$B47r4+63kuKKHWt`1Lc^DBSI}3Y=hjO6outQ#*eko2D%cNUBM;UUvQ4$Jm#3SQ@ zXcU~2OR-*JXEaM}4>`)#D=VZlqLuXfvr2Td=ZZt|X35nEm!zSC#BX?k)Rgp;mz$f* zKBEiLrA?769sNw^j*l0aa8L3^J4m;a%F@wwyA;o=l%_pJQfkcPWI?<*9{`xenN(C(UWc`o5<&|MWUmuD66d3iE+SI`H`?$g3V?~k8g{`+cI1t(j6tBX>WOR zptVH*NtIHE6=G&$E}c#7}@Y{QM;ww zE47ykM;6GFxf$X)^`vy}`%tF%7083~){@lskF?JCDUUVwiPq4r^3?6LWU4(Ef1?hv zR^^4LyDX7E_Hm;9%tbc0%xs2eE_j~B?M02{eDBv#f%9F%s4v!yigwWzf!klRlm$c{E9(qct-$xVJLaf>#K z(w!PHyk{Ubmm|caQ=r_l9SB3G+8u0E6Cc0abg|zNKQMx5F7V%vL-ZMe#PoY@Ba5hcdU-= z2~Lo?_Xf#e{}J+f&I36Y7$QT81LQ!khOC{LC#ee-NfWnbG9hNGEJ*7s-F6HRGq+o! z+Oem2C2x{3W4DM>)>z3JRxDj!^^}f>cgxbDE^?>kL;2kpDi&KErS(A@G2i}MY~qiK z?un@~|3fS3SrRC-|4x&{ew$^KT638>-b7sV?WOVlU3s#8qwGo?C=v6H%3Q_2vekK( zbZ_b|A=)Yu)#aL)5Be_oU(U*%S^H&MZH`zxZ7S3E1W95@nRv!*mdF`PL@(A%?rr=g zk$EGejYYj|oBvy0R?d{mW>ZA>?-Ysoq9MAoVXZvPDAY^ zd!o0jR@o@QN8)7jlqM3k#a+~$+@#LBuWU{IBvzkwWl?7xS?*OYE%HanY?Ur@EN%=Xf3&K_|ZyHDa>B1LDevn+aaUp)7plUr|MWc`mz5~BZC0y^}S0mE9$ zrDe$K6;EVSX_N$1giEc>W^s5kP{yT}$j9{Ua{lICDI2g>j^!T``JRC4UrqM6&5*=Z z24Y^QB0+0+$orSg#p2^ZSu!nCPW5~(tJj!HX4V7gW}6_I3S*^P+rzT@_*;2=aiw^A zo)o3j3h_$)F1K4ni${H(bZ>D-W?Jl*U)q~1t{i5)^ zQk=bQND|}zW2MKn50dfQ zQyd>RlPP8cM7K6Xj(=$@-`}+mrEbo0>Vc*tRreRS4Kb4OZkF^vRVo^j%VkoJu~JrY zUlyf~m)?HvvT@W-8S!kkG&!a&&zp1-?eQyR`k^?v9qlhQ&+Elv$84!I-6O9eCdi1m zA5t>IN9yk_5j)GT67j1>?%#5hEnc@}eY5%UZ*{oDKd6*GDJSHNYJk+6Jd^6~!Qy6g zQ(6ytCHZP$65+f-{LI?O6Q3yg*Kw`1_BXXnIOHTg% zCa?WhNX(~~lCyo0Jie(QO8uIP(w*i~;O!)qR}v*6%2G6kw3R(`y2=*|OKGdpU*;>t zN=X{hr*MFzef}y*>fw^>?=Odc-IE>dS4+_5Rx;#;$jJlV(otck{LZ(MKy`mHu-_&t zRfo#-zWXKhNu@kq+)Rw+y|~|NE;Ej)%DM4dMD^cb0rLN6k-q=4iN*h{V){S3=>N|$ z8vnD6+W)Mh@<011{m()QK-2%@36f>&)Lxkb6dBphmUkT|Vx?9~rNam zKZkpVGU`f>;u|-S&aMhX4-8_;`nPmyp+oebBMkp?fhIYTEMH#5nW~rQZ%m|m+i;e> zID}gp4+idSL(<=_>{`Bg;lpQ0z5l0^lLE&s=6rc zAIMg>^Qi4Q!vmLb7}i>I-*qwzEcbEkY%hA>)u6r0Q9?)fvgA}5&ZRFY@-bxkpkvez z?8?o*2k~3=hRO4-cxKU>D*^u@H=mNaZVWLgC3G%RrT0KZ#$GZeW@j>;tgleyKAwE* zOg{8{&Z?{b@#%B{ANr5NBViOjrk}?zx{&lq`phf{!fELNYDO#ZN9J*P_YI1&tniN> z&(L?Ve0x-e<)ELW-)YT(9_z7k*XK#iG!z3i((XzqC%U}kQ16{Mt@g*`{Vcr4PNzc6 ziuI=!vH0gsbgvC(=V)hE+q|WE_%6K0#PhPp2a19Vux@q{CHEj!h%E~CFPU`UBMR+R zIbU#x+Kp{!<~E1!m9AumB{AiK5ylS_8TTli5ABsOem&m z^UV1^@4|O6H75bZgv%^?*^8f(l4K6Z z$8hy)WaM4rqIDE0O`db-P9JvGw~@f>z{ac0uBqilQs zkcGSKiD-436R$_oYPbjX2WqgHaD=aYJ#ek~MfIl^814_^y?Yt6oHSUlvj;02*0DX# zo&clPc(l36EbBUMg&iQl@jM1Gleuz75v!^+-kO}jyH_g14q5OkvjN5KXQ=M+p1=EG z@a8}iC!FS-(jS%-r%>s#hpsuw7#*C!+0Wha51otu*Dw@@#qq&chdRS=XncD?aGz0B zo=C=3&5mN{q10*maeK-Hc62(%$W0H?I9knStIk}?I*g5SJ?n}t(eIiS-!c>UWOa_y zBi~_o>IQ~w=b*Y~0FQ5+MW@*Wo{VqG@sb7xd|kw_mT@df+r)^t8bX5iGS0S;UEkO7 z^*>*(el};9=_6JQoQi>!8?OFu*x!E>$Ika*$*!O%^&Dax1r-yteDo1m3w~> zl;O-e|7rYw9K!|wwx}0cu;1(hOK$vQwc!$8tbEPj;Q^eB&By0%Ce0gXlM_6ZQ)4^w z{+|JP;r@ia(`4%NLA;u;Pn^pAmv^I7o}KlWO1bk`TU zcHWAUXJ^zhmUC>G12=E_Qn=WGll6Uw*FQ-S#Cr zS2f}9+j&ULHYP2ZMe5Z+9(Uc%ps|-|Vxx@yzpk{_*hl1aZQ^|wQ(iWW%b!dMj~a*4 zj8Lu=%pqemCVsU!p~0r7?_<#U8N0w+|G0R zSroq=baAQHrNcub{OxLZw`e<|@jugtg}lF>lyP#EXUe(LGOv`49{Z%$era9iy=>+{_rtoYDPA zTanEAs1ULOmf@@0Narr*Y^g5ho|P$YH>_shoems4l245DGaSFS<=p*!*q@4~+*y-p z&Iwf3oyThT8Lrtx5_kMM3c+>62Q6mT*n50$Y(}5ArS!B{W}4w>%vQ&fX+IBzXG#=5 zu;lBqckEtX&Br5`a69vZcmKk;F?lzYsv5Yw>`Z3CCbllkF}w!;&7nKg;>-OuT=K94RXZ%J%v%BsGz_3+Ed|E(@(xRM2PZ}cTJs6WduZDZjzPjvbnLZ|s_UMjr9Z1g1@ z8{W~+Z4LgDmhiRw5AkMm@z4&XU)%LuG_Ygdv=l1l>N0q0I&aS2r_m%7#q0eUuVIg0 zU=~Zy-a%*kNGgJ|2%j>MYb(=8(VI^*m!&+k=}utfS>_i_#nfDtpI>yit!>5m0hEzp5lr9{TzJMQ*rBglCt^z`Di_q;AdV;PgG#i_Y0f}$wd2?3cp9s zWo%9ux7~9%lW~N`t*^-vO>W=niC?!Mls@d?!0;h#$S>ncPbEey_>RtgO*Sn#!FR=u z9E-fc!@C>MFKAAJr6G<#Zjqz2oR$NFSZD6S>`}=CMEEf);Q$7{pLk^Ij^Es3uJ`g} z^ZFVFT+qPy^Bblrgz?h56O$uWF=5(Pk_^Ui<clbpFPAdIZ!|qF*P)Uy1IweOXdfTU@0`o@NSsA-{YG>WbVz%V!YK6~+)$`y z*rBOZ_-B&PNtN-s*=V~Wa8fI5ijoKp)+<6=7lY2 z^fBRZOehJ~^LehYk1JYxX*}CN%HCicGGcKoD4^AeGzR8wr1aMV=GCOqYfmEE9v;9y zy9HC*J*3l&m25Fwz~&KOXsWT9jIwK7Y4o7)n|{oj^cuHO(JVZ171ioMtUDiN$9z2o zy>wwr(@LK9x5jb%MSf|SQ}Zm71DzMq(yk*7RX&XTy@;R(7l`oLPr;k-=oXlv(5)k* zyt2vtr;Llr3c7BI;?tJyJZY%ltdlQ2(i(YK)slcoe^H(?50m#s{1;Nii>c;}F4D(x zXFswtPcdooetxdXWq?xvnXBI5R63Xr>u=yU(}L*qVvY<+XR3`3T9zFcH^7l=5xKZ2 zq;mV+9aee2V#7)YX4khRcjzsW&KyTQL4h%?elaBEzyG`cAbr&AkL8xDd@C76Y>z`I zcNxd+ z^(R_C8pwy=o4Hc7gaE63=o=rVwQD7d5>qhk+zX4XTi9fDn~+IP{HSu|^sBujHd{_~ z;~QRlt)pbXQBL;GN59om`n^5C-mjrpzxzd4&`&<5hU04IM@I8U^emo2{PVZ`y?KX0 zy|3|pSO->_7El!8jPtpv_$;%dOvjLE%5F zK99p!b3QuSCg`j6=IQJ-zBSioSmj%eocW9Q*jhRd$|kyCF=YW0xxe%Xt9xD`+%uCY z^*xAv7E14sGKx0D;rGg$kVOwrUo(N(Q9W1`_?0%pTM#?q7)B~FEI)CU7Y^RE-gFp` zQ;TTS*-k)3CQVkopu?SB3~C$6_Qo*;Wc4RwOFiGhW4K{w&cExQ$coy8>+n)Wn6_ZI zb9cO&s8eabk*q_juzeRm^#eEJjMn0DZU!n>{?JPCEQL$9qP-egPuC~B!H-cNGq~6* z5Yzp6G~c01+>$!Vb_B6>UkZmCyeKzU;-+^Bp20hLcy|ldYY#H7-6Yn0T*Z-^pUks8 zjbl|VRvmU>@VXw?^n7fq3eny4p0F7W1g80OO!W`?XI}Hq`3g}tKe0*gIzJm;ljCuO zChM=TyXAe%O1d%s*hB2s9Hv2M9Sc5wqfuon>vDVZc~}~rD|3)eU$~oZjMJT&6fPTr zmgyK;v55}x$wX_bWpa%DUxzY#{sQugC$Tfo2flrBCc90(?NkqP@=Fd!XR?VzpSnV;><)x)2rK#=M+jBcptE~9%jv_s( z{aL@l1Ivr|QF+iv;JkJ0Ow3^B<{PXxnSfWzwG>_3hsVgH9B&cAr|+luR9HuG|2LEl zfvM*fvMSw%jEaS{o?FDKQ;&JxYAWTCH|b`xm_@oioO-i?ka_*sf2KW?F16vMjVV4h zwzzzm!qZV&Otd-2<-p54Og16AeL8z82C!23Apw6p_nmvV}2jh}ep6TvXo zc-ror#FrB*sQCjEwUcm2R3gG*A}dF{VYI6@N8Q#if50wgjroY-$Ii^0>IsH66fJ4b z=&1!f{9;Cl?{>n>OX%oXN8!KA)U;a2_zp*iYoEe!g#%Os(YxSAOU;jTFz~~!ZxZv%vM4HjgMQL) zyl*yV^0oKivJlPU&II&`$0I@=4aF8bZBdKsv!`?|*u}5YuH3tlL!uP&qDK}@l3TGe zd@Ns@1h9T+CA~{-;Np9Q@GehiX|vNl z-DJtqN&Fl1o~ySrX|8>i$Y&o2KIy~D@5c$U`%BoOaU377%dR&ANq^m%h@^f@e>9H$ zIs+K))tY|GlkwMUjc4oyPB)prt?<<>Hl0AQwH?;60W?XsU{0Gvw5O(Wa=Xy8e+x{K z%b9TT7Kt7i+)LTQ?1?$(UHQz9e;FK5+QsOSbZ)o0%A?RsCSML>>eIsv^?Zq6q#nga zt=Tr&3-|Y9C>j?@j#eQS2D|AK(2-ERJYx4|GO33o*8IT!_fuCAKDGBmn>L6#-@|M#0xRD-TzcV{>C@#%9 zQD5AiC#9F!68{d>+S#~1jH7EmBl;SZ(4}7nX~P34DXgPa_H5iI_T_iZECL1>5okAu zO%{*Qvw6>cSwWyt9_ zOc`%a`HyT2(#&|>V=j~9&3L!G56R7sQ8V9%EbXQY8WM0f*tIb_EojUpcQ3+BPd0bJGXK*6Pc%y`?0wW9+#8n%|p zSNsW!{egPj0!sF*CiTQo>h&hFG}sI8Eh|X&>BGeht%)5R#EWcomV|^*d;1qNeC#-2 z+nuY|nla;%Ci7-K;!;2$(<1Nk{iGJ^y4@)DIL8s^4|IFkjPi;Wd`^y}=%X2Ni*IB8 zxeF=&zfjrlORvQ#*zb(R>+T?iS!Z$U^=DT4-{pgMXY?z~2^%?>*qjB}s^>7SZ7Me+ zY>2F%N9itqe!jSf`vVk@O zhfsC7j89)bgQ6|5m$oqI@?DykWfSapm9TmjZr?QF_;zDjy;@1_qh6TpZ%sjFM|wP- zNv5(dIZe%JW8IY4TPJw^I1-Q8(L`8nWOw{3R6i+VIq3`g^i&vrD3YZ1%9!~UQQD@-bIVjpSINM%%R`sGu(0Pgl_ULS`7b( z#|t=c%b=|=clId%^?S`X`^XDikE^v2=g!0vFBKU7P=lKH-#BBO$$!a?^tG}ia9$PNd%;(5CKk}#PVcgvV_m<^MEa=7jmNQXkTZ2*CEm+swr%O8% zX3y7TcWNqOTeJE1)`GViQt2I4L-a9k@^0PY`RNAU+8$%!=r7doY|Yef0TlI(=3ZqX zho^Mlx9=!iy)5u~G@BpJ?r0pSBCkyuqhfCpmcN%~zO%7taSLaI#q{6l&6JorYJaD3 zR_8G`+NGpj_9G|$2E~_7vn?Zr(y$uNhX?ag?*tt*httiZlE{(W_**-kV^?Q$*dPME zlFsCupTW$x>bQkgVd$1Zh_5AcMzqFl_X_IsGgs z62!@V_I#OsoeM|bv#a$5iDr=Fnd^NtGqEOsr&X)!^Mm}A{>nG=M|L+0ohkm8Ab}~y{?CGPs zhE_So6cu%6oL2(n(azW}P9x9Mi07O7v9slA!nf8^A3l}dQGZx+O9|7#J{0@3rRq~V z=Cu0E(b1Ri?di?J=81%7r1RorJiWAeEx)qLESaDpH8jS&pi`?{ za*yS)edK21$DG8))1JHTKDfCXVq4~n+qRjs=#p@OI7cCXTif`!Y#47W3X-j52zFi9!UGms1#~;D`ORK$<_>L|I)VsY55wub~A2 z!J6!vJq_!%J=s|Fi3FWac&v!RY|tg@rY}Wr$uo4fq_KW#E)%-`;e}&+d=2|?u6GTO zPOW8k**=Vp^u%skItNUC^S4(GC!Bv^xXz0CH*ARPrixZ|= zyuFg~Z-?=1-*v7l9>iyO70P-;I5>YW6W7FJmaf3ilfLvi{hH&BI`la1f$x=}oLlO^ z;MO~NggnnI!UyXK9yS+GHT!uo-FRf z%l1($Tvo(?H($|jp*_Jqvza(L8(rNBEPl*EcjXbk<6&y9HoZX zwGM=}twAYOm;bg*0`K5uY$c}YwgE54f^;6|to=ZtlkF{6&Wty}Tc@+bIY za~NvN>lw{Z()+@@{6zYf7Z9xSip0Ho_-VGI%D4{of>sP%+??$6j#M9?7v0 ze8M~OOYf7m{W9)n*RW!lIyHKi*z)fi2Nup_{n!{RCa3bOav@rtlTp^3&dVn+7&~eo zzqi|?;kuXgL-w&XtR)EMQzcmSO6Az}NREHaqP48${l>0XoasidXBqVU zxs}-VDg3#9fH`mNxL){=yO-2yczS|Xb63-8`%6sR(m6J`BaV*2m}D#>Yu6?0HmC7h ztB}k_b+%X<(tQ0{f=B5w<-kx9&TMB%)j>M`I8WVmMaHe_O5Qv>Hjg?=P|-(jJxU@V z;ux)Osp2!c1nZBrlt0X;VUibbCXHw4`$C$iohIpO6c#lH*rxUl*MUt*yx9r2nr|$c zP(jUQZ^l|Jz;-$UyE>_$b+ z8nbzZc=$S^Aa%q|zQ_-yzdSyBhJ_oKGS14Gyx=S<&z4h_c!*Iy^~hLtmQlag(9>cA zhl1yj)m?+cqA~R7bC9L}??`@AMB0fLjM>wJvd+#d>GGcYMN3iew4?IE2$r^4j@IFJ z@8JB*xQkM;36UF%1m5r%M-0D)R!0W*=-%; zRZNN99zxR(ZiGxz=lq3wbT{>)@ajq)E^?-Hw-+5$3#c72hT|8uQhnMM@9CZB*19#t zeW&rQTLh&~JEA^5mr%PbVomn5{elIX+_mYsEsCds`RKpdOU0hA_+HE;Yw~bT2ip-k zWCgx&TQfdm1})}`H40N*zNxRSe-olyo2iY)5jn=o8;(2NuBj%o_ z%EAoKb0%nP+=HIaY3wGyz_Q^5MVWDA{YpXQzqV-27>MVat|X6KOV=ik(CD(83dhMT z?W@m@$DY*v>y6g10A}BLgueAi%xYX&ao2|Nnq~x!ZB6K<-}HRZn$mau(I5Gn!p$8x z*7-ZbzcnM^Tsl3*&LwoYCI33^Vab&S7F3KR#HE10b9XUdYd&5(OSrMxo}~wO@;9&v zD`Pq^Ei;}abFXuI!b#GPeP`6C7|a)hp=V=;XZ1IV#;>G{VhnoAhT|ONM6dgY=>PaB zI*oVno19DfaAyj-#gOeE!S3f(9541_e2G3w4qIXQ> zKj=oAs~^cxoJ_>LD>QS-XGXVHjGEL)gvvF(&YnoMaTO{j=3s8ngPUE;X*c3LgF7tY z|Bw4W+Cf@zTv^rxU6d{Vn#-g%6;eEYf^5;7C+aG%rK&McjudSYC525gLw~e{Y*3Of zS87Ff$YohIv#&&NJ0|`^Or@#f4C!I{QD%%hEIq6qN_piiDLGpz?XtAxI!9%VzP==; z6w9EfVPa-*Rt)=$m1U|gZZ?;#ksoC8C{KwQ)<(Sh{gH29I!X8Xa=Cr= znM8zG$>&2`#H36|j?YUG?OokuNZlK`^i@+T#s!Gp>V;z2beCK-|0LHsH;VIgP01MY zPyFI*B}#X&Xl3ck%zdXtbC|xwsaZ))w7OJY(vrWOeZ+K7o3mfG6=0H(Su#>l&wIwz3l_d16k+aV?OXc?vS!H@ga-tf= z>tZu`6?#UxM)--rtNmit(ps8$ACYq&|D?CfllX;GMeo{Zne#bQVut!l!2U&I5)mv@ z3~q~lUa<^$v0wbA+?A%YQY5JAs)W_9kV)AM@=>X~6e`)u$`_?_zrUtbAA2B6UdBqs z_mkpV_d*7~(UhYK0n)Evqs(rVE88vpNVRLBEVC|<*Hd$(?|2<)=5RtZ<|>N){@t=+ zbc0B>zg*GyEkk#8lQ0t64XG0F(?v91trqi) zkFs(66WLv`QqHG$l3z16$=}ycWI;-@XoC_0CdWsXgMdFvP_HE(l7b=POv7`Q@4o34|C@029R_mG$?r^xEs z0dgkoq703Qmlg&w(pd3Uk~hbS(V61IyFfCe^can-v?sovqip}bP%7&DXbNY{+genHuj!r$k4Nq$P;sorQ8|{T1119xd+PNs@o)xr|8c zE~lTKm#FQ3g^eC!7Bxb;_P8WFhFz87MUk@Vhqt6Dtdxx_yUWSV&!xClU50JEB!^D) zmCo-EN%Dq1V&>LPMowrd4r>b~zx@Gm&9;$!v&PHxTVAp>c!S(J;2`yti=TWc1+*`4g!lkH7bn$I->oa>pS#I4wy&9QKtqC5PnLjBt5x{Xv$L-jlv}ev8x2 z-*UZugS0eDljc`qWkLKqIc@ABD;7sdV6V1P6{abJkM$Dk$S$I=_@sDyz`o_t!oD~=KIt?44+)Opb{Z=Hy2 zxi9R?+r+pJ^Mu+XTQR4#r^s3|S7jw=+GrLKx`9#cg_4|9>7>LO;8e-)YCZwvGHABBwU zSfN$rBC7Xhi`R#&M8xE6!ZX58jE*=WUS)k2PMepAOWW@X+q8e8SKDQwC#4|@{@fE; z>r+LoMTZC|JSs*dDT(RTM&g81gcz|kM?^K{igh*ZBH#L|P@a2PsJagp!!0U> znZ;vq;gqkqYCJ`RCA=4ER!SoJy{uSmHcm)fd?><)3{-y|_wIzWWow-OVkHwew8-NXymA~9z44&mK2OYE8PPfXkAB8F^h7nX&K#WSD3 z;$Q3~5nJOSp4|-)jt-8(F>$SMh}tiH&HN>Fq{QWuPg{hXcM(NLLxlcBRdFYI zn>ebtT&OI%B;vGUMCI}$VsA{d7@zt^SfB6_6(_WXMx~{QsM;kqEp`>>7d{c?y`4qq zjudg+tecqn{6DdvOOB|0u~fJ8fx^)rK`z8vB6|cpFP5B~We1*s!8YWaVCyI9$r;F=j$BOj> zl076^)`u>&8x&0PfsyM=a#S=)m?lHt`rx`dW!Iy6~b?@ndoUbNrd}53e5{j zBI9AbIJ?zPYz@6CZhf9AOuFQYGh>^Dz06u6zp7RQ$;S$tT{p#G?{IN9LP0pYZWEd7 zv_xXMgXoqaEuNjV6er4t3sr}4B5BT75wc%d%&*Q6Q`~}tOq##=+*Bnj*2sz)y;(xV zXq4D){6jcoH3`LSDPqKuSh4nRfYAOjMeH1@C+-iI5K`Cm#NEM&rx{UV{nqVb=D!MI z+8HDqqCbkJ<2mBP?jWJ~bFjGIHciO5>510-BO<7Ljc}E=6*uxtMUnSX(Y(-ATo^J= zOqTj1qPtWJ_4A?P`L-hA^Jav&GUbZ!U79Jj4E-%am&u5<<|UZ-4lSIs*gIS#?3|dbnAX-2Ec%<@6RRGyjUJfyYJB`>kSI zv$9BVXce3Og^HI;XNqq=%SE2vFQHYoRLEJR3a@~k!q`+w#9kO79Bv;K*GzwiNde1* zaqv*#6+cWw4-OW(Ee_&o?Fdmi1Y&^-?b(msTd`v~LpstsN}JAFmhQr_PJsDh=Y}wo4+fvx{(Q zA0l2)lNM$zheYlBJz`vIword|N7w`o6Zc9x#IWH)6YnRv<=CnQy6a0&HdcdsDxdMX9UI(RT-xG9y9tCC}7}s(H5j~vX0C`sE{$ftXYxv=c+Woi0_R1stvjdJd$+W(@(ISvJw z6vpsDx(601SID{U#1Ju@+W{MiUadmeiidn6YT!FMXZ#}5 zpqbw0_bKUE%c{KFoK|e&UJqI9lH6Impbx3LC$b>#7=7*h$T+=`F6tX_e=v#H%?;EV zhLCJ~i~h!~I1W2T;=s!!zWjs2w(sN!TY{zDQh2(OYbwvt>vNL`J0(1GE19Bf#bC*` zM4W!k*mg-^354~Ohzld#0Z%z}%$p}b$zi{+(>__k@1 zcJ~&)-&fQ9r54i^cJpwJ6J6a;W7lmkWu8hD%v-^eBb^M4k>j5C0OI0{IAY$7`Zqz$ z*fW{peVKga5h248Fz6pvK z^6ma|E;(C34?Xs8ZRY)-k6dV2itI!W)-KN`@RuLUq789y^}_O1H+ls3Ca+`zEid{o zV#o?!G#QfpU?xW#2J&z84PO4;!=e3`2=n@dS&unrs%8`GD8(TsWwZ}CQS{uGNu%SK zzq1F&FPO8b#gd*;JFt3}$llts{HL~v`8651#%<$rs1!cWTrqE|pkH_xK|b1KpFNMS z{ZZyg4?|PMfE|&`@tgS`^+k_qbt)wL%u2Kzm(v)zAJ6ZLag>^kX5wVZqRUxr@R8oK z@yO|%!c6%lovHb>v>aiN)OA{eVp!@I&0MttqM3rm(OL#;c#UE-}e;}BcKX2*x zXf~0u&u@^fbA(TtTkz>3gOjWsy-XbVZ}Abfr_?jB;VF6U zz653Wp=3uoe^zVbp*fX^w*zQa*vy^cPvl)z#Z=ysZC#YnG?|ZnZYcTAdy(9+ACn$| z7`<$v)iV;O&7X*KaYO%}IVS6)n7I8Lo!xXZ9Hf%pGZ2x^8=kc1R2}6t8ha^C)BM&)~glG;0nH;J~3VbnTi+)&4Ie4ADf(J(MSo zYY1(Oq5qIv8V@e!m0uNCca?D~B!{p!R;Ud=&iYrgc;Y;UJ83sCRtsSA(RRPOnQcKvI%J?chWey0jFdAS>1mJYx;)r(RKixE|yF=ycDJ6mqgcT(Qw!h zU$p|#ul{24vPc5%Ni#}sD9#18xNRASkh#Z!>FRjS>%=BUj=lqHs1t*cxc7r0f!Em? z{FmKYqgePS2Ipumt_;&;<+=B$x{jyh!9>Dy?K$q(M7q=r+N%~(t#g9U5hd8_4kb3U z9q*DN6rPRX;*HPbcJD;VR*Qof(rE2ZW7p6>7`m?FRL)Y&PsH<5-WT@;d-->87Gr-8 zCG~fEZ>cW>Mvq;j=}A71ad7ykg@zsw-A5cX?4(l{A}#jFC%t9BW+!UR(clNyj!io)1Kmv85m@#<(a-Gi%UNE~(A4g@aKwO=YOoW`0K~ zk?>_2BDE{E?OQ1`yUID4d_d4<04oBs;o=*|)N38o3Ii zkye<0?gg@^koBKN;LJ&ksP-XY{~JVq7f!C9hD+&2W*_-V@y7}#ON}E-_9b4?-FR4X z0THpB$o?NO?7V?r{Wd}kOEG?)O~7a=cIpfxzSm%s{<$FE=M-L3omgbF1C?1O6c$~? zqpW}iiD>p;n9TIWiu_&gLec_#R;P7AF-;AZ#Md|_y(7QFg7Fp)nAR-_#s1MekKDy( z=?tQN_|bD{AX(MrB%SO+PpbuV?Yas(?|j}J8^XjKGd}u`q+EFrWA!Ac$h5)B)PYsH zaa_Ez9*Gbo4AyG#?SVh)v8zci{!C>0J=8v$V|RQyy9TME8f(gg;!66uT%p!6j}Mlm zSas`5)YP4+Ww4bo2N0vvKhOl|M>9w1%Iaxr}@P)PPooOIqMmV+BFFX-Nda5S02_} zV!-9)th?>WA88v#4nD-?z*8uG=t1cB+1%_q1g}5e8J@h6K}Ht{)okIQ#1ckSsq%h6 zUl#7l;?$aKLYLcdw_^>1CpvSlMGoh8r&y}Ej_EHypf)a_;bs!kmkG4PHkY*#c8~d})`YeqXXRzh* z3`U=oK|Zk(SI1$b2dA(pw-d+0o_vySAv1Uu9n(K^_r*EfzrKXN)7atS!#3Sh7^un< zb#Fh{+#>0lcZvk39{jwUz@zAIoH=jFlJYz0GDrW+0 zXd3Rveb{|8mb&1sR;6`>Hw^jg5K(oKkq|eDd%zfg=G0$3#-dVx^pKmZ5 zF_d#=*V%Bh58lHxxFylT=^1A@ClOC`b_Gp+CsG`!OrI07ls3*Kd&gNOOkB>++m;x- zHsQ`zJElY?k{0C6tPX#Mje5#GgKDNfPN00#N51cMAyKkWJUrW*)iCPdr*{T}M9@Y@>Y$^wycE{zv9ZZ^S&heX8 z{CZJOQDGMGU#{?F!z|87PiMmMzwCZ#N=omET=)5dQ_wO-nVsZ=#0U<^mf;@%j<}HX z92p!-h13>aEK()Z`yFTRl%Vi9isr8|+{_H({-Cc+nW)3$1qYe=c?rX}=W^nuGrGlB zQ8Cs=-un*?LG`@+JchW>-I$#I851#$0Sf#0;LsQMC+&Qfdr0x4EZTqXW%z(+j4T>Y zrFg>c_T^k$w43L0+qrh4HwSWlkQ|qf*%NlV5B%D*5wRwMi0t43gaL(o}mab(S zE}PDWg(tbPE|G2}FL)TdndW^yJkOrRvxWNPzBtR3@h=Fwx0{c13y3`Vmf8mnbT$pZ zub?Y&_44exn?vHx3H%!@$DRsf>bH$xgiRL$PZ?ljwV$ZNSqxqVfs$v?oO_lc=~P0V zy+i4uFGlOiXz6y7$MG^G=Ko<#rV+2w4ConJLz%WZ{z^mm>imxdf1}a@=Xf*<`=a&Y2~Sp5AcqE4$Ff*l{1W@jM7%0B*!o`` z+lThSrE4K+RrC4yE{Owm!_nLqOvBzqs65rdq9ubHk87#e-O7*K#k8u`(3+aa=Hk(~ zKkZKUnrWC9&*V?`Mk1RIBE4}Q2XsQ%BR`&%lP{u#qc4@F{{zM_BA9#!von6?@a|2Kr_{HsKm zlrYmdi35FllQ{k{sgwLrDAePV${co@rLbM~8>VG{=rL!K%HR{Jt^^abi1 zW>E614`K2Ncue{?7O&jK_551a$N_ z^XAefe(qM|$4(RWcI`*8d>PS2Q*ko;%%a^(Y3&GR=hJ`eyV;2CzF8P;Z>IEcIW3L; ztX~pKh*dl*l`@&xHizV3J#HSzVNNTYyB9^gxQLl-B@wS;NGiU>1@8==cKl$Bc3-l3 ztioV-9NJ-b>E(8jziR_o^h}e~7fskt^g+IM8uQ<}F{{y>w*G~D%TVBzWggSA2IGFZ zfn-ZBEV9oN{bd}HVVN{Md`oC{JR9og@HcD+uZAq5P)e1T`+cz=xQhA!85RusN_lM% zleG%@u4_!+-y_%@aFV9)GI;N-qr7u2#;Y@stx)D$z8Cua;|W<3$*86G+>r%W4@JioWU5-L#ZmXC5-Y|7jdt+9;A4!-Ovl?BBARwb%3bH2*o3 zUGuOqs>J771vk5u^Rq6RX}xaH=a(C;LmRo?dX2r;qv-W5h5<^&ER*cb;_bei7+^%` zk}h0$V8nk`MU-jIXU|VH=8TBtjdpkb7@OhWb1;vNf2PfOF4YN_2%h4}*nP7ZV0eZ8 zDWlMMw-MXgT!#ET!=jrdY?OL{?I{x)b1XQodL6aIwWKWgK~?rHRvLdL)Z-J?3u~CV z>oYY=rSa(hm06OOT%X~EU$z{5D^4;X_zaUOBzdy5Cw8mU@Va}3E^W&&4_J?8t19ts za~b}pfjxT`<2^hR^&=WA`(1$R32XNI?`8gh!zAkO;joGZCZ-BJle1y|?L@|!n)1$9 z0k5&W@Y`Tbv{5%4Bq#FzUOoGtZRO6kY6=S%u%+i%ZXCTqoBKLW54XX@VL6`vK2X%0 zi;b2(hhpYnpEQXNU;onM>nYqetmL0!Ch6aHaz3$*g)f_V-bb58?bp1QI?qjCdnCSo zz;$3Wvs~tjHO6F_Xj86k;HcsWJ?@o zQp65SLQ6QjF#%tbp)Bj8!s4!SEL8Zx2)P>8UI=2d)Hhb0@W=OY4we6$k-t|!OFvWA zd=QL(Xpel)G$LAnv+4G3=)oVIvgiVsVxDg%raqhK|Uz!LVO=Ww*2C5N+kogJSAYT0>U(o zF>emiz2_6Y$zSGy)Bz$w7qh_H17W%lXZHzMtq;NRRd3!^96|5U6U?{8V3RkI?Y~cw zHRB}rI~;NQSw`6v4KgnK(;Sn>C;#zGoZ7$;F@!g+>HJ>5hsb~ZX&>>N`i=x1RsSJ4 zX)AseJGi*QfF4e6TpDx%Q>%7HuR4ayD0zH~q6u94AA7#PAVVU8)vhUAUK>fxn{6oR zoaTAzEKXgXPP$Y!j&3K|b9fK_>)N;~X^nPt8nf&dF;YsF#8E@Jp_0%3_13)nxRn`a zwfWL}AlGLFGv>f3yxYI=tH_GuLXx!-`b;|eg()|RL8Xc|jY@jE7!vqPo{Yt17`{AA zPDpp2j8(8G7Wb2CUZ?^Ub~sf2IteRJK3!-`>aTWdg6`8hQP|frs;d5@WrH!gbk94^qc{ zUJjiP4RL%CL-7k;rq4dW+2>h=%$kJW+aw;(Fy+sV4z6DvOi4^DpHwIFC}%D9hv$>5 zSBl-Oap=En`v)SAi!n-D~yY|(|DMixKQd0CsX+&hJV+_(Q0_Sq$Q7^ihVZ{l|9wUBo@@%|eMTxXhC3QE@3$Ozer05dqu9j+FmhZdUusjC6T6VM z+g;girB2bhaITHK!lSdwL|@&5vh5OFiqt88REcV!In#P%vh`;nJ$Ad%OWA_i#+IzS zQ$w0V1`WYuxLmLvjR8-%d1eB#mpoC|aA391Z~XUv#&~=(^9N5M+g6f0a%mjB7{KJq zY5bjRNtX3Cnib|zd$v1wmsZeav>~&#Ur^OukNiTx7x9s5!)R(M))mwFv&o%=n-?*PDrIo#Ep)17`F3h4 zyU#oFVnQjw9=?odi{x~>KXBQ)Jg$e?6Vnt+ziBIZ zZd{Dv@K%QRQD>i77DeT%{91j0q!kxfwyXl(JIg8fvX61EqgcGK8pml`bXG*Mvg9bm zM-q8p9MA4YuSmMOhrN?5(a2Hej6nf`DN(qs+DzDudw6Q?;nl}wjly6nWK-OXHkP=iE)Iqu=k%=i;c{ewbMHjHH0L1l{D-}A~RoJ~KQ80DJA z$&5ox8G911t%?-CkR|f(MB0zUKhjVzzrZt^5(7Tw=pu^BA5hkRFLPJwG2C?)w_-Fn{$MP*I`T~3oyoNKABmKkOxcXvC{CJ+&0|?A z`sGliYJzv?1@<}=V%pKf&vI#sob;I=c8o+Z7>y01c%ZYFgT;r@?>nA<<>Of7u#J%| zO1z#{itVgK9$lEu;8W*#w?~Jj2g@)uucWNz55^mg)8))Cl5-qMt?f(2A8XEZ*ihLo z36Ghsdzbdk}nmUuhQq3VVt8J*pDCf%F) zu{RjsWXJ2qB^=SLqv={t6y_gC$@?fB&vZEC_=&fDO!;@`3QG(sx$?b@52`z<-*%QO zOEr-?r-0I$4yGmVW!mk5nCq7^rFJfT2lKX(7BaJ5xHK|TkSLuSmLBF&0_A9#AKi2Tg{^#1srjNkSB#vuti05jXXa^$tL#zL^(sV@Nyh#NCDi)b%oD?dGW%s?WjA zv5xy%8c3FDQd60NUcbLQw{0Y&wI?gGF38mUV8(X02t$ttu?1I@7H& zjIpkj%b=A^d=W-i(me8NJ`(2l3N_bC3jF z_V2gn^@bX(qfha%T7}<}4Kc6rByVFq86VX+)hv(cDP2a+JI*zW(J0*yWx0h7vg=n9 zJ?uN)389!jk!Qv{8UCs&QWx2snTAme^w4AI`5Tm!tD+U-O6A3&{L6n&^tT@*8?|!n zKCCXSLt&sVu{S?cQu>3>?S+`kmnCG=ZDRd0I9t)eLhY3V8`twpqc_DiGf??v&t!+^ zs6Ud#O7|l*@dG*aB97vYTGH-kptofQ4$V4T-L{Iab9W)_^NXb+64dp8e%BsyyUP#y zToF`nyF<1+6s#J_zEy7Y)O}3fagsz%{Dhu=Pr6GxaYNxHUekZ$SmjSd|4Nov=wtJ$ z2HVb+{Pt?(?SJ6}WbGsCpFPWbx-jfe1cSPKr@;LQ9Lj4Z9@n1m_8^h_tx`#$5dJvl$!gB$O<@MuUm4uO6I zt-DCei*kC!HWFO_7MTsM3_2jgJLw>NTrQySb1T!g%*3eXB>jy0bG{;qqdnCLRGxtL z>;N<}J9xXmm%bJGTr@a}=95`G{;;S1|7Dx2|C@H`|Lg1Mf6f0nK+^tOqBd*&Ub?>D z$Kw44s79|O&*meNeco|qeGvw7b(rj^;_H1`R!%*^CY`~j`P|q8 zr(1g{n3utoA|FJmA{04h|BiXx!im_?pV!}J^7O0&J6_#J z>-RUj)?H-GBW-HG`O=&!LH`d>)qO65qc@Wk|C<><4Xf*e?s&XJ?G4rZNGhww3o0X?hG35#+ zdd1v$wTSE8zoWZEnyNS#((<&hy3~Syo-|pSi3Hu2pkQGe*LE1QtMUVh%VZgm_KSB% zOHj7FLDQLS3{W~i|EP25FY#r^U>o9MEZMX36Sa@d(z08+6MJqSF?7hfLI7$u{sE_=>v2B^GOqa#K>mOS3 z)KQ;)mkEJY{7cs W2bJq!4fu1V_ALtMVSgHXd8e9#+@bk%i^3mLW#I*Dat8-0QV z!MWNv^lBwhcRwFLTxW$`I@{Jxz+;*RJ6so|s(g$)kKUkLl}`WQ8t!cA&eiy06fCD= zuwXRdJCra@8O6BUZd4oG;n%ub?AdvZ1@ER1_k0o)tOxKUCycyZCsCB~q4x4KKIzOs zuAzb*3F(Y8>Pw#`i)b?1%ggt2too6LQD;B4I=rP|Ljz;lULyT8m4c9Zw)Z$qWEJ(S;4D;aog7L6zExhWrr-p|Ke9S}~h zQ(82qW^v()8ZP`ocXuAYU%ew=++w80AntCICf3ds^N<~wN4?^%)MfM|MzUma3%B}J zkm2)zbIK>MjM>SX%H`bAIm>&i(WE?@f=y04HA5Zft=^sKCU2S4(wA^`RUCF_Avakc zhk9e`Z_MWL!FE<2%BJ+%7S86jQ&5^kt9b(3PR0{q=Y`eGTmsW;h*e1;s^dEYPgHV9 zb33awiV05}!{?mcOwH)QzWY5ePx(Zh(P83O#f2BvSwgW_;`oY-7UE~{0=hA;|eEAu{v*bA#HT34| zmT?>zyB&wkw%DtkKzpnM-wifluR4QJ=cU-1dLbe69Lwj)T*=SpsI?9YpS7~~nG<^= z9f{7X<;|i(*5y7UMdv&hCr{l}_OdaMP3T4b?A2v&xV40|fO6&$otsmcB=J#U2eodsk*3&V~j>#5Q*d;xt-b9APZ~in|y(Td)AFZOn6lEnb* zHgPGr3%WTU$qX@Je9A$t_3O?b3~?%;7{m?;vl&7NnvjF*x2vjq?J3mo3t8x6To-pWM~vVI#MT+Xp;h#t4%+nM^KKV4HJQNHHME0Y7< zxiJ*?x!w8lVle;S{YCX(0dMyz&{C5~*T?egRlLuw`$g2Ge#Q9ZNEQz`jd5EIT3*+g znL(AW))ReGFTDohwhZ#xE;Dkx2k@;?6ZgC z6VKpdTY%Pw6f`{~c-7C4u;6K2+OEZ4-ylL|thlpVpG@85qz@QCuKF&5wt1trU@Zn4 zpR@Gt0kT``_}MasL0$b(SR919uO{6rjxe_WL*kzLGGo;NUd|c8fQPwsbUn!L`=6LI z%NynSc9a*bWLtFx)-6ks8vTzF4HZtwDC2Nh58Y$8=|1EUqs=d~z*UDs)s`Gp_2KQn zTNKx7@H{Gj0V!q-JJpp7ll*ANu&46YZL&x8VyR~}g@LpiyLZn}E(03}QYrAvmjWdVi&XKF{gP7fosiEGinQ$G&!ghS(x3We*0>wISMha{u^riQT9;t<1Xy7K&a9j-H`qWZ83{jpk9Y?9$>`X~md zD{)r2jsb3=SpFN!_^y`;d9{FryPa9F?jA?}O(J%w4M7^uNVZjHZGaJy#}{z(wLHx- z7uXP#&L8I$eEaD^-jVa1?Y4!oll!=)x{+!B`05jG zt(2tGb|!Zk*H9g}2usIbjQM#CCI5{auT`K|S`r%u7NBBSiiffU8`U##y)+Vo?iI! zU*@cOJLMkh$u+-<^Uxd;D-N=7dkLi_66lq!#w;d@OY`G-r?icJJ?ApCz#6j?6R=_u z*;>-LExAN|^iSTDe&$_a0z1E$kT4|(*Tdt{m^cQB$#eNw@_^#v^Z%oO^gpiHg(QS> z+1ZVjP!ALXbWm46jDF%Sen{M-DP|EkkbKQ z;*gb0>7IFf4>w}z?vEIiO+~L>nW#IeJh9r({sA^5HXmU1&PCXN4&&vZw>;jZNKYvr z;i3c{a$QP{m8?13;GYw!0=Kq1JY|a ze(naz{RBnHe~?w^%CDTuXnd^V{^lJF+qI2J=~MAcI*n<2AtkF6Ie63^i}kaKZ|T5$ z>}QNpMl$xx9x85@v98Z2p1OxoCozbcxJNYp>WYb%5jL)VoPE=UgK9mv_@s^DPK7jO z?59?LHmleFB(tiVvRD7uZLY%>HEYi9a^O+dyG(vw!Q<(VX_4JVw}{`|e`Uz|oB6z+ zY|g-IdEEXlfP0trl5r}9d+t{_HqH;3CyVJw9m0(bNxs*3a$SkDkJnHW z8qAb_dGvn#noHfLqLrG%)wLVw`%H@iQY%STiNt5<6Ydu|;qmMobu!LOkUYXLH$D39 z_F+|;BLkfmVmRe515b?S{-Ix#I;Ef+IGIh7YGn03&+higXkIzMsv|G>c1xSi6A2uy zo{zbT4xx#oNN?VOzEmVnb@tJAHISN4TeRkUrnkikbl+X)roBBAEPvuL{Ta6#BB