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: std::num::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: std::num::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 = {
push_constant_items.push(Vec::new());
push_constant_items.last_mut().unwrap()
}; let context = CompilationContext {
layout,
sampler_map: &mut sampler_map,
name_binding_map: &mut name_binding_map,
push_constant_items: pc_item,
multiview,
};
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::info!("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::warn!("\tLink: {}", 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 {:?} from program {:?}", name, 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 {} to {}",
name,
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_idx, stage_items) in push_constant_items.into_iter().enumerate() { for item in stage_items { let naga_module = &shaders[stage_idx].1.module.naga.module; let type_inner = &naga_module.types[item.ty].inner;
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(crate::BufferUses::INDEX) {
glow::ELEMENT_ARRAY_BUFFER
} else {
glow::ARRAY_BUFFER
};
let is_host_visible = desc
.usage
.intersects(crate::BufferUses::MAP_READ | crate::BufferUses::MAP_WRITE); let is_coherent = desc
.memory_flags
.contains(crate::MemoryFlags::PREFER_COHERENT);
letmut map_flags = 0; if desc.usage.contains(crate::BufferUses::MAP_READ) {
map_flags |= glow::MAP_READ_BIT;
} if desc.usage.contains(crate::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(crate::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(crate::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(crate::BufferUses::MAP_WRITE) {
map_flags |= glow::MAP_FLUSH_EXPLICIT_BIT;
} //TODO: do we need `glow::MAP_UNSYNCHRONIZED_BIT`?
let data = if emulate_map && desc.usage.contains(crate::BufferUses::MAP_READ) {
Some(Arc::new(Mutex::new(vec![0; desc.size as usize])))
} else {
None
};
// 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() { // 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 desc.vertex_buffers.iter().enumerate() {
buffers.push(super::VertexBufferDesc {
step: vb_layout.step_mode,
stride: vb_layout.array_stride as u32,
}); 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,
});
}
}
(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.
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.