diff --git a/Cargo.lock b/Cargo.lock index 86e8e96a26..2d140c2e8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -365,6 +365,24 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "peek-poke" +version = "0.2.0" +source = "git+https://github.com/kvark/peek-poke?rev=969bd7fe2be1a83f87916dc8b388c63cfd457075#969bd7fe2be1a83f87916dc8b388c63cfd457075" +dependencies = [ + "peek-poke-derive 0.2.0 (git+https://github.com/kvark/peek-poke?rev=969bd7fe2be1a83f87916dc8b388c63cfd457075)", +] + +[[package]] +name = "peek-poke-derive" +version = "0.2.0" +source = "git+https://github.com/kvark/peek-poke?rev=969bd7fe2be1a83f87916dc8b388c63cfd457075#969bd7fe2be1a83f87916dc8b388c63cfd457075" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pkg-config" version = "0.3.17" @@ -612,6 +630,7 @@ dependencies = [ "gfx-hal 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "peek-poke 0.2.0 (git+https://github.com/kvark/peek-poke?rev=969bd7fe2be1a83f87916dc8b388c63cfd457075)", "rendy-descriptor 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "rendy-memory 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", @@ -623,6 +642,7 @@ dependencies = [ name = "wgpu-native" version = "0.4.0" dependencies = [ + "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -717,6 +737,8 @@ dependencies = [ "checksum objc_exception 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" "checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" "checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" +"checksum peek-poke 0.2.0 (git+https://github.com/kvark/peek-poke?rev=969bd7fe2be1a83f87916dc8b388c63cfd457075)" = "" +"checksum peek-poke-derive 0.2.0 (git+https://github.com/kvark/peek-poke?rev=969bd7fe2be1a83f87916dc8b388c63cfd457075)" = "" "checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" "checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" diff --git a/ffi/wgpu.h b/ffi/wgpu.h index c8a0cfce53..c8499a90fc 100644 --- a/ffi/wgpu.h +++ b/ffi/wgpu.h @@ -19,6 +19,18 @@ #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, @@ -282,20 +294,19 @@ typedef uint64_t WGPUId_CommandBuffer_Dummy; typedef WGPUId_CommandBuffer_Dummy WGPUCommandBufferId; -typedef uint64_t WGPUId_ComputePass_Dummy; - -typedef WGPUId_ComputePass_Dummy WGPUComputePassId; - typedef WGPUCommandBufferId WGPUCommandEncoderId; +typedef struct { + uint8_t *data; + uint8_t *base; + uintptr_t capacity; + WGPUCommandEncoderId parent; +} WGPURawPass; + typedef struct { uint32_t todo; } WGPUComputePassDescriptor; -typedef uint64_t WGPUId_RenderPass_Dummy; - -typedef WGPUId_RenderPass_Dummy WGPURenderPassId; - typedef uint64_t WGPUId_TextureView_Dummy; typedef WGPUId_TextureView_Dummy WGPUTextureViewId; @@ -315,11 +326,13 @@ typedef struct { typedef struct { WGPUTextureViewId attachment; - const WGPUTextureViewId *resolve_target; + WGPUTextureViewId resolve_target; WGPULoadOp load_op; WGPUStoreOp store_op; WGPUColor clear_color; -} WGPURenderPassColorAttachmentDescriptor; +} WGPURenderPassColorAttachmentDescriptorBase_TextureViewId__TextureViewId; + +typedef WGPURenderPassColorAttachmentDescriptorBase_TextureViewId__TextureViewId WGPURawRenderPassColorAttachmentDescriptor; typedef struct { WGPUTextureViewId attachment; @@ -329,12 +342,36 @@ typedef struct { WGPULoadOp stencil_load_op; WGPUStoreOp stencil_store_op; uint32_t clear_stencil; -} WGPURenderPassDepthStencilAttachmentDescriptor_TextureViewId; +} WGPURenderPassDepthStencilAttachmentDescriptorBase_TextureViewId; + +typedef WGPURenderPassDepthStencilAttachmentDescriptorBase_TextureViewId WGPURenderPassDepthStencilAttachmentDescriptor; + +typedef struct { + WGPURawRenderPassColorAttachmentDescriptor colors[WGPUMAX_COLOR_TARGETS]; + WGPURenderPassDepthStencilAttachmentDescriptor depth_stencil; +} WGPURawRenderTargets; + +typedef struct { + WGPURawPass raw; + WGPURawRenderTargets targets; +} WGPURawRenderPass; + +typedef const WGPUTextureViewId *WGPUOptionRef_TextureViewId; + +typedef struct { + WGPUTextureViewId attachment; + WGPUOptionRef_TextureViewId resolve_target; + WGPULoadOp load_op; + WGPUStoreOp store_op; + WGPUColor clear_color; +} WGPURenderPassColorAttachmentDescriptorBase_TextureViewId__OptionRef_TextureViewId; + +typedef WGPURenderPassColorAttachmentDescriptorBase_TextureViewId__OptionRef_TextureViewId WGPURenderPassColorAttachmentDescriptor; typedef struct { const WGPURenderPassColorAttachmentDescriptor *color_attachments; uintptr_t color_attachments_length; - const WGPURenderPassDepthStencilAttachmentDescriptor_TextureViewId *depth_stencil_attachment; + const WGPURenderPassDepthStencilAttachmentDescriptor *depth_stencil_attachment; } WGPURenderPassDescriptor; typedef struct { @@ -372,6 +409,8 @@ typedef struct { uint32_t todo; } WGPUCommandBufferDescriptor; +typedef WGPURawPass *WGPUComputePassId; + typedef const char *WGPURawString; typedef uint64_t WGPUId_ComputePipeline_Dummy; @@ -639,6 +678,8 @@ typedef struct { typedef WGPUDeviceId WGPUQueueId; +typedef WGPURawRenderPass *WGPURenderPassId; + typedef uint64_t WGPUId_RenderBundle_Dummy; typedef WGPUId_RenderBundle_Dummy WGPURenderBundleId; @@ -688,11 +729,11 @@ void wgpu_buffer_unmap(WGPUBufferId buffer_id); void wgpu_command_buffer_destroy(WGPUCommandBufferId command_buffer_id); -WGPUComputePassId wgpu_command_encoder_begin_compute_pass(WGPUCommandEncoderId encoder_id, - const WGPUComputePassDescriptor *desc); +WGPURawPass *wgpu_command_encoder_begin_compute_pass(WGPUCommandEncoderId encoder_id, + const WGPUComputePassDescriptor *_desc); -WGPURenderPassId wgpu_command_encoder_begin_render_pass(WGPUCommandEncoderId encoder_id, - const WGPURenderPassDescriptor *desc); +WGPURawRenderPass *wgpu_command_encoder_begin_render_pass(WGPUCommandEncoderId encoder_id, + const WGPURenderPassDescriptor *desc); void wgpu_command_encoder_copy_buffer_to_buffer(WGPUCommandEncoderId command_encoder_id, WGPUBufferId source, @@ -721,27 +762,30 @@ void wgpu_command_encoder_destroy(WGPUCommandEncoderId command_encoder_id); WGPUCommandBufferId wgpu_command_encoder_finish(WGPUCommandEncoderId encoder_id, const WGPUCommandBufferDescriptor *desc); -void wgpu_compute_pass_dispatch(WGPUComputePassId pass_id, uint32_t x, uint32_t y, uint32_t z); +void wgpu_compute_pass_dispatch(WGPURawPass *pass, + uint32_t groups_x, + uint32_t groups_y, + uint32_t groups_z); -void wgpu_compute_pass_dispatch_indirect(WGPUComputePassId pass_id, - WGPUBufferId indirect_buffer_id, - WGPUBufferAddress indirect_offset); +void wgpu_compute_pass_dispatch_indirect(WGPURawPass *pass, + WGPUBufferId buffer_id, + WGPUBufferAddress offset); void wgpu_compute_pass_end_pass(WGPUComputePassId pass_id); -void wgpu_compute_pass_insert_debug_marker(WGPUComputePassId _pass_id, WGPURawString _label); +void wgpu_compute_pass_insert_debug_marker(WGPURawPass *_pass, WGPURawString _label); -void wgpu_compute_pass_pop_debug_group(WGPUComputePassId _pass_id); +void wgpu_compute_pass_pop_debug_group(WGPURawPass *_pass); -void wgpu_compute_pass_push_debug_group(WGPUComputePassId _pass_id, WGPURawString _label); +void wgpu_compute_pass_push_debug_group(WGPURawPass *_pass, WGPURawString _label); -void wgpu_compute_pass_set_bind_group(WGPUComputePassId pass_id, +void wgpu_compute_pass_set_bind_group(WGPURawPass *pass, uint32_t index, WGPUBindGroupId bind_group_id, const WGPUBufferAddress *offsets, - uintptr_t offsets_length); + uintptr_t offset_length); -void wgpu_compute_pass_set_pipeline(WGPUComputePassId pass_id, WGPUComputePipelineId pipeline_id); +void wgpu_compute_pass_set_pipeline(WGPURawPass *pass, WGPUComputePipelineId pipeline_id); WGPUSurfaceId wgpu_create_surface_from_metal_layer(void *layer); @@ -798,74 +842,74 @@ void wgpu_queue_submit(WGPUQueueId queue_id, const WGPUCommandBufferId *command_buffers, uintptr_t command_buffers_length); -void wgpu_render_pass_draw(WGPURenderPassId pass_id, +void wgpu_render_pass_draw(WGPURawRenderPass *pass, uint32_t vertex_count, uint32_t instance_count, uint32_t first_vertex, uint32_t first_instance); -void wgpu_render_pass_draw_indexed(WGPURenderPassId pass_id, +void wgpu_render_pass_draw_indexed(WGPURawRenderPass *pass, uint32_t index_count, uint32_t instance_count, uint32_t first_index, int32_t base_vertex, uint32_t first_instance); -void wgpu_render_pass_draw_indexed_indirect(WGPURenderPassId pass_id, - WGPUBufferId indirect_buffer_id, - WGPUBufferAddress indirect_offset); +void wgpu_render_pass_draw_indexed_indirect(WGPURawRenderPass *pass, + WGPUBufferId buffer_id, + WGPUBufferAddress offset); -void wgpu_render_pass_draw_indirect(WGPURenderPassId pass_id, - WGPUBufferId indirect_buffer_id, - WGPUBufferAddress indirect_offset); +void wgpu_render_pass_draw_indirect(WGPURawRenderPass *pass, + WGPUBufferId buffer_id, + WGPUBufferAddress offset); void wgpu_render_pass_end_pass(WGPURenderPassId pass_id); -void wgpu_render_pass_execute_bundles(WGPURenderPassId _pass_id, +void wgpu_render_pass_execute_bundles(WGPURawRenderPass *_pass, const WGPURenderBundleId *_bundles, uintptr_t _bundles_length); -void wgpu_render_pass_insert_debug_marker(WGPURenderPassId _pass_id, WGPURawString _label); +void wgpu_render_pass_insert_debug_marker(WGPURawRenderPass *_pass, WGPURawString _label); -void wgpu_render_pass_pop_debug_group(WGPURenderPassId _pass_id); +void wgpu_render_pass_pop_debug_group(WGPURawRenderPass *_pass); -void wgpu_render_pass_push_debug_group(WGPURenderPassId _pass_id, WGPURawString _label); +void wgpu_render_pass_push_debug_group(WGPURawRenderPass *_pass, WGPURawString _label); -void wgpu_render_pass_set_bind_group(WGPURenderPassId pass_id, +void wgpu_render_pass_set_bind_group(WGPURawRenderPass *pass, uint32_t index, WGPUBindGroupId bind_group_id, const WGPUBufferAddress *offsets, - uintptr_t offsets_length); + uintptr_t offset_length); -void wgpu_render_pass_set_blend_color(WGPURenderPassId pass_id, const WGPUColor *color); +void wgpu_render_pass_set_blend_color(WGPURawRenderPass *pass, const WGPUColor *color); -void wgpu_render_pass_set_index_buffer(WGPURenderPassId pass_id, +void wgpu_render_pass_set_index_buffer(WGPURawRenderPass *pass, WGPUBufferId buffer_id, WGPUBufferAddress offset); -void wgpu_render_pass_set_pipeline(WGPURenderPassId pass_id, WGPURenderPipelineId pipeline_id); +void wgpu_render_pass_set_pipeline(WGPURawRenderPass *pass, WGPURenderPipelineId pipeline_id); -void wgpu_render_pass_set_scissor_rect(WGPURenderPassId pass_id, +void wgpu_render_pass_set_scissor_rect(WGPURawRenderPass *pass, uint32_t x, uint32_t y, uint32_t w, uint32_t h); -void wgpu_render_pass_set_stencil_reference(WGPURenderPassId pass_id, uint32_t value); +void wgpu_render_pass_set_stencil_reference(WGPURawRenderPass *pass, uint32_t value); -void wgpu_render_pass_set_vertex_buffers(WGPURenderPassId pass_id, +void wgpu_render_pass_set_vertex_buffers(WGPURawRenderPass *pass, uint32_t start_slot, - const WGPUBufferId *buffers, + const WGPUBufferId *buffer_ids, const WGPUBufferAddress *offsets, uintptr_t length); -void wgpu_render_pass_set_viewport(WGPURenderPassId pass_id, +void wgpu_render_pass_set_viewport(WGPURawRenderPass *pass, float x, float y, float w, float h, - float min_depth, - float max_depth); + float depth_min, + float depth_max); void wgpu_request_adapter_async(const WGPURequestAdapterOptions *desc, WGPUBackendBit mask, diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml index 7ce6d763b7..2df606641b 100644 --- a/wgpu-core/Cargo.toml +++ b/wgpu-core/Cargo.toml @@ -29,6 +29,7 @@ log = "0.4" hal = { package = "gfx-hal", version = "0.4" } gfx-backend-empty = { version = "0.4" } parking_lot = "0.9" +peek-poke = { git = "https://github.com/kvark/peek-poke", rev = "969bd7fe2be1a83f87916dc8b388c63cfd457075" } rendy-memory = "0.5" rendy-descriptor = "0.5" serde = { version = "1.0", features = ["serde_derive"], optional = true } diff --git a/wgpu-core/src/command/compute.rs b/wgpu-core/src/command/compute.rs index a4da853735..631488dfec 100644 --- a/wgpu-core/src/command/compute.rs +++ b/wgpu-core/src/command/compute.rs @@ -6,237 +6,294 @@ use crate::{ command::{ bind::{Binder, LayoutChange}, CommandBuffer, + PhantomSlice, }, device::{all_buffer_stages, BIND_BUFFER_ALIGNMENT}, - hub::{GfxBackend, Global, IdentityFilter, Token}, - id::{BindGroupId, BufferId, CommandBufferId, ComputePassId, ComputePipelineId}, + hub::{GfxBackend, Global, Token}, + id, resource::BufferUsage, - track::TrackerSet, BufferAddress, - Stored, }; -use hal::{self, command::CommandBuffer as _}; +use hal::command::CommandBuffer as _; +use peek_poke::{Peek, PeekCopy, Poke}; 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, - } - } +#[derive(Clone, Copy, Debug, PeekCopy, Poke)] +enum ComputeCommand { + SetBindGroup { + index: u8, + num_dynamic_offsets: u8, + bind_group_id: id::BindGroupId, + phantom_offsets: PhantomSlice, + }, + SetPipeline(id::ComputePipelineId), + Dispatch([u32; 3]), + DispatchIndirect { + buffer_id: id::BufferId, + offset: BufferAddress, + }, + End, } -// Common routines between render/compute +impl super::RawPass { + pub fn new_compute(parent: id::CommandEncoderId) -> Self { + Self::from_vec(Vec::::with_capacity(1), parent) + } -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. - log::debug!("Compute pass {:?} {:#?}", pass_id, pass.trackers); - cmb.trackers = pass.trackers; - cmb.raw.push(pass.raw); + pub unsafe fn finish_compute(self) -> (Vec, id::CommandEncoderId) { + self.finish_with(ComputeCommand::End) } } +#[repr(C)] +#[derive(Clone, Debug, Default)] +pub struct ComputePassDescriptor { + pub todo: u32, +} + +// Common routines between render/compute + impl Global { - pub fn compute_pass_set_bind_group( + pub fn command_encoder_run_compute_pass( &self, - pass_id: ComputePassId, - index: u32, - bind_group_id: BindGroupId, - offsets: &[BufferAddress], + encoder_id: id::CommandEncoderId, + raw_data: &[u8], ) { 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.last_mut().unwrap(); + let mut binder = Binder::new(cmb.features.max_bind_groups); + 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 (pipeline_guard, mut token) = hub.compute_pipelines.read(&mut token); 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, - &*buffer_guard, - &*texture_guard, - ); + let mut peeker = raw_data.as_ptr(); + let raw_data_end = unsafe { + raw_data.as_ptr().add(raw_data.len()) + }; + let mut command = ComputeCommand::Dispatch([0; 3]); // dummy + loop { + assert!(unsafe { peeker.add(ComputeCommand::max_size()) } <= raw_data_end); + peeker = unsafe { command.peek_from(peeker) }; + match command { + ComputeCommand::SetBindGroup { index, num_dynamic_offsets, bind_group_id, phantom_offsets } => { + let (new_peeker, offsets) = unsafe { + phantom_offsets.decode(peeker, num_dynamic_offsets as usize, raw_data_end) + }; + peeker = new_peeker; + + 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 + ); + } + } + + let bind_group = cmb + .trackers + .bind_groups + .use_extend(&*bind_group_guard, bind_group_id, (), ()) + .unwrap(); + assert_eq!(bind_group.dynamic_count, offsets.len()); + + log::trace!( + "Encoding barriers on binding of {:?} to {:?}", + bind_group_id, + encoder_id + ); + CommandBuffer::insert_barriers( + raw, + &mut cmb.trackers, + &bind_group.used, + &*buffer_guard, + &*texture_guard, + ); + + if let Some((pipeline_layout_id, follow_ups)) = binder + .provide_entry(index as usize, bind_group_id, bind_group, offsets) + { + let bind_groups = iter::once(bind_group.raw.raw()) + .chain(follow_ups.clone().map(|(bg_id, _)| bind_group_guard[bg_id].raw.raw())); + unsafe { + raw.bind_compute_descriptor_sets( + &pipeline_layout_guard[pipeline_layout_id].raw, + index as usize, + bind_groups, + offsets + .iter() + .chain(follow_ups.flat_map(|(_, offsets)| offsets)) + .map(|&off| off as hal::command::DescriptorSetOffset), + ); + } + } + } + ComputeCommand::SetPipeline(pipeline_id) => { + let pipeline = &pipeline_guard[pipeline_id]; + + unsafe { + raw.bind_compute_pipeline(&pipeline.raw); + } + + // Rebind resources + if binder.pipeline_layout_id != Some(pipeline.layout_id) { + let pipeline_layout = &pipeline_layout_guard[pipeline.layout_id]; + binder.pipeline_layout_id = Some(pipeline.layout_id); + binder + .reset_expectations(pipeline_layout.bind_group_layout_ids.len()); + let mut is_compatible = true; - if let Some((pipeline_layout_id, follow_ups)) = pass - .binder - .provide_entry(index as usize, bind_group_id, bind_group, offsets) - { - let bind_groups = iter::once(bind_group.raw.raw()) - .chain(follow_ups.clone().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_ups.flat_map(|(_, offsets)| offsets)) - .map(|&off| off as hal::command::DescriptorSetOffset), - ); + for (index, (entry, &bgl_id)) in 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 { + 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; + } + } + } + } + } + ComputeCommand::Dispatch(groups) => { + unsafe { + raw.dispatch(groups); + } + } + ComputeCommand::DispatchIndirect { buffer_id, offset } => { + let (src_buffer, src_pending) = cmb.trackers.buffers.use_replace( + &*buffer_guard, + buffer_id, + (), + BufferUsage::INDIRECT, + ); + assert!(src_buffer.usage.contains(BufferUsage::INDIRECT)); + + let barriers = src_pending.map(|pending| pending.into_hal(src_buffer)); + + unsafe { + raw.pipeline_barrier( + all_buffer_stages() .. all_buffer_stages(), + hal::memory::Dependencies::empty(), + barriers, + ); + raw.dispatch_indirect(&src_buffer.raw, offset); + } + } + ComputeCommand::End => break, } - }; + } } +} - // Compute-specific routines +pub mod compute_ffi { + use super::{ + ComputeCommand, + super::{PhantomSlice, RawPass}, + }; + use crate::{ + id, + BufferAddress, + RawString, + }; + use std::{convert::TryInto, slice}; - pub fn compute_pass_dispatch( - &self, - pass_id: ComputePassId, - x: u32, - y: u32, - z: u32, + #[no_mangle] + pub unsafe extern "C" fn wgpu_compute_pass_set_bind_group( + pass: &mut RawPass, + index: u32, + bind_group_id: id::BindGroupId, + offsets: *const BufferAddress, + offset_length: usize, ) { - 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]); - } + pass.encode(&ComputeCommand::SetBindGroup { + index: index.try_into().unwrap(), + num_dynamic_offsets: offset_length.try_into().unwrap(), + bind_group_id, + phantom_offsets: PhantomSlice::new(), + }); + pass.encode_slice( + slice::from_raw_parts(offsets, offset_length), + ); } - pub fn compute_pass_dispatch_indirect( - &self, - pass_id: ComputePassId, - indirect_buffer_id: BufferId, - indirect_offset: BufferAddress, + #[no_mangle] + pub unsafe extern "C" fn wgpu_compute_pass_set_pipeline( + pass: &mut RawPass, + pipeline_id: id::ComputePipelineId, ) { - let hub = B::hub(self); - let mut token = Token::root(); - let (mut pass_guard, mut token) = hub.compute_passes.write(&mut token); - let (buffer_guard, _) = hub.buffers.read(&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)); + pass.encode(&ComputeCommand::SetPipeline(pipeline_id)); + } - let barriers = src_pending.map(|pending| pending.into_hal(src_buffer)); + #[no_mangle] + pub unsafe extern "C" fn wgpu_compute_pass_dispatch( + pass: &mut RawPass, + groups_x: u32, + groups_y: u32, + groups_z: u32, + ) { + pass.encode(&ComputeCommand::Dispatch([groups_x, groups_y, groups_z])); + } - 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); - } + #[no_mangle] + pub unsafe extern "C" fn wgpu_compute_pass_dispatch_indirect( + pass: &mut RawPass, + buffer_id: id::BufferId, + offset: BufferAddress, + ) { + pass.encode(&ComputeCommand::DispatchIndirect { + buffer_id, + offset, + }); } - pub fn compute_pass_set_pipeline( - &self, - pass_id: ComputePassId, - pipeline_id: ComputePipelineId, + #[no_mangle] + pub extern "C" fn wgpu_compute_pass_push_debug_group( + _pass: &mut RawPass, + _label: RawString, ) { - 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]; + //TODO + } - unsafe { - pass.raw.bind_compute_pipeline(&pipeline.raw); - } + #[no_mangle] + pub extern "C" fn wgpu_compute_pass_pop_debug_group( + _pass: &mut RawPass, + ) { + //TODO + } - // Rebind resources - if pass.binder.pipeline_layout_id != Some(pipeline.layout_id) { - let pipeline_layout = &pipeline_layout_guard[pipeline.layout_id]; - pass.binder.pipeline_layout_id = Some(pipeline.layout_id); - 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; - } - } - } - } + #[no_mangle] + pub extern "C" fn wgpu_compute_pass_insert_debug_marker( + _pass: &mut RawPass, + _label: RawString, + ) { + //TODO } } diff --git a/wgpu-core/src/command/mod.rs b/wgpu-core/src/command/mod.rs index 9eaeabbdd4..d623e646df 100644 --- a/wgpu-core/src/command/mod.rs +++ b/wgpu-core/src/command/mod.rs @@ -14,40 +14,21 @@ pub use self::render::*; pub use self::transfer::*; use crate::{ - conv, device::{ + MAX_COLOR_TARGETS, 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}, + hub::{GfxBackend, Global, Storage, Token}, + id, + resource::{Buffer, Texture}, track::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, marker::PhantomData, mem, slice, @@ -55,58 +36,96 @@ use std::{ }; -pub struct RenderBundle { - _raw: B::CommandBuffer, -} +#[derive(Clone, Copy, Debug, peek_poke::PeekCopy, peek_poke::Poke)] +struct PhantomSlice(PhantomData); -#[repr(C)] -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -pub enum LoadOp { - Clear = 0, - Load = 1, -} +impl PhantomSlice { + fn new() -> Self { + PhantomSlice(PhantomData) + } -#[repr(C)] -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -pub enum StoreOp { - Clear = 0, - Store = 1, + unsafe fn decode<'a>( + self, pointer: *const u8, count: usize, bound: *const u8 + ) -> (*const u8, &'a [T]) { + let align_offset = pointer.align_offset(mem::align_of::()); + let aligned = pointer.add(align_offset); + let size = count * mem::size_of::(); + let end = aligned.add(size); + assert!(end <= bound); + (end, slice::from_raw_parts(aligned as *const T, count)) + } } #[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, +pub struct RawPass { + data: *mut u8, + base: *mut u8, + capacity: usize, + parent: id::CommandEncoderId, } -#[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, -} +impl RawPass { + fn from_vec( + mut vec: Vec, + encoder_id: id::CommandEncoderId, + ) -> Self { + let ptr = vec.as_mut_ptr() as *mut u8; + let capacity = vec.capacity() * mem::size_of::(); + mem::forget(vec); + RawPass { + data: ptr, + base: ptr, + capacity, + parent: encoder_id, + } + } -#[repr(C)] -#[derive(Debug)] -pub struct RenderPassDescriptor { - pub color_attachments: *const RenderPassColorAttachmentDescriptor, - pub color_attachments_length: usize, - pub depth_stencil_attachment: *const RenderPassDepthStencilAttachmentDescriptor, + /// Finish encoding a raw pass. + /// + /// The last command is provided, yet the encoder + /// is guaranteed to have exactly `C::max_size()` space for it. + unsafe fn finish_with( + mut self, command: C + ) -> (Vec, id::CommandEncoderId) { + self.ensure_extra_size(C::max_size()); + command.poke_into(self.data); + let size = self.data as usize + C::max_size() - self.base as usize; + assert!(size <= self.capacity); + (Vec::from_raw_parts(self.base, size, self.capacity), self.parent) + } + + unsafe fn ensure_extra_size(&mut self, extra_size: usize) { + let size = self.data as usize - self.base as usize; + if size + extra_size > self.capacity { + let mut vec = Vec::from_raw_parts(self.base, size, self.capacity); + vec.reserve(extra_size); + //let (data, size, capacity) = vec.into_raw_parts(); //TODO: when stable + self.data = vec.as_mut_ptr().add(vec.len()); + self.base = vec.as_mut_ptr(); + self.capacity = vec.capacity(); + mem::forget(vec); + } + } + + #[inline] + unsafe fn encode(&mut self, command: &C) { + self.ensure_extra_size(C::max_size()); + self.data = command.poke_into(self.data); + } + + #[inline] + unsafe fn encode_slice(&mut self, data: &[T]) { + let align_offset = self.data.align_offset(mem::align_of::()); + let extra = align_offset + mem::size_of::() * data.len(); + self.ensure_extra_size(extra); + slice::from_raw_parts_mut(self.data.add(align_offset) as *mut T, data.len()) + .copy_from_slice(data); + self.data = self.data.add(extra); + } } -#[repr(C)] -#[derive(Clone, Debug, Default)] -pub struct ComputePassDescriptor { - pub todo: u32, +pub struct RenderBundle { + _raw: B::CommandBuffer, } #[derive(Debug)] @@ -114,10 +133,10 @@ pub struct CommandBuffer { pub(crate) raw: Vec, is_recording: bool, recorded_thread_id: ThreadId, - pub(crate) device_id: Stored, + 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) used_swap_chain: Option<(Stored, B::Framebuffer)>, pub(crate) features: Features, } @@ -126,9 +145,11 @@ impl CommandBuffer { raw: &mut B::CommandBuffer, base: &mut TrackerSet, head: &TrackerSet, - buffer_guard: &Storage, BufferId>, - texture_guard: &Storage, TextureId>, + buffer_guard: &Storage, id::BufferId>, + texture_guard: &Storage, id::TextureId>, ) { + use hal::command::CommandBuffer as _; + debug_assert_eq!(B::VARIANT, base.backend()); debug_assert_eq!(B::VARIANT, head.backend()); @@ -175,12 +196,66 @@ pub struct CommandBufferDescriptor { pub todo: u32, } +#[no_mangle] +pub unsafe extern "C" fn wgpu_command_encoder_begin_compute_pass( + encoder_id: id::CommandEncoderId, + _desc: Option<&ComputePassDescriptor>, +) -> *mut RawPass { + let pass = RawPass::new_compute(encoder_id); + Box::into_raw(Box::new(pass)) +} + +type RawRenderPassColorAttachmentDescriptor = + RenderPassColorAttachmentDescriptorBase; + +#[repr(C)] +pub struct RawRenderTargets { + pub colors: [RawRenderPassColorAttachmentDescriptor; MAX_COLOR_TARGETS], + pub depth_stencil: RenderPassDepthStencilAttachmentDescriptor, +} + +#[repr(C)] +pub struct RawRenderPass { + raw: RawPass, + targets: RawRenderTargets, +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_command_encoder_begin_render_pass( + encoder_id: id::CommandEncoderId, + desc: &RenderPassDescriptor, +) -> *mut RawRenderPass { + let mut colors: [RawRenderPassColorAttachmentDescriptor; MAX_COLOR_TARGETS] = mem::zeroed(); + for (color, at) in colors + .iter_mut() + .zip(slice::from_raw_parts(desc.color_attachments, desc.color_attachments_length)) + { + *color = RawRenderPassColorAttachmentDescriptor { + attachment: at.attachment, + resolve_target: at.resolve_target.map_or(id::TextureViewId::ERROR, |rt| *rt), + load_op: at.load_op, + store_op: at.store_op, + clear_color: at.clear_color, + }; + } + let pass = RawRenderPass { + raw: RawPass::new_render(encoder_id), + targets: RawRenderTargets { + colors, + depth_stencil: desc.depth_stencil_attachment + .cloned() + .unwrap_or_else(|| mem::zeroed()), + }, + }; + Box::into_raw(Box::new(pass)) +} + impl Global { pub fn command_encoder_finish( &self, - encoder_id: CommandEncoderId, + encoder_id: id::CommandEncoderId, _desc: &CommandBufferDescriptor, - ) -> CommandBufferId { + ) -> id::CommandBufferId { let hub = B::hub(self); let mut token = Token::root(); //TODO: actually close the last recorded command buffer @@ -196,517 +271,3 @@ impl Global { 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 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" - ); - - const MAX_TOTAL_ATTACHMENTS: usize = 10; - type OutputAttachment<'a> = ( - &'a Stored, - &'a hal::image::SubresourceRange, - Option, - ); - let mut output_attachments = ArrayVec::<[OutputAttachment; MAX_TOTAL_ATTACHMENTS]>::new(); - - log::trace!( - "Encoding render pass begin in command buffer {:?}", - encoder_id - ); - let rp_key = { - let depth_stencil = match depth_stencil_attachment { - Some(at) => { - let view = cmb.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 source_id = match view.inner { - TextureViewInner::Native { ref source_id, .. } => source_id, - TextureViewInner::SwapChain { .. } => { - panic!("Unexpected depth/stencil use of swapchain image!") - } - }; - - // Using render pass for transition. - let consistent_usage = cmb.trackers.textures.query( - source_id.value, - view.range.clone(), - ); - output_attachments.push((source_id, &view.range, consistent_usage)); - - let old_layout = match consistent_usage { - Some(usage) => conv::map_texture_state( - usage, - hal::format::Aspects::DEPTH | hal::format::Aspects::STENCIL, - ).1, - None => hal::image::Layout::DepthStencilAttachmentOptimal, - }; - - Some(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, - }) - } - None => None, - }; - - 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 = cmb.trackers.views.init( - at.attachment, - view.life_guard.add_ref(), - PhantomData, - ).is_ok(); - - let layouts = match view.inner { - TextureViewInner::Native { ref source_id, .. } => { - let consistent_usage = cmb.trackers.textures.query( - source_id.value, - view.range.clone(), - ); - output_attachments.push((source_id, &view.range, consistent_usage)); - - let old_layout = match consistent_usage { - Some(usage) => conv::map_texture_state(usage, hal::format::Aspects::COLOR).1, - None => 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.add_ref(), - }); - } - - 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 = cmb.trackers.views.init( - resolve_target, - view.life_guard.add_ref(), - PhantomData, - ).is_ok(); - - let layouts = match view.inner { - TextureViewInner::Native { ref source_id, .. } => { - let consistent_usage = cmb.trackers.textures.query( - source_id.value, - view.range.clone(), - ); - output_attachments.push((source_id, &view.range, consistent_usage)); - - let old_layout = match consistent_usage { - Some(usage) => conv::map_texture_state(usage, hal::format::Aspects::COLOR).1, - None => 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.add_ref(), - }); - } - - 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, - } - }; - - let mut trackers = TrackerSet::new(B::VARIANT); - for (source_id, view_range, consistent_usage) in output_attachments { - let texture = &texture_guard[source_id.value]; - assert!(texture.usage.contains(TextureUsage::OUTPUT_ATTACHMENT)); - - let usage = consistent_usage.unwrap_or(TextureUsage::OUTPUT_ATTACHMENT); - // this is important to record the `first` state. - let _ = trackers.textures.change_replace( - source_id.value, - &source_id.ref_count, - view_range.clone(), - usage, - ); - if consistent_usage.is_some() { - // If we expect the texture to be transited to a new state by the - // render pass configuration, make the tracker aware of that. - let _ = trackers.textures.change_replace( - source_id.value, - &source_id.ref_count, - view_range.clone(), - TextureUsage::OUTPUT_ATTACHMENT, - ); - }; - } - - 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.is_null()) - { - for (i, at) in color_attachments.iter().enumerate() { - if at.resolve_target.is_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.add_ref(), - }, - context, - trackers, - 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.add_ref(), - }; - - 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 index 3ca9004ae1..959cf7f04e 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -5,23 +5,165 @@ use crate::{ command::{ bind::{Binder, LayoutChange}, - CommandBuffer, + PhantomSlice, + RawRenderTargets, }, conv, - device::{RenderPassContext, BIND_BUFFER_ALIGNMENT, MAX_VERTEX_BUFFERS}, - hub::{GfxBackend, Global, IdentityFilter, Token}, - id::{BindGroupId, BufferId, CommandBufferId, RenderPassId, RenderPipelineId}, + device::{ + FramebufferKey, + RenderPassContext, + RenderPassKey, + BIND_BUFFER_ALIGNMENT, + MAX_VERTEX_BUFFERS, + MAX_COLOR_TARGETS, + }, + hub::{GfxBackend, Global, Token}, + id, pipeline::{IndexFormat, InputStepMode, PipelineFlags}, - resource::BufferUsage, + resource::{BufferUsage, TextureUsage, TextureViewInner}, track::TrackerSet, BufferAddress, Color, Stored, }; +use arrayvec::ArrayVec; use hal::command::CommandBuffer as _; +use peek_poke::{Peek, PeekCopy, Poke}; + +use std::{ + borrow::Borrow, + collections::hash_map::Entry, + iter, + marker::PhantomData, + ops::Range, +}; + -use std::{iter, ops::Range}; +#[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 RenderPassColorAttachmentDescriptorBase { + pub attachment: T, + pub resolve_target: R, + pub load_op: LoadOp, + pub store_op: StoreOp, + pub clear_color: Color, +} + +#[repr(C)] +#[derive(Clone, Debug)] +pub struct RenderPassDepthStencilAttachmentDescriptorBase { + 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, +} + +//Note: this could look better if `cbindgen` wasn't confused by &T used in place of +// a generic paramter, it's not able to mange +pub type OptionRef<'a, T> = Option<&'a T>; +pub type RenderPassColorAttachmentDescriptor<'a> = + RenderPassColorAttachmentDescriptorBase>; +pub type RenderPassDepthStencilAttachmentDescriptor = + RenderPassDepthStencilAttachmentDescriptorBase; + +#[repr(C)] +#[derive(Debug)] +pub struct RenderPassDescriptor<'a> { + pub color_attachments: *const RenderPassColorAttachmentDescriptor<'a>, + pub color_attachments_length: usize, + pub depth_stencil_attachment: Option<&'a RenderPassDepthStencilAttachmentDescriptor>, +} + +#[derive(Clone, Copy, Debug, PeekCopy, Poke)] +pub struct Rect { + pub x: T, + pub y: T, + pub w: T, + pub h: T, +} + +#[derive(Clone, Copy, Debug, PeekCopy, Poke)] +enum RenderCommand { + SetBindGroup { + index: u8, + num_dynamic_offsets: u8, + bind_group_id: id::BindGroupId, + phantom_offsets: PhantomSlice, + }, + SetPipeline(id::RenderPipelineId), + SetIndexBuffer { + buffer_id: id::BufferId, + offset: BufferAddress, + }, + SetVertexBuffers { + start_index: u8, + count: u8, + phantom_buffer_ids: PhantomSlice, + phantom_offsets: PhantomSlice, + }, + SetBlendColor(Color), + SetStencilReference(u32), + SetViewport { + rect: Rect, + //TODO: use half-float to reduce the size? + depth_min: f32, + depth_max: f32, + }, + SetScissor(Rect), + Draw { + vertex_count: u32, + instance_count: u32, + first_vertex: u32, + first_instance: u32, + }, + DrawIndexed { + index_count: u32, + instance_count: u32, + first_index: u32, + base_vertex: i32, + first_instance: u32, + }, + DrawIndirect { + buffer_id: id::BufferId, + offset: BufferAddress, + }, + DrawIndexedIndirect { + buffer_id: id::BufferId, + offset: BufferAddress, + }, + End, +} + +impl super::RawPass { + pub fn new_render(parent_id: id::CommandEncoderId) -> Self { + Self::from_vec(Vec::::with_capacity(1), parent_id) + } +} + +impl super::RawRenderPass { + pub unsafe fn finish_render(self) -> (Vec, id::CommandEncoderId, RawRenderTargets) { + let (vec, parent_id) = self.raw.finish_with(RenderCommand::End); + (vec, parent_id, self.targets) + } +} #[derive(Debug, PartialEq)] enum OptionalState { @@ -51,7 +193,7 @@ enum DrawError { #[derive(Debug)] pub struct IndexState { - bound_buffer_view: Option<(BufferId, Range)>, + bound_buffer_view: Option<(id::BufferId, Range)>, format: IndexFormat, limit: u32, } @@ -111,50 +253,15 @@ impl VertexState { } #[derive(Debug)] -pub struct RenderPass { - raw: B::CommandBuffer, - cmb_id: Stored, - context: RenderPassContext, +struct State { binder: Binder, - trackers: TrackerSet, - blend_color_status: OptionalState, - stencil_reference_status: OptionalState, - index_state: IndexState, - vertex_state: VertexState, - sample_count: u8, + blend_color: OptionalState, + stencil_reference: OptionalState, + index: IndexState, + vertex: VertexState, } -impl RenderPass { - pub(crate) fn new( - raw: B::CommandBuffer, - cmb_id: Stored, - context: RenderPassContext, - trackers: TrackerSet, - sample_count: u8, - max_bind_groups: u32, - ) -> Self { - RenderPass { - raw, - cmb_id, - context, - binder: Binder::new(max_bind_groups), - trackers, - 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, - } - } - +impl State { fn is_ready(&self) -> Result<(), DrawError> { //TODO: vertex buffers let bind_mask = self.binder.invalid_mask(); @@ -164,10 +271,10 @@ impl RenderPass { index: bind_mask.trailing_zeros() as u32, }); } - if self.blend_color_status == OptionalState::Required { + if self.blend_color == OptionalState::Required { return Err(DrawError::MissingBlendColor); } - if self.stencil_reference_status == OptionalState::Required { + if self.stencil_reference == OptionalState::Required { return Err(DrawError::MissingStencilReference); } Ok(()) @@ -176,505 +283,1042 @@ impl RenderPass { // Common routines between render/compute -impl> Global { - pub fn render_pass_end_pass(&self, pass_id: RenderPassId) { +impl Global { + pub fn command_encoder_run_render_pass( + &self, + encoder_id: id::CommandEncoderId, + color_attachments: &[RenderPassColorAttachmentDescriptor], + depth_stencil_attachment: Option<&RenderPassDepthStencilAttachmentDescriptor>, + raw_data: &[u8], + ) { 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 (mut pass, mut token) = hub.render_passes.unregister(pass_id, &mut token); + + let mut trackers = TrackerSet::new(B::VARIANT); + let cmb = &mut cmb_guard[encoder_id]; + let device = &device_guard[cmb.device_id.value]; + let mut raw = device.com_allocator.extend(cmb); + unsafe { - pass.raw.end_render_pass(); + raw.begin_primary(hal::command::CommandBufferFlags::ONE_TIME_SUBMIT); } - pass.trackers.optimize(); - log::debug!("Render pass {:?} {:#?}", pass_id, pass.trackers); - let cmb = &mut cmb_guard[pass.cmb_id.value]; + 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 (pipeline_guard, mut token) = hub.render_pipelines.read(&mut token); 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, - &*buffer_guard, - &*texture_guard, - ); - unsafe { last.finish() }; - } - None => { - cmb.trackers.merge_extend(&pass.trackers); - } - } + let (texture_guard, mut token) = hub.textures.read(&mut token); + let (view_guard, _) = hub.texture_views.read(&mut token); + + let (context, sample_count) = { + use hal::{adapter::PhysicalDevice as _, device::Device as _}; + + let limits = adapter_guard[device.adapter_id] + .raw + .physical_device + .limits(); + let samples_count_limit = limits.framebuffer_color_sample_counts; + let base_trackers = &cmb.trackers; + + let mut extent = None; + let mut used_swap_chain_image = None::>; + + 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" + ); - if false { - log::debug!("Command buffer {:?} after render pass {:#?}", - pass.cmb_id.value, cmb.trackers); - } + const MAX_TOTAL_ATTACHMENTS: usize = 10; + type OutputAttachment<'a> = ( + &'a Stored, + &'a hal::image::SubresourceRange, + Option, + ); + let mut output_attachments = ArrayVec::<[OutputAttachment; MAX_TOTAL_ATTACHMENTS]>::new(); - cmb.raw.push(pass.raw); - } + log::trace!( + "Encoding render pass begin in command buffer {:?}", + encoder_id + ); + let rp_key = { + let depth_stencil = match depth_stencil_attachment { + Some(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 source_id = match view.inner { + TextureViewInner::Native { ref source_id, .. } => source_id, + TextureViewInner::SwapChain { .. } => { + panic!("Unexpected depth/stencil use of swapchain image!") + } + }; + + // Using render pass for transition. + let consistent_usage = base_trackers.textures.query( + source_id.value, + view.range.clone(), + ); + output_attachments.push((source_id, &view.range, consistent_usage)); + + let old_layout = match consistent_usage { + Some(usage) => conv::map_texture_state( + usage, + hal::format::Aspects::DEPTH | hal::format::Aspects::STENCIL, + ).1, + None => hal::image::Layout::DepthStencilAttachmentOptimal, + }; + + Some(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, + }) + } + None => None, + }; - 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 colors = ArrayVec::new(); + let mut resolves = ArrayVec::new(); - let (mut pass_guard, _) = hub.render_passes.write(&mut token); - let pass = &mut pass_guard[pass_id]; + 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.add_ref(), + PhantomData, + ).is_ok(); + + let layouts = match view.inner { + TextureViewInner::Native { ref source_id, .. } => { + let consistent_usage = base_trackers.textures.query( + source_id.value, + view.range.clone(), + ); + output_attachments.push((source_id, &view.range, consistent_usage)); - let bind_group = pass - .trackers - .bind_groups - .use_extend(&*bind_group_guard, bind_group_id, (), ()) - .unwrap(); + let old_layout = match consistent_usage { + Some(usage) => conv::map_texture_state(usage, hal::format::Aspects::COLOR).1, + None => 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.add_ref(), + }); + } + + 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, + }); + } - assert_eq!(bind_group.dynamic_count, offsets.len()); + for &resolve_target in color_attachments + .iter() + .flat_map(|at| at.resolve_target) + { + 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.add_ref(), + PhantomData, + ).is_ok(); + + let layouts = match view.inner { + TextureViewInner::Native { ref source_id, .. } => { + let consistent_usage = base_trackers.textures.query( + source_id.value, + view.range.clone(), + ); + output_attachments.push((source_id, &view.range, consistent_usage)); - 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 + let old_layout = match consistent_usage { + Some(usage) => conv::map_texture_state(usage, hal::format::Aspects::COLOR).1, + None => 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.add_ref(), + }); + } + + 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, + } + }; + + for (source_id, view_range, consistent_usage) in output_attachments { + let texture = &texture_guard[source_id.value]; + assert!(texture.usage.contains(TextureUsage::OUTPUT_ATTACHMENT)); + + let usage = consistent_usage.unwrap_or(TextureUsage::OUTPUT_ATTACHMENT); + // this is important to record the `first` state. + let _ = trackers.textures.change_replace( + source_id.value, + &source_id.ref_count, + view_range.clone(), + usage, ); + if consistent_usage.is_some() { + // If we expect the texture to be transited to a new state by the + // render pass configuration, make the tracker aware of that. + let _ = trackers.textures.change_replace( + source_id.value, + &source_id.ref_count, + view_range.clone(), + TextureUsage::OUTPUT_ATTACHMENT, + ); + }; } - } - pass.trackers.merge_extend(&bind_group.used); + 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::<[_; MAX_COLOR_TARGETS]>::new(); + let mut attachment_index = color_attachments.len(); + if color_attachments + .iter() + .any(|at| at.resolve_target.is_some()) + { + for (i, at) in color_attachments.iter().enumerate() { + if at.resolve_target.is_none() { + 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| at.resolve_target) + .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, + }) + } + } + })); - if let Some((pipeline_layout_id, follow_ups)) = pass - .binder - .provide_entry(index as usize, bind_group_id, bind_group, offsets) - { - let bind_groups = iter::once(bind_group.raw.raw()) - .chain(follow_ups.clone().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_ups.flat_map(|(_, offsets)| offsets)) - .map(|&off| off as hal::command::DescriptorSetOffset), + raw.begin_render_pass( + render_pass, + framebuffer, + rect, + clear_values, + hal::command::SubpassContents::Inline, + ); + raw.set_scissors(0, iter::once(&rect)); + raw.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| at.resolve_target) + .map(|resolve| view_guard[*resolve].format) + .collect(), + depth_stencil: depth_stencil_attachment.map(|at| view_guard[at.attachment].format), + }; + (context, sample_count) }; - } - // Render-specific routines + let mut state = State { + binder: Binder::new(cmb.features.max_bind_groups), + blend_color: OptionalState::Unused, + stencil_reference: OptionalState::Unused, + index: IndexState { + bound_buffer_view: None, + format: IndexFormat::Uint16, + limit: 0, + }, + vertex: VertexState { + inputs: [VertexBufferState::EMPTY; MAX_VERTEX_BUFFERS], + vertex_limit: 0, + instance_limit: 0, + }, + }; - 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), + let mut peeker = raw_data.as_ptr(); + let raw_data_end = unsafe { + raw_data.as_ptr().add(raw_data.len()) + }; + let mut command = RenderCommand::Draw { + vertex_count: 0, + instance_count: 0, + first_vertex: 0, + first_instance: 0, }; + loop { + assert!(unsafe { peeker.add(RenderCommand::max_size()) } <= raw_data_end); + peeker = unsafe { command.peek_from(peeker) }; + match command { + RenderCommand::SetBindGroup { index, num_dynamic_offsets, bind_group_id, phantom_offsets } => { + let (new_peeker, offsets) = unsafe { + phantom_offsets.decode(peeker, num_dynamic_offsets as usize, raw_data_end) + }; + peeker = new_peeker; + + 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 + ); + } + } - unsafe { - pass.raw.bind_index_buffer(view); - } - } + let bind_group = trackers + .bind_groups + .use_extend(&*bind_group_guard, bind_group_id, (), ()) + .unwrap(); + assert_eq!(bind_group.dynamic_count, offsets.len()); - 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; - } + trackers.merge_extend(&bind_group.used); - pass.vertex_state.update_limits(); + if let Some((pipeline_layout_id, follow_ups)) = state.binder + .provide_entry(index as usize, bind_group_id, bind_group, offsets) + { + let bind_groups = iter::once(bind_group.raw.raw()) + .chain(follow_ups.clone().map(|(bg_id, _)| bind_group_guard[bg_id].raw.raw())); + unsafe { + raw.bind_graphics_descriptor_sets( + &&pipeline_layout_guard[pipeline_layout_id].raw, + index as usize, + bind_groups, + offsets + .iter() + .chain(follow_ups.flat_map(|(_, offsets)| offsets)) + .map(|&off| off as hal::command::DescriptorSetOffset), + ); + } + }; + } + RenderCommand::SetPipeline(pipeline_id) => { + let pipeline = &pipeline_guard[pipeline_id]; + + assert!( + context.compatible(&pipeline.pass_context), + "The render pipeline is not compatible with the pass!" + ); + assert_eq!( + pipeline.sample_count, sample_count, + "The render pipeline and renderpass have mismatching sample_count" + ); + + state.blend_color + .require(pipeline.flags.contains(PipelineFlags::BLEND_COLOR)); + state.stencil_reference + .require(pipeline.flags.contains(PipelineFlags::STENCIL_REFERENCE)); + + unsafe { + raw.bind_graphics_pipeline(&pipeline.raw); + } - let buffers = buffers - .iter() - .map(|&id| &buffer_guard[id].raw) - .zip(offsets.iter().cloned()); + // Rebind resource + if state.binder.pipeline_layout_id != Some(pipeline.layout_id) { + let pipeline_layout = &pipeline_layout_guard[pipeline.layout_id]; + state.binder.pipeline_layout_id = Some(pipeline.layout_id); + state.binder + .reset_expectations(pipeline_layout.bind_group_layout_ids.len()); + let mut is_compatible = true; + + for (index, (entry, &bgl_id)) in state + .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 { + 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; + } + } + } + } - unsafe { - pass.raw.bind_vertex_buffers(start_slot, buffers); + // Rebind index buffer if the index format has changed with the pipeline switch + if state.index.format != pipeline.index_format { + state.index.format = pipeline.index_format; + state.index.update_limit(); + + if let Some((buffer_id, ref range)) = state.index.bound_buffer_view { + let buffer = 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(state.index.format), + }; + + unsafe { + raw.bind_index_buffer(view); + } + } + } + // Update vertex buffer limits + for (vbs, &(stride, rate)) in state + .vertex + .inputs + .iter_mut() + .zip(&pipeline.vertex_strides) + { + vbs.stride = stride; + vbs.rate = rate; + } + for vbs in state.vertex.inputs[pipeline.vertex_strides.len() ..].iter_mut() { + vbs.stride = 0; + vbs.rate = InputStepMode::Vertex; + } + state.vertex.update_limits(); + } + RenderCommand::SetIndexBuffer { buffer_id, offset } => { + let buffer = trackers + .buffers + .use_extend(&*buffer_guard, buffer_id, (), BufferUsage::INDEX) + .unwrap(); + assert!(buffer.usage.contains(BufferUsage::INDEX)); + + let range = offset .. buffer.size; + state.index.bound_buffer_view = Some((buffer_id, range)); + state.index.update_limit(); + + let view = hal::buffer::IndexBufferView { + buffer: &buffer.raw, + offset, + index_type: conv::map_index_format(state.index.format), + }; + + unsafe { + raw.bind_index_buffer(view); + } + } + RenderCommand::SetVertexBuffers { start_index, count, phantom_buffer_ids, phantom_offsets } => { + let (new_peeker, buffer_ids) = unsafe { + phantom_buffer_ids.decode(peeker, count as usize, raw_data_end) + }; + let (new_peeker, offsets) = unsafe { + phantom_offsets.decode(new_peeker, count as usize, raw_data_end) + }; + peeker = new_peeker; + + let pairs = state.vertex.inputs[start_index as usize ..] + .iter_mut() + .zip(buffer_ids.iter().zip(offsets)) + .map(|(vbs, (&id, &offset))| { + let buffer = trackers + .buffers + .use_extend(&*buffer_guard, id, (), BufferUsage::VERTEX) + .unwrap(); + assert!(buffer.usage.contains(BufferUsage::VERTEX)); + + vbs.total_size = buffer.size - offset; + (&buffer.raw, offset) + }); + + unsafe { + raw.bind_vertex_buffers(start_index as u32, pairs); + } + state.vertex.update_limits(); + } + RenderCommand::SetBlendColor(ref color) => { + state.blend_color = OptionalState::Set; + unsafe { + raw.set_blend_constants(conv::map_color_f32(color)); + } + } + RenderCommand::SetStencilReference(value) => { + state.stencil_reference = OptionalState::Set; + unsafe { + raw.set_stencil_reference(hal::pso::Face::all(), value); + } + } + RenderCommand::SetViewport { ref rect, depth_min, depth_max } => { + use std::{convert::TryFrom, i16}; + let r = hal::pso::Rect { + x: i16::try_from(rect.x.round() as i64).unwrap_or(0), + y: i16::try_from(rect.y.round() as i64).unwrap_or(0), + w: i16::try_from(rect.w.round() as i64).unwrap_or(i16::MAX), + h: i16::try_from(rect.h.round() as i64).unwrap_or(i16::MAX), + }; + unsafe { + raw.set_viewports( + 0, + iter::once(hal::pso::Viewport { + rect: r, + depth: depth_min .. depth_max, + }), + ); + } + } + RenderCommand::SetScissor(ref rect) => { + use std::{convert::TryFrom, i16}; + let r = hal::pso::Rect { + x: i16::try_from(rect.x).unwrap_or(0), + y: i16::try_from(rect.y).unwrap_or(0), + w: i16::try_from(rect.w).unwrap_or(i16::MAX), + h: i16::try_from(rect.h).unwrap_or(i16::MAX), + }; + unsafe { + raw.set_scissors( + 0, + iter::once(r), + ); + } + } + RenderCommand::Draw { vertex_count, instance_count, first_vertex, first_instance } => { + state.is_ready().unwrap(); + assert!( + first_vertex + vertex_count <= state.vertex.vertex_limit, + "Vertex out of range!" + ); + assert!( + first_instance + instance_count <= state.vertex.instance_limit, + "Instance out of range!" + ); + + unsafe { + raw.draw( + first_vertex .. first_vertex + vertex_count, + first_instance .. first_instance + instance_count, + ); + } + } + RenderCommand::DrawIndexed { index_count, instance_count, first_index, base_vertex, first_instance } => { + state.is_ready().unwrap(); + + //TODO: validate that base_vertex + max_index() is within the provided range + assert!( + first_index + index_count <= state.index.limit, + "Index out of range!" + ); + assert!( + first_instance + instance_count <= state.vertex.instance_limit, + "Instance out of range!" + ); + + unsafe { + raw.draw_indexed( + first_index .. first_index + index_count, + base_vertex, + first_instance .. first_instance + instance_count, + ); + } + } + RenderCommand::DrawIndirect { buffer_id, offset } => { + state.is_ready().unwrap(); + + let buffer = trackers + .buffers + .use_extend( + &*buffer_guard, + buffer_id, + (), + BufferUsage::INDIRECT, + ) + .unwrap(); + assert!(buffer.usage.contains(BufferUsage::INDIRECT)); + + unsafe { + raw.draw_indirect(&buffer.raw, offset, 1, 0); + } + } + RenderCommand::DrawIndexedIndirect { buffer_id, offset } => { + state.is_ready().unwrap(); + + let buffer = trackers + .buffers + .use_extend( + &*buffer_guard, + buffer_id, + (), + BufferUsage::INDIRECT, + ) + .unwrap(); + assert!(buffer.usage.contains(BufferUsage::INDIRECT)); + + unsafe { + raw.draw_indexed_indirect(&buffer.raw, offset, 1, 0); + } + } + RenderCommand::End => break, + } } - } - 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!" + super::CommandBuffer::insert_barriers( + cmb.raw.last_mut().unwrap(), + &mut cmb.trackers, + &trackers, + &*buffer_guard, + &*texture_guard, ); - unsafe { - pass.raw.draw( - first_vertex .. first_vertex + vertex_count, - first_instance .. first_instance + instance_count, - ); + cmb.raw.last_mut().unwrap().finish(); + raw.end_render_pass(); } + cmb.raw.push(raw); } +} - 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, mut token) = 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 mod render_ffi { + use super::{ + RenderCommand, + super::{PhantomSlice, RawRenderPass, Rect}, + }; + use crate::{ + id, + BufferAddress, + Color, + RawString, + }; + use std::{convert::TryInto, slice}; - 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, + #[no_mangle] + pub unsafe extern "C" fn wgpu_render_pass_set_bind_group( + pass: &mut RawRenderPass, + index: u32, + bind_group_id: id::BindGroupId, + offsets: *const BufferAddress, + offset_length: usize, ) { - 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!" + pass.raw.encode(&RenderCommand::SetBindGroup { + index: index.try_into().unwrap(), + num_dynamic_offsets: offset_length.try_into().unwrap(), + bind_group_id, + phantom_offsets: PhantomSlice::new(), + }); + pass.raw.encode_slice( + slice::from_raw_parts(offsets, offset_length), ); - - 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, + #[no_mangle] + pub unsafe extern "C" fn wgpu_render_pass_set_pipeline( + pass: &mut RawRenderPass, + pipeline_id: id::RenderPipelineId, ) { - 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]; - 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); - } + pass.raw.encode(&RenderCommand::SetPipeline(pipeline_id)); } - pub fn render_pass_set_pipeline( - &self, - pass_id: RenderPassId, - pipeline_id: RenderPipelineId, + #[no_mangle] + pub unsafe extern "C" fn wgpu_render_pass_set_index_buffer( + pass: &mut RawRenderPass, + buffer_id: id::BufferId, + offset: 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.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]; + pass.raw.encode(&RenderCommand::SetIndexBuffer { + buffer_id, + offset, + }); + } - assert!( - pass.context.compatible(&pipeline.pass_context), - "The render pipeline is not compatible with the pass!" + #[no_mangle] + pub unsafe extern "C" fn wgpu_render_pass_set_vertex_buffers( + pass: &mut RawRenderPass, + start_slot: u32, + buffer_ids: *const id::BufferId, + offsets: *const BufferAddress, + length: usize, + ) { + pass.raw.encode(&RenderCommand::SetVertexBuffers { + start_index: start_slot.try_into().unwrap(), + count: length.try_into().unwrap(), + phantom_buffer_ids: PhantomSlice::new(), + phantom_offsets: PhantomSlice::new(), + }); + pass.raw.encode_slice( + slice::from_raw_parts(buffer_ids, length), ); - assert_eq!( - pipeline.sample_count, pass.sample_count, - "The render pipeline and renderpass have mismatching sample_count" + pass.raw.encode_slice( + slice::from_raw_parts(offsets, length), ); - - 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) { - let pipeline_layout = &pipeline_layout_guard[pipeline.layout_id]; - pass.binder.pipeline_layout_id = Some(pipeline.layout_id); - 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)); - } + #[no_mangle] + pub unsafe extern "C" fn wgpu_render_pass_set_blend_color( + pass: &mut RawRenderPass, + color: &Color, + ) { + pass.raw.encode(&RenderCommand::SetBlendColor(*color)); } - pub fn render_pass_set_stencil_reference( - &self, - pass_id: RenderPassId, + #[no_mangle] + pub unsafe extern "C" fn wgpu_render_pass_set_stencil_reference( + pass: &mut RawRenderPass, 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); - } + pass.raw.encode(&RenderCommand::SetStencilReference(value)); } - pub fn render_pass_set_viewport( - &self, - pass_id: RenderPassId, + #[no_mangle] + pub unsafe extern "C" fn wgpu_render_pass_set_viewport( + pass: &mut RawRenderPass, x: f32, y: f32, w: f32, h: f32, - min_depth: f32, - max_depth: f32, + depth_min: f32, + depth_max: 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, - }], - ); - } + pass.raw.encode(&RenderCommand::SetViewport { + rect: Rect { x, y, w, h }, + depth_min, + depth_max, + }); } - pub fn render_pass_set_scissor_rect( - &self, - pass_id: RenderPassId, + #[no_mangle] + pub unsafe extern "C" fn wgpu_render_pass_set_scissor_rect( + pass: &mut RawRenderPass, 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]; + pass.raw.encode(&RenderCommand::SetScissor(Rect { x, y, w, h })); + } - 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), - }], - ); - } + #[no_mangle] + pub unsafe extern "C" fn wgpu_render_pass_draw( + pass: &mut RawRenderPass, + vertex_count: u32, + instance_count: u32, + first_vertex: u32, + first_instance: u32, + ) { + pass.raw.encode(&RenderCommand::Draw { + vertex_count, + instance_count, + first_vertex, + first_instance, + }); + } + + #[no_mangle] + pub unsafe extern "C" fn wgpu_render_pass_draw_indexed( + pass: &mut RawRenderPass, + index_count: u32, + instance_count: u32, + first_index: u32, + base_vertex: i32, + first_instance: u32, + ) { + pass.raw.encode(&RenderCommand::DrawIndexed { + index_count, + instance_count, + first_index, + base_vertex, + first_instance, + }); + } + + #[no_mangle] + pub unsafe extern "C" fn wgpu_render_pass_draw_indirect( + pass: &mut RawRenderPass, + buffer_id: id::BufferId, + offset: BufferAddress, + ) { + pass.raw.encode(&RenderCommand::DrawIndirect { + buffer_id, + offset, + }); + } + + #[no_mangle] + pub unsafe extern "C" fn wgpu_render_pass_draw_indexed_indirect( + pass: &mut RawRenderPass, + buffer_id: id::BufferId, + offset: BufferAddress, + ) { + pass.raw.encode(&RenderCommand::DrawIndexedIndirect { + buffer_id, + offset, + }); + } + + #[no_mangle] + pub extern "C" fn wgpu_render_pass_execute_bundles( + _pass: &mut RawRenderPass, + _bundles: *const id::RenderBundleId, + _bundles_length: usize, + ) { + unimplemented!() + } + + #[no_mangle] + pub extern "C" fn wgpu_render_pass_push_debug_group( + _pass: &mut RawRenderPass, + _label: RawString, + ) { + //TODO + } + + #[no_mangle] + pub extern "C" fn wgpu_render_pass_pop_debug_group( + _pass: &mut RawRenderPass, + ) { + //TODO + } + + #[no_mangle] + pub extern "C" fn wgpu_render_pass_insert_debug_marker( + _pass: &mut RawRenderPass, + _label: RawString, + ) { + //TODO } } diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index c2f8140752..4b5f35846b 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -1216,9 +1216,8 @@ impl> Global { .com_allocator .allocate(dev_stored, &device.raw, device.features, lowest_active_index); unsafe { - comb.raw.last_mut().unwrap().begin( + comb.raw.last_mut().unwrap().begin_primary( hal::command::CommandBufferFlags::ONE_TIME_SUBMIT, - hal::command::CommandBufferInheritanceInfo::default(), ); } @@ -1381,9 +1380,8 @@ impl> Global { // execute resource transitions let mut transit = device.com_allocator.extend(comb); unsafe { - transit.begin( + transit.begin_primary( hal::command::CommandBufferFlags::ONE_TIME_SUBMIT, - hal::command::CommandBufferInheritanceInfo::default(), ); } log::trace!("Stitching command buffer {:?} before submission", cmb_id); diff --git a/wgpu-core/src/hub.rs b/wgpu-core/src/hub.rs index d27a43b9df..208ed5ad97 100644 --- a/wgpu-core/src/hub.rs +++ b/wgpu-core/src/hub.rs @@ -5,7 +5,7 @@ use crate::{ backend, binding_model::{BindGroup, BindGroupLayout, PipelineLayout}, - command::{CommandBuffer, ComputePass, RenderPass}, + command::CommandBuffer, device::{Device, ShaderModule}, id::{ AdapterId, @@ -13,11 +13,9 @@ use crate::{ BindGroupLayoutId, BufferId, CommandBufferId, - ComputePassId, ComputePipelineId, DeviceId, PipelineLayoutId, - RenderPassId, RenderPipelineId, SamplerId, ShaderModuleId, @@ -170,6 +168,7 @@ impl Access> for Adapter {} impl Access> for Device {} impl Access> for Root {} impl Access> for Device {} +impl Access> for CommandBuffer {} impl Access> for Root {} impl Access> for Device {} impl Access> for Root {} @@ -180,16 +179,10 @@ impl Access> for CommandBuffer {} impl Access> for Root {} impl Access> for Device {} impl Access> for SwapChain {} -impl Access> for Root {} -impl Access> for BindGroup {} -impl Access> for CommandBuffer {} -impl Access> for Root {} -impl Access> for BindGroup {} -impl Access> for CommandBuffer {} impl Access> for Root {} -impl Access> for ComputePass {} +impl Access> for BindGroup {} impl Access> for Root {} -impl Access> for RenderPass {} +impl Access> for BindGroup {} impl Access> for Root {} impl Access> for PipelineLayout {} impl Access> for Root {} @@ -197,9 +190,7 @@ impl Access> for Device {} impl Access> for BindGroupLayout {} impl Access> for BindGroup {} impl Access> for CommandBuffer {} -impl Access> for ComputePass {} impl Access> for ComputePipeline {} -impl Access> for RenderPass {} impl Access> for RenderPipeline {} impl Access> for Root {} impl Access> for Device {} @@ -376,9 +367,7 @@ pub struct Hub { 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>, @@ -397,9 +386,7 @@ impl Default for Hub { bind_group_layouts: Registry::new(B::VARIANT), bind_groups: Registry::new(B::VARIANT), command_buffers: Registry::new(B::VARIANT), - render_passes: Registry::new(B::VARIANT), render_pipelines: Registry::new(B::VARIANT), - compute_passes: Registry::new(B::VARIANT), compute_pipelines: Registry::new(B::VARIANT), buffers: Registry::new(B::VARIANT), textures: Registry::new(B::VARIANT), @@ -456,9 +443,7 @@ impl Drop for Hub { //TODO: // self.compute_pipelines - // self.compute_passes // self.render_pipelines - // self.render_passes // self.bind_group_layouts // self.pipeline_layouts // self.shader_modules diff --git a/wgpu-core/src/id.rs b/wgpu-core/src/id.rs index 4df6fea703..b2492b49b7 100644 --- a/wgpu-core/src/id.rs +++ b/wgpu-core/src/id.rs @@ -5,7 +5,7 @@ use crate::{Backend, Epoch, Index}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use std::{fmt, marker::PhantomData}; +use std::{fmt, marker::PhantomData, mem}; const BACKEND_BITS: usize = 3; const EPOCH_MASK: u32 = (1 << (32 - BACKEND_BITS)) - 1; @@ -57,6 +57,21 @@ impl PartialEq for Id { } } +unsafe impl peek_poke::Poke for Id { + fn max_size() -> usize { + mem::size_of::() + } + unsafe fn poke_into(&self, data: *mut u8) -> *mut u8 { + self.0.poke_into(data) + } +} + +impl peek_poke::Peek for Id { + unsafe fn peek_from(&mut self, data: *const u8) -> *const u8 { + self.0.peek_from(data) + } +} + pub trait TypedId { fn zip(index: Index, epoch: Epoch, backend: Backend) -> Self; fn unzip(self) -> (Index, Epoch, Backend); @@ -100,9 +115,9 @@ pub type ComputePipelineId = Id>; // Command pub type CommandBufferId = Id>; pub type CommandEncoderId = CommandBufferId; +pub type RenderPassId = *mut crate::command::RawRenderPass; +pub type ComputePassId = *mut crate::command::RawPass; pub type RenderBundleId = Id>; -pub type RenderPassId = Id>; -pub type ComputePassId = Id>; // Swap chain pub type SwapChainId = Id>; diff --git a/wgpu-core/src/lib.rs b/wgpu-core/src/lib.rs index 1789d38fec..d6e2fc9208 100644 --- a/wgpu-core/src/lib.rs +++ b/wgpu-core/src/lib.rs @@ -31,6 +31,7 @@ pub mod swap_chain; pub mod track; pub use hal::pso::read_spirv; +use peek_poke::{PeekCopy, Poke}; use std::{ os::raw::c_char, @@ -121,7 +122,7 @@ struct Stored { } #[repr(C)] -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PeekCopy, Poke)] pub struct Color { pub r: f64, pub g: f64, diff --git a/wgpu-core/src/track/mod.rs b/wgpu-core/src/track/mod.rs index 01f973dd89..fdbf962496 100644 --- a/wgpu-core/src/track/mod.rs +++ b/wgpu-core/src/track/mod.rs @@ -280,7 +280,7 @@ impl ResourceTracker { /// /// Returns `Some(Usage)` only if this usage is consistent /// across the given selector. - pub fn query(&mut self, id: S::Id, selector: S::Selector) -> Option { + pub fn query(&self, id: S::Id, selector: S::Selector) -> Option { let (index, epoch, backend) = id.unzip(); debug_assert_eq!(backend, self.backend); let res = self.map.get(&index)?; diff --git a/wgpu-native/Cargo.toml b/wgpu-native/Cargo.toml index d1f6e01294..fe6423ed96 100644 --- a/wgpu-native/Cargo.toml +++ b/wgpu-native/Cargo.toml @@ -26,6 +26,7 @@ package = "wgpu-core" version = "0.1" [dependencies] +arrayvec = "0.5" lazy_static = "1.1" parking_lot = "0.9" raw-window-handle = "0.3" diff --git a/wgpu-native/cbindgen.toml b/wgpu-native/cbindgen.toml index 6dee67146a..87fcb28d32 100644 --- a/wgpu-native/cbindgen.toml +++ b/wgpu-native/cbindgen.toml @@ -25,6 +25,8 @@ exclude = ["BufferMapResult"] parse_deps = true include = ["wgpu-core"] +extra_bindings = ["wgpu-core"] + [fn] [struct] @@ -38,3 +40,7 @@ derive_helper_methods = true bitflags = true [defines] +#"target_os = ios" = "WGPU_TARGET_IOS" +#"target_os = macos" = "WGPU_TARGET_MACOS" +#"unix" = "WGPU_TARGET_FAMILY_UNIX" +#"windows" = "WGPU_TARGET_WINDOWS" diff --git a/wgpu-native/src/command.rs b/wgpu-native/src/command.rs index 79f4a2bd3a..ddbb67a6d6 100644 --- a/wgpu-native/src/command.rs +++ b/wgpu-native/src/command.rs @@ -4,9 +4,14 @@ use crate::GLOBAL; -use core::{gfx_select, id}; +pub use core::command::{ + wgpu_command_encoder_begin_compute_pass, + wgpu_command_encoder_begin_render_pass, + compute_ffi::*, + render_ffi::*, +}; -use std::{marker::PhantomData, slice}; +use core::{gfx_select, id}; #[no_mangle] @@ -79,239 +84,38 @@ pub extern "C" fn wgpu_command_encoder_copy_texture_to_texture( #[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 unsafe 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 { - slice::from_raw_parts(offsets, offsets_length) +pub unsafe extern "C" fn wgpu_render_pass_end_pass(pass_id: id::RenderPassId) { + let (pass_data, encoder_id, targets) = Box::from_raw(pass_id).finish_render(); + let color_attachments: arrayvec::ArrayVec<[_; core::device::MAX_COLOR_TARGETS]> = targets.colors + .iter() + .flat_map(|at| { + if at.attachment == id::TextureViewId::ERROR { + None + } else { + Some(core::command::RenderPassColorAttachmentDescriptor { + attachment: at.attachment, + resolve_target: if at.resolve_target == id::TextureViewId::ERROR { + None + } else { + Some(&at.resolve_target) + }, + load_op: at.load_op, + store_op: at.store_op, + clear_color: at.clear_color, + }) + } + }) + .collect(); + let depth_stencil_attachment = if targets.depth_stencil.attachment == id::TextureViewId::ERROR { + None } else { - &[] + Some(&targets.depth_stencil) }; - 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 + gfx_select!(encoder_id => GLOBAL.command_encoder_run_render_pass(encoder_id, &color_attachments, depth_stencil_attachment, &pass_data)) } #[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 unsafe 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 = slice::from_raw_parts(buffers, length); - let offsets = 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 unsafe 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 { - 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)) +pub unsafe extern "C" fn wgpu_compute_pass_end_pass(pass_id: id::ComputePassId) { + let (pass_data, encoder_id) = Box::from_raw(pass_id).finish_compute(); + gfx_select!(encoder_id => GLOBAL.command_encoder_run_compute_pass(encoder_id, &pass_data)) } diff --git a/wgpu-remote/src/lib.rs b/wgpu-remote/src/lib.rs index 38a62f4975..310c09f99d 100644 --- a/wgpu-remote/src/lib.rs +++ b/wgpu-remote/src/lib.rs @@ -20,6 +20,7 @@ struct IdentityHub { adapters: IdentityManager, devices: IdentityManager, buffers: IdentityManager, + command_buffers: IdentityManager, } #[derive(Debug, Default)] @@ -153,3 +154,30 @@ pub extern "C" fn wgpu_client_kill_buffer_id(client: &Client, id: id::BufferId) .buffers .free(id) } + +#[no_mangle] +pub extern "C" fn wgpu_client_make_encoder_id( + client: &Client, + device_id: id::DeviceId, +) -> id::CommandEncoderId { + let backend = device_id.backend(); + client + .identities + .lock() + .select(backend) + .command_buffers + .alloc(backend) +} + +#[no_mangle] +pub extern "C" fn wgpu_client_kill_encoder_id( + client: &Client, + id: id::CommandEncoderId, +) { + client + .identities + .lock() + .select(id.backend()) + .command_buffers + .free(id) +} diff --git a/wgpu-remote/src/server.rs b/wgpu-remote/src/server.rs index e19c47d642..abe7a6947a 100644 --- a/wgpu-remote/src/server.rs +++ b/wgpu-remote/src/server.rs @@ -101,3 +101,60 @@ pub extern "C" fn wgpu_server_device_get_buffer_sub_data( pub extern "C" fn wgpu_server_buffer_destroy(global: &Global, self_id: id::BufferId) { gfx_select!(self_id => global.buffer_destroy(self_id)); } + +#[no_mangle] +pub extern "C" fn wgpu_server_device_create_encoder( + global: &Global, + self_id: id::DeviceId, + encoder_id: id::CommandEncoderId, +) { + let desc = core::command::CommandEncoderDescriptor { + todo: 0, + }; + gfx_select!(self_id => global.device_create_command_encoder(self_id, &desc, encoder_id)); +} + +#[no_mangle] +pub extern "C" fn wgpu_server_encoder_destroy( + global: &Global, + self_id: id::CommandEncoderId, +) { + gfx_select!(self_id => global.command_encoder_destroy(self_id)); +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_server_encode_compute_pass( + global: &Global, + self_id: id::CommandEncoderId, + bytes: *const u8, + byte_length: usize, +) { + let raw_data = slice::from_raw_parts(bytes, byte_length); + gfx_select!(self_id => global.command_encoder_run_compute_pass(self_id, raw_data)); +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_server_encode_render_pass( + global: &Global, + self_id: id::CommandEncoderId, + color_attachments: *const core::command::RenderPassColorAttachmentDescriptor, + color_attachment_length: usize, + depth_stencil_attachment: Option<&core::command::RenderPassDepthStencilAttachmentDescriptor>, + commands: *const u8, + command_length: usize, +) { + let color_attachments = slice::from_raw_parts(color_attachments, color_attachment_length); + let raw_pass = slice::from_raw_parts(commands, command_length); + gfx_select!(self_id => global.command_encoder_run_render_pass(self_id, color_attachments, depth_stencil_attachment, raw_pass)); +} + +#[no_mangle] +pub unsafe extern "C" fn wgpu_server_queue_submit( + global: &Global, + self_id: id::QueueId, + command_buffer_ids: *const id::CommandBufferId, + command_buffer_id_length: usize, +) { + let command_buffers = slice::from_raw_parts(command_buffer_ids, command_buffer_id_length); + gfx_select!(self_id => global.queue_submit(self_id, command_buffers)); +}