bitflags::bitflags! { /// Structure used to encode additions to GLSL that aren't supported by all versions. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pubstruct Features: u32 { /// Buffer address space support. const BUFFER_STORAGE = 1; const ARRAY_OF_ARRAYS = 1 << 1; /// 8 byte floats. const DOUBLE_TYPE = 1 << 2; /// More image formats. const FULL_IMAGE_FORMATS = 1 << 3; const MULTISAMPLED_TEXTURES = 1 << 4; const MULTISAMPLED_TEXTURE_ARRAYS = 1 << 5; const CUBE_TEXTURES_ARRAY = 1 << 6; const COMPUTE_SHADER = 1 << 7; /// Image load and early depth tests. const IMAGE_LOAD_STORE = 1 << 8; const CONSERVATIVE_DEPTH = 1 << 9; /// Interpolation and auxiliary qualifiers. /// /// Perspective, Flat, and Centroid are available in all GLSL versions we support. const NOPERSPECTIVE_QUALIFIER = 1 << 11; const SAMPLE_QUALIFIER = 1 << 12; const CLIP_DISTANCE = 1 << 13; const CULL_DISTANCE = 1 << 14; /// Sample ID. const SAMPLE_VARIABLES = 1 << 15; /// Arrays with a dynamic length. const DYNAMIC_ARRAY_SIZE = 1 << 16; const MULTI_VIEW = 1 << 17; /// Texture samples query const TEXTURE_SAMPLES = 1 << 18; /// Texture levels query const TEXTURE_LEVELS = 1 << 19; /// Image size query const IMAGE_SIZE = 1 << 20; /// Dual source blending const DUAL_SOURCE_BLENDING = 1 << 21; /// Instance index /// /// We can always support this, either through the language or a polyfill const INSTANCE_INDEX = 1 << 22; /// Sample specific LODs of cube / array shadow textures const TEXTURE_SHADOW_LOD = 1 << 23; /// Subgroup operations const SUBGROUP_OPERATIONS = 1 << 24; /// Image atomics const TEXTURE_ATOMICS = 1 << 25; /// Image atomics const SHADER_BARYCENTRICS = 1 << 26; /// Primitive index builtin const PRIMITIVE_INDEX = 1 << 27;
}
}
/// Helper structure used to store the required [`Features`] needed to output a /// [`Module`](crate::Module) /// /// Provides helper methods to check for availability and writing required extensions pub(crate) struct FeaturesManager(Features);
impl FeaturesManager { /// Creates a new [`FeaturesManager`] instance pubconstfn new() -> Self { Self(Features::empty())
}
/// Adds to the list of required [`Features`] pubfn request(&mutself, features: Features) { self.0 |= features
}
/// Checks if the list of features [`Features`] contains the specified [`Features`] pubconstfn contains(&mutself, features: Features) -> bool { self.0.contains(features)
}
/// Checks that all required [`Features`] are available for the specified /// [`Version`] otherwise returns an [`Error::MissingFeatures`]. pubfn check_availability(&self, version: Version) -> BackendResult { // Will store all the features that are unavailable letmut missing = Features::empty();
// Helper macro to check for feature availability
macro_rules! check_feature { // Used when only core glsl supports the feature
($feature:ident, $core:literal) => { ifself.0.contains(Features::$feature)
&& (version < Version::Desktop($core) || version.is_es())
{
missing |= Features::$feature;
}
}; // Used when both core and es support the feature
($feature:ident, $core:literal, $es:literal) => { ifself.0.contains(Features::$feature)
&& (version < Version::Desktop($core) || version < Version::new_gles($es))
{
missing |= Features::$feature;
}
};
}
check_feature!(COMPUTE_SHADER, 420, 310);
check_feature!(BUFFER_STORAGE, 400, 310);
check_feature!(DOUBLE_TYPE, 150);
check_feature!(CUBE_TEXTURES_ARRAY, 130, 310);
check_feature!(MULTISAMPLED_TEXTURES, 150, 300);
check_feature!(MULTISAMPLED_TEXTURE_ARRAYS, 150, 310);
check_feature!(ARRAY_OF_ARRAYS, 120, 310);
check_feature!(IMAGE_LOAD_STORE, 130, 310);
check_feature!(CONSERVATIVE_DEPTH, 130, 300);
check_feature!(NOPERSPECTIVE_QUALIFIER, 130);
check_feature!(SAMPLE_QUALIFIER, 400, 320);
check_feature!(CLIP_DISTANCE, 130, 300/* with extension */);
check_feature!(CULL_DISTANCE, 450, 300/* with extension */);
check_feature!(SAMPLE_VARIABLES, 400, 300);
check_feature!(DYNAMIC_ARRAY_SIZE, 400/* with extension */, 310);
check_feature!(DUAL_SOURCE_BLENDING, 330, 300/* with extension */);
check_feature!(SUBGROUP_OPERATIONS, 430, 310);
check_feature!(TEXTURE_ATOMICS, 420, 310); match version {
Version::Embedded { is_webgl: true, .. } => check_feature!(MULTI_VIEW, 140, 300),
_ => check_feature!(MULTI_VIEW, 140, 310),
}; // Only available on glsl core, this means that opengl es can't query the number // of samples nor levels in a image and neither do bound checks on the sample nor // the level argument of texelFecth
check_feature!(TEXTURE_SAMPLES, 150);
check_feature!(TEXTURE_LEVELS, 130);
check_feature!(IMAGE_SIZE, 430, 310);
check_feature!(TEXTURE_SHADOW_LOD, 200, 300);
// Return an error if there are missing features if missing.is_empty() {
Ok(())
} else {
Err(Error::MissingFeatures(missing))
}
}
/// Helper method used to write all needed extensions /// /// # Notes /// This won't check for feature availability so it might output extensions that aren't even /// supported.[`check_availability`](Self::check_availability) will check feature availability pubfn write(&self, options: &Options, mut out: impl Write) -> BackendResult { ifself.0.contains(Features::COMPUTE_SHADER) && !options.version.is_es() { // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_compute_shader.txt
writeln!(out, "#extension GL_ARB_compute_shader : require")?;
}
ifself.0.contains(Features::PRIMITIVE_INDEX) { match options.version {
Version::Embedded { version, .. } if version < 320 => {
writeln!(out, "#extension GL_OES_geometry_shader : require")?;
}
Version::Desktop(version) if version < 150 => {
writeln!(out, "#extension GL_ARB_geometry_shader4 : require")?;
}
_ => (),
}
}
Ok(())
}
}
impl<W> Writer<'_, W> { /// Helper method that searches the module for all the needed [`Features`] /// /// # Errors /// If the version doesn't support any of the needed [`Features`] a /// [`Error::MissingFeatures`] will be returned pub(super) fn collect_required_features(&mutself) -> BackendResult { let ep_info = self.info.get_entry_point(self.entry_point_idx as usize);
// If the array is dynamically sized if size == crate::ArraySize::Dynamic { letmut is_used = false;
// Check if this type is used in a global that is needed by the current entrypoint for (global_handle, global) inself.module.global_variables.iter() { // Skip unused globals if ep_info[global_handle].is_empty() { continue;
}
// If this array is the type of a global, then this array is used if global.ty == ty_handle {
is_used = true; break;
}
// If the type of this global is a struct iflet TypeInner::Struct { ref members, .. } = self.module.types[global.ty].inner
{ // Check the last element of the struct to see if it's type uses // this array iflet Some(last) = members.last() { if last.ty == ty_handle {
is_used = true; break;
}
}
}
}
// If this dynamically size array is used, we need dynamic array size support if is_used { self.features.request(Features::DYNAMIC_ARRAY_SIZE);
}
}
}
TypeInner::Image {
dim,
arrayed,
class,
} => { if arrayed && dim == ImageDimension::Cube { self.features.request(Features::CUBE_TEXTURES_ARRAY)
}
for (handle, global) inself.module.global_variables.iter() { if ep_info[handle].is_empty() { continue;
} match global.space {
AddressSpace::WorkGroup => self.features.request(Features::COMPUTE_SHADER),
AddressSpace::Storage { .. } => self.features.request(Features::BUFFER_STORAGE),
AddressSpace::Immediate => { if immediates_used { return Err(Error::MultipleImmediateData);
}
immediates_used = true;
}
_ => {}
}
}
// We will need to pass some of the members to a closure, so we need // to separate them otherwise the borrow checker will complain, this // shouldn't be needed in rust 2021 let &mutSelf {
module,
info, refmut features,
entry_point,
entry_point_idx, ref policies,
..
} = self;
// Loop through all expressions in both functions and the entry point // to check for needed features for (expressions, info) in module
.functions
.iter()
.map(|(h, f)| (&f.expressions, &info[h]))
.chain(core::iter::once((
&entry_point.function.expressions,
info.get_entry_point(entry_point_idx as usize),
)))
{ for (_, expr) in expressions.iter() { match *expr { // Check for queries that need aditonal features
Expression::ImageQuery {
image,
query,
..
} => match query { // Storage images use `imageSize` which is only available // in glsl > 420 // // layers queries are also implemented as size queries crate::ImageQuery::Size { .. } | crate::ImageQuery::NumLayers => { iflet TypeInner::Image {
class: ImageClass::Storage { .. }, ..
} = *info[image].ty.inner_with(&module.types) {
features.request(Features::IMAGE_SIZE)
}
}, crate::ImageQuery::NumLevels => features.request(Features::TEXTURE_LEVELS), crate::ImageQuery::NumSamples => features.request(Features::TEXTURE_SAMPLES),
}
, // Check for image loads that needs bound checking on the sample // or level argument since this requires a feature
Expression::ImageLoad {
sample, level, ..
} => { if policies.image_load != crate::proc::BoundsCheckPolicy::Unchecked { if sample.is_some() {
features.request(Features::TEXTURE_SAMPLES)
}
if level.is_some() {
features.request(Features::TEXTURE_LEVELS)
}
}
}
Expression::ImageSample { image, level, offset, .. } => { iflet TypeInner::Image {
dim,
arrayed,
class: ImageClass::Depth { .. },
} = *info[image].ty.inner_with(&module.types) { let lod = matches!(level, SampleLevel::Zero | SampleLevel::Exact(_)); let bias = matches!(level, SampleLevel::Bias(_)); let auto = matches!(level, SampleLevel::Auto); let cube = dim == ImageDimension::Cube; let array2d = dim == ImageDimension::D2 && arrayed; let gles = self.options.version.is_es();
// We have a workaround of using `textureGrad` instead of `textureLod` if the LOD is zero, // so we don't *need* this extension for those cases. // But if we're explicitly allowed to use the extension (`WriterFlags::TEXTURE_SHADOW_LOD`), // we always use it instead of the workaround. let grad_workaround_applicable = (array2d || (cube && !arrayed)) && level == SampleLevel::Zero; let prefer_grad_workaround = grad_workaround_applicable && !self.options.writer_flags.contains(WriterFlags::TEXTURE_SHADOW_LOD);
// The non `bias` version of this was standardized in GL 4.3, but never in GLES. // float textureOffset(sampler2DArrayShadow sampler, vec4 P, ivec2 offset [, float bias])
ext_used |= array2d && (bias || (gles && auto)) && offset.is_some();
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.