/**************************************************************************
 *
 * Copyright 2015 Advanced Micro Devices, Inc.
 * Copyright 2008 VMware, Inc.
 * All Rights Reserved.
 *
 * 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
 * on the rights to use, copy, modify, merge, publish, distribute, sub
 * license, 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 (including the next
 * paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS 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.
 *
 **************************************************************************/

#include "dd_pipe.h"

#include "util/u_dump.h"
#include "util/u_format.h"
#include "tgsi/tgsi_scan.h"


enum call_type
{
   CALL_DRAW_VBO,
   CALL_RESOURCE_COPY_REGION,
   CALL_BLIT,
   CALL_FLUSH_RESOURCE,
   CALL_CLEAR,
   CALL_CLEAR_BUFFER,
   CALL_CLEAR_RENDER_TARGET,
   CALL_CLEAR_DEPTH_STENCIL,
};

struct call_resource_copy_region
{
   struct pipe_resource *dst;
   unsigned dst_level;
   unsigned dstx, dsty, dstz;
   struct pipe_resource *src;
   unsigned src_level;
   const struct pipe_box *src_box;
};

struct call_clear
{
   unsigned buffers;
   const union pipe_color_union *color;
   double depth;
   unsigned stencil;
};

struct call_clear_buffer
{
   struct pipe_resource *res;
   unsigned offset;
   unsigned size;
   const void *clear_value;
   int clear_value_size;
};

struct dd_call
{
   enum call_type type;

   union {
      struct pipe_draw_info draw_vbo;
      struct call_resource_copy_region resource_copy_region;
      struct pipe_blit_info blit;
      struct pipe_resource *flush_resource;
      struct call_clear clear;
      struct call_clear_buffer clear_buffer;
   } info;
};

static FILE *
dd_get_file_stream(struct dd_context *dctx)
{
   struct dd_screen *dscreen = dd_screen(dctx->base.screen);
   struct pipe_screen *screen = dctx->pipe->screen;
   FILE *f = dd_get_debug_file(dscreen->verbose);
   if (!f)
      return NULL;

   fprintf(f, "Driver vendor: %s\n", screen->get_vendor(screen));
   fprintf(f, "Device vendor: %s\n", screen->get_device_vendor(screen));
   fprintf(f, "Device name: %s\n\n", screen->get_name(screen));
   return f;
}

static void
dd_close_file_stream(FILE *f)
{
   fclose(f);
}

static unsigned
dd_num_active_viewports(struct dd_context *dctx)
{
   struct tgsi_shader_info info;
   const struct tgsi_token *tokens;

   if (dctx->shaders[PIPE_SHADER_GEOMETRY])
      tokens = dctx->shaders[PIPE_SHADER_GEOMETRY]->state.shader.tokens;
   else if (dctx->shaders[PIPE_SHADER_TESS_EVAL])
      tokens = dctx->shaders[PIPE_SHADER_TESS_EVAL]->state.shader.tokens;
   else if (dctx->shaders[PIPE_SHADER_VERTEX])
      tokens = dctx->shaders[PIPE_SHADER_VERTEX]->state.shader.tokens;
   else
      return 1;

   tgsi_scan_shader(tokens, &info);
   return info.writes_viewport_index ? PIPE_MAX_VIEWPORTS : 1;
}

#define COLOR_RESET	"\033[0m"
#define COLOR_SHADER	"\033[1;32m"
#define COLOR_STATE	"\033[1;33m"

#define DUMP(name, var) do { \
   fprintf(f, COLOR_STATE #name ": " COLOR_RESET); \
   util_dump_##name(f, var); \
   fprintf(f, "\n"); \
} while(0)

#define DUMP_I(name, var, i) do { \
   fprintf(f, COLOR_STATE #name " %i: " COLOR_RESET, i); \
   util_dump_##name(f, var); \
   fprintf(f, "\n"); \
} while(0)

