Implement TEXTURE_BINDING_ARRAY extension

This commit is contained in:
Connor Fitzgerald
2020-06-09 13:05:44 -04:00
parent 7b866b1a1b
commit f8be38cc01
16 changed files with 419 additions and 190 deletions

View File

@@ -57,6 +57,7 @@ impl framework::Example for Example {
binding: 0,
visibility: wgpu::ShaderStage::COMPUTE,
ty: wgpu::BindingType::UniformBuffer { dynamic: false },
..Default::default()
},
wgpu::BindGroupLayoutEntry {
binding: 1,
@@ -65,6 +66,7 @@ impl framework::Example for Example {
dynamic: false,
readonly: false,
},
..Default::default()
},
wgpu::BindGroupLayoutEntry {
binding: 2,
@@ -73,6 +75,7 @@ impl framework::Example for Example {
dynamic: false,
readonly: false,
},
..Default::default()
},
],
label: None,

View File

@@ -139,6 +139,7 @@ impl framework::Example for Example {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX,
ty: wgpu::BindingType::UniformBuffer { dynamic: false },
..Default::default()
},
wgpu::BindGroupLayoutEntry {
binding: 1,
@@ -148,11 +149,13 @@ impl framework::Example for Example {
component_type: wgpu::TextureComponentType::Float,
dimension: wgpu::TextureViewDimension::D2,
},
..Default::default()
},
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler { comparison: false },
..Default::default()
},
],
});

View File

@@ -30,6 +30,9 @@ pub enum ShaderStage {
}
pub trait Example: 'static + Sized {
fn needed_extensions() -> (wgt::Extensions, wgt::UnsafeExtensions) {
(wgpu::Extensions::empty(), wgt::UnsafeExtensions::disallow())
}
fn init(
sc_desc: &wgpu::SwapChainDescriptor,
device: &wgpu::Device,
@@ -60,23 +63,27 @@ async fn run_async<E: Example>(event_loop: EventLoop<()>, window: Window) {
(size, surface)
};
let (needed_extensions, unsafe_extensions) = E::needed_extensions();
let adapter = instance
.request_adapter(
&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface),
},
wgpu::UnsafeExtensions::disallow(),
unsafe_extensions,
wgpu::BackendBit::PRIMARY,
)
.await
.unwrap();
let adapter_extensions = adapter.extensions();
let trace_dir = std::env::var("WGPU_TRACE");
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
extensions: wgpu::Extensions::empty(),
extensions: adapter_extensions & needed_extensions,
limits: wgpu::Limits::default(),
shader_validation: true,
},

View File

@@ -72,6 +72,7 @@ async fn execute_gpu(numbers: Vec<u32>) -> Vec<u32> {
dynamic: false,
readonly: false,
},
..Default::default()
}],
});

View File

@@ -92,11 +92,13 @@ impl Example {
component_type: wgpu::TextureComponentType::Float,
dimension: wgpu::TextureViewDimension::D2,
},
..Default::default()
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler { comparison: false },
..Default::default()
},
],
label: None,
@@ -231,6 +233,7 @@ impl framework::Example for Example {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX,
ty: wgpu::BindingType::UniformBuffer { dynamic: false },
..Default::default()
},
wgpu::BindGroupLayoutEntry {
binding: 1,
@@ -240,11 +243,13 @@ impl framework::Example for Example {
multisampled: false,
dimension: wgpu::TextureViewDimension::D2,
},
..Default::default()
},
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler { comparison: false },
..Default::default()
},
],
label: None,

View File

@@ -248,6 +248,7 @@ impl framework::Example for Example {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::UniformBuffer { dynamic: false },
..Default::default()
}],
label: None,
});
@@ -430,6 +431,7 @@ impl framework::Example for Example {
binding: 0, // global
visibility: wgpu::ShaderStage::VERTEX,
ty: wgpu::BindingType::UniformBuffer { dynamic: false },
..Default::default()
}],
label: None,
});
@@ -518,11 +520,13 @@ impl framework::Example for Example {
binding: 0, // global
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::UniformBuffer { dynamic: false },
..Default::default()
},
wgpu::BindGroupLayoutEntry {
binding: 1, // lights
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::UniformBuffer { dynamic: false },
..Default::default()
},
wgpu::BindGroupLayoutEntry {
binding: 2,
@@ -532,11 +536,13 @@ impl framework::Example for Example {
component_type: wgpu::TextureComponentType::Float,
dimension: wgpu::TextureViewDimension::D2Array,
},
..Default::default()
},
wgpu::BindGroupLayoutEntry {
binding: 3,
visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler { comparison: true },
..Default::default()
},
],
label: None,

