freedreno: slurp in decode tools
cffdump, crashdec, etc At this point there is some duplication with other files in-tree (ie. a2xx and a3xx+ disassembly), which will be cleaned up in a later commit. Signed-off-by: Rob Clark <robdclark@chromium.org> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/6070>
This commit is contained in:
parent
7c0bd8429f
commit
1ea4ef0d3b
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Helper lib to track gpu buffers contents/address, and map between gpu and
|
||||
* host address while decoding cmdstream/crashdumps
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "buffers.h"
|
||||
|
||||
struct buffer {
|
||||
void *hostptr;
|
||||
unsigned int len;
|
||||
uint64_t gpuaddr;
|
||||
|
||||
/* for 'once' mode, for buffers containing cmdstream keep track per offset
|
||||
* into buffer of which modes it has already been dumped;
|
||||
*/
|
||||
struct {
|
||||
unsigned offset;
|
||||
unsigned dumped_mask;
|
||||
} offsets[64];
|
||||
unsigned noffsets;
|
||||
};
|
||||
|
||||
static struct buffer buffers[512];
|
||||
static int nbuffers;
|
||||
|
||||
static int
|
||||
buffer_contains_gpuaddr(struct buffer *buf, uint64_t gpuaddr, uint32_t len)
|
||||
{
|
||||
return (buf->gpuaddr <= gpuaddr) && (gpuaddr < (buf->gpuaddr + buf->len));
|
||||
}
|
||||
|
||||
static int
|
||||
buffer_contains_hostptr(struct buffer *buf, void *hostptr)
|
||||
{
|
||||
return (buf->hostptr <= hostptr) && (hostptr < (buf->hostptr + buf->len));
|
||||
}
|
||||
|
||||
|
||||
uint64_t
|
||||
gpuaddr(void *hostptr)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < nbuffers; i++)
|
||||
if (buffer_contains_hostptr(&buffers[i], hostptr))
|
||||
return buffers[i].gpuaddr + (hostptr - buffers[i].hostptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
gpubaseaddr(uint64_t gpuaddr)
|
||||
{
|
||||
int i;
|
||||
if (!gpuaddr)
|
||||
return 0;
|
||||
for (i = 0; i < nbuffers; i++)
|
||||
if (buffer_contains_gpuaddr(&buffers[i], gpuaddr, 0))
|
||||
return buffers[i].gpuaddr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *
|
||||
hostptr(uint64_t gpuaddr)
|
||||
{
|
||||
int i;
|
||||
if (!gpuaddr)
|
||||
return 0;
|
||||
for (i = 0; i < nbuffers; i++)
|
||||
if (buffer_contains_gpuaddr(&buffers[i], gpuaddr, 0))
|
||||
return buffers[i].hostptr + (gpuaddr - buffers[i].gpuaddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned
|
||||
hostlen(uint64_t gpuaddr)
|
||||
{
|
||||
int i;
|
||||
if (!gpuaddr)
|
||||
return 0;
|
||||
for (i = 0; i < nbuffers; i++)
|
||||
if (buffer_contains_gpuaddr(&buffers[i], gpuaddr, 0))
|
||||
return buffers[i].len + buffers[i].gpuaddr - gpuaddr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
has_dumped(uint64_t gpuaddr, unsigned enable_mask)
|
||||
{
|
||||
if (!gpuaddr)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < nbuffers; i++) {
|
||||
if (buffer_contains_gpuaddr(&buffers[i], gpuaddr, 0)) {
|
||||
struct buffer *b = &buffers[i];
|
||||
assert(gpuaddr >= b->gpuaddr);
|
||||
unsigned offset = gpuaddr - b->gpuaddr;
|
||||
|
||||
unsigned n = 0;
|
||||
while (n < b->noffsets) {
|
||||
if (offset == b->offsets[n].offset)
|
||||
break;
|
||||
n++;
|
||||
}
|
||||
|
||||
/* if needed, allocate a new offset entry: */
|
||||
if (n == b->noffsets) {
|
||||
b->noffsets++;
|
||||
assert(b->noffsets < ARRAY_SIZE(b->offsets));
|
||||
b->offsets[n].dumped_mask = 0;
|
||||
b->offsets[n].offset = offset;
|
||||
}
|
||||
|
||||
if ((b->offsets[n].dumped_mask & enable_mask) == enable_mask)
|
||||
return true;
|
||||
|
||||
b->offsets[n].dumped_mask |= enable_mask;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
reset_buffers(void)
|
||||
{
|
||||
for (int i = 0; i < nbuffers; i++) {
|
||||
free(buffers[i].hostptr);
|
||||
buffers[i].hostptr = NULL;
|
||||
buffers[i].len = 0;
|
||||
buffers[i].noffsets = 0;
|
||||
}
|
||||
nbuffers = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record buffer contents, takes ownership of hostptr (freed in
|
||||
* reset_buffers())
|
||||
*/
|
||||
void
|
||||
add_buffer(uint64_t gpuaddr, unsigned int len, void *hostptr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nbuffers; i++) {
|
||||
if (buffers[i].gpuaddr == gpuaddr)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == nbuffers) {
|
||||
/* some traces, like test-perf, with some blob versions,
|
||||
* seem to generate an unreasonable # of gpu buffers (a
|
||||
* leak?), so just ignore them.
|
||||
*/
|
||||
if (nbuffers >= ARRAY_SIZE(buffers)) {
|
||||
free(hostptr);
|
||||
return;
|
||||
}
|
||||
nbuffers++;
|
||||
}
|
||||
|
||||
buffers[i].hostptr = hostptr;
|
||||
buffers[i].len = len;
|
||||
buffers[i].gpuaddr = gpuaddr;
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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 __BUFFERS_H__
|
||||
#define __BUFFERS_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
uint64_t gpuaddr(void *hostptr);
|
||||
uint64_t gpubaseaddr(uint64_t gpuaddr);
|
||||
void * hostptr(uint64_t gpuaddr);
|
||||
unsigned hostlen(uint64_t gpuaddr);
|
||||
bool has_dumped(uint64_t gpuaddr, unsigned enable_mask);
|
||||
|
||||
void reset_buffers(void);
|
||||
void add_buffer(uint64_t gpuaddr, unsigned int len, void *hostptr);
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||
#endif
|
||||
|
||||
#endif /* __BUFFERS_H__ */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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 __CFFDEC_H__
|
||||
#define __CFFDEC_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
enum query_mode {
|
||||
/* default mode, dump all queried regs on each draw: */
|
||||
QUERY_ALL = 0,
|
||||
|
||||
/* only dump if any of the queried regs were written
|
||||
* since last draw:
|
||||
*/
|
||||
QUERY_WRITTEN,
|
||||
|
||||
/* only dump if any of the queried regs changed since
|
||||
* last draw:
|
||||
*/
|
||||
QUERY_DELTA,
|
||||
};
|
||||
|
||||
struct cffdec_options {
|
||||
unsigned gpu_id;
|
||||
int draw_filter;
|
||||
int color;
|
||||
int dump_shaders;
|
||||
int summary;
|
||||
int allregs;
|
||||
int dump_textures;
|
||||
int decode_markers;
|
||||
char *script;
|
||||
|
||||
int query_compare; /* binning vs SYSMEM/GMEM compare mode */
|
||||
int query_mode; /* enum query_mode */
|
||||
char **querystrs;
|
||||
int nquery;
|
||||
|
||||
/* In "once" mode, only decode a cmdstream buffer once (per draw
|
||||
* mode, in the case of a6xx+ where a single cmdstream buffer can
|
||||
* be used for both binning and draw pass), rather than each time
|
||||
* encountered (ie. once per tile/bin in GMEM draw passes)
|
||||
*/
|
||||
int once;
|
||||
|
||||
/* for crashdec, where we know CP_IBx_REM_SIZE, we can use this
|
||||
* to highlight the cmdstream not parsed yet, to make it easier
|
||||
* to see how far along the CP is.
|
||||
*/
|
||||
struct {
|
||||
uint64_t base;
|
||||
uint32_t rem;
|
||||
} ibs[4];
|
||||
};
|
||||
|
||||
void printl(int lvl, const char *fmt, ...);
|
||||
const char * pktname(unsigned opc);
|
||||
uint32_t regbase(const char *name);
|
||||
const char * regname(uint32_t regbase, int color);
|
||||
bool reg_written(uint32_t regbase);
|
||||
uint32_t reg_lastval(uint32_t regbase);
|
||||
uint32_t reg_val(uint32_t regbase);
|
||||
void reg_set(uint32_t regbase, uint32_t val);
|
||||
void reset_regs(void);
|
||||
void cffdec_init(const struct cffdec_options *options);
|
||||
void dump_register_val(uint32_t regbase, uint32_t dword, int level);
|
||||
void dump_commands(uint32_t *dwords, uint32_t sizedwords, int level);
|
||||
|
||||
/*
|
||||
* Helpers for packet parsing:
|
||||
*/
|
||||
|
||||
|
||||
#define CP_TYPE0_PKT 0x00000000
|
||||
#define CP_TYPE2_PKT 0x80000000
|
||||
#define CP_TYPE3_PKT 0xc0000000
|
||||
#define CP_TYPE4_PKT 0x40000000
|
||||
#define CP_TYPE7_PKT 0x70000000
|
||||
|
||||
#define pkt_is_type0(pkt) (((pkt) & 0XC0000000) == CP_TYPE0_PKT)
|
||||
#define type0_pkt_size(pkt) ((((pkt) >> 16) & 0x3FFF) + 1)
|
||||
#define type0_pkt_offset(pkt) ((pkt) & 0x7FFF)
|
||||
|
||||
#define pkt_is_type2(pkt) ((pkt) == CP_TYPE2_PKT)
|
||||
|
||||
/*
|
||||
* Check both for the type3 opcode and make sure that the reserved bits [1:7]
|
||||
* and 15 are 0
|
||||
*/
|
||||
|
||||
static inline uint pm4_calc_odd_parity_bit(uint val)
|
||||
{
|
||||
return (0x9669 >> (0xf & ((val) ^
|
||||
((val) >> 4) ^ ((val) >> 8) ^ ((val) >> 12) ^
|
||||
((val) >> 16) ^ ((val) >> 20) ^ ((val) >> 24) ^
|
||||
((val) >> 28)))) & 1;
|
||||
}
|
||||
|
||||
#define pkt_is_type3(pkt) \
|
||||
((((pkt) & 0xC0000000) == CP_TYPE3_PKT) && \
|
||||
(((pkt) & 0x80FE) == 0))
|
||||
|
||||
#define cp_type3_opcode(pkt) (((pkt) >> 8) & 0xFF)
|
||||
#define type3_pkt_size(pkt) ((((pkt) >> 16) & 0x3FFF) + 1)
|
||||
|
||||
#define pkt_is_type4(pkt) \
|
||||
((((pkt) & 0xF0000000) == CP_TYPE4_PKT) && \
|
||||
((((pkt) >> 27) & 0x1) == \
|
||||
pm4_calc_odd_parity_bit(type4_pkt_offset(pkt))) \
|
||||
&& ((((pkt) >> 7) & 0x1) == \
|
||||
pm4_calc_odd_parity_bit(type4_pkt_size(pkt))))
|
||||
|
||||
#define type4_pkt_offset(pkt) (((pkt) >> 8) & 0x7FFFF)
|
||||
#define type4_pkt_size(pkt) ((pkt) & 0x7F)
|
||||
|
||||
#define pkt_is_type7(pkt) \
|
||||
((((pkt) & 0xF0000000) == CP_TYPE7_PKT) && \
|
||||
(((pkt) & 0x0F000000) == 0) && \
|
||||
((((pkt) >> 23) & 0x1) == \
|
||||
pm4_calc_odd_parity_bit(cp_type7_opcode(pkt))) \
|
||||
&& ((((pkt) >> 15) & 0x1) == \
|
||||
pm4_calc_odd_parity_bit(type7_pkt_size(pkt))))
|
||||
|
||||
#define cp_type7_opcode(pkt) (((pkt) >> 16) & 0x7F)
|
||||
#define type7_pkt_size(pkt) ((pkt) & 0x3FFF)
|
||||
|
||||
#endif /* __CFFDEC_H__ */
|
|
@ -0,0 +1,370 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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 <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "redump.h"
|
||||
#include "disasm.h"
|
||||
#include "script.h"
|
||||
#include "io.h"
|
||||
#include "rnnutil.h"
|
||||
#include "pager.h"
|
||||
#include "buffers.h"
|
||||
#include "cffdec.h"
|
||||
|
||||
static struct cffdec_options options = {
|
||||
.gpu_id = 220,
|
||||
};
|
||||
|
||||
static bool needs_wfi = false;
|
||||
static bool is_blob = false;
|
||||
static int show_comp = false;
|
||||
static int interactive;
|
||||
static int vertices;
|
||||
|
||||
static int handle_file(const char *filename, int start, int end, int draw);
|
||||
|
||||
static void print_usage(const char *name)
|
||||
{
|
||||
fprintf(stderr, "Usage:\n\n"
|
||||
"\t%s [OPTSIONS]... FILE...\n\n"
|
||||
"Options:\n"
|
||||
"\t-v, --verbose - more verbose disassembly\n"
|
||||
"\t--dump-shaders - dump each shader to a raw file\n"
|
||||
"\t--no-color - disable colorized output (default for non-console\n"
|
||||
"\t output)\n"
|
||||
"\t--color - enable colorized output (default for tty output)\n"
|
||||
"\t--no-pager - disable pager (default for non-console output)\n"
|
||||
"\t--pager - enable pager (default for tty output)\n"
|
||||
"\t-s, --summary - don't show individual register writes, but just\n"
|
||||
"\t register values on draws\n"
|
||||
"\t-a, --allregs - show all registers (including ones not written\n"
|
||||
"\t since previous draw) on each draw\n"
|
||||
"\t-S, --start=N - start decoding from frame N\n"
|
||||
"\t-E, --end=N - stop decoding after frame N\n"
|
||||
"\t-F, --frame=N - decode only frame N\n"
|
||||
"\t-D, --draw=N - decode only draw N\n"
|
||||
"\t--textures - dump texture contents (if possible)\n"
|
||||
"\t-L, --script=LUA - run specified lua script to analyze state\n"
|
||||
"\t-q, --query=REG - query mode, dump only specified query registers on\n"
|
||||
"\t each draw; multiple --query/-q args can be given to\n"
|
||||
"\t dump multiple registers; register can be specified\n"
|
||||
"\t either by name or numeric offset\n"
|
||||
"\t--query-all - in query mode, show all queried regs on each draw\n"
|
||||
"\t (default query mode)\n"
|
||||
"\t--query-written - in query mode, show queried regs on draws if any of\n"
|
||||
"\t them have been written since previous draw\n"
|
||||
"\t--query-delta - in query mode, show queried regs on draws if any of\n"
|
||||
"\t them have changed since previous draw\n"
|
||||
"\t--query-compare - dump registers for BINNING vs GMEM/BYPASS per draw;\n"
|
||||
"\t only applicable for regs set via SDS group (a6xx+),\n"
|
||||
"\t implies --once, can be combined with --query-all,\n"
|
||||
"\t --query-written, or --query-delta\n"
|
||||
"\t--once - decode cmdstream only once (per draw mode); if same\n"
|
||||
"\t cmdstream is executed for each tile, this will decode\n"
|
||||
"\t it only for the first tile and skip the remainder,\n"
|
||||
"\t which can be useful when looking at state that does\n"
|
||||
"\t not change per tile\n"
|
||||
"\t--not-once - decode cmdstream for each IB (default)\n"
|
||||
"\t-h, --help - show this message\n"
|
||||
, name);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
static const struct option opts[] = {
|
||||
/* Long opts that simply set a flag (no corresponding short alias: */
|
||||
{ "dump-shaders", no_argument, &options.dump_shaders, 1 },
|
||||
{ "no-color", no_argument, &options.color, 0 },
|
||||
{ "color", no_argument, &options.color, 1 },
|
||||
{ "no-pager", no_argument, &interactive, 0 },
|
||||
{ "pager", no_argument, &interactive, 1 },
|
||||
{ "textures", no_argument, &options.dump_textures, 1 },
|
||||
{ "show-compositor", no_argument, &show_comp, 1 },
|
||||
{ "query-all", no_argument, &options.query_mode, QUERY_ALL },
|
||||
{ "query-written", no_argument, &options.query_mode, QUERY_WRITTEN },
|
||||
{ "query-delta", no_argument, &options.query_mode, QUERY_DELTA },
|
||||
{ "query-compare", no_argument, &options.query_compare, 1 },
|
||||
{ "once", no_argument, &options.once, 1 },
|
||||
{ "not-once", no_argument, &options.once, 0 },
|
||||
|
||||
/* Long opts with short alias: */
|
||||
{ "verbose", no_argument, 0, 'v' },
|
||||
{ "summary", no_argument, 0, 's' },
|
||||
{ "allregs", no_argument, 0, 'a' },
|
||||
{ "start", required_argument, 0, 'S' },
|
||||
{ "end", required_argument, 0, 'E' },
|
||||
{ "frame", required_argument, 0, 'F' },
|
||||
{ "draw", required_argument, 0, 'D' },
|
||||
{ "script", required_argument, 0, 'L' },
|
||||
{ "query", required_argument, 0, 'q' },
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret = -1;
|
||||
int start = 0, end = 0x7ffffff, draw = -1;
|
||||
int c;
|
||||
|
||||
interactive = isatty(STDOUT_FILENO);
|
||||
|
||||
options.color = interactive;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "vsaS:E:F:D:L:q:h", opts, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 0:
|
||||
/* option that set a flag, nothing to do */
|
||||
break;
|
||||
case 'v':
|
||||
disasm_set_debug(PRINT_RAW | EXPAND_REPEAT | PRINT_VERBOSE);
|
||||
break;
|
||||
case 's':
|
||||
options.summary = true;
|
||||
break;
|
||||
case 'a':
|
||||
options.allregs = true;
|
||||
break;
|
||||
case 'S':
|
||||
start = atoi(optarg);
|
||||
break;
|
||||
case 'E':
|
||||
end = atoi(optarg);
|
||||
break;
|
||||
case 'F':
|
||||
start = end = atoi(optarg);
|
||||
break;
|
||||
case 'D':
|
||||
draw = atoi(optarg);
|
||||
break;
|
||||
case 'L':
|
||||
options.script = optarg;
|
||||
if (script_load(options.script)) {
|
||||
errx(-1, "error loading %s\n", options.script);
|
||||
}
|
||||
break;
|
||||
case 'q':
|
||||
options.querystrs = realloc(options.querystrs,
|
||||
(options.nquery + 1) * sizeof(*options.querystrs));
|
||||
options.querystrs[options.nquery] = optarg;
|
||||
options.nquery++;
|
||||
interactive = 0;
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
print_usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (interactive) {
|
||||
pager_open();
|
||||
}
|
||||
|
||||
while (optind < argc) {
|
||||
ret = handle_file(argv[optind], start, end, draw);
|
||||
if (ret) {
|
||||
fprintf(stderr, "error reading: %s\n", argv[optind]);
|
||||
fprintf(stderr, "continuing..\n");
|
||||
}
|
||||
optind++;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
print_usage(argv[0]);
|
||||
|
||||
if ((options.query_mode || options.query_compare) && !options.nquery) {
|
||||
fprintf(stderr, "query options only valid in query mode!\n");
|
||||
print_usage(argv[0]);
|
||||
}
|
||||
|
||||
script_finish();
|
||||
|
||||
if (interactive) {
|
||||
pager_close();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void parse_addr(uint32_t *buf, int sz, unsigned int *len, uint64_t *gpuaddr)
|
||||
{
|
||||
*gpuaddr = buf[0];
|
||||
*len = buf[1];
|
||||
if (sz > 8)
|
||||
*gpuaddr |= ((uint64_t)(buf[2])) << 32;
|
||||
}
|
||||
|
||||
static int handle_file(const char *filename, int start, int end, int draw)
|
||||
{
|
||||
enum rd_sect_type type = RD_NONE;
|
||||
void *buf = NULL;
|
||||
struct io *io;
|
||||
int submit = 0, got_gpu_id = 0;
|
||||
int sz, ret = 0;
|
||||
bool needs_reset = false;
|
||||
bool skip = false;
|
||||
|
||||
options.draw_filter = draw;
|
||||
|
||||
cffdec_init(&options);
|
||||
|
||||
printf("Reading %s...\n", filename);
|
||||
|
||||
script_start_cmdstream(filename);
|
||||
|
||||
if (!strcmp(filename, "-"))
|
||||
io = io_openfd(0);
|
||||
else
|
||||
io = io_open(filename);
|
||||
|
||||
if (!io) {
|
||||
fprintf(stderr, "could not open: %s\n", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct {
|
||||
unsigned int len;
|
||||
uint64_t gpuaddr;
|
||||
} gpuaddr = {0};
|
||||
|
||||
while (true) {
|
||||
uint32_t arr[2];
|
||||
|
||||
ret = io_readn(io, arr, 8);
|
||||
if (ret <= 0)
|
||||
goto end;
|
||||
|
||||
while ((arr[0] == 0xffffffff) && (arr[1] == 0xffffffff)) {
|
||||
ret = io_readn(io, arr, 8);
|
||||
if (ret <= 0)
|
||||
goto end;
|
||||
}
|
||||
|
||||
type = arr[0];
|
||||
sz = arr[1];
|
||||
|
||||
if (sz < 0) {
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
needs_wfi = false;
|
||||
|
||||
buf = malloc(sz + 1);
|
||||
((char *)buf)[sz] = '\0';
|
||||
ret = io_readn(io, buf, sz);
|
||||
if (ret < 0)
|
||||
goto end;
|
||||
|
||||
switch(type) {
|
||||
case RD_TEST:
|
||||
printl(1, "test: %s\n", (char *)buf);
|
||||
break;
|
||||
case RD_CMD:
|
||||
is_blob = true;
|
||||
printl(2, "cmd: %s\n", (char *)buf);
|
||||
skip = false;
|
||||
if (!show_comp) {
|
||||
skip |= (strstr(buf, "fdperf") == buf);
|
||||
skip |= (strstr(buf, "chrome") == buf);
|
||||
skip |= (strstr(buf, "surfaceflinger") == buf);
|
||||
skip |= ((char *)buf)[0] == 'X';
|
||||
}
|
||||
break;
|
||||
case RD_VERT_SHADER:
|
||||
printl(2, "vertex shader:\n%s\n", (char *)buf);
|
||||
break;
|
||||
case RD_FRAG_SHADER:
|
||||
printl(2, "fragment shader:\n%s\n", (char *)buf);
|
||||
break;
|
||||
case RD_GPUADDR:
|
||||
if (needs_reset) {
|
||||
reset_buffers();
|
||||
needs_reset = false;
|
||||
}
|
||||
parse_addr(buf, sz, &gpuaddr.len, &gpuaddr.gpuaddr);
|
||||
break;
|
||||
case RD_BUFFER_CONTENTS:
|
||||
add_buffer(gpuaddr.gpuaddr, gpuaddr.len, buf);
|
||||
buf = NULL;
|
||||
break;
|
||||
case RD_CMDSTREAM_ADDR:
|
||||
if ((start <= submit) && (submit <= end)) {
|
||||
unsigned int sizedwords;
|
||||
uint64_t gpuaddr;
|
||||
parse_addr(buf, sz, &sizedwords, &gpuaddr);
|
||||
printl(2, "############################################################\n");
|
||||
printl(2, "cmdstream: %d dwords\n", sizedwords);
|
||||
if (!skip) {
|
||||
script_start_submit();
|
||||
dump_commands(hostptr(gpuaddr), sizedwords, 0);
|
||||
script_end_submit();
|
||||
}
|
||||
printl(2, "############################################################\n");
|
||||
printl(2, "vertices: %d\n", vertices);
|
||||
}
|
||||
needs_reset = true;
|
||||
submit++;
|
||||
break;
|
||||
case RD_GPU_ID:
|
||||
if (!got_gpu_id) {
|
||||
options.gpu_id = *((unsigned int *)buf);
|
||||
printl(2, "gpu_id: %d\n", options.gpu_id);
|
||||
cffdec_init(&options);
|
||||
got_gpu_id = 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
script_end_cmdstream();
|
||||
|
||||
io_close(io);
|
||||
fflush(stdout);
|
||||
|
||||
if (ret < 0) {
|
||||
printf("corrupt file\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,623 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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 <string.h>
|
||||
|
||||
#include "disasm.h"
|
||||
#include "instr-a2xx.h"
|
||||
#include "rnnutil.h"
|
||||
|
||||
static const char *levels[] = {
|
||||
"",
|
||||
"\t",
|
||||
"\t\t",
|
||||
"\t\t\t",
|
||||
"\t\t\t\t",
|
||||
"\t\t\t\t\t",
|
||||
"\t\t\t\t\t\t",
|
||||
"\t\t\t\t\t\t\t",
|
||||
"\t\t\t\t\t\t\t\t",
|
||||
"\t\t\t\t\t\t\t\t\t",
|
||||
"x",
|
||||
"x",
|
||||
"x",
|
||||
"x",
|
||||
"x",
|
||||
"x",
|
||||
};
|
||||
|
||||
enum debug_t debug;
|
||||
|
||||
static struct rnn *rnn;
|
||||
|
||||
/*
|
||||
* ALU instructions:
|
||||
*/
|
||||
|
||||
static const char chan_names[] = {
|
||||
'x', 'y', 'z', 'w',
|
||||
/* these only apply to FETCH dst's: */
|
||||
'0', '1', '?', '_',
|
||||
};
|
||||
|
||||
static void print_srcreg(uint32_t num, uint32_t type,
|
||||
uint32_t swiz, uint32_t negate, uint32_t abs)
|
||||
{
|
||||
if (negate)
|
||||
printf("-");
|
||||
if (abs)
|
||||
printf("|");
|
||||
printf("%c%u", type ? 'R' : 'C', num);
|
||||
if (swiz) {
|
||||
int i;
|
||||
printf(".");
|
||||
for (i = 0; i < 4; i++) {
|
||||
printf("%c", chan_names[(swiz + i) & 0x3]);
|
||||
swiz >>= 2;
|
||||
}
|
||||
}
|
||||
if (abs)
|
||||
printf("|");
|
||||
}
|
||||
|
||||
static void print_dstreg(uint32_t num, uint32_t mask, uint32_t dst_exp)
|
||||
{
|
||||
printf("%s%u", dst_exp ? "export" : "R", num);
|
||||
if (mask != 0xf) {
|
||||
int i;
|
||||
printf(".");
|
||||
for (i = 0; i < 4; i++) {
|
||||
printf("%c", (mask & 0x1) ? chan_names[i] : '_');
|
||||
mask >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void print_export_comment(uint32_t num, enum shader_t type)
|
||||
{
|
||||
const char *name = NULL;
|
||||
switch (type) {
|
||||
case SHADER_VERTEX:
|
||||
switch (num) {
|
||||
case 62: name = "gl_Position"; break;
|
||||
case 63: name = "gl_PointSize"; break;
|
||||
}
|
||||
break;
|
||||
case SHADER_FRAGMENT:
|
||||
switch (num) {
|
||||
case 0: name = "gl_FragColor"; break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* if we had a symbol table here, we could look
|
||||
* up the name of the varying..
|
||||
*/
|
||||
if (name) {
|
||||
printf("\t; %s", name);
|
||||
}
|
||||
}
|
||||
|
||||
struct {
|
||||
uint32_t num_srcs;
|
||||
const char *name;
|
||||
} vector_instructions[0x20] = {
|
||||
#define INSTR(opc, num_srcs) [opc] = { num_srcs, #opc }
|
||||
INSTR(ADDv, 2),
|
||||
INSTR(MULv, 2),
|
||||
INSTR(MAXv, 2),
|
||||
INSTR(MINv, 2),
|
||||
INSTR(SETEv, 2),
|
||||
INSTR(SETGTv, 2),
|
||||
INSTR(SETGTEv, 2),
|
||||
INSTR(SETNEv, 2),
|
||||
INSTR(FRACv, 1),
|
||||
INSTR(TRUNCv, 1),
|
||||
INSTR(FLOORv, 1),
|
||||
INSTR(MULADDv, 3),
|
||||
INSTR(CNDEv, 3),
|
||||
INSTR(CNDGTEv, 3),
|
||||
INSTR(CNDGTv, 3),
|
||||
INSTR(DOT4v, 2),
|
||||
INSTR(DOT3v, 2),
|
||||
INSTR(DOT2ADDv, 3), // ???
|
||||
INSTR(CUBEv, 2),
|
||||
INSTR(MAX4v, 1),
|
||||
INSTR(PRED_SETE_PUSHv, 2),
|
||||
INSTR(PRED_SETNE_PUSHv, 2),
|
||||
INSTR(PRED_SETGT_PUSHv, 2),
|
||||
INSTR(PRED_SETGTE_PUSHv, 2),
|
||||
INSTR(KILLEv, 2),
|
||||
INSTR(KILLGTv, 2),
|
||||
INSTR(KILLGTEv, 2),
|
||||
INSTR(KILLNEv, 2),
|
||||
INSTR(DSTv, 2),
|
||||
INSTR(MOVAv, 1),
|
||||
}, scalar_instructions[0x40] = {
|
||||
INSTR(ADDs, 1),
|
||||
INSTR(ADD_PREVs, 1),
|
||||
INSTR(MULs, 1),
|
||||
INSTR(MUL_PREVs, 1),
|
||||
INSTR(MUL_PREV2s, 1),
|
||||
INSTR(MAXs, 1),
|
||||
INSTR(MINs, 1),
|
||||
INSTR(SETEs, 1),
|
||||
INSTR(SETGTs, 1),
|
||||
INSTR(SETGTEs, 1),
|
||||
INSTR(SETNEs, 1),
|
||||
INSTR(FRACs, 1),
|
||||
INSTR(TRUNCs, 1),
|
||||
INSTR(FLOORs, 1),
|
||||
INSTR(EXP_IEEE, 1),
|
||||
INSTR(LOG_CLAMP, 1),
|
||||
INSTR(LOG_IEEE, 1),
|
||||
INSTR(RECIP_CLAMP, 1),
|
||||
INSTR(RECIP_FF, 1),
|
||||
INSTR(RECIP_IEEE, 1),
|
||||
INSTR(RECIPSQ_CLAMP, 1),
|
||||
INSTR(RECIPSQ_FF, 1),
|
||||
INSTR(RECIPSQ_IEEE, 1),
|
||||
INSTR(MOVAs, 1),
|
||||
INSTR(MOVA_FLOORs, 1),
|
||||
INSTR(SUBs, 1),
|
||||
INSTR(SUB_PREVs, 1),
|
||||
INSTR(PRED_SETEs, 1),
|
||||
INSTR(PRED_SETNEs, 1),
|
||||
INSTR(PRED_SETGTs, 1),
|
||||
INSTR(PRED_SETGTEs, 1),
|
||||
INSTR(PRED_SET_INVs, 1),
|
||||
INSTR(PRED_SET_POPs, 1),
|
||||
INSTR(PRED_SET_CLRs, 1),
|
||||
INSTR(PRED_SET_RESTOREs, 1),
|
||||
INSTR(KILLEs, 1),
|
||||
INSTR(KILLGTs, 1),
|
||||
INSTR(KILLGTEs, 1),
|
||||
INSTR(KILLNEs, 1),
|
||||
INSTR(KILLONEs, 1),
|
||||
INSTR(SQRT_IEEE, 1),
|
||||
INSTR(MUL_CONST_0, 1),
|
||||
INSTR(MUL_CONST_1, 1),
|
||||
INSTR(ADD_CONST_0, 1),
|
||||
INSTR(ADD_CONST_1, 1),
|
||||
INSTR(SUB_CONST_0, 1),
|
||||
INSTR(SUB_CONST_1, 1),
|
||||
INSTR(SIN, 1),
|
||||
INSTR(COS, 1),
|
||||
INSTR(RETAIN_PREV, 1),
|
||||
#undef INSTR
|
||||
};
|
||||
|
||||
static int disasm_alu(uint32_t *dwords, uint32_t alu_off,
|
||||
int level, int sync, enum shader_t type)
|
||||
{
|
||||
instr_alu_t *alu = (instr_alu_t *)dwords;
|
||||
|
||||
printf("%s", levels[level]);
|
||||
if (debug & PRINT_RAW) {
|
||||
printf("%02x: %08x %08x %08x\t", alu_off,
|
||||
dwords[0], dwords[1], dwords[2]);
|
||||
}
|
||||
|
||||
printf(" %sALU:\t", sync ? "(S)" : " ");
|
||||
|
||||
printf("%s", vector_instructions[alu->vector_opc].name);
|
||||
|
||||
if (alu->pred_select & 0x2) {
|
||||
/* seems to work similar to conditional execution in ARM instruction
|
||||
* set, so let's use a similar syntax for now:
|
||||
*/
|
||||
printf((alu->pred_select & 0x1) ? "EQ" : "NE");
|
||||
}
|
||||
|
||||
printf("\t");
|
||||
|
||||
print_dstreg(alu->vector_dest, alu->vector_write_mask, alu->export_data);
|
||||
printf(" = ");
|
||||
if (vector_instructions[alu->vector_opc].num_srcs == 3) {
|
||||
print_srcreg(alu->src3_reg, alu->src3_sel, alu->src3_swiz,
|
||||
alu->src3_reg_negate, alu->src3_reg_abs);
|
||||
printf(", ");
|
||||
}
|
||||
print_srcreg(alu->src1_reg, alu->src1_sel, alu->src1_swiz,
|
||||
alu->src1_reg_negate, alu->src1_reg_abs);
|
||||
if (vector_instructions[alu->vector_opc].num_srcs > 1) {
|
||||
printf(", ");
|
||||
print_srcreg(alu->src2_reg, alu->src2_sel, alu->src2_swiz,
|
||||
alu->src2_reg_negate, alu->src2_reg_abs);
|
||||
}
|
||||
|
||||
if (alu->vector_clamp)
|
||||
printf(" CLAMP");
|
||||
|
||||
if (alu->export_data)
|
||||
print_export_comment(alu->vector_dest, type);
|
||||
|
||||
printf("\n");
|
||||
|
||||
if (alu->scalar_write_mask || !alu->vector_write_mask) {
|
||||
/* 2nd optional scalar op: */
|
||||
|
||||
printf("%s", levels[level]);
|
||||
if (debug & PRINT_RAW)
|
||||
printf(" \t");
|
||||
|
||||
if (scalar_instructions[alu->scalar_opc].name) {
|
||||
printf("\t \t%s\t", scalar_instructions[alu->scalar_opc].name);
|
||||
} else {
|
||||
printf("\t \tOP(%u)\t", alu->scalar_opc);
|
||||
}
|
||||
|
||||
print_dstreg(alu->scalar_dest, alu->scalar_write_mask, alu->export_data);
|
||||
printf(" = ");
|
||||
print_srcreg(alu->src3_reg, alu->src3_sel, alu->src3_swiz,
|
||||
alu->src3_reg_negate, alu->src3_reg_abs);
|
||||
// TODO ADD/MUL must have another src?!?
|
||||
if (alu->scalar_clamp)
|
||||
printf(" CLAMP");
|
||||
if (alu->export_data)
|
||||
print_export_comment(alu->scalar_dest, type);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* FETCH instructions:
|
||||
*/
|
||||
|
||||
static void print_fetch_dst(uint32_t dst_reg, uint32_t dst_swiz)
|
||||
{
|
||||
int i;
|
||||
printf("\tR%u.", dst_reg);
|
||||
for (i = 0; i < 4; i++) {
|
||||
printf("%c", chan_names[dst_swiz & 0x7]);
|
||||
dst_swiz >>= 3;
|
||||
}
|
||||
}
|
||||
|
||||
static void print_fetch_vtx(instr_fetch_t *fetch)
|
||||
{
|
||||
instr_fetch_vtx_t *vtx = &fetch->vtx;
|
||||
|
||||
if (vtx->pred_select) {
|
||||
/* seems to work similar to conditional execution in ARM instruction
|
||||
* set, so let's use a similar syntax for now:
|
||||
*/
|
||||
printf(vtx->pred_condition ? "EQ" : "NE");
|
||||
}
|
||||
|
||||
print_fetch_dst(vtx->dst_reg, vtx->dst_swiz);
|
||||
printf(" = R%u.", vtx->src_reg);
|
||||
printf("%c", chan_names[vtx->src_swiz & 0x3]);
|
||||
|
||||
const char *fmt = rnn_enumname(rnn, "a2xx_sq_surfaceformat", vtx->format);
|
||||
if (fmt) {
|
||||
printf(" %s", fmt);
|
||||
} else {
|
||||
printf(" TYPE(0x%x)", vtx->format);
|
||||
}
|
||||
printf(" %s", vtx->format_comp_all ? "SIGNED" : "UNSIGNED");
|
||||
if (!vtx->num_format_all)
|
||||
printf(" NORMALIZED");
|
||||
printf(" STRIDE(%u)", vtx->stride);
|
||||
if (vtx->offset)
|
||||
printf(" OFFSET(%u)", vtx->offset);
|
||||
printf(" CONST(%u, %u)", vtx->const_index, vtx->const_index_sel);
|
||||
if (0) {
|
||||
// XXX
|
||||
printf(" src_reg_am=%u", vtx->src_reg_am);
|
||||
printf(" dst_reg_am=%u", vtx->dst_reg_am);
|
||||
printf(" num_format_all=%u", vtx->num_format_all);
|
||||
printf(" signed_rf_mode_all=%u", vtx->signed_rf_mode_all);
|
||||
printf(" exp_adjust_all=%u", vtx->exp_adjust_all);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_fetch_tex(instr_fetch_t *fetch)
|
||||
{
|
||||
static const char *filter[] = {
|
||||
[TEX_FILTER_POINT] = "POINT",
|
||||
[TEX_FILTER_LINEAR] = "LINEAR",
|
||||
[TEX_FILTER_BASEMAP] = "BASEMAP",
|
||||
};
|
||||
static const char *aniso_filter[] = {
|
||||
[ANISO_FILTER_DISABLED] = "DISABLED",
|
||||
[ANISO_FILTER_MAX_1_1] = "MAX_1_1",
|
||||
[ANISO_FILTER_MAX_2_1] = "MAX_2_1",
|
||||
[ANISO_FILTER_MAX_4_1] = "MAX_4_1",
|
||||
[ANISO_FILTER_MAX_8_1] = "MAX_8_1",
|
||||
[ANISO_FILTER_MAX_16_1] = "MAX_16_1",
|
||||
};
|
||||
static const char *arbitrary_filter[] = {
|
||||
[ARBITRARY_FILTER_2X4_SYM] = "2x4_SYM",
|
||||
[ARBITRARY_FILTER_2X4_ASYM] = "2x4_ASYM",
|
||||
[ARBITRARY_FILTER_4X2_SYM] = "4x2_SYM",
|
||||
[ARBITRARY_FILTER_4X2_ASYM] = "4x2_ASYM",
|
||||
[ARBITRARY_FILTER_4X4_SYM] = "4x4_SYM",
|
||||
[ARBITRARY_FILTER_4X4_ASYM] = "4x4_ASYM",
|
||||
};
|
||||
static const char *sample_loc[] = {
|
||||
[SAMPLE_CENTROID] = "CENTROID",
|
||||
[SAMPLE_CENTER] = "CENTER",
|
||||
};
|
||||
instr_fetch_tex_t *tex = &fetch->tex;
|
||||
uint32_t src_swiz = tex->src_swiz;
|
||||
int i;
|
||||
|
||||
if (tex->pred_select) {
|
||||
/* seems to work similar to conditional execution in ARM instruction
|
||||
* set, so let's use a similar syntax for now:
|
||||
*/
|
||||
printf(tex->pred_condition ? "EQ" : "NE");
|
||||
}
|
||||
|
||||
print_fetch_dst(tex->dst_reg, tex->dst_swiz);
|
||||
printf(" = R%u.", tex->src_reg);
|
||||
for (i = 0; i < 3; i++) {
|
||||
printf("%c", chan_names[src_swiz & 0x3]);
|
||||
src_swiz >>= 2;
|
||||
}
|
||||
printf(" CONST(%u)", tex->const_idx);
|
||||
if (tex->fetch_valid_only)
|
||||
printf(" VALID_ONLY");
|
||||
if (tex->tx_coord_denorm)
|
||||
printf(" DENORM");
|
||||
if (tex->mag_filter != TEX_FILTER_USE_FETCH_CONST)
|
||||
printf(" MAG(%s)", filter[tex->mag_filter]);
|
||||
if (tex->min_filter != TEX_FILTER_USE_FETCH_CONST)
|
||||
printf(" MIN(%s)", filter[tex->min_filter]);
|
||||
if (tex->mip_filter != TEX_FILTER_USE_FETCH_CONST)
|
||||
printf(" MIP(%s)", filter[tex->mip_filter]);
|
||||
if (tex->aniso_filter != ANISO_FILTER_USE_FETCH_CONST)
|
||||
printf(" ANISO(%s)", aniso_filter[tex->aniso_filter]);
|
||||
if (tex->arbitrary_filter != ARBITRARY_FILTER_USE_FETCH_CONST)
|
||||
printf(" ARBITRARY(%s)", arbitrary_filter[tex->arbitrary_filter]);
|
||||
if (tex->vol_mag_filter != TEX_FILTER_USE_FETCH_CONST)
|
||||
printf(" VOL_MAG(%s)", filter[tex->vol_mag_filter]);
|
||||
if (tex->vol_min_filter != TEX_FILTER_USE_FETCH_CONST)
|
||||
printf(" VOL_MIN(%s)", filter[tex->vol_min_filter]);
|
||||
if (!tex->use_comp_lod) {
|
||||
printf(" LOD(%u)", tex->use_comp_lod);
|
||||
printf(" LOD_BIAS(%u)", tex->lod_bias);
|
||||
}
|
||||
if (tex->use_reg_lod) {
|
||||
printf(" REG_LOD(%u)", tex->use_reg_lod);
|
||||
}
|
||||
if (tex->use_reg_gradients)
|
||||
printf(" USE_REG_GRADIENTS");
|
||||
printf(" LOCATION(%s)", sample_loc[tex->sample_location]);
|
||||
if (tex->offset_x || tex->offset_y || tex->offset_z)
|
||||
printf(" OFFSET(%u,%u,%u)", tex->offset_x, tex->offset_y, tex->offset_z);
|
||||
}
|
||||
|
||||
struct {
|
||||
const char *name;
|
||||
void (*fxn)(instr_fetch_t *cf);
|
||||
} fetch_instructions[] = {
|
||||
#define INSTR(opc, name, fxn) [opc] = { name, fxn }
|
||||
INSTR(VTX_FETCH, "VERTEX", print_fetch_vtx),
|
||||
INSTR(TEX_FETCH, "SAMPLE", print_fetch_tex),
|
||||
INSTR(TEX_GET_BORDER_COLOR_FRAC, "?", print_fetch_tex),
|
||||
INSTR(TEX_GET_COMP_TEX_LOD, "?", print_fetch_tex),
|
||||
INSTR(TEX_GET_GRADIENTS, "?", print_fetch_tex),
|
||||
INSTR(TEX_GET_WEIGHTS, "?", print_fetch_tex),
|
||||
INSTR(TEX_SET_TEX_LOD, "SET_TEX_LOD", print_fetch_tex),
|
||||
INSTR(TEX_SET_GRADIENTS_H, "?", print_fetch_tex),
|
||||
INSTR(TEX_SET_GRADIENTS_V, "?", print_fetch_tex),
|
||||
INSTR(TEX_RESERVED_4, "?", print_fetch_tex),
|
||||
#undef INSTR
|
||||
};
|
||||
|
||||
static int disasm_fetch(uint32_t *dwords, uint32_t alu_off, int level, int sync)
|
||||
{
|
||||
instr_fetch_t *fetch = (instr_fetch_t *)dwords;
|
||||
|
||||
printf("%s", levels[level]);
|
||||
if (debug & PRINT_RAW) {
|
||||
printf("%02x: %08x %08x %08x\t", alu_off,
|
||||
dwords[0], dwords[1], dwords[2]);
|
||||
}
|
||||
|
||||
printf(" %sFETCH:\t", sync ? "(S)" : " ");
|
||||
printf("%s", fetch_instructions[fetch->opc].name);
|
||||
fetch_instructions[fetch->opc].fxn(fetch);
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* CF instructions:
|
||||
*/
|
||||
|
||||
static int cf_exec(instr_cf_t *cf)
|
||||
{
|
||||
return (cf->opc == EXEC) ||
|
||||
(cf->opc == EXEC_END) ||
|
||||
(cf->opc == COND_EXEC) ||
|
||||
(cf->opc == COND_EXEC_END) ||
|
||||
(cf->opc == COND_PRED_EXEC) ||
|
||||
(cf->opc == COND_PRED_EXEC_END) ||
|
||||
(cf->opc == COND_EXEC_PRED_CLEAN) ||
|
||||
(cf->opc == COND_EXEC_PRED_CLEAN_END);
|
||||
}
|
||||
|
||||
static int cf_cond_exec(instr_cf_t *cf)
|
||||
{
|
||||
return (cf->opc == COND_EXEC) ||
|
||||
(cf->opc == COND_EXEC_END) ||
|
||||
(cf->opc == COND_PRED_EXEC) ||
|
||||
(cf->opc == COND_PRED_EXEC_END) ||
|
||||
(cf->opc == COND_EXEC_PRED_CLEAN) ||
|
||||
(cf->opc == COND_EXEC_PRED_CLEAN_END);
|
||||
}
|
||||
|
||||
static void print_cf_nop(instr_cf_t *cf)
|
||||
{
|
||||
}
|
||||
|
||||
static void print_cf_exec(instr_cf_t *cf)
|
||||
{
|
||||
printf(" ADDR(0x%x) CNT(0x%x)", cf->exec.address, cf->exec.count);
|
||||
if (cf->exec.yeild)
|
||||
printf(" YIELD");
|
||||
if (cf->exec.vc)
|
||||
printf(" VC(0x%x)", cf->exec.vc);
|
||||
if (cf->exec.bool_addr)
|
||||
printf(" BOOL_ADDR(0x%x)", cf->exec.bool_addr);
|
||||
if (cf->exec.address_mode == ABSOLUTE_ADDR)
|
||||
printf(" ABSOLUTE_ADDR");
|
||||
if (cf_cond_exec(cf))
|
||||
printf(" COND(%d)", cf->exec.condition);
|
||||
}
|
||||
|
||||
static void print_cf_loop(instr_cf_t *cf)
|
||||
{
|
||||
printf(" ADDR(0x%x) LOOP_ID(%d)", cf->loop.address, cf->loop.loop_id);
|
||||
if (cf->loop.address_mode == ABSOLUTE_ADDR)
|
||||
printf(" ABSOLUTE_ADDR");
|
||||
}
|
||||
|
||||
static void print_cf_jmp_call(instr_cf_t *cf)
|
||||
{
|
||||
printf(" ADDR(0x%x) DIR(%d)", cf->jmp_call.address, cf->jmp_call.direction);
|
||||
if (cf->jmp_call.force_call)
|
||||
printf(" FORCE_CALL");
|
||||
if (cf->jmp_call.predicated_jmp)
|
||||
printf(" COND(%d)", cf->jmp_call.condition);
|
||||
if (cf->jmp_call.bool_addr)
|
||||
printf(" BOOL_ADDR(0x%x)", cf->jmp_call.bool_addr);
|
||||
if (cf->jmp_call.address_mode == ABSOLUTE_ADDR)
|
||||
printf(" ABSOLUTE_ADDR");
|
||||
}
|
||||
|
||||
static void print_cf_alloc(instr_cf_t *cf)
|
||||
{
|
||||
static const char *bufname[] = {
|
||||
[SQ_NO_ALLOC] = "NO ALLOC",
|
||||
[SQ_POSITION] = "POSITION",
|
||||
[SQ_PARAMETER_PIXEL] = "PARAM/PIXEL",
|
||||
[SQ_MEMORY] = "MEMORY",
|
||||
};
|
||||
printf(" %s SIZE(0x%x)", bufname[cf->alloc.buffer_select], cf->alloc.size);
|
||||
if (cf->alloc.no_serial)
|
||||
printf(" NO_SERIAL");
|
||||
if (cf->alloc.alloc_mode) // ???
|
||||
printf(" ALLOC_MODE");
|
||||
}
|
||||
|
||||
struct {
|
||||
const char *name;
|
||||
void (*fxn)(instr_cf_t *cf);
|
||||
} cf_instructions[] = {
|
||||
#define INSTR(opc, fxn) [opc] = { #opc, fxn }
|
||||
INSTR(NOP, print_cf_nop),
|
||||
INSTR(EXEC, print_cf_exec),
|
||||
INSTR(EXEC_END, print_cf_exec),
|
||||
INSTR(COND_EXEC, print_cf_exec),
|
||||
INSTR(COND_EXEC_END, print_cf_exec),
|
||||
INSTR(COND_PRED_EXEC, print_cf_exec),
|
||||
INSTR(COND_PRED_EXEC_END, print_cf_exec),
|
||||
INSTR(LOOP_START, print_cf_loop),
|
||||
INSTR(LOOP_END, print_cf_loop),
|
||||
INSTR(COND_CALL, print_cf_jmp_call),
|
||||
INSTR(RETURN, print_cf_jmp_call),
|
||||
INSTR(COND_JMP, print_cf_jmp_call),
|
||||
INSTR(ALLOC, print_cf_alloc),
|
||||
INSTR(COND_EXEC_PRED_CLEAN, print_cf_exec),
|
||||
INSTR(COND_EXEC_PRED_CLEAN_END, print_cf_exec),
|
||||
INSTR(MARK_VS_FETCH_DONE, print_cf_nop), // ??
|
||||
#undef INSTR
|
||||
};
|
||||
|
||||
static void print_cf(instr_cf_t *cf, int level)
|
||||
{
|
||||
printf("%s", levels[level]);
|
||||
if (debug & PRINT_RAW) {
|
||||
uint16_t *words = (uint16_t *)cf;
|
||||
printf(" %04x %04x %04x \t",
|
||||
words[0], words[1], words[2]);
|
||||
}
|
||||
printf("%s", cf_instructions[cf->opc].name);
|
||||
cf_instructions[cf->opc].fxn(cf);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* The adreno shader microcode consists of two parts:
|
||||
* 1) A CF (control-flow) program, at the header of the compiled shader,
|
||||
* which refers to ALU/FETCH instructions that follow it by address.
|
||||
* 2) ALU and FETCH instructions
|
||||
*/
|
||||
|
||||
int disasm_a2xx(uint32_t *dwords, int sizedwords, int level, enum shader_t type)
|
||||
{
|
||||
instr_cf_t *cfs = (instr_cf_t *)dwords;
|
||||
int idx, max_idx;
|
||||
|
||||
if (!rnn) {
|
||||
rnn = rnn_new(1);
|
||||
rnn_load(rnn, "a2xx");
|
||||
}
|
||||
|
||||
for (idx = 0; ; idx++) {
|
||||
instr_cf_t *cf = &cfs[idx];
|
||||
if (cf_exec(cf)) {
|
||||
max_idx = 2 * cf->exec.address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (idx = 0; idx < max_idx; idx++) {
|
||||
instr_cf_t *cf = &cfs[idx];
|
||||
|
||||
print_cf(cf, level);
|
||||
|
||||
if (cf_exec(cf)) {
|
||||
uint32_t sequence = cf->exec.serialize;
|
||||
uint32_t i;
|
||||
for (i = 0; i < cf->exec.count; i++) {
|
||||
uint32_t alu_off = (cf->exec.address + i);
|
||||
if (sequence & 0x1) {
|
||||
disasm_fetch(dwords + alu_off * 3, alu_off, level, sequence & 0x2);
|
||||
} else {
|
||||
disasm_alu(dwords + alu_off * 3, alu_off, level, sequence & 0x2, type);
|
||||
}
|
||||
sequence >>= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void disasm_set_debug(enum debug_t d)
|
||||
{
|
||||
debug = d;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright © 2012 Rob Clark <robclark@freedesktop.org>
|
||||
*
|
||||
* 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 DISASM_H_
|
||||
#define DISASM_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
enum shader_t {
|
||||
SHADER_VERTEX,
|
||||
SHADER_TCS,
|
||||
SHADER_TES,
|
||||
SHADER_GEOM,
|
||||
SHADER_FRAGMENT,
|
||||
SHADER_COMPUTE,
|
||||
};
|
||||
|
||||
/* bitmask of debug flags */
|
||||
enum debug_t {
|
||||
PRINT_RAW = 0x1, /* dump raw hexdump */
|
||||
PRINT_VERBOSE = 0x2,
|
||||
EXPAND_REPEAT = 0x4,
|
||||
};
|
||||
|
||||
struct shader_stats {
|
||||
/* instructions counts rpnN, and instlen does not */
|
||||
int instructions, instlen;
|
||||
int nops;
|
||||
int ss, sy;
|
||||
int constlen;
|
||||
};
|
||||
|
||||
int disasm_a2xx(uint32_t *dwords, int sizedwords, int level, enum shader_t type);
|
||||
int disasm_a3xx(uint32_t *dwords, int sizedwords, int level, FILE *out, unsigned gpu_id);
|
||||
int disasm_a3xx_stat(uint32_t *dwords, int sizedwords, int level, FILE *out,
|
||||
unsigned gpu_id, struct shader_stats *stats);
|
||||
void disasm_set_debug(enum debug_t debug);
|
||||
|
||||
#endif /* DISASM_H_ */
|
|
@ -0,0 +1,385 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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 INSTR_A2XX_H_
|
||||
#define INSTR_A2XX_H_
|
||||
|
||||
#define PACKED __attribute__((__packed__))
|
||||
|
||||
|
||||
/*
|
||||
* ALU instructions:
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
ADDs = 0,
|
||||
ADD_PREVs = 1,
|
||||
MULs = 2,
|
||||
MUL_PREVs = 3,
|
||||
MUL_PREV2s = 4,
|
||||
MAXs = 5,
|
||||
MINs = 6,
|
||||
SETEs = 7,
|
||||
SETGTs = 8,
|
||||
SETGTEs = 9,
|
||||
SETNEs = 10,
|
||||
FRACs = 11,
|
||||
TRUNCs = 12,
|
||||
FLOORs = 13,
|
||||
EXP_IEEE = 14,
|
||||
LOG_CLAMP = 15,
|
||||
LOG_IEEE = 16,
|
||||
RECIP_CLAMP = 17,
|
||||
RECIP_FF = 18,
|
||||
RECIP_IEEE = 19,
|
||||
RECIPSQ_CLAMP = 20,
|
||||
RECIPSQ_FF = 21,
|
||||
RECIPSQ_IEEE = 22,
|
||||
MOVAs = 23,
|
||||
MOVA_FLOORs = 24,
|
||||
SUBs = 25,
|
||||
SUB_PREVs = 26,
|
||||
PRED_SETEs = 27,
|
||||
PRED_SETNEs = 28,
|
||||
PRED_SETGTs = 29,
|
||||
PRED_SETGTEs = 30,
|
||||
PRED_SET_INVs = 31,
|
||||
PRED_SET_POPs = 32,
|
||||
PRED_SET_CLRs = 33,
|
||||
PRED_SET_RESTOREs = 34,
|
||||
KILLEs = 35,
|
||||
KILLGTs = 36,
|
||||
KILLGTEs = 37,
|
||||
KILLNEs = 38,
|
||||
KILLONEs = 39,
|
||||
SQRT_IEEE = 40,
|
||||
MUL_CONST_0 = 42,
|
||||
MUL_CONST_1 = 43,
|
||||
ADD_CONST_0 = 44,
|
||||
ADD_CONST_1 = 45,
|
||||
SUB_CONST_0 = 46,
|
||||
SUB_CONST_1 = 47,
|
||||
SIN = 48,
|
||||
COS = 49,
|
||||
RETAIN_PREV = 50,
|
||||
} instr_scalar_opc_t;
|
||||
|
||||
typedef enum {
|
||||
ADDv = 0,
|
||||
MULv = 1,
|
||||
MAXv = 2,
|
||||
MINv = 3,
|
||||
SETEv = 4,
|
||||
SETGTv = 5,
|
||||
SETGTEv = 6,
|
||||
SETNEv = 7,
|
||||
FRACv = 8,
|
||||
TRUNCv = 9,
|
||||
FLOORv = 10,
|
||||
MULADDv = 11,
|
||||
CNDEv = 12,
|
||||
CNDGTEv = 13,
|
||||
CNDGTv = 14,
|
||||
DOT4v = 15,
|
||||
DOT3v = 16,
|
||||
DOT2ADDv = 17,
|
||||
CUBEv = 18,
|
||||
MAX4v = 19,
|
||||
PRED_SETE_PUSHv = 20,
|
||||
PRED_SETNE_PUSHv = 21,
|
||||
PRED_SETGT_PUSHv = 22,
|
||||
PRED_SETGTE_PUSHv = 23,
|
||||
KILLEv = 24,
|
||||
KILLGTv = 25,
|
||||
KILLGTEv = 26,
|
||||
KILLNEv = 27,
|
||||
DSTv = 28,
|
||||
MOVAv = 29,
|
||||
} instr_vector_opc_t;
|
||||
|
||||
typedef struct PACKED {
|
||||
/* dword0: */
|
||||
uint8_t vector_dest : 6;
|
||||
uint8_t vector_dest_rel : 1;
|
||||
uint8_t low_precision_16b_fp : 1;
|
||||
uint8_t scalar_dest : 6;
|
||||
uint8_t scalar_dest_rel : 1;
|
||||
uint8_t export_data : 1;
|
||||
uint8_t vector_write_mask : 4;
|
||||
uint8_t scalar_write_mask : 4;
|
||||
uint8_t vector_clamp : 1;
|
||||
uint8_t scalar_clamp : 1;
|
||||
instr_scalar_opc_t scalar_opc : 6;
|
||||
/* dword1: */
|
||||
uint8_t src3_swiz : 8;
|
||||
uint8_t src2_swiz : 8;
|
||||
uint8_t src1_swiz : 8;
|
||||
uint8_t src3_reg_negate : 1;
|
||||
uint8_t src2_reg_negate : 1;
|
||||
uint8_t src1_reg_negate : 1;
|
||||
uint8_t pred_select : 2;
|
||||
uint8_t relative_addr : 1;
|
||||
uint8_t const_1_rel_abs : 1;
|
||||
uint8_t const_0_rel_abs : 1;
|
||||
/* dword2: */
|
||||
uint8_t src3_reg : 6;
|
||||
uint8_t src3_reg_select : 1;
|
||||
uint8_t src3_reg_abs : 1;
|
||||
uint8_t src2_reg : 6;
|
||||
uint8_t src2_reg_select : 1;
|
||||
uint8_t src2_reg_abs : 1;
|
||||
uint8_t src1_reg : 6;
|
||||
uint8_t src1_reg_select : 1;
|
||||
uint8_t src1_reg_abs : 1;
|
||||
instr_vector_opc_t vector_opc : 5;
|
||||
uint8_t src3_sel : 1;
|
||||
uint8_t src2_sel : 1;
|
||||
uint8_t src1_sel : 1;
|
||||
} instr_alu_t;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* CF instructions:
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
NOP = 0,
|
||||
EXEC = 1,
|
||||
EXEC_END = 2,
|
||||
COND_EXEC = 3,
|
||||
COND_EXEC_END = 4,
|
||||
COND_PRED_EXEC = 5,
|
||||
COND_PRED_EXEC_END = 6,
|
||||
LOOP_START = 7,
|
||||
LOOP_END = 8,
|
||||
COND_CALL = 9,
|
||||
RETURN = 10,
|
||||
COND_JMP = 11,
|
||||
ALLOC = 12,
|
||||
COND_EXEC_PRED_CLEAN = 13,
|
||||
COND_EXEC_PRED_CLEAN_END = 14,
|
||||
MARK_VS_FETCH_DONE = 15,
|
||||
} instr_cf_opc_t;
|
||||
|
||||
typedef enum {
|
||||
RELATIVE_ADDR = 0,
|
||||
ABSOLUTE_ADDR = 1,
|
||||
} instr_addr_mode_t;
|
||||
|
||||
typedef enum {
|
||||
SQ_NO_ALLOC = 0,
|
||||
SQ_POSITION = 1,
|
||||
SQ_PARAMETER_PIXEL = 2,
|
||||
SQ_MEMORY = 3,
|
||||
} instr_alloc_type_t;
|
||||
|
||||
typedef struct PACKED {
|
||||
uint16_t address : 9;
|
||||
uint8_t reserved0 : 3;
|
||||
uint8_t count : 3;
|
||||
uint8_t yeild : 1;
|
||||
uint16_t serialize : 12;
|
||||
uint8_t vc : 6; /* vertex cache? */
|
||||
uint8_t bool_addr : 8;
|
||||
uint8_t condition : 1;
|
||||
instr_addr_mode_t address_mode : 1;
|
||||
instr_cf_opc_t opc : 4;
|
||||
} instr_cf_exec_t;
|
||||
|
||||
typedef struct PACKED {
|
||||
uint16_t address : 10;
|
||||
uint8_t reserved0 : 6;
|
||||
uint8_t loop_id : 5;
|
||||
uint32_t reserved1 : 22;
|
||||
instr_addr_mode_t address_mode : 1;
|
||||
instr_cf_opc_t opc : 4;
|
||||
} instr_cf_loop_t;
|
||||
|
||||
typedef struct PACKED {
|
||||
uint16_t address : 10;
|
||||
uint8_t reserved0 : 3;
|
||||
uint8_t force_call : 1;
|
||||
uint8_t predicated_jmp : 1;
|
||||
uint32_t reserved1 : 18;
|
||||
uint8_t direction : 1;
|
||||
uint8_t bool_addr : 8;
|
||||
uint8_t condition : 1;
|
||||
instr_addr_mode_t address_mode : 1;
|
||||
instr_cf_opc_t opc : 4;
|
||||
} instr_cf_jmp_call_t;
|
||||
|
||||
typedef struct PACKED {
|
||||
uint8_t size : 4;
|
||||
uint64_t reserved0 : 36;
|
||||
uint8_t no_serial : 1;
|
||||
instr_alloc_type_t buffer_select : 2;
|
||||
uint8_t alloc_mode : 1;
|
||||
instr_cf_opc_t opc : 4;
|
||||
} instr_cf_alloc_t;
|
||||
|
||||
typedef union PACKED {
|
||||
instr_cf_exec_t exec;
|
||||
instr_cf_loop_t loop;
|
||||
instr_cf_jmp_call_t jmp_call;
|
||||
instr_cf_alloc_t alloc;
|
||||
struct PACKED {
|
||||
uint64_t dummy : 44;
|
||||
instr_cf_opc_t opc : 4;
|
||||
};
|
||||
} instr_cf_t;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* FETCH instructions:
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
VTX_FETCH = 0,
|
||||
TEX_FETCH = 1,
|
||||
TEX_GET_BORDER_COLOR_FRAC = 16,
|
||||
TEX_GET_COMP_TEX_LOD = 17,
|
||||
TEX_GET_GRADIENTS = 18,
|
||||
TEX_GET_WEIGHTS = 19,
|
||||
TEX_SET_TEX_LOD = 24,
|
||||
TEX_SET_GRADIENTS_H = 25,
|
||||
TEX_SET_GRADIENTS_V = 26,
|
||||
TEX_RESERVED_4 = 27,
|
||||
} instr_fetch_opc_t;
|
||||
|
||||
typedef enum {
|
||||
TEX_FILTER_POINT = 0,
|
||||
TEX_FILTER_LINEAR = 1,
|
||||
TEX_FILTER_BASEMAP = 2, /* only applicable for mip-filter */
|
||||
TEX_FILTER_USE_FETCH_CONST = 3,
|
||||
} instr_tex_filter_t;
|
||||
|
||||
typedef enum {
|
||||
ANISO_FILTER_DISABLED = 0,
|
||||
ANISO_FILTER_MAX_1_1 = 1,
|
||||
ANISO_FILTER_MAX_2_1 = 2,
|
||||
ANISO_FILTER_MAX_4_1 = 3,
|
||||
ANISO_FILTER_MAX_8_1 = 4,
|
||||
ANISO_FILTER_MAX_16_1 = 5,
|
||||
ANISO_FILTER_USE_FETCH_CONST = 7,
|
||||
} instr_aniso_filter_t;
|
||||
|
||||
typedef enum {
|
||||
ARBITRARY_FILTER_2X4_SYM = 0,
|
||||
ARBITRARY_FILTER_2X4_ASYM = 1,
|
||||
ARBITRARY_FILTER_4X2_SYM = 2,
|
||||
ARBITRARY_FILTER_4X2_ASYM = 3,
|
||||
ARBITRARY_FILTER_4X4_SYM = 4,
|
||||
ARBITRARY_FILTER_4X4_ASYM = 5,
|
||||
ARBITRARY_FILTER_USE_FETCH_CONST = 7,
|
||||
} instr_arbitrary_filter_t;
|
||||
|
||||
typedef enum {
|
||||
SAMPLE_CENTROID = 0,
|
||||
SAMPLE_CENTER = 1,
|
||||
} instr_sample_loc_t;
|
||||
|
||||
typedef unsigned instr_surf_fmt_t;
|
||||
|
||||
typedef struct PACKED {
|
||||
/* dword0: */
|
||||
instr_fetch_opc_t opc : 5;
|
||||
uint8_t src_reg : 6;
|
||||
uint8_t src_reg_am : 1;
|
||||
uint8_t dst_reg : 6;
|
||||
uint8_t dst_reg_am : 1;
|
||||
uint8_t fetch_valid_only : 1;
|
||||
uint8_t const_idx : 5;
|
||||
uint8_t tx_coord_denorm : 1;
|
||||
uint8_t src_swiz : 6;
|
||||
/* dword1: */
|
||||
uint16_t dst_swiz : 12;
|
||||
instr_tex_filter_t mag_filter : 2;
|
||||
instr_tex_filter_t min_filter : 2;
|
||||
instr_tex_filter_t mip_filter : 2;
|
||||
instr_aniso_filter_t aniso_filter : 3;
|
||||
instr_arbitrary_filter_t arbitrary_filter : 3;
|
||||
instr_tex_filter_t vol_mag_filter : 2;
|
||||
instr_tex_filter_t vol_min_filter : 2;
|
||||
uint8_t use_comp_lod : 1;
|
||||
uint8_t use_reg_lod : 2;
|
||||
uint8_t pred_select : 1;
|
||||
/* dword2: */
|
||||
uint8_t use_reg_gradients : 1;
|
||||
instr_sample_loc_t sample_location : 1;
|
||||
uint8_t lod_bias : 7;
|
||||
uint8_t unused : 7;
|
||||
uint8_t offset_x : 5;
|
||||
uint8_t offset_y : 5;
|
||||
uint8_t offset_z : 5;
|
||||
uint8_t pred_condition : 1;
|
||||
} instr_fetch_tex_t;
|
||||
|
||||
typedef struct PACKED {
|
||||
/* dword0: */
|
||||
instr_fetch_opc_t opc : 5;
|
||||
uint8_t src_reg : 6;
|
||||
uint8_t src_reg_am : 1;
|
||||
uint8_t dst_reg : 6;
|
||||
uint8_t dst_reg_am : 1;
|
||||
uint8_t must_be_one : 1;
|
||||
uint8_t const_index : 5;
|
||||
uint8_t const_index_sel : 2;
|
||||
uint8_t reserved0 : 3;
|
||||
uint8_t src_swiz : 2;
|
||||
/* dword1: */
|
||||
uint16_t dst_swiz : 12;
|
||||
uint8_t format_comp_all : 1; /* '1' for signed, '0' for unsigned? */
|
||||
uint8_t num_format_all : 1; /* '0' for normalized, '1' for unnormalized */
|
||||
uint8_t signed_rf_mode_all : 1;
|
||||
uint8_t reserved1 : 1;
|
||||
instr_surf_fmt_t format : 6;
|
||||
uint8_t reserved2 : 1;
|
||||
uint8_t exp_adjust_all : 7;
|
||||
uint8_t reserved3 : 1;
|
||||
uint8_t pred_select : 1;
|
||||
/* dword2: */
|
||||
uint8_t stride : 8;
|
||||
/* possibly offset and reserved4 are swapped on a200? */
|
||||
uint8_t offset : 8;
|
||||
uint8_t reserved4 : 8;
|
||||
uint8_t reserved5 : 7;
|
||||
uint8_t pred_condition : 1;
|
||||
} instr_fetch_vtx_t;
|
||||
|
||||
typedef union PACKED {
|
||||
instr_fetch_tex_t tex;
|
||||
instr_fetch_vtx_t vtx;
|
||||
struct PACKED {
|
||||
/* dword0: */
|
||||
instr_fetch_opc_t opc : 5;
|
||||
uint32_t dummy0 : 27;
|
||||
/* dword1: */
|
||||
uint32_t dummy1 : 32;
|
||||
/* dword2: */
|
||||
uint32_t dummy2 : 32;
|
||||
};
|
||||
} instr_fetch_t;
|
||||
|
||||
#endif /* INSTR_H_ */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,163 @@
|
|||
/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014 Rob Clark <robclark@freedesktop.org>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors:
|
||||
* Rob Clark <robclark@freedesktop.org>
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
|
||||
#include "io.h"
|
||||
|
||||
struct io {
|
||||
struct archive *a;
|
||||
struct archive_entry *entry;
|
||||
unsigned offset;
|
||||
};
|
||||
|
||||
static void io_error(struct io *io)
|
||||
{
|
||||
fprintf(stderr, "%s\n", archive_error_string(io->a));
|
||||
io_close(io);
|
||||
}
|
||||
|
||||
static struct io * io_new(void)
|
||||
{
|
||||
struct io *io = calloc(1, sizeof(*io));
|
||||
int ret;
|
||||
|
||||
if (!io)
|
||||
return NULL;
|
||||
|
||||
io->a = archive_read_new();
|
||||
ret = archive_read_support_filter_gzip(io->a);
|
||||
if (ret != ARCHIVE_OK) {
|
||||
io_error(io);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = archive_read_support_filter_none(io->a);
|
||||
if (ret != ARCHIVE_OK) {
|
||||
io_error(io);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = archive_read_support_format_all(io->a);
|
||||
if (ret != ARCHIVE_OK) {
|
||||
io_error(io);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = archive_read_support_format_raw(io->a);
|
||||
if (ret != ARCHIVE_OK) {
|
||||
io_error(io);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
struct io * io_open(const char *filename)
|
||||
{
|
||||
struct io *io = io_new();
|
||||
int ret;
|
||||
|
||||
if (!io)
|
||||
return NULL;
|
||||
|
||||
ret = archive_read_open_filename(io->a, filename, 10240);
|
||||
if (ret != ARCHIVE_OK) {
|
||||
io_error(io);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = archive_read_next_header(io->a, &io->entry);
|
||||
if (ret != ARCHIVE_OK) {
|
||||
io_error(io);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
struct io * io_openfd(int fd)
|
||||
{
|
||||
struct io *io = io_new();
|
||||
int ret;
|
||||
|
||||
if (!io)
|
||||
return NULL;
|
||||
|
||||
ret = archive_read_open_fd(io->a, fd, 10240);
|
||||
if (ret != ARCHIVE_OK) {
|
||||
io_error(io);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = archive_read_next_header(io->a, &io->entry);
|
||||
if (ret != ARCHIVE_OK) {
|
||||
io_error(io);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
void io_close(struct io *io)
|
||||
{
|
||||
archive_read_free(io->a);
|
||||
free(io);
|
||||
}
|
||||
|
||||
unsigned io_offset(struct io *io)
|
||||
{
|
||||
return io->offset;
|
||||
}
|
||||
|
||||
#include <assert.h>
|
||||
int io_readn(struct io *io, void *buf, int nbytes)
|
||||
{
|
||||
char *ptr = buf;
|
||||
int ret = 0;
|
||||
while (nbytes > 0) {
|
||||
int n = archive_read_data(io->a, ptr, nbytes);
|
||||
if (n < 0) {
|
||||
fprintf(stderr, "%s\n", archive_error_string(io->a));
|
||||
return n;
|
||||
}
|
||||
if (n == 0)
|
||||
break;
|
||||
ptr += n;
|
||||
nbytes -= n;
|
||||
ret += n;
|
||||
io->offset += n;
|
||||
}
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014 Rob Clark <robclark@freedesktop.org>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors:
|
||||
* Rob Clark <robclark@freedesktop.org>
|
||||
*/
|
||||
|
||||
#ifndef IO_H_
|
||||
#define IO_H_
|
||||
|
||||
/* Simple API to abstract reading from file which might be compressed.
|
||||
* Maybe someday I'll add writing..
|
||||
*/
|
||||
|
||||
struct io;
|
||||
|
||||
struct io * io_open(const char *filename);
|
||||
struct io * io_openfd(int fd);
|
||||
void io_close(struct io *io);
|
||||
unsigned io_offset(struct io *io);
|
||||
int io_readn(struct io *io, void *buf, int nbytes);
|
||||
|
||||
|
||||
static inline int
|
||||
check_extension(const char *path, const char *ext)
|
||||
{
|
||||
return strcmp(path + strlen(path) - strlen(ext), ext) == 0;
|
||||
}
|
||||
|
||||
#endif /* IO_H_ */
|
|
@ -0,0 +1,144 @@
|
|||
# Copyright © 2020 Google, Inc
|
||||
|
||||
# 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 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.
|
||||
|
||||
dep_lua = dependency('lua53', required: false)
|
||||
if not dep_lua.found()
|
||||
dep_lua = dependency('lua52', required: false)
|
||||
endif
|
||||
if not dep_lua.found()
|
||||
dep_lua = dependency('lua', required: false)
|
||||
endif
|
||||
|
||||
dep_libarchive = dependency('libarchive', required: false)
|
||||
|
||||
# Shared cmdstream decoding:
|
||||
libfreedreno_cffdec = static_library(
|
||||
'freedreno_cffdec',
|
||||
[
|
||||
'buffers.c',
|
||||
'buffers.h',
|
||||
'cffdec.c',
|
||||
'cffdec.h',
|
||||
'disasm-a2xx.c',
|
||||
'disasm-a3xx.c',
|
||||
'disasm.h',
|
||||
'instr-a2xx.h',
|
||||
'instr-a3xx.h',
|
||||
'pager.c',
|
||||
'pager.h',
|
||||
'rnnutil.c',
|
||||
'rnnutil.h',
|
||||
'util.h',
|
||||
],
|
||||
include_directories: [
|
||||
inc_freedreno_rnn,
|
||||
],
|
||||
c_args : [ no_override_init_args ],
|
||||
gnu_symbol_visibility: 'hidden',
|
||||
dependencies: [],
|
||||
link_with: libfreedreno_rnn,
|
||||
build_by_default: false,
|
||||
)
|
||||
|
||||
if dep_libarchive.found()
|
||||
libfreedreno_io = static_library(
|
||||
'libfreedreno_io',
|
||||
[
|
||||
'io.c',
|
||||
'io.h',
|
||||
],
|
||||
include_directories: [],
|
||||
c_args : [no_override_init_args],
|
||||
gnu_symbol_visibility: 'hidden',
|
||||
dependencies: [
|
||||
dep_libarchive,
|
||||
],
|
||||
build_by_default: false,
|
||||
)
|
||||
endif
|
||||
|
||||
if dep_lua.found() and dep_libarchive.found()
|
||||
cffdump = executable(
|
||||
'cffdump',
|
||||
[
|
||||
'cffdump.c',
|
||||
'script.c',
|
||||
'script.h'
|
||||
],
|
||||
include_directories: [
|
||||
inc_freedreno_rnn,
|
||||
],
|
||||
c_args : [no_override_init_args],
|
||||
gnu_symbol_visibility: 'hidden',
|
||||
dependencies: [
|
||||
dep_lua,
|
||||
],
|
||||
link_with: [
|
||||
libfreedreno_cffdec,
|
||||
libfreedreno_io,
|
||||
],
|
||||
build_by_default: with_tools.contains('freedreno'),
|
||||
install : with_tools.contains('freedreno'),
|
||||
)
|
||||
endif
|
||||
|
||||
crashdec = executable(
|
||||
'crashdec',
|
||||
'crashdec.c',
|
||||
include_directories: [
|
||||
inc_freedreno_rnn,
|
||||
],
|
||||
gnu_symbol_visibility: 'hidden',
|
||||
dependencies: [],
|
||||
link_with: [
|
||||
libfreedreno_cffdec,
|
||||
],
|
||||
build_by_default: with_tools.contains('freedreno'),
|
||||
install : with_tools.contains('freedreno'),
|
||||
)
|
||||
|
||||
if dep_libarchive.found()
|
||||
pgmdump = executable(
|
||||
'pgmdump',
|
||||
'pgmdump.c',
|
||||
include_directories: [],
|
||||
gnu_symbol_visibility: 'hidden',
|
||||
dependencies: [],
|
||||
link_with: [
|
||||
libfreedreno_cffdec,
|
||||
libfreedreno_io,
|
||||
],
|
||||
build_by_default: with_tools.contains('freedreno'),
|
||||
install: false,
|
||||
)
|
||||
pgmdump2 = executable(
|
||||
'pgmdump2',
|
||||
'pgmdump2.c',
|
||||
include_directories: [],
|
||||
gnu_symbol_visibility: 'hidden',
|
||||
dependencies: [],
|
||||
link_with: [
|
||||
libfreedreno_cffdec,
|
||||
libfreedreno_io,
|
||||
],
|
||||
build_by_default: with_tools.contains('freedreno'),
|
||||
install: false,
|
||||
)
|
||||
endif
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "pager.h"
|
||||
|
||||
static pid_t pager_pid;
|
||||
|
||||
|
||||
static void
|
||||
pager_death(int n)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void
|
||||
pager_open(void)
|
||||
{
|
||||
int fd[2];
|
||||
|
||||
if (pipe(fd) < 0) {
|
||||
fprintf(stderr, "Failed to create pager pipe: %m\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
pager_pid = fork();
|
||||
if (pager_pid < 0) {
|
||||
fprintf(stderr, "Failed to fork pager: %m\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (pager_pid == 0) {
|
||||
const char* less_opts;
|
||||
|
||||
dup2(fd[0], STDIN_FILENO);
|
||||
close(fd[0]);
|
||||
close(fd[1]);
|
||||
|
||||
less_opts = "FRSMKX";
|
||||
setenv("LESS", less_opts, 1);
|
||||
|
||||
execlp("less", "less", NULL);
|
||||
|
||||
} else {
|
||||
/* we want to kill the parent process when pager exits: */
|
||||
signal(SIGCHLD, pager_death);
|
||||
dup2(fd[1], STDOUT_FILENO);
|
||||
close(fd[0]);
|
||||
close(fd[1]);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
pager_close(void)
|
||||
{
|
||||
siginfo_t status;
|
||||
|
||||
close(STDOUT_FILENO);
|
||||
|
||||
while (true) {
|
||||
memset(&status, 0, sizeof(status));
|
||||
if (waitid(P_PID, pager_pid, &status, WEXITED) < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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 __PAGER_H__
|
||||
#define __PAGER_H__
|
||||
|
||||
void pager_open(void);
|
||||
int pager_close(void);
|
||||
|
||||
#endif /* __PAGER_H__ */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,585 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Decoder for "new" GL_OES_get_program_binary format.
|
||||
*
|
||||
* Overall structure is:
|
||||
*
|
||||
* - header at top, contains, amongst other things, offsets of
|
||||
* per shader stage sections.
|
||||
* - per shader stage section (shader_info) starts with a header,
|
||||
* followed by a variably length list of descriptors. Each
|
||||
* descriptor has a type/count/size plus offset from the start
|
||||
* of shader_info section where the data is found
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stddef.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "redump.h"
|
||||
#include "disasm.h"
|
||||
#include "io.h"
|
||||
#include "util.h"
|
||||
|
||||
const char *infile;
|
||||
static int dump_full = 0;
|
||||
static int dump_offsets = 0;
|
||||
static int gpu_id = 320;
|
||||
static int shaderdb = 0; /* output shaderdb style traces to stderr */
|
||||
|
||||
struct state {
|
||||
char *buf;
|
||||
int sz;
|
||||
int lvl;
|
||||
|
||||
/* current shader_info section, some offsets calculated relative to
|
||||
* this, rather than relative to start of buffer.
|
||||
*/
|
||||
void *shader;
|
||||
|
||||
/* size of each entry within a shader_descriptor_blk: */
|
||||
int desc_size;
|
||||
|
||||
const char *shader_type;
|
||||
int full_regs;
|
||||
int half_regs;
|
||||
};
|
||||
|
||||
#define PACKED __attribute__((__packed__))
|
||||
|
||||
#define OFF(field) do { \
|
||||
if (dump_offsets) \
|
||||
printf("%08x: ", (uint32_t)((char *)&field - state->buf));\
|
||||
} while (0)
|
||||
|
||||
/* decode field as hex */
|
||||
#define X(s, field) do { \
|
||||
OFF(s->field); \
|
||||
printf("%s%12s:\t0x%x\n", tab(state->lvl), #field, s->field); \
|
||||
} while (0)
|
||||
|
||||
/* decode field as digit */
|
||||
#define D(s, field) do { \
|
||||
OFF(s->field); \
|
||||
printf("%s%12s:\t%u\n", tab(state->lvl), #field, s->field); \
|
||||
} while (0)
|
||||
|
||||
/* decode field as float/hex */
|
||||
#define F(s, field) do { \
|
||||
OFF(s->field); \
|
||||
printf("%s%12s:\t%f (0x%0x)\n", tab(state->lvl), #field, \
|
||||
d2f(s->field), s->field); \
|
||||
} while (0)
|
||||
|
||||
/* decode field as register: (type is 'r' or 'c') */
|
||||
#define R(s, field, type) do { \
|
||||
OFF(s->field); \
|
||||
printf("%s%12s:\t%c%u.%c\n", tab(state->lvl), #field, type, \
|
||||
(s->field >> 2), "xyzw"[s->field & 0x3]); \
|
||||
} while (0)
|
||||
|
||||
/* decode inline string (presumably null terminated?) */
|
||||
#define S(s, field) do { \
|
||||
OFF(s->field); \
|
||||
printf("%s%12s:\t%s\n", tab(state->lvl), #field, s->field); \
|
||||
} while (0)
|
||||
|
||||
/* decode string-table string */
|
||||
#define T(s, field) TODO
|
||||
|
||||
/* decode field as unknown */
|
||||
#define U(s, start, end) \
|
||||
dump_unknown(state, s->unk_ ## start ## _ ## end, 0x ## start, (4 + 0x ## end - 0x ## start) / 4)
|
||||
|
||||
/* decode field as offset to other section */
|
||||
#define O(s, field, type) do { \
|
||||
X(s, field); \
|
||||
assert(s->field < state->sz); \
|
||||
void *_p = &state->buf[s->field]; \
|
||||
state->lvl++; \
|
||||
decode_ ## type (state, _p); \
|
||||
state->lvl--; \
|
||||
} while (0)
|
||||
|
||||
struct shader_info;
|
||||
static void decode_shader_info(struct state *state, struct shader_info *info);
|
||||
|
||||
static void dump_unknown(struct state *state, void *buf, unsigned start, unsigned n)
|
||||
{
|
||||
uint32_t *ptr = buf;
|
||||
uint8_t *ascii = buf;
|
||||
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
uint32_t d = ptr[i];
|
||||
|
||||
if (dump_offsets)
|
||||
printf("%08x:", (uint32_t)((char *)&ptr[i] - state->buf));
|
||||
|
||||
printf("%s %04x:\t%08x", tab(state->lvl), start + i * 4, d);
|
||||
|
||||
printf("\t|");
|
||||
for (unsigned j = 0; j < 4; j++) {
|
||||
uint8_t c = *(ascii++);
|
||||
printf("%c", (isascii(c) && !iscntrl(c)) ? c : '.');
|
||||
}
|
||||
printf("|\t%f", d2f(d));
|
||||
|
||||
/* TODO maybe scan for first non-null and non-ascii char starting from
|
||||
* end of shader binary to (roughly) establish the start of the string
|
||||
* table.. that would be a bit better filter for deciding if something
|
||||
* might be a pointer into the string table. Also, the previous char
|
||||
* to what it points to should probably be null.
|
||||
*/
|
||||
if ((d < state->sz) &&
|
||||
isascii(state->buf[d]) &&
|
||||
(strlen(&state->buf[d]) > 2) &&
|
||||
isascii(state->buf[d+1]))
|
||||
printf("\t<== %s", &state->buf[d]);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
struct PACKED header {
|
||||
uint32_t version; /* I guess, always b10bcace ? */
|
||||
uint32_t unk_0004_0014[5];
|
||||
uint32_t size;
|
||||
uint32_t size2; /* just to be sure? */
|
||||
uint32_t unk_0020_0020[1];
|
||||
uint32_t chksum; /* I guess? Small changes seem to result in big diffs here */
|
||||
uint32_t unk_0028_0050[11];
|
||||
uint32_t fs_info; /* offset of FS shader_info section */
|
||||
uint32_t unk_0058_0090[15];
|
||||
uint32_t vs_info; /* offset of VS shader_info section */
|
||||
uint32_t unk_0098_00b0[7];
|
||||
uint32_t vs_info2; /* offset of VS shader_info section (again?) */
|
||||
uint32_t unk_00b8_0110[23];
|
||||
uint32_t bs_info; /* offset of binning shader_info section */
|
||||
};
|
||||
|
||||
static void decode_header(struct state *state, struct header *hdr)
|
||||
{
|
||||
X(hdr, version);
|
||||
U(hdr, 0004, 0014);
|
||||
X(hdr, size);
|
||||
X(hdr, size2);
|
||||
U(hdr, 0020, 0020);
|
||||
X(hdr, chksum);
|
||||
U(hdr, 0028, 0050);
|
||||
state->shader_type = "FRAG";
|
||||
O(hdr, fs_info, shader_info);
|
||||
U(hdr, 0058, 0090);
|
||||
state->shader_type = "VERT";
|
||||
O(hdr, vs_info, shader_info);
|
||||
U(hdr, 0098, 00b0);
|
||||
assert(hdr->vs_info == hdr->vs_info2); /* not sure what this if it is ever different */
|
||||
X(hdr, vs_info2);
|
||||
U(hdr, 00b8, 0110);
|
||||
state->shader_type = "BVERT";
|
||||
O(hdr, bs_info, shader_info);
|
||||
|
||||
/* not sure how much of the rest of contents before start of fs_info
|
||||
* is the header, vs other things.. just dump it all as unknown for
|
||||
* now:
|
||||
*/
|
||||
dump_unknown(state, (void *)hdr + sizeof(*hdr),
|
||||
sizeof(*hdr), (hdr->fs_info - sizeof(*hdr)) / 4);
|
||||
}
|
||||
|
||||
struct PACKED shader_entry_point {
|
||||
/* entry point name, ie. "main" of TBD length, followed by unknown */
|
||||
char name[8];
|
||||
};
|
||||
|
||||
static void decode_shader_entry_point(struct state *state,
|
||||
struct shader_entry_point *e)
|
||||
{
|
||||
S(e, name);
|
||||
}
|
||||
|
||||
struct PACKED shader_config {
|
||||
uint32_t unk_0000_0008[3];
|
||||
uint32_t full_regs;
|
||||
uint32_t half_regs;
|
||||
};
|
||||
|
||||
static void decode_shader_config(struct state *state, struct shader_config *cfg)
|
||||
{
|
||||
U(cfg, 0000, 0008);
|
||||
D(cfg, full_regs);
|
||||
D(cfg, half_regs);
|
||||
|
||||
state->full_regs = cfg->full_regs;
|
||||
state->half_regs = cfg->half_regs;
|
||||
|
||||
/* dump reset of unknown (size differs btwn versions) */
|
||||
dump_unknown(state, (void *)cfg + sizeof(*cfg), sizeof(*cfg),
|
||||
(state->desc_size - sizeof(*cfg))/4);
|
||||
}
|
||||
|
||||
struct PACKED shader_io_block {
|
||||
/* name of TBD length followed by unknown.. 42 dwords total */
|
||||
char name[20];
|
||||
uint32_t unk_0014_00a4[37];
|
||||
};
|
||||
|
||||
static void decode_shader_io_block(struct state *state,
|
||||
struct shader_io_block *io)
|
||||
{
|
||||
S(io, name);
|
||||
U(io, 0014, 00a4);
|
||||
}
|
||||
|
||||
struct PACKED shader_constant_block {
|
||||
uint32_t value;
|
||||
uint32_t unk_0004_000c[3];
|
||||
uint32_t regid;
|
||||
uint32_t unk_0014_0024[5];
|
||||
};
|
||||
|
||||
static void decode_shader_constant_block(struct state *state,
|
||||
struct shader_constant_block *c)
|
||||
{
|
||||
F(c, value);
|
||||
U(c, 0004, 000c);
|
||||
R(c, regid, 'c');
|
||||
U(c, 0014, 0024);
|
||||
}
|
||||
|
||||
enum {
|
||||
ENTRY_POINT = 0, /* shader_entry_point */
|
||||
SHADER_CONFIG = 1, /* XXX placeholder name */
|
||||
SHADER_INPUT = 2, /* shader_io_block */
|
||||
SHADER_OUTPUT = 3, /* shader_io_block */
|
||||
CONSTANTS = 6, /* shader_constant_block */
|
||||
INTERNAL = 8, /* internal input, like bary.f coord */
|
||||
SHADER = 10,
|
||||
} shader_info_block_type;
|
||||
|
||||
/* Refers to location of some type of records, with an offset relative to
|
||||
* start of shader_info block.
|
||||
*/
|
||||
struct PACKED shader_descriptor_block {
|
||||
uint32_t type; /* block type */
|
||||
uint32_t offset; /* offset (relative to start of shader_info block) */
|
||||
uint32_t size; /* size in bytes */
|
||||
uint32_t count; /* number of records */
|
||||
uint32_t unk_0010_0010[1];
|
||||
};
|
||||
|
||||
static void decode_shader_descriptor_block(struct state *state,
|
||||
struct shader_descriptor_block *blk)
|
||||
{
|
||||
D(blk, type);
|
||||
X(blk, offset);
|
||||
D(blk, size);
|
||||
D(blk, count);
|
||||
U(blk, 0010, 0010);
|
||||
|
||||
/* offset relative to current shader block: */
|
||||
void *ptr = state->shader + blk->offset;
|
||||
|
||||
if (blk->count == 0) {
|
||||
assert(blk->size == 0);
|
||||
} else {
|
||||
assert((blk->size % blk->count) == 0);
|
||||
}
|
||||
|
||||
state->desc_size = blk->size / blk->count;
|
||||
state->lvl++;
|
||||
for (unsigned i = 0; i < blk->count; i++) {
|
||||
switch (blk->type) {
|
||||
case ENTRY_POINT:
|
||||
printf("%sentry point %u:\n", tab(state->lvl-1), i);
|
||||
decode_shader_entry_point(state, ptr);
|
||||
break;
|
||||
case SHADER_CONFIG:
|
||||
printf("%sconfig %u:\n", tab(state->lvl-1), i);
|
||||
decode_shader_config(state, ptr);
|
||||
break;
|
||||
case SHADER_INPUT:
|
||||
printf("%sinput %u:\n", tab(state->lvl-1), i);
|
||||
decode_shader_io_block(state, ptr);
|
||||
break;
|
||||
case SHADER_OUTPUT:
|
||||
printf("%soutput %u:\n", tab(state->lvl-1), i);
|
||||
decode_shader_io_block(state, ptr);
|
||||
break;
|
||||
case INTERNAL:
|
||||
printf("%sinternal input %u:\n", tab(state->lvl-1), i);
|
||||
decode_shader_io_block(state, ptr);
|
||||
break;
|
||||
case CONSTANTS:
|
||||
printf("%sconstant %u:\n", tab(state->lvl-1), i);
|
||||
decode_shader_constant_block(state, ptr);
|
||||
break;
|
||||
case SHADER: {
|
||||
struct shader_stats stats;
|
||||
printf("%sshader %u:\n", tab(state->lvl-1), i);
|
||||
disasm_a3xx_stat(ptr, blk->size/4, state->lvl, stdout, gpu_id, &stats);
|
||||
if (shaderdb) {
|
||||
unsigned dwords = 2 * stats.instlen;
|
||||
|
||||
if (gpu_id >= 400) {
|
||||
dwords = ALIGN(dwords, 16 * 2);
|
||||
} else {
|
||||
dwords = ALIGN(dwords, 4 * 2);
|
||||
}
|
||||
|
||||
unsigned half_regs = state->half_regs;
|
||||
unsigned full_regs = state->full_regs;
|
||||
|
||||
/* On a6xx w/ merged/conflicting half and full regs, the
|
||||
* full_regs footprint will be max of full_regs and half
|
||||
* of half_regs.. we only care about which value is higher.
|
||||
*/
|
||||
if (gpu_id >= 600) {
|
||||
/* footprint of half_regs in units of full_regs: */
|
||||
unsigned half_full = (half_regs + 1) / 2;
|
||||
if (half_full > full_regs)
|
||||
full_regs = half_full;
|
||||
half_regs = 0;
|
||||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"%s shader: %u inst, %u nops, %u non-nops, %u dwords, "
|
||||
"%u half, %u full, %u constlen, "
|
||||
"%u (ss), %u (sy), %d max_sun, %d loops\n",
|
||||
state->shader_type, stats.instructions,
|
||||
stats.nops, stats.instructions - stats.nops,
|
||||
dwords, half_regs, full_regs,
|
||||
stats.constlen, stats.ss, stats.sy,
|
||||
0, 0); /* max_sun or loops not possible */
|
||||
}
|
||||
/* this is a special case in a way, blk->count is # of
|
||||
* instructions but disasm_a3xx() decodes all instructions,
|
||||
* so just bail.
|
||||
*/
|
||||
i = blk->count;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
dump_unknown(state, ptr, 0, state->desc_size/4);
|
||||
break;
|
||||
}
|
||||
ptr += state->desc_size;
|
||||
}
|
||||
state->lvl--;
|
||||
}
|
||||
|
||||
/* there looks like one of these per shader, followed by "main" and
|
||||
* some more info, and then the shader itself.
|
||||
*/
|
||||
struct PACKED shader_info {
|
||||
uint32_t unk_0000_0010[5];
|
||||
uint32_t desc_off; /* offset to first descriptor block */
|
||||
uint32_t num_blocks;
|
||||
};
|
||||
|
||||
static void decode_shader_info(struct state *state, struct shader_info *info)
|
||||
{
|
||||
assert((info->desc_off % 4) == 0);
|
||||
|
||||
U(info, 0000, 0010);
|
||||
X(info, desc_off);
|
||||
D(info, num_blocks);
|
||||
|
||||
dump_unknown(state, &info[1], 0, (info->desc_off - sizeof(*info))/4);
|
||||
|
||||
state->shader = info;
|
||||
|
||||
struct shader_descriptor_block *blocks = ((void *)info) + info->desc_off;
|
||||
for (unsigned i = 0; i < info->num_blocks; i++) {
|
||||
printf("%sdescriptor %u:\n", tab(state->lvl), i);
|
||||
state->lvl++;
|
||||
decode_shader_descriptor_block(state, &blocks[i]);
|
||||
state->lvl--;
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_program(struct state *state)
|
||||
{
|
||||
struct header *hdr = (void *)state->buf;
|
||||
|
||||
if (dump_full)
|
||||
dump_unknown(state, state->buf, 0, state->sz/4);
|
||||
|
||||
decode_header(state, hdr);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
enum rd_sect_type type = RD_NONE;
|
||||
enum debug_t debug = 0;
|
||||
void *buf = NULL;
|
||||
int sz;
|
||||
struct io *io;
|
||||
int raw_program = 0;
|
||||
|
||||
/* lame argument parsing: */
|
||||
|
||||
while (1) {
|
||||
if ((argc > 1) && !strcmp(argv[1], "--verbose")) {
|
||||
debug |= PRINT_RAW | PRINT_VERBOSE;
|
||||
argv++;
|
||||
argc--;
|
||||
continue;
|
||||
}
|
||||
if ((argc > 1) && !strcmp(argv[1], "--expand")) {
|
||||
debug |= EXPAND_REPEAT;
|
||||
argv++;
|
||||
argc--;
|
||||
continue;
|
||||
}
|
||||
if ((argc > 1) && !strcmp(argv[1], "--full")) {
|
||||
/* only short dump, original shader, symbol table, and disassembly */
|
||||
dump_full = 1;
|
||||
argv++;
|
||||
argc--;
|
||||
continue;
|
||||
}
|
||||
if ((argc > 1) && !strcmp(argv[1], "--dump-offsets")) {
|
||||
dump_offsets = 1;
|
||||
argv++;
|
||||
argc--;
|
||||
continue;
|
||||
}
|
||||
if ((argc > 1) && !strcmp(argv[1], "--raw")) {
|
||||
raw_program = 1;
|
||||
argv++;
|
||||
argc--;
|
||||
continue;
|
||||
}
|
||||
if ((argc > 1) && !strcmp(argv[1], "--shaderdb")) {
|
||||
shaderdb = 1;
|
||||
argv++;
|
||||
argc--;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: pgmdump2 [--verbose] [--expand] [--full] [--dump-offsets] [--raw] [--shaderdb] testlog.rd\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
disasm_set_debug(debug);
|
||||
|
||||
infile = argv[1];
|
||||
|
||||
io = io_open(infile);
|
||||
if (!io) {
|
||||
fprintf(stderr, "could not open: %s\n", infile);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (raw_program)
|
||||
{
|
||||
io_readn(io, &sz, 4);
|
||||
free(buf);
|
||||
|
||||
/* note: allow hex dumps to go a bit past the end of the buffer..
|
||||
* might see some garbage, but better than missing the last few bytes..
|
||||
*/
|
||||
buf = calloc(1, sz + 3);
|
||||
io_readn(io, buf + 4, sz);
|
||||
(*(int*)buf) = sz;
|
||||
|
||||
struct state state = {
|
||||
.buf = buf,
|
||||
.sz = sz,
|
||||
};
|
||||
printf("############################################################\n");
|
||||
printf("program:\n");
|
||||
dump_program(&state);
|
||||
printf("############################################################\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* figure out what sort of input we are dealing with: */
|
||||
if (!(check_extension(infile, ".rd") || check_extension(infile, ".rd.gz"))) {
|
||||
int ret;
|
||||
buf = calloc(1, 100 * 1024);
|
||||
ret = io_readn(io, buf, 100 * 1024);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "error: %m");
|
||||
return -1;
|
||||
}
|
||||
return disasm_a3xx(buf, ret/4, 0, stdout, gpu_id);
|
||||
}
|
||||
|
||||
while ((io_readn(io, &type, sizeof(type)) > 0) && (io_readn(io, &sz, 4) > 0)) {
|
||||
free(buf);
|
||||
|
||||
/* note: allow hex dumps to go a bit past the end of the buffer..
|
||||
* might see some garbage, but better than missing the last few bytes..
|
||||
*/
|
||||
buf = calloc(1, sz + 3);
|
||||
io_readn(io, buf, sz);
|
||||
|
||||
switch(type) {
|
||||
case RD_TEST:
|
||||
if (dump_full)
|
||||
printf("test: %s\n", (char *)buf);
|
||||
break;
|
||||
case RD_VERT_SHADER:
|
||||
printf("vertex shader:\n%s\n", (char *)buf);
|
||||
break;
|
||||
case RD_FRAG_SHADER:
|
||||
printf("fragment shader:\n%s\n", (char *)buf);
|
||||
break;
|
||||
case RD_PROGRAM: {
|
||||
struct state state = {
|
||||
.buf = buf,
|
||||
.sz = sz,
|
||||
};
|
||||
printf("############################################################\n");
|
||||
printf("program:\n");
|
||||
dump_program(&state);
|
||||
printf("############################################################\n");
|
||||
break;
|
||||
}
|
||||
case RD_GPU_ID:
|
||||
gpu_id = *((unsigned int *)buf);
|
||||
printf("gpu_id: %d\n", gpu_id);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
io_close(io);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright © 2012 Rob Clark <robclark@freedesktop.org>
|
||||
*
|
||||
* 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 REDUMP_H_
|
||||
#define REDUMP_H_
|
||||
|
||||
enum rd_sect_type {
|
||||
RD_NONE,
|
||||
RD_TEST, /* ascii text */
|
||||
RD_CMD, /* ascii text */
|
||||
RD_GPUADDR, /* u32 gpuaddr, u32 size */
|
||||
RD_CONTEXT, /* raw dump */
|
||||
RD_CMDSTREAM, /* raw dump */
|
||||
RD_CMDSTREAM_ADDR, /* gpu addr of cmdstream */
|
||||
RD_PARAM, /* u32 param_type, u32 param_val, u32 bitlen */
|
||||
RD_FLUSH, /* empty, clear previous params */
|
||||
RD_PROGRAM, /* shader program, raw dump */
|
||||
RD_VERT_SHADER,
|
||||
RD_FRAG_SHADER,
|
||||
RD_BUFFER_CONTENTS,
|
||||
RD_GPU_ID,
|
||||
};
|
||||
|
||||
/* RD_PARAM types: */
|
||||
enum rd_param_type {
|
||||
RD_PARAM_SURFACE_WIDTH,
|
||||
RD_PARAM_SURFACE_HEIGHT,
|
||||
RD_PARAM_SURFACE_PITCH,
|
||||
RD_PARAM_COLOR,
|
||||
RD_PARAM_BLIT_X,
|
||||
RD_PARAM_BLIT_Y,
|
||||
RD_PARAM_BLIT_WIDTH,
|
||||
RD_PARAM_BLIT_HEIGHT,
|
||||
RD_PARAM_BLIT_X2, /* BLIT_X + BLIT_WIDTH */
|
||||
RD_PARAM_BLIT_Y2, /* BLIT_Y + BLIT_WIDTH */
|
||||
};
|
||||
|
||||
void rd_start(const char *name, const char *fmt, ...) __attribute__((weak));
|
||||
void rd_end(void) __attribute__((weak));
|
||||
void rd_write_section(enum rd_sect_type type, const void *buf, int sz) __attribute__((weak));
|
||||
|
||||
/* for code that should run with and without libwrap, use the following
|
||||
* macros which check if the fxns are present before calling
|
||||
*/
|
||||
#define RD_START(n,f,...) do { if (rd_start) rd_start(n,f,##__VA_ARGS__); } while (0)
|
||||
#define RD_END() do { if (rd_end) rd_end(); } while (0)
|
||||
#define RD_WRITE_SECTION(t,b,s) do { if (rd_write_section) rd_write_section(t,b,s); } while (0)
|
||||
|
||||
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||
#undef ALIGN
|
||||
#define ALIGN(v,a) (((v) + (a) - 1) & ~((a) - 1))
|
||||
|
||||
#define min(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#define max(a, b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
#endif /* REDUMP_H_ */
|
|
@ -0,0 +1,217 @@
|
|||
/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014 Rob Clark <robclark@freedesktop.org>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors:
|
||||
* Rob Clark <robclark@freedesktop.org>
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "rnnutil.h"
|
||||
|
||||
static struct rnndomain *finddom(struct rnn *rnn, uint32_t regbase)
|
||||
{
|
||||
if (rnndec_checkaddr(rnn->vc, rnn->dom[0], regbase, 0))
|
||||
return rnn->dom[0];
|
||||
return rnn->dom[1];
|
||||
}
|
||||
|
||||
void _rnn_init(struct rnn *rnn, int nocolor)
|
||||
{
|
||||
rnn_init();
|
||||
|
||||
rnn->db = rnn_newdb();
|
||||
rnn->vc_nocolor = rnndec_newcontext(rnn->db);
|
||||
rnn->vc_nocolor->colors = &envy_null_colors;
|
||||
if (nocolor) {
|
||||
rnn->vc = rnn->vc_nocolor;
|
||||
} else {
|
||||
rnn->vc = rnndec_newcontext(rnn->db);
|
||||
rnn->vc->colors = &envy_def_colors;
|
||||
}
|
||||
}
|
||||
|
||||
struct rnn *rnn_new(int nocolor)
|
||||
{
|
||||
struct rnn *rnn = calloc(sizeof(*rnn), 1);
|
||||
|
||||
if (!rnn)
|
||||
return NULL;
|
||||
|
||||
_rnn_init(rnn, nocolor);
|
||||
|
||||
return rnn;
|
||||
}
|
||||
|
||||
static void init(struct rnn *rnn, char *file, char *domain)
|
||||
{
|
||||
/* prepare rnn stuff for lookup */
|
||||
rnn_parsefile(rnn->db, file);
|
||||
rnn_prepdb(rnn->db);
|
||||
rnn->dom[0] = rnn_finddomain(rnn->db, domain);
|
||||
if ((strcmp(domain, "A2XX") == 0) || (strcmp(domain, "A3XX") == 0)) {
|
||||
rnn->dom[1] = rnn_finddomain(rnn->db, "AXXX");
|
||||
} else {
|
||||
rnn->dom[1] = rnn->dom[0];
|
||||
}
|
||||
if (!rnn->dom[0] && rnn->dom[1]) {
|
||||
fprintf(stderr, "Could not find domain %s in %s\n", domain, file);
|
||||
}
|
||||
rnn->variant = domain;
|
||||
|
||||
rnndec_varadd(rnn->vc, "chip", domain);
|
||||
if (rnn->vc != rnn->vc_nocolor)
|
||||
rnndec_varadd(rnn->vc_nocolor, "chip", domain);
|
||||
}
|
||||
|
||||
void rnn_load_file(struct rnn *rnn, char *file, char *domain)
|
||||
{
|
||||
init(rnn, file, domain);
|
||||
}
|
||||
|
||||
void rnn_load(struct rnn *rnn, const char *gpuname)
|
||||
{
|
||||
if (strstr(gpuname, "a2")) {
|
||||
init(rnn, "adreno/a2xx.xml", "A2XX");
|
||||
} else if (strstr(gpuname, "a3")) {
|
||||
init(rnn, "adreno/a3xx.xml", "A3XX");
|
||||
} else if (strstr(gpuname, "a4")) {
|
||||
init(rnn, "adreno/a4xx.xml", "A4XX");
|
||||
} else if (strstr(gpuname, "a5")) {
|
||||
init(rnn, "adreno/a5xx.xml", "A5XX");
|
||||
} else if (strstr(gpuname, "a6")) {
|
||||
init(rnn, "adreno/a6xx.xml", "A6XX");
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t rnn_regbase(struct rnn *rnn, const char *name)
|
||||
{
|
||||
uint32_t regbase = rnndec_decodereg(rnn->vc_nocolor, rnn->dom[0], name);
|
||||
if (!regbase)
|
||||
regbase = rnndec_decodereg(rnn->vc_nocolor, rnn->dom[1], name);
|
||||
return regbase;
|
||||
}
|
||||
|
||||
const char *rnn_regname(struct rnn *rnn, uint32_t regbase, int color)
|
||||
{
|
||||
static char buf[128];
|
||||
struct rnndecaddrinfo *info;
|
||||
|
||||
info = rnndec_decodeaddr(color ? rnn->vc : rnn->vc_nocolor,
|
||||
finddom(rnn, regbase), regbase, 0);
|
||||
if (info) {
|
||||
strcpy(buf, info->name);
|
||||
free(info->name);
|
||||
free(info);
|
||||
return buf;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct rnndecaddrinfo *rnn_reginfo(struct rnn *rnn, uint32_t regbase)
|
||||
{
|
||||
return rnndec_decodeaddr(rnn->vc, finddom(rnn, regbase), regbase, 0);
|
||||
}
|
||||
|
||||
const char *rnn_enumname(struct rnn *rnn, const char *name, uint32_t val)
|
||||
{
|
||||
struct rnndeccontext *ctx = rnn->vc;
|
||||
struct rnnenum *en = rnn_findenum(ctx->db, name);
|
||||
if (en) {
|
||||
int i;
|
||||
for (i = 0; i < en->valsnum; i++) {
|
||||
struct rnnvalue *eval = en->vals[i];
|
||||
if (eval->valvalid && eval->value == val &&
|
||||
rnndec_varmatch(ctx, &eval->varinfo)) {
|
||||
return en->vals[i]->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct rnndelem *regelem(struct rnndomain *domain, const char *name)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < domain->subelemsnum; i++) {
|
||||
struct rnndelem *elem = domain->subelems[i];
|
||||
if (!strcmp(elem->name, name))
|
||||
return elem;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Lookup rnndelem by name: */
|
||||
struct rnndelem *rnn_regelem(struct rnn *rnn, const char *name)
|
||||
{
|
||||
struct rnndelem *elem = regelem(rnn->dom[0], name);
|
||||
if (elem)
|
||||
return elem;
|
||||
return regelem(rnn->dom[1], name);
|
||||
}
|
||||
|
||||
static struct rnndelem *regoff(struct rnndomain *domain, uint32_t offset)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < domain->subelemsnum; i++) {
|
||||
struct rnndelem *elem = domain->subelems[i];
|
||||
if (elem->offset == offset)
|
||||
return elem;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Lookup rnndelem by offset: */
|
||||
struct rnndelem *rnn_regoff(struct rnn *rnn, uint32_t offset)
|
||||
{
|
||||
struct rnndelem *elem = regoff(rnn->dom[0], offset);
|
||||
if (elem)
|
||||
return elem;
|
||||
return regoff(rnn->dom[1], offset);
|
||||
}
|
||||
|
||||
enum rnnttype rnn_decodelem(struct rnn *rnn, struct rnntypeinfo *info,
|
||||
uint32_t regval, union rnndecval *val)
|
||||
{
|
||||
val->u = regval;
|
||||
switch (info->type) {
|
||||
case RNN_TTYPE_INLINE_ENUM:
|
||||
case RNN_TTYPE_ENUM:
|
||||
case RNN_TTYPE_HEX:
|
||||
case RNN_TTYPE_INT:
|
||||
case RNN_TTYPE_UINT:
|
||||
case RNN_TTYPE_FLOAT:
|
||||
case RNN_TTYPE_BOOLEAN:
|
||||
return info->type;
|
||||
case RNN_TTYPE_FIXED:
|
||||
case RNN_TTYPE_UFIXED:
|
||||
/* TODO */
|
||||
default:
|
||||
return RNN_TTYPE_INVALID;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014 Rob Clark <robclark@freedesktop.org>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors:
|
||||
* Rob Clark <robclark@freedesktop.org>
|
||||
*/
|
||||
|
||||
#ifndef RNNUTIL_H_
|
||||
#define RNNUTIL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "rnn.h"
|
||||
#include "rnndec.h"
|
||||
|
||||
struct rnn {
|
||||
struct rnndb *db;
|
||||
struct rnndeccontext *vc, *vc_nocolor;
|
||||
struct rnndomain *dom[2];
|
||||
const char *variant;
|
||||
};
|
||||
|
||||
union rnndecval {
|
||||
uint32_t u;
|
||||
int32_t i;
|
||||
float f;
|
||||
};
|
||||
|
||||
void _rnn_init(struct rnn *rnn, int nocolor);
|
||||
struct rnn *rnn_new(int nocolor);
|
||||
void rnn_load_file(struct rnn *rnn, char *file, char *domain);
|
||||
void rnn_load(struct rnn *rnn, const char *gpuname);
|
||||
uint32_t rnn_regbase(struct rnn *rnn, const char *name);
|
||||
const char *rnn_regname(struct rnn *rnn, uint32_t regbase, int color);
|
||||
struct rnndecaddrinfo *rnn_reginfo(struct rnn *rnn, uint32_t regbase);
|
||||
const char *rnn_enumname(struct rnn *rnn, const char *name, uint32_t val);
|
||||
|
||||
struct rnndelem *rnn_regelem(struct rnn *rnn, const char *name);
|
||||
struct rnndelem *rnn_regoff(struct rnn *rnn, uint32_t offset);
|
||||
enum rnnttype rnn_decodelem(struct rnn *rnn, struct rnntypeinfo *info,
|
||||
uint32_t regval, union rnndecval *val);
|
||||
|
||||
#endif /* RNNUTIL_H_ */
|
|
@ -0,0 +1,775 @@
|
|||
/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014 Rob Clark <robclark@freedesktop.org>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors:
|
||||
* Rob Clark <robclark@freedesktop.org>
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#define LUA_COMPAT_APIINTCASTS
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <lualib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "script.h"
|
||||
#include "cffdec.h"
|
||||
#include "rnnutil.h"
|
||||
|
||||
static lua_State *L;
|
||||
|
||||
#if 0
|
||||
#define DBG(fmt, ...) \
|
||||
do { printf(" ** %s:%d ** "fmt "\n", \
|
||||
__FUNCTION__, __LINE__, ##__VA_ARGS__); } while (0)
|
||||
#else
|
||||
#define DBG(fmt, ...) do {} while (0)
|
||||
#endif
|
||||
|
||||
/* An rnn based decoder, which can either be decoding current register
|
||||
* values, or domain based decoding of a pm4 packet.
|
||||
*
|
||||
*/
|
||||
struct rnndec {
|
||||
struct rnn base;
|
||||
|
||||
/* for pm4 packet decoding: */
|
||||
uint32_t sizedwords;
|
||||
uint32_t *dwords;
|
||||
};
|
||||
|
||||
static inline struct rnndec *to_rnndec(struct rnn *rnn)
|
||||
{
|
||||
return (struct rnndec *)rnn;
|
||||
}
|
||||
|
||||
static uint32_t rnn_val(struct rnn *rnn, uint32_t regbase)
|
||||
{
|
||||
struct rnndec *rnndec = to_rnndec(rnn);
|
||||
|
||||
if (!rnndec->sizedwords) {
|
||||
return reg_val(regbase);
|
||||
} else if (regbase < rnndec->sizedwords) {
|
||||
return rnndec->dwords[regbase];
|
||||
} else {
|
||||
// XXX throw an error
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* does not return */
|
||||
static void error(const char *fmt)
|
||||
{
|
||||
fprintf(stderr, fmt, lua_tostring(L, -1));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* An enum type that can be used as string or number:
|
||||
*/
|
||||
|
||||
struct rnndenum {
|
||||
const char *str;
|
||||
int val;
|
||||
};
|
||||
|
||||
static int l_meta_rnn_enum_tostring(lua_State *L)
|
||||
{
|
||||
struct rnndenum *e = lua_touserdata(L, 1);
|
||||
if (e->str) {
|
||||
lua_pushstring(L, e->str);
|
||||
} else {
|
||||
char buf[32];
|
||||
sprintf(buf, "%u", e->val);
|
||||
lua_pushstring(L, buf);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* so, this doesn't actually seem to be implemented yet, but hopefully
|
||||
* some day lua comes to it's senses
|
||||
*/
|
||||
static int l_meta_rnn_enum_tonumber(lua_State *L)
|
||||
{
|
||||
struct rnndenum *e = lua_touserdata(L, 1);
|
||||
lua_pushinteger(L, e->val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg l_meta_rnn_enum[] = {
|
||||
{"__tostring", l_meta_rnn_enum_tostring},
|
||||
{"__tonumber", l_meta_rnn_enum_tonumber},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
static void pushenum(struct lua_State *L, int val, struct rnnenum *info)
|
||||
{
|
||||
struct rnndenum *e = lua_newuserdata(L, sizeof(*e));
|
||||
|
||||
e->val = val;
|
||||
e->str = NULL;
|
||||
|
||||
for (int i = 0; i < info->valsnum; i++) {
|
||||
if (info->vals[i]->valvalid && (info->vals[i]->value == val)) {
|
||||
e->str = info->vals[i]->name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
luaL_newmetatable(L, "rnnmetaenum");
|
||||
luaL_setfuncs(L, l_meta_rnn_enum, 0);
|
||||
lua_pop(L, 1);
|
||||
|
||||
luaL_setmetatable(L, "rnnmetaenum");
|
||||
}
|
||||
|
||||
/* Expose rnn decode to script environment as "rnn" library:
|
||||
*/
|
||||
|
||||
struct rnndoff {
|
||||
struct rnn *rnn;
|
||||
struct rnndelem *elem;
|
||||
uint64_t offset;
|
||||
};
|
||||
|
||||
static void push_rnndoff(lua_State *L, struct rnn *rnn,
|
||||
struct rnndelem *elem, uint64_t offset)
|
||||
{
|
||||
struct rnndoff *rnndoff = lua_newuserdata(L, sizeof(*rnndoff));
|
||||
rnndoff->rnn = rnn;
|
||||
rnndoff->elem = elem;
|
||||
rnndoff->offset = offset;
|
||||
}
|
||||
|
||||
static int l_rnn_etype_array(lua_State *L, struct rnn *rnn,
|
||||
struct rnndelem *elem, uint64_t offset);
|
||||
static int l_rnn_etype_reg(lua_State *L, struct rnn *rnn,
|
||||
struct rnndelem *elem, uint64_t offset);
|
||||
|
||||
static int pushdecval(struct lua_State *L, struct rnn *rnn,
|
||||
uint32_t regval, struct rnntypeinfo *info)
|
||||
{
|
||||
union rnndecval val;
|
||||
switch (rnn_decodelem(rnn, info, regval, &val)) {
|
||||
case RNN_TTYPE_ENUM:
|
||||
case RNN_TTYPE_INLINE_ENUM:
|
||||
pushenum(L, val.i, info->eenum);
|
||||
return 1;
|
||||
case RNN_TTYPE_INT:
|
||||
lua_pushinteger(L, val.i);
|
||||
return 1;
|
||||
case RNN_TTYPE_UINT:
|
||||
case RNN_TTYPE_HEX:
|
||||
lua_pushunsigned(L, val.u);
|
||||
return 1;
|
||||
case RNN_TTYPE_FLOAT:
|
||||
lua_pushnumber(L, val.f);
|
||||
return 1;
|
||||
case RNN_TTYPE_BOOLEAN:
|
||||
lua_pushboolean(L, val.u);
|
||||
return 1;
|
||||
case RNN_TTYPE_INVALID:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int l_rnn_etype(lua_State *L, struct rnn *rnn,
|
||||
struct rnndelem *elem, uint64_t offset)
|
||||
{
|
||||
int ret;
|
||||
uint32_t regval;
|
||||
DBG("elem=%p (%d), offset=%lu", elem, elem->type, offset);
|
||||
switch (elem->type) {
|
||||
case RNN_ETYPE_REG:
|
||||
/* if a register has no bitfields, just return
|
||||
* the raw value:
|
||||
*/
|
||||
regval = rnn_val(rnn, offset);
|
||||
regval <<= elem->typeinfo.shr;
|
||||
ret = pushdecval(L, rnn, regval, &elem->typeinfo);
|
||||
if (ret)
|
||||
return ret;
|
||||
return l_rnn_etype_reg(L, rnn, elem, offset);
|
||||
case RNN_ETYPE_ARRAY:
|
||||
return l_rnn_etype_array(L, rnn, elem, offset);
|
||||
default:
|
||||
/* hmm.. */
|
||||
printf("unhandled type: %d\n", elem->type);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Struct Object:
|
||||
* To implement stuff like 'RB_MRT[n].CONTROL' we need a struct-object
|
||||
* to represent the current array index (ie. 'RB_MRT[n]')
|
||||
*/
|
||||
|
||||
static int l_rnn_struct_meta_index(lua_State *L)
|
||||
{
|
||||
struct rnndoff *rnndoff = lua_touserdata(L, 1);
|
||||
const char *name = lua_tostring(L, 2);
|
||||
struct rnndelem *elem = rnndoff->elem;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < elem->subelemsnum; i++) {
|
||||
struct rnndelem *subelem = elem->subelems[i];
|
||||
if (!strcmp(name, subelem->name)) {
|
||||
return l_rnn_etype(L, rnndoff->rnn, subelem,
|
||||
rnndoff->offset + subelem->offset);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg l_meta_rnn_struct[] = {
|
||||
{"__index", l_rnn_struct_meta_index},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
static int l_rnn_etype_struct(lua_State *L, struct rnn *rnn,
|
||||
struct rnndelem *elem, uint64_t offset)
|
||||
{
|
||||
push_rnndoff(L, rnn, elem, offset);
|
||||
|
||||
luaL_newmetatable(L, "rnnmetastruct");
|
||||
luaL_setfuncs(L, l_meta_rnn_struct, 0);
|
||||
lua_pop(L, 1);
|
||||
|
||||
luaL_setmetatable(L, "rnnmetastruct");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Array Object:
|
||||
*/
|
||||
|
||||
static int l_rnn_array_meta_index(lua_State *L)
|
||||
{
|
||||
struct rnndoff *rnndoff = lua_touserdata(L, 1);
|
||||
int idx = lua_tointeger(L, 2);
|
||||
struct rnndelem *elem = rnndoff->elem;
|
||||
uint64_t offset = rnndoff->offset + (elem->stride * idx);
|
||||
|
||||
DBG("rnndoff=%p, idx=%d, numsubelems=%d",
|
||||
rnndoff, idx, rnndoff->elem->subelemsnum);
|
||||
|
||||
/* if just a single sub-element, it is directly a register,
|
||||
* otherwise we need to accumulate the array index while
|
||||
* we wait for the register name within the array..
|
||||
*/
|
||||
if (elem->subelemsnum == 1) {
|
||||
return l_rnn_etype(L, rnndoff->rnn, elem->subelems[0], offset);
|
||||
} else {
|
||||
return l_rnn_etype_struct(L, rnndoff->rnn, elem, offset);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg l_meta_rnn_array[] = {
|
||||
{"__index", l_rnn_array_meta_index},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
static int l_rnn_etype_array(lua_State *L, struct rnn *rnn,
|
||||
struct rnndelem *elem, uint64_t offset)
|
||||
{
|
||||
push_rnndoff(L, rnn, elem, offset);
|
||||
|
||||
luaL_newmetatable(L, "rnnmetaarray");
|
||||
luaL_setfuncs(L, l_meta_rnn_array, 0);
|
||||
lua_pop(L, 1);
|
||||
|
||||
luaL_setmetatable(L, "rnnmetaarray");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register element:
|
||||
*/
|
||||
|
||||
static int l_rnn_reg_meta_index(lua_State *L)
|
||||
{
|
||||
struct rnndoff *rnndoff = lua_touserdata(L, 1);
|
||||
const char *name = lua_tostring(L, 2);
|
||||
struct rnndelem *elem = rnndoff->elem;
|
||||
struct rnntypeinfo *info = &elem->typeinfo;
|
||||
struct rnnbitfield **bitfields;
|
||||
int bitfieldsnum;
|
||||
int i;
|
||||
|
||||
switch (info->type) {
|
||||
case RNN_TTYPE_BITSET:
|
||||
bitfields = info->ebitset->bitfields;
|
||||
bitfieldsnum = info->ebitset->bitfieldsnum;
|
||||
break;
|
||||
case RNN_TTYPE_INLINE_BITSET:
|
||||
bitfields = info->bitfields;
|
||||
bitfieldsnum = info->bitfieldsnum;
|
||||
break;
|
||||
default:
|
||||
printf("invalid register type: %d\n", info->type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < bitfieldsnum; i++) {
|
||||
struct rnnbitfield *bf = bitfields[i];
|
||||
if (!strcmp(name, bf->name)) {
|
||||
uint32_t regval = rnn_val(rnndoff->rnn, rnndoff->offset);
|
||||
|
||||
regval &= typeinfo_mask(&bf->typeinfo);
|
||||
regval >>= bf->typeinfo.low;
|
||||
regval <<= bf->typeinfo.shr;
|
||||
|
||||
DBG("name=%s, info=%p, subelemsnum=%d, type=%d, regval=%x",
|
||||
name, info, rnndoff->elem->subelemsnum,
|
||||
bf->typeinfo.type, regval);
|
||||
|
||||
return pushdecval(L, rnndoff->rnn, regval, &bf->typeinfo);
|
||||
}
|
||||
}
|
||||
|
||||
printf("invalid member: %s\n", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_rnn_reg_meta_tostring(lua_State *L)
|
||||
{
|
||||
struct rnndoff *rnndoff = lua_touserdata(L, 1);
|
||||
uint32_t regval = rnn_val(rnndoff->rnn, rnndoff->offset);
|
||||
struct rnndecaddrinfo *info = rnn_reginfo(rnndoff->rnn, rnndoff->offset);
|
||||
char *decoded;
|
||||
if (info && info->typeinfo) {
|
||||
decoded = rnndec_decodeval(rnndoff->rnn->vc,
|
||||
info->typeinfo, regval);
|
||||
} else {
|
||||
asprintf(&decoded, "%08x", regval);
|
||||
}
|
||||
lua_pushstring(L, decoded);
|
||||
free(decoded);
|
||||
if (info) {
|
||||
free(info->name);
|
||||
free(info);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_rnn_reg_meta_tonumber(lua_State *L)
|
||||
{
|
||||
struct rnndoff *rnndoff = lua_touserdata(L, 1);
|
||||
uint32_t regval = rnn_val(rnndoff->rnn, rnndoff->offset);
|
||||
|
||||
regval <<= rnndoff->elem->typeinfo.shr;
|
||||
|
||||
lua_pushnumber(L, regval);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg l_meta_rnn_reg[] = {
|
||||
{"__index", l_rnn_reg_meta_index},
|
||||
{"__tostring", l_rnn_reg_meta_tostring},
|
||||
{"__tonumber", l_rnn_reg_meta_tonumber},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
static int l_rnn_etype_reg(lua_State *L, struct rnn *rnn,
|
||||
struct rnndelem *elem, uint64_t offset)
|
||||
{
|
||||
push_rnndoff(L, rnn, elem, offset);
|
||||
|
||||
luaL_newmetatable(L, "rnnmetareg");
|
||||
luaL_setfuncs(L, l_meta_rnn_reg, 0);
|
||||
lua_pop(L, 1);
|
||||
|
||||
luaL_setmetatable(L, "rnnmetareg");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
static int l_rnn_meta_index(lua_State *L)
|
||||
{
|
||||
struct rnn *rnn = lua_touserdata(L, 1);
|
||||
const char *name = lua_tostring(L, 2);
|
||||
struct rnndelem *elem;
|
||||
|
||||
elem = rnn_regelem(rnn, name);
|
||||
if (!elem)
|
||||
return 0;
|
||||
|
||||
return l_rnn_etype(L, rnn, elem, elem->offset);
|
||||
}
|
||||
|
||||
static int l_rnn_meta_gc(lua_State *L)
|
||||
{
|
||||
// TODO
|
||||
//struct rnn *rnn = lua_touserdata(L, 1);
|
||||
//rnn_deinit(rnn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg l_meta_rnn[] = {
|
||||
{"__index", l_rnn_meta_index},
|
||||
{"__gc", l_rnn_meta_gc},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
static int l_rnn_init(lua_State *L)
|
||||
{
|
||||
const char *gpuname = lua_tostring(L, 1);
|
||||
struct rnndec *rnndec = lua_newuserdata(L, sizeof(*rnndec));
|
||||
_rnn_init(&rnndec->base, 0);
|
||||
rnn_load(&rnndec->base, gpuname);
|
||||
rnndec->sizedwords = 0;
|
||||
|
||||
luaL_newmetatable(L, "rnnmeta");
|
||||
luaL_setfuncs(L, l_meta_rnn, 0);
|
||||
lua_pop(L, 1);
|
||||
|
||||
luaL_setmetatable(L, "rnnmeta");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_rnn_enumname(lua_State *L)
|
||||
{
|
||||
struct rnn *rnn = lua_touserdata(L, 1);
|
||||
const char *name = lua_tostring(L, 2);
|
||||
uint32_t val = (uint32_t)lua_tonumber(L, 3);
|
||||
lua_pushstring(L, rnn_enumname(rnn, name, val));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_rnn_regname(lua_State *L)
|
||||
{
|
||||
struct rnn *rnn = lua_touserdata(L, 1);
|
||||
uint32_t regbase = (uint32_t)lua_tonumber(L, 2);
|
||||
lua_pushstring(L, rnn_regname(rnn, regbase, 1));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_rnn_regval(lua_State *L)
|
||||
{
|
||||
struct rnn *rnn = lua_touserdata(L, 1);
|
||||
uint32_t regbase = (uint32_t)lua_tonumber(L, 2);
|
||||
uint32_t regval = (uint32_t)lua_tonumber(L, 3);
|
||||
struct rnndecaddrinfo *info = rnn_reginfo(rnn, regbase);
|
||||
char *decoded;
|
||||
if (info && info->typeinfo) {
|
||||
decoded = rnndec_decodeval(rnn->vc, info->typeinfo, regval);
|
||||
} else {
|
||||
asprintf(&decoded, "%08x", regval);
|
||||
}
|
||||
lua_pushstring(L, decoded);
|
||||
free(decoded);
|
||||
if (info) {
|
||||
free(info->name);
|
||||
free(info);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg l_rnn[] = {
|
||||
{"init", l_rnn_init},
|
||||
{"enumname", l_rnn_enumname},
|
||||
{"regname", l_rnn_regname},
|
||||
{"regval", l_rnn_regval},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Expose the register state to script enviroment as a "regs" library:
|
||||
*/
|
||||
|
||||
static int l_reg_written(lua_State *L)
|
||||
{
|
||||
uint32_t regbase = (uint32_t)lua_tonumber(L, 1);
|
||||
lua_pushnumber(L, reg_written(regbase));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_reg_lastval(lua_State *L)
|
||||
{
|
||||
uint32_t regbase = (uint32_t)lua_tonumber(L, 1);
|
||||
lua_pushnumber(L, reg_lastval(regbase));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_reg_val(lua_State *L)
|
||||
{
|
||||
uint32_t regbase = (uint32_t)lua_tonumber(L, 1);
|
||||
lua_pushnumber(L, reg_val(regbase));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg l_regs[] = {
|
||||
{"written", l_reg_written},
|
||||
{"lastval", l_reg_lastval},
|
||||
{"val", l_reg_val},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
/* Expose API to lookup snapshot buffers:
|
||||
*/
|
||||
|
||||
uint64_t gpubaseaddr(uint64_t gpuaddr);
|
||||
unsigned hostlen(uint64_t gpuaddr);
|
||||
|
||||
/* given address, return base-address of buffer: */
|
||||
static int l_bo_base(lua_State *L)
|
||||
{
|
||||
uint64_t addr = (uint64_t)lua_tonumber(L, 1);
|
||||
lua_pushnumber(L, gpubaseaddr(addr));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* given address, return the remaining size of the buffer: */
|
||||
static int l_bo_size(lua_State *L)
|
||||
{
|
||||
uint64_t addr = (uint64_t)lua_tonumber(L, 1);
|
||||
lua_pushnumber(L, hostlen(addr));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg l_bos[] = {
|
||||
{"base", l_bo_base},
|
||||
{"size", l_bo_size},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
static void openlib(const char *lib, const luaL_Reg *reg)
|
||||
{
|
||||
lua_newtable(L);
|
||||
luaL_setfuncs(L, reg, 0);
|
||||
lua_setglobal(L, lib);
|
||||
}
|
||||
|
||||
/* called at start to load the script: */
|
||||
int script_load(const char *file)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(!L);
|
||||
|
||||
L = luaL_newstate();
|
||||
luaL_openlibs(L);
|
||||
openlib("bos", l_bos);
|
||||
openlib("regs", l_regs);
|
||||
openlib("rnn", l_rnn);
|
||||
|
||||
ret = luaL_loadfile(L, file);
|
||||
if (ret)
|
||||
error("%s\n");
|
||||
|
||||
ret = lua_pcall(L, 0, LUA_MULTRET, 0);
|
||||
if (ret)
|
||||
error("%s\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* called at start of each cmdstream file: */
|
||||
void script_start_cmdstream(const char *name)
|
||||
{
|
||||
if (!L)
|
||||
return;
|
||||
|
||||
lua_getglobal(L, "start_cmdstream");
|
||||
|
||||
/* if no handler just ignore it: */
|
||||
if (!lua_isfunction(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
lua_pushstring(L, name);
|
||||
|
||||
/* do the call (1 arguments, 0 result) */
|
||||
if (lua_pcall(L, 1, 0, 0) != 0)
|
||||
error("error running function `f': %s\n");
|
||||
}
|
||||
|
||||
/* called at each DRAW_INDX, calls script drawidx fxn to process
|
||||
* the current state
|
||||
*/
|
||||
void script_draw(const char *primtype, uint32_t nindx)
|
||||
{
|
||||
if (!L)
|
||||
return;
|
||||
|
||||
lua_getglobal(L, "draw");
|
||||
|
||||
/* if no handler just ignore it: */
|
||||
if (!lua_isfunction(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
lua_pushstring(L, primtype);
|
||||
lua_pushnumber(L, nindx);
|
||||
|
||||
/* do the call (2 arguments, 0 result) */
|
||||
if (lua_pcall(L, 2, 0, 0) != 0)
|
||||
error("error running function `f': %s\n");
|
||||
}
|
||||
|
||||
|
||||
static int l_rnn_meta_dom_index(lua_State *L)
|
||||
{
|
||||
struct rnn *rnn = lua_touserdata(L, 1);
|
||||
uint32_t offset = (uint32_t)lua_tonumber(L, 2);
|
||||
struct rnndelem *elem;
|
||||
|
||||
/* TODO might be nicer if the arg isn't a number, to search the domain
|
||||
* for matching bitfields.. so that the script could do something like
|
||||
* 'pkt.WIDTH' insteadl of 'pkt[1].WIDTH', ie. not have to remember the
|
||||
* offset of the dword containing the bitfield..
|
||||
*/
|
||||
|
||||
elem = rnn_regoff(rnn, offset);
|
||||
if (!elem)
|
||||
return 0;
|
||||
|
||||
return l_rnn_etype(L, rnn, elem, elem->offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* A wrapper object for rnndomain based decoding of an array of dwords
|
||||
* (ie. for pm4 packet decoding). Mostly re-uses the register-value
|
||||
* decoding for the individual dwords and bitfields.
|
||||
*/
|
||||
|
||||
static int l_rnn_meta_dom_gc(lua_State *L)
|
||||
{
|
||||
// TODO
|
||||
//struct rnn *rnn = lua_touserdata(L, 1);
|
||||
//rnn_deinit(rnn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg l_meta_rnn_dom[] = {
|
||||
{"__index", l_rnn_meta_dom_index},
|
||||
{"__gc", l_rnn_meta_dom_gc},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
/* called to general pm4 packet decoding, such as texture/sampler state
|
||||
*/
|
||||
void script_packet(uint32_t *dwords, uint32_t sizedwords,
|
||||
struct rnn *rnn, struct rnndomain *dom)
|
||||
{
|
||||
if (!L)
|
||||
return;
|
||||
|
||||
lua_getglobal(L, dom->name);
|
||||
|
||||
/* if no handler for the packet, just ignore it: */
|
||||
if (!lua_isfunction(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
struct rnndec *rnndec = lua_newuserdata(L, sizeof(*rnndec));
|
||||
|
||||
rnndec->base = *rnn;
|
||||
rnndec->base.dom[0] = dom;
|
||||
rnndec->base.dom[1] = NULL;
|
||||
rnndec->dwords = dwords;
|
||||
rnndec->sizedwords = sizedwords;
|
||||
|
||||
luaL_newmetatable(L, "rnnmetadom");
|
||||
luaL_setfuncs(L, l_meta_rnn_dom, 0);
|
||||
lua_pop(L, 1);
|
||||
|
||||
luaL_setmetatable(L, "rnnmetadom");
|
||||
|
||||
lua_pushnumber(L, sizedwords);
|
||||
|
||||
if (lua_pcall(L, 2, 0, 0) != 0)
|
||||
error("error running function `f': %s\n");
|
||||
}
|
||||
|
||||
/* helper to call fxn that takes and returns void: */
|
||||
static void simple_call(const char *name)
|
||||
{
|
||||
if (!L)
|
||||
return;
|
||||
|
||||
lua_getglobal(L, name);
|
||||
|
||||
/* if no handler just ignore it: */
|
||||
if (!lua_isfunction(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* do the call (0 arguments, 0 result) */
|
||||
if (lua_pcall(L, 0, 0, 0) != 0)
|
||||
error("error running function `f': %s\n");
|
||||
}
|
||||
|
||||
/* called at end of each cmdstream file: */
|
||||
void script_end_cmdstream(void)
|
||||
{
|
||||
simple_call("end_cmdstream");
|
||||
}
|
||||
|
||||
/* called at start of submit/issueibcmds: */
|
||||
void script_start_submit(void)
|
||||
{
|
||||
simple_call("start_submit");
|
||||
}
|
||||
|
||||
/* called at end of submit/issueibcmds: */
|
||||
void script_end_submit(void)
|
||||
{
|
||||
simple_call("end_submit");
|
||||
}
|
||||
|
||||
/* called after last cmdstream file: */
|
||||
void script_finish(void)
|
||||
{
|
||||
if (!L)
|
||||
return;
|
||||
|
||||
simple_call("finish");
|
||||
|
||||
lua_close(L);
|
||||
L = NULL;
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014 Rob Clark <robclark@freedesktop.org>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors:
|
||||
* Rob Clark <robclark@freedesktop.org>
|
||||
*/
|
||||
|
||||
#ifndef SCRIPT_H_
|
||||
#define SCRIPT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
// XXX make script support optional
|
||||
#define ENABLE_SCRIPTING 1
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
||||
/* called at start to load the script: */
|
||||
int script_load(const char *file);
|
||||
|
||||
/* called at start of each cmdstream file: */
|
||||
void script_start_cmdstream(const char *name);
|
||||
|
||||
/* called at each DRAW_INDX, calls script drawidx fxn to process
|
||||
* the current state
|
||||
*/
|
||||
__attribute__((weak))
|
||||
void script_draw(const char *primtype, uint32_t nindx);
|
||||
|
||||
struct rnn;
|
||||
struct rnndomain;
|
||||
__attribute__((weak))
|
||||
void script_packet(uint32_t *dwords, uint32_t sizedwords,
|
||||
struct rnn *rnn, struct rnndomain *dom);
|
||||
|
||||
/* maybe at some point it is interesting to add additional script
|
||||
* hooks for CP_EVENT_WRITE, etc?
|
||||
*/
|
||||
|
||||
/* called at end of each cmdstream file: */
|
||||
void script_end_cmdstream(void);
|
||||
|
||||
void script_start_submit(void);
|
||||
void script_end_submit(void);
|
||||
|
||||
/* called after last cmdstream file: */
|
||||
void script_finish(void);
|
||||
|
||||
#else
|
||||
// TODO no-op stubs..
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* SCRIPT_H_ */
|
|
@ -0,0 +1,178 @@
|
|||
-- A script that compares a set of equivalent cmdstream captures from
|
||||
-- various generations, looking for equivalencies between registers.
|
||||
--
|
||||
-- This would be run across a group of similar tests for various
|
||||
-- generations, for example:
|
||||
--
|
||||
-- cffdump --script scripts/analyze.lua a320/quad-flat-*.rd a420/quad-flat-*.rd
|
||||
--
|
||||
-- This is done by comparing unique register values. Ie. for each
|
||||
-- generation, find the set of registers that have different values
|
||||
-- between equivalent draw calls.
|
||||
|
||||
local posix = require "posix"
|
||||
|
||||
io.write("Analyzing Data...\n")
|
||||
|
||||
-- results - table structure:
|
||||
-- * [gpuname] - gpu
|
||||
-- * tests
|
||||
-- * [testname] - current test
|
||||
-- * draws
|
||||
-- * [1..n] - the draws
|
||||
-- * primtype - the primitive type
|
||||
-- * regs - table of values for draw
|
||||
-- * [regbase] - regval
|
||||
-- * regvals - table of unique values across all draws
|
||||
-- * [regbase]
|
||||
-- * [regval] - list of test names
|
||||
-- * [1..n] - testname "." didx
|
||||
local results = {}
|
||||
|
||||
local test = nil
|
||||
local gpuname = nil
|
||||
local testname = nil
|
||||
|
||||
|
||||
-- srsly, no sparse table size() op?
|
||||
function tblsz(tbl)
|
||||
local n = 0;
|
||||
for k,v in pairs(tbl) do
|
||||
n = n + 1
|
||||
end
|
||||
return n
|
||||
end
|
||||
|
||||
|
||||
function start_cmdstream(name)
|
||||
testname = posix.basename(name)
|
||||
gpuname = posix.basename(posix.dirname(name))
|
||||
--io.write("START: gpuname=" .. gpuname .. ", testname=" .. testname .. "\n");
|
||||
local gpu = results[gpuname]
|
||||
if gpu == nil then
|
||||
gpu = {["tests"] = {}, ["regvals"] = {}}
|
||||
results[gpuname] = gpu
|
||||
end
|
||||
test = {["draws"] = {}}
|
||||
gpu["tests"][testname] = test
|
||||
end
|
||||
|
||||
function draw(primtype, nindx)
|
||||
-- RECTLIST is only used internally.. we want to ignore it for
|
||||
-- now, although it could potentially be interesting to track
|
||||
-- these separately (separating clear/restore/resolve) just to
|
||||
-- figure out which registers are used for which..
|
||||
if primtype == "DI_PT_RECTLIST" then
|
||||
return
|
||||
end
|
||||
local regtbl = {}
|
||||
local draw = {["primtype"] = primtype, ["regs"] = regtbl}
|
||||
local didx = tblsz(test["draws"])
|
||||
|
||||
test["draws"][didx] = draw
|
||||
|
||||
-- populate current regs. For now just consider ones that have
|
||||
-- been written.. maybe we need to make that configurable in
|
||||
-- case it filters out too many registers.
|
||||
for regbase=0,0xffff do
|
||||
if regs.written(regbase) ~= 0 then
|
||||
local regval = regs.val(regbase)
|
||||
|
||||
-- track reg vals per draw:
|
||||
regtbl[regbase] = regval
|
||||
|
||||
-- also track which reg vals appear in which tests:
|
||||
local uniq_regvals = results[gpuname]["regvals"][regbase]
|
||||
if uniq_regvals == nil then
|
||||
uniq_regvals = {}
|
||||
results[gpuname]["regvals"][regbase] = uniq_regvals;
|
||||
end
|
||||
local drawlist = uniq_regvals[regval]
|
||||
if drawlist == nil then
|
||||
drawlist = {}
|
||||
uniq_regvals[regval] = drawlist
|
||||
end
|
||||
table.insert(drawlist, testname .. "." .. didx)
|
||||
end
|
||||
end
|
||||
|
||||
-- TODO maybe we want to whitelist a few well known regs, for the
|
||||
-- convenience of the code that runs at the end to analyze the data?
|
||||
-- TODO also would be useful to somehow capture CP_SET_BIN..
|
||||
|
||||
end
|
||||
|
||||
function end_cmdstream()
|
||||
test = nil
|
||||
gpuname = nil
|
||||
testname = nil
|
||||
end
|
||||
|
||||
function print_draws(gpuname, gpu)
|
||||
io.write(" " .. gpuname .. "\n")
|
||||
for testname,test in pairs(gpu["tests"]) do
|
||||
io.write(" " .. testname .. ", draws=" .. #test["draws"] .. "\n")
|
||||
for didx,draw in pairs(test["draws"]) do
|
||||
io.write(" " .. didx .. ": " .. draw["primtype"] .. "\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- sort and concat a list of draw names to form a key which can be
|
||||
-- compared to other drawlists to check for equality
|
||||
-- TODO maybe we instead want a scheme that allows for some fuzzyness
|
||||
-- in the matching??
|
||||
function drawlistname(drawlist)
|
||||
local name = nil
|
||||
for idx,draw in pairs(drawlist) do
|
||||
if name == nil then
|
||||
name = draw
|
||||
else
|
||||
name = name .. ":" .. draw
|
||||
end
|
||||
end
|
||||
return name
|
||||
end
|
||||
|
||||
local rnntbl = {}
|
||||
|
||||
function dumpmatches(name)
|
||||
for gpuname,gpu in pairs(results) do
|
||||
local r = rnntbl[gpuname]
|
||||
if r == nil then
|
||||
io.write("loading rnn database: \n" .. gpuname)
|
||||
r = rnn.init(gpuname)
|
||||
rnntbl[gpuname] = r
|
||||
end
|
||||
for regbase,regvals in pairs(gpu["regvals"]) do
|
||||
for regval,drawlist in pairs(regvals) do
|
||||
local name2 = drawlistname(drawlist)
|
||||
if name == name2 then
|
||||
io.write(string.format(" %s:%s:\t%08x %s\n",
|
||||
gpuname, rnn.regname(r, regbase),
|
||||
regval, rnn.regval(r, regbase, regval)))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function finish()
|
||||
-- drawlistnames that we've already dumped:
|
||||
local dumped = {}
|
||||
|
||||
for gpuname,gpu in pairs(results) do
|
||||
-- print_draws(gpuname, gpu)
|
||||
for regbase,regvals in pairs(gpu["regvals"]) do
|
||||
for regval,drawlist in pairs(regvals) do
|
||||
local name = drawlistname(drawlist)
|
||||
if dumped[name] == nil then
|
||||
io.write("\n" .. name .. ":\n")
|
||||
dumpmatches(name)
|
||||
dumped[name] = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,413 @@
|
|||
-- Parse cmdstream dump and analyse blits and batches
|
||||
|
||||
--local posix = require "posix"
|
||||
|
||||
function printf(fmt, ...)
|
||||
return io.write(string.format(fmt, ...))
|
||||
end
|
||||
|
||||
function dbg(fmt, ...)
|
||||
--printf(fmt, ...)
|
||||
end
|
||||
|
||||
printf("Analyzing Data...\n")
|
||||
|
||||
local r = rnn.init("a630")
|
||||
|
||||
-- Each submit, all draws will target the same N MRTs:
|
||||
local mrts = {}
|
||||
local allmrts = {} -- includes historical render targets
|
||||
function push_mrt(fmt, w, h, samples, base, flag, gmem)
|
||||
dbg("MRT: %s %ux%u 0x%x\n", fmt, w, h, base)
|
||||
|
||||
local mrt = {}
|
||||
mrt.format = fmt
|
||||
mrt.w = w
|
||||
mrt.h = h
|
||||
mrt.samples = samples
|
||||
mrt.base = base
|
||||
mrt.flag = flag
|
||||
mrt.gmem = gmem
|
||||
|
||||
mrts[base] = mrt
|
||||
allmrts[base] = mrt
|
||||
end
|
||||
|
||||
-- And each each draw will read from M sources/textures:
|
||||
local sources = {}
|
||||
function push_source(fmt, w, h, samples, base, flag)
|
||||
dbg("SRC: %s %ux%u 0x%x\n", fmt, w, h, base)
|
||||
|
||||
local source = {}
|
||||
source.format = fmt
|
||||
source.w = w
|
||||
source.h = h
|
||||
source.samples = samples
|
||||
source.base = base
|
||||
source.flag = flag
|
||||
|
||||
sources[base] = source
|
||||
end
|
||||
|
||||
local binw
|
||||
local binh
|
||||
local nbins
|
||||
local blits = 0
|
||||
local draws = 0
|
||||
local drawmode
|
||||
local cleared
|
||||
local restored
|
||||
local resolved
|
||||
local nullbatch
|
||||
local depthtest
|
||||
local depthwrite
|
||||
local stenciltest
|
||||
local stencilwrite
|
||||
|
||||
function start_cmdstream(name)
|
||||
printf("Parsing %s\n", name)
|
||||
end
|
||||
|
||||
function reset()
|
||||
dbg("reset\n")
|
||||
mrts = {}
|
||||
sources = {}
|
||||
draws = 0
|
||||
blits = 0
|
||||
cleared = {}
|
||||
restored = {}
|
||||
resolved = {}
|
||||
depthtest = false
|
||||
depthwrite = false
|
||||
stenciltest = false
|
||||
stencilwrite = false
|
||||
drawmode = Nil
|
||||
end
|
||||
|
||||
function start_submit()
|
||||
dbg("start_submit\n")
|
||||
reset()
|
||||
nullbatch = true
|
||||
end
|
||||
|
||||
function finish()
|
||||
dbg("finish\n")
|
||||
|
||||
printf("\n")
|
||||
|
||||
-- TODO we get false-positives for 'NULL BATCH!' because we don't have
|
||||
-- a really good way to differentiate between submits and cmds. Ie.
|
||||
-- with growable cmdstream, and a large # of tiles, IB1 can get split
|
||||
-- across multiple buffers. Since we ignore GMEM draws for window-
|
||||
-- offset != 0,0, the later cmds will appear as null batches
|
||||
if draws == 0 and blits == 0 then
|
||||
if nullbatch then
|
||||
printf("NULL BATCH!\n");
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if draws > 0 then
|
||||
printf("Batch:\n")
|
||||
printf("-------\n")
|
||||
printf(" # of draws: %u\n", draws)
|
||||
printf(" mode: %s\n", drawmode)
|
||||
if drawmode == "RM6_GMEM" then
|
||||
printf(" bin size: %ux%u (%u bins)\n", binw, binh, nbins)
|
||||
end
|
||||
if depthtest or depthwrite then
|
||||
printf(" ")
|
||||
if depthtest then
|
||||
printf("DEPTHTEST ")
|
||||
end
|
||||
if depthwrite then
|
||||
printf("DEPTHWRITE")
|
||||
end
|
||||
printf("\n")
|
||||
end
|
||||
if stenciltest or stencilwrite then
|
||||
printf(" ")
|
||||
if stenciltest then
|
||||
printf("STENCILTEST ")
|
||||
end
|
||||
if stencilwrite then
|
||||
printf("STENCILWRITE")
|
||||
end
|
||||
printf("\n")
|
||||
end
|
||||
else
|
||||
printf("Blit:\n")
|
||||
printf("-----\n")
|
||||
end
|
||||
|
||||
for base,mrt in pairs(mrts) do
|
||||
printf(" MRT[0x%x:0x%x]:\t%ux%u\t\t%s (%s)", base, mrt.flag, mrt.w, mrt.h, mrt.format, mrt.samples)
|
||||
if drawmode == "RM6_GMEM" then
|
||||
if cleared[mrt.gmem] then
|
||||
printf("\tCLEARED")
|
||||
end
|
||||
if restored[mrt.gmem] then
|
||||
printf("\tRESTORED")
|
||||
end
|
||||
if resolved[mrt.gmem] then
|
||||
printf("\tRESOLVED")
|
||||
end
|
||||
else
|
||||
if cleared[mrt.base] then
|
||||
printf("\tCLEARED")
|
||||
end
|
||||
end
|
||||
printf("\n")
|
||||
end
|
||||
|
||||
function print_source(source)
|
||||
printf(" SRC[0x%x:0x%x]:\t%ux%u\t\t%s (%s)\n", source.base, source.flag, source.w, source.h, source.format, source.samples)
|
||||
end
|
||||
|
||||
for base,source in pairs(sources) do
|
||||
-- only show sources that have been previously rendered to, other
|
||||
-- textures are less interesting. Possibly this should be an
|
||||
-- option somehow
|
||||
if draws < 10 then
|
||||
print_source(source)
|
||||
elseif allmrts[base] or draws == 0 then
|
||||
print_source(source)
|
||||
elseif source.flag and allmrts[source.flag] then
|
||||
print_source(source)
|
||||
end
|
||||
end
|
||||
reset()
|
||||
end
|
||||
|
||||
function end_submit()
|
||||
dbg("end_submit\n")
|
||||
finish()
|
||||
end
|
||||
|
||||
-- Track the current mode:
|
||||
local mode = ""
|
||||
function CP_SET_MARKER(pkt, size)
|
||||
mode = pkt[0].MARKER
|
||||
dbg("mode: %s\n", mode)
|
||||
end
|
||||
|
||||
function CP_EVENT_WRITE(pkt, size)
|
||||
if tostring(pkt[0].EVENT) ~= "BLIT" then
|
||||
return
|
||||
end
|
||||
nullbatch = false
|
||||
local m = tostring(mode)
|
||||
if m == "RM6_GMEM" then
|
||||
-- either clear or restore:
|
||||
if r.RB_BLIT_INFO.CLEAR_MASK == 0 then
|
||||
restored[r.RB_BLIT_BASE_GMEM] = 1
|
||||
else
|
||||
cleared[r.RB_BLIT_BASE_GMEM] = 1
|
||||
end
|
||||
-- push_mrt() because we could have GMEM
|
||||
-- passes with only a clear and no draws:
|
||||
local flag = 0
|
||||
local sysmem = 0;
|
||||
-- try to match up the GMEM addr with the MRT/DEPTH state,
|
||||
-- to avoid relying on RB_BLIT_DST also getting written:
|
||||
for n = 0,r.RB_FS_OUTPUT_CNTL1.MRT-1 do
|
||||
if r.RB_MRT[n].BASE_GMEM == r.RB_BLIT_BASE_GMEM then
|
||||
sysmem = r.RB_MRT[n].BASE_LO | (r.RB_MRT[n].BASE_HI << 32)
|
||||
flag = r.RB_MRT_FLAG_BUFFER[n].ADDR_LO | (r.RB_MRT_FLAG_BUFFER[n].ADDR_HI << 32)
|
||||
break
|
||||
end
|
||||
end
|
||||
if sysmem == 0 and r.RB_BLIT_BASE_GMEM == r.RB_DEPTH_BUFFER_BASE_GMEM then
|
||||
sysmem = r.RB_DEPTH_BUFFER_BASE_LO | (r.RB_DEPTH_BUFFER_BASE_HI << 32)
|
||||
flag = r.RB_DEPTH_FLAG_BUFFER_BASE_LO | (r.RB_DEPTH_FLAG_BUFFER_BASE_HI << 32)
|
||||
|
||||
end
|
||||
--NOTE this can get confused by previous blits:
|
||||
--if sysmem == 0 then
|
||||
-- -- fallback:
|
||||
-- sysmem = r.RB_BLIT_DST_LO | (r.RB_BLIT_DST_HI << 32)
|
||||
-- flag = r.RB_BLIT_FLAG_DST_LO | (r.RB_BLIT_FLAG_DST_HI << 32)
|
||||
--end
|
||||
if not r.RB_BLIT_DST_INFO.FLAGS then
|
||||
flag = 0
|
||||
end
|
||||
-- TODO maybe just emit RB_BLIT_DST_LO/HI for clears.. otherwise
|
||||
-- we get confused by stale values in registers.. not sure
|
||||
-- if this is a problem w/ blob
|
||||
push_mrt(r.RB_BLIT_DST_INFO.COLOR_FORMAT,
|
||||
r.RB_BLIT_SCISSOR_BR.X + 1,
|
||||
r.RB_BLIT_SCISSOR_BR.Y + 1,
|
||||
r.RB_BLIT_DST_INFO.SAMPLES,
|
||||
sysmem,
|
||||
flag,
|
||||
r.RB_BLIT_BASE_GMEM)
|
||||
elseif m == "RM6_RESOLVE" then
|
||||
resolved[r.RB_BLIT_BASE_GMEM] = 1
|
||||
else
|
||||
printf("I am confused!!!\n")
|
||||
end
|
||||
end
|
||||
|
||||
function A6XX_TEX_CONST(pkt, size)
|
||||
push_source(pkt[0].FMT,
|
||||
pkt[1].WIDTH, pkt[1].HEIGHT,
|
||||
pkt[0].SAMPLES,
|
||||
pkt[4].BASE_LO | (pkt[5].BASE_HI << 32),
|
||||
pkt[7].FLAG_LO | (pkt[8].FLAG_HI << 32))
|
||||
end
|
||||
|
||||
function handle_blit()
|
||||
-- blob sometimes uses CP_BLIT for resolves, so filter those out:
|
||||
-- TODO it would be nice to not hard-code GMEM addr:
|
||||
-- TODO I guess the src can be an offset from GMEM addr..
|
||||
if r.SP_PS_2D_SRC_LO == 0x100000 and not r.RB_2D_BLIT_CNTL.SOLID_COLOR then
|
||||
resolved[0] = 1
|
||||
return
|
||||
end
|
||||
if draws > 0 then
|
||||
finish()
|
||||
end
|
||||
reset()
|
||||
drawmode = "BLIT"
|
||||
-- This kinda assumes that we are doing full img blits, which is maybe
|
||||
-- Not completely legit. We could perhaps instead just track pitch and
|
||||
-- size/pitch?? Or maybe the size doesn't matter much
|
||||
push_mrt(r.RB_2D_DST_INFO.COLOR_FORMAT,
|
||||
r.GRAS_2D_DST_BR.X + 1,
|
||||
r.GRAS_2D_DST_BR.Y + 1,
|
||||
"MSAA_ONE",
|
||||
r.RB_2D_DST_LO | (r.RB_2D_DST_HI << 32),
|
||||
r.RB_2D_DST_FLAGS_LO | (r.RB_2D_DST_FLAGS_HI << 32),
|
||||
-1)
|
||||
if r.RB_2D_BLIT_CNTL.SOLID_COLOR then
|
||||
dbg("CLEAR=%x\n", r.RB_2D_DST_LO | (r.RB_2D_DST_HI << 32))
|
||||
cleared[r.RB_2D_DST_LO | (r.RB_2D_DST_HI << 32)] = 1
|
||||
else
|
||||
push_source(r.SP_2D_SRC_FORMAT.COLOR_FORMAT,
|
||||
r.GRAS_2D_SRC_BR_X.X + 1,
|
||||
r.GRAS_2D_SRC_BR_Y.Y + 1,
|
||||
"MSAA_ONE",
|
||||
r.SP_PS_2D_SRC_LO | (r.SP_PS_2D_SRC_HI << 32),
|
||||
r.SP_PS_2D_SRC_FLAGS_LO | (r.SP_PS_2D_SRC_FLAGS_HI << 32))
|
||||
end
|
||||
blits = blits + 1
|
||||
finish()
|
||||
end
|
||||
|
||||
function valid_transition(curmode, newmode)
|
||||
if curmode == "RM6_BINNING" and newmode == "RM6_GMEM" then
|
||||
return true
|
||||
end
|
||||
if curmode == "RM6_GMEM" and newmode == "RM6_RESOLVE" then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function draw(primtype, nindx)
|
||||
dbg("draw: %s (%s)\n", primtype, mode)
|
||||
nullbatch = false
|
||||
if primtype == "BLIT_OP_SCALE" then
|
||||
handle_blit()
|
||||
return
|
||||
elseif primtype == "EVENT:BLIT" then
|
||||
return
|
||||
end
|
||||
|
||||
local m = tostring(mode)
|
||||
|
||||
-- detect changes in drawmode which indicate a different
|
||||
-- pass.. BINNING->GMEM means same pass, but other
|
||||
-- transitions mean different pass:
|
||||
if drawmode and m ~= drawmode then
|
||||
dbg("%s -> %s transition\n", drawmode, m)
|
||||
if not valid_transition(drawmode, m) then
|
||||
dbg("invalid transition, new render pass!\n")
|
||||
finish()
|
||||
reset()
|
||||
end
|
||||
end
|
||||
|
||||
if m ~= "RM6_GMEM" and m ~= "RM6_BYPASS" then
|
||||
if m == "RM6_BINNING" then
|
||||
drawmode = m
|
||||
return
|
||||
end
|
||||
if m == "RM6_RESOLVE" and primtype == "EVENT:BLIT" then
|
||||
return
|
||||
end
|
||||
printf("unknown MODE %s for primtype %s\n", m, primtype)
|
||||
return
|
||||
end
|
||||
|
||||
-- Only count the first tile for GMEM mode to avoid counting
|
||||
-- each draw for each tile
|
||||
if m == "RM6_GMEM" then
|
||||
if r.RB_WINDOW_OFFSET.X ~= 0 or r.RB_WINDOW_OFFSET.Y ~= 0 then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
drawmode = m
|
||||
local render_components = {}
|
||||
render_components[0] = r.RB_RENDER_COMPONENTS.RT0;
|
||||
render_components[1] = r.RB_RENDER_COMPONENTS.RT1;
|
||||
render_components[2] = r.RB_RENDER_COMPONENTS.RT2;
|
||||
render_components[3] = r.RB_RENDER_COMPONENTS.RT3;
|
||||
render_components[4] = r.RB_RENDER_COMPONENTS.RT4;
|
||||
render_components[5] = r.RB_RENDER_COMPONENTS.RT5;
|
||||
render_components[6] = r.RB_RENDER_COMPONENTS.RT6;
|
||||
render_components[7] = r.RB_RENDER_COMPONENTS.RT7;
|
||||
for n = 0,r.RB_FS_OUTPUT_CNTL1.MRT-1 do
|
||||
if render_components[n] ~= 0 then
|
||||
push_mrt(r.RB_MRT[n].BUF_INFO.COLOR_FORMAT,
|
||||
r.GRAS_SC_SCREEN_SCISSOR[0].BR.X + 1,
|
||||
r.GRAS_SC_SCREEN_SCISSOR[0].BR.Y + 1,
|
||||
r.RB_MSAA_CNTL.SAMPLES,
|
||||
r.RB_MRT[n].BASE_LO | (r.RB_MRT[n].BASE_HI << 32),
|
||||
r.RB_MRT_FLAG_BUFFER[n].ADDR_LO | (r.RB_MRT_FLAG_BUFFER[n].ADDR_HI << 32),
|
||||
r.RB_MRT[n].BASE_GMEM)
|
||||
end
|
||||
end
|
||||
|
||||
local depthbase = r.RB_DEPTH_BUFFER_BASE_LO |
|
||||
(r.RB_DEPTH_BUFFER_BASE_HI << 32)
|
||||
|
||||
if depthbase ~= 0 then
|
||||
push_mrt(r.RB_DEPTH_BUFFER_INFO.DEPTH_FORMAT,
|
||||
r.GRAS_SC_SCREEN_SCISSOR[0].BR.X + 1,
|
||||
r.GRAS_SC_SCREEN_SCISSOR[0].BR.Y + 1,
|
||||
r.RB_MSAA_CNTL.SAMPLES,
|
||||
depthbase,
|
||||
r.RB_DEPTH_FLAG_BUFFER_BASE_LO | (r.RB_DEPTH_FLAG_BUFFER_BASE_HI << 32),
|
||||
r.RB_DEPTH_BUFFER_BASE_GMEM)
|
||||
end
|
||||
|
||||
if r.RB_DEPTH_CNTL.Z_WRITE_ENABLE then
|
||||
depthwrite = true
|
||||
end
|
||||
|
||||
if r.RB_DEPTH_CNTL.Z_ENABLE then
|
||||
depthtest = true
|
||||
end
|
||||
|
||||
-- clearly 0 != false.. :-/
|
||||
if r.RB_STENCILWRMASK.WRMASK ~= 0 then
|
||||
stencilwrite = true
|
||||
end
|
||||
|
||||
if r.RB_STENCIL_CONTROL.STENCIL_ENABLE then
|
||||
stenciltest = true
|
||||
end
|
||||
|
||||
-- TODO should also check for stencil buffer for z32+s8 case
|
||||
|
||||
if m == "RM6_GMEM" then
|
||||
binw = r.VSC_BIN_SIZE.WIDTH
|
||||
binh = r.VSC_BIN_SIZE.HEIGHT
|
||||
nbins = r.VSC_BIN_COUNT.NX * r.VSC_BIN_COUNT.NY
|
||||
end
|
||||
|
||||
draws = draws + 1
|
||||
end
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
-- Parse cmdstream dump and check for common errors
|
||||
-- 1) Check for overflowing HLSQ_xS_CNTL.CONSTLEN
|
||||
-- 2) Check for constant uploades that overwrite each other. The
|
||||
-- range checking is reset on each draw, since it is a valid
|
||||
-- use-case to do partial constant upload. But if we see two
|
||||
-- CP_LOAD_STATE* that overwrite the same range of constants
|
||||
-- within the same draw, that is almost certainly unintentional.
|
||||
--
|
||||
-- TODO add more checks
|
||||
-- TODO maybe some parts could be shared across
|
||||
-- different generations
|
||||
|
||||
--local posix = require "posix"
|
||||
|
||||
function printf(fmt, ...)
|
||||
return io.write(string.format(fmt, ...))
|
||||
end
|
||||
|
||||
function dbg(fmt, ...)
|
||||
--printf(fmt, ...)
|
||||
end
|
||||
|
||||
stages = {
|
||||
"SB6_VS_SHADER",
|
||||
"SB6_HS_SHADER",
|
||||
"SB6_DS_SHADER",
|
||||
"SB6_GS_SHADER",
|
||||
"SB6_FS_SHADER",
|
||||
"SB6_CS_SHADER",
|
||||
}
|
||||
|
||||
-- maps shader stage to HLSQ_xS_CNTL register name:
|
||||
cntl_regs = {
|
||||
["SB6_VS_SHADER"] = "HLSQ_VS_CNTL",
|
||||
["SB6_HS_SHADER"] = "HLSQ_HS_CNTL",
|
||||
["SB6_DS_SHADER"] = "HLSQ_DS_CNTL",
|
||||
["SB6_GS_SHADER"] = "HLSQ_GS_CNTL",
|
||||
["SB6_FS_SHADER"] = "HLSQ_FS_CNTL",
|
||||
["SB6_CS_SHADER"] = "HLSQ_CS_CNTL",
|
||||
}
|
||||
|
||||
-- initialize constant updated ranges:
|
||||
-- constranges[stagename] -> table of offsets that have been uploaded
|
||||
constranges = {}
|
||||
function reset_constranges()
|
||||
for i,stage in ipairs(stages) do
|
||||
constranges[stage] = {}
|
||||
end
|
||||
end
|
||||
|
||||
reset_constranges()
|
||||
|
||||
printf("Checking cmdstream...\n")
|
||||
|
||||
local r = rnn.init("a630")
|
||||
|
||||
function draw(primtype, nindx)
|
||||
printf("draw!\n")
|
||||
-- reset ranges of uploaded consts on each draw:
|
||||
reset_constranges()
|
||||
end
|
||||
|
||||
function CP_LOAD_STATE6(pkt, size)
|
||||
if tostring(pkt[0].STATE_TYPE) ~= "ST6_CONSTANTS" then
|
||||
return
|
||||
end
|
||||
dbg("got CP_LOAD_STATE6\n")
|
||||
stage = tostring(pkt[0].STATE_BLOCK)
|
||||
max = pkt[0].DST_OFF + pkt[0].NUM_UNIT
|
||||
cntl_reg = cntl_regs[stage]
|
||||
dbg("looking for %s.. max=%d vs %d\n", cntl_reg, max, r[cntl_reg].CONSTLEN)
|
||||
if max > r[cntl_reg].CONSTLEN then
|
||||
printf("ERROR: invalid max constant offset for stage %s: %d vs %d\n", stage, max, r[cntl_reg].CONSTLEN)
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,31 @@
|
|||
io.write("HELLO WORLD\n")
|
||||
|
||||
r = rnn.init("a630")
|
||||
|
||||
function start_cmdstream(name)
|
||||
io.write("START: " .. name .. "\n")
|
||||
end
|
||||
|
||||
function draw(primtype, nindx)
|
||||
io.write("DRAW: " .. primtype .. ", " .. nindx .. "\n")
|
||||
-- io.write("GRAS_CL_VPORT_XOFFSET: " .. r.GRAS_CL_VPORT_XOFFSET .. "\n")
|
||||
io.write("RB_MRT[0].CONTROL.ROP_CODE: " .. r.RB_MRT[0].CONTROL.ROP_CODE .. "\n")
|
||||
io.write("SP_VS_OUT[0].A_COMPMASK: " .. r.SP_VS_OUT[0].A_COMPMASK .. "\n")
|
||||
--io.write("RB_DEPTH_CONTROL.Z_ENABLE: " .. tostring(r.RB_DEPTH_CONTROL.Z_ENABLE) .. "\n")
|
||||
io.write("0x2280: written=" .. regs.written(0x2280) .. ", lastval=" .. regs.lastval(0x2280) .. ", val=" .. regs.val(0x2280) .. "\n")
|
||||
end
|
||||
|
||||
function A6XX_TEX_CONST(pkt, size)
|
||||
io.write("\n-------- " .. size .. "\n")
|
||||
io.write("-------- w=" .. pkt[1].WIDTH .. ", h=" .. pkt[1].HEIGHT .. "\n")
|
||||
io.write("\n");
|
||||
end
|
||||
|
||||
function end_cmdstream()
|
||||
io.write("END\n")
|
||||
end
|
||||
|
||||
function finish()
|
||||
io.write("FINISH\n")
|
||||
end
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
-- Parse logs from test-quad-textured-3d.c to exctract layer/level
|
||||
-- offsets
|
||||
--
|
||||
-- We figure out the offsets from blits, but there may be some
|
||||
-- unrelated blits. So just save all of them until we find the
|
||||
-- texture state for the 3d texture. This gives us the base
|
||||
-- address, and the miplevel #0 width/height/depth. Then work
|
||||
-- backwards from there finding the blits to the same dst buffer
|
||||
-- and deducing the miplevel from the minified dimensions
|
||||
|
||||
local posix = require "posix"
|
||||
|
||||
io.write("Analyzing Data...\n")
|
||||
|
||||
local allblits = {}
|
||||
local nallblits = 0
|
||||
local r = rnn.init("a630")
|
||||
|
||||
function minify(val, lvls)
|
||||
val = val >> lvls
|
||||
if val < 1 then
|
||||
return 1
|
||||
end
|
||||
return val
|
||||
end
|
||||
|
||||
function printf(fmt, ...)
|
||||
return io.write(string.format(fmt, ...))
|
||||
end
|
||||
|
||||
function start_cmdstream(name)
|
||||
io.write("Parsing " .. name .. "\n")
|
||||
allblits = {}
|
||||
nallblits = 0
|
||||
end
|
||||
|
||||
function draw(primtype, nindx)
|
||||
if primtype ~= "BLIT_OP_SCALE" then
|
||||
return
|
||||
end
|
||||
|
||||
-- Just in case, filter out anything that isn't starting
|
||||
-- at 0,0
|
||||
if r.GRAS_2D_DST_TL.X ~= 0 or r.GRAS_2D_DST_TL.Y ~= 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local blit = {}
|
||||
|
||||
blit.width = r.GRAS_2D_DST_BR.X + 1
|
||||
blit.height = r.GRAS_2D_DST_BR.Y + 1
|
||||
blit.pitch = r.RB_2D_DST_SIZE.PITCH
|
||||
blit.addr = r.RB_2D_DST_LO | (r.RB_2D_DST_HI << 32)
|
||||
blit.base = bos.base(blit.addr)
|
||||
blit.endaddr = 0 -- filled in later
|
||||
--printf("Found blit: 0x%x (0x%x)\n", blit.addr, blit.base)
|
||||
|
||||
allblits[nallblits] = blit
|
||||
nallblits = nallblits + 1
|
||||
end
|
||||
|
||||
function A6XX_TEX_CONST(pkt, size)
|
||||
-- ignore any texture state w/ DEPTH=1, these aren't the 3d tex state we
|
||||
-- are looking for
|
||||
if pkt[5].DEPTH <= 1 then
|
||||
return
|
||||
end
|
||||
|
||||
local base = pkt[4].BASE_LO | (pkt[5].BASE_HI << 32)
|
||||
local width0 = pkt[1].WIDTH
|
||||
local height0 = pkt[1].HEIGHT
|
||||
local depth0 = pkt[5].DEPTH
|
||||
|
||||
printf("Found texture state: %ux%ux%u (MIN_LAYERSZ=0x%x)\n",
|
||||
width0, height0, depth0, pkt[3].MIN_LAYERSZ)
|
||||
|
||||
-- Note that in some case the texture has some extra page or so
|
||||
-- at the beginning:
|
||||
local basebase = bos.base(base)
|
||||
printf("base: 0x%x (0x%x)\n", base, basebase)
|
||||
|
||||
-- see if we can find the associated blits.. The blob always seems to
|
||||
-- start from the lower (larger) mipmap levels and layers, so we don't
|
||||
-- need to sort by dst address. Also, while we are at it, fill in the
|
||||
-- end-addr (at least for everything but the last blit)
|
||||
local blits = {}
|
||||
local nblits = 0
|
||||
local lastblit = nil
|
||||
for n = 0,nallblits-1 do
|
||||
local blit = allblits[n]
|
||||
--printf("blit addr: 0x%x (0x%x)\n", blit.addr, blit.base)
|
||||
if blit.base == basebase and blit.addr >= base then
|
||||
blits[nblits] = blit
|
||||
nblits = nblits + 1
|
||||
if lastblit then
|
||||
lastblit.endaddr = blit.addr
|
||||
end
|
||||
lastblit = blit
|
||||
end
|
||||
end
|
||||
|
||||
-- now go thru the relevant blits and print out interesting details
|
||||
local level = 0
|
||||
local layer = 0
|
||||
local w = width0 -- track current width/height to detect changing
|
||||
local h = height0 -- mipmap level
|
||||
for n = 0,nblits-1 do
|
||||
local blit = blits[n]
|
||||
--printf("%u: %ux%u, addr=%x\n", n, blit.width, blit.height, blit.addr)
|
||||
if w ~= blit.width or h ~= blit.height then
|
||||
level = level + 1
|
||||
layer = 0
|
||||
|
||||
if blit.width ~= minify(w, 1) or blit.height ~= minify(h, 1) then
|
||||
printf("I am confused! %ux%u vs %ux%u\n", blit.width, blit.height, minify(w, 1), minify(h, 1))
|
||||
printf("addr=%x\n", blit.addr)
|
||||
--return
|
||||
end
|
||||
|
||||
w = blit.width
|
||||
h = blit.height
|
||||
end
|
||||
|
||||
printf("level=%u, layer=%u, sz=%ux%u, pitch=%u, offset=0x%x, addr=%x",
|
||||
level, layer, w, h, blit.pitch, blit.addr - base, blit.addr)
|
||||
if blit.endaddr ~= 0 then
|
||||
local layersz = blit.endaddr - blit.addr
|
||||
local alignedheight = layersz / blit.pitch
|
||||
printf(", layersz=0x%x, alignedheight=%f", layersz, alignedheight)
|
||||
end
|
||||
printf("\n")
|
||||
|
||||
layer = layer + 1
|
||||
end
|
||||
printf("\n\n")
|
||||
end
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
-- Parse logs from https://github.com/freedreno/freedreno/
|
||||
-- test-texturator.c to generate a src/freedreno/fdl/fd5_layout_test.c
|
||||
-- block. We figure out the offsets from blits, but there may be some
|
||||
-- unrelated blits. So just save all of them until we find the
|
||||
-- texture state. This gives us the base address, and the miplevel #0
|
||||
-- width/height/depth. Then work backwards from there finding the
|
||||
-- blits to the same dst buffer and deducing the miplevel from the
|
||||
-- minified dimensions
|
||||
|
||||
local posix = require "posix"
|
||||
|
||||
io.write("Analyzing Data...\n")
|
||||
|
||||
local r = rnn.init("a530")
|
||||
local found_tex = 0
|
||||
|
||||
local allblits = {}
|
||||
local nallblits = 0
|
||||
|
||||
function get_first_blit(base, width, height)
|
||||
local first_blit = nil
|
||||
|
||||
for n = 0,nallblits-1 do
|
||||
local blit = allblits[n]
|
||||
if blit.base == base and blit.width == width and blit.height == height then
|
||||
if not first_blit or blit.addr < first_blit.addr then
|
||||
first_blit = blit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return first_blit
|
||||
end
|
||||
|
||||
function minify(val, lvls)
|
||||
val = val >> lvls
|
||||
if val < 1 then
|
||||
return 1
|
||||
end
|
||||
return val
|
||||
end
|
||||
|
||||
function printf(fmt, ...)
|
||||
return io.write(string.format(fmt, ...))
|
||||
end
|
||||
|
||||
function start_cmdstream(name)
|
||||
io.write("Parsing " .. name .. "\n")
|
||||
allblits = {}
|
||||
nallblits = 0
|
||||
end
|
||||
|
||||
-- Record texture upload blits done through CP_EVENT_WRITE
|
||||
function CP_EVENT_WRITE(pkt, size)
|
||||
if tostring(pkt[0].EVENT) ~= "BLIT" then
|
||||
return
|
||||
end
|
||||
|
||||
local blit = {}
|
||||
|
||||
blit.width = r.RB_RESOLVE_CNTL_2.X + 1
|
||||
blit.height = r.RB_RESOLVE_CNTL_2.Y + 1
|
||||
blit.pitch = r.RB_BLIT_DST_PITCH
|
||||
blit.addr = r.RB_BLIT_DST_LO | (r.RB_BLIT_DST_HI << 32)
|
||||
blit.base = bos.base(blit.addr)
|
||||
blit.ubwc_addr = r.RB_BLIT_FLAG_DST_LO | (r.RB_BLIT_FLAG_DST_HI << 32)
|
||||
blit.ubwc_base = bos.base(blit.ubwc_addr)
|
||||
blit.ubwc_pitch = r.RB_BLIT_FLAG_DST_PITCH
|
||||
blit.endaddr = 0 -- filled in later
|
||||
printf("Found event blit: 0x%x (0x%x) %dx%d UBWC 0x%x (0x%x) tiled %s\n", blit.addr, blit.base, blit.width, blit.height, blit.ubwc_addr, blit.ubwc_base, r.RB_RESOLVE_CNTL_3.TILED)
|
||||
|
||||
allblits[nallblits] = blit
|
||||
nallblits = nallblits + 1
|
||||
end
|
||||
|
||||
function CP_BLIT(pkt, size)
|
||||
-- Just in case, filter out anything that isn't starting
|
||||
-- at 0,0
|
||||
if pkt[1].SRC_X1 ~= 0 or pkt[1].SRC_Y1 ~= 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local blit = {}
|
||||
|
||||
blit.width = pkt[2].SRC_X2 + 1
|
||||
blit.height = pkt[2].SRC_Y2 + 1
|
||||
blit.pitch = r.RB_2D_DST_SIZE.PITCH
|
||||
blit.addr = r.RB_2D_DST_LO | (r.RB_2D_DST_HI << 32)
|
||||
blit.base = bos.base(blit.addr)
|
||||
blit.ubwc_addr = r.RB_2D_DST_FLAGS_LO | (r.RB_2D_DST_FLAGS_HI << 32)
|
||||
blit.ubwc_base = bos.base(blit.ubwc_addr)
|
||||
blit.ubwc_pitch = r.RB_2D_DST_FLAGS_PITCH
|
||||
blit.endaddr = 0 -- filled in later
|
||||
printf("Found cp blit: 0x%x (0x%x) %dx%d UBWC 0x%x (0x%x) %s\n", blit.addr, blit.base, blit.width, blit.height, blit.ubwc_addr, blit.ubwc_base, r.RB_2D_DST_INFO.TILE_MODE)
|
||||
|
||||
allblits[nallblits] = blit
|
||||
nallblits = nallblits + 1
|
||||
end
|
||||
|
||||
function A5XX_TEX_CONST(pkt, size)
|
||||
-- ignore any texture state w/ DEPTH=1, these aren't the 3d tex state we
|
||||
-- are looking for
|
||||
|
||||
local base = pkt[4].BASE_LO | (pkt[5].BASE_HI << 32)
|
||||
-- UBWC base on a5xx seems to be at the start of each miplevel, followed by pixels
|
||||
-- somewhere past that.
|
||||
local ubwc_base = base
|
||||
local width0 = pkt[1].WIDTH
|
||||
local height0 = pkt[1].HEIGHT
|
||||
local depth0 = pkt[5].DEPTH
|
||||
|
||||
if (found_tex ~= 0) then
|
||||
return
|
||||
end
|
||||
found_tex = 1
|
||||
|
||||
printf("Found texture state:\n %ux%ux%u (%s, %s, UBWC=%s)\n",
|
||||
width0, height0, depth0, pkt[0].FMT, pkt[0].TILE_MODE, tostring(pkt[3].FLAG))
|
||||
|
||||
-- Note that in some case the texture has some extra page or so
|
||||
-- at the beginning:
|
||||
local basebase = bos.base(base)
|
||||
printf("base: 0x%x (0x%x)\n", base, basebase)
|
||||
printf("ubwcbase: 0x%x (0x%x)\n", ubwc_base, bos.base(ubwc_base))
|
||||
|
||||
-- see if we can find the associated blits.. The blob always seems to
|
||||
-- start from the lower (larger) mipmap levels and layers, so we don't
|
||||
-- need to sort by dst address. Also, while we are at it, fill in the
|
||||
-- end-addr (at least for everything but the last blit)
|
||||
local blits = {}
|
||||
local nblits = 0
|
||||
local lastblit = nil
|
||||
for n = 0,nallblits-1 do
|
||||
local blit = allblits[n]
|
||||
--printf("blit addr: 0x%x (0x%x)\n", blit.addr, blit.base)
|
||||
if blit.base == basebase and blit.addr >= base then
|
||||
blits[nblits] = blit
|
||||
nblits = nblits + 1
|
||||
if lastblit then
|
||||
lastblit.endaddr = blit.addr
|
||||
end
|
||||
lastblit = blit
|
||||
end
|
||||
end
|
||||
|
||||
printf(" {\n")
|
||||
printf(" .format = %s,\n", pkt[0].FMT)
|
||||
if (tostring(pkt[2].TYPE) == "A5XX_TEX_3D") then
|
||||
printf(" .is_3d = true,\n")
|
||||
end
|
||||
|
||||
printf(" .layout = {\n")
|
||||
printf(" .tile_mode = %s,\n", pkt[0].TILE_MODE)
|
||||
printf(" .ubwc = %s,\n", tostring(pkt[3].FLAG))
|
||||
|
||||
if (tostring(pkt[2].TYPE) == "A5XX_TEX_3D") then
|
||||
printf(" .width0 = %d, .height0 = %d, .depth0 = %d,\n", width0, height0, depth0)
|
||||
else
|
||||
printf(" .width0 = %d, .height0 = %d,\n", width0, height0)
|
||||
end
|
||||
|
||||
printf(" .slices = {\n")
|
||||
local w = 0
|
||||
local h = 0
|
||||
local level = 0
|
||||
repeat
|
||||
local w = minify(width0, level)
|
||||
local h = minify(height0, level)
|
||||
local blit = get_first_blit(basebase, w, h)
|
||||
if blit then
|
||||
printf(" { .offset = %d, .pitch = %u },\n",
|
||||
blit.addr - base,
|
||||
blit.pitch);
|
||||
end
|
||||
level = level + 1
|
||||
until w == 1 and h == 1
|
||||
printf(" },\n")
|
||||
|
||||
if pkt[3].FLAG then
|
||||
printf(" .ubwc_slices = {\n")
|
||||
level = 0
|
||||
repeat
|
||||
local w = minify(width0, level)
|
||||
local h = minify(height0, level)
|
||||
local blit = get_first_blit(basebase, w, h)
|
||||
if blit then
|
||||
printf(" { .offset = %d, .pitch = %u },\n",
|
||||
blit.ubwc_addr - ubwc_base,
|
||||
blit.ubwc_pitch);
|
||||
end
|
||||
level = level + 1
|
||||
until w == 1 and h == 1
|
||||
printf(" },\n")
|
||||
end
|
||||
|
||||
printf(" },\n")
|
||||
printf(" },\n")
|
||||
printf("\n\n")
|
||||
end
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
-- Parse logs from https://github.com/freedreno/freedreno/
|
||||
-- test-texturator.c to generate a src/freedreno/fdl/fd6_layout_test.c
|
||||
-- block. We figure out the offsets from blits, but there may be some
|
||||
-- unrelated blits. So just save all of them until we find the
|
||||
-- texture state. This gives us the base address, and the miplevel #0
|
||||
-- width/height/depth. Then work backwards from there finding the
|
||||
-- blits to the same dst buffer and deducing the miplevel from the
|
||||
-- minified dimensions
|
||||
|
||||
local posix = require "posix"
|
||||
|
||||
io.write("Analyzing Data...\n")
|
||||
|
||||
local r = rnn.init("a630")
|
||||
local found_tex = 0
|
||||
|
||||
local allblits = {}
|
||||
local nallblits = 0
|
||||
|
||||
function get_first_blit(base, width, height)
|
||||
local first_blit = nil
|
||||
|
||||
for n = 0,nallblits-1 do
|
||||
local blit = allblits[n]
|
||||
if blit.base == base and blit.width == width and blit.height == height then
|
||||
if not first_blit or blit.addr < first_blit.addr then
|
||||
first_blit = blit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return first_blit
|
||||
end
|
||||
|
||||
function minify(val, lvls)
|
||||
val = val >> lvls
|
||||
if val < 1 then
|
||||
return 1
|
||||
end
|
||||
return val
|
||||
end
|
||||
|
||||
function printf(fmt, ...)
|
||||
return io.write(string.format(fmt, ...))
|
||||
end
|
||||
|
||||
function start_cmdstream(name)
|
||||
io.write("Parsing " .. name .. "\n")
|
||||
allblits = {}
|
||||
nallblits = 0
|
||||
end
|
||||
|
||||
function draw(primtype, nindx)
|
||||
if primtype ~= "BLIT_OP_SCALE" then
|
||||
return
|
||||
end
|
||||
|
||||
-- Just in case, filter out anything that isn't starting
|
||||
-- at 0,0
|
||||
if r.GRAS_2D_DST_TL.X ~= 0 or r.GRAS_2D_DST_TL.Y ~= 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local blit = {}
|
||||
|
||||
blit.width = r.GRAS_2D_DST_BR.X + 1
|
||||
blit.height = r.GRAS_2D_DST_BR.Y + 1
|
||||
blit.pitch = r.RB_2D_DST_SIZE.PITCH
|
||||
blit.addr = r.RB_2D_DST_LO | (r.RB_2D_DST_HI << 32)
|
||||
blit.base = bos.base(blit.addr)
|
||||
blit.ubwc_addr = r.RB_2D_DST_FLAGS_LO | (r.RB_2D_DST_FLAGS_HI << 32)
|
||||
blit.ubwc_base = bos.base(blit.uwbc_addr)
|
||||
blit.ubwc_pitch = r.RB_2D_DST_FLAGS_PITCH.PITCH
|
||||
blit.endaddr = 0 -- filled in later
|
||||
printf("Found blit: 0x%x (0x%x) %dx%d UBWC 0x%x (0x%x)\n", blit.addr, blit.base, blit.width, blit.height, blit.ubwc_addr, blit.ubwc_base)
|
||||
|
||||
allblits[nallblits] = blit
|
||||
nallblits = nallblits + 1
|
||||
end
|
||||
|
||||
function A6XX_TEX_CONST(pkt, size)
|
||||
-- ignore any texture state w/ DEPTH=1, these aren't the 3d tex state we
|
||||
-- are looking for
|
||||
|
||||
local base = pkt[4].BASE_LO | (pkt[5].BASE_HI << 32)
|
||||
local ubwc_base = pkt[7].FLAG_LO | (pkt[8].FLAG_HI << 32)
|
||||
local width0 = pkt[1].WIDTH
|
||||
local height0 = pkt[1].HEIGHT
|
||||
local depth0 = pkt[5].DEPTH
|
||||
|
||||
if (found_tex ~= 0) then
|
||||
return
|
||||
end
|
||||
found_tex = 1
|
||||
|
||||
printf("Found texture state:\n %ux%ux%u (%s, %s, MIN_LAYERSZ=0x%x, TILE_ALL=%s, UBWC=%s FLAG_LOG2=%ux%u)\n",
|
||||
width0, height0, depth0, pkt[0].FMT, pkt[0].TILE_MODE, pkt[3].MIN_LAYERSZ, tostring(pkt[3].TILE_ALL), tostring(pkt[3].FLAG), pkt[10].FLAG_BUFFER_LOGW, pkt[10].FLAG_BUFFER_LOGH)
|
||||
|
||||
-- Note that in some case the texture has some extra page or so
|
||||
-- at the beginning:
|
||||
local basebase = bos.base(base)
|
||||
printf("base: 0x%x (0x%x)\n", base, basebase)
|
||||
printf("ubwcbase: 0x%x (0x%x)\n", ubwc_base, bos.base(ubwc_base))
|
||||
|
||||
-- see if we can find the associated blits.. The blob always seems to
|
||||
-- start from the lower (larger) mipmap levels and layers, so we don't
|
||||
-- need to sort by dst address. Also, while we are at it, fill in the
|
||||
-- end-addr (at least for everything but the last blit)
|
||||
local blits = {}
|
||||
local nblits = 0
|
||||
local lastblit = nil
|
||||
for n = 0,nallblits-1 do
|
||||
local blit = allblits[n]
|
||||
--printf("blit addr: 0x%x (0x%x)\n", blit.addr, blit.base)
|
||||
if blit.base == basebase and blit.addr >= base then
|
||||
blits[nblits] = blit
|
||||
nblits = nblits + 1
|
||||
if lastblit then
|
||||
lastblit.endaddr = blit.addr
|
||||
end
|
||||
lastblit = blit
|
||||
end
|
||||
end
|
||||
|
||||
printf(" {\n")
|
||||
printf(" .format = %s,\n", pkt[0].FMT)
|
||||
if (tostring(pkt[2].TYPE) == "A6XX_TEX_3D") then
|
||||
printf(" .is_3d = true,\n")
|
||||
end
|
||||
|
||||
printf(" .layout = {\n")
|
||||
printf(" .tile_mode = %s,\n", pkt[0].TILE_MODE)
|
||||
printf(" .ubwc = %s,\n", tostring(pkt[3].FLAG))
|
||||
|
||||
if (tostring(pkt[2].TYPE) == "A6XX_TEX_3D") then
|
||||
printf(" .width0 = %d, .height0 = %d, .depth = %d,\n", width0, height0, depth0)
|
||||
else
|
||||
printf(" .width0 = %d, .height0 = %d,\n", width0, height0)
|
||||
end
|
||||
|
||||
printf(" .slices = {\n")
|
||||
local w = 0
|
||||
local h = 0
|
||||
local level = 0
|
||||
repeat
|
||||
local w = minify(width0, level)
|
||||
local h = minify(height0, level)
|
||||
local blit = get_first_blit(basebase, w, h)
|
||||
if blit then
|
||||
printf(" { .offset = %d, .pitch = %u },\n",
|
||||
blit.addr - base,
|
||||
blit.pitch);
|
||||
end
|
||||
level = level + 1
|
||||
until w == 1 and h == 1
|
||||
printf(" },\n")
|
||||
|
||||
if pkt[3].FLAG then
|
||||
printf(" .ubwc_slices = {\n")
|
||||
level = 0
|
||||
repeat
|
||||
local w = minify(width0, level)
|
||||
local h = minify(height0, level)
|
||||
local blit = get_first_blit(basebase, w, h)
|
||||
if blit then
|
||||
printf(" { .offset = %d, .pitch = %u },\n",
|
||||
blit.ubwc_addr - ubwc_base,
|
||||
blit.ubwc_pitch);
|
||||
end
|
||||
level = level + 1
|
||||
until w == 1 and h == 1
|
||||
printf(" },\n")
|
||||
end
|
||||
|
||||
printf(" },\n")
|
||||
printf(" },\n")
|
||||
printf("\n\n")
|
||||
end
|
||||
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2018 Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* 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 __UTIL_H__
|
||||
#define __UTIL_H__
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* old-style program binary XOR'd ascii w/ 0xff */
|
||||
#ifndef ASCII_XOR
|
||||
# define ASCII_XOR 0
|
||||
#endif
|
||||
|
||||
static inline const char *tab(int lvl)
|
||||
{
|
||||
const char *TAB = "\t\t\t\t\t\t\t\t\0";
|
||||
return &TAB[strlen(TAB) - lvl];
|
||||
}
|
||||
|
||||
/* convert float to dword */
|
||||
static inline float d2f(uint32_t d)
|
||||
{
|
||||
union {
|
||||
float f;
|
||||
uint32_t d;
|
||||
} u = {
|
||||
.d = d,
|
||||
};
|
||||
return u.f;
|
||||
}
|
||||
|
||||
static inline void dump_hex(const void *buf, int sz)
|
||||
{
|
||||
uint8_t *ptr = (uint8_t *)buf;
|
||||
uint8_t *end = ptr + sz;
|
||||
int i = 0;
|
||||
|
||||
while (ptr < end) {
|
||||
uint32_t d = 0;
|
||||
|
||||
printf((i % 8) ? " " : "\t");
|
||||
|
||||
d |= *(ptr++) << 0;
|
||||
d |= *(ptr++) << 8;
|
||||
d |= *(ptr++) << 16;
|
||||
d |= *(ptr++) << 24;
|
||||
|
||||
printf("%08x", d);
|
||||
|
||||
if ((i % 8) == 7) {
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i % 8) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
dump_float(const void *buf, int sz)
|
||||
{
|
||||
uint8_t *ptr = (uint8_t *)buf;
|
||||
uint8_t *end = ptr + sz - 3;
|
||||
int i = 0;
|
||||
|
||||
while (ptr < end) {
|
||||
uint32_t d = 0;
|
||||
|
||||
printf((i % 8) ? " " : "\t");
|
||||
|
||||
d |= *(ptr++) << 0;
|
||||
d |= *(ptr++) << 8;
|
||||
d |= *(ptr++) << 16;
|
||||
d |= *(ptr++) << 24;
|
||||
|
||||
printf("%8f", d2f(d));
|
||||
|
||||
if ((i % 8) == 7) {
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i % 8) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
#define is_ok_ascii(c) \
|
||||
(isascii(c) && ((c == '\t') || !iscntrl(c)))
|
||||
|
||||
static inline void
|
||||
clean_ascii(char *buf, int sz)
|
||||
{
|
||||
uint8_t *ptr = (uint8_t *)buf;
|
||||
uint8_t *end = ptr + sz;
|
||||
while (ptr < end) {
|
||||
*(ptr++) ^= ASCII_XOR;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
dump_ascii(const void *buf, int sz)
|
||||
{
|
||||
uint8_t *ptr = (uint8_t *)buf;
|
||||
uint8_t *end = ptr + sz;
|
||||
printf("\t");
|
||||
while (ptr < end) {
|
||||
uint8_t c = *(ptr++) ^ ASCII_XOR;
|
||||
if (c == '\n') {
|
||||
printf("\n\t");
|
||||
} else if (c == '\0') {
|
||||
printf("\n\t-----------------------------------\n\t");
|
||||
} else if (is_ok_ascii(c)) {
|
||||
printf("%c", c);
|
||||
} else {
|
||||
printf("?");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static inline void
|
||||
dump_hex_ascii(const void *buf, int sz, int level)
|
||||
{
|
||||
uint8_t *ptr = (uint8_t *)buf;
|
||||
uint8_t *end = ptr + sz;
|
||||
uint8_t *ascii = ptr;
|
||||
int i = 0;
|
||||
|
||||
printf("%s-----------------------------------------------\n", tab(level));
|
||||
printf("%s%d (0x%x) bytes\n", tab(level), sz, sz);
|
||||
|
||||
while (ptr < end) {
|
||||
uint32_t d = 0;
|
||||
|
||||
if (i % 4) {
|
||||
printf(" ");
|
||||
} else {
|
||||
printf("%s%06x: ", tab(level), (uint32_t)(ptr - (uint8_t *)buf));
|
||||
}
|
||||
|
||||
d |= *(ptr++) << 0;
|
||||
d |= *(ptr++) << 8;
|
||||
d |= *(ptr++) << 16;
|
||||
d |= *(ptr++) << 24;
|
||||
|
||||
printf("%08x", d);
|
||||
|
||||
if ((i % 4) == 3) {
|
||||
int j;
|
||||
printf("\t|");
|
||||
for (j = 0; j < 16; j++) {
|
||||
uint8_t c = *(ascii++);
|
||||
c ^= ASCII_XOR;
|
||||
printf("%c", (isascii(c) && !iscntrl(c)) ? c : '.');
|
||||
}
|
||||
printf("|\n");
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i % 4) {
|
||||
for (int j = 4 - (i % 4); j > 0; j--) {
|
||||
printf(" ");
|
||||
}
|
||||
printf("\t|");
|
||||
while (ascii < end) {
|
||||
uint8_t c = *(ascii++);
|
||||
c ^= ASCII_XOR;
|
||||
printf("%c", (isascii(c) && !iscntrl(c)) ? c : '.');
|
||||
}
|
||||
printf("|\n");
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __UTIL_H__ */
|
|
@ -19,6 +19,7 @@
|
|||
# SOFTWARE.
|
||||
|
||||
inc_freedreno = include_directories(['.', './registers'])
|
||||
inc_freedreno_rnn = include_directories('rnn')
|
||||
|
||||
subdir('common')
|
||||
subdir('registers')
|
||||
|
@ -33,6 +34,7 @@ dep_libxml2 = dependency('libxml-2.0', required: false)
|
|||
# Everything that depends on rnn requires (indirectly) libxml2:
|
||||
if dep_libxml2.found()
|
||||
subdir('rnn')
|
||||
subdir('decode')
|
||||
endif
|
||||
|
||||
if with_tools.contains('drm-shim')
|
||||
|
|
Loading…
Reference in New Issue