#define DUMP_M(name, var, member) do { \
   fprintf(f, "  " #member ": "); \
   util_dump_##name(f, (var)->member); \
   fprintf(f, "\n"); \
} while(0)

#define DUMP_M_ADDR(name, var, member) do { \
   fprintf(f, "  " #member ": "); \
   util_dump_##name(f, &(var)->member); \
   fprintf(f, "\n"); \
} while(0)

static void
print_named_value(FILE *f, const char *name, int value)
{
   fprintf(f, COLOR_STATE "%s" COLOR_RESET " = %i\n", name, value);
}

static void
print_named_xvalue(FILE *f, const char *name, int value)
{
   fprintf(f, COLOR_STATE "%s" COLOR_RESET " = 0x%08x\n", name, value);
}

static void
util_dump_uint(FILE *f, unsigned i)
{
   fprintf(f, "%u", i);
}

static void
util_dump_hex(FILE *f, unsigned i)
{
   fprintf(f, "0x%x", i);
}

static void
util_dump_double(FILE *f, double d)
{
   fprintf(f, "%f", d);
}

static void
util_dump_format(FILE *f, enum pipe_format format)
{
   fprintf(f, "%s", util_format_name(format));
}

static void
util_dump_color_union(FILE *f, const union pipe_color_union *color)
{
   fprintf(f, "{f = {%f, %f, %f, %f}, ui = {%u, %u, %u, %u}",
           color->f[0], color->f[1], color->f[2], color->f[3],
           color->ui[0], color->ui[1], color->ui[2], color->ui[3]);
}

static void
util_dump_query(FILE *f, struct dd_query *query)
{
   if (query->type >= PIPE_QUERY_DRIVER_SPECIFIC)
      fprintf(f, "PIPE_QUERY_DRIVER_SPECIFIC + %i",
              query->type - PIPE_QUERY_DRIVER_SPECIFIC);
   else
      fprintf(f, "%s", util_dump_query_type(query->type, false));
}

static void
dd_dump_render_condition(struct dd_context *dctx, FILE *f)
{
   if (dctx->render_cond.query) {
      fprintf(f, "render condition:\n");
      DUMP_M(query, &dctx->render_cond, query);
      DUMP_M(uint, &dctx->render_cond, condition);
      DUMP_M(uint, &dctx->render_cond, mode);
      fprintf(f, "\n");
   }
}

