错误处理

This commit is contained in:
2026-08-16 18:33:24 +08:00
parent 8612b5ff86
commit 389ff81d42
44 changed files with 536 additions and 261 deletions
+3 -1
View File
@@ -70,7 +70,9 @@ if (RENDERIVE_BUILD_TESTS)
target_link_libraries("${Renderive_Kernel_test_target}" PRIVATE Renderive_Kernel GTest::gtest_main)
renderive_stage_kernel_runtime("${Renderive_Kernel_test_target}")
add_test(NAME "${Renderive_Kernel_test_target}" COMMAND "${Renderive_Kernel_test_target}")
set_tests_properties("${Renderive_Kernel_test_target}" PROPERTIES LABELS "Renderive_Kernel")
set_tests_properties("${Renderive_Kernel_test_target}" PROPERTIES
LABELS "Renderive_Kernel"
ENVIRONMENT "RENDERIVE_ERROR_MODE=exception")
list(APPEND Renderive_Kernel_test_targets "${Renderive_Kernel_test_target}")
endforeach ()
add_custom_target(Renderive_Kernel_check
@@ -1,4 +1,5 @@
#pragma once
#include <renderive/error/Error_Policy.hpp>
#include <cstddef>
#include <memory>
#include <memory_resource>
@@ -61,6 +62,7 @@ Memory_Resource_Unique_Ptr<Value> make_memory_resource_unique(std::pmr::memory_r
return Memory_Resource_Unique_Ptr<Value>(value, Memory_Resource_Deleter<Value>{&resource});
} catch (...) {
resource.deallocate(storage, sizeof(Value), alignof(Value));
throw;
::renderive::error::unexpected(
"allocating from memory resource", std::current_exception());
}
}
@@ -1,4 +1,5 @@
#pragma once
#include <renderive/error/Error_Policy.hpp>
#include <algorithm>
#include <cstddef>
#include <memory_resource>
@@ -24,10 +25,10 @@ public:
}
void insert_child(std::size_t index, Multiway_Node& child) {
if (index > children_.size()) {
throw std::out_of_range("multiway node child index");
::renderive::error::unexpected<std::out_of_range>("multiway node child index");
}
if (&child == this || child.is_ancestor_of(*this)) {
throw std::invalid_argument("multiway node cycle");
::renderive::error::unexpected<std::invalid_argument>("multiway node cycle");
}
if (child.parent_ == this) {
const auto current = static_cast<std::size_t>(std::find(children_.begin(), children_.end(), &child) - children_.begin());
@@ -1,4 +1,5 @@
#pragma once
#include <renderive/error/Error_Policy.hpp>
#include "Concepts.hpp"
#include <stdexcept>
template <Property_Set Properties>
@@ -10,7 +11,7 @@ struct Range_Validator {
static_assert(!(Max < Min));
void operator()(const Value& value) const {
if (value < Min || Max < value) {
throw std::out_of_range("property value is outside the declared range");
::renderive::error::unexpected<std::out_of_range>("property value is outside the declared range");
}
}
};
+9 -8
View File
@@ -1,3 +1,4 @@
#include <renderive/error/Error_Policy.hpp>
#include "Capture.hpp"
#include <algorithm>
#include <limits>
@@ -14,7 +15,7 @@ Capture_Request_Result Capture_Controller::capture_frames(std::size_t count) {
Capture_Session_Id id = next_session_id_.load(std::memory_order_relaxed);
for (;;) {
if (id == 0 || id == std::numeric_limits<Capture_Session_Id>::max())
throw std::overflow_error("capture session id exhausted");
::renderive::error::unexpected<std::overflow_error>("capture session id exhausted");
if (next_session_id_.compare_exchange_weak(
id, id + 1, std::memory_order_relaxed,
std::memory_order_relaxed))
@@ -86,17 +87,17 @@ Capture_Controller_State Capture_Controller::state() const noexcept {
void Capture_Repository::begin_session(Capture_Session_Id session_id,
std::size_t requested_count) {
if (session_id == 0 || requested_count == 0)
throw std::logic_error("invalid capture session invariant");
::renderive::error::unexpected<std::logic_error>("invalid capture session invariant");
std::lock_guard lock(mutex_);
if (std::any_of(sessions_.begin(), sessions_.end(), [session_id](const auto& session) {
return session.session_id == session_id;
}))
throw std::logic_error("capture session already exists");
::renderive::error::unexpected<std::logic_error>("capture session already exists");
while (sessions_.size() >= retained_session_count) {
const auto completed = std::find_if(sessions_.begin(), sessions_.end(),
[](const Capture_Session& session) { return !session.active(); });
if (completed == sessions_.end())
throw std::logic_error("capture repository retention is occupied by active sessions");
::renderive::error::unexpected<std::logic_error>("capture repository retention is occupied by active sessions");
sessions_.erase(completed);
}
sessions_.push_back({session_id, requested_count, {}});
@@ -108,18 +109,18 @@ void Capture_Repository::publish(Capture_Session_Id session_id,
std::shared_ptr<const Render_Plan> render_plan,
Frame_Analysis analysis) {
if (!snapshot || !render_plan)
throw std::logic_error("captured frame snapshot or render plan is null");
::renderive::error::unexpected<std::logic_error>("captured frame snapshot or render plan is null");
if (analysis.frame_id != snapshot->frame_id ||
analysis.render_plan_version != snapshot->render_plan_version ||
render_plan->version != snapshot->render_plan_version)
throw std::logic_error("captured frame analysis does not match snapshot or render plan");
::renderive::error::unexpected<std::logic_error>("captured frame analysis does not match snapshot or render plan");
std::lock_guard lock(mutex_);
const auto iterator = std::find_if(sessions_.begin(), sessions_.end(),
[session_id](const auto& session) { return session.session_id == session_id; });
if (iterator == sessions_.end())
throw std::logic_error("capture session does not exist");
::renderive::error::unexpected<std::logic_error>("capture session does not exist");
if (!iterator->active())
throw std::logic_error("capture session is complete");
::renderive::error::unexpected<std::logic_error>("capture session is complete");
iterator->frames.push_back({std::move(snapshot), std::move(render_plan),
std::move(analysis)});
}
@@ -0,0 +1,65 @@
#include "Error_Policy.hpp"
#include <atomic>
#include <cstdio>
#include <cstdlib>
#include <cstring>
namespace renderive::error {
namespace {
Mode read_mode() noexcept {
const char* value = std::getenv("RENDERIVE_ERROR_MODE");
if (value && std::strcmp(value, "exception") == 0)
return Mode::exception;
return Mode::fast_fail;
}
std::atomic mode_value{read_mode()};
void print_exception(std::exception_ptr exception) noexcept {
if (!exception)
return;
try {
std::rethrow_exception(exception);
} catch (const std::exception& value) {
std::fprintf(stderr, ": %s", value.what());
} catch (...) {
std::fprintf(stderr, ": unknown exception");
}
}
}
Mode mode() noexcept {
return mode_value.load(std::memory_order_relaxed);
}
void set_mode(Mode value) noexcept {
mode_value.store(value, std::memory_order_relaxed);
}
[[noreturn]] void fast_fail(std::string_view message) noexcept {
std::fprintf(stderr, "Renderive unexpected error: %.*s\n", static_cast<int>(message.size()), message.data());
std::fflush(stderr);
std::abort();
}
[[noreturn]] void fast_fail(std::string_view context, std::exception_ptr exception) noexcept {
std::fprintf(stderr, "Renderive unexpected error");
if (!context.empty())
std::fprintf(stderr, " during %.*s", static_cast<int>(context.size()), context.data());
print_exception(exception);
std::fprintf(stderr, "\n");
std::fflush(stderr);
std::abort();
}
std::exception_ptr capture(
std::string_view context, std::exception_ptr exception) {
if (mode() == Mode::exception) {
if (exception)
return exception;
return std::make_exception_ptr(std::runtime_error(
context.empty() ? "Renderive unexpected error" : std::string(context)));
}
fast_fail(context, exception);
}
[[noreturn]] void unexpected(std::string_view context, std::exception_ptr exception) {
if (mode() == Mode::exception) {
if (exception)
std::rethrow_exception(exception);
throw std::runtime_error(context.empty() ? "Renderive unexpected error" : std::string(context));
}
fast_fail(context, exception);
}
}
@@ -0,0 +1,45 @@
#pragma once
#include <concepts>
#include <exception>
#include <stdexcept>
#include <string>
#include <string_view>
#include <utility>
namespace renderive::error {
enum class Mode {
fast_fail,
exception
};
[[nodiscard]] Mode mode() noexcept;
void set_mode(Mode mode) noexcept;
[[noreturn]] void fast_fail(std::string_view message) noexcept;
[[noreturn]] void fast_fail(std::string_view context, std::exception_ptr exception) noexcept;
[[nodiscard]] std::exception_ptr capture(
std::string_view context, std::exception_ptr exception);
[[noreturn]] void unexpected(std::string_view context, std::exception_ptr exception);
template <class Exception, class... Arguments>
requires std::constructible_from<Exception, Arguments...>
[[nodiscard]] std::exception_ptr capture(Arguments&&... arguments) {
if (mode() == Mode::exception)
return std::make_exception_ptr(
Exception(std::forward<Arguments>(arguments)...));
try {
Exception exception(std::forward<Arguments>(arguments)...);
fast_fail(exception.what());
} catch (...) {
fast_fail("Renderive unexpected error");
}
}
template <class Exception = std::runtime_error, class... Arguments>
requires std::constructible_from<Exception, Arguments...>
[[noreturn]] void unexpected(Arguments&&... arguments) {
if (mode() == Mode::exception)
throw Exception(std::forward<Arguments>(arguments)...);
try {
Exception exception(std::forward<Arguments>(arguments)...);
fast_fail(exception.what());
} catch (...) {
fast_fail("Renderive unexpected error");
}
}
}
@@ -1,4 +1,5 @@
#pragma once
#include <renderive/error/Error_Policy.hpp>
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Painter_Lease::Painter_Lease(Flow_Refresh_Strategy& strategy)
: strategy_(&strategy) {
@@ -147,7 +148,7 @@ template <class Scene_Frame, Mutex_Type Mutex, class Observer>
Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::Flow_Refresh_Strategy(std::pmr::memory_resource& memory_resource, Observer observer, std::size_t capacity)
: Frame_Control_Strategy_Base(), memory_resource_(&memory_resource), observer_(std::move(observer)) {
if (capacity == 0)
throw std::invalid_argument("flow refresh queue capacity must be greater than zero");
::renderive::error::unexpected<std::invalid_argument>("flow refresh queue capacity must be greater than zero");
frames_.set_capacity(static_cast<typename decltype(frames_)::size_type>(capacity));
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
@@ -1,4 +1,5 @@
#pragma once
#include <renderive/error/Error_Policy.hpp>
#include <algorithm>
#include <limits>
#include <stdexcept>
@@ -358,7 +359,7 @@ bool Low_Latency_Strategy<Scene_Frame, Mutex, Observer>::discard_stale_latest_da
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
double Low_Latency_Strategy<Scene_Frame, Mutex, Observer>::checked_configuration_frequency(double frequency_hz) {
if (!std::isfinite(frequency_hz) || frequency_hz <= 0.0)
throw std::invalid_argument("low-latency frame-control configuration frequency must be positive and finite");
::renderive::error::unexpected<std::invalid_argument>("low-latency frame-control configuration frequency must be positive and finite");
return frequency_hz;
}
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
@@ -1,4 +1,5 @@
#pragma once
#include <renderive/error/Error_Policy.hpp>
#include <array>
#include <atomic>
#include <concepts>
@@ -28,7 +29,7 @@ struct With_Real_Time_Data {
if (!std::apply([](const auto&... source) {
return (static_cast<bool>(source) && ...);
}, this->data)) {
throw std::invalid_argument("real-time data source is null");
::renderive::error::unexpected<std::invalid_argument>("real-time data source is null");
}
}
std::tuple<std::shared_ptr<Data>...> data;
@@ -1,3 +1,4 @@
#include <renderive/error/Error_Policy.hpp>
#include "Real_Time_Data_Base.hpp"
#include "renderive/renderable/base/Renderable_Base_p.hpp"
#include <stdexcept>
@@ -19,7 +20,7 @@ Real_Time_Data_Binding Real_Time_Data_Base::bind_renderable(Renderable_Base& ren
{
std::lock_guard lock(binding_mutex_);
if (bound_renderable_)
throw std::logic_error("real-time data is already bound to a renderable");
::renderive::error::unexpected<std::logic_error>("real-time data is already bound to a renderable");
bound_renderable_ = &renderable;
}
try {
@@ -27,7 +28,8 @@ Real_Time_Data_Binding Real_Time_Data_Base::bind_renderable(Renderable_Base& ren
} catch (...) {
std::lock_guard lock(binding_mutex_);
bound_renderable_ = nullptr;
throw;
::renderive::error::unexpected(
"binding real-time data", std::current_exception());
}
return {renderable, *this};
}
@@ -1,3 +1,4 @@
#include <renderive/error/Error_Policy.hpp>
#include "External_Operation.hpp"
#include <condition_variable>
#include <cstdint>
@@ -22,7 +23,8 @@ public:
{
std::lock_guard lock(mutex_);
if (failure_)
std::rethrow_exception(failure_);
::renderive::error::unexpected(
"scheduling external operation deadline", failure_);
ticket = ++sequence_;
auto iterator = entries_.emplace(deadline, Entry{ticket, std::move(callback)});
tickets_.emplace(ticket, iterator);
@@ -37,7 +39,8 @@ public:
{
std::lock_guard lock(mutex_);
if (failure_)
std::rethrow_exception(failure_);
::renderive::error::unexpected(
"cancelling external operation deadline", failure_);
const auto found = tickets_.find(ticket);
if (found == tickets_.end())
return;
@@ -99,7 +102,9 @@ private:
}
} catch (...) {
std::lock_guard lock(mutex_);
failure_ = std::current_exception();
failure_ = ::renderive::error::capture(
"running external operation deadline service",
std::current_exception());
stopping_ = true;
}
}
@@ -115,7 +120,7 @@ private:
External_Operation_Error checked_cancellation_error(External_Operation_Error error) {
if (error != External_Operation_Error::cancelled &&
error != External_Operation_Error::deadline_exceeded)
throw std::invalid_argument("external operation cancellation error is invalid");
::renderive::error::unexpected<std::invalid_argument>("external operation cancellation error is invalid");
return error;
}
}
@@ -138,7 +143,8 @@ struct External_Operation::State {
} catch (...) {
terminal = External_Operation_Status::failed;
terminal_error = External_Operation_Error::none;
terminal_exception = std::current_exception();
terminal_exception = ::renderive::error::capture(
"finishing external operation", std::current_exception());
}
}
Completion callback;
@@ -172,15 +178,15 @@ External_Operation::External_Operation(std::shared_ptr<State> state) noexcept
: state_(std::move(state)) {}
void External_Operation::on_complete(Completion completion) const {
if (!state_)
throw std::logic_error("external operation is empty");
::renderive::error::unexpected<std::logic_error>("external operation is empty");
if (!completion)
throw std::invalid_argument("external operation completion is empty");
::renderive::error::unexpected<std::invalid_argument>("external operation completion is empty");
External_Operation_Completion result;
bool invoke{};
{
std::lock_guard lock(state_->mutex);
if (state_->subscribed)
throw std::logic_error("external operation already has a completion subscriber");
::renderive::error::unexpected<std::logic_error>("external operation already has a completion subscriber");
state_->subscribed = true;
if (state_->status == External_Operation_Status::pending) {
state_->completion = std::move(completion);
@@ -218,12 +224,14 @@ bool External_Operation_Source::complete(std::function<void()> commit) {
}
bool External_Operation_Source::fail(External_Operation_Error error) {
if (error != External_Operation_Error::external_failure)
throw std::invalid_argument("external operation failure error is invalid");
::renderive::error::unexpected<std::invalid_argument>("external operation failure error is invalid");
return state_ && state_->finish(External_Operation_Status::failed, error, {});
}
bool External_Operation_Source::fail(std::exception_ptr exception) {
if (!exception)
throw std::invalid_argument("external operation failure exception is empty");
::renderive::error::unexpected<std::invalid_argument>("external operation failure exception is empty");
exception = ::renderive::error::capture(
"failing external operation", std::move(exception));
return state_ && state_->finish(External_Operation_Status::failed,
External_Operation_Error::none,
std::move(exception));
@@ -234,7 +242,7 @@ bool External_Operation_Source::cancel(External_Operation_Error error) {
}
void External_Operation_Source::set_deadline(Clock::time_point deadline) {
if (!state_)
throw std::logic_error("external operation source is empty");
::renderive::error::unexpected<std::logic_error>("external operation source is empty");
Deadline_Service::Ticket previous_ticket{};
{
std::lock_guard lock(state_->mutex);
@@ -275,12 +283,12 @@ Node_Execution_Result Node_Execution_Result::completed() noexcept {
}
Node_Execution_Result Node_Execution_Result::failed(External_Operation_Error error) {
if (error == External_Operation_Error::none)
throw std::invalid_argument("failed node result has no error");
::renderive::error::unexpected<std::invalid_argument>("failed node result has no error");
return Node_Execution_Result(error, std::nullopt);
}
Node_Execution_Result Node_Execution_Result::external(External_Operation operation) {
if (!operation)
throw std::invalid_argument("external node result has no operation");
::renderive::error::unexpected<std::invalid_argument>("external node result has no operation");
return Node_Execution_Result(External_Operation_Error::none, std::move(operation));
}
External_Operation_Error Node_Execution_Result::error() const noexcept {
@@ -291,6 +299,6 @@ bool Node_Execution_Result::is_external() const noexcept {
}
const External_Operation& Node_Execution_Result::operation() const {
if (!operation_)
throw std::logic_error("node result has no external operation");
::renderive::error::unexpected<std::logic_error>("node result has no external operation");
return *operation_;
}
@@ -1,3 +1,4 @@
#include <renderive/error/Error_Policy.hpp>
#include "Frame_Analysis.hpp"
#include <algorithm>
#include <cmath>
@@ -131,9 +132,9 @@ Node_Statistics statistics_for(Render_Node_Id id, const Node_Samples& samples) {
Frame_Analysis analyze_frame(const Render_Plan& plan, const Frame_Snapshot& frame) {
if (frame.render_plan_version != plan.version)
throw std::invalid_argument("frame and render plan versions differ");
::renderive::error::unexpected<std::invalid_argument>("frame and render plan versions differ");
if (frame.node_executions.size() != plan.graph.nodes.size())
throw std::invalid_argument("frame execution slot count differs from render plan");
::renderive::error::unexpected<std::invalid_argument>("frame execution slot count differs from render plan");
Frame_Analysis result;
result.frame_id = frame.frame_id;
@@ -146,7 +147,7 @@ Frame_Analysis analyze_frame(const Render_Plan& plan, const Frame_Snapshot& fram
indices.emplace(node.node_id, node.execution_index);
const auto& execution = frame.node_executions.at(node.execution_index);
if (execution.node_id != node.node_id)
throw std::invalid_argument("frame execution slot has a different node id");
::renderive::error::unexpected<std::invalid_argument>("frame execution slot has a different node id");
auto& analysis = result.nodes[node.execution_index];
analysis.node_id = node.node_id;
analysis.duration_ns = execution.duration_ns();
@@ -209,7 +210,7 @@ Frame_Analysis analyze_frame(const Render_Plan& plan, const Frame_Snapshot& fram
}
}
if (topological.size() != plan.graph.nodes.size())
throw std::invalid_argument("render plan contains a cycle");
::renderive::error::unexpected<std::invalid_argument>("render plan contains a cycle");
std::vector<std::uint64_t> path_duration(plan.graph.nodes.size());
std::vector<std::size_t> path_parent(plan.graph.nodes.size(),
@@ -1,3 +1,4 @@
#include <renderive/error/Error_Policy.hpp>
#include "Render_Plan.hpp"
#include <algorithm>
#include <limits>
@@ -28,7 +29,7 @@ void validate_graph(const Render_Graph& graph) {
for (std::size_t index = 0; index < graph.nodes.size(); ++index) {
const auto id = graph.nodes[index].node_id;
if (id == 0 || !indices.emplace(id, index).second)
throw std::invalid_argument("render graph has an invalid or duplicate node id");
::renderive::error::unexpected<std::invalid_argument>("render graph has an invalid or duplicate node id");
}
std::vector<std::vector<std::size_t>> successors(graph.nodes.size());
std::vector<std::size_t> indegree(graph.nodes.size());
@@ -36,9 +37,9 @@ void validate_graph(const Render_Graph& graph) {
const auto from = indices.find(edge.from);
const auto to = indices.find(edge.to);
if (from == indices.end() || to == indices.end())
throw std::invalid_argument("render graph edge references an unknown node");
::renderive::error::unexpected<std::invalid_argument>("render graph edge references an unknown node");
if (from->second == to->second)
throw std::invalid_argument("render graph cycle");
::renderive::error::unexpected<std::invalid_argument>("render graph cycle");
successors[from->second].push_back(to->second);
++indegree[to->second];
}
@@ -55,7 +56,7 @@ void validate_graph(const Render_Graph& graph) {
}
}
if (ready.size() != graph.nodes.size())
throw std::invalid_argument("render graph cycle");
::renderive::error::unexpected<std::invalid_argument>("render graph cycle");
}
struct Render_Edge_Hash {
@@ -85,9 +86,9 @@ Render_Graph_Builder::Task Render_Graph_Builder::emplace(
Render_Node_Id node_id, std::uint64_t owner_id, std::string name,
Render_Node_Kind kind) {
if (node_id == 0)
throw std::invalid_argument("render node id must not be zero");
::renderive::error::unexpected<std::invalid_argument>("render node id must not be zero");
if (!node_ids_.insert(node_id).second)
throw std::invalid_argument("duplicate render node id");
::renderive::error::unexpected<std::invalid_argument>("duplicate render node id");
graph_.nodes.push_back({node_id, owner_id, std::move(name), kind,
graph_.nodes.size()});
return {this, generation_, graph_.nodes.size() - 1};
@@ -97,7 +98,7 @@ void Render_Graph_Builder::precede(Task from, Task to) {
validate(from);
validate(to);
if (from.index == to.index)
throw std::invalid_argument("render graph cycle");
::renderive::error::unexpected<std::invalid_argument>("render graph cycle");
const Render_Edge edge{graph_.nodes[from.index].node_id, graph_.nodes[to.index].node_id};
if (successors_[edge.from].insert(edge.to).second)
graph_.edges.push_back(edge);
@@ -117,7 +118,7 @@ Render_Graph Render_Graph_Builder::finish() && {
void Render_Graph_Builder::validate(Task task) const {
if (task.builder != this || task.generation != generation_ ||
task.index >= graph_.nodes.size())
throw std::invalid_argument("invalid render graph task");
::renderive::error::unexpected<std::invalid_argument>("invalid render graph task");
}
std::shared_ptr<const Render_Plan> Render_Plan_History::publish(Render_Graph graph) {
@@ -127,7 +128,7 @@ std::shared_ptr<const Render_Plan> Render_Plan_History::publish(Render_Graph gra
if (!plans_.empty() && same_topology(plans_.back()->graph, graph))
return plans_.back();
if (next_version_ == 0 || next_version_ == std::numeric_limits<Render_Plan_Version>::max())
throw std::overflow_error("render plan version exhausted");
::renderive::error::unexpected<std::overflow_error>("render plan version exhausted");
auto plan = std::make_shared<Render_Plan>();
plan->version = next_version_++;
plan->graph = std::move(graph);
@@ -1,3 +1,4 @@
#include <renderive/error/Error_Policy.hpp>
#include "Render_Graph_Runtime.hpp"
#include <atomic>
#include <exception>
@@ -21,7 +22,7 @@ Render_Graph_Execution_Error graph_error(External_Operation_Error error) {
case External_Operation_Error::external_failure:
return Render_Graph_Execution_Error::external_failure;
}
throw std::logic_error("unknown external operation error");
::renderive::error::unexpected<std::logic_error>("unknown external operation error");
}
}
struct Render_Graph_Runtime::State
@@ -72,7 +73,7 @@ struct Render_Graph_Runtime::State
{
std::lock_guard lock(lifecycle_mutex);
if (running.load(std::memory_order_acquire))
throw std::logic_error("render graph runtime is already executing");
::renderive::error::unexpected<std::logic_error>("render graph runtime is already executing");
cancellation_requested.store(false, std::memory_order_release);
cancellation_error = External_Operation_Error::cancelled;
running.store(true, std::memory_order_release);
@@ -81,10 +82,10 @@ struct Render_Graph_Runtime::State
External_Operation_Error execution_error{};
try {
if (!execute)
throw std::invalid_argument("render graph node executor is empty");
::renderive::error::unexpected<std::invalid_argument>("render graph node executor is empty");
if (!execution_slots.empty() &&
execution_slots.size() != execution_count)
throw std::invalid_argument("render graph execution slot count differs from plan");
::renderive::error::unexpected<std::invalid_argument>("render graph execution slot count differs from plan");
graph.reset();
executions.assign(execution_slots.begin(), execution_slots.end());
if (executions.empty())
@@ -105,7 +106,7 @@ struct Render_Graph_Runtime::State
arena.execute([this] {
for (auto& node : nodes) {
if (node.root && !node.ready->try_put(Message{}))
throw std::logic_error(
::renderive::error::unexpected<std::logic_error>(
"render graph rejected root execution message");
}
graph.wait_for_all();
@@ -116,11 +117,14 @@ struct Render_Graph_Runtime::State
execution_error = first_error;
}
} catch (...) {
execution_exception = std::current_exception();
execution_exception = ::renderive::error::capture(
"coordinating render graph execution",
std::current_exception());
}
finish_execution();
if (execution_exception)
std::rethrow_exception(execution_exception);
::renderive::error::unexpected(
"executing render graph", execution_exception);
return graph_error(execution_error);
}
void finish_execution() {
@@ -140,7 +144,7 @@ struct Render_Graph_Runtime::State
}
void cancel_pending(External_Operation_Error error) {
if (error == External_Operation_Error::none)
throw std::invalid_argument("render graph cancellation error is none");
::renderive::error::unexpected<std::invalid_argument>("render graph cancellation error is none");
{
std::lock_guard lifecycle_lock(lifecycle_mutex);
if (!running.load(std::memory_order_acquire))
@@ -183,7 +187,11 @@ struct Render_Graph_Runtime::State
try {
result = execute_node(index, metrics);
} catch (...) {
fail_exception(index, std::current_exception(), render_clock_now_ns());
fail_exception(
index,
::renderive::error::capture(
"executing render graph node", std::current_exception()),
render_clock_now_ns());
return;
}
const std::uint64_t cpu_end = render_clock_now_ns();
@@ -235,17 +243,31 @@ struct Render_Graph_Runtime::State
static_cast<void>(gateway_ptr->try_put(Message{}));
}
} catch (...) {
self->fail_exception(index, std::current_exception(), end);
self->fail_exception(
index,
::renderive::error::capture(
"completing external render graph node",
std::current_exception()),
end);
}
gateway_ptr->release_wait();
});
} catch (...) {
clear_external(index);
fail_exception(index, std::current_exception(), render_clock_now_ns());
fail_exception(
index,
::renderive::error::capture(
"subscribing external render graph node",
std::current_exception()),
render_clock_now_ns());
gateway.release_wait();
}
} catch (...) {
fail_exception(index, std::current_exception(), render_clock_now_ns());
fail_exception(
index,
::renderive::error::capture(
"running render graph node", std::current_exception()),
render_clock_now_ns());
}
}
void clear_external(std::size_t index) {
@@ -1,3 +1,4 @@
#include <renderive/error/Error_Policy.hpp>
#include "Renderable_Graph_Builder.hpp"
#include <stdexcept>
#include <utility>
@@ -6,7 +7,7 @@ Renderable_Graph_Builder::Renderable_Graph_Builder(
std::uint64_t owner_id, Node_Id_Resolver resolve_node_id)
: owner_id_(owner_id), resolve_node_id_(std::move(resolve_node_id)) {
if (owner_id_ == 0 || !resolve_node_id_)
throw std::invalid_argument("renderable graph builder requires an owner and id resolver");
::renderive::error::unexpected<std::invalid_argument>("renderable graph builder requires an owner and id resolver");
}
Renderable_Graph_Builder::Task Renderable_Graph_Builder::emplace(
@@ -29,11 +30,11 @@ Renderable_Graph_Builder::Task Renderable_Graph_Builder::emplace_function(
std::string logical_key, std::string name, Render_Node_Kind kind,
Render_Node_Function function) {
if (logical_key.empty())
throw std::invalid_argument("render node logical key must not be empty");
::renderive::error::unexpected<std::invalid_argument>("render node logical key must not be empty");
if (tasks_.contains(logical_key))
throw std::invalid_argument("duplicate render node logical key");
::renderive::error::unexpected<std::invalid_argument>("duplicate render node logical key");
if (std::visit([](const auto& value) { return !value; }, function))
throw std::invalid_argument("render node function must not be empty");
::renderive::error::unexpected<std::invalid_argument>("render node function must not be empty");
const Render_Node_Id id = resolve_node_id_(logical_key);
const Task task = builder_.emplace(id, owner_id_, std::move(name), kind);
functions_.emplace(id, std::move(function));
@@ -49,7 +50,7 @@ Renderable_Graph_Builder::Task Renderable_Graph_Builder::find(
std::string_view logical_key) const {
const auto iterator = tasks_.find(std::string(logical_key));
if (iterator == tasks_.end())
throw std::out_of_range("render node logical key");
::renderive::error::unexpected<std::out_of_range>("render node logical key");
return iterator->second;
}
@@ -1,3 +1,4 @@
#include <renderive/error/Error_Policy.hpp>
#include "Renderive_Id_Allocator.hpp"
#include <atomic>
#include <limits>
@@ -8,7 +9,7 @@ Id allocate_id(std::atomic<Id>& next, const char* message) {
Id value = next.load(std::memory_order_relaxed);
for (;;) {
if (value == 0 || value == std::numeric_limits<Id>::max())
throw std::overflow_error(message);
::renderive::error::unexpected<std::overflow_error>(message);
if (next.compare_exchange_weak(value, value + 1, std::memory_order_relaxed))
return value;
}
@@ -1,3 +1,4 @@
#include <renderive/error/Error_Policy.hpp>
#include "Renderable_Base_p.hpp"
#include <algorithm>
@@ -62,7 +63,7 @@ void Renderable_Base::Impl::bind_scene(
if (!current)
current = std::move(lifetime);
else if (current != lifetime)
throw std::invalid_argument("renderable belongs to another scene");
::renderive::error::unexpected<std::invalid_argument>("renderable belongs to another scene");
}
void Renderable_Base::Impl::rebuild_render_graph() {
@@ -90,7 +91,7 @@ Renderable_Base::Impl::render_graph_snapshot() {
[this, &next_identities](std::string_view key) {
const std::string logical_key(key);
if (next_identities.contains(logical_key))
throw std::invalid_argument(
::renderive::error::unexpected<std::invalid_argument>(
"duplicate render node logical key");
const auto current = node_identities.find(logical_key);
const Render_Node_Id node_id =
@@ -118,10 +119,10 @@ Renderable_Base::Impl::memory_resource() const noexcept {
Scene_Base& Renderable_Base::Impl::scene() const {
const auto lifetime = real_time_data_state->scene_lifetime;
if (!lifetime)
throw std::logic_error("renderable is not bound to a scene");
::renderive::error::unexpected<std::logic_error>("renderable is not bound to a scene");
auto lease = lifetime->acquire();
if (!lease)
throw std::logic_error("renderable scene is no longer alive");
::renderive::error::unexpected<std::logic_error>("renderable scene is no longer alive");
return lease.scene();
}
@@ -180,7 +181,7 @@ void Renderable_Base::Impl::capture_frame_data(Frame_Render_Snapshot&) const {}
void Renderable_Base::Impl::add_prepare_action(Prepare_Action action) {
if (!action)
throw std::invalid_argument("renderable prepare action is empty");
::renderive::error::unexpected<std::invalid_argument>("renderable prepare action is empty");
prepare_actions.push_back(std::move(action));
}
@@ -242,7 +243,7 @@ Renderable_Base::Renderable_Base(std::unique_ptr<Impl> implementation,
std::pmr::memory_resource& memory_resource)
: d_ptr(std::move(implementation)) {
if (!d_ptr)
throw std::invalid_argument("renderable implementation is null");
::renderive::error::unexpected<std::invalid_argument>("renderable implementation is null");
d_ptr->initialize(*this, configuration, memory_resource);
}
@@ -1,4 +1,5 @@
#pragma once
#include <renderive/error/Error_Policy.hpp>
#include <any>
#include <cstdint>
@@ -57,7 +58,7 @@ public:
[[nodiscard]] const State& scene_state() const {
const auto* value = std::any_cast<State>(&scene_state_);
if (!value)
throw std::logic_error("frame scene state type mismatch");
::renderive::error::unexpected<std::logic_error>("frame scene state type mismatch");
return *value;
}
@@ -69,11 +70,11 @@ public:
template <class Data>
void capture(Renderable_Id owner, std::shared_ptr<const Data> data) {
if (!data)
throw std::invalid_argument("captured render data is null");
::renderive::error::unexpected<std::invalid_argument>("captured render data is null");
const auto [entry, inserted] = captured_.try_emplace(
owner, Typed_Data{typeid(Data), std::move(data)});
if (!inserted)
throw std::logic_error(
::renderive::error::unexpected<std::logic_error>(
"renderable captured frame data more than once");
}
@@ -84,7 +85,7 @@ public:
if (entry == captured_.end())
return {};
if (entry->second.type != std::type_index(typeid(Data)))
throw std::logic_error("captured render data type mismatch");
::renderive::error::unexpected<std::logic_error>("captured render data type mismatch");
return std::static_pointer_cast<const Data>(entry->second.value);
}
@@ -92,12 +93,12 @@ public:
void publish_prepared(Renderable_Id owner,
std::shared_ptr<const Data> data) const {
if (!data)
throw std::invalid_argument("prepared render data is null");
::renderive::error::unexpected<std::invalid_argument>("prepared render data is null");
std::lock_guard lock(prepared_mutex_);
const auto [entry, inserted] = prepared_.try_emplace(
owner, Typed_Data{typeid(Data), std::move(data)});
if (!inserted)
throw std::logic_error(
::renderive::error::unexpected<std::logic_error>(
"renderable published prepared data more than once");
}
@@ -109,7 +110,7 @@ public:
if (entry == prepared_.end())
return {};
if (entry->second.type != std::type_index(typeid(Data)))
throw std::logic_error("prepared render data type mismatch");
::renderive::error::unexpected<std::logic_error>("prepared render data type mismatch");
return std::static_pointer_cast<const Data>(entry->second.value);
}
+68 -47
View File
@@ -1,3 +1,4 @@
#include <renderive/error/Error_Policy.hpp>
#include "Scene_Base.hpp"
#include <algorithm>
@@ -50,7 +51,7 @@ Scene_Edit_Error relationship_error(
return display ? Scene_Edit_Error::display_cycle
: Scene_Edit_Error::dependency_cycle;
}
throw std::logic_error("unknown scene relationship mutation error");
::renderive::error::unexpected<std::logic_error>("unknown scene relationship mutation error");
}
Scene_Render_Error scene_render_error(
renderive::render_graph::detail::Render_Graph_Execution_Error error) {
@@ -65,7 +66,7 @@ Scene_Render_Error scene_render_error(
case Error::external_failure:
return Scene_Render_Error::external_failure;
}
throw std::logic_error("unknown render graph execution error");
::renderive::error::unexpected<std::logic_error>("unknown render graph execution error");
}
}
@@ -80,7 +81,9 @@ public:
execute_available();
} catch (...) {
scheduled_.store(false, std::memory_order_release);
scene_.record_pending_exception(std::current_exception());
scene_.record_pending_exception(::renderive::error::capture(
"executing queued scene operation",
std::current_exception()));
}
return Message{};
}) {
@@ -102,7 +105,7 @@ public:
const std::size_t queued = queued_.fetch_add(1, std::memory_order_relaxed) + 1;
update_peak(peak_queued_, queued);
if (!operations_.try_push(std::move(operation)))
throw std::logic_error("scene execution queue rejected admitted operation");
::renderive::error::unexpected<std::logic_error>("scene execution queue rejected admitted operation");
schedule();
}
void wait() {
@@ -131,7 +134,7 @@ private:
const bool accepted = arena.execute([this] { return node_.try_put(Message{}); });
if (!accepted) {
scheduled_.store(false, std::memory_order_release);
throw std::logic_error("scene execution graph rejected work");
::renderive::error::unexpected<std::logic_error>("scene execution graph rejected work");
}
}
void execute_available() {
@@ -270,7 +273,7 @@ Scene_Base::~Scene_Base() {
Scene_Base::Attach_Builder::Attach_Builder(Scene_Base& scene)
: scene_(scene), task_lock_(scene.task_mutex_) {
if (scene.runtime_started_)
throw std::logic_error("scene attach builder is only available before runtime starts");
::renderive::error::unexpected<std::logic_error>("scene attach builder is only available before runtime starts");
}
Scene_Edit_Error Scene_Base::Attach_Builder::attach(Renderable renderable) {
std::lock_guard lock(scene_.model_mutex_);
@@ -314,7 +317,7 @@ Scene_Edit_Error Scene_Base::Renderable_Editor::attach(Renderable renderable) {
if (const auto existing = scene_.renderables_.find(id);
existing != scene_.renderables_.end()) {
if (existing->second != renderable)
throw std::logic_error("renderable id collision");
::renderive::error::unexpected<std::logic_error>("renderable id collision");
return Scene_Edit_Error::none;
}
auto cache = scene_.raster_capabilities_
@@ -326,13 +329,14 @@ Scene_Edit_Error Scene_Base::Renderable_Editor::attach(Renderable renderable) {
if (scene_.raster_capabilities_)
scene_.raster_capabilities_->attach(id);
if (!scene_.renderables_.try_emplace(id, renderable).second)
throw std::logic_error("renderable id collision");
::renderive::error::unexpected<std::logic_error>("renderable id collision");
try {
if (cache && !scene_.color_caches_.try_emplace(id, std::move(cache)).second)
throw std::logic_error("renderable color cache collision");
::renderive::error::unexpected<std::logic_error>("renderable color cache collision");
} catch (...) {
scene_.renderables_.erase(id);
throw;
::renderive::error::unexpected(
"attaching renderable", std::current_exception());
}
renderable_data.real_time_data_state->attached.store(true, std::memory_order_release);
renderable_data.invalidate_prepare();
@@ -435,7 +439,7 @@ Scene_Edit_Error Scene_Base::Renderable_Editor::clear_dependency_parent(
}
Scene_Edit_Error Scene_Base::Edit_Operation::wait() const {
if (!result_.valid())
throw std::logic_error("scene edit operation is invalid");
::renderive::error::unexpected<std::logic_error>("scene edit operation is invalid");
return result_.get();
}
Scene_Base::Edit_Operation Scene_Base::edit_renderables(Renderable_Edit edit) {
@@ -630,7 +634,8 @@ Scene_Render_Error Scene_Base::submit_render(Abstract_Frame* frame) {
}
if (previous_exception) {
task_lock.unlock();
std::rethrow_exception(previous_exception);
::renderive::error::unexpected(
"notifying render failure", previous_exception);
}
auto& strategy = frame_control_strategy();
strategy.swap();
@@ -641,7 +646,7 @@ Scene_Render_Error Scene_Base::submit_render(Abstract_Frame* frame) {
snapshot = capture_live_frame();
snapshot->next_refresh_interval_ns_ = strategy.frame_control_state().next_refresh_interval_ns;
if (render_sequence_ == std::numeric_limits<std::uint64_t>::max())
throw std::overflow_error("render sequence exhausted");
::renderive::error::unexpected<std::overflow_error>("render sequence exhausted");
snapshot->render_sequence = ++render_sequence_;
auto task = std::make_shared<Render_Task>();
task->frame = std::make_shared<Abstract_Frame::Render_State>();
@@ -665,7 +670,8 @@ Scene_Render_Error Scene_Base::submit_render(Abstract_Frame* frame) {
d_func().dispatch(submitted_observation);
} catch (...) {
active_submitted_observer_scene_ = previous_observer_scene;
const auto exception = std::current_exception();
const auto exception = ::renderive::error::capture(
"notifying submitted scene render", std::current_exception());
{
std::lock_guard<std::recursive_mutex> lock(task_mutex_);
task->completion->exception = exception;
@@ -675,7 +681,8 @@ Scene_Render_Error Scene_Base::submit_render(Abstract_Frame* frame) {
}
capture_controller_.finish_frame(capture_ticket, false);
complete_pending_operation();
std::rethrow_exception(exception);
::renderive::error::unexpected(
"waiting for submitted render", exception);
}
active_submitted_observer_scene_ = previous_observer_scene;
try {
@@ -685,7 +692,8 @@ Scene_Render_Error Scene_Base::submit_render(Abstract_Frame* frame) {
});
} catch (...) {
capture_controller_.finish_frame(capture_ticket, false);
const auto exception = std::current_exception();
const auto exception = ::renderive::error::capture(
"scheduling scene render", std::current_exception());
{
std::lock_guard<std::recursive_mutex> lock(task_mutex_);
current_completion_->exception = exception;
@@ -693,7 +701,8 @@ Scene_Render_Error Scene_Base::submit_render(Abstract_Frame* frame) {
current_completion_->observed = true;
}
complete_pending_operation();
std::rethrow_exception(exception);
::renderive::error::unexpected(
"waiting for render completion", exception);
}
std::size_t deferred_render_count{};
{
@@ -722,7 +731,8 @@ Scene_Render_Error Scene_Base::wait_for_render() {
const auto error = completion->error;
lock.unlock();
if (exception)
std::rethrow_exception(exception);
::renderive::error::unexpected(
"waiting for scene idle", exception);
return error;
}
Scene_Base::Edit_Operation Scene_Base::enqueue_renderable_edit(
@@ -744,7 +754,9 @@ Scene_Base::Edit_Operation Scene_Base::enqueue_renderable_edit(
try {
promise->set_value(execute_renderable_edit(std::move(edit)));
} catch (...) {
const auto exception = std::current_exception();
const auto exception = ::renderive::error::capture(
"executing asynchronous renderable edit",
std::current_exception());
record_pending_exception(exception);
promise->set_exception(exception);
}
@@ -752,7 +764,8 @@ Scene_Base::Edit_Operation Scene_Base::enqueue_renderable_edit(
});
} catch (...) {
complete_pending_operation();
throw;
::renderive::error::unexpected(
"observing scene render submission", std::current_exception());
}
return operation;
}
@@ -761,7 +774,7 @@ void Scene_Base::submit_operation(std::function<void()> operation) {
{
std::lock_guard<std::recursive_mutex> lock(task_mutex_);
if (shutting_down_)
throw std::logic_error("scene is shutting down");
::renderive::error::unexpected<std::logic_error>("scene is shutting down");
runtime_started_ = true;
++pending_operations_;
}
@@ -770,13 +783,16 @@ void Scene_Base::submit_operation(std::function<void()> operation) {
try {
operation();
} catch (...) {
record_pending_exception(std::current_exception());
record_pending_exception(::renderive::error::capture(
"executing asynchronous scene operation",
std::current_exception()));
}
complete_pending_operation();
});
} catch (...) {
complete_pending_operation();
throw;
::renderive::error::unexpected(
"submitting scene render", std::current_exception());
}
}
void Scene_Base::complete_pending_operation() {
@@ -811,24 +827,24 @@ Scene_Edit_Error Scene_Base::cleanup_detached_topology_locked() {
Scene_Edit_Error Scene_Base::validate_structure_locked() const {
for (const auto& [id, renderable] : renderables_) {
if (!renderable)
throw std::logic_error("scene contains null renderable owner");
::renderive::error::unexpected<std::logic_error>("scene contains null renderable owner");
if (renderable_scene_error(*renderable) != Scene_Edit_Error::none)
throw std::logic_error("scene contains renderable bound to another scene");
::renderive::error::unexpected<std::logic_error>("scene contains renderable bound to another scene");
if (!renderable->d_func().real_time_data_state->attached.load(
std::memory_order_acquire))
throw std::logic_error("scene contains renderable whose attached state is false");
::renderive::error::unexpected<std::logic_error>("scene contains renderable whose attached state is false");
if (!dependency_resolver_.contains(id))
throw std::logic_error("dependency graph is missing attached renderable " + std::to_string(id));
::renderive::error::unexpected<std::logic_error>("dependency graph is missing attached renderable " + std::to_string(id));
}
const auto dependency = dependency_resolver_.resolve();
if (dependency.order.size() != renderables_.size())
throw std::logic_error("dependency graph does not match attached renderables");
::renderive::error::unexpected<std::logic_error>("dependency graph does not match attached renderables");
for (const Renderable_Id id : dependency.order) {
if (!renderables_.contains(id))
throw std::logic_error("dependency graph references detached renderable " + std::to_string(id));
::renderive::error::unexpected<std::logic_error>("dependency graph references detached renderable " + std::to_string(id));
}
if (raster_capabilities_ && !raster_capabilities_->valid())
throw std::logic_error("display graph does not match attached renderables");
::renderive::error::unexpected<std::logic_error>("display graph does not match attached renderables");
return Scene_Edit_Error::none;
}
@@ -840,7 +856,7 @@ void Scene_Base::request_render_graph_rebuild(Renderable_Base& renderable) {
return;
}
if (renderable_scene_error(renderable) != Scene_Edit_Error::none)
throw std::logic_error("attached renderable scene invariant violated");
::renderive::error::unexpected<std::logic_error>("attached renderable scene invariant violated");
if (active_renderable_edit_scene_ == this) {
renderable_data.reset_render_graph();
notify_model_dirty();
@@ -1076,7 +1092,8 @@ Capture_Request_Result Scene_Base::capture_frames(std::size_t count) {
capture_repository_.begin_session(request.session_id, count);
} catch (...) {
capture_controller_.cancel(request.session_id);
throw;
::renderive::error::unexpected(
"executing renderable edit", std::current_exception());
}
return request;
}
@@ -1141,7 +1158,7 @@ const Frame_Control_Strategy_Base& Scene_Base::frame_control_strategy() const {
void Scene_Base::bind_raster_capabilities(
renderive::scene::detail::Raster_Capabilities& capabilities) {
if (raster_capabilities_ != nullptr)
throw std::logic_error("scene raster capabilities are already bound");
::renderive::error::unexpected<std::logic_error>("scene raster capabilities are already bound");
raster_capabilities_ = &capabilities;
}
@@ -1183,9 +1200,9 @@ std::uint64_t Scene_Base::Impl::now_ns() const noexcept {
std::unique_lock<std::recursive_mutex> Scene_Base::lock_render_idle() {
if (is_render_execution_context())
throw std::logic_error("render-idle operation is not allowed during render execution");
::renderive::error::unexpected<std::logic_error>("render-idle operation is not allowed during render execution");
if (active_submitted_observer_scene_ == this)
throw std::logic_error("render-idle operation is not allowed during render submission observation");
::renderive::error::unexpected<std::logic_error>("render-idle operation is not allowed during render submission observation");
std::unique_lock<std::recursive_mutex> lock(task_mutex_);
render_completed_.wait(lock, [this] { return pending_operations_ == 0; });
return lock;
@@ -1243,7 +1260,8 @@ Scene_Edit_Error Scene_Base::execute_renderable_edit(
transaction.rollback();
active_renderable_edit_scene_ = previous;
active_edit_transaction_ = nullptr;
std::rethrow_exception(exception);
::renderive::error::unexpected(
"executing scene render task", exception);
}
}
@@ -1266,7 +1284,8 @@ void Scene_Base::execute_render_task(std::shared_ptr<Render_Task> task) {
snapshot.scene_state_revision_, snapshot.renderables.size(), task->topology,
task->snapshot, task->compiled_plan->plan});
} catch (...) {
exception = std::current_exception();
exception = ::renderive::error::capture(
"executing scene render task", std::current_exception());
if (!render_graph_entered)
capture_controller_.finish_frame(snapshot.capture_ticket_, false);
try {
@@ -1274,8 +1293,9 @@ void Scene_Base::execute_render_task(std::shared_ptr<Render_Task> task) {
snapshot.scene_state_revision_, snapshot.renderables.size(), task->topology,
task->snapshot, task->compiled_plan->plan});
} catch (...) {
exception = std::make_exception_ptr(Render_Failure_Notification_Error(
std::move(exception), std::current_exception()));
exception = ::renderive::error::capture<
Render_Failure_Notification_Error>(
std::move(exception), std::current_exception());
}
}
{
@@ -1345,10 +1365,10 @@ std::shared_ptr<Scene_Base::Compiled_Render_Plan> Scene_Base::compile_render_pla
for (auto& item : active) {
const auto& state = snapshot.renderables[item.snapshot_index];
if (!state.render_graph)
throw std::logic_error("frame renderable has no graph snapshot");
::renderive::error::unexpected<std::logic_error>("frame renderable has no graph snapshot");
for (const auto& node : state.render_graph->graph.nodes) {
if (node.kind == Render_Node_Kind::composite)
throw std::logic_error(
::renderive::error::unexpected<std::logic_error>(
"renderable graph must not define composite nodes");
graph.nodes.push_back(node);
const auto& render_function =
@@ -1370,7 +1390,7 @@ std::shared_ptr<Scene_Base::Compiled_Render_Plan> Scene_Base::compile_render_pla
const auto from = local_kinds.find(edge.from);
const auto to = local_kinds.find(edge.to);
if (from == local_kinds.end() || to == local_kinds.end())
throw std::logic_error(
::renderive::error::unexpected<std::logic_error>(
"renderable graph edge references an unknown node");
if (from->second == Render_Node_Kind::prepare &&
to->second == Render_Node_Kind::prepare) {
@@ -1466,7 +1486,7 @@ std::shared_ptr<Scene_Base::Compiled_Render_Plan> Scene_Base::compile_render_pla
for (const auto& node : plan->graph.nodes) {
auto function = functions.find(node.node_id);
if (function == functions.end())
throw std::logic_error("render plan node has no execution binding");
::renderive::error::unexpected<std::logic_error>("render plan node has no execution binding");
execution_bindings[node.execution_index] = std::move(function->second);
}
if (compiled_render_plan_ && compiled_render_plan_->plan == plan) {
@@ -1486,7 +1506,7 @@ std::shared_ptr<Scene_Base::Compiled_Render_Plan> Scene_Base::compile_render_pla
Scene_Render_Error Scene_Base::execute_render_graph(Render_Task& task) {
if (!task.snapshot || !task.compiled_plan || !task.frame)
throw std::logic_error("render task is incomplete");
::renderive::error::unexpected<std::logic_error>("render task is incomplete");
auto& frame = *task.frame;
const auto& snapshot = *task.snapshot;
const Capture_Frame_Ticket capture_ticket = snapshot.capture_ticket_;
@@ -1537,7 +1557,7 @@ Scene_Render_Error Scene_Base::execute_render_graph(Render_Task& task) {
if (!state.paint_required)
return Node_Execution_Result::completed();
if (!state.paint_buffer_)
throw std::logic_error(
::renderive::error::unexpected<std::logic_error>(
"paint node has no frame color cache");
std::get<Paint_Render_Node_Function>(binding.function)(
Paint_Render_Context{snapshot, state,
@@ -1563,7 +1583,7 @@ Scene_Render_Error Scene_Base::execute_render_graph(Render_Task& task) {
return std::get<Scene_Render_Node_Function>(binding.function)(
Scene_Render_Context{snapshot, metrics, diagnostics});
}
throw std::logic_error("unknown render node kind");
::renderive::error::unexpected<std::logic_error>("unknown render node kind");
});
if (graph_error != renderive::render_graph::detail::Render_Graph_Execution_Error::none) {
Abstract_Frame::discard_render(frame);
@@ -1601,7 +1621,8 @@ Scene_Render_Error Scene_Base::execute_render_graph(Render_Task& task) {
if (frame_active)
Abstract_Frame::discard_render(frame);
capture_controller_.finish_frame(capture_ticket, false);
throw;
::renderive::error::unexpected(
"notifying scene render completion", std::current_exception());
}
}
@@ -1,4 +1,5 @@
#pragma once
#include <renderive/error/Error_Policy.hpp>
#include <algorithm>
#include <cstddef>
#include <functional>
@@ -45,12 +46,12 @@ public:
for (const auto& relationship : resolution.relationships) {
const auto result = add_parent(relationship.child, relationship.parent);
if (!result)
throw std::logic_error("invalid dependency resolution");
::renderive::error::unexpected<std::logic_error>("invalid dependency resolution");
}
}
void attach(Id id) {
if (!nodes_.try_emplace(id, resource()).second)
throw std::logic_error("dependency id is already attached");
::renderive::error::unexpected<std::logic_error>("dependency id is already attached");
}
void erase(Id id) {
validate_endpoint(id);
@@ -155,7 +156,7 @@ public:
}
}
if (result.order.size() != nodes_.size())
throw std::logic_error("dependency graph contains a cycle");
::renderive::error::unexpected<std::logic_error>("dependency graph contains a cycle");
return result;
}
private:
@@ -170,7 +171,7 @@ private:
}
void validate_endpoint(Id id) const {
if (!nodes_.contains(id))
throw std::logic_error("dependency endpoint invariant violated");
::renderive::error::unexpected<std::logic_error>("dependency endpoint invariant violated");
}
[[nodiscard]] bool acyclic() const {
std::unordered_map<Id, std::size_t, Hash> indegree;
@@ -0,0 +1,35 @@
#include <renderive/error/Error_Policy.hpp>
#include <gtest/gtest.h>
#include <stdexcept>
namespace renderive::error {
namespace {
TEST(ErrorPolicy, ExceptionModePreservesExceptionType) {
set_mode(Mode::exception);
EXPECT_THROW(unexpected<std::invalid_argument>("invalid value"),
std::invalid_argument);
}
TEST(ErrorPolicy, FastFailModeTerminatesAtThePolicyBoundary) {
EXPECT_DEATH(
{
set_mode(Mode::fast_fail);
unexpected<std::logic_error>("broken invariant");
},
"broken invariant");
}
TEST(ErrorPolicy, CaptureUsesTheSameMode) {
set_mode(Mode::exception);
const auto exception = capture<std::logic_error>("async failure");
ASSERT_TRUE(exception);
EXPECT_THROW(std::rethrow_exception(exception), std::logic_error);
}
} // namespace
} // namespace renderive::error
+3 -1
View File
@@ -79,7 +79,9 @@ if (RENDERIVE_BUILD_TESTS)
"QT_QPA_PLATFORM_PLUGIN_PATH=${Renderive_Qt_runtime_root}/plugins/platforms"
"$<TARGET_FILE:${Renderive_Qt_test_target}>"
)
set_tests_properties("${Renderive_Qt_test_target}" PROPERTIES LABELS "Renderive_Qt")
set_tests_properties("${Renderive_Qt_test_target}" PROPERTIES
LABELS "Renderive_Qt"
ENVIRONMENT "RENDERIVE_ERROR_MODE=exception")
list(APPEND Renderive_Qt_test_targets "${Renderive_Qt_test_target}")
endforeach ()
add_custom_target(Renderive_Qt_check
+3 -1
View File
@@ -56,7 +56,9 @@ if (RENDERIVE_BUILD_TESTS)
target_link_libraries("${Renderive_render_2D_test_target}" PRIVATE Renderive_render_2D GTest::gtest_main)
renderive_stage_kernel_runtime("${Renderive_render_2D_test_target}")
add_test(NAME "${Renderive_render_2D_test_target}" COMMAND "${Renderive_render_2D_test_target}")
set_tests_properties("${Renderive_render_2D_test_target}" PROPERTIES LABELS "Renderive_render_2D")
set_tests_properties("${Renderive_render_2D_test_target}" PROPERTIES
LABELS "Renderive_render_2D"
ENVIRONMENT "RENDERIVE_ERROR_MODE=exception")
list(APPEND Renderive_render_2D_test_targets "${Renderive_render_2D_test_target}")
endforeach ()
add_custom_target(Renderive_render_2D_check
+2 -1
View File
@@ -1,4 +1,5 @@
#pragma once
#include <renderive/error/Error_Policy.hpp>
#include "Axis_State_Strategy.hpp"
#include <renderive/base/property/Property.hpp>
#include <cmath>
@@ -7,7 +8,7 @@ namespace renderive {
struct Axis_Coordinate_Range_Validator {
void operator()(const Range& value) const {
if (!std::isfinite(value.origin) || !std::isfinite(value.target) || !(value.size() > 0.0))
throw std::invalid_argument("axis coordinate range must be finite and non-empty");
::renderive::error::unexpected<std::invalid_argument>("axis coordinate range must be finite and non-empty");
}
};
struct Axis_Properties : Axis_Base_Properties {
+2 -1
View File
@@ -1,3 +1,4 @@
#include <renderive/error/Error_Policy.hpp>
#include "Plot_Scene.h"
#include "detail/Plot_Scene_Model.hpp"
#include "../plottable/Performance_Overlay.h"
@@ -54,7 +55,7 @@ struct Plot_Scene::Impl {
case Plot_Frame_Mode::Playback:
return std::make_unique<Playback_Plot_Scene>();
}
throw std::invalid_argument("unknown Plot_Scene frame mode");
::renderive::error::unexpected<std::invalid_argument>("unknown Plot_Scene frame mode");
}
Plot_Frame_Mode mode;
@@ -1,4 +1,5 @@
#pragma once
#include <renderive/error/Error_Policy.hpp>
#include "../Plot_Scene.h"
#include "../../render/Blend2D_Cache.h"
#include <renderive/base/observer/Observer.hpp>
@@ -129,7 +130,7 @@ struct Plot_Scene_Model final : ::Scene2D_Context<Frame_Control, detail::Blend2D
root->set_object_name("root");
auto builder = this->attach_builder();
if (builder.attach(root) != Scene_Edit_Error::none)
throw std::logic_error("validated plot root attachment failed");
::renderive::error::unexpected<std::logic_error>("validated plot root attachment failed");
}
[[nodiscard]] renderive_Owner<Renderable> root_renderable() const {
const auto topology = this->topology_snapshot();
@@ -208,7 +209,7 @@ struct Plot_Scene_Model final : ::Scene2D_Context<Frame_Control, detail::Blend2D
case Scene_Render_Error::shutting_down:
return Plot_Render_Error::scene_shutting_down;
}
throw std::logic_error("unknown scene render error");
::renderive::error::unexpected<std::logic_error>("unknown scene render error");
}
[[nodiscard]] bool discard_pending_frame() {
if constexpr (Mode == Plot_Frame_Mode::Playback)
@@ -239,7 +240,8 @@ struct Plot_Scene_Model final : ::Scene2D_Context<Frame_Control, detail::Blend2D
}
catch (...) {
this->notify_model_dirty();
throw;
::renderive::error::unexpected(
"rolling back plot frame preparation", std::current_exception());
}
const auto finished = std::chrono::steady_clock::now();
const double duration_ms = std::chrono::duration<double, std::milli>(finished - started).count();
+3 -1
View File
@@ -158,7 +158,9 @@ if (RENDERIVE_BUILD_TESTS)
endif ()
renderive_stage_render_3D_runtime("${Renderive_render_3D_test_target}")
add_test(NAME "${Renderive_render_3D_test_target}" COMMAND "${Renderive_render_3D_test_target}")
set_tests_properties("${Renderive_render_3D_test_target}" PROPERTIES LABELS "Renderive_render_3D")
set_tests_properties("${Renderive_render_3D_test_target}" PROPERTIES
LABELS "Renderive_render_3D"
ENVIRONMENT "RENDERIVE_ERROR_MODE=exception")
list(APPEND Renderive_render_3D_test_targets "${Renderive_render_3D_test_target}")
endforeach ()
add_custom_target(Renderive_render_3D_check
+31 -18
View File
@@ -1,3 +1,4 @@
#include <renderive/error/Error_Policy.hpp>
#include "Point_Scene.h"
#include "Scene_Context.hpp"
@@ -124,7 +125,7 @@ struct Point_Backend_State final {
detail::Datoviz_Visual_Backend& require_backend() {
if (!backend)
throw std::logic_error("Datoviz backend invariant is unavailable");
::renderive::error::unexpected<std::logic_error>("Datoviz backend invariant is unavailable");
return *backend;
}
[[nodiscard]] bool backend_available() const noexcept {
@@ -176,7 +177,7 @@ struct Basic_Point_Scene final
void initialize(const Scene_Options& options,
std::shared_ptr<Point_Visual> point_visual) {
if (!point_visual)
throw std::invalid_argument("Point_Scene requires a Point_Visual");
::renderive::error::unexpected<std::invalid_argument>("Point_Scene requires a Point_Visual");
visual = std::move(point_visual);
point_id = visual->renderable_id();
this->Scene_State_Strategy::template set<
@@ -189,7 +190,7 @@ struct Basic_Point_Scene final
auto builder = this->attach_builder();
if (builder.attach(renderive_Owner<Point_Visual>(visual)) !=
Scene_Edit_Error::none)
throw std::logic_error("validated point visual attachment failed");
::renderive::error::unexpected<std::logic_error>("validated point visual attachment failed");
}
const detail::Scene_State initial{
options.viewport, options.clear_color, options.visual_family};
@@ -199,7 +200,7 @@ struct Basic_Point_Scene final
options.gpu_index, options.validation_enabled, initial);
});
if (!invocation)
throw std::logic_error("render domain stopped during Point_Scene initialization");
::renderive::error::unexpected<std::logic_error>("render domain stopped during Point_Scene initialization");
}
void resize(Extent extent) override {
@@ -320,7 +321,7 @@ struct Basic_Point_Scene final
case Scene_Render_Error::shutting_down:
return Frame_Request_Error::scene_shutting_down;
}
throw std::logic_error("unknown scene render error");
::renderive::error::unexpected<std::logic_error>("unknown scene render error");
}
[[nodiscard]] Frame_Request_Error request_frame() override {
@@ -406,7 +407,7 @@ struct Basic_Point_Scene final
External_Operation_Error::external_failure);
const auto prepared = context.prepared<detail::Prepared_Point>(point_id);
if (!prepared)
throw std::logic_error("Point_Visual did not publish prepared data");
::renderive::error::unexpected<std::logic_error>("Point_Visual did not publish prepared data");
const detail::Scene_State scene_state =
context.frame.scene_state<detail::Scene_State>();
const std::uint64_t scene_revision =
@@ -432,8 +433,8 @@ struct Basic_Point_Scene final
[state, async_frame, metrics, diagnostics, source]() mutable {
if (!async_frame->pending) {
static_cast<void>(source->fail(
std::make_exception_ptr(std::logic_error(
"GPU completion has no pending frame"))));
::renderive::error::capture<std::logic_error>(
"GPU completion has no pending frame")));
return;
}
auto pending = std::move(*async_frame->pending);
@@ -447,7 +448,10 @@ struct Basic_Point_Scene final
static_cast<void>(source->fail(
External_Operation_Error::external_failure));
} catch (...) {
static_cast<void>(source->fail(std::current_exception()));
static_cast<void>(source->fail(
::renderive::error::capture(
"quarantining Point_Scene backend",
std::current_exception())));
}
return;
}
@@ -474,7 +478,9 @@ struct Basic_Point_Scene final
}));
} catch (...) {
static_cast<void>(source->fail(
std::current_exception()));
::renderive::error::capture(
"collecting Point_Scene frame",
std::current_exception())));
}
}, [source](std::exception_ptr exception) {
static_cast<void>(source->fail(std::move(exception)));
@@ -498,7 +504,9 @@ struct Basic_Point_Scene final
static_cast<void>(source->cancel());
} catch (...) {
static_cast<void>(source->fail(
std::current_exception()));
::renderive::error::capture(
"posting Point_Scene completion",
std::current_exception())));
}
},
[source](std::exception_ptr exception) {
@@ -556,8 +564,10 @@ struct Basic_Point_Scene final
std::move(*pending));
completion->watch(owned.device, owned.fence);
} catch (...) {
static_cast<void>(
source->fail(std::current_exception()));
static_cast<void>(source->fail(
::renderive::error::capture(
"submitting Point_Scene frame",
std::current_exception())));
}
}, [source](std::exception_ptr exception) {
static_cast<void>(source->fail(std::move(exception)));
@@ -565,7 +575,10 @@ struct Basic_Point_Scene final
if (post_error != detail::Render_Domain::Error::none)
static_cast<void>(source->cancel());
} catch (...) {
static_cast<void>(source->fail(std::current_exception()));
static_cast<void>(source->fail(
::renderive::error::capture(
"posting Point_Scene render task",
std::current_exception())));
}
return Node_Execution_Result::external(operation);
}
@@ -591,7 +604,7 @@ std::unique_ptr<Scene_Model> make_scene_model(
Basic_Point_Scene<Playback_Frame, Frame_Mode::Playback>>(
options, std::move(visual));
}
throw std::invalid_argument("unknown Point_Scene frame mode");
::renderive::error::unexpected<std::invalid_argument>("unknown Point_Scene frame mode");
}
} // namespace
@@ -607,12 +620,12 @@ struct Point_Scene::Impl {
Point_Scene::Point_Scene(Scene_Options options,
std::shared_ptr<Point_Visual> visual) {
if (!valid(options.viewport))
throw std::invalid_argument("Point_Scene viewport must be nonempty");
::renderive::error::unexpected<std::invalid_argument>("Point_Scene viewport must be nonempty");
if (!valid(options.clear_color))
throw std::invalid_argument("Point_Scene clear color must be in [0, 1]");
::renderive::error::unexpected<std::invalid_argument>("Point_Scene clear color must be in [0, 1]");
if (!std::isfinite(options.maximum_frames_per_second) ||
options.maximum_frames_per_second <= 0.0)
throw std::invalid_argument(
::renderive::error::unexpected<std::invalid_argument>(
"Point_Scene frame frequency must be positive");
impl_ = std::make_unique<Impl>(options, std::move(visual));
}
+6 -5
View File
@@ -1,3 +1,4 @@
#include <renderive/error/Error_Policy.hpp>
#include "Point_Visual.h"
#include "detail/Point_Core.h"
@@ -49,11 +50,11 @@ struct Point_Data_Observation {
void Point_Visual::State_Validator::operator()(const State& state) const {
if (!std::isfinite(state.style.stroke_width_px) ||
state.style.stroke_width_px < 0.0F)
throw std::invalid_argument(
::renderive::error::unexpected<std::invalid_argument>(
"point stroke width must be finite and nonnegative");
for (float value : state.transform.values) {
if (!std::isfinite(value))
throw std::invalid_argument(
::renderive::error::unexpected<std::invalid_argument>(
"point transform must contain finite values");
}
}
@@ -73,7 +74,7 @@ struct Point_Visual::Impl : next_Impl<Impl>, Point_Buffer_Strategy {
explicit Impl(std::vector<Point> points) {
if (update_points(std::move(points)) != Visual_Data_Error::none)
throw std::invalid_argument("point payload contains invalid coordinates or diameter");
::renderive::error::unexpected<std::invalid_argument>("point payload contains invalid coordinates or diameter");
}
std::shared_ptr<void> real_time_data_binding;
@@ -124,7 +125,7 @@ struct Point_Visual::Impl : next_Impl<Impl>, Point_Buffer_Strategy {
const auto& scene = context.frame.scene_state<Scene_State>();
const auto input = context.captured<Captured_Point>();
if (!input)
throw std::logic_error("Point_Visual frame input was not captured");
::renderive::error::unexpected<std::logic_error>("Point_Visual frame input was not captured");
auto output = std::make_shared<Prepared_Point>();
output->state = input->state;
output->family = scene.visual_family;
@@ -134,7 +135,7 @@ struct Point_Visual::Impl : next_Impl<Impl>, Point_Buffer_Strategy {
const auto& source = input->points;
const std::size_t count = source ? source->size() : 0U;
if (count > std::numeric_limits<std::uint32_t>::max())
throw std::length_error("Datoviz point payload is too large");
::renderive::error::unexpected<std::length_error>("Datoviz point payload is too large");
output->positions.reserve(count);
output->colors.reserve(count);
output->sizes.reserve(count);
@@ -1,3 +1,4 @@
#include <renderive/error/Error_Policy.hpp>
#include "Datoviz_Visual_Backend.h"
#include <datoviz/drp2/stream.h>
@@ -29,7 +30,7 @@ template <class Resource, class Allocate>
Resource* allocate_wrapper(Allocate allocate, const char* message) {
Resource* resource = allocate();
if (resource == nullptr)
throw std::runtime_error(message);
::renderive::error::unexpected<std::runtime_error>(message);
return resource;
}
@@ -110,10 +111,10 @@ void configure_glyph_text(DvzScene* scene, DvzVisual* visual, const char* text)
dvz_text_atlas_spec(DVZ_TEXT_RENDERER_MSDF_ATLAS, 64.0F);
if (font == nullptr ||
!dvz_font_atlas_ensure_string(font, &atlas_specification, text))
throw std::runtime_error("failed to create Datoviz text atlas");
::renderive::error::unexpected<std::runtime_error>("failed to create Datoviz text atlas");
const auto* atlas = dvz_font_atlas(font, &atlas_specification);
if (atlas == nullptr || dvz_glyph_set_atlas(visual, atlas) != DVZ_OK)
throw std::runtime_error("failed to bind Datoviz text atlas");
::renderive::error::unexpected<std::runtime_error>("failed to bind Datoviz text atlas");
float line_width = 0.0F;
for (const auto* character = text; *character != '\0'; ++character) {
@@ -157,7 +158,7 @@ void configure_glyph_text(DvzScene* scene, DvzVisual* visual, const char* text)
++glyph_index;
}
if (positions.empty())
throw std::runtime_error("Datoviz text atlas contains no visible glyphs");
::renderive::error::unexpected<std::runtime_error>("Datoviz text atlas contains no visible glyphs");
const auto count = static_cast<std::uint32_t>(positions.size());
const std::array<DvzVisualDataUpdate, 5> updates{{
{"position", positions.data(), count}, {"bounds", bounds.data(), count},
@@ -166,7 +167,7 @@ void configure_glyph_text(DvzScene* scene, DvzVisual* visual, const char* text)
if (dvz_visual_set_data_many(visual, updates.data(),
static_cast<std::uint32_t>(updates.size())) != DVZ_OK ||
dvz_visual_set_depth_test(visual, false) != DVZ_OK)
throw std::runtime_error("failed to upload Datoviz text geometry");
::renderive::error::unexpected<std::runtime_error>("failed to upload Datoviz text geometry");
}
} // namespace
@@ -181,18 +182,18 @@ public:
Frame_Target(DvzGpuCtx* gpu_context, Extent extent, std::uint64_t generation)
: gpu_context_(gpu_context), extent_(extent), generation_(generation) {
if (gpu_context == nullptr || extent.empty())
throw std::invalid_argument("invalid Datoviz point frame target");
::renderive::error::unexpected<std::invalid_argument>("invalid Datoviz point frame target");
const std::uint64_t byte_size = static_cast<std::uint64_t>(extent.width) *
extent.height * 4ULL;
if (byte_size > std::numeric_limits<DvzSize>::max())
throw std::length_error("Datoviz point frame target is too large");
::renderive::error::unexpected<std::length_error>("Datoviz point frame target is too large");
byte_size_ = static_cast<DvzSize>(byte_size);
DvzDevice* device = dvz_gpu_ctx_device(gpu_context_);
DvzVma* allocator = dvz_gpu_ctx_alloc(gpu_context_);
DvzQueue* queue = dvz_gpu_ctx_queue(gpu_context_, DVZ_QUEUE_MAIN);
if (device == nullptr || allocator == nullptr || queue == nullptr)
throw std::runtime_error("Datoviz GPU context is incomplete");
::renderive::error::unexpected<std::runtime_error>("Datoviz GPU context is incomplete");
try {
image_ = allocate_wrapper<DvzImages>(dvz_images_create_wrapper,
"failed to allocate Datoviz image");
@@ -204,7 +205,7 @@ public:
VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
dvz_images_alloc_flags(image_, DVZ_ALLOC_FLAGS_NONE);
if (dvz_images_create(image_) != 0)
throw std::runtime_error("failed to create Datoviz point image");
::renderive::error::unexpected<std::runtime_error>("failed to create Datoviz point image");
view_ = allocate_wrapper<DvzImageViews>(dvz_image_views_create_wrapper,
"failed to allocate Datoviz image view");
@@ -214,19 +215,19 @@ public:
dvz_image_views_mip(view_, 0, 1);
dvz_image_views_layers(view_, 0, 1);
if (dvz_image_views_create(view_) != 0)
throw std::runtime_error("failed to create Datoviz point image view");
::renderive::error::unexpected<std::runtime_error>("failed to create Datoviz point image view");
commands_ = allocate_wrapper<DvzCommands>(dvz_commands_create_wrapper,
"failed to allocate Datoviz commands");
dvz_commands(device, queue, 1, commands_);
if (dvz_commands_handle(commands_) == VK_NULL_HANDLE)
throw std::runtime_error("failed to create Datoviz command buffer");
::renderive::error::unexpected<std::runtime_error>("failed to create Datoviz command buffer");
fence_ = allocate_wrapper<DvzFence>(dvz_fence_create_wrapper,
"failed to allocate Datoviz fence");
dvz_fence(device, true, fence_);
if (dvz_fence_handle(fence_) == VK_NULL_HANDLE)
throw std::runtime_error("failed to create Datoviz fence");
::renderive::error::unexpected<std::runtime_error>("failed to create Datoviz fence");
submit_ = allocate_wrapper<DvzSubmit>(dvz_submit_create_wrapper,
"failed to allocate Datoviz submit");
@@ -237,18 +238,19 @@ public:
dvz_buffer_flags(readback_, DVZ_ALLOC_HOST_ACCESS_RANDOM | DVZ_ALLOC_MAPPED);
dvz_buffer_usage(readback_, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
if (dvz_buffer_create(readback_) != 0)
throw std::runtime_error("failed to create Datoviz readback buffer");
::renderive::error::unexpected<std::runtime_error>("failed to create Datoviz readback buffer");
} catch (...) {
destroy();
throw;
::renderive::error::unexpected(
"creating Datoviz frame target", std::current_exception());
}
}
~Frame_Target() { destroy(); }
~Frame_Target() noexcept(false) { destroy(); }
void begin(bool observe) {
if (in_flight_)
throw std::logic_error("Datoviz frame target is still in flight");
::renderive::error::unexpected<std::logic_error>("Datoviz frame target is still in flight");
observing_ = observe;
if (observing_ && !timestamps_initialized_) {
initialize_timestamps(
@@ -257,7 +259,7 @@ public:
}
dvz_cmd_reset(commands_);
if (dvz_cmd_begin_result(commands_) != 0)
throw std::runtime_error("failed to begin Datoviz command buffer");
::renderive::error::unexpected<std::runtime_error>("failed to begin Datoviz command buffer");
DvzBarriers barriers{};
dvz_barriers(&barriers);
@@ -318,7 +320,7 @@ public:
void submit() {
if (!recording_)
throw std::logic_error("Datoviz frame target is not recording");
::renderive::error::unexpected<std::logic_error>("Datoviz frame target is not recording");
const VkCommandBuffer command_buffer = dvz_commands_handle(commands_);
if (observing_ && timestamps_supported_) {
vkCmdWriteTimestamp(
@@ -372,7 +374,7 @@ public:
}
if (dvz_cmd_end_result(commands_) != 0)
throw std::runtime_error("failed to end Datoviz command buffer");
::renderive::error::unexpected<std::runtime_error>("failed to end Datoviz command buffer");
recording_ = false;
dvz_fence_reset(fence_);
dvz_submit(submit_);
@@ -380,14 +382,14 @@ public:
DvzQueue* queue = dvz_gpu_ctx_queue(gpu_context_, DVZ_QUEUE_MAIN);
if (dvz_submit_send(submit_, dvz_queue_handle(queue),
dvz_fence_handle(fence_)) != VK_SUCCESS)
throw std::runtime_error("failed to submit Datoviz point frame");
::renderive::error::unexpected<std::runtime_error>("failed to submit Datoviz point frame");
in_flight_ = true;
completed_layout_ = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
}
[[nodiscard]] Collection collect() {
if (!in_flight_)
throw std::logic_error("Datoviz frame target has no pending frame");
::renderive::error::unexpected<std::logic_error>("Datoviz frame target has no pending frame");
Collection result;
try {
result.pixels.resize(static_cast<std::size_t>(byte_size_));
@@ -396,7 +398,8 @@ public:
} catch (...) {
in_flight_ = false;
observing_ = false;
throw;
::renderive::error::unexpected(
"submitting Datoviz frame target", std::current_exception());
}
in_flight_ = false;
observing_ = false;
@@ -405,7 +408,7 @@ public:
void discard_after_completion() {
if (!in_flight_)
throw std::logic_error("Datoviz frame target has no pending frame");
::renderive::error::unexpected<std::logic_error>("Datoviz frame target has no pending frame");
in_flight_ = false;
observing_ = false;
}
@@ -513,9 +516,10 @@ private:
elapsed(timestamps[0], timestamps[3])};
}
void destroy() noexcept {
void destroy() {
if (in_flight_)
std::terminate();
::renderive::error::unexpected<std::logic_error>(
"Datoviz frame target is still in flight during destruction");
if (query_pool_ != VK_NULL_HANDLE && gpu_context_ != nullptr) {
vkDestroyQueryPool(
dvz_device_handle(dvz_gpu_ctx_device(gpu_context_)),
@@ -585,34 +589,35 @@ Datoviz_Visual_Backend::Datoviz_Visual_Backend(
dvz_gpu_ctx_config_enable_canvas_extensions(&configuration, false);
gpu_context_ = dvz_gpu_ctx(&configuration);
if (gpu_context_ == nullptr)
throw std::runtime_error("failed to create Datoviz GPU context");
::renderive::error::unexpected<std::runtime_error>("failed to create Datoviz GPU context");
DvzDrp2RuntimeConfig runtime_configuration = dvz_drp2_runtime_vklite_config(
dvz_gpu_ctx_device(gpu_context_), dvz_gpu_ctx_alloc(gpu_context_));
runtime_ = dvz_drp2_runtime_vklite(&runtime_configuration);
if (runtime_ == nullptr)
throw std::runtime_error("failed to create Datoviz DRP2 runtime");
::renderive::error::unexpected<std::runtime_error>("failed to create Datoviz DRP2 runtime");
create_scene(initial_scene);
} catch (...) {
destroy();
throw;
::renderive::error::unexpected(
"creating Datoviz backend", std::current_exception());
}
}
Datoviz_Visual_Backend::~Datoviz_Visual_Backend() { destroy(); }
Datoviz_Visual_Backend::~Datoviz_Visual_Backend() noexcept(false) { destroy(); }
void Datoviz_Visual_Backend::require_domain() const {
if (std::this_thread::get_id() != domain_thread_)
throw std::logic_error("Datoviz objects may only be used on the render domain");
::renderive::error::unexpected<std::logic_error>("Datoviz objects may only be used on the render domain");
}
void Datoviz_Visual_Backend::create_scene(const Scene_State& initial_scene) {
const Visual_Family family = initial_scene.visual_family;
scene_ = dvz_scene();
if (scene_ == nullptr)
throw std::runtime_error("failed to create Datoviz scene");
::renderive::error::unexpected<std::runtime_error>("failed to create Datoviz scene");
const auto capabilities = dvz_capability_snapshot();
if (dvz_scene_set_capabilities(scene_, &capabilities) != DVZ_OK)
throw std::runtime_error("failed to configure Datoviz scene capabilities");
::renderive::error::unexpected<std::runtime_error>("failed to configure Datoviz scene capabilities");
figure_ = dvz_figure(scene_, initial_scene.viewport.width,
initial_scene.viewport.height, 0);
panel_ = figure_ != nullptr ? dvz_panel_full(figure_) : nullptr;
@@ -666,7 +671,7 @@ void Datoviz_Visual_Backend::create_scene(const Scene_State& initial_scene) {
}
if (visual_ != nullptr &&
dvz_visual_set_alpha_mode(visual_, DVZ_ALPHA_OPAQUE) != DVZ_OK)
throw std::runtime_error("failed to configure Datoviz visual alpha mode");
::renderive::error::unexpected<std::runtime_error>("failed to configure Datoviz visual alpha mode");
if (visual_ != nullptr && family == Visual_Family::Glyph)
configure_glyph_text(scene_, visual_, "GLYPH");
@@ -704,7 +709,7 @@ void Datoviz_Visual_Backend::create_scene(const Scene_State& initial_scene) {
view.rows_per_image = height;
if (field == nullptr || dvz_sampled_field_set_data(field, &view) != DVZ_OK ||
dvz_visual_set_field(visual_, "field", field) != DVZ_OK)
throw std::runtime_error("failed to create Datoviz 2D sampled field");
::renderive::error::unexpected<std::runtime_error>("failed to create Datoviz 2D sampled field");
}
if (visual_ != nullptr && family == Visual_Family::Labels) {
@@ -742,7 +747,7 @@ void Datoviz_Visual_Backend::create_scene(const Scene_State& initial_scene) {
dvz_scale_set_categories(scale, categories.data(),
static_cast<std::uint32_t>(categories.size())) != DVZ_OK ||
dvz_visual_set_scale(visual_, "labels", scale) != DVZ_OK)
throw std::runtime_error("failed to create Datoviz label field");
::renderive::error::unexpected<std::runtime_error>("failed to create Datoviz label field");
}
if (visual_ != nullptr && family == Visual_Family::Volume) {
@@ -777,11 +782,11 @@ void Datoviz_Visual_Backend::create_scene(const Scene_State& initial_scene) {
dvz_visual_set_field(visual_, "field", field) != DVZ_OK ||
dvz_volume_set_render_mode(visual_, DVZ_VOLUME_RENDER_MIP) != DVZ_OK ||
dvz_volume_set_step_count(visual_, 48) != DVZ_OK)
throw std::runtime_error("failed to create Datoviz volume field");
::renderive::error::unexpected<std::runtime_error>("failed to create Datoviz volume field");
}
if (figure_ == nullptr || panel_ == nullptr || visual_ == nullptr ||
dvz_panel_add_visual(panel_, visual_, nullptr) != DVZ_OK)
throw std::runtime_error("failed to create Datoviz visual family");
::renderive::error::unexpected<std::runtime_error>("failed to create Datoviz visual family");
DvzCameraDesc camera = dvz_camera_desc();
camera.view.eye[0] = 0.0F;
@@ -793,11 +798,11 @@ void Datoviz_Visual_Backend::create_scene(const Scene_State& initial_scene) {
camera.projection.near_clip = 0.01F;
camera.projection.far_clip = 100.0F;
if (dvz_panel_set_camera_desc(panel_, &camera) != DVZ_OK)
throw std::runtime_error("failed to create Datoviz point camera");
::renderive::error::unexpected<std::runtime_error>("failed to create Datoviz point camera");
DvzController* controller = dvz_arcball(scene_, nullptr);
if (controller == nullptr ||
dvz_panel_bind_controller(panel_, controller, DVZ_DIM_MASK_XYZ) != DVZ_OK)
throw std::runtime_error("failed to bind Datoviz arcball controller");
::renderive::error::unexpected<std::runtime_error>("failed to bind Datoviz arcball controller");
input_router_ = dvz_input_router();
gesture_handler_ = input_router_ != nullptr
@@ -805,7 +810,7 @@ void Datoviz_Visual_Backend::create_scene(const Scene_State& initial_scene) {
: nullptr;
if (input_router_ == nullptr || gesture_handler_ == nullptr ||
dvz_panel_connect_input(panel_, input_router_) != DVZ_OK)
throw std::runtime_error("failed to connect Datoviz point input");
::renderive::error::unexpected<std::runtime_error>("failed to connect Datoviz point input");
DvzInputResizeEvent resize{
initial_scene.viewport.width, initial_scene.viewport.height,
@@ -820,7 +825,7 @@ void Datoviz_Visual_Backend::apply(
if (scene_revision != applied_scene_revision_) {
if (dvz_figure_resize(figure_, scene.viewport.width,
scene.viewport.height) != DVZ_OK)
throw std::runtime_error("failed to resize Datoviz point figure");
::renderive::error::unexpected<std::runtime_error>("failed to resize Datoviz point figure");
DvzInputResizeEvent resize{
scene.viewport.width, scene.viewport.height,
scene.viewport.width, scene.viewport.height, 1.0F, 1.0F};
@@ -843,13 +848,13 @@ void Datoviz_Visual_Backend::apply(
style.stroke_width_px = state.style.stroke_width_px;
style.aspect = aspect(state.style.aspect);
if (dvz_point_set_style(visual_, &style) != DVZ_OK)
throw std::runtime_error("failed to apply Datoviz point style");
::renderive::error::unexpected<std::runtime_error>("failed to apply Datoviz point style");
}
if (dvz_visual_set_transform(visual_, transform) != DVZ_OK ||
dvz_visual_set_depth_test(visual_, state.depth_test) != DVZ_OK ||
dvz_visual_set_visible(
visual_, state.visible && !point.positions.empty()) != DVZ_OK)
throw std::runtime_error("failed to apply Datoviz visual state");
::renderive::error::unexpected<std::runtime_error>("failed to apply Datoviz visual state");
applied_state_revision_ = point.state_revision;
}
@@ -857,7 +862,7 @@ void Datoviz_Visual_Backend::apply(
return;
if (point.positions.empty()) {
if (dvz_visual_set_visible(visual_, false) != DVZ_OK)
throw std::runtime_error("failed to hide empty Datoviz point visual");
::renderive::error::unexpected<std::runtime_error>("failed to hide empty Datoviz point visual");
applied_data_revision_ = point.data_revision;
return;
}
@@ -958,7 +963,7 @@ void Datoviz_Visual_Backend::apply(
}
if (result != DVZ_OK ||
dvz_visual_set_visible(visual_, point.state.visible) != DVZ_OK)
throw std::runtime_error("failed to upload Datoviz visual payload");
::renderive::error::unexpected<std::runtime_error>("failed to upload Datoviz visual payload");
applied_data_revision_ = point.data_revision;
}
@@ -1029,7 +1034,7 @@ DvzSceneFrameArtifact* Datoviz_Visual_Backend::emit(
if (const char* diagnostic = dvz_diagnostic_report_get(&report, 0))
message += ": " + std::string(diagnostic);
}
throw std::runtime_error(message);
::renderive::error::unexpected<std::runtime_error>(message);
}
return artifact;
}
@@ -1067,7 +1072,8 @@ Datoviz_Visual_Backend::submit(
trace.emit_ns = trace_now_ns() - phase_started;
} catch (...) {
target_->abort();
throw;
::renderive::error::unexpected(
"preparing Datoviz frame", std::current_exception());
}
if (observe) {
trace.artifact_status = static_cast<std::uint32_t>(
@@ -1102,7 +1108,7 @@ Datoviz_Visual_Backend::submit(
dvz_scene_frame_artifact_destroy(artifact);
if (!attached) {
target_->abort();
throw std::runtime_error("failed to attach the Datoviz point frame target");
::renderive::error::unexpected<std::runtime_error>("failed to attach the Datoviz point frame target");
}
if (!result.ok) {
target_->abort();
@@ -1115,7 +1121,7 @@ Datoviz_Visual_Backend::submit(
trace.artifact_json.substr(
0, std::min<std::size_t>(trace.artifact_json.size(), 2048));
}
throw std::runtime_error(std::move(message));
::renderive::error::unexpected<std::runtime_error>(std::move(message));
}
if (observe)
@@ -1133,7 +1139,7 @@ Datoviz_Visual_Backend::Completed_Frame Datoviz_Visual_Backend::collect(
require_domain();
if (target_ == nullptr ||
target_->generation() != pending.target_generation)
throw std::logic_error("Datoviz pending frame target no longer exists");
::renderive::error::unexpected<std::logic_error>("Datoviz pending frame target no longer exists");
const std::uint64_t readback_started = pending.trace.observed
? trace_now_ns()
: 0;
@@ -1152,13 +1158,14 @@ void Datoviz_Visual_Backend::discard(Pending_Frame pending) {
require_domain();
if (target_ == nullptr ||
target_->generation() != pending.target_generation)
throw std::logic_error("Datoviz pending frame target no longer exists");
::renderive::error::unexpected<std::logic_error>("Datoviz pending frame target no longer exists");
target_->discard_after_completion();
}
void Datoviz_Visual_Backend::destroy() noexcept {
void Datoviz_Visual_Backend::destroy() {
if (std::this_thread::get_id() != domain_thread_)
std::terminate();
::renderive::error::unexpected<std::logic_error>(
"Datoviz backend may only be destroyed on the render domain");
if (runtime_ != nullptr) {
dvz_drp2_runtime_destroy(runtime_);
runtime_ = nullptr;
@@ -37,7 +37,7 @@ public:
Datoviz_Visual_Backend(std::uint32_t gpu_index, bool validation_enabled,
const Scene_State& initial_scene);
~Datoviz_Visual_Backend();
~Datoviz_Visual_Backend() noexcept(false);
Datoviz_Visual_Backend(const Datoviz_Visual_Backend&) = delete;
Datoviz_Visual_Backend& operator=(const Datoviz_Visual_Backend&) = delete;
@@ -66,7 +66,7 @@ private:
void apply(const Scene_State& scene, std::uint64_t scene_revision,
const Prepared_Point& point);
[[nodiscard]] DvzSceneFrameArtifact* emit(const Scene_State& scene);
void destroy() noexcept;
void destroy();
std::thread::id domain_thread_;
DvzGpuCtx* gpu_context_{};
@@ -1,3 +1,4 @@
#include <renderive/error/Error_Policy.hpp>
#include "Gpu_Completion_Service.h"
#include <algorithm>
#include <stdexcept>
@@ -27,13 +28,13 @@ Gpu_Completion_Service::Reservation::Reservation(Reservation&& other) noexcept
void Gpu_Completion_Service::Reservation::watch(VkDevice device,
VkFence fence) {
if (!pending_ || device == VK_NULL_HANDLE || fence == VK_NULL_HANDLE)
throw std::logic_error("GPU completion reservation or fence is invalid");
::renderive::error::unexpected<std::logic_error>("GPU completion reservation or fence is invalid");
auto pending = std::exchange(pending_, {});
auto* const service = pending->service;
{
std::lock_guard lock(pending->mutex);
if (pending->status != Pending_Fence::Status::reserved)
throw std::logic_error("GPU completion reservation is not reserved");
::renderive::error::unexpected<std::logic_error>("GPU completion reservation is not reserved");
pending->device = device;
pending->fence = fence;
// This timestamp is part of correctness, not only observability: it
@@ -92,9 +93,9 @@ void Gpu_Completion_Service::release_slot() noexcept {
Gpu_Completion_Service::Prepare_Result Gpu_Completion_Service::prepare(
Completion completion, Exception_Handler on_exception, bool observe) {
if (!completion)
throw std::invalid_argument("GPU completion callback is empty");
::renderive::error::unexpected<std::invalid_argument>("GPU completion callback is empty");
if (!on_exception)
throw std::invalid_argument("GPU completion exception handler is empty");
::renderive::error::unexpected<std::invalid_argument>("GPU completion exception handler is empty");
if (stopping_.load(std::memory_order_acquire))
return {{}, Error::stopping};
auto pending = std::make_shared<Pending_Fence>();
@@ -109,7 +110,7 @@ Gpu_Completion_Service::Prepare_Result Gpu_Completion_Service::prepare(
}
if (!pending_.try_push(pending)) {
release_slot();
throw std::logic_error("GPU completion admission invariant violated");
::renderive::error::unexpected<std::logic_error>("GPU completion admission invariant violated");
}
wake();
return {Reservation(std::move(pending)), Error::none};
@@ -174,7 +175,8 @@ void Gpu_Completion_Service::run() {
try {
completion(std::move(completion_result));
} catch (...) {
on_exception(std::current_exception());
on_exception(::renderive::error::capture(
"delivering GPU completion", std::current_exception()));
}
release_slot();
};
+9 -6
View File
@@ -1,3 +1,4 @@
#include <renderive/error/Error_Policy.hpp>
#include "Render_Domain.h"
#include <mutex>
#include <stdexcept>
@@ -95,9 +96,9 @@ void Render_Domain::release_admission() noexcept {
Render_Domain::Prepare_Result Render_Domain::prepare(
std::function<void()> function, Exception_Handler on_exception) {
if (!function)
throw std::invalid_argument("render domain task is empty");
::renderive::error::unexpected<std::invalid_argument>("render domain task is empty");
if (!on_exception)
throw std::invalid_argument("render domain exception handler is empty");
::renderive::error::unexpected<std::invalid_argument>("render domain exception handler is empty");
if (stopping_.load(std::memory_order_acquire))
return {{}, Error::stopping};
acquire_admission();
@@ -110,7 +111,8 @@ Render_Domain::Prepare_Result Render_Domain::prepare(
std::move(function), std::move(on_exception))), Error::none};
} catch (...) {
release_admission();
throw;
::renderive::error::unexpected(
"preparing render domain task", std::current_exception());
}
}
Render_Domain::Error Render_Domain::post(
@@ -122,11 +124,11 @@ Render_Domain::Error Render_Domain::post(
}
Render_Domain::Error Render_Domain::post(Prepared_Task task) {
if (!task.task_ || task.domain_.get() != this)
throw std::logic_error("render domain prepared task is invalid");
::renderive::error::unexpected<std::logic_error>("render domain prepared task is invalid");
if (stopping_.load(std::memory_order_acquire))
return Error::stopping;
if (!tasks_.try_push(std::move(task.task_)))
throw std::logic_error("render domain admission invariant violated");
::renderive::error::unexpected<std::logic_error>("render domain admission invariant violated");
const std::size_t queued = queued_.fetch_add(1, std::memory_order_relaxed) + 1;
update_peak(peak_queued_, queued);
task.domain_.reset();
@@ -158,7 +160,8 @@ void Render_Domain::run() {
try {
task->function();
} catch (...) {
task->on_exception(std::current_exception());
task->on_exception(::renderive::error::capture(
"executing render domain task", std::current_exception()));
}
task.reset();
if (destroy_on_exit_.load(std::memory_order_acquire)) {
+14 -6
View File
@@ -1,4 +1,5 @@
#pragma once
#include <renderive/error/Error_Policy.hpp>
#include <atomic>
#include <chrono>
#include <cstddef>
@@ -97,15 +98,22 @@ public:
auto result = task->get_future();
output.error = post([task] { (*task)(); },
[](std::exception_ptr exception) {
std::rethrow_exception(exception);
::renderive::error::unexpected(
"render domain invocation", exception);
});
if (output.error != Error::none)
return output;
if constexpr (std::is_void_v<Result>) {
result.get();
output.value.emplace();
} else {
output.value.emplace(result.get());
try {
if constexpr (std::is_void_v<Result>) {
result.get();
output.value.emplace();
} else {
output.value.emplace(result.get());
}
} catch (...) {
::renderive::error::unexpected(
"collecting render domain invocation",
std::current_exception());
}
return output;
}
+6 -5
View File
@@ -1,4 +1,5 @@
#pragma once
#include <renderive/error/Error_Policy.hpp>
#include "Visual_Types.h"
#include "../renderable/Inheritance.h"
@@ -47,13 +48,13 @@ struct Basic_Visual : Renderable<Basic_Visual<Spec>> {
void operator()(const State& state) const {
for (float value : state.transform.values) {
if (!finite(value))
throw std::invalid_argument("visual transform must be finite");
::renderive::error::unexpected<std::invalid_argument>("visual transform must be finite");
}
if constexpr (requires { state.field_width; state.field_height; }) {
const bool any = state.field_width || state.field_height;
const bool all = state.field_width && state.field_height;
if (any && !all)
throw std::invalid_argument(
::renderive::error::unexpected<std::invalid_argument>(
"texture field dimensions must be specified together");
}
if constexpr (requires { state.field_depth; }) {
@@ -62,7 +63,7 @@ struct Basic_Visual : Renderable<Basic_Visual<Spec>> {
const bool all = state.field_width && state.field_height &&
state.field_depth;
if (any && !all)
throw std::invalid_argument(
::renderive::error::unexpected<std::invalid_argument>(
"volume field dimensions must be specified together");
}
}
@@ -92,7 +93,7 @@ struct Basic_Visual : Renderable<Basic_Visual<Spec>> {
explicit Impl(std::vector<Item> items) {
if (update(std::move(items)) != Visual_Data_Error::none)
throw std::invalid_argument(std::string(Spec::name) +
::renderive::error::unexpected<std::invalid_argument>(std::string(Spec::name) +
" payload contains invalid data");
}
@@ -132,7 +133,7 @@ struct Basic_Visual : Renderable<Basic_Visual<Spec>> {
void prepare(const Prepare_Render_Context& context) override {
const auto input = context.template captured<Frame_Data>();
if (!input)
throw std::logic_error(
::renderive::error::unexpected<std::logic_error>(
"Datoviz visual frame input was not captured");
if (context.metrics) {
const auto count = input->items ? input->items->size() : 0U;
+3 -1
View File
@@ -206,7 +206,9 @@ if (RENDERIVE_BUILD_TESTS)
target_link_libraries("${Renderive_Web_test_target}" PRIVATE Renderive_Web Adminive::Nlohmann GTest::gtest_main)
renderive_stage_render_3D_runtime("${Renderive_Web_test_target}")
add_test(NAME "${Renderive_Web_test_target}" COMMAND "${Renderive_Web_test_target}")
set_tests_properties("${Renderive_Web_test_target}" PROPERTIES LABELS "Renderive_Web")
set_tests_properties("${Renderive_Web_test_target}" PROPERTIES
LABELS "Renderive_Web"
ENVIRONMENT "RENDERIVE_ERROR_MODE=exception")
list(APPEND Renderive_Web_test_targets "${Renderive_Web_test_target}")
endforeach ()
add_custom_target(Renderive_Web_check
+2 -1
View File
@@ -1,4 +1,5 @@
#pragma once
#include <renderive/error/Error_Policy.hpp>
#include "Gallery_Enum.h"
#include "common/Gallery_Performance_Types.h"
@@ -76,7 +77,7 @@ struct Value_Adapter<renderive::Plot_Frame_Mode, Json> {
const auto parsed =
renderive::web::gallery_enum_cast<renderive::Plot_Frame_Mode>(value);
if (!parsed)
throw std::invalid_argument("unknown frame control mode: " + value);
::renderive::error::unexpected<std::invalid_argument>("unknown frame control mode: " + value);
target = *parsed;
}
};
+7 -3
View File
@@ -1,3 +1,4 @@
#include <renderive/error/Error_Policy.hpp>
#include "Gallery_Plot_Session.h"
#include "Gallery_Protocol.h"
#include "common/Gallery_Scene_Interface.h"
@@ -252,7 +253,8 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
} catch (...) {
std::lock_guard lock(mutex);
render_task_pending = false;
automatic_render_exception = std::current_exception();
automatic_render_exception = ::renderive::error::capture(
"arming automatic gallery render", std::current_exception());
disarm_automatic_render();
}
}
@@ -270,7 +272,8 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
} catch (...) {
std::lock_guard lock(mutex);
render_task_pending = false;
automatic_render_exception = std::current_exception();
automatic_render_exception = ::renderive::error::capture(
"running automatic gallery render", std::current_exception());
disarm_automatic_render();
}
}
@@ -447,7 +450,8 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
exception = std::exchange(automatic_render_exception, {});
}
if (exception)
std::rethrow_exception(exception);
::renderive::error::unexpected(
"automatic gallery render", exception);
}
if (std::holds_alternative<Frame_Request>(event))
return handle_frame_request();
+4 -3
View File
@@ -1,3 +1,4 @@
#include <renderive/error/Error_Policy.hpp>
#include "Gallery_Protocol.h"
#include "Gallery_Actions.h"
#include "Gallery_Enum.h"
@@ -95,7 +96,7 @@ void append_session_actions(Gallery_Frame_Mode frame_mode,
append_gallery_actions<Gallery_Session_Control<Plot_Scene>>(result);
return;
}
throw std::invalid_argument("unknown gallery frame mode");
::renderive::error::unexpected<std::invalid_argument>("unknown gallery frame mode");
}
std::size_t session_control_count(std::string_view case_id, Gallery_Frame_Mode frame_mode) {
if (case_id.starts_with("datoviz_"))
@@ -108,7 +109,7 @@ std::size_t session_control_count(std::string_view case_id, Gallery_Frame_Mode f
case Gallery_Frame_Mode::Playback:
return gallery_control_count<Gallery_Session_Control<Plot_Scene>>();
}
throw std::invalid_argument("unknown gallery frame mode");
::renderive::error::unexpected<std::invalid_argument>("unknown gallery frame mode");
}
void append_3d_actions(Gallery_Frame_Mode frame_mode, std::vector<Action_Model>& result) {
const auto append = [&result](const Gallery_Action_Attribute& action) {
@@ -257,7 +258,7 @@ const Json& described_field(std::string_view name) {
return field.at("name").template get<std::string>() == name;
});
if (found == fields.end())
throw std::logic_error("missing Adminive dashboard field: " + std::string(name));
::renderive::error::unexpected<std::logic_error>("missing Adminive dashboard field: " + std::string(name));
return *found;
}
+2 -1
View File
@@ -1,4 +1,5 @@
#pragma once
#include <renderive/error/Error_Policy.hpp>
#include "Gallery_Actions.h"
#include "Gallery_Renderable_Types.h"
@@ -174,7 +175,7 @@ struct Value_Adapter<renderive::Color, Json> {
!std::all_of(value.begin() + 1, value.end(), [](unsigned char character) {
return std::isxdigit(character) != 0;
}))
throw std::invalid_argument("color must use #RRGGBB");
::renderive::error::unexpected<std::invalid_argument>("color must use #RRGGBB");
const auto channel = [&value](std::size_t offset) {
return static_cast<std::uint8_t>(std::stoul(value.substr(offset, 2), nullptr, 16));
};
+8 -7
View File
@@ -1,3 +1,4 @@
#include <renderive/error/Error_Policy.hpp>
#include "Gallery_Scene2D.h"
#include "../Gallery_Controls.h"
#include "../Gallery_Enum.h"
@@ -172,7 +173,7 @@ Plot_Frame_Mode plot_frame_mode(Gallery_Frame_Mode mode) {
case Gallery_Frame_Mode::Playback:
return Plot_Frame_Mode::Playback;
}
throw std::invalid_argument("unknown gallery frame mode");
::renderive::error::unexpected<std::invalid_argument>("unknown gallery frame mode");
}
} // namespace
class Gallery_Scene final : public Gallery_Scene_Interface {
@@ -192,7 +193,7 @@ public:
if (attach.attach(group) != Scene_Edit_Error::none ||
attach.add_display_parent(group, root_) != Scene_Edit_Error::none ||
attach.add_dependency_parent(group, root_) != Scene_Edit_Error::none)
throw std::logic_error("validated gallery group attachment failed");
::renderive::error::unexpected<std::logic_error>("validated gallery group attachment failed");
return group;
};
axes_node_ = make_group("坐标轴层");
@@ -393,7 +394,7 @@ public:
if (!capture) {
if (capture.error == Capture_Error::session_active)
return "A performance capture session is already active";
throw std::logic_error("validated capture request rejected");
::renderive::error::unexpected<std::logic_error>("validated capture request rejected");
}
last_action_result_ = "capture session=" + std::to_string(capture.session_id);
return "Capture next render frame requested";
@@ -411,7 +412,7 @@ public:
if (!capture) {
if (capture.error == Capture_Error::session_active)
return "A performance capture session is already active";
throw std::logic_error("validated capture request rejected");
::renderive::error::unexpected<std::logic_error>("validated capture request rejected");
}
last_action_result_ = "capture session=" + std::to_string(capture.session_id) +
", frames=" + std::to_string(count);
@@ -916,7 +917,7 @@ private:
consumer_feedback_source_ = "none";
if (!feedback_policy_.enabled) {
if (plot_.clear_consumer_feedback() != Plot_Control_Error::none)
throw std::logic_error("low-latency feedback clearing failed");
::renderive::error::unexpected<std::logic_error>("low-latency feedback clearing failed");
consumer_feedback_source_ = "disabled";
return;
}
@@ -944,11 +945,11 @@ private:
consumer_manual_interval_ns_, "manual");
if (interval_ns == 0) {
if (plot_.clear_consumer_feedback() != Plot_Control_Error::none)
throw std::logic_error("low-latency feedback clearing failed");
::renderive::error::unexpected<std::logic_error>("low-latency feedback clearing failed");
return;
}
if (plot_.set_consumer_feedback({interval_ns}) != Plot_Control_Error::none)
throw std::logic_error("validated low-latency feedback was rejected");
::renderive::error::unexpected<std::logic_error>("validated low-latency feedback was rejected");
}
void record_performance(std::chrono::steady_clock::time_point started, bool rendered) {
const auto finished = std::chrono::steady_clock::now();
+9 -8
View File
@@ -1,3 +1,4 @@
#include <renderive/error/Error_Policy.hpp>
#include "Gallery_Scene3D.h"
#include "../Gallery_Capture_Json.h"
@@ -39,7 +40,7 @@ struct Value_Adapter<
const auto parsed = renderive::web::gallery_enum_cast<
renderive::render_3d::Renderable_Observer_Event>(value);
if (!parsed)
throw std::invalid_argument("unknown 3D observer event");
::renderive::error::unexpected<std::invalid_argument>("unknown 3D observer event");
target = *parsed;
}
};
@@ -93,7 +94,7 @@ Gallery_Render_Error gallery_render_error(Scene_Render_Error error) {
case Scene_Render_Error::shutting_down:
return Gallery_Render_Error::shutting_down;
}
throw std::logic_error("unknown scene render error");
::renderive::error::unexpected<std::logic_error>("unknown scene render error");
}
Frame_Request_Error frame_request_error(Scene_Render_Error error) {
switch (error) {
@@ -108,7 +109,7 @@ Frame_Request_Error frame_request_error(Scene_Render_Error error) {
case Scene_Render_Error::shutting_down:
return Frame_Request_Error::scene_shutting_down;
}
throw std::logic_error("unknown scene render error");
::renderive::error::unexpected<std::logic_error>("unknown scene render error");
}
struct Gallery_Point {
@@ -125,7 +126,7 @@ Frame_Mode render_frame_mode(Gallery_Frame_Mode mode) {
case Gallery_Frame_Mode::Playback:
return Frame_Mode::Playback;
}
throw std::invalid_argument("unknown gallery frame mode");
::renderive::error::unexpected<std::invalid_argument>("unknown gallery frame mode");
}
std::string mode_id(Gallery_Frame_Mode mode) {
@@ -231,7 +232,7 @@ public:
.visual_family = visual_family(case_id_)},
visual_) {
if (!visual_)
throw std::runtime_error("failed to create Point_Visual");
::renderive::error::unexpected<std::runtime_error>("failed to create Point_Visual");
visual_->set_object_name("3D Visual");
render_initial_frame();
}
@@ -401,7 +402,7 @@ public:
if (!capture) {
if (capture.error == Capture_Error::session_active)
return "A performance capture session is already active";
throw std::logic_error("validated capture request rejected");
::renderive::error::unexpected<std::logic_error>("validated capture request rejected");
}
last_action_result_ =
"capture session=" + std::to_string(capture.session_id);
@@ -422,7 +423,7 @@ public:
if (!capture) {
if (capture.error == Capture_Error::session_active)
return "A performance capture session is already active";
throw std::logic_error("validated capture request rejected");
::renderive::error::unexpected<std::logic_error>("validated capture request rejected");
}
last_action_result_ = "capture session=" +
std::to_string(capture.session_id) +
@@ -617,7 +618,7 @@ private:
points.push_back({{0.0F, 0.62F, 0.0F}, {255, 230, 20, 255}, 58.0F});
if (visual_->update_points(std::move(points)) !=
Visual_Data_Error::none)
throw std::logic_error("generated gallery point data is invalid");
::renderive::error::unexpected<std::logic_error>("generated gallery point data is invalid");
}
void advance_blue_point(float step) {
+5 -1
View File
@@ -527,7 +527,11 @@ TEST(RenderiveWebGallery, PerformanceCaptureReturnsPlanFrameAnalysisAndStatistic
ASSERT_EQ(rendered.at("type"), "case_state");
}
const auto& capture = rendered.at("telemetry").at("performance_capture");
nlohmann::json capture;
ASSERT_TRUE(wait_for_condition([&] {
capture = observe_telemetry(session).at("performance_capture");
return !capture.at("controller").at("enabled").get<bool>();
}));
EXPECT_FALSE(capture.at("controller").at("enabled"));
ASSERT_EQ(capture.at("sessions").size(), 1U);
const auto& captured = capture.at("sessions").at(0);