virgl: implement EXT_multisampled_render_to_texture

Signed-off-by: Italo Nicola <italonicola@collabora.com>
Reviewed-by: Gert Wollny <gert.wollny@collabora.com>
diff --git a/src/virgl_hw.h b/src/virgl_hw.h
index fc6f733..dce8cbc 100644
--- a/src/virgl_hw.h
+++ b/src/virgl_hw.h
@@ -441,6 +441,7 @@
 #define VIRGL_CAP_V2_MEMINFO              (1 << 3)
 #define VIRGL_CAP_V2_STRING_MARKER        (1 << 4)
 #define VIRGL_CAP_V2_DIFFERENT_GPU        (1 << 5)
+#define VIRGL_CAP_V2_IMPLICIT_MSAA        (1 << 6)
 
 /* virgl bind flags - these are compatible with mesa 10.5 gallium.
  * but are fixed, no other should be passed to virgl either.
diff --git a/src/virgl_protocol.h b/src/virgl_protocol.h
index 19068ad..ec3c458 100644
--- a/src/virgl_protocol.h
+++ b/src/virgl_protocol.h
@@ -57,6 +57,7 @@
    VIRGL_OBJECT_SURFACE,
    VIRGL_OBJECT_QUERY,
    VIRGL_OBJECT_STREAMOUT_TARGET,
+   VIRGL_OBJECT_MSAA_SURFACE,
    VIRGL_MAX_OBJECTS,
 };
 
@@ -345,6 +346,10 @@
 #define VIRGL_OBJ_SURFACE_TEXTURE_LEVEL 4
 #define VIRGL_OBJ_SURFACE_TEXTURE_LAYERS 5
 
+/* create surface with implicit MSAA support (for EXT_multisample_render_to_texture) */
+#define VIRGL_OBJ_MSAA_SURFACE_SIZE (VIRGL_OBJ_SURFACE_SIZE + 1)
+#define VIRGL_OBJ_SURFACE_SAMPLE_COUNT 6
+
 /* create streamout target */
 #define VIRGL_OBJ_STREAMOUT_SIZE 4
 #define VIRGL_OBJ_STREAMOUT_HANDLE 1
diff --git a/src/vrend_blitter.c b/src/vrend_blitter.c
index a96b6bc..41bd90e 100644
--- a/src/vrend_blitter.c
+++ b/src/vrend_blitter.c
@@ -748,7 +748,7 @@
    glUseProgram(prog_id);
 
    glBindFramebuffer(GL_FRAMEBUFFER, blit_ctx->fb_id);
-   vrend_fb_bind_texture_id(dst_res, blit_views[1], 0, info->dst.level, info->dst.box.z);
+   vrend_fb_bind_texture_id(dst_res, blit_views[1], 0, info->dst.level, info->dst.box.z, 0);
 
    buffers = GL_COLOR_ATTACHMENT0;
    glDrawBuffers(1, &buffers);
@@ -783,7 +783,7 @@
       uint32_t layer = (dst_res->target == GL_TEXTURE_CUBE_MAP) ? info->dst.box.z : dst_z;
 
       glBindFramebuffer(GL_FRAMEBUFFER, blit_ctx->fb_id);
-      vrend_fb_bind_texture_id(dst_res, blit_views[1], 0, info->dst.level, layer);
+      vrend_fb_bind_texture_id(dst_res, blit_views[1], 0, info->dst.level, layer, 0);
 
       buffers = GL_COLOR_ATTACHMENT0;
       glDrawBuffers(1, &buffers);
diff --git a/src/vrend_debug.c b/src/vrend_debug.c
index b888ad2..a6e2896 100644
--- a/src/vrend_debug.c
+++ b/src/vrend_debug.c
@@ -94,7 +94,8 @@
    "SAMPLER_STATE",
    "SURFACE",
    "QUERY",
-   "STREAMOUT_TARGET"
+   "STREAMOUT_TARGET",
+   "MSAA_SURFACE"
 };
 
 const char *vrend_get_comand_name(enum virgl_context_cmd cmd)