static void
dd_dump_draw_vbo(struct dd_context *dctx, struct pipe_draw_info *info, FILE *f)
{
   int sh, i;
   const char *shader_str[PIPE_SHADER_TYPES];

   shader_str[PIPE_SHADER_VERTEX] = "VERTEX";
   shader_str[PIPE_SHADER_TESS_CTRL] = "TESS_CTRL";
   shader_str[PIPE_SHADER_TESS_EVAL] = "TESS_EVAL";
   shader_str[PIPE_SHADER_GEOMETRY] = "GEOMETRY";
   shader_str[PIPE_SHADER_FRAGMENT] = "FRAGMENT";
   shader_str[PIPE_SHADER_COMPUTE] = "COMPUTE";

   DUMP(draw_info, info);
   if (info->indexed) {
      DUMP(index_buffer, &dctx->index_buffer);
      if (dctx->index_buffer.buffer)
         DUMP_M(resource, &dctx->index_buffer, buffer);
   }
   if (info->count_from_stream_output)
      DUMP_M(stream_output_target, info,
             count_from_stream_output);
   if (info->indirect)
      DUMP_M(resource, info, indirect);
   fprintf(f, "\n");

   /* TODO: dump active queries */

   dd_dump_render_condition(dctx, f);

   for (i = 0; i < PIPE_MAX_ATTRIBS; i++)
      if (dctx->vertex_buffers[i].buffer ||
          dctx->vertex_buffers[i].user_buffer) {
         DUMP_I(vertex_buffer, &dctx->vertex_buffers[i], i);
         if (dctx->vertex_buffers[i].buffer)
            DUMP_M(resource, &dctx->vertex_buffers[i], buffer);
      }

   if (dctx->velems) {
      print_named_value(f, "num vertex elements",
                        dctx->velems->state.velems.count);
      for (i = 0; i < dctx->velems->state.velems.count; i++) {
         fprintf(f, "  ");
         DUMP_I(vertex_element, &dctx->velems->state.velems.velems[i], i);
      }
   }

   print_named_value(f, "num stream output targets", dctx->num_so_targets);
   for (i = 0; i < dctx->num_so_targets; i++)
      if (dctx->so_targets[i]) {
         DUMP_I(stream_output_target, dctx->so_targets[i], i);
         DUMP_M(resource, dctx->so_targets[i], buffer);
         fprintf(f, "  offset = %i\n", dctx->so_offsets[i]);
      }

   fprintf(f, "\n");
   for (sh = 0; sh < PIPE_SHADER_TYPES; sh++) {
      if (sh == PIPE_SHADER_COMPUTE)
         continue;

      if (sh == PIPE_SHADER_TESS_CTRL &&
          !dctx->shaders[PIPE_SHADER_TESS_CTRL] &&
          dctx->shaders[PIPE_SHADER_TESS_EVAL])
         fprintf(f, "tess_state: {default_outer_level = {%f, %f, %f, %f}, "
                 "default_inner_level = {%f, %f}}\n",
                 dctx->tess_default_levels[0],
                 dctx->tess_default_levels[1],
                 dctx->tess_default_levels[2],
                 dctx->tess_default_levels[3],
                 dctx->tess_default_levels[4],
                 dctx->tess_default_levels[5]);

      if (sh == PIPE_SHADER_FRAGMENT)
         if (dctx->rs) {
            unsigned num_viewports = dd_num_active_viewports(dctx);

            if (dctx->rs->state.rs.clip_plane_enable)
               DUMP(clip_state, &dctx->clip_state);

            for (i = 0; i < num_viewports; i++)
               DUMP_I(viewport_state, &dctx->viewports[i], i);

            if (dctx->rs->state.rs.scissor)
               for (i = 0; i < num_viewports; i++)
                  DUMP_I(scissor_state, &dctx->scissors[i], i);

            DUMP(rasterizer_state, &dctx->rs->state.rs);

            if (dctx->rs->state.rs.poly_stipple_enable)
               DUMP(poly_stipple, &dctx->polygon_stipple);
            fprintf(f, "\n");
         }

      if (!dctx->shaders[sh])
         continue;

      fprintf(f, COLOR_SHADER "begin shader: %s" COLOR_RESET "\n", shader_str[sh]);
      DUMP(shader_state, &dctx->shaders[sh]->state.shader);

      for (i = 0; i < PIPE_MAX_CONSTANT_BUFFERS; i++)
         if (dctx->constant_buffers[sh][i].buffer ||
             dctx->constant_buffers[sh][i].user_buffer) {
            DUMP_I(constant_buffer, &dctx->constant_buffers[sh][i], i);
            if (dctx->constant_buffers[sh][i].buffer)
               DUMP_M(resource, &dctx->constant_buffers[sh][i], buffer);
         }

      for (i = 0; i < PIPE_MAX_SAMPLERS; i++)
         if (dctx->sampler_states[sh][i])
            DUMP_I(sampler_state, &dctx->sampler_states[sh][i]->state.sampler, i);

      for (i = 0; i < PIPE_MAX_SAMPLERS; i++)
         if (dctx->sampler_views[sh][i]) {
            DUMP_I(sampler_view, dctx->sampler_views[sh][i], i);
            DUMP_M(resource, dctx->sampler_views[sh][i], texture);
         }

      /* TODO: print shader images */
      /* TODO: print shader buffers */

      fprintf(f, COLOR_SHADER "end shader: %s" COLOR_RESET "\n\n", shader_str[sh]);
   }

   if (dctx->dsa)
      DUMP(depth_stencil_alpha_state, &dctx->dsa->state.dsa);
   DUMP(stencil_ref, &dctx->stencil_ref);

   if (dctx->blend)
      DUMP(blend_state, &dctx->blend->state.blend);
   DUMP(blend_color, &dctx->blend_color);

   print_named_value(f, "min_samples", dctx->min_samples);
   print_named_xvalue(f, "sample_mask", dctx->sample_mask);
   fprintf(f, "\n");

   DUMP(framebuffer_state, &dctx->framebuffer_state);
   for (i = 0; i < dctx->framebuffer_state.nr_cbufs; i++)
      if (dctx->framebuffer_state.cbufs[i]) {
         fprintf(f, "  " COLOR_STATE "cbufs[%i]:" COLOR_RESET "\n    ", i);
         DUMP(surface, dctx->framebuffer_state.cbufs[i]);
         fprintf(f, "    ");
         DUMP(resource, dctx->framebuffer_state.cbufs[i]->texture);
      }
   if (dctx->framebuffer_state.zsbuf) {
      fprintf(f, "  " COLOR_STATE "zsbuf:" COLOR_RESET "\n    ");
      DUMP(surface, dctx->framebuffer_state.zsbuf);
      fprintf(f, "    ");
      DUMP(resource, dctx->framebuffer_state.zsbuf->texture);
   }
   fprintf(f, "\n");
}

