错误处理
This commit is contained in:
@@ -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");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user