diff --git a/src/vrend_decode.c b/src/vrend_decode.c
index 91f5f24..35ccc23 100644
--- a/src/vrend_decode.c
+++ b/src/vrend_decode.c
@@ -612,21 +612,34 @@
    return 0;
 }
 
-static int vrend_decode_create_surface(struct vrend_context *ctx, const uint32_t *buf, uint32_t handle, uint16_t length)
+static int vrend_decode_create_surface_common(struct vrend_context *ctx, const uint32_t *buf, uint32_t handle, uint32_t sample_count)
 {
    uint32_t res_handle, format, val0, val1;
-   int ret;
-
-   if (length != VIRGL_OBJ_SURFACE_SIZE)
-      return EINVAL;
 
    res_handle = get_buf_entry(buf, VIRGL_OBJ_SURFACE_RES_HANDLE);
    format = get_buf_entry(buf, VIRGL_OBJ_SURFACE_FORMAT);
    /* decide later if these are texture or buffer */
    val0 = get_buf_entry(buf, VIRGL_OBJ_SURFACE_BUFFER_FIRST_ELEMENT);
    val1 = get_buf_entry(buf, VIRGL_OBJ_SURFACE_BUFFER_LAST_ELEMENT);
-   ret = vrend_create_surface(ctx, handle, res_handle, format, val0, val1);
-   return ret;
+
+   return vrend_create_surface(ctx, handle, res_handle, format, val0, val1, sample_count);
+}
+
+static int vrend_decode_create_surface(struct vrend_context *ctx, const uint32_t *buf, uint32_t handle, uint16_t length)
+{
+   if (length != VIRGL_OBJ_SURFACE_SIZE)
+      return EINVAL;
+
+   return vrend_decode_create_surface_common(ctx, buf, handle, 0);
+}
+
+static int vrend_decode_create_msaa_surface(struct vrend_context *ctx, const uint32_t *buf, uint32_t handle, uint16_t length)
+{
+   if (length != VIRGL_OBJ_MSAA_SURFACE_SIZE)
+      return EINVAL;
+
+   uint32_t sample_count = get_buf_entry(buf, VIRGL_OBJ_SURFACE_SAMPLE_COUNT);
+   return vrend_decode_create_surface_common(ctx, buf, handle, sample_count);
 }
 
 static int vrend_decode_create_sampler_view(struct vrend_context *ctx, const uint32_t *buf, uint32_t handle, uint16_t length)
@@ -789,6 +802,9 @@
    case VIRGL_OBJECT_STREAMOUT_TARGET:
       ret = vrend_decode_create_stream_output_target(ctx, buf, handle, length);
       break;
+   case VIRGL_OBJECT_MSAA_SURFACE:
+      ret = vrend_decode_create_msaa_surface(ctx, buf, handle, length);
+      break;
    default:
       return EINVAL;
    }
diff --git a/src/vrend_renderer.c b/src/vrend_renderer.c
index 7d1d7e8..2d534ef 100644
--- a/src/vrend_renderer.c
+++ b/src/vrend_renderer.c
@@ -202,6 +202,7 @@
    feat_txqs,
    feat_ubo,
    feat_viewport_array,
+   feat_implicit_msaa,
    feat_last,
 };
 
@@ -302,6 +303,7 @@
    FEAT(txqs, 45, UNAVAIL,  "GL_ARB_shader_texture_image_samples" ),
    FEAT(ubo, 31, 30,  "GL_ARB_uniform_buffer_object" ),
    FEAT(viewport_array, 41, UNAVAIL,  "GL_ARB_viewport_array", "GL_OES_viewport_array"),
+   FEAT(implicit_msaa, UNAVAIL, UNAVAIL,  "GL_EXT_multisampled_render_to_texture"),
 };
 
 struct global_renderer_state {
@@ -451,6 +453,7 @@
    GLuint res_handle;
    GLuint format;
    GLuint val0, val1;
+   GLuint nr_samples;
    struct vrend_resource *texture;
 };
 
@@ -959,25 +962,27 @@
 #define GLES_WARN_DEPTH_CLEAR 14
 #define GLES_WARN_LOGIC_OP 15
 #define GLES_WARN_TIMESTAMP 16
