meta: Add support for doing multisample resolves.
authorEric Anholt <eric@anholt.net>
Fri, 7 Feb 2014 22:00:31 +0000 (14:00 -0800)
committerEric Anholt <eric@anholt.net>
Fri, 21 Feb 2014 18:43:38 +0000 (10:43 -0800)
Note that this doesn't handle GL_EXT_multisample_scaled_blit yet.  The
i965 code for that extension bakes in knowledge of the sample positions
(well, knowledge of the sample positions aligned to a lower-resolution
grid), which we would have to do at runtime somehow for meta.

Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
src/mesa/drivers/common/meta.h
src/mesa/drivers/common/meta_blit.c

index 822bfa13d37af80b63f2c762b89e92b8e2e54743..5d79253139f14bdadb436bf001624134c84cfdc2 100644 (file)
@@ -221,6 +221,12 @@ struct blit_shader_table {
    struct blit_shader sampler_cubemap_array;
 };
 
+enum blit_msaa_shader {
+   BLIT_MSAA_SHADER_2D_MULTISAMPLE,
+   BLIT_MSAA_SHADER_2D_MULTISAMPLE_DEPTH,
+   BLIT_MSAA_SHADER_COUNT,
+};
+
 /**
  * State for glBlitFramebufer()
  */
@@ -230,6 +236,7 @@ struct blit_state
    GLuint VBO;
    GLuint DepthFP;
    struct blit_shader_table shaders;
+   GLuint msaa_shaders[BLIT_MSAA_SHADER_COUNT];
    struct temp_texture depthTex;
 };
 
index a2b284b0d8f94a18ea6137c13a6bd5fa5ee55e8e..be91247c57c019b411233fb13d9f487a7ce71abd 100644 (file)
@@ -31,6 +31,7 @@
 #include "main/condrender.h"
 #include "main/depth.h"
 #include "main/enable.h"
+#include "main/enums.h"
 #include "main/fbobject.h"
 #include "main/macros.h"
 #include "main/matrix.h"
@@ -80,9 +81,159 @@ init_blit_depth_pixels(struct gl_context *ctx)
                           strlen(program2), (const GLubyte *) program2);
 }
 
