[Mesa-dev,03/15] gallium/ddebug: new pipe for hang detection and driver state dumping (v2)

Submitted by Marek Olšák on Aug. 23, 2015, 12:04 p.m.

Details

Message ID 1440331501-4654-4-git-send-email-maraeo@gmail.com
State New
Headers show

Not browsing as part of any series.

Commit Message

Marek Olšák Aug. 23, 2015, 12:04 p.m.
From: Marek Olšák <marek.olsak@amd.com>

v2: lots of improvements

This is like identity or trace, but simpler. It doesn't wrap most states.

Run with:
  GALLIUM_DDEBUG=1000 [executable]
where "executable" is the app and "1000" is in miliseconds, meaning that
the context will be considered hung if a fence fails to signal in 1000 ms.

If that happens, all shaders, context states, bound resources, draw
parameters, and driver debug information (if any) will be dumped into:
  /home/$username/dd_dumps/$processname_$pid_$index.

Note that the context is flushed after every draw/clear/copy/blit operation
and then waited for to find the exact call that hangs.

You can also do:
  GALLIUM_DDEBUG=always
to do the dumping after every draw/clear/copy/blit operation without
flushing and waiting.

Examples of driver states that can be dumped are:
- Hardware status registers saying which hw block is busy (hung).
- Disassembled shaders in a human-readable form.
- The last submitted command buffer in a human-readable form.
---
 configure.ac                                       |   1 +
 src/gallium/Makefile.am                            |   1 +
 .../auxiliary/target-helpers/inline_debug_helper.h |   8 +
 src/gallium/drivers/ddebug/Makefile.am             |  11 +
 src/gallium/drivers/ddebug/Makefile.sources        |   6 +
 src/gallium/drivers/ddebug/SConscript              |  12 +
 src/gallium/drivers/ddebug/dd.h                    | 141 ++++
 src/gallium/drivers/ddebug/dd_context.c            | 771 ++++++++++++++++++++
 src/gallium/drivers/ddebug/dd_draw.c               | 807 +++++++++++++++++++++
 src/gallium/drivers/ddebug/dd_public.h             |  36 +
 src/gallium/drivers/ddebug/dd_screen.c             | 353 +++++++++
 src/gallium/targets/dri/Makefile.am                |   2 +
 src/gallium/targets/pipe-loader/Makefile.am        |   2 +
 13 files changed, 2151 insertions(+)
 create mode 100644 src/gallium/drivers/ddebug/Makefile.am
 create mode 100644 src/gallium/drivers/ddebug/Makefile.sources
 create mode 100644 src/gallium/drivers/ddebug/SConscript
 create mode 100644 src/gallium/drivers/ddebug/dd.h
 create mode 100644 src/gallium/drivers/ddebug/dd_context.c
 create mode 100644 src/gallium/drivers/ddebug/dd_draw.c
 create mode 100644 src/gallium/drivers/ddebug/dd_public.h
 create mode 100644 src/gallium/drivers/ddebug/dd_screen.c

Patch hide | download patch | download mbox