View File

@@ -46,6 +46,7 @@ impl framework::Example for Skybox {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::UniformBuffer { dynamic: false },
..Default::default()
},
wgpu::BindGroupLayoutEntry {
binding: 1,
@@ -55,11 +56,13 @@ impl framework::Example for Skybox {
multisampled: false,
dimension: wgpu::TextureViewDimension::Cube,
},
..Default::default()
},
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler { comparison: false },
..Default::default()
},
],
label: None,

View File

@@ -0,0 +1,318 @@
#[path = "../framework.rs"]
mod framework;
use bytemuck::{Pod, Zeroable};
#[repr(C)]
#[derive(Clone, Copy)]
struct Vertex {
_pos: [f32; 2],
_tex_coord: [f32; 2],
_index: u32,
}
unsafe impl Pod for Vertex {}
unsafe impl Zeroable for Vertex {}
fn vertex(pos: [i8; 2], tc: [i8; 2], index: i8) -> Vertex {
Vertex {
_pos: [pos[0] as f32, pos[1] as f32],
_tex_coord: [tc[0] as f32, tc[1] as f32],
_index: index as u32,
}
}
fn create_vertices() -> Vec<Vertex> {
vec![
// left rectangle
vertex([-1, -1], [0, 1], 0),
vertex([-1, 1], [0, 0], 0),
vertex([0, 1], [1, 0], 0),
vertex([0, -1], [1, 1], 0),
// right rectangle
vertex([0, -1], [0, 1], 1),
vertex([0, 1], [0, 0], 1),
vertex([1, 1], [1, 0], 1),
vertex([1, -1], [1, 1], 1),
]
}
fn create_indices() -> Vec<u16> {
vec![
// Left rectangle
0, 1, 2, // 1st
2, 0, 3, // 2nd
// Right rectangle
4, 5, 6, // 1st
6, 4, 7, // 2nd
]
}
#[derive(Copy, Clone)]
enum Color {
RED,
GREEN,
}
fn create_texture_data(color: Color) -> [u8; 4] {
match color {
Color::RED => [255, 0, 0, 255],
Color::GREEN => [0, 255, 0, 255],
}
}
struct Example {
pipeline: wgpu::RenderPipeline,
bind_group: wgpu::BindGroup,
vertex_buffer: wgpu::Buffer,
index_buffer: wgpu::Buffer,
}
impl framework::Example for Example {
fn needed_extensions() -> (wgpu::Extensions, wgpu::UnsafeExtensions) {
(
wgpu::Extensions::TEXTURE_BINDING_ARRAY,
wgpu::UnsafeExtensions::disallow(),
)
}
fn init(
sc_desc: &wgpu::SwapChainDescriptor,
device: &wgpu::Device,
queue: &wgpu::Queue,
) -> (Self, Option<wgpu::CommandBuffer>) {
let device_extensions = device.extensions();
assert!(
device_extensions.contains(wgpu::Extensions::TEXTURE_BINDING_ARRAY),
"Graphics Device does not support TEXTURE_BINDING_ARRAY extension"
);
let vertex_size = std::mem::size_of::<Vertex>();
let vertex_data = create_vertices();
let vertex_buffer = device.create_buffer_with_data(
bytemuck::cast_slice(&vertex_data),
wgpu::BufferUsage::VERTEX,
);
let index_data = create_indices();
let index_buffer = device
.create_buffer_with_data(bytemuck::cast_slice(&index_data), wgpu::BufferUsage::INDEX);
let red_texture_data = create_texture_data(Color::RED);
let green_texture_data = create_texture_data(Color::GREEN);
let texture_descriptor = wgpu::TextureDescriptor {
size: wgpu::Extent3d {
width: 1,
height: 1,
depth: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
label: None,
};
let red_texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("red"),
..texture_descriptor
});
let green_texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("green"),
..texture_descriptor
});
let red_texture_view = red_texture.create_default_view();
let green_texture_view = green_texture.create_default_view();
queue.write_texture(
wgpu::TextureCopyView {
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
texture: &red_texture,
},
&red_texture_data,
wgpu::TextureDataLayout {
offset: 0,
bytes_per_row: 4,
rows_per_image: 0,
},
wgpu::Extent3d {
width: 1,
height: 1,
depth: 1,
},
);
queue.write_texture(
wgpu::TextureCopyView {
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
texture: &green_texture,
},
&green_texture_data,
wgpu::TextureDataLayout {
offset: 0,
bytes_per_row: 4,
rows_per_image: 0,
},
wgpu::Extent3d {
width: 1,
height: 1,
depth: 1,
},
);
let sampler = device.create_sampler(&wgpu::SamplerDescriptor::default());
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
bindings: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::SampledTexture {
component_type: wgpu::TextureComponentType::Float,
dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: Some(2),
..Default::default()
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler { comparison: false },
..Default::default()
},
],
label: Some("bind group layout"),
});
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
bindings: &[
wgpu::Binding {
binding: 0,
resource: wgpu::BindingResource::TextureViewArray(&[
red_texture_view,
green_texture_view,
]),
},
wgpu::Binding {
binding: 1,
resource: wgpu::BindingResource::Sampler(&sampler),
},
],
layout: &bind_group_layout,
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 = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
layout: &pipeline_layout,
vertex_stage: wgpu::ProgrammableStageDescriptor {
module: &vs_module,
entry_point: "main",
},
fragment_stage: Some(wgpu::ProgrammableStageDescriptor {
module: &fs_module,
entry_point: "main",
}),
rasterization_state: Some(wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::None,
depth_bias: 0,
depth_bias_slope_scale: 0.0,
depth_bias_clamp: 0.0,
}),
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[wgpu::ColorStateDescriptor {
format: sc_desc.format,
color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL,
}],
depth_stencil_state: None,
vertex_state: wgpu::VertexStateDescriptor {
index_format: wgpu::IndexFormat::Uint16,
vertex_buffers: &[wgpu::VertexBufferDescriptor {
stride: vertex_size as wgpu::BufferAddress,
step_mode: wgpu::InputStepMode::Vertex,
attributes: &wgpu::vertex_attr_array![0 => Float2, 1 => Float2, 2 => Int],
}],
},
sample_count: 1,
sample_mask: !0,
alpha_to_coverage_enabled: false,
});
(
Self {
vertex_buffer,
index_buffer,
bind_group,
pipeline,
},
None,
)
}
fn resize(
&mut self,
_sc_desc: &wgpu::SwapChainDescriptor,
_device: &wgpu::Device,
_queue: &wgpu::Queue,
) {
// noop
}
fn update(&mut self, _event: winit::event::WindowEvent) {
// noop
}
fn render(
&mut self,
frame: &wgpu::SwapChainTexture,
device: &wgpu::Device,
_queue: &wgpu::Queue,
) -> wgpu::CommandBuffer {
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("primary"),
});
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view,
resolve_target: None,
load_op: wgpu::LoadOp::Clear,
store_op: wgpu::StoreOp::Store,
clear_color: wgpu::Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 1.0,
},
}],
depth_stencil_attachment: None,
});
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(..));
rpass.draw_indexed(0..12, 0, 0..1);
drop(rpass);
encoder.finish()
}
}
fn main() {
framework::run::<Example>("texture-arrays");
}

View File

@@ -0,0 +1,18 @@
#version 450
layout(location = 0) in vec2 v_TexCoord;
layout(location = 1) flat in int v_Index;
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() {
if (v_Index == 0) {
o_Color = vec4(texture(sampler2D(u_Textures[0], u_Sampler), v_TexCoord).rgb, 1.0);
} 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);
}
}

Binary file not shown.

View File

@@ -0,0 +1,13 @@
#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);
}

Binary file not shown.