[virglrenderer-devel] [PATCH v2 2/2] vrend: Work around multi-sample glBlitFramebuffer limitations on GLES

Gert Wollny gert.wollny at collabora.com
Tue Jun 12 10:55:41 UTC 2018


On GLES a multi-sample FBO region can only be blit to an non multi-sample
region if the source and the target rectangle are the same. The fallback
using GL and shaders doesn't work if a stencil buffer is involved, because
writing to a stencil buffer from a shader is usually not supported (i.e.
the extension ARB_shader_stencil_export is not available).

Implement a workaround for this case and also the case when the depth
buffer is involved by first blitting the complete multi-sample FBO to an
intermediate, non multi-sample FBO, and apply the region blit to the
actual target FBO afterwards.

Fixes:
  dEQP-GLES3.functional.fbo.invalidate.sub.unbind_blit_msaa_color
  dEQP-GLES3.functional.fbo.invalidate.sub.unbind_blit_msaa_depth
  dEQP-GLES3.functional.fbo.invalidate.sub.unbind_blit_msaa_stencil
  dEQP-GLES3.functional.fbo.invalidate.whole.unbind_blit_msaa_color
  dEQP-GLES3.functional.fbo.invalidate.whole.unbind_blit_msaa_depth
  dEQP-GLES3.functional.fbo.invalidate.whole.unbind_blit_msaa_stencil

v2: * Replace allocation via the formerly extracted resource allocation
      funtion with allocating the texture via the newly extracted methods
      to copy the texture parameters and allocate the texture.
    * in the intermediate blit use the minified size that corresonds
      to the layer to be copied

Signed-off-by: Gert Wollny <gert.wollny at collabora.com>
---
 src/vrend_renderer.c | 72 +++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 68 insertions(+), 4 deletions(-)

diff --git a/src/vrend_renderer.c b/src/vrend_renderer.c
index 9a7412b..30d112e 100644
--- a/src/vrend_renderer.c
+++ b/src/vrend_renderer.c
@@ -6092,6 +6092,9 @@ static void vrend_renderer_blit_int(struct vrend_context *ctx,
    GLenum filter;
    int n_layers = 1, i;
    bool use_gl = false;
+   bool make_intermediate_copy = false;
+   GLuint intermediate_fbo = 0;
+   struct vrend_resource *intermediate_copy = 0;
 
    filter = convert_mag_filter(info->filter);
 
@@ -6178,6 +6181,51 @@ static void vrend_renderer_blit_int(struct vrend_context *ctx,
    } else
       glDisable(GL_SCISSOR_TEST);
 
+   /* An GLES GL_INVALID_OPERATION is generated if one wants to blit from a
+    * multi-sample fbo to a non multi-sample fbo and the source and destination
+    * rectangles are not defined with the same (X0, Y0) and (X1, Y1) bounds.
+    *
+    * Since stencil data can only be written in a fragment shader when
+    * ARB_shader_stencil_export is available, the workaround using GL as given
+    * above is usually not available. Instead, to work around the blit
+    * limitations on GLES first copy the full frame to a non-multisample
+    * surface and then copy the according area to the final target surface.
+    */
+   if (vrend_state.use_gles &&
+       (info->mask & PIPE_MASK_ZS) &&
+       ((src_res->base.nr_samples > 1) &&
+        (src_res->base.nr_samples != dst_res->base.nr_samples)) &&
+        ((info->src.box.x != info->dst.box.x) ||
+         (src_y1 != dst_y1) ||
+         (info->src.box.width != info->dst.box.width) ||
+         (src_y2 != dst_y2))) {
+
+      make_intermediate_copy = true;
+
+      /* Create a texture that is the same like the src_res texture, but
+       * without multi-sample */
+      struct vrend_renderer_resource_create_args args;
+      memset(&args, 0, sizeof(struct vrend_renderer_resource_create_args));
+      args.width = src_res->base.width0;
+      args.height = src_res->base.height0;
+      args.depth = src_res->base.depth0;
+      args.format = src_res->base.format;
+      args.target = src_res->base.target;
+      args.last_level = src_res->base.last_level;
+      args.array_size = src_res->base.array_size;
+      intermediate_copy = (struct vrend_resource *)CALLOC_STRUCT(vrend_texture);
+      vrend_renderer_resource_copy_args(&args, intermediate_copy);
+      vrend_renderer_resource_allocate_texture(intermediate_copy);
+
+      glGenFramebuffers(1, &intermediate_fbo);
+   } else {
+      /* If no intermediate copy is needed make the variables point to the
+       * original source to simplify the code below.
+       */
+      intermediate_fbo = ctx->sub->blit_fb_ids[0];
+      intermediate_copy = src_res;
+   }
+
    glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->sub->blit_fb_ids[0]);
    if (info->mask & PIPE_MASK_RGBA)
       glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT,
@@ -6196,16 +6244,28 @@ static void vrend_renderer_blit_int(struct vrend_context *ctx,
       n_layers = info->dst.box.depth;
    for (i = 0; i < n_layers; i++) {
       glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->sub->blit_fb_ids[0]);
-      glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT,
-                                GL_TEXTURE_2D, 0, 0);
       vrend_fb_bind_texture(src_res, 0, info->src.level, info->src.box.z + i);
 
-      glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->sub->blit_fb_ids[1]);
+      if (make_intermediate_copy) {
+         int level_width = u_minify(src_res->base.width0, info->src.level);
+         int level_height = u_minify(src_res->base.width0, info->src.level);
+         glBindFramebuffer(GL_FRAMEBUFFER_EXT, intermediate_fbo);
+         glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0,
+                                   GL_TEXTURE_2D, 0, 0);
+         vrend_fb_bind_texture(intermediate_copy, 0, info->src.level, info->src.box.z + i);
+
+         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, intermediate_fbo);
+         glBindFramebuffer(GL_READ_FRAMEBUFFER, ctx->sub->blit_fb_ids[0]);
+         glBlitFramebuffer(0, 0, level_width, level_height,
+                           0, 0, level_width, level_height,
+                           glmask, filter);
+      }
 
+      glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->sub->blit_fb_ids[1]);
       vrend_fb_bind_texture(dst_res, 0, info->dst.level, info->dst.box.z + i);
       glBindFramebuffer(GL_DRAW_FRAMEBUFFER, ctx->sub->blit_fb_ids[1]);
 
-      glBindFramebuffer(GL_READ_FRAMEBUFFER, ctx->sub->blit_fb_ids[0]);
+      glBindFramebuffer(GL_READ_FRAMEBUFFER, intermediate_fbo);
 
       glBlitFramebuffer(info->src.box.x,
                         src_y1,
@@ -6218,6 +6278,10 @@ static void vrend_renderer_blit_int(struct vrend_context *ctx,
                         glmask, filter);
    }
 
+   if (make_intermediate_copy) {
+      vrend_renderer_resource_destroy(intermediate_copy, false);
+      glDeleteFramebuffers(1, &intermediate_fbo);
+   }
 }
 
 void vrend_renderer_blit(struct vrend_context *ctx,
-- 
2.17.1



More information about the virglrenderer-devel mailing list