1-- A script that compares a set of equivalent cmdstream captures from
2-- various generations, looking for equivalencies between registers.
3--
4-- This would be run across a group of similar tests for various
5-- generations, for example:
6--
7--   cffdump --script scripts/analyze.lua a320/quad-flat-*.rd a420/quad-flat-*.rd
8--
9-- This is done by comparing unique register values.  Ie. for each
10-- generation, find the set of registers that have different values
11-- between equivalent draw calls.
12
13local posix = require "posix"
14
15io.write("Analyzing Data...\n")
16
17-- results - table structure:
18-- * [gpuname] - gpu
19--   * tests
20--     * [testname] - current test
21--       * draws
22--         * [1..n] - the draws
23--           * primtype - the primitive type
24--           * regs - table of values for draw
25--             * [regbase] - regval
26--   * regvals - table of unique values across all draws
27--     * [regbase]
28--       * [regval] - list of test names
29--         * [1..n] - testname "." didx
30local results = {}
31
32local test = nil
33local gpuname = nil
34local testname = nil
35
36
37-- srsly, no sparse table size() op?
38function tblsz(tbl)
39  local n = 0;
40  for k,v in pairs(tbl) do
41    n = n + 1
42  end
43  return n
44end
45
46
47function start_cmdstream(name)
48  testname = posix.basename(name)
49  gpuname = posix.basename(posix.dirname(name))
50  --io.write("START: gpuname=" .. gpuname .. ", testname=" .. testname .. "\n");
51  local gpu = results[gpuname]
52  if gpu == nil then
53    gpu = {["tests"] = {}, ["regvals"] = {}}
54    results[gpuname] = gpu
55  end
56  test = {["draws"] = {}}
57  gpu["tests"][testname] = test
58end
59
60function draw(primtype, nindx)
61  -- RECTLIST is only used internally.. we want to ignore it for
62  -- now, although it could potentially be interesting to track
63  -- these separately (separating clear/restore/resolve) just to
64  -- figure out which registers are used for which..
65  if primtype == "DI_PT_RECTLIST" then
66    return
67  end
68  local regtbl = {}
69  local draw = {["primtype"] = primtype, ["regs"] = regtbl}
70  local didx = tblsz(test["draws"])
71
72  test["draws"][didx] = draw
73
74  -- populate current regs.  For now just consider ones that have
75  -- been written.. maybe we need to make that configurable in
76  -- case it filters out too many registers.
77  for regbase=0,0xffff do
78    if regs.written(regbase) ~= 0 then
79      local regval = regs.val(regbase)
80
81      -- track reg vals per draw:
82      regtbl[regbase] = regval
83
84      -- also track which reg vals appear in which tests:
85      local uniq_regvals = results[gpuname]["regvals"][regbase]
86      if uniq_regvals == nil then
87        uniq_regvals = {}
88        results[gpuname]["regvals"][regbase] = uniq_regvals;
89      end
90      local drawlist = uniq_regvals[regval]
91      if drawlist == nil then
92        drawlist = {}
93        uniq_regvals[regval] = drawlist
94      end
95      table.insert(drawlist, testname .. "." .. didx)
96    end
97  end
98
99  -- TODO maybe we want to whitelist a few well known regs, for the
100  -- convenience of the code that runs at the end to analyze the data?
101  -- TODO also would be useful to somehow capture CP_SET_BIN..
102
103end
104
105function end_cmdstream()
106  test = nil
107  gpuname = nil
108  testname = nil
109end
110
111function print_draws(gpuname, gpu)
112  io.write("  " .. gpuname .. "\n")
113  for testname,test in pairs(gpu["tests"]) do
114    io.write("    " .. testname .. ", draws=" .. #test["draws"] .. "\n")
115    for didx,draw in pairs(test["draws"]) do
116      io.write("      " .. didx .. ": " .. draw["primtype"] .. "\n")
117    end
118  end
119end
120
121-- sort and concat a list of draw names to form a key which can be
122-- compared to other drawlists to check for equality
123-- TODO maybe we instead want a scheme that allows for some fuzzyness
124-- in the matching??
125function drawlistname(drawlist)
126  local name = nil
127  for idx,draw in pairs(drawlist) do
128    if name == nil then
129      name = draw
130    else
131      name = name .. ":" .. draw
132    end
133  end
134  return name
135end
136
137local rnntbl = {}
138
139function dumpmatches(name)
140  for gpuname,gpu in pairs(results) do
141    local r = rnntbl[gpuname]
142    if r == nil then
143      io.write("loading rnn database: \n" .. gpuname)
144      r = rnn.init(gpuname)
145      rnntbl[gpuname] = r
146    end
147    for regbase,regvals in pairs(gpu["regvals"]) do
148      for regval,drawlist in pairs(regvals) do
149        local name2 = drawlistname(drawlist)
150        if name == name2 then
151          io.write(string.format("  %s:%s:\t%08x  %s\n",
152                                 gpuname, rnn.regname(r, regbase),
153                                 regval, rnn.regval(r, regbase, regval)))
154        end
155      end
156    end
157  end
158end
159
160function finish()
161  -- drawlistnames that we've already dumped:
162  local dumped = {}
163
164  for gpuname,gpu in pairs(results) do
165    -- print_draws(gpuname, gpu)
166    for regbase,regvals in pairs(gpu["regvals"]) do
167      for regval,drawlist in pairs(regvals) do
168        local name = drawlistname(drawlist)
169        if dumped[name] == nil then
170          io.write("\n" .. name .. ":\n")
171          dumpmatches(name)
172          dumped[name] = 1
173        end
174      end
175    end
176  end
177end
178
179