Move texture-array example over to wgsl (#2618)

This commit is contained in:
Connor Fitzgerald 2022-04-25 14:32:53 -04:00 committed by GitHub
parent bc850d2a0e
commit e54a36ee78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 544 additions and 369 deletions

2
Cargo.lock generated
View File

@ -1032,7 +1032,7 @@ dependencies = [
[[package]]
name = "naga"
version = "0.8.0"
source = "git+https://github.com/gfx-rs/naga?rev=85056524#850565243d1d0d03215f13246c94d63e1d4c51cd"
source = "git+https://github.com/gfx-rs/naga?rev=1aa91549#1aa9154964238af8c692cf521ff90e1f2395e147"
dependencies = [
"bit-set",
"bitflags",

View File

@ -170,9 +170,6 @@ fn deserialize_features(features: &wgpu_types::Features) -> Vec<&'static str> {
) {
return_features.push("uniform-buffer-and-storage-buffer-texture-non-uniform-indexing");
}
if features.contains(wgpu_types::Features::UNSIZED_BINDING_ARRAY) {
return_features.push("unsized-binding-array");
}
if features.contains(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER) {
return_features.push("address-mode-clamp-to-border");
}
@ -341,10 +338,6 @@ impl From<GpuRequiredFeatures> for wgpu_types::Features {
.0
.contains("uniform-buffer-and-storage-buffer-texture-non-uniform-indexing"),
);
features.set(
wgpu_types::Features::UNSIZED_BINDING_ARRAY,
required_features.0.contains("unsized-binding-array"),
);
features.set(
wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER,
required_features.0.contains("address-mode-clamp-to-border"),

View File

@ -1,5 +1,5 @@
(
features: 0x0000_0020_0000_0000,
features: 0x0000_0010_0000_0000,
expectations: [
(
name: "Quad",

View File

@ -40,7 +40,7 @@ thiserror = "1"
[dependencies.naga]
git = "https://github.com/gfx-rs/naga"
rev = "85056524"
rev = "1aa91549"
#version = "0.8"
features = ["span", "validate", "wgsl-in"]

View File

@ -1145,6 +1145,25 @@ impl<A: HalApi> Device<A> {
self.features
.contains(wgt::Features::SHADER_PRIMITIVE_INDEX),
);
caps.set(
Caps::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
self.features.contains(
wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
),
);
caps.set(
Caps::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
self.features.contains(
wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
),
);
// TODO: This needs a proper wgpu feature
caps.set(
Caps::SAMPLER_NON_UNIFORM_INDEXING,
self.features.contains(
wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
),
);
let info = naga::valid::Validator::new(naga::valid::ValidationFlags::all(), caps)
.validate(&module)
.map_err(|inner| {

View File

@ -867,7 +867,14 @@ impl Interface {
Some(ref br) => br.clone(),
_ => continue,
};
let ty = match module.types[var.ty].inner {
let naga_ty = &module.types[var.ty].inner;
let inner_ty = match *naga_ty {
naga::TypeInner::BindingArray { base, .. } => &module.types[base].inner,
ref ty => ty,
};
let ty = match *inner_ty {
naga::TypeInner::Image {
dim,
arrayed,

View File

@ -91,14 +91,14 @@ js-sys = { version = "0.3" }
[dependencies.naga]
git = "https://github.com/gfx-rs/naga"
rev = "85056524"
rev = "1aa91549"
#version = "0.8"
# DEV dependencies
[dev-dependencies.naga]
git = "https://github.com/gfx-rs/naga"
rev = "85056524"
rev = "1aa91549"
#version = "0.8"
features = ["wgsl-in"]

View File

@ -91,6 +91,18 @@ impl super::Adapter {
)
});
let mut shader_model_support: d3d12::D3D12_FEATURE_DATA_SHADER_MODEL =
d3d12::D3D12_FEATURE_DATA_SHADER_MODEL {
HighestShaderModel: d3d12::D3D_SHADER_MODEL_6_0,
};
assert_eq!(0, unsafe {
device.CheckFeatureSupport(
d3d12::D3D12_FEATURE_SHADER_MODEL,
&mut shader_model_support as *mut _ as *mut _,
mem::size_of::<d3d12::D3D12_FEATURE_DATA_SHADER_MODEL>() as _,
)
});
let mut workarounds = super::Workarounds::default();
let info = wgt::AdapterInfo {
@ -175,11 +187,6 @@ impl super::Adapter {
| wgt::Features::DEPTH_CLIP_CONTROL
| wgt::Features::INDIRECT_FIRST_INSTANCE
| wgt::Features::MAPPABLE_PRIMARY_BUFFERS
//TODO: Naga part
//| wgt::Features::TEXTURE_BINDING_ARRAY
//| wgt::Features::BUFFER_BINDING_ARRAY
//| wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY
//| wgt::Features::UNSIZED_BINDING_ARRAY
| wgt::Features::MULTI_DRAW_INDIRECT
| wgt::Features::MULTI_DRAW_INDIRECT_COUNT
| wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER
@ -204,6 +211,13 @@ impl super::Adapter {
!= d3d12::D3D12_CONSERVATIVE_RASTERIZATION_TIER_NOT_SUPPORTED,
);
features.set(
wgt::Features::TEXTURE_BINDING_ARRAY
| wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING
| wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
shader_model_support.HighestShaderModel >= d3d12::D3D_SHADER_MODEL_5_1,
);
let base = wgt::Limits::default();
Some(crate::ExposedAdapter {
@ -282,7 +296,7 @@ impl super::Adapter {
impl crate::Adapter<super::Api> for super::Adapter {
unsafe fn open(
&self,
features: wgt::Features,
_features: wgt::Features,
_limits: &wgt::Limits,
) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
let queue = {
@ -297,13 +311,7 @@ impl crate::Adapter<super::Api> for super::Adapter {
.into_device_result("Queue creation")?
};
let device = super::Device::new(
self.device,
queue,
features,
self.private_caps,
&self.library,
)?;
let device = super::Device::new(self.device, queue, self.private_caps, &self.library)?;
Ok(crate::OpenDevice {
device,
queue: super::Queue {

View File

@ -21,7 +21,6 @@ impl super::Device {
pub(super) fn new(
raw: native::Device,
present_queue: native::CommandQueue,
features: wgt::Features,
private_caps: super::PrivateCapabilities,
library: &Arc<native::D3D12Lib>,
) -> Result<Self, crate::DeviceError> {
@ -87,7 +86,6 @@ impl super::Device {
let capacity_samplers = 2_048;
let shared = super::DeviceShared {
features,
zero_buffer,
cmd_signatures: super::CommandSignatures {
draw: raw
@ -222,13 +220,6 @@ impl super::Device {
compile_flags |=
d3dcompiler::D3DCOMPILE_DEBUG | d3dcompiler::D3DCOMPILE_SKIP_OPTIMIZATION;
}
if self
.shared
.features
.contains(wgt::Features::UNSIZED_BINDING_ARRAY)
{
compile_flags |= d3dcompiler::D3DCOMPILE_ENABLE_UNBOUNDED_DESCRIPTOR_TABLES;
}
let source_name = match stage.module.raw_name {
Some(ref cstr) => cstr.as_c_str().as_ptr(),
@ -691,16 +682,17 @@ impl crate::Device<super::Api> for super::Device {
) -> Result<super::BindGroupLayout, crate::DeviceError> {
let (mut num_buffer_views, mut num_samplers, mut num_texture_views) = (0, 0, 0);
for entry in desc.entries.iter() {
let count = entry.count.map_or(1, NonZeroU32::get);
match entry.ty {
wgt::BindingType::Buffer {
has_dynamic_offset: true,
..
} => {}
wgt::BindingType::Buffer { .. } => num_buffer_views += 1,
wgt::BindingType::Buffer { .. } => num_buffer_views += count,
wgt::BindingType::Texture { .. } | wgt::BindingType::StorageTexture { .. } => {
num_texture_views += 1
num_texture_views += count
}
wgt::BindingType::Sampler { .. } => num_samplers += 1,
wgt::BindingType::Sampler { .. } => num_samplers += count,
}
}
@ -858,7 +850,10 @@ impl crate::Device<super::Api> for super::Device {
group: index as u32,
binding: entry.binding,
},
bt.clone(),
hlsl::BindTarget {
binding_array_size: entry.count.map(NonZeroU32::get),
..bt.clone()
},
);
ranges.push(native::DescriptorRange::new(
range_ty,
@ -866,7 +861,7 @@ impl crate::Device<super::Api> for super::Device {
native_binding(bt),
d3d12::D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND,
));
bt.register += 1;
bt.register += entry.count.map(NonZeroU32::get).unwrap_or(1);
}
if ranges.len() > range_base {
log::debug!(
@ -894,7 +889,10 @@ impl crate::Device<super::Api> for super::Device {
group: index as u32,
binding: entry.binding,
},
bind_sampler.clone(),
hlsl::BindTarget {
binding_array_size: entry.count.map(NonZeroU32::get),
..bind_sampler.clone()
},
);
ranges.push(native::DescriptorRange::new(
range_ty,
@ -902,7 +900,7 @@ impl crate::Device<super::Api> for super::Device {
native_binding(&bind_sampler),
d3d12::D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND,
));
bind_sampler.register += 1;
bind_sampler.register += entry.count.map(NonZeroU32::get).unwrap_or(1);
}
if ranges.len() > range_base {
log::debug!(
@ -953,7 +951,10 @@ impl crate::Device<super::Api> for super::Device {
group: index as u32,
binding: entry.binding,
},
bt.clone(),
hlsl::BindTarget {
binding_array_size: entry.count.map(NonZeroU32::get),
..bt.clone()
},
);
info.dynamic_buffers.push(kind);
@ -969,7 +970,7 @@ impl crate::Device<super::Api> for super::Device {
native_binding(bt),
));
bt.register += 1;
bt.register += entry.count.map_or(1, NonZeroU32::get);
}
bind_group_infos.push(info);
@ -1082,82 +1083,97 @@ impl crate::Device<super::Api> for super::Device {
has_dynamic_offset: true,
..
} => {
let data = &desc.buffers[entry.resource_index as usize];
dynamic_buffers.push(data.resolve_address());
let start = entry.resource_index as usize;
let end = start + entry.count as usize;
for data in &desc.buffers[start..end] {
dynamic_buffers.push(data.resolve_address());
}
}
wgt::BindingType::Buffer { ty, .. } => {
let data = &desc.buffers[entry.resource_index as usize];
let gpu_address = data.resolve_address();
let size = data.resolve_size() as u32;
let inner = cpu_views.as_mut().unwrap();
let cpu_index = inner.stage.len() as u32;
let handle = desc.layout.cpu_heap_views.as_ref().unwrap().at(cpu_index);
match ty {
wgt::BufferBindingType::Uniform => {
let size_mask =
d3d12::D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1;
let raw_desc = d3d12::D3D12_CONSTANT_BUFFER_VIEW_DESC {
BufferLocation: gpu_address,
SizeInBytes: ((size - 1) | size_mask) + 1,
};
self.raw.CreateConstantBufferView(&raw_desc, handle);
}
wgt::BufferBindingType::Storage { read_only: true } => {
let mut raw_desc = d3d12::D3D12_SHADER_RESOURCE_VIEW_DESC {
Format: dxgiformat::DXGI_FORMAT_R32_TYPELESS,
Shader4ComponentMapping:
view::D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING,
ViewDimension: d3d12::D3D12_SRV_DIMENSION_BUFFER,
u: mem::zeroed(),
};
*raw_desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_SRV {
FirstElement: data.offset / 4,
NumElements: size / 4,
StructureByteStride: 0,
Flags: d3d12::D3D12_BUFFER_SRV_FLAG_RAW,
};
self.raw.CreateShaderResourceView(
data.buffer.resource.as_mut_ptr(),
&raw_desc,
handle,
);
}
wgt::BufferBindingType::Storage { read_only: false } => {
let mut raw_desc = d3d12::D3D12_UNORDERED_ACCESS_VIEW_DESC {
Format: dxgiformat::DXGI_FORMAT_R32_TYPELESS,
ViewDimension: d3d12::D3D12_UAV_DIMENSION_BUFFER,
u: mem::zeroed(),
};
*raw_desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_UAV {
FirstElement: data.offset / 4,
NumElements: size / 4,
StructureByteStride: 0,
CounterOffsetInBytes: 0,
Flags: d3d12::D3D12_BUFFER_UAV_FLAG_RAW,
};
self.raw.CreateUnorderedAccessView(
data.buffer.resource.as_mut_ptr(),
ptr::null_mut(),
&raw_desc,
handle,
);
let start = entry.resource_index as usize;
let end = start + entry.count as usize;
for data in &desc.buffers[start..end] {
let gpu_address = data.resolve_address();
let size = data.resolve_size() as u32;
let inner = cpu_views.as_mut().unwrap();
let cpu_index = inner.stage.len() as u32;
let handle = desc.layout.cpu_heap_views.as_ref().unwrap().at(cpu_index);
match ty {
wgt::BufferBindingType::Uniform => {
let size_mask =
d3d12::D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1;
let raw_desc = d3d12::D3D12_CONSTANT_BUFFER_VIEW_DESC {
BufferLocation: gpu_address,
SizeInBytes: ((size - 1) | size_mask) + 1,
};
self.raw.CreateConstantBufferView(&raw_desc, handle);
}
wgt::BufferBindingType::Storage { read_only: true } => {
let mut raw_desc = d3d12::D3D12_SHADER_RESOURCE_VIEW_DESC {
Format: dxgiformat::DXGI_FORMAT_R32_TYPELESS,
Shader4ComponentMapping:
view::D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING,
ViewDimension: d3d12::D3D12_SRV_DIMENSION_BUFFER,
u: mem::zeroed(),
};
*raw_desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_SRV {
FirstElement: data.offset / 4,
NumElements: size / 4,
StructureByteStride: 0,
Flags: d3d12::D3D12_BUFFER_SRV_FLAG_RAW,
};
self.raw.CreateShaderResourceView(
data.buffer.resource.as_mut_ptr(),
&raw_desc,
handle,
);
}
wgt::BufferBindingType::Storage { read_only: false } => {
let mut raw_desc = d3d12::D3D12_UNORDERED_ACCESS_VIEW_DESC {
Format: dxgiformat::DXGI_FORMAT_R32_TYPELESS,
ViewDimension: d3d12::D3D12_UAV_DIMENSION_BUFFER,
u: mem::zeroed(),
};
*raw_desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_UAV {
FirstElement: data.offset / 4,
NumElements: size / 4,
StructureByteStride: 0,
CounterOffsetInBytes: 0,
Flags: d3d12::D3D12_BUFFER_UAV_FLAG_RAW,
};
self.raw.CreateUnorderedAccessView(
data.buffer.resource.as_mut_ptr(),
ptr::null_mut(),
&raw_desc,
handle,
);
}
}
inner.stage.push(handle);
}
inner.stage.push(handle);
}
wgt::BindingType::Texture { .. } => {
let data = &desc.textures[entry.resource_index as usize];
let handle = data.view.handle_srv.unwrap();
cpu_views.as_mut().unwrap().stage.push(handle.raw);
let start = entry.resource_index as usize;
let end = start + entry.count as usize;
for data in &desc.textures[start..end] {
let handle = data.view.handle_srv.unwrap();
cpu_views.as_mut().unwrap().stage.push(handle.raw);
}
}
wgt::BindingType::StorageTexture { .. } => {
let data = &desc.textures[entry.resource_index as usize];
let handle = data.view.handle_uav.unwrap();
cpu_views.as_mut().unwrap().stage.push(handle.raw);
let start = entry.resource_index as usize;
let end = start + entry.count as usize;
for data in &desc.textures[start..end] {
let handle = data.view.handle_uav.unwrap();
cpu_views.as_mut().unwrap().stage.push(handle.raw);
}
}
wgt::BindingType::Sampler { .. } => {
let data = &desc.samplers[entry.resource_index as usize];
cpu_samplers.as_mut().unwrap().stage.push(data.handle.raw);
let start = entry.resource_index as usize;
let end = start + entry.count as usize;
for data in &desc.samplers[start..end] {
cpu_samplers.as_mut().unwrap().stage.push(data.handle.raw);
}
}
}
}

View File

@ -199,7 +199,6 @@ impl CommandSignatures {
}
struct DeviceShared {
features: wgt::Features,
zero_buffer: native::Resource,
cmd_signatures: CommandSignatures,
heap_views: descriptor::GeneralHeap,

View File

@ -1,5 +1,6 @@
use parking_lot::Mutex;
use std::{
num::NonZeroU32,
ptr,
sync::{atomic, Arc},
thread, time,
@ -73,6 +74,13 @@ impl super::Device {
)
.map_err(|e| crate::PipelineError::Linkage(stage_bit, format!("MSL: {:?}", e)))?;
log::debug!(
"Naga generated shader for entry point '{}' and stage {:?}\n{}",
stage.entry_point,
naga_stage,
&source
);
let options = mtl::CompileOptions::new();
options.set_language_version(self.shared.private_caps.msl_version);
@ -558,10 +566,12 @@ impl crate::Device<super::Api> for super::Device {
}
let mut target = naga::back::msl::BindTarget::default();
let count = entry.count.map_or(1, NonZeroU32::get);
target.binding_array_size = entry.count.map(NonZeroU32::get);
match entry.ty {
wgt::BindingType::Buffer { ty, .. } => {
target.buffer = Some(info.counters.buffers as _);
info.counters.buffers += 1;
info.counters.buffers += count;
if let wgt::BufferBindingType::Storage { read_only } = ty {
target.mutable = !read_only;
}
@ -570,15 +580,15 @@ impl crate::Device<super::Api> for super::Device {
target.sampler = Some(naga::back::msl::BindSamplerTarget::Resource(
info.counters.samplers as _,
));
info.counters.samplers += 1;
info.counters.samplers += count;
}
wgt::BindingType::Texture { .. } => {
target.texture = Some(info.counters.textures as _);
info.counters.textures += 1;
info.counters.textures += count;
}
wgt::BindingType::StorageTexture { access, .. } => {
target.texture = Some(info.counters.textures as _);
info.counters.textures += 1;
info.counters.textures += count;
target.mutable = match access {
wgt::StorageTextureAccess::ReadOnly => false,
wgt::StorageTextureAccess::WriteOnly => true,
@ -667,6 +677,8 @@ impl crate::Device<super::Api> for super::Device {
index: naga::proc::BoundsCheckPolicy::ReadZeroSkipWrite,
buffer: naga::proc::BoundsCheckPolicy::ReadZeroSkipWrite,
image: naga::proc::BoundsCheckPolicy::ReadZeroSkipWrite,
// TODO: support bounds checks on binding arrays
binding_array: naga::proc::BoundsCheckPolicy::Unchecked,
},
},
total_push_constants,
@ -689,7 +701,7 @@ impl crate::Device<super::Api> for super::Device {
..
} = layout.ty
{
dynamic_offsets_count += 1;
dynamic_offsets_count += size;
}
if !layout.visibility.contains(stage_bit) {
continue;
@ -700,39 +712,44 @@ impl crate::Device<super::Api> for super::Device {
has_dynamic_offset,
..
} => {
debug_assert_eq!(size, 1);
let source = &desc.buffers[entry.resource_index as usize];
let remaining_size =
wgt::BufferSize::new(source.buffer.size - source.offset);
let binding_size = match ty {
wgt::BufferBindingType::Storage { .. } => {
source.size.or(remaining_size)
}
_ => None,
};
bg.buffers.push(super::BufferResource {
ptr: source.buffer.as_raw(),
offset: source.offset,
dynamic_index: if has_dynamic_offset {
Some(dynamic_offsets_count - 1)
} else {
None
},
binding_size,
binding_location: layout.binding,
});
let start = entry.resource_index as usize;
let end = start + size as usize;
bg.buffers
.extend(desc.buffers[start..end].iter().map(|source| {
let remaining_size =
wgt::BufferSize::new(source.buffer.size - source.offset);
let binding_size = match ty {
wgt::BufferBindingType::Storage { .. } => {
source.size.or(remaining_size)
}
_ => None,
};
super::BufferResource {
ptr: source.buffer.as_raw(),
offset: source.offset,
dynamic_index: if has_dynamic_offset {
Some(dynamic_offsets_count - 1)
} else {
None
},
binding_size,
binding_location: layout.binding,
}
}));
counter.buffers += 1;
}
wgt::BindingType::Sampler { .. } => {
let res = desc.samplers[entry.resource_index as usize].as_raw();
bg.samplers.push(res);
counter.samplers += 1;
let start = entry.resource_index as usize;
let end = start + size as usize;
bg.samplers
.extend(desc.samplers[start..end].iter().map(|samp| samp.as_raw()));
counter.samplers += size;
}
wgt::BindingType::Texture { .. } | wgt::BindingType::StorageTexture { .. } => {
let start = entry.resource_index;
let end = start + size;
let start = entry.resource_index as usize;
let end = start + size as usize;
bg.textures.extend(
desc.textures[start as usize..end as usize]
desc.textures[start..end]
.iter()
.map(|tex| tex.view.as_raw()),
);

View File

@ -3,13 +3,12 @@ use super::conv;
use ash::{extensions::khr, vk};
use parking_lot::Mutex;
use std::{ffi::CStr, sync::Arc};
use std::{collections::BTreeMap, ffi::CStr, sync::Arc};
//TODO: const fn?
fn indexing_features() -> wgt::Features {
wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING
| wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING
| wgt::Features::UNSIZED_BINDING_ARRAY
}
/// Aggregate of the `vk::PhysicalDevice*Features` structs used by `gfx`.
@ -212,9 +211,6 @@ impl PhysicalDeviceFeatures {
uab_types.contains(super::UpdateAfterBindTypes::STORAGE_BUFFER),
)
.descriptor_binding_partially_bound(needs_partially_bound)
.runtime_descriptor_array(
requested_features.contains(wgt::Features::UNSIZED_BINDING_ARRAY),
)
//.sampler_filter_minmax(requested_features.contains(wgt::Features::SAMPLER_REDUCTION))
.imageless_framebuffer(private_caps.imageless_framebuffers)
.timeline_semaphore(private_caps.timeline_semaphores)
@ -253,9 +249,6 @@ impl PhysicalDeviceFeatures {
uab_types.contains(super::UpdateAfterBindTypes::STORAGE_BUFFER),
)
.descriptor_binding_partially_bound(needs_partially_bound)
.runtime_descriptor_array(
requested_features.contains(wgt::Features::UNSIZED_BINDING_ARRAY),
)
.build(),
)
} else {
@ -471,9 +464,6 @@ impl PhysicalDeviceFeatures {
) {
features.insert(F::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING);
}
if vulkan_1_2.runtime_descriptor_array != 0 {
features |= F::UNSIZED_BINDING_ARRAY;
}
if vulkan_1_2.descriptor_binding_partially_bound != 0 && !intel_windows {
features |= F::PARTIALLY_BOUND_BINDING_ARRAY;
}
@ -519,9 +509,6 @@ impl PhysicalDeviceFeatures {
if descriptor_indexing.descriptor_binding_partially_bound != 0 && !intel_windows {
features |= F::PARTIALLY_BOUND_BINDING_ARRAY;
}
if descriptor_indexing.runtime_descriptor_array != 0 {
features |= F::UNSIZED_BINDING_ARRAY;
}
}
if let Some(ref feature) = self.depth_clip_enable {
@ -1183,6 +1170,13 @@ impl super::Adapter {
capabilities.push(spv::Capability::MultiView);
}
if features.intersects(
wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING
| wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
) {
capabilities.push(spv::Capability::ShaderNonUniform);
}
let mut flags = spv::WriterFlags::empty();
flags.set(
spv::WriterFlags::DEBUG,
@ -1215,7 +1209,11 @@ impl super::Adapter {
} else {
naga::proc::BoundsCheckPolicy::Restrict
},
// TODO: support bounds checks on binding arrays
binding_array: naga::proc::BoundsCheckPolicy::Unchecked,
},
// We need to build this separately for each invocation, so just default it out here
binding_map: BTreeMap::default(),
}
};

View File

@ -6,7 +6,12 @@ use inplace_it::inplace_or_alloc_from_iter;
use parking_lot::Mutex;
use std::{
borrow::Cow, collections::hash_map::Entry, ffi::CString, num::NonZeroU32, ptr, sync::Arc,
borrow::Cow,
collections::{hash_map::Entry, BTreeMap},
ffi::CString,
num::NonZeroU32,
ptr,
sync::Arc,
};
impl super::DeviceShared {
@ -628,6 +633,7 @@ impl super::Device {
&self,
stage: &crate::ProgrammableStage<super::Api>,
naga_stage: naga::ShaderStage,
binding_map: &naga::back::spv::BindingMap,
) -> Result<CompiledStage, crate::PipelineError> {
let stage_flags = crate::auxil::map_naga_stage(naga_stage);
let vk_module = match *stage.module {
@ -640,16 +646,21 @@ impl super::Device {
entry_point: stage.entry_point.to_string(),
shader_stage: naga_stage,
};
let temp_options;
let options = if !runtime_checks {
temp_options = naga::back::spv::Options {
bounds_check_policies: naga::proc::BoundsCheckPolicies {
let needs_temp_options = !runtime_checks || !binding_map.is_empty();
let mut temp_options;
let options = if needs_temp_options {
temp_options = self.naga_options.clone();
if !runtime_checks {
temp_options.bounds_check_policies = naga::proc::BoundsCheckPolicies {
index: naga::proc::BoundsCheckPolicy::Unchecked,
buffer: naga::proc::BoundsCheckPolicy::Unchecked,
image: naga::proc::BoundsCheckPolicy::Unchecked,
},
..self.naga_options.clone()
};
binding_array: naga::proc::BoundsCheckPolicy::Unchecked,
};
}
if !binding_map.is_empty() {
temp_options.binding_map = binding_map.clone();
}
&temp_options
} else {
&self.naga_options
@ -1100,6 +1111,13 @@ impl crate::Device<super::Api> for super::Device {
let vk_info = vk::DescriptorSetLayoutCreateInfo::builder().bindings(&vk_bindings);
let binding_arrays = desc
.entries
.iter()
.enumerate()
.filter_map(|(idx, entry)| entry.count.map(|count| (idx as u32, count)))
.collect();
let mut binding_flag_info;
let binding_flag_vec;
let mut requires_update_after_bind = false;
@ -1176,6 +1194,7 @@ impl crate::Device<super::Api> for super::Device {
raw,
desc_count,
types: types.into_boxed_slice(),
binding_arrays,
requires_update_after_bind,
})
}
@ -1220,7 +1239,25 @@ impl crate::Device<super::Api> for super::Device {
.set_object_name(vk::ObjectType::PIPELINE_LAYOUT, raw, label);
}
Ok(super::PipelineLayout { raw })
let mut binding_arrays = BTreeMap::new();
for (group, &layout) in desc.bind_group_layouts.iter().enumerate() {
for &(binding, binding_array_size) in &layout.binding_arrays {
binding_arrays.insert(
naga::ResourceBinding {
group: group as u32,
binding,
},
naga::back::spv::BindingInfo {
binding_array_size: Some(binding_array_size.get()),
},
);
}
}
Ok(super::PipelineLayout {
raw,
binding_arrays,
})
}
unsafe fn destroy_pipeline_layout(&self, pipeline_layout: super::PipelineLayout) {
self.shared
@ -1266,11 +1303,15 @@ impl crate::Device<super::Api> for super::Device {
write = match ty {
vk::DescriptorType::SAMPLER => {
let index = sampler_infos.len();
let binding = desc.samplers[entry.resource_index as usize];
let vk_info = vk::DescriptorImageInfo::builder()
.sampler(binding.raw)
.build();
sampler_infos.push(vk_info);
let start = entry.resource_index;
let end = start + entry.count;
sampler_infos.extend(desc.samplers[start as usize..end as usize].iter().map(
|binding| {
vk::DescriptorImageInfo::builder()
.sampler(binding.raw)
.build()
},
));
write.image_info(&sampler_infos[index..])
}
vk::DescriptorType::SAMPLED_IMAGE | vk::DescriptorType::STORAGE_IMAGE => {
@ -1344,6 +1385,7 @@ impl crate::Device<super::Api> for super::Device {
index: naga::proc::BoundsCheckPolicy::Unchecked,
buffer: naga::proc::BoundsCheckPolicy::Unchecked,
image: naga::proc::BoundsCheckPolicy::Unchecked,
binding_array: naga::proc::BoundsCheckPolicy::Unchecked,
};
}
Cow::Owned(
@ -1425,11 +1467,19 @@ impl crate::Device<super::Api> for super::Device {
.primitive_restart_enable(desc.primitive.strip_index_format.is_some())
.build();
let compiled_vs = self.compile_stage(&desc.vertex_stage, naga::ShaderStage::Vertex)?;
let compiled_vs = self.compile_stage(
&desc.vertex_stage,
naga::ShaderStage::Vertex,
&desc.layout.binding_arrays,
)?;
stages.push(compiled_vs.create_info);
let compiled_fs = match desc.fragment_stage {
Some(ref stage) => {
let compiled = self.compile_stage(stage, naga::ShaderStage::Fragment)?;
let compiled = self.compile_stage(
stage,
naga::ShaderStage::Fragment,
&desc.layout.binding_arrays,
)?;
stages.push(compiled.create_info);
Some(compiled)
}
@ -1604,7 +1654,11 @@ impl crate::Device<super::Api> for super::Device {
&self,
desc: &crate::ComputePipelineDescriptor<super::Api>,
) -> Result<super::ComputePipeline, crate::PipelineError> {
let compiled = self.compile_stage(&desc.stage, naga::ShaderStage::Compute)?;
let compiled = self.compile_stage(
&desc.stage,
naga::ShaderStage::Compute,
&desc.layout.binding_arrays,
)?;
let vk_infos = [{
vk::ComputePipelineCreateInfo::builder()

View File

@ -398,12 +398,15 @@ pub struct BindGroupLayout {
raw: vk::DescriptorSetLayout,
desc_count: gpu_descriptor::DescriptorTotalCount,
types: Box<[(vk::DescriptorType, u32)]>,
/// Map of binding index to size,
binding_arrays: Vec<(u32, NonZeroU32)>,
requires_update_after_bind: bool,
}
#[derive(Debug)]
pub struct PipelineLayout {
raw: vk::PipelineLayout,
binding_arrays: naga::back::spv::BindingMap,
}
#[derive(Debug)]

View File

@ -348,16 +348,6 @@ bitflags::bitflags! {
///
/// This is a native only feature.
const PARTIALLY_BOUND_BINDING_ARRAY = 1 << 22;
/// Allows the user to create unsized uniform arrays of bindings:
///
/// eg. `uniform texture2D textures[]`.
///
/// Supported platforms:
/// - DX12
/// - Vulkan 1.2+ (or VK_EXT_descriptor_indexing)'s runtimeDescriptorArray feature
///
/// This is a native only feature.
const UNSIZED_BINDING_ARRAY = 1 << 23;
/// Allows the user to call [`RenderPass::multi_draw_indirect`] and [`RenderPass::multi_draw_indexed_indirect`].
///
/// Allows multiple indirect calls to be dispatched from a single buffer.
@ -367,7 +357,7 @@ bitflags::bitflags! {
/// - Vulkan
///
/// This is a native only feature.
const MULTI_DRAW_INDIRECT = 1 << 24;
const MULTI_DRAW_INDIRECT = 1 << 23;
/// Allows the user to call [`RenderPass::multi_draw_indirect_count`] and [`RenderPass::multi_draw_indexed_indirect_count`].
///
/// This allows the use of a buffer containing the actual number of draw calls.
@ -377,7 +367,7 @@ bitflags::bitflags! {
/// - Vulkan 1.2+ (or VK_KHR_draw_indirect_count)
///
/// This is a native only feature.
const MULTI_DRAW_INDIRECT_COUNT = 1 << 25;
const MULTI_DRAW_INDIRECT_COUNT = 1 << 24;
/// Allows the use of push constants: small, fast bits of memory that can be updated
/// inside a [`RenderPass`].
///
@ -394,7 +384,7 @@ bitflags::bitflags! {
/// - OpenGL (emulated with uniforms)
///
/// This is a native only feature.
const PUSH_CONSTANTS = 1 << 26;
const PUSH_CONSTANTS = 1 << 25;
/// Allows the use of [`AddressMode::ClampToBorder`] with a border color
/// other than [`SamplerBorderColor::Zero`].
///
@ -406,7 +396,7 @@ bitflags::bitflags! {
/// - OpenGL
///
/// This is a web and native feature.
const ADDRESS_MODE_CLAMP_TO_BORDER = 1 << 27;
const ADDRESS_MODE_CLAMP_TO_BORDER = 1 << 26;
/// Allows the user to set [`PolygonMode::Line`] in [`PrimitiveState::polygon_mode`]
///
/// This allows drawing polygons/triangles as lines (wireframe) instead of filled
@ -417,7 +407,7 @@ bitflags::bitflags! {
/// - Metal
///
/// This is a native only feature.
const POLYGON_MODE_LINE = 1 << 28;
const POLYGON_MODE_LINE = 1 << 27;
/// Allows the user to set [`PolygonMode::Point`] in [`PrimitiveState::polygon_mode`]
///
/// This allows only drawing the vertices of polygons/triangles instead of filled
@ -427,7 +417,7 @@ bitflags::bitflags! {
/// - Vulkan
///
/// This is a native only feature.
const POLYGON_MODE_POINT = 1 << 29;
const POLYGON_MODE_POINT = 1 << 28;
/// Enables ETC family of compressed textures. All ETC textures use 4x4 pixel blocks.
/// ETC2 RGB and RGBA1 are 8 bytes per block. RTC2 RGBA8 and EAC are 16 bytes per block.
///
@ -442,7 +432,7 @@ bitflags::bitflags! {
/// - Mobile (some)
///
/// This is a native-only feature.
const TEXTURE_COMPRESSION_ETC2 = 1 << 30;
const TEXTURE_COMPRESSION_ETC2 = 1 << 29;
/// Enables ASTC family of compressed textures. ASTC textures use pixel blocks varying from 4x4 to 12x12.
/// Blocks are always 16 bytes.
///
@ -457,7 +447,7 @@ bitflags::bitflags! {
/// - Mobile (some)
///
/// This is a native-only feature.
const TEXTURE_COMPRESSION_ASTC_LDR = 1 << 31;
const TEXTURE_COMPRESSION_ASTC_LDR = 1 << 30;
/// Enables device specific texture format features.
///
/// See `TextureFormatFeatures` for a listing of the features in question.
@ -469,7 +459,7 @@ bitflags::bitflags! {
/// This extension does not enable additional formats.
///
/// This is a native-only feature.
const TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES = 1 << 32;
const TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES = 1 << 31;
/// Enables 64-bit floating point types in SPIR-V shaders.
///
/// Note: even when supported by GPU hardware, 64-bit floating point operations are
@ -479,7 +469,7 @@ bitflags::bitflags! {
/// - Vulkan
///
/// This is a native-only feature.
const SHADER_FLOAT64 = 1 << 33;
const SHADER_FLOAT64 = 1 << 32;
/// Enables using 64-bit types for vertex attributes.
///
/// Requires SHADER_FLOAT64.
@ -487,7 +477,7 @@ bitflags::bitflags! {
/// Supported Platforms: N/A
///
/// This is a native-only feature.
const VERTEX_ATTRIBUTE_64BIT = 1 << 34;
const VERTEX_ATTRIBUTE_64BIT = 1 << 33;
/// Allows the user to set a overestimation-conservative-rasterization in [`PrimitiveState::conservative`]
///
/// Processing of degenerate triangles/lines is hardware specific.
@ -497,7 +487,7 @@ bitflags::bitflags! {
/// - Vulkan
///
/// This is a native only feature.
const CONSERVATIVE_RASTERIZATION = 1 << 35;
const CONSERVATIVE_RASTERIZATION = 1 << 34;
/// Enables bindings of writable storage buffers and textures visible to vertex shaders.
///
/// Note: some (tiled-based) platforms do not support vertex shaders with any side-effects.
@ -506,14 +496,14 @@ bitflags::bitflags! {
/// - All
///
/// This is a native-only feature.
const VERTEX_WRITABLE_STORAGE = 1 << 36;
const VERTEX_WRITABLE_STORAGE = 1 << 35;
/// Enables clear to zero for textures.
///
/// Supported platforms:
/// - All
///
/// This is a native only feature.
const CLEAR_TEXTURE = 1 << 37;
const CLEAR_TEXTURE = 1 << 36;
/// Enables creating shader modules from SPIR-V binary data (unsafe).
///
/// SPIR-V data is not parsed or interpreted in any way; you can use
@ -525,7 +515,7 @@ bitflags::bitflags! {
/// Vulkan implementation.
///
/// This is a native only feature.
const SPIRV_SHADER_PASSTHROUGH = 1 << 38;
const SPIRV_SHADER_PASSTHROUGH = 1 << 37;
/// Enables `builtin(primitive_index)` in fragment shaders.
///
/// Note: enables geometry processing for pipelines using the builtin.
@ -536,14 +526,14 @@ bitflags::bitflags! {
/// - Vulkan
///
/// This is a native only feature.
const SHADER_PRIMITIVE_INDEX = 1 << 39;
const SHADER_PRIMITIVE_INDEX = 1 << 38;
/// Enables multiview render passes and `builtin(view_index)` in vertex shaders.
///
/// Supported platforms:
/// - Vulkan
///
/// This is a native only feature.
const MULTIVIEW = 1 << 40;
const MULTIVIEW = 1 << 39;
/// Enables normalized `16-bit` texture formats.
///
/// Supported platforms:
@ -552,7 +542,7 @@ bitflags::bitflags! {
/// - Metal
///
/// This is a native only feature.
const TEXTURE_FORMAT_16BIT_NORM = 1 << 41;
const TEXTURE_FORMAT_16BIT_NORM = 1 << 40;
/// Allows the use of [`AddressMode::ClampToBorder`] with a border color
/// of [`SamplerBorderColor::Zero`].
///
@ -564,12 +554,12 @@ bitflags::bitflags! {
/// - OpenGL
///
/// This is a native only feature.
const ADDRESS_MODE_CLAMP_TO_ZERO = 1 << 42;
const ADDRESS_MODE_CLAMP_TO_ZERO = 1 << 41;
/// Supported Platforms:
/// - Metal
///
/// This is a native-only feature.
const TEXTURE_COMPRESSION_ASTC_HDR = 1 << 43;
const TEXTURE_COMPRESSION_ASTC_HDR = 1 << 42;
}
}

View File

@ -69,7 +69,6 @@ test = true
[[example]]
name="texture-arrays"
required-features = ["spirv"]
test = true
[[example]]
@ -139,20 +138,20 @@ env_logger = "0.9"
[dependencies.naga]
git = "https://github.com/gfx-rs/naga"
rev = "85056524"
rev = "1aa91549"
#version = "0.8"
optional = true
# used to test all the example shaders
[dev-dependencies.naga]
git = "https://github.com/gfx-rs/naga"
rev = "85056524"
rev = "1aa91549"
#version = "0.8"
features = ["wgsl-in"]
[target.'cfg(target_arch = "wasm32")'.dependencies.naga]
git = "https://github.com/gfx-rs/naga"
rev = "85056524"
rev = "1aa91549"
#version = "0.8"
features = ["wgsl-out"]

View File

@ -21,7 +21,6 @@ All framework-based examples render to the window and are reftested against the
| implicit layout | | | | :star: | | | | | | |
| sampled color textures | :star: | :star: | :star: | :star: | | | :star: | :star: | :star: | :star: |
| storage textures | :star: | | | | | | | | | |
| binding array | | | | | | | | :star: | | |
| comparison samplers | | | | | | :star: | | | | |
| subresource views | | | | :star: | | :star: | | | | |
| cubemaps | | | | | | | :star: | | | |
@ -36,9 +35,9 @@ All framework-based examples render to the window and are reftested against the
| compute passes | :star: | | | | | | | | | |
| error scopes | | | :star: | | | | | | | |
| *optional extensions* | | | | | | | | :star: | | |
| - SPIR-V shaders | | | | | | | | :star: | | |
| - binding indexing | | | | | | | | :star: | | |
| - push constants | | | | | | | | :star: | | |
| - SPIR-V shaders | | | | | | | | | | |
| - binding array | | | | | | | | :star: | | |
| - push constants | | | | | | | | | | |
| - depth clamping | | | | | | :star: | | | | |
| - compressed textures | | | | | | | :star: | | | |
| - polygon mode | | | :star: | | | | | | | |

View File

@ -1,19 +0,0 @@
#version 450
layout(location = 0) in vec2 v_TexCoord;
layout(location = 1) flat in int v_Index; // dynamically non-uniform
layout(location = 0) out vec4 o_Color;
layout(set = 0, binding = 0) uniform texture2D u_Textures[2];
layout(set = 0, binding = 1) uniform sampler u_Sampler[2];
void main() {
if (v_Index == 0) {
o_Color = vec4(texture(sampler2D(u_Textures[0], u_Sampler[0]), v_TexCoord).rgb, 1.0);
} else if (v_Index == 1) {
o_Color = vec4(texture(sampler2D(u_Textures[1], u_Sampler[1]), v_TexCoord).rgb, 1.0);
} else {
// We need to write something to output color
o_Color = vec4(0.0, 0.0, 1.0, 0.0);
}
}

View File

@ -0,0 +1,61 @@
struct VertexInput {
@location(0) position: vec2<f32>,
@location(1) tex_coord: vec2<f32>,
@location(2) index: i32,
}
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) tex_coord: vec2<f32>,
@location(1) index: i32,
}
@vertex
fn vert_main(vertex: VertexInput) -> VertexOutput {
var outval: VertexOutput;
outval.position = vec4<f32>(vertex.position.x, vertex.position.y, 0.0, 1.0);
outval.tex_coord = vertex.tex_coord;
outval.index = vertex.index;
return outval;
}
struct FragmentInput {
@location(0) tex_coord: vec2<f32>,
@location(1) index: i32,
}
@group(0) @binding(0)
var texture_array_top: binding_array<texture_2d<f32>>;
@group(0) @binding(1)
var texture_array_bottom: binding_array<texture_2d<f32>>;
@group(0) @binding(2)
var sampler_array: binding_array<sampler>;
struct Uniforms {
index: u32,
}
@group(0) @binding(3)
var<uniform> uniforms: Uniforms;
@fragment
fn uniform_main(fragment: FragmentInput) -> @location(0) vec4<f32> {
var outval: vec3<f32>;
if fragment.tex_coord.y <= 0.5 {
outval = textureSampleLevel(
texture_array_top[uniforms.index],
sampler_array[uniforms.index],
fragment.tex_coord,
0.0
).rgb;
} else {
outval = textureSampleLevel(
texture_array_bottom[uniforms.index],
sampler_array[uniforms.index],
fragment.tex_coord,
0.0
).rgb;
}
return vec4<f32>(outval.x, outval.y, outval.z, 1.0);
}

View File

@ -2,7 +2,7 @@
mod framework;
use bytemuck::{Pod, Zeroable};
use std::num::NonZeroU32;
use std::num::{NonZeroU32, NonZeroU64};
use wgpu::util::DeviceExt;
#[repr(C)]
@ -51,12 +51,16 @@ fn create_indices() -> Vec<u16> {
enum Color {
Red,
Green,
Blue,
White,
}
fn create_texture_data(color: Color) -> [u8; 4] {
match color {
Color::Red => [255, 0, 0, 255],
Color::Green => [0, 255, 0, 255],
Color::Blue => [0, 0, 255, 255],
Color::White => [255, 255, 255, 255],
}
}
@ -71,18 +75,10 @@ struct Example {
impl framework::Example for Example {
fn optional_features() -> wgpu::Features {
wgpu::Features::UNSIZED_BINDING_ARRAY
| wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING
| wgpu::Features::PUSH_CONSTANTS
wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING
}
fn required_features() -> wgpu::Features {
wgpu::Features::TEXTURE_BINDING_ARRAY | wgpu::Features::SPIRV_SHADER_PASSTHROUGH
}
fn required_limits() -> wgpu::Limits {
wgpu::Limits {
max_push_constant_size: 4,
..wgpu::Limits::default()
}
wgpu::Features::TEXTURE_BINDING_ARRAY
}
fn init(
config: &wgpu::SurfaceConfiguration,
@ -91,24 +87,45 @@ impl framework::Example for Example {
queue: &wgpu::Queue,
) -> Self {
let mut uniform_workaround = false;
let vs_module = device.create_shader_module(&wgpu::include_spirv!("shader.vert.spv"));
let fs_source = match device.features() {
f if f.contains(wgpu::Features::UNSIZED_BINDING_ARRAY) => {
wgpu::include_spirv_raw!("unsized-non-uniform.frag.spv")
}
f if f.contains(
wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
) =>
{
wgpu::include_spirv_raw!("non-uniform.frag.spv")
}
f if f.contains(wgpu::Features::TEXTURE_BINDING_ARRAY) => {
uniform_workaround = true;
wgpu::include_spirv_raw!("uniform.frag.spv")
}
_ => unreachable!(),
let base_shader_module = device.create_shader_module(&wgpu::include_wgsl!("indexing.wgsl"));
let env_override = match std::env::var("WGPU_TEXTURE_ARRAY_STYLE") {
Ok(value) => match &*value.to_lowercase() {
"nonuniform" | "non_uniform" => Some(true),
"uniform" => Some(false),
_ => None,
},
Err(_) => None,
};
let fs_module = unsafe { device.create_shader_module_spirv(&fs_source) };
let fragment_entry_point = match (device.features(), env_override) {
(_, Some(false)) => {
uniform_workaround = true;
"uniform_main"
}
(_, Some(true)) => "non_uniform_main",
(f, _)
if f.contains(
wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
) =>
{
"non_uniform_main"
}
_ => {
uniform_workaround = true;
"uniform_main"
}
};
let non_uniform_shader_module;
// TODO: Because naga's capibilities are evaluated on validate, not on write, we cannot make a shader module with unsupported
// capabilities even if we don't use it. So for now put it in a separate module.
let fragment_shader_module = if !uniform_workaround {
non_uniform_shader_module =
device.create_shader_module(&wgpu::include_wgsl!("non_uniform_indexing.wgsl"));
&non_uniform_shader_module
} else {
&base_shader_module
};
println!("Using fragment entry point '{}'", fragment_entry_point);
let vertex_size = std::mem::size_of::<Vertex>();
let vertex_data = create_vertices();
@ -125,8 +142,19 @@ impl framework::Example for Example {
usage: wgpu::BufferUsages::INDEX,
});
let mut texture_index_buffer_contents = vec![0u32; 128];
texture_index_buffer_contents[0] = 0;
texture_index_buffer_contents[64] = 1;
let texture_index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"),
contents: bytemuck::cast_slice(&texture_index_buffer_contents),
usage: wgpu::BufferUsages::UNIFORM,
});
let red_texture_data = create_texture_data(Color::Red);
let green_texture_data = create_texture_data(Color::Green);
let blue_texture_data = create_texture_data(Color::Blue);
let white_texture_data = create_texture_data(Color::White);
let texture_descriptor = wgpu::TextureDescriptor {
size: wgpu::Extent3d::default(),
@ -145,9 +173,19 @@ impl framework::Example for Example {
label: Some("green"),
..texture_descriptor
});
let blue_texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("blue"),
..texture_descriptor
});
let white_texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("white"),
..texture_descriptor
});
let red_texture_view = red_texture.create_view(&wgpu::TextureViewDescriptor::default());
let green_texture_view = green_texture.create_view(&wgpu::TextureViewDescriptor::default());
let blue_texture_view = blue_texture.create_view(&wgpu::TextureViewDescriptor::default());
let white_texture_view = white_texture.create_view(&wgpu::TextureViewDescriptor::default());
queue.write_texture(
red_texture.as_image_copy(),
@ -169,6 +207,26 @@ impl framework::Example for Example {
},
wgpu::Extent3d::default(),
);
queue.write_texture(
blue_texture.as_image_copy(),
&blue_texture_data,
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(NonZeroU32::new(4).unwrap()),
rows_per_image: None,
},
wgpu::Extent3d::default(),
);
queue.write_texture(
white_texture.as_image_copy(),
&white_texture_data,
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(NonZeroU32::new(4).unwrap()),
rows_per_image: None,
},
wgpu::Extent3d::default(),
);
let sampler = device.create_sampler(&wgpu::SamplerDescriptor::default());
@ -188,9 +246,29 @@ impl framework::Example for Example {
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: NonZeroU32::new(2),
},
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: NonZeroU32::new(2),
},
wgpu::BindGroupLayoutEntry {
binding: 3,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: Some(NonZeroU64::new(4).unwrap()),
},
count: None,
},
],
});
@ -205,8 +283,23 @@ impl framework::Example for Example {
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::TextureViewArray(&[
&blue_texture_view,
&white_texture_view,
]),
},
wgpu::BindGroupEntry {
binding: 2,
resource: wgpu::BindingResource::SamplerArray(&[&sampler, &sampler]),
},
wgpu::BindGroupEntry {
binding: 3,
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
buffer: &texture_index_buffer,
offset: 0,
size: Some(NonZeroU64::new(4).unwrap()),
}),
},
],
layout: &bind_group_layout,
label: Some("bind group"),
@ -215,14 +308,7 @@ impl framework::Example for Example {
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("main"),
bind_group_layouts: &[&bind_group_layout],
push_constant_ranges: if uniform_workaround {
&[wgpu::PushConstantRange {
stages: wgpu::ShaderStages::FRAGMENT,
range: 0..4,
}]
} else {
&[]
},
push_constant_ranges: &[],
});
let index_format = wgpu::IndexFormat::Uint16;
@ -231,8 +317,8 @@ impl framework::Example for Example {
label: None,
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &vs_module,
entry_point: "main",
module: &base_shader_module,
entry_point: "vert_main",
buffers: &[wgpu::VertexBufferLayout {
array_stride: vertex_size as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
@ -240,8 +326,8 @@ impl framework::Example for Example {
}],
},
fragment: Some(wgpu::FragmentState {
module: &fs_module,
entry_point: "main",
module: fragment_shader_module,
entry_point: fragment_entry_point,
targets: &[config.format.into()],
}),
primitive: wgpu::PrimitiveState {
@ -298,15 +384,15 @@ impl framework::Example for Example {
});
rpass.set_pipeline(&self.pipeline);
rpass.set_bind_group(0, &self.bind_group, &[]);
rpass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
rpass.set_index_buffer(self.index_buffer.slice(..), self.index_format);
if self.uniform_workaround {
rpass.set_push_constants(wgpu::ShaderStages::FRAGMENT, 0, bytemuck::cast_slice(&[0]));
rpass.set_bind_group(0, &self.bind_group, &[0]);
rpass.draw_indexed(0..6, 0, 0..1);
rpass.set_push_constants(wgpu::ShaderStages::FRAGMENT, 0, bytemuck::cast_slice(&[1]));
rpass.set_bind_group(0, &self.bind_group, &[256]);
rpass.draw_indexed(6..12, 0, 0..1);
} else {
rpass.set_bind_group(0, &self.bind_group, &[0]);
rpass.draw_indexed(0..12, 0, 0..1);
}
@ -320,60 +406,28 @@ fn main() {
framework::run::<Example>("texture-arrays");
}
// This fails due to an issue with naga https://github.com/gfx-rs/wgpu/issues/1532
#[test]
fn texture_arrays_constant() {
framework::test::<Example>(framework::FrameworkRefTest {
image_path: "/examples/texture-arrays/screenshot.png",
width: 1024,
height: 768,
optional_features: wgpu::Features::default(),
base_test_parameters: framework::test_common::TestParameters::default().failure(),
tolerance: 0,
max_outliers: 0,
});
}
// This fails due to an issue with naga https://github.com/gfx-rs/wgpu/issues/1532
#[test]
fn texture_arrays_uniform() {
framework::test::<Example>(framework::FrameworkRefTest {
image_path: "/examples/texture-arrays/screenshot.png",
width: 1024,
height: 768,
optional_features: wgpu::Features::TEXTURE_BINDING_ARRAY | wgpu::Features::PUSH_CONSTANTS,
base_test_parameters: framework::test_common::TestParameters::default().failure(),
optional_features: wgpu::Features::empty(),
base_test_parameters: framework::test_common::TestParameters::default(),
tolerance: 0,
max_outliers: 0,
});
}
// This fails due to an issue with naga https://github.com/gfx-rs/wgpu/issues/1532
#[test]
fn texture_arrays_non_uniform() {
framework::test::<Example>(framework::FrameworkRefTest {
image_path: "/examples/texture-arrays/screenshot.png",
width: 1024,
height: 768,
optional_features: wgpu::Features::TEXTURE_BINDING_ARRAY
| wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
base_test_parameters: framework::test_common::TestParameters::default().failure(),
tolerance: 0,
max_outliers: 0,
});
}
// This fails due to an issue with naga https://github.com/gfx-rs/wgpu/issues/1532
#[test]
fn texture_arrays_unsized_non_uniform() {
framework::test::<Example>(framework::FrameworkRefTest {
image_path: "/examples/texture-arrays/screenshot.png",
width: 1024,
height: 768,
optional_features: wgpu::Features::TEXTURE_BINDING_ARRAY
| wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING
| wgpu::Features::UNSIZED_BINDING_ARRAY,
base_test_parameters: framework::test_common::TestParameters::default().failure(),
optional_features:
wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
base_test_parameters: framework::test_common::TestParameters::default(),
tolerance: 0,
max_outliers: 0,
});

View File

@ -1,14 +0,0 @@
#version 450
#extension GL_EXT_nonuniform_qualifier : require
layout(location = 0) in vec2 v_TexCoord;
layout(location = 1) nonuniformEXT flat in int v_Index; // dynamically non-uniform
layout(location = 0) out vec4 o_Color;
layout(set = 0, binding = 0) uniform texture2D u_Textures[2];
layout(set = 0, binding = 1) uniform sampler u_Sampler[2];
void main() {
o_Color = vec4(texture(sampler2D(u_Textures[v_Index], u_Sampler[v_Index]), v_TexCoord).rgb, 1.0);
}

View File

@ -0,0 +1,33 @@
struct FragmentInput {
@location(0) tex_coord: vec2<f32>,
@location(1) index: i32,
}
@group(0) @binding(0)
var texture_array_top: binding_array<texture_2d<f32>>;
@group(0) @binding(1)
var texture_array_bottom: binding_array<texture_2d<f32>>;
@group(0) @binding(2)
var sampler_array: binding_array<sampler>;
@fragment
fn non_uniform_main(fragment: FragmentInput) -> @location(0) vec4<f32> {
var outval: vec3<f32>;
if fragment.tex_coord.y <= 0.5 {
outval = textureSampleLevel(
texture_array_top[fragment.index],
sampler_array[fragment.index],
fragment.tex_coord,
0.0
).rgb;
} else {
outval = textureSampleLevel(
texture_array_bottom[fragment.index],
sampler_array[fragment.index],
fragment.tex_coord,
0.0
).rgb;
}
return vec4<f32>(outval.x, outval.y, outval.z, 1.0);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -1,13 +0,0 @@
#version 450
layout(location = 0) in vec2 a_Pos;
layout(location = 1) in vec2 a_TexCoord;
layout(location = 2) in int a_Index;
layout(location = 0) out vec2 v_TexCoord;
layout(location = 1) flat out int v_Index;
void main() {
v_TexCoord = a_TexCoord;
v_Index = a_Index;
gl_Position = vec4(a_Pos, 0.0, 1.0);
}

View File

@ -1,15 +0,0 @@
#version 450
layout(location = 0) in vec2 v_TexCoord;
layout(location = 1) flat in int v_Index; // dynamically non-uniform
layout(location = 0) out vec4 o_Color;
layout(set = 0, binding = 0) uniform texture2D u_Textures[2];
layout(set = 0, binding = 1) uniform sampler u_Sampler[2];
layout(push_constant) uniform Uniforms {
int u_Index; // dynamically uniform
};
void main() {
o_Color = vec4(texture(sampler2D(u_Textures[u_Index], u_Sampler[u_Index]), v_TexCoord).rgb, 1.0);
}

View File

@ -1,14 +0,0 @@
#version 450
#extension GL_EXT_nonuniform_qualifier : require
layout(location = 0) in vec2 v_TexCoord;
layout(location = 1) nonuniformEXT flat in int v_Index; // dynamically non-uniform
layout(location = 0) out vec4 o_Color;
layout(set = 0, binding = 0) uniform texture2D u_Textures[];
layout(set = 0, binding = 1) uniform sampler u_Sampler[];
void main() {
o_Color = vec4(texture(sampler2D(u_Textures[v_Index], u_Sampler[v_Index]), v_TexCoord).rgb, 1.0);
}

View File

@ -1329,8 +1329,8 @@ impl crate::Context for Context {
));
if let Some(cause) = error {
if let wgc::pipeline::CreateRenderPipelineError::Internal { stage, ref error } = cause {
log::warn!("Shader translation error for stage {:?}: {}", stage, error);
log::warn!("Please report it to https://github.com/gfx-rs/naga");
log::error!("Shader translation error for stage {:?}: {}", stage, error);
log::error!("Please report it to https://github.com/gfx-rs/naga");
}
self.handle_error(
&device.error_sink,