static void vrend_renderer_fill_caps_v2(int gl_ver, int gles_ver,  union virgl_caps *caps)
{
   GLint max;
   GLfloat range[2];
   uint32_t video_memory;
   const char *renderer = (const char *)glGetString(GL_RENDERER);

   /* Count this up when you add a feature flag that is used to set a CAP in
    * the guest that was set unconditionally before. Then check that flag and
    * this value to avoid regressions when a guest with a new mesa version is
    * run on an old virgl host. Use it also to indicate non-cap fixes on the
    * host that help enable features in the guest. */
   caps->v2.host_feature_check_version = 7;

   /* Forward host GL_RENDERER to the guest. */
   strncpy(caps->v2.renderer, renderer, sizeof(caps->v2.renderer) - 1);

   glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, range);
   caps->v2.min_aliased_point_size = range[0];
   caps->v2.max_aliased_point_size = range[1];

   glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, range);
   caps->v2.min_aliased_line_width = range[0];
   caps->v2.max_aliased_line_width = range[1];

   if (gl_ver > 0) {
      glGetFloatv(GL_SMOOTH_POINT_SIZE_RANGE, range);
      caps->v2.min_smooth_point_size = range[0];
      caps->v2.max_smooth_point_size = range[1];

      glGetFloatv(GL_SMOOTH_LINE_WIDTH_RANGE, range);
      caps->v2.min_smooth_line_width = range[0];
      caps->v2.max_smooth_line_width = range[1];
   }

   glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &caps->v2.max_texture_lod_bias);
   glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, (GLint*)&caps->v2.max_vertex_attribs);

   if (gl_ver >= 32 || (vrend_state.use_gles && gl_ver >= 30))
      glGetIntegerv(GL_MAX_VERTEX_OUTPUT_COMPONENTS, &max);
   else
      max = 64; // minimum required value

   caps->v2.max_vertex_outputs = max / 4;

   glGetIntegerv(GL_MIN_PROGRAM_TEXEL_OFFSET, &caps->v2.min_texel_offset);
   glGetIntegerv(GL_MAX_PROGRAM_TEXEL_OFFSET, &caps->v2.max_texel_offset);

   glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, (GLint*)&caps->v2.uniform_buffer_offset_alignment);

   glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint*)&caps->v2.max_texture_2d_size);
   glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, (GLint*)&caps->v2.max_texture_3d_size);
   glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, (GLint*)&caps->v2.max_texture_cube_size);
   vrend_state.max_texture_2d_size = caps->v2.max_texture_2d_size;
   vrend_state.max_texture_3d_size = caps->v2.max_texture_3d_size;
   vrend_state.max_texture_cube_size = caps->v2.max_texture_cube_size;
   VREND_DEBUG(dbg_features, NULL, "Texture limits: 2D:%u 3D:%u Cube:%u\n",
               vrend_state.max_texture_2d_size, vrend_state.max_texture_3d_size,
               vrend_state.max_texture_cube_size);

   if (has_feature(feat_geometry_shader)) {
      glGetIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES, (GLint*)&caps->v2.max_geom_output_vertices);
      glGetIntegerv(GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS, (GLint*)&caps->v2.max_geom_total_output_components);
   }

   if (has_feature(feat_tessellation)) {
      glGetIntegerv(GL_MAX_TESS_PATCH_COMPONENTS, &max);
      caps->v2.max_shader_patch_varyings = max / 4;
   } else
      caps->v2.max_shader_patch_varyings = 0;

   if (has_feature(feat_texture_gather)) {
       glGetIntegerv(GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET, &caps->v2.min_texture_gather_offset);
       glGetIntegerv(GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET, &caps->v2.max_texture_gather_offset);
   }

   if (has_feature(feat_texture_buffer_range)) {
      glGetIntegerv(GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT, (GLint*)&caps->v2.texture_buffer_offset_alignment);
   }

   if (has_feature(feat_ssbo)) {
      glGetIntegerv(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, (GLint*)&caps->v2.shader_buffer_offset_alignment);

      glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &max);
      if (max > PIPE_MAX_SHADER_BUFFERS)
         max = PIPE_MAX_SHADER_BUFFERS;
      caps->v2.max_shader_buffer_other_stages = max;
      glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &max);
      if (max > PIPE_MAX_SHADER_BUFFERS)
         max = PIPE_MAX_SHADER_BUFFERS;
      caps->v2.max_shader_buffer_frag_compute = max;
      glGetIntegerv(GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS,
                    (GLint*)&caps->v2.max_combined_shader_buffers);
   }

   if (has_feature(feat_images)) {
      glGetIntegerv(GL_MAX_VERTEX_IMAGE_UNIFORMS, &max);
      if (max > PIPE_MAX_SHADER_IMAGES)
         max = PIPE_MAX_SHADER_IMAGES;
      caps->v2.max_shader_image_other_stages = max;
      glGetIntegerv(GL_MAX_FRAGMENT_IMAGE_UNIFORMS, &max);
      if (max > PIPE_MAX_SHADER_IMAGES)
         max = PIPE_MAX_SHADER_IMAGES;
      caps->v2.max_shader_image_frag_compute = max;

      if (gl_ver > 0) /* Seems GLES doesn't support multisample images */
         glGetIntegerv(GL_MAX_IMAGE_SAMPLES, (GLint*)&caps->v2.max_image_samples);
   }

   if (has_feature(feat_storage_multisample))
      caps->v1.max_samples = vrend_renderer_query_multisample_caps(caps->v1.max_samples, &caps->v2);

   caps->v2.capability_bits |= VIRGL_CAP_TGSI_INVARIANT | VIRGL_CAP_SET_MIN_SAMPLES |
                               VIRGL_CAP_TGSI_PRECISE | VIRGL_CAP_APP_TWEAK_SUPPORT;

   /* If attribute isn't supported, assume 2048 which is the minimum allowed
      by the specification. */
   if (gl_ver >= 44 || gles_ver >= 31)
      glGetIntegerv(GL_MAX_VERTEX_ATTRIB_STRIDE, (GLint*)&caps->v2.max_vertex_attrib_stride);
   else
      caps->v2.max_vertex_attrib_stride = 2048;

   if (has_feature(feat_compute_shader) && (vrend_state.use_gles || gl_ver >= 33)) {
      glGetIntegerv(GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS, (GLint*)&caps->v2.max_compute_work_group_invocations);
      glGetIntegerv(GL_MAX_COMPUTE_SHARED_MEMORY_SIZE, (GLint*)&caps->v2.max_compute_shared_memory_size);
      glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, (GLint*)&caps->v2.max_compute_grid_size[0]);
      glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, (GLint*)&caps->v2.max_compute_grid_size[1]);
      glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2, (GLint*)&caps->v2.max_compute_grid_size[2]);
      glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, (GLint*)&caps->v2.max_compute_block_size[0]);
      glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 1, (GLint*)&caps->v2.max_compute_block_size[1]);
      glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2, (GLint*)&caps->v2.max_compute_block_size[2]);

      caps->v2.capability_bits |= VIRGL_CAP_COMPUTE_SHADER;
   }

   if (has_feature(feat_atomic_counters)) {
      glGetIntegerv(GL_MAX_VERTEX_ATOMIC_COUNTERS,
                    (GLint*)(caps->v2.max_atomic_counters + PIPE_SHADER_VERTEX));
      glGetIntegerv(GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS,
                    (GLint*)(caps->v2.max_atomic_counter_buffers + PIPE_SHADER_VERTEX));
      glGetIntegerv(GL_MAX_FRAGMENT_ATOMIC_COUNTERS,
                    (GLint*)(caps->v2.max_atomic_counters + PIPE_SHADER_FRAGMENT));
      glGetIntegerv(GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS,
                    (GLint*)(caps->v2.max_atomic_counter_buffers + PIPE_SHADER_FRAGMENT));

      if (has_feature(feat_geometry_shader)) {
         glGetIntegerv(GL_MAX_GEOMETRY_ATOMIC_COUNTERS,
                       (GLint*)(caps->v2.max_atomic_counters + PIPE_SHADER_GEOMETRY));
         glGetIntegerv(GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS,
                       (GLint*)(caps->v2.max_atomic_counter_buffers + PIPE_SHADER_GEOMETRY));
      }

      if (has_feature(feat_tessellation)) {
         glGetIntegerv(GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS,
                       (GLint*)(caps->v2.max_atomic_counters + PIPE_SHADER_TESS_CTRL));
         glGetIntegerv(GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS,
                       (GLint*)(caps->v2.max_atomic_counter_buffers + PIPE_SHADER_TESS_CTRL));
         glGetIntegerv(GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS,
                       (GLint*)(caps->v2.max_atomic_counters + PIPE_SHADER_TESS_EVAL));
         glGetIntegerv(GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS,
                       (GLint*)(caps->v2.max_atomic_counter_buffers + PIPE_SHADER_TESS_EVAL));
      }

      if (has_feature(feat_compute_shader)) {
         glGetIntegerv(GL_MAX_COMPUTE_ATOMIC_COUNTERS,
                       (GLint*)(caps->v2.max_atomic_counters + PIPE_SHADER_COMPUTE));
         glGetIntegerv(GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS,
                       (GLint*)(caps->v2.max_atomic_counter_buffers + PIPE_SHADER_COMPUTE));
      }

      glGetIntegerv(GL_MAX_COMBINED_ATOMIC_COUNTERS,
                    (GLint*)&caps->v2.max_combined_atomic_counters);
      glGetIntegerv(GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS,
                    (GLint*)&caps->v2.max_combined_atomic_counter_buffers);
   }

   if (has_feature(feat_fb_no_attach))
      caps->v2.capability_bits |= VIRGL_CAP_FB_NO_ATTACH;

   if (has_feature(feat_texture_view))
      caps->v2.capability_bits |= VIRGL_CAP_TEXTURE_VIEW;

   if (has_feature(feat_txqs))
      caps->v2.capability_bits |= VIRGL_CAP_TXQS;

   if (has_feature(feat_barrier))
      caps->v2.capability_bits |= VIRGL_CAP_MEMORY_BARRIER;

   if (has_feature(feat_copy_image))
      caps->v2.capability_bits |= VIRGL_CAP_COPY_IMAGE;

   if (has_feature(feat_robust_buffer_access))
      caps->v2.capability_bits |= VIRGL_CAP_ROBUST_BUFFER_ACCESS;

   if (has_feature(feat_framebuffer_fetch))
      caps->v2.capability_bits |= VIRGL_CAP_TGSI_FBFETCH;

   if (has_feature(feat_shader_clock))
      caps->v2.capability_bits |= VIRGL_CAP_SHADER_CLOCK;

   if (has_feature(feat_texture_barrier))
      caps->v2.capability_bits |= VIRGL_CAP_TEXTURE_BARRIER;

   /* If we enable input arrays and don't have enhanced layouts then we
    * can't support components. */
   if (has_feature(feat_enhanced_layouts))
      caps->v2.capability_bits |= VIRGL_CAP_TGSI_COMPONENTS;

   if (has_feature(feat_srgb_write_control))
      caps->v2.capability_bits |= VIRGL_CAP_SRGB_WRITE_CONTROL;

   if (has_feature(feat_transform_feedback3))
         caps->v2.capability_bits |= VIRGL_CAP_TRANSFORM_FEEDBACK3;
   /* Enable feature use just now otherwise we just get a lot noise because
    * of the caps setting */
   if (vrend_debug(NULL, dbg_features))
      vrend_debug_add_flag(dbg_feature_use);

   /* always enable, only indicates that the CMD is supported */
   caps->v2.capability_bits |= VIRGL_CAP_GUEST_MAY_INIT_LOG;

   if (has_feature(feat_qbo))
      caps->v2.capability_bits |= VIRGL_CAP_QBO;

   caps->v2.capability_bits |= VIRGL_CAP_TRANSFER;

   if (vrend_check_framebuffer_mixed_color_attachements())
      caps->v2.capability_bits |= VIRGL_CAP_FBO_MIXED_COLOR_FORMATS;

   /* We want to expose ARB_gpu_shader_fp64 when running on top of ES */
   if (vrend_state.use_gles) {
      caps->v2.capability_bits |= VIRGL_CAP_FAKE_FP64;
   }

   if (has_feature(feat_indirect_draw))
      caps->v2.capability_bits |= VIRGL_CAP_BIND_COMMAND_ARGS;

   if (has_feature(feat_multi_draw_indirect))
      caps->v2.capability_bits |= VIRGL_CAP_MULTI_DRAW_INDIRECT;

   if (has_feature(feat_indirect_params))
      caps->v2.capability_bits |= VIRGL_CAP_INDIRECT_PARAMS;

   for (int i = 0; i < VIRGL_FORMAT_MAX; i++) {
      enum virgl_formats fmt = (enum virgl_formats)i;
      if (tex_conv_table[i].internalformat != 0) {
         if (vrend_format_can_readback(fmt)) {
            VREND_DEBUG(dbg_features, NULL, "Support readback of %s\n",
                        util_format_name(fmt));
            set_format_bit(&caps->v2.supported_readback_formats, fmt);
         }
      }

      if (vrend_format_can_scanout(fmt))
         set_format_bit(&caps->v2.scanout, fmt);
   }

   if (has_feature(feat_clear_texture))
      caps->v2.capability_bits |= VIRGL_CAP_CLEAR_TEXTURE;

   if (has_feature(feat_clip_control))
      caps->v2.capability_bits |= VIRGL_CAP_CLIP_HALFZ;

   if (epoxy_has_gl_extension("GL_KHR_texture_compression_astc_sliced_3d"))
      caps->v2.capability_bits |= VIRGL_CAP_3D_ASTC;

   caps->v2.capability_bits |= VIRGL_CAP_INDIRECT_INPUT_ADDR;

   caps->v2.capability_bits |= VIRGL_CAP_COPY_TRANSFER;


   if (has_feature(feat_arb_buffer_storage) && !vrend_state.use_external_blob) {
      const char *vendor = (const char *)glGetString(GL_VENDOR);
      bool is_mesa = ((strstr(renderer, "Mesa") != NULL) || (strstr(renderer, "DRM") != NULL));
      /*
       * Intel GPUs (aside from Atom, which doesn't expose GL4.5) are cache-coherent.
       * Mesa AMDGPUs use write-combine mappings for coherent/persistent memory (see
       * RADEON_FLAG_GTT_WC in si_buffer.c/r600_buffer_common.c). For Nvidia, we can guess and
       * check.  Long term, maybe a GL extension or using VK could replace these heuristics.
       *
       * Note Intel VMX ignores the caching type returned from virglrenderer, while AMD SVM and
       * ARM honor it.
       */
      if (is_mesa) {
         if (strstr(vendor, "Intel") != NULL)
            vrend_state.inferred_gl_caching_type = VIRGL_RENDERER_MAP_CACHE_CACHED;
         else if (strstr(vendor, "AMD") != NULL)
            vrend_state.inferred_gl_caching_type = VIRGL_RENDERER_MAP_CACHE_WC;
      } else {
         /* This is an educated guess since things don't explode with VMX + Nvidia. */
         if (strstr(renderer, "Quadro K2200") != NULL)
            vrend_state.inferred_gl_caching_type = VIRGL_RENDERER_MAP_CACHE_CACHED;
      }

      if (vrend_state.inferred_gl_caching_type)
         caps->v2.capability_bits |= VIRGL_CAP_ARB_BUFFER_STORAGE;
   }

