use alloc::{
borrow::{Cow, ToOwned},
format,
string::String,
string::ToString as _,
sync::Arc,
vec,
vec::Vec,
}; use core::{cmp::max, convert::TryInto, num::NonZeroU32, ptr, sync::atomic::Ordering};
use arrayvec::ArrayVec; use glow::HasContext; use naga::FastHashMap;
implsuper::Device { /// # Safety /// /// - `name` must be created respecting `desc` /// - `name` must be a texture /// - If `drop_callback` is [`None`], wgpu-hal will take ownership of the texture. If /// `drop_callback` is [`Some`], the texture must be valid until the callback is called. #[cfg(any(native, Emscripten))] pubunsafefn texture_from_raw(
&self,
name: NonZeroU32,
desc: &crate::TextureDescriptor,
drop_callback: Option<crate::DropCallback>,
) -> super::Texture { super::Texture {
inner: super::TextureInner::Texture {
raw: glow::NativeTexture(name),
target: super::Texture::get_info_from_desc(desc),
},
drop_guard: crate::DropGuard::from_option(drop_callback),
mip_level_count: desc.mip_level_count,
array_layer_count: desc.array_layer_count(),
format: desc.format,
format_desc: self.shared.describe_texture_format(desc.format),
copy_size: desc.copy_extent(),
}
}
/// # Safety /// /// - `name` must be created respecting `desc` /// - `name` must be a renderbuffer /// - If `drop_callback` is [`None`], wgpu-hal will take ownership of the renderbuffer. If /// `drop_callback` is [`Some`], the renderbuffer must be valid until the callback is called. #[cfg(any(native, Emscripten))] pubunsafefn texture_from_raw_renderbuffer(
&self,
name: NonZeroU32,
desc: &crate::TextureDescriptor,
drop_callback: Option<crate::DropCallback>,
) -> super::Texture { super::Texture {
inner: super::TextureInner::Renderbuffer {
raw: glow::NativeRenderbuffer(name),
},
drop_guard: crate::DropGuard::from_option(drop_callback),
mip_level_count: desc.mip_level_count,
array_layer_count: desc.array_layer_count(),
format: desc.format,
format_desc: self.shared.describe_texture_format(desc.format),
copy_size: desc.copy_extent(),
}
}
let raw = unsafe { gl.create_shader(target) }.unwrap(); #[cfg(native)] if gl.supports_debug() { let name = raw.0.get(); unsafe { gl.object_label(glow::SHADER, name, label) };
}
use naga::proc::BoundsCheckPolicy; // The image bounds checks require the TEXTURE_LEVELS feature available in GL core 4.3+. let version = gl.version(); let image_check = if !version.is_embedded && (version.major, version.minor) >= (4, 3) {
BoundsCheckPolicy::ReadZeroSkipWrite
} else {
BoundsCheckPolicy::Unchecked
};
// Other bounds check are either provided by glsl or not implemented yet. let policies = naga::proc::BoundsCheckPolicies {
index: BoundsCheckPolicy::Unchecked,
buffer: BoundsCheckPolicy::Unchecked,
image_load: image_check,
binding_array: BoundsCheckPolicy::Unchecked,
};
letmut output = String::new(); let needs_temp_options = stage.zero_initialize_workgroup_memory
!= context.layout.naga_options.zero_initialize_workgroup_memory; letmut temp_options; let naga_options = if needs_temp_options { // We use a conditional here, as cloning the naga_options could be expensive // That is, we want to avoid doing that unless we cannot avoid it
temp_options = context.layout.naga_options.clone();
temp_options.zero_initialize_workgroup_memory =
stage.zero_initialize_workgroup_memory;
&temp_options
} else {
&context.layout.naga_options
}; letmut writer = glsl::Writer::new(
&mut output,
&module,
&info,
naga_options,
&pipeline_options,
policies,
)
.map_err(|e| { let msg = format!("{e}"); crate::PipelineError::Linkage(map_naga_stage(naga_stage), msg)
})?;
let reflection_info = writer.write().map_err(|e| { let msg = format!("{e}"); crate::PipelineError::Linkage(map_naga_stage(naga_stage), msg)
})?;
for &(naga_stage, stage) in &shaders {
has_stages |= map_naga_stage(naga_stage); let pc_item = {
immediates_items.push(Vec::new());
immediates_items.last_mut().unwrap()
}; let context = CompilationContext {
layout,
sampler_map: &mut sampler_map,
name_binding_map: &mut name_binding_map,
immediates_items: pc_item,
multiview_mask,
clip_distance_count: &mut clip_distance_count,
};
let shader = Self::create_shader(gl, naga_stage, stage, context, program)?;
shaders_to_delete.push(shader);
}
// Create empty fragment shader if only vertex shader is present if has_stages == wgt::ShaderStages::VERTEX { let shader_src = format!("#version {glsl_version}\n void main(void) {{}}",);
log::debug!("Only vertex shader is present. Creating an empty fragment shader",); let shader = unsafe { Self::compile_shader(
gl,
&shader_src,
naga::ShaderStage::Fragment,
Some("(wgpu internal) dummy fragment shader"),
)
}?;
shaders_to_delete.push(shader);
}
for &shader in shaders_to_delete.iter() { unsafe { gl.attach_shader(program, shader) };
} unsafe { gl.link_program(program) };
for shader in shaders_to_delete { unsafe { gl.delete_shader(shader) };
}
log::debug!("\tLinked program {program:?}");
let linked_ok = unsafe { gl.get_program_link_status(program) }; let msg = unsafe { gl.get_program_info_log(program) }; if !linked_ok { return Err(crate::PipelineError::Linkage(has_stages, msg));
} if !msg.is_empty() {
log::debug!("\tLink message: {msg}");
}
if !private_caps.contains(PrivateCapabilities::SHADER_BINDING_LAYOUT) { // This remapping is only needed if we aren't able to put the binding layout // in the shader. We can't remap storage buffers this way. unsafe { gl.use_program(Some(program)) }; for (ref name, (register, slot)) in name_binding_map {
log::trace!("Get binding {name:?} from program {program:?}"); match register { super::BindingRegister::UniformBuffers => { let index = unsafe { gl.get_uniform_block_index(program, name) }.unwrap();
log::trace!("\tBinding slot {slot} to block index {index}"); unsafe { gl.uniform_block_binding(program, index, slot as _) };
} super::BindingRegister::StorageBuffers => { let index = unsafe { gl.get_shader_storage_block_index(program, name) }.unwrap();
log::error!("Unable to re-map shader storage block {name} to {index}"); return Err(crate::DeviceError::Lost.into());
} super::BindingRegister::Textures | super::BindingRegister::Images => { let location = unsafe { gl.get_uniform_location(program, name) }; unsafe { gl.uniform_1_i32(location.as_ref(), slot as _) };
}
}
}
}
letmut uniforms = ArrayVec::new();
for stage_items in immediates_items { for item in stage_items { let location = unsafe { gl.get_uniform_location(program, &item.access_path) };
let first_instance_location = if has_stages.contains(wgt::ShaderStages::VERTEX) { // If this returns none (the uniform isn't active), that's fine, we just won't set it. unsafe { gl.get_uniform_location(program, naga::back::glsl::FIRST_INSTANCE_BINDING) }
} else {
None
};
let target = if desc.usage.contains(wgt::BufferUses::INDEX) {
glow::ELEMENT_ARRAY_BUFFER
} else {
glow::ARRAY_BUFFER
};
let is_host_visible = desc
.usage
.intersects(wgt::BufferUses::MAP_READ | wgt::BufferUses::MAP_WRITE); let is_coherent = desc
.memory_flags
.contains(crate::MemoryFlags::PREFER_COHERENT);
letmut map_flags = 0; if desc.usage.contains(wgt::BufferUses::MAP_READ) {
map_flags |= glow::MAP_READ_BIT;
} if desc.usage.contains(wgt::BufferUses::MAP_WRITE) {
map_flags |= glow::MAP_WRITE_BIT;
}
let raw = Some(unsafe { gl.create_buffer() }.map_err(|_| crate::DeviceError::OutOfMemory)?); unsafe { gl.bind_buffer(target, raw) }; let raw_size = desc
.size
.try_into()
.map_err(|_| crate::DeviceError::OutOfMemory)?;
ifself
.shared
.private_caps
.contains(PrivateCapabilities::BUFFER_ALLOCATION)
{ if is_host_visible {
map_flags |= glow::MAP_PERSISTENT_BIT; if is_coherent {
map_flags |= glow::MAP_COHERENT_BIT;
}
} // TODO: may also be required for other calls involving `buffer_sub_data_u8_slice` (e.g. copy buffer to buffer and clear buffer) if desc.usage.intersects(wgt::BufferUses::QUERY_RESOLVE) {
map_flags |= glow::DYNAMIC_STORAGE_BIT;
} unsafe { gl.buffer_storage(target, raw_size, None, map_flags) };
} else {
assert!(!is_coherent); let usage = if is_host_visible { if desc.usage.contains(wgt::BufferUses::MAP_READ) {
glow::STREAM_READ
} else {
glow::DYNAMIC_DRAW
}
} else { // Even if the usage doesn't contain SRC_READ, we update it internally at least once // Some vendors take usage very literally and STATIC_DRAW will freeze us with an empty buffer // https://github.com/gfx-rs/wgpu/issues/3371
glow::DYNAMIC_DRAW
}; unsafe { gl.buffer_data_size(target, raw_size, usage) };
}
unsafe { gl.bind_buffer(target, None) };
if !is_coherent && desc.usage.contains(wgt::BufferUses::MAP_WRITE) {
map_flags |= glow::MAP_FLUSH_EXPLICIT_BIT;
} //TODO: do we need `glow::MAP_UNSYNCHRONIZED_BIT`?
// For clarity, we explicitly drop the drop guard. Although this has no real semantic effect as the // end of the scope will drop the drop guard since this function takes ownership of the texture.
drop(texture.drop_guard);
// If clamp is not 1, we know anisotropy is supported up to 16x if desc.anisotropy_clamp != 1 { unsafe {
gl.sampler_parameter_i32(
raw,
glow::TEXTURE_MAX_ANISOTROPY,
desc.anisotropy_clamp as i32,
)
};
}
letmut writer_flags = glsl::WriterFlags::ADJUST_COORDINATE_SPACE;
writer_flags.set(
glsl::WriterFlags::TEXTURE_SHADOW_LOD, self.shared
.private_caps
.contains(PrivateCapabilities::SHADER_TEXTURE_SHADOW_LOD),
);
writer_flags.set(
glsl::WriterFlags::DRAW_PARAMETERS, self.shared
.private_caps
.contains(PrivateCapabilities::FULLY_FEATURED_INSTANCING),
); // We always force point size to be written and it will be ignored by the driver if it's not a point list primitive. // https://github.com/gfx-rs/wgpu/pull/3440/files#r1095726950
writer_flags.set(glsl::WriterFlags::FORCE_POINT_SIZE, true); letmut binding_map = glsl::BindingMap::default();
for (group_index, bg_layout) in desc.bind_group_layouts.iter().enumerate() { let Some(bg_layout) = bg_layout else {
group_infos.push(None); continue;
};
// create a vector with the size enough to hold all the bindings, filled with `!0` letmut binding_to_slot = vec![
!0;
bg_layout
.entries
.iter()
.map(|b| b.binding)
.max()
.map_or(0, |idx| idx as usize + 1)
]
.into_boxed_slice();
let layout_and_entry_iter = desc.entries.iter().map(|entry| { let layout = desc
.layout
.entries
.iter()
.find(|layout_entry| layout_entry.binding == entry.binding)
.expect("internal error: no layout entry found with binding slot");
(entry, layout)
}); for (entry, layout) in layout_and_entry_iter { let binding = match layout.ty {
wgt::BindingType::Buffer { .. } => { let bb = &desc.buffers[entry.resource_index as usize]; super::RawBinding::Buffer {
raw: bb.buffer.raw.unwrap(),
offset: bb.offset as i32,
size: match bb.size {
Some(s) => s.get() as i32,
None => (bb.buffer.size - bb.offset) as i32,
},
}
}
wgt::BindingType::Sampler { .. } => { let sampler = desc.samplers[entry.resource_index as usize]; super::RawBinding::Sampler(sampler.raw)
}
wgt::BindingType::Texture { view_dimension, .. } => { let view = desc.textures[entry.resource_index as usize].view; if view.array_layers.start != 0 {
log::error!("Unable to create a sampled texture binding for non-zero array layer.\n{}", "This is an implementation problem of wgpu-hal/gles backend.")
} let (raw, target) = view.inner.as_native();
let (vertex_buffers, vertex_attributes) = { letmut buffers = Vec::new(); letmut attributes = Vec::new(); for (index, vb_layout) in vertex_buffers.iter().enumerate() { let vb_desc = iflet Some(vb_layout) = vb_layout { for vat in vb_layout.attributes.iter() { let format_desc = conv::describe_vertex_format(vat.format);
attributes.push(super::AttributeDesc {
location: vat.shader_location,
offset: vat.offset as u32,
buffer_index: index as u32,
format_desc,
});
}
Some(super::VertexBufferDesc {
step: vb_layout.step_mode,
stride: vb_layout.array_stride as u32,
})
} else {
None
};
buffers.push(vb_desc);
}
(buffers.into_boxed_slice(), attributes.into_boxed_slice())
};
let color_targets = { letmut targets = Vec::new(); for ct in desc.color_targets.iter().filter_map(|at| at.as_ref()) {
targets.push(super::ColorTargetDesc {
mask: ct.write_mask,
blend: ct.blend.as_ref().map(conv::map_blend),
});
} //Note: if any of the states are different, and `INDEPENDENT_BLEND` flag // is not exposed, then this pipeline will not bind correctly.
targets.into_boxed_slice()
};
unsafefn destroy_render_pipeline(&self, pipeline: super::RenderPipeline) { // If the pipeline only has 2 strong references remaining, they're `pipeline` and `program_cache` // This is safe to assume as long as: // - `RenderPipeline` can't be cloned // - The only place that we can get a new reference is during `program_cache.lock()` if Arc::strong_count(&pipeline.inner) == 2 { let gl = &self.shared.context.lock(); letmut program_cache = self.shared.program_cache.lock();
program_cache.retain(|_, v| match *v {
Ok(ref p) => p.program != pipeline.inner.program,
Err(_) => false,
}); unsafe { gl.delete_program(pipeline.inner.program) };
}
unsafefn destroy_compute_pipeline(&self, pipeline: super::ComputePipeline) { // If the pipeline only has 2 strong references remaining, they're `pipeline` and `program_cache`` // This is safe to assume as long as: // - `ComputePipeline` can't be cloned // - The only place that we can get a new reference is during `program_cache.lock()` if Arc::strong_count(&pipeline.inner) == 2 { let gl = &self.shared.context.lock(); letmut program_cache = self.shared.program_cache.lock();
program_cache.retain(|_, v| match *v {
Ok(ref p) => p.program != pipeline.inner.program,
Err(_) => false,
}); unsafe { gl.delete_program(pipeline.inner.program) };
}
self.counters.compute_pipelines.sub(1);
}
unsafefn create_pipeline_cache(
&self,
_: &crate::PipelineCacheDescriptor<'_>,
) -> Result<super::PipelineCache, crate::PipelineCacheError> { // Even though the cache doesn't do anything, we still return something here // as the least bad option
Ok(super::PipelineCache)
} unsafefn destroy_pipeline_cache(&self, _: super::PipelineCache) {}
letmut queries = Vec::with_capacity(desc.count as usize); for _ in0..desc.count { let query = unsafe { gl.create_query() }.map_err(|_| crate::DeviceError::OutOfMemory)?;
// We aren't really able to, in general, label queries. // // We could take a timestamp here to "initialize" the query, // but that's a bit of a hack, and we don't want to insert // random timestamps into the command stream of we don't have to.
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.38Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-30)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.