impr: Run data processor in a worker task
This commit is contained in:
parent
7975edade4
commit
8422965d0b
@ -12,6 +12,7 @@
|
||||
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
#include <imgui.h>
|
||||
#include <hex/providers/provider_data.hpp>
|
||||
|
||||
namespace hex::prv {
|
||||
class Provider;
|
||||
@ -42,8 +43,9 @@ namespace hex::dp {
|
||||
m_overlay = overlay;
|
||||
}
|
||||
|
||||
virtual void drawNode() { }
|
||||
void draw();
|
||||
virtual void process() = 0;
|
||||
virtual void reset() { }
|
||||
|
||||
virtual void store(nlohmann::json &j) const { hex::unused(j); }
|
||||
virtual void load(const nlohmann::json &j) { hex::unused(j); }
|
||||
@ -80,6 +82,11 @@ namespace hex::dp {
|
||||
void setIntegerOnOutput(u32 index, i128 integer);
|
||||
void setFloatOnOutput(u32 index, double floatingPoint);
|
||||
|
||||
static void interrupt();
|
||||
|
||||
protected:
|
||||
virtual void drawNode() { }
|
||||
|
||||
private:
|
||||
int m_id;
|
||||
UnlocalizedString m_unlocalizedTitle, m_unlocalizedName;
|
||||
@ -90,45 +97,16 @@ namespace hex::dp {
|
||||
|
||||
static int s_idCounter;
|
||||
|
||||
Attribute& getAttribute(u32 index) {
|
||||
if (index >= this->getAttributes().size())
|
||||
throw std::runtime_error("Attribute index out of bounds!");
|
||||
|
||||
return this->getAttributes()[index];
|
||||
}
|
||||
|
||||
Attribute *getConnectedInputAttribute(u32 index) {
|
||||
const auto &connectedAttribute = this->getAttribute(index).getConnectedAttributes();
|
||||
|
||||
if (connectedAttribute.empty())
|
||||
return nullptr;
|
||||
|
||||
return connectedAttribute.begin()->second;
|
||||
}
|
||||
|
||||
void markInputProcessed(u32 index) {
|
||||
const auto &[iter, inserted] = m_processedInputs.insert(index);
|
||||
if (!inserted)
|
||||
throwNodeError("Recursion detected!");
|
||||
}
|
||||
|
||||
void unmarkInputProcessed(u32 index) {
|
||||
m_processedInputs.erase(index);
|
||||
}
|
||||
Attribute& getAttribute(u32 index);
|
||||
Attribute *getConnectedInputAttribute(u32 index);
|
||||
void markInputProcessed(u32 index);
|
||||
void unmarkInputProcessed(u32 index);
|
||||
|
||||
protected:
|
||||
[[noreturn]] void throwNodeError(const std::string &message) {
|
||||
throw NodeError { this, message };
|
||||
}
|
||||
[[noreturn]] void throwNodeError(const std::string &message);
|
||||
|
||||
void setOverlayData(u64 address, const std::vector<u8> &data);
|
||||
|
||||
void setAttributes(std::vector<Attribute> attributes) {
|
||||
m_attributes = std::move(attributes);
|
||||
|
||||
for (auto &attr : m_attributes)
|
||||
attr.setParentNode(this);
|
||||
}
|
||||
void setAttributes(std::vector<Attribute> attributes);
|
||||
};
|
||||
|
||||
}
|
@ -8,12 +8,18 @@
|
||||
namespace hex::dp {
|
||||
|
||||
int Node::s_idCounter = 1;
|
||||
static std::atomic_bool s_interrupted;
|
||||
|
||||
Node::Node(UnlocalizedString unlocalizedTitle, std::vector<Attribute> attributes) : m_id(s_idCounter++), m_unlocalizedTitle(std::move(unlocalizedTitle)), m_attributes(std::move(attributes)) {
|
||||
for (auto &attr : m_attributes)
|
||||
attr.setParentNode(this);
|
||||
}
|
||||
|
||||
void Node::draw() {
|
||||
this->drawNode();
|
||||
}
|
||||
|
||||
|
||||
const std::vector<u8>& Node::getBufferOnInput(u32 index) {
|
||||
auto attribute = this->getConnectedInputAttribute(index);
|
||||
|
||||
@ -148,9 +154,56 @@ namespace hex::dp {
|
||||
m_overlay->getData() = data;
|
||||
}
|
||||
|
||||
[[noreturn]] void Node::throwNodeError(const std::string &message) {
|
||||
throw NodeError { this, message };
|
||||
}
|
||||
|
||||
void Node::setAttributes(std::vector<Attribute> attributes) {
|
||||
m_attributes = std::move(attributes);
|
||||
|
||||
for (auto &attr : m_attributes)
|
||||
attr.setParentNode(this);
|
||||
}
|
||||
|
||||
void Node::setIdCounter(int id) {
|
||||
if (id > s_idCounter)
|
||||
s_idCounter = id;
|
||||
}
|
||||
|
||||
Attribute& Node::getAttribute(u32 index) {
|
||||
if (index >= this->getAttributes().size())
|
||||
throw std::runtime_error("Attribute index out of bounds!");
|
||||
|
||||
return this->getAttributes()[index];
|
||||
}
|
||||
|
||||
Attribute *Node::getConnectedInputAttribute(u32 index) {
|
||||
const auto &connectedAttribute = this->getAttribute(index).getConnectedAttributes();
|
||||
|
||||
if (connectedAttribute.empty())
|
||||
return nullptr;
|
||||
|
||||
return connectedAttribute.begin()->second;
|
||||
}
|
||||
|
||||
void Node::markInputProcessed(u32 index) {
|
||||
const auto &[iter, inserted] = m_processedInputs.insert(index);
|
||||
if (!inserted)
|
||||
throwNodeError("Recursion detected!");
|
||||
|
||||
if (s_interrupted) {
|
||||
s_interrupted = false;
|
||||
throwNodeError("Execution interrupted!");
|
||||
}
|
||||
}
|
||||
|
||||
void Node::unmarkInputProcessed(u32 index) {
|
||||
m_processedInputs.erase(index);
|
||||
}
|
||||
|
||||
void Node::interrupt() {
|
||||
s_interrupted = true;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -8,6 +8,7 @@
|
||||
#include <imnodes_internal.h>
|
||||
|
||||
#include <string>
|
||||
#include <hex/api/task_manager.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
@ -17,6 +18,8 @@ namespace hex::plugin::builtin {
|
||||
struct Workspace {
|
||||
Workspace() = default;
|
||||
|
||||
|
||||
|
||||
std::unique_ptr<ImNodesContext, void(*)(ImNodesContext*)> context = { []{
|
||||
ImNodesContext *ctx = ImNodes::CreateContext();
|
||||
ctx->Style = ImNodes::GetStyle();
|
||||
@ -47,7 +50,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
static void eraseLink(Workspace &workspace, int id);
|
||||
static void eraseNodes(Workspace &workspace, const std::vector<int> &ids);
|
||||
static void processNodes(Workspace &workspace);
|
||||
void processNodes(Workspace &workspace);
|
||||
|
||||
void reloadCustomNodes();
|
||||
void updateNodePositions();
|
||||
@ -74,6 +77,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
PerProvider<Workspace> m_mainWorkspace;
|
||||
PerProvider<std::vector<Workspace*>> m_workspaceStack;
|
||||
TaskHolder m_evaluationTask;
|
||||
};
|
||||
|
||||
}
|
@ -138,6 +138,10 @@ namespace hex::plugin::builtin {
|
||||
this->setIntegerOnOutput(4, m_value);
|
||||
}
|
||||
|
||||
void reset() override {
|
||||
m_started = false;
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_started = false;
|
||||
i128 m_value = 0;
|
||||
|
@ -28,10 +28,7 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
void process() override {
|
||||
m_value.reset();
|
||||
const auto &input = this->getIntegerOnInput(0);
|
||||
|
||||
m_value = input;
|
||||
m_value = this->getIntegerOnInput(0);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -390,10 +390,6 @@ namespace hex::plugin::builtin {
|
||||
m_updateNodePositions = true;
|
||||
});
|
||||
|
||||
EventDataChanged::subscribe(this, [this](prv::Provider *provider) {
|
||||
ViewDataProcessor::processNodes(*m_workspaceStack.get(provider).back());
|
||||
});
|
||||
|
||||
/* Import Nodes */
|
||||
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.import", "hex.builtin.menu.file.import.data_processor" }, ICON_VS_CHIP, 4050, Shortcut::None, [this]{
|
||||
fs::openFileBrowser(fs::DialogMode::Open, { {"hex.builtin.view.data_processor.name"_lang, "hexnode" } },
|
||||
@ -532,36 +528,49 @@ namespace hex::plugin::builtin {
|
||||
// Reset any potential node errors
|
||||
workspace.currNodeError.reset();
|
||||
|
||||
// Process all nodes in the workspace
|
||||
try {
|
||||
for (auto &endNode : workspace.endNodes) {
|
||||
// Reset the output data of the end node
|
||||
endNode->resetOutputData();
|
||||
m_evaluationTask = TaskManager::createTask("Evaluating Nodes...", 0, [this, workspace = &workspace](Task& task) {
|
||||
task.setInterruptCallback([]{
|
||||
dp::Node::interrupt();
|
||||
});
|
||||
do {
|
||||
|
||||
// Reset processed inputs of all nodes
|
||||
for (auto &node : workspace.nodes)
|
||||
node->resetProcessedInputs();
|
||||
// Process all nodes in the workspace
|
||||
try {
|
||||
for (auto &endNode : workspace->endNodes) {
|
||||
// Reset the output data of the end node
|
||||
endNode->resetOutputData();
|
||||
|
||||
// Process the end node
|
||||
endNode->process();
|
||||
}
|
||||
} catch (const dp::Node::NodeError &e) {
|
||||
// Handle user errors
|
||||
// Reset processed inputs of all nodes
|
||||
for (auto &node : workspace->nodes) {
|
||||
node->reset();
|
||||
node->resetProcessedInputs();
|
||||
}
|
||||
|
||||
// Add the node error to the current workspace, so it can be displayed
|
||||
workspace.currNodeError = e;
|
||||
// Process the end node
|
||||
endNode->process();
|
||||
}
|
||||
} catch (const dp::Node::NodeError &e) {
|
||||
// Handle user errors
|
||||
|
||||
// Add the node error to the current workspace, so it can be displayed
|
||||
workspace->currNodeError = e;
|
||||
|
||||
// Delete all overlays
|
||||
for (auto overlay : workspace->dataOverlays)
|
||||
ImHexApi::Provider::get()->deleteOverlay(overlay);
|
||||
workspace->dataOverlays.clear();
|
||||
} catch (const std::runtime_error &e) {
|
||||
// Handle internal errors
|
||||
log::fatal("Data processor node implementation bug! {}", e.what());
|
||||
} catch (const std::exception &e) {
|
||||
// Handle other fatal errors
|
||||
log::fatal("Unhandled exception thrown in data processor node! {}", e.what());
|
||||
}
|
||||
|
||||
task.update();
|
||||
} while (m_continuousEvaluation);
|
||||
});
|
||||
|
||||
// Delete all overlays
|
||||
for (auto overlay : workspace.dataOverlays)
|
||||
ImHexApi::Provider::get()->deleteOverlay(overlay);
|
||||
workspace.dataOverlays.clear();
|
||||
} catch (const std::runtime_error &e) {
|
||||
// Handle internal errors
|
||||
log::fatal("Data processor node implementation bug! {}", e.what());
|
||||
} catch (const std::exception &e) {
|
||||
// Handle other fatal errors
|
||||
log::fatal("Unhandled exception thrown in data processor node! {}", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void ViewDataProcessor::reloadCustomNodes() {
|
||||
@ -774,7 +783,9 @@ namespace hex::plugin::builtin {
|
||||
|
||||
// Draw the node's body
|
||||
ImGui::PopStyleVar();
|
||||
node.drawNode();
|
||||
if (!m_evaluationTask.isRunning()) {
|
||||
node.draw();
|
||||
}
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(1.0F, 1.0F));
|
||||
|
||||
// Draw all attributes of the node
|
||||
@ -851,6 +862,8 @@ namespace hex::plugin::builtin {
|
||||
void ViewDataProcessor::drawContent() {
|
||||
auto &workspace = *m_workspaceStack->back();
|
||||
|
||||
ImGui::BeginDisabled(m_evaluationTask.isRunning());
|
||||
|
||||
bool popWorkspace = false;
|
||||
// Set the ImNodes context to the current workspace context
|
||||
ImNodes::SetCurrentContext(workspace.context.get());
|
||||
@ -873,6 +886,9 @@ namespace hex::plugin::builtin {
|
||||
if (ImGui::BeginChild("##node_editor", ImGui::GetContentRegionAvail() - ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 1.3F))) {
|
||||
ImNodes::BeginNodeEditor();
|
||||
|
||||
if (m_evaluationTask.isRunning())
|
||||
ImNodes::GetCurrentContext()->MousePos = { FLT_MAX, FLT_MAX };
|
||||
|
||||
// Loop over all nodes that have been placed in the workspace
|
||||
bool stillUpdating = m_updateNodePositions;
|
||||
for (auto &node : workspace.nodes) {
|
||||
@ -933,10 +949,19 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::EndDisabled();
|
||||
|
||||
// Draw the control bar at the bottom
|
||||
{
|
||||
if (ImGuiExt::IconButton(ICON_VS_DEBUG_START, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen)) || m_continuousEvaluation)
|
||||
this->processNodes(workspace);
|
||||
if (!m_evaluationTask.isRunning()) {
|
||||
if (ImGuiExt::IconButton(ICON_VS_DEBUG_START, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen))) {
|
||||
this->processNodes(workspace);
|
||||
}
|
||||
} else {
|
||||
if (ImGuiExt::IconButton(ICON_VS_DEBUG_STOP, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed))) {
|
||||
m_evaluationTask.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
@ -944,7 +969,7 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
|
||||
// Erase links that have been distroyed
|
||||
// Erase links that have been destroyed
|
||||
{
|
||||
int linkId;
|
||||
if (ImNodes::IsLinkDestroyed(&linkId)) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user