summaryrefslogtreecommitdiffstats
path: root/third_party/rust/wgpu-hal/src/gles/queue.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/wgpu-hal/src/gles/queue.rs
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/wgpu-hal/src/gles/queue.rs')
-rw-r--r--third_party/rust/wgpu-hal/src/gles/queue.rs1812
1 files changed, 1812 insertions, 0 deletions
diff --git a/third_party/rust/wgpu-hal/src/gles/queue.rs b/third_party/rust/wgpu-hal/src/gles/queue.rs
new file mode 100644
index 0000000000..6ec553bd29
--- /dev/null
+++ b/third_party/rust/wgpu-hal/src/gles/queue.rs
@@ -0,0 +1,1812 @@
+use super::{conv::is_layered_target, Command as C, PrivateCapabilities};
+use arrayvec::ArrayVec;
+use glow::HasContext;
+use std::{
+ mem, slice,
+ sync::{atomic::Ordering, Arc},
+};
+
+const DEBUG_ID: u32 = 0;
+
+fn extract_marker<'a>(data: &'a [u8], range: &std::ops::Range<u32>) -> &'a str {
+ std::str::from_utf8(&data[range.start as usize..range.end as usize]).unwrap()
+}
+
+fn get_2d_target(target: u32, array_layer: u32) -> u32 {
+ const CUBEMAP_FACES: [u32; 6] = [
+ glow::TEXTURE_CUBE_MAP_POSITIVE_X,
+ glow::TEXTURE_CUBE_MAP_NEGATIVE_X,
+ glow::TEXTURE_CUBE_MAP_POSITIVE_Y,
+ glow::TEXTURE_CUBE_MAP_NEGATIVE_Y,
+ glow::TEXTURE_CUBE_MAP_POSITIVE_Z,
+ glow::TEXTURE_CUBE_MAP_NEGATIVE_Z,
+ ];
+
+ match target {
+ glow::TEXTURE_2D => target,
+ glow::TEXTURE_CUBE_MAP => CUBEMAP_FACES[array_layer as usize],
+ _ => unreachable!(),
+ }
+}
+
+fn get_z_offset(target: u32, base: &crate::TextureCopyBase) -> u32 {
+ match target {
+ glow::TEXTURE_2D_ARRAY | glow::TEXTURE_CUBE_MAP_ARRAY => base.array_layer,
+ glow::TEXTURE_3D => base.origin.z,
+ _ => unreachable!(),
+ }
+}
+
+impl super::Queue {
+ /// Performs a manual shader clear, used as a workaround for a clearing bug on mesa
+ unsafe fn perform_shader_clear(&self, gl: &glow::Context, draw_buffer: u32, color: [f32; 4]) {
+ unsafe { gl.use_program(Some(self.shader_clear_program)) };
+ unsafe {
+ gl.uniform_4_f32(
+ Some(&self.shader_clear_program_color_uniform_location),
+ color[0],
+ color[1],
+ color[2],
+ color[3],
+ )
+ };
+ unsafe { gl.disable(glow::DEPTH_TEST) };
+ unsafe { gl.disable(glow::STENCIL_TEST) };
+ unsafe { gl.disable(glow::SCISSOR_TEST) };
+ unsafe { gl.disable(glow::BLEND) };
+ unsafe { gl.disable(glow::CULL_FACE) };
+ unsafe { gl.draw_buffers(&[glow::COLOR_ATTACHMENT0 + draw_buffer]) };
+ unsafe { gl.draw_arrays(glow::TRIANGLES, 0, 3) };
+
+ let draw_buffer_count = self.draw_buffer_count.load(Ordering::Relaxed);
+ if draw_buffer_count != 0 {
+ // Reset the draw buffers to what they were before the clear
+ let indices = (0..draw_buffer_count as u32)
+ .map(|i| glow::COLOR_ATTACHMENT0 + i)
+ .collect::<ArrayVec<_, { crate::MAX_COLOR_ATTACHMENTS }>>();
+ unsafe { gl.draw_buffers(&indices) };
+ }
+ }
+
+ unsafe fn reset_state(&self, gl: &glow::Context) {
+ unsafe { gl.use_program(None) };
+ unsafe { gl.bind_framebuffer(glow::FRAMEBUFFER, None) };
+ unsafe { gl.disable(glow::DEPTH_TEST) };
+ unsafe { gl.disable(glow::STENCIL_TEST) };
+ unsafe { gl.disable(glow::SCISSOR_TEST) };
+ unsafe { gl.disable(glow::BLEND) };
+ unsafe { gl.disable(glow::CULL_FACE) };
+ unsafe { gl.disable(glow::POLYGON_OFFSET_FILL) };
+ unsafe { gl.disable(glow::SAMPLE_ALPHA_TO_COVERAGE) };
+ if self.features.contains(wgt::Features::DEPTH_CLIP_CONTROL) {
+ unsafe { gl.disable(glow::DEPTH_CLAMP) };
+ }
+
+ unsafe { gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None) };
+ let mut current_index_buffer = self.current_index_buffer.lock();
+ *current_index_buffer = None;
+ }
+
+ unsafe fn set_attachment(
+ &self,
+ gl: &glow::Context,
+ fbo_target: u32,
+ attachment: u32,
+ view: &super::TextureView,
+ ) {
+ match view.inner {
+ super::TextureInner::Renderbuffer { raw } => {
+ unsafe {
+ gl.framebuffer_renderbuffer(
+ fbo_target,
+ attachment,
+ glow::RENDERBUFFER,
+ Some(raw),
+ )
+ };
+ }
+ super::TextureInner::DefaultRenderbuffer => panic!("Unexpected default RBO"),
+ super::TextureInner::Texture { raw, target } => {
+ let num_layers = view.array_layers.end - view.array_layers.start;
+ if num_layers > 1 {
+ #[cfg(webgl)]
+ unsafe {
+ gl.framebuffer_texture_multiview_ovr(
+ fbo_target,
+ attachment,
+ Some(raw),
+ view.mip_levels.start as i32,
+ view.array_layers.start as i32,
+ num_layers as i32,
+ )
+ };
+ } else if is_layered_target(target) {
+ unsafe {
+ gl.framebuffer_texture_layer(
+ fbo_target,
+ attachment,
+ Some(raw),
+ view.mip_levels.start as i32,
+ view.array_layers.start as i32,
+ )
+ };
+ } else {
+ unsafe {
+ assert_eq!(view.mip_levels.len(), 1);
+ gl.framebuffer_texture_2d(
+ fbo_target,
+ attachment,
+ get_2d_target(target, view.array_layers.start),
+ Some(raw),
+ view.mip_levels.start as i32,
+ )
+ };
+ }
+ }
+ #[cfg(webgl)]
+ super::TextureInner::ExternalFramebuffer { ref inner } => unsafe {
+ gl.bind_external_framebuffer(glow::FRAMEBUFFER, inner);
+ },
+ }
+ }
+
+ unsafe fn process(
+ &self,
+ gl: &glow::Context,
+ command: &C,
+ #[cfg_attr(target_arch = "wasm32", allow(unused))] data_bytes: &[u8],
+ queries: &[glow::Query],
+ ) {
+ match *command {
+ C::Draw {
+ topology,
+ first_vertex,
+ vertex_count,
+ instance_count,
+ first_instance,
+ ref first_instance_location,
+ } => {
+ let supports_full_instancing = self
+ .shared
+ .private_caps
+ .contains(PrivateCapabilities::FULLY_FEATURED_INSTANCING);
+
+ if supports_full_instancing {
+ unsafe {
+ gl.draw_arrays_instanced_base_instance(
+ topology,
+ first_vertex as i32,
+ vertex_count as i32,
+ instance_count as i32,
+ first_instance,
+ )
+ }
+ } else {
+ unsafe {
+ gl.uniform_1_u32(first_instance_location.as_ref(), first_instance);
+ }
+
+ // Don't use `gl.draw_arrays` for `instance_count == 1`.
+ // Angle has a bug where it doesn't consider the instance divisor when `DYNAMIC_DRAW` is used in `draw_arrays`.
+ // See https://github.com/gfx-rs/wgpu/issues/3578
+ unsafe {
+ gl.draw_arrays_instanced(
+ topology,
+ first_vertex as i32,
+ vertex_count as i32,
+ instance_count as i32,
+ )
+ }
+ };
+ }
+ C::DrawIndexed {
+ topology,
+ index_type,
+ index_count,
+ index_offset,
+ base_vertex,
+ first_instance,
+ instance_count,
+ ref first_instance_location,
+ } => {
+ match base_vertex {
+ 0 => {
+ unsafe {
+ gl.uniform_1_u32(first_instance_location.as_ref(), first_instance)
+ };
+
+ unsafe {
+ // Don't use `gl.draw_elements`/`gl.draw_elements_base_vertex` for `instance_count == 1`.
+ // Angle has a bug where it doesn't consider the instance divisor when `DYNAMIC_DRAW` is used in `gl.draw_elements`/`gl.draw_elements_base_vertex`.
+ // See https://github.com/gfx-rs/wgpu/issues/3578
+ gl.draw_elements_instanced(
+ topology,
+ index_count as i32,
+ index_type,
+ index_offset as i32,
+ instance_count as i32,
+ )
+ }
+ }
+ _ => {
+ let supports_full_instancing = self
+ .shared
+ .private_caps
+ .contains(PrivateCapabilities::FULLY_FEATURED_INSTANCING);
+
+ if supports_full_instancing {
+ unsafe {
+ gl.draw_elements_instanced_base_vertex_base_instance(
+ topology,
+ index_count as i32,
+ index_type,
+ index_offset as i32,
+ instance_count as i32,
+ base_vertex,
+ first_instance,
+ )
+ }
+ } else {
+ unsafe {
+ gl.uniform_1_u32(first_instance_location.as_ref(), first_instance)
+ };
+
+ // If we've gotten here, wgpu-core has already validated that this function exists via the DownlevelFlags::BASE_VERTEX feature.
+ unsafe {
+ gl.draw_elements_instanced_base_vertex(
+ topology,
+ index_count as _,
+ index_type,
+ index_offset as i32,
+ instance_count as i32,
+ base_vertex,
+ )
+ }
+ }
+ }
+ }
+ }
+ C::DrawIndirect {
+ topology,
+ indirect_buf,
+ indirect_offset,
+ ref first_instance_location,
+ } => {
+ unsafe { gl.uniform_1_u32(first_instance_location.as_ref(), 0) };
+
+ unsafe { gl.bind_buffer(glow::DRAW_INDIRECT_BUFFER, Some(indirect_buf)) };
+ unsafe { gl.draw_arrays_indirect_offset(topology, indirect_offset as i32) };
+ }
+ C::DrawIndexedIndirect {
+ topology,
+ index_type,
+ indirect_buf,
+ indirect_offset,
+ ref first_instance_location,
+ } => {
+ unsafe { gl.uniform_1_u32(first_instance_location.as_ref(), 0) };
+
+ unsafe { gl.bind_buffer(glow::DRAW_INDIRECT_BUFFER, Some(indirect_buf)) };
+ unsafe {
+ gl.draw_elements_indirect_offset(topology, index_type, indirect_offset as i32)
+ };
+ }
+ C::Dispatch(group_counts) => {
+ unsafe { gl.dispatch_compute(group_counts[0], group_counts[1], group_counts[2]) };
+ }
+ C::DispatchIndirect {
+ indirect_buf,
+ indirect_offset,
+ } => {
+ unsafe { gl.bind_buffer(glow::DISPATCH_INDIRECT_BUFFER, Some(indirect_buf)) };
+ unsafe { gl.dispatch_compute_indirect(indirect_offset as i32) };
+ }
+ C::ClearBuffer {
+ ref dst,
+ dst_target,
+ ref range,
+ } => match dst.raw {
+ Some(buffer) => {
+ // When `INDEX_BUFFER_ROLE_CHANGE` isn't available, we can't copy into the
+ // index buffer from the zero buffer. This would fail in Chrome with the
+ // following message:
+ //
+ // > Cannot copy into an element buffer destination from a non-element buffer
+ // > source
+ //
+ // Instead, we'll upload zeroes into the buffer.
+ let can_use_zero_buffer = self
+ .shared
+ .private_caps
+ .contains(super::PrivateCapabilities::INDEX_BUFFER_ROLE_CHANGE)
+ || dst_target != glow::ELEMENT_ARRAY_BUFFER;
+
+ if can_use_zero_buffer {
+ unsafe { gl.bind_buffer(glow::COPY_READ_BUFFER, Some(self.zero_buffer)) };
+ unsafe { gl.bind_buffer(dst_target, Some(buffer)) };
+ let mut dst_offset = range.start;
+ while dst_offset < range.end {
+ let size = (range.end - dst_offset).min(super::ZERO_BUFFER_SIZE as u64);
+ unsafe {
+ gl.copy_buffer_sub_data(
+ glow::COPY_READ_BUFFER,
+ dst_target,
+ 0,
+ dst_offset as i32,
+ size as i32,
+ )
+ };
+ dst_offset += size;
+ }
+ } else {
+ unsafe { gl.bind_buffer(dst_target, Some(buffer)) };
+ let zeroes = vec![0u8; (range.end - range.start) as usize];
+ unsafe {
+ gl.buffer_sub_data_u8_slice(dst_target, range.start as i32, &zeroes)
+ };
+ }
+ }
+ None => {
+ dst.data.as_ref().unwrap().lock().unwrap().as_mut_slice()
+ [range.start as usize..range.end as usize]
+ .fill(0);
+ }
+ },
+ C::CopyBufferToBuffer {
+ ref src,
+ src_target,
+ ref dst,
+ dst_target,
+ copy,
+ } => {
+ let copy_src_target = glow::COPY_READ_BUFFER;
+ let is_index_buffer_only_element_dst = !self
+ .shared
+ .private_caps
+ .contains(super::PrivateCapabilities::INDEX_BUFFER_ROLE_CHANGE)
+ && dst_target == glow::ELEMENT_ARRAY_BUFFER
+ || src_target == glow::ELEMENT_ARRAY_BUFFER;
+
+ // WebGL not allowed to copy data from other targets to element buffer and can't copy element data to other buffers
+ let copy_dst_target = if is_index_buffer_only_element_dst {
+ glow::ELEMENT_ARRAY_BUFFER
+ } else {
+ glow::COPY_WRITE_BUFFER
+ };
+ let size = copy.size.get() as usize;
+ match (src.raw, dst.raw) {
+ (Some(ref src), Some(ref dst)) => {
+ unsafe { gl.bind_buffer(copy_src_target, Some(*src)) };
+ unsafe { gl.bind_buffer(copy_dst_target, Some(*dst)) };
+ unsafe {
+ gl.copy_buffer_sub_data(
+ copy_src_target,
+ copy_dst_target,
+ copy.src_offset as _,
+ copy.dst_offset as _,
+ copy.size.get() as _,
+ )
+ };
+ }
+ (Some(src), None) => {
+ let mut data = dst.data.as_ref().unwrap().lock().unwrap();
+ let dst_data = &mut data.as_mut_slice()
+ [copy.dst_offset as usize..copy.dst_offset as usize + size];
+
+ unsafe { gl.bind_buffer(copy_src_target, Some(src)) };
+ unsafe {
+ self.shared.get_buffer_sub_data(
+ gl,
+ copy_src_target,
+ copy.src_offset as i32,
+ dst_data,
+ )
+ };
+ }
+ (None, Some(dst)) => {
+ let data = src.data.as_ref().unwrap().lock().unwrap();
+ let src_data = &data.as_slice()
+ [copy.src_offset as usize..copy.src_offset as usize + size];
+ unsafe { gl.bind_buffer(copy_dst_target, Some(dst)) };
+ unsafe {
+ gl.buffer_sub_data_u8_slice(
+ copy_dst_target,
+ copy.dst_offset as i32,
+ src_data,
+ )
+ };
+ }
+ (None, None) => {
+ todo!()
+ }
+ }
+ unsafe { gl.bind_buffer(copy_src_target, None) };
+ if is_index_buffer_only_element_dst {
+ unsafe {
+ gl.bind_buffer(
+ glow::ELEMENT_ARRAY_BUFFER,
+ *self.current_index_buffer.lock(),
+ )
+ };
+ } else {
+ unsafe { gl.bind_buffer(copy_dst_target, None) };
+ }
+ }
+ #[cfg(webgl)]
+ C::CopyExternalImageToTexture {
+ ref src,
+ dst,
+ dst_target,
+ dst_format,
+ dst_premultiplication,
+ ref copy,
+ } => {
+ const UNPACK_FLIP_Y_WEBGL: u32 =
+ web_sys::WebGl2RenderingContext::UNPACK_FLIP_Y_WEBGL;
+ const UNPACK_PREMULTIPLY_ALPHA_WEBGL: u32 =
+ web_sys::WebGl2RenderingContext::UNPACK_PREMULTIPLY_ALPHA_WEBGL;
+
+ unsafe {
+ if src.flip_y {
+ gl.pixel_store_bool(UNPACK_FLIP_Y_WEBGL, true);
+ }
+ if dst_premultiplication {
+ gl.pixel_store_bool(UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
+ }
+ }
+
+ unsafe { gl.bind_texture(dst_target, Some(dst)) };
+ let format_desc = self.shared.describe_texture_format(dst_format);
+ if is_layered_target(dst_target) {
+ let z_offset = get_z_offset(dst_target, &copy.dst_base);
+
+ match src.source {
+ wgt::ExternalImageSource::ImageBitmap(ref b) => unsafe {
+ gl.tex_sub_image_3d_with_image_bitmap(
+ dst_target,
+ copy.dst_base.mip_level as i32,
+ copy.dst_base.origin.x as i32,
+ copy.dst_base.origin.y as i32,
+ z_offset as i32,
+ copy.size.width as i32,
+ copy.size.height as i32,
+ copy.size.depth as i32,
+ format_desc.external,
+ format_desc.data_type,
+ b,
+ );
+ },
+ wgt::ExternalImageSource::HTMLVideoElement(ref v) => unsafe {
+ gl.tex_sub_image_3d_with_html_video_element(
+ dst_target,
+ copy.dst_base.mip_level as i32,
+ copy.dst_base.origin.x as i32,
+ copy.dst_base.origin.y as i32,
+ z_offset as i32,
+ copy.size.width as i32,
+ copy.size.height as i32,
+ copy.size.depth as i32,
+ format_desc.external,
+ format_desc.data_type,
+ v,
+ );
+ },
+ wgt::ExternalImageSource::HTMLCanvasElement(ref c) => unsafe {
+ gl.tex_sub_image_3d_with_html_canvas_element(
+ dst_target,
+ copy.dst_base.mip_level as i32,
+ copy.dst_base.origin.x as i32,
+ copy.dst_base.origin.y as i32,
+ z_offset as i32,
+ copy.size.width as i32,
+ copy.size.height as i32,
+ copy.size.depth as i32,
+ format_desc.external,
+ format_desc.data_type,
+ c,
+ );
+ },
+ wgt::ExternalImageSource::OffscreenCanvas(_) => unreachable!(),
+ }
+ } else {
+ let dst_target = get_2d_target(dst_target, copy.dst_base.array_layer);
+
+ match src.source {
+ wgt::ExternalImageSource::ImageBitmap(ref b) => unsafe {
+ gl.tex_sub_image_2d_with_image_bitmap_and_width_and_height(
+ dst_target,
+ copy.dst_base.mip_level as i32,
+ copy.dst_base.origin.x as i32,
+ copy.dst_base.origin.y as i32,
+ copy.size.width as i32,
+ copy.size.height as i32,
+ format_desc.external,
+ format_desc.data_type,
+ b,
+ );
+ },
+ wgt::ExternalImageSource::HTMLVideoElement(ref v) => unsafe {
+ gl.tex_sub_image_2d_with_html_video_and_width_and_height(
+ dst_target,
+ copy.dst_base.mip_level as i32,
+ copy.dst_base.origin.x as i32,
+ copy.dst_base.origin.y as i32,
+ copy.size.width as i32,
+ copy.size.height as i32,
+ format_desc.external,
+ format_desc.data_type,
+ v,
+ )
+ },
+ wgt::ExternalImageSource::HTMLCanvasElement(ref c) => unsafe {
+ gl.tex_sub_image_2d_with_html_canvas_and_width_and_height(
+ dst_target,
+ copy.dst_base.mip_level as i32,
+ copy.dst_base.origin.x as i32,
+ copy.dst_base.origin.y as i32,
+ copy.size.width as i32,
+ copy.size.height as i32,
+ format_desc.external,
+ format_desc.data_type,
+ c,
+ )
+ },
+ wgt::ExternalImageSource::OffscreenCanvas(_) => unreachable!(),
+ }
+ }
+
+ unsafe {
+ if src.flip_y {
+ gl.pixel_store_bool(UNPACK_FLIP_Y_WEBGL, false);
+ }
+ if dst_premultiplication {
+ gl.pixel_store_bool(UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
+ }
+ }
+ }
+ C::CopyTextureToTexture {
+ src,
+ src_target,
+ dst,
+ dst_target,
+ ref copy,
+ } => {
+ //TODO: handle 3D copies
+ unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(self.copy_fbo)) };
+ if is_layered_target(src_target) {
+ //TODO: handle GLES without framebuffer_texture_3d
+ unsafe {
+ gl.framebuffer_texture_layer(
+ glow::READ_FRAMEBUFFER,
+ glow::COLOR_ATTACHMENT0,
+ Some(src),
+ copy.src_base.mip_level as i32,
+ copy.src_base.array_layer as i32,
+ )
+ };
+ } else {
+ unsafe {
+ gl.framebuffer_texture_2d(
+ glow::READ_FRAMEBUFFER,
+ glow::COLOR_ATTACHMENT0,
+ src_target,
+ Some(src),
+ copy.src_base.mip_level as i32,
+ )
+ };
+ }
+
+ unsafe { gl.bind_texture(dst_target, Some(dst)) };
+ if is_layered_target(dst_target) {
+ unsafe {
+ gl.copy_tex_sub_image_3d(
+ dst_target,
+ copy.dst_base.mip_level as i32,
+ copy.dst_base.origin.x as i32,
+ copy.dst_base.origin.y as i32,
+ get_z_offset(dst_target, &copy.dst_base) as i32,
+ copy.src_base.origin.x as i32,
+ copy.src_base.origin.y as i32,
+ copy.size.width as i32,
+ copy.size.height as i32,
+ )
+ };
+ } else {
+ unsafe {
+ gl.copy_tex_sub_image_2d(
+ get_2d_target(dst_target, copy.dst_base.array_layer),
+ copy.dst_base.mip_level as i32,
+ copy.dst_base.origin.x as i32,
+ copy.dst_base.origin.y as i32,
+ copy.src_base.origin.x as i32,
+ copy.src_base.origin.y as i32,
+ copy.size.width as i32,
+ copy.size.height as i32,
+ )
+ };
+ }
+ }
+ C::CopyBufferToTexture {
+ ref src,
+ src_target: _,
+ dst,
+ dst_target,
+ dst_format,
+ ref copy,
+ } => {
+ let (block_width, block_height) = dst_format.block_dimensions();
+ let block_size = dst_format.block_copy_size(None).unwrap();
+ let format_desc = self.shared.describe_texture_format(dst_format);
+ let row_texels = copy
+ .buffer_layout
+ .bytes_per_row
+ .map_or(0, |bpr| block_width * bpr / block_size);
+ let column_texels = copy
+ .buffer_layout
+ .rows_per_image
+ .map_or(0, |rpi| block_height * rpi);
+
+ unsafe { gl.bind_texture(dst_target, Some(dst)) };
+ unsafe { gl.pixel_store_i32(glow::UNPACK_ROW_LENGTH, row_texels as i32) };
+ unsafe { gl.pixel_store_i32(glow::UNPACK_IMAGE_HEIGHT, column_texels as i32) };
+ let mut unbind_unpack_buffer = false;
+ if !dst_format.is_compressed() {
+ let buffer_data;
+ let unpack_data = match src.raw {
+ Some(buffer) => {
+ unsafe { gl.bind_buffer(glow::PIXEL_UNPACK_BUFFER, Some(buffer)) };
+ unbind_unpack_buffer = true;
+ glow::PixelUnpackData::BufferOffset(copy.buffer_layout.offset as u32)
+ }
+ None => {
+ buffer_data = src.data.as_ref().unwrap().lock().unwrap();
+ let src_data =
+ &buffer_data.as_slice()[copy.buffer_layout.offset as usize..];
+ glow::PixelUnpackData::Slice(src_data)
+ }
+ };
+ if is_layered_target(dst_target) {
+ unsafe {
+ gl.tex_sub_image_3d(
+ dst_target,
+ copy.texture_base.mip_level as i32,
+ copy.texture_base.origin.x as i32,
+ copy.texture_base.origin.y as i32,
+ get_z_offset(dst_target, &copy.texture_base) as i32,
+ copy.size.width as i32,
+ copy.size.height as i32,
+ copy.size.depth as i32,
+ format_desc.external,
+ format_desc.data_type,
+ unpack_data,
+ )
+ };
+ } else {
+ unsafe {
+ gl.tex_sub_image_2d(
+ get_2d_target(dst_target, copy.texture_base.array_layer),
+ copy.texture_base.mip_level as i32,
+ copy.texture_base.origin.x as i32,
+ copy.texture_base.origin.y as i32,
+ copy.size.width as i32,
+ copy.size.height as i32,
+ format_desc.external,
+ format_desc.data_type,
+ unpack_data,
+ )
+ };
+ }
+ } else {
+ let bytes_per_row = copy
+ .buffer_layout
+ .bytes_per_row
+ .unwrap_or(copy.size.width * block_size);
+ let minimum_rows_per_image =
+ (copy.size.height + block_height - 1) / block_height;
+ let rows_per_image = copy
+ .buffer_layout
+ .rows_per_image
+ .unwrap_or(minimum_rows_per_image);
+
+ let bytes_per_image = bytes_per_row * rows_per_image;
+ let minimum_bytes_per_image = bytes_per_row * minimum_rows_per_image;
+ let bytes_in_upload =
+ (bytes_per_image * (copy.size.depth - 1)) + minimum_bytes_per_image;
+ let offset = copy.buffer_layout.offset as u32;
+
+ let buffer_data;
+ let unpack_data = match src.raw {
+ Some(buffer) => {
+ unsafe { gl.bind_buffer(glow::PIXEL_UNPACK_BUFFER, Some(buffer)) };
+ unbind_unpack_buffer = true;
+ glow::CompressedPixelUnpackData::BufferRange(
+ offset..offset + bytes_in_upload,
+ )
+ }
+ None => {
+ buffer_data = src.data.as_ref().unwrap().lock().unwrap();
+ let src_data = &buffer_data.as_slice()
+ [(offset as usize)..(offset + bytes_in_upload) as usize];
+ glow::CompressedPixelUnpackData::Slice(src_data)
+ }
+ };
+
+ if is_layered_target(dst_target) {
+ unsafe {
+ gl.compressed_tex_sub_image_3d(
+ dst_target,
+ copy.texture_base.mip_level as i32,
+ copy.texture_base.origin.x as i32,
+ copy.texture_base.origin.y as i32,
+ get_z_offset(dst_target, &copy.texture_base) as i32,
+ copy.size.width as i32,
+ copy.size.height as i32,
+ copy.size.depth as i32,
+ format_desc.internal,
+ unpack_data,
+ )
+ };
+ } else {
+ unsafe {
+ gl.compressed_tex_sub_image_2d(
+ get_2d_target(dst_target, copy.texture_base.array_layer),
+ copy.texture_base.mip_level as i32,
+ copy.texture_base.origin.x as i32,
+ copy.texture_base.origin.y as i32,
+ copy.size.width as i32,
+ copy.size.height as i32,
+ format_desc.internal,
+ unpack_data,
+ )
+ };
+ }
+ }
+ if unbind_unpack_buffer {
+ unsafe { gl.bind_buffer(glow::PIXEL_UNPACK_BUFFER, None) };
+ }
+ }
+ C::CopyTextureToBuffer {
+ src,
+ src_target,
+ src_format,
+ ref dst,
+ dst_target: _,
+ ref copy,
+ } => {
+ let block_size = src_format.block_copy_size(None).unwrap();
+ if src_format.is_compressed() {
+ log::error!("Not implemented yet: compressed texture copy to buffer");
+ return;
+ }
+ if src_target == glow::TEXTURE_CUBE_MAP
+ || src_target == glow::TEXTURE_CUBE_MAP_ARRAY
+ {
+ log::error!("Not implemented yet: cubemap texture copy to buffer");
+ return;
+ }
+ let format_desc = self.shared.describe_texture_format(src_format);
+ let row_texels = copy
+ .buffer_layout
+ .bytes_per_row
+ .map_or(copy.size.width, |bpr| bpr / block_size);
+ let column_texels = copy
+ .buffer_layout
+ .rows_per_image
+ .unwrap_or(copy.size.height);
+
+ unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(self.copy_fbo)) };
+
+ let read_pixels = |offset| {
+ let mut buffer_data;
+ let unpack_data = match dst.raw {
+ Some(buffer) => {
+ unsafe { gl.pixel_store_i32(glow::PACK_ROW_LENGTH, row_texels as i32) };
+ unsafe { gl.bind_buffer(glow::PIXEL_PACK_BUFFER, Some(buffer)) };
+ glow::PixelPackData::BufferOffset(offset as u32)
+ }
+ None => {
+ buffer_data = dst.data.as_ref().unwrap().lock().unwrap();
+ let dst_data = &mut buffer_data.as_mut_slice()[offset as usize..];
+ glow::PixelPackData::Slice(dst_data)
+ }
+ };
+ unsafe {
+ gl.read_pixels(
+ copy.texture_base.origin.x as i32,
+ copy.texture_base.origin.y as i32,
+ copy.size.width as i32,
+ copy.size.height as i32,
+ format_desc.external,
+ format_desc.data_type,
+ unpack_data,
+ )
+ };
+ };
+
+ match src_target {
+ glow::TEXTURE_2D => {
+ unsafe {
+ gl.framebuffer_texture_2d(
+ glow::READ_FRAMEBUFFER,
+ glow::COLOR_ATTACHMENT0,
+ src_target,
+ Some(src),
+ copy.texture_base.mip_level as i32,
+ )
+ };
+ read_pixels(copy.buffer_layout.offset);
+ }
+ glow::TEXTURE_2D_ARRAY => {
+ unsafe {
+ gl.framebuffer_texture_layer(
+ glow::READ_FRAMEBUFFER,
+ glow::COLOR_ATTACHMENT0,
+ Some(src),
+ copy.texture_base.mip_level as i32,
+ copy.texture_base.array_layer as i32,
+ )
+ };
+ read_pixels(copy.buffer_layout.offset);
+ }
+ glow::TEXTURE_3D => {
+ for z in copy.texture_base.origin.z..copy.size.depth {
+ unsafe {
+ gl.framebuffer_texture_layer(
+ glow::READ_FRAMEBUFFER,
+ glow::COLOR_ATTACHMENT0,
+ Some(src),
+ copy.texture_base.mip_level as i32,
+ z as i32,
+ )
+ };
+ let offset = copy.buffer_layout.offset
+ + (z * block_size * row_texels * column_texels) as u64;
+ read_pixels(offset);
+ }
+ }
+ glow::TEXTURE_CUBE_MAP | glow::TEXTURE_CUBE_MAP_ARRAY => unimplemented!(),
+ _ => unreachable!(),
+ }
+ }
+ C::SetIndexBuffer(buffer) => {
+ unsafe { gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(buffer)) };
+ let mut current_index_buffer = self.current_index_buffer.lock();
+ *current_index_buffer = Some(buffer);
+ }
+ C::BeginQuery(query, target) => {
+ unsafe { gl.begin_query(target, query) };
+ }
+ C::EndQuery(target) => {
+ unsafe { gl.end_query(target) };
+ }
+ C::TimestampQuery(query) => {
+ unsafe { gl.query_counter(query, glow::TIMESTAMP) };
+ }
+ C::CopyQueryResults {
+ ref query_range,
+ ref dst,
+ dst_target,
+ dst_offset,
+ } => {
+ if self
+ .shared
+ .private_caps
+ .contains(PrivateCapabilities::QUERY_BUFFERS)
+ && dst.raw.is_some()
+ {
+ unsafe {
+ // We're assuming that the only relevant queries are 8 byte timestamps or
+ // occlusion tests.
+ let query_size = 8;
+
+ let query_range_size = query_size * query_range.len();
+
+ let buffer = gl.create_buffer().ok();
+ gl.bind_buffer(glow::QUERY_BUFFER, buffer);
+ gl.buffer_data_size(
+ glow::QUERY_BUFFER,
+ query_range_size as _,
+ glow::STREAM_COPY,
+ );
+
+ for (i, &query) in queries
+ [query_range.start as usize..query_range.end as usize]
+ .iter()
+ .enumerate()
+ {
+ gl.get_query_parameter_u64_with_offset(
+ query,
+ glow::QUERY_RESULT,
+ query_size * i,
+ )
+ }
+ gl.bind_buffer(dst_target, dst.raw);
+ gl.copy_buffer_sub_data(
+ glow::QUERY_BUFFER,
+ dst_target,
+ 0,
+ dst_offset as _,
+ query_range_size as _,
+ );
+ if let Some(buffer) = buffer {
+ gl.delete_buffer(buffer)
+ }
+ }
+ } else {
+ let mut temp_query_results = self.temp_query_results.lock();
+ temp_query_results.clear();
+ for &query in
+ queries[query_range.start as usize..query_range.end as usize].iter()
+ {
+ let mut result: u64 = 0;
+ unsafe {
+ if self
+ .shared
+ .private_caps
+ .contains(PrivateCapabilities::QUERY_64BIT)
+ {
+ let result: *mut u64 = &mut result;
+ gl.get_query_parameter_u64_with_offset(
+ query,
+ glow::QUERY_RESULT,
+ result as usize,
+ )
+ } else {
+ result =
+ gl.get_query_parameter_u32(query, glow::QUERY_RESULT) as u64;
+ }
+ };
+ temp_query_results.push(result);
+ }
+ let query_data = unsafe {
+ slice::from_raw_parts(
+ temp_query_results.as_ptr() as *const u8,
+ temp_query_results.len() * mem::size_of::<u64>(),
+ )
+ };
+ match dst.raw {
+ Some(buffer) => {
+ unsafe { gl.bind_buffer(dst_target, Some(buffer)) };
+ unsafe {
+ gl.buffer_sub_data_u8_slice(
+ dst_target,
+ dst_offset as i32,
+ query_data,
+ )
+ };
+ }
+ None => {
+ let data = &mut dst.data.as_ref().unwrap().lock().unwrap();
+ let len = query_data.len().min(data.len());
+ data[..len].copy_from_slice(&query_data[..len]);
+ }
+ }
+ }
+ }
+ C::ResetFramebuffer { is_default } => {
+ if is_default {
+ unsafe { gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, None) };
+ } else {
+ unsafe { gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, Some(self.draw_fbo)) };
+ unsafe {
+ gl.framebuffer_texture_2d(
+ glow::DRAW_FRAMEBUFFER,
+ glow::DEPTH_STENCIL_ATTACHMENT,
+ glow::TEXTURE_2D,
+ None,
+ 0,
+ )
+ };
+ for i in 0..crate::MAX_COLOR_ATTACHMENTS {
+ let target = glow::COLOR_ATTACHMENT0 + i as u32;
+ unsafe {
+ gl.framebuffer_texture_2d(
+ glow::DRAW_FRAMEBUFFER,
+ target,
+ glow::TEXTURE_2D,
+ None,
+ 0,
+ )
+ };
+ }
+ }
+ unsafe { gl.color_mask(true, true, true, true) };
+ unsafe { gl.depth_mask(true) };
+ unsafe { gl.stencil_mask(!0) };
+ unsafe { gl.disable(glow::DEPTH_TEST) };
+ unsafe { gl.disable(glow::STENCIL_TEST) };
+ unsafe { gl.disable(glow::SCISSOR_TEST) };
+ }
+ C::BindAttachment {
+ attachment,
+ ref view,
+ } => {
+ unsafe { self.set_attachment(gl, glow::DRAW_FRAMEBUFFER, attachment, view) };
+ }
+ C::ResolveAttachment {
+ attachment,
+ ref dst,
+ ref size,
+ } => {
+ unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(self.draw_fbo)) };
+ unsafe { gl.read_buffer(attachment) };
+ unsafe { gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, Some(self.copy_fbo)) };
+ unsafe {
+ self.set_attachment(gl, glow::DRAW_FRAMEBUFFER, glow::COLOR_ATTACHMENT0, dst)
+ };
+ unsafe {
+ gl.blit_framebuffer(
+ 0,
+ 0,
+ size.width as i32,
+ size.height as i32,
+ 0,
+ 0,
+ size.width as i32,
+ size.height as i32,
+ glow::COLOR_BUFFER_BIT,
+ glow::NEAREST,
+ )
+ };
+ unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None) };
+ unsafe { gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, Some(self.draw_fbo)) };
+ }
+ C::InvalidateAttachments(ref list) => {
+ if self
+ .shared
+ .private_caps
+ .contains(PrivateCapabilities::INVALIDATE_FRAMEBUFFER)
+ {
+ unsafe { gl.invalidate_framebuffer(glow::DRAW_FRAMEBUFFER, list) };
+ }
+ }
+ C::SetDrawColorBuffers(count) => {
+ self.draw_buffer_count.store(count, Ordering::Relaxed);
+ let indices = (0..count as u32)
+ .map(|i| glow::COLOR_ATTACHMENT0 + i)
+ .collect::<ArrayVec<_, { crate::MAX_COLOR_ATTACHMENTS }>>();
+ unsafe { gl.draw_buffers(&indices) };
+ }
+ C::ClearColorF {
+ draw_buffer,
+ ref color,
+ is_srgb,
+ } => {
+ if self
+ .shared
+ .workarounds
+ .contains(super::Workarounds::MESA_I915_SRGB_SHADER_CLEAR)
+ && is_srgb
+ {
+ unsafe { self.perform_shader_clear(gl, draw_buffer, *color) };
+ } else {
+ // Prefer `clear` as `clear_buffer` functions have issues on Sandy Bridge
+ // on Windows.
+ unsafe {
+ gl.draw_buffers(&[glow::COLOR_ATTACHMENT0 + draw_buffer]);
+ gl.clear_color(color[0], color[1], color[2], color[3]);
+ gl.clear(glow::COLOR_BUFFER_BIT);
+ }
+ }
+ }
+ C::ClearColorU(draw_buffer, ref color) => {
+ unsafe { gl.clear_buffer_u32_slice(glow::COLOR, draw_buffer, color) };
+ }
+ C::ClearColorI(draw_buffer, ref color) => {
+ unsafe { gl.clear_buffer_i32_slice(glow::COLOR, draw_buffer, color) };
+ }
+ C::ClearDepth(depth) => {
+ // Prefer `clear` as `clear_buffer` functions have issues on Sandy Bridge
+ // on Windows.
+ unsafe {
+ gl.clear_depth_f32(depth);
+ gl.clear(glow::DEPTH_BUFFER_BIT);
+ }
+ }
+ C::ClearStencil(value) => {
+ // Prefer `clear` as `clear_buffer` functions have issues on Sandy Bridge
+ // on Windows.
+ unsafe {
+ gl.clear_stencil(value as i32);
+ gl.clear(glow::STENCIL_BUFFER_BIT);
+ }
+ }
+ C::ClearDepthAndStencil(depth, stencil_value) => {
+ // Prefer `clear` as `clear_buffer` functions have issues on Sandy Bridge
+ // on Windows.
+ unsafe {
+ gl.clear_depth_f32(depth);
+ gl.clear_stencil(stencil_value as i32);
+ gl.clear(glow::DEPTH_BUFFER_BIT | glow::STENCIL_BUFFER_BIT);
+ }
+ }
+ C::BufferBarrier(raw, usage) => {
+ let mut flags = 0;
+ if usage.contains(crate::BufferUses::VERTEX) {
+ flags |= glow::VERTEX_ATTRIB_ARRAY_BARRIER_BIT;
+ unsafe { gl.bind_buffer(glow::ARRAY_BUFFER, Some(raw)) };
+ unsafe { gl.vertex_attrib_pointer_f32(0, 1, glow::BYTE, true, 0, 0) };
+ }
+ if usage.contains(crate::BufferUses::INDEX) {
+ flags |= glow::ELEMENT_ARRAY_BARRIER_BIT;
+ unsafe { gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(raw)) };
+ }
+ if usage.contains(crate::BufferUses::UNIFORM) {
+ flags |= glow::UNIFORM_BARRIER_BIT;
+ }
+ if usage.contains(crate::BufferUses::INDIRECT) {
+ flags |= glow::COMMAND_BARRIER_BIT;
+ unsafe { gl.bind_buffer(glow::DRAW_INDIRECT_BUFFER, Some(raw)) };
+ }
+ if usage.contains(crate::BufferUses::COPY_SRC) {
+ flags |= glow::PIXEL_BUFFER_BARRIER_BIT;
+ unsafe { gl.bind_buffer(glow::PIXEL_UNPACK_BUFFER, Some(raw)) };
+ }
+ if usage.contains(crate::BufferUses::COPY_DST) {
+ flags |= glow::PIXEL_BUFFER_BARRIER_BIT;
+ unsafe { gl.bind_buffer(glow::PIXEL_PACK_BUFFER, Some(raw)) };
+ }
+ if usage.intersects(crate::BufferUses::MAP_READ | crate::BufferUses::MAP_WRITE) {
+ flags |= glow::BUFFER_UPDATE_BARRIER_BIT;
+ }
+ if usage.intersects(
+ crate::BufferUses::STORAGE_READ | crate::BufferUses::STORAGE_READ_WRITE,
+ ) {
+ flags |= glow::SHADER_STORAGE_BARRIER_BIT;
+ }
+ unsafe { gl.memory_barrier(flags) };
+ }
+ C::TextureBarrier(usage) => {
+ let mut flags = 0;
+ if usage.contains(crate::TextureUses::RESOURCE) {
+ flags |= glow::TEXTURE_FETCH_BARRIER_BIT;
+ }
+ if usage.intersects(
+ crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_READ_WRITE,
+ ) {
+ flags |= glow::SHADER_IMAGE_ACCESS_BARRIER_BIT;
+ }
+ if usage.contains(crate::TextureUses::COPY_DST) {
+ flags |= glow::TEXTURE_UPDATE_BARRIER_BIT;
+ }
+ if usage.intersects(
+ crate::TextureUses::COLOR_TARGET
+ | crate::TextureUses::DEPTH_STENCIL_READ
+ | crate::TextureUses::DEPTH_STENCIL_WRITE,
+ ) {
+ flags |= glow::FRAMEBUFFER_BARRIER_BIT;
+ }
+ unsafe { gl.memory_barrier(flags) };
+ }
+ C::SetViewport {
+ ref rect,
+ ref depth,
+ } => {
+ unsafe { gl.viewport(rect.x, rect.y, rect.w, rect.h) };
+ unsafe { gl.depth_range_f32(depth.start, depth.end) };
+ }
+ C::SetScissor(ref rect) => {
+ unsafe { gl.scissor(rect.x, rect.y, rect.w, rect.h) };
+ unsafe { gl.enable(glow::SCISSOR_TEST) };
+ }
+ C::SetStencilFunc {
+ face,
+ function,
+ reference,
+ read_mask,
+ } => {
+ unsafe { gl.stencil_func_separate(face, function, reference as i32, read_mask) };
+ }
+ C::SetStencilOps {
+ face,
+ write_mask,
+ ref ops,
+ } => {
+ unsafe { gl.stencil_mask_separate(face, write_mask) };
+ unsafe { gl.stencil_op_separate(face, ops.fail, ops.depth_fail, ops.pass) };
+ }
+ C::SetVertexAttribute {
+ buffer,
+ ref buffer_desc,
+ attribute_desc: ref vat,
+ } => {
+ unsafe { gl.bind_buffer(glow::ARRAY_BUFFER, buffer) };
+ unsafe { gl.enable_vertex_attrib_array(vat.location) };
+
+ if buffer.is_none() {
+ match vat.format_desc.attrib_kind {
+ super::VertexAttribKind::Float => unsafe {
+ gl.vertex_attrib_format_f32(
+ vat.location,
+ vat.format_desc.element_count,
+ vat.format_desc.element_format,
+ true, // always normalized
+ vat.offset,
+ )
+ },
+ super::VertexAttribKind::Integer => unsafe {
+ gl.vertex_attrib_format_i32(
+ vat.location,
+ vat.format_desc.element_count,
+ vat.format_desc.element_format,
+ vat.offset,
+ )
+ },
+ }
+
+ //Note: there is apparently a bug on AMD 3500U:
+ // this call is ignored if the current array is disabled.
+ unsafe { gl.vertex_attrib_binding(vat.location, vat.buffer_index) };
+ } else {
+ match vat.format_desc.attrib_kind {
+ super::VertexAttribKind::Float => unsafe {
+ gl.vertex_attrib_pointer_f32(
+ vat.location,
+ vat.format_desc.element_count,
+ vat.format_desc.element_format,
+ true, // always normalized
+ buffer_desc.stride as i32,
+ vat.offset as i32,
+ )
+ },
+ super::VertexAttribKind::Integer => unsafe {
+ gl.vertex_attrib_pointer_i32(
+ vat.location,
+ vat.format_desc.element_count,
+ vat.format_desc.element_format,
+ buffer_desc.stride as i32,
+ vat.offset as i32,
+ )
+ },
+ }
+ unsafe { gl.vertex_attrib_divisor(vat.location, buffer_desc.step as u32) };
+ }
+ }
+ C::UnsetVertexAttribute(location) => {
+ unsafe { gl.disable_vertex_attrib_array(location) };
+ }
+ C::SetVertexBuffer {
+ index,
+ ref buffer,
+ ref buffer_desc,
+ } => {
+ unsafe { gl.vertex_binding_divisor(index, buffer_desc.step as u32) };
+ unsafe {
+ gl.bind_vertex_buffer(
+ index,
+ Some(buffer.raw),
+ buffer.offset as i32,
+ buffer_desc.stride as i32,
+ )
+ };
+ }
+ C::SetDepth(ref depth) => {
+ unsafe { gl.depth_func(depth.function) };
+ unsafe { gl.depth_mask(depth.mask) };
+ }
+ C::SetDepthBias(bias) => {
+ if bias.is_enabled() {
+ unsafe { gl.enable(glow::POLYGON_OFFSET_FILL) };
+ unsafe { gl.polygon_offset(bias.slope_scale, bias.constant as f32) };
+ } else {
+ unsafe { gl.disable(glow::POLYGON_OFFSET_FILL) };
+ }
+ }
+ C::ConfigureDepthStencil(aspects) => {
+ if aspects.contains(crate::FormatAspects::DEPTH) {
+ unsafe { gl.enable(glow::DEPTH_TEST) };
+ } else {
+ unsafe { gl.disable(glow::DEPTH_TEST) };
+ }
+ if aspects.contains(crate::FormatAspects::STENCIL) {
+ unsafe { gl.enable(glow::STENCIL_TEST) };
+ } else {
+ unsafe { gl.disable(glow::STENCIL_TEST) };
+ }
+ }
+ C::SetAlphaToCoverage(enabled) => {
+ if enabled {
+ unsafe { gl.enable(glow::SAMPLE_ALPHA_TO_COVERAGE) };
+ } else {
+ unsafe { gl.disable(glow::SAMPLE_ALPHA_TO_COVERAGE) };
+ }
+ }
+ C::SetProgram(program) => {
+ unsafe { gl.use_program(Some(program)) };
+ }
+ C::SetPrimitive(ref state) => {
+ unsafe { gl.front_face(state.front_face) };
+ if state.cull_face != 0 {
+ unsafe { gl.enable(glow::CULL_FACE) };
+ unsafe { gl.cull_face(state.cull_face) };
+ } else {
+ unsafe { gl.disable(glow::CULL_FACE) };
+ }
+ if self.features.contains(wgt::Features::DEPTH_CLIP_CONTROL) {
+ //Note: this is a bit tricky, since we are controlling the clip, not the clamp.
+ if state.unclipped_depth {
+ unsafe { gl.enable(glow::DEPTH_CLAMP) };
+ } else {
+ unsafe { gl.disable(glow::DEPTH_CLAMP) };
+ }
+ }
+ // POLYGON_MODE_LINE also implies POLYGON_MODE_POINT
+ if self.features.contains(wgt::Features::POLYGON_MODE_LINE) {
+ unsafe { gl.polygon_mode(glow::FRONT_AND_BACK, state.polygon_mode) };
+ }
+ }
+ C::SetBlendConstant(c) => {
+ unsafe { gl.blend_color(c[0], c[1], c[2], c[3]) };
+ }
+ C::SetColorTarget {
+ draw_buffer_index,
+ desc: super::ColorTargetDesc { mask, ref blend },
+ } => {
+ use wgt::ColorWrites as Cw;
+ if let Some(index) = draw_buffer_index {
+ unsafe {
+ gl.color_mask_draw_buffer(
+ index,
+ mask.contains(Cw::RED),
+ mask.contains(Cw::GREEN),
+ mask.contains(Cw::BLUE),
+ mask.contains(Cw::ALPHA),
+ )
+ };
+ if let Some(ref blend) = *blend {
+ unsafe { gl.enable_draw_buffer(glow::BLEND, index) };
+ if blend.color != blend.alpha {
+ unsafe {
+ gl.blend_equation_separate_draw_buffer(
+ index,
+ blend.color.equation,
+ blend.alpha.equation,
+ )
+ };
+ unsafe {
+ gl.blend_func_separate_draw_buffer(
+ index,
+ blend.color.src,
+ blend.color.dst,
+ blend.alpha.src,
+ blend.alpha.dst,
+ )
+ };
+ } else {
+ unsafe { gl.blend_equation_draw_buffer(index, blend.color.equation) };
+ unsafe {
+ gl.blend_func_draw_buffer(index, blend.color.src, blend.color.dst)
+ };
+ }
+ } else {
+ unsafe { gl.disable_draw_buffer(glow::BLEND, index) };
+ }
+ } else {
+ unsafe {
+ gl.color_mask(
+ mask.contains(Cw::RED),
+ mask.contains(Cw::GREEN),
+ mask.contains(Cw::BLUE),
+ mask.contains(Cw::ALPHA),
+ )
+ };
+ if let Some(ref blend) = *blend {
+ unsafe { gl.enable(glow::BLEND) };
+ if blend.color != blend.alpha {
+ unsafe {
+ gl.blend_equation_separate(
+ blend.color.equation,
+ blend.alpha.equation,
+ )
+ };
+ unsafe {
+ gl.blend_func_separate(
+ blend.color.src,
+ blend.color.dst,
+ blend.alpha.src,
+ blend.alpha.dst,
+ )
+ };
+ } else {
+ unsafe { gl.blend_equation(blend.color.equation) };
+ unsafe { gl.blend_func(blend.color.src, blend.color.dst) };
+ }
+ } else {
+ unsafe { gl.disable(glow::BLEND) };
+ }
+ }
+ }
+ C::BindBuffer {
+ target,
+ slot,
+ buffer,
+ offset,
+ size,
+ } => {
+ unsafe { gl.bind_buffer_range(target, slot, Some(buffer), offset, size) };
+ }
+ C::BindSampler(texture_index, sampler) => {
+ unsafe { gl.bind_sampler(texture_index, sampler) };
+ }
+ C::BindTexture {
+ slot,
+ texture,
+ target,
+ aspects,
+ ref mip_levels,
+ } => {
+ unsafe { gl.active_texture(glow::TEXTURE0 + slot) };
+ unsafe { gl.bind_texture(target, Some(texture)) };
+
+ unsafe {
+ gl.tex_parameter_i32(target, glow::TEXTURE_BASE_LEVEL, mip_levels.start as i32)
+ };
+ unsafe {
+ gl.tex_parameter_i32(
+ target,
+ glow::TEXTURE_MAX_LEVEL,
+ (mip_levels.end - 1) as i32,
+ )
+ };
+
+ let version = gl.version();
+ let is_min_es_3_1 = version.is_embedded && (version.major, version.minor) >= (3, 1);
+ let is_min_4_3 = !version.is_embedded && (version.major, version.minor) >= (4, 3);
+ if is_min_es_3_1 || is_min_4_3 {
+ let mode = match aspects {
+ crate::FormatAspects::DEPTH => Some(glow::DEPTH_COMPONENT),
+ crate::FormatAspects::STENCIL => Some(glow::STENCIL_INDEX),
+ _ => None,
+ };
+ if let Some(mode) = mode {
+ unsafe {
+ gl.tex_parameter_i32(
+ target,
+ glow::DEPTH_STENCIL_TEXTURE_MODE,
+ mode as _,
+ )
+ };
+ }
+ }
+ }
+ C::BindImage { slot, ref binding } => {
+ unsafe {
+ gl.bind_image_texture(
+ slot,
+ binding.raw,
+ binding.mip_level as i32,
+ binding.array_layer.is_none(),
+ binding.array_layer.unwrap_or_default() as i32,
+ binding.access,
+ binding.format,
+ )
+ };
+ }
+ C::InsertDebugMarker(ref range) => {
+ let marker = extract_marker(data_bytes, range);
+ unsafe {
+ if self
+ .shared
+ .private_caps
+ .contains(PrivateCapabilities::DEBUG_FNS)
+ {
+ gl.debug_message_insert(
+ glow::DEBUG_SOURCE_APPLICATION,
+ glow::DEBUG_TYPE_MARKER,
+ DEBUG_ID,
+ glow::DEBUG_SEVERITY_NOTIFICATION,
+ marker,
+ )
+ }
+ };
+ }
+ C::PushDebugGroup(ref range) => {
+ let marker = extract_marker(data_bytes, range);
+ unsafe {
+ if self
+ .shared
+ .private_caps
+ .contains(PrivateCapabilities::DEBUG_FNS)
+ {
+ gl.push_debug_group(glow::DEBUG_SOURCE_APPLICATION, DEBUG_ID, marker)
+ }
+ };
+ }
+ C::PopDebugGroup => {
+ unsafe {
+ if self
+ .shared
+ .private_caps
+ .contains(PrivateCapabilities::DEBUG_FNS)
+ {
+ gl.pop_debug_group()
+ }
+ };
+ }
+ C::SetPushConstants {
+ ref uniform,
+ offset,
+ } => {
+ // T must be POD
+ //
+ // This function is absolutely sketchy and we really should be using bytemuck.
+ unsafe fn get_data<T, const COUNT: usize>(data: &[u8], offset: u32) -> &[T; COUNT] {
+ let data_required = mem::size_of::<T>() * COUNT;
+
+ let raw = &data[(offset as usize)..][..data_required];
+
+ debug_assert_eq!(data_required, raw.len());
+
+ let slice: &[T] =
+ unsafe { slice::from_raw_parts(raw.as_ptr() as *const _, COUNT) };
+
+ slice.try_into().unwrap()
+ }
+
+ let location = Some(&uniform.location);
+
+ match uniform.ty {
+ //
+ // --- Float 1-4 Component ---
+ //
+ naga::TypeInner::Scalar(naga::Scalar::F32) => {
+ let data = unsafe { get_data::<f32, 1>(data_bytes, offset)[0] };
+ unsafe { gl.uniform_1_f32(location, data) };
+ }
+ naga::TypeInner::Vector {
+ size: naga::VectorSize::Bi,
+ scalar: naga::Scalar::F32,
+ } => {
+ let data = unsafe { get_data::<f32, 2>(data_bytes, offset) };
+ unsafe { gl.uniform_2_f32_slice(location, data) };
+ }
+ naga::TypeInner::Vector {
+ size: naga::VectorSize::Tri,
+ scalar: naga::Scalar::F32,
+ } => {
+ let data = unsafe { get_data::<f32, 3>(data_bytes, offset) };
+ unsafe { gl.uniform_3_f32_slice(location, data) };
+ }
+ naga::TypeInner::Vector {
+ size: naga::VectorSize::Quad,
+ scalar: naga::Scalar::F32,
+ } => {
+ let data = unsafe { get_data::<f32, 4>(data_bytes, offset) };
+ unsafe { gl.uniform_4_f32_slice(location, data) };
+ }
+
+ //
+ // --- Int 1-4 Component ---
+ //
+ naga::TypeInner::Scalar(naga::Scalar::I32) => {
+ let data = unsafe { get_data::<i32, 1>(data_bytes, offset)[0] };
+ unsafe { gl.uniform_1_i32(location, data) };
+ }
+ naga::TypeInner::Vector {
+ size: naga::VectorSize::Bi,
+ scalar: naga::Scalar::I32,
+ } => {
+ let data = unsafe { get_data::<i32, 2>(data_bytes, offset) };
+ unsafe { gl.uniform_2_i32_slice(location, data) };
+ }
+ naga::TypeInner::Vector {
+ size: naga::VectorSize::Tri,
+ scalar: naga::Scalar::I32,
+ } => {
+ let data = unsafe { get_data::<i32, 3>(data_bytes, offset) };
+ unsafe { gl.uniform_3_i32_slice(location, data) };
+ }
+ naga::TypeInner::Vector {
+ size: naga::VectorSize::Quad,
+ scalar: naga::Scalar::I32,
+ } => {
+ let data = unsafe { get_data::<i32, 4>(data_bytes, offset) };
+ unsafe { gl.uniform_4_i32_slice(location, data) };
+ }
+
+ //
+ // --- Uint 1-4 Component ---
+ //
+ naga::TypeInner::Scalar(naga::Scalar::U32) => {
+ let data = unsafe { get_data::<u32, 1>(data_bytes, offset)[0] };
+ unsafe { gl.uniform_1_u32(location, data) };
+ }
+ naga::TypeInner::Vector {
+ size: naga::VectorSize::Bi,
+ scalar: naga::Scalar::U32,
+ } => {
+ let data = unsafe { get_data::<u32, 2>(data_bytes, offset) };
+ unsafe { gl.uniform_2_u32_slice(location, data) };
+ }
+ naga::TypeInner::Vector {
+ size: naga::VectorSize::Tri,
+ scalar: naga::Scalar::U32,
+ } => {
+ let data = unsafe { get_data::<u32, 3>(data_bytes, offset) };
+ unsafe { gl.uniform_3_u32_slice(location, data) };
+ }
+ naga::TypeInner::Vector {
+ size: naga::VectorSize::Quad,
+ scalar: naga::Scalar::U32,
+ } => {
+ let data = unsafe { get_data::<u32, 4>(data_bytes, offset) };
+ unsafe { gl.uniform_4_u32_slice(location, data) };
+ }
+
+ //
+ // --- Matrix 2xR ---
+ //
+ naga::TypeInner::Matrix {
+ columns: naga::VectorSize::Bi,
+ rows: naga::VectorSize::Bi,
+ scalar: naga::Scalar::F32,
+ } => {
+ let data = unsafe { get_data::<f32, 4>(data_bytes, offset) };
+ unsafe { gl.uniform_matrix_2_f32_slice(location, false, data) };
+ }
+ naga::TypeInner::Matrix {
+ columns: naga::VectorSize::Bi,
+ rows: naga::VectorSize::Tri,
+ scalar: naga::Scalar::F32,
+ } => {
+ // repack 2 vec3s into 6 values.
+ let unpacked_data = unsafe { get_data::<f32, 8>(data_bytes, offset) };
+ #[rustfmt::skip]
+ let packed_data = [
+ unpacked_data[0], unpacked_data[1], unpacked_data[2],
+ unpacked_data[4], unpacked_data[5], unpacked_data[6],
+ ];
+ unsafe { gl.uniform_matrix_2x3_f32_slice(location, false, &packed_data) };
+ }
+ naga::TypeInner::Matrix {
+ columns: naga::VectorSize::Bi,
+ rows: naga::VectorSize::Quad,
+ scalar: naga::Scalar::F32,
+ } => {
+ let data = unsafe { get_data::<f32, 8>(data_bytes, offset) };
+ unsafe { gl.uniform_matrix_2x4_f32_slice(location, false, data) };
+ }
+
+ //
+ // --- Matrix 3xR ---
+ //
+ naga::TypeInner::Matrix {
+ columns: naga::VectorSize::Tri,
+ rows: naga::VectorSize::Bi,
+ scalar: naga::Scalar::F32,
+ } => {
+ let data = unsafe { get_data::<f32, 6>(data_bytes, offset) };
+ unsafe { gl.uniform_matrix_3x2_f32_slice(location, false, data) };
+ }
+ naga::TypeInner::Matrix {
+ columns: naga::VectorSize::Tri,
+ rows: naga::VectorSize::Tri,
+ scalar: naga::Scalar::F32,
+ } => {
+ // repack 3 vec3s into 9 values.
+ let unpacked_data = unsafe { get_data::<f32, 12>(data_bytes, offset) };
+ #[rustfmt::skip]
+ let packed_data = [
+ unpacked_data[0], unpacked_data[1], unpacked_data[2],
+ unpacked_data[4], unpacked_data[5], unpacked_data[6],
+ unpacked_data[8], unpacked_data[9], unpacked_data[10],
+ ];
+ unsafe { gl.uniform_matrix_3_f32_slice(location, false, &packed_data) };
+ }
+ naga::TypeInner::Matrix {
+ columns: naga::VectorSize::Tri,
+ rows: naga::VectorSize::Quad,
+ scalar: naga::Scalar::F32,
+ } => {
+ let data = unsafe { get_data::<f32, 12>(data_bytes, offset) };
+ unsafe { gl.uniform_matrix_3x4_f32_slice(location, false, data) };
+ }
+
+ //
+ // --- Matrix 4xR ---
+ //
+ naga::TypeInner::Matrix {
+ columns: naga::VectorSize::Quad,
+ rows: naga::VectorSize::Bi,
+ scalar: naga::Scalar::F32,
+ } => {
+ let data = unsafe { get_data::<f32, 8>(data_bytes, offset) };
+ unsafe { gl.uniform_matrix_4x2_f32_slice(location, false, data) };
+ }
+ naga::TypeInner::Matrix {
+ columns: naga::VectorSize::Quad,
+ rows: naga::VectorSize::Tri,
+ scalar: naga::Scalar::F32,
+ } => {
+ // repack 4 vec3s into 12 values.
+ let unpacked_data = unsafe { get_data::<f32, 16>(data_bytes, offset) };
+ #[rustfmt::skip]
+ let packed_data = [
+ unpacked_data[0], unpacked_data[1], unpacked_data[2],
+ unpacked_data[4], unpacked_data[5], unpacked_data[6],
+ unpacked_data[8], unpacked_data[9], unpacked_data[10],
+ unpacked_data[12], unpacked_data[13], unpacked_data[14],
+ ];
+ unsafe { gl.uniform_matrix_4x3_f32_slice(location, false, &packed_data) };
+ }
+ naga::TypeInner::Matrix {
+ columns: naga::VectorSize::Quad,
+ rows: naga::VectorSize::Quad,
+ scalar: naga::Scalar::F32,
+ } => {
+ let data = unsafe { get_data::<f32, 16>(data_bytes, offset) };
+ unsafe { gl.uniform_matrix_4_f32_slice(location, false, data) };
+ }
+ _ => panic!("Unsupported uniform datatype: {:?}!", uniform.ty),
+ }
+ }
+ }
+ }
+}
+
+impl crate::Queue<super::Api> for super::Queue {
+ unsafe fn submit(
+ &self,
+ command_buffers: &[&super::CommandBuffer],
+ _surface_textures: &[&super::Texture],
+ signal_fence: Option<(&mut super::Fence, crate::FenceValue)>,
+ ) -> Result<(), crate::DeviceError> {
+ let shared = Arc::clone(&self.shared);
+ let gl = &shared.context.lock();
+ for cmd_buf in command_buffers.iter() {
+ // The command encoder assumes a default state when encoding the command buffer.
+ // Always reset the state between command_buffers to reflect this assumption. Do
+ // this at the beginning of the loop in case something outside of wgpu modified
+ // this state prior to commit.
+ unsafe { self.reset_state(gl) };
+ if let Some(ref label) = cmd_buf.label {
+ if self
+ .shared
+ .private_caps
+ .contains(PrivateCapabilities::DEBUG_FNS)
+ {
+ unsafe { gl.push_debug_group(glow::DEBUG_SOURCE_APPLICATION, DEBUG_ID, label) };
+ }
+ }
+
+ for command in cmd_buf.commands.iter() {
+ unsafe { self.process(gl, command, &cmd_buf.data_bytes, &cmd_buf.queries) };
+ }
+
+ if cmd_buf.label.is_some()
+ && self
+ .shared
+ .private_caps
+ .contains(PrivateCapabilities::DEBUG_FNS)
+ {
+ unsafe { gl.pop_debug_group() };
+ }
+ }
+
+ if let Some((fence, value)) = signal_fence {
+ fence.maintain(gl);
+ let sync = unsafe { gl.fence_sync(glow::SYNC_GPU_COMMANDS_COMPLETE, 0) }
+ .map_err(|_| crate::DeviceError::OutOfMemory)?;
+ fence.pending.push((value, sync));
+ }
+
+ Ok(())
+ }
+
+ unsafe fn present(
+ &self,
+ surface: &super::Surface,
+ texture: super::Texture,
+ ) -> Result<(), crate::SurfaceError> {
+ unsafe { surface.present(texture, &self.shared.context) }
+ }
+
+ unsafe fn get_timestamp_period(&self) -> f32 {
+ 1.0
+ }
+}
+
+#[cfg(send_sync)]
+unsafe impl Sync for super::Queue {}
+#[cfg(send_sync)]
+unsafe impl Send for super::Queue {}