Skip to content
This repository was archived by the owner on Jun 18, 2021. It is now read-only.

Implement SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING #369

Merged
merged 1 commit into from
Jun 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ vulkan = ["wgc/gfx-backend-vulkan"]
package = "wgpu-core"
version = "0.5"
git = "https://github.com/gfx-rs/wgpu"
rev = "ad34c37127c8361bc2b07e8775816b2bbf8c0a73"
rev = "2dd3439475de6ea59488a91ea7d66096d3aecbbf"
features = ["raw-window-handle"]

[dependencies.wgt]
package = "wgpu-types"
version = "0.5"
git = "https://github.com/gfx-rs/wgpu"
rev = "ad34c37127c8361bc2b07e8775816b2bbf8c0a73"
rev = "2dd3439475de6ea59488a91ea7d66096d3aecbbf"

[dependencies]
arrayvec = "0.5"
Expand Down Expand Up @@ -73,7 +73,7 @@ test = true
#gfx-backend-vulkan = { version = "0.5.0", path = "../gfx/src/backend/vulkan" }
#gfx-backend-dx12 = { version = "0.5.0", path = "../gfx/src/backend/dx12" }
#gfx-backend-dx11 = { version = "0.5.0", path = "../gfx/src/backend/dx11" }
#gfx-backend-metal = { version = "0.5.0", path = "../gfx/src/backend/dx11" }
#gfx-backend-metal = { version = "0.5.0", path = "../gfx/src/backend/metal" }
#gfx-descriptor = { version = "0.1.0", path = "../gfx-extras/gfx-descriptor" }
#gfx-memory = { version = "0.1.0", path = "../gfx-extras/gfx-memory" }

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#version 450

layout(location = 0) in vec2 v_TexCoord;
layout(location = 1) flat in int v_Index;
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];
Expand All @@ -13,6 +13,7 @@ void main() {
} else if (v_Index == 1) {
o_Color = vec4(texture(sampler2D(u_Textures[1], u_Sampler), v_TexCoord).rgb, 1.0);
} else {
o_Color = vec4(0.0, 0.0, 1.0, 1.0);
// We need to write something to output color
o_Color = vec4(0.0, 0.0, 1.0, 0.0);
}
}
Binary file not shown.
125 changes: 107 additions & 18 deletions examples/texture-arrays/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,21 @@ struct Vertex {
unsafe impl Pod for Vertex {}
unsafe impl Zeroable for Vertex {}

#[repr(C)]
#[derive(Clone, Copy)]
struct Uniform {
index: u32,
}

unsafe impl Pod for Uniform {}
unsafe impl Zeroable for Uniform {}

struct UniformWorkaroundData {
bind_group_layout: wgpu::BindGroupLayout,
bind_group0: wgpu::BindGroup,
bind_group1: wgpu::BindGroup,
}

fn vertex(pos: [i8; 2], tc: [i8; 2], index: i8) -> Vertex {
Vertex {
_pos: [pos[0] as f32, pos[1] as f32],
Expand Down Expand Up @@ -66,12 +81,13 @@ struct Example {
bind_group: wgpu::BindGroup,
vertex_buffer: wgpu::Buffer,
index_buffer: wgpu::Buffer,
uniform_workaround_data: Option<UniformWorkaroundData>,
}

impl framework::Example for Example {
fn needed_extensions() -> (wgpu::Extensions, wgpu::UnsafeExtensions) {
(
wgpu::Extensions::SAMPLED_TEXTURE_BINDING_ARRAY,
wgpu::Extensions::BINDING_INDEXING,
wgpu::UnsafeExtensions::disallow(),
)
}
Expand All @@ -80,12 +96,30 @@ impl framework::Example for Example {
device: &wgpu::Device,
queue: &wgpu::Queue,
) -> (Self, Option<wgpu::CommandBuffer>) {
let device_extensions = device.extensions();

assert!(
device_extensions.contains(wgpu::Extensions::SAMPLED_TEXTURE_BINDING_ARRAY),
"Graphics Device does not support SAMPLED_TEXTURE_BINDING_ARRAY extension"
);
let mut uniform_workaround = false;
let vs_bytes: &[u8] = include_bytes!("shader.vert.spv");
let fs_bytes: &[u8] = match device.capabilities() {
c if c.contains(wgpu::Capabilities::UNSIZED_BINDING_ARRAY) => {
include_bytes!("unsized-non-uniform.frag.spv")
}
c if c.contains(wgpu::Capabilities::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING) => {
include_bytes!("non-uniform.frag.spv")
}
c if c.contains(wgpu::Capabilities::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING) => {
uniform_workaround = true;
include_bytes!("uniform.frag.spv")
}
c if c.contains(wgpu::Capabilities::SAMPLED_TEXTURE_BINDING_ARRAY) => {
include_bytes!("constant.frag.spv")
}
_ => {
panic!("Graphics adapter does not support any of the capabilities needed for this example");
}
};
let vs_module =
device.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(vs_bytes)).unwrap());
let fs_module =
device.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(fs_bytes)).unwrap());

let vertex_size = std::mem::size_of::<Vertex>();
let vertex_data = create_vertices();
Expand All @@ -98,6 +132,54 @@ impl framework::Example for Example {
let index_buffer = device
.create_buffer_with_data(bytemuck::cast_slice(&index_data), wgpu::BufferUsage::INDEX);

let uniform_workaround_data = if uniform_workaround {
let buffer0 = device.create_buffer_with_data(
&bytemuck::cast_slice(&[Uniform { index: 0 }]),
wgpu::BufferUsage::UNIFORM,
);
let buffer1 = device.create_buffer_with_data(
&bytemuck::cast_slice(&[Uniform { index: 1 }]),
wgpu::BufferUsage::UNIFORM,
);

let bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
bindings: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::UniformBuffer { dynamic: false },
..wgpu::BindGroupLayoutEntry::default()
}],
label: Some("uniform workaround bind group layout"),
});