#ifdef ENABLE_MINIGBM_ALLOCATION
   if (has_feature(feat_memory_object) && has_feature(feat_memory_object_fd)) {
         if (!strcmp(gbm_device_get_backend_name(gbm->device), "i915") &&
             !vrend_winsys_different_gpu())
            caps->v2.capability_bits |= VIRGL_CAP_ARB_BUFFER_STORAGE;
   }
#endif

   if (has_feature(feat_blend_equation_advanced))
      caps->v2.capability_bits_v2 |= VIRGL_CAP_V2_BLEND_EQUATION;

#ifdef HAVE_EPOXY_EGL_H
   if (egl)
      caps->v2.capability_bits_v2 |= VIRGL_CAP_V2_UNTYPED_RESOURCE;
#endif

   video_memory = vrend_renderer_get_video_memory();
   if (video_memory) {
      caps->v2.capability_bits_v2 |= VIRGL_CAP_V2_VIDEO_MEMORY;
      caps->v2.max_video_memory = video_memory;
   }

   if (has_feature(feat_ati_meminfo) || has_feature(feat_nvx_gpu_memory_info)) {
      caps->v2.capability_bits_v2 |= VIRGL_CAP_V2_MEMINFO;
   }

   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;

   // we use capability bits (not a version of protocol), because 
   // we disable this on client side if virglrenderer is used under
   // vtest. vtest can't support this, because size of resource
   // is used to create shmem. On drm path, we can use this, because
   // size of drm resource (bo) is not passed to virglrenderer and
   // we can pass "1" as size on drm path, but not on vtest.
   caps->v2.capability_bits_v2 |= VIRGL_CAP_V2_COPY_TRANSFER_BOTH_DIRECTIONS;

   if (has_feature(feat_anisotropic_filter)) {
      float max_aniso;
      glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &max_aniso);
      caps->v2.max_anisotropy = MIN2(max_aniso, 16.0);
   }

   glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max);
   caps->v2.max_texture_image_units = max;
}