From 5b8be078b0fe6293d6c899fe5783ab56223bc63a Mon Sep 17 00:00:00 2001 From: Jamie Nicol Date: Wed, 14 Nov 2018 15:33:12 +0000 Subject: [PATCH] Use an intermediate renderbuffer when blitting between texture arrays on adreno devices When the texture cache is grown we allocate a larger texture array then copy each layer of the old array in to the new one. To perform this copy we use use glBlitFramebuffer, with the read and draw framebuffers bound to the correct layer in the old and new textures. Unfortunately on Adreno devices this does not work. glBlitFramebuffer always writes to the 0th layer of the texture bound to the draw framebuffer. glCopyTexSubImage3D also does not work correctly - it always reads from the 0th layer of the texture bound to the read framebuffer. As a workaround, we use glBlitFramebuffer to blit from the old texture in to a temporary renderbuffer, then use glCopyTexSubImage3D to copy from that renderbuffer to the new texture. --- webrender/src/device/gl.rs | 51 +++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/webrender/src/device/gl.rs b/webrender/src/device/gl.rs index d1f9eedf62..d98f04c89e 100644 --- a/webrender/src/device/gl.rs +++ b/webrender/src/device/gl.rs @@ -857,6 +857,11 @@ pub struct Device { /// format, we fall back to glTexImage*. texture_storage_usage: TexStorageUsage, + // Whether we are able to use glBlitFramebuffers with the draw fbo + // bound to a non-0th layer of a texture array. This is buggy on + // adreno devices. + can_blit_to_texture_array: bool, + // GL extensions extensions: Vec, } @@ -1024,6 +1029,10 @@ impl Device { ) }; + // Due to a bug on adreno devices, blitting to an fbo bound to + // a non-0th layer of a texture array is not supported. + let can_blit_to_texture_array = !renderer_name.starts_with("Adreno"); + // Explicitly set some global states to the values we expect. gl.disable(gl::FRAMEBUFFER_SRGB); gl.disable(gl::MULTISAMPLE); @@ -1066,6 +1075,7 @@ impl Device { frame_id: GpuFrameId(0), extensions, texture_storage_usage, + can_blit_to_texture_array, } } @@ -1621,12 +1631,47 @@ impl Device { debug_assert!(dst.size.height >= src.size.height); debug_assert!(dst.layer_count >= src.layer_count); - // Note that zip() truncates to the shorter of the two iterators. + // If we are unable to blit directly between the texture arrays + // then we must create an intermediate renderbuffer. + let temp_buffer = if src.layer_count > 1 && !self.can_blit_to_texture_array { + let rbo = RBOId(self.gl.gen_renderbuffers(1)[0]); + let fbo = FBOId(self.gl.gen_framebuffers(1)[0]); + self.gl.bind_renderbuffer(gl::RENDERBUFFER, rbo.0); + self.gl.renderbuffer_storage(gl::RENDERBUFFER, gl::RGBA8, + src.size.width as _, src.size.height as _); + self.bind_draw_target_impl(fbo); + self.gl.framebuffer_renderbuffer(gl::DRAW_FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + gl::RENDERBUFFER, + rbo.0); + + Some((rbo, fbo)) + } else { + None + }; + let rect = DeviceIntRect::new(DeviceIntPoint::zero(), src.get_dimensions().to_i32()); - for (read_fbo, draw_fbo) in src.fbos.iter().zip(&dst.fbos) { + for (i, read_fbo) in src.fbos.iter().enumerate() { + // Blit from the src to the dst (or the intermediate buffer if necessary) self.bind_read_target_impl(*read_fbo); - self.bind_draw_target_impl(*draw_fbo); + self.bind_draw_target_impl(match &temp_buffer { + Some((_, fbo)) => *fbo, + None => dst.fbos[i], + }); self.blit_render_target(rect, rect); + + // Copy from the intermediate buffer to the dst texture if necessary + if let Some((_, fbo)) = &temp_buffer { + self.bind_read_target_impl(*fbo); + self.bind_texture(DEFAULT_TEXTURE, dst); + self.gl.copy_tex_sub_image_3d(dst.target, 0, 0, 0, i as _, + 0, 0, src.size.width as _, src.size.height as _); + self.gl.invalidate_framebuffer(gl::READ_FRAMEBUFFER, &[gl::COLOR_ATTACHMENT0]); + } + } + if let Some((rbo, fbo)) = &temp_buffer { + self.gl.delete_renderbuffers(&[rbo.0]); + self.gl.delete_framebuffers(&[fbo.0]); } self.reset_draw_target(); self.reset_read_target();