diff --git a/libs/matdbg/CMakeLists.txt b/libs/matdbg/CMakeLists.txt index f53413be05..0dafe5c71d 100644 --- a/libs/matdbg/CMakeLists.txt +++ b/libs/matdbg/CMakeLists.txt @@ -22,6 +22,8 @@ set(PUBLIC_HDRS ) set(SRCS + src/ApiHandler.cpp + src/ApiHandler.h src/CommonWriter.h src/CommonWriter.cpp src/DebugServer.cpp @@ -59,7 +61,6 @@ endif() set(DUMMY_SRC "${RESOURCE_DIR}/dummy.c") add_custom_command(OUTPUT ${DUMMY_SRC} COMMAND echo "//" > ${DUMMY_SRC}) - add_library(matdbg_resources ${DUMMY_SRC} ${RESGEN_SOURCE}) set_target_properties(matdbg_resources PROPERTIES FOLDER Libs) diff --git a/libs/matdbg/include/matdbg/DebugServer.h b/libs/matdbg/include/matdbg/DebugServer.h index fa1ef0fc9b..48a0b25e0f 100644 --- a/libs/matdbg/include/matdbg/DebugServer.h +++ b/libs/matdbg/include/matdbg/DebugServer.h @@ -27,11 +27,19 @@ class CivetServer; -namespace filament { -namespace matdbg { +namespace filament::matdbg { using MaterialKey = uint32_t; +struct MaterialRecord { + void* userdata; + const uint8_t* package; + size_t packageSize; + utils::CString name; + MaterialKey key; + VariantList activeVariants; +}; + /** * Server-side material debugger. * @@ -41,6 +49,9 @@ using MaterialKey = uint32_t; */ class DebugServer { public: + static std::string_view const kSuccessHeader; + static std::string_view const kErrorHeader; + DebugServer(backend::Backend backend, int port); ~DebugServer(); @@ -74,16 +85,7 @@ public: bool isReady() const { return mServer; } private: - struct MaterialRecord { - void* userdata; - const uint8_t* package; - size_t packageSize; - utils::CString name; - MaterialKey key; - VariantList activeVariants; - }; - - const MaterialRecord* getRecord(const MaterialKey& key) const; + MaterialRecord const* getRecord(const MaterialKey& key) const; void updateActiveVariants(); @@ -109,15 +111,14 @@ private: QueryCallback mQueryCallback = nullptr; class FileRequestHandler* mFileHandler = nullptr; - class RestRequestHandler* mRestHandler = nullptr; + class ApiHandler* mApiHandler = nullptr; class WebSocketHandler* mWebSocketHandler = nullptr; friend class FileRequestHandler; - friend class RestRequestHandler; + friend class ApiHandler; friend class WebSocketHandler; }; -} // namespace matdbg -} // namespace filament +} // namespace filament::matdbg #endif // MATDBG_DEBUGSERVER_H diff --git a/libs/matdbg/src/ApiHandler.cpp b/libs/matdbg/src/ApiHandler.cpp new file mode 100644 index 0000000000..42cb9eb2de --- /dev/null +++ b/libs/matdbg/src/ApiHandler.cpp @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "ApiHandler.h" + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +namespace filament::matdbg { + +using namespace filament::backend; + +namespace { + +auto const& kSuccessHeader = DebugServer::kSuccessHeader; +auto const& kErrorHeader = DebugServer::kErrorHeader; + +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) { + auto glsl = ShaderExtractor::spirvToGLSL(shaderModel, spirv, size / 4); + mg_printf(conn, kSuccessHeader.data(), "application/txt"); + mg_printf(conn, glsl.c_str(), glsl.size()); +} + +} // anonymous + +using filaflat::ChunkContainer; +using filamat::ChunkType; +using utils::FixedCapacityVector; + +static auto const error = [](int line, std::string const& uri) { + utils::slog.e << "DebugServer: 404 at line " << line << ": " << uri << utils::io::endl; + return false; +}; + +MaterialRecord const* ApiHandler::getMaterialRecord(struct mg_request_info const* request) { + size_t const qlength = strlen(request->query_string); + char matid[9] = {}; + if (mg_get_var(request->query_string, qlength, "matid", matid, sizeof(matid)) < 0) { + return nullptr; + } + uint32_t const id = strtoul(matid, nullptr, 16); + return mServer->getRecord(id); +} + +bool ApiHandler::handleGetApiShader(struct mg_connection* conn, + struct mg_request_info const* request) { + auto const softError = [conn, request](char const* msg) { + utils::slog.e << "DebugServer: " << msg << ": " << request->query_string << utils::io::endl; + mg_printf(conn, kErrorHeader.data(), "application/txt"); + mg_write(conn, msg, strlen(msg)); + return true; + }; + + MaterialRecord const* result = getMaterialRecord(request); + std::string const& uri = request->local_uri; + + if (!result) { + return error(__LINE__, uri); + } + + ChunkContainer package(result->package, result->packageSize); + if (!package.parse()) { + return error(__LINE__, uri); + } + + std::string_view const glsl("glsl"); + std::string_view const msl("msl"); + std::string_view const spirv("spirv"); + size_t const qlength = strlen(request->query_string); + + char type[6] = {}; + if (mg_get_var(request->query_string, qlength, "type", type, sizeof(type)) < 0) { + return error(__LINE__, uri); + } + + std::string_view const language(type, strlen(type)); + + char glindex[4] = {}; + char vkindex[4] = {}; + char metalindex[4] = {}; + mg_get_var(request->query_string, qlength, "glindex", glindex, sizeof(glindex)); + mg_get_var(request->query_string, qlength, "vkindex", vkindex, sizeof(vkindex)); + mg_get_var(request->query_string, qlength, "metalindex", metalindex, sizeof(metalindex)); + + if (!glindex[0] && !vkindex[0] && !metalindex[0]) { + return error(__LINE__, uri); + } + + if (glindex[0]) { + if (language != glsl) { + return softError("Only GLSL is supported."); + } + + FixedCapacityVector info(getShaderCount(package, ChunkType::MaterialGlsl)); + if (!getShaderInfo(package, info.data(), ChunkType::MaterialGlsl)) { + return error(__LINE__, uri); + } + + int const shaderIndex = std::stoi(glindex); + if (shaderIndex >= info.size()) { + return error(__LINE__, uri); + } + + ShaderExtractor extractor(ShaderLanguage::ESSL3, result->package, result->packageSize); + if (!extractor.parse()) { + return error(__LINE__, uri); + } + + auto const& item = info[shaderIndex]; + filaflat::ShaderContent content; + extractor.getShader(item.shaderModel, item.variant, item.pipelineStage, content); + + mg_printf(conn, kSuccessHeader.data(), "application/txt"); + mg_write(conn, content.data(), content.size() - 1); + return true; + } + + if (vkindex[0]) { + ShaderExtractor extractor(ShaderLanguage::SPIRV, result->package, result->packageSize); + if (!extractor.parse()) { + return error(__LINE__, uri); + } + + FixedCapacityVector info(getShaderCount(package, ChunkType::MaterialSpirv)); + if (!getShaderInfo(package, info.data(), ChunkType::MaterialSpirv)) { + return error(__LINE__, uri); + } + + int const shaderIndex = std::stoi(vkindex); + if (shaderIndex >= info.size()) { + return error(__LINE__, uri); + } + + auto const& item = info[shaderIndex]; + filaflat::ShaderContent content; + extractor.getShader(item.shaderModel, item.variant, item.pipelineStage, content); + + if (language == spirv) { + spirvToAsm(conn, (uint32_t const*) content.data(), content.size()); + return true; + } + + if (language == glsl) { + spirvToGlsl(item.shaderModel, conn, (uint32_t const*) content.data(), content.size()); + return true; + } + + return softError("Only SPIRV is supported."); + } + + if (metalindex[0]) { + ShaderExtractor extractor(ShaderLanguage::MSL, result->package, result->packageSize); + if (!extractor.parse()) { + return error(__LINE__, uri); + } + + FixedCapacityVector info(getShaderCount(package, ChunkType::MaterialMetal)); + if (!getShaderInfo(package, info.data(), ChunkType::MaterialMetal)) { + return error(__LINE__, uri); + } + + int const shaderIndex = std::stoi(metalindex); + if (shaderIndex >= info.size()) { + return error(__LINE__, uri); + } + + auto const& item = info[shaderIndex]; + filaflat::ShaderContent content; + extractor.getShader(item.shaderModel, item.variant, item.pipelineStage, content); + + if (language == msl) { + mg_printf(conn, kSuccessHeader.data(), "application/txt"); + mg_write(conn, content.data(), content.size() - 1); + return true; + } + + return softError("Only MSL is supported."); + } + return error(__LINE__, uri); +} + +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; + + // For now we simply use istringstream for parsing, so command arguments are delimited + // with space characters. + // + // The "shader index" is a zero-based index into the list of variants using the order that + // they appear in the package, where each API (GL / VK / Metal) has its own list. + // + // POST body format: + // [material id] [api index] [shader index] [shader source....] + if (uri == "/api/edit") { + struct mg_request_info const* req_info = mg_get_request_info(conn); + size_t msgLen = req_info->content_length; + + char buf[1024]; + size_t readLen = 0; + std::stringstream sstream; + while (readLen < msgLen) { + int res = mg_read(conn, buf, sizeof(buf)); + if (res < 0) { + utils::slog.e << "civet error parsing /api/edit body: " << res << utils::io::endl; + break; + } + if (res == 0) { + break; + } + readLen += res; + sstream.write(buf, res); + } + uint32_t matid; + int api; + int shaderIndex; + sstream >> std::hex >> matid >> std::dec >> api >> shaderIndex; + std::string shader = sstream.str().substr(sstream.tellg()); + + mServer->handleEditCommand(matid, backend::Backend(api), shaderIndex, shader.c_str(), + shader.size()); + + mg_printf(conn, "HTTP/1.1 200 OK\r\nConnection: close"); + return true; + } + return error(__LINE__, uri); +} + +bool ApiHandler::handleGet(CivetServer* server, struct mg_connection* conn) { + struct mg_request_info const* request = mg_get_request_info(conn); + std::string const& uri = request->local_uri; + + if (uri == "/api/active") { + mServer->updateActiveVariants(); + mg_printf(conn, kSuccessHeader.data(), "application/json"); + mg_printf(conn, "{"); + + // If the backend has not been resolved to Vulkan, Metal, etc., then return an empty + // list. This can occur if the server is matinfo rather than an actual Filament session. + if (mServer->mBackend == backend::Backend::DEFAULT) { + mg_printf(conn, "}"); + return true; + } + + int index = 0; + for (auto const& pair: mServer->mMaterialRecords) { + auto const& record = pair.second; + ChunkContainer package(record.package, record.packageSize); + if (!package.parse()) { + return error(__LINE__, uri); + } + JsonWriter writer; + if (!writer.writeActiveInfo(package, mServer->mBackend, record.activeVariants)) { + return error(__LINE__, uri); + } + bool const last = (++index) == mServer->mMaterialRecords.size(); + mg_printf(conn, "\"%8.8x\": %s %s", pair.first, writer.getJsonString(), + last ? "" : ","); + } + mg_printf(conn, "}"); + return true; + } + + if (uri == "/api/matids") { + mg_printf(conn, kSuccessHeader.data(), "application/json"); + mg_printf(conn, "["); + int index = 0; + for (auto const& record: mServer->mMaterialRecords) { + bool const last = (++index) == mServer->mMaterialRecords.size(); + mg_printf(conn, "\"%8.8x\" %s", record.first, last ? "" : ","); + } + mg_printf(conn, "]"); + return true; + } + + if (uri == "/api/materials") { + mg_printf(conn, kSuccessHeader.data(), "application/json"); + mg_printf(conn, "["); + int index = 0; + for (auto const& record: mServer->mMaterialRecords) { + bool const last = (++index) == mServer->mMaterialRecords.size(); + + ChunkContainer package(record.second.package, record.second.packageSize); + if (!package.parse()) { + return error(__LINE__, uri); + } + + JsonWriter writer; + if (!writer.writeMaterialInfo(package)) { + return error(__LINE__, uri); + } + + mg_printf(conn, "{ \"matid\": \"%8.8x\", %s } %s", record.first, writer.getJsonString(), + last ? "" : ","); + } + mg_printf(conn, "]"); + return true; + } + + if (uri == "/api/material") { + MaterialRecord const* result = getMaterialRecord(request); + if (!result) { + return error(__LINE__, uri); + } + + ChunkContainer package(result->package, result->packageSize); + if (!package.parse()) { + return error(__LINE__, uri); + } + + JsonWriter writer; + if (!writer.writeMaterialInfo(package)) { + return error(__LINE__, uri); + } + mg_printf(conn, kSuccessHeader.data(), "application/json"); + mg_printf(conn, "{ %s }", writer.getJsonString()); + return true; + } + + if (uri == "/api/shader") { + return handleGetApiShader(conn, request); + } + + return error(__LINE__, uri); +} + +} // filament::matdbg diff --git a/libs/matdbg/src/ApiHandler.h b/libs/matdbg/src/ApiHandler.h new file mode 100644 index 0000000000..f76169e762 --- /dev/null +++ b/libs/matdbg/src/ApiHandler.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MATDBG_APIHANDLER_H +#define MATDBG_APIHANDLER_H + +#include + +namespace filament::matdbg { + +class DebugServer; +struct MaterialRecord; + +// Handles the following REST requests, where {id} is an 8-digit hex string. +// +// GET /api/matids +// GET /api/materials +// GET /api/material?matid={id} +// GET /api/shader?matid={id}&type=[glsl|spirv]&[glindex|vkindex|metalindex]={index} +// GET /api/active +// POST /api/edit +// +class ApiHandler : public CivetHandler { +public: + explicit ApiHandler(DebugServer* server) + : mServer(server) {} + + bool handleGet(CivetServer* server, struct mg_connection* conn); + + bool handlePost(CivetServer* server, struct mg_connection* conn); + + ~ApiHandler() = default; + +private: + bool handleGetApiShader(struct mg_connection* conn, struct mg_request_info const* request); + MaterialRecord const* getMaterialRecord(struct mg_request_info const* request); + DebugServer* mServer; +}; + +} // filament::matdbg + +#endif // MATDBG_APIHANDLER_H diff --git a/libs/matdbg/src/DebugServer.cpp b/libs/matdbg/src/DebugServer.cpp index b52375197e..62de97fb29 100644 --- a/libs/matdbg/src/DebugServer.cpp +++ b/libs/matdbg/src/DebugServer.cpp @@ -16,6 +16,8 @@ #include +#include "ApiHandler.h" + #include #include @@ -52,8 +54,7 @@ using utils::FixedCapacityVector; #include "matdbg_resources.h" #endif -namespace filament { -namespace matdbg { +namespace filament::matdbg { using namespace utils; using namespace filament::backend; @@ -61,31 +62,24 @@ using namespace filament::backend; using filaflat::ChunkContainer; using filamat::ChunkType; -static const std::string_view kSuccessHeader = +std::string_view const DebugServer::kSuccessHeader = "HTTP/1.1 200 OK\r\nContent-Type: %s\r\n" "Connection: close\r\n\r\n"; -static const std::string_view kErrorHeader = +std::string_view const DebugServer::kErrorHeader = "HTTP/1.1 404 Not Found\r\nContent-Type: %s\r\n" "Connection: close\r\n\r\n"; -static void spirvToAsm(struct mg_connection *conn, const uint32_t* 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()); -} +namespace { -static void spirvToGlsl(ShaderModel shaderModel, struct mg_connection *conn, - const uint32_t* 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()); -} +}// namespace class FileRequestHandler : public CivetHandler { public: FileRequestHandler(DebugServer* server) : mServer(server) {} bool handleGet(CivetServer *server, struct mg_connection *conn) { + auto const& kSuccessHeader = DebugServer::kSuccessHeader; + const struct mg_request_info* request = mg_get_request_info(conn); std::string uri(request->request_uri); if (uri == "/" || uri == "/index.html") { @@ -122,256 +116,6 @@ private: DebugServer* mServer; }; -// Handles the following REST requests, where {id} is an 8-digit hex string. -// -// GET /api/matids -// GET /api/materials -// GET /api/material?matid={id} -// GET /api/shader?matid={id}&type=[glsl|spirv]&[glindex|vkindex|metalindex]={index} -// GET /api/active -// -class RestRequestHandler : public CivetHandler { -public: - RestRequestHandler(DebugServer* server) : mServer(server) {} - - bool handleGet(CivetServer *server, struct mg_connection *conn) { - const struct mg_request_info* request = mg_get_request_info(conn); - std::string uri(request->local_uri); - - const auto error = [request](int line) { - slog.e << "DebugServer: 404 at line " << line << ": " << request->local_uri - << io::endl; - return false; - }; - - const auto softError = [request, conn](const char* msg) { - slog.e << "DebugServer: " << msg << ": " << request->query_string << io::endl; - mg_printf(conn, kErrorHeader.data(), "application/txt"); - mg_write(conn, msg, strlen(msg)); - return true; - }; - - if (uri == "/api/active") { - mServer->updateActiveVariants(); - mg_printf(conn, kSuccessHeader.data(), "application/json"); - mg_printf(conn, "{"); - - // If the backend has not been resolved to Vulkan, Metal, etc., then return an empty - // list. This can occur if the server is matinfo rather than an actual Filament session. - if (mServer->mBackend == backend::Backend::DEFAULT) { - mg_printf(conn, "}"); - return true; - } - - int index = 0; - for (const auto& pair : mServer->mMaterialRecords) { - const auto& record = pair.second; - ChunkContainer package(record.package, record.packageSize); - if (!package.parse()) { - return error(__LINE__); - } - JsonWriter writer; - if (!writer.writeActiveInfo(package, mServer->mBackend, record.activeVariants)) { - return error(__LINE__); - } - const bool last = (++index) == mServer->mMaterialRecords.size(); - mg_printf(conn, "\"%8.8x\": %s %s", pair.first, writer.getJsonString(), - last ? "" : ","); - } - mg_printf(conn, "}"); - return true; - } - - if (uri == "/api/matids") { - mg_printf(conn, kSuccessHeader.data(), "application/json"); - mg_printf(conn, "["); - int index = 0; - for (const auto& record : mServer->mMaterialRecords) { - const bool last = (++index) == mServer->mMaterialRecords.size(); - mg_printf(conn, "\"%8.8x\" %s", record.first, last ? "" : ","); - } - mg_printf(conn, "]"); - return true; - } - - if (uri == "/api/materials") { - mg_printf(conn, kSuccessHeader.data(), "application/json"); - mg_printf(conn, "["); - int index = 0; - for (const auto& record : mServer->mMaterialRecords) { - const bool last = (++index) == mServer->mMaterialRecords.size(); - - ChunkContainer package(record.second.package, record.second.packageSize); - if (!package.parse()) { - return error(__LINE__); - } - - JsonWriter writer; - if (!writer.writeMaterialInfo(package)) { - return error(__LINE__); - } - - mg_printf(conn, "{ \"matid\": \"%8.8x\", %s } %s", record.first, - writer.getJsonString(), last ? "" : ","); - } - mg_printf(conn, "]"); - return true; - } - - if (!request->query_string) { - return error(__LINE__); - } - - const size_t qlength = strlen(request->query_string); - char matid[9] = {}; - if (mg_get_var(request->query_string, qlength, "matid", matid, sizeof(matid)) < 0) { - return error(__LINE__); - } - const uint32_t id = strtoul(matid, nullptr, 16); - const DebugServer::MaterialRecord* result = mServer->getRecord(id); - if (result == nullptr) { - return error(__LINE__); - } - - ChunkContainer package(result->package, result->packageSize); - if (!package.parse()) { - return error(__LINE__); - } - - if (uri == "/api/material") { - JsonWriter writer; - if (!writer.writeMaterialInfo(package)) { - return error(__LINE__); - } - mg_printf(conn, kSuccessHeader.data(), "application/json"); - mg_printf(conn, "{ %s }", writer.getJsonString()); - return true; - } - - const std::string_view glsl("glsl"); - const std::string_view msl("msl"); - const std::string_view spirv("spirv"); - - char type[6] = {}; - if (mg_get_var(request->query_string, qlength, "type", type, sizeof(type)) < 0) { - return error(__LINE__); - } - - std::string_view const language(type, strlen(type)); - - char glindex[4] = {}; - char vkindex[4] = {}; - char metalindex[4] = {}; - mg_get_var(request->query_string, qlength, "glindex", glindex, sizeof(glindex)); - mg_get_var(request->query_string, qlength, "vkindex", vkindex, sizeof(vkindex)); - mg_get_var(request->query_string, qlength, "metalindex", metalindex, sizeof(metalindex)); - - if (!glindex[0] && !vkindex[0] && !metalindex[0]) { - return error(__LINE__); - } - - if (uri != "/api/shader") { - return error(__LINE__); - } - - if (glindex[0]) { - if (language != glsl) { - return softError("Only GLSL is supported."); - } - - FixedCapacityVector info(getShaderCount(package, ChunkType::MaterialGlsl)); - if (!getShaderInfo(package, info.data(), ChunkType::MaterialGlsl)) { - return error(__LINE__); - } - - const int shaderIndex = std::stoi(glindex); - if (shaderIndex >= info.size()) { - return error(__LINE__); - } - - ShaderExtractor extractor(ShaderLanguage::ESSL3, result->package, result->packageSize); - if (!extractor.parse()) { - return error(__LINE__); - } - - const auto& item = info[shaderIndex]; - filaflat::ShaderContent content; - extractor.getShader(item.shaderModel, item.variant, item.pipelineStage, content); - - mg_printf(conn, kSuccessHeader.data(), "application/txt"); - mg_write(conn, content.data(), content.size() - 1); - return true; - } - - if (vkindex[0]) { - ShaderExtractor extractor(ShaderLanguage::SPIRV, result->package, result->packageSize); - if (!extractor.parse()) { - return error(__LINE__); - } - - filaflat::ShaderContent content; - FixedCapacityVector info(getShaderCount(package, ChunkType::MaterialSpirv)); - if (!getShaderInfo(package, info.data(), ChunkType::MaterialSpirv)) { - return error(__LINE__); - } - - const int shaderIndex = std::stoi(vkindex); - if (shaderIndex >= info.size()) { - return error(__LINE__); - } - - const auto& item = info[shaderIndex]; - extractor.getShader(item.shaderModel, item.variant, item.pipelineStage, content); - - if (language == spirv) { - spirvToAsm(conn, (const uint32_t*) content.data(), content.size()); - return true; - } - - if (language == glsl) { - spirvToGlsl(item.shaderModel, conn, (const uint32_t*) content.data(), content.size()); - return true; - } - - return softError("Only SPIRV is supported."); - } - - if (metalindex[0]) { - ShaderExtractor extractor(ShaderLanguage::MSL, result->package, result->packageSize); - if (!extractor.parse()) { - return error(__LINE__); - } - - filaflat::ShaderContent content; - FixedCapacityVector info(getShaderCount(package, ChunkType::MaterialMetal)); - if (!getShaderInfo(package, info.data(), ChunkType::MaterialMetal)) { - return error(__LINE__); - } - - const int shaderIndex = std::stoi(metalindex); - if (shaderIndex >= info.size()) { - return error(__LINE__); - } - - const auto& item = info[shaderIndex]; - extractor.getShader(item.shaderModel, item.variant, item.pipelineStage, content); - - if (language == msl) { - mg_printf(conn, kSuccessHeader.data(), "application/txt"); - mg_write(conn, content.data(), content.size() - 1); - return true; - } - - return softError("Only MSL is supported."); - } - - return error(__LINE__); - } - -private: - DebugServer* mServer; -}; - class WebSocketHandler : public CivetWebSocketHandler { public: WebSocketHandler(DebugServer* server) : mServer(server) {} @@ -386,81 +130,6 @@ public: bool handleData(CivetServer *server, struct mg_connection *conn, int bits, char *data, size_t size) override { - - // First check if this chunk is a continuation of a partial existing message. - if (mServer->mChunkedMessageRemaining > 0) { - const CString chunk(data, size); - const size_t pos = mServer->mChunkedMessage.size(); - - // Append the partial existing message. - mServer->mChunkedMessage = mServer->mChunkedMessage.insert(pos, chunk); - - // Determine number of outstanding bytes. - if (size > mServer->mChunkedMessageRemaining) { - mServer->mChunkedMessageRemaining = 0; - } else { - mServer->mChunkedMessageRemaining -= size; - } - - // Return early and wait for more chunks if some bytes are still outstanding. - if (mServer->mChunkedMessageRemaining > 0) { - return true; - } - - data = mServer->mChunkedMessage.data(); - size = mServer->mChunkedMessage.size(); - - // Ignore the handshake message that occurs after startup. - } else if (size < 8) { - return true; - } - - mServer->mChunkedMessageRemaining = 0; - - // Every WebSocket message is prefixed with a command name followed by a space. - // - // For now we simply use istringstream for parsing, so command arguments are delimited - // with space characters. - // - // The "API index" matches the values of filament::backend::Backend (zero is invalid). - // - // The "shader index" is a zero-based index into the list of variants using the order that - // they appear in the package, where each API (GL / VK / Metal) has its own list. - // - // Commands: - // - // EDIT [material id] [api index] [shader index] [shader length] [shader source....] - // - - const static std::string_view kEditCmd = "EDIT "; - const static size_t kEditCmdLength = kEditCmd.size(); - - if (0 == strncmp(data, kEditCmd.data(), kEditCmdLength)) { - std::string command(data + kEditCmdLength, size - kEditCmdLength); - std::istringstream str(command); - uint32_t matid; - int api; - int shaderIndex; - int shaderLength; - str >> std::hex >> matid >> std::dec >> api >> shaderIndex >> shaderLength; - const char* source = data + kEditCmdLength + str.tellg() + 1; - const size_t remaining = size - kEditCmdLength - str.tellg(); - - // Return early and wait for more chunks if some bytes are still outstanding. - if (remaining < shaderLength + 1) { - mServer->mChunkedMessage = CString(data, size); - mServer->mChunkedMessageRemaining = shaderLength + 1 - remaining; - return true; - } - - mServer->handleEditCommand(matid, backend::Backend(api), shaderIndex, source, - shaderLength); - return true; - } - - const std::string firstFewChars(data, std::min(size, size_t(8))); - slog.e << "Bad WebSocket message. First few characters: " - << "[" << firstFewChars << "]" << io::endl; return false; } @@ -470,7 +139,7 @@ public: } // Notify all JavaScript clients that a new material package has been loaded. - void addMaterial(const DebugServer::MaterialRecord& material) { + void addMaterial(MaterialRecord const& material) { for (auto connection : mConnections) { char matid[9] = {}; snprintf(matid, sizeof(matid), "%8.8x", material.key); @@ -512,10 +181,10 @@ DebugServer::DebugServer(Backend backend, int port) : mBackend(backend) { } mFileHandler = new FileRequestHandler(this); - mRestHandler = new RestRequestHandler(this); + mApiHandler = new ApiHandler(this); mWebSocketHandler = new WebSocketHandler(this); - mServer->addHandler("/api", mRestHandler); + mServer->addHandler("/api", mApiHandler); mServer->addHandler("", mFileHandler); mServer->addWebSocketHandler("", mWebSocketHandler); @@ -529,7 +198,7 @@ DebugServer::~DebugServer() { delete [] pair.second.package; } delete mFileHandler; - delete mRestHandler; + delete mApiHandler; delete mServer; } @@ -559,7 +228,7 @@ void DebugServer::removeMaterial(MaterialKey key) { mMaterialRecords.erase(key); } -const DebugServer::MaterialRecord* DebugServer::getRecord(const MaterialKey& key) const { +const MaterialRecord* DebugServer::getRecord(const MaterialKey& key) const { const auto& iter = mMaterialRecords.find(key); return iter == mMaterialRecords.end() ? nullptr : &iter->second; } @@ -649,5 +318,4 @@ bool DebugServer::handleEditCommand(const MaterialKey& key, backend::Backend api return true; } -} // namespace matdbg -} // namespace filament +} // namespace filament::matdbg diff --git a/libs/matdbg/web/script.js b/libs/matdbg/web/script.js index 5928858108..5fe11b7e5c 100644 --- a/libs/matdbg/web/script.js +++ b/libs/matdbg/web/script.js @@ -79,8 +79,9 @@ function rebuildMaterial() { } const editedText = shader[gCurrentLanguage]; - const byteCount = new Blob([editedText]).size; - gSocket.send(`EDIT ${gCurrentShader.matid} ${api} ${index} ${byteCount} ${editedText}`); + const req = new XMLHttpRequest(); + req.open('POST', '/api/edit'); + req.send(`${gCurrentShader.matid} ${api} ${index} ${editedText}`); } document.querySelector("body").addEventListener("click", (evt) => {