Path: blob/master/Core/Debugger/WebSocket/GPURecordSubscriber.cpp
3187 views
// Copyright (c) 2018- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include "Common/Data/Encoding/Base64.h"18#include "Common/File/FileUtil.h"19#include "Core/Debugger/WebSocket/GPURecordSubscriber.h"20#include "Core/Debugger/WebSocket/WebSocketUtils.h"21#include "Core/System.h"22#include "GPU/Debugger/Record.h"23#include "GPU/GPU.h"24#include "GPU/Common/GPUDebugInterface.h"2526struct WebSocketGPURecordState : public DebuggerSubscriber {27~WebSocketGPURecordState();28void Dump(DebuggerRequest &req);2930void Broadcast(net::WebSocketServer *ws) override;3132protected:33bool pending_ = false;34std::string lastTicket_;35Path lastFilename_;36};3738DebuggerSubscriber *WebSocketGPURecordInit(DebuggerEventHandlerMap &map) {39auto p = new WebSocketGPURecordState();40map["gpu.record.dump"] = std::bind(&WebSocketGPURecordState::Dump, p, std::placeholders::_1);4142return p;43}4445WebSocketGPURecordState::~WebSocketGPURecordState() {46// Clear the callback to hopefully avoid a crash.47if (pending_)48gpuDebug->GetRecorder()->ClearCallback();49}5051// Begin recording (gpu.record.dump)52//53// No parameters.54//55// Response (same event name):56// - uri: data: URI containing debug dump data.57//58// Note: recording may take a moment.59void WebSocketGPURecordState::Dump(DebuggerRequest &req) {60if (PSP_GetBootState() != BootState::Complete) {61return req.Fail("CPU not started");62}6364bool result = gpuDebug->GetRecorder()->RecordNextFrame([=](const Path &filename) {65lastFilename_ = filename;66pending_ = false;67});6869if (!result) {70return req.Fail("Recording already in progress");71}7273pending_ = true;7475const JsonNode *value = req.data.get("ticket");76lastTicket_ = value ? json_stringify(value) : "";77}7879// This handles the asynchronous gpu.record.dump response.80void WebSocketGPURecordState::Broadcast(net::WebSocketServer *ws) {81if (!lastFilename_.empty()) {82FILE *fp = File::OpenCFile(lastFilename_, "rb");83if (!fp) {84lastFilename_.clear();85return;86}8788// We write directly to the stream since this is a large chunk of data.89ws->AddFragment(false, R"({"event":"gpu.record.dump")");90if (!lastTicket_.empty()) {91ws->AddFragment(false, R"(,"ticket":)");92ws->AddFragment(false, lastTicket_);93}94ws->AddFragment(false, R"(,"uri":"data:application/octet-stream;base64,)");9596// Divisible by 3 for base64 reasons.97const size_t BUF_SIZE = 16383;98std::vector<uint8_t> buf;99buf.resize(BUF_SIZE);100while (!feof(fp)) {101size_t bytes = fread(&buf[0], 1, BUF_SIZE, fp);102ws->AddFragment(false, Base64Encode(&buf[0], bytes));103}104fclose(fp);105106ws->AddFragment(true, R"("})");107108lastFilename_.clear();109lastTicket_.clear();110}111}112113114