From 6c632d136656811fc55f3a61966f94e945de984f Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 15 Nov 2019 21:17:07 -0500 Subject: [PATCH] Separate native from the core --- .travis.yml | 4 +- Cargo.lock | 18 +- Cargo.toml | 1 + Makefile | 13 +- README.md | 7 +- examples/compute/main.c | 1 + examples/remote/main.c | 1 - examples/triangle/main.c | 2 +- ffi/wgpu-remote.h | 5 - ffi/wgpu.h | 142 +- wgpu-core/Cargo.toml | 48 + .../src/binding_model.rs | 9 +- .../src/command/allocator.rs | 10 +- .../src/command/bind.rs | 6 +- wgpu-core/src/command/compute.rs | 246 ++ wgpu-core/src/command/mod.rs | 753 ++++++ wgpu-core/src/command/render.rs | 673 +++++ wgpu-core/src/command/transfer.rs | 355 +++ {wgpu-native => wgpu-core}/src/conv.rs | 4 +- wgpu-core/src/device.rs | 2072 ++++++++++++++ {wgpu-native => wgpu-core}/src/hub.rs | 299 +- {wgpu-native => wgpu-core}/src/id.rs | 47 +- wgpu-core/src/instance.rs | 427 +++ wgpu-core/src/lib.rs | 221 ++ {wgpu-native => wgpu-core}/src/pipeline.rs | 9 +- {wgpu-native => wgpu-core}/src/resource.rs | 12 +- wgpu-core/src/swap_chain.rs | 244 ++ .../src/track/buffer.rs | 4 +- {wgpu-native => wgpu-core}/src/track/mod.rs | 9 +- {wgpu-native => wgpu-core}/src/track/range.rs | 0 .../src/track/texture.rs | 2 +- wgpu-native/Cargo.toml | 40 +- wgpu-native/cbindgen.toml | 10 +- wgpu-native/src/command.rs | 313 +++ wgpu-native/src/command/compute.rs | 316 --- wgpu-native/src/command/mod.rs | 769 ------ wgpu-native/src/command/render.rs | 848 ------ wgpu-native/src/command/transfer.rs | 421 --- wgpu-native/src/device.rs | 2396 ++--------------- wgpu-native/src/instance.rs | 589 ---- wgpu-native/src/lib.rs | 240 +- wgpu-native/src/swap_chain.rs | 261 -- wgpu-remote/Cargo.toml | 8 +- wgpu-remote/cbindgen.toml | 2 +- wgpu-remote/src/lib.rs | 61 +- wgpu-remote/src/server.rs | 52 +- 46 files changed, 5894 insertions(+), 6076 deletions(-) create mode 100644 wgpu-core/Cargo.toml rename {wgpu-native => wgpu-core}/src/binding_model.rs (96%) rename {wgpu-native => wgpu-core}/src/command/allocator.rs (94%) rename {wgpu-native => wgpu-core}/src/command/bind.rs (98%) create mode 100644 wgpu-core/src/command/compute.rs create mode 100644 wgpu-core/src/command/mod.rs create mode 100644 wgpu-core/src/command/render.rs create mode 100644 wgpu-core/src/command/transfer.rs rename {wgpu-native => wgpu-core}/src/conv.rs (99%) create mode 100644 wgpu-core/src/device.rs rename {wgpu-native => wgpu-core}/src/hub.rs (75%) rename {wgpu-native => wgpu-core}/src/id.rs (69%) create mode 100644 wgpu-core/src/instance.rs create mode 100644 wgpu-core/src/lib.rs rename {wgpu-native => wgpu-core}/src/pipeline.rs (98%) rename {wgpu-native => wgpu-core}/src/resource.rs (98%) create mode 100644 wgpu-core/src/swap_chain.rs rename {wgpu-native => wgpu-core}/src/track/buffer.rs (97%) rename {wgpu-native => wgpu-core}/src/track/mod.rs (99%) rename {wgpu-native => wgpu-core}/src/track/range.rs (100%) rename {wgpu-native => wgpu-core}/src/track/texture.rs (99%) create mode 100644 wgpu-native/src/command.rs delete mode 100644 wgpu-native/src/command/compute.rs delete mode 100644 wgpu-native/src/command/mod.rs delete mode 100644 wgpu-native/src/command/render.rs delete mode 100644 wgpu-native/src/command/transfer.rs delete mode 100644 wgpu-native/src/instance.rs delete mode 100644 wgpu-native/src/swap_chain.rs diff --git a/.travis.yml b/.travis.yml index 9815f3e95..2b61fbbeb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,8 +58,8 @@ before_install: script: - cargo test # TODO: enable GL backend - - (cd wgpu-native && cargo check --all-features) - - if [[ $TRAVIS_OS_NAME == "osx" ]]; then (cd wgpu-native && cargo check --features gfx-backend-vulkan); fi + - (cd wgpu-core && cargo check --all-features) + - if [[ $TRAVIS_OS_NAME == "osx" ]]; then (cd wgpu-native && cargo check --features vulkan-portability); fi - if [[ $TRAVIS_OS_NAME == "linux" ]]; then cargo check --release; fi - if [[ $TRAVIS_RUST_VERSION == "nightly" ]]; then cargo +nightly install cbindgen; fi - if [[ $TRAVIS_RUST_VERSION == "nightly" ]] && [[ $TRAVIS_OS_NAME == "windows" ]]; then diff --git a/Cargo.lock b/Cargo.lock index dedc0e556..6a266a702 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -619,8 +619,8 @@ version = "0.2.48" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "wgpu-native" -version = "0.4.0" +name = "wgpu-core" +version = "0.1.0" dependencies = [ "arrayvec 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -632,10 +632,8 @@ dependencies = [ "gfx-backend-metal 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "gfx-backend-vulkan 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "gfx-hal 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "raw-window-handle 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rendy-descriptor 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "rendy-memory 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", @@ -643,13 +641,23 @@ dependencies = [ "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "wgpu-native" +version = "0.4.0" +dependencies = [ + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "raw-window-handle 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wgpu-core 0.1.0", +] + [[package]] name = "wgpu-remote" version = "0.1.0" dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wgpu-native 0.4.0", + "wgpu-core 0.1.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f8760017e..d1b20a801 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "wgpu-core", "wgpu-native", "wgpu-remote", ] diff --git a/Makefile b/Makefile index ed0a88493..36018b7bf 100644 --- a/Makefile +++ b/Makefile @@ -8,8 +8,8 @@ BUILD_DIR:=build CLEAN_FFI_DIR:= CREATE_BUILD_DIR:= -WILDCARD_WGPU_NATIVE:=$(wildcard wgpu-native/**/*.rs) -WILDCARD_WGPU_NATIVE_AND_REMOTE:=$(wildcard wgpu-native/**/*.rs wgpu-remote/**/*.rs) +WILDCARD_WGPU_NATIVE:=$(wildcard wgpu-native/**/*.rs wgpu-core/**/*.rs) +WILDCARD_WGPU_REMOTE:=$(wildcard wgpu-remote/**/*.rs wgpu-core/**/*.rs) ifeq (,$(TARGET)) CHECK_TARGET_FLAG= @@ -30,7 +30,8 @@ endif example-compute example-triangle example-remote \ run-example-compute run-example-triangle run-example-remote -all: example-compute example-triangle example-remote +#TODO: example-remote +all: example-compute example-triangle check: cargo check --all @@ -46,15 +47,15 @@ clear: $(CLEAN_FFI_DIR) lib-native: Cargo.lock wgpu-native/Cargo.toml $(WILDCARD_WGPU_NATIVE) - cargo build --manifest-path wgpu-native/Cargo.toml --features local + cargo build --manifest-path wgpu-native/Cargo.toml -lib-remote: Cargo.lock wgpu-remote/Cargo.toml $(WILDCARD_WGPU_NATIVE_AND_REMOTE) +lib-remote: Cargo.lock wgpu-remote/Cargo.toml $(WILDCARD_WGPU_REMOTE) cargo build --manifest-path wgpu-remote/Cargo.toml $(FFI_DIR)/wgpu.h: wgpu-native/cbindgen.toml $(WILDCARD_WGPU_NATIVE) rustup run nightly cbindgen -o $(FFI_DIR)/wgpu.h wgpu-native -$(FFI_DIR)/wgpu-remote.h: wgpu-remote/cbindgen.toml $(WILDCARD_WGPU_NATIVE_AND_REMOTE) +$(FFI_DIR)/wgpu-remote.h: wgpu-remote/cbindgen.toml $(WILDCARD_WGPU_REMOTE) rustup run nightly cbindgen -o $(FFI_DIR)/wgpu-remote.h wgpu-remote example-compute: lib-native $(FFI_DIR)/wgpu.h examples/compute/main.c diff --git a/README.md b/README.md index 9f76f65b2..133f793aa 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,10 @@ This is an active GitHub mirror of the WebGPU native implementation in Rust, whi This is an experimental [WebGPU](https://www.w3.org/community/gpu/) implementation as a native static library. It's written in Rust and is based on [gfx-hal](https://github.com/gfx-rs/gfx) and [Rendy](https://github.com/amethyst/rendy) libraries. The corresponding WebIDL specification can be found at [gpuweb project](https://github.com/gpuweb/gpuweb/blob/master/spec/index.bs). The implementation consists of the following parts: - 1. `wgpu-native` - the native implementation of WebGPU as a C API library - 2. `wgpu-remote` - remoting layer to work with WebGPU across the process boundary - 3. `ffi` - the C headers generated by [cbindgen](https://github.com/eqrion/cbindgen) for both of the libraries + 1. `wgpu-core` - internal Rust API for WebGPU implementations to use + 2. `wgpu-native` - the native implementation of WebGPU as a C API library + 3. `wgpu-remote` - remoting layer to work with WebGPU across the process boundary + 4. `ffi` - the C headers generated by [cbindgen](https://github.com/eqrion/cbindgen) for both of the libraries ## Supported Platforms diff --git a/examples/compute/main.c b/examples/compute/main.c index 5f6bfc665..031e5209e 100644 --- a/examples/compute/main.c +++ b/examples/compute/main.c @@ -59,6 +59,7 @@ int main( WGPUAdapterId adapter = { 0 }; wgpu_request_adapter_async( NULL, + 2 | 4 | 8, request_adapter_callback, (void *) &adapter ); diff --git a/examples/remote/main.c b/examples/remote/main.c index 22c6f4e73..89fe71b4d 100644 --- a/examples/remote/main.c +++ b/examples/remote/main.c @@ -31,7 +31,6 @@ int main() { WGPURequestAdapterOptions options = { .power_preference = WGPUPowerPreference_LowPower, - .backends = 2 | 4 | 8, }; char index = wgpu_server_instance_request_adapter(server, &options, ids, count); if (index < 0) { diff --git a/examples/triangle/main.c b/examples/triangle/main.c index 8d18af2bf..379232059 100644 --- a/examples/triangle/main.c +++ b/examples/triangle/main.c @@ -46,8 +46,8 @@ int main() { wgpu_request_adapter_async( &(WGPURequestAdapterOptions){ .power_preference = WGPUPowerPreference_LowPower, - .backends = 2 | 4 | 8, }, + 2 | 4 | 8, request_adapter_callback, (void *) &adapter ); diff --git a/ffi/wgpu-remote.h b/ffi/wgpu-remote.h index 59cae1103..22223daba 100644 --- a/ffi/wgpu-remote.h +++ b/ffi/wgpu-remote.h @@ -27,8 +27,6 @@ typedef enum { typedef struct WGPUClient WGPUClient; -typedef struct WGPUGlobal WGPUGlobal; - typedef uint64_t WGPUId_Adapter_Dummy; typedef WGPUId_Adapter_Dummy WGPUAdapterId; @@ -55,11 +53,8 @@ typedef struct { WGPULimits limits; } WGPUDeviceDescriptor; -typedef uint32_t WGPUBackendBit; - typedef struct { WGPUPowerPreference power_preference; - WGPUBackendBit backends; } WGPURequestAdapterOptions; WGPU_INLINE diff --git a/ffi/wgpu.h b/ffi/wgpu.h index 2b9ad5625..9ded93989 100644 --- a/ffi/wgpu.h +++ b/ffi/wgpu.h @@ -19,18 +19,6 @@ #include #include -#define WGPUDEFAULT_BIND_GROUPS 4 - -#define WGPUDESIRED_NUM_FRAMES 3 - -#define WGPUMAX_BIND_GROUPS 4 - -#define WGPUMAX_COLOR_TARGETS 4 - -#define WGPUMAX_MIP_LEVELS 16 - -#define WGPUMAX_VERTEX_BUFFERS 8 - typedef enum { WGPUAddressMode_ClampToEdge = 0, WGPUAddressMode_Repeat = 1, @@ -655,13 +643,12 @@ typedef uint64_t WGPUId_RenderBundle_Dummy; typedef WGPUId_RenderBundle_Dummy WGPURenderBundleId; -typedef uint32_t WGPUBackendBit; - typedef struct { WGPUPowerPreference power_preference; - WGPUBackendBit backends; } WGPURequestAdapterOptions; +typedef uint32_t WGPUBackendBit; + typedef void (*WGPURequestAdapterCallback)(const WGPUAdapterId *adapter, void *userdata); typedef struct { @@ -678,97 +665,65 @@ typedef struct { uint32_t array_layer_count; } WGPUTextureViewDescriptor; -#if defined(WGPU_LOCAL) WGPUDeviceId wgpu_adapter_request_device(WGPUAdapterId adapter_id, const WGPUDeviceDescriptor *desc); -#endif -#if defined(WGPU_LOCAL) void wgpu_bind_group_destroy(WGPUBindGroupId bind_group_id); -#endif -#if defined(WGPU_LOCAL) void wgpu_buffer_destroy(WGPUBufferId buffer_id); -#endif -#if defined(WGPU_LOCAL) void wgpu_buffer_map_read_async(WGPUBufferId buffer_id, WGPUBufferAddress start, WGPUBufferAddress size, WGPUBufferMapReadCallback callback, uint8_t *userdata); -#endif -#if defined(WGPU_LOCAL) void wgpu_buffer_map_write_async(WGPUBufferId buffer_id, WGPUBufferAddress start, WGPUBufferAddress size, WGPUBufferMapWriteCallback callback, uint8_t *userdata); -#endif -#if defined(WGPU_LOCAL) void wgpu_buffer_unmap(WGPUBufferId buffer_id); -#endif -#if defined(WGPU_LOCAL) WGPUComputePassId wgpu_command_encoder_begin_compute_pass(WGPUCommandEncoderId encoder_id, const WGPUComputePassDescriptor *desc); -#endif -#if defined(WGPU_LOCAL) WGPURenderPassId wgpu_command_encoder_begin_render_pass(WGPUCommandEncoderId encoder_id, const WGPURenderPassDescriptor *desc); -#endif -#if defined(WGPU_LOCAL) void wgpu_command_encoder_copy_buffer_to_buffer(WGPUCommandEncoderId command_encoder_id, WGPUBufferId source, WGPUBufferAddress source_offset, WGPUBufferId destination, WGPUBufferAddress destination_offset, WGPUBufferAddress size); -#endif -#if defined(WGPU_LOCAL) void wgpu_command_encoder_copy_buffer_to_texture(WGPUCommandEncoderId command_encoder_id, const WGPUBufferCopyView *source, const WGPUTextureCopyView *destination, WGPUExtent3d copy_size); -#endif -#if defined(WGPU_LOCAL) void wgpu_command_encoder_copy_texture_to_buffer(WGPUCommandEncoderId command_encoder_id, const WGPUTextureCopyView *source, const WGPUBufferCopyView *destination, WGPUExtent3d copy_size); -#endif -#if defined(WGPU_LOCAL) void wgpu_command_encoder_copy_texture_to_texture(WGPUCommandEncoderId command_encoder_id, const WGPUTextureCopyView *source, const WGPUTextureCopyView *destination, WGPUExtent3d copy_size); -#endif -#if defined(WGPU_LOCAL) WGPUCommandBufferId wgpu_command_encoder_finish(WGPUCommandEncoderId encoder_id, const WGPUCommandBufferDescriptor *desc); -#endif -#if defined(WGPU_LOCAL) void wgpu_compute_pass_dispatch(WGPUComputePassId pass_id, uint32_t x, uint32_t y, uint32_t z); -#endif -#if defined(WGPU_LOCAL) void wgpu_compute_pass_dispatch_indirect(WGPUComputePassId pass_id, WGPUBufferId indirect_buffer_id, WGPUBufferAddress indirect_offset); -#endif -#if defined(WGPU_LOCAL) void wgpu_compute_pass_end_pass(WGPUComputePassId pass_id); -#endif void wgpu_compute_pass_insert_debug_marker(WGPUComputePassId _pass_id, WGPURawString _label); @@ -776,205 +731,128 @@ void wgpu_compute_pass_pop_debug_group(WGPUComputePassId _pass_id); void wgpu_compute_pass_push_debug_group(WGPUComputePassId _pass_id, WGPURawString _label); -#if defined(WGPU_LOCAL) void wgpu_compute_pass_set_bind_group(WGPUComputePassId pass_id, uint32_t index, WGPUBindGroupId bind_group_id, const WGPUBufferAddress *offsets, uintptr_t offsets_length); -#endif -#if defined(WGPU_LOCAL) void wgpu_compute_pass_set_pipeline(WGPUComputePassId pass_id, WGPUComputePipelineId pipeline_id); -#endif -#if defined(WGPU_LOCAL) WGPUSurfaceId wgpu_create_surface_from_metal_layer(void *layer); -#endif -#if defined(WGPU_LOCAL) WGPUSurfaceId wgpu_create_surface_from_windows_hwnd(void *_hinstance, void *hwnd); -#endif -#if defined(WGPU_LOCAL) WGPUSurfaceId wgpu_create_surface_from_xlib(const void **display, uint64_t window); -#endif -#if defined(WGPU_LOCAL) WGPUBindGroupId wgpu_device_create_bind_group(WGPUDeviceId device_id, const WGPUBindGroupDescriptor *desc); -#endif -#if defined(WGPU_LOCAL) WGPUBindGroupLayoutId wgpu_device_create_bind_group_layout(WGPUDeviceId device_id, const WGPUBindGroupLayoutDescriptor *desc); -#endif -#if defined(WGPU_LOCAL) WGPUBufferId wgpu_device_create_buffer(WGPUDeviceId device_id, const WGPUBufferDescriptor *desc); -#endif -#if defined(WGPU_LOCAL) WGPUBufferId wgpu_device_create_buffer_mapped(WGPUDeviceId device_id, const WGPUBufferDescriptor *desc, uint8_t **mapped_ptr_out); -#endif -#if defined(WGPU_LOCAL) WGPUCommandEncoderId wgpu_device_create_command_encoder(WGPUDeviceId device_id, const WGPUCommandEncoderDescriptor *desc); -#endif -#if defined(WGPU_LOCAL) WGPUComputePipelineId wgpu_device_create_compute_pipeline(WGPUDeviceId device_id, const WGPUComputePipelineDescriptor *desc); -#endif -#if defined(WGPU_LOCAL) WGPUPipelineLayoutId wgpu_device_create_pipeline_layout(WGPUDeviceId device_id, const WGPUPipelineLayoutDescriptor *desc); -#endif -#if defined(WGPU_LOCAL) WGPURenderPipelineId wgpu_device_create_render_pipeline(WGPUDeviceId device_id, const WGPURenderPipelineDescriptor *desc); -#endif -#if defined(WGPU_LOCAL) WGPUSamplerId wgpu_device_create_sampler(WGPUDeviceId device_id, const WGPUSamplerDescriptor *desc); -#endif -#if defined(WGPU_LOCAL) WGPUShaderModuleId wgpu_device_create_shader_module(WGPUDeviceId device_id, const WGPUShaderModuleDescriptor *desc); -#endif -#if defined(WGPU_LOCAL) WGPUSwapChainId wgpu_device_create_swap_chain(WGPUDeviceId device_id, WGPUSurfaceId surface_id, const WGPUSwapChainDescriptor *desc); -#endif -#if defined(WGPU_LOCAL) WGPUTextureId wgpu_device_create_texture(WGPUDeviceId device_id, const WGPUTextureDescriptor *desc); -#endif -#if defined(WGPU_LOCAL) void wgpu_device_destroy(WGPUDeviceId device_id); -#endif -#if defined(WGPU_LOCAL) void wgpu_device_get_limits(WGPUDeviceId _device_id, WGPULimits *limits); -#endif -#if defined(WGPU_LOCAL) WGPUQueueId wgpu_device_get_queue(WGPUDeviceId device_id); -#endif -#if defined(WGPU_LOCAL) void wgpu_device_poll(WGPUDeviceId device_id, bool force_wait); -#endif -#if defined(WGPU_LOCAL) void wgpu_queue_submit(WGPUQueueId queue_id, const WGPUCommandBufferId *command_buffers, uintptr_t command_buffers_length); -#endif -#if defined(WGPU_LOCAL) void wgpu_render_pass_draw(WGPURenderPassId pass_id, uint32_t vertex_count, uint32_t instance_count, uint32_t first_vertex, uint32_t first_instance); -#endif -#if defined(WGPU_LOCAL) void wgpu_render_pass_draw_indexed(WGPURenderPassId pass_id, uint32_t index_count, uint32_t instance_count, uint32_t first_index, int32_t base_vertex, uint32_t first_instance); -#endif -#if defined(WGPU_LOCAL) void wgpu_render_pass_draw_indexed_indirect(WGPURenderPassId pass_id, WGPUBufferId indirect_buffer_id, WGPUBufferAddress indirect_offset); -#endif -#if defined(WGPU_LOCAL) void wgpu_render_pass_draw_indirect(WGPURenderPassId pass_id, WGPUBufferId indirect_buffer_id, WGPUBufferAddress indirect_offset); -#endif -#if defined(WGPU_LOCAL) void wgpu_render_pass_end_pass(WGPURenderPassId pass_id); -#endif -#if defined(WGPU_LOCAL) void wgpu_render_pass_execute_bundles(WGPURenderPassId _pass_id, const WGPURenderBundleId *_bundles, uintptr_t _bundles_length); -#endif -#if defined(WGPU_LOCAL) void wgpu_render_pass_insert_debug_marker(WGPURenderPassId _pass_id, WGPURawString _label); -#endif -#if defined(WGPU_LOCAL) void wgpu_render_pass_pop_debug_group(WGPURenderPassId _pass_id); -#endif -#if defined(WGPU_LOCAL) void wgpu_render_pass_push_debug_group(WGPURenderPassId _pass_id, WGPURawString _label); -#endif -#if defined(WGPU_LOCAL) void wgpu_render_pass_set_bind_group(WGPURenderPassId pass_id, uint32_t index, WGPUBindGroupId bind_group_id, const WGPUBufferAddress *offsets, uintptr_t offsets_length); -#endif -#if defined(WGPU_LOCAL) void wgpu_render_pass_set_blend_color(WGPURenderPassId pass_id, const WGPUColor *color); -#endif -#if defined(WGPU_LOCAL) void wgpu_render_pass_set_index_buffer(WGPURenderPassId pass_id, WGPUBufferId buffer_id, WGPUBufferAddress offset); -#endif -#if defined(WGPU_LOCAL) void wgpu_render_pass_set_pipeline(WGPURenderPassId pass_id, WGPURenderPipelineId pipeline_id); -#endif -#if defined(WGPU_LOCAL) void wgpu_render_pass_set_scissor_rect(WGPURenderPassId pass_id, uint32_t x, uint32_t y, uint32_t w, uint32_t h); -#endif -#if defined(WGPU_LOCAL) void wgpu_render_pass_set_stencil_reference(WGPURenderPassId pass_id, uint32_t value); -#endif -#if defined(WGPU_LOCAL) void wgpu_render_pass_set_vertex_buffers(WGPURenderPassId pass_id, uint32_t start_slot, const WGPUBufferId *buffers, const WGPUBufferAddress *offsets, uintptr_t length); -#endif -#if defined(WGPU_LOCAL) void wgpu_render_pass_set_viewport(WGPURenderPassId pass_id, float x, float y, @@ -982,35 +860,21 @@ void wgpu_render_pass_set_viewport(WGPURenderPassId pass_id, float h, float min_depth, float max_depth); -#endif -#if defined(WGPU_LOCAL) void wgpu_request_adapter_async(const WGPURequestAdapterOptions *desc, + WGPUBackendBit mask, WGPURequestAdapterCallback callback, void *userdata); -#endif -#if defined(WGPU_LOCAL) void wgpu_sampler_destroy(WGPUSamplerId sampler_id); -#endif -#if defined(WGPU_LOCAL) WGPUSwapChainOutput wgpu_swap_chain_get_next_texture(WGPUSwapChainId swap_chain_id); -#endif -#if defined(WGPU_LOCAL) void wgpu_swap_chain_present(WGPUSwapChainId swap_chain_id); -#endif -#if defined(WGPU_LOCAL) WGPUTextureViewId wgpu_texture_create_view(WGPUTextureId texture_id, const WGPUTextureViewDescriptor *desc); -#endif -#if defined(WGPU_LOCAL) void wgpu_texture_destroy(WGPUTextureId texture_id); -#endif -#if defined(WGPU_LOCAL) void wgpu_texture_view_destroy(WGPUTextureViewId texture_view_id); -#endif diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml new file mode 100644 index 000000000..c33264088 --- /dev/null +++ b/wgpu-core/Cargo.toml @@ -0,0 +1,48 @@ +[package] +name = "wgpu-core" +version = "0.1.0" +authors = [ + "Dzmitry Malyshau ", + "Joshua Groves ", +] +edition = "2018" +description = "WebGPU core logic on gfx-hal/rendy" +homepage = "https://github.com/gfx-rs/wgpu" +repository = "https://github.com/gfx-rs/wgpu" +keywords = ["graphics"] +license = "MPL-2.0" + +[lib] + +[features] +default = [] +metal-auto-capture = ["gfx-backend-metal/auto-capture"] +#NOTE: glutin feature is not stable, use at your own risk +#glutin = ["gfx-backend-gl/glutin"] + +[dependencies] +arrayvec = "0.5" +bitflags = "1.0" +copyless = "0.1" +fxhash = "0.2" +log = "0.4" +hal = { package = "gfx-hal", version = "0.4" } +gfx-backend-empty = { version = "0.4" } +parking_lot = "0.9" +rendy-memory = "0.5" +rendy-descriptor = "0.5" +serde = { version = "1.0", features = ["serde_derive"], optional = true } +smallvec = "0.6" +vec_map = "0.8" + +[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies] +gfx-backend-metal = { version = "0.4" } +gfx-backend-vulkan = { version = "0.4", optional = true } + +[target.'cfg(all(unix, not(target_os = "ios"), not(target_os = "macos")))'.dependencies] +gfx-backend-vulkan = { version = "0.4", features = ["x11"] } + +[target.'cfg(windows)'.dependencies] +gfx-backend-dx12 = { version = "0.4.1" } +gfx-backend-dx11 = { version = "0.4" } +gfx-backend-vulkan = { version = "0.4" } diff --git a/wgpu-native/src/binding_model.rs b/wgpu-core/src/binding_model.rs similarity index 96% rename from wgpu-native/src/binding_model.rs rename to wgpu-core/src/binding_model.rs index cc3e62a2d..076590460 100644 --- a/wgpu-native/src/binding_model.rs +++ b/wgpu-core/src/binding_model.rs @@ -3,28 +3,23 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::{ + id::{BindGroupLayoutId, BufferId, DeviceId, SamplerId, TextureViewId}, resource::TextureViewDimension, track::TrackerSet, - BindGroupLayoutId, BufferAddress, - BufferId, - DeviceId, LifeGuard, RefCount, - SamplerId, Stored, - TextureViewId, }; use arrayvec::ArrayVec; -use bitflags::bitflags; use rendy_descriptor::{DescriptorRanges, DescriptorSet}; use std::borrow::Borrow; pub const MAX_BIND_GROUPS: usize = 4; -bitflags! { +bitflags::bitflags! { #[repr(transparent)] pub struct ShaderStage: u32 { const NONE = 0; diff --git a/wgpu-native/src/command/allocator.rs b/wgpu-core/src/command/allocator.rs similarity index 94% rename from wgpu-native/src/command/allocator.rs rename to wgpu-core/src/command/allocator.rs index 8761e7b13..de8af9a0c 100644 --- a/wgpu-native/src/command/allocator.rs +++ b/wgpu-core/src/command/allocator.rs @@ -5,8 +5,8 @@ use super::CommandBuffer; use crate::{ hub::GfxBackend, + id::DeviceId, track::TrackerSet, - DeviceId, Features, LifeGuard, Stored, @@ -27,9 +27,7 @@ struct CommandPool { impl CommandPool { fn allocate(&mut self) -> B::CommandBuffer { if self.available.is_empty() { - let extra = unsafe { - self.raw.allocate_vec(20, hal::command::Level::Primary) - }; + let extra = unsafe { self.raw.allocate_vec(20, hal::command::Level::Primary) }; self.available.extend(extra); } @@ -113,9 +111,7 @@ impl CommandAllocator { let pool = inner.pools.get_mut(&cmd_buf.recorded_thread_id).unwrap(); if pool.available.is_empty() { - let extra = unsafe { - pool.raw.allocate_vec(20, hal::command::Level::Primary) - }; + let extra = unsafe { pool.raw.allocate_vec(20, hal::command::Level::Primary) }; pool.available.extend(extra); } diff --git a/wgpu-native/src/command/bind.rs b/wgpu-core/src/command/bind.rs similarity index 98% rename from wgpu-native/src/command/bind.rs rename to wgpu-core/src/command/bind.rs index e6e04365b..1c04b3c52 100644 --- a/wgpu-native/src/command/bind.rs +++ b/wgpu-core/src/command/bind.rs @@ -3,12 +3,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::{ + binding_model::BindGroup, hub::GfxBackend, - BindGroup, - BindGroupId, - BindGroupLayoutId, + id::{BindGroupId, BindGroupLayoutId, PipelineLayoutId}, BufferAddress, - PipelineLayoutId, Stored, }; diff --git a/wgpu-core/src/command/compute.rs b/wgpu-core/src/command/compute.rs new file mode 100644 index 000000000..7670da604 --- /dev/null +++ b/wgpu-core/src/command/compute.rs @@ -0,0 +1,246 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use crate::{ + command::{ + bind::{Binder, LayoutChange}, + CommandBuffer, + }, + device::{all_buffer_stages, BIND_BUFFER_ALIGNMENT}, + hub::{GfxBackend, Global, IdentityFilter, Token}, + id::{BindGroupId, BufferId, CommandBufferId, ComputePassId, ComputePipelineId}, + resource::BufferUsage, + track::{Stitch, TrackerSet}, + BufferAddress, + Stored, +}; + +use hal::{self, command::CommandBuffer as _}; + +use std::iter; + +#[derive(Debug)] +pub struct ComputePass { + raw: B::CommandBuffer, + cmb_id: Stored, + binder: Binder, + trackers: TrackerSet, +} + +impl ComputePass { + pub(crate) fn new( + raw: B::CommandBuffer, + cmb_id: Stored, + trackers: TrackerSet, + max_bind_groups: u32, + ) -> Self { + ComputePass { + raw, + cmb_id, + binder: Binder::new(max_bind_groups), + trackers, + } + } +} + +// Common routines between render/compute + +impl> Global { + pub fn compute_pass_end_pass(&self, pass_id: ComputePassId) { + let mut token = Token::root(); + let hub = B::hub(self); + let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token); + let (pass, _) = hub.compute_passes.unregister(pass_id, &mut token); + let cmb = &mut cmb_guard[pass.cmb_id.value]; + + // There are no transitions to be made: we've already been inserting barriers + // into the parent command buffer while recording this compute pass. + cmb.trackers = pass.trackers; + cmb.raw.push(pass.raw); + } +} + +impl Global { + pub fn compute_pass_set_bind_group( + &self, + pass_id: ComputePassId, + index: u32, + bind_group_id: BindGroupId, + offsets: &[BufferAddress], + ) { + let hub = B::hub(self); + let mut token = Token::root(); + + let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token); + let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token); + let (mut pass_guard, mut token) = hub.compute_passes.write(&mut token); + let pass = &mut pass_guard[pass_id]; + + let bind_group = pass + .trackers + .bind_groups + .use_extend(&*bind_group_guard, bind_group_id, (), ()) + .unwrap(); + + assert_eq!(bind_group.dynamic_count, offsets.len()); + + if cfg!(debug_assertions) { + for off in offsets { + assert_eq!( + *off % BIND_BUFFER_ALIGNMENT, + 0, + "Misaligned dynamic buffer offset: {} does not align with {}", + off, + BIND_BUFFER_ALIGNMENT + ); + } + } + + //Note: currently, WebGPU compute passes have synchronization defined + // at a dispatch granularity, so we insert the necessary barriers here. + let (buffer_guard, mut token) = hub.buffers.read(&mut token); + let (texture_guard, _) = hub.textures.read(&mut token); + + log::trace!( + "Encoding barriers on binding of {:?} in pass {:?}", + bind_group_id, + pass_id + ); + CommandBuffer::insert_barriers( + &mut pass.raw, + &mut pass.trackers, + &bind_group.used, + Stitch::Last, + &*buffer_guard, + &*texture_guard, + ); + + if let Some((pipeline_layout_id, follow_up_sets, follow_up_offsets)) = pass + .binder + .provide_entry(index as usize, bind_group_id, bind_group, offsets) + { + let bind_groups = iter::once(bind_group.raw.raw()) + .chain(follow_up_sets.map(|bg_id| bind_group_guard[bg_id].raw.raw())); + unsafe { + pass.raw.bind_compute_descriptor_sets( + &pipeline_layout_guard[pipeline_layout_id].raw, + index as usize, + bind_groups, + offsets + .iter() + .chain(follow_up_offsets) + .map(|&off| off as hal::command::DescriptorSetOffset), + ); + } + }; + } + + // Compute-specific routines + + pub fn compute_pass_dispatch( + &self, + pass_id: ComputePassId, + x: u32, + y: u32, + z: u32, + ) { + let hub = B::hub(self); + let mut token = Token::root(); + let (mut pass_guard, _) = hub.compute_passes.write(&mut token); + unsafe { + pass_guard[pass_id].raw.dispatch([x, y, z]); + } + } + + pub fn compute_pass_dispatch_indirect( + &self, + pass_id: ComputePassId, + indirect_buffer_id: BufferId, + indirect_offset: BufferAddress, + ) { + let hub = B::hub(self); + let mut token = Token::root(); + let (buffer_guard, _) = hub.buffers.read(&mut token); + let (mut pass_guard, _) = hub.compute_passes.write(&mut token); + let pass = &mut pass_guard[pass_id]; + + let (src_buffer, src_pending) = pass.trackers.buffers.use_replace( + &*buffer_guard, + indirect_buffer_id, + (), + BufferUsage::INDIRECT, + ); + assert!(src_buffer.usage.contains(BufferUsage::INDIRECT)); + + let barriers = src_pending.map(|pending| hal::memory::Barrier::Buffer { + states: pending.to_states(), + target: &src_buffer.raw, + families: None, + range: None .. None, + }); + + unsafe { + pass.raw.pipeline_barrier( + all_buffer_stages() .. all_buffer_stages(), + hal::memory::Dependencies::empty(), + barriers, + ); + pass.raw.dispatch_indirect(&src_buffer.raw, indirect_offset); + } + } + + pub fn compute_pass_set_pipeline( + &self, + pass_id: ComputePassId, + pipeline_id: ComputePipelineId, + ) { + let hub = B::hub(self); + let mut token = Token::root(); + let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token); + let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token); + let (mut pass_guard, mut token) = hub.compute_passes.write(&mut token); + let pass = &mut pass_guard[pass_id]; + let (pipeline_guard, _) = hub.compute_pipelines.read(&mut token); + let pipeline = &pipeline_guard[pipeline_id]; + + unsafe { + pass.raw.bind_compute_pipeline(&pipeline.raw); + } + + // Rebind resources + if pass.binder.pipeline_layout_id != Some(pipeline.layout_id.clone()) { + let pipeline_layout = &pipeline_layout_guard[pipeline.layout_id]; + pass.binder.pipeline_layout_id = Some(pipeline.layout_id.clone()); + pass.binder + .reset_expectations(pipeline_layout.bind_group_layout_ids.len()); + let mut is_compatible = true; + + for (index, (entry, &bgl_id)) in pass + .binder + .entries + .iter_mut() + .zip(&pipeline_layout.bind_group_layout_ids) + .enumerate() + { + match entry.expect_layout(bgl_id) { + LayoutChange::Match(bg_id, offsets) if is_compatible => { + let desc_set = bind_group_guard[bg_id].raw.raw(); + unsafe { + pass.raw.bind_compute_descriptor_sets( + &pipeline_layout.raw, + index, + iter::once(desc_set), + offsets.iter().map(|offset| *offset as u32), + ); + } + } + LayoutChange::Match(..) | LayoutChange::Unchanged => {} + LayoutChange::Mismatch => { + is_compatible = false; + } + } + } + } + } +} diff --git a/wgpu-core/src/command/mod.rs b/wgpu-core/src/command/mod.rs new file mode 100644 index 000000000..c493da459 --- /dev/null +++ b/wgpu-core/src/command/mod.rs @@ -0,0 +1,753 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +mod allocator; +mod bind; +mod compute; +mod render; +mod transfer; + +pub(crate) use self::allocator::CommandAllocator; +pub use self::compute::*; +pub use self::render::*; +pub use self::transfer::*; + +use crate::{ + conv, + device::{ + all_buffer_stages, + all_image_stages, + FramebufferKey, + RenderPassContext, + RenderPassKey, + }, + hub::{GfxBackend, Global, IdentityFilter, Storage, Token}, + id::{ + BufferId, + CommandBufferId, + CommandEncoderId, + ComputePassId, + DeviceId, + RenderPassId, + TextureId, + TextureViewId, + }, + resource::{Buffer, Texture, TextureUsage, TextureViewInner}, + track::{Stitch, TrackerSet}, + Color, + Features, + LifeGuard, + Stored, +}; + +use arrayvec::ArrayVec; +use hal::{adapter::PhysicalDevice as _, command::CommandBuffer as _, device::Device as _}; + +use std::{borrow::Borrow, collections::hash_map::Entry, iter, mem, ptr, slice, thread::ThreadId}; + + +pub struct RenderBundle { + _raw: B::CommandBuffer, +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +pub enum LoadOp { + Clear = 0, + Load = 1, +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +pub enum StoreOp { + Clear = 0, + Store = 1, +} + +#[repr(C)] +#[derive(Debug)] +pub struct RenderPassColorAttachmentDescriptor { + pub attachment: TextureViewId, + pub resolve_target: *const TextureViewId, + pub load_op: LoadOp, + pub store_op: StoreOp, + pub clear_color: Color, +} + +#[repr(C)] +#[derive(Debug)] +pub struct RenderPassDepthStencilAttachmentDescriptor { + pub attachment: T, + pub depth_load_op: LoadOp, + pub depth_store_op: StoreOp, + pub clear_depth: f32, + pub stencil_load_op: LoadOp, + pub stencil_store_op: StoreOp, + pub clear_stencil: u32, +} + +#[repr(C)] +#[derive(Debug)] +pub struct RenderPassDescriptor { + pub color_attachments: *const RenderPassColorAttachmentDescriptor, + pub color_attachments_length: usize, + pub depth_stencil_attachment: *const RenderPassDepthStencilAttachmentDescriptor, +} + +#[repr(C)] +#[derive(Clone, Debug, Default)] +pub struct ComputePassDescriptor { + pub todo: u32, +} + +#[derive(Debug)] +pub struct CommandBuffer { + pub(crate) raw: Vec, + is_recording: bool, + recorded_thread_id: ThreadId, + pub(crate) device_id: Stored, + pub(crate) life_guard: LifeGuard, + pub(crate) trackers: TrackerSet, + pub(crate) used_swap_chain: Option<(Stored, B::Framebuffer)>, + pub(crate) features: Features, +} + +impl CommandBuffer { + pub(crate) fn insert_barriers( + raw: &mut B::CommandBuffer, + base: &mut TrackerSet, + head: &TrackerSet, + stitch: Stitch, + buffer_guard: &Storage, BufferId>, + texture_guard: &Storage, TextureId>, + ) { + log::trace!("\tstitch {:?}", stitch); + debug_assert_eq!(B::VARIANT, base.backend()); + debug_assert_eq!(B::VARIANT, head.backend()); + + let buffer_barriers = base + .buffers + .merge_replace(&head.buffers, stitch) + .map(|pending| { + log::trace!("\tbuffer -> {:?}", pending); + hal::memory::Barrier::Buffer { + states: pending.to_states(), + target: &buffer_guard[pending.id].raw, + range: None .. None, + families: None, + } + }); + let texture_barriers = base + .textures + .merge_replace(&head.textures, stitch) + .map(|pending| { + log::trace!("\ttexture -> {:?}", pending); + hal::memory::Barrier::Image { + states: pending.to_states(), + target: &texture_guard[pending.id].raw, + range: pending.selector, + families: None, + } + }); + base.views.merge_extend(&head.views).unwrap(); + base.bind_groups.merge_extend(&head.bind_groups).unwrap(); + base.samplers.merge_extend(&head.samplers).unwrap(); + + let stages = all_buffer_stages() | all_image_stages(); + unsafe { + raw.pipeline_barrier( + stages .. stages, + hal::memory::Dependencies::empty(), + buffer_barriers.chain(texture_barriers), + ); + } + } +} + +#[repr(C)] +#[derive(Clone, Debug, Default)] +pub struct CommandEncoderDescriptor { + // MSVC doesn't allow zero-sized structs + // We can remove this when we actually have a field + pub todo: u32, +} + +#[repr(C)] +#[derive(Clone, Debug, Default)] +pub struct CommandBufferDescriptor { + pub todo: u32, +} + +impl Global { + pub fn command_encoder_finish( + &self, + encoder_id: CommandEncoderId, + _desc: &CommandBufferDescriptor, + ) -> CommandBufferId { + let hub = B::hub(self); + let mut token = Token::root(); + //TODO: actually close the last recorded command buffer + let (mut comb_guard, _) = hub.command_buffers.write(&mut token); + let comb = &mut comb_guard[encoder_id]; + assert!(comb.is_recording); + comb.is_recording = false; + // stop tracking the swapchain image, if used + if let Some((ref view_id, _)) = comb.used_swap_chain { + comb.trackers.views.remove(view_id.value); + } + encoder_id + } +} + +impl> Global { + pub fn command_encoder_begin_render_pass( + &self, + encoder_id: CommandEncoderId, + desc: &RenderPassDescriptor, + id_in: F::Input, + ) -> RenderPassId { + let hub = B::hub(self); + let mut token = Token::root(); + + let (adapter_guard, mut token) = hub.adapters.read(&mut token); + let (device_guard, mut token) = hub.devices.read(&mut token); + let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token); + let cmb = &mut cmb_guard[encoder_id]; + let device = &device_guard[cmb.device_id.value]; + + let limits = adapter_guard[device.adapter_id] + .raw + .physical_device + .limits(); + let samples_count_limit = limits.framebuffer_color_sample_counts; + + let mut current_comb = device.com_allocator.extend(cmb); + unsafe { + current_comb.begin( + hal::command::CommandBufferFlags::ONE_TIME_SUBMIT, + hal::command::CommandBufferInheritanceInfo::default(), + ); + } + + let pass = { + let (_, mut token) = hub.buffers.read(&mut token); //skip token + let (texture_guard, mut token) = hub.textures.read(&mut token); + let (view_guard, _) = hub.texture_views.read(&mut token); + + let mut extent = None; + let mut barriers = Vec::new(); + let mut used_swap_chain_image = None::>; + + let color_attachments = unsafe { + slice::from_raw_parts(desc.color_attachments, desc.color_attachments_length) + }; + let depth_stencil_attachment = unsafe { desc.depth_stencil_attachment.as_ref() }; + + let sample_count = color_attachments + .get(0) + .map(|at| view_guard[at.attachment].samples) + .unwrap_or(1); + assert!( + sample_count & samples_count_limit != 0, + "Attachment sample_count must be supported by physical device limits" + ); + + log::trace!( + "Encoding render pass begin in command buffer {:?}", + encoder_id + ); + let rp_key = { + let trackers = &mut cmb.trackers; + + let depth_stencil = depth_stencil_attachment.map(|at| { + let view = trackers + .views + .use_extend(&*view_guard, at.attachment, (), ()) + .unwrap(); + if let Some(ex) = extent { + assert_eq!(ex, view.extent); + } else { + extent = Some(view.extent); + } + let texture_id = match view.inner { + TextureViewInner::Native { ref source_id, .. } => source_id.value, + TextureViewInner::SwapChain { .. } => { + panic!("Unexpected depth/stencil use of swapchain image!") + } + }; + + let texture = &texture_guard[texture_id]; + assert!(texture.usage.contains(TextureUsage::OUTPUT_ATTACHMENT)); + + let old_layout = match trackers.textures.query(texture_id, view.range.clone()) { + Some(usage) => { + conv::map_texture_state( + usage, + hal::format::Aspects::DEPTH | hal::format::Aspects::STENCIL, + ) + .1 + } + None => { + // Required sub-resources have inconsistent states, we need to + // issue individual barriers instead of relying on the render pass. + let pending = trackers.textures.change_replace( + texture_id, + &texture.life_guard.ref_count, + view.range.clone(), + TextureUsage::OUTPUT_ATTACHMENT, + ); + + barriers.extend(pending.map(|pending| { + log::trace!("\tdepth-stencil {:?}", pending); + hal::memory::Barrier::Image { + states: pending.to_states(), + target: &texture.raw, + families: None, + range: pending.selector, + } + })); + hal::image::Layout::DepthStencilAttachmentOptimal + } + }; + hal::pass::Attachment { + format: Some(conv::map_texture_format(view.format, device.features)), + samples: view.samples, + ops: conv::map_load_store_ops(at.depth_load_op, at.depth_store_op), + stencil_ops: conv::map_load_store_ops( + at.stencil_load_op, + at.stencil_store_op, + ), + layouts: old_layout .. hal::image::Layout::DepthStencilAttachmentOptimal, + } + }); + + let mut colors = ArrayVec::new(); + let mut resolves = ArrayVec::new(); + + for at in color_attachments { + let view = &view_guard[at.attachment]; + if let Some(ex) = extent { + assert_eq!(ex, view.extent); + } else { + extent = Some(view.extent); + } + assert_eq!( + view.samples, sample_count, + "All attachments must have the same sample_count" + ); + let first_use = trackers.views.init( + at.attachment, + view.life_guard.ref_count.clone(), + (), + (), + ); + + let layouts = match view.inner { + TextureViewInner::Native { ref source_id, .. } => { + let texture = &texture_guard[source_id.value]; + assert!(texture.usage.contains(TextureUsage::OUTPUT_ATTACHMENT)); + + let old_layout = match trackers + .textures + .query(source_id.value, view.range.clone()) + { + Some(usage) => { + conv::map_texture_state(usage, hal::format::Aspects::COLOR).1 + } + None => { + // Required sub-resources have inconsistent states, we need to + // issue individual barriers instead of relying on the render pass. + let pending = trackers.textures.change_replace( + source_id.value, + &texture.life_guard.ref_count, + view.range.clone(), + TextureUsage::OUTPUT_ATTACHMENT, + ); + barriers.extend(pending.map(|pending| { + log::trace!("\tcolor {:?}", pending); + hal::memory::Barrier::Image { + states: pending.to_states(), + target: &texture.raw, + families: None, + range: pending.selector, + } + })); + hal::image::Layout::ColorAttachmentOptimal + } + }; + old_layout .. hal::image::Layout::ColorAttachmentOptimal + } + TextureViewInner::SwapChain { .. } => { + if let Some((ref view_id, _)) = cmb.used_swap_chain { + assert_eq!(view_id.value, at.attachment); + } else { + assert!(used_swap_chain_image.is_none()); + used_swap_chain_image = Some(Stored { + value: at.attachment, + ref_count: view.life_guard.ref_count.clone(), + }); + } + + let end = hal::image::Layout::Present; + let start = if first_use { + hal::image::Layout::Undefined + } else { + end + }; + start .. end + } + }; + + colors.push(hal::pass::Attachment { + format: Some(conv::map_texture_format(view.format, device.features)), + samples: view.samples, + ops: conv::map_load_store_ops(at.load_op, at.store_op), + stencil_ops: hal::pass::AttachmentOps::DONT_CARE, + layouts, + }); + } + + for &resolve_target in color_attachments + .iter() + .flat_map(|at| unsafe { at.resolve_target.as_ref() }) + { + let view = &view_guard[resolve_target]; + assert_eq!(extent, Some(view.extent)); + assert_eq!( + view.samples, 1, + "All resolve_targets must have a sample_count of 1" + ); + let first_use = trackers.views.init( + resolve_target, + view.life_guard.ref_count.clone(), + (), + (), + ); + + let layouts = match view.inner { + TextureViewInner::Native { ref source_id, .. } => { + let texture = &texture_guard[source_id.value]; + assert!(texture.usage.contains(TextureUsage::OUTPUT_ATTACHMENT)); + + let old_layout = match trackers + .textures + .query(source_id.value, view.range.clone()) + { + Some(usage) => { + conv::map_texture_state(usage, hal::format::Aspects::COLOR).1 + } + None => { + // Required sub-resources have inconsistent states, we need to + // issue individual barriers instead of relying on the render pass. + let pending = trackers.textures.change_replace( + source_id.value, + &texture.life_guard.ref_count, + view.range.clone(), + TextureUsage::OUTPUT_ATTACHMENT, + ); + barriers.extend(pending.map(|pending| { + log::trace!("\tresolve {:?}", pending); + hal::memory::Barrier::Image { + states: pending.to_states(), + target: &texture.raw, + families: None, + range: pending.selector, + } + })); + hal::image::Layout::ColorAttachmentOptimal + } + }; + old_layout .. hal::image::Layout::ColorAttachmentOptimal + } + TextureViewInner::SwapChain { .. } => { + if let Some((ref view_id, _)) = cmb.used_swap_chain { + assert_eq!(view_id.value, resolve_target); + } else { + assert!(used_swap_chain_image.is_none()); + used_swap_chain_image = Some(Stored { + value: resolve_target, + ref_count: view.life_guard.ref_count.clone(), + }); + } + + let end = hal::image::Layout::Present; + let start = if first_use { + hal::image::Layout::Undefined + } else { + end + }; + start .. end + } + }; + + resolves.push(hal::pass::Attachment { + format: Some(conv::map_texture_format(view.format, device.features)), + samples: view.samples, + ops: hal::pass::AttachmentOps::new( + hal::pass::AttachmentLoadOp::DontCare, + hal::pass::AttachmentStoreOp::Store, + ), + stencil_ops: hal::pass::AttachmentOps::DONT_CARE, + layouts, + }); + } + + RenderPassKey { + colors, + resolves, + depth_stencil, + } + }; + + if !barriers.is_empty() { + unsafe { + current_comb.pipeline_barrier( + all_image_stages() .. all_image_stages(), + hal::memory::Dependencies::empty(), + barriers, + ); + } + } + + let mut render_pass_cache = device.render_passes.lock(); + let render_pass = match render_pass_cache.entry(rp_key.clone()) { + Entry::Occupied(e) => e.into_mut(), + Entry::Vacant(e) => { + let color_ids = [ + (0, hal::image::Layout::ColorAttachmentOptimal), + (1, hal::image::Layout::ColorAttachmentOptimal), + (2, hal::image::Layout::ColorAttachmentOptimal), + (3, hal::image::Layout::ColorAttachmentOptimal), + ]; + + let mut resolve_ids = ArrayVec::<[_; crate::device::MAX_COLOR_TARGETS]>::new(); + let mut attachment_index = color_attachments.len(); + if color_attachments + .iter() + .any(|at| at.resolve_target != ptr::null()) + { + for (i, at) in color_attachments.iter().enumerate() { + if at.resolve_target == ptr::null() { + resolve_ids.push(( + hal::pass::ATTACHMENT_UNUSED, + hal::image::Layout::ColorAttachmentOptimal, + )); + } else { + let sample_count_check = + view_guard[color_attachments[i].attachment].samples; + assert!(sample_count_check > 1, "RenderPassColorAttachmentDescriptor with a resolve_target must have an attachment with sample_count > 1"); + resolve_ids.push(( + attachment_index, + hal::image::Layout::ColorAttachmentOptimal, + )); + attachment_index += 1; + } + } + } + + let depth_id = ( + attachment_index, + hal::image::Layout::DepthStencilAttachmentOptimal, + ); + + let subpass = hal::pass::SubpassDesc { + colors: &color_ids[.. color_attachments.len()], + resolves: &resolve_ids, + depth_stencil: depth_stencil_attachment.map(|_| &depth_id), + inputs: &[], + preserves: &[], + }; + + let pass = unsafe { + device + .raw + .create_render_pass(e.key().all(), &[subpass], &[]) + } + .unwrap(); + e.insert(pass) + } + }; + + let mut framebuffer_cache; + let fb_key = FramebufferKey { + colors: color_attachments.iter().map(|at| at.attachment).collect(), + resolves: color_attachments + .iter() + .filter_map(|at| unsafe { at.resolve_target.as_ref() }.cloned()) + .collect(), + depth_stencil: depth_stencil_attachment.map(|at| at.attachment), + }; + + let framebuffer = match used_swap_chain_image.take() { + Some(view_id) => { + assert!(cmb.used_swap_chain.is_none()); + // Always create a new framebuffer and delete it after presentation. + let attachments = fb_key.all().map(|&id| match view_guard[id].inner { + TextureViewInner::Native { ref raw, .. } => raw, + TextureViewInner::SwapChain { ref image, .. } => Borrow::borrow(image), + }); + let framebuffer = unsafe { + device + .raw + .create_framebuffer(&render_pass, attachments, extent.unwrap()) + .unwrap() + }; + cmb.used_swap_chain = Some((view_id, framebuffer)); + &mut cmb.used_swap_chain.as_mut().unwrap().1 + } + None => { + // Cache framebuffers by the device. + framebuffer_cache = device.framebuffers.lock(); + match framebuffer_cache.entry(fb_key) { + Entry::Occupied(e) => e.into_mut(), + Entry::Vacant(e) => { + let fb = { + let attachments = + e.key().all().map(|&id| match view_guard[id].inner { + TextureViewInner::Native { ref raw, .. } => raw, + TextureViewInner::SwapChain { ref image, .. } => { + Borrow::borrow(image) + } + }); + unsafe { + device.raw.create_framebuffer( + &render_pass, + attachments, + extent.unwrap(), + ) + } + .unwrap() + }; + e.insert(fb) + } + } + } + }; + + let rect = { + let ex = extent.unwrap(); + hal::pso::Rect { + x: 0, + y: 0, + w: ex.width as _, + h: ex.height as _, + } + }; + + let clear_values = color_attachments + .iter() + .zip(&rp_key.colors) + .flat_map(|(at, key)| { + match at.load_op { + LoadOp::Load => None, + LoadOp::Clear => { + use hal::format::ChannelType; + //TODO: validate sign/unsign and normalized ranges of the color values + let value = match key.format.unwrap().base_format().1 { + ChannelType::Unorm + | ChannelType::Snorm + | ChannelType::Ufloat + | ChannelType::Sfloat + | ChannelType::Uscaled + | ChannelType::Sscaled + | ChannelType::Srgb => hal::command::ClearColor { + float32: conv::map_color_f32(&at.clear_color), + }, + ChannelType::Sint => hal::command::ClearColor { + sint32: conv::map_color_i32(&at.clear_color), + }, + ChannelType::Uint => hal::command::ClearColor { + uint32: conv::map_color_u32(&at.clear_color), + }, + }; + Some(hal::command::ClearValue { color: value }) + } + } + }) + .chain(depth_stencil_attachment.and_then(|at| { + match (at.depth_load_op, at.stencil_load_op) { + (LoadOp::Load, LoadOp::Load) => None, + (LoadOp::Clear, _) | (_, LoadOp::Clear) => { + let value = hal::command::ClearDepthStencil { + depth: at.clear_depth, + stencil: at.clear_stencil, + }; + Some(hal::command::ClearValue { + depth_stencil: value, + }) + } + } + })); + + unsafe { + current_comb.begin_render_pass( + render_pass, + framebuffer, + rect, + clear_values, + hal::command::SubpassContents::Inline, + ); + current_comb.set_scissors(0, iter::once(&rect)); + current_comb.set_viewports( + 0, + iter::once(hal::pso::Viewport { + rect, + depth: 0.0 .. 1.0, + }), + ); + } + + let context = RenderPassContext { + colors: color_attachments + .iter() + .map(|at| view_guard[at.attachment].format) + .collect(), + resolves: color_attachments + .iter() + .filter_map(|at| unsafe { at.resolve_target.as_ref() }) + .map(|resolve| view_guard[*resolve].format) + .collect(), + depth_stencil: depth_stencil_attachment.map(|at| view_guard[at.attachment].format), + }; + + RenderPass::new( + current_comb, + Stored { + value: encoder_id, + ref_count: cmb.life_guard.ref_count.clone(), + }, + context, + sample_count, + cmb.features.max_bind_groups, + ) + }; + hub.render_passes.register_identity(id_in, pass, &mut token) + } +} + +impl> Global { + pub fn command_encoder_begin_compute_pass( + &self, + encoder_id: CommandEncoderId, + _desc: &ComputePassDescriptor, + id_in: F::Input, + ) -> ComputePassId { + let hub = B::hub(self); + let mut token = Token::root(); + + let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token); + let cmb = &mut cmb_guard[encoder_id]; + + let raw = cmb.raw.pop().unwrap(); + let trackers = mem::replace(&mut cmb.trackers, TrackerSet::new(encoder_id.backend())); + let stored = Stored { + value: encoder_id, + ref_count: cmb.life_guard.ref_count.clone(), + }; + + let pass = ComputePass::new(raw, stored, trackers, cmb.features.max_bind_groups); + hub.compute_passes + .register_identity(id_in, pass, &mut token) + } +} diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs new file mode 100644 index 000000000..4a1b25888 --- /dev/null +++ b/wgpu-core/src/command/render.rs @@ -0,0 +1,673 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use crate::{ + command::{ + bind::{Binder, LayoutChange}, + CommandBuffer, + }, + conv, + device::{RenderPassContext, BIND_BUFFER_ALIGNMENT, MAX_VERTEX_BUFFERS}, + hub::{GfxBackend, Global, IdentityFilter, Token}, + id::{BindGroupId, BufferId, CommandBufferId, RenderPassId, RenderPipelineId}, + pipeline::{IndexFormat, InputStepMode, PipelineFlags}, + resource::BufferUsage, + track::{Stitch, TrackerSet}, + BufferAddress, + Color, + Stored, +}; + +use hal::command::CommandBuffer as _; + +use std::{iter, ops::Range}; + +#[derive(Debug, PartialEq)] +enum OptionalState { + Unused, + Required, + Set, +} + +impl OptionalState { + fn require(&mut self, require: bool) { + if require && *self == OptionalState::Unused { + *self = OptionalState::Required; + } + } +} + +#[derive(Debug, PartialEq)] +enum DrawError { + MissingBlendColor, + MissingStencilReference, + IncompatibleBindGroup { + index: u32, + //expected: BindGroupLayoutId, + //provided: Option<(BindGroupLayoutId, BindGroupId)>, + }, +} + +#[derive(Debug)] +pub struct IndexState { + bound_buffer_view: Option<(BufferId, Range)>, + format: IndexFormat, + limit: u32, +} + +impl IndexState { + fn update_limit(&mut self) { + self.limit = match self.bound_buffer_view { + Some((_, ref range)) => { + let shift = match self.format { + IndexFormat::Uint16 => 1, + IndexFormat::Uint32 => 2, + }; + ((range.end - range.start) >> shift) as u32 + } + None => 0, + } + } +} + +#[derive(Clone, Copy, Debug)] +pub struct VertexBufferState { + total_size: BufferAddress, + stride: BufferAddress, + rate: InputStepMode, +} + +impl VertexBufferState { + const EMPTY: Self = VertexBufferState { + total_size: 0, + stride: 0, + rate: InputStepMode::Vertex, + }; +} + +#[derive(Debug)] +pub struct VertexState { + inputs: [VertexBufferState; MAX_VERTEX_BUFFERS], + vertex_limit: u32, + instance_limit: u32, +} + +impl VertexState { + fn update_limits(&mut self) { + self.vertex_limit = !0; + self.instance_limit = !0; + for vbs in &self.inputs { + if vbs.stride == 0 { + continue; + } + let limit = (vbs.total_size / vbs.stride) as u32; + match vbs.rate { + InputStepMode::Vertex => self.vertex_limit = self.vertex_limit.min(limit), + InputStepMode::Instance => self.instance_limit = self.instance_limit.min(limit), + } + } + } +} + +#[derive(Debug)] +pub struct RenderPass { + raw: B::CommandBuffer, + cmb_id: Stored, + context: RenderPassContext, + binder: Binder, + trackers: TrackerSet, + blend_color_status: OptionalState, + stencil_reference_status: OptionalState, + index_state: IndexState, + vertex_state: VertexState, + sample_count: u8, +} + +impl RenderPass { + pub(crate) fn new( + raw: B::CommandBuffer, + cmb_id: Stored, + context: RenderPassContext, + sample_count: u8, + max_bind_groups: u32, + ) -> Self { + RenderPass { + raw, + cmb_id, + context, + binder: Binder::new(max_bind_groups), + trackers: TrackerSet::new(B::VARIANT), + blend_color_status: OptionalState::Unused, + stencil_reference_status: OptionalState::Unused, + index_state: IndexState { + bound_buffer_view: None, + format: IndexFormat::Uint16, + limit: 0, + }, + vertex_state: VertexState { + inputs: [VertexBufferState::EMPTY; MAX_VERTEX_BUFFERS], + vertex_limit: 0, + instance_limit: 0, + }, + sample_count, + } + } + + fn is_ready(&self) -> Result<(), DrawError> { + //TODO: vertex buffers + let bind_mask = self.binder.invalid_mask(); + if bind_mask != 0 { + //let (expected, provided) = self.binder.entries[index as usize].info(); + return Err(DrawError::IncompatibleBindGroup { + index: bind_mask.trailing_zeros() as u32, + }); + } + if self.blend_color_status == OptionalState::Required { + return Err(DrawError::MissingBlendColor); + } + if self.stencil_reference_status == OptionalState::Required { + return Err(DrawError::MissingStencilReference); + } + Ok(()) + } +} + +// Common routines between render/compute + +impl> Global { + pub fn render_pass_end_pass(&self, pass_id: RenderPassId) { + let hub = B::hub(self); + let mut token = Token::root(); + let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token); + let (mut pass, mut token) = hub.render_passes.unregister(pass_id, &mut token); + unsafe { + pass.raw.end_render_pass(); + } + pass.trackers.optimize(); + let cmb = &mut cmb_guard[pass.cmb_id.value]; + let (buffer_guard, mut token) = hub.buffers.read(&mut token); + let (texture_guard, _) = hub.textures.read(&mut token); + + match cmb.raw.last_mut() { + Some(last) => { + log::trace!("Encoding barriers before pass {:?}", pass_id); + CommandBuffer::insert_barriers( + last, + &mut cmb.trackers, + &pass.trackers, + Stitch::Last, + &*buffer_guard, + &*texture_guard, + ); + unsafe { last.finish() }; + } + None => { + cmb.trackers.merge_extend(&pass.trackers); + } + } + + cmb.raw.push(pass.raw); + } + + pub fn render_pass_set_bind_group( + &self, + pass_id: RenderPassId, + index: u32, + bind_group_id: BindGroupId, + offsets: &[BufferAddress], + ) { + let hub = B::hub(self); + let mut token = Token::root(); + let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token); + let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token); + + let (mut pass_guard, _) = hub.render_passes.write(&mut token); + let pass = &mut pass_guard[pass_id]; + + let bind_group = pass + .trackers + .bind_groups + .use_extend(&*bind_group_guard, bind_group_id, (), ()) + .unwrap(); + + assert_eq!(bind_group.dynamic_count, offsets.len()); + + if cfg!(debug_assertions) { + for off in offsets { + assert_eq!( + *off % BIND_BUFFER_ALIGNMENT, + 0, + "Misaligned dynamic buffer offset: {} does not align with {}", + off, + BIND_BUFFER_ALIGNMENT + ); + } + } + + pass.trackers.merge_extend(&bind_group.used); + + if let Some((pipeline_layout_id, follow_up_sets, follow_up_offsets)) = pass + .binder + .provide_entry(index as usize, bind_group_id, bind_group, offsets) + { + let bind_groups = iter::once(bind_group.raw.raw()) + .chain(follow_up_sets.map(|bg_id| bind_group_guard[bg_id].raw.raw())); + unsafe { + pass.raw.bind_graphics_descriptor_sets( + &&pipeline_layout_guard[pipeline_layout_id].raw, + index as usize, + bind_groups, + offsets + .iter() + .chain(follow_up_offsets) + .map(|&off| off as hal::command::DescriptorSetOffset), + ); + } + }; + } + + // Render-specific routines + + pub fn render_pass_set_index_buffer( + &self, + pass_id: RenderPassId, + buffer_id: BufferId, + offset: BufferAddress, + ) { + let hub = B::hub(self); + let mut token = Token::root(); + let (mut pass_guard, mut token) = hub.render_passes.write(&mut token); + let (buffer_guard, _) = hub.buffers.read(&mut token); + + let pass = &mut pass_guard[pass_id]; + let buffer = pass + .trackers + .buffers + .use_extend(&*buffer_guard, buffer_id, (), BufferUsage::INDEX) + .unwrap(); + assert!(buffer.usage.contains(BufferUsage::INDEX)); + + let range = offset .. buffer.size; + pass.index_state.bound_buffer_view = Some((buffer_id, range)); + pass.index_state.update_limit(); + + let view = hal::buffer::IndexBufferView { + buffer: &buffer.raw, + offset, + index_type: conv::map_index_format(pass.index_state.format), + }; + + unsafe { + pass.raw.bind_index_buffer(view); + } + } + + pub fn render_pass_set_vertex_buffers( + &self, + pass_id: RenderPassId, + start_slot: u32, + buffers: &[BufferId], + offsets: &[BufferAddress], + ) { + let hub = B::hub(self); + let mut token = Token::root(); + assert_eq!(buffers.len(), offsets.len()); + + let (mut pass_guard, mut token) = hub.render_passes.write(&mut token); + let (buffer_guard, _) = hub.buffers.read(&mut token); + + let pass = &mut pass_guard[pass_id]; + for (vbs, (&id, &offset)) in pass.vertex_state.inputs[start_slot as usize ..] + .iter_mut() + .zip(buffers.iter().zip(offsets)) + { + let buffer = pass + .trackers + .buffers + .use_extend(&*buffer_guard, id, (), BufferUsage::VERTEX) + .unwrap(); + assert!(buffer.usage.contains(BufferUsage::VERTEX)); + + vbs.total_size = buffer.size - offset; + } + + pass.vertex_state.update_limits(); + + let buffers = buffers + .iter() + .map(|&id| &buffer_guard[id].raw) + .zip(offsets.iter().cloned()); + + unsafe { + pass.raw.bind_vertex_buffers(start_slot, buffers); + } + } + + pub fn render_pass_draw( + &self, + pass_id: RenderPassId, + vertex_count: u32, + instance_count: u32, + first_vertex: u32, + first_instance: u32, + ) { + let hub = B::hub(self); + let mut token = Token::root(); + let (mut pass_guard, _) = hub.render_passes.write(&mut token); + let pass = &mut pass_guard[pass_id]; + pass.is_ready().unwrap(); + + assert!( + first_vertex + vertex_count <= pass.vertex_state.vertex_limit, + "Vertex out of range!" + ); + assert!( + first_instance + instance_count <= pass.vertex_state.instance_limit, + "Instance out of range!" + ); + + unsafe { + pass.raw.draw( + first_vertex .. first_vertex + vertex_count, + first_instance .. first_instance + instance_count, + ); + } + } + + pub fn render_pass_draw_indirect( + &self, + pass_id: RenderPassId, + indirect_buffer_id: BufferId, + indirect_offset: BufferAddress, + ) { + let hub = B::hub(self); + let mut token = Token::root(); + let (mut pass_guard, _) = hub.render_passes.write(&mut token); + let (buffer_guard, _) = hub.buffers.read(&mut token); + let pass = &mut pass_guard[pass_id]; + pass.is_ready().unwrap(); + + let buffer = pass + .trackers + .buffers + .use_extend( + &*buffer_guard, + indirect_buffer_id, + (), + BufferUsage::INDIRECT, + ) + .unwrap(); + assert!(buffer.usage.contains(BufferUsage::INDIRECT)); + + unsafe { + pass.raw.draw_indirect(&buffer.raw, indirect_offset, 1, 0); + } + } + + pub fn render_pass_draw_indexed( + &self, + pass_id: RenderPassId, + index_count: u32, + instance_count: u32, + first_index: u32, + base_vertex: i32, + first_instance: u32, + ) { + let hub = B::hub(self); + let mut token = Token::root(); + let (mut pass_guard, _) = hub.render_passes.write(&mut token); + let pass = &mut pass_guard[pass_id]; + pass.is_ready().unwrap(); + + //TODO: validate that base_vertex + max_index() is within the provided range + assert!( + first_index + index_count <= pass.index_state.limit, + "Index out of range!" + ); + assert!( + first_instance + instance_count <= pass.vertex_state.instance_limit, + "Instance out of range!" + ); + + unsafe { + pass.raw.draw_indexed( + first_index .. first_index + index_count, + base_vertex, + first_instance .. first_instance + instance_count, + ); + } + } + + pub fn render_pass_draw_indexed_indirect( + &self, + pass_id: RenderPassId, + indirect_buffer_id: BufferId, + indirect_offset: BufferAddress, + ) { + let hub = B::hub(self); + let mut token = Token::root(); + let (mut pass_guard, _) = hub.render_passes.write(&mut token); + let (buffer_guard, _) = hub.buffers.read(&mut token); + let pass = &mut pass_guard[pass_id]; + pass.is_ready().unwrap(); + + let buffer = pass + .trackers + .buffers + .use_extend( + &*buffer_guard, + indirect_buffer_id, + (), + BufferUsage::INDIRECT, + ) + .unwrap(); + assert!(buffer.usage.contains(BufferUsage::INDIRECT)); + + unsafe { + pass.raw + .draw_indexed_indirect(&buffer.raw, indirect_offset, 1, 0); + } + } + + pub fn render_pass_set_pipeline( + &self, + pass_id: RenderPassId, + pipeline_id: RenderPipelineId, + ) { + let hub = B::hub(self); + let mut token = Token::root(); + let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token); + let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token); + let (mut pass_guard, mut token) = hub.render_passes.write(&mut token); + let pass = &mut pass_guard[pass_id]; + let (pipeline_guard, mut token) = hub.render_pipelines.read(&mut token); + let pipeline = &pipeline_guard[pipeline_id]; + + assert!( + pass.context.compatible(&pipeline.pass_context), + "The render pipeline is not compatible with the pass!" + ); + assert_eq!( + pipeline.sample_count, pass.sample_count, + "The render pipeline and renderpass have mismatching sample_count" + ); + + pass.blend_color_status + .require(pipeline.flags.contains(PipelineFlags::BLEND_COLOR)); + pass.stencil_reference_status + .require(pipeline.flags.contains(PipelineFlags::STENCIL_REFERENCE)); + + unsafe { + pass.raw.bind_graphics_pipeline(&pipeline.raw); + } + + // Rebind resource + if pass.binder.pipeline_layout_id != Some(pipeline.layout_id.clone()) { + let pipeline_layout = &pipeline_layout_guard[pipeline.layout_id]; + pass.binder.pipeline_layout_id = Some(pipeline.layout_id.clone()); + pass.binder + .reset_expectations(pipeline_layout.bind_group_layout_ids.len()); + let mut is_compatible = true; + + for (index, (entry, &bgl_id)) in pass + .binder + .entries + .iter_mut() + .zip(&pipeline_layout.bind_group_layout_ids) + .enumerate() + { + match entry.expect_layout(bgl_id) { + LayoutChange::Match(bg_id, offsets) if is_compatible => { + let desc_set = bind_group_guard[bg_id].raw.raw(); + unsafe { + pass.raw.bind_graphics_descriptor_sets( + &pipeline_layout.raw, + index, + iter::once(desc_set), + offsets.iter().map(|offset| *offset as u32), + ); + } + } + LayoutChange::Match(..) | LayoutChange::Unchanged => {} + LayoutChange::Mismatch => { + is_compatible = false; + } + } + } + } + + // Rebind index buffer if the index format has changed with the pipeline switch + if pass.index_state.format != pipeline.index_format { + pass.index_state.format = pipeline.index_format; + pass.index_state.update_limit(); + + if let Some((buffer_id, ref range)) = pass.index_state.bound_buffer_view { + let (buffer_guard, _) = hub.buffers.read(&mut token); + let buffer = pass + .trackers + .buffers + .use_extend(&*buffer_guard, buffer_id, (), BufferUsage::INDEX) + .unwrap(); + + let view = hal::buffer::IndexBufferView { + buffer: &buffer.raw, + offset: range.start, + index_type: conv::map_index_format(pass.index_state.format), + }; + + unsafe { + pass.raw.bind_index_buffer(view); + } + } + } + // Update vertex buffer limits + for (vbs, &(stride, rate)) in pass + .vertex_state + .inputs + .iter_mut() + .zip(&pipeline.vertex_strides) + { + vbs.stride = stride; + vbs.rate = rate; + } + for vbs in pass.vertex_state.inputs[pipeline.vertex_strides.len() ..].iter_mut() { + vbs.stride = 0; + vbs.rate = InputStepMode::Vertex; + } + pass.vertex_state.update_limits(); + } + + pub fn render_pass_set_blend_color(&self, pass_id: RenderPassId, color: &Color) { + let hub = B::hub(self); + let mut token = Token::root(); + let (mut pass_guard, _) = hub.render_passes.write(&mut token); + let pass = &mut pass_guard[pass_id]; + + pass.blend_color_status = OptionalState::Set; + + unsafe { + pass.raw.set_blend_constants(conv::map_color_f32(color)); + } + } + + pub fn render_pass_set_stencil_reference( + &self, + pass_id: RenderPassId, + value: u32, + ) { + let hub = B::hub(self); + let mut token = Token::root(); + let (mut pass_guard, _) = hub.render_passes.write(&mut token); + let pass = &mut pass_guard[pass_id]; + + pass.stencil_reference_status = OptionalState::Set; + + unsafe { + pass.raw.set_stencil_reference(hal::pso::Face::all(), value); + } + } + + pub fn render_pass_set_viewport( + &self, + pass_id: RenderPassId, + x: f32, + y: f32, + w: f32, + h: f32, + min_depth: f32, + max_depth: f32, + ) { + let hub = B::hub(self); + let mut token = Token::root(); + let (mut pass_guard, _) = hub.render_passes.write(&mut token); + let pass = &mut pass_guard[pass_id]; + + unsafe { + use std::convert::TryFrom; + use std::i16; + + pass.raw.set_viewports( + 0, + &[hal::pso::Viewport { + rect: hal::pso::Rect { + x: i16::try_from(x.round() as i64).unwrap_or(0), + y: i16::try_from(y.round() as i64).unwrap_or(0), + w: i16::try_from(w.round() as i64).unwrap_or(i16::MAX), + h: i16::try_from(h.round() as i64).unwrap_or(i16::MAX), + }, + depth: min_depth .. max_depth, + }], + ); + } + } + + pub fn render_pass_set_scissor_rect( + &self, + pass_id: RenderPassId, + x: u32, + y: u32, + w: u32, + h: u32, + ) { + let hub = B::hub(self); + let mut token = Token::root(); + let (mut pass_guard, _) = hub.render_passes.write(&mut token); + let pass = &mut pass_guard[pass_id]; + + unsafe { + use std::convert::TryFrom; + use std::i16; + + pass.raw.set_scissors( + 0, + &[hal::pso::Rect { + x: i16::try_from(x).unwrap_or(0), + y: i16::try_from(y).unwrap_or(0), + w: i16::try_from(w).unwrap_or(i16::MAX), + h: i16::try_from(h).unwrap_or(i16::MAX), + }], + ); + } + } +} diff --git a/wgpu-core/src/command/transfer.rs b/wgpu-core/src/command/transfer.rs new file mode 100644 index 000000000..d594e12bc --- /dev/null +++ b/wgpu-core/src/command/transfer.rs @@ -0,0 +1,355 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use crate::{ + conv, + device::{all_buffer_stages, all_image_stages}, + hub::{GfxBackend, Global, Token}, + id::{BufferId, CommandEncoderId, TextureId}, + resource::{BufferUsage, TextureUsage}, + BufferAddress, + Extent3d, + Origin3d, +}; + +use hal::command::CommandBuffer as _; + +use std::iter; + +const BITS_PER_BYTE: u32 = 8; + +#[repr(C)] +#[derive(Debug)] +pub struct BufferCopyView { + pub buffer: BufferId, + pub offset: BufferAddress, + pub row_pitch: u32, + pub image_height: u32, +} + +#[repr(C)] +#[derive(Debug)] +pub struct TextureCopyView { + pub texture: TextureId, + pub mip_level: u32, + pub array_layer: u32, + pub origin: Origin3d, +} + +impl TextureCopyView { + //TODO: we currently access each texture twice for a transfer, + // once only to get the aspect flags, which is unfortunate. + fn to_selector(&self, aspects: hal::format::Aspects) -> hal::image::SubresourceRange { + let level = self.mip_level as hal::image::Level; + let layer = self.array_layer as hal::image::Layer; + hal::image::SubresourceRange { + aspects, + levels: level .. level + 1, + layers: layer .. layer + 1, + } + } + + fn to_sub_layers(&self, aspects: hal::format::Aspects) -> hal::image::SubresourceLayers { + let layer = self.array_layer as hal::image::Layer; + hal::image::SubresourceLayers { + aspects, + level: self.mip_level as hal::image::Level, + layers: layer .. layer + 1, + } + } +} + +impl Global { + pub fn command_encoder_copy_buffer_to_buffer( + &self, + command_encoder_id: CommandEncoderId, + source: BufferId, + source_offset: BufferAddress, + destination: BufferId, + destination_offset: BufferAddress, + size: BufferAddress, + ) { + let hub = B::hub(self); + let mut token = Token::root(); + + let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token); + let cmb = &mut cmb_guard[command_encoder_id]; + let (buffer_guard, _) = hub.buffers.read(&mut token); + // we can't hold both src_pending and dst_pending in scope because they + // borrow the buffer tracker mutably... + let mut barriers = Vec::new(); + + let (src_buffer, src_pending) = + cmb.trackers + .buffers + .use_replace(&*buffer_guard, source, (), BufferUsage::COPY_SRC); + assert!(src_buffer.usage.contains(BufferUsage::COPY_SRC)); + + barriers.extend(src_pending.map(|pending| hal::memory::Barrier::Buffer { + states: pending.to_states(), + target: &src_buffer.raw, + families: None, + range: None .. None, + })); + + let (dst_buffer, dst_pending) = cmb.trackers.buffers.use_replace( + &*buffer_guard, + destination, + (), + BufferUsage::COPY_DST, + ); + assert!(dst_buffer.usage.contains(BufferUsage::COPY_DST)); + + barriers.extend(dst_pending.map(|pending| hal::memory::Barrier::Buffer { + states: pending.to_states(), + target: &dst_buffer.raw, + families: None, + range: None .. None, + })); + + let region = hal::command::BufferCopy { + src: source_offset, + dst: destination_offset, + size, + }; + let cmb_raw = cmb.raw.last_mut().unwrap(); + unsafe { + cmb_raw.pipeline_barrier( + all_buffer_stages() .. all_buffer_stages(), + hal::memory::Dependencies::empty(), + barriers, + ); + cmb_raw.copy_buffer(&src_buffer.raw, &dst_buffer.raw, iter::once(region)); + } + } + + pub fn command_encoder_copy_buffer_to_texture( + &self, + command_encoder_id: CommandEncoderId, + source: &BufferCopyView, + destination: &TextureCopyView, + copy_size: Extent3d, + ) { + let hub = B::hub(self); + let mut token = Token::root(); + let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token); + let cmb = &mut cmb_guard[command_encoder_id]; + let (buffer_guard, mut token) = hub.buffers.read(&mut token); + let (texture_guard, _) = hub.textures.read(&mut token); + let aspects = texture_guard[destination.texture].full_range.aspects; + + let (src_buffer, src_pending) = cmb.trackers.buffers.use_replace( + &*buffer_guard, + source.buffer, + (), + BufferUsage::COPY_SRC, + ); + assert!(src_buffer.usage.contains(BufferUsage::COPY_SRC)); + + let src_barriers = src_pending.map(|pending| hal::memory::Barrier::Buffer { + states: pending.to_states(), + target: &src_buffer.raw, + families: None, + range: None .. None, + }); + + let (dst_texture, dst_pending) = cmb.trackers.textures.use_replace( + &*texture_guard, + destination.texture, + destination.to_selector(aspects), + TextureUsage::COPY_DST, + ); + assert!(dst_texture.usage.contains(TextureUsage::COPY_DST)); + + let dst_barriers = dst_pending.map(|pending| hal::memory::Barrier::Image { + states: pending.to_states(), + target: &dst_texture.raw, + families: None, + range: pending.selector, + }); + + let aspects = dst_texture.full_range.aspects; + let bytes_per_texel = conv::map_texture_format(dst_texture.format, cmb.features) + .surface_desc() + .bits as u32 + / BITS_PER_BYTE; + let buffer_width = source.row_pitch / bytes_per_texel; + assert_eq!(source.row_pitch % bytes_per_texel, 0); + let region = hal::command::BufferImageCopy { + buffer_offset: source.offset, + buffer_width, + buffer_height: source.image_height, + image_layers: destination.to_sub_layers(aspects), + image_offset: conv::map_origin(destination.origin), + image_extent: conv::map_extent(copy_size), + }; + let cmb_raw = cmb.raw.last_mut().unwrap(); + let stages = all_buffer_stages() | all_image_stages(); + unsafe { + cmb_raw.pipeline_barrier( + stages .. stages, + hal::memory::Dependencies::empty(), + src_barriers.chain(dst_barriers), + ); + cmb_raw.copy_buffer_to_image( + &src_buffer.raw, + &dst_texture.raw, + hal::image::Layout::TransferDstOptimal, + iter::once(region), + ); + } + } + + pub fn command_encoder_copy_texture_to_buffer( + &self, + command_encoder_id: CommandEncoderId, + source: &TextureCopyView, + destination: &BufferCopyView, + copy_size: Extent3d, + ) { + let hub = B::hub(self); + let mut token = Token::root(); + let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token); + let cmb = &mut cmb_guard[command_encoder_id]; + let (buffer_guard, mut token) = hub.buffers.read(&mut token); + let (texture_guard, _) = hub.textures.read(&mut token); + let aspects = texture_guard[source.texture].full_range.aspects; + + let (src_texture, src_pending) = cmb.trackers.textures.use_replace( + &*texture_guard, + source.texture, + source.to_selector(aspects), + TextureUsage::COPY_SRC, + ); + assert!(src_texture.usage.contains(TextureUsage::COPY_SRC)); + + let src_barriers = src_pending.map(|pending| hal::memory::Barrier::Image { + states: pending.to_states(), + target: &src_texture.raw, + families: None, + range: pending.selector, + }); + + let (dst_buffer, dst_barriers) = cmb.trackers.buffers.use_replace( + &*buffer_guard, + destination.buffer, + (), + BufferUsage::COPY_DST, + ); + assert!(dst_buffer.usage.contains(BufferUsage::COPY_DST)); + + let dst_barrier = dst_barriers.map(|pending| hal::memory::Barrier::Buffer { + states: pending.to_states(), + target: &dst_buffer.raw, + families: None, + range: None .. None, + }); + + let aspects = src_texture.full_range.aspects; + let bytes_per_texel = conv::map_texture_format(src_texture.format, cmb.features) + .surface_desc() + .bits as u32 + / BITS_PER_BYTE; + let buffer_width = destination.row_pitch / bytes_per_texel; + assert_eq!(destination.row_pitch % bytes_per_texel, 0); + let region = hal::command::BufferImageCopy { + buffer_offset: destination.offset, + buffer_width, + buffer_height: destination.image_height, + image_layers: source.to_sub_layers(aspects), + image_offset: conv::map_origin(source.origin), + image_extent: conv::map_extent(copy_size), + }; + let cmb_raw = cmb.raw.last_mut().unwrap(); + let stages = all_buffer_stages() | all_image_stages(); + unsafe { + cmb_raw.pipeline_barrier( + stages .. stages, + hal::memory::Dependencies::empty(), + src_barriers.chain(dst_barrier), + ); + cmb_raw.copy_image_to_buffer( + &src_texture.raw, + hal::image::Layout::TransferSrcOptimal, + &dst_buffer.raw, + iter::once(region), + ); + } + } + + pub fn command_encoder_copy_texture_to_texture( + &self, + command_encoder_id: CommandEncoderId, + source: &TextureCopyView, + destination: &TextureCopyView, + copy_size: Extent3d, + ) { + let hub = B::hub(self); + let mut token = Token::root(); + + let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token); + let cmb = &mut cmb_guard[command_encoder_id]; + let (_, mut token) = hub.buffers.read(&mut token); // skip token + let (texture_guard, _) = hub.textures.read(&mut token); + // we can't hold both src_pending and dst_pending in scope because they + // borrow the buffer tracker mutably... + let mut barriers = Vec::new(); + let aspects = texture_guard[source.texture].full_range.aspects + & texture_guard[destination.texture].full_range.aspects; + + let (src_texture, src_pending) = cmb.trackers.textures.use_replace( + &*texture_guard, + source.texture, + source.to_selector(aspects), + TextureUsage::COPY_SRC, + ); + assert!(src_texture.usage.contains(TextureUsage::COPY_SRC)); + + barriers.extend(src_pending.map(|pending| hal::memory::Barrier::Image { + states: pending.to_states(), + target: &src_texture.raw, + families: None, + range: pending.selector, + })); + + let (dst_texture, dst_pending) = cmb.trackers.textures.use_replace( + &*texture_guard, + destination.texture, + destination.to_selector(aspects), + TextureUsage::COPY_DST, + ); + assert!(dst_texture.usage.contains(TextureUsage::COPY_DST)); + + barriers.extend(dst_pending.map(|pending| hal::memory::Barrier::Image { + states: pending.to_states(), + target: &dst_texture.raw, + families: None, + range: pending.selector, + })); + + let aspects = src_texture.full_range.aspects & dst_texture.full_range.aspects; + let region = hal::command::ImageCopy { + src_subresource: source.to_sub_layers(aspects), + src_offset: conv::map_origin(source.origin), + dst_subresource: destination.to_sub_layers(aspects), + dst_offset: conv::map_origin(destination.origin), + extent: conv::map_extent(copy_size), + }; + let cmb_raw = cmb.raw.last_mut().unwrap(); + unsafe { + cmb_raw.pipeline_barrier( + all_image_stages() .. all_image_stages(), + hal::memory::Dependencies::empty(), + barriers, + ); + cmb_raw.copy_image( + &src_texture.raw, + hal::image::Layout::TransferSrcOptimal, + &dst_texture.raw, + hal::image::Layout::TransferDstOptimal, + iter::once(region), + ); + } + } +} diff --git a/wgpu-native/src/conv.rs b/wgpu-core/src/conv.rs similarity index 99% rename from wgpu-native/src/conv.rs rename to wgpu-core/src/conv.rs index f9ea2782a..4a9ef0d5c 100644 --- a/wgpu-native/src/conv.rs +++ b/wgpu-core/src/conv.rs @@ -138,7 +138,9 @@ pub fn map_extent(extent: Extent3d) -> hal::image::Extent { } } -pub fn map_primitive_topology(primitive_topology: pipeline::PrimitiveTopology) -> hal::pso::Primitive { +pub fn map_primitive_topology( + primitive_topology: pipeline::PrimitiveTopology, +) -> hal::pso::Primitive { use crate::pipeline::PrimitiveTopology as Pt; use hal::pso::Primitive as H; match primitive_topology { diff --git a/wgpu-core/src/device.rs b/wgpu-core/src/device.rs new file mode 100644 index 000000000..e80eeba09 --- /dev/null +++ b/wgpu-core/src/device.rs @@ -0,0 +1,2072 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use crate::{ + binding_model, + command, + conv, + hub::{AllIdentityFilter, GfxBackend, Global, IdentityFilter, Token}, + id::{ + AdapterId, + BindGroupId, + BindGroupLayoutId, + BufferId, + CommandBufferId, + CommandEncoderId, + ComputePipelineId, + DeviceId, + PipelineLayoutId, + QueueId, + RenderPipelineId, + SamplerId, + ShaderModuleId, + SurfaceId, + SwapChainId, + TextureId, + TextureViewId, + }, + pipeline, + resource, + swap_chain, + track::{Stitch, TrackerSet}, + BufferAddress, + FastHashMap, + Features, + LifeGuard, + RefCount, + Stored, + SubmissionIndex, +}; + +use arrayvec::ArrayVec; +use copyless::VecHelper as _; +use hal::{ + self, + command::CommandBuffer as _, + device::Device as _, + queue::CommandQueue as _, + window::{PresentationSurface as _, Surface as _}, +}; +use parking_lot::Mutex; +use rendy_descriptor::{DescriptorAllocator, DescriptorRanges, DescriptorSet}; +use rendy_memory::{Block, Heaps, MemoryBlock}; + +use std::{ + collections::hash_map::Entry, + ffi, + iter, + ops::Range, + ptr, + slice, + sync::atomic::Ordering, +}; + + +const CLEANUP_WAIT_MS: u64 = 5000; +pub const MAX_COLOR_TARGETS: usize = 4; +pub const MAX_MIP_LEVELS: usize = 16; +pub const MAX_VERTEX_BUFFERS: usize = 8; + +/// Bound uniform/storage buffer offsets must be aligned to this number. +pub const BIND_BUFFER_ALIGNMENT: hal::buffer::Offset = 256; + +pub fn all_buffer_stages() -> hal::pso::PipelineStage { + use hal::pso::PipelineStage as Ps; + Ps::DRAW_INDIRECT + | Ps::VERTEX_INPUT + | Ps::VERTEX_SHADER + | Ps::FRAGMENT_SHADER + | Ps::COMPUTE_SHADER + | Ps::TRANSFER + | Ps::HOST +} +pub fn all_image_stages() -> hal::pso::PipelineStage { + use hal::pso::PipelineStage as Ps; + Ps::EARLY_FRAGMENT_TESTS + | Ps::LATE_FRAGMENT_TESTS + | Ps::COLOR_ATTACHMENT_OUTPUT + | Ps::VERTEX_SHADER + | Ps::FRAGMENT_SHADER + | Ps::COMPUTE_SHADER + | Ps::TRANSFER +} + +#[derive(Clone, Copy, Debug, PartialEq)] +enum HostMap { + Read, + Write, +} + +#[derive(Clone, Debug, Hash, PartialEq)] +pub(crate) struct AttachmentData { + pub colors: ArrayVec<[T; MAX_COLOR_TARGETS]>, + pub resolves: ArrayVec<[T; MAX_COLOR_TARGETS]>, + pub depth_stencil: Option, +} +impl Eq for AttachmentData {} +impl AttachmentData { + pub(crate) fn all(&self) -> impl Iterator { + self.colors + .iter() + .chain(&self.resolves) + .chain(&self.depth_stencil) + } +} + +impl RenderPassContext { + // Assumed the renderpass only contains one subpass + pub(crate) fn compatible(&self, other: &RenderPassContext) -> bool { + self.colors == other.colors && self.depth_stencil == other.depth_stencil + } +} + +pub(crate) type RenderPassKey = AttachmentData; +pub(crate) type FramebufferKey = AttachmentData; +pub(crate) type RenderPassContext = AttachmentData; + +#[derive(Debug, PartialEq)] +enum ResourceId { + Buffer(BufferId), + Texture(TextureId), + TextureView(TextureViewId), + BindGroup(BindGroupId), + Sampler(SamplerId), +} + +#[derive(Debug)] +enum NativeResource { + Buffer(B::Buffer, MemoryBlock), + Image(B::Image, MemoryBlock), + ImageView(B::ImageView), + Framebuffer(B::Framebuffer), + DescriptorSet(DescriptorSet), + Sampler(B::Sampler), +} + +#[derive(Debug)] +struct ActiveSubmission { + index: SubmissionIndex, + fence: B::Fence, + // Note: we keep the associated ID here in order to be able to check + // at any point what resources are used in a submission. + resources: Vec<(Option, NativeResource)>, + mapped: Vec, +} + +/// A struct responsible for tracking resource lifetimes. +/// +/// Here is how host mapping is handled: +/// 1. When mapping is requested we add the buffer to the pending list of `mapped` buffers. +/// 2. When `triage_referenced` is called, it checks the last submission index associated with each of the mapped buffer, +/// and register the buffer with either a submission in flight, or straight into `ready_to_map` vector. +/// 3. When `ActiveSubmission` is retired, the mapped buffers associated with it are moved to `ready_to_map` vector. +/// 4. Finally, `handle_mapping` issues all the callbacks. + +#[derive(Debug)] +struct PendingResources { + /// Resources that the user has requested be mapped, but are still in use. + mapped: Vec>, + /// Resources that are destroyed by the user but still referenced by + /// other objects or command buffers. + referenced: Vec<(ResourceId, RefCount)>, + /// Resources that are not referenced any more but still used by GPU. + /// Grouped by submissions associated with a fence and a submission index. + /// The active submissions have to be stored in FIFO order: oldest come first. + active: Vec>, + /// Resources that are neither referenced or used, just pending + /// actual deletion. + free: Vec>, + ready_to_map: Vec, +} + +impl PendingResources { + fn destroy(&mut self, resource_id: ResourceId, ref_count: RefCount) { + debug_assert!(!self.referenced.iter().any(|r| r.0 == resource_id)); + self.referenced.push((resource_id, ref_count)); + } + + fn map(&mut self, buffer: BufferId, ref_count: RefCount) { + self.mapped.push(Stored { + value: buffer, + ref_count, + }); + } + + /// Returns the last submission index that is done. + fn cleanup( + &mut self, + device: &B::Device, + heaps_mutex: &Mutex>, + descriptor_allocator_mutex: &Mutex>, + force_wait: bool, + ) -> SubmissionIndex { + if force_wait && !self.active.is_empty() { + let status = unsafe { + device.wait_for_fences( + self.active.iter().map(|a| &a.fence), + hal::device::WaitFor::All, + CLEANUP_WAIT_MS * 1_000_000, + ) + }; + assert_eq!(status, Ok(true), "GPU got stuck :("); + } + + //TODO: enable when `is_sorted_by_key` is stable + //debug_assert!(self.active.is_sorted_by_key(|a| a.index)); + let done_count = self + .active + .iter() + .position(|a| unsafe { !device.get_fence_status(&a.fence).unwrap() }) + .unwrap_or(self.active.len()); + let last_done = if done_count != 0 { + self.active[done_count - 1].index + } else { + return 0; + }; + + for a in self.active.drain(.. done_count) { + log::trace!("Active submission {} is done", a.index); + self.free.extend(a.resources.into_iter().map(|(_, r)| r)); + self.ready_to_map.extend(a.mapped); + unsafe { + device.destroy_fence(a.fence); + } + } + + let mut heaps = heaps_mutex.lock(); + let mut descriptor_allocator = descriptor_allocator_mutex.lock(); + for resource in self.free.drain(..) { + match resource { + NativeResource::Buffer(raw, memory) => unsafe { + device.destroy_buffer(raw); + heaps.free(device, memory); + }, + NativeResource::Image(raw, memory) => unsafe { + device.destroy_image(raw); + heaps.free(device, memory); + }, + NativeResource::ImageView(raw) => unsafe { + device.destroy_image_view(raw); + }, + NativeResource::Framebuffer(raw) => unsafe { + device.destroy_framebuffer(raw); + }, + NativeResource::DescriptorSet(raw) => unsafe { + descriptor_allocator.free(iter::once(raw)); + }, + NativeResource::Sampler(raw) => unsafe { + device.destroy_sampler(raw); + }, + } + } + + last_done + } + + fn triage_referenced( + &mut self, + global: &Global, + trackers: &mut TrackerSet, + mut token: &mut Token>, + ) { + // Before destruction, a resource is expected to have the following strong refs: + // - in resource itself + // - in the device tracker + // - in this list + const MIN_REFS: usize = 4; + + if self.referenced.iter().all(|r| r.1.load() >= MIN_REFS) { + return; + } + + let hub = B::hub(global); + //TODO: lock less, if possible + let (mut bind_group_guard, mut token) = hub.bind_groups.write(&mut token); + let (mut buffer_guard, mut token) = hub.buffers.write(&mut token); + let (mut texture_guard, mut token) = hub.textures.write(&mut token); + let (mut teview_view_guard, mut token) = hub.texture_views.write(&mut token); + let (mut sampler_guard, _) = hub.samplers.write(&mut token); + + for i in (0 .. self.referenced.len()).rev() { + let num_refs = self.referenced[i].1.load(); + if num_refs <= 3 { + let resource_id = self.referenced.swap_remove(i).0; + assert_eq!( + num_refs, 3, + "Resource {:?} misses some references", + resource_id + ); + let (life_guard, resource) = match resource_id { + ResourceId::Buffer(id) => { + if buffer_guard[id].pending_map_operation.is_some() { + continue; + } + trackers.buffers.remove(id); + let buf = buffer_guard.remove(id).unwrap(); + hub.buffers.identity.free(id); + (buf.life_guard, NativeResource::Buffer(buf.raw, buf.memory)) + } + ResourceId::Texture(id) => { + trackers.textures.remove(id); + let tex = texture_guard.remove(id).unwrap(); + hub.textures.identity.free(id); + (tex.life_guard, NativeResource::Image(tex.raw, tex.memory)) + } + ResourceId::TextureView(id) => { + trackers.views.remove(id); + let view = teview_view_guard.remove(id).unwrap(); + let raw = match view.inner { + resource::TextureViewInner::Native { raw, .. } => raw, + resource::TextureViewInner::SwapChain { .. } => unreachable!(), + }; + hub.texture_views.identity.free(id); + (view.life_guard, NativeResource::ImageView(raw)) + } + ResourceId::BindGroup(id) => { + trackers.bind_groups.remove(id); + let bind_group = bind_group_guard.remove(id).unwrap(); + hub.bind_groups.identity.free(id); + ( + bind_group.life_guard, + NativeResource::DescriptorSet(bind_group.raw), + ) + } + ResourceId::Sampler(id) => { + trackers.samplers.remove(id); + let sampler = sampler_guard.remove(id).unwrap(); + hub.samplers.identity.free(id); + (sampler.life_guard, NativeResource::Sampler(sampler.raw)) + } + }; + + let submit_index = life_guard.submission_index.load(Ordering::Acquire); + match self.active.iter_mut().find(|a| a.index == submit_index) { + Some(a) => { + a.resources.alloc().init((Some(resource_id), resource)); + } + None => self.free.push(resource), + } + } + } + } + + fn triage_mapped(&mut self, global: &Global, token: &mut Token>) { + if self.mapped.is_empty() { + return; + } + let (buffer_guard, _) = B::hub(global).buffers.read(token); + + for stored in self.mapped.drain(..) { + let resource_id = stored.value; + let buf = &buffer_guard[resource_id]; + + let submit_index = buf.life_guard.submission_index.load(Ordering::Acquire); + log::trace!( + "Mapping of {:?} at submission {:?} gets assigned to active {:?}", + resource_id, + submit_index, + self.active.iter().position(|a| a.index == submit_index) + ); + + self.active + .iter_mut() + .find(|a| a.index == submit_index) + .map_or(&mut self.ready_to_map, |a| &mut a.mapped) + .push(resource_id); + } + } + + fn triage_framebuffers( + &mut self, + global: &Global, + framebuffers: &mut FastHashMap, + token: &mut Token>, + ) { + let (texture_view_guard, _) = B::hub(global).texture_views.read(token); + let remove_list = framebuffers + .keys() + .filter_map(|key| { + let mut last_submit: SubmissionIndex = 0; + for &at in key.all() { + if texture_view_guard.contains(at) { + return None; + } + // This attachment is no longer registered. + // Let's see if it's used by any of the active submissions. + let res_id = &Some(ResourceId::TextureView(at)); + for a in &self.active { + if a.resources.iter().any(|&(ref id, _)| id == res_id) { + last_submit = last_submit.max(a.index); + } + } + } + Some((key.clone(), last_submit)) + }) + .collect::>(); + + for (ref key, submit_index) in remove_list { + let resource = NativeResource::Framebuffer(framebuffers.remove(key).unwrap()); + match self.active.iter_mut().find(|a| a.index == submit_index) { + Some(a) => { + a.resources.alloc().init((None, resource)); + } + None => self.free.push(resource), + } + } + } + + fn handle_mapping( + &mut self, + global: &Global, + raw: &B::Device, + token: &mut Token>, + ) -> Vec { + if self.ready_to_map.is_empty() { + return Vec::new(); + } + let (mut buffer_guard, _) = B::hub(global).buffers.write(token); + self.ready_to_map + .drain(..) + .map(|buffer_id| { + let buffer = &mut buffer_guard[buffer_id]; + let operation = buffer.pending_map_operation.take().unwrap(); + let result = match operation { + resource::BufferMapOperation::Read(ref range, ..) => { + map_buffer(raw, buffer, range.clone(), HostMap::Read) + } + resource::BufferMapOperation::Write(ref range, ..) => { + map_buffer(raw, buffer, range.clone(), HostMap::Write) + } + }; + (operation, result) + }) + .collect() + } +} + +type BufferMapResult = Result<*mut u8, hal::device::MapError>; +type BufferMapPendingCallback = (resource::BufferMapOperation, BufferMapResult); + +fn map_buffer( + raw: &B::Device, + buffer: &mut resource::Buffer, + buffer_range: Range, + kind: HostMap, +) -> BufferMapResult { + let is_coherent = buffer + .memory + .properties() + .contains(hal::memory::Properties::COHERENT); + let (ptr, mapped_range) = { + let mapped = buffer.memory.map(raw, buffer_range)?; + (mapped.ptr(), mapped.range()) + }; + + if !is_coherent { + match kind { + HostMap::Read => unsafe { + raw.invalidate_mapped_memory_ranges(iter::once(( + buffer.memory.memory(), + mapped_range, + ))) + .unwrap(); + }, + HostMap::Write => { + buffer.mapped_write_ranges.push(mapped_range); + } + } + } + + Ok(ptr.as_ptr()) +} + +#[derive(Debug)] +pub struct Device { + pub(crate) raw: B::Device, + pub(crate) adapter_id: AdapterId, + pub(crate) queue_group: hal::queue::QueueGroup, + pub(crate) com_allocator: command::CommandAllocator, + mem_allocator: Mutex>, + desc_allocator: Mutex>, + life_guard: LifeGuard, + pub(crate) trackers: Mutex, + pub(crate) render_passes: Mutex>, + pub(crate) framebuffers: Mutex>, + pending: Mutex>, + pub(crate) features: Features, +} + +impl Device { + pub(crate) fn new( + raw: B::Device, + adapter_id: AdapterId, + queue_group: hal::queue::QueueGroup, + mem_props: hal::adapter::MemoryProperties, + supports_texture_d24_s8: bool, + max_bind_groups: u32, + ) -> Self { + // don't start submission index at zero + let life_guard = LifeGuard::new(); + life_guard.submission_index.fetch_add(1, Ordering::Relaxed); + + let heaps = { + let types = mem_props.memory_types.iter().map(|mt| { + use rendy_memory::{DynamicConfig, HeapsConfig, LinearConfig}; + let config = HeapsConfig { + linear: if mt.properties.contains(hal::memory::Properties::CPU_VISIBLE) { + Some(LinearConfig { + linear_size: 0x10_00_00, + }) + } else { + None + }, + dynamic: Some(DynamicConfig { + block_size_granularity: 0x1_00, + max_chunk_size: 0x1_00_00_00, + min_device_allocation: 0x1_00_00, + }), + }; + (mt.properties.into(), mt.heap_index as u32, config) + }); + unsafe { Heaps::new(types, mem_props.memory_heaps.iter().cloned()) } + }; + + Device { + raw, + adapter_id, + com_allocator: command::CommandAllocator::new(queue_group.family), + mem_allocator: Mutex::new(heaps), + desc_allocator: Mutex::new(DescriptorAllocator::new()), + queue_group, + life_guard, + trackers: Mutex::new(TrackerSet::new(B::VARIANT)), + render_passes: Mutex::new(FastHashMap::default()), + framebuffers: Mutex::new(FastHashMap::default()), + pending: Mutex::new(PendingResources { + mapped: Vec::new(), + referenced: Vec::new(), + active: Vec::new(), + free: Vec::new(), + ready_to_map: Vec::new(), + }), + features: Features { + max_bind_groups, + supports_texture_d24_s8, + }, + } + } + + fn maintain( + &self, + global: &Global, + force_wait: bool, + token: &mut Token, + ) -> Vec { + let mut pending = self.pending.lock(); + let mut trackers = self.trackers.lock(); + + pending.triage_referenced(global, &mut *trackers, token); + pending.triage_mapped(global, token); + pending.triage_framebuffers(global, &mut *self.framebuffers.lock(), token); + let last_done = pending.cleanup( + &self.raw, + &self.mem_allocator, + &self.desc_allocator, + force_wait, + ); + let callbacks = pending.handle_mapping(global, &self.raw, token); + + unsafe { + self.desc_allocator.lock().cleanup(&self.raw); + } + + if last_done != 0 { + self.com_allocator.maintain(last_done); + } + + callbacks + } + + //Note: this logic is specifically moved out of `handle_mapping()` in order to + // have nothing locked by the time we execute users callback code. + fn fire_map_callbacks>(callbacks: I) { + for (operation, result) in callbacks { + let (status, ptr) = match result { + Ok(ptr) => (resource::BufferMapAsyncStatus::Success, ptr), + Err(e) => { + log::error!("failed to map buffer: {:?}", e); + (resource::BufferMapAsyncStatus::Error, ptr::null_mut()) + } + }; + match operation { + resource::BufferMapOperation::Read(_, on_read, userdata) => { + on_read(status, ptr, userdata) + } + resource::BufferMapOperation::Write(_, on_write, userdata) => { + on_write(status, ptr, userdata) + } + } + } + } + + fn create_buffer( + &self, + self_id: DeviceId, + desc: &resource::BufferDescriptor, + ) -> resource::Buffer { + debug_assert_eq!(self_id.backend(), B::VARIANT); + let (usage, _memory_properties) = conv::map_buffer_usage(desc.usage); + + let rendy_usage = { + use rendy_memory::MemoryUsageValue as Muv; + use resource::BufferUsage as Bu; + + if !desc.usage.intersects(Bu::MAP_READ | Bu::MAP_WRITE) { + Muv::Data + } else if (Bu::MAP_WRITE | Bu::COPY_SRC).contains(desc.usage) { + Muv::Upload + } else if (Bu::MAP_READ | Bu::COPY_DST).contains(desc.usage) { + Muv::Download + } else { + Muv::Dynamic + } + }; + + let mut buffer = unsafe { self.raw.create_buffer(desc.size, usage).unwrap() }; + let requirements = unsafe { self.raw.get_buffer_requirements(&buffer) }; + let memory = self + .mem_allocator + .lock() + .allocate( + &self.raw, + requirements.type_mask as u32, + rendy_usage, + requirements.size, + requirements.alignment, + ) + .unwrap(); + + unsafe { + self.raw + .bind_buffer_memory(memory.memory(), memory.range().start, &mut buffer) + .unwrap() + }; + + resource::Buffer { + raw: buffer, + device_id: Stored { + value: self_id, + ref_count: self.life_guard.ref_count.clone(), + }, + usage: desc.usage, + memory, + size: desc.size, + mapped_write_ranges: Vec::new(), + pending_map_operation: None, + life_guard: LifeGuard::new(), + } + } + + fn create_texture( + &self, + self_id: DeviceId, + desc: &resource::TextureDescriptor, + ) -> resource::Texture { + debug_assert_eq!(self_id.backend(), B::VARIANT); + + // Ensure `D24Plus` textures cannot be copied + match desc.format { + resource::TextureFormat::Depth24Plus | resource::TextureFormat::Depth24PlusStencil8 => { + assert!(!desc.usage.intersects( + resource::TextureUsage::COPY_SRC | resource::TextureUsage::COPY_DST + )); + } + _ => {} + } + + let kind = conv::map_texture_dimension_size( + desc.dimension, + desc.size, + desc.array_layer_count, + desc.sample_count, + ); + let format = conv::map_texture_format(desc.format, self.features); + let aspects = format.surface_desc().aspects; + let usage = conv::map_texture_usage(desc.usage, aspects); + + assert!((desc.mip_level_count as usize) < MAX_MIP_LEVELS); + let mut view_capabilities = hal::image::ViewCapabilities::empty(); + + // 2D textures with array layer counts that are multiples of 6 could be cubemaps + // Following gpuweb/gpuweb#68 always add the hint in that case + if desc.dimension == resource::TextureDimension::D2 && desc.array_layer_count % 6 == 0 { + view_capabilities |= hal::image::ViewCapabilities::KIND_CUBE; + }; + + // TODO: 2D arrays, cubemap arrays + + let mut image = unsafe { + self.raw.create_image( + kind, + desc.mip_level_count as hal::image::Level, + format, + hal::image::Tiling::Optimal, + usage, + view_capabilities, + ) + } + .unwrap(); + let requirements = unsafe { self.raw.get_image_requirements(&image) }; + + let memory = self + .mem_allocator + .lock() + .allocate( + &self.raw, + requirements.type_mask as u32, + rendy_memory::Data, + requirements.size, + requirements.alignment, + ) + .unwrap(); + + unsafe { + self.raw + .bind_image_memory(memory.memory(), memory.range().start, &mut image) + .unwrap() + }; + + resource::Texture { + raw: image, + device_id: Stored { + value: self_id, + ref_count: self.life_guard.ref_count.clone(), + }, + usage: desc.usage, + kind, + format: desc.format, + full_range: hal::image::SubresourceRange { + aspects, + levels: 0 .. desc.mip_level_count as hal::image::Level, + layers: 0 .. desc.array_layer_count as hal::image::Layer, + }, + memory, + life_guard: LifeGuard::new(), + } + } +} + +impl Device { + pub(crate) fn destroy_bind_group(&self, bind_group: binding_model::BindGroup) { + unsafe { + self.desc_allocator.lock().free(iter::once(bind_group.raw)); + } + } + + pub(crate) fn dispose(self) { + self.com_allocator.destroy(&self.raw); + let desc_alloc = self.desc_allocator.into_inner(); + unsafe { + desc_alloc.dispose(&self.raw); + } + } +} + +#[derive(Debug)] +pub struct ShaderModule { + pub(crate) raw: B::ShaderModule, +} + + +impl> Global { + pub fn device_create_buffer( + &self, + device_id: DeviceId, + desc: &resource::BufferDescriptor, + id_in: F::Input, + ) -> BufferId { + let hub = B::hub(self); + let mut token = Token::root(); + + let (device_guard, _) = hub.devices.read(&mut token); + let device = &device_guard[device_id]; + let buffer = device.create_buffer(device_id, desc); + let ref_count = buffer.life_guard.ref_count.clone(); + + let id = hub.buffers.register_identity(id_in, buffer, &mut token); + let ok = + device + .trackers + .lock() + .buffers + .init(id, ref_count, (), resource::BufferUsage::empty()); + assert!(ok); + id + } + + pub fn device_create_buffer_mapped( + &self, + device_id: DeviceId, + desc: &resource::BufferDescriptor, + mapped_ptr_out: *mut *mut u8, + id_in: F::Input, + ) -> BufferId { + let hub = B::hub(self); + let mut token = Token::root(); + let mut desc = desc.clone(); + desc.usage |= resource::BufferUsage::MAP_WRITE; + + let (device_guard, _) = hub.devices.read(&mut token); + let device = &device_guard[device_id]; + let mut buffer = device.create_buffer(device_id, &desc); + let ref_count = buffer.life_guard.ref_count.clone(); + + match map_buffer(&device.raw, &mut buffer, 0 .. desc.size, HostMap::Write) { + Ok(ptr) => unsafe { + *mapped_ptr_out = ptr; + }, + Err(e) => { + log::error!("failed to create buffer in a mapped state: {:?}", e); + unsafe { + *mapped_ptr_out = ptr::null_mut(); + } + } + } + + let id = hub.buffers.register_identity(id_in, buffer, &mut token); + let ok = device.trackers.lock().buffers.init( + id, + ref_count, + (), + resource::BufferUsage::MAP_WRITE, + ); + assert!(ok); + id + } + + pub fn buffer_destroy(&self, buffer_id: BufferId) { + let hub = B::hub(self); + let mut token = Token::root(); + let (device_guard, mut token) = hub.devices.read(&mut token); + let (buffer_guard, _) = hub.buffers.read(&mut token); + let buffer = &buffer_guard[buffer_id]; + device_guard[buffer.device_id.value].pending.lock().destroy( + ResourceId::Buffer(buffer_id), + buffer.life_guard.ref_count.clone(), + ); + } +} + +impl> Global { + pub fn device_create_texture( + &self, + device_id: DeviceId, + desc: &resource::TextureDescriptor, + id_in: F::Input, + ) -> TextureId { + let hub = B::hub(self); + let mut token = Token::root(); + + let (device_guard, _) = hub.devices.read(&mut token); + let device = &device_guard[device_id]; + let texture = device.create_texture(device_id, desc); + let range = texture.full_range.clone(); + let ref_count = texture.life_guard.ref_count.clone(); + + let id = hub.textures.register_identity(id_in, texture, &mut token); + let ok = device.trackers.lock().textures.init( + id, + ref_count, + range, + resource::TextureUsage::UNINITIALIZED, + ); + assert!(ok); + id + } + + pub fn texture_destroy(&self, texture_id: TextureId) { + let hub = B::hub(self); + let mut token = Token::root(); + + let (device_guard, mut token) = hub.devices.read(&mut token); + let (texture_guard, _) = hub.textures.read(&mut token); + let texture = &texture_guard[texture_id]; + device_guard[texture.device_id.value] + .pending + .lock() + .destroy( + ResourceId::Texture(texture_id), + texture.life_guard.ref_count.clone(), + ); + } +} + +impl> Global { + pub fn texture_create_view( + &self, + texture_id: TextureId, + desc: Option<&resource::TextureViewDescriptor>, + id_in: F::Input, + ) -> TextureViewId { + let hub = B::hub(self); + let mut token = Token::root(); + + let (device_guard, mut token) = hub.devices.read(&mut token); + let (texture_guard, mut token) = hub.textures.read(&mut token); + let texture = &texture_guard[texture_id]; + let device = &device_guard[texture.device_id.value]; + + let (format, view_kind, range) = match desc { + Some(desc) => { + let kind = conv::map_texture_view_dimension(desc.dimension); + let end_level = if desc.level_count == 0 { + texture.full_range.levels.end + } else { + (desc.base_mip_level + desc.level_count) as u8 + }; + let end_layer = if desc.array_layer_count == 0 { + texture.full_range.layers.end + } else { + (desc.base_array_layer + desc.array_layer_count) as u16 + }; + let range = hal::image::SubresourceRange { + aspects: match desc.aspect { + resource::TextureAspect::All => texture.full_range.aspects, + resource::TextureAspect::DepthOnly => hal::format::Aspects::DEPTH, + resource::TextureAspect::StencilOnly => hal::format::Aspects::STENCIL, + }, + levels: desc.base_mip_level as u8 .. end_level, + layers: desc.base_array_layer as u16 .. end_layer, + }; + (desc.format, kind, range) + } + None => { + let kind = match texture.kind { + hal::image::Kind::D1(_, 1) => hal::image::ViewKind::D1, + hal::image::Kind::D1(..) => hal::image::ViewKind::D1Array, + hal::image::Kind::D2(_, _, 1, _) => hal::image::ViewKind::D2, + hal::image::Kind::D2(..) => hal::image::ViewKind::D2Array, + hal::image::Kind::D3(..) => hal::image::ViewKind::D3, + }; + (texture.format, kind, texture.full_range.clone()) + } + }; + + let raw = unsafe { + device + .raw + .create_image_view( + &texture.raw, + view_kind, + conv::map_texture_format(format, device.features), + hal::format::Swizzle::NO, + range.clone(), + ) + .unwrap() + }; + + let view = resource::TextureView { + inner: resource::TextureViewInner::Native { + raw, + source_id: Stored { + value: texture_id, + ref_count: texture.life_guard.ref_count.clone(), + }, + }, + format: texture.format, + extent: texture.kind.extent().at_level(range.levels.start), + samples: texture.kind.num_samples(), + range, + life_guard: LifeGuard::new(), + }; + let ref_count = view.life_guard.ref_count.clone(); + + let id = hub.texture_views.register_identity(id_in, view, &mut token); + let ok = device.trackers.lock().views.init(id, ref_count, (), ()); + assert!(ok); + id + } + + pub fn texture_view_destroy(&self, texture_view_id: TextureViewId) { + let hub = B::hub(self); + let mut token = Token::root(); + let (device_guard, mut token) = hub.devices.read(&mut token); + let (texture_guard, mut token) = hub.textures.read(&mut token); + let (texture_view_guard, _) = hub.texture_views.read(&mut token); + let view = &texture_view_guard[texture_view_id]; + let device_id = match view.inner { + resource::TextureViewInner::Native { ref source_id, .. } => { + texture_guard[source_id.value].device_id.value + } + resource::TextureViewInner::SwapChain { .. } => { + panic!("Can't destroy a swap chain image") + } + }; + device_guard[device_id].pending.lock().destroy( + ResourceId::TextureView(texture_view_id), + view.life_guard.ref_count.clone(), + ); + } +} + +impl> Global { + pub fn device_create_sampler( + &self, + device_id: DeviceId, + desc: &resource::SamplerDescriptor, + id_in: F::Input, + ) -> SamplerId { + let hub = B::hub(self); + let mut token = Token::root(); + let (device_guard, mut token) = hub.devices.read(&mut token); + let device = &device_guard[device_id]; + + let info = hal::image::SamplerDesc { + min_filter: conv::map_filter(desc.min_filter), + mag_filter: conv::map_filter(desc.mag_filter), + mip_filter: conv::map_filter(desc.mipmap_filter), + wrap_mode: ( + conv::map_wrap(desc.address_mode_u), + conv::map_wrap(desc.address_mode_v), + conv::map_wrap(desc.address_mode_w), + ), + lod_bias: hal::image::Lod(0.0), + lod_range: hal::image::Lod(desc.lod_min_clamp) .. hal::image::Lod(desc.lod_max_clamp), + comparison: if desc.compare_function == resource::CompareFunction::Always { + None + } else { + Some(conv::map_compare_function(desc.compare_function)) + }, + border: hal::image::PackedColor(0), + normalized: true, + anisotropic: hal::image::Anisotropic::Off, //TODO + }; + + let sampler = resource::Sampler { + raw: unsafe { device.raw.create_sampler(&info).unwrap() }, + device_id: Stored { + value: device_id, + ref_count: device.life_guard.ref_count.clone(), + }, + life_guard: LifeGuard::new(), + }; + let ref_count = sampler.life_guard.ref_count.clone(); + + let id = hub.samplers.register_identity(id_in, sampler, &mut token); + let ok = device.trackers.lock().samplers.init(id, ref_count, (), ()); + assert!(ok); + id + } + + pub fn sampler_destroy(&self, sampler_id: SamplerId) { + let hub = B::hub(self); + let mut token = Token::root(); + let (device_guard, mut token) = hub.devices.read(&mut token); + let (sampler_guard, _) = hub.samplers.read(&mut token); + let sampler = &sampler_guard[sampler_id]; + device_guard[sampler.device_id.value] + .pending + .lock() + .destroy( + ResourceId::Sampler(sampler_id), + sampler.life_guard.ref_count.clone(), + ); + } +} + +impl> Global { + pub fn device_create_bind_group_layout( + &self, + device_id: DeviceId, + desc: &binding_model::BindGroupLayoutDescriptor, + id_in: F::Input, + ) -> BindGroupLayoutId { + let mut token = Token::root(); + let hub = B::hub(self); + let bindings = unsafe { slice::from_raw_parts(desc.bindings, desc.bindings_length) }; + + let raw_bindings = bindings + .iter() + .map(|binding| hal::pso::DescriptorSetLayoutBinding { + binding: binding.binding, + ty: conv::map_binding_type(binding), + count: 1, //TODO: consolidate + stage_flags: conv::map_shader_stage_flags(binding.visibility), + immutable_samplers: false, // TODO + }) + .collect::>(); //TODO: avoid heap allocation + + let raw = unsafe { + let (device_guard, _) = hub.devices.read(&mut token); + device_guard[device_id] + .raw + .create_descriptor_set_layout(&raw_bindings, &[]) + .unwrap() + }; + + let layout = binding_model::BindGroupLayout { + raw, + bindings: bindings.to_vec(), + desc_ranges: DescriptorRanges::from_bindings(&raw_bindings), + dynamic_count: bindings.iter().filter(|b| b.dynamic).count(), + }; + + hub.bind_group_layouts + .register_identity(id_in, layout, &mut token) + } +} + +impl> Global { + pub fn device_create_pipeline_layout( + &self, + device_id: DeviceId, + desc: &binding_model::PipelineLayoutDescriptor, + id_in: F::Input, + ) -> PipelineLayoutId { + let hub = B::hub(self); + let mut token = Token::root(); + + let (device_guard, mut token) = hub.devices.read(&mut token); + let device = &device_guard[device_id]; + let bind_group_layout_ids = unsafe { + slice::from_raw_parts(desc.bind_group_layouts, desc.bind_group_layouts_length) + }; + + assert!(desc.bind_group_layouts_length <= (device.features.max_bind_groups as usize), + "Cannot set a bind group which is beyond the `max_bind_groups` limit requested on device creation"); + + // TODO: push constants + let pipeline_layout = { + let (bind_group_layout_guard, _) = hub.bind_group_layouts.read(&mut token); + let descriptor_set_layouts = bind_group_layout_ids + .iter() + .map(|&id| &bind_group_layout_guard[id].raw); + unsafe { + device + .raw + .create_pipeline_layout(descriptor_set_layouts, &[]) + } + .unwrap() + }; + + let layout = binding_model::PipelineLayout { + raw: pipeline_layout, + bind_group_layout_ids: bind_group_layout_ids.iter().cloned().collect(), + }; + hub.pipeline_layouts + .register_identity(id_in, layout, &mut token) + } +} + +impl> Global { + pub fn device_create_bind_group( + &self, + device_id: DeviceId, + desc: &binding_model::BindGroupDescriptor, + id_in: F::Input, + ) -> BindGroupId { + let hub = B::hub(self); + let mut token = Token::root(); + + let (device_guard, mut token) = hub.devices.read(&mut token); + let device = &device_guard[device_id]; + let (bind_group_layout_guard, _) = hub.bind_group_layouts.read(&mut token); + let bind_group_layout = &bind_group_layout_guard[desc.layout]; + let bindings = + unsafe { slice::from_raw_parts(desc.bindings, desc.bindings_length as usize) }; + assert_eq!(bindings.len(), bind_group_layout.bindings.len()); + + let desc_set = unsafe { + let mut desc_sets = ArrayVec::<[_; 1]>::new(); + device + .desc_allocator + .lock() + .allocate( + &device.raw, + &bind_group_layout.raw, + bind_group_layout.desc_ranges, + 1, + &mut desc_sets, + ) + .unwrap(); + desc_sets.pop().unwrap() + }; + + // fill out the descriptors + let mut used = TrackerSet::new(B::VARIANT); + { + let (buffer_guard, mut token) = hub.buffers.read(&mut token); + let (texture_guard, mut token) = hub.textures.read(&mut token); //skip token + let (texture_view_guard, mut token) = hub.texture_views.read(&mut token); + let (sampler_guard, _) = hub.samplers.read(&mut token); + + //TODO: group writes into contiguous sections + let mut writes = Vec::new(); + for (b, decl) in bindings.iter().zip(&bind_group_layout.bindings) { + let descriptor = match b.resource { + binding_model::BindingResource::Buffer(ref bb) => { + let (alignment, usage) = match decl.ty { + binding_model::BindingType::UniformBuffer => { + (BIND_BUFFER_ALIGNMENT, resource::BufferUsage::UNIFORM) + } + binding_model::BindingType::StorageBuffer => { + (BIND_BUFFER_ALIGNMENT, resource::BufferUsage::STORAGE) + } + binding_model::BindingType::ReadonlyStorageBuffer => { + (BIND_BUFFER_ALIGNMENT, resource::BufferUsage::STORAGE_READ) + } + binding_model::BindingType::Sampler + | binding_model::BindingType::SampledTexture + | binding_model::BindingType::StorageTexture => { + panic!("Mismatched buffer binding for {:?}", decl) + } + }; + assert_eq!( + bb.offset as hal::buffer::Offset % alignment, + 0, + "Misaligned buffer offset {}", + bb.offset + ); + let buffer = used + .buffers + .use_extend(&*buffer_guard, bb.buffer, (), usage) + .unwrap(); + assert!( + buffer.usage.contains(usage), + "Expected buffer usage {:?}", + usage + ); + + let end = if bb.size == 0 { + None + } else { + let end = bb.offset + bb.size; + assert!( + end <= buffer.size, + "Bound buffer range {:?} does not fit in buffer size {}", + bb.offset .. end, + buffer.size + ); + Some(end) + }; + + let range = Some(bb.offset) .. end; + hal::pso::Descriptor::Buffer(&buffer.raw, range) + } + binding_model::BindingResource::Sampler(id) => { + assert_eq!(decl.ty, binding_model::BindingType::Sampler); + let sampler = used + .samplers + .use_extend(&*sampler_guard, id, (), ()) + .unwrap(); + hal::pso::Descriptor::Sampler(&sampler.raw) + } + binding_model::BindingResource::TextureView(id) => { + let (usage, image_layout) = match decl.ty { + binding_model::BindingType::SampledTexture => ( + resource::TextureUsage::SAMPLED, + hal::image::Layout::ShaderReadOnlyOptimal, + ), + binding_model::BindingType::StorageTexture => { + (resource::TextureUsage::STORAGE, hal::image::Layout::General) + } + _ => panic!("Mismatched texture binding for {:?}", decl), + }; + let view = used + .views + .use_extend(&*texture_view_guard, id, (), ()) + .unwrap(); + match view.inner { + resource::TextureViewInner::Native { + ref raw, + ref source_id, + } => { + let texture = used + .textures + .use_extend( + &*texture_guard, + source_id.value, + view.range.clone(), + usage, + ) + .unwrap(); + assert!(texture.usage.contains(usage)); + + hal::pso::Descriptor::Image(raw, image_layout) + } + resource::TextureViewInner::SwapChain { .. } => { + panic!("Unable to create a bind group with a swap chain image") + } + } + } + }; + writes.alloc().init(hal::pso::DescriptorSetWrite { + set: desc_set.raw(), + binding: b.binding, + array_offset: 0, //TODO + descriptors: iter::once(descriptor), + }); + } + + unsafe { + device.raw.write_descriptor_sets(writes); + } + } + + let bind_group = binding_model::BindGroup { + raw: desc_set, + device_id: Stored { + value: device_id, + ref_count: device.life_guard.ref_count.clone(), + }, + layout_id: desc.layout, + life_guard: LifeGuard::new(), + used, + dynamic_count: bind_group_layout.dynamic_count, + }; + let ref_count = bind_group.life_guard.ref_count.clone(); + + + let id = hub + .bind_groups + .register_identity(id_in, bind_group, &mut token); + let ok = device + .trackers + .lock() + .bind_groups + .init(id, ref_count, (), ()); + assert!(ok); + id + } + + pub fn bind_group_destroy(&self, bind_group_id: BindGroupId) { + let hub = B::hub(self); + let mut token = Token::root(); + let (device_guard, mut token) = hub.devices.read(&mut token); + let (bind_group_guard, _) = hub.bind_groups.read(&mut token); + let bind_group = &bind_group_guard[bind_group_id]; + device_guard[bind_group.device_id.value] + .pending + .lock() + .destroy( + ResourceId::BindGroup(bind_group_id), + bind_group.life_guard.ref_count.clone(), + ); + } +} + +impl> Global { + pub fn device_create_shader_module( + &self, + device_id: DeviceId, + desc: &pipeline::ShaderModuleDescriptor, + id_in: F::Input, + ) -> ShaderModuleId { + let hub = B::hub(self); + let mut token = Token::root(); + + let spv = unsafe { slice::from_raw_parts(desc.code.bytes, desc.code.length) }; + let shader = { + let (device_guard, _) = hub.devices.read(&mut token); + ShaderModule { + raw: unsafe { + device_guard[device_id] + .raw + .create_shader_module(spv) + .unwrap() + }, + } + }; + hub.shader_modules + .register_identity(id_in, shader, &mut token) + } +} + +impl> Global { + pub fn device_create_command_encoder( + &self, + device_id: DeviceId, + _desc: &command::CommandEncoderDescriptor, + id_in: F::Input, + ) -> CommandEncoderId { + let hub = B::hub(self); + let mut token = Token::root(); + + let (device_guard, mut token) = hub.devices.read(&mut token); + let device = &device_guard[device_id]; + + let dev_stored = Stored { + value: device_id, + ref_count: device.life_guard.ref_count.clone(), + }; + let mut comb = device + .com_allocator + .allocate(dev_stored, &device.raw, device.features); + unsafe { + comb.raw.last_mut().unwrap().begin( + hal::command::CommandBufferFlags::ONE_TIME_SUBMIT, + hal::command::CommandBufferInheritanceInfo::default(), + ); + } + + hub.command_buffers + .register_identity(id_in, comb, &mut token) + } +} + +impl> Global { + pub fn queue_submit( + &self, + queue_id: QueueId, + command_buffer_ids: &[CommandBufferId], + ) { + let hub = B::hub(self); + + let (submit_index, fence) = { + let mut token = Token::root(); + let (mut device_guard, mut token) = hub.devices.write(&mut token); + let (swap_chain_guard, mut token) = hub.swap_chains.read(&mut token); + let device = &mut device_guard[queue_id]; + + let mut trackers = device.trackers.lock(); + let mut signal_semaphores = Vec::new(); + + let submit_index = 1 + device + .life_guard + .submission_index + .fetch_add(1, Ordering::Relaxed); + + let (mut command_buffer_guard, mut token) = hub.command_buffers.write(&mut token); + let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token); + let (buffer_guard, mut token) = hub.buffers.read(&mut token); + let (texture_guard, mut token) = hub.textures.read(&mut token); + let (mut texture_view_guard, mut token) = hub.texture_views.write(&mut token); + let (sampler_guard, _) = hub.samplers.read(&mut token); + + //TODO: if multiple command buffers are submitted, we can re-use the last + // native command buffer of the previous chain instead of always creating + // a temporary one, since the chains are not finished. + + // finish all the command buffers first + for &cmb_id in command_buffer_ids { + let comb = &mut command_buffer_guard[cmb_id]; + + if let Some((view_id, fbo)) = comb.used_swap_chain.take() { + match texture_view_guard[view_id.value].inner { + resource::TextureViewInner::Native { .. } => unreachable!(), + resource::TextureViewInner::SwapChain { + ref source_id, + ref mut framebuffers, + .. + } => { + if framebuffers.is_empty() { + let sem = &swap_chain_guard[source_id.value].semaphore; + signal_semaphores.push(sem); + } + framebuffers.push(fbo); + } + }; + } + + // optimize the tracked states + comb.trackers.optimize(); + + // update submission IDs + for id in comb.trackers.buffers.used() { + let buffer = &buffer_guard[id]; + assert!(buffer.pending_map_operation.is_none()); + buffer + .life_guard + .submission_index + .store(submit_index, Ordering::Release); + } + for id in comb.trackers.textures.used() { + texture_guard[id] + .life_guard + .submission_index + .store(submit_index, Ordering::Release); + } + for id in comb.trackers.views.used() { + texture_view_guard[id] + .life_guard + .submission_index + .store(submit_index, Ordering::Release); + } + for id in comb.trackers.bind_groups.used() { + bind_group_guard[id] + .life_guard + .submission_index + .store(submit_index, Ordering::Release); + } + for id in comb.trackers.samplers.used() { + sampler_guard[id] + .life_guard + .submission_index + .store(submit_index, Ordering::Release); + } + + // execute resource transitions + let mut transit = device.com_allocator.extend(comb); + unsafe { + transit.begin( + hal::command::CommandBufferFlags::ONE_TIME_SUBMIT, + hal::command::CommandBufferInheritanceInfo::default(), + ); + } + log::trace!("Stitching command buffer {:?} before submission", cmb_id); + command::CommandBuffer::insert_barriers( + &mut transit, + &mut *trackers, + &comb.trackers, + Stitch::Init, + &*buffer_guard, + &*texture_guard, + ); + unsafe { + transit.finish(); + } + comb.raw.insert(0, transit); + unsafe { + comb.raw.last_mut().unwrap().finish(); + } + } + + // now prepare the GPU submission + let fence = device.raw.create_fence(false).unwrap(); + let submission = hal::queue::Submission::<_, _, Vec<&B::Semaphore>> { + command_buffers: command_buffer_ids + .iter() + .flat_map(|&cmb_id| &command_buffer_guard[cmb_id].raw), + wait_semaphores: Vec::new(), + signal_semaphores, + }; + + unsafe { + device.queue_group.queues[0].submit(submission, Some(&fence)); + } + + (submit_index, fence) + }; + + // No need for write access to the device from here on out + let callbacks = { + let mut token = Token::root(); + let (device_guard, mut token) = hub.devices.read(&mut token); + let device = &device_guard[queue_id]; + + let callbacks = device.maintain(self, false, &mut token); + device.pending.lock().active.alloc().init(ActiveSubmission { + index: submit_index, + fence, + resources: Vec::new(), + mapped: Vec::new(), + }); + + // finally, return the command buffers to the allocator + for &cmb_id in command_buffer_ids { + let (cmd_buf, _) = hub.command_buffers.unregister(cmb_id, &mut token); + device.com_allocator.after_submit(cmd_buf, submit_index); + } + + callbacks + }; + + Device::::fire_map_callbacks(callbacks); + } +} + +impl> Global { + pub fn device_create_render_pipeline( + &self, + device_id: DeviceId, + desc: &pipeline::RenderPipelineDescriptor, + id_in: F::Input, + ) -> RenderPipelineId { + let hub = B::hub(self); + let mut token = Token::root(); + + let sc = desc.sample_count; + assert!( + sc == 1 || sc == 2 || sc == 4 || sc == 8 || sc == 16 || sc == 32, + "Invalid sample_count of {}", + sc + ); + let sc = sc as u8; + + let color_states = + unsafe { slice::from_raw_parts(desc.color_states, desc.color_states_length) }; + let depth_stencil_state = unsafe { desc.depth_stencil_state.as_ref() }; + + let rasterizer = conv::map_rasterization_state_descriptor( + &unsafe { desc.rasterization_state.as_ref() } + .cloned() + .unwrap_or_default(), + ); + + let desc_vbs = unsafe { + slice::from_raw_parts( + desc.vertex_input.vertex_buffers, + desc.vertex_input.vertex_buffers_length, + ) + }; + let mut vertex_strides = Vec::with_capacity(desc_vbs.len()); + let mut vertex_buffers = Vec::with_capacity(desc_vbs.len()); + let mut attributes = Vec::new(); + for (i, vb_state) in desc_vbs.iter().enumerate() { + vertex_strides + .alloc() + .init((vb_state.stride, vb_state.step_mode)); + if vb_state.attributes_length == 0 { + continue; + } + vertex_buffers.alloc().init(hal::pso::VertexBufferDesc { + binding: i as u32, + stride: vb_state.stride as u32, + rate: match vb_state.step_mode { + pipeline::InputStepMode::Vertex => hal::pso::VertexInputRate::Vertex, + pipeline::InputStepMode::Instance => hal::pso::VertexInputRate::Instance(1), + }, + }); + let desc_atts = + unsafe { slice::from_raw_parts(vb_state.attributes, vb_state.attributes_length) }; + for attribute in desc_atts { + assert_eq!(0, attribute.offset >> 32); + attributes.alloc().init(hal::pso::AttributeDesc { + location: attribute.shader_location, + binding: i as u32, + element: hal::pso::Element { + format: conv::map_vertex_format(attribute.format), + offset: attribute.offset as u32, + }, + }); + } + } + + let input_assembler = hal::pso::InputAssemblerDesc { + primitive: conv::map_primitive_topology(desc.primitive_topology), + with_adjacency: false, + restart_index: None, //TODO + }; + + let blender = hal::pso::BlendDesc { + logic_op: None, // TODO + targets: color_states + .iter() + .map(conv::map_color_state_descriptor) + .collect(), + }; + let depth_stencil = depth_stencil_state + .map(conv::map_depth_stencil_state_descriptor) + .unwrap_or_default(); + + let multisampling: Option = if sc == 1 { + None + } else { + Some(hal::pso::Multisampling { + rasterization_samples: sc, + sample_shading: None, + sample_mask: desc.sample_mask as u64, + alpha_coverage: desc.alpha_to_coverage_enabled, + alpha_to_one: false, + }) + }; + + // TODO + let baked_states = hal::pso::BakedStates { + viewport: None, + scissor: None, + blend_color: None, + depth_bounds: None, + }; + + let raw_pipeline = { + let (device_guard, mut token) = hub.devices.read(&mut token); + let device = &device_guard[device_id]; + let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token); + let layout = &pipeline_layout_guard[desc.layout].raw; + let (shader_module_guard, _) = hub.shader_modules.read(&mut token); + + let rp_key = RenderPassKey { + colors: color_states + .iter() + .map(|at| hal::pass::Attachment { + format: Some(conv::map_texture_format(at.format, device.features)), + samples: sc, + ops: hal::pass::AttachmentOps::PRESERVE, + stencil_ops: hal::pass::AttachmentOps::DONT_CARE, + layouts: hal::image::Layout::General .. hal::image::Layout::General, + }) + .collect(), + // We can ignore the resolves as the vulkan specs says: + // As an additional special case, if two render passes have a single subpass, + // they are compatible even if they have different resolve attachment references + // or depth/stencil resolve modes but satisfy the other compatibility conditions. + resolves: ArrayVec::new(), + depth_stencil: depth_stencil_state.map(|at| hal::pass::Attachment { + format: Some(conv::map_texture_format(at.format, device.features)), + samples: sc, + ops: hal::pass::AttachmentOps::PRESERVE, + stencil_ops: hal::pass::AttachmentOps::PRESERVE, + layouts: hal::image::Layout::General .. hal::image::Layout::General, + }), + }; + + let mut render_pass_cache = device.render_passes.lock(); + let main_pass = match render_pass_cache.entry(rp_key) { + Entry::Occupied(e) => e.into_mut(), + Entry::Vacant(e) => { + let color_ids = [ + (0, hal::image::Layout::ColorAttachmentOptimal), + (1, hal::image::Layout::ColorAttachmentOptimal), + (2, hal::image::Layout::ColorAttachmentOptimal), + (3, hal::image::Layout::ColorAttachmentOptimal), + ]; + + let depth_id = ( + desc.color_states_length, + hal::image::Layout::DepthStencilAttachmentOptimal, + ); + + let subpass = hal::pass::SubpassDesc { + colors: &color_ids[.. desc.color_states_length], + depth_stencil: depth_stencil_state.map(|_| &depth_id), + inputs: &[], + resolves: &[], + preserves: &[], + }; + + let pass = unsafe { + device + .raw + .create_render_pass(e.key().all(), &[subpass], &[]) + } + .unwrap(); + e.insert(pass) + } + }; + + let vertex = hal::pso::EntryPoint:: { + entry: unsafe { ffi::CStr::from_ptr(desc.vertex_stage.entry_point) } + .to_str() + .to_owned() + .unwrap(), // TODO + module: &shader_module_guard[desc.vertex_stage.module].raw, + specialization: hal::pso::Specialization::EMPTY, + }; + let fragment = + unsafe { desc.fragment_stage.as_ref() }.map(|stage| hal::pso::EntryPoint:: { + entry: unsafe { ffi::CStr::from_ptr(stage.entry_point) } + .to_str() + .to_owned() + .unwrap(), // TODO + module: &shader_module_guard[stage.module].raw, + specialization: hal::pso::Specialization::EMPTY, + }); + + let shaders = hal::pso::GraphicsShaderSet { + vertex, + hull: None, + domain: None, + geometry: None, + fragment, + }; + + let subpass = hal::pass::Subpass { + index: 0, + main_pass, + }; + + // TODO + let flags = hal::pso::PipelineCreationFlags::empty(); + // TODO + let parent = hal::pso::BasePipeline::None; + + let pipeline_desc = hal::pso::GraphicsPipelineDesc { + shaders, + rasterizer, + vertex_buffers, + attributes, + input_assembler, + blender, + depth_stencil, + multisampling, + baked_states, + layout, + subpass, + flags, + parent, + }; + + // TODO: cache + unsafe { + device + .raw + .create_graphics_pipeline(&pipeline_desc, None) + .unwrap() + } + }; + + let pass_context = RenderPassContext { + colors: color_states.iter().map(|state| state.format).collect(), + resolves: ArrayVec::new(), + depth_stencil: depth_stencil_state.map(|state| state.format), + }; + + let mut flags = pipeline::PipelineFlags::empty(); + for state in color_states { + if state.color_blend.uses_color() | state.alpha_blend.uses_color() { + flags |= pipeline::PipelineFlags::BLEND_COLOR; + } + } + if let Some(ds) = depth_stencil_state { + if ds.needs_stencil_reference() { + flags |= pipeline::PipelineFlags::STENCIL_REFERENCE; + } + } + + let pipeline = pipeline::RenderPipeline { + raw: raw_pipeline, + layout_id: desc.layout, + pass_context, + flags, + index_format: desc.vertex_input.index_format, + vertex_strides, + sample_count: sc, + }; + + hub.render_pipelines + .register_identity(id_in, pipeline, &mut token) + } +} + +impl> Global { + pub fn device_create_compute_pipeline( + &self, + device_id: DeviceId, + desc: &pipeline::ComputePipelineDescriptor, + id_in: F::Input, + ) -> ComputePipelineId { + let hub = B::hub(self); + let mut token = Token::root(); + + let raw_pipeline = { + let (device_guard, mut token) = hub.devices.read(&mut token); + let device = &device_guard[device_id].raw; + let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token); + let layout = &pipeline_layout_guard[desc.layout].raw; + let pipeline_stage = &desc.compute_stage; + let (shader_module_guard, _) = hub.shader_modules.read(&mut token); + + let shader = hal::pso::EntryPoint:: { + entry: unsafe { ffi::CStr::from_ptr(pipeline_stage.entry_point) } + .to_str() + .to_owned() + .unwrap(), // TODO + module: &shader_module_guard[pipeline_stage.module].raw, + specialization: hal::pso::Specialization::EMPTY, + }; + + // TODO + let flags = hal::pso::PipelineCreationFlags::empty(); + // TODO + let parent = hal::pso::BasePipeline::None; + + let pipeline_desc = hal::pso::ComputePipelineDesc { + shader, + layout, + flags, + parent, + }; + + unsafe { + device + .create_compute_pipeline(&pipeline_desc, None) + .unwrap() + } + }; + + let pipeline = pipeline::ComputePipeline { + raw: raw_pipeline, + layout_id: desc.layout, + }; + hub.compute_pipelines + .register_identity(id_in, pipeline, &mut token) + } +} + +impl> Global { + pub fn device_create_swap_chain( + &self, + device_id: DeviceId, + surface_id: SurfaceId, + desc: &swap_chain::SwapChainDescriptor, + ) -> SwapChainId { + log::info!("creating swap chain {:?}", desc); + let hub = B::hub(self); + let mut token = Token::root(); + + let (mut surface_guard, mut token) = self.surfaces.write(&mut token); + let (adapter_guard, mut token) = hub.adapters.read(&mut token); + let (device_guard, mut token) = hub.devices.read(&mut token); + let (mut swap_chain_guard, _) = hub.swap_chains.write(&mut token); + let device = &device_guard[device_id]; + let surface = &mut surface_guard[surface_id]; + + let (caps, formats) = { + let suf = B::get_surface_mut(surface); + let adapter = &adapter_guard[device.adapter_id]; + assert!(suf.supports_queue_family(&adapter.raw.queue_families[0])); + let formats = suf.supported_formats(&adapter.raw.physical_device); + let caps = suf.capabilities(&adapter.raw.physical_device); + (caps, formats) + }; + let num_frames = swap_chain::DESIRED_NUM_FRAMES + .max(*caps.image_count.start()) + .min(*caps.image_count.end()); + let config = desc.to_hal(num_frames, &device.features); + + if let Some(formats) = formats { + assert!( + formats.contains(&config.format), + "Requested format {:?} is not in supported list: {:?}", + config.format, + formats + ); + } + if desc.width < caps.extents.start().width + || desc.width > caps.extents.end().width + || desc.height < caps.extents.start().height + || desc.height > caps.extents.end().height + { + log::warn!( + "Requested size {}x{} is outside of the supported range: {:?}", + desc.width, + desc.height, + caps.extents + ); + } + + unsafe { + B::get_surface_mut(surface) + .configure_swapchain(&device.raw, config) + .unwrap(); + } + + let sc_id = surface_id.to_swap_chain_id(B::VARIANT); + if let Some(sc) = swap_chain_guard.remove(sc_id) { + unsafe { + device.raw.destroy_semaphore(sc.semaphore); + } + } + let swap_chain = swap_chain::SwapChain { + life_guard: LifeGuard::new(), + device_id: Stored { + value: device_id, + ref_count: device.life_guard.ref_count.clone(), + }, + desc: desc.clone(), + num_frames, + semaphore: device.raw.create_semaphore().unwrap(), + acquired_view_id: None, + }; + swap_chain_guard.insert(sc_id, swap_chain); + sc_id + } +} + +impl Global { + pub fn device_poll(&self, device_id: DeviceId, force_wait: bool) { + let hub = B::hub(self); + let callbacks = { + let (device_guard, mut token) = hub.devices.read(&mut Token::root()); + device_guard[device_id].maintain(self, force_wait, &mut token) + }; + Device::::fire_map_callbacks(callbacks); + } +} + +impl> Global { + pub fn device_destroy(&self, device_id: DeviceId) { + let hub = B::hub(self); + let (device, mut token) = hub.devices.unregister(device_id, &mut Token::root()); + device.maintain(self, true, &mut token); + device.com_allocator.destroy(&device.raw); + } +} + +pub type BufferMapReadCallback = + extern "C" fn(status: resource::BufferMapAsyncStatus, data: *const u8, userdata: *mut u8); +pub type BufferMapWriteCallback = + extern "C" fn(status: resource::BufferMapAsyncStatus, data: *mut u8, userdata: *mut u8); + +impl Global { + pub fn buffer_map_async( + &self, + buffer_id: BufferId, + usage: resource::BufferUsage, + operation: resource::BufferMapOperation, + ) { + let hub = B::hub(self); + let mut token = Token::root(); + let (device_guard, mut token) = hub.devices.read(&mut token); + + let (device_id, ref_count) = { + let (mut buffer_guard, _) = hub.buffers.write(&mut token); + let buffer = &mut buffer_guard[buffer_id]; + + if usage.contains(resource::BufferUsage::MAP_READ) { + assert!(buffer.usage.contains(resource::BufferUsage::MAP_READ)); + } + + if usage.contains(resource::BufferUsage::MAP_WRITE) { + assert!(buffer.usage.contains(resource::BufferUsage::MAP_WRITE)); + } + + if buffer.pending_map_operation.is_some() { + operation.call_error(); + return; + } + + buffer.pending_map_operation = Some(operation); + (buffer.device_id.value, buffer.life_guard.ref_count.clone()) + }; + + let device = &device_guard[device_id]; + + device + .trackers + .lock() + .buffers + .change_replace(buffer_id, &ref_count, (), usage); + + device.pending.lock().map(buffer_id, ref_count); + } + + pub fn buffer_unmap(&self, buffer_id: BufferId) { + let hub = B::hub(self); + let mut token = Token::root(); + + let (device_guard, mut token) = hub.devices.read(&mut token); + let (mut buffer_guard, _) = hub.buffers.write(&mut token); + + let buffer = &mut buffer_guard[buffer_id]; + let device_raw = &device_guard[buffer.device_id.value].raw; + + if !buffer.mapped_write_ranges.is_empty() { + unsafe { + device_raw + .flush_mapped_memory_ranges( + buffer + .mapped_write_ranges + .iter() + .map(|r| (buffer.memory.memory(), r.clone())), + ) + .unwrap() + }; + buffer.mapped_write_ranges.clear(); + } + + buffer.memory.unmap(device_raw); + } +} diff --git a/wgpu-native/src/hub.rs b/wgpu-core/src/hub.rs similarity index 75% rename from wgpu-native/src/hub.rs rename to wgpu-core/src/hub.rs index f22a682a6..bf09358e4 100644 --- a/wgpu-native/src/hub.rs +++ b/wgpu-core/src/hub.rs @@ -4,96 +4,77 @@ use crate::{ backend, - id::{Input, Output}, - Adapter, - AdapterId, + binding_model::{BindGroup, BindGroupLayout, PipelineLayout}, + command::{CommandBuffer, ComputePass, RenderPass}, + device::{Device, ShaderModule}, + id::{ + AdapterId, + BindGroupId, + BindGroupLayoutId, + BufferId, + CommandBufferId, + ComputePassId, + ComputePipelineId, + DeviceId, + PipelineLayoutId, + RenderPassId, + RenderPipelineId, + SamplerId, + ShaderModuleId, + SurfaceId, + SwapChainId, + TextureId, + TextureViewId, + TypedId, + }, + instance::{Adapter, Instance, Surface}, + pipeline::{ComputePipeline, RenderPipeline}, + resource::{Buffer, Sampler, Texture, TextureView}, + swap_chain::SwapChain, Backend, - BindGroup, - BindGroupId, - BindGroupLayout, - BindGroupLayoutId, - Buffer, - BufferId, - CommandBuffer, - CommandBufferId, - ComputePass, - ComputePassId, - ComputePipeline, - ComputePipelineId, - Device, - DeviceId, Epoch, Index, - Instance, - PipelineLayout, - PipelineLayoutId, - RenderPass, - RenderPassId, - RenderPipeline, - RenderPipelineId, - Sampler, - SamplerId, - ShaderModule, - ShaderModuleId, - Surface, - SurfaceId, - SwapChain, - SwapChainId, - Texture, - TextureId, - TextureView, - TextureViewId, - TypedId, }; -#[cfg(feature = "local")] -use parking_lot::Mutex; -use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +use parking_lot::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}; use vec_map::VecMap; #[cfg(debug_assertions)] use std::cell::Cell; -#[cfg(feature = "local")] -use std::sync::Arc; use std::{marker::PhantomData, ops}; /// A simple structure to manage identities of objects. #[derive(Debug)] -pub struct IdentityManager { +pub struct IdentityManager { free: Vec, epochs: Vec, - backend: Backend, - phantom: PhantomData, } -impl IdentityManager { - pub fn new(backend: Backend) -> Self { +impl Default for IdentityManager { + fn default() -> Self { IdentityManager { free: Default::default(), epochs: Default::default(), - backend, - phantom: PhantomData, } } } -impl IdentityManager { - pub fn alloc(&mut self) -> I { +impl IdentityManager { + pub fn alloc(&mut self, backend: Backend) -> I { match self.free.pop() { - Some(index) => I::zip(index, self.epochs[index as usize], self.backend), + Some(index) => I::zip(index, self.epochs[index as usize], backend), None => { let epoch = 1; - let id = I::zip(self.epochs.len() as Index, epoch, self.backend); + let id = I::zip(self.epochs.len() as Index, epoch, backend); self.epochs.push(epoch); id } } } - pub fn free(&mut self, id: I) { - let (index, epoch, backend) = id.unzip(); - debug_assert_eq!(backend, self.backend); + pub fn free(&mut self, id: I) { + let (index, epoch, _backend) = id.unzip(); // avoid doing this check in release if cfg!(debug_assertions) { assert!(!self.free.contains(&index)); @@ -271,19 +252,57 @@ impl<'a, T> Drop for Token<'a, T> { } +pub trait IdentityFilter { + type Input: Clone; + fn process(&self, id: Self::Input, backend: Backend) -> I; + fn free(&self, id: I); +} + +impl IdentityFilter for () { + type Input = I; + fn process(&self, id: I, _backend: Backend) -> I { + //debug_assert_eq!(id.unzip().2, backend); + id + } + fn free(&self, _id: I) {} +} + +impl IdentityFilter for Mutex { + type Input = PhantomData; + fn process(&self, _id: Self::Input, backend: Backend) -> I { + self.lock().alloc(backend) + } + fn free(&self, id: I) { + self.lock().free(id) + } +} + +/// Compound trait for all the things a device cares about +/// for the matter of destruction/cleanup. +pub trait AllIdentityFilter: + IdentityFilter + + IdentityFilter + + IdentityFilter + + IdentityFilter + + IdentityFilter +{ +} + +impl AllIdentityFilter for Mutex {} +impl AllIdentityFilter for () {} + + #[derive(Debug)] -pub struct Registry { - #[cfg(feature = "local")] - pub identity: Mutex>, +pub struct Registry { + pub(crate) identity: F, data: RwLock>, backend: Backend, } -impl Registry { +impl Registry { fn new(backend: Backend) -> Self { Registry { - #[cfg(feature = "local")] - identity: Mutex::new(IdentityManager::new(backend)), + identity: F::default(), data: RwLock::new(Storage { map: VecMap::new(), _phantom: PhantomData, @@ -293,44 +312,13 @@ impl Registry { } } -impl Registry { +impl Registry { pub fn register>(&self, id: I, value: T, _token: &mut Token) { debug_assert_eq!(id.unzip().2, self.backend); let old = self.data.write().insert(id, value); assert!(old.is_none()); } - #[cfg(feature = "local")] - pub fn new_identity(&self, _id_in: Input) -> (I, Output) { - let id = self.identity.lock().alloc(); - (id, id) - } - - #[cfg(not(feature = "local"))] - pub fn new_identity(&self, id_in: Input) -> (I, Output) { - //TODO: debug_assert_eq!(self.backend, id_in.backend()); - (id_in, PhantomData) - } - - pub fn register_identity>( - &self, - id_in: Input, - value: T, - token: &mut Token, - ) -> Output { - let (id, output) = self.new_identity(id_in); - self.register(id, value, token); - output - } - - pub fn unregister>(&self, id: I, _token: &mut Token) -> (T, Token) { - let value = self.data.write().remove(id).unwrap(); - //Note: careful about the order here! - #[cfg(feature = "local")] - self.identity.lock().free(id); - (value, Token::new()) - } - pub fn read>( &self, _token: &mut Token, @@ -346,27 +334,47 @@ impl Registry { } } -#[derive(Debug)] -pub struct Hub { - pub adapters: Registry, AdapterId>, - pub devices: Registry, DeviceId>, - pub swap_chains: Registry, SwapChainId>, - pub pipeline_layouts: Registry, PipelineLayoutId>, - pub shader_modules: Registry, ShaderModuleId>, - pub bind_group_layouts: Registry, BindGroupLayoutId>, - pub bind_groups: Registry, BindGroupId>, - pub command_buffers: Registry, CommandBufferId>, - pub render_passes: Registry, RenderPassId>, - pub render_pipelines: Registry, RenderPipelineId>, - pub compute_passes: Registry, ComputePassId>, - pub compute_pipelines: Registry, ComputePipelineId>, - pub buffers: Registry, BufferId>, - pub textures: Registry, TextureId>, - pub texture_views: Registry, TextureViewId>, - pub samplers: Registry, SamplerId>, +impl> Registry { + pub fn register_identity>( + &self, + id_in: F::Input, + value: T, + token: &mut Token, + ) -> I { + let id = self.identity.process(id_in, self.backend); + self.register(id, value, token); + id + } + + pub fn unregister>(&self, id: I, _token: &mut Token) -> (T, Token) { + let value = self.data.write().remove(id).unwrap(); + //Note: careful about the order here! + self.identity.free(id); + (value, Token::new()) + } } -impl Default for Hub { +#[derive(Debug)] +pub struct Hub { + pub adapters: Registry, AdapterId, F>, + pub devices: Registry, DeviceId, F>, + pub swap_chains: Registry, SwapChainId, F>, + pub pipeline_layouts: Registry, PipelineLayoutId, F>, + pub shader_modules: Registry, ShaderModuleId, F>, + pub bind_group_layouts: Registry, BindGroupLayoutId, F>, + pub bind_groups: Registry, BindGroupId, F>, + pub command_buffers: Registry, CommandBufferId, F>, + pub render_passes: Registry, RenderPassId, F>, + pub render_pipelines: Registry, RenderPipelineId, F>, + pub compute_passes: Registry, ComputePassId, F>, + pub compute_pipelines: Registry, ComputePipelineId, F>, + pub buffers: Registry, BufferId, F>, + pub textures: Registry, TextureId, F>, + pub texture_views: Registry, TextureViewId, F>, + pub samplers: Registry, SamplerId, F>, +} + +impl Default for Hub { fn default() -> Self { Hub { adapters: Registry::new(B::VARIANT), @@ -389,7 +397,7 @@ impl Default for Hub { } } -impl Drop for Hub { +impl Drop for Hub { fn drop(&mut self) { use crate::resource::TextureViewInner; use hal::device::Device as _; @@ -398,7 +406,9 @@ impl Drop for Hub { for (_, (sampler, _)) in self.samplers.data.write().map.drain() { unsafe { - devices[sampler.device_id.value].raw.destroy_sampler(sampler.raw); + devices[sampler.device_id.value] + .raw + .destroy_sampler(sampler.raw); } } { @@ -417,16 +427,22 @@ impl Drop for Hub { } for (_, (texture, _)) in self.textures.data.write().map.drain() { unsafe { - devices[texture.device_id.value].raw.destroy_image(texture.raw); + devices[texture.device_id.value] + .raw + .destroy_image(texture.raw); } } for (_, (buffer, _)) in self.buffers.data.write().map.drain() { unsafe { - devices[buffer.device_id.value].raw.destroy_buffer(buffer.raw); + devices[buffer.device_id.value] + .raw + .destroy_buffer(buffer.raw); } } for (_, (command_buffer, _)) in self.command_buffers.data.write().map.drain() { - devices[command_buffer.device_id.value].com_allocator.after_submit(command_buffer, 0); + devices[command_buffer.device_id.value] + .com_allocator + .after_submit(command_buffer, 0); } for (_, (bind_group, _)) in self.bind_groups.data.write().map.drain() { let device = &devices[bind_group.device_id.value]; @@ -451,29 +467,29 @@ impl Drop for Hub { } #[derive(Debug, Default)] -pub struct Hubs { +pub struct Hubs { #[cfg(any( not(any(target_os = "ios", target_os = "macos")), feature = "gfx-backend-vulkan" ))] - vulkan: Hub, + vulkan: Hub, #[cfg(any(target_os = "ios", target_os = "macos"))] - metal: Hub, + metal: Hub, #[cfg(windows)] - dx12: Hub, + dx12: Hub, #[cfg(windows)] - dx11: Hub, + dx11: Hub, } #[derive(Debug)] -pub struct Global { +pub struct Global { pub instance: Instance, - pub surfaces: Registry, - hubs: Hubs, + pub surfaces: Registry, + hubs: Hubs, } -impl Global { - fn new_impl(name: &str) -> Self { +impl Global { + pub fn new(name: &str) -> Self { Global { instance: Instance::new(name, 1), surfaces: Registry::new(Backend::Empty), @@ -481,14 +497,12 @@ impl Global { } } - #[cfg(not(feature = "local"))] - pub fn new(name: &str) -> Self { - Self::new_impl(name) - } - - #[cfg(not(feature = "local"))] pub fn delete(self) { - let Global { mut instance, surfaces, hubs } = self; + let Global { + mut instance, + surfaces, + hubs, + } = self; drop(hubs); // destroy surfaces for (_, (surface, _)) in surfaces.data.write().map.drain() { @@ -497,14 +511,9 @@ impl Global { } } -#[cfg(feature = "local")] -lazy_static::lazy_static! { - pub static ref GLOBAL: Arc = Arc::new(Global::new_impl("wgpu")); -} - pub trait GfxBackend: hal::Backend { const VARIANT: Backend; - fn hub(global: &Global) -> &Hub; + fn hub(global: &Global) -> &Hub; fn get_surface_mut(surface: &mut Surface) -> &mut Self::Surface; } @@ -514,7 +523,7 @@ pub trait GfxBackend: hal::Backend { ))] impl GfxBackend for backend::Vulkan { const VARIANT: Backend = Backend::Vulkan; - fn hub(global: &Global) -> &Hub { + fn hub(global: &Global) -> &Hub { &global.hubs.vulkan } fn get_surface_mut(surface: &mut Surface) -> &mut Self::Surface { @@ -525,7 +534,7 @@ impl GfxBackend for backend::Vulkan { #[cfg(any(target_os = "ios", target_os = "macos"))] impl GfxBackend for backend::Metal { const VARIANT: Backend = Backend::Metal; - fn hub(global: &Global) -> &Hub { + fn hub(global: &Global) -> &Hub { &global.hubs.metal } fn get_surface_mut(surface: &mut Surface) -> &mut Self::Surface { @@ -536,7 +545,7 @@ impl GfxBackend for backend::Metal { #[cfg(windows)] impl GfxBackend for backend::Dx12 { const VARIANT: Backend = Backend::Dx12; - fn hub(global: &Global) -> &Hub { + fn hub(global: &Global) -> &Hub { &global.hubs.dx12 } fn get_surface_mut(surface: &mut Surface) -> &mut Self::Surface { @@ -547,7 +556,7 @@ impl GfxBackend for backend::Dx12 { #[cfg(windows)] impl GfxBackend for backend::Dx11 { const VARIANT: Backend = Backend::Dx11; - fn hub(global: &Global) -> &Hub { + fn hub(global: &Global) -> &Hub { &global.hubs.dx11 } fn get_surface_mut(surface: &mut Surface) -> &mut Self::Surface { diff --git a/wgpu-native/src/id.rs b/wgpu-core/src/id.rs similarity index 69% rename from wgpu-native/src/id.rs rename to wgpu-core/src/id.rs index 403dd2386..d5d609b83 100644 --- a/wgpu-native/src/id.rs +++ b/wgpu-core/src/id.rs @@ -78,42 +78,33 @@ impl TypedId for Id { } } -#[cfg(not(feature = "local"))] -pub type Input = T; -#[cfg(feature = "local")] -pub type Input = PhantomData; -#[cfg(feature = "local")] -pub type Output = T; -#[cfg(not(feature = "local"))] -pub type Output = PhantomData; - -pub type AdapterId = Id>; -pub type DeviceId = Id>; +pub type AdapterId = Id>; +pub type SurfaceId = Id; +// Device +pub type DeviceId = Id>; pub type QueueId = DeviceId; +pub type ShaderModuleId = Id>; // Resource -pub type BufferId = Id>; -pub type TextureViewId = Id>; -pub type TextureId = Id>; -pub type SamplerId = Id>; +pub type BufferId = Id>; +pub type TextureViewId = Id>; +pub type TextureId = Id>; +pub type SamplerId = Id>; // Binding model -pub type BindGroupLayoutId = Id>; -pub type PipelineLayoutId = Id>; -pub type BindGroupId = Id>; +pub type BindGroupLayoutId = Id>; +pub type PipelineLayoutId = Id>; +pub type BindGroupId = Id>; // Pipeline -pub type InputStateId = Id; -pub type ShaderModuleId = Id>; -pub type RenderPipelineId = Id>; -pub type ComputePipelineId = Id>; +pub type RenderPipelineId = Id>; +pub type ComputePipelineId = Id>; // Command -pub type CommandBufferId = Id>; +pub type CommandBufferId = Id>; pub type CommandEncoderId = CommandBufferId; -pub type RenderBundleId = Id>; -pub type RenderPassId = Id>; -pub type ComputePassId = Id>; +pub type RenderBundleId = Id>; +pub type RenderPassId = Id>; +pub type ComputePassId = Id>; // Swap chain -pub type SurfaceId = Id; -pub type SwapChainId = Id>; +pub type SwapChainId = Id>; impl SurfaceId { pub(crate) fn to_swap_chain_id(&self, backend: Backend) -> SwapChainId { diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs new file mode 100644 index 000000000..a8686aeb5 --- /dev/null +++ b/wgpu-core/src/instance.rs @@ -0,0 +1,427 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use crate::{ + backend, + binding_model::MAX_BIND_GROUPS, + device::{Device, BIND_BUFFER_ALIGNMENT}, + hub::{GfxBackend, Global, IdentityFilter, Token}, + id::{AdapterId, DeviceId}, + Backend, +}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +pub use hal::adapter::AdapterInfo; +use hal::{self, adapter::PhysicalDevice as _, queue::QueueFamily as _, Instance as _}; + +use std::ffi::c_void; + +#[derive(Debug)] +pub struct Instance { + #[cfg(any( + not(any(target_os = "ios", target_os = "macos")), + feature = "gfx-backend-vulkan" + ))] + pub vulkan: Option, + #[cfg(any(target_os = "ios", target_os = "macos"))] + pub metal: gfx_backend_metal::Instance, + #[cfg(windows)] + pub dx12: Option, + #[cfg(windows)] + pub dx11: gfx_backend_dx11::Instance, +} + +impl Instance { + pub fn new(name: &str, version: u32) -> Self { + Instance { + #[cfg(any( + not(any(target_os = "ios", target_os = "macos")), + feature = "gfx-backend-vulkan" + ))] + vulkan: gfx_backend_vulkan::Instance::create(name, version).ok(), + #[cfg(any(target_os = "ios", target_os = "macos"))] + metal: gfx_backend_metal::Instance::create(name, version).unwrap(), + #[cfg(windows)] + dx12: gfx_backend_dx12::Instance::create(name, version).ok(), + #[cfg(windows)] + dx11: gfx_backend_dx11::Instance::create(name, version).unwrap(), + } + } + + pub(crate) fn destroy_surface(&mut self, surface: Surface) { + //TODO: fill out the proper destruction once we are on gfx-0.4 + #[cfg(any( + not(any(target_os = "ios", target_os = "macos")), + feature = "gfx-backend-vulkan" + ))] + { + if let Some(_suf) = surface.vulkan { + //self.vulkan.as_mut().unwrap().destroy_surface(suf); + } + } + #[cfg(any(target_os = "ios", target_os = "macos"))] + { + let _ = surface; + //self.metal.destroy_surface(surface.metal); + } + #[cfg(windows)] + { + if let Some(_suf) = surface.dx12 { + //self.dx12.as_mut().unwrap().destroy_surface(suf); + } + //self.dx11.destroy_surface(surface.dx11); + } + } +} + +type GfxSurface = ::Surface; + +#[derive(Debug)] +pub struct Surface { + #[cfg(any( + not(any(target_os = "ios", target_os = "macos")), + feature = "gfx-backend-vulkan" + ))] + pub vulkan: Option>, + #[cfg(any(target_os = "ios", target_os = "macos"))] + pub metal: GfxSurface, + #[cfg(windows)] + pub dx12: Option>, + #[cfg(windows)] + pub dx11: GfxSurface, +} + +#[derive(Debug)] +pub struct Adapter { + pub(crate) raw: hal::adapter::Adapter, +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum PowerPreference { + Default = 0, + LowPower = 1, + HighPerformance = 2, +} + +#[repr(C)] +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct RequestAdapterOptions { + pub power_preference: PowerPreference, +} + +impl Default for RequestAdapterOptions { + fn default() -> Self { + RequestAdapterOptions { + power_preference: PowerPreference::Default, + } + } +} + +#[repr(C)] +#[derive(Clone, Debug, Default)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Extensions { + pub anisotropic_filtering: bool, +} + +#[repr(C)] +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Limits { + pub max_bind_groups: u32, +} + +impl Default for Limits { + fn default() -> Self { + Limits { + max_bind_groups: MAX_BIND_GROUPS as u32, + } + } +} + +#[repr(C)] +#[derive(Clone, Debug, Default)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct DeviceDescriptor { + pub extensions: Extensions, + pub limits: Limits, +} + +pub type RequestAdapterCallback = extern "C" fn(adapter: *const AdapterId, userdata: *mut c_void); + +bitflags::bitflags! { + #[repr(transparent)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct BackendBit: u32 { + const VULKAN = 1 << Backend::Vulkan as u32; + const GL = 1 << Backend::Gl as u32; + const METAL = 1 << Backend::Metal as u32; + const DX12 = 1 << Backend::Dx12 as u32; + const DX11 = 1 << Backend::Dx11 as u32; + /// Vulkan + METAL + DX12 + const PRIMARY = Self::VULKAN.bits | Self::METAL.bits | Self::DX12.bits; + /// OpenGL + DX11 + const SECONDARY = Self::GL.bits | Self::DX11.bits; + } +} + +impl From for BackendBit { + fn from(backend: Backend) -> Self { + BackendBit::from_bits(1 << backend as u32).unwrap() + } +} + +pub enum AdapterInputs<'a, I> { + IdSet(&'a [I], fn(&I) -> Backend), + Mask(BackendBit, fn() -> I), +} + +impl AdapterInputs<'_, I> { + fn find(&self, b: Backend) -> Option { + match *self { + AdapterInputs::IdSet(ids, ref fun) => ids.iter().find(|id| fun(id) == b).cloned(), + AdapterInputs::Mask(bits, ref fun) => { + if bits.contains(b.into()) { + Some(fun()) + } else { + None + } + } + } + } +} + +impl> Global { + pub fn pick_adapter( + &self, + desc: &RequestAdapterOptions, + inputs: AdapterInputs, + ) -> Option { + let instance = &self.instance; + let mut device_types = Vec::new(); + + let id_vulkan = inputs.find(Backend::Vulkan); + let id_metal = inputs.find(Backend::Metal); + let id_dx12 = inputs.find(Backend::Dx12); + let id_dx11 = inputs.find(Backend::Dx11); + + #[cfg(any( + not(any(target_os = "ios", target_os = "macos")), + feature = "gfx-backend-vulkan" + ))] + let mut adapters_vk = match instance.vulkan { + Some(ref inst) if id_vulkan.is_some() => { + let adapters = inst.enumerate_adapters(); + device_types.extend(adapters.iter().map(|ad| ad.info.device_type.clone())); + adapters + } + _ => Vec::new(), + }; + #[cfg(any(target_os = "ios", target_os = "macos"))] + let mut adapters_mtl = if id_metal.is_some() { + let adapters = instance.metal.enumerate_adapters(); + device_types.extend(adapters.iter().map(|ad| ad.info.device_type.clone())); + adapters + } else { + Vec::new() + }; + #[cfg(windows)] + let mut adapters_dx12 = match instance.dx12 { + Some(ref inst) if id_dx12.is_some() => { + let adapters = inst.enumerate_adapters(); + device_types.extend(adapters.iter().map(|ad| ad.info.device_type.clone())); + adapters + } + _ => Vec::new(), + }; + #[cfg(windows)] + let mut adapters_dx11 = if id_dx11.is_some() { + let adapters = instance.dx11.enumerate_adapters(); + device_types.extend(adapters.iter().map(|ad| ad.info.device_type.clone())); + adapters + } else { + Vec::new() + }; + + if device_types.is_empty() { + log::warn!("No adapters are available!"); + return None; + } + + let (mut integrated, mut discrete, mut virt, mut other) = (None, None, None, None); + + for (i, ty) in device_types.into_iter().enumerate() { + match ty { + hal::adapter::DeviceType::IntegratedGpu => { + integrated = integrated.or(Some(i)); + } + hal::adapter::DeviceType::DiscreteGpu => { + discrete = discrete.or(Some(i)); + } + hal::adapter::DeviceType::VirtualGpu => { + virt = virt.or(Some(i)); + } + _ => { + other = other.or(Some(i)); + } + } + } + + let preferred_gpu = match desc.power_preference { + PowerPreference::Default => integrated.or(discrete).or(other).or(virt), + PowerPreference::LowPower => integrated.or(other).or(discrete).or(virt), + PowerPreference::HighPerformance => discrete.or(other).or(integrated).or(virt), + }; + + let mut token = Token::root(); + let mut selected = preferred_gpu.unwrap_or(0); + #[cfg(any( + not(any(target_os = "ios", target_os = "macos")), + feature = "gfx-backend-vulkan" + ))] + { + if selected < adapters_vk.len() { + let adapter = Adapter { + raw: adapters_vk.swap_remove(selected), + }; + log::info!("Adapter Vulkan {:?}", adapter.raw.info); + let id = backend::Vulkan::hub(self).adapters.register_identity( + id_vulkan.unwrap(), + adapter, + &mut token, + ); + return Some(id); + } + selected -= adapters_vk.len(); + } + #[cfg(any(target_os = "ios", target_os = "macos"))] + { + if selected < adapters_mtl.len() { + let adapter = Adapter { + raw: adapters_mtl.swap_remove(selected), + }; + log::info!("Adapter Metal {:?}", adapter.raw.info); + let id = backend::Metal::hub(self).adapters.register_identity( + id_metal.unwrap(), + adapter, + &mut token, + ); + return Some(id); + } + selected -= adapters_mtl.len(); + } + #[cfg(windows)] + { + if selected < adapters_dx12.len() { + let adapter = Adapter { + raw: adapters_dx12.swap_remove(selected), + }; + log::info!("Adapter Dx12 {:?}", adapter.raw.info); + let id = backend::Dx12::hub(self).adapters.register_identity( + id_dx12.unwrap(), + adapter, + &mut token, + ); + return Some(id); + } + selected -= adapters_dx12.len(); + if selected < adapters_dx11.len() { + let adapter = Adapter { + raw: adapters_dx11.swap_remove(selected), + }; + log::info!("Adapter Dx11 {:?}", adapter.raw.info); + let id = backend::Dx11::hub(self).adapters.register_identity( + id_dx11.unwrap(), + adapter, + &mut token, + ); + return Some(id); + } + selected -= adapters_dx11.len(); + } + + let _ = (selected, id_vulkan, id_metal, id_dx12, id_dx11); + unreachable!() + } + + pub fn adapter_get_info(&self, adapter_id: AdapterId) -> AdapterInfo { + let hub = B::hub(self); + let mut token = Token::root(); + let (adapter_guard, _) = hub.adapters.read(&mut token); + let adapter = &adapter_guard[adapter_id]; + adapter.raw.info.clone() + } +} + +impl> Global { + pub fn adapter_request_device( + &self, + adapter_id: AdapterId, + desc: &DeviceDescriptor, + id_in: F::Input, + ) -> DeviceId { + let hub = B::hub(self); + let mut token = Token::root(); + let device = { + let (adapter_guard, _) = hub.adapters.read(&mut token); + let adapter = &adapter_guard[adapter_id].raw; + + let family = adapter + .queue_families + .iter() + .find(|family| family.queue_type().supports_graphics()) + .unwrap(); + let mut gpu = unsafe { + adapter + .physical_device + .open(&[(family, &[1.0])], hal::Features::empty()) + .unwrap() + }; + + let limits = adapter.physical_device.limits(); + assert_eq!( + 0, + BIND_BUFFER_ALIGNMENT % limits.min_storage_buffer_offset_alignment, + "Adapter storage buffer offset alignment not compatible with WGPU" + ); + assert_eq!( + 0, + BIND_BUFFER_ALIGNMENT % limits.min_uniform_buffer_offset_alignment, + "Adapter uniform buffer offset alignment not compatible with WGPU" + ); + if limits.max_bound_descriptor_sets == 0 { + log::warn!("max_bind_groups limit is missing"); + } else { + assert!( + u32::from(limits.max_bound_descriptor_sets) >= desc.limits.max_bind_groups, + "Adapter does not support the requested max_bind_groups" + ); + } + + let mem_props = adapter.physical_device.memory_properties(); + + let supports_texture_d24_s8 = adapter + .physical_device + .format_properties(Some(hal::format::Format::D24UnormS8Uint)) + .optimal_tiling + .contains(hal::format::ImageFeature::DEPTH_STENCIL_ATTACHMENT); + + Device::new( + gpu.device, + adapter_id, + gpu.queue_groups.swap_remove(0), + mem_props, + supports_texture_d24_s8, + desc.limits.max_bind_groups, + ) + }; + + hub.devices.register_identity(id_in, device, &mut token) + } +} diff --git a/wgpu-core/src/lib.rs b/wgpu-core/src/lib.rs new file mode 100644 index 000000000..2d9696556 --- /dev/null +++ b/wgpu-core/src/lib.rs @@ -0,0 +1,221 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +pub mod backend { + #[cfg(windows)] + pub use gfx_backend_dx11::Backend as Dx11; + #[cfg(windows)] + pub use gfx_backend_dx12::Backend as Dx12; + pub use gfx_backend_empty::Backend as Empty; + #[cfg(any(target_os = "ios", target_os = "macos"))] + pub use gfx_backend_metal::Backend as Metal; + #[cfg(any( + not(any(target_os = "ios", target_os = "macos")), + feature = "gfx-backend-vulkan" + ))] + pub use gfx_backend_vulkan::Backend as Vulkan; +} + +pub mod binding_model; +pub mod command; +pub mod conv; +pub mod device; +pub mod hub; +pub mod id; +pub mod instance; +pub mod pipeline; +pub mod resource; +pub mod swap_chain; +pub mod track; + +pub use hal::pso::read_spirv; + +use std::{ + os::raw::c_char, + ptr, + sync::atomic::{AtomicUsize, Ordering}, +}; + +type SubmissionIndex = usize; +type Index = u32; +type Epoch = u32; + +#[repr(u8)] +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum Backend { + Empty = 0, + Vulkan = 1, + Metal = 2, + Dx12 = 3, + Dx11 = 4, + Gl = 5, +} + +pub type BufferAddress = u64; +pub type RawString = *const c_char; + +//TODO: make it private. Currently used for swapchain creation impl. +#[derive(Debug)] +pub struct RefCount(ptr::NonNull); + +unsafe impl Send for RefCount {} +unsafe impl Sync for RefCount {} + +impl RefCount { + const MAX: usize = 1 << 24; + + fn load(&self) -> usize { + unsafe { self.0.as_ref() }.load(Ordering::Acquire) + } +} + +impl Clone for RefCount { + fn clone(&self) -> Self { + let old_size = unsafe { self.0.as_ref() }.fetch_add(1, Ordering::Relaxed); + assert!(old_size < Self::MAX); + RefCount(self.0) + } +} + +impl Drop for RefCount { + fn drop(&mut self) { + if unsafe { self.0.as_ref() }.fetch_sub(1, Ordering::Relaxed) == 1 { + let _ = unsafe { Box::from_raw(self.0.as_ptr()) }; + } + } +} + +#[derive(Debug)] +struct LifeGuard { + ref_count: RefCount, + submission_index: AtomicUsize, +} + +impl LifeGuard { + fn new() -> Self { + let bx = Box::new(AtomicUsize::new(1)); + LifeGuard { + ref_count: RefCount(ptr::NonNull::new(Box::into_raw(bx)).unwrap()), + submission_index: AtomicUsize::new(0), + } + } +} + +#[derive(Clone, Debug)] +struct Stored { + value: T, + ref_count: RefCount, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Color { + pub r: f64, + pub g: f64, + pub b: f64, + pub a: f64, +} + +impl Color { + pub const TRANSPARENT: Self = Color { + r: 0.0, + g: 0.0, + b: 0.0, + a: 0.0, + }; + pub const BLACK: Self = Color { + r: 0.0, + g: 0.0, + b: 0.0, + a: 1.0, + }; + pub const WHITE: Self = Color { + r: 1.0, + g: 1.0, + b: 1.0, + a: 1.0, + }; + pub const RED: Self = Color { + r: 1.0, + g: 0.0, + b: 0.0, + a: 1.0, + }; + pub const GREEN: Self = Color { + r: 0.0, + g: 1.0, + b: 0.0, + a: 1.0, + }; + pub const BLUE: Self = Color { + r: 0.0, + g: 0.0, + b: 1.0, + a: 1.0, + }; +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Origin3d { + pub x: f32, + pub y: f32, + pub z: f32, +} + +impl Origin3d { + pub const ZERO: Self = Origin3d { + x: 0.0, + y: 0.0, + z: 0.0, + }; +} + +impl Default for Origin3d { + fn default() -> Self { + Origin3d::ZERO + } +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Extent3d { + pub width: u32, + pub height: u32, + pub depth: u32, +} + +#[repr(C)] +#[derive(Debug)] +pub struct U32Array { + pub bytes: *const u32, + pub length: usize, +} + +#[derive(Clone, Copy, Debug)] +pub(crate) struct Features { + pub max_bind_groups: u32, + pub supports_texture_d24_s8: bool, +} + +#[macro_export] +macro_rules! gfx_select { + ($id:expr => $global:ident.$method:ident( $($param:expr),+ )) => { + match $id.backend() { + #[cfg(any(not(any(target_os = "ios", target_os = "macos")), feature = "gfx-backend-vulkan"))] + $crate::Backend::Vulkan => $global.$method::<$crate::backend::Vulkan>( $($param),+ ), + #[cfg(any(target_os = "ios", target_os = "macos"))] + $crate::Backend::Metal => $global.$method::<$crate::backend::Metal>( $($param),+ ), + #[cfg(windows)] + $crate::Backend::Dx12 => $global.$method::<$crate::backend::Dx12>( $($param),+ ), + #[cfg(windows)] + $crate::Backend::Dx11 => $global.$method::<$crate::backend::Dx11>( $($param),+ ), + _ => unreachable!() + } + }; +} + +/// Fast hash map used internally. +type FastHashMap = + std::collections::HashMap>; diff --git a/wgpu-native/src/pipeline.rs b/wgpu-core/src/pipeline.rs similarity index 98% rename from wgpu-native/src/pipeline.rs rename to wgpu-core/src/pipeline.rs index 177a165d2..b8ba778dc 100644 --- a/wgpu-native/src/pipeline.rs +++ b/wgpu-core/src/pipeline.rs @@ -4,16 +4,13 @@ use crate::{ device::RenderPassContext, + id::{PipelineLayoutId, ShaderModuleId}, resource, BufferAddress, - PipelineLayoutId, RawString, - ShaderModuleId, U32Array, }; -use bitflags::bitflags; - pub type ShaderLocation = u32; #[repr(C)] @@ -50,7 +47,7 @@ impl Default for BlendOperation { } } -bitflags! { +bitflags::bitflags! { #[repr(transparent)] pub struct ColorWrite: u32 { const RED = 1; @@ -334,7 +331,7 @@ pub struct RenderPipelineDescriptor { pub alpha_to_coverage_enabled: bool, } -bitflags! { +bitflags::bitflags! { #[repr(transparent)] pub struct PipelineFlags: u32 { const BLEND_COLOR = 1; diff --git a/wgpu-native/src/resource.rs b/wgpu-core/src/resource.rs similarity index 98% rename from wgpu-native/src/resource.rs rename to wgpu-core/src/resource.rs index 53a43e2ba..d8f01ee8c 100644 --- a/wgpu-native/src/resource.rs +++ b/wgpu-core/src/resource.rs @@ -3,26 +3,22 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::{ + device::{BufferMapReadCallback, BufferMapWriteCallback}, + id::{DeviceId, SwapChainId, TextureId}, BufferAddress, - BufferMapReadCallback, - BufferMapWriteCallback, - DeviceId, Extent3d, LifeGuard, RefCount, Stored, - SwapChainId, - TextureId, }; -use bitflags::bitflags; use hal; use rendy_memory::MemoryBlock; use smallvec::SmallVec; use std::borrow::Borrow; -bitflags! { +bitflags::bitflags! { #[repr(transparent)] pub struct BufferUsage: u32 { const MAP_READ = 1; @@ -178,7 +174,7 @@ pub enum TextureFormat { Depth24PlusStencil8 = 43, } -bitflags! { +bitflags::bitflags! { #[repr(transparent)] pub struct TextureUsage: u32 { const COPY_SRC = 1; diff --git a/wgpu-core/src/swap_chain.rs b/wgpu-core/src/swap_chain.rs new file mode 100644 index 000000000..412ad99c6 --- /dev/null +++ b/wgpu-core/src/swap_chain.rs @@ -0,0 +1,244 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*! Swap chain management. + + ## Lifecycle + + At the low level, the swap chain is using the new simplified model of gfx-rs. + + A swap chain is a separate object that is backend-dependent but shares the index with + the parent surface, which is backend-independent. This ensures a 1:1 correspondence + between them. + + `get_next_image()` requests a new image from the surface. It becomes a part of + `TextureViewInner::SwapChain` of the resulted view. The view is registered in the HUB + but not in the device tracker. + + The only operation allowed on the view is to be either a color or a resolve attachment. + It can only be used in one command buffer, which needs to be submitted before presenting. + Command buffer tracker knows about the view, but only for the duration of recording. + The view ID is erased from it at the end, so that it's not merged into the device tracker. + + When a swapchain view is used in `begin_render_pass()`, we assume the start and end image + layouts purely based on whether or not this view was used in this command buffer before. + It always starts with `Uninitialized` and ends with `Present`, so that no barriers are + needed when we need to actually present it. + + In `queue_submit()` we make sure to signal the semaphore whenever we render to a swap + chain view. + + In `present()` we return the swap chain image back and wait on the semaphore. +!*/ + +use crate::{ + conv, + hub::{GfxBackend, Global, IdentityFilter, Token}, + id::{DeviceId, SwapChainId, TextureViewId}, + resource, + Extent3d, + Features, + LifeGuard, + Stored, +}; + +use hal::{self, device::Device as _, queue::CommandQueue as _, window::PresentationSurface as _}; + +use smallvec::SmallVec; + + +const FRAME_TIMEOUT_MS: u64 = 1000; +pub const DESIRED_NUM_FRAMES: u32 = 3; + +#[derive(Debug)] +pub struct SwapChain { + pub(crate) life_guard: LifeGuard, + pub(crate) device_id: Stored, + pub(crate) desc: SwapChainDescriptor, + pub(crate) num_frames: hal::window::SwapImageIndex, + pub(crate) semaphore: B::Semaphore, + pub(crate) acquired_view_id: Option>, +} + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub enum PresentMode { + NoVsync = 0, + Vsync = 1, +} + +#[repr(C)] +#[derive(Clone, Debug)] +pub struct SwapChainDescriptor { + pub usage: resource::TextureUsage, + pub format: resource::TextureFormat, + pub width: u32, + pub height: u32, + pub present_mode: PresentMode, +} + +impl SwapChainDescriptor { + pub(crate) fn to_hal( + &self, + num_frames: u32, + features: &Features, + ) -> hal::window::SwapchainConfig { + let mut config = hal::window::SwapchainConfig::new( + self.width, + self.height, + conv::map_texture_format(self.format, *features), + num_frames, + ); + //TODO: check for supported + config.image_usage = conv::map_texture_usage(self.usage, hal::format::Aspects::COLOR); + config.composite_alpha_mode = hal::window::CompositeAlphaMode::OPAQUE; + config.present_mode = match self.present_mode { + PresentMode::NoVsync => hal::window::PresentMode::IMMEDIATE, + PresentMode::Vsync => hal::window::PresentMode::FIFO, + }; + config + } + + pub fn to_texture_desc(&self) -> resource::TextureDescriptor { + resource::TextureDescriptor { + size: Extent3d { + width: self.width, + height: self.height, + depth: 1, + }, + mip_level_count: 1, + array_layer_count: 1, + sample_count: 1, + dimension: resource::TextureDimension::D2, + format: self.format, + usage: self.usage, + } + } +} + +#[repr(C)] +#[derive(Debug)] +pub struct SwapChainOutput { + pub view_id: TextureViewId, +} + +#[derive(Debug)] +pub enum SwapChainGetNextTextureError { + GpuProcessingTimeout, +} + +impl> Global { + pub fn swap_chain_get_next_texture( + &self, + swap_chain_id: SwapChainId, + view_id_in: F::Input, + ) -> Result { + let hub = B::hub(self); + let mut token = Token::root(); + + let (mut surface_guard, mut token) = self.surfaces.write(&mut token); + let surface = &mut surface_guard[swap_chain_id.to_surface_id()]; + let (device_guard, mut token) = hub.devices.read(&mut token); + let (mut swap_chain_guard, mut token) = hub.swap_chains.write(&mut token); + let sc = &mut swap_chain_guard[swap_chain_id]; + let device = &device_guard[sc.device_id.value]; + + let (image, _) = { + let suf = B::get_surface_mut(surface); + match unsafe { suf.acquire_image(FRAME_TIMEOUT_MS * 1_000_000) } { + Ok(surface_image) => surface_image, + Err(hal::window::AcquireError::Timeout) => { + return Err(SwapChainGetNextTextureError::GpuProcessingTimeout); + } + Err(e) => { + log::warn!("acquire_image() failed ({:?}), reconfiguring swapchain", e); + let desc = sc.desc.to_hal(sc.num_frames, &device.features); + unsafe { + suf.configure_swapchain(&device.raw, desc).unwrap(); + suf.acquire_image(FRAME_TIMEOUT_MS * 1_000_000).unwrap() + } + } + } + }; + + let view = resource::TextureView { + inner: resource::TextureViewInner::SwapChain { + image, + source_id: Stored { + value: swap_chain_id, + ref_count: sc.life_guard.ref_count.clone(), + }, + framebuffers: SmallVec::new(), + }, + format: sc.desc.format, + extent: hal::image::Extent { + width: sc.desc.width, + height: sc.desc.height, + depth: 1, + }, + samples: 1, + range: hal::image::SubresourceRange { + aspects: hal::format::Aspects::COLOR, + layers: 0 .. 1, + levels: 0 .. 1, + }, + life_guard: LifeGuard::new(), + }; + let ref_count = view.life_guard.ref_count.clone(); + let view_id = hub + .texture_views + .register_identity(view_id_in, view, &mut token); + + assert!( + sc.acquired_view_id.is_none(), + "Swap chain image is already acquired" + ); + sc.acquired_view_id = Some(Stored { + value: view_id, + ref_count, + }); + + Ok(SwapChainOutput { view_id }) + } + + pub fn swap_chain_present(&self, swap_chain_id: SwapChainId) { + let hub = B::hub(self); + let mut token = Token::root(); + + let (mut surface_guard, mut token) = self.surfaces.write(&mut token); + let surface = &mut surface_guard[swap_chain_id.to_surface_id()]; + let (mut device_guard, mut token) = hub.devices.write(&mut token); + let (mut swap_chain_guard, mut token) = hub.swap_chains.write(&mut token); + let sc = &mut swap_chain_guard[swap_chain_id]; + let device = &mut device_guard[sc.device_id.value]; + + let view_id = sc + .acquired_view_id + .take() + .expect("Swap chain image is not acquired"); + let (view, _) = hub.texture_views.unregister(view_id.value, &mut token); + let (image, framebuffers) = match view.inner { + resource::TextureViewInner::Native { .. } => unreachable!(), + resource::TextureViewInner::SwapChain { + image, + framebuffers, + .. + } => (image, framebuffers), + }; + + let err = unsafe { + let queue = &mut device.queue_group.queues[0]; + queue.present_surface(B::get_surface_mut(surface), image, Some(&sc.semaphore)) + }; + if let Err(e) = err { + log::warn!("present failed: {:?}", e); + } + + for fbo in framebuffers { + unsafe { + device.raw.destroy_framebuffer(fbo); + } + } + } +} diff --git a/wgpu-native/src/track/buffer.rs b/wgpu-core/src/track/buffer.rs similarity index 97% rename from wgpu-native/src/track/buffer.rs rename to wgpu-core/src/track/buffer.rs index 2079b8b3a..ce90022b3 100644 --- a/wgpu-native/src/track/buffer.rs +++ b/wgpu-core/src/track/buffer.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::{PendingTransition, ResourceState, Stitch, Unit}; -use crate::{conv, resource::BufferUsage, BufferId}; +use crate::{conv, id::BufferId, resource::BufferUsage}; use std::ops::Range; //TODO: store `hal::buffer::State` here to avoid extra conversions @@ -106,7 +106,7 @@ impl ResourceState for BufferState { #[cfg(test)] mod test { use super::*; - use crate::{Backend, TypedId}; + use crate::{id::TypedId, Backend}; #[test] fn change() { diff --git a/wgpu-native/src/track/mod.rs b/wgpu-core/src/track/mod.rs similarity index 99% rename from wgpu-native/src/track/mod.rs rename to wgpu-core/src/track/mod.rs index c3046bd31..67aa3b5e7 100644 --- a/wgpu-native/src/track/mod.rs +++ b/wgpu-core/src/track/mod.rs @@ -8,15 +8,12 @@ mod texture; use crate::{ hub::Storage, + id::{BindGroupId, SamplerId, TextureViewId, TypedId}, Backend, - BindGroupId, Epoch, FastHashMap, Index, RefCount, - SamplerId, - TextureViewId, - TypedId, }; use std::{ @@ -212,7 +209,7 @@ impl ResourceTracker { pub fn init( &mut self, id: S::Id, - ref_count: &RefCount, + ref_count: RefCount, selector: S::Selector, default: S::Usage, ) -> bool { @@ -228,7 +225,7 @@ impl ResourceTracker { .insert( index, Resource { - ref_count: ref_count.clone(), + ref_count, state, epoch, }, diff --git a/wgpu-native/src/track/range.rs b/wgpu-core/src/track/range.rs similarity index 100% rename from wgpu-native/src/track/range.rs rename to wgpu-core/src/track/range.rs diff --git a/wgpu-native/src/track/texture.rs b/wgpu-core/src/track/texture.rs similarity index 99% rename from wgpu-native/src/track/texture.rs rename to wgpu-core/src/track/texture.rs index b845e1b25..f46d795ab 100644 --- a/wgpu-native/src/track/texture.rs +++ b/wgpu-core/src/track/texture.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::{range::RangedStates, PendingTransition, ResourceState, Stitch, Unit}; -use crate::{conv, device::MAX_MIP_LEVELS, resource::TextureUsage, TextureId}; +use crate::{conv, device::MAX_MIP_LEVELS, id::TextureId, resource::TextureUsage}; use arrayvec::ArrayVec; diff --git a/wgpu-native/Cargo.toml b/wgpu-native/Cargo.toml index 7409f2968..dc66ec014 100644 --- a/wgpu-native/Cargo.toml +++ b/wgpu-native/Cargo.toml @@ -13,41 +13,19 @@ keywords = ["graphics"] license = "MPL-2.0" [lib] -# Enabling these targets makes our CI bots try to build them and fail atm crate-type = ["lib", "cdylib", "staticlib"] [features] default = [] -local = ["lazy_static", "raw-window-handle"] -metal-auto-capture = ["gfx-backend-metal/auto-capture"] -#NOTE: glutin feature is not stable, use at your own risk -#glutin = ["gfx-backend-gl/glutin"] +#metal-auto-capture = ["gfx-backend-metal/auto-capture"] +vulkan-portability = ["core/gfx-backend-vulkan"] + +[dependencies.core] +path = "../wgpu-core" +package = "wgpu-core" +version = "0.1" [dependencies] -arrayvec = "0.5" -bitflags = "1.0" -copyless = "0.1" -fxhash = "0.2" -lazy_static = { version = "1.1.0", optional = true } -log = "0.4" -hal = { package = "gfx-hal", version = "0.4" } -gfx-backend-empty = { version = "0.4" } +lazy_static = "1.1" parking_lot = "0.9" -raw-window-handle = { version = "0.3", optional = true } -rendy-memory = "0.5" -rendy-descriptor = "0.5" -serde = { version = "1.0", features = ["serde_derive"], optional = true } -smallvec = "0.6" -vec_map = "0.8" - -[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies] -gfx-backend-metal = { version = "0.4" } -gfx-backend-vulkan = { version = "0.4", optional = true } - -[target.'cfg(all(unix, not(target_os = "ios"), not(target_os = "macos")))'.dependencies] -gfx-backend-vulkan = { version = "0.4", features = ["x11"] } - -[target.'cfg(windows)'.dependencies] -gfx-backend-dx12 = { version = "0.4.1" } -gfx-backend-dx11 = { version = "0.4" } -gfx-backend-vulkan = { version = "0.4" } +raw-window-handle = "0.3" diff --git a/wgpu-native/cbindgen.toml b/wgpu-native/cbindgen.toml index 511a50a3b..6dee67146 100644 --- a/wgpu-native/cbindgen.toml +++ b/wgpu-native/cbindgen.toml @@ -22,10 +22,8 @@ prefix = "WGPU" exclude = ["BufferMapResult"] [parse] -parse_deps = false - -[parse.expand] -features = ["local"] +parse_deps = true +include = ["wgpu-core"] [fn] @@ -40,7 +38,3 @@ derive_helper_methods = true bitflags = true [defines] -"feature = local" = "WGPU_LOCAL" -"feature = gfx-backend-gl" = "WGPU_BACKEND_GL" -"feature = winit" = "WGPU_WINIT" -"feature = glutin" = "WGPU_GLUTIN" diff --git a/wgpu-native/src/command.rs b/wgpu-native/src/command.rs new file mode 100644 index 000000000..ed5f87a12 --- /dev/null +++ b/wgpu-native/src/command.rs @@ -0,0 +1,313 @@ +use crate::GLOBAL; + +use core::{gfx_select, id}; + +use std::{marker::PhantomData, slice}; + + +#[no_mangle] +pub extern "C" fn wgpu_command_encoder_finish( + encoder_id: id::CommandEncoderId, + desc: Option<&core::command::CommandBufferDescriptor>, +) -> id::CommandBufferId { + let desc = &desc.cloned().unwrap_or_default(); + gfx_select!(encoder_id => GLOBAL.command_encoder_finish(encoder_id, desc)) +} + +#[no_mangle] +pub extern "C" fn wgpu_command_encoder_copy_buffer_to_buffer( + command_encoder_id: id::CommandEncoderId, + source: id::BufferId, + source_offset: core::BufferAddress, + destination: id::BufferId, + destination_offset: core::BufferAddress, + size: core::BufferAddress, +) { + gfx_select!(command_encoder_id => GLOBAL.command_encoder_copy_buffer_to_buffer( + command_encoder_id, + source, source_offset, + destination, + destination_offset, + size)) +} + +#[no_mangle] +pub extern "C" fn wgpu_command_encoder_copy_buffer_to_texture( + command_encoder_id: id::CommandEncoderId, + source: &core::command::BufferCopyView, + destination: &core::command::TextureCopyView, + copy_size: core::Extent3d, +) { + gfx_select!(command_encoder_id => GLOBAL.command_encoder_copy_buffer_to_texture( + command_encoder_id, + source, + destination, + copy_size)) +} + +#[no_mangle] +pub extern "C" fn wgpu_command_encoder_copy_texture_to_buffer( + command_encoder_id: id::CommandEncoderId, + source: &core::command::TextureCopyView, + destination: &core::command::BufferCopyView, + copy_size: core::Extent3d, +) { + gfx_select!(command_encoder_id => GLOBAL.command_encoder_copy_texture_to_buffer( + command_encoder_id, + source, + destination, + copy_size)) +} + +#[no_mangle] +pub extern "C" fn wgpu_command_encoder_copy_texture_to_texture( + command_encoder_id: id::CommandEncoderId, + source: &core::command::TextureCopyView, + destination: &core::command::TextureCopyView, + copy_size: core::Extent3d, +) { + gfx_select!(command_encoder_id => GLOBAL.command_encoder_copy_texture_to_texture( + command_encoder_id, + source, + destination, + copy_size)) +} + + +#[no_mangle] +pub extern "C" fn wgpu_command_encoder_begin_render_pass( + encoder_id: id::CommandEncoderId, + desc: &core::command::RenderPassDescriptor, +) -> id::RenderPassId { + gfx_select!(encoder_id => GLOBAL.command_encoder_begin_render_pass(encoder_id, desc, PhantomData)) +} + +#[no_mangle] +pub extern "C" fn wgpu_render_pass_end_pass(pass_id: id::RenderPassId) { + gfx_select!(pass_id => GLOBAL.render_pass_end_pass(pass_id)) +} + +#[no_mangle] +pub extern "C" fn wgpu_render_pass_set_bind_group( + pass_id: id::RenderPassId, + index: u32, + bind_group_id: id::BindGroupId, + offsets: *const core::BufferAddress, + offsets_length: usize, +) { + let offsets = if offsets_length != 0 { + unsafe { slice::from_raw_parts(offsets, offsets_length) } + } else { + &[] + }; + gfx_select!(pass_id => GLOBAL.render_pass_set_bind_group(pass_id, index, bind_group_id, offsets)) +} + +#[no_mangle] +pub extern "C" fn wgpu_render_pass_push_debug_group( + _pass_id: id::RenderPassId, + _label: core::RawString, +) { + //TODO +} + +#[no_mangle] +pub extern "C" fn wgpu_render_pass_pop_debug_group(_pass_id: id::RenderPassId) { + //TODO +} + +#[no_mangle] +pub extern "C" fn wgpu_render_pass_insert_debug_marker( + _pass_id: id::RenderPassId, + _label: core::RawString, +) { + //TODO +} + +#[no_mangle] +pub extern "C" fn wgpu_render_pass_set_index_buffer( + pass_id: id::RenderPassId, + buffer_id: id::BufferId, + offset: core::BufferAddress, +) { + gfx_select!(pass_id => GLOBAL.render_pass_set_index_buffer(pass_id, buffer_id, offset)) +} + +#[no_mangle] +pub extern "C" fn wgpu_render_pass_set_vertex_buffers( + pass_id: id::RenderPassId, + start_slot: u32, + buffers: *const id::BufferId, + offsets: *const core::BufferAddress, + length: usize, +) { + let buffers = unsafe { slice::from_raw_parts(buffers, length) }; + let offsets = unsafe { slice::from_raw_parts(offsets, length) }; + gfx_select!(pass_id => GLOBAL.render_pass_set_vertex_buffers(pass_id, start_slot, buffers, offsets)) +} + +#[no_mangle] +pub extern "C" fn wgpu_render_pass_draw( + pass_id: id::RenderPassId, + vertex_count: u32, + instance_count: u32, + first_vertex: u32, + first_instance: u32, +) { + gfx_select!(pass_id => GLOBAL.render_pass_draw(pass_id, vertex_count, instance_count, first_vertex, first_instance)) +} + +#[no_mangle] +pub extern "C" fn wgpu_render_pass_draw_indirect( + pass_id: id::RenderPassId, + indirect_buffer_id: id::BufferId, + indirect_offset: core::BufferAddress, +) { + gfx_select!(pass_id => GLOBAL.render_pass_draw_indirect(pass_id, indirect_buffer_id, indirect_offset)) +} + +#[no_mangle] +pub extern "C" fn wgpu_render_pass_draw_indexed( + pass_id: id::RenderPassId, + index_count: u32, + instance_count: u32, + first_index: u32, + base_vertex: i32, + first_instance: u32, +) { + gfx_select!(pass_id => GLOBAL.render_pass_draw_indexed(pass_id, index_count, instance_count, first_index, base_vertex, first_instance)) +} + +#[no_mangle] +pub extern "C" fn wgpu_render_pass_draw_indexed_indirect( + pass_id: id::RenderPassId, + indirect_buffer_id: id::BufferId, + indirect_offset: core::BufferAddress, +) { + gfx_select!(pass_id => GLOBAL.render_pass_draw_indexed_indirect(pass_id, indirect_buffer_id, indirect_offset)) +} + +#[no_mangle] +pub extern "C" fn wgpu_render_pass_set_pipeline( + pass_id: id::RenderPassId, + pipeline_id: id::RenderPipelineId, +) { + gfx_select!(pass_id => GLOBAL.render_pass_set_pipeline(pass_id, pipeline_id)) +} + +#[no_mangle] +pub extern "C" fn wgpu_render_pass_set_blend_color(pass_id: id::RenderPassId, color: &core::Color) { + gfx_select!(pass_id => GLOBAL.render_pass_set_blend_color(pass_id, color)) +} + +#[no_mangle] +pub extern "C" fn wgpu_render_pass_set_stencil_reference(pass_id: id::RenderPassId, value: u32) { + gfx_select!(pass_id => GLOBAL.render_pass_set_stencil_reference(pass_id, value)) +} + +#[no_mangle] +pub extern "C" fn wgpu_render_pass_set_viewport( + pass_id: id::RenderPassId, + x: f32, + y: f32, + w: f32, + h: f32, + min_depth: f32, + max_depth: f32, +) { + gfx_select!(pass_id => GLOBAL.render_pass_set_viewport(pass_id, x, y, w, h, min_depth, max_depth)) +} + +#[no_mangle] +pub extern "C" fn wgpu_render_pass_set_scissor_rect( + pass_id: id::RenderPassId, + x: u32, + y: u32, + w: u32, + h: u32, +) { + gfx_select!(pass_id => GLOBAL.render_pass_set_scissor_rect(pass_id, x, y, w, h)) +} + +#[no_mangle] +pub extern "C" fn wgpu_render_pass_execute_bundles( + _pass_id: id::RenderPassId, + _bundles: *const id::RenderBundleId, + _bundles_length: usize, +) { + unimplemented!() +} + + +#[no_mangle] +pub extern "C" fn wgpu_command_encoder_begin_compute_pass( + encoder_id: id::CommandEncoderId, + desc: Option<&core::command::ComputePassDescriptor>, +) -> id::ComputePassId { + let desc = &desc.cloned().unwrap_or_default(); + gfx_select!(encoder_id => GLOBAL.command_encoder_begin_compute_pass(encoder_id, desc, PhantomData)) +} + +#[no_mangle] +pub extern "C" fn wgpu_compute_pass_end_pass(pass_id: id::ComputePassId) { + gfx_select!(pass_id => GLOBAL.compute_pass_end_pass(pass_id)) +} + +#[no_mangle] +pub extern "C" fn wgpu_compute_pass_set_bind_group( + pass_id: id::ComputePassId, + index: u32, + bind_group_id: id::BindGroupId, + offsets: *const core::BufferAddress, + offsets_length: usize, +) { + let offsets = if offsets_length != 0 { + unsafe { slice::from_raw_parts(offsets, offsets_length) } + } else { + &[] + }; + gfx_select!(pass_id => GLOBAL.compute_pass_set_bind_group(pass_id, index, bind_group_id, offsets)) +} + +#[no_mangle] +pub extern "C" fn wgpu_compute_pass_push_debug_group( + _pass_id: id::ComputePassId, + _label: core::RawString, +) { + //TODO +} + +#[no_mangle] +pub extern "C" fn wgpu_compute_pass_pop_debug_group(_pass_id: id::ComputePassId) { + //TODO +} + +#[no_mangle] +pub extern "C" fn wgpu_compute_pass_insert_debug_marker( + _pass_id: id::ComputePassId, + _label: core::RawString, +) { + //TODO +} + +#[no_mangle] +pub extern "C" fn wgpu_compute_pass_dispatch(pass_id: id::ComputePassId, x: u32, y: u32, z: u32) { + gfx_select!(pass_id => GLOBAL.compute_pass_dispatch(pass_id, x, y, z)) +} + +#[no_mangle] +pub extern "C" fn wgpu_compute_pass_dispatch_indirect( + pass_id: id::ComputePassId, + indirect_buffer_id: id::BufferId, + indirect_offset: core::BufferAddress, +) { + gfx_select!(pass_id => GLOBAL.compute_pass_dispatch_indirect(pass_id, indirect_buffer_id, indirect_offset)) +} + +#[no_mangle] +pub extern "C" fn wgpu_compute_pass_set_pipeline( + pass_id: id::ComputePassId, + pipeline_id: id::ComputePipelineId, +) { + gfx_select!(pass_id => GLOBAL.compute_pass_set_pipeline(pass_id, pipeline_id)) +} diff --git a/wgpu-native/src/command/compute.rs b/wgpu-native/src/command/compute.rs deleted file mode 100644 index 5278cc299..000000000 --- a/wgpu-native/src/command/compute.rs +++ /dev/null @@ -1,316 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use crate::{ - command::bind::{Binder, LayoutChange}, - device::all_buffer_stages, - hub::{GfxBackend, Global, Token}, - track::{Stitch, TrackerSet}, - BindGroupId, - BufferAddress, - BufferId, - BufferUsage, - CommandBuffer, - CommandBufferId, - ComputePassId, - ComputePipelineId, - RawString, - Stored, - BIND_BUFFER_ALIGNMENT, -}; -#[cfg(feature = "local")] -use crate::{gfx_select, hub::GLOBAL}; - -use hal::{self, command::CommandBuffer as _}; - -use std::iter; -#[cfg(feature = "local")] -use std::slice; - -#[derive(Debug)] -pub struct ComputePass { - raw: B::CommandBuffer, - cmb_id: Stored, - binder: Binder, - trackers: TrackerSet, -} - -impl ComputePass { - pub(crate) fn new( - raw: B::CommandBuffer, - cmb_id: Stored, - trackers: TrackerSet, - max_bind_groups: u32, - ) -> Self { - ComputePass { - raw, - cmb_id, - binder: Binder::new(max_bind_groups), - trackers, - } - } -} - -// Common routines between render/compute - -pub fn compute_pass_end_pass(global: &Global, pass_id: ComputePassId) { - let mut token = Token::root(); - let hub = B::hub(global); - let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token); - let (pass, _) = hub.compute_passes.unregister(pass_id, &mut token); - let cmb = &mut cmb_guard[pass.cmb_id.value]; - - // There are no transitions to be made: we've already been inserting barriers - // into the parent command buffer while recording this compute pass. - cmb.trackers = pass.trackers; - cmb.raw.push(pass.raw); -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_compute_pass_end_pass(pass_id: ComputePassId) { - gfx_select!(pass_id => compute_pass_end_pass(&*GLOBAL, pass_id)) -} - -pub fn compute_pass_set_bind_group( - global: &Global, - pass_id: ComputePassId, - index: u32, - bind_group_id: BindGroupId, - offsets: &[BufferAddress], -) { - let hub = B::hub(global); - let mut token = Token::root(); - - let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token); - let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token); - let (mut pass_guard, mut token) = hub.compute_passes.write(&mut token); - let pass = &mut pass_guard[pass_id]; - - let bind_group = pass - .trackers - .bind_groups - .use_extend(&*bind_group_guard, bind_group_id, (), ()) - .unwrap(); - - assert_eq!(bind_group.dynamic_count, offsets.len()); - - if cfg!(debug_assertions) { - for off in offsets { - assert_eq!( - *off % BIND_BUFFER_ALIGNMENT, - 0, - "Misaligned dynamic buffer offset: {} does not align with {}", - off, - BIND_BUFFER_ALIGNMENT - ); - } - } - - //Note: currently, WebGPU compute passes have synchronization defined - // at a dispatch granularity, so we insert the necessary barriers here. - let (buffer_guard, mut token) = hub.buffers.read(&mut token); - let (texture_guard, _) = hub.textures.read(&mut token); - - log::trace!( - "Encoding barriers on binding of {:?} in pass {:?}", - bind_group_id, - pass_id - ); - CommandBuffer::insert_barriers( - &mut pass.raw, - &mut pass.trackers, - &bind_group.used, - Stitch::Last, - &*buffer_guard, - &*texture_guard, - ); - - if let Some((pipeline_layout_id, follow_up_sets, follow_up_offsets)) = pass - .binder - .provide_entry(index as usize, bind_group_id, bind_group, offsets) - { - let bind_groups = iter::once(bind_group.raw.raw()) - .chain(follow_up_sets.map(|bg_id| bind_group_guard[bg_id].raw.raw())); - unsafe { - pass.raw.bind_compute_descriptor_sets( - &pipeline_layout_guard[pipeline_layout_id].raw, - index as usize, - bind_groups, - offsets - .iter() - .chain(follow_up_offsets) - .map(|&off| off as hal::command::DescriptorSetOffset), - ); - } - }; -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_compute_pass_set_bind_group( - pass_id: ComputePassId, - index: u32, - bind_group_id: BindGroupId, - offsets: *const BufferAddress, - offsets_length: usize, -) { - let offsets = if offsets_length != 0 { - unsafe { slice::from_raw_parts(offsets, offsets_length) } - } else { - &[] - }; - gfx_select!(pass_id => compute_pass_set_bind_group(&*GLOBAL, pass_id, index, bind_group_id, offsets)) -} - -#[no_mangle] -pub extern "C" fn wgpu_compute_pass_push_debug_group(_pass_id: ComputePassId, _label: RawString) { - //TODO -} - -#[no_mangle] -pub extern "C" fn wgpu_compute_pass_pop_debug_group(_pass_id: ComputePassId) { - //TODO -} - -#[no_mangle] -pub extern "C" fn wgpu_compute_pass_insert_debug_marker( - _pass_id: ComputePassId, - _label: RawString, -) { - //TODO -} - -// Compute-specific routines - -pub fn compute_pass_dispatch( - global: &Global, - pass_id: ComputePassId, - x: u32, - y: u32, - z: u32, -) { - let hub = B::hub(global); - let mut token = Token::root(); - let (mut pass_guard, _) = hub.compute_passes.write(&mut token); - unsafe { - pass_guard[pass_id].raw.dispatch([x, y, z]); - } -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_compute_pass_dispatch(pass_id: ComputePassId, x: u32, y: u32, z: u32) { - gfx_select!(pass_id => compute_pass_dispatch(&*GLOBAL, pass_id, x, y, z)) -} - -pub fn compute_pass_dispatch_indirect( - global: &Global, - pass_id: ComputePassId, - indirect_buffer_id: BufferId, - indirect_offset: BufferAddress, -) { - let hub = B::hub(global); - let mut token = Token::root(); - let (buffer_guard, _) = hub.buffers.read(&mut token); - let (mut pass_guard, _) = hub.compute_passes.write(&mut token); - let pass = &mut pass_guard[pass_id]; - - let (src_buffer, src_pending) = pass.trackers.buffers.use_replace( - &*buffer_guard, - indirect_buffer_id, - (), - BufferUsage::INDIRECT, - ); - assert!(src_buffer.usage.contains(BufferUsage::INDIRECT)); - - let barriers = src_pending.map(|pending| hal::memory::Barrier::Buffer { - states: pending.to_states(), - target: &src_buffer.raw, - families: None, - range: None .. None, - }); - - unsafe { - pass.raw.pipeline_barrier( - all_buffer_stages() .. all_buffer_stages(), - hal::memory::Dependencies::empty(), - barriers, - ); - pass.raw.dispatch_indirect(&src_buffer.raw, indirect_offset); - } -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_compute_pass_dispatch_indirect( - pass_id: ComputePassId, - indirect_buffer_id: BufferId, - indirect_offset: BufferAddress, -) { - gfx_select!(pass_id => compute_pass_dispatch_indirect(&*GLOBAL, pass_id, indirect_buffer_id, indirect_offset)) -} - -pub fn compute_pass_set_pipeline( - global: &Global, - pass_id: ComputePassId, - pipeline_id: ComputePipelineId, -) { - let hub = B::hub(global); - let mut token = Token::root(); - let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token); - let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token); - let (mut pass_guard, mut token) = hub.compute_passes.write(&mut token); - let pass = &mut pass_guard[pass_id]; - let (pipeline_guard, _) = hub.compute_pipelines.read(&mut token); - let pipeline = &pipeline_guard[pipeline_id]; - - unsafe { - pass.raw.bind_compute_pipeline(&pipeline.raw); - } - - // Rebind resources - if pass.binder.pipeline_layout_id != Some(pipeline.layout_id.clone()) { - let pipeline_layout = &pipeline_layout_guard[pipeline.layout_id]; - pass.binder.pipeline_layout_id = Some(pipeline.layout_id.clone()); - pass.binder - .reset_expectations(pipeline_layout.bind_group_layout_ids.len()); - let mut is_compatible = true; - - for (index, (entry, &bgl_id)) in pass - .binder - .entries - .iter_mut() - .zip(&pipeline_layout.bind_group_layout_ids) - .enumerate() - { - match entry.expect_layout(bgl_id) { - LayoutChange::Match(bg_id, offsets) if is_compatible => { - let desc_set = bind_group_guard[bg_id].raw.raw(); - unsafe { - pass.raw.bind_compute_descriptor_sets( - &pipeline_layout.raw, - index, - iter::once(desc_set), - offsets.iter().map(|offset| *offset as u32), - ); - } - } - LayoutChange::Match(..) | LayoutChange::Unchanged => {} - LayoutChange::Mismatch => { - is_compatible = false; - } - } - } - } -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_compute_pass_set_pipeline( - pass_id: ComputePassId, - pipeline_id: ComputePipelineId, -) { - gfx_select!(pass_id => compute_pass_set_pipeline(&*GLOBAL, pass_id, pipeline_id)) -} diff --git a/wgpu-native/src/command/mod.rs b/wgpu-native/src/command/mod.rs deleted file mode 100644 index efa2d9956..000000000 --- a/wgpu-native/src/command/mod.rs +++ /dev/null @@ -1,769 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -mod allocator; -mod bind; -mod compute; -mod render; -mod transfer; - -pub(crate) use self::allocator::CommandAllocator; -pub use self::compute::*; -pub use self::render::*; -pub use self::transfer::*; - -use crate::{ - conv, - device::{ - all_buffer_stages, - all_image_stages, - FramebufferKey, - RenderPassContext, - RenderPassKey, - }, - hub::{GfxBackend, Global, Storage, Token}, - id::{Input, Output}, - resource::TextureViewInner, - track::{Stitch, TrackerSet}, - Buffer, - BufferId, - Color, - CommandBufferId, - CommandEncoderId, - ComputePassId, - DeviceId, - Features, - LifeGuard, - RenderPassId, - Stored, - Texture, - TextureId, - TextureUsage, - TextureViewId, -}; -#[cfg(feature = "local")] -use crate::{gfx_select, hub::GLOBAL}; - -use arrayvec::ArrayVec; -use hal::{adapter::PhysicalDevice as _, command::CommandBuffer as _, device::Device as _}; - -#[cfg(feature = "local")] -use std::marker::PhantomData; -use std::{borrow::Borrow, collections::hash_map::Entry, iter, mem, ptr, slice, thread::ThreadId}; - - -pub struct RenderBundle { - _raw: B::CommandBuffer, -} - -#[repr(C)] -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -pub enum LoadOp { - Clear = 0, - Load = 1, -} - -#[repr(C)] -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -pub enum StoreOp { - Clear = 0, - Store = 1, -} - -#[repr(C)] -#[derive(Debug)] -pub struct RenderPassColorAttachmentDescriptor { - pub attachment: TextureViewId, - pub resolve_target: *const TextureViewId, - pub load_op: LoadOp, - pub store_op: StoreOp, - pub clear_color: Color, -} - -#[repr(C)] -#[derive(Debug)] -pub struct RenderPassDepthStencilAttachmentDescriptor { - pub attachment: T, - pub depth_load_op: LoadOp, - pub depth_store_op: StoreOp, - pub clear_depth: f32, - pub stencil_load_op: LoadOp, - pub stencil_store_op: StoreOp, - pub clear_stencil: u32, -} - -#[repr(C)] -#[derive(Debug)] -pub struct RenderPassDescriptor { - pub color_attachments: *const RenderPassColorAttachmentDescriptor, - pub color_attachments_length: usize, - pub depth_stencil_attachment: *const RenderPassDepthStencilAttachmentDescriptor, -} - -#[repr(C)] -#[derive(Clone, Debug, Default)] -pub struct ComputePassDescriptor { - pub todo: u32, -} - -#[derive(Debug)] -pub struct CommandBuffer { - pub(crate) raw: Vec, - is_recording: bool, - recorded_thread_id: ThreadId, - pub(crate) device_id: Stored, - pub(crate) life_guard: LifeGuard, - pub(crate) trackers: TrackerSet, - pub(crate) used_swap_chain: Option<(Stored, B::Framebuffer)>, - pub(crate) features: Features, -} - -impl CommandBuffer { - pub(crate) fn insert_barriers( - raw: &mut B::CommandBuffer, - base: &mut TrackerSet, - head: &TrackerSet, - stitch: Stitch, - buffer_guard: &Storage, BufferId>, - texture_guard: &Storage, TextureId>, - ) { - log::trace!("\tstitch {:?}", stitch); - debug_assert_eq!(B::VARIANT, base.backend()); - debug_assert_eq!(B::VARIANT, head.backend()); - - let buffer_barriers = base - .buffers - .merge_replace(&head.buffers, stitch) - .map(|pending| { - log::trace!("\tbuffer -> {:?}", pending); - hal::memory::Barrier::Buffer { - states: pending.to_states(), - target: &buffer_guard[pending.id].raw, - range: None .. None, - families: None, - } - }); - let texture_barriers = base - .textures - .merge_replace(&head.textures, stitch) - .map(|pending| { - log::trace!("\ttexture -> {:?}", pending); - hal::memory::Barrier::Image { - states: pending.to_states(), - target: &texture_guard[pending.id].raw, - range: pending.selector, - families: None, - } - }); - base.views.merge_extend(&head.views).unwrap(); - base.bind_groups.merge_extend(&head.bind_groups).unwrap(); - base.samplers.merge_extend(&head.samplers).unwrap(); - - let stages = all_buffer_stages() | all_image_stages(); - unsafe { - raw.pipeline_barrier( - stages .. stages, - hal::memory::Dependencies::empty(), - buffer_barriers.chain(texture_barriers), - ); - } - } -} - -#[repr(C)] -#[derive(Clone, Debug, Default)] -pub struct CommandEncoderDescriptor { - // MSVC doesn't allow zero-sized structs - // We can remove this when we actually have a field - pub todo: u32, -} - -#[repr(C)] -#[derive(Clone, Debug, Default)] -pub struct CommandBufferDescriptor { - pub todo: u32, -} - -pub fn command_encoder_finish( - global: &Global, - encoder_id: CommandEncoderId, - _desc: &CommandBufferDescriptor, -) -> CommandBufferId { - let hub = B::hub(global); - let mut token = Token::root(); - //TODO: actually close the last recorded command buffer - let (mut comb_guard, _) = hub.command_buffers.write(&mut token); - let comb = &mut comb_guard[encoder_id]; - assert!(comb.is_recording); - comb.is_recording = false; - // stop tracking the swapchain image, if used - if let Some((ref view_id, _)) = comb.used_swap_chain { - comb.trackers.views.remove(view_id.value); - } - encoder_id -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_command_encoder_finish( - encoder_id: CommandEncoderId, - desc: Option<&CommandBufferDescriptor>, -) -> CommandBufferId { - let desc = &desc.cloned().unwrap_or_default(); - gfx_select!(encoder_id => command_encoder_finish(&*GLOBAL, encoder_id, desc)) -} - -pub fn command_encoder_begin_render_pass( - global: &Global, - encoder_id: CommandEncoderId, - desc: &RenderPassDescriptor, - id_in: Input, -) -> Output { - let hub = B::hub(global); - let mut token = Token::root(); - - let (adapter_guard, mut token) = hub.adapters.read(&mut token); - let (device_guard, mut token) = hub.devices.read(&mut token); - let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token); - let cmb = &mut cmb_guard[encoder_id]; - let device = &device_guard[cmb.device_id.value]; - - let limits = adapter_guard[device.adapter_id] - .raw - .physical_device - .limits(); - let samples_count_limit = limits.framebuffer_color_sample_counts; - - let mut current_comb = device.com_allocator.extend(cmb); - unsafe { - current_comb.begin( - hal::command::CommandBufferFlags::ONE_TIME_SUBMIT, - hal::command::CommandBufferInheritanceInfo::default(), - ); - } - - let pass = { - let (_, mut token) = hub.buffers.read(&mut token); //skip token - let (texture_guard, mut token) = hub.textures.read(&mut token); - let (view_guard, _) = hub.texture_views.read(&mut token); - - let mut extent = None; - let mut barriers = Vec::new(); - let mut used_swap_chain_image = None::>; - - let color_attachments = - unsafe { slice::from_raw_parts(desc.color_attachments, desc.color_attachments_length) }; - let depth_stencil_attachment = unsafe { desc.depth_stencil_attachment.as_ref() }; - - let sample_count = color_attachments - .get(0) - .map(|at| view_guard[at.attachment].samples) - .unwrap_or(1); - assert!( - sample_count & samples_count_limit != 0, - "Attachment sample_count must be supported by physical device limits" - ); - - log::trace!( - "Encoding render pass begin in command buffer {:?}", - encoder_id - ); - let rp_key = { - let trackers = &mut cmb.trackers; - - let depth_stencil = depth_stencil_attachment.map(|at| { - let view = trackers - .views - .use_extend(&*view_guard, at.attachment, (), ()) - .unwrap(); - if let Some(ex) = extent { - assert_eq!(ex, view.extent); - } else { - extent = Some(view.extent); - } - let texture_id = match view.inner { - TextureViewInner::Native { ref source_id, .. } => source_id.value, - TextureViewInner::SwapChain { .. } => { - panic!("Unexpected depth/stencil use of swapchain image!") - } - }; - - let texture = &texture_guard[texture_id]; - assert!(texture.usage.contains(TextureUsage::OUTPUT_ATTACHMENT)); - - let old_layout = match trackers.textures.query(texture_id, view.range.clone()) { - Some(usage) => { - conv::map_texture_state( - usage, - hal::format::Aspects::DEPTH | hal::format::Aspects::STENCIL, - ) - .1 - } - None => { - // Required sub-resources have inconsistent states, we need to - // issue individual barriers instead of relying on the render pass. - let pending = trackers.textures.change_replace( - texture_id, - &texture.life_guard.ref_count, - view.range.clone(), - TextureUsage::OUTPUT_ATTACHMENT, - ); - - barriers.extend(pending.map(|pending| { - log::trace!("\tdepth-stencil {:?}", pending); - hal::memory::Barrier::Image { - states: pending.to_states(), - target: &texture.raw, - families: None, - range: pending.selector, - } - })); - hal::image::Layout::DepthStencilAttachmentOptimal - } - }; - hal::pass::Attachment { - format: Some(conv::map_texture_format(view.format, device.features)), - samples: view.samples, - ops: conv::map_load_store_ops(at.depth_load_op, at.depth_store_op), - stencil_ops: conv::map_load_store_ops(at.stencil_load_op, at.stencil_store_op), - layouts: old_layout .. hal::image::Layout::DepthStencilAttachmentOptimal, - } - }); - - let mut colors = ArrayVec::new(); - let mut resolves = ArrayVec::new(); - - for at in color_attachments { - let view = &view_guard[at.attachment]; - if let Some(ex) = extent { - assert_eq!(ex, view.extent); - } else { - extent = Some(view.extent); - } - assert_eq!( - view.samples, sample_count, - "All attachments must have the same sample_count" - ); - let first_use = - trackers - .views - .init(at.attachment, &view.life_guard.ref_count, (), ()); - - let layouts = match view.inner { - TextureViewInner::Native { ref source_id, .. } => { - let texture = &texture_guard[source_id.value]; - assert!(texture.usage.contains(TextureUsage::OUTPUT_ATTACHMENT)); - - let old_layout = - match trackers.textures.query(source_id.value, view.range.clone()) { - Some(usage) => { - conv::map_texture_state(usage, hal::format::Aspects::COLOR).1 - } - None => { - // Required sub-resources have inconsistent states, we need to - // issue individual barriers instead of relying on the render pass. - let pending = trackers.textures.change_replace( - source_id.value, - &texture.life_guard.ref_count, - view.range.clone(), - TextureUsage::OUTPUT_ATTACHMENT, - ); - barriers.extend(pending.map(|pending| { - log::trace!("\tcolor {:?}", pending); - hal::memory::Barrier::Image { - states: pending.to_states(), - target: &texture.raw, - families: None, - range: pending.selector, - } - })); - hal::image::Layout::ColorAttachmentOptimal - } - }; - old_layout .. hal::image::Layout::ColorAttachmentOptimal - } - TextureViewInner::SwapChain { .. } => { - if let Some((ref view_id, _)) = cmb.used_swap_chain { - assert_eq!(view_id.value, at.attachment); - } else { - assert!(used_swap_chain_image.is_none()); - used_swap_chain_image = Some(Stored { - value: at.attachment, - ref_count: view.life_guard.ref_count.clone(), - }); - } - - let end = hal::image::Layout::Present; - let start = if first_use { - hal::image::Layout::Undefined - } else { - end - }; - start .. end - } - }; - - colors.push(hal::pass::Attachment { - format: Some(conv::map_texture_format(view.format, device.features)), - samples: view.samples, - ops: conv::map_load_store_ops(at.load_op, at.store_op), - stencil_ops: hal::pass::AttachmentOps::DONT_CARE, - layouts, - }); - } - - for &resolve_target in color_attachments - .iter() - .flat_map(|at| unsafe { at.resolve_target.as_ref() }) - { - let view = &view_guard[resolve_target]; - assert_eq!(extent, Some(view.extent)); - assert_eq!( - view.samples, 1, - "All resolve_targets must have a sample_count of 1" - ); - let first_use = - trackers - .views - .init(resolve_target, &view.life_guard.ref_count, (), ()); - - let layouts = match view.inner { - TextureViewInner::Native { ref source_id, .. } => { - let texture = &texture_guard[source_id.value]; - assert!(texture.usage.contains(TextureUsage::OUTPUT_ATTACHMENT)); - - let old_layout = - match trackers.textures.query(source_id.value, view.range.clone()) { - Some(usage) => { - conv::map_texture_state(usage, hal::format::Aspects::COLOR).1 - } - None => { - // Required sub-resources have inconsistent states, we need to - // issue individual barriers instead of relying on the render pass. - let pending = trackers.textures.change_replace( - source_id.value, - &texture.life_guard.ref_count, - view.range.clone(), - TextureUsage::OUTPUT_ATTACHMENT, - ); - barriers.extend(pending.map(|pending| { - log::trace!("\tresolve {:?}", pending); - hal::memory::Barrier::Image { - states: pending.to_states(), - target: &texture.raw, - families: None, - range: pending.selector, - } - })); - hal::image::Layout::ColorAttachmentOptimal - } - }; - old_layout .. hal::image::Layout::ColorAttachmentOptimal - } - TextureViewInner::SwapChain { .. } => { - if let Some((ref view_id, _)) = cmb.used_swap_chain { - assert_eq!(view_id.value, resolve_target); - } else { - assert!(used_swap_chain_image.is_none()); - used_swap_chain_image = Some(Stored { - value: resolve_target, - ref_count: view.life_guard.ref_count.clone(), - }); - } - - let end = hal::image::Layout::Present; - let start = if first_use { - hal::image::Layout::Undefined - } else { - end - }; - start .. end - } - }; - - resolves.push(hal::pass::Attachment { - format: Some(conv::map_texture_format(view.format, device.features)), - samples: view.samples, - ops: hal::pass::AttachmentOps::new( - hal::pass::AttachmentLoadOp::DontCare, - hal::pass::AttachmentStoreOp::Store, - ), - stencil_ops: hal::pass::AttachmentOps::DONT_CARE, - layouts, - }); - } - - RenderPassKey { - colors, - resolves, - depth_stencil, - } - }; - - if !barriers.is_empty() { - unsafe { - current_comb.pipeline_barrier( - all_image_stages() .. all_image_stages(), - hal::memory::Dependencies::empty(), - barriers, - ); - } - } - - let mut render_pass_cache = device.render_passes.lock(); - let render_pass = match render_pass_cache.entry(rp_key.clone()) { - Entry::Occupied(e) => e.into_mut(), - Entry::Vacant(e) => { - let color_ids = [ - (0, hal::image::Layout::ColorAttachmentOptimal), - (1, hal::image::Layout::ColorAttachmentOptimal), - (2, hal::image::Layout::ColorAttachmentOptimal), - (3, hal::image::Layout::ColorAttachmentOptimal), - ]; - - let mut resolve_ids = ArrayVec::<[_; crate::device::MAX_COLOR_TARGETS]>::new(); - let mut attachment_index = color_attachments.len(); - if color_attachments - .iter() - .any(|at| at.resolve_target != ptr::null()) - { - for (i, at) in color_attachments.iter().enumerate() { - if at.resolve_target == ptr::null() { - resolve_ids.push(( - hal::pass::ATTACHMENT_UNUSED, - hal::image::Layout::ColorAttachmentOptimal, - )); - } else { - let sample_count_check = - view_guard[color_attachments[i].attachment].samples; - assert!(sample_count_check > 1, "RenderPassColorAttachmentDescriptor with a resolve_target must have an attachment with sample_count > 1"); - resolve_ids.push(( - attachment_index, - hal::image::Layout::ColorAttachmentOptimal, - )); - attachment_index += 1; - } - } - } - - let depth_id = ( - attachment_index, - hal::image::Layout::DepthStencilAttachmentOptimal, - ); - - let subpass = hal::pass::SubpassDesc { - colors: &color_ids[.. color_attachments.len()], - resolves: &resolve_ids, - depth_stencil: depth_stencil_attachment.map(|_| &depth_id), - inputs: &[], - preserves: &[], - }; - - let pass = unsafe { - device - .raw - .create_render_pass(e.key().all(), &[subpass], &[]) - } - .unwrap(); - e.insert(pass) - } - }; - - let mut framebuffer_cache; - let fb_key = FramebufferKey { - colors: color_attachments.iter().map(|at| at.attachment).collect(), - resolves: color_attachments - .iter() - .filter_map(|at| unsafe { at.resolve_target.as_ref() }.cloned()) - .collect(), - depth_stencil: depth_stencil_attachment.map(|at| at.attachment), - }; - - let framebuffer = match used_swap_chain_image.take() { - Some(view_id) => { - assert!(cmb.used_swap_chain.is_none()); - // Always create a new framebuffer and delete it after presentation. - let attachments = fb_key.all().map(|&id| match view_guard[id].inner { - TextureViewInner::Native { ref raw, .. } => raw, - TextureViewInner::SwapChain { ref image, .. } => Borrow::borrow(image), - }); - let framebuffer = unsafe { - device - .raw - .create_framebuffer(&render_pass, attachments, extent.unwrap()) - .unwrap() - }; - cmb.used_swap_chain = Some((view_id, framebuffer)); - &mut cmb.used_swap_chain.as_mut().unwrap().1 - } - None => { - // Cache framebuffers by the device. - framebuffer_cache = device.framebuffers.lock(); - match framebuffer_cache.entry(fb_key) { - Entry::Occupied(e) => e.into_mut(), - Entry::Vacant(e) => { - let fb = { - let attachments = e.key().all().map(|&id| match view_guard[id].inner { - TextureViewInner::Native { ref raw, .. } => raw, - TextureViewInner::SwapChain { ref image, .. } => { - Borrow::borrow(image) - } - }); - unsafe { - device.raw.create_framebuffer( - &render_pass, - attachments, - extent.unwrap(), - ) - } - .unwrap() - }; - e.insert(fb) - } - } - } - }; - - let rect = { - let ex = extent.unwrap(); - hal::pso::Rect { - x: 0, - y: 0, - w: ex.width as _, - h: ex.height as _, - } - }; - - let clear_values = color_attachments - .iter() - .zip(&rp_key.colors) - .flat_map(|(at, key)| { - match at.load_op { - LoadOp::Load => None, - LoadOp::Clear => { - use hal::format::ChannelType; - //TODO: validate sign/unsign and normalized ranges of the color values - let value = match key.format.unwrap().base_format().1 { - ChannelType::Unorm - | ChannelType::Snorm - | ChannelType::Ufloat - | ChannelType::Sfloat - | ChannelType::Uscaled - | ChannelType::Sscaled - | ChannelType::Srgb => hal::command::ClearColor { - float32: conv::map_color_f32(&at.clear_color), - }, - ChannelType::Sint => hal::command::ClearColor { - sint32: conv::map_color_i32(&at.clear_color), - }, - ChannelType::Uint => hal::command::ClearColor { - uint32: conv::map_color_u32(&at.clear_color), - }, - }; - Some(hal::command::ClearValue { color: value }) - } - } - }) - .chain(depth_stencil_attachment.and_then(|at| { - match (at.depth_load_op, at.stencil_load_op) { - (LoadOp::Load, LoadOp::Load) => None, - (LoadOp::Clear, _) | (_, LoadOp::Clear) => { - let value = hal::command::ClearDepthStencil { - depth: at.clear_depth, - stencil: at.clear_stencil, - }; - Some(hal::command::ClearValue { - depth_stencil: value, - }) - } - } - })); - - unsafe { - current_comb.begin_render_pass( - render_pass, - framebuffer, - rect, - clear_values, - hal::command::SubpassContents::Inline, - ); - current_comb.set_scissors(0, iter::once(&rect)); - current_comb.set_viewports( - 0, - iter::once(hal::pso::Viewport { - rect, - depth: 0.0 .. 1.0, - }), - ); - } - - let context = RenderPassContext { - colors: color_attachments - .iter() - .map(|at| view_guard[at.attachment].format) - .collect(), - resolves: color_attachments - .iter() - .filter_map(|at| unsafe { at.resolve_target.as_ref() }) - .map(|resolve| view_guard[*resolve].format) - .collect(), - depth_stencil: depth_stencil_attachment.map(|at| view_guard[at.attachment].format), - }; - - RenderPass::new( - current_comb, - Stored { - value: encoder_id, - ref_count: cmb.life_guard.ref_count.clone(), - }, - context, - sample_count, - cmb.features.max_bind_groups, - ) - }; - hub.render_passes.register_identity(id_in, pass, &mut token) -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_command_encoder_begin_render_pass( - encoder_id: CommandEncoderId, - desc: &RenderPassDescriptor, -) -> RenderPassId { - gfx_select!(encoder_id => command_encoder_begin_render_pass(&*GLOBAL, encoder_id, desc, PhantomData)) -} - -pub fn command_encoder_begin_compute_pass( - global: &Global, - encoder_id: CommandEncoderId, - _desc: &ComputePassDescriptor, - id_in: Input, -) -> Output { - let hub = B::hub(global); - let mut token = Token::root(); - - let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token); - let cmb = &mut cmb_guard[encoder_id]; - - let raw = cmb.raw.pop().unwrap(); - let trackers = mem::replace(&mut cmb.trackers, TrackerSet::new(encoder_id.backend())); - let stored = Stored { - value: encoder_id, - ref_count: cmb.life_guard.ref_count.clone(), - }; - - let pass = ComputePass::new(raw, stored, trackers, cmb.features.max_bind_groups); - hub.compute_passes - .register_identity(id_in, pass, &mut token) -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_command_encoder_begin_compute_pass( - encoder_id: CommandEncoderId, - desc: Option<&ComputePassDescriptor>, -) -> ComputePassId { - let desc = &desc.cloned().unwrap_or_default(); - gfx_select!(encoder_id => command_encoder_begin_compute_pass(&*GLOBAL, encoder_id, desc, PhantomData)) -} diff --git a/wgpu-native/src/command/render.rs b/wgpu-native/src/command/render.rs deleted file mode 100644 index 2c4c8a979..000000000 --- a/wgpu-native/src/command/render.rs +++ /dev/null @@ -1,848 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use crate::{ - command::bind::{Binder, LayoutChange}, - conv, - device::{RenderPassContext, BIND_BUFFER_ALIGNMENT, MAX_VERTEX_BUFFERS}, - hub::{GfxBackend, Global, Token}, - pipeline::{IndexFormat, InputStepMode, PipelineFlags}, - resource::BufferUsage, - track::{Stitch, TrackerSet}, - BindGroupId, - BufferAddress, - BufferId, - Color, - CommandBuffer, - CommandBufferId, - RenderPassId, - RenderPipelineId, - Stored, -}; -#[cfg(feature = "local")] -use crate::{gfx_select, hub::GLOBAL, RawString, RenderBundleId}; - -use hal::command::CommandBuffer as _; - -#[cfg(feature = "local")] -use std::slice; -use std::{iter, ops::Range}; - -#[derive(Debug, PartialEq)] -enum OptionalState { - Unused, - Required, - Set, -} - -impl OptionalState { - fn require(&mut self, require: bool) { - if require && *self == OptionalState::Unused { - *self = OptionalState::Required; - } - } -} - -#[derive(Debug, PartialEq)] -enum DrawError { - MissingBlendColor, - MissingStencilReference, - IncompatibleBindGroup { - index: u32, - //expected: BindGroupLayoutId, - //provided: Option<(BindGroupLayoutId, BindGroupId)>, - }, -} - -#[derive(Debug)] -pub struct IndexState { - bound_buffer_view: Option<(BufferId, Range)>, - format: IndexFormat, - limit: u32, -} - -impl IndexState { - fn update_limit(&mut self) { - self.limit = match self.bound_buffer_view { - Some((_, ref range)) => { - let shift = match self.format { - IndexFormat::Uint16 => 1, - IndexFormat::Uint32 => 2, - }; - ((range.end - range.start) >> shift) as u32 - } - None => 0, - } - } -} - -#[derive(Clone, Copy, Debug)] -pub struct VertexBufferState { - total_size: BufferAddress, - stride: BufferAddress, - rate: InputStepMode, -} - -impl VertexBufferState { - const EMPTY: Self = VertexBufferState { - total_size: 0, - stride: 0, - rate: InputStepMode::Vertex, - }; -} - -#[derive(Debug)] -pub struct VertexState { - inputs: [VertexBufferState; MAX_VERTEX_BUFFERS], - vertex_limit: u32, - instance_limit: u32, -} - -impl VertexState { - fn update_limits(&mut self) { - self.vertex_limit = !0; - self.instance_limit = !0; - for vbs in &self.inputs { - if vbs.stride == 0 { - continue; - } - let limit = (vbs.total_size / vbs.stride) as u32; - match vbs.rate { - InputStepMode::Vertex => self.vertex_limit = self.vertex_limit.min(limit), - InputStepMode::Instance => self.instance_limit = self.instance_limit.min(limit), - } - } - } -} - -#[derive(Debug)] -pub struct RenderPass { - raw: B::CommandBuffer, - cmb_id: Stored, - context: RenderPassContext, - binder: Binder, - trackers: TrackerSet, - blend_color_status: OptionalState, - stencil_reference_status: OptionalState, - index_state: IndexState, - vertex_state: VertexState, - sample_count: u8, -} - -impl RenderPass { - pub(crate) fn new( - raw: B::CommandBuffer, - cmb_id: Stored, - context: RenderPassContext, - sample_count: u8, - max_bind_groups: u32, - ) -> Self { - RenderPass { - raw, - cmb_id, - context, - binder: Binder::new(max_bind_groups), - trackers: TrackerSet::new(B::VARIANT), - blend_color_status: OptionalState::Unused, - stencil_reference_status: OptionalState::Unused, - index_state: IndexState { - bound_buffer_view: None, - format: IndexFormat::Uint16, - limit: 0, - }, - vertex_state: VertexState { - inputs: [VertexBufferState::EMPTY; MAX_VERTEX_BUFFERS], - vertex_limit: 0, - instance_limit: 0, - }, - sample_count, - } - } - - fn is_ready(&self) -> Result<(), DrawError> { - //TODO: vertex buffers - let bind_mask = self.binder.invalid_mask(); - if bind_mask != 0 { - //let (expected, provided) = self.binder.entries[index as usize].info(); - return Err(DrawError::IncompatibleBindGroup { - index: bind_mask.trailing_zeros() as u32, - }); - } - if self.blend_color_status == OptionalState::Required { - return Err(DrawError::MissingBlendColor); - } - if self.stencil_reference_status == OptionalState::Required { - return Err(DrawError::MissingStencilReference); - } - Ok(()) - } -} - -// Common routines between render/compute - -pub fn render_pass_end_pass(global: &Global, pass_id: RenderPassId) { - let hub = B::hub(global); - let mut token = Token::root(); - let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token); - let (mut pass, mut token) = hub.render_passes.unregister(pass_id, &mut token); - unsafe { - pass.raw.end_render_pass(); - } - pass.trackers.optimize(); - let cmb = &mut cmb_guard[pass.cmb_id.value]; - let (buffer_guard, mut token) = hub.buffers.read(&mut token); - let (texture_guard, _) = hub.textures.read(&mut token); - - match cmb.raw.last_mut() { - Some(last) => { - log::trace!("Encoding barriers before pass {:?}", pass_id); - CommandBuffer::insert_barriers( - last, - &mut cmb.trackers, - &pass.trackers, - Stitch::Last, - &*buffer_guard, - &*texture_guard, - ); - unsafe { last.finish() }; - } - None => { - cmb.trackers.merge_extend(&pass.trackers); - } - } - - cmb.raw.push(pass.raw); -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_render_pass_end_pass(pass_id: RenderPassId) { - gfx_select!(pass_id => render_pass_end_pass(&*GLOBAL, pass_id)) -} - -pub fn render_pass_set_bind_group( - global: &Global, - pass_id: RenderPassId, - index: u32, - bind_group_id: BindGroupId, - offsets: &[BufferAddress], -) { - let hub = B::hub(global); - let mut token = Token::root(); - let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token); - let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token); - - let (mut pass_guard, _) = hub.render_passes.write(&mut token); - let pass = &mut pass_guard[pass_id]; - - let bind_group = pass - .trackers - .bind_groups - .use_extend(&*bind_group_guard, bind_group_id, (), ()) - .unwrap(); - - assert_eq!(bind_group.dynamic_count, offsets.len()); - - if cfg!(debug_assertions) { - for off in offsets { - assert_eq!( - *off % BIND_BUFFER_ALIGNMENT, - 0, - "Misaligned dynamic buffer offset: {} does not align with {}", - off, - BIND_BUFFER_ALIGNMENT - ); - } - } - - pass.trackers.merge_extend(&bind_group.used); - - if let Some((pipeline_layout_id, follow_up_sets, follow_up_offsets)) = pass - .binder - .provide_entry(index as usize, bind_group_id, bind_group, offsets) - { - let bind_groups = iter::once(bind_group.raw.raw()) - .chain(follow_up_sets.map(|bg_id| bind_group_guard[bg_id].raw.raw())); - unsafe { - pass.raw.bind_graphics_descriptor_sets( - &&pipeline_layout_guard[pipeline_layout_id].raw, - index as usize, - bind_groups, - offsets - .iter() - .chain(follow_up_offsets) - .map(|&off| off as hal::command::DescriptorSetOffset), - ); - } - }; -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_render_pass_set_bind_group( - pass_id: RenderPassId, - index: u32, - bind_group_id: BindGroupId, - offsets: *const BufferAddress, - offsets_length: usize, -) { - let offsets = if offsets_length != 0 { - unsafe { slice::from_raw_parts(offsets, offsets_length) } - } else { - &[] - }; - gfx_select!(pass_id => render_pass_set_bind_group(&*GLOBAL, pass_id, index, bind_group_id, offsets)) -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_render_pass_push_debug_group(_pass_id: RenderPassId, _label: RawString) { - //TODO -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_render_pass_pop_debug_group(_pass_id: RenderPassId) { - //TODO -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_render_pass_insert_debug_marker(_pass_id: RenderPassId, _label: RawString) { - //TODO -} - -// Render-specific routines - -pub fn render_pass_set_index_buffer( - global: &Global, - pass_id: RenderPassId, - buffer_id: BufferId, - offset: BufferAddress, -) { - let hub = B::hub(global); - let mut token = Token::root(); - let (mut pass_guard, mut token) = hub.render_passes.write(&mut token); - let (buffer_guard, _) = hub.buffers.read(&mut token); - - let pass = &mut pass_guard[pass_id]; - let buffer = pass - .trackers - .buffers - .use_extend(&*buffer_guard, buffer_id, (), BufferUsage::INDEX) - .unwrap(); - assert!(buffer.usage.contains(BufferUsage::INDEX)); - - let range = offset .. buffer.size; - pass.index_state.bound_buffer_view = Some((buffer_id, range)); - pass.index_state.update_limit(); - - let view = hal::buffer::IndexBufferView { - buffer: &buffer.raw, - offset, - index_type: conv::map_index_format(pass.index_state.format), - }; - - unsafe { - pass.raw.bind_index_buffer(view); - } -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_render_pass_set_index_buffer( - pass_id: RenderPassId, - buffer_id: BufferId, - offset: BufferAddress, -) { - gfx_select!(pass_id => render_pass_set_index_buffer(&*GLOBAL, pass_id, buffer_id, offset)) -} - -pub fn render_pass_set_vertex_buffers( - global: &Global, - pass_id: RenderPassId, - start_slot: u32, - buffers: &[BufferId], - offsets: &[BufferAddress], -) { - let hub = B::hub(global); - let mut token = Token::root(); - assert_eq!(buffers.len(), offsets.len()); - - let (mut pass_guard, mut token) = hub.render_passes.write(&mut token); - let (buffer_guard, _) = hub.buffers.read(&mut token); - - let pass = &mut pass_guard[pass_id]; - for (vbs, (&id, &offset)) in pass.vertex_state.inputs[start_slot as usize ..] - .iter_mut() - .zip(buffers.iter().zip(offsets)) - { - let buffer = pass - .trackers - .buffers - .use_extend(&*buffer_guard, id, (), BufferUsage::VERTEX) - .unwrap(); - assert!(buffer.usage.contains(BufferUsage::VERTEX)); - - vbs.total_size = buffer.size - offset; - } - - pass.vertex_state.update_limits(); - - let buffers = buffers - .iter() - .map(|&id| &buffer_guard[id].raw) - .zip(offsets.iter().cloned()); - - unsafe { - pass.raw.bind_vertex_buffers(start_slot, buffers); - } -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_render_pass_set_vertex_buffers( - pass_id: RenderPassId, - start_slot: u32, - buffers: *const BufferId, - offsets: *const BufferAddress, - length: usize, -) { - let buffers = unsafe { slice::from_raw_parts(buffers, length) }; - let offsets = unsafe { slice::from_raw_parts(offsets, length) }; - gfx_select!(pass_id => render_pass_set_vertex_buffers(&*GLOBAL, pass_id, start_slot, buffers, offsets)) -} - -pub fn render_pass_draw( - global: &Global, - pass_id: RenderPassId, - vertex_count: u32, - instance_count: u32, - first_vertex: u32, - first_instance: u32, -) { - let hub = B::hub(global); - let mut token = Token::root(); - let (mut pass_guard, _) = hub.render_passes.write(&mut token); - let pass = &mut pass_guard[pass_id]; - pass.is_ready().unwrap(); - - assert!( - first_vertex + vertex_count <= pass.vertex_state.vertex_limit, - "Vertex out of range!" - ); - assert!( - first_instance + instance_count <= pass.vertex_state.instance_limit, - "Instance out of range!" - ); - - unsafe { - pass.raw.draw( - first_vertex .. first_vertex + vertex_count, - first_instance .. first_instance + instance_count, - ); - } -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_render_pass_draw( - pass_id: RenderPassId, - vertex_count: u32, - instance_count: u32, - first_vertex: u32, - first_instance: u32, -) { - gfx_select!(pass_id => render_pass_draw(&*GLOBAL, pass_id, vertex_count, instance_count, first_vertex, first_instance)) -} - -pub fn render_pass_draw_indirect( - global: &Global, - pass_id: RenderPassId, - indirect_buffer_id: BufferId, - indirect_offset: BufferAddress, -) { - let hub = B::hub(global); - let mut token = Token::root(); - let (mut pass_guard, _) = hub.render_passes.write(&mut token); - let (buffer_guard, _) = hub.buffers.read(&mut token); - let pass = &mut pass_guard[pass_id]; - pass.is_ready().unwrap(); - - let buffer = pass - .trackers - .buffers - .use_extend( - &*buffer_guard, - indirect_buffer_id, - (), - BufferUsage::INDIRECT, - ) - .unwrap(); - assert!(buffer.usage.contains(BufferUsage::INDIRECT)); - - unsafe { - pass.raw.draw_indirect(&buffer.raw, indirect_offset, 1, 0); - } -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_render_pass_draw_indirect( - pass_id: RenderPassId, - indirect_buffer_id: BufferId, - indirect_offset: BufferAddress, -) { - gfx_select!(pass_id => render_pass_draw_indirect(&*GLOBAL, pass_id, indirect_buffer_id, indirect_offset)) -} - -pub fn render_pass_draw_indexed( - global: &Global, - pass_id: RenderPassId, - index_count: u32, - instance_count: u32, - first_index: u32, - base_vertex: i32, - first_instance: u32, -) { - let hub = B::hub(global); - let mut token = Token::root(); - let (mut pass_guard, _) = hub.render_passes.write(&mut token); - let pass = &mut pass_guard[pass_id]; - pass.is_ready().unwrap(); - - //TODO: validate that base_vertex + max_index() is within the provided range - assert!( - first_index + index_count <= pass.index_state.limit, - "Index out of range!" - ); - assert!( - first_instance + instance_count <= pass.vertex_state.instance_limit, - "Instance out of range!" - ); - - unsafe { - pass.raw.draw_indexed( - first_index .. first_index + index_count, - base_vertex, - first_instance .. first_instance + instance_count, - ); - } -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_render_pass_draw_indexed( - pass_id: RenderPassId, - index_count: u32, - instance_count: u32, - first_index: u32, - base_vertex: i32, - first_instance: u32, -) { - gfx_select!(pass_id => render_pass_draw_indexed(&*GLOBAL, pass_id, index_count, instance_count, first_index, base_vertex, first_instance)) -} - -pub fn render_pass_draw_indexed_indirect( - global: &Global, - pass_id: RenderPassId, - indirect_buffer_id: BufferId, - indirect_offset: BufferAddress, -) { - let hub = B::hub(global); - let mut token = Token::root(); - let (mut pass_guard, _) = hub.render_passes.write(&mut token); - let (buffer_guard, _) = hub.buffers.read(&mut token); - let pass = &mut pass_guard[pass_id]; - pass.is_ready().unwrap(); - - let buffer = pass - .trackers - .buffers - .use_extend( - &*buffer_guard, - indirect_buffer_id, - (), - BufferUsage::INDIRECT, - ) - .unwrap(); - assert!(buffer.usage.contains(BufferUsage::INDIRECT)); - - unsafe { - pass.raw - .draw_indexed_indirect(&buffer.raw, indirect_offset, 1, 0); - } -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_render_pass_draw_indexed_indirect( - pass_id: RenderPassId, - indirect_buffer_id: BufferId, - indirect_offset: BufferAddress, -) { - gfx_select!(pass_id => render_pass_draw_indexed_indirect(&*GLOBAL, pass_id, indirect_buffer_id, indirect_offset)) -} - -pub fn render_pass_set_pipeline( - global: &Global, - pass_id: RenderPassId, - pipeline_id: RenderPipelineId, -) { - let hub = B::hub(global); - let mut token = Token::root(); - let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token); - let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token); - let (mut pass_guard, mut token) = hub.render_passes.write(&mut token); - let pass = &mut pass_guard[pass_id]; - let (pipeline_guard, mut token) = hub.render_pipelines.read(&mut token); - let pipeline = &pipeline_guard[pipeline_id]; - - assert!( - pass.context.compatible(&pipeline.pass_context), - "The render pipeline is not compatible with the pass!" - ); - assert_eq!( - pipeline.sample_count, pass.sample_count, - "The render pipeline and renderpass have mismatching sample_count" - ); - - pass.blend_color_status - .require(pipeline.flags.contains(PipelineFlags::BLEND_COLOR)); - pass.stencil_reference_status - .require(pipeline.flags.contains(PipelineFlags::STENCIL_REFERENCE)); - - unsafe { - pass.raw.bind_graphics_pipeline(&pipeline.raw); - } - - // Rebind resource - if pass.binder.pipeline_layout_id != Some(pipeline.layout_id.clone()) { - let pipeline_layout = &pipeline_layout_guard[pipeline.layout_id]; - pass.binder.pipeline_layout_id = Some(pipeline.layout_id.clone()); - pass.binder - .reset_expectations(pipeline_layout.bind_group_layout_ids.len()); - let mut is_compatible = true; - - for (index, (entry, &bgl_id)) in pass - .binder - .entries - .iter_mut() - .zip(&pipeline_layout.bind_group_layout_ids) - .enumerate() - { - match entry.expect_layout(bgl_id) { - LayoutChange::Match(bg_id, offsets) if is_compatible => { - let desc_set = bind_group_guard[bg_id].raw.raw(); - unsafe { - pass.raw.bind_graphics_descriptor_sets( - &pipeline_layout.raw, - index, - iter::once(desc_set), - offsets.iter().map(|offset| *offset as u32), - ); - } - } - LayoutChange::Match(..) | LayoutChange::Unchanged => {} - LayoutChange::Mismatch => { - is_compatible = false; - } - } - } - } - - // Rebind index buffer if the index format has changed with the pipeline switch - if pass.index_state.format != pipeline.index_format { - pass.index_state.format = pipeline.index_format; - pass.index_state.update_limit(); - - if let Some((buffer_id, ref range)) = pass.index_state.bound_buffer_view { - let (buffer_guard, _) = hub.buffers.read(&mut token); - let buffer = pass - .trackers - .buffers - .use_extend(&*buffer_guard, buffer_id, (), BufferUsage::INDEX) - .unwrap(); - - let view = hal::buffer::IndexBufferView { - buffer: &buffer.raw, - offset: range.start, - index_type: conv::map_index_format(pass.index_state.format), - }; - - unsafe { - pass.raw.bind_index_buffer(view); - } - } - } - // Update vertex buffer limits - for (vbs, &(stride, rate)) in pass - .vertex_state - .inputs - .iter_mut() - .zip(&pipeline.vertex_strides) - { - vbs.stride = stride; - vbs.rate = rate; - } - for vbs in pass.vertex_state.inputs[pipeline.vertex_strides.len() ..].iter_mut() { - vbs.stride = 0; - vbs.rate = InputStepMode::Vertex; - } - pass.vertex_state.update_limits(); -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_render_pass_set_pipeline( - pass_id: RenderPassId, - pipeline_id: RenderPipelineId, -) { - gfx_select!(pass_id => render_pass_set_pipeline(&*GLOBAL, pass_id, pipeline_id)) -} - -pub fn render_pass_set_blend_color( - global: &Global, - pass_id: RenderPassId, - color: &Color, -) { - let hub = B::hub(global); - let mut token = Token::root(); - let (mut pass_guard, _) = hub.render_passes.write(&mut token); - let pass = &mut pass_guard[pass_id]; - - pass.blend_color_status = OptionalState::Set; - - unsafe { - pass.raw.set_blend_constants(conv::map_color_f32(color)); - } -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_render_pass_set_blend_color(pass_id: RenderPassId, color: &Color) { - gfx_select!(pass_id => render_pass_set_blend_color(&*GLOBAL, pass_id, color)) -} - -pub fn render_pass_set_stencil_reference( - global: &Global, - pass_id: RenderPassId, - value: u32, -) { - let hub = B::hub(global); - let mut token = Token::root(); - let (mut pass_guard, _) = hub.render_passes.write(&mut token); - let pass = &mut pass_guard[pass_id]; - - pass.stencil_reference_status = OptionalState::Set; - - unsafe { - pass.raw.set_stencil_reference(hal::pso::Face::all(), value); - } -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_render_pass_set_stencil_reference(pass_id: RenderPassId, value: u32) { - gfx_select!(pass_id => render_pass_set_stencil_reference(&*GLOBAL, pass_id, value)) -} - -pub fn render_pass_set_viewport( - global: &Global, - pass_id: RenderPassId, - x: f32, - y: f32, - w: f32, - h: f32, - min_depth: f32, - max_depth: f32, -) { - let hub = B::hub(global); - let mut token = Token::root(); - let (mut pass_guard, _) = hub.render_passes.write(&mut token); - let pass = &mut pass_guard[pass_id]; - - unsafe { - use std::convert::TryFrom; - use std::i16; - - pass.raw.set_viewports( - 0, - &[hal::pso::Viewport { - rect: hal::pso::Rect { - x: i16::try_from(x.round() as i64).unwrap_or(0), - y: i16::try_from(y.round() as i64).unwrap_or(0), - w: i16::try_from(w.round() as i64).unwrap_or(i16::MAX), - h: i16::try_from(h.round() as i64).unwrap_or(i16::MAX), - }, - depth: min_depth .. max_depth, - }], - ); - } -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_render_pass_set_viewport( - pass_id: RenderPassId, - x: f32, - y: f32, - w: f32, - h: f32, - min_depth: f32, - max_depth: f32, -) { - gfx_select!(pass_id => render_pass_set_viewport(&*GLOBAL, pass_id, x, y, w, h, min_depth, max_depth)) -} - -pub fn render_pass_set_scissor_rect( - global: &Global, - pass_id: RenderPassId, - x: u32, - y: u32, - w: u32, - h: u32, -) { - let hub = B::hub(global); - let mut token = Token::root(); - let (mut pass_guard, _) = hub.render_passes.write(&mut token); - let pass = &mut pass_guard[pass_id]; - - unsafe { - use std::convert::TryFrom; - use std::i16; - - pass.raw.set_scissors( - 0, - &[hal::pso::Rect { - x: i16::try_from(x).unwrap_or(0), - y: i16::try_from(y).unwrap_or(0), - w: i16::try_from(w).unwrap_or(i16::MAX), - h: i16::try_from(h).unwrap_or(i16::MAX), - }], - ); - } -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_render_pass_set_scissor_rect( - pass_id: RenderPassId, - x: u32, - y: u32, - w: u32, - h: u32, -) { - gfx_select!(pass_id => render_pass_set_scissor_rect(&*GLOBAL, pass_id, x, y, w, h)) -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_render_pass_execute_bundles( - _pass_id: RenderPassId, - _bundles: *const RenderBundleId, - _bundles_length: usize, -) { - unimplemented!() -} diff --git a/wgpu-native/src/command/transfer.rs b/wgpu-native/src/command/transfer.rs deleted file mode 100644 index fc85ff13d..000000000 --- a/wgpu-native/src/command/transfer.rs +++ /dev/null @@ -1,421 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use crate::{ - conv, - device::{all_buffer_stages, all_image_stages}, - hub::{GfxBackend, Global, Token}, - BufferAddress, - BufferId, - BufferUsage, - CommandEncoderId, - Extent3d, - Origin3d, - TextureId, - TextureUsage, -}; -#[cfg(feature = "local")] -use crate::{gfx_select, hub::GLOBAL}; - -use hal::command::CommandBuffer as _; - -use std::iter; - -const BITS_PER_BYTE: u32 = 8; - -#[repr(C)] -#[derive(Debug)] -pub struct BufferCopyView { - pub buffer: BufferId, - pub offset: BufferAddress, - pub row_pitch: u32, - pub image_height: u32, -} - -#[repr(C)] -#[derive(Debug)] -pub struct TextureCopyView { - pub texture: TextureId, - pub mip_level: u32, - pub array_layer: u32, - pub origin: Origin3d, -} - -impl TextureCopyView { - //TODO: we currently access each texture twice for a transfer, - // once only to get the aspect flags, which is unfortunate. - fn to_selector(&self, aspects: hal::format::Aspects) -> hal::image::SubresourceRange { - let level = self.mip_level as hal::image::Level; - let layer = self.array_layer as hal::image::Layer; - hal::image::SubresourceRange { - aspects, - levels: level .. level + 1, - layers: layer .. layer + 1, - } - } - - fn to_sub_layers(&self, aspects: hal::format::Aspects) -> hal::image::SubresourceLayers { - let layer = self.array_layer as hal::image::Layer; - hal::image::SubresourceLayers { - aspects, - level: self.mip_level as hal::image::Level, - layers: layer .. layer + 1, - } - } -} - -pub fn command_encoder_copy_buffer_to_buffer( - global: &Global, - command_encoder_id: CommandEncoderId, - source: BufferId, - source_offset: BufferAddress, - destination: BufferId, - destination_offset: BufferAddress, - size: BufferAddress, -) { - let hub = B::hub(global); - let mut token = Token::root(); - - let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token); - let cmb = &mut cmb_guard[command_encoder_id]; - let (buffer_guard, _) = hub.buffers.read(&mut token); - // we can't hold both src_pending and dst_pending in scope because they - // borrow the buffer tracker mutably... - let mut barriers = Vec::new(); - - let (src_buffer, src_pending) = - cmb.trackers - .buffers - .use_replace(&*buffer_guard, source, (), BufferUsage::COPY_SRC); - assert!(src_buffer.usage.contains(BufferUsage::COPY_SRC)); - - barriers.extend(src_pending.map(|pending| hal::memory::Barrier::Buffer { - states: pending.to_states(), - target: &src_buffer.raw, - families: None, - range: None .. None, - })); - - let (dst_buffer, dst_pending) = - cmb.trackers - .buffers - .use_replace(&*buffer_guard, destination, (), BufferUsage::COPY_DST); - assert!(dst_buffer.usage.contains(BufferUsage::COPY_DST)); - - barriers.extend(dst_pending.map(|pending| hal::memory::Barrier::Buffer { - states: pending.to_states(), - target: &dst_buffer.raw, - families: None, - range: None .. None, - })); - - let region = hal::command::BufferCopy { - src: source_offset, - dst: destination_offset, - size, - }; - let cmb_raw = cmb.raw.last_mut().unwrap(); - unsafe { - cmb_raw.pipeline_barrier( - all_buffer_stages() .. all_buffer_stages(), - hal::memory::Dependencies::empty(), - barriers, - ); - cmb_raw.copy_buffer(&src_buffer.raw, &dst_buffer.raw, iter::once(region)); - } -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_command_encoder_copy_buffer_to_buffer( - command_encoder_id: CommandEncoderId, - source: BufferId, - source_offset: BufferAddress, - destination: BufferId, - destination_offset: BufferAddress, - size: BufferAddress, -) { - gfx_select!(command_encoder_id => command_encoder_copy_buffer_to_buffer( - &*GLOBAL, - command_encoder_id, - source, source_offset, - destination, - destination_offset, - size)) -} - -pub fn command_encoder_copy_buffer_to_texture( - global: &Global, - command_encoder_id: CommandEncoderId, - source: &BufferCopyView, - destination: &TextureCopyView, - copy_size: Extent3d, -) { - let hub = B::hub(global); - let mut token = Token::root(); - let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token); - let cmb = &mut cmb_guard[command_encoder_id]; - let (buffer_guard, mut token) = hub.buffers.read(&mut token); - let (texture_guard, _) = hub.textures.read(&mut token); - let aspects = texture_guard[destination.texture].full_range.aspects; - - let (src_buffer, src_pending) = - cmb.trackers - .buffers - .use_replace(&*buffer_guard, source.buffer, (), BufferUsage::COPY_SRC); - assert!(src_buffer.usage.contains(BufferUsage::COPY_SRC)); - - let src_barriers = src_pending.map(|pending| hal::memory::Barrier::Buffer { - states: pending.to_states(), - target: &src_buffer.raw, - families: None, - range: None .. None, - }); - - let (dst_texture, dst_pending) = cmb.trackers.textures.use_replace( - &*texture_guard, - destination.texture, - destination.to_selector(aspects), - TextureUsage::COPY_DST, - ); - assert!(dst_texture.usage.contains(TextureUsage::COPY_DST)); - - let dst_barriers = dst_pending.map(|pending| hal::memory::Barrier::Image { - states: pending.to_states(), - target: &dst_texture.raw, - families: None, - range: pending.selector, - }); - - let aspects = dst_texture.full_range.aspects; - let bytes_per_texel = conv::map_texture_format(dst_texture.format, cmb.features) - .surface_desc() - .bits as u32 - / BITS_PER_BYTE; - let buffer_width = source.row_pitch / bytes_per_texel; - assert_eq!(source.row_pitch % bytes_per_texel, 0); - let region = hal::command::BufferImageCopy { - buffer_offset: source.offset, - buffer_width, - buffer_height: source.image_height, - image_layers: destination.to_sub_layers(aspects), - image_offset: conv::map_origin(destination.origin), - image_extent: conv::map_extent(copy_size), - }; - let cmb_raw = cmb.raw.last_mut().unwrap(); - let stages = all_buffer_stages() | all_image_stages(); - unsafe { - cmb_raw.pipeline_barrier( - stages .. stages, - hal::memory::Dependencies::empty(), - src_barriers.chain(dst_barriers), - ); - cmb_raw.copy_buffer_to_image( - &src_buffer.raw, - &dst_texture.raw, - hal::image::Layout::TransferDstOptimal, - iter::once(region), - ); - } -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_command_encoder_copy_buffer_to_texture( - command_encoder_id: CommandEncoderId, - source: &BufferCopyView, - destination: &TextureCopyView, - copy_size: Extent3d, -) { - gfx_select!(command_encoder_id => command_encoder_copy_buffer_to_texture( - &*GLOBAL, - command_encoder_id, - source, - destination, - copy_size)) -} - -pub fn command_encoder_copy_texture_to_buffer( - global: &Global, - command_encoder_id: CommandEncoderId, - source: &TextureCopyView, - destination: &BufferCopyView, - copy_size: Extent3d, -) { - let hub = B::hub(global); - let mut token = Token::root(); - let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token); - let cmb = &mut cmb_guard[command_encoder_id]; - let (buffer_guard, mut token) = hub.buffers.read(&mut token); - let (texture_guard, _) = hub.textures.read(&mut token); - let aspects = texture_guard[source.texture].full_range.aspects; - - let (src_texture, src_pending) = cmb.trackers.textures.use_replace( - &*texture_guard, - source.texture, - source.to_selector(aspects), - TextureUsage::COPY_SRC, - ); - assert!(src_texture.usage.contains(TextureUsage::COPY_SRC)); - - let src_barriers = src_pending.map(|pending| hal::memory::Barrier::Image { - states: pending.to_states(), - target: &src_texture.raw, - families: None, - range: pending.selector, - }); - - let (dst_buffer, dst_barriers) = cmb.trackers.buffers.use_replace( - &*buffer_guard, - destination.buffer, - (), - BufferUsage::COPY_DST, - ); - assert!(dst_buffer.usage.contains(BufferUsage::COPY_DST)); - - let dst_barrier = dst_barriers.map(|pending| hal::memory::Barrier::Buffer { - states: pending.to_states(), - target: &dst_buffer.raw, - families: None, - range: None .. None, - }); - - let aspects = src_texture.full_range.aspects; - let bytes_per_texel = conv::map_texture_format(src_texture.format, cmb.features) - .surface_desc() - .bits as u32 - / BITS_PER_BYTE; - let buffer_width = destination.row_pitch / bytes_per_texel; - assert_eq!(destination.row_pitch % bytes_per_texel, 0); - let region = hal::command::BufferImageCopy { - buffer_offset: destination.offset, - buffer_width, - buffer_height: destination.image_height, - image_layers: source.to_sub_layers(aspects), - image_offset: conv::map_origin(source.origin), - image_extent: conv::map_extent(copy_size), - }; - let cmb_raw = cmb.raw.last_mut().unwrap(); - let stages = all_buffer_stages() | all_image_stages(); - unsafe { - cmb_raw.pipeline_barrier( - stages .. stages, - hal::memory::Dependencies::empty(), - src_barriers.chain(dst_barrier), - ); - cmb_raw.copy_image_to_buffer( - &src_texture.raw, - hal::image::Layout::TransferSrcOptimal, - &dst_buffer.raw, - iter::once(region), - ); - } -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_command_encoder_copy_texture_to_buffer( - command_encoder_id: CommandEncoderId, - source: &TextureCopyView, - destination: &BufferCopyView, - copy_size: Extent3d, -) { - gfx_select!(command_encoder_id => command_encoder_copy_texture_to_buffer( - &*GLOBAL, - command_encoder_id, - source, - destination, - copy_size)) -} - -pub fn command_encoder_copy_texture_to_texture( - global: &Global, - command_encoder_id: CommandEncoderId, - source: &TextureCopyView, - destination: &TextureCopyView, - copy_size: Extent3d, -) { - let hub = B::hub(global); - let mut token = Token::root(); - - let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token); - let cmb = &mut cmb_guard[command_encoder_id]; - let (_, mut token) = hub.buffers.read(&mut token); // skip token - let (texture_guard, _) = hub.textures.read(&mut token); - // we can't hold both src_pending and dst_pending in scope because they - // borrow the buffer tracker mutably... - let mut barriers = Vec::new(); - let aspects = texture_guard[source.texture].full_range.aspects - & texture_guard[destination.texture].full_range.aspects; - - let (src_texture, src_pending) = cmb.trackers.textures.use_replace( - &*texture_guard, - source.texture, - source.to_selector(aspects), - TextureUsage::COPY_SRC, - ); - assert!(src_texture.usage.contains(TextureUsage::COPY_SRC)); - - barriers.extend(src_pending.map(|pending| hal::memory::Barrier::Image { - states: pending.to_states(), - target: &src_texture.raw, - families: None, - range: pending.selector, - })); - - let (dst_texture, dst_pending) = cmb.trackers.textures.use_replace( - &*texture_guard, - destination.texture, - destination.to_selector(aspects), - TextureUsage::COPY_DST, - ); - assert!(dst_texture.usage.contains(TextureUsage::COPY_DST)); - - barriers.extend(dst_pending.map(|pending| hal::memory::Barrier::Image { - states: pending.to_states(), - target: &dst_texture.raw, - families: None, - range: pending.selector, - })); - - let aspects = src_texture.full_range.aspects & dst_texture.full_range.aspects; - let region = hal::command::ImageCopy { - src_subresource: source.to_sub_layers(aspects), - src_offset: conv::map_origin(source.origin), - dst_subresource: destination.to_sub_layers(aspects), - dst_offset: conv::map_origin(destination.origin), - extent: conv::map_extent(copy_size), - }; - let cmb_raw = cmb.raw.last_mut().unwrap(); - unsafe { - cmb_raw.pipeline_barrier( - all_image_stages() .. all_image_stages(), - hal::memory::Dependencies::empty(), - barriers, - ); - cmb_raw.copy_image( - &src_texture.raw, - hal::image::Layout::TransferSrcOptimal, - &dst_texture.raw, - hal::image::Layout::TransferDstOptimal, - iter::once(region), - ); - } -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_command_encoder_copy_texture_to_texture( - command_encoder_id: CommandEncoderId, - source: &TextureCopyView, - destination: &TextureCopyView, - copy_size: Extent3d, -) { - gfx_select!(command_encoder_id => command_encoder_copy_texture_to_texture( - &*GLOBAL, - command_encoder_id, - source, - destination, - copy_size)) -} diff --git a/wgpu-native/src/device.rs b/wgpu-native/src/device.rs index df1d33551..917e23af2 100644 --- a/wgpu-native/src/device.rs +++ b/wgpu-native/src/device.rs @@ -1,2261 +1,355 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use crate::GLOBAL; -#[cfg(feature = "local")] -use crate::instance::Limits; -use crate::{ - binding_model, - command, - conv, - hub::{GfxBackend, Global, Token}, - id::{Input, Output}, - pipeline, - resource, - swap_chain, - track::{Stitch, TrackerSet}, - AdapterId, - BindGroupId, - BindGroupLayoutId, - BufferAddress, - BufferId, - BufferMapAsyncStatus, - BufferMapOperation, - CommandBufferId, - CommandEncoderId, - ComputePipelineId, - DeviceId, - FastHashMap, - Features, - LifeGuard, - PipelineLayoutId, - QueueId, - RefCount, - RenderPipelineId, - SamplerId, - ShaderModuleId, - Stored, - SubmissionIndex, - SurfaceId, - SwapChainId, - TextureDimension, - TextureFormat, - TextureId, - TextureUsage, - TextureViewId, -}; -#[cfg(feature = "local")] -use crate::{gfx_select, hub::GLOBAL}; +use core::{gfx_select, hub::Token, id}; -use arrayvec::ArrayVec; -use copyless::VecHelper as _; -use hal::{ - self, - command::CommandBuffer as _, - device::Device as _, - queue::CommandQueue as _, - window::{PresentationSurface as _, Surface as _}, -}; -use parking_lot::Mutex; -use rendy_descriptor::{DescriptorAllocator, DescriptorRanges, DescriptorSet}; -use rendy_memory::{Block, Heaps, MemoryBlock}; - -#[cfg(feature = "local")] -use std::marker::PhantomData; -use std::{ - collections::hash_map::Entry, - ffi, - iter, - ops::Range, - ptr, - slice, - sync::atomic::Ordering, -}; +use std::{marker::PhantomData, slice}; -const CLEANUP_WAIT_MS: u64 = 5000; -pub const MAX_COLOR_TARGETS: usize = 4; -pub const MAX_MIP_LEVELS: usize = 16; -pub const MAX_VERTEX_BUFFERS: usize = 8; +pub fn wgpu_create_surface(raw_handle: raw_window_handle::RawWindowHandle) -> id::SurfaceId { + use raw_window_handle::RawWindowHandle as Rwh; -/// Bound uniform/storage buffer offsets must be aligned to this number. -pub const BIND_BUFFER_ALIGNMENT: hal::buffer::Offset = 256; - -pub fn all_buffer_stages() -> hal::pso::PipelineStage { - use hal::pso::PipelineStage as Ps; - Ps::DRAW_INDIRECT - | Ps::VERTEX_INPUT - | Ps::VERTEX_SHADER - | Ps::FRAGMENT_SHADER - | Ps::COMPUTE_SHADER - | Ps::TRANSFER - | Ps::HOST -} -pub fn all_image_stages() -> hal::pso::PipelineStage { - use hal::pso::PipelineStage as Ps; - Ps::EARLY_FRAGMENT_TESTS - | Ps::LATE_FRAGMENT_TESTS - | Ps::COLOR_ATTACHMENT_OUTPUT - | Ps::VERTEX_SHADER - | Ps::FRAGMENT_SHADER - | Ps::COMPUTE_SHADER - | Ps::TRANSFER -} - -#[derive(Clone, Copy, Debug, PartialEq)] -enum HostMap { - Read, - Write, -} - -#[derive(Clone, Debug, Hash, PartialEq)] -pub(crate) struct AttachmentData { - pub colors: ArrayVec<[T; MAX_COLOR_TARGETS]>, - pub resolves: ArrayVec<[T; MAX_COLOR_TARGETS]>, - pub depth_stencil: Option, -} -impl Eq for AttachmentData {} -impl AttachmentData { - pub(crate) fn all(&self) -> impl Iterator { - self.colors - .iter() - .chain(&self.resolves) - .chain(&self.depth_stencil) - } -} - -impl RenderPassContext { - // Assumed the renderpass only contains one subpass - pub(crate) fn compatible(&self, other: &RenderPassContext) -> bool { - self.colors == other.colors && self.depth_stencil == other.depth_stencil - } -} - -pub(crate) type RenderPassKey = AttachmentData; -pub(crate) type FramebufferKey = AttachmentData; -pub(crate) type RenderPassContext = AttachmentData; - -#[derive(Debug, PartialEq)] -enum ResourceId { - Buffer(BufferId), - Texture(TextureId), - TextureView(TextureViewId), - BindGroup(BindGroupId), - Sampler(SamplerId), -} - -#[derive(Debug)] -enum NativeResource { - Buffer(B::Buffer, MemoryBlock), - Image(B::Image, MemoryBlock), - ImageView(B::ImageView), - Framebuffer(B::Framebuffer), - DescriptorSet(DescriptorSet), - Sampler(B::Sampler), -} - -#[derive(Debug)] -struct ActiveSubmission { - index: SubmissionIndex, - fence: B::Fence, - // Note: we keep the associated ID here in order to be able to check - // at any point what resources are used in a submission. - resources: Vec<(Option, NativeResource)>, - mapped: Vec, -} - -/// A struct responsible for tracking resource lifetimes. -/// -/// Here is how host mapping is handled: -/// 1. When mapping is requested we add the buffer to the pending list of `mapped` buffers. -/// 2. When `triage_referenced` is called, it checks the last submission index associated with each of the mapped buffer, -/// and register the buffer with either a submission in flight, or straight into `ready_to_map` vector. -/// 3. When `ActiveSubmission` is retired, the mapped buffers associated with it are moved to `ready_to_map` vector. -/// 4. Finally, `handle_mapping` issues all the callbacks. - -#[derive(Debug)] -struct PendingResources { - /// Resources that the user has requested be mapped, but are still in use. - mapped: Vec>, - /// Resources that are destroyed by the user but still referenced by - /// other objects or command buffers. - referenced: Vec<(ResourceId, RefCount)>, - /// Resources that are not referenced any more but still used by GPU. - /// Grouped by submissions associated with a fence and a submission index. - /// The active submissions have to be stored in FIFO order: oldest come first. - active: Vec>, - /// Resources that are neither referenced or used, just pending - /// actual deletion. - free: Vec>, - ready_to_map: Vec, -} - -impl PendingResources { - fn destroy(&mut self, resource_id: ResourceId, ref_count: RefCount) { - debug_assert!(!self.referenced.iter().any(|r| r.0 == resource_id)); - self.referenced.push((resource_id, ref_count)); - } - - fn map(&mut self, buffer: BufferId, ref_count: RefCount) { - self.mapped.push(Stored { - value: buffer, - ref_count, - }); - } - - /// Returns the last submission index that is done. - fn cleanup( - &mut self, - device: &B::Device, - heaps_mutex: &Mutex>, - descriptor_allocator_mutex: &Mutex>, - force_wait: bool, - ) -> SubmissionIndex { - if force_wait && !self.active.is_empty() { - let status = unsafe { - device.wait_for_fences( - self.active.iter().map(|a| &a.fence), - hal::device::WaitFor::All, - CLEANUP_WAIT_MS * 1_000_000, - ) - }; - assert_eq!(status, Ok(true), "GPU got stuck :("); - } - - //TODO: enable when `is_sorted_by_key` is stable - //debug_assert!(self.active.is_sorted_by_key(|a| a.index)); - let done_count = self - .active - .iter() - .position(|a| unsafe { !device.get_fence_status(&a.fence).unwrap() }) - .unwrap_or(self.active.len()); - let last_done = if done_count != 0 { - self.active[done_count - 1].index - } else { - return 0; - }; - - for a in self.active.drain(.. done_count) { - log::trace!("Active submission {} is done", a.index); - self.free.extend(a.resources.into_iter().map(|(_, r)| r)); - self.ready_to_map.extend(a.mapped); - unsafe { - device.destroy_fence(a.fence); - } - } - - let mut heaps = heaps_mutex.lock(); - let mut descriptor_allocator = descriptor_allocator_mutex.lock(); - for resource in self.free.drain(..) { - match resource { - NativeResource::Buffer(raw, memory) => unsafe { - device.destroy_buffer(raw); - heaps.free(device, memory); - }, - NativeResource::Image(raw, memory) => unsafe { - device.destroy_image(raw); - heaps.free(device, memory); - }, - NativeResource::ImageView(raw) => unsafe { - device.destroy_image_view(raw); - }, - NativeResource::Framebuffer(raw) => unsafe { - device.destroy_framebuffer(raw); - }, - NativeResource::DescriptorSet(raw) => unsafe { - descriptor_allocator.free(iter::once(raw)); - }, - NativeResource::Sampler(raw) => unsafe { - device.destroy_sampler(raw); - }, - } - } - - last_done - } - - fn triage_referenced( - &mut self, - global: &Global, - trackers: &mut TrackerSet, - mut token: &mut Token>, - ) { - // Before destruction, a resource is expected to have the following strong refs: - // - in resource itself - // - in the device tracker - // - in this list - const MIN_REFS: usize = 4; - - if self.referenced.iter().all(|r| r.1.load() >= MIN_REFS) { - return; - } - - let hub = B::hub(global); - //TODO: lock less, if possible - let (mut bind_group_guard, mut token) = hub.bind_groups.write(&mut token); - let (mut buffer_guard, mut token) = hub.buffers.write(&mut token); - let (mut texture_guard, mut token) = hub.textures.write(&mut token); - let (mut teview_view_guard, mut token) = hub.texture_views.write(&mut token); - let (mut sampler_guard, _) = hub.samplers.write(&mut token); - - for i in (0 .. self.referenced.len()).rev() { - let num_refs = self.referenced[i].1.load(); - if num_refs <= 3 { - let resource_id = self.referenced.swap_remove(i).0; - assert_eq!( - num_refs, 3, - "Resource {:?} misses some references", - resource_id - ); - let (life_guard, resource) = match resource_id { - ResourceId::Buffer(id) => { - if buffer_guard[id].pending_map_operation.is_some() { - continue; - } - trackers.buffers.remove(id); - let buf = buffer_guard.remove(id).unwrap(); - #[cfg(feature = "local")] - hub.buffers.identity.lock().free(id); - (buf.life_guard, NativeResource::Buffer(buf.raw, buf.memory)) - } - ResourceId::Texture(id) => { - trackers.textures.remove(id); - let tex = texture_guard.remove(id).unwrap(); - #[cfg(feature = "local")] - hub.textures.identity.lock().free(id); - (tex.life_guard, NativeResource::Image(tex.raw, tex.memory)) - } - ResourceId::TextureView(id) => { - trackers.views.remove(id); - let view = teview_view_guard.remove(id).unwrap(); - let raw = match view.inner { - resource::TextureViewInner::Native { raw, .. } => raw, - resource::TextureViewInner::SwapChain { .. } => unreachable!(), - }; - #[cfg(feature = "local")] - hub.texture_views.identity.lock().free(id); - (view.life_guard, NativeResource::ImageView(raw)) - } - ResourceId::BindGroup(id) => { - trackers.bind_groups.remove(id); - let bind_group = bind_group_guard.remove(id).unwrap(); - #[cfg(feature = "local")] - hub.bind_groups.identity.lock().free(id); - ( - bind_group.life_guard, - NativeResource::DescriptorSet(bind_group.raw), - ) - } - ResourceId::Sampler(id) => { - trackers.samplers.remove(id); - let sampler = sampler_guard.remove(id).unwrap(); - #[cfg(feature = "local")] - hub.samplers.identity.lock().free(id); - (sampler.life_guard, NativeResource::Sampler(sampler.raw)) - } - }; - - let submit_index = life_guard.submission_index.load(Ordering::Acquire); - match self.active.iter_mut().find(|a| a.index == submit_index) { - Some(a) => { - a.resources.alloc().init((Some(resource_id), resource)); - } - None => self.free.push(resource), - } - } - } - } - - fn triage_mapped(&mut self, global: &Global, token: &mut Token>) { - if self.mapped.is_empty() { - return; - } - let (buffer_guard, _) = B::hub(global).buffers.read(token); - - for stored in self.mapped.drain(..) { - let resource_id = stored.value; - let buf = &buffer_guard[resource_id]; - - let submit_index = buf.life_guard.submission_index.load(Ordering::Acquire); - log::trace!( - "Mapping of {:?} at submission {:?} gets assigned to active {:?}", - resource_id, - submit_index, - self.active.iter().position(|a| a.index == submit_index) - ); - - self.active - .iter_mut() - .find(|a| a.index == submit_index) - .map_or(&mut self.ready_to_map, |a| &mut a.mapped) - .push(resource_id); - } - } - - fn triage_framebuffers( - &mut self, - global: &Global, - framebuffers: &mut FastHashMap, - token: &mut Token>, - ) { - let (texture_view_guard, _) = B::hub(global).texture_views.read(token); - let remove_list = framebuffers - .keys() - .filter_map(|key| { - let mut last_submit: SubmissionIndex = 0; - for &at in key.all() { - if texture_view_guard.contains(at) { - return None; - } - // This attachment is no longer registered. - // Let's see if it's used by any of the active submissions. - let res_id = &Some(ResourceId::TextureView(at)); - for a in &self.active { - if a.resources.iter().any(|&(ref id, _)| id == res_id) { - last_submit = last_submit.max(a.index); - } - } - } - Some((key.clone(), last_submit)) - }) - .collect::>(); - - for (ref key, submit_index) in remove_list { - let resource = NativeResource::Framebuffer(framebuffers.remove(key).unwrap()); - match self.active.iter_mut().find(|a| a.index == submit_index) { - Some(a) => { - a.resources.alloc().init((None, resource)); - } - None => self.free.push(resource), - } - } - } - - fn handle_mapping( - &mut self, - global: &Global, - raw: &B::Device, - token: &mut Token>, - ) -> Vec { - if self.ready_to_map.is_empty() { - return Vec::new(); - } - let (mut buffer_guard, _) = B::hub(global).buffers.write(token); - self.ready_to_map - .drain(..) - .map(|buffer_id| { - let buffer = &mut buffer_guard[buffer_id]; - let operation = buffer.pending_map_operation.take().unwrap(); - let result = match operation { - BufferMapOperation::Read(ref range, ..) => { - map_buffer(raw, buffer, range.clone(), HostMap::Read) - } - BufferMapOperation::Write(ref range, ..) => { - map_buffer(raw, buffer, range.clone(), HostMap::Write) - } - }; - (operation, result) - }) - .collect() - } -} - -type BufferMapResult = Result<*mut u8, hal::device::MapError>; -type BufferMapPendingCallback = (BufferMapOperation, BufferMapResult); - -fn map_buffer( - raw: &B::Device, - buffer: &mut resource::Buffer, - buffer_range: Range, - kind: HostMap, -) -> BufferMapResult { - let is_coherent = buffer - .memory - .properties() - .contains(hal::memory::Properties::COHERENT); - let (ptr, mapped_range) = { - let mapped = buffer.memory.map(raw, buffer_range)?; - (mapped.ptr(), mapped.range()) + let instance = &GLOBAL.instance; + let surface = match raw_handle { + #[cfg(target_os = "ios")] + Rwh::IOS(h) => Surface { + #[cfg(feature = "vulkan-portability")] + vulkan: None, + metal: instance + .metal + .create_surface_from_uiview(h.ui_view, cfg!(debug_assertions)), + }, + #[cfg(target_os = "macos")] + Rwh::MacOS(h) => core::instance::Surface { + #[cfg(feature = "vulkan-portability")] + vulkan: instance + .vulkan + .as_ref() + .map(|inst| inst.create_surface_from_ns_view(h.ns_view)), + metal: instance + .metal + .create_surface_from_nsview(h.ns_view, cfg!(debug_assertions)), + }, + #[cfg(all(unix, not(target_os = "ios"), not(target_os = "macos")))] + Rwh::Xlib(h) => core::instance::Surface { + vulkan: instance + .vulkan + .as_ref() + .map(|inst| inst.create_surface_from_xlib(h.display as _, h.window as _)), + }, + #[cfg(all(unix, not(target_os = "ios"), not(target_os = "macos")))] + Rwh::Wayland(h) => core::instance::Surface { + vulkan: instance + .vulkan + .as_ref() + .map(|inst| inst.create_surface_from_wayland(h.display, h.surface)), + }, + #[cfg(windows)] + Rwh::Windows(h) => core::instance::Surface { + vulkan: instance + .vulkan + .as_ref() + .map(|inst| inst.create_surface_from_hwnd(std::ptr::null_mut(), h.hwnd)), + dx12: instance + .dx12 + .as_ref() + .map(|inst| inst.create_surface_from_hwnd(h.hwnd)), + dx11: instance.dx11.create_surface_from_hwnd(h.hwnd), + }, + _ => panic!("Unsupported window handle"), }; - if !is_coherent { - match kind { - HostMap::Read => unsafe { - raw.invalidate_mapped_memory_ranges(iter::once(( - buffer.memory.memory(), - mapped_range, - ))) - .unwrap(); - }, - HostMap::Write => { - buffer.mapped_write_ranges.push(mapped_range); - } - } - } - - Ok(ptr.as_ptr()) -} - -#[derive(Debug)] -pub struct Device { - pub(crate) raw: B::Device, - pub(crate) adapter_id: AdapterId, - pub(crate) queue_group: hal::queue::QueueGroup, - pub(crate) com_allocator: command::CommandAllocator, - mem_allocator: Mutex>, - desc_allocator: Mutex>, - life_guard: LifeGuard, - pub(crate) trackers: Mutex, - pub(crate) render_passes: Mutex>, - pub(crate) framebuffers: Mutex>, - pending: Mutex>, - pub(crate) features: Features, -} - -impl Device { - pub(crate) fn new( - raw: B::Device, - adapter_id: AdapterId, - queue_group: hal::queue::QueueGroup, - mem_props: hal::adapter::MemoryProperties, - supports_texture_d24_s8: bool, - max_bind_groups: u32, - ) -> Self { - // don't start submission index at zero - let life_guard = LifeGuard::new(); - life_guard.submission_index.fetch_add(1, Ordering::Relaxed); - - let heaps = { - let types = mem_props.memory_types.iter().map(|mt| { - use rendy_memory::{DynamicConfig, HeapsConfig, LinearConfig}; - let config = HeapsConfig { - linear: if mt.properties.contains(hal::memory::Properties::CPU_VISIBLE) { - Some(LinearConfig { - linear_size: 0x10_00_00, - }) - } else { - None - }, - dynamic: Some(DynamicConfig { - block_size_granularity: 0x1_00, - max_chunk_size: 0x1_00_00_00, - min_device_allocation: 0x1_00_00, - }), - }; - (mt.properties.into(), mt.heap_index as u32, config) - }); - unsafe { Heaps::new(types, mem_props.memory_heaps.iter().cloned()) } - }; - - Device { - raw, - adapter_id, - com_allocator: command::CommandAllocator::new(queue_group.family), - mem_allocator: Mutex::new(heaps), - desc_allocator: Mutex::new(DescriptorAllocator::new()), - queue_group, - life_guard, - trackers: Mutex::new(TrackerSet::new(B::VARIANT)), - render_passes: Mutex::new(FastHashMap::default()), - framebuffers: Mutex::new(FastHashMap::default()), - pending: Mutex::new(PendingResources { - mapped: Vec::new(), - referenced: Vec::new(), - active: Vec::new(), - free: Vec::new(), - ready_to_map: Vec::new(), - }), - features: Features { - max_bind_groups, - supports_texture_d24_s8, - }, - } - } - - fn maintain( - &self, - global: &Global, - force_wait: bool, - token: &mut Token, - ) -> Vec { - let mut pending = self.pending.lock(); - let mut trackers = self.trackers.lock(); - - pending.triage_referenced(global, &mut *trackers, token); - pending.triage_mapped(global, token); - pending.triage_framebuffers(global, &mut *self.framebuffers.lock(), token); - let last_done = pending.cleanup( - &self.raw, - &self.mem_allocator, - &self.desc_allocator, - force_wait, - ); - let callbacks = pending.handle_mapping(global, &self.raw, token); - - unsafe { - self.desc_allocator.lock().cleanup(&self.raw); - } - - if last_done != 0 { - self.com_allocator.maintain(last_done); - } - - callbacks - } - - //Note: this logic is specifically moved out of `handle_mapping()` in order to - // have nothing locked by the time we execute users callback code. - fn fire_map_callbacks>(callbacks: I) { - for (operation, result) in callbacks { - let (status, ptr) = match result { - Ok(ptr) => (BufferMapAsyncStatus::Success, ptr), - Err(e) => { - log::error!("failed to map buffer: {:?}", e); - (BufferMapAsyncStatus::Error, ptr::null_mut()) - } - }; - match operation { - BufferMapOperation::Read(_, on_read, userdata) => on_read(status, ptr, userdata), - BufferMapOperation::Write(_, on_write, userdata) => on_write(status, ptr, userdata), - } - } - } - - fn create_buffer( - &self, - self_id: DeviceId, - desc: &resource::BufferDescriptor, - ) -> resource::Buffer { - debug_assert_eq!(self_id.backend(), B::VARIANT); - let (usage, _memory_properties) = conv::map_buffer_usage(desc.usage); - - let rendy_usage = { - use rendy_memory::MemoryUsageValue as Muv; - use resource::BufferUsage as Bu; - - if !desc.usage.intersects(Bu::MAP_READ | Bu::MAP_WRITE) { - Muv::Data - } else if (Bu::MAP_WRITE | Bu::COPY_SRC).contains(desc.usage) { - Muv::Upload - } else if (Bu::MAP_READ | Bu::COPY_DST).contains(desc.usage) { - Muv::Download - } else { - Muv::Dynamic - } - }; - - let mut buffer = unsafe { self.raw.create_buffer(desc.size, usage).unwrap() }; - let requirements = unsafe { self.raw.get_buffer_requirements(&buffer) }; - let memory = self - .mem_allocator - .lock() - .allocate( - &self.raw, - requirements.type_mask as u32, - rendy_usage, - requirements.size, - requirements.alignment, - ) - .unwrap(); - - unsafe { - self.raw - .bind_buffer_memory(memory.memory(), memory.range().start, &mut buffer) - .unwrap() - }; - - resource::Buffer { - raw: buffer, - device_id: Stored { - value: self_id, - ref_count: self.life_guard.ref_count.clone(), - }, - usage: desc.usage, - memory, - size: desc.size, - mapped_write_ranges: Vec::new(), - pending_map_operation: None, - life_guard: LifeGuard::new(), - } - } - - fn create_texture( - &self, - self_id: DeviceId, - desc: &resource::TextureDescriptor, - ) -> resource::Texture { - debug_assert_eq!(self_id.backend(), B::VARIANT); - - // Ensure `D24Plus` textures cannot be copied - match desc.format { - TextureFormat::Depth24Plus | TextureFormat::Depth24PlusStencil8 => { - assert!(!desc - .usage - .intersects(TextureUsage::COPY_SRC | TextureUsage::COPY_DST)); - } - _ => {} - } - - let kind = conv::map_texture_dimension_size( - desc.dimension, - desc.size, - desc.array_layer_count, - desc.sample_count, - ); - let format = conv::map_texture_format(desc.format, self.features); - let aspects = format.surface_desc().aspects; - let usage = conv::map_texture_usage(desc.usage, aspects); - - assert!((desc.mip_level_count as usize) < MAX_MIP_LEVELS); - let mut view_capabilities = hal::image::ViewCapabilities::empty(); - - // 2D textures with array layer counts that are multiples of 6 could be cubemaps - // Following gpuweb/gpuweb#68 always add the hint in that case - if desc.dimension == TextureDimension::D2 && desc.array_layer_count % 6 == 0 { - view_capabilities |= hal::image::ViewCapabilities::KIND_CUBE; - }; - - // TODO: 2D arrays, cubemap arrays - - let mut image = unsafe { - self.raw.create_image( - kind, - desc.mip_level_count as hal::image::Level, - format, - hal::image::Tiling::Optimal, - usage, - view_capabilities, - ) - } - .unwrap(); - let requirements = unsafe { self.raw.get_image_requirements(&image) }; - - let memory = self - .mem_allocator - .lock() - .allocate( - &self.raw, - requirements.type_mask as u32, - rendy_memory::Data, - requirements.size, - requirements.alignment, - ) - .unwrap(); - - unsafe { - self.raw - .bind_image_memory(memory.memory(), memory.range().start, &mut image) - .unwrap() - }; - - resource::Texture { - raw: image, - device_id: Stored { - value: self_id, - ref_count: self.life_guard.ref_count.clone(), - }, - usage: desc.usage, - kind, - format: desc.format, - full_range: hal::image::SubresourceRange { - aspects, - levels: 0 .. desc.mip_level_count as hal::image::Level, - layers: 0 .. desc.array_layer_count as hal::image::Layer, - }, - memory, - life_guard: LifeGuard::new(), - } - } -} - -impl Device { - pub(crate) fn destroy_bind_group(&self, bind_group: binding_model::BindGroup) { - unsafe { - self.desc_allocator.lock().free(iter::once(bind_group.raw)); - } - } - - pub(crate) fn dispose(self) { - self.com_allocator.destroy(&self.raw); - let desc_alloc = self.desc_allocator.into_inner(); - unsafe { - desc_alloc.dispose(&self.raw); - } - } -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_device_get_limits(_device_id: DeviceId, limits: &mut Limits) { - *limits = Limits::default(); // TODO -} - -#[derive(Debug)] -pub struct ShaderModule { - pub(crate) raw: B::ShaderModule, -} - -pub fn device_create_buffer( - global: &Global, - device_id: DeviceId, - desc: &resource::BufferDescriptor, - id_in: Input, -) -> Output { - let hub = B::hub(global); let mut token = Token::root(); - - let (device_guard, _) = hub.devices.read(&mut token); - let device = &device_guard[device_id]; - let buffer = device.create_buffer(device_id, desc); - - let (id, id_out) = hub.buffers.new_identity(id_in); - let ok = device.trackers.lock().buffers.init( - id, - &buffer.life_guard.ref_count, - (), - resource::BufferUsage::empty(), - ); - assert!(ok); - - hub.buffers.register(id, buffer, &mut token); - id_out + GLOBAL + .surfaces + .register_identity(PhantomData, surface, &mut token) +} + +#[cfg(all(unix, not(target_os = "ios"), not(target_os = "macos")))] +#[no_mangle] +pub extern "C" fn wgpu_create_surface_from_xlib( + display: *mut *const std::ffi::c_void, + window: u64, +) -> id::SurfaceId { + use raw_window_handle::unix::XlibHandle; + wgpu_create_surface(raw_window_handle::RawWindowHandle::Xlib(XlibHandle { + window, + display: display as *mut _, + ..XlibHandle::empty() + })) +} + +#[cfg(any(target_os = "ios", target_os = "macos"))] +#[no_mangle] +pub extern "C" fn wgpu_create_surface_from_metal_layer( + layer: *mut std::ffi::c_void, +) -> id::SurfaceId { + let surface = core::instance::Surface { + #[cfg(feature = "vulkan-portability")] + vulkan: None, //TODO: currently requires `NSView` + metal: GLOBAL + .instance + .metal + .create_surface_from_layer(layer as *mut _, cfg!(debug_assertions)), + }; + + GLOBAL + .surfaces + .register_identity(PhantomData, surface, &mut Token::root()) +} + +#[cfg(windows)] +#[no_mangle] +pub extern "C" fn wgpu_create_surface_from_windows_hwnd( + _hinstance: *mut std::ffi::c_void, + hwnd: *mut std::ffi::c_void, +) -> id::SurfaceId { + use raw_window_handle::windows::WindowsHandle; + wgpu_create_surface(raw_window_handle::RawWindowHandle::Windows( + raw_window_handle::windows::WindowsHandle { + hwnd, + ..WindowsHandle::empty() + }, + )) +} + +#[no_mangle] +pub extern "C" fn wgpu_request_adapter_async( + desc: Option<&core::instance::RequestAdapterOptions>, + mask: core::instance::BackendBit, + callback: core::instance::RequestAdapterCallback, + userdata: *mut std::ffi::c_void, +) { + let id = GLOBAL.pick_adapter( + &desc.cloned().unwrap_or_default(), + core::instance::AdapterInputs::Mask(mask, || PhantomData), + ); + callback( + id.as_ref().map_or(&id::AdapterId::ERROR, |x| x as *const _), + userdata, + ); +} + +#[no_mangle] +pub extern "C" fn wgpu_adapter_request_device( + adapter_id: id::AdapterId, + desc: Option<&core::instance::DeviceDescriptor>, +) -> id::DeviceId { + let desc = &desc.cloned().unwrap_or_default(); + gfx_select!(adapter_id => GLOBAL.adapter_request_device(adapter_id, desc, PhantomData)) +} + +pub fn wgpu_adapter_get_info(adapter_id: id::AdapterId) -> core::instance::AdapterInfo { + gfx_select!(adapter_id => GLOBAL.adapter_get_info(adapter_id)) +} + +#[no_mangle] +pub extern "C" fn wgpu_device_get_limits( + _device_id: id::DeviceId, + limits: &mut core::instance::Limits, +) { + *limits = core::instance::Limits::default(); // TODO } -#[cfg(feature = "local")] #[no_mangle] pub extern "C" fn wgpu_device_create_buffer( - device_id: DeviceId, - desc: &resource::BufferDescriptor, -) -> BufferId { - gfx_select!(device_id => device_create_buffer(&*GLOBAL, device_id, desc, PhantomData)) + device_id: id::DeviceId, + desc: &core::resource::BufferDescriptor, +) -> id::BufferId { + gfx_select!(device_id => GLOBAL.device_create_buffer(device_id, desc, PhantomData)) } -pub fn device_create_buffer_mapped( - global: &Global, - device_id: DeviceId, - desc: &resource::BufferDescriptor, - mapped_ptr_out: *mut *mut u8, - id_in: Input, -) -> Output { - let hub = B::hub(global); - let mut token = Token::root(); - let mut desc = desc.clone(); - desc.usage |= resource::BufferUsage::MAP_WRITE; - - let (device_guard, _) = hub.devices.read(&mut token); - let device = &device_guard[device_id]; - let mut buffer = device.create_buffer(device_id, &desc); - - match map_buffer(&device.raw, &mut buffer, 0 .. desc.size, HostMap::Write) { - Ok(ptr) => unsafe { - *mapped_ptr_out = ptr; - }, - Err(e) => { - log::error!("failed to create buffer in a mapped state: {:?}", e); - unsafe { - *mapped_ptr_out = ptr::null_mut(); - } - } - } - - let (id, id_out) = hub.buffers.new_identity(id_in); - let ok = device.trackers.lock().buffers.init( - id, - &buffer.life_guard.ref_count, - (), - resource::BufferUsage::MAP_WRITE, - ); - assert!(ok); - - hub.buffers.register(id, buffer, &mut token); - id_out -} - -#[cfg(feature = "local")] #[no_mangle] pub extern "C" fn wgpu_device_create_buffer_mapped( - device_id: DeviceId, - desc: &resource::BufferDescriptor, + device_id: id::DeviceId, + desc: &core::resource::BufferDescriptor, mapped_ptr_out: *mut *mut u8, -) -> BufferId { - gfx_select!(device_id => device_create_buffer_mapped(&*GLOBAL, device_id, desc, mapped_ptr_out, PhantomData)) +) -> id::BufferId { + gfx_select!(device_id => GLOBAL.device_create_buffer_mapped(device_id, desc, mapped_ptr_out, PhantomData)) } -pub fn buffer_destroy(global: &Global, buffer_id: BufferId) { - let hub = B::hub(global); - let mut token = Token::root(); - let (device_guard, mut token) = hub.devices.read(&mut token); - let (buffer_guard, _) = hub.buffers.read(&mut token); - let buffer = &buffer_guard[buffer_id]; - device_guard[buffer.device_id.value].pending.lock().destroy( - ResourceId::Buffer(buffer_id), - buffer.life_guard.ref_count.clone(), - ); -} - -#[cfg(feature = "local")] #[no_mangle] -pub extern "C" fn wgpu_buffer_destroy(buffer_id: BufferId) { - gfx_select!(buffer_id => buffer_destroy(&*GLOBAL, buffer_id)) +pub extern "C" fn wgpu_buffer_destroy(buffer_id: id::BufferId) { + gfx_select!(buffer_id => GLOBAL.buffer_destroy(buffer_id)) } -pub fn device_create_texture( - global: &Global, - device_id: DeviceId, - desc: &resource::TextureDescriptor, - id_in: Input, -) -> Output { - let hub = B::hub(global); - let mut token = Token::root(); - - let (device_guard, _) = hub.devices.read(&mut token); - let device = &device_guard[device_id]; - let texture = device.create_texture(device_id, desc); - - let (id, id_out) = hub.textures.new_identity(id_in); - let ok = device.trackers.lock().textures.init( - id, - &texture.life_guard.ref_count, - texture.full_range.clone(), - resource::TextureUsage::UNINITIALIZED, - ); - assert!(ok); - - hub.textures.register(id, texture, &mut token); - id_out -} - -#[cfg(feature = "local")] #[no_mangle] pub extern "C" fn wgpu_device_create_texture( - device_id: DeviceId, - desc: &resource::TextureDescriptor, -) -> TextureId { - gfx_select!(device_id => device_create_texture(&*GLOBAL, device_id, desc, PhantomData)) + device_id: id::DeviceId, + desc: &core::resource::TextureDescriptor, +) -> id::TextureId { + gfx_select!(device_id => GLOBAL.device_create_texture(device_id, desc, PhantomData)) } -pub fn texture_create_view( - global: &Global, - texture_id: TextureId, - desc: Option<&resource::TextureViewDescriptor>, - id_in: Input, -) -> Output { - let hub = B::hub(global); - let mut token = Token::root(); - - let (device_guard, mut token) = hub.devices.read(&mut token); - let (texture_guard, mut token) = hub.textures.read(&mut token); - let texture = &texture_guard[texture_id]; - let device = &device_guard[texture.device_id.value]; - - let (format, view_kind, range) = match desc { - Some(desc) => { - let kind = conv::map_texture_view_dimension(desc.dimension); - let end_level = if desc.level_count == 0 { - texture.full_range.levels.end - } else { - (desc.base_mip_level + desc.level_count) as u8 - }; - let end_layer = if desc.array_layer_count == 0 { - texture.full_range.layers.end - } else { - (desc.base_array_layer + desc.array_layer_count) as u16 - }; - let range = hal::image::SubresourceRange { - aspects: match desc.aspect { - resource::TextureAspect::All => texture.full_range.aspects, - resource::TextureAspect::DepthOnly => hal::format::Aspects::DEPTH, - resource::TextureAspect::StencilOnly => hal::format::Aspects::STENCIL, - }, - levels: desc.base_mip_level as u8 .. end_level, - layers: desc.base_array_layer as u16 .. end_layer, - }; - (desc.format, kind, range) - } - None => { - let kind = match texture.kind { - hal::image::Kind::D1(_, 1) => hal::image::ViewKind::D1, - hal::image::Kind::D1(..) => hal::image::ViewKind::D1Array, - hal::image::Kind::D2(_, _, 1, _) => hal::image::ViewKind::D2, - hal::image::Kind::D2(..) => hal::image::ViewKind::D2Array, - hal::image::Kind::D3(..) => hal::image::ViewKind::D3, - }; - (texture.format, kind, texture.full_range.clone()) - } - }; - - let raw = unsafe { - device - .raw - .create_image_view( - &texture.raw, - view_kind, - conv::map_texture_format(format, device.features), - hal::format::Swizzle::NO, - range.clone(), - ) - .unwrap() - }; - - let view = resource::TextureView { - inner: resource::TextureViewInner::Native { - raw, - source_id: Stored { - value: texture_id, - ref_count: texture.life_guard.ref_count.clone(), - }, - }, - format: texture.format, - extent: texture.kind.extent().at_level(range.levels.start), - samples: texture.kind.num_samples(), - range, - life_guard: LifeGuard::new(), - }; - - let (id, id_out) = hub.texture_views.new_identity(id_in); - let ok = device - .trackers - .lock() - .views - .init(id, &view.life_guard.ref_count, (), ()); - assert!(ok); - - hub.texture_views.register(id, view, &mut token); - id_out +#[no_mangle] +pub extern "C" fn wgpu_texture_destroy(texture_id: id::TextureId) { + gfx_select!(texture_id => GLOBAL.texture_destroy(texture_id)) } -#[cfg(feature = "local")] #[no_mangle] pub extern "C" fn wgpu_texture_create_view( - texture_id: TextureId, - desc: Option<&resource::TextureViewDescriptor>, -) -> TextureViewId { - gfx_select!(texture_id => texture_create_view(&*GLOBAL, texture_id, desc, PhantomData)) + texture_id: id::TextureId, + desc: Option<&core::resource::TextureViewDescriptor>, +) -> id::TextureViewId { + gfx_select!(texture_id => GLOBAL.texture_create_view(texture_id, desc, PhantomData)) } -pub fn texture_destroy(global: &Global, texture_id: TextureId) { - let hub = B::hub(global); - let mut token = Token::root(); - - let (device_guard, mut token) = hub.devices.read(&mut token); - let (texture_guard, _) = hub.textures.read(&mut token); - let texture = &texture_guard[texture_id]; - device_guard[texture.device_id.value] - .pending - .lock() - .destroy( - ResourceId::Texture(texture_id), - texture.life_guard.ref_count.clone(), - ); -} - -#[cfg(feature = "local")] #[no_mangle] -pub extern "C" fn wgpu_texture_destroy(texture_id: TextureId) { - gfx_select!(texture_id => texture_destroy(&*GLOBAL, texture_id)) +pub extern "C" fn wgpu_texture_view_destroy(texture_view_id: id::TextureViewId) { + gfx_select!(texture_view_id => GLOBAL.texture_view_destroy(texture_view_id)) } -pub fn texture_view_destroy(global: &Global, texture_view_id: TextureViewId) { - let hub = B::hub(global); - let mut token = Token::root(); - let (device_guard, mut token) = hub.devices.read(&mut token); - let (texture_guard, mut token) = hub.textures.read(&mut token); - let (texture_view_guard, _) = hub.texture_views.read(&mut token); - let view = &texture_view_guard[texture_view_id]; - let device_id = match view.inner { - resource::TextureViewInner::Native { ref source_id, .. } => { - texture_guard[source_id.value].device_id.value - } - resource::TextureViewInner::SwapChain { .. } => panic!("Can't destroy a swap chain image"), - }; - device_guard[device_id].pending.lock().destroy( - ResourceId::TextureView(texture_view_id), - view.life_guard.ref_count.clone(), - ); -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_texture_view_destroy(texture_view_id: TextureViewId) { - gfx_select!(texture_view_id => texture_view_destroy(&*GLOBAL, texture_view_id)) -} - -pub fn device_create_sampler( - global: &Global, - device_id: DeviceId, - desc: &resource::SamplerDescriptor, - id_in: Input, -) -> Output { - let hub = B::hub(global); - let mut token = Token::root(); - let (device_guard, mut token) = hub.devices.read(&mut token); - let device = &device_guard[device_id]; - - let info = hal::image::SamplerDesc { - min_filter: conv::map_filter(desc.min_filter), - mag_filter: conv::map_filter(desc.mag_filter), - mip_filter: conv::map_filter(desc.mipmap_filter), - wrap_mode: ( - conv::map_wrap(desc.address_mode_u), - conv::map_wrap(desc.address_mode_v), - conv::map_wrap(desc.address_mode_w), - ), - lod_bias: hal::image::Lod(0.0), - lod_range: hal::image::Lod(desc.lod_min_clamp) .. hal::image::Lod(desc.lod_max_clamp), - comparison: if desc.compare_function == resource::CompareFunction::Always { - None - } else { - Some(conv::map_compare_function(desc.compare_function)) - }, - border: hal::image::PackedColor(0), - normalized: true, - anisotropic: hal::image::Anisotropic::Off, //TODO - }; - - let sampler = resource::Sampler { - raw: unsafe { device.raw.create_sampler(&info).unwrap() }, - device_id: Stored { - value: device_id, - ref_count: device.life_guard.ref_count.clone(), - }, - life_guard: LifeGuard::new(), - }; - hub.samplers.register_identity(id_in, sampler, &mut token) -} - -#[cfg(feature = "local")] #[no_mangle] pub extern "C" fn wgpu_device_create_sampler( - device_id: DeviceId, - desc: &resource::SamplerDescriptor, -) -> SamplerId { - gfx_select!(device_id => device_create_sampler(&*GLOBAL, device_id, desc, PhantomData)) + device_id: id::DeviceId, + desc: &core::resource::SamplerDescriptor, +) -> id::SamplerId { + gfx_select!(device_id => GLOBAL.device_create_sampler(device_id, desc, PhantomData)) } -pub fn sampler_destroy(global: &Global, sampler_id: SamplerId) { - let hub = B::hub(global); - let mut token = Token::root(); - let (device_guard, mut token) = hub.devices.read(&mut token); - let (sampler_guard, _) = hub.samplers.read(&mut token); - let sampler = &sampler_guard[sampler_id]; - device_guard[sampler.device_id.value] - .pending - .lock() - .destroy( - ResourceId::Sampler(sampler_id), - sampler.life_guard.ref_count.clone(), - ); -} - -#[cfg(feature = "local")] #[no_mangle] -pub extern "C" fn wgpu_sampler_destroy(sampler_id: SamplerId) { - gfx_select!(sampler_id => sampler_destroy(&*GLOBAL, sampler_id)) +pub extern "C" fn wgpu_sampler_destroy(sampler_id: id::SamplerId) { + gfx_select!(sampler_id => GLOBAL.sampler_destroy(sampler_id)) } -pub fn device_create_bind_group_layout( - global: &Global, - device_id: DeviceId, - desc: &binding_model::BindGroupLayoutDescriptor, - id_in: Input, -) -> Output { - let mut token = Token::root(); - let hub = B::hub(global); - let bindings = unsafe { slice::from_raw_parts(desc.bindings, desc.bindings_length) }; - - let raw_bindings = bindings - .iter() - .map(|binding| hal::pso::DescriptorSetLayoutBinding { - binding: binding.binding, - ty: conv::map_binding_type(binding), - count: 1, //TODO: consolidate - stage_flags: conv::map_shader_stage_flags(binding.visibility), - immutable_samplers: false, // TODO - }) - .collect::>(); //TODO: avoid heap allocation - - let raw = unsafe { - let (device_guard, _) = hub.devices.read(&mut token); - device_guard[device_id] - .raw - .create_descriptor_set_layout(&raw_bindings, &[]) - .unwrap() - }; - - let layout = binding_model::BindGroupLayout { - raw, - bindings: bindings.to_vec(), - desc_ranges: DescriptorRanges::from_bindings(&raw_bindings), - dynamic_count: bindings.iter().filter(|b| b.dynamic).count(), - }; - - hub.bind_group_layouts - .register_identity(id_in, layout, &mut token) -} - -#[cfg(feature = "local")] #[no_mangle] pub extern "C" fn wgpu_device_create_bind_group_layout( - device_id: DeviceId, - desc: &binding_model::BindGroupLayoutDescriptor, -) -> BindGroupLayoutId { - gfx_select!(device_id => device_create_bind_group_layout(&*GLOBAL, device_id, desc, PhantomData)) + device_id: id::DeviceId, + desc: &core::binding_model::BindGroupLayoutDescriptor, +) -> id::BindGroupLayoutId { + gfx_select!(device_id => GLOBAL.device_create_bind_group_layout(device_id, desc, PhantomData)) } -pub fn device_create_pipeline_layout( - global: &Global, - device_id: DeviceId, - desc: &binding_model::PipelineLayoutDescriptor, - id_in: Input, -) -> Output { - let hub = B::hub(global); - let mut token = Token::root(); - - let (device_guard, mut token) = hub.devices.read(&mut token); - let device = &device_guard[device_id]; - let bind_group_layout_ids = - unsafe { slice::from_raw_parts(desc.bind_group_layouts, desc.bind_group_layouts_length) }; - - assert!(desc.bind_group_layouts_length <= (device.features.max_bind_groups as usize), - "Cannot set a bind group which is beyond the `max_bind_groups` limit requested on device creation"); - - // TODO: push constants - let pipeline_layout = { - let (bind_group_layout_guard, _) = hub.bind_group_layouts.read(&mut token); - let descriptor_set_layouts = bind_group_layout_ids - .iter() - .map(|&id| &bind_group_layout_guard[id].raw); - unsafe { - device.raw.create_pipeline_layout(descriptor_set_layouts, &[]) - } - .unwrap() - }; - - let layout = binding_model::PipelineLayout { - raw: pipeline_layout, - bind_group_layout_ids: bind_group_layout_ids.iter().cloned().collect(), - }; - hub.pipeline_layouts - .register_identity(id_in, layout, &mut token) -} - -#[cfg(feature = "local")] #[no_mangle] pub extern "C" fn wgpu_device_create_pipeline_layout( - device_id: DeviceId, - desc: &binding_model::PipelineLayoutDescriptor, -) -> PipelineLayoutId { - gfx_select!(device_id => device_create_pipeline_layout(&*GLOBAL, device_id, desc, PhantomData)) + device_id: id::DeviceId, + desc: &core::binding_model::PipelineLayoutDescriptor, +) -> id::PipelineLayoutId { + gfx_select!(device_id => GLOBAL.device_create_pipeline_layout(device_id, desc, PhantomData)) } -pub fn device_create_bind_group( - global: &Global, - device_id: DeviceId, - desc: &binding_model::BindGroupDescriptor, - id_in: Input, -) -> Output { - let hub = B::hub(global); - let mut token = Token::root(); - - let (device_guard, mut token) = hub.devices.read(&mut token); - let device = &device_guard[device_id]; - let (bind_group_layout_guard, _) = hub.bind_group_layouts.read(&mut token); - let bind_group_layout = &bind_group_layout_guard[desc.layout]; - let bindings = unsafe { slice::from_raw_parts(desc.bindings, desc.bindings_length as usize) }; - assert_eq!(bindings.len(), bind_group_layout.bindings.len()); - - let desc_set = unsafe { - let mut desc_sets = ArrayVec::<[_; 1]>::new(); - device - .desc_allocator - .lock() - .allocate( - &device.raw, - &bind_group_layout.raw, - bind_group_layout.desc_ranges, - 1, - &mut desc_sets, - ) - .unwrap(); - desc_sets.pop().unwrap() - }; - - // fill out the descriptors - let mut used = TrackerSet::new(B::VARIANT); - { - let (buffer_guard, mut token) = hub.buffers.read(&mut token); - let (texture_guard, mut token) = hub.textures.read(&mut token); //skip token - let (texture_view_guard, mut token) = hub.texture_views.read(&mut token); - let (sampler_guard, _) = hub.samplers.read(&mut token); - - //TODO: group writes into contiguous sections - let mut writes = Vec::new(); - for (b, decl) in bindings.iter().zip(&bind_group_layout.bindings) { - let descriptor = match b.resource { - binding_model::BindingResource::Buffer(ref bb) => { - let (alignment, usage) = match decl.ty { - binding_model::BindingType::UniformBuffer => { - (BIND_BUFFER_ALIGNMENT, resource::BufferUsage::UNIFORM) - } - binding_model::BindingType::StorageBuffer => { - (BIND_BUFFER_ALIGNMENT, resource::BufferUsage::STORAGE) - } - binding_model::BindingType::ReadonlyStorageBuffer => { - (BIND_BUFFER_ALIGNMENT, resource::BufferUsage::STORAGE_READ) - } - binding_model::BindingType::Sampler - | binding_model::BindingType::SampledTexture - | binding_model::BindingType::StorageTexture => { - panic!("Mismatched buffer binding for {:?}", decl) - } - }; - assert_eq!( - bb.offset as hal::buffer::Offset % alignment, - 0, - "Misaligned buffer offset {}", - bb.offset - ); - let buffer = used - .buffers - .use_extend(&*buffer_guard, bb.buffer, (), usage) - .unwrap(); - assert!( - buffer.usage.contains(usage), - "Expected buffer usage {:?}", - usage - ); - - let end = if bb.size == 0 { - None - } else { - let end = bb.offset + bb.size; - assert!( - end <= buffer.size, - "Bound buffer range {:?} does not fit in buffer size {}", - bb.offset .. end, - buffer.size - ); - Some(end) - }; - - let range = Some(bb.offset) .. end; - hal::pso::Descriptor::Buffer(&buffer.raw, range) - } - binding_model::BindingResource::Sampler(id) => { - assert_eq!(decl.ty, binding_model::BindingType::Sampler); - let sampler = used - .samplers - .use_extend(&*sampler_guard, id, (), ()) - .unwrap(); - hal::pso::Descriptor::Sampler(&sampler.raw) - } - binding_model::BindingResource::TextureView(id) => { - let (usage, image_layout) = match decl.ty { - binding_model::BindingType::SampledTexture => ( - resource::TextureUsage::SAMPLED, - hal::image::Layout::ShaderReadOnlyOptimal, - ), - binding_model::BindingType::StorageTexture => { - (resource::TextureUsage::STORAGE, hal::image::Layout::General) - } - _ => panic!("Mismatched texture binding for {:?}", decl), - }; - let view = used - .views - .use_extend(&*texture_view_guard, id, (), ()) - .unwrap(); - match view.inner { - resource::TextureViewInner::Native { - ref raw, - ref source_id, - } => { - let texture = used - .textures - .use_extend( - &*texture_guard, - source_id.value, - view.range.clone(), - usage, - ) - .unwrap(); - assert!(texture.usage.contains(usage)); - - hal::pso::Descriptor::Image(raw, image_layout) - } - resource::TextureViewInner::SwapChain { .. } => { - panic!("Unable to create a bind group with a swap chain image") - } - } - } - }; - writes.alloc().init(hal::pso::DescriptorSetWrite { - set: desc_set.raw(), - binding: b.binding, - array_offset: 0, //TODO - descriptors: iter::once(descriptor), - }); - } - - unsafe { - device.raw.write_descriptor_sets(writes); - } - } - - let bind_group = binding_model::BindGroup { - raw: desc_set, - device_id: Stored { - value: device_id, - ref_count: device.life_guard.ref_count.clone(), - }, - layout_id: desc.layout, - life_guard: LifeGuard::new(), - used, - dynamic_count: bind_group_layout.dynamic_count, - }; - let (id, id_out) = hub.bind_groups.new_identity(id_in); - let ok = device - .trackers - .lock() - .bind_groups - .init(id, &bind_group.life_guard.ref_count, (), ()); - assert!(ok); - - hub.bind_groups.register(id, bind_group, &mut token); - id_out -} - -#[cfg(feature = "local")] #[no_mangle] pub extern "C" fn wgpu_device_create_bind_group( - device_id: DeviceId, - desc: &binding_model::BindGroupDescriptor, -) -> BindGroupId { - gfx_select!(device_id => device_create_bind_group(&*GLOBAL, device_id, desc, PhantomData)) + device_id: id::DeviceId, + desc: &core::binding_model::BindGroupDescriptor, +) -> id::BindGroupId { + gfx_select!(device_id => GLOBAL.device_create_bind_group(device_id, desc, PhantomData)) } -pub fn bind_group_destroy(global: &Global, bind_group_id: BindGroupId) { - let hub = B::hub(global); - let mut token = Token::root(); - let (device_guard, mut token) = hub.devices.read(&mut token); - let (bind_group_guard, _) = hub.bind_groups.read(&mut token); - let bind_group = &bind_group_guard[bind_group_id]; - device_guard[bind_group.device_id.value] - .pending - .lock() - .destroy( - ResourceId::BindGroup(bind_group_id), - bind_group.life_guard.ref_count.clone(), - ); -} - -#[cfg(feature = "local")] #[no_mangle] -pub extern "C" fn wgpu_bind_group_destroy(bind_group_id: BindGroupId) { - gfx_select!(bind_group_id => bind_group_destroy(&*GLOBAL, bind_group_id)) +pub extern "C" fn wgpu_bind_group_destroy(bind_group_id: id::BindGroupId) { + gfx_select!(bind_group_id => GLOBAL.bind_group_destroy(bind_group_id)) } -pub fn device_create_shader_module( - global: &Global, - device_id: DeviceId, - desc: &pipeline::ShaderModuleDescriptor, - id_in: Input, -) -> Output { - let hub = B::hub(global); - let mut token = Token::root(); - - let spv = unsafe { slice::from_raw_parts(desc.code.bytes, desc.code.length) }; - let shader = { - let (device_guard, _) = hub.devices.read(&mut token); - ShaderModule { - raw: unsafe { - device_guard[device_id] - .raw - .create_shader_module(spv) - .unwrap() - }, - } - }; - hub.shader_modules - .register_identity(id_in, shader, &mut token) -} - -#[cfg(feature = "local")] #[no_mangle] pub extern "C" fn wgpu_device_create_shader_module( - device_id: DeviceId, - desc: &pipeline::ShaderModuleDescriptor, -) -> ShaderModuleId { - gfx_select!(device_id => device_create_shader_module(&*GLOBAL, device_id, desc, PhantomData)) + device_id: id::DeviceId, + desc: &core::pipeline::ShaderModuleDescriptor, +) -> id::ShaderModuleId { + gfx_select!(device_id => GLOBAL.device_create_shader_module(device_id, desc, PhantomData)) } -pub fn device_create_command_encoder( - global: &Global, - device_id: DeviceId, - _desc: &command::CommandEncoderDescriptor, - id_in: Input, -) -> Output { - let hub = B::hub(global); - let mut token = Token::root(); - - let (device_guard, mut token) = hub.devices.read(&mut token); - let device = &device_guard[device_id]; - - let dev_stored = Stored { - value: device_id, - ref_count: device.life_guard.ref_count.clone(), - }; - let mut comb = device - .com_allocator - .allocate(dev_stored, &device.raw, device.features); - unsafe { - comb.raw.last_mut().unwrap().begin( - hal::command::CommandBufferFlags::ONE_TIME_SUBMIT, - hal::command::CommandBufferInheritanceInfo::default(), - ); - } - - hub.command_buffers - .register_identity(id_in, comb, &mut token) -} - -#[cfg(feature = "local")] #[no_mangle] pub extern "C" fn wgpu_device_create_command_encoder( - device_id: DeviceId, - desc: Option<&command::CommandEncoderDescriptor>, -) -> CommandEncoderId { + device_id: id::DeviceId, + desc: Option<&core::command::CommandEncoderDescriptor>, +) -> id::CommandEncoderId { let desc = &desc.cloned().unwrap_or_default(); - gfx_select!(device_id => device_create_command_encoder(&*GLOBAL, device_id, desc, PhantomData)) + gfx_select!(device_id => GLOBAL.device_create_command_encoder(device_id, desc, PhantomData)) } -#[cfg(feature = "local")] #[no_mangle] -pub extern "C" fn wgpu_device_get_queue(device_id: DeviceId) -> QueueId { +pub extern "C" fn wgpu_device_get_queue(device_id: id::DeviceId) -> id::QueueId { device_id } -pub fn queue_submit( - global: &Global, - queue_id: QueueId, - command_buffer_ids: &[CommandBufferId], -) { - let hub = B::hub(global); - - let (submit_index, fence) = { - let mut token = Token::root(); - let (mut device_guard, mut token) = hub.devices.write(&mut token); - let (swap_chain_guard, mut token) = hub.swap_chains.read(&mut token); - let device = &mut device_guard[queue_id]; - - let mut trackers = device.trackers.lock(); - let mut signal_semaphores = Vec::new(); - - let submit_index = 1 + device - .life_guard - .submission_index - .fetch_add(1, Ordering::Relaxed); - - let (mut command_buffer_guard, mut token) = hub.command_buffers.write(&mut token); - let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token); - let (buffer_guard, mut token) = hub.buffers.read(&mut token); - let (texture_guard, mut token) = hub.textures.read(&mut token); - let (mut texture_view_guard, mut token) = hub.texture_views.write(&mut token); - let (sampler_guard, _) = hub.samplers.read(&mut token); - - //TODO: if multiple command buffers are submitted, we can re-use the last - // native command buffer of the previous chain instead of always creating - // a temporary one, since the chains are not finished. - - // finish all the command buffers first - for &cmb_id in command_buffer_ids { - let comb = &mut command_buffer_guard[cmb_id]; - - if let Some((view_id, fbo)) = comb.used_swap_chain.take() { - match texture_view_guard[view_id.value].inner { - resource::TextureViewInner::Native { .. } => unreachable!(), - resource::TextureViewInner::SwapChain { - ref source_id, - ref mut framebuffers, - .. - } => { - if framebuffers.is_empty() { - let sem = &swap_chain_guard[source_id.value].semaphore; - signal_semaphores.push(sem); - } - framebuffers.push(fbo); - } - }; - } - - // optimize the tracked states - comb.trackers.optimize(); - - // update submission IDs - for id in comb.trackers.buffers.used() { - let buffer = &buffer_guard[id]; - assert!(buffer.pending_map_operation.is_none()); - buffer - .life_guard - .submission_index - .store(submit_index, Ordering::Release); - } - for id in comb.trackers.textures.used() { - texture_guard[id] - .life_guard - .submission_index - .store(submit_index, Ordering::Release); - } - for id in comb.trackers.views.used() { - texture_view_guard[id] - .life_guard - .submission_index - .store(submit_index, Ordering::Release); - } - for id in comb.trackers.bind_groups.used() { - bind_group_guard[id] - .life_guard - .submission_index - .store(submit_index, Ordering::Release); - } - for id in comb.trackers.samplers.used() { - sampler_guard[id] - .life_guard - .submission_index - .store(submit_index, Ordering::Release); - } - - // execute resource transitions - let mut transit = device.com_allocator.extend(comb); - unsafe { - transit.begin( - hal::command::CommandBufferFlags::ONE_TIME_SUBMIT, - hal::command::CommandBufferInheritanceInfo::default(), - ); - } - log::trace!("Stitching command buffer {:?} before submission", cmb_id); - command::CommandBuffer::insert_barriers( - &mut transit, - &mut *trackers, - &comb.trackers, - Stitch::Init, - &*buffer_guard, - &*texture_guard, - ); - unsafe { - transit.finish(); - } - comb.raw.insert(0, transit); - unsafe { - comb.raw.last_mut().unwrap().finish(); - } - } - - // now prepare the GPU submission - let fence = device.raw.create_fence(false).unwrap(); - let submission = hal::queue::Submission::<_, _, Vec<&B::Semaphore>> { - command_buffers: command_buffer_ids - .iter() - .flat_map(|&cmb_id| &command_buffer_guard[cmb_id].raw), - wait_semaphores: Vec::new(), - signal_semaphores, - }; - - unsafe { - device.queue_group.queues[0].submit(submission, Some(&fence)); - } - - (submit_index, fence) - }; - - // No need for write access to the device from here on out - let callbacks = { - let mut token = Token::root(); - let (device_guard, mut token) = hub.devices.read(&mut token); - let device = &device_guard[queue_id]; - - let callbacks = device.maintain(global, false, &mut token); - device.pending.lock().active.alloc().init(ActiveSubmission { - index: submit_index, - fence, - resources: Vec::new(), - mapped: Vec::new(), - }); - - // finally, return the command buffers to the allocator - for &cmb_id in command_buffer_ids { - let (cmd_buf, _) = hub.command_buffers.unregister(cmb_id, &mut token); - device.com_allocator.after_submit(cmd_buf, submit_index); - } - - callbacks - }; - - Device::::fire_map_callbacks(callbacks); -} - -#[cfg(feature = "local")] #[no_mangle] pub extern "C" fn wgpu_queue_submit( - queue_id: QueueId, - command_buffers: *const CommandBufferId, + queue_id: id::QueueId, + command_buffers: *const id::CommandBufferId, command_buffers_length: usize, ) { let command_buffer_ids = unsafe { slice::from_raw_parts(command_buffers, command_buffers_length) }; - gfx_select!(queue_id => queue_submit(&*GLOBAL, queue_id, command_buffer_ids)) + gfx_select!(queue_id => GLOBAL.queue_submit(queue_id, command_buffer_ids)) } -pub fn device_create_render_pipeline( - global: &Global, - device_id: DeviceId, - desc: &pipeline::RenderPipelineDescriptor, - id_in: Input, -) -> Output { - let hub = B::hub(global); - let mut token = Token::root(); - - let sc = desc.sample_count; - assert!( - sc == 1 || sc == 2 || sc == 4 || sc == 8 || sc == 16 || sc == 32, - "Invalid sample_count of {}", - sc - ); - let sc = sc as u8; - - let color_states = - unsafe { slice::from_raw_parts(desc.color_states, desc.color_states_length) }; - let depth_stencil_state = unsafe { desc.depth_stencil_state.as_ref() }; - - let rasterizer = conv::map_rasterization_state_descriptor( - &unsafe { desc.rasterization_state.as_ref() } - .cloned() - .unwrap_or_default(), - ); - - let desc_vbs = unsafe { - slice::from_raw_parts( - desc.vertex_input.vertex_buffers, - desc.vertex_input.vertex_buffers_length, - ) - }; - let mut vertex_strides = Vec::with_capacity(desc_vbs.len()); - let mut vertex_buffers = Vec::with_capacity(desc_vbs.len()); - let mut attributes = Vec::new(); - for (i, vb_state) in desc_vbs.iter().enumerate() { - vertex_strides - .alloc() - .init((vb_state.stride, vb_state.step_mode)); - if vb_state.attributes_length == 0 { - continue; - } - vertex_buffers.alloc().init(hal::pso::VertexBufferDesc { - binding: i as u32, - stride: vb_state.stride as u32, - rate: match vb_state.step_mode { - pipeline::InputStepMode::Vertex => hal::pso::VertexInputRate::Vertex, - pipeline::InputStepMode::Instance => hal::pso::VertexInputRate::Instance(1), - }, - }); - let desc_atts = - unsafe { slice::from_raw_parts(vb_state.attributes, vb_state.attributes_length) }; - for attribute in desc_atts { - assert_eq!(0, attribute.offset >> 32); - attributes.alloc().init(hal::pso::AttributeDesc { - location: attribute.shader_location, - binding: i as u32, - element: hal::pso::Element { - format: conv::map_vertex_format(attribute.format), - offset: attribute.offset as u32, - }, - }); - } - } - - let input_assembler = hal::pso::InputAssemblerDesc { - primitive: conv::map_primitive_topology(desc.primitive_topology), - with_adjacency: false, - restart_index: None, //TODO - }; - - let blender = hal::pso::BlendDesc { - logic_op: None, // TODO - targets: color_states - .iter() - .map(conv::map_color_state_descriptor) - .collect(), - }; - let depth_stencil = depth_stencil_state - .map(conv::map_depth_stencil_state_descriptor) - .unwrap_or_default(); - - let multisampling: Option = if sc == 1 { - None - } else { - Some(hal::pso::Multisampling { - rasterization_samples: sc, - sample_shading: None, - sample_mask: desc.sample_mask as u64, - alpha_coverage: desc.alpha_to_coverage_enabled, - alpha_to_one: false, - }) - }; - - // TODO - let baked_states = hal::pso::BakedStates { - viewport: None, - scissor: None, - blend_color: None, - depth_bounds: None, - }; - - let raw_pipeline = { - let (device_guard, mut token) = hub.devices.read(&mut token); - let device = &device_guard[device_id]; - let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token); - let layout = &pipeline_layout_guard[desc.layout].raw; - let (shader_module_guard, _) = hub.shader_modules.read(&mut token); - - let rp_key = RenderPassKey { - colors: color_states - .iter() - .map(|at| hal::pass::Attachment { - format: Some(conv::map_texture_format(at.format, device.features)), - samples: sc, - ops: hal::pass::AttachmentOps::PRESERVE, - stencil_ops: hal::pass::AttachmentOps::DONT_CARE, - layouts: hal::image::Layout::General .. hal::image::Layout::General, - }) - .collect(), - // We can ignore the resolves as the vulkan specs says: - // As an additional special case, if two render passes have a single subpass, - // they are compatible even if they have different resolve attachment references - // or depth/stencil resolve modes but satisfy the other compatibility conditions. - resolves: ArrayVec::new(), - depth_stencil: depth_stencil_state.map(|at| hal::pass::Attachment { - format: Some(conv::map_texture_format(at.format, device.features)), - samples: sc, - ops: hal::pass::AttachmentOps::PRESERVE, - stencil_ops: hal::pass::AttachmentOps::PRESERVE, - layouts: hal::image::Layout::General .. hal::image::Layout::General, - }), - }; - - let mut render_pass_cache = device.render_passes.lock(); - let main_pass = match render_pass_cache.entry(rp_key) { - Entry::Occupied(e) => e.into_mut(), - Entry::Vacant(e) => { - let color_ids = [ - (0, hal::image::Layout::ColorAttachmentOptimal), - (1, hal::image::Layout::ColorAttachmentOptimal), - (2, hal::image::Layout::ColorAttachmentOptimal), - (3, hal::image::Layout::ColorAttachmentOptimal), - ]; - - let depth_id = ( - desc.color_states_length, - hal::image::Layout::DepthStencilAttachmentOptimal, - ); - - let subpass = hal::pass::SubpassDesc { - colors: &color_ids[.. desc.color_states_length], - depth_stencil: depth_stencil_state.map(|_| &depth_id), - inputs: &[], - resolves: &[], - preserves: &[], - }; - - let pass = unsafe { - device - .raw - .create_render_pass(e.key().all(), &[subpass], &[]) - } - .unwrap(); - e.insert(pass) - } - }; - - let vertex = hal::pso::EntryPoint:: { - entry: unsafe { ffi::CStr::from_ptr(desc.vertex_stage.entry_point) } - .to_str() - .to_owned() - .unwrap(), // TODO - module: &shader_module_guard[desc.vertex_stage.module].raw, - specialization: hal::pso::Specialization::EMPTY, - }; - let fragment = - unsafe { desc.fragment_stage.as_ref() }.map(|stage| hal::pso::EntryPoint:: { - entry: unsafe { ffi::CStr::from_ptr(stage.entry_point) } - .to_str() - .to_owned() - .unwrap(), // TODO - module: &shader_module_guard[stage.module].raw, - specialization: hal::pso::Specialization::EMPTY, - }); - - let shaders = hal::pso::GraphicsShaderSet { - vertex, - hull: None, - domain: None, - geometry: None, - fragment, - }; - - let subpass = hal::pass::Subpass { - index: 0, - main_pass, - }; - - // TODO - let flags = hal::pso::PipelineCreationFlags::empty(); - // TODO - let parent = hal::pso::BasePipeline::None; - - let pipeline_desc = hal::pso::GraphicsPipelineDesc { - shaders, - rasterizer, - vertex_buffers, - attributes, - input_assembler, - blender, - depth_stencil, - multisampling, - baked_states, - layout, - subpass, - flags, - parent, - }; - - // TODO: cache - unsafe { - device - .raw - .create_graphics_pipeline(&pipeline_desc, None) - .unwrap() - } - }; - - let pass_context = RenderPassContext { - colors: color_states.iter().map(|state| state.format).collect(), - resolves: ArrayVec::new(), - depth_stencil: depth_stencil_state.map(|state| state.format), - }; - - let mut flags = pipeline::PipelineFlags::empty(); - for state in color_states { - if state.color_blend.uses_color() | state.alpha_blend.uses_color() { - flags |= pipeline::PipelineFlags::BLEND_COLOR; - } - } - if let Some(ds) = depth_stencil_state { - if ds.needs_stencil_reference() { - flags |= pipeline::PipelineFlags::STENCIL_REFERENCE; - } - } - - let pipeline = pipeline::RenderPipeline { - raw: raw_pipeline, - layout_id: desc.layout, - pass_context, - flags, - index_format: desc.vertex_input.index_format, - vertex_strides, - sample_count: sc, - }; - - hub.render_pipelines - .register_identity(id_in, pipeline, &mut token) -} - -#[cfg(feature = "local")] #[no_mangle] pub extern "C" fn wgpu_device_create_render_pipeline( - device_id: DeviceId, - desc: &pipeline::RenderPipelineDescriptor, -) -> RenderPipelineId { - gfx_select!(device_id => device_create_render_pipeline(&*GLOBAL, device_id, desc, PhantomData)) + device_id: id::DeviceId, + desc: &core::pipeline::RenderPipelineDescriptor, +) -> id::RenderPipelineId { + gfx_select!(device_id => GLOBAL.device_create_render_pipeline(device_id, desc, PhantomData)) } -pub fn device_create_compute_pipeline( - global: &Global, - device_id: DeviceId, - desc: &pipeline::ComputePipelineDescriptor, - id_in: Input, -) -> Output { - let hub = B::hub(global); - let mut token = Token::root(); - - let raw_pipeline = { - let (device_guard, mut token) = hub.devices.read(&mut token); - let device = &device_guard[device_id].raw; - let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token); - let layout = &pipeline_layout_guard[desc.layout].raw; - let pipeline_stage = &desc.compute_stage; - let (shader_module_guard, _) = hub.shader_modules.read(&mut token); - - let shader = hal::pso::EntryPoint:: { - entry: unsafe { ffi::CStr::from_ptr(pipeline_stage.entry_point) } - .to_str() - .to_owned() - .unwrap(), // TODO - module: &shader_module_guard[pipeline_stage.module].raw, - specialization: hal::pso::Specialization::EMPTY, - }; - - // TODO - let flags = hal::pso::PipelineCreationFlags::empty(); - // TODO - let parent = hal::pso::BasePipeline::None; - - let pipeline_desc = hal::pso::ComputePipelineDesc { - shader, - layout, - flags, - parent, - }; - - unsafe { - device - .create_compute_pipeline(&pipeline_desc, None) - .unwrap() - } - }; - - let pipeline = pipeline::ComputePipeline { - raw: raw_pipeline, - layout_id: desc.layout, - }; - hub.compute_pipelines - .register_identity(id_in, pipeline, &mut token) -} - -#[cfg(feature = "local")] #[no_mangle] pub extern "C" fn wgpu_device_create_compute_pipeline( - device_id: DeviceId, - desc: &pipeline::ComputePipelineDescriptor, -) -> ComputePipelineId { - gfx_select!(device_id => device_create_compute_pipeline(&*GLOBAL, device_id, desc, PhantomData)) + device_id: id::DeviceId, + desc: &core::pipeline::ComputePipelineDescriptor, +) -> id::ComputePipelineId { + gfx_select!(device_id => GLOBAL.device_create_compute_pipeline(device_id, desc, PhantomData)) } -pub fn device_create_swap_chain( - global: &Global, - device_id: DeviceId, - surface_id: SurfaceId, - desc: &swap_chain::SwapChainDescriptor, -) -> SwapChainId { - log::info!("creating swap chain {:?}", desc); - let hub = B::hub(global); - let mut token = Token::root(); - - let (mut surface_guard, mut token) = global.surfaces.write(&mut token); - let (adapter_guard, mut token) = hub.adapters.read(&mut token); - let (device_guard, mut token) = hub.devices.read(&mut token); - let (mut swap_chain_guard, _) = hub.swap_chains.write(&mut token); - let device = &device_guard[device_id]; - let surface = &mut surface_guard[surface_id]; - - let (caps, formats) = { - let suf = B::get_surface_mut(surface); - let adapter = &adapter_guard[device.adapter_id]; - assert!(suf.supports_queue_family(&adapter.raw.queue_families[0])); - let formats = suf.supported_formats(&adapter.raw.physical_device); - let caps = suf.capabilities(&adapter.raw.physical_device); - (caps, formats) - }; - let num_frames = swap_chain::DESIRED_NUM_FRAMES - .max(*caps.image_count.start()) - .min(*caps.image_count.end()); - let config = desc.to_hal(num_frames, &device.features); - - if let Some(formats) = formats { - assert!( - formats.contains(&config.format), - "Requested format {:?} is not in supported list: {:?}", - config.format, - formats - ); - } - if desc.width < caps.extents.start().width - || desc.width > caps.extents.end().width - || desc.height < caps.extents.start().height - || desc.height > caps.extents.end().height - { - log::warn!( - "Requested size {}x{} is outside of the supported range: {:?}", - desc.width, - desc.height, - caps.extents - ); - } - - unsafe { - B::get_surface_mut(surface) - .configure_swapchain(&device.raw, config) - .unwrap(); - } - - let sc_id = surface_id.to_swap_chain_id(B::VARIANT); - if let Some(sc) = swap_chain_guard.remove(sc_id) { - unsafe { - device.raw.destroy_semaphore(sc.semaphore); - } - } - let swap_chain = swap_chain::SwapChain { - life_guard: LifeGuard::new(), - device_id: Stored { - value: device_id, - ref_count: device.life_guard.ref_count.clone(), - }, - desc: desc.clone(), - num_frames, - semaphore: device.raw.create_semaphore().unwrap(), - acquired_view_id: None, - }; - swap_chain_guard.insert(sc_id, swap_chain); - sc_id -} - -#[cfg(feature = "local")] #[no_mangle] pub extern "C" fn wgpu_device_create_swap_chain( - device_id: DeviceId, - surface_id: SurfaceId, - desc: &swap_chain::SwapChainDescriptor, -) -> SwapChainId { - gfx_select!(device_id => device_create_swap_chain(&*GLOBAL, device_id, surface_id, desc)) + device_id: id::DeviceId, + surface_id: id::SurfaceId, + desc: &core::swap_chain::SwapChainDescriptor, +) -> id::SwapChainId { + gfx_select!(device_id => GLOBAL.device_create_swap_chain(device_id, surface_id, desc)) } -pub fn device_poll(global: &Global, device_id: DeviceId, force_wait: bool) { - let hub = B::hub(global); - let callbacks = { - let (device_guard, mut token) = hub.devices.read(&mut Token::root()); - device_guard[device_id].maintain(global, force_wait, &mut token) - }; - Device::::fire_map_callbacks(callbacks); -} - -#[cfg(feature = "local")] #[no_mangle] -pub extern "C" fn wgpu_device_poll(device_id: DeviceId, force_wait: bool) { - gfx_select!(device_id => device_poll(&*GLOBAL, device_id, force_wait)) +pub extern "C" fn wgpu_device_poll(device_id: id::DeviceId, force_wait: bool) { + gfx_select!(device_id => GLOBAL.device_poll(device_id, force_wait)) } -pub fn device_destroy(global: &Global, device_id: DeviceId) { - let hub = B::hub(global); - let (device, mut token) = hub.devices.unregister(device_id, &mut Token::root()); - device.maintain(global, true, &mut token); - device.com_allocator.destroy(&device.raw); -} - -#[cfg(feature = "local")] #[no_mangle] -pub extern "C" fn wgpu_device_destroy(device_id: DeviceId) { - gfx_select!(device_id => device_destroy(&*GLOBAL, device_id)) +pub extern "C" fn wgpu_device_destroy(device_id: id::DeviceId) { + gfx_select!(device_id => GLOBAL.device_destroy(device_id)) } -pub type BufferMapReadCallback = - extern "C" fn(status: BufferMapAsyncStatus, data: *const u8, userdata: *mut u8); -pub type BufferMapWriteCallback = - extern "C" fn(status: BufferMapAsyncStatus, data: *mut u8, userdata: *mut u8); - -pub fn buffer_map_async( - global: &Global, - buffer_id: BufferId, - usage: resource::BufferUsage, - operation: BufferMapOperation, -) { - let hub = B::hub(global); - let mut token = Token::root(); - let (device_guard, mut token) = hub.devices.read(&mut token); - - let (device_id, ref_count) = { - let (mut buffer_guard, _) = hub.buffers.write(&mut token); - let buffer = &mut buffer_guard[buffer_id]; - - if usage.contains(resource::BufferUsage::MAP_READ) { - assert!(buffer.usage.contains(resource::BufferUsage::MAP_READ)); - } - - if usage.contains(resource::BufferUsage::MAP_WRITE) { - assert!(buffer.usage.contains(resource::BufferUsage::MAP_WRITE)); - } - - if buffer.pending_map_operation.is_some() { - operation.call_error(); - return; - } - - buffer.pending_map_operation = Some(operation); - (buffer.device_id.value, buffer.life_guard.ref_count.clone()) - }; - - let device = &device_guard[device_id]; - - device - .trackers - .lock() - .buffers - .change_replace(buffer_id, &ref_count, (), usage); - - device.pending.lock().map(buffer_id, ref_count); -} - -#[cfg(feature = "local")] #[no_mangle] pub extern "C" fn wgpu_buffer_map_read_async( - buffer_id: BufferId, - start: BufferAddress, - size: BufferAddress, - callback: BufferMapReadCallback, + buffer_id: id::BufferId, + start: core::BufferAddress, + size: core::BufferAddress, + callback: core::device::BufferMapReadCallback, userdata: *mut u8, ) { - let operation = BufferMapOperation::Read(start .. start + size, callback, userdata); - gfx_select!(buffer_id => buffer_map_async(&*GLOBAL, buffer_id, resource::BufferUsage::MAP_READ, operation)) + let operation = + core::resource::BufferMapOperation::Read(start .. start + size, callback, userdata); + gfx_select!(buffer_id => GLOBAL.buffer_map_async(buffer_id, core::resource::BufferUsage::MAP_READ, operation)) } -#[cfg(feature = "local")] #[no_mangle] pub extern "C" fn wgpu_buffer_map_write_async( - buffer_id: BufferId, - start: BufferAddress, - size: BufferAddress, - callback: BufferMapWriteCallback, + buffer_id: id::BufferId, + start: core::BufferAddress, + size: core::BufferAddress, + callback: core::device::BufferMapWriteCallback, userdata: *mut u8, ) { - let operation = BufferMapOperation::Write(start .. start + size, callback, userdata); - gfx_select!(buffer_id => buffer_map_async(&*GLOBAL, buffer_id, resource::BufferUsage::MAP_WRITE, operation)) + let operation = + core::resource::BufferMapOperation::Write(start .. start + size, callback, userdata); + gfx_select!(buffer_id => GLOBAL.buffer_map_async(buffer_id, core::resource::BufferUsage::MAP_WRITE, operation)) } -pub fn buffer_unmap(global: &Global, buffer_id: BufferId) { - let hub = B::hub(global); - let mut token = Token::root(); - - let (device_guard, mut token) = hub.devices.read(&mut token); - let (mut buffer_guard, _) = hub.buffers.write(&mut token); - - let buffer = &mut buffer_guard[buffer_id]; - let device_raw = &device_guard[buffer.device_id.value].raw; - - if !buffer.mapped_write_ranges.is_empty() { - unsafe { - device_raw - .flush_mapped_memory_ranges( - buffer - .mapped_write_ranges - .iter() - .map(|r| (buffer.memory.memory(), r.clone())), - ) - .unwrap() - }; - buffer.mapped_write_ranges.clear(); - } - - buffer.memory.unmap(device_raw); -} - -#[cfg(feature = "local")] #[no_mangle] -pub extern "C" fn wgpu_buffer_unmap(buffer_id: BufferId) { - gfx_select!(buffer_id => buffer_unmap(&*GLOBAL, buffer_id)) +pub extern "C" fn wgpu_buffer_unmap(buffer_id: id::BufferId) { + gfx_select!(buffer_id => GLOBAL.buffer_unmap(buffer_id)) +} + +#[no_mangle] +pub extern "C" fn wgpu_swap_chain_get_next_texture( + swap_chain_id: id::SwapChainId, +) -> core::swap_chain::SwapChainOutput { + gfx_select!(swap_chain_id => GLOBAL.swap_chain_get_next_texture(swap_chain_id, PhantomData)) + .unwrap_or(core::swap_chain::SwapChainOutput { + view_id: id::TextureViewId::ERROR, + }) +} + +#[no_mangle] +pub extern "C" fn wgpu_swap_chain_present(swap_chain_id: id::SwapChainId) { + gfx_select!(swap_chain_id => GLOBAL.swap_chain_present(swap_chain_id)) } diff --git a/wgpu-native/src/instance.rs b/wgpu-native/src/instance.rs deleted file mode 100644 index da3a26a3d..000000000 --- a/wgpu-native/src/instance.rs +++ /dev/null @@ -1,589 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use crate::{ - backend, - binding_model::MAX_BIND_GROUPS, - device::BIND_BUFFER_ALIGNMENT, - hub::{GfxBackend, Global, Token}, - id::{Input, Output}, - AdapterId, - AdapterInfo, - Backend, - Device, - DeviceId, -}; -#[cfg(feature = "local")] -use crate::{gfx_select, hub::GLOBAL, SurfaceId}; - -#[cfg(feature = "local")] -use bitflags::bitflags; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -use hal::{self, adapter::PhysicalDevice as _, queue::QueueFamily as _, Instance as _}; -#[cfg(feature = "local")] -use std::marker::PhantomData; - -use std::ffi::c_void; - -#[derive(Debug)] -pub struct Instance { - #[cfg(any( - not(any(target_os = "ios", target_os = "macos")), - feature = "gfx-backend-vulkan" - ))] - vulkan: Option, - #[cfg(any(target_os = "ios", target_os = "macos"))] - metal: gfx_backend_metal::Instance, - #[cfg(windows)] - dx12: Option, - #[cfg(windows)] - dx11: gfx_backend_dx11::Instance, -} - -impl Instance { - pub fn new(name: &str, version: u32) -> Self { - Instance { - #[cfg(any( - not(any(target_os = "ios", target_os = "macos")), - feature = "gfx-backend-vulkan" - ))] - vulkan: gfx_backend_vulkan::Instance::create(name, version).ok(), - #[cfg(any(target_os = "ios", target_os = "macos"))] - metal: gfx_backend_metal::Instance::create(name, version).unwrap(), - #[cfg(windows)] - dx12: gfx_backend_dx12::Instance::create(name, version).ok(), - #[cfg(windows)] - dx11: gfx_backend_dx11::Instance::create(name, version).unwrap(), - } - } - - #[cfg(not(feature = "local"))] - pub(crate) fn destroy_surface(&mut self, surface: Surface) { - //TODO: fill out the proper destruction once we are on gfx-0.4 - #[cfg(any( - not(any(target_os = "ios", target_os = "macos")), - feature = "gfx-backend-vulkan" - ))] - { - if let Some(_suf) = surface.vulkan { - //self.vulkan.as_mut().unwrap().destroy_surface(suf); - } - } - #[cfg(any(target_os = "ios", target_os = "macos"))] - { - let _ = surface; - //self.metal.destroy_surface(surface.metal); - } - #[cfg(windows)] - { - if let Some(_suf) = surface.dx12 { - //self.dx12.as_mut().unwrap().destroy_surface(suf); - } - //self.dx11.destroy_surface(surface.dx11); - } - } -} - -type GfxSurface = ::Surface; - -#[derive(Debug)] -pub struct Surface { - #[cfg(any( - not(any(target_os = "ios", target_os = "macos")), - feature = "gfx-backend-vulkan" - ))] - pub(crate) vulkan: Option>, - #[cfg(any(target_os = "ios", target_os = "macos"))] - pub(crate) metal: GfxSurface, - #[cfg(windows)] - pub(crate) dx12: Option>, - #[cfg(windows)] - pub(crate) dx11: GfxSurface, -} - -#[derive(Debug)] -pub struct Adapter { - pub(crate) raw: hal::adapter::Adapter, -} - -#[repr(C)] -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum PowerPreference { - Default = 0, - LowPower = 1, - HighPerformance = 2, -} - -#[cfg(feature = "local")] -bitflags! { - #[repr(transparent)] - #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] - pub struct BackendBit: u32 { - const VULKAN = 1 << Backend::Vulkan as u32; - const GL = 1 << Backend::Gl as u32; - const METAL = 1 << Backend::Metal as u32; - const DX12 = 1 << Backend::Dx12 as u32; - const DX11 = 1 << Backend::Dx11 as u32; - /// Vulkan + METAL + DX12 - const PRIMARY = Self::VULKAN.bits | Self::METAL.bits | Self::DX12.bits; - /// OpenGL + DX11 - const SECONDARY = Self::GL.bits | Self::DX11.bits; - } -} - -#[cfg(feature = "local")] -impl From for BackendBit { - fn from(backend: Backend) -> Self { - BackendBit::from_bits(1 << backend as u32).unwrap() - } -} - -#[repr(C)] -#[derive(Clone, Debug)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct RequestAdapterOptions { - pub power_preference: PowerPreference, - #[cfg(feature = "local")] - pub backends: BackendBit, -} - -impl Default for RequestAdapterOptions { - fn default() -> Self { - RequestAdapterOptions { - power_preference: PowerPreference::Default, - #[cfg(feature = "local")] - backends: BackendBit::PRIMARY, - } - } -} - -#[repr(C)] -#[derive(Clone, Debug, Default)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct Extensions { - pub anisotropic_filtering: bool, -} - -#[repr(C)] -#[derive(Clone, Debug)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct Limits { - pub max_bind_groups: u32, -} - -impl Default for Limits { - fn default() -> Self { - Limits { - max_bind_groups: MAX_BIND_GROUPS as u32, - } - } -} - -#[repr(C)] -#[derive(Clone, Debug, Default)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct DeviceDescriptor { - pub extensions: Extensions, - pub limits: Limits, -} - -#[cfg(feature = "local")] -pub fn wgpu_create_surface(raw_handle: raw_window_handle::RawWindowHandle) -> SurfaceId { - use raw_window_handle::RawWindowHandle as Rwh; - - let instance = &GLOBAL.instance; - let surface = match raw_handle { - #[cfg(target_os = "ios")] - Rwh::IOS(h) => Surface { - #[cfg(feature = "gfx-backend-vulkan")] - vulkan: None, - metal: instance - .metal - .create_surface_from_uiview(h.ui_view, cfg!(debug_assertions)), - }, - #[cfg(target_os = "macos")] - Rwh::MacOS(h) => Surface { - #[cfg(feature = "gfx-backend-vulkan")] - vulkan: instance - .vulkan - .as_ref() - .map(|inst| inst.create_surface_from_ns_view(h.ns_view)), - metal: instance - .metal - .create_surface_from_nsview(h.ns_view, cfg!(debug_assertions)), - }, - #[cfg(all(unix, not(target_os = "ios"), not(target_os = "macos")))] - Rwh::Xlib(h) => Surface { - vulkan: instance - .vulkan - .as_ref() - .map(|inst| inst.create_surface_from_xlib(h.display as _, h.window as _)), - }, - #[cfg(all(unix, not(target_os = "ios"), not(target_os = "macos")))] - Rwh::Wayland(h) => Surface { - vulkan: instance - .vulkan - .as_ref() - .map(|inst| inst.create_surface_from_wayland(h.display, h.surface)), - }, - #[cfg(windows)] - Rwh::Windows(h) => Surface { - vulkan: instance - .vulkan - .as_ref() - .map(|inst| inst.create_surface_from_hwnd(std::ptr::null_mut(), h.hwnd)), - dx12: instance - .dx12 - .as_ref() - .map(|inst| inst.create_surface_from_hwnd(h.hwnd)), - dx11: instance.dx11.create_surface_from_hwnd(h.hwnd), - }, - _ => panic!("Unsupported window handle"), - }; - - let mut token = Token::root(); - GLOBAL - .surfaces - .register_identity(PhantomData, surface, &mut token) -} - -#[cfg(all( - feature = "local", - unix, - not(target_os = "ios"), - not(target_os = "macos") -))] -#[no_mangle] -pub extern "C" fn wgpu_create_surface_from_xlib( - display: *mut *const std::ffi::c_void, - window: u64, -) -> SurfaceId { - use raw_window_handle::unix::XlibHandle; - wgpu_create_surface(raw_window_handle::RawWindowHandle::Xlib(XlibHandle { - window, - display: display as *mut _, - ..XlibHandle::empty() - })) -} - -#[cfg(all(feature = "local", any(target_os = "ios", target_os = "macos")))] -#[no_mangle] -pub extern "C" fn wgpu_create_surface_from_metal_layer(layer: *mut std::ffi::c_void) -> SurfaceId { - let surface = Surface { - #[cfg(feature = "gfx-backend-vulkan")] - vulkan: None, //TODO: currently requires `NSView` - metal: GLOBAL - .instance - .metal - .create_surface_from_layer(layer as *mut _, cfg!(debug_assertions)), - }; - - GLOBAL - .surfaces - .register_identity(PhantomData, surface, &mut Token::root()) -} - -#[cfg(all(feature = "local", windows))] -#[no_mangle] -pub extern "C" fn wgpu_create_surface_from_windows_hwnd( - _hinstance: *mut std::ffi::c_void, - hwnd: *mut std::ffi::c_void, -) -> SurfaceId { - use raw_window_handle::windows::WindowsHandle; - wgpu_create_surface(raw_window_handle::RawWindowHandle::Windows( - raw_window_handle::windows::WindowsHandle { - hwnd, - ..WindowsHandle::empty() - }, - )) -} - -pub type RequestAdapterCallback = - extern "C" fn(adapter: *const AdapterId, userdata: *mut c_void); - -pub fn request_adapter_async( - global: &Global, - desc: &RequestAdapterOptions, - input_ids: &[Input], - callback: RequestAdapterCallback, - userdata: *mut c_void, -) { - let adapter = pick_adapter(global, desc, input_ids); - callback(adapter.as_ref().map_or(&AdapterId::ERROR, |x| x as *const _), userdata); -} - -fn pick_adapter( - global: &Global, - desc: &RequestAdapterOptions, - input_ids: &[Input], -) -> Option { - let instance = &global.instance; - let mut device_types = Vec::new(); - - #[cfg(not(feature = "local"))] - let find_input = |b: Backend| input_ids.iter().find(|id| id.backend() == b).cloned(); - #[cfg(feature = "local")] - let find_input = |b: Backend| { - let _ = input_ids; - if desc.backends.contains(b.into()) { - Some(PhantomData) - } else { - None - } - }; - - let id_vulkan = find_input(Backend::Vulkan); - let id_metal = find_input(Backend::Metal); - let id_dx12 = find_input(Backend::Dx12); - let id_dx11 = find_input(Backend::Dx11); - - #[cfg(any( - not(any(target_os = "ios", target_os = "macos")), - feature = "gfx-backend-vulkan" - ))] - let mut adapters_vk = match instance.vulkan { - Some(ref inst) if id_vulkan.is_some() => { - let adapters = inst.enumerate_adapters(); - device_types.extend(adapters.iter().map(|ad| ad.info.device_type.clone())); - adapters - } - _ => Vec::new(), - }; - #[cfg(any(target_os = "ios", target_os = "macos"))] - let mut adapters_mtl = if id_metal.is_some() { - let adapters = instance.metal.enumerate_adapters(); - device_types.extend(adapters.iter().map(|ad| ad.info.device_type.clone())); - adapters - } else { - Vec::new() - }; - #[cfg(windows)] - let mut adapters_dx12 = match instance.dx12 { - Some(ref inst) if id_dx12.is_some() => { - let adapters = inst.enumerate_adapters(); - device_types.extend(adapters.iter().map(|ad| ad.info.device_type.clone())); - adapters - } - _ => Vec::new(), - }; - #[cfg(windows)] - let mut adapters_dx11 = if id_dx11.is_some() { - let adapters = instance.dx11.enumerate_adapters(); - device_types.extend(adapters.iter().map(|ad| ad.info.device_type.clone())); - adapters - } else { - Vec::new() - }; - - if device_types.is_empty() { - log::warn!("No adapters are available!"); - return None; - } - - let (mut integrated, mut discrete, mut virt, mut other) = (None, None, None, None); - - for (i, ty) in device_types.into_iter().enumerate() { - match ty { - hal::adapter::DeviceType::IntegratedGpu => { - integrated = integrated.or(Some(i)); - } - hal::adapter::DeviceType::DiscreteGpu => { - discrete = discrete.or(Some(i)); - } - hal::adapter::DeviceType::VirtualGpu => { - virt = virt.or(Some(i)); - } - _ => { - other = other.or(Some(i)); - } - } - } - - let preferred_gpu = match desc.power_preference { - PowerPreference::Default => integrated.or(discrete).or(other).or(virt), - PowerPreference::LowPower => integrated.or(other).or(discrete).or(virt), - PowerPreference::HighPerformance => discrete.or(other).or(integrated).or(virt), - }; - - #[allow(unused_variables)] - let local_or_remote_id = |local_id, remote_id| { - #[cfg(not(feature = "local"))] - let id = remote_id; - #[cfg(feature = "local")] - let id = Some(local_id); - id - }; - - let mut token = Token::root(); - - let mut selected = preferred_gpu.unwrap_or(0); - #[cfg(any( - not(any(target_os = "ios", target_os = "macos")), - feature = "gfx-backend-vulkan" - ))] - { - if selected < adapters_vk.len() { - let adapter = Adapter { - raw: adapters_vk.swap_remove(selected), - }; - log::info!("Adapter Vulkan {:?}", adapter.raw.info); - let id_out = backend::Vulkan::hub(global).adapters.register_identity( - id_vulkan.unwrap(), - adapter, - &mut token, - ); - return local_or_remote_id(id_out, id_vulkan); - } - selected -= adapters_vk.len(); - } - #[cfg(any(target_os = "ios", target_os = "macos"))] - { - if selected < adapters_mtl.len() { - let adapter = Adapter { - raw: adapters_mtl.swap_remove(selected), - }; - log::info!("Adapter Metal {:?}", adapter.raw.info); - let id_out = backend::Metal::hub(global).adapters.register_identity( - id_metal.unwrap(), - adapter, - &mut token, - ); - return local_or_remote_id(id_out, id_metal); - } - selected -= adapters_mtl.len(); - } - #[cfg(windows)] - { - if selected < adapters_dx12.len() { - let adapter = Adapter { - raw: adapters_dx12.swap_remove(selected), - }; - log::info!("Adapter Dx12 {:?}", adapter.raw.info); - let id_out = backend::Dx12::hub(global).adapters.register_identity( - id_dx12.unwrap(), - adapter, - &mut token, - ); - return local_or_remote_id(id_out, id_dx12); - } - selected -= adapters_dx12.len(); - if selected < adapters_dx11.len() { - let adapter = Adapter { - raw: adapters_dx11.swap_remove(selected), - }; - log::info!("Adapter Dx11 {:?}", adapter.raw.info); - let id_out = backend::Dx11::hub(global).adapters.register_identity( - id_dx11.unwrap(), - adapter, - &mut token, - ); - return local_or_remote_id(id_out, id_dx11); - } - selected -= adapters_dx11.len(); - } - - let _ = (selected, id_vulkan, id_metal, id_dx12, id_dx11); - unreachable!() -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_request_adapter_async( - desc: Option<&RequestAdapterOptions>, - callback: RequestAdapterCallback, - userdata: *mut c_void, -) { - request_adapter_async(&*GLOBAL, &desc.cloned().unwrap_or_default(), &[], callback, userdata); -} - -pub fn adapter_request_device( - global: &Global, - adapter_id: AdapterId, - desc: &DeviceDescriptor, - id_in: Input, -) -> Output { - let hub = B::hub(global); - let mut token = Token::root(); - let device = { - let (adapter_guard, _) = hub.adapters.read(&mut token); - let adapter = &adapter_guard[adapter_id].raw; - - let family = adapter - .queue_families - .iter() - .find(|family| family.queue_type().supports_graphics()) - .unwrap(); - let mut gpu = unsafe { - adapter - .physical_device - .open(&[(family, &[1.0])], hal::Features::empty()) - .unwrap() - }; - - let limits = adapter.physical_device.limits(); - assert_eq!( - 0, - BIND_BUFFER_ALIGNMENT % limits.min_storage_buffer_offset_alignment, - "Adapter storage buffer offset alignment not compatible with WGPU" - ); - assert_eq!( - 0, - BIND_BUFFER_ALIGNMENT % limits.min_uniform_buffer_offset_alignment, - "Adapter uniform buffer offset alignment not compatible with WGPU" - ); - if limits.max_bound_descriptor_sets == 0 { - log::warn!("max_bind_groups limit is missing"); - } else { - assert!( - u32::from(limits.max_bound_descriptor_sets) >= desc.limits.max_bind_groups, - "Adapter does not support the requested max_bind_groups" - ); - } - - let mem_props = adapter.physical_device.memory_properties(); - - let supports_texture_d24_s8 = adapter - .physical_device - .format_properties(Some(hal::format::Format::D24UnormS8Uint)) - .optimal_tiling - .contains(hal::format::ImageFeature::DEPTH_STENCIL_ATTACHMENT); - - Device::new( - gpu.device, - adapter_id, - gpu.queue_groups.swap_remove(0), - mem_props, - supports_texture_d24_s8, - desc.limits.max_bind_groups, - ) - }; - - hub.devices.register_identity(id_in, device, &mut token) -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_adapter_request_device( - adapter_id: AdapterId, - desc: Option<&DeviceDescriptor>, -) -> DeviceId { - let desc = &desc.cloned().unwrap_or_default(); - gfx_select!(adapter_id => adapter_request_device(&*GLOBAL, adapter_id, desc, PhantomData)) -} - -pub fn adapter_get_info(global: &Global, adapter_id: AdapterId) -> AdapterInfo { - let hub = B::hub(global); - let mut token = Token::root(); - let (adapter_guard, _) = hub.adapters.read(&mut token); - let adapter = &adapter_guard[adapter_id]; - adapter.raw.info.clone() -} - -#[cfg(feature = "local")] -pub fn wgpu_adapter_get_info(adapter_id: AdapterId) -> AdapterInfo { - gfx_select!(adapter_id => adapter_get_info(&*GLOBAL, adapter_id)) -} diff --git a/wgpu-native/src/lib.rs b/wgpu-native/src/lib.rs index 86fbd1e6e..4260ec83a 100644 --- a/wgpu-native/src/lib.rs +++ b/wgpu-native/src/lib.rs @@ -1,234 +1,10 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use std::sync::Arc; -pub mod backend { - #[cfg(windows)] - pub use gfx_backend_dx11::Backend as Dx11; - #[cfg(windows)] - pub use gfx_backend_dx12::Backend as Dx12; - pub use gfx_backend_empty::Backend as Empty; - #[cfg(any(target_os = "ios", target_os = "macos"))] - pub use gfx_backend_metal::Backend as Metal; - #[cfg(any( - not(any(target_os = "ios", target_os = "macos")), - feature = "gfx-backend-vulkan" - ))] - pub use gfx_backend_vulkan::Backend as Vulkan; +pub mod command; +pub mod device; + +type Global = core::hub::Global>; + +lazy_static::lazy_static! { + static ref GLOBAL: Arc = Arc::new(Global::new("wgpu")); } - -mod binding_model; -mod command; -mod conv; -mod device; -mod hub; -mod id; -mod instance; -mod pipeline; -mod resource; -mod swap_chain; -mod track; - -pub use self::binding_model::*; -pub use self::command::*; -pub use self::device::*; -#[cfg(not(feature = "local"))] -pub use self::hub::{Access, Global, IdentityManager, Registry, Token}; -pub use self::id::*; -pub use self::instance::*; -pub use self::pipeline::*; -pub use self::resource::*; -pub use self::swap_chain::*; -pub use hal::adapter::AdapterInfo; -pub use hal::pso::read_spirv; - -use std::{ - os::raw::c_char, - ptr, - sync::atomic::{AtomicUsize, Ordering}, -}; - -type SubmissionIndex = usize; -type Index = u32; -type Epoch = u32; - -#[repr(u8)] -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum Backend { - Empty = 0, - Vulkan = 1, - Metal = 2, - Dx12 = 3, - Dx11 = 4, - Gl = 5, -} - -pub type BufferAddress = u64; -pub type RawString = *const c_char; - -//TODO: make it private. Currently used for swapchain creation impl. -#[derive(Debug)] -pub struct RefCount(ptr::NonNull); - -unsafe impl Send for RefCount {} -unsafe impl Sync for RefCount {} - -impl RefCount { - const MAX: usize = 1 << 24; - - fn load(&self) -> usize { - unsafe { self.0.as_ref() }.load(Ordering::Acquire) - } -} - -impl Clone for RefCount { - fn clone(&self) -> Self { - let old_size = unsafe { self.0.as_ref() }.fetch_add(1, Ordering::Relaxed); - assert!(old_size < Self::MAX); - RefCount(self.0) - } -} - -impl Drop for RefCount { - fn drop(&mut self) { - if unsafe { self.0.as_ref() }.fetch_sub(1, Ordering::Relaxed) == 1 { - let _ = unsafe { Box::from_raw(self.0.as_ptr()) }; - } - } -} - -#[derive(Debug)] -struct LifeGuard { - ref_count: RefCount, - submission_index: AtomicUsize, -} - -impl LifeGuard { - fn new() -> Self { - let bx = Box::new(AtomicUsize::new(1)); - LifeGuard { - ref_count: RefCount(ptr::NonNull::new(Box::into_raw(bx)).unwrap()), - submission_index: AtomicUsize::new(0), - } - } -} - -#[derive(Clone, Debug)] -struct Stored { - value: T, - ref_count: RefCount, -} - -#[repr(C)] -#[derive(Clone, Copy, Debug)] -pub struct Color { - pub r: f64, - pub g: f64, - pub b: f64, - pub a: f64, -} - -impl Color { - pub const TRANSPARENT: Self = Color { - r: 0.0, - g: 0.0, - b: 0.0, - a: 0.0, - }; - pub const BLACK: Self = Color { - r: 0.0, - g: 0.0, - b: 0.0, - a: 1.0, - }; - pub const WHITE: Self = Color { - r: 1.0, - g: 1.0, - b: 1.0, - a: 1.0, - }; - pub const RED: Self = Color { - r: 1.0, - g: 0.0, - b: 0.0, - a: 1.0, - }; - pub const GREEN: Self = Color { - r: 0.0, - g: 1.0, - b: 0.0, - a: 1.0, - }; - pub const BLUE: Self = Color { - r: 0.0, - g: 0.0, - b: 1.0, - a: 1.0, - }; -} - -#[repr(C)] -#[derive(Clone, Copy, Debug)] -pub struct Origin3d { - pub x: f32, - pub y: f32, - pub z: f32, -} - -impl Origin3d { - pub const ZERO: Self = Origin3d { - x: 0.0, - y: 0.0, - z: 0.0, - }; -} - -impl Default for Origin3d { - fn default() -> Self { - Origin3d::ZERO - } -} - -#[repr(C)] -#[derive(Clone, Copy, Debug)] -pub struct Extent3d { - pub width: u32, - pub height: u32, - pub depth: u32, -} - -#[repr(C)] -#[derive(Debug)] -pub struct U32Array { - pub bytes: *const u32, - pub length: usize, -} - -#[derive(Debug)] -pub enum InputState {} - -#[macro_export] -macro_rules! gfx_select { - ($id:expr => $function:ident( $($param:expr),+ )) => { - match $id.backend() { - #[cfg(any(not(any(target_os = "ios", target_os = "macos")), feature = "gfx-backend-vulkan"))] - $crate::Backend::Vulkan => $function::<$crate::backend::Vulkan>( $($param),+ ), - #[cfg(any(target_os = "ios", target_os = "macos"))] - $crate::Backend::Metal => $function::<$crate::backend::Metal>( $($param),+ ), - #[cfg(windows)] - $crate::Backend::Dx12 => $function::<$crate::backend::Dx12>( $($param),+ ), - #[cfg(windows)] - $crate::Backend::Dx11 => $function::<$crate::backend::Dx11>( $($param),+ ), - _ => unreachable!() - } - }; -} - -#[derive(Clone, Copy, Debug)] -pub(crate) struct Features { - pub max_bind_groups: u32, - pub supports_texture_d24_s8: bool, -} - -/// Fast hash map used internally. -type FastHashMap = std::collections::HashMap>; diff --git a/wgpu-native/src/swap_chain.rs b/wgpu-native/src/swap_chain.rs deleted file mode 100644 index cfdc10884..000000000 --- a/wgpu-native/src/swap_chain.rs +++ /dev/null @@ -1,261 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/*! Swap chain management. - - ## Lifecycle - - At the low level, the swap chain is using the new simplified model of gfx-rs. - - A swap chain is a separate object that is backend-dependent but shares the index with - the parent surface, which is backend-independent. This ensures a 1:1 correspondence - between them. - - `get_next_image()` requests a new image from the surface. It becomes a part of - `TextureViewInner::SwapChain` of the resulted view. The view is registered in the HUB - but not in the device tracker. - - The only operation allowed on the view is to be either a color or a resolve attachment. - It can only be used in one command buffer, which needs to be submitted before presenting. - Command buffer tracker knows about the view, but only for the duration of recording. - The view ID is erased from it at the end, so that it's not merged into the device tracker. - - When a swapchain view is used in `begin_render_pass()`, we assume the start and end image - layouts purely based on whether or not this view was used in this command buffer before. - It always starts with `Uninitialized` and ends with `Present`, so that no barriers are - needed when we need to actually present it. - - In `queue_submit()` we make sure to signal the semaphore whenever we render to a swap - chain view. - - In `present()` we return the swap chain image back and wait on the semaphore. -!*/ - -use crate::{ - conv, - hub::{GfxBackend, Global, Token}, - resource, - DeviceId, - Extent3d, - Features, - Input, - LifeGuard, - Stored, - SwapChainId, - TextureViewId, -}; -#[cfg(feature = "local")] -use crate::{gfx_select, hub::GLOBAL}; - -use hal::{self, device::Device as _, queue::CommandQueue as _, window::PresentationSurface as _}; - -use smallvec::SmallVec; - -#[cfg(feature = "local")] -use std::marker::PhantomData; - - -const FRAME_TIMEOUT_MS: u64 = 1000; -pub const DESIRED_NUM_FRAMES: u32 = 3; - -#[derive(Debug)] -pub struct SwapChain { - pub(crate) life_guard: LifeGuard, - pub(crate) device_id: Stored, - pub(crate) desc: SwapChainDescriptor, - pub(crate) num_frames: hal::window::SwapImageIndex, - pub(crate) semaphore: B::Semaphore, - pub(crate) acquired_view_id: Option>, -} - -#[repr(C)] -#[derive(Copy, Clone, Debug)] -pub enum PresentMode { - NoVsync = 0, - Vsync = 1, -} - -#[repr(C)] -#[derive(Clone, Debug)] -pub struct SwapChainDescriptor { - pub usage: resource::TextureUsage, - pub format: resource::TextureFormat, - pub width: u32, - pub height: u32, - pub present_mode: PresentMode, -} - -impl SwapChainDescriptor { - pub(crate) fn to_hal( - &self, - num_frames: u32, - features: &Features, - ) -> hal::window::SwapchainConfig { - let mut config = hal::window::SwapchainConfig::new( - self.width, - self.height, - conv::map_texture_format(self.format, *features), - num_frames, - ); - //TODO: check for supported - config.image_usage = conv::map_texture_usage(self.usage, hal::format::Aspects::COLOR); - config.composite_alpha_mode = hal::window::CompositeAlphaMode::OPAQUE; - config.present_mode = match self.present_mode { - PresentMode::NoVsync => hal::window::PresentMode::IMMEDIATE, - PresentMode::Vsync => hal::window::PresentMode::FIFO, - }; - config - } - - pub fn to_texture_desc(&self) -> resource::TextureDescriptor { - resource::TextureDescriptor { - size: Extent3d { - width: self.width, - height: self.height, - depth: 1, - }, - mip_level_count: 1, - array_layer_count: 1, - sample_count: 1, - dimension: resource::TextureDimension::D2, - format: self.format, - usage: self.usage, - } - } -} - -#[repr(C)] -#[derive(Debug)] -pub struct SwapChainOutput { - pub view_id: TextureViewId, -} - -#[derive(Debug)] -pub enum SwapChainGetNextTextureError { - GpuProcessingTimeout, -} - -pub fn swap_chain_get_next_texture( - global: &Global, - swap_chain_id: SwapChainId, - view_id_in: Input, -) -> Result { - let hub = B::hub(global); - let mut token = Token::root(); - - let (mut surface_guard, mut token) = global.surfaces.write(&mut token); - let surface = &mut surface_guard[swap_chain_id.to_surface_id()]; - let (device_guard, mut token) = hub.devices.read(&mut token); - let (mut swap_chain_guard, mut token) = hub.swap_chains.write(&mut token); - let sc = &mut swap_chain_guard[swap_chain_id]; - let device = &device_guard[sc.device_id.value]; - - let (image, _) = { - let suf = B::get_surface_mut(surface); - match unsafe { suf.acquire_image(FRAME_TIMEOUT_MS * 1_000_000) } { - Ok(surface_image) => surface_image, - Err(hal::window::AcquireError::Timeout) => { - return Err(SwapChainGetNextTextureError::GpuProcessingTimeout); - } - Err(e) => { - log::warn!("acquire_image() failed ({:?}), reconfiguring swapchain", e); - let desc = sc.desc.to_hal(sc.num_frames, &device.features); - unsafe { - suf.configure_swapchain(&device.raw, desc).unwrap(); - suf.acquire_image(FRAME_TIMEOUT_MS * 1_000_000).unwrap() - } - } - } - }; - - let view = resource::TextureView { - inner: resource::TextureViewInner::SwapChain { - image, - source_id: Stored { - value: swap_chain_id, - ref_count: sc.life_guard.ref_count.clone(), - }, - framebuffers: SmallVec::new(), - }, - format: sc.desc.format, - extent: hal::image::Extent { - width: sc.desc.width, - height: sc.desc.height, - depth: 1, - }, - samples: 1, - range: hal::image::SubresourceRange { - aspects: hal::format::Aspects::COLOR, - layers: 0 .. 1, - levels: 0 .. 1, - }, - life_guard: LifeGuard::new(), - }; - let ref_count = view.life_guard.ref_count.clone(); - let (view_id, _) = hub.texture_views.new_identity(view_id_in); - hub.texture_views.register(view_id, view, &mut token); - - assert!( - sc.acquired_view_id.is_none(), - "Swap chain image is already acquired" - ); - sc.acquired_view_id = Some(Stored { - value: view_id, - ref_count, - }); - - Ok(SwapChainOutput { view_id }) -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_swap_chain_get_next_texture(swap_chain_id: SwapChainId) -> SwapChainOutput { - gfx_select!(swap_chain_id => swap_chain_get_next_texture(&*GLOBAL, swap_chain_id, PhantomData)).unwrap_or(SwapChainOutput { - view_id: TextureViewId::ERROR, - }) -} - -pub fn swap_chain_present(global: &Global, swap_chain_id: SwapChainId) { - let hub = B::hub(global); - let mut token = Token::root(); - - let (mut surface_guard, mut token) = global.surfaces.write(&mut token); - let surface = &mut surface_guard[swap_chain_id.to_surface_id()]; - let (mut device_guard, mut token) = hub.devices.write(&mut token); - let (mut swap_chain_guard, mut token) = hub.swap_chains.write(&mut token); - let sc = &mut swap_chain_guard[swap_chain_id]; - let device = &mut device_guard[sc.device_id.value]; - - let view_id = sc - .acquired_view_id - .take() - .expect("Swap chain image is not acquired"); - let (view, _) = hub.texture_views.unregister(view_id.value, &mut token); - let (image, framebuffers) = match view.inner { - resource::TextureViewInner::Native { .. } => unreachable!(), - resource::TextureViewInner::SwapChain { - image, framebuffers, .. - } => (image, framebuffers), - }; - - let err = unsafe { - let queue = &mut device.queue_group.queues[0]; - queue.present_surface(B::get_surface_mut(surface), image, Some(&sc.semaphore)) - }; - if let Err(e) = err { - log::warn!("present failed: {:?}", e); - } - - for fbo in framebuffers { - unsafe { - device.raw.destroy_framebuffer(fbo); - } - } -} - -#[cfg(feature = "local")] -#[no_mangle] -pub extern "C" fn wgpu_swap_chain_present(swap_chain_id: SwapChainId) { - gfx_select!(swap_chain_id => swap_chain_present(&*GLOBAL, swap_chain_id)) -} diff --git a/wgpu-remote/Cargo.toml b/wgpu-remote/Cargo.toml index a65264e7b..09f8e823f 100644 --- a/wgpu-remote/Cargo.toml +++ b/wgpu-remote/Cargo.toml @@ -9,12 +9,16 @@ edition = "2018" [lib] # Enabling these targets makes our CI bots try to build them and fail atm -crate-type = ["lib", "cdylib", "staticlib"] +#crate-type = ["lib", "cdylib", "staticlib"] [features] default = [] +[dependencies.core] +path = "../wgpu-core" +package = "wgpu-core" +version = "0.1" + [dependencies] -wgn = { path = "../wgpu-native", package = "wgpu-native", version = "0.4" } log = "0.4" parking_lot = { version = "0.9" } diff --git a/wgpu-remote/cbindgen.toml b/wgpu-remote/cbindgen.toml index 2d552f481..1bc9b6880 100644 --- a/wgpu-remote/cbindgen.toml +++ b/wgpu-remote/cbindgen.toml @@ -22,7 +22,7 @@ exclude = ["BufferMapResult"] [parse] parse_deps = true -include = ["wgpu-native"] +include = ["wgpu-core"] [fn] prefix = "WGPU_INLINE" diff --git a/wgpu-remote/src/lib.rs b/wgpu-remote/src/lib.rs index 0a209807f..5469a67fd 100644 --- a/wgpu-remote/src/lib.rs +++ b/wgpu-remote/src/lib.rs @@ -2,7 +2,11 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use wgn::{AdapterId, Backend, DeviceId, IdentityManager, SurfaceId}; +use core::{ + hub::IdentityManager, + id::{AdapterId, DeviceId}, + Backend, +}; use parking_lot::Mutex; @@ -10,24 +14,16 @@ use std::{ptr, slice}; pub mod server; -#[derive(Debug)] + +#[derive(Debug, Default)] struct IdentityHub { - adapters: IdentityManager, - devices: IdentityManager, + adapters: IdentityManager, + devices: IdentityManager, } -impl IdentityHub { - fn new(backend: Backend) -> Self { - IdentityHub { - adapters: IdentityManager::new(backend), - devices: IdentityManager::new(backend), - } - } -} - -#[derive(Debug)] +#[derive(Debug, Default)] struct Identities { - surfaces: IdentityManager, + surfaces: IdentityManager, vulkan: IdentityHub, #[cfg(any(target_os = "ios", target_os = "macos"))] metal: IdentityHub, @@ -36,17 +32,6 @@ struct Identities { } impl Identities { - fn new() -> Self { - Identities { - surfaces: IdentityManager::new(Backend::Empty), - vulkan: IdentityHub::new(Backend::Vulkan), - #[cfg(any(target_os = "ios", target_os = "macos"))] - metal: IdentityHub::new(Backend::Metal), - #[cfg(windows)] - dx12: IdentityHub::new(Backend::Dx12), - } - } - fn select(&mut self, backend: Backend) -> &mut IdentityHub { match backend { Backend::Vulkan => &mut self.vulkan, @@ -75,7 +60,7 @@ pub struct Infrastructure { pub extern "C" fn wgpu_client_new() -> Infrastructure { log::info!("Initializing WGPU client"); let client = Box::new(Client { - identities: Mutex::new(Identities::new()), + identities: Mutex::new(Identities::default()), }); Infrastructure { client: Box::into_raw(client), @@ -92,22 +77,22 @@ pub extern "C" fn wgpu_client_delete(client: *mut Client) { #[no_mangle] pub extern "C" fn wgpu_client_make_adapter_ids( client: &Client, - ids: *mut wgn::AdapterId, + ids: *mut AdapterId, id_length: usize, ) -> usize { let mut identities = client.identities.lock(); assert_ne!(id_length, 0); let mut ids = unsafe { slice::from_raw_parts_mut(ids, id_length) }.iter_mut(); - *ids.next().unwrap() = identities.vulkan.adapters.alloc(); + *ids.next().unwrap() = identities.vulkan.adapters.alloc(Backend::Vulkan); #[cfg(any(target_os = "ios", target_os = "macos"))] { - *ids.next().unwrap() = identities.metal.adapters.alloc(); + *ids.next().unwrap() = identities.metal.adapters.alloc(Backend::Metal); } #[cfg(windows)] { - *ids.next().unwrap() = identities.dx12.adapters.alloc(); + *ids.next().unwrap() = identities.dx12.adapters.alloc(Backend::Dx12); } id_length - ids.len() @@ -116,7 +101,7 @@ pub extern "C" fn wgpu_client_make_adapter_ids( #[no_mangle] pub extern "C" fn wgpu_client_kill_adapter_ids( client: &Client, - ids: *const wgn::AdapterId, + ids: *const AdapterId, id_length: usize, ) { let mut identity = client.identities.lock(); @@ -127,20 +112,18 @@ pub extern "C" fn wgpu_client_kill_adapter_ids( } #[no_mangle] -pub extern "C" fn wgpu_client_make_device_id( - client: &Client, - adapter_id: wgn::AdapterId, -) -> wgn::DeviceId { +pub extern "C" fn wgpu_client_make_device_id(client: &Client, adapter_id: AdapterId) -> DeviceId { + let backend = adapter_id.backend(); client .identities .lock() - .select(adapter_id.backend()) + .select(backend) .devices - .alloc() + .alloc(backend) } #[no_mangle] -pub extern "C" fn wgpu_client_kill_device_id(client: &Client, id: wgn::DeviceId) { +pub extern "C" fn wgpu_client_kill_device_id(client: &Client, id: DeviceId) { client .identities .lock() diff --git a/wgpu-remote/src/server.rs b/wgpu-remote/src/server.rs index 3e59edb2a..39eb40ddd 100644 --- a/wgpu-remote/src/server.rs +++ b/wgpu-remote/src/server.rs @@ -2,16 +2,18 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use core::{gfx_select, hub::Global, id}; + use std::slice; #[no_mangle] -pub extern "C" fn wgpu_server_new() -> *mut wgn::Global { +pub extern "C" fn wgpu_server_new() -> *mut Global<()> { log::info!("Initializing WGPU server"); - Box::into_raw(Box::new(wgn::Global::new("wgpu"))) + Box::into_raw(Box::new(Global::new("wgpu"))) } #[no_mangle] -pub extern "C" fn wgpu_server_delete(global: *mut wgn::Global) { +pub extern "C" fn wgpu_server_delete(global: *mut Global<()>) { log::info!("Terminating WGPU server"); unsafe { Box::from_raw(global) }.delete(); log::info!("\t...done"); @@ -23,44 +25,32 @@ pub extern "C" fn wgpu_server_delete(global: *mut wgn::Global) { /// Returns the index in this list, or -1 if unable to pick. #[no_mangle] pub extern "C" fn wgpu_server_instance_request_adapter( - global: &wgn::Global, - desc: &wgn::RequestAdapterOptions, - ids: *const wgn::AdapterId, + global: &Global<()>, + desc: &core::instance::RequestAdapterOptions, + ids: *const id::AdapterId, id_length: usize, ) -> i8 { - extern "C" fn request_adapter_callback( - data: *const wgn::AdapterId, - user_data: *mut std::ffi::c_void, - ) { - unsafe { - *(user_data as *mut wgn::AdapterId) = *data; - } - } - let ids = unsafe { slice::from_raw_parts(ids, id_length) }; - let mut adapter_id: wgn::AdapterId = wgn::AdapterId::ERROR; - let adapter_id_ref = &mut adapter_id; - wgn::request_adapter_async(global, desc, ids, request_adapter_callback, adapter_id_ref as *mut _ as *mut std::ffi::c_void); - if adapter_id == wgn::AdapterId::ERROR { - -1 - } else { - ids.iter().position(|&i| i == adapter_id).unwrap() as i8 + match global.pick_adapter( + desc, + core::instance::AdapterInputs::IdSet(ids, |i| i.backend()), + ) { + Some(id) => ids.iter().position(|&i| i == id).unwrap() as i8, + None => -1, } } #[no_mangle] pub extern "C" fn wgpu_server_adapter_request_device( - global: &wgn::Global, - self_id: wgn::AdapterId, - desc: &wgn::DeviceDescriptor, - new_id: wgn::DeviceId, + global: &Global<()>, + self_id: id::AdapterId, + desc: &core::instance::DeviceDescriptor, + new_id: id::DeviceId, ) { - use wgn::adapter_request_device as func; - wgn::gfx_select!(self_id => func(global, self_id, desc, new_id)); + gfx_select!(self_id => global.adapter_request_device(self_id, desc, new_id)); } #[no_mangle] -pub extern "C" fn wgpu_server_device_destroy(global: &wgn::Global, self_id: wgn::DeviceId) { - use wgn::device_destroy as func; - wgn::gfx_select!(self_id => func(global, self_id)) +pub extern "C" fn wgpu_server_device_destroy(global: &Global<()>, self_id: id::DeviceId) { + gfx_select!(self_id => global.device_destroy(self_id)) }