static void
dd_dump_resource_copy_region(struct dd_context *dctx,
                             struct call_resource_copy_region *info,
                             FILE *f)
{
   fprintf(f, "%s:\n", __func__+8);
   DUMP_M(resource, info, dst);
   DUMP_M(uint, info, dst_level);
   DUMP_M(uint, info, dstx);
   DUMP_M(uint, info, dsty);
   DUMP_M(uint, info, dstz);
   DUMP_M(resource, info, src);
   DUMP_M(uint, info, src_level);
   DUMP_M(box, info, src_box);
}

static void
dd_dump_blit(struct dd_context *dctx, struct pipe_blit_info *info, FILE *f)
{
   fprintf(f, "%s:\n", __func__+8);
   DUMP_M(resource, info, dst.resource);
   DUMP_M(uint, info, dst.level);
   DUMP_M_ADDR(box, info, dst.box);
   DUMP_M(format, info, dst.format);

   DUMP_M(resource, info, src.resource);
   DUMP_M(uint, info, src.level);
   DUMP_M_ADDR(box, info, src.box);
   DUMP_M(format, info, src.format);

   DUMP_M(hex, info, mask);
   DUMP_M(uint, info, filter);
   DUMP_M(uint, info, scissor_enable);
   DUMP_M_ADDR(scissor_state, info, scissor);
   DUMP_M(uint, info, render_condition_enable);

   if (info->render_condition_enable)
      dd_dump_render_condition(dctx, f);
}

static void
dd_dump_flush_resource(struct dd_context *dctx, struct pipe_resource *res,
                       FILE *f)
{
   fprintf(f, "%s:\n", __func__+8);
   DUMP(resource, res);
}

static void
dd_dump_clear(struct dd_context *dctx, struct call_clear *info, FILE *f)
{
   fprintf(f, "%s:\n", __func__+8);
   DUMP_M(uint, info, buffers);
   DUMP_M(color_union, info, color);
   DUMP_M(double, info, depth);
   DUMP_M(hex, info, stencil);
}

static void
dd_dump_clear_buffer(struct dd_context *dctx, struct call_clear_buffer *info,
                     FILE *f)
{
   int i;
   const char *value = (const char*)info->clear_value;

   fprintf(f, "%s:\n", __func__+8);
   DUMP_M(resource, info, res);
   DUMP_M(uint, info, offset);
   DUMP_M(uint, info, size);
   DUMP_M(uint, info, clear_value_size);

   fprintf(f, "  clear_value:");
   for (i = 0; i < info->clear_value_size; i++)
      fprintf(f, " %02x", value[i]);
   fprintf(f, "\n");
}