+static void
+setup_glsl_msaa_blit_shader(struct gl_context *ctx,
+                            struct blit_state *blit,
+                            struct gl_renderbuffer *src_rb,
+                            GLenum target)
+{
+   const char *vs_source;
+   char *fs_source;
+   GLuint vs, fs;
+   void *mem_ctx;
+   enum blit_msaa_shader shader_index;
+   const char *samplers[] = {
+      [BLIT_MSAA_SHADER_2D_MULTISAMPLE] = "sampler2DMS",
+   };
+
+   switch (target) {
+   case GL_TEXTURE_2D_MULTISAMPLE:
+      if (src_rb->_BaseFormat == GL_DEPTH_COMPONENT ||
+          src_rb->_BaseFormat == GL_DEPTH_STENCIL) {
+         shader_index = BLIT_MSAA_SHADER_2D_MULTISAMPLE_DEPTH;
+      } else {
+         shader_index = BLIT_MSAA_SHADER_2D_MULTISAMPLE;
+      }
+      break;
+   default:
+      _mesa_problem(ctx, "Unkown texture target %s\n",
+                    _mesa_lookup_enum_by_nr(target));
+      shader_index = BLIT_MSAA_SHADER_2D_MULTISAMPLE;
+   }
+
+   if (blit->msaa_shaders[shader_index]) {
+      _mesa_UseProgram(blit->msaa_shaders[shader_index]);
+      return;
+   }
+
+   mem_ctx = ralloc_context(NULL);
+
+   if (shader_index == BLIT_MSAA_SHADER_2D_MULTISAMPLE_DEPTH) {
+      /* From the GL 4.3 spec:
+       *
+       *     "If there is a multisample buffer (the value of SAMPLE_BUFFERS is
+       *      one), then values are obtained from the depth samples in this
+       *      buffer. It is recommended that the depth value of the centermost
+       *      sample be used, though implementations may choose any function
+       *      of the depth sample values at each pixel.
+       *
+       * We're slacking and instead of choosing centermost, we've got 0.
+       */
+      vs_source = ralloc_asprintf(mem_ctx,
+                                  "#version 130\n"
+                                  "in vec2 position;\n"
+                                  "in vec2 textureCoords;\n"
+                                  "out vec2 texCoords;\n"
+                                  "void main()\n"
+                                  "{\n"
+                                  "   texCoords = textureCoords;\n"
+                                  "   gl_Position = vec4(position, 0.0, 1.0);\n"
+                                  "}\n");
+      fs_source = ralloc_asprintf(mem_ctx,
+                                  "#version 130\n"
+                                  "#extension GL_ARB_texture_multisample : enable\n"
+                                  "uniform sampler2DMS texSampler;\n"
+                                  "in vec2 texCoords;\n"
+                                  "out vec4 out_color;\n"
+                                  "\n"
+                                  "void main()\n"
+                                  "{\n"
+                                  "   gl_FragDepth = texelFetch(texSampler, ivec2(texCoords), 0).r;\n"
+                                  "}\n");
+   } else if (shader_index == BLIT_MSAA_SHADER_2D_MULTISAMPLE) {
+      char *sample_resolve;
+      /* You can create 2D_MULTISAMPLE textures with 0 sample count (meaning 1
+       * sample).  Yes, this is ridiculous.
+       */
+      int samples = MAX2(src_rb->NumSamples, 1);
+
+      /* We're assuming power of two samples for this resolution procedure.
+       *
+       * To avoid losing any floating point precision if the samples all
+       * happen to have the same value, we merge pairs of values at a time (so
+       * the floating point exponent just gets increased), rather than doing a
+       * naive sum and dividing.
+       */
+      assert((samples & (samples - 1)) == 0);
+      /* Fetch each individual sample. */
+      sample_resolve = rzalloc_size(mem_ctx, 1);
+      for (int i = 0; i < samples; i++) {
+         ralloc_asprintf_append(&sample_resolve,
+                                "   vec4 sample_1_%d = texelFetch(texSampler, ivec2(texCoords), %d);\n",
+                                i, i);
+      }
+      /* Now, merge each pair of samples, then each pair of those merges, etc.
+       */
+      for (int step = 2; step <= samples; step *= 2) {
+         for (int i = 0; i < samples; i += step) {
+            ralloc_asprintf_append(&sample_resolve,
+                                   "vec4 sample_%d_%d = sample_%d_%d + sample_%d_%d;\n",
+                                   step, i,
+                                   step / 2, i,
+                                   step / 2, i + step / 2);
+         }
+      }
+
+      /* Scale the final result. */
+      ralloc_asprintf_append(&sample_resolve,
+                             "   out_color = sample_%d_0 / %f;\n",
+                             samples, (float)samples);
+
+      vs_source = ralloc_asprintf(mem_ctx,
+                                  "#version 130\n"
+                                  "in vec2 position;\n"
+                                  "in vec2 textureCoords;\n"
+                                  "out vec2 texCoords;\n"
+                                  "void main()\n"
+                                  "{\n"
+                                  "   texCoords = textureCoords;\n"
+                                  "   gl_Position = vec4(position, 0.0, 1.0);\n"
+                                  "}\n");
+      fs_source = ralloc_asprintf(mem_ctx,
+                                  "#version 130\n"
+                                  "#extension GL_ARB_texture_multisample : enable\n"
+                                  "uniform %s texSampler;\n"
+                                  "in vec2 texCoords;\n"
+                                  "out vec4 out_color;\n"
+                                  "\n"
+                                  "void main()\n"
+                                  "{\n"
+                                  "%s\n" /* sample_resolve */
+                                  "}\n",
+                                  samplers[shader_index],
+                                  sample_resolve);
+   }
+
+   vs = _mesa_meta_compile_shader_with_debug(ctx, GL_VERTEX_SHADER, vs_source);
+   fs = _mesa_meta_compile_shader_with_debug(ctx, GL_FRAGMENT_SHADER, fs_source);
+
+   blit->msaa_shaders[shader_index] = _mesa_CreateProgramObjectARB();
+   _mesa_AttachShader(blit->msaa_shaders[shader_index], fs);
+   _mesa_DeleteObjectARB(fs);
+   _mesa_AttachShader(blit->msaa_shaders[shader_index], vs);
+   _mesa_DeleteObjectARB(vs);
+   _mesa_BindAttribLocation(blit->msaa_shaders[shader_index], 0, "position");
+   _mesa_BindAttribLocation(blit->msaa_shaders[shader_index], 1, "texcoords");
+   _mesa_meta_link_program_with_debug(ctx, blit->msaa_shaders[shader_index]);
+   ralloc_free(mem_ctx);
+
+   _mesa_UseProgram(blit->msaa_shaders[shader_index]);
+}
+
 static void
 setup_glsl_blit_framebuffer(struct gl_context *ctx,
                             struct blit_state *blit,
+                            struct gl_renderbuffer *src_rb,
                             GLenum target)
 {
    /* target = GL_TEXTURE_RECTANGLE is not supported in GLES 3.0 */
@@ -90,7 +241,11 @@ setup_glsl_blit_framebuffer(struct gl_context *ctx,
 
    _mesa_meta_setup_vertex_objects(&blit->VAO, &blit->VBO, true, 2, 2, 0);
 
-   _mesa_meta_setup_blit_shader(ctx, target, &blit->shaders);
+   if (target == GL_TEXTURE_2D_MULTISAMPLE) {
+      setup_glsl_msaa_blit_shader(ctx, blit, src_rb, target);
+   } else {
+      _mesa_meta_setup_blit_shader(ctx, target, &blit->shaders);
+   }
 }
 
 /**
@@ -124,6 +279,15 @@ blitframebuffer_texture(struct gl_context *ctx,
       ctx->Texture.Unit[ctx->Texture.CurrentUnit].Sampler ?
       ctx->Texture.Unit[ctx->Texture.CurrentUnit].Sampler->Name : 0;
    GLuint tempTex = 0;
+   struct gl_renderbuffer *rb = readAtt->Renderbuffer;
+
+   if (rb->NumSamples && !ctx->Extensions.ARB_texture_multisample)
+      return false;
+
+   if (filter == GL_SCALED_RESOLVE_FASTEST_EXT ||
+       filter == GL_SCALED_RESOLVE_NICEST_EXT) {
+      filter = GL_LINEAR;
+   }
 
    if (readAtt->Texture) {
       /* If there's a texture attached of a type we can handle, then just use
@@ -133,16 +297,25 @@ blitframebuffer_texture(struct gl_context *ctx,
       texObj = readAtt->Texture;
       target = texObj->Target;
 
-      if (target != GL_TEXTURE_2D && target != GL_TEXTURE_RECTANGLE_ARB)
+      switch (target) {
+      case GL_TEXTURE_2D:
+      case GL_TEXTURE_RECTANGLE:
+      case GL_TEXTURE_2D_MULTISAMPLE:
+         break;
+      default:
          return false;
+      }
    } else if (ctx->Driver.BindRenderbufferTexImage) {
       /* Otherwise, we need the driver to be able to bind a renderbuffer as
        * a texture image.
        */
       struct gl_texture_image *texImage;
-      struct gl_renderbuffer *rb = readAtt->Renderbuffer;
 
-      target = GL_TEXTURE_2D;
+      if (rb->NumSamples > 1)
+         target = GL_TEXTURE_2D_MULTISAMPLE;
+      else
+         target = GL_TEXTURE_2D;
+
       _mesa_GenTextures(1, &tempTex);
       _mesa_BindTexture(target, tempTex);
       srcLevel = 0;
@@ -174,7 +347,7 @@ blitframebuffer_texture(struct gl_context *ctx,
    maxLevelSave = texObj->MaxLevel;
 
    if (glsl_version) {
-      setup_glsl_blit_framebuffer(ctx, blit, target);
+      setup_glsl_blit_framebuffer(ctx, blit, rb, target);
    }
    else {
       _mesa_meta_setup_ff_tnl_for_blit(&ctx->Meta->Blit.VAO,
@@ -230,7 +403,8 @@ blitframebuffer_texture(struct gl_context *ctx,
          t1 = srcY1 / (float) texImage->Height;
       }
       else {
-         assert(target == GL_TEXTURE_RECTANGLE_ARB);
+         assert(target == GL_TEXTURE_RECTANGLE_ARB ||
+                target == GL_TEXTURE_2D_MULTISAMPLE);
          s0 = (float) srcX0;
          s1 = (float) srcX1;
          t0 = (float) srcY0;
@@ -326,12 +500,15 @@ _mesa_meta_BlitFramebuffer(struct gl_context *ctx,
     * texture size, fallback if the source is multisampled.  This fallback can
     * be removed once Mesa gets support ARB_texture_multisample.
     */
-   if (srcW > maxTexSize || srcH > maxTexSize
-       || ctx->ReadBuffer->Visual.samples > 0) {
+   if (srcW > maxTexSize || srcH > maxTexSize) {
       /* XXX avoid this fallback */
-      _swrast_BlitFramebuffer(ctx, srcX0, srcY0, srcX1, srcY1,
-                              dstX0, dstY0, dstX1, dstY1, mask, filter);
-      return;
+      goto fallback;
+   }
+
+   /* Multisample texture blit support requires texture multisample. */
+   if (ctx->ReadBuffer->Visual.samples > 0 &&
+       !ctx->Extensions.ARB_texture_multisample) {
+      goto fallback;
    }
 
    /* only scissor effects blit so save/clear all other relevant state */
@@ -368,7 +545,7 @@ _mesa_meta_BlitFramebuffer(struct gl_context *ctx,
     * BlitFramebuffer function.
     */
    if (use_glsl_version) {
-      setup_glsl_blit_framebuffer(ctx, blit, tex->Target);
+      setup_glsl_blit_framebuffer(ctx, blit, NULL, tex->Target);
    }
    else {
       _mesa_meta_setup_ff_tnl_for_blit(&blit->VAO, &blit->VBO, 2);
@@ -492,6 +669,7 @@ _mesa_meta_BlitFramebuffer(struct gl_context *ctx,
 
    _mesa_meta_end(ctx);
 
+fallback:
    if (mask) {
       _swrast_BlitFramebuffer(ctx, srcX0, srcY0, srcX1, srcY1,
                               dstX0, dstY0, dstX1, dstY1, mask, filter);