diff --git a/configure.ac b/configure.ac
index e3b5f2e..90ba4fe 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2317,6 +2317,7 @@  AC_CONFIG_FILES([Makefile
 		src/gallium/auxiliary/Makefile
 		src/gallium/auxiliary/pipe-loader/Makefile
 		src/gallium/drivers/freedreno/Makefile
+		src/gallium/drivers/ddebug/Makefile
 		src/gallium/drivers/i915/Makefile
 		src/gallium/drivers/ilo/Makefile
 		src/gallium/drivers/llvmpipe/Makefile
diff --git a/src/gallium/Makefile.am b/src/gallium/Makefile.am
index e2c1090..a7c3606 100644
--- a/src/gallium/Makefile.am
+++ b/src/gallium/Makefile.am
@@ -11,6 +11,7 @@  SUBDIRS += auxiliary
 ##
 
 SUBDIRS += \
+	drivers/ddebug \
 	drivers/noop \
 	drivers/trace \
 	drivers/rbug
diff --git a/src/gallium/auxiliary/target-helpers/inline_debug_helper.h b/src/gallium/auxiliary/target-helpers/inline_debug_helper.h
index d353ab8..2443bf2 100644
--- a/src/gallium/auxiliary/target-helpers/inline_debug_helper.h
+++ b/src/gallium/auxiliary/target-helpers/inline_debug_helper.h
@@ -11,6 +11,10 @@ 
  * one or more debug driver: rbug, trace.
  */
 
+#ifdef GALLIUM_DDEBUG
+#include "ddebug/dd_public.h"
+#endif
+
 #ifdef GALLIUM_TRACE
 #include "trace/tr_public.h"
 #endif
@@ -30,6 +34,10 @@ 
 static inline struct pipe_screen *
 debug_screen_wrap(struct pipe_screen *screen)
 {
+#if defined(GALLIUM_DDEBUG)
+   screen = ddebug_screen_create(screen);
+#endif
+
 #if defined(GALLIUM_RBUG)
    screen = rbug_screen_create(screen);
 #endif
diff --git a/src/gallium/drivers/ddebug/Makefile.am b/src/gallium/drivers/ddebug/Makefile.am
new file mode 100644
index 0000000..e8a187d
--- /dev/null
+++ b/src/gallium/drivers/ddebug/Makefile.am
@@ -0,0 +1,11 @@ 
+include Makefile.sources
+include $(top_srcdir)/src/gallium/Automake.inc
+
+AM_CFLAGS = \
+	$(GALLIUM_DRIVER_CFLAGS)
+
+noinst_LTLIBRARIES = libddebug.la
+
+libddebug_la_SOURCES = $(C_SOURCES)
+
+EXTRA_DIST = SConscript
diff --git a/src/gallium/drivers/ddebug/Makefile.sources b/src/gallium/drivers/ddebug/Makefile.sources
new file mode 100644
index 0000000..60d012d
--- /dev/null
+++ b/src/gallium/drivers/ddebug/Makefile.sources
@@ -0,0 +1,6 @@ 
+C_SOURCES := \
+	dd.h \
+	dd_public.h \
+	dd_context.c \
+	dd_draw.c \
+	dd_screen.c
diff --git a/src/gallium/drivers/ddebug/SConscript b/src/gallium/drivers/ddebug/SConscript
new file mode 100644
index 0000000..75792a2
--- /dev/null
+++ b/src/gallium/drivers/ddebug/SConscript
@@ -0,0 +1,12 @@ 
+Import('*')
+
+env = env.Clone()
+
+ddebug = env.ConvenienceLibrary(
+    target = 'ddebug',
+    source = env.ParseSourceList('Makefile.sources', 'C_SOURCES')
+    )
+
+env.Alias('ddebug', ddebug)
+
+Export('ddebug')
diff --git a/src/gallium/drivers/ddebug/dd.h b/src/gallium/drivers/ddebug/dd.h
new file mode 100644
index 0000000..c78d112
--- /dev/null
+++ b/src/gallium/drivers/ddebug/dd.h
@@ -0,0 +1,141 @@ 
+/**************************************************************************
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef DD_H_
+#define DD_H_
+
+#include "pipe/p_context.h"
+#include "pipe/p_state.h"
+#include "pipe/p_screen.h"
+
+/* name of the directory in home */
+#define DD_DIR "ddebug_dumps"
+
+enum dd_mode {
+   DD_DETECT_HANGS,
+   DD_DUMP_ALL_CALLS
+};
+
+struct dd_screen
+{
+   struct pipe_screen base;
+   struct pipe_screen *screen;
+   unsigned timeout_ms;
+   enum dd_mode mode;
+   bool no_flush;
+};
+
+struct dd_query
+{
+   unsigned type;
+   struct pipe_query *query;
+};
+
+struct dd_state
+{
+   void *cso;
+
+   union {
+      struct pipe_blend_state blend;
+      struct pipe_depth_stencil_alpha_state dsa;
+      struct pipe_rasterizer_state rs;
+      struct pipe_sampler_state sampler;
+      struct {
+         struct pipe_vertex_element velems[PIPE_MAX_ATTRIBS];
+         unsigned count;
+      } velems;
+      struct pipe_shader_state shader;
+   } state;
+};
+
+struct dd_context
+{
+   struct pipe_context base;
+   struct pipe_context *pipe;
+
+   struct {
+      struct dd_query *query;
+      bool condition;
+      unsigned mode;
+   } render_cond;
+
+   struct pipe_index_buffer index_buffer;
+   struct pipe_vertex_buffer vertex_buffers[PIPE_MAX_ATTRIBS];
+
+   unsigned num_so_targets;
+   struct pipe_stream_output_target *so_targets[PIPE_MAX_SO_BUFFERS];
+   unsigned so_offsets[PIPE_MAX_SO_BUFFERS];
+
+   struct dd_state *shaders[PIPE_SHADER_TYPES];
+   struct pipe_constant_buffer constant_buffers[PIPE_SHADER_TYPES][PIPE_MAX_CONSTANT_BUFFERS];
+   struct pipe_sampler_view *sampler_views[PIPE_SHADER_TYPES][PIPE_MAX_SAMPLERS];
+   struct dd_state *sampler_states[PIPE_SHADER_TYPES][PIPE_MAX_SAMPLERS];
+   struct pipe_image_view *shader_images[PIPE_SHADER_TYPES][PIPE_MAX_SHADER_IMAGES];
+   struct pipe_shader_buffer shader_buffers[PIPE_SHADER_TYPES][PIPE_MAX_SHADER_BUFFERS];
+
+   struct dd_state *velems;
+   struct dd_state *rs;
+   struct dd_state *dsa;
+   struct dd_state *blend;
+
+   struct pipe_blend_color blend_color;
+   struct pipe_stencil_ref stencil_ref;
+   unsigned sample_mask;
+   unsigned min_samples;
+   struct pipe_clip_state clip_state;
+   struct pipe_framebuffer_state framebuffer_state;
+   struct pipe_poly_stipple polygon_stipple;
+   struct pipe_scissor_state scissors[PIPE_MAX_VIEWPORTS];
+   struct pipe_viewport_state viewports[PIPE_MAX_VIEWPORTS];
+   float tess_default_levels[6];
+};
+
+
+struct pipe_context *
+dd_context_create(struct dd_screen *dscreen, struct pipe_context *pipe);
+
+void
+dd_init_draw_functions(struct dd_context *dctx);
+
+
+static inline struct dd_context *
+dd_context(struct pipe_context *pipe)
+{
+   return (struct dd_context *)pipe;
+}
+
+static inline struct dd_screen *
+dd_screen(struct pipe_screen *screen)
+{
+   return (struct dd_screen*)screen;
+}
+
+
+#define CTX_INIT(_member) \
+   dctx->base._member = dctx->pipe->_member ? dd_context_##_member : NULL
+
+#endif /* DD_H_ */
diff --git a/src/gallium/drivers/ddebug/dd_context.c b/src/gallium/drivers/ddebug/dd_context.c
new file mode 100644
index 0000000..a73d99d
--- /dev/null
+++ b/src/gallium/drivers/ddebug/dd_context.c
@@ -0,0 +1,771 @@ 
+/**************************************************************************
+ *
+ * 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.h"
+#include "tgsi/tgsi_parse.h"
+#include "util/u_memory.h"
+
+
+static void
+safe_memcpy(void *dst, const void *src, size_t size)
+{
+   if (src)
+      memcpy(dst, src, size);
+   else
+      memset(dst, 0, size);
+}
+
+
+/********************************************************************
+ * queries
+ */
+
+static struct dd_query *
+dd_query(struct pipe_query *query)
+{
+   return (struct dd_query *)query;
+}
+
+static struct pipe_query *
+dd_query_unwrap(struct pipe_query *query)
+{
+   if (query) {
+      return dd_query(query)->query;
+   } else {
+      return NULL;
+   }
+}
+
+static struct pipe_query *
+dd_context_create_query(struct pipe_context *_pipe, unsigned query_type,
+                        unsigned index)
+{
+   struct pipe_context *pipe = dd_context(_pipe)->pipe;
+   struct pipe_query *query;
+
+   query = pipe->create_query(pipe, query_type, index);
+
+   /* Wrap query object. */
+   if (query) {
+      struct dd_query *dd_query = CALLOC_STRUCT(dd_query);
+      if (dd_query) {
+         dd_query->type = query_type;
+         dd_query->query = query;
+         query = (struct pipe_query *)dd_query;
+      } else {
+         pipe->destroy_query(pipe, query);
+         query = NULL;
+      }
+   }
+
+   return query;
+}
+
+static void
+dd_context_destroy_query(struct pipe_context *_pipe,
+                         struct pipe_query *query)
+{
+   struct pipe_context *pipe = dd_context(_pipe)->pipe;
+
+   pipe->destroy_query(pipe, dd_query_unwrap(query));
+   FREE(query);
+}
+
+static boolean
+dd_context_begin_query(struct pipe_context *_pipe, struct pipe_query *query)
+{
+   struct dd_context *dctx = dd_context(_pipe);
+   struct pipe_context *pipe = dctx->pipe;
+
+   return pipe->begin_query(pipe, dd_query_unwrap(query));
+}
+
+static void
+dd_context_end_query(struct pipe_context *_pipe, struct pipe_query *query)
+{
+   struct dd_context *dctx = dd_context(_pipe);
+   struct pipe_context *pipe = dctx->pipe;
+
+   pipe->end_query(pipe, dd_query_unwrap(query));
+}
+
+static boolean
+dd_context_get_query_result(struct pipe_context *_pipe,
+                            struct pipe_query *query, boolean wait,
+                            union pipe_query_result *result)
+{
+   struct pipe_context *pipe = dd_context(_pipe)->pipe;
+
+   return pipe->get_query_result(pipe, dd_query_unwrap(query), wait, result);
+}
+
+static void
+dd_context_render_condition(struct pipe_context *_pipe,
+                            struct pipe_query *query, boolean condition,
+                            uint mode)
+{
+   struct dd_context *dctx = dd_context(_pipe);
+   struct pipe_context *pipe = dctx->pipe;
+
+   pipe->render_condition(pipe, dd_query_unwrap(query), condition, mode);
+   dctx->render_cond.query = dd_query(query);
+   dctx->render_cond.condition = condition;
+   dctx->render_cond.mode = mode;
+}
+
+
+/********************************************************************
+ * constant (immutable) non-shader states
+ */
+
+#define DD_CSO_CREATE(name, shortname) \
+   static void * \
+   dd_context_create_##name##_state(struct pipe_context *_pipe, \
+                                    const struct pipe_##name##_state *state) \
+   { \
+      struct pipe_context *pipe = dd_context(_pipe)->pipe; \
+      struct dd_state *hstate = CALLOC_STRUCT(dd_state); \
+ \
+      if (!hstate) \
+         return NULL; \
+      hstate->cso = pipe->create_##name##_state(pipe, state); \
+      hstate->state.shortname = *state; \
+      return hstate; \
+   }
+
+#define DD_CSO_BIND(name, shortname) \
+   static void \
+   dd_context_bind_##name##_state(struct pipe_context *_pipe, void *state) \
+   { \
+      struct dd_context *dctx = dd_context(_pipe); \
+      struct pipe_context *pipe = dctx->pipe; \
+      struct dd_state *hstate = state; \
+ \
+      dctx->shortname = hstate; \
+      pipe->bind_##name##_state(pipe, hstate ? hstate->cso : NULL); \
+   }
+
+#define DD_CSO_DELETE(name) \
+   static void \
+   dd_context_delete_##name##_state(struct pipe_context *_pipe, void *state) \
+   { \
+      struct dd_context *dctx = dd_context(_pipe); \
+      struct pipe_context *pipe = dctx->pipe; \
+      struct dd_state *hstate = state; \
+ \
+      pipe->delete_##name##_state(pipe, hstate->cso); \
+      FREE(hstate); \
+   }
+
+#define DD_CSO_WHOLE(name, shortname) \
+   DD_CSO_CREATE(name, shortname) \
+   DD_CSO_BIND(name, shortname) \
+   DD_CSO_DELETE(name)
+
+DD_CSO_WHOLE(blend, blend)
+DD_CSO_WHOLE(rasterizer, rs)
+DD_CSO_WHOLE(depth_stencil_alpha, dsa)
+
+DD_CSO_CREATE(sampler, sampler)
+DD_CSO_DELETE(sampler)
+
+static void
+dd_context_bind_sampler_states(struct pipe_context *_pipe, unsigned shader,
+                               unsigned start, unsigned count, void **states)
+{
+   struct dd_context *dctx = dd_context(_pipe);
+   struct pipe_context *pipe = dctx->pipe;
+
+   memcpy(&dctx->sampler_states[shader][start], states,
+          sizeof(void*) * count);
+
+   if (states) {
+      void *samp[PIPE_MAX_SAMPLERS];
+      int i;
+
+      for (i = 0; i < count; i++) {
+         struct dd_state *s = states[i];
+         samp[i] = s ? s->cso : NULL;
+      }
+
+      pipe->bind_sampler_states(pipe, shader, start, count, samp);
+   }
+   else
+      pipe->bind_sampler_states(pipe, shader, start, count, NULL);
+}
+
+static void *
+dd_context_create_vertex_elements_state(struct pipe_context *_pipe,
+                                        unsigned num_elems,
+                                        const struct pipe_vertex_element *elems)
+{
+   struct pipe_context *pipe = dd_context(_pipe)->pipe;
+   struct dd_state *hstate = CALLOC_STRUCT(dd_state);
+
+   if (!hstate)
+      return NULL;
+   hstate->cso = pipe->create_vertex_elements_state(pipe, num_elems, elems);
+   memcpy(hstate->state.velems.velems, elems, sizeof(elems[0]) * num_elems);
+   hstate->state.velems.count = num_elems;
+   return hstate;
+}
+
+DD_CSO_BIND(vertex_elements, velems)
+DD_CSO_DELETE(vertex_elements)
+
+
+/********************************************************************
+ * shaders
+ */
+
+#define DD_SHADER(NAME, name) \
+   static void * \
+   dd_context_create_##name##_state(struct pipe_context *_pipe, \
+                                    const struct pipe_shader_state *state) \
+   { \
+      struct pipe_context *pipe = dd_context(_pipe)->pipe; \
+      struct dd_state *hstate = CALLOC_STRUCT(dd_state); \
+ \
+      if (!hstate) \
+         return NULL; \
+      hstate->cso = pipe->create_##name##_state(pipe, state); \
+      hstate->state.shader = *state; \
+      hstate->state.shader.tokens = tgsi_dup_tokens(state->tokens); \
+      return hstate; \
+   } \
+    \
+   static void \
+   dd_context_bind_##name##_state(struct pipe_context *_pipe, void *state) \
+   { \
+      struct dd_context *dctx = dd_context(_pipe); \
+      struct pipe_context *pipe = dctx->pipe; \
+      struct dd_state *hstate = state; \
+   \
+      dctx->shaders[PIPE_SHADER_##NAME] = hstate; \
+      pipe->bind_##name##_state(pipe, hstate ? hstate->cso : NULL); \
+   } \
+    \
+   static void \
+   dd_context_delete_##name##_state(struct pipe_context *_pipe, void *state) \
+   { \
+      struct dd_context *dctx = dd_context(_pipe); \
+      struct pipe_context *pipe = dctx->pipe; \
+      struct dd_state *hstate = state; \
+   \
+      pipe->delete_##name##_state(pipe, hstate->cso); \
+      tgsi_free_tokens(hstate->state.shader.tokens); \
+      FREE(hstate); \
+   }
+
+DD_SHADER(FRAGMENT, fs)
+DD_SHADER(VERTEX, vs)
+DD_SHADER(GEOMETRY, gs)
+DD_SHADER(TESS_CTRL, tcs)
+DD_SHADER(TESS_EVAL, tes)
+
+
+/********************************************************************
+ * immediate states
+ */
+
+#define DD_IMM_STATE(name, type, deref, ref) \
+   static void \
+   dd_context_set_##name(struct pipe_context *_pipe, type deref) \
+   { \
+      struct dd_context *dctx = dd_context(_pipe); \
+      struct pipe_context *pipe = dctx->pipe; \
+ \
+      dctx->name = deref; \
+      pipe->set_##name(pipe, ref); \
+   }
+
+DD_IMM_STATE(blend_color, const struct pipe_blend_color, *state, state)
+DD_IMM_STATE(stencil_ref, const struct pipe_stencil_ref, *state, state)
+DD_IMM_STATE(clip_state, const struct pipe_clip_state, *state, state)
+DD_IMM_STATE(sample_mask, unsigned, sample_mask, sample_mask)
+DD_IMM_STATE(min_samples, unsigned, min_samples, min_samples)
+DD_IMM_STATE(framebuffer_state, const struct pipe_framebuffer_state, *state, state)
+DD_IMM_STATE(polygon_stipple, const struct pipe_poly_stipple, *state, state)
+
+static void
+dd_context_set_constant_buffer(struct pipe_context *_pipe,
+                               uint shader, uint index,
+                               struct pipe_constant_buffer *constant_buffer)
+{
+   struct dd_context *dctx = dd_context(_pipe);
+   struct pipe_context *pipe = dctx->pipe;
+
+   safe_memcpy(&dctx->constant_buffers[shader][index], constant_buffer,
+          sizeof(*constant_buffer));
+   pipe->set_constant_buffer(pipe, shader, index, constant_buffer);
+}
+
+static void
+dd_context_set_scissor_states(struct pipe_context *_pipe,
+                              unsigned start_slot, unsigned num_scissors,
+                              const struct pipe_scissor_state *states)
+{
+   struct dd_context *dctx = dd_context(_pipe);
+   struct pipe_context *pipe = dctx->pipe;
+
+   safe_memcpy(&dctx->scissors[start_slot], states,
+               sizeof(*states) * num_scissors);
+   pipe->set_scissor_states(pipe, start_slot, num_scissors, states);
+}
+
+static void
+dd_context_set_viewport_states(struct pipe_context *_pipe,
+                               unsigned start_slot, unsigned num_viewports,
+                               const struct pipe_viewport_state *states)
+{
+   struct dd_context *dctx = dd_context(_pipe);
+   struct pipe_context *pipe = dctx->pipe;
+
+   safe_memcpy(&dctx->viewports[start_slot], states,
+               sizeof(*states) * num_viewports);
+   pipe->set_viewport_states(pipe, start_slot, num_viewports, states);
+}
+
+static void dd_context_set_tess_state(struct pipe_context *_pipe,
+                                      const float default_outer_level[4],
+                                      const float default_inner_level[2])
+{
+   struct dd_context *dctx = dd_context(_pipe);
+   struct pipe_context *pipe = dctx->pipe;
+
+   memcpy(dctx->tess_default_levels, default_outer_level, sizeof(float) * 4);
+   memcpy(dctx->tess_default_levels+4, default_inner_level, sizeof(float) * 2);
+   pipe->set_tess_state(pipe, default_outer_level, default_inner_level);
+}
+
+
+/********************************************************************
+ * views
+ */
+
+static struct pipe_surface *
+dd_context_create_surface(struct pipe_context *_pipe,
+                          struct pipe_resource *resource,
+                          const struct pipe_surface *surf_tmpl)
+{
+   struct pipe_context *pipe = dd_context(_pipe)->pipe;
+   struct pipe_surface *view =
+      pipe->create_surface(pipe, resource, surf_tmpl);
+
+   if (!view)
+      return NULL;
+   view->context = _pipe;
+   return view;
+}
+
+static void
+dd_context_surface_destroy(struct pipe_context *_pipe,
+                           struct pipe_surface *surf)
+{
+   struct pipe_context *pipe = dd_context(_pipe)->pipe;
+
+   pipe->surface_destroy(pipe, surf);
+}
+
+static struct pipe_sampler_view *
+dd_context_create_sampler_view(struct pipe_context *_pipe,
+                               struct pipe_resource *resource,
+                               const struct pipe_sampler_view *templ)
+{
+   struct pipe_context *pipe = dd_context(_pipe)->pipe;
+   struct pipe_sampler_view *view =
+      pipe->create_sampler_view(pipe, resource, templ);
+
+   if (!view)
+      return NULL;
+   view->context = _pipe;
+   return view;
+}
+
+static void
+dd_context_sampler_view_destroy(struct pipe_context *_pipe,
+                                struct pipe_sampler_view *view)
+{
+   struct pipe_context *pipe = dd_context(_pipe)->pipe;
+
+   pipe->sampler_view_destroy(pipe, view);
+}
+
+static struct pipe_image_view *
+dd_context_create_image_view(struct pipe_context *_pipe,
+                             struct pipe_resource *resource,
+                             const struct pipe_image_view *templ)
+{
+   struct pipe_context *pipe = dd_context(_pipe)->pipe;
+   struct pipe_image_view *view =
+      pipe->create_image_view(pipe, resource, templ);
+
+   if (!view)
+      return NULL;
+   view->context = _pipe;
+   return view;
+}
+
+static void
+dd_context_image_view_destroy(struct pipe_context *_pipe,
+                              struct pipe_image_view *view)
+{
+   struct pipe_context *pipe = dd_context(_pipe)->pipe;
+
+   pipe->image_view_destroy(pipe, view);
+}
+
+static struct pipe_stream_output_target *
+dd_context_create_stream_output_target(struct pipe_context *_pipe,
+                                       struct pipe_resource *res,
+                                       unsigned buffer_offset,
+                                       unsigned buffer_size)
+{
+   struct pipe_context *pipe = dd_context(_pipe)->pipe;
+   struct pipe_stream_output_target *view =
+      pipe->create_stream_output_target(pipe, res, buffer_offset,
+                                        buffer_size);
+
+   if (!view)
+      return NULL;
+   view->context = _pipe;
+   return view;
+}
+
+static void
+dd_context_stream_output_target_destroy(struct pipe_context *_pipe,
+                                        struct pipe_stream_output_target *target)
+{
+   struct pipe_context *pipe = dd_context(_pipe)->pipe;
+
+   pipe->stream_output_target_destroy(pipe, target);
+}
+
+
+/********************************************************************
+ * set states
+ */
+
+static void
+dd_context_set_sampler_views(struct pipe_context *_pipe, unsigned shader,
+                             unsigned start, unsigned num,
+                             struct pipe_sampler_view **views)
+{
+   struct dd_context *dctx = dd_context(_pipe);
+   struct pipe_context *pipe = dctx->pipe;
+
+   safe_memcpy(&dctx->sampler_views[shader][start], views,
+               sizeof(views[0]) * num);
+   pipe->set_sampler_views(pipe, shader, start, num, views);
+}
+
+static void
+dd_context_set_shader_images(struct pipe_context *_pipe, unsigned shader,
+                             unsigned start, unsigned num,
+                             struct pipe_image_view **views)
+{
+   struct dd_context *dctx = dd_context(_pipe);
+   struct pipe_context *pipe = dctx->pipe;
+
+   safe_memcpy(&dctx->shader_images[shader][start], views,
+               sizeof(views[0]) * num);
+   pipe->set_shader_images(pipe, shader, start, num, views);
+}
+
+static void
+dd_context_set_shader_buffers(struct pipe_context *_pipe, unsigned shader,
+                              unsigned start, unsigned num_buffers,
+                              struct pipe_shader_buffer *buffers)
+{
+   struct dd_context *dctx = dd_context(_pipe);
+   struct pipe_context *pipe = dctx->pipe;
+
+   safe_memcpy(&dctx->shader_buffers[shader][start], buffers,
+               sizeof(buffers[0]) * num_buffers);
+   pipe->set_shader_buffers(pipe, shader, start, num_buffers, buffers);
+}
+
+static void
+dd_context_set_vertex_buffers(struct pipe_context *_pipe,
+                              unsigned start, unsigned num_buffers,
+                              const struct pipe_vertex_buffer *buffers)
+{
+   struct dd_context *dctx = dd_context(_pipe);
+   struct pipe_context *pipe = dctx->pipe;
+
+   safe_memcpy(&dctx->vertex_buffers[start], buffers,
+               sizeof(buffers[0]) * num_buffers);
+   pipe->set_vertex_buffers(pipe, start, num_buffers, buffers);
+}
+
+static void
+dd_context_set_index_buffer(struct pipe_context *_pipe,
+                            const struct pipe_index_buffer *ib)
+{
+   struct dd_context *dctx = dd_context(_pipe);
+   struct pipe_context *pipe = dctx->pipe;
+
+   safe_memcpy(&dctx->index_buffer, ib, sizeof(*ib));
+   pipe->set_index_buffer(pipe, ib);
+}
+
+static void
+dd_context_set_stream_output_targets(struct pipe_context *_pipe,
+                                     unsigned num_targets,
+                                     struct pipe_stream_output_target **tgs,
+                                     const unsigned *offsets)
+{
+   struct dd_context *dctx = dd_context(_pipe);
+   struct pipe_context *pipe = dctx->pipe;
+
+   dctx->num_so_targets = num_targets;
+   safe_memcpy(dctx->so_targets, tgs, sizeof(*tgs) * num_targets);
+   safe_memcpy(dctx->so_offsets, offsets, sizeof(*offsets) * num_targets);
+   pipe->set_stream_output_targets(pipe, num_targets, tgs, offsets);
+}
+
+static void
+dd_context_destroy(struct pipe_context *_pipe)
+{
+   struct dd_context *dctx = dd_context(_pipe);
+   struct pipe_context *pipe = dctx->pipe;
+
+   pipe->destroy(pipe);
+   FREE(dctx);
+}
+
+
+/********************************************************************
+ * transfer
+ */
+
+static void *
+dd_context_transfer_map(struct pipe_context *_pipe,
+                        struct pipe_resource *resource, unsigned level,
+                        unsigned usage, const struct pipe_box *box,
+                        struct pipe_transfer **transfer)
+{
+   struct pipe_context *pipe = dd_context(_pipe)->pipe;
+
+   return pipe->transfer_map(pipe, resource, level, usage, box, transfer);
+}
+
+static void
+dd_context_transfer_flush_region(struct pipe_context *_pipe,
+                                 struct pipe_transfer *transfer,
+                                 const struct pipe_box *box)
+{
+   struct pipe_context *pipe = dd_context(_pipe)->pipe;
+
+   pipe->transfer_flush_region(pipe, transfer, box);
+}
+
+static void
+dd_context_transfer_unmap(struct pipe_context *_pipe,
+                          struct pipe_transfer *transfer)
+{
+   struct pipe_context *pipe = dd_context(_pipe)->pipe;
+
+   pipe->transfer_unmap(pipe, transfer);
+}
+
+static void
+dd_context_transfer_inline_write(struct pipe_context *_pipe,
+                                 struct pipe_resource *resource,
+                                 unsigned level, unsigned usage,
+                                 const struct pipe_box *box,
+                                 const void *data, unsigned stride,
+                                 unsigned layer_stride)
+{
+   struct pipe_context *pipe = dd_context(_pipe)->pipe;
+
+   pipe->transfer_inline_write(pipe, resource, level, usage, box, data,
+                               stride, layer_stride);
+}
+
+
+/********************************************************************
+ * miscellaneous
+ */
+
+static void
+dd_context_texture_barrier(struct pipe_context *_pipe)
+{
+   struct pipe_context *pipe = dd_context(_pipe)->pipe;
+
+   pipe->texture_barrier(pipe);
+}
+
+static void
+dd_context_memory_barrier(struct pipe_context *_pipe, unsigned flags)
+{
+   struct pipe_context *pipe = dd_context(_pipe)->pipe;
+
+   pipe->memory_barrier(pipe, flags);
+}
+
+static void
+dd_context_get_sample_position(struct pipe_context *_pipe,
+                               unsigned sample_count, unsigned sample_index,
+                               float *out_value)
+{
+   struct pipe_context *pipe = dd_context(_pipe)->pipe;
+
+   return pipe->get_sample_position(pipe, sample_count, sample_index,
+                                    out_value);
+}
+
+static void
+dd_context_invalidate_resource(struct pipe_context *_pipe,
+                               struct pipe_resource *resource)
+{
+   struct pipe_context *pipe = dd_context(_pipe)->pipe;
+
+   pipe->invalidate_resource(pipe, resource);
+}
+
+static enum pipe_reset_status
+dd_context_get_device_reset_status(struct pipe_context *_pipe)
+{
+   struct pipe_context *pipe = dd_context(_pipe)->pipe;
+
+   return pipe->get_device_reset_status(pipe);
+}
+
+static void
+dd_context_dump_debug_state(struct pipe_context *_pipe, FILE *stream,
+                            unsigned flags)
+{
+   struct pipe_context *pipe = dd_context(_pipe)->pipe;
+
+   return pipe->dump_debug_state(pipe, stream, flags);
+}
+
+struct pipe_context *
+dd_context_create(struct dd_screen *dscreen, struct pipe_context *pipe)
+{
+   struct dd_context *dctx;
+
+   if (!pipe)
+      return NULL;
+
+   dctx = CALLOC_STRUCT(dd_context);
+   if (!dctx) {
+      pipe->destroy(pipe);
+      return NULL;
+   }
+
+   dctx->pipe = pipe;
+   dctx->base.priv = pipe->priv; /* expose wrapped priv data */
+   dctx->base.screen = &dscreen->base;
+
+   dctx->base.destroy = dd_context_destroy;
+
+   CTX_INIT(render_condition);
+   CTX_INIT(create_query);
+   CTX_INIT(destroy_query);
+   CTX_INIT(begin_query);
+   CTX_INIT(end_query);
+   CTX_INIT(get_query_result);
+   CTX_INIT(create_blend_state);
+   CTX_INIT(bind_blend_state);
+   CTX_INIT(delete_blend_state);
+   CTX_INIT(create_sampler_state);
+   CTX_INIT(bind_sampler_states);
+   CTX_INIT(delete_sampler_state);
+   CTX_INIT(create_rasterizer_state);
+   CTX_INIT(bind_rasterizer_state);
+   CTX_INIT(delete_rasterizer_state);
+   CTX_INIT(create_depth_stencil_alpha_state);
+   CTX_INIT(bind_depth_stencil_alpha_state);
+   CTX_INIT(delete_depth_stencil_alpha_state);
+   CTX_INIT(create_fs_state);
+   CTX_INIT(bind_fs_state);
+   CTX_INIT(delete_fs_state);
+   CTX_INIT(create_vs_state);
+   CTX_INIT(bind_vs_state);
+   CTX_INIT(delete_vs_state);
+   CTX_INIT(create_gs_state);
+   CTX_INIT(bind_gs_state);
+   CTX_INIT(delete_gs_state);
+   CTX_INIT(create_tcs_state);
+   CTX_INIT(bind_tcs_state);
+   CTX_INIT(delete_tcs_state);
+   CTX_INIT(create_tes_state);
+   CTX_INIT(bind_tes_state);
+   CTX_INIT(delete_tes_state);
+   CTX_INIT(create_vertex_elements_state);
+   CTX_INIT(bind_vertex_elements_state);
+   CTX_INIT(delete_vertex_elements_state);
+   CTX_INIT(set_blend_color);
+   CTX_INIT(set_stencil_ref);
+   CTX_INIT(set_sample_mask);
+   CTX_INIT(set_min_samples);
+   CTX_INIT(set_clip_state);
+   CTX_INIT(set_constant_buffer);
+   CTX_INIT(set_framebuffer_state);
+   CTX_INIT(set_polygon_stipple);
+   CTX_INIT(set_scissor_states);
+   CTX_INIT(set_viewport_states);
+   CTX_INIT(set_sampler_views);
+   CTX_INIT(set_tess_state);
+   CTX_INIT(set_shader_buffers);
+   CTX_INIT(set_shader_images);
+   CTX_INIT(set_vertex_buffers);
+   CTX_INIT(set_index_buffer);
+   CTX_INIT(create_stream_output_target);
+   CTX_INIT(stream_output_target_destroy);
+   CTX_INIT(set_stream_output_targets);
+   CTX_INIT(create_sampler_view);
+   CTX_INIT(sampler_view_destroy);
+   CTX_INIT(create_surface);
+   CTX_INIT(surface_destroy);
+   CTX_INIT(create_image_view);
+   CTX_INIT(image_view_destroy);
+   CTX_INIT(transfer_map);
+   CTX_INIT(transfer_flush_region);
+   CTX_INIT(transfer_unmap);
+   CTX_INIT(transfer_inline_write);
+   CTX_INIT(texture_barrier);
+   CTX_INIT(memory_barrier);
+   /* create_video_codec */
+   /* create_video_buffer */
+   /* create_compute_state */
+   /* bind_compute_state */
+   /* delete_compute_state */
+   /* set_compute_resources */
+   /* set_global_binding */
+   CTX_INIT(get_sample_position);
+   CTX_INIT(invalidate_resource);
+   CTX_INIT(get_device_reset_status);
+   CTX_INIT(dump_debug_state);
+
+   dd_init_draw_functions(dctx);
+
+   dctx->sample_mask = ~0;
+   return &dctx->base;
+}
diff --git a/src/gallium/drivers/ddebug/dd_draw.c b/src/gallium/drivers/ddebug/dd_draw.c
new file mode 100644
index 0000000..33f46a6
--- /dev/null
+++ b/src/gallium/drivers/ddebug/dd_draw.c
@@ -0,0 +1,807 @@ 
+/**************************************************************************
+ *
+ * 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.h"
+
+#include "util/u_dump.h"
+#include "util/u_format.h"
+#include "tgsi/tgsi_scan.h"
+#include "os/os_process.h"
+#include <errno.h>
+#include <sys/stat.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 pipe_screen *screen = dctx->pipe->screen;
+   static unsigned index;
+   char proc_name[128], dir[256], name[512];
+   FILE *f;
+
+   if (!os_get_process_name(proc_name, sizeof(proc_name))) {
+      fprintf(stderr, "dd: can't get the process name\n");
+      return NULL;
+   }
+
+   snprintf(dir, sizeof(dir), "%s/"DD_DIR, debug_get_option("HOME", "."));
+
+   if (mkdir(dir, 0774) && errno != EEXIST) {
+      fprintf(stderr, "dd: can't create a directory (%i)\n", errno);
+      return NULL;
+   }
+
+   snprintf(name, sizeof(name), "%s/%s_%u_%08u", dir, proc_name, getpid(), index++);
+   f = fopen(name, "w");
+   if (!f) {
+      fprintf(stderr, "dd: can't open file %s\n", name);
+      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)
+{
+   if (dd_screen(dctx->base.screen)->mode == DD_DETECT_HANGS &&
+       !dd_screen(dctx->base.screen)->no_flush)
+      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)
+{
+   switch (dd_screen(dctx->base.screen)->mode) {
+   case DD_DETECT_HANGS:
+      if (!dd_screen(dctx->base.screen)->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:
+      dd_dump_call(dctx, call, 0);
+      break;
+   default:
+      assert(0);
+   }
+}
+
+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 */
+}
diff --git a/src/gallium/drivers/ddebug/dd_public.h b/src/gallium/drivers/ddebug/dd_public.h
new file mode 100644
index 0000000..e660765
--- /dev/null
+++ b/src/gallium/drivers/ddebug/dd_public.h
@@ -0,0 +1,36 @@ 
+/**************************************************************************
+ *
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ * Copyright 2010 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.
+ *
+ **************************************************************************/
+
+#ifndef DD_PUBLIC_H_
+#define DD_PUBLIC_H_
+
+struct pipe_screen;
+
+struct pipe_screen *
+ddebug_screen_create(struct pipe_screen *screen);
+
+#endif /* DD_PUBLIC_H_ */
diff --git a/src/gallium/drivers/ddebug/dd_screen.c b/src/gallium/drivers/ddebug/dd_screen.c
new file mode 100644
index 0000000..ee04c77
--- /dev/null
+++ b/src/gallium/drivers/ddebug/dd_screen.c
@@ -0,0 +1,353 @@ 
+/**************************************************************************
+ *
+ * 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.h"
+#include "dd_public.h"
+#include "util/u_memory.h"
+#include <stdio.h>
+
+
+static const char *
+dd_screen_get_name(struct pipe_screen *_screen)
+{
+   struct pipe_screen *screen = dd_screen(_screen)->screen;
+
+   return screen->get_name(screen);
+}
+
+static const char *
+dd_screen_get_vendor(struct pipe_screen *_screen)
+{
+   struct pipe_screen *screen = dd_screen(_screen)->screen;
+
+   return screen->get_vendor(screen);
+}
+
+static const char *
+dd_screen_get_device_vendor(struct pipe_screen *_screen)
+{
+   struct pipe_screen *screen = dd_screen(_screen)->screen;
+
+   return screen->get_device_vendor(screen);
+}
+
+static int
+dd_screen_get_param(struct pipe_screen *_screen,
+                    enum pipe_cap param)
+{
+   struct pipe_screen *screen = dd_screen(_screen)->screen;
+
+   return screen->get_param(screen, param);
+}
+
+static float
+dd_screen_get_paramf(struct pipe_screen *_screen,
+                     enum pipe_capf param)
+{
+   struct pipe_screen *screen = dd_screen(_screen)->screen;
+
+   return screen->get_paramf(screen, param);
+}
+
+static int
+dd_screen_get_shader_param(struct pipe_screen *_screen, unsigned shader,
+                           enum pipe_shader_cap param)
+{
+   struct pipe_screen *screen = dd_screen(_screen)->screen;
+
+   return screen->get_shader_param(screen, shader, param);
+}
+
+static uint64_t
+dd_screen_get_timestamp(struct pipe_screen *_screen)
+{
+   struct pipe_screen *screen = dd_screen(_screen)->screen;
+
+   return screen->get_timestamp(screen);
+}
+
+static struct pipe_context *
+dd_screen_context_create(struct pipe_screen *_screen, void *priv,
+                         unsigned flags)
+{
+   struct dd_screen *dscreen = dd_screen(_screen);
+   struct pipe_screen *screen = dscreen->screen;
+
+   flags |= PIPE_CONTEXT_DEBUG;
+
+   return dd_context_create(dscreen,
+                            screen->context_create(screen, priv, flags));
+}
+
+static boolean
+dd_screen_is_format_supported(struct pipe_screen *_screen,
+                              enum pipe_format format,
+                              enum pipe_texture_target target,
+                              unsigned sample_count,
+                              unsigned tex_usage)
+{
+   struct pipe_screen *screen = dd_screen(_screen)->screen;
+
+   return screen->is_format_supported(screen, format, target, sample_count,
+                                      tex_usage);
+}
+
+static boolean
+dd_screen_can_create_resource(struct pipe_screen *_screen,
+                              const struct pipe_resource *templat)
+{
+   struct pipe_screen *screen = dd_screen(_screen)->screen;
+
+   return screen->can_create_resource(screen, templat);
+}
+
+static void
+dd_screen_flush_frontbuffer(struct pipe_screen *_screen,
+                            struct pipe_resource *resource,
+                            unsigned level, unsigned layer,
+                            void *context_private,
+                            struct pipe_box *sub_box)
+{
+   struct pipe_screen *screen = dd_screen(_screen)->screen;
+
+   screen->flush_frontbuffer(screen, resource, level, layer, context_private,
+                             sub_box);
+}
+
+static int
+dd_screen_get_driver_query_info(struct pipe_screen *_screen,
+                                unsigned index,
+                                struct pipe_driver_query_info *info)
+{
+   struct pipe_screen *screen = dd_screen(_screen)->screen;
+
+   return screen->get_driver_query_info(screen, index, info);
+}
+
+static int
+dd_screen_get_driver_query_group_info(struct pipe_screen *_screen,
+                                      unsigned index,
+                                      struct pipe_driver_query_group_info *info)
+{
+   struct pipe_screen *screen = dd_screen(_screen)->screen;
+
+   return screen->get_driver_query_group_info(screen, index, info);
+}
+
+
+/********************************************************************
+ * resource
+ */
+
+static struct pipe_resource *
+dd_screen_resource_create(struct pipe_screen *_screen,
+                          const struct pipe_resource *templat)
+{
+   struct pipe_screen *screen = dd_screen(_screen)->screen;
+   struct pipe_resource *res = screen->resource_create(screen, templat);
+
+   if (!res)
+      return NULL;
+   res->screen = _screen;
+   return res;
+}
+
+static struct pipe_resource *
+dd_screen_resource_from_handle(struct pipe_screen *_screen,
+                               const struct pipe_resource *templ,
+                               struct winsys_handle *handle)
+{
+   struct pipe_screen *screen = dd_screen(_screen)->screen;
+   struct pipe_resource *res =
+      screen->resource_from_handle(screen, templ, handle);
+
+   if (!res)
+      return NULL;
+   res->screen = _screen;
+   return res;
+}
+
+static struct pipe_resource *
+dd_screen_resource_from_user_memory(struct pipe_screen *_screen,
+                                    const struct pipe_resource *templ,
+                                    void *user_memory)
+{
+   struct pipe_screen *screen = dd_screen(_screen)->screen;
+   struct pipe_resource *res =
+      screen->resource_from_user_memory(screen, templ, user_memory);
+
+   if (!res)
+      return NULL;
+   res->screen = _screen;
+   return res;
+}
+
+static void
+dd_screen_resource_destroy(struct pipe_screen *_screen,
+                           struct pipe_resource *res)
+{
+   struct pipe_screen *screen = dd_screen(_screen)->screen;
+
+   screen->resource_destroy(screen, res);
+}
+
+static boolean
+dd_screen_resource_get_handle(struct pipe_screen *_screen,
+                              struct pipe_resource *resource,
+                              struct winsys_handle *handle)
+{
+   struct pipe_screen *screen = dd_screen(_screen)->screen;
+
+   return screen->resource_get_handle(screen, resource, handle);
+}
+
+
+/********************************************************************
+ * fence
+ */
+
+static void
+dd_screen_fence_reference(struct pipe_screen *_screen,
+                          struct pipe_fence_handle **pdst,
+                          struct pipe_fence_handle *src)
+{
+   struct pipe_screen *screen = dd_screen(_screen)->screen;
+
+   screen->fence_reference(screen, pdst, src);
+}
+
+static boolean
+dd_screen_fence_finish(struct pipe_screen *_screen,
+                       struct pipe_fence_handle *fence,
+                       uint64_t timeout)
+{
+   struct pipe_screen *screen = dd_screen(_screen)->screen;
+
+   return screen->fence_finish(screen, fence, timeout);
+}
+
+
+/********************************************************************
+ * screen
+ */
+
+static void
+dd_screen_destroy(struct pipe_screen *_screen)
+{
+   struct dd_screen *dscreen = dd_screen(_screen);
+   struct pipe_screen *screen = dscreen->screen;
+
+   screen->destroy(screen);
+   FREE(dscreen);
+}
+
+struct pipe_screen *
+ddebug_screen_create(struct pipe_screen *screen)
+{
+   struct dd_screen *dscreen;
+   const char *option = debug_get_option("GALLIUM_DDEBUG", NULL);
+   bool dump_always = option && !strcmp(option, "always");
+   bool no_flush = option && strstr(option, "noflush");
+   bool help = option && !strcmp(option, "help");
+   unsigned timeout = 0;
+
+   if (help) {
+      puts("Gallium driver debugger");
+      puts("");
+      puts("Usage:");
+      puts("");
+      puts("  GALLIUM_DDEBUG=always");
+      puts("    Dump context and driver information after every draw call into");
+      puts("    $HOME/"DD_DIR"/.");
+      puts("");
+      puts("  GALLIUM_DDEBUG=[timeout in ms] noflush");
+      puts("    Flush and detect a device hang after every draw call based on the given");
+      puts("    fence timeout and dump context and driver information into");
+      puts("    $HOME/"DD_DIR"/ when a hang is detected.");
+      puts("    If 'noflush' is specified, only detect hangs in pipe->flush.");
+      puts("");
+      exit(0);
+   }
+
+   if (!option)
+      return screen;
+   if (!dump_always && sscanf(option, "%u", &timeout) != 1)
+      return screen;
+
+   dscreen = CALLOC_STRUCT(dd_screen);
+   if (!dscreen)
+      return NULL;
+
+#define SCR_INIT(_member) \
+   dscreen->base._member = screen->_member ? dd_screen_##_member : NULL
+
+   dscreen->base.destroy = dd_screen_destroy;
+   dscreen->base.get_name = dd_screen_get_name;
+   dscreen->base.get_vendor = dd_screen_get_vendor;
+   dscreen->base.get_device_vendor = dd_screen_get_device_vendor;
+   dscreen->base.get_param = dd_screen_get_param;
+   dscreen->base.get_paramf = dd_screen_get_paramf;
+   dscreen->base.get_shader_param = dd_screen_get_shader_param;
+   /* get_video_param */
+   /* get_compute_param */
+   SCR_INIT(get_timestamp);
+   dscreen->base.context_create = dd_screen_context_create;
+   dscreen->base.is_format_supported = dd_screen_is_format_supported;
+   /* is_video_format_supported */
+   SCR_INIT(can_create_resource);
+   dscreen->base.resource_create = dd_screen_resource_create;
+   dscreen->base.resource_from_handle = dd_screen_resource_from_handle;
+   SCR_INIT(resource_from_user_memory);
+   dscreen->base.resource_get_handle = dd_screen_resource_get_handle;
+   dscreen->base.resource_destroy = dd_screen_resource_destroy;
+   SCR_INIT(flush_frontbuffer);
+   SCR_INIT(fence_reference);
+   SCR_INIT(fence_finish);
+   SCR_INIT(get_driver_query_info);
+   SCR_INIT(get_driver_query_group_info);
+
+#undef SCR_INIT
+
+   dscreen->screen = screen;
+   dscreen->timeout_ms = timeout;
+   dscreen->mode = dump_always ? DD_DUMP_ALL_CALLS : DD_DETECT_HANGS;
+   dscreen->no_flush = no_flush;
+
+   switch (dscreen->mode) {
+   case DD_DUMP_ALL_CALLS:
+      fprintf(stderr, "Gallium debugger active. Logging all calls.\n");
+      break;
+   case DD_DETECT_HANGS:
+      fprintf(stderr, "Gallium debugger active. "
+              "The hang detection timout is %i ms.\n", timeout);
+      break;
+   default:
+      assert(0);
+   }
+
+   return &dscreen->base;
+}
diff --git a/src/gallium/targets/dri/Makefile.am b/src/gallium/targets/dri/Makefile.am
index 7c86ea1..7f945d1 100644
--- a/src/gallium/targets/dri/Makefile.am
+++ b/src/gallium/targets/dri/Makefile.am
@@ -11,6 +11,7 @@  AM_CFLAGS = \
 AM_CPPFLAGS = \
 	$(DEFINES) \
 	-DDRI_TARGET \
+        -DGALLIUM_DDEBUG \
 	-DGALLIUM_NOOP \
 	-DGALLIUM_RBUG \
 	-DGALLIUM_TRACE
@@ -45,6 +46,7 @@  gallium_dri_la_LIBADD = \
 	$(top_builddir)/src/gallium/state_trackers/dri/libdri.la \
 	$(top_builddir)/src/gallium/auxiliary/libgalliumvl.la \
 	$(top_builddir)/src/gallium/auxiliary/libgallium.la \
+        $(top_builddir)/src/gallium/drivers/ddebug/libddebug.la \
 	$(top_builddir)/src/gallium/drivers/noop/libnoop.la \
 	$(top_builddir)/src/gallium/drivers/rbug/librbug.la \
 	$(top_builddir)/src/gallium/drivers/trace/libtrace.la \
diff --git a/src/gallium/targets/pipe-loader/Makefile.am b/src/gallium/targets/pipe-loader/Makefile.am
index 4d9f7be..f18e68c 100644
--- a/src/gallium/targets/pipe-loader/Makefile.am
+++ b/src/gallium/targets/pipe-loader/Makefile.am
@@ -29,6 +29,7 @@  AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/gallium/winsys \
 	$(LIBDRM_CFLAGS) \
 	$(VISIBILITY_CFLAGS) \
+        -DGALLIUM_DDEBUG \
 	-DGALLIUM_RBUG \
 	-DGALLIUM_TRACE
 
@@ -55,6 +56,7 @@  PIPE_LIBS += \
 	$(top_builddir)/src/glsl/libnir.la \
 	$(top_builddir)/src/libglsl_util.la \
 	$(top_builddir)/src/util/libmesautil.la \
+        $(top_builddir)/src/gallium/drivers/ddebug/libddebug.la \
 	$(top_builddir)/src/gallium/drivers/rbug/librbug.la \
 	$(top_builddir)/src/gallium/drivers/trace/libtrace.la \
 	$(GALLIUM_COMMON_LIB_DEPS)

Comments

Hi Marek,

On 23 August 2015 at 13:04, Marek Olšák <maraeo@gmail.com> wrote:


> --- /dev/null
> +++ b/src/gallium/drivers/ddebug/Makefile.am
> @@ -0,0 +1,11 @@
> +include Makefile.sources
> +include $(top_srcdir)/src/gallium/Automake.inc
> +
> +AM_CFLAGS = \
> +       $(GALLIUM_DRIVER_CFLAGS)
> +
> +noinst_LTLIBRARIES = libddebug.la
> +
> +libddebug_la_SOURCES = $(C_SOURCES)
> +
> +EXTRA_DIST = SConscript


> --- /dev/null
> +++ b/src/gallium/drivers/ddebug/SConscript
Considering it's not wired up, you can just drop the sconscript build
(and the EXTRA_DIST above)

> diff --git a/src/gallium/drivers/ddebug/dd.h b/src/gallium/drivers/ddebug/dd.h
> new file mode 100644
> index 0000000..c78d112
> --- /dev/null
> +++ b/src/gallium/drivers/ddebug/dd.h
There is already a dd.h file in mesa. So it might be save us some
headaches if we rename this one.

Haven't looked at the symbols provided by dd.h/dd_public.h but I'd
suspect there are no conflicts.

> diff --git a/src/gallium/targets/pipe-loader/Makefile.am b/src/gallium/targets/pipe-loader/Makefile.am
> index 4d9f7be..f18e68c 100644
> --- a/src/gallium/targets/pipe-loader/Makefile.am
> +++ b/src/gallium/targets/pipe-loader/Makefile.am
> @@ -29,6 +29,7 @@ AM_CPPFLAGS = \
>         -I$(top_srcdir)/src/gallium/winsys \
>         $(LIBDRM_CFLAGS) \
>         $(VISIBILITY_CFLAGS) \
> +        -DGALLIUM_DDEBUG \
>         -DGALLIUM_RBUG \
>         -DGALLIUM_TRACE
>
> @@ -55,6 +56,7 @@ PIPE_LIBS += \
>         $(top_builddir)/src/glsl/libnir.la \
>         $(top_builddir)/src/libglsl_util.la \
>         $(top_builddir)/src/util/libmesautil.la \
> +        $(top_builddir)/src/gallium/drivers/ddebug/libddebug.la \
>         $(top_builddir)/src/gallium/drivers/rbug/librbug.la \
>         $(top_builddir)/src/gallium/drivers/trace/libtrace.la \
>         $(GALLIUM_COMMON_LIB_DEPS)
For this to work with the separate pipe-drivers, you need to update
each one (pipe_*.c)

If you want you can do so later on, but if so please drop these
makefile changes.

Thanks
Emil