mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-03 07:01:19 +00:00
Bug 1629605 - wgpu tracking fixes r=jgilbert
Differential Revision: https://phabricator.services.mozilla.com/D70707 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
2ad9b58e73
commit
964f4f889c
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -5021,7 +5021,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wgpu-core"
|
||||
version = "0.1.0"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags",
|
||||
@ -5056,7 +5056,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wgpu-types"
|
||||
version = "0.1.0"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"peek-poke 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1,5 +1,35 @@
|
||||
# Change Log
|
||||
|
||||
## v0.5 (06-04-2020)
|
||||
- Crates:
|
||||
- `wgpu-types`: common types between native and web targets
|
||||
- `wgpu-core`: internal API for the native and remote wrappers
|
||||
- Features:
|
||||
- based on gfx-hal-0.5
|
||||
- moved from Rendy to the new `gfx-memory` and `gfx-descriptor` crates
|
||||
- passes are now recorded on the client side. The user is also responsible to keep all resources referenced in the pass up until it ends recording.
|
||||
- revised GPU lifetime tracking of all resources
|
||||
- revised usage tracking logic
|
||||
- all IDs are now non-zero
|
||||
- Mailbox present mode
|
||||
- Validation:
|
||||
- active pipeline
|
||||
- Fixes:
|
||||
- lots of small API changes to closely match upstream WebGPU
|
||||
- true read-only storage bindings
|
||||
- unmapping dropped buffers
|
||||
- better error messages on misused swapchain frames
|
||||
|
||||
## v0.4.3 (20-01-2020)
|
||||
- improved swap chain error handling
|
||||
|
||||
## v0.4.2 (15-12-2019)
|
||||
- fixed render pass transitions
|
||||
|
||||
## v0.4.1 (28-11-2019)
|
||||
- fixed depth/stencil transitions
|
||||
- fixed dynamic offset iteration
|
||||
|
||||
## v0.4 (03-11-2019)
|
||||
- Platforms: removed OpenGL/WebGL support temporarily
|
||||
- Features:
|
||||
|
16
gfx/wgpu/Cargo.lock
generated
16
gfx/wgpu/Cargo.lock
generated
@ -653,7 +653,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "wgpu-core"
|
||||
version = "0.1.0"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"battery 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -674,12 +674,12 @@ dependencies = [
|
||||
"serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wgpu-types 0.1.0",
|
||||
"wgpu-types 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wgpu-native"
|
||||
version = "0.4.0"
|
||||
version = "0.5.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)",
|
||||
@ -687,8 +687,8 @@ dependencies = [
|
||||
"objc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"raw-window-handle 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wgpu-core 0.1.0",
|
||||
"wgpu-types 0.1.0",
|
||||
"wgpu-core 0.5.0",
|
||||
"wgpu-types 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -697,13 +697,13 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wgpu-core 0.1.0",
|
||||
"wgpu-types 0.1.0",
|
||||
"wgpu-core 0.5.0",
|
||||
"wgpu-types 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wgpu-types"
|
||||
version = "0.1.0"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"peek-poke 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2,7 +2,7 @@
|
||||
* 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/. */
|
||||
|
||||
/* Generated with cbindgen:0.14.0 */
|
||||
/* Generated with cbindgen:0.14.1 */
|
||||
|
||||
/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen.
|
||||
* To generate this file:
|
||||
@ -37,7 +37,7 @@ typedef unsigned long long WGPUOption_TextureViewId;
|
||||
|
||||
#define WGPUMAX_MIP_LEVELS 16
|
||||
|
||||
#define WGPUMAX_VERTEX_BUFFERS 8
|
||||
#define WGPUMAX_VERTEX_BUFFERS 16
|
||||
|
||||
typedef enum {
|
||||
WGPUAddressMode_ClampToEdge = 0,
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wgpu-core"
|
||||
version = "0.1.0"
|
||||
version = "0.5.0"
|
||||
authors = [
|
||||
"Dzmitry Malyshau <kvark@mozilla.com>",
|
||||
"Joshua Groves <josh@joshgroves.com>",
|
||||
@ -45,7 +45,7 @@ optional = true
|
||||
[dependencies.wgt]
|
||||
path = "../wgpu-types"
|
||||
package = "wgpu-types"
|
||||
version = "0.1"
|
||||
version = "0.5"
|
||||
features = ["peek-poke"]
|
||||
|
||||
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
|
||||
|
@ -23,15 +23,14 @@ use crate::{
|
||||
use arrayvec::ArrayVec;
|
||||
use hal::command::CommandBuffer as _;
|
||||
use peek_poke::{Peek, PeekPoke, Poke};
|
||||
use smallvec::SmallVec;
|
||||
use wgt::{
|
||||
BufferAddress, BufferUsage, Color, DynamicOffset, IndexFormat, InputStepMode, LoadOp,
|
||||
RenderPassColorAttachmentDescriptorBase, RenderPassDepthStencilAttachmentDescriptorBase,
|
||||
TextureUsage, BIND_BUFFER_ALIGNMENT,
|
||||
};
|
||||
|
||||
use std::{
|
||||
borrow::Borrow, collections::hash_map::Entry, iter, marker::PhantomData, mem, ops::Range, slice,
|
||||
};
|
||||
use std::{borrow::Borrow, collections::hash_map::Entry, iter, mem, ops::Range, slice};
|
||||
|
||||
pub type RenderPassColorAttachmentDescriptor =
|
||||
RenderPassColorAttachmentDescriptorBase<id::TextureViewId>;
|
||||
@ -227,7 +226,7 @@ impl VertexBufferState {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VertexState {
|
||||
inputs: [VertexBufferState; MAX_VERTEX_BUFFERS],
|
||||
inputs: SmallVec<[VertexBufferState; MAX_VERTEX_BUFFERS]>,
|
||||
vertex_limit: u32,
|
||||
instance_limit: u32,
|
||||
}
|
||||
@ -438,7 +437,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let mut resolves = ArrayVec::new();
|
||||
|
||||
for at in &color_attachments {
|
||||
let view = &view_guard[at.attachment];
|
||||
let view = trackers
|
||||
.views
|
||||
.use_extend(&*view_guard, at.attachment, (), ())
|
||||
.unwrap();
|
||||
if let Some(ex) = extent {
|
||||
assert_eq!(ex, view.extent);
|
||||
} else {
|
||||
@ -448,10 +450,6 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
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, .. } => {
|
||||
@ -477,10 +475,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
}
|
||||
|
||||
let end = hal::image::Layout::Present;
|
||||
let start = if first_use {
|
||||
hal::image::Layout::Undefined
|
||||
} else {
|
||||
end
|
||||
let start = match base_trackers.views.query(at.attachment, ()) {
|
||||
Some(_) => end,
|
||||
None => hal::image::Layout::Undefined,
|
||||
};
|
||||
start..end
|
||||
}
|
||||
@ -496,16 +493,15 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
}
|
||||
|
||||
for resolve_target in color_attachments.iter().flat_map(|at| at.resolve_target) {
|
||||
let view = &view_guard[resolve_target];
|
||||
let view = trackers
|
||||
.views
|
||||
.use_extend(&*view_guard, resolve_target, (), ())
|
||||
.unwrap();
|
||||
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, .. } => {
|
||||
@ -531,10 +527,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
}
|
||||
|
||||
let end = hal::image::Layout::Present;
|
||||
let start = if first_use {
|
||||
hal::image::Layout::Undefined
|
||||
} else {
|
||||
end
|
||||
let start = match base_trackers.views.query(resolve_target, ()) {
|
||||
Some(_) => end,
|
||||
None => hal::image::Layout::Undefined,
|
||||
};
|
||||
start..end
|
||||
}
|
||||
@ -798,7 +793,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
limit: 0,
|
||||
},
|
||||
vertex: VertexState {
|
||||
inputs: [VertexBufferState::EMPTY; MAX_VERTEX_BUFFERS],
|
||||
inputs: SmallVec::new(),
|
||||
vertex_limit: 0,
|
||||
instance_limit: 0,
|
||||
},
|
||||
@ -968,7 +963,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
vbs.stride = stride;
|
||||
vbs.rate = rate;
|
||||
}
|
||||
for vbs in state.vertex.inputs[pipeline.vertex_strides.len()..].iter_mut() {
|
||||
let vertex_strides_len = pipeline.vertex_strides.len();
|
||||
for vbs in state.vertex.inputs.iter_mut().skip(vertex_strides_len) {
|
||||
vbs.stride = 0;
|
||||
vbs.rate = InputStepMode::Vertex;
|
||||
}
|
||||
@ -1017,6 +1013,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
.use_extend(&*buffer_guard, buffer_id, (), BufferUsage::VERTEX)
|
||||
.unwrap();
|
||||
assert!(buffer.usage.contains(BufferUsage::VERTEX));
|
||||
let empty_slots = (1 + slot as usize).saturating_sub(state.vertex.inputs.len());
|
||||
state
|
||||
.vertex
|
||||
.inputs
|
||||
.extend(iter::repeat(VertexBufferState::EMPTY).take(empty_slots));
|
||||
state.vertex.inputs[slot as usize].total_size = if size != 0 {
|
||||
size
|
||||
} else {
|
||||
@ -1158,6 +1159,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
}
|
||||
}
|
||||
|
||||
log::trace!("Merging {:?} with the render pass", encoder_id);
|
||||
unsafe {
|
||||
raw.end_render_pass();
|
||||
}
|
||||
super::CommandBuffer::insert_barriers(
|
||||
cmb.raw.last_mut().unwrap(),
|
||||
&mut cmb.trackers,
|
||||
@ -1167,7 +1172,6 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
);
|
||||
unsafe {
|
||||
cmb.raw.last_mut().unwrap().finish();
|
||||
raw.end_render_pass();
|
||||
}
|
||||
cmb.raw.push(raw);
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ mod life;
|
||||
|
||||
pub const MAX_COLOR_TARGETS: usize = 4;
|
||||
pub const MAX_MIP_LEVELS: usize = 16;
|
||||
pub const MAX_VERTEX_BUFFERS: usize = 8;
|
||||
pub const MAX_VERTEX_BUFFERS: usize = 16;
|
||||
|
||||
pub fn all_buffer_stages() -> hal::pso::PipelineStage {
|
||||
use hal::pso::PipelineStage as Ps;
|
||||
@ -1044,7 +1044,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let (bind_group_layout_guard, mut token) = hub.bind_group_layouts.read(&mut token);
|
||||
let bind_group_layout = &bind_group_layout_guard[desc.layout];
|
||||
let entries = unsafe { slice::from_raw_parts(desc.entries, desc.entries_length) };
|
||||
assert_eq!(entries.len(), bind_group_layout.entries.len());
|
||||
assert_eq!(entries.len(), bind_group_layout.entries.len(), "Bind group has {} entries and bind group layout has {} entries, they should be the same.", entries.len(), bind_group_layout.entries.len());
|
||||
|
||||
let desc_set = unsafe {
|
||||
let mut desc_sets = ArrayVec::<[_; 1]>::new();
|
||||
@ -1503,6 +1503,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
// execute resource transitions
|
||||
let mut transit = device.com_allocator.extend(comb);
|
||||
unsafe {
|
||||
// the last buffer was open, closing now
|
||||
comb.raw.last_mut().unwrap().finish();
|
||||
transit.begin_primary(hal::command::CommandBufferFlags::ONE_TIME_SUBMIT);
|
||||
}
|
||||
log::trace!("Stitching command buffer {:?} before submission", cmb_id);
|
||||
@ -1517,9 +1519,6 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
transit.finish();
|
||||
}
|
||||
comb.raw.insert(0, transit);
|
||||
unsafe {
|
||||
comb.raw.last_mut().unwrap().finish();
|
||||
}
|
||||
}
|
||||
|
||||
log::debug!("Device after submission {}: {:#?}", submit_index, trackers);
|
||||
|
@ -23,7 +23,13 @@ pub struct Id<T>(NonZeroU64, PhantomData<T>);
|
||||
// required for PeekPoke
|
||||
impl<T> Default for Id<T> {
|
||||
fn default() -> Self {
|
||||
Id(unsafe { NonZeroU64::new_unchecked(!0) }, PhantomData)
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,22 +55,28 @@ impl ResourceState for BufferState {
|
||||
output: Option<&mut Vec<PendingTransition<Self>>>,
|
||||
) -> Result<(), PendingTransition<Self>> {
|
||||
let old = self.last;
|
||||
if usage != old || !BufferUsage::ORDERED.contains(usage) {
|
||||
if old != usage || !BufferUsage::ORDERED.contains(usage) {
|
||||
let pending = PendingTransition {
|
||||
id,
|
||||
selector: (),
|
||||
usage: old..usage,
|
||||
};
|
||||
self.last = match output {
|
||||
None => pending.collapse()?,
|
||||
*self = match output {
|
||||
None => {
|
||||
assert_eq!(
|
||||
self.first, None,
|
||||
"extending a state that is already a transition"
|
||||
);
|
||||
Unit::new(pending.collapse()?)
|
||||
}
|
||||
Some(transitions) => {
|
||||
transitions.push(pending);
|
||||
if self.first.is_none() {
|
||||
self.first = Some(old);
|
||||
Unit {
|
||||
first: self.first.or(Some(old)),
|
||||
last: usage,
|
||||
}
|
||||
usage
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -83,28 +89,33 @@ impl ResourceState for BufferState {
|
||||
) -> Result<(), PendingTransition<Self>> {
|
||||
let old = self.last;
|
||||
let new = other.port();
|
||||
self.last = if old == new && BufferUsage::ORDERED.contains(new) {
|
||||
if self.first.is_none() {
|
||||
if old == new && BufferUsage::ORDERED.contains(new) {
|
||||
if output.is_some() && self.first.is_none() {
|
||||
self.first = Some(old);
|
||||
}
|
||||
other.last
|
||||
} else {
|
||||
let pending = PendingTransition {
|
||||
id,
|
||||
selector: (),
|
||||
usage: old..new,
|
||||
};
|
||||
match output {
|
||||
None => pending.collapse()?,
|
||||
*self = match output {
|
||||
None => {
|
||||
assert_eq!(
|
||||
self.first, None,
|
||||
"extending a state that is already a transition"
|
||||
);
|
||||
Unit::new(pending.collapse()?)
|
||||
}
|
||||
Some(transitions) => {
|
||||
transitions.push(pending);
|
||||
if self.first.is_none() {
|
||||
self.first = Some(old);
|
||||
Unit {
|
||||
first: self.first.or(Some(old)),
|
||||
last: other.last,
|
||||
}
|
||||
other.last
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -114,19 +125,71 @@ impl ResourceState for BufferState {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::id::TypedId;
|
||||
use crate::id::Id;
|
||||
|
||||
#[test]
|
||||
fn change() {
|
||||
fn change_extend() {
|
||||
let mut bs = Unit {
|
||||
first: Some(BufferUsage::INDEX),
|
||||
first: None,
|
||||
last: BufferUsage::INDEX,
|
||||
};
|
||||
let id = Id::default();
|
||||
assert_eq!(
|
||||
bs.change(id, (), BufferUsage::STORAGE, None),
|
||||
Err(PendingTransition {
|
||||
id,
|
||||
selector: (),
|
||||
usage: BufferUsage::INDEX..BufferUsage::STORAGE,
|
||||
}),
|
||||
);
|
||||
bs.change(id, (), BufferUsage::VERTEX, None).unwrap();
|
||||
bs.change(id, (), BufferUsage::INDEX, None).unwrap();
|
||||
assert_eq!(bs, Unit::new(BufferUsage::VERTEX | BufferUsage::INDEX));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_replace() {
|
||||
let mut bs = Unit {
|
||||
first: None,
|
||||
last: BufferUsage::STORAGE,
|
||||
};
|
||||
let id = TypedId::zip(1, 0, wgt::Backend::Empty);
|
||||
assert!(bs.change(id, (), BufferUsage::VERTEX, None).is_err());
|
||||
bs.change(id, (), BufferUsage::VERTEX, Some(&mut Vec::new()))
|
||||
let id = Id::default();
|
||||
let mut list = Vec::new();
|
||||
bs.change(id, (), BufferUsage::VERTEX, Some(&mut list))
|
||||
.unwrap();
|
||||
bs.change(id, (), BufferUsage::INDEX, None).unwrap();
|
||||
assert_eq!(bs.last, BufferUsage::VERTEX | BufferUsage::INDEX);
|
||||
assert_eq!(
|
||||
&list,
|
||||
&[PendingTransition {
|
||||
id,
|
||||
selector: (),
|
||||
usage: BufferUsage::STORAGE..BufferUsage::VERTEX,
|
||||
}],
|
||||
);
|
||||
assert_eq!(
|
||||
bs,
|
||||
Unit {
|
||||
first: Some(BufferUsage::STORAGE),
|
||||
last: BufferUsage::VERTEX,
|
||||
}
|
||||
);
|
||||
|
||||
list.clear();
|
||||
bs.change(id, (), BufferUsage::STORAGE, Some(&mut list))
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
&list,
|
||||
&[PendingTransition {
|
||||
id,
|
||||
selector: (),
|
||||
usage: BufferUsage::VERTEX..BufferUsage::STORAGE,
|
||||
}],
|
||||
);
|
||||
assert_eq!(
|
||||
bs,
|
||||
Unit {
|
||||
first: Some(BufferUsage::STORAGE),
|
||||
last: BufferUsage::STORAGE,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ struct Resource<S> {
|
||||
/// A structure containing all the information about a particular resource
|
||||
/// transition. User code should be able to generate a pipeline barrier
|
||||
/// based on the contents.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct PendingTransition<S: ResourceState> {
|
||||
pub id: S::Id,
|
||||
pub selector: S::Selector,
|
||||
|
@ -9,7 +9,7 @@ use std::{cmp::Ordering, fmt::Debug, iter, ops::Range, slice::Iter};
|
||||
/// Structure that keeps track of a I -> T mapping,
|
||||
/// optimized for a case where keys of the same values
|
||||
/// are often grouped together linearly.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct RangedStates<I, T> {
|
||||
/// List of ranges, each associated with a singe value.
|
||||
/// Ranges of keys have to be non-intersecting and ordered.
|
||||
|
@ -13,7 +13,7 @@ use std::{iter, ops::Range};
|
||||
//TODO: store `hal::image::State` here to avoid extra conversions
|
||||
type PlaneStates = RangedStates<hal::image::Layer, Unit<TextureUsage>>;
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct TextureState {
|
||||
mips: ArrayVec<[PlaneStates; MAX_MIP_LEVELS]>,
|
||||
/// True if we have the information about all the subresources here
|
||||
@ -116,14 +116,20 @@ impl ResourceState for TextureState {
|
||||
usage: unit.last..usage,
|
||||
};
|
||||
|
||||
unit.last = match output {
|
||||
None => pending.collapse()?,
|
||||
*unit = match output {
|
||||
None => {
|
||||
assert_eq!(
|
||||
unit.first, None,
|
||||
"extending a state that is already a transition"
|
||||
);
|
||||
Unit::new(pending.collapse()?)
|
||||
}
|
||||
Some(ref mut out) => {
|
||||
out.push(pending);
|
||||
if unit.first.is_none() {
|
||||
unit.first = Some(unit.last);
|
||||
Unit {
|
||||
first: unit.first.or(Some(unit.last)),
|
||||
last: usage,
|
||||
}
|
||||
usage
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -172,7 +178,10 @@ impl ResourceState for TextureState {
|
||||
let to_usage = end.port();
|
||||
if start.last == to_usage && TextureUsage::ORDERED.contains(to_usage) {
|
||||
Unit {
|
||||
first: start.first,
|
||||
first: match output {
|
||||
None => start.first,
|
||||
Some(_) => start.first.or(Some(start.last)),
|
||||
},
|
||||
last: end.last,
|
||||
}
|
||||
} else {
|
||||
@ -191,14 +200,18 @@ impl ResourceState for TextureState {
|
||||
};
|
||||
|
||||
match output {
|
||||
None => Unit {
|
||||
first: start.first,
|
||||
last: pending.collapse()?,
|
||||
},
|
||||
None => {
|
||||
assert_eq!(
|
||||
start.first, None,
|
||||
"extending a state that is already a transition"
|
||||
);
|
||||
Unit::new(pending.collapse()?)
|
||||
}
|
||||
Some(ref mut out) => {
|
||||
out.push(pending);
|
||||
Unit {
|
||||
first: Some(start.last),
|
||||
// this has to leave a valid `first` state
|
||||
first: start.first.or(Some(start.last)),
|
||||
last: end.last,
|
||||
}
|
||||
}
|
||||
@ -222,9 +235,9 @@ impl ResourceState for TextureState {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
//TODO: change() and merge() tests
|
||||
//use crate::TypedId;
|
||||
//TODO: change() tests
|
||||
use super::*;
|
||||
use crate::id::Id;
|
||||
use hal::{format::Aspects, image::SubresourceRange};
|
||||
|
||||
#[test]
|
||||
@ -274,4 +287,151 @@ mod test {
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge() {
|
||||
let id = Id::default();
|
||||
let mut ts1 = TextureState::default();
|
||||
ts1.mips.push(PlaneStates::from_slice(&[(
|
||||
1..3,
|
||||
Unit::new(TextureUsage::SAMPLED),
|
||||
)]));
|
||||
let mut ts2 = TextureState::default();
|
||||
assert_eq!(
|
||||
ts1.merge(id, &ts2, None),
|
||||
Ok(()),
|
||||
"failed to merge with an empty"
|
||||
);
|
||||
|
||||
ts2.mips.push(PlaneStates::from_slice(&[(
|
||||
1..2,
|
||||
Unit::new(TextureUsage::COPY_SRC),
|
||||
)]));
|
||||
assert_eq!(
|
||||
ts1.merge(Id::default(), &ts2, None),
|
||||
Ok(()),
|
||||
"failed to extend a compatible state"
|
||||
);
|
||||
assert_eq!(
|
||||
ts1.mips[0].query(&(1..2), |&v| v),
|
||||
Some(Ok(Unit {
|
||||
first: None,
|
||||
last: TextureUsage::SAMPLED | TextureUsage::COPY_SRC,
|
||||
})),
|
||||
"wrong extension result"
|
||||
);
|
||||
|
||||
ts2.mips[0] = PlaneStates::from_slice(&[(1..2, Unit::new(TextureUsage::COPY_DST))]);
|
||||
assert_eq!(
|
||||
ts1.clone().merge(Id::default(), &ts2, None),
|
||||
Err(PendingTransition {
|
||||
id,
|
||||
selector: SubresourceRange {
|
||||
aspects: Aspects::empty(),
|
||||
levels: 0..1,
|
||||
layers: 1..2,
|
||||
},
|
||||
usage: TextureUsage::SAMPLED | TextureUsage::COPY_SRC..TextureUsage::COPY_DST,
|
||||
}),
|
||||
"wrong error on extending with incompatible state"
|
||||
);
|
||||
|
||||
let mut list = Vec::new();
|
||||
ts2.mips[0] = PlaneStates::from_slice(&[
|
||||
(1..2, Unit::new(TextureUsage::COPY_DST)),
|
||||
(
|
||||
2..3,
|
||||
Unit {
|
||||
first: Some(TextureUsage::COPY_SRC),
|
||||
last: TextureUsage::OUTPUT_ATTACHMENT,
|
||||
},
|
||||
),
|
||||
]);
|
||||
ts1.merge(Id::default(), &ts2, Some(&mut list)).unwrap();
|
||||
assert_eq!(
|
||||
&list,
|
||||
&[
|
||||
PendingTransition {
|
||||
id,
|
||||
selector: SubresourceRange {
|
||||
aspects: Aspects::empty(),
|
||||
levels: 0..1,
|
||||
layers: 1..2,
|
||||
},
|
||||
usage: TextureUsage::SAMPLED | TextureUsage::COPY_SRC..TextureUsage::COPY_DST,
|
||||
},
|
||||
PendingTransition {
|
||||
id,
|
||||
selector: SubresourceRange {
|
||||
aspects: Aspects::empty(),
|
||||
levels: 0..1,
|
||||
layers: 2..3,
|
||||
},
|
||||
// the transition links the end of the base rage (..SAMPLED)
|
||||
// with the start of the next range (COPY_SRC..)
|
||||
usage: TextureUsage::SAMPLED..TextureUsage::COPY_SRC,
|
||||
},
|
||||
],
|
||||
"replacing produced wrong transitions"
|
||||
);
|
||||
assert_eq!(
|
||||
ts1.mips[0].query(&(1..2), |&v| v),
|
||||
Some(Ok(Unit {
|
||||
first: Some(TextureUsage::SAMPLED | TextureUsage::COPY_SRC),
|
||||
last: TextureUsage::COPY_DST,
|
||||
})),
|
||||
"wrong final layer 1 state"
|
||||
);
|
||||
assert_eq!(
|
||||
ts1.mips[0].query(&(2..3), |&v| v),
|
||||
Some(Ok(Unit {
|
||||
first: Some(TextureUsage::SAMPLED),
|
||||
last: TextureUsage::OUTPUT_ATTACHMENT,
|
||||
})),
|
||||
"wrong final layer 2 state"
|
||||
);
|
||||
|
||||
list.clear();
|
||||
ts2.mips[0] = PlaneStates::from_slice(&[(
|
||||
2..3,
|
||||
Unit {
|
||||
first: Some(TextureUsage::OUTPUT_ATTACHMENT),
|
||||
last: TextureUsage::COPY_SRC,
|
||||
},
|
||||
)]);
|
||||
ts1.merge(Id::default(), &ts2, Some(&mut list)).unwrap();
|
||||
assert_eq!(&list, &[], "unexpected replacing transition");
|
||||
|
||||
list.clear();
|
||||
ts2.mips[0] = PlaneStates::from_slice(&[(
|
||||
2..3,
|
||||
Unit {
|
||||
first: Some(TextureUsage::COPY_DST),
|
||||
last: TextureUsage::COPY_DST,
|
||||
},
|
||||
)]);
|
||||
ts1.merge(Id::default(), &ts2, Some(&mut list)).unwrap();
|
||||
assert_eq!(
|
||||
&list,
|
||||
&[PendingTransition {
|
||||
id,
|
||||
selector: SubresourceRange {
|
||||
aspects: Aspects::empty(),
|
||||
levels: 0..1,
|
||||
layers: 2..3,
|
||||
},
|
||||
usage: TextureUsage::COPY_SRC..TextureUsage::COPY_DST,
|
||||
},],
|
||||
"invalid replacing transition"
|
||||
);
|
||||
assert_eq!(
|
||||
ts1.mips[0].query(&(2..3), |&v| v),
|
||||
Some(Ok(Unit {
|
||||
// the initial state here is never expected to change
|
||||
first: Some(TextureUsage::SAMPLED),
|
||||
last: TextureUsage::COPY_DST,
|
||||
})),
|
||||
"wrong final layer 2 state"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wgpu-native"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
authors = [
|
||||
"Dzmitry Malyshau <kvark@mozilla.com>",
|
||||
"Joshua Groves <josh@joshgroves.com>",
|
||||
@ -23,12 +23,12 @@ vulkan-portability = ["core/gfx-backend-vulkan"]
|
||||
[dependencies.core]
|
||||
path = "../wgpu-core"
|
||||
package = "wgpu-core"
|
||||
version = "0.1"
|
||||
version = "0.5"
|
||||
|
||||
[dependencies.wgt]
|
||||
path = "../wgpu-types"
|
||||
package = "wgpu-types"
|
||||
version = "0.1"
|
||||
version = "0.5"
|
||||
|
||||
[dependencies]
|
||||
arrayvec = "0.5"
|
||||
|
@ -7,6 +7,7 @@ authors = [
|
||||
]
|
||||
edition = "2018"
|
||||
license = "MPL-2.0"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
# Enabling these targets makes our CI bots try to build them and fail atm
|
||||
@ -18,13 +19,13 @@ default = []
|
||||
[dependencies.core]
|
||||
path = "../wgpu-core"
|
||||
package = "wgpu-core"
|
||||
version = "0.1"
|
||||
version = "0.5"
|
||||
features = ["serde"]
|
||||
|
||||
[dependencies.wgt]
|
||||
path = "../wgpu-types"
|
||||
package = "wgpu-types"
|
||||
version = "0.1"
|
||||
version = "0.5"
|
||||
features = ["serde"]
|
||||
|
||||
[dependencies]
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "wgpu-types"
|
||||
version = "0.1.0"
|
||||
version = "0.5.0"
|
||||
authors = [
|
||||
"Dzmitry Malyshau <kvark@mozilla.com>",
|
||||
"Joshua Groves <josh@joshgroves.com>",
|
||||
|
Loading…
x
Reference in New Issue
Block a user