Skip to content

Commit

Permalink
matdbg: change from websocket to GET (#7263)
Browse files Browse the repository at this point in the history
- Use a hanging-GET approach to reduce dependency on websockets.
 - Also add mutex to protect access to MaterialRecords, which is
   written to/read from from multiple threads.
  • Loading branch information
poweifeng authored Oct 13, 2023
1 parent d5ebca0 commit 9284630
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 94 deletions.
11 changes: 6 additions & 5 deletions libs/matdbg/include/matdbg/DebugServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <private/filament/Variant.h>

#include <tsl/robin_map.h>
#include <utils/Mutex.h>

class CivetServer;

Expand All @@ -43,9 +44,8 @@ struct MaterialRecord {
/**
* Server-side material debugger.
*
* This class manages an HTTP server and a WebSockets server that listen on a secondary thread. It
* receives material packages from the Filament C++ engine or from a standalone tool such as
* matinfo.
* This class manages an HTTP server. It receives material packages from the Filament C++ engine or
* from a standalone tool such as matinfo.
*/
class DebugServer {
public:
Expand Down Expand Up @@ -99,7 +99,10 @@ class DebugServer {
const backend::Backend mBackend;

CivetServer* mServer;

tsl::robin_map<MaterialKey, MaterialRecord> mMaterialRecords;
mutable utils::Mutex mMaterialRecordsMutex;

utils::CString mHtml;
utils::CString mJavascript;
utils::CString mCss;
Expand All @@ -112,11 +115,9 @@ class DebugServer {

class FileRequestHandler* mFileHandler = nullptr;
class ApiHandler* mApiHandler = nullptr;
class WebSocketHandler* mWebSocketHandler = nullptr;

friend class FileRequestHandler;
friend class ApiHandler;
friend class WebSocketHandler;
};

} // namespace filament::matdbg
Expand Down
42 changes: 39 additions & 3 deletions libs/matdbg/src/ApiHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ namespace {
auto const& kSuccessHeader = DebugServer::kSuccessHeader;
auto const& kErrorHeader = DebugServer::kErrorHeader;

void spirvToAsm(struct mg_connection *conn, uint32_t const* spirv, size_t size) {
void spirvToAsm(struct mg_connection* conn, uint32_t const* spirv, size_t size) {
auto spirvDisassembly = ShaderExtractor::spirvToText(spirv, size / 4);
mg_printf(conn, kSuccessHeader.data(), "application/txt");
mg_write(conn, spirvDisassembly.c_str(), spirvDisassembly.size());
}

void spirvToGlsl(ShaderModel shaderModel, struct mg_connection *conn,
uint32_t const* spirv, size_t size) {
void spirvToGlsl(ShaderModel shaderModel, struct mg_connection* conn, uint32_t const* spirv,
size_t size) {
auto glsl = ShaderExtractor::spirvToGLSL(shaderModel, spirv, size / 4);
mg_printf(conn, kSuccessHeader.data(), "application/txt");
mg_printf(conn, glsl.c_str(), glsl.size());
Expand Down Expand Up @@ -212,6 +212,33 @@ bool ApiHandler::handleGetApiShader(struct mg_connection* conn,
return error(__LINE__, uri);
}

void ApiHandler::addMaterial(MaterialRecord const* material) {
std::unique_lock<utils::Mutex> lock(mStatusMutex);
snprintf(statusMaterialId, sizeof(statusMaterialId), "%8.8x", material->key);
mStatusCondition.notify_all();
}

bool ApiHandler::handleGetStatus(struct mg_connection* conn,
struct mg_request_info const* request) {
char const* qstr = request->query_string;
if (qstr && strcmp(qstr, "firstTime") == 0) {
mg_printf(conn, kSuccessHeader.data(), "application/txt");
mg_write(conn, "0", 1);
return true;
}

{
std::unique_lock<utils::Mutex> lock(mStatusMutex);
uint64_t const currentStatusCount = mCurrentStatus;
mStatusCondition.wait(lock,
[this, currentStatusCount] { return currentStatusCount < mCurrentStatus; });

mg_printf(conn, kSuccessHeader.data(), "application/txt");
mg_write(conn, statusMaterialId, 8);
}
return true;
}

bool ApiHandler::handlePost(CivetServer* server, struct mg_connection* conn) {
struct mg_request_info const* request = mg_get_request_info(conn);
std::string const& uri = request->local_uri;
Expand Down Expand Up @@ -264,6 +291,9 @@ bool ApiHandler::handleGet(CivetServer* server, struct mg_connection* conn) {

if (uri == "/api/active") {
mServer->updateActiveVariants();

// Careful not to lock the above line.
std::unique_lock<utils::Mutex> lock(mServer->mMaterialRecordsMutex);
mg_printf(conn, kSuccessHeader.data(), "application/json");
mg_printf(conn, "{");

Expand Down Expand Up @@ -294,6 +324,7 @@ bool ApiHandler::handleGet(CivetServer* server, struct mg_connection* conn) {
}

if (uri == "/api/matids") {
std::unique_lock<utils::Mutex> lock(mServer->mMaterialRecordsMutex);
mg_printf(conn, kSuccessHeader.data(), "application/json");
mg_printf(conn, "[");
int index = 0;
Expand All @@ -306,6 +337,7 @@ bool ApiHandler::handleGet(CivetServer* server, struct mg_connection* conn) {
}

if (uri == "/api/materials") {
std::unique_lock<utils::Mutex> lock(mServer->mMaterialRecordsMutex);
mg_printf(conn, kSuccessHeader.data(), "application/json");
mg_printf(conn, "[");
int index = 0;
Expand Down Expand Up @@ -353,6 +385,10 @@ bool ApiHandler::handleGet(CivetServer* server, struct mg_connection* conn) {
return handleGetApiShader(conn, request);
}

if (uri.find("/api/status") == 0) {
return handleGetStatus(conn, request);
}

return error(__LINE__, uri);
}

Expand Down
19 changes: 16 additions & 3 deletions libs/matdbg/src/ApiHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#ifndef MATDBG_APIHANDLER_H
#define MATDBG_APIHANDLER_H

#include <utils/Mutex.h>

#include <CivetServer.h>

namespace filament::matdbg {
Expand All @@ -31,23 +33,34 @@ struct MaterialRecord;
// GET /api/material?matid={id}
// GET /api/shader?matid={id}&type=[glsl|spirv]&[glindex|vkindex|metalindex]={index}
// GET /api/active
// GET /api/status
// POST /api/edit
//
class ApiHandler : public CivetHandler {
public:
explicit ApiHandler(DebugServer* server)
: mServer(server) {}
~ApiHandler() = default;

bool handleGet(CivetServer* server, struct mg_connection* conn);

bool handlePost(CivetServer* server, struct mg_connection* conn);

~ApiHandler() = default;
void addMaterial(MaterialRecord const* material);

private:
bool handleGetApiShader(struct mg_connection* conn, struct mg_request_info const* request);
bool handleGetStatus(struct mg_connection* conn, struct mg_request_info const* request);
MaterialRecord const* getMaterialRecord(struct mg_request_info const* request);

DebugServer* mServer;

utils::Mutex mStatusMutex;
std::condition_variable mStatusCondition;
char statusMaterialId[9] = {};

// This variable is to implement a *hanging* effect for /api/status. The call to /api/status
// will always block until statusMaterialId is updated again. The client is expected to keep
// calling /api/status (a constant "pull" to simulate a push).
std::atomic_uint64_t mCurrentStatus = 0;
};

} // filament::matdbg
Expand Down
56 changes: 14 additions & 42 deletions libs/matdbg/src/DebugServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,42 +116,6 @@ class FileRequestHandler : public CivetHandler {
DebugServer* mServer;
};

class WebSocketHandler : public CivetWebSocketHandler {
public:
WebSocketHandler(DebugServer* server) : mServer(server) {}

bool handleConnection(CivetServer *server, const struct mg_connection *conn) override {
return true;
}

void handleReadyState(CivetServer *server, struct mg_connection *conn) override {
mConnections.insert(conn);
}

bool handleData(CivetServer *server, struct mg_connection *conn, int bits, char *data,
size_t size) override {
return false;
}

void handleClose(CivetServer *server, const struct mg_connection *conn) override {
struct mg_connection *key = const_cast<struct mg_connection *>(conn);
mConnections.erase(key);
}

// Notify all JavaScript clients that a new material package has been loaded.
void addMaterial(MaterialRecord const& material) {
for (auto connection : mConnections) {
char matid[9] = {};
snprintf(matid, sizeof(matid), "%8.8x", material.key);
mg_websocket_write(connection, MG_WEBSOCKET_OPCODE_TEXT, matid, 8);
}
}

private:
DebugServer* mServer;
tsl::robin_set<struct mg_connection*> mConnections;
};

DebugServer::DebugServer(Backend backend, int port) : mBackend(backend) {
#if !SERVE_FROM_SOURCE_TREE
mHtml = CString((const char*) MATDBG_RESOURCES_INDEX_DATA, MATDBG_RESOURCES_INDEX_SIZE - 1);
Expand Down Expand Up @@ -182,24 +146,27 @@ DebugServer::DebugServer(Backend backend, int port) : mBackend(backend) {

mFileHandler = new FileRequestHandler(this);
mApiHandler = new ApiHandler(this);
mWebSocketHandler = new WebSocketHandler(this);

mServer->addHandler("/api", mApiHandler);
mServer->addHandler("", mFileHandler);
mServer->addWebSocketHandler("", mWebSocketHandler);

slog.i << "DebugServer listening at http://localhost:" << port << io::endl;
filamat::GLSLTools::init();
}

DebugServer::~DebugServer() {
filamat::GLSLTools::shutdown();
for (auto& pair : mMaterialRecords) {
delete [] pair.second.package;
}

mServer->close();

delete mFileHandler;
delete mApiHandler;
delete mServer;

std::unique_lock<utils::Mutex> lock(mMaterialRecordsMutex);
for (auto& pair : mMaterialRecords) {
delete [] pair.second.package;
}
}

MaterialKey
Expand All @@ -218,23 +185,27 @@ DebugServer::addMaterial(const CString& name, const void* data, size_t size, voi
uint8_t* package = new uint8_t[size];
memcpy(package, data, size);

std::unique_lock<utils::Mutex> lock(mMaterialRecordsMutex);
MaterialRecord info = {userdata, package, size, name, key};
mMaterialRecords.insert({key, info});
mWebSocketHandler->addMaterial(info);
mApiHandler->addMaterial(&info);
return key;
}

void DebugServer::removeMaterial(MaterialKey key) {
std::unique_lock<utils::Mutex> lock(mMaterialRecordsMutex);
mMaterialRecords.erase(key);
}

const MaterialRecord* DebugServer::getRecord(const MaterialKey& key) const {
std::unique_lock<utils::Mutex> lock(mMaterialRecordsMutex);
const auto& iter = mMaterialRecords.find(key);
return iter == mMaterialRecords.end() ? nullptr : &iter->second;
}

void DebugServer::updateActiveVariants() {
if (mQueryCallback) {
std::unique_lock<utils::Mutex> lock(mMaterialRecordsMutex);
auto curr = mMaterialRecords.begin();
auto end = mMaterialRecords.end();
while (curr != end) {
Expand All @@ -253,6 +224,7 @@ bool DebugServer::handleEditCommand(const MaterialKey& key, backend::Backend api
return false;
};

std::unique_lock<utils::Mutex> lock(mMaterialRecordsMutex);
if (mMaterialRecords.find(key) == mMaterialRecords.end()) {
return error(__LINE__);
}
Expand Down
Loading

0 comments on commit 9284630

Please sign in to comment.