text: /**************************************************************************
   *
   * Copyright (C) 2014 Red Hat Inc.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a
   * copy of this software and associated documentation files (the "Software"),
   * to deal in the Software without restriction, including without limitation
   * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   * and/or sell copies of the Software, and to permit persons to whom the
   * Software is furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included
   * in all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
   * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
   * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
   * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
   * OTHER DEALINGS IN THE SOFTWARE.
   *
   **************************************************************************/
  
  /* gallium blitter implementation in GL */
  /* for when we can't use glBlitFramebuffer */
  
  #include <stdio.h>
  
  #include "util/u_memory.h"
  #include "util/u_format.h"
  #include "util/u_hash_table.h"
  #include "util/u_texture.h"
  
  #include "vrend_shader.h"
  #include "vrend_renderer.h"
  #include "vrend_blitter.h"
  
  #define XXH_INLINE_ALL
  #include "util/xxhash.h"
  
  #define DEST_SWIZZLE_SNIPPET_SIZE 64
  
  #define BLIT_USE_GLES           (1 << 0)
  #define BLIT_USE_MSAA           (1 << 1)
  #define BLIT_USE_DEPTH          (1 << 2)
  #define BLIT_MANUAL_SRGB_DECODE (1 << 3)
  #define BLIT_MANUAL_SRGB_ENCODE (1 << 4)
  
  struct vec4 {
     GLfloat x,y,z,w;
  };
  
  struct blit_coord {
    struct vec4 pos;
    struct vec4 tex;
  };
  
  struct vrend_blitter_ctx {
     virgl_gl_context gl_context;
     bool initialised;
     bool use_gles;
  
     GLuint vaoid;
  
     struct util_hash_table *blit_programs;
  
     GLuint vs;
     GLuint fb_id;
  
     unsigned dst_width;
     unsigned dst_height;
  
     GLuint vbo_id;
     struct blit_coord vertices[4];
  };
  
  static struct vrend_blitter_ctx vrend_blit_ctx;
  
  struct blit_point {
      int x;
      int y;
  };
  
  struct blit_swizzle_and_type {
    char *swizzle;
    char *type;
    bool is_array;
  };
  
  struct blit_prog_key {
     bool is_color: 1;
     bool is_msaa: 1;
     bool manual_srgb_decode: 1;
     bool manual_srgb_encode: 1;
     uint8_t num_samples;
     int pipe_tex_target;
     struct {
        bool has_swizzle;
        enum virgl_formats src_format;
        uint8_t swizzle[4];
     } texcol;
  };
  
  static GLint blit_shader_build_and_check(GLenum shader_type, const char *buf)
  {
     GLint param;
     GLint id = glCreateShader(shader_type);
     glShaderSource(id, 1, (const char **)&buf, NULL);
     glCompileShader(id);
  
     glGetShaderiv(id, GL_COMPILE_STATUS, &param);
     if (param == GL_FALSE) {
        char infolog[65536];
        int len;
        glGetShaderInfoLog(id, 65536, &len, infolog);
        vrend_printf("shader failed to compile\n%s\n", infolog);
        vrend_printf("GLSL:\n%s\n", buf);
        glDeleteShader(id);
        return 0;
     }
     return id;
  }
  
  static bool blit_shader_link_and_check(GLuint prog_id)
  {
     GLint lret;
  
     glLinkProgram(prog_id);
     glGetProgramiv(prog_id, GL_LINK_STATUS, &lret);
     if (lret == GL_FALSE) {
        char infolog[65536];
        int len;
        glGetProgramInfoLog(prog_id, 65536, &len, infolog);
        vrend_printf("got error linking\n%s\n", infolog);
        /* dump shaders */
        glDeleteProgram(prog_id);
        return false;
     }
     return true;
  }
  
  static void create_dest_swizzle_snippet(const uint8_t swizzle[4],
                                          char snippet[DEST_SWIZZLE_SNIPPET_SIZE])
  {
     static const uint8_t invalid_swizzle = 0xff;
     ssize_t si = 0;
     uint8_t inverse[4] = {invalid_swizzle, invalid_swizzle,
                           invalid_swizzle, invalid_swizzle};
  
     for (int i = 0; i < 4; ++i) {
        if (swizzle[i] > 3)
           continue;
  
        if (inverse[swizzle[i]] == invalid_swizzle)
           inverse[swizzle[i]] = i;
     }
  
     for (int i = 0; i < 4; ++i) {
        int res = -1;
        if (inverse[i] > 3) {
            /* Use 0.0f for unused color values, 1.0f for an unused alpha value */
           res = snprintf(&snippet[si], DEST_SWIZZLE_SNIPPET_SIZE - si,
                          i < 3 ? "0.0f, " : "1.0f");
        } else {
           res = snprintf(&snippet[si], DEST_SWIZZLE_SNIPPET_SIZE - si,
                          "texel.%c%s", "rgba"[inverse[i]], i < 3 ? ", " : "");
        }
        si += res > 0 ? res : 0;
     }
  }
  
  static const char *vec4_type_for_tgsi_ret(enum tgsi_return_type tgsi_ret)
  {
     switch (tgsi_ret) {
     case TGSI_RETURN_TYPE_SINT: return "ivec4";
     case TGSI_RETURN_TYPE_UINT: return "uvec4";
     default: return "vec4";
     }
  }
  
  static enum tgsi_return_type tgsi_ret_for_format(enum virgl_formats format)
  {
     if (util_format_is_pure_uint(format))
        return TGSI_RETURN_TYPE_UINT;
     else if (util_format_is_pure_sint(format))
        return TGSI_RETURN_TYPE_SINT;
  
     return TGSI_RETURN_TYPE_UNORM;
  }
  
  static void blit_get_swizzle(int tgsi_tex_target, unsigned flags,
                               struct blit_swizzle_and_type *retval)
  {
     retval->swizzle = "";
     retval->type = "";
     retval->is_array = false;
     switch (tgsi_tex_target) {
     case TGSI_TEXTURE_1D:
        if (flags & (BLIT_USE_GLES | BLIT_USE_DEPTH)) {
           retval->swizzle = ".xy";
           break;
        }
        /* fallthrough */
     case TGSI_TEXTURE_BUFFER:
        retval->swizzle = ".x";
        break;
     case TGSI_TEXTURE_2D_MSAA:
        if (flags & BLIT_USE_MSAA) {
           retval->type = "ivec2";
        }
        retval->swizzle = ".xy";
        break;
     case TGSI_TEXTURE_1D_ARRAY:
        if (flags & (BLIT_USE_GLES)) {
           retval->swizzle = ".xyz";
           break;
        }
        /* fallthrough */
     case TGSI_TEXTURE_2D:
     case TGSI_TEXTURE_RECT:
        retval->swizzle = ".xy";
        break;
     case TGSI_TEXTURE_2D_ARRAY_MSAA:
        if (flags & BLIT_USE_MSAA) {
           retval->type = "ivec3";
           retval->is_array = true;
        }
        /* fallthrough */
     case TGSI_TEXTURE_SHADOW1D:
     case TGSI_TEXTURE_SHADOW2D:
     case TGSI_TEXTURE_SHADOW1D_ARRAY:
     case TGSI_TEXTURE_SHADOWRECT:
     case TGSI_TEXTURE_3D:
     case TGSI_TEXTURE_CUBE:
     case TGSI_TEXTURE_2D_ARRAY:
        retval->swizzle = ".xyz";
        break;
     case TGSI_TEXTURE_SHADOWCUBE:
     case TGSI_TEXTURE_SHADOW2D_ARRAY:
     case TGSI_TEXTURE_SHADOWCUBE_ARRAY:
     case TGSI_TEXTURE_CUBE_ARRAY:
        retval->swizzle = "";
        break;
     default:
        if (flags & BLIT_USE_MSAA) {
           break;
        }
        retval->swizzle = ".xy";
        break;
     }
  }
  
  static GLuint blit_build_frag_tex_col(struct vrend_blitter_ctx *blit_ctx,
                                        int tgsi_tex_target,
                                        enum tgsi_return_type tgsi_ret,
                                        const uint8_t swizzle[4],
                                        int nr_samples,
                                        uint32_t flags)
  {
     char shader_buf[4096];
     struct blit_swizzle_and_type swizzle_and_type;
     unsigned swizzle_flags = 0;
     char dest_swizzle_snippet[DEST_SWIZZLE_SNIPPET_SIZE] = "texel";
     const char *ext_str = "";
     bool msaa = nr_samples > 0;
  
     if (msaa && !blit_ctx->use_gles)
        ext_str = "#extension GL_ARB_texture_multisample : enable\n";
     else if (tgsi_tex_target == TGSI_TEXTURE_CUBE_ARRAY ||
              tgsi_tex_target == TGSI_TEXTURE_SHADOWCUBE_ARRAY) {
        if (blit_ctx->use_gles)
           ext_str = "#extension GL_EXT_texture_cube_map_array : require\n";
        else
           ext_str = "#extension GL_ARB_texture_cube_map_array : require\n";
     }
  
     if (blit_ctx->use_gles)
        swizzle_flags |= BLIT_USE_GLES;
     if (msaa)
        swizzle_flags |= BLIT_USE_MSAA;
     blit_get_swizzle(tgsi_tex_target, swizzle_flags, &swizzle_and_type);
  
     if (swizzle)
        create_dest_swizzle_snippet(swizzle, dest_swizzle_snippet);
  
     bool needs_manual_srgb_decode = has_bit(flags, BLIT_MANUAL_SRGB_DECODE);
     bool needs_manual_srgb_encode = has_bit(flags, BLIT_MANUAL_SRGB_ENCODE);
  
     if (msaa)
        snprintf(shader_buf, 4096, blit_ctx->use_gles ?
                                   (swizzle_and_type.is_array ? FS_TEXFETCH_COL_MSAA_ARRAY_GLES
                                                     : FS_TEXFETCH_COL_MSAA_GLES)
                                   : FS_TEXFETCH_COL_MSAA_GL,
           ext_str, vec4_type_for_tgsi_ret(tgsi_ret),
           needs_manual_srgb_decode ? FS_FUNC_COL_SRGB_DECODE : "",
           needs_manual_srgb_encode ? FS_FUNC_COL_SRGB_ENCODE : "",
           needs_manual_srgb_decode ? "srgb_decode" : "",
           needs_manual_srgb_encode ? "srgb_encode" : "",
           vrend_shader_samplerreturnconv(tgsi_ret),
           vrend_shader_samplertypeconv(blit_ctx->use_gles, tgsi_tex_target),
           nr_samples, swizzle_and_type.type, swizzle_and_type.swizzle, dest_swizzle_snippet);
     else
        snprintf(shader_buf, 4096, blit_ctx->use_gles ?
                                   (tgsi_tex_target == TGSI_TEXTURE_1D ?
                                      FS_TEXFETCH_COL_GLES_1D : FS_TEXFETCH_COL_GLES)
                                   : FS_TEXFETCH_COL_GL,
           ext_str, vec4_type_for_tgsi_ret(tgsi_ret),
           needs_manual_srgb_decode ? FS_FUNC_COL_SRGB_DECODE : "",
           needs_manual_srgb_encode ? FS_FUNC_COL_SRGB_ENCODE : "",
           needs_manual_srgb_decode ? "srgb_decode" : "",
           needs_manual_srgb_encode ? "srgb_encode" : "",
           vrend_shader_samplerreturnconv(tgsi_ret),
           vrend_shader_samplertypeconv(blit_ctx->use_gles, tgsi_tex_target),
           swizzle_and_type.swizzle, dest_swizzle_snippet);
  
     VREND_DEBUG(dbg_blit, NULL, "-- Blit FS color shader MSAA: %d -----------------\n"
                 "%s\n---------------------------------------\n", msaa, shader_buf);
  
     return blit_shader_build_and_check(GL_FRAGMENT_SHADER, shader_buf);
  }
  
  static GLuint blit_build_frag_depth(struct vrend_blitter_ctx *blit_ctx, int tgsi_tex_target, bool msaa)
  {
     char shader_buf[4096];
     struct blit_swizzle_and_type swizzle_and_type;
     unsigned flags = BLIT_USE_DEPTH;
  
     if (msaa)
        flags |= BLIT_USE_MSAA;
  
     blit_get_swizzle(tgsi_tex_target, flags, &swizzle_and_type);
  
     if (msaa)
        snprintf(shader_buf, 4096, blit_ctx->use_gles ?
                                   (swizzle_and_type.is_array ?  FS_TEXFETCH_DS_MSAA_ARRAY_GLES :  FS_TEXFETCH_DS_MSAA_GLES)
                                   : FS_TEXFETCH_DS_MSAA_GL,
           vrend_shader_samplertypeconv(blit_ctx->use_gles, tgsi_tex_target), swizzle_and_type.type, swizzle_and_type.swizzle);
     else
        snprintf(shader_buf, 4096, blit_ctx->use_gles ? FS_TEXFETCH_DS_GLES : FS_TEXFETCH_DS_GL,
           vrend_shader_samplertypeconv(blit_ctx->use_gles, tgsi_tex_target), swizzle_and_type.swizzle);
  
     VREND_DEBUG(dbg_blit, NULL, "-- Blit FS depth shader MSAA: %d -----------------\n"
                 "%s\n---------------------------------------\n", msaa, shader_buf);
  
     return blit_shader_build_and_check(GL_FRAGMENT_SHADER, shader_buf);
  }
  
  static GLuint blit_get_frag_tex_writedepth(struct vrend_blitter_ctx *blit_ctx, int pipe_tex_target, unsigned nr_samples)
  {
     struct blit_prog_key key = {
           .is_color = false,
           .is_msaa = nr_samples > 0,
           .num_samples = nr_samples,
           .pipe_tex_target = pipe_tex_target,
        };
  
        void *shader = util_hash_table_get(blit_ctx->blit_programs, &key);
        GLuint prog_id;
        if (shader) {
           prog_id = (GLuint)((size_t)(shader) & 0xffffffff);
        } else {
           prog_id = glCreateProgram();
           glAttachShader(prog_id, blit_ctx->vs);
           unsigned tgsi_tex = util_pipe_tex_to_tgsi_tex(pipe_tex_target, key.num_samples);
           GLuint fs_id = blit_build_frag_depth(blit_ctx, tgsi_tex, key.is_msaa);
           glAttachShader(prog_id, fs_id);
           if(!blit_shader_link_and_check(prog_id))
              return 0;
  
           glDeleteShader(fs_id);
           util_hash_table_set(blit_ctx->blit_programs, &key, (void *)(uintptr_t)prog_id);
        }
        return prog_id;
  }
  
  static GLuint blit_get_frag_tex_col(struct vrend_blitter_ctx *blit_ctx,
                                         int pipe_tex_target,
                                         unsigned nr_samples,
                                         const struct vrend_format_table *src_entry,
                                         const uint8_t swizzle[static 4],
                                         uint32_t flags)
  {
     bool needs_swizzle = false;
     for (uint i = 0; i < 4; ++i) {
        if (swizzle[i] != i) {
           needs_swizzle = true;
           break;
        }
     }
  
     struct blit_prog_key key = {
        .is_color = true,
        .is_msaa = nr_samples > 0,
        .manual_srgb_decode = has_bit(flags, BLIT_MANUAL_SRGB_DECODE),
        .manual_srgb_encode = has_bit(flags, BLIT_MANUAL_SRGB_ENCODE),
        .num_samples = nr_samples,
        .pipe_tex_target  = pipe_tex_target
     };
  
     key.texcol.src_format = src_entry->format;
     key.texcol.has_swizzle = needs_swizzle;
     if (key.texcol.has_swizzle)
        memcpy(key.texcol.swizzle, swizzle, 4);
  
     GLuint prog_id = 0;
     void *shader = util_hash_table_get(blit_ctx->blit_programs, &key);
  
     if (shader) {
        prog_id = (GLuint)((size_t)(shader) & 0xffffffff);
     } else {
        prog_id = glCreateProgram();
        glAttachShader(prog_id, blit_ctx->vs);
        unsigned tgsi_tex = util_pipe_tex_to_tgsi_tex(pipe_tex_target, key.num_samples);
        enum tgsi_return_type tgsi_ret = tgsi_ret_for_format(src_entry->format);
        int msaa_samples = nr_samples > 0 ? (tgsi_ret == TGSI_RETURN_TYPE_UNORM ? nr_samples : 1) : 0;
  
        GLuint fs_id = blit_build_frag_tex_col(blit_ctx, tgsi_tex, tgsi_ret,
                                               swizzle, msaa_samples, flags);
        glAttachShader(prog_id, fs_id);
        if(!blit_shader_link_and_check(prog_id))
           return 0;
  
        glDeleteShader(fs_id);
        util_hash_table_set(blit_ctx->blit_programs, &key, (void *)(uintptr_t)prog_id);
     }
  
     return prog_id;
  }
  
  static uint32_t program_hash_func(void *key)
  {
     return XXH32(key, sizeof(struct blit_prog_key), 0);
  }
  
  static int program_comp_func(void *key1, void *key2)
  {
     return memcmp(key1, key2, sizeof(struct blit_prog_key));
  }
  
  static void program_destroy_func(void *shader_id)
  {
     GLuint id = ((uint64_t)(shader_id)) & 0xffffffff;
     glDeleteProgram(id);
  }
  
  
  static void vrend_renderer_init_blit_ctx(struct vrend_blitter_ctx *blit_ctx)
  {
     struct virgl_gl_ctx_param ctx_params;
     int i;
     if (blit_ctx->initialised) {
        vrend_sync_make_current(blit_ctx->gl_context);
        return;
     }
  
     vrend_blit_ctx.blit_programs = util_hash_table_create(program_hash_func,
                                                           program_comp_func,
                                                           program_destroy_func);
  
     blit_ctx->use_gles = epoxy_is_desktop_gl() == 0;
     ctx_params.shared = true;
     for (uint32_t i = 0; i < ARRAY_SIZE(gl_versions); i++) {
        ctx_params.major_ver = gl_versions[i].major;
        ctx_params.minor_ver = gl_versions[i].minor;
  
        blit_ctx->gl_context = vrend_clicbs->create_gl_context(0, &ctx_params);
        if (blit_ctx->gl_context)
           break;
     }
  
     if (!blit_ctx->gl_context) {
        vrend_printf("virglrenderer: Unable to create blit context");
        abort();
     }
  
     vrend_sync_make_current(blit_ctx->gl_context);
     glGenVertexArrays(1, &blit_ctx->vaoid);
     glGenFramebuffers(1, &blit_ctx->fb_id);
  
     glGenBuffers(1, &blit_ctx->vbo_id);
     blit_ctx->vs = blit_shader_build_and_check(GL_VERTEX_SHADER,
          blit_ctx->use_gles ? VS_PASSTHROUGH_GLES : VS_PASSTHROUGH_GL);
  
     for (i = 0; i < 4; i++) {
        blit_ctx->vertices[i].pos.z = 0;
        blit_ctx->vertices[i].pos.w = 1;
     }
  
     glBindVertexArray(blit_ctx->vaoid);
     glBindBuffer(GL_ARRAY_BUFFER, blit_ctx->vbo_id);
  
     if (!blit_ctx->use_gles)
        glEnable(GL_FRAMEBUFFER_SRGB);
  
     blit_ctx->initialised = true;
  }
  
  static void blitter_set_rectangle(struct vrend_blitter_ctx *blit_ctx,
                                    int x1, int y1, int x2, int y2)
  {
     /* set vertex positions */
     blit_ctx->vertices[0].pos.x = (float)x1 / blit_ctx->dst_width * 2.0f - 1.0f; /*v0.x*/
     blit_ctx->vertices[0].pos.y = (float)y1 / blit_ctx->dst_height * 2.0f - 1.0f; /*v0.y*/
  
     blit_ctx->vertices[1].pos.x = (float)x2 / blit_ctx->dst_width * 2.0f - 1.0f; /*v1.x*/
     blit_ctx->vertices[1].pos.y = (float)y1 / blit_ctx->dst_height * 2.0f - 1.0f; /*v1.y*/
  
     blit_ctx->vertices[2].pos.x = (float)x2 / blit_ctx->dst_width * 2.0f - 1.0f; /*v2.x*/
     blit_ctx->vertices[2].pos.y = (float)y2 / blit_ctx->dst_height * 2.0f - 1.0f; /*v2.y*/
  
     blit_ctx->vertices[3].pos.x = (float)x1 / blit_ctx->dst_width * 2.0f - 1.0f; /*v3.x*/
     blit_ctx->vertices[3].pos.y = (float)y2 / blit_ctx->dst_height * 2.0f - 1.0f; /*v3.y*/
  
     glViewport(0, 0, blit_ctx->dst_width, blit_ctx->dst_height);
  }
  
  static void get_texcoords(struct vrend_blitter_ctx *blit_ctx,
                            struct vrend_resource *src_res,
                            int src_level,
                            int x1, int y1, int x2, int y2,
                            float out[4])
  {
     bool normalized = (src_res->base.target != PIPE_TEXTURE_RECT || blit_ctx->use_gles) &&
                       src_res->base.nr_samples < 1;
  
     if (normalized) {
        out[0] = x1 / (float)u_minify(src_res->base.width0,  src_level);
        out[1] = y1 / (float)u_minify(src_res->base.height0, src_level);
        out[2] = x2 / (float)u_minify(src_res->base.width0,  src_level);
        out[3] = y2 / (float)u_minify(src_res->base.height0, src_level);
     } else {
        out[0] = (float) x1;
        out[1] = (float) y1;
        out[2] = (float) x2;
        out[3] = (float) y2;
     }
  }
  
  static void set_texcoords_in_vertices(const float coord[4],
                                        float *out, unsigned stride)
  {
     out[0] = coord[0]; /*t0.s*/
     out[1] = coord[1]; /*t0.t*/
     out += stride;
     out[0] = coord[2]; /*t1.s*/
     out[1] = coord[1]; /*t1.t*/
     out += stride;
     out[0] = coord[2]; /*t2.s*/
     out[1] = coord[3]; /*t2.t*/
     out += stride;
     out[0] = coord[0]; /*t3.s*/
     out[1] = coord[3]; /*t3.t*/
  }
  
  static void blitter_set_texcoords(struct vrend_blitter_ctx *blit_ctx,
                                    struct vrend_resource *src_res,
                                    int level,
                                    float layer, unsigned sample,
                                    int x1, int y1, int x2, int y2)
  {
     float coord[4];
     float face_coord[4][2];
     int i;
     get_texcoords(blit_ctx, src_res, level, x1, y1, x2, y2, coord);
  
     if (src_res->base.target == PIPE_TEXTURE_CUBE ||
         src_res->base.target == PIPE_TEXTURE_CUBE_ARRAY) {
        set_texcoords_in_vertices(coord, &face_coord[0][0], 2);
        util_map_texcoords2d_onto_cubemap((unsigned)layer % 6,
                                          /* pointer, stride in floats */
                                          &face_coord[0][0], 2,
                                          &blit_ctx->vertices[0].tex.x, 8,
                                          FALSE);
     } else {
        set_texcoords_in_vertices(coord, &blit_ctx->vertices[0].tex.x, 8);
     }
  
     switch (src_res->base.target) {
     case PIPE_TEXTURE_3D:
     {
        float r = layer / (float)u_minify(src_res->base.depth0,
                                          level);
        for (i = 0; i < 4; i++)
           blit_ctx->vertices[i].tex.z = r; /*r*/
     }
     break;
  
     case PIPE_TEXTURE_1D_ARRAY:
        for (i = 0; i < 4; i++)
           blit_ctx->vertices[i].tex.y = (float) layer; /*t*/
        break;
  
     case PIPE_TEXTURE_2D_ARRAY:
        for (i = 0; i < 4; i++) {
           blit_ctx->vertices[i].tex.z = (float) layer;  /*r*/
           blit_ctx->vertices[i].tex.w = (float) sample; /*q*/
        }
        break;
     case PIPE_TEXTURE_CUBE_ARRAY:
        for (i = 0; i < 4; i++)
           blit_ctx->vertices[i].tex.w = (float) ((unsigned)layer / 6); /*w*/
        break;
     case PIPE_TEXTURE_2D:
        for (i = 0; i < 4; i++) {
           blit_ctx->vertices[i].tex.w = (float) sample; /*r*/
        }
        break;
     default:;
     }
  }
  
  static void set_dsa_write_depth_keep_stencil(void)
  {
     glDisable(GL_STENCIL_TEST);
     glEnable(GL_DEPTH_TEST);
     glDepthFunc(GL_ALWAYS);
     glDepthMask(GL_TRUE);
  }
  
  static inline GLenum to_gl_swizzle(int swizzle)
  {
     switch (swizzle) {
     case PIPE_SWIZZLE_RED: return GL_RED;
     case PIPE_SWIZZLE_GREEN: return GL_GREEN;
     case PIPE_SWIZZLE_BLUE: return GL_BLUE;
     case PIPE_SWIZZLE_ALPHA: return GL_ALPHA;
     case PIPE_SWIZZLE_ZERO: return GL_ZERO;
     case PIPE_SWIZZLE_ONE: return GL_ONE;
     default:
        assert(0);
        return 0;
     }
  }
  
  /* Calculate the delta required to keep 'v' within [0, max] */
  static int calc_delta_for_bound(int v, int max)
  {
      int delta = 0;
  
      if (v < 0)
          delta = -v;
      else if (v > max)
          delta = - (v - max);
  
      return delta;
  }
  
  /* Calculate the deltas for the source blit region points in order to bound
   * them within the source resource extents */
  static void calc_src_deltas_for_bounds(struct vrend_resource *src_res,
                                         const struct pipe_blit_info *info,
                                         struct blit_point *src0_delta,
                                         struct blit_point *src1_delta)
  {
     int max_x = u_minify(src_res->base.width0, info->src.level) - 1;
     int max_y = u_minify(src_res->base.height0, info->src.level) - 1;
  
     /* Whether the bounds for the coordinates of a point are inclusive or
      * exclusive depends on the direction of the blit read. Adjust the max
      * bounds accordingly, with an adjustment of 0 for inclusive, and 1 for
      * exclusive. */
     int src0_x_excl = info->src.box.width < 0;
     int src0_y_excl = info->src.box.height < 0;
  
     src0_delta->x = calc_delta_for_bound(info->src.box.x, max_x + src0_x_excl);
     src0_delta->y = calc_delta_for_bound(info->src.box.y, max_y + src0_y_excl);
  
     src1_delta->x = calc_delta_for_bound(info->src.box.x + info->src.box.width,
                                           max_x + !src0_x_excl);
     src1_delta->y = calc_delta_for_bound(info->src.box.y + info->src.box.height,
                                           max_y + !src0_y_excl);
  }
  
  /* Calculate dst delta values to adjust the dst points for any changes in the
   * src points */
  static void calc_dst_deltas_from_src(const struct pipe_blit_info *info,
                                       const struct blit_point *src0_delta,
                                       const struct blit_point *src1_delta,
                                       struct blit_point *dst0_delta,
                                       struct blit_point *dst1_delta)
  {
     float scale_x = (float)info->dst.box.width / (float)info->src.box.width;
     float scale_y = (float)info->dst.box.height / (float)info->src.box.height;
  
     dst0_delta->x = src0_delta->x * scale_x;
     dst0_delta->y = src0_delta->y * scale_y;
  
     dst1_delta->x = src1_delta->x * scale_x;
     dst1_delta->y = src1_delta->y * scale_y;
  }
  
  static void blitter_set_points(struct vrend_blitter_ctx *blit_ctx,
                                 const struct pipe_blit_info *info,
                                 struct vrend_resource *src_res,
                                 struct vrend_resource *dst_res,
                                 struct blit_point *src0,
                                 struct blit_point *src1)
  {
     struct blit_point dst0, dst1;
     struct blit_point src0_delta, src1_delta, dst0_delta, dst1_delta;
  
     blit_ctx->dst_width = u_minify(dst_res->base.width0, info->dst.level);
     blit_ctx->dst_height = u_minify(dst_res->base.height0, info->dst.level);
  
     /* Calculate src and dst points taking deltas into account */
     calc_src_deltas_for_bounds(src_res, info, &src0_delta, &src1_delta);
     calc_dst_deltas_from_src(info, &src0_delta, &src1_delta, &dst0_delta, &dst1_delta);
  
     src0->x = info->src.box.x + src0_delta.x;
     src0->y = info->src.box.y + src0_delta.y;
     src1->x = info->src.box.x + info->src.box.width + src1_delta.x;
     src1->y = info->src.box.y + info->src.box.height + src1_delta.y;
  
     dst0.x = info->dst.box.x + dst0_delta.x;
     dst0.y = info->dst.box.y + dst0_delta.y;
     dst1.x = info->dst.box.x + info->dst.box.width + dst1_delta.x;
     dst1.y = info->dst.box.y + info->dst.box.height + dst1_delta.y;
  
     VREND_DEBUG(dbg_blit, NULL, "Blitter src:[%3d, %3d] - [%3d, %3d] to dst:[%3d, %3d] - [%3d, %3d]\n",
                 src0->x, src0->y, src1->x, src1->y,
                 dst0.x, dst0.y, dst1.x, dst1.y);
  
     blitter_set_rectangle(blit_ctx, dst0.x, dst0.y, dst1.x, dst1.y);
  }
  
  static void vrend_set_tex_param(struct vrend_resource *src_res,
                                  const struct pipe_blit_info *info,
                                  bool has_texture_srgb_decode)
  {
     const struct vrend_format_table *src_entry =
        vrend_get_format_table_entry(info->src.format);
  
     if (src_entry->flags & VIRGL_TEXTURE_NEED_SWIZZLE) {
        glTexParameteri(src_res->target, GL_TEXTURE_SWIZZLE_R,
                        to_gl_swizzle(src_entry->swizzle[0]));
        glTexParameteri(src_res->target, GL_TEXTURE_SWIZZLE_G,
                        to_gl_swizzle(src_entry->swizzle[1]));
        glTexParameteri(src_res->target, GL_TEXTURE_SWIZZLE_B,
                        to_gl_swizzle(src_entry->swizzle[2]));
        glTexParameteri(src_res->target, GL_TEXTURE_SWIZZLE_A,
                        to_gl_swizzle(src_entry->swizzle[3]));
     }
  
     /* Just make sure that no stale state disabled decoding */
     if (has_texture_srgb_decode && util_format_is_srgb(info->src.format) &&
         src_res->base.nr_samples < 1)
        glTexParameteri(src_res->target, GL_TEXTURE_SRGB_DECODE_EXT, GL_DECODE_EXT);
  
     if (src_res->base.nr_samples < 1) {
        glTexParameteri(src_res->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(src_res->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(src_res->target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
     }
  
     glTexParameteri(src_res->target, GL_TEXTURE_BASE_LEVEL, info->src.level);
     glTexParameteri(src_res->target, GL_TEXTURE_MAX_LEVEL, info->src.level);
  
     if (src_res->base.nr_samples < 1) {
        GLenum filter = info->filter == PIPE_TEX_FILTER_NEAREST ?
                                         GL_NEAREST : GL_LINEAR;
        glTexParameteri(src_res->target, GL_TEXTURE_MAG_FILTER, filter);
        glTexParameteri(src_res->target, GL_TEXTURE_MIN_FILTER, filter);
     }
  }
  
  static void vrend_set_vertex_param(GLuint prog_id)
  {
     GLuint pos_loc, tc_loc;
  
     pos_loc = glGetAttribLocation(prog_id, "arg0");
     tc_loc = glGetAttribLocation(prog_id, "arg1");
  
     glVertexAttribPointer(pos_loc, 4, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)0);
     glVertexAttribPointer(tc_loc, 4, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)(4 * sizeof(float)));
  
     glEnableVertexAttribArray(pos_loc);
     glEnableVertexAttribArray(tc_loc);
  }
  
  /* implement blitting using OpenGL. */
  void vrend_renderer_blit_gl(ASSERTED struct vrend_context *ctx,
                              struct vrend_resource *src_res,
                              struct vrend_resource *dst_res,
                              const struct vrend_blit_info *info)
  {
     struct vrend_blitter_ctx *blit_ctx = &vrend_blit_ctx;
  
     int dst_z;
     struct blit_point src0, src1;
     const struct util_format_description *src_desc =
        util_format_description(src_res->base.format);
     const struct util_format_description *dst_desc =
        util_format_description(dst_res->base.format);
     const struct vrend_format_table *orig_src_entry = vrend_get_format_table_entry(info->b.src.format);
  
     bool blit_depth = util_format_has_depth(src_desc) &&
           util_format_has_depth(dst_desc) &&
           (info->b.mask & PIPE_MASK_Z);
  
     vrend_renderer_init_blit_ctx(blit_ctx);
     blitter_set_points(blit_ctx, &info->b, src_res, dst_res, &src0, &src1);
  
     GLuint prog_id;
  
     if (blit_depth) {
        prog_id = blit_get_frag_tex_writedepth(blit_ctx, src_res->base.target,
                                               src_res->base.nr_samples);
     } else {
        VREND_DEBUG(dbg_blit, ctx, "BLIT: applying swizzle during blit: (%d %d %d %d)\n",
                    info->swizzle[0], info->swizzle[1], info->swizzle[2], info->swizzle[3]);
  
        if (info->needs_manual_srgb_decode)
           VREND_DEBUG(dbg_blit, ctx,
                       "BLIT: applying manual srgb->linear conversion for src %s(%s)\n",
                       util_format_name(src_res->base.format),
                       util_format_name(info->b.src.format));
  
        if (info->needs_manual_srgb_encode)
           VREND_DEBUG(dbg_blit, ctx,
                       "BLIT: applying manual linear->srgb conversion for dst %s(%s)\n",
                       util_format_name(dst_res->base.format),
                       util_format_name(info->b.dst.format));
  
        uint32_t flags = 0;
        flags |= info->needs_manual_srgb_decode ? BLIT_MANUAL_SRGB_DECODE : 0;
        flags |= info->needs_manual_srgb_encode ? BLIT_MANUAL_SRGB_ENCODE : 0;
        prog_id = blit_get_frag_tex_col(blit_ctx, src_res->base.target,
                                        src_res->base.nr_samples,
                                        orig_src_entry,
                                        info->swizzle,
                                        flags);
     }
     if (!prog_id) {
        vrend_printf("Blitter: unable to create or find shader program\n");
        return;
     }
  
     glUseProgram(prog_id);
  
     glBindFramebuffer(GL_FRAMEBUFFER, blit_ctx->fb_id);
     vrend_fb_bind_texture_id(dst_res, info->dst_view, 0, info->b.dst.level, info->b.dst.box.z, 0);
  
     GLuint buffers = GL_COLOR_ATTACHMENT0;
     glDrawBuffers(1, &buffers);
  
     glBindTexture(src_res->target, info->src_view);
     vrend_set_tex_param(src_res, &info->b,
                         info->has_texture_srgb_decode &&
                         !info->needs_manual_srgb_decode);
     vrend_set_vertex_param(prog_id);
  
     set_dsa_write_depth_keep_stencil();
  
     if (info->b.scissor_enable) {
        glScissor(info->b.scissor.minx, info->b.scissor.miny,
                  info->b.scissor.maxx - info->b.scissor.minx,
                  info->b.scissor.maxy - info->b.scissor.miny);
        glEnable(GL_SCISSOR_TEST);
     } else
        glDisable(GL_SCISSOR_TEST);
  
     if (info->has_srgb_write_control) {
        if (!info->needs_manual_srgb_encode &&
            (util_format_is_srgb(info->b.dst.format) || util_format_is_srgb(info->b.src.format))) {
           VREND_DEBUG(dbg_blit, ctx, "%s: Enable GL_FRAMEBUFFER_SRGB\n", __func__);
           glEnable(GL_FRAMEBUFFER_SRGB);
        } else {
           VREND_DEBUG(dbg_blit, ctx, "%s: Disable GL_FRAMEBUFFER_SRGB\n", __func__);
           glDisable(GL_FRAMEBUFFER_SRGB);
        }
     }
  
     for (dst_z = 0; dst_z < info->b.dst.box.depth; dst_z++) {
        float dst2src_scale = info->b.src.box.depth / (float)info->b.dst.box.depth;
        float dst_offset = ((info->b.src.box.depth - 1) -
                            (info->b.dst.box.depth - 1) * dst2src_scale) * 0.5;
        float src_z = (dst_z + dst_offset) * dst2src_scale;
  
        uint32_t layer = (dst_res->target == GL_TEXTURE_CUBE_MAP ||
                          dst_res->target == GL_TEXTURE_1D_ARRAY ||
                          dst_res->target == GL_TEXTURE_2D_ARRAY) ? info->b.dst.box.z : dst_z;
  
        vrend_fb_bind_texture_id(dst_res, info->dst_view, 0, info->b.dst.level, layer, 0);
  
        blitter_set_texcoords(blit_ctx, src_res, info->b.src.level,
                              info->b.src.box.z + src_z, 0,
                              src0.x, src0.y, src1.x, src1.y);
  
        glBufferData(GL_ARRAY_BUFFER, sizeof(blit_ctx->vertices), blit_ctx->vertices, GL_STATIC_DRAW);
        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
     }
  
     glUseProgram(0);
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
                               GL_TEXTURE_2D, 0, 0);
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                               GL_TEXTURE_2D, 0, 0);
     glBindTexture(src_res->target, 0);
  }
  
  void vrend_blitter_fini(void)
  {
     vrend_blit_ctx.initialised = false;
     vrend_clicbs->destroy_gl_context(vrend_blit_ctx.gl_context);
     if (vrend_blit_ctx.blit_programs)
        util_hash_table_destroy(vrend_blit_ctx.blit_programs);
     memset(&vrend_blit_ctx, 0, sizeof(vrend_blit_ctx));
  }
  