/**
 * Setup uniforms telling the coordinates of the destination rectangle in the
 * native w-tiled space. These are needed to ignore pixels that lie outside.
 * The destination is drawn as Y-tiled and in some cases the Y-tiled drawing
 * rectangle is larger than the original (for example 1x4 w-tiled requires
 * 16x2 y-tiled).
 */
static void
setup_bounding_rect(GLuint prog, const struct blit_dims *dims)
{
   _mesa_Uniform1i(_mesa_GetUniformLocation(prog, "dst_x0"), dims->dst_x0);
   _mesa_Uniform1i(_mesa_GetUniformLocation(prog, "dst_x1"), dims->dst_x1);
   _mesa_Uniform1i(_mesa_GetUniformLocation(prog, "dst_y0"), dims->dst_y0);
   _mesa_Uniform1i(_mesa_GetUniformLocation(prog, "dst_y1"), dims->dst_y1);
}
/**
 * Setup uniforms telling the destination width, height and the offset. These
 * are needed to unnoormalize the input coordinates and to correctly translate
 * between destination and source that may have differing offsets.
 */
static void
setup_drawing_rect(GLuint prog, const struct blit_dims *dims)
{
   _mesa_Uniform1f(_mesa_GetUniformLocation(prog, "draw_rect_w"),
                   dims->dst_x1 - dims->dst_x0);
   _mesa_Uniform1f(_mesa_GetUniformLocation(prog, "draw_rect_h"),
                   dims->dst_y1 - dims->dst_y0);
   _mesa_Uniform1f(_mesa_GetUniformLocation(prog, "dst_x_off"), dims->dst_x0);
   _mesa_Uniform1f(_mesa_GetUniformLocation(prog, "dst_y_off"), dims->dst_y0);
}
/**
 * Setup uniforms providing relation between source and destination surfaces.
 * Destination coordinates are in Y-tiling layout while texelFetch() expects
 * W-tiled coordinates. Once the destination coordinates are re-interpreted by
 * the program into the original W-tiled layout, the program needs to know the
 * offset and scaling factors between the destination and source.
 * Note that these are calculated in the original W-tiled space before the
 * destination rectangle is adjusted for possible msaa and Y-tiling.
 */
