Bug 1688921 - Support GPU cache resize when EXT_color_buffer_float is not supported. r=kvark

Currently when the GPU cache is resized we allocate a new texture and
then copy the contents of the old texture to the new texture. This
copy requires either EXT_copy_image (for glCopyImageSubData) or
EXT_color_buffer_float (to bind the RGBAF32 texture to a framebuffer).

On devices where neither extension is supported, don't attempt to copy
the old texture. Instead mark the entire CPU-side copy of the cache as
dirty, meaning we will subsequently upload the entire contents to the
new texture. (A complete CPU-side copy is only mainted for the
PixelBuffer gpu cache bus type, not for Scatter ones. However, as the
Scatter type also requires EXT_color_buffer_float, we will only be in
this situation for PixelBuffer buses.)

Differential Revision: https://phabricator.services.mozilla.com/D103071
This commit is contained in:
Jamie Nicol 2021-01-27 08:47:11 +00:00
parent fd5fcc2c87
commit 62eb49693e
3 changed files with 42 additions and 18 deletions

View File

@ -954,6 +954,8 @@ pub struct Capabilities {
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 we are able to use `glBlitFramebuffers` with the draw fbo
@ -1540,6 +1542,11 @@ impl Device {
supports_extension(&extensions, "GL_ARB_copy_image")
};
let supports_color_buffer_float = match gl.get_type() {
gl::GlType::Gl => true,
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
@ -1652,6 +1659,7 @@ impl Device {
capabilities: Capabilities {
supports_multisampling: false, //TODO
supports_copy_image_sub_data,
supports_color_buffer_float,
supports_buffer_storage,
supports_blit_to_texture_array,
supports_advanced_blend_equation,

View File

@ -91,8 +91,7 @@ pub struct GpuCacheTexture {
}
impl GpuCacheTexture {
/// Ensures that we have an appropriately-sized texture. Returns true if a
/// new texture was created.
/// Ensures that we have an appropriately-sized texture.
fn ensure_texture(&mut self, device: &mut Device, height: i32) {
// If we already have a texture that works, we're done.
if self.texture.as_ref().map_or(false, |t| t.get_dimensions().height >= height) {
@ -109,17 +108,20 @@ impl GpuCacheTexture {
// Create the new texture.
assert!(height >= 2, "Height is too small for ANGLE");
let new_size = DeviceIntSize::new(super::MAX_VERTEX_TEXTURE_WIDTH as _, height);
// If glCopyImageSubData is supported, this texture doesn't need
// to be a render target. This prevents GL errors due to framebuffer
// incompleteness on devices that don't support RGBAF32 render targets.
// TODO(gw): We still need a proper solution for the subset of devices
// that don't support glCopyImageSubData *OR* rendering to a
// RGBAF32 render target. These devices will currently fail
// to resize the GPU cache texture.
// GpuCacheBus::Scatter always requires the texture to be a render target. For
// GpuCacheBus::PixelBuffer, we only create the texture with a render target if
// RGBAF32 render targets are actually supported, and only if glCopyImageSubData
// is not. glCopyImageSubData does not require a render target to copy the texture
// data, and if neither RGBAF32 render targets nor glCopyImageSubData is supported,
// we simply re-upload the entire contents rather than copying upon resize.
let supports_copy_image_sub_data = device.get_capabilities().supports_copy_image_sub_data;
let rt_info = match self.bus {
GpuCacheBus::PixelBuffer { .. } if supports_copy_image_sub_data => None,
_ => Some(RenderTargetInfo { has_depth: false }),
let supports_color_buffer_float = device.get_capabilities().supports_color_buffer_float;
let rt_info = if matches!(self.bus, GpuCacheBus::PixelBuffer { .. })
&& (supports_copy_image_sub_data || !supports_color_buffer_float)
{
None
} else {
Some(RenderTargetInfo { has_depth: false })
};
let mut texture = device.create_texture(
api::ImageBufferKind::Texture2D,
@ -133,7 +135,21 @@ impl GpuCacheTexture {
// Copy the contents of the previous texture, if applicable.
if let Some(blit_source) = blit_source {
device.copy_entire_texture(&mut texture, &blit_source);
if !supports_copy_image_sub_data && !supports_color_buffer_float {
// Cannot copy texture, so must re-upload everything.
match self.bus {
GpuCacheBus::PixelBuffer { ref mut rows } => {
for row in rows {
row.add_dirty(0, super::MAX_VERTEX_TEXTURE_WIDTH);
}
}
GpuCacheBus::Scatter { .. } => {
panic!("Texture must be copyable to use scatter GPU cache bus method");
}
}
} else {
device.copy_entire_texture(&mut texture, &blit_source);
}
device.delete_texture(blit_source);
}
@ -144,6 +160,10 @@ impl GpuCacheTexture {
use super::desc::GPU_CACHE_UPDATE;
let bus = if use_scatter {
assert!(
device.get_capabilities().supports_color_buffer_float,
"GpuCache scatter method requires EXT_color_buffer_float",
);
let program = device.create_program_linked(
"gpu_cache_update",
&[],

View File

@ -1059,11 +1059,7 @@ impl Renderer {
// On other GL platforms, like macOS or Android, creating many PBOs is very inefficient.
// This is what happens in GPU cache updates in PBO path. Instead, we switch everything
// except software GL to use the GPU scattered updates.
let supports_scatter = match gl_type {
gl::GlType::Gl => true,
gl::GlType::Gles => device.supports_extension("GL_EXT_color_buffer_float"),
};
let supports_scatter = device.get_capabilities().supports_color_buffer_float;
let gpu_cache_texture = gpu_cache::GpuCacheTexture::new(
&mut device,
supports_scatter && !is_software,