Remove peek-poke.

There was a lot of highly unsafe use of serialization based on peek-poke that we
weren't entirely happy with. It's replaced by just serializing the passes now.
Also, switch BufferSize to Option<NonZero>.
This commit is contained in:
Dzmitry Malyshau 2020-06-19 16:21:49 -04:00
parent c8a1734044
commit 365f4e8786
18 changed files with 543 additions and 1097 deletions

View File

@ -76,13 +76,13 @@ jobs:
name: Ubuntu Nightly
channel: nightly
build_command: cargo test
additional_core_features:
additional_core_features: serial-pass
additional_player_features: winit
- os: windows-2019
name: Windows Stable
channel: stable
build_command: rustup default stable-msvc; cargo clippy
additional_core_features: trace
additional_core_features: trace,serial-pass
additional_player_features: renderdoc
- os: windows-2019
name: Windows Nightly

39
Cargo.lock generated
View File

@ -43,6 +43,9 @@ name = "arrayvec"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
dependencies = [
"serde",
]
[[package]]
name = "ash"
@ -908,28 +911,6 @@ dependencies = [
"winapi 0.3.8",
]
[[package]]
name = "peek-poke"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d93fd6a575ebf1ac2668d08443c97a22872cfb463fd8b7ddd141e9f6be59af2f"
dependencies = [
"peek-poke-derive",
]
[[package]]
name = "peek-poke-derive"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fb44a25c5bba983be0fc8592dfaf3e6d0935ce8be0c6b15b2a39507af34a926"
dependencies = [
"proc-macro2 1.0.18",
"quote 1.0.7",
"syn",
"synstructure",
"unicode-xid 0.2.0",
]
[[package]]
name = "percent-encoding"
version = "2.1.0"
@ -1242,18 +1223,6 @@ dependencies = [
"unicode-xid 0.2.0",
]
[[package]]
name = "synstructure"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
dependencies = [
"proc-macro2 1.0.18",
"quote 1.0.7",
"syn",
"unicode-xid 0.2.0",
]
[[package]]
name = "termcolor"
version = "1.1.0"
@ -1467,7 +1436,6 @@ dependencies = [
"loom",
"naga",
"parking_lot",
"peek-poke",
"raw-window-handle",
"ron",
"serde",
@ -1482,7 +1450,6 @@ name = "wgpu-types"
version = "0.5.0"
dependencies = [
"bitflags",
"peek-poke",
"serde",
]

View File

@ -13,4 +13,4 @@ publish = false
path = "../wgpu-core"
package = "wgpu-core"
version = "0.5"
features = ["battery", "trace"]
features = ["battery", "serial-pass", "trace"]

View File

@ -139,33 +139,21 @@ impl GlobalExt for wgc::hub::Global<IdentityPassThroughFactory> {
trace::Command::CopyTextureToTexture { src, dst, size } => {
self.command_encoder_copy_texture_to_texture::<B>(encoder, &src, &dst, &size)
}
trace::Command::RunComputePass {
commands,
dynamic_offsets,
} => unsafe {
let mut pass = wgc::command::RawPass::new_compute(encoder);
pass.fill_compute_commands(&commands, &dynamic_offsets);
let (data, _) = pass.finish_compute();
self.command_encoder_run_compute_pass::<B>(encoder, &data);
},
trace::Command::RunComputePass { base } => {
self.command_encoder_run_compute_pass_impl::<B>(encoder, base.as_ref());
}
trace::Command::RunRenderPass {
base,
target_colors,
target_depth_stencil,
commands,
dynamic_offsets,
} => unsafe {
let mut pass = wgc::command::RawPass::new_render(
} => {
self.command_encoder_run_render_pass_impl::<B>(
encoder,
&wgc::command::RenderPassDescriptor {
color_attachments: target_colors.as_ptr(),
color_attachments_length: target_colors.len(),
depth_stencil_attachment: target_depth_stencil.as_ref(),
},
base.as_ref(),
&target_colors,
target_depth_stencil.as_ref(),
);
pass.fill_render_commands(&commands, &dynamic_offsets);
let (data, _) = pass.finish_render();
self.command_encoder_run_render_pass::<B>(encoder, &data);
},
}
}
}
self.command_encoder_finish::<B>(encoder, &wgt::CommandBufferDescriptor { todo: 0 })
@ -385,14 +373,9 @@ impl GlobalExt for wgc::hub::Global<IdentityPassThroughFactory> {
A::DestroyRenderPipeline(id) => {
self.render_pipeline_destroy::<B>(id);
}
A::CreateRenderBundle {
id,
desc,
commands,
dynamic_offsets,
} => {
A::CreateRenderBundle { id, desc, base } => {
let label = Label::new(&desc.label);
let mut bundle_encoder = wgc::command::RenderBundleEncoder::new(
let bundle = wgc::command::RenderBundleEncoder::new(
&wgt::RenderBundleEncoderDescriptor {
label: None,
color_formats: &desc.color_formats,
@ -400,10 +383,10 @@ impl GlobalExt for wgc::hub::Global<IdentityPassThroughFactory> {
sample_count: desc.sample_count,
},
device,
Some(base),
);
bundle_encoder.fill_commands(&commands, &dynamic_offsets);
self.render_bundle_encoder_finish::<B>(
bundle_encoder,
bundle,
&wgt::RenderBundleDescriptor {
label: label.as_ptr(),
},

View File

@ -13,8 +13,12 @@ license = "MPL-2.0"
[features]
default = []
# Enable API tracing
trace = ["ron", "serde", "wgt/trace"]
# Enable API replaying
replay = ["serde", "wgt/replay"]
# Enable serializable compute/render passes, and bundle encoders.
serial-pass = ["serde", "wgt/serde", "arrayvec/serde"]
[dependencies]
arrayvec = "0.5"
@ -25,7 +29,6 @@ log = "0.4"
hal = { package = "gfx-hal", version = "0.5.2" }
gfx-backend-empty = "0.5"
parking_lot = "0.10"
peek-poke = "0.2"
raw-window-handle = { version = "0.3", optional = true }
ron = { version = "0.5", optional = true }
serde = { version = "1.0", features = ["serde_derive"], optional = true }
@ -49,7 +52,6 @@ rev = "438353c3f75368c12024ad2fc03cbeb15f351fd9"
path = "../wgpu-types"
package = "wgpu-types"
version = "0.5"
features = ["peek-poke"]
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
gfx-backend-metal = { version = "0.5.4" }

View File

@ -67,7 +67,7 @@ pub struct PipelineLayout<B: hal::Backend> {
pub struct BufferBinding {
pub buffer_id: BufferId,
pub offset: wgt::BufferAddress,
pub size: wgt::BufferSize,
pub size: Option<wgt::BufferSize>,
}
// Note: Duplicated in wgpu-rs as BindingResource

View File

@ -37,7 +37,7 @@
!*/
use crate::{
command::{PhantomSlice, RawPass, RenderCommand},
command::{BasePass, RenderCommand},
conv,
device::{AttachmentData, Label, RenderPassContext, MAX_VERTEX_BUFFERS},
hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Input, Storage, Token},
@ -47,19 +47,24 @@ use crate::{
LifeGuard, RefCount, Stored, MAX_BIND_GROUPS,
};
use arrayvec::ArrayVec;
use peek_poke::{Peek, Poke};
use std::{borrow::Borrow, iter, marker::PhantomData, ops::Range};
#[derive(Debug)]
#[cfg_attr(feature = "serial-pass", derive(serde::Deserialize, serde::Serialize))]
pub struct RenderBundleEncoder {
raw: RawPass<id::DeviceId>,
base: BasePass<RenderCommand>,
parent_id: id::DeviceId,
pub(crate) context: RenderPassContext,
}
impl RenderBundleEncoder {
pub fn new(desc: &wgt::RenderBundleEncoderDescriptor, device_id: id::DeviceId) -> Self {
pub fn new(
desc: &wgt::RenderBundleEncoderDescriptor,
parent_id: id::DeviceId,
base: Option<BasePass<RenderCommand>>,
) -> Self {
RenderBundleEncoder {
raw: RawPass::new::<RenderCommand>(device_id),
base: base.unwrap_or_else(BasePass::new),
parent_id,
context: RenderPassContext {
attachments: AttachmentData {
colors: desc.color_formats.iter().cloned().collect(),
@ -76,15 +81,7 @@ impl RenderBundleEncoder {
}
pub fn parent(&self) -> id::DeviceId {
self.raw.parent
}
pub fn fill_commands(&mut self, commands: &[RenderCommand], offsets: &[wgt::DynamicOffset]) {
unsafe { self.raw.fill_render_commands(commands, offsets) }
}
pub fn destroy(mut self) {
unsafe { self.raw.invalidate() };
self.parent_id
}
}
@ -95,8 +92,7 @@ impl RenderBundleEncoder {
pub struct RenderBundle {
// Normalized command stream. It can be executed verbatim,
// without re-binding anything on the pipeline change.
commands: Vec<RenderCommand>,
dynamic_offsets: Vec<wgt::DynamicOffset>,
base: BasePass<RenderCommand>,
pub(crate) device_id: Stored<id::DeviceId>,
pub(crate) used: TrackerSet,
pub(crate) context: RenderPassContext,
@ -125,17 +121,16 @@ impl RenderBundle {
) {
use hal::command::CommandBuffer as _;
let mut offsets = self.dynamic_offsets.as_slice();
let mut offsets = self.base.dynamic_offsets.as_slice();
let mut index_type = hal::IndexType::U16;
let mut pipeline_layout_id = None::<id::PipelineLayoutId>;
for command in self.commands.iter() {
for command in self.base.commands.iter() {
match *command {
RenderCommand::SetBindGroup {
index,
num_dynamic_offsets,
bind_group_id,
phantom_offsets: _,
} => {
let bind_group = &bind_group_guard[bind_group_id];
comb.bind_graphics_descriptor_sets(
@ -162,11 +157,7 @@ impl RenderBundle {
buffer: &buffer.raw,
range: hal::buffer::SubRange {
offset,
size: if size != wgt::BufferSize::WHOLE {
Some(size.0)
} else {
None
},
size: size.map(|s| s.get()),
},
index_type,
};
@ -182,11 +173,7 @@ impl RenderBundle {
let buffer = &buffer_guard[buffer_id];
let range = hal::buffer::SubRange {
offset,
size: if size != wgt::BufferSize::WHOLE {
Some(size.0)
} else {
None
},
size: size.map(|s| s.get()),
};
comb.bind_vertex_buffers(slot, iter::once((&buffer.raw, range)));
}
@ -222,23 +209,14 @@ impl RenderBundle {
let buffer = &buffer_guard[buffer_id];
comb.draw_indexed_indirect(&buffer.raw, offset, 1, 0);
}
RenderCommand::PushDebugGroup {
color: _,
len: _,
phantom_marker: _,
} => unimplemented!(),
RenderCommand::InsertDebugMarker {
color: _,
len: _,
phantom_marker: _,
} => unimplemented!(),
RenderCommand::PushDebugGroup { color: _, len: _ } => unimplemented!(),
RenderCommand::InsertDebugMarker { color: _, len: _ } => unimplemented!(),
RenderCommand::PopDebugGroup => unimplemented!(),
RenderCommand::ExecuteBundle(_)
| RenderCommand::SetBlendColor(_)
| RenderCommand::SetStencilReference(_)
| RenderCommand::SetViewport { .. }
| RenderCommand::SetScissor(_)
| RenderCommand::End => unreachable!(),
| RenderCommand::SetScissor(_) => unreachable!(),
}
}
}
@ -283,7 +261,7 @@ impl IndexState {
Some(RenderCommand::SetIndexBuffer {
buffer_id: self.buffer.unwrap(),
offset: self.range.start,
size: wgt::BufferSize(self.range.end - self.range.start),
size: wgt::BufferSize::new(self.range.end - self.range.start),
})
} else {
None
@ -337,7 +315,7 @@ impl VertexState {
slot,
buffer_id: self.buffer.unwrap(),
offset: self.range.start,
size: wgt::BufferSize(self.range.end - self.range.start),
size: wgt::BufferSize::new(self.range.end - self.range.start),
})
} else {
None
@ -488,7 +466,6 @@ impl State {
bind_group_id: bs.bind_group.unwrap().0,
num_dynamic_offsets: (bs.dynamic_offsets.end - bs.dynamic_offsets.start)
as u8,
phantom_offsets: PhantomSlice::default(),
})
} else {
None
@ -508,8 +485,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let mut token = Token::root();
let (device_guard, mut token) = hub.devices.read(&mut token);
let (data, device_id) = unsafe { bundle_encoder.raw.finish_render() };
let device = &device_guard[device_id];
let device = &device_guard[bundle_encoder.parent_id];
let render_bundle = {
let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token);
let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token);
@ -517,7 +493,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (buffer_guard, _) = hub.buffers.read(&mut token);
let mut state = State {
trackers: TrackerSet::new(device_id.backend()),
trackers: TrackerSet::new(bundle_encoder.parent_id.backend()),
index: IndexState::new(),
vertex: (0..MAX_VERTEX_BUFFERS)
.map(|_| VertexState::new())
@ -528,38 +504,18 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
used_bind_groups: 0,
};
let mut commands = Vec::new();
let mut base = bundle_encoder.base.as_ref();
let mut peeker = data.as_ptr();
let raw_data_end = unsafe { data.as_ptr().offset(data.len() as isize) };
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 },
"RenderCommand (size {}) is too big to fit within raw_data",
RenderCommand::max_size(),
);
peeker = unsafe { RenderCommand::peek_from(peeker, &mut command) };
for &command in base.commands {
match command {
RenderCommand::SetBindGroup {
index,
num_dynamic_offsets,
bind_group_id,
phantom_offsets,
} => {
let (new_peeker, offsets) = unsafe {
phantom_offsets.decode_unaligned(
peeker,
num_dynamic_offsets as usize,
raw_data_end,
)
};
peeker = new_peeker;
let offsets = &base.dynamic_offsets[..num_dynamic_offsets as usize];
base.dynamic_offsets =
&base.dynamic_offsets[num_dynamic_offsets as usize..];
for off in offsets {
assert_eq!(
*off as wgt::BufferAddress % wgt::BIND_BUFFER_ALIGNMENT,
@ -614,10 +570,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.unwrap();
assert!(buffer.usage.contains(wgt::BufferUsage::INDEX), "An invalid setIndexBuffer call has been made. The buffer usage is {:?} which does not contain required usage INDEX", buffer.usage);
let end = if size != wgt::BufferSize::WHOLE {
offset + size.0
} else {
buffer.size
let end = match size {
Some(s) => offset + s.get(),
None => buffer.size,
};
state.index.set_buffer(buffer_id, offset..end);
}
@ -638,10 +593,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
buffer.usage
);
let end = if size != wgt::BufferSize::WHOLE {
offset + size.0
} else {
buffer.size
let end = match size {
Some(s) => offset + s.get(),
None => buffer.size,
};
state.vertex[slot as usize].set_buffer(buffer_id, offset..end);
}
@ -734,17 +688,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
commands.extend(state.flush_binds());
commands.push(command);
}
RenderCommand::End => break,
RenderCommand::PushDebugGroup {
color: _,
len: _,
phantom_marker: _,
} => unimplemented!(),
RenderCommand::InsertDebugMarker {
color: _,
len: _,
phantom_marker: _,
} => unimplemented!(),
RenderCommand::PushDebugGroup { color: _, len: _ } => unimplemented!(),
RenderCommand::InsertDebugMarker { color: _, len: _ } => unimplemented!(),
RenderCommand::PopDebugGroup => unimplemented!(),
RenderCommand::ExecuteBundle(_)
| RenderCommand::SetBlendColor(_)
@ -760,10 +705,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let _ = desc.label; //TODO: actually use
//TODO: check if the device is still alive
RenderBundle {
commands,
dynamic_offsets: state.flat_dynamic_offsets,
base: BasePass {
commands,
dynamic_offsets: state.flat_dynamic_offsets,
string_data: Vec::new(),
},
device_id: Stored {
value: device_id,
value: bundle_encoder.parent_id,
ref_count: device.life_guard.add_ref(),
},
used: state.trackers,
@ -786,8 +734,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
trace.lock().add(trace::Action::CreateRenderBundle {
id,
desc: trace::RenderBundleDescriptor::new(desc.label, &bundle.context),
commands: bundle.commands.clone(),
dynamic_offsets: bundle.dynamic_offsets.clone(),
base: BasePass::from_ref(bundle.base.as_ref()),
});
}
None => {}
@ -804,7 +751,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
pub mod bundle_ffi {
use super::{super::PhantomSlice, RenderBundleEncoder, RenderCommand};
use super::{RenderBundleEncoder, RenderCommand};
use crate::{id, RawString};
use std::{convert::TryInto, slice};
use wgt::{BufferAddress, BufferSize, DynamicOffset};
@ -817,41 +764,42 @@ pub mod bundle_ffi {
// `RawPass::encode` and `RawPass::encode_slice`.
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_bundle_set_bind_group(
bundle_encoder: &mut RenderBundleEncoder,
bundle: &mut RenderBundleEncoder,
index: u32,
bind_group_id: id::BindGroupId,
offsets: *const DynamicOffset,
offset_length: usize,
) {
bundle_encoder.raw.encode(&RenderCommand::SetBindGroup {
bundle.base.commands.push(RenderCommand::SetBindGroup {
index: index.try_into().unwrap(),
num_dynamic_offsets: offset_length.try_into().unwrap(),
bind_group_id,
phantom_offsets: PhantomSlice::default(),
});
bundle_encoder
.raw
.encode_slice(slice::from_raw_parts(offsets, offset_length));
bundle
.base
.dynamic_offsets
.extend_from_slice(slice::from_raw_parts(offsets, offset_length));
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_bundle_set_pipeline(
bundle_encoder: &mut RenderBundleEncoder,
pub extern "C" fn wgpu_render_bundle_set_pipeline(
bundle: &mut RenderBundleEncoder,
pipeline_id: id::RenderPipelineId,
) {
bundle_encoder
.raw
.encode(&RenderCommand::SetPipeline(pipeline_id));
bundle
.base
.commands
.push(RenderCommand::SetPipeline(pipeline_id));
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_bundle_set_index_buffer(
bundle_encoder: &mut RenderBundleEncoder,
pub extern "C" fn wgpu_render_bundle_set_index_buffer(
bundle: &mut RenderBundleEncoder,
buffer_id: id::BufferId,
offset: BufferAddress,
size: BufferSize,
size: Option<BufferSize>,
) {
bundle_encoder.raw.encode(&RenderCommand::SetIndexBuffer {
bundle.base.commands.push(RenderCommand::SetIndexBuffer {
buffer_id,
offset,
size,
@ -859,14 +807,14 @@ pub mod bundle_ffi {
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_bundle_set_vertex_buffer(
bundle_encoder: &mut RenderBundleEncoder,
pub extern "C" fn wgpu_render_bundle_set_vertex_buffer(
bundle: &mut RenderBundleEncoder,
slot: u32,
buffer_id: id::BufferId,
offset: BufferAddress,
size: BufferSize,
size: Option<BufferSize>,
) {
bundle_encoder.raw.encode(&RenderCommand::SetVertexBuffer {
bundle.base.commands.push(RenderCommand::SetVertexBuffer {
slot,
buffer_id,
offset,
@ -875,14 +823,14 @@ pub mod bundle_ffi {
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_bundle_draw(
bundle_encoder: &mut RenderBundleEncoder,
pub extern "C" fn wgpu_render_bundle_draw(
bundle: &mut RenderBundleEncoder,
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
) {
bundle_encoder.raw.encode(&RenderCommand::Draw {
bundle.base.commands.push(RenderCommand::Draw {
vertex_count,
instance_count,
first_vertex,
@ -891,15 +839,15 @@ pub mod bundle_ffi {
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_bundle_draw_indexed(
bundle_encoder: &mut RenderBundleEncoder,
pub extern "C" fn wgpu_render_bundle_draw_indexed(
bundle: &mut RenderBundleEncoder,
index_count: u32,
instance_count: u32,
first_index: u32,
base_vertex: i32,
first_instance: u32,
) {
bundle_encoder.raw.encode(&RenderCommand::DrawIndexed {
bundle.base.commands.push(RenderCommand::DrawIndexed {
index_count,
instance_count,
first_index,
@ -909,45 +857,45 @@ pub mod bundle_ffi {
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_bundle_draw_indirect(
bundle_encoder: &mut RenderBundleEncoder,
pub extern "C" fn wgpu_render_bundle_draw_indirect(
bundle: &mut RenderBundleEncoder,
buffer_id: id::BufferId,
offset: BufferAddress,
) {
bundle_encoder
.raw
.encode(&RenderCommand::DrawIndirect { buffer_id, offset });
bundle
.base
.commands
.push(RenderCommand::DrawIndirect { buffer_id, offset });
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_pass_bundle_indexed_indirect(
bundle_encoder: &mut RenderBundleEncoder,
pub extern "C" fn wgpu_render_pass_bundle_indexed_indirect(
bundle: &mut RenderBundleEncoder,
buffer_id: id::BufferId,
offset: BufferAddress,
) {
bundle_encoder
.raw
.encode(&RenderCommand::DrawIndexedIndirect { buffer_id, offset });
bundle
.base
.commands
.push(RenderCommand::DrawIndexedIndirect { buffer_id, offset });
}
#[no_mangle]
pub extern "C" fn wgpu_render_bundle_push_debug_group(
_bundle_encoder: &mut RenderBundleEncoder,
pub unsafe extern "C" fn wgpu_render_bundle_push_debug_group(
_bundle: &mut RenderBundleEncoder,
_label: RawString,
) {
//TODO
}
#[no_mangle]
pub extern "C" fn wgpu_render_bundle_pop_debug_group(
_bundle_encoder: &mut RenderBundleEncoder,
) {
pub unsafe extern "C" fn wgpu_render_bundle_pop_debug_group(_bundle: &mut RenderBundleEncoder) {
//TODO
}
#[no_mangle]
pub extern "C" fn wgpu_render_bundle_insert_debug_marker(
_bundle_encoder: &mut RenderBundleEncoder,
pub unsafe extern "C" fn wgpu_render_bundle_insert_debug_marker(
_bundle: &mut RenderBundleEncoder,
_label: RawString,
) {
//TODO

View File

@ -5,7 +5,7 @@
use crate::{
command::{
bind::{Binder, LayoutChange},
CommandBuffer, PhantomSlice,
BasePass, BasePassRef, CommandBuffer,
},
device::all_buffer_stages,
hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Token},
@ -14,27 +14,25 @@ use crate::{
};
use hal::command::CommandBuffer as _;
use peek_poke::{Peek, PeekPoke, Poke};
use wgt::{BufferAddress, BufferUsage, DynamicOffset, BIND_BUFFER_ALIGNMENT};
use wgt::{BufferAddress, BufferUsage, BIND_BUFFER_ALIGNMENT};
use std::{iter, str};
#[derive(Debug, PartialEq)]
enum PipelineState {
Required,
Set,
}
#[derive(Clone, Copy, Debug, PeekPoke)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
#[cfg_attr(
any(feature = "serial-pass", feature = "trace"),
derive(serde::Serialize)
)]
#[cfg_attr(
any(feature = "serial-pass", feature = "replay"),
derive(serde::Deserialize)
)]
pub enum ComputeCommand {
SetBindGroup {
index: u8,
num_dynamic_offsets: u8,
bind_group_id: id::BindGroupId,
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(skip))]
phantom_offsets: PhantomSlice<DynamicOffset>,
},
SetPipeline(id::ComputePipelineId),
Dispatch([u32; 3]),
@ -45,57 +43,30 @@ pub enum ComputeCommand {
PushDebugGroup {
color: u32,
len: usize,
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(skip))]
phantom_marker: PhantomSlice<u8>,
},
PopDebugGroup,
InsertDebugMarker {
color: u32,
len: usize,
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(skip))]
phantom_marker: PhantomSlice<u8>,
},
End,
}
#[derive(Debug)]
struct State {
binder: Binder,
debug_scope_depth: u32,
#[cfg_attr(feature = "serial-pass", derive(serde::Deserialize, serde::Serialize))]
pub struct ComputePass {
base: BasePass<ComputeCommand>,
parent_id: id::CommandEncoderId,
}
impl Default for ComputeCommand {
fn default() -> Self {
ComputeCommand::End
}
}
impl super::RawPass<id::CommandEncoderId> {
pub unsafe fn new_compute(parent: id::CommandEncoderId) -> Self {
Self::new::<ComputeCommand>(parent)
}
pub unsafe fn fill_compute_commands(
&mut self,
commands: &[ComputeCommand],
mut offsets: &[DynamicOffset],
) {
for com in commands {
self.encode(com);
if let ComputeCommand::SetBindGroup {
num_dynamic_offsets,
..
} = *com
{
self.encode_slice(&offsets[..num_dynamic_offsets as usize]);
offsets = &offsets[num_dynamic_offsets as usize..];
}
impl ComputePass {
pub fn new(parent_id: id::CommandEncoderId) -> Self {
ComputePass {
base: BasePass::new(),
parent_id,
}
}
pub unsafe fn finish_compute(mut self) -> (Vec<u8>, id::CommandEncoderId) {
self.finish(ComputeCommand::End);
self.into_vec()
pub fn parent_id(&self) -> id::CommandEncoderId {
self.parent_id
}
}
@ -105,13 +76,35 @@ pub struct ComputePassDescriptor {
pub todo: u32,
}
#[derive(Debug, PartialEq)]
enum PipelineState {
Required,
Set,
}
#[derive(Debug)]
struct State {
binder: Binder,
pipeline: PipelineState,
debug_scope_depth: u32,
}
// Common routines between render/compute
impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn command_encoder_run_compute_pass<B: GfxBackend>(
&self,
encoder_id: id::CommandEncoderId,
raw_data: &[u8],
pass: &ComputePass,
) {
self.command_encoder_run_compute_pass_impl::<B>(encoder_id, pass.base.as_ref())
}
#[doc(hidden)]
pub fn command_encoder_run_compute_pass_impl<B: GfxBackend>(
&self,
encoder_id: id::CommandEncoderId,
mut base: BasePassRef<ComputeCommand>,
) {
let hub = B::hub(self);
let mut token = Token::root();
@ -120,6 +113,16 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let cmb = &mut cmb_guard[encoder_id];
let raw = cmb.raw.last_mut().unwrap();
#[cfg(feature = "trace")]
match cmb.commands {
Some(ref mut list) => {
list.push(crate::device::trace::Command::RunComputePass {
base: BasePass::from_ref(base),
});
}
None => {}
}
let (_, mut token) = hub.render_bundles.read(&mut token);
let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token);
let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token);
@ -127,45 +130,30 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (buffer_guard, mut token) = hub.buffers.read(&mut token);
let (texture_guard, _) = hub.textures.read(&mut token);
let mut pipeline_state = PipelineState::Required;
let mut state = State {
binder: Binder::new(cmb.limits.max_bind_groups),
pipeline: PipelineState::Required,
debug_scope_depth: 0,
};
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 { ComputeCommand::peek_from(peeker, &mut command) };
match command {
for command in base.commands {
match *command {
ComputeCommand::SetBindGroup {
index,
num_dynamic_offsets,
bind_group_id,
phantom_offsets,
} => {
let (new_peeker, offsets) = unsafe {
phantom_offsets.decode_unaligned(
peeker,
num_dynamic_offsets as usize,
raw_data_end,
)
};
peeker = new_peeker;
let offsets = &base.dynamic_offsets[..num_dynamic_offsets as usize];
base.dynamic_offsets = &base.dynamic_offsets[num_dynamic_offsets as usize..];
if cfg!(debug_assertions) {
for off in offsets {
assert_eq!(
*off as BufferAddress % BIND_BUFFER_ALIGNMENT,
0,
"Misaligned dynamic buffer offset: {} does not align with {}",
off,
BIND_BUFFER_ALIGNMENT
);
}
for off in offsets {
assert_eq!(
*off as BufferAddress % BIND_BUFFER_ALIGNMENT,
0,
"Misaligned dynamic buffer offset: {} does not align with {}",
off,
BIND_BUFFER_ALIGNMENT
);
}
let bind_group = cmb
@ -213,7 +201,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
}
ComputeCommand::SetPipeline(pipeline_id) => {
pipeline_state = PipelineState::Set;
state.pipeline = PipelineState::Set;
let pipeline = cmb
.trackers
.compute_pipes
@ -262,7 +250,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
ComputeCommand::Dispatch(groups) => {
assert_eq!(
pipeline_state,
state.pipeline,
PipelineState::Set,
"Dispatch error: Pipeline is missing"
);
@ -272,7 +260,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
ComputeCommand::DispatchIndirect { buffer_id, offset } => {
assert_eq!(
pipeline_state,
state.pipeline,
PipelineState::Set,
"Dispatch error: Pipeline is missing"
);
@ -295,90 +283,41 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
raw.dispatch_indirect(&src_buffer.raw, offset);
}
}
ComputeCommand::PushDebugGroup {
color,
len,
phantom_marker,
} => unsafe {
ComputeCommand::PushDebugGroup { color, len } => {
state.debug_scope_depth += 1;
let (new_peeker, label) =
{ phantom_marker.decode_unaligned(peeker, len, raw_data_end) };
peeker = new_peeker;
raw.begin_debug_marker(str::from_utf8(label).unwrap(), color)
},
ComputeCommand::PopDebugGroup => unsafe {
let label = str::from_utf8(&base.string_data[..len]).unwrap();
unsafe {
raw.begin_debug_marker(label, color);
}
base.string_data = &base.string_data[len..];
}
ComputeCommand::PopDebugGroup => {
assert_ne!(
state.debug_scope_depth, 0,
"Can't pop debug group, because number of pushed debug groups is zero!"
);
state.debug_scope_depth -= 1;
raw.end_debug_marker()
},
ComputeCommand::InsertDebugMarker {
color,
len,
phantom_marker,
} => unsafe {
let (new_peeker, label) =
{ phantom_marker.decode_unaligned(peeker, len, raw_data_end) };
peeker = new_peeker;
raw.insert_debug_marker(str::from_utf8(label).unwrap(), color)
},
ComputeCommand::End => break,
}
}
#[cfg(feature = "trace")]
match cmb.commands {
Some(ref mut list) => {
let mut pass_commands = Vec::new();
let mut pass_dynamic_offsets = Vec::new();
peeker = raw_data.as_ptr();
loop {
peeker = unsafe { ComputeCommand::peek_from(peeker, &mut command) };
match command {
ComputeCommand::SetBindGroup {
num_dynamic_offsets,
phantom_offsets,
..
} => {
let (new_peeker, offsets) = unsafe {
phantom_offsets.decode_unaligned(
peeker,
num_dynamic_offsets as usize,
raw_data_end,
)
};
peeker = new_peeker;
pass_dynamic_offsets.extend_from_slice(offsets);
}
ComputeCommand::End => break,
_ => {}
unsafe {
raw.end_debug_marker();
}
pass_commands.push(command);
}
list.push(crate::device::trace::Command::RunComputePass {
commands: pass_commands,
dynamic_offsets: pass_dynamic_offsets,
});
ComputeCommand::InsertDebugMarker { color, len } => {
let label = str::from_utf8(&base.string_data[..len]).unwrap();
unsafe { raw.insert_debug_marker(label, color) }
base.string_data = &base.string_data[len..];
}
}
None => {}
}
}
}
pub mod compute_ffi {
use super::{super::PhantomSlice, ComputeCommand};
use super::{ComputeCommand, ComputePass};
use crate::{id, RawString};
use std::{convert::TryInto, ffi, slice};
use wgt::{BufferAddress, DynamicOffset};
type RawPass = super::super::RawPass<id::CommandEncoderId>;
/// # Safety
///
/// This function is unsafe as there is no guarantee that the given pointer is
@ -387,92 +326,87 @@ pub mod compute_ffi {
// `RawPass::encode` and `RawPass::encode_slice`.
#[no_mangle]
pub unsafe extern "C" fn wgpu_compute_pass_set_bind_group(
pass: &mut RawPass,
pass: &mut ComputePass,
index: u32,
bind_group_id: id::BindGroupId,
offsets: *const DynamicOffset,
offset_length: usize,
) {
pass.encode(&ComputeCommand::SetBindGroup {
pass.base.commands.push(ComputeCommand::SetBindGroup {
index: index.try_into().unwrap(),
num_dynamic_offsets: offset_length.try_into().unwrap(),
bind_group_id,
phantom_offsets: PhantomSlice::default(),
});
pass.encode_slice(slice::from_raw_parts(offsets, offset_length));
pass.base
.dynamic_offsets
.extend_from_slice(slice::from_raw_parts(offsets, offset_length));
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_compute_pass_set_pipeline(
pass: &mut RawPass,
pub extern "C" fn wgpu_compute_pass_set_pipeline(
pass: &mut ComputePass,
pipeline_id: id::ComputePipelineId,
) {
pass.encode(&ComputeCommand::SetPipeline(pipeline_id));
pass.base
.commands
.push(ComputeCommand::SetPipeline(pipeline_id));
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_compute_pass_dispatch(
pass: &mut RawPass,
pub extern "C" fn wgpu_compute_pass_dispatch(
pass: &mut ComputePass,
groups_x: u32,
groups_y: u32,
groups_z: u32,
) {
pass.encode(&ComputeCommand::Dispatch([groups_x, groups_y, groups_z]));
pass.base
.commands
.push(ComputeCommand::Dispatch([groups_x, groups_y, groups_z]));
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_compute_pass_dispatch_indirect(
pass: &mut RawPass,
pub extern "C" fn wgpu_compute_pass_dispatch_indirect(
pass: &mut ComputePass,
buffer_id: id::BufferId,
offset: BufferAddress,
) {
pass.encode(&ComputeCommand::DispatchIndirect { buffer_id, offset });
pass.base
.commands
.push(ComputeCommand::DispatchIndirect { buffer_id, offset });
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_compute_pass_push_debug_group(
pass: &mut RawPass,
pass: &mut ComputePass,
label: RawString,
color: u32,
) {
let bytes = ffi::CStr::from_ptr(label).to_bytes();
pass.base.string_data.extend_from_slice(bytes);
pass.encode(&ComputeCommand::PushDebugGroup {
pass.base.commands.push(ComputeCommand::PushDebugGroup {
color,
len: bytes.len(),
phantom_marker: PhantomSlice::default(),
});
pass.encode_slice(bytes);
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_compute_pass_pop_debug_group(pass: &mut RawPass) {
pass.encode(&ComputeCommand::PopDebugGroup);
pub extern "C" fn wgpu_compute_pass_pop_debug_group(pass: &mut ComputePass) {
pass.base.commands.push(ComputeCommand::PopDebugGroup);
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_compute_pass_insert_debug_marker(
pass: &mut RawPass,
pass: &mut ComputePass,
label: RawString,
color: u32,
) {
let bytes = ffi::CStr::from_ptr(label).to_bytes();
pass.base.string_data.extend_from_slice(bytes);
pass.encode(&ComputeCommand::InsertDebugMarker {
pass.base.commands.push(ComputeCommand::InsertDebugMarker {
color,
len: bytes.len(),
phantom_marker: PhantomSlice::default(),
});
pass.encode_slice(bytes);
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_compute_pass_finish(
pass: &mut RawPass,
length: &mut usize,
) -> *const u8 {
pass.finish(ComputeCommand::End);
*length = pass.size();
pass.base
}
}

View File

@ -16,7 +16,7 @@ pub use self::render::*;
pub use self::transfer::*;
use crate::{
device::{all_buffer_stages, all_image_stages, MAX_COLOR_TARGETS},
device::{all_buffer_stages, all_image_stages},
hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Storage, Token},
id,
resource::{Buffer, Texture},
@ -26,133 +26,7 @@ use crate::{
use hal::command::CommandBuffer as _;
use peek_poke::PeekPoke;
use std::{marker::PhantomData, mem, ptr, slice, thread::ThreadId};
#[derive(Clone, Copy, Debug, PeekPoke)]
pub struct PhantomSlice<T>(PhantomData<T>);
impl<T> Default for PhantomSlice<T> {
fn default() -> Self {
PhantomSlice(PhantomData)
}
}
impl<T> PhantomSlice<T> {
unsafe fn decode_unaligned<'a>(
self,
pointer: *const u8,
count: usize,
bound: *const u8,
) -> (*const u8, &'a [T]) {
let align_offset = pointer.align_offset(mem::align_of::<T>());
let aligned = pointer.add(align_offset);
let size = count * mem::size_of::<T>();
let end = aligned.add(size);
assert!(
end <= bound,
"End of phantom slice ({:?}) exceeds bound ({:?})",
end,
bound
);
(end, slice::from_raw_parts(aligned as *const T, count))
}
}
#[repr(C)]
#[derive(Debug)]
pub struct RawPass<P> {
data: *mut u8,
base: *mut u8,
capacity: usize,
parent: P,
}
impl<P: Copy> RawPass<P> {
fn new<T>(parent: P) -> Self {
let mut vec = Vec::<T>::with_capacity(1);
let ptr = vec.as_mut_ptr() as *mut u8;
let capacity = mem::size_of::<T>();
assert_ne!(capacity, 0);
mem::forget(vec);
RawPass {
data: ptr,
base: ptr,
capacity,
parent,
}
}
/// 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<C: peek_poke::Poke>(&mut self, command: C) {
self.ensure_extra_size(C::max_size());
let extended_end = self.data.add(C::max_size());
let end = command.poke_into(self.data);
ptr::write_bytes(end, 0, extended_end as usize - end as usize);
self.data = extended_end;
}
fn size(&self) -> usize {
self.data as usize - self.base as usize
}
/// Recover the data vector of the pass, consuming `self`.
unsafe fn into_vec(mut self) -> (Vec<u8>, P) {
self.invalidate()
}
/// Make pass contents invalid, return the contained data.
///
/// Any following access to the pass will result in a crash
/// for accessing address 0.
pub unsafe fn invalidate(&mut self) -> (Vec<u8>, P) {
let size = self.size();
assert!(
size <= self.capacity,
"Size of RawPass ({}) exceeds capacity ({})",
size,
self.capacity
);
let vec = Vec::from_raw_parts(self.base, size, self.capacity);
self.data = ptr::null_mut();
self.base = ptr::null_mut();
self.capacity = 0;
(vec, self.parent)
}
unsafe fn ensure_extra_size(&mut self, extra_size: usize) {
let size = self.size();
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]
pub(crate) unsafe fn encode<C: peek_poke::Poke>(&mut self, command: &C) {
self.ensure_extra_size(C::max_size());
self.data = command.poke_into(self.data);
}
#[inline]
pub(crate) unsafe fn encode_slice<T: Copy>(&mut self, data: &[T]) {
let align_offset = self.data.align_offset(mem::align_of::<T>());
let extra = align_offset + mem::size_of::<T>() * 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);
}
}
use std::thread::ThreadId;
#[derive(Debug)]
pub struct CommandBuffer<B: hal::Backend> {
@ -209,48 +83,54 @@ impl<B: GfxBackend> CommandBuffer<B> {
}
}
#[repr(C)]
#[derive(PeekPoke)]
struct PassComponent<T> {
load_op: wgt::LoadOp,
store_op: wgt::StoreOp,
clear_value: T,
read_only: bool,
#[derive(Copy, Clone, Debug)]
pub struct BasePassRef<'a, C> {
pub commands: &'a [C],
pub dynamic_offsets: &'a [wgt::DynamicOffset],
pub string_data: &'a [u8],
}
// required for PeekPoke
impl<T: Default> Default for PassComponent<T> {
fn default() -> Self {
PassComponent {
load_op: wgt::LoadOp::Clear,
store_op: wgt::StoreOp::Clear,
clear_value: T::default(),
read_only: false,
#[doc(hidden)]
#[derive(Debug)]
#[cfg_attr(
any(feature = "serial-pass", feature = "trace"),
derive(serde::Serialize)
)]
#[cfg_attr(
any(feature = "serial-pass", feature = "replay"),
derive(serde::Deserialize)
)]
pub struct BasePass<C> {
pub commands: Vec<C>,
pub dynamic_offsets: Vec<wgt::DynamicOffset>,
pub string_data: Vec<u8>,
}
impl<C: Clone> BasePass<C> {
fn new() -> Self {
BasePass {
commands: Vec::new(),
dynamic_offsets: Vec::new(),
string_data: Vec::new(),
}
}
}
#[repr(C)]
#[derive(Default, PeekPoke)]
struct RawRenderPassColorAttachmentDescriptor {
attachment: u64,
resolve_target: u64,
component: PassComponent<wgt::Color>,
}
#[cfg(feature = "trace")]
fn from_ref(base: BasePassRef<C>) -> Self {
BasePass {
commands: base.commands.to_vec(),
dynamic_offsets: base.dynamic_offsets.to_vec(),
string_data: base.string_data.to_vec(),
}
}
#[repr(C)]
#[derive(Default, PeekPoke)]
struct RawRenderPassDepthStencilAttachmentDescriptor {
attachment: u64,
depth: PassComponent<f32>,
stencil: PassComponent<u32>,
}
#[repr(C)]
#[derive(Default, PeekPoke)]
struct RawRenderTargets {
colors: [RawRenderPassColorAttachmentDescriptor; MAX_COLOR_TARGETS],
depth_stencil: RawRenderPassDepthStencilAttachmentDescriptor,
pub fn as_ref(&self) -> BasePassRef<C> {
BasePassRef {
commands: &self.commands,
dynamic_offsets: &self.dynamic_offsets,
string_data: &self.string_data,
}
}
}
impl<G: GlobalIdentityHandlerFactory> Global<G> {

View File

@ -5,8 +5,7 @@
use crate::{
command::{
bind::{Binder, LayoutChange},
PassComponent, PhantomSlice, RawPass, RawRenderPassColorAttachmentDescriptor,
RawRenderPassDepthStencilAttachmentDescriptor, RawRenderTargets,
BasePass, BasePassRef,
},
conv,
device::{
@ -23,54 +22,49 @@ use crate::{
use arrayvec::ArrayVec;
use hal::command::CommandBuffer as _;
use peek_poke::{Peek, PeekPoke, Poke};
use wgt::{
BufferAddress, BufferSize, BufferUsage, Color, DynamicOffset, IndexFormat, InputStepMode,
LoadOp, RenderPassColorAttachmentDescriptorBase,
RenderPassDepthStencilAttachmentDescriptorBase, StoreOp, TextureUsage, BIND_BUFFER_ALIGNMENT,
BufferAddress, BufferSize, BufferUsage, Color, IndexFormat, InputStepMode, LoadOp,
RenderPassColorAttachmentDescriptorBase, RenderPassDepthStencilAttachmentDescriptorBase,
StoreOp, TextureUsage, BIND_BUFFER_ALIGNMENT,
};
use std::{borrow::Borrow, collections::hash_map::Entry, fmt, iter, mem, ops::Range, slice, str};
use std::{borrow::Borrow, collections::hash_map::Entry, fmt, iter, ops::Range, str};
pub type RenderPassColorAttachmentDescriptor =
RenderPassColorAttachmentDescriptorBase<id::TextureViewId>;
pub type RenderPassDepthStencilAttachmentDescriptor =
pub type ColorAttachmentDescriptor = RenderPassColorAttachmentDescriptorBase<id::TextureViewId>;
pub type DepthStencilAttachmentDescriptor =
RenderPassDepthStencilAttachmentDescriptorBase<id::TextureViewId>;
fn is_depth_stencil_read_only(
desc: &RenderPassDepthStencilAttachmentDescriptor,
desc: &DepthStencilAttachmentDescriptor,
aspects: hal::format::Aspects,
) -> bool {
if aspects.contains(hal::format::Aspects::DEPTH) && !desc.depth_read_only {
if aspects.contains(hal::format::Aspects::DEPTH) && !desc.depth.read_only {
return false;
}
assert_eq!(
(desc.depth_load_op, desc.depth_store_op),
(desc.depth.load_op, desc.depth.store_op),
(LoadOp::Load, StoreOp::Store),
"Unable to clear non-present/read-only depth"
);
if aspects.contains(hal::format::Aspects::STENCIL) && !desc.stencil_read_only {
if aspects.contains(hal::format::Aspects::STENCIL) && !desc.stencil.read_only {
return false;
}
assert_eq!(
(desc.stencil_load_op, desc.stencil_store_op),
(desc.stencil.load_op, desc.stencil.store_op),
(LoadOp::Load, StoreOp::Store),
"Unable to clear non-present/read-only stencil"
);
true
}
#[repr(C)]
#[derive(Debug)]
pub struct RenderPassDescriptor<'a> {
pub color_attachments: *const RenderPassColorAttachmentDescriptor,
pub color_attachments_length: usize,
pub depth_stencil_attachment: Option<&'a RenderPassDepthStencilAttachmentDescriptor>,
pub color_attachments: &'a [ColorAttachmentDescriptor],
pub depth_stencil_attachment: Option<&'a DepthStencilAttachmentDescriptor>,
}
#[derive(Clone, Copy, Debug, Default, PeekPoke)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
#[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Rect<T> {
pub x: T,
pub y: T,
@ -78,28 +72,33 @@ pub struct Rect<T> {
pub h: T,
}
#[derive(Clone, Copy, Debug, PeekPoke)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
#[cfg_attr(
any(feature = "serial-pass", feature = "trace"),
derive(serde::Serialize)
)]
#[cfg_attr(
any(feature = "serial-pass", feature = "replay"),
derive(serde::Deserialize)
)]
pub enum RenderCommand {
SetBindGroup {
index: u8,
num_dynamic_offsets: u8,
bind_group_id: id::BindGroupId,
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(skip))]
phantom_offsets: PhantomSlice<DynamicOffset>,
},
SetPipeline(id::RenderPipelineId),
SetIndexBuffer {
buffer_id: id::BufferId,
offset: BufferAddress,
size: BufferSize,
size: Option<BufferSize>,
},
SetVertexBuffer {
slot: u32,
buffer_id: id::BufferId,
offset: BufferAddress,
size: BufferSize,
size: Option<BufferSize>,
},
SetBlendColor(Color),
SetStencilReference(u32),
@ -134,93 +133,35 @@ pub enum RenderCommand {
PushDebugGroup {
color: u32,
len: usize,
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(skip))]
phantom_marker: PhantomSlice<u8>,
},
PopDebugGroup,
InsertDebugMarker {
color: u32,
len: usize,
#[cfg_attr(any(feature = "trace", feature = "replay"), serde(skip))]
phantom_marker: PhantomSlice<u8>,
},
ExecuteBundle(id::RenderBundleId),
End,
}
// required for PeekPoke
impl Default for RenderCommand {
fn default() -> Self {
RenderCommand::End
}
#[cfg_attr(feature = "serial-pass", derive(serde::Deserialize, serde::Serialize))]
pub struct RenderPass {
base: BasePass<RenderCommand>,
parent_id: id::CommandEncoderId,
color_targets: ArrayVec<[ColorAttachmentDescriptor; MAX_COLOR_TARGETS]>,
depth_stencil_target: Option<DepthStencilAttachmentDescriptor>,
}
impl RawPass<id::CommandEncoderId> {
pub unsafe fn new_render(parent_id: id::CommandEncoderId, desc: &RenderPassDescriptor) -> Self {
let mut pass = Self::new::<RenderCommand>(parent_id);
let mut targets: RawRenderTargets = mem::zeroed();
if let Some(ds) = desc.depth_stencil_attachment {
targets.depth_stencil = RawRenderPassDepthStencilAttachmentDescriptor {
attachment: ds.attachment.into_raw(),
depth: PassComponent {
load_op: ds.depth_load_op,
store_op: ds.depth_store_op,
clear_value: ds.clear_depth,
read_only: ds.depth_read_only,
},
stencil: PassComponent {
load_op: ds.stencil_load_op,
store_op: ds.stencil_store_op,
clear_value: ds.clear_stencil,
read_only: ds.stencil_read_only,
},
};
}
for (color, at) in targets.colors.iter_mut().zip(slice::from_raw_parts(
desc.color_attachments,
desc.color_attachments_length,
)) {
*color = RawRenderPassColorAttachmentDescriptor {
attachment: at.attachment.into_raw(),
resolve_target: at.resolve_target.map_or(0, |id| id.into_raw()),
component: PassComponent {
load_op: at.load_op,
store_op: at.store_op,
clear_value: at.clear_color,
read_only: false,
},
};
}
pass.encode(&targets);
pass
}
}
impl<P: Copy> RawPass<P> {
pub unsafe fn fill_render_commands(
&mut self,
commands: &[RenderCommand],
mut offsets: &[DynamicOffset],
) {
for com in commands {
self.encode(com);
if let RenderCommand::SetBindGroup {
num_dynamic_offsets,
..
} = *com
{
self.encode_slice(&offsets[..num_dynamic_offsets as usize]);
offsets = &offsets[num_dynamic_offsets as usize..];
}
impl RenderPass {
pub fn new(parent_id: id::CommandEncoderId, desc: RenderPassDescriptor) -> Self {
RenderPass {
base: BasePass::new(),
parent_id,
color_targets: desc.color_attachments.iter().cloned().collect(),
depth_stencil_target: desc.depth_stencil_attachment.cloned(),
}
}
pub unsafe fn finish_render(mut self) -> (Vec<u8>, P) {
self.finish(RenderCommand::End);
self.into_vec()
pub fn parent_id(&self) -> id::CommandEncoderId {
self.parent_id
}
}
@ -382,7 +323,23 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn command_encoder_run_render_pass<B: GfxBackend>(
&self,
encoder_id: id::CommandEncoderId,
raw_data: &[u8],
pass: &RenderPass,
) {
self.command_encoder_run_render_pass_impl::<B>(
encoder_id,
pass.base.as_ref(),
&pass.color_targets,
pass.depth_stencil_target.as_ref(),
)
}
#[doc(hidden)]
pub fn command_encoder_run_render_pass_impl<B: GfxBackend>(
&self,
encoder_id: id::CommandEncoderId,
mut base: BasePassRef<RenderCommand>,
color_attachments: &[ColorAttachmentDescriptor],
depth_stencil_attachment: Option<&DepthStencilAttachmentDescriptor>,
) {
let hub = B::hub(self);
let mut token = Token::root();
@ -395,6 +352,18 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let device = &device_guard[cmb.device_id.value];
let mut raw = device.com_allocator.extend(cmb);
#[cfg(feature = "trace")]
match cmb.commands {
Some(ref mut list) => {
list.push(crate::device::trace::Command::RunRenderPass {
base: BasePass::from_ref(base),
target_colors: color_attachments.iter().cloned().collect(),
target_depth_stencil: depth_stencil_attachment.cloned(),
});
}
None => {}
}
unsafe {
raw.begin_primary(hal::command::CommandBufferFlags::ONE_TIME_SUBMIT);
}
@ -407,50 +376,6 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (texture_guard, mut token) = hub.textures.read(&mut token);
let (view_guard, _) = hub.texture_views.read(&mut token);
let mut peeker = raw_data.as_ptr();
let raw_data_end = unsafe { raw_data.as_ptr().add(raw_data.len()) };
let mut targets: RawRenderTargets = unsafe { mem::zeroed() };
assert!(
unsafe { peeker.add(RawRenderTargets::max_size()) <= raw_data_end },
"RawRenderTargets (size {}) is too big to fit within raw_data (size {})",
RawRenderTargets::max_size(),
raw_data.len()
);
peeker = unsafe { RawRenderTargets::peek_from(peeker, &mut targets) };
#[cfg(feature = "trace")]
let command_peeker_base = peeker;
let color_attachments = targets
.colors
.iter()
.take_while(|at| at.attachment != 0)
.map(|at| RenderPassColorAttachmentDescriptor {
attachment: id::TextureViewId::from_raw(at.attachment).unwrap(),
resolve_target: id::TextureViewId::from_raw(at.resolve_target),
load_op: at.component.load_op,
store_op: at.component.store_op,
clear_color: at.component.clear_value,
})
.collect::<ArrayVec<[_; MAX_COLOR_TARGETS]>>();
let depth_stencil_attachment_body;
let depth_stencil_attachment = if targets.depth_stencil.attachment == 0 {
None
} else {
let at = &targets.depth_stencil;
depth_stencil_attachment_body = RenderPassDepthStencilAttachmentDescriptor {
attachment: id::TextureViewId::from_raw(at.attachment).unwrap(),
depth_load_op: at.depth.load_op,
depth_store_op: at.depth.store_op,
depth_read_only: at.depth.read_only,
clear_depth: at.depth.clear_value,
stencil_load_op: at.stencil.load_op,
stencil_store_op: at.stencil.store_op,
clear_stencil: at.stencil.clear_value,
stencil_read_only: at.stencil.read_only,
};
Some(&depth_stencil_attachment_body)
};
// We default to false intentionally, even if depth-stencil isn't used at all.
// This allows us to use the primary raw pipeline in `RenderPipeline`,
// instead of the special read-only one, which would be `None`.
@ -535,11 +460,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
device.private_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,
),
ops: conv::map_load_store_ops(&at.depth),
stencil_ops: conv::map_load_store_ops(&at.stencil),
layouts: old_layout..new_layout,
};
Some((ds_at, new_layout))
@ -550,7 +472,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let mut colors = ArrayVec::new();
let mut resolves = ArrayVec::new();
for at in &color_attachments {
for at in color_attachments {
let view = trackers
.views
.use_extend(&*view_guard, at.attachment, (), ())
@ -600,7 +522,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
let end = hal::image::Layout::Present;
let start = match at.load_op {
let start = match at.channel.load_op {
LoadOp::Clear => hal::image::Layout::Undefined,
LoadOp::Load => end,
};
@ -614,7 +536,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
device.private_features,
)),
samples: view.samples,
ops: conv::map_load_store_ops(at.load_op, at.store_op),
ops: conv::map_load_store_ops(&at.channel),
stencil_ops: hal::pass::AttachmentOps::DONT_CARE,
layouts,
};
@ -835,7 +757,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.iter()
.zip(&rp_key.colors)
.flat_map(|(at, (rat, _layout))| {
match at.load_op {
match at.channel.load_op {
LoadOp::Load => None,
LoadOp::Clear => {
use hal::format::ChannelType;
@ -848,13 +770,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
| ChannelType::Uscaled
| ChannelType::Sscaled
| ChannelType::Srgb => hal::command::ClearColor {
float32: conv::map_color_f32(&at.clear_color),
float32: conv::map_color_f32(&at.channel.clear_value),
},
ChannelType::Sint => hal::command::ClearColor {
sint32: conv::map_color_i32(&at.clear_color),
sint32: conv::map_color_i32(&at.channel.clear_value),
},
ChannelType::Uint => hal::command::ClearColor {
uint32: conv::map_color_u32(&at.clear_color),
uint32: conv::map_color_u32(&at.channel.clear_value),
},
};
Some(hal::command::ClearValue { color: value })
@ -862,12 +784,12 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
})
.chain(depth_stencil_attachment.and_then(|at| {
match (at.depth_load_op, at.stencil_load_op) {
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,
depth: at.depth.clear_value,
stencil: at.stencil.clear_value,
};
Some(hal::command::ClearValue {
depth_stencil: value,
@ -922,46 +844,23 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
debug_scope_depth: 0,
};
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 },
"RenderCommand (size {}) is too big to fit within raw_data (size {})",
RenderCommand::max_size(),
raw_data.len()
);
peeker = unsafe { RenderCommand::peek_from(peeker, &mut command) };
match command {
for command in base.commands {
match *command {
RenderCommand::SetBindGroup {
index,
num_dynamic_offsets,
bind_group_id,
phantom_offsets,
} => {
let (new_peeker, offsets) = unsafe {
phantom_offsets.decode_unaligned(
peeker,
num_dynamic_offsets as usize,
raw_data_end,
)
};
peeker = new_peeker;
if cfg!(debug_assertions) {
for off in offsets {
assert_eq!(
*off as BufferAddress % BIND_BUFFER_ALIGNMENT,
0,
"Misaligned dynamic buffer offset: {} does not align with {}",
off,
BIND_BUFFER_ALIGNMENT
);
}
let offsets = &base.dynamic_offsets[..num_dynamic_offsets as usize];
base.dynamic_offsets = &base.dynamic_offsets[num_dynamic_offsets as usize..];
for off in offsets {
assert_eq!(
*off as BufferAddress % BIND_BUFFER_ALIGNMENT,
0,
"Misaligned dynamic buffer offset: {} does not align with {}",
off,
BIND_BUFFER_ALIGNMENT
);
}
let bind_group = trackers
@ -1110,10 +1009,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.unwrap();
assert!(buffer.usage.contains(BufferUsage::INDEX), "An invalid setIndexBuffer call has been made. The buffer usage is {:?} which does not contain required usage INDEX", buffer.usage);
let end = if size != BufferSize::WHOLE {
offset + size.0
} else {
buffer.size
let end = match size {
Some(s) => offset + s.get(),
None => buffer.size,
};
state.index.bound_buffer_view = Some((buffer_id, offset..end));
state.index.update_limit();
@ -1147,19 +1045,14 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.vertex
.inputs
.extend(iter::repeat(VertexBufferState::EMPTY).take(empty_slots));
state.vertex.inputs[slot as usize].total_size = if size != BufferSize::WHOLE {
size.0
} else {
buffer.size - offset
state.vertex.inputs[slot as usize].total_size = match size {
Some(s) => s.get(),
None => buffer.size - offset,
};
let range = hal::buffer::SubRange {
offset,
size: if size != BufferSize::WHOLE {
Some(size.0)
} else {
None
},
size: size.map(|s| s.get()),
};
unsafe {
raw.bind_vertex_buffers(slot, iter::once((&buffer.raw, range)));
@ -1296,39 +1189,30 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
raw.draw_indexed_indirect(&buffer.raw, offset, 1, 0);
}
}
RenderCommand::PushDebugGroup {
color,
len,
phantom_marker,
} => unsafe {
RenderCommand::PushDebugGroup { color, len } => {
state.debug_scope_depth += 1;
let (new_peeker, label) =
{ phantom_marker.decode_unaligned(peeker, len, raw_data_end) };
peeker = new_peeker;
raw.begin_debug_marker(str::from_utf8(label).unwrap(), color)
},
RenderCommand::PopDebugGroup => unsafe {
let label = str::from_utf8(&base.string_data[..len]).unwrap();
unsafe {
raw.begin_debug_marker(label, color);
}
base.string_data = &base.string_data[len..];
}
RenderCommand::PopDebugGroup => {
assert_ne!(
state.debug_scope_depth, 0,
"Can't pop debug group, because number of pushed debug groups is zero!"
);
state.debug_scope_depth -= 1;
raw.end_debug_marker()
},
RenderCommand::InsertDebugMarker {
color,
len,
phantom_marker,
} => unsafe {
let (new_peeker, label) =
{ phantom_marker.decode_unaligned(peeker, len, raw_data_end) };
peeker = new_peeker;
raw.insert_debug_marker(str::from_utf8(label).unwrap(), color)
},
unsafe {
raw.end_debug_marker();
}
}
RenderCommand::InsertDebugMarker { color, len } => {
let label = str::from_utf8(&base.string_data[..len]).unwrap();
unsafe {
raw.insert_debug_marker(label, color);
}
}
RenderCommand::ExecuteBundle(bundle_id) => {
let bundle = trackers
.bundles
@ -1353,49 +1237,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
trackers.merge_extend(&bundle.used);
state.reset_bundle();
}
RenderCommand::End => break,
}
}
#[cfg(feature = "trace")]
match cmb.commands {
Some(ref mut list) => {
let mut pass_commands = Vec::new();
let mut pass_dynamic_offsets = Vec::new();
peeker = command_peeker_base;
loop {
peeker = unsafe { RenderCommand::peek_from(peeker, &mut command) };
match command {
RenderCommand::SetBindGroup {
num_dynamic_offsets,
phantom_offsets,
..
} => {
let (new_peeker, offsets) = unsafe {
phantom_offsets.decode_unaligned(
peeker,
num_dynamic_offsets as usize,
raw_data_end,
)
};
peeker = new_peeker;
pass_dynamic_offsets.extend_from_slice(offsets);
}
RenderCommand::End => break,
_ => {}
}
pass_commands.push(command);
}
list.push(crate::device::trace::Command::RunRenderPass {
target_colors: color_attachments.into_iter().collect(),
target_depth_stencil: depth_stencil_attachment.cloned(),
commands: pass_commands,
dynamic_offsets: pass_dynamic_offsets,
});
}
None => {}
}
log::trace!("Merging {:?} with the render pass", encoder_id);
unsafe {
raw.end_render_pass();
@ -1451,16 +1295,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
pub mod render_ffi {
use super::{
super::{PhantomSlice, Rect},
RenderCommand,
};
use super::{super::Rect, RenderCommand, RenderPass};
use crate::{id, RawString};
use std::{convert::TryInto, ffi, slice};
use wgt::{BufferAddress, BufferSize, Color, DynamicOffset};
type RawPass = super::super::RawPass<id::CommandEncoderId>;
/// # Safety
///
/// This function is unsafe as there is no guarantee that the given pointer is
@ -1469,37 +1308,40 @@ pub mod render_ffi {
// `RawPass::encode` and `RawPass::encode_slice`.
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_pass_set_bind_group(
pass: &mut RawPass,
pass: &mut RenderPass,
index: u32,
bind_group_id: id::BindGroupId,
offsets: *const DynamicOffset,
offset_length: usize,
) {
pass.encode(&RenderCommand::SetBindGroup {
pass.base.commands.push(RenderCommand::SetBindGroup {
index: index.try_into().unwrap(),
num_dynamic_offsets: offset_length.try_into().unwrap(),
bind_group_id,
phantom_offsets: PhantomSlice::default(),
});
pass.encode_slice(slice::from_raw_parts(offsets, offset_length));
pass.base
.dynamic_offsets
.extend_from_slice(slice::from_raw_parts(offsets, offset_length));
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_pass_set_pipeline(
pass: &mut RawPass,
pub extern "C" fn wgpu_render_pass_set_pipeline(
pass: &mut RenderPass,
pipeline_id: id::RenderPipelineId,
) {
pass.encode(&RenderCommand::SetPipeline(pipeline_id));
pass.base
.commands
.push(RenderCommand::SetPipeline(pipeline_id));
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_pass_set_index_buffer(
pass: &mut RawPass,
pub extern "C" fn wgpu_render_pass_set_index_buffer(
pass: &mut RenderPass,
buffer_id: id::BufferId,
offset: BufferAddress,
size: BufferSize,
size: Option<BufferSize>,
) {
pass.encode(&RenderCommand::SetIndexBuffer {
pass.base.commands.push(RenderCommand::SetIndexBuffer {
buffer_id,
offset,
size,
@ -1507,14 +1349,14 @@ pub mod render_ffi {
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_pass_set_vertex_buffer(
pass: &mut RawPass,
pub extern "C" fn wgpu_render_pass_set_vertex_buffer(
pass: &mut RenderPass,
slot: u32,
buffer_id: id::BufferId,
offset: BufferAddress,
size: BufferSize,
size: Option<BufferSize>,
) {
pass.encode(&RenderCommand::SetVertexBuffer {
pass.base.commands.push(RenderCommand::SetVertexBuffer {
slot,
buffer_id,
offset,
@ -1523,21 +1365,22 @@ pub mod render_ffi {
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_pass_set_blend_color(pass: &mut RawPass, color: &Color) {
pass.encode(&RenderCommand::SetBlendColor(*color));
pub extern "C" fn wgpu_render_pass_set_blend_color(pass: &mut RenderPass, color: &Color) {
pass.base
.commands
.push(RenderCommand::SetBlendColor(*color));
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_pass_set_stencil_reference(
pass: &mut RawPass,
value: u32,
) {
pass.encode(&RenderCommand::SetStencilReference(value));
pub extern "C" fn wgpu_render_pass_set_stencil_reference(pass: &mut RenderPass, value: u32) {
pass.base
.commands
.push(RenderCommand::SetStencilReference(value));
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_pass_set_viewport(
pass: &mut RawPass,
pub extern "C" fn wgpu_render_pass_set_viewport(
pass: &mut RenderPass,
x: f32,
y: f32,
w: f32,
@ -1545,7 +1388,7 @@ pub mod render_ffi {
depth_min: f32,
depth_max: f32,
) {
pass.encode(&RenderCommand::SetViewport {
pass.base.commands.push(RenderCommand::SetViewport {
rect: Rect { x, y, w, h },
depth_min,
depth_max,
@ -1553,25 +1396,27 @@ pub mod render_ffi {
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_pass_set_scissor_rect(
pass: &mut RawPass,
pub extern "C" fn wgpu_render_pass_set_scissor_rect(
pass: &mut RenderPass,
x: u32,
y: u32,
w: u32,
h: u32,
) {
pass.encode(&RenderCommand::SetScissor(Rect { x, y, w, h }));
pass.base
.commands
.push(RenderCommand::SetScissor(Rect { x, y, w, h }));
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_pass_draw(
pass: &mut RawPass,
pub extern "C" fn wgpu_render_pass_draw(
pass: &mut RenderPass,
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
) {
pass.encode(&RenderCommand::Draw {
pass.base.commands.push(RenderCommand::Draw {
vertex_count,
instance_count,
first_vertex,
@ -1580,15 +1425,15 @@ pub mod render_ffi {
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_pass_draw_indexed(
pass: &mut RawPass,
pub extern "C" fn wgpu_render_pass_draw_indexed(
pass: &mut RenderPass,
index_count: u32,
instance_count: u32,
first_index: u32,
base_vertex: i32,
first_instance: u32,
) {
pass.encode(&RenderCommand::DrawIndexed {
pass.base.commands.push(RenderCommand::DrawIndexed {
index_count,
instance_count,
first_index,
@ -1598,78 +1443,72 @@ pub mod render_ffi {
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_pass_draw_indirect(
pass: &mut RawPass,
pub extern "C" fn wgpu_render_pass_draw_indirect(
pass: &mut RenderPass,
buffer_id: id::BufferId,
offset: BufferAddress,
) {
pass.encode(&RenderCommand::DrawIndirect { buffer_id, offset });
pass.base
.commands
.push(RenderCommand::DrawIndirect { buffer_id, offset });
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_pass_draw_indexed_indirect(
pass: &mut RawPass,
pub extern "C" fn wgpu_render_pass_draw_indexed_indirect(
pass: &mut RenderPass,
buffer_id: id::BufferId,
offset: BufferAddress,
) {
pass.encode(&RenderCommand::DrawIndexedIndirect { buffer_id, offset });
pass.base
.commands
.push(RenderCommand::DrawIndexedIndirect { buffer_id, offset });
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_pass_push_debug_group(
pass: &mut RawPass,
pass: &mut RenderPass,
label: RawString,
color: u32,
) {
let bytes = ffi::CStr::from_ptr(label).to_bytes();
pass.base.string_data.extend_from_slice(bytes);
pass.encode(&RenderCommand::PushDebugGroup {
pass.base.commands.push(RenderCommand::PushDebugGroup {
color,
len: bytes.len(),
phantom_marker: PhantomSlice::default(),
});
pass.encode_slice(bytes);
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_pass_pop_debug_group(pass: &mut RawPass) {
pass.encode(&RenderCommand::PopDebugGroup);
pub extern "C" fn wgpu_render_pass_pop_debug_group(pass: &mut RenderPass) {
pass.base.commands.push(RenderCommand::PopDebugGroup);
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_pass_insert_debug_marker(
pass: &mut RawPass,
pass: &mut RenderPass,
label: RawString,
color: u32,
) {
let bytes = ffi::CStr::from_ptr(label).to_bytes();
pass.base.string_data.extend_from_slice(bytes);
pass.encode(&RenderCommand::InsertDebugMarker {
pass.base.commands.push(RenderCommand::InsertDebugMarker {
color,
len: bytes.len(),
phantom_marker: PhantomSlice::default(),
});
pass.encode_slice(bytes);
}
#[no_mangle]
pub unsafe fn wgpu_render_pass_execute_bundles(
pass: &mut RawPass,
pass: &mut RenderPass,
render_bundle_ids: *const id::RenderBundleId,
render_bundle_ids_length: usize,
) {
for &bundle_id in slice::from_raw_parts(render_bundle_ids, render_bundle_ids_length) {
pass.encode(&RenderCommand::ExecuteBundle(bundle_id));
pass.base
.commands
.push(RenderCommand::ExecuteBundle(bundle_id));
}
}
#[no_mangle]
pub unsafe extern "C" fn wgpu_render_pass_finish(
pass: &mut RawPass,
length: &mut usize,
) -> *const u8 {
pass.finish(RenderCommand::End);
*length = pass.size();
pass.base
}
}

View File

@ -574,13 +574,13 @@ pub(crate) fn map_texture_state(
(access, layout)
}
pub fn map_load_store_ops(load: wgt::LoadOp, store: wgt::StoreOp) -> hal::pass::AttachmentOps {
pub fn map_load_store_ops<V>(channel: &wgt::PassChannel<V>) -> hal::pass::AttachmentOps {
hal::pass::AttachmentOps {
load: match load {
load: match channel.load_op {
wgt::LoadOp::Clear => hal::pass::AttachmentLoadOp::Clear,
wgt::LoadOp::Load => hal::pass::AttachmentLoadOp::Load,
},
store: match store {
store: match channel.store_op {
wgt::StoreOp::Clear => hal::pass::AttachmentStoreOp::DontCare, //TODO!
wgt::StoreOp::Store => hal::pass::AttachmentStoreOp::Store,
},

View File

@ -86,6 +86,7 @@ pub enum HostMap {
}
#[derive(Clone, Debug, Hash, PartialEq)]
#[cfg_attr(feature = "serial-pass", derive(serde::Deserialize, serde::Serialize))]
pub(crate) struct AttachmentData<T> {
pub colors: ArrayVec<[T; MAX_COLOR_TARGETS]>,
pub resolves: ArrayVec<[T; MAX_COLOR_TARGETS]>,
@ -105,6 +106,7 @@ pub(crate) type RenderPassKey = AttachmentData<(hal::pass::Attachment, hal::imag
pub(crate) type FramebufferKey = AttachmentData<id::TextureViewId>;
#[derive(Clone, Debug, Hash, PartialEq)]
#[cfg_attr(feature = "serial-pass", derive(serde::Deserialize, serde::Serialize))]
pub(crate) struct RenderPassContext {
pub attachments: AttachmentData<TextureFormat>,
pub sample_count: u8,
@ -1492,17 +1494,18 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
buffer.usage,
pub_usage
);
let bind_size = if bb.size == BufferSize::WHOLE {
buffer.size - bb.offset
} else {
let end = bb.offset + bb.size.0;
assert!(
end <= buffer.size,
"Bound buffer range {:?} does not fit in buffer size {}",
bb.offset..end,
buffer.size
);
bb.size.0
let bind_size = match bb.size {
Some(size) => {
let end = bb.offset + size.get();
assert!(
end <= buffer.size,
"Bound buffer range {:?} does not fit in buffer size {}",
bb.offset..end,
buffer.size
);
size.get()
}
None => buffer.size - bb.offset,
};
match min_size {
Some(non_zero) if non_zero.get() > bind_size => panic!(
@ -1919,17 +1922,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
device_id: id::DeviceId,
desc: &wgt::RenderBundleEncoderDescriptor,
) -> id::RenderBundleEncoderId {
let encoder = command::RenderBundleEncoder::new(desc, device_id);
let encoder = command::RenderBundleEncoder::new(desc, device_id, None);
Box::into_raw(Box::new(encoder))
}
pub fn render_bundle_encoder_destroy(
&self,
render_bundle_encoder: command::RenderBundleEncoder,
) {
render_bundle_encoder.destroy();
}
pub fn render_bundle_destroy<B: GfxBackend>(&self, render_bundle_id: id::RenderBundleId) {
let hub = B::hub(self);
let mut token = Token::root();
@ -2732,7 +2728,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
&self,
buffer_id: id::BufferId,
offset: BufferAddress,
_size: BufferSize,
_size: Option<BufferSize>,
) -> *mut u8 {
let hub = B::hub(self);
let mut token = Token::root();

View File

@ -20,7 +20,7 @@ pub enum BindingResource {
Buffer {
id: id::BufferId,
offset: wgt::BufferAddress,
size: wgt::BufferSize,
size: Option<wgt::BufferSize>,
},
Sampler(id::SamplerId),
TextureView(id::TextureViewId),
@ -185,8 +185,7 @@ pub enum Action {
CreateRenderBundle {
id: id::RenderBundleId,
desc: RenderBundleDescriptor,
commands: Vec<crate::command::RenderCommand>,
dynamic_offsets: Vec<wgt::DynamicOffset>,
base: crate::command::BasePass<crate::command::RenderCommand>,
},
DestroyRenderBundle(id::RenderBundleId),
WriteBuffer {
@ -231,14 +230,12 @@ pub enum Command {
size: wgt::Extent3d,
},
RunComputePass {
commands: Vec<crate::command::ComputeCommand>,
dynamic_offsets: Vec<wgt::DynamicOffset>,
base: crate::command::BasePass<crate::command::ComputeCommand>,
},
RunRenderPass {
target_colors: Vec<crate::command::RenderPassColorAttachmentDescriptor>,
target_depth_stencil: Option<crate::command::RenderPassDepthStencilAttachmentDescriptor>,
commands: Vec<crate::command::RenderCommand>,
dynamic_offsets: Vec<wgt::DynamicOffset>,
base: crate::command::BasePass<crate::command::RenderCommand>,
target_colors: Vec<crate::command::ColorAttachmentDescriptor>,
target_depth_stencil: Option<crate::command::DepthStencilAttachmentDescriptor>,
},
}

View File

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::{Epoch, Index};
use std::{fmt, marker::PhantomData, mem, num::NonZeroU64};
use std::{fmt, marker::PhantomData, num::NonZeroU64};
use wgt::Backend;
const BACKEND_BITS: usize = 3;
@ -17,6 +17,14 @@ type Dummy = crate::backend::Empty;
derive(serde::Deserialize),
serde(from = "SerialId")
)]
#[cfg_attr(
all(feature = "serde", not(feature = "trace")),
derive(serde::Serialize)
)]
#[cfg_attr(
all(feature = "serde", not(feature = "replay")),
derive(serde::Deserialize)
)]
pub struct Id<T>(NonZeroU64, PhantomData<T>);
// This type represents Id in a more readable (and editable) way.
@ -43,20 +51,12 @@ impl<T> From<SerialId> for Id<T> {
}
}
// required for PeekPoke
impl<T> Default for Id<T> {
fn default() -> Self {
Id(
// Create an ID that doesn't make sense:
// the high `BACKEND_BITS` are to be set to 0, which matches `Backend::Empty`,
// the other bits are all 1s
unsafe { NonZeroU64::new_unchecked(!0 >> BACKEND_BITS) },
PhantomData,
)
}
}
impl<T> Id<T> {
#[cfg(test)]
pub(crate) fn dummy() -> Self {
Id(NonZeroU64::new(1).unwrap(), PhantomData)
}
pub fn backend(self) -> Backend {
match self.0.get() >> (64 - BACKEND_BITS) as u8 {
0 => Backend::Empty,
@ -68,14 +68,6 @@ impl<T> Id<T> {
_ => unreachable!(),
}
}
pub(crate) fn into_raw(self) -> u64 {
self.0.get()
}
pub(crate) fn from_raw(value: u64) -> Option<Self> {
NonZeroU64::new(value).map(|nz| Id(nz, PhantomData))
}
}
impl<T> Copy for Id<T> {}
@ -106,24 +98,6 @@ impl<T> PartialEq for Id<T> {
impl<T> Eq for Id<T> {}
unsafe impl<T> peek_poke::Poke for Id<T> {
fn max_size() -> usize {
mem::size_of::<u64>()
}
unsafe fn poke_into(&self, data: *mut u8) -> *mut u8 {
self.0.get().poke_into(data)
}
}
impl<T> peek_poke::Peek for Id<T> {
unsafe fn peek_from(mut data: *const u8, this: *mut Self) -> *const u8 {
let mut v = 0u64;
data = u64::peek_from(data, &mut v);
(*this).0 = NonZeroU64::new(v).unwrap();
data
}
}
pub trait TypedId {
fn zip(index: Index, epoch: Epoch, backend: Backend) -> Self;
fn unzip(self) -> (Index, Epoch, Backend);
@ -166,8 +140,8 @@ pub type ComputePipelineId = Id<crate::pipeline::ComputePipeline<Dummy>>;
// Command
pub type CommandEncoderId = CommandBufferId;
pub type CommandBufferId = Id<crate::command::CommandBuffer<Dummy>>;
pub type RenderPassId = *mut crate::command::RawPass<CommandEncoderId>;
pub type ComputePassId = *mut crate::command::RawPass<CommandEncoderId>;
pub type RenderPassEncoderId = *mut crate::command::RenderPass;
pub type ComputePassEncoderId = *mut crate::command::ComputePass;
pub type RenderBundleEncoderId = *mut crate::command::RenderBundleEncoder;
pub type RenderBundleId = Id<crate::command::RenderBundle>;
// Swap chain

View File

@ -150,7 +150,7 @@ mod test {
first: None,
last: BufferUse::INDEX,
};
let id = Id::default();
let id = Id::dummy();
assert_eq!(
bs.change(id, (), BufferUse::STORAGE_STORE, None),
Err(PendingTransition {
@ -170,7 +170,7 @@ mod test {
first: None,
last: BufferUse::STORAGE_STORE,
};
let id = Id::default();
let id = Id::dummy();
let mut list = Vec::new();
bs.change(id, (), BufferUse::VERTEX, Some(&mut list))
.unwrap();
@ -216,7 +216,7 @@ mod test {
first: None,
last: BufferUse::VERTEX,
};
let id = Id::default();
let id = Id::dummy();
bs.prepend(id, (), BufferUse::INDEX).unwrap();
bs.prepend(id, (), BufferUse::INDEX).unwrap();
assert_eq!(

View File

@ -324,7 +324,7 @@ mod test {
#[test]
fn merge() {
let id = Id::default();
let id = Id::dummy();
let mut ts1 = TextureState::default();
ts1.mips.push(PlaneStates::from_slice(&[(
1..3,
@ -342,7 +342,7 @@ mod test {
Unit::new(TextureUse::COPY_SRC),
)]));
assert_eq!(
ts1.merge(Id::default(), &ts2, None),
ts1.merge(Id::dummy(), &ts2, None),
Ok(()),
"failed to extend a compatible state"
);
@ -357,7 +357,7 @@ mod test {
ts2.mips[0] = PlaneStates::from_slice(&[(1..2, Unit::new(TextureUse::COPY_DST))]);
assert_eq!(
ts1.clone().merge(Id::default(), &ts2, None),
ts1.clone().merge(Id::dummy(), &ts2, None),
Err(PendingTransition {
id,
selector: SubresourceRange {
@ -381,7 +381,7 @@ mod test {
},
),
]);
ts1.merge(Id::default(), &ts2, Some(&mut list)).unwrap();
ts1.merge(Id::dummy(), &ts2, Some(&mut list)).unwrap();
assert_eq!(
&list,
&[
@ -433,7 +433,7 @@ mod test {
last: TextureUse::COPY_SRC,
},
)]);
ts1.merge(Id::default(), &ts2, Some(&mut list)).unwrap();
ts1.merge(Id::dummy(), &ts2, Some(&mut list)).unwrap();
assert_eq!(&list, &[], "unexpected replacing transition");
list.clear();
@ -444,7 +444,7 @@ mod test {
last: TextureUse::COPY_DST,
},
)]);
ts1.merge(Id::default(), &ts2, Some(&mut list)).unwrap();
ts1.merge(Id::dummy(), &ts2, Some(&mut list)).unwrap();
assert_eq!(
&list,
&[PendingTransition {

View File

@ -18,4 +18,3 @@ replay = ["serde"]
[dependencies]
bitflags = "1.0"
serde = { version = "1.0", features = ["serde_derive"], optional = true }
peek-poke = { version = "0.2", optional = true }

View File

@ -2,16 +2,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#[cfg(feature = "peek-poke")]
use peek_poke::PeekPoke;
#[cfg(feature = "replay")]
use serde::Deserialize;
#[cfg(feature = "trace")]
use serde::Serialize;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// Integral type used for buffer sizes/offsets.
/// Integral type used for buffer offsets.
pub type BufferAddress = u64;
pub type NonZeroBufferAddress = std::num::NonZeroU64;
/// Integral type used for buffer slice sizes.
pub type BufferSize = std::num::NonZeroU64;
/// Buffer-Texture copies must have [`bytes_per_row`] aligned to this number.
///
@ -24,32 +21,6 @@ pub const BIND_BUFFER_ALIGNMENT: BufferAddress = 256;
/// Buffer to buffer copy offsets and sizes must be aligned to this number.
pub const COPY_BUFFER_ALIGNMENT: BufferAddress = 4;
/// Integral newtype for buffer sizes.
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Hash, PartialEq)]
#[cfg_attr(feature = "peek-poke", derive(PeekPoke))]
#[cfg_attr(
feature = "trace",
derive(serde::Serialize),
serde(into = "SerBufferSize")
)]
#[cfg_attr(
feature = "replay",
derive(serde::Deserialize),
serde(from = "SerBufferSize")
)]
pub struct BufferSize(pub BufferAddress);
impl BufferSize {
pub const WHOLE: BufferSize = BufferSize(!0);
}
impl Default for BufferSize {
fn default() -> Self {
BufferSize::WHOLE
}
}
/// Backends supported by wgpu.
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@ -612,8 +583,7 @@ pub struct RasterizationStateDescriptor {
/// loading from texture in a shader. When writing to the texture, the opposite conversion takes place.
#[repr(C)]
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum TextureFormat {
// Normal 8 bit formats
/// Red channel only. 8 bit integer per channel. [0, 255] converted to/from float [0, 1] in shader.
@ -1149,9 +1119,7 @@ pub enum SwapChainStatus {
/// Operation to perform to the output attachment at the start of a renderpass.
#[repr(C)]
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[cfg_attr(feature = "peek-poke", derive(PeekPoke))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum LoadOp {
/// Clear the output attachment with the clear color. Clearing is faster than loading.
Clear = 0,
@ -1162,9 +1130,7 @@ pub enum LoadOp {
/// Operation to perform to the output attachment at the end of a renderpass.
#[repr(C)]
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[cfg_attr(feature = "peek-poke", derive(PeekPoke))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum StoreOp {
/// Clear the render target. If you don't care about the contents of the target, this can be faster.
Clear = 0,
@ -1172,12 +1138,27 @@ pub enum StoreOp {
Store = 1,
}
/// Describes an individual channel within a render pass, such as color, depth, or stencil.
#[repr(C)]
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PassChannel<V> {
/// Operation to perform to the output attachment at the start of a renderpass. This must be clear if it
/// is the first renderpass rendering to a swap chain image.
pub load_op: LoadOp,
/// Operation to perform to the output attachment at the end of a renderpass.
pub store_op: StoreOp,
/// If load_op is [`LoadOp::Clear`], the attachement will be cleared to this color.
pub clear_value: V,
/// If true, the relevant channel is not changed by a renderpass, and the corresponding attachment
/// can be used inside the pass by other read-only usages.
pub read_only: bool,
}
/// Describes a color attachment to a [`RenderPass`].
#[repr(C)]
#[derive(Clone, Debug)]
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[cfg_attr(feature = "peek-poke", derive(PeekPoke))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct RenderPassColorAttachmentDescriptorBase<T> {
/// Texture attachment to render to. Must contain [`TextureUsage::OUTPUT_ATTACHMENT`].
pub attachment: T,
@ -1185,44 +1166,22 @@ pub struct RenderPassColorAttachmentDescriptorBase<T> {
/// attachment has 1 sample (does not have MSAA). This is not mandatory for rendering with multisampling,
/// you can choose to resolve later or manually.
pub resolve_target: Option<T>,
/// Operation to perform to the output attachment at the start of a renderpass. This must be clear if it
/// is the first renderpass rendering to a swap chain image.
pub load_op: LoadOp,
/// Operation to perform to the output attachment at the end of a renderpass.
pub store_op: StoreOp,
/// If load_op is [`LoadOp::Clear`], the attachement will be cleared to this color.
pub clear_color: Color,
/// Color channel.
pub channel: PassChannel<Color>,
}
/// Describes a depth/stencil attachment to a [`RenderPass`].
#[repr(C)]
#[derive(Clone, Debug)]
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[cfg_attr(feature = "peek-poke", derive(PeekPoke))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct RenderPassDepthStencilAttachmentDescriptorBase<T> {
/// Texture attachment to render to. Must contain [`TextureUsage::OUTPUT_ATTACHMENT`] and be a valid
/// texture type for a depth/stencil attachment.
pub attachment: T,
/// Operation to perform to the depth component of the output attachment at the start of a renderpass.
pub depth_load_op: LoadOp,
/// Operation to perform to the depth component of the output attachment at the end of a renderpass.
pub depth_store_op: StoreOp,
/// If depth_load_op is [`LoadOp::Clear`], the depth component will be cleared to this depth.
pub clear_depth: f32,
/// Enabling read only depth means that depth cannot be cleared or written to, but it can be used
/// as the input to some part of this render pass.
pub depth_read_only: bool,
/// Operation to perform to the stencil component of the output attachment at the start of a renderpass.
pub stencil_load_op: LoadOp,
/// Operation to perform to the stencil component of the output attachment at the end of a renderpass.
pub stencil_store_op: StoreOp,
/// If stencil_load_op is [`LoadOp::Clear`], the stencil component will be cleared to this stencil value.
/// Only the low 8 bits are used.
pub clear_stencil: u32,
/// Enabling read only stencil means that stencil cannot be cleared or written to, but it can be used
/// as the input to some part of this render pass.
pub stencil_read_only: bool,
/// Depth channel.
pub depth: PassChannel<f32>,
/// Stencil channel.
pub stencil: PassChannel<u32>,
}
/// RGBA double precision color.
@ -1230,9 +1189,7 @@ pub struct RenderPassDepthStencilAttachmentDescriptorBase<T> {
/// This is not to be used as a generic color type, only for specific wgpu interfaces.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq)]
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[cfg_attr(feature = "peek-poke", derive(PeekPoke))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Color {
pub r: f64,
pub g: f64,
@ -1683,7 +1640,7 @@ pub enum BindingType {
/// When pipeline is created, the size has to cover at least the corresponding structure in the shader
/// plus one element of the unbound array, which can only be last in the structure.
/// If `None`, the check is performed at draw call time instead of pipeline and bind group creation.
min_binding_size: Option<NonZeroBufferAddress>,
min_binding_size: Option<BufferSize>,
},
/// A storage buffer.
///
@ -1701,7 +1658,7 @@ pub enum BindingType {
/// When pipeline is created, the size has to cover at least the corresponding structure in the shader
/// plus one element of the unbound array, which can only be last in the structure.
/// If `None`, the check is performed at draw call time instead of pipeline and bind group creation.
min_binding_size: Option<NonZeroBufferAddress>,
min_binding_size: Option<BufferSize>,
/// The buffer can only be read in the shader and it must be annotated with `readonly`.
///
/// Example GLSL syntax:
@ -1817,33 +1774,3 @@ pub struct BindGroupLayoutDescriptor<'a> {
/// Array of bindings in this BindGroupLayout
pub bindings: &'a [BindGroupLayoutEntry],
}
/// This type allows us to make the serialized representation of a BufferSize more human-readable
#[allow(dead_code)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
enum SerBufferSize {
Size(u64),
Whole,
}
#[cfg(feature = "trace")]
impl From<BufferSize> for SerBufferSize {
fn from(buffer_size: BufferSize) -> Self {
if buffer_size == BufferSize::WHOLE {
Self::Whole
} else {
Self::Size(buffer_size.0)
}
}
}
#[cfg(feature = "replay")]
impl From<SerBufferSize> for BufferSize {
fn from(ser_buffer_size: SerBufferSize) -> Self {
match ser_buffer_size {
SerBufferSize::Size(size) => BufferSize(size),
SerBufferSize::Whole => BufferSize::WHOLE,
}
}
}