static void
dd_dump_clear_render_target(struct dd_context *dctx, FILE *f)
{
   fprintf(f, "%s:\n", __func__+8);
   /* TODO */
}

static void
dd_dump_clear_depth_stencil(struct dd_context *dctx, FILE *f)
{
   fprintf(f, "%s:\n", __func__+8);
   /* TODO */
}

static void
dd_dump_driver_state(struct dd_context *dctx, FILE *f, unsigned flags)
{
   if (dctx->pipe->dump_debug_state) {
	   fprintf(f,"\n\n**************************************************"
		     "***************************\n");
	   fprintf(f, "Driver-specific state:\n\n");
	   dctx->pipe->dump_debug_state(dctx->pipe, f, flags);
   }
}

static void
dd_dump_call(struct dd_context *dctx, struct dd_call *call, unsigned flags)
{
   FILE *f = dd_get_file_stream(dctx);

   if (!f)
      return;

   switch (call->type) {
   case CALL_DRAW_VBO:
      dd_dump_draw_vbo(dctx, &call->info.draw_vbo, f);
      break;
   case CALL_RESOURCE_COPY_REGION:
      dd_dump_resource_copy_region(dctx, &call->info.resource_copy_region, f);
      break;
   case CALL_BLIT:
      dd_dump_blit(dctx, &call->info.blit, f);
      break;
   case CALL_FLUSH_RESOURCE:
      dd_dump_flush_resource(dctx, call->info.flush_resource, f);
      break;
   case CALL_CLEAR:
      dd_dump_clear(dctx, &call->info.clear, f);
      break;
   case CALL_CLEAR_BUFFER:
      dd_dump_clear_buffer(dctx, &call->info.clear_buffer, f);
      break;
   case CALL_CLEAR_RENDER_TARGET:
      dd_dump_clear_render_target(dctx, f);
      break;
   case CALL_CLEAR_DEPTH_STENCIL:
      dd_dump_clear_depth_stencil(dctx, f);
   }

   dd_dump_driver_state(dctx, f, flags);
   dd_close_file_stream(f);
}

static void
dd_kill_process(void)
{
   sync();
   fprintf(stderr, "dd: Aborting the process...\n");
   fflush(stdout);
   fflush(stderr);
   abort();
}

static bool
dd_flush_and_check_hang(struct dd_context *dctx,
                        struct pipe_fence_handle **flush_fence,
                        unsigned flush_flags)
{
   struct pipe_fence_handle *fence = NULL;
   struct pipe_context *pipe = dctx->pipe;
   struct pipe_screen *screen = pipe->screen;
   uint64_t timeout_ms = dd_screen(dctx->base.screen)->timeout_ms;
   bool idle;

   assert(timeout_ms > 0);

   pipe->flush(pipe, &fence, flush_flags);
   if (flush_fence)
      screen->fence_reference(screen, flush_fence, fence);
   if (!fence)
      return false;

   idle = screen->fence_finish(screen, fence, timeout_ms * 1000000);
   screen->fence_reference(screen, &fence, NULL);
   if (!idle)
      fprintf(stderr, "dd: GPU hang detected!\n");
   return !idle;
}

static void
dd_flush_and_handle_hang(struct dd_context *dctx,
                         struct pipe_fence_handle **fence, unsigned flags,
                         const char *cause)
{
   if (dd_flush_and_check_hang(dctx, fence, flags)) {
      FILE *f = dd_get_file_stream(dctx);

      if (f) {
         fprintf(f, "dd: %s.\n", cause);
         dd_dump_driver_state(dctx, f, PIPE_DEBUG_DEVICE_IS_HUNG);
         dd_close_file_stream(f);
      }

      /* Terminate the process to prevent future hangs. */
      dd_kill_process();
   }
}

static void
dd_context_flush(struct pipe_context *_pipe,
                 struct pipe_fence_handle **fence, unsigned flags)
{
   struct dd_context *dctx = dd_context(_pipe);
   struct pipe_context *pipe = dctx->pipe;