let bind_group0 = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &bind_group_layout,
bindings: &[wgpu::Binding {
binding: 0,
resource: wgpu::BindingResource::Buffer(buffer0.slice(..)),
}],
label: Some("uniform workaround bind group 0"),
});

let bind_group1 = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &bind_group_layout,
bindings: &[wgpu::Binding {
binding: 0,
resource: wgpu::BindingResource::Buffer(buffer1.slice(..)),
}],
label: Some("uniform workaround bind group 1"),
});

Some(UniformWorkaroundData {
bind_group_layout,
bind_group0,
bind_group1,
})
} else {
None
};

let red_texture_data = create_texture_data(Color::RED);
let green_texture_data = create_texture_data(Color::GREEN);

Expand Down Expand Up @@ -206,16 +288,15 @@ impl framework::Example for Example {
label: Some("bind group"),
});

let vs_bytes = include_bytes!("shader.vert.spv");
let fs_bytes = include_bytes!("shader.frag.spv");
let vs_module = device
.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&vs_bytes[..])).unwrap());
let fs_module = device
.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&fs_bytes[..])).unwrap());

let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[&bind_group_layout],
});
let pipeline_layout = if let Some(ref workaround) = uniform_workaround_data {
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[&bind_group_layout, &workaround.bind_group_layout],
})
} else {
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[&bind_group_layout],
})
};

let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
layout: &pipeline_layout,
Expand Down Expand Up @@ -261,6 +342,7 @@ impl framework::Example for Example {
index_buffer,
bind_group,
pipeline,
uniform_workaround_data,
},
None,
)
Expand Down Expand Up @@ -305,7 +387,14 @@ impl framework::Example for Example {
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(..));
rpass.draw_indexed(0..12, 0, 0..1);
if let Some(ref workaround) = self.uniform_workaround_data {
rpass.set_bind_group(1, &workaround.bind_group0, &[]);
rpass.draw_indexed(0..6, 0, 0..1);
rpass.set_bind_group(1, &workaround.bind_group1, &[]);
rpass.draw_indexed(6..12, 0, 0..1);
} else {
rpass.draw_indexed(0..12, 0, 0..1);
}

drop(rpass);

Expand Down
14 changes: 14 additions & 0 deletions examples/texture-arrays/non-uniform.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#version 450

#extension GL_EXT_nonuniform_qualifier : require

layout(location = 0) in vec2 v_TexCoord;
layout(location = 1) flat in int v_Index; // dynamically non-uniform
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually uniform across the wave, since it's flat-interpolated. Is it supposed to be non-uniform in a difference sense?

Copy link
Member Author

@cwfitzgerald cwfitzgerald Jun 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you consider how gpus run fragment shaders, it may not be even coherent in the wave. They take 2x2 blocks of pixels, put N of them together into a wave. These blocks may not be from the same primative, so this may be different across the wave. This 2x2 action is also why gpus are bad at rendering small polygons, they always get a 2x2 section, even if they are only 1x1. (edit: src also this comment which is really helpful)

Maybe confusion between uniform (same over the draw call) and coherent (same over the wave)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that 2x2 blocks are always from the same primitive. If some of these pixels lie outside, they become "ghost" threads, but regardless - flat is there precisely to inform the compiler that this is constant across the wave.

