Merge pull request #997 from Lectem/cmdlist_full_debug
citra-qt: Improve pica command list widget (add mask, fix some issues)
This commit is contained in:
commit
f5144e6c10
|
@ -175,29 +175,29 @@ int GPUCommandListModel::rowCount(const QModelIndex& parent) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
int GPUCommandListModel::columnCount(const QModelIndex& parent) const {
|
int GPUCommandListModel::columnCount(const QModelIndex& parent) const {
|
||||||
return 3;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const {
|
QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const {
|
||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
|
||||||
const auto& writes = pica_trace.writes;
|
const auto& write = pica_trace.writes[index.row()];
|
||||||
const Pica::CommandProcessor::CommandHeader cmd{writes[index.row()].Id()};
|
|
||||||
const u32 val{writes[index.row()].Value()};
|
|
||||||
|
|
||||||
if (role == Qt::DisplayRole) {
|
if (role == Qt::DisplayRole) {
|
||||||
QString content;
|
QString content;
|
||||||
switch ( index.column() ) {
|
switch ( index.column() ) {
|
||||||
case 0:
|
case 0:
|
||||||
return QString::fromLatin1(Pica::Regs::GetCommandName(cmd.cmd_id).c_str());
|
return QString::fromLatin1(Pica::Regs::GetCommandName(write.cmd_id).c_str());
|
||||||
case 1:
|
case 1:
|
||||||
return QString("%1").arg(cmd.cmd_id, 3, 16, QLatin1Char('0'));
|
return QString("%1").arg(write.cmd_id, 3, 16, QLatin1Char('0'));
|
||||||
case 2:
|
case 2:
|
||||||
return QString("%1").arg(val, 8, 16, QLatin1Char('0'));
|
return QString("%1").arg(write.mask, 4, 2, QLatin1Char('0'));
|
||||||
|
case 3:
|
||||||
|
return QString("%1").arg(write.value, 8, 16, QLatin1Char('0'));
|
||||||
}
|
}
|
||||||
} else if (role == CommandIdRole) {
|
} else if (role == CommandIdRole) {
|
||||||
return QVariant::fromValue<int>(cmd.cmd_id.Value());
|
return QVariant::fromValue<int>(write.cmd_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
@ -213,6 +213,8 @@ QVariant GPUCommandListModel::headerData(int section, Qt::Orientation orientatio
|
||||||
case 1:
|
case 1:
|
||||||
return tr("Register");
|
return tr("Register");
|
||||||
case 2:
|
case 2:
|
||||||
|
return tr("Mask");
|
||||||
|
case 3:
|
||||||
return tr("New Value");
|
return tr("New Value");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,7 +262,7 @@ void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
|
void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
|
||||||
QWidget* new_info_widget;
|
QWidget* new_info_widget = nullptr;
|
||||||
|
|
||||||
const unsigned int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt();
|
const unsigned int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toUInt();
|
||||||
if (COMMAND_IN_RANGE(command_id, texture0) ||
|
if (COMMAND_IN_RANGE(command_id, texture0) ||
|
||||||
|
@ -281,15 +283,16 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
|
||||||
auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format);
|
auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format);
|
||||||
u8* src = Memory::GetPhysicalPointer(config.GetPhysicalAddress());
|
u8* src = Memory::GetPhysicalPointer(config.GetPhysicalAddress());
|
||||||
new_info_widget = new TextureInfoWidget(src, info);
|
new_info_widget = new TextureInfoWidget(src, info);
|
||||||
} else {
|
|
||||||
new_info_widget = new QWidget;
|
|
||||||
}
|
}
|
||||||
|
if (command_info_widget) {
|
||||||
widget()->layout()->removeWidget(command_info_widget);
|
|
||||||
delete command_info_widget;
|
delete command_info_widget;
|
||||||
|
command_info_widget = nullptr;
|
||||||
|
}
|
||||||
|
if (new_info_widget) {
|
||||||
widget()->layout()->addWidget(new_info_widget);
|
widget()->layout()->addWidget(new_info_widget);
|
||||||
command_info_widget = new_info_widget;
|
command_info_widget = new_info_widget;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#undef COMMAND_IN_RANGE
|
#undef COMMAND_IN_RANGE
|
||||||
|
|
||||||
GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pica Command List"), parent) {
|
GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pica Command List"), parent) {
|
||||||
|
@ -300,7 +303,9 @@ GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pi
|
||||||
|
|
||||||
list_widget = new QTreeView;
|
list_widget = new QTreeView;
|
||||||
list_widget->setModel(model);
|
list_widget->setModel(model);
|
||||||
list_widget->setFont(QFont("monospace"));
|
QFont font("monospace");
|
||||||
|
font.setStyleHint(QFont::Monospace); // Automatic fallback to a monospace font on on platforms without a font called "monospace"
|
||||||
|
list_widget->setFont(font);
|
||||||
list_widget->setRootIsDecorated(false);
|
list_widget->setRootIsDecorated(false);
|
||||||
list_widget->setUniformRowHeights(true);
|
list_widget->setUniformRowHeights(true);
|
||||||
|
|
||||||
|
@ -324,7 +329,7 @@ GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pi
|
||||||
|
|
||||||
connect(copy_all, SIGNAL(clicked()), this, SLOT(CopyAllToClipboard()));
|
connect(copy_all, SIGNAL(clicked()), this, SLOT(CopyAllToClipboard()));
|
||||||
|
|
||||||
command_info_widget = new QWidget;
|
command_info_widget = nullptr;
|
||||||
|
|
||||||
QVBoxLayout* main_layout = new QVBoxLayout;
|
QVBoxLayout* main_layout = new QVBoxLayout;
|
||||||
main_layout->addWidget(list_widget);
|
main_layout->addWidget(list_widget);
|
||||||
|
@ -334,7 +339,6 @@ GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pi
|
||||||
sub_layout->addWidget(copy_all);
|
sub_layout->addWidget(copy_all);
|
||||||
main_layout->addLayout(sub_layout);
|
main_layout->addLayout(sub_layout);
|
||||||
}
|
}
|
||||||
main_layout->addWidget(command_info_widget);
|
|
||||||
main_widget->setLayout(main_layout);
|
main_widget->setLayout(main_layout);
|
||||||
|
|
||||||
setWidget(main_widget);
|
setWidget(main_widget);
|
||||||
|
|
|
@ -35,7 +35,15 @@ static u32 default_attr_write_buffer[3];
|
||||||
|
|
||||||
Common::Profiling::TimingCategory category_drawing("Drawing");
|
Common::Profiling::TimingCategory category_drawing("Drawing");
|
||||||
|
|
||||||
static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
|
// Expand a 4-bit mask to 4-byte mask, e.g. 0b0101 -> 0x00FF00FF
|
||||||
|
static const u32 expand_bits_to_bytes[] = {
|
||||||
|
0x00000000, 0x000000ff, 0x0000ff00, 0x0000ffff,
|
||||||
|
0x00ff0000, 0x00ff00ff, 0x00ffff00, 0x00ffffff,
|
||||||
|
0xff000000, 0xff0000ff, 0xff00ff00, 0xff00ffff,
|
||||||
|
0xffff0000, 0xffff00ff, 0xffffff00, 0xffffffff
|
||||||
|
};
|
||||||
|
|
||||||
|
static void WritePicaReg(u32 id, u32 value, u32 mask) {
|
||||||
auto& regs = g_state.regs;
|
auto& regs = g_state.regs;
|
||||||
|
|
||||||
if (id >= regs.NumIds())
|
if (id >= regs.NumIds())
|
||||||
|
@ -47,13 +55,16 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
|
||||||
|
|
||||||
// TODO: Figure out how register masking acts on e.g. vs.uniform_setup.set_value
|
// TODO: Figure out how register masking acts on e.g. vs.uniform_setup.set_value
|
||||||
u32 old_value = regs[id];
|
u32 old_value = regs[id];
|
||||||
regs[id] = (old_value & ~mask) | (value & mask);
|
|
||||||
|
const u32 write_mask = expand_bits_to_bytes[mask];
|
||||||
|
|
||||||
|
regs[id] = (old_value & ~write_mask) | (value & write_mask);
|
||||||
|
|
||||||
|
DebugUtils::OnPicaRegWrite({ (u16)id, (u16)mask, regs[id] });
|
||||||
|
|
||||||
if (g_debug_context)
|
if (g_debug_context)
|
||||||
g_debug_context->OnEvent(DebugContext::Event::PicaCommandLoaded, reinterpret_cast<void*>(&id));
|
g_debug_context->OnEvent(DebugContext::Event::PicaCommandLoaded, reinterpret_cast<void*>(&id));
|
||||||
|
|
||||||
DebugUtils::OnPicaRegWrite(id, regs[id]);
|
|
||||||
|
|
||||||
switch(id) {
|
switch(id) {
|
||||||
// Trigger IRQ
|
// Trigger IRQ
|
||||||
case PICA_REG_INDEX(trigger_irq):
|
case PICA_REG_INDEX(trigger_irq):
|
||||||
|
@ -469,13 +480,6 @@ void ProcessCommandList(const u32* list, u32 size) {
|
||||||
g_state.cmd_list.length = size / sizeof(u32);
|
g_state.cmd_list.length = size / sizeof(u32);
|
||||||
|
|
||||||
while (g_state.cmd_list.current_ptr < g_state.cmd_list.head_ptr + g_state.cmd_list.length) {
|
while (g_state.cmd_list.current_ptr < g_state.cmd_list.head_ptr + g_state.cmd_list.length) {
|
||||||
// Expand a 4-bit mask to 4-byte mask, e.g. 0b0101 -> 0x00FF00FF
|
|
||||||
static const u32 expand_bits_to_bytes[] = {
|
|
||||||
0x00000000, 0x000000ff, 0x0000ff00, 0x0000ffff,
|
|
||||||
0x00ff0000, 0x00ff00ff, 0x00ffff00, 0x00ffffff,
|
|
||||||
0xff000000, 0xff0000ff, 0xff00ff00, 0xff00ffff,
|
|
||||||
0xffff0000, 0xffff00ff, 0xffffff00, 0xffffffff
|
|
||||||
};
|
|
||||||
|
|
||||||
// Align read pointer to 8 bytes
|
// Align read pointer to 8 bytes
|
||||||
if ((g_state.cmd_list.head_ptr - g_state.cmd_list.current_ptr) % 2 != 0)
|
if ((g_state.cmd_list.head_ptr - g_state.cmd_list.current_ptr) % 2 != 0)
|
||||||
|
@ -483,14 +487,13 @@ void ProcessCommandList(const u32* list, u32 size) {
|
||||||
|
|
||||||
u32 value = *g_state.cmd_list.current_ptr++;
|
u32 value = *g_state.cmd_list.current_ptr++;
|
||||||
const CommandHeader header = { *g_state.cmd_list.current_ptr++ };
|
const CommandHeader header = { *g_state.cmd_list.current_ptr++ };
|
||||||
const u32 write_mask = expand_bits_to_bytes[header.parameter_mask];
|
|
||||||
u32 cmd = header.cmd_id;
|
u32 cmd = header.cmd_id;
|
||||||
|
|
||||||
WritePicaReg(cmd, value, write_mask);
|
WritePicaReg(cmd, value, header.parameter_mask);
|
||||||
|
|
||||||
for (unsigned i = 0; i < header.extra_data_length; ++i) {
|
for (unsigned i = 0; i < header.extra_data_length; ++i) {
|
||||||
u32 cmd = header.cmd_id + (header.group_commands ? i + 1 : 0);
|
u32 cmd = header.cmd_id + (header.group_commands ? i + 1 : 0);
|
||||||
WritePicaReg(cmd, *g_state.cmd_list.current_ptr++, write_mask);
|
WritePicaReg(cmd, *g_state.cmd_list.current_ptr++, header.parameter_mask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ void DebugContext::OnEvent(Event event, void* data) {
|
||||||
|
|
||||||
void DebugContext::Resume() {
|
void DebugContext::Resume() {
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(breakpoint_mutex);
|
std::lock_guard<std::mutex> lock(breakpoint_mutex);
|
||||||
|
|
||||||
// Tell all observers that we are about to resume
|
// Tell all observers that we are about to resume
|
||||||
for (auto& breakpoint_observer : breakpoint_observers) {
|
for (auto& breakpoint_observer : breakpoint_observers) {
|
||||||
|
@ -312,11 +312,10 @@ void StartPicaTracing()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pica_trace_mutex.lock();
|
std::lock_guard<std::mutex> lock(pica_trace_mutex);
|
||||||
pica_trace = std::unique_ptr<PicaTrace>(new PicaTrace);
|
pica_trace = std::unique_ptr<PicaTrace>(new PicaTrace);
|
||||||
|
|
||||||
is_pica_tracing = true;
|
is_pica_tracing = true;
|
||||||
pica_trace_mutex.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsPicaTracing()
|
bool IsPicaTracing()
|
||||||
|
@ -324,18 +323,18 @@ bool IsPicaTracing()
|
||||||
return is_pica_tracing != 0;
|
return is_pica_tracing != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnPicaRegWrite(u32 id, u32 value)
|
void OnPicaRegWrite(PicaTrace::Write write)
|
||||||
{
|
{
|
||||||
// Double check for is_pica_tracing to avoid pointless locking overhead
|
// Double check for is_pica_tracing to avoid pointless locking overhead
|
||||||
if (!is_pica_tracing)
|
if (!is_pica_tracing)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lock(pica_trace_mutex);
|
std::lock_guard<std::mutex> lock(pica_trace_mutex);
|
||||||
|
|
||||||
if (!is_pica_tracing)
|
if (!is_pica_tracing)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pica_trace->writes.emplace_back(id, value);
|
pica_trace->writes.push_back(write);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<PicaTrace> FinishPicaTracing()
|
std::unique_ptr<PicaTrace> FinishPicaTracing()
|
||||||
|
@ -349,9 +348,9 @@ std::unique_ptr<PicaTrace> FinishPicaTracing()
|
||||||
is_pica_tracing = false;
|
is_pica_tracing = false;
|
||||||
|
|
||||||
// Wait until running tracing is finished
|
// Wait until running tracing is finished
|
||||||
pica_trace_mutex.lock();
|
std::lock_guard<std::mutex> lock(pica_trace_mutex);
|
||||||
std::unique_ptr<PicaTrace> ret(std::move(pica_trace));
|
std::unique_ptr<PicaTrace> ret(std::move(pica_trace));
|
||||||
pica_trace_mutex.unlock();
|
|
||||||
return std::move(ret);
|
return std::move(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -187,21 +187,17 @@ void DumpShader(const std::string& filename, const Regs::ShaderConfig& config,
|
||||||
|
|
||||||
// Utility class to log Pica commands.
|
// Utility class to log Pica commands.
|
||||||
struct PicaTrace {
|
struct PicaTrace {
|
||||||
struct Write : public std::pair<u32,u32> {
|
struct Write {
|
||||||
Write(u32 id, u32 value) : std::pair<u32,u32>(id, value) {}
|
u16 cmd_id;
|
||||||
|
u16 mask;
|
||||||
u32& Id() { return first; }
|
u32 value;
|
||||||
const u32& Id() const { return first; }
|
|
||||||
|
|
||||||
u32& Value() { return second; }
|
|
||||||
const u32& Value() const { return second; }
|
|
||||||
};
|
};
|
||||||
std::vector<Write> writes;
|
std::vector<Write> writes;
|
||||||
};
|
};
|
||||||
|
|
||||||
void StartPicaTracing();
|
void StartPicaTracing();
|
||||||
bool IsPicaTracing();
|
bool IsPicaTracing();
|
||||||
void OnPicaRegWrite(u32 id, u32 value);
|
void OnPicaRegWrite(PicaTrace::Write write);
|
||||||
std::unique_ptr<PicaTrace> FinishPicaTracing();
|
std::unique_ptr<PicaTrace> FinishPicaTracing();
|
||||||
|
|
||||||
struct TextureInfo {
|
struct TextureInfo {
|
||||||
|
|
Reference in New Issue