[Mesa-dev] [PATCH 33/33] intel: add aubinator ui

Scott D Phillips scott.d.phillips at intel.com
Tue Oct 31 21:11:26 UTC 2017


Lionel Landwerlin <lionel.g.landwerlin at intel.com> writes:

> The motivation for building this UI is to help debug the intel
> drivers. It can be tricky to figure what is going wrong in the
> emission of commands (figuring out if there is an off by one error in
> the sampler state pointers for example or if you accidently override
> some bits of memory). Going through the text output of aubinator can
> be quite tedious (time consuming and error prone), as you need to go
> back and forth several pages of dumps to build a mental model of the
> state of the GPU for a given draw call. This calls for a better way to
> display/search an aubdump.
>
> This UI is mostly based off the original aubinator code. It's using
> the ImGui toolkit [1]. Being an immediate mode UI toolkit, it makes
> the code of the original aubinator pretty easy to reuse/port to
> generate a UI.
>
> This also adds an interesting feature which collects the GPU state by
> walking the instruction stream and "snapshotting" the last sets of
> commands allowing to present what would be the state of the GPU at a
> given point (right now it only snapshots on 3DPRIMITIVE commands).
>
> Some screenshots :
>     https://i.imgur.com/0JTLkTo.png
>     https://i.imgur.com/ABq31XD.png
>
> Although the UI toolkit isn't tied to any of the main toolkit (like
> Gtk+, Qt, etc...), we still require a connection to the desktop
> display. The ImGui backend currently used is built on Gtk+ & Cogl. The
> choice of Gtk+ is mostly due to having UI scaling working properly on
> wayland/x11 (on hidpi screens). Cogl mostly because it's more
> convenient that raw GL. Of course this could be revisited.
>
> [1] : https://github.com/ocornut/imgui
> ---
>  configure.ac                                   |    16 +
>  meson.build                                    |     5 +-
>  meson_options.txt                              |     6 +
>  src/intel/Makefile.tools.am                    |    58 +
>  src/intel/tools/.gitignore                     |     2 +
>  src/intel/tools/aubinator_imgui_widgets.cpp    |   183 +
>  src/intel/tools/aubinator_imgui_widgets.h      |    12 +
>  src/intel/tools/aubinator_ui.cpp               |  3116 +++++++
>  src/intel/tools/imgui/LICENSE.txt              |    21 +
>  src/intel/tools/imgui/imconfig.h               |    57 +
>  src/intel/tools/imgui/imgui.cpp                | 10725 +++++++++++++++++++++++
>  src/intel/tools/imgui/imgui.h                  |  1516 ++++
>  src/intel/tools/imgui/imgui_demo.cpp           |  2827 ++++++
>  src/intel/tools/imgui/imgui_draw.cpp           |  2673 ++++++
>  src/intel/tools/imgui/imgui_impl_gtk3_cogl.cpp |   784 ++
>  src/intel/tools/imgui/imgui_impl_gtk3_cogl.h   |    27 +
>  src/intel/tools/imgui/imgui_internal.h         |   864 ++
>  src/intel/tools/imgui/stb_rect_pack.h          |   583 ++
>  src/intel/tools/imgui/stb_textedit.h           |  1322 +++
>  src/intel/tools/imgui/stb_truetype.h           |  4018 +++++++++
>  src/intel/tools/memory.c                       |   545 ++
>  src/intel/tools/memory.h                       |    55 +
>  src/intel/tools/meson.build                    |    37 +
>  23 files changed, 29451 insertions(+), 1 deletion(-)
>  create mode 100644 src/intel/tools/aubinator_imgui_widgets.cpp
>  create mode 100644 src/intel/tools/aubinator_imgui_widgets.h
>  create mode 100644 src/intel/tools/aubinator_ui.cpp
>  create mode 100644 src/intel/tools/imgui/LICENSE.txt
>  create mode 100644 src/intel/tools/imgui/imconfig.h
>  create mode 100644 src/intel/tools/imgui/imgui.cpp
>  create mode 100644 src/intel/tools/imgui/imgui.h
>  create mode 100644 src/intel/tools/imgui/imgui_demo.cpp
>  create mode 100644 src/intel/tools/imgui/imgui_draw.cpp
>  create mode 100644 src/intel/tools/imgui/imgui_impl_gtk3_cogl.cpp
>  create mode 100644 src/intel/tools/imgui/imgui_impl_gtk3_cogl.h
>  create mode 100644 src/intel/tools/imgui/imgui_internal.h
>  create mode 100644 src/intel/tools/imgui/stb_rect_pack.h
>  create mode 100644 src/intel/tools/imgui/stb_textedit.h
>  create mode 100644 src/intel/tools/imgui/stb_truetype.h
>  create mode 100644 src/intel/tools/memory.c
>  create mode 100644 src/intel/tools/memory.h
>
> diff --git a/configure.ac b/configure.ac
> index 9aa02f55ded..ec5cfae459d 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -2839,6 +2839,22 @@ fi
>  
>  AC_MSG_RESULT([$VALGRIND])
>  
> +AC_ARG_ENABLE(intel-tools,
> +              [AS_HELP_STRING([--enable-intel-tools],
> +                             [Build tools for intel GPUs (default: auto)])],
> +                             [INTEL_TOOLS=$enableval], [INTEL_TOOLS=auto])
> +if test "x$INTEL_TOOLS" != xno; then
> +    PKG_CHECK_MODULES([AUBINATOR_UI], [gtk+-3.0 cogl-2.0-experimental],
> +                      [have_intel_tools=yes], [have_intel_tools=no])
> +fi
> +AC_MSG_CHECKING([whether to compile tools for intel GPUs])
> +if test "x$INTEL_TOOLS" = xauto; then
> +    INTEL_TOOLS="$have_intel_tools"
> +fi
> +AC_MSG_RESULT([$INTEL_TOOLS])
> +
> +AM_CONDITIONAL(HAVE_INTEL_TOOLS, test "x$INTEL_TOOLS" = xyes)
> +
>  dnl Restore LDFLAGS and CPPFLAGS
>  LDFLAGS="$_SAVE_LDFLAGS"
>  CPPFLAGS="$_SAVE_CPPFLAGS"
> diff --git a/meson.build b/meson.build
> index 24d997b3e0a..e5e691e276c 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -43,6 +43,7 @@ pre_args = [
>  with_vulkan_icd_dir = get_option('vulkan-icd-dir')
>  with_tests = get_option('build-tests')
>  with_valgrind = get_option('valgrind')
> +with_intel_tools = get_option('intel-tools')
>  with_libunwind = get_option('libunwind')
>  with_asm = get_option('asm')
>  with_llvm = get_option('llvm')
> @@ -209,7 +210,7 @@ if with_glx != 'disabled'
>      else
>        error('Cannot build GLX support without X11 platform support and at least one OpenGL API')
>      endif
> -  elif with_glx == 'gallium-xlib' 
> +  elif with_glx == 'gallium-xlib'

take this out of the mega commit.

>      if not with_gallium
>        error('Gallium-xlib based GLX requires at least one gallium driver')
>      elif with_dri
> @@ -696,6 +697,8 @@ if dep_valgrind.found() and with_valgrind
>    pre_args += '-DHAVE_VALGRIND'
>  endif
>  
> +dep_aubinator_ui = dependency('gtk+-3.0 cogl-2.0-experimental', required : false)
> +
>  # pthread stubs. Lets not and say we didn't
>  
>  prog_bison = find_program('bison', required : with_any_opengl)
> diff --git a/meson_options.txt b/meson_options.txt
> index 74f1e71bf43..d8931629b63 100644
> --- a/meson_options.txt
> +++ b/meson_options.txt
> @@ -180,3 +180,9 @@ option(
>    choices : ['8', '16', '32'],
>    description : 'Number of channel bits for OSMesa.'
>  )
> +option(
> +  'intel-tools',
> +  type : 'boolean',
> +  value : true,
> +  description : 'Build intel tools if possible'
> +)

Doesn't seem like this option is connected to anything.

> diff --git a/src/intel/Makefile.tools.am b/src/intel/Makefile.tools.am
> index af8cd5b6287..73c3769efdf 100644
> --- a/src/intel/Makefile.tools.am
> +++ b/src/intel/Makefile.tools.am
> @@ -23,6 +23,12 @@ noinst_PROGRAMS += \
>  	tools/aubinator \
>  	tools/aubinator_error_decode
>  
> +if HAVE_INTEL_TOOLS
> +noinst_PROGRAMS += \
> +	tools/aubinator_ui \
> +	tools/memory_tests
> +endif
> +
>  tools_aubinator_SOURCES = \
>  	tools/aubinator.c \
>  	tools/disasm.c \
> @@ -43,6 +49,51 @@ tools_aubinator_LDADD = \
>  	$(ZLIB_LIBS) \
>  	-lm
>  
> +tools_aubinator_ui_SOURCES = \
> +	tools/imgui/imconfig.h \
> +	tools/imgui/imgui.cpp \
> +	tools/imgui/imgui_demo.cpp \
> +	tools/imgui/imgui_draw.cpp \
> +	tools/imgui/imgui.h \
> +	tools/imgui/imgui_impl_gtk3_cogl.cpp \
> +	tools/imgui/imgui_impl_gtk3_cogl.h \
> +	tools/imgui/imgui_internal.h \
> +	tools/imgui/stb_rect_pack.h \
> +	tools/imgui/stb_textedit.h \
> +	tools/imgui/stb_truetype.h \
> +	\
> +	tools/aubinator_ui.cpp \
> +	tools/aubinator_imgui_widgets.cpp \
> +	tools/aubinator_imgui_widgets.h \
> +	tools/disasm.c \
> +	tools/gen_disasm.h \
> +	tools/intel_aub.h \
> +	tools/memory.c \
> +	tools/memory.h
> +
> +tools_aubinator_ui_CFLAGS = \
> +	$(AM_CFLAGS) \
> +	$(AUBINATOR_UI_CFLAGS) \
> +	$(EXPAT_CFLAGS) \
> +	$(ZLIB_CFLAGS) \
> +	-Itools/imgui
> +
> +tools_aubinator_ui_CXXFLAGS = \
> +	$(AUBINATOR_UI_CFLAGS) \
> +	-Itools/imgui
> +
> +tools_aubinator_ui_LDADD = \
> +	common/libintel_common.la \
> +	compiler/libintel_compiler.la \
> +	isl/libisl.la \
> +	$(top_builddir)/src/util/libmesautil.la \
> +	$(PER_GEN_LIBS) \
> +	$(PTHREAD_LIBS) \
> +	$(DLOPEN_LIBS) \
> +	$(EXPAT_LIBS) \
> +	$(ZLIB_LIBS) \
> +	$(AUBINATOR_UI_LIBS) \
> +	-lm
>  
>  tools_aubinator_error_decode_SOURCES = \
>  	tools/aubinator_error_decode.c \
> @@ -59,3 +110,10 @@ tools_aubinator_error_decode_LDADD = \
>  tools_aubinator_error_decode_CFLAGS = \
>  	$(AM_CFLAGS) \
>  	$(ZLIB_CFLAGS)
> +
> +tools_memory_tests_CFLAGS = \
> +	$(AM_CFLAGS) \
> +	-DBUILD_TESTS=1
> +tools_memory_tests_SOURCES = \
> +	tools/memory.c \
> +	tools/memory.h
> diff --git a/src/intel/tools/.gitignore b/src/intel/tools/.gitignore
> index 27437f9eefd..e648c99df62 100644
> --- a/src/intel/tools/.gitignore
> +++ b/src/intel/tools/.gitignore
> @@ -1,2 +1,4 @@
>  /aubinator
> +/aubinator_ui
>  /aubinator_error_decode
> +/memory_test
> diff --git a/src/intel/tools/aubinator_imgui_widgets.cpp b/src/intel/tools/aubinator_imgui_widgets.cpp
> new file mode 100644
> index 00000000000..6643cc1c512
> --- /dev/null
> +++ b/src/intel/tools/aubinator_imgui_widgets.cpp
> @@ -0,0 +1,183 @@
> +#include "aubinator_imgui_widgets.h"
> +
> +#define IMGUI_DEFINE_MATH_OPERATORS
> +
> +#include "imgui_internal.h"
> +
> +using namespace ImGui;
> +
> +namespace Aubinator {
> +
> +/* Sampler positions */
> +
> +void PlotDotsEx(const char* label, ImVec2 (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, ImVec2 scale_min, ImVec2 scale_max, ImVec2 graph_size, float radius)
> +{
> +   ImGuiWindow* window = GetCurrentWindow();
> +    if (window->SkipItems)
> +        return;
> +
> +    ImGuiContext& g = *GImGui;
> +    const ImGuiStyle& style = g.Style;
> +
> +    const ImVec2 label_size = CalcTextSize(label, NULL, true);
> +    if (graph_size.x == 0.0f)
> +        graph_size.x = CalcItemWidth();
> +    if (graph_size.y == 0.0f)
> +        graph_size.y = label_size.y + (style.FramePadding.y * 2);
> +
> +    const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y));
> +    const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
> +    const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0));
> +    ItemSize(total_bb, style.FramePadding.y);
> +    if (!ItemAdd(total_bb, NULL))
> +        return;
> +    const bool hovered = ItemHoverable(inner_bb, 0);
> +
> +    // Determine scale from values if not specified
> +    if (scale_min.x == FLT_MAX || scale_min.y == FLT_MAX ||
> +        scale_max.x == FLT_MAX || scale_max.y == FLT_MAX)
> +    {
> +        ImVec2 v_min(FLT_MAX, FLT_MAX);
> +        ImVec2 v_max(-FLT_MAX, -FLT_MAX);
> +        for (int i = 0; i < values_count; i++)
> +        {
> +            const ImVec2 v = values_getter(data, i);
> +            v_min = ImVec2(ImMin(v_min.x, v.x), ImMin(v_min.y, v.y));
> +            v_max = ImVec2(ImMax(v_max.x, v.x), ImMax(v_max.y, v.y));
> +        }
> +        if (scale_min.x == FLT_MAX)
> +            scale_min.x = v_min.x;
> +        if (scale_min.y == FLT_MAX)
> +            scale_min.y = v_min.y;
> +        if (scale_max.x == FLT_MAX)
> +            scale_max.x = v_max.x;
> +        if (scale_max.y == FLT_MAX)
> +            scale_max.y = v_max.y;
> +    }
> +
> +    RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
> +
> +
> +    ImVec2 max_deltas(scale_max.x - scale_min.x, scale_max.y - scale_min.y);
> +    ImVec2 paint_size = inner_bb.GetSize();
> +    for (int n = 0; n < values_count; n++)
> +    {
> +       const ImVec2 v = values_getter(data, n + values_offset);
> +       const ImVec2 point(inner_bb.GetTL() +
> +                      ImVec2(paint_size.x * (v.x - scale_min.x) / max_deltas.x,
> +                             paint_size.y * (v.y - scale_min.y) / max_deltas.y));
> +
> +       ImRect square(point, point);
> +       square.Expand(radius);
> +       if (hovered && square.Contains(g.IO.MousePos))
> +           SetTooltip("%fx%f", v.x, v.y);
> +
> +       window->DrawList->AddCircleFilled(point, radius,
> +                                         GetColorU32(ImGuiCol_PlotLines), 12);
> +    }
> +
> +    // Text overlay
> +    if (overlay_text)
> +        RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f,0.0f));
> +
> +    if (label_size.x > 0.0f)
> +        RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
> +}
> +
> +struct ImGuiPlotDotsArrayGetterData
> +{
> +    const ImVec2* Values;
> +    int Stride;
> +
> +    ImGuiPlotDotsArrayGetterData(const ImVec2* values, int stride) { Values = values; Stride = stride; }
> +};
> +
> +static ImVec2 PlotDots_ArrayGetter(void* data, int idx)
> +{
> +    ImGuiPlotDotsArrayGetterData* plot_data = (ImGuiPlotDotsArrayGetterData*)data;
> +    const ImVec2 v = *(ImVec2*)(void*)((unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride);
> +    return v;
> +}
> +
> +void PlotDots(const char* label, const ImVec2* values, int values_count, int values_offset, const char* overlay_text, ImVec2 scale_min, ImVec2 scale_max, ImVec2 graph_size, float radius, int stride)
> +{
> +    ImGuiPlotDotsArrayGetterData data(values, stride);
> +    PlotDotsEx(label, &PlotDots_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size, radius);
> +}
> +
> +void PlotDots(const char* label, ImVec2 (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, ImVec2 scale_min, ImVec2 scale_max, ImVec2 graph_size, float radius)
> +{
> +    PlotDotsEx(label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size, radius);
> +}
> +
> +/* URB */
> +
> +static bool urb_skip = true;
> +static int urb_element = 0, urb_n_elements = 1;
> +static float urb_size = 1;
> +static ImRect urb_inner_bb;
> +
> +void BeginURB(const char *label, int n_elements, float max_size, ImVec2 graph_size)
> +{
> +   ImGuiWindow* window = GetCurrentWindow();
> +   if (window->SkipItems)
> +      return;
> +
> +   urb_skip = false;
> +
> +   ImGuiContext& g = *GImGui;
> +   const ImGuiStyle& style = g.Style;
> +
> +   const ImVec2 label_size = CalcTextSize(label, NULL, true);
> +   if (graph_size.x == 0.0f)
> +      graph_size.x = CalcItemWidth();
> +   if (graph_size.y == 0.0f)
> +      graph_size.y = label_size.y + (style.FramePadding.y * 2);
> +
> +   const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y));
> +   const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
> +   const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0));
> +   ItemSize(total_bb, style.FramePadding.y);
> +   if (!ItemAdd(total_bb, NULL))
> +      return;
> +
> +   RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
> +
> +   urb_n_elements = n_elements;
> +   urb_element = 0;
> +   urb_inner_bb = inner_bb;
> +   urb_size = max_size;
> +}
> +
> +void URBItem(const char *label, const ImVec2& value, ImColor *out_color)
> +{
> +   if (urb_skip)
> +      return;
> +
> +   const ImRect rect(urb_inner_bb.GetTL() + ImVec2((value.x / urb_size) * urb_inner_bb.GetWidth(), 0),
> +                     urb_inner_bb.GetBL() + ImVec2(((value.x + value.y) / urb_size) * urb_inner_bb.GetWidth(), 0));
> +
> +   const bool hovered = ItemHoverable(rect, 0);
> +   float r, g, b;
> +   ImGui::ColorConvertHSVtoRGB(urb_element * 1.0f / urb_n_elements,
> +                               hovered ? 0.8f : 1.0f, 1.0f, r, g, b);
> +   if (hovered)
> +      SetTooltip("%s: offset=%.0f size=%.0f ", label, value.x, value.y);
> +
> +   ImGuiWindow* window = GetCurrentWindow();
> +   window->DrawList->AddRectFilled(rect.GetTL(), rect.GetBR(), ImColor(r, g, b));
> +
> +   if (out_color) {
> +      ImGui::ColorConvertHSVtoRGB(urb_element * 1.0f / urb_n_elements, 1.0f, 1.0f, r, g, b);
> +      *out_color = ImColor(r, g, b);
> +   }
> +
> +   urb_element++;
> +}
> +
> +void EndURB()
> +{
> +   urb_skip = true;
> +}
> +
> +}
> diff --git a/src/intel/tools/aubinator_imgui_widgets.h b/src/intel/tools/aubinator_imgui_widgets.h
> new file mode 100644
> index 00000000000..1cc0dbdb227
> --- /dev/null
> +++ b/src/intel/tools/aubinator_imgui_widgets.h
> @@ -0,0 +1,12 @@
> +#include "imgui.h"
> +
> +namespace Aubinator {
> +
> +void PlotDots(const char* label, const ImVec2* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, ImVec2 scale_min = ImVec2(FLT_MAX, FLT_MAX), ImVec2 scale_max = ImVec2(FLT_MAX, FLT_MAX), ImVec2 graph_size = ImVec2(0,0), float radius = 1.0f, int stride = sizeof(ImVec2));
> +void PlotDots(const char* label, ImVec2 (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, ImVec2 scale_min = ImVec2(FLT_MAX, FLT_MAX), ImVec2 scale_max = ImVec2(FLT_MAX, FLT_MAX), ImVec2 graph_size = ImVec2(0,0), float radius = 1.0f);
> +
> +void BeginURB(const char *label, int n_elements, float max_size, ImVec2 graph_size = ImVec2(0,0));
> +void URBItem(const char *label, const ImVec2& value, ImColor *out_color = NULL);
> +void EndURB();
> +
> +};
> diff --git a/src/intel/tools/aubinator_ui.cpp b/src/intel/tools/aubinator_ui.cpp
> new file mode 100644
> index 00000000000..fed43d685db
> --- /dev/null
> +++ b/src/intel/tools/aubinator_ui.cpp
> @@ -0,0 +1,3116 @@
> +/*
> + * Copyright © 2017 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (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 NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <stdint.h>
> +#include <getopt.h>
> +
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <string.h>
> +#include <signal.h>
> +#include <errno.h>
> +#include <inttypes.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <sys/wait.h>
> +#include <sys/mman.h>
> +
> +#include "common/gen_decoder.h"
> +#include "intel_aub.h"
> +#include "isl/isl.h"
> +#include "gen_disasm.h"
> +#include "memory.h"
> +#include "util/list.h"
> +#include "util/ralloc.h"
> +
> +#include "imgui.h"
> +#include "imgui_impl_gtk3_cogl.h"
> +#include "aubinator_imgui_widgets.h"
> +
> +#define xtzalloc(__type) ((__type *) calloc(1, sizeof(__type)))
> +#define xtalloc(__type) ((__type *) malloc(sizeof(__type)))
> +
> +/* Below is the only command missing from intel_aub.h in libdrm
> + * So, reuse intel_aub.h from libdrm and #define the
> + * AUB_MI_BATCH_BUFFER_END as below
> + */
> +#define AUB_MI_BATCH_BUFFER_END (0x0500 << 16)

Might as well send this as a patch to intel_aub.h

> +struct offset_at_base {
> +   const uint64_t *base;
> +   const char *base_name;
> +   char address[12];
> +   uint64_t offset;
> +};
> +
> +struct window {
> +   struct list_head windows;
> +
> +   char name[128];
> +   bool opened;
> +
> +   ImVec2 position;
> +   ImVec2 size;
> +
> +   void (*display)(struct window*);
> +   void (*destroy)(struct window*);
> +
> +   void (*reset)(struct window*);
> +};
> +
> +struct decode_options {
> +   struct ImGuiTextFilter field_filter;
> +
> +   bool drop_filtered;
> +   bool show_dwords;
> +};
> +
> +struct as_window {
> +   struct window base;
> +
> +   struct gpu_state *state;
> +   struct offset_at_base offset;
> +
> +   struct gen_group *as;
> +
> +   struct decode_options options;
> +};
> +
> +struct batch_window {
> +   struct window base;
> +
> +   bool collapsed;
> +
> +   uint32_t *ring_buffer;
> +
> +   struct ImGuiTextFilter cmd_filter;
> +   struct decode_options options;
> +};
> +
> +struct image_window {
> +   struct window base;
> +
> +   char shader[1024];
> +
> +   struct gpu_state *state;
> +   struct offset_at_base offset;
> +
> +   uint64_t base_address;
> +   char base_address_buf[20];
> +
> +   int width;
> +   int height;
> +   int stride;
> +   int plane_stride;
> +   enum isl_format format;
> +   uint32_t tiling;
> +
> +   CoglPipeline *pipeline;
> +};
> +
> +struct pattern_window {
> +   struct window base;
> +
> +   ImVec2 points[31];
> +   int ms_offset;
> +};
> +
> +struct shader_window {
> +   struct window base;
> +
> +   char *shader;
> +   size_t shader_len;
> +
> +   struct gpu_state *state;
> +   struct offset_at_base offset;
> +};
> +
> +struct state_window {
> +   struct window base;
> +
> +   struct gpu_state *state;
> +
> +   bool collapsed;
> +   struct ImGuiTextFilter cmd_filter;
> +   struct decode_options options;
> +};
> +
> +struct urb_window {
> +   struct window base;
> +
> +   struct gpu_state *state;
> +
> +   uint32_t max_allocation;
> +   uint32_t n_allocation;
> +   struct {
> +      const char *name;
> +      uint32_t offset;
> +      uint32_t length;
> +      ImColor color;
> +   } allocations[20];
> +};
> +
> +struct gpu_state_item {
> +   uint32_t refcount;
> +
> +   memory_t *gtt;
> +   uint64_t gtt_offset;
> +   struct gen_group *group;
> +};
> +
> +struct gpu_state {
> +   uint64_t general_state_base;
> +   uint64_t surface_state_base;
> +   uint64_t dynamic_state_base;
> +   uint64_t instruction_base;
> +   uint64_t instruction_bound;
> +
> +   struct hash_table *items;
> +
> +   memory_t *gtt;
> +};
> +
> +static struct {
> +   struct aub_file *file;
> +   char *input_file;
> +   char *xml_path;
> +
> +   memory_t *gtt;
> +
> +   /* Device state */
> +   uint16_t pci_id = 0;
> +   bool read_pci_id;
> +   char app_name[33];
> +   struct gen_device_info devinfo;
> +   struct gen_spec *spec;
> +   struct gen_disasm *disasm;
> +
> +   /* Custom handlers */
> +   struct hash_table *accumulation_handlers;
> +   struct hash_table *display_handlers;
> +
> +   /* Decoding state */
> +   int32_t command_index;
> +   uint32_t *command_block;
> +
> +    /* Snapshots of GPU states at each point of interest (usually
> +     * 3DPRIMITIVEs, but we could add more later...)
> +     */
> +   struct gpu_state *gpu_states[100];
> +   uint32_t n_gpu_states;
> +   uint32_t gpu_state_display_idx;
> +
> +   /* UI state*/
> +   bool show_commands_window;
> +   bool show_registers_window;
> +
> +   ImVec4 clear_color;
> +   ImVec4 dwords_color;
> +   ImVec4 highlight_color;
> +   ImVec4 error_color;
> +
> +   struct list_head windows;
> +   struct window *focused_window;
> +
> +   struct window file_window;
> +   struct window commands_window;
> +   struct window registers_window;
> +} context;
> +
> +/**/
> +
> +static struct gpu_state_item *
> +gpu_state_item_ref(struct gpu_state_item *item)
> +{
> +   item->refcount++;
> +   return item;
> +}
> +
> +static void
> +gpu_state_item_unref(struct gpu_state_item *item)
> +{
> +   if (--item->refcount == 0) {
> +      memory_unref(item->gtt);
> +      free(item);
> +   }
> +}
> +
> +static struct gpu_state_item *
> +gpu_state_item_new(memory_t *gtt, uint64_t gtt_offset,
> +                   struct gen_group *group)
> +{
> +   struct gpu_state_item *item = xtzalloc(struct gpu_state_item);
> +
> +   item->refcount = 1;
> +   item->gtt = memory_ref(gtt);
> +   item->gtt_offset = gtt_offset;
> +   item->group = group;
> +
> +   return item;
> +}
> +
> +static struct gpu_state *
> +gpu_state_new(void)
> +{
> +   struct gpu_state *state = xtzalloc(struct gpu_state);
> +
> +   state->gtt = memory_new();
> +   state->items = _mesa_hash_table_create(NULL, _mesa_hash_string,
> +                                          _mesa_key_string_equal);
> +
> +   return state;
> +}
> +
> +static struct gpu_state *
> +gpu_state_copy(struct gpu_state *parent)
> +{
> +   struct gpu_state *state = xtalloc(struct gpu_state);
> +   struct hash_entry *entry;
> +
> +   memcpy(state, parent, sizeof(*state));
> +
> +   state->gtt = memory_ref(parent->gtt);
> +   state->items = _mesa_hash_table_create(NULL, _mesa_hash_string,
> +                                          _mesa_key_string_equal);
> +   hash_table_foreach(parent->items, entry) {
> +      _mesa_hash_table_insert(state->items,
> +                              entry->key,
> +                              gpu_state_item_ref((struct gpu_state_item *) entry->data));
> +   }
> +
> +   return state;
> +}
> +
> +static void
> +gpu_state_free(struct gpu_state *state)
> +{
> +   struct hash_entry *entry;
> +   hash_table_foreach(state->items, entry)
> +      gpu_state_item_unref((struct gpu_state_item *) entry->data);
> +   _mesa_hash_table_destroy(state->items, NULL);
> +   memory_unref(state->gtt);
> +   free(state);
> +}
> +
> +static void
> +gpu_state_add(struct gpu_state *state,
> +              memory_t *gtt, uint64_t gtt_offset,
> +              struct gen_group *group)
> +{
> +   struct hash_entry *previous_entry =
> +      _mesa_hash_table_search(state->items, group->name);
> +   if (previous_entry)
> +      gpu_state_item_unref((struct gpu_state_item *) previous_entry->data);
> +   _mesa_hash_table_insert(state->items,
> +                           group->name,
> +                           gpu_state_item_new(gtt, gtt_offset, group));
> +}
> +
> +static struct gpu_state_item *
> +gpu_state_find(struct gpu_state *state, const char *name)
> +{
> +   struct hash_entry *entry =
> +      _mesa_hash_table_search(state->items, name);
> +   return entry ? ((struct gpu_state_item *) entry->data) : NULL;
> +}
> +
> +static inline struct gpu_state *
> +gpu_state_last(void)
> +{
> +   return context.gpu_states[context.n_gpu_states - 1];
> +}
> +
> +/**/
> +
> +static void new_image_window(struct gpu_state *state, uint64_t offset);
> +static void new_pattern_window(const struct gen_dword_reader *reader);
> +static void new_pattern_window(struct gpu_state *state);
> +static void new_shader_window(struct gpu_state *state, uint64_t offset, const char *type);
> +static void new_state_window(struct gpu_state *state);
> +static void new_urb_window(struct gpu_state *state);
> +
> +static bool
> +has_ctrl_key(char key)
> +{
> +   return ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_COUNT + key);
> +}
> +
> +static bool
> +has_ctrl_imgui_key(ImGuiKey key)
> +{
> +   return ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(key);
> +}
> +
> +static bool
> +window_has_ctrl_key(char key)
> +{
> +   return ImGui::IsRootWindowOrAnyChildFocused() && has_ctrl_key(key);
> +}
> +
> +/**/
> +
> +const struct {
> +   const char *name;
> +   int pci_id;
> +} supported_gens[] = {
> +   { "ilk", 0x0046 }, /* Intel(R) Ironlake Mobile */
> +   { "snb", 0x0126 }, /* Intel(R) Sandybridge Mobile GT2 */
> +   { "ivb", 0x0166 }, /* Intel(R) Ivybridge Mobile GT2 */
> +   { "hsw", 0x0416 }, /* Intel(R) Haswell Mobile GT2 */
> +   { "byt", 0x0155 }, /* Intel(R) Bay Trail */
> +   { "bdw", 0x1616 }, /* Intel(R) HD Graphics 5500 (Broadwell GT2) */
> +   { "chv", 0x22B3 }, /* Intel(R) HD Graphics (Cherryview) */
> +   { "skl", 0x1912 }, /* Intel(R) HD Graphics 530 (Skylake GT2) */
> +   { "kbl", 0x591D }, /* Intel(R) Kabylake GT2 */
> +   { "bxt", 0x0A84 },  /* Intel(R) HD Graphics (Broxton) */
> +   { "cnl", 0x5A52 },  /* Intel(R) HD Graphics (Cannonlake) */
> +};
> +
> +/**/
> +
> +// static inline uint32_t
> +// field(uint32_t value, int start, int end)
> +// {
> +//    uint32_t mask;
> +
> +//    mask = ~0U >> (31 - end + start);
> +
> +//    return (value >> start) & mask;
> +// }
> +
> +struct brw_instruction;
> +
> +static uint32_t reader_cb(void *user_data, uint32_t dword_offset);
> +static uint64_t reader_offset(const struct gen_dword_reader *_reader);
> +
> +struct aubinator_reader {
> +   struct gen_dword_reader base;
> +
> +   enum {
> +      MEMORY,
> +      OFFSET,
> +      POINTER
> +   } type;
> +
> +   union {
> +      struct {
> +         memory_t *memory;
> +         uint64_t offset;
> +      };
> +      struct {
> +         const struct gen_dword_reader *parent;
> +         uint32_t dword_offset;
> +      };
> +      struct {
> +         uint32_t *ptr;
> +      };
> +   };
> +
> +   aubinator_reader() {
> +      this->base.read = reader_cb;
> +      this->base.user_data = this;
> +   }
> +
> +   aubinator_reader(memory_t *memory, uint64_t offset) : aubinator_reader() {
> +      this->type = MEMORY;
> +      this->memory = memory;
> +      this->offset = offset;
> +   }
> +
> +   aubinator_reader(const struct gen_dword_reader *parent, uint32_t dword_offset)
> +      : aubinator_reader() {
> +      this->type = OFFSET;
> +      this->parent = parent;
> +      this->dword_offset = dword_offset;
> +   }
> +
> +   aubinator_reader(uint32_t *ptr) : aubinator_reader() {
> +      this->type = POINTER;
> +      this->ptr = ptr;
> +   }
> +
> +   uint32_t read_dword(uint32_t dword_offset) const {
> +      switch (this->type) {
> +      case MEMORY:
> +         return memory_read_dword(this->memory, this->offset + 4 * dword_offset);
> +      case OFFSET:
> +         return gen_read_dword(this->parent, this->dword_offset + dword_offset);
> +      case POINTER:
> +         return this->ptr[dword_offset];
> +      }
> +      unreachable("Invalid memory");
> +   }
> +
> +   uint64_t get_offset() const {
> +      switch (this->type) {
> +      case MEMORY: return this->offset;
> +      case OFFSET: return reader_offset(this->parent) + 4 * this->dword_offset;
> +      case POINTER: return 0;
> +      }
> +      unreachable("Invalid memory");
> +   }
> +
> +   const struct gen_dword_reader *gen() const { return &this->base; }
> +};
> +
> +static uint64_t
> +reader_offset(const struct gen_dword_reader *_reader)
> +{
> +   const struct aubinator_reader *reader = (const struct aubinator_reader *) _reader;
> +   return reader->get_offset();
> +}
> +
> +static uint32_t
> +reader_cb(void *user_data, uint32_t dword_offset)
> +{
> +   const struct aubinator_reader *reader = (const struct aubinator_reader *) user_data;
> +   return reader->read_dword(dword_offset);
> +}
> +
> +/**/
> +
> +static inline int
> +gtt_valid_offset(memory_t *mem, uint64_t offset)
> +{
> +   return (offset >= memory_start(mem) && offset < memory_end(mem));
> +}
> +
> +static inline int
> +gtt_valid_range(memory_t *mem, uint64_t offset, uint32_t size)
> +{
> +   return (offset >= memory_start(mem) && offset < memory_end(mem) &&
> +           (offset + size) < memory_end(mem));
> +}
> +
> +static void
> +decode_group(struct gen_group *strct,
> +             const struct gen_dword_reader *reader,
> +             const struct decode_options *options)
> +{
> +   uint64_t offset = reader_offset(reader);
> +   struct gen_field_iterator iter;
> +   int last_dword = -1;
> +
> +   gen_field_iterator_init(&iter, strct, reader, false);
> +   do {
> +      // Display dword offset.
> +      if (options->show_dwords && last_dword != iter.dword) {
> +         for (int i = last_dword + 1; i <= iter.dword; i++) {
> +            ImGui::TextColored(context.dwords_color,
> +                               "0x%08" PRIx64 ":  0x%08x : Dword %d",
> +                               offset + 4 * i, gen_read_dword(reader, i), i);
> +         }
> +         last_dword = iter.dword;
> +      }
> +
> +      // Skip header fields.
> +      if (gen_field_is_header(iter.field))
> +         continue;
> +
> +      bool pass = (options->field_filter.PassFilter(iter.name) ||
> +                   options->field_filter.PassFilter(iter.value));
> +      bool highlight = options->field_filter.IsActive() && pass;
> +      if (highlight) ImGui::PushStyleColor(ImGuiCol_Text, context.highlight_color);
> +      if (!options->drop_filtered || pass) {
> +         if (!iter.struct_desc) {
> +            ImGui::Text("   %s: ", iter.name); ImGui::SameLine();
> +            ImGui::PushStyleColor(ImGuiCol_Text, context.dwords_color);
> +            ImGui::Text("%s", iter.value);
> +            ImGui::PopStyleColor();
> +         } else if (ImGui::TreeNodeEx((void *) (offset + iter.dword * 4),
> +                                      ImGuiTreeNodeFlags_DefaultOpen,
> +                                      "   %s: %s", iter.name, iter.value)) {
> +            struct aubinator_reader child_reader(reader, iter.dword);
> +            decode_group(iter.struct_desc, child_reader.gen(), options);
> +            ImGui::TreePop();
> +         }
> +      } else {
> +         if (iter.struct_desc && ImGui::TreeNodeEx((void *) (offset + iter.dword * 4),
> +                                                   ImGuiTreeNodeFlags_DefaultOpen,
> +                                                   "   %s: %s", iter.name, iter.value)) {
> +            struct aubinator_reader child_reader(reader, iter.dword);
> +            decode_group(iter.struct_desc, child_reader.gen(), options);
> +            ImGui::TreePop();
> +         }
> +      }
> +
> +      if (highlight) ImGui::PopStyleColor();
> +   } while (gen_field_iterator_next(&iter));
> +}
> +
> +// static void
> +// handle_3dstate_index_buffer(struct gen_spec *spec, uint32_t *p)
> +// {
> +//    void *start;
> +//    uint32_t length, i, type, size;
> +
> +//    start = gtt_at32(p[2]);
> +//    type = (p[1] >> 8) & 3;
> +//    size = 1 << type;
> +//    length = p[4] / size;
> +//    if (length > 10)
> +//       length = 10;
> +
> +//    fprintf(stdout, "\t");
> +
> +//    for (i = 0; i < length; i++) {
> +//       switch (type) {
> +//       case 0:
> +//          fprintf(stdout, "%3d ", ((uint8_t *)start)[i]);
> +//          break;
> +//       case 1:
> +//          fprintf(stdout, "%3d ", ((uint16_t *)start)[i]);
> +//          break;
> +//       case 2:
> +//          fprintf(stdout, "%3d ", ((uint32_t *)start)[i]);
> +//          break;
> +//       }
> +//    }
> +//    if (length < p[4] / size)
> +//       fprintf(stdout, "...\n");
> +//    else
> +//       fprintf(stdout, "\n");
> +// }
> +
> +static inline uint64_t
> +read_address(const struct gen_dword_reader *reader,
> +             uint32_t offset)
> +{
> +   /* Addresses are always guaranteed to be page-aligned and sometimes
> +    * hardware packets have extra stuff stuffed in the bottom 12 bits.
> +    */
> +   uint64_t addr = gen_read_dword(reader, offset) & ~0xfffu;
> +
> +   if (gen_spec_get_gen(context.spec) >= gen_make_gen(8,0)) {
> +      /* On Broadwell and above, we have 48-bit addresses which consume two
> +       * dwords.  Some packets require that these get stored in a "canonical
> +       * form" which means that bit 47 is sign-extended through the upper
> +       * bits. In order to correctly handle those aub dumps, we need to mask
> +       * off the top 16 bits.
> +       */
> +      addr |= ((uint64_t)gen_read_dword(reader, offset + 1) & 0xffff) << 32;
> +   }
> +
> +   return addr;
> +}
> +
> +static inline uint64_t
> +read_offset(const struct gen_dword_reader *reader,
> +            uint32_t dw_offset, uint32_t start, uint32_t end)
> +{
> +   assert(start <= end);
> +   assert(end < 64);
> +
> +   uint64_t mask = (~0ull >> (64 - (end - start + 1))) << start;
> +
> +   uint64_t offset = gen_read_dword(reader, dw_offset);
> +   if (end >= 32)
> +      offset |= (uint64_t) gen_read_dword(reader, dw_offset + 1) << 32;
> +
> +   return offset & mask;
> +}
> +
> +static void
> +address_maybe_update(struct gen_group *group,
> +                     const char *enable, const char *field_name,
> +                     const struct gen_dword_reader *reader,
> +                     uint64_t *address)
> +{
> +   struct gen_field *field = gen_group_find_field(group, enable);
> +   if (!field)
> +      return;
> +
> +   union gen_field_value value = { .u64 = 0 };
> +   gen_field_decode(field, reader, &value);
> +   if (!value.b32)
> +      return;
> +
> +   field = gen_group_find_field(group, field_name);
> +   assert(field);
> +
> +   gen_field_decode(field, reader, &value);
> +   *address = value.u64;
> +}
> +
> +static void
> +handle_state_base_address(struct gpu_state *state,
> +                          const struct gen_dword_reader *reader,
> +                          const struct decode_options *)
> +{
> +   struct gen_group *group =
> +      gen_spec_find_instruction_by_name(context.spec, "STATE_BASE_ADDRESS");
> +
> +   address_maybe_update(group,
> +                        "General State Base Address Modify Enable",
> +                        "General State Base Address",
> +                        reader, &state->general_state_base);
> +   address_maybe_update(group,
> +                        "Surface State Base Address Modify Enable",
> +                        "Surface State Base Address",
> +                        reader, &state->surface_state_base);
> +   address_maybe_update(group,
> +                        "Dynamic State Base Address Modify Enable",
> +                        "Dynamic State Base Address",
> +                        reader, &state->dynamic_state_base);
> +   address_maybe_update(group,
> +                        "Instruction Base Address Modify Enable",
> +                        "Instruction Base Address",
> +                        reader, &state->instruction_base);
> +   address_maybe_update(group,
> +                        "Instruction Buffer size Modify Enable",
> +                        "Instruction Buffer Size",
> +                        reader, &state->instruction_bound);
> +}
> +
> +static void
> +snapshot_gpu_state(struct gpu_state *,
> +                   const struct gen_dword_reader *,
> +                   const struct decode_options *)
> +{
> +   assert(context.n_gpu_states < ARRAY_SIZE(context.gpu_states));
> +   context.gpu_states[context.n_gpu_states++] = gpu_state_copy(gpu_state_last());
> +}
> +
> +static void
> +dump_binding_table(struct gpu_state *state, uint64_t offset,
> +                   const struct decode_options *options)
> +{
> +   struct gen_group *surface_state =
> +      gen_spec_find_struct(context.spec, "RENDER_SURFACE_STATE");
> +   if (surface_state == NULL) {
> +      fprintf(stderr, "did not find RENDER_SURFACE_STATE info\n");
> +      return;
> +   }
> +
> +   uint64_t surfaces_offset = state->surface_state_base + offset;
> +   for (uint32_t i = 0; i < 16; i++) {
> +      uint32_t surface_offset =
> +         memory_read_dword(state->gtt, surfaces_offset + i * 4);
> +      if (surface_offset == 0)
> +         continue;
> +      uint64_t surface_state_offset = state->surface_state_base + surface_offset;
> +      if (!gtt_valid_offset(state->gtt, surface_state_offset)) {
> +         ImGui::TextColored(context.error_color,
> +                            "pointer %u: %08lx <not valid>",
> +                            i, surface_state_offset);
> +         continue;
> +      }
> +
> +      if (ImGui::TreeNodeEx((void *) surface_state_offset,
> +                            ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen,
> +                            "pointer %u: %08lx", i, surface_state_offset)) {
> +         char button_name[64];
> +         snprintf(button_name, sizeof(button_name),
> +                  "Show as image##%lx", surface_state_offset);
> +         if (ImGui::Button(button_name))
> +            new_image_window(state, surface_state_offset);
> +
> +         struct aubinator_reader reader(state->gtt, surface_state_offset);
> +         decode_group(surface_state, reader.gen(), options);
> +         ImGui::TreePop();
> +      }
> +   }
> +}
> +
> +static void
> +dump_samplers(struct gpu_state *state, uint64_t offset,
> +              const struct decode_options *options)
> +{
> +   struct gen_group *sampler_state =
> +      gen_spec_find_struct(context.spec, "SAMPLER_STATE");
> +
> +   uint64_t samplers_offset = state->dynamic_state_base + offset;
> +   for (uint32_t i = 0; i < 4; i++) {
> +      uint64_t sampler_state_offset = samplers_offset + i * 16;
> +      if (ImGui::TreeNodeEx((void *) sampler_state_offset,
> +                            ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen,
> +                            "sampler state %d", i)) {
> +         struct aubinator_reader reader(state->gtt, sampler_state_offset);
> +         decode_group(sampler_state, reader.gen(), options);
> +         ImGui::TreePop();
> +      }
> +   }
> +}
> +
> +static void
> +handle_media_interface_descriptor_load(struct gpu_state *state,
> +                                       const struct gen_dword_reader *reader,
> +                                       const struct decode_options *options)
> +{
> +   struct gen_group *descriptor_structure =
> +      gen_spec_find_struct(context.spec, "INTERFACE_DESCRIPTOR_DATA");
> +
> +   if (!descriptor_structure) {
> +      fprintf(stdout, "did not find INTERFACE_DESCRIPTOR_DATA info\n");
> +      return;
> +   }
> +
> +   uint64_t start = state->dynamic_state_base + gen_read_dword(reader, 3);
> +   unsigned length = gen_read_dword(reader, 2) / 32;
> +   for (unsigned i = 0; i < length; i++) {
> +      ImGui::Text("descriptor %u:", i);
> +      struct aubinator_reader child_reader(state->gtt, start + 32 * i);
> +      decode_group(descriptor_structure, child_reader.gen(), options);
> +
> +      start = state->instruction_base + gen_read_dword(child_reader.gen(), 0);
> +      if (!gtt_valid_offset(state->gtt, start)) {
> +         ImGui::Text("kernel: %08" PRIx64 " <not valid>\n", start);
> +         continue;
> +      }
> +
> +      char button_name[64];
> +      snprintf(button_name, sizeof(button_name), "Kernel##%" PRIx64, start);
> +      if (ImGui::Button(button_name))
> +         new_shader_window(state, start, "CS/Media");
> +
> +      dump_samplers(state, gen_read_dword(child_reader.gen(), 3) & ~0x1f, options);
> +      dump_binding_table(state, gen_read_dword(child_reader.gen(), 4) & ~0x1f, options);
> +   }
> +}
> +
> +// /* Heuristic to determine whether a uint32_t is probably actually a float
> +//  * (http://stackoverflow.com/a/2953466)
> +//  */
> +
> +// static bool
> +// probably_float(uint32_t bits)

