Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/Debugger/WebSocket/GPURecordSubscriber.cpp
3187 views
1
// Copyright (c) 2018- 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/Data/Encoding/Base64.h"
19
#include "Common/File/FileUtil.h"
20
#include "Core/Debugger/WebSocket/GPURecordSubscriber.h"
21
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
22
#include "Core/System.h"
23
#include "GPU/Debugger/Record.h"
24
#include "GPU/GPU.h"
25
#include "GPU/Common/GPUDebugInterface.h"
26
27
struct WebSocketGPURecordState : public DebuggerSubscriber {
28
~WebSocketGPURecordState();
29
void Dump(DebuggerRequest &req);
30
31
void Broadcast(net::WebSocketServer *ws) override;
32
33
protected:
34
bool pending_ = false;
35
std::string lastTicket_;
36
Path lastFilename_;
37
};
38
39
DebuggerSubscriber *WebSocketGPURecordInit(DebuggerEventHandlerMap &map) {
40
auto p = new WebSocketGPURecordState();
41
map["gpu.record.dump"] = std::bind(&WebSocketGPURecordState::Dump, p, std::placeholders::_1);
42
43
return p;
44
}
45
46
WebSocketGPURecordState::~WebSocketGPURecordState() {
47
// Clear the callback to hopefully avoid a crash.
48
if (pending_)
49
gpuDebug->GetRecorder()->ClearCallback();
50
}
51
52
// Begin recording (gpu.record.dump)
53
//
54
// No parameters.
55
//
56
// Response (same event name):
57
// - uri: data: URI containing debug dump data.
58
//
59
// Note: recording may take a moment.
60
void WebSocketGPURecordState::Dump(DebuggerRequest &req) {
61
if (PSP_GetBootState() != BootState::Complete) {
62
return req.Fail("CPU not started");
63
}
64
65
bool result = gpuDebug->GetRecorder()->RecordNextFrame([=](const Path &filename) {
66
lastFilename_ = filename;
67
pending_ = false;
68
});
69
70
if (!result) {
71
return req.Fail("Recording already in progress");
72
}
73
74
pending_ = true;
75
76
const JsonNode *value = req.data.get("ticket");
77
lastTicket_ = value ? json_stringify(value) : "";
78
}
79
80
// This handles the asynchronous gpu.record.dump response.
81
void WebSocketGPURecordState::Broadcast(net::WebSocketServer *ws) {
82
if (!lastFilename_.empty()) {
83
FILE *fp = File::OpenCFile(lastFilename_, "rb");
84
if (!fp) {
85
lastFilename_.clear();
86
return;
87
}
88
89
// We write directly to the stream since this is a large chunk of data.
90
ws->AddFragment(false, R"({"event":"gpu.record.dump")");
91
if (!lastTicket_.empty()) {
92
ws->AddFragment(false, R"(,"ticket":)");
93
ws->AddFragment(false, lastTicket_);
94
}
95
ws->AddFragment(false, R"(,"uri":"data:application/octet-stream;base64,)");
96
97
// Divisible by 3 for base64 reasons.
98
const size_t BUF_SIZE = 16383;
99
std::vector<uint8_t> buf;
100
buf.resize(BUF_SIZE);
101
while (!feof(fp)) {
102
size_t bytes = fread(&buf[0], 1, BUF_SIZE, fp);
103
ws->AddFragment(false, Base64Encode(&buf[0], bytes));
104
}
105
fclose(fp);
106
107
ws->AddFragment(true, R"("})");
108
109
lastFilename_.clear();
110
lastTicket_.clear();
111
}
112
}
113
114