[RE] Log render-target state, resolves and PS constants on every UI draw
The UI draw log recorded blend state, textures and geometry, which is
enough to say WHICH sprite a draw is and how it composites, and not
enough to answer whether a screen has more than one PASS. A second draw
into an off-screen target, or a resolve of the EDRAM into a texture that
a later full-screen quad samples, both look like just another quad in
the old format.
Three additions, all read straight out of the register file:
mode= RB_MODECONTROL.edram_mode. kCopy (6) is how this GPU issues
a RESOLVE, and it arrives through the same DRAW_INDX packet
as a sprite -- so without this field a resolve was
indistinguishable from a draw.
rt0=/pitch RB_COLOR_INFO's EDRAM tile and format, RB_SURFACE_INFO's
pitch and MSAA. A second target shows up as a different
tile; a half-resolution post-process shows up as a pitch
that is not the screen's.
RESOLVE on a kCopy draw, RB_COPY_CONTROL and RB_COPY_DEST_BASE.
That destination reappearing as a later tex[base=...] is
what 'resolve-and-resample' means stated in addresses,
rather than inferred from the picture.
ps_c[...] the pixel shader's float constants, taken off its OWN
float_bitmap -- the same one the backend uploads from -- so
this is the shader's declared dependency set rather than a
fixed window that could miss the one that matters. Pixel
constants live at SHADER_CONSTANT_256_X and the c# printed
is the index the shader's disassembly uses.
Without the constants, an alpha ramp driven by a shader constant and one
driven by per-vertex colour are the same picture.
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
#include "xenia/base/cvar.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/profiling.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/gpu/gpu_flags.h"
|
||||
#include "xenia/gpu/graphics_system.h"
|
||||
#include "xenia/gpu/packet_disassembler.h"
|
||||
@@ -323,6 +324,49 @@ void CommandProcessor::CaptureUiDrawForRE(
|
||||
uint32_t(bc.alpha_comb_fcn), uint32_t(bc.alpha_destblend), cc.value,
|
||||
mask);
|
||||
}
|
||||
// ── RE: the RENDER TARGET this draw goes to, and whether it is a RESOLVE ──
|
||||
// The question this answers: "is there a post-process pass at all?" is a
|
||||
// question about render targets and passes, and the log above cannot see one.
|
||||
// Two screens' worth of splash draws were read off a stream that recorded only
|
||||
// blend state and geometry, so a second pass into an off-screen target — or a
|
||||
// resolve of the EDRAM into a texture that a later full-screen quad samples —
|
||||
// would have been invisible: it looks like just another quad.
|
||||
//
|
||||
// mode= RB_MODECONTROL.edram_mode. 0 = colour+depth (a normal draw),
|
||||
// 4 = kCopy, which on this GPU is how a RESOLVE is issued — it
|
||||
// arrives through the same DRAW_INDX packet as everything else,
|
||||
// so without this field a resolve is indistinguishable from a
|
||||
// sprite.
|
||||
// rt0= RB_COLOR_INFO: the EDRAM tile the draw writes to, its format,
|
||||
// and the exponent bias. A second pass into a DIFFERENT edram
|
||||
// base is the signature of an off-screen target.
|
||||
// pitch/msaa RB_SURFACE_INFO. A post-process at reduced resolution shows up
|
||||
// here as a pitch that is not the screen's.
|
||||
// RESOLVE … on a kCopy draw, where the EDRAM is being copied TO. That
|
||||
// destination address reappearing as a `tex[base=…]` on a later
|
||||
// draw is what "resolve-and-resample" means, stated in addresses
|
||||
// rather than inferred from the picture.
|
||||
{
|
||||
auto mc = register_file_->Get<reg::RB_MODECONTROL>();
|
||||
auto si = register_file_->Get<reg::RB_SURFACE_INFO>();
|
||||
auto ci = register_file_->Get<reg::RB_COLOR_INFO>();
|
||||
ui_out << fmt::format(
|
||||
" mode={} pitch={} msaa={} rt0=[tile={} fmt={} exp={}]",
|
||||
uint32_t(mc.edram_mode), uint32_t(si.surface_pitch),
|
||||
uint32_t(si.msaa_samples),
|
||||
uint32_t(ci.color_base) | (uint32_t(ci.color_base_bit_11) << 11),
|
||||
uint32_t(ci.color_format), int32_t(ci.color_exp_bias));
|
||||
if (mc.edram_mode == xenos::EdramMode::kCopy) {
|
||||
auto cpc = register_file_->Get<reg::RB_COPY_CONTROL>();
|
||||
ui_out << fmt::format(
|
||||
" RESOLVE copy=0x{:08X}[src={} samp={} cmd={} cclr={} dclr={}]"
|
||||
" dest=0x{:08X}",
|
||||
cpc.value, uint32_t(cpc.copy_src_select),
|
||||
uint32_t(cpc.copy_sample_select), uint32_t(cpc.copy_command),
|
||||
uint32_t(cpc.color_clear_enable), uint32_t(cpc.depth_clear_enable),
|
||||
register_file_->values[XE_GPU_REG_RB_COPY_DEST_BASE]);
|
||||
}
|
||||
}
|
||||
if (ps && ps->is_ucode_analyzed()) {
|
||||
for (const auto& tb : ps->texture_bindings()) {
|
||||
xenos::xe_gpu_texture_fetch_t tf =
|
||||
@@ -335,6 +379,44 @@ void CommandProcessor::CaptureUiDrawForRE(
|
||||
uint32_t(tf.format));
|
||||
}
|
||||
}
|
||||
// ── RE: the PIXEL SHADER's float constants, as the shader itself indexes ──
|
||||
// "Where do a pass's parameters come from?" has four candidate answers —
|
||||
// immediates in the command stream, the constant banks, a table in a pak, or
|
||||
// a ramp computed in guest code. This log could not previously distinguish
|
||||
// any of them, because it recorded no constants at all: an alpha ramp driven
|
||||
// by a PS constant and one driven by per-vertex colour look identical once
|
||||
// you are reading pixels.
|
||||
//
|
||||
// Only the constants the shader ACTUALLY READS are printed, off its own
|
||||
// `float_bitmap` — the same bitmap the backend uses to upload them — so this
|
||||
// is the shader's declared dependency set rather than a fixed window that
|
||||
// might miss the one that matters or bury it in 200 unused vectors. Pixel
|
||||
// constants live in the second half of the file (SHADER_CONSTANT_256_X), and
|
||||
// the c# printed is the index the shader's own disassembly uses.
|
||||
if (ps && ps->is_ucode_analyzed()) {
|
||||
const auto& crm = ps->constant_register_map();
|
||||
ui_out << fmt::format("\n ps_c[n={}{}]:", crm.float_count,
|
||||
crm.float_dynamic_addressing ? " DYNAMIC" : "");
|
||||
uint32_t printed = 0;
|
||||
for (uint32_t w = 0; w < 4 && printed < 24; ++w) {
|
||||
uint64_t bits = crm.float_bitmap[w];
|
||||
uint32_t b;
|
||||
while (printed < 24 && xe::bit_scan_forward(bits, &b)) {
|
||||
bits = xe::clear_lowest_bit(bits);
|
||||
uint32_t idx = (w << 6) + b;
|
||||
uint32_t r = XE_GPU_REG_SHADER_CONSTANT_256_X + (idx << 2);
|
||||
ui_out << fmt::format(" c{}=({:.5f},{:.5f},{:.5f},{:.5f})", idx,
|
||||
register_file_->Get<float>(r),
|
||||
register_file_->Get<float>(r + 1),
|
||||
register_file_->Get<float>(r + 2),
|
||||
register_file_->Get<float>(r + 3));
|
||||
++printed;
|
||||
}
|
||||
}
|
||||
if (crm.float_count > printed) {
|
||||
ui_out << fmt::format(" …+{} more", crm.float_count - printed);
|
||||
}
|
||||
}
|
||||
// The quad's geometry is the only thing that says WHICH element a draw is:
|
||||
// these sprites all share one shader and sample big texture pages, so the
|
||||
// vertex data is the identity. Dump attribute 0 of binding 0 for the first
|
||||
|
||||
Reference in New Issue
Block a user