pvr: debug: Add "cs" debug option to dump control stream on job submit

With PVR_DEBUG=cs, the control stream will be dumped to stderr
immediately prior to every render or compute job submission.

Signed-off-by: Matt Coster <matt.coster@imgtec.com>
Reviewed-by: Karmjit Mahil <Karmjit.Mahil@imgtec.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/18948>
This commit is contained in:
Matt Coster 2022-06-29 09:47:33 +01:00 committed by Marge Bot
parent 0432015265
commit f9a234ef04
18 changed files with 2250 additions and 13 deletions

View file

@ -24,11 +24,13 @@ libpowervr_common = static_library(
[
'pvr_debug.c',
'pvr_device_info.c',
'pvr_dump.c',
'pvr_util.c',
],
include_directories : [
inc_include,
inc_src,
inc_imagination,
],
c_args : [no_override_init_args],
gnu_symbol_visibility : 'hidden',

View file

@ -28,13 +28,13 @@
uint32_t PVR_DEBUG = 0;
/* clang-format off */
static const struct debug_named_value debug_control[] = {
/* Define debug values here in the same order as in "pvr_debug.h". Example:
{ "some_option", PVR_DEBUG_SOME_OPTION,
"This is a description for some option" },
*/
{ "cs", PVR_DEBUG_DUMP_CONTROL_STREAM,
"Dump the contents of the control stream buffer on every job submit." },
DEBUG_NAMED_VALUE_END
};
/* clang-format on */
DEBUG_GET_ONCE_FLAGS_OPTION(pvr_debug, "PVR_DEBUG", debug_control, 0)

View file

@ -30,9 +30,11 @@
extern uint32_t PVR_DEBUG;
/* Define debug values here. Example:
#define PVR_DEBUG_SOME_OPTION BITFIELD_BIT(0)
*/
/* clang-format off */
#define PVR_IS_DEBUG_SET(x) unlikely(PVR_DEBUG & PVR_DEBUG_##x)
/* clang-format on */
#define PVR_DEBUG_DUMP_CONTROL_STREAM BITFIELD_BIT(0)
void pvr_process_debug_variable(void);

View file

@ -0,0 +1,276 @@
/*
* Copyright © 2022 Imagination Technologies Ltd.
*
* 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 <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include "pvr_dump.h"
#include "pvr_util.h"
const struct pvr_dump_ctx __pvr_dump_ctx_invalid = {
.active_child = &__pvr_dump_ctx_invalid,
};
/*****************************************************************************
Hex dumps
*****************************************************************************/
#define HEX_WORD_SIZE ((unsigned)sizeof(uint32_t))
#define HEX_WORD_FMT "%08" PRIx32
/* This must be even, and should probably always be a power of 2. */
#define HEX_LINE_SIZE UINT32_C(8)
struct pvr_dump_hex_ctx {
struct pvr_dump_ctx base;
const uint32_t *start_ptr;
const uint32_t *end_ptr;
uint64_t nr_words;
uint32_t offset_digits;
/* User-modifiable values */
const uint32_t *line_ptr;
uint32_t prev_non_zero_trailing_zero_words;
uint64_t prev_non_zero_leading_zero_lines;
const uint32_t *prev_non_zero_line;
uint64_t zero_lines;
};
static bool pvr_dump_hex_ctx_push(struct pvr_dump_hex_ctx *const ctx,
struct pvr_dump_buffer_ctx *const parent_ctx,
const uint64_t nr_words)
{
const uint64_t real_nr_words =
nr_words ? nr_words : parent_ctx->remaining_size / HEX_WORD_SIZE;
const uint64_t nr_bytes = real_nr_words * HEX_WORD_SIZE;
bool ret;
if (parent_ctx->remaining_size < nr_bytes ||
(!nr_words && nr_bytes != parent_ctx->remaining_size) ||
!ptr_is_aligned(parent_ctx->ptr, HEX_WORD_SIZE)) {
return false;
}
ret = pvr_dump_ctx_push(&ctx->base, &parent_ctx->base);
if (!ret)
return false;
ctx->start_ptr = parent_ctx->ptr;
ctx->end_ptr = ctx->start_ptr + real_nr_words;
ctx->nr_words = real_nr_words;
ctx->offset_digits = u64_hex_digits(nr_bytes);
ctx->line_ptr = ctx->start_ptr;
ctx->prev_non_zero_trailing_zero_words = 0;
ctx->prev_non_zero_leading_zero_lines = 0;
ctx->prev_non_zero_line = NULL;
ctx->zero_lines = 0;
return true;
}
static struct pvr_dump_buffer_ctx *
pvr_dump_hex_ctx_pop(struct pvr_dump_hex_ctx *const ctx)
{
struct pvr_dump_buffer_ctx *parent;
struct pvr_dump_ctx *parent_base;
if (ctx->line_ptr != ctx->end_ptr) {
ctx->base.ok = false;
return NULL;
}
parent_base = pvr_dump_ctx_pop(&ctx->base);
if (!parent_base)
return NULL;
parent = container_of(parent_base, struct pvr_dump_buffer_ctx, base);
pvr_dump_buffer_advance(parent, ctx->nr_words * HEX_WORD_SIZE);
return parent;
}
static inline void pvr_dump_hex_print_prefix(const struct pvr_dump_hex_ctx *ctx,
const uint64_t offset)
{
pvr_dump_printf(&ctx->base,
PVR_DUMP_OFFSET_PREFIX,
ctx->offset_digits,
offset * HEX_WORD_SIZE);
}
#define pvr_dump_hex_println(ctx, offset, format, args...) \
pvr_dump_println(&(ctx)->base, \
PVR_DUMP_OFFSET_PREFIX format, \
(ctx)->offset_digits, \
offset, \
##args);
#define pvr_dump_hex_println_no_prefix(ctx, format, args...) \
pvr_dump_println(&(ctx)->base, \
"%*c" format, \
(ctx)->offset_digits + 3, \
' ', \
##args);
static void
pvr_dump_hex_print_zero_lines(const struct pvr_dump_hex_ctx *const ctx,
const uint64_t zero_lines)
{
const uint64_t zero_words = zero_lines * HEX_LINE_SIZE;
const uint64_t zero_bytes = zero_words * HEX_WORD_SIZE;
if (zero_lines == 0)
return;
pvr_dump_hex_println_no_prefix(ctx,
" + %" PRIu64 " zero line%s (%" PRIu64
" words; %" PRIu64 "/0x%" PRIx64 " bytes)",
zero_lines,
zero_lines == 1 ? "" : "s",
zero_words,
zero_bytes,
zero_bytes);
}
static void
pvr_dump_hex_print_trailing_zeroes(const struct pvr_dump_hex_ctx *const ctx)
{
const uint64_t zero_words =
ctx->zero_lines * HEX_LINE_SIZE + ctx->prev_non_zero_trailing_zero_words;
const uint64_t zero_bytes = zero_words * HEX_WORD_SIZE;
if (!ctx->prev_non_zero_trailing_zero_words)
return pvr_dump_hex_print_zero_lines(ctx, ctx->zero_lines);
if (!ctx->zero_lines)
return;
pvr_dump_hex_println_no_prefix(ctx,
" + %" PRIu64 "+%" PRIu32
" zero lines (%" PRIu64 " words; %" PRIu64
"/0x%" PRIx64 " bytes)",
ctx->zero_lines,
ctx->prev_non_zero_trailing_zero_words,
zero_words,
zero_bytes,
zero_bytes);
}
static void pvr_dump_hex_print_line(struct pvr_dump_hex_ctx *ctx,
const uint32_t *const line_ptr,
const uint32_t truncate)
{
const uint32_t nr_words =
MIN2(HEX_LINE_SIZE - truncate, ctx->end_ptr - line_ptr);
pvr_dump_hex_print_prefix(ctx, line_ptr - ctx->start_ptr);
for (uint32_t i = 0; i < nr_words; i++) {
if (i == HEX_LINE_SIZE / 2)
pvr_dump_printf_cont(&ctx->base, " ");
pvr_dump_printf_cont(&ctx->base, " " HEX_WORD_FMT, line_ptr[i]);
}
pvr_dump_print_eol(&ctx->base);
}
static void pvr_dump_hex_process_line(struct pvr_dump_hex_ctx *const ctx)
{
uint32_t trailing_zero_words = HEX_LINE_SIZE;
for (uint32_t i = HEX_LINE_SIZE; i > 0; i--) {
if (ctx->line_ptr[i - 1]) {
trailing_zero_words = HEX_LINE_SIZE - i;
break;
}
}
if (trailing_zero_words == HEX_LINE_SIZE) {
/* No non-zero words were found in this line; mark it and move on. */
ctx->zero_lines++;
return;
}
/* We have at least one non-zero word in this line. If we have a previous
* non-zero line stored, collapse and print any leading zero-only lines
* before it then print the stored line.
*/
if (ctx->prev_non_zero_line) {
pvr_dump_hex_print_zero_lines(ctx, ctx->prev_non_zero_leading_zero_lines);
pvr_dump_hex_print_line(ctx, ctx->prev_non_zero_line, 0);
}
/* Now we store the current non-zero line for printing later. This way we
* can treat the last non-zero line specially.
*/
ctx->prev_non_zero_line = ctx->line_ptr;
ctx->prev_non_zero_leading_zero_lines = ctx->zero_lines;
ctx->prev_non_zero_trailing_zero_words = trailing_zero_words;
ctx->zero_lines = 0;
}
static void pvr_dump_hex(struct pvr_dump_hex_ctx *const ctx)
{
while (ctx->end_ptr - ctx->line_ptr > 0) {
pvr_dump_hex_process_line(ctx);
ctx->line_ptr += HEX_LINE_SIZE;
}
if (ctx->prev_non_zero_line) {
pvr_dump_hex_print_zero_lines(ctx, ctx->prev_non_zero_leading_zero_lines);
pvr_dump_hex_print_line(ctx,
ctx->prev_non_zero_line,
ctx->prev_non_zero_trailing_zero_words);
/* Collapse and print any trailing zeroes. */
pvr_dump_hex_print_trailing_zeroes(ctx);
} else {
/* We made it to the end of the buffer without ever encountering a
* non-zero word. Make this known.
*/
pvr_dump_hex_println(ctx, UINT64_C(0), " <empty buffer>");
}
pvr_dump_hex_println(ctx, ctx->nr_words, " <end of buffer>");
}
bool pvr_dump_buffer_hex(struct pvr_dump_buffer_ctx *const ctx,
const uint64_t nr_words)
{
struct pvr_dump_hex_ctx hex_ctx;
if (!pvr_dump_hex_ctx_push(&hex_ctx, ctx, nr_words))
return false;
pvr_dump_hex(&hex_ctx);
return !!pvr_dump_hex_ctx_pop(&hex_ctx);
}

View file

@ -0,0 +1,594 @@
/*
* Copyright © 2022 Imagination Technologies Ltd.
*
* 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 PVR_DUMP_H
#define PVR_DUMP_H
#include <inttypes.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "pvr_types.h"
#include "pvr_util.h"
#include "util/macros.h"
#include "util/u_math.h"
/** BASIC PRINTING **/
#define PVR_DUMP_OFFSET_PREFIX "[%0*" PRIx64 "] "
/** CONTEXTS **/
#define PVR_DUMP_INDENT_SIZE 2U
#define PVR_DUMP_FIELD_COLUMN_WIDTH 36U
/* This is an invalid context used to permanently mark popped contexts as
* unusable. All operations on a context check that it's the "top" context
* by ensuring it has no active child. The only way to remove the active child
* of a context is by popping the active child directly. Assigning an invalid
* context as the active child of a context therefore makes it impossible to
* use.
*/
extern const struct pvr_dump_ctx __pvr_dump_ctx_invalid;
struct pvr_dump_ctx {
/* This is const because only the "top" context should ever be modified. It's
* fine to extract information from the parent context, but not to modify it.
* There is *one* exception: pvr_dump_ctx_pop() must cast away the const to
* return the parent context as the new "top" context. This is considered
* sound because the parent context was not const when assigned here in
* pvr_dump_ctx_push().
*/
const struct pvr_dump_ctx *parent;
/* This is const because it's not meant to be used for access - it's just a
* way of checking if this context is the "top" context (see the comment on
* __pvr_dump_ctx_invalid for more details). Unlike parent, the const
* qualifier here should never be cast away.
*/
const struct pvr_dump_ctx *active_child;
FILE *file;
const char *name;
uint32_t allowed_child_depth;
uint32_t parent_indent;
/* User-modifiable values */
uint32_t indent;
bool ok;
};
static inline uint32_t
__pvr_dump_ctx_get_indent(const struct pvr_dump_ctx *const ctx)
{
return (ctx->parent_indent + ctx->indent) * PVR_DUMP_INDENT_SIZE;
}
struct pvr_dump_buffer_ctx {
struct pvr_dump_ctx base;
const void *initial_ptr;
uint64_t capacity;
/* User-modifiable values */
const void *ptr;
uint64_t remaining_size;
};
#define pvr_dump_printf(ctx, format, args...) \
pvr_dump_printf_cont(ctx, \
"%*s" format, \
__pvr_dump_ctx_get_indent(ctx), \
"", \
##args)
/* Same as pvr_dump_printf(), but with no indent.
* Intended for continuation lines.
*/
#define pvr_dump_printf_cont(ctx, format, args...) \
fprintf((ctx)->file, format, ##args)
#define pvr_dump_println(ctx, format, args...) \
pvr_dump_printf(ctx, format "\n", ##args)
#define pvr_dump_println_cont(ctx, format, args...) \
pvr_dump_printf_cont(ctx, format "\n", ##args)
#define pvr_dump_print_eol(ctx) fprintf((ctx)->file, "\n")
#define pvr_dump_mark_section(ctx, format, args...) \
do { \
pvr_dump_print_eol(ctx); \
pvr_dump_println(ctx, "------- " format " -------", ##args); \
} while (0)
#define pvr_dump_buffer_print_header_prefix(ctx) \
do { \
struct pvr_dump_buffer_ctx *_prefix_ctx = (ctx); \
pvr_dump_printf(&_prefix_ctx->base, \
PVR_DUMP_OFFSET_PREFIX, \
u64_dec_digits(_prefix_ctx->capacity), \
_prefix_ctx->capacity - _prefix_ctx->remaining_size); \
} while (0)
#define pvr_dump_buffer_print_header_line(ctx, format, args...) \
do { \
struct pvr_dump_buffer_ctx *_ctx = (ctx); \
pvr_dump_buffer_print_header_prefix(_ctx); \
pvr_dump_printf_cont(&_ctx->base, format "\n", ##args); \
} while (0)
#define pvr_dump_error(ctx, format, args...) \
({ \
struct pvr_dump_ctx *_ctx = (ctx); \
pvr_dump_println(_ctx, "<!ERROR! " format ">", ##args); \
_ctx->ok = false; \
false; \
})
static inline bool pvr_dump_ctx_require_top(struct pvr_dump_ctx *const ctx)
{
if (ctx->active_child != NULL)
return pvr_dump_error(ctx, "use of non-top context");
return true;
}
static inline void pvr_dump_indent(struct pvr_dump_ctx *const ctx)
{
ctx->indent++;
}
static inline void pvr_dump_dedent(struct pvr_dump_ctx *const ctx)
{
if (ctx->indent)
ctx->indent--;
}
static inline void __pvr_dump_ctx_init(struct pvr_dump_ctx *const ctx,
const struct pvr_dump_ctx *const parent,
FILE *const file,
const char *const name,
const uint32_t allowed_child_depth,
const uint32_t parent_indent)
{
ctx->parent = parent;
ctx->active_child = NULL;
ctx->file = file;
ctx->name = name;
ctx->allowed_child_depth = allowed_child_depth;
ctx->parent_indent = parent_indent;
ctx->indent = 0;
ctx->ok = true;
}
static inline void __pvr_dump_ctx_mark_popped(struct pvr_dump_ctx *const ctx)
{
ctx->active_child = &__pvr_dump_ctx_invalid;
}
static inline void pvr_dump_begin(struct pvr_dump_ctx *const root_ctx,
FILE *const file,
const char *const name,
const uint32_t max_depth)
{
__pvr_dump_ctx_init(root_ctx, NULL, file, name, max_depth, 0);
flockfile(file);
pvr_dump_println(root_ctx, "======= BEGIN %s =======", name);
}
static inline bool pvr_dump_end(struct pvr_dump_ctx *const root_ctx)
{
/* In order to end a dump, we must be in a root context (no parent) and have
* no active child context.
*/
if (!pvr_dump_ctx_require_top(root_ctx))
return false;
if (root_ctx->parent)
return pvr_dump_error(root_ctx, "ending non-root context");
pvr_dump_println(root_ctx, "======= END %s =======", root_ctx->name);
funlockfile(root_ctx->file);
__pvr_dump_ctx_mark_popped(root_ctx);
return true;
}
static inline bool pvr_dump_ctx_push(struct pvr_dump_ctx *const ctx,
struct pvr_dump_ctx *const parent_ctx)
{
if (!parent_ctx->ok)
return false;
if (!parent_ctx->allowed_child_depth)
return pvr_dump_error(parent_ctx, "context stack depth limit reached");
__pvr_dump_ctx_init(ctx,
parent_ctx,
parent_ctx->file,
parent_ctx->name,
parent_ctx->allowed_child_depth - 1,
parent_ctx->parent_indent + parent_ctx->indent);
parent_ctx->active_child = ctx;
return true;
}
static inline struct pvr_dump_ctx *
pvr_dump_ctx_pop(struct pvr_dump_ctx *const ctx)
{
struct pvr_dump_ctx *const parent = (struct pvr_dump_ctx *)ctx->parent;
if (!pvr_dump_ctx_require_top(ctx))
return NULL;
if (!parent) {
pvr_dump_error(ctx, "popped root context");
return NULL;
}
parent->active_child = NULL;
__pvr_dump_ctx_mark_popped(ctx);
return parent;
}
static inline bool
pvr_dump_buffer_ctx_push(struct pvr_dump_buffer_ctx *const ctx,
struct pvr_dump_ctx *const parent_ctx,
const void *const initial_ptr,
const uint64_t size)
{
if (!pvr_dump_ctx_push(&ctx->base, parent_ctx))
return false;
ctx->initial_ptr = initial_ptr;
ctx->capacity = size;
ctx->ptr = initial_ptr;
ctx->remaining_size = size;
return true;
}
static inline struct pvr_dump_ctx *
pvr_dump_buffer_ctx_pop(struct pvr_dump_buffer_ctx *const ctx)
{
return pvr_dump_ctx_pop(&ctx->base);
}
bool pvr_dump_buffer_hex(struct pvr_dump_buffer_ctx *ctx, uint64_t nr_words);
static inline void __pvr_dump_buffer_advance(struct pvr_dump_buffer_ctx *ctx,
const uint64_t nr_bytes)
{
ctx->ptr += nr_bytes;
ctx->remaining_size -= nr_bytes;
}
static inline bool pvr_dump_buffer_advance(struct pvr_dump_buffer_ctx *ctx,
const uint64_t nr_bytes)
{
if (!ctx->base.ok || !pvr_dump_ctx_require_top(&ctx->base))
return false;
if (nr_bytes > ctx->remaining_size)
return pvr_dump_error(&ctx->base, "advanced past end of context buffer");
__pvr_dump_buffer_advance(ctx, nr_bytes);
return true;
}
static inline bool pvr_dump_buffer_truncate(struct pvr_dump_buffer_ctx *ctx,
const uint64_t remaining_size)
{
if (!ctx->base.ok || !pvr_dump_ctx_require_top(&ctx->base))
return false;
if (remaining_size > ctx->remaining_size)
return pvr_dump_error(&ctx->base, "truncated to larger size");
ctx->remaining_size = remaining_size;
return true;
}
static inline const void *restrict
pvr_dump_buffer_peek(struct pvr_dump_buffer_ctx *const restrict ctx,
const uint64_t nr_bytes)
{
if (!ctx->base.ok || !pvr_dump_ctx_require_top(&ctx->base))
return NULL;
if (nr_bytes > ctx->remaining_size) {
pvr_dump_error(&ctx->base, "peeked past end of context buffer");
return NULL;
}
return ctx->ptr;
}
static inline const void *restrict
pvr_dump_buffer_take(struct pvr_dump_buffer_ctx *const restrict ctx,
const uint64_t nr_bytes)
{
const void *const ptr = pvr_dump_buffer_peek(ctx, nr_bytes);
if (ptr)
__pvr_dump_buffer_advance(ctx, nr_bytes);
return ptr;
}
static inline void
pvr_dump_buffer_restart(struct pvr_dump_buffer_ctx *const ctx)
{
ctx->ptr = ctx->initial_ptr;
ctx->remaining_size = ctx->capacity;
}
/*****************************************************************************
Field printers
*****************************************************************************/
#define pvr_dump_field(ctx, name, format, args...) \
pvr_dump_println(ctx, \
"%-*s : " format, \
PVR_DUMP_FIELD_COLUMN_WIDTH - \
__pvr_dump_ctx_get_indent(ctx), \
name, \
##args)
#define pvr_dump_field_computed(ctx, name, format, raw_format, args...) \
pvr_dump_field(ctx, name, format " (" raw_format ")", ##args)
#define pvr_dump_field_error(ctx, format, args...) \
({ \
struct pvr_dump_ctx *_ctx = (ctx); \
pvr_dump_field(_ctx, "<!ERROR!>", "<" format ">", ##args); \
_ctx->ok = false; \
false; \
})
/*****************************************************************************
Field printers: integers
*****************************************************************************/
static inline void pvr_dump_field_u32(struct pvr_dump_ctx *const ctx,
const char *const name,
const uint32_t value)
{
pvr_dump_field(ctx, name, "%" PRIu32, value);
}
static inline void pvr_dump_field_u32_units(struct pvr_dump_ctx *const ctx,
const char *const name,
const uint32_t value,
const char *const units)
{
pvr_dump_field(ctx, name, "%" PRIu32 " %s", value, units);
}
static inline void pvr_dump_field_u32_offset(struct pvr_dump_ctx *const ctx,
const char *const name,
const uint32_t value,
const uint32_t offset)
{
pvr_dump_field_computed(ctx,
name,
"%" PRIu32,
"%" PRIu32 " + %" PRIu32,
value + offset,
value,
offset);
}
static inline void pvr_dump_field_u32_scaled(struct pvr_dump_ctx *const ctx,
const char *const name,
const uint32_t value,
const uint32_t scale)
{
pvr_dump_field_computed(ctx,
name,
"%" PRIu32,
"%" PRIu32 " x %" PRIu32,
value * scale,
value,
scale);
}
static inline void
pvr_dump_field_u32_scaled_units(struct pvr_dump_ctx *const ctx,
const char *const name,
const uint32_t value,
const uint32_t scale,
const char *const units)
{
pvr_dump_field_computed(ctx,
name,
"%" PRIu32 " %s",
"%" PRIu32 " x %" PRIu32 " %s",
value * scale,
units,
value,
scale,
units);
}
static inline void pvr_dump_field_u32_zero(struct pvr_dump_ctx *const ctx,
const char *const name,
const uint32_t value,
const uint32_t zero_value)
{
if (value)
pvr_dump_field_u32(ctx, name, value);
else
pvr_dump_field_computed(ctx, name, "%" PRIu32, "0", zero_value);
}
static inline void pvr_dump_field_x32(struct pvr_dump_ctx *const ctx,
const char *const name,
const uint32_t value,
const uint32_t chars)
{
pvr_dump_field(ctx,
name,
"0x%0*" PRIx32,
chars,
value & BITFIELD_MASK(chars * 4));
}
/*****************************************************************************
Field printers: device address
*****************************************************************************/
static inline void pvr_dump_field_addr_non_null(struct pvr_dump_ctx *const ctx,
const char *const name,
const pvr_dev_addr_t value)
{
pvr_dump_field(ctx, name, PVR_DEV_ADDR_FMT, value.addr);
}
static inline void pvr_dump_field_addr(struct pvr_dump_ctx *const ctx,
const char *const name,
const pvr_dev_addr_t value)
{
if (value.addr)
pvr_dump_field_addr_non_null(ctx, name, value);
else
pvr_dump_field(ctx, name, "<null>");
}
static inline void pvr_dump_field_addr_split(struct pvr_dump_ctx *const ctx,
const char *const name,
const pvr_dev_addr_t msb,
const pvr_dev_addr_t lsb)
{
pvr_dump_field_addr(ctx, name, PVR_DEV_ADDR(msb.addr | lsb.addr));
pvr_dump_indent(ctx);
pvr_dump_field_addr_non_null(ctx, "msb", msb);
pvr_dump_field_addr_non_null(ctx, "lsb", lsb);
pvr_dump_dedent(ctx);
}
/*****************************************************************************
Field printers: enums
*****************************************************************************/
#define pvr_dump_field_enum(ctx, name, value, to_str) \
do { \
__typeof__(value) _value = (value); \
const char *_str = to_str(_value); \
if (!_str) \
_str = "<unknown>"; \
pvr_dump_field_computed(ctx, name, "%s", "%u", _str, _value); \
} while (0)
static inline const char *__bool_to_str(const bool b)
{
return b ? "yes" : "no";
}
/* A bool is just an enum with two values. */
static inline void pvr_dump_field_bool(struct pvr_dump_ctx *const ctx,
const char *const name,
const bool value)
{
pvr_dump_field_enum(ctx, name, value, __bool_to_str);
}
/*****************************************************************************
Field printers: not present
*****************************************************************************/
static inline void pvr_dump_field_no_fields(struct pvr_dump_ctx *const ctx)
{
pvr_dump_println(ctx, "<no fields>");
}
static inline void pvr_dump_field_not_present(struct pvr_dump_ctx *const ctx,
const char *const name)
{
pvr_dump_field(ctx, name, "<not present>");
}
/*****************************************************************************
Field printers: helpers for members
*****************************************************************************/
/* clang-format off */
#define pvr_dump_field_member_u32(ctx, compound, member) \
pvr_dump_field_u32(ctx, #member, (compound)->member)
#define pvr_dump_field_member_u32_units(ctx, compound, member, units) \
pvr_dump_field_u32_units(ctx, #member, (compound)->member, units)
#define pvr_dump_field_member_u32_offset(ctx, compound, member, offset) \
pvr_dump_field_u32_offset(ctx, #member, (compound)->member, offset)
#define pvr_dump_field_member_u32_scaled(ctx, compound, member, scale) \
pvr_dump_field_u32_scaled(ctx, #member, (compound)->member, scale)
#define pvr_dump_field_member_u32_scaled_units(ctx, compound, member, scale, units) \
pvr_dump_field_u32_scaled_units(ctx, #member, (compound)->member, scale, units)
#define pvr_dump_field_member_u32_zero(ctx, compound, member, zero_value) \
pvr_dump_field_u32_zero(ctx, #member, (compound)->member, zero_value)
#define pvr_dump_field_member_x32(ctx, compound, member, chars) \
pvr_dump_field_x32(ctx, #member, (compound)->member, chars)
#define pvr_dump_field_member_f32(ctx, compound, member) \
pvr_dump_field_f32(ctx, #member, (compound)->member)
#define pvr_dump_field_member_addr(ctx, compound, member) \
pvr_dump_field_addr(ctx, #member, (compound)->member)
#define pvr_dump_field_member_enum(ctx, compound, member, to_str) \
pvr_dump_field_enum(ctx, #member, (compound)->member, to_str)
#define pvr_dump_field_member_bool(ctx, compound, member) \
pvr_dump_field_bool(ctx, #member, (compound)->member)
/* clang-format on */
#define pvr_dump_field_member_not_present(ctx, compound, member) \
do { \
(void)&(compound)->member; \
pvr_dump_field_not_present(ctx, #member); \
} while (0)
#endif /* PVR_DUMP_H */

View file

@ -24,9 +24,18 @@
#ifndef PVR_UTIL_H
#define PVR_UTIL_H
#include <assert.h>
#include <stdint.h>
#include "util/bitscan.h"
#include "util/macros.h"
static inline bool ptr_is_aligned(const void *const ptr,
const uint32_t alignment)
{
assert(util_is_power_of_two_nonzero(alignment));
return ((uintptr_t)(ptr) & (alignment - 1)) == 0;
}
/*****************************************************************************
Math functions

View file

@ -24,6 +24,7 @@
#ifndef PVR_TYPES_H
#define PVR_TYPES_H
#include <inttypes.h>
#include <stdint.h>
/*****************************************************************************
@ -38,4 +39,7 @@ typedef struct pvr_dev_addr {
#define PVR_DEV_ADDR_OFFSET(base, offset) PVR_DEV_ADDR((base).addr + (offset))
#define PVR_DEV_ADDR_INVALID PVR_DEV_ADDR(0)
/* All currently supported devices use a 40-bit virtual address space. */
#define PVR_DEV_ADDR_FMT "0x%010" PRIx64
#endif /* PVR_TYPES_H */

View file

@ -42,6 +42,8 @@ pvr_files = files(
'pvr_csb.c',
'pvr_descriptor_set.c',
'pvr_device.c',
'pvr_dump_bo.c',
'pvr_dump_csb.c',
'pvr_formats.c',
'pvr_hardcode.c',
'pvr_hw_pass.c',

View file

@ -27,12 +27,62 @@
#include <vulkan/vulkan.h>
#include "pvr_bo.h"
#include "pvr_dump.h"
#include "pvr_private.h"
#include "pvr_types.h"
#include "pvr_winsys.h"
#include "vk_alloc.h"
#include "vk_log.h"
static void pvr_bo_dump_line(struct pvr_dump_ctx *const ctx,
const struct pvr_bo *bo,
const uint32_t index,
const uint32_t nr_bos_log10)
{
static const char *const pretty_sizes[64 + 1] = {
"", "1 B", "2 B", "4 B", "8 B", "16 B",
"32 B", "64 B", "128 B", "256 B", "512 B", "1 KiB",
"2 KiB", "4 KiB", "8 KiB", "16 KiB", "32 KiB", "64 KiB",
"128 KiB", "256 KiB", "512 KiB", "1 MiB", "2 MiB", "4 MiB",
"8 MiB", "16 MiB", "32 MiB", "64 MiB", "128 MiB", "256 MiB",
"512 MiB", "1 GiB", "2 GiB", "4 GiB", "8 GiB", "16 GiB",
"32 GiB", "64 GiB", "128 GiB", "256 GiB", "512 GiB", "1 TiB",
"2 TiB", "4 TiB", "8 TiB", "16 TiB", "32 TiB", "64 TiB",
"128 TiB", "256 TiB", "512 TiB", "1 PiB", "2 PiB", "4 PiB",
"8 PiB", "16 PiB", "32 PiB", "64 PiB", "128 PiB", "256 PiB",
"512 PiB", "1 EiB", "2 EiB", "4 EiB", "8 EiB",
};
const uint64_t size = bo->vma->size;
const uint32_t size_log2 =
util_is_power_of_two_or_zero64(size) ? util_last_bit(size) : 0;
pvr_dump_println(ctx,
"[%0*" PRIu32 "] " PVR_DEV_ADDR_FMT " -> %*p "
"(%s%s0x%" PRIx64 " bytes)",
nr_bos_log10,
index,
bo->vma->dev_addr.addr,
(int)sizeof(void *) * 2 + 2, /* nr hex digits + 0x prefix */
bo->bo->map,
pretty_sizes[size_log2],
size_log2 ? ", " : "",
size);
}
void pvr_bo_list_dump(struct pvr_dump_ctx *const ctx,
const struct list_head *const bo_list,
const uint32_t nr_bos)
{
const uint32_t real_nr_bos = nr_bos ? nr_bos : list_length(bo_list);
const uint32_t nr_bos_log10 = u32_dec_digits(real_nr_bos);
uint32_t bo_idx = 0;
list_for_each_entry (struct pvr_bo, bo, bo_list, link) {
pvr_bo_dump_line(ctx, bo, bo_idx++, nr_bos_log10);
}
}
static uint32_t pvr_bo_alloc_to_winsys_flags(uint64_t flags)
{
uint32_t ws_flags = 0;
@ -103,6 +153,9 @@ VkResult pvr_bo_alloc(struct pvr_device *device,
result = VK_ERROR_MEMORY_MAP_FAILED;
goto err_buffer_destroy;
}
if (flags & PVR_BO_ALLOC_FLAG_ZERO_ON_ALLOC)
VG(VALGRIND_MAKE_MEM_DEFINED(map, pvr_bo->bo->size));
}
pvr_bo->vma = device->ws->ops->heap_alloc(heap, size, alignment);

View file

@ -32,6 +32,7 @@
#include "util/macros.h"
struct pvr_device;
struct pvr_dump_ctx;
struct pvr_winsys_bo;
struct pvr_winsys_vma;
struct pvr_winsys_heap;
@ -98,4 +99,8 @@ static ALWAYS_INLINE void *pvr_bo_cpu_map_unchanged(struct pvr_device *device,
}
#endif /* defined(HAVE_VALGRIND) */
void pvr_bo_list_dump(struct pvr_dump_ctx *ctx,
const struct list_head *bo_list,
uint32_t bo_size);
#endif /* PVR_BO_H */

View file

@ -36,6 +36,7 @@
#include "hwdef/rogue_hw_utils.h"
#include "pvr_bo.h"
#include "pvr_csb.h"
#include "pvr_debug.h"
#include "pvr_device_info.h"
#include "pvr_private.h"
#include "util/list.h"
@ -61,11 +62,6 @@
* type control streams.
*/
/**
* \brief Size of the individual csb buffer object.
*/
#define PVR_CMD_BUFFER_CSB_BO_SIZE 4096
/**
* \brief Initializes the csb object.
*
@ -137,9 +133,17 @@ static bool pvr_csb_buffer_extend(struct pvr_csb *csb)
sizeof(uint32_t);
const uint32_t cache_line_size =
rogue_get_slc_cache_line_size(&csb->device->pdevice->dev_info);
uint64_t alloc_flags = PVR_BO_ALLOC_FLAG_CPU_MAPPED;
struct pvr_bo *pvr_bo;
VkResult result;
/* If we're dumping the control stream, ensure the buffer is zeroed to make
* the contents deterministic. This keeps valgrind happy and makes for
* cleaner dump output.
*/
if (PVR_IS_DEBUG_SET(DUMP_CONTROL_STREAM))
alloc_flags |= PVR_BO_ALLOC_FLAG_ZERO_ON_ALLOC;
/* Make sure extra space allocated for stream links is sufficient for both
* stream types.
*/
@ -152,7 +156,7 @@ static bool pvr_csb_buffer_extend(struct pvr_csb *csb)
csb->device->heaps.general_heap,
PVR_CMD_BUFFER_CSB_BO_SIZE,
cache_line_size,
PVR_BO_ALLOC_FLAG_CPU_MAPPED,
alloc_flags,
&pvr_bo);
if (result != VK_SUCCESS) {
vk_error(csb->device, result);

View file

@ -50,6 +50,11 @@
#include "csbgen/rogue_hwdefs.h"
/**
* \brief Size of the individual csb buffer object.
*/
#define PVR_CMD_BUFFER_CSB_BO_SIZE 4096
struct pvr_device;
enum pvr_cmd_stream_type {
@ -132,6 +137,10 @@ void pvr_csb_emit_link(struct pvr_csb *csb, pvr_dev_addr_t addr, bool ret);
VkResult pvr_csb_emit_return(struct pvr_csb *csb);
VkResult pvr_csb_emit_terminate(struct pvr_csb *csb);
void pvr_csb_dump(const struct pvr_csb *csb,
uint32_t frame_num,
uint32_t job_num);
#define PVRX(x) ROGUE_##x
#define pvr_cmd_length(x) PVRX(x##_length)
#define pvr_cmd_header(x) PVRX(x##_header)

View file

@ -31,6 +31,21 @@
#include "rogue/rogue.h"
#include "util/macros.h"
static const char *
pvr_cmd_stream_type_to_str(const enum pvr_cmd_stream_type stream_type)
{
switch (stream_type) {
case PVR_CMD_STREAM_TYPE_INVALID:
return "INVALID";
case PVR_CMD_STREAM_TYPE_GRAPHICS:
return "GRAPHICS";
case PVR_CMD_STREAM_TYPE_COMPUTE:
return "COMPUTE";
default:
return NULL;
}
}
/******************************************************************************
CR
******************************************************************************/

View file

@ -0,0 +1,82 @@
/*
* Copyright © 2022 Imagination Technologies Ltd.
*
* 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 <stdbool.h>
#include <stdint.h>
#include "pvr_bo.h"
#include "pvr_dump_bo.h"
#include "pvr_dump.h"
#include "pvr_winsys.h"
#include "util/u_math.h"
struct pvr_device;
bool pvr_dump_bo_ctx_push(struct pvr_dump_bo_ctx *const ctx,
struct pvr_dump_ctx *const parent_ctx,
struct pvr_device *const device,
struct pvr_bo *const bo)
{
bool did_map_bo = false;
if (!bo->bo->map) {
if (!pvr_bo_cpu_map_unchanged(device, bo))
goto err_out;
did_map_bo = true;
}
if (bo->bo->size > UINT32_MAX) {
mesa_logw_once("Attempted to dump a BO larger than 4GiB; time to rework"
"pvr_dump_buffer_ctx to use 64-bit sizes.");
goto err_unmap_bo;
}
if (!pvr_dump_buffer_ctx_push(&ctx->base,
parent_ctx,
bo->bo->map,
bo->bo->size)) {
goto err_unmap_bo;
}
ctx->device = device;
ctx->bo = bo;
ctx->bo_mapped_in_ctx = did_map_bo;
return true;
err_unmap_bo:
if (did_map_bo)
pvr_bo_cpu_unmap(device, bo);
err_out:
return false;
}
bool pvr_dump_bo_ctx_pop(struct pvr_dump_bo_ctx *const ctx)
{
if (ctx->bo_mapped_in_ctx)
pvr_bo_cpu_unmap(ctx->device, ctx->bo);
return pvr_dump_buffer_ctx_pop(&ctx->base);
}

View file

@ -0,0 +1,50 @@
/*
* Copyright © 2022 Imagination Technologies Ltd.
*
* 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 PVR_DUMP_BO_H
#define PVR_DUMP_BO_H
#include <stdbool.h>
#include "pvr_dump.h"
struct pvr_bo;
struct pvr_device;
struct pvr_dump_bo_ctx {
struct pvr_dump_buffer_ctx base;
struct pvr_device *device;
struct pvr_bo *bo;
bool bo_mapped_in_ctx;
/* No user-modifiable values */
};
bool pvr_dump_bo_ctx_push(struct pvr_dump_bo_ctx *ctx,
struct pvr_dump_ctx *parent_ctx,
struct pvr_device *device,
struct pvr_bo *bo);
bool pvr_dump_bo_ctx_pop(struct pvr_dump_bo_ctx *ctx);
#endif /* PVR_DUMP_BO_H */

File diff suppressed because it is too large Load diff

View file

@ -27,6 +27,7 @@
#include <vulkan/vulkan.h>
#include "pvr_csb.h"
#include "pvr_debug.h"
#include "pvr_job_common.h"
#include "pvr_job_context.h"
#include "pvr_job_compute.h"
@ -213,6 +214,12 @@ VkResult pvr_compute_job_submit(struct pvr_compute_ctx *ctx,
stage_flags,
&submit_info);
if (PVR_IS_DEBUG_SET(DUMP_CONTROL_STREAM)) {
pvr_csb_dump(&sub_cmd->control_stream,
submit_info.frame_num,
submit_info.job_num);
}
return device->ws->ops->compute_submit(ctx->ws_ctx,
&submit_info,
&device->pdevice->dev_info,

View file

@ -30,6 +30,7 @@
#include "hwdef/rogue_hw_utils.h"
#include "pvr_bo.h"
#include "pvr_csb.h"
#include "pvr_debug.h"
#include "pvr_csb_enum_helpers.h"
#include "pvr_debug.h"
#include "pvr_job_common.h"
@ -1677,6 +1678,20 @@ VkResult pvr_render_job_submit(struct pvr_render_ctx *ctx,
stage_flags,
&submit_info);
if (PVR_IS_DEBUG_SET(DUMP_CONTROL_STREAM)) {
/* FIXME: This isn't an ideal method of accessing the information we
* need, but it's considered good enough for a debug code path. It can be
* streamlined and made more correct if/when pvr_render_job becomes a
* subclass of pvr_sub_cmd.
*/
const struct pvr_sub_cmd *sub_cmd =
container_of(job, const struct pvr_sub_cmd, gfx.job);
pvr_csb_dump(&sub_cmd->gfx.control_stream,
submit_info.frame_num,
submit_info.job_num);
}
result = device->ws->ops->render_submit(ctx->ws_ctx,
&submit_info,
&device->pdevice->dev_info,