+#define GLES_WARN_IMPLICIT_MSAA_SURFACE 17
 
 MAYBE_UNUSED
 static const char *vrend_gles_warn_strings[] = {
-   [GLES_WARN_NONE]             = "None",
-   [GLES_WARN_STIPPLE]          = "Stipple",
-   [GLES_WARN_POLYGON_MODE]     = "Polygon Mode",
-   [GLES_WARN_DEPTH_RANGE]      = "Depth Range",
-   [GLES_WARN_POINT_SIZE]       = "Point Size",
-   [GLES_WARN_SEAMLESS_CUBE_MAP] = "Seamless Cube Map",
-   [GLES_WARN_LOD_BIAS]         = "Lod Bias",
-   [GLES_WARN_TEXTURE_RECT]     = "Texture Rect",
-   [GLES_WARN_OFFSET_LINE]      = "Offset Line",
-   [GLES_WARN_OFFSET_POINT]     = "Offset Point",
-   [GLES_WARN_FLATSHADE_FIRST]  = "Flatshade First",
-   [GLES_WARN_LINE_SMOOTH]      = "Line Smooth",
-   [GLES_WARN_POLY_SMOOTH]      = "Poly Smooth",
-   [GLES_WARN_DEPTH_CLEAR]      = "Depth Clear",
-   [GLES_WARN_LOGIC_OP]         = "LogicOp",
-   [GLES_WARN_TIMESTAMP]        = "GL_TIMESTAMP",
+   [GLES_WARN_NONE]                  = "None",
+   [GLES_WARN_STIPPLE]               = "Stipple",
+   [GLES_WARN_POLYGON_MODE]          = "Polygon Mode",
+   [GLES_WARN_DEPTH_RANGE]           = "Depth Range",
+   [GLES_WARN_POINT_SIZE]            = "Point Size",
+   [GLES_WARN_SEAMLESS_CUBE_MAP]     = "Seamless Cube Map",
+   [GLES_WARN_LOD_BIAS]              = "Lod Bias",
+   [GLES_WARN_TEXTURE_RECT]          = "Texture Rect",
+   [GLES_WARN_OFFSET_LINE]           = "Offset Line",
+   [GLES_WARN_OFFSET_POINT]          = "Offset Point",
+   [GLES_WARN_FLATSHADE_FIRST]       = "Flatshade First",
+   [GLES_WARN_LINE_SMOOTH]           = "Line Smooth",
+   [GLES_WARN_POLY_SMOOTH]           = "Poly Smooth",
+   [GLES_WARN_DEPTH_CLEAR]           = "Depth Clear",
+   [GLES_WARN_LOGIC_OP]              = "LogicOp",
+   [GLES_WARN_TIMESTAMP]             = "GL_TIMESTAMP",
+   [GLES_WARN_IMPLICIT_MSAA_SURFACE] = "Implicit MSAA Surface",
 };
 
 static void __report_gles_warn(MAYBE_UNUSED const char *fname,
@@ -1849,7 +1854,8 @@
 int vrend_create_surface(struct vrend_context *ctx,
                          uint32_t handle,
                          uint32_t res_handle, uint32_t format,
-                         uint32_t val0, uint32_t val1)
+                         uint32_t val0, uint32_t val1,
+                         uint32_t nr_samples)
 {
    struct vrend_surface *surf;
    struct vrend_resource *res;
@@ -1876,6 +1882,7 @@
    surf->val0 = val0;
    surf->val1 = val1;
    surf->id = res->id;
+   surf->nr_samples = nr_samples;
 
    if (!has_bit(res->storage_bits, VREND_STORAGE_GL_BUFFER) &&
          has_bit(res->storage_bits, VREND_STORAGE_GL_IMMUTABLE)) {
@@ -2305,6 +2312,41 @@
    return 0;
 }
 
+static void vrend_framebuffer_texture_2d(struct vrend_resource *res,
+                                         GLenum target, GLenum attachment,
+                                         GLenum textarget, uint32_t texture,
+                                         int32_t level, uint32_t samples)
+{
+   assert(textarget == GL_TEXTURE_2D);
+
+   if (samples == 0) {
+      glFramebufferTexture2D(target, attachment, textarget, texture, level);
+   } else if (!has_feature(feat_implicit_msaa)) {
+      /* fallback to non-msaa */
+      report_gles_warn(vrend_state.current_ctx, GLES_WARN_IMPLICIT_MSAA_SURFACE);
+      glFramebufferTexture2D(target, attachment, textarget, texture, level);
+   } else if (attachment == GL_COLOR_ATTACHMENT0){
+      glFramebufferTexture2DMultisampleEXT(target, attachment, textarget,
+                                           texture, level, samples);
+   } else if (attachment == GL_STENCIL_ATTACHMENT || attachment == GL_DEPTH_ATTACHMENT) {
+      GLenum internalformat =
+              attachment == GL_STENCIL_ATTACHMENT ?  GL_STENCIL_INDEX8 : GL_DEPTH_COMPONENT16;
+
+      glGenRenderbuffers(1, &res->rbo_id);
+      glBindRenderbuffer(GL_RENDERBUFFER, res->rbo_id);
+      glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples,
+                                          internalformat, res->base.width0,
+                                          res->base.height0);
+      glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment,
+                                GL_RENDERBUFFER, res->rbo_id);
+      glBindRenderbuffer(GL_RENDERBUFFER, 0);
+   } else {
+      /* unsupported attachment for EXT_multisampled_render_to_texture, fallback to non-msaa */
+      report_gles_warn(vrend_state.current_ctx, GLES_WARN_IMPLICIT_MSAA_SURFACE);
+      glFramebufferTexture2D(target, attachment, textarget, texture, level);
+   }
+}
+
 static
 void debug_texture(MAYBE_UNUSED const char *f, const struct vrend_resource *gt)
 {
@@ -2333,9 +2375,8 @@
 }
 
 void vrend_fb_bind_texture_id(struct vrend_resource *res,
-                              int id,
-                              int idx,
-                              uint32_t level, uint32_t layer)
+                              int id, int idx, uint32_t level,
+                              uint32_t layer, uint32_t samples)
 {
    const struct util_format_description *desc = util_format_description(res->base.format);
    GLenum attachment = GL_COLOR_ATTACHMENT0 + idx;
@@ -2380,8 +2421,9 @@
          glFramebufferTexture(GL_FRAMEBUFFER, attachment,
                               id, level);
       else
-         glFramebufferTexture2D(GL_FRAMEBUFFER, attachment,
-                                GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer, id, level);
+         vrend_framebuffer_texture_2d(res, GL_FRAMEBUFFER, attachment,
+                                      GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer,
+                                      id, level, samples);
       break;
    case GL_TEXTURE_1D:
       glFramebufferTexture1D(GL_FRAMEBUFFER, attachment,
@@ -2389,8 +2431,8 @@
       break;
    case GL_TEXTURE_2D:
    default:
-      glFramebufferTexture2D(GL_FRAMEBUFFER, attachment,
-                             res->target, id, level);
+      vrend_framebuffer_texture_2d(res, GL_FRAMEBUFFER, attachment,
+                                   res->target, id, level, samples);
       break;
    }
 
@@ -2413,7 +2455,7 @@
                            int idx,
                            uint32_t level, uint32_t layer)
 {
-   vrend_fb_bind_texture_id(res, res->id, idx, level, layer);
+   vrend_fb_bind_texture_id(res, res->id, idx, level, layer, 0);
 }
 
 static void vrend_hw_set_zsurf_texture(struct vrend_context *ctx)