// neato

> +// {
> +//    int exp = ((bits & 0x7f800000U) >> 23) - 127;
> +//    uint32_t mant = bits & 0x007fffff;
> +
> +//    /* +- 0.0 */
> +//    if (exp == -127 && mant == 0)
> +//       return true;
> +
> +//    /* +- 1 billionth to 1 billion */
> +//    if (-30 <= exp && exp <= 30)
> +//       return true;
> +
> +//    /* some value with only a few binary digits */
> +//    if ((mant & 0x0000ffff) == 0)
> +//       return true;
> +
> +//    return false;
> +// }
> +
> +// static void
> +// handle_3dstate_vertex_buffers(struct gen_spec *spec, uint32_t *p)
> +// {
> +//    uint32_t *end, *s, *dw, *dwend;
> +//    uint64_t offset;
> +//    int n, i, count, stride;
> +
> +//    end = (p[0] & 0xff) + p + 2;
> +//    for (s = &p[1], n = 0; s < end; s += 4, n++) {
> +//       if (gen_spec_get_gen(spec) >= gen_make_gen(8, 0)) {
> +//          offset = *(uint64_t *) &s[1];
> +//          dwend = gtt_at32(offset + s[3]);
> +//       } else {
> +//          offset = s[1];
> +//          dwend = gtt_at32(s[2] + 1);
> +//       }
> +
> +//       stride = field(s[0], 0, 11);
> +//       count = 0;
> +//       fprintf(stdout, "vertex buffer %d, size %d\n", n, s[3]);
> +//       for (dw = gtt_at32(offset), i = 0; dw < dwend && i < 256; dw++) {
> +//          if (count == 0 && count % (8 * 4) == 0)
> +//             fprintf(stdout, "  ");
> +
> +//          if (probably_float(*dw))
> +//             fprintf(stdout, "  %8.2f", *(float *) dw);
> +//          else
> +//             fprintf(stdout, "  0x%08x", *dw);
> +
> +//          i++;
> +//          count += 4;
> +
> +//          if (count == stride) {
> +//             fprintf(stdout, "\n");
> +//             count = 0;
> +//          } else if (count % (8 * 4) == 0) {
> +//             fprintf(stdout, "\n");
> +//          } else {
> +//             fprintf(stdout, " ");
> +//          }
> +//       }
> +//       if (count > 0 && count % (8 * 4) != 0)
> +//          fprintf(stdout, "\n");
> +//    }
> +// }
> +
> +// static void
> +// handle_3dstate_constant(struct gen_spec *spec, uint32_t *p)
> +// {
> +//    int i, j, length;
> +//    uint32_t *dw;
> +//    float *f;
> +
> +//    for (i = 0; i < 4; i++) {
> +//       length = (p[1 + i / 2] >> (i & 1) * 16) & 0xffff;
> +//       f = (float *) (gtt_at32(p[3 + i * 2] + dynamic_state_base));
> +//       dw = (uint32_t *) f;
> +//       for (j = 0; j < length * 8; j++) {
> +//          if (probably_float(dw[j]))
> +//             fprintf(stdout, "  %04.3f", f[j]);
> +//          else
> +//             fprintf(stdout, "  0x%08x", dw[j]);
> +
> +//          if ((j & 7) == 7)
> +//             fprintf(stdout, "\n");
> +//       }
> +//    }
> +// }
> +
> +static void
> +handle_3dstate_vs(struct gpu_state *state,
> +                  const struct gen_dword_reader *reader,
> +                  const struct decode_options *)
> +{
> +   uint64_t start;
> +   int vs_enable;
> +
> +   if (gen_spec_get_gen(context.spec) >= gen_make_gen(8, 0)) {
> +      start = read_offset(reader, 1, 6, 63);
> +      vs_enable = gen_read_dword(reader, 7) & 1;
> +   } else {
> +      start = read_offset(reader, 1, 6, 31);
> +      vs_enable = gen_read_dword(reader, 5) & 1;
> +   }
> +
> +   if (vs_enable) {
> +      char button_name[30];
> +      snprintf(button_name, sizeof(button_name), "Kernel##%" PRIx64, start);
> +      if (ImGui::Button(button_name))
> +         new_shader_window(state, start, "VS/GS/DS");
> +   }
> +}
> +
> +static void
> +handle_3dstate_hs(struct gpu_state *state,
> +                  const struct gen_dword_reader *reader,
> +                  const struct decode_options *)
> +{
> +   uint64_t start;
> +   int hs_enable;
> +
> +   if (gen_spec_get_gen(context.spec) >= gen_make_gen(8, 0)) {
> +      start = read_offset(reader, 3, 6, 63);
> +   } else {
> +      start = read_offset(reader, 3, 6, 31);
> +   }
> +
> +   hs_enable = gen_read_dword(reader, 2) & 0x80000000;
> +
> +   if (hs_enable) {
> +      char button_name[30];
> +      snprintf(button_name, sizeof(button_name), "Kernel##%" PRIx64, start);
> +      if (ImGui::Button(button_name))
> +         new_shader_window(state, start, "HS");
> +   }
> +}
> +
> +static void
> +handle_3dstate_ps(struct gpu_state *state,
> +                  const struct gen_dword_reader *reader,
> +                  const struct decode_options *)
> +{
> +   uint32_t mask = ~((1 << 6) - 1);
> +   static const char *pixel_type[3] = {"8 pixel", "16 pixel", "32 pixel"};
> +   uint32_t k_mask, k_offsets[3] = { 1, 0, 0 };
> +   const char *used[3] = { NULL, NULL, NULL };
> +
> +   if (gen_spec_get_gen(context.spec) >= gen_make_gen(8, 0)) {
> +      k_mask = gen_read_dword(reader, 6) & 7;
> +      k_offsets[1] = 8;
> +      k_offsets[2] = 10;
> +   } else {
> +      k_mask = gen_read_dword(reader, 4) & 7;
> +      k_offsets[1] = 6;
> +      k_offsets[2] = 7;
> +   }
> +
> +#define DISPATCH_8 1
> +#define DISPATCH_16 2
> +#define DISPATCH_32 4
> +   switch (k_mask) {
> +   case DISPATCH_8:
> +      used[0] = pixel_type[0];
> +      break;
> +   case DISPATCH_16:
> +      used[0] = pixel_type[1];
> +      break;
> +   case DISPATCH_8 | DISPATCH_16:
> +      used[0] = pixel_type[0];
> +      used[2] = pixel_type[1];
> +      break;
> +   case DISPATCH_32:
> +      used[0] = pixel_type[2];
> +      break;
> +   case DISPATCH_16 | DISPATCH_32:
> +      used[1] = pixel_type[2];
> +      used[2] = pixel_type[1];
> +      break;
> +   case DISPATCH_8 | DISPATCH_16 | DISPATCH_32:
> +      used[0] = pixel_type[0];
> +      used[1] = pixel_type[2];
> +      used[2] = pixel_type[1];
> +      break;
> +   default:
> +      break;
> +   }
> +
> +   for (uint32_t i = 0; i < ARRAY_SIZE(k_offsets); i++) {
> +      if (!used[i]) {
> +         ImGui::Text("Kernel[%i] unused\n", i);
> +         continue;
> +      }
> +
> +      char button_name[20];
> +      snprintf(button_name, sizeof(button_name), "Kernel[%i] %s", i, used[i]);
> +      if (ImGui::Button(button_name)) {
> +         new_shader_window(state,
> +                           gen_read_dword(reader, k_offsets[i]) & mask, "PS");
> +      }
> +   }
> +}
> +
> +static void
> +handle_3dstate_binding_table_pointers(struct gpu_state *state,
> +                                      const struct gen_dword_reader *reader,
> +                                      const struct decode_options *options)
> +{
> +   dump_binding_table(state, gen_read_dword(reader, 1), options);
> +}
> +
> +static void
> +handle_3dstate_sampler_state_pointers(struct gpu_state *state,
> +                                      const struct gen_dword_reader *reader,
> +                                      const struct decode_options *options)
> +{
> +   dump_samplers(state, gen_read_dword(reader, 1), options);
> +}
> +
> +static void
> +handle_3dstate_sampler_state_pointers_gen6(struct gpu_state *state,
> +                                           const struct gen_dword_reader *reader,
> +                                           const struct decode_options *options)
> +{
> +   dump_samplers(state, gen_read_dword(reader, 1), options);
> +   dump_samplers(state, gen_read_dword(reader, 2), options);
> +   dump_samplers(state, gen_read_dword(reader, 3), options);
> +}
> +
> +static void
> +handle_3dstate_viewport_state_pointers_cc(struct gpu_state *state,
> +                                          const struct gen_dword_reader *reader,
> +                                          const struct decode_options *options)
> +{
> +   struct gen_group *cc_viewport = gen_spec_find_struct(context.spec, "CC_VIEWPORT");
> +   uint64_t start = state->dynamic_state_base + (gen_read_dword(reader, 1) & ~0x1fu);
> +
> +   for (uint32_t i = 0; i < 4; i++) {
> +      uint64_t viewport_offset = start + i * 8;
> +      if (ImGui::TreeNodeEx((void *) viewport_offset,
> +                            ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen,
> +                            "viewport %d", i)) {
> +         struct aubinator_reader child_reader(state->gtt, viewport_offset);
> +         decode_group(cc_viewport, child_reader.gen(), options);
> +         ImGui::TreePop();
> +      }
> +   }
> +}
> +
> +static void
> +handle_3dstate_viewport_state_pointers_sf_clip(struct gpu_state *state,
> +                                               const struct gen_dword_reader *reader,
> +                                               const struct decode_options *options)
> +{
> +   struct gen_group *sf_clip_viewport =
> +      gen_spec_find_struct(context.spec, "SF_CLIP_VIEWPORT");
> +   uint64_t start =
> +      state->dynamic_state_base + (gen_read_dword(reader, 1) & ~0x3fu);
> +   for (uint32_t i = 0; i < 4; i++) {
> +      uint64_t sf_clip_offset = start + i * 64;
> +      if (ImGui::TreeNodeEx((void *) sf_clip_offset,
> +                            ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen,
> +                            "viewport %d", i)) {
> +         struct aubinator_reader child_reader(state->gtt, sf_clip_offset);
> +         decode_group(sf_clip_viewport, child_reader.gen(), options);
> +         ImGui::TreePop();
> +      }
> +   }
> +}
> +
> +static void
> +handle_3dstate_blend_state_pointers(struct gpu_state *state,
> +                                    const struct gen_dword_reader *reader,
> +                                    const struct decode_options *options)
> +{
> +   struct gen_group *blend_state = gen_spec_find_struct(context.spec, "BLEND_STATE");
> +   uint64_t start = state->dynamic_state_base + (gen_read_dword(reader, 1) & ~0x3fu);
> +
> +   struct aubinator_reader child_reader(state->gtt, start);
> +   decode_group(blend_state, child_reader.gen(), options);
> +}
> +
> +static void
> +handle_3dstate_cc_state_pointers(struct gpu_state *state,
> +                                 const struct gen_dword_reader *reader,
> +                                 const struct decode_options *options)
> +{
> +   struct gen_group *cc_state = gen_spec_find_struct(context.spec, "COLOR_CALC_STATE");
> +   uint64_t start = state->dynamic_state_base + (gen_read_dword(reader, 1) & ~0x3fu);
> +
> +   struct aubinator_reader child_reader(state->gtt, start);
> +   decode_group(cc_state, child_reader.gen(), options);
> +}
> +
> +static void
> +handle_3dstate_scissor_state_pointers(struct gpu_state *state,
> +                                      const struct gen_dword_reader *reader,
> +                                      const struct decode_options *options)
> +{
> +   struct gen_group *scissor_rect = gen_spec_find_struct(context.spec, "SCISSOR_RECT");
> +   uint64_t start = state->dynamic_state_base + (gen_read_dword(reader, 1) & ~0x1fu);
> +
> +   struct aubinator_reader child_reader(state->gtt, start);
> +   decode_group(scissor_rect, child_reader.gen(), options);
> +}
> +
> +// static void
> +// handle_load_register_imm(struct gen_spec *spec, uint32_t *p)
> +// {
> +//    struct gen_group *reg = gen_spec_find_register(spec, p[1]);
> +
> +//    if (reg != NULL) {
> +//       fprintf(stdout, "register %s (0x%x): 0x%x\n",
> +//               reg->name, reg->register_offset, p[2]);
> +//       decode_group(reg, &p[2]);
> +//    }
> +// }
> +
> +static void
> +handle_3dstate_sample_pattern(struct gpu_state *state,
> +                              const struct gen_dword_reader *reader,
> +                              const struct decode_options *options)
> +{
> +   if (ImGui::Button("Show pattern"))
> +      new_pattern_window(reader);
> +}
> +
> +static void
> +handle_snapshot(struct gpu_state *state,
> +                const struct gen_dword_reader *reader,
> +                const struct decode_options *options)
> +{
> +   char title[20];
> +   snprintf(title, sizeof(title), "Show GPU state %i/%i",
> +            context.gpu_state_display_idx, context.n_gpu_states);
> +   if (ImGui::Button(title))
> +      new_state_window(context.gpu_states[context.gpu_state_display_idx]);
> +   context.gpu_state_display_idx++;
> +}
> +
> +typedef void (*instruction_handler)(struct gpu_state *state,
> +                                    const struct gen_dword_reader *reader,
> +                                    const struct decode_options *options);
> +#define CB(arg) ((void*)arg)
> +
> +static struct hash_table *
> +init_accumulation_handlers(void)
> +{
> +   struct hash_table *handlers =
> +      _mesa_hash_table_create(NULL, _mesa_hash_string, _mesa_key_string_equal);
> +
> +   _mesa_hash_table_insert(handlers, "STATE_BASE_ADDRESS", CB(handle_state_base_address));
> +   _mesa_hash_table_insert(handlers, "3DPRIMITIVE", CB(snapshot_gpu_state));
> +
> +   return handlers;
> +}
> +
> +static struct hash_table *
> +init_display_handlers(void)
> +{
> +   struct hash_table *handlers =
> +      _mesa_hash_table_create(NULL, _mesa_hash_string, _mesa_key_string_equal);
> +
> +   // _mesa_hash_table_insert(handlers, "3DSTATE_VERTEX_BUFFERS", handle_3dstate_vertex_buffers);
> +   // _mesa_hash_table_insert(handlers, "3DSTATE_INDEX_BUFFERS", handle_3dstate_index_buffer);
> +   _mesa_hash_table_insert(handlers, "3DSTATE_VS", CB(handle_3dstate_vs));
> +   _mesa_hash_table_insert(handlers, "3DSTATE_GS", CB(handle_3dstate_vs));
> +   _mesa_hash_table_insert(handlers, "3DSTATE_DS", CB(handle_3dstate_vs));
> +   _mesa_hash_table_insert(handlers, "3DSTATE_HS", CB(handle_3dstate_hs));
> +   _mesa_hash_table_insert(handlers, "3DSTATE_PS", CB(handle_3dstate_ps));
> +
> +   static const char *stages[] = { "VS", "GS", "DS", "HS", "PS" };
> +   for (uint32_t i = 0; i < ARRAY_SIZE(stages); i++) {
> +      // _mesa_hash_table_insert(handlers,
> +      //                         ralloc_asprintf(handlers,
> +      //                                         "3DSTATE_CONSTANT_%s", stages[i]),
> +      //                         handle_3dstate_constant);
> +      _mesa_hash_table_insert(handlers,
> +                              ralloc_asprintf(handlers,
> +                                              "3DSTATE_BINDING_TABLE_POINTERS_%s", stages[i]),
> +                              CB(handle_3dstate_binding_table_pointers));
> +      _mesa_hash_table_insert(handlers,
> +                              ralloc_asprintf(handlers,
> +                                              "3DSTATE_SAMPLER_STATE_POINTERS_%s", stages[i]),
> +                              CB(handle_3dstate_sampler_state_pointers));
> +   }
> +   _mesa_hash_table_insert(handlers,
> +                           ralloc_strdup(handlers, "3DSTATE_SAMPLER_STATE_POINTERS"),
> +                           CB(handle_3dstate_sampler_state_pointers_gen6));
> +
> +   _mesa_hash_table_insert(handlers,
> +                           "3DSTATE_VIEWPORT_STATE_POINTERS_CC",
> +                           CB(handle_3dstate_viewport_state_pointers_cc));
> +   _mesa_hash_table_insert(handlers,
> +                           "3DSTATE_VIEWPORT_STATE_POINTERS_SF_CLIP",
> +                           CB(handle_3dstate_viewport_state_pointers_sf_clip));
> +   _mesa_hash_table_insert(handlers,
> +                           "3DSTATE_CC_STATE_POINTERS",
> +                           CB(handle_3dstate_cc_state_pointers));
> +   _mesa_hash_table_insert(handlers,
> +                           "3DSTATE_SCISSOR_STATE_POINTERS",
> +                           CB(handle_3dstate_scissor_state_pointers));
> +   _mesa_hash_table_insert(handlers,
> +                           "3DSTATE_BLEND_STATE_POINTERS",
> +                           CB(handle_3dstate_blend_state_pointers));
> +   // _mesa_hash_table_insert(handlers,
> +   //                         "MI_LOAD_REGISTER_IMM",
> +   //                         handle_load_register_imm);
> +   _mesa_hash_table_insert(handlers,
> +                           "3DPRIMITIVE",
> +                           CB(handle_snapshot));
> +
> +   _mesa_hash_table_insert(handlers,
> +                           "3DSTATE_SAMPLE_PATTERN",
> +                           CB(handle_3dstate_sample_pattern));
> +   _mesa_hash_table_insert(handlers,
> +                           "MEDIA_INTERFACE_DESCRIPTOR_LOAD",
> +                           CB(handle_media_interface_descriptor_load));
> +
> +   return handlers;
> +}
> +
> +#undef CB
> +
> +static void
> +parse_commands(struct batch_window *window,
> +               const struct gen_dword_reader *reader,
> +               uint32_t n_dwords, bool from_ring)
> +{
> +   uint32_t dw_length;
> +
> +   for (uint32_t dw_offset = 0; dw_offset < n_dwords; dw_offset += dw_length) {
> +      uint32_t dw0 = gen_read_dword(reader, dw_offset);
> +      struct gen_group *inst = gen_spec_find_instruction(context.spec, dw0);
> +      dw_length = gen_group_get_length(inst, dw0);
> +      assert(inst == NULL || dw_length > 0);
> +      dw_length = MAX(1, dw_length);
> +      if (inst == NULL) {
> +         ImGui::TextColored(context.error_color,
> +                            "unknown instruction %08x", dw0);
> +         continue;
> +      }
> +
> +      struct gpu_state *state = gpu_state_last();
> +      uint64_t offset = reader_offset(reader) + 4 * dw_offset;
> +      struct aubinator_reader child_reader(reader, dw_offset);
> +
> +      if (window) {
> +         if (window->cmd_filter.PassFilter(inst->name) &&
> +             ImGui::TreeNodeEx((void *) offset,
> +                               ((window->collapsed ? 0 : ImGuiTreeNodeFlags_DefaultOpen) |
> +                                ImGuiTreeNodeFlags_Framed),
> +                               "0x%08" PRIx64 ":  %s",
> +                               offset, inst->name)) {
> +
> +            decode_group(inst, child_reader.gen(), &window->options);
> +
> +            struct hash_entry *entry =
> +               _mesa_hash_table_search(context.display_handlers, inst->name);
> +            if (entry) {
> +               instruction_handler ui_handler = (instruction_handler) entry->data;
> +               ui_handler(state, child_reader.gen(), &window->options);
> +            }
> +
> +            ImGui::TreePop();
> +         }
> +      } else {
> +         /* Only go through accumulation handlers when not rendering
> +          * (!window).
> +          */
> +         if ((dw0 & 0xffff0000) != AUB_MI_BATCH_BUFFER_START)
> +            gpu_state_add(state, state->gtt, offset, inst);
> +
> +         struct hash_entry *entry =
> +            _mesa_hash_table_search(context.accumulation_handlers, inst->name);
> +         if (entry) {
> +            instruction_handler state_handler = (instruction_handler) entry->data;
> +            state_handler(state, child_reader.gen(), NULL);
> +         }
> +      }
> +
> +      if ((dw0 & 0xffff0000) == AUB_MI_BATCH_BUFFER_START) {
> +         uint64_t start = read_address(reader, 1);
> +
> +         if (dw0 & (1 << 22)) {
> +            /* MI_BATCH_BUFFER_START with "2nd Level Batch Buffer" set acts
> +             * like a subroutine call.  Commands that come afterwards get
> +             * processed once the 2nd level batch buffer returns with
> +             * MI_BATCH_BUFFER_END.
> +             */
> +            struct aubinator_reader child_reader(state->gtt, start);
> +            parse_commands(window, child_reader.gen(),
> +                           (memory_end(state->gtt) - start) / 4, false);
> +         } else {
> +            /* MI_BATCH_BUFFER_START with "2nd Level Batch Buffer" unset acts
> +             * like a goto. Nothing after it will ever get processed. Just
> +             * break out of this loop.
> +             */
> +            struct aubinator_reader child_reader(state->gtt, start);
> +            parse_commands(window, child_reader.gen(),
> +                           (memory_end(state->gtt) - start) / 4, false);
> +            break;
> +         }
> +      } else if ((dw0 & 0xffff0000) == AUB_MI_BATCH_BUFFER_END) {
> +         break;
> +      }
> +   }
> +}
> +
> +#define GEN_ENGINE_RENDER 1
> +#define GEN_ENGINE_BLITTER 2
> +
> +static uint32_t
> +frame_read_block_type(uint32_t *p)
> +{
> +   return p[1] & AUB_TRACE_OPERATION_MASK;
> +}
> +
> +static void
> +handle_trace_block_data_write(uint32_t *p)
> +{
> +   UNUSED int operation = frame_read_block_type(p);
> +   UNUSED int type = p[1] & AUB_TRACE_TYPE_MASK;
> +   int address_space = p[1] & AUB_TRACE_ADDRESS_SPACE_MASK;
> +   uint64_t offset = p[3];
> +   uint32_t size = p[4];
> +   int header_length = p[0] & 0xffff;
> +   uint32_t *data = p + header_length + 2;
> +   UNUSED int engine = GEN_ENGINE_RENDER;
> +
> +   if (gen_spec_get_gen(context.spec) >= gen_make_gen(8,0))
> +      offset += (uint64_t) p[5] << 32;
> +
> +   assert(operation == AUB_TRACE_OP_DATA_WRITE);
> +
> +   if (address_space != AUB_TRACE_MEMTYPE_GTT)
> +      return;
> +
> +   struct gpu_state *state = gpu_state_last();
> +   state->gtt = memory_write_unref(state->gtt, offset, data, size);
> +}
> +
> +static void
> +handle_trace_block_command_write(uint32_t *p, struct batch_window *window)
> +{
> +   UNUSED int operation = frame_read_block_type(p);
> +   int type = p[1] & AUB_TRACE_TYPE_MASK;
> +   UNUSED int address_space = p[1] & AUB_TRACE_ADDRESS_SPACE_MASK;
> +   uint64_t offset = p[3];
> +   uint32_t size = p[4];
> +   int header_length = p[0] & 0xffff;
> +   uint32_t *data = p + header_length + 2;
> +
> +   if (gen_spec_get_gen(context.spec) >= gen_make_gen(8,0))
> +      offset += (uint64_t) p[5] << 32;
> +
> +   assert(operation == AUB_TRACE_OP_COMMAND_WRITE);
> +
> +   if (type != AUB_TRACE_TYPE_RING_PRB0 &&
> +       type != AUB_TRACE_TYPE_RING_PRB2) {
> +      if (window) {
> +         ImGui::TextColored(context.error_color,
> +                            "command write to unknown ring %d\n", type);
> +      }
> +   }
> +
> +   /* When no window, record the states */
> +   if (!window) {
> +      if (context.n_gpu_states > 0) {
> +         for (uint32_t s = 0; s < (context.n_gpu_states - 1); s++)
> +            gpu_state_free(context.gpu_states[s]);
> +         context.gpu_states[0] = gpu_state_last();
> +      } else
> +         context.gpu_states[0] = gpu_state_new();
> +      context.n_gpu_states = 1;
> +   }
> +
> +   context.gpu_state_display_idx = 0;
> +
> +   struct aubinator_reader reader(data);
> +   parse_commands(window, reader.gen(), size, true);
> +   //context.gtt_end = 0;
> +}
> +
> +static void
> +handle_trace_header(uint32_t *p)
> +{
> +   /* The intel_aubdump tool from IGT is kind enough to put a PCI-ID= tag in
> +    * the AUB header comment.  If the user hasn't specified a hardware
> +    * generation, try to use the one from the AUB file.
> +    */
> +   uint32_t *end = p + (p[0] & 0xffff) + 2;
> +   int aub_pci_id = 0;
> +
> +   if (context.pci_id == 0) {
> +      if (end > &p[12] && p[12] > 0)
> +         sscanf((char *)&p[13], "PCI-ID=%i", &aub_pci_id);
> +      context.pci_id = aub_pci_id;
> +   }
> +   context.read_pci_id = aub_pci_id != 0;
> +
> +   if (!gen_get_device_info(context.pci_id, &context.devinfo)) {
> +      fprintf(stderr, "can't find device information: pci_id=0x%x\n", context.pci_id);
> +      exit(EXIT_FAILURE);
> +   }
> +
> +   if (context.xml_path == NULL)
> +      context.spec = gen_spec_load(&context.devinfo);
> +   else
> +      context.spec = gen_spec_load_from_path(&context.devinfo, context.xml_path);
> +   context.disasm = gen_disasm_create(context.pci_id);
> +
> +   if (context.spec == NULL || context.disasm == NULL)
> +      exit(EXIT_FAILURE);
> +
> +   strncpy(context.app_name, (char *)&p[2], 32);
> +   context.app_name[32] = 0;
> +}
> +
> +struct aub_file {
> +   FILE *stream;
> +
> +   uint32_t *map, *end, *cursor;
> +   uint32_t *mem_end;
> +};
> +
> +static struct aub_file *
> +aub_file_open(const char *filename)
> +{
> +   struct aub_file *file;
> +   struct stat sb;
> +   int fd;
> +
> +   file = xtzalloc(struct aub_file);
> +   fd = open(filename, O_RDONLY);
> +   if (fd == -1) {
> +      fprintf(stderr, "open %s failed: %s\n", filename, strerror(errno));
> +      exit(EXIT_FAILURE);
> +   }
> +
> +   if (fstat(fd, &sb) == -1) {
> +      fprintf(stderr, "stat failed: %s\n", strerror(errno));
> +      exit(EXIT_FAILURE);
> +   }
> +
> +   file->map = (uint32_t *) mmap(NULL, sb.st_size,
> +                                 PROT_READ, MAP_SHARED, fd, 0);
> +   if (file->map == MAP_FAILED) {
> +      fprintf(stderr, "mmap failed: %s\n", strerror(errno));
> +      exit(EXIT_FAILURE);
> +   }
> +
> +   close(fd);
> +
> +   file->cursor = file->map;
> +   file->end = file->map + sb.st_size / 4;
> +
> +   return file;
> +}
> +
> +static struct aub_file *
> +aub_file_stdin(void)
> +{
> +   struct aub_file *file;
> +
> +   file = xtzalloc(struct aub_file);
> +   file->stream = stdin;
> +
> +   return file;
> +}
> +
> +#define TYPE(dw)       (((dw) >> 29) & 7)
> +#define OPCODE(dw)     (((dw) >> 23) & 0x3f)
> +#define SUBOPCODE(dw)  (((dw) >> 16) & 0x7f)
> +
> +#define MAKE_HEADER(type, opcode, subopcode) \
> +                   (((type) << 29) | ((opcode) << 23) | ((subopcode) << 16))
> +
> +#define TYPE_AUB            0x7U
> +
> +/* Classic AUB opcodes */
> +#define OPCODE_AUB          0x01U
> +#define SUBOPCODE_HEADER    0x05U
> +#define SUBOPCODE_BLOCK     0x41U
> +#define SUBOPCODE_BMP       0x1eU
> +
> +/* Newer version AUB opcode */
> +#define OPCODE_NEW_AUB      0x2eU
> +#define SUBOPCODE_VERSION   0x00U
> +#define SUBOPCODE_REG_WRITE 0x03U
> +#define SUBOPCODE_MEM_POLL  0x05U
> +#define SUBOPCODE_MEM_WRITE 0x06U
> +
> +#define MAKE_GEN(major, minor) ( ((major) << 8) | (minor) )
> +
> +enum {
> +   AUB_ITEM_DECODE_OK,
> +   AUB_ITEM_DECODE_FAILED,
> +   AUB_ITEM_DECODE_NEED_MORE_DATA,
> +};
> +
> +static int
> +aub_file_next_frame(struct aub_file *file,
> +                    uint32_t *frame_type,
> +                    uint32_t **output_cursor)
> +{
> +   uint32_t h, device, data_type, *new_cursor;
> +   int header_length, bias;
> +
> +   if (file->end - file->cursor < 1)
> +      return AUB_ITEM_DECODE_NEED_MORE_DATA;
> +
> +   h = *file->cursor;
> +   header_length = h & 0xffff;
> +
> +   switch (OPCODE(h)) {
> +   case OPCODE_AUB:
> +      bias = 2;
> +      break;
> +   case OPCODE_NEW_AUB:
> +      bias = 1;
> +      break;
> +   default:
> +      ImGui::Text("unknown opcode %d at %td/%td\n",
> +           OPCODE(h), file->cursor - file->map,
> +           file->end - file->map);
> +      return AUB_ITEM_DECODE_FAILED;
> +   }
> +
> +   new_cursor = file->cursor + header_length + bias;
> +   if ((h & 0xffff0000) == MAKE_HEADER(TYPE_AUB, OPCODE_AUB, SUBOPCODE_BLOCK)) {
> +      if (file->end - file->cursor < 4)
> +         return AUB_ITEM_DECODE_NEED_MORE_DATA;
> +      new_cursor += file->cursor[4] / 4;
> +   }
> +
> +   if (new_cursor > file->end)
> +      return AUB_ITEM_DECODE_NEED_MORE_DATA;
> +
> +   if (frame_type)
> +      *frame_type = h & 0xffff0000;
> +   if (output_cursor)
> +      *output_cursor = new_cursor;
> +
> +   switch (h & 0xffff0000) {
> +   case MAKE_HEADER(TYPE_AUB, OPCODE_AUB, SUBOPCODE_HEADER):
> +      //handle_trace_header(file->cursor);
> +      break;
> +   case MAKE_HEADER(TYPE_AUB, OPCODE_AUB, SUBOPCODE_BLOCK):
> +      //handle_trace_block(file->cursor);
> +      break;
> +   case MAKE_HEADER(TYPE_AUB, OPCODE_AUB, SUBOPCODE_BMP):
> +      break;
> +   case MAKE_HEADER(TYPE_AUB, OPCODE_NEW_AUB, SUBOPCODE_VERSION): {
> +      fprintf(stdout, "version block: dw1 %08x\n", file->cursor[1]);
> +      device = (file->cursor[1] >> 8) & 0xff;
> +      const char *device_map[] = {
> +         "bwr",
> +         "cln",
> +         "blc",
> +         "ctg",
> +         "el",
> +         "il",
> +         "sbr",
> +         "ivb",
> +         "lrb2",
> +         "hsw",
> +         "vlv",
> +         "bdw",
> +         "skl",
> +         "chv",
> +         "bxt",
> +         "cnl",
> +      };
> +      fprintf(stdout, "  device %s\n", device_map[device]);
> +      break;
> +   }
> +   case MAKE_HEADER(TYPE_AUB, OPCODE_NEW_AUB, SUBOPCODE_REG_WRITE):
> +      fprintf(stdout, "register write block: (dwords %d)\n", h & 0xffff);
> +      fprintf(stdout, "  reg 0x%x, data 0x%x\n",
> +              file->cursor[1], file->cursor[5]);
> +      break;
> +   case MAKE_HEADER(TYPE_AUB, OPCODE_NEW_AUB, SUBOPCODE_MEM_WRITE):
> +      fprintf(stdout, "memory write block (dwords %d):\n", h & 0xffff);
> +      fprintf(stdout, "  address 0x%" PRIx64 "\n", *(uint64_t *) &file->cursor[1]);
> +      data_type = (file->cursor[3] >> 20) & 0xff;
> +      if (data_type != 0)
> +         fprintf(stdout, "  data type 0x%x\n", data_type);
> +      fprintf(stdout, "  address space 0x%x\n", (file->cursor[3] >> 28) & 0xf);
> +      break;
> +   case MAKE_HEADER(TYPE_AUB, OPCODE_NEW_AUB, SUBOPCODE_MEM_POLL):
> +      fprintf(stdout, "memory poll block (dwords %d):\n", h & 0xffff);
> +      break;
> +   default:
> +      fprintf(stdout, "unknown block type=0x%x, opcode=0x%x, "
> +             "subopcode=0x%x (%08x)\n", TYPE(h), OPCODE(h), SUBOPCODE(h), h);
> +      break;
> +   }
> +
> +   return AUB_ITEM_DECODE_OK;
> +}
> +
> +static int
> +aub_file_more_stuff(struct aub_file *file)
> +{
> +   return file->cursor < file->end || (file->stream && !feof(file->stream));
> +}
> +
> +#define AUB_READ_BUFFER_SIZE (4096)
> +
> +static void
> +aub_file_data_grow(struct aub_file *file)
> +{
> +   size_t old_size = (file->mem_end - file->map) * 4;
> +   size_t new_size = MAX(old_size * 2, AUB_READ_BUFFER_SIZE);
> +   uint32_t *new_start = (uint32_t *) realloc(file->map, new_size);
> +
> +   file->cursor = new_start + (file->cursor - file->map);
> +   file->end = new_start + (file->end - file->map);
> +   file->map = new_start;
> +   file->mem_end = file->map + (new_size / 4);
> +}
> +
> +static bool
> +aub_file_data_load(struct aub_file *file)
> +{
> +   size_t r;
> +
> +   if (file->stream == NULL)
> +      return false;
> +
> +   /* First remove any consumed data */
> +   if (file->cursor > file->map) {
> +      memmove(file->map, file->cursor,
> +              (file->end - file->cursor) * 4);
> +      file->end -= file->cursor - file->map;
> +      file->cursor = file->map;
> +   }
> +
> +   /* Then load some new data in */
> +   if ((file->mem_end - file->end) < (AUB_READ_BUFFER_SIZE / 4))
> +      aub_file_data_grow(file);
> +
> +   r = fread(file->end, 1, (file->mem_end - file->end) * 4, file->stream);
> +   file->end += r / 4;
> +
> +   return r != 0;
> +}
> +
> +static void
> +aub_file_reset(struct aub_file *file)
> +{
> +   file->cursor = file->map;
> +}
> +
> +static void
> +aub_file_read_headers(void)
> +{
> +   bool found = false;
> +
> +   list_for_each_entry(struct window, window, &context.windows, windows) {
> +      if (window->reset) window->reset(window);
> +   }
> +
> +   if (context.disasm) {
> +      gen_disasm_destroy(context.disasm);
> +      context.disasm = NULL;
> +   }
> +   if (context.spec) {
> +      gen_spec_destroy(context.spec);
> +      context.spec = NULL;
> +   }
> +
> +   aub_file_reset(context.file);
> +   while (aub_file_more_stuff(context.file) && !found) {
> +      uint32_t *new_cursor;
> +      uint32_t frame_type;
> +
> +      switch (aub_file_next_frame(context.file, &frame_type, &new_cursor)) {
> +      case AUB_ITEM_DECODE_OK:
> +         found = MAKE_HEADER(TYPE_AUB, OPCODE_AUB, SUBOPCODE_HEADER) == frame_type;
> +         if (found)
> +            handle_trace_header(context.file->cursor);
> +         context.file->cursor = new_cursor;
> +         break;
> +      case AUB_ITEM_DECODE_NEED_MORE_DATA:
> +         if (!context.file->stream) {
> +            context.file->cursor = context.file->end;
> +            break;
> +         }
> +         if (aub_file_more_stuff(context.file) && !aub_file_data_load(context.file)) {
> +            fprintf(stderr, "failed to load data from stdin\n");
> +            exit(EXIT_FAILURE);
> +         }
> +         break;
> +      default:
> +         fprintf(stderr, "failed to parse aubdump data\n");
> +         exit(EXIT_FAILURE);
> +      }
> +   }
> +}
> +
> +static void
> +aub_file_read_block(uint32_t new_index)
> +{
> +   int32_t index = -1;
> +
> +   context.command_index = new_index;
> +
> +   aub_file_reset(context.file);
> +   while (aub_file_more_stuff(context.file) && index < context.command_index) {
> +      uint32_t *new_cursor;
> +      uint32_t frame_type;
> +
> +      switch (aub_file_next_frame(context.file, &frame_type, &new_cursor)) {
> +      case AUB_ITEM_DECODE_OK:
> +         if (frame_type == MAKE_HEADER(TYPE_AUB, OPCODE_AUB, SUBOPCODE_BLOCK)) {
> +            switch (frame_read_block_type(context.file->cursor)) {
> +            case AUB_TRACE_OP_DATA_WRITE:
> +               handle_trace_block_data_write(context.file->cursor);
> +               break;
> +            case AUB_TRACE_OP_COMMAND_WRITE:
> +               handle_trace_block_command_write(context.file->cursor, NULL);
> +               context.command_block = context.file->cursor;
> +               index++;
> +               break;
> +            }
> +         }
> +         context.file->cursor = new_cursor;
> +         break;
> +      case AUB_ITEM_DECODE_NEED_MORE_DATA:
> +         if (!context.file->stream) {
> +            context.file->cursor = context.file->end;
> +            break;
> +         }
> +         if (aub_file_more_stuff(context.file) && !aub_file_data_load(context.file)) {
> +            fprintf(stderr, "failed to load data from stdin\n");
> +            exit(EXIT_FAILURE);
> +         }
> +         break;
> +      default:
> +         fprintf(stderr, "failed to parse aubdump data\n");
> +         exit(EXIT_FAILURE);
> +      }
> +   }
> +}
> +
> +/**/
> +
> +static bool
> +select_offset_base(struct offset_at_base *offset)
> +{
> +   static const uint64_t zero = 0;
> +   struct gpu_state *state = gpu_state_last();
> +   const struct {
> +      const char *name;
> +      const uint64_t *base;
> +   } items[] = {
> +      { "zero",        &zero },
> +      { "general",     &state->general_state_base },
> +      { "surface",     &state->surface_state_base },
> +      { "dynamic",     &state->dynamic_state_base },
> +      { "instruction", &state->instruction_base },
> +   };
> +   bool selected = false;
> +
> +   for (uint32_t i = 0; i < ARRAY_SIZE(items); i++) {
> +      if (ImGui::Selectable(items[i].name)) {
> +         offset->base = items[i].base;
> +         offset->base_name = items[i].name;
> +         selected = true;
> +      }
> +   }
> +   return selected;
> +}
> +
> +static bool
> +offset_is_valid(struct offset_at_base *offset, memory_t *mem)
> +{
> +   return gtt_valid_offset(mem, *offset->base + offset->offset);
> +}
> +
> +// static bool
> +// offset_is_valid_range(struct offset_at_base *offset, uint32_t size,
> +//                       memory_t *mem)
> +// {
> +//    return gtt_valid_range(mem, *offset->base + offset->offset, size);
> +// }
> +
> +static uint64_t
> +offset_get_absolute(struct offset_at_base *offset)
> +{
> +   return *offset->base + offset->offset;
> +}
> +
> +static uint32_t
> +offset_read_dword(struct offset_at_base *offset,
> +                  uint32_t dword_offset,
> +                  memory_t *mem)
> +{
> +   return memory_read_dword(mem, offset_get_absolute(offset) + 4 * dword_offset);
> +}
> +
> +static bool
> +display_offset(struct offset_at_base *offset)
> +{
> +   bool changed = false;
> +
> +   ImGui::Text("From:"); ImGui::SameLine();
> +   bool open_popup = ImGui::Button(offset->base_name); ImGui::SameLine();
> +   ImGui::Text("0x%08" PRIx64, *offset->base); ImGui::SameLine();
> +   if (open_popup)
> +      ImGui::OpenPopup("base picker");
> +   if (ImGui::BeginPopup("base picker")) {
> +      changed = select_offset_base(offset);
> +      ImGui::EndPopup();
> +   }
> +
> +   ImGui::Text(" @ "); ImGui::SameLine();
> +   ImGui::PushItemWidth(100);
> +   snprintf(offset->address, sizeof(offset->address),
> +            "%" PRIx64, offset->offset);
> +   if (ImGui::InputText("", offset->address, sizeof(offset->address),
> +                        ImGuiInputTextFlags_CharsHexadecimal)) {
> +      sscanf(offset->address, "%" PRIx64, &offset->offset);
> +      changed = true;
> +   }
> +   ImGui::PopItemWidth();
> +
> +   return changed;
> +}
> +
> +/**/
> +
> +static void
> +display_file_props(void)
> +{
> +   if (context.input_file)
> +      ImGui::Text("File name:        %s\n", context.input_file);
> +   ImGui::Text("PCI ID:           0x%x\n", context.pci_id);
> +   ImGui::Text("Application name: %s\n", context.app_name);
> +
> +   ImGui::Text("Decoding as:"); ImGui::SameLine();
> +   if (ImGui::Button("Select..")) { ImGui::OpenPopup("select"); } ImGui::SameLine();
> +   if (ImGui::BeginPopup("select")) {
> +      for (uint32_t i = 0; i < ARRAY_SIZE(supported_gens); i++) {
> +         if (ImGui::Selectable(supported_gens[i].name)) {
> +            context.pci_id = supported_gens[i].pci_id;
> +            aub_file_read_headers();
> +         }
> +      }
> +      if (ImGui::Selectable("Reset")) {
> +         context.pci_id = 0;
> +         aub_file_read_headers();
> +      }
> +      ImGui::EndPopup();
> +   }
> +   ImGui::SameLine();
> +   ImGui::Text(gen_get_device_name(context.pci_id));
> +}
> +
> +/**/
> +
> +static void display_decode_options(struct decode_options *options)
> +{
> +   char name[40];
> +   snprintf(name, sizeof(name), "field filter##%p", options);
> +   options->field_filter.Draw(name); ImGui::SameLine();
> +   if (ImGui::Button("Dwords")) options->show_dwords ^= 1;
> +}
> +
> +/* Batch decoding windows */
> +
> +static void
> +display_batch_window(struct window *win)
> +{
> +   struct batch_window *window = (struct batch_window *) win;
> +
> +   ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth() / (2 * 2));
> +   if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();
> +   char name[40];
> +   snprintf(name, sizeof(name), "command filter##%p", window);
> +   window->cmd_filter.Draw(name); ImGui::SameLine();
> +   display_decode_options(&window->options);
> +   ImGui::PopItemWidth();
> +
> +   if (ImGui::Button("Collapse columns")) { window->collapsed ^= 1; } ImGui::SameLine();
> +   if (window->ring_buffer == NULL) ImGui::Text("Batch buffer %i", context.command_index);
> +   ImGui::BeginChild(ImGui::GetID("##block"));
> +   uint32_t *commands =
> +      window->ring_buffer ? window->ring_buffer : context.command_block;
> +   handle_trace_block_command_write(commands, window);
> +   ImGui::EndChild();
> +}
> +
> +static void
> +destroy_batch_window(struct window *win)
> +{
> +   struct batch_window *window = (struct batch_window *) win;
> +
> +   free(window);
> +}
> +
> +static void
> +new_batch_window(bool snapshot)
> +{
> +   struct batch_window *window = xtzalloc(struct batch_window);
> +
> +   if (snapshot) {
> +      window->ring_buffer = context.command_block;
> +      /* TODO copy gpu state */
> +      snprintf(window->base.name, sizeof(window->base.name),
> +               "Batch view (batch %u)##%p", context.command_index, window);
> +   } else {
> +      snprintf(window->base.name, sizeof(window->base.name),
> +               "Batch view (live)##%p", window);
> +   }
> +   window->base.position = ImVec2(-1, -1);
> +   window->base.size = ImVec2(500, 600);
> +   window->base.opened = true;
> +   window->base.display = display_batch_window;
> +   window->base.destroy = destroy_batch_window;
> +
> +   window->collapsed = true;
> +   window->options.show_dwords = true;
> +
> +   list_add(&window->base.windows, &context.windows);
> +}
> +
> +/* Decode as windows */
> +
> +static void
> +select_structure(gen_group **group)
> +{
> +   static ImGuiTextFilter filter;
> +   filter.Draw();
> +
> +   struct hash_entry *entry;
> +   hash_table_foreach(context.spec->commands, entry) {
> +      struct gen_group *cmd = (struct gen_group *) entry->data;
> +      if (filter.PassFilter(cmd->name) &&
> +          ImGui::Selectable(cmd->name)) {
> +         *group = cmd;
> +      }
> +   }
> +   hash_table_foreach(context.spec->registers_by_name, entry) {
> +      struct gen_group *reg = (struct gen_group *) entry->data;
> +      if (filter.PassFilter(reg->name) &&
> +          ImGui::Selectable(reg->name)) {
> +         *group = reg;
> +      }
> +   }
> +   hash_table_foreach(context.spec->structs, entry) {
> +      struct gen_group *strct = (struct gen_group *) entry->data;
> +      if (filter.PassFilter(strct->name) &&
> +          ImGui::Selectable(strct->name)) {
> +         *group = strct;
> +      }
> +   }
> +}
> +
> +static void
> +display_as_window(struct window *win)
> +{
> +   struct as_window *window = (struct as_window *) win;
> +
> +   display_offset(&window->offset);
> +
> +   bool open_popup = ImGui::Button(window->as ? window->as->name : "None");
> +   if (open_popup)
> +      ImGui::OpenPopup("struct picker");
> +   if (ImGui::BeginPopup("struct picker")) {
> +      select_structure(&window->as);
> +      ImGui::EndPopup();
> +   }
> +   ImGui::SameLine();
> +
> +   if (window->as) {
> +      if (ImGui::Button("Previous"))
> +         window->offset.offset -= window->as->dw_length * 4;
> +      ImGui::SameLine();
> +      if (ImGui::Button("Next"))
> +         window->offset.offset += window->as->dw_length * 4;
> +
> +      display_decode_options(&window->options);
> +   }
> +
> +   ImGui::BeginChild(ImGui::GetID("##block"));
> +   if (!offset_is_valid(&window->offset, window->state->gtt))
> +       ImGui::TextColored(context.error_color, "outside of GTT");
> +   else if (window->as) {
> +      bool all_zeros = true;
> +      for (uint32_t dw = 0; dw < window->as->dw_length; dw++) {
> +         all_zeros = all_zeros &&
> +            offset_read_dword(&window->offset, dw, window->state->gtt) == 0;
> +      }
> +      if (all_zeros) ImGui::TextColored(context.error_color, "All dwords at 0");
> +
> +      struct aubinator_reader reader(window->state->gtt, offset_get_absolute(&window->offset));
> +      decode_group(window->as, reader.gen(), &window->options);
> +   }
> +   ImGui::EndChild();
> +}
> +
> +static void
> +destroy_as_window(struct window *win)
> +{
> +   struct as_window *window = (struct as_window *) win;
> +
> +   gpu_state_free(window->state);
> +   free(window);
> +}
> +
> +static void
> +reset_as_window(struct window *win)
> +{
> +   struct as_window *window = (struct as_window *) win;
> +
> +   window->as = NULL;
> +}
> +
> +static void
> +new_as_window(struct gpu_state *state, uint64_t offset)
> +{
> +   struct as_window *window = xtzalloc(struct as_window);
> +
> +   snprintf(window->base.name, sizeof(window->base.name),
> +            "Decode view##%p", window);
> +   window->base.position = ImVec2(-1, -1);
> +   window->base.size = ImVec2(400, 400);
> +   window->base.opened = true;
> +   window->base.display = display_as_window;
> +   window->base.destroy = destroy_as_window;
> +   window->base.reset = reset_as_window;
> +
> +   window->state = gpu_state_copy(state);
> +   window->offset.base = &state->general_state_base;
> +   window->offset.base_name = "general";
> +   window->offset.offset = offset;
> +   window->options.field_filter = ImGuiTextFilter();
> +
> +   list_add(&window->base.windows, &context.windows);
> +}
> +
> +/* Image decode windows */
> +
> +static void
> +update_image_window(struct image_window *window)
> +{
> +   if (!gtt_valid_range(window->state->gtt,
> +                        window->base_address,
> +                        window->plane_stride) ||
> +       !window->width || !window->height)
> +      return;
> +
> +   static const struct {
> +      CoglPixelFormat format;
> +      const char *shader;
> +   } formats[] = {
> +      { COGL_PIXEL_FORMAT_A_8,
> +        "cogl_layer = vec4(cogl_texel0.a, 0.0, 0.0, 1.0);\n", },
> +      { COGL_PIXEL_FORMAT_RG_88,
> +        "cogl_layer = vec4(cogl_texel0.r,\n"
> +        "                  cogl_texel0.g,\n"
> +        "                  0.0, 1.0);\n", },
> +      { COGL_PIXEL_FORMAT_RGB_888,
> +        "cogl_layer = vec4(cogl_texel0.r,\n"
> +        "                  cogl_texel0.g,\n"
> +        "                  cogl_texel0.b,\n"
> +        "                  1.0);\n", },
> +      { COGL_PIXEL_FORMAT_RGBA_8888,
> +        "cogl_layer = vec4(cogl_texel0.r,\n"
> +        "                  cogl_texel0.g,\n"
> +        "                  cogl_texel0.b,\n"
> +        "                  1.0);\n", },
> +   };
> +   uint32_t channels =
> +      isl_format_get_num_channels((enum isl_format) window->format);
> +   if (channels < 1 || channels > 4) return;
> +
> +   uint8_t *data = (uint8_t *) malloc(window->plane_stride);
> +   memory_read(window->state->gtt, window->base_address, data, window->plane_stride);
> +   CoglTexture *texture =
> +      COGL_TEXTURE(cogl_texture_2d_new_from_data(ImGui_ImplGtk3Cogl_GetContext(),
> +                                                 window->width,
> +                                                 window->height,
> +                                                 formats[channels - 1].format,
> +                                                 window->stride,
> +                                                 (uint8_t *) data,
> +                                                 NULL));
> +   free(data);
> +   if (!texture) return;
> +
> +   strncpy(window->shader, formats[channels - 1].shader, sizeof(window->shader));
> +
> +   CoglPipeline *pipeline = cogl_pipeline_new(ImGui_ImplGtk3Cogl_GetContext());
> +   cogl_pipeline_set_layer_texture(pipeline, 0, texture);
> +   cogl_pipeline_set_layer_filters(pipeline, 0,
> +                                   COGL_PIPELINE_FILTER_NEAREST,
> +                                   COGL_PIPELINE_FILTER_NEAREST);
> +   cogl_object_unref(texture);
> +
> +   // "cogl_position_in * vec2(%i, %i)\n"
> +
> +   cogl_pipeline_add_layer_snippet(pipeline, 0,
> +                                   cogl_snippet_new(COGL_SNIPPET_HOOK_LAYER_FRAGMENT,
> +                                                    "",
> +                                                    window->shader));
> +
> +   if (window->pipeline) cogl_object_unref(window->pipeline);
> +   window->pipeline = pipeline;
> +}
> +
> +static bool
> +select_image_format(enum isl_format *format)
> +{
> +   bool selected = false;
> +   static ImGuiTextFilter filter;
> +   filter.Draw();
> +
> +   for (uint32_t f = 0; f < ISL_FORMAT_RAW; f++) {
> +      const char *name = isl_format_get_name((enum isl_format) f);
> +      if (!name || !filter.PassFilter(name))
> +         continue;
> +      if (ImGui::Selectable(name)) {
> +         *format = (enum isl_format) f;
> +         selected = true;
> +      }
> +   }
> +
> +   return selected;
> +}
> +
> +static void
> +display_image_window(struct window *win)
> +{
> +   struct image_window *window = (struct image_window *) win;
> +
> +   if (display_offset(&window->offset)) update_image_window(window);
> +
> +   bool open_popup = ImGui::Button(isl_format_get_name(window->format));
> +   if (open_popup)
> +      ImGui::OpenPopup("format picker");
> +   if (ImGui::BeginPopup("format picker")) {
> +      if (select_image_format(&window->format))
> +         update_image_window(window);
> +      ImGui::EndPopup();
> +   }
> +   ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth() / 2);
> +   if (ImGui::InputInt("Width", &window->width)) update_image_window(window);
> +   if (ImGui::InputInt("Height", &window->height)) update_image_window(window);
> +   if (ImGui::InputInt("Row stride", &window->stride)) update_image_window(window);
> +
> +   snprintf(window->base_address_buf, sizeof(window->base_address_buf),
> +            "%" PRIx64, window->base_address);
> +   if (ImGui::InputText("Surface base address", window->base_address_buf,
> +                        sizeof(window->base_address_buf),
> +                        ImGuiInputTextFlags_CharsHexadecimal)) {
> +      sscanf(window->base_address_buf, "%" PRIx64, &window->base_address);
> +      update_image_window(window);
> +   }
> +
> +   ImGui::PopItemWidth();
> +
> +   ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth());
> +   if (ImGui::InputTextMultiline("##shader",
> +                                 window->shader, ARRAY_SIZE(window->shader),
> +                                 ImVec2(-1.0f, 0), 0)) {
> +      g_message("update shader");
> +      update_image_window(window);
> +   }
> +
> +   if (window->pipeline) {
> +      ImGui::Image(window->pipeline,
> +                   ImVec2(ImGui::GetContentRegionAvailWidth(),
> +                          window->height * ImGui::GetContentRegionAvailWidth() / window->width));
> +      // if (ImGui::IsItemHovered()) {
> +      //    ImGui::BeginTooltip();
> +      //    ImVec2 tex_screen_pos = ImGui::GetCursorScreenPos();
> +      //    CoglTexture *texture = cogl_pipeline_get_layer_texture(window->pipeline, 0);
> +      //    int tex_w = cogl_texture_get_width(texture), tex_h = cogl_texture_get_height(texture);
> +      //    float focus_sz = 32.0f;
> +      //    float focus_x = ImGui::GetMousePos().x - tex_screen_pos.x - focus_sz * 0.5f; if (focus_x < 0.0f) focus_x = 0.0f; else if (focus_x > tex_w - focus_sz) focus_x = tex_w - focus_sz;
> +      //    float focus_y = ImGui::GetMousePos().y - tex_screen_pos.y - focus_sz * 0.5f; if (focus_y < 0.0f) focus_y = 0.0f; else if (focus_y > tex_h - focus_sz) focus_y = tex_h - focus_sz;
> +      //    ImGui::Text("Min: (%.2f, %.2f)", focus_x, focus_y);
> +      //    ImGui::Text("Max: (%.2f, %.2f)", focus_x + focus_sz, focus_y + focus_sz);
> +      //    ImVec2 uv0 = ImVec2((focus_x) / tex_w, (focus_y) / tex_h);
> +      //    ImVec2 uv1 = ImVec2((focus_x + focus_sz) / tex_w, (focus_y + focus_sz) / tex_h);
> +      //    ImGui::Image(window->pipeline, ImVec2(128,128), uv0, uv1, ImColor(255,255,255,255), ImColor(255,255,255,128));
> +      //    ImGui::EndTooltip();
> +      // }
> +   }
> +
> +   if (!offset_is_valid(&window->offset, window->state->gtt))
> +      ImGui::TextColored(context.error_color, "Invalid offset in GTT");
> +   ImGui::PopItemWidth();
> +}
> +
> +static void
> +destroy_image_window(struct window *win)
> +{
> +   struct image_window *window = (struct image_window *) win;
> +
> +   if (window->pipeline) cogl_object_unref(window->pipeline);
> +   gpu_state_free(window->state);
> +   free(window);
> +}
> +
> +static void
> +new_image_window(struct gpu_state *state, uint64_t offset)
> +{
> +   struct image_window *window = xtzalloc(struct image_window);
> +
> +   snprintf(window->base.name, sizeof(window->base.name),
> +            "Image view##%p", window);
> +   window->base.opened = true;
> +   window->base.display = display_image_window;
> +   window->base.destroy = destroy_image_window;
> +
> +   window->state = gpu_state_copy(state);
> +   window->offset.base = &state->surface_state_base;
> +   window->offset.base_name = "surface";
> +   window->offset.offset = offset;
> +
> +   window->shader[0] = '\0';
> +
> +   if (offset_is_valid(&window->offset, window->state->gtt)) {
> +      struct gen_group *render =
> +         gen_spec_find_struct(context.spec, "RENDER_SURFACE_STATE");
> +      struct aubinator_reader reader(window->state->gtt, offset);
> +
> +      union gen_field_value value = { .u64 = 0 };
> +      gen_field_decode(gen_group_find_field(render, "Surface Base Address"),
> +                       reader.gen(), &value);
> +      window->base_address = value.u64;
> +      gen_field_decode(gen_group_find_field(render, "Width"),
> +                       reader.gen(), &value);
> +      window->width = value.u64 + 1;
> +      gen_field_decode(gen_group_find_field(render, "Height"),
> +                       reader.gen(), &value);
> +      window->height = value.u64 + 1;
> +      gen_field_decode(gen_group_find_field(render, "Surface Pitch"),
> +                       reader.gen(), &value);
> +      window->stride = value.u64 + 1;
> +      gen_field_decode(gen_group_find_field(render, "Surface QPitch"),
> +                       reader.gen(), &value);
> +      window->plane_stride = (value.u64 << 2) * window->stride;
> +      gen_field_decode(gen_group_find_field(render, "Surface Format"),
> +                       reader.gen(), &value);
> +      window->format = (enum isl_format) value.u64;
> +      gen_field_decode(gen_group_find_field(render, "Tiled Resource Mode"),
> +                       reader.gen(), &value);
> +      switch (value.u64) {
> +      default:
> +      case 0: window->tiling = ISL_TILING_LINEAR; break;
> +      case 1: window->tiling = ISL_TILING_Yf; break;
> +      case 2: window->tiling = ISL_TILING_Ys; break;
> +      }
> +
> +      // const struct isl_format_layout *layout =
> +      //    isl_format_get_layout(value.u64);
> +      // uint32_t channels = isl_format_get_num_channels(value.u64);
> +      update_image_window(window);
> +   }
> +
> +   list_add(&window->base.windows, &context.windows);
> +}
> +
> +/* Pattern windows */
> +
> +static void
> +display_pattern_window(struct window *win)
> +{
> +   struct pattern_window *window = (struct pattern_window *) win;
> +   static const struct {
> +      const char *name;
> +      uint8_t offset;
> +      uint8_t length;
> +   } ms[] = {
> +      { "16x", 0, 16 },
> +      { "8x", 16, 8 },
> +      { "4x", 24, 4 },
> +      { "2x", 28, 2 },
> +      { "1x", 30, 1 },
> +   };
> +
> +   for (unsigned i = 0; i < ARRAY_SIZE(ms); i++) {
> +      ImGui::SameLine();
> +      ImGui::RadioButton(ms[i].name, &window->ms_offset, i);
> +   }
> +
> +   ImGui::PushStyleColor(ImGuiCol_PlotLines, context.highlight_color);
> +   Aubinator::PlotDots("points", window->points,
> +                       ms[window->ms_offset].length, ms[window->ms_offset].offset,
> +                       "Sampling points", ImVec2(0.0, 0.0), ImVec2(1.0, 1.0),
> +                       ImVec2(ImGui::GetContentRegionAvailWidth(),
> +                              ImGui::GetContentRegionAvailWidth()), 5.0f);
> +   ImGui::PopStyleColor();
> +}
> +
> +static void
> +destroy_pattern_window(struct window *win)
> +{
> +   free(win);
> +}
> +
> +static void
> +new_pattern_window(const struct gen_dword_reader *reader)
> +{
> +   struct pattern_window *window = xtzalloc(struct pattern_window);
> +
> +   snprintf(window->base.name, sizeof(window->base.name),
> +            "Sampling pattern##%p", window);
> +   window->base.position = ImVec2(-1, -1);
> +   window->base.size = ImVec2(300, 360);
> +   window->base.opened = true;
> +   window->base.display = display_pattern_window;
> +   window->base.destroy = destroy_pattern_window;
> +
> +   for (unsigned p = 0, dw_idx = 1; p < ARRAY_SIZE(window->points); ) {
> +      union {
> +         uint32_t dw;
> +         uint8_t points[4];
> +      } data;
> +      data.dw = gen_read_dword(reader, dw_idx++);
> +
> +      for (unsigned b = 0; b < 4 && p < ARRAY_SIZE(window->points); b++, p++) {
> +         uint8_t fixed_point = data.points[b];
> +         window->points[p] = ImVec2((fixed_point & 0xf) / 16.0f,
> +                                    ((fixed_point >> 4) & 0xf) / 16.0f);
> +      }
> +   }
> +
> +   list_add(&window->base.windows, &context.windows);
> +}
> +
> +static void
> +new_pattern_window(struct gpu_state *state)
> +{
> +   struct gpu_state_item *item = gpu_state_find(state, "3DSTATE_SAMPLE_PATTERN");
> +   if (!item)
> +      return;
> +
> +   struct aubinator_reader reader(item->gtt, item->gtt_offset);
> +   new_pattern_window(reader.gen());
> +}
> +
> +/* Shader windows */
> +
> +static void
> +display_shader_window(struct window *win)
> +{
> +   struct shader_window *window = (struct shader_window *) win;
> +
> +   display_offset(&window->offset);
> +
> +   ImGui::BeginChild(ImGui::GetID("##block"));
> +   ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth());
> +   ImGui::InputTextMultiline("##shader",
> +                             window->shader, window->shader_len,
> +                             ImVec2(-1.0f, -1.0f), ImGuiInputTextFlags_ReadOnly);
> +   ImGui::PopItemWidth();
> +   ImGui::EndChild();
> +}
> +
> +static void
> +destroy_shader_window(struct window *win)
> +{
> +   struct shader_window *window = (struct shader_window *) win;
> +
> +   if (window->shader) free(window->shader);
> +   gpu_state_free(window->state);
> +   free(window);
> +}
> +
> +static void
> +new_shader_window(struct gpu_state *state, uint64_t shader_offset, const char *type)
> +{
> +   struct shader_window *window = xtzalloc(struct shader_window);
> +
> +   snprintf(window->base.name, sizeof(window->base.name),
> +            "%s shader view##%p", type, window);
> +   window->base.position = ImVec2(-1, -1);
> +   window->base.size = ImVec2(800, 300);
> +   window->base.opened = true;
> +   window->base.display = display_shader_window;
> +   window->base.destroy = destroy_shader_window;
> +
> +   window->state = gpu_state_copy(state);
> +   window->offset.base = &state->instruction_base;
> +   window->offset.base_name = "instruction";
> +   window->offset.offset = shader_offset;
> +
> +   if (offset_is_valid(&window->offset, window->state->gtt)) {
> +      uint64_t offset = offset_get_absolute(&window->offset);
> +      fprintf(stderr, "shader offset=%lx\n", offset);
> +      struct aubinator_reader reader(window->state->gtt, offset);
> +      uint32_t shader_length =
> +         gen_disasm_get_assembly_size(context.disasm, reader.gen());
> +      void *data = malloc(shader_length);
> +      memory_read(window->state->gtt, offset, data, shader_length);
> +      gen_disasm_disassemble(context.disasm, data, 0, &window->shader);
> +      free(data);
> +      if (window->shader)
> +         window->shader_len = strlen(window->shader);
> +   }
> +
> +   list_add(&window->base.windows, &context.windows);
> +}
> +
> +/* State windows */
> +
> +static void
> +display_state_window(struct window *win)
> +{
> +   struct state_window *window = (struct state_window *) win;
> +   struct gpu_state *state = window->state ? window->state : gpu_state_last();
> +
> +   if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();
> +   char name[40];
> +   snprintf(name, sizeof(name), "filter##%p", window);
> +   window->cmd_filter.Draw(name);
> +
> +   ImGui::Text("state items: %i\n",
> +               _mesa_hash_table_num_entries(state->items));
> +   if (ImGui::Button("Collapse columns")) { window->collapsed ^= 1; } ImGui::SameLine();
> +   if (ImGui::Button("Dwords")) window->options.show_dwords ^= 1;
> +
> +   if (ImGui::Button("Show URB")) { new_urb_window(state); } ImGui::SameLine();
> +   if (ImGui::Button("Show sampling pattern")) new_pattern_window(state);
> +
> +   ImGui::BeginChild(ImGui::GetID("##block"));
> +   struct hash_entry *entry;
> +   hash_table_foreach(state->items, entry) {
> +      struct gpu_state_item *item = (struct gpu_state_item *) entry->data;
> +      ImGui::PushID(item);
> +      if (window->cmd_filter.PassFilter(item->group->name) &&
> +          ImGui::CollapsingHeader(item->group->name)) {
> +         struct aubinator_reader child_reader(item->gtt, item->gtt_offset);
> +         decode_group(item->group, child_reader.gen(), &window->options);
> +      }
> +      ImGui::PopID();
> +   }
> +   ImGui::EndChild();
> +}
> +
> +static void
> +destroy_state_window(struct window *win)
> +{
> +   struct state_window *window = (struct state_window *) win;
> +
> +   gpu_state_free(window->state);
> +   free(window);
> +}
> +
> +static void
> +new_state_window(struct gpu_state *state)
> +{
> +   struct state_window *window = xtzalloc(struct state_window);
> +
> +   if (state)
> +      snprintf(window->base.name, sizeof(window->base.name),
> +               "State view##%p", window);
> +   else
> +      snprintf(window->base.name, sizeof(window->base.name),
> +               "State view (live)##%p", window);
> +   window->base.position = ImVec2(-1, -1);
> +   window->base.size = ImVec2(500, 600);
> +   window->base.opened = true;
> +   window->base.display = display_state_window;
> +   window->base.destroy = destroy_state_window;
> +
> +   window->state = state ? gpu_state_copy(state) : NULL;
> +
> +   list_add(&window->base.windows, &context.windows);
> +}
> +
> +/* URB windows */
> +
> +static void
> +display_urb_window(struct window *win)
> +{
> +   struct urb_window *window = (struct urb_window *) win;
> +
> +   Aubinator::BeginURB("URB layout", window->n_allocation, window->max_allocation,
> +                       ImVec2(ImGui::GetContentRegionAvailWidth(), 0));
> +   for (uint32_t a = 0; a < window->n_allocation; a++) {
> +      Aubinator::URBItem(window->allocations[a].name,
> +                         ImVec2(window->allocations[a].offset,
> +                                window->allocations[a].length),
> +                         &window->allocations[a].color);
> +   }
> +   for (uint32_t a = 0; a < window->n_allocation; a++) {
> +      ImGui::ColorButton(window->allocations[a].name,
> +                         window->allocations[a].length == 0 ?
> +                         ImColor(131, 131, 131) :
> +                         window->allocations[a].color,
> +                         ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoTooltip);
> +      ImGui::SameLine();
> +      ImGui::Text("%s: (%s) offset: %u, length: %u",
> +                  window->allocations[a].name,
> +                  window->allocations[a].length == 0 ?
> +                  "disabled" : "enabled",
> +                  window->allocations[a].offset,
> +                  window->allocations[a].length);
> +   }
> +
> +   Aubinator::EndURB();
> +}
> +
> +static void
> +destroy_urb_window(struct window *win)
> +{
> +   struct urb_window *window = (struct urb_window *) win;
> +
> +   gpu_state_free(window->state);
> +   free(window);
> +}
> +
> +static bool
> +get_allocation(struct gpu_state *state,
> +               const char *instruction,
> +               const char *offset_field,
> +               uint32_t *offset,
> +               const char *length_field,
> +               uint32_t *length)
> +{
> +   struct gpu_state_item *item = gpu_state_find(state, instruction);
> +   if (!item)
> +      return false;
> +
> +   struct gen_field *gen_offset_field =
> +      gen_group_find_field(item->group, offset_field);
> +   struct gen_field *gen_length_field =
> +      gen_group_find_field(item->group, length_field);
> +   if (!gen_offset_field || !gen_length_field)
> +      return false;
> +
> +   struct aubinator_reader reader(item->gtt, item->gtt_offset);
> +
> +   union gen_field_value value = { .u64 = 0 };
> +   gen_field_decode(gen_offset_field, reader.gen(), &value);
> +   *offset = (uint32_t) value.u64 * 8 * 1024; /* 8Kb units */
> +   gen_field_decode(gen_length_field, reader.gen(), &value);
> +   *length = (uint32_t) value.u64 * 64; /* 512-bit units */
> +
> +   return true;
> +}
> +
> +static bool
> +get_constant_allocation(struct gpu_state *state, const char *instruction,
> +                        uint32_t *offset, uint32_t *length)
> +{
> +   struct gpu_state_item *item = gpu_state_find(state, instruction);
> +   if (!item)
> +      return false;
> +
> +   struct gen_field *gen_offset_field =
> +      gen_group_find_field(item->group, "Constant Buffer Offset");
> +   struct gen_field *gen_length_field =
> +      gen_group_find_field(item->group, "Constant Buffer Size");
> +   if (!gen_offset_field || !gen_length_field)
> +      return false;
> +
> +   struct aubinator_reader reader(item->gtt, item->gtt_offset);
> +
> +   union gen_field_value value = { .u64 = 0 };
> +   gen_field_decode(gen_offset_field, reader.gen(), &value);
> +   *offset = (uint32_t) value.u64 * 1024;
> +   gen_field_decode(gen_length_field, reader.gen(), &value);
> +   *length = (uint32_t) value.u64 * 1024;
> +
> +   return true;
> +}
> +
> +static void
> +new_urb_window(struct gpu_state *state)
> +{
> +   struct urb_window *window = xtzalloc(struct urb_window);
> +
> +   snprintf(window->base.name, sizeof(window->base.name),
> +            "URB view##%p", window);
> +   window->base.position = ImVec2(-1, -1);
> +   window->base.size = ImVec2(500, 300);
> +   window->base.opened = true;
> +   window->base.display = display_urb_window;
> +   window->base.destroy = destroy_urb_window;
> +
> +   window->state = gpu_state_copy(state);
> +
> +   list_add(&window->base.windows, &context.windows);
> +
> +   uint32_t a = 0;
> +   if (get_allocation(window->state, "3DSTATE_URB_VS",
> +                      "VS URB Starting Address", &window->allocations[a].offset,
> +                      "VS Number of URB Entries", &window->allocations[a].length))
> +      window->allocations[a++].name = "VS";
> +   if (get_allocation(window->state, "3DSTATE_URB_DS",
> +                      "DS URB Starting Address", &window->allocations[a].offset,
> +                      "DS Number of URB Entries", &window->allocations[a].length))
> +      window->allocations[a++].name = "DS";
> +   if (get_allocation(window->state, "3DSTATE_URB_GS",
> +                      "GS URB Starting Address", &window->allocations[a].offset,
> +                      "GS Number of URB Entries", &window->allocations[a].length))
> +      window->allocations[a++].name = "GS";
> +   if (get_allocation(window->state, "3DSTATE_URB_HS",
> +                      "HS URB Starting Address", &window->allocations[a].offset,
> +                      "HS Number of URB Entries", &window->allocations[a].length))
> +      window->allocations[a++].name = "HS";
> +   if (get_allocation(window->state, "MEDIA_VFE_STATE",
> +                      "", &window->allocations[a].offset,
> +                      "Number of URB Entries", &window->allocations[a].length))
> +      window->allocations[a++].name = "HS";
> +   if (get_constant_allocation(window->state, "3DSTATE_PUSH_CONSTANT_ALLOC_VS",
> +                               &window->allocations[a].offset,
> +                               &window->allocations[a].length))
> +      window->allocations[a++].name = "VS constants";
> +   if (get_constant_allocation(window->state, "3DSTATE_PUSH_CONSTANT_ALLOC_DS",
> +                               &window->allocations[a].offset,
> +                               &window->allocations[a].length))
> +      window->allocations[a++].name = "DS constants";
> +   if (get_constant_allocation(window->state, "3DSTATE_PUSH_CONSTANT_ALLOC_GS",
> +                               &window->allocations[a].offset,
> +                               &window->allocations[a].length))
> +      window->allocations[a++].name = "GS constants";
> +   if (get_constant_allocation(window->state, "3DSTATE_PUSH_CONSTANT_ALLOC_HS",
> +                               &window->allocations[a].offset,
> +                               &window->allocations[a].length))
> +      window->allocations[a++].name = "HS constants";
> +   if (get_constant_allocation(window->state, "3DSTATE_PUSH_CONSTANT_ALLOC_PS",
> +                               &window->allocations[a].offset,
> +                               &window->allocations[a].length))
> +      window->allocations[a++].name = "PS constants";
> +   window->n_allocation = a;
> +
> +   window->max_allocation = 0;
> +   for (a = 0; a < window->n_allocation; a++) {
> +      window->max_allocation = MAX2(window->max_allocation,
> +                                    window->allocations[a].offset +
> +                                    window->allocations[a].length);
> +   }
> +}
> +
> +/**/
> +
> +static void
> +destroy_window_noop(struct window *win)
> +{
> +}
> +
> +static void
> +display_registers_window(struct window *win)
> +{
> +   static struct ImGuiTextFilter filter;
> +   if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();
> +   filter.Draw();
> +
> +   ImGui::BeginChild(ImGui::GetID("##block"));
> +   struct hash_entry *entry;
> +   hash_table_foreach(context.spec->registers_by_name, entry) {
> +      struct gen_group *reg = (struct gen_group *) entry->data;
> +      if (filter.PassFilter(reg->name) &&
> +          ImGui::CollapsingHeader(reg->name)) {
> +         const struct gen_field *field = reg->fields;
> +         while (field) {
> +            ImGui::Text("%s : %i -> %i\n", field->name, field->start, field->end);
> +            field = field->next;
> +         }
> +      }
> +   }
> +   ImGui::EndChild();
> +}
> +
> +static void
> +show_register_window(void)
> +{
> +   struct window *window = &context.registers_window;
> +
> +   if (window->opened) {
> +      window->opened = false;
> +      return;
> +   }
> +
> +   snprintf(window->name, sizeof(window->name), "Registers");
> +   window->position = ImVec2(-1, -1);
> +   window->size = ImVec2(200, 400);
> +   window->opened = true;
> +   window->display = display_registers_window;
> +   window->destroy = destroy_window_noop;
> +
> +   list_add(&window->windows, &context.windows);
> +}
> +
> +static void
> +display_commands_window(struct window *win)
> +{
> +   static struct ImGuiTextFilter cmd_filter;
> +   if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();
> +   cmd_filter.Draw("name filter");
> +   static struct ImGuiTextFilter field_filter;
> +   field_filter.Draw("field filter");
> +
> +   static char opcode_str[9] = { 0, };
> +   ImGui::InputText("opcode filter", opcode_str, sizeof(opcode_str),
> +                    ImGuiInputTextFlags_CharsHexadecimal);
> +   size_t opcode_len = strlen(opcode_str);
> +   uint64_t opcode = strtol(opcode_str, NULL, 16);
> +
> +   static bool show_dwords = true;
> +   if (ImGui::Button("Dwords")) show_dwords ^= 1;
> +
> +   ImGui::BeginChild(ImGui::GetID("##block"));
> +   struct hash_entry *entry;
> +   hash_table_foreach(context.spec->commands, entry) {
> +      struct gen_group *cmd = (struct gen_group *) entry->data;
> +      if ((cmd_filter.PassFilter(cmd->name) &&
> +           (opcode_len == 0 || (opcode & cmd->opcode_mask) == cmd->opcode)) &&
> +          ImGui::CollapsingHeader(cmd->name)) {
> +         const struct gen_field *field = cmd->fields;
> +         int32_t last_dword = -1;
> +         while (field) {
> +            if (show_dwords && field->start / 32 != last_dword) {
> +               for (last_dword = MAX(0, last_dword + 1);
> +                    last_dword < field->start / 32; last_dword++) {
> +                  ImGui::TextColored(context.dwords_color,
> +                                     "Dword %d", last_dword);
> +               }
> +               ImGui::TextColored(context.dwords_color, "Dword %d", last_dword);
> +            }
> +            if (field_filter.PassFilter(field->name))
> +               ImGui::Text("%s : %i -> %i\n", field->name, field->start, field->end);
> +            field = field->next;
> +         }
> +      }
> +   }
> +   hash_table_foreach(context.spec->structs, entry) {
> +      struct gen_group *cmd = (struct gen_group *) entry->data;
> +      if (cmd_filter.PassFilter(cmd->name) && opcode_len == 0 &&
> +          ImGui::CollapsingHeader(cmd->name)) {
> +         const struct gen_field *field = cmd->fields;
> +         int32_t last_dword = -1;
> +         while (field) {
> +            if (show_dwords && field->start / 32 != last_dword) {
> +               last_dword = field->start / 32;
> +               ImGui::TextColored(context.dwords_color,
> +                                  "Dword %d", last_dword);
> +            }
> +            if (field_filter.PassFilter(field->name))
> +               ImGui::Text("%s : %i -> %i\n", field->name, field->start, field->end);
> +            field = field->next;
> +         }
> +      }
> +   }
> +   ImGui::EndChild();
> +}
> +
> +static void
> +show_commands_window(void)
> +{
> +   struct window *window = &context.commands_window;
> +
> +   if (window->opened) {
> +      window->opened = false;
> +      return;
> +   }
> +
> +   snprintf(window->name, sizeof(window->name), "Commands & structs");
> +   window->position = ImVec2(-1, -1);
> +   window->size = ImVec2(300, 400);
> +   window->opened = true;
> +   window->display = display_commands_window;
> +   window->destroy = destroy_window_noop;
> +
> +   list_add(&window->windows, &context.windows);
> +}
> +
> +/* Main window */
> +
> +static const char *
> +human_size(size_t size)
> +{
> +   unsigned divisions = 0;
> +   double v = size;
> +   double divider = 1024;
> +   while (v >= divider) {
> +      v /= divider;
> +      divisions++;
> +   }
> +
> +   static const char *units[] = { "Bytes", "Kilobytes", "Megabytes", "Gigabytes" };
> +   static char result[20];
> +   snprintf(result, sizeof(result), "%.2f %s",
> +            v, divisions >= ARRAY_SIZE(units) ? "Too much!" : units[divisions]);
> +   return result;
> +}
> +
> +static void
> +display_aubfile_window(struct window *win)
> +{
> +   ImGuiColorEditFlags cflags = (ImGuiColorEditFlags_NoAlpha |
> +                                 ImGuiColorEditFlags_NoLabel |
> +                                 ImGuiColorEditFlags_NoInputs);
> +   ImGui::ColorEdit3("background", (float *)&context.clear_color, cflags); ImGui::SameLine();
> +   ImGui::ColorEdit3("error", (float *)&context.error_color, cflags); ImGui::SameLine();
> +   ImGui::ColorEdit3("highlight", (float *)&context.highlight_color, cflags); ImGui::SameLine();
> +   ImGui::ColorEdit3("dwords", (float *)&context.dwords_color, cflags); ImGui::SameLine();
> +   if (ImGui::Button("Commands list") || has_ctrl_key('c')) { show_commands_window(); } ImGui::SameLine();
> +   if (ImGui::Button("Registers list") || has_ctrl_key('r')) { show_register_window(); } ImGui::SameLine();
> +   if (ImGui::Button("Help") || has_ctrl_key('h')) { ImGui::OpenPopup("Help"); }
> +
> +   if (ImGui::Button("Previous batch") || has_ctrl_key('p')) aub_file_read_block(MAX(context.command_index - 1, 0));
> +   ImGui::SameLine();
> +   ImGui::Text("%04i", context.command_index);
> +   ImGui::SameLine();
> +   if (ImGui::Button("Next batch") || has_ctrl_key('n')) aub_file_read_block(context.command_index + 1);
> +
> +   if (ImGui::Button("New batch window") || has_ctrl_key('b')) { new_batch_window(false); } ImGui::SameLine();
> +   if (ImGui::Button("New decode window")) { new_as_window(gpu_state_last(), 0); } ImGui::SameLine();
> +   if (ImGui::Button("New image window")) { new_image_window(gpu_state_last(), 0); } ImGui::SameLine();
> +   if (ImGui::Button("New state window") || has_ctrl_key('s')) new_state_window(NULL);
> +
> +   display_file_props();
> +
> +   ImGui::Text("Memory consumption: %s\n", human_size(memory_size()));
> +   ImGui::Text("GPU states: %i\n", context.n_gpu_states);
> +   ImGui::Text("last GPU state items: %i\n", _mesa_hash_table_num_entries(gpu_state_last()->items));
> +
> +   ImGui::SetNextWindowContentWidth(500);
> +   if (ImGui::BeginPopupModal("Help", NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
> +      ImGui::Text("Some global keybindings:");
> +      ImGui::Separator();
> +
> +      static const char *texts[] = {
> +         "Ctrl-h",          "show this screen",
> +         "Ctrl-c",          "show commands list",
> +         "Ctrl-r",          "show registers list",
> +         "Ctrl-b",          "new batch window",
> +         "Ctrl-s",          "new state window",
> +         "Ctrl-p/n",        "switch to previous/next batch buffer",
> +         "Ctrl-Tab",        "switch focus between window",
> +         "Ctrl-left/right", "align window to the side of the screen",
> +      };
> +      float align = 0.0f;
> +      for (uint32_t i = 0; i < ARRAY_SIZE(texts); i += 2)
> +         align = MAX(align, ImGui::CalcTextSize(texts[i]).x);
> +      align += ImGui::GetStyle().WindowPadding.x + 10;
> +
> +      for (uint32_t i = 0; i < ARRAY_SIZE(texts); i += 2) {
> +         ImGui::Text(texts[i]); ImGui::SameLine(align); ImGui::Text(texts[i + 1]);
> +      }
> +
> +      if (ImGui::Button("Done") || ImGui::IsKeyPressed(ImGuiKey_Escape))
> +         ImGui::CloseCurrentPopup();
> +      ImGui::EndPopup();
> +   }
> +}
> +
> +static void
> +show_aubfile_window(void)
> +{
> +   struct window *window = &context.file_window;
> +
> +   if (window->opened)
> +      return;
> +
> +   snprintf(window->name, sizeof(window->name),
> +            "Aubinator: Intel AUB file decoder");
> +   window->size = ImVec2(-1, 200);
> +   window->position =
> +      ImVec2(0, ImGui::GetIO().DisplaySize.y - window->size.y);
> +   window->opened = true;
> +   window->display = display_aubfile_window;
> +   window->destroy = NULL;
> +
> +   list_add(&window->windows, &context.windows);
> +}
> +
> +/* Focus management */
> +
> +static struct window *
> +update_focus(void)
> +{
> +   int focus_dir = (ImGui::GetIO().KeyCtrl &&
> +                    ImGui::GetIO().KeysDownDuration[ImGuiKey_Tab] == 0.0f) ?
> +      (ImGui::GetIO().KeyShift ? -1 : 1) :
> +      0;
> +
> +   struct window *focus = context.focused_window;
> +   if (!context.focused_window) {
> +      focus = context.focused_window =
> +         list_first_entry(&context.windows, struct window, windows);
> +      return focus;
> +   }
> +
> +   /* Only modify the list of windows on release of Ctrl key. */
> +   if (!ImGui::GetIO().KeyCtrl) {
> +      list_del(&context.focused_window->windows);
> +      list_add(&context.focused_window->windows, &context.windows);
> +   }
> +
> +   if (focus_dir == 0)
> +      return NULL;
> +
> +   if (focus_dir < 0) {
> +      if (focus->windows.prev == &context.windows)
> +         focus = list_last_entry(&context.windows, struct window, windows);
> +      else
> +         focus = LIST_ENTRY(struct window, focus->windows.prev, windows);
> +   } else {
> +      if (focus->windows.next == &context.windows)
> +         focus = list_first_entry(&context.windows, struct window, windows);
> +      else
> +         focus = LIST_ENTRY(struct window, focus->windows.next, windows);
> +   }
> +
> +   context.focused_window = focus;
> +
> +   return focus;
> +}
> +
> +static void
> +handle_resize(struct window *window)
> +{
> +   ImVec2 screen_size = ImGui::GetIO().DisplaySize;
> +
> +   if (window != context.focused_window) {
> +      ImGui::SetNextWindowPos(window->position, ImGuiCond_FirstUseEver);
> +      ImGui::SetNextWindowSize(window->size, ImGuiCond_FirstUseEver);
> +      return;
> +   }
> +
> +   if (has_ctrl_imgui_key(ImGuiKey_LeftArrow)) {
> +      ImGui::SetNextWindowPos(ImVec2(0, 0));
> +      ImGui::SetNextWindowSize(ImVec2(window->size.x, screen_size.y));
> +   } else if (has_ctrl_imgui_key(ImGuiKey_RightArrow)) {
> +      ImGui::SetNextWindowPos(ImVec2(screen_size.x - window->size.x, 0));
> +      ImGui::SetNextWindowSize(ImVec2(window->size.x, screen_size.y));
> +   }
> +}
> +
> +/* Main redrawing */
> +
> +static void
> +display_windows(void)
> +{
> +   struct window *new_focus = update_focus();
> +
> +   /* Start by disposing closed windows, we don't want to destroy windows that
> +    * have already been scheduled to be painted. So destroy always happens on
> +    * the next draw cycle, prior to any drawing.
> +    */
> +   list_for_each_entry_safe(struct window, window,
> +                            &context.windows, windows) {
> +      if (window->opened)
> +         continue;
> +
> +      if (window->destroy) {
> +         list_del(&window->windows);
> +         if (window == context.focused_window) {
> +            context.focused_window = list_first_entry(&context.windows,
> +                                                      struct window, windows);
> +         }
> +         window->destroy(window);
> +      } else
> +         window->opened = true;
> +   }
> +
> +   list_for_each_entry(struct window, window, &context.windows, windows) {
> +      handle_resize(window);
> +      if (new_focus && window == context.focused_window) ImGui::SetNextWindowFocus();
> +      ImGui::Begin(window->name, &window->opened);
> +      if (!new_focus && ImGui::IsRootWindowOrAnyChildFocused()) context.focused_window = window;
> +      window->display(window);
> +      window->position = ImGui::GetWindowPos();
> +      window->size = ImGui::GetWindowSize();
> +      ImGui::End();
> +   }
> +
> +   if (has_ctrl_key('w') &&
> +       context.focused_window != &context.file_window) {
> +      context.focused_window->opened = false;
> +   }
> +}
> +
> +static void repaint_window(CoglOnscreen *onscreen, gpointer user_data)
> +{
> +   ImGui_ImplGtk3Cogl_NewFrame();
> +
> +   show_aubfile_window();
> +
> +   display_windows();
> +
> +   /* Rendering */
> +   CoglFramebuffer *fb = COGL_FRAMEBUFFER(onscreen);
> +   cogl_framebuffer_set_viewport(fb,
> +                                 0, 0,
> +                                 cogl_framebuffer_get_width(fb),
> +                                 cogl_framebuffer_get_height(fb));
> +
> +   cogl_framebuffer_clear4f(fb, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH,
> +                            context.clear_color.x,
> +                            context.clear_color.y,
> +                            context.clear_color.z, 1.0);
> +   ImGui::Render();
> +   cogl_onscreen_swap_buffers(onscreen);
> +}
> +
> +static void
> +print_help(const char *progname, FILE *file)
> +{
> +   fprintf(file,
> +           "Usage: %s [OPTION]... [FILE]\n"
> +           "Decode aub file contents from either FILE or the standard input.\n\n"
> +           "A valid --gen option must be provided.\n\n"
> +           "      --help          display this help and exit\n"
> +           "      --gen=platform  decode for given platform (ivb, byt, hsw, bdw, chv, skl, kbl, bxt or cnl)\n"
> +           "      --headers       decode only command headers\n"
> +           "      --color[=WHEN]  colorize the output; WHEN can be 'auto' (default\n"
> +           "                        if omitted), 'always', or 'never'\n"
> +           "      --no-offsets    don't print instruction offsets\n"
> +           "      --xml=DIR       load hardware xml description from directory DIR\n",
> +           progname);
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +   int c, i;
> +   bool help = false;
> +   const struct option aubinator_opts[] = {
> +      { "help",       no_argument,       (int *) &help,                 true },
> +      { "gen",        required_argument, NULL,                          'g' },
> +      { "xml",        required_argument, NULL,                          'x' },
> +      { NULL,         0,                 NULL,                          0 }
> +   };
> +
> +   memset(&context, 0, sizeof(context));
> +
> +   i = 0;
> +   while ((c = getopt_long(argc, argv, "", aubinator_opts, &i)) != -1) {
> +      switch (c) {
> +      case 'g':
> +         context.pci_id = 0;
> +         for (unsigned g = 0; g < ARRAY_SIZE(supported_gens); g++) {
> +            if (!strcmp(optarg, supported_gens[g].name)) {
> +               context.pci_id = supported_gens[g].pci_id;
> +               break;
> +            }
> +         }
> +         if (context.pci_id == 0) {
> +            fprintf(stderr, "can't parse gen: '%s', expected ivb, byt, hsw, "
> +                                   "bdw, chv, skl, kbl or bxt\n", optarg);
> +            exit(EXIT_FAILURE);
> +         }
> +         break;
> +      case 'x':
> +         context.xml_path = strdup(optarg);
> +         break;
> +      default:
> +         break;
> +      }
> +   }
> +
> +   if (help || argc == 1) {
> +      print_help(argv[0], stderr);
> +      exit(0);
> +   }
> +
> +   if (optind < argc)
> +      context.input_file = argv[optind];
> +
> +   if (context.input_file == NULL)
> +      context.file = aub_file_stdin();
> +   else
> +      context.file = aub_file_open(context.input_file);
> +
> +   context.accumulation_handlers = init_accumulation_handlers();
> +   context.display_handlers = init_display_handlers();
> +
> +   context.clear_color = ImColor(114, 144, 154);
> +   context.dwords_color = ImColor(29, 177, 194, 255);
> +   context.error_color = ImColor(236, 255, 0, 255);
> +   context.highlight_color = ImColor(0, 230, 0, 255);
> +
> +   context.gpu_states[0] = gpu_state_new();
> +   context.n_gpu_states = 1;
> +
> +   list_inithead(&context.windows);
> +
> +   aub_file_read_headers();
> +   aub_file_read_block(0);
> +
> +   gtk_init(&argc, &argv);
> +
> +   GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
> +   g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), NULL);
> +   gtk_window_resize(GTK_WINDOW(window), 800, 600);
> +
> +   GtkWidget *box = gtk_event_box_new();
> +   gtk_widget_set_can_focus(box, TRUE);
> +   gtk_container_add(GTK_CONTAINER(window), box);
> +   gtk_widget_show_all(window);
> +
> +   UNUSED CoglOnscreen *onscreen = ImGui_ImplGtk3Cogl_Init(box, repaint_window, NULL);
> +
> +   char *config_file = NULL;
> +   if (context.input_file) {
> +      size_t max_len = strlen(context.input_file) + 10;
> +      config_file = (char *) malloc(max_len);
> +      snprintf(config_file, max_len, "%s.ini", context.input_file);
> +   }
> +   ImGui::GetIO().IniFilename = config_file;
> +   ImGui::GetIO().Fonts->AddFontDefault();
> +   //ImGui::GetIO().Fonts->AddFontFromFileTTF("./Roboto-Medium.ttf", 16.0f);
> +
> +   gtk_main();
> +
> +   free(context.xml_path);
> +
> +   return EXIT_SUCCESS;
> +}

