CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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