feat(web): unify worker wasm calls behind json abi
This commit is contained in:
@@ -129,7 +129,7 @@ target_link_options(web_engine PRIVATE
|
||||
"-sINITIAL_MEMORY=67108864"
|
||||
"-sMAXIMUM_MEMORY=2147483648"
|
||||
"-sSTACK_SIZE=8388608"
|
||||
"-sEXPORTED_FUNCTIONS=['_malloc','_free','_web_engine_create','_web_engine_destroy','_web_engine_get_memory_stats','_web_engine_get_live_handles','_web_engine_get_allocated_bytes','_web_engine_open_blend','_web_engine_apply_command','_web_engine_append_library_object','_web_engine_undo','_web_engine_redo','_web_engine_get_scene_snapshot','_web_engine_get_scene_metadata','_web_engine_get_scene_geometry','_web_engine_get_scene_delta','_web_engine_get_packed_asset','_web_engine_evaluate_depsgraph','_web_engine_save_blend','_web_engine_free_buffer','_web_engine_last_error_code','_web_engine_last_error_message','_web_engine_decimate_apply']"
|
||||
"-sEXPORTED_FUNCTIONS=['_malloc','_free','_web_engine_create','_web_engine_destroy','_web_engine_get_memory_stats','_web_engine_get_live_handles','_web_engine_get_allocated_bytes','_web_engine_open_blend','_web_engine_call_json','_web_engine_append_library_object','_web_engine_get_scene_metadata','_web_engine_get_scene_geometry','_web_engine_get_packed_asset','_web_engine_save_blend','_web_engine_free_buffer','_web_engine_last_error_code','_web_engine_last_error_message','_web_engine_decimate_apply']"
|
||||
"-sEXPORTED_RUNTIME_METHODS=['ccall','cwrap','UTF8ToString']"
|
||||
)
|
||||
if(WEB_ENGINE_ENABLE_UNSAFE_DEPSGRAPH)
|
||||
|
||||
@@ -1048,6 +1048,26 @@ void set_error(const int code, const char *message)
|
||||
last_error_message = message != nullptr ? message : "unknown web engine error";
|
||||
}
|
||||
|
||||
int write_json_result(const json &value, const uint8_t **result_data, uint32_t *result_length)
|
||||
{
|
||||
if (result_data == nullptr || result_length == nullptr) {
|
||||
set_error(WEB_ENGINE_INVALID_ARGUMENT, "unified JSON result output is null");
|
||||
return last_error_code;
|
||||
}
|
||||
const std::string serialized = value.dump();
|
||||
uint8_t *copy = static_cast<uint8_t *>(std::malloc(serialized.size()));
|
||||
if (copy == nullptr && !serialized.empty()) {
|
||||
*result_data = nullptr;
|
||||
*result_length = 0;
|
||||
set_error(WEB_ENGINE_BLEND_WRITE_FAILED, "unable to allocate unified JSON result");
|
||||
return last_error_code;
|
||||
}
|
||||
if (!serialized.empty()) std::memcpy(copy, serialized.data(), serialized.size());
|
||||
*result_data = copy;
|
||||
*result_length = static_cast<uint32_t>(serialized.size());
|
||||
return WEB_ENGINE_OK;
|
||||
}
|
||||
|
||||
EngineState *find_engine(const int handle)
|
||||
{
|
||||
const auto found = engines.find(handle);
|
||||
@@ -2355,6 +2375,90 @@ EMSCRIPTEN_KEEPALIVE int web_engine_apply_command(const int handle,
|
||||
}
|
||||
}
|
||||
|
||||
/* Stable JSON facade for Worker-to-WASM calls. Binary-only transfers retain their
|
||||
* dedicated ABI, while stateful commands and JSON reports use this entrypoint. */
|
||||
EMSCRIPTEN_KEEPALIVE int web_engine_call_json(const int handle,
|
||||
const uint8_t *data,
|
||||
const uint32_t length,
|
||||
const uint8_t **result_data,
|
||||
uint32_t *result_length)
|
||||
{
|
||||
if (result_data != nullptr) *result_data = nullptr;
|
||||
if (result_length != nullptr) *result_length = 0;
|
||||
if (data == nullptr || length == 0) {
|
||||
set_error(WEB_ENGINE_INVALID_ARGUMENT, "unified JSON request is empty");
|
||||
return write_json_result({{"ok", false}, {"code", last_error_code}, {"error", last_error_message}}, result_data, result_length);
|
||||
}
|
||||
try {
|
||||
const json request = json::parse(reinterpret_cast<const char *>(data),
|
||||
reinterpret_cast<const char *>(data) + length);
|
||||
if (!request.is_object() || !request.contains("cmd") || !request["cmd"].is_string()) {
|
||||
set_error(WEB_ENGINE_INVALID_ARGUMENT, "unified JSON request requires string cmd");
|
||||
return write_json_result({{"ok", false}, {"code", last_error_code}, {"error", last_error_message}}, result_data, result_length);
|
||||
}
|
||||
const std::string cmd = request["cmd"].get<std::string>();
|
||||
const json params = request.value("params", json::object());
|
||||
if (!params.is_object()) {
|
||||
set_error(WEB_ENGINE_INVALID_ARGUMENT, "unified JSON params must be an object");
|
||||
return write_json_result({{"ok", false}, {"cmd", cmd}, {"code", last_error_code}, {"error", last_error_message}}, result_data, result_length);
|
||||
}
|
||||
json result;
|
||||
if (cmd == "applyCommand") {
|
||||
if (!params.contains("command") || !params["command"].is_object()) {
|
||||
set_error(WEB_ENGINE_INVALID_ARGUMENT, "applyCommand params.command must be an object");
|
||||
return write_json_result({{"ok", false}, {"cmd", cmd}, {"code", last_error_code}, {"error", last_error_message}}, result_data, result_length);
|
||||
}
|
||||
const std::string command = params["command"].dump();
|
||||
const int code = web_engine_apply_command(handle,
|
||||
reinterpret_cast<const uint8_t *>(command.data()),
|
||||
static_cast<uint32_t>(command.size()));
|
||||
if (code != WEB_ENGINE_OK) {
|
||||
return write_json_result({{"ok", false}, {"cmd", cmd}, {"code", last_error_code}, {"error", last_error_message}}, result_data, result_length);
|
||||
}
|
||||
result = {{"status", "applied"}};
|
||||
}
|
||||
else if (cmd == "snapshot" || cmd == "delta" || cmd == "memoryStats") {
|
||||
EngineState *engine = find_engine(handle);
|
||||
if (engine == nullptr) {
|
||||
return write_json_result({{"ok", false}, {"cmd", cmd}, {"code", last_error_code}, {"error", last_error_message}}, result_data, result_length);
|
||||
}
|
||||
if (cmd == "snapshot") result = json::parse(engine->scene_snapshot.empty() ? empty_scene_snapshot_json() : engine->scene_snapshot);
|
||||
else if (cmd == "delta") result = json::parse(engine->scene_delta.empty() ? "{\"schemaVersion\":1,\"baseRevision\":0,\"nextRevision\":0}" : engine->scene_delta);
|
||||
else {
|
||||
const WebEngineMemoryStats stats = web_engine_get_memory_stats();
|
||||
result = {{"liveHandles", stats.live_handles}, {"allocatedBytes", stats.allocated_bytes}};
|
||||
}
|
||||
}
|
||||
else if (cmd == "undo" || cmd == "redo") {
|
||||
const int code = cmd == "undo" ? web_engine_undo(handle) : web_engine_redo(handle);
|
||||
if (code != WEB_ENGINE_OK) {
|
||||
return write_json_result({{"ok", false}, {"cmd", cmd}, {"code", last_error_code}, {"error", last_error_message}}, result_data, result_length);
|
||||
}
|
||||
result = {{"status", cmd}};
|
||||
}
|
||||
else if (cmd == "depsgraph") {
|
||||
const uint8_t *report_data = nullptr;
|
||||
uint32_t report_length = 0;
|
||||
const int code = web_engine_evaluate_depsgraph(handle, &report_data, &report_length);
|
||||
if (code != WEB_ENGINE_OK) {
|
||||
return write_json_result({{"ok", false}, {"cmd", cmd}, {"code", last_error_code}, {"error", last_error_message}}, result_data, result_length);
|
||||
}
|
||||
result = json::parse(reinterpret_cast<const char *>(report_data),
|
||||
reinterpret_cast<const char *>(report_data) + report_length);
|
||||
}
|
||||
else {
|
||||
set_error(WEB_ENGINE_NOT_IMPLEMENTED, "unified JSON cmd is not implemented");
|
||||
return write_json_result({{"ok", false}, {"cmd", cmd}, {"code", last_error_code}, {"error", last_error_message}}, result_data, result_length);
|
||||
}
|
||||
set_error(WEB_ENGINE_OK, "ok");
|
||||
return write_json_result({{"ok", true}, {"cmd", cmd}, {"result", std::move(result)}}, result_data, result_length);
|
||||
}
|
||||
catch (const std::exception &exception) {
|
||||
set_error(WEB_ENGINE_INVALID_ARGUMENT, exception.what());
|
||||
return write_json_result({{"ok", false}, {"code", last_error_code}, {"error", last_error_message}}, result_data, result_length);
|
||||
}
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE int web_engine_append_library_object(const int handle,
|
||||
const uint8_t *source_data,
|
||||
const uint32_t source_length,
|
||||
|
||||
@@ -28,6 +28,11 @@ uint32_t web_engine_get_live_handles(void);
|
||||
uint32_t web_engine_get_allocated_bytes(void);
|
||||
int web_engine_open_blend(int handle, const uint8_t *data, uint32_t length);
|
||||
int web_engine_apply_command(int handle, const uint8_t *data, uint32_t length);
|
||||
int web_engine_call_json(int handle,
|
||||
const uint8_t *data,
|
||||
uint32_t length,
|
||||
const uint8_t **result_data,
|
||||
uint32_t *result_length);
|
||||
int web_engine_append_library_object(int handle,
|
||||
const uint8_t *source_data,
|
||||
uint32_t source_length,
|
||||
|
||||
Reference in New Issue
Block a user