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

Gert Wollny gert.wollny at collabora.com
Thu May 31 16:49:39 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

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

diff --git a/src/vrend_renderer.c b/src/vrend_renderer.c
index 0c42a59..bd9b2f6 100644
--- a/src/vrend_renderer.c
+++ b/src/vrend_renderer.c
@@ -6059,6 +6059,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;
 
    filter = convert_mag_filter(info->filter);
 
@@ -6145,6 +6148,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;
+      args.handle = 0;
+      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.nr_samples = 0;
+      args.array_size = src_res->base.array_size;
+      intermediate_copy  = vrend_renderer_resource_allocate(&args);
+
+      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,
@@ -6152,6 +6200,7 @@ static void vrend_renderer_blit_int(struct vrend_context *ctx,
    else
       glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0,
                                 GL_TEXTURE_2D, 0, 0);
+
    glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->sub->blit_fb_ids[1]);
    if (info->mask & PIPE_MASK_RGBA)
       glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT,
@@ -6159,20 +6208,33 @@ static void vrend_renderer_blit_int(struct vrend_context *ctx,
    else if (info->mask & (PIPE_MASK_Z | PIPE_MASK_S))
       glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0,
                                 GL_TEXTURE_2D, 0, 0);
+
    if (info->src.box.depth == info->dst.box.depth)
       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) {
+         glBindFramebuffer(GL_FRAMEBUFFER_EXT, intermediate_fbo);
+         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, src_res->base.width0, src_res->base.height0,
+                           0, 0, src_res->base.width0, src_res->base.height0,
+                           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,
@@ -6185,6 +6247,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.0



More information about the virglrenderer-devel mailing list