static void vrend_renderer_fill_caps_v2(int gl_ver, int gles_ver,  union virgl_caps *caps)
{
   GLint max;
   GLfloat range[2];

   /* 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 = 3;

   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)) {
      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;
      caps->v2.capability_bits |= VIRGL_CAP_BGRA_SRGB_IS_EMULATED;
   }

   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_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;
}