   switch (dd_screen(dctx->base.screen)->mode) {
   case DD_DETECT_HANGS:
      dd_flush_and_handle_hang(dctx, fence, flags,
                               "GPU hang detected in pipe->flush()");
      break;
   case DD_DUMP_ALL_CALLS:
      pipe->flush(pipe, fence, flags);
      break;
   default:
      assert(0);
   }
}

static void
dd_before_draw(struct dd_context *dctx)
{
   struct dd_screen *dscreen = dd_screen(dctx->base.screen);

   if (dscreen->mode == DD_DETECT_HANGS &&
       !dscreen->no_flush &&
       dctx->num_draw_calls >= dscreen->skip_count)
      dd_flush_and_handle_hang(dctx, NULL, 0,
                               "GPU hang most likely caused by internal "
                               "driver commands");
}

static void
dd_after_draw(struct dd_context *dctx, struct dd_call *call)
{
   struct dd_screen *dscreen = dd_screen(dctx->base.screen);
   struct pipe_context *pipe = dctx->pipe;

   if (dctx->num_draw_calls >= dscreen->skip_count) {
      switch (dscreen->mode) {
      case DD_DETECT_HANGS:
         if (!dscreen->no_flush &&
            dd_flush_and_check_hang(dctx, NULL, 0)) {
            dd_dump_call(dctx, call, PIPE_DEBUG_DEVICE_IS_HUNG);

            /* Terminate the process to prevent future hangs. */
            dd_kill_process();
         }
         break;
      case DD_DUMP_ALL_CALLS:
         if (!dscreen->no_flush)
            pipe->flush(pipe, NULL, 0);
         dd_dump_call(dctx, call, 0);
         break;
      default:
         assert(0);
      }
   }

   ++dctx->num_draw_calls;
   if (dscreen->skip_count && dctx->num_draw_calls % 10000 == 0)
      fprintf(stderr, "Gallium debugger reached %u draw calls.\n",
              dctx->num_draw_calls);
}

static void
dd_context_draw_vbo(struct pipe_context *_pipe,
                    const struct pipe_draw_info *info)
{
   struct dd_context *dctx = dd_context(_pipe);
   struct pipe_context *pipe = dctx->pipe;
   struct dd_call call;

   call.type = CALL_DRAW_VBO;
   call.info.draw_vbo = *info;

   dd_before_draw(dctx);
   pipe->draw_vbo(pipe, info);
   dd_after_draw(dctx, &call);
}

static void
dd_context_resource_copy_region(struct pipe_context *_pipe,
                                struct pipe_resource *dst, unsigned dst_level,
                                unsigned dstx, unsigned dsty, unsigned dstz,
                                struct pipe_resource *src, unsigned src_level,
                                const struct pipe_box *src_box)
{
   struct dd_context *dctx = dd_context(_pipe);
   struct pipe_context *pipe = dctx->pipe;
   struct dd_call call;

   call.type = CALL_RESOURCE_COPY_REGION;
   call.info.resource_copy_region.dst = dst;
   call.info.resource_copy_region.dst_level = dst_level;
   call.info.resource_copy_region.dstx = dstx;
   call.info.resource_copy_region.dsty = dsty;
   call.info.resource_copy_region.dstz = dstz;
   call.info.resource_copy_region.src = src;
   call.info.resource_copy_region.src_level = src_level;
   call.info.resource_copy_region.src_box = src_box;

   dd_before_draw(dctx);
   pipe->resource_copy_region(pipe,
                              dst, dst_level, dstx, dsty, dstz,
                              src, src_level, src_box);
   dd_after_draw(dctx, &call);
}

