Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Windows/GEDebugger/TabVertices.cpp
3186 views
1
// Copyright (c) 2012- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include "Common/CommonWindows.h"
19
#include <commctrl.h>
20
#include "Common/CommonTypes.h"
21
#include "Common/Data/Encoding/Utf8.h"
22
#include "Common/StringUtils.h"
23
#include "Core/System.h"
24
#include "Windows/resource.h"
25
#include "Windows/InputBox.h"
26
#include "Windows/GEDebugger/GEDebugger.h"
27
#include "Windows/GEDebugger/TabVertices.h"
28
#include "Windows/W32Util/ContextMenu.h"
29
#include "GPU/Common/VertexDecoderCommon.h"
30
#include "GPU/GPUState.h"
31
#include "GPU/GeDisasm.h"
32
#include "GPU/Common/GPUDebugInterface.h"
33
#include "GPU/Debugger/Breakpoints.h"
34
#include "GPU/Debugger/Stepping.h"
35
#include "GPU/Debugger/State.h"
36
37
static const GenericListViewColumn vertexListCols[] = {
38
{ L"X", 0.1f },
39
{ L"Y", 0.1f },
40
{ L"Z", 0.1f },
41
{ L"U", 0.1f },
42
{ L"V", 0.1f },
43
{ L"Color", 0.1f },
44
{ L"NX", 0.1f },
45
{ L"NY", 0.1f },
46
{ L"NZ", 0.1f },
47
// TODO: weight, morph?
48
};
49
50
GenericListViewDef vertexListDef = {
51
vertexListCols, ARRAY_SIZE(vertexListCols), NULL, false
52
};
53
54
static const GenericListViewColumn matrixListCols[] = {
55
{ L"", 0.03f },
56
{ L"Name", 0.21f },
57
{ L"0", 0.19f },
58
{ L"1", 0.19f },
59
{ L"2", 0.19f },
60
{ L"3", 0.19f },
61
};
62
63
GenericListViewDef matrixListDef = {
64
matrixListCols, ARRAY_SIZE(matrixListCols), NULL, false
65
};
66
67
enum MatrixListCols {
68
MATRIXLIST_COL_BREAKPOINT,
69
MATRIXLIST_COL_NAME,
70
MATRIXLIST_COL_0,
71
MATRIXLIST_COL_1,
72
MATRIXLIST_COL_2,
73
MATRIXLIST_COL_3,
74
75
MATRIXLIST_COL_COUNT,
76
};
77
78
enum MatrixListRows {
79
MATRIXLIST_ROW_WORLD_0,
80
MATRIXLIST_ROW_WORLD_1,
81
MATRIXLIST_ROW_WORLD_2,
82
MATRIXLIST_ROW_VIEW_0,
83
MATRIXLIST_ROW_VIEW_1,
84
MATRIXLIST_ROW_VIEW_2,
85
MATRIXLIST_ROW_PROJ_0,
86
MATRIXLIST_ROW_PROJ_1,
87
MATRIXLIST_ROW_PROJ_2,
88
MATRIXLIST_ROW_PROJ_3,
89
MATRIXLIST_ROW_TGEN_0,
90
MATRIXLIST_ROW_TGEN_1,
91
MATRIXLIST_ROW_TGEN_2,
92
MATRIXLIST_ROW_BONE_0_0,
93
MATRIXLIST_ROW_BONE_0_1,
94
MATRIXLIST_ROW_BONE_0_2,
95
MATRIXLIST_ROW_BONE_1_0,
96
MATRIXLIST_ROW_BONE_1_1,
97
MATRIXLIST_ROW_BONE_1_2,
98
MATRIXLIST_ROW_BONE_2_0,
99
MATRIXLIST_ROW_BONE_2_1,
100
MATRIXLIST_ROW_BONE_2_2,
101
MATRIXLIST_ROW_BONE_3_0,
102
MATRIXLIST_ROW_BONE_3_1,
103
MATRIXLIST_ROW_BONE_3_2,
104
MATRIXLIST_ROW_BONE_4_0,
105
MATRIXLIST_ROW_BONE_4_1,
106
MATRIXLIST_ROW_BONE_4_2,
107
MATRIXLIST_ROW_BONE_5_0,
108
MATRIXLIST_ROW_BONE_5_1,
109
MATRIXLIST_ROW_BONE_5_2,
110
MATRIXLIST_ROW_BONE_6_0,
111
MATRIXLIST_ROW_BONE_6_1,
112
MATRIXLIST_ROW_BONE_6_2,
113
MATRIXLIST_ROW_BONE_7_0,
114
MATRIXLIST_ROW_BONE_7_1,
115
MATRIXLIST_ROW_BONE_7_2,
116
117
MATRIXLIST_ROW_COUNT,
118
};
119
120
CtrlVertexList::CtrlVertexList(HWND hwnd)
121
: GenericListControl(hwnd, vertexListDef), raw_(false) {
122
decoder = new VertexDecoder();
123
Update();
124
}
125
126
CtrlVertexList::~CtrlVertexList() {
127
delete decoder;
128
}
129
130
void CtrlVertexList::GetColumnText(wchar_t *dest, size_t destSize, int row, int col) {
131
if (row < 0 || row >= rowCount_ ) {
132
wcscpy(dest, L"Invalid");
133
return;
134
}
135
136
if (!indices.empty()) {
137
if (row >= (int)indices.size()) {
138
swprintf(dest, destSize, L"Invalid index %d", row);
139
return;
140
}
141
row = indices[row];
142
}
143
144
char temp[256];
145
if (raw_) {
146
FormatVertColRaw(decoder, temp, sizeof(temp), row, col);
147
} else {
148
if (row >= (int)vertices.size()) {
149
swprintf(dest, destSize, L"Invalid vertex %d", row);
150
return;
151
}
152
153
FormatVertCol(temp, sizeof(temp), vertices[row], col);
154
}
155
ConvertUTF8ToWString(dest, destSize, temp);
156
}
157
158
int CtrlVertexList::GetRowCount() {
159
auto memLock = Memory::Lock();
160
if (!PSP_IsInited()) {
161
return 0;
162
}
163
164
if (!gpuDebug || !Memory::IsValidAddress(gpuDebug->GetVertexAddress())) {
165
rowCount_ = 0;
166
return rowCount_;
167
}
168
169
// TODO: Maybe there are smarter ways? Also, is this the best place to recalc?
170
auto state = gpuDebug->GetGState();
171
172
int rowCount_ = gpuDebug->GetCurrentPrimCount();
173
if (!gpuDebug->GetCurrentDrawAsDebugVertices(rowCount_, vertices, indices)) {
174
rowCount_ = 0;
175
}
176
VertexDecoderOptions options{};
177
// TODO: Maybe an option?
178
u32 vertTypeID = GetVertTypeID(state.vertType, state.getUVGenMode(), true);
179
decoder->SetVertexType(vertTypeID, options);
180
return rowCount_;
181
}
182
183
TabVertices::TabVertices(HINSTANCE _hInstance, HWND _hParent)
184
: Dialog((LPCSTR)IDD_GEDBG_TAB_VERTICES, _hInstance, _hParent) {
185
values = new CtrlVertexList(GetDlgItem(m_hDlg, IDC_GEDBG_VERTICES));
186
}
187
188
TabVertices::~TabVertices() {
189
delete values;
190
}
191
192
void TabVertices::UpdateSize(WORD width, WORD height) {
193
struct Position {
194
int x,y;
195
int w,h;
196
};
197
198
Position position;
199
static const int borderMargin = 5;
200
static const int checkboxSpace = 22;
201
202
position.x = borderMargin;
203
position.y = borderMargin + checkboxSpace;
204
position.w = width - 2 * borderMargin;
205
position.h = height - 2 * borderMargin - checkboxSpace;
206
207
HWND handle = GetDlgItem(m_hDlg, IDC_GEDBG_VERTICES);
208
MoveWindow(handle, position.x, position.y, position.w, position.h, TRUE);
209
}
210
211
BOOL TabVertices::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
212
switch (message) {
213
case WM_INITDIALOG:
214
return TRUE;
215
216
case WM_SIZE:
217
UpdateSize(LOWORD(lParam), HIWORD(lParam));
218
return TRUE;
219
220
case WM_COMMAND:
221
if (LOWORD(wParam) == IDC_GEDBG_RAWVERTS) {
222
values->SetRaw(IsDlgButtonChecked(m_hDlg, IDC_GEDBG_RAWVERTS) == BST_CHECKED);
223
values->Update();
224
}
225
return TRUE;
226
227
case WM_NOTIFY:
228
switch (wParam)
229
{
230
case IDC_GEDBG_VERTICES:
231
SetWindowLongPtr(m_hDlg, DWLP_MSGRESULT, values->HandleNotify(lParam));
232
return TRUE;
233
}
234
break;
235
}
236
237
return FALSE;
238
}
239
240
CtrlMatrixList::CtrlMatrixList(HWND hwnd)
241
: GenericListControl(hwnd, matrixListDef) {
242
SetIconList(12, 12, { (HICON)LoadIcon(GetModuleHandle(nullptr), (LPCWSTR)IDI_BREAKPOINT_SMALL) });
243
Update();
244
}
245
246
bool CtrlMatrixList::OnColPrePaint(int row, int col, LPNMLVCUSTOMDRAW msg) {
247
const auto state = gpuDebug->GetGState();
248
const auto lastState = GPUStepping::LastState();
249
250
bool changed = false;
251
if (col < MATRIXLIST_COL_0) {
252
for (int c = MATRIXLIST_COL_0; c <= MATRIXLIST_COL_3; ++c) {
253
changed = changed || ColChanged(lastState, state, row, c);
254
}
255
} else {
256
changed = ColChanged(lastState, state, row, col);
257
}
258
259
// At the column level, we have to reset the color back.
260
static int lastRow = -1;
261
static COLORREF rowDefaultText;
262
if (lastRow != row) {
263
rowDefaultText = msg->clrText;
264
lastRow = row;
265
}
266
267
if (changed) {
268
msg->clrText = RGB(255, 0, 0);
269
return true;
270
} else if (msg->clrText != rowDefaultText) {
271
msg->clrText = rowDefaultText;
272
return true;
273
}
274
275
return false;
276
}
277
278
bool CtrlMatrixList::ColChanged(const GPUgstate &lastState, const GPUgstate &state, int row, int col) {
279
union {
280
float f;
281
uint32_t u;
282
} newVal, oldVal;
283
if (!GetValue(state, row, col, newVal.f) || !GetValue(lastState, row, col, oldVal.f))
284
return false;
285
286
// If there's any difference in bits, highlight.
287
return newVal.u != oldVal.u;
288
}
289
290
bool CtrlMatrixList::GetValue(const GPUgstate &state, int row, int col, float &val) {
291
if (!gpuDebug || row < 0 || row >= MATRIXLIST_ROW_COUNT || col < 0 || col >= MATRIXLIST_COL_COUNT)
292
return false;
293
294
if (col < MATRIXLIST_COL_0)
295
col = MATRIXLIST_COL_0;
296
297
if (row >= MATRIXLIST_ROW_BONE_0_0) {
298
int b = (row - MATRIXLIST_ROW_BONE_0_0) / 3;
299
int r = (row - MATRIXLIST_ROW_BONE_0_0) % 3;
300
int offset = b * 12 + r + (col - MATRIXLIST_COL_0) * 3;
301
302
val = state.boneMatrix[offset];
303
return true;
304
} else if (row >= MATRIXLIST_ROW_TGEN_0) {
305
int r = row - MATRIXLIST_ROW_TGEN_0;
306
int offset = r + (col - MATRIXLIST_COL_0) * 3;
307
308
val = state.tgenMatrix[offset];
309
return true;
310
} else if (row >= MATRIXLIST_ROW_PROJ_0) {
311
int r = row - MATRIXLIST_ROW_PROJ_0;
312
int offset = r + (col - MATRIXLIST_COL_0) * 4;
313
314
val = state.projMatrix[offset];
315
return true;
316
} else if (row >= MATRIXLIST_ROW_VIEW_0) {
317
int r = row - MATRIXLIST_ROW_VIEW_0;
318
int offset = r + (col - MATRIXLIST_COL_0) * 3;
319
320
val = state.viewMatrix[offset];
321
return true;
322
}
323
324
int r = row - MATRIXLIST_ROW_WORLD_0;
325
int offset = r + (col - MATRIXLIST_COL_0) * 3;
326
327
val = state.worldMatrix[offset];
328
return true;
329
}
330
331
void CtrlMatrixList::GetColumnText(wchar_t *dest, size_t destSize, int row, int col) {
332
if (col == MATRIXLIST_COL_BREAKPOINT) {
333
wcscpy(dest, L" ");
334
return;
335
}
336
337
float val;
338
if (!GetValue(gpuDebug->GetGState(), row, col, val)) {
339
wcscpy(dest, L"Invalid");
340
return;
341
}
342
343
if (row >= MATRIXLIST_ROW_BONE_0_0) {
344
int b = (row - MATRIXLIST_ROW_BONE_0_0) / 3;
345
int r = (row - MATRIXLIST_ROW_BONE_0_0) % 3;
346
switch (col) {
347
case MATRIXLIST_COL_NAME:
348
swprintf(dest, 255, L"Bone #%d row %d", b, r);
349
break;
350
351
default:
352
swprintf(dest, 255, L"%f", val);
353
break;
354
}
355
} else if (row >= MATRIXLIST_ROW_TGEN_0) {
356
int r = row - MATRIXLIST_ROW_TGEN_0;
357
switch (col) {
358
case MATRIXLIST_COL_NAME:
359
swprintf(dest, 255, L"Texgen %d", r);
360
break;
361
362
default:
363
swprintf(dest, 255, L"%f", val);
364
break;
365
}
366
} else if (row >= MATRIXLIST_ROW_PROJ_0) {
367
int r = row - MATRIXLIST_ROW_PROJ_0;
368
switch (col) {
369
case MATRIXLIST_COL_NAME:
370
swprintf(dest, 255, L"Proj %d", r);
371
break;
372
373
default:
374
swprintf(dest, 255, L"%f", val);
375
break;
376
}
377
} else if (row >= MATRIXLIST_ROW_VIEW_0) {
378
int r = row - MATRIXLIST_ROW_VIEW_0;
379
switch (col) {
380
case MATRIXLIST_COL_NAME:
381
swprintf(dest, 255, L"View %d", r);
382
break;
383
384
default:
385
swprintf(dest, 255, L"%f", val);
386
break;
387
}
388
} else {
389
int r = row - MATRIXLIST_ROW_WORLD_0;
390
switch (col) {
391
case MATRIXLIST_COL_NAME:
392
swprintf(dest, 255, L"World %d", r);
393
break;
394
395
default:
396
swprintf(dest, 255, L"%f", val);
397
break;
398
}
399
}
400
}
401
402
int CtrlMatrixList::GetRowCount() {
403
if (!gpuDebug) {
404
return 0;
405
}
406
407
return MATRIXLIST_ROW_COUNT;
408
}
409
410
struct MatrixCmdPair {
411
MatrixListRows row;
412
GECommand numCmd;
413
GECommand cmd;
414
};
415
static constexpr MatrixCmdPair matrixCmds[] = {
416
{ MATRIXLIST_ROW_WORLD_0, GE_CMD_WORLDMATRIXNUMBER, GE_CMD_WORLDMATRIXDATA },
417
{ MATRIXLIST_ROW_VIEW_0, GE_CMD_VIEWMATRIXNUMBER, GE_CMD_VIEWMATRIXDATA },
418
{ MATRIXLIST_ROW_PROJ_0, GE_CMD_PROJMATRIXNUMBER, GE_CMD_PROJMATRIXDATA },
419
{ MATRIXLIST_ROW_TGEN_0, GE_CMD_TGENMATRIXNUMBER, GE_CMD_TGENMATRIXDATA },
420
{ MATRIXLIST_ROW_BONE_0_0, GE_CMD_BONEMATRIXNUMBER, GE_CMD_BONEMATRIXDATA },
421
{ MATRIXLIST_ROW_COUNT, GE_CMD_NOP },
422
};
423
424
static const MatrixCmdPair *FindCmdPair(int row) {
425
for (int i = 0; i < ARRAY_SIZE(matrixCmds) - 1; ++i) {
426
if (row < matrixCmds[i].row || row >= matrixCmds[i + 1].row)
427
continue;
428
429
return &matrixCmds[i];
430
}
431
return nullptr;
432
}
433
434
void CtrlMatrixList::ToggleBreakpoint(int row) {
435
const MatrixCmdPair *info = FindCmdPair(row);
436
if (!info)
437
return;
438
439
// Okay, this command is in range. Toggle the actual breakpoint.
440
bool state = !gpuDebug->GetBreakpoints()->IsCmdBreakpoint(info->cmd);
441
if (state) {
442
gpuDebug->GetBreakpoints()->AddCmdBreakpoint(info->cmd);
443
} else {
444
if (gpuDebug->GetBreakpoints()->GetCmdBreakpointCond(info->cmd, nullptr)) {
445
int ret = MessageBox(GetHandle(), L"This breakpoint has a custom condition.\nDo you want to remove it?", L"Confirmation", MB_YESNO);
446
if (ret != IDYES)
447
return;
448
}
449
gpuDebug->GetBreakpoints()->RemoveCmdBreakpoint(info->cmd);
450
}
451
452
for (int r = info->row; r < (info + 1)->row; ++r) {
453
SetItemState(r, state ? 1 : 0);
454
}
455
}
456
457
void CtrlMatrixList::PromptBreakpointCond(int row) {
458
const MatrixCmdPair *info = FindCmdPair(row);
459
if (!info)
460
return;
461
462
std::string expression;
463
gpuDebug->GetBreakpoints()->GetCmdBreakpointCond(info->cmd, &expression);
464
if (!InputBox_GetString(GetModuleHandle(NULL), GetHandle(), L"Expression", expression, expression))
465
return;
466
467
std::string error;
468
if (!gpuDebug->GetBreakpoints()->SetCmdBreakpointCond(info->cmd, expression, &error))
469
MessageBox(GetHandle(), ConvertUTF8ToWString(error).c_str(), L"Invalid expression", MB_OK | MB_ICONEXCLAMATION);
470
}
471
472
void CtrlMatrixList::OnDoubleClick(int row, int column) {
473
if (row >= GetRowCount())
474
return;
475
476
if (column == MATRIXLIST_COL_BREAKPOINT) {
477
ToggleBreakpoint(row);
478
return;
479
}
480
481
float val;
482
if (!GetValue(gpuDebug->GetGState(), row, column, val))
483
return;
484
485
std::string strvalue = StringFromFormat("%f", val);
486
bool res = InputBox_GetString(GetModuleHandle(NULL), GetHandle(), L"Column value", strvalue, strvalue);
487
if (!res)
488
return;
489
490
if (sscanf(strvalue.c_str(), "%f", &val) == 1) {
491
auto prevState = gpuDebug->GetGState();
492
auto setCmdValue = [&](u32 op) {
493
SendMessage(GetParent(GetParent(GetHandle())), WM_GEDBG_SETCMDWPARAM, op, 0);
494
};
495
496
union {
497
float f;
498
u32 u;
499
} temp = { val };
500
501
for (int i = 0; i < ARRAY_SIZE(matrixCmds) - 1; ++i) {
502
if (row < matrixCmds[i].row || row >= matrixCmds[i + 1].row)
503
continue;
504
505
// Everything is 3 except the projection matrix, which is 4.
506
int sz = matrixCmds[i + 1].row - matrixCmds[i].row == 4 ? 4 : 3;
507
// Always zero except for bones.
508
int b = (row - matrixCmds[i].row) / sz;
509
int r = (row - matrixCmds[i].row) % sz;
510
int c = column >= MATRIXLIST_COL_0 ? column - MATRIXLIST_COL_0 : 0;
511
512
// Okay, now set the number, then data.
513
int n = b * 12 + r + c * sz;
514
setCmdValue((matrixCmds[i].numCmd << 24) | n);
515
setCmdValue((matrixCmds[i].cmd << 24) | (temp.u >> 8));
516
517
// Done, revert the number.
518
setCmdValue(prevState.cmdmem[matrixCmds[i].numCmd]);
519
Update();
520
}
521
}
522
}
523
524
void CtrlMatrixList::OnRightClick(int row, int column, const POINT &point) {
525
if (row >= GetRowCount())
526
return;
527
const MatrixCmdPair *info = FindCmdPair(row);
528
529
POINT screenPt(point);
530
ClientToScreen(GetHandle(), &screenPt);
531
532
HMENU subMenu = GetContextMenu(ContextMenuID::GEDBG_MATRIX);
533
SetMenuDefaultItem(subMenu, ID_REGLIST_CHANGE, FALSE);
534
EnableMenuItem(subMenu, ID_GEDBG_SETCOND, info && gpuDebug->GetBreakpoints()->IsCmdBreakpoint(info->cmd) ? MF_ENABLED : MF_GRAYED);
535
536
switch (TriggerContextMenu(ContextMenuID::GEDBG_MATRIX, GetHandle(), ContextPoint::FromClient(point))) {
537
case ID_DISASM_TOGGLEBREAKPOINT:
538
ToggleBreakpoint(row);
539
break;
540
541
case ID_GEDBG_SETCOND:
542
PromptBreakpointCond(row);
543
break;
544
545
case ID_DISASM_COPYINSTRUCTIONDISASM:
546
{
547
float val;
548
if (GetValue(gpuDebug->GetGState(), row, column, val)) {
549
wchar_t dest[512];
550
swprintf(dest, 511, L"%f", val);
551
W32Util::CopyTextToClipboard(GetHandle(), dest);
552
}
553
break;
554
}
555
556
case ID_GEDBG_COPYALL:
557
CopyRows(0, GetRowCount());
558
break;
559
560
case ID_REGLIST_CHANGE:
561
OnDoubleClick(row, MATRIXLIST_COL_0);
562
break;
563
}
564
}
565
566
TabMatrices::TabMatrices(HINSTANCE _hInstance, HWND _hParent)
567
: Dialog((LPCSTR)IDD_GEDBG_TAB_MATRICES, _hInstance, _hParent) {
568
values = new CtrlMatrixList(GetDlgItem(m_hDlg, IDC_GEDBG_MATRICES));
569
}
570
571
TabMatrices::~TabMatrices() {
572
delete values;
573
}
574
575
void TabMatrices::UpdateSize(WORD width, WORD height) {
576
struct Position {
577
int x,y;
578
int w,h;
579
};
580
581
Position position;
582
static const int borderMargin = 5;
583
584
position.x = borderMargin;
585
position.y = borderMargin;
586
position.w = width - 2 * borderMargin;
587
position.h = height - 2 * borderMargin;
588
589
HWND handle = GetDlgItem(m_hDlg, IDC_GEDBG_MATRICES);
590
MoveWindow(handle, position.x, position.y, position.w, position.h, TRUE);
591
}
592
593
BOOL TabMatrices::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
594
switch (message) {
595
case WM_INITDIALOG:
596
return TRUE;
597
598
case WM_SIZE:
599
UpdateSize(LOWORD(lParam), HIWORD(lParam));
600
return TRUE;
601
602
case WM_NOTIFY:
603
switch (wParam)
604
{
605
case IDC_GEDBG_MATRICES:
606
SetWindowLongPtr(m_hDlg, DWLP_MSGRESULT, values->HandleNotify(lParam));
607
return TRUE;
608
}
609
break;
610
}
611
612
return FALSE;
613
}
614
615