/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
usesuper::super::shader_source::{OPTIMIZED_SHADERS, UNOPTIMIZED_SHADERS}; use api::{ImageDescriptor, ImageFormat, Parameter, BoolParameter, IntParameter, ImageRendering}; use api::{MixBlendMode, ImageBufferKind, VoidPtrToSizeFn}; use api::{CrashAnnotator, CrashAnnotation, CrashAnnotatorGuard}; use api::units::*; use euclid::default::Transform3D; use gleam::gl; usecrate::render_api::MemoryReport; usecrate::internal_types::{FastHashMap, RenderTargetInfo, Swizzle, SwizzleSettings}; usecrate::util::round_up_to_multiple; usecrate::profiler; use log::Level; use smallvec::SmallVec; use std::{
borrow::Cow,
cell::{Cell, RefCell},
cmp,
collections::hash_map::Entry,
marker::PhantomData,
mem,
num::NonZeroUsize,
os::raw::c_void,
ops::Add,
path::PathBuf,
ptr,
rc::Rc,
slice,
sync::Arc,
thread,
time::Duration,
}; use webrender_build::shader::{
ProgramSourceDigest, ShaderKind, ShaderVersion, build_shader_main_string,
build_shader_prefix_string, do_build_shader_string, shader_source_from_file,
}; use malloc_size_of::MallocSizeOfOps;
/// Sequence number for frames, as tracked by the device layer. #[derive(Debug, Copy, Clone, PartialEq, Ord, Eq, PartialOrd)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pubstruct GpuFrameId(usize);
/// A structure defining a particular workflow of texture transfers. #[derive(Clone, Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pubstruct TextureFormatPair<T> { /// Format the GPU natively stores texels in. pub internal: T, /// Format we expect the users to provide the texels in. pub external: T,
}
/// Method of uploading texel data from CPU to GPU. #[derive(Debug, Clone)] pubenum UploadMethod { /// Just call `glTexSubImage` directly with the CPU data pointer
Immediate, /// Accumulate the changes in PBO first before transferring to a texture.
PixelBuffer(VertexUsageHint),
}
/// Plain old data that can be used to initialize a texture. pubunsafetrait Texel: Copy + Default { fn image_format() -> ImageFormat;
}
/// Returns the size in bytes of a depth target with the given dimensions. fn depth_target_size_in_bytes(dimensions: &DeviceIntSize) -> usize { // DEPTH24 textures generally reserve 3 bytes for depth and 1 byte // for stencil, so we measure them as 32 bits. let pixels = dimensions.width * dimensions.height;
(pixels as usize) * 4
}
// Get an unoptimized shader string by name, from the built in resources or // an override path, if supplied. pubfn get_unoptimized_shader_source(shader_name: &str, base_path: Option<&PathBuf>) -> Cow<'static, str> { iflet Some(ref base) = base_path { let shader_path = base.join(&format!("{}.glsl", shader_name));
Cow::Owned(shader_source_from_file(&shader_path))
} else {
Cow::Borrowed(
UNOPTIMIZED_SHADERS
.get(shader_name)
.expect("Shader not found")
.source
)
}
}
bitflags! { #[derive(Default, Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] pubstruct TextureFlags: u32 { /// This texture corresponds to one of the shared texture caches. const IS_SHARED_TEXTURE_CACHE = 1 << 0;
}
}
/// WebRender interface to an OpenGL texture. /// /// Because freeing a texture requires various device handles that are not /// reachable from this struct, manual destruction via `Device` is required. /// Our `Drop` implementation asserts that this has happened. #[derive(Debug)] pubstruct Texture {
id: gl::GLuint,
target: gl::GLuint,
format: ImageFormat,
size: DeviceIntSize,
filter: TextureFilter,
flags: TextureFlags, /// An internally mutable swizzling state that may change between batches.
active_swizzle: Cell<Swizzle>, /// Framebuffer Object allowing this texture to be rendered to. /// /// Empty if this texture is not used as a render target or if a depth buffer is needed.
fbo: Option<FBOId>, /// Same as the above, but with a depth buffer attached. /// /// FBOs are cheap to create but expensive to reconfigure (since doing so /// invalidates framebuffer completeness caching). Moreover, rendering with /// a depth buffer attached but the depth write+test disabled relies on the /// driver to optimize it out of the rendering pass, which most drivers /// probably do but, according to jgilbert, is best not to rely on. /// /// So we lazily generate a second list of FBOs with depth. This list is /// empty if this texture is not used as a render target _or_ if it is, but /// the depth buffer has never been requested. /// /// Note that we always fill fbo, and then lazily create fbo_with_depth /// when needed. We could make both lazy (i.e. render targets would have one /// or the other, but not both, unless they were actually used in both /// configurations). But that would complicate a lot of logic in this module, /// and FBOs are cheap enough to create.
fbo_with_depth: Option<FBOId>,
last_frame_used: GpuFrameId,
}
/// Returns true if this texture was used within `threshold` frames of /// the current frame. pubfn used_recently(&self, current_frame_id: GpuFrameId, threshold: usize) -> bool { self.last_frame_used + threshold >= current_frame_id
}
/// Returns the flags for this texture. pubfn flags(&self) -> &TextureFlags {
&self.flags
}
/// Returns a mutable borrow of the flags for this texture. pubfn flags_mut(&mutself) -> &mut TextureFlags {
&mutself.flags
}
/// Returns the number of bytes (generally in GPU memory) that this texture /// consumes. pubfn size_in_bytes(&self) -> usize { let bpp = self.format.bytes_per_pixel() as usize; let w = self.size.width as usize; let h = self.size.height as usize;
bpp * w * h
}
#[cfg(feature = "replay")] pubfn into_external(mutself) -> ExternalTexture { let ext = ExternalTexture {
id: self.id,
target: self.target, // TODO(gw): Support custom UV rect for external textures during captures
uv_rect: TexelRect::new( 0.0, 0.0, self.size.width as f32, self.size.height as f32,
),
image_rendering: ImageRendering::Auto,
}; self.id = 0; // don't complain, moved out
ext
}
}
impl Drop for Texture { fn drop(&mutself) {
debug_assert!(thread::panicking() || self.id == 0);
}
}
impl Drop for PBO { fn drop(&mutself) {
debug_assert!(
thread::panicking() || self.id == 0, "renderer::deinit not called or PBO not returned to pool"
);
}
}
// Compute the digest. Assuming the device has a `ProgramCache`, this // will always be needed, whereas the source is rarely needed.
use std::collections::hash_map::DefaultHasher; use std::hash::Hasher;
// Setup. letmut hasher = DefaultHasher::new(); let gl_version = get_shader_version(&*device.gl());
// Hash the renderer name.
hasher.write(device.capabilities.renderer_name.as_bytes());
let full_name = Self::make_full_name(name, features);
let optimized_source = if device.use_optimized_shaders {
OPTIMIZED_SHADERS.get(&(gl_version, &full_name)).or_else(|| {
warn!("Missing optimized shader source for {}", &full_name);
None
})
} else {
None
};
let source_type = match optimized_source {
Some(source_and_digest) => { // Optimized shader sources are used as-is, without any run-time processing. // The vertex and fragment shaders are different, so must both be hashed. // We use the hashes that were computed at build time, and verify it in debug builds. if cfg!(debug_assertions) { letmut h = DefaultHasher::new();
h.write(source_and_digest.vert_source.as_bytes());
h.write(source_and_digest.frag_source.as_bytes()); let d: ProgramSourceDigest = h.into(); let digest = d.to_string();
debug_assert_eq!(digest, source_and_digest.digest);
hasher.write(digest.as_bytes());
} else {
hasher.write(source_and_digest.digest.as_bytes());
}
ProgramSourceType::Optimized(gl_version)
}
None => { // For non-optimized sources we compute the hash by walking the static strings // in the same order as we would when concatenating the source, to avoid // heap-allocating in the common case. // // Note that we cheat a bit to make the hashing more efficient. First, the only // difference between the vertex and fragment shader is a single deterministic // define, so we don't need to hash both. Second, we precompute the digest of the // expanded source file at build time, and then just hash that digest here. let override_path = device.resource_override_path.as_ref(); let source_and_digest = UNOPTIMIZED_SHADERS.get(&name).expect("Shader not found");
/// Returns a reference to the source digest hash. pubfn source_digest(&self) -> &ProgramSourceDigest {
&self.source_digest
}
}
/// The interfaces that an application can implement to handle ProgramCache update pubtrait ProgramCacheObserver { fn save_shaders_to_disk(&self, entries: Vec<Arc<ProgramBinary>>); fn set_startup_shaders(&self, entries: Vec<Arc<ProgramBinary>>); fn try_load_shader_from_disk(&self, digest: &ProgramSourceDigest, program_cache: &Rc<ProgramCache>); fn notify_program_binary_failed(&self, program_binary: &Arc<ProgramBinary>);
}
struct ProgramCacheEntry { /// The binary.
binary: Arc<ProgramBinary>, /// True if the binary has been linked, i.e. used for rendering.
linked: bool,
}
/// Save any new program binaries to the disk cache, and if startup has /// just completed then write the list of shaders to load on next startup. fn update_disk_cache(&self, startup_complete: bool) { iflet Some(ref handler) = self.program_cache_handler { if !self.pending_entries.borrow().is_empty() { let pending_entries = self.pending_entries.replace(Vec::default());
handler.save_shaders_to_disk(pending_entries);
}
if startup_complete { let startup_shaders = self.entries.borrow().values()
.filter(|e| e.linked).map(|e| e.binary.clone())
.collect::<Vec<_>>();
handler.set_startup_shaders(startup_shaders);
}
}
}
/// Add a new ProgramBinary to the cache. /// This function is typically used after compiling and linking a new program. /// The binary will be saved to disk the next time update_disk_cache() is called. fn add_new_program_binary(&self, program_binary: Arc<ProgramBinary>) { self.pending_entries.borrow_mut().push(program_binary.clone());
let digest = program_binary.source_digest.clone(); let entry = ProgramCacheEntry {
binary: program_binary,
linked: true,
}; self.entries.borrow_mut().insert(digest, entry);
}
/// Load ProgramBinary to ProgramCache. /// The function is typically used to load ProgramBinary from disk. #[cfg(feature = "serialize_program")] pubfn load_program_binary(&self, program_binary: Arc<ProgramBinary>) { let digest = program_binary.source_digest.clone(); let entry = ProgramCacheEntry {
binary: program_binary,
linked: false,
}; self.entries.borrow_mut().insert(digest, entry);
}
/// Returns the number of bytes allocated for shaders in the cache. pubfn report_memory(&self, op: VoidPtrToSizeFn) -> usize { self.entries.borrow().values()
.map(|e| unsafe { op(e.binary.bytes.as_ptr() as *const c_void ) })
.sum()
}
}
#[derive(Debug)] pubstruct Capabilities { /// Whether multisampled render targets are supported. pub supports_multisampling: bool, /// Whether the function `glCopyImageSubData` is available. pub supports_copy_image_sub_data: bool, /// Whether the RGBAF32 textures can be bound to framebuffers. pub supports_color_buffer_float: bool, /// Whether the device supports persistently mapped buffers, via glBufferStorage. pub supports_buffer_storage: bool, /// Whether advanced blend equations are supported. pub supports_advanced_blend_equation: bool, /// Whether dual-source blending is supported. pub supports_dual_source_blending: bool, /// Whether KHR_debug is supported for getting debug messages from /// the driver. pub supports_khr_debug: bool, /// Whether we can configure texture units to do swizzling on sampling. pub supports_texture_swizzle: bool, /// Whether the driver supports uploading to textures from a non-zero /// offset within a PBO. pub supports_nonzero_pbo_offsets: bool, /// Whether the driver supports specifying the texture usage up front. pub supports_texture_usage: bool, /// Whether offscreen render targets can be partially updated. pub supports_render_target_partial_update: bool, /// Whether we can use SSBOs. pub supports_shader_storage_object: bool, /// Whether to enforce that texture uploads be batched regardless of what /// the pref says. pub requires_batched_texture_uploads: Option<bool>, /// Whether we are able to ue glClear to clear regions of an alpha render target. /// If false, we must use a shader to clear instead. pub supports_alpha_target_clears: bool, /// Whether we must perform a full unscissored glClear on alpha targets /// prior to rendering. pub requires_alpha_target_full_clear: bool, /// Whether clearing a render target (immediately after binding it) is faster using a scissor /// rect to clear just the required area, or clearing the entire target without a scissor rect. pub prefers_clear_scissor: bool, /// Whether the driver can correctly invalidate render targets. This can be /// a worthwhile optimization, but is buggy on some devices. pub supports_render_target_invalidate: bool, /// Whether the driver can reliably upload data to R8 format textures. pub supports_r8_texture_upload: bool, /// Whether the extension QCOM_tiled_rendering is supported. pub supports_qcom_tiled_rendering: bool, /// Whether clip-masking is supported natively by the GL implementation /// rather than emulated in shaders. pub uses_native_clip_mask: bool, /// Whether anti-aliasing is supported natively by the GL implementation /// rather than emulated in shaders. pub uses_native_antialiasing: bool, /// Whether the extension GL_OES_EGL_image_external_essl3 is supported. If true, external /// textures can be used as normal. If false, external textures can only be rendered with /// certain shaders, and must first be copied in to regular textures for others. pub supports_image_external_essl3: bool, /// Whether the VAO must be rebound after an attached VBO has been orphaned. pub requires_vao_rebind_after_orphaning: bool, /// The name of the renderer, as reported by GL pub renderer_name: String,
}
/// A refcounted depth target, which may be shared by multiple textures across /// the device. struct SharedDepthTarget { /// The Render Buffer Object representing the depth target.
rbo_id: RBOId, /// Reference count. When this drops to zero, the RBO is deleted.
refcount: usize,
}
#[cfg(debug_assertions)] impl Drop for SharedDepthTarget { fn drop(&mutself) {
debug_assert!(thread::panicking() || self.refcount == 0);
}
}
/// Describes for which texture formats to use the glTexStorage* /// family of functions. #[derive(PartialEq, Debug)] enum TexStorageUsage {
Never,
NonBGRA8,
Always,
}
/// Describes a required alignment for a stride, /// which can either be represented in bytes or pixels. #[derive(Copy, Clone, Debug)] pubenum StrideAlignment {
Bytes(NonZeroUsize),
Pixels(NonZeroUsize),
}
// We get 24 bits of Z value - use up 22 bits of it to give us // 4 bits to account for GPU issues. This seems to manifest on // some GPUs under certain perspectives due to z interpolation // precision problems. const RESERVE_DEPTH_BITS: i32 = 2;
pubstruct Device {
gl: Rc<dyn gl::Gl>,
/// If non-None, |gl| points to a profiling wrapper, and this points to the /// underling Gl instance.
base_gl: Option<Rc<dyn gl::Gl>>,
/// Track depth state for assertions. Note that the default FBO has depth, /// so this defaults to true.
depth_available: bool,
upload_method: UploadMethod,
use_batched_texture_uploads: bool, /// Whether to use draw calls instead of regular blitting commands. /// /// Note: this currently only applies to the batched texture uploads /// path.
use_draw_calls_for_texture_copy: bool, /// Number of pixels below which we prefer batched uploads.
batched_upload_threshold: i32,
// HW or API capabilities
capabilities: Capabilities,
/// Map from texture dimensions to shared depth buffers for render targets. /// /// Render targets often have the same width/height, so we can save memory /// by sharing these across targets.
depth_targets: FastHashMap<DeviceIntSize, SharedDepthTarget>,
// Frame counter. This is used to map between CPU // frames and GPU frames.
frame_id: GpuFrameId,
/// When to use glTexStorage*. We prefer this over glTexImage* because it /// guarantees that mipmaps won't be generated (which they otherwise are on /// some drivers, particularly ANGLE). However, it is not always supported /// at all, or for BGRA8 format. If it's not supported for the required /// format, we fall back to glTexImage*.
texture_storage_usage: TexStorageUsage,
/// Required stride alignment for pixel transfers. This may be required for /// correctness reasons due to driver bugs, or for performance reasons to /// ensure we remain on the fast-path for transfers.
required_pbo_stride: StrideAlignment,
/// Whether we must ensure the source strings passed to glShaderSource() /// are null-terminated, to work around driver bugs.
requires_null_terminated_shader_source: bool,
/// Whether we must unbind any texture from GL_TEXTURE_EXTERNAL_OES before /// binding to GL_TEXTURE_2D, to work around an android emulator bug.
requires_texture_external_unbind: bool,
///
is_software_webrender: bool,
// GL extensions
extensions: Vec<String>,
/// Dumps the source of the shader with the given name
dump_shader_source: Option<String>,
surface_origin_is_top_left: bool,
/// A debug boolean for tracking if the shader program has been set after /// a blend mode change. /// /// This is needed for compatibility with next-gen /// GPU APIs that switch states using "pipeline object" that bundles /// together the blending state with the shader. /// /// Having the constraint of always binding the shader last would allow /// us to have the "pipeline object" bound at that time. Without this /// constraint, we'd either have to eagerly bind the "pipeline object" /// on changing either the shader or the blend more, or lazily bind it /// at draw call time, neither of which is desirable. #[cfg(debug_assertions)]
shader_is_ready: bool,
// count created/deleted textures to report in the profiler. pub textures_created: u32, pub textures_deleted: u32,
}
/// Contains the parameters necessary to bind a draw target. #[derive(Clone, Copy, Debug)] pubenum DrawTarget { /// Use the device's default draw target, with the provided dimensions, /// which are used to set the viewport.
Default { /// Target rectangle to draw.
rect: FramebufferIntRect, /// Total size of the target.
total_size: FramebufferIntSize,
surface_origin_is_top_left: bool,
}, /// Use the provided texture.
Texture { /// Size of the texture in pixels
dimensions: DeviceIntSize, /// Whether to draw with the texture's associated depth target
with_depth: bool, /// FBO that corresponds to the selected layer / depth mode
fbo_id: FBOId, /// Native GL texture ID
id: gl::GLuint, /// Native GL texture target
target: gl::GLuint,
}, /// Use an FBO attached to an external texture.
External {
fbo: FBOId,
size: FramebufferIntSize,
}, /// An OS compositor surface
NativeSurface {
offset: DeviceIntPoint,
external_fbo_id: u32,
dimensions: DeviceIntSize,
},
}
/// Given a scissor rect, convert it to the right coordinate space /// depending on the draw target kind. If no scissor rect was supplied, /// returns a scissor rect that encloses the entire render target. pubfn build_scissor_rect(
&self,
scissor_rect: Option<DeviceIntRect>,
) -> FramebufferIntRect { let dimensions = self.dimensions();
/// Contains the parameters necessary to bind a texture-backed read target. #[derive(Clone, Copy, Debug)] pubenum ReadTarget { /// Use the device's default draw target.
Default, /// Use the provided texture,
Texture { /// ID of the FBO to read from.
fbo_id: FBOId,
}, /// Use an FBO attached to an external texture.
External {
fbo: FBOId,
}, /// An FBO bound to a native (OS compositor) surface
NativeSurface {
fbo_id: FBOId,
offset: DeviceIntPoint,
},
}
/// Parses the major, release, and patch versions from a GL_VERSION string on /// Mali devices. For example, for the version string /// "OpenGL ES 3.2 v1.r36p0-01eac0.28ab3a577f105e026887e2b4c93552fb" this /// returns Some((1, 36, 0)). Returns None if the version cannot be parsed. fn parse_mali_version(version_string: &str) -> Option<(u32, u32, u32)> { let (_prefix, version_string) = version_string.split_once("v")?; let (v_str, version_string) = version_string.split_once(".r")?; let v = v_str.parse().ok()?;
let (r_str, version_string) = version_string.split_once("p")?; let r = r_str.parse().ok()?;
// Not all devices have the trailing string following the "p" number. let (p_str, _) = version_string.split_once("-").unwrap_or((version_string, "")); let p = p_str.parse().ok()?;
Some((v, r, p))
}
/// Returns whether this GPU belongs to the Mali Midgard family fn is_mali_midgard(renderer_name: &str) -> bool {
renderer_name.starts_with("Mali-T")
}
/// Returns whether this GPU belongs to the Mali Bifrost family fn is_mali_bifrost(renderer_name: &str) -> bool {
renderer_name == "Mali-G31"
|| renderer_name == "Mali-G51"
|| renderer_name == "Mali-G71"
|| renderer_name == "Mali-G52"
|| renderer_name == "Mali-G72"
|| renderer_name == "Mali-G76"
}
/// Returns whether this GPU belongs to the Mali Valhall family fn is_mali_valhall(renderer_name: &str) -> bool { // As new Valhall GPUs may be released in the future we match all Mali-G models, apart from // Bifrost models (of which we don't expect any new ones to be released)
renderer_name.starts_with("Mali-G") && !is_mali_bifrost(renderer_name)
} #[inline(never)] fn gl_error_string(code: u32) -> &'static str { match code {
gl::INVALID_ENUM => "GL_INVALID_ENUM",
gl::INVALID_VALUE => "GL_INVALID_VALUE",
gl::INVALID_OPERATION => "GL_INVALID_OPERATION",
gl::STACK_OVERFLOW => "GL_STACK_OVERFLOW",
gl::STACK_UNDERFLOW => "GL_STACK_UNDERFLOW",
gl::OUT_OF_MEMORY => "GL_OUT_OF_MEMORY",
gl::INVALID_FRAMEBUFFER_OPERATION => "GL_INVALID_FRAMEBUFFER_OPERATION", 0x507 => "GL_CONTEXT_LOST",
_ => "(unknown error code)",
}
}
// We cap the max texture size at 16384. Some hardware report higher // capabilities but get very unstable with very large textures. // Bug 1702494 tracks re-evaluating this cap. let max_texture_size = max_texture_size[0].min(16384);
let renderer_name = gl.get_string(gl::RENDERER);
info!("Renderer: {}", renderer_name); let version_string = gl.get_string(gl::VERSION);
info!("Version: {}", version_string);
info!("Max texture size: {}", max_texture_size);
letmut extension_count = [0]; unsafe {
gl.get_integer_v(gl::NUM_EXTENSIONS, &mut extension_count);
} let extension_count = extension_count[0] as gl::GLuint; letmut extensions = Vec::new(); for i in0 .. extension_count {
extensions.push(gl.get_string_i(gl::EXTENSIONS, i));
}
// We block this on Mali Valhall GPUs as the extension's functions always return // GL_OUT_OF_MEMORY, causing us to panic in debug builds. let supports_khr_debug = supports_extension(&extensions, "GL_KHR_debug")
&& !is_mali_valhall(&renderer_name);
// On debug builds, assert that each GL call is error-free. We don't do // this on release builds because the synchronous call can stall the // pipeline. if panic_on_gl_error || cfg!(debug_assertions) {
gl = gl::ErrorReactingGl::wrap(gl, move |gl, name, code| { if supports_khr_debug { Self::log_driver_messages(gl);
} let err_name = gl_error_string(code);
error!("Caught GL error 0x{:x} {} at {}", code, err_name, name);
panic!("Caught GL error 0x{:x} {} at {}", code, err_name, name);
});
}
if supports_extension(&extensions, "GL_ANGLE_provoking_vertex") {
gl.provoking_vertex_angle(gl::FIRST_VERTEX_CONVENTION);
}
let supports_texture_usage = supports_extension(&extensions, "GL_ANGLE_texture_usage");
// Our common-case image data in Firefox is BGRA, so we make an effort // to use BGRA as the internal texture storage format to avoid the need // to swizzle during upload. Currently we only do this on GLES (and thus // for Windows, via ANGLE). // // On Mac, Apple docs [1] claim that BGRA is a more efficient internal // format, but they don't support it with glTextureStorage. As a workaround, // we pretend that it's RGBA8 for the purposes of texture transfers, // but swizzle R with B for the texture sampling. // // We also need our internal format types to be sized, since glTexStorage* // will reject non-sized internal format types. // // Unfortunately, with GL_EXT_texture_format_BGRA8888, BGRA8 is not a // valid internal format (for glTexImage* or glTexStorage*) unless // GL_EXT_texture_storage is also available [2][3], which is usually // not the case on GLES 3 as the latter's functionality has been // included by default but the former has not been updated. // The extension is available on ANGLE, but on Android this usually // means we must fall back to using unsized BGRA and glTexImage*. // // Overall, we have the following factors in play when choosing the formats: // - with glTexStorage, the internal format needs to match the external format, // or the driver would have to do the conversion, which is slow // - on desktop GL, there is no BGRA internal format. However, initializing // the textures with glTexImage as RGBA appears to use BGRA internally, // preferring BGRA external data [4]. // - when glTexStorage + BGRA internal format is not supported, // and the external data is BGRA, we have the following options: // 1. use glTexImage with RGBA internal format, this costs us VRAM for mipmaps // 2. use glTexStorage with RGBA internal format, this costs us the conversion by the driver // 3. pretend we are uploading RGBA and set up the swizzling of the texture unit - this costs us batch breaks // // [1] https://developer.apple.com/library/archive/documentation/ // GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_texturedata/ // opengl_texturedata.html#//apple_ref/doc/uid/TP40001987-CH407-SW22 // [2] https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_format_BGRA8888.txt // [3] https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_storage.txt // [4] http://http.download.nvidia.com/developer/Papers/2005/Fast_Texture_Transfers/Fast_Texture_Transfers.pdf
// On the android emulator glTexImage fails to create textures larger than 3379. // So we must use glTexStorage instead. See bug 1591436. let is_emulator = renderer_name.starts_with("Android Emulator"); let avoid_tex_image = is_emulator; letmut gl_version = [0; 2]; unsafe {
gl.get_integer_v(gl::MAJOR_VERSION, &mut gl_version[0..1]);
gl.get_integer_v(gl::MINOR_VERSION, &mut gl_version[1..2]);
}
info!("GL context {:?} {}.{}", gl.get_type(), gl_version[0], gl_version[1]);
// We block texture storage on mac because it doesn't support BGRA let supports_texture_storage = allow_texture_storage_support && !cfg!(target_os = "macos") && match gl.get_type() {
gl::GlType::Gl => supports_extension(&extensions, "GL_ARB_texture_storage"),
gl::GlType::Gles => true,
};
// The GL_EXT_texture_format_BGRA8888 extension allows us to use BGRA as an internal format // with glTexImage on GLES. However, we can only use BGRA8 as an internal format for // glTexStorage when GL_EXT_texture_storage is also explicitly supported. This is because // glTexStorage was added in GLES 3, but GL_EXT_texture_format_BGRA8888 was written against // GLES 2 and GL_EXT_texture_storage. // To complicate things even further, some Intel devices claim to support both extensions // but in practice do not allow BGRA to be used with glTexStorage. let supports_gles_bgra = supports_extension(&extensions, "GL_EXT_texture_format_BGRA8888"); let supports_texture_storage_with_gles_bgra = supports_gles_bgra
&& supports_extension(&extensions, "GL_EXT_texture_storage")
&& !renderer_name.starts_with("Intel(R) HD Graphics for BayTrail")
&& !renderer_name.starts_with("Intel(R) HD Graphics for Atom(TM) x5/x7");
let supports_texture_swizzle = allow_texture_swizzling && match gl.get_type() { // see https://www.g-truc.net/post-0734.html
gl::GlType::Gl => gl_version >= [3, 3] ||
supports_extension(&extensions, "GL_ARB_texture_swizzle"),
gl::GlType::Gles => true,
};
let (color_formats, bgra_formats, bgra_pixel_type, bgra8_sampling_swizzle, texture_storage_usage) = match gl.get_type() { // There is `glTexStorage`, use it and expect RGBA on the input.
gl::GlType::Gl if supports_texture_storage && supports_texture_swizzle => (
TextureFormatPair::from(ImageFormat::RGBA8),
TextureFormatPair { internal: gl::RGBA8, external: gl::RGBA },
gl::UNSIGNED_BYTE,
Swizzle::Bgra, // pretend it's RGBA, rely on swizzling
TexStorageUsage::Always
), // There is no `glTexStorage`, upload as `glTexImage` with BGRA input.
gl::GlType::Gl => (
TextureFormatPair { internal: ImageFormat::BGRA8, external: ImageFormat::BGRA8 },
TextureFormatPair { internal: gl::RGBA, external: gl::BGRA },
gl::UNSIGNED_INT_8_8_8_8_REV,
Swizzle::Rgba, // converted on uploads by the driver, no swizzling needed
TexStorageUsage::Never
), // glTexStorage is always supported in GLES 3, but because the GL_EXT_texture_storage // extension is supported we can use glTexStorage with BGRA8 as the internal format. // Prefer BGRA textures over RGBA.
gl::GlType::Gles if supports_texture_storage_with_gles_bgra => (
TextureFormatPair::from(ImageFormat::BGRA8),
TextureFormatPair { internal: gl::BGRA8_EXT, external: gl::BGRA_EXT },
gl::UNSIGNED_BYTE,
Swizzle::Rgba, // no conversion needed
TexStorageUsage::Always,
), // BGRA is not supported as an internal format with glTexStorage, therefore we will // use RGBA textures instead and pretend BGRA data is RGBA when uploading. // The swizzling will happen at the texture unit.
gl::GlType::Gles if supports_texture_swizzle => (
TextureFormatPair::from(ImageFormat::RGBA8),
TextureFormatPair { internal: gl::RGBA8, external: gl::RGBA },
gl::UNSIGNED_BYTE,
Swizzle::Bgra, // pretend it's RGBA, rely on swizzling
TexStorageUsage::Always,
), // BGRA is not supported as an internal format with glTexStorage, and we cannot use // swizzling either. Therefore prefer BGRA textures over RGBA, but use glTexImage // to initialize BGRA textures. glTexStorage can still be used for other formats.
gl::GlType::Gles if supports_gles_bgra && !avoid_tex_image => (
TextureFormatPair::from(ImageFormat::BGRA8),
TextureFormatPair::from(gl::BGRA_EXT),
gl::UNSIGNED_BYTE,
Swizzle::Rgba, // no conversion needed
TexStorageUsage::NonBGRA8,
), // Neither BGRA or swizzling are supported. GLES does not allow format conversion // during upload so we must use RGBA textures and pretend BGRA data is RGBA when // uploading. Images may be rendered incorrectly as a result.
gl::GlType::Gles => {
warn!("Neither BGRA or texture swizzling are supported. Images may be rendered incorrectly.");
(
TextureFormatPair::from(ImageFormat::RGBA8),
TextureFormatPair { internal: gl::RGBA8, external: gl::RGBA },
gl::UNSIGNED_BYTE,
Swizzle::Rgba,
TexStorageUsage::Always,
)
}
};
let is_software_webrender = renderer_name.starts_with("Software WebRender"); let upload_method = if is_software_webrender { // Uploads in SWGL generally reduce to simple memory copies.
UploadMethod::Immediate
} else {
upload_method
}; // Prefer 24-bit depth format. While 16-bit depth also works, it may exhaust depth ids easily. let depth_format = gl::DEPTH_COMPONENT24;
// On Mali-T devices glCopyImageSubData appears to stall the pipeline until any pending // renders to the source texture have completed. On Mali-G, it has been observed to // indefinitely hang in some circumstances. Using an alternative such as glBlitFramebuffer // is preferable on such devices, so pretend we don't support glCopyImageSubData. // See bugs 1669494 and 1677757. let supports_copy_image_sub_data = if renderer_name.starts_with("Mali") { false
} else {
supports_extension(&extensions, "GL_EXT_copy_image") ||
supports_extension(&extensions, "GL_ARB_copy_image")
};
// We have seen crashes on x86 PowerVR Rogue G6430 devices during GPU cache // updates using the scatter shader. It seems likely that GL_EXT_color_buffer_float // is broken. See bug 1709408. let is_x86_powervr_rogue_g6430 = renderer_name.starts_with("PowerVR Rogue G6430")
&& cfg!(target_arch = "x86"); let supports_color_buffer_float = match gl.get_type() {
gl::GlType::Gl => true,
gl::GlType::Gles if is_x86_powervr_rogue_g6430 => false,
gl::GlType::Gles => supports_extension(&extensions, "GL_EXT_color_buffer_float"),
};
let is_adreno = renderer_name.starts_with("Adreno");
// There appears to be a driver bug on older versions of the Adreno // driver which prevents usage of persistenly mapped buffers. // See bugs 1678585 and 1683936. // TODO: only disable feature for affected driver versions. let supports_buffer_storage = if is_adreno { false
} else {
supports_extension(&extensions, "GL_EXT_buffer_storage") ||
supports_extension(&extensions, "GL_ARB_buffer_storage")
};
// KHR_blend_equation_advanced renders incorrectly on Adreno // devices. This has only been confirmed up to Adreno 5xx, and has been // fixed for Android 9, so this condition could be made more specific. let supports_advanced_blend_equation =
supports_extension(&extensions, "GL_KHR_blend_equation_advanced") &&
!is_adreno;
let supports_dual_source_blending = match gl.get_type() {
gl::GlType::Gl => supports_extension(&extensions,"GL_ARB_blend_func_extended") &&
supports_extension(&extensions,"GL_ARB_explicit_attrib_location"),
gl::GlType::Gles => supports_extension(&extensions,"GL_EXT_blend_func_extended"),
};
// Software webrender relies on the unoptimized shader source. let use_optimized_shaders = use_optimized_shaders && !is_software_webrender;
// On the android emulator, and possibly some Mali devices, glShaderSource // can crash if the source strings are not null-terminated. // See bug 1591945 and bug 1799722. // Likewise on Lenovo devices with Adreno 750 GPUs we have seen glCompileShader // failures and subsequent crashes due to glGetShaderInfoLog returning invalid // UTF-8. See bug 2014925. let requires_null_terminated_shader_source = is_emulator || renderer_name == "Mali-T628"
|| renderer_name == "Mali-T720" || renderer_name == "Mali-T760"
|| renderer_name == "Mali-G57" || renderer_name == "Adreno (TM) 750";
// The android emulator gets confused if you don't explicitly unbind any texture // from GL_TEXTURE_EXTERNAL_OES before binding another to GL_TEXTURE_2D. See bug 1636085. let requires_texture_external_unbind = is_emulator;
let is_macos = cfg!(target_os = "macos"); // && renderer_name.starts_with("AMD"); // (XXX: we apply this restriction to all GPUs to handle switching)
let is_windows_angle = cfg!(target_os = "windows")
&& renderer_name.starts_with("ANGLE"); let is_adreno_3xx = renderer_name.starts_with("Adreno (TM) 3");
// Some GPUs require the stride of the data during texture uploads to be // aligned to certain requirements, either for correctness or performance // reasons. let required_pbo_stride = if is_adreno_3xx { // On Adreno 3xx, alignments of < 128 bytes can result in corrupted // glyphs. See bug 1696039.
StrideAlignment::Bytes(NonZeroUsize::new(128).unwrap())
} elseif is_adreno { // On later Adreno devices it must be a multiple of 64 *pixels* to // hit the fast path, meaning value in bytes varies with the texture // format. This is purely an optimization.
StrideAlignment::Pixels(NonZeroUsize::new(64).unwrap())
} elseif is_macos { // On AMD Mac, it must always be a multiple of 256 bytes. // We apply this restriction to all GPUs to handle switching
StrideAlignment::Bytes(NonZeroUsize::new(256).unwrap())
} elseif is_windows_angle { // On ANGLE-on-D3D, PBO texture uploads get incorrectly truncated // if the stride is greater than the width * bpp.
StrideAlignment::Bytes(NonZeroUsize::new(1).unwrap())
} else { // Other platforms may have similar requirements and should be added // here. The default value should be 4 bytes.
StrideAlignment::Bytes(NonZeroUsize::new(4).unwrap())
};
// On AMD Macs there is a driver bug which causes some texture uploads // from a non-zero offset within a PBO to fail. See bug 1603783. let supports_nonzero_pbo_offsets = !is_macos;
// We have encountered several issues when only partially updating render targets on a // variety of Mali GPUs. As a precaution avoid doing so on all Midgard and Bifrost GPUs. // Valhall (eg Mali-Gx7 onwards) appears to be unaffected. See bug 1691955, bug 1558374, // and bug 1663355. // We have Additionally encountered issues on PowerVR D-Series. See bug 2005312. let supports_render_target_partial_update = !is_mali_midgard(&renderer_name)
&& !is_mali_bifrost(&renderer_name)
&& !renderer_name.starts_with("PowerVR D-Series");
let supports_shader_storage_object = match gl.get_type() { // see https://www.g-truc.net/post-0734.html
gl::GlType::Gl => supports_extension(&extensions, "GL_ARB_shader_storage_buffer_object"),
gl::GlType::Gles => gl_version >= [3, 1],
};
// SWGL uses swgl_clipMask() instead of implementing clip-masking in shaders. // This allows certain shaders to potentially bypass the more expensive alpha- // pass variants if they know the alpha-pass was only required to deal with // clip-masking. let uses_native_clip_mask = is_software_webrender;
// SWGL uses swgl_antiAlias() instead of implementing anti-aliasing in shaders. // As above, this allows bypassing certain alpha-pass variants. let uses_native_antialiasing = is_software_webrender;
// If running on android with a mesa driver (eg intel chromebooks), parse the mesa version. letmut android_mesa_version = None; if cfg!(target_os = "android") && renderer_name.starts_with("Mesa") { iflet Some((_, mesa_version)) = version_string.split_once("Mesa ") { iflet Some((major_str, _)) = mesa_version.split_once(".") { iflet Ok(major) = major_str.parse::<i32>() {
android_mesa_version = Some(major);
}
}
}
}
// If the device supports OES_EGL_image_external_essl3 we can use it to render // external images. If not, we must use the ESSL 1.0 OES_EGL_image_external // extension instead. // Mesa versions prior to 20.0 do not implement textureSize(samplerExternalOES), // so we must use the fallback path. let supports_image_external_essl3 = match android_mesa_version {
Some(major) if major < 20 => false,
_ => supports_extension(&extensions, "GL_OES_EGL_image_external_essl3"),
};
letmut requires_batched_texture_uploads = None; if is_software_webrender { // No benefit to batching texture uploads with swgl.
requires_batched_texture_uploads = Some(false);
} elseif renderer_name.starts_with("Mali-G") { // On Mali-Gxx the driver really struggles with many small texture uploads, // and handles fewer, larger uploads better.
requires_batched_texture_uploads = Some(true);
}
// On Mali-Txxx devices we have observed crashes during draw calls when rendering // to an alpha target immediately after using glClear to clear regions of it. // Using a shader to clear the regions avoids the crash. See bug 1638593. // On Adreno 510 devices we have seen garbage being used as masks when clearing // alpha targets with glClear. Using quads to clear avoids this. See bug 1941154. let is_adreno_510 = renderer_name.starts_with("Adreno (TM) 510"); let supports_alpha_target_clears = !is_mali_midgard(&renderer_name) && !is_adreno_510;
// On Adreno 4xx devices with older drivers we have seen render tasks to alpha targets have // no effect unless the target is fully cleared prior to rendering. See bug 1714227. let is_adreno_4xx = renderer_name.starts_with("Adreno (TM) 4"); let requires_alpha_target_full_clear = is_adreno_4xx;
// Testing on Intel and nVidia GPUs, as well as software webrender, showed large performance // wins applying a scissor rect when clearing render targets. Assume this is the best // default. On mobile GPUs, however, it can be much more efficient to clear the entire // render target. For now, enable the scissor everywhere except Android hardware // webrender. We can tweak this further if needs be. let prefers_clear_scissor = !cfg!(target_os = "android") || is_software_webrender;
letmut supports_render_target_invalidate = true;
// On PowerVR Rogue devices we have seen that invalidating render targets after we are done // with them can incorrectly cause pending renders to be written to different targets // instead. See bug 1719345. let is_powervr_rogue = renderer_name.starts_with("PowerVR Rogue"); if is_powervr_rogue {
supports_render_target_invalidate = false;
}
// On Mali Valhall devices with a driver version v1.r36p0 we have seen that invalidating // render targets can result in image corruption, perhaps due to subsequent reuses of the // render target not correctly reinitializing them to a valid state. See bug 1787520. if is_mali_valhall(&renderer_name) { match parse_mali_version(&version_string) {
Some(version) if version >= (1, 36, 0) => supports_render_target_invalidate = false,
_ => {}
}
}
// On Linux we we have seen uploads to R8 format textures result in // corruption on some AMD cards. // See https://bugzilla.mozilla.org/show_bug.cgi?id=1687554#c13 let supports_r8_texture_upload = if cfg!(target_os = "linux")
&& renderer_name.starts_with("AMD Radeon RX")
{ false
} else { true
};
let supports_qcom_tiled_rendering = if is_adreno && version_string.contains("V@0490") { // We have encountered rendering errors on a variety of Adreno GPUs specifically on // driver version V@0490, so block this extension on that driver version. See bug 1828248. false
} elseif renderer_name == "Adreno (TM) 308" { // And specifically on Areno 308 GPUs we have encountered rendering errors on driver // versions V@331, V@415, and V@0502. We presume this therefore affects all driver // versions. See bug 1843749 and bug 1847319. false
} else {
supports_extension(&extensions, "GL_QCOM_tiled_rendering")
};
// On some Adreno 3xx devices the vertex array object must be unbound and rebound after // an attached buffer has been orphaned. let requires_vao_rebind_after_orphaning = is_adreno_3xx;
/// Ensures that the maximum texture size is less than or equal to the /// provided value. If the provided value is less than the value supported /// by the driver, the latter is used. pubfn clamp_max_texture_size(&mutself, size: i32) { self.max_texture_size = self.max_texture_size.min(size);
}
/// Returns the limit on texture dimensions (width or height). pubfn max_texture_size(&self) -> i32 { self.max_texture_size
}
// See gpu_types.rs where we declare the number of possible documents and // number of items per document. This should match up with that. pubfn max_depth_ids(&self) -> i32 { return1 << (self.depth_bits() - RESERVE_DEPTH_BITS);
}
pubfn ortho_near_plane(&self) -> f32 { return -self.max_depth_ids() as f32;
}
pubfn reset_state(&mutself) { for i in0 .. self.bound_textures.len() { self.bound_textures[i] = 0; self.gl.active_texture(gl::TEXTURE0 + i as gl::GLuint); self.gl.bind_texture(gl::TEXTURE_2D, 0);
}
letmut new_source = Cow::from(source.as_str()); // Ensure the source strings we pass to glShaderSource are // null-terminated on buggy platforms. ifself.requires_null_terminated_shader_source {
new_source.to_mut().push('\0');
}
self.gl.shader_source(id, &[new_source.as_bytes()]); self.gl.compile_shader(id); let log = self.gl.get_shader_info_log(id); letmut status = [0]; unsafe { self.gl.get_shader_iv(id, gl::COMPILE_STATUS, &mut status);
} if status[0] == 0 { let type_str = match shader_type {
gl::VERTEX_SHADER => "vertex",
gl::FRAGMENT_SHADER => "fragment",
_ => panic!("Unexpected shader type {:x}", shader_type),
};
error!("Failed to compile {} shader: {}\n{}", type_str, name, log); #[cfg(debug_assertions)] Self::print_shader_errors(source, &log);
Err(ShaderError::Compilation(name.to_string(), log))
} else { if !log.is_empty() {
warn!("Warnings detected on shader: {}\n{}", name, log);
}
Ok(id)
}
}
// If our profiler state has changed, apply or remove the profiling // wrapper from our GL context. let being_profiled = profiler::thread_is_being_profiled(); let using_wrapper = self.base_gl.is_some();
// We can usually unwind driver stacks on OSes other than Android, so we don't need to // manually instrument gl calls there. Timestamps can be pretty expensive on Windows (2us // each and perhaps an opportunity to be descheduled?) which makes the profiles gathered // with this turned on less useful so only profile on ARM Android. if cfg!(any(target_arch = "arm", target_arch = "aarch64"))
&& cfg!(target_os = "android")
&& being_profiled
&& !using_wrapper
{ fn note(name: &str, duration: Duration) {
profiler::add_text_marker("OpenGL Calls", name, duration);
} let threshold = Duration::from_millis(1); let wrapped = gl::ProfilingGl::wrap(self.gl.clone(), threshold, note); let base = mem::replace(&mutself.gl, wrapped); self.base_gl = Some(base);
} elseif !being_profiled && using_wrapper { self.gl = self.base_gl.take().unwrap();
}
// Retrieve the currently set FBO. letmut default_read_fbo = [0]; unsafe { self.gl.get_integer_v(gl::READ_FRAMEBUFFER_BINDING, &mut default_read_fbo);
} self.default_read_fbo = FBOId(default_read_fbo[0] as gl::GLuint); letmut default_draw_fbo = [0]; unsafe { self.gl.get_integer_v(gl::DRAW_FRAMEBUFFER_BINDING, &mut default_draw_fbo);
} self.default_draw_fbo = FBOId(default_draw_fbo[0] as gl::GLuint);
// Shader state self.bound_program = 0; self.gl.use_program(0);
// Reset common state self.reset_state();
// Pixel op state self.gl.pixel_store_i(gl::UNPACK_ALIGNMENT, 1); self.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, 0);
// Default is sampler 0, always self.gl.active_texture(gl::TEXTURE0);
/// Creates an unbound FBO object. Additional attachment API calls are /// required to make it complete. pubfn create_fbo(&mutself) -> FBOId {
FBOId(self.gl.gen_framebuffers(1)[0])
}
/// Creates an FBO with the given texture bound as the color attachment. pubfn create_fbo_for_external_texture(&mutself, texture_id: u32) -> FBOId { let fbo = self.create_fbo();
fbo.bind(self.gl(), FBOTarget::Draw); self.gl.framebuffer_texture_2d(
gl::DRAW_FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
texture_id, 0,
);
debug_assert_eq!( self.gl.check_frame_buffer_status(gl::DRAW_FRAMEBUFFER),
gl::FRAMEBUFFER_COMPLETE, "Incomplete framebuffer",
); self.bound_draw_fbo.bind(self.gl(), FBOTarget::Draw);
fbo
}
/// Link a program, attaching the supplied vertex format. /// /// If `create_program()` finds a binary shader on disk, it will kick /// off linking immediately, which some drivers (notably ANGLE) run /// in parallel on background threads. As such, this function should /// ideally be run sometime later, to give the driver time to do that /// before blocking due to an API call accessing the shader. /// /// This generally means that the first run of the application will have /// to do a bunch of blocking work to compile the shader from source, but /// subsequent runs should load quickly. pubfn link_program(
&mutself,
program: &mut Program,
descriptor: &VertexDescriptor,
) -> Result<(), ShaderError> {
profile_scope!("compile shader");
let _guard = CrashAnnotatorGuard::new(
&self.crash_annotator,
CrashAnnotation::CompileShader,
&program.source_info.full_name_cstr
);
assert!(!program.is_initialized()); letmut build_program = true; let info = &program.source_info;
// See if we hit the binary shader cache iflet Some(ref cached_programs) = self.cached_programs { // If the shader is not in the cache, attempt to load it from disk if cached_programs.entries.borrow().get(&program.source_info.digest).is_none() { iflet Some(ref handler) = cached_programs.program_cache_handler {
handler.try_load_shader_from_disk(&program.source_info.digest, cached_programs); iflet Some(entry) = cached_programs.entries.borrow().get(&program.source_info.digest) { self.gl.program_binary(program.id, entry.binary.format, &entry.binary.bytes);
}
}
}
iflet Some(entry) = cached_programs.entries.borrow_mut().get_mut(&info.digest) { letmut link_status = [0]; unsafe { self.gl.get_program_iv(program.id, gl::LINK_STATUS, &mut link_status);
} if link_status[0] == 0 { let error_log = self.gl.get_program_info_log(program.id);
error!( "Failed to load a program object with a program binary: {} renderer {}\n{}",
&info.base_filename, self.capabilities.renderer_name,
error_log
); iflet Some(ref program_cache_handler) = cached_programs.program_cache_handler {
program_cache_handler.notify_program_binary_failed(&entry.binary);
}
} else {
entry.linked = true;
build_program = false;
}
}
}
// If not, we need to do a normal compile + link pass. if build_program { // Compile the vertex shader let vs_source = info.compute_source(self, ShaderKind::Vertex); let vs_id = matchself.compile_shader(&info.full_name(), gl::VERTEX_SHADER, &vs_source) {
Ok(vs_id) => vs_id,
Err(err) => return Err(err),
};
// Compile the fragment shader let fs_source = info.compute_source(self, ShaderKind::Fragment); let fs_id = matchself.compile_shader(&info.full_name(), gl::FRAGMENT_SHADER, &fs_source) {
Ok(fs_id) => fs_id,
Err(err) => { self.gl.delete_shader(vs_id); return Err(err);
}
};
// Check if shader source should be dumped if Some(info.base_filename) == self.dump_shader_source.as_ref().map(String::as_ref) { let path = std::path::Path::new(info.base_filename);
std::fs::write(path.with_extension("vert"), vs_source).unwrap();
std::fs::write(path.with_extension("frag"), fs_source).unwrap();
}
// GL recommends detaching and deleting shaders once the link // is complete (whether successful or not). This allows the driver // to free any memory associated with the parsing and compilation. self.gl.detach_shader(program.id, vs_id); self.gl.detach_shader(program.id, fs_id); self.gl.delete_shader(vs_id); self.gl.delete_shader(fs_id);
letmut link_status = [0]; unsafe { self.gl.get_program_iv(program.id, gl::LINK_STATUS, &mut link_status);
} if link_status[0] == 0 { let error_log = self.gl.get_program_info_log(program.id);
error!( "Failed to link shader program: {}\n{}",
&info.base_filename,
error_log
); self.gl.delete_program(program.id); return Err(ShaderError::Link(info.base_filename.to_owned(), error_log));
}
iflet Some(ref cached_programs) = self.cached_programs { if !cached_programs.entries.borrow().contains_key(&info.digest) { let (buffer, format) = self.gl.get_program_binary(program.id); if buffer.len() > 0 { let binary = Arc::new(ProgramBinary::new(buffer, format, info.digest.clone()));
cached_programs.add_new_program_binary(binary);
}
}
}
}
// If we get here, the link succeeded, so get the uniforms.
program.is_initialized = true;
program.u_transform = self.gl.get_uniform_location(program.id, "uTransform");
program.u_texture_size = self.gl.get_uniform_location(program.id, "uTextureSize");
if width > self.max_texture_size || height > self.max_texture_size {
error!("Attempting to allocate a texture of size {}x{} above the limit, trimming", width, height);
width = width.min(self.max_texture_size);
height = height.min(self.max_texture_size);
}
// Set up the texture book-keeping. letmut texture = Texture {
id: self.gl.gen_textures(1)[0],
target: get_gl_target(target),
size: DeviceIntSize::new(width, height),
format,
filter,
active_swizzle: Cell::default(),
fbo: None,
fbo_with_depth: None,
last_frame_used: self.frame_id,
flags: TextureFlags::default(),
}; self.bind_texture(DEFAULT_TEXTURE, &texture, Swizzle::default()); self.set_texture_parameters(texture.target, filter);
ifself.capabilities.supports_texture_usage && render_target.is_some() { self.gl.tex_parameter_i(texture.target, gl::TEXTURE_USAGE_ANGLE, gl::FRAMEBUFFER_ATTACHMENT_ANGLE as gl::GLint);
}
// Allocate storage. let desc = self.gl_describe_format(texture.format);
// Firefox doesn't use mipmaps, but Servo uses them for standalone image // textures images larger than 512 pixels. This is the only case where // we set the filter to trilinear. let mipmap_levels = if texture.filter == TextureFilter::Trilinear { let max_dimension = cmp::max(width, height);
((max_dimension) as f64).log2() as gl::GLint + 1
} else { 1
};
// We never want to upload texture data at the same time as allocating the texture. self.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, 0);
// Use glTexStorage where available, since it avoids allocating // unnecessary mipmap storage and generally improves performance with // stronger invariants. let use_texture_storage = matchself.texture_storage_usage {
TexStorageUsage::Always => true,
TexStorageUsage::NonBGRA8 => texture.format != ImageFormat::BGRA8,
TexStorageUsage::Never => false,
}; if use_texture_storage { self.gl.tex_storage_2d(
texture.target,
mipmap_levels,
desc.internal,
texture.size.width as gl::GLint,
texture.size.height as gl::GLint,
);
} else { self.gl.tex_image_2d(
texture.target, 0,
desc.internal as gl::GLint,
texture.size.width as gl::GLint,
texture.size.height as gl::GLint, 0,
desc.external,
desc.pixel_type,
None,
);
}
// Set up FBOs, if required. iflet Some(rt_info) = render_target { self.init_fbos(&mut texture, false); if rt_info.has_depth { self.init_fbos(&mut texture, true);
}
}
let min_filter = match filter {
TextureFilter::Nearest => gl::NEAREST,
TextureFilter::Linear => gl::LINEAR,
TextureFilter::Trilinear => gl::LINEAR_MIPMAP_LINEAR,
};
self.gl
.tex_parameter_i(target, gl::TEXTURE_MAG_FILTER, mag_filter as gl::GLint); self.gl
.tex_parameter_i(target, gl::TEXTURE_MIN_FILTER, min_filter as gl::GLint);
self.gl
.tex_parameter_i(target, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as gl::GLint); self.gl
.tex_parameter_i(target, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as gl::GLint);
}
/// Copies the entire contents of one texture to another. The dest texture must be at least /// as large as the source texture in each dimension. No scaling is performed, so if the dest /// texture is larger than the source texture then some of its pixels will not be written to. pubfn copy_entire_texture(
&mutself,
dst: &mut Texture,
src: &Texture,
) {
debug_assert!(self.inside_frame);
debug_assert!(dst.size.width >= src.size.width);
debug_assert!(dst.size.height >= src.size.height);
self.copy_texture_sub_region(
src, 0, 0,
dst, 0, 0,
src.size.width as _,
src.size.height as _,
);
}
/// Copies the specified subregion from src_texture to dest_texture. pubfn copy_texture_sub_region(
&mutself,
src_texture: &Texture,
src_x: usize,
src_y: usize,
dest_texture: &Texture,
dest_x: usize,
dest_y: usize,
width: usize,
height: usize,
) { ifself.capabilities.supports_copy_image_sub_data {
assert_ne!(
src_texture.id, dest_texture.id, "glCopyImageSubData's behaviour is undefined if src and dst images are identical and the rectangles overlap."
); unsafe { self.gl.copy_image_sub_data(
src_texture.id,
src_texture.target, 0,
src_x as _,
src_y as _, 0,
dest_texture.id,
dest_texture.target, 0,
dest_x as _,
dest_y as _, 0,
width as _,
height as _, 1,
);
}
} else { let src_offset = FramebufferIntPoint::new(src_x as i32, src_y as i32); let dest_offset = FramebufferIntPoint::new(dest_x as i32, dest_y as i32); let size = FramebufferIntSize::new(width as i32, height as i32);
self.blit_render_target(
ReadTarget::from_texture(src_texture),
FramebufferIntRect::from_origin_and_size(src_offset, size),
DrawTarget::from_texture(dest_texture, false),
FramebufferIntRect::from_origin_and_size(dest_offset, size), // In most cases the filter shouldn't matter, as there is no scaling involved // in the blit. We were previously using Linear, but this caused issues when // blitting RGBAF32 textures on Mali, so use Nearest to be safe.
TextureFilter::Nearest,
);
}
}
/// Notifies the device that the contents of a render target are no longer /// needed. pubfn invalidate_render_target(&mutself, texture: &Texture) { ifself.capabilities.supports_render_target_invalidate { let (fbo, attachments) = if texture.supports_depth() {
(&texture.fbo_with_depth,
&[gl::COLOR_ATTACHMENT0, gl::DEPTH_ATTACHMENT] as &[gl::GLenum])
} else {
(&texture.fbo, &[gl::COLOR_ATTACHMENT0] as &[gl::GLenum])
};
iflet Some(fbo_id) = fbo { let original_bound_fbo = self.bound_draw_fbo; // Note: The invalidate extension may not be supported, in which // case this is a no-op. That's ok though, because it's just a // hint. self.bind_external_draw_target(*fbo_id); self.gl.invalidate_framebuffer(gl::FRAMEBUFFER, attachments); self.bind_external_draw_target(original_bound_fbo);
}
}
}
/// Notifies the device that the contents of the current framebuffer's depth /// attachment is no longer needed. Unlike invalidate_render_target, this can /// be called even when the contents of the colour attachment is still required. /// This should be called before unbinding the framebuffer at the end of a pass, /// to allow tiled GPUs to avoid writing the contents back to memory. pubfn invalidate_depth_target(&mutself) {
assert!(self.depth_available); let attachments = ifself.bound_draw_fbo == self.default_draw_fbo {
&[gl::DEPTH] as &[gl::GLenum]
} else {
&[gl::DEPTH_ATTACHMENT] as &[gl::GLenum]
}; self.gl.invalidate_framebuffer(gl::DRAW_FRAMEBUFFER, attachments);
}
/// Notifies the device that a render target is about to be reused. /// /// This method adds or removes a depth target as necessary. pubfn reuse_render_target<T: Texel>(
&mutself,
texture: &mut Texture,
rt_info: RenderTargetInfo,
) {
texture.last_frame_used = self.frame_id;
// Add depth support if needed. if rt_info.has_depth && !texture.supports_depth() { self.init_fbos(texture, true);
}
}
fn init_fbos(&mutself, texture: &mut Texture, with_depth: bool) { let (fbo, depth_rb) = if with_depth { let depth_target = self.acquire_depth_target(texture.get_dimensions());
(&mut texture.fbo_with_depth, Some(depth_target))
} else {
(&mut texture.fbo, None)
};
// Generate the FBOs.
assert!(fbo.is_none()); let fbo_id = FBOId(*self.gl.gen_framebuffers(1).first().unwrap());
*fbo = Some(fbo_id);
// Bind the FBOs. let original_bound_fbo = self.bound_draw_fbo;
/// Create a shader program and link it immediately. pubfn create_program_linked(
&mutself,
base_filename: &'static str,
features: &[&'static str],
descriptor: &VertexDescriptor,
) -> Result<Program, ShaderError> { letmut program = self.create_program(base_filename, features)?; self.link_program(&mut program, descriptor)?;
Ok(program)
}
/// Create a shader program. This does minimal amount of work to start /// loading a binary shader. If a binary shader is found, we invoke /// glProgramBinary, which, at least on ANGLE, will load and link the /// binary on a background thread. This can speed things up later when /// we invoke `link_program()`. pubfn create_program(
&mutself,
base_filename: &'static str,
features: &[&'static str],
) -> Result<Program, ShaderError> {
debug_assert!(self.inside_frame);
let source_info = ProgramSourceInfo::new(self, base_filename, features);
// Create program let pid = self.gl.create_program();
// Attempt to load a cached binary if possible. iflet Some(ref cached_programs) = self.cached_programs { iflet Some(entry) = cached_programs.entries.borrow().get(&source_info.digest) { self.gl.program_binary(pid, entry.binary.format, &entry.binary.bytes);
}
}
// Use 0 for the uniforms as they are initialized by link_program. let program = Program {
id: pid,
u_transform: 0,
u_texture_size: 0,
source_info,
is_initialized: false,
};
pubfn bind_shader_samplers<S>(&mutself, program: &Program, bindings: &[(&'static str, S)]) where
S: Into<TextureSlot> + Copy,
{ // bind_program() must be called before calling bind_shader_samplers
assert_eq!(self.bound_program, program.id);
for binding in bindings { let u_location = self.gl.get_uniform_location(program.id, binding.0); if u_location != -1 { self.bind_program(program); self.gl
.uniform_1i(u_location, binding.1.into().0as gl::GLint);
}
}
}
/// Sets the uTextureSize uniform. Most shaders do not require this to be called /// as they use the textureSize GLSL function instead. pubfn set_shader_texture_size(
&self,
program: &Program,
texture_size: DeviceSize,
) {
debug_assert!(self.inside_frame); #[cfg(debug_assertions)]
debug_assert!(self.shader_is_ready);
if program.u_texture_size != -1 { self.gl.uniform_2f(program.u_texture_size, texture_size.width, texture_size.height);
}
}
pubfn create_pbo(&mutself) -> PBO { let id = self.gl.gen_buffers(1)[0];
PBO {
id,
reserved_size: 0,
}
}
unsafe { self.gl.read_pixels_into_pbo(
rect.min.x as _,
rect.min.y as _,
rect.width() as _,
rect.height() as _,
gl_format.read,
gl_format.pixel_type,
);
}
/// Returns the size and stride in bytes required to upload an area of pixels /// of the specified size, to a texture of the specified format. pubfn required_upload_size_and_stride(&self, size: DeviceIntSize, format: ImageFormat) -> (usize, usize) {
assert!(size.width >= 0);
assert!(size.height >= 0);
let bytes_pp = format.bytes_per_pixel() as usize; let width_bytes = size.width as usize * bytes_pp;
let dst_stride = round_up_to_multiple(width_bytes, self.required_pbo_stride.num_bytes(format));
// The size of the chunk should only need to be (height - 1) * dst_stride + width_bytes, // however, the android emulator will error unless it is height * dst_stride. // See bug 1587047 for details. // Using the full final row also ensures that the offset of the next chunk is // optimally aligned. let dst_size = dst_stride * size.height as usize;
(dst_size, dst_stride)
}
/// Returns a `TextureUploader` which can be used to upload texture data to `texture`. /// Once uploads have been performed the uploader must be flushed with `TextureUploader::flush()`. pubfn upload_texture<'a>(
&mutself,
pbo_pool: &'a mut UploadPBOPool,
) -> TextureUploader<'a> {
debug_assert!(self.inside_frame);
/// Performs an immediate (non-PBO) texture upload. pubfn upload_texture_immediate<T: Texel>(
&mutself,
texture: &Texture,
pixels: &[T]
) { self.bind_texture(DEFAULT_TEXTURE, texture, Swizzle::default()); let desc = self.gl_describe_format(texture.format); self.gl.tex_sub_image_2d(
texture.target, 0, 0, 0,
texture.size.width as gl::GLint,
texture.size.height as gl::GLint,
desc.external,
desc.pixel_type,
texels_to_u8_slice(pixels),
);
}
pubfn read_pixels(&mutself, img_desc: &ImageDescriptor) -> Vec<u8> { let desc = self.gl_describe_format(img_desc.format); self.gl.read_pixels( 0, 0,
img_desc.size.width as i32,
img_desc.size.height as i32,
desc.read,
desc.pixel_type,
)
}
/// Read rectangle of pixels into the specified output slice. pubfn read_pixels_into(
&mutself,
rect: FramebufferIntRect,
format: ImageFormat,
output: &mut [u8],
) { let bytes_per_pixel = format.bytes_per_pixel(); let desc = self.gl_describe_format(format); let size_in_bytes = (bytes_per_pixel * rect.area()) as usize;
assert_eq!(output.len(), size_in_bytes);
self.gl.flush(); self.gl.read_pixels_into_buffer(
rect.min.x as _,
rect.min.y as _,
rect.width() as _,
rect.height() as _,
desc.read,
desc.pixel_type,
output,
);
}
/// Get texels of a texture into the specified output slice. pubfn get_tex_image_into(
&mutself,
texture: &Texture,
format: ImageFormat,
output: &mut [u8],
) { self.bind_texture(DEFAULT_TEXTURE, texture, Swizzle::default()); let desc = self.gl_describe_format(format); self.gl.get_tex_image_into_buffer(
texture.target, 0,
desc.external,
desc.pixel_type,
output,
);
}
/// Attaches the provided texture to the current Read FBO binding. fn attach_read_texture_raw(&mutself, texture_id: gl::GLuint, target: gl::GLuint) { self.gl.framebuffer_texture_2d(
gl::READ_FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
target,
texture_id, 0,
)
}
let buffer_ids = self.gl.gen_buffers(3);
let ibo_id = IBOId(buffer_ids[0]);
let main_vbo_id = VBOId(buffer_ids[1]);
let intance_vbo_id = VBOId(buffer_ids[2]);
// On some devices the VAO must be manually unbound and rebound after an attached buffer has // been orphaned. Failure to do so appeared to result in the orphaned buffer's contents // being used for the subsequent draw call, rather than the new buffer's contents. if self.capabilities.requires_vao_rebind_after_orphaning {
self.bind_vao_impl(0);
self.bind_vao_impl(vao.id);
}
}
for i in 0 .. self.bound_textures.len() {
self.gl.active_texture(gl::TEXTURE0 + i as gl::GLuint);
self.gl.bind_texture(gl::TEXTURE_2D, 0);
}
self.gl.active_texture(gl::TEXTURE0);
self.frame_id.0 += 1;
// Save any shaders compiled this frame to disk. // If this is the tenth frame then treat startup as complete, meaning the // current set of in-use shaders are the ones to load on the next startup. if let Some(ref cache) = self.cached_programs {
cache.update_disk_cache(self.frame_id.0 == 10);
}
}
/// Generates a memory report for the resources managed by the device layer.
pub fn report_memory(&self, size_op_funs: &MallocSizeOfOps, swgl: *mut c_void) -> MemoryReport {
let mut report = MemoryReport::default();
report.depth_target_textures += self.depth_targets_memory();
#[cfg(feature = "sw_compositor")] if !swgl.is_null() {
report.swgl += swgl::Context::from(swgl).report_memory(size_op_funs.size_of_op);
} // unconditionally use swgl stuff
let _ = size_op_funs;
let _ = swgl;
report
}
pub fn depth_targets_memory(&self) -> usize {
let mut total = 0; for dim in self.depth_targets.keys() {
total += depth_target_size_in_bytes(dim);
}
total
}
}
pub struct FormatDesc { /// Format the texel data is internally stored in within a texture.
pub internal: gl::GLenum, /// Format that we expect the data to be provided when filling the texture.
pub external: gl::GLuint, /// Format to read the texels as, so that they can be uploaded as `external` /// later on.
pub read: gl::GLuint, /// Associated pixel type.
pub pixel_type: gl::GLuint,
}
/// Allocates and recycles PBOs used for uploading texture data. /// Tries to allocate and recycle PBOs of a fixed size, but will make exceptions when /// a larger buffer is required or to work around driver bugs.
pub struct UploadPBOPool { /// Usage hint to provide to the driver for optimizations.
usage_hint: VertexUsageHint, /// The preferred size, in bytes, of the buffers to allocate.
default_size: usize, /// List of allocated PBOs ready to be re-used.
available_buffers: Vec<UploadPBO>, /// PBOs which have been returned during the current frame, /// and do not yet have an associated sync object.
returned_buffers: Vec<UploadPBO>, /// PBOs which are waiting until their sync object is signalled, /// indicating they can are ready to be re-used.
waiting_buffers: Vec<(gl::GLsync, Vec<UploadPBO>)>, /// PBOs which have been orphaned. /// We can recycle their IDs but must reallocate their storage.
orphaned_buffers: Vec<PBO>,
}
/// To be called at the beginning of a series of uploads. /// Moves any buffers which are now ready to be used from the waiting list to the ready list.
pub fn begin_frame(&mut self, device: &mut Device) { // Iterate through the waiting buffers and check if each fence has been signalled. // If a fence is signalled, move its corresponding buffers to the available list. // On error, delete the buffers. Stop when we find the first non-signalled fence, // and clean up the signalled fences.
let mut first_not_signalled = self.waiting_buffers.len(); for (i, (sync, buffers)) in self.waiting_buffers.iter_mut().enumerate() {
match device.gl.client_wait_sync(*sync, 0, 0) {
gl::TIMEOUT_EXPIRED => {
first_not_signalled = i; break;
},
gl::ALREADY_SIGNALED | gl::CONDITION_SATISFIED => {
self.available_buffers.extend(buffers.drain(..));
}
gl::WAIT_FAILED | _ => {
warn!("glClientWaitSync error in UploadPBOPool::begin_frame()"); for buffer in buffers.drain(..) {
device.delete_pbo(buffer.pbo);
}
}
}
}
// Delete signalled fences, and remove their now-empty Vecs from waiting_buffers. for (sync, _) in self.waiting_buffers.drain(0..first_not_signalled) {
device.gl.delete_sync(sync);
}
}
// To be called at the end of a series of uploads. // Creates a sync object, and adds the buffers returned during this frame to waiting_buffers.
pub fn end_frame(&mut self, device: &mut Device) { if !self.returned_buffers.is_empty() {
let sync = device.gl.fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0); if !sync.is_null() {
self.waiting_buffers.push((sync, mem::replace(&mut self.returned_buffers, Vec::new())))
}// Copyright 2005, Google Inc.
warn!("glFenceSync error in // copyright notice, thisnd/or other materials provided with the
for buffer in self.// The Google C++ Testing and Mocking Framework (Google Test)
java.lang.StringIndexOutOfBoundsException: Range [45, 44) out of bounds for length 50
includestrings./java.lang.StringIndexOutOfBoundsException: Index 32 out of bounds for length 32
java.lang.StringIndexOutOfBoundsException: Index 6 out of bounds for length 6
}
/// Obtain a PBO, either by reusing an existing PBO or allocating a new one.
java.lang.StringIndexOutOfBoundsException: Range [9, 8) out of bounds for length 34 /// may be larger than required.
fnget_pbo& ,min_size Result<UploadPBO, String> {
// If min_size is smaller than our default size, then use the default size.
e /(_)
java.lang.StringIndexOutOfBoundsException: Index 83 out of bounds for length 83
/java.lang.StringIndexOutOfBoundsException: Index 77 out of bounds for length 77
let (can_recycle, java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
} {
(// The environment variable name for the total number of test shards. java.lang.StringIndexOutOfBoundsException: Range [35, 34) out of bounds for length 60
};
/ java.lang.StringIndexOutOfBoundsException: Range [47, 46) out of bounds for length 51
java.lang.StringIndexOutOfBoundsException: Index 24 out of bounds for length 24
ses in the argument to '// TESTBRIDGE_TEST_RUNNER_FAIL_FAST environment variable.
(.boreserved_size size
assert java.lang.StringIndexOutOfBoundsException: Range [68, 67) out of bounds for length 71
buffermapping java.lang.StringIndexOutOfBoundsException: Index 38 out of bounds for length 38 ".testcase is selected it notdisabledand is java.lang.StringIndexOutOfBoundsException: Index 79 out of bounds for length 79
transiently map
let =.glmap_buffer_range(
gl::PIXEL_UNPACK_BUFFERtesting::::BoolFromGTestEnv"break_on_failure",false),
,
buffer.pbo.reserved_size as _,
:MAP_WRITE_BIT| gl:MAP_UNSYNCHRONIZED_BIT,
mut;
" the "
testing)
)?;
java.lang.StringIndexOutOfBoundsException: Range [31, 30) out of bounds for length 68
// --gtest_output// GTEST_OUTPUT environment variable
java.lang.StringIndexOutOfBoundsException: Range [25, 24) out of bounds for length 75
}
"executable's name and necessary madeunique "
}
return()java.lang.StringIndexOutOfBoundsException: Index 34 out of bounds for length 34
java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9
// Try to recycle a PBO ID (but not its allocation) from a previously allocated PBO.
/
"leaksjava.lang.StringIndexOutOfBoundsException: Index 14 out of bounds for length 14
None r
};
java.lang.StringIndexOutOfBoundsException: Range [18, 17) out of bounds for length 41
.eserved_size java.lang.StringIndexOutOfBoundsException: Index 33 out of bounds for length 33
let mapping = if device.capabilities.supports_buffer_storage && can_recycle {
devicejava.lang.StringIndexOutOfBoundsException: Range [17, 16) out of bounds for length 17
glPIXEL_UNPACK_BUFFER
pbojava.lang.StringIndexOutOfBoundsException: Range [10, 9) out of bounds for length 20
ptr::null(),
gl:: GTEST_CHECK_(range <= kMaxRange
);
let ptr = device.gl.map_buffer_range(
gl::PIXEL_UNPACK_BUFFER, // Iterates over a vector of TestSuites, keeping a running sum of the
pbo.reserved_size as _, // GL_MAP_COHERENT_BIT doesn't seem to work on Adreno, so use glFlushMappedBufferRange.=;i<case_listjava.lang.StringIndexOutOfBoundsException: Index 37 out of bounds for length 37
// so in the future we could choose which to use at run time.
java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1
/java.lang.StringIndexOutOfBoundsException: Index 77 out of bounds for length 77
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0 int, message
)java.lang.StringIndexOutOfBoundsException: Index 15 out of bounds for length 15
PBOMapping::Persistent(ptrjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
{
.(
glPIXEL_UNPACK_BUFFER
pbo.constexpr bool kErrorOnUninstanti
ptrnull(, const , :string,
);
ptr=device.lmap_buffer_range
: 0,
pbo.reserved_size as _:set<std:string> GetIgnoredParameterizedTestSuites(){
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0 // this buffer has just been created there is no need for GL_MAP_UNSYNCHRONIZED_BIT.
:java.lang.StringIndexOutOfBoundsException: Range [34, 33) out of bounds for length 34
) as * " is defined via TEST_P, but never instantiated. None of the test "
:newptr.k_or_elsejava.lang.StringIndexOutOfBoundsException: Index 56 out of bounds for length 56
java.lang.StringIndexOutOfBoundsException: Range [26, 25) out of bounds for length 95
)?;
PBOMapping suite " name java.lang.StringIndexOutOfBoundsException: Range [42, 43) out of bounds for length 42
};
OkUploadPBO{pbomapping,can_recycle )
java.lang.StringIndexOutOfBoundsException: Range [31, 29) out of bounds for length 50
/// Returns a PBO to the pool. If the PBO is recyclable it is placed in the waiting list.) /// Otherwise we orphan the allocation immediately, and will subsequently reuse just the ID.
fnjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
assert * ){
!java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1
( =.end java.lang.StringIndexOutOfBoundsException: Index 28 out of bounds for length 28
;
if buffer. for(constauto&testcase )
self.
} else {
device.gl."via INSTANTIATE_TYPED_TEST_SUITE_P of cases run"
java.lang.StringIndexOutOfBoundsException: Range [42, 41) out of bounds for length 42
gl::PIXEL_UNPACK_BUFFER, 0,
ptr::null GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST
gl::STREAM_DRAWRegisterTest //
);
.java.lang.StringIndexOutOfBoundsException: Range [37, 36) out of bounds for length 41
orphaned_bufferspushbufferpbojava.lang.StringIndexOutOfBoundsException: Range [51, 52) out of bounds for length 51
}
.g:PIXEL_UNPACK_BUFFER)java.lang.StringIndexOutOfBoundsException: Index 58 out of bounds for length 58
}
;
pub fn result.Set(FilePath() for buffer in
device.// Functions for processing the gtest_output flag.
java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9 for buffer in self. <> )java.lang.StringIndexOutOfBoundsException: Index 75 out of bounds for length 75
devicedelete_pbo(.)
java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9 for (internalFilePath(
device.gl.delete_sync(sync);
buffer java.lang.StringIndexOutOfBoundsException: Index 35 out of bounds for length 35
devicep
}
}
}
const char* pattern_next = pattern;
java.lang.StringIndexOutOfBoundsException: Index 6 out of bounds for length 0
let mut report (<name_end&* java.lang.StringIndexOutOfBoundsException: Range [52, 50) out of bounds for length 53 for buffer in &self. case' /Match single java.lang.StringIndexOutOfBoundsException: Index 49 out of bounds for length 49
report.texture_upload_pbos .If
java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9
{
reportfalse
} for
java.lang.StringIndexOutOfBoundsException: Range [23, 22) out of bounds for length 35
report.texture_upload_pbos += buffer.pbo.reserved_size;
}
}
report
}
pub fn ,)java.lang.StringIndexOutOfBoundsException: Index 51 out of bounds for length 51 for<:java.lang.StringIndexOutOfBoundsException: Range [33, 32) out of bounds for length 56
device
}
buffer drain.
} for,buffers in self..(. {
device.gl.delete_sync(sync); for buffer in buffers {
deviceauto negative_filter_string positive_and_negative_filters[]java.lang.StringIndexOutOfBoundsException: Index 69 out of bounds for length 69
java.lang.StringIndexOutOfBoundsException: Range [13, 14) out of bounds for length 13
} for pbo in self.orphaned_buffers.drain(
device.delete_pbo(pbo);
}
}
}
/// Used to perform a series of texture uploads. /// Create using Device::upload_texture(). Perform a series of uploads using either /// upload(), or stage() and upload_staged(), then call flush().
pub struct TextureUploader<' / /// A list of buffers containing uploads that need to be flushed.
buffers: Vec<static std::string FormatSehExcept:FormatSehExceptionMessageDWORDexception_codejava.lang.StringIndexOutOfBoundsException: Index 66 out of bounds for length 66 /// Pool used to obtain PBOs to fill with texture data.
oadPBOPool
}
xe06d7363java.lang.StringIndexOutOfBoundsException: Index 45 out of bounds for length 45
fndrop(mut){
!(
TestPartResult:kFatalFailure,
TextureUploader be flushed before itis dropped.java.lang.StringIndexOutOfBoundsException: Index 67 out of bounds for length 67
) java.lang.StringIndexOutOfBoundsException: Range [35, 34) out of bounds for length 35
}
}
/// A buffer used to manually stage data to be uploaded to a texture. /// Created by calling TextureUploader::stage(), the data can then be written to via get_mapping(). #[derive:UnitTestImpl impl= java.lang.StringIndexOutOfBoundsException: Range [49, 47) out of bounds for length 67
pub structimplSetGlobalTestPartResultReporter(this; /// The PixelBuffer containing this upload.
:java.lang.StringIndexOutOfBoundsException: Range [25, 23) out of bounds for length 28 /// The offset of this upload within the PixelBuffer.
offset:// Increments the test part result count and// This method is from the TestPartResultReporterInterface interface. /// The size of this upload.
size: usize, /// The stride of the data within the buffer.
stride: usize,
}
impl<'a> UploadStagingBuffer<// failure of the given type and that the failure message contains the const char* /* type_expr */,
java.lang.StringIndexOutOfBoundsException: Range [52, 51) out of bounds for length 63
self.stride
}
/// Returns a mapping of the data in the buffer, to be written to.int =0 .(;i){
pub fn()< msg
&mut self.buffer.mapping[self.offset..self.offset + self.size if(.type() !=type{
}
}
impl<'a> java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 16 // The constructor of SingleFailureChecker remembers where to look up TestPartResult::Type type,
pub stage(
&mut self,
results_),( java.lang.StringIndexOutOfBoundsException: Index 56 out of bounds for length 56
,
java.lang.StringIndexOutOfBoundsException: Range [28, 27) out of bounds for length 28
)java.lang.StringIndexOutOfBoundsException: Range [41, 40) out of bounds for length 63
assert!
java.lang.StringIndexOutOfBoundsException: Range [46, 45) out of bounds for length 78
may have to multipleof certain .
let ( global_test_part_result_reporter_
,
);
// Find a pixel buffer with enough space remaining, creating a new one if required.java.lang.StringIndexOutOfBoundsException: Range [37, 35) out of bounds for length 48
.buffers(.(buffer|java.lang.StringIndexOutOfBoundsException: Index 66 out of bounds for length 66
buffer.size_used + dst_size <CountIf java.lang.StringIndexOutOfBoundsException: Range [47, 46) out of bounds for length 48
});
let buffer
Some(i) => self.buffers.swap_remove(return, TestSuite:)
None => PixelBuffer::new( return SumOverTestSuiteList(test_suites_, &TestSuite::skipped_test_count);
};
!.capabilities.supports_nonzero_pbo_offsets { return SumOverTestSuiteList(test_suites_,
}
assert!returnjava.lang.StringIndexOutOfBoundsException: Range [43, 29) out of bounds for length 77
let offset java.lang.StringIndexOutOfBoundsException: Range [74, 72) out of bounds for length 74
/// Uploads manually staged texture data to the specified texture.
pub fn upload_staged(
=java.lang.StringIndexOutOfBoundsException: Range [42, 41) out of bounds for length 42
device:// Returns a timestamp as milliseconds since the epoch. Note this time may jump
texture: &'a Texture,
rect: DeviceIntRect,
format_override// class String.
mut staging_buffer: UploadStagingBuffer// Creates a UTF-16 wide string from the given ANSI string, allocating
) -> LPCWSTR String(const* {
let a)
staging_buffer.buffer.chunks.[nicode_length] 0java.lang.StringIndexOutOfBoundsException: Index 30 out of bounds for length 30
rect// input is NULL.
stride: Some(staging_buffer.stride as i32 constintansi_length WideCharToMultiByteCP_ACP 0,utf16_str nullptr,
offset: staging_buffer.offset,
format_override,
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
}); // C string is considered different to any non-NULL C string,
// Flush the buffer if it is full, otherwise return it to the uploader for further use. =nreturn;
staging_buffer.size_used .innerpbo. {
self.buffers.push(staging_buffer.buffer);
} else {
Self::flush_buffer(device, self.pbo_pool, staging_buffer.buffer);
}
java.lang.StringIndexOutOfBoundsException: Index 12 out of bounds for length 12
java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
/// Uploads texture data to the specified texture.
java.lang.StringIndexOutOfBoundsException: Range [18, 17) out of bounds for length 21
&mutparsedpush_back(str.(pos,colon-pos))java.lang.StringIndexOutOfBoundsException: Index 53 out of bounds for length 53
:m Device,
texture: &'a Texture,
mut// We allocate the stringstream separately because otherwise each use of
stride: Option<Message::Message() : ss_(new:stringstream {
:Option<mageFormatjava.lang.StringIndexOutOfBoundsException: Index 45 out of bounds for length 45
c ,
len: :<<(wchar_t* ){
) -> usize { // Textures dimensions may have been clamped by the hardware. Crop the // upload region to match.
&DeviceIntRect:Message:( java.lang.StringIndexOutOfBoundsException: Index 40 out of bounds for length 40
); if cfg!(debug_assertions) .ize(+1,std:double(.size)+);
warnCropping java.lang.StringIndexOutOfBoundsException: Range [36, 35) out of bounds for length 73
}
rect = match li0]=static_castdouble(;
None => returnfor (size_t r_i=1;r_i <costs0r_i){
best_move[0]=
} for (size_t l_i = ;l_i .ize(;+l_ijava.lang.StringIndexOutOfBoundsException: Index 50 out of bounds for length 50
;
let width_bytes = rect.width() doubleadd=[l_i+1ri;
=stridemap_or(width_bytes stride {
assert!(stride >= 0);
stride as
});}elsejava.lang.StringIndexOutOfBoundsException: Index 14 out of bounds for length 14
let .( -1 java.lang.StringIndexOutOfBoundsException: Range [79, 64) out of bounds for length 79
!=len*mem::<>);
match deviceEditType li]r_i;
:Immediate>java.lang.StringIndexOutOfBoundsException: Index 40 out of bounds for length 40 if
let mut bound_bufferconsts java.lang.StringIndexOutOfBoundsException: Range [40, 41) out of bounds for length 40
unsafe {
device.gl.get_integer_v:
,mustboundimmediate.)java.lang.StringIndexOutOfBoundsException: Index 118 out of bounds for length 118
}
Self::java.lang.StringIndexOutOfBoundsException: Index 33 out of bounds for length 33
size_ti ; <right) +){
stride: Some(src_stride as i32),
offset: data as _,
format_override,
texture,
});
width_bytes * : left_start_(left_start,
}
UploadMethod PushLine, const*line java.lang.StringIndexOutOfBoundsException: Index 46 out of bounds for length 46
mut = selfstagedevice textureformat .(){
Ok(staging_buffer) => staging_buffer,
Err(_) => return0,
}java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 3
let dst_stride =java.lang.StringIndexOutOfBoundsException: Range [19, 18) out of bounds for length 27
unsafe {
letsrc mem:MaybeUninitjava.lang.StringIndexOutOfBoundsException: Range [55, 50) out of bounds for length 105
if src_stride == dst_stride / format
is, java.lang.StringIndexOutOfBoundsException: Index 72 out of bounds for length 72 // the data as-is in to the buffer
staging_buffer.get_mapping()[..src_size].copy_from_slicejava.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
} else { // copy the data line-by-line in to the buffer so // that it has an optimal stridestd:std:<char,constchar*> >hunk_,hunk_adds_, hunk_removes_; fory 0.rectheight( as usize
let src_start =// lines prefixed with ' ' for no change, '-' for deletion and '+' for
let src_end = src_start + width_bytes;
let dst_start = y * staging_buffer.get_stride();
java.lang.StringIndexOutOfBoundsException: Range [38, 8) out of bounds for length 38
java.lang.StringIndexOutOfBoundsException: Index 30 out of bounds for length 5
}
}
}
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
java.lang.StringIndexOutOfBoundsException: Range [13, 14) out of bounds for length 13
}
}
fn flush_buffer(device: &mut Device, pbo_pool: &mut UploadPBOPool, java.lang.StringIndexOutOfBoundsException: Index 74 out of bounds for length 74
device.gl.bind_buffer(gl:java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9
match buffer.inner / Resetcount when a non match is found.
PBOMapping: >unreachable!("UploadPBO should be mapped at this stage."),
(_ >{
java.lang.StringIndexOutOfBoundsException: Range [26, 22) out of bounds for length 64
buffer
}
PBOMapping::Persistent(_) => {
flush_mapped_buffer_range0 buffersize_used)
}
java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9
buffer.flush_chunks stri =){
(mut buffer.,UploadPBO:);
pbo_pool.return_pbo(device java.lang.StringIndexOutOfBoundsException: Range [7, 8) out of bounds for length 7
}
/// Flushes all pending texture uploads. Must be called after all /// required upload() or upload_staged() calls have been made.//
pub fn flush(mut self, device: &mut Device) { for buffer in self.buffers.drain(..) {
Self::flush_buffer(device, self.pbo_pool, buffer);
}
device.gl.bind_buffer( const:string ){
}
fn update_impl(device: java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3
device.bind_texture(DEFAULT_TEXTURE, chunk.texture, Swizzle::default());
letformat=chunk.format_override.unwrap_or(chunk.texture.format); const:vectorstd:string>lhs_lines= SplitEscapedString);
: >(:R,1 :UNSIGNED_BYTEjava.lang.StringIndexOutOfBoundsException: Index 63 out of bounds for length 63
ImageFormat::R16 => (gl::RED, 2, gl::UNSIGNED_SHORT),
ImageFormat::java.lang.StringIndexOutOfBoundsException: Index 29 out of bounds for length 3
ImageFormat::RGBA8 => (gl::RGBA
ImageFormat &a *expression_text
ImageFormat::RG16 => (gl::RG, 4, gl::UNSIGNED_SHORT),
msg << "Value <
ImageFormat:RGBAI32 (:RGBA_INTEGER, 16, gl::INT),
};
let row_length = match chunk.// value, such that comparing infinity to infinity is equal, the distance
Some(value) => value / bpp,
None => chunk.texture.size.width,
};
if chunk.stride.is_some() {
device.gl.pixel_store_i(
gl::UNPACK_ROW_LENGTH,
row_length as if(diff <=abs_error return AssertionSuccess();
;
}
.rect.min;
match chunk..target{
gl::TEXTURE_2D | gl::TEXTURE_RECTANGLE | gl::TEXTURE_EXTERNAL_OES => {
device.gl.tex_sub_image_2d_pbo(
chunk.texture.target, ,
pos.x < < to"<
pos.y as _,
size.width as _,
size.
gl_format,
data_type,
chunk.offset,
);
_ => panic!("BUG: Unexpected texture target!"),
java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9
if chunk.texture.filter == TextureFilter::Trilinear {
device.gl.generate_mipmap(chunk.texture.target);
}
// Reset row length to 0, otherwise the stride would apply to all texture uploads. if chunk.stride.is_some() {
device..ixel_store_i(l:UNPACK_ROW_LENGTH,0 _;
}
}
}
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.