[snip imgui]

imgui seems to be the first instance of someone pasting a sizeable third
party library into the repo.  I'm not sure how everyone feels about
that. Unfortunately it seems like imgui isn't packaged by any distros
that I can see either.

Maybe we could do some meson wrap magic or something to pull it in
without having to commit the code.

> diff --git a/src/intel/tools/memory.c b/src/intel/tools/memory.c
> new file mode 100644
> index 00000000000..871853de2ad
> --- /dev/null
> +++ b/src/intel/tools/memory.c
> @@ -0,0 +1,545 @@
> +/*
> + * Copyright © 2017 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (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 NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + */
> +
> +#undef NDEBUG
> +
> +#include <assert.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include "memory.h"
> +#include "util/macros.h"
> +
> +#define LEAF_SIZE  (4096UL)
> +
> +#define BASE_SHIFT   (12)
> +#define BRANCH_SHIFT (5)
> +#define BRANCH_MASK  (0x1f)
> +
> +#define LEAF_MASK    (~(LEAF_SIZE - 1UL))
> +
> +#define MAX_LEVEL    (11)
> +
> +#define BRANCH(offset, level) \
> +   (((offset) >> (BASE_SHIFT + BRANCH_SHIFT * (level))) & BRANCH_MASK)
> +
> +static inline unsigned
> +branch(uint64_t offset, unsigned level)
> +{
> +   offset >>= BASE_SHIFT;
> +   return (offset >> (BRANCH_SHIFT * (MAX_LEVEL - level))) & BRANCH_MASK;
> +}
> +
> +typedef struct {
> +   unsigned refcount;
> +
> +   enum {
> +      MEMORY_NODE_TYPE_BRANCH,
> +      MEMORY_NODE_TYPE_LEAF,
> +   } type;
> +} memory_node;
> +
> +typedef struct {
> +   memory_node base;
> +
> +   uint64_t offset;
> +   uint8_t data[];
> +} memory_leaf_node;
> +
> +typedef struct {
> +   memory_node base;
> +
> +   memory_node *children[32];
> +   unsigned level;
> +} memory_branch_node;
> +
> +struct _memory_t {
> +   memory_node *root;
> +
> +   uint32_t refcount;
> +
> +   uint64_t base_offset;
> +   uint64_t start;
> +   uint64_t end;
> +
> +   uint64_t last_leaf_offset;
> +   memory_leaf_node *last_leaf;
> +};
> +
> +/**/
> +
> +struct memory_iterator {
> +   memory_node *path[MAX_LEVEL + 2];
> +   unsigned n_path;
> +
> +   uint64_t offset;
> +   bool create;
> +};
> +
> +static uint32_t n_branches = 0;
> +static uint32_t n_leafs = 0;
> +
> +size_t
> +memory_size(void)
> +{
> +   return (LEAF_SIZE + sizeof(memory_leaf_node)) * n_leafs +
> +          sizeof(memory_branch_node) * n_branches;
> +}
> +
> +static memory_node *
> +memory_node_ref(memory_node *node)
> +{
> +   if (!node)
> +      return NULL;
> +
> +   node->refcount++;
> +   return node;
> +}
> +
> +static void
> +memory_node_unref(memory_node *node)
> +{
> +   if (!node)
> +      return;
> +
> +   node->refcount--;
> +   if (node->refcount == 0) {
> +      if (node->type == MEMORY_NODE_TYPE_BRANCH) {
> +         memory_branch_node *branch = (memory_branch_node *) node;
> +         for (unsigned i = 0; i < ARRAY_SIZE(branch->children); i++)
> +            memory_node_unref(branch->children[i]);
> +         n_branches--;
> +      } else
> +         n_leafs--;
> +      free(node);
> +   }
> +}
> +
> +static memory_node *
> +memory_leaf_node_new(uint64_t offset)
> +{
> +   memory_leaf_node *node = calloc(1, LEAF_SIZE + sizeof(*node));
> +   node->base.type = MEMORY_NODE_TYPE_LEAF;
> +   node->base.refcount = 1;
> +   node->offset = offset;
> +
> +   n_leafs++;
> +
> +   return &node->base;
> +}
> +
> +static memory_node *
> +memory_leaf_node_copy(memory_leaf_node *src)
> +{
> +   memory_leaf_node *node = calloc(1, LEAF_SIZE + sizeof(*node));
> +   node->base.type = MEMORY_NODE_TYPE_LEAF;
> +   node->base.refcount = 1;
> +   node->offset = src->offset;
> +   memcpy(node->data, src->data, LEAF_SIZE);
> +
> +   n_leafs++;
> +
> +   return &node->base;
> +}
> +
> +static memory_node *
> +memory_branch_node_new(unsigned level)
> +{
> +   memory_branch_node *node = calloc(1, LEAF_SIZE + sizeof(*node));
> +   node->base.type = MEMORY_NODE_TYPE_BRANCH;
> +   node->base.refcount = 1;
> +   node->level = level;
> +
> +   n_branches++;
> +
> +   return &node->base;
> +}
> +
> +static memory_node *
> +memory_branch_node_copy(memory_branch_node *src)
> +{
> +   memory_branch_node *node = calloc(1, LEAF_SIZE + sizeof(*node));
> +   node->base.type = MEMORY_NODE_TYPE_BRANCH;
> +   node->base.refcount = 1;
> +   node->level = src->level;
> +
> +   for (unsigned i = 0; i < ARRAY_SIZE(src->children); i++)
> +      node->children[i] = memory_node_ref(src->children[i]);
> +
> +   n_branches++;
> +
> +   return &node->base;
> +}
> +
> +static bool
> +find_path(struct memory_iterator *iter)
> +{
> +   if (iter->path[iter->n_path - 1]->type == MEMORY_NODE_TYPE_LEAF)
> +      return true;
> +
> +   memory_branch_node *bnode =
> +      (memory_branch_node *) iter->path[iter->n_path - 1];
> +   unsigned child = branch(iter->offset, iter->n_path - 1);
> +   assert(child < ARRAY_SIZE(bnode->children));
> +   if (!bnode->children[child]) {
> +      if (!iter->create)
> +         return false;
> +
> +      if (bnode->level == MAX_LEVEL) {
> +         bnode->children[child] =
> +            memory_leaf_node_new(iter->offset - iter->offset % LEAF_SIZE);
> +      } else {
> +         bnode->children[child] = memory_branch_node_new(bnode->level + 1);
> +      }
> +   }
> +
> +   iter->path[iter->n_path++] = bnode->children[child];
> +   assert(iter->n_path <= ARRAY_SIZE(iter->path));
> +   return find_path(iter);
> +}
> +
> +/**/
> +
> +memory_t *
> +memory_new(void)
> +{
> +   memory_t *mem = calloc(1, sizeof(*mem));
> +   mem->base_offset = 0;
> +   mem->root = memory_branch_node_new(0);
> +   mem->refcount = 1;
> +
> +   return mem;
> +}
> +
> +memory_t *
> +memory_ref(memory_t *mem)
> +{
> +   mem->refcount++;
> +
> +   return mem;
> +}
> +
> +void
> +memory_unref(memory_t *mem)
> +{
> +   if (--mem->refcount == 0) {
> +      memory_node_unref(mem->root);
> +      free(mem);
> +   }
> +}
> +
> +static memory_t *
> +memory_copy(memory_t *parent_mem)
> +{
> +   memory_t *mem = calloc(1, sizeof(*mem));
> +
> +   mem->base_offset = parent_mem->base_offset;
> +   mem->root = memory_node_ref(parent_mem->root);
> +   mem->start = parent_mem->start;
> +   mem->end = parent_mem->end;
> +   mem->refcount = 1;
> +
> +   return mem;
> +}
> +
> +
> +uint32_t
> +memory_read_dword(memory_t *mem, uint64_t offset)
> +{
> +   assert(offset % 4 == 0);
> +
> +   /* Reading dwords is usually contiguous, so it saves quite a bit of
> +    * computation to keep pointer to the last accessed leaf.
> +    */
> +   memory_leaf_node *leaf;
> +   if (!mem->last_leaf ||
> +       (mem->last_leaf_offset & LEAF_MASK) != (offset & LEAF_MASK)) {
> +      struct memory_iterator iter = {
> +         .n_path = 1,
> +         .offset = offset,
> +         .create = false,
> +      };
> +      iter.path[0] = mem->root;
> +      if (!find_path(&iter))
> +         return 0;
> +
> +      mem->last_leaf = leaf = (memory_leaf_node *) iter.path[iter.n_path - 1];
> +      mem->last_leaf_offset = offset;
> +   } else {
> +      leaf = mem->last_leaf;
> +   }
> +   /* fprintf(stderr, "read offset=%llu:\n", offset); */
> +   /* for (unsigned i = 0; i < iter.n_path; i++) */
> +   /*    fprintf(stderr, "\tpath=%i %p\n", i, iter.path[i]); */
> +
> +   uint32_t *data = (uint32_t *) leaf->data;
> +   uint64_t node_offset = offset % LEAF_SIZE;
> +   return data[node_offset / 4];
> +}
> +
> +static void
> +update_path(memory_node **nodes, unsigned n_nodes, uint64_t leaf_offset)
> +{
> +   for (unsigned n = 0; n < n_nodes; n++) {
> +      unsigned idx = n_nodes - n - 1;
> +
> +      /* memory_node *old = nodes[idx]; */
> +
> +      if (nodes[idx]->type == MEMORY_NODE_TYPE_BRANCH) {
> +         memory_branch_node *tmp =
> +            (memory_branch_node *) memory_branch_node_copy((memory_branch_node *) nodes[idx]);
> +         nodes[idx] = &tmp->base;
> +
> +         uint64_t child = branch(leaf_offset, MAX_LEVEL - n + 1);
> +         memory_node_unref(tmp->children[child]);
> +         tmp->children[child] = nodes[idx + 1];
> +      } else {
> +         nodes[idx] = memory_leaf_node_copy((memory_leaf_node *) nodes[idx]);
> +      }
> +
> +      /* memory_node *new = nodes[idx]; */
> +
> +      /* fprintf(stderr, "\tupdate_path idx=%i old=%p new=%p child_idx=%i\n", */
> +      /*         idx, old, new, branch(leaf_offset, MAX_LEVEL - n)); */
> +   }
> +}
> +
> +void
> +memory_read(memory_t *mem, uint64_t offset, void *data, size_t length)
> +{
> +   while (length > 0) {
> +      struct memory_iterator iter = {
> +         .n_path = 1,
> +         .offset = offset,
> +         .create = true,
> +      };
> +      iter.path[0] = mem->root;
> +      find_path(&iter);
> +
> +      memory_leaf_node *leaf = (memory_leaf_node *) iter.path[iter.n_path - 1];
> +
> +      size_t copy_length = MIN2(length, LEAF_SIZE - (offset - leaf->offset));
> +      memcpy(data, &leaf->data[offset - leaf->offset], copy_length);
> +
> +      offset += copy_length;
> +      data += copy_length;
> +      length -= copy_length;
> +   }
> +}
> +
> +memory_t *
> +memory_write(memory_t *parent_mem, uint64_t offset, void *data, size_t length)
> +{
> +   memory_t *mem = memory_copy(parent_mem);
> +
> +   while (length > 0) {
> +      struct memory_iterator iter = {
> +         .n_path = 1,
> +         .offset = offset,
> +         .create = true,
> +      };
> +      iter.path[0] = mem->root;
> +      find_path(&iter);
> +      /* fprintf(stderr, "pre update offset=%llu:\n", offset); */
> +      /* for (unsigned i = 0; i < iter.n_path; i++) */
> +      /*    fprintf(stderr, "\tpath=%i %p\n", i, iter.path[i]); */
> +      /* memory_node *src = iter.path[iter.n_path - 1]; */
> +      update_path(iter.path, iter.n_path, iter.offset);
> +      find_path(&iter);
> +      /* fprintf(stderr, "post update offset=%llu:\n", offset); */
> +      /* for (unsigned i = 0; i < iter.n_path; i++) */
> +      /*    fprintf(stderr, "\tpath=%i %p\n", i, iter.path[i]); */
> +
> +      memory_leaf_node *dst = (memory_leaf_node *) iter.path[iter.n_path - 1];
> +      size_t copy_length = MIN2(length, LEAF_SIZE - (offset - dst->offset));
> +      memcpy(&dst->data[offset - dst->offset], data, copy_length);
> +      /* fprintf(stderr, "=> write into dst=%p src=%p\n", dst, src); */
> +
> +      assert(copy_length != 0);
> +
> +      offset += copy_length;
> +      data += copy_length;
> +      length -= copy_length;
> +
> +      memory_node_unref(mem->root);
> +      mem->root = iter.path[0];
> +   }
> +
> +   mem->start = MIN2(mem->start, offset);
> +   mem->end   = MAX2(mem->end,   offset + length);
> +
> +   return mem;
> +}
> +
> +memory_t *
> +memory_write_unref(memory_t *mem, uint64_t offset, void *data, size_t length)
> +{
> +   memory_t *new_mem = memory_write(mem, offset, data, length);
> +   memory_unref(mem);
> +   return new_mem;
> +}
> +
> +uint64_t
> +memory_start(memory_t *mem)
> +{
> +   return mem->start;
> +}
> +
> +uint64_t
> +memory_end(memory_t *mem)
> +{
> +   return mem->end;
> +}
> +
> +#ifdef BUILD_TESTS
> +int
> +main(int argc, char *argv[])
> +{
> +   {
> +      uint64_t leaf_size = LEAF_SIZE;
> +
> +      for (unsigned i = 0; i <= MAX_LEVEL; i++)
> +         assert(branch(0, i) == 0);
> +
> +      for (unsigned i = 0; i < MAX_LEVEL; i++)
> +         assert(branch(leaf_size, i) == 0);
> +      assert(branch(leaf_size, MAX_LEVEL) == 1);
> +
> +      for (unsigned i = 0; i < MAX_LEVEL; i++)
> +         assert(branch(leaf_size * 2ULL, i) == 0);
> +      assert(branch(leaf_size * 2ULL, MAX_LEVEL) == 2);
> +
> +      for (unsigned i = 0; i < (MAX_LEVEL - 1); i++)
> +         assert(branch(leaf_size * 32ULL, 0) == 0);
> +      assert(branch(leaf_size * 32ULL, MAX_LEVEL - 1) == 1);
> +      assert(branch(leaf_size * 32ULL, MAX_LEVEL) == 0);
> +   }
> +
> +   {
> +      memory_t *mem1 = memory_new();
> +
> +      char *data = "PlopPlopPlop";
> +      memory_t *mem2 = memory_write(mem1, 0, data, strlen(data));
> +
> +      assert(mem1 != mem2);
> +
> +      assert(memory_read_dword(mem1, 0) == 0);
> +      assert(((memory_read_dword(mem2, 0) >> 0) & 0xff)  == 'P');
> +      assert(((memory_read_dword(mem2, 0) >> 8) & 0xff)  == 'l');
> +      assert(((memory_read_dword(mem2, 0) >> 16) & 0xff) == 'o');
> +      assert(((memory_read_dword(mem2, 0) >> 24) & 0xff) == 'p');
> +
> +      memory_unref(mem1);
> +      memory_unref(mem2);
> +
> +      assert(memory_size() == 0);
> +   }
> +
> +   {
> +      memory_t *mem1 = memory_new();
> +
> +      char *data = "PlopPlopPlop";
> +      memory_t *mem2 = memory_write(mem1, LEAF_SIZE, data, strlen(data));
> +
> +      assert(mem1 != mem2);
> +
> +      assert(memory_read_dword(mem1, LEAF_SIZE) == 0);
> +      assert(((memory_read_dword(mem2, LEAF_SIZE) >> 0) & 0xff)  == 'P');
> +      assert(((memory_read_dword(mem2, LEAF_SIZE) >> 8) & 0xff)  == 'l');
> +      assert(((memory_read_dword(mem2, LEAF_SIZE) >> 16) & 0xff) == 'o');
> +      assert(((memory_read_dword(mem2, LEAF_SIZE) >> 24) & 0xff) == 'p');
> +
> +      memory_unref(mem1);
> +      memory_unref(mem2);
> +
> +      assert(memory_size() == 0);
> +   }
> +
> +   {
> +      memory_t *mem1 = memory_new();
> +
> +      char *data = "PlopPlopPlop";
> +      memory_t *mem2 = memory_write(mem1, LEAF_SIZE, data, strlen(data));
> +      assert(mem1 != mem2);
> +
> +      assert(memory_read_dword(mem1, LEAF_SIZE) == 0);
> +      assert(((memory_read_dword(mem2, LEAF_SIZE) >> 0) & 0xff)  == 'P');
> +      assert(((memory_read_dword(mem2, LEAF_SIZE) >> 8) & 0xff)  == 'l');
> +      assert(((memory_read_dword(mem2, LEAF_SIZE) >> 16) & 0xff) == 'o');
> +      assert(((memory_read_dword(mem2, LEAF_SIZE) >> 24) & 0xff) == 'p');
> +
> +      memory_t *mem3 = memory_write(mem1, LEAF_SIZE * 2, data, strlen(data));
> +      assert(mem2 != mem3);
> +
> +      assert(memory_read_dword(mem2, LEAF_SIZE * 2) == 0);
> +      assert(((memory_read_dword(mem3, LEAF_SIZE * 2) >> 0) & 0xff)  == 'P');
> +      assert(((memory_read_dword(mem3, LEAF_SIZE * 2) >> 8) & 0xff)  == 'l');
> +      assert(((memory_read_dword(mem3, LEAF_SIZE * 2) >> 16) & 0xff) == 'o');
> +      assert(((memory_read_dword(mem3, LEAF_SIZE * 2) >> 24) & 0xff) == 'p');
> +
> +      assert(memory_size() > 0);
> +
> +      memory_unref(mem1);
> +      memory_unref(mem2);
> +      memory_unref(mem3);
> +
> +      assert(memory_size() == 0);
> +   }
> +
> +   {
> +      memory_t *mem1 = memory_new();
> +
> +      char *data = "PlopPlipPlop";
> +      memory_t *mem2 = memory_write(mem1, LEAF_SIZE - 4, data, strlen(data));
> +
> +      assert(mem1 != mem2);
> +
> +      assert(memory_read_dword(mem1, 0) == 0);
> +      assert(memory_read_dword(mem2, 0) == 0);
> +
> +      assert(((memory_read_dword(mem2, LEAF_SIZE - 4) >> 0) & 0xff)  == 'P');
> +      assert(((memory_read_dword(mem2, LEAF_SIZE - 4) >> 8) & 0xff)  == 'l');
> +      assert(((memory_read_dword(mem2, LEAF_SIZE - 4) >> 16) & 0xff) == 'o');
> +      assert(((memory_read_dword(mem2, LEAF_SIZE - 4) >> 24) & 0xff) == 'p');
> +
> +      assert(((memory_read_dword(mem2, LEAF_SIZE) >> 0) & 0xff)  == 'P');
> +      assert(((memory_read_dword(mem2, LEAF_SIZE) >> 8) & 0xff)  == 'l');
> +      assert(((memory_read_dword(mem2, LEAF_SIZE) >> 16) & 0xff) == 'i');
> +      assert(((memory_read_dword(mem2, LEAF_SIZE) >> 24) & 0xff) == 'p');
> +
> +      char ret[200] = { 0, };
> +      memory_read(mem2, LEAF_SIZE - 4, ret, strlen(data));
> +
> +      assert(strcmp(ret, data) == 0);
> +
> +      memory_unref(mem1);
> +      memory_unref(mem2);
> +
> +      assert(memory_size() == 0);
> +   }
> +
> +   return 0;
> +}
> +#endif
> diff --git a/src/intel/tools/memory.h b/src/intel/tools/memory.h
> new file mode 100644
> index 00000000000..d706a21f4a6
> --- /dev/null
> +++ b/src/intel/tools/memory.h
> @@ -0,0 +1,55 @@
> +/*
> + * Copyright © 2017 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (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 NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + */
> +
> +#ifndef __MEMORY_H__
> +#define __MEMORY_H__
> +
> +#include <stdint.h>
> +#include <stdlib.h>
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +typedef struct _memory_t memory_t;
> +
> +memory_t *memory_new(void);
> +memory_t *memory_ref(memory_t *parent_mem);
> +void memory_unref(memory_t *mem);
> +
> +uint32_t memory_read_dword(memory_t *mem, uint64_t offset);
> +
> +void memory_read(memory_t *mem, uint64_t offset, void *data, size_t length);
> +memory_t *memory_write(memory_t *mem, uint64_t offset, void *data, size_t length);
> +memory_t *memory_write_unref(memory_t *mem, uint64_t offset, void *data, size_t length);
> +
> +uint64_t memory_start(memory_t *mem);
> +uint64_t memory_end(memory_t *mem);
> +
> +size_t memory_size(void);
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif /* __MEMORY_H__ */
> diff --git a/src/intel/tools/meson.build b/src/intel/tools/meson.build
> index 1996d5208f0..c66240220f7 100644
> --- a/src/intel/tools/meson.build
> +++ b/src/intel/tools/meson.build
> @@ -28,6 +28,34 @@ aubinator = executable(
>    build_by_default : false,
>  )
>  
> +if dep_aubinator_ui.found()
> +  inc_imgui = include_directories('imgui')
> +  aubinator_ui = executable(
> +    'aubinator_ui',
> +    files('aubinator_ui.cpp',
> +          'aubinator_imgui_widgets.cpp',
> +          'aubinator_imgui_widgets.h',
> +          'imgui/imconfig.h',
> +          'imgui/imgui.cpp',
> +          'imgui/imgui_demo.cpp',
> +          'imgui/imgui_draw.cpp',
> +          'imgui/imgui.h',
> +          'imgui/imgui_impl_gtk3_cogl.cpp',
> +          'imgui/imgui_impl_gtk3_cogl.h',
> +          'imgui/imgui_internal.h',
> +          'imgui/stb_rect_pack.h',
> +          'imgui/stb_textedit.h',
> +          'imgui/stb_truetype.h',
> +          'memory.c', 'memory.h',
> +          'disasm.c', 'gen_disasm.h', 'intel_aub.h'),
> +    dependencies : [dep_aubinator_ui, dep_expat, dep_zlib, dep_dl, dep_thread, dep_m],
> +    include_directories : [inc_common, inc_intel, inc_imgui],
> +    link_with : [libisl, libintel_common, libintel_compiler, libmesa_util],
> +    c_args : [c_vis_args, no_override_init_args],
> +    build_by_default : false,
> +  )
> +endif
> +
>  aubinator_error_decode = executable(
>    'aubinator_error_decode',
>    files('aubinator_error_decode.c', 'disasm.c', 'gen_disasm.h'),
> @@ -37,3 +65,12 @@ aubinator_error_decode = executable(
>    c_args : [c_vis_args, no_override_init_args],
>    build_by_default : false,
>  )
> +
> +memory_tests = executable(
> +  'memory_tests',
> +  files('memory.c', 'memory.h'),
> +  include_directories : inc_common,
> +  link_with : libmesa_util,
> +  c_args : [c_vis_args, no_override_init_args, '-DBUILD_TESTS'],
> +  build_by_default : false,
> +)
> -- 
> 2.15.0.rc2
>
> _______________________________________________
> mesa-dev mailing list
> mesa-dev at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/mesa-dev


More information about the mesa-dev mailing list