@@ -2431,7 +2473,8 @@
          return;
 
       vrend_fb_bind_texture_id(surf->texture, surf->id, 0, surf->val0,
-			       first_layer != last_layer ? 0xffffffff : first_layer);
+                               first_layer != last_layer ? 0xffffffff : first_layer,
+                               surf->nr_samples);
    }
 }
 
@@ -2449,7 +2492,8 @@
       uint32_t last_layer = (sub_ctx->surf[index]->val1 >> 16) & 0xffff;
 
       vrend_fb_bind_texture_id(surf->texture, surf->id, index, surf->val0,
-                               first_layer != last_layer ? 0xffffffff : first_layer);
+                               first_layer != last_layer ? 0xffffffff : first_layer,
+                               surf->nr_samples);
    }
 }
 
@@ -7193,6 +7237,10 @@
       free(res->ptr);
    }
 
+   if (res->rbo_id) {
+      glDeleteRenderbuffers(1, &res->rbo_id);
+   }
+
    if (has_bit(res->storage_bits, VREND_STORAGE_GL_MEMOBJ)) {
       glDeleteMemoryObjectsEXT(1, &res->memobj);
    }
@@ -9057,7 +9105,7 @@
       n_layers = info->dst.box.depth;
    for (i = 0; i < n_layers; i++) {
       glBindFramebuffer(GL_FRAMEBUFFER, ctx->sub->blit_fb_ids[0]);
-      vrend_fb_bind_texture_id(src_res, blitter_views[0], 0, info->src.level, info->src.box.z + i);
+      vrend_fb_bind_texture_id(src_res, blitter_views[0], 0, info->src.level, info->src.box.z + i, 0);
 
       if (make_intermediate_copy) {
          int level_width = u_minify(src_res->base.width0, info->src.level);
@@ -9075,7 +9123,7 @@
       }
 
       glBindFramebuffer(GL_FRAMEBUFFER, ctx->sub->blit_fb_ids[1]);
-      vrend_fb_bind_texture_id(dst_res, blitter_views[1], 0, info->dst.level, info->dst.box.z + i);
+      vrend_fb_bind_texture_id(dst_res, blitter_views[1], 0, info->dst.level, info->dst.box.z + i, 0);
       glBindFramebuffer(GL_DRAW_FRAMEBUFFER, ctx->sub->blit_fb_ids[1]);
 
       if (has_feature(feat_srgb_write_control)) {
@@ -10483,6 +10531,9 @@
    if (has_feature(feat_khr_debug))
        caps->v2.capability_bits_v2 |= VIRGL_CAP_V2_STRING_MARKER;
 
+   if (has_feature(feat_implicit_msaa))
+       caps->v2.capability_bits_v2 |= VIRGL_CAP_V2_IMPLICIT_MSAA;
+
    if (vrend_winsys_different_gpu())
       caps->v2.capability_bits_v2 |= VIRGL_CAP_V2_DIFFERENT_GPU;
 }
diff --git a/src/vrend_renderer.h b/src/vrend_renderer.h
index 55d6b94..b132384 100644
--- a/src/vrend_renderer.h
+++ b/src/vrend_renderer.h
@@ -74,6 +74,10 @@
    GLuint tbo_tex_id;/* tbos have two ids to track */
    bool y_0_top;
 
+   /* used for keeping track of multisampled renderbuffer for
+    * GL_EXT_multisampled_render_to_texture. */
+   GLuint rbo_id;
+
    /* Pointer to system memory storage for this resource. Only valid for
     * VREND_RESOURCE_STORAGE_GUEST_ELSE_SYSTEM buffer storage.
     */
@@ -204,7 +208,8 @@
 int vrend_create_surface(struct vrend_context *ctx,
                          uint32_t handle,
                          uint32_t res_handle, uint32_t format,
-                         uint32_t val0, uint32_t val1);
+                         uint32_t val0, uint32_t val1,
+                         uint32_t nr_samples);
 int vrend_create_sampler_view(struct vrend_context *ctx,
                               uint32_t handle,
                               uint32_t res_handle, uint32_t format,
@@ -347,9 +352,8 @@
                               uint32_t res_handle);
 
 void vrend_fb_bind_texture_id(struct vrend_resource *res,
-                              int id,
-                              int idx,
-                              uint32_t level, uint32_t layer);
+                              int id, int idx, uint32_t level,
+                              uint32_t layer, uint32_t samples);
 
 void vrend_set_tess_state(struct vrend_context *ctx, const float tess_factors[6]);