static void
setup_coord_transform(GLuint prog, const struct blit_dims *dims)
{
   setup_coord_coeff(prog,
                     _mesa_GetUniformLocation(prog, "src_x_scale"),
                     _mesa_GetUniformLocation(prog, "src_x_off"),
                     dims->src_x0, dims->src_x1, dims->dst_x0, dims->dst_x1,
                     dims->mirror_x);

   setup_coord_coeff(prog,
                     _mesa_GetUniformLocation(prog, "src_y_scale"),
                     _mesa_GetUniformLocation(prog, "src_y_off"),
                     dims->src_y0, dims->src_y1, dims->dst_y0, dims->dst_y1,
                     dims->mirror_y);
}
static void
brw_bind_rep_write_shader(struct brw_context *brw, float *color)
{
   const char *vs_source =
      "#extension GL_AMD_vertex_shader_layer : enable\n"
      "#extension GL_ARB_draw_instanced : enable\n"
      "attribute vec4 position;\n"
      "uniform int layer;\n"
      "void main()\n"
      "{\n"
      "#ifdef GL_AMD_vertex_shader_layer\n"
      "   gl_Layer = gl_InstanceID;\n"
      "#endif\n"
      "   gl_Position = position;\n"
      "}\n";
   const char *fs_source =
      "uniform vec4 color;\n"
      "void main()\n"
      "{\n"
      "   gl_FragColor = color;\n"
      "}\n";

   GLuint vs, fs;
   struct brw_fast_clear_state *clear = brw->fast_clear_state;
   struct gl_context *ctx = &brw->ctx;

   if (clear->shader_prog) {
      _mesa_UseProgram(clear->shader_prog);
      _mesa_Uniform4fv(clear->color_location, 1, color);
      return;
   }

   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);

   clear->shader_prog = _mesa_CreateProgram();
   _mesa_AttachShader(clear->shader_prog, fs);
   _mesa_DeleteShader(fs);
   _mesa_AttachShader(clear->shader_prog, vs);
   _mesa_DeleteShader(vs);
   _mesa_BindAttribLocation(clear->shader_prog, 0, "position");
   _mesa_ObjectLabel(GL_PROGRAM, clear->shader_prog, -1, "meta clear");
   _mesa_LinkProgram(clear->shader_prog);

   clear->color_location =
      _mesa_GetUniformLocation(clear->shader_prog, "color");

   _mesa_UseProgram(clear->shader_prog);
   _mesa_Uniform4fv(clear->color_location, 1, color);
}
static void
setup_glsl_msaa_blit_scaled_shader(struct gl_context *ctx,
                                   struct blit_state *blit,
                                   struct gl_renderbuffer *src_rb,
                                   GLenum target, GLenum filter)
{
   GLint loc_src_width, loc_src_height;
   int i, samples;
   int shader_offset = 0;
   void *mem_ctx = ralloc_context(NULL);
   char *fs_source;
   char *name, *sample_number;
   const uint8_t *sample_map;
   char *sample_map_str = rzalloc_size(mem_ctx, 1);
   char *sample_map_expr = rzalloc_size(mem_ctx, 1);
   char *texel_fetch_macro = rzalloc_size(mem_ctx, 1);
   const char *sampler_array_suffix = "";
   float y_scale;
   enum blit_msaa_shader shader_index;

   assert(src_rb);
   samples = MAX2(src_rb->NumSamples, 1);
   y_scale = samples * 0.5;

   /* We expect only power of 2 samples in source multisample buffer. */
   assert(samples > 0 && _mesa_is_pow_two(samples));
   while (samples >> (shader_offset + 1)) {
      shader_offset++;
   }
   /* Update the assert if we plan to support more than 8X MSAA. */
   assert(shader_offset > 0 && shader_offset < 4);

   assert(target == GL_TEXTURE_2D_MULTISAMPLE ||
          target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY);

   shader_index = BLIT_2X_MSAA_SHADER_2D_MULTISAMPLE_SCALED_RESOLVE +
                  shader_offset - 1;

   if (target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) {
      shader_index += BLIT_2X_MSAA_SHADER_2D_MULTISAMPLE_ARRAY_SCALED_RESOLVE -
                      BLIT_2X_MSAA_SHADER_2D_MULTISAMPLE_SCALED_RESOLVE;
      sampler_array_suffix = "Array";
   }

   if (blit->msaa_shaders[shader_index]) {
      _mesa_UseProgram(blit->msaa_shaders[shader_index]);
      /* Update the uniform values. */
      loc_src_width =
         _mesa_GetUniformLocation(blit->msaa_shaders[shader_index], "src_width");
      loc_src_height =
         _mesa_GetUniformLocation(blit->msaa_shaders[shader_index], "src_height");
      _mesa_Uniform1f(loc_src_width, src_rb->Width);
      _mesa_Uniform1f(loc_src_height, src_rb->Height);
      return;
   }

   name = ralloc_asprintf(mem_ctx, "vec4 MSAA scaled resolve");

   /* Below switch is used to setup the shader expression, which computes
    * sample index and map it to to a sample number on hardware.
    */
   switch(samples) {
   case 2:
      sample_number =  "sample_map[int(2 * fract(coord.x))]";
      sample_map = ctx->Const.SampleMap2x;
      break;
   case 4:
      sample_number =  "sample_map[int(2 * fract(coord.x) + 4 * fract(coord.y))]";
      sample_map = ctx->Const.SampleMap4x;
      break;
   case 8:
      sample_number =  "sample_map[int(2 * fract(coord.x) + 8 * fract(coord.y))]";
      sample_map = ctx->Const.SampleMap8x;
      break;
   default:
      sample_number = NULL;
      sample_map = NULL;
      _mesa_problem(ctx, "Unsupported sample count %d\n", samples);
      unreachable("Unsupported sample count");
   }

   /* Create sample map string. */
   for (i = 0 ; i < samples - 1; i++) {
      ralloc_asprintf_append(&sample_map_str, "%d, ", sample_map[i]);
   }
   ralloc_asprintf_append(&sample_map_str, "%d", sample_map[samples - 1]);

   /* Create sample map expression using above string. */
   ralloc_asprintf_append(&sample_map_expr,
                          "   const int sample_map[%d] = int[%d](%s);\n",
                          samples, samples, sample_map_str);

   if (target == GL_TEXTURE_2D_MULTISAMPLE) {
      ralloc_asprintf_append(&texel_fetch_macro,
                             "#define TEXEL_FETCH(coord) texelFetch(texSampler, ivec2(coord), %s);\n",
                             sample_number);
   } else {
      ralloc_asprintf_append(&texel_fetch_macro,
                             "#define TEXEL_FETCH(coord) texelFetch(texSampler, ivec3(coord, layer), %s);\n",
                             sample_number);
   }

   static const char vs_source[] =
                               "#version 130\n"
                               "in vec2 position;\n"
                               "in vec3 textureCoords;\n"
                               "out vec2 texCoords;\n"
                               "flat out int layer;\n"
                               "void main()\n"
                               "{\n"
                               "   texCoords = textureCoords.xy;\n"
                               "   layer = int(textureCoords.z);\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%s texSampler;\n"
                               "uniform float src_width, src_height;\n"
                               "in vec2 texCoords;\n"
                               "flat in int layer;\n"
                               "out vec4 out_color;\n"
                               "\n"
                               "void main()\n"
                               "{\n"
                               "%s"
                               "   vec2 interp;\n"
                               "   const vec2 scale = vec2(2.0f, %ff);\n"
                               "   const vec2 scale_inv = vec2(0.5f, %ff);\n"
                               "   const vec2 s_0_offset = vec2(0.25f, %ff);\n"
                               "   vec2 s_0_coord, s_1_coord, s_2_coord, s_3_coord;\n"
                               "   vec4 s_0_color, s_1_color, s_2_color, s_3_color;\n"
                               "   vec4 x_0_color, x_1_color;\n"
                               "   vec2 tex_coord = texCoords - s_0_offset;\n"
                               "\n"
                               "   tex_coord *= scale;\n"
                               "   clamp(tex_coord.x, 0.0f, scale.x * src_width - 1.0f);\n"
                               "   clamp(tex_coord.y, 0.0f, scale.y * src_height - 1.0f);\n"
                               "   interp = fract(tex_coord);\n"
                               "   tex_coord = ivec2(tex_coord) * scale_inv;\n"
                               "\n"
                               "   /* Compute the sample coordinates used for filtering. */\n"
                               "   s_0_coord = tex_coord;\n"
                               "   s_1_coord = tex_coord + vec2(scale_inv.x, 0.0f);\n"
                               "   s_2_coord = tex_coord + vec2(0.0f, scale_inv.y);\n"
                               "   s_3_coord = tex_coord + vec2(scale_inv.x, scale_inv.y);\n"
                               "\n"
                               "   /* Fetch sample color values. */\n"
                               "%s"
                               "   s_0_color = TEXEL_FETCH(s_0_coord)\n"
                               "   s_1_color = TEXEL_FETCH(s_1_coord)\n"
                               "   s_2_color = TEXEL_FETCH(s_2_coord)\n"
                               "   s_3_color = TEXEL_FETCH(s_3_coord)\n"
                               "#undef TEXEL_FETCH\n"
                               "\n"
                               "   /* Do bilinear filtering on sample colors. */\n"
                               "   x_0_color =  mix(s_0_color, s_1_color, interp.x);\n"
                               "   x_1_color =  mix(s_2_color, s_3_color, interp.x);\n"
                               "   out_color = mix(x_0_color, x_1_color, interp.y);\n"
                               "}\n",
                               sampler_array_suffix,
                               sample_map_expr,
                               y_scale,
                               1.0f / y_scale,
                               1.0f / samples,
                               texel_fetch_macro);

   _mesa_meta_compile_and_link_program(ctx, vs_source, fs_source, name,
                                       &blit->msaa_shaders[shader_index]);
   loc_src_width =
      _mesa_GetUniformLocation(blit->msaa_shaders[shader_index], "src_width");
   loc_src_height =
      _mesa_GetUniformLocation(blit->msaa_shaders[shader_index], "src_height");
   _mesa_Uniform1f(loc_src_width, src_rb->Width);
   _mesa_Uniform1f(loc_src_height, src_rb->Height);

   ralloc_free(mem_ctx);
}