The link you provided is very familiar to me, although I'll make sure to rehash that part in my mind.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pixels from within the 2x2 blocks are always from the same primitive, being masked out and such as needed, but when 8 of those are combined into a wave, each of those 8 blocks could be from a different primitive. If that wasn't allowed, having a 1x1 triangle would waste an entire wave of processing power, not just a 2x2 block.

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;

void main() {
o_Color = vec4(texture(sampler2D(u_Textures[v_Index], u_Sampler), v_TexCoord).rgb, 1.0);
}
Binary file added examples/texture-arrays/non-uniform.frag.spv
Binary file not shown.
15 changes: 15 additions & 0 deletions examples/texture-arrays/uniform.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#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;
layout(set = 1, binding = 0) uniform Uniforms {
int u_Index; // dynamically uniform
};

void main() {
o_Color = vec4(texture(sampler2D(u_Textures[u_Index], u_Sampler), v_TexCoord).rgb, 1.0);
}
Binary file added examples/texture-arrays/uniform.frag.spv
Binary file not shown.
14 changes: 14 additions & 0 deletions examples/texture-arrays/unsized-non-uniform.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#version 450

#extension GL_EXT_nonuniform_qualifier : require

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[];
layout(set = 0, binding = 1) uniform sampler u_Sampler;

void main() {
o_Color = vec4(texture(sampler2D(u_Textures[v_Index], u_Sampler), v_TexCoord).rgb, 1.0);
}
Binary file added examples/texture-arrays/unsized-non-uniform.frag.spv
Binary file not shown.
14 changes: 11 additions & 3 deletions src/backend/direct.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::{
backend::native_gpu_future, BindGroupDescriptor, BindGroupLayoutDescriptor, BindingResource,
BufferDescriptor, CommandEncoderDescriptor, ComputePipelineDescriptor, Extensions, Limits,
MapMode, PipelineLayoutDescriptor, RenderPipelineDescriptor, SamplerDescriptor,
SwapChainStatus, TextureDescriptor, TextureViewDescriptor,
BufferDescriptor, Capabilities, CommandEncoderDescriptor, ComputePipelineDescriptor,
Extensions, Limits, MapMode, PipelineLayoutDescriptor, RenderPipelineDescriptor,
SamplerDescriptor, SwapChainStatus, TextureDescriptor, TextureViewDescriptor,
};

use arrayvec::ArrayVec;
Expand Down Expand Up @@ -354,6 +354,10 @@ impl crate::Context for Context {
gfx_select!(*adapter => self.adapter_limits(*adapter))
}

fn adapter_capabilities(&self, adapter: &Self::AdapterId) -> Capabilities {
gfx_select!(*adapter => self.adapter_capabilities(*adapter))
}

fn device_extensions(&self, device: &Self::DeviceId) -> Extensions {
gfx_select!(*device => self.device_extensions(*device))
}
Expand All @@ -362,6 +366,10 @@ impl crate::Context for Context {
gfx_select!(*device => self.device_limits(*device))
}

fn device_capabilities(&self, device: &Self::DeviceId) -> Capabilities {
gfx_select!(*device => self.device_capabilities(*device))
}

fn device_create_swap_chain(
&self,
device: &Self::DeviceId,
Expand Down
16 changes: 13 additions & 3 deletions src/backend/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,11 @@ impl crate::Context for Context {
wgt::Limits::default()
}

fn adapter_capabilities(&self, _adapter: &Self::AdapterId) -> wgt::Capabilities {
// TODO: webgpu doesn't support capabilities, so return an empty set of capabilities
wgt::Capabilities::default()
}

fn device_extensions(&self, _device: &Self::DeviceId) -> wgt::Extensions {
// TODO: web-sys has no way of getting extensions on devices
wgt::Extensions::empty()
Expand All @@ -735,6 +740,11 @@ impl crate::Context for Context {
wgt::Limits::default()
}

fn device_capabilities(&self, _device: &Self::DeviceId) -> wgt::Capabilities {
// TODO: webgpu doesn't support capabilities, so return an empty set of capabilities
wgt::Capabilities::default()
}

fn device_create_swap_chain(
&self,
device: &Self::DeviceId,
Expand Down Expand Up @@ -863,9 +873,9 @@ impl crate::Context for Context {
view: ref texture_view,
read_only_depth_stencil: _,
} => JsValue::from(texture_view.id.0.clone()),
BindingResource::TextureViewArray(..) => {
panic!("Web backend does not support TEXTURE_BINDING_ARRAY extension")
}
BindingResource::TextureViewArray(..) => panic!(
"Web backend does not support SAMPLED_TEXTURE_BINDING_ARRAY extension"
),
};

web_sys::GpuBindGroupEntry::new(binding.binding, &mapped_resource)
Expand Down
Loading