static void
dd_context_blit(struct pipe_context *_pipe, const struct pipe_blit_info *info)
{
   struct dd_context *dctx = dd_context(_pipe);
   struct pipe_context *pipe = dctx->pipe;
   struct dd_call call;

   call.type = CALL_BLIT;
   call.info.blit = *info;

   dd_before_draw(dctx);
   pipe->blit(pipe, info);
   dd_after_draw(dctx, &call);
}

static void
dd_context_flush_resource(struct pipe_context *_pipe,
                          struct pipe_resource *resource)
{
   struct dd_context *dctx = dd_context(_pipe);
   struct pipe_context *pipe = dctx->pipe;
   struct dd_call call;

   call.type = CALL_FLUSH_RESOURCE;
   call.info.flush_resource = resource;

   dd_before_draw(dctx);
   pipe->flush_resource(pipe, resource);
   dd_after_draw(dctx, &call);
}

static void
dd_context_clear(struct pipe_context *_pipe, unsigned buffers,
                 const union pipe_color_union *color, double depth,
                 unsigned stencil)
{
   struct dd_context *dctx = dd_context(_pipe);
   struct pipe_context *pipe = dctx->pipe;
   struct dd_call call;

   call.type = CALL_CLEAR;
   call.info.clear.buffers = buffers;
   call.info.clear.color = color;
   call.info.clear.depth = depth;
   call.info.clear.stencil = stencil;

   dd_before_draw(dctx);
   pipe->clear(pipe, buffers, color, depth, stencil);
   dd_after_draw(dctx, &call);
}

static void
dd_context_clear_render_target(struct pipe_context *_pipe,
                               struct pipe_surface *dst,
                               const union pipe_color_union *color,
                               unsigned dstx, unsigned dsty,
                               unsigned width, unsigned height)
{
   struct dd_context *dctx = dd_context(_pipe);
   struct pipe_context *pipe = dctx->pipe;
   struct dd_call call;

   call.type = CALL_CLEAR_RENDER_TARGET;

   dd_before_draw(dctx);
   pipe->clear_render_target(pipe, dst, color, dstx, dsty, width, height);
   dd_after_draw(dctx, &call);
}

static void
dd_context_clear_depth_stencil(struct pipe_context *_pipe,
                               struct pipe_surface *dst, unsigned clear_flags,
                               double depth, unsigned stencil, unsigned dstx,
                               unsigned dsty, unsigned width, unsigned height)
{
   struct dd_context *dctx = dd_context(_pipe);
   struct pipe_context *pipe = dctx->pipe;
   struct dd_call call;

   call.type = CALL_CLEAR_DEPTH_STENCIL;

   dd_before_draw(dctx);
   pipe->clear_depth_stencil(pipe, dst, clear_flags, depth, stencil,
                             dstx, dsty, width, height);
   dd_after_draw(dctx, &call);
}

static void
dd_context_clear_buffer(struct pipe_context *_pipe, struct pipe_resource *res,
                        unsigned offset, unsigned size,
                        const void *clear_value, int clear_value_size)
{
   struct dd_context *dctx = dd_context(_pipe);
   struct pipe_context *pipe = dctx->pipe;
   struct dd_call call;

   call.type = CALL_CLEAR_BUFFER;
   call.info.clear_buffer.res = res;
   call.info.clear_buffer.offset = offset;
   call.info.clear_buffer.size = size;
   call.info.clear_buffer.clear_value = clear_value;
   call.info.clear_buffer.clear_value_size = clear_value_size;

   dd_before_draw(dctx);
   pipe->clear_buffer(pipe, res, offset, size, clear_value, clear_value_size);
   dd_after_draw(dctx, &call);
}

void
dd_init_draw_functions(struct dd_context *dctx)
{
   CTX_INIT(flush);
   CTX_INIT(draw_vbo);
   CTX_INIT(resource_copy_region);
   CTX_INIT(blit);
   CTX_INIT(clear);
   CTX_INIT(clear_render_target);
   CTX_INIT(clear_depth_stencil);
   CTX_INIT(clear_buffer);
   CTX_INIT(flush_resource);
